layerchart 2.0.0-next.23 → 2.0.0-next.24
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.
|
@@ -207,7 +207,16 @@
|
|
|
207
207
|
: undefined
|
|
208
208
|
);
|
|
209
209
|
const tickVals = $derived(resolveTickVals(scale, ticks, tickCount));
|
|
210
|
-
const tickFormat = $derived(
|
|
210
|
+
const tickFormat = $derived(
|
|
211
|
+
resolveTickFormat({
|
|
212
|
+
scale,
|
|
213
|
+
ticks,
|
|
214
|
+
count: tickCount,
|
|
215
|
+
formatType: format,
|
|
216
|
+
multiline: tickMultiline,
|
|
217
|
+
placement,
|
|
218
|
+
})
|
|
219
|
+
);
|
|
211
220
|
|
|
212
221
|
function getCoords(tick: any) {
|
|
213
222
|
switch (placement) {
|
|
@@ -255,14 +264,14 @@
|
|
|
255
264
|
return {
|
|
256
265
|
textAnchor: 'middle',
|
|
257
266
|
verticalAnchor: 'end',
|
|
258
|
-
dy: -tickLength
|
|
267
|
+
dy: -tickLength,
|
|
259
268
|
};
|
|
260
269
|
|
|
261
270
|
case 'bottom':
|
|
262
271
|
return {
|
|
263
272
|
textAnchor: 'middle',
|
|
264
273
|
verticalAnchor: 'start',
|
|
265
|
-
dy: tickLength,
|
|
274
|
+
dy: tickLength,
|
|
266
275
|
};
|
|
267
276
|
|
|
268
277
|
case 'left':
|
|
@@ -270,7 +279,6 @@
|
|
|
270
279
|
textAnchor: 'end',
|
|
271
280
|
verticalAnchor: 'middle',
|
|
272
281
|
dx: -tickLength,
|
|
273
|
-
dy: -2, // manually adjusted until Text supports custom styles
|
|
274
282
|
};
|
|
275
283
|
|
|
276
284
|
case 'right':
|
|
@@ -278,7 +286,6 @@
|
|
|
278
286
|
textAnchor: 'start',
|
|
279
287
|
verticalAnchor: 'middle',
|
|
280
288
|
dx: tickLength,
|
|
281
|
-
dy: -2, // manually adjusted until Text supports custom styles
|
|
282
289
|
};
|
|
283
290
|
|
|
284
291
|
case 'angle':
|
|
@@ -293,7 +300,7 @@
|
|
|
293
300
|
? 'end'
|
|
294
301
|
: 'start',
|
|
295
302
|
verticalAnchor: 'middle',
|
|
296
|
-
dx: Math.sin(xValue) *
|
|
303
|
+
dx: Math.sin(xValue) * tickLength,
|
|
297
304
|
dy: -Math.cos(xValue) * (tickLength + 4), // manually adjusted until Text supports custom styles
|
|
298
305
|
};
|
|
299
306
|
|
|
@@ -302,7 +309,6 @@
|
|
|
302
309
|
textAnchor: 'middle',
|
|
303
310
|
verticalAnchor: 'middle',
|
|
304
311
|
dx: 2,
|
|
305
|
-
dy: -2, // manually adjusted until Text supports custom styles
|
|
306
312
|
};
|
|
307
313
|
}
|
|
308
314
|
}
|
|
@@ -407,6 +413,9 @@
|
|
|
407
413
|
value: tickFormat(tick, index),
|
|
408
414
|
...getDefaultTickLabelProps(tick),
|
|
409
415
|
motion,
|
|
416
|
+
// complement 10px text (until Text supports custom styles)
|
|
417
|
+
capHeight: '7px',
|
|
418
|
+
lineHeight: '11px',
|
|
410
419
|
...tickLabelProps,
|
|
411
420
|
class: cls(
|
|
412
421
|
layerClass('axis-tick-label'),
|
|
@@ -86,9 +86,9 @@
|
|
|
86
86
|
title?: string;
|
|
87
87
|
label?: string;
|
|
88
88
|
tick?: string;
|
|
89
|
-
|
|
89
|
+
items?: string;
|
|
90
90
|
swatch?: string;
|
|
91
|
-
item?: (item: LegendItem) => string;
|
|
91
|
+
item?: string | ((item: LegendItem) => string);
|
|
92
92
|
};
|
|
93
93
|
|
|
94
94
|
/**
|
|
@@ -383,7 +383,7 @@
|
|
|
383
383
|
layerClass('legend-swatch-group'),
|
|
384
384
|
'flex gap-x-4 gap-y-1',
|
|
385
385
|
orientation === 'vertical' && 'flex-col',
|
|
386
|
-
classes.
|
|
386
|
+
classes.items
|
|
387
387
|
)}
|
|
388
388
|
>
|
|
389
389
|
{#each scaleConfig.tickValues ?? scaleConfig.xScale?.ticks?.(ticks) ?? [] as tick}
|
|
@@ -392,22 +392,26 @@
|
|
|
392
392
|
<button
|
|
393
393
|
class={cls(
|
|
394
394
|
layerClass('legend-swatch-button'),
|
|
395
|
-
'flex gap-1',
|
|
395
|
+
'flex items-center gap-1 truncate',
|
|
396
396
|
!onclick && 'cursor-auto',
|
|
397
|
-
classes.item
|
|
397
|
+
typeof classes.item === 'function' ? classes.item(item) : classes.item
|
|
398
398
|
)}
|
|
399
399
|
onclick={(e) => onclick?.(e, item)}
|
|
400
400
|
onpointerenter={(e) => onpointerenter?.(e, item)}
|
|
401
401
|
onpointerleave={(e) => onpointerleave?.(e, item)}
|
|
402
402
|
>
|
|
403
403
|
<div
|
|
404
|
-
class={cls(
|
|
404
|
+
class={cls(
|
|
405
|
+
layerClass('legend-swatch'),
|
|
406
|
+
'h-4 w-4 shrink-0 rounded-full',
|
|
407
|
+
classes.swatch
|
|
408
|
+
)}
|
|
405
409
|
style:background-color={color}
|
|
406
410
|
></div>
|
|
407
411
|
<div
|
|
408
412
|
class={cls(
|
|
409
413
|
layerClass('legend-swatch-label'),
|
|
410
|
-
'text-xs text-surface-content whitespace-nowrap',
|
|
414
|
+
'text-xs text-surface-content truncate whitespace-nowrap',
|
|
411
415
|
classes.label
|
|
412
416
|
)}
|
|
413
417
|
>
|
|
@@ -73,9 +73,9 @@ export type LegendPropsWithoutHTML = {
|
|
|
73
73
|
title?: string;
|
|
74
74
|
label?: string;
|
|
75
75
|
tick?: string;
|
|
76
|
-
|
|
76
|
+
items?: string;
|
|
77
77
|
swatch?: string;
|
|
78
|
-
item?: (item: LegendItem) => string;
|
|
78
|
+
item?: string | ((item: LegendItem) => string);
|
|
79
79
|
};
|
|
80
80
|
/**
|
|
81
81
|
* A bindable reference to the wrapping `<div>` element.
|
|
@@ -337,21 +337,11 @@
|
|
|
337
337
|
|
|
338
338
|
const startDy = $derived.by(() => {
|
|
339
339
|
if (verticalAnchor === 'start') {
|
|
340
|
-
return getPixelValue(
|
|
340
|
+
return getPixelValue(lineHeight);
|
|
341
341
|
} else if (verticalAnchor === 'middle') {
|
|
342
342
|
return ((lineCount - 1) / 2) * -getPixelValue(lineHeight) + getPixelValue(capHeight) / 2;
|
|
343
343
|
} else {
|
|
344
|
-
return (lineCount - 1) * -getPixelValue(lineHeight);
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
const pathStartDy = $derived.by(() => {
|
|
349
|
-
if (verticalAnchor === 'start') {
|
|
350
|
-
return getPixelValue(capHeight);
|
|
351
|
-
} else if (verticalAnchor === 'middle') {
|
|
352
|
-
return (0 / 2) * -getPixelValue(lineHeight) + getPixelValue(capHeight) / 2;
|
|
353
|
-
} else {
|
|
354
|
-
return 0 * -getPixelValue(lineHeight);
|
|
344
|
+
return (lineCount - 1) * -getPixelValue(lineHeight) - getPixelValue(capHeight) / 2;
|
|
355
345
|
}
|
|
356
346
|
});
|
|
357
347
|
|
|
@@ -537,7 +527,7 @@
|
|
|
537
527
|
{#each wordsByLines as line, index}
|
|
538
528
|
<tspan
|
|
539
529
|
x={motionX.current}
|
|
540
|
-
dy={index === 0 ? startDy : lineHeight}
|
|
530
|
+
dy={index === 0 ? startDy : getPixelValue(lineHeight)}
|
|
541
531
|
class={layerClass('text-tspan')}
|
|
542
532
|
>
|
|
543
533
|
{line.words.join(' ')}
|
package/dist/utils/ticks.d.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { type TimeInterval } from 'd3-time';
|
|
2
2
|
import { Duration, type FormatType, type FormatConfig } from '@layerstack/utils';
|
|
3
3
|
import { type AnyScale } from './scales.svelte.js';
|
|
4
|
-
|
|
4
|
+
import type { AxisProps } from '../components/Axis.svelte';
|
|
5
|
+
export declare function getDurationFormat(duration: Duration, options?: {
|
|
6
|
+
multiline?: boolean;
|
|
7
|
+
placement?: AxisProps['placement'];
|
|
8
|
+
}): (date: Date, i: number) => string;
|
|
5
9
|
export type TicksConfig = number | any[] | ((scale: AnyScale) => any[] | undefined) | {
|
|
6
10
|
interval: TimeInterval | null;
|
|
7
11
|
} | null;
|
|
8
12
|
export declare function resolveTickVals(scale: AnyScale, ticks?: TicksConfig, count?: number): any[];
|
|
9
|
-
export declare function resolveTickFormat(
|
|
13
|
+
export declare function resolveTickFormat(options: {
|
|
14
|
+
scale: AnyScale;
|
|
15
|
+
ticks?: TicksConfig;
|
|
16
|
+
count?: number;
|
|
17
|
+
formatType?: FormatType | FormatConfig;
|
|
18
|
+
multiline?: boolean;
|
|
19
|
+
placement?: AxisProps['placement'];
|
|
20
|
+
}): (date: Date, i: number) => string;
|
package/dist/utils/ticks.js
CHANGED
|
@@ -1,43 +1,52 @@
|
|
|
1
1
|
import { timeYear, timeMonth, timeDay, timeTicks } from 'd3-time';
|
|
2
2
|
import { format, Duration, isLiteralObject, DateToken, } from '@layerstack/utils';
|
|
3
3
|
import { isScaleBand, isScaleTime } from './scales.svelte.js';
|
|
4
|
-
export function getDurationFormat(duration,
|
|
4
|
+
export function getDurationFormat(duration, options = {
|
|
5
|
+
multiline: false,
|
|
6
|
+
}) {
|
|
7
|
+
const { multiline = false, placement = 'bottom' } = options;
|
|
5
8
|
return function (date, i) {
|
|
9
|
+
let result = '';
|
|
6
10
|
if (+duration >= +new Duration({ duration: { years: 1 } })) {
|
|
7
11
|
// Year
|
|
8
|
-
|
|
12
|
+
result = format(date, 'year');
|
|
9
13
|
}
|
|
10
14
|
else if (+duration >= +new Duration({ duration: { days: 28 } })) {
|
|
11
15
|
// Month
|
|
12
16
|
const isFirst = i === 0 || +timeYear.floor(date) === +date;
|
|
13
17
|
if (multiline) {
|
|
14
|
-
|
|
18
|
+
result = [format(date, 'month', { variant: 'short' }), isFirst && format(date, 'year')];
|
|
15
19
|
}
|
|
16
20
|
else {
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
result =
|
|
22
|
+
format(date, 'month', { variant: 'short' }) +
|
|
23
|
+
(isFirst ? ` '${format(date, 'year', { variant: 'short' })}` : '');
|
|
19
24
|
}
|
|
20
25
|
}
|
|
21
26
|
else if (+duration >= +new Duration({ duration: { days: 1 } })) {
|
|
22
27
|
// Day
|
|
23
28
|
const isFirst = i === 0 || date.getDate() <= duration.days;
|
|
24
29
|
if (multiline) {
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
result = [
|
|
31
|
+
format(date, 'custom', { custom: DateToken.DayOfMonth_numeric }),
|
|
32
|
+
isFirst && format(date, 'month', { variant: 'short' }),
|
|
33
|
+
];
|
|
27
34
|
}
|
|
28
35
|
else {
|
|
29
|
-
|
|
36
|
+
result = format(date, 'day', { variant: 'short' });
|
|
30
37
|
}
|
|
31
38
|
}
|
|
32
39
|
else if (+duration >= +new Duration({ duration: { hours: 1 } })) {
|
|
33
40
|
// Hours
|
|
34
41
|
const isFirst = i === 0 || +timeDay.floor(date) === +date;
|
|
35
42
|
if (multiline) {
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
result = [
|
|
44
|
+
format(date, 'custom', { custom: DateToken.Hour_numeric }),
|
|
45
|
+
isFirst && format(date, 'day', { variant: 'short' }),
|
|
46
|
+
];
|
|
38
47
|
}
|
|
39
48
|
else {
|
|
40
|
-
|
|
49
|
+
result = isFirst
|
|
41
50
|
? format(date, 'day', { variant: 'short' })
|
|
42
51
|
: format(date, 'custom', { custom: DateToken.Hour_numeric });
|
|
43
52
|
}
|
|
@@ -46,34 +55,58 @@ export function getDurationFormat(duration, multiline = false) {
|
|
|
46
55
|
// Minutes
|
|
47
56
|
const isFirst = i === 0 || +timeDay.floor(date) === +date;
|
|
48
57
|
if (multiline) {
|
|
49
|
-
|
|
50
|
-
|
|
58
|
+
result = [
|
|
59
|
+
format(date, 'time', { variant: 'short' }),
|
|
60
|
+
isFirst && format(date, 'day', { variant: 'short' }),
|
|
61
|
+
];
|
|
51
62
|
}
|
|
52
63
|
else {
|
|
53
|
-
|
|
64
|
+
result = format(date, 'time', { variant: 'short' });
|
|
54
65
|
}
|
|
55
66
|
}
|
|
56
67
|
else if (+duration >= +new Duration({ duration: { seconds: 1 } })) {
|
|
57
68
|
// Seconds
|
|
58
69
|
const isFirst = i === 0 || +timeDay.floor(date) === +date;
|
|
59
|
-
|
|
60
|
-
|
|
70
|
+
result = [
|
|
71
|
+
format(date, 'time'),
|
|
72
|
+
multiline && isFirst && format(date, 'day', { variant: 'short' }),
|
|
73
|
+
];
|
|
61
74
|
}
|
|
62
75
|
else if (+duration >= +new Duration({ duration: { milliseconds: 1 } })) {
|
|
63
76
|
// Milliseconds
|
|
64
77
|
const isFirst = i === 0 || +timeDay.floor(date) === +date;
|
|
65
|
-
|
|
66
|
-
custom
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
result = [
|
|
79
|
+
format(date, 'custom', {
|
|
80
|
+
custom: [
|
|
81
|
+
DateToken.Hour_2Digit,
|
|
82
|
+
DateToken.Minute_2Digit,
|
|
83
|
+
DateToken.Second_2Digit,
|
|
84
|
+
DateToken.MiliSecond_3,
|
|
85
|
+
DateToken.Hour_woAMPM,
|
|
86
|
+
],
|
|
87
|
+
}),
|
|
88
|
+
multiline && isFirst && format(date, 'day', { variant: 'short' }),
|
|
89
|
+
];
|
|
74
90
|
}
|
|
75
91
|
else {
|
|
76
|
-
|
|
92
|
+
result = date.toString();
|
|
93
|
+
}
|
|
94
|
+
if (Array.isArray(result)) {
|
|
95
|
+
switch (placement) {
|
|
96
|
+
case 'top':
|
|
97
|
+
return result.filter(Boolean).reverse().join('\n');
|
|
98
|
+
case 'bottom':
|
|
99
|
+
return result.filter(Boolean).join('\n');
|
|
100
|
+
case 'left':
|
|
101
|
+
return result.filter(Boolean).reverse().join(' ');
|
|
102
|
+
case 'right':
|
|
103
|
+
return result.filter(Boolean).join(' ');
|
|
104
|
+
default:
|
|
105
|
+
return result.filter(Boolean).join('\n');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
return result;
|
|
77
110
|
}
|
|
78
111
|
};
|
|
79
112
|
}
|
|
@@ -103,7 +136,8 @@ export function resolveTickVals(scale, ticks, count) {
|
|
|
103
136
|
}
|
|
104
137
|
return [];
|
|
105
138
|
}
|
|
106
|
-
export function resolveTickFormat(
|
|
139
|
+
export function resolveTickFormat(options) {
|
|
140
|
+
const { scale, ticks, count, formatType, multiline, placement } = options;
|
|
107
141
|
// Explicit format
|
|
108
142
|
if (formatType) {
|
|
109
143
|
// @ts-expect-error - improve types
|
|
@@ -114,12 +148,12 @@ export function resolveTickFormat(scale, ticks, count, formatType, multiline = f
|
|
|
114
148
|
if (isLiteralObject(ticks) && 'interval' in ticks && ticks.interval != null) {
|
|
115
149
|
const start = ticks.interval.floor(new Date());
|
|
116
150
|
const end = ticks.interval.ceil(new Date());
|
|
117
|
-
return getDurationFormat(new Duration({ start, end }), multiline);
|
|
151
|
+
return getDurationFormat(new Duration({ start, end }), { multiline, placement });
|
|
118
152
|
}
|
|
119
153
|
else {
|
|
120
154
|
// Compare first 2 ticks to determine duration between ticks for formatting
|
|
121
155
|
const [start, end] = timeTicks(scale.domain()[0], scale.domain()[1], count);
|
|
122
|
-
return getDurationFormat(new Duration({ start, end }), multiline);
|
|
156
|
+
return getDurationFormat(new Duration({ start, end }), { multiline, placement });
|
|
123
157
|
}
|
|
124
158
|
}
|
|
125
159
|
// Format from scale
|
package/package.json
CHANGED