@contraagent/elements 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -2
- package/custom-elements.json +4260 -0
- package/dist/charts/bar-chart.d.ts +32 -0
- package/dist/charts/bar-chart.d.ts.map +1 -0
- package/dist/charts/bar-chart.js +70 -0
- package/dist/charts/bar-chart.js.map +1 -0
- package/dist/charts/base.d.ts +48 -0
- package/dist/charts/base.d.ts.map +1 -0
- package/dist/charts/base.js +203 -0
- package/dist/charts/base.js.map +1 -0
- package/dist/charts/column-chart.d.ts +42 -0
- package/dist/charts/column-chart.d.ts.map +1 -0
- package/dist/charts/column-chart.js +146 -0
- package/dist/charts/column-chart.js.map +1 -0
- package/dist/charts/heatmap.d.ts +42 -0
- package/dist/charts/heatmap.d.ts.map +1 -0
- package/dist/charts/heatmap.js +124 -0
- package/dist/charts/heatmap.js.map +1 -0
- package/dist/charts/index.d.ts +9 -0
- package/dist/charts/index.d.ts.map +1 -0
- package/dist/charts/index.js +11 -0
- package/dist/charts/index.js.map +1 -0
- package/dist/charts/line-chart.d.ts +36 -0
- package/dist/charts/line-chart.d.ts.map +1 -0
- package/dist/charts/line-chart.js +118 -0
- package/dist/charts/line-chart.js.map +1 -0
- package/dist/charts/scale.d.ts +32 -0
- package/dist/charts/scale.d.ts.map +1 -0
- package/dist/charts/scale.js +50 -0
- package/dist/charts/scale.js.map +1 -0
- package/dist/charts/state-grid.d.ts +64 -0
- package/dist/charts/state-grid.d.ts.map +1 -0
- package/dist/charts/state-grid.js +223 -0
- package/dist/charts/state-grid.js.map +1 -0
- package/dist/charts/types.d.ts +34 -0
- package/dist/charts/types.d.ts.map +1 -0
- package/dist/charts/types.js +11 -0
- package/dist/charts/types.js.map +1 -0
- package/dist/checks/check-table.d.ts +32 -0
- package/dist/checks/check-table.d.ts.map +1 -0
- package/dist/checks/check-table.js +94 -0
- package/dist/checks/check-table.js.map +1 -0
- package/dist/checks/index.d.ts +4 -0
- package/dist/checks/index.d.ts.map +1 -0
- package/dist/checks/index.js +6 -0
- package/dist/checks/index.js.map +1 -0
- package/dist/checks/styles.d.ts +5 -0
- package/dist/checks/styles.d.ts.map +1 -0
- package/dist/checks/styles.js +30 -0
- package/dist/checks/styles.js.map +1 -0
- package/dist/checks/types.d.ts +35 -0
- package/dist/checks/types.d.ts.map +1 -0
- package/dist/checks/types.js +12 -0
- package/dist/checks/types.js.map +1 -0
- package/dist/core/clock.d.ts +20 -0
- package/dist/core/clock.d.ts.map +1 -0
- package/dist/core/clock.js +45 -0
- package/dist/core/clock.js.map +1 -0
- package/dist/core/define.d.ts +3 -0
- package/dist/core/define.d.ts.map +1 -0
- package/dist/core/define.js +8 -0
- package/dist/core/define.js.map +1 -0
- package/dist/core/events.d.ts +78 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +8 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/format.d.ts +8 -0
- package/dist/core/format.d.ts.map +1 -0
- package/dist/core/format.js +28 -0
- package/dist/core/format.js.map +1 -0
- package/dist/core/frame.d.ts +4 -0
- package/dist/core/frame.d.ts.map +1 -0
- package/dist/core/frame.js +5 -0
- package/dist/core/frame.js.map +1 -0
- package/dist/core/strings.d.ts +3 -0
- package/dist/core/strings.d.ts.map +1 -0
- package/dist/core/strings.js +14 -0
- package/dist/core/strings.js.map +1 -0
- package/dist/core/tone.d.ts +8 -0
- package/dist/core/tone.d.ts.map +1 -0
- package/dist/core/tone.js +9 -0
- package/dist/core/tone.js.map +1 -0
- package/dist/count/count.d.ts +22 -0
- package/dist/count/count.d.ts.map +1 -0
- package/dist/count/count.js +60 -0
- package/dist/count/count.js.map +1 -0
- package/dist/count/elapsed.d.ts +18 -0
- package/dist/count/elapsed.d.ts.map +1 -0
- package/dist/count/elapsed.js +33 -0
- package/dist/count/elapsed.js.map +1 -0
- package/dist/count/index.d.ts +3 -0
- package/dist/count/index.d.ts.map +1 -0
- package/dist/count/index.js +5 -0
- package/dist/count/index.js.map +1 -0
- package/dist/define/all.d.ts +13 -0
- package/dist/define/all.d.ts.map +1 -0
- package/dist/define/all.js +14 -0
- package/dist/define/all.js.map +1 -0
- package/dist/define/charts.d.ts +8 -0
- package/dist/define/charts.d.ts.map +1 -0
- package/dist/define/charts.js +15 -0
- package/dist/define/charts.js.map +1 -0
- package/dist/define/check-table.d.ts +4 -0
- package/dist/define/check-table.d.ts.map +1 -0
- package/dist/define/check-table.js +7 -0
- package/dist/define/check-table.js.map +1 -0
- package/dist/define/count.d.ts +5 -0
- package/dist/define/count.d.ts.map +1 -0
- package/dist/define/count.js +9 -0
- package/dist/define/count.js.map +1 -0
- package/dist/define/data-table.d.ts +4 -0
- package/dist/define/data-table.d.ts.map +1 -0
- package/dist/define/data-table.js +7 -0
- package/dist/define/data-table.js.map +1 -0
- package/dist/define/graph.d.ts +6 -0
- package/dist/define/graph.d.ts.map +1 -0
- package/dist/define/graph.js +9 -0
- package/dist/define/graph.js.map +1 -0
- package/dist/define/json-tree.d.ts +4 -0
- package/dist/define/json-tree.d.ts.map +1 -0
- package/dist/define/json-tree.js +7 -0
- package/dist/define/json-tree.js.map +1 -0
- package/dist/define/lanes.d.ts +4 -0
- package/dist/define/lanes.d.ts.map +1 -0
- package/dist/define/lanes.js +7 -0
- package/dist/define/lanes.js.map +1 -0
- package/dist/define/markdown.d.ts +4 -0
- package/dist/define/markdown.d.ts.map +1 -0
- package/dist/define/markdown.js +7 -0
- package/dist/define/markdown.js.map +1 -0
- package/dist/define/matrix.d.ts +4 -0
- package/dist/define/matrix.d.ts.map +1 -0
- package/dist/define/matrix.js +7 -0
- package/dist/define/matrix.js.map +1 -0
- package/dist/define/segmented.d.ts +4 -0
- package/dist/define/segmented.d.ts.map +1 -0
- package/dist/define/segmented.js +7 -0
- package/dist/define/segmented.js.map +1 -0
- package/dist/define/tiles.d.ts +5 -0
- package/dist/define/tiles.d.ts.map +1 -0
- package/dist/define/tiles.js +9 -0
- package/dist/define/tiles.js.map +1 -0
- package/dist/define/transcript.d.ts +4 -0
- package/dist/define/transcript.d.ts.map +1 -0
- package/dist/define/transcript.js +7 -0
- package/dist/define/transcript.js.map +1 -0
- package/dist/graph/arrange.d.ts +43 -0
- package/dist/graph/arrange.d.ts.map +1 -0
- package/dist/graph/arrange.js +390 -0
- package/dist/graph/arrange.js.map +1 -0
- package/dist/graph/card.d.ts +71 -0
- package/dist/graph/card.d.ts.map +1 -0
- package/dist/graph/card.js +185 -0
- package/dist/graph/card.js.map +1 -0
- package/dist/graph/host.d.ts +82 -0
- package/dist/graph/host.d.ts.map +1 -0
- package/dist/graph/host.js +21 -0
- package/dist/graph/host.js.map +1 -0
- package/dist/graph/icons.d.ts +7 -0
- package/dist/graph/icons.d.ts.map +1 -0
- package/dist/graph/icons.js +18 -0
- package/dist/graph/icons.js.map +1 -0
- package/dist/graph/index.d.ts +15 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +17 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/layered-graph.d.ts +226 -0
- package/dist/graph/layered-graph.d.ts.map +1 -0
- package/dist/graph/layered-graph.js +396 -0
- package/dist/graph/layered-graph.js.map +1 -0
- package/dist/graph/layout.d.ts +80 -0
- package/dist/graph/layout.d.ts.map +1 -0
- package/dist/graph/layout.js +264 -0
- package/dist/graph/layout.js.map +1 -0
- package/dist/graph/marks.d.ts +24 -0
- package/dist/graph/marks.d.ts.map +1 -0
- package/dist/graph/marks.js +38 -0
- package/dist/graph/marks.js.map +1 -0
- package/dist/graph/nav.d.ts +14 -0
- package/dist/graph/nav.d.ts.map +1 -0
- package/dist/graph/nav.js +14 -0
- package/dist/graph/nav.js.map +1 -0
- package/dist/graph/pan-zoom.d.ts +80 -0
- package/dist/graph/pan-zoom.d.ts.map +1 -0
- package/dist/graph/pan-zoom.js +191 -0
- package/dist/graph/pan-zoom.js.map +1 -0
- package/dist/graph/paths.d.ts +9 -0
- package/dist/graph/paths.d.ts.map +1 -0
- package/dist/graph/paths.js +44 -0
- package/dist/graph/paths.js.map +1 -0
- package/dist/graph/place-card.d.ts +36 -0
- package/dist/graph/place-card.d.ts.map +1 -0
- package/dist/graph/place-card.js +68 -0
- package/dist/graph/place-card.js.map +1 -0
- package/dist/graph/strings.d.ts +50 -0
- package/dist/graph/strings.d.ts.map +1 -0
- package/dist/graph/strings.js +37 -0
- package/dist/graph/strings.js.map +1 -0
- package/dist/graph/styles.d.ts +2 -0
- package/dist/graph/styles.d.ts.map +1 -0
- package/dist/graph/styles.js +203 -0
- package/dist/graph/styles.js.map +1 -0
- package/dist/graph/types.d.ts +184 -0
- package/dist/graph/types.d.ts.map +1 -0
- package/dist/graph/types.js +2 -0
- package/dist/graph/types.js.map +1 -0
- package/dist/graph/view.d.ts +17 -0
- package/dist/graph/view.d.ts.map +1 -0
- package/dist/graph/view.js +21 -0
- package/dist/graph/view.js.map +1 -0
- package/dist/graph/views/dag.d.ts +10 -0
- package/dist/graph/views/dag.d.ts.map +1 -0
- package/dist/graph/views/dag.js +292 -0
- package/dist/graph/views/dag.js.map +1 -0
- package/dist/graph/views/foot.d.ts +8 -0
- package/dist/graph/views/foot.d.ts.map +1 -0
- package/dist/graph/views/foot.js +103 -0
- package/dist/graph/views/foot.js.map +1 -0
- package/dist/graph/views/node-table.d.ts +17 -0
- package/dist/graph/views/node-table.d.ts.map +1 -0
- package/dist/graph/views/node-table.js +80 -0
- package/dist/graph/views/node-table.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/json/index.d.ts +2 -0
- package/dist/json/index.d.ts.map +1 -0
- package/dist/json/index.js +3 -0
- package/dist/json/index.js.map +1 -0
- package/dist/json/json-tree.d.ts +21 -0
- package/dist/json/json-tree.d.ts.map +1 -0
- package/dist/json/json-tree.js +51 -0
- package/dist/json/json-tree.js.map +1 -0
- package/dist/lanes/index.d.ts +5 -0
- package/dist/lanes/index.d.ts.map +1 -0
- package/dist/lanes/index.js +7 -0
- package/dist/lanes/index.js.map +1 -0
- package/dist/lanes/lanes.d.ts +56 -0
- package/dist/lanes/lanes.d.ts.map +1 -0
- package/dist/lanes/lanes.js +160 -0
- package/dist/lanes/lanes.js.map +1 -0
- package/dist/lanes/stack.d.ts +26 -0
- package/dist/lanes/stack.d.ts.map +1 -0
- package/dist/lanes/stack.js +43 -0
- package/dist/lanes/stack.js.map +1 -0
- package/dist/lanes/styles.d.ts +2 -0
- package/dist/lanes/styles.d.ts.map +1 -0
- package/dist/lanes/styles.js +30 -0
- package/dist/lanes/styles.js.map +1 -0
- package/dist/lanes/types.d.ts +41 -0
- package/dist/lanes/types.d.ts.map +1 -0
- package/dist/lanes/types.js +6 -0
- package/dist/lanes/types.js.map +1 -0
- package/dist/markdown/cae-markdown.d.ts +37 -0
- package/dist/markdown/cae-markdown.d.ts.map +1 -0
- package/dist/markdown/cae-markdown.js +68 -0
- package/dist/markdown/cae-markdown.js.map +1 -0
- package/dist/markdown/highlight.d.ts +5 -0
- package/dist/markdown/highlight.d.ts.map +1 -0
- package/dist/markdown/highlight.js +86 -0
- package/dist/markdown/highlight.js.map +1 -0
- package/dist/markdown/index.d.ts +4 -0
- package/dist/markdown/index.d.ts.map +1 -0
- package/dist/markdown/index.js +6 -0
- package/dist/markdown/index.js.map +1 -0
- package/dist/markdown/markdown.d.ts +4 -0
- package/dist/markdown/markdown.d.ts.map +1 -0
- package/dist/markdown/markdown.js +105 -0
- package/dist/markdown/markdown.js.map +1 -0
- package/dist/matrix/grid-step.d.ts +8 -0
- package/dist/matrix/grid-step.d.ts.map +1 -0
- package/dist/matrix/grid-step.js +69 -0
- package/dist/matrix/grid-step.js.map +1 -0
- package/dist/matrix/index.d.ts +5 -0
- package/dist/matrix/index.d.ts.map +1 -0
- package/dist/matrix/index.js +7 -0
- package/dist/matrix/index.js.map +1 -0
- package/dist/matrix/mark-matrix.d.ts +97 -0
- package/dist/matrix/mark-matrix.d.ts.map +1 -0
- package/dist/matrix/mark-matrix.js +272 -0
- package/dist/matrix/mark-matrix.js.map +1 -0
- package/dist/matrix/styles.d.ts +4 -0
- package/dist/matrix/styles.d.ts.map +1 -0
- package/dist/matrix/styles.js +47 -0
- package/dist/matrix/styles.js.map +1 -0
- package/dist/matrix/types.d.ts +79 -0
- package/dist/matrix/types.d.ts.map +1 -0
- package/dist/matrix/types.js +5 -0
- package/dist/matrix/types.js.map +1 -0
- package/dist/primitives/fact-line.d.ts +14 -0
- package/dist/primitives/fact-line.d.ts.map +1 -0
- package/dist/primitives/fact-line.js +18 -0
- package/dist/primitives/fact-line.js.map +1 -0
- package/dist/primitives/icon.d.ts +14 -0
- package/dist/primitives/icon.d.ts.map +1 -0
- package/dist/primitives/icon.js +19 -0
- package/dist/primitives/icon.js.map +1 -0
- package/dist/primitives/index.d.ts +10 -0
- package/dist/primitives/index.d.ts.map +1 -0
- package/dist/primitives/index.js +13 -0
- package/dist/primitives/index.js.map +1 -0
- package/dist/primitives/legend.d.ts +28 -0
- package/dist/primitives/legend.d.ts.map +1 -0
- package/dist/primitives/legend.js +28 -0
- package/dist/primitives/legend.js.map +1 -0
- package/dist/primitives/pill.d.ts +23 -0
- package/dist/primitives/pill.d.ts.map +1 -0
- package/dist/primitives/pill.js +40 -0
- package/dist/primitives/pill.js.map +1 -0
- package/dist/primitives/roving-focus.d.ts +47 -0
- package/dist/primitives/roving-focus.d.ts.map +1 -0
- package/dist/primitives/roving-focus.js +111 -0
- package/dist/primitives/roving-focus.js.map +1 -0
- package/dist/primitives/segmented.d.ts +41 -0
- package/dist/primitives/segmented.d.ts.map +1 -0
- package/dist/primitives/segmented.js +88 -0
- package/dist/primitives/segmented.js.map +1 -0
- package/dist/primitives/share-bar.d.ts +37 -0
- package/dist/primitives/share-bar.d.ts.map +1 -0
- package/dist/primitives/share-bar.js +69 -0
- package/dist/primitives/share-bar.js.map +1 -0
- package/dist/primitives/states.d.ts +19 -0
- package/dist/primitives/states.d.ts.map +1 -0
- package/dist/primitives/states.js +11 -0
- package/dist/primitives/states.js.map +1 -0
- package/dist/primitives/window.d.ts +24 -0
- package/dist/primitives/window.d.ts.map +1 -0
- package/dist/primitives/window.js +56 -0
- package/dist/primitives/window.js.map +1 -0
- package/dist/styles/chips.d.ts +2 -0
- package/dist/styles/chips.d.ts.map +1 -0
- package/dist/styles/chips.js +18 -0
- package/dist/styles/chips.js.map +1 -0
- package/dist/styles/facts.d.ts +2 -0
- package/dist/styles/facts.d.ts.map +1 -0
- package/dist/styles/facts.js +7 -0
- package/dist/styles/facts.js.map +1 -0
- package/dist/styles/index.d.ts +16 -0
- package/dist/styles/index.d.ts.map +1 -0
- package/dist/styles/index.js +15 -0
- package/dist/styles/index.js.map +1 -0
- package/dist/styles/layout.d.ts +2 -0
- package/dist/styles/layout.d.ts.map +1 -0
- package/dist/styles/layout.js +33 -0
- package/dist/styles/layout.js.map +1 -0
- package/dist/styles/marks.d.ts +2 -0
- package/dist/styles/marks.d.ts.map +1 -0
- package/dist/styles/marks.js +30 -0
- package/dist/styles/marks.js.map +1 -0
- package/dist/styles/meter.d.ts +3 -0
- package/dist/styles/meter.d.ts.map +1 -0
- package/dist/styles/meter.js +9 -0
- package/dist/styles/meter.js.map +1 -0
- package/dist/styles/motion.d.ts +2 -0
- package/dist/styles/motion.d.ts.map +1 -0
- package/dist/styles/motion.js +27 -0
- package/dist/styles/motion.js.map +1 -0
- package/dist/styles/prose.d.ts +3 -0
- package/dist/styles/prose.d.ts.map +1 -0
- package/dist/styles/prose.js +59 -0
- package/dist/styles/prose.js.map +1 -0
- package/dist/styles/reset.d.ts +2 -0
- package/dist/styles/reset.d.ts.map +1 -0
- package/dist/styles/reset.js +13 -0
- package/dist/styles/reset.js.map +1 -0
- package/dist/styles/status.d.ts +2 -0
- package/dist/styles/status.d.ts.map +1 -0
- package/dist/styles/status.js +52 -0
- package/dist/styles/status.js.map +1 -0
- package/dist/styles/text.d.ts +2 -0
- package/dist/styles/text.d.ts.map +1 -0
- package/dist/styles/text.js +21 -0
- package/dist/styles/text.js.map +1 -0
- package/dist/styles/tokens.d.ts +8 -0
- package/dist/styles/tokens.d.ts.map +1 -0
- package/dist/styles/tokens.js +32 -0
- package/dist/styles/tokens.js.map +1 -0
- package/dist/table/data-table.d.ts +106 -0
- package/dist/table/data-table.d.ts.map +1 -0
- package/dist/table/data-table.js +408 -0
- package/dist/table/data-table.js.map +1 -0
- package/dist/table/index.d.ts +4 -0
- package/dist/table/index.d.ts.map +1 -0
- package/dist/table/index.js +6 -0
- package/dist/table/index.js.map +1 -0
- package/dist/table/styles.d.ts +2 -0
- package/dist/table/styles.d.ts.map +1 -0
- package/dist/table/styles.js +32 -0
- package/dist/table/styles.js.map +1 -0
- package/dist/table/types.d.ts +48 -0
- package/dist/table/types.d.ts.map +1 -0
- package/dist/table/types.js +30 -0
- package/dist/table/types.js.map +1 -0
- package/dist/themes/boot.d.ts +7 -0
- package/dist/themes/boot.d.ts.map +1 -0
- package/dist/themes/boot.js +14 -0
- package/dist/themes/boot.js.map +1 -0
- package/dist/themes/choice.d.ts +16 -0
- package/dist/themes/choice.d.ts.map +1 -0
- package/dist/themes/choice.js +45 -0
- package/dist/themes/choice.js.map +1 -0
- package/dist/themes/data.d.ts +43 -0
- package/dist/themes/data.d.ts.map +1 -0
- package/dist/themes/data.js +408 -0
- package/dist/themes/data.js.map +1 -0
- package/dist/themes/index.d.ts +7 -0
- package/dist/themes/index.d.ts.map +1 -0
- package/dist/themes/index.js +11 -0
- package/dist/themes/index.js.map +1 -0
- package/dist/themes/sheet.d.ts +16 -0
- package/dist/themes/sheet.d.ts.map +1 -0
- package/dist/themes/sheet.js +28 -0
- package/dist/themes/sheet.js.map +1 -0
- package/dist/themes/token-meanings.d.ts +3 -0
- package/dist/themes/token-meanings.d.ts.map +1 -0
- package/dist/themes/token-meanings.js +67 -0
- package/dist/themes/token-meanings.js.map +1 -0
- package/dist/themes/tokens-list.d.ts +14 -0
- package/dist/themes/tokens-list.d.ts.map +1 -0
- package/dist/themes/tokens-list.js +69 -0
- package/dist/themes/tokens-list.js.map +1 -0
- package/dist/tiles/index.d.ts +4 -0
- package/dist/tiles/index.d.ts.map +1 -0
- package/dist/tiles/index.js +6 -0
- package/dist/tiles/index.js.map +1 -0
- package/dist/tiles/stat-tiles.d.ts +40 -0
- package/dist/tiles/stat-tiles.d.ts.map +1 -0
- package/dist/tiles/stat-tiles.js +99 -0
- package/dist/tiles/stat-tiles.js.map +1 -0
- package/dist/tiles/styles.d.ts +2 -0
- package/dist/tiles/styles.d.ts.map +1 -0
- package/dist/tiles/styles.js +23 -0
- package/dist/tiles/styles.js.map +1 -0
- package/dist/tiles/types.d.ts +22 -0
- package/dist/tiles/types.d.ts.map +1 -0
- package/dist/tiles/types.js +2 -0
- package/dist/tiles/types.js.map +1 -0
- package/dist/transcript/follow-scroll.d.ts +21 -0
- package/dist/transcript/follow-scroll.d.ts.map +1 -0
- package/dist/transcript/follow-scroll.js +76 -0
- package/dist/transcript/follow-scroll.js.map +1 -0
- package/dist/transcript/index.d.ts +5 -0
- package/dist/transcript/index.d.ts.map +1 -0
- package/dist/transcript/index.js +7 -0
- package/dist/transcript/index.js.map +1 -0
- package/dist/transcript/styles.d.ts +2 -0
- package/dist/transcript/styles.d.ts.map +1 -0
- package/dist/transcript/styles.js +114 -0
- package/dist/transcript/styles.js.map +1 -0
- package/dist/transcript/tags.d.ts +6 -0
- package/dist/transcript/tags.d.ts.map +1 -0
- package/dist/transcript/tags.js +13 -0
- package/dist/transcript/tags.js.map +1 -0
- package/dist/transcript/tool-card.d.ts +16 -0
- package/dist/transcript/tool-card.d.ts.map +1 -0
- package/dist/transcript/tool-card.js +87 -0
- package/dist/transcript/tool-card.js.map +1 -0
- package/dist/transcript/transcript.d.ts +103 -0
- package/dist/transcript/transcript.d.ts.map +1 -0
- package/dist/transcript/transcript.js +397 -0
- package/dist/transcript/transcript.js.map +1 -0
- package/dist/transcript/types.d.ts +161 -0
- package/dist/transcript/types.d.ts.map +1 -0
- package/dist/transcript/types.js +25 -0
- package/dist/transcript/types.js.map +1 -0
- package/docs/charts.md +58 -0
- package/docs/check-table.md +40 -0
- package/docs/data-table.md +57 -0
- package/docs/examples/charts.ts +61 -0
- package/docs/examples/check-table.ts +19 -0
- package/docs/examples/ci-pipeline.ts +271 -0
- package/docs/examples/data-table.ts +39 -0
- package/docs/examples/graph.ts +57 -0
- package/docs/examples/lanes.ts +40 -0
- package/docs/examples/matrix.ts +56 -0
- package/docs/examples/page.css +34 -0
- package/docs/examples/segmented.ts +18 -0
- package/docs/examples/status.ts +30 -0
- package/docs/examples/theme.css +54 -0
- package/docs/examples/tiles.ts +27 -0
- package/docs/examples/transcript.ts +59 -0
- package/docs/graph.md +112 -0
- package/docs/lanes.md +48 -0
- package/docs/matrix.md +34 -0
- package/docs/page.md +87 -0
- package/docs/primitives.md +57 -0
- package/docs/strings.md +16 -0
- package/docs/theming.md +147 -0
- package/docs/tiles.md +40 -0
- package/docs/transcript.md +73 -0
- package/docs/tsconfig.json +20 -0
- package/package.json +161 -4
- package/src/charts/bar-chart.ts +74 -0
- package/src/charts/base.ts +188 -0
- package/src/charts/column-chart.ts +153 -0
- package/src/charts/heatmap.ts +129 -0
- package/src/charts/index.ts +10 -0
- package/src/charts/line-chart.ts +128 -0
- package/src/charts/scale.ts +61 -0
- package/src/charts/state-grid.ts +245 -0
- package/src/charts/types.ts +48 -0
- package/src/checks/check-table.ts +85 -0
- package/src/checks/index.ts +5 -0
- package/src/checks/styles.ts +31 -0
- package/src/checks/types.ts +48 -0
- package/src/core/clock.ts +46 -0
- package/src/core/define.ts +7 -0
- package/src/core/events.ts +89 -0
- package/src/core/format.ts +25 -0
- package/src/core/frame.ts +5 -0
- package/src/core/strings.ts +11 -0
- package/src/core/tone.ts +14 -0
- package/src/count/count.ts +54 -0
- package/src/count/elapsed.ts +33 -0
- package/src/count/index.ts +4 -0
- package/src/define/all.ts +13 -0
- package/src/define/charts.ts +17 -0
- package/src/define/check-table.ts +9 -0
- package/src/define/count.ts +11 -0
- package/src/define/data-table.ts +9 -0
- package/src/define/graph.ts +11 -0
- package/src/define/json-tree.ts +9 -0
- package/src/define/lanes.ts +9 -0
- package/src/define/markdown.ts +9 -0
- package/src/define/matrix.ts +9 -0
- package/src/define/segmented.ts +9 -0
- package/src/define/tiles.ts +11 -0
- package/src/define/transcript.ts +9 -0
- package/src/graph/arrange.ts +399 -0
- package/src/graph/card.ts +209 -0
- package/src/graph/host.ts +100 -0
- package/src/graph/icons.ts +23 -0
- package/src/graph/index.ts +16 -0
- package/src/graph/layered-graph.ts +417 -0
- package/src/graph/layout.ts +331 -0
- package/src/graph/marks.ts +42 -0
- package/src/graph/nav.ts +19 -0
- package/src/graph/pan-zoom.ts +219 -0
- package/src/graph/paths.ts +41 -0
- package/src/graph/place-card.ts +93 -0
- package/src/graph/strings.ts +86 -0
- package/src/graph/styles.ts +204 -0
- package/src/graph/types.ts +202 -0
- package/src/graph/view.ts +28 -0
- package/src/graph/views/dag.ts +294 -0
- package/src/graph/views/foot.ts +109 -0
- package/src/graph/views/node-table.ts +90 -0
- package/src/index.ts +25 -0
- package/src/json/index.ts +2 -0
- package/src/json/json-tree.ts +49 -0
- package/src/lanes/index.ts +6 -0
- package/src/lanes/lanes.ts +143 -0
- package/src/lanes/stack.ts +51 -0
- package/src/lanes/styles.ts +30 -0
- package/src/lanes/types.ts +51 -0
- package/src/markdown/cae-markdown.ts +64 -0
- package/src/markdown/highlight.ts +94 -0
- package/src/markdown/index.ts +5 -0
- package/src/markdown/markdown.ts +110 -0
- package/src/matrix/grid-step.ts +58 -0
- package/src/matrix/index.ts +6 -0
- package/src/matrix/mark-matrix.ts +285 -0
- package/src/matrix/styles.ts +48 -0
- package/src/matrix/types.ts +83 -0
- package/src/primitives/fact-line.ts +26 -0
- package/src/primitives/icon.ts +26 -0
- package/src/primitives/index.ts +12 -0
- package/src/primitives/legend.ts +56 -0
- package/src/primitives/pill.ts +58 -0
- package/src/primitives/roving-focus.ts +138 -0
- package/src/primitives/segmented.ts +92 -0
- package/src/primitives/share-bar.ts +94 -0
- package/src/primitives/states.ts +28 -0
- package/src/primitives/window.ts +65 -0
- package/src/styles/chips.ts +18 -0
- package/src/styles/facts.ts +7 -0
- package/src/styles/index.ts +19 -0
- package/src/styles/layout.ts +33 -0
- package/src/styles/marks.ts +30 -0
- package/src/styles/meter.ts +9 -0
- package/src/styles/motion.ts +27 -0
- package/src/styles/prose.ts +59 -0
- package/src/styles/reset.ts +13 -0
- package/src/styles/status.ts +52 -0
- package/src/styles/text.ts +21 -0
- package/src/styles/tokens.ts +35 -0
- package/src/table/data-table.ts +367 -0
- package/src/table/index.ts +5 -0
- package/src/table/styles.ts +32 -0
- package/src/table/types.ts +73 -0
- package/src/themes/boot.ts +14 -0
- package/src/themes/choice.ts +47 -0
- package/src/themes/data.ts +433 -0
- package/src/themes/index.ts +10 -0
- package/src/themes/sheet.ts +29 -0
- package/src/themes/token-meanings.ts +67 -0
- package/src/themes/tokens-list.ts +82 -0
- package/src/tiles/index.ts +5 -0
- package/src/tiles/stat-tiles.ts +92 -0
- package/src/tiles/styles.ts +23 -0
- package/src/tiles/types.ts +20 -0
- package/src/transcript/follow-scroll.ts +72 -0
- package/src/transcript/index.ts +6 -0
- package/src/transcript/styles.ts +114 -0
- package/src/transcript/tags.ts +18 -0
- package/src/transcript/tool-card.ts +103 -0
- package/src/transcript/transcript.ts +358 -0
- package/src/transcript/types.ts +167 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// What every chart shares: its width, its words, a legend, the switch to a table of
|
|
2
|
+
// its values, and the tooltip of the item the reader points at or moves to.
|
|
3
|
+
import { LitElement, css, html, nothing, type TemplateResult } from 'lit';
|
|
4
|
+
import { property, state } from 'lit/decorators.js';
|
|
5
|
+
import { withDefaults } from '../core/strings.js';
|
|
6
|
+
import { legend, legendStyles, type LegendItem } from '../primitives/legend.js';
|
|
7
|
+
import { base } from '../styles/index.js';
|
|
8
|
+
import { plainNumber } from './scale.js';
|
|
9
|
+
import { DEFAULT_CHART_STRINGS, type ChartStrings, type ValueText } from './types.js';
|
|
10
|
+
|
|
11
|
+
/** The size of the axis labels, in px. */
|
|
12
|
+
const AXIS_PX = 10.5;
|
|
13
|
+
/** The room beside a label, in px, so that labels side by side do not touch. */
|
|
14
|
+
const LABEL_ROOM = 6;
|
|
15
|
+
|
|
16
|
+
let measurer: CanvasRenderingContext2D | null | undefined;
|
|
17
|
+
const measured = new Map<string, number>();
|
|
18
|
+
|
|
19
|
+
/** `text`'s width in px in the CSS font `font`; where there is no canvas to measure it, an estimate. */
|
|
20
|
+
function measureText(text: string, font: string): number {
|
|
21
|
+
measurer ??= typeof document === 'undefined' ? null : document.createElement('canvas').getContext('2d');
|
|
22
|
+
if (!measurer) return text.length * AXIS_PX * 0.62;
|
|
23
|
+
const key = `${font}\n${text}`;
|
|
24
|
+
let w = measured.get(key);
|
|
25
|
+
if (w === undefined) {
|
|
26
|
+
measurer.font = font;
|
|
27
|
+
w = measurer.measureText(text).width;
|
|
28
|
+
measured.set(key, w);
|
|
29
|
+
}
|
|
30
|
+
return w;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export abstract class ChartElement extends LitElement {
|
|
34
|
+
static override styles = [
|
|
35
|
+
base,
|
|
36
|
+
legendStyles,
|
|
37
|
+
css`
|
|
38
|
+
:host { display: block; min-width: 0; }
|
|
39
|
+
.head { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 8px; min-height: 22px; }
|
|
40
|
+
.head .legend { flex: 1; min-width: 0; }
|
|
41
|
+
.head .legend span { color: var(--text-2); }
|
|
42
|
+
.switch { margin-left: auto; flex: none; background: none; border: 1px solid var(--line); border-radius: 999px; padding: 2px 10px;
|
|
43
|
+
font: inherit; font-size: var(--font-size-sm); color: var(--text-muted); cursor: pointer; }
|
|
44
|
+
.switch:hover, .switch:focus-visible { color: var(--text); border-color: var(--line2); }
|
|
45
|
+
.plot { position: relative; outline-offset: 2px; }
|
|
46
|
+
.plot:focus-visible { outline: 2px solid var(--accent); border-radius: var(--radius-sm); }
|
|
47
|
+
svg { display: block; overflow: visible; }
|
|
48
|
+
.axis { font-size: ${AXIS_PX}px; fill: var(--text-muted); font-variant-numeric: tabular-nums; }
|
|
49
|
+
.grid { stroke: var(--line); stroke-width: 1; shape-rendering: crispEdges; }
|
|
50
|
+
.base { stroke: var(--line2); stroke-width: 1; shape-rendering: crispEdges; }
|
|
51
|
+
.hit { fill: transparent; }
|
|
52
|
+
.band { fill: var(--text); opacity: 0.05; }
|
|
53
|
+
.tip { position: absolute; z-index: 2; pointer-events: none; padding: 6px 9px; border-radius: var(--radius-sm); background: var(--raise);
|
|
54
|
+
border: 1px solid var(--line2); box-shadow: 0 4px 14px var(--shadow); color: var(--text); font-size: var(--font-size-sm);
|
|
55
|
+
white-space: nowrap; line-height: 1.45; }
|
|
56
|
+
.tip b { font-weight: 600; }
|
|
57
|
+
.tip .row { display: flex; align-items: center; gap: 6px; }
|
|
58
|
+
.tip .row i { width: 9px; height: 9px; border-radius: 2px; flex: none; }
|
|
59
|
+
.tip .row .v { margin-left: auto; padding-left: 12px; font-variant-numeric: tabular-nums; }
|
|
60
|
+
.scale { display: inline-flex; gap: 1px; margin-right: 6px; vertical-align: -1px; }
|
|
61
|
+
.legend .scale i { width: 12px; height: 10px; margin: 0; border-radius: 2px; }
|
|
62
|
+
.empty { padding: 18px 0; color: var(--text-muted); font-size: var(--font-size-sm); }
|
|
63
|
+
table { border-collapse: collapse; font-size: var(--font-size-sm); font-variant-numeric: tabular-nums; }
|
|
64
|
+
.tbl { overflow-x: auto; max-width: 100%; }
|
|
65
|
+
th, td { padding: 4px 12px 4px 0; text-align: right; white-space: nowrap; border-bottom: 1px solid var(--line); }
|
|
66
|
+
th { color: var(--text-muted); font-weight: 500; }
|
|
67
|
+
th:first-child, td:first-child { text-align: left; }
|
|
68
|
+
td { color: var(--text-2); }
|
|
69
|
+
`,
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
/** The chart's accessible name (its title, or what it shows). */
|
|
73
|
+
@property() label = '';
|
|
74
|
+
/** A value in words: the host's format for the chart's unit. */
|
|
75
|
+
@property({ attribute: false }) format: ValueText = plainNumber;
|
|
76
|
+
@property({ attribute: false }) strings: Partial<ChartStrings> = {};
|
|
77
|
+
/** The plot's height in px (the axis included). */
|
|
78
|
+
@property({ type: Number }) height = 200;
|
|
79
|
+
|
|
80
|
+
/** The width the chart has, in px. */
|
|
81
|
+
@state() protected width = 0;
|
|
82
|
+
/** The values shown as a table instead of the chart. */
|
|
83
|
+
@state() protected asTable = false;
|
|
84
|
+
/** The item the reader points at or moved to with the keys; null: none. */
|
|
85
|
+
@state() protected active: number | null = null;
|
|
86
|
+
|
|
87
|
+
private resized: ResizeObserver | null = null;
|
|
88
|
+
|
|
89
|
+
protected get words(): ChartStrings {
|
|
90
|
+
return withDefaults(DEFAULT_CHART_STRINGS, this.strings);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
override connectedCallback(): void {
|
|
94
|
+
super.connectedCallback();
|
|
95
|
+
if (typeof ResizeObserver === 'undefined') return;
|
|
96
|
+
this.resized ??= new ResizeObserver((entries) => {
|
|
97
|
+
const w = Math.floor(entries[0]?.contentRect.width ?? 0);
|
|
98
|
+
if (w && Math.abs(w - this.width) >= 1) this.width = w;
|
|
99
|
+
});
|
|
100
|
+
this.resized.observe(this);
|
|
101
|
+
document.fonts?.addEventListener('loadingdone', this.fontsLoaded);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
override disconnectedCallback(): void {
|
|
105
|
+
super.disconnectedCallback();
|
|
106
|
+
this.resized?.disconnect();
|
|
107
|
+
document.fonts?.removeEventListener('loadingdone', this.fontsLoaded);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** A font that loads late changes the labels' widths: they are measured again. */
|
|
111
|
+
private readonly fontsLoaded = (): void => {
|
|
112
|
+
measured.clear();
|
|
113
|
+
this.requestUpdate();
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** An axis label's width in px, as the chart draws it, with room beside it: measured in the chart's font. */
|
|
117
|
+
protected labelWidth(text: string): number {
|
|
118
|
+
const cs = getComputedStyle(this);
|
|
119
|
+
return Math.ceil(measureText(text, `${cs.fontStyle} ${cs.fontWeight} ${AXIS_PX}px ${cs.fontFamily}`)) + LABEL_ROOM;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
protected override firstUpdated(): void {
|
|
123
|
+
if (!this.width) this.width = Math.floor(this.getBoundingClientRect().width) || 600;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** The plot area's width: the element's, never less than a narrow screen's. */
|
|
127
|
+
protected get plotWidth(): number {
|
|
128
|
+
return Math.max(240, this.width || 600);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Whether there is anything to draw. */
|
|
132
|
+
protected abstract hasValues(): boolean;
|
|
133
|
+
/** The legend's entries: none for a single series, whose title names it. */
|
|
134
|
+
protected abstract legendItems(): LegendItem[];
|
|
135
|
+
/** How many items the keys move between. */
|
|
136
|
+
protected abstract itemCount(): number;
|
|
137
|
+
/** How many items the up and down keys move by: a row of a grid; 1 for a row of items. */
|
|
138
|
+
protected rowLength(): number {
|
|
139
|
+
return 1;
|
|
140
|
+
}
|
|
141
|
+
protected abstract plot(): TemplateResult;
|
|
142
|
+
protected abstract table(): TemplateResult;
|
|
143
|
+
|
|
144
|
+
override render() {
|
|
145
|
+
const s = this.words;
|
|
146
|
+
if (!this.hasValues()) return html`<div class="empty" part="empty">${s.empty}</div>`;
|
|
147
|
+
const items = this.legendItems();
|
|
148
|
+
return html`<div class="head">
|
|
149
|
+
${items.length ? legend(items, { label: this.label }) : html`<span class="legend"></span>`}
|
|
150
|
+
<button type="button" class="switch" part="switch" aria-pressed=${this.asTable}
|
|
151
|
+
@click=${() => ((this.asTable = !this.asTable), (this.active = null))}>${this.asTable ? s.showChart : s.showTable}</button>
|
|
152
|
+
</div>
|
|
153
|
+
${this.asTable
|
|
154
|
+
? html`<div class="tbl" part="table">${this.table()}</div>`
|
|
155
|
+
: html`<div class="plot" part="plot" tabindex=${this.itemCount() ? 0 : nothing} role="group" aria-label=${this.label || nothing}
|
|
156
|
+
@keydown=${this.onKey} @blur=${() => (this.active = null)} @mouseleave=${() => (this.active = null)}>${this.plot()}</div>`}`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** The arrow keys move between the items (up and down by a row of a grid), Home and End to the first and the last; Escape hides the tooltip. */
|
|
160
|
+
private readonly onKey = (e: KeyboardEvent): void => {
|
|
161
|
+
const n = this.itemCount();
|
|
162
|
+
if (!n) return;
|
|
163
|
+
const at = this.active;
|
|
164
|
+
let next: number | null = at;
|
|
165
|
+
const row = this.rowLength();
|
|
166
|
+
const by = e.key === 'ArrowRight' ? 1 : e.key === 'ArrowLeft' ? -1 : e.key === 'ArrowDown' ? row : e.key === 'ArrowUp' ? -row : 0;
|
|
167
|
+
if (by) next = at === null ? (by > 0 ? 0 : n - 1) : Math.min(n - 1, Math.max(0, at + by));
|
|
168
|
+
else if (e.key === 'Home') next = 0;
|
|
169
|
+
else if (e.key === 'End') next = n - 1;
|
|
170
|
+
else if (e.key === 'Escape') next = null;
|
|
171
|
+
else return;
|
|
172
|
+
e.preventDefault();
|
|
173
|
+
this.active = next;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/** A tooltip at `left` px (its middle) and `top` px of the plot, kept inside it. */
|
|
177
|
+
protected tip(left: number, top: number, body: TemplateResult): TemplateResult {
|
|
178
|
+
const w = this.plotWidth;
|
|
179
|
+
const side = left > w * 0.6 ? 'right' : 'left';
|
|
180
|
+
const x = side === 'left' ? Math.max(0, left + 12) : Math.max(0, w - left + 12);
|
|
181
|
+
return html`<div class="tip" role="status" style="${side}: ${x}px; top: ${Math.max(0, top)}px">${body}</div>`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** One line of a tooltip: a swatch, a name and its value. */
|
|
185
|
+
protected tipRow(colour: string | null, name: string, value: string): TemplateResult {
|
|
186
|
+
return html`<div class="row">${colour ? html`<i style="background: ${colour}"></i>` : nothing}<span>${name}</span><span class="v">${value}</span></div>`;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { html, nothing, svg, type TemplateResult } from 'lit';
|
|
2
|
+
import { property } from 'lit/decorators.js';
|
|
3
|
+
import type { LegendItem } from '../primitives/legend.js';
|
|
4
|
+
import { ChartElement } from './base.js';
|
|
5
|
+
import { foldSeries, LABEL_GAP, labelStride, niceAxis, type DrawnSeries } from './scale.js';
|
|
6
|
+
import type { ChartSeries } from './types.js';
|
|
7
|
+
|
|
8
|
+
/** The plot's margins: the value axis on the left, the labels under it. */
|
|
9
|
+
const M = { top: 8, right: 4, bottom: 22 };
|
|
10
|
+
/** A bar's top: rounded, the end anchored to the axis square. */
|
|
11
|
+
const RADIUS = 4;
|
|
12
|
+
/** The gap between the stacked parts of a bar, and between the bars of a group. */
|
|
13
|
+
const GAP = 2;
|
|
14
|
+
|
|
15
|
+
/** A bar from the axis up, its top corners rounded when `round`. */
|
|
16
|
+
export function barPath(x: number, y: number, w: number, h: number, round: boolean): string {
|
|
17
|
+
if (h <= 0 || w <= 0) return '';
|
|
18
|
+
const r = round ? Math.min(RADIUS, w / 2, h) : 0;
|
|
19
|
+
return `M${x},${y + h}V${y + r}${r ? `Q${x},${y} ${x + r},${y}` : ''}H${x + w - r}${r ? `Q${x + w},${y} ${x + w},${y + r}` : ''}V${y + h}Z`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Vertical bars, one group per label of `x`: a bar per series side by side, or with
|
|
24
|
+
* `stacked`, the series of a label on one bar. Six series take the series colours in
|
|
25
|
+
* their order; past six, the sixth and later are one bar part, "other". A legend
|
|
26
|
+
* names the series when there are two or more. Pointing at a label, or moving to it
|
|
27
|
+
* with the arrow keys, shows its values; the switch shows them all as a table.
|
|
28
|
+
*
|
|
29
|
+
* @tagname cae-column-chart
|
|
30
|
+
* @csspart plot - the chart
|
|
31
|
+
* @csspart table - the table of its values
|
|
32
|
+
* @csspart switch - the button between the two
|
|
33
|
+
* @csspart empty - what it shows with no values
|
|
34
|
+
* @csspart legend - the legend
|
|
35
|
+
*/
|
|
36
|
+
export class CaeColumnChart extends ChartElement {
|
|
37
|
+
/** A label per group of bars, in order. */
|
|
38
|
+
@property({ attribute: false }) x: readonly string[] = [];
|
|
39
|
+
@property({ attribute: false }) series: readonly ChartSeries[] = [];
|
|
40
|
+
/** The series of a label on one bar, instead of side by side. */
|
|
41
|
+
@property({ type: Boolean }) stacked = false;
|
|
42
|
+
|
|
43
|
+
private get drawn(): DrawnSeries[] {
|
|
44
|
+
return foldSeries(this.series, this.words.other);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected hasValues(): boolean {
|
|
48
|
+
return this.x.length > 0 && this.series.some((s) => s.values.some((v) => v !== null && v !== undefined));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected legendItems(): LegendItem[] {
|
|
52
|
+
const d = this.drawn;
|
|
53
|
+
return d.length > 1 ? d.map((s) => ({ label: s.name, swatch: s.colour })) : [];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected itemCount(): number {
|
|
57
|
+
return this.x.length;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The total of label `j` (the stacked bar's height). */
|
|
61
|
+
private total(j: number): number {
|
|
62
|
+
return this.drawn.reduce((a, s) => a + Math.max(0, s.values[j] ?? 0), 0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
protected plot(): TemplateResult {
|
|
66
|
+
const d = this.drawn;
|
|
67
|
+
const n = this.x.length;
|
|
68
|
+
const values = d.flatMap((s) => s.values.filter((v): v is number => v !== null && v !== undefined));
|
|
69
|
+
const high = this.stacked ? Math.max(0, ...this.x.map((_, j) => this.total(j))) : Math.max(0, ...values);
|
|
70
|
+
const axis = niceAxis(Math.min(0, ...values), high);
|
|
71
|
+
const left = Math.max(...axis.ticks.map((t) => this.labelWidth(this.format(t)))) + 4;
|
|
72
|
+
const w = this.plotWidth;
|
|
73
|
+
const h = this.height;
|
|
74
|
+
const pw = w - left - M.right;
|
|
75
|
+
const ph = h - M.top - M.bottom;
|
|
76
|
+
const y = (v: number) => M.top + ph - ((v - axis.min) / (axis.max - axis.min)) * ph;
|
|
77
|
+
const band = pw / n;
|
|
78
|
+
const barW = Math.max(2, Math.min(40, band * (this.stacked || d.length === 1 ? 0.62 : 0.8)));
|
|
79
|
+
const stride = labelStride(n, pw, Math.max(...this.x.map((s) => this.labelWidth(s))) + LABEL_GAP);
|
|
80
|
+
const bars: TemplateResult[] = [];
|
|
81
|
+
for (let j = 0; j < n; j++) {
|
|
82
|
+
const x0 = left + j * band + (band - barW) / 2;
|
|
83
|
+
if (this.stacked) {
|
|
84
|
+
let at = 0;
|
|
85
|
+
const parts = d.map((s) => ({ s, v: Math.max(0, s.values[j] ?? 0) })).filter((p) => p.v > 0);
|
|
86
|
+
parts.forEach((p, k) => {
|
|
87
|
+
const top = y(at + p.v);
|
|
88
|
+
const bottom = y(at);
|
|
89
|
+
// Each part is 2px shorter than its share, so the surface shows between the parts.
|
|
90
|
+
const hgt = bottom - top - (k > 0 ? GAP : 0);
|
|
91
|
+
bars.push(svg`<path d=${barPath(x0, top, barW, Math.max(0, hgt), k === parts.length - 1)} fill=${p.s.colour}></path>`);
|
|
92
|
+
at += p.v;
|
|
93
|
+
});
|
|
94
|
+
} else {
|
|
95
|
+
const each = (barW - GAP * (d.length - 1)) / d.length;
|
|
96
|
+
d.forEach((s, k) => {
|
|
97
|
+
const v = s.values[j];
|
|
98
|
+
if (v === null || v === undefined || v === 0) return;
|
|
99
|
+
const top = y(Math.max(0, v));
|
|
100
|
+
const bottom = y(Math.min(0, v));
|
|
101
|
+
bars.push(svg`<path d=${barPath(x0 + k * (each + GAP), top, Math.max(1, each), bottom - top, v > 0)} fill=${s.colour}></path>`);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const a = this.active;
|
|
106
|
+
return html`<svg width=${w} height=${h} role="img" aria-label=${this.label || nothing}>
|
|
107
|
+
${axis.ticks.map(
|
|
108
|
+
(t) => svg`<line class=${t === 0 ? 'base' : 'grid'} x1=${left} x2=${w - M.right} y1=${y(t)} y2=${y(t)}></line>
|
|
109
|
+
<text class="axis" x=${left - 6} y=${y(t)} dy="0.32em" text-anchor="end">${this.format(t)}</text>`,
|
|
110
|
+
)}
|
|
111
|
+
${a !== null ? svg`<rect class="band" x=${left + a * band} y=${M.top} width=${band} height=${ph}></rect>` : nothing}
|
|
112
|
+
${bars}
|
|
113
|
+
${this.x.map((label, j) =>
|
|
114
|
+
j % stride === 0
|
|
115
|
+
? svg`<text class="axis" x=${left + j * band + band / 2} y=${h - 6} text-anchor="middle">${label}</text>`
|
|
116
|
+
: nothing,
|
|
117
|
+
)}
|
|
118
|
+
${this.x.map(
|
|
119
|
+
(_, j) => svg`<rect class="hit" x=${left + j * band} y=${M.top} width=${band} height=${ph + M.bottom}
|
|
120
|
+
@mouseenter=${() => (this.active = j)}></rect>`,
|
|
121
|
+
)}
|
|
122
|
+
</svg>
|
|
123
|
+
${a !== null && a < n ? this.tip(left + a * band + band / 2, M.top, this.tipBody(a)) : nothing}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private tipBody(j: number): TemplateResult {
|
|
127
|
+
const d = this.drawn;
|
|
128
|
+
const none = this.words.none;
|
|
129
|
+
return html`<b>${this.x[j]}</b>
|
|
130
|
+
${[...d].reverse().map((s) => this.tipRow(s.colour, s.name, s.values[j] === null || s.values[j] === undefined ? none : this.format(s.values[j])))}
|
|
131
|
+
${this.stacked && d.length > 1 ? this.tipRow(null, this.words.total, this.format(this.total(j))) : nothing}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
protected table(): TemplateResult {
|
|
135
|
+
const d = this.drawn;
|
|
136
|
+
const none = this.words.none;
|
|
137
|
+
const sum = this.stacked && d.length > 1;
|
|
138
|
+
return html`<table aria-label=${this.label || nothing}>
|
|
139
|
+
<thead><tr><th scope="col">${this.words.label}</th>${d.map((s) => html`<th scope="col">${s.name}</th>`)}${sum ? html`<th scope="col">${this.words.total}</th>` : nothing}</tr></thead>
|
|
140
|
+
<tbody>${this.x.map(
|
|
141
|
+
(label, j) => html`<tr><th scope="row">${label}</th>${d.map((s) => html`<td>${s.values[j] === null || s.values[j] === undefined ? none : this.format(s.values[j])}</td>`)}${
|
|
142
|
+
sum ? html`<td>${this.format(this.total(j))}</td>` : nothing
|
|
143
|
+
}</tr>`,
|
|
144
|
+
)}</tbody>
|
|
145
|
+
</table>`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
declare global {
|
|
150
|
+
interface HTMLElementTagNameMap {
|
|
151
|
+
'cae-column-chart': CaeColumnChart;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { html, nothing, svg, type TemplateResult } from 'lit';
|
|
2
|
+
import { property } from 'lit/decorators.js';
|
|
3
|
+
import type { LegendItem } from '../primitives/legend.js';
|
|
4
|
+
import { ChartElement } from './base.js';
|
|
5
|
+
import { LABEL_GAP, labelStride } from './scale.js';
|
|
6
|
+
|
|
7
|
+
/** One hue, light to dark: a value's share of the colour, by its step of five. */
|
|
8
|
+
export const HEAT_STEPS = [16, 34, 52, 72, 92] as const;
|
|
9
|
+
/** The hue of the scale. */
|
|
10
|
+
const HUE = 'var(--series-1)';
|
|
11
|
+
const CELL_H = 22;
|
|
12
|
+
const GAP = 2;
|
|
13
|
+
const M = { top: 2, right: 2, bottom: 22 };
|
|
14
|
+
|
|
15
|
+
/** A colour of the scale: `pct` percent of its hue over the card. */
|
|
16
|
+
export const heatColour = (pct: number): string => `color-mix(in srgb, ${HUE} ${pct}%, var(--card))`;
|
|
17
|
+
|
|
18
|
+
/** The step (0..4) of `v` between `low` and `high`. */
|
|
19
|
+
export function heatStep(v: number, low: number, high: number): number {
|
|
20
|
+
if (!(high > low)) return HEAT_STEPS.length - 1;
|
|
21
|
+
const t = (v - low) / (high - low);
|
|
22
|
+
return Math.min(HEAT_STEPS.length - 1, Math.max(0, Math.floor(t * HEAT_STEPS.length)));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A value per row (`y`) and column (`x`), each a cell coloured on one scale, light to
|
|
27
|
+
* dark (`values[row][column]`; a null cell is left empty, a 0 is the lightest step).
|
|
28
|
+
* The legend gives the scale's lowest and highest value. Pointing at a cell, or moving
|
|
29
|
+
* to it with the arrow keys (up and down by a row), shows its row, column and value;
|
|
30
|
+
* the switch shows them all as a table.
|
|
31
|
+
*
|
|
32
|
+
* @tagname cae-heatmap
|
|
33
|
+
* @csspart plot - the chart
|
|
34
|
+
* @csspart table - the table of its values
|
|
35
|
+
* @csspart switch - the button between the two
|
|
36
|
+
* @csspart empty - what it shows with no values
|
|
37
|
+
* @csspart legend - the legend
|
|
38
|
+
*/
|
|
39
|
+
export class CaeHeatmap extends ChartElement {
|
|
40
|
+
@property({ attribute: false }) x: readonly string[] = [];
|
|
41
|
+
@property({ attribute: false }) y: readonly string[] = [];
|
|
42
|
+
@property({ attribute: false }) values: readonly (readonly (number | null)[])[] = [];
|
|
43
|
+
|
|
44
|
+
private get range(): [number, number] {
|
|
45
|
+
const all = this.values.flat().filter((v): v is number => v !== null && v !== undefined);
|
|
46
|
+
return all.length ? [Math.min(0, ...all), Math.max(...all)] : [0, 0];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
protected hasValues(): boolean {
|
|
50
|
+
return this.x.length > 0 && this.y.length > 0 && this.values.some((r) => r.some((v) => v !== null && v !== undefined));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
protected legendItems(): LegendItem[] {
|
|
54
|
+
const [low, high] = this.range;
|
|
55
|
+
const strip = html`<span class="scale" aria-hidden="true">${HEAT_STEPS.map((p) => html`<i style="background: ${heatColour(p)}"></i>`)}</span>`;
|
|
56
|
+
return [{ label: this.words.scale(this.format(low), this.format(high)), icon: strip }];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
protected itemCount(): number {
|
|
60
|
+
return this.x.length * this.y.length;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
protected override rowLength(): number {
|
|
64
|
+
return this.x.length;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
protected plot(): TemplateResult {
|
|
68
|
+
const nx = this.x.length;
|
|
69
|
+
const ny = this.y.length;
|
|
70
|
+
const [low, high] = this.range;
|
|
71
|
+
const left = Math.max(...this.y.map((s) => this.labelWidth(s))) + 2;
|
|
72
|
+
const w = this.plotWidth;
|
|
73
|
+
const pw = w - left - M.right;
|
|
74
|
+
const cw = pw / nx;
|
|
75
|
+
const h = M.top + ny * CELL_H + M.bottom;
|
|
76
|
+
const stride = labelStride(nx, pw, Math.max(...this.x.map((s) => this.labelWidth(s))) + LABEL_GAP);
|
|
77
|
+
const a = this.active;
|
|
78
|
+
const cells: TemplateResult[] = [];
|
|
79
|
+
for (let r = 0; r < ny; r++)
|
|
80
|
+
for (let c = 0; c < nx; c++) {
|
|
81
|
+
const v = this.values[r]?.[c];
|
|
82
|
+
const x = left + c * cw;
|
|
83
|
+
const y = M.top + r * CELL_H;
|
|
84
|
+
const i = r * nx + c;
|
|
85
|
+
cells.push(
|
|
86
|
+
v === null || v === undefined
|
|
87
|
+
? svg`<rect x=${x + GAP / 2} y=${y + GAP / 2} width=${Math.max(1, cw - GAP)} height=${CELL_H - GAP} rx="2" fill="none" stroke="var(--line)"
|
|
88
|
+
@mouseenter=${() => (this.active = i)}></rect>`
|
|
89
|
+
: svg`<rect x=${x + GAP / 2} y=${y + GAP / 2} width=${Math.max(1, cw - GAP)} height=${CELL_H - GAP} rx="2"
|
|
90
|
+
fill=${heatColour(HEAT_STEPS[heatStep(v, low, high)] ?? 92)} stroke=${a === i ? 'var(--text)' : 'none'} stroke-width="1.5"
|
|
91
|
+
@mouseenter=${() => (this.active = i)}></rect>`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const at = a !== null && a < nx * ny ? { r: Math.floor(a / nx), c: a % nx } : null;
|
|
95
|
+
return html`<svg width=${w} height=${h} role="img" aria-label=${this.label || nothing}>
|
|
96
|
+
${this.y.map((label, r) => svg`<text class="axis" x=${left - 6} y=${M.top + r * CELL_H + CELL_H / 2} dy="0.32em" text-anchor="end">${label}</text>`)}
|
|
97
|
+
${cells}
|
|
98
|
+
${this.x.map((label, c) =>
|
|
99
|
+
c % stride === 0 ? svg`<text class="axis" x=${left + c * cw + cw / 2} y=${h - 6} text-anchor="middle">${label}</text>` : nothing,
|
|
100
|
+
)}
|
|
101
|
+
</svg>
|
|
102
|
+
${at ? this.tip(left + at.c * cw + cw / 2, M.top + (at.r + 1) * CELL_H, this.tipBody(at.r, at.c)) : nothing}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private tipBody(r: number, c: number): TemplateResult {
|
|
106
|
+
const v = this.values[r]?.[c];
|
|
107
|
+
return html`<b>${this.y[r]} | ${this.x[c]}</b>
|
|
108
|
+
<div class="row"><span>${v === null || v === undefined ? this.words.none : this.format(v)}</span></div>`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
protected table(): TemplateResult {
|
|
112
|
+
const none = this.words.none;
|
|
113
|
+
return html`<table aria-label=${this.label || nothing}>
|
|
114
|
+
<thead><tr><th scope="col">${this.words.label}</th>${this.x.map((l) => html`<th scope="col">${l}</th>`)}</tr></thead>
|
|
115
|
+
<tbody>${this.y.map(
|
|
116
|
+
(label, r) => html`<tr><th scope="row">${label}</th>${this.x.map((_, c) => {
|
|
117
|
+
const v = this.values[r]?.[c];
|
|
118
|
+
return html`<td>${v === null || v === undefined ? none : this.format(v)}</td>`;
|
|
119
|
+
})}</tr>`,
|
|
120
|
+
)}</tbody>
|
|
121
|
+
</table>`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
declare global {
|
|
126
|
+
interface HTMLElementTagNameMap {
|
|
127
|
+
'cae-heatmap': CaeHeatmap;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Charts of plain series: `@contraagent/elements/charts`. The elements are registered
|
|
2
|
+
// by `@contraagent/elements/charts/define`.
|
|
3
|
+
export * from './types.js';
|
|
4
|
+
export * from './scale.js';
|
|
5
|
+
export * from './base.js';
|
|
6
|
+
export * from './column-chart.js';
|
|
7
|
+
export * from './line-chart.js';
|
|
8
|
+
export * from './heatmap.js';
|
|
9
|
+
export * from './state-grid.js';
|
|
10
|
+
export * from './bar-chart.js';
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { html, nothing, svg, type TemplateResult } from 'lit';
|
|
2
|
+
import { property } from 'lit/decorators.js';
|
|
3
|
+
import type { LegendItem } from '../primitives/legend.js';
|
|
4
|
+
import { ChartElement } from './base.js';
|
|
5
|
+
import { LABEL_GAP, labelStride, niceAxis, seriesColour } from './scale.js';
|
|
6
|
+
import type { ChartSeries } from './types.js';
|
|
7
|
+
|
|
8
|
+
const M = { top: 8, right: 10, bottom: 22 };
|
|
9
|
+
|
|
10
|
+
/** The path of a series: a line through its values, broken where a value is null. */
|
|
11
|
+
export function linePath(points: readonly ([number, number] | null)[]): string {
|
|
12
|
+
let d = '';
|
|
13
|
+
let open = false;
|
|
14
|
+
for (const p of points) {
|
|
15
|
+
if (!p) {
|
|
16
|
+
open = false;
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
d += `${open ? 'L' : 'M'}${p[0].toFixed(1)},${p[1].toFixed(1)}`;
|
|
20
|
+
open = true;
|
|
21
|
+
}
|
|
22
|
+
return d;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Lines over the labels of `x`, one per series (a value per label; null breaks the
|
|
27
|
+
* line). Six series take the series colours in their order; any past six are drawn in
|
|
28
|
+
* the colour of "other" and named in the legend and the tooltip. Pointing at a label,
|
|
29
|
+
* or moving to it with the arrow keys, marks it with a rule and shows every series'
|
|
30
|
+
* value there; the switch shows them all as a table.
|
|
31
|
+
*
|
|
32
|
+
* @tagname cae-line-chart
|
|
33
|
+
* @csspart plot - the chart
|
|
34
|
+
* @csspart table - the table of its values
|
|
35
|
+
* @csspart switch - the button between the two
|
|
36
|
+
* @csspart empty - what it shows with no values
|
|
37
|
+
* @csspart legend - the legend
|
|
38
|
+
*/
|
|
39
|
+
export class CaeLineChart extends ChartElement {
|
|
40
|
+
@property({ attribute: false }) x: readonly string[] = [];
|
|
41
|
+
@property({ attribute: false }) series: readonly ChartSeries[] = [];
|
|
42
|
+
|
|
43
|
+
protected hasValues(): boolean {
|
|
44
|
+
return this.x.length > 0 && this.series.some((s) => s.values.some((v) => v !== null && v !== undefined));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
protected legendItems(): LegendItem[] {
|
|
48
|
+
return this.series.length > 1 ? this.series.map((s, i) => ({ label: s.name, swatch: seriesColour(i) })) : [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected itemCount(): number {
|
|
52
|
+
return this.x.length;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
protected plot(): TemplateResult {
|
|
56
|
+
const n = this.x.length;
|
|
57
|
+
const values = this.series.flatMap((s) => s.values.filter((v): v is number => v !== null && v !== undefined));
|
|
58
|
+
const axis = niceAxis(Math.min(0, ...values), Math.max(0, ...values));
|
|
59
|
+
const left = Math.max(...axis.ticks.map((t) => this.labelWidth(this.format(t)))) + 4;
|
|
60
|
+
const w = this.plotWidth;
|
|
61
|
+
const h = this.height;
|
|
62
|
+
const pw = w - left - M.right;
|
|
63
|
+
const ph = h - M.top - M.bottom;
|
|
64
|
+
const step = n > 1 ? pw / (n - 1) : 0;
|
|
65
|
+
const px = (j: number) => (n > 1 ? left + j * step : left + pw / 2);
|
|
66
|
+
const py = (v: number) => M.top + ph - ((v - axis.min) / (axis.max - axis.min)) * ph;
|
|
67
|
+
const stride = labelStride(n, pw, Math.max(...this.x.map((s) => this.labelWidth(s))) + LABEL_GAP);
|
|
68
|
+
const a = this.active;
|
|
69
|
+
const hitW = n > 1 ? step : pw;
|
|
70
|
+
return html`<svg width=${w} height=${h} role="img" aria-label=${this.label || nothing}>
|
|
71
|
+
${axis.ticks.map(
|
|
72
|
+
(t) => svg`<line class=${t === 0 ? 'base' : 'grid'} x1=${left} x2=${w - M.right} y1=${py(t)} y2=${py(t)}></line>
|
|
73
|
+
<text class="axis" x=${left - 6} y=${py(t)} dy="0.32em" text-anchor="end">${this.format(t)}</text>`,
|
|
74
|
+
)}
|
|
75
|
+
${a !== null ? svg`<line class="base" x1=${px(a)} x2=${px(a)} y1=${M.top} y2=${M.top + ph}></line>` : nothing}
|
|
76
|
+
${this.series.map((s, i) => {
|
|
77
|
+
const pts = this.x.map((_, j) => {
|
|
78
|
+
const v = s.values[j];
|
|
79
|
+
return v === null || v === undefined ? null : ([px(j), py(v)] as [number, number]);
|
|
80
|
+
});
|
|
81
|
+
const lone = pts.filter((p, j) => p && !pts[j - 1] && !pts[j + 1]) as [number, number][];
|
|
82
|
+
return svg`<path d=${linePath(pts)} fill="none" stroke=${seriesColour(i)} stroke-width="2" stroke-linejoin="round" stroke-linecap="round"></path>
|
|
83
|
+
${lone.map((p) => svg`<circle cx=${p[0]} cy=${p[1]} r="2.5" fill=${seriesColour(i)}></circle>`)}`;
|
|
84
|
+
})}
|
|
85
|
+
${a !== null
|
|
86
|
+
? this.series.map((s, i) => {
|
|
87
|
+
const v = s.values[a];
|
|
88
|
+
return v === null || v === undefined
|
|
89
|
+
? nothing
|
|
90
|
+
: svg`<circle cx=${px(a)} cy=${py(v)} r="4.5" fill=${seriesColour(i)} stroke="var(--card)" stroke-width="2"></circle>`;
|
|
91
|
+
})
|
|
92
|
+
: nothing}
|
|
93
|
+
${this.x.map((label, j) =>
|
|
94
|
+
j % stride === 0
|
|
95
|
+
? svg`<text class="axis" x=${px(j)} y=${h - 6} text-anchor=${n > 1 && j === 0 ? 'start' : n > 1 && j === n - 1 ? 'end' : 'middle'}>${label}</text>`
|
|
96
|
+
: nothing,
|
|
97
|
+
)}
|
|
98
|
+
${this.x.map(
|
|
99
|
+
(_, j) => svg`<rect class="hit" x=${px(j) - hitW / 2} y=${M.top} width=${hitW} height=${ph + M.bottom}
|
|
100
|
+
@mouseenter=${() => (this.active = j)}></rect>`,
|
|
101
|
+
)}
|
|
102
|
+
</svg>
|
|
103
|
+
${a !== null && a < n ? this.tip(px(a), M.top, this.tipBody(a)) : nothing}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private tipBody(j: number): TemplateResult {
|
|
107
|
+
const none = this.words.none;
|
|
108
|
+
return html`<b>${this.x[j]}</b>
|
|
109
|
+
${this.series.map((s, i) => this.tipRow(seriesColour(i), s.name, s.values[j] === null || s.values[j] === undefined ? none : this.format(s.values[j])))}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
protected table(): TemplateResult {
|
|
113
|
+
const none = this.words.none;
|
|
114
|
+
return html`<table aria-label=${this.label || nothing}>
|
|
115
|
+
<thead><tr><th scope="col">${this.words.label}</th>${this.series.map((s) => html`<th scope="col">${s.name}</th>`)}</tr></thead>
|
|
116
|
+
<tbody>${this.x.map(
|
|
117
|
+
(label, j) =>
|
|
118
|
+
html`<tr><th scope="row">${label}</th>${this.series.map((s) => html`<td>${s.values[j] === null || s.values[j] === undefined ? none : this.format(s.values[j])}</td>`)}</tr>`,
|
|
119
|
+
)}</tbody>
|
|
120
|
+
</table>`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
declare global {
|
|
125
|
+
interface HTMLElementTagNameMap {
|
|
126
|
+
'cae-line-chart': CaeLineChart;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// The arithmetic the charts share: an axis of round numbers, the colours of the series.
|
|
2
|
+
import type { ChartSeries } from './types.js';
|
|
3
|
+
|
|
4
|
+
/** The series colours (`--series-1` ...), in their fixed order. */
|
|
5
|
+
export const SERIES_COLOURS = 6;
|
|
6
|
+
/** The colour of the series folded past the sixth, and of anything that is not a series. */
|
|
7
|
+
export const OTHER_COLOUR = 'var(--meter)';
|
|
8
|
+
|
|
9
|
+
/** Series `i`'s colour: the series colours in order, then the colour of "other". */
|
|
10
|
+
export const seriesColour = (i: number): string => (i < SERIES_COLOURS ? `var(--series-${i + 1})` : OTHER_COLOUR);
|
|
11
|
+
|
|
12
|
+
/** A series with its colour. */
|
|
13
|
+
export interface DrawnSeries extends ChartSeries {
|
|
14
|
+
colour: string;
|
|
15
|
+
/** It stands for several series, folded into one. */
|
|
16
|
+
folded?: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The series as drawn: the first six each in its own colour, by their order (a series
|
|
21
|
+
* keeps its colour whatever its values); with more than six, the sixth and later are
|
|
22
|
+
* summed into one series named `other`, so no colour is ever made up.
|
|
23
|
+
*/
|
|
24
|
+
export function foldSeries(series: readonly ChartSeries[], other: string): DrawnSeries[] {
|
|
25
|
+
if (series.length <= SERIES_COLOURS) return series.map((s, i) => ({ ...s, colour: seriesColour(i) }));
|
|
26
|
+
const own = series.slice(0, SERIES_COLOURS - 1).map((s, i) => ({ ...s, colour: seriesColour(i) }));
|
|
27
|
+
const rest = series.slice(SERIES_COLOURS - 1);
|
|
28
|
+
const n = Math.max(...rest.map((s) => s.values.length));
|
|
29
|
+
const values = Array.from({ length: n }, (_, j) => {
|
|
30
|
+
const vs = rest.map((s) => s.values[j]).filter((v): v is number => v !== null && v !== undefined);
|
|
31
|
+
return vs.length ? vs.reduce((a, v) => a + v, 0) : null;
|
|
32
|
+
});
|
|
33
|
+
return [...own, { name: other, values, colour: OTHER_COLOUR, folded: rest.map((s) => s.name) }];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** An axis from `low` (0 or below) to a round number at or above `high`, and its ticks (`count` or so). */
|
|
37
|
+
export function niceAxis(low: number, high: number, count = 4): { min: number; max: number; ticks: number[] } {
|
|
38
|
+
const lo = Math.min(0, low);
|
|
39
|
+
const hi = Math.max(0, high);
|
|
40
|
+
if (!(hi > lo)) return { min: 0, max: 1, ticks: [0, 1] };
|
|
41
|
+
const raw = (hi - lo) / count;
|
|
42
|
+
const mag = 10 ** Math.floor(Math.log10(raw));
|
|
43
|
+
const step = [1, 2, 2.5, 5, 10].map((m) => m * mag).find((s) => s >= raw) ?? 10 * mag;
|
|
44
|
+
const min = Math.floor(lo / step) * step;
|
|
45
|
+
const max = Math.ceil(hi / step) * step;
|
|
46
|
+
const ticks: number[] = [];
|
|
47
|
+
for (let v = min; v <= max + step / 2; v += step) ticks.push(Math.round(v / step) * step);
|
|
48
|
+
return { min, max, ticks };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The space kept between two labels of an axis, in px. */
|
|
52
|
+
export const LABEL_GAP = 12;
|
|
53
|
+
|
|
54
|
+
/** Every `k`-th label of `n` fits in `width` px when a label is `labelWidth` px wide: the smallest such `k`. */
|
|
55
|
+
export function labelStride(n: number, width: number, labelWidth: number): number {
|
|
56
|
+
if (n <= 0 || width <= 0) return 1;
|
|
57
|
+
return Math.max(1, Math.ceil((n * labelWidth) / width));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** A number as written when the host gives no format: up to two decimals. */
|
|
61
|
+
export const plainNumber = (v: number): string => String(Math.round(v * 100) / 100);
|