svelteplot 0.1.3-next.7 → 0.1.3-next.9

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/dist/Mark.svelte CHANGED
@@ -1,7 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { getContext, untrack, type Snippet } from 'svelte';
3
3
 
4
- import { CHANNEL_SCALE } from './constants.js';
4
+ import { CHANNEL_SCALE, INDEX } from './constants.js';
5
5
  import type {
6
6
  ScaledChannelName,
7
7
  MarkType,
@@ -126,24 +126,26 @@
126
126
  const testFacet = $derived(getTestFacet());
127
127
 
128
128
  const resolvedData: ResolvedDataRecord[] = $derived(
129
- data.flatMap((row) => {
130
- const channels = options as Record<ChannelName, ChannelAccessor>;
131
- if (!testFacet(row, channels) || !testFilter(row, channels)) return [];
132
- const out: ResolvedDataRecord = {
133
- datum: row
134
- };
135
- for (const [channel] of Object.entries(CHANNEL_SCALE) as [
136
- ScaledChannelName,
137
- ScaleName
138
- ][]) {
139
- // check if the mark has defined an accessor for this channel
140
- if (options?.[channel] !== undefined && out[channel] === undefined) {
141
- // resolve value
142
- out[channel] = resolveChannel(channel, row, options);
129
+ data
130
+ .map((d, i) => ({ ...d, [INDEX]: i }))
131
+ .flatMap((row) => {
132
+ const channels = options as Record<ChannelName, ChannelAccessor>;
133
+ if (!testFacet(row, channels) || !testFilter(row, channels)) return [];
134
+ const out: ResolvedDataRecord = {
135
+ datum: row
136
+ };
137
+ for (const [channel] of Object.entries(CHANNEL_SCALE) as [
138
+ ScaledChannelName,
139
+ ScaleName
140
+ ][]) {
141
+ // check if the mark has defined an accessor for this channel
142
+ if (options?.[channel] !== undefined && out[channel] === undefined) {
143
+ // resolve value
144
+ out[channel] = resolveChannel(channel, row, options);
145
+ }
143
146
  }
144
- }
145
- return [out];
146
- })
147
+ return [out];
148
+ })
147
149
  );
148
150
 
149
151
  let prevResolvedData: ResolvedDataRecord[] = [];
@@ -156,7 +158,6 @@
156
158
  }
157
159
  });
158
160
 
159
-
160
161
  function isDifferent(array1: ResolvedDataRecord[], array2: ResolvedDataRecord[]) {
161
162
  if (array1.length !== array2.length) return true;
162
163
  for (let i = 0; i < array1.length; i++) {
@@ -12,3 +12,4 @@ export declare const CSS_COLOR_MIX: RegExp;
12
12
  export declare const CSS_COLOR_CONTRAST: RegExp;
13
13
  export declare const CSS_RGBA: RegExp;
14
14
  export declare const CSS_URL: RegExp;
15
+ export declare const INDEX: unique symbol;
package/dist/constants.js CHANGED
@@ -94,6 +94,7 @@ export const CSS_COLOR_MIX = /^color-mix\(/; // just check for prefix
94
94
  export const CSS_COLOR_CONTRAST = /^color-contrast\(/; // just check for prefix
95
95
  export const CSS_RGBA = /^rgba\(/; // just check for prefix
96
96
  export const CSS_URL = /^url\(#/; // just check for prefix
97
+ export const INDEX = Symbol('index');
97
98
  // export const CHANNEL_MAP: Record<ScaleName, ValueOf<typeof SCALE_TYPES>> = {
98
99
  // x: SCALE_TYPES.x,
99
100
  // y: SCALE_TYPES.y,
@@ -108,8 +108,6 @@
108
108
  inset?: number;
109
109
  };
110
110
 
111
- let scaleCounter = $state(0);
112
-
113
111
  /**
114
112
  * the marks used in the plot
115
113
  */
@@ -177,7 +175,9 @@
177
175
 
178
176
  const hasProjection = $derived(!!preScales.projection);
179
177
 
180
- const plotWidth = $derived((fixedWidth || width) - plotOptions.marginLeft - plotOptions.marginRight);
178
+ const plotWidth = $derived(
179
+ (fixedWidth || width) - plotOptions.marginLeft - plotOptions.marginRight
180
+ );
181
181
 
182
182
  // the facet and y domain counts are used for computing the automatic height
183
183
  const xFacetCount = $derived(Math.max(1, preScales.fx.domain.length));
@@ -1,5 +1,6 @@
1
1
  import { resolveProp } from './resolve.js';
2
2
  import { isDate } from './typeChecks';
3
+ import { RAW_VALUE } from '../transforms/recordize.js';
3
4
  /**
4
5
  * Returns first argument that is not null or undefined
5
6
  */
@@ -12,7 +13,8 @@ export function coalesce(...args) {
12
13
  return null; // Return null if all arguments are null or undefined
13
14
  }
14
15
  export function testFilter(datum, options) {
15
- return options.filter == null || resolveProp(options.filter, datum);
16
+ return (options.filter == null ||
17
+ resolveProp(options.filter, datum?.hasOwnProperty(RAW_VALUE) ? datum[RAW_VALUE] : datum));
16
18
  }
17
19
  export function randomId() {
18
20
  return Math.ceil(1e9 + Math.random() * 1e9).toString(36);
@@ -147,7 +147,7 @@ export function createScale(name, scaleOptions, marks, plotOptions, plotWidth, p
147
147
  valueArr.sort(ascending);
148
148
  }
149
149
  const domain = scaleOptions.domain
150
- ? scaleOptions.domain
150
+ ? extent(scaleOptions.zero ? [0, ...scaleOptions.domain] : scaleOptions.domain)
151
151
  : type === 'band' ||
152
152
  type === 'point' ||
153
153
  type === 'ordinal' ||
@@ -46,6 +46,8 @@
46
46
  stack
47
47
  )
48
48
  );
49
+
50
+ $inspect({ args });
49
51
  </script>
50
52
 
51
53
  <Mark
@@ -71,7 +73,7 @@
71
73
  transform="translate({[minx + dx, d.y + inset + dy - bw * 0.5]})"
72
74
  use:addEventHandlers={{
73
75
  getPlotState,
74
- options: mark.options,
76
+ options: args,
75
77
  datum: d.datum
76
78
  }} />
77
79
  {/if}
@@ -85,7 +85,7 @@
85
85
  transform="translate({[d.x + inset + dx - bw * 0.5, miny + dy]})"
86
86
  use:addEventHandlers={{
87
87
  getPlotState,
88
- options: mark.options,
88
+ options: args,
89
89
  datum: d.datum
90
90
  }} />
91
91
  {/if}
@@ -93,7 +93,7 @@
93
93
  ]})"
94
94
  use:addEventHandlers={{
95
95
  getPlotState,
96
- options: mark.options,
96
+ options: args,
97
97
  datum: d.datum
98
98
  }} />
99
99
  {/if}
@@ -123,7 +123,7 @@
123
123
  {style}
124
124
  use:addEventHandlers={{
125
125
  getPlotState,
126
- options: mark.options,
126
+ options: args,
127
127
  datum: d.datum
128
128
  }} />
129
129
  {/if}
@@ -14,10 +14,17 @@
14
14
  let { automatic, class: className = null, ...options }: FrameMarkProps = $props();
15
15
 
16
16
  const { getPlotState } = getContext<PlotContext>('svelteplot');
17
- let plot = $derived(getPlotState());
17
+ const plot = $derived(getPlotState());
18
18
 
19
- let dx = $derived(resolveProp(options.dx, null, 0));
20
- let dy = $derived(resolveProp(options.dy, null, 0));
19
+ const dx = $derived(resolveProp(options.dx, null, 0));
20
+ const dy = $derived(resolveProp(options.dy, null, 0));
21
+
22
+ const {
23
+ insetLeft = options.inset || 0,
24
+ insetTop = options.inset || 0,
25
+ insetRight = options.inset || 0,
26
+ insetBottom = options.inset || 0
27
+ } = $derived(options);
21
28
  </script>
22
29
 
23
30
  <Mark type="frame" {automatic}>
@@ -25,12 +32,12 @@
25
32
  class={['frame', className]}
26
33
  transform={dx || dy ? `translate(${dx},${dy})` : null}
27
34
  style={resolveScaledStyles({}, options, {}, plot, 'stroke')}
28
- x={plot.options.marginLeft}
29
- y={plot.options.marginTop}
35
+ x={plot.options.marginLeft + +insetLeft}
36
+ y={plot.options.marginTop + +insetTop}
30
37
  rx={resolveProp(options.rx, null, null)}
31
38
  ry={resolveProp(options.ry, null, null)}
32
- width={plot.facetWidth}
33
- height={plot.facetHeight}
39
+ width={plot.facetWidth - (insetLeft || 0) - (insetRight || 0)}
40
+ height={plot.facetHeight - (insetBottom || 0) - (insetTop || 0)}
34
41
  use:addEventHandlers={{ getPlotState, options: options, datum: {} }} />
35
42
  </Mark>
36
43
 
@@ -79,7 +79,7 @@
79
79
  )}
80
80
  use:addEventHandlers={{
81
81
  getPlotState,
82
- options: mark.options,
82
+ options: args,
83
83
  datum
84
84
  }}>
85
85
  {#if title}<title>{title}</title>{/if}
@@ -156,7 +156,7 @@
156
156
  )}
157
157
 
158
158
  <MarkerPath
159
- {mark}
159
+ mark={{ ...mark, options: args }}
160
160
  scales={plot.scales}
161
161
  markerStart={args.markerStart}
162
162
  markerEnd={args.markerEnd}
@@ -131,8 +131,13 @@
131
131
  {@const [style, styleClass] = resolveStyles(
132
132
  plot,
133
133
  { ...d, __tspanIndex: 0 },
134
- { fontSize: 12, fontWeight: 500, strokeWidth: 1.6,
135
- textAnchor: isLeft ? 'start' : isRight ? 'end' : 'middle', ...args },
134
+ {
135
+ fontSize: 12,
136
+ fontWeight: 500,
137
+ strokeWidth: 1.6,
138
+ textAnchor: isLeft ? 'start' : isRight ? 'end' : 'middle',
139
+ ...args
140
+ },
136
141
  'fill',
137
142
  usedScales
138
143
  )}
@@ -156,8 +161,11 @@
156
161
  fontSize
157
162
  )
158
163
  ]})"
159
- >{#each textLines as line, l}<tspan x="0" dy={l ? fontSize : 0} class={styleClass} {style}
160
- >{line}</tspan
164
+ >{#each textLines as line, l}<tspan
165
+ x="0"
166
+ dy={l ? fontSize : 0}
167
+ class={styleClass}
168
+ {style}>{line}</tspan
161
169
  >{/each}{#if title}<title>{title}</title>{/if}</text>
162
170
  {:else}
163
171
  <!-- singleline text-->
@@ -48,6 +48,7 @@
48
48
  import Mark from '../Mark.svelte';
49
49
  //import DotCanvas from './helpers/DotCanvas.svelte';
50
50
  import { maybeData, testFilter, isValid } from '../helpers/index.js';
51
+ import { addEventHandlers } from './helpers/events.js';
51
52
 
52
53
  const defaultRadius = 3.5;
53
54
 
@@ -198,6 +199,11 @@
198
199
  ? `translate(0, ${d.length})`
199
200
  : `translate(0, ${d.length / 2})`}"
200
201
  {style}
202
+ use:addEventHandlers={{
203
+ getPlotState,
204
+ options: args,
205
+ datum: d.datum
206
+ }}
201
207
  class={[styleClass]} />
202
208
  {/if}
203
209
  {/each}
@@ -1,4 +1,6 @@
1
- import { pick } from 'es-toolkit';
1
+ import { invert, pick } from 'es-toolkit';
2
+ import { RAW_VALUE } from '../../transforms/recordize.js';
3
+ import { INDEX } from '../../constants.js';
2
4
  export function addEventHandlers(node, { options, datum, getPlotState }) {
3
5
  const events = pick(options, [
4
6
  'onclick',
@@ -38,13 +40,11 @@ export function addEventHandlers(node, { options, datum, getPlotState }) {
38
40
  origEvent.dataY = y;
39
41
  }
40
42
  else {
41
- origEvent.dataX =
42
- scales.x.fn.invert && scales.x.fn.invert(origEvent.layerX);
43
- origEvent.dataY =
44
- scales.y.fn.invert && scales.y.fn.invert(origEvent.layerY);
43
+ origEvent.dataX = invertScale(scales.x, origEvent.layerX);
44
+ origEvent.dataY = invertScale(scales.y, origEvent.layerY);
45
45
  }
46
46
  }
47
- eventHandler(origEvent, datum.___orig___ !== undefined ? datum.___orig___ : datum);
47
+ eventHandler(origEvent, datum.hasOwnProperty(RAW_VALUE) ? datum[RAW_VALUE] : datum, datum[INDEX]);
48
48
  };
49
49
  listeners.set(eventName, wrappedHandler);
50
50
  node.addEventListener(eventName.substring(2), wrappedHandler);
@@ -62,3 +62,13 @@ export function addEventHandlers(node, { options, datum, getPlotState }) {
62
62
  }
63
63
  };
64
64
  }
65
+ function invertScale(scale, position) {
66
+ if (scale.type === 'band') {
67
+ // invert band scale since scaleBand doesn't have an invert function
68
+ const eachBand = scale.fn.step();
69
+ console.log({ eachBand, position });
70
+ const index = Math.floor(position / eachBand);
71
+ return scale.fn.domain()[index];
72
+ }
73
+ return scale.fn.invert ? scale.fn.invert(position) : undefined;
74
+ }
@@ -1,4 +1,5 @@
1
1
  import type { TransformArgsRow, TransformArgsRecord } from '../types.js';
2
+ export declare const RAW_VALUE: unique symbol;
2
3
  export declare function recordizeX({ data, ...channels }: TransformArgsRow, { withIndex }?: {
3
4
  withIndex: boolean;
4
5
  }): TransformArgsRecord;
@@ -1,4 +1,5 @@
1
1
  import isDataRecord from '../helpers/isDataRecord.js';
2
+ export const RAW_VALUE = Symbol();
2
3
  /*
3
4
  * This transform takes an array of raw values as input and returns data records
4
5
  * in which the values are interpreted as x channel and their index as y
@@ -10,6 +11,8 @@ export function recordizeX({ data, ...channels }, { withIndex } = { withIndex: t
10
11
  data: data.map((value, index) => ({
11
12
  __value: value,
12
13
  ...(withIndex ? { __index: index } : {}),
14
+ [RAW_VALUE]: value,
15
+ // TODO: remove ___orig___ references
13
16
  ___orig___: value
14
17
  })),
15
18
  ...channels,
@@ -32,6 +35,7 @@ export function recordizeY({ data, ...channels }, { withIndex } = { withIndex: t
32
35
  data: Array.from(data).map((value, index) => ({
33
36
  __value: value,
34
37
  ...(withIndex ? { __index: index } : {}),
38
+ [RAW_VALUE]: value,
35
39
  ___orig___: value
36
40
  })),
37
41
  ...channels,
@@ -1,10 +1,10 @@
1
1
  import type { DataRecord, DataRow, TransformArg } from '../types.js';
2
+ export declare const SORT_KEY: unique symbol;
2
3
  export declare function sort({ data, ...channels }: TransformArg<DataRecord>, options?: {
3
4
  reverse?: boolean;
4
5
  }): {
5
6
  sort: null;
6
7
  data: {
7
- __sortkey: import("../types.js").RawValue;
8
8
  ___orig___?: import("../types.js").RawValue | [import("../types.js").RawValue, import("../types.js").RawValue];
9
9
  }[];
10
10
  } | {
@@ -2,6 +2,7 @@ import isDataRecord from '../helpers/isDataRecord.js';
2
2
  import { resolveChannel } from '../helpers/resolve.js';
3
3
  import { shuffler } from 'd3-array';
4
4
  import { randomLcg } from 'd3-random';
5
+ export const SORT_KEY = Symbol('sortKey');
5
6
  export function sort({ data, ...channels }, options = {}) {
6
7
  if (!Array.isArray(data))
7
8
  return { data, ...channels };
@@ -16,11 +17,15 @@ export function sort({ data, ...channels }, options = {}) {
16
17
  // sort data
17
18
  return {
18
19
  data: data
19
- .map((d) => ({ ...d, __sortkey: resolveChannel('sort', d, { ...channels, sort }) }))
20
- .toSorted((a, b) => (a.__sortkey > b.__sortkey ? 1 : a.__sortkey < b.__sortkey ? -1 : 0) *
20
+ .map((d) => ({
21
+ ...d,
22
+ [SORT_KEY]: resolveChannel('sort', d, { ...channels, sort })
23
+ }))
24
+ .toSorted((a, b) => (a[SORT_KEY] > b[SORT_KEY] ? 1 : a[SORT_KEY] < b[SORT_KEY] ? -1 : 0) *
21
25
  (options.reverse || (isDataRecord(sort) && sort?.order === 'descending')
22
26
  ? -1
23
- : 1)),
27
+ : 1))
28
+ .map(({ [SORT_KEY]: a, ...rest }) => rest),
24
29
  ...channels,
25
30
  // set the sort channel to null to disable the implicit alphabetical
26
31
  // ordering of ordinal domains, and also to avoid double sorting in case
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svelteplot",
3
- "version": "0.1.3-next.7",
3
+ "version": "0.1.3-next.9",
4
4
  "license": "ISC",
5
5
  "author": {
6
6
  "name": "Gregor Aisch",
@@ -67,12 +67,12 @@
67
67
  "@types/d3-scale": "^4.0.9",
68
68
  "@types/d3-scale-chromatic": "^3.1.0",
69
69
  "@types/d3-shape": "^3.1.7",
70
- "@typescript-eslint/eslint-plugin": "^8.30.1",
71
- "@typescript-eslint/parser": "^8.30.1",
70
+ "@typescript-eslint/eslint-plugin": "^8.31.1",
71
+ "@typescript-eslint/parser": "^8.31.1",
72
72
  "d3-dsv": "^3.0.1",
73
73
  "d3-fetch": "^3.0.1",
74
74
  "d3-force": "^3.0.0",
75
- "eslint": "^9.25.0",
75
+ "eslint": "^9.25.1",
76
76
  "eslint-config-prettier": "^10.1.2",
77
77
  "eslint-plugin-svelte": "3.5.1",
78
78
  "jsdom": "^26.1.0",
@@ -81,17 +81,17 @@
81
81
  "remark-code-extra": "^1.0.1",
82
82
  "remark-code-frontmatter": "^1.0.0",
83
83
  "resize-observer-polyfill": "^1.5.1",
84
- "sass": "^1.86.3",
84
+ "sass": "^1.87.0",
85
85
  "svelte-check": "^4.1.6",
86
86
  "svelte-eslint-parser": "1.1.3",
87
87
  "svelte-highlight": "^7.8.3",
88
88
  "topojson-client": "^3.1.0",
89
89
  "tslib": "^2.8.1",
90
90
  "typedoc": "^0.28.3",
91
- "typedoc-plugin-markdown": "^4.6.2",
91
+ "typedoc-plugin-markdown": "^4.6.3",
92
92
  "typescript": "^5.8.3",
93
- "vite": "^6.3.2",
94
- "vitest": "^3.1.1"
93
+ "vite": "^6.3.3",
94
+ "vitest": "^3.1.2"
95
95
  },
96
96
  "types": "./dist/index.d.ts",
97
97
  "type": "module",
@@ -109,9 +109,9 @@
109
109
  "d3-scale-chromatic": "^3.1.0",
110
110
  "d3-shape": "^3.2.0",
111
111
  "d3-time": "^3.1.0",
112
- "es-toolkit": "^1.35.0",
112
+ "es-toolkit": "^1.36.0",
113
113
  "fast-equals": "^5.2.2",
114
114
  "merge-deep": "^3.0.3",
115
- "svelte": "5.28.1"
115
+ "svelte": "5.28.2"
116
116
  }
117
117
  }