svelteplot 0.1.3-next.9 → 0.2.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.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # SveltePlot
2
2
 
3
- SveltePlot is a new reactive 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.
3
+ ![Tests](https://github.com/svelteplot/svelteplot/actions/workflows/test.yml/badge.svg)
4
4
 
5
- <img src="static/logo.png" alt="logo" width="400" />
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
+
7
+ <img src="static/logo.png" alt="SveltePlot logo" width="401" />
package/dist/Mark.svelte CHANGED
@@ -227,7 +227,12 @@
227
227
  );
228
228
  out[`x${suffix}`] = x;
229
229
  out[`y${suffix}`] = y;
230
- out.valid = out.valid && isValid(row.x) && isValid(row.y);
230
+ out.valid =
231
+ out.valid &&
232
+ isValid(row.x) &&
233
+ isValid(row.y) &&
234
+ isValid(x) &&
235
+ isValid(y);
231
236
  }
232
237
  }
233
238
  }
package/dist/Plot.svelte CHANGED
@@ -116,39 +116,47 @@
116
116
  scales,
117
117
  ...restProps
118
118
  })}
119
- <!-- implicit axes -->
120
- {#if !hasProjection && !hasExplicitAxisX}
121
- {#if options.axes && (options.x.axis === 'top' || options.x.axis === 'both')}
122
- <AxisX anchor="top" automatic />
119
+ <svelte:boundary onerror={(err) => console.warn(err)}>
120
+ <!-- implicit axes -->
121
+ {#if !hasProjection && !hasExplicitAxisX}
122
+ {#if options.axes && (options.x.axis === 'top' || options.x.axis === 'both')}
123
+ <AxisX anchor="top" automatic />
124
+ {/if}
125
+ {#if options.axes && (options.x.axis === 'bottom' || options.x.axis === 'both')}
126
+ <AxisX anchor="bottom" automatic />
127
+ {/if}
123
128
  {/if}
124
- {#if options.axes && (options.x.axis === 'bottom' || options.x.axis === 'both')}
125
- <AxisX anchor="bottom" automatic />
129
+ {#if !hasProjection && !hasExplicitAxisY}
130
+ {#if options.axes && (options.y.axis === 'left' || options.y.axis === 'both')}
131
+ <AxisY anchor="left" automatic />
132
+ {/if}
133
+ {#if options.axes && (options.y.axis === 'right' || options.y.axis === 'both')}
134
+ <AxisY anchor="right" automatic />
135
+ {/if}
126
136
  {/if}
127
- {/if}
128
- {#if !hasProjection && !hasExplicitAxisY}
129
- {#if options.axes && (options.y.axis === 'left' || options.y.axis === 'both')}
130
- <AxisY anchor="left" automatic />
137
+ <!-- implicit grids -->
138
+ {#if !hasExplicitGridX && (options.grid || options.x.grid)}
139
+ <GridX automatic />
131
140
  {/if}
132
- {#if options.axes && (options.y.axis === 'right' || options.y.axis === 'both')}
133
- <AxisY anchor="right" automatic />
141
+ {#if !hasExplicitGridY && (options.grid || options.y.grid)}
142
+ <GridY automatic />
134
143
  {/if}
135
- {/if}
136
- <!-- implicit grids -->
137
- {#if !hasExplicitGridX && (options.grid || options.x.grid)}
138
- <GridX automatic />
139
- {/if}
140
- {#if !hasExplicitGridY && (options.grid || options.y.grid)}
141
- <GridY automatic />
142
- {/if}
143
- <!-- implicit frame -->
144
- {#if options.frame}
145
- <Frame automatic />
146
- {/if}
147
- {@render parentChildren?.({
148
- options,
149
- scales,
150
- ...restProps
151
- })}
144
+ <!-- implicit frame -->
145
+ {#if options.frame}
146
+ <Frame automatic />
147
+ {/if}
148
+ {@render parentChildren?.({
149
+ options,
150
+ scales,
151
+ ...restProps
152
+ })}
153
+ {#snippet failed(error, reset)}
154
+ <text class="error" transform="translate(10,10)">
155
+ {#each error.message.split('\n') as line, i}
156
+ <tspan x="0" dy={i ? 14 : 0}>{line}</tspan>
157
+ {/each}
158
+ </text>{/snippet}
159
+ </svelte:boundary>
152
160
  {/snippet}
153
161
  {#snippet facetAxes()}
154
162
  <FacetAxes />
@@ -160,4 +168,12 @@
160
168
  --plot-bg: white;
161
169
  --plot-fg: currentColor;
162
170
  }
171
+ text.error {
172
+ stroke: var(--plot-bg);
173
+ fill: crimson;
174
+ font-size: 11px;
175
+ stroke-width: 3px;
176
+ font-weight: bold;
177
+ paint-order: stroke fill;
178
+ }
163
179
  </style>
@@ -4,7 +4,7 @@ import type { Snippet } from 'svelte';
4
4
  * Returns first argument that is not null or undefined
5
5
  */
6
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 | Date | null;
7
+ export declare function testFilter(datum: DataRecord, options: Record<ChannelName, ChannelAccessor>): string | number | boolean | symbol | Date | null;
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;
@@ -51,7 +51,7 @@ function resolve(datum, accessor, channel, scale) {
51
51
  // so we're passing the original value to accessor functions instead of our wrapped record
52
52
  return accessor(datum.___orig___ != null ? datum.___orig___ : datum);
53
53
  // use accessor string
54
- if (typeof accessor === 'string' && datum[accessor] !== undefined)
54
+ if ((typeof accessor === 'string' || typeof accessor === 'symbol') && datum[accessor] !== undefined)
55
55
  return datum[accessor];
56
56
  // fallback to channel name as accessor
57
57
  if (accessor === null && datum[channel] !== undefined)
@@ -147,7 +147,9 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
147
147
  valueArr.sort(ascending);
148
148
  }
149
149
  const domain = scaleOptions.domain
150
- ? extent(scaleOptions.zero ? [0, ...scaleOptions.domain] : scaleOptions.domain)
150
+ ? isOrdinal
151
+ ? scaleOptions.domain
152
+ : extent(scaleOptions.zero ? [0, ...scaleOptions.domain] : scaleOptions.domain)
151
153
  : type === 'band' ||
152
154
  type === 'point' ||
153
155
  type === 'ordinal' ||
package/dist/index.d.ts CHANGED
@@ -10,6 +10,9 @@ export { default as BollingerX } from './marks/BollingerX.svelte';
10
10
  export { default as BollingerY } from './marks/BollingerY.svelte';
11
11
  export { default as BoxX } from './marks/BoxX.svelte';
12
12
  export { default as BoxY } from './marks/BoxY.svelte';
13
+ export { default as Brush } from './marks/Brush.svelte';
14
+ export { default as BrushX } from './marks/BrushX.svelte';
15
+ export { default as BrushY } from './marks/BrushY.svelte';
13
16
  export { default as Cell } from './marks/Cell.svelte';
14
17
  export { default as CellX } from './marks/CellX.svelte';
15
18
  export { default as CellY } from './marks/CellY.svelte';
package/dist/index.js CHANGED
@@ -11,6 +11,9 @@ export { default as BollingerX } from './marks/BollingerX.svelte';
11
11
  export { default as BollingerY } from './marks/BollingerY.svelte';
12
12
  export { default as BoxX } from './marks/BoxX.svelte';
13
13
  export { default as BoxY } from './marks/BoxY.svelte';
14
+ export { default as Brush } from './marks/Brush.svelte';
15
+ export { default as BrushX } from './marks/BrushX.svelte';
16
+ export { default as BrushY } from './marks/BrushY.svelte';
14
17
  export { default as Cell } from './marks/Cell.svelte';
15
18
  export { default as CellX } from './marks/CellX.svelte';
16
19
  export { default as CellY } from './marks/CellY.svelte';
@@ -46,8 +46,6 @@
46
46
  stack
47
47
  )
48
48
  );
49
-
50
- $inspect({ args });
51
49
  </script>
52
50
 
53
51
  <Mark
@@ -61,16 +59,28 @@
61
59
  {@const bw = plot.scales.y.fn.bandwidth()}
62
60
  {@const minx = Math.min(d.x1, d.x2)}
63
61
  {@const maxx = Math.max(d.x1, d.x2)}
64
- {@const inset = resolveProp(args.inset, d.datum, 0)}
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)}
65
66
  {@const dx = resolveProp(args.dx, d.datum, 0)}
66
67
  {@const dy = resolveProp(args.dy, d.datum, 0)}
67
68
  {#if d.valid}
68
69
  {@const [style, styleClass] = resolveStyles(plot, d, args, 'fill', usedScales)}
69
70
  <path
70
- d={roundedRect(0, 0, maxx - minx, bw - inset * 2, options.borderRadius)}
71
+ d={roundedRect(
72
+ 0,
73
+ 0,
74
+ maxx - minx - insetLeft - insetRight,
75
+ bw - insetTop - insetBottom,
76
+ options.borderRadius
77
+ )}
71
78
  class={[styleClass, className]}
72
79
  {style}
73
- transform="translate({[minx + dx, d.y + inset + dy - bw * 0.5]})"
80
+ transform="translate({[
81
+ minx + dx + insetLeft,
82
+ d.y + insetTop + dy - bw * 0.5
83
+ ]})"
74
84
  use:addEventHandlers={{
75
85
  getPlotState,
76
86
  options: args,
@@ -11,10 +11,8 @@
11
11
  import {
12
12
  type PlotContext,
13
13
  type BaseMarkProps,
14
- type RectMarkProps,
15
14
  type ChannelAccessor,
16
- type DataRow,
17
- type FacetContext
15
+ type DataRow
18
16
  } from '../types.js';
19
17
  import type { StackOptions } from '../transforms/stack.js';
20
18
  import { maybeData } from '../helpers/index.js';
@@ -41,25 +39,23 @@
41
39
  bottomRight?: number;
42
40
  bottomLeft?: number;
43
41
  };
44
- } & RectMarkProps;
42
+ };
45
43
 
46
44
  let { data = [{}], class: className = null, stack, ...options }: BarYProps = $props();
47
45
 
48
46
  const { getPlotState } = getContext<PlotContext>('svelteplot');
49
- let plot = $derived(getPlotState());
47
+ const plot = $derived(getPlotState());
50
48
 
51
- let args = $derived(
49
+ const args = $derived(
52
50
  stackY(
53
51
  intervalY(
54
52
  // by default, sort by x channel (the ordinal labels)
55
- sort(recordizeY({ data: maybeData(data), sort: { channel: 'x' }, ...options })),
53
+ sort(recordizeY({ data, sort: { channel: 'x' }, ...options })),
56
54
  { plot }
57
55
  ),
58
56
  stack
59
57
  )
60
58
  );
61
-
62
- const { getTestFacet } = getContext<FacetContext>('svelteplot/facet');
63
59
  </script>
64
60
 
65
61
  <Mark
@@ -73,16 +69,28 @@
73
69
  {@const bw = plot.scales.x.fn.bandwidth()}
74
70
  {@const miny = Math.min(d.y1, d.y2)}
75
71
  {@const maxy = Math.max(d.y1, d.y2)}
76
- {@const inset = resolveProp(args.inset, d.datum, 0)}
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)}
77
76
  {@const dx = resolveProp(args.dx, d.datum, 0)}
78
77
  {@const dy = resolveProp(args.dy, d.datum, 0)}
79
78
  {#if d.valid}
80
79
  {@const [style, styleClass] = resolveStyles(plot, d, args, 'fill', usedScales)}
81
80
  <path
82
- d={roundedRect(0, 0, bw - inset * 2, maxy - miny, options.borderRadius)}
81
+ d={roundedRect(
82
+ 0,
83
+ 0,
84
+ bw - insetLeft - insetRight,
85
+ maxy - miny - insetTop - insetBottom,
86
+ options.borderRadius
87
+ )}
83
88
  class={[styleClass, className]}
84
89
  {style}
85
- transform="translate({[d.x + inset + dx - bw * 0.5, miny + dy]})"
90
+ transform="translate({[
91
+ d.x + insetLeft + dx - bw * 0.5,
92
+ miny + dy + insetTop
93
+ ]})"
86
94
  use:addEventHandlers={{
87
95
  getPlotState,
88
96
  options: args,
@@ -1,4 +1,25 @@
1
+ import { type BaseMarkProps, type ChannelAccessor, type DataRow } from '../types.js';
2
+ import type { StackOptions } from '../transforms/stack.js';
3
+ type BarYProps = BaseMarkProps & {
4
+ data: DataRow[];
5
+ x?: ChannelAccessor;
6
+ y?: ChannelAccessor;
7
+ y1?: ChannelAccessor;
8
+ y2?: ChannelAccessor;
9
+ stack?: StackOptions;
10
+ /**
11
+ * Converts y into y1/y2 ranges based on the provided interval. Disables the
12
+ * implicit stacking
13
+ */
14
+ interval?: number | string;
15
+ borderRadius?: number | {
16
+ topLeft?: number;
17
+ topRight?: number;
18
+ bottomRight?: number;
19
+ bottomLeft?: number;
20
+ };
21
+ };
1
22
  /** For vertical column charts using a band scale as x axis */
2
- declare const BarY: import("svelte").Component<any, {}, "">;
23
+ declare const BarY: import("svelte").Component<BarYProps, {}, "">;
3
24
  type BarY = ReturnType<typeof BarY>;
4
25
  export default BarY;