@fundar/data-chart-telling 1.18.0 → 1.20.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/CHANGELOG.md +53 -0
- package/README.md +2 -1
- package/dist/configuration/config.svelte.js +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/layout/coordinates/CoordinatesLayout.svelte +3 -1
- package/dist/layout/facet/FacetLayout.svelte +54 -41
- package/dist/layout/geometry/GeometryLayout.svelte +57 -22
- package/dist/layout/geometry/ScaleFreeContext.d.ts +31 -0
- package/dist/layout/geometry/ScaleFreeContext.js +65 -0
- package/dist/layout/geometry/ScaleFreeMarks.svelte +14 -0
- package/dist/layout/geometry/ScaleFreeMarks.svelte.d.ts +7 -0
- package/dist/layout/geometry/ScaleGate.svelte.d.ts +8 -0
- package/dist/layout/geometry/ScaleGate.svelte.js +95 -0
- package/dist/layout/geometry/ScaleGateMarks.svelte +15 -0
- package/dist/layout/geometry/ScaleGateMarks.svelte.d.ts +7 -0
- package/dist/layout/hover/HoverShapes.d.ts +20 -8
- package/dist/layout/hover/HoverShapes.js +119 -59
- package/dist/layout/legend/ContinuousSection.svelte +10 -0
- package/dist/layout/legend/LegendState.svelte.d.ts +13 -1
- package/dist/layout/legend/LegendState.svelte.js +32 -0
- package/dist/layout/margins/Box.d.ts +4 -0
- package/dist/layout/margins/Box.js +14 -0
- package/dist/layout/margins/PlotMargins.svelte.js +26 -1
- package/dist/layout/plot/{Plot.svelte → AbstractPlot.svelte} +83 -6
- package/dist/layout/plot/{Plot.svelte.d.ts → AbstractPlot.svelte.d.ts} +4 -4
- package/dist/layout/plot/PublicPlotContext.d.ts +5 -0
- package/dist/layout/plot/PublicPlotContext.js +7 -0
- package/dist/layout/styles/ValueLabels.d.ts +109 -58
- package/dist/layout/styles/ValueLabels.js +299 -141
- package/dist/markers/MarkerKinds.d.ts +79 -1
- package/dist/markers/MarkerKinds.js +149 -23
- package/dist/markers/area/AreaMarker.svelte +1 -1
- package/dist/markers/band/BandMarker.svelte +2 -1
- package/dist/markers/bar/BarMarker.svelte +8 -6
- package/dist/markers/bollinger/BollingerMarker.svelte +1 -1
- package/dist/markers/box/BoxMarker.svelte +1 -1
- package/dist/markers/dot/DotRadius.d.ts +11 -0
- package/dist/markers/dot/DotRadius.js +21 -0
- package/dist/markers/geo/GeoHoverMarker.svelte +14 -11
- package/dist/markers/labels/CurveLabel.svelte +56 -0
- package/dist/markers/labels/CurveLabel.svelte.d.ts +9 -0
- package/dist/markers/labels/LabelMerge.d.ts +11 -0
- package/dist/markers/labels/LabelMerge.js +87 -0
- package/dist/markers/labels/MarkerLabel.svelte +36 -0
- package/dist/markers/labels/MarkerLabel.svelte.d.ts +10 -0
- package/dist/markers/labels/MarkerLabels.d.ts +52 -0
- package/dist/markers/labels/MarkerLabels.js +97 -0
- package/dist/markers/labels/geometry/BoxGeometry.d.ts +8 -0
- package/dist/markers/labels/geometry/BoxGeometry.js +101 -0
- package/dist/markers/labels/geometry/CurveGeometry.d.ts +34 -0
- package/dist/markers/labels/geometry/CurveGeometry.js +193 -0
- package/dist/markers/labels/geometry/PointGeometry.d.ts +9 -0
- package/dist/markers/labels/geometry/PointGeometry.js +22 -0
- package/dist/markers/labels/layouts/BoxLayout.d.ts +10 -0
- package/dist/markers/labels/layouts/BoxLayout.js +49 -0
- package/dist/markers/labels/layouts/PathLayout.d.ts +11 -0
- package/dist/markers/labels/layouts/PathLayout.js +91 -0
- package/dist/markers/labels/layouts/PointLayout.d.ts +19 -0
- package/dist/markers/labels/layouts/PointLayout.js +94 -0
- package/dist/markers/regression/RegressionMarker.svelte +1 -1
- package/dist/markers/text/Covering.d.ts +13 -0
- package/dist/markers/text/Covering.js +29 -0
- package/dist/markers/text/Declutter.d.ts +24 -3
- package/dist/markers/text/Declutter.js +46 -3
- package/dist/markers/text/TextMarker.svelte +90 -17
- package/dist/markers/text/TextMarker.svelte.d.ts +1 -1
- package/dist/markers/tick/TickMarker.svelte +1 -1
- package/dist/markers/utils/MarkerChannels.d.ts +1 -1
- package/dist/markers/utils/MarkerChannels.js +3 -2
- package/dist/markers/violin/ViolinMarker.svelte +1 -1
- package/dist/markers/waffle/WaffleMarker.svelte +1 -1
- package/dist/phases/context/RowContext.d.ts +2 -0
- package/dist/phases/context/RowContext.js +4 -2
- package/dist/phases/context/rowContextKey.d.ts +2 -0
- package/dist/phases/context/rowContextKey.js +2 -0
- package/dist/phases/markers/MarkerStyles.js +14 -11
- package/dist/phases/markers/MarkersPhase.d.ts +2 -3
- package/dist/phases/markers/MarkersPhase.js +17 -11
- package/dist/phases/sharedScales/SharedStepScales.d.ts +10 -0
- package/dist/phases/sharedScales/SharedStepScales.js +43 -0
- package/dist/phases/statistics/MarkerGrouping.js +5 -32
- package/dist/phases/statistics/MarkerInheritance.d.ts +11 -0
- package/dist/phases/statistics/MarkerInheritance.js +25 -0
- package/dist/phases/statistics/MarkerOrientation.d.ts +26 -0
- package/dist/phases/statistics/MarkerOrientation.js +29 -0
- package/dist/phases/statistics/StatisticsPhase.js +15 -15
- package/dist/phases/transforms/TransformsPhase.js +5 -3
- package/dist/plots/correlation/heatmap/Plot.svelte +2 -2
- package/dist/plots/correlation/heatmap/Plot.svelte.d.ts +0 -1
- package/dist/plots/correlation/heatmap/defaults.js +14 -1
- package/dist/plots/correlation/heatmap/markers.d.ts +0 -6
- package/dist/plots/correlation/heatmap/markers.js +5 -9
- package/dist/plots/correlation/scatter/Plot.svelte +2 -2
- package/dist/plots/correlation/scatter/Plot.svelte.d.ts +0 -1
- package/dist/plots/correlation/scatter/defaults.js +9 -3
- package/dist/plots/correlation/scatter/markers.d.ts +0 -9
- package/dist/plots/correlation/scatter/markers.js +21 -28
- package/dist/plots/distribution/box/BoxSummaries.d.ts +5 -0
- package/dist/plots/distribution/box/BoxSummaries.js +9 -0
- package/dist/plots/distribution/box/Plot.svelte +2 -2
- package/dist/plots/distribution/box/Plot.svelte.d.ts +0 -1
- package/dist/plots/distribution/box/defaults.js +11 -0
- package/dist/plots/distribution/box/markers.js +10 -1
- package/dist/plots/distribution/density/Plot.svelte +2 -2
- package/dist/plots/distribution/density/Plot.svelte.d.ts +0 -1
- package/dist/plots/distribution/density/defaults.js +16 -0
- package/dist/plots/distribution/density/markers.js +5 -1
- package/dist/plots/distribution/histogram/Plot.svelte +2 -2
- package/dist/plots/distribution/histogram/Plot.svelte.d.ts +0 -1
- package/dist/plots/distribution/histogram/defaults.js +16 -11
- package/dist/plots/distribution/histogram/markers.js +3 -3
- package/dist/plots/distribution/pyramid/Plot.svelte +2 -2
- package/dist/plots/distribution/pyramid/Plot.svelte.d.ts +0 -1
- package/dist/plots/distribution/pyramid/defaults.js +9 -2
- package/dist/plots/distribution/pyramid/markers.d.ts +0 -8
- package/dist/plots/distribution/pyramid/markers.js +6 -16
- package/dist/plots/distribution/violin/Plot.svelte +2 -2
- package/dist/plots/distribution/violin/Plot.svelte.d.ts +0 -1
- package/dist/plots/distribution/violin/defaults.js +11 -0
- package/dist/plots/distribution/violin/markers.js +27 -3
- package/dist/plots/evolution/area/Plot.svelte +2 -2
- package/dist/plots/evolution/area/Plot.svelte.d.ts +0 -1
- package/dist/plots/evolution/area/defaults.js +4 -7
- package/dist/plots/evolution/area/markers.js +3 -1
- package/dist/plots/evolution/line/Plot.svelte +2 -2
- package/dist/plots/evolution/line/Plot.svelte.d.ts +0 -1
- package/dist/plots/evolution/line/defaults.js +4 -6
- package/dist/plots/evolution/line/markers.js +3 -1
- package/dist/plots/evolution/slope/Plot.svelte +2 -2
- package/dist/plots/evolution/slope/Plot.svelte.d.ts +0 -1
- package/dist/plots/evolution/slope/defaults.js +4 -3
- package/dist/plots/evolution/slope/markers.d.ts +4 -3
- package/dist/plots/evolution/slope/markers.js +16 -9
- package/dist/plots/evolution/sparkline/Plot.svelte +2 -2
- package/dist/plots/evolution/sparkline/Plot.svelte.d.ts +0 -1
- package/dist/plots/evolution/sparkline/defaults.js +1 -1
- package/dist/plots/evolution/sparkline/markers.js +3 -1
- package/dist/plots/generic/Plot.svelte +42 -0
- package/dist/plots/generic/Plot.svelte.d.ts +25 -0
- package/dist/plots/generic/defaults.d.ts +4 -0
- package/dist/plots/generic/defaults.js +7 -0
- package/dist/plots/map/geo/Core.svelte +29 -6
- package/dist/plots/map/geo/Plot.svelte +40 -12
- package/dist/plots/map/geo/Plot.svelte.d.ts +0 -1
- package/dist/plots/map/geo/phases/data.d.ts +2 -1
- package/dist/plots/map/geo/phases/data.js +8 -2
- package/dist/plots/map/geo/phases/scales.js +6 -2
- package/dist/plots/map/geo/slices.d.ts +27 -3
- package/dist/plots/map/geo/slices.js +58 -5
- package/dist/plots/part-of-a-whole/treemap/Plot.svelte +2 -2
- package/dist/plots/part-of-a-whole/treemap/Plot.svelte.d.ts +0 -1
- package/dist/plots/part-of-a-whole/treemap/defaults.js +2 -2
- package/dist/plots/ranking/bar/Plot.svelte +2 -2
- package/dist/plots/ranking/bar/Plot.svelte.d.ts +0 -1
- package/dist/plots/ranking/bar/defaults.js +13 -3
- package/dist/plots/ranking/bar/markers.js +12 -29
- package/dist/plots/ranking/bullet/Plot.svelte +2 -2
- package/dist/plots/ranking/bullet/Plot.svelte.d.ts +0 -1
- package/dist/plots/ranking/bullet/defaults.js +20 -0
- package/dist/plots/ranking/bullet/markers.js +7 -1
- package/dist/plots/ranking/cleveland/ClevelandMarkers.d.ts +2 -9
- package/dist/plots/ranking/cleveland/ClevelandMarkers.js +65 -47
- package/dist/plots/ranking/cleveland/Plot.svelte +2 -2
- package/dist/plots/ranking/cleveland/Plot.svelte.d.ts +0 -1
- package/dist/plots/ranking/cleveland/defaults.js +33 -2
- package/dist/plots/ranking/dumbbell/Plot.svelte +2 -2
- package/dist/plots/ranking/dumbbell/Plot.svelte.d.ts +0 -1
- package/dist/plots/ranking/forest/Plot.svelte +2 -2
- package/dist/plots/ranking/forest/Plot.svelte.d.ts +0 -1
- package/dist/plots/ranking/lollipop/Plot.svelte +2 -2
- package/dist/plots/ranking/lollipop/Plot.svelte.d.ts +0 -1
- package/dist/types/layout/context.d.ts +2 -0
- package/dist/types/layout/plot.d.ts +2 -0
- package/dist/types/layout/styles/bullet.d.ts +4 -1
- package/dist/types/layout/styles/geo.d.ts +3 -3
- package/dist/types/layout/styles/values.d.ts +51 -11
- package/dist/types/markers/axis/area.d.ts +5 -1
- package/dist/types/markers/axis/bar.d.ts +5 -2
- package/dist/types/markers/axis/bollinger.d.ts +2 -1
- package/dist/types/markers/axis/box.d.ts +2 -1
- package/dist/types/markers/axis/cell.d.ts +4 -1
- package/dist/types/markers/axis/line.d.ts +4 -1
- package/dist/types/markers/axis/rect.d.ts +4 -1
- package/dist/types/markers/axis/regression.d.ts +2 -1
- package/dist/types/markers/axis/tick.d.ts +2 -1
- package/dist/types/markers/axis/trail.d.ts +4 -1
- package/dist/types/markers/axis/violin.d.ts +2 -1
- package/dist/types/markers/axis/waffle.d.ts +5 -1
- package/dist/types/markers/basic/dot.d.ts +4 -1
- package/dist/types/markers/basic/text.d.ts +25 -21
- package/dist/types/markers/channels/commonMarkerConfigWithLabel.d.ts +5 -9
- package/dist/types/markers/channels/commonMarkerConfigWithOrientation.d.ts +5 -0
- package/dist/types/markers/channels/commonMarkerConfigWithOrientation.js +1 -0
- package/dist/types/markers/labels/box.d.ts +57 -0
- package/dist/types/markers/labels/box.js +1 -0
- package/dist/types/markers/labels/common.d.ts +15 -0
- package/dist/types/markers/labels/common.js +1 -0
- package/dist/types/markers/labels/path.d.ts +52 -0
- package/dist/types/markers/labels/path.js +1 -0
- package/dist/types/markers/labels/point.d.ts +40 -0
- package/dist/types/markers/labels/point.js +1 -0
- package/dist/types/markers/traits.d.ts +10 -0
- package/dist/types/phases/markers/phase.d.ts +1 -8
- package/dist/types/phases/statistics/phase.d.ts +8 -3
- package/dist/types/phases/styles/phase.d.ts +15 -3
- package/dist/types/phases/transforms/phase.d.ts +2 -0
- package/dist/types/plots/constants.d.ts +0 -12
- package/dist/types/plots/correlation/heatmap/props.d.ts +3 -2
- package/dist/types/plots/correlation/scatter/props.d.ts +3 -2
- package/dist/types/plots/data/geo.d.ts +19 -5
- package/dist/types/plots/distribution/box/props.d.ts +6 -2
- package/dist/types/plots/distribution/density/props.d.ts +2 -1
- package/dist/types/plots/distribution/histogram/props.d.ts +3 -2
- package/dist/types/plots/distribution/pyramid/props.d.ts +3 -2
- package/dist/types/plots/distribution/violin/props.d.ts +4 -1
- package/dist/types/plots/evolution/area/props.d.ts +3 -2
- package/dist/types/plots/evolution/line/props.d.ts +3 -2
- package/dist/types/plots/generic/props.d.ts +16 -0
- package/dist/types/plots/generic/props.js +1 -0
- package/dist/types/plots/generic/state.d.ts +4 -0
- package/dist/types/plots/generic/state.js +1 -0
- package/dist/types/plots/map/geo/props.d.ts +9 -3
- package/dist/types/plots/map/geo/state.d.ts +3 -1
- package/dist/types/plots/part-of-a-whole/treemap/props.d.ts +4 -2
- package/dist/types/plots/ranking/bar/props.d.ts +3 -2
- package/dist/types/plots/ranking/cleveland/props.d.ts +3 -2
- package/dist/types/plots/ranking/dumbbell/props.d.ts +3 -2
- package/dist/types/plots/ranking/forest/props.d.ts +5 -2
- package/dist/types/plots/ranking/lollipop/props.d.ts +3 -2
- package/dist/utils/ConfigEquality.d.ts +11 -0
- package/dist/utils/ConfigEquality.js +37 -0
- package/docs/architecture.md +9 -9
- package/docs/facets.md +12 -0
- package/docs/geo.md +35 -7
- package/docs/legend.md +6 -0
- package/docs/markers.md +2 -4
- package/docs/plots/ranking/bar.md +1 -1
- package/docs/plots/ranking/cleveland.md +1 -1
- package/docs/state.md +1 -1
- package/docs/styles-and-segments.md +1 -14
- package/docs/timeline.md +1 -1
- package/docs/transforms-and-statistics.md +1 -1
- package/docs/value-labels.md +159 -0
- package/examples/BarValues.svelte +1 -1
- package/llms-full.txt +5 -5
- package/llms.txt +1 -0
- package/package.json +3 -1
- package/dist/markers/utils/BoxLabel.d.ts +0 -58
- package/dist/markers/utils/BoxLabel.js +0 -175
- package/dist/markers/utils/MarkerLabel.svelte +0 -33
- package/dist/markers/utils/MarkerLabel.svelte.d.ts +0 -10
|
@@ -12,8 +12,13 @@ function readField(field, row) {
|
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* What a GeoPlot hands `Plot` to slice by: one stand-in row per value
|
|
15
|
-
* combination
|
|
16
|
-
*
|
|
15
|
+
* combination, and timeline/facet accessors that read a stand-in or a real
|
|
16
|
+
* row. A dimension's values are the ones the timeline or facet names
|
|
17
|
+
* (`timeline.values`, `facet.values`, `rowValues`, `colValues`), in
|
|
18
|
+
* their order, then any more found in the features and markers' rows - so
|
|
19
|
+
* the slices exist even when no feature carries the field. Each combination
|
|
20
|
+
* is also returned as a `slices` entry: its place and the context its
|
|
21
|
+
* features are read in.
|
|
17
22
|
*/
|
|
18
23
|
export function buildGeoSlicing(args) {
|
|
19
24
|
const { rows, timeline, facet } = args;
|
|
@@ -31,17 +36,47 @@ export function buildGeoSlicing(args) {
|
|
|
31
36
|
const reader = (dimension) => (row) => SLICE in row
|
|
32
37
|
? row[SLICE][dimension]
|
|
33
38
|
: readField(fields[dimension], row);
|
|
39
|
+
/** The values the timeline or facet names for a dimension, ahead of the ones found. */
|
|
40
|
+
const named = {
|
|
41
|
+
time: timeline?.values,
|
|
42
|
+
by: facet && isFacetWrap(facet) ? facet.values : undefined,
|
|
43
|
+
rows: facet && !isFacetWrap(facet) ? facet.rowValues : undefined,
|
|
44
|
+
cols: facet && !isFacetWrap(facet) ? facet.colValues : undefined
|
|
45
|
+
};
|
|
46
|
+
const time = new TimelineTime(timeline?.dateFormat);
|
|
34
47
|
let combos = [{}];
|
|
35
48
|
for (const dimension of Object.keys(fields)) {
|
|
36
49
|
const read = rows.map((row) => readField(fields[dimension], row)).filter((v) => v != null);
|
|
37
50
|
// Dates on the same step are one value.
|
|
38
|
-
const
|
|
39
|
-
const
|
|
40
|
-
|
|
51
|
+
const keyOf = (value) => dimension === 'time' ? String(time.toTime(value) ?? value) : String(value);
|
|
52
|
+
const values = [
|
|
53
|
+
...new Map([...(named[dimension] ?? []), ...read].map((value) => [keyOf(value), value])).values()
|
|
54
|
+
];
|
|
41
55
|
combos = combos.flatMap((combo) => values.map((value) => ({ ...combo, [dimension]: value })));
|
|
42
56
|
}
|
|
57
|
+
const slices = combos.map((combo) => {
|
|
58
|
+
const tick = combo.time === undefined ? undefined : time.toTime(combo.time);
|
|
59
|
+
const by = combo.by === undefined ? undefined : String(combo.by);
|
|
60
|
+
const row = combo.rows === undefined ? undefined : String(combo.rows);
|
|
61
|
+
const col = combo.cols === undefined ? undefined : String(combo.cols);
|
|
62
|
+
const value = by ?? (row !== undefined && col !== undefined ? `${row} · ${col}` : (row ?? col));
|
|
63
|
+
const place = {
|
|
64
|
+
...(tick === undefined ? {} : { time: tick }),
|
|
65
|
+
...(value === undefined
|
|
66
|
+
? {}
|
|
67
|
+
: {
|
|
68
|
+
facet: {
|
|
69
|
+
facetValue: value,
|
|
70
|
+
...(row === undefined ? {} : { row }),
|
|
71
|
+
...(col === undefined ? {} : { col })
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
};
|
|
75
|
+
return { place, context: GeoSlices.contextOf(place) };
|
|
76
|
+
});
|
|
43
77
|
return {
|
|
44
78
|
rows: combos.map((combo) => ({ [SLICE]: combo })),
|
|
79
|
+
slices,
|
|
45
80
|
timeline: timeline && {
|
|
46
81
|
...timeline,
|
|
47
82
|
accessor: reader('time'),
|
|
@@ -57,3 +92,21 @@ export function buildGeoSlicing(args) {
|
|
|
57
92
|
})
|
|
58
93
|
};
|
|
59
94
|
}
|
|
95
|
+
export const GeoSlices = {
|
|
96
|
+
/** The context a callback reads a feature in, for the slice at `place`: empty when there is none. */
|
|
97
|
+
contextOf(place) {
|
|
98
|
+
const { time, facet } = place;
|
|
99
|
+
return {
|
|
100
|
+
...(facet
|
|
101
|
+
? {
|
|
102
|
+
facet: {
|
|
103
|
+
value: facet.facetValue,
|
|
104
|
+
...(facet.row === undefined ? {} : { row: facet.row }),
|
|
105
|
+
...(facet.col === undefined ? {} : { col: facet.col })
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
: {}),
|
|
109
|
+
...(time === undefined ? {} : { time })
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" generics="TData extends Record<string, unknown>">
|
|
2
|
-
import
|
|
2
|
+
import AbstractPlot from '../../../layout/plot/AbstractPlot.svelte';
|
|
3
3
|
import { buildTreemapDefaults } from './defaults';
|
|
4
4
|
import type {
|
|
5
5
|
TreemapPlotProps,
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
}: TreemapPlotProps<TData> = $props();
|
|
31
31
|
</script>
|
|
32
32
|
|
|
33
|
-
<
|
|
33
|
+
<AbstractPlot
|
|
34
34
|
core={core as TreemapPlotCoreProps<TData>}
|
|
35
35
|
defaults={buildTreemapDefaults}
|
|
36
36
|
bind:margins
|
|
@@ -87,7 +87,7 @@ function buildMarks(state, env) {
|
|
|
87
87
|
};
|
|
88
88
|
/** The label of a series' tiles: the name over the share, fitted - or the share alone where the name does not fit. */
|
|
89
89
|
const labelOf = (labels) => {
|
|
90
|
-
const shape = labels.
|
|
90
|
+
const shape = labels.label(env, (row) => `${contextOf(row).name}
|
|
91
91
|
${shareText(row)}`, { fontSize: look.fontSize });
|
|
92
92
|
return {
|
|
93
93
|
...shape,
|
|
@@ -155,7 +155,7 @@ export function buildTreemapDefaults() {
|
|
|
155
155
|
}),
|
|
156
156
|
fit: () => ({ x: { domain: [0, 1], fitted: true }, y: { domain: [0, 1], fitted: true } })
|
|
157
157
|
},
|
|
158
|
-
styles: { values: { show: true } },
|
|
158
|
+
styles: { values: { show: true, layout: 'box' } },
|
|
159
159
|
markers: { marks: buildMarks },
|
|
160
160
|
hover: {
|
|
161
161
|
strategy: 'punctual',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" generics="TData extends Record<string, unknown>">
|
|
2
|
-
import
|
|
2
|
+
import AbstractPlot from '../../../layout/plot/AbstractPlot.svelte';
|
|
3
3
|
import { buildBarDefaults } from './defaults';
|
|
4
4
|
import type { BarPlotProps, BarPlotCoreProps } from '../../../types/plots/ranking/bar/props';
|
|
5
5
|
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
}: BarPlotProps<TData> = $props();
|
|
25
25
|
</script>
|
|
26
26
|
|
|
27
|
-
<
|
|
27
|
+
<AbstractPlot
|
|
28
28
|
core={core as BarPlotCoreProps<TData>}
|
|
29
29
|
defaults={buildBarDefaults}
|
|
30
30
|
bind:margins
|
|
@@ -59,17 +59,27 @@ export function buildBarDefaults() {
|
|
|
59
59
|
}),
|
|
60
60
|
fit: (state) => fitValueDomain(state)
|
|
61
61
|
},
|
|
62
|
+
transforms: { labels: {} },
|
|
62
63
|
styles: {
|
|
63
64
|
values: {
|
|
65
|
+
layout: (state) => ProcessedPositions.span(state.processedData, PlotAxes.valueOf(state))
|
|
66
|
+
.stacked
|
|
67
|
+
? 'box'
|
|
68
|
+
: 'path',
|
|
69
|
+
layouts: {
|
|
70
|
+
path: { edge: 'tip', side: 'outside' },
|
|
71
|
+
box: { anchor: 'middle' },
|
|
72
|
+
point: { anchor: 'top', side: 'above' }
|
|
73
|
+
},
|
|
64
74
|
// An `'outside'` label sits past its bar's end, which the tallest bar takes to the edge.
|
|
65
75
|
reach: (labels, state) => {
|
|
66
|
-
if (labels.
|
|
76
|
+
if (!labels.pastTip)
|
|
67
77
|
return [];
|
|
68
78
|
const axis = PlotAxes.valueOf(state);
|
|
69
79
|
if (ProcessedPositions.span(state.processedData, axis).stacked)
|
|
70
80
|
return [];
|
|
71
81
|
if (!state.axes?.isHorizontal) {
|
|
72
|
-
const offset = labels.offset(
|
|
82
|
+
const offset = labels.offset();
|
|
73
83
|
const boxes = [
|
|
74
84
|
labels.edgeBox('0', { x: 'middle', y: 'before' }, { y: -offset }, ['top'])
|
|
75
85
|
];
|
|
@@ -82,7 +92,7 @@ export function buildBarDefaults() {
|
|
|
82
92
|
return boxes;
|
|
83
93
|
}
|
|
84
94
|
// A bar below zero reaches out to the left, with its label turned that way.
|
|
85
|
-
const offset = labels.offset(
|
|
95
|
+
const offset = labels.offset();
|
|
86
96
|
return labels
|
|
87
97
|
.extremes(state.resolvedSeries ?? [], PlotAxes.valueOf(state), 'both')
|
|
88
98
|
.map(({ series, value }) => value < 0
|
|
@@ -4,7 +4,6 @@ import { GAP_KIND, GAP_VALUE, SERIES_ID_KEY } from '../../../phases/transforms/T
|
|
|
4
4
|
import { seriesIdOf } from '../../../utils/grouping';
|
|
5
5
|
import { resolveGapAreaStyle } from '../../utils/gaps';
|
|
6
6
|
import { PlotAxes } from '../../../phases/data/PlotAxes';
|
|
7
|
-
import { ValueLabels } from '../../../layout/styles/ValueLabels';
|
|
8
7
|
const GAP_KINDS = ['start', 'middle', 'end'];
|
|
9
8
|
/**
|
|
10
9
|
* Where a bar plot's bars sit, read off the state its phases leave: each
|
|
@@ -58,11 +57,11 @@ hover, env) {
|
|
|
58
57
|
data: [row],
|
|
59
58
|
category: categoryOf(row),
|
|
60
59
|
hover: hover.active && 'trigger',
|
|
61
|
-
|
|
60
|
+
styles: (slice
|
|
62
61
|
? isHorizontal
|
|
63
62
|
? { insetTop: slice.start, insetBottom: slice.end }
|
|
64
63
|
: { insetLeft: slice.start, insetRight: slice.end }
|
|
65
|
-
:
|
|
64
|
+
: undefined)
|
|
66
65
|
};
|
|
67
66
|
});
|
|
68
67
|
}
|
|
@@ -89,21 +88,14 @@ export function buildMarks(state, env) {
|
|
|
89
88
|
// which stays a field key whenever the plot has no transforms.
|
|
90
89
|
const valueOf = (channel) => (geometry.stacked || channel === undefined ? top : channel);
|
|
91
90
|
const gapValue = valueOf(channels[valueAxis]);
|
|
92
|
-
// What each bar writes
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
const turns = !!labels && !geometry.stacked && ValueLabels.followsSign(labels.anchor);
|
|
98
|
-
const labelAnchor = (negative) => ValueLabels.boxAnchor(labels?.anchor ?? 'outside', {
|
|
99
|
-
isHorizontal,
|
|
100
|
-
negative,
|
|
101
|
-
stacked: geometry.stacked
|
|
102
|
-
});
|
|
103
|
-
const labelFor = (name, slice, negative) => labels && {
|
|
104
|
-
...labels.shapeLabel(env, (d) => labels.format(Number(valueRead(d)), d, name), {
|
|
91
|
+
// What each bar writes about itself: its value, in the layout of `styles.values` - by default past its tip, or
|
|
92
|
+
// in the middle of a stacked bar's segment.
|
|
93
|
+
const labels = state.valueLabels?.show && state.valueLabels.writes('value') ? state.valueLabels : undefined;
|
|
94
|
+
const labelFor = (name, slice) => labels && {
|
|
95
|
+
...labels.label(env, (d) => labels.text(d), {
|
|
105
96
|
fill: 'currentColor',
|
|
106
|
-
|
|
97
|
+
labelled: state.labelled,
|
|
98
|
+
seriesColor: () => colorForSeries(name)
|
|
107
99
|
}),
|
|
108
100
|
bandSlice: slice
|
|
109
101
|
};
|
|
@@ -155,18 +147,9 @@ export function buildMarks(state, env) {
|
|
|
155
147
|
};
|
|
156
148
|
const real = seg.data.filter(isReal);
|
|
157
149
|
const value = valueOf(group.series[valueAxis]);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
negative,
|
|
162
|
-
rows: real.filter((row) => Number(valueRead(row)) < 0 === negative)
|
|
163
|
-
}))
|
|
164
|
-
: [{ negative: false, rows: real }];
|
|
165
|
-
for (const { negative, rows } of parts) {
|
|
166
|
-
const marker = makeMarker(rows, slice, value, styles, labelFor(name, slice, negative));
|
|
167
|
-
if (marker)
|
|
168
|
-
segmentMarkers.push(marker);
|
|
169
|
-
}
|
|
150
|
+
const marker = makeMarker(real, slice, value, styles, labelFor(name, slice));
|
|
151
|
+
if (marker)
|
|
152
|
+
segmentMarkers.push(marker);
|
|
170
153
|
}
|
|
171
154
|
const id = seriesIdOf(group.series);
|
|
172
155
|
const fills = [...(gapFillMaps.get(id)?.values() ?? [])];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" generics="TData extends Record<string, unknown>">
|
|
2
|
-
import
|
|
2
|
+
import AbstractPlot from '../../../layout/plot/AbstractPlot.svelte';
|
|
3
3
|
import { buildBulletDefaults } from './defaults';
|
|
4
4
|
import type { BulletPlotProps, BulletPlotCoreProps } from '../../../types/plots/ranking/bullet/props';
|
|
5
5
|
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
}: BulletPlotProps<TData> = $props();
|
|
26
26
|
</script>
|
|
27
27
|
|
|
28
|
-
<
|
|
28
|
+
<AbstractPlot
|
|
29
29
|
core={core as BulletPlotCoreProps<TData>}
|
|
30
30
|
defaults={buildBulletDefaults}
|
|
31
31
|
bind:margins
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ValueLabels } from '../../../layout/styles/ValueLabels';
|
|
2
|
+
import { RowNumbers } from '../../utils/RowNumbers';
|
|
1
3
|
import { buildBarDefaults, fitValueDomain } from '../bar/defaults';
|
|
2
4
|
import { buildMarks, buildReach } from './markers';
|
|
3
5
|
/**
|
|
@@ -19,6 +21,24 @@ export function buildBulletDefaults() {
|
|
|
19
21
|
}),
|
|
20
22
|
fit: (state, env) => fitValueDomain(state, buildReach(state, env))
|
|
21
23
|
},
|
|
24
|
+
// Each bar's target and range bounds, for its tooltip, hover and value labels to read.
|
|
25
|
+
context: (state) => ((row) => ({
|
|
26
|
+
target: RowNumbers.of(state.target, row),
|
|
27
|
+
ranges: (state.ranges ?? [])
|
|
28
|
+
.map((range) => RowNumbers.of(range, row))
|
|
29
|
+
.filter((bound) => bound !== undefined)
|
|
30
|
+
.sort((a, b) => a - b)
|
|
31
|
+
})),
|
|
32
|
+
styles: {
|
|
33
|
+
...bar.styles,
|
|
34
|
+
values: {
|
|
35
|
+
...bar.styles?.values,
|
|
36
|
+
parts: {
|
|
37
|
+
placements: (state) => ValueLabels.besideBand(state, ['target', 'ranges']),
|
|
38
|
+
default: ['value']
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
22
42
|
markers: { ...bar.markers, marks: buildMarks }
|
|
23
43
|
};
|
|
24
44
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ValueLabels } from '../../../layout/styles/ValueLabels';
|
|
1
2
|
import { buildMarks as buildBarMarks, buildBarGeometry } from '../bar/markers';
|
|
2
3
|
import { GAP_VALUE } from '../../../phases/transforms/TransformsPhase';
|
|
3
4
|
import { RowNumbers } from '../../utils/RowNumbers';
|
|
@@ -85,5 +86,10 @@ export function buildMarks(state, env) {
|
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
];
|
|
88
|
-
|
|
89
|
+
// Its other parts `styles.values` labels - the target, the range bounds - beside the band; the bars write the value.
|
|
90
|
+
const geometry = buildBarGeometry(state, env.cfg.bar.seriesPadding);
|
|
91
|
+
const labels = state.valueLabels?.show
|
|
92
|
+
? state.valueLabels.partMarkers(env, (state.processedData?.data ?? []).filter((row) => row[GAP_VALUE] === undefined && geometry.spanOf(row) !== undefined), ValueLabels.bandPoint(state, geometry.categoryOf), { skip: ['value'] })
|
|
93
|
+
: [];
|
|
94
|
+
return [...ranges, ...bars, ...ticks, ...labels];
|
|
89
95
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ValueLabels } from '../../../layout/styles/ValueLabels';
|
|
2
2
|
import type { ClevelandPlotState, ClevelandPlotEnv, ClevelandOptions } from '../../../types/plots/ranking/cleveland/state';
|
|
3
|
-
import type { AxisBasedMarkersConfig
|
|
3
|
+
import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
|
|
4
4
|
import type { EdgeBox } from '../../../types/layout/box';
|
|
5
5
|
/**
|
|
6
6
|
* The marks of the Cleveland family of plots, which differ only by their
|
|
@@ -8,7 +8,7 @@ import type { EdgeBox } from '../../../types/layout/box';
|
|
|
8
8
|
* highest value (from the baseline, for a lollipop) with a dot at each value.
|
|
9
9
|
* A category with one dot gets a stem in that dot's series color, restyled by
|
|
10
10
|
* the segment its dot belongs to; a category with several gets one neutral
|
|
11
|
-
* connector. The "markers" phase takes its `marks
|
|
11
|
+
* connector. The "markers" phase takes its `marks` and `reach`.
|
|
12
12
|
*/
|
|
13
13
|
export declare class ClevelandMarkers {
|
|
14
14
|
#private;
|
|
@@ -17,13 +17,6 @@ export declare class ClevelandMarkers {
|
|
|
17
17
|
static resetWarnings(): void;
|
|
18
18
|
/** The plot's connectors, then its dots, which draw on top of them. */
|
|
19
19
|
marks: <TData extends Record<string, unknown>>(state: ClevelandPlotState<TData>, env: ClevelandPlotEnv) => AxisBasedMarkersConfig[];
|
|
20
|
-
/**
|
|
21
|
-
* The value labels of the dots `styles.values.of` picks, one `'text'` marker
|
|
22
|
-
* per place a dot can hold in its range - past the top end (above, or right
|
|
23
|
-
* of it), past the bottom end (below, or left of it) and, for the dots
|
|
24
|
-
* between, beside it - and per series, when they take their series' color.
|
|
25
|
-
*/
|
|
26
|
-
valueLabels: <TData extends Record<string, unknown>>(labels: ValueLabels, state: ClevelandPlotState<TData>, env: ClevelandPlotEnv) => TextMarkerConfig[];
|
|
27
20
|
/**
|
|
28
21
|
* Room for the labels past the dots at the end of the value axis, so the
|
|
29
22
|
* farthest one fits inside the figure instead of crossing its edge. Only the
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isGapValue } from '../../utils/gaps';
|
|
1
2
|
import { PlotAxes } from '../../../phases/data/PlotAxes';
|
|
2
3
|
import { ProcessedPositions } from '../../../phases/transforms/ProcessedPositions';
|
|
3
4
|
import { SERIES_KEY } from '../../../phases/transforms/TransformsPhase';
|
|
@@ -5,6 +6,7 @@ import { ValueLabels } from '../../../layout/styles/ValueLabels';
|
|
|
5
6
|
import { ClevelandRanges } from './ClevelandRanges';
|
|
6
7
|
import { ClevelandConnector } from './ClevelandConnector';
|
|
7
8
|
import { BasicStyles } from '../../../layout/styles/BasicStyles';
|
|
9
|
+
import { PointLayout } from '../../../markers/labels/layouts/PointLayout';
|
|
8
10
|
/** The messages already warned about, so a plot that redraws warns once. */
|
|
9
11
|
const warned = new Set();
|
|
10
12
|
/**
|
|
@@ -13,7 +15,7 @@ const warned = new Set();
|
|
|
13
15
|
* highest value (from the baseline, for a lollipop) with a dot at each value.
|
|
14
16
|
* A category with one dot gets a stem in that dot's series color, restyled by
|
|
15
17
|
* the segment its dot belongs to; a category with several gets one neutral
|
|
16
|
-
* connector. The "markers" phase takes its `marks
|
|
18
|
+
* connector. The "markers" phase takes its `marks` and `reach`.
|
|
17
19
|
*/
|
|
18
20
|
export class ClevelandMarkers {
|
|
19
21
|
#options;
|
|
@@ -202,12 +204,24 @@ export class ClevelandMarkers {
|
|
|
202
204
|
};
|
|
203
205
|
});
|
|
204
206
|
});
|
|
207
|
+
const values = state.valueLabels?.show ? state.valueLabels : undefined;
|
|
208
|
+
const labels = values?.writes('value') ? this.#valueLabels(values, state, env) : undefined;
|
|
209
|
+
// The other parts of a row it labels - an interval's ends - beside them; the dots write the value.
|
|
210
|
+
const processed = state.processedData;
|
|
211
|
+
const position = ProcessedPositions.of(processed, PlotAxes.valueOf(state));
|
|
212
|
+
const parts = values
|
|
213
|
+
? values.partMarkers(env, processed.data.filter((row) => !isGapValue(position[PlotAxes.valueOf(state)](row))), ValueLabels.bandPoint(state, position[PlotAxes.categoryOf(state)]), {
|
|
214
|
+
skip: ['value'],
|
|
215
|
+
seriesColor: (row) => state.colorForSeries(String(row[SERIES_KEY]))
|
|
216
|
+
})
|
|
217
|
+
: [];
|
|
205
218
|
return [
|
|
206
219
|
...rules,
|
|
207
220
|
...caps,
|
|
208
221
|
...arrows,
|
|
209
|
-
...dots,
|
|
210
|
-
...this.#connectorLabels(state, env, connector, looks, isHorizontal)
|
|
222
|
+
...ValueLabels.onMarkers(dots, 'dot', labels),
|
|
223
|
+
...this.#connectorLabels(state, env, connector, looks, isHorizontal),
|
|
224
|
+
...parts
|
|
211
225
|
];
|
|
212
226
|
};
|
|
213
227
|
/** The dash, cap and join a connector is drawn with, leaving out what is not set. */
|
|
@@ -282,68 +296,72 @@ export class ClevelandMarkers {
|
|
|
282
296
|
];
|
|
283
297
|
}
|
|
284
298
|
/**
|
|
285
|
-
* The value labels of the dots `styles.values
|
|
286
|
-
*
|
|
287
|
-
* of it), past the bottom end (below, or left of it) and, for the dots
|
|
288
|
-
* between, beside it - and per series, when they take their series' color.
|
|
299
|
+
* The value labels of the dots `styles.values` picks, placed by where each dot sits in its range - or on the
|
|
300
|
+
* `side` the author asks of them all, or around each dot's rim (`'path'`).
|
|
289
301
|
*/
|
|
290
|
-
valueLabels
|
|
302
|
+
#valueLabels(labels, state, env) {
|
|
291
303
|
if (!state.processedData?.data.length)
|
|
292
304
|
return [];
|
|
293
305
|
const { isHorizontal } = ClevelandMarkers.#geometry(state);
|
|
294
|
-
const { category, value } = ClevelandMarkers.#channels(state);
|
|
295
306
|
const { font, format } = labels;
|
|
296
307
|
const read = ValueLabels.valueOf(state);
|
|
297
308
|
const valueOf = ProcessedPositions.of(state.processedData, PlotAxes.valueOf(state))[PlotAxes.valueOf(state)];
|
|
298
|
-
const gap = labels.offset(
|
|
309
|
+
const gap = labels.offset(env.cfg[this.#options.config].labelGap);
|
|
299
310
|
const nameOf = (d) => String(d[SERIES_KEY]);
|
|
300
311
|
const picked = this.#picked(state);
|
|
301
|
-
//
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
312
|
+
// Along the range past an end, across it between them.
|
|
313
|
+
const side = labels.placement.side;
|
|
314
|
+
const own = side && { ...PointLayout.sideOf(side, gap) };
|
|
315
|
+
const placement = own
|
|
316
|
+
? { max: own, min: own, between: own }
|
|
317
|
+
: {
|
|
318
|
+
max: isHorizontal
|
|
319
|
+
? { dx: gap, dy: 0, textAnchor: 'start', lineAnchor: 'middle' }
|
|
320
|
+
: { dx: 0, dy: -gap, textAnchor: 'middle', lineAnchor: 'bottom' },
|
|
321
|
+
min: isHorizontal
|
|
322
|
+
? { dx: -gap, dy: 0, textAnchor: 'end', lineAnchor: 'middle' }
|
|
323
|
+
: { dx: 0, dy: gap, textAnchor: 'middle', lineAnchor: 'top' },
|
|
324
|
+
between: isHorizontal
|
|
325
|
+
? { dx: 0, dy: -gap, textAnchor: 'middle', lineAnchor: 'bottom' }
|
|
326
|
+
: { dx: gap, dy: 0, textAnchor: 'start', lineAnchor: 'middle' }
|
|
327
|
+
};
|
|
328
|
+
const fill = labels.fill ??
|
|
329
|
+
(labels.color === 'series'
|
|
330
|
+
? (d) => state.colorForSeries(nameOf(d))
|
|
331
|
+
: 'currentColor');
|
|
332
|
+
if (labels.layout === 'path') {
|
|
333
|
+
const text = (d) => format(Number(read(d) ?? valueOf(d)), d, nameOf(d));
|
|
334
|
+
const rim = labels.label(env, text, { fontSize: 12 });
|
|
335
|
+
return [{ ...rim, styles: { ...rim.styles, fill: font?.fill ?? fill } }];
|
|
336
|
+
}
|
|
337
|
+
const { styles: shared, ...hooks } = labels.hooks(env);
|
|
338
|
+
return ['max', 'min', 'between'].flatMap((role) => {
|
|
339
|
+
const rows = new Set(picked
|
|
340
|
+
.filter(({ entry, dot }) => ClevelandRanges.roleOf(entry, dot.value) === role)
|
|
341
|
+
.map(({ dot }) => dot.row));
|
|
342
|
+
if (rows.size === 0)
|
|
326
343
|
return [];
|
|
327
344
|
const at = placement[role];
|
|
328
345
|
return [
|
|
329
346
|
{
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
347
|
+
...hooks,
|
|
348
|
+
layout: 'point',
|
|
349
|
+
select: (row) => rows.has(row),
|
|
333
350
|
text: (d) => format(Number(read(d) ?? valueOf(d)), d, nameOf(d)),
|
|
334
351
|
styles: {
|
|
352
|
+
...shared,
|
|
335
353
|
...font,
|
|
336
|
-
fill
|
|
354
|
+
fill,
|
|
337
355
|
fontSize: font?.fontSize ?? 12,
|
|
338
|
-
dx:
|
|
339
|
-
dy:
|
|
340
|
-
textAnchor:
|
|
341
|
-
lineAnchor:
|
|
356
|
+
dx: at.dx,
|
|
357
|
+
dy: at.dy,
|
|
358
|
+
textAnchor: at.textAnchor,
|
|
359
|
+
lineAnchor: at.lineAnchor
|
|
342
360
|
}
|
|
343
361
|
}
|
|
344
362
|
];
|
|
345
|
-
})
|
|
346
|
-
}
|
|
363
|
+
});
|
|
364
|
+
}
|
|
347
365
|
/**
|
|
348
366
|
* Room for the labels past the dots at the end of the value axis, so the
|
|
349
367
|
* farthest one fits inside the figure instead of crossing its edge. Only the
|
|
@@ -355,10 +373,10 @@ export class ClevelandMarkers {
|
|
|
355
373
|
if (top.length === 0)
|
|
356
374
|
return [];
|
|
357
375
|
if (!state.axes?.isHorizontal) {
|
|
358
|
-
const at = { y: -labels.offset(
|
|
376
|
+
const at = { y: -labels.offset(gap) };
|
|
359
377
|
return [labels.edgeBox('0', { x: 'middle', y: 'before' }, at, ['top'])];
|
|
360
378
|
}
|
|
361
|
-
const at = { x: labels.offset(
|
|
379
|
+
const at = { x: labels.offset(gap) };
|
|
362
380
|
const texts = new Set(top.map(({ dot }) => labels.format(dot.value, dot.row, dot.series)));
|
|
363
381
|
return [...texts].map((text) => labels.edgeBox(text, { x: 'after', y: 'middle' }, at, ['right']));
|
|
364
382
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" generics="TData extends Record<string, unknown>">
|
|
2
|
-
import
|
|
2
|
+
import AbstractPlot from '../../../layout/plot/AbstractPlot.svelte';
|
|
3
3
|
import { buildClevelandDefaults } from './defaults';
|
|
4
4
|
import type {
|
|
5
5
|
ClevelandPlotProps,
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
}: ClevelandPlotProps<TData> = $props();
|
|
29
29
|
</script>
|
|
30
30
|
|
|
31
|
-
<
|
|
31
|
+
<AbstractPlot
|
|
32
32
|
core={core as ClevelandPlotCoreProps<TData>}
|
|
33
33
|
defaults={buildClevelandDefaults}
|
|
34
34
|
bind:margins
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ValueLabels } from '../../../layout/styles/ValueLabels';
|
|
2
|
+
import { RowNumbers } from '../../utils/RowNumbers';
|
|
1
3
|
import { ClevelandMarkers } from './ClevelandMarkers';
|
|
2
4
|
import { HoverDefaults } from '../../../layout/hover/HoverDefaults';
|
|
3
5
|
import { ClevelandRanges } from './ClevelandRanges';
|
|
@@ -63,8 +65,37 @@ export function buildClevelandDefaults(options = { name: 'ClevelandPlot', config
|
|
|
63
65
|
},
|
|
64
66
|
// A dot is labelled by what its category's dots say: the first, the highest... of each.
|
|
65
67
|
transforms: { labels: { group: 'category', skip: ClevelandRanges.skipGaps } },
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
// An interval's ends, for its tooltip, hover and value labels to read.
|
|
69
|
+
context: options.interval
|
|
70
|
+
? (state) => {
|
|
71
|
+
const valueOf = ValueLabels.valueOf(state);
|
|
72
|
+
return ((row) => {
|
|
73
|
+
const [lower, upper] = RowNumbers.interval(state, row, Number(valueOf(row)));
|
|
74
|
+
return { lower, upper };
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
: undefined,
|
|
78
|
+
styles: {
|
|
79
|
+
values: {
|
|
80
|
+
layout: 'point',
|
|
81
|
+
layouts: { path: { side: 'outside' } },
|
|
82
|
+
// An interval's ends sit past it, each on its own side.
|
|
83
|
+
parts: options.interval
|
|
84
|
+
? {
|
|
85
|
+
placements: (state) => {
|
|
86
|
+
const sideways = state.axes?.isHorizontal;
|
|
87
|
+
return {
|
|
88
|
+
lower: { side: sideways ? 'before' : 'below' },
|
|
89
|
+
upper: { side: sideways ? 'after' : 'above' }
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
default: ['value']
|
|
93
|
+
}
|
|
94
|
+
: undefined,
|
|
95
|
+
reach: (labels, state) => (labels.layout === 'point' ? markers.reach(labels, state) : [])
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
markers: { marks: markers.marks },
|
|
68
99
|
// A rule along the value axis at the hovered dot, and a dot and a label on each one of its category.
|
|
69
100
|
hover: { marker: (state) => HoverDefaults.valueRule(PlotAxes.valueOf(state)) }
|
|
70
101
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" generics="TData extends Record<string, unknown>">
|
|
2
|
-
import
|
|
2
|
+
import AbstractPlot from '../../../layout/plot/AbstractPlot.svelte';
|
|
3
3
|
import { buildDumbbellDefaults } from './defaults';
|
|
4
4
|
import type {
|
|
5
5
|
DumbbellPlotProps,
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
}: DumbbellPlotProps<TData> = $props();
|
|
29
29
|
</script>
|
|
30
30
|
|
|
31
|
-
<
|
|
31
|
+
<AbstractPlot
|
|
32
32
|
core={core as DumbbellPlotCoreProps<TData>}
|
|
33
33
|
defaults={buildDumbbellDefaults}
|
|
34
34
|
bind:margins
|