svelteplot 0.1.3-next.9 → 0.2.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.
Files changed (51) hide show
  1. package/README.md +4 -2
  2. package/dist/Mark.svelte +18 -2
  3. package/dist/Plot.svelte +45 -29
  4. package/dist/helpers/index.d.ts +2 -1
  5. package/dist/helpers/index.js +1 -0
  6. package/dist/helpers/resolve.js +7 -6
  7. package/dist/helpers/scales.d.ts +2 -2
  8. package/dist/helpers/scales.js +8 -5
  9. package/dist/helpers/typeChecks.js +14 -10
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.js +3 -0
  12. package/dist/marks/BarX.svelte +15 -5
  13. package/dist/marks/BarY.svelte +20 -12
  14. package/dist/marks/BarY.svelte.d.ts +22 -1
  15. package/dist/marks/Brush.svelte +364 -0
  16. package/dist/marks/Brush.svelte.d.ts +32 -0
  17. package/dist/marks/BrushX.svelte +7 -0
  18. package/dist/marks/BrushX.svelte.d.ts +4 -0
  19. package/dist/marks/BrushY.svelte +7 -0
  20. package/dist/marks/BrushY.svelte.d.ts +4 -0
  21. package/dist/marks/Cell.svelte +0 -7
  22. package/dist/marks/ColorLegend.svelte +6 -10
  23. package/dist/marks/Dot.svelte +11 -20
  24. package/dist/marks/Dot.svelte.d.ts +8 -8
  25. package/dist/marks/Frame.svelte +10 -5
  26. package/dist/marks/Frame.svelte.d.ts +6 -1
  27. package/dist/marks/Geo.svelte +50 -41
  28. package/dist/marks/Geo.svelte.d.ts +3 -1
  29. package/dist/marks/GridX.svelte +17 -8
  30. package/dist/marks/GridY.svelte +17 -8
  31. package/dist/marks/Pointer.svelte +4 -3
  32. package/dist/marks/Pointer.svelte.d.ts +2 -2
  33. package/dist/marks/Rect.svelte +12 -19
  34. package/dist/marks/Sphere.svelte.d.ts +14 -4
  35. package/dist/marks/Text.svelte +2 -2
  36. package/dist/marks/Text.svelte.d.ts +2 -2
  37. package/dist/marks/helpers/CanvasLayer.svelte +10 -16
  38. package/dist/marks/helpers/CanvasLayer.svelte.d.ts +2 -6
  39. package/dist/marks/helpers/DotCanvas.svelte +82 -159
  40. package/dist/marks/helpers/DotCanvas.svelte.d.ts +2 -4
  41. package/dist/marks/helpers/GeoCanvas.svelte +95 -145
  42. package/dist/marks/helpers/GeoCanvas.svelte.d.ts +3 -5
  43. package/dist/marks/helpers/events.d.ts +13 -0
  44. package/dist/marks/helpers/events.js +32 -3
  45. package/dist/transforms/bin.d.ts +7 -7
  46. package/dist/transforms/recordize.d.ts +2 -0
  47. package/dist/transforms/recordize.js +20 -10
  48. package/dist/transforms/stack.js +10 -7
  49. package/dist/transforms/window.d.ts +2 -0
  50. package/dist/types.d.ts +34 -13
  51. package/package.json +23 -20
@@ -1,4 +1,5 @@
1
1
  import type { TransformArgsRow, TransformArgsRecord } from '../types.js';
2
+ export declare const INDEX: unique symbol;
2
3
  export declare const RAW_VALUE: unique symbol;
3
4
  export declare function recordizeX({ data, ...channels }: TransformArgsRow, { withIndex }?: {
4
5
  withIndex: boolean;
@@ -12,3 +13,4 @@ export declare function recordizeY({ data, ...channels }: TransformArgsRow, { wi
12
13
  * the rest of our code doesn't have to deal with this case anymore.
13
14
  */
14
15
  export declare function recordizeXY({ data, ...channels }: TransformArgsRow): TransformArgsRecord;
16
+ export declare function recordize({ data, ...channels }: TransformArgsRow): TransformArgsRecord;
@@ -1,5 +1,6 @@
1
1
  import isDataRecord from '../helpers/isDataRecord.js';
2
- export const RAW_VALUE = Symbol();
2
+ export const INDEX = Symbol('index');
3
+ export const RAW_VALUE = Symbol('originalValue');
3
4
  /*
4
5
  * This transform takes an array of raw values as input and returns data records
5
6
  * in which the values are interpreted as x channel and their index as y
@@ -10,14 +11,12 @@ export function recordizeX({ data, ...channels }, { withIndex } = { withIndex: t
10
11
  return {
11
12
  data: data.map((value, index) => ({
12
13
  __value: value,
13
- ...(withIndex ? { __index: index } : {}),
14
+ ...(withIndex ? { [INDEX]: index } : {}),
14
15
  [RAW_VALUE]: value,
15
- // TODO: remove ___orig___ references
16
- ___orig___: value
17
16
  })),
18
17
  ...channels,
19
- x: '__value',
20
- ...(withIndex ? { y: '__index' } : {})
18
+ x: RAW_VALUE,
19
+ ...(withIndex ? { y: INDEX } : {})
21
20
  };
22
21
  }
23
22
  return { data: data, ...channels };
@@ -33,14 +32,12 @@ export function recordizeY({ data, ...channels }, { withIndex } = { withIndex: t
33
32
  if (dataIsRawValueArray) {
34
33
  return {
35
34
  data: Array.from(data).map((value, index) => ({
36
- __value: value,
37
35
  ...(withIndex ? { __index: index } : {}),
38
36
  [RAW_VALUE]: value,
39
- ___orig___: value
40
37
  })),
41
38
  ...channels,
42
39
  ...(withIndex ? { x: '__index' } : {}),
43
- y: '__value'
40
+ y: RAW_VALUE
44
41
  };
45
42
  }
46
43
  return {
@@ -66,7 +63,7 @@ export function recordizeXY({ data, ...channels }) {
66
63
  channels.y === undefined) {
67
64
  return {
68
65
  data: data.map(([x, y, ...rest]) => ({
69
- ___orig___: [x, y, ...rest],
66
+ [RAW_VALUE]: [x, y, ...rest],
70
67
  __x: x,
71
68
  __y: y
72
69
  })),
@@ -77,3 +74,16 @@ export function recordizeXY({ data, ...channels }) {
77
74
  }
78
75
  return { data, ...channels };
79
76
  }
77
+ export function recordize({ data, ...channels }) {
78
+ if (!data)
79
+ return { data, ...channels };
80
+ if (!isDataRecord(data[0])) {
81
+ return {
82
+ data: data.map((d) => ({
83
+ [RAW_VALUE]: d,
84
+ })),
85
+ ...channels,
86
+ };
87
+ }
88
+ return { data, ...channels };
89
+ }
@@ -2,6 +2,9 @@ import isDataRecord from '../helpers/isDataRecord.js';
2
2
  import { resolveChannel } from '../helpers/resolve.js';
3
3
  import { stack, stackOffsetExpand, stackOffsetSilhouette, stackOffsetWiggle, stackOrderAppearance, stackOrderAscending, stackOrderInsideOut, stackOrderNone, stackOffsetDiverging } from 'd3-shape';
4
4
  import { index, union, groups as d3Groups } from 'd3-array';
5
+ import { RAW_VALUE } from './recordize';
6
+ const GROUP = Symbol('group');
7
+ const FACET = Symbol('group');
5
8
  const DEFAULT_STACK_OPTIONS = {
6
9
  order: null,
7
10
  offset: null,
@@ -39,8 +42,8 @@ function stackXY(byDim, data, channels, options) {
39
42
  const resolvedData = data.map((d) => ({
40
43
  ...(isDataRecord(d) ? d : { __orig: d }),
41
44
  [`__${secondDim}`]: resolveChannel(secondDim, d, channels),
42
- __group: groupBy === true ? 'G' : resolveChannel(groupBy, d, channels),
43
- __facet: groupFacetsBy.length > 0
45
+ [GROUP]: groupBy === true ? 'G' : resolveChannel(groupBy, d, channels),
46
+ [FACET]: groupFacetsBy.length > 0
44
47
  ? groupFacetsBy
45
48
  .map((channel) => String(resolveChannel(channel, d, channels)))
46
49
  .join('---')
@@ -51,11 +54,11 @@ function stackXY(byDim, data, channels, options) {
51
54
  const out = [];
52
55
  // first we group the dataset by facets to avoid stacking of rows that are
53
56
  // in separate panels
54
- const groups = d3Groups(resolvedData, (d) => d.__facet);
57
+ const groups = d3Groups(resolvedData, (d) => d[FACET]);
55
58
  for (const [, facetData] of groups) {
56
59
  // now we index the data on the second dimension, e.g. over x
57
60
  // when stacking over y
58
- const indexed = index(facetData, (d) => d[`__${secondDim}`], (d) => d.__group);
61
+ const indexed = index(facetData, (d) => d[`__${secondDim}`], (d) => d[GROUP]);
59
62
  const stackOrder = (series) => {
60
63
  const f = STACK_ORDER[options.order || 'none'];
61
64
  return options.reverse ? f(series).reverse() : f(series);
@@ -64,7 +67,7 @@ function stackXY(byDim, data, channels, options) {
64
67
  const series = stack()
65
68
  .order(stackOrder)
66
69
  .offset(STACK_OFFSET[options.offset])
67
- .keys(union(facetData.map((d) => d.__group)))
70
+ .keys(union(facetData.map((d) => d[GROUP])))
68
71
  .value(([, group], key) => (group.get(key) ? group.get(key)[`__${byDim}`] : 0))(indexed);
69
72
  // and combine it all back into a flat array
70
73
  const newData = series
@@ -75,8 +78,8 @@ function stackXY(byDim, data, channels, options) {
75
78
  .map((d) => {
76
79
  const datum = d.data[1].get(groupKey);
77
80
  // cleanup our internal keys
78
- delete datum.__group;
79
- delete datum.__facet;
81
+ delete datum[GROUP];
82
+ delete datum[FACET];
80
83
  return { ...datum, [`__${byLow}`]: d[0], [`__${byHigh}`]: d[1] };
81
84
  });
82
85
  })
@@ -10,12 +10,14 @@ type WindowOptions = {
10
10
  export declare function windowX(args: TransformArg<DataRecord>, options: WindowOptions): {
11
11
  data: {
12
12
  [x: string]: import("../types.js").RawValue;
13
+ [x: symbol]: import("../types.js").RawValue;
13
14
  ___orig___?: import("../types.js").RawValue | [import("../types.js").RawValue, import("../types.js").RawValue];
14
15
  }[];
15
16
  };
16
17
  export declare function windowY(args: TransformArg<DataRecord>, options: WindowOptions): {
17
18
  data: {
18
19
  [x: string]: import("../types.js").RawValue;
20
+ [x: symbol]: import("../types.js").RawValue;
19
21
  ___orig___?: import("../types.js").RawValue | [import("../types.js").RawValue, import("../types.js").RawValue];
20
22
  }[];
21
23
  };
package/dist/types.d.ts CHANGED
@@ -3,6 +3,7 @@ import type { Snippet } from 'svelte';
3
3
  import type { MouseEventHandler } from 'svelte/elements';
4
4
  import type { MarkerShape } from './marks/helpers/Marker.svelte';
5
5
  import type { Writable } from 'svelte/store';
6
+ import type * as CSS from 'csstype';
6
7
  export type MarkType = 'area' | 'arrow' | 'axisX' | 'axisY' | 'barX' | 'barY' | 'cell' | 'dot' | 'vector' | 'frame' | 'geo' | 'gridX' | 'gridY' | 'line' | 'rect' | 'regression' | 'ruleX' | 'ruleY' | 'swoopyArrow' | 'text' | 'tickX' | 'tickY';
7
8
  export type ScaleName = 'x' | 'y' | 'r' | 'color' | 'opacity' | 'length' | 'symbol' | 'fx' | 'fy' | 'projection';
8
9
  export type ScaleType = 'linear' | 'pow' | 'sqrt' | 'log' | 'symlog' | 'time' | 'point' | 'ordinal' | 'sequential' | 'band' | 'categorical' | 'cyclical' | 'threshold' | 'quantile-cont' | 'quantile' | 'quantize' | 'diverging' | 'diverging-log' | 'diverging-pow' | 'diverging-sqrt' | 'diverging-symlog';
@@ -16,7 +17,7 @@ export type Mark<T> = {
16
17
  };
17
18
  export type ScaledChannelName = 'fill' | 'fillOpacity' | 'opacity' | 'r' | 'length' | 'stroke' | 'strokeOpacity' | 'symbol' | 'fx' | 'fy' | 'x' | 'x1' | 'x2' | 'y' | 'y1' | 'y2';
18
19
  export type ChannelName = ScaledChannelName | 'z' | 'sort' | 'filter' | 'interval';
19
- export type RawValue = number | Date | boolean | string;
20
+ export type RawValue = number | Date | boolean | string | symbol;
20
21
  export type ScaleOptions = {
21
22
  /**
22
23
  * Override the automatic scale type detection.
@@ -159,6 +160,10 @@ export type PlotOptions = {
159
160
  * setting the maxWidth style property you can limit the width of your plot.
160
161
  */
161
162
  maxWidth?: string;
163
+ /**
164
+ * force the plot into a fixed width
165
+ */
166
+ width?: number;
162
167
  /**
163
168
  * force the plot into a fixed height
164
169
  */
@@ -166,7 +171,12 @@ export type PlotOptions = {
166
171
  /**
167
172
  * Convenience option for setting all four margins at once, in px.
168
173
  */
169
- margin: number;
174
+ margin: number | {
175
+ top?: number;
176
+ right?: number;
177
+ bottom?: number;
178
+ left?: number;
179
+ };
170
180
  /**
171
181
  * Left margin of the plot, in px.
172
182
  */
@@ -236,11 +246,11 @@ export type PlotOptions = {
236
246
  /**
237
247
  * Options for the shared radius scale
238
248
  */
239
- r: ScaleOptions;
240
- color: ColorScaleOptions;
241
- opacity: ScaleOptions;
242
- symbol: LegendScaleOptions;
243
- length: ScaleOptions;
249
+ r: Partial<ScaleOptions>;
250
+ color: Partial<ColorScaleOptions>;
251
+ opacity: Partial<ScaleOptions>;
252
+ symbol: Partial<LegendScaleOptions>;
253
+ length: Partial<ScaleOptions>;
244
254
  fx: Partial<ScaleOptions>;
245
255
  fy: Partial<ScaleOptions>;
246
256
  children: Snippet<[
@@ -331,7 +341,7 @@ export type PlotDefaults = {
331
341
  markerDotRadius: number;
332
342
  };
333
343
  export type GenericMarkOptions = Record<string, any>;
334
- export type DataRecord = Record<string, RawValue> & {
344
+ export type DataRecord = Record<string | symbol, RawValue> & {
335
345
  ___orig___?: RawValue | [RawValue, RawValue];
336
346
  };
337
347
  export type ResolvedDataRecord = Partial<Record<ScaledChannelName, any>> & {
@@ -488,16 +498,16 @@ export type BaseMarkProps = Partial<{
488
498
  stroke: ConstantAccessor<string>;
489
499
  strokeWidth: ConstantAccessor<number>;
490
500
  strokeOpacity: ConstantAccessor<number>;
491
- strokeLinejoin: ConstantAccessor<'bevel' | 'miter' | 'miter-clip' | 'round'>;
492
- strokeLinecap: ConstantAccessor<'butt' | 'square' | 'round'>;
501
+ strokeLinejoin: ConstantAccessor<CSS.Property.StrokeLinejoin>;
502
+ strokeLinecap: ConstantAccessor<CSS.Property.StrokeLinecap>;
493
503
  strokeMiterlimit: ConstantAccessor<number>;
494
504
  opacity: ConstantAccessor<number>;
495
505
  strokeDasharray: ConstantAccessor<string>;
496
506
  strokeDashoffset: ConstantAccessor<number>;
497
- mixBlendMode: ConstantAccessor<'normal' | 'multiply' | 'screen' | 'overlay' | 'darken' | 'lighten' | 'color-dodge' | 'color-burn' | 'hard-light' | 'soft-light' | 'difference' | 'exclusion' | 'hue' | 'saturation' | 'color' | 'luminosity' | 'plus-darker' | 'plus-lighter'>;
507
+ mixBlendMode: ConstantAccessor<CSS.Property.MixBlendMode>;
498
508
  clipPath: string;
499
509
  imageFilter: ConstantAccessor<string>;
500
- shapeRendering: ConstantAccessor<'crispEdges' | 'geometricPrecision' | 'optimizeSpeed' | 'auto'>;
510
+ shapeRendering: ConstantAccessor<CSS.Property.ShapeRendering>;
501
511
  paintOrder: ConstantAccessor<string>;
502
512
  onclick?: MouseEventHandler<SVGPathElement>;
503
513
  ondblclick?: MouseEventHandler<SVGPathElement>;
@@ -507,6 +517,15 @@ export type BaseMarkProps = Partial<{
507
517
  onmousemove?: MouseEventHandler<SVGPathElement>;
508
518
  onmouseleave?: MouseEventHandler<SVGPathElement>;
509
519
  onmouseout?: MouseEventHandler<SVGPathElement>;
520
+ onmouseover?: MouseEventHandler<SVGPathElement>;
521
+ onpointercancel?: MouseEventHandler<SVGPathElement>;
522
+ onpointerdown?: MouseEventHandler<SVGPathElement>;
523
+ onpointerup?: MouseEventHandler<SVGPathElement>;
524
+ onpointerenter?: MouseEventHandler<SVGPathElement>;
525
+ onpointerleave?: MouseEventHandler<SVGPathElement>;
526
+ onpointermove?: MouseEventHandler<SVGPathElement>;
527
+ onpointerover?: MouseEventHandler<SVGPathElement>;
528
+ onpointerout?: MouseEventHandler<SVGPathElement>;
510
529
  ondrag?: MouseEventHandler<SVGPathElement>;
511
530
  ondrop?: MouseEventHandler<SVGPathElement>;
512
531
  ondragstart?: MouseEventHandler<SVGPathElement>;
@@ -534,6 +553,7 @@ export type BaseMarkProps = Partial<{
534
553
  * if you want to give your mark element an extra CSS class
535
554
  */
536
555
  class: string;
556
+ cursor: ConstantAccessor<CSS.Property.Cursor>;
537
557
  }>;
538
558
  export type BaseRectMarkProps = {
539
559
  rx?: ConstantAccessor<number>;
@@ -544,7 +564,7 @@ export type BaseRectMarkProps = {
544
564
  insetRight?: ConstantAccessor<number>;
545
565
  insetBottom?: ConstantAccessor<number>;
546
566
  };
547
- export type Channels = Record<string, ChannelAccessor | ConstantAccessor<string | number | boolean>>;
567
+ export type Channels = Record<string, ChannelAccessor | ConstantAccessor<string | number | boolean | symbol>>;
548
568
  export type TransformArg<K> = Channels & {
549
569
  data: K[];
550
570
  };
@@ -632,4 +652,5 @@ export type MapIndexObject = {
632
652
  };
633
653
  export type MapMethod = 'cumsum' | 'rank' | 'quantile' | ((I: number[], S: number[]) => number[]) | MapIndexObject;
634
654
  export type MapOptions = Partial<Record<ScaledChannelName, MapMethod>>;
655
+ export type UsedScales = Record<ScaledChannelName, boolean>;
635
656
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelteplot",
3
- "version": "0.1.3-next.9",
3
+ "version": "0.2.1",
4
4
  "license": "ISC",
5
5
  "author": {
6
6
  "name": "Gregor Aisch",
@@ -47,16 +47,17 @@
47
47
  "devDependencies": {
48
48
  "@aitodotai/json-stringify-pretty-compact": "^1.3.0",
49
49
  "@emotion/css": "^11.13.5",
50
- "@sveltejs/adapter-auto": "^6.0.0",
50
+ "@sveltejs/adapter-auto": "^6.0.1",
51
51
  "@sveltejs/adapter-static": "^3.0.8",
52
52
  "@sveltejs/eslint-config": "^8.2.0",
53
- "@sveltejs/kit": "^2.20.7",
53
+ "@sveltejs/kit": "^2.21.0",
54
54
  "@sveltejs/package": "^2.3.11",
55
55
  "@sveltejs/vite-plugin-svelte": "5.0.3",
56
- "@sveltepress/theme-default": "^6.0.2",
57
- "@sveltepress/twoslash": "^1.2.1",
58
- "@sveltepress/vite": "^1.2.1",
56
+ "@sveltepress/theme-default": "^6.0.3",
57
+ "@sveltepress/twoslash": "^1.2.2",
58
+ "@sveltepress/vite": "^1.2.2",
59
59
  "@testing-library/svelte": "^5.2.7",
60
+ "@testing-library/user-event": "^14.6.1",
60
61
  "@types/d3-array": "^3.2.1",
61
62
  "@types/d3-color": "^3.1.3",
62
63
  "@types/d3-dsv": "^3.0.7",
@@ -67,31 +68,33 @@
67
68
  "@types/d3-scale": "^4.0.9",
68
69
  "@types/d3-scale-chromatic": "^3.1.0",
69
70
  "@types/d3-shape": "^3.1.7",
70
- "@typescript-eslint/eslint-plugin": "^8.31.1",
71
- "@typescript-eslint/parser": "^8.31.1",
71
+ "@typescript-eslint/eslint-plugin": "^8.32.1",
72
+ "@typescript-eslint/parser": "^8.32.1",
73
+ "csstype": "^3.1.3",
72
74
  "d3-dsv": "^3.0.1",
73
75
  "d3-fetch": "^3.0.1",
74
76
  "d3-force": "^3.0.0",
75
- "eslint": "^9.25.1",
76
- "eslint-config-prettier": "^10.1.2",
77
- "eslint-plugin-svelte": "3.5.1",
77
+ "eslint": "^9.26.0",
78
+ "eslint-config-prettier": "^10.1.5",
79
+ "eslint-plugin-svelte": "3.7.0",
78
80
  "jsdom": "^26.1.0",
79
81
  "prettier": "^3.5.3",
80
- "prettier-plugin-svelte": "^3.3.3",
82
+ "prettier-plugin-svelte": "^3.4.0",
81
83
  "remark-code-extra": "^1.0.1",
82
84
  "remark-code-frontmatter": "^1.0.0",
83
85
  "resize-observer-polyfill": "^1.5.1",
84
- "sass": "^1.87.0",
85
- "svelte-check": "^4.1.6",
86
- "svelte-eslint-parser": "1.1.3",
86
+ "sass": "^1.89.0",
87
+ "svelte-check": "^4.2.1",
88
+ "svelte-eslint-parser": "1.2.0",
87
89
  "svelte-highlight": "^7.8.3",
88
90
  "topojson-client": "^3.1.0",
89
91
  "tslib": "^2.8.1",
90
- "typedoc": "^0.28.3",
92
+ "typedoc": "^0.28.4",
91
93
  "typedoc-plugin-markdown": "^4.6.3",
92
94
  "typescript": "^5.8.3",
93
- "vite": "^6.3.3",
94
- "vitest": "^3.1.2"
95
+ "vite": "^6.3.5",
96
+ "vitest": "^3.1.3",
97
+ "vitest-matchmedia-mock": "^2.0.3"
95
98
  },
96
99
  "types": "./dist/index.d.ts",
97
100
  "type": "module",
@@ -109,9 +112,9 @@
109
112
  "d3-scale-chromatic": "^3.1.0",
110
113
  "d3-shape": "^3.2.0",
111
114
  "d3-time": "^3.1.0",
112
- "es-toolkit": "^1.36.0",
115
+ "es-toolkit": "^1.37.2",
113
116
  "fast-equals": "^5.2.2",
114
117
  "merge-deep": "^3.0.3",
115
- "svelte": "5.28.2"
118
+ "svelte": "5.30.1"
116
119
  }
117
120
  }