@fundar/data-chart-telling 0.0.63 → 0.0.64

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
@@ -482,10 +482,13 @@ Each is a `TextStyle` — `{ font, size, weight, color, leading, spacing }` —
482
482
  | `legend.navButton.fontSize` | `string` | `'1rem'` | `--dct-legend-nav-font-size` |
483
483
  | `legend.navButton.disabledOpacity` | `number` | `0.3` | `--dct-legend-nav-disabled-opacity` |
484
484
 
485
- `legend.continuous` — the gradient bar's draggable handles, ticks, and labels:
485
+ `legend.continuous` — the gradient bar itself, its draggable handles, ticks, and labels:
486
486
 
487
487
  | Field | Type | Default | CSS variable |
488
488
  | ----- | ---- | ------- | ------------- |
489
+ | `legend.continuous.height` | `number` (px) | `20` | — |
490
+ | `legend.continuous.barHeight` | `number` (px) | `12` | — |
491
+ | `legend.continuous.barRadius` | `number` (px) | `2` | — |
489
492
  | `legend.continuous.handle.width` | `string` | `'6px'` | `--dct-continuous-handle-width` |
490
493
  | `legend.continuous.handle.height` | `string` | `'20px'` | `--dct-continuous-handle-height` |
491
494
  | `legend.continuous.handle.radius` | `string` | `'3px'` | `--dct-continuous-handle-radius` |
@@ -64,6 +64,9 @@ export const DEFAULT_CONFIG = {
64
64
  disabledOpacity: 0.3
65
65
  },
66
66
  continuous: {
67
+ height: 20,
68
+ barHeight: 12,
69
+ barRadius: 2,
67
70
  // A slim vertical bar handle, taller than the 12px gradient track.
68
71
  handle: {
69
72
  width: '6px',
@@ -58,6 +58,9 @@ export declare const THEMES: {
58
58
  disabledOpacity?: number | undefined;
59
59
  } | undefined;
60
60
  continuous?: {
61
+ height?: number | undefined;
62
+ barHeight?: number | undefined;
63
+ barRadius?: number | undefined;
61
64
  handle?: {
62
65
  width?: string | undefined;
63
66
  height?: string | undefined;
@@ -296,6 +299,9 @@ export declare const THEMES: {
296
299
  disabledOpacity?: number | undefined;
297
300
  } | undefined;
298
301
  continuous?: {
302
+ height?: number | undefined;
303
+ barHeight?: number | undefined;
304
+ barRadius?: number | undefined;
299
305
  handle?: {
300
306
  width?: string | undefined;
301
307
  height?: string | undefined;
@@ -534,6 +540,9 @@ export declare const THEMES: {
534
540
  disabledOpacity?: number | undefined;
535
541
  } | undefined;
536
542
  continuous?: {
543
+ height?: number | undefined;
544
+ barHeight?: number | undefined;
545
+ barRadius?: number | undefined;
537
546
  handle?: {
538
547
  width?: string | undefined;
539
548
  height?: string | undefined;
@@ -772,6 +781,9 @@ export declare const THEMES: {
772
781
  disabledOpacity?: number | undefined;
773
782
  } | undefined;
774
783
  continuous?: {
784
+ height?: number | undefined;
785
+ barHeight?: number | undefined;
786
+ barRadius?: number | undefined;
775
787
  handle?: {
776
788
  width?: string | undefined;
777
789
  height?: string | undefined;
@@ -14,6 +14,23 @@
14
14
  const fmt = $derived(section.format ?? ((v: number) => String(v)));
15
15
  const isInteractive = $derived(section.interactive?.enabled ?? false);
16
16
 
17
+ // How far the default handle bleeds past its 0/1 track position, so the
18
+ // track can be inset from `barWidth`'s edges by that much.
19
+ let loHandleEl = $state<SVGRectElement>();
20
+ const handleBleed = $derived(
21
+ isInteractive && !section.interactive?.handle && loHandleEl
22
+ ? Math.ceil(loHandleEl.getBoundingClientRect().width / 2)
23
+ : 0,
24
+ );
25
+ const trackWidth = $derived(Math.max(0, barWidth - handleBleed * 2));
26
+ function trackX(t: number): number {
27
+ return handleBleed + t * trackWidth;
28
+ }
29
+
30
+ const svgHeight = $derived(cfg.legend.continuous.height);
31
+ const barHeight = $derived(cfg.legend.continuous.barHeight);
32
+ const barRadius = $derived(cfg.legend.continuous.barRadius);
33
+
17
34
  // Fill/opacity for the excluded-margin rects.
18
35
  const excludedStyle = $derived.by((): { fill: string; opacity: number } => {
19
36
  const d = section.interactive?.disabledStyle;
@@ -133,7 +150,7 @@
133
150
  target.setPointerCapture(event.pointerId);
134
151
  const startX = event.clientX;
135
152
  const startValue = which === 'lo' ? lo : hi;
136
- const valuePerPixel = (section.max - section.min) / barWidth;
153
+ const valuePerPixel = (section.max - section.min) / trackWidth;
137
154
 
138
155
  function onMove(e: PointerEvent): void {
139
156
  const next = startValue + (e.clientX - startX) * valuePerPixel;
@@ -173,12 +190,12 @@
173
190
  const labelsOverlap = $derived(
174
191
  isInteractive &&
175
192
  cfg.legend.continuous.label.overlap.enabled &&
176
- (hiT - loT) * barWidth - loLabelWidth - hiLabelWidth < cfg.legend.continuous.label.overlap.margin,
193
+ (hiT - loT) * trackWidth - loLabelWidth - hiLabelWidth < cfg.legend.continuous.label.overlap.margin,
177
194
  );
178
195
  </script>
179
196
 
180
197
  <div class="dct-continuous" style:width="{barWidth}px">
181
- <svg width={barWidth} height="20" style="overflow: visible">
198
+ <svg width={barWidth} height={svgHeight} style="overflow: visible">
182
199
  <defs>
183
200
  <linearGradient id={gradientId} x1="0" x2="1" y1="0" y2="0">
184
201
  {#each stops as s (s.offset)}
@@ -186,17 +203,17 @@
186
203
  {/each}
187
204
  </linearGradient>
188
205
  </defs>
189
- <rect width={barWidth} height="12" rx="2" fill="url(#{gradientId})" />
206
+ <rect x={handleBleed} width={trackWidth} height={barHeight} rx={barRadius} fill="url(#{gradientId})" />
190
207
  {#each tickPositions as tick (tick.v)}
191
- <rect class="dct-continuous__tick-mark" x={tick.t * barWidth} y="12" />
208
+ <rect class="dct-continuous__tick-mark" x={trackX(tick.t)} y={barHeight} />
192
209
  {/each}
193
210
 
194
211
  {#if isInteractive}
195
212
  {#if loT > 0}
196
- <rect x="0" y="0" width={loT * barWidth} height="12" rx="2" fill={excludedStyle.fill} fill-opacity={excludedStyle.opacity} />
213
+ <rect x={handleBleed} y="0" width={loT * trackWidth} height={barHeight} rx={barRadius} fill={excludedStyle.fill} fill-opacity={excludedStyle.opacity} />
197
214
  {/if}
198
215
  {#if hiT < 1}
199
- <rect x={hiT * barWidth} y="0" width={barWidth - hiT * barWidth} height="12" rx="2" fill={excludedStyle.fill} fill-opacity={excludedStyle.opacity} />
216
+ <rect x={trackX(hiT)} y="0" width={trackWidth - hiT * trackWidth} height={barHeight} rx={barRadius} fill={excludedStyle.fill} fill-opacity={excludedStyle.opacity} />
200
217
  {/if}
201
218
  {#if section.interactive?.handle}
202
219
  {@render section.interactive.handle({
@@ -216,8 +233,9 @@
216
233
  {:else}
217
234
  <rect
218
235
  class="dct-continuous__handle"
219
- x={loT * barWidth}
220
- y="6"
236
+ bind:this={loHandleEl}
237
+ x={trackX(loT)}
238
+ y={barHeight / 2}
221
239
  onpointerdown={startDrag('lo')}
222
240
  ondblclick={resetHandle('lo')}
223
241
  role="slider"
@@ -229,8 +247,8 @@
229
247
  />
230
248
  <rect
231
249
  class="dct-continuous__handle"
232
- x={hiT * barWidth}
233
- y="6"
250
+ x={trackX(hiT)}
251
+ y={barHeight / 2}
234
252
  onpointerdown={startDrag('hi')}
235
253
  ondblclick={resetHandle('hi')}
236
254
  role="slider"
@@ -251,17 +269,17 @@
251
269
  <span
252
270
  class="dct-continuous__tick dct-continuous__tick--handle-lo"
253
271
  class:dct-continuous__tick--vertical={labelsOverlap}
254
- style:left="{loT * barWidth}px"
272
+ style:left="{trackX(loT)}px"
255
273
  >{fmt(lo)}</span>
256
274
  <span
257
275
  class="dct-continuous__tick dct-continuous__tick--handle-hi"
258
276
  class:dct-continuous__tick--vertical={labelsOverlap}
259
- style:left="{hiT * barWidth}px"
277
+ style:left="{trackX(hiT)}px"
260
278
  >{fmt(hi)}</span>
261
279
  {:else}
262
280
  <span class="dct-continuous__edge dct-continuous__edge--start">{fmt(section.min)}</span>
263
281
  {#each tickPositions as tick (tick.v)}
264
- <span class="dct-continuous__tick" style:left="{tick.t * barWidth}px">{tick.label}</span>
282
+ <span class="dct-continuous__tick" style:left="{trackX(tick.t)}px">{tick.label}</span>
265
283
  {/each}
266
284
  <span class="dct-continuous__edge dct-continuous__edge--end">{fmt(section.max)}</span>
267
285
  {/if}
@@ -26,6 +26,7 @@
26
26
  const cfg = $derived(getConfiguration());
27
27
  const layout = $derived(section.layout ?? defaultItemsLayout);
28
28
  const isHorizontal = $derived(layout.type === 'horizontal');
29
+ const isFlex = $derived(layout.type === 'flex');
29
30
  const maxRows = $derived(layout.type === 'horizontal' ? Math.max(1, layout.maxRows ?? 1) : 1);
30
31
  const columns = $derived(section.columns ?? 1);
31
32
  const isInteractive = $derived(section.interactive?.enabled ?? false);
@@ -238,6 +239,12 @@
238
239
  )}
239
240
  {/if}
240
241
  </div>
242
+ {:else if isFlex}
243
+ <div class="dct-discrete dct-discrete--horizontal">
244
+ {#each section.items as item, i (item.name)}
245
+ {@render discreteItem(item, i)}
246
+ {/each}
247
+ </div>
241
248
  {:else}
242
249
  <div class="dct-discrete" style:grid-template-columns="repeat({columns}, max-content)">
243
250
  {#each section.items as item, i (item.name)}
@@ -50,6 +50,12 @@ export type ChartConfig = {
50
50
  };
51
51
  /** Continuous section styling: the draggable lo/hi handles and the tick/edge labels. */
52
52
  continuous: {
53
+ /** Total height (px) of the bar's drawing area. */
54
+ height: number;
55
+ /** Height (px) of the gradient bar itself, centered within `height`. */
56
+ barHeight: number;
57
+ /** Corner radius (px) of the gradient bar and excluded-range overlays. */
58
+ barRadius: number;
53
59
  /** The draggable lo/hi handle. */
54
60
  handle: {
55
61
  width: string;
@@ -37,13 +37,16 @@ export type LegendItem = {
37
37
  /**
38
38
  * How a discrete section's items flow. `'vertical'` stacks them in a
39
39
  * `columns`-wide grid; `'horizontal'` flows them left to right, paginating
40
- * past `maxRows` (default 1) with prev/next arrows.
40
+ * past `maxRows` (default 1) with prev/next arrows; `'flex'` also flows them
41
+ * left to right, but wraps onto as many rows as it takes instead of paginating.
41
42
  */
42
43
  export type LegendItemsLayout = {
43
44
  type: 'vertical';
44
45
  } | {
45
46
  type: 'horizontal';
46
47
  maxRows?: number;
48
+ } | {
49
+ type: 'flex';
47
50
  };
48
51
  export type LegendNavDirection = 'prev' | 'next';
49
52
  export type LegendNavContext = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fundar/data-chart-telling",
3
- "version": "0.0.63",
3
+ "version": "0.0.64",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"