@fundar/data-chart-telling 0.0.25 → 0.0.27
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/dist/charts/BaseChart.svelte +18 -10
- package/dist/charts/BaseChart.svelte.d.ts +4 -2
- package/dist/charts/bar/Chart.svelte +2 -0
- package/dist/charts/heatmap/Chart.svelte +5 -3
- package/dist/charts/heatmap/Chart.svelte.d.ts +1 -2
- package/dist/charts/line/Chart.svelte +2 -0
- package/dist/charts/pyramid/Chart.svelte +5 -3
- package/dist/charts/pyramid/Chart.svelte.d.ts +1 -2
- package/dist/charts/scatter/Chart.svelte +95 -0
- package/dist/charts/scatter/Chart.svelte.d.ts +28 -0
- package/dist/configuration/config.svelte.js +28 -0
- package/dist/configuration/themes/dark.js +4 -0
- package/dist/configuration/themes/index.d.ts +112 -0
- package/dist/index.d.ts +26 -16
- package/dist/index.js +11 -3
- package/dist/layout/coordinates/CoordinatesLayout.svelte +93 -0
- package/dist/layout/{plot/RuleLayout.svelte.d.ts → coordinates/CoordinatesLayout.svelte.d.ts} +13 -16
- package/dist/layout/coordinates/margins.d.ts +16 -0
- package/dist/layout/coordinates/margins.js +11 -0
- package/dist/{plots/geo → layout/coordinates}/projections/argentina.d.ts +1 -1
- package/dist/layout/{geo → coordinates}/resolveProjection.d.ts +1 -1
- package/dist/layout/coordinates/resolveProjection.js +24 -0
- package/dist/{plots/geo → layout/coordinates}/zoom.svelte.d.ts +1 -1
- package/dist/layout/facet/FacetIndicator.svelte +31 -0
- package/dist/layout/facet/FacetIndicator.svelte.d.ts +8 -0
- package/dist/layout/facet/FacetLayout.svelte +249 -24
- package/dist/layout/facet/FacetLayout.svelte.d.ts +7 -1
- package/dist/layout/geometry/GeometryLayout.svelte +231 -0
- package/dist/layout/geometry/GeometryLayout.svelte.d.ts +62 -0
- package/dist/layout/legend/ContinuousSection.svelte +1 -1
- package/dist/layout/legend/ContinuousSection.svelte.d.ts +1 -1
- package/dist/layout/legend/DiscreteSection.svelte +1 -1
- package/dist/layout/legend/DiscreteSection.svelte.d.ts +1 -1
- package/dist/layout/legend/LegendLayout.svelte +1 -1
- package/dist/layout/legend/LegendLayout.svelte.d.ts +1 -1
- package/dist/layout/legend/interaction.svelte.d.ts +9 -1
- package/dist/layout/legend/interaction.svelte.js +9 -1
- package/dist/layout/plot/BasePlotLayout.svelte +142 -155
- package/dist/layout/plot/BasePlotLayout.svelte.d.ts +61 -51
- package/dist/layout/plot/margins.d.ts +0 -16
- package/dist/layout/plot/margins.js +0 -11
- package/dist/layout/{plot/AxisLayout.svelte → scales/ScalesLayout.svelte} +23 -16
- package/dist/layout/{plot/AxisLayout.svelte.d.ts → scales/ScalesLayout.svelte.d.ts} +4 -5
- package/dist/layout/scales/buildScale.d.ts +8 -0
- package/dist/layout/scales/buildScale.js +43 -0
- package/dist/layout/styles/StylesLayout.svelte +75 -0
- package/dist/layout/styles/StylesLayout.svelte.d.ts +15 -0
- package/dist/{plots/geo → layout/styles}/TileLayer.svelte +2 -2
- package/dist/{plots/geo → layout/styles}/TileLayer.svelte.d.ts +1 -1
- package/dist/layout/timeline/Timeline.svelte +1 -1
- package/dist/layout/timeline/Timeline.svelte.d.ts +1 -1
- package/dist/layout/timeline/TimelineLayout.svelte +1 -1
- package/dist/layout/timeline/TimelineLayout.svelte.d.ts +1 -1
- package/dist/layout/tooltip/Tooltip.svelte +16 -20
- package/dist/layout/tooltip/Tooltip.svelte.d.ts +5 -0
- package/dist/layout/tooltip/TooltipLayout.svelte +7 -1
- package/dist/layout/tooltip/TooltipLayout.svelte.d.ts +4 -0
- package/dist/layout/tooltip/controller.svelte.d.ts +4 -5
- package/dist/layout/tooltip/controller.svelte.js +42 -16
- package/dist/layout/tooltip/resolveTooltipPlacement.d.ts +24 -0
- package/dist/layout/tooltip/resolveTooltipPlacement.js +49 -0
- package/dist/layout/tooltip/utils.d.ts +27 -19
- package/dist/layout/tooltip/utils.js +16 -15
- package/dist/markers/BarMarker.svelte +92 -0
- package/dist/markers/BarMarker.svelte.d.ts +7 -0
- package/dist/markers/CellMarker.svelte +21 -0
- package/dist/markers/CellMarker.svelte.d.ts +7 -0
- package/dist/markers/ContourMarker.svelte +28 -0
- package/dist/markers/ContourMarker.svelte.d.ts +7 -0
- package/dist/markers/DeltaMarker.svelte +32 -39
- package/dist/markers/DeltaMarker.svelte.d.ts +4 -5
- package/dist/markers/DotMarker.svelte +35 -6
- package/dist/markers/DotMarker.svelte.d.ts +11 -2
- package/dist/markers/GeoFeaturesMarker.svelte +19 -0
- package/dist/markers/GeoFeaturesMarker.svelte.d.ts +7 -0
- package/dist/markers/GeoHoverMarker.svelte +25 -0
- package/dist/markers/GeoHoverMarker.svelte.d.ts +9 -0
- package/dist/markers/GraticuleMarker.svelte +18 -0
- package/dist/markers/GraticuleMarker.svelte.d.ts +7 -0
- package/dist/markers/HoverMarker.svelte +34 -35
- package/dist/markers/HoverMarker.svelte.d.ts +15 -9
- package/dist/markers/LineMarker.svelte +19 -0
- package/dist/markers/LineMarker.svelte.d.ts +7 -0
- package/dist/markers/LinkMarker.svelte +42 -0
- package/dist/markers/LinkMarker.svelte.d.ts +7 -0
- package/dist/markers/RuleMarker.svelte +24 -0
- package/dist/markers/RuleMarker.svelte.d.ts +10 -0
- package/dist/markers/SphereMarker.svelte +15 -0
- package/dist/markers/SphereMarker.svelte.d.ts +7 -0
- package/dist/markers/TextMarker.svelte +1 -1
- package/dist/markers/TextMarker.svelte.d.ts +1 -1
- package/dist/markers/VectorMarker.svelte +20 -0
- package/dist/markers/VectorMarker.svelte.d.ts +7 -0
- package/dist/markers/inset/InsetMarker.svelte +38 -45
- package/dist/markers/inset/InsetMarker.svelte.d.ts +6 -2
- package/dist/plots/bar/Plot.svelte +96 -87
- package/dist/plots/bar/Plot.svelte.d.ts +4 -5
- package/dist/plots/bar/ValueLabels.svelte +70 -24
- package/dist/plots/bar/ValueLabels.svelte.d.ts +11 -2
- package/dist/plots/bar/buildBarMarkers.d.ts +30 -0
- package/dist/plots/bar/buildBarMarkers.js +100 -0
- package/dist/plots/bar/layout.svelte.d.ts +1 -2
- package/dist/plots/bar/layout.svelte.js +1 -1
- package/dist/plots/geo/Plot.svelte +113 -387
- package/dist/plots/geo/Plot.svelte.d.ts +5 -4
- package/dist/plots/geo/buildGeoMarkers.d.ts +33 -0
- package/dist/plots/geo/buildGeoMarkers.js +47 -0
- package/dist/plots/heatmap/Plot.svelte +27 -52
- package/dist/plots/heatmap/Plot.svelte.d.ts +4 -5
- package/dist/plots/heatmap/ValueLabels.svelte +5 -5
- package/dist/plots/heatmap/ValueLabels.svelte.d.ts +3 -3
- package/dist/plots/heatmap/buildCellMarkers.d.ts +21 -0
- package/dist/plots/heatmap/buildCellMarkers.js +25 -0
- package/dist/plots/line/Plot.svelte +73 -141
- package/dist/plots/line/Plot.svelte.d.ts +4 -4
- package/dist/plots/line/ValueLabels.svelte +84 -85
- package/dist/plots/line/ValueLabels.svelte.d.ts +10 -1
- package/dist/plots/line/buildLineMarkers.d.ts +15 -0
- package/dist/plots/line/buildLineMarkers.js +57 -0
- package/dist/plots/pyramid/Plot.svelte +80 -121
- package/dist/plots/pyramid/Plot.svelte.d.ts +4 -5
- package/dist/plots/pyramid/ValueLabels.svelte +71 -25
- package/dist/plots/pyramid/ValueLabels.svelte.d.ts +11 -2
- package/dist/plots/pyramid/buildPyramidBarMarkers.d.ts +19 -0
- package/dist/plots/pyramid/buildPyramidBarMarkers.js +32 -0
- package/dist/plots/scatter/Plot.svelte +242 -0
- package/dist/{layout/plot/GridLayout.svelte.d.ts → plots/scatter/Plot.svelte.d.ts} +9 -7
- package/dist/plots/scatter/ValueLabels.svelte +149 -0
- package/dist/plots/{bar/BarSegments.svelte.d.ts → scatter/ValueLabels.svelte.d.ts} +17 -15
- package/dist/plots/scatter/buildScatterMarkers.d.ts +11 -0
- package/dist/plots/scatter/buildScatterMarkers.js +37 -0
- package/dist/plots/scatter/resolveRadiusPaddedDomain.d.ts +10 -0
- package/dist/plots/scatter/resolveRadiusPaddedDomain.js +23 -0
- package/dist/plots/utils/declutter.d.ts +55 -37
- package/dist/plots/utils/declutter.js +79 -64
- package/dist/plots/utils/geoAccessors.d.ts +1 -1
- package/dist/plots/utils/geoAccessors.js +1 -1
- package/dist/plots/utils/labelOverflow.svelte.d.ts +51 -0
- package/dist/plots/utils/labelOverflow.svelte.js +116 -0
- package/dist/plots/utils/legendDisabled.d.ts +1 -1
- package/dist/plots/utils/segments.d.ts +17 -10
- package/dist/plots/utils/segments.js +26 -16
- package/dist/types/charts/common.d.ts +3 -50
- package/dist/types/charts/props.d.ts +8 -8
- package/dist/types/configuration/styling.d.ts +26 -2
- package/dist/types/{plots/scales/geo.d.ts → layout/coordinates.d.ts} +33 -1
- package/dist/types/layout/facet.d.ts +41 -0
- package/dist/types/layout/geometry.d.ts +17 -0
- package/dist/types/layout/legend.d.ts +65 -1
- package/dist/types/{plots/axis.d.ts → layout/scales.d.ts} +11 -0
- package/dist/types/layout/styles.d.ts +115 -0
- package/dist/types/layout/timeline.d.ts +32 -0
- package/dist/types/layout/tooltip.d.ts +20 -1
- package/dist/types/markers/all.d.ts +11 -0
- package/dist/types/markers/all.js +1 -0
- package/dist/types/markers/base.d.ts +45 -0
- package/dist/types/markers/base.js +1 -0
- package/dist/types/markers/common.d.ts +64 -16
- package/dist/types/markers/geo.d.ts +75 -100
- package/dist/types/markers/line.d.ts +34 -0
- package/dist/types/markers/line.js +1 -0
- package/dist/types/markers/props.d.ts +1 -1
- package/dist/types/plots/data/common.d.ts +1 -1
- package/dist/types/plots/data/scatter.d.ts +13 -0
- package/dist/types/plots/data/scatter.js +1 -0
- package/dist/types/plots/props.d.ts +46 -74
- package/dist/types/plots/segments/common.d.ts +6 -12
- package/dist/types/plots/segments/config.d.ts +1 -1
- package/dist/types/plots/styling.d.ts +10 -0
- package/dist/{plots/utils → utils}/delta.d.ts +2 -2
- package/dist/{plots/utils → utils}/delta.js +1 -1
- package/dist/utils/edgeAwareAnchor.d.ts +37 -0
- package/dist/utils/edgeAwareAnchor.js +65 -0
- package/dist/utils/grouping.d.ts +1 -1
- package/dist/utils/interpolate.d.ts +2 -2
- package/package.json +3 -1
- package/dist/layout/geo/GeoLayout.svelte +0 -51
- package/dist/layout/geo/GeoLayout.svelte.d.ts +0 -24
- package/dist/layout/geo/resolveProjection.js +0 -48
- package/dist/layout/plot/GridLayout.svelte +0 -30
- package/dist/layout/plot/RuleLayout.svelte +0 -111
- package/dist/plots/bar/BarSegments.svelte +0 -69
- package/dist/types/charts/legend.d.ts +0 -60
- package/dist/types/plots/delta.d.ts +0 -19
- package/dist/types/plots/styles/common.d.ts +0 -52
- package/dist/types/plots/styles/geo.d.ts +0 -42
- /package/dist/{plots/geo → layout/coordinates}/projections/argentina.js +0 -0
- /package/dist/{plots/geo → layout/coordinates}/projections/fit.d.ts +0 -0
- /package/dist/{plots/geo → layout/coordinates}/projections/fit.js +0 -0
- /package/dist/{plots/geo → layout/coordinates}/rotate.svelte.d.ts +0 -0
- /package/dist/{plots/geo → layout/coordinates}/rotate.svelte.js +0 -0
- /package/dist/{plots/geo → layout/coordinates}/zoom.svelte.js +0 -0
- /package/dist/types/{charts/legend.js → layout/coordinates.js} +0 -0
- /package/dist/types/{plots/axis.js → layout/facet.js} +0 -0
- /package/dist/types/{plots/delta.js → layout/geometry.js} +0 -0
- /package/dist/types/{plots/scales/geo.js → layout/scales.js} +0 -0
- /package/dist/types/{plots/styles/common.js → layout/styles.js} +0 -0
- /package/dist/types/{plots/styles/geo.js → layout/timeline.js} +0 -0
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
<script lang="ts" generics="TData extends Record<string, unknown>">
|
|
2
|
-
import { AxisX, AxisY } from 'svelteplot';
|
|
2
|
+
import { AxisX, AxisY, GridX, GridY } from 'svelteplot';
|
|
3
3
|
import { getConfiguration } from '../../configuration/config.svelte';
|
|
4
|
-
import type { AxisValue } from '../../types/
|
|
5
|
-
import type { AxisBasedScalesConfig } from '../../types/plots/props';
|
|
4
|
+
import type { AxisValue, AxisBasedScalesConfig } from '../../types/layout/scales';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
|
-
* Axis ticks/titles for
|
|
9
|
-
* (line/bar/heatmap/pyramid)
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* `AxisX`/`AxisY` themselves inherit their actual scale from svelteplot's
|
|
13
|
-
* `<Plot>` context — this only supplies their cosmetic/tick-count props.
|
|
7
|
+
* Axis ticks/titles and grid lines for plot kinds with an x/y axis pair
|
|
8
|
+
* (line/bar/heatmap/pyramid). Not used by `GeoPlot` (a map has no x/y axis
|
|
9
|
+
* pair). The scale itself is resolved by `buildScale` and applied via
|
|
10
|
+
* `BasePlotLayout`'s `<Plot x y>` — this only renders the cosmetics.
|
|
14
11
|
*/
|
|
15
12
|
let {
|
|
16
13
|
data,
|
|
@@ -35,13 +32,7 @@
|
|
|
35
32
|
const xValues = $derived(data.map(getX));
|
|
36
33
|
const yValues = $derived(data.map(getY));
|
|
37
34
|
|
|
38
|
-
/**
|
|
39
|
-
* Caps the requested tick count to the axis's own number of distinct
|
|
40
|
-
* values, but never above what the pixel width would naturally request —
|
|
41
|
-
* so a narrow domain (e.g. 5 whole years) never gets subdivided into
|
|
42
|
-
* fractional ticks (2020, 2020.5, 2021, …) just to fill the space. Skipped
|
|
43
|
-
* whenever the caller already controls tick density directly.
|
|
44
|
-
*/
|
|
35
|
+
/** Caps tick count to the axis's distinct value count, never above what fits the pixel span. Skipped once the caller sets tick density directly. */
|
|
45
36
|
function autoTickCount(scale: AxisBasedScalesConfig<TData>['x'], values: AxisValue[], span: number, spacing: number, min: number): number | undefined {
|
|
46
37
|
if (scale?.ticks != null || scale?.tickSpacing != null || scale?.interval != null) return undefined;
|
|
47
38
|
const uniqueCount = new Set(values.map(String)).size;
|
|
@@ -93,3 +84,19 @@
|
|
|
93
84
|
opacity="0.8"
|
|
94
85
|
>{verticalYLabel.text}</text>
|
|
95
86
|
{/if}
|
|
87
|
+
{#if scales?.x?.grid}
|
|
88
|
+
<GridX
|
|
89
|
+
stroke={typeof scales.x.grid === 'object' ? scales.x.grid.stroke : undefined}
|
|
90
|
+
strokeWidth={typeof scales.x.grid === 'object' ? scales.x.grid.strokeWidth : undefined}
|
|
91
|
+
strokeOpacity={typeof scales.x.grid === 'object' ? scales.x.grid.strokeOpacity : undefined}
|
|
92
|
+
strokeDasharray={typeof scales.x.grid === 'object' ? scales.x.grid.strokeDasharray : undefined}
|
|
93
|
+
/>
|
|
94
|
+
{/if}
|
|
95
|
+
{#if scales?.y?.grid}
|
|
96
|
+
<GridY
|
|
97
|
+
stroke={typeof scales.y.grid === 'object' ? scales.y.grid.stroke : undefined}
|
|
98
|
+
strokeWidth={typeof scales.y.grid === 'object' ? scales.y.grid.strokeWidth : undefined}
|
|
99
|
+
strokeOpacity={typeof scales.y.grid === 'object' ? scales.y.grid.strokeOpacity : undefined}
|
|
100
|
+
strokeDasharray={typeof scales.y.grid === 'object' ? scales.y.grid.strokeDasharray : undefined}
|
|
101
|
+
/>
|
|
102
|
+
{/if}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { AxisValue } from '../../types/
|
|
2
|
-
import type { AxisBasedScalesConfig } from '../../types/plots/props';
|
|
1
|
+
import type { AxisValue, AxisBasedScalesConfig } from '../../types/layout/scales';
|
|
3
2
|
declare function $$render<TData extends Record<string, unknown>>(): {
|
|
4
3
|
props: {
|
|
5
4
|
data: TData[];
|
|
@@ -34,6 +33,6 @@ interface $$IsomorphicComponent {
|
|
|
34
33
|
<TData extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<TData>['props']> & {}): ReturnType<__sveltets_Render<TData>['exports']>;
|
|
35
34
|
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
36
35
|
}
|
|
37
|
-
declare const
|
|
38
|
-
type
|
|
39
|
-
export default
|
|
36
|
+
declare const ScalesLayout: $$IsomorphicComponent;
|
|
37
|
+
type ScalesLayout<TData extends Record<string, unknown>> = InstanceType<typeof ScalesLayout<TData>>;
|
|
38
|
+
export default ScalesLayout;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AxisValue, AxisScale } from '../../types/layout/scales';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves an author-facing {@link AxisScale} into what svelteplot's
|
|
4
|
+
* `<Plot x>`/`<Plot y>` accepts — strips `grid`/`seriesLayout`/
|
|
5
|
+
* `seriesPadding`/a vertical `label`, since `ScalesLayout` renders those
|
|
6
|
+
* itself.
|
|
7
|
+
*/
|
|
8
|
+
export declare function buildScale<TData>(scale: AxisScale<TData> | undefined, rows: TData[], getAxisValue: (d: TData) => AxisValue): AxisScale<TData> | undefined;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
2
|
+
/** One representative row per unique axis value (first occurrence in `rows`). */
|
|
3
|
+
function uniqueRows(rows, getAxisValue) {
|
|
4
|
+
const seen = new SvelteSet();
|
|
5
|
+
const out = [];
|
|
6
|
+
for (const row of rows) {
|
|
7
|
+
const v = getAxisValue(row);
|
|
8
|
+
if (seen.has(v))
|
|
9
|
+
continue;
|
|
10
|
+
seen.add(v);
|
|
11
|
+
out.push(row);
|
|
12
|
+
}
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
/** svelteplot's `sort` option only does a fixed ascending sort; a custom `scale.sort` comparator is resolved into an explicit `domain` instead, which svelteplot honors as-is. */
|
|
16
|
+
function sortedScale(scale, rows, getAxisValue) {
|
|
17
|
+
const sort = scale?.sort;
|
|
18
|
+
if (!sort || scale?.domain)
|
|
19
|
+
return scale;
|
|
20
|
+
const reps = uniqueRows(rows, getAxisValue);
|
|
21
|
+
if (reps.length === 0 || !reps.every((r) => typeof getAxisValue(r) === 'string'))
|
|
22
|
+
return scale;
|
|
23
|
+
const domain = [...reps].sort(sort).map(getAxisValue);
|
|
24
|
+
return { ...scale, domain };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolves an author-facing {@link AxisScale} into what svelteplot's
|
|
28
|
+
* `<Plot x>`/`<Plot y>` accepts — strips `grid`/`seriesLayout`/
|
|
29
|
+
* `seriesPadding`/a vertical `label`, since `ScalesLayout` renders those
|
|
30
|
+
* itself.
|
|
31
|
+
*/
|
|
32
|
+
export function buildScale(scale, rows, getAxisValue) {
|
|
33
|
+
const sorted = sortedScale(scale, rows, getAxisValue);
|
|
34
|
+
if (!sorted)
|
|
35
|
+
return sorted;
|
|
36
|
+
const out = { ...sorted };
|
|
37
|
+
delete out.grid;
|
|
38
|
+
delete out.seriesLayout;
|
|
39
|
+
delete out.seriesPadding;
|
|
40
|
+
if (scale?.labelOrientation === 'vertical')
|
|
41
|
+
delete out.label;
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Frame } from 'svelteplot';
|
|
3
|
+
import TileLayer from './TileLayer.svelte';
|
|
4
|
+
import { geometryLonLatBounds } from '../../plots/utils/geoAccessors';
|
|
5
|
+
import type { StylesLayoutConfig } from '../../types/layout/styles';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Non-data cosmetic embellishments driven by a plot kind's own `styles`
|
|
9
|
+
* prop. Composed by `BasePlotLayout` for every plot kind, nested inside
|
|
10
|
+
* `CoordinatesLayout` so embellishments pan/zoom together with whatever
|
|
11
|
+
* they're placed alongside: the raster basemap `tileLayer` (`GeoPlot`
|
|
12
|
+
* only — other kinds' `styles` simply don't declare it) and the `frame`
|
|
13
|
+
* border, available on every plot kind.
|
|
14
|
+
*/
|
|
15
|
+
let {
|
|
16
|
+
styles,
|
|
17
|
+
width,
|
|
18
|
+
height,
|
|
19
|
+
numericMargins,
|
|
20
|
+
}: {
|
|
21
|
+
styles?: StylesLayoutConfig;
|
|
22
|
+
width: number;
|
|
23
|
+
height: number;
|
|
24
|
+
numericMargins: { top: number; right: number; bottom: number; left: number };
|
|
25
|
+
} = $props();
|
|
26
|
+
|
|
27
|
+
const frame = $derived(styles?.frame);
|
|
28
|
+
const frameStyle = $derived(typeof frame === 'object' ? frame : undefined);
|
|
29
|
+
|
|
30
|
+
/** Auto-fits `TileLayer` to the tile-layer feature set `GeoPlot` tacks onto `styles.tileLayer.features` — see `StylesLayoutConfig`'s own doc comment. */
|
|
31
|
+
const dataLonLatBounds = $derived.by((): [number, number, number, number] | null => {
|
|
32
|
+
const features = styles?.tileLayer?.features;
|
|
33
|
+
if (!features?.length) return null;
|
|
34
|
+
let minLon = Infinity;
|
|
35
|
+
let maxLon = -Infinity;
|
|
36
|
+
let minLat = Infinity;
|
|
37
|
+
let maxLat = -Infinity;
|
|
38
|
+
for (const f of features) {
|
|
39
|
+
const b = geometryLonLatBounds(f.geometry);
|
|
40
|
+
if (!b) continue;
|
|
41
|
+
if (b[0] < minLon) minLon = b[0];
|
|
42
|
+
if (b[2] > maxLon) maxLon = b[2];
|
|
43
|
+
if (b[1] < minLat) minLat = b[1];
|
|
44
|
+
if (b[3] > maxLat) maxLat = b[3];
|
|
45
|
+
}
|
|
46
|
+
return minLon === Infinity ? null : [minLon, minLat, maxLon, maxLat];
|
|
47
|
+
});
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
{#if styles?.tileLayer}
|
|
51
|
+
<TileLayer
|
|
52
|
+
config={styles.tileLayer}
|
|
53
|
+
left={numericMargins.left}
|
|
54
|
+
top={numericMargins.top}
|
|
55
|
+
width={Math.max(0, width - numericMargins.left - numericMargins.right)}
|
|
56
|
+
height={Math.max(0, height - numericMargins.top - numericMargins.bottom)}
|
|
57
|
+
{dataLonLatBounds}
|
|
58
|
+
/>
|
|
59
|
+
{/if}
|
|
60
|
+
{#if frame}
|
|
61
|
+
<Frame
|
|
62
|
+
fill={frameStyle?.fill}
|
|
63
|
+
fillOpacity={frameStyle?.fillOpacity}
|
|
64
|
+
stroke={frameStyle?.stroke}
|
|
65
|
+
strokeWidth={frameStyle?.strokeWidth}
|
|
66
|
+
strokeOpacity={frameStyle?.strokeOpacity}
|
|
67
|
+
strokeDasharray={frameStyle?.strokeDasharray}
|
|
68
|
+
borderRadius={frameStyle?.borderRadius}
|
|
69
|
+
inset={frameStyle?.inset}
|
|
70
|
+
insetTop={frameStyle?.insetTop}
|
|
71
|
+
insetRight={frameStyle?.insetRight}
|
|
72
|
+
insetBottom={frameStyle?.insetBottom}
|
|
73
|
+
insetLeft={frameStyle?.insetLeft}
|
|
74
|
+
/>
|
|
75
|
+
{/if}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { StylesLayoutConfig } from '../../types/layout/styles';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
styles?: StylesLayoutConfig;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
numericMargins: {
|
|
7
|
+
top: number;
|
|
8
|
+
right: number;
|
|
9
|
+
bottom: number;
|
|
10
|
+
left: number;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
declare const StylesLayout: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
14
|
+
type StylesLayout = ReturnType<typeof StylesLayout>;
|
|
15
|
+
export default StylesLayout;
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
tileUrl,
|
|
8
8
|
toProjectionLike,
|
|
9
9
|
type StreamingProjection,
|
|
10
|
-
} from '
|
|
11
|
-
import type { GeoTileLayerConfig } from '../../types/
|
|
10
|
+
} from '../../plots/utils/tiles';
|
|
11
|
+
import type { GeoTileLayerConfig } from '../../types/layout/styles';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Renders an XYZ raster tile basemap under the vector geometry. Mounted as
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { Plot, RuleX, RuleY, Text } from 'svelteplot';
|
|
3
|
-
import type { Orientation, TimeValue } from '../../types/
|
|
3
|
+
import type { Orientation, TimeValue } from '../../types/layout/timeline';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Purely visual time axis: a baseline rule plus the ordered step labels with
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Orientation, TimeValue, TimelineChildSnippet, TimelineInterpolation } from '../../types/
|
|
1
|
+
import type { Orientation, TimeValue, TimelineChildSnippet, TimelineInterpolation } from '../../types/layout/timeline';
|
|
2
2
|
import type { Accessor } from '../../types/plots/data/common';
|
|
3
3
|
declare function $$render<TRow extends Record<string, unknown>>(): {
|
|
4
4
|
props: {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
+
import { resolveTooltipPlacement } from './resolveTooltipPlacement';
|
|
4
|
+
import type { TooltipAnchorX, TooltipAnchorY } from '../../types/layout/tooltip';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Presentational tooltip positioned at `(x, y)` within a `position: relative`
|
|
@@ -7,10 +9,11 @@
|
|
|
7
9
|
* {@link ChartContainer} stamps from the config, so it themes for free. Plots
|
|
8
10
|
* that implement hovering can render this with their own content snippet.
|
|
9
11
|
*
|
|
10
|
-
* Placement is boundary-aware: it prefers sitting
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Placement is boundary-aware: it prefers sitting `anchorX`/`anchorY` of the
|
|
13
|
+
* point (default: to the right and below — clear of the cursor so the
|
|
14
|
+
* tooltip never covers what's being hovered), falling back toward the
|
|
15
|
+
* opposite side (via the centered position) when the preferred side would
|
|
16
|
+
* overflow the `bounds` box — see `resolveTooltipPlacement`.
|
|
14
17
|
*/
|
|
15
18
|
let {
|
|
16
19
|
x,
|
|
@@ -20,6 +23,8 @@
|
|
|
20
23
|
color,
|
|
21
24
|
bounds,
|
|
22
25
|
plain = false,
|
|
26
|
+
anchorX = 'right',
|
|
27
|
+
anchorY = 'bottom',
|
|
23
28
|
children,
|
|
24
29
|
}: {
|
|
25
30
|
x: number;
|
|
@@ -36,28 +41,19 @@
|
|
|
36
41
|
* so a custom card can match its own corners via `--dct-tooltip-radius`.
|
|
37
42
|
*/
|
|
38
43
|
plain?: boolean;
|
|
44
|
+
/** Preferred horizontal anchor relative to the cursor. Defaults to `'right'`. */
|
|
45
|
+
anchorX?: TooltipAnchorX;
|
|
46
|
+
/** Preferred vertical anchor relative to the cursor. Defaults to `'bottom'`. */
|
|
47
|
+
anchorY?: TooltipAnchorY;
|
|
39
48
|
children: Snippet;
|
|
40
49
|
} = $props();
|
|
41
50
|
|
|
42
|
-
const GAP = 10;
|
|
43
|
-
const EDGE_MARGIN = 6;
|
|
44
|
-
|
|
45
51
|
let boxWidth = $state(0);
|
|
46
52
|
let boxHeight = $state(0);
|
|
47
53
|
|
|
48
|
-
const placement = $derived
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
let top = y - GAP - boxHeight;
|
|
53
|
-
if (top < EDGE_MARGIN) top = y + GAP;
|
|
54
|
-
top = Math.min(Math.max(top, EDGE_MARGIN), Math.max(EDGE_MARGIN, boundsHeight - boxHeight - EDGE_MARGIN));
|
|
55
|
-
|
|
56
|
-
let left = x - boxWidth / 2;
|
|
57
|
-
left = Math.min(Math.max(left, EDGE_MARGIN), Math.max(EDGE_MARGIN, boundsWidth - boxWidth - EDGE_MARGIN));
|
|
58
|
-
|
|
59
|
-
return { left, top };
|
|
60
|
-
});
|
|
54
|
+
const placement = $derived(
|
|
55
|
+
resolveTooltipPlacement({ x, y, boxWidth, boxHeight, bounds, anchorX, anchorY }),
|
|
56
|
+
);
|
|
61
57
|
</script>
|
|
62
58
|
|
|
63
59
|
{#if visible}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { TooltipAnchorX, TooltipAnchorY } from '../../types/layout/tooltip';
|
|
2
3
|
type $$ComponentProps = {
|
|
3
4
|
x: number;
|
|
4
5
|
y: number;
|
|
@@ -17,6 +18,10 @@ type $$ComponentProps = {
|
|
|
17
18
|
* so a custom card can match its own corners via `--dct-tooltip-radius`.
|
|
18
19
|
*/
|
|
19
20
|
plain?: boolean;
|
|
21
|
+
/** Preferred horizontal anchor relative to the cursor. Defaults to `'right'`. */
|
|
22
|
+
anchorX?: TooltipAnchorX;
|
|
23
|
+
/** Preferred vertical anchor relative to the cursor. Defaults to `'bottom'`. */
|
|
24
|
+
anchorY?: TooltipAnchorY;
|
|
20
25
|
children: Snippet;
|
|
21
26
|
};
|
|
22
27
|
declare const Tooltip: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts" generics="TRow extends Record<string, unknown>">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import Tooltip from './Tooltip.svelte';
|
|
4
|
+
import type { TooltipAnchorX, TooltipAnchorY } from '../../types/layout/tooltip';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* The "resolve + render" step every hoverable plot repeats verbatim:
|
|
@@ -20,6 +21,8 @@
|
|
|
20
21
|
rows,
|
|
21
22
|
format,
|
|
22
23
|
content,
|
|
24
|
+
anchorX,
|
|
25
|
+
anchorY,
|
|
23
26
|
}: {
|
|
24
27
|
visible: boolean;
|
|
25
28
|
x: number;
|
|
@@ -31,11 +34,14 @@
|
|
|
31
34
|
format: (row: TRow) => string;
|
|
32
35
|
/** Custom tooltip body; replaces the joined `format` text when given. */
|
|
33
36
|
content?: Snippet<[{ rows: TRow[] }]>;
|
|
37
|
+
/** Preferred anchor — forwarded to `Tooltip` as-is; `undefined` falls through to its own system default. */
|
|
38
|
+
anchorX?: TooltipAnchorX;
|
|
39
|
+
anchorY?: TooltipAnchorY;
|
|
34
40
|
} = $props();
|
|
35
41
|
</script>
|
|
36
42
|
|
|
37
43
|
{#if visible}
|
|
38
|
-
<Tooltip {x} {y} {bounds} plain={!!content}>
|
|
44
|
+
<Tooltip {x} {y} {bounds} plain={!!content} {anchorX} {anchorY}>
|
|
39
45
|
{#if content}
|
|
40
46
|
{@render content({ rows })}
|
|
41
47
|
{:else}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { TooltipAnchorX, TooltipAnchorY } from '../../types/layout/tooltip';
|
|
2
3
|
declare function $$render<TRow extends Record<string, unknown>>(): {
|
|
3
4
|
props: {
|
|
4
5
|
visible: boolean;
|
|
@@ -16,6 +17,9 @@ declare function $$render<TRow extends Record<string, unknown>>(): {
|
|
|
16
17
|
content?: Snippet<[{
|
|
17
18
|
rows: TRow[];
|
|
18
19
|
}]>;
|
|
20
|
+
/** Preferred anchor — forwarded to `Tooltip` as-is; `undefined` falls through to its own system default. */
|
|
21
|
+
anchorX?: TooltipAnchorX;
|
|
22
|
+
anchorY?: TooltipAnchorY;
|
|
19
23
|
};
|
|
20
24
|
exports: {};
|
|
21
25
|
bindings: "";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HoverStore, HoverPoint } from '../../types/layout/tooltip';
|
|
2
|
-
import type { AxisValue } from '../../types/
|
|
2
|
+
import type { AxisValue } from '../../types/layout/scales';
|
|
3
3
|
/**
|
|
4
4
|
* The plot-agnostic hover state machine shared by every plot. Given reactive
|
|
5
5
|
* accessors for the current hover store, this facet's id, and the local points,
|
|
@@ -38,10 +38,9 @@ export declare function createHoverController<T extends Record<string, unknown>>
|
|
|
38
38
|
row: T;
|
|
39
39
|
}[];
|
|
40
40
|
readonly matchedRows: HoverPoint<T>[];
|
|
41
|
-
readonly
|
|
42
|
-
readonly
|
|
43
|
-
readonly
|
|
44
|
-
readonly impliesRuleY: boolean;
|
|
41
|
+
readonly activePoint: HoverPoint<T> | null;
|
|
42
|
+
readonly impliesCrosshairX: boolean;
|
|
43
|
+
readonly impliesCrosshairY: boolean;
|
|
45
44
|
readonly showTooltip: boolean;
|
|
46
45
|
readonly tooltipPosition: {
|
|
47
46
|
x: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { eq, matchByStrategy,
|
|
1
|
+
import { eq, matchByStrategy, impliesCrosshairX, impliesCrosshairY, filterBySeries, } from './utils';
|
|
2
2
|
/**
|
|
3
3
|
* The plot-agnostic hover state machine shared by every plot. Given reactive
|
|
4
4
|
* accessors for the current hover store, this facet's id, and the local points,
|
|
@@ -35,13 +35,42 @@ export function createHoverController(args) {
|
|
|
35
35
|
// resolve a semantic value off the source row directly — e.g. a bar
|
|
36
36
|
// plot's category, regardless of which visual axis it's drawn on.
|
|
37
37
|
const highlight = $derived(matched.map(({ x, y, label, series, color, dx, dy, row }) => ({ x, y, label, series, color, dx, dy, row })));
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
/**
|
|
39
|
+
* The specific matched point that was actually hovered — found by the
|
|
40
|
+
* shared store's `activeSeries` (set from whichever row was hit), falling
|
|
41
|
+
* back to the first match when there's no series concept (punctual
|
|
42
|
+
* matches) or nothing tags a series. Consumers read `.x`/`.y` off this
|
|
43
|
+
* for the crosshair rule position — every point in `matched` already
|
|
44
|
+
* shares the "implied" axis's value by construction of `matchByStrategy`,
|
|
45
|
+
* and picking the actively-hovered one is what makes forcing a rule on
|
|
46
|
+
* the *other* axis (e.g. `ruleY: true` under an `'x'`-strategy chart)
|
|
47
|
+
* point at the row under the cursor rather than an arbitrary matched
|
|
48
|
+
* sibling. `BasePlotLayout` gates actual display with
|
|
49
|
+
* `impliesCrosshairX`/`Y` + the marker's own `ruleX`/`ruleY` props.
|
|
50
|
+
*
|
|
51
|
+
* `activeSeries` alone can't disambiguate two matched rows that share the
|
|
52
|
+
* same (or absent) series tag — e.g. an ungrouped bar/line chart where two
|
|
53
|
+
* categories tie on the matched axis value. `onPointer` sets `activeX`/
|
|
54
|
+
* `activeY` straight from the hit row, so when more than one series-tied
|
|
55
|
+
* candidate remains, narrowing to the one whose own x/y equals the hit
|
|
56
|
+
* row's picks the actual row under the cursor instead of an arbitrary tie.
|
|
57
|
+
*/
|
|
58
|
+
const activePoint = $derived.by(() => {
|
|
59
|
+
if (matched.length === 0)
|
|
60
|
+
return null;
|
|
61
|
+
const h = args.hover();
|
|
62
|
+
const bySeries = matched.filter((p) => p.series === h.activeSeries);
|
|
63
|
+
const candidates = bySeries.length > 0 ? bySeries : matched;
|
|
64
|
+
if (candidates.length === 1)
|
|
65
|
+
return candidates[0];
|
|
66
|
+
const { activeX, activeY } = h;
|
|
67
|
+
if (activeX == null || activeY == null)
|
|
68
|
+
return candidates[0];
|
|
69
|
+
return candidates.find((p) => eq(p.x, activeX) && eq(p.y, activeY)) ?? candidates[0];
|
|
70
|
+
});
|
|
42
71
|
// Whether the current strategy implies a crosshair rule on each axis by default.
|
|
43
|
-
const
|
|
44
|
-
const
|
|
72
|
+
const _impliesCrosshairX = $derived(impliesCrosshairX(args.hover().strategy));
|
|
73
|
+
const _impliesCrosshairY = $derived(impliesCrosshairY(args.hover().strategy));
|
|
45
74
|
const showTooltip = $derived.by(() => {
|
|
46
75
|
if (matchedRows.length === 0)
|
|
47
76
|
return false;
|
|
@@ -102,17 +131,14 @@ export function createHoverController(args) {
|
|
|
102
131
|
get matchedRows() {
|
|
103
132
|
return matchedRows;
|
|
104
133
|
},
|
|
105
|
-
get
|
|
106
|
-
return
|
|
107
|
-
},
|
|
108
|
-
get ruleY() {
|
|
109
|
-
return ruleY;
|
|
134
|
+
get activePoint() {
|
|
135
|
+
return activePoint;
|
|
110
136
|
},
|
|
111
|
-
get
|
|
112
|
-
return
|
|
137
|
+
get impliesCrosshairX() {
|
|
138
|
+
return _impliesCrosshairX;
|
|
113
139
|
},
|
|
114
|
-
get
|
|
115
|
-
return
|
|
140
|
+
get impliesCrosshairY() {
|
|
141
|
+
return _impliesCrosshairY;
|
|
116
142
|
},
|
|
117
143
|
get showTooltip() {
|
|
118
144
|
return showTooltip;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { TooltipAnchorX, TooltipAnchorY } from '../../types/layout/tooltip';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a floating tooltip's top-left pixel position relative to a cursor
|
|
4
|
+
* point, honoring a preferred anchor per axis and falling back toward the
|
|
5
|
+
* opposite side (via the centered position) when the preferred side would
|
|
6
|
+
* push it past `bounds`. Used by `Tooltip.svelte` for both the default box
|
|
7
|
+
* and any custom `content` snippet — both go through the same placement.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveTooltipPlacement(args: {
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
boxWidth: number;
|
|
13
|
+
boxHeight: number;
|
|
14
|
+
/** Box the tooltip must stay within — usually the hosting plot's width/height. */
|
|
15
|
+
bounds?: {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
};
|
|
19
|
+
anchorX: TooltipAnchorX;
|
|
20
|
+
anchorY: TooltipAnchorY;
|
|
21
|
+
}): {
|
|
22
|
+
left: number;
|
|
23
|
+
top: number;
|
|
24
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const GAP = 10;
|
|
2
|
+
const EDGE_MARGIN = 6;
|
|
3
|
+
/** Anchors tried in order for a given preference — the opposite side is always the last resort. */
|
|
4
|
+
function candidateOrder(anchor) {
|
|
5
|
+
if (anchor === 'before')
|
|
6
|
+
return ['before', 'middle', 'after'];
|
|
7
|
+
if (anchor === 'after')
|
|
8
|
+
return ['after', 'middle', 'before'];
|
|
9
|
+
return ['middle'];
|
|
10
|
+
}
|
|
11
|
+
function axisStart(anchor, pos, size) {
|
|
12
|
+
if (anchor === 'before')
|
|
13
|
+
return pos - GAP - size;
|
|
14
|
+
if (anchor === 'after')
|
|
15
|
+
return pos + GAP;
|
|
16
|
+
return pos - size / 2;
|
|
17
|
+
}
|
|
18
|
+
function fits(start, size, boundsSize) {
|
|
19
|
+
return start >= EDGE_MARGIN && start + size <= boundsSize - EDGE_MARGIN;
|
|
20
|
+
}
|
|
21
|
+
/** Resolves one axis: tries the preferred anchor, then its fallbacks in order, then clamps as a last resort. */
|
|
22
|
+
function resolveAxis(pos, size, boundsSize, anchor) {
|
|
23
|
+
const order = candidateOrder(anchor);
|
|
24
|
+
for (const candidate of order) {
|
|
25
|
+
const start = axisStart(candidate, pos, size);
|
|
26
|
+
if (fits(start, size, boundsSize))
|
|
27
|
+
return start;
|
|
28
|
+
}
|
|
29
|
+
const fallbackStart = axisStart(order[order.length - 1], pos, size);
|
|
30
|
+
const maxStart = Math.max(EDGE_MARGIN, boundsSize - size - EDGE_MARGIN);
|
|
31
|
+
return Math.min(Math.max(fallbackStart, EDGE_MARGIN), maxStart);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolves a floating tooltip's top-left pixel position relative to a cursor
|
|
35
|
+
* point, honoring a preferred anchor per axis and falling back toward the
|
|
36
|
+
* opposite side (via the centered position) when the preferred side would
|
|
37
|
+
* push it past `bounds`. Used by `Tooltip.svelte` for both the default box
|
|
38
|
+
* and any custom `content` snippet — both go through the same placement.
|
|
39
|
+
*/
|
|
40
|
+
export function resolveTooltipPlacement(args) {
|
|
41
|
+
const boundsWidth = args.bounds?.width ?? Infinity;
|
|
42
|
+
const boundsHeight = args.bounds?.height ?? Infinity;
|
|
43
|
+
const xAnchor = args.anchorX === 'right' ? 'after' : args.anchorX === 'left' ? 'before' : 'middle';
|
|
44
|
+
const yAnchor = args.anchorY === 'bottom' ? 'after' : args.anchorY === 'top' ? 'before' : 'middle';
|
|
45
|
+
return {
|
|
46
|
+
left: resolveAxis(args.x, args.boxWidth, boundsWidth, xAnchor),
|
|
47
|
+
top: resolveAxis(args.y, args.boxHeight, boundsHeight, yAnchor),
|
|
48
|
+
};
|
|
49
|
+
}
|