Built with blockbuilder.org
Last active
April 15, 2020 16:50
-
-
Save gordonwoodhull/2bb509445fa53a9d02e8a182597e55aa to your computer and use it in GitHub Desktop.
dc_table
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
license: mit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title>Test-Dashboard</title> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/3.0.9/dc.min.css"> | |
<!-- <link rel="stylesheet" href="d3.slider.css"> --> | |
<!-- Dashboard Dependecies --> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.2.0/css/bootstrap.min.css"> | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/keen/dashboards@gh-pages/assets/css/keen-dashboards.css"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.2.0/js/bootstrap.min.js"></script> | |
<style> | |
html, body { | |
height: 100%; | |
width: 100%; | |
} | |
.svg-container { | |
width: 100%; | |
} | |
.irs--big.irs-with-grid { | |
height: 70px; | |
width: 500px; | |
margin-left: 5%; | |
} | |
</style> | |
</head> | |
<body class="keen-dashboard"> | |
<!-- Navbar --> | |
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation"> | |
<div class="container-fluid"> | |
<div class="navbar-header"> | |
<span> | |
<a class="navbar-brand" title="Click to view data source" target="_blank" href="https://github.com/square/crossfilter/wiki/API-Reference"> | |
Dc.js Simple Dashboard | |
</a> | |
</span> | |
</div> | |
</div> | |
</div> | |
<div class="container"> | |
<div class="row"> | |
<div class="table-responsive col-md-12"> | |
<h4>Filtered Records</h4> | |
<table class='table table-hover table-striped table-bordered' id='dc-table-graph'> | |
<thead> | |
<tr class='table-header'> | |
<!-- Programmatically insert table headers here --> | |
</tr> | |
</thead> | |
</table> | |
<!-- <div class="col-sm-12" id="paging"> | |
Showing <span id="begin"></span>-<span id="end"></span> of <span id="size"></span> | |
<input id="Prev" class="btn btn-secondary" role="button" type="button" value="Prev" onclick="javascript:prevPage()" /> | |
<input id="Next" class="btn btn-secondary" role="button" type="button" value="Next" onclick="javascript:nextPage()"/> | |
</div> --> | |
</div> | |
</div> | |
<div id="vertical-whitespace" style="padding: 10px"></div> | |
</div> | |
<div class="col-lg-6 col-md-6"> | |
<div class="row"> | |
<div class="col-lg-12 col-md-12"> | |
<div class="chart-wrapper"> | |
<div class="chart-stage"> | |
<div id="pie-chart" class="svg-container"> | |
<div class="chart-title"> | |
<span> | |
<strong>Pie Types</strong> | |
</span> | |
<span> | |
<a href="javascript:pieChart.filterAll(); dc.redrawAll();" style="display:none;" class="reset">reset</a> | |
</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script data-require="[email protected]" data-semver="1.5.2" src="//cdn.jsdelivr.net/underscorejs/1.5.2/underscore-min.js"></script> | |
<!-- <script data-require="[email protected]" data-semver="1.4.0" src="//cdnjs.cloudflare.com/ajax/libs/coffee-script/1.4.0/coffee-script.min.js"></script> --> | |
<!-- <script src="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/js/ion.rangeSlider.js"></script> --> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.7/crossfilter.js"></script> | |
<!-- <script src="d3.slider.js"></script> --> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.15.1/d3.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.2.1/dc.js"></script> | |
<script src="https://unpkg.com/reductio/reductio.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script> | |
<script lang="babel" type="text/babel" src="index.js"></script> | |
<!--Plugin CSS file with desired skin--> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/css/ion.rangeSlider.min.css"/> | |
<!--jQuery--> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> | |
<!--Plugin JavaScript file--> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/ion-rangeslider/2.3.1/js/ion.rangeSlider.min.js"></script> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const log = console.log; | |
var dataTable = dc.dataTable("#dc-table-graph"); | |
var pieChart = dc.pieChart("#pie-chart"); | |
var dim = {}, // Stores all crossfilter dimensions | |
groups = {}, // Stores all crossfilter groups | |
cf; | |
d3.csv("info.csv").then(function(data) { | |
// console.log("data:",data); | |
data.forEach(d => { | |
// var parts = d.Created.split('/'); | |
// console.log("----------------:",new Date(d.date)); | |
d.date = new Date(d.date); | |
}); | |
create_graph(data); | |
}); | |
function create_graph(data) | |
{ | |
// Programmatically insert header labels for table | |
var tableHeader = d3.select(".table-header") | |
.selectAll("th"); | |
// Bind data to tableHeader selection. | |
tableHeader = tableHeader.data( | |
[ | |
{label: "pid", field_name: "pid", sort_state: "ascending"}, | |
{label: "cid", field_name: "cid", sort_state: "ascending"}, | |
{label: "date", field_name: "date", sort_state: "ascending"}, | |
{label: "type", field_name: "type", sort_state: "ascending"}, | |
{label: "probability", field_name: "probability", sort_state: "descending"} // Note Max Conf row starts off as descending | |
] | |
); | |
// enter() into virtual selection and create new <th> header elements for each table column | |
tableHeader = tableHeader.enter() | |
.append("th") | |
.text(function (d) { | |
console.log("lab::::") | |
return d.label; }) // Accessor function for header titles | |
.on("click", tableHeaderCallback); | |
function tableHeaderCallback(d) { | |
// Highlight column header being sorted and show bootstrap glyphicon | |
var activeClass = "info"; | |
d3.selectAll("#dc-table-graph th") // Disable all highlighting and icons | |
.classed(activeClass, false) | |
.selectAll("span") | |
.style("visibility", "hidden") // Hide glyphicon | |
var activeSpan = d3.select(this) // Enable active highlight and icon for active column for sorting | |
.classed(activeClass, true) // Set bootstrap "info" class on active header for highlight | |
.select("span") | |
.style("visibility", "visible"); | |
// Toggle sort order state to user desired state | |
d.sort_state = d.sort_state === "ascending" ? "descending" : "ascending"; | |
var isAscendingOrder = d.sort_state === "ascending"; | |
dataTable | |
.order(isAscendingOrder ? d3.ascending : d3.descending) | |
.sortBy(function(datum) { return datum[d.field_name]; }); | |
// Reset glyph icon for all other headers and update this headers icon | |
activeSpan.node().className = ''; // Remove all glyphicon classes | |
// Toggle glyphicon based on ascending/descending sort_state | |
activeSpan.classed( | |
isAscendingOrder ? "glyphicon glyphicon-sort-by-attributes" : | |
"glyphicon glyphicon-sort-by-attributes-alt", true); | |
updateTable(); | |
dataTable.redraw(); | |
} | |
// Initialize sort state and sort icon on one of the header columns | |
// Highlight "Max Conf" cell on page load | |
// This can be done programmatically for user specified column | |
tableHeader.filter(function(d) { return d.label === "Max Conf"; }) | |
.classed("info", true); | |
var tableSpans = tableHeader | |
.append("span") // For Sort glyphicon on active table headers | |
.classed("glyphicon glyphicon-sort-by-attributes-alt", true) | |
.style("visibility", "hidden") | |
.filter(function(d) { return d.label === "Max Conf"; }) | |
.style("visibility", "visible"); | |
cf = crossfilter(data); // Main crossfilter objects | |
let pieTypeDimension = cf.dimension(function(d) { return d.pid}); | |
let pieTypeGroup = pieTypeDimension.group(); | |
var reducer_prob = reductio().max(function(d) { | |
// console.log("max:",d.Time_estimate); | |
return parseFloat(d.probability) | |
}); | |
var pred_prob_max_pie = reducer_prob(pieTypeGroup).top(Infinity); | |
console.log('==XX>', pred_prob_max_pie); | |
pieChart | |
.height(400) | |
// .useViewBoxResizing(true) | |
.dimension(pieTypeDimension) | |
.group(pieTypeGroup) | |
.valueAccessor(function(p) { | |
return parseFloat(p.value.max) || 0; | |
}) | |
// this doesn't work because d is not a number; even d.value.max can be undefined | |
// which coerces to NaN | |
// .data(function (d) { | |
// return d.order(function (d) { | |
// console.log('d is', d); | |
// return +d; | |
// }).top(10) | |
// }) | |
.ordering(d => -(d.value.max || 0)) | |
.othersGrouper(null) | |
.slicesCap(4) | |
.innerRadius(100) | |
.externalLabels(30) | |
.externalRadiusPadding(50) | |
.drawPaths(true) | |
// .dimension(runDimension) | |
// .group(speedSumGroup) | |
.legend(dc.legend()); | |
// example of formatting the legend via svg | |
// http://stackoverflow.com/questions/38430632/how-can-we-add-legends-value-beside-of-legend-with-proper-alignment | |
pieChart.on('pretransition', function(chart) { | |
chart.selectAll('.dc-legend-item text') | |
.text('') | |
.append('tspan') | |
.text(function(d) { return d.name; }) | |
.append('tspan') | |
.attr('x', 100) | |
.attr('text-anchor', 'end') | |
.text(function(d) { | |
return d.data.max; }); | |
}); | |
// Setup different dimensions for plots | |
dim.tableMaxConfidence = cf.dimension(function (d) { | |
return d.pid; | |
}); | |
// ############################## | |
// Generate the dc.js dataTable | |
// ############################## | |
// Create generating functions for each columns | |
var columnFunctions = [ | |
function(d) { return d.pid; }, | |
function(d) { return d.cid; }, | |
function(d) { return d.date; }, | |
function(d) { return d.types; }, | |
function(d) { return d.probability; }, | |
]; | |
// extra dimensions for filtering | |
const cidDimension = cf.dimension(d => d.cid), | |
dateDimension = cf.dimension(d => d.date); | |
var color = d3.scaleSequential() //d3.scaleLinear() | |
.domain([0, 0.9]) | |
.interpolator(d3.interpolateBlues); | |
// Pagination implementation inspired by: | |
// https://github.com/dc-js/dc.js/blob/master/web/examples/table-pagination.html | |
dataTable.width(960).height(800) | |
.dimension(dim.tableMaxConfidence) | |
.group(function(d) { return "Dummy"}) // Must pass in. Ignored since .showGroups(false) | |
.size(Infinity) | |
.columns(columnFunctions) | |
.showGroups(false) | |
.sortBy(function(d){ return d.max_conf; }) // Initially sort by max_conf column | |
.order(d3.descending) | |
.on('pretransition', function (table) { | |
table.selectAll('td.dc-table-column._0') | |
.on('click',function(d){ | |
console.log(d); | |
table.filter(d.pid) | |
dc.redrawAll(); | |
}) | |
table.selectAll('td.dc-table-column._1') | |
.on('click',function(d){ | |
console.log("select:",d); | |
cidDimension.filter(d.cid) | |
dc.redrawAll(); | |
}) | |
table.selectAll('td.dc-table-column._2') | |
.on('click',function(d){ | |
dateDimension.filter(d.date) | |
dc.redrawAll(); | |
}) | |
table.selectAll('td.dc-table-column._4') | |
.style("background-color", function(d){ | |
return color(d.probability)}); | |
}); | |
// log('!!!!!!!!!!:',dataTable.selectAll('td').style('visibility', "hidden")); | |
updateTable(); | |
dc.renderAll(); | |
dataTable.redraw(); | |
} | |
// ##################################################333 | |
// Data Table Pagination | |
var tableOffset = 0, tablePageSize = 10; | |
// updateTable calculates correct start and end indices for current page view | |
// it slices and pulls appropriate date for current page from dataTable object | |
// Finally, it updates the pagination button states depending on if more records | |
// are available | |
function updateTable() { | |
// Visually encode the table as per rate value | |
d3.selectAll('td.dc-table-column _4').style("background-color", function(d, i){ | |
return colorScale(i); | |
}); | |
function colorScale(i){ | |
var color = d3.scaleLinear() | |
.domain([0, 10]) | |
.interpolate(d3.interpolateRgb) | |
.range(["orangered", "silver"]); | |
return color(i); | |
} | |
// Ensure Prev/Next bounds are correct, especially after filters applied to dc charts | |
var totFilteredRecs = cf.groupAll().value(); | |
// Adjust values of start and end record numbers for edge cases | |
var end = tableOffset + tablePageSize > totFilteredRecs ? totFilteredRecs : tableOffset + tablePageSize; | |
tableOffset = tableOffset >= totFilteredRecs ? Math.floor((totFilteredRecs - 1) / tablePageSize) * tablePageSize : tableOffset; | |
tableOffset = tableOffset < 0 ? 0 : tableOffset; // In case of zero entries | |
// Grab data for current page from the dataTable object | |
dataTable.beginSlice(tableOffset); | |
dataTable.endSlice(tableOffset + tablePageSize); | |
// Update Table paging buttons and footer text | |
d3.select('span#begin') | |
.text(end === 0 ? tableOffset : tableOffset + 1); // Correct for "Showing 1 of 0" bug | |
d3.select('span#end') | |
.text(end); | |
d3.select('#Prev.btn') | |
.attr('disabled', tableOffset - tablePageSize < 0 ? 'true' : null); | |
d3.select('#Next.btn') | |
.attr('disabled', tableOffset + tablePageSize >= totFilteredRecs ? 'true' : null); | |
d3.select('span#size').text(totFilteredRecs); | |
dataTable.redraw(); | |
} | |
// Callback function for clicking "Next" page button | |
function nextPage() { | |
tableOffset += tablePageSize; | |
updateTable(); | |
} | |
// Callback function for clicking "Prev" page button | |
function prevPage() { | |
tableOffset -= tablePageSize; | |
updateTable(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cid | pid | date | types | probability | |
---|---|---|---|---|---|
3485 | 8894 | 2018-01-09 06:00:00 | 1 | 0.7 | |
3497 | 3602 | 2018-05-19 06:00:00 | 1 | 0.6 | |
3500 | 4763 | 2018-09-22 06:00:00 | 1 | 0.59 | |
3497 | 3794 | 2018-05-26 06:00:00 | 1 | 0.57 | |
3502 | 0506 | 2018-03-26 22:06:00 | 1 | 0.57 | |
3257 | 4953 | 2018-02-26 22:26:00 | 1 | 0.56 | |
3707 | 3606 | 2018-10-11 05:00:00 | 1 | 0.56 | |
3732 | 7520 | 2018-09-15 06:00:00 | 1 | 0.52 | |
3697 | 8894 | 2018-11-08 06:00:00 | 1 | 0.52 | |
3459 | 7942 | 2018-03-26 22:06:00 | 1 | 0.5 | |
3379 | 8866 | 2018-02-26 22:25:00 | 0 | 0.49 | |
3805 | 8081 | 2019-02-16 05:01:00 | 0 | 0.49 | |
3295 | 7414 | 2018-02-26 22:23:00 | 0 | 0.48 | |
3521 | 7202 | 2018-09-22 06:00:00 | 0 | 0.48 | |
3824 | 0612 | 2018-11-17 05:01:00 | 0 | 0.48 | |
3550 | 9658 | 2018-12-05 06:00:00 | 0 | 0.47 | |
3338 | 6140 | 2018-02-26 22:26:00 | 0 | 0.47 | |
3856 | 6715 | 2019-09-02 05:01:00 | 0 | 0.47 | |
3381 | 6001 | 2018-03-26 22:06:00 | 0 | 0.47 | |
3559 | 2893 | 2018-10-13 06:00:00 | 0 | 0.46 | |
3760 | 1922 | 2019-02-16 05:01:00 | 0 | 0.45 | |
3484 | 5105 | 2018-07-07 06:00:00 | 0 | 0.45 | |
3279 | 6651 | 2018-02-26 22:25:00 | 0 | 0.44 | |
3712 | 2532 | 2018-09-22 06:00:00 | 0 | 0.44 | |
3501 | 7533 | 2018-03-26 22:06:00 | 0 | 0.44 | |
3313 | 1229 | 2018-02-26 22:29:00 | 0 | 0.44 | |
3227 | 0146 | 2018-02-26 22:25:00 | 0 | 0.43 | |
3201 | 5867 | 2018-03-26 22:06:00 | 0 | 0.43 | |
3682 | 5074 | 2018-09-15 06:00:00 | 0 | 0.42 | |
3103 | 4232 | 2018-02-28 13:45:00 | 0 | 0.42 | |
3117 | 8669 | 2018-02-28 13:46:00 | 0 | 0.42 | |
3217 | 9501 | 2018-02-26 22:25:00 | 0 | 0.41 | |
3345 | 8891 | 2018-03-26 22:06:00 | 0 | 0.41 | |
3385 | 8380 | 2018-02-26 22:26:00 | 0 | 0.41 | |
3279 | 5178 | 2018-02-26 22:25:00 | 0 | 0.4 | |
3393 | 0709 | 2018-02-26 22:26:00 | 0 | 0.4 | |
3496 | 6176 | 2018-07-04 06:01:00 | 0 | 0.4 | |
3663 | 9447 | 2018-10-13 06:00:00 | 0 | 0.39 | |
3700 | 2764 | 2018-01-09 06:00:00 | 0 | 0.39 | |
3382 | 6062 | 2018-02-26 22:26:00 | 0 | 0.38 | |
3357 | 6329 | 2018-03-26 22:06:00 | 0 | 0.38 | |
3713 | 6939 | 2018-10-11 05:00:00 | 0 | 0.38 | |
3444 | 1056 | 2018-03-26 22:06:00 | 0 | 0.37 | |
3495 | 6200 | 2018-09-06 06:01:00 | 0 | 0.37 | |
3486 | 2617 | 2018-10-11 05:00:00 | 0 | 0.37 | |
3332 | 2572 | 2018-02-26 22:26:00 | 0 | 0.36 | |
3498 | 3562 | 2018-06-23 06:00:00 | 0 | 0.36 | |
3313 | 1644 | 2018-03-26 22:06:00 | 0 | 0.36 | |
3398 | 6989 | 2018-03-26 22:06:00 | 0 | 0.36 | |
3446 | 6067 | 2018-03-26 22:06:00 | 0 | 0.35 | |
3589 | 2556 | 2018-05-19 06:00:00 | 0 | 0.34 | |
3339 | 6257 | 2018-02-26 22:28:00 | 0 | 0.34 | |
3484 | 9501 | 2018-11-08 06:00:00 | 0 | 0.34 | |
3612 | 1056 | 2018-05-26 06:00:00 | 0 | 0.34 | |
3388 | 0174 | 2018-02-26 22:26:00 | 0 | 0.33 | |
3797 | 0832 | 2018-10-11 05:00:00 | 0 | 0.32 | |
3574 | 4439 | 2018-05-05 06:00:00 | 0 | 0.32 | |
3687 | 8794 | 2018-07-21 06:00:00 | 0 | 0.32 | |
3391 | 1644 | 2018-02-26 22:26:00 | 0 | 0.31 | |
3828 | 3562 | 2019-02-26 06:00:00 | 0 | 0.31 | |
3899 | 3895 | 2019-09-02 05:01:00 | 0 | 0.31 | |
3747 | 1922 | 2018-10-13 06:00:00 | 0 | 0.31 | |
3649 | 8072 | 2018-11-08 06:00:00 | 0 | 0.31 | |
3690 | 8891 | 2018-10-11 05:00:00 | 0 | 0.31 | |
3698 | 9763 | 2018-07-28 06:00:00 | 0 | 0.31 | |
3475 | 3243 | 2018-03-26 22:06:00 | 0 | 0.3 | |
3492 | 2260 | 2018-03-26 22:06:00 | 0 | 0.3 | |
3464 | 6257 | 2018-03-26 22:06:00 | 0 | 0.3 | |
3856 | 7098 | 2019-02-26 06:00:00 | 0 | 0.3 | |
3406 | 7029 | 2018-03-26 22:06:00 | 0 | 0.3 | |
3592 | 7601 | 2018-06-16 06:00:00 | 0 | 0.3 | |
3279 | 4439 | 2018-02-26 22:25:00 | 0 | 0.3 | |
3433 | 8507 | 2018-04-21 06:01:00 | 0 | 0.3 | |
3575 | 3546 | 2018-04-26 00:27:00 | 0 | 0.3 | |
3361 | 8009 | 2018-03-26 22:06:00 | 0 | 0.3 | |
3538 | 8220 | 2018-03-26 22:06:00 | 0 | 0.3 | |
3552 | 0906 | 2018-05-26 06:00:00 | 0 | 0.3 | |
3321 | 1851 | 2018-02-26 22:23:00 | 0 | 0.29 | |
3810 | 6532 | 2019-09-02 05:01:00 | 0 | 0.29 | |
3416 | 8808 | 2018-03-26 22:06:00 | 0 | 0.29 | |
3833 | 0217 | 2019-09-02 05:01:00 | 0 | 0.28 | |
3661 | 8369 | 2018-07-07 06:00:00 | 0 | 0.28 | |
3383 | 8316 | 2018-03-26 22:06:00 | 0 | 0.28 | |
3513 | 1922 | 2018-03-26 22:06:00 | 0 | 0.28 | |
3454 | 5787 | 2018-03-27 07:00:00 | 0 | 0.28 | |
3293 | 0453 | 2018-03-26 22:06:00 | 0 | 0.28 | |
3364 | 7575 | 2018-02-28 14:08:00 | 0 | 0.27 | |
3538 | 8670 | 2018-03-26 22:06:00 | 0 | 0.27 | |
3894 | 0612 | 2019-02-26 06:00:00 | 0 | 0.27 | |
3378 | 3393 | 2018-03-26 22:06:00 | 0 | 0.27 | |
3764 | 3546 | 2019-02-23 05:01:00 | 0 | 0.27 | |
3715 | 3330 | 2018-10-13 06:00:00 | 0 | 0.27 | |
3689 | 3147 | 2018-04-08 06:00:00 | 0 | 0.27 | |
3534 | 3552 | 2018-03-26 22:06:00 | 0 | 0.27 | |
3861 | 4963 | 2019-02-26 12:57:00 | 0 | 0.27 | |
3612 | 1557 | 2018-09-06 06:01:00 | 0 | 0.26 | |
3432 | 3243 | 2018-03-26 22:06:00 | 0 | 0.26 | |
3776 | 7219 | 2018-03-11 06:01:00 | 0 | 0.26 | |
3639 | 9225 | 2018-09-06 06:01:00 | 0 | 0.26 | |
3894 | 2303 | 2019-02-26 05:03:00 | 0 | 0.26 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment