├── .gitignore ├── README.md ├── config.json ├── dist ├── apps │ ├── BarChart.css │ ├── BarChart.js │ ├── BarChart.js.map │ ├── Chart.css │ ├── Chart.js │ ├── Chart.js.map │ ├── ColumnChart.css │ ├── ColumnChart.js │ ├── ColumnChart.js.map │ ├── DotPlotChart.css │ ├── DotPlotChart.js │ ├── DotPlotChart.js.map │ ├── LineChart.css │ ├── LineChart.js │ ├── LineChart.js.map │ ├── ScatterChart.css │ ├── ScatterChart.js │ └── ScatterChart.js.map ├── favicon.png ├── global.css └── index.html ├── index.mjs ├── package-lock.json ├── package.json ├── rollup.config.build.js ├── rollup.config.dev.js ├── rollup.config.module.js └── src ├── App.svelte ├── apps ├── BarChart.js ├── Chart.js ├── ColumnChart.js ├── DotPlotChart.js ├── LineChart.js └── ScatterChart.js ├── charts ├── BarChart.svelte ├── Chart.svelte ├── ColumnChart.svelte ├── DotPlotChart.svelte ├── LineChart.svelte ├── MarkerChart.svelte ├── ScatterChart.svelte └── shared │ ├── Annotations.svelte │ ├── Area.svelte │ ├── ArrowheadDef.svelte │ ├── Arrows.svelte │ ├── AxisRadial.svelte │ ├── AxisX.html.svelte │ ├── AxisX.svelte │ ├── AxisY.html.svelte │ ├── AxisY.svelte │ ├── Bar.svelte │ ├── Beeswarm.html.svelte │ ├── Beeswarm.svelte │ ├── BeeswarmForce.html.svelte │ ├── BeeswarmForce.svelte │ ├── Brush.svelte │ ├── CalendarMonth.svelte │ ├── CirclePack.html.svelte │ ├── CirclePackForce.svelte │ ├── Column.svelte │ ├── ColumnLinear.svelte │ ├── DotPlot.html.svelte │ ├── DotPlot.svelte │ ├── Export.svelte │ ├── Footer.svelte │ ├── ForceDirectedGraph.svelte │ ├── Key.svelte │ ├── Labels-html.svelte │ ├── Labels.svelte │ ├── Legend.svelte │ ├── Line.svelte │ ├── Map.canvas.svelte │ ├── Map.svg.svelte │ ├── MapPoints.svelte │ ├── MultiLine.svelte │ ├── QuadTree.percent-range.svelte │ ├── QuadTree.svelte │ ├── Radar.svelte │ ├── Sankey.svelte │ ├── Scatter.canvas.svelte │ ├── Scatter.html.svelte │ ├── Scatter.svg.svelte │ ├── Scatter.webgl.svelte │ ├── SetCoords.svelte │ ├── SharedTooltip.percent-range.svelte │ ├── SharedTooltip.svelte │ ├── SmallMultipleWrapper.percent-range.svelte │ ├── SmallMultipleWrapper.svelte │ ├── Stack.svelte │ ├── Subtitle.svelte │ ├── SyncedBrushWrapper.percent-range.svelte │ ├── SyncedBrushWrapper.svelte │ ├── Table.svelte │ ├── Title.svelte │ ├── Tooltip.svelte │ ├── Voronoi.svelte │ └── ss │ ├── AreaStacked.svelte │ ├── BarStacked.svelte │ ├── ColumnStacked.svelte │ └── LineStacked.svelte ├── data ├── data-scatter.js ├── data.js └── us-states.topojson.json ├── js ├── accurate-beeswarm.js ├── spread-labels.js ├── utils.js └── wrap.js └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /dist/build/ 3 | 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @onsvisual/svelte-charts 2 | 3 | [![npm version](https://badge.fury.io/js/@onsvisual%2Fsvelte-charts.svg)](https://www.npmjs.com/package/@onsvisual/svelte-charts) 4 | 5 | Reusable chart templates for Svelte projects. 6 | 7 | Usage examples for these charts can be found on the [ONS svelte-components Storybook pages](https://onsvisual.github.io/svelte-components/): 8 | 9 | - [BarChart](https://onsvisual.github.io/svelte-components/?path=/docs/data-visualisation-barchart--docs) 10 | - [ColumnChart](https://onsvisual.github.io/svelte-components/?path=/docs/data-visualisation-columnchart--docs) 11 | - [DotPlotChart](https://onsvisual.github.io/svelte-components/?path=/docs/data-visualisation-dotplotchart--docs) 12 | - [LineChart](https://onsvisual.github.io/svelte-components/?path=/docs/data-visualisation-linechart--docs) 13 | - [ScatterChart](https://onsvisual.github.io/svelte-components/?path=/docs/data-visualisation-scatterchart--docs) (includes beeswarm) 14 | 15 | Further examples with interactions can be found in the **/src/App.svelte** file in this repo, which can be [previewed live here](https://onsvisual.github.io/svelte-charts/). 16 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "hydrate": false 3 | } 4 | -------------------------------------------------------------------------------- /dist/apps/BarChart.css: -------------------------------------------------------------------------------- 1 | .chart-container.svelte-1fy6g2i{width:100%}.visuallyhidden.svelte-1fy6g2i{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.bar-group.svelte-1kkdyuo polygon.svelte-1kkdyuo,.line-group.svelte-1kkdyuo polygon.svelte-1kkdyuo{shape-rendering:crispEdges}.tick.svelte-1b8cddl.svelte-1b8cddl{font-size:.8em}.tick.svelte-1b8cddl .tick-mark.svelte-1b8cddl,.tick.tick-0.svelte-1b8cddl line.svelte-1b8cddl{stroke-dasharray:0}.tick.svelte-1b8cddl line.svelte-1b8cddl{shape-rendering:crispEdges}.dashed.svelte-1b8cddl.svelte-1b8cddl{stroke-dasharray:2}.axis.snapTicks.svelte-1b8cddl .tick:last-child text.svelte-1b8cddl{transform:translateX(3px)}.axis.snapTicks.svelte-1b8cddl .tick.tick-0 text.svelte-1b8cddl{transform:translateX(-3px)}.tick.svelte-f7wn4m.svelte-f7wn4m{font-size:.8em}.dashed.svelte-f7wn4m.svelte-f7wn4m{stroke-dasharray:2}.tick.tick-0.svelte-f7wn4m line.svelte-f7wn4m{stroke-dasharray:0}ul.legend.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{margin:0;padding:0}ul.legend.svelte-nqsavd li.svelte-nqsavd.svelte-nqsavd{display:inline;font-size:.8em}ul.legend.svelte-nqsavd li.svelte-nqsavd+li.svelte-nqsavd{margin-left:8px}.bullet.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{display:inline-block;vertical-align:middle;transform:translateY(-1px)}.round.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{border-radius:50%}.chart-subtitle.svelte-1clt1zg{margin-top:-6px;margin-bottom:10px}.chart-footer.svelte-16332wo{font-size:.8em;color:grey;margin-top:5px}.chart-title.svelte-pvs6ms{font-size:1.1em;font-weight:bold;margin-bottom:10px}.chart-export.svelte-1no2d6c{margin-top:10px;font-size:.8em}button.svelte-1no2d6c{color:#206095;text-decoration:underline;text-underline-position:under;background:none;border:none;padding:0 0 1px}button.svelte-1no2d6c:hover{cursor:pointer;color:#003c57;text-decoration-thickness:2px}button.svelte-1no2d6c:focus{color:#222;background-color:#fbc900;text-decoration-thickness:3px}.layercake-container.svelte-vhzpsp,.layercake-container.svelte-vhzpsp *{box-sizing:border-box}.layercake-container.svelte-vhzpsp{width:100%;height:100%}div.svelte-1bu60uu,slot.svelte-1bu60uu{position:absolute;top:0;left:0}svg.svelte-u84d8d{position:absolute;top:0;left:0;overflow:visible}svg.svelte-6sm8ei{position:absolute;width:100%;height:100%;overflow:visible}svg.svelte-6sm8ei *{vector-effect:non-scaling-stroke} -------------------------------------------------------------------------------- /dist/apps/Chart.css: -------------------------------------------------------------------------------- 1 | .chart-container.svelte-1fy6g2i{width:100%}.visuallyhidden.svelte-1fy6g2i{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.chart-container.svelte-1fy6g2i{width:100%}.visuallyhidden.svelte-1fy6g2i{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.chart-container.svelte-1fy6g2i{width:100%}.visuallyhidden.svelte-1fy6g2i{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.chart-container.svelte-1fy6g2i{width:100%}.visuallyhidden.svelte-1fy6g2i{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.chart-container.svelte-1fy6g2i{width:100%}.visuallyhidden.svelte-1fy6g2i{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.tick.svelte-1b8cddl.svelte-1b8cddl{font-size:.8em}.tick.svelte-1b8cddl .tick-mark.svelte-1b8cddl,.tick.tick-0.svelte-1b8cddl line.svelte-1b8cddl{stroke-dasharray:0}.tick.svelte-1b8cddl line.svelte-1b8cddl{shape-rendering:crispEdges}.dashed.svelte-1b8cddl.svelte-1b8cddl{stroke-dasharray:2}.axis.snapTicks.svelte-1b8cddl .tick:last-child text.svelte-1b8cddl{transform:translateX(3px)}.axis.snapTicks.svelte-1b8cddl .tick.tick-0 text.svelte-1b8cddl{transform:translateX(-3px)}ul.legend.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{margin:0;padding:0}ul.legend.svelte-nqsavd li.svelte-nqsavd.svelte-nqsavd{display:inline;font-size:.8em}ul.legend.svelte-nqsavd li.svelte-nqsavd+li.svelte-nqsavd{margin-left:8px}.bullet.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{display:inline-block;vertical-align:middle;transform:translateY(-1px)}.round.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{border-radius:50%}.chart-subtitle.svelte-1clt1zg{margin-top:-6px;margin-bottom:10px}.chart-title.svelte-pvs6ms{font-size:1.1em;font-weight:bold;margin-bottom:10px}.bar-group.svelte-1kkdyuo polygon.svelte-1kkdyuo,.line-group.svelte-1kkdyuo polygon.svelte-1kkdyuo{shape-rendering:crispEdges}.tick.svelte-f7wn4m.svelte-f7wn4m{font-size:.8em}.dashed.svelte-f7wn4m.svelte-f7wn4m{stroke-dasharray:2}.tick.tick-0.svelte-f7wn4m line.svelte-f7wn4m{stroke-dasharray:0}.chart-footer.svelte-16332wo{font-size:.8em;color:grey;margin-top:5px}.chart-export.svelte-1no2d6c{margin-top:10px;font-size:.8em}button.svelte-1no2d6c{color:#206095;text-decoration:underline;text-underline-position:under;background:none;border:none;padding:0 0 1px}button.svelte-1no2d6c:hover{cursor:pointer;color:#003c57;text-decoration-thickness:2px}button.svelte-1no2d6c:focus{color:#222;background-color:#fbc900;text-decoration-thickness:3px}.column-group.svelte-maq6yn polygon.svelte-maq6yn,.line-group.svelte-maq6yn polygon.svelte-maq6yn{shape-rendering:crispEdges}.voronoi-cell.svelte-169satm{fill:none;stroke:none;pointer-events:all}.chart-label.svelte-dqkttw{font-size:0.8em}path.svelte-rh3b33{fill:none;stroke-linejoin:round;stroke-linecap:round}.path-hover.svelte-rh3b33{stroke:rgba(255,255,255,0);stroke-width:7}.path-line.svelte-rh3b33,.path-overlay.svelte-rh3b33{pointer-events:none}.layercake-container.svelte-vhzpsp,.layercake-container.svelte-vhzpsp *{box-sizing:border-box}.layercake-container.svelte-vhzpsp{width:100%;height:100%}svg.svelte-u84d8d{position:absolute;top:0;left:0;overflow:visible}div.svelte-1bu60uu,slot.svelte-1bu60uu{position:absolute;top:0;left:0}svg.svelte-6sm8ei{position:absolute;width:100%;height:100%;overflow:visible}svg.svelte-6sm8ei *{vector-effect:non-scaling-stroke} -------------------------------------------------------------------------------- /dist/apps/ColumnChart.css: -------------------------------------------------------------------------------- 1 | .chart-container.svelte-1fy6g2i{width:100%}.visuallyhidden.svelte-1fy6g2i{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.tick.svelte-f7wn4m.svelte-f7wn4m{font-size:.8em}.dashed.svelte-f7wn4m.svelte-f7wn4m{stroke-dasharray:2}.tick.tick-0.svelte-f7wn4m line.svelte-f7wn4m{stroke-dasharray:0}.tick.svelte-1b8cddl.svelte-1b8cddl{font-size:.8em}.tick.svelte-1b8cddl .tick-mark.svelte-1b8cddl,.tick.tick-0.svelte-1b8cddl line.svelte-1b8cddl{stroke-dasharray:0}.tick.svelte-1b8cddl line.svelte-1b8cddl{shape-rendering:crispEdges}.dashed.svelte-1b8cddl.svelte-1b8cddl{stroke-dasharray:2}.axis.snapTicks.svelte-1b8cddl .tick:last-child text.svelte-1b8cddl{transform:translateX(3px)}.axis.snapTicks.svelte-1b8cddl .tick.tick-0 text.svelte-1b8cddl{transform:translateX(-3px)}ul.legend.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{margin:0;padding:0}ul.legend.svelte-nqsavd li.svelte-nqsavd.svelte-nqsavd{display:inline;font-size:.8em}ul.legend.svelte-nqsavd li.svelte-nqsavd+li.svelte-nqsavd{margin-left:8px}.bullet.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{display:inline-block;vertical-align:middle;transform:translateY(-1px)}.round.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{border-radius:50%}.chart-title.svelte-pvs6ms{font-size:1.1em;font-weight:bold;margin-bottom:10px}.chart-subtitle.svelte-1clt1zg{margin-top:-6px;margin-bottom:10px}.column-group.svelte-maq6yn polygon.svelte-maq6yn,.line-group.svelte-maq6yn polygon.svelte-maq6yn{shape-rendering:crispEdges}.chart-footer.svelte-16332wo{font-size:.8em;color:grey;margin-top:5px}.chart-export.svelte-1no2d6c{margin-top:10px;font-size:.8em}button.svelte-1no2d6c{color:#206095;text-decoration:underline;text-underline-position:under;background:none;border:none;padding:0 0 1px}button.svelte-1no2d6c:hover{cursor:pointer;color:#003c57;text-decoration-thickness:2px}button.svelte-1no2d6c:focus{color:#222;background-color:#fbc900;text-decoration-thickness:3px}.layercake-container.svelte-vhzpsp,.layercake-container.svelte-vhzpsp *{box-sizing:border-box}.layercake-container.svelte-vhzpsp{width:100%;height:100%}svg.svelte-6sm8ei{position:absolute;width:100%;height:100%;overflow:visible}svg.svelte-6sm8ei *{vector-effect:non-scaling-stroke}div.svelte-1bu60uu,slot.svelte-1bu60uu{position:absolute;top:0;left:0}svg.svelte-u84d8d{position:absolute;top:0;left:0;overflow:visible} -------------------------------------------------------------------------------- /dist/apps/DotPlotChart.css: -------------------------------------------------------------------------------- 1 | .chart-container.svelte-1fy6g2i{width:100%}.visuallyhidden.svelte-1fy6g2i{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.tick.svelte-1b8cddl.svelte-1b8cddl{font-size:.8em}.tick.svelte-1b8cddl .tick-mark.svelte-1b8cddl,.tick.tick-0.svelte-1b8cddl line.svelte-1b8cddl{stroke-dasharray:0}.tick.svelte-1b8cddl line.svelte-1b8cddl{shape-rendering:crispEdges}.dashed.svelte-1b8cddl.svelte-1b8cddl{stroke-dasharray:2}.axis.snapTicks.svelte-1b8cddl .tick:last-child text.svelte-1b8cddl{transform:translateX(3px)}.axis.snapTicks.svelte-1b8cddl .tick.tick-0 text.svelte-1b8cddl{transform:translateX(-3px)}.tick.svelte-f7wn4m.svelte-f7wn4m{font-size:.8em}.dashed.svelte-f7wn4m.svelte-f7wn4m{stroke-dasharray:2}.tick.tick-0.svelte-f7wn4m line.svelte-f7wn4m{stroke-dasharray:0}ul.legend.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{margin:0;padding:0}ul.legend.svelte-nqsavd li.svelte-nqsavd.svelte-nqsavd{display:inline;font-size:.8em}ul.legend.svelte-nqsavd li.svelte-nqsavd+li.svelte-nqsavd{margin-left:8px}.bullet.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{display:inline-block;vertical-align:middle;transform:translateY(-1px)}.round.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{border-radius:50%}.chart-title.svelte-pvs6ms{font-size:1.1em;font-weight:bold;margin-bottom:10px}.chart-footer.svelte-16332wo{font-size:.8em;color:grey;margin-top:5px}.chart-export.svelte-1no2d6c{margin-top:10px;font-size:.8em}button.svelte-1no2d6c{color:#206095;text-decoration:underline;text-underline-position:under;background:none;border:none;padding:0 0 1px}button.svelte-1no2d6c:hover{cursor:pointer;color:#003c57;text-decoration-thickness:2px}button.svelte-1no2d6c:focus{color:#222;background-color:#fbc900;text-decoration-thickness:3px}.chart-subtitle.svelte-1clt1zg{margin-top:-6px;margin-bottom:10px}.layercake-container.svelte-vhzpsp,.layercake-container.svelte-vhzpsp *{box-sizing:border-box}.layercake-container.svelte-vhzpsp{width:100%;height:100%}svg.svelte-6sm8ei{position:absolute;width:100%;height:100%;overflow:visible}svg.svelte-6sm8ei *{vector-effect:non-scaling-stroke}svg.svelte-u84d8d{position:absolute;top:0;left:0;overflow:visible}div.svelte-1bu60uu,slot.svelte-1bu60uu{position:absolute;top:0;left:0} -------------------------------------------------------------------------------- /dist/apps/LineChart.css: -------------------------------------------------------------------------------- 1 | .chart-container.svelte-1fy6g2i{width:100%}.visuallyhidden.svelte-1fy6g2i{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.chart-title.svelte-pvs6ms{font-size:1.1em;font-weight:bold;margin-bottom:10px}path.svelte-rh3b33{fill:none;stroke-linejoin:round;stroke-linecap:round}.path-hover.svelte-rh3b33{stroke:rgba(255,255,255,0);stroke-width:7}.path-line.svelte-rh3b33,.path-overlay.svelte-rh3b33{pointer-events:none}.tick.svelte-1b8cddl.svelte-1b8cddl{font-size:.8em}.tick.svelte-1b8cddl .tick-mark.svelte-1b8cddl,.tick.tick-0.svelte-1b8cddl line.svelte-1b8cddl{stroke-dasharray:0}.tick.svelte-1b8cddl line.svelte-1b8cddl{shape-rendering:crispEdges}.dashed.svelte-1b8cddl.svelte-1b8cddl{stroke-dasharray:2}.axis.snapTicks.svelte-1b8cddl .tick:last-child text.svelte-1b8cddl{transform:translateX(3px)}.axis.snapTicks.svelte-1b8cddl .tick.tick-0 text.svelte-1b8cddl{transform:translateX(-3px)}.tick.svelte-f7wn4m.svelte-f7wn4m{font-size:.8em}.dashed.svelte-f7wn4m.svelte-f7wn4m{stroke-dasharray:2}.tick.tick-0.svelte-f7wn4m line.svelte-f7wn4m{stroke-dasharray:0}.chart-footer.svelte-16332wo{font-size:.8em;color:grey;margin-top:5px}.chart-subtitle.svelte-1clt1zg{margin-top:-6px;margin-bottom:10px}ul.legend.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{margin:0;padding:0}ul.legend.svelte-nqsavd li.svelte-nqsavd.svelte-nqsavd{display:inline;font-size:.8em}ul.legend.svelte-nqsavd li.svelte-nqsavd+li.svelte-nqsavd{margin-left:8px}.bullet.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{display:inline-block;vertical-align:middle;transform:translateY(-1px)}.round.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{border-radius:50%}.chart-label.svelte-dqkttw{font-size:0.8em}.chart-export.svelte-1no2d6c{margin-top:10px;font-size:.8em}button.svelte-1no2d6c{color:#206095;text-decoration:underline;text-underline-position:under;background:none;border:none;padding:0 0 1px}button.svelte-1no2d6c:hover{cursor:pointer;color:#003c57;text-decoration-thickness:2px}button.svelte-1no2d6c:focus{color:#222;background-color:#fbc900;text-decoration-thickness:3px}.layercake-container.svelte-vhzpsp,.layercake-container.svelte-vhzpsp *{box-sizing:border-box}.layercake-container.svelte-vhzpsp{width:100%;height:100%}svg.svelte-u84d8d{position:absolute;top:0;left:0;overflow:visible}svg.svelte-6sm8ei{position:absolute;width:100%;height:100%;overflow:visible}svg.svelte-6sm8ei *{vector-effect:non-scaling-stroke}div.svelte-1bu60uu,slot.svelte-1bu60uu{position:absolute;top:0;left:0} -------------------------------------------------------------------------------- /dist/apps/ScatterChart.css: -------------------------------------------------------------------------------- 1 | .chart-container.svelte-1fy6g2i{width:100%}.visuallyhidden.svelte-1fy6g2i{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}.tick.svelte-1b8cddl.svelte-1b8cddl{font-size:.8em}.tick.svelte-1b8cddl .tick-mark.svelte-1b8cddl,.tick.tick-0.svelte-1b8cddl line.svelte-1b8cddl{stroke-dasharray:0}.tick.svelte-1b8cddl line.svelte-1b8cddl{shape-rendering:crispEdges}.dashed.svelte-1b8cddl.svelte-1b8cddl{stroke-dasharray:2}.axis.snapTicks.svelte-1b8cddl .tick:last-child text.svelte-1b8cddl{transform:translateX(3px)}.axis.snapTicks.svelte-1b8cddl .tick.tick-0 text.svelte-1b8cddl{transform:translateX(-3px)}ul.legend.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{margin:0;padding:0}ul.legend.svelte-nqsavd li.svelte-nqsavd.svelte-nqsavd{display:inline;font-size:.8em}ul.legend.svelte-nqsavd li.svelte-nqsavd+li.svelte-nqsavd{margin-left:8px}.bullet.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{display:inline-block;vertical-align:middle;transform:translateY(-1px)}.round.svelte-nqsavd.svelte-nqsavd.svelte-nqsavd{border-radius:50%}.chart-title.svelte-pvs6ms{font-size:1.1em;font-weight:bold;margin-bottom:10px}.tick.svelte-f7wn4m.svelte-f7wn4m{font-size:.8em}.dashed.svelte-f7wn4m.svelte-f7wn4m{stroke-dasharray:2}.tick.tick-0.svelte-f7wn4m line.svelte-f7wn4m{stroke-dasharray:0}.chart-subtitle.svelte-1clt1zg{margin-top:-6px;margin-bottom:10px}.voronoi-cell.svelte-169satm{fill:none;stroke:none;pointer-events:all}.chart-footer.svelte-16332wo{font-size:.8em;color:grey;margin-top:5px}.chart-label.svelte-dqkttw{font-size:0.8em}.chart-export.svelte-1no2d6c{margin-top:10px;font-size:.8em}button.svelte-1no2d6c{color:#206095;text-decoration:underline;text-underline-position:under;background:none;border:none;padding:0 0 1px}button.svelte-1no2d6c:hover{cursor:pointer;color:#003c57;text-decoration-thickness:2px}button.svelte-1no2d6c:focus{color:#222;background-color:#fbc900;text-decoration-thickness:3px}.layercake-container.svelte-vhzpsp,.layercake-container.svelte-vhzpsp *{box-sizing:border-box}.layercake-container.svelte-vhzpsp{width:100%;height:100%}div.svelte-1bu60uu,slot.svelte-1bu60uu{position:absolute;top:0;left:0}svg.svelte-u84d8d{position:absolute;top:0;left:0;overflow:visible}svg.svelte-6sm8ei{position:absolute;width:100%;height:100%;overflow:visible}svg.svelte-6sm8ei *{vector-effect:non-scaling-stroke} -------------------------------------------------------------------------------- /dist/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ONSvisual/svelte-charts/9c609ba71f9668acd76e70b78c9d7b87ef6d6d3b/dist/favicon.png -------------------------------------------------------------------------------- /dist/global.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | position: relative; 3 | width: 100%; 4 | height: 100%; 5 | } 6 | 7 | body { 8 | color: #333; 9 | margin: 0; 10 | padding: 8px; 11 | box-sizing: border-box; 12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 13 | } 14 | 15 | a { 16 | color: rgb(0,100,200); 17 | text-decoration: none; 18 | } 19 | 20 | a:hover { 21 | text-decoration: underline; 22 | } 23 | 24 | a:visited { 25 | color: rgb(0,80,160); 26 | } 27 | 28 | label { 29 | display: block; 30 | } 31 | 32 | input, button, select, textarea { 33 | font-family: inherit; 34 | font-size: inherit; 35 | padding: 0.4em; 36 | margin: 0 0 0.5em 0; 37 | box-sizing: border-box; 38 | border: 1px solid #ccc; 39 | border-radius: 2px; 40 | } 41 | 42 | input:disabled { 43 | color: #ccc; 44 | } 45 | 46 | button { 47 | color: #333; 48 | background-color: #f4f4f4; 49 | outline: none; 50 | } 51 | 52 | button:disabled { 53 | color: #999; 54 | } 55 | 56 | button:not(:disabled):active { 57 | background-color: #ddd; 58 | } 59 | 60 | button:focus { 61 | border-color: #666; 62 | } -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ONS Svelte Charts Library 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /index.mjs: -------------------------------------------------------------------------------- 1 | // CHARTS 2 | export { default as Chart } from './src/charts/Chart.svelte'; 3 | export { default as LineChart } from './src/charts/LineChart.svelte'; 4 | export { default as BarChart } from './src/charts/BarChart.svelte'; 5 | export { default as ColumnChart } from './src/charts/ColumnChart.svelte'; 6 | export { default as ScatterChart } from './src/charts/ScatterChart.svelte'; 7 | export { default as DotPlotChart } from './src/charts/DotPlotChart.svelte'; 8 | 9 | // SHARED COMPONENTS 10 | // export { default as Annotations } from './src/charts/shared/Annotations.svelte'; 11 | export { default as Area } from './src/charts/shared/Area.svelte'; 12 | // export { default as ArrowheadDef } from './src/charts/shared/ArrowheadDef.svelte'; 13 | // export { default as Arrows } from './src/charts/shared/Arrows.svelte'; 14 | // export { default as AxisRadial } from './src/charts/shared/AxisRadial.svelte'; 15 | export { default as AxisX } from './src/charts/shared/AxisX.svelte'; 16 | // export { default as AxisX_html } from './src/charts/shared/AxisX.html.svelte'; 17 | export { default as AxisY } from './src/charts/shared/AxisY.svelte'; 18 | // export { default as AxisY_html } from './src/charts/shared/AxisY.html.svelte'; 19 | export { default as Bar } from './src/charts/shared/Bar.svelte'; 20 | // export { default as Beeswarm } from './src/charts/shared/Beeswarm.svelte'; 21 | // export { default as Beeswarm_html } from './src/charts/shared/Beeswarm.html.svelte'; 22 | // export { default as BeeswarmForce } from './src/charts/shared/BeeswarmForce.svelte'; 23 | // export { default as BeeswarmForce_html } from './src/charts/shared/BeeswarmForce.html.svelte'; 24 | // export { default as Brush } from './src/charts/shared/Brush.svelte'; 25 | // export { default as CalendarMonth } from './src/charts/shared/CalendarMonth.svelte'; 26 | // export { default as CirclePack_html } from './src/charts/shared/CirclePack.html.svelte'; 27 | // export { default as CirclePackForce } from './src/charts/shared/CirclePackForce.svelte'; 28 | export { default as DotPlot } from './src/charts/shared/DotPlot.svelte'; 29 | // export { default as DotPlot_html } from './src/charts/shared/DotPlot.html.svelte'; 30 | export { default as Column } from './src/charts/shared/Column.svelte'; 31 | // export { default as ColumnLinear } from './src/charts/shared/ColumnLinear.svelte'; 32 | export { default as Footer } from './src/charts/shared/Footer.svelte'; 33 | // export { default as ForceDirectedGraph } from './src/charts/shared/ForceDirectedGraph.svelte'; 34 | // export { default as Key } from './src/charts/shared/Key.svelte'; 35 | // export { default as Labels } from './src/charts/shared/Labels.svelte'; 36 | export { default as Legend } from './src/charts/shared/Legend.svelte'; 37 | export { default as Line } from './src/charts/shared/Line.svelte'; 38 | // export { default as Map_canvas } from './src/charts/shared/Map.canvas.svelte'; 39 | // export { default as Map_svg } from './src/charts/shared/Map.svg.svelte'; 40 | // export { default as MapPoints } from './src/charts/shared/MapPoints.svelte'; 41 | // export { default as MultiLine } from './src/charts/shared/MultiLine.svelte'; 42 | // export { default as QuadTree } from './src/charts/shared/QuadTree.svelte'; 43 | // export { default as QuadTree_perc } from './src/charts/shared/QuadTree.percent_range.svelte'; 44 | // export { default as Radar } from './src/charts/shared/Radar.svelte'; 45 | // export { default as Sankey } from './src/charts/shared/Sankey.svelte'; 46 | export { default as Scatter } from './src/charts/shared/Scatter.svg.svelte'; 47 | // export { default as Scatter_canvas } from './src/charts/shared/Scatter.canvas.svelte'; 48 | // export { default as Scatter_html } from './src/charts/shared/Scatter.html.svelte'; 49 | // export { default as Scatter_gl } from './src/charts/shared/Scatter.webgl.svelte'; 50 | // export { default as SharedTooltip } from './src/charts/shared/SharedTooltip.svelte'; 51 | // export { default as SharedTooltip_perc } from './src/charts/shared/SharedTooltip.percent-range.svelte'; 52 | // export { default as SmallMultipleWrapper } from './src/charts/shared/SmallMultipleWrapper.svelte'; 53 | // export { default as SmallMultipleWrapper_perc } from './src/charts/shared/SmallMultipleWrapper.percent-range.svelte'; 54 | // export { default as SyncedBrushWrapper } from './src/charts/shared/SyncedBrushWrapper.svelte'; 55 | // export { default as SyncedBrushWrapper_perc } from './src/charts/shared/SyncedBrushWrapper.percent-range.svelte'; 56 | // export { default as Tooltip } from './src/charts/shared/Tooltip.svelte'; 57 | export { default as Title } from './src/charts/shared/Title.svelte'; 58 | export { default as Voronoi } from './src/charts/shared/Voronoi.svelte'; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@onsvisual/svelte-charts", 3 | "version": "0.3.19", 4 | "description": "Reusable chart templates for Svelte projects.", 5 | "homepage": "http://onsvisual.github.io/svelte-charts", 6 | "module": "index.mjs", 7 | "svelte": "index.mjs", 8 | "files": [ 9 | "index.mjs", 10 | "src", 11 | "dist" 12 | ], 13 | "scripts": { 14 | "build": "rollup -c rollup.config.build.js", 15 | "dev": "rollup -c rollup.config.dev.js -w", 16 | "start": "sirv dist", 17 | "test": "echo \"No test specified\"", 18 | "deploy": "gh-pages -d dist" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+ssh://git@github.com/onsvisual/svelte-charts.git" 23 | }, 24 | "keywords": [ 25 | "svelte", 26 | "charts", 27 | "layercake", 28 | "dataviz", 29 | "svg" 30 | ], 31 | "author": "Ahmad Barclay", 32 | "license": "MIT", 33 | "bugs": { 34 | "url": "https://github.com/onsvisual/svelte-charts/issues" 35 | }, 36 | "devDependencies": { 37 | "@rollup/plugin-commonjs": "^11.0.0", 38 | "@rollup/plugin-dsv": "^2.0.0", 39 | "@rollup/plugin-json": "^4.1.0", 40 | "@rollup/plugin-node-resolve": "^15.0.1", 41 | "debounce": "^1.2.1", 42 | "gh-pages": "^6.2.0", 43 | "rollup": "^2.38.5", 44 | "rollup-plugin-css-only": "^3.1.0", 45 | "rollup-plugin-execute": "^1.1.1", 46 | "rollup-plugin-livereload": "^2.0.0", 47 | "rollup-plugin-svelte": "^7.1.0", 48 | "rollup-plugin-terser": "^7.0.2", 49 | "sirv-cli": "^1.0.0" 50 | }, 51 | "peerDependencies": { 52 | "d3-delaunay": "^6.0.2", 53 | "d3-dsv": "latest", 54 | "d3-quadtree": "latest", 55 | "d3-scale": "^4.0.2", 56 | "d3-time-format": "^4.1.0", 57 | "html2canvas": "^1.3.3", 58 | "layercake": "^7.6.1", 59 | "regl": "latest", 60 | "regl-tween": "latest", 61 | "svelte": "3 - 4" 62 | }, 63 | "main": "index.mjs", 64 | "directories": { 65 | "test": "test" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /rollup.config.build.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte'; 2 | import resolve from '@rollup/plugin-node-resolve'; 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | import dsv from '@rollup/plugin-dsv'; 5 | import { terser } from 'rollup-plugin-terser'; 6 | import json from "@rollup/plugin-json"; 7 | import css from 'rollup-plugin-css-only'; 8 | 9 | // List of modules to compile (must match .js filenames in src, eg. LineChart.js => LineChart) 10 | const modules = ['Chart', 'LineChart', 'BarChart', 'ColumnChart', 'ScatterChart', 'DotPlotChart']; 11 | 12 | export default [ 13 | ...modules.map(module => { 14 | return { 15 | input: `src/apps/${module}.js`, 16 | output: { 17 | sourcemap: true, 18 | format: 'iife', 19 | name: `${module}`, 20 | file: `dist/apps/${module}.js` 21 | }, 22 | plugins: [ 23 | svelte({ 24 | compilerOptions: { 25 | dev: false, 26 | hydratable: false 27 | } 28 | }), 29 | css({ output: `${module}.css` }), 30 | 31 | // If you have external dependencies installed from 32 | // npm, you'll most likely need these plugins. In 33 | // some cases you'll need additional configuration - 34 | // consult the documentation for details: 35 | // https://github.com/rollup/plugins/tree/master/packages/commonjs 36 | resolve({ 37 | browser: true, 38 | dedupe: ['svelte'] 39 | }), 40 | commonjs(), 41 | 42 | terser() 43 | ], 44 | watch: { 45 | clearScreen: false 46 | } 47 | } 48 | }), 49 | { 50 | input: 'src/main.js', 51 | output: { 52 | sourcemap: true, 53 | format: 'iife', 54 | name: 'app', 55 | file: 'dist/build/bundle.js' 56 | }, 57 | plugins: [ 58 | // Allow for importing csv files as modules 59 | dsv(), 60 | // And importing json files 61 | json(), 62 | 63 | svelte({ 64 | compilerOptions: { 65 | dev: false, 66 | hydratable: false 67 | } 68 | }), 69 | // we'll extract any component CSS out into 70 | // a separate file - better for performance 71 | css({ output: 'bundle.css' }), 72 | 73 | // If you have external dependencies installed from 74 | // npm, you'll most likely need these plugins. In 75 | // some cases you'll need additional configuration — 76 | // consult the documentation for details: 77 | // https://github.com/rollup/plugins/tree/master/packages/commonjs 78 | resolve({ 79 | browser: true, 80 | dedupe: importee => importee === 'svelte' || importee.startsWith('svelte/') 81 | }), 82 | commonjs(), 83 | 84 | terser() 85 | 86 | ], 87 | watch: { 88 | clearScreen: false 89 | } 90 | } 91 | ]; -------------------------------------------------------------------------------- /rollup.config.dev.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte'; 2 | import resolve from '@rollup/plugin-node-resolve'; 3 | import commonjs from '@rollup/plugin-commonjs'; 4 | import dsv from '@rollup/plugin-dsv'; 5 | import livereload from 'rollup-plugin-livereload'; 6 | import execute from "rollup-plugin-execute"; 7 | import json from "@rollup/plugin-json"; 8 | import css from 'rollup-plugin-css-only'; 9 | 10 | export default { 11 | input: 'src/main.js', 12 | output: { 13 | sourcemap: true, 14 | format: 'iife', 15 | name: 'app', 16 | file: 'dist/build/bundle.js' 17 | }, 18 | plugins: [ 19 | // Allow for importing csv files as modules 20 | dsv(), 21 | // And importing json files 22 | json(), 23 | 24 | svelte({ 25 | // enable run-time checks when not in production 26 | compilerOptions: { 27 | dev: true, 28 | hydratable: false 29 | } 30 | }), 31 | // we'll extract any component CSS out into 32 | // a separate file - better for performance 33 | css({ output: 'bundle.css' }), 34 | 35 | // If you have external dependencies installed from 36 | // npm, you'll most likely need these plugins. In 37 | // some cases you'll need additional configuration — 38 | // consult the documentation for details: 39 | // https://github.com/rollup/plugins/tree/master/packages/commonjs 40 | resolve({ 41 | browser: true, 42 | dedupe: importee => importee === 'svelte' || importee.startsWith('svelte/') 43 | }), 44 | commonjs(), 45 | 46 | // In dev mode, call `npm run start` once 47 | // the bundle has been generated 48 | serve(), 49 | 50 | // Watch the `public` directory and refresh the 51 | // browser on changes when not in production 52 | livereload('dist'), 53 | ], 54 | watch: { 55 | clearScreen: false 56 | } 57 | }; 58 | 59 | function serve() { 60 | let started = false; 61 | 62 | return { 63 | writeBundle() { 64 | if (!started) { 65 | started = true; 66 | 67 | require('child_process').spawn('npm', ['run', 'start', '--', '--dev'], { 68 | stdio: ['ignore', 'inherit', 'inherit'], 69 | shell: true 70 | }); 71 | } 72 | } 73 | }; 74 | } 75 | -------------------------------------------------------------------------------- /rollup.config.module.js: -------------------------------------------------------------------------------- 1 | import svelte from 'rollup-plugin-svelte'; 2 | import resolve from '@rollup/plugin-node-resolve'; 3 | import pkg from './package.json'; 4 | 5 | const name = pkg.name 6 | .replace(/^(@\S+\/)?(svelte-)?(\S+)/, '$3') 7 | .replace(/^\w/, m => m.toUpperCase()) 8 | .replace(/-\w/g, m => m[1].toUpperCase()); 9 | 10 | export default { 11 | input: './index.mjs', 12 | output: [ 13 | { file: pkg.module, 'format': 'es' }, 14 | { file: pkg.main, 'format': 'umd', name } 15 | ], 16 | plugins: [ 17 | svelte(), 18 | resolve() 19 | ] 20 | }; -------------------------------------------------------------------------------- /src/App.svelte: -------------------------------------------------------------------------------- 1 | 47 | 48 |
49 |
50 |

ONS Svelte Charts Library

51 |

Below are a series of charts built using Layer Cake, a responsive charts/graphics framework for Svelte. The charts are all responsive in that they fit to the width of their html container element, and will resize automatically when the screen/window is resized. The charts below can be used in the following ways:

52 |
    53 |
  1. The charts and/or their sub-components (axes etc) can be imported into Svelte projects via NPM.
  2. 54 |
  3. The charts also have compiled versions that can be embedded in "vanilla" javascript projects.
  4. 55 |
  5. This whole repository can be downloaded/cloned and hacked to create variations of the charts.
  6. 56 |
57 |
58 | 59 | 60 | 61 |
62 |
63 |
64 | 65 |
66 |
67 |
68 | d.group == barchart1.selected)} 70 | xKey="value" yKey="year" 71 | title="Single variable bar chart" 72 | footer="Source: Fictitious data about fruit, 2020." 73 | {hover} {hovered} on:hover={doHover} 74 | {select} {selected} on:select={doSelect} 75 | {animation}> 76 |
77 | {#each barchart1.options as option} 78 | 79 | {/each} 80 |
81 |
82 |
83 |
84 | 92 |
93 | {#each barchart2.options as option} 94 | 95 | {/each} 96 |
97 |
98 |
99 |
100 | d.year == 2020)} xKey="value" yKey="group" zKey="group" title="Coloured bar chart with export options" output={{csv: true, png: true}}/> 101 |
102 |
103 | d.group == barchart1.selected)} 105 | xKey="year" yKey="value" 106 | title="Single variable column chart" 107 | {hover} {hovered} on:hover={doHover} 108 | {select} {selected} on:select={doSelect} 109 | {animation}> 110 |
111 | {#each barchart1.options as option} 112 | 113 | {/each} 114 |
115 |
116 |
117 |
118 | 126 |
127 | {#each barchart2.options as option} 128 | 129 | {/each} 130 |
131 |
132 |
133 |
134 | d.year == 2020)} xKey="group" yKey="value" zKey="group" title="Coloured column chart with export options" output={{csv: true, png: true}}/> 135 |
136 |
137 | d.group == barchart1.selected)} xKey="year" yKey="value" area={true} areaOpacity={0.3} title="Line chart with area" animation={animation} > 138 |
139 | {#each barchart1.options as option} 140 | 141 | {/each} 142 |
143 |
144 |
145 |
146 | 152 |
153 | 154 | 155 | 156 | 157 |
158 |
159 |
160 |
161 | 172 |
173 | 174 |
175 | ({ 177 | year: new Date(`${d.year}`), 178 | value: d.value, 179 | group: d.group 180 | }))} 181 | xKey="year" yKey="value" zKey="group" 182 | color="grey" lineWidth={1} 183 | area={false} 184 | xScale="time" 185 | xFormatTickString='%b %y' 186 | title="Line chart with time data" 187 | padding={{ top: 0, bottom: 28, left: 35, right: 60 }} 188 | {animation} labels 189 | {hover} {select} 190 | snapTicks={false}/> 191 |
192 | 193 |
194 | 202 |
203 |
204 | 210 |
211 | 212 | 213 | 214 |
215 |
216 |
217 |
218 | 225 | 226 |
227 | {#if false} 228 |
229 | 230 |
231 | {/if} 232 |
233 |
234 | 235 |
236 |
237 |

Current features

238 |

In all use cases, each top-level chart component (eg. bar chart, scatter chart) is able to take a series of parameters (data, colours, title, etc) to initialise and customise it.

239 |

As far as possible these parameters are consistent across chart types, and generally there are sensible default values for all parameters (eg. height = 300 pixels) other than "data", which always requires a Tidy Data array.

240 |

Examples of usage

241 |

You can see examples of how these charts can be used in a Svelte project in this REPL, and how they can be used in a plain/vanilla javascript project in this CodePen.

242 |

Intended features

243 |

It is the intention to translate all of the most useful charts from Layer Cake's examples into components, as well as a number of other custom charts (eg. spine charts). For each of the charts, it is the intention to support the following functionalities:

244 | 256 |

Acknowledgement

257 |

These charts are largely based on the chart examples by Michael Keller, the author of the Layer Cake charts/graphics framework for Svelte.

259 |
260 |
261 | 262 | 304 | -------------------------------------------------------------------------------- /src/apps/BarChart.js: -------------------------------------------------------------------------------- 1 | export { default } from '../charts/BarChart.svelte'; -------------------------------------------------------------------------------- /src/apps/Chart.js: -------------------------------------------------------------------------------- 1 | export { default } from '../charts/Chart.svelte'; -------------------------------------------------------------------------------- /src/apps/ColumnChart.js: -------------------------------------------------------------------------------- 1 | export { default } from '../charts/ColumnChart.svelte'; -------------------------------------------------------------------------------- /src/apps/DotPlotChart.js: -------------------------------------------------------------------------------- 1 | export { default } from '../charts/DotPlotChart.svelte'; -------------------------------------------------------------------------------- /src/apps/LineChart.js: -------------------------------------------------------------------------------- 1 | export { default } from '../charts/LineChart.svelte'; -------------------------------------------------------------------------------- /src/apps/ScatterChart.js: -------------------------------------------------------------------------------- 1 | export { default } from '../charts/ScatterChart.svelte'; -------------------------------------------------------------------------------- /src/charts/BarChart.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 126 | 127 |
128 | {#if title} 129 | {title} 130 | {/if} 131 | {#if subtitle} 132 | {subtitle} 133 | {/if} 134 | {#if alt} 135 |
{alt}
136 | {/if} 137 | 138 | 184 | {#if table} 185 |
186 | 187 | 188 | {/if} 189 | 190 | {#if legend && _zDomain} 191 | 192 | {/if} 193 | {#if footer} 194 |
{footer}
195 | {/if} 196 | 197 | {#if output} 198 | 199 | {/if} 200 | 201 | -------------------------------------------------------------------------------- /src/charts/Chart.svelte: -------------------------------------------------------------------------------- 1 | 67 | 68 | {#if props} 69 | {#key props.data} 70 | {#if type.toLowerCase() === "bar" && props.xKey && props.yKey} 71 | 72 | {:else if type.toLowerCase() === "bar-highlight" && props.xKey && props.yKey} 73 | 74 | {:else if type.toLowerCase() === "column" && props.xKey && props.yKey} 75 | 76 | {:else if type.toLowerCase() === "column-highlight" && props.xKey && props.yKey} 77 | 78 | {:else if type.toLowerCase() === "line" && props.xKey && props.yKey} 79 | 80 | {:else if type.toLowerCase() === "line-highlight" && props.xKey && props.yKey} 81 | 82 | {:else if type.toLowerCase() === "scatter" && props.xKey} 83 | 84 | {:else if type.toLowerCase() === "scatter-highlight" && props.xKey} 85 | 86 | {:else if type.toLowerCase() === "dotplot" && props.xKey && props.yKey} 87 | 88 | {/if} 89 | {/key} 90 | {/if} 91 | -------------------------------------------------------------------------------- /src/charts/ColumnChart.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 120 | 121 |
122 | {#if title} 123 | {title} 124 | {/if} 125 | {#if subtitle} 126 | {subtitle} 127 | {/if} 128 | {#if alt} 129 |
{alt}
130 | {/if} 131 | 132 | 178 | {#if table} 179 |
180 |
181 | 182 | {/if} 183 | 184 | {#if legend && _zDomain} 185 | 186 | {/if} 187 | {#if footer} 188 |
{footer}
189 | {/if} 190 | 191 | {#if output} 192 | 193 | {/if} 194 | 195 | -------------------------------------------------------------------------------- /src/charts/DotPlotChart.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 111 | 112 |
113 | {#if title} 114 | {title} 115 | {/if} 116 | {#if subtitle} 117 | {subtitle} 118 | {/if} 119 | {#if alt} 120 |
{alt}
121 | {/if} 122 | 123 | 171 | {#if table} 172 |
173 |
174 | 175 | {/if} 176 | 177 | {#if false && legend && _zDomain} 178 | 179 | {/if} 180 | {#if footer} 181 |
{footer}
182 | {/if} 183 | 184 | {#if output} 185 | 186 | {/if} 187 | 188 | -------------------------------------------------------------------------------- /src/charts/LineChart.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 132 | 133 |
134 | {#if title} 135 | {title} 136 | {/if} 137 | {#if subtitle} 138 | {subtitle} 139 | {/if} 140 | {#if alt} 141 |
{alt}
142 | {/if} 143 | 144 | 197 | {#if table} 198 |
199 |
200 | 201 | {/if} 202 | 203 | {#if legend && _zDomain} 204 | 205 | {/if} 206 | {#if footer} 207 |
{footer}
208 | {/if} 209 | 210 | {#if output} 211 | 212 | {/if} 213 | 214 | -------------------------------------------------------------------------------- /src/charts/MarkerChart.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 50 | 51 | {#if title} 52 | {title} 53 | {/if} 54 | {#if subtitle} 55 | {subtitle} 56 | {/if} 57 | {#if alt} 58 |
{alt}
59 | {/if} 60 | 61 | 94 | 95 | {#if footer} 96 |
{footer}
97 | {/if} 98 | 99 | -------------------------------------------------------------------------------- /src/charts/ScatterChart.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 124 | 125 |
126 | {#if title} 127 | {title} 128 | {/if} 129 | {#if subtitle} 130 | {subtitle} 131 | {/if} 132 | {#if alt} 133 |
{alt}
134 | {/if} 135 | 136 | 192 | {#if table} 193 |
194 |
195 | 196 | {/if} 197 | 198 | {#if legend && _zDomain} 199 | 200 | {/if} 201 | {#if footer} 202 |
{footer}
203 | {/if} 204 | 205 | {#if output} 206 | 207 | {/if} 208 | 209 | -------------------------------------------------------------------------------- /src/charts/shared/Annotations.svelte: -------------------------------------------------------------------------------- 1 | 23 | 24 |
25 | {#each annotations as d, i} 26 |
{d.text}
31 | {/each} 32 |
33 | 34 | 39 | 40 | -------------------------------------------------------------------------------- /src/charts/shared/Area.svelte: -------------------------------------------------------------------------------- 1 | 35 | 36 | {#if $coords} 37 | 38 | {#each $coords as group, i} 39 | 40 | {/each} 41 | 42 | {/if} 43 | -------------------------------------------------------------------------------- /src/charts/shared/ArrowheadDef.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/charts/shared/Arrows.svelte: -------------------------------------------------------------------------------- 1 | 70 | 71 | 72 | {#if annotations.length} 73 | 74 | {#each annotations as anno, i} 75 | {#if anno.arrows} 76 | {#each anno.arrows as arrow} 77 | 80 | {/each} 81 | {/if} 82 | {/each} 83 | 84 | {/if} 85 | 86 | 98 | -------------------------------------------------------------------------------- /src/charts/shared/AxisRadial.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | 26 | 35 | 43 | 44 | {#each $config.x as label, i} 45 | 54 | 55 | {label} 61 | {/each} 62 | 63 | -------------------------------------------------------------------------------- /src/charts/shared/AxisX.html.svelte: -------------------------------------------------------------------------------- 1 | 26 | 27 |
28 | {#each tickVals as tick, i} 29 | {#if gridlines !== false} 30 |
31 | {/if} 32 | {#if tickMarks === true} 33 |
34 | {/if} 35 |
38 |
{i == tickVals.length - 1 ? prefix + formatTick(tick) + suffix : formatTick(tick)}
41 |
42 | {/each} 43 | {#if baseline === true} 44 |
45 | {/if} 46 |
47 | 48 | 89 | -------------------------------------------------------------------------------- /src/charts/shared/AxisX.svelte: -------------------------------------------------------------------------------- 1 | 62 | 63 | 64 | {#each tickVals as tick, i} 65 | 66 | {#if gridlines !== false} 67 | 68 | {/if} 69 | {#if tickMarks === true} 70 | 71 | {/if} 72 | 79 | {i == tickVals.length - 1 ? prefix + formatTick(tick) + suffix : formatTick(tick)} 80 | 81 | 82 | {/each} 83 | 84 | 85 | 110 | -------------------------------------------------------------------------------- /src/charts/shared/AxisY.html.svelte: -------------------------------------------------------------------------------- 1 | 28 | 29 |
30 | {#each tickVals as tick, i} 31 |
32 | {#if gridlines !== false} 33 |
34 | {/if} 35 | {#if baseline !== false && i === 0} 36 |
37 | {/if} 38 | {#if tickMarks === true} 39 |
40 | {/if} 41 |
{i == tickVals.length - 1 ? prefix + formatTick(tick) + suffix : formatTick(tick)}
49 |
50 | {/each} 51 |
52 | 53 | 86 | -------------------------------------------------------------------------------- /src/charts/shared/AxisY.svelte: -------------------------------------------------------------------------------- 1 | 39 | 40 | 41 | {#each tickVals as tick, i} 42 | 43 | {#if gridlines !== false} 44 | 55 | {/if} 56 | {#if tickMarks === true} 57 | 67 | {/if} 68 | 74 | {i == tickVals.length - 1 ? prefix + formatTick(tick) + suffix : formatTick(tick)} 75 | 76 | 77 | {/each} 78 | 79 | 80 | 93 | -------------------------------------------------------------------------------- /src/charts/shared/Bar.svelte: -------------------------------------------------------------------------------- 1 | 60 | 61 | {#if $coords} 62 | {#if mode === "confidence"} 63 | 64 | {#each mapData($coords) as d, i} 65 | 72 | {/each} 73 | 74 | {/if} 75 | 76 | {#each $coords as group, i} 77 | {#each group as d, j} 78 | {#if !(mode === 'confidence' && i > 0)} 79 | 80 | 81 | doHover(e, $data[i][j]) : null} 90 | on:mouseleave={hover ? (e) => doHover(e, null) : null} 91 | on:focus={select ? (e) => doHover(e, $data[i][j]) : null} 92 | on:blur={select ? (e) => doHover(e, null) : null} 93 | on:click={select ? (e) => doSelect(e, $data[i][j]) : null} 94 | tabindex="{hover || select ? 0 : -1}" 95 | /> 96 | {/if} 97 | {/each} 98 | {/each} 99 | 100 | {/if} 101 | 102 | -------------------------------------------------------------------------------- /src/charts/shared/Beeswarm.html.svelte: -------------------------------------------------------------------------------- 1 | 57 | 58 |
59 | {#each circles as d} 60 |
72 | {$custom.getTitle(d)} 73 |
74 | {/each} 75 |
76 | 77 | 85 | -------------------------------------------------------------------------------- /src/charts/shared/Beeswarm.svelte: -------------------------------------------------------------------------------- 1 | 57 | 58 | 59 | {#each circles as d} 60 | 68 | {$custom.getTitle(d)} 69 | 70 | {/each} 71 | 72 | -------------------------------------------------------------------------------- /src/charts/shared/BeeswarmForce.html.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 |
32 | {#each simulation.nodes() as node} 33 |
45 | {$custom.getTitle(node)} 46 |
47 | {/each} 48 |
49 | 50 | 58 | -------------------------------------------------------------------------------- /src/charts/shared/BeeswarmForce.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 | 32 | {#each simulation.nodes() as node} 33 | 41 | {$custom.getTitle(node)} 42 | 43 | {/each} 44 | 45 | -------------------------------------------------------------------------------- /src/charts/shared/Brush.svelte: -------------------------------------------------------------------------------- 1 | 86 | 87 |
88 | {#if min !== null} 89 |
90 |
91 |
92 | {/if} 93 |
94 | 95 | 128 | -------------------------------------------------------------------------------- /src/charts/shared/CalendarMonth.svelte: -------------------------------------------------------------------------------- 1 | 59 | 60 | {#each days as day} 61 | 70 | {/each} 71 | 72 | 79 | -------------------------------------------------------------------------------- /src/charts/shared/CirclePack.html.svelte: -------------------------------------------------------------------------------- 1 | 62 | 63 |
64 | {#each descendants as d} 65 |
70 |
74 |
87 |
{titleCase(d.data.id)}
88 | {#if d.data.data[valueKey]} 89 |
{commas(d.data.data[valueKey])}
90 | {/if} 91 |
92 |
93 | {/each} 94 |
95 | 96 | 156 | -------------------------------------------------------------------------------- /src/charts/shared/CirclePackForce.svelte: -------------------------------------------------------------------------------- 1 | 60 | {#each nodes as point} 61 | 70 | 71 | 72 | {/each} 73 | -------------------------------------------------------------------------------- /src/charts/shared/Column.svelte: -------------------------------------------------------------------------------- 1 | 60 | 61 | {#if $coords} 62 | {#if mode === "confidence"} 63 | 64 | {#each mapData($coords) as d, i} 65 | 72 | {/each} 73 | 74 | {/if} 75 | 76 | {#each $coords as group, i} 77 | {#each group as d, j} 78 | {#if !(mode === 'confidence' && i > 0)} 79 | 80 | 81 | doHover(e, $data[i][j]) : null} 90 | on:mouseleave={hover ? (e) => doHover(e, null) : null} 91 | on:focus={select ? (e) => doHover(e, $data[i][j]) : null} 92 | on:blur={select ? (e) => doHover(e, null) : null} 93 | on:click={select ? (e) => doSelect(e, $data[i][j]) : null} 94 | tabindex="{hover || select ? 0 : -1}" 95 | /> 96 | {/if} 97 | {/each} 98 | {/each} 99 | 100 | {/if} 101 | 102 | -------------------------------------------------------------------------------- /src/charts/shared/ColumnLinear.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | {#each $data as d, i} 24 | 35 | {/each} 36 | -------------------------------------------------------------------------------- /src/charts/shared/DotPlot.html.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 |
13 | {#each $data as row} 14 |
15 |
23 | 24 | {#each $xGet(row) as circleX, i} 25 |
35 | {/each} 36 |
37 | {/each} 38 |
39 | 40 | 53 | -------------------------------------------------------------------------------- /src/charts/shared/DotPlot.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | {#if $coords} 23 | 24 | {#each $coords.map(d => ({x: d.map(e => e.x), y: d[0].y})) as d} 25 | 33 | {/each} 34 | 35 | 36 | {#each $coords as group, i} 37 | {#each group as d, j} 38 | 44 | {/each} 45 | {/each} 46 | 47 | {#if idKey && (hovered || selected || highlighted[0])} 48 | {#each $coords as group, i} 49 | {#each group as d, j} 50 | {#if [...highlighted, selected, hovered].includes($data[i][j][idKey])} 51 | 60 | {/if} 61 | {/each} 62 | {/each} 63 | {/if} 64 | 65 | {/if} 66 | -------------------------------------------------------------------------------- /src/charts/shared/Export.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | {#if output.csv} 13 | 14 | {/if} 15 | {#if output.csv && output.png}|{/if} 16 | {#if output.png} 17 | 18 | {/if} 19 |
20 | 21 | -------------------------------------------------------------------------------- /src/charts/shared/Footer.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/charts/shared/ForceDirectedGraph.svelte: -------------------------------------------------------------------------------- 1 | 68 | {#each links as link} 69 | 70 | 76 | {$x(link.source)} 77 | 78 | 79 | {/each} 80 | 81 | {#each nodes as point} 82 | 91 | {$x(point)} 92 | 93 | {/each} 94 | -------------------------------------------------------------------------------- /src/charts/shared/Key.svelte: -------------------------------------------------------------------------------- 1 | 37 | 38 | 68 | 69 |
70 | {#each $zDomain as item} 71 |
72 |
79 |
{displayName(item)}
80 |
81 | {/each} 82 |
83 | -------------------------------------------------------------------------------- /src/charts/shared/Labels-html.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | {#each $data as group} 20 |
{cap(group.key)}
27 | {/each} 28 | 29 | 36 | -------------------------------------------------------------------------------- /src/charts/shared/Labels.svelte: -------------------------------------------------------------------------------- 1 | 54 | 55 | {#if coordsWithLabels} 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | {#if coordsWithLabels?.[0]?.x} 67 | {#each coordsWithLabels as d, i} 68 | {#if labelAll || [hovered, selected].includes($data[i][idKey])} 69 | 81 | {content ? content : $data[i][labelKey]} 82 | 83 | {/if} 84 | {/each} 85 | {:else if coordsWithLabels?.[0]?.[0]?.x} 86 | {#each coordsWithLabels as d, i} 87 | {#if labelAll || [hovered, selected].includes($data[i][0][idKey])} 88 | {#if marker} 89 | 102 | {/if} 103 | 122 | {content ? content : $data[i][0][labelKey]} 123 | 124 | {/if} 125 | {/each} 126 | {/if} 127 | 128 | {/if} 129 | 130 | 135 | -------------------------------------------------------------------------------- /src/charts/shared/Legend.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | {#if Array.isArray(_domain) && Array.isArray(colors)} 16 | 31 | {/if} 32 | 33 | -------------------------------------------------------------------------------- /src/charts/shared/Line.svelte: -------------------------------------------------------------------------------- 1 | 55 | 56 | {#if $coords} 57 | 58 | {#each $coords as group, i} 59 | 60 | 61 | doHover(e, $data[i]) : null} 65 | on:mouseleave={hover ? (e) => doHover(e, null) : null} 66 | on:focus={select ? (e) => doHover(e, $data[i]) : null} 67 | on:blur={select ? (e) => doHover(e, null) : null} 68 | on:click={select ? (e) => doSelect(e, $data[i]) : null} 69 | tabindex="{hover || select ? 0 : -1}" 70 | /> 71 | 80 | {/each} 81 | 82 | {#if idKey && (hover || selected || highlighted[0])} 83 | {#each $coords as group, i} 84 | {#if [hovered, selected, ...highlighted].includes($data[i][0][idKey]) } 85 | 98 | {/if} 99 | {/each} 100 | {/if} 101 | 102 | {/if} 103 | 104 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /src/charts/shared/Map.canvas.svelte: -------------------------------------------------------------------------------- 1 | 55 | -------------------------------------------------------------------------------- /src/charts/shared/Map.svg.svelte: -------------------------------------------------------------------------------- 1 | 53 | 54 | dispatch('mouseout')} 57 | > 58 | {#each features as feature} 59 | dispatch('mousemove', { e, props: feature.properties })} 66 | on:mousemove={handleMousemove(feature)} 67 | > 68 | {/each} 69 | 70 | 71 | 81 | -------------------------------------------------------------------------------- /src/charts/shared/MapPoints.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | {#each pointsData as d} 20 | 25 | 26 | {/each} 27 | 28 | 29 | 36 | -------------------------------------------------------------------------------- /src/charts/shared/MultiLine.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | {#each $data as group} 17 | 22 | {/each} 23 | 24 | 25 | 33 | -------------------------------------------------------------------------------- /src/charts/shared/QuadTree.percent-range.svelte: -------------------------------------------------------------------------------- 1 | 40 | 41 | 50 | 51 |
56 | 63 | -------------------------------------------------------------------------------- /src/charts/shared/QuadTree.svelte: -------------------------------------------------------------------------------- 1 | 36 | 37 | 46 | 47 |
52 | 59 | -------------------------------------------------------------------------------- /src/charts/shared/Radar.svelte: -------------------------------------------------------------------------------- 1 | 29 | 30 | 33 | {#each $data as row} 34 | 35 | 42 | 43 | 44 | {#each $xGet(row) as circleR, i} 45 | 53 | {/each} 54 | {/each} 55 | 56 | 57 | 64 | -------------------------------------------------------------------------------- /src/charts/shared/Sankey.svelte: -------------------------------------------------------------------------------- 1 | 34 | 35 | 40 | 41 | 42 | 43 | {#each sankeyData.links as d} 44 | 50 | {/each} 51 | 52 | 53 | {#each sankeyData.nodes as d, i} 54 | 60 | 67 | {d.id} 68 | 69 | {/each} 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/charts/shared/Scatter.canvas.svelte: -------------------------------------------------------------------------------- 1 | 35 | -------------------------------------------------------------------------------- /src/charts/shared/Scatter.html.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
15 | {#each $data as d} 16 |
27 | {/each} 28 |
29 | 30 | 37 | -------------------------------------------------------------------------------- /src/charts/shared/Scatter.svg.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 | {#if $coords} 21 | 22 | {#each $coords as d, i} 23 | 30 | {/each} 31 | 32 | {#if idKey && (hovered || selected || highlighted[0])} 33 | {#each $coords as d, i} 34 | {#if [...highlighted, selected, hovered].includes($data[i][idKey])} 35 | 44 | {/if} 45 | {/each} 46 | {/if} 47 | 48 | {/if} 49 | -------------------------------------------------------------------------------- /src/charts/shared/Scatter.webgl.svelte: -------------------------------------------------------------------------------- 1 | 154 | -------------------------------------------------------------------------------- /src/charts/shared/SetCoords.svelte: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/charts/shared/SharedTooltip.percent-range.svelte: -------------------------------------------------------------------------------- 1 | 37 | 38 | 68 | 69 | 78 | {#if visible === true} 79 |
82 |
90 |
{formatTitle(found[$config.x])}
91 | {#each sortResult(found) as row} 92 |
{formatKey(row.key)}: {formatValue(row.value)}
93 | {/each} 94 |
95 | {/if} 96 |
97 | 98 | -------------------------------------------------------------------------------- /src/charts/shared/SharedTooltip.svelte: -------------------------------------------------------------------------------- 1 | 37 | 38 | 69 | 70 | 79 | {#if visible === true} 80 |
83 |
91 |
{formatTitle(found[$config.x])}
92 | {#each sortResult(found) as row} 93 |
{formatKey(row.key)}: {formatValue(row.value)}
94 | {/each} 95 |
96 | {/if} 97 |
98 | 99 | -------------------------------------------------------------------------------- /src/charts/shared/SmallMultipleWrapper.percent-range.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 | 38 | 39 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/charts/shared/SmallMultipleWrapper.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 | 36 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/charts/shared/Stack.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | {#each $points as d, i} 27 | {#if i < $points.length - 1} 28 | 37 | {/if} 38 | {/each} 39 | 40 | -------------------------------------------------------------------------------- /src/charts/shared/Subtitle.svelte: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 | -------------------------------------------------------------------------------- /src/charts/shared/SyncedBrushWrapper.percent-range.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | 45 | 46 |
47 |
48 | 57 | 58 | { 60 | const filtered = ticks.filter(t => t % 1 === 0); 61 | if (filtered.length > 7) { 62 | return filtered.filter((t, i) => i % 2 === 0); 63 | } 64 | return filtered; 65 | }} 66 | /> 67 | 70 | 71 | 72 | 75 | 78 | 79 | 80 |
81 | 82 |
83 | 92 | 93 | 96 | 99 | 100 | 101 | 105 | 106 | 107 |
108 |
109 | -------------------------------------------------------------------------------- /src/charts/shared/SyncedBrushWrapper.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | 45 | 46 |
47 |
48 | 55 | 56 | { 58 | const filtered = ticks.filter(t => t % 1 === 0); 59 | if (filtered.length > 7) { 60 | return filtered.filter((t, i) => i % 2 === 0); 61 | } 62 | return filtered; 63 | }} 64 | /> 65 | 68 | 71 | 74 | 75 | 76 |
77 | 78 |
79 | 86 | 87 | 90 | 93 | 94 | 95 | 99 | 100 | 101 |
102 |
103 | -------------------------------------------------------------------------------- /src/charts/shared/Table.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | {#if data && keys} 20 |
21 | 22 | 23 | {#each keys as key} 24 | 25 | {/each} 26 | 27 | 28 | 29 | {#each data as d} 30 | 31 | {#each keys as key} 32 | 33 | {/each} 34 | 35 | {/each} 36 | 37 |
{key}
{d[key]}
38 | {/if} -------------------------------------------------------------------------------- /src/charts/shared/Title.svelte: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | -------------------------------------------------------------------------------- /src/charts/shared/Tooltip.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 18 | 19 | {#if evt.detail} 20 |
27 | 28 |
29 | {/if} 30 | -------------------------------------------------------------------------------- /src/charts/shared/Voronoi.svelte: -------------------------------------------------------------------------------- 1 | 42 | 43 | 50 | 51 | {#if voronoi} 52 | 53 | {#each $data as d, i} 54 | 55 | 56 | doHover(e, $data[i]) : null} 60 | on:mouseleave={hover ? e => doHover(e, null) : null} 61 | on:focus={select ? e => doHover(e, $data[i]): null} 62 | on:blur={select ? e => doHover(e, null) : null} 63 | on:click={select ? e => doSelect(e, $data[i]) : null} 64 | tabindex="{hover || select ? 0 : -1}" 65 | /> 66 | {/each} 67 | 68 | {/if} 69 | -------------------------------------------------------------------------------- /src/charts/shared/ss/AreaStacked.svelte: -------------------------------------------------------------------------------- 1 | 32 | 33 | 34 | {#each groups as group, i} 35 | 36 | {/each} 37 | 38 | -------------------------------------------------------------------------------- /src/charts/shared/ss/BarStacked.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | {#each groups as group, i} 19 | {#each group as d, j} 20 | 29 | {/each} 30 | {/each} 31 | 32 | -------------------------------------------------------------------------------- /src/charts/shared/ss/ColumnStacked.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | {#each groups as group, i} 16 | {#each group as d, j} 17 | 26 | {/each} 27 | {/each} 28 | 29 | -------------------------------------------------------------------------------- /src/charts/shared/ss/LineStacked.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | {#each groups as group} 25 | 31 | {/each} 32 | 33 | 34 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/data/data-scatter.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | year: 1979, 4 | value: 7.19, 5 | alt: 12, 6 | group: 'apples' 7 | }, 8 | { 9 | year: 1980, 10 | value: 7.83, 11 | alt: 15, 12 | group: 'apples' 13 | }, 14 | { 15 | year: 1981, 16 | value: 7.24, 17 | alt: 17, 18 | group: 'apples' 19 | }, 20 | { 21 | year: 1982, 22 | value: 7.44, 23 | alt: 28, 24 | group: 'apples' 25 | }, 26 | { 27 | year: 1983, 28 | value: 7.51, 29 | alt: 23, 30 | group: 'apples' 31 | }, 32 | { 33 | year: 1984, 34 | value: 7.1, 35 | alt: 17, 36 | group: 'apples' 37 | }, 38 | { 39 | year: 1985, 40 | value: 6.91, 41 | alt: 11, 42 | group: 'apples' 43 | }, 44 | { 45 | year: 1986, 46 | value: 7.53, 47 | alt: 13, 48 | group: 'apples' 49 | }, 50 | { 51 | year: 1987, 52 | value: 7.47, 53 | alt: 18, 54 | group: 'apples' 55 | }, 56 | { 57 | year: 1988, 58 | value: 7.48, 59 | alt: 20, 60 | group: 'apples' 61 | }, 62 | { 63 | year: 1989, 64 | value: 7.03, 65 | alt: 24, 66 | group: 'apples' 67 | }, 68 | { 69 | year: 1990, 70 | value: 6.23, 71 | alt: 26, 72 | group: 'bananas' 73 | }, 74 | { 75 | year: 1991, 76 | value: 6.54, 77 | alt: 16, 78 | group: 'bananas' 79 | }, 80 | { 81 | year: 1992, 82 | value: 7.54, 83 | alt: 25, 84 | group: 'bananas' 85 | }, 86 | { 87 | year: 1993, 88 | value: 6.5, 89 | alt: 28, 90 | group: 'bananas' 91 | }, 92 | { 93 | year: 1994, 94 | value: 7.18, 95 | alt: 11, 96 | group: 'bananas' 97 | }, 98 | { 99 | year: 1995, 100 | value: 6.12, 101 | alt: 19, 102 | group: 'bananas' 103 | }, 104 | { 105 | year: 1996, 106 | value: 7.87, 107 | alt: 14, 108 | group: 'bananas' 109 | }, 110 | { 111 | year: 1997, 112 | value: 6.73, 113 | alt: 22, 114 | group: 'bananas' 115 | }, 116 | { 117 | year: 1998, 118 | value: 6.55, 119 | alt: 13, 120 | group: 'bananas' 121 | }, 122 | { 123 | year: 1999, 124 | value: 6.23, 125 | alt: 30, 126 | group: 'bananas' 127 | }, 128 | { 129 | year: 2000, 130 | value: 6.31, 131 | alt: 27, 132 | group: 'bananas' 133 | }, 134 | { 135 | year: 2001, 136 | value: 6.74, 137 | alt: 13, 138 | group: 'cherries' 139 | }, 140 | { 141 | year: 2002, 142 | value: 5.95, 143 | alt: 18, 144 | group: 'cherries' 145 | }, 146 | { 147 | year: 2003, 148 | value: 6.13, 149 | alt: 15, 150 | group: 'cherries' 151 | }, 152 | { 153 | year: 2004, 154 | value: 6.04, 155 | alt: 11, 156 | group: 'cherries' 157 | }, 158 | { 159 | year: 2005, 160 | value: 5.56, 161 | alt: 29, 162 | group: 'cherries' 163 | }, 164 | { 165 | year: 2006, 166 | value: 5.91, 167 | alt: 26, 168 | group: 'cherries' 169 | }, 170 | { 171 | year: 2007, 172 | value: 4.29, 173 | alt: 10, 174 | group: 'cherries' 175 | }, 176 | { 177 | year: 2008, 178 | value: 4.72, 179 | alt: 14, 180 | group: 'cherries' 181 | }, 182 | { 183 | year: 2009, 184 | value: 5.38, 185 | alt: 21, 186 | group: 'cherries' 187 | }, 188 | { 189 | year: 2010, 190 | value: 4.92, 191 | alt: 20, 192 | group: 'cherries' 193 | }, 194 | { 195 | year: 2011, 196 | value: 4.61, 197 | alt: 24, 198 | group: 'dates' 199 | }, 200 | { 201 | year: 2012, 202 | value: 3.62, 203 | alt: 19, 204 | group: 'dates' 205 | }, 206 | { 207 | year: 2013, 208 | value: 5.35, 209 | alt: 12, 210 | group: 'dates' 211 | }, 212 | { 213 | year: 2014, 214 | value: 5.28, 215 | alt: 13, 216 | group: 'dates' 217 | }, 218 | { 219 | year: 2015, 220 | value: 4.63, 221 | alt: 28, 222 | group: 'dates' 223 | }, 224 | { 225 | year: 2016, 226 | value: 4.72, 227 | alt: 30, 228 | group: 'dates' 229 | } 230 | ]; -------------------------------------------------------------------------------- /src/data/data.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | year: 2017, 4 | value: 480, 5 | group: 'apples' 6 | }, 7 | { 8 | year: 2017, 9 | value: 320, 10 | group: 'bananas' 11 | }, 12 | { 13 | year: 2017, 14 | value: 640, 15 | group: 'cherries' 16 | }, 17 | { 18 | year: 2017, 19 | value: 400, 20 | group: 'dates' 21 | }, 22 | { 23 | year: 2018, 24 | value: 960, 25 | group: 'apples' 26 | }, 27 | { 28 | year: 2018, 29 | value: 640, 30 | group: 'bananas' 31 | }, 32 | { 33 | year: 2018, 34 | value: 640, 35 | group: 'cherries' 36 | }, 37 | { 38 | year: 2018, 39 | value: 400, 40 | group: 'dates' 41 | }, 42 | { 43 | year: 2019, 44 | value: 1440, 45 | group: 'apples' 46 | }, 47 | { 48 | year: 2019, 49 | value: 1600, 50 | group: 'bananas' 51 | }, 52 | { 53 | year: 2019, 54 | value: 960, 55 | group: 'cherries' 56 | }, 57 | { 58 | year: 2019, 59 | value: 400, 60 | group: 'dates' 61 | }, 62 | { 63 | year: 2020, 64 | value: 1920, 65 | group: 'apples' 66 | }, 67 | { 68 | year: 2020, 69 | value: 3840, 70 | group: 'bananas' 71 | }, 72 | { 73 | year: 2020, 74 | value: 960, 75 | group: 'cherries' 76 | }, 77 | { 78 | year: 2020, 79 | value: 400, 80 | group: 'dates' 81 | } 82 | ] -------------------------------------------------------------------------------- /src/js/accurate-beeswarm.js: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/jtrim-ons/accurate-beeswarm-plot 2 | const seed = 1; 3 | const randomness1 = 5; 4 | const randomness2 = 2; 5 | 6 | export default class { 7 | constructor(items, radiusFun, xFun, padding, yOffset) { 8 | this.items = items; 9 | this.radiusFun = radiusFun; 10 | this.xFun = xFun; 11 | this.padding = padding; 12 | this.yOffset = yOffset; 13 | this.tieBreakFn = this._sfc32(0x9E3779B9, 0x243F6A88, 0xB7E15162, seed); 14 | this.maxR = Math.max(...items.map(d => radiusFun(d))); 15 | this.rng = this._sfc32(1, 2, 3, seed); 16 | } 17 | 18 | calculateYPositions() { 19 | let all = this.items 20 | .map((d, i) => ({ 21 | datum: d, 22 | originalIndex: i, 23 | x: this.xFun(d), 24 | r: this.radiusFun(d) + this.padding, 25 | y: null, 26 | placed: false 27 | })) 28 | .sort((a, b) => a.x - b.x); 29 | all.forEach(function(d, i) { 30 | d.index = i; 31 | }); 32 | let tieBreakFn = this.tieBreakFn; 33 | all.forEach(function(d) { 34 | d.tieBreaker = tieBreakFn(d.x); 35 | }); 36 | let allSortedByPriority = [...all].sort((a, b) => { 37 | let key_a = this.radiusFun(a.datum) + a.tieBreaker * randomness1; 38 | let key_b = this.radiusFun(b.datum) + b.tieBreaker * randomness1; 39 | if (key_a != key_b) return key_b - key_a; 40 | return a.x - b.x; 41 | }); 42 | for (let item of allSortedByPriority) { 43 | item.placed = true; 44 | item.y = this._getBestYPosition(item, all); 45 | } 46 | all.sort((a, b) => a.originalIndex - b.originalIndex); 47 | return all.map(d => ({ 48 | x: d.x, 49 | y: d.y + this.yOffset, 50 | r: this.radiusFun(d.datum) 51 | })); 52 | } 53 | 54 | // Random number generator (for reproducibility) 55 | // https://stackoverflow.com/a/47593316 56 | _sfc32(a, b, c, d) { 57 | let rng = function() { 58 | a >>>= 0; 59 | b >>>= 0; 60 | c >>>= 0; 61 | d >>>= 0; 62 | var t = (a + b) | 0; 63 | a = b ^ (b >>> 9); 64 | b = (c + (c << 3)) | 0; 65 | c = (c << 21) | (c >>> 11); 66 | d = (d + 1) | 0; 67 | t = (t + d) | 0; 68 | c = (c + t) | 0; 69 | return (t >>> 0) / 4294967296; 70 | }; 71 | for (let i = 0; i < 10; i++) { 72 | rng(); 73 | } 74 | return rng; 75 | } 76 | 77 | _getBestYPosition(item, all) { 78 | let forbiddenIntervals = []; 79 | for (let step of [-1, 1]) { 80 | let xDist; 81 | let r = item.r; 82 | for ( 83 | let i = item.index + step; 84 | i >= 0 && 85 | i < all.length && 86 | (xDist = Math.abs(item.x - all[i].x)) < r + this.maxR; 87 | i += step 88 | ) { 89 | let other = all[i]; 90 | if (!other.placed) continue; 91 | let sumOfRadii = r + other.r; 92 | if (xDist >= r + other.r) continue; 93 | let yDist = Math.sqrt(sumOfRadii * sumOfRadii - xDist * xDist); 94 | let forbiddenInterval = [other.y - yDist, other.y + yDist]; 95 | forbiddenIntervals.push(forbiddenInterval); 96 | } 97 | } 98 | if (forbiddenIntervals.length == 0) { 99 | return item.r * (this.rng() - .5) * randomness2; 100 | } 101 | let candidatePositions = forbiddenIntervals.flat(); 102 | candidatePositions.push(0); 103 | candidatePositions.sort((a, b) => { 104 | let abs_a = Math.abs(a); 105 | let abs_b = Math.abs(b); 106 | if (abs_a < abs_b) return -1; 107 | if (abs_a > abs_b) return 1; 108 | return a - b; 109 | }); 110 | // find first candidate position that is not in any of the 111 | // forbidden intervals 112 | for (let i = 0; i < candidatePositions.length; i++) { 113 | let position = candidatePositions[i]; 114 | if ( 115 | forbiddenIntervals.every( 116 | interval => position <= interval[0] || position >= interval[1] 117 | ) 118 | ) { 119 | return position; 120 | } 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /src/js/spread-labels.js: -------------------------------------------------------------------------------- 1 | // https://observablehq.com/@jtrim-ons/label-placement-for-a-slope-chart-2 2 | export default (ys, radius) => { 3 | let zs = ys.map((y, i) => y - i * radius * 2); 4 | return f(zs).map((x, i) => x + i * radius * 2); 5 | } 6 | 7 | const f = (zs) => { 8 | let batches = []; 9 | for (let z of zs) { 10 | batches.push({ size: 1, mean: z }); 11 | while (batches.length > 1) { 12 | let b = batches[batches.length - 2]; 13 | let c = batches[batches.length - 1]; 14 | if (b.mean < c.mean) break; 15 | b.mean = (b.mean * b.size + c.mean * c.size) / (b.size + c.size); 16 | b.size = b.size + c.size; 17 | batches.pop(); 18 | } 19 | } 20 | let xs = []; 21 | for (const batch of batches) 22 | for (let i = 0; i < batch.size; i++) xs.push(batch.mean); 23 | return xs; 24 | } -------------------------------------------------------------------------------- /src/js/utils.js: -------------------------------------------------------------------------------- 1 | import html2canvas from 'html2canvas'; 2 | 3 | export function groupData(data, domain, key) { 4 | let groups = []; 5 | if (key) { 6 | domain.forEach(group => { 7 | groups.push(data.filter(d => d[key] == group)); 8 | }); 9 | } else { 10 | groups = [data]; 11 | } 12 | return groups; 13 | } 14 | 15 | export function stackData(data, domain, valKey, grpKey) { 16 | let groups = []; 17 | let base = JSON.parse(JSON.stringify(data.filter(d => d[grpKey] == domain[0]))); 18 | base.forEach(d => d[valKey] = 0); 19 | domain.forEach(group => { 20 | let clone = JSON.parse(JSON.stringify(data.filter(d => d[grpKey] == group))); 21 | clone.forEach((d, i) => { 22 | d[valKey] += base[i][valKey]; 23 | base[i][valKey] = d[valKey]; 24 | }); 25 | groups.push(clone); 26 | }); 27 | return groups; 28 | } 29 | 30 | export function getCSV(data, keys = [], filename) { 31 | let str = ''; 32 | let newkeys = []; 33 | keys.forEach(key => { 34 | if (key && !newkeys.includes(key)) { 35 | newkeys.push(key); 36 | } 37 | }); 38 | str += newkeys.join(',') + '\n'; 39 | data.forEach(d => { 40 | str += newkeys.map(key => d[key]).join(',') + '\n'; 41 | }); 42 | let content = 'data:text/csv;charset=utf-8,' + encodeURI(str); 43 | download(content, filename + '.csv'); 44 | } 45 | 46 | export function getPNG(target, filename) { 47 | html2canvas(target) 48 | .then(canvas => { 49 | let content = canvas.toDataURL(); 50 | download(content, filename + '.png'); 51 | }); 52 | } 53 | 54 | function download(content, filename) { 55 | var a = document.createElement('a'); 56 | a.href = content; 57 | a.download = filename; 58 | a.click(); 59 | } 60 | 61 | export function commas(num) { 62 | const parts = String(num).split("."); 63 | parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); 64 | return parts.join("."); 65 | } -------------------------------------------------------------------------------- /src/js/wrap.js: -------------------------------------------------------------------------------- 1 | // Based on https://observablehq.com/@jtrim-ons/svg-text-wrapping 2 | // Adapted to remove D3 dependency 3 | export default function (node, options = {}) { 4 | if (!options.disable) { 5 | const getVal = (val, fallback) => typeof val === "number" ? val : fallback; 6 | const width = getVal(options.width, 100); 7 | const dyAdjust = getVal(options.dyAdjust, 0.05); 8 | const lineHeightEms = getVal(options.lineHeightEms, 1); 9 | const lineHeightSquishFactor = getVal(options.lineHeightSquishFactor, 1); 10 | const splitOnHyphen = options.splitOnHyphen || true; 11 | const centreVertically = options.centreVertically || true; 12 | 13 | const x = +node.getAttribute("x"); 14 | const y = +node.getAttribute("y"); 15 | const anchor = node.getAttribute("text-anchor"); 16 | 17 | const svgNode = (parent, type, content = null) => { 18 | const node = document.createElementNS("http://www.w3.org/2000/svg", type); 19 | if (content) node.textContent = content; 20 | parent.append(node); 21 | return node; 22 | } 23 | 24 | const words = []; 25 | node.textContent 26 | .split(/\s+/) 27 | .forEach(function (w) { 28 | if (splitOnHyphen) { 29 | var subWords = w.split("-"); 30 | for (var i = 0; i < subWords.length - 1; i++) 31 | words.push(subWords[i] + "-"); 32 | words.push(subWords[subWords.length - 1] + " "); 33 | } else { 34 | words.push(w + " "); 35 | } 36 | }); 37 | 38 | node.textContent = ""; // Empty the text element 39 | 40 | // `tspan` is the tspan element that is currently being added to 41 | let tspan = svgNode(node, "tspan"); 42 | 43 | let line = ""; // The current value of the line 44 | let prevLine = ""; // The value of the line before the last word (or sub-word) was added 45 | let nWordsInLine = 0; // Number of words in the line 46 | for (let i = 0; i < words.length; i++) { 47 | let word = words[i]; 48 | prevLine = line; 49 | line = line + word; 50 | ++nWordsInLine; 51 | tspan.textContent = line.trim(); 52 | if (tspan.getComputedTextLength() > width && nWordsInLine > 1) { 53 | // The tspan is too long, and it contains more than one word. 54 | // Remove the last word and add it to a new tspan. 55 | tspan.textContent = prevLine.trim(); 56 | prevLine = ""; 57 | line = word; 58 | nWordsInLine = 1; 59 | tspan = svgNode(node, "tspan", word.trim()); 60 | } 61 | } 62 | 63 | const tspans = node.childNodes; 64 | 65 | let h = lineHeightEms; 66 | // Reduce the line height a bit if there are more than 2 lines. 67 | if (tspans.length > 2) 68 | for (let i = 0; i < tspans.length; i++) h *= lineHeightSquishFactor; 69 | 70 | let dx = 0; 71 | tspans.forEach((d, i) => { 72 | // Calculate the y offset (dy) for each tspan so that the vertical centre 73 | // of the tspans roughly aligns with the text element's y position. 74 | if (anchor === "end" && i !== 0) dx = -d.getComputedTextLength(); 75 | let dy = i === 0 ? dyAdjust : 1; 76 | if (centreVertically && i === 0) dy -= ((tspans.length - 1) * h) / 2; 77 | 78 | d.setAttribute("dx", dx); 79 | d.setAttribute("dy", dy + "em"); 80 | if (anchor !== "end") dx = -d.getComputedTextLength(); 81 | }); 82 | } 83 | } -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.svelte'; 2 | import config from '../config.json'; 3 | 4 | const app = new App({ 5 | target: document.body, 6 | hydrate: config.hydrate 7 | }); 8 | 9 | export default app; 10 | --------------------------------------------------------------------------------