layerchart 2.0.0-next.33 → 2.0.0-next.35

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.
@@ -439,8 +439,8 @@
439
439
  {#if rule !== false}
440
440
  {@const ruleProps = extractLayerProps(rule, 'axis-rule')}
441
441
  <Rule
442
- x={placement === 'left' || placement === 'right' ? placement : placement === 'angle'}
443
- y={placement === 'top' || placement === 'bottom' ? placement : placement === 'radius'}
442
+ x={placement === 'left' ? '$left' : placement === 'right' ? '$right' : placement === 'angle'}
443
+ y={placement === 'top' ? '$top' : placement === 'bottom' ? '$bottom' : placement === 'radius'}
444
444
  {motion}
445
445
  {...ruleProps}
446
446
  class={cls('stroke-surface-content/50', classes.rule, ruleProps?.class)}
@@ -32,8 +32,8 @@
32
32
  import Bar, { type BarProps, type BarPropsWithoutHTML } from './Bar.svelte';
33
33
  import Group from './Group.svelte';
34
34
 
35
- import { chartDataArray } from '../utils/common.js';
36
35
  import { getChartContext } from './Chart.svelte';
36
+ import { chartDataArray } from '../utils/common.js';
37
37
  import { extractLayerProps, layerClass } from '../utils/attributes.js';
38
38
 
39
39
  let {
@@ -1,9 +1,10 @@
1
1
  <script lang="ts" module>
2
- import { scaleLinear, scaleOrdinal, scaleSqrt } from 'd3-scale';
2
+ import { scaleOrdinal, scaleSqrt } from 'd3-scale';
3
3
  import { type Accessor, accessor, chartDataArray } from '../utils/common.js';
4
4
  import { printDebug } from '../utils/debug.js';
5
5
  import { filterObject } from '../utils/filterObject.js';
6
6
  import {
7
+ autoScale,
7
8
  createScale,
8
9
  getRange,
9
10
  isScaleBand,
@@ -421,21 +422,21 @@
421
422
  /**
422
423
  * The D3 scale that should be used for the x-dimension. Pass in an instantiated D3 scale if
423
424
  * you want to override the default or you want to extra options.
424
- * @default scaleLinear
425
+ * @default autoScale
425
426
  */
426
427
  xScale?: XScale;
427
428
 
428
429
  /**
429
430
  * The D3 scale that should be used for the x-dimension. Pass in an instantiated D3 scale if
430
431
  * you want to override the default or you want to extra options.
431
- * @default scaleLinear
432
+ * @default autoScale
432
433
  */
433
434
  yScale?: YScale;
434
435
 
435
436
  /**
436
437
  * The D3 scale that should be used for the x-dimension. Pass in an instantiated D3 scale if
437
438
  * you want to override the default or you want to extra options.
438
- * @default scaleLinear
439
+ * @default autoScale
439
440
  */
440
441
  zScale?: AnyScale;
441
442
 
@@ -449,14 +450,14 @@
449
450
  /**
450
451
  * The D3 scale that should be used for the x1-dimension. Pass in an instantiated D3 scale if
451
452
  * you want to override the default or you want to extra options.
452
- * @default scaleLinear
453
+ * @default autoScale
453
454
  */
454
455
  x1Scale?: AnyScale;
455
456
 
456
457
  /**
457
458
  * The D3 scale that should be used for the y1-dimension. Pass in an instantiated D3 scale if
458
459
  * you want to override the default or you want to extra options.
459
- * @default scaleLinear
460
+ * @default autoScale
460
461
  */
461
462
  y1Scale?: AnyScale;
462
463
 
@@ -717,6 +718,7 @@
717
718
  z: zProp,
718
719
  r: rProp,
719
720
  data = [],
721
+ flatData: flatDataProp,
720
722
  xDomain: xDomainProp,
721
723
  yDomain: yDomainProp,
722
724
  zDomain: zDomainProp,
@@ -730,12 +732,12 @@
730
732
  zPadding,
731
733
  rPadding,
732
734
  // @ts-expect-error shh
733
- xScale: xScaleProp = scaleLinear(),
735
+ xScale: xScaleProp = autoScale(xDomainProp, flatDataProp ?? data, xProp),
736
+ // @ts-expect-error shh
737
+ yScale: yScaleProp = autoScale(yDomainProp, flatDataProp ?? data, yProp),
734
738
  // @ts-expect-error shh
735
- yScale: yScaleProp = scaleLinear(),
736
- zScale: zScaleProp = scaleLinear(),
739
+ zScale: zScaleProp = autoScale(zDomainProp, flatDataProp ?? data, zProp),
737
740
  rScale: rScaleProp = scaleSqrt(),
738
- flatData: flatDataProp,
739
741
  padding: paddingProp = {},
740
742
  verbose = true,
741
743
  debug = false,
@@ -1009,24 +1011,36 @@
1009
1011
  const rGet = $derived(createGetter(r, rScale));
1010
1012
 
1011
1013
  const x1Scale = $derived(
1012
- x1ScaleProp && x1RangeProp
1013
- ? createScale(x1ScaleProp, x1Domain, x1RangeProp, {
1014
- xScale: xScale,
1015
- width,
1016
- height,
1017
- })
1014
+ x1RangeProp
1015
+ ? createScale(
1016
+ // @ts-expect-error shh
1017
+ x1ScaleProp ?? autoScale(x1DomainProp, flatDataProp ?? data, x1Prop),
1018
+ x1Domain,
1019
+ x1RangeProp,
1020
+ {
1021
+ xScale,
1022
+ width,
1023
+ height,
1024
+ }
1025
+ )
1018
1026
  : null
1019
1027
  );
1020
1028
 
1021
1029
  const x1Get = $derived(createGetter(x1, x1Scale));
1022
1030
 
1023
1031
  const y1Scale = $derived(
1024
- y1ScaleProp && y1RangeProp
1025
- ? createScale(y1ScaleProp, y1Domain, y1RangeProp, {
1026
- yScale: yScale,
1027
- width,
1028
- height,
1029
- })
1032
+ y1RangeProp
1033
+ ? createScale(
1034
+ // @ts-expect-error shh
1035
+ y1ScaleProp ?? autoScale(y1DomainProp, flatDataProp ?? data, y1Prop),
1036
+ y1Domain,
1037
+ y1RangeProp,
1038
+ {
1039
+ yScale,
1040
+ width,
1041
+ height,
1042
+ }
1043
+ )
1030
1044
  : null
1031
1045
  );
1032
1046
 
@@ -288,19 +288,19 @@ export type ChartPropsWithoutHTML<T, XScale extends AnyScale = AnyScale, YScale
288
288
  /**
289
289
  * The D3 scale that should be used for the x-dimension. Pass in an instantiated D3 scale if
290
290
  * you want to override the default or you want to extra options.
291
- * @default scaleLinear
291
+ * @default autoScale
292
292
  */
293
293
  xScale?: XScale;
294
294
  /**
295
295
  * The D3 scale that should be used for the x-dimension. Pass in an instantiated D3 scale if
296
296
  * you want to override the default or you want to extra options.
297
- * @default scaleLinear
297
+ * @default autoScale
298
298
  */
299
299
  yScale?: YScale;
300
300
  /**
301
301
  * The D3 scale that should be used for the x-dimension. Pass in an instantiated D3 scale if
302
302
  * you want to override the default or you want to extra options.
303
- * @default scaleLinear
303
+ * @default autoScale
304
304
  */
305
305
  zScale?: AnyScale;
306
306
  /**
@@ -312,13 +312,13 @@ export type ChartPropsWithoutHTML<T, XScale extends AnyScale = AnyScale, YScale
312
312
  /**
313
313
  * The D3 scale that should be used for the x1-dimension. Pass in an instantiated D3 scale if
314
314
  * you want to override the default or you want to extra options.
315
- * @default scaleLinear
315
+ * @default autoScale
316
316
  */
317
317
  x1Scale?: AnyScale;
318
318
  /**
319
319
  * The D3 scale that should be used for the y1-dimension. Pass in an instantiated D3 scale if
320
320
  * you want to override the default or you want to extra options.
321
- * @default scaleLinear
321
+ * @default autoScale
322
322
  */
323
323
  y1Scale?: AnyScale;
324
324
  /**
@@ -1,6 +1,6 @@
1
1
  <script lang="ts" module>
2
2
  import type { CommonStyleProps, Without } from '../utils/types.js';
3
- import type { ComponentProps, Snippet } from 'svelte';
3
+ import type { Snippet } from 'svelte';
4
4
 
5
5
  export type Point = { x: number; y: number; r: number; xValue: any; yValue: any; data: any };
6
6
  type Offset = number | ((value: number, context: any) => number) | undefined;
@@ -37,13 +37,6 @@
37
37
  */
38
38
  offsetY?: Offset;
39
39
 
40
- /**
41
- * Enable showing links between related points (array x/y accessors)
42
- *
43
- * @default false
44
- */
45
- links?: boolean | Partial<ComponentProps<typeof Link>>;
46
-
47
40
  children?: Snippet<[{ points: Point[] }]>;
48
41
  } & CommonStyleProps;
49
42
 
@@ -71,7 +64,6 @@
71
64
  r = 5,
72
65
  offsetX,
73
66
  offsetY,
74
- links = false,
75
67
  fill,
76
68
  fillOpacity,
77
69
  stroke,
@@ -136,66 +128,11 @@
136
128
  return [];
137
129
  }) as Point[]
138
130
  );
139
-
140
- const _links = $derived(
141
- pointsData.flatMap((d: any) => {
142
- const xValue = xAccessor(d);
143
- const yValue = yAccessor(d);
144
-
145
- if (Array.isArray(xValue)) {
146
- /*
147
- x={["prop1" ,"prop2"]}
148
- y="prop3"
149
- */
150
- const [xMin, xMax] = extent(ctx.xGet(d)) as unknown as [number, number];
151
- const y = ctx.yGet(d) + getOffset(ctx.yGet(d), offsetY, ctx.yScale);
152
- return {
153
- source: {
154
- x: xMin + getOffset(xMin, offsetX, ctx.xScale) + (ctx.config.r ? ctx.rGet(d) : r),
155
- y,
156
- },
157
- target: {
158
- x: xMax + getOffset(xMax, offsetX, ctx.xScale) - (ctx.config.r ? ctx.rGet(d) : r),
159
- y: y,
160
- },
161
- data: d,
162
- };
163
- } else if (Array.isArray(yValue)) {
164
- /*
165
- x="prop1"
166
- y={["prop2" ,"prop3"]}
167
- */
168
- const x = ctx.xGet(d) + getOffset(ctx.xGet(d), offsetX, ctx.xScale);
169
- const [yMin, yMax] = extent(ctx.yGet(d)) as unknown as [number, number];
170
- return {
171
- source: {
172
- x: x,
173
- y: yMin + getOffset(yMin, offsetY, ctx.yScale),
174
- },
175
- target: {
176
- x: x,
177
- y: yMax + getOffset(yMax, offsetY, ctx.yScale),
178
- },
179
- data: d,
180
- };
181
- }
182
- })
183
- );
184
131
  </script>
185
132
 
186
133
  {#if children}
187
134
  {@render children({ points })}
188
135
  {:else}
189
- {#if links}
190
- {#each _links as link}
191
- <Link
192
- data={link}
193
- stroke={fill ?? (ctx.config.c ? ctx.cGet(link.data) : null)}
194
- {...extractLayerProps(links, 'points-link')}
195
- />
196
- {/each}
197
- {/if}
198
-
199
136
  {#each points as point}
200
137
  <Circle
201
138
  cx={point.x}
@@ -1,5 +1,5 @@
1
1
  import type { CommonStyleProps, Without } from '../utils/types.js';
2
- import type { ComponentProps, Snippet } from 'svelte';
2
+ import type { Snippet } from 'svelte';
3
3
  export type Point = {
4
4
  x: number;
5
5
  y: number;
@@ -36,19 +36,12 @@ export type PointsPropsWithoutHTML = {
36
36
  * The offset of the point in the y direction
37
37
  */
38
38
  offsetY?: Offset;
39
- /**
40
- * Enable showing links between related points (array x/y accessors)
41
- *
42
- * @default false
43
- */
44
- links?: boolean | Partial<ComponentProps<typeof Link>>;
45
39
  children?: Snippet<[{
46
40
  points: Point[];
47
41
  }]>;
48
42
  } & CommonStyleProps;
49
43
  export type PointsProps = PointsPropsWithoutHTML & Omit<Without<CircleProps, PointsPropsWithoutHTML>, 'ref'>;
50
44
  import { type CircleProps } from './Circle.svelte';
51
- import Link from './Link.svelte';
52
45
  import { type Accessor } from '../utils/common.js';
53
46
  declare const Points: import("svelte").Component<PointsProps, {}, "">;
54
47
  type Points = ReturnType<typeof Points>;
@@ -3,6 +3,11 @@
3
3
  import type { SVGAttributes } from 'svelte/elements';
4
4
 
5
5
  export type BaseRulePropsWithoutHTML = {
6
+ /**
7
+ * Override the data from the context.
8
+ */
9
+ data?: any;
10
+
6
11
  /**
7
12
  * Create a vertical `x` line
8
13
  * - If true or 'left', will draw at chart left (xRange[0])
@@ -12,7 +17,7 @@
12
17
  *
13
18
  * @default false
14
19
  */
15
- x?: number | Date | boolean | 'left' | 'right';
20
+ x?: number | Date | boolean | '$left' | '$right' | Accessor;
16
21
 
17
22
  /**
18
23
  * Pixel offset to apply to `x` coordinate
@@ -30,7 +35,7 @@
30
35
  *
31
36
  * @default false
32
37
  */
33
- y?: number | Date | boolean | 'top' | 'bottom';
38
+ y?: number | Date | boolean | '$top' | '$bottom' | Accessor;
34
39
 
35
40
  /**
36
41
  * Pixel offset to apply to `y` coordinate
@@ -55,13 +60,17 @@
55
60
  import Group from './Group.svelte';
56
61
  import Line, { type LinePropsWithoutHTML } from './Line.svelte';
57
62
  import { getChartContext } from './Chart.svelte';
63
+ import { accessor, chartDataArray, type Accessor } from '../utils/common.js';
58
64
  import { layerClass } from '../utils/attributes.js';
65
+ import { isScaleBand, isScaleNumeric } from '../utils/scales.svelte.js';
59
66
 
60
67
  let {
68
+ data: dataProp,
61
69
  x = false,
62
70
  xOffset = 0,
63
71
  y = false,
64
72
  yOffset = 0,
73
+ stroke: strokeProp,
65
74
  class: className,
66
75
  children,
67
76
  ...restProps
@@ -69,89 +78,163 @@
69
78
 
70
79
  const ctx = getChartContext();
71
80
 
72
- const xRangeMinMax = $derived(extent<number | Date>(ctx.xRange));
73
- const yRangeMinMax = $derived(extent<number | Date>(ctx.yRange));
74
-
75
- function showRule(value: typeof x | typeof y, axis: 'x' | 'y') {
76
- switch (typeof value) {
77
- case 'boolean':
78
- return value;
79
- case 'string':
80
- return true;
81
- default:
82
- if (axis === 'x') {
83
- return ctx.xScale(value) >= xRangeMinMax[0]! && ctx.xScale(value) <= xRangeMinMax[1]!;
84
- } else {
85
- return ctx.yScale(value) >= yRangeMinMax[0]! && ctx.yScale(value) <= yRangeMinMax[1]!;
86
- }
81
+ const data = $derived(chartDataArray(dataProp ?? ctx.data));
82
+
83
+ const singleX = $derived(
84
+ typeof x === 'number' ||
85
+ x instanceof Date ||
86
+ x === true ||
87
+ x === '$left' ||
88
+ x === '$right' ||
89
+ (isScaleBand(ctx.xScale) && ctx.xDomain.includes(x as any))
90
+ );
91
+ const singleY = $derived(
92
+ typeof y === 'number' ||
93
+ y instanceof Date ||
94
+ y === true ||
95
+ y === '$bottom' ||
96
+ y === '$top' ||
97
+ (isScaleBand(ctx.yScale) && ctx.yDomain.includes(y as any))
98
+ );
99
+
100
+ const xRangeMinMax = $derived(extent<number>(ctx.xRange));
101
+ const yRangeMinMax = $derived(extent<number>(ctx.yRange));
102
+
103
+ const lines = $derived.by(() => {
104
+ const result: {
105
+ x1: number;
106
+ y1: number;
107
+ x2: number;
108
+ y2: number;
109
+ axis: 'x' | 'y';
110
+ stroke?: string;
111
+ }[] = [];
112
+
113
+ // Single x line
114
+ if (singleX) {
115
+ const _x =
116
+ x === true || x === '$left'
117
+ ? xRangeMinMax[0]!
118
+ : x === '$right'
119
+ ? xRangeMinMax[1]!
120
+ : ctx.xScale(x) + xOffset;
121
+
122
+ result.push({
123
+ x1: _x,
124
+ y1: ctx.yRange[0] || 0,
125
+ x2: _x,
126
+ y2: ctx.yRange[1] || 0,
127
+ axis: 'x',
128
+ });
129
+ }
130
+
131
+ // Single y line
132
+ if (singleY) {
133
+ const _y =
134
+ y === true || y === '$bottom'
135
+ ? yRangeMinMax[1]!
136
+ : y === '$top'
137
+ ? yRangeMinMax[0]!
138
+ : ctx.yScale(y) + yOffset;
139
+
140
+ result.push({
141
+ x1: ctx.xRange[0] || 0,
142
+ y1: _y,
143
+ x2: ctx.xRange[1] || 0,
144
+ y2: _y,
145
+ axis: 'y',
146
+ });
87
147
  }
88
- }
148
+
149
+ // Data driven lines
150
+ if (!singleX && !singleY) {
151
+ const xAccessor = x !== false ? accessor(x as Accessor) : ctx.x;
152
+ const yAccessor = y !== false ? accessor(y as Accessor) : ctx.y;
153
+
154
+ const xBandOffset = isScaleBand(ctx.xScale) ? ctx.xScale.bandwidth() / 2 : 0;
155
+ const yBandOffset = isScaleBand(ctx.yScale) ? ctx.yScale.bandwidth() / 2 : 0;
156
+
157
+ for (const d of data) {
158
+ const xValue = xAccessor(d);
159
+ const yValue = yAccessor(d);
160
+
161
+ const x1Value = Array.isArray(xValue) ? xValue[0] : isScaleNumeric(ctx.xScale) ? 0 : xValue;
162
+ const x2Value = Array.isArray(xValue) ? xValue[1] : xValue;
163
+ const y1Value = Array.isArray(yValue) ? yValue[0] : isScaleNumeric(ctx.yScale) ? 0 : yValue;
164
+ const y2Value = Array.isArray(yValue) ? yValue[1] : yValue;
165
+
166
+ result.push({
167
+ x1: ctx.xScale(x1Value) + xBandOffset + xOffset,
168
+ y1: ctx.yScale(y1Value) + yBandOffset + yOffset,
169
+ x2: ctx.xScale(x2Value) + xBandOffset + xOffset,
170
+ y2: ctx.yScale(y2Value) + yBandOffset + yOffset,
171
+ axis: Array.isArray(yValue) || isScaleBand(ctx.xScale) ? 'x' : 'y', // TODO: what about single prop like lollipop?
172
+ stroke: (strokeProp ?? ctx.config.c) ? ctx.cGet(d) : null, // use color scale, if available
173
+ });
174
+ }
175
+ }
176
+
177
+ // Remove lines if out of range of chart (non-0 baseline, brushing, etc)
178
+ return result.filter((line) => {
179
+ return (
180
+ line.x1 >= xRangeMinMax[0]! &&
181
+ line.x2 <= xRangeMinMax[1]! &&
182
+ line.y1 >= yRangeMinMax[0]! &&
183
+ line.y2 <= yRangeMinMax[1]!
184
+ );
185
+ });
186
+ });
187
+
188
+ // $inspect({ lines });
89
189
  </script>
90
190
 
91
191
  <Group class={layerClass('rule-g')}>
92
- {#if showRule(x, 'x')}
93
- {@const xCoord =
94
- x === true || x === 'left'
95
- ? xRangeMinMax[0]
96
- : x === 'right'
97
- ? xRangeMinMax[1]
98
- : ctx.xScale(x) + xOffset}
192
+ {#each lines as line}
193
+ {@const stroke = line.stroke}
99
194
 
100
195
  {#if ctx.radial}
101
- {@const [x1, y1] = pointRadial(xCoord, Number(yRangeMinMax[0]))}
102
- {@const [x2, y2] = pointRadial(xCoord, Number(yRangeMinMax[1]))}
103
-
104
- <Line
105
- {...restProps}
106
- {x1}
107
- {y1}
108
- {x2}
109
- {y2}
110
- class={cls(layerClass('rule-x-radial-line'), 'stroke-surface-content/10', className)}
111
- />
196
+ {#if line.axis === 'x'}
197
+ {@const [x1, y1] = pointRadial(line.x1, line.y1)}
198
+ {@const [x2, y2] = pointRadial(line.x2, line.y2)}
199
+ <Line
200
+ {...restProps}
201
+ {x1}
202
+ {y1}
203
+ {x2}
204
+ {y2}
205
+ {stroke}
206
+ class={cls(
207
+ layerClass('rule-x-radial-line'),
208
+ !stroke && 'stroke-surface-content/10',
209
+ className
210
+ )}
211
+ />
212
+ {:else if line.axis === 'y'}
213
+ <Circle
214
+ r={line.y1}
215
+ {stroke}
216
+ class={cls(
217
+ layerClass('rule-y-radial-circle'),
218
+ !stroke && 'stroke-surface-content/50',
219
+ 'fill-none',
220
+ className
221
+ )}
222
+ />
223
+ {/if}
112
224
  {:else}
113
225
  <Line
114
226
  {...restProps}
115
- x1={xCoord}
116
- x2={xCoord}
117
- y1={ctx.yRange[0] || 0}
118
- y2={ctx.yRange[1] || 0}
119
- class={cls(layerClass('rule-x-line'), 'stroke-surface-content/50', className)}
120
- />
121
- {/if}
122
- {/if}
123
-
124
- {#if showRule(y, 'y')}
125
- {#if ctx.radial}
126
- <Circle
127
- r={y === true || y === 'bottom'
128
- ? yRangeMinMax[1]
129
- : y === 'top'
130
- ? yRangeMinMax[0]
131
- : ctx.yScale(y) + yOffset}
227
+ x1={line.x1}
228
+ y1={line.y1}
229
+ x2={line.x2}
230
+ y2={line.y2}
231
+ {stroke}
132
232
  class={cls(
133
- layerClass('rule-y-radial-circle'),
134
- 'fill-none stroke-surface-content/50',
233
+ layerClass(line.axis === 'x' ? 'rule-x-line' : 'rule-y-line'),
234
+ !stroke && 'stroke-surface-content/50',
135
235
  className
136
236
  )}
137
237
  />
138
- {:else}
139
- <Line
140
- {...restProps}
141
- x1={ctx.xRange[0] || 0}
142
- x2={ctx.xRange[1] || 0}
143
- y1={y === true || y === 'bottom'
144
- ? yRangeMinMax[1]
145
- : y === 'top'
146
- ? yRangeMinMax[0]
147
- : ctx.yScale(y) + yOffset}
148
- y2={y === true || y === 'bottom'
149
- ? yRangeMinMax[1]
150
- : y === 'top'
151
- ? yRangeMinMax[0]
152
- : ctx.yScale(y) + yOffset}
153
- class={cls(layerClass('rule-y-line'), 'stroke-surface-content/50', className)}
154
- />
155
238
  {/if}
156
- {/if}
239
+ {/each}
157
240
  </Group>
@@ -1,6 +1,10 @@
1
1
  import type { Without } from '../utils/types.js';
2
2
  import type { SVGAttributes } from 'svelte/elements';
3
3
  export type BaseRulePropsWithoutHTML = {
4
+ /**
5
+ * Override the data from the context.
6
+ */
7
+ data?: any;
4
8
  /**
5
9
  * Create a vertical `x` line
6
10
  * - If true or 'left', will draw at chart left (xRange[0])
@@ -10,7 +14,7 @@ export type BaseRulePropsWithoutHTML = {
10
14
  *
11
15
  * @default false
12
16
  */
13
- x?: number | Date | boolean | 'left' | 'right';
17
+ x?: number | Date | boolean | '$left' | '$right' | Accessor;
14
18
  /**
15
19
  * Pixel offset to apply to `x` coordinate
16
20
  *
@@ -26,7 +30,7 @@ export type BaseRulePropsWithoutHTML = {
26
30
  *
27
31
  * @default false
28
32
  */
29
- y?: number | Date | boolean | 'top' | 'bottom';
33
+ y?: number | Date | boolean | '$top' | '$bottom' | Accessor;
30
34
  /**
31
35
  * Pixel offset to apply to `y` coordinate
32
36
  * @default 0
@@ -36,6 +40,7 @@ export type BaseRulePropsWithoutHTML = {
36
40
  export type RulePropsWithoutHTML = BaseRulePropsWithoutHTML & Without<Partial<LinePropsWithoutHTML>, BaseRulePropsWithoutHTML>;
37
41
  export type RuleProps = RulePropsWithoutHTML & Without<SVGAttributes<SVGElement>, RulePropsWithoutHTML>;
38
42
  import { type LinePropsWithoutHTML } from './Line.svelte';
43
+ import { type Accessor } from '../utils/common.js';
39
44
  declare const Rule: import("svelte").Component<RuleProps, {}, "">;
40
45
  type Rule = ReturnType<typeof Rule>;
41
46
  export default Rule;
@@ -122,7 +122,6 @@
122
122
  renderContext = 'svg',
123
123
  profile = false,
124
124
  debug = false,
125
- xScale: xScaleProp,
126
125
  children: childrenProp,
127
126
  aboveContext,
128
127
  belowContext,
@@ -196,11 +195,6 @@
196
195
  return _chartData;
197
196
  });
198
197
 
199
- // Default xScale based on first data's `x` value
200
- const xScale = $derived(
201
- xScaleProp ?? (accessor(x)(chartData[0]) instanceof Date ? scaleTime() : scaleLinear())
202
- );
203
-
204
198
  function isStackData(d: TData): d is TData & { stackData: any[] } {
205
199
  return d && typeof d === 'object' && 'stackData' in d;
206
200
  }
@@ -433,7 +427,6 @@
433
427
  data={chartData}
434
428
  {x}
435
429
  {xDomain}
436
- {xScale}
437
430
  y={resolveAccessor(y)}
438
431
  yBaseline={0}
439
432
  yNice
@@ -69,7 +69,6 @@
69
69
 
70
70
  <script lang="ts" generics="TData">
71
71
  import { onMount, type ComponentProps } from 'svelte';
72
- import { scaleLinear, scaleTime } from 'd3-scale';
73
72
  import { cls } from '@layerstack/tailwind';
74
73
 
75
74
  import Axis from '../Axis.svelte';
@@ -124,7 +123,6 @@
124
123
  renderContext = 'svg',
125
124
  profile = false,
126
125
  debug = false,
127
- xScale: xScaleProp,
128
126
  tooltip = true,
129
127
  children: childrenProp,
130
128
  aboveContext,
@@ -159,11 +157,6 @@
159
157
  : chartDataArray(data)) as Array<TData>
160
158
  );
161
159
 
162
- // Default xScale based on first data's `x` value
163
- const xScale = $derived(
164
- xScaleProp ?? (accessor(xProp)(chartData[0]) instanceof Date ? scaleTime() : scaleLinear())
165
- );
166
-
167
160
  function getSplineProps(s: SeriesData<TData, typeof Spline>, i: number) {
168
161
  const splineProps: ComponentProps<typeof Spline> = {
169
162
  data: s.data,
@@ -333,7 +326,6 @@
333
326
  data={chartData}
334
327
  x={xProp}
335
328
  {xDomain}
336
- {xScale}
337
329
  y={yProp ?? series.map((s) => s.value ?? s.key)}
338
330
  yBaseline={0}
339
331
  yNice
@@ -44,7 +44,6 @@
44
44
  </script>
45
45
 
46
46
  <script lang="ts" generics="TData">
47
- import { scaleLinear, scaleTime } from 'd3-scale';
48
47
  import { format } from '@layerstack/utils';
49
48
  import { cls } from '@layerstack/tailwind';
50
49
 
@@ -88,8 +87,6 @@
88
87
  renderContext = 'svg',
89
88
  profile = false,
90
89
  debug = false,
91
- xScale: xScaleProp,
92
- yScale: yScaleProp,
93
90
  children: childrenProp,
94
91
  aboveContext,
95
92
  belowContext,
@@ -107,18 +104,6 @@
107
104
 
108
105
  const seriesState = new SeriesState(() => series);
109
106
 
110
- // Default xScale based on first data's `x` value
111
- const xScale = $derived(
112
- xScaleProp ??
113
- (accessor(xProp)(chartDataArray(data)[0]) instanceof Date ? scaleTime() : scaleLinear())
114
- );
115
-
116
- // Default yScale based on first data's `y` value
117
- const yScale = $derived(
118
- yScaleProp ??
119
- (accessor(yProp)(chartDataArray(data)[0]) instanceof Date ? scaleTime() : scaleLinear())
120
- );
121
-
122
107
  const chartData = $derived(
123
108
  seriesState.visibleSeries
124
109
  .flatMap((s) => s.data?.map((d) => ({ seriesKey: s.key, ...d })))
@@ -241,10 +226,8 @@
241
226
  data={chartData}
242
227
  x={xProp}
243
228
  {xDomain}
244
- {xScale}
245
229
  y={yProp}
246
230
  {yDomain}
247
- {yScale}
248
231
  yNice
249
232
  c={yProp}
250
233
  cRange={['var(--color-primary)']}
@@ -23,21 +23,21 @@ export declare function createSeries<TKey extends string>(options: {
23
23
  }): ({
24
24
  x: number;
25
25
  } & { [K in TKey]: number; })[];
26
- export declare function createDateSeries<TKey extends string>(options: {
26
+ export declare function createDateSeries<TKey extends string>(options?: {
27
27
  count?: number;
28
- min: number;
29
- max: number;
28
+ min?: number;
29
+ max?: number;
30
30
  keys?: TKey[];
31
31
  value?: 'number' | 'integer';
32
32
  }): ({
33
33
  date: Date;
34
34
  } & { [K in TKey]: number; })[];
35
- export declare function createTimeSeries<TKey extends string>(options: {
35
+ export declare function createTimeSeries<TKey extends string>(options?: {
36
36
  count?: number;
37
- min: number;
38
- max: number;
39
- keys: TKey[];
40
- value: 'number' | 'integer';
37
+ min?: number;
38
+ max?: number;
39
+ keys?: TKey[];
40
+ value?: 'number' | 'integer';
41
41
  }): ({
42
42
  name: string;
43
43
  startDate: Date;
@@ -43,29 +43,31 @@ export function createSeries(options) {
43
43
  };
44
44
  });
45
45
  }
46
- export function createDateSeries(options) {
46
+ export function createDateSeries(options = {}) {
47
47
  const now = timeDay.floor(new Date());
48
48
  const count = options.count ?? 10;
49
- const min = options.min;
50
- const max = options.max;
49
+ const min = options.min ?? 0;
50
+ const max = options.max ?? 100;
51
51
  const keys = options.keys ?? ['value'];
52
+ const valueType = options.value ?? 'number';
52
53
  return Array.from({ length: count }).map((_, i) => {
53
54
  return {
54
55
  date: timeDay.offset(now, -count + i),
55
56
  ...Object.fromEntries(keys.map((key) => {
56
57
  return [
57
58
  key,
58
- options.value === 'integer' ? getRandomInteger(min, max) : getRandomNumber(min, max),
59
+ valueType === 'integer' ? getRandomInteger(min, max) : getRandomNumber(min, max),
59
60
  ];
60
61
  })),
61
62
  };
62
63
  });
63
64
  }
64
- export function createTimeSeries(options) {
65
+ export function createTimeSeries(options = {}) {
65
66
  const count = options.count ?? 10;
66
- const min = options.min;
67
- const max = options.max;
67
+ const min = options.min ?? 0;
68
+ const max = options.max ?? 100;
68
69
  const keys = options.keys ?? ['value'];
70
+ const valueType = options.value ?? 'number';
69
71
  let lastStartDate = timeDay.floor(new Date());
70
72
  const timeSeries = Array.from({ length: count }).map((_, i) => {
71
73
  const startDate = timeMinute.offset(lastStartDate, getRandomInteger(0, 60));
@@ -78,7 +80,7 @@ export function createTimeSeries(options) {
78
80
  ...Object.fromEntries(keys.map((key) => {
79
81
  return [
80
82
  key,
81
- options.value === 'integer' ? getRandomInteger(min, max) : getRandomNumber(min, max),
83
+ valueType === 'integer' ? getRandomInteger(min, max) : getRandomNumber(min, max),
82
84
  ];
83
85
  })),
84
86
  };
@@ -1,6 +1,6 @@
1
1
  import { type ScaleBand, type ScaleTime } from 'd3-scale';
2
2
  import { type MotionProp, type MotionOptions, type SpringOptions, type TweenOptions } from './motion.svelte.js';
3
- import type { Accessor } from './common.js';
3
+ import { type Accessor } from './common.js';
4
4
  import type { OnlyObjects } from './types.js';
5
5
  export type AnyScale<TInput extends SingleDomainType = any, TOutput extends SingleDomainType = any, TScaleArgs extends any[] | readonly any[] = any[]> = {
6
6
  (value: TInput): TOutput;
@@ -25,6 +25,7 @@ export type AnyScale<TInput extends SingleDomainType = any, TOutput extends Sing
25
25
  };
26
26
  export declare function isScaleBand(scale: AnyScale<any, any>): scale is ScaleBand<any>;
27
27
  export declare function isScaleTime(scale: AnyScale<any, any>): scale is ScaleTime<any, any>;
28
+ export declare function isScaleNumeric(scale: AnyScale<any, any>): scale is ScaleTime<any, any>;
28
29
  export declare function getRange(scale: any): any[];
29
30
  export type SingleDomainType = number | string | Date | null | undefined;
30
31
  export type DomainType = (number | string | Date | null | undefined)[] | null | undefined;
@@ -54,6 +55,10 @@ export declare function scaleBandInvert(scale: ScaleBand<any>): (value: number)
54
55
  export declare function scaleInvert(scale: AnyScale<any, any>, value: number): any;
55
56
  /** Create new copy of scale with domain and range */
56
57
  export declare function createScale(scale: AnyScale, domain: DomainType, range: any[] | readonly any[] | Function, context?: Record<any, any>): AnyScale<any, any, any[]>;
58
+ /**
59
+ * Auto-detect scale type based on domain values or data values
60
+ */
61
+ export declare function autoScale(domain?: DomainType, data?: any[], propAccessor?: Accessor<any>): AnyScale;
57
62
  /**
58
63
  * Create a `scaleBand()` within another scaleBand()'s bandwidth
59
64
  * (typically a x1 of an x0 scale, used for grouping)
@@ -1,7 +1,8 @@
1
1
  import { unique } from '@layerstack/utils';
2
- import { scaleBand } from 'd3-scale';
2
+ import { scaleBand, scaleLinear, scaleTime } from 'd3-scale';
3
3
  import { createControlledMotion, } from './motion.svelte.js';
4
4
  import { Spring, Tween } from 'svelte/motion';
5
+ import { accessor } from './common.js';
5
6
  function isAnyScale(scale) {
6
7
  return typeof scale === 'function' && typeof scale.range === 'function';
7
8
  }
@@ -12,6 +13,10 @@ export function isScaleTime(scale) {
12
13
  const domain = scale.domain();
13
14
  return domain[0] instanceof Date || domain[1] instanceof Date;
14
15
  }
16
+ export function isScaleNumeric(scale) {
17
+ const domain = scale.domain();
18
+ return typeof domain[0] === 'number' || typeof domain[1] === 'number';
19
+ }
15
20
  export function getRange(scale) {
16
21
  if (isAnyScale(scale)) {
17
22
  return scale.range();
@@ -87,6 +92,40 @@ export function createScale(scale, domain, range, context) {
87
92
  }
88
93
  return scaleCopy;
89
94
  }
95
+ /**
96
+ * Auto-detect scale type based on domain values or data values
97
+ */
98
+ export function autoScale(domain, data, propAccessor) {
99
+ let values = null;
100
+ if (domain && domain.length > 0) {
101
+ // Determine based on domain values
102
+ values = domain;
103
+ }
104
+ else if (data && data.length > 0 && propAccessor) {
105
+ // Determine based on data values
106
+ const value = accessor(propAccessor)(data[0]);
107
+ // If accessor defined with an array (ex. `x={['start', 'end']}`) use both values
108
+ if (Array.isArray(value)) {
109
+ values = value;
110
+ }
111
+ else {
112
+ values = [value];
113
+ }
114
+ }
115
+ if (values) {
116
+ if (values.some((v) => v instanceof Date)) {
117
+ return scaleTime();
118
+ }
119
+ else if (values.some((v) => typeof v === 'number')) {
120
+ return scaleLinear();
121
+ }
122
+ else if (values.some((v) => typeof v === 'string')) {
123
+ return scaleBand();
124
+ }
125
+ }
126
+ // fallback to linear scale
127
+ return scaleLinear();
128
+ }
90
129
  /**
91
130
  * Create a `scaleBand()` within another scaleBand()'s bandwidth
92
131
  * (typically a x1 of an x0 scale, used for grouping)
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "author": "Sean Lynch <techniq35@gmail.com>",
5
5
  "license": "MIT",
6
6
  "repository": "techniq/layerchart",
7
- "version": "2.0.0-next.33",
7
+ "version": "2.0.0-next.35",
8
8
  "devDependencies": {
9
9
  "@changesets/cli": "^2.29.4",
10
10
  "@iconify-json/lucide": "^1.2.48",
@@ -55,7 +55,7 @@
55
55
  "svelte": "5.34.1",
56
56
  "svelte-check": "^4.2.1",
57
57
  "svelte-json-tree": "^2.2.0",
58
- "svelte-ux": "2.0.0-next.14",
58
+ "svelte-ux": "2.0.0-next.17",
59
59
  "svelte2tsx": "^0.7.39",
60
60
  "tailwindcss": "^4.1.10",
61
61
  "topojson-client": "^3.1.0",