@fundar/data-chart-telling 1.16.0 → 1.18.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 +56 -0
- package/dist/configuration/ConfigVars.svelte +14 -0
- package/dist/configuration/ConfigVars.svelte.d.ts +3 -0
- package/dist/configuration/config.svelte.d.ts +5 -0
- package/dist/configuration/config.svelte.js +20 -6
- package/dist/configuration/themes/dark.js +1 -1
- package/dist/configuration/themes/editorial.js +1 -1
- package/dist/index.d.ts +12 -4
- package/dist/index.js +4 -1
- package/dist/layout/base/BasePlotLayout.svelte +1 -1
- package/dist/layout/figure/Figure.svelte +5 -12
- package/dist/layout/figure/FigureChrome.svelte +2 -10
- package/dist/layout/geometry/GeometryLayout.svelte +219 -44
- package/dist/layout/hover/HoverDefaults.d.ts +52 -0
- package/dist/layout/hover/HoverDefaults.js +96 -0
- package/dist/layout/hover/HoverLayers.d.ts +35 -0
- package/dist/layout/hover/HoverLayers.js +122 -0
- package/dist/layout/hover/HoverSettings.js +4 -1
- package/dist/layout/hover/HoverShapes.d.ts +44 -0
- package/dist/layout/hover/HoverShapes.js +103 -0
- package/dist/layout/hover/PlotHover.svelte.d.ts +18 -0
- package/dist/layout/hover/PlotHover.svelte.js +54 -1
- package/dist/layout/legend/DisabledStyle.d.ts +1 -1
- package/dist/layout/legend/DisabledStyle.js +1 -1
- package/dist/layout/legend/DiscreteBadgeItem.svelte +1 -1
- package/dist/layout/legend/DiscreteSection.svelte +1 -1
- package/dist/layout/legend/LegendState.svelte.d.ts +14 -2
- package/dist/layout/legend/LegendState.svelte.js +23 -4
- package/dist/layout/legend/LegendText.d.ts +4 -1
- package/dist/layout/legend/LegendText.js +7 -1
- package/dist/layout/margins/PlotMargins.svelte.d.ts +2 -2
- package/dist/layout/margins/PlotMargins.svelte.js +2 -2
- package/dist/layout/plot/Core.svelte +2 -2
- package/dist/layout/plot/Plot.svelte +4 -2
- package/dist/layout/plot/Plot.svelte.d.ts +2 -1
- package/dist/layout/plot/PublicPlotContext.d.ts +6 -0
- package/dist/layout/plot/PublicPlotContext.js +8 -0
- package/dist/layout/scales/ScalesLayout.svelte +1 -1
- package/dist/layout/styles/ValueLabels.d.ts +43 -4
- package/dist/layout/styles/ValueLabels.js +87 -19
- package/dist/layout/timeline/Timeline.svelte +5 -1
- package/dist/layout/tooltip/TooltipState.svelte.d.ts +8 -9
- package/dist/layout/tooltip/TooltipState.svelte.js +28 -29
- package/dist/markers/MarkerKinds.d.ts +5 -0
- package/dist/markers/MarkerKinds.js +1 -0
- package/dist/markers/area/AreaMarker.svelte +3 -0
- package/dist/markers/arrow/ArrowMarker.svelte +9 -5
- package/dist/markers/band/BandMarker.svelte +74 -0
- package/dist/markers/band/BandMarker.svelte.d.ts +7 -0
- package/dist/markers/band/Bands.d.ts +16 -0
- package/dist/markers/band/Bands.js +48 -0
- package/dist/markers/bar/BarMarker.svelte +50 -96
- package/dist/markers/bollinger/BollingerMarker.svelte +3 -0
- package/dist/markers/box/BoxMarker.svelte +3 -1
- package/dist/markers/cell/CellMarker.svelte +11 -7
- package/dist/markers/contour/ContourMarker.svelte +3 -0
- package/dist/markers/delaunay/DelaunayLinkMarker.svelte +9 -5
- package/dist/markers/delaunay/DelaunayMeshMarker.svelte +3 -0
- package/dist/markers/density/DensityMarker.svelte +3 -0
- package/dist/markers/difference/DifferenceMarker.svelte +3 -0
- package/dist/markers/dot/DotMarker.svelte +11 -6
- package/dist/markers/geo/GraticuleMarker.svelte +3 -0
- package/dist/markers/geo/SphereMarker.svelte +3 -0
- package/dist/markers/hover/HoverMarker.svelte +7 -249
- package/dist/markers/hover/HoverMarker.svelte.d.ts +4 -27
- package/dist/markers/hull/HullMarker.svelte +3 -0
- package/dist/markers/image/ImageMarker.svelte +3 -0
- package/dist/markers/line/LineMarker.svelte +3 -0
- package/dist/markers/link/LinkMarker.svelte +3 -0
- package/dist/markers/raster/RasterMarker.svelte +3 -0
- package/dist/markers/rect/RectMarker.svelte +25 -17
- package/dist/markers/regression/RegressionMarker.svelte +3 -0
- package/dist/markers/rule/RuleMarker.svelte +15 -5
- package/dist/markers/text/TextFit.d.ts +44 -0
- package/dist/markers/text/TextFit.js +86 -0
- package/dist/markers/text/TextMarker.svelte +186 -90
- package/dist/markers/text/TextMarker.svelte.d.ts +2 -0
- package/dist/markers/tick/TickMarker.svelte +9 -3
- package/dist/markers/trail/TrailMarker.svelte +3 -0
- package/dist/markers/utils/BoxLabel.d.ts +58 -0
- package/dist/markers/utils/BoxLabel.js +175 -0
- package/dist/markers/utils/MarkerEvents.d.ts +23 -0
- package/dist/markers/utils/MarkerEvents.js +44 -0
- package/dist/markers/utils/MarkerLabel.svelte +33 -0
- package/dist/markers/utils/MarkerLabel.svelte.d.ts +10 -0
- package/dist/markers/utils/PointPixel.d.ts +37 -0
- package/dist/markers/utils/PointPixel.js +44 -0
- package/dist/markers/utils/RowStroke.d.ts +9 -21
- package/dist/markers/utils/RowStroke.js +10 -21
- package/dist/markers/utils/RowStyle.d.ts +26 -0
- package/dist/markers/utils/RowStyle.js +55 -0
- package/dist/markers/vector/VectorMarker.svelte +3 -0
- package/dist/markers/voronoi/VoronoiMarker.svelte +9 -5
- package/dist/markers/voronoi/VoronoiMeshMarker.svelte +3 -0
- package/dist/markers/waffle/WaffleMarker.svelte +9 -5
- package/dist/phases/Pipeline.d.ts +10 -8
- package/dist/phases/Pipeline.js +13 -9
- package/dist/phases/context/ContextPhase.d.ts +22 -0
- package/dist/phases/context/ContextPhase.js +23 -0
- package/dist/phases/context/RowContext.d.ts +35 -0
- package/dist/phases/context/RowContext.js +95 -0
- package/dist/phases/data/DataPhase.d.ts +4 -4
- package/dist/phases/data/DataPhase.js +53 -12
- package/dist/phases/hover/HoverPhase.d.ts +6 -7
- package/dist/phases/hover/HoverPhase.js +28 -15
- package/dist/phases/mapping/MappingPhase.d.ts +4 -4
- package/dist/phases/mapping/MappingPhase.js +4 -3
- package/dist/phases/markers/MarkerStyles.d.ts +26 -0
- package/dist/phases/markers/MarkerStyles.js +126 -0
- package/dist/phases/markers/MarkersPhase.d.ts +5 -5
- package/dist/phases/markers/MarkersPhase.js +8 -6
- package/dist/phases/scales/ScalesPhase.d.ts +4 -4
- package/dist/phases/scales/ScalesPhase.js +4 -4
- package/dist/phases/sharedScales/SharedScalesPhase.d.ts +2 -2
- package/dist/phases/sharedScales/SharedScalesPhase.js +2 -2
- package/dist/phases/statistics/MarkerGrouping.d.ts +0 -9
- package/dist/phases/statistics/MarkerGrouping.js +0 -16
- package/dist/phases/statistics/StatisticsPhase.js +1 -1
- package/dist/phases/styles/StylesPhase.d.ts +3 -3
- package/dist/phases/styles/StylesPhase.js +2 -2
- package/dist/phases/tooltip/TooltipPhase.d.ts +4 -4
- package/dist/phases/tooltip/TooltipPhase.js +10 -7
- package/dist/phases/transforms/Transforms.js +3 -1
- package/dist/phases/transforms/TransformsPhase.d.ts +3 -3
- package/dist/phases/transforms/TransformsPhase.js +60 -6
- package/dist/phases/transforms/methods/TreemapTransform.d.ts +28 -0
- package/dist/phases/transforms/methods/TreemapTransform.js +119 -0
- package/dist/plots/correlation/heatmap/defaults.d.ts +2 -2
- package/dist/plots/correlation/heatmap/defaults.js +23 -8
- package/dist/plots/correlation/heatmap/markers.d.ts +2 -10
- package/dist/plots/correlation/heatmap/markers.js +11 -37
- package/dist/plots/correlation/scatter/defaults.d.ts +2 -2
- package/dist/plots/correlation/scatter/defaults.js +9 -3
- package/dist/plots/correlation/scatter/markers.d.ts +3 -3
- package/dist/plots/correlation/scatter/markers.js +6 -5
- package/dist/plots/distribution/box/BoxSummaries.d.ts +17 -8
- package/dist/plots/distribution/box/BoxSummaries.js +90 -40
- package/dist/plots/distribution/box/defaults.d.ts +2 -2
- package/dist/plots/distribution/box/defaults.js +9 -6
- package/dist/plots/distribution/box/markers.d.ts +2 -2
- package/dist/plots/distribution/box/markers.js +3 -3
- package/dist/plots/distribution/density/defaults.d.ts +2 -2
- package/dist/plots/distribution/density/defaults.js +17 -1
- package/dist/plots/distribution/density/markers.d.ts +2 -2
- package/dist/plots/distribution/density/markers.js +4 -4
- package/dist/plots/distribution/histogram/defaults.d.ts +2 -2
- package/dist/plots/distribution/histogram/defaults.js +22 -27
- package/dist/plots/distribution/histogram/markers.d.ts +3 -12
- package/dist/plots/distribution/histogram/markers.js +21 -62
- package/dist/plots/distribution/pyramid/defaults.d.ts +2 -2
- package/dist/plots/distribution/pyramid/defaults.js +7 -4
- package/dist/plots/distribution/pyramid/markers.d.ts +2 -12
- package/dist/plots/distribution/pyramid/markers.js +16 -66
- package/dist/plots/distribution/violin/defaults.d.ts +2 -2
- package/dist/plots/distribution/violin/defaults.js +9 -7
- package/dist/plots/distribution/violin/markers.d.ts +2 -2
- package/dist/plots/distribution/violin/markers.js +4 -4
- package/dist/plots/evolution/area/defaults.d.ts +2 -2
- package/dist/plots/evolution/area/defaults.js +1 -1
- package/dist/plots/evolution/area/markers.d.ts +2 -2
- package/dist/plots/evolution/area/markers.js +4 -4
- package/dist/plots/evolution/line/defaults.d.ts +2 -2
- package/dist/plots/evolution/line/defaults.js +1 -1
- package/dist/plots/evolution/line/markers.d.ts +2 -2
- package/dist/plots/evolution/line/markers.js +3 -3
- package/dist/plots/evolution/slope/defaults.d.ts +2 -2
- package/dist/plots/evolution/slope/defaults.js +8 -4
- package/dist/plots/evolution/slope/markers.d.ts +2 -2
- package/dist/plots/evolution/slope/markers.js +3 -3
- package/dist/plots/evolution/sparkline/defaults.d.ts +2 -2
- package/dist/plots/evolution/sparkline/defaults.js +5 -2
- package/dist/plots/evolution/sparkline/markers.d.ts +2 -2
- package/dist/plots/evolution/sparkline/markers.js +3 -3
- package/dist/plots/map/geo/Core.svelte +3 -3
- package/dist/plots/map/geo/defaults.d.ts +2 -2
- package/dist/plots/map/geo/phases/data.d.ts +1 -1
- package/dist/plots/map/geo/phases/data.js +1 -1
- package/dist/plots/map/geo/phases/hover.d.ts +2 -2
- package/dist/plots/map/geo/phases/hover.js +3 -3
- package/dist/plots/map/geo/phases/markers.d.ts +2 -2
- package/dist/plots/map/geo/phases/markers.js +5 -4
- package/dist/plots/map/geo/phases/scales.d.ts +2 -2
- package/dist/plots/map/geo/phases/scales.js +2 -2
- package/dist/plots/part-of-a-whole/treemap/Plot.svelte +47 -0
- package/dist/plots/part-of-a-whole/treemap/Plot.svelte.d.ts +26 -0
- package/dist/plots/part-of-a-whole/treemap/defaults.d.ts +10 -0
- package/dist/plots/part-of-a-whole/treemap/defaults.js +183 -0
- package/dist/plots/ranking/bar/defaults.d.ts +2 -2
- package/dist/plots/ranking/bar/defaults.js +15 -9
- package/dist/plots/ranking/bar/markers.d.ts +4 -14
- package/dist/plots/ranking/bar/markers.js +45 -86
- package/dist/plots/ranking/bullet/defaults.d.ts +2 -2
- package/dist/plots/ranking/bullet/defaults.js +4 -4
- package/dist/plots/ranking/bullet/markers.d.ts +3 -3
- package/dist/plots/ranking/bullet/markers.js +11 -12
- package/dist/plots/ranking/cleveland/ClevelandMarkers.d.ts +3 -3
- package/dist/plots/ranking/cleveland/ClevelandMarkers.js +28 -23
- package/dist/plots/ranking/cleveland/defaults.d.ts +2 -2
- package/dist/plots/ranking/cleveland/defaults.js +7 -4
- package/dist/plots/ranking/dumbbell/defaults.d.ts +2 -2
- package/dist/plots/ranking/forest/defaults.d.ts +2 -2
- package/dist/plots/ranking/lollipop/defaults.d.ts +2 -2
- package/dist/types/configuration/styling.d.ts +31 -2
- package/dist/types/layout/context.d.ts +21 -0
- package/dist/types/layout/context.js +1 -0
- package/dist/types/layout/hover.d.ts +3 -0
- package/dist/types/layout/legend.d.ts +8 -1
- package/dist/types/layout/mapping.d.ts +12 -3
- package/dist/types/layout/scales.d.ts +6 -0
- package/dist/types/layout/styles/values.d.ts +8 -2
- package/dist/types/layout/tooltip.d.ts +11 -4
- package/dist/types/markers/axis/arrow.d.ts +2 -8
- package/dist/types/markers/axis/axisBasedMarkersConfig.d.ts +2 -1
- package/dist/types/markers/axis/band.d.ts +24 -0
- package/dist/types/markers/axis/band.js +1 -0
- package/dist/types/markers/axis/bar.d.ts +6 -10
- package/dist/types/markers/axis/cell.d.ts +5 -5
- package/dist/types/markers/axis/delaunayLink.d.ts +2 -2
- package/dist/types/markers/axis/hover.d.ts +60 -15
- package/dist/types/markers/axis/rect.d.ts +4 -8
- package/dist/types/markers/axis/rule.d.ts +2 -16
- package/dist/types/markers/axis/tick.d.ts +2 -2
- package/dist/types/markers/axis/voronoi.d.ts +2 -2
- package/dist/types/markers/axis/waffle.d.ts +4 -3
- package/dist/types/markers/basic/dot.d.ts +2 -2
- package/dist/types/markers/basic/text.d.ts +56 -14
- package/dist/types/markers/channels/commonMarkerConfig.d.ts +8 -1
- package/dist/types/markers/channels/commonMarkerConfigWithArea.d.ts +1 -2
- package/dist/types/markers/channels/commonMarkerConfigWithLabel.d.ts +10 -0
- package/dist/types/markers/channels/commonMarkerConfigWithLabel.js +1 -0
- package/dist/types/markers/channels/commonMarkerEvents.d.ts +17 -0
- package/dist/types/markers/channels/commonMarkerEvents.js +13 -0
- package/dist/types/markers/common.d.ts +1 -0
- package/dist/types/markers/common.js +1 -0
- package/dist/types/markers/geo/contour.d.ts +2 -1
- package/dist/types/markers/geo/graticule.d.ts +2 -1
- package/dist/types/markers/geo/link.d.ts +2 -1
- package/dist/types/markers/geo/sphere.d.ts +2 -1
- package/dist/types/markers/geo/vector.d.ts +2 -1
- package/dist/types/phases/context/phase.d.ts +7 -0
- package/dist/types/phases/context/phase.js +1 -0
- package/dist/types/phases/data/phase.d.ts +7 -2
- package/dist/types/phases/hover/phase.d.ts +10 -5
- package/dist/types/phases/index.d.ts +2 -1
- package/dist/types/phases/kind.d.ts +11 -8
- package/dist/types/phases/mapping/phase.d.ts +2 -2
- package/dist/types/phases/markers/phase.d.ts +3 -3
- package/dist/types/phases/scales/phase.d.ts +4 -4
- package/dist/types/phases/stage.d.ts +3 -3
- package/dist/types/phases/state.d.ts +8 -1
- package/dist/types/phases/statistics/phase.d.ts +2 -1
- package/dist/types/phases/styles/phase.d.ts +3 -2
- package/dist/types/phases/transforms/phase.d.ts +13 -2
- package/dist/types/plots/constants.d.ts +17 -19
- package/dist/types/plots/correlation/heatmap/props.d.ts +3 -3
- package/dist/types/plots/correlation/heatmap/state.d.ts +2 -2
- package/dist/types/plots/correlation/scatter/props.d.ts +3 -3
- package/dist/types/plots/correlation/scatter/state.d.ts +2 -2
- package/dist/types/plots/data/common.d.ts +19 -6
- package/dist/types/plots/data/geo.d.ts +1 -1
- package/dist/types/plots/distribution/box/props.d.ts +25 -3
- package/dist/types/plots/distribution/box/state.d.ts +6 -17
- package/dist/types/plots/distribution/density/props.d.ts +3 -11
- package/dist/types/plots/distribution/density/state.d.ts +2 -2
- package/dist/types/plots/distribution/histogram/props.d.ts +16 -11
- package/dist/types/plots/distribution/histogram/state.d.ts +2 -2
- package/dist/types/plots/distribution/pyramid/props.d.ts +3 -3
- package/dist/types/plots/distribution/pyramid/state.d.ts +2 -2
- package/dist/types/plots/distribution/violin/props.d.ts +4 -3
- package/dist/types/plots/distribution/violin/state.d.ts +2 -2
- package/dist/types/plots/evolution/area/props.d.ts +3 -3
- package/dist/types/plots/evolution/area/state.d.ts +2 -2
- package/dist/types/plots/evolution/line/props.d.ts +3 -3
- package/dist/types/plots/evolution/line/state.d.ts +2 -2
- package/dist/types/plots/evolution/slope/state.d.ts +2 -2
- package/dist/types/plots/evolution/sparkline/state.d.ts +2 -2
- package/dist/types/plots/map/geo/state.d.ts +1 -1
- package/dist/types/plots/part-of-a-whole/treemap/props.d.ts +29 -0
- package/dist/types/plots/part-of-a-whole/treemap/props.js +1 -0
- package/dist/types/plots/part-of-a-whole/treemap/state.d.ts +6 -0
- package/dist/types/plots/part-of-a-whole/treemap/state.js +1 -0
- package/dist/types/plots/props.d.ts +10 -4
- package/dist/types/plots/ranking/bar/props.d.ts +3 -3
- package/dist/types/plots/ranking/bar/state.d.ts +2 -2
- package/dist/types/plots/ranking/bullet/state.d.ts +2 -2
- package/dist/types/plots/ranking/cleveland/props.d.ts +3 -3
- package/dist/types/plots/ranking/cleveland/state.d.ts +2 -2
- package/dist/types/plots/ranking/dumbbell/props.d.ts +3 -3
- package/dist/types/plots/ranking/dumbbell/state.d.ts +2 -2
- package/dist/types/plots/ranking/forest/props.d.ts +3 -3
- package/dist/types/plots/ranking/forest/state.d.ts +2 -2
- package/dist/types/plots/ranking/lollipop/props.d.ts +3 -3
- package/dist/types/plots/ranking/lollipop/state.d.ts +2 -2
- package/dist/types/plots/styling.d.ts +24 -0
- package/dist/types/transforms/index.d.ts +3 -1
- package/dist/types/transforms/treemap.d.ts +32 -0
- package/dist/types/transforms/treemap.js +5 -0
- package/dist/utils/color/ColorTones.d.ts +19 -0
- package/dist/utils/color/ColorTones.js +64 -0
- package/dist/utils/color/CssColor.d.ts +14 -5
- package/dist/utils/color/CssColor.js +69 -27
- package/dist/utils/color/index.d.ts +4 -0
- package/dist/utils/color/index.js +4 -0
- package/dist/utils/grouping.d.ts +18 -1
- package/dist/utils/grouping.js +31 -0
- package/docs/concepts.md +3 -2
- package/docs/legend.md +8 -5
- package/docs/markers.md +110 -28
- package/docs/plots/evolution/line.md +1 -1
- package/docs/plots/evolution/slope.md +1 -1
- package/docs/plots/part-of-a-whole/treemap.md +103 -0
- package/docs/plots/ranking/bar.md +2 -1
- package/docs/plots/ranking/cleveland.md +1 -1
- package/docs/plots.md +21 -20
- package/docs/scales.md +3 -0
- package/docs/state.md +34 -1
- package/docs/styles-and-segments.md +20 -19
- package/docs/theming.md +6 -12
- package/docs/tooltip-and-hover.md +81 -3
- package/docs/transforms-and-statistics.md +17 -16
- package/examples/LineDates.svelte +1 -1
- package/examples/ResponsiveLine.svelte +3 -1
- package/examples/Treemap.svelte +13 -0
- package/examples/TreemapHover.svelte +25 -0
- package/examples/ValueLabels.svelte +1 -1
- package/examples/datasets/economic-activity.ts +40 -0
- package/examples/datasets/index.ts +1 -0
- package/llms-full.txt +11 -9
- package/llms.txt +4 -1
- package/package.json +3 -1
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { TREEMAP_ROW } from '../../../phases/transforms/methods/TreemapTransform';
|
|
2
|
+
import { HoverDefaults } from '../../../layout/hover/HoverDefaults';
|
|
3
|
+
import { RowContext } from '../../../phases/context/RowContext';
|
|
4
|
+
import { NumberFormat } from '../../../utils/NumberFormat';
|
|
5
|
+
import { Grouping } from '../../../utils/grouping';
|
|
6
|
+
import { getConfiguration } from '../../../configuration/config.svelte';
|
|
7
|
+
/** A share is written with one decimal unless the author or the configuration's `numbers.decimals` says otherwise. */
|
|
8
|
+
const SHARE_DECIMALS = 1;
|
|
9
|
+
/** The width over the height of the box the tiles are shaped for, before the plot's own is known. */
|
|
10
|
+
const DEFAULT_ASPECT = 1.6;
|
|
11
|
+
/**
|
|
12
|
+
* What a treemap writes for a tile's share: `suffix` is a percent sign unless the author asks for something
|
|
13
|
+
* else, since a tile is a part of a whole.
|
|
14
|
+
*/
|
|
15
|
+
function shareDisplay(values) {
|
|
16
|
+
return { ...NumberFormat.displayOf(values), suffix: values?.suffix ?? '%' };
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* What a treemap knows of a tile, as the transform left it on its row: its name, path, summed value and share
|
|
20
|
+
* (from `0` to `100`). The tiles sit in a unit square, so they have no `x` or `y` to read.
|
|
21
|
+
*/
|
|
22
|
+
function tileOf(row) {
|
|
23
|
+
return {
|
|
24
|
+
name: String(row[TREEMAP_ROW.name] ?? ''),
|
|
25
|
+
path: row[TREEMAP_ROW.path] ?? [],
|
|
26
|
+
value: Number(row[TREEMAP_ROW.value]),
|
|
27
|
+
share: Number(row[TREEMAP_ROW.share]) * 100,
|
|
28
|
+
x: undefined,
|
|
29
|
+
y: undefined
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** A tile's context, as the "context" phase left it on its row. */
|
|
33
|
+
const contextOf = (row) => RowContext.of(row);
|
|
34
|
+
/**
|
|
35
|
+
* The plot's tiles: a `treemap` of its `value`, nested by the levels of its `grouping` (its `color` when unset,
|
|
36
|
+
* as every plot groups by it then) and shaped for its box,
|
|
37
|
+
* with the options of the author's own `treemap` transforms over it (`tile`...), and their other transforms
|
|
38
|
+
* after it in order.
|
|
39
|
+
*/
|
|
40
|
+
function buildTransforms(state) {
|
|
41
|
+
const own = state.transforms ?? [];
|
|
42
|
+
const base = {
|
|
43
|
+
type: 'treemap',
|
|
44
|
+
value: state.value,
|
|
45
|
+
levels: Grouping.levels(state.grouping ?? state.color, state.data),
|
|
46
|
+
aspect: state.width && state.height ? state.width / state.height : DEFAULT_ASPECT
|
|
47
|
+
};
|
|
48
|
+
const treemap = own
|
|
49
|
+
.filter((t) => t.type === 'treemap')
|
|
50
|
+
.reduce((merged, t) => ({ ...merged, ...t }), base);
|
|
51
|
+
return [treemap, ...own.filter((t) => t.type !== 'treemap')];
|
|
52
|
+
}
|
|
53
|
+
/** The edges of the tiles, as the processed rows carry them. */
|
|
54
|
+
function edgesOf(state) {
|
|
55
|
+
const { x1, x2, y1, y2 } = state.processedData.channels;
|
|
56
|
+
return { x1, x2, y1, y2 };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The keys of the segments a tile has below its series: its path down to each deeper level
|
|
60
|
+
* (`'Tertiary.Finance'`), outermost first, so the more specific wins. The series' own are in its style already.
|
|
61
|
+
*/
|
|
62
|
+
function pathKeysOf(state, row) {
|
|
63
|
+
const { path } = contextOf(row);
|
|
64
|
+
return path
|
|
65
|
+
.slice(1)
|
|
66
|
+
.map((_, i) => path.slice(0, i + 2).join(Grouping.SEPARATOR))
|
|
67
|
+
.filter((key) => state.segments?.[key]);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The tiles as `rect` markers, each with a `label`: the name of the tile over its share, fitted to it. A series'
|
|
71
|
+
* tiles take its style - its color, under `styles.area` and the segments keyed by it (and `default`) - and the
|
|
72
|
+
* segments keyed by a tile's path restyle it, so the tiles split into one marker per style. The legend's dimming
|
|
73
|
+
* goes over all of them, and the hover marker's `active` and `others` over the tiles it reaches.
|
|
74
|
+
*/
|
|
75
|
+
function buildMarks(state, env) {
|
|
76
|
+
if (!state.processedData?.data.length)
|
|
77
|
+
return [];
|
|
78
|
+
const look = env.cfg.treemap;
|
|
79
|
+
const edges = edgesOf(state);
|
|
80
|
+
const labels = state.valueLabels?.show ? state.valueLabels : undefined;
|
|
81
|
+
const number = NumberFormat.of(shareDisplay(state.values), env.cfg, SHARE_DECIMALS);
|
|
82
|
+
const shareText = (row) => {
|
|
83
|
+
const context = contextOf(row);
|
|
84
|
+
return state.values?.format
|
|
85
|
+
? state.values.format(context.share, number, context)
|
|
86
|
+
: number(context.share);
|
|
87
|
+
};
|
|
88
|
+
/** The label of a series' tiles: the name over the share, fitted - or the share alone where the name does not fit. */
|
|
89
|
+
const labelOf = (labels) => {
|
|
90
|
+
const shape = labels.shapeLabel(env, (row) => `${contextOf(row).name}
|
|
91
|
+
${shareText(row)}`, { fontSize: look.fontSize });
|
|
92
|
+
return {
|
|
93
|
+
...shape,
|
|
94
|
+
styles: { lineHeight: look.lineHeight, ...shape.styles },
|
|
95
|
+
fit: { padding: look.padding, fallback: (row) => shareText(row) }
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
return (state.processedGroups ?? []).flatMap((group) => {
|
|
99
|
+
const series = group.series.name;
|
|
100
|
+
const dimmed = env.interaction?.fillFor(series);
|
|
101
|
+
const seriesColor = state.colorForSeries(series);
|
|
102
|
+
/** A tile's color: its series', or a tone of it by its path while `scales.color.depth` asks for one. */
|
|
103
|
+
const colorOf = (row) => state.colorForPath ? state.colorForPath(contextOf(row).path) : seriesColor;
|
|
104
|
+
return group.visualSegments.flatMap((segment) => {
|
|
105
|
+
const styled = new Map();
|
|
106
|
+
for (const row of segment.data) {
|
|
107
|
+
const keys = pathKeysOf(state, row);
|
|
108
|
+
const color = colorOf(row);
|
|
109
|
+
const id = JSON.stringify([keys, color]);
|
|
110
|
+
const bucket = styled.get(id) ?? styled.set(id, { keys, color, rows: [] }).get(id);
|
|
111
|
+
bucket.rows.push(row);
|
|
112
|
+
}
|
|
113
|
+
return [...styled.values()].map(({ keys, color, rows }) => {
|
|
114
|
+
const style = Object.assign({}, segment.styles, ...keys.flatMap((key) => state.segments[key].map((own) => own.styles ?? {})));
|
|
115
|
+
return {
|
|
116
|
+
type: 'rect',
|
|
117
|
+
data: rows,
|
|
118
|
+
...edges,
|
|
119
|
+
styles: {
|
|
120
|
+
...style,
|
|
121
|
+
fill: dimmed?.fill ?? style.fill ?? color,
|
|
122
|
+
fillOpacity: dimmed?.fillOpacity ?? style.fillOpacity,
|
|
123
|
+
stroke: style.stroke ?? look.stroke,
|
|
124
|
+
strokeWidth: style.strokeWidth ?? look.strokeWidth
|
|
125
|
+
},
|
|
126
|
+
label: labels && labelOf(labels),
|
|
127
|
+
hover: 'style'
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* TreemapPlot on the shared pipeline: its rows are the tiles of the `treemap` transform, laid out in a unit square
|
|
135
|
+
* the plot's two continuous axes span (hidden). Its series are the outermost level of its `grouping`, which colors
|
|
136
|
+
* the tiles and fills the legend. The tiles are `rect` markers, labelled by their own `label`. Hovering a tile is
|
|
137
|
+
* hovering its row: an invisible `rect` over each one drives the plot's hover, and its tooltip names the tile and
|
|
138
|
+
* gives its share.
|
|
139
|
+
*/
|
|
140
|
+
export function buildTreemapDefaults() {
|
|
141
|
+
return {
|
|
142
|
+
data: {
|
|
143
|
+
discreteAxes: 0,
|
|
144
|
+
nested: true,
|
|
145
|
+
segments: { color: (style) => style?.fill, connect: false, base: { flat: 'area' } }
|
|
146
|
+
},
|
|
147
|
+
// An omitted sector keeps its tiles' room, and every other tile its share; a rescaled one does not.
|
|
148
|
+
transforms: { transforms: buildTransforms, keepsOmitted: true },
|
|
149
|
+
// A tile's name, path, value and share, for its label, its tooltip and its hover.
|
|
150
|
+
context: () => tileOf,
|
|
151
|
+
scales: {
|
|
152
|
+
defaults: () => ({
|
|
153
|
+
x: { axisDisplay: 'none', grid: false },
|
|
154
|
+
y: { axisDisplay: 'none', grid: false, reverse: true }
|
|
155
|
+
}),
|
|
156
|
+
fit: () => ({ x: { domain: [0, 1], fitted: true }, y: { domain: [0, 1], fitted: true } })
|
|
157
|
+
},
|
|
158
|
+
styles: { values: { show: true } },
|
|
159
|
+
markers: { marks: buildMarks },
|
|
160
|
+
hover: {
|
|
161
|
+
strategy: 'punctual',
|
|
162
|
+
// A tile is hovered through its own hit area, not through whichever point the pointer is nearest.
|
|
163
|
+
pointMatching: 'contains',
|
|
164
|
+
// A tile has no axes for a crosshair, and writes its own label: its hover only restyles the tiles.
|
|
165
|
+
marker: (_state, env) => HoverDefaults.shapes(env.cfg),
|
|
166
|
+
hitAreas: (state, hover) => hover.points.map(({ row }) => ({
|
|
167
|
+
type: 'rect',
|
|
168
|
+
data: [row],
|
|
169
|
+
...edgesOf(state),
|
|
170
|
+
styles: { fill: 'transparent' },
|
|
171
|
+
hover: hover.active && 'trigger'
|
|
172
|
+
})),
|
|
173
|
+
format: (state) => {
|
|
174
|
+
const number = NumberFormat.of(shareDisplay(state.values), getConfiguration(), SHARE_DECIMALS);
|
|
175
|
+
return ((row) => {
|
|
176
|
+
const tile = contextOf(row);
|
|
177
|
+
return `${tile.name}: ${number(tile.share)}`;
|
|
178
|
+
});
|
|
179
|
+
},
|
|
180
|
+
label: () => ((row) => contextOf(row).name)
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AxisPlotDefaults } from '../../../types/phases';
|
|
2
2
|
import type { AxisScale } from '../../../types/layout/scales';
|
|
3
|
-
import type { BarPlotState,
|
|
3
|
+
import type { BarPlotState, BarPlotEnv } from '../../../types/plots/ranking/bar/state';
|
|
4
4
|
/**
|
|
5
5
|
* The domain a bar plot fits to its own bars on its value axis: from zero to
|
|
6
6
|
* the tallest span (and the `extra` values a plot draws beyond its bars), niced to as many ticks as fit the plot. Left out when the
|
|
@@ -13,4 +13,4 @@ export declare function fitValueDomain<TData extends Record<string, unknown>>(st
|
|
|
13
13
|
* it off the values - its own scales (padding, bar layout) and markers.
|
|
14
14
|
* Its plot defaults to: hover along the category axis, timeline tweens matched per category and group.
|
|
15
15
|
*/
|
|
16
|
-
export declare function buildBarDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<BarPlotState<TData>,
|
|
16
|
+
export declare function buildBarDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<BarPlotState<TData>, BarPlotEnv>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { ProcessedPositions } from '../../../phases/transforms/ProcessedPositions';
|
|
2
|
-
import { buildMarks,
|
|
2
|
+
import { buildMarks, buildHitAreas, buildBarGeometry } from './markers';
|
|
3
3
|
import { NiceTicks } from '../../../layout/scales/NiceTicks';
|
|
4
4
|
import { SERIES_KEY } from '../../../phases/transforms/TransformsPhase';
|
|
5
5
|
import { PlotAxes } from '../../../phases/data/PlotAxes';
|
|
6
|
+
import { HoverDefaults } from '../../../layout/hover/HoverDefaults';
|
|
6
7
|
/**
|
|
7
8
|
* The domain a bar plot fits to its own bars on its value axis: from zero to
|
|
8
9
|
* the tallest span (and the `extra` values a plot draws beyond its bars), niced to as many ticks as fit the plot. Left out when the
|
|
@@ -53,8 +54,8 @@ export function buildBarDefaults() {
|
|
|
53
54
|
},
|
|
54
55
|
scales: {
|
|
55
56
|
zero: ['value'],
|
|
56
|
-
defaults: (_state,
|
|
57
|
-
category: { paddingInner:
|
|
57
|
+
defaults: (_state, env) => ({
|
|
58
|
+
category: { paddingInner: env.cfg.bar.paddingInner, paddingOuter: env.cfg.bar.paddingOuter }
|
|
58
59
|
}),
|
|
59
60
|
fit: (state) => fitValueDomain(state)
|
|
60
61
|
},
|
|
@@ -85,23 +86,28 @@ export function buildBarDefaults() {
|
|
|
85
86
|
return labels
|
|
86
87
|
.extremes(state.resolvedSeries ?? [], PlotAxes.valueOf(state), 'both')
|
|
87
88
|
.map(({ series, value }) => value < 0
|
|
88
|
-
? labels.edgeBox(labels.format(value, series.name), { x: 'before', y: 'middle' }, { x: -offset }, ['left'])
|
|
89
|
-
: labels.edgeBox(labels.format(value, series.name), { x: 'after', y: 'middle' }, { x: offset }, ['right']));
|
|
89
|
+
? labels.edgeBox(labels.format(value, undefined, series.name), { x: 'before', y: 'middle' }, { x: -offset }, ['left'])
|
|
90
|
+
: labels.edgeBox(labels.format(value, undefined, series.name), { x: 'after', y: 'middle' }, { x: offset }, ['right']));
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
},
|
|
93
|
-
markers: { marks: buildMarks
|
|
94
|
+
markers: { marks: buildMarks },
|
|
94
95
|
hover: {
|
|
95
96
|
// Each bar matched by its own band (see `buildHitAreas`); a grouped bar's dot is
|
|
96
97
|
// drawn in its series' slice of the band.
|
|
97
98
|
pointMatching: 'contains',
|
|
98
|
-
decorate: (state,
|
|
99
|
-
const { sliceOf } = buildBarGeometry(state,
|
|
99
|
+
decorate: (state, env) => {
|
|
100
|
+
const { sliceOf } = buildBarGeometry(state, env.cfg.bar.seriesPadding);
|
|
100
101
|
return (row) => ({
|
|
101
102
|
areaInset: sliceOf(String(row[SERIES_KEY]))
|
|
102
103
|
});
|
|
103
104
|
},
|
|
104
|
-
hitAreas: buildHitAreas
|
|
105
|
+
hitAreas: buildHitAreas,
|
|
106
|
+
// The hovered category's band behind its bars - its series' slice of it, when that bar alone is hovered.
|
|
107
|
+
marker: (state) => ({
|
|
108
|
+
back: [HoverDefaults.band(state.axes?.isHorizontal ? 'y' : 'x')],
|
|
109
|
+
front: []
|
|
110
|
+
})
|
|
105
111
|
},
|
|
106
112
|
timeline: { interpolation: { value: 'value', key: ['category', 'grouping'] } }
|
|
107
113
|
};
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type { BarMarkerConfig
|
|
2
|
-
import type { BarPlotState,
|
|
1
|
+
import type { BarMarkerConfig } from '../../../types/markers/common';
|
|
2
|
+
import type { BarPlotState, BarPlotEnv } from '../../../types/plots/ranking/bar/state';
|
|
3
3
|
import type { ProcessedRow } from '../../../types/phases';
|
|
4
4
|
import type { AxisValue } from '../../../types/layout/mapping';
|
|
5
5
|
import type { BandSlice } from '../../../types/layout/hover';
|
|
6
6
|
import type { HoverConfig } from '../../../types/plots/props';
|
|
7
|
-
import { ValueLabels } from '../../../layout/styles/ValueLabels';
|
|
8
7
|
import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
|
|
9
8
|
/**
|
|
10
9
|
* Where a bar plot's bars sit, read off the state its phases leave: each
|
|
@@ -26,20 +25,11 @@ export declare function buildBarGeometry<TData extends Record<string, unknown>>(
|
|
|
26
25
|
* slice of it. The pointer needn't be over the visible bar. They drive the
|
|
27
26
|
* plot's hover when it is active.
|
|
28
27
|
*/
|
|
29
|
-
export declare function buildHitAreas<TData extends Record<string, unknown>>(state: BarPlotState<TData>, hover: HoverConfig<any>,
|
|
30
|
-
/**
|
|
31
|
-
* BarPlot's `styles.values` as one `'text'` marker per series, over the same
|
|
32
|
-
* processed rows its bars draw (gap rows never get a label). `anchor` places
|
|
33
|
-
* each label along its bar: past its top (`'outside'`), just inside it
|
|
34
|
-
* (`'start'`), at its base (`'end'`), or centred (`'middle'`); a stacked bar's
|
|
35
|
-
* label always sits at the middle of its own span. A grouped bar's label
|
|
36
|
-
* shifts with its series' slice of the band.
|
|
37
|
-
*/
|
|
38
|
-
export declare function buildValueLabels<TData extends Record<string, unknown>>(labels: ValueLabels, state: BarPlotState<TData>, ctx: BarPlotCtx): TextMarkerConfig[];
|
|
28
|
+
export declare function buildHitAreas<TData extends Record<string, unknown>>(state: BarPlotState<TData>, hover: HoverConfig<any>, env: BarPlotEnv): BarMarkerConfig[];
|
|
39
29
|
/**
|
|
40
30
|
* BarPlot's "markers" phase: visible `role: 'segment'` bars - real data plus
|
|
41
31
|
* gap fills - drawing the rows the "transforms" phase left, prepended onto
|
|
42
32
|
* whatever the "statistics" phase already put in `state.markers`, and its
|
|
43
33
|
* value labels on top of everything.
|
|
44
34
|
*/
|
|
45
|
-
export declare function buildMarks<TData extends Record<string, unknown>>(state: BarPlotState<TData>,
|
|
35
|
+
export declare function buildMarks<TData extends Record<string, unknown>>(state: BarPlotState<TData>, env: BarPlotEnv): AxisBasedMarkersConfig[];
|
|
@@ -46,8 +46,8 @@ export function buildBarGeometry(state, seriesPadding) {
|
|
|
46
46
|
*/
|
|
47
47
|
export function buildHitAreas(state,
|
|
48
48
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
-
hover,
|
|
50
|
-
const { categoryOf, sliceOf } = buildBarGeometry(state,
|
|
49
|
+
hover, env) {
|
|
50
|
+
const { categoryOf, sliceOf } = buildBarGeometry(state, env.cfg.bar.seriesPadding);
|
|
51
51
|
const isHorizontal = state.axes?.isHorizontal ?? false;
|
|
52
52
|
return hover.points.map(({ row, series }) => {
|
|
53
53
|
const slice = series === undefined ? undefined : sliceOf(series);
|
|
@@ -57,7 +57,7 @@ hover, ctx) {
|
|
|
57
57
|
isHorizontal,
|
|
58
58
|
data: [row],
|
|
59
59
|
category: categoryOf(row),
|
|
60
|
-
hover: hover.active,
|
|
60
|
+
hover: hover.active && 'trigger',
|
|
61
61
|
...(slice
|
|
62
62
|
? isHorizontal
|
|
63
63
|
? { insetTop: slice.start, insetBottom: slice.end }
|
|
@@ -66,88 +66,17 @@ hover, ctx) {
|
|
|
66
66
|
};
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
|
-
/**
|
|
70
|
-
* BarPlot's `styles.values` as one `'text'` marker per series, over the same
|
|
71
|
-
* processed rows its bars draw (gap rows never get a label). `anchor` places
|
|
72
|
-
* each label along its bar: past its top (`'outside'`), just inside it
|
|
73
|
-
* (`'start'`), at its base (`'end'`), or centred (`'middle'`); a stacked bar's
|
|
74
|
-
* label always sits at the middle of its own span. A grouped bar's label
|
|
75
|
-
* shifts with its series' slice of the band.
|
|
76
|
-
*/
|
|
77
|
-
export function buildValueLabels(labels, state, ctx) {
|
|
78
|
-
const { processedData } = state;
|
|
79
|
-
const isHorizontal = state.axes?.isHorizontal ?? false;
|
|
80
|
-
if (!processedData)
|
|
81
|
-
return [];
|
|
82
|
-
const geometry = buildBarGeometry(state, ctx.cfg.bar.seriesPadding);
|
|
83
|
-
const { font, anchor, format } = labels;
|
|
84
|
-
const valueOf = ValueLabels.valueOf(state);
|
|
85
|
-
const spanOf = (d) => geometry.spanOf(d) ?? [0, 0];
|
|
86
|
-
const lowOf = (d) => Math.min(...spanOf(d));
|
|
87
|
-
const highOf = (d) => Math.max(...spanOf(d));
|
|
88
|
-
const below = (d) => Number(valueOf(d)) < 0;
|
|
89
|
-
const place = (negative) => {
|
|
90
|
-
const mid = (d) => (lowOf(d) + highOf(d)) / 2;
|
|
91
|
-
if (geometry.stacked)
|
|
92
|
-
return { at: mid, side: 0 };
|
|
93
|
-
if (anchor === 'middle')
|
|
94
|
-
return { at: mid, side: 0 };
|
|
95
|
-
if (anchor === 'outside')
|
|
96
|
-
return isHorizontal
|
|
97
|
-
? { at: negative ? lowOf : highOf, side: negative ? -1 : 1 }
|
|
98
|
-
: { at: negative ? lowOf : highOf, side: negative ? 1 : -1 };
|
|
99
|
-
if (anchor === 'start')
|
|
100
|
-
return isHorizontal ? { at: lowOf, side: 1 } : { at: highOf, side: 1 };
|
|
101
|
-
return isHorizontal ? { at: highOf, side: -1 } : { at: lowOf, side: -1 };
|
|
102
|
-
};
|
|
103
|
-
const gap = anchor === 'outside' ? 6 : 4;
|
|
104
|
-
const labelled = processedData.data.filter((d) => d[GAP_VALUE] === undefined && geometry.spanOf(d) !== undefined);
|
|
105
|
-
return (state.resolvedSeries ?? []).flatMap((s) => {
|
|
106
|
-
const own = labelled.filter((d) => d[SERIES_ID_KEY] === seriesIdOf(s));
|
|
107
|
-
// Only a label beyond the tip turns with the sign of its value.
|
|
108
|
-
const turns = !geometry.stacked && anchor === 'outside';
|
|
109
|
-
return (turns ? [false, true] : [false]).flatMap((negative) => {
|
|
110
|
-
const data = turns ? own.filter((d) => below(d) === negative) : own;
|
|
111
|
-
if (data.length === 0)
|
|
112
|
-
return [];
|
|
113
|
-
const { at, side } = place(negative);
|
|
114
|
-
// A horizontal label reads away from its point to `side`; a vertical one sits on the far side of it.
|
|
115
|
-
const textAnchor = labels.textAnchor ??
|
|
116
|
-
(isHorizontal ? (side === 1 ? 'start' : side === -1 ? 'end' : 'middle') : 'middle');
|
|
117
|
-
const lineAnchor = font?.lineAnchor ??
|
|
118
|
-
(isHorizontal ? 'middle' : side === 1 ? 'top' : side === -1 ? 'bottom' : 'middle');
|
|
119
|
-
return [
|
|
120
|
-
{
|
|
121
|
-
type: 'text',
|
|
122
|
-
data,
|
|
123
|
-
...(isHorizontal ? { x: at, y: geometry.categoryOf } : { x: geometry.categoryOf, y: at }),
|
|
124
|
-
bandSlice: geometry.sliceOf(s.name),
|
|
125
|
-
text: (d) => format(Number(valueOf(d)), s.name, d),
|
|
126
|
-
styles: {
|
|
127
|
-
...font,
|
|
128
|
-
fill: font?.fill ?? ctx.cfg.axis.color,
|
|
129
|
-
fontSize: font?.fontSize ?? 12,
|
|
130
|
-
dx: font?.dx ?? (isHorizontal ? side * gap : 0),
|
|
131
|
-
dy: font?.dy ?? (isHorizontal ? 0 : side * gap),
|
|
132
|
-
textAnchor,
|
|
133
|
-
lineAnchor
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
];
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
69
|
/**
|
|
141
70
|
* BarPlot's "markers" phase: visible `role: 'segment'` bars - real data plus
|
|
142
71
|
* gap fills - drawing the rows the "transforms" phase left, prepended onto
|
|
143
72
|
* whatever the "statistics" phase already put in `state.markers`, and its
|
|
144
73
|
* value labels on top of everything.
|
|
145
74
|
*/
|
|
146
|
-
export function buildMarks(state,
|
|
75
|
+
export function buildMarks(state, env) {
|
|
147
76
|
const { gapFillMaps, colorForSeries, processedData } = state;
|
|
148
77
|
const isHorizontal = state.axes?.isHorizontal ?? false;
|
|
149
78
|
const [categoryAxis, valueAxis] = isHorizontal ? ['y', 'x'] : ['x', 'y'];
|
|
150
|
-
const geometry = buildBarGeometry(state,
|
|
79
|
+
const geometry = buildBarGeometry(state, env.cfg.bar.seriesPadding);
|
|
151
80
|
const { channels } = processedData;
|
|
152
81
|
const gapDefaults = getConfiguration().bar.gap;
|
|
153
82
|
// Forwarded raw when the transforms left a plain channel (a field key keeps
|
|
@@ -160,7 +89,25 @@ export function buildMarks(state, ctx) {
|
|
|
160
89
|
// which stays a field key whenever the plot has no transforms.
|
|
161
90
|
const valueOf = (channel) => (geometry.stacked || channel === undefined ? top : channel);
|
|
162
91
|
const gapValue = valueOf(channels[valueAxis]);
|
|
163
|
-
|
|
92
|
+
// What each bar writes in itself: its value, anchored along the bar by `anchor` - past its tip
|
|
93
|
+
// (`'outside'`), just inside it (`'start'`), at its base (`'end'`) or in the middle. A stacked bar's
|
|
94
|
+
// label is always in the middle of its segment.
|
|
95
|
+
const labels = state.valueLabels?.show ? state.valueLabels : undefined;
|
|
96
|
+
const valueRead = ValueLabels.valueOf(state);
|
|
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), {
|
|
105
|
+
fill: 'currentColor',
|
|
106
|
+
...labelAnchor(negative)
|
|
107
|
+
}),
|
|
108
|
+
bandSlice: slice
|
|
109
|
+
};
|
|
110
|
+
function makeMarker(data, slice, value, styles, label) {
|
|
164
111
|
if (data.length === 0)
|
|
165
112
|
return null;
|
|
166
113
|
const from = (geometry.stacked ? bottom : geometry.baseline);
|
|
@@ -170,18 +117,18 @@ export function buildMarks(state, ctx) {
|
|
|
170
117
|
isHorizontal,
|
|
171
118
|
data,
|
|
172
119
|
styles,
|
|
120
|
+
hover: 'style',
|
|
121
|
+
...(label ? { label } : {}),
|
|
173
122
|
...(isHorizontal
|
|
174
123
|
? { y: category, x1: from, x2: value }
|
|
175
124
|
: { x: category, y1: from, y2: value })
|
|
176
125
|
};
|
|
177
126
|
if (slice) {
|
|
178
127
|
if (isHorizontal) {
|
|
179
|
-
marker.
|
|
180
|
-
marker.insetBottom = slice.end;
|
|
128
|
+
marker.styles = { ...marker.styles, insetTop: slice.start, insetBottom: slice.end };
|
|
181
129
|
}
|
|
182
130
|
else {
|
|
183
|
-
marker.
|
|
184
|
-
marker.insetRight = slice.end;
|
|
131
|
+
marker.styles = { ...marker.styles, insetLeft: slice.start, insetRight: slice.end };
|
|
185
132
|
}
|
|
186
133
|
}
|
|
187
134
|
return marker;
|
|
@@ -192,10 +139,10 @@ export function buildMarks(state, ctx) {
|
|
|
192
139
|
const name = group.series.name;
|
|
193
140
|
const slice = geometry.sliceOf(name);
|
|
194
141
|
const defaultColor = colorForSeries(name);
|
|
195
|
-
const disabledFill =
|
|
142
|
+
const disabledFill = env.interaction?.fillFor(name);
|
|
196
143
|
for (const seg of group.visualSegments) {
|
|
197
144
|
const fill = disabledFill?.fill ?? seg.styles.fill ?? defaultColor;
|
|
198
|
-
const
|
|
145
|
+
const styles = {
|
|
199
146
|
// An outline asked for without a color is the fill's.
|
|
200
147
|
...(seg.styles.strokeWidth !== undefined ? { stroke: fill } : {}),
|
|
201
148
|
...seg.styles,
|
|
@@ -205,9 +152,21 @@ export function buildMarks(state, ctx) {
|
|
|
205
152
|
...(disabledFill?.fillOpacity !== undefined
|
|
206
153
|
? { strokeOpacity: disabledFill.fillOpacity }
|
|
207
154
|
: {})
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
155
|
+
};
|
|
156
|
+
const real = seg.data.filter(isReal);
|
|
157
|
+
const value = valueOf(group.series[valueAxis]);
|
|
158
|
+
// A label past the tip turns with the sign of its value, so the bars below zero are a marker of their own.
|
|
159
|
+
const parts = turns
|
|
160
|
+
? [false, true].map((negative) => ({
|
|
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
|
+
}
|
|
211
170
|
}
|
|
212
171
|
const id = seriesIdOf(group.series);
|
|
213
172
|
const fills = [...(gapFillMaps.get(id)?.values() ?? [])];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { AxisPlotDefaults } from '../../../types/phases';
|
|
2
|
-
import type { BulletPlotState,
|
|
2
|
+
import type { BulletPlotState, BulletPlotEnv } from '../../../types/plots/ranking/bullet/state';
|
|
3
3
|
/**
|
|
4
4
|
* BulletPlot on the shared pipeline: a BarPlot whose bars are drawn thinner
|
|
5
5
|
* over `ranges` - bands that grade the value - with a tick at each `target`.
|
|
6
6
|
* Its value axis fits them along with the bars.
|
|
7
7
|
*/
|
|
8
|
-
export declare function buildBulletDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<BulletPlotState<TData>,
|
|
8
|
+
export declare function buildBulletDefaults<TData extends Record<string, unknown>>(): AxisPlotDefaults<BulletPlotState<TData>, BulletPlotEnv>;
|
|
@@ -11,13 +11,13 @@ export function buildBulletDefaults() {
|
|
|
11
11
|
...bar,
|
|
12
12
|
scales: {
|
|
13
13
|
...bar.scales,
|
|
14
|
-
defaults: (_state,
|
|
14
|
+
defaults: (_state, env) => ({
|
|
15
15
|
category: {
|
|
16
|
-
paddingInner:
|
|
17
|
-
paddingOuter:
|
|
16
|
+
paddingInner: env.cfg.bullet.paddingInner,
|
|
17
|
+
paddingOuter: env.cfg.bullet.paddingOuter
|
|
18
18
|
}
|
|
19
19
|
}),
|
|
20
|
-
fit: (state,
|
|
20
|
+
fit: (state, env) => fitValueDomain(state, buildReach(state, env))
|
|
21
21
|
},
|
|
22
22
|
markers: { ...bar.markers, marks: buildMarks }
|
|
23
23
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { AxisBasedMarkersConfig } from '../../../types/markers/common';
|
|
2
|
-
import type { BulletPlotState,
|
|
2
|
+
import type { BulletPlotState, BulletPlotEnv } from '../../../types/plots/ranking/bullet/state';
|
|
3
3
|
/** Every value the axis has to reach beyond the bars. */
|
|
4
|
-
export declare function buildReach<TData extends Record<string, unknown>>(state: BulletPlotState<TData>,
|
|
4
|
+
export declare function buildReach<TData extends Record<string, unknown>>(state: BulletPlotState<TData>, env: BulletPlotEnv): number[];
|
|
5
5
|
/**
|
|
6
6
|
* BulletPlot's "markers" phase, back to front: a rule per range, the bars of a
|
|
7
7
|
* bar plot - thinner, so the ranges show around them - and a tick at each
|
|
8
8
|
* target.
|
|
9
9
|
*/
|
|
10
|
-
export declare function buildMarks<TData extends Record<string, unknown>>(state: BulletPlotState<TData>,
|
|
10
|
+
export declare function buildMarks<TData extends Record<string, unknown>>(state: BulletPlotState<TData>, env: BulletPlotEnv): AxisBasedMarkersConfig[];
|
|
@@ -2,8 +2,8 @@ import { buildMarks as buildBarMarks, buildBarGeometry } from '../bar/markers';
|
|
|
2
2
|
import { GAP_VALUE } from '../../../phases/transforms/TransformsPhase';
|
|
3
3
|
import { RowNumbers } from '../../utils/RowNumbers';
|
|
4
4
|
/** Each category's target and range bounds, read off the rows the bars draw. */
|
|
5
|
-
function buildMeasures(state,
|
|
6
|
-
const geometry = buildBarGeometry(state,
|
|
5
|
+
function buildMeasures(state, env) {
|
|
6
|
+
const geometry = buildBarGeometry(state, env.cfg.bar.seriesPadding);
|
|
7
7
|
return (state.processedData?.data ?? [])
|
|
8
8
|
.filter((row) => row[GAP_VALUE] === undefined && geometry.spanOf(row) !== undefined)
|
|
9
9
|
.map((row) => ({
|
|
@@ -16,8 +16,8 @@ function buildMeasures(state, ctx) {
|
|
|
16
16
|
}));
|
|
17
17
|
}
|
|
18
18
|
/** Every value the axis has to reach beyond the bars. */
|
|
19
|
-
export function buildReach(state,
|
|
20
|
-
return buildMeasures(state,
|
|
19
|
+
export function buildReach(state, env) {
|
|
20
|
+
return buildMeasures(state, env).flatMap(({ target, bounds }) => [
|
|
21
21
|
...(target === undefined ? [] : [target]),
|
|
22
22
|
...bounds
|
|
23
23
|
]);
|
|
@@ -27,19 +27,19 @@ export function buildReach(state, ctx) {
|
|
|
27
27
|
* bar plot - thinner, so the ranges show around them - and a tick at each
|
|
28
28
|
* target.
|
|
29
29
|
*/
|
|
30
|
-
export function buildMarks(state,
|
|
31
|
-
const configured =
|
|
30
|
+
export function buildMarks(state, env) {
|
|
31
|
+
const configured = env.cfg.bullet;
|
|
32
32
|
const own = (state.styles ?? {});
|
|
33
33
|
const rangeColors = own.ranges?.colors ?? configured.rangeColors;
|
|
34
34
|
const rangeWidth = own.ranges?.width ?? configured.rangeWidth;
|
|
35
35
|
const target = {
|
|
36
|
-
color: own.target?.color ?? configured.targetColor ??
|
|
36
|
+
color: own.target?.color ?? configured.targetColor ?? 'currentColor',
|
|
37
37
|
width: own.target?.width ?? configured.targetWidth,
|
|
38
38
|
length: own.target?.length ?? configured.targetLength
|
|
39
39
|
};
|
|
40
40
|
const isHorizontal = state.axes?.isHorizontal ?? false;
|
|
41
|
-
const baseline = buildBarGeometry(state,
|
|
42
|
-
const measures = buildMeasures(state,
|
|
41
|
+
const baseline = buildBarGeometry(state, env.cfg.bar.seriesPadding).baseline;
|
|
42
|
+
const measures = buildMeasures(state, env);
|
|
43
43
|
// Each bound is a band from the baseline, drawn widest first so the narrower ones lie over it.
|
|
44
44
|
const rows = measures.flatMap(({ category, bounds }) => bounds
|
|
45
45
|
.map((bound, rank) => ({
|
|
@@ -59,12 +59,11 @@ export function buildMarks(state, ctx) {
|
|
|
59
59
|
data: rows,
|
|
60
60
|
value: 'category',
|
|
61
61
|
...(isHorizontal ? { x1: 'from', x2: 'to' } : { y1: 'from', y2: 'to' }),
|
|
62
|
-
stroke: 'stroke',
|
|
63
|
-
styles: { strokeWidth: rangeWidth }
|
|
62
|
+
styles: { stroke: 'stroke', strokeWidth: rangeWidth }
|
|
64
63
|
}
|
|
65
64
|
];
|
|
66
65
|
// The bars narrowed to a thickness of their own, centered in their band.
|
|
67
|
-
const bars = buildBarMarks(state,
|
|
66
|
+
const bars = buildBarMarks(state, env).map((marker) => marker.type === 'bar' && marker.role === 'segment'
|
|
68
67
|
? {
|
|
69
68
|
...marker,
|
|
70
69
|
thickness: own.bar?.thickness ?? configured.barThickness
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ValueLabels } from '../../../layout/styles/ValueLabels';
|
|
2
|
-
import type { ClevelandPlotState,
|
|
2
|
+
import type { ClevelandPlotState, ClevelandPlotEnv, ClevelandOptions } from '../../../types/plots/ranking/cleveland/state';
|
|
3
3
|
import type { AxisBasedMarkersConfig, TextMarkerConfig } from '../../../types/markers/common';
|
|
4
4
|
import type { EdgeBox } from '../../../types/layout/box';
|
|
5
5
|
/**
|
|
@@ -16,14 +16,14 @@ export declare class ClevelandMarkers {
|
|
|
16
16
|
/** Forgets what was warned about; for tests. */
|
|
17
17
|
static resetWarnings(): void;
|
|
18
18
|
/** The plot's connectors, then its dots, which draw on top of them. */
|
|
19
|
-
marks: <TData extends Record<string, unknown>>(state: ClevelandPlotState<TData>,
|
|
19
|
+
marks: <TData extends Record<string, unknown>>(state: ClevelandPlotState<TData>, env: ClevelandPlotEnv) => AxisBasedMarkersConfig[];
|
|
20
20
|
/**
|
|
21
21
|
* The value labels of the dots `styles.values.of` picks, one `'text'` marker
|
|
22
22
|
* per place a dot can hold in its range - past the top end (above, or right
|
|
23
23
|
* of it), past the bottom end (below, or left of it) and, for the dots
|
|
24
24
|
* between, beside it - and per series, when they take their series' color.
|
|
25
25
|
*/
|
|
26
|
-
valueLabels: <TData extends Record<string, unknown>>(labels: ValueLabels, state: ClevelandPlotState<TData>,
|
|
26
|
+
valueLabels: <TData extends Record<string, unknown>>(labels: ValueLabels, state: ClevelandPlotState<TData>, env: ClevelandPlotEnv) => TextMarkerConfig[];
|
|
27
27
|
/**
|
|
28
28
|
* Room for the labels past the dots at the end of the value axis, so the
|
|
29
29
|
* farthest one fits inside the figure instead of crossing its edge. Only the
|