layerchart 2.0.0-next.37 → 2.0.0-next.39

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 (115) hide show
  1. package/dist/components/AnnotationLine.svelte +15 -2
  2. package/dist/components/AnnotationPoint.svelte +13 -2
  3. package/dist/components/AnnotationRange.svelte +16 -2
  4. package/dist/components/Arc.svelte +3 -3
  5. package/dist/components/Area.svelte +10 -2
  6. package/dist/components/Axis.svelte +43 -26
  7. package/dist/components/Axis.svelte.d.ts +10 -3
  8. package/dist/components/Bar.svelte +6 -5
  9. package/dist/components/Bar.svelte.d.ts +2 -2
  10. package/dist/components/Bars.svelte +3 -3
  11. package/dist/components/Blur.svelte +2 -3
  12. package/dist/components/BrushContext.svelte +44 -44
  13. package/dist/components/Calendar.svelte +21 -4
  14. package/dist/components/Chart.svelte +1 -2
  15. package/dist/components/Chart.svelte.d.ts +10 -3
  16. package/dist/components/ChartClipPath.svelte +1 -1
  17. package/dist/components/Circle.svelte +44 -3
  18. package/dist/components/CircleClipPath.svelte +8 -1
  19. package/dist/components/ClipPath.svelte +1 -2
  20. package/dist/components/ColorRamp.svelte +1 -1
  21. package/dist/components/ComputedStyles.svelte +9 -2
  22. package/dist/components/Connector.svelte +1 -1
  23. package/dist/components/Ellipse.svelte +44 -3
  24. package/dist/components/ForceSimulation.svelte.d.ts +10 -3
  25. package/dist/components/Frame.svelte +1 -1
  26. package/dist/components/GeoCircle.svelte +1 -1
  27. package/dist/components/GeoEdgeFade.svelte +1 -1
  28. package/dist/components/GeoPath.svelte +18 -3
  29. package/dist/components/GeoPoint.svelte +3 -3
  30. package/dist/components/GeoSpline.svelte +1 -1
  31. package/dist/components/GeoTile.svelte +1 -1
  32. package/dist/components/Graticule.svelte +5 -5
  33. package/dist/components/Grid.svelte +57 -60
  34. package/dist/components/Group.svelte +11 -6
  35. package/dist/components/Group.svelte.d.ts +10 -3
  36. package/dist/components/Highlight.svelte +46 -22
  37. package/dist/components/Highlight.svelte.d.ts +4 -0
  38. package/dist/components/Hull.svelte +11 -4
  39. package/dist/components/Labels.svelte +21 -11
  40. package/dist/components/Labels.svelte.d.ts +10 -3
  41. package/dist/components/Legend.svelte +133 -67
  42. package/dist/components/Legend.svelte.d.ts +7 -3
  43. package/dist/components/Line.svelte +40 -3
  44. package/dist/components/LinearGradient.svelte +35 -4
  45. package/dist/components/Link.svelte +1 -1
  46. package/dist/components/Marker.svelte +37 -26
  47. package/dist/components/MonthPath.svelte +14 -3
  48. package/dist/components/MotionPath.svelte +1 -1
  49. package/dist/components/Pack.svelte.d.ts +10 -3
  50. package/dist/components/Partition.svelte.d.ts +10 -3
  51. package/dist/components/Pattern.svelte +5 -5
  52. package/dist/components/Pie.svelte +1 -2
  53. package/dist/components/Points.svelte +1 -3
  54. package/dist/components/Polygon.svelte +27 -3
  55. package/dist/components/RadialGradient.svelte +3 -3
  56. package/dist/components/Rect.svelte +55 -5
  57. package/dist/components/Rect.svelte.d.ts +2 -2
  58. package/dist/components/RectClipPath.svelte +4 -3
  59. package/dist/components/RectClipPath.svelte.d.ts +2 -2
  60. package/dist/components/Rule.svelte +30 -23
  61. package/dist/components/Spline.svelte +29 -10
  62. package/dist/components/Text.svelte +59 -13
  63. package/dist/components/TileImage.svelte +19 -4
  64. package/dist/components/TransformContext.svelte +9 -3
  65. package/dist/components/TransformControls.svelte +72 -17
  66. package/dist/components/Tree.svelte.d.ts +10 -3
  67. package/dist/components/Treemap.svelte.d.ts +10 -3
  68. package/dist/components/Voronoi.svelte +12 -13
  69. package/dist/components/charts/ArcChart.svelte +40 -69
  70. package/dist/components/charts/ArcChart.svelte.d.ts +10 -3
  71. package/dist/components/charts/AreaChart.svelte +19 -42
  72. package/dist/components/charts/AreaChart.svelte.d.ts +10 -3
  73. package/dist/components/charts/BarChart.svelte +7 -18
  74. package/dist/components/charts/BarChart.svelte.d.ts +10 -3
  75. package/dist/components/charts/DefaultTooltip.svelte +2 -2
  76. package/dist/components/charts/DefaultTooltip.svelte.d.ts +1 -1
  77. package/dist/components/charts/LineChart.svelte +61 -66
  78. package/dist/components/charts/LineChart.svelte.d.ts +21 -8
  79. package/dist/components/charts/PieChart.svelte +41 -69
  80. package/dist/components/charts/PieChart.svelte.d.ts +10 -3
  81. package/dist/components/charts/ScatterChart.svelte +8 -19
  82. package/dist/components/charts/ScatterChart.svelte.d.ts +10 -3
  83. package/dist/components/charts/utils.svelte.d.ts +1 -19
  84. package/dist/components/charts/utils.svelte.js +7 -39
  85. package/dist/components/layout/Canvas.svelte +29 -20
  86. package/dist/components/layout/Html.svelte +15 -9
  87. package/dist/components/layout/Svg.svelte +19 -11
  88. package/dist/components/layout/WebGL.svelte +26 -6
  89. package/dist/components/layout/WebGL.svelte.d.ts +5 -2
  90. package/dist/components/tooltip/Tooltip.svelte +60 -29
  91. package/dist/components/tooltip/Tooltip.svelte.d.ts +10 -3
  92. package/dist/components/tooltip/TooltipContext.svelte +73 -36
  93. package/dist/components/tooltip/TooltipContext.svelte.d.ts +17 -3
  94. package/dist/components/tooltip/TooltipHeader.svelte +27 -14
  95. package/dist/components/tooltip/TooltipItem.svelte +41 -33
  96. package/dist/components/tooltip/TooltipList.svelte +12 -10
  97. package/dist/components/tooltip/TooltipSeparator.svelte +18 -10
  98. package/dist/states/series.svelte.d.ts +30 -0
  99. package/dist/states/series.svelte.js +54 -0
  100. package/dist/styles/daisyui-5.css +6 -0
  101. package/dist/styles/shadcn-svelte.css +11 -0
  102. package/dist/styles/skeleton-3.css +15 -0
  103. package/dist/utils/attributes.d.ts +3 -13
  104. package/dist/utils/attributes.js +4 -18
  105. package/dist/utils/common.d.ts +9 -0
  106. package/dist/utils/common.js +18 -1
  107. package/dist/utils/common.test.js +26 -1
  108. package/dist/utils/graph/dagre.d.ts +4 -4
  109. package/dist/utils/graph/dagre.js +5 -7
  110. package/dist/utils/math.d.ts +17 -0
  111. package/dist/utils/math.js +17 -0
  112. package/dist/utils/scales.svelte.js +3 -3
  113. package/dist/utils/stack.js +1 -1
  114. package/dist/utils/types.d.ts +15 -2
  115. package/package.json +25 -22
@@ -101,6 +101,11 @@
101
101
  */
102
102
  motion?: MotionProp;
103
103
 
104
+ /**
105
+ * The opacity of the element. (0 to 1)
106
+ */
107
+ opacity?: number;
108
+
104
109
  onAreaClick?: (e: MouseEvent, detail: { data: any }) => void;
105
110
  onBarClick?: (e: MouseEvent, detail: { data: any }) => void;
106
111
 
@@ -114,7 +119,6 @@
114
119
  import { max, min } from 'd3-array';
115
120
  import { pointRadial, type Series, type SeriesPoint } from 'd3-shape';
116
121
  import { notNull } from '@layerstack/utils';
117
- import { cls } from '@layerstack/tailwind';
118
122
 
119
123
  import { isScaleBand, isScaleTime } from '../utils/scales.svelte.js';
120
124
  import { asAny } from '../utils/types.js';
@@ -136,6 +140,7 @@
136
140
  lines: linesProp = false,
137
141
  area = false,
138
142
  bar = false,
143
+ opacity,
139
144
  motion = 'spring',
140
145
  onAreaClick,
141
146
  onBarClick,
@@ -435,11 +440,6 @@
435
440
  return tmpPoints;
436
441
  }
437
442
  );
438
-
439
- const areaProps = $derived(extractLayerProps(area, 'highlight-area'));
440
- const barProps = $derived(extractLayerProps(bar, 'highlight-bar'));
441
- const linesProps = $derived(extractLayerProps(linesProp, 'highlight-line'));
442
- const pointsProps = $derived(extractLayerProps(points, 'highlight-point'));
443
443
  </script>
444
444
 
445
445
  {#if highlightData}
@@ -454,15 +454,16 @@
454
454
  endAngle={_area.x + _area.width}
455
455
  innerRadius={_area.y}
456
456
  outerRadius={_area.y + _area.height}
457
- class={cls(!areaProps.fill && 'fill-surface-content/5', areaProps.class)}
457
+ {opacity}
458
+ class="lc-highlight-area"
458
459
  onclick={onAreaClick && ((e) => onAreaClick(e, { data: highlightData }))}
459
460
  />
460
461
  {:else}
461
462
  <Rect
462
463
  motion={motion === 'spring' ? 'spring' : undefined}
464
+ {opacity}
463
465
  {..._area}
464
- {...areaProps}
465
- class={cls(!areaProps.fill && 'fill-surface-content/5', areaProps.class)}
466
+ {...extractLayerProps(area, 'lc-highlight-area')}
466
467
  onclick={onAreaClick && ((e) => onAreaClick(e, { data: highlightData }))}
467
468
  />
468
469
  {/if}
@@ -475,8 +476,8 @@
475
476
  <Bar
476
477
  motion={motion === 'spring' ? 'spring' : undefined}
477
478
  data={highlightData}
478
- {...barProps}
479
- class={cls(!barProps.fill && 'fill-primary', barProps.class)}
479
+ {opacity}
480
+ {...extractLayerProps(bar, 'lc-highlight-bar')}
480
481
  onclick={onBarClick && ((e) => onBarClick(e, { data: highlightData }))}
481
482
  />
482
483
  {/if}
@@ -493,11 +494,8 @@
493
494
  y1={line.y1}
494
495
  x2={line.x2}
495
496
  y2={line.y2}
496
- {...linesProps}
497
- class={cls(
498
- 'stroke-surface-content/20 stroke-2 [stroke-dasharray:2,2] pointer-events-none',
499
- linesProps.class
500
- )}
497
+ {opacity}
498
+ {...extractLayerProps(linesProp, 'lc-highlight-line')}
501
499
  />
502
500
  {/each}
503
501
  {/if}
@@ -515,12 +513,8 @@
515
513
  fill={point.fill}
516
514
  r={4}
517
515
  strokeWidth={6}
518
- {...pointsProps}
519
- class={cls(
520
- 'stroke-white [paint-order:stroke] drop-shadow-sm',
521
- !point.fill && (typeof points === 'boolean' || !points.fill) && 'fill-primary',
522
- pointsProps.class
523
- )}
516
+ {opacity}
517
+ {...extractLayerProps(points, 'lc-highlight-point')}
524
518
  onpointerdown={onPointClick &&
525
519
  ((e) => {
526
520
  // Do not propagate `pointerdown` event to `BrushContext` if `onclick` is provided
@@ -546,3 +540,33 @@
546
540
  {/if}
547
541
  {/if}
548
542
  {/if}
543
+
544
+ <style>
545
+ @layer components {
546
+ :global(:where(.lc-highlight-area)) {
547
+ --fill-color: color-mix(in oklab, var(--color-surface-content, currentColor) 5%, transparent);
548
+ }
549
+
550
+ :global(:where(.lc-highlight-bar)) {
551
+ --fill-color: var(--color-primary, currentColor);
552
+ }
553
+
554
+ :global(:where(.lc-highlight-line)) {
555
+ --stroke-color: color-mix(
556
+ in oklab,
557
+ var(--color-surface-content, currentColor) 20%,
558
+ transparent
559
+ );
560
+ stroke-width: 2;
561
+ stroke-dasharray: 2 2;
562
+ pointer-events: none;
563
+ }
564
+
565
+ :global(:where(.lc-highlight-point)) {
566
+ --stroke-color: white;
567
+ --fill-color: var(--color-primary, currentColor);
568
+ paint-order: stroke;
569
+ filter: drop-shadow(var(--drop-shadow-sm, 0 1px 2px rgb(0 0 0 / 0.15)));
570
+ }
571
+ }
572
+ </style>
@@ -82,6 +82,10 @@ export type HighlightPropsWithoutHTML = {
82
82
  * @default true
83
83
  */
84
84
  motion?: MotionProp;
85
+ /**
86
+ * The opacity of the element. (0 to 1)
87
+ */
88
+ opacity?: number;
85
89
  onAreaClick?: (e: MouseEvent, detail: {
86
90
  data: any;
87
91
  }) => void;
@@ -65,7 +65,6 @@
65
65
  import Spline from './Spline.svelte';
66
66
  import { getChartContext } from './Chart.svelte';
67
67
  import { getGeoContext } from './GeoContext.svelte';
68
- import { layerClass } from '../utils/attributes.js';
69
68
 
70
69
  let {
71
70
  data,
@@ -104,13 +103,13 @@
104
103
  );
105
104
  </script>
106
105
 
107
- <Group {...restProps} class={cls(layerClass('hull-g'), classes.root, className)} bind:ref>
106
+ <Group {...restProps} class={cls('lc-hull-g', classes.root, className)} bind:ref>
108
107
  {#if geoCtx.projection}
109
108
  {@const polygon = geoVoronoi().hull(points)}
110
109
  <GeoPath
111
110
  geojson={polygon}
112
111
  {curve}
113
- class={cls(layerClass('hull-path'), 'fill-transparent', classes.path)}
112
+ class={['lc-hull-path', classes.path]}
114
113
  onclick={(e) => onclick?.(e, { points, polygon })}
115
114
  onpointermove={(e) => onpointermove?.(e, { points, polygon })}
116
115
  {onpointerleave}
@@ -123,10 +122,18 @@
123
122
  x={(d) => d[0]}
124
123
  y={(d) => d[1]}
125
124
  {curve}
126
- class={cls(layerClass('hull-class'), 'fill-transparent', classes.path)}
125
+ class={['lc-hull-class', classes.path]}
127
126
  onclick={(e) => onclick?.(e, { points, polygon })}
128
127
  onpointermove={(e) => onpointermove?.(e, { points, polygon })}
129
128
  {onpointerleave}
130
129
  />
131
130
  {/if}
132
131
  </Group>
132
+
133
+ <style>
134
+ @layer components {
135
+ :global(:where(.lc-hull-path, .lc-hull-geo-path)) {
136
+ fill: transparent;
137
+ }
138
+ }
139
+ </style>
@@ -74,7 +74,7 @@
74
74
  import { isScaleBand } from '../utils/scales.svelte.js';
75
75
  import { getChartContext } from './Chart.svelte';
76
76
  import Group from './Group.svelte';
77
- import { extractLayerProps, layerClass } from '../utils/attributes.js';
77
+ import { extractLayerProps } from '../utils/attributes.js';
78
78
 
79
79
  const ctx = getChartContext();
80
80
 
@@ -174,28 +174,38 @@
174
174
  }
175
175
  </script>
176
176
 
177
- <Group class={layerClass('labels-g')}>
177
+ <Group class="lc-labels-g">
178
178
  <Points {data} {x} {y}>
179
179
  {#snippet children({ points })}
180
180
  {#each points as point, i (key(point.data, i))}
181
- {@const textProps = extractLayerProps(getTextProps(point), 'labels-text')}
181
+ {@const textProps = extractLayerProps(getTextProps(point), 'lc-labels-text')}
182
182
  {#if childrenProp}
183
183
  {@render childrenProp({ data: point, textProps })}
184
184
  {:else}
185
185
  <Text
186
+ data-placement={placement}
186
187
  {...textProps}
187
188
  {...restProps}
188
- class={cls(
189
- 'text-xs',
190
- placement === 'inside'
191
- ? 'fill-surface-300 stroke-surface-content'
192
- : 'fill-surface-content stroke-surface-100',
193
- textProps.class,
194
- className
195
- )}
189
+ {...extractLayerProps(getTextProps(point), 'lc-labels-text', className ?? '')}
196
190
  />
197
191
  {/if}
198
192
  {/each}
199
193
  {/snippet}
200
194
  </Points>
201
195
  </Group>
196
+
197
+ <style>
198
+ @layer components {
199
+ :global(:where(.lc-labels-text)) {
200
+ font-size: 12px;
201
+
202
+ --fill-color: var(--color-surface-content, currentColor);
203
+ --stroke-color: var(--color-surface-100, light-dark(white, black));
204
+
205
+ &[data-placement='inside'] {
206
+ --fill-color: var(--color-surface-100, light-dark(white, black));
207
+ --stroke-color: var(--color-surface-content, currentColor);
208
+ }
209
+ }
210
+ }
211
+ </style>
@@ -56,10 +56,17 @@ export type LabelsPropsWithoutHTML<T = any> = {
56
56
  }]>;
57
57
  };
58
58
  export type LabelsProps<T = any> = LabelsPropsWithoutHTML<T> & Without<TextProps, LabelsPropsWithoutHTML<T>>;
59
+ declare function $$render<TData = any>(): {
60
+ props: LabelsProps<TData>;
61
+ exports: {};
62
+ bindings: "";
63
+ slots: {};
64
+ events: {};
65
+ };
59
66
  declare class __sveltets_Render<TData = any> {
60
- props(): LabelsProps<TData>;
61
- events(): {};
62
- slots(): {};
67
+ props(): ReturnType<typeof $$render<TData>>['props'];
68
+ events(): ReturnType<typeof $$render<TData>>['events'];
69
+ slots(): ReturnType<typeof $$render<TData>>['slots'];
63
70
  bindings(): "";
64
71
  exports(): {};
65
72
  }
@@ -65,10 +65,6 @@
65
65
  */
66
66
  orientation?: 'horizontal' | 'vertical';
67
67
 
68
- onclick?: (e: MouseEvent, detail: LegendItem) => any;
69
- onpointerenter?: (e: MouseEvent, detail: LegendItem) => any;
70
- onpointerleave?: (e: MouseEvent, detail: LegendItem) => any;
71
-
72
68
  /**
73
69
  * Determine display ramp (individual color swatches or continuous ramp)
74
70
  *
@@ -76,6 +72,11 @@
76
72
  */
77
73
  variant?: 'ramp' | 'swatches';
78
74
 
75
+ /**
76
+ * An array of selected items. If provided, the legend fades unselected items.
77
+ */
78
+ selected?: string[];
79
+
79
80
  /**
80
81
  * Classes to apply to the elements.
81
82
  *
@@ -91,6 +92,10 @@
91
92
  item?: string | ((item: LegendItem) => string);
92
93
  };
93
94
 
95
+ onclick?: (e: MouseEvent, detail: LegendItem) => any;
96
+ onpointerenter?: (e: MouseEvent, detail: LegendItem) => any;
97
+ onpointerleave?: (e: MouseEvent, detail: LegendItem) => any;
98
+
94
99
  /**
95
100
  * A bindable reference to the wrapping `<div>` element.
96
101
  *
@@ -116,7 +121,8 @@
116
121
  import { cls } from '@layerstack/tailwind';
117
122
  import type { AnyScale } from '../utils/scales.svelte.js';
118
123
  import { getChartContext } from './Chart.svelte';
119
- import { extractLayerProps, layerClass } from '../utils/attributes.js';
124
+ import { extractLayerProps } from '../utils/attributes.js';
125
+ import { resolveMaybeFn } from '../utils/common.js';
120
126
 
121
127
  let {
122
128
  scale: scaleProp,
@@ -134,6 +140,7 @@
134
140
  onpointerenter,
135
141
  onpointerleave,
136
142
  variant = 'ramp',
143
+ selected = [],
137
144
  classes = {},
138
145
  ref: refProp = $bindable(),
139
146
  class: className,
@@ -296,29 +303,9 @@
296
303
  bind:this={ref}
297
304
  {...restProps}
298
305
  data-placement={placement}
299
- class={cls(
300
- layerClass('legend-container'),
301
- 'inline-block',
302
- 'z-1', // stack above tooltip context layers (band rects, voronoi, ...)
303
- placement && [
304
- 'absolute',
305
- {
306
- 'top-left': 'top-0 left-0',
307
- top: 'top-0 left-1/2 -translate-x-1/2',
308
- 'top-right': 'top-0 right-0',
309
- left: 'top-1/2 left-0 -translate-y-1/2',
310
- center: 'top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2',
311
- right: 'top-1/2 right-0 -translate-y-1/2',
312
- 'bottom-left': 'bottom-0 left-0',
313
- bottom: 'bottom-0 left-1/2 -translate-x-1/2',
314
- 'bottom-right': 'bottom-0 right-0',
315
- }[placement],
316
- ],
317
- className,
318
- classes.root
319
- )}
306
+ class={cls('lc-legend-container', className, classes.root)}
320
307
  >
321
- <div class={cls(layerClass('legend-title'), 'text-[10px] font-semibold', classes.title)}>
308
+ <div class={cls('lc-legend-title', classes.title)}>
322
309
  {title}
323
310
  </div>
324
311
  {#if children}
@@ -331,35 +318,31 @@
331
318
  {width}
332
319
  height={height + tickLengthProp + tickFontSize}
333
320
  viewBox="0 0 {width} {height + tickLengthProp + tickFontSize}"
334
- class={cls(layerClass('legend-ramp-svg'), 'overflow-visible')}
321
+ class={cls('lc-legend-ramp-svg')}
335
322
  >
336
- <g class={layerClass('legend-ramp-g')}>
323
+ <g class="lc-legend-ramp-g">
337
324
  {#if scaleConfig.interpolator}
338
325
  <ColorRamp
339
326
  {width}
340
327
  {height}
341
328
  interpolator={scaleConfig.interpolator}
342
- class={layerClass('legend-color-ramp')}
329
+ class="lc-legend-color-ramp"
343
330
  />
344
331
  {:else if scaleConfig.swatches}
345
332
  {#each scaleConfig.swatches as swatch, i}
346
- <rect {...extractLayerProps(swatch, 'legend-swatch')} />
333
+ <rect {...extractLayerProps(swatch, 'lc-legend-ramp-swatch')} />
347
334
  {/each}
348
335
  {/if}
349
336
  </g>
350
337
 
351
- <g class={layerClass('legend-tick-group')}>
338
+ <g class="lc-legend-tick-group">
352
339
  {#each tickValuesProp ?? scaleConfig.xScale?.ticks?.(ticks) ?? [] as tick, i}
353
340
  <text
354
341
  text-anchor="middle"
355
342
  x={scaleConfig.xScale?.(tick) + scaleConfig.tickLabelOffset}
356
343
  y={height + tickLengthProp + tickFontSize}
357
344
  style:font-size={tickFontSize}
358
- class={cls(
359
- layerClass('legend-tick-text'),
360
- 'text-[10px] fill-surface-content',
361
- classes.label
362
- )}
345
+ class={cls('lc-legend-tick-text', classes.label)}
363
346
  >
364
347
  <!-- @ts-expect-error - improve types -->
365
348
  {tickFormatProp ? format(tick, asAny(tickFormatProp)) : tick}
@@ -371,50 +354,26 @@
371
354
  y1={0}
372
355
  x2={scaleConfig.xScale?.(tick)}
373
356
  y2={height + tickLengthProp}
374
- class={cls(layerClass('legend-tick-line'), 'stroke-surface-content', classes.tick)}
357
+ class={cls('lc-legend-tick-line', classes.tick)}
375
358
  />
376
359
  {/if}
377
360
  {/each}
378
361
  </g>
379
362
  </svg>
380
363
  {:else if variant === 'swatches'}
381
- <div
382
- class={cls(
383
- layerClass('legend-swatch-group'),
384
- 'flex gap-x-4 gap-y-1',
385
- orientation === 'vertical' && 'flex-col',
386
- classes.items
387
- )}
388
- >
364
+ <div class={cls('lc-legend-swatch-group', classes.items)} data-orientation={orientation}>
389
365
  {#each scaleConfig.tickValues ?? scaleConfig.xScale?.ticks?.(ticks) ?? [] as tick}
390
366
  {@const color = scale?.(tick) ?? ''}
391
367
  {@const item = { value: tick, color }}
392
368
  <button
393
- class={cls(
394
- layerClass('legend-swatch-button'),
395
- 'flex items-center gap-1 truncate',
396
- !onclick && 'cursor-auto',
397
- typeof classes.item === 'function' ? classes.item(item) : classes.item
398
- )}
369
+ class={cls('lc-legend-swatch-button', resolveMaybeFn(classes?.item, item))}
370
+ style:opacity={selected.length === 0 || selected.includes(tick) ? 1 : 0.3}
399
371
  onclick={(e) => onclick?.(e, item)}
400
372
  onpointerenter={(e) => onpointerenter?.(e, item)}
401
373
  onpointerleave={(e) => onpointerleave?.(e, item)}
402
374
  >
403
- <div
404
- class={cls(
405
- layerClass('legend-swatch'),
406
- 'h-4 w-4 shrink-0 rounded-full',
407
- classes.swatch
408
- )}
409
- style:background-color={color}
410
- ></div>
411
- <div
412
- class={cls(
413
- layerClass('legend-swatch-label'),
414
- 'text-xs text-surface-content truncate whitespace-nowrap',
415
- classes.label
416
- )}
417
- >
375
+ <div class={cls('lc-legend-swatch', classes.swatch)} style:background-color={color}></div>
376
+ <div class={cls('lc-legend-swatch-label', classes.label)}>
418
377
  <!-- @ts-expect-error - improve types -->
419
378
  {tickFormatProp ? format(tick, asAny(tickFormatProp)) : tick}
420
379
  </div>
@@ -423,3 +382,110 @@
423
382
  </div>
424
383
  {/if}
425
384
  </div>
385
+
386
+ <style>
387
+ @layer components {
388
+ :where(.lc-legend-container) {
389
+ display: inline-block;
390
+ z-index: 1; /*stack above tooltip context layers (band rects, voronoi, ...) */
391
+
392
+ &[data-placement] {
393
+ position: absolute;
394
+ }
395
+
396
+ &[data-placement='top-left'] {
397
+ top: 0;
398
+ left: 0;
399
+ }
400
+ &[data-placement='top'] {
401
+ top: 0;
402
+ left: 50%;
403
+ transform: translateX(-50%);
404
+ }
405
+ &[data-placement='top-right'] {
406
+ top: 0;
407
+ right: 0;
408
+ }
409
+ &[data-placement='left'] {
410
+ top: 50%;
411
+ left: 0;
412
+ transform: translateY(-50%);
413
+ }
414
+ &[data-placement='center'] {
415
+ top: 50%;
416
+ left: 50%;
417
+ transform: translate(-50%, -50%);
418
+ }
419
+ &[data-placement='right'] {
420
+ top: 50%;
421
+ right: 0;
422
+ transform: translateY(-50%);
423
+ }
424
+ &[data-placement='bottom-left'] {
425
+ bottom: 0;
426
+ left: 0;
427
+ }
428
+ &[data-placement='bottom'] {
429
+ bottom: 0;
430
+ left: 50%;
431
+ transform: translateX(-50%);
432
+ }
433
+ &[data-placement='bottom-right'] {
434
+ bottom: 0;
435
+ right: 0;
436
+ }
437
+ }
438
+
439
+ :where(.lc-legend-title) {
440
+ font-size: 10px;
441
+ font-weight: 600;
442
+ }
443
+
444
+ :where(.lc-legend-ramp-svg) {
445
+ overflow: visible;
446
+ }
447
+
448
+ :where(.lc-legend-tick-text) {
449
+ font-size: 10px;
450
+ fill: var(--color-surface-content, currentColor);
451
+ }
452
+
453
+ :where(.lc-legend-tick-line) {
454
+ stroke: var(--color-surface-content, currentColor);
455
+ }
456
+
457
+ :where(.lc-legend-swatch-group) {
458
+ display: flex;
459
+ gap: 0.25rem 1rem;
460
+
461
+ &[data-orientation='vertical'] {
462
+ flex-direction: column;
463
+ }
464
+ }
465
+
466
+ :where(.lc-legend-swatch-button) {
467
+ display: flex;
468
+ align-items: center;
469
+ gap: 0.25rem;
470
+ white-space: nowrap;
471
+ overflow: hidden;
472
+ text-overflow: ellipsis;
473
+ }
474
+
475
+ :where(.lc-legend-swatch) {
476
+ width: 16px;
477
+ height: 16px;
478
+ flex-shrink: 0;
479
+ border-radius: 9999px; /* full */
480
+ }
481
+
482
+ :where(.lc-legend-swatch-label) {
483
+ font-size: 0.75rem; /* text-xs */
484
+ line-height: calc(1 / 0.75);
485
+ color: var(--color-surface-content, currentColor);
486
+ overflow: hidden;
487
+ text-overflow: ellipsis;
488
+ white-space: nowrap;
489
+ }
490
+ }
491
+ </style>
@@ -54,15 +54,16 @@ export type LegendPropsWithoutHTML = {
54
54
  * @default 'horizontal'
55
55
  */
56
56
  orientation?: 'horizontal' | 'vertical';
57
- onclick?: (e: MouseEvent, detail: LegendItem) => any;
58
- onpointerenter?: (e: MouseEvent, detail: LegendItem) => any;
59
- onpointerleave?: (e: MouseEvent, detail: LegendItem) => any;
60
57
  /**
61
58
  * Determine display ramp (individual color swatches or continuous ramp)
62
59
  *
63
60
  * @default 'ramp'
64
61
  */
65
62
  variant?: 'ramp' | 'swatches';
63
+ /**
64
+ * An array of selected items. If provided, the legend fades unselected items.
65
+ */
66
+ selected?: string[];
66
67
  /**
67
68
  * Classes to apply to the elements.
68
69
  *
@@ -77,6 +78,9 @@ export type LegendPropsWithoutHTML = {
77
78
  swatch?: string;
78
79
  item?: string | ((item: LegendItem) => string);
79
80
  };
81
+ onclick?: (e: MouseEvent, detail: LegendItem) => any;
82
+ onpointerenter?: (e: MouseEvent, detail: LegendItem) => any;
83
+ onpointerleave?: (e: MouseEvent, detail: LegendItem) => any;
80
84
  /**
81
85
  * A bindable reference to the wrapping `<div>` element.
82
86
  *
@@ -4,6 +4,7 @@
4
4
  import { renderPathData, type ComputedStylesOptions } from '../utils/canvas.js';
5
5
  import MarkerWrapper, { type MarkerOptions } from './MarkerWrapper.svelte';
6
6
  import type { CommonStyleProps, Without } from '../utils/types.js';
7
+ import { pointsToAngleAndLength } from '../utils/math.js';
7
8
 
8
9
  export type LinePropsWithoutHTML = {
9
10
  /**
@@ -98,7 +99,6 @@
98
99
 
99
100
  import { createKey } from '../utils/key.svelte.js';
100
101
  import { createId } from '../utils/createId.js';
101
- import { layerClass } from '../utils/attributes.js';
102
102
 
103
103
  const uid = $props.id();
104
104
 
@@ -148,7 +148,7 @@
148
148
  ? merge({ styles: { strokeWidth } }, styleOverrides)
149
149
  : {
150
150
  styles: { fill, stroke, strokeWidth, opacity },
151
- classes: className,
151
+ classes: cls('lc-line', className),
152
152
  }
153
153
  );
154
154
  }
@@ -195,10 +195,47 @@
195
195
  marker-start={markerStartId ? `url(#${markerStartId})` : undefined}
196
196
  marker-mid={markerMidId ? `url(#${markerMidId})` : undefined}
197
197
  marker-end={markerEndId ? `url(#${markerEndId})` : undefined}
198
- class={cls(layerClass('line'), stroke === undefined && 'stroke-surface-content', className)}
198
+ class={cls('lc-line', className)}
199
199
  {...restProps}
200
200
  />
201
201
  <MarkerWrapper id={markerStartId} marker={markerStart ?? marker} />
202
202
  <MarkerWrapper id={markerMidId} marker={markerMid ?? marker} />
203
203
  <MarkerWrapper id={markerEndId} marker={markerEnd ?? marker} />
204
+ {:else if renderCtx === 'html'}
205
+ {@const { angle, length } = pointsToAngleAndLength(
206
+ { x: motionX1.current, y: motionY1.current },
207
+ { x: motionX2.current, y: motionY2.current }
208
+ )}
209
+ <!-- STYLE-TODO: Should html use stroke for fill? -->
210
+ <div
211
+ style:position="absolute"
212
+ style:left="{motionX1.current}px"
213
+ style:top="{motionY1.current}px"
214
+ style:width="{length}px"
215
+ style:height="{strokeWidth ?? 1}px"
216
+ style:transform="translateY(-50%) rotate({angle}deg)"
217
+ style:transform-origin="0 50%"
218
+ style:opacity
219
+ style:background-color={stroke}
220
+ class={cls('lc-line', className)}
221
+ style={restProps.style}
222
+ ></div>
204
223
  {/if}
224
+
225
+ <style>
226
+ @layer base {
227
+ :global(:where(.lc-line)) {
228
+ --stroke-color: var(--color-surface-content, currentColor);
229
+ }
230
+
231
+ /* Svg | Canvas layers */
232
+ :global(:where(.lc-layout-svg .lc-line, svg.lc-line):not([stroke])) {
233
+ stroke: var(--stroke-color);
234
+ }
235
+
236
+ /* Html layers */
237
+ :global(:where(.lc-layout-html .lc-line):not([background-color])) {
238
+ background-color: var(--stroke-color);
239
+ }
240
+ }
241
+ </style>