layerchart 2.0.0-next.2 → 2.0.0-next.21
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/components/AnnotationLine.svelte +17 -29
- package/dist/components/AnnotationLine.svelte.d.ts +4 -2
- package/dist/components/AnnotationPoint.svelte +11 -13
- package/dist/components/AnnotationPoint.svelte.d.ts +4 -2
- package/dist/components/AnnotationRange.svelte +3 -3
- package/dist/components/Arc.svelte +2 -2
- package/dist/components/Axis.svelte +52 -24
- package/dist/components/Axis.svelte.d.ts +14 -3
- package/dist/components/Bar.svelte +7 -4
- package/dist/components/Bar.svelte.d.ts +4 -1
- package/dist/components/Bars.svelte +9 -6
- package/dist/components/Bars.svelte.d.ts +3 -3
- package/dist/components/Blur.svelte +20 -12
- package/dist/components/Blur.svelte.d.ts +2 -5
- package/dist/components/Calendar.svelte +10 -6
- package/dist/components/Calendar.svelte.d.ts +2 -1
- package/dist/components/Chart.svelte +2 -2
- package/dist/components/ClipPath.svelte +14 -9
- package/dist/components/Connector.svelte +2 -2
- package/dist/components/Connector.svelte.d.ts +1 -1
- package/dist/components/Ellipse.svelte +187 -0
- package/dist/components/Ellipse.svelte.d.ts +64 -0
- package/dist/components/ForceSimulation.svelte +168 -50
- package/dist/components/ForceSimulation.svelte.d.ts +80 -21
- package/dist/components/GeoEdgeFade.svelte +4 -3
- package/dist/components/GeoEdgeFade.svelte.d.ts +2 -2
- package/dist/components/GeoPath.svelte +12 -5
- package/dist/components/GeoPoint.svelte +1 -2
- package/dist/components/GeoSpline.svelte +4 -4
- package/dist/components/GeoSpline.svelte.d.ts +1 -1
- package/dist/components/Graticule.svelte +3 -2
- package/dist/components/Grid.svelte +8 -7
- package/dist/components/Grid.svelte.d.ts +2 -1
- package/dist/components/Group.svelte +45 -5
- package/dist/components/Group.svelte.d.ts +32 -4
- package/dist/components/Highlight.svelte +1 -1
- package/dist/components/Hull.svelte +4 -4
- package/dist/components/Hull.svelte.d.ts +2 -2
- package/dist/components/Labels.svelte +6 -4
- package/dist/components/Labels.svelte.d.ts +2 -2
- package/dist/components/Legend.svelte +8 -5
- package/dist/components/Legend.svelte.d.ts +3 -3
- package/dist/components/MonthPath.svelte +14 -11
- package/dist/components/MonthPath.svelte.d.ts +4 -3
- package/dist/components/Polygon.svelte +285 -0
- package/dist/components/Polygon.svelte.d.ts +115 -0
- package/dist/components/RadialGradient.svelte +1 -3
- package/dist/components/Rule.svelte +3 -2
- package/dist/components/Spline.svelte +30 -18
- package/dist/components/Spline.svelte.d.ts +12 -4
- package/dist/components/Text.svelte +60 -48
- package/dist/components/Text.svelte.d.ts +6 -0
- package/dist/components/Treemap.svelte +63 -26
- package/dist/components/Treemap.svelte.d.ts +11 -11
- package/dist/components/Voronoi.svelte +55 -36
- package/dist/components/Voronoi.svelte.d.ts +5 -3
- package/dist/components/charts/ArcChart.svelte +2 -2
- package/dist/components/charts/AreaChart.svelte +9 -10
- package/dist/components/charts/BarChart.svelte +63 -53
- package/dist/components/charts/DefaultTooltip.svelte +1 -1
- package/dist/components/charts/LineChart.svelte +8 -5
- package/dist/components/charts/PieChart.svelte +2 -2
- package/dist/components/charts/ScatterChart.svelte +0 -2
- package/dist/components/charts/utils.svelte.d.ts +3 -3
- package/dist/components/charts/utils.svelte.js +7 -3
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +4 -0
- package/dist/components/layout/Canvas.svelte +67 -49
- package/dist/components/layout/Canvas.svelte.d.ts +6 -0
- package/dist/components/layout/Layer.svelte +6 -4
- package/dist/components/layout/Layer.svelte.d.ts +6 -4
- package/dist/components/tooltip/Tooltip.svelte +14 -7
- package/dist/components/tooltip/TooltipContext.svelte +25 -8
- package/dist/components/tooltip/TooltipContext.svelte.d.ts +1 -1
- package/dist/components/tooltip/TooltipHeader.svelte +5 -4
- package/dist/components/tooltip/TooltipHeader.svelte.d.ts +3 -3
- package/dist/components/tooltip/TooltipItem.svelte +5 -4
- package/dist/components/tooltip/TooltipItem.svelte.d.ts +3 -3
- package/dist/components/tooltip/TooltipList.svelte +1 -1
- package/dist/components/tooltip/tooltipMetaContext.d.ts +2 -2
- package/dist/docs/Blockquote.svelte +3 -1
- package/dist/docs/Blockquote.svelte.d.ts +4 -19
- package/dist/docs/Code.svelte +20 -12
- package/dist/docs/Code.svelte.d.ts +9 -23
- package/dist/docs/Header1.svelte +4 -2
- package/dist/docs/Header1.svelte.d.ts +4 -28
- package/dist/docs/Json.svelte +11 -3
- package/dist/docs/Json.svelte.d.ts +9 -21
- package/dist/docs/Layout.svelte +10 -7
- package/dist/docs/Layout.svelte.d.ts +4 -19
- package/dist/docs/Link.svelte +7 -3
- package/dist/docs/Link.svelte.d.ts +4 -38
- package/dist/docs/TilesetField.svelte +20 -19
- package/dist/docs/TilesetField.svelte.d.ts +5 -22
- package/dist/docs/ViewSourceButton.svelte +7 -4
- package/dist/docs/ViewSourceButton.svelte.d.ts +7 -21
- package/dist/utils/arcText.svelte.js +4 -4
- package/dist/utils/canvas.d.ts +77 -0
- package/dist/utils/canvas.js +105 -41
- package/dist/utils/genData.d.ts +14 -0
- package/dist/utils/genData.js +24 -6
- package/dist/utils/path.d.ts +10 -0
- package/dist/utils/path.js +30 -0
- package/dist/utils/scales.svelte.d.ts +3 -2
- package/dist/utils/scales.svelte.js +7 -3
- package/dist/utils/shape.d.ts +43 -0
- package/dist/utils/shape.js +59 -0
- package/dist/utils/string.d.ts +49 -0
- package/dist/utils/string.js +4 -2
- package/dist/utils/ticks.d.ts +4 -4
- package/dist/utils/ticks.js +106 -159
- package/dist/utils/ticks.test.js +6 -16
- package/dist/utils/treemap.d.ts +1 -1
- package/package.json +27 -24
- package/dist/utils/object.js +0 -2
package/dist/utils/canvas.js
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
|
+
import memoize from 'memoize';
|
|
1
2
|
import { cls } from '@layerstack/tailwind';
|
|
2
|
-
import { memoize } from 'lodash-es';
|
|
3
3
|
export const DEFAULT_FILL = 'rgb(0, 0, 0)';
|
|
4
4
|
const CANVAS_STYLES_ELEMENT_ID = '__layerchart_canvas_styles_id';
|
|
5
|
+
const supportedStyles = [
|
|
6
|
+
'fill',
|
|
7
|
+
'fillOpacity',
|
|
8
|
+
'stroke',
|
|
9
|
+
'strokeWidth',
|
|
10
|
+
'opacity',
|
|
11
|
+
'fontWeight',
|
|
12
|
+
'fontSize',
|
|
13
|
+
'fontFamily',
|
|
14
|
+
'textAnchor',
|
|
15
|
+
'textAlign',
|
|
16
|
+
'paintOrder',
|
|
17
|
+
];
|
|
5
18
|
/**
|
|
6
19
|
* Appends or reuses `<svg>` element below `<canvas>` to resolve CSS variables and classes (ex. `stroke: var(--color-primary)` => `stroke: rgb(...)` )
|
|
7
20
|
*/
|
|
8
|
-
export function
|
|
21
|
+
export function _getComputedStyles(canvas, { styles, classes } = {}) {
|
|
22
|
+
// console.count(`getComputedStyles: ${getComputedStylesKey(canvas, { styles, classes })}`);
|
|
9
23
|
try {
|
|
10
24
|
// Get or create `<svg>` below `<canvas>`
|
|
11
25
|
let svg = document.getElementById(CANVAS_STYLES_ELEMENT_ID);
|
|
@@ -24,13 +38,19 @@ export function getComputedStyles(canvas, { styles, classes } = {}) {
|
|
|
24
38
|
if (styles) {
|
|
25
39
|
Object.assign(svg.style, styles);
|
|
26
40
|
}
|
|
41
|
+
// Make sure `<svg>` is not visible
|
|
42
|
+
svg.style.display = 'none';
|
|
27
43
|
if (classes) {
|
|
28
44
|
svg.setAttribute('class', cls(classes)
|
|
29
45
|
.split(' ')
|
|
30
46
|
.filter((s) => !s.startsWith('transition-'))
|
|
31
47
|
.join(' '));
|
|
32
48
|
}
|
|
33
|
-
|
|
49
|
+
// Capture copy to enable memoization and avoid capturing all styles (which is very slow)
|
|
50
|
+
const computedStyles = supportedStyles.reduce((acc, style) => {
|
|
51
|
+
acc[style] = window.getComputedStyle(svg)[style];
|
|
52
|
+
return acc;
|
|
53
|
+
}, {});
|
|
34
54
|
return computedStyles;
|
|
35
55
|
}
|
|
36
56
|
catch (e) {
|
|
@@ -38,38 +58,71 @@ export function getComputedStyles(canvas, { styles, classes } = {}) {
|
|
|
38
58
|
return {};
|
|
39
59
|
}
|
|
40
60
|
}
|
|
61
|
+
function getComputedStylesKey(canvas, { styles, classes } = {}) {
|
|
62
|
+
return JSON.stringify({ canvasId: canvas.id, styles, classes });
|
|
63
|
+
}
|
|
64
|
+
export const getComputedStyles = memoize(_getComputedStyles, {
|
|
65
|
+
cacheKey: ([canvas, styleOptions]) => {
|
|
66
|
+
return getComputedStylesKey(canvas, styleOptions);
|
|
67
|
+
},
|
|
68
|
+
});
|
|
41
69
|
/** Render onto canvas context. Supports CSS variables and classes by tranferring to hidden `<svg>` element before retrieval) */
|
|
42
|
-
function render(ctx, render, styleOptions = {}) {
|
|
70
|
+
function render(ctx, render, styleOptions = {}, { applyText, } = {}) {
|
|
43
71
|
// console.count('render');
|
|
44
72
|
// TODO: Consider memoizing? How about reactiving to CSS variable changes (light/dark mode toggle)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
73
|
+
let resolvedStyles;
|
|
74
|
+
if (styleOptions.classes == null &&
|
|
75
|
+
!Object.values(styleOptions.styles ?? {}).some((v) => typeof v === 'string' && v.includes('var('))) {
|
|
76
|
+
// Skip resolving styles if no classes are provided and no styles are using CSS variables
|
|
77
|
+
resolvedStyles = styleOptions.styles ?? {};
|
|
50
78
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
79
|
+
else {
|
|
80
|
+
// Remove constant non-css variable properties (ex. `strokeWidth: 0.5`, `fill: #123456`) as not needed and improves memoization cache hit
|
|
81
|
+
const { constantStyles, variableStyles } = Object.entries(styleOptions.styles ?? {}).reduce((acc, [key, value]) => {
|
|
82
|
+
if (typeof value === 'number' || (typeof value === 'string' && !value.includes('var('))) {
|
|
83
|
+
acc.constantStyles[key] = value;
|
|
84
|
+
}
|
|
85
|
+
else if (typeof value === 'string' && value.includes('var(')) {
|
|
86
|
+
acc.variableStyles[key] = value;
|
|
87
|
+
}
|
|
88
|
+
return acc;
|
|
89
|
+
}, { constantStyles: {}, variableStyles: {} });
|
|
90
|
+
const computedStyles = getComputedStyles(ctx.canvas, {
|
|
91
|
+
styles: variableStyles,
|
|
92
|
+
classes: styleOptions.classes,
|
|
93
|
+
});
|
|
94
|
+
resolvedStyles = { ...computedStyles, ...constantStyles };
|
|
56
95
|
}
|
|
57
|
-
|
|
58
|
-
|
|
96
|
+
// Adhere to CSS paint order: https://developer.mozilla.org/en-US/docs/Web/CSS/paint-order
|
|
97
|
+
const paintOrder = resolvedStyles?.paintOrder === 'stroke' ? ['stroke', 'fill'] : ['fill', 'stroke'];
|
|
98
|
+
if (resolvedStyles?.opacity) {
|
|
99
|
+
ctx.globalAlpha = Number(resolvedStyles?.opacity);
|
|
59
100
|
}
|
|
60
|
-
|
|
61
|
-
|
|
101
|
+
// font/text properties can be expensive to set (not sure why), so only apply if needed (renderText())
|
|
102
|
+
if (applyText) {
|
|
103
|
+
// Text properties
|
|
104
|
+
ctx.font = `${resolvedStyles.fontWeight} ${resolvedStyles.fontSize} ${resolvedStyles.fontFamily}`; // build string instead of using `computedStyles.font` to fix/workaround `tabular-nums` returning `null`
|
|
105
|
+
// TODO: Hack to handle `textAnchor` with canvas. Try to find a better approach
|
|
106
|
+
if (resolvedStyles.textAnchor === 'middle') {
|
|
107
|
+
ctx.textAlign = 'center';
|
|
108
|
+
}
|
|
109
|
+
else if (resolvedStyles.textAnchor === 'end') {
|
|
110
|
+
ctx.textAlign = 'right';
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
ctx.textAlign = resolvedStyles.textAlign; // TODO: Handle/map `justify` and `match-parent`?
|
|
114
|
+
}
|
|
115
|
+
// TODO: Handle `textBaseline` / `verticalAnchor` (Text)
|
|
116
|
+
// ctx.textBaseline = 'top';
|
|
117
|
+
// ctx.textBaseline = 'middle';
|
|
118
|
+
// ctx.textBaseline = 'bottom';
|
|
119
|
+
// ctx.textBaseline = 'alphabetic';
|
|
120
|
+
// ctx.textBaseline = 'hanging';
|
|
121
|
+
// ctx.textBaseline = 'ideographic';
|
|
62
122
|
}
|
|
63
|
-
// TODO: Handle `textBaseline` / `verticalAnchor` (Text)
|
|
64
|
-
// ctx.textBaseline = 'top';
|
|
65
|
-
// ctx.textBaseline = 'middle';
|
|
66
|
-
// ctx.textBaseline = 'bottom';
|
|
67
|
-
// ctx.textBaseline = 'alphabetic';
|
|
68
|
-
// ctx.textBaseline = 'hanging';
|
|
69
|
-
// ctx.textBaseline = 'ideographic';
|
|
70
123
|
// Dashed lines
|
|
71
|
-
if (
|
|
72
|
-
const dashArray =
|
|
124
|
+
if (resolvedStyles.strokeDasharray?.includes(',')) {
|
|
125
|
+
const dashArray = resolvedStyles.strokeDasharray
|
|
73
126
|
.split(',')
|
|
74
127
|
.map((s) => Number(s.replace('px', '')));
|
|
75
128
|
ctx.setLineDash(dashArray);
|
|
@@ -81,11 +134,11 @@ function render(ctx, render, styleOptions = {}) {
|
|
|
81
134
|
styleOptions.styles?.fill instanceof CanvasPattern ||
|
|
82
135
|
!styleOptions.styles?.fill?.includes('var'))
|
|
83
136
|
? styleOptions.styles.fill
|
|
84
|
-
:
|
|
137
|
+
: resolvedStyles?.fill;
|
|
85
138
|
if (fill && !['none', DEFAULT_FILL].includes(fill)) {
|
|
86
139
|
const currentGlobalAlpha = ctx.globalAlpha;
|
|
87
|
-
const fillOpacity = Number(
|
|
88
|
-
const opacity = Number(
|
|
140
|
+
const fillOpacity = Number(resolvedStyles?.fillOpacity);
|
|
141
|
+
const opacity = Number(resolvedStyles?.opacity);
|
|
89
142
|
ctx.globalAlpha = fillOpacity * opacity;
|
|
90
143
|
ctx.fillStyle = fill;
|
|
91
144
|
render.fill(ctx);
|
|
@@ -98,12 +151,12 @@ function render(ctx, render, styleOptions = {}) {
|
|
|
98
151
|
(styleOptions.styles?.stroke instanceof CanvasGradient ||
|
|
99
152
|
!styleOptions.styles?.stroke?.includes('var'))
|
|
100
153
|
? styleOptions.styles?.stroke
|
|
101
|
-
:
|
|
154
|
+
: resolvedStyles?.stroke;
|
|
102
155
|
if (stroke && !['none'].includes(stroke)) {
|
|
103
156
|
ctx.lineWidth =
|
|
104
|
-
typeof
|
|
105
|
-
? Number(
|
|
106
|
-
: (
|
|
157
|
+
typeof resolvedStyles?.strokeWidth === 'string'
|
|
158
|
+
? Number(resolvedStyles?.strokeWidth?.replace('px', ''))
|
|
159
|
+
: (resolvedStyles?.strokeWidth ?? 1);
|
|
107
160
|
ctx.strokeStyle = stroke;
|
|
108
161
|
render.stroke(ctx);
|
|
109
162
|
}
|
|
@@ -123,7 +176,7 @@ export function renderText(ctx, text, coords, styleOptions = {}) {
|
|
|
123
176
|
render(ctx, {
|
|
124
177
|
fill: (ctx) => ctx.fillText(text.toString(), coords.x, coords.y),
|
|
125
178
|
stroke: (ctx) => ctx.strokeText(text.toString(), coords.x, coords.y),
|
|
126
|
-
}, styleOptions);
|
|
179
|
+
}, styleOptions, { applyText: true });
|
|
127
180
|
}
|
|
128
181
|
}
|
|
129
182
|
export function renderRect(ctx, coords, styleOptions = {}) {
|
|
@@ -145,6 +198,19 @@ export function renderCircle(ctx, coords, styleOptions = {}) {
|
|
|
145
198
|
}, styleOptions);
|
|
146
199
|
ctx.closePath();
|
|
147
200
|
}
|
|
201
|
+
export function renderEllipse(ctx, coords, styleOptions = {}) {
|
|
202
|
+
ctx.beginPath();
|
|
203
|
+
ctx.ellipse(coords.cx, coords.cy, coords.rx, coords.ry, 0, 0, 2 * Math.PI);
|
|
204
|
+
render(ctx, {
|
|
205
|
+
fill: (ctx) => {
|
|
206
|
+
ctx.fill();
|
|
207
|
+
},
|
|
208
|
+
stroke: (ctx) => {
|
|
209
|
+
ctx.stroke();
|
|
210
|
+
},
|
|
211
|
+
}, styleOptions);
|
|
212
|
+
ctx.closePath();
|
|
213
|
+
}
|
|
148
214
|
/** Clear canvas accounting for Canvas `context.translate(...)` */
|
|
149
215
|
export function clearCanvasContext(ctx, options) {
|
|
150
216
|
// Clear with negative offset due to Canvas `context.translate(...)`
|
|
@@ -179,9 +245,8 @@ export function _createLinearGradient(ctx, x0, y0, x1, y1, stops) {
|
|
|
179
245
|
return gradient;
|
|
180
246
|
}
|
|
181
247
|
/** Create linear gradient and memoize result to fix reactivity */
|
|
182
|
-
export const createLinearGradient = memoize(_createLinearGradient,
|
|
183
|
-
|
|
184
|
-
return key;
|
|
248
|
+
export const createLinearGradient = memoize(_createLinearGradient, {
|
|
249
|
+
cacheKey: (args) => JSON.stringify(args.slice(1)), // Ignore `ctx` argument
|
|
185
250
|
});
|
|
186
251
|
export function _createPattern(ctx, width, height, shapes, background) {
|
|
187
252
|
const patternCanvas = document.createElement('canvas');
|
|
@@ -214,7 +279,6 @@ export function _createPattern(ctx, width, height, shapes, background) {
|
|
|
214
279
|
return pattern;
|
|
215
280
|
}
|
|
216
281
|
/** Create pattern and memoize result to fix reactivity */
|
|
217
|
-
export const createPattern = memoize(_createPattern,
|
|
218
|
-
|
|
219
|
-
return key;
|
|
282
|
+
export const createPattern = memoize(_createPattern, {
|
|
283
|
+
cacheKey: (args) => JSON.stringify(args.slice(1)), // Ignore `ctx` argument
|
|
220
284
|
});
|
package/dist/utils/genData.d.ts
CHANGED
|
@@ -75,3 +75,17 @@ export declare function getSpiral({ angle, radius, count, width, height, }: {
|
|
|
75
75
|
x: number;
|
|
76
76
|
y: number;
|
|
77
77
|
}[];
|
|
78
|
+
interface SineWaveOptions {
|
|
79
|
+
numPoints: number;
|
|
80
|
+
frequency?: number;
|
|
81
|
+
amplitude?: number;
|
|
82
|
+
noiseLevel?: number;
|
|
83
|
+
phase?: number;
|
|
84
|
+
xMin?: number;
|
|
85
|
+
xMax?: number;
|
|
86
|
+
}
|
|
87
|
+
export declare function generateSineWave(options: SineWaveOptions): {
|
|
88
|
+
x: number;
|
|
89
|
+
y: number;
|
|
90
|
+
}[];
|
|
91
|
+
export {};
|
package/dist/utils/genData.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { timeMinute, timeDay } from 'd3-time';
|
|
2
2
|
import { cumsum } from 'd3-array';
|
|
3
3
|
import { randomNormal } from 'd3-random';
|
|
4
4
|
import { degreesToRadians, radiansToDegrees } from './math.js';
|
|
@@ -44,14 +44,14 @@ export function createSeries(options) {
|
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
export function createDateSeries(options) {
|
|
47
|
-
const now =
|
|
47
|
+
const now = timeDay.floor(new Date());
|
|
48
48
|
const count = options.count ?? 10;
|
|
49
49
|
const min = options.min;
|
|
50
50
|
const max = options.max;
|
|
51
51
|
const keys = options.keys ?? ['value'];
|
|
52
52
|
return Array.from({ length: count }).map((_, i) => {
|
|
53
53
|
return {
|
|
54
|
-
date:
|
|
54
|
+
date: timeDay.offset(now, -count + i),
|
|
55
55
|
...Object.fromEntries(keys.map((key) => {
|
|
56
56
|
return [
|
|
57
57
|
key,
|
|
@@ -66,10 +66,10 @@ export function createTimeSeries(options) {
|
|
|
66
66
|
const min = options.min;
|
|
67
67
|
const max = options.max;
|
|
68
68
|
const keys = options.keys ?? ['value'];
|
|
69
|
-
let lastStartDate =
|
|
69
|
+
let lastStartDate = timeDay.floor(new Date());
|
|
70
70
|
const timeSeries = Array.from({ length: count }).map((_, i) => {
|
|
71
|
-
const startDate =
|
|
72
|
-
const endDate =
|
|
71
|
+
const startDate = timeMinute.offset(lastStartDate, getRandomInteger(0, 60));
|
|
72
|
+
const endDate = timeMinute.offset(startDate, getRandomInteger(5, 60));
|
|
73
73
|
lastStartDate = startDate;
|
|
74
74
|
return {
|
|
75
75
|
name: `item ${i + 1}`,
|
|
@@ -124,3 +124,21 @@ export function getSpiral({ angle, radius, count, width, height, }) {
|
|
|
124
124
|
};
|
|
125
125
|
});
|
|
126
126
|
}
|
|
127
|
+
export function generateSineWave(options) {
|
|
128
|
+
const { numPoints, frequency = 1, amplitude = 1, noiseLevel = 0, phase = 0, xMin = 0, xMax = 2 * Math.PI, } = options;
|
|
129
|
+
if (numPoints <= 0) {
|
|
130
|
+
throw new Error('Number of points must be greater than 0');
|
|
131
|
+
}
|
|
132
|
+
const points = [];
|
|
133
|
+
const xStep = (xMax - xMin) / (numPoints - 1);
|
|
134
|
+
for (let i = 0; i < numPoints; i++) {
|
|
135
|
+
const x = xMin + i * xStep;
|
|
136
|
+
// Generate base sine wave
|
|
137
|
+
const sineValue = amplitude * Math.sin(frequency * x + phase);
|
|
138
|
+
// Add random noise if specified
|
|
139
|
+
const noise = noiseLevel > 0 ? (Math.random() - 0.5) * 2 * noiseLevel : 0;
|
|
140
|
+
const y = sineValue + noise;
|
|
141
|
+
points.push({ x, y });
|
|
142
|
+
}
|
|
143
|
+
return points;
|
|
144
|
+
}
|
package/dist/utils/path.d.ts
CHANGED
|
@@ -17,5 +17,15 @@ export declare function spikePath({ x, y, width, height, }: {
|
|
|
17
17
|
width: number;
|
|
18
18
|
height: number;
|
|
19
19
|
}): string;
|
|
20
|
+
/** Create rounded polygon path
|
|
21
|
+
*
|
|
22
|
+
* @param coords - Array of points (x, y)
|
|
23
|
+
* @param radius - Radius of the curve
|
|
24
|
+
* @returns String of path data
|
|
25
|
+
*/
|
|
26
|
+
export declare function roundedPolygonPath(coords: {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
}[], radius: number): string;
|
|
20
30
|
/** Flatten all `y` coordinates to `0` */
|
|
21
31
|
export declare function flattenPathData(pathData: string, yOverride?: number): string;
|
package/dist/utils/path.js
CHANGED
|
@@ -36,6 +36,36 @@ export function spikePath({ x, y, width, height, }) {
|
|
|
36
36
|
`;
|
|
37
37
|
return pathData;
|
|
38
38
|
}
|
|
39
|
+
/** Create rounded polygon path
|
|
40
|
+
*
|
|
41
|
+
* @param coords - Array of points (x, y)
|
|
42
|
+
* @param radius - Radius of the curve
|
|
43
|
+
* @returns String of path data
|
|
44
|
+
*/
|
|
45
|
+
export function roundedPolygonPath(coords, radius) {
|
|
46
|
+
if (radius === 0) {
|
|
47
|
+
// Simple polygon with straight lines
|
|
48
|
+
return `M${coords[0].x},${coords[0].y}${coords
|
|
49
|
+
.slice(1)
|
|
50
|
+
.map((p) => `L${p.x},${p.y}`)
|
|
51
|
+
.join('')}Z`;
|
|
52
|
+
}
|
|
53
|
+
let path = '';
|
|
54
|
+
const length = coords.length + 1;
|
|
55
|
+
for (let i = 0; i < length; i++) {
|
|
56
|
+
const a = coords[i % coords.length];
|
|
57
|
+
const b = coords[(i + 1) % coords.length];
|
|
58
|
+
const t = Math.min(radius / Math.hypot(b.x - a.x, b.y - a.y), 0.5);
|
|
59
|
+
if (i == 0)
|
|
60
|
+
path += `M${a.x * (1 - t) + b.x * t},${a.y * (1 - t) + b.y * t}`;
|
|
61
|
+
if (i > 0)
|
|
62
|
+
path += `Q${a.x},${a.y} ${a.x * (1 - t) + b.x * t},${a.y * (1 - t) + b.y * t}`;
|
|
63
|
+
if (i < length - 1)
|
|
64
|
+
path += `L${a.x * t + b.x * (1 - t)},${a.y * t + b.y * (1 - t)}`;
|
|
65
|
+
}
|
|
66
|
+
path += 'Z';
|
|
67
|
+
return path;
|
|
68
|
+
}
|
|
39
69
|
/** Flatten all `y` coordinates to `0` */
|
|
40
70
|
export function flattenPathData(pathData, yOverride = 0) {
|
|
41
71
|
let result = pathData;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ScaleBand } from 'd3-scale';
|
|
1
|
+
import { type ScaleBand, type ScaleTime } from 'd3-scale';
|
|
2
2
|
import { type MotionProp, type MotionOptions, type SpringOptions, type TweenOptions } from './motion.svelte.js';
|
|
3
3
|
import type { Accessor } from './common.js';
|
|
4
4
|
import type { OnlyObjects } from './types.js';
|
|
@@ -23,6 +23,8 @@ export type AnyScale<TInput extends SingleDomainType = any, TOutput extends Sing
|
|
|
23
23
|
thresholds?: () => TInput[];
|
|
24
24
|
quantiles?: () => TInput[];
|
|
25
25
|
};
|
|
26
|
+
export declare function isScaleBand(scale: AnyScale<any, any>): scale is ScaleBand<any>;
|
|
27
|
+
export declare function isScaleTime(scale: AnyScale<any, any>): scale is ScaleTime<any, any>;
|
|
26
28
|
export declare function getRange(scale: any): any[];
|
|
27
29
|
export type SingleDomainType = number | string | Date | null | undefined;
|
|
28
30
|
export type DomainType = (number | string | Date | null | undefined)[] | null | undefined;
|
|
@@ -45,7 +47,6 @@ export declare function createMotionScale<Domain, Range>(scale: AnyScale, motion
|
|
|
45
47
|
* https://gist.github.com/LuisSevillano/d53a1dc529eef518780c6df99613e2fd
|
|
46
48
|
*/
|
|
47
49
|
export declare function scaleBandInvert(scale: ScaleBand<any>): (value: number) => any;
|
|
48
|
-
export declare function isScaleBand(scale: AnyScale<any, any>): scale is ScaleBand<any>;
|
|
49
50
|
/**
|
|
50
51
|
* Generic way to invert a scale value, handling scaleBand and continuous scales (linear, time, etc).
|
|
51
52
|
* Useful to map mouse event location (x,y) to domain value
|
|
@@ -5,6 +5,13 @@ import { Spring, Tween } from 'svelte/motion';
|
|
|
5
5
|
function isAnyScale(scale) {
|
|
6
6
|
return typeof scale === 'function' && typeof scale.range === 'function';
|
|
7
7
|
}
|
|
8
|
+
export function isScaleBand(scale) {
|
|
9
|
+
return typeof scale.bandwidth === 'function';
|
|
10
|
+
}
|
|
11
|
+
export function isScaleTime(scale) {
|
|
12
|
+
const domain = scale.domain();
|
|
13
|
+
return domain[0] instanceof Date || domain[1] instanceof Date;
|
|
14
|
+
}
|
|
8
15
|
export function getRange(scale) {
|
|
9
16
|
if (isAnyScale(scale)) {
|
|
10
17
|
return scale.range();
|
|
@@ -54,9 +61,6 @@ export function scaleBandInvert(scale) {
|
|
|
54
61
|
return domain[Math.max(0, Math.min(index, domain.length - 1))];
|
|
55
62
|
};
|
|
56
63
|
}
|
|
57
|
-
export function isScaleBand(scale) {
|
|
58
|
-
return typeof scale.bandwidth === 'function';
|
|
59
|
-
}
|
|
60
64
|
/**
|
|
61
65
|
* Generic way to invert a scale value, handling scaleBand and continuous scales (linear, time, etc).
|
|
62
66
|
* Useful to map mouse event location (x,y) to domain value
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Get points to create a polygon with given number of points and radius
|
|
2
|
+
*
|
|
3
|
+
* @param count - Number of points
|
|
4
|
+
* @param radius - Radius of the polygon
|
|
5
|
+
* @returns Array of points (angle, radius)
|
|
6
|
+
*/
|
|
7
|
+
export declare function polygonPoints(count: number, radius: number, rotate?: number): {
|
|
8
|
+
angle: number;
|
|
9
|
+
radius: number;
|
|
10
|
+
}[];
|
|
11
|
+
/** Create polygon
|
|
12
|
+
*
|
|
13
|
+
* @param cx - Center x coordinate
|
|
14
|
+
* @param cy - Center y coordinate
|
|
15
|
+
* @param count - Number of points
|
|
16
|
+
* @param radius - Radius of the polygon
|
|
17
|
+
* @param rotate - Rotation of the polygon (degrees)
|
|
18
|
+
* @param inset - Percent to inset odd points (<1 inset, >1 outset)
|
|
19
|
+
* @param scaleX - Horizontal stretch factor
|
|
20
|
+
* @param scaleY - Vertical stretch factor
|
|
21
|
+
* @param skewX - Skew angle in degrees along the X axis
|
|
22
|
+
* @param skewY - Skew angle in degrees along the Y axis
|
|
23
|
+
* @param tiltX - Tilt factor for x-coordinates (0 = no tilt, positive moves points top => down, negative moves points bottom => up)
|
|
24
|
+
* @param tiltY - Tilt factor for y-coordinates (0 = no tilt, positive moves points left => right, negative moves points right => left)
|
|
25
|
+
* @returns Array of points (x, y)
|
|
26
|
+
*/
|
|
27
|
+
export declare function polygon(options: {
|
|
28
|
+
cx: number;
|
|
29
|
+
cy: number;
|
|
30
|
+
count: number;
|
|
31
|
+
radius: number;
|
|
32
|
+
rotate?: number;
|
|
33
|
+
inset?: number;
|
|
34
|
+
scaleX?: number;
|
|
35
|
+
scaleY?: number;
|
|
36
|
+
skewX?: number;
|
|
37
|
+
skewY?: number;
|
|
38
|
+
tiltX?: number;
|
|
39
|
+
tiltY?: number;
|
|
40
|
+
}): {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
}[];
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { range } from 'd3-array';
|
|
2
|
+
import { degreesToRadians } from './math.js';
|
|
3
|
+
/** Get points to create a polygon with given number of points and radius
|
|
4
|
+
*
|
|
5
|
+
* @param count - Number of points
|
|
6
|
+
* @param radius - Radius of the polygon
|
|
7
|
+
* @returns Array of points (angle, radius)
|
|
8
|
+
*/
|
|
9
|
+
export function polygonPoints(count, radius, rotate = 0) {
|
|
10
|
+
const angle = 360 / count;
|
|
11
|
+
return range(count).map((index) => {
|
|
12
|
+
return {
|
|
13
|
+
angle: degreesToRadians(angle * index) + degreesToRadians(rotate),
|
|
14
|
+
radius,
|
|
15
|
+
};
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/** Create polygon
|
|
19
|
+
*
|
|
20
|
+
* @param cx - Center x coordinate
|
|
21
|
+
* @param cy - Center y coordinate
|
|
22
|
+
* @param count - Number of points
|
|
23
|
+
* @param radius - Radius of the polygon
|
|
24
|
+
* @param rotate - Rotation of the polygon (degrees)
|
|
25
|
+
* @param inset - Percent to inset odd points (<1 inset, >1 outset)
|
|
26
|
+
* @param scaleX - Horizontal stretch factor
|
|
27
|
+
* @param scaleY - Vertical stretch factor
|
|
28
|
+
* @param skewX - Skew angle in degrees along the X axis
|
|
29
|
+
* @param skewY - Skew angle in degrees along the Y axis
|
|
30
|
+
* @param tiltX - Tilt factor for x-coordinates (0 = no tilt, positive moves points top => down, negative moves points bottom => up)
|
|
31
|
+
* @param tiltY - Tilt factor for y-coordinates (0 = no tilt, positive moves points left => right, negative moves points right => left)
|
|
32
|
+
* @returns Array of points (x, y)
|
|
33
|
+
*/
|
|
34
|
+
export function polygon(options) {
|
|
35
|
+
const { cx, cy, count, radius, rotate = 0, inset = 1, scaleX = 1, scaleY = 1, skewX = 0, skewY = 0, tiltX = 0, tiltY = 0, } = options;
|
|
36
|
+
const skewXRad = degreesToRadians(skewX);
|
|
37
|
+
const skewYRad = degreesToRadians(skewY);
|
|
38
|
+
return polygonPoints(count, radius, rotate).map(({ angle, radius }, i) => {
|
|
39
|
+
// inset
|
|
40
|
+
const insetScale = i % 2 == 0 ? 1 : 1 - inset;
|
|
41
|
+
// scale
|
|
42
|
+
let x = radius * insetScale * Math.cos(angle) * scaleX;
|
|
43
|
+
let y = radius * insetScale * Math.sin(angle) * scaleY;
|
|
44
|
+
// tilt
|
|
45
|
+
const normalizedY = (y + radius) / (2 * radius);
|
|
46
|
+
const normalizedX = (x + radius) / (2 * radius);
|
|
47
|
+
const tiltScaleX = tiltX > 0 ? 1 + tiltX * (1 - normalizedY) : 1 - tiltX * normalizedY;
|
|
48
|
+
const tiltScaleY = tiltY > 0 ? 1 + tiltY * (1 - normalizedX) : 1 - tiltY * normalizedX;
|
|
49
|
+
x *= tiltScaleX;
|
|
50
|
+
y *= tiltScaleY;
|
|
51
|
+
// skew
|
|
52
|
+
const xSkewed = x + Math.tan(skewXRad) * y;
|
|
53
|
+
const ySkewed = y + Math.tan(skewYRad) * x;
|
|
54
|
+
return {
|
|
55
|
+
x: cx + xSkewed,
|
|
56
|
+
y: cy + ySkewed,
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export declare const getStringWidth: any;
|
|
2
|
+
export type RasterizeTextOptions = {
|
|
3
|
+
fontSize?: string;
|
|
4
|
+
fontWeight?: number;
|
|
5
|
+
fontFamily?: string;
|
|
6
|
+
textAlign?: CanvasTextAlign;
|
|
7
|
+
textBaseline?: CanvasTextBaseline;
|
|
8
|
+
spacing?: number;
|
|
9
|
+
width?: number;
|
|
10
|
+
height?: number;
|
|
11
|
+
x?: number;
|
|
12
|
+
y?: number;
|
|
13
|
+
};
|
|
14
|
+
export declare function rasterizeText(text: string, options?: RasterizeTextOptions): number[][];
|
|
15
|
+
export declare function toTitleCase(str: string): string;
|
|
16
|
+
export type TruncateTextOptions = {
|
|
17
|
+
/**
|
|
18
|
+
* The maximum pixel width (optional if maxChars is provided).
|
|
19
|
+
*/
|
|
20
|
+
maxWidth?: number;
|
|
21
|
+
/**
|
|
22
|
+
* CSS style for width calculation
|
|
23
|
+
*/
|
|
24
|
+
style?: CSSStyleDeclaration;
|
|
25
|
+
/**
|
|
26
|
+
* The maximum character count
|
|
27
|
+
*/
|
|
28
|
+
maxChars?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Where to place the ellipsis: 'start', 'middle', or 'end'
|
|
31
|
+
*
|
|
32
|
+
* @default 'end'
|
|
33
|
+
*/
|
|
34
|
+
position?: 'start' | 'middle' | 'end';
|
|
35
|
+
/**
|
|
36
|
+
* The character(s) to use as the ellipsis
|
|
37
|
+
*
|
|
38
|
+
* @default '…'
|
|
39
|
+
*/
|
|
40
|
+
ellipsis?: string;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Truncates a string to fit within a specified pixel width or character count.
|
|
44
|
+
* If the string's width exceeds the maxWidth, it will be truncated. If the character
|
|
45
|
+
* count exceeds maxChars, it will also be truncated.
|
|
46
|
+
*
|
|
47
|
+
* The ellipsis can be placed at the start, middle, or end of the string.
|
|
48
|
+
*/
|
|
49
|
+
export declare function truncateText(text: string, { position, ellipsis, maxWidth, style, maxChars }: TruncateTextOptions): string;
|
package/dist/utils/string.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import memoize from 'memoize';
|
|
2
2
|
const MEASUREMENT_ELEMENT_ID = '__text_measurement_id';
|
|
3
3
|
function _getStringWidth(str, style) {
|
|
4
4
|
try {
|
|
@@ -24,7 +24,9 @@ function _getStringWidth(str, style) {
|
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
export const getStringWidth = memoize(_getStringWidth,
|
|
27
|
+
export const getStringWidth = memoize(_getStringWidth, {
|
|
28
|
+
cacheKey: ([str, style]) => `${str}_${JSON.stringify(style)}`,
|
|
29
|
+
});
|
|
28
30
|
export function rasterizeText(text, options = {}) {
|
|
29
31
|
const fontSize = options.fontSize ?? '200px';
|
|
30
32
|
const fontWeight = options.fontWeight ?? 600;
|
package/dist/utils/ticks.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { type TimeInterval } from 'd3-time';
|
|
2
|
+
import { Duration, type FormatType, type FormatConfig } from '@layerstack/utils';
|
|
2
3
|
import { type AnyScale } from './scales.svelte.js';
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function formatMajorTick(date: Date, rangeStart: Date, rangeEnd: Date): string;
|
|
5
|
-
export declare function getMinorTicks(start: Date, end: Date): TimeInterval | null;
|
|
4
|
+
export declare function getDurationFormat(duration: Duration, multiline?: boolean): (date: Date, i: number) => string;
|
|
6
5
|
export type TicksConfig = number | any[] | ((scale: AnyScale) => any[] | undefined) | {
|
|
7
6
|
interval: TimeInterval | null;
|
|
8
7
|
} | null;
|
|
9
|
-
export declare function resolveTickVals(scale: AnyScale, ticks?: TicksConfig,
|
|
8
|
+
export declare function resolveTickVals(scale: AnyScale, ticks?: TicksConfig, count?: number): any[];
|
|
9
|
+
export declare function resolveTickFormat(scale: AnyScale, ticks?: TicksConfig, count?: number, formatType?: FormatType | FormatConfig, multiline?: boolean): (date: Date, i: number) => string;
|