layerchart 0.72.2 → 0.74.0

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.
@@ -5,7 +5,7 @@
5
5
  import Rect from './Rect.svelte';
6
6
  import Spline from './Spline.svelte';
7
7
 
8
- import { createDimensionGetter } from '../utils/rect.js';
8
+ import { createDimensionGetter, type Insets } from '../utils/rect.js';
9
9
  import { isScaleBand } from '../utils/scales.js';
10
10
  import { accessor, type Accessor } from '../utils/common.js';
11
11
  import { greatestAbs } from '@layerstack/utils';
@@ -54,7 +54,7 @@
54
54
  | 'bottom-left'
55
55
  | 'bottom-right' = 'all';
56
56
 
57
- export let inset = 0;
57
+ export let insets: Insets | undefined = undefined;
58
58
 
59
59
  export let spring: ComponentProps<Rect>['spring'] = undefined;
60
60
  export let tweened: ComponentProps<Rect>['tweened'] = undefined;
@@ -66,7 +66,7 @@
66
66
  y,
67
67
  x1,
68
68
  y1,
69
- inset,
69
+ insets,
70
70
  });
71
71
  $: dimensions = $getDimensions(bar) ?? { x: 0, y: 0, width: 0, height: 0 };
72
72
 
@@ -5,6 +5,7 @@
5
5
  import Bar from './Bar.svelte';
6
6
  import Rect from './Rect.svelte';
7
7
  import { chartDataArray, type Accessor } from '../utils/common.js';
8
+ import type { Insets } from '../index.js/utils/rect.js';
8
9
 
9
10
  const { data: contextData, cGet, config } = chartContext();
10
11
 
@@ -39,7 +40,7 @@
39
40
  export let fill: string | undefined = undefined;
40
41
 
41
42
  /** Inset the rect for amount of padding. Useful with multiple bars (bullet, overlap, etc) */
42
- export let inset = 0;
43
+ export let insets: Insets | undefined = undefined;
43
44
 
44
45
  /** Define unique value for {#each} `(key)` expressions to improve transitions. `index` position used by default */
45
46
  export let key: (d: any, index: number) => any = (d, i) => i;
@@ -66,7 +67,7 @@
66
67
  {stroke}
67
68
  {strokeWidth}
68
69
  {radius}
69
- {inset}
70
+ {insets}
70
71
  {spring}
71
72
  {tweened}
72
73
  on:click={() => onBarClick({ data: d })}
@@ -361,7 +361,7 @@
361
361
  spring={motion}
362
362
  x={typeof bar === 'object' ? bar.x : undefined}
363
363
  y={typeof bar === 'object' ? bar.y : undefined}
364
- inset={typeof bar === 'object' ? bar.inset : undefined}
364
+ insets={typeof bar === 'object' ? bar.insets : undefined}
365
365
  stroke={typeof bar === 'object' ? bar.stroke : undefined}
366
366
  strokeWidth={typeof bar === 'object' ? bar.strokeWidth : undefined}
367
367
  radius={typeof bar === 'object' ? bar.radius : undefined}
@@ -92,6 +92,13 @@
92
92
  points?: Partial<ComponentProps<Points>>;
93
93
  highlight?: Partial<ComponentProps<Highlight>>;
94
94
  labels?: Partial<ComponentProps<Labels>>;
95
+ tooltip?: {
96
+ root?: Partial<ComponentProps<Tooltip.Root>>;
97
+ header?: Partial<ComponentProps<Tooltip.Header>>;
98
+ list?: Partial<ComponentProps<Tooltip.List>>;
99
+ item?: Partial<ComponentProps<Tooltip.Item>>;
100
+ separator?: Partial<ComponentProps<Tooltip.Separator>>;
101
+ };
95
102
  } = {};
96
103
 
97
104
  export let renderContext: 'svg' | 'canvas' = 'svg';
@@ -303,9 +310,9 @@
303
310
  </slot>
304
311
 
305
312
  <slot name="tooltip" {...slotProps}>
306
- <Tooltip.Root let:data>
307
- <Tooltip.Header>{format(x(data))}</Tooltip.Header>
308
- <Tooltip.List>
313
+ <Tooltip.Root {...props.tooltip?.root} let:data>
314
+ <Tooltip.Header {...props.tooltip?.header}>{format(x(data))}</Tooltip.Header>
315
+ <Tooltip.List {...props.tooltip?.list}>
309
316
  <!-- Reverse series order so tooltip items match stacks -->
310
317
  {@const seriesItems = stackSeries ? [...series].reverse() : series}
311
318
  {#each seriesItems as s}
@@ -318,11 +325,12 @@
318
325
  color={s.color}
319
326
  {format}
320
327
  valueAlign="right"
328
+ {...props.tooltip?.item}
321
329
  />
322
330
  {/each}
323
331
 
324
332
  {#if stackSeries}
325
- <Tooltip.Separator />
333
+ <Tooltip.Separator {...props.tooltip?.separator} />
326
334
 
327
335
  <Tooltip.Item
328
336
  label="total"
@@ -334,6 +342,7 @@
334
342
  })}
335
343
  format="integer"
336
344
  valueAlign="right"
345
+ {...props.tooltip?.root}
337
346
  />
338
347
  {/if}
339
348
  </Tooltip.List>
@@ -25,6 +25,7 @@
25
25
  type Accessor,
26
26
  } from '../../utils/common.js';
27
27
  import { asAny } from '../../utils/types.js';
28
+ import type { Insets } from '../../index.js/utils/rect.js';
28
29
 
29
30
  type ChartProps = ComponentProps<Chart<TData>>;
30
31
 
@@ -33,6 +34,7 @@
33
34
  grid?: typeof grid;
34
35
  bandPadding?: typeof bandPadding;
35
36
  groupPadding?: typeof groupPadding;
37
+ stackPadding?: typeof stackPadding;
36
38
  labels?: typeof labels;
37
39
  legend?: typeof legend;
38
40
  orientation?: typeof orientation;
@@ -84,6 +86,8 @@
84
86
  export let bandPadding = 0.4;
85
87
  /** Padding between group/series items when using 'seriesLayout="group"', applied to scaleBand().padding() */
86
88
  export let groupPadding = 0;
89
+ /** Padding between series items within bars when using 'seriesLayout="stack"' */
90
+ export let stackPadding = 0;
87
91
 
88
92
  /** Event dispatched with current tooltip data */
89
93
  export let onTooltipClick: (e: { data: any }) => void = () => {};
@@ -127,6 +131,13 @@
127
131
  legend?: Partial<ComponentProps<Legend>>;
128
132
  highlight?: Partial<ComponentProps<Highlight>>;
129
133
  labels?: Partial<ComponentProps<Labels>>;
134
+ tooltip?: {
135
+ root?: Partial<ComponentProps<Tooltip.Root>>;
136
+ header?: Partial<ComponentProps<Tooltip.Header>>;
137
+ list?: Partial<ComponentProps<Tooltip.List>>;
138
+ item?: Partial<ComponentProps<Tooltip.Item>>;
139
+ separator?: Partial<ComponentProps<Tooltip.Separator>>;
140
+ };
130
141
  } = {};
131
142
 
132
143
  export let renderContext: 'svg' | 'canvas' = 'svg';
@@ -170,18 +181,41 @@
170
181
  }
171
182
 
172
183
  function getBarsProps(s: (typeof series)[number], i: number) {
173
- const valueAccesor = stackSeries
184
+ const isFirst = i == 0;
185
+ const isLast = i == series.length - 1;
186
+
187
+ const isStackLayout = seriesLayout.startsWith('stack');
188
+
189
+ let stackInsets: Insets | undefined = undefined;
190
+
191
+ if (isStackLayout) {
192
+ const stackInset = stackPadding / 2;
193
+ if (isVertical) {
194
+ stackInsets = {
195
+ bottom: isFirst ? undefined : stackInset,
196
+ top: isLast ? undefined : stackInset,
197
+ };
198
+ } else {
199
+ stackInsets = {
200
+ left: isFirst ? undefined : stackInset,
201
+ right: isLast ? undefined : stackInset,
202
+ };
203
+ }
204
+ }
205
+
206
+ const valueAccessor = stackSeries
174
207
  ? (d: any) => d.stackData[i]
175
208
  : (s.value ?? (s.data ? undefined : s.key));
176
209
  const barsProps: ComponentProps<Bars> = {
177
210
  data: s.data,
178
- x: !isVertical ? valueAccesor : undefined,
179
- y: isVertical ? valueAccesor : undefined,
211
+ x: !isVertical ? valueAccessor : undefined,
212
+ y: isVertical ? valueAccessor : undefined,
180
213
  x1: isVertical && groupSeries ? (d) => s.value ?? s.key : undefined,
181
214
  y1: !isVertical && groupSeries ? (d) => s.value ?? s.key : undefined,
182
- rounded: seriesLayout.startsWith('stack') && i !== series.length - 1 ? 'none' : 'edge',
215
+ rounded: isStackLayout && i !== series.length - 1 ? 'none' : 'edge',
183
216
  radius: 4,
184
217
  strokeWidth: 1,
218
+ insets: stackInsets,
185
219
  fill: s.color,
186
220
  onBarClick: (e) => onBarClick({ data: e.data, series: s }),
187
221
  ...props.bars,
@@ -337,9 +371,11 @@
337
371
  </slot>
338
372
 
339
373
  <slot name="tooltip" {...slotProps}>
340
- <Tooltip.Root let:data>
341
- <Tooltip.Header>{format(isVertical ? x(data) : y(data))}</Tooltip.Header>
342
- <Tooltip.List>
374
+ <Tooltip.Root {...props.tooltip?.root} let:data>
375
+ <Tooltip.Header {...props.tooltip?.header}
376
+ >{format(isVertical ? x(data) : y(data))}</Tooltip.Header
377
+ >
378
+ <Tooltip.List {...props.tooltip?.list}>
343
379
  <!-- Reverse series order so tooltip items match stacks -->
344
380
  {@const seriesItems = stackSeries ? [...series].reverse() : series}
345
381
  {#each seriesItems as s}
@@ -351,11 +387,12 @@
351
387
  color={s.color ?? cScale?.(c(data))}
352
388
  {format}
353
389
  valueAlign="right"
390
+ {...props.tooltip?.item}
354
391
  />
355
392
  {/each}
356
393
 
357
394
  {#if stackSeries || groupSeries}
358
- <Tooltip.Separator />
395
+ <Tooltip.Separator {...props.tooltip?.separator} />
359
396
 
360
397
  <Tooltip.Item
361
398
  label="total"
@@ -366,6 +403,7 @@
366
403
  })}
367
404
  format="integer"
368
405
  valueAlign="right"
406
+ {...props.tooltip?.item}
369
407
  />
370
408
  {/if}
371
409
  </Tooltip.List>
@@ -83,6 +83,13 @@
83
83
  highlight?: Partial<ComponentProps<Highlight>>;
84
84
  labels?: Partial<ComponentProps<Labels>>;
85
85
  points?: Partial<ComponentProps<Points>>;
86
+ tooltip?: {
87
+ root?: Partial<ComponentProps<Tooltip.Root>>;
88
+ header?: Partial<ComponentProps<Tooltip.Header>>;
89
+ list?: Partial<ComponentProps<Tooltip.List>>;
90
+ item?: Partial<ComponentProps<Tooltip.Item>>;
91
+ separator?: Partial<ComponentProps<Tooltip.Separator>>;
92
+ };
86
93
  } = {};
87
94
 
88
95
  export let renderContext: 'svg' | 'canvas' = 'svg';
@@ -243,9 +250,9 @@
243
250
  </slot>
244
251
 
245
252
  <slot name="tooltip" {...slotProps}>
246
- <Tooltip.Root let:data>
247
- <Tooltip.Header>{format(x(data))}</Tooltip.Header>
248
- <Tooltip.List>
253
+ <Tooltip.Root {...props.tooltip?.root} let:data>
254
+ <Tooltip.Header {...props.tooltip?.header}>{format(x(data))}</Tooltip.Header>
255
+ <Tooltip.List {...props.tooltip?.list}>
249
256
  {#each series as s}
250
257
  {@const seriesTooltipData = s.data ? findRelatedData(s.data, data, x) : data}
251
258
  {@const valueAccessor = accessor(s.value ?? (s.data ? asAny(y) : s.key))}
@@ -255,6 +262,7 @@
255
262
  value={seriesTooltipData ? valueAccessor(seriesTooltipData) : null}
256
263
  color={s.color}
257
264
  {format}
265
+ {...props.tooltip?.item}
258
266
  />
259
267
  {/each}
260
268
  </Tooltip.List>
@@ -107,6 +107,13 @@
107
107
  group?: Partial<ComponentProps<Group>>;
108
108
  arc?: Partial<ComponentProps<Arc>>;
109
109
  legend?: Partial<ComponentProps<Legend>>;
110
+ tooltip?: {
111
+ root?: Partial<ComponentProps<Tooltip.Root>>;
112
+ header?: Partial<ComponentProps<Tooltip.Header>>;
113
+ list?: Partial<ComponentProps<Tooltip.List>>;
114
+ item?: Partial<ComponentProps<Tooltip.Item>>;
115
+ separator?: Partial<ComponentProps<Tooltip.Separator>>;
116
+ };
110
117
  } = {};
111
118
 
112
119
  export let renderContext: 'svg' | 'canvas' = 'svg';
@@ -257,13 +264,14 @@
257
264
  </slot>
258
265
 
259
266
  <slot name="tooltip" {...slotProps}>
260
- <Tooltip.Root let:data>
261
- <Tooltip.List>
267
+ <Tooltip.Root {...props.tooltip?.root} let:data>
268
+ <Tooltip.List {...props.tooltip?.list}>
262
269
  <Tooltip.Item
263
270
  label={labelAccessor(data) || keyAccessor(data)}
264
271
  value={valueAccessor(data)}
265
272
  color={cScale?.(c(data))}
266
273
  {format}
274
+ {...props.tooltip?.item}
267
275
  />
268
276
  </Tooltip.List>
269
277
  </Tooltip.Root>
@@ -4,6 +4,7 @@ import Arc from '../Arc.svelte';
4
4
  import Group from '../Group.svelte';
5
5
  import Legend from '../Legend.svelte';
6
6
  import Pie from '../Pie.svelte';
7
+ import * as Tooltip from '../tooltip/index.js';
7
8
  import { type Accessor } from '../../utils/common.js';
8
9
  declare class __sveltets_Render<TData> {
9
10
  props(): {
@@ -230,6 +231,13 @@ declare class __sveltets_Render<TData> {
230
231
  group?: Partial<ComponentProps<Group>>;
231
232
  arc?: Partial<ComponentProps<Arc>>;
232
233
  legend?: Partial<ComponentProps<Legend>>;
234
+ tooltip?: {
235
+ root?: Partial<ComponentProps<Tooltip.Root>>;
236
+ header?: Partial<ComponentProps<Tooltip.Header>>;
237
+ list?: Partial<ComponentProps<Tooltip.List>>;
238
+ item?: Partial<ComponentProps<Tooltip.Item>>;
239
+ separator?: Partial<ComponentProps<Tooltip.Separator>>;
240
+ } | undefined;
233
241
  } | undefined;
234
242
  range?: number[];
235
243
  series?: {
@@ -64,6 +64,13 @@
64
64
  labels?: Partial<ComponentProps<Labels>>;
65
65
  legend?: Partial<ComponentProps<Legend>>;
66
66
  rule?: Partial<ComponentProps<Rule>>;
67
+ tooltip?: {
68
+ root?: Partial<ComponentProps<Tooltip.Root>>;
69
+ header?: Partial<ComponentProps<Tooltip.Header>>;
70
+ list?: Partial<ComponentProps<Tooltip.List>>;
71
+ item?: Partial<ComponentProps<Tooltip.Item>>;
72
+ separator?: Partial<ComponentProps<Tooltip.Separator>>;
73
+ };
67
74
  } = {};
68
75
 
69
76
  export let renderContext: 'svg' | 'canvas' = 'svg';
@@ -199,28 +206,31 @@
199
206
  </slot>
200
207
 
201
208
  <slot name="tooltip" {...slotProps}>
202
- <Tooltip.Root let:data>
209
+ <Tooltip.Root {...props.tooltip?.root} let:data>
203
210
  {#if activeSeries?.key !== 'default'}
204
- <Tooltip.Header color={activeSeries?.color}>
211
+ <Tooltip.Header {...props.tooltip?.header} color={activeSeries?.color}>
205
212
  {activeSeries?.label ?? activeSeries?.key}
206
213
  </Tooltip.Header>
207
214
  {/if}
208
- <Tooltip.List>
215
+ <Tooltip.List {...props.tooltip?.list}>
209
216
  <Tooltip.Item
210
217
  label={typeof config.x === 'string' ? config.x : 'x'}
211
218
  value={x(data)}
212
219
  {format}
220
+ {...props.tooltip?.item}
213
221
  />
214
222
  <Tooltip.Item
215
223
  label={typeof config.y === 'string' ? config.y : 'y'}
216
224
  value={y(data)}
217
225
  {format}
226
+ {...props.tooltip?.item}
218
227
  />
219
228
  {#if config.r}
220
229
  <Tooltip.Item
221
230
  label={typeof config.r === 'string' ? config.r : 'r'}
222
231
  value={r(data)}
223
232
  {format}
233
+ {...props.tooltip?.item}
224
234
  />
225
235
  {/if}
226
236
  </Tooltip.List>
@@ -1,5 +1,22 @@
1
1
  import type { ChartContext } from '../components/ChartContext.svelte';
2
2
  import { type Accessor } from './common.js';
3
+ /** A set of inset distances, applied to a rectangle to shrink or expand the area represented by that rectangle. */
4
+ export type Insets = {
5
+ /** Applies an inset all sides of a rectangle: `left`, `right`, `bottom`, and `top` */
6
+ all?: number;
7
+ /** Applies an inset all horizontal sides of a rectangle: `left`, and `right`, overriding `all` */
8
+ x?: number;
9
+ /** Applies an inset all vertical sides of a rectangle: `top`, and `bottom`, overriding `all` */
10
+ y?: number;
11
+ /** Applies an inset the left side of a rectangle, overriding `x` */
12
+ left?: number;
13
+ /** Applies an inset the right side of a rectangle, overriding `x` */
14
+ right?: number;
15
+ /** Applies an inset the top side of a rectangle, overriding `y` */
16
+ top?: number;
17
+ /** Applies an inset the bottom side of a rectangle, overriding `y` */
18
+ bottom?: number;
19
+ };
3
20
  type DimensionGetterOptions = {
4
21
  /** Override `x` accessor from context */
5
22
  x?: Accessor;
@@ -9,7 +26,7 @@ type DimensionGetterOptions = {
9
26
  x1?: Accessor;
10
27
  /** Override `y1` accessor from context */
11
28
  y1?: Accessor;
12
- inset?: number;
29
+ insets?: Insets;
13
30
  };
14
31
  export declare function createDimensionGetter<TData>(context: ChartContext<TData>, options?: DimensionGetterOptions): import("svelte/store").Readable<(item: any) => {
15
32
  x: any;
@@ -4,8 +4,8 @@ import { isScaleBand } from './scales.js';
4
4
  import { accessor } from './common.js';
5
5
  export function createDimensionGetter(context, options) {
6
6
  const { xScale, yScale, x: xAccessor, y: yAccessor, x1: x1Accessor, y1: y1Accessor, x1Scale, y1Scale, } = context;
7
- const inset = options?.inset ?? 0;
8
7
  return derived([xScale, x1Scale, yScale, y1Scale, xAccessor, yAccessor, x1Accessor, y1Accessor], ([$xScale, $x1Scale, $yScale, $y1Scale, $xAccessor, $yAccessor, $x1Accessor, $y1Accessor]) => {
8
+ const insets = resolveInsets(options?.insets);
9
9
  // Use `xscale.domain()` instead of `$xDomain` to include `nice()` being applied
10
10
  const [minXDomain, maxXDomain] = $xScale.domain();
11
11
  const [minYDomain, maxYDomain] = $yScale.domain();
@@ -17,9 +17,11 @@ export function createDimensionGetter(context, options) {
17
17
  return function getter(item) {
18
18
  if (isScaleBand($yScale)) {
19
19
  // Horizontal band
20
- const y = firstValue($yScale(_y(item)) ?? 0) + ($y1Scale ? $y1Scale(_y1(item)) : 0) + inset / 2;
20
+ const y = firstValue($yScale(_y(item)) ?? 0) + ($y1Scale ? $y1Scale(_y1(item)) : 0) + insets.top;
21
21
  const height = Math.max(0, $yScale.bandwidth
22
- ? ($y1Scale ? ($y1Scale.bandwidth?.() ?? 0) : $yScale.bandwidth()) - inset
22
+ ? ($y1Scale ? ($y1Scale.bandwidth?.() ?? 0) : $yScale.bandwidth()) -
23
+ insets.bottom -
24
+ insets.top
23
25
  : 0);
24
26
  const xValue = _x(item);
25
27
  let left = 0;
@@ -44,18 +46,17 @@ export function createDimensionGetter(context, options) {
44
46
  left = xValue;
45
47
  right = min([0, maxXDomain]);
46
48
  }
47
- return {
48
- x: $xScale(left),
49
- y,
50
- width: $xScale(right) - $xScale(left),
51
- height,
52
- };
49
+ const x = $xScale(left) + insets.left;
50
+ const width = Math.max(0, $xScale(right) - $xScale(left) - insets.left - insets.right);
51
+ return { x, y, width, height };
53
52
  }
54
53
  else {
55
54
  // Vertical band or linear
56
- const x = firstValue($xScale(_x(item))) + ($x1Scale ? $x1Scale(_x1(item)) : 0) + inset / 2;
55
+ const x = firstValue($xScale(_x(item))) + ($x1Scale ? $x1Scale(_x1(item)) : 0) + insets.left;
57
56
  const width = Math.max(0, $xScale.bandwidth
58
- ? ($x1Scale ? ($x1Scale.bandwidth?.() ?? 0) : $xScale.bandwidth()) - inset
57
+ ? ($x1Scale ? ($x1Scale.bandwidth?.() ?? 0) : $xScale.bandwidth()) -
58
+ insets.left -
59
+ insets.right
59
60
  : 0);
60
61
  const yValue = _y(item);
61
62
  let top = 0;
@@ -80,12 +81,9 @@ export function createDimensionGetter(context, options) {
80
81
  top = min([0, maxYDomain]);
81
82
  bottom = yValue;
82
83
  }
83
- return {
84
- x,
85
- y: $yScale(top),
86
- width,
87
- height: $yScale(bottom) - $yScale(top),
88
- };
84
+ const y = $yScale(top) + insets.top;
85
+ const height = $yScale(bottom) - $yScale(top) - insets.bottom - insets.top;
86
+ return { x, y, width, height };
89
87
  }
90
88
  };
91
89
  });
@@ -97,3 +95,13 @@ export function createDimensionGetter(context, options) {
97
95
  export function firstValue(value) {
98
96
  return Array.isArray(value) ? value[0] : value;
99
97
  }
98
+ function resolveInsets(insets) {
99
+ const all = insets?.all ?? 0;
100
+ const x = insets?.x ?? all;
101
+ const y = insets?.y ?? all;
102
+ const left = insets?.left ?? x;
103
+ const right = insets?.right ?? x;
104
+ const top = insets?.top ?? y;
105
+ const bottom = insets?.bottom ?? y;
106
+ return { left, right, bottom, top };
107
+ }
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": "0.72.2",
7
+ "version": "0.74.0",
8
8
  "devDependencies": {
9
9
  "@changesets/cli": "^2.27.10",
10
10
  "@mdi/js": "^7.4.47",