svelteplot 0.4.2 → 0.4.3

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.
@@ -9,11 +9,14 @@
9
9
  ConstantAccessor,
10
10
  PlotState,
11
11
  RawValue,
12
+ ScaledDataRecord,
12
13
  ScaleType
13
14
  } from '../../types/index.js';
14
15
  import { resolveProp, resolveStyles } from '../../helpers/resolve.js';
15
16
  import { max } from 'd3-array';
16
17
  import { randomId, testFilter } from '../../helpers/index.js';
18
+ import { INDEX } from '../../constants';
19
+ import { RAW_VALUE } from '../../transforms/recordize';
17
20
 
18
21
  type BaseAxisXProps = {
19
22
  scaleFn: (d: RawValue) => number;
@@ -76,11 +79,12 @@
76
79
  const positionedTicks = $derived.by(() => {
77
80
  let tickObjects = removeIdenticalLines(
78
81
  ticks.map((tick, i) => {
82
+ const datum = { [RAW_VALUE]: tick, [INDEX]: i };
79
83
  return {
80
- value: tick,
84
+ ...datum,
81
85
  hidden: false,
82
- dx: +resolveProp(options.dx, tick, 0),
83
- dy: +resolveProp(options.dy, tick, 0),
86
+ dx: +resolveProp(options.dx, datum, 0),
87
+ dy: +resolveProp(options.dy, datum, 0),
84
88
  x: scaleFn(tick) + (scaleType === 'band' ? scaleFn.bandwidth() * 0.5 : 0),
85
89
  text: splitTick(tickFormat(tick, i)),
86
90
  element: null as SVGTextElement | null
@@ -101,7 +105,7 @@
101
105
  }
102
106
  }
103
107
  }
104
- return tickObjects;
108
+ return tickObjects as ScaledDataRecord[];
105
109
  });
106
110
 
107
111
  $effect(() => {
@@ -114,11 +118,11 @@
114
118
  max(
115
119
  positionedTicks.map((tick, i) => {
116
120
  if (
117
- resolveProp(options.anchor, tick.value, outsideTextAnchor) !==
121
+ resolveProp(options.anchor, tick, outsideTextAnchor) !==
118
122
  outsideTextAnchor
119
123
  )
120
124
  return 0;
121
- if (tick.hidden || !testFilter(tick.value, options)) return 0;
125
+ if (tick.hidden || !testFilter(tick, options)) return 0;
122
126
  if (tickTextElements[i])
123
127
  return tickTextElements[i].getBoundingClientRect().height;
124
128
  return 0;
@@ -148,8 +152,9 @@
148
152
 
149
153
  <g class="axis-x">
150
154
  {#each positionedTicks as tick, t (t)}
151
- {#if testFilter(tick.value, options) && !tick.hidden}
152
- {@const tickClass_ = resolveProp(tickClass, tick.value)}
155
+ {#if testFilter(tick, options) && !tick.hidden}
156
+ {@const tickClass_ = resolveProp(tickClass, tick)}
157
+ {@const tickFontSize_ = +resolveProp(tickFontSize, tick, 10)}
153
158
  <g
154
159
  class="tick {tickClass_ || ''}"
155
160
  transform="translate({tick.x + tick.dx}, {tickY + tick.dy})"
@@ -157,7 +162,7 @@
157
162
  {#if tickSize}
158
163
  {@const [tickLineStyle, tickLineClass] = resolveStyles(
159
164
  plot,
160
- tick,
165
+ { datum: tick },
161
166
  options,
162
167
  'stroke',
163
168
  { x: true },
@@ -174,15 +179,15 @@
174
179
  {@const prevTextLines = t && positionedTicks[t - 1].text}
175
180
 
176
181
  {@const moveDown =
177
- (tickSize + tickPadding + (tickRotate !== 0 ? tickFontSize * 0.35 : 0)) *
182
+ (tickSize + tickPadding + (tickRotate !== 0 ? tickFontSize_ * 0.35 : 0)) *
178
183
  (anchor === 'bottom' ? 1 : -1)}
179
184
  {@const [textStyle, textClass] = resolveStyles(
180
185
  plot,
181
- tick,
186
+ { datum: tick },
182
187
  {
183
188
  fontVariant: isQuantitative ? 'tabular-nums' : 'normal',
184
189
  ...options,
185
- fontSize: tickFontSize,
190
+ fontSize: tickFontSize_,
186
191
  stroke: null
187
192
  },
188
193
  'fill',
@@ -6,10 +6,14 @@
6
6
  import type {
7
7
  AutoMarginStores,
8
8
  ConstantAccessor,
9
+ DataRecord,
9
10
  PlotState,
10
11
  RawValue,
12
+ ScaledDataRecord,
11
13
  ScaleType
12
14
  } from '../../types/index.js';
15
+ import { RAW_VALUE } from '../../transforms/recordize';
16
+ import { INDEX } from '../../constants';
13
17
 
14
18
  type BaseAxisYProps = {
15
19
  scaleFn: (d: RawValue) => number;
@@ -61,13 +65,14 @@
61
65
 
62
66
  const positionedTicks = $derived.by(() => {
63
67
  let tickObjects = ticks.map((tick, i) => {
68
+ const datum = { [RAW_VALUE]: tick, [INDEX]: i };
64
69
  return {
65
- value: tick,
70
+ ...datum,
66
71
  hidden: false,
67
- dx: +resolveProp(options.dx, tick, 0),
68
- dy: +resolveProp(options.dy, tick, 0),
72
+ dx: +resolveProp(options.dx, datum, 0),
73
+ dy: +resolveProp(options.dy, datum, 0),
69
74
  y: scaleFn(tick) + (scaleType === 'band' ? scaleFn.bandwidth() * 0.5 : 0),
70
- text: tickFormat(tick),
75
+ text: tickFormat(tick, i),
71
76
  element: null as SVGTextElement | null
72
77
  };
73
78
  });
@@ -107,11 +112,11 @@
107
112
  max(
108
113
  positionedTicks.map((tick, i) => {
109
114
  if (
110
- resolveProp(options.textAnchor, tick.value, outsideTextAnchor) !==
115
+ resolveProp(options.textAnchor, tick, outsideTextAnchor) !==
111
116
  outsideTextAnchor
112
117
  )
113
118
  return 0;
114
- if (tick.hidden || !testFilter(tick.value, options)) return 0;
119
+ if (tick.hidden || !testFilter(tick, options)) return 0;
115
120
  if (tickTexts[i]) return tickTexts[i].getBoundingClientRect().width;
116
121
  return 0;
117
122
  }) as number[]
@@ -150,11 +155,11 @@
150
155
 
151
156
  <g class="axis-y">
152
157
  {#each positionedTicks as tick, t (t)}
153
- {#if testFilter(tick.value, options) && !tick.hidden}
154
- {@const tickClass_ = resolveProp(tickClass, tick.value)}
158
+ {#if testFilter(tick, options) && !tick.hidden}
159
+ {@const tickClass_ = resolveProp(tickClass, tick)}
155
160
  {@const [textStyle, textClass] = resolveStyles(
156
161
  plot,
157
- tick,
162
+ { datum: tick },
158
163
  {
159
164
  fontVariant: isQuantitative ? 'tabular-nums' : 'normal',
160
165
  ...options,
@@ -173,7 +178,7 @@
173
178
  {#if tickSize}
174
179
  {@const [tickLineStyle, tickLineClass] = resolveStyles(
175
180
  plot,
176
- tick,
181
+ { datum: tick },
177
182
  options,
178
183
  'stroke',
179
184
  { y: true },
@@ -95,7 +95,11 @@
95
95
  {transform}
96
96
  class={className}
97
97
  stroke-width={strokeWidth_}
98
- use:addEventHandlers={{ getPlotState, options: mark.options, datum }}>
98
+ {@attach addEventHandlers({
99
+ getPlotState,
100
+ options: mark.options,
101
+ datum: datum
102
+ })}>
99
103
  {#each Object.entries( { start: markerStart, mid: markerMid, end: markerEnd, all: marker } ) as [key, marker] (key)}
100
104
  {@const markerId = `marker-${key === 'all' ? '' : `${key}-`}${id}`}
101
105
  {#if isSnippet(marker)}
@@ -122,7 +126,11 @@
122
126
  marker-end={markerEnd || marker ? `url(#marker-${markerEnd ? 'end-' : ''}${id})` : null}
123
127
  {d}
124
128
  {style}
125
- use:addEventHandlers={{ getPlotState, options: mark.options, datum }} />
129
+ {@attach addEventHandlers({
130
+ getPlotState,
131
+ options: mark.options,
132
+ datum: datum
133
+ })} />
126
134
  {#if text}
127
135
  <!-- since textPath.side is not yet supported, we have to use an invisible
128
136
  path in order to keep the text from turning upside down -->
@@ -28,18 +28,13 @@ Helper component for rendering rectangular marks in SVG
28
28
 
29
29
  import { resolveProp, resolveStyles } from '../../helpers/resolve';
30
30
  import { roundedRect } from '../../helpers/roundedRect';
31
- import type {
32
- BaseMarkProps,
33
- BaseRectMarkProps,
34
- BorderRadius,
35
- ScaledDataRecord,
36
- UsedScales,
37
- PlotContext,
38
- DataRecord
39
- } from '../../index.js';
40
31
  import { addEventHandlers } from './events.js';
41
32
  import { getContext } from 'svelte';
42
33
  import Anchor from './Anchor.svelte';
34
+ import type { BaseMarkProps, BaseRectMarkProps, BorderRadius } from '../../types/mark.js';
35
+ import type { DataRecord, ScaledDataRecord } from '../../types/data.js';
36
+ import type { PlotContext, UsedScales } from '../../types/index.js';
37
+ import { RAW_VALUE } from '../../transforms/recordize.js';
43
38
 
44
39
  let {
45
40
  datum,
@@ -116,11 +111,11 @@ Helper component for rendering rectangular marks in SVG
116
111
  )}
117
112
  class={[styleClass, className]}
118
113
  {style}
119
- use:addEventHandlers={{
114
+ {@attach addEventHandlers({
120
115
  getPlotState,
121
116
  options,
122
117
  datum: datum?.datum
123
- }} />
118
+ })} />
124
119
  {:else}
125
120
  <rect
126
121
  transform="translate({x + insetLeft},{y + insetBottom})"
@@ -128,10 +123,10 @@ Helper component for rendering rectangular marks in SVG
128
123
  height={height - insetTop - insetBottom}
129
124
  class={[styleClass, className]}
130
125
  {style}
131
- use:addEventHandlers={{
126
+ {@attach addEventHandlers({
132
127
  getPlotState,
133
128
  options,
134
129
  datum: datum?.datum
135
- }} />
130
+ })} />
136
131
  {/if}
137
132
  </Anchor>
@@ -1,13 +1,74 @@
1
- import type { BaseMarkProps, BaseRectMarkProps, ScaledDataRecord, UsedScales, DataRecord } from '../../index.js';
1
+ import type { BaseRectMarkProps } from '../../types/mark.js';
2
+ import type { DataRecord, ScaledDataRecord } from '../../types/data.js';
3
+ import type { UsedScales } from '../../types/index.js';
2
4
  declare class __sveltets_Render<Datum extends DataRecord> {
3
5
  props(): {
4
- datum: ScaledDataRecord<Datum_1>;
6
+ datum: ScaledDataRecord<Datum>;
5
7
  class: string | null;
6
8
  x: number;
7
9
  y: number;
8
10
  width: number;
9
11
  height: number;
10
- options: BaseRectMarkProps<Datum_1> & BaseMarkProps<Datum_1>;
12
+ options: BaseRectMarkProps<Datum> & Partial<{
13
+ filter?: import("../../types/index.js").ConstantAccessor<boolean, Datum>;
14
+ facet?: "auto" | "include" | "exclude";
15
+ fx: import("../../types/index.js").ChannelAccessor<Datum>;
16
+ fy: import("../../types/index.js").ChannelAccessor<Datum>;
17
+ dx: import("../../types/index.js").ConstantAccessor<number, Datum>;
18
+ dy: import("../../types/index.js").ConstantAccessor<number, Datum>;
19
+ fill: import("../../types/index.js").ChannelAccessor<Datum>;
20
+ fillOpacity: import("../../types/index.js").ConstantAccessor<number, Datum>;
21
+ sort: {
22
+ channel: string;
23
+ order?: "ascending" | "descending";
24
+ } | ((a: import("../../types/data.js").RawValue, b: import("../../types/data.js").RawValue) => number) | import("../../types/index.js").ConstantAccessor<import("../../types/data.js").RawValue, Datum>;
25
+ stroke: import("../../types/index.js").ChannelAccessor<Datum>;
26
+ strokeWidth: import("../../types/index.js").ConstantAccessor<number, Datum>;
27
+ strokeOpacity: import("../../types/index.js").ConstantAccessor<number, Datum>;
28
+ strokeLinejoin: import("../../types/index.js").ConstantAccessor<import("csstype").Property.StrokeLinejoin, Datum>;
29
+ strokeLinecap: import("../../types/index.js").ConstantAccessor<import("csstype").Property.StrokeLinecap, Datum>;
30
+ strokeMiterlimit: import("../../types/index.js").ConstantAccessor<number, Datum>;
31
+ opacity: import("../../types/index.js").ChannelAccessor<Datum>;
32
+ strokeDasharray: import("../../types/index.js").ConstantAccessor<string, Datum>;
33
+ strokeDashoffset: import("../../types/index.js").ConstantAccessor<number, Datum>;
34
+ mixBlendMode: import("../../types/index.js").ConstantAccessor<import("csstype").Property.MixBlendMode, Datum>;
35
+ clipPath: string;
36
+ imageFilter: import("../../types/index.js").ConstantAccessor<string, Datum>;
37
+ shapeRendering: import("../../types/index.js").ConstantAccessor<import("csstype").Property.ShapeRendering, Datum>;
38
+ paintOrder: import("../../types/index.js").ConstantAccessor<string, Datum>;
39
+ onclick?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
40
+ ondblclick?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
41
+ onmouseup?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
42
+ onmousedown?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
43
+ onmouseenter?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
44
+ onmousemove?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
45
+ onmouseleave?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
46
+ onmouseout?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
47
+ onmouseover?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
48
+ onpointercancel?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
49
+ onpointerdown?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
50
+ onpointerup?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
51
+ onpointerenter?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
52
+ onpointerleave?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
53
+ onpointermove?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
54
+ onpointerover?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
55
+ onpointerout?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
56
+ ondrag?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
57
+ ondrop?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
58
+ ondragstart?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
59
+ ondragenter?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
60
+ ondragleave?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
61
+ ondragover?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
62
+ ondragend?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
63
+ ontouchstart?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
64
+ ontouchmove?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
65
+ ontouchend?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
66
+ ontouchcancel?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
67
+ oncontextmenu?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
68
+ onwheel?: import("svelte/elements.js").MouseEventHandler<SVGPathElement>;
69
+ class: string | null;
70
+ cursor: import("../../types/index.js").ConstantAccessor<import("csstype").Property.Cursor, Datum>;
71
+ }>;
11
72
  /**
12
73
  * By default, the `inset` property is applied to all four insets. Mark components
13
74
  * can tweak this behavior for insetTop and insetBottom by setting the
@@ -1,4 +1,5 @@
1
- import type { BaseMarkProps, DataRecord, PlotState } from '../../types/index.js';
1
+ import type { BaseMarkProps, DataRecord, DataRow, PlotState } from '../../types/index.js';
2
+ import type { Attachment } from 'svelte/attachments';
2
3
  declare global {
3
4
  interface MouseEvent {
4
5
  layerX?: number;
@@ -12,10 +13,8 @@ declare global {
12
13
  * of the plot frame, regardless of which element triggered the event
13
14
  */
14
15
  export declare function clientToLayerCoordinates(event: MouseEvent, plotBody: HTMLElement | null | undefined): [number, number];
15
- export declare function addEventHandlers(node: SVGElement, { options, datum, getPlotState }: {
16
- options: BaseMarkProps;
16
+ export declare function addEventHandlers<T extends DataRow>({ options, datum, getPlotState }: {
17
+ options: BaseMarkProps<T>;
17
18
  datum: DataRecord;
18
19
  getPlotState: () => PlotState;
19
- }): {
20
- destroy(): void;
21
- };
20
+ }): Attachment;
@@ -19,7 +19,7 @@ export function clientToLayerCoordinates(event, plotBody) {
19
19
  // Calculate the coordinates relative to the plot body
20
20
  return [event.clientX - plotBodyRect.left, event.clientY - plotBodyRect.top];
21
21
  }
22
- export function addEventHandlers(node, { options, datum, getPlotState }) {
22
+ export function addEventHandlers({ options, datum, getPlotState }) {
23
23
  const events = pick(options, [
24
24
  'onclick',
25
25
  'oncontextmenu',
@@ -51,50 +51,62 @@ export function addEventHandlers(node, { options, datum, getPlotState }) {
51
51
  'ontouchmove',
52
52
  'onwheel'
53
53
  ]);
54
- const listeners = new Map();
55
- // attach event handlers
56
- for (const [eventName, eventHandler] of Object.entries(events)) {
57
- if (eventHandler) {
58
- const wrappedHandler = (origEvent) => {
59
- const { scales } = getPlotState();
60
- if (origEvent.layerX !== undefined) {
61
- if (scales.projection) {
62
- const [x, y] = scales.projection.invert([
63
- origEvent.layerX,
64
- origEvent.layerY
65
- ]);
66
- origEvent.dataX = x;
67
- origEvent.dataY = y;
54
+ return (node) => {
55
+ const listeners = new Map();
56
+ // attach event handlers
57
+ for (const [eventName, eventHandler] of Object.entries(events)) {
58
+ if (eventHandler) {
59
+ const wrappedHandler = (origEvent) => {
60
+ const { scales, body, options } = getPlotState();
61
+ if (origEvent instanceof MouseEvent || origEvent instanceof PointerEvent) {
62
+ let facetEl = origEvent.target;
63
+ while (facetEl &&
64
+ !facetEl.classList.contains('facet') &&
65
+ facetEl.parentElement) {
66
+ // ensure that parentElement is SVGElement
67
+ if (!(facetEl.parentElement instanceof SVGElement))
68
+ break;
69
+ facetEl = facetEl.parentElement;
70
+ }
71
+ const facetRect = (facetEl?.firstElementChild ?? body).getBoundingClientRect();
72
+ const relativeX = origEvent.clientX - facetRect.left + (options.marginLeft ?? 0);
73
+ const relativeY = origEvent.clientY - facetRect.top + (options.marginTop ?? 0);
74
+ if (scales.projection) {
75
+ const [x, y] = scales.projection.invert([relativeX, relativeY]);
76
+ origEvent.dataX = x;
77
+ origEvent.dataY = y;
78
+ }
79
+ else {
80
+ origEvent.dataX = invertScale(scales.x, relativeX);
81
+ origEvent.dataY = invertScale(scales.y, relativeY);
82
+ }
68
83
  }
69
- else {
70
- origEvent.dataX = invertScale(scales.x, origEvent.layerX);
71
- origEvent.dataY = invertScale(scales.y, origEvent.layerY);
72
- }
73
- }
74
- eventHandler(origEvent, datum.hasOwnProperty(RAW_VALUE) ? datum[RAW_VALUE] : datum, datum[INDEX]);
75
- };
76
- listeners.set(eventName, wrappedHandler);
77
- node.addEventListener(eventName.substring(2), wrappedHandler);
84
+ eventHandler(origEvent, datum.hasOwnProperty(RAW_VALUE) ? datum[RAW_VALUE] : datum, datum[INDEX]);
85
+ };
86
+ listeners.set(eventName, wrappedHandler);
87
+ node.addEventListener(eventName.substring(2), wrappedHandler);
88
+ }
78
89
  }
79
- }
80
- if (events.onclick || events.onmousedown || events.onmouseup) {
81
- // force role button
82
- node.setAttribute('role', 'button');
83
- }
84
- return {
85
- destroy() {
90
+ if (events.onclick || events.onmousedown || events.onmouseup) {
91
+ // force role button
92
+ node.setAttribute('role', 'button');
93
+ }
94
+ return () => {
86
95
  for (const [eventName, handler] of listeners.entries()) {
87
96
  node.removeEventListener(eventName.substring(2), handler);
88
97
  }
89
- }
98
+ };
90
99
  };
91
100
  }
92
101
  function invertScale(scale, position) {
93
102
  if (scale.type === 'band') {
94
- // invert band scale since scaleBand doesn't have an invert function
103
+ const range = scale.fn.range();
104
+ const domain = scale.fn.domain();
95
105
  const eachBand = scale.fn.step();
96
- const index = Math.floor(position / eachBand);
97
- return scale.fn.domain()[index];
106
+ const extent = range[1] - range[0];
107
+ const posInRange = (position - range[0]) * Math.sign(extent);
108
+ const index = Math.floor(posInRange / eachBand);
109
+ return domain[index];
98
110
  }
99
111
  return scale.fn.invert ? scale.fn.invert(position) : undefined;
100
112
  }
@@ -1,16 +1,21 @@
1
1
  import type { TransformArgsRow, TransformArgsRecord } from '../types/index.js';
2
- export declare const INDEX: unique symbol;
2
+ import { INDEX } from '../constants';
3
+ export declare const X: unique symbol;
4
+ export declare const Y: unique symbol;
3
5
  export declare const RAW_VALUE: unique symbol;
4
- export declare function recordizeX({ data, ...channels }: TransformArgsRow, { withIndex }?: {
6
+ export declare function indexData<T extends object>(data: T[]): (T & {
7
+ [INDEX]: number;
8
+ })[];
9
+ export declare function recordizeX<T>({ data, ...channels }: TransformArgsRow<T>, { withIndex }?: {
5
10
  withIndex: boolean;
6
- }): TransformArgsRecord;
7
- export declare function recordizeY({ data, ...channels }: TransformArgsRow, { withIndex }?: {
11
+ }): TransformArgsRecord<T>;
12
+ export declare function recordizeY<T>({ data, ...channels }: TransformArgsRow<T>, { withIndex }?: {
8
13
  withIndex: boolean;
9
- }): TransformArgsRecord;
14
+ }): TransformArgsRecord<T>;
10
15
  /**
11
16
  * This transform is used to allow users to pass an [[x0, y0], [x1, y1], ...] array
12
17
  * as dataset to marks that support it. It transforms the arrays into records, so
13
18
  * the rest of our code doesn't have to deal with this case anymore.
14
19
  */
15
- export declare function recordizeXY({ data, ...channels }: TransformArgsRow): TransformArgsRecord;
16
- export declare function recordize({ data, ...channels }: TransformArgsRow): TransformArgsRecord;
20
+ export declare function recordizeXY<T>({ data, ...channels }: TransformArgsRow<T>): TransformArgsRecord<T>;
21
+ export declare function recordize<T>({ data, ...channels }: TransformArgsRow<T>): TransformArgsRecord<T>;
@@ -1,6 +1,11 @@
1
1
  import isDataRecord from '../helpers/isDataRecord.js';
2
- export const INDEX = Symbol('index');
2
+ import { INDEX } from '../constants';
3
+ export const X = Symbol('x');
4
+ export const Y = Symbol('y');
3
5
  export const RAW_VALUE = Symbol('originalValue');
6
+ export function indexData(data) {
7
+ return data.map((d, i) => ({ ...d, [INDEX]: i }));
8
+ }
4
9
  /*
5
10
  * This transform takes an array of raw values as input and returns data records
6
11
  * in which the values are interpreted as x channel and their index as y
@@ -10,16 +15,15 @@ export function recordizeX({ data, ...channels }, { withIndex } = { withIndex: t
10
15
  if (dataIsRawValueArray) {
11
16
  return {
12
17
  data: data.map((value, index) => ({
13
- __value: value,
14
- ...(withIndex ? { [INDEX]: index } : {}),
15
- [RAW_VALUE]: value
18
+ [RAW_VALUE]: value,
19
+ [INDEX]: index
16
20
  })),
17
21
  ...channels,
18
22
  x: RAW_VALUE,
19
23
  ...(withIndex ? { y: INDEX } : {})
20
24
  };
21
25
  }
22
- return { data: data, ...channels };
26
+ return { data: indexData(data), ...channels };
23
27
  }
24
28
  /*
25
29
  * This transform takes an array of raw values as input and returns data records
@@ -32,22 +36,15 @@ export function recordizeY({ data, ...channels }, { withIndex } = { withIndex: t
32
36
  if (dataIsRawValueArray) {
33
37
  return {
34
38
  data: Array.from(data).map((value, index) => ({
35
- ...(withIndex ? { __index: index } : {}),
39
+ [INDEX]: index,
36
40
  [RAW_VALUE]: value
37
41
  })),
38
42
  ...channels,
39
- ...(withIndex ? { x: '__index' } : {}),
43
+ ...(withIndex ? { x: INDEX } : {}),
40
44
  y: RAW_VALUE
41
45
  };
42
46
  }
43
- return {
44
- data: Array.from(data).map((d, index) => ({
45
- ...d,
46
- ...(withIndex ? { __index: index } : {})
47
- })),
48
- x: '__index',
49
- ...channels
50
- };
47
+ return { data: indexData(data), ...channels };
51
48
  }
52
49
  /**
53
50
  * This transform is used to allow users to pass an [[x0, y0], [x1, y1], ...] array
@@ -62,28 +59,30 @@ export function recordizeXY({ data, ...channels }) {
62
59
  channels.x === undefined &&
63
60
  channels.y === undefined) {
64
61
  return {
65
- data: data.map(([x, y, ...rest]) => ({
62
+ data: data.map(([x, y, ...rest], i) => ({
66
63
  [RAW_VALUE]: [x, y, ...rest],
67
- __x: x,
68
- __y: y
64
+ [INDEX]: i,
65
+ [X]: x,
66
+ [Y]: y
69
67
  })),
70
68
  ...channels,
71
- x: '__x',
72
- y: '__y'
69
+ x: X,
70
+ y: Y
73
71
  };
74
72
  }
75
- return { data, ...channels };
73
+ return { data: data, ...channels };
76
74
  }
77
75
  export function recordize({ data, ...channels }) {
78
76
  if (!data)
79
77
  return { data, ...channels };
80
78
  if (!isDataRecord(data[0])) {
81
79
  return {
82
- data: data.map((d) => ({
83
- [RAW_VALUE]: d
80
+ data: data.map((d, i) => ({
81
+ [RAW_VALUE]: d,
82
+ [INDEX]: i
84
83
  })),
85
84
  ...channels
86
85
  };
87
86
  }
88
- return { data, ...channels };
87
+ return { data: indexData(data), ...channels };
89
88
  }
@@ -7,7 +7,7 @@ export declare function sort<T>({ data, ...channels }: TransformArg<T>, options?
7
7
  [IS_SORTED]: string | number | true | symbol | Date | {
8
8
  channel: string;
9
9
  order?: "ascending" | "descending";
10
- } | ((a: import("../types/index.js").RawValue, b: import("../types/index.js").RawValue) => number) | ((d: T) => import("../types/index.js").RawValue);
10
+ } | ((a: import("../types/index.js").RawValue, b: import("../types/index.js").RawValue) => number) | ((d: T, index: number) => import("../types/index.js").RawValue);
11
11
  sort: null;
12
12
  filter?: import("../types/index.js").ConstantAccessor<boolean, T>;
13
13
  facet?: "auto" | "include" | "exclude" | undefined;
@@ -6,7 +6,7 @@ export type ChannelAccessor<T = Record<string | symbol, RawValue>> = ChannelValu
6
6
  /** you can bypass the scale by passing null */
7
7
  scale: boolean | null;
8
8
  };
9
- export type ChannelValue<T = Record<string | symbol, RawValue>> = RawValue | keyof T | ((d: T) => RawValue) | null | undefined;
9
+ export type ChannelValue<T = Record<string | symbol, RawValue>> = RawValue | keyof T | ((d: T, index: number) => RawValue) | null | undefined;
10
10
  export type ScaledChannelName = 'fill' | 'fillOpacity' | 'opacity' | 'r' | 'length' | 'stroke' | 'strokeOpacity' | 'symbol' | 'fx' | 'fy' | 'x' | 'x1' | 'x2' | 'y' | 'y1' | 'y2';
11
11
  export type ScaledChannelType<T extends ScaledChannelName> = T extends 'fill' | 'stroke' | 'symbol' ? string : number;
12
12
  export type ChannelName = ScaledChannelName | 'z' | 'sort' | 'filter' | 'interval';
@@ -3,6 +3,7 @@ export type RawValue = number | Date | boolean | string | symbol;
3
3
  export type DataRecord<T = Record<string | symbol, RawValue>> = T;
4
4
  export type ResolvedDataRecord<T = Record<string | symbol, RawValue>> = Partial<Record<ScaledChannelName, any>> & {
5
5
  datum: DataRecord<T>;
6
+ index: number;
6
7
  };
7
8
  export type ScaledDataRecord<T = Record<string | symbol, RawValue>> = Partial<{
8
9
  [K in ScaledChannelName]?: ScaledChannelType<K>;
@@ -11,5 +12,6 @@ export type ScaledDataRecord<T = Record<string | symbol, RawValue>> = Partial<{
11
12
  dy: number;
12
13
  datum: DataRecord<T>;
13
14
  valid: Boolean;
15
+ index: number;
14
16
  };
15
17
  export type DataRow<T = Record<string | symbol, RawValue>> = DataRecord<T> | RawValue | [number, number] | null;
@@ -2,7 +2,7 @@ import type { Snippet } from 'svelte';
2
2
  import type { Writable } from 'svelte/store';
3
3
  import type { MarkerShape } from '../marks/helpers/Marker.svelte';
4
4
  import type { Channels, ScaledChannelName } from './channel.js';
5
- import type { DataRecord, DataRow, RawValue } from './data.js';
5
+ import type { RawValue } from './data.js';
6
6
  import type { BaseMarkProps } from './mark.js';
7
7
  export type GenericMarkOptions = Record<string | symbol, any>;
8
8
  export type CurveName = 'basis' | 'basis-closed' | 'basis-open' | 'bundle' | 'bump-x' | 'bump-y' | 'cardinal' | 'cardinal-closed' | 'cardinal-open' | 'catmull-rom' | 'catmull-rom-closed' | 'catmull-rom-open' | 'linear' | 'linear-closed' | 'monotone-x' | 'monotone-y' | 'natural' | 'step' | 'step-after' | 'step-before';
@@ -24,18 +24,18 @@ export type MarkerOptions = {
24
24
  */
25
25
  marker?: boolean | MarkerShape | Snippet;
26
26
  };
27
- export type ConstantAccessor<K, T = Record<string | symbol, RawValue>> = K | ((d: T) => K) | null | undefined;
27
+ export type ConstantAccessor<K, T = Record<string | symbol, RawValue>> = K | ((d: T, index: number) => K) | null | undefined;
28
28
  export type TransformArg<T> = Channels<T> & BaseMarkProps<T> & {
29
29
  data: T[];
30
30
  };
31
31
  export type MapArg<T> = Channels<T> & {
32
32
  data: T[];
33
33
  };
34
- export type TransformArgsRow = Partial<Channels> & {
35
- data: DataRow[];
34
+ export type TransformArgsRow<T extends RawValue & object> = Partial<Channels<T>> & {
35
+ data: T[];
36
36
  };
37
- export type TransformArgsRecord = Partial<Channels> & {
38
- data: DataRecord[];
37
+ export type TransformArgsRecord<T extends object> = Partial<Channels<T>> & {
38
+ data: T[];
39
39
  };
40
40
  export type AutoMarginStores = {
41
41
  autoMarginTop: Writable<Map<string, number>>;