layerchart 2.0.0-next.56 → 2.0.0-next.58

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 (29) hide show
  1. package/dist/components/AnnotationLine.svelte +112 -66
  2. package/dist/components/AnnotationLine.svelte.d.ts +10 -2
  3. package/dist/components/AnnotationPoint.svelte +97 -23
  4. package/dist/components/AnnotationPoint.svelte.d.ts +8 -1
  5. package/dist/components/AnnotationRange.svelte +17 -6
  6. package/dist/components/GeoPath.svelte +4 -4
  7. package/dist/components/Link.svelte +261 -75
  8. package/dist/components/Link.svelte.d.ts +69 -26
  9. package/dist/components/Spline.svelte +2 -2
  10. package/dist/components/Text.svelte +1 -1
  11. package/dist/components/Voronoi.svelte +35 -6
  12. package/dist/components/Voronoi.svelte.d.ts +9 -0
  13. package/dist/components/charts/__screenshots__/BarChart.svelte.test.ts/BarChart-separate-data-per-series-should-render-stacked-series-with-separate-data-arrays-1.png +0 -0
  14. package/dist/components/charts/__screenshots__/BarChart.svelte.test.ts/BarChart-separate-data-per-series-should-render-stacked-series-with-separate-data-arrays-2.png +0 -0
  15. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-ScatterChart--single-point--quadtree-mode--should-show-series-header-for-multi-series-1.png +0 -0
  16. package/dist/components/charts/__screenshots__/DefaultTooltip.svelte.test.ts/DefaultTooltip-ScatterChart--single-point--quadtree-mode--should-show-series-header-for-multi-series-2.png +0 -0
  17. package/dist/components/index.d.ts +0 -2
  18. package/dist/components/index.js +0 -2
  19. package/dist/components/tooltip/TooltipContext.svelte +39 -10
  20. package/dist/components/tooltip/TooltipContext.svelte.d.ts +14 -0
  21. package/dist/states/brush.svelte.d.ts +1 -1
  22. package/dist/states/chart.svelte.js +38 -12
  23. package/dist/states/chart.svelte.test.js +227 -0
  24. package/dist/utils/linkUtils.d.ts +42 -0
  25. package/dist/utils/{connectorUtils.js → linkUtils.js} +56 -6
  26. package/package.json +1 -1
  27. package/dist/components/Connector.svelte +0 -167
  28. package/dist/components/Connector.svelte.d.ts +0 -56
  29. package/dist/utils/connectorUtils.d.ts +0 -34
@@ -58,27 +58,63 @@ function createRoundedPath(opts) {
58
58
  return `M ${source.x} ${source.y} L ${pBeforeCorner.x} ${pBeforeCorner.y} A ${effectiveRadius} ${effectiveRadius} 0 0 ${sweepFlag} ${pAfterCorner.x} ${pAfterCorner.y} L ${target.x} ${target.y}`;
59
59
  }
60
60
  }
61
+ /**
62
+ * Swoop: circular arc between source and target. Equivalent to ObservablePlot's
63
+ * Arrow `bend` option — positive angle bends right (clockwise from source to
64
+ * target), negative bends left, 0 is a straight line.
65
+ */
66
+ function createSwoopPath({ source, target, dx, dy, bend = 22.5 }) {
67
+ const chordLen = Math.hypot(dx, dy);
68
+ const bendRad = (bend * Math.PI) / 180;
69
+ if (Math.abs(bendRad) < 1e-6 || chordLen < 1e-6) {
70
+ return createDirectPath(source, target);
71
+ }
72
+ // Half-chord subtends `bend` at the arc center, so radius = chord / (2 * sin(bend))
73
+ const arcRadius = chordLen / (2 * Math.sin(Math.abs(bendRad)));
74
+ const largeArc = Math.abs(bend) > 90 ? 1 : 0;
75
+ const sweepFlag = bend > 0 ? 1 : 0;
76
+ return `M${source.x},${source.y}A${arcRadius},${arcRadius} 0 ${largeArc} ${sweepFlag} ${target.x},${target.y}`;
77
+ }
61
78
  const pathStrategies = {
62
79
  square: createSquarePath,
63
80
  beveled: createBeveledPath,
64
81
  rounded: createRoundedPath,
82
+ swoop: createSwoopPath,
65
83
  };
66
- export function getConnectorPresetPath(opts) {
84
+ export function getLinkPresetPath(opts) {
67
85
  const { source, target, type } = opts;
68
86
  if (isSamePoint(source, target))
69
87
  return '';
70
88
  const dx = target.x - source.x;
71
89
  const dy = target.y - source.y;
72
- // straight line cases
73
- if (type === 'straight' || isNearZero(dx) || isNearZero(dy)) {
90
+ // straight line cases (swoop still bends even when axis-aligned)
91
+ if (type === 'straight' || (type !== 'swoop' && (isNearZero(dx) || isNearZero(dy)))) {
74
92
  return createDirectPath(source, target);
75
93
  }
76
94
  return (pathStrategies[type] || pathStrategies.square)({ ...opts, dx, dy });
77
95
  }
78
96
  const FALLBACK_PATH = 'M0,0L0,0';
79
- export function getConnectorD3Path({ source, target, sweep, curve }) {
97
+ export function getLinkD3Path({ source, target, sweep, curve, orientation = 'horizontal', }) {
80
98
  const dx = target.x - source.x;
81
99
  const dy = target.y - source.y;
100
+ // d3 step curves always step along x. For vertical orientation, emit a
101
+ // y-axis step manually so the step sits between parent/child along depth.
102
+ if (orientation === 'vertical' && sweep === 'none') {
103
+ const { x: sx, y: sy } = source;
104
+ const { x: tx, y: ty } = target;
105
+ if (curve === curveStep) {
106
+ const my = (sy + ty) / 2;
107
+ return `M${sx},${sy}L${sx},${my}L${tx},${my}L${tx},${ty}`;
108
+ }
109
+ if (curve === curveStepBefore) {
110
+ // Bump near source: sibling (x) changes first, then depth (y)
111
+ return `M${sx},${sy}L${tx},${sy}L${tx},${ty}`;
112
+ }
113
+ if (curve === curveStepAfter) {
114
+ // Bump near target: depth (y) changes first, then sibling (x)
115
+ return `M${sx},${sy}L${sx},${ty}L${tx},${ty}`;
116
+ }
117
+ }
82
118
  const line = d3Line().curve(curve);
83
119
  let points = [];
84
120
  const isAligned = isNearZero(dx) || isNearZero(dy);
@@ -135,12 +171,26 @@ function radialGeometry(source, target) {
135
171
  sweepFlag,
136
172
  };
137
173
  }
138
- export function getConnectorRadialPresetPath({ source, target, type, radius, }) {
174
+ export function getLinkRadialPresetPath({ source, target, type, radius, bend = 22.5, }) {
139
175
  const g = radialGeometry(source, target);
140
176
  const { sr, ta, tr, sc, ss, tc, ts, sx, sy, tx, ty, sweepFlag } = g;
141
177
  if (type === 'straight') {
142
178
  return `M${sx},${sy}L${tx},${ty}`;
143
179
  }
180
+ if (type === 'swoop') {
181
+ // Circular arc in cartesian space between the polar-converted endpoints.
182
+ const dx = tx - sx;
183
+ const dy = ty - sy;
184
+ const chordLen = Math.hypot(dx, dy);
185
+ const bendRad = (bend * Math.PI) / 180;
186
+ if (Math.abs(bendRad) < 1e-6 || chordLen < 1e-6) {
187
+ return `M${sx},${sy}L${tx},${ty}`;
188
+ }
189
+ const arcRadius = chordLen / (2 * Math.sin(Math.abs(bendRad)));
190
+ const largeArc = Math.abs(bend) > 90 ? 1 : 0;
191
+ const arcSweep = bend > 0 ? 1 : 0;
192
+ return `M${sx},${sy}A${arcRadius},${arcRadius} 0 ${largeArc} ${arcSweep} ${tx},${ty}`;
193
+ }
144
194
  if (type === 'rounded') {
145
195
  // visx LinkRadialCurve: cubic Bezier with rotated offset (percent controls tension)
146
196
  const percent = 0.2;
@@ -183,7 +233,7 @@ export function getConnectorRadialPresetPath({ source, target, type, radius, })
183
233
  const p2y = cornerY + radialDir * r * ts;
184
234
  return `M${sx},${sy}L${p1x},${p1y}L${p2x},${p2y}L${tx},${ty}`;
185
235
  }
186
- export function getConnectorRadialD3Path({ source, target, curve, }) {
236
+ export function getLinkRadialD3Path({ source, target, curve, }) {
187
237
  const g = radialGeometry(source, target);
188
238
  const { sr, tr, sc, ss, tc, ts, sx, sy, tx, ty, sweepFlag } = g;
189
239
  // Step curves render as polar arcs/radials rather than cartesian stairs.
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "license": "MIT",
6
6
  "repository": "techniq/layerchart",
7
7
  "homepage": "https://layerchart.com",
8
- "version": "2.0.0-next.56",
8
+ "version": "2.0.0-next.58",
9
9
  "devDependencies": {
10
10
  "@changesets/cli": "^2.30.0",
11
11
  "@napi-rs/canvas": "^0.1.97",
@@ -1,167 +0,0 @@
1
- <script lang="ts" module>
2
- export type ConnectorPropsWithoutHTML = {
3
- /**
4
- * The coordinates of the start point of the connector.
5
- * @default { x: 0, y: 0 }
6
- */
7
- source: ConnectorCoords;
8
-
9
- /**
10
- * The coordinates of the end point of the connector.
11
- *
12
- * @default { x: 100, y: 100 }
13
- */
14
- target: ConnectorCoords;
15
-
16
- /**
17
- * The sweep direction of the connector.
18
- *
19
- * @default 'horizontal-vertical'
20
- */
21
- sweep?: ConnectorSweep;
22
-
23
- /**
24
- * The type of the connector.
25
- *
26
- * Set to `'d3'` to use a D3 curve function via the `curve` prop.
27
- *
28
- * @default 'rounded'
29
- */
30
- type?: ConnectorType;
31
-
32
- /**
33
- * The radius of the connector.
34
- *
35
- * Only used when type is `'beveled'` or `'rounded'`
36
- *
37
- * @default 20
38
- */
39
- radius?: number;
40
-
41
- /**
42
- * The D3 curve function to use for the connector.
43
- *
44
- * Only used when type is `'d3'`
45
- *
46
- * @default `d3.curveLinear`
47
- */
48
- curve?: CurveFactory;
49
-
50
- /**
51
- * Interpret `source`/`target` as polar coordinates (`x` = angle, `y` = radius)
52
- * and render the path in radial space. Defaults to `ctx.radial` when unset.
53
- */
54
- radial?: boolean;
55
- } & PathPropsWithoutHTML;
56
-
57
- export type ConnectorProps = ConnectorPropsWithoutHTML &
58
- Without<PathProps, ConnectorPropsWithoutHTML>;
59
- </script>
60
-
61
- <script lang="ts">
62
- import { type CurveFactory, curveLinear } from 'd3-shape';
63
- import {
64
- getConnectorD3Path,
65
- getConnectorPresetPath,
66
- getConnectorRadialD3Path,
67
- getConnectorRadialPresetPath,
68
- type ConnectorCoords,
69
- type ConnectorSweep,
70
- type ConnectorType,
71
- } from '../utils/connectorUtils.js';
72
- import { getChartContext } from '../contexts/chart.js';
73
- import Path, { type PathProps, type PathPropsWithoutHTML } from './Path.svelte';
74
- import type { Without } from '../utils/types.js';
75
- import { createId } from '../utils/createId.js';
76
- import { extractLayerProps } from '../utils/attributes.js';
77
- import MarkerWrapper from './MarkerWrapper.svelte';
78
- import {
79
- createMotion,
80
- extractTweenConfig,
81
- type ResolvedMotion,
82
- } from '../utils/motion.svelte.js';
83
- import { interpolatePath } from 'd3-interpolate-path';
84
-
85
- const uid = $props.id();
86
-
87
- let {
88
- source = { x: 0, y: 0 },
89
- target = { x: 100, y: 100 },
90
- sweep: sweepProp,
91
- type = 'rounded',
92
- radius = 20,
93
- curve = curveLinear,
94
- radial: radialProp,
95
- pathRef = $bindable(),
96
- pathData: pathDataProp,
97
- marker,
98
- markerStart,
99
- markerMid,
100
- markerEnd,
101
- motion,
102
- ...restProps
103
- }: ConnectorProps = $props();
104
-
105
- const ctx = getChartContext();
106
- const radial = $derived(radialProp ?? ctx.radial ?? false);
107
-
108
- const sweep = $derived.by(() => {
109
- if (sweepProp) return sweepProp;
110
- if (type === 'd3') return 'none';
111
- return 'horizontal-vertical';
112
- });
113
-
114
- const markerStartId = $derived(markerStart || marker ? createId('marker-start', uid) : '');
115
- const markerMidId = $derived(markerMid || marker ? createId('marker-mid', uid) : '');
116
- const markerEndId = $derived(markerEnd || marker ? createId('marker-end', uid) : '');
117
-
118
- const extractedTween = extractTweenConfig(motion);
119
-
120
- const tweenOptions: ResolvedMotion | undefined = extractedTween
121
- ? {
122
- type: extractedTween.type,
123
- options: {
124
- interpolate: interpolatePath,
125
- ...extractedTween.options,
126
- },
127
- }
128
- : undefined;
129
-
130
- const pathData = $derived.by(() => {
131
- if (pathDataProp) return pathDataProp;
132
- if (radial) {
133
- return type === 'd3'
134
- ? getConnectorRadialD3Path({ source, target, curve })
135
- : getConnectorRadialPresetPath({ source, target, type, radius });
136
- }
137
- if (type === 'd3') {
138
- return getConnectorD3Path({
139
- source,
140
- target,
141
- sweep,
142
- curve,
143
- });
144
- } else {
145
- return getConnectorPresetPath({ source, target, sweep, type, radius });
146
- }
147
- });
148
-
149
- const motionPath = createMotion(
150
- '',
151
- () => pathData,
152
- tweenOptions ? tweenOptions : { type: 'none' }
153
- );
154
- </script>
155
-
156
- <Path
157
- pathData={motionPath.current}
158
- bind:pathRef
159
- marker-start={markerStartId ? `url(#${markerStartId})` : undefined}
160
- marker-mid={markerMidId ? `url(#${markerMidId})` : undefined}
161
- marker-end={markerEndId ? `url(#${markerEndId})` : undefined}
162
- {...extractLayerProps(restProps, 'lc-connector')}
163
- {...restProps}
164
- />
165
- <MarkerWrapper id={markerStartId} marker={markerStart} />
166
- <MarkerWrapper id={markerMidId} marker={markerMid} />
167
- <MarkerWrapper id={markerEndId} marker={markerEnd} />
@@ -1,56 +0,0 @@
1
- export type ConnectorPropsWithoutHTML = {
2
- /**
3
- * The coordinates of the start point of the connector.
4
- * @default { x: 0, y: 0 }
5
- */
6
- source: ConnectorCoords;
7
- /**
8
- * The coordinates of the end point of the connector.
9
- *
10
- * @default { x: 100, y: 100 }
11
- */
12
- target: ConnectorCoords;
13
- /**
14
- * The sweep direction of the connector.
15
- *
16
- * @default 'horizontal-vertical'
17
- */
18
- sweep?: ConnectorSweep;
19
- /**
20
- * The type of the connector.
21
- *
22
- * Set to `'d3'` to use a D3 curve function via the `curve` prop.
23
- *
24
- * @default 'rounded'
25
- */
26
- type?: ConnectorType;
27
- /**
28
- * The radius of the connector.
29
- *
30
- * Only used when type is `'beveled'` or `'rounded'`
31
- *
32
- * @default 20
33
- */
34
- radius?: number;
35
- /**
36
- * The D3 curve function to use for the connector.
37
- *
38
- * Only used when type is `'d3'`
39
- *
40
- * @default `d3.curveLinear`
41
- */
42
- curve?: CurveFactory;
43
- /**
44
- * Interpret `source`/`target` as polar coordinates (`x` = angle, `y` = radius)
45
- * and render the path in radial space. Defaults to `ctx.radial` when unset.
46
- */
47
- radial?: boolean;
48
- } & PathPropsWithoutHTML;
49
- export type ConnectorProps = ConnectorPropsWithoutHTML & Without<PathProps, ConnectorPropsWithoutHTML>;
50
- import { type CurveFactory } from 'd3-shape';
51
- import { type ConnectorCoords, type ConnectorSweep, type ConnectorType } from '../utils/connectorUtils.js';
52
- import { type PathProps, type PathPropsWithoutHTML } from './Path.svelte';
53
- import type { Without } from '../utils/types.js';
54
- declare const Connector: import("svelte").Component<ConnectorProps, {}, "pathRef">;
55
- type Connector = ReturnType<typeof Connector>;
56
- export default Connector;
@@ -1,34 +0,0 @@
1
- import { type CurveFactory } from 'd3-shape';
2
- export type ConnectorCoords = {
3
- x: number;
4
- y: number;
5
- };
6
- export type PresetConnectorType = 'straight' | 'square' | 'beveled' | 'rounded';
7
- export type ConnectorType = PresetConnectorType | 'd3';
8
- export type ConnectorSweep = 'horizontal-vertical' | 'vertical-horizontal' | 'none';
9
- type GetConnectorPresetPathProps = {
10
- source: ConnectorCoords;
11
- target: ConnectorCoords;
12
- radius: number;
13
- type: PresetConnectorType;
14
- sweep: ConnectorSweep;
15
- };
16
- export declare function getConnectorPresetPath(opts: GetConnectorPresetPathProps): string;
17
- type GetConnectorD3PathProps = Omit<GetConnectorPresetPathProps, 'radius' | 'type'> & {
18
- curve: CurveFactory;
19
- };
20
- export declare function getConnectorD3Path({ source, target, sweep, curve }: GetConnectorD3PathProps): string;
21
- type GetConnectorRadialPresetPathProps = {
22
- source: ConnectorCoords;
23
- target: ConnectorCoords;
24
- type: PresetConnectorType;
25
- radius: number;
26
- };
27
- export declare function getConnectorRadialPresetPath({ source, target, type, radius, }: GetConnectorRadialPresetPathProps): string;
28
- type GetConnectorRadialD3PathProps = {
29
- source: ConnectorCoords;
30
- target: ConnectorCoords;
31
- curve?: CurveFactory;
32
- };
33
- export declare function getConnectorRadialD3Path({ source, target, curve, }: GetConnectorRadialD3PathProps): string;
34
- export {};