@fundar/data-chart-telling 0.0.60 → 0.0.62
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/README.md
CHANGED
|
@@ -399,6 +399,193 @@ Omitting `segments` (or passing `[]`) renders every mark in its default palette
|
|
|
399
399
|
|
|
400
400
|
---
|
|
401
401
|
|
|
402
|
+
## Theming & configuration
|
|
403
|
+
|
|
404
|
+
The whole package reads its default styling — fonts, colours, spacing, legend/tooltip/axis chrome, plot geometry — from one global, reactive config store (`src/lib/configuration/config.svelte.ts`). Overriding it re-themes every chart on screen; nothing needs to be passed prop-by-prop to each component.
|
|
405
|
+
|
|
406
|
+
```ts
|
|
407
|
+
import { setConfiguration, getConfiguration, resetConfiguration } from '@fundar/data-chart-telling';
|
|
408
|
+
|
|
409
|
+
setConfiguration({
|
|
410
|
+
font: '"Inter", sans-serif',
|
|
411
|
+
palette: ['#4f46e5', '#e6580d', '#16a34a'],
|
|
412
|
+
legend: { continuous: { label: { size: '0.8125rem', weight: 400 } } }
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
getConfiguration(); // read the live, merged config
|
|
416
|
+
resetConfiguration(); // restore shipped defaults
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
- `setConfiguration` takes a **deep-partial** patch (`ChartConfigInput`) and deep-merges it into the current config — you only specify the leaves you want to change, everything else keeps its previous value. It's reactive (`$state`-backed), so applying it re-renders every mounted chart.
|
|
420
|
+
- Call it once at app startup (or per-theme-switch); it's global, not per-chart. A single chart can still override specific visuals via its own props (`styles`, `scales`, a section's own `format`/`color`, etc.) — those always win over the global config.
|
|
421
|
+
|
|
422
|
+
### How values reach the DOM
|
|
423
|
+
|
|
424
|
+
The config feeds two different consumers, and a field is **one or the other**, never both:
|
|
425
|
+
|
|
426
|
+
1. **CSS custom properties** — `configToCssVars` flattens the relevant fields into `--dct-*` variables, which `ChartContainer` stamps on its own root element; every component's scoped `<style>` block reads them with a hardcoded fallback (e.g. `font-size: var(--dct-continuous-label-size, 0.875rem)`). These are the fields listed with a **CSS variable** below — you can also override them directly with plain CSS on a wrapping element instead of going through `setConfiguration`, if you only need a one-off, non-reactive tweak.
|
|
427
|
+
2. **Plain JS values** — read directly by plot components as props svelteplot needs (numeric tick sizes, the categorical `palette` array, margins, gradient endpoints, hover geometry, per-mark defaults). These have **no** CSS variable — `setConfiguration` (or editing `DEFAULT_CONFIG`) is the only way to change them.
|
|
428
|
+
|
|
429
|
+
### Reference
|
|
430
|
+
|
|
431
|
+
All types live in `src/lib/types/configuration/styling.ts`; all defaults in `src/lib/configuration/config.svelte.ts`. "CSS variable" is `—` for JS-only fields.
|
|
432
|
+
|
|
433
|
+
#### Base
|
|
434
|
+
|
|
435
|
+
| Field | Type | Default | CSS variable |
|
|
436
|
+
| ----- | ---- | ------- | ------------- |
|
|
437
|
+
| `font` | `string` | `'inherit'` | `--dct-font` |
|
|
438
|
+
| `color` | `string` | `'currentColor'` | `--dct-fg` |
|
|
439
|
+
| `background` | `string` | `'transparent'` | `--dct-bg` |
|
|
440
|
+
| `border` | `string` | `'color-mix(in srgb, currentColor 15%, transparent)'` | `--dct-border` |
|
|
441
|
+
|
|
442
|
+
#### Spacing
|
|
443
|
+
|
|
444
|
+
| Field | Type | Default | CSS variable |
|
|
445
|
+
| ----- | ---- | ------- | ------------- |
|
|
446
|
+
| `spacing.headerPadding` | `string` | `'0.75rem 0 1rem'` | `--dct-header-pad` |
|
|
447
|
+
| `spacing.legendPadding` | `string` | `'0.75rem 0'` | `--dct-legend-pad` |
|
|
448
|
+
| `spacing.captionPadding` | `string` | `'0.5rem 0'` | `--dct-caption-pad` |
|
|
449
|
+
| `spacing.bleed` | `string` | `'12px'` | `--dct-bleed` |
|
|
450
|
+
|
|
451
|
+
#### Text blocks: `title`, `subtitle`, `caption`
|
|
452
|
+
|
|
453
|
+
Each is a `TextStyle` — `{ font, size, weight, color, leading, spacing }` — mapped to `--dct-{title|subtitle|caption}-{font|size|weight|color|leading|spacing}`. Shared base before per-block overrides: `font: 'inherit'`, `size: '1rem'`, `weight: 400`, `color: 'inherit'`, `leading: '1.3'`, `spacing: 'normal'`.
|
|
454
|
+
|
|
455
|
+
| Block | Overrides its own default |
|
|
456
|
+
| ----- | -------------------------- |
|
|
457
|
+
| `title` | `size: '1.5rem'`, `weight: 500`, `leading: '1.2'` |
|
|
458
|
+
| `subtitle` | `size: '1rem'`, `weight: 400`, `color: 'color-mix(in srgb, currentColor 85%, transparent)'` |
|
|
459
|
+
| `caption` | `size: '0.75rem'`, `color: 'color-mix(in srgb, currentColor 75%, transparent)'` |
|
|
460
|
+
|
|
461
|
+
#### Legend
|
|
462
|
+
|
|
463
|
+
| Field | Type | Default | CSS variable |
|
|
464
|
+
| ----- | ---- | ------- | ------------- |
|
|
465
|
+
| `legend.font` | `string` | `'inherit'` | `--dct-legend-font` |
|
|
466
|
+
| `legend.size` | `string` | `'0.875rem'` | `--dct-legend-size` |
|
|
467
|
+
| `legend.weight` | `number \| string` | `400` | `--dct-legend-weight` |
|
|
468
|
+
| `legend.color` | `string` | `'inherit'` | `--dct-legend-color` |
|
|
469
|
+
| `legend.titleSize` | `string` | `'0.875rem'` | `--dct-legend-title-size` |
|
|
470
|
+
| `legend.titleWeight` | `number \| string` | `500` | `--dct-legend-title-weight` |
|
|
471
|
+
| `legend.gap` | `string` (CSS `gap` shorthand) | `'0.5rem 2rem'` | `--dct-legend-gap` |
|
|
472
|
+
| `legend.swatchSize` | `string` | `'1.25rem'` | `--dct-legend-swatch` |
|
|
473
|
+
| `legend.disabledOpacity` | `number` | `0.12` | — (fallback for a legend-disabled series/value when a section doesn't set its own style) |
|
|
474
|
+
|
|
475
|
+
`legend.navButton` — prev/next arrows on an overflowing horizontal discrete section:
|
|
476
|
+
|
|
477
|
+
| Field | Type | Default | CSS variable |
|
|
478
|
+
| ----- | ---- | ------- | ------------- |
|
|
479
|
+
| `legend.navButton.background` | `string` | `'transparent'` | `--dct-legend-nav-bg` |
|
|
480
|
+
| `legend.navButton.color` | `string` | `'currentColor'` | `--dct-legend-nav-color` |
|
|
481
|
+
| `legend.navButton.size` | `string` | `'1.5rem'` | `--dct-legend-nav-size` |
|
|
482
|
+
| `legend.navButton.fontSize` | `string` | `'1rem'` | `--dct-legend-nav-font-size` |
|
|
483
|
+
| `legend.navButton.disabledOpacity` | `number` | `0.3` | `--dct-legend-nav-disabled-opacity` |
|
|
484
|
+
|
|
485
|
+
`legend.continuous` — the gradient bar's draggable handles, ticks, and labels:
|
|
486
|
+
|
|
487
|
+
| Field | Type | Default | CSS variable |
|
|
488
|
+
| ----- | ---- | ------- | ------------- |
|
|
489
|
+
| `legend.continuous.handle.width` | `string` | `'6px'` | `--dct-continuous-handle-width` |
|
|
490
|
+
| `legend.continuous.handle.height` | `string` | `'20px'` | `--dct-continuous-handle-height` |
|
|
491
|
+
| `legend.continuous.handle.radius` | `string` | `'3px'` | `--dct-continuous-handle-radius` |
|
|
492
|
+
| `legend.continuous.handle.fill` | `string` | `'currentColor'` | `--dct-continuous-handle-fill` |
|
|
493
|
+
| `legend.continuous.handle.stroke` | `string` | `'var(--dct-bg, #fff)'` | `--dct-continuous-handle-stroke` |
|
|
494
|
+
| `legend.continuous.handle.strokeWidth` | `string` | `'1.5px'` | `--dct-continuous-handle-stroke-width` |
|
|
495
|
+
| `legend.continuous.tick.color` | `string` | `'currentColor'` | `--dct-continuous-tick-color` |
|
|
496
|
+
| `legend.continuous.tick.width` | `string` | `'1px'` | `--dct-continuous-tick-width` |
|
|
497
|
+
| `legend.continuous.tick.length` | `string` | `'6px'` | `--dct-continuous-tick-length` |
|
|
498
|
+
| `legend.continuous.label.rotate` | `string` | `'0deg'` | `--dct-continuous-label-rotate` |
|
|
499
|
+
| `legend.continuous.label.size` | `string` | `'0.875rem'` | `--dct-continuous-label-size` |
|
|
500
|
+
| `legend.continuous.label.weight` | `number \| string` | `600` | `--dct-continuous-label-weight` |
|
|
501
|
+
|
|
502
|
+
`legend.continuous.label.overlap` — interactive mode only: once dragging brings the lo/hi handle labels too close to read side by side, both flip to read top-to-bottom instead of clipping or overlapping:
|
|
503
|
+
|
|
504
|
+
| Field | Type | Default | CSS variable |
|
|
505
|
+
| ----- | ---- | ------- | ------------- |
|
|
506
|
+
| `legend.continuous.label.overlap.enabled` | `boolean` | `true` | — (`false` always keeps both labels horizontal, however close together) |
|
|
507
|
+
| `legend.continuous.label.overlap.margin` | `number` (px) | `4` | — (horizontal slack the two labels need before they're considered overlapping) |
|
|
508
|
+
| `legend.continuous.label.overlap.rotate` | `string` | `'90deg'` | `--dct-continuous-label-overlap-rotate` (lo rotates by this; hi by its negation, via `calc(... * -1)`, so both still grow inward toward the bar) |
|
|
509
|
+
|
|
510
|
+
#### Axis
|
|
511
|
+
|
|
512
|
+
Consumed as plain numeric/boolean props by the underlying svelteplot axes — only `color` is also exposed as a CSS variable (used by non-plot axis-adjacent chrome).
|
|
513
|
+
|
|
514
|
+
| Field | Type | Default | CSS variable |
|
|
515
|
+
| ----- | ---- | ------- | ------------- |
|
|
516
|
+
| `axis.color` | `string` | `'currentColor'` | `--dct-axis-color` |
|
|
517
|
+
| `axis.fontSize` | `number` | `14` | — |
|
|
518
|
+
| `axis.labelFontSize` | `number` | `14` | — |
|
|
519
|
+
| `axis.titleFontSize` | `number` | `14` | — |
|
|
520
|
+
| `axis.xTickSize` | `number` | `5` | — |
|
|
521
|
+
| `axis.yTickSize` | `number` | `5` | — |
|
|
522
|
+
| `axis.xTickPadding` | `number` | `10` | — |
|
|
523
|
+
| `axis.yTickPadding` | `number` | `10` | — |
|
|
524
|
+
| `axis.grid` | `boolean` | `false` | — |
|
|
525
|
+
|
|
526
|
+
#### Tooltip
|
|
527
|
+
|
|
528
|
+
| Field | Type | Default | CSS variable |
|
|
529
|
+
| ----- | ---- | ------- | ------------- |
|
|
530
|
+
| `tooltip.background` | `string` | `'#111827'` | `--dct-tooltip-bg` |
|
|
531
|
+
| `tooltip.color` | `string` | `'#ffffff'` | `--dct-tooltip-color` |
|
|
532
|
+
| `tooltip.fontSize` | `string` | `'0.8125rem'` | `--dct-tooltip-size` |
|
|
533
|
+
| `tooltip.padding` | `string` | `'0.5rem 0.75rem'` | `--dct-tooltip-pad` |
|
|
534
|
+
| `tooltip.radius` | `string` | `'0.375rem'` | `--dct-tooltip-radius` |
|
|
535
|
+
| `tooltip.border` | `string` | `'none'` | `--dct-tooltip-border` |
|
|
536
|
+
|
|
537
|
+
#### Plot geometry & colour (all JS-only — no CSS variables)
|
|
538
|
+
|
|
539
|
+
| Field | Type | Default |
|
|
540
|
+
| ----- | ---- | ------- |
|
|
541
|
+
| `margins.top/right/bottom/left` | `number \| 'auto'` | `'auto'` each — lets svelteplot size that side to fit whatever it actually renders there; a number pins it exactly |
|
|
542
|
+
| `palette` | `string[]` | `['#4f46e5', '#e6580d', '#16a34a', '#dc2626', '#0891b2', '#9333ea', '#ca8a04', '#db2777']` — categorical series colours |
|
|
543
|
+
| `continuous.min` / `continuous.max` | `string` | `'#e0e7ff'` / `'#4f46e5'` — heatmap/choropleth gradient endpoints |
|
|
544
|
+
| `continuous.labelColor` | `string` | `'#111827'` — on-cell label colour |
|
|
545
|
+
| `line.strokeWidth` | `number` | `3` |
|
|
546
|
+
| `line.dotRadius` | `number` | `4` |
|
|
547
|
+
| `line.gap.stroke` / `line.gap.dots` | `StrokeStyle` / `DotStyle` | `{ strokeOpacity: 0.6, strokeDasharray: '4 4' }` / `{}` — fallback for `LinePlot`'s `styles.gaps` |
|
|
548
|
+
| `line.valueLabels.elbowGap` | `number` | `10` — shortest leader-line lane distance before it bends |
|
|
549
|
+
| `line.valueLabels.laneStep` | `number` | `7` — extra distance each subsequent lane sits out |
|
|
550
|
+
| `line.valueLabels.labelGap` | `number` | `6` — clearance between the outermost lane and its label |
|
|
551
|
+
| `line.valueLabels.connectorColor` | `string` | `'#94a3b8'` |
|
|
552
|
+
| `line.valueLabels.connectorWidth` | `number` | `0.5` |
|
|
553
|
+
| `bar.gap` | `AreaStyle` | `{ fillOpacity: 0.5 }` — fallback for `BarPlot`'s `styles.gaps` |
|
|
554
|
+
| `bar.seriesPadding` | `number` (0–1) | `0.2` — fallback `scales.z.seriesPadding` for grouped series |
|
|
555
|
+
| `bar.paddingInner` | `number` (0–1) | `0.15` — gap between category groups |
|
|
556
|
+
| `bar.paddingOuter` | `number` (0–1) | `0.15` — gap before the first / after the last group |
|
|
557
|
+
| `pyramid.gap` | `AreaStyle` | `{ fillOpacity: 0.5 }` — fallback for `PyramidPlot`'s `styles.gaps` |
|
|
558
|
+
| `scatter.dotRadius` | `number` | `5` |
|
|
559
|
+
| `timeline.thickness` | `number` | `60` |
|
|
560
|
+
| `hover.rule` | `StrokeStyle` | `{ stroke: '#e6580d', strokeOpacity: 0.35, strokeWidth: 1.5, strokeDasharray: '4 4' }` — hover crosshair |
|
|
561
|
+
| `hover.dot` | `DotStyle` | `{ dotRadius: 5, dotFill: 'currentColor' }` |
|
|
562
|
+
| `hover.font` | `FontStyle` | `{ fill: 'currentColor', fontSize: 12, dy: -18 }` |
|
|
563
|
+
| `hover.area` | `AreaStyle` | `{ fill: '#94a3b8', fillOpacity: 0.25 }` — band highlight on a band scale |
|
|
564
|
+
|
|
565
|
+
`timeline.axisColor` / `timeline.activeColor` are the two exceptions in this group — they *do* have CSS variables (see below), since the timeline gutter is HTML chrome, not an SVG plot mark.
|
|
566
|
+
|
|
567
|
+
| Field | Type | Default | CSS variable |
|
|
568
|
+
| ----- | ---- | ------- | ------------- |
|
|
569
|
+
| `timeline.axisColor` | `string` | `'currentColor'` | `--dct-timeline-axis` |
|
|
570
|
+
| `timeline.activeColor` | `string` | `'#e6580d'` | `--dct-timeline-active` |
|
|
571
|
+
|
|
572
|
+
#### Facet (small multiples)
|
|
573
|
+
|
|
574
|
+
| Field | Type | Default | CSS variable |
|
|
575
|
+
| ----- | ---- | ------- | ------------- |
|
|
576
|
+
| `facet.indicator.background` | `string` | `'#111827'` | `--dct-facet-indicator-bg` |
|
|
577
|
+
| `facet.indicator.color` | `string` | `'#ffffff'` | `--dct-facet-indicator-color` |
|
|
578
|
+
| `facet.indicator.fontSize` | `string` | `'0.8125rem'` | `--dct-facet-indicator-size` |
|
|
579
|
+
| `facet.indicator.padding` | `string` | `'0.125rem 0.5rem'` | `--dct-facet-indicator-pad` |
|
|
580
|
+
| `facet.indicator.radius` | `string` | `'999px'` | `--dct-facet-indicator-radius` |
|
|
581
|
+
| `facet.navButton.background` | `string` | `'#111827'` | `--dct-facet-nav-bg` |
|
|
582
|
+
| `facet.navButton.color` | `string` | `'#ffffff'` | `--dct-facet-nav-color` |
|
|
583
|
+
| `facet.navButton.size` | `string` | `'2rem'` | `--dct-facet-nav-btn-size` |
|
|
584
|
+
| `facet.navButton.fontSize` | `string` | `'1.1rem'` | `--dct-facet-nav-font-size` |
|
|
585
|
+
| `facet.navButton.disabledOpacity` | `number` | `0.3` | `--dct-facet-nav-disabled-opacity` |
|
|
586
|
+
|
|
587
|
+
---
|
|
588
|
+
|
|
402
589
|
## Public API
|
|
403
590
|
|
|
404
591
|
```ts
|
|
@@ -77,7 +77,12 @@ export const DEFAULT_CONFIG = {
|
|
|
77
77
|
label: {
|
|
78
78
|
rotate: '0deg',
|
|
79
79
|
size: '0.875rem',
|
|
80
|
-
weight: 600
|
|
80
|
+
weight: 600,
|
|
81
|
+
overlap: {
|
|
82
|
+
enabled: true,
|
|
83
|
+
margin: 4,
|
|
84
|
+
rotate: '90deg'
|
|
85
|
+
}
|
|
81
86
|
}
|
|
82
87
|
}
|
|
83
88
|
},
|
|
@@ -228,6 +233,7 @@ export function configToCssVars(c) {
|
|
|
228
233
|
'--dct-continuous-label-rotate': c.legend.continuous.label.rotate,
|
|
229
234
|
'--dct-continuous-label-size': c.legend.continuous.label.size,
|
|
230
235
|
'--dct-continuous-label-weight': String(c.legend.continuous.label.weight),
|
|
236
|
+
'--dct-continuous-label-overlap-rotate': c.legend.continuous.label.overlap.rotate,
|
|
231
237
|
'--dct-axis-color': c.axis.color,
|
|
232
238
|
'--dct-tooltip-bg': c.tooltip.background,
|
|
233
239
|
'--dct-tooltip-color': c.tooltip.color,
|
|
@@ -75,6 +75,11 @@ export declare const THEMES: {
|
|
|
75
75
|
rotate?: string | undefined;
|
|
76
76
|
size?: string | undefined;
|
|
77
77
|
weight?: string | number | undefined;
|
|
78
|
+
overlap?: {
|
|
79
|
+
enabled?: boolean | undefined;
|
|
80
|
+
margin?: number | undefined;
|
|
81
|
+
rotate?: string | undefined;
|
|
82
|
+
} | undefined;
|
|
78
83
|
} | undefined;
|
|
79
84
|
} | undefined;
|
|
80
85
|
} | undefined;
|
|
@@ -306,6 +311,11 @@ export declare const THEMES: {
|
|
|
306
311
|
rotate?: string | undefined;
|
|
307
312
|
size?: string | undefined;
|
|
308
313
|
weight?: string | number | undefined;
|
|
314
|
+
overlap?: {
|
|
315
|
+
enabled?: boolean | undefined;
|
|
316
|
+
margin?: number | undefined;
|
|
317
|
+
rotate?: string | undefined;
|
|
318
|
+
} | undefined;
|
|
309
319
|
} | undefined;
|
|
310
320
|
} | undefined;
|
|
311
321
|
} | undefined;
|
|
@@ -537,6 +547,11 @@ export declare const THEMES: {
|
|
|
537
547
|
rotate?: string | undefined;
|
|
538
548
|
size?: string | undefined;
|
|
539
549
|
weight?: string | number | undefined;
|
|
550
|
+
overlap?: {
|
|
551
|
+
enabled?: boolean | undefined;
|
|
552
|
+
margin?: number | undefined;
|
|
553
|
+
rotate?: string | undefined;
|
|
554
|
+
} | undefined;
|
|
540
555
|
} | undefined;
|
|
541
556
|
} | undefined;
|
|
542
557
|
} | undefined;
|
|
@@ -768,6 +783,11 @@ export declare const THEMES: {
|
|
|
768
783
|
rotate?: string | undefined;
|
|
769
784
|
size?: string | undefined;
|
|
770
785
|
weight?: string | number | undefined;
|
|
786
|
+
overlap?: {
|
|
787
|
+
enabled?: boolean | undefined;
|
|
788
|
+
margin?: number | undefined;
|
|
789
|
+
rotate?: string | undefined;
|
|
790
|
+
} | undefined;
|
|
771
791
|
} | undefined;
|
|
772
792
|
} | undefined;
|
|
773
793
|
} | undefined;
|
|
@@ -166,6 +166,15 @@
|
|
|
166
166
|
}
|
|
167
167
|
};
|
|
168
168
|
}
|
|
169
|
+
|
|
170
|
+
let loLabelWidth = $state(0);
|
|
171
|
+
let hiLabelWidth = $state(0);
|
|
172
|
+
|
|
173
|
+
const labelsOverlap = $derived(
|
|
174
|
+
isInteractive &&
|
|
175
|
+
cfg.legend.continuous.label.overlap.enabled &&
|
|
176
|
+
(hiT - loT) * barWidth - loLabelWidth - hiLabelWidth < cfg.legend.continuous.label.overlap.margin,
|
|
177
|
+
);
|
|
169
178
|
</script>
|
|
170
179
|
|
|
171
180
|
<div class="dct-continuous" style:width="{barWidth}px">
|
|
@@ -237,8 +246,18 @@
|
|
|
237
246
|
|
|
238
247
|
<div class="dct-continuous__labels" style:width="{barWidth}px">
|
|
239
248
|
{#if isInteractive}
|
|
240
|
-
<span class="dct-continuous__tick
|
|
241
|
-
<span class="dct-continuous__tick
|
|
249
|
+
<span class="dct-continuous__tick-measure" aria-hidden="true" bind:clientWidth={loLabelWidth}>{fmt(lo)}</span>
|
|
250
|
+
<span class="dct-continuous__tick-measure" aria-hidden="true" bind:clientWidth={hiLabelWidth}>{fmt(hi)}</span>
|
|
251
|
+
<span
|
|
252
|
+
class="dct-continuous__tick dct-continuous__tick--handle-lo"
|
|
253
|
+
class:dct-continuous__tick--vertical={labelsOverlap}
|
|
254
|
+
style:left="{loT * barWidth}px"
|
|
255
|
+
>{fmt(lo)}</span>
|
|
256
|
+
<span
|
|
257
|
+
class="dct-continuous__tick dct-continuous__tick--handle-hi"
|
|
258
|
+
class:dct-continuous__tick--vertical={labelsOverlap}
|
|
259
|
+
style:left="{hiT * barWidth}px"
|
|
260
|
+
>{fmt(hi)}</span>
|
|
242
261
|
{:else}
|
|
243
262
|
<span class="dct-continuous__edge dct-continuous__edge--start">{fmt(section.min)}</span>
|
|
244
263
|
{#each tickPositions as tick (tick.v)}
|
|
@@ -292,6 +311,30 @@
|
|
|
292
311
|
.dct-continuous__tick--handle-hi {
|
|
293
312
|
transform: translateX(-100%) rotate(var(--dct-continuous-label-rotate, 0deg));
|
|
294
313
|
}
|
|
314
|
+
/* Invisible, always-horizontal twin used only to measure a handle label's
|
|
315
|
+
natural width (see `labelsOverlap` above) — never rendered. */
|
|
316
|
+
.dct-continuous__tick-measure {
|
|
317
|
+
position: absolute;
|
|
318
|
+
visibility: hidden;
|
|
319
|
+
white-space: nowrap;
|
|
320
|
+
font-weight: var(--dct-continuous-label-weight, 600);
|
|
321
|
+
}
|
|
322
|
+
/*
|
|
323
|
+
* Once the two handle labels would otherwise collide, both read
|
|
324
|
+
* top-to-bottom instead of left-to-right — the *same* rotation and the
|
|
325
|
+
* *same* pivot corner for lo and hi, so the two read identically instead
|
|
326
|
+
* of one appearing mirrored against the other. Pivoting on the label's
|
|
327
|
+
* own bottom-left corner (exactly where `left: <anchor>px` already
|
|
328
|
+
* places it) means the rotation doesn't shift the label off its anchor
|
|
329
|
+
* at all — it grows right-and-down from the point it was already
|
|
330
|
+
* sitting at, the same "grow into the bar, not off the container's edge"
|
|
331
|
+
* reasoning as the horizontal anchoring above.
|
|
332
|
+
*/
|
|
333
|
+
.dct-continuous__tick--handle-lo.dct-continuous__tick--vertical,
|
|
334
|
+
.dct-continuous__tick--handle-hi.dct-continuous__tick--vertical {
|
|
335
|
+
transform-origin: left bottom;
|
|
336
|
+
transform: rotate(var(--dct-continuous-label-overlap-rotate, 90deg));
|
|
337
|
+
}
|
|
295
338
|
/* Tick mark below the bar at each non-interactive tick's position. */
|
|
296
339
|
.dct-continuous__tick-mark {
|
|
297
340
|
width: var(--dct-continuous-tick-width, 1px);
|
|
@@ -72,6 +72,15 @@ export type ChartConfig = {
|
|
|
72
72
|
size: string;
|
|
73
73
|
/** font-weight of the draggable lo/hi handle labels. */
|
|
74
74
|
weight: number | string;
|
|
75
|
+
/** Interactive-mode-only: the lo/hi handle labels flip to read top-to-bottom once dragging brings them too close to read side by side. */
|
|
76
|
+
overlap: {
|
|
77
|
+
/** Whether the flip-to-vertical behavior is active at all; false always keeps both labels horizontal, however close together. */
|
|
78
|
+
enabled: boolean;
|
|
79
|
+
/** How many px of horizontal slack the two labels need between them before they're considered overlapping (and flipped). */
|
|
80
|
+
margin: number;
|
|
81
|
+
/** Rotation magnitude applied once overlapping — lo rotates by this amount, hi by its negation, so both still grow inward toward the bar instead of off its edges. */
|
|
82
|
+
rotate: string;
|
|
83
|
+
};
|
|
75
84
|
};
|
|
76
85
|
};
|
|
77
86
|
};
|