svelteplot 0.2.0 → 0.2.2

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 (45) hide show
  1. package/dist/Mark.svelte +12 -1
  2. package/dist/helpers/index.d.ts +1 -0
  3. package/dist/helpers/index.js +1 -0
  4. package/dist/helpers/resolve.d.ts +1 -1
  5. package/dist/helpers/resolve.js +6 -5
  6. package/dist/helpers/scales.d.ts +2 -2
  7. package/dist/helpers/scales.js +5 -4
  8. package/dist/helpers/typeChecks.js +14 -10
  9. package/dist/index.d.ts +3 -1
  10. package/dist/index.js +4 -2
  11. package/dist/marks/BarX.svelte +11 -37
  12. package/dist/marks/BarY.svelte +27 -58
  13. package/dist/marks/BarY.svelte.d.ts +2 -8
  14. package/dist/marks/Cell.svelte +12 -36
  15. package/dist/marks/ColorLegend.svelte +6 -10
  16. package/dist/marks/Dot.svelte +2 -2
  17. package/dist/marks/Geo.svelte +50 -41
  18. package/dist/marks/Geo.svelte.d.ts +3 -1
  19. package/dist/marks/GridX.svelte +2 -2
  20. package/dist/marks/GridY.svelte +2 -2
  21. package/dist/marks/Line.svelte +98 -80
  22. package/dist/marks/Line.svelte.d.ts +5 -3
  23. package/dist/marks/Pointer.svelte +2 -1
  24. package/dist/marks/Rect.svelte +10 -24
  25. package/dist/marks/helpers/CanvasLayer.svelte +10 -16
  26. package/dist/marks/helpers/CanvasLayer.svelte.d.ts +2 -6
  27. package/dist/marks/helpers/DotCanvas.svelte +72 -159
  28. package/dist/marks/helpers/DotCanvas.svelte.d.ts +2 -4
  29. package/dist/marks/helpers/GeoCanvas.svelte +95 -145
  30. package/dist/marks/helpers/GeoCanvas.svelte.d.ts +3 -5
  31. package/dist/marks/helpers/LineCanvas.svelte +116 -0
  32. package/dist/marks/helpers/LineCanvas.svelte.d.ts +12 -0
  33. package/dist/marks/helpers/LinearGradientX.svelte +27 -0
  34. package/dist/marks/helpers/LinearGradientX.svelte.d.ts +11 -0
  35. package/dist/marks/helpers/LinearGradientY.svelte +27 -0
  36. package/dist/marks/helpers/LinearGradientY.svelte.d.ts +11 -0
  37. package/dist/marks/helpers/RectPath.svelte +129 -0
  38. package/dist/marks/helpers/RectPath.svelte.d.ts +27 -0
  39. package/dist/marks/helpers/canvas.d.ts +1 -0
  40. package/dist/marks/helpers/canvas.js +34 -0
  41. package/dist/transforms/recordize.d.ts +1 -0
  42. package/dist/transforms/recordize.js +16 -5
  43. package/dist/transforms/stack.js +10 -7
  44. package/dist/types.d.ts +12 -6
  45. package/package.json +19 -17
package/dist/Mark.svelte CHANGED
@@ -18,6 +18,7 @@
18
18
  ScaledDataRecord,
19
19
  ScaleType
20
20
  } from './types.js';
21
+ import { isEqual } from 'es-toolkit';
21
22
  import { getUsedScales, projectXY, projectX, projectY } from './helpers/scales.js';
22
23
  import { testFilter, isValid } from './helpers/index.js';
23
24
  import { resolveChannel, resolveProp } from './helpers/resolve.js';
@@ -105,6 +106,13 @@
105
106
 
106
107
  let added = false;
107
108
 
109
+ $effect(() => {
110
+ const prevOptions = untrack(() => mark.options);
111
+ if (!isEqual(prevOptions, optionsWithAutoFacet)) {
112
+ mark.options = optionsWithAutoFacet;
113
+ }
114
+ });
115
+
108
116
  $effect(() => {
109
117
  if (added) return;
110
118
  // without using untrack() here we end up with inexplicable
@@ -118,6 +126,7 @@
118
126
  );
119
127
  mark.data = untrack(() => data);
120
128
  mark.options = untrack(() => optionsWithAutoFacet);
129
+
121
130
  addMark(mark);
122
131
  added = true;
123
132
  });
@@ -225,6 +234,7 @@
225
234
  usedScales.y,
226
235
  suffix
227
236
  );
237
+
228
238
  out[`x${suffix}`] = x;
229
239
  out[`y${suffix}`] = y;
230
240
  out.valid =
@@ -243,7 +253,7 @@
243
253
  ScaleName
244
254
  ][]) {
245
255
  // check if the mark has defined an accessor for this channel
246
- if (options?.[channel] !== undefined && out[channel] === undefined) {
256
+ if (options?.[channel] != null && out[channel] === undefined) {
247
257
  // resolve value
248
258
  const value = row[channel];
249
259
 
@@ -256,6 +266,7 @@
256
266
  : value;
257
267
 
258
268
  out.valid = out.valid && isValid(value);
269
+
259
270
  // apply dx/dy transform
260
271
  out[channel] =
261
272
  scale === 'x' && Number.isFinite(scaled) ? (scaled as number) + dx : scaled;
@@ -16,3 +16,4 @@ export declare const POSITION_CHANNELS: Set<ChannelName>;
16
16
  export declare function parseInset(inset: number | string, width: number): number;
17
17
  export declare function omit<T extends {}, K extends keyof T>(obj: T, ...keys: K[]): Omit<T, K>;
18
18
  export declare function identity<T>(x: T): T;
19
+ export declare const GEOJSON_PREFER_STROKE: Set<string>;
@@ -53,3 +53,4 @@ export function omit(obj, ...keys) {
53
53
  export function identity(x) {
54
54
  return x;
55
55
  }
56
+ export const GEOJSON_PREFER_STROKE = new Set(['MultiLineString', 'LineString']);
@@ -3,7 +3,7 @@ import type { ScaleName, ChannelName, ScaledChannelName, ChannelAccessor, DataRo
3
3
  type ChannelAlias = {
4
4
  channel: ScaledChannelName;
5
5
  };
6
- export declare function resolveProp<T>(accessor: ConstantAccessor<T>, datum: DataRecord | null, _defaultValue?: T | null): T | null;
6
+ export declare function resolveProp<T>(accessor: ConstantAccessor<T>, datum: DataRecord | null, _defaultValue?: T | null): T | typeof _defaultValue;
7
7
  type ChannelOptions = {
8
8
  value: ChannelAccessor;
9
9
  scale?: ScaleName | null;
@@ -4,16 +4,17 @@ import isRawValue from './isRawValue.js';
4
4
  import { isValid } from './isValid.js';
5
5
  import { pick } from 'es-toolkit';
6
6
  import { getBaseStylesObject } from './getBaseStyles.js';
7
+ import { RAW_VALUE } from '../transforms/recordize.js';
7
8
  export function resolveProp(accessor, datum, _defaultValue = null) {
8
9
  if (typeof accessor === 'function') {
9
- // datum.___orig___ exists if an array of raw values was used as dataset and got
10
+ // datum[RAW_VALUE] exists if an array of raw values was used as dataset and got
10
11
  // "recordized" by the recordize transform. We want to hide this wrapping to the user
11
12
  // so we're passing the original value to accessor functions instead of our wrapped record
12
13
  return datum == null
13
14
  ? accessor()
14
- : accessor(datum.___orig___ != null ? datum.___orig___ : datum);
15
+ : accessor(datum[RAW_VALUE] != null ? datum[RAW_VALUE] : datum);
15
16
  }
16
- else if (typeof accessor === 'string' && datum && datum[accessor] !== undefined) {
17
+ else if ((typeof accessor === 'string' || typeof accessor === 'symbol') && datum && datum[accessor] !== undefined) {
17
18
  return datum[accessor];
18
19
  }
19
20
  return isRawValue(accessor) ? accessor : _defaultValue;
@@ -46,10 +47,10 @@ function resolve(datum, accessor, channel, scale) {
46
47
  if (isDataRecord(datum)) {
47
48
  // use accessor function
48
49
  if (typeof accessor === 'function')
49
- // datum.___orig___ exists if an array of raw values was used as dataset and got
50
+ // datum[RAW_VALUE] exists if an array of raw values was used as dataset and got
50
51
  // "recordized" by the recordize transform. We want to hide this wrapping to the user
51
52
  // so we're passing the original value to accessor functions instead of our wrapped record
52
- return accessor(datum.___orig___ != null ? datum.___orig___ : datum);
53
+ return accessor(datum[RAW_VALUE] != null ? datum[RAW_VALUE] : datum);
53
54
  // use accessor string
54
55
  if ((typeof accessor === 'string' || typeof accessor === 'symbol') && datum[accessor] !== undefined)
55
56
  return datum[accessor];
@@ -1,4 +1,4 @@
1
- import type { ChannelAccessor, GenericMarkOptions, Mark, MarkType, PlotDefaults, PlotOptions, PlotScales, PlotState, RawValue, ScaleName, ScaleOptions, ScaleType, ScaledChannelName } from '../types.js';
1
+ import type { ChannelAccessor, GenericMarkOptions, Mark, MarkType, PlotDefaults, PlotOptions, PlotScales, PlotState, RawValue, ScaleName, ScaleOptions, ScaleType, ScaledChannelName, UsedScales } from '../types.js';
2
2
  /**
3
3
  * compute the plot scales
4
4
  */
@@ -35,7 +35,7 @@ export declare function inferScaleType(name: ScaleName, dataValues: RawValue[],
35
35
  * scales, we need to check if the the scale is supposed to be used
36
36
  * not. That's what this function is used for.
37
37
  */
38
- export declare function getUsedScales(plot: PlotState, options: GenericMarkOptions, mark: Mark<GenericMarkOptions>): { [k in ScaledChannelName]: boolean; };
38
+ export declare function getUsedScales(plot: PlotState, options: GenericMarkOptions, mark: Mark<GenericMarkOptions>): UsedScales;
39
39
  export declare function looksLikeANumber(input: string | number): boolean;
40
40
  export declare function projectXY(scales: PlotScales, x: RawValue, y: RawValue, useXScale?: boolean, useYScale?: boolean): [number, number];
41
41
  export declare function projectX(channel: 'x' | 'x1' | 'x2', scales: PlotScales, value: RawValue): number;
@@ -113,7 +113,7 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
113
113
  for (const datum of mark.data) {
114
114
  const value = resolveProp(channelOptions.value, datum);
115
115
  dataValues.add(value);
116
- if (name === 'color' && scaleOptions.type === 'quantile') {
116
+ if (name === 'color' && scaleOptions.type === 'quantile' || scaleOptions.type === 'quantile-cont') {
117
117
  allDataValues.push(value);
118
118
  }
119
119
  }
@@ -146,6 +146,7 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
146
146
  if (isOrdinal && sortOrdinalDomain) {
147
147
  valueArr.sort(ascending);
148
148
  }
149
+ const valueArray = type === 'quantile' || type === 'quantile-cont' ? allDataValues.toSorted() : valueArr;
149
150
  const domain = scaleOptions.domain
150
151
  ? isOrdinal
151
152
  ? scaleOptions.domain
@@ -157,9 +158,9 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
157
158
  type === 'quantile' ||
158
159
  type === 'quantile-cont'
159
160
  ? name === 'y'
160
- ? valueArr.toReversed()
161
- : valueArr
162
- : extent(scaleOptions.zero ? [0, ...valueArr] : valueArr);
161
+ ? valueArray.toReversed()
162
+ : valueArray
163
+ : extent(scaleOptions.zero ? [0, ...valueArray] : valueArray);
163
164
  if (!scaleOptions.scale) {
164
165
  throw new Error(`No scale function defined for ${name}`);
165
166
  }
@@ -26,16 +26,20 @@ export function isSymbolOrNull(v) {
26
26
  return v == null || ((typeof v === 'string' || typeof v === 'object') && isSymbol(v));
27
27
  }
28
28
  export function isColorOrNull(v) {
29
- return (v == null ||
30
- (typeof v === 'string' &&
31
- (v === 'currentColor' ||
32
- CSS_VAR.test(v) ||
33
- CSS_COLOR.test(v) ||
34
- CSS_COLOR_MIX.test(v) ||
35
- CSS_COLOR_CONTRAST.test(v) ||
36
- CSS_RGBA.test(v) ||
37
- CSS_URL.test(v) ||
38
- color(v) !== null)));
29
+ if (v == null)
30
+ return true;
31
+ if (typeof v === 'string') {
32
+ v = `${v}`.toLowerCase();
33
+ return (v === 'currentcolor' ||
34
+ CSS_VAR.test(v) ||
35
+ CSS_COLOR.test(v) ||
36
+ CSS_COLOR_MIX.test(v) ||
37
+ CSS_COLOR_CONTRAST.test(v) ||
38
+ CSS_RGBA.test(v) ||
39
+ CSS_URL.test(v) ||
40
+ color(v) !== null);
41
+ }
42
+ return false;
39
43
  }
40
44
  export function isOpacityOrNull(v) {
41
45
  return v == null || (typeof v === 'number' && Number.isFinite(v) && v >= 0 && v <= 1);
package/dist/index.d.ts CHANGED
@@ -58,10 +58,12 @@ export { normalizeX, normalizeY } from './transforms/normalize.js';
58
58
  export { group, groupX, groupY, groupZ } from './transforms/group.js';
59
59
  export { intervalX, intervalY } from './transforms/interval.js';
60
60
  export { recordizeX, recordizeY } from './transforms/recordize.js';
61
- export { renameChannels } from './transforms/rename.js';
61
+ export { renameChannels, replaceChannels } from './transforms/rename.js';
62
62
  export { select, selectFirst, selectLast, selectMaxX, selectMaxY, selectMinX, selectMinY } from './transforms/select.js';
63
63
  export { shiftX, shiftY } from './transforms/shift.js';
64
64
  export { sort, shuffle, reverse } from './transforms/sort.js';
65
65
  export { stackX, stackY } from './transforms/stack.js';
66
66
  export { windowX, windowY } from './transforms/window.js';
67
67
  export { formatMonth } from './helpers/formats.js';
68
+ export { default as LinearGradientX } from './marks/helpers/LinearGradientX.svelte';
69
+ export { default as LinearGradientY } from './marks/helpers/LinearGradientY.svelte';
package/dist/index.js CHANGED
@@ -61,11 +61,13 @@ export { normalizeX, normalizeY } from './transforms/normalize.js';
61
61
  export { group, groupX, groupY, groupZ } from './transforms/group.js';
62
62
  export { intervalX, intervalY } from './transforms/interval.js';
63
63
  export { recordizeX, recordizeY } from './transforms/recordize.js';
64
- export { renameChannels } from './transforms/rename.js';
64
+ export { renameChannels, replaceChannels } from './transforms/rename.js';
65
65
  export { select, selectFirst, selectLast, selectMaxX, selectMaxY, selectMinX, selectMinY } from './transforms/select.js';
66
66
  export { shiftX, shiftY } from './transforms/shift.js';
67
67
  export { sort, shuffle, reverse } from './transforms/sort.js';
68
68
  export { stackX, stackY } from './transforms/stack.js';
69
69
  export { windowX, windowY } from './transforms/window.js';
70
- // format helpers
70
+ // helpers
71
71
  export { formatMonth } from './helpers/formats.js';
72
+ export { default as LinearGradientX } from './marks/helpers/LinearGradientX.svelte';
73
+ export { default as LinearGradientY } from './marks/helpers/LinearGradientY.svelte';
@@ -6,13 +6,11 @@
6
6
  import Mark from '../Mark.svelte';
7
7
  import { getContext } from 'svelte';
8
8
  import { stackX, recordizeX, intervalX, sort } from '../index.js';
9
- import { resolveProp, resolveStyles } from '../helpers/resolve.js';
10
- import { roundedRect } from '../helpers/roundedRect.js';
11
9
  import type { PlotContext, BaseMarkProps, RectMarkProps, ChannelAccessor } from '../types.js';
12
10
  import type { StackOptions } from '../transforms/stack.js';
13
11
  import type { DataRow } from '../types.js';
14
- import { addEventHandlers } from './helpers/events.js';
15
12
  import GroupMultiple from './helpers/GroupMultiple.svelte';
13
+ import RectPath from './helpers/RectPath.svelte';
16
14
 
17
15
  type BarXProps = BaseMarkProps & {
18
16
  data: DataRow[];
@@ -21,14 +19,6 @@
21
19
  x2?: ChannelAccessor;
22
20
  y?: ChannelAccessor;
23
21
  stack?: StackOptions;
24
- borderRadius?:
25
- | number
26
- | {
27
- topLeft?: number;
28
- topRight?: number;
29
- bottomRight?: number;
30
- bottomLeft?: number;
31
- };
32
22
  } & RectMarkProps;
33
23
 
34
24
  let { data = [{}], class: className = null, stack, ...options }: BarXProps = $props();
@@ -59,33 +49,17 @@
59
49
  {@const bw = plot.scales.y.fn.bandwidth()}
60
50
  {@const minx = Math.min(d.x1, d.x2)}
61
51
  {@const maxx = Math.max(d.x1, d.x2)}
62
- {@const insetLeft = resolveProp(args.insetLeft, d.datum, 0)}
63
- {@const insetRight = resolveProp(args.insetRight, d.datum, 0)}
64
- {@const insetTop = resolveProp(args.insetTop || args.inset, d.datum, 0)}
65
- {@const insetBottom = resolveProp(args.insetBottom || args.inset, d.datum, 0)}
66
- {@const dx = resolveProp(args.dx, d.datum, 0)}
67
- {@const dy = resolveProp(args.dy, d.datum, 0)}
68
52
  {#if d.valid}
69
- {@const [style, styleClass] = resolveStyles(plot, d, args, 'fill', usedScales)}
70
- <path
71
- d={roundedRect(
72
- 0,
73
- 0,
74
- maxx - minx - insetLeft - insetRight,
75
- bw - insetTop - insetBottom,
76
- options.borderRadius
77
- )}
78
- class={[styleClass, className]}
79
- {style}
80
- transform="translate({[
81
- minx + dx + insetLeft,
82
- d.y + insetTop + dy - bw * 0.5
83
- ]})"
84
- use:addEventHandlers={{
85
- getPlotState,
86
- options: args,
87
- datum: d.datum
88
- }} />
53
+ <RectPath
54
+ {usedScales}
55
+ class={className}
56
+ {options}
57
+ datum={d}
58
+ x={minx}
59
+ useInsetAsFallbackHorizontally={false}
60
+ y={d.y - bw * 0.5}
61
+ width={maxx - minx}
62
+ height={bw} />
89
63
  {/if}
90
64
  {/each}
91
65
  </GroupMultiple>
@@ -6,40 +6,25 @@
6
6
  import Mark from '../Mark.svelte';
7
7
  import { getContext } from 'svelte';
8
8
  import { intervalY, stackY, recordizeY, sort } from '../index.js';
9
- import { resolveProp, resolveStyles } from '../helpers/resolve.js';
10
- import { roundedRect } from '../helpers/roundedRect.js';
11
- import {
12
- type PlotContext,
13
- type BaseMarkProps,
14
- type ChannelAccessor,
15
- type DataRow
16
- } from '../types.js';
9
+ import type { PlotContext, BaseMarkProps, BaseRectMarkProps } from '../types.js';
17
10
  import type { StackOptions } from '../transforms/stack.js';
18
- import { maybeData } from '../helpers/index.js';
19
- import { addEventHandlers } from './helpers/events.js';
20
11
  import GroupMultiple from './helpers/GroupMultiple.svelte';
12
+ import RectPath from './helpers/RectPath.svelte';
21
13
 
22
- type BarYProps = BaseMarkProps & {
23
- data: DataRow[];
24
- x?: ChannelAccessor;
25
- y?: ChannelAccessor;
26
- y1?: ChannelAccessor;
27
- y2?: ChannelAccessor;
28
- stack?: StackOptions;
29
- /**
30
- * Converts y into y1/y2 ranges based on the provided interval. Disables the
31
- * implicit stacking
32
- */
33
- interval?: number | string;
34
- borderRadius?:
35
- | number
36
- | {
37
- topLeft?: number;
38
- topRight?: number;
39
- bottomRight?: number;
40
- bottomLeft?: number;
41
- };
42
- };
14
+ type BarYProps = BaseMarkProps &
15
+ BaseRectMarkProps & {
16
+ data: DataRow[];
17
+ x?: ChannelAccessor;
18
+ y?: ChannelAccessor;
19
+ y1?: ChannelAccessor;
20
+ y2?: ChannelAccessor;
21
+ stack?: StackOptions;
22
+ /**
23
+ * Converts y into y1/y2 ranges based on the provided interval. Disables the
24
+ * implicit stacking
25
+ */
26
+ interval?: number | string;
27
+ };
43
28
 
44
29
  let { data = [{}], class: className = null, stack, ...options }: BarYProps = $props();
45
30
 
@@ -63,39 +48,23 @@
63
48
  requiredScales={{ x: ['band'] }}
64
49
  channels={['x', 'y1', 'y2', 'fill', 'stroke', 'opacity', 'fillOpacity', 'strokeOpacity']}
65
50
  {...args}>
66
- {#snippet children({ mark, scaledData, usedScales })}
51
+ {#snippet children({ scaledData, usedScales })}
67
52
  <GroupMultiple class="bar-y" length={scaledData.length}>
68
53
  {#each scaledData as d}
69
54
  {@const bw = plot.scales.x.fn.bandwidth()}
70
55
  {@const miny = Math.min(d.y1, d.y2)}
71
56
  {@const maxy = Math.max(d.y1, d.y2)}
72
- {@const insetLeft = resolveProp(args.insetLeft || args.inset, d.datum, 0)}
73
- {@const insetRight = resolveProp(args.insetRight || args.inset, d.datum, 0)}
74
- {@const insetTop = resolveProp(args.insetTop, d.datum, 0)}
75
- {@const insetBottom = resolveProp(args.insetBottom, d.datum, 0)}
76
- {@const dx = resolveProp(args.dx, d.datum, 0)}
77
- {@const dy = resolveProp(args.dy, d.datum, 0)}
78
57
  {#if d.valid}
79
- {@const [style, styleClass] = resolveStyles(plot, d, args, 'fill', usedScales)}
80
- <path
81
- d={roundedRect(
82
- 0,
83
- 0,
84
- bw - insetLeft - insetRight,
85
- maxy - miny - insetTop - insetBottom,
86
- options.borderRadius
87
- )}
88
- class={[styleClass, className]}
89
- {style}
90
- transform="translate({[
91
- d.x + insetLeft + dx - bw * 0.5,
92
- miny + dy + insetTop
93
- ]})"
94
- use:addEventHandlers={{
95
- getPlotState,
96
- options: args,
97
- datum: d.datum
98
- }} />
58
+ <RectPath
59
+ x={d.x - bw * 0.5}
60
+ y={miny}
61
+ options={args}
62
+ class={className}
63
+ width={bw}
64
+ height={maxy - miny}
65
+ datum={d}
66
+ {usedScales}
67
+ useInsetAsFallbackVertically={false} />
99
68
  {/if}
100
69
  {/each}
101
70
  </GroupMultiple>
@@ -1,6 +1,6 @@
1
- import { type BaseMarkProps, type ChannelAccessor, type DataRow } from '../types.js';
1
+ import type { BaseMarkProps, BaseRectMarkProps } from '../types.js';
2
2
  import type { StackOptions } from '../transforms/stack.js';
3
- type BarYProps = BaseMarkProps & {
3
+ type BarYProps = BaseMarkProps & BaseRectMarkProps & {
4
4
  data: DataRow[];
5
5
  x?: ChannelAccessor;
6
6
  y?: ChannelAccessor;
@@ -12,12 +12,6 @@ type BarYProps = BaseMarkProps & {
12
12
  * implicit stacking
13
13
  */
14
14
  interval?: number | string;
15
- borderRadius?: number | {
16
- topLeft?: number;
17
- topRight?: number;
18
- bottomRight?: number;
19
- bottomLeft?: number;
20
- };
21
15
  };
22
16
  /** For vertical column charts using a band scale as x axis */
23
17
  declare const BarY: import("svelte").Component<BarYProps, {}, "">;
@@ -6,9 +6,7 @@
6
6
  import Mark from '../Mark.svelte';
7
7
  import { getContext } from 'svelte';
8
8
  import { recordizeY, sort } from '../index.js';
9
- import { roundedRect } from '../helpers/roundedRect.js';
10
- import { resolveChannel, resolveProp, resolveStyles } from '../helpers/resolve.js';
11
- import { coalesce, maybeNumber } from '../helpers/index.js';
9
+ import { resolveChannel } from '../helpers/resolve.js';
12
10
  import type {
13
11
  PlotContext,
14
12
  DataRecord,
@@ -17,7 +15,7 @@
17
15
  ChannelAccessor
18
16
  } from '../types.js';
19
17
  import { isValid } from '../helpers/isValid.js';
20
- import { addEventHandlers } from './helpers/events.js';
18
+ import RectPath from './helpers/RectPath.svelte';
21
19
 
22
20
  type CellProps = BaseMarkProps & {
23
21
  data: DataRecord[];
@@ -59,43 +57,21 @@
59
57
  requiredScales={{ x: ['band'], y: ['band'] }}
60
58
  channels={['x', 'y', 'fill', 'stroke', 'opacity', 'fillOpacity', 'strokeOpacity']}
61
59
  {...args}>
62
- {#snippet children({ mark, scaledData, usedScales })}
60
+ {#snippet children({ scaledData, usedScales })}
63
61
  {@const bwx = plot.scales.x.fn.bandwidth()}
64
62
  {@const bwy = plot.scales.y.fn.bandwidth()}
65
63
  <g class="cell {className || ''}" data-fill={usedScales.fillOpacity}>
66
64
  {#each scaledData as d}
67
- {@const inset = resolveProp(args.inset, d.datum, 0)}
68
- {@const insetLeft = resolveProp(args.insetLeft, d.datum)}
69
- {@const insetRight = resolveProp(args.insetRight, d.datum)}
70
- {@const insetTop = resolveProp(args.insetTop, d.datum)}
71
- {@const insetBottom = resolveProp(args.insetBottom, d.datum)}
72
- {@const dx = resolveProp(args.dx, d.datum, 0)}
73
- {@const dy = resolveProp(args.dy, d.datum, 0)}
74
- {@const insetL = maybeNumber(coalesce(insetLeft, inset, 0))}
75
- {@const insetT = maybeNumber(coalesce(insetTop, inset, 0))}
76
- {@const insetR = maybeNumber(coalesce(insetRight, inset, 0))}
77
- {@const insetB = maybeNumber(coalesce(insetBottom, inset, 0))}
78
65
  {#if d.valid && (args.fill == null || isValid(resolveChannel('fill', d.datum, args)))}
79
- {@const [style, styleClass] = resolveStyles(plot, d, args, 'fill', usedScales)}
80
- <path
81
- d={roundedRect(
82
- 0,
83
- 0,
84
- bwx - insetL - insetR,
85
- bwy - insetT - insetB,
86
- options.borderRadius
87
- )}
88
- class={[styleClass]}
89
- {style}
90
- transform="translate({[
91
- d.x + insetL + dx - bwx * 0.5,
92
- d.y + insetT + dy - bwy * 0.5
93
- ]})"
94
- use:addEventHandlers={{
95
- getPlotState,
96
- options: args,
97
- datum: d.datum
98
- }} />
66
+ <RectPath
67
+ datum={d}
68
+ class={className}
69
+ {usedScales}
70
+ options={args}
71
+ x={d.x - bwx * 0.5}
72
+ y={d.y - bwy * 0.5}
73
+ width={bwx}
74
+ height={bwy} />
99
75
  {/if}
100
76
  {/each}
101
77
  </g>
@@ -2,7 +2,7 @@
2
2
  import { getContext } from 'svelte';
3
3
  import { Plot, AxisX, Frame } from '../index.js';
4
4
  import { symbol as d3Symbol, symbol } from 'd3-shape';
5
- import { range as d3Range } from 'd3-array';
5
+ import { range as d3Range, extent } from 'd3-array';
6
6
  import { maybeSymbol } from '../helpers/symbols.js';
7
7
 
8
8
  import type { DefaultOptions, PlotContext } from '../types.js';
@@ -72,7 +72,7 @@
72
72
  </div>
73
73
  {/each}
74
74
  {:else if scaleType === 'quantile' || scaleType === 'quantize' || scaleType === 'threshold'}
75
- {@const domain = plot.scales.color.domain}
75
+ {@const domain = extent(plot.scales.color.fn.domain())}
76
76
  {@const range = plot.scales.color.range}
77
77
  {@const tickLabels =
78
78
  scaleType === 'quantile'
@@ -85,7 +85,6 @@
85
85
  domain[1],
86
86
  (domain[1] - domain[0]) / range.length
87
87
  ).slice(1)}
88
-
89
88
  <Plot
90
89
  maxWidth="240px"
91
90
  margins={1}
@@ -112,16 +111,13 @@
112
111
  </linearGradient>
113
112
  </defs>
114
113
  <Frame dy={-5} stroke={null} fill="url(#gradient-{randId})" />
115
- <AxisX tickSize={18} dy={-17} />
114
+ <AxisX tickSize={18} dy={-17} tickFormat={(d, i) => tickFormat(tickLabels[i])} />
116
115
  </Plot>
117
116
  {:else}
118
117
  <!--- continuous -->
119
- {@const domain = plot.scales.color.domain}
120
- {@const ticks = new Set([
121
- domain[0],
122
- ...(plot.scales.color?.fn?.ticks?.(Math.ceil(width / 5)) ?? []),
123
- domain[1]
124
- ])}
118
+ {@const domain = extent(plot.scales.color.domain)}
119
+ {@const ticks = d3Range(domain[0], domain[1], (domain[1] - domain[0]) / 7).slice(1)}
120
+
125
121
  <Plot
126
122
  maxWidth="240px"
127
123
  margins={1}
@@ -53,7 +53,7 @@
53
53
  }: DotProps = $props();
54
54
 
55
55
  const { getPlotState } = getContext<PlotContext>('svelteplot');
56
- let plot = $derived(getPlotState());
56
+ const plot = $derived(getPlotState());
57
57
 
58
58
  function getSymbolPath(symbolType, size) {
59
59
  return d3Symbol(maybeSymbol(symbolType), size)();
@@ -96,7 +96,7 @@
96
96
  {#snippet children({ mark, usedScales, scaledData })}
97
97
  <g class="dots {className || ''}">
98
98
  {#if canvas}
99
- <DotCanvas data={args.data} {mark} {plot} {testFacet} {usedScales} />
99
+ <DotCanvas data={scaledData} {mark} />
100
100
  {:else}
101
101
  {#each scaledData as d}
102
102
  {#if d.valid && isValid(d.r)}