svelteplot 0.2.9 → 0.2.10-pr-110.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.
Files changed (63) hide show
  1. package/README.md +1 -1
  2. package/dist/Mark.svelte +5 -4
  3. package/dist/Plot.svelte +3 -2
  4. package/dist/core/Plot.svelte +37 -20
  5. package/dist/helpers/colors.d.ts +1 -1
  6. package/dist/helpers/index.d.ts +2 -2
  7. package/dist/helpers/scales.d.ts +1 -1
  8. package/dist/helpers/scales.js +9 -1
  9. package/dist/helpers/typeChecks.d.ts +4 -4
  10. package/dist/marks/Area.svelte +31 -29
  11. package/dist/marks/AreaX.svelte +7 -3
  12. package/dist/marks/AreaX.svelte.d.ts +1 -6
  13. package/dist/marks/AreaY.svelte +7 -2
  14. package/dist/marks/AreaY.svelte.d.ts +1 -9
  15. package/dist/marks/Arrow.svelte +21 -7
  16. package/dist/marks/AxisX.svelte +24 -21
  17. package/dist/marks/AxisX.svelte.d.ts +3 -4
  18. package/dist/marks/AxisY.svelte +23 -20
  19. package/dist/marks/AxisY.svelte.d.ts +3 -4
  20. package/dist/marks/BarX.svelte +18 -2
  21. package/dist/marks/BarX.svelte.d.ts +2 -2
  22. package/dist/marks/BarY.svelte +18 -3
  23. package/dist/marks/BarY.svelte.d.ts +2 -2
  24. package/dist/marks/BollingerX.svelte.d.ts +1 -1
  25. package/dist/marks/BollingerY.svelte.d.ts +1 -1
  26. package/dist/marks/BrushX.svelte +1 -1
  27. package/dist/marks/BrushY.svelte +1 -1
  28. package/dist/marks/Cell.svelte +1 -0
  29. package/dist/marks/Cell.svelte.d.ts +2 -2
  30. package/dist/marks/ColorLegend.svelte +3 -2
  31. package/dist/marks/Dot.svelte +29 -24
  32. package/dist/marks/Dot.svelte.d.ts +2 -2
  33. package/dist/marks/Frame.svelte +24 -4
  34. package/dist/marks/Frame.svelte.d.ts +2 -2
  35. package/dist/marks/Geo.svelte +27 -33
  36. package/dist/marks/Geo.svelte.d.ts +4 -2
  37. package/dist/marks/Graticule.svelte +2 -2
  38. package/dist/marks/Rect.svelte +1 -0
  39. package/dist/marks/Rect.svelte.d.ts +2 -2
  40. package/dist/marks/Sphere.svelte +2 -2
  41. package/dist/marks/Sphere.svelte.d.ts +3 -58
  42. package/dist/marks/helpers/Anchor.svelte +37 -0
  43. package/dist/marks/helpers/Anchor.svelte.d.ts +15 -0
  44. package/dist/marks/helpers/BaseAxisX.svelte +59 -53
  45. package/dist/marks/helpers/BaseAxisX.svelte.d.ts +1 -0
  46. package/dist/marks/helpers/BaseAxisY.svelte +24 -18
  47. package/dist/marks/helpers/BaseAxisY.svelte.d.ts +1 -0
  48. package/dist/marks/helpers/MarkerPath.svelte.d.ts +1 -41
  49. package/dist/marks/helpers/RectPath.svelte +33 -30
  50. package/dist/marks/helpers/Regression.svelte +1 -1
  51. package/dist/transforms/bollinger.d.ts +1 -8
  52. package/dist/transforms/centroid.d.ts +1 -8
  53. package/dist/transforms/group.d.ts +4 -12
  54. package/dist/transforms/interval.d.ts +2 -6
  55. package/dist/transforms/map.d.ts +4 -10
  56. package/dist/transforms/normalize.d.ts +2 -6
  57. package/dist/transforms/select.d.ts +7 -21
  58. package/dist/transforms/sort.d.ts +5 -17
  59. package/dist/transforms/sort.js +23 -13
  60. package/dist/transforms/window.d.ts +2 -14
  61. package/dist/types.d.ts +232 -78
  62. package/dist/ui/ExamplesGrid.svelte +1 -1
  63. package/package.json +121 -120
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # SveltePlot
2
2
 
3
- ![Tests](https://github.com/svelteplot/svelteplot/actions/workflows/test.yml/badge.svg)
3
+ ![License](https://img.shields.io/npm/l/svelteplot.svg) ![Tests](https://github.com/svelteplot/svelteplot/actions/workflows/test.yml/badge.svg)
4
4
 
5
5
  SveltePlot is a visualization framework based on the [layered grammar of graphics](https://vita.had.co.nz/papers/layered-grammar.html) ideas. It's API is heavily inspired by [Observable Plot](https://github.com/observablehq/plot). Created by Gregor Aisch.
6
6
 
package/dist/Mark.svelte CHANGED
@@ -56,7 +56,7 @@
56
56
 
57
57
  const channelsWithFacets: ScaledChannelName[] = $derived([...channels, 'fx', 'fy']);
58
58
 
59
- const { addMark, updateMark, updatePlotState, removeMark, getTopLevelFacet, getPlotState } =
59
+ const { addMark, removeMark, getTopLevelFacet, getPlotState } =
60
60
  getContext<PlotContext>('svelteplot');
61
61
 
62
62
  const plot = $derived(getPlotState());
@@ -258,16 +258,17 @@
258
258
  if (options?.[channel] != null && out[channel] === undefined) {
259
259
  // resolve value
260
260
  const value = row[channel];
261
-
262
261
  const scaled = usedScales[channel]
263
262
  ? scale === 'x'
264
263
  ? projectX(channel as 'x' | 'x1' | 'x2', plot.scales, value)
265
264
  : scale === 'y'
266
265
  ? projectY(channel as 'y' | 'y1' | 'y1', plot.scales, value)
267
- : plot.scales[scale].fn(value)
266
+ : scale === 'color' && !isValid(value)
267
+ ? plot.options.color.unknown
268
+ : plot.scales[scale].fn(value)
268
269
  : value;
269
270
 
270
- out.valid = out.valid && isValid(value);
271
+ out.valid = out.valid && (scale === 'color' || isValid(value));
271
272
 
272
273
  // apply dx/dy transform
273
274
  out[channel] =
package/dist/Plot.svelte CHANGED
@@ -12,7 +12,7 @@
12
12
  <script lang="ts">
13
13
  import Plot from './core/Plot.svelte';
14
14
 
15
- import type { PlotOptions } from './types.js';
15
+ import type { PlotDefaults, PlotOptions } from './types.js';
16
16
 
17
17
  // implicit marks
18
18
  import AxisX from './marks/AxisX.svelte';
@@ -28,6 +28,7 @@
28
28
  import { autoScale, autoScaleColor } from './helpers/autoScales.js';
29
29
  import { namedProjection } from './helpers/autoProjection.js';
30
30
  import { isObject } from './helpers/index.js';
31
+ import { getContext } from 'svelte';
31
32
 
32
33
  let {
33
34
  header: userHeader,
@@ -117,7 +118,7 @@
117
118
  scales,
118
119
  ...restProps
119
120
  })}
120
- <svelte:boundary onerror={(err) => console.warn(err)}>
121
+ <svelte:boundary onerror={(err) => console.error(err)}>
121
122
  <!-- implicit axes -->
122
123
  {#if !hasProjection && !hasExplicitAxisX}
123
124
  {#if options.axes && (options.x.axis === 'top' || options.x.axis === 'both')}
@@ -51,22 +51,17 @@
51
51
  const maxMarginBottom = $derived(Math.max(...$autoMarginBottom.values()));
52
52
  const maxMarginTop = $derived(Math.max(...$autoMarginTop.values()));
53
53
 
54
+ const USER_DEFAULTS = getContext<Partial<PlotDefaults>>('svelteplot/defaults') || {};
55
+
54
56
  // default settings in the plot and marks can be overwritten by
55
57
  // defining the svelteplot/defaults context outside of Plot
56
58
  const DEFAULTS: PlotDefaults = {
57
- axisXAnchor: 'bottom',
58
- axisYAnchor: 'left',
59
- xTickSpacing: 80,
60
- yTickSpacing: 50,
61
59
  height: 350,
62
60
  initialWidth: 500,
63
61
  inset: 0,
64
62
  colorScheme: 'turbo',
65
- unknown: '#cccccc',
66
- dotRadius: 3,
67
- frame: false,
68
- axes: true,
69
- grid: false,
63
+ unknown: '#cccccc99',
64
+
70
65
  categoricalColorScheme: 'observable10',
71
66
  pointScaleHeight: 18,
72
67
  bandScaleHeight: 30,
@@ -77,7 +72,29 @@
77
72
  compactDisplay: 'short'
78
73
  },
79
74
  markerDotRadius: 3,
80
- ...getContext<Partial<PlotDefaults>>('svelteplot/defaults')
75
+ ...USER_DEFAULTS,
76
+ axisX: {
77
+ anchor: 'bottom',
78
+ implicit: true,
79
+ ...USER_DEFAULTS.axis,
80
+ ...USER_DEFAULTS.axisX
81
+ },
82
+ axisY: {
83
+ anchor: 'left',
84
+ implicit: true,
85
+ ...USER_DEFAULTS.axis,
86
+ ...USER_DEFAULTS.axisY
87
+ },
88
+ gridX: {
89
+ implicit: false,
90
+ ...USER_DEFAULTS.grid,
91
+ ...USER_DEFAULTS.gridX
92
+ },
93
+ gridY: {
94
+ implicit: false,
95
+ ...USER_DEFAULTS.grid,
96
+ ...USER_DEFAULTS.gridY
97
+ }
81
98
  };
82
99
 
83
100
  let {
@@ -390,16 +407,16 @@
390
407
  ? margins
391
408
  : Math.max(5, maxMarginBottom),
392
409
  inset: isOneDimensional ? 10 : DEFAULTS.inset,
393
- grid: DEFAULTS.grid,
394
- axes: DEFAULTS.axes,
395
- frame: DEFAULTS.frame,
410
+ grid: (DEFAULTS.gridX?.implicit ?? false) && (DEFAULTS.gridY?.implicit ?? false),
411
+ axes: (DEFAULTS.axisX?.implicit ?? false) && (DEFAULTS.axisY?.implicit ?? false),
412
+ frame: DEFAULTS.frame?.implicit ?? false,
396
413
  projection: null,
397
414
  aspectRatio: null,
398
415
  facet: {},
399
416
  padding: 0.1,
400
417
  x: {
401
418
  type: 'auto',
402
- axis: autoXAxis ? DEFAULTS.axisXAnchor : false,
419
+ axis: DEFAULTS.axisX.implicit && autoXAxis ? DEFAULTS.axisX.anchor : false,
403
420
  labelAnchor: 'auto',
404
421
  reverse: false,
405
422
  clamp: false,
@@ -408,13 +425,13 @@
408
425
  round: false,
409
426
  percent: false,
410
427
  align: 0.5,
411
- tickSpacing: DEFAULTS.xTickSpacing,
428
+ tickSpacing: DEFAULTS.axisX.tickSpacing ?? 80,
412
429
  tickFormat: 'auto',
413
- grid: false
430
+ grid: DEFAULTS.gridX.implicit ?? false
414
431
  },
415
432
  y: {
416
433
  type: 'auto',
417
- axis: autoYAxis ? DEFAULTS.axisYAnchor : false,
434
+ axis: DEFAULTS.axisY.implicit && autoYAxis ? DEFAULTS.axisY.anchor : false,
418
435
  labelAnchor: 'auto',
419
436
  reverse: false,
420
437
  clamp: false,
@@ -423,9 +440,9 @@
423
440
  round: false,
424
441
  percent: false,
425
442
  align: 0.5,
426
- tickSpacing: DEFAULTS.yTickSpacing,
443
+ tickSpacing: DEFAULTS.axisY.tickSpacing ?? 50,
427
444
  tickFormat: 'auto',
428
- grid: false
445
+ grid: DEFAULTS.gridY.implicit ?? false
429
446
  },
430
447
  opacity: {
431
448
  type: 'linear',
@@ -450,7 +467,7 @@
450
467
  padding: 0,
451
468
  align: 0
452
469
  },
453
- color: { type: 'auto' },
470
+ color: { type: 'auto', unknown: DEFAULTS.unknown },
454
471
  length: { type: 'linear' },
455
472
  symbol: { type: 'ordinal' },
456
473
  fx: { type: 'band', axis: 'top' },
@@ -6,7 +6,7 @@ type SchemeGetter = (n: number) => readonly string[];
6
6
  export declare function isOrdinalScheme(scheme: ColorScheme): boolean;
7
7
  export declare function ordinalScheme(scheme: string): SchemeGetter | undefined;
8
8
  export declare function ordinalRange(scheme: string, length: number): readonly string[] | undefined;
9
- export declare function maybeBooleanRange(domain: boolean[], scheme?: string): unknown[] | undefined;
9
+ export declare function maybeBooleanRange(domain: boolean[], scheme?: string): any[] | undefined;
10
10
  export declare function isQuantitativeScheme(scheme: string): boolean;
11
11
  export declare function quantitativeScheme(scheme: string): typeof interpolateBrBG | undefined;
12
12
  export declare function isDivergingScheme(scheme: string): boolean;
@@ -3,8 +3,8 @@ import type { Snippet } from 'svelte';
3
3
  /**
4
4
  * Returns first argument that is not null or undefined
5
5
  */
6
- export declare function coalesce(...args: (RawValue | undefined | null)[]): RawValue | null;
7
- export declare function testFilter(datum: DataRecord, options: Record<ChannelName, ChannelAccessor>): string | number | boolean | symbol | Date | null;
6
+ export declare function coalesce(...args: (RawValue | undefined | null)[]): any;
7
+ export declare function testFilter(datum: DataRecord, options: Record<ChannelName, ChannelAccessor>): any;
8
8
  export declare function randomId(): string;
9
9
  export declare function isSnippet(value: unknown): value is Snippet;
10
10
  export declare function isValid(value: RawValue | undefined): value is number | Date | string;
@@ -15,7 +15,7 @@ export declare function createScale<T extends ScaleOptions>(name: ScaleName, sca
15
15
  autoTitle?: undefined;
16
16
  } | {
17
17
  type: ScaleType;
18
- domain: RawValue[] | [undefined, undefined];
18
+ domain: any;
19
19
  range: any;
20
20
  fn: any;
21
21
  skip: Map<ScaledChannelName, Set<symbol>>;
@@ -6,6 +6,7 @@ import { resolveProp, toChannelOption } from './resolve.js';
6
6
  import isDataRecord from './isDataRecord.js';
7
7
  import { createProjection } from './projection.js';
8
8
  import { maybeInterval } from './autoTicks.js';
9
+ import { IS_SORTED } from '../transforms/sort.js';
9
10
  /**
10
11
  * compute the plot scales
11
12
  */
@@ -54,8 +55,9 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
54
55
  // we're deliberately checking for !== undefined and not for != null
55
56
  // since the explicit sort transforms like shuffle will set the
56
57
  // sort channel to null to we know that there's an explicit order
57
- if (mark.channels.sort !== undefined)
58
+ if ((name === 'x' || name === 'y') && mark.options[IS_SORTED] != undefined) {
58
59
  sortOrdinalDomain = false;
60
+ }
59
61
  for (const channel of mark.channels) {
60
62
  // channelOptions can be passed as prop, but most often users will just
61
63
  // pass the channel accessor or constant value, so we may need to wrap
@@ -135,6 +137,12 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
135
137
  }
136
138
  }
137
139
  }
140
+ if ((name === 'x' || name === 'y') && scaleOptions.sort) {
141
+ sortOrdinalDomain = true;
142
+ }
143
+ if ((name === 'x' || name === 'y') && scaleOptions.sort === false) {
144
+ sortOrdinalDomain = false;
145
+ }
138
146
  // construct domain from data values
139
147
  const valueArr = [...dataValues.values(), ...(scaleOptions.domain || [])].filter((d) => d != null);
140
148
  const type = scaleOptions.type === 'auto'
@@ -1,10 +1,10 @@
1
1
  import type { RawValue } from '../types.js';
2
- export declare function isBooleanOrNull(v: RawValue): v is boolean;
2
+ export declare function isBooleanOrNull(v: RawValue): boolean;
3
3
  export declare function isDate(v: RawValue): v is Date;
4
- export declare function isDateOrNull(v: RawValue | null | undefined): v is Date | null | undefined;
4
+ export declare function isDateOrNull(v: RawValue | null | undefined): boolean;
5
5
  export declare function isNumberOrNull(v: RawValue | null | undefined): boolean;
6
6
  export declare function isNumberOrNullOrNaN(v: RawValue | null | undefined): boolean;
7
- export declare function isStringOrNull(v: RawValue | null | undefined): v is string | null | undefined;
7
+ export declare function isStringOrNull(v: RawValue | null | undefined): boolean;
8
8
  export declare function isSymbolOrNull(v: RawValue | null | undefined): boolean;
9
- export declare function isColorOrNull(v: RawValue | null | undefined): boolean;
9
+ export declare function isColorOrNull(v: RawValue | null | undefined): any;
10
10
  export declare function isOpacityOrNull(v: RawValue): boolean;
@@ -29,6 +29,7 @@
29
29
  import { maybeCurve } from '../helpers/curves.js';
30
30
  import { isValid } from '../helpers/index.js';
31
31
  import AreaCanvas from './helpers/AreaCanvas.svelte';
32
+ import Anchor from './helpers/Anchor.svelte';
32
33
 
33
34
  import type {
34
35
  CurveName,
@@ -37,14 +38,23 @@
37
38
  BaseMarkProps,
38
39
  ConstantAccessor,
39
40
  ChannelAccessor,
40
- FacetContext,
41
41
  ScaledDataRecord,
42
- LinkableMarkProps
42
+ LinkableMarkProps,
43
+ PlotDefaults
43
44
  } from '../types.js';
44
45
  import type { RawValue } from '../types.js';
45
46
  import type { StackOptions } from '../transforms/stack.js';
46
47
 
47
- let {
48
+ let markProps: AreaMarkProps = $props();
49
+
50
+ const DEFAULTS = {
51
+ fill: 'currentColor',
52
+ curve: 'linear',
53
+ tension: 0,
54
+ ...getContext<PlotDefaults>('svelteplot/_defaults').area
55
+ };
56
+
57
+ const {
48
58
  data,
49
59
  /** the curve */
50
60
  curve = 'linear',
@@ -52,7 +62,7 @@
52
62
  class: className = '',
53
63
  canvas = false,
54
64
  ...options
55
- }: AreaMarkProps = $props();
65
+ }: AreaMarkProps = $derived({ ...DEFAULTS, ...markProps });
56
66
 
57
67
  const { getPlotState } = getContext<PlotContext>('svelteplot');
58
68
  const plot = $derived(getPlotState());
@@ -113,32 +123,24 @@
113
123
  {:else}
114
124
  <GroupMultiple length={grouped.length}>
115
125
  {#each grouped as areaData, i (i)}
116
- {#snippet el(datum: ScaledDataRecord)}
117
- {@const title = resolveProp(options.title, datum.datum, '')}
118
- {@const [style, styleClass] = resolveStyles(
119
- plot,
120
- datum,
121
- options,
122
- 'fill',
123
- usedScales
124
- )}
125
- <path
126
- class={['svelteplot-area', className, styleClass]}
127
- clip-path={options.clipPath}
128
- d={areaPath(areaData)}
129
- {style}
130
- >{#if title}<title>{title}</title>{/if}</path>
131
- {/snippet}
126
+ {@const datum = areaData[0]}
132
127
  {#if areaData.length > 0}
133
- {#if options.href}
134
- <a
135
- href={resolveProp(options.href, areaData[0].datum, '')}
136
- target={resolveProp(options.target, areaData[0].datum, '_self')}>
137
- {@render el(areaData[0])}
138
- </a>
139
- {:else}
140
- {@render el(areaData[0])}
141
- {/if}
128
+ <Anchor {options} {datum}>
129
+ {@const title = resolveProp(options.title, datum.datum, '')}
130
+ {@const [style, styleClass] = resolveStyles(
131
+ plot,
132
+ datum,
133
+ options,
134
+ 'fill',
135
+ usedScales
136
+ )}
137
+ <path
138
+ class={['svelteplot-area', className, styleClass]}
139
+ clip-path={options.clipPath}
140
+ d={areaPath(areaData)}
141
+ {style}
142
+ >{#if title}<title>{title}</title>{/if}</path>
143
+ </Anchor>
142
144
  {/if}
143
145
  {/each}
144
146
  </GroupMultiple>
@@ -7,15 +7,19 @@
7
7
  import { renameChannels } from '../transforms/rename.js';
8
8
  import { stackX } from '../transforms/stack.js';
9
9
  import { recordizeX } from '../transforms/recordize.js';
10
- import type { ChannelAccessor } from '../types.js';
10
+ import type { ChannelAccessor, PlotDefaults } from '../types.js';
11
+ import { getContext } from 'svelte';
11
12
 
12
13
  type AreaXProps = Omit<AreaMarkProps, 'y1' | 'y2'> & {
13
14
  x?: ChannelAccessor;
14
15
  y?: ChannelAccessor;
15
16
  };
16
17
 
17
- // we're discarding y1 and y2 props since they are not
18
- let { data, stack, ...options }: AreaXProps = $props();
18
+ let markProps: AreaMarkProps = $props();
19
+
20
+ const DEFAULTS = getContext<PlotDefaults>('svelteplot/_defaults').areaX;
21
+
22
+ const { data, stack, ...options }: AreaMarkProps = $derived({ ...DEFAULTS, ...markProps });
19
23
 
20
24
  const args = $derived(
21
25
  renameChannels<AreaXProps>(
@@ -1,13 +1,8 @@
1
1
  import { type AreaMarkProps } from './Area.svelte';
2
- import type { ChannelAccessor } from '../types.js';
3
- type AreaXProps = Omit<AreaMarkProps, 'y1' | 'y2'> & {
4
- x?: ChannelAccessor;
5
- y?: ChannelAccessor;
6
- };
7
2
  /**
8
3
  * Creates a horizontal area chart with x value and baseline. Areas are implicitly
9
4
  * stacked horizontally if just the x channel is defined.
10
5
  */
11
- declare const AreaX: import("svelte").Component<AreaXProps, {}, "">;
6
+ declare const AreaX: import("svelte").Component<AreaMarkProps, {}, "">;
12
7
  type AreaX = ReturnType<typeof AreaX>;
13
8
  export default AreaX;
@@ -7,7 +7,8 @@
7
7
  import { renameChannels } from '../transforms/rename.js';
8
8
  import { stackY } from '../transforms/stack.js';
9
9
  import { recordizeY } from '../transforms/recordize.js';
10
- import type { ChannelAccessor } from '../types.js';
10
+ import type { ChannelAccessor, PlotDefaults } from '../types.js';
11
+ import { getContext } from 'svelte';
11
12
 
12
13
  /**
13
14
  * AreaY mark foo
@@ -17,7 +18,11 @@
17
18
  y?: ChannelAccessor;
18
19
  };
19
20
 
20
- let { data, stack, ...options }: AreaYProps = $props();
21
+ let markProps: AreaMarkProps = $props();
22
+
23
+ const DEFAULTS = getContext<PlotDefaults>('svelteplot/_defaults').areaY;
24
+
25
+ const { data, stack, ...options }: AreaMarkProps = $derived({ ...DEFAULTS, ...markProps });
21
26
 
22
27
  const args = $derived(
23
28
  renameChannels<AreaYProps>(
@@ -1,16 +1,8 @@
1
1
  import { type AreaMarkProps } from './Area.svelte';
2
- import type { ChannelAccessor } from '../types.js';
3
- /**
4
- * AreaY mark foo
5
- */
6
- type AreaYProps = Omit<AreaMarkProps, 'x1' | 'x2'> & {
7
- x?: ChannelAccessor;
8
- y?: ChannelAccessor;
9
- };
10
2
  /**
11
3
  * Creates a vertical area chart with y value and baseline. Areas are implicitly
12
4
  * stacked vertically if just the y channel is defined.
13
5
  */
14
- declare const AreaY: import("svelte").Component<AreaYProps, {}, "">;
6
+ declare const AreaY: import("svelte").Component<AreaMarkProps, {}, "">;
15
7
  type AreaY = ReturnType<typeof AreaY>;
16
8
  export default AreaY;
@@ -38,14 +38,15 @@
38
38
  </script>
39
39
 
40
40
  <script lang="ts">
41
- import { getContext, type Snippet } from 'svelte';
41
+ import { getContext } from 'svelte';
42
42
  import type {
43
43
  PlotContext,
44
44
  DataRecord,
45
45
  BaseMarkProps,
46
46
  ConstantAccessor,
47
47
  ChannelAccessor,
48
- RawValue
48
+ RawValue,
49
+ PlotDefaults
49
50
  } from '../types.js';
50
51
  import { resolveChannel, resolveProp, resolveStyles } from '../helpers/resolve.js';
51
52
  import { coalesce, maybeData, maybeNumber } from '../helpers/index.js';
@@ -55,7 +56,20 @@
55
56
  import { addEventHandlers } from './helpers/events.js';
56
57
  import GroupMultiple from './helpers/GroupMultiple.svelte';
57
58
 
58
- let { data = [{}], class: className = null, ...options }: ArrowMarkProps = $props();
59
+ let markProps: ArrowMarkProps = $props();
60
+
61
+ const DEFAULTS = {
62
+ headAngle: 60,
63
+ headLength: 8,
64
+ inset: 0,
65
+ ...getContext<PlotDefaults>('svelteplot/_defaults').arrow
66
+ };
67
+
68
+ const {
69
+ data = [{}],
70
+ class: className = null,
71
+ ...options
72
+ }: ArrowMarkProps = $derived({ ...DEFAULTS, ...markProps });
59
73
 
60
74
  const { getPlotState } = getContext<PlotContext>('svelteplot');
61
75
  const plot = $derived(getPlotState());
@@ -68,7 +82,7 @@
68
82
  : maybeData(data)
69
83
  );
70
84
 
71
- const args: ArrowProps = $derived(
85
+ const args: ArrowMarkProps = $derived(
72
86
  replaceChannels({ data: sorted, ...options }, { y: ['y1', 'y2'], x: ['x1', 'x2'] })
73
87
  );
74
88
  </script>
@@ -78,7 +92,7 @@
78
92
  required={['x1', 'x2', 'y1', 'y2']}
79
93
  channels={['x1', 'y1', 'x2', 'y2', 'opacity', 'stroke', 'strokeOpacity']}
80
94
  {...args}>
81
- {#snippet children({ mark, usedScales, scaledData })}
95
+ {#snippet children({ usedScales, scaledData })}
82
96
  {@const sweep = maybeSweep(args.sweep)}
83
97
  <GroupMultiple class="arrow" length={scaledData.length}>
84
98
  {#each scaledData as d, i (i)}
@@ -86,8 +100,8 @@
86
100
  {@const inset = resolveProp(args.inset, d.datum, 0)}
87
101
  {@const insetStart = resolveProp(args.insetStart, d.datum)}
88
102
  {@const insetEnd = resolveProp(args.insetEnd, d.datum)}
89
- {@const headAngle = resolveProp(args.headAngle, d.datum, 60)}
90
- {@const headLength = resolveProp(args.headLength, d.datum, 8)}
103
+ {@const headAngle = resolveProp(args.headAngle, d.datum)}
104
+ {@const headLength = resolveProp(args.headLength, d.datum)}
91
105
  {@const bend = resolveProp(args.bend, d.datum, 0)}
92
106
  {@const strokeWidth = resolveProp(args.strokeWidth, d.datum, 1)}
93
107
  {@const arrPath = arrowPath(
@@ -25,16 +25,13 @@
25
25
  tickClass?: ConstantAccessor<string>;
26
26
  /** ticks is a shorthand for defining data, tickCount or interval */
27
27
  ticks?: number | string | RawValue[];
28
- } & XOR<
29
- {
30
- /** approximate number of ticks to be generated */
31
- tickCount?: number;
32
- },
33
- {
34
- /** approximate number of pixels between generated ticks */
35
- tickSpacing?: number;
36
- }
37
- >;
28
+ /** set to false or null to disable tick labels */
29
+ text: boolean | null;
30
+ /** approximate number of ticks to be generated */
31
+ tickCount?: number;
32
+ /** approximate number of pixels between generated ticks */
33
+ tickSpacing?: number;
34
+ };
38
35
  </script>
39
36
 
40
37
  <script lang="ts">
@@ -47,40 +44,45 @@
47
44
  RawValue,
48
45
  ConstantAccessor,
49
46
  FacetContext,
50
- DefaultOptions
47
+ PlotDefaults,
48
+ ChannelName
51
49
  } from '../types.js';
52
50
  import autoTimeFormat from '../helpers/autoTimeFormat.js';
53
51
  import { derived } from 'svelte/store';
54
52
  import { autoTicks } from '../helpers/autoTicks.js';
55
53
  import { resolveScaledStyles } from '../helpers/resolve.js';
56
54
 
57
- const DEFAULTS = {
55
+ let markProps: AxisXMarkProps = $props();
56
+
57
+ const DEFAULTS: Omit<AxisXMarkProps, 'data' | ChannelName> = {
58
58
  tickSize: 6,
59
59
  tickPadding: 3,
60
60
  tickFontSize: 11,
61
- axisXAnchor: 'bottom',
62
- ...getContext<Partial<DefaultOptions>>('svelteplot/_defaults')
61
+ anchor: 'bottom',
62
+ ...getContext<PlotDefaults>('svelteplot/_defaults').axis,
63
+ ...getContext<PlotDefaults>('svelteplot/_defaults').axisX
63
64
  };
64
65
 
65
- let {
66
+ const {
66
67
  ticks: magicTicks,
67
68
  data = Array.isArray(magicTicks) ? magicTicks : [],
68
69
  automatic = false,
69
70
  title,
70
- anchor = DEFAULTS.axisXAnchor as 'top' | 'bottom',
71
+ anchor,
71
72
  facetAnchor = 'auto',
72
73
  interval = typeof magicTicks === 'string' ? magicTicks : undefined,
73
- tickSize = DEFAULTS.tickSize,
74
- tickFontSize = DEFAULTS.tickFontSize,
75
- tickPadding = DEFAULTS.tickPadding,
74
+ tickSize,
75
+ tickFontSize,
76
+ tickPadding,
76
77
  labelAnchor,
77
78
  tickFormat,
78
79
  tickClass,
79
80
  class: className,
80
81
  tickCount = typeof magicTicks === 'number' ? magicTicks : undefined,
81
82
  tickSpacing,
83
+ text = true,
82
84
  ...options
83
- }: AxisXMarkProps = $props();
85
+ }: AxisXMarkProps = $derived({ ...DEFAULTS, ...markProps });
84
86
 
85
87
  const { getPlotState } = getContext<PlotContext>('svelteplot');
86
88
  const plot = $derived(getPlotState());
@@ -96,7 +98,7 @@
96
98
  const ticks: RawValue[] = $derived(
97
99
  data.length > 0
98
100
  ? // use custom tick values if user passed any as prop
99
- data
101
+ Array.from(new Set(data))
100
102
  : // use custom scale tick values if user passed any as plot scale option
101
103
  autoTicks(
102
104
  plot.scales.x.type,
@@ -220,6 +222,7 @@
220
222
  {tickPadding}
221
223
  {tickFontSize}
222
224
  {tickClass}
225
+ {text}
223
226
  {options}
224
227
  title={useTitle}
225
228
  {plot} />
@@ -1,4 +1,3 @@
1
- import type { XOR } from 'ts-essentials';
2
1
  export type AxisXMarkProps = Omit<BaseMarkProps, 'fill' | 'fillOpacity' | 'paintOrder' | 'title' | 'href' | 'target'> & {
3
2
  data?: RawValue[];
4
3
  automatic?: boolean;
@@ -14,13 +13,13 @@ export type AxisXMarkProps = Omit<BaseMarkProps, 'fill' | 'fillOpacity' | 'paint
14
13
  tickClass?: ConstantAccessor<string>;
15
14
  /** ticks is a shorthand for defining data, tickCount or interval */
16
15
  ticks?: number | string | RawValue[];
17
- } & XOR<{
16
+ /** set to false or null to disable tick labels */
17
+ text: boolean | null;
18
18
  /** approximate number of ticks to be generated */
19
19
  tickCount?: number;
20
- }, {
21
20
  /** approximate number of pixels between generated ticks */
22
21
  tickSpacing?: number;
23
- }>;
22
+ };
24
23
  import type { BaseMarkProps, RawValue, ConstantAccessor } from '../types.js';
25
24
  /** Renders a horizontal axis with labels and tick marks */
26
25
  declare const AxisX: import("svelte").Component<AxisXMarkProps, {}, "">;