layerchart 0.78.0 → 0.79.1

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.
@@ -24,6 +24,8 @@
24
24
  import { scaleLinear } from 'd3-scale';
25
25
  import { min, max } from 'd3-array';
26
26
 
27
+ import { objectId } from '@layerstack/utils/object';
28
+
27
29
  import { chartContext } from './ChartContext.svelte';
28
30
  import { motionStore } from '../stores/motionStore.js';
29
31
  import { degreesToRadians } from '../utils/math.js';
@@ -87,6 +89,9 @@
87
89
  export let stroke: string | undefined = undefined;
88
90
  export let strokeWidth: number | undefined = undefined;
89
91
 
92
+ let className: string | undefined = undefined;
93
+ export { className as class };
94
+
90
95
  export let track: boolean | SVGAttributes<SVGPathElement> = false;
91
96
 
92
97
  const { yRange } = chartContext();
@@ -213,19 +218,23 @@
213
218
  // Arc
214
219
  renderPathData(ctx, arc(), {
215
220
  styles: { fill, fillOpacity, stroke, strokeWidth },
216
- classes: $$props.class,
221
+ classes: className,
217
222
  });
218
223
  }
219
224
 
220
- let canvasUnregister: ReturnType<typeof canvasContext.register>;
225
+ // TODO: Use objectId to work around Svelte 4 reactivity issue (even when memoizing gradients)
226
+ $: fillKey = typeof fill === 'object' ? objectId(fill) : fill;
227
+ $: strokeKey = typeof stroke === 'object' ? objectId(stroke) : stroke;
228
+
221
229
  $: if (renderContext === 'canvas') {
222
- canvasUnregister = canvasContext.register({ name: 'Arc', render });
230
+ // Redraw when props change
231
+ arc && trackArc && fillKey && fillOpacity && strokeKey && strokeWidth && className;
232
+ canvasContext.invalidate();
223
233
  }
224
234
 
235
+ let canvasUnregister: ReturnType<typeof canvasContext.register>;
225
236
  $: if (renderContext === 'canvas') {
226
- // Redraw when props changes (TODO: styles, class, etc)
227
- arc && trackArc;
228
- canvasContext.invalidate();
237
+ canvasUnregister = canvasContext.register({ name: 'Arc', render });
229
238
  }
230
239
 
231
240
  onDestroy(() => {
@@ -253,6 +262,7 @@
253
262
  fill-opacity={fillOpacity}
254
263
  {stroke}
255
264
  stroke-width={strokeWidth}
265
+ class={className}
256
266
  {...$$restProps}
257
267
  on:pointerenter={(e) => tooltip?.show(e, data)}
258
268
  on:pointermove={(e) => tooltip?.show(e, data)}
@@ -7,6 +7,7 @@
7
7
  import { interpolatePath } from 'd3-interpolate-path';
8
8
 
9
9
  import { cls } from '@layerstack/tailwind';
10
+ import { objectId } from '@layerstack/utils/object';
10
11
 
11
12
  import { motionStore } from '../stores/motionStore.js';
12
13
 
@@ -59,6 +60,9 @@
59
60
  export let stroke: string | undefined = undefined;
60
61
  export let strokeWidth: number | undefined = undefined;
61
62
 
63
+ let className: string | undefined = undefined;
64
+ export { className as class };
65
+
62
66
  $: xAccessor = x ? accessor(x) : $contextX;
63
67
  $: y0Accessor = y0 ? accessor(y0) : (d: any) => min($yDomain);
64
68
  $: y1Accessor = y1 ? accessor(y1) : $y;
@@ -139,10 +143,20 @@
139
143
  function render(ctx: CanvasRenderingContext2D) {
140
144
  renderPathData(ctx, $tweened_d, {
141
145
  styles: { fill, fillOpacity, stroke, strokeWidth },
142
- classes: $$props.class,
146
+ classes: className,
143
147
  });
144
148
  }
145
149
 
150
+ // TODO: Use objectId to work around Svelte 4 reactivity issue (even when memoizing gradients)
151
+ $: fillKey = typeof fill === 'object' ? objectId(fill) : fill;
152
+ $: strokeKey = typeof stroke === 'object' ? objectId(stroke) : stroke;
153
+
154
+ $: if (renderContext === 'canvas') {
155
+ // Redraw when props change
156
+ fillKey && fillOpacity && strokeKey && strokeWidth && className;
157
+ canvasContext.invalidate();
158
+ }
159
+
146
160
  let canvasUnregister: ReturnType<typeof canvasContext.register>;
147
161
  $: if (renderContext === 'canvas') {
148
162
  canvasUnregister = canvasContext.register({ name: 'Area', render });
@@ -182,7 +196,7 @@
182
196
  {stroke}
183
197
  stroke-width={strokeWidth}
184
198
  {...$$restProps}
185
- class={cls('path-area', $$props.class)}
199
+ class={cls('path-area', className)}
186
200
  on:click
187
201
  on:pointermove
188
202
  on:pointerleave
@@ -2,6 +2,7 @@
2
2
  import { onDestroy, tick } from 'svelte';
3
3
  import type { spring as springStore, tweened as tweenedStore } from 'svelte/motion';
4
4
  import { cls } from '@layerstack/tailwind';
5
+ import { objectId } from '@layerstack/utils/object';
5
6
 
6
7
  import { motionStore } from '../stores/motionStore.js';
7
8
  import { getCanvasContext } from './layout/Canvas.svelte';
@@ -25,6 +26,9 @@
25
26
  export let stroke: string | undefined = undefined;
26
27
  export let strokeWidth: number | undefined = undefined;
27
28
 
29
+ let className: string | undefined = undefined;
30
+ export { className as class };
31
+
28
32
  let tweened_cx = motionStore(initialCx, { spring, tweened });
29
33
  let tweened_cy = motionStore(initialCy, { spring, tweened });
30
34
  let tweened_r = motionStore(initialR, { spring, tweened });
@@ -46,15 +50,26 @@
46
50
  });
47
51
  }
48
52
 
49
- let canvasUnregister: ReturnType<typeof canvasContext.register>;
53
+ // TODO: Use objectId to work around Svelte 4 reactivity issue (even when memoizing gradients)
54
+ $: fillKey = fill && typeof fill === 'object' ? objectId(fill) : fill;
55
+ $: strokeKey = stroke && typeof stroke === 'object' ? objectId(stroke) : stroke;
56
+
50
57
  $: if (renderContext === 'canvas') {
51
- canvasUnregister = canvasContext.register({ name: 'Circle', render });
58
+ // Redraw when props changes
59
+ $tweened_cx &&
60
+ $tweened_cy &&
61
+ $tweened_r &&
62
+ fillKey &&
63
+ fillOpacity &&
64
+ strokeKey &&
65
+ strokeWidth &&
66
+ className;
67
+ canvasContext.invalidate();
52
68
  }
53
69
 
70
+ let canvasUnregister: ReturnType<typeof canvasContext.register>;
54
71
  $: if (renderContext === 'canvas') {
55
- // Redraw when props changes (TODO: styles, class, etc)
56
- $tweened_cx && $tweened_cy && $tweened_r;
57
- canvasContext.invalidate();
72
+ canvasUnregister = canvasContext.register({ name: 'Circle', render });
58
73
  }
59
74
 
60
75
  onDestroy(() => {
@@ -15,6 +15,7 @@
15
15
  import { geoCurvePath } from '../utils/geo.js';
16
16
  import { renderPathData } from '../utils/canvas.js';
17
17
  import { getCanvasContext } from './layout/Canvas.svelte';
18
+ import { objectId } from '@layerstack/utils/object';
18
19
 
19
20
  export let geojson: GeoPermissibleObjects | null | undefined = undefined;
20
21
 
@@ -89,17 +90,21 @@
89
90
  }
90
91
  }
91
92
 
92
- let canvasUnregister: ReturnType<typeof canvasContext.register>;
93
- $: if (renderContext === 'canvas') {
94
- canvasUnregister = canvasContext.register({ name: 'GeoPath', render: _render });
95
- }
93
+ // TODO: Use objectId to work around Svelte 4 reactivity issue (even when memoizing gradients)
94
+ $: fillKey = typeof fill === 'object' ? objectId(fill) : fill;
95
+ $: strokeKey = typeof stroke === 'object' ? objectId(stroke) : stroke;
96
96
 
97
97
  $: if (renderContext === 'canvas') {
98
98
  // Redraw when geojson, projection, or class change
99
- geojson && _projection && className && fill && stroke && strokeWidth;
99
+ geojson && _projection && fillKey && strokeKey && strokeWidth && className;
100
100
  canvasContext.invalidate();
101
101
  }
102
102
 
103
+ let canvasUnregister: ReturnType<typeof canvasContext.register>;
104
+ $: if (renderContext === 'canvas') {
105
+ canvasUnregister = canvasContext.register({ name: 'GeoPath', render: _render });
106
+ }
107
+
103
108
  onDestroy(() => {
104
109
  if (renderContext === 'canvas') {
105
110
  canvasUnregister();
@@ -46,14 +46,14 @@
46
46
  });
47
47
  }
48
48
 
49
- let canvasUnregister: ReturnType<typeof canvasContext.register>;
50
49
  $: if (renderContext === 'canvas') {
51
- canvasUnregister = canvasContext.register({ name: 'GeoTile', render });
50
+ tile;
51
+ canvasContext.invalidate();
52
52
  }
53
53
 
54
+ let canvasUnregister: ReturnType<typeof canvasContext.register>;
54
55
  $: if (renderContext === 'canvas') {
55
- tile;
56
- canvasContext.invalidate();
56
+ canvasUnregister = canvasContext.register({ name: 'GeoTile', render });
57
57
  }
58
58
 
59
59
  onDestroy(() => {
@@ -53,14 +53,14 @@
53
53
  ctx.translate($tweened_x ?? 0, $tweened_y ?? 0);
54
54
  }
55
55
 
56
- let canvasUnregister: ReturnType<typeof canvasContext.register>;
57
56
  $: if (renderContext === 'canvas') {
58
- canvasUnregister = canvasContext.register({ name: 'Group', render, retainState: true });
57
+ $tweened_x && $tweened_y;
58
+ canvasContext.invalidate();
59
59
  }
60
60
 
61
+ let canvasUnregister: ReturnType<typeof canvasContext.register>;
61
62
  $: if (renderContext === 'canvas') {
62
- $tweened_x && $tweened_y;
63
- canvasContext.invalidate();
63
+ canvasUnregister = canvasContext.register({ name: 'Group', render, retainState: true });
64
64
  }
65
65
 
66
66
  onDestroy(() => {
@@ -2,9 +2,10 @@
2
2
  import { onDestroy, tick, type ComponentProps } from 'svelte';
3
3
  import type { spring as springStore, tweened as tweenedStore } from 'svelte/motion';
4
4
  import { cls } from '@layerstack/tailwind';
5
+ import { uniqueId } from '@layerstack/utils';
6
+ import { objectId } from '@layerstack/utils/object';
5
7
 
6
8
  import { motionStore } from '../stores/motionStore.js';
7
- import { uniqueId } from '@layerstack/utils';
8
9
 
9
10
  import Marker from './Marker.svelte';
10
11
  import { renderPathData } from '../utils/canvas.js';
@@ -26,6 +27,9 @@
26
27
  export let stroke: string | undefined = undefined;
27
28
  export let strokeWidth: number | undefined = undefined;
28
29
 
30
+ let className: string | undefined = undefined;
31
+ export { className as class };
32
+
29
33
  /** Marker to attach to start and end points of path */
30
34
  export let marker: ComponentProps<Marker>['type'] | ComponentProps<Marker> | undefined =
31
35
  undefined;
@@ -61,19 +65,30 @@
61
65
  const pathData = `M ${$tweened_x1},${$tweened_y1} L ${$tweened_x2},${$tweened_y2}`;
62
66
  renderPathData(ctx, pathData, {
63
67
  styles: { fill, stroke, strokeWidth },
64
- classes: $$props.class,
68
+ classes: className,
65
69
  });
66
70
  }
67
71
 
68
- let canvasUnregister: ReturnType<typeof canvasContext.register>;
72
+ // TODO: Use objectId to work around Svelte 4 reactivity issue (even when memoizing gradients)
73
+ $: fillKey = typeof fill === 'object' ? objectId(fill) : fill;
74
+ $: strokeKey = typeof stroke === 'object' ? objectId(stroke) : stroke;
75
+
69
76
  $: if (renderContext === 'canvas') {
70
- canvasUnregister = canvasContext.register({ name: 'Line', render });
77
+ // Redraw when props change
78
+ $tweened_x1 &&
79
+ $tweened_y1 &&
80
+ $tweened_x2 &&
81
+ $tweened_y2 &&
82
+ fillKey &&
83
+ strokeKey &&
84
+ strokeWidth &&
85
+ className;
86
+ canvasContext.invalidate();
71
87
  }
72
88
 
89
+ let canvasUnregister: ReturnType<typeof canvasContext.register>;
73
90
  $: if (renderContext === 'canvas') {
74
- // Redraw when props changes (TODO: styles, class, etc)
75
- $tweened_x1 && $tweened_y1 && $tweened_x2 && $tweened_y2;
76
- canvasContext.invalidate();
91
+ canvasUnregister = canvasContext.register({ name: 'Line', render });
77
92
  }
78
93
 
79
94
  onDestroy(() => {
@@ -95,8 +110,8 @@
95
110
  stroke-width={strokeWidth}
96
111
  marker-start={markerStartId ? `url(#${markerStartId})` : undefined}
97
112
  marker-end={markerEndId ? `url(#${markerEndId})` : undefined}
98
- class={cls($$props.stroke === undefined && 'stroke-surface-content')}
99
113
  {...$$restProps}
114
+ class={cls($$props.stroke === undefined && 'stroke-surface-content', className)}
100
115
  on:click
101
116
  on:pointermove
102
117
  on:pointerleave
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { chartContext } from './ChartContext.svelte';
6
6
  import { getCanvasContext } from './layout/Canvas.svelte';
7
- import { getComputedStyles } from '../utils/canvas.js';
7
+ import { createLinearGradient, getComputedStyles } from '../utils/canvas.js';
8
8
  import { parsePercent } from '../utils/math.js';
9
9
 
10
10
  /** Unique id for linearGradient */
@@ -36,43 +36,45 @@
36
36
  let canvasGradient: CanvasGradient;
37
37
 
38
38
  function render(ctx: CanvasRenderingContext2D) {
39
- // TODO: Use x1/y1/x2/y2 values (convert from pecentage strings)
40
- const gradient = ctx.createLinearGradient(
41
- $padding.left,
42
- $padding.top,
43
- vertical ? $padding.left : $width - $padding.right,
44
- vertical ? $height + $padding.bottom : $padding.top
45
- );
46
-
47
39
  // Use `getComputedStyles()` to convert each stop (if using CSS variables and/or classes) to color values
48
- stops.forEach((stop, i) => {
40
+ const _stops = stops.map((stop, i) => {
49
41
  if (Array.isArray(stop)) {
50
42
  const { fill } = getComputedStyles(ctx.canvas, {
51
43
  styles: { fill: stop[1] },
52
44
  classes: $$props.class,
53
45
  });
54
- gradient.addColorStop(parsePercent(stop[0]), fill);
46
+ return { offset: parsePercent(stop[0]), color: fill };
55
47
  } else {
56
48
  const { fill } = getComputedStyles(ctx.canvas, {
57
49
  styles: { fill: stop },
58
50
  classes: $$props.class,
59
51
  });
60
- gradient.addColorStop(i / (stops.length - 1), fill);
52
+ return { offset: i / (stops.length - 1), color: fill };
61
53
  }
62
54
  });
63
55
 
56
+ // TODO: Use x1/y1/x2/y2 values (convert from pecentage strings)
57
+ const gradient = createLinearGradient(
58
+ ctx,
59
+ $padding.left,
60
+ $padding.top,
61
+ vertical ? $padding.left : $width - $padding.right,
62
+ vertical ? $height + $padding.bottom : $padding.top,
63
+ _stops
64
+ );
65
+
64
66
  canvasGradient = gradient;
65
67
  }
66
68
 
67
- let canvasUnregister: ReturnType<typeof canvasContext.register>;
68
69
  $: if (renderContext === 'canvas') {
69
- canvasUnregister = canvasContext.register({ name: 'Gradient', render });
70
+ // Redraw when props changes (TODO: styles, class, etc)
71
+ x1 && y1 && x2 && y2 && stops;
72
+ canvasContext.invalidate();
70
73
  }
71
74
 
75
+ let canvasUnregister: ReturnType<typeof canvasContext.register>;
72
76
  $: if (renderContext === 'canvas') {
73
- // Redraw when props changes (TODO: styles, class, etc)
74
- stops && x1 && y1 && x2 && y2 && $width && $height;
75
- canvasContext.invalidate();
77
+ canvasUnregister = canvasContext.register({ name: 'Gradient', render });
76
78
  }
77
79
 
78
80
  onDestroy(() => {
@@ -41,6 +41,7 @@
41
41
 
42
42
  function render(ctx: CanvasRenderingContext2D) {
43
43
  // TODO: Set correct values: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createRadialGradient. See also: LinearGradient
44
+ // TODO: Memoize `createRadialGradient()` (see LinearGradient)
44
45
  const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, 0);
45
46
 
46
47
  // Use `getComputedStyles()` to convert each stop (if using CSS variables and/or classes) to color values
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { onDestroy, tick } from 'svelte';
3
3
  import { cls } from '@layerstack/tailwind';
4
+ import { objectId } from '@layerstack/utils/object';
4
5
 
5
6
  import {
6
7
  motionStore,
@@ -28,6 +29,9 @@
28
29
  export let stroke: string | undefined = undefined;
29
30
  export let strokeWidth: number | undefined = undefined;
30
31
 
32
+ let className: string | undefined = undefined;
33
+ export { className as class };
34
+
31
35
  export let spring: boolean | SpringOptions | { [prop: string]: SpringOptions } = undefined;
32
36
  export let tweened: boolean | TweenedOptions | { [prop: string]: TweenedOptions } = undefined;
33
37
 
@@ -52,20 +56,31 @@
52
56
  { x: $tweened_x, y: $tweened_y, width: $tweened_width, height: $tweened_height },
53
57
  {
54
58
  styles: { fill, fillOpacity, stroke, strokeWidth },
55
- classes: $$props.class,
59
+ classes: className,
56
60
  }
57
61
  );
58
62
  }
59
63
 
60
- let canvasUnregister: ReturnType<typeof canvasContext.register>;
64
+ // TODO: Use objectId to work around Svelte 4 reactivity issue (even when memoizing gradients)
65
+ $: fillKey = typeof fill === 'object' ? objectId(fill) : fill;
66
+ $: strokeKey = typeof stroke === 'object' ? objectId(stroke) : stroke;
67
+
61
68
  $: if (renderContext === 'canvas') {
62
- canvasUnregister = canvasContext.register({ name: 'Rect', render });
69
+ // Redraw when props change
70
+ $tweened_x &&
71
+ $tweened_y &&
72
+ $tweened_width &&
73
+ $tweened_height &&
74
+ fillKey &&
75
+ strokeKey &&
76
+ strokeWidth &&
77
+ className;
78
+ canvasContext.invalidate();
63
79
  }
64
80
 
81
+ let canvasUnregister: ReturnType<typeof canvasContext.register>;
65
82
  $: if (renderContext === 'canvas') {
66
- // Redraw when props changes (TODO: styles, class, etc)
67
- $tweened_x && $tweened_y && $tweened_width && $tweened_height;
68
- canvasContext.invalidate();
83
+ canvasUnregister = canvasContext.register({ name: 'Rect', render });
69
84
  }
70
85
 
71
86
  onDestroy(() => {
@@ -83,7 +98,7 @@
83
98
  y={$tweened_y}
84
99
  width={$tweened_width}
85
100
  height={$tweened_height}
86
- class={cls($$props.fill == null && 'fill-surface-content')}
101
+ class={cls(fill == null && 'fill-surface-content')}
87
102
  {fill}
88
103
  fill-opacity={fillOpacity}
89
104
  {stroke}
@@ -12,6 +12,7 @@
12
12
  import { max } from 'd3-array';
13
13
  import { cls } from '@layerstack/tailwind';
14
14
  import { uniqueId } from '@layerstack/utils';
15
+ import { objectId } from '@layerstack/utils/object';
15
16
 
16
17
  import { chartContext } from './ChartContext.svelte';
17
18
  import Group from './Group.svelte';
@@ -68,6 +69,9 @@
68
69
  export let strokeWidth: number | undefined = undefined;
69
70
  export let opacity: number | undefined = undefined;
70
71
 
72
+ let className: string | undefined = undefined;
73
+ export { className as class };
74
+
71
75
  /** Marker to attach to start, mid, and end points of path */
72
76
  export let marker: ComponentProps<Marker>['type'] | ComponentProps<Marker> | undefined =
73
77
  undefined;
@@ -166,17 +170,23 @@
166
170
  function render(ctx: CanvasRenderingContext2D) {
167
171
  renderPathData(ctx, $tweened_d, {
168
172
  styles: { stroke, fill, strokeWidth, opacity },
169
- classes: $$props.class,
173
+ classes: className,
170
174
  });
171
175
  }
172
176
 
177
+ // TODO: Use objectId to work around Svelte 4 reactivity issue (even when memoizing gradients)
178
+ $: fillKey = typeof fill === 'object' ? objectId(fill) : fill;
179
+ $: strokeKey = typeof stroke === 'object' ? objectId(stroke) : stroke;
180
+
181
+ $: if (renderContext === 'canvas') {
182
+ // Redraw when props change
183
+ $tweened_d && fillKey && strokeKey && strokeWidth && className;
184
+ canvasContext.invalidate();
185
+ }
186
+
173
187
  let canvasUnregister: ReturnType<typeof canvasContext.register>;
174
188
  $: if (renderContext === 'canvas') {
175
189
  canvasUnregister = canvasContext.register({ name: 'Spline', render });
176
-
177
- tweened_d.subscribe(() => {
178
- canvasContext.invalidate();
179
- });
180
190
  }
181
191
 
182
192
  onDestroy(() => {
@@ -225,12 +235,7 @@
225
235
  <path
226
236
  d={$tweened_d}
227
237
  {...$$restProps}
228
- class={cls(
229
- 'path-line',
230
- !$$props.fill && 'fill-none',
231
- !$$props.stroke && 'stroke-surface-content',
232
- $$props.class
233
- )}
238
+ class={cls('path-line', !fill && 'fill-none', !stroke && 'stroke-surface-content', className)}
234
239
  {fill}
235
240
  {stroke}
236
241
  stroke-width={strokeWidth}
@@ -2,6 +2,7 @@
2
2
  import { onDestroy, tick } from 'svelte';
3
3
  import type { spring as springStore, tweened as tweenedStore } from 'svelte/motion';
4
4
  import { cls } from '@layerstack/tailwind';
5
+ import { objectId } from '@layerstack/utils/object';
5
6
 
6
7
  import { getStringWidth } from '../utils/string.js';
7
8
  import { motionStore } from '../stores/motionStore.js';
@@ -65,6 +66,9 @@
65
66
  export let stroke: string | undefined = undefined;
66
67
  export let strokeWidth: number | undefined = undefined;
67
68
 
69
+ let className: string | undefined = undefined;
70
+ export { className as class };
71
+
68
72
  let wordsByLines: { words: string[]; width?: number }[] = [];
69
73
  let wordsWithWidth: { word: string; width: number }[] = [];
70
74
  let spaceWidth: number = 0;
@@ -192,21 +196,25 @@
192
196
  },
193
197
  {
194
198
  styles: { fill, fillOpacity, stroke, strokeWidth, paintOrder: 'stroke', textAnchor },
195
- classes: cls(fill === undefined && 'fill-surface-content', $$props.class),
199
+ classes: cls(fill === undefined && 'fill-surface-content', className),
196
200
  }
197
201
  );
198
202
  });
199
203
  }
200
204
 
201
- let canvasUnregister: ReturnType<typeof canvasContext.register>;
205
+ // TODO: Use objectId to work around Svelte 4 reactivity issue (even when memoizing gradients)
206
+ $: fillKey = typeof fill === 'object' ? objectId(fill) : fill;
207
+ $: strokeKey = typeof stroke === 'object' ? objectId(stroke) : stroke;
208
+
202
209
  $: if (renderContext === 'canvas') {
203
- canvasUnregister = canvasContext.register({ name: 'Text', render });
210
+ // Redraw when props change
211
+ value && $tweened_x && $tweened_y && fillKey && strokeKey && strokeWidth && className;
212
+ canvasContext.invalidate();
204
213
  }
205
214
 
215
+ let canvasUnregister: ReturnType<typeof canvasContext.register>;
206
216
  $: if (renderContext === 'canvas') {
207
- // Redraw when props changes (TODO: styles, class, etc)
208
- value && $tweened_x && $tweened_y;
209
- canvasContext.invalidate();
217
+ canvasUnregister = canvasContext.register({ name: 'Text', render });
210
218
  }
211
219
 
212
220
  onDestroy(() => {
@@ -231,7 +239,7 @@
231
239
  fill-opacity={fillOpacity}
232
240
  {stroke}
233
241
  stroke-width={strokeWidth}
234
- class={cls(fill === undefined && 'fill-surface-content', $$props.class)}
242
+ class={cls(fill === undefined && 'fill-surface-content', className)}
235
243
  >
236
244
  {#each wordsByLines as line, index}
237
245
  <tspan x={$tweened_x} dy={index === 0 ? startDy : lineHeight}>
@@ -1,3 +1,5 @@
1
+ import { cls } from '@layerstack/tailwind';
2
+ import { memoize } from 'lodash-es';
1
3
  export const DEFAULT_FILL = 'rgb(0, 0, 0)';
2
4
  const CANVAS_STYLES_ELEMENT_ID = '__layerchart_canvas_styles_id';
3
5
  /**
@@ -23,7 +25,10 @@ export function getComputedStyles(canvas, { styles, classes } = {}) {
23
25
  Object.assign(svg.style, styles);
24
26
  }
25
27
  if (classes) {
26
- svg.setAttribute('class', classes);
28
+ svg.setAttribute('class', cls(classes)
29
+ .split(' ')
30
+ .filter((s) => !s.startsWith('transition-'))
31
+ .join(' '));
27
32
  }
28
33
  const computedStyles = window.getComputedStyle(svg);
29
34
  return computedStyles;
@@ -35,6 +40,7 @@ export function getComputedStyles(canvas, { styles, classes } = {}) {
35
40
  }
36
41
  /** Render onto canvas context. Supports CSS variables and classes by tranferring to hidden `<svg>` element before retrieval) */
37
42
  function render(canvasCtx, render, styleOptions = {}) {
43
+ // console.count('render');
38
44
  // TODO: Consider memoizing? How about reactiving to CSS variable changes (light/dark mode toggle)
39
45
  const computedStyles = getComputedStyles(canvasCtx.canvas, styleOptions);
40
46
  // Adhere to CSS paint order: https://developer.mozilla.org/en-US/docs/Web/CSS/paint-order
@@ -77,9 +83,9 @@ function render(canvasCtx, render, styleOptions = {}) {
77
83
  : computedStyles?.fill;
78
84
  if (fill) {
79
85
  const currentGlobalAlpha = canvasCtx.globalAlpha;
80
- if (computedStyles?.fillOpacity) {
81
- canvasCtx.globalAlpha = Number(computedStyles?.fillOpacity);
82
- }
86
+ const fillOpacity = Number(computedStyles?.fillOpacity);
87
+ const opacity = Number(computedStyles?.opacity);
88
+ canvasCtx.globalAlpha = fillOpacity * opacity;
83
89
  canvasCtx.fillStyle = fill;
84
90
  render.fill(canvasCtx);
85
91
  // Restore in case it was modified by `fillOpacity`
@@ -144,3 +150,15 @@ export function scaleCanvas(ctx, width, height) {
144
150
  ctx.scale(devicePixelRatio, devicePixelRatio);
145
151
  return { width: ctx.canvas.width, height: ctx.canvas.height };
146
152
  }
153
+ export function _createLinearGradient(canvasCtx, x0, y0, x1, y1, stops) {
154
+ const gradient = canvasCtx.createLinearGradient(x0, y0, x1, y1);
155
+ stops.forEach(({ offset, color }) => {
156
+ gradient.addColorStop(offset, color);
157
+ });
158
+ return gradient;
159
+ }
160
+ /** Create linear gradient and memoize result to fix reactivity */
161
+ export const createLinearGradient = memoize(_createLinearGradient, (canvasCtx, x0, y0, x1, y1, stops) => {
162
+ const key = JSON.stringify({ x0, y0, x1, y1, stops });
163
+ return key;
164
+ });
package/package.json CHANGED
@@ -4,17 +4,17 @@
4
4
  "author": "Sean Lynch <techniq35@gmail.com>",
5
5
  "license": "MIT",
6
6
  "repository": "techniq/layerchart",
7
- "version": "0.78.0",
7
+ "version": "0.79.1",
8
8
  "devDependencies": {
9
- "@changesets/cli": "^2.27.10",
9
+ "@changesets/cli": "^2.27.12",
10
10
  "@mdi/js": "^7.4.47",
11
11
  "@rollup/plugin-dsv": "^3.0.5",
12
- "@sveltejs/adapter-cloudflare": "^4.8.0",
13
- "@sveltejs/kit": "^2.11.1",
14
- "@sveltejs/package": "^2.3.7",
15
- "@sveltejs/vite-plugin-svelte": "^5.0.2",
12
+ "@sveltejs/adapter-cloudflare": "^5.0.1",
13
+ "@sveltejs/kit": "^2.16.1",
14
+ "@sveltejs/package": "^2.3.9",
15
+ "@sveltejs/vite-plugin-svelte": "^5.0.3",
16
16
  "@svitejs/changesets-changelog-github-compact": "^1.2.0",
17
- "@tailwindcss/typography": "^0.5.15",
17
+ "@tailwindcss/typography": "^0.5.16",
18
18
  "@types/d3-array": "^3.2.1",
19
19
  "@types/d3-color": "^3.1.3",
20
20
  "@types/d3-delaunay": "^6.0.4",
@@ -30,7 +30,7 @@
30
30
  "@types/d3-sankey": "^0.12.4",
31
31
  "@types/d3-scale": "^4.0.8",
32
32
  "@types/d3-scale-chromatic": "^3.1.0",
33
- "@types/d3-shape": "^3.1.6",
33
+ "@types/d3-shape": "^3.1.7",
34
34
  "@types/d3-time": "^3.0.4",
35
35
  "@types/lodash-es": "^4.17.12",
36
36
  "@types/prismjs": "^1.26.5",
@@ -39,31 +39,31 @@
39
39
  "@types/topojson-simplify": "^3.0.3",
40
40
  "@types/topojson-specification": "^1.0.5",
41
41
  "autoprefixer": "^10.4.20",
42
- "marked": "^15.0.3",
42
+ "marked": "^15.0.6",
43
43
  "mdsvex": "^0.12.3",
44
- "posthog-js": "^1.203.1",
44
+ "posthog-js": "^1.210.2",
45
45
  "prettier": "^3.4.2",
46
- "prettier-plugin-svelte": "^3.3.2",
46
+ "prettier-plugin-svelte": "^3.3.3",
47
47
  "prism-svelte": "^0.5.0",
48
48
  "prism-themes": "^1.9.0",
49
49
  "prismjs": "^1.29.0",
50
50
  "rehype-slug": "^6.0.0",
51
51
  "shapefile": "^0.6.6",
52
52
  "solar-calculator": "^0.3.0",
53
- "svelte": "^5.13.0",
54
- "svelte-check": "^4.1.1",
53
+ "svelte": "^5.19.3",
54
+ "svelte-check": "^4.1.4",
55
55
  "svelte-json-tree": "^2.2.0",
56
- "svelte-ux": "^0.76.0",
57
- "svelte2tsx": "^0.7.30",
56
+ "svelte-ux": "^0.90.0",
57
+ "svelte2tsx": "^0.7.34",
58
58
  "tailwindcss": "^3.4.16",
59
59
  "topojson-client": "^3.1.0",
60
60
  "topojson-simplify": "^3.0.3",
61
61
  "tslib": "^2.8.1",
62
- "typescript": "^5.7.2",
62
+ "typescript": "^5.7.3",
63
63
  "unist-util-visit": "^5.0.0",
64
64
  "us-atlas": "^3.0.1",
65
- "vite": "^6.0.3",
66
- "vitest": "^2.1.8"
65
+ "vite": "^6.0.11",
66
+ "vitest": "^3.0.4"
67
67
  },
68
68
  "type": "module",
69
69
  "dependencies": {
@@ -1,46 +0,0 @@
1
- export declare const DEFAULT_FILL = "rgb(0, 0, 0)";
2
- type ComputedStylesOptions = {
3
- styles?: Partial<Omit<CSSStyleDeclaration, 'fillOpacity' | 'strokeWidth' | 'opacity'> & {
4
- fillOpacity?: number | string;
5
- strokeWidth?: number | string;
6
- opacity?: number | string;
7
- }>;
8
- classes?: string;
9
- };
10
- /**
11
- * Appends or reuses `<svg>` element below `<canvas>` to resolve CSS variables and classes (ex. `stroke: hsl(var(--color-primary))` => `stroke: rgb(...)` )
12
- */
13
- export declare function getComputedStyles(canvas: HTMLCanvasElement, { styles, classes }?: ComputedStylesOptions): CSSStyleDeclaration;
14
- /** Render SVG path data onto canvas context. Supports CSS variables and classes by tranferring to hidden `<svg>` element before retrieval) */
15
- export declare function renderPathData(canvasCtx: CanvasRenderingContext2D, pathData: string | null | undefined, styleOptions?: ComputedStylesOptions): void;
16
- export declare function renderText(canvasCtx: CanvasRenderingContext2D, text: string | number | null | undefined, coords: {
17
- x: number;
18
- y: number;
19
- }, styleOptions?: ComputedStylesOptions): void;
20
- export declare function renderRect(canvasCtx: CanvasRenderingContext2D, coords: {
21
- x: number;
22
- y: number;
23
- width: number;
24
- height: number;
25
- }, styleOptions?: ComputedStylesOptions): void;
26
- /** Clear canvas accounting for Canvas `context.translate(...)` */
27
- export declare function clearCanvasContext(canvasCtx: CanvasRenderingContext2D, options: {
28
- containerWidth: number;
29
- containerHeight: number;
30
- padding: {
31
- top: number;
32
- bottom: number;
33
- left: number;
34
- right: number;
35
- };
36
- }): void;
37
- /**
38
- Scales a canvas for high DPI / retina displays.
39
- @see: https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#examples
40
- @see: https://web.dev/articles/canvas-hidipi
41
- */
42
- export declare function scaleCanvas(ctx: CanvasRenderingContext2D, width: number, height: number): {
43
- width: number;
44
- height: number;
45
- };
46
- export {};