@fundar/data-chart-telling 0.0.29 → 0.0.31
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 +1 -0
- package/dist/charts/bar/Chart.svelte +2 -2
- package/dist/charts/bar/Chart.svelte.d.ts +2 -2
- package/dist/charts/line/Chart.svelte +2 -2
- package/dist/charts/line/Chart.svelte.d.ts +2 -2
- package/dist/charts/pyramid/Chart.svelte +2 -2
- package/dist/charts/pyramid/Chart.svelte.d.ts +3 -2
- package/dist/configuration/config.svelte.js +2 -0
- package/dist/configuration/themes/index.d.ts +88 -0
- package/dist/index.d.ts +4 -3
- package/dist/layout/coordinates/CoordinatesLayout.svelte +10 -5
- package/dist/layout/coordinates/CoordinatesLayout.svelte.d.ts +2 -0
- package/dist/layout/facet/FacetLayout.svelte +77 -20
- package/dist/layout/facet/FacetLayout.svelte.d.ts +2 -1
- package/dist/layout/plot/BasePlotLayout.svelte +19 -0
- package/dist/layout/plot/BasePlotLayout.svelte.d.ts +17 -0
- package/dist/plots/bar/Plot.svelte +13 -2
- package/dist/plots/bar/Plot.svelte.d.ts +2 -2
- package/dist/plots/bar/ValueLabels.svelte +5 -1
- package/dist/plots/bar/buildBarMarkers.d.ts +10 -3
- package/dist/plots/bar/buildBarMarkers.js +80 -57
- package/dist/plots/bar/hoverPoints.js +2 -1
- package/dist/plots/bar/layout.svelte.d.ts +2 -0
- package/dist/plots/bar/layout.svelte.js +6 -1
- package/dist/plots/heatmap/Plot.svelte +2 -2
- package/dist/plots/heatmap/Plot.svelte.d.ts +2 -2
- package/dist/plots/line/Plot.svelte +9 -9
- package/dist/plots/line/Plot.svelte.d.ts +2 -6
- package/dist/plots/line/ValueLabels.svelte +17 -2
- package/dist/plots/line/buildGapMarkers.d.ts +1 -1
- package/dist/plots/pyramid/Plot.svelte +39 -14
- package/dist/plots/pyramid/Plot.svelte.d.ts +2 -2
- package/dist/plots/pyramid/ValueLabels.svelte +5 -1
- package/dist/plots/pyramid/buildPyramidBarMarkers.d.ts +8 -1
- package/dist/plots/pyramid/buildPyramidBarMarkers.js +43 -3
- package/dist/plots/scatter/Plot.svelte +2 -2
- package/dist/plots/scatter/Plot.svelte.d.ts +2 -2
- package/dist/plots/utils/gaps.d.ts +20 -0
- package/dist/plots/utils/gaps.js +67 -0
- package/dist/plots/utils/labelOverflow.svelte.d.ts +9 -0
- package/dist/plots/utils/labelOverflow.svelte.js +11 -0
- package/dist/types/charts/props.d.ts +11 -2
- package/dist/types/configuration/styling.d.ts +11 -1
- package/dist/types/layout/facet.d.ts +2 -0
- package/dist/types/layout/styles.d.ts +29 -11
- package/dist/types/plots/gaps.d.ts +17 -2
- package/dist/types/plots/props.d.ts +1 -10
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { resolveAccessor } from '../../plots/utils/accessors';
|
|
11
11
|
import { resolveTimelineInterpolation } from '../../utils/interpolate';
|
|
12
12
|
import type { BarSegmentStyle } from '../../types/plots/segments/common';
|
|
13
|
-
import type {
|
|
13
|
+
import type { GapsAwareChartProps } from '../../types/charts/props';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* High-level bar chart. Groups flat rows by `z` into one series per group
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
facet,
|
|
40
40
|
timeline,
|
|
41
41
|
tooltip,
|
|
42
|
-
}:
|
|
42
|
+
}: GapsAwareChartProps<TRow, BarSegmentStyle> = $props();
|
|
43
43
|
|
|
44
44
|
const tip = $derived(resolveTooltip(tooltip, 'x'));
|
|
45
45
|
const hoverEnabled = $derived(tip.enabled || hasHoverMarker(markers));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { BarSegmentStyle } from '../../types/plots/segments/common';
|
|
2
|
-
import type {
|
|
2
|
+
import type { GapsAwareChartProps } from '../../types/charts/props';
|
|
3
3
|
declare function $$render<TRow extends Record<string, unknown>>(): {
|
|
4
|
-
props:
|
|
4
|
+
props: GapsAwareChartProps<TRow, BarSegmentStyle>;
|
|
5
5
|
exports: {};
|
|
6
6
|
bindings: "";
|
|
7
7
|
slots: {};
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { resolveAccessor } from '../../plots/utils/accessors';
|
|
11
11
|
import { resolveTimelineInterpolation } from '../../utils/interpolate';
|
|
12
12
|
import type { LineSegmentStyle } from '../../types/plots/segments/common';
|
|
13
|
-
import type {
|
|
13
|
+
import type { GapsAwareChartProps } from '../../types/charts/props';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* High-level line chart. It takes flat rows plus `x`/`y`/`z` encodings, groups
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
facet,
|
|
40
40
|
timeline,
|
|
41
41
|
tooltip,
|
|
42
|
-
}:
|
|
42
|
+
}: GapsAwareChartProps<TRow, LineSegmentStyle> = $props();
|
|
43
43
|
|
|
44
44
|
// A line should grow into its full extent over time, not snap to a single
|
|
45
45
|
// year's point — so default to a cumulative reveal unless the caller wants
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { LineSegmentStyle } from '../../types/plots/segments/common';
|
|
2
|
-
import type {
|
|
2
|
+
import type { GapsAwareChartProps } from '../../types/charts/props';
|
|
3
3
|
declare function $$render<TRow extends Record<string, unknown>>(): {
|
|
4
|
-
props:
|
|
4
|
+
props: GapsAwareChartProps<TRow, LineSegmentStyle>;
|
|
5
5
|
exports: {};
|
|
6
6
|
bindings: "";
|
|
7
7
|
slots: {};
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import type { AxisValue } from '../../types/layout/scales';
|
|
13
13
|
import type { Accessor } from '../../types/plots/data/common';
|
|
14
14
|
import type { BarSegmentStyle } from '../../types/plots/segments/common';
|
|
15
|
-
import type {
|
|
15
|
+
import type { GapsAwareChartProps } from '../../types/charts/props';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* High-level population-pyramid-style chart. Forwards flat rows and the
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
facet,
|
|
44
44
|
timeline,
|
|
45
45
|
tooltip,
|
|
46
|
-
}:
|
|
46
|
+
}: GapsAwareChartProps<TRow, BarSegmentStyle> & { x: Accessor<TRow, number>; z: Accessor<TRow, unknown> } = $props();
|
|
47
47
|
|
|
48
48
|
const getMagnitude = $derived(resolveAccessor<TRow>(magnitude));
|
|
49
49
|
const getCategory = $derived(resolveAccessor<TRow>(category));
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { Accessor } from '../../types/plots/data/common';
|
|
2
|
-
import type { ChartProps } from '../../types/charts/props';
|
|
3
2
|
declare function $$render<TRow extends Record<string, unknown>>(): {
|
|
4
|
-
props: ChartProps<TRow, import("../..").AreaStyle> & {
|
|
3
|
+
props: Omit<import("../../types/charts/props").ChartProps<TRow, import("../..").AreaStyle>, "styles"> & {
|
|
4
|
+
styles?: import("../..").GapsAwarePlotStylesConfig<import("../..").AreaStyle> | undefined;
|
|
5
|
+
} & {
|
|
5
6
|
x: Accessor<TRow, number>;
|
|
6
7
|
z: Accessor<TRow, unknown>;
|
|
7
8
|
};
|
|
@@ -85,6 +85,8 @@ export const DEFAULT_CONFIG = {
|
|
|
85
85
|
connectorWidth: 0.5,
|
|
86
86
|
},
|
|
87
87
|
},
|
|
88
|
+
bar: { gap: { fillOpacity: 0.5 } },
|
|
89
|
+
pyramid: { gap: { fillOpacity: 0.5 } },
|
|
88
90
|
scatter: { dotRadius: 5 },
|
|
89
91
|
timeline: { axisColor: 'currentColor', activeColor: '#e6580d', thickness: 60 },
|
|
90
92
|
hover: {
|
|
@@ -112,6 +112,28 @@ export declare const THEMES: {
|
|
|
112
112
|
connectorWidth?: number | undefined;
|
|
113
113
|
} | undefined;
|
|
114
114
|
} | undefined;
|
|
115
|
+
bar?: {
|
|
116
|
+
gap?: {
|
|
117
|
+
fill?: string | undefined;
|
|
118
|
+
fillOpacity?: number | undefined;
|
|
119
|
+
stroke?: string | undefined;
|
|
120
|
+
strokeWidth?: number | undefined;
|
|
121
|
+
strokeOpacity?: number | undefined;
|
|
122
|
+
strokeDasharray?: (string | import("../..").LineStyle) | undefined;
|
|
123
|
+
borderRadius?: number | undefined;
|
|
124
|
+
} | undefined;
|
|
125
|
+
} | undefined;
|
|
126
|
+
pyramid?: {
|
|
127
|
+
gap?: {
|
|
128
|
+
fill?: string | undefined;
|
|
129
|
+
fillOpacity?: number | undefined;
|
|
130
|
+
stroke?: string | undefined;
|
|
131
|
+
strokeWidth?: number | undefined;
|
|
132
|
+
strokeOpacity?: number | undefined;
|
|
133
|
+
strokeDasharray?: (string | import("../..").LineStyle) | undefined;
|
|
134
|
+
borderRadius?: number | undefined;
|
|
135
|
+
} | undefined;
|
|
136
|
+
} | undefined;
|
|
115
137
|
scatter?: {
|
|
116
138
|
dotRadius?: number | undefined;
|
|
117
139
|
} | undefined;
|
|
@@ -290,6 +312,28 @@ export declare const THEMES: {
|
|
|
290
312
|
connectorWidth?: number | undefined;
|
|
291
313
|
} | undefined;
|
|
292
314
|
} | undefined;
|
|
315
|
+
bar?: {
|
|
316
|
+
gap?: {
|
|
317
|
+
fill?: string | undefined;
|
|
318
|
+
fillOpacity?: number | undefined;
|
|
319
|
+
stroke?: string | undefined;
|
|
320
|
+
strokeWidth?: number | undefined;
|
|
321
|
+
strokeOpacity?: number | undefined;
|
|
322
|
+
strokeDasharray?: (string | import("../..").LineStyle) | undefined;
|
|
323
|
+
borderRadius?: number | undefined;
|
|
324
|
+
} | undefined;
|
|
325
|
+
} | undefined;
|
|
326
|
+
pyramid?: {
|
|
327
|
+
gap?: {
|
|
328
|
+
fill?: string | undefined;
|
|
329
|
+
fillOpacity?: number | undefined;
|
|
330
|
+
stroke?: string | undefined;
|
|
331
|
+
strokeWidth?: number | undefined;
|
|
332
|
+
strokeOpacity?: number | undefined;
|
|
333
|
+
strokeDasharray?: (string | import("../..").LineStyle) | undefined;
|
|
334
|
+
borderRadius?: number | undefined;
|
|
335
|
+
} | undefined;
|
|
336
|
+
} | undefined;
|
|
293
337
|
scatter?: {
|
|
294
338
|
dotRadius?: number | undefined;
|
|
295
339
|
} | undefined;
|
|
@@ -468,6 +512,28 @@ export declare const THEMES: {
|
|
|
468
512
|
connectorWidth?: number | undefined;
|
|
469
513
|
} | undefined;
|
|
470
514
|
} | undefined;
|
|
515
|
+
bar?: {
|
|
516
|
+
gap?: {
|
|
517
|
+
fill?: string | undefined;
|
|
518
|
+
fillOpacity?: number | undefined;
|
|
519
|
+
stroke?: string | undefined;
|
|
520
|
+
strokeWidth?: number | undefined;
|
|
521
|
+
strokeOpacity?: number | undefined;
|
|
522
|
+
strokeDasharray?: (string | import("../..").LineStyle) | undefined;
|
|
523
|
+
borderRadius?: number | undefined;
|
|
524
|
+
} | undefined;
|
|
525
|
+
} | undefined;
|
|
526
|
+
pyramid?: {
|
|
527
|
+
gap?: {
|
|
528
|
+
fill?: string | undefined;
|
|
529
|
+
fillOpacity?: number | undefined;
|
|
530
|
+
stroke?: string | undefined;
|
|
531
|
+
strokeWidth?: number | undefined;
|
|
532
|
+
strokeOpacity?: number | undefined;
|
|
533
|
+
strokeDasharray?: (string | import("../..").LineStyle) | undefined;
|
|
534
|
+
borderRadius?: number | undefined;
|
|
535
|
+
} | undefined;
|
|
536
|
+
} | undefined;
|
|
471
537
|
scatter?: {
|
|
472
538
|
dotRadius?: number | undefined;
|
|
473
539
|
} | undefined;
|
|
@@ -646,6 +712,28 @@ export declare const THEMES: {
|
|
|
646
712
|
connectorWidth?: number | undefined;
|
|
647
713
|
} | undefined;
|
|
648
714
|
} | undefined;
|
|
715
|
+
bar?: {
|
|
716
|
+
gap?: {
|
|
717
|
+
fill?: string | undefined;
|
|
718
|
+
fillOpacity?: number | undefined;
|
|
719
|
+
stroke?: string | undefined;
|
|
720
|
+
strokeWidth?: number | undefined;
|
|
721
|
+
strokeOpacity?: number | undefined;
|
|
722
|
+
strokeDasharray?: (string | import("../..").LineStyle) | undefined;
|
|
723
|
+
borderRadius?: number | undefined;
|
|
724
|
+
} | undefined;
|
|
725
|
+
} | undefined;
|
|
726
|
+
pyramid?: {
|
|
727
|
+
gap?: {
|
|
728
|
+
fill?: string | undefined;
|
|
729
|
+
fillOpacity?: number | undefined;
|
|
730
|
+
stroke?: string | undefined;
|
|
731
|
+
strokeWidth?: number | undefined;
|
|
732
|
+
strokeOpacity?: number | undefined;
|
|
733
|
+
strokeDasharray?: (string | import("../..").LineStyle) | undefined;
|
|
734
|
+
borderRadius?: number | undefined;
|
|
735
|
+
} | undefined;
|
|
736
|
+
} | undefined;
|
|
649
737
|
scatter?: {
|
|
650
738
|
dotRadius?: number | undefined;
|
|
651
739
|
} | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -39,7 +39,8 @@ export { makeColorScale, normalize, resolveCssColor, paletteColor } from './util
|
|
|
39
39
|
export type { AxisValue, AxisScale, AxisBasedScalesConfig } from './types/layout/scales';
|
|
40
40
|
export type { Accessor, Series, SeriesProps, DataProps } from './types/plots/data/common';
|
|
41
41
|
export type { BasePlotProps, StylesConfig, MarkersConfig, SegmentsConfig, ScalesConfig, MarginConfig, TooltipConfig, HoverConfig, PointMatchingStrategy, } from './types/plots/props';
|
|
42
|
-
export type { ValuesStyle, ColorsStyle, FrameStyle, FrameProp, BasePlotStylesConfig, GeoStylesConfig, GeoTileLayerConfig, } from './types/layout/styles';
|
|
42
|
+
export type { ValuesStyle, ColorsStyle, FrameStyle, FrameProp, BasePlotStylesConfig, AxisBasedStylesConfig, GapsAwarePlotStylesConfig, GeoStylesConfig, GeoTileLayerConfig, } from './types/layout/styles';
|
|
43
|
+
export type { GapsConfig, GapZonesConfig, GapZoneConfig, OpenGapZoneConfig, } from './types/plots/gaps';
|
|
43
44
|
export type { LineSegmentStyle, BarSegmentStyle, CellSegmentStyle, GeoSegmentStyle, ScatterSegmentStyle } from './types/plots/segments/common';
|
|
44
45
|
export type { Range, Area, Segment } from './types/plots/segments/config';
|
|
45
46
|
export type { ValueAnchor, SymbolType, LineStyle, LineCap, LineJoin, FontWeight, FontStyleKeyword, TextAnchor, LineAnchor, } from './types/plots/constants';
|
|
@@ -53,8 +54,8 @@ export type { GeoProjectionName, GeoProjectionConfig, GeoCustomProjection, GeoCu
|
|
|
53
54
|
export type { GeoMarkersConfig, GeoInsetMarkerConfig, GeoInsetTooltipConfig, GeoInsetLocation, GeoInsetProjectionName, GeoInsetProjectionConfig, } from './types/markers/geo';
|
|
54
55
|
export type { ChartPlotContext, ChartPlotSnippet, } from './types/charts/common';
|
|
55
56
|
export type { TimeValue, Orientation, TimelineConfig, } from './types/layout/timeline';
|
|
56
|
-
export type { Responsive, FacetColumns, FacetMode, FacetConfig, FacetIndicatorContext, FacetIndicatorSnippet, FacetNavDirection, FacetNavContext, FacetNavSnippet, } from './types/layout/facet';
|
|
57
|
+
export type { Responsive, FacetColumns, FacetMode, FacetNavLayout, FacetConfig, FacetIndicatorContext, FacetIndicatorSnippet, FacetNavDirection, FacetNavContext, FacetNavSnippet, } from './types/layout/facet';
|
|
57
58
|
export type { LegendSection, DiscreteLegendSection, ContinuousLegendSection, LegendItem, LegendDisabledStyle, LegendInteractionStore, LegendPlotContext, } from './types/layout/legend';
|
|
58
59
|
export type { HoverStrategy, TooltipOptions, TooltipProp, TooltipAnchorX, TooltipAnchorY, } from './types/layout/tooltip';
|
|
59
60
|
export type { ChartConfig, ChartConfigInput, TextStyle } from './types/configuration/styling';
|
|
60
|
-
export type { ChartProps } from './types/charts/props';
|
|
61
|
+
export type { ChartProps, GapsAwareChartProps } from './types/charts/props';
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
coordinates = undefined,
|
|
28
28
|
data,
|
|
29
29
|
containerEl,
|
|
30
|
+
remountKey = undefined,
|
|
30
31
|
children,
|
|
31
32
|
}: {
|
|
32
33
|
width: number;
|
|
@@ -38,6 +39,8 @@
|
|
|
38
39
|
/** Fed to `resolveProjection`'s `domain: 'data'` sentinel — the same rows `BasePlotLayout` uses for hover matching. */
|
|
39
40
|
data: TData[];
|
|
40
41
|
containerEl?: HTMLDivElement;
|
|
42
|
+
/** See `BasePlotLayout`'s own `remountKey` doc comment — forwarded verbatim to key svelteplot's `<Plot>` itself. */
|
|
43
|
+
remountKey?: string | number;
|
|
41
44
|
children: Snippet;
|
|
42
45
|
} = $props();
|
|
43
46
|
|
|
@@ -88,8 +91,10 @@
|
|
|
88
91
|
});
|
|
89
92
|
</script>
|
|
90
93
|
|
|
91
|
-
|
|
92
|
-
<
|
|
93
|
-
{
|
|
94
|
-
|
|
95
|
-
</
|
|
94
|
+
{#key remountKey}
|
|
95
|
+
<Plot {width} {height} {margin} {x} {y} projection={finalProjection as SveltePlotProps['projection']}>
|
|
96
|
+
<g transform="translate({geoZoom.transform.x}, {geoZoom.transform.y}) scale({geoZoom.transform.k})">
|
|
97
|
+
{@render children()}
|
|
98
|
+
</g>
|
|
99
|
+
</Plot>
|
|
100
|
+
{/key}
|
|
@@ -11,6 +11,8 @@ declare function $$render<TData extends Record<string, unknown>>(): {
|
|
|
11
11
|
/** Fed to `resolveProjection`'s `domain: 'data'` sentinel — the same rows `BasePlotLayout` uses for hover matching. */
|
|
12
12
|
data: TData[];
|
|
13
13
|
containerEl?: HTMLDivElement;
|
|
14
|
+
/** See `BasePlotLayout`'s own `remountKey` doc comment — forwarded verbatim to key svelteplot's `<Plot>` itself. */
|
|
15
|
+
remountKey?: string | number;
|
|
14
16
|
children: Snippet;
|
|
15
17
|
};
|
|
16
18
|
exports: {};
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import type {
|
|
6
6
|
FacetColumns,
|
|
7
7
|
FacetMode,
|
|
8
|
+
FacetNavLayout,
|
|
8
9
|
Responsive,
|
|
9
10
|
FacetCellSnippet,
|
|
10
11
|
FacetIndicatorSnippet,
|
|
@@ -32,6 +33,7 @@
|
|
|
32
33
|
mode = 'grid',
|
|
33
34
|
visible = 1,
|
|
34
35
|
pageSize,
|
|
36
|
+
navLayout = 'overlay',
|
|
35
37
|
formatIndicator,
|
|
36
38
|
indicator,
|
|
37
39
|
navButton,
|
|
@@ -47,6 +49,7 @@
|
|
|
47
49
|
mode?: Responsive<FacetMode>;
|
|
48
50
|
visible?: number;
|
|
49
51
|
pageSize?: number;
|
|
52
|
+
navLayout?: FacetNavLayout;
|
|
50
53
|
formatIndicator?: (current: number, total: number) => string;
|
|
51
54
|
indicator?: FacetIndicatorSnippet;
|
|
52
55
|
navButton?: FacetNavSnippet;
|
|
@@ -73,20 +76,16 @@
|
|
|
73
76
|
|
|
74
77
|
const resolvedMode = $derived(resolveResponsive(mode, $isMobile));
|
|
75
78
|
|
|
76
|
-
|
|
79
|
+
// Columns/rows for a "full" grid of every entry — used for plain grid-mode
|
|
80
|
+
// layout, and as the default `pageSize` (one page = one full screen).
|
|
81
|
+
const gridCols = $derived.by(() => {
|
|
77
82
|
const desired = resolveResponsive(columns, $isMobile);
|
|
78
83
|
return Math.max(1, Math.min(desired, entries.length || 1));
|
|
79
84
|
});
|
|
80
|
-
|
|
81
|
-
const rows = $derived(Math.ceil((entries.length || 1) / cols));
|
|
82
|
-
const cellWidth = $derived(Math.max(0, Math.floor((width - (cols - 1) * gap) / cols)));
|
|
83
|
-
const TITLE_H = 24;
|
|
84
|
-
const cellHeight = $derived(
|
|
85
|
-
Math.max(0, Math.floor((height - (rows - 1) * gap) / rows) - TITLE_H),
|
|
86
|
-
);
|
|
85
|
+
const gridRows = $derived(Math.ceil((entries.length || 1) / gridCols));
|
|
87
86
|
|
|
88
87
|
// ── paginated ────────────────────────────────────────────────────────────
|
|
89
|
-
const resolvedPageSize = $derived(Math.max(1, pageSize ??
|
|
88
|
+
const resolvedPageSize = $derived(Math.max(1, pageSize ?? gridCols * gridRows));
|
|
90
89
|
let page = $state(0);
|
|
91
90
|
const totalPages = $derived(Math.max(1, Math.ceil(entries.length / resolvedPageSize)));
|
|
92
91
|
const pageEntries = $derived(
|
|
@@ -104,6 +103,37 @@
|
|
|
104
103
|
page = Math.min(totalPages - 1, page + 1);
|
|
105
104
|
}
|
|
106
105
|
|
|
106
|
+
const showNav = $derived(resolvedMode === 'paginated' && totalPages > 1);
|
|
107
|
+
const NAV_BELOW_H = 44;
|
|
108
|
+
|
|
109
|
+
// Columns/rows actually used to size each rendered cell. `grid` mode always
|
|
110
|
+
// renders every entry, so it sizes off `gridCols`/`gridRows` as before. In
|
|
111
|
+
// `paginated` mode only `pageEntries` (at most `pageSize`, possibly fewer
|
|
112
|
+
// on the last page) are ever on screen at once — sizing off the *total*
|
|
113
|
+
// entry count instead (as `gridCols`/`gridRows` do) left a page with fewer
|
|
114
|
+
// entries than a full grid mostly blank, e.g. `pageSize: 1` still reserved
|
|
115
|
+
// room for every other page's rows instead of filling the chart area with
|
|
116
|
+
// its one cell.
|
|
117
|
+
const cols = $derived(
|
|
118
|
+
resolvedMode === 'paginated' ? Math.max(1, Math.min(gridCols, pageEntries.length || 1)) : gridCols,
|
|
119
|
+
);
|
|
120
|
+
const rows = $derived(
|
|
121
|
+
resolvedMode === 'paginated' ? Math.ceil((pageEntries.length || 1) / cols) : gridRows,
|
|
122
|
+
);
|
|
123
|
+
// In `navLayout: 'below'`, the nav row sits in normal flow beneath the
|
|
124
|
+
// grid rather than floating over it, so the cells must be measured against
|
|
125
|
+
// the height left over after reserving space for that row.
|
|
126
|
+
const contentHeight = $derived(
|
|
127
|
+
resolvedMode === 'paginated' && navLayout === 'below' && showNav
|
|
128
|
+
? Math.max(0, height - NAV_BELOW_H)
|
|
129
|
+
: height,
|
|
130
|
+
);
|
|
131
|
+
const cellWidth = $derived(Math.max(0, Math.floor((width - (cols - 1) * gap) / cols)));
|
|
132
|
+
const TITLE_H = 24;
|
|
133
|
+
const cellHeight = $derived(
|
|
134
|
+
Math.max(0, Math.floor((contentHeight - (rows - 1) * gap) / rows) - TITLE_H),
|
|
135
|
+
);
|
|
136
|
+
|
|
107
137
|
// ── carousel ─────────────────────────────────────────────────────────────
|
|
108
138
|
const visibleCount = $derived(Math.max(1, Math.min(visible, entries.length || 1)));
|
|
109
139
|
const carouselCellWidth = $derived(
|
|
@@ -152,13 +182,17 @@
|
|
|
152
182
|
}
|
|
153
183
|
</script>
|
|
154
184
|
|
|
185
|
+
{#snippet indicatorContent(current: number, total: number)}
|
|
186
|
+
{#if indicator}
|
|
187
|
+
{@render indicator({ current, total })}
|
|
188
|
+
{:else}
|
|
189
|
+
<FacetIndicator {current} {total} format={formatIndicator} />
|
|
190
|
+
{/if}
|
|
191
|
+
{/snippet}
|
|
192
|
+
|
|
155
193
|
{#snippet indicatorSlot(current: number, total: number)}
|
|
156
194
|
<div class="dct-facet-indicator-slot">
|
|
157
|
-
{
|
|
158
|
-
{@render indicator({ current, total })}
|
|
159
|
-
{:else}
|
|
160
|
-
<FacetIndicator {current} {total} format={formatIndicator} />
|
|
161
|
-
{/if}
|
|
195
|
+
{@render indicatorContent(current, total)}
|
|
162
196
|
</div>
|
|
163
197
|
{/snippet}
|
|
164
198
|
|
|
@@ -196,12 +230,20 @@
|
|
|
196
230
|
{#if resolvedMode === 'paginated'}
|
|
197
231
|
<div class="dct-facet-paginated" style:width="{width}px" style:height="{height}px">
|
|
198
232
|
{@render grid(pageEntries)}
|
|
199
|
-
{#if
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
233
|
+
{#if showNav}
|
|
234
|
+
{#if navLayout === 'below'}
|
|
235
|
+
<div class="dct-facet-paginated__nav-below" style:height="{NAV_BELOW_H}px">
|
|
236
|
+
{@render navButtons('prev', page === 0, prevPage, 'Previous page', '‹')}
|
|
237
|
+
{@render indicatorContent(page + 1, totalPages)}
|
|
238
|
+
{@render navButtons('next', page >= totalPages - 1, nextPage, 'Next page', '›')}
|
|
239
|
+
</div>
|
|
240
|
+
{:else}
|
|
241
|
+
<div class="dct-facet-paginated__nav">
|
|
242
|
+
{@render navButtons('prev', page === 0, prevPage, 'Previous page', '‹')}
|
|
243
|
+
{@render navButtons('next', page >= totalPages - 1, nextPage, 'Next page', '›')}
|
|
244
|
+
</div>
|
|
245
|
+
{@render indicatorSlot(page + 1, totalPages)}
|
|
246
|
+
{/if}
|
|
205
247
|
{/if}
|
|
206
248
|
</div>
|
|
207
249
|
{:else if resolvedMode === 'carousel'}
|
|
@@ -248,6 +290,14 @@
|
|
|
248
290
|
display: grid;
|
|
249
291
|
width: 100%;
|
|
250
292
|
height: 100%;
|
|
293
|
+
/* Only takes effect when `.dct-facet` is itself a flex item (inside
|
|
294
|
+
`.dct-facet-paginated`): lets it shrink to the space left over after
|
|
295
|
+
a `flex: 0 0 auto` sibling (the below-layout nav row) claims its
|
|
296
|
+
fixed height, instead of overflowing past the fixed-height parent
|
|
297
|
+
the way a bare `height: 100%` would. No-op outside a flex parent
|
|
298
|
+
(e.g. plain `grid` mode), where `width/height: 100%` alone applies. */
|
|
299
|
+
flex: 1 1 auto;
|
|
300
|
+
min-height: 0;
|
|
251
301
|
}
|
|
252
302
|
.dct-facet__cell {
|
|
253
303
|
display: flex;
|
|
@@ -281,6 +331,13 @@
|
|
|
281
331
|
.dct-facet-nav-slot {
|
|
282
332
|
pointer-events: auto;
|
|
283
333
|
}
|
|
334
|
+
.dct-facet-paginated__nav-below {
|
|
335
|
+
flex: 0 0 auto;
|
|
336
|
+
display: flex;
|
|
337
|
+
align-items: center;
|
|
338
|
+
justify-content: center;
|
|
339
|
+
gap: var(--dct-facet-nav-below-gap, 0.75rem);
|
|
340
|
+
}
|
|
284
341
|
.dct-facet-paginated__btn {
|
|
285
342
|
border: none;
|
|
286
343
|
border-radius: 999px;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FacetColumns, FacetMode, Responsive, FacetCellSnippet, FacetIndicatorSnippet, FacetNavSnippet } from '../../types/layout/facet';
|
|
1
|
+
import type { FacetColumns, FacetMode, FacetNavLayout, Responsive, FacetCellSnippet, FacetIndicatorSnippet, FacetNavSnippet } from '../../types/layout/facet';
|
|
2
2
|
import type { Accessor } from '../../types/plots/data/common';
|
|
3
3
|
declare function $$render<TRow extends Record<string, unknown>>(): {
|
|
4
4
|
props: {
|
|
@@ -10,6 +10,7 @@ declare function $$render<TRow extends Record<string, unknown>>(): {
|
|
|
10
10
|
mode?: Responsive<FacetMode>;
|
|
11
11
|
visible?: number;
|
|
12
12
|
pageSize?: number;
|
|
13
|
+
navLayout?: FacetNavLayout;
|
|
13
14
|
formatIndicator?: (current: number, total: number) => string;
|
|
14
15
|
indicator?: FacetIndicatorSnippet;
|
|
15
16
|
navButton?: FacetNavSnippet;
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
coordinates = undefined,
|
|
48
48
|
margins,
|
|
49
49
|
marginEstimate = AUTO_MARGIN_ESTIMATE,
|
|
50
|
+
marginRemountKey = undefined,
|
|
50
51
|
xTickRotate = undefined,
|
|
51
52
|
styles = undefined,
|
|
52
53
|
markers = undefined,
|
|
@@ -76,6 +77,23 @@
|
|
|
76
77
|
margins?: MarginConfig;
|
|
77
78
|
/** Numeric fallback for a margin side that resolved to `'auto'` — see `numericMargin`'s doc comment. */
|
|
78
79
|
marginEstimate?: Record<'top' | 'right' | 'bottom' | 'left', number>;
|
|
80
|
+
/**
|
|
81
|
+
* Forces svelteplot's own `<Plot>` to fully unmount/remount (via
|
|
82
|
+
* `{#key}`) whenever this changes, instead of reacting to a `margins`
|
|
83
|
+
* prop change on an already-mounted instance. Only needed by a caller
|
|
84
|
+
* (line's own `Plot.svelte`) whose `margins` grows *after* mount, from
|
|
85
|
+
* measuring rendered content (see `createLabelMarginTracker`) — handing
|
|
86
|
+
* svelteplot's `<Plot>` a live post-mount margin change (rather than the
|
|
87
|
+
* same final value from its very first mount) was observed to send its
|
|
88
|
+
* own internal auto-margin sizing into a permanent one-pixel
|
|
89
|
+
* bottom-margin oscillation for line's end-point value labels
|
|
90
|
+
* specifically, which in turn kept re-triggering the overflow watcher
|
|
91
|
+
* that grew the margin in the first place — an infinite effect loop
|
|
92
|
+
* that tripped Svelte's `effect_update_depth_exceeded` guard. A static
|
|
93
|
+
* `margins` prop (the common case — every other plot kind) never needs
|
|
94
|
+
* this; leave it unset there.
|
|
95
|
+
*/
|
|
96
|
+
marginRemountKey?: string | number;
|
|
79
97
|
/** Boosts the bottom margin for a rotated x tick label — axis-having plot kinds only. */
|
|
80
98
|
xTickRotate?: number;
|
|
81
99
|
/** This plot kind's own `styles` prop, forwarded to the Styles layer. */
|
|
@@ -229,6 +247,7 @@
|
|
|
229
247
|
{coordinates}
|
|
230
248
|
{data}
|
|
231
249
|
{containerEl}
|
|
250
|
+
remountKey={marginRemountKey}
|
|
232
251
|
>
|
|
233
252
|
<StylesLayout {styles} {width} {height} {numericMargins} />
|
|
234
253
|
{#if scales !== undefined}
|
|
@@ -30,6 +30,23 @@ declare function $$render<TData extends Record<string, unknown>, TSeries extends
|
|
|
30
30
|
margins?: MarginConfig;
|
|
31
31
|
/** Numeric fallback for a margin side that resolved to `'auto'` — see `numericMargin`'s doc comment. */
|
|
32
32
|
marginEstimate?: Record<"top" | "right" | "bottom" | "left", number>;
|
|
33
|
+
/**
|
|
34
|
+
* Forces svelteplot's own `<Plot>` to fully unmount/remount (via
|
|
35
|
+
* `{#key}`) whenever this changes, instead of reacting to a `margins`
|
|
36
|
+
* prop change on an already-mounted instance. Only needed by a caller
|
|
37
|
+
* (line's own `Plot.svelte`) whose `margins` grows *after* mount, from
|
|
38
|
+
* measuring rendered content (see `createLabelMarginTracker`) — handing
|
|
39
|
+
* svelteplot's `<Plot>` a live post-mount margin change (rather than the
|
|
40
|
+
* same final value from its very first mount) was observed to send its
|
|
41
|
+
* own internal auto-margin sizing into a permanent one-pixel
|
|
42
|
+
* bottom-margin oscillation for line's end-point value labels
|
|
43
|
+
* specifically, which in turn kept re-triggering the overflow watcher
|
|
44
|
+
* that grew the margin in the first place — an infinite effect loop
|
|
45
|
+
* that tripped Svelte's `effect_update_depth_exceeded` guard. A static
|
|
46
|
+
* `margins` prop (the common case — every other plot kind) never needs
|
|
47
|
+
* this; leave it unset there.
|
|
48
|
+
*/
|
|
49
|
+
marginRemountKey?: string | number;
|
|
33
50
|
/** Boosts the bottom margin for a rotated x tick label — axis-having plot kinds only. */
|
|
34
51
|
xTickRotate?: number;
|
|
35
52
|
/** This plot kind's own `styles` prop, forwarded to the Styles layer. */
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { createLabelMarginTracker } from '../utils/labelOverflow.svelte';
|
|
14
14
|
import { buildHoverPoints } from './hoverPoints';
|
|
15
15
|
import { buildBarMarkers } from './buildBarMarkers';
|
|
16
|
+
import { resolveGapsForSeries, resolveDiscreteGapFills } from '../utils/gaps';
|
|
16
17
|
import ValueLabels from './ValueLabels.svelte';
|
|
17
18
|
import BasePlotLayout from '../../layout/plot/BasePlotLayout.svelte';
|
|
18
19
|
import { hasHoverMarker, resolveHoverStrategy, resolveHoverSync } from '../../layout/tooltip/utils';
|
|
@@ -20,7 +21,7 @@
|
|
|
20
21
|
import type { BasePlotProps } from '../../types/plots/props';
|
|
21
22
|
import type { AxisScale, AxisBasedScalesConfig } from '../../types/layout/scales';
|
|
22
23
|
import type { Series, SeriesProps, DataProps } from '../../types/plots/data/common';
|
|
23
|
-
import type {
|
|
24
|
+
import type { GapsAwarePlotStylesConfig } from '../../types/layout/styles';
|
|
24
25
|
import type { ValueAnchor } from '../../types/plots/constants';
|
|
25
26
|
import type { BarSegmentStyle } from '../../types/plots/segments/common';
|
|
26
27
|
import type { LegendDisabledStyle } from '../../types/layout/legend';
|
|
@@ -33,7 +34,7 @@
|
|
|
33
34
|
type Props = BasePlotProps<
|
|
34
35
|
SeriesProps<TData> | DataProps<TData>,
|
|
35
36
|
TData,
|
|
36
|
-
|
|
37
|
+
GapsAwarePlotStylesConfig<BarSegmentStyle>,
|
|
37
38
|
BarSegmentStyle,
|
|
38
39
|
AxisBasedMarkersConfig,
|
|
39
40
|
AxisBasedScalesConfig<TData>
|
|
@@ -68,6 +69,14 @@
|
|
|
68
69
|
const formatValue = $derived(styles.values?.format ?? ((v: number) => `${v}`));
|
|
69
70
|
const valAnchor = $derived<ValueAnchor>(styles.values?.anchor ?? 'outside');
|
|
70
71
|
|
|
72
|
+
// Fills leading/internal/trailing missing-data holes with an
|
|
73
|
+
// interpolated, distinctly-styled bar, resolved per series like
|
|
74
|
+
// `segments` — see `GapsAwarePlotStylesConfig` (`types/layout/styles.ts`).
|
|
75
|
+
const gaps = $derived(styles.gaps ?? {});
|
|
76
|
+
const gapFillMaps = $derived(
|
|
77
|
+
new Map(resolvedSeries.map((s) => [s.name, resolveDiscreteGapFills(s, resolveGapsForSeries(gaps, s.name))])),
|
|
78
|
+
);
|
|
79
|
+
|
|
71
80
|
// `xAcc`/`yAcc` are always data-semantic (category/value) — independent of
|
|
72
81
|
// which *visual* axis ends up drawing them, decided by `categoricalAxis`.
|
|
73
82
|
const xAcc = $derived(resolveAccessor(resolvedSeries[0]?.x ?? ((d: TData) => d)));
|
|
@@ -148,6 +157,7 @@
|
|
|
148
157
|
flat: () => flat,
|
|
149
158
|
xAcc: () => xAcc,
|
|
150
159
|
groupedSeries: () => groupedSeries,
|
|
160
|
+
gapFillMaps: () => gapFillMaps,
|
|
151
161
|
});
|
|
152
162
|
|
|
153
163
|
// `x`/`y` here are visual (matched against BasePlotLayout's own getX/getY,
|
|
@@ -228,6 +238,7 @@
|
|
|
228
238
|
active: hover.active,
|
|
229
239
|
setHoverMatch,
|
|
230
240
|
clearHoverMatch,
|
|
241
|
+
gapFillMaps,
|
|
231
242
|
}),
|
|
232
243
|
...markers,
|
|
233
244
|
]}
|
|
@@ -2,9 +2,9 @@ import type { AxisBasedMarkersConfig } from '../../types/markers/common';
|
|
|
2
2
|
import type { BasePlotProps } from '../../types/plots/props';
|
|
3
3
|
import type { AxisBasedScalesConfig } from '../../types/layout/scales';
|
|
4
4
|
import type { SeriesProps, DataProps } from '../../types/plots/data/common';
|
|
5
|
-
import type {
|
|
5
|
+
import type { GapsAwarePlotStylesConfig } from '../../types/layout/styles';
|
|
6
6
|
declare function $$render<TData extends Record<string, unknown>>(): {
|
|
7
|
-
props: BasePlotProps<SeriesProps<TData> | DataProps<TData>, TData,
|
|
7
|
+
props: BasePlotProps<SeriesProps<TData> | DataProps<TData>, TData, GapsAwarePlotStylesConfig<import("../..").AreaStyle>, import("../..").AreaStyle, AxisBasedMarkersConfig, AxisBasedScalesConfig<TData>>;
|
|
8
8
|
exports: {};
|
|
9
9
|
bindings: "";
|
|
10
10
|
slots: {};
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { resolveAccessor } from '../utils/accessors';
|
|
4
4
|
import { disabledFillOverride } from '../utils/legendDisabled';
|
|
5
5
|
import { watchLabelOverflow } from '../utils/labelOverflow.svelte';
|
|
6
|
+
import { isGapValue } from '../utils/gaps';
|
|
6
7
|
import type { Series } from '../../types/plots/data/common';
|
|
7
8
|
import type { ValuesStyle } from '../../types/layout/styles';
|
|
8
9
|
import type { ValueAnchor } from '../../types/plots/constants';
|
|
@@ -77,6 +78,9 @@
|
|
|
77
78
|
|
|
78
79
|
const xFn = $derived(resolveAccessor(group.series.x));
|
|
79
80
|
const yFn = $derived(resolveAccessor(group.series.y));
|
|
81
|
+
// A gap row — missing data, whether or not `styles.gaps` fills it with a
|
|
82
|
+
// synthetic bar — never gets a value label, same as LinePlot's `lastPoints`.
|
|
83
|
+
const labelData = $derived(group.series.data.filter((d) => !isGapValue(yFn(d))));
|
|
80
84
|
|
|
81
85
|
const valueTextFn = $derived(
|
|
82
86
|
barLayout.layout === 'stacked'
|
|
@@ -113,7 +117,7 @@
|
|
|
113
117
|
boundary crossing instead (pointerenter/leave fire on every ancestor). -->
|
|
114
118
|
<g bind:this={groupEl} role="presentation" onpointerenter={onHoverSeries} onpointerleave={onHoverEnd}>
|
|
115
119
|
<Text
|
|
116
|
-
data={
|
|
120
|
+
data={labelData}
|
|
117
121
|
{...isHorizontal ? { y: xFn, x: valueTextFn } : { x: xFn, y: valueTextFn }}
|
|
118
122
|
text={(d: TData) => formatValue(Number(yFn(d)), group.series.name)}
|
|
119
123
|
dx={font?.dx ?? (isHorizontal ? defaultOffset : groupOffset.dx)}
|