@fundar/data-chart-telling 0.0.28 → 0.0.30

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.
Files changed (49) hide show
  1. package/dist/charts/bar/Chart.svelte +2 -2
  2. package/dist/charts/bar/Chart.svelte.d.ts +2 -2
  3. package/dist/charts/line/Chart.svelte +2 -2
  4. package/dist/charts/line/Chart.svelte.d.ts +2 -2
  5. package/dist/charts/pyramid/Chart.svelte +2 -2
  6. package/dist/charts/pyramid/Chart.svelte.d.ts +3 -2
  7. package/dist/configuration/config.svelte.js +6 -0
  8. package/dist/configuration/themes/index.d.ts +168 -0
  9. package/dist/index.d.ts +4 -3
  10. package/dist/layout/coordinates/CoordinatesLayout.svelte +10 -5
  11. package/dist/layout/coordinates/CoordinatesLayout.svelte.d.ts +2 -0
  12. package/dist/layout/facet/FacetLayout.svelte +77 -20
  13. package/dist/layout/facet/FacetLayout.svelte.d.ts +2 -1
  14. package/dist/layout/plot/BasePlotLayout.svelte +19 -0
  15. package/dist/layout/plot/BasePlotLayout.svelte.d.ts +17 -0
  16. package/dist/plots/bar/Plot.svelte +13 -2
  17. package/dist/plots/bar/Plot.svelte.d.ts +2 -2
  18. package/dist/plots/bar/ValueLabels.svelte +5 -1
  19. package/dist/plots/bar/buildBarMarkers.d.ts +10 -3
  20. package/dist/plots/bar/buildBarMarkers.js +86 -55
  21. package/dist/plots/bar/hoverPoints.js +2 -1
  22. package/dist/plots/bar/layout.svelte.d.ts +2 -0
  23. package/dist/plots/bar/layout.svelte.js +6 -1
  24. package/dist/plots/heatmap/Plot.svelte +2 -2
  25. package/dist/plots/heatmap/Plot.svelte.d.ts +2 -2
  26. package/dist/plots/line/Plot.svelte +39 -15
  27. package/dist/plots/line/Plot.svelte.d.ts +2 -2
  28. package/dist/plots/line/ValueLabels.svelte +17 -2
  29. package/dist/plots/line/buildGapMarkers.d.ts +21 -0
  30. package/dist/plots/line/buildGapMarkers.js +68 -0
  31. package/dist/plots/pyramid/Plot.svelte +39 -14
  32. package/dist/plots/pyramid/Plot.svelte.d.ts +2 -2
  33. package/dist/plots/pyramid/ValueLabels.svelte +5 -1
  34. package/dist/plots/pyramid/buildPyramidBarMarkers.d.ts +8 -1
  35. package/dist/plots/pyramid/buildPyramidBarMarkers.js +43 -3
  36. package/dist/plots/scatter/Plot.svelte +2 -2
  37. package/dist/plots/scatter/Plot.svelte.d.ts +2 -2
  38. package/dist/plots/utils/gaps.d.ts +52 -0
  39. package/dist/plots/utils/gaps.js +156 -0
  40. package/dist/plots/utils/labelOverflow.svelte.d.ts +9 -0
  41. package/dist/plots/utils/labelOverflow.svelte.js +11 -0
  42. package/dist/types/charts/props.d.ts +11 -2
  43. package/dist/types/configuration/styling.d.ts +19 -0
  44. package/dist/types/layout/facet.d.ts +2 -0
  45. package/dist/types/layout/styles.d.ts +29 -11
  46. package/dist/types/plots/gaps.d.ts +54 -0
  47. package/dist/types/plots/gaps.js +1 -0
  48. package/dist/types/plots/props.d.ts +1 -1
  49. 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 { ChartProps } from '../../types/charts/props';
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
- }: ChartProps<TRow, BarSegmentStyle> = $props();
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 { ChartProps } from '../../types/charts/props';
2
+ import type { GapsAwareChartProps } from '../../types/charts/props';
3
3
  declare function $$render<TRow extends Record<string, unknown>>(): {
4
- props: ChartProps<TRow, BarSegmentStyle>;
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 { ChartProps } from '../../types/charts/props';
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
- }: ChartProps<TRow, LineSegmentStyle> = $props();
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 { ChartProps } from '../../types/charts/props';
2
+ import type { GapsAwareChartProps } from '../../types/charts/props';
3
3
  declare function $$render<TRow extends Record<string, unknown>>(): {
4
- props: ChartProps<TRow, LineSegmentStyle>;
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 { ChartProps } from '../../types/charts/props';
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
- }: ChartProps<TRow, BarSegmentStyle> & { x: Accessor<TRow, number>; z: Accessor<TRow, unknown> } = $props();
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
  };
@@ -73,6 +73,10 @@ export const DEFAULT_CONFIG = {
73
73
  line: {
74
74
  strokeWidth: 3,
75
75
  dotRadius: 4,
76
+ gap: {
77
+ stroke: { strokeOpacity: 0.6, strokeDasharray: '4 4' },
78
+ dots: {},
79
+ },
76
80
  valueLabels: {
77
81
  elbowGap: 10,
78
82
  laneStep: 7,
@@ -81,6 +85,8 @@ export const DEFAULT_CONFIG = {
81
85
  connectorWidth: 0.5,
82
86
  },
83
87
  },
88
+ bar: { gap: { fillOpacity: 0.5 } },
89
+ pyramid: { gap: { fillOpacity: 0.5 } },
84
90
  scatter: { dotRadius: 5 },
85
91
  timeline: { axisColor: 'currentColor', activeColor: '#e6580d', thickness: 60 },
86
92
  hover: {
@@ -84,6 +84,26 @@ export declare const THEMES: {
84
84
  line?: {
85
85
  strokeWidth?: number | undefined;
86
86
  dotRadius?: number | undefined;
87
+ gap?: {
88
+ stroke?: {
89
+ stroke?: string | undefined;
90
+ strokeWidth?: number | undefined;
91
+ strokeOpacity?: number | undefined;
92
+ strokeDasharray?: (string | import("../..").LineStyle) | undefined;
93
+ strokeLinecap?: import("../..").LineCap | undefined;
94
+ strokeLinejoin?: import("../..").LineJoin | undefined;
95
+ } | undefined;
96
+ dots?: {
97
+ dotRadius?: number | undefined;
98
+ dotFill?: string | undefined;
99
+ dotFillOpacity?: number | undefined;
100
+ dotSymbol?: import("../..").SymbolType | undefined;
101
+ dotStroke?: string | undefined;
102
+ dotStrokeWidth?: number | undefined;
103
+ dotStrokeOpacity?: number | undefined;
104
+ dotStrokeDasharray?: (string | import("../..").LineStyle) | undefined;
105
+ } | undefined;
106
+ } | undefined;
87
107
  valueLabels?: {
88
108
  elbowGap?: number | undefined;
89
109
  laneStep?: number | undefined;
@@ -92,6 +112,28 @@ export declare const THEMES: {
92
112
  connectorWidth?: number | undefined;
93
113
  } | undefined;
94
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;
95
137
  scatter?: {
96
138
  dotRadius?: number | undefined;
97
139
  } | undefined;
@@ -242,6 +284,26 @@ export declare const THEMES: {
242
284
  line?: {
243
285
  strokeWidth?: number | undefined;
244
286
  dotRadius?: number | undefined;
287
+ gap?: {
288
+ stroke?: {
289
+ stroke?: string | undefined;
290
+ strokeWidth?: number | undefined;
291
+ strokeOpacity?: number | undefined;
292
+ strokeDasharray?: (string | import("../..").LineStyle) | undefined;
293
+ strokeLinecap?: import("../..").LineCap | undefined;
294
+ strokeLinejoin?: import("../..").LineJoin | undefined;
295
+ } | undefined;
296
+ dots?: {
297
+ dotRadius?: number | undefined;
298
+ dotFill?: string | undefined;
299
+ dotFillOpacity?: number | undefined;
300
+ dotSymbol?: import("../..").SymbolType | undefined;
301
+ dotStroke?: string | undefined;
302
+ dotStrokeWidth?: number | undefined;
303
+ dotStrokeOpacity?: number | undefined;
304
+ dotStrokeDasharray?: (string | import("../..").LineStyle) | undefined;
305
+ } | undefined;
306
+ } | undefined;
245
307
  valueLabels?: {
246
308
  elbowGap?: number | undefined;
247
309
  laneStep?: number | undefined;
@@ -250,6 +312,28 @@ export declare const THEMES: {
250
312
  connectorWidth?: number | undefined;
251
313
  } | undefined;
252
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;
253
337
  scatter?: {
254
338
  dotRadius?: number | undefined;
255
339
  } | undefined;
@@ -400,6 +484,26 @@ export declare const THEMES: {
400
484
  line?: {
401
485
  strokeWidth?: number | undefined;
402
486
  dotRadius?: number | undefined;
487
+ gap?: {
488
+ stroke?: {
489
+ stroke?: string | undefined;
490
+ strokeWidth?: number | undefined;
491
+ strokeOpacity?: number | undefined;
492
+ strokeDasharray?: (string | import("../..").LineStyle) | undefined;
493
+ strokeLinecap?: import("../..").LineCap | undefined;
494
+ strokeLinejoin?: import("../..").LineJoin | undefined;
495
+ } | undefined;
496
+ dots?: {
497
+ dotRadius?: number | undefined;
498
+ dotFill?: string | undefined;
499
+ dotFillOpacity?: number | undefined;
500
+ dotSymbol?: import("../..").SymbolType | undefined;
501
+ dotStroke?: string | undefined;
502
+ dotStrokeWidth?: number | undefined;
503
+ dotStrokeOpacity?: number | undefined;
504
+ dotStrokeDasharray?: (string | import("../..").LineStyle) | undefined;
505
+ } | undefined;
506
+ } | undefined;
403
507
  valueLabels?: {
404
508
  elbowGap?: number | undefined;
405
509
  laneStep?: number | undefined;
@@ -408,6 +512,28 @@ export declare const THEMES: {
408
512
  connectorWidth?: number | undefined;
409
513
  } | undefined;
410
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;
411
537
  scatter?: {
412
538
  dotRadius?: number | undefined;
413
539
  } | undefined;
@@ -558,6 +684,26 @@ export declare const THEMES: {
558
684
  line?: {
559
685
  strokeWidth?: number | undefined;
560
686
  dotRadius?: number | undefined;
687
+ gap?: {
688
+ stroke?: {
689
+ stroke?: string | undefined;
690
+ strokeWidth?: number | undefined;
691
+ strokeOpacity?: number | undefined;
692
+ strokeDasharray?: (string | import("../..").LineStyle) | undefined;
693
+ strokeLinecap?: import("../..").LineCap | undefined;
694
+ strokeLinejoin?: import("../..").LineJoin | undefined;
695
+ } | undefined;
696
+ dots?: {
697
+ dotRadius?: number | undefined;
698
+ dotFill?: string | undefined;
699
+ dotFillOpacity?: number | undefined;
700
+ dotSymbol?: import("../..").SymbolType | undefined;
701
+ dotStroke?: string | undefined;
702
+ dotStrokeWidth?: number | undefined;
703
+ dotStrokeOpacity?: number | undefined;
704
+ dotStrokeDasharray?: (string | import("../..").LineStyle) | undefined;
705
+ } | undefined;
706
+ } | undefined;
561
707
  valueLabels?: {
562
708
  elbowGap?: number | undefined;
563
709
  laneStep?: number | undefined;
@@ -566,6 +712,28 @@ export declare const THEMES: {
566
712
  connectorWidth?: number | undefined;
567
713
  } | undefined;
568
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;
569
737
  scatter?: {
570
738
  dotRadius?: number | undefined;
571
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
- <Plot {width} {height} {margin} {x} {y} projection={finalProjection as SveltePlotProps['projection']}>
92
- <g transform="translate({geoZoom.transform.x}, {geoZoom.transform.y}) scale({geoZoom.transform.k})">
93
- {@render children()}
94
- </g>
95
- </Plot>
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
- const cols = $derived.by(() => {
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 ?? cols * rows));
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
- {#if indicator}
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 totalPages > 1}
200
- <div class="dct-facet-paginated__nav">
201
- {@render navButtons('prev', page === 0, prevPage, 'Previous page', '‹')}
202
- {@render navButtons('next', page >= totalPages - 1, nextPage, 'Next page', '')}
203
- </div>
204
- {@render indicatorSlot(page + 1, totalPages)}
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. */