layerchart 0.80.0 → 0.81.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.
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { createEventDispatcher, type ComponentProps } from 'svelte';
2
+ import { type ComponentProps } from 'svelte';
3
3
  import type { SVGAttributes } from 'svelte/elements';
4
4
  import { extent, min, max } from 'd3-array';
5
5
  import { clamp } from '@layerstack/utils';
@@ -17,12 +17,6 @@
17
17
 
18
18
  const { xScale, yScale, width, height, padding } = chartContext();
19
19
 
20
- const dispatch = createEventDispatcher<{
21
- change: { xDomain?: DomainType; yDomain?: DomainType };
22
- brushStart: { xDomain?: DomainType; yDomain?: DomainType };
23
- brushEnd: { xDomain?: DomainType; yDomain?: DomainType };
24
- }>();
25
-
26
20
  /** Axis to apply brushing */
27
21
  export let axis: 'x' | 'y' | 'both' = 'x';
28
22
 
@@ -37,6 +31,12 @@
37
31
 
38
32
  export let labels: ComponentProps<Text> | boolean = false;
39
33
 
34
+ /** Mode of operation
35
+ * `integrated`: use with single chart
36
+ * `separated`: use with separate (typically smaller) chart and state can be managed externally (sync with other charts, etc). Show active selection when domain does not equal original
37
+ */
38
+ export let mode: 'integrated' | 'separated' = 'integrated';
39
+
40
40
  // Capture original domains for reset()
41
41
  const originalXDomain = $xScale.domain() as [number, number];
42
42
  const originalYDomain = $yScale.domain() as [number, number];
@@ -61,6 +61,10 @@
61
61
  labels?: string;
62
62
  } = {};
63
63
 
64
+ export let onChange: (e: { xDomain?: DomainType; yDomain?: DomainType }) => void = () => {};
65
+ export let onBrushStart: (e: { xDomain?: DomainType; yDomain?: DomainType }) => void = () => {};
66
+ export let onBrushEnd: (e: { xDomain?: DomainType; yDomain?: DomainType }) => void = () => {};
67
+
64
68
  let frameEl: SVGRectElement;
65
69
 
66
70
  function handler(
@@ -83,7 +87,7 @@
83
87
  },
84
88
  };
85
89
 
86
- dispatch('brushStart', { xDomain, yDomain });
90
+ onBrushStart({ xDomain, yDomain });
87
91
 
88
92
  const onPointerMove = (e: PointerEvent) => {
89
93
  fn(start, {
@@ -95,17 +99,17 @@
95
99
  // // Ignore?
96
100
  // // TODO: What about when using `x` or `y` axis?
97
101
  // } else {
98
- dispatch('change', { xDomain, yDomain });
102
+ onChange({ xDomain, yDomain });
99
103
  // }
100
104
  };
101
105
 
102
106
  const onPointerUp = (e: PointerEvent) => {
103
107
  if (e.target === frameEl) {
104
108
  reset();
105
- dispatch('change', { xDomain, yDomain });
109
+ onChange({ xDomain, yDomain });
106
110
  }
107
111
 
108
- dispatch('brushEnd', { xDomain, yDomain });
112
+ onBrushEnd({ xDomain, yDomain });
109
113
 
110
114
  if (resetOnEnd) {
111
115
  reset();
@@ -214,12 +218,15 @@
214
218
  $: rangeWidth = axis === 'both' || axis === 'x' ? right - left : $width;
215
219
  $: rangeHeight = axis === 'both' || axis === 'y' ? bottom - top : $height;
216
220
 
217
- // Set reactively to handle cases where xDomain/yDomain are set externally (ex. `bind:xDomain`)
218
- $: isActive =
219
- xDomain?.[0]?.valueOf() !== originalXDomain[0]?.valueOf() ||
220
- xDomain?.[1]?.valueOf() !== originalXDomain[1]?.valueOf() ||
221
- yDomain?.[0]?.valueOf() !== originalYDomain[0]?.valueOf() ||
222
- yDomain?.[1]?.valueOf() !== originalYDomain[1]?.valueOf();
221
+ let isActive = false;
222
+ $: if (mode === 'separated') {
223
+ // Set reactively to handle cases where xDomain/yDomain are set externally (ex. `bind:xDomain`)
224
+ isActive =
225
+ xDomain?.[0]?.valueOf() !== originalXDomain[0]?.valueOf() ||
226
+ xDomain?.[1]?.valueOf() !== originalXDomain[1]?.valueOf() ||
227
+ yDomain?.[0]?.valueOf() !== originalYDomain[0]?.valueOf() ||
228
+ yDomain?.[1]?.valueOf() !== originalYDomain[1]?.valueOf();
229
+ }
223
230
  </script>
224
231
 
225
232
  <g class={cls('Brush select-none', classes.root, $$props.class)}>
@@ -258,7 +265,7 @@
258
265
  on:dblclick={() => {
259
266
  if (yDomain) {
260
267
  yDomain[0] = yDomainMin;
261
- dispatch('change', { xDomain, yDomain });
268
+ onChange({ xDomain, yDomain });
262
269
  }
263
270
  }}
264
271
  >
@@ -303,7 +310,7 @@
303
310
  on:dblclick={() => {
304
311
  if (xDomain) {
305
312
  xDomain[0] = xDomainMin;
306
- dispatch('change', { xDomain, yDomain });
313
+ onChange({ xDomain, yDomain });
307
314
  }
308
315
  }}
309
316
  >
@@ -325,7 +332,7 @@
325
332
  on:dblclick={() => {
326
333
  if (xDomain) {
327
334
  xDomain[1] = xDomainMax;
328
- dispatch('change', { xDomain, yDomain });
335
+ onChange({ xDomain, yDomain });
329
336
  }
330
337
  }}
331
338
  >
@@ -13,6 +13,7 @@ declare const __propDef: {
13
13
  xDomain?: DomainType | undefined;
14
14
  yDomain?: DomainType | undefined;
15
15
  labels?: (ComponentProps<Text> | boolean) | undefined;
16
+ mode?: "integrated" | "separated" | undefined;
16
17
  range?: SVGAttributes<SVGRectElement> | undefined;
17
18
  handle?: SVGAttributes<SVGRectElement> | undefined;
18
19
  format?: FormatType | undefined;
@@ -23,21 +24,20 @@ declare const __propDef: {
23
24
  handle?: string;
24
25
  labels?: string;
25
26
  } | undefined;
26
- };
27
- events: {
28
- change: CustomEvent<{
27
+ onChange?: ((e: {
29
28
  xDomain?: DomainType;
30
29
  yDomain?: DomainType;
31
- }>;
32
- brushStart: CustomEvent<{
30
+ }) => void) | undefined;
31
+ onBrushStart?: ((e: {
33
32
  xDomain?: DomainType;
34
33
  yDomain?: DomainType;
35
- }>;
36
- brushEnd: CustomEvent<{
34
+ }) => void) | undefined;
35
+ onBrushEnd?: ((e: {
37
36
  xDomain?: DomainType;
38
37
  yDomain?: DomainType;
39
- }>;
40
- } & {
38
+ }) => void) | undefined;
39
+ };
40
+ events: {
41
41
  [evt: string]: CustomEvent<any>;
42
42
  };
43
43
  slots: {
@@ -75,7 +75,7 @@
75
75
  $: stackSeries = seriesLayout.startsWith('stack');
76
76
 
77
77
  export let axis: ComponentProps<Axis> | 'x' | 'y' | boolean = true;
78
- export let brush: ({ mode: 'integrated' } & ComponentProps<Brush>) | false = false;
78
+ export let brush: ComponentProps<Brush> | boolean = false;
79
79
  export let grid: ComponentProps<Grid> | boolean = true;
80
80
  export let labels: ComponentProps<Labels> | boolean = false;
81
81
  export let legend: ComponentProps<Legend> | boolean = false;
@@ -396,17 +396,18 @@
396
396
  {/if}
397
397
  </svelte:component>
398
398
 
399
- {#if brush && brush.mode === 'integrated'}
399
+ {#if brush && (brush === true || brush.mode == undefined || brush.mode === 'integrated')}
400
400
  <Svg>
401
+ {@const brushProps = { ...(typeof brush === 'object' ? brush : null), ...props.brush }}
401
402
  <Brush
402
403
  axis="x"
403
404
  resetOnEnd
404
405
  {xDomain}
405
- on:brushEnd={(e) => {
406
- xDomain = e.detail.xDomain;
406
+ {...brushProps}
407
+ onBrushEnd={(e) => {
408
+ xDomain = e.xDomain;
409
+ brushProps.onBrushEnd?.(e);
407
410
  }}
408
- {...typeof brush === 'object' ? brush : null}
409
- {...props.brush}
410
411
  />
411
412
  </Svg>
412
413
  {/if}
@@ -67,7 +67,7 @@
67
67
  $: isDefaultSeries = series.length === 1 && series[0].key === 'default';
68
68
 
69
69
  export let axis: ComponentProps<Axis> | 'x' | 'y' | boolean = true;
70
- export let brush: ({ mode: 'integrated' } & ComponentProps<Brush>) | false = false;
70
+ export let brush: ComponentProps<Brush> | boolean = false;
71
71
  export let grid: ComponentProps<Grid> | boolean = true;
72
72
  export let labels: ComponentProps<Labels> | boolean = false;
73
73
  export let legend: ComponentProps<Legend> | boolean = false;
@@ -322,17 +322,18 @@
322
322
  </slot>
323
323
  </svelte:component>
324
324
 
325
- {#if brush && brush.mode === 'integrated'}
325
+ {#if brush && (brush === true || brush.mode == undefined || brush.mode === 'integrated')}
326
326
  <Svg>
327
+ {@const brushProps = { ...(typeof brush === 'object' ? brush : null), ...props.brush }}
327
328
  <Brush
328
329
  axis="x"
329
330
  resetOnEnd
330
331
  {xDomain}
331
- on:brushEnd={(e) => {
332
- xDomain = e.detail.xDomain;
332
+ {...brushProps}
333
+ onBrushEnd={(e) => {
334
+ xDomain = e.xDomain;
335
+ brushProps.onBrushEnd?.(e);
333
336
  }}
334
- {...typeof brush === 'object' ? brush : null}
335
- {...props.brush}
336
337
  />
337
338
  </Svg>
338
339
  {/if}
@@ -58,7 +58,7 @@
58
58
  $: isDefaultSeries = series.length === 1 && series[0].key === 'default';
59
59
 
60
60
  export let axis: ComponentProps<Axis> | 'x' | 'y' | boolean = true;
61
- export let brush: ({ mode: 'integrated' } & ComponentProps<Brush>) | false = false;
61
+ export let brush: ComponentProps<Brush> | boolean = false;
62
62
  export let grid: ComponentProps<Grid> | boolean = true;
63
63
  export let labels: ComponentProps<Labels> | boolean = false;
64
64
  export let legend: ComponentProps<Legend> | boolean = false;
@@ -270,19 +270,20 @@
270
270
  </svelte:component>
271
271
 
272
272
  <!-- TODO: Determine how to coordinate with `tooltip={{ mode: 'voronoi' }} -->
273
- {#if brush && brush.mode === 'integrated'}
273
+ {#if brush && (brush === true || brush.mode == undefined || brush.mode === 'integrated')}
274
274
  <Svg>
275
+ {@const brushProps = { ...(typeof brush === 'object' ? brush : null), ...props.brush }}
275
276
  <Brush
276
277
  axis="both"
277
278
  resetOnEnd
278
279
  {xDomain}
279
280
  {yDomain}
280
- on:brushEnd={(e) => {
281
- xDomain = e.detail.xDomain;
282
- yDomain = e.detail.yDomain;
281
+ {...brushProps}
282
+ onBrushEnd={(e) => {
283
+ xDomain = e.xDomain;
284
+ yDomain = e.yDomain;
285
+ brushProps.onBrushEnd?.(e);
283
286
  }}
284
- {...typeof brush === 'object' ? brush : null}
285
- {...props.brush}
286
287
  />
287
288
  </Svg>
288
289
  {/if}
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.80.0",
7
+ "version": "0.81.1",
8
8
  "devDependencies": {
9
9
  "@changesets/cli": "^2.27.12",
10
10
  "@mdi/js": "^7.4.47",