layerchart 2.0.0-next.23 → 2.0.0-next.25
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/Axis.svelte +16 -7
- package/dist/components/Legend.svelte +11 -7
- package/dist/components/Legend.svelte.d.ts +2 -2
- package/dist/components/Text.svelte +3 -13
- package/dist/components/charts/ArcChart.svelte +3 -1
- package/dist/components/charts/AreaChart.svelte +2 -1
- package/dist/components/charts/BarChart.svelte +1 -0
- package/dist/components/charts/LineChart.svelte +2 -1
- package/dist/components/charts/PieChart.svelte +3 -1
- package/dist/components/charts/ScatterChart.svelte +2 -1
- package/dist/components/tooltip/TooltipContext.svelte +22 -7
- package/dist/components/tooltip/TooltipContext.svelte.d.ts +1 -1
- package/dist/utils/ticks.d.ts +13 -2
- package/dist/utils/ticks.js +63 -29
- package/package.json +1 -1
|
@@ -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(' ')}
|
|
@@ -384,7 +384,9 @@
|
|
|
384
384
|
]}
|
|
385
385
|
padding={{ bottom: legend === true ? 32 : 0 }}
|
|
386
386
|
{...restProps}
|
|
387
|
-
tooltip={tooltip === false
|
|
387
|
+
tooltip={tooltip === false
|
|
388
|
+
? false
|
|
389
|
+
: { ...props.tooltip?.context, ...(typeof tooltip === 'object' ? tooltip : null) }}
|
|
388
390
|
>
|
|
389
391
|
{#snippet children({ context })}
|
|
390
392
|
{@const snippetProps = {
|
|
@@ -443,10 +443,11 @@
|
|
|
443
443
|
tooltip={tooltip === false
|
|
444
444
|
? false
|
|
445
445
|
: {
|
|
446
|
-
mode: '
|
|
446
|
+
mode: 'quadtree-x',
|
|
447
447
|
onclick: onTooltipClick,
|
|
448
448
|
debug,
|
|
449
449
|
...props.tooltip?.context,
|
|
450
|
+
...(typeof tooltip === 'object' ? tooltip : null),
|
|
450
451
|
}}
|
|
451
452
|
brush={brush && (brush === true || brush.mode == undefined || brush.mode === 'integrated')
|
|
452
453
|
? {
|
|
@@ -343,10 +343,11 @@
|
|
|
343
343
|
tooltip={tooltip === false
|
|
344
344
|
? false
|
|
345
345
|
: {
|
|
346
|
-
mode: '
|
|
346
|
+
mode: 'quadtree-x',
|
|
347
347
|
onclick: onTooltipClick,
|
|
348
348
|
debug,
|
|
349
349
|
...props.tooltip?.context,
|
|
350
|
+
...(typeof tooltip === 'object' ? tooltip : null),
|
|
350
351
|
}}
|
|
351
352
|
brush={brush && (brush === true || brush.mode == undefined || brush.mode === 'integrated')
|
|
352
353
|
? {
|
|
@@ -409,7 +409,9 @@
|
|
|
409
409
|
]}
|
|
410
410
|
padding={{ bottom: legend === true ? 32 : 0 }}
|
|
411
411
|
{...restProps}
|
|
412
|
-
tooltip={tooltip === false
|
|
412
|
+
tooltip={tooltip === false
|
|
413
|
+
? false
|
|
414
|
+
: { ...props.tooltip?.context, ...(typeof tooltip === 'object' ? tooltip : null) }}
|
|
413
415
|
>
|
|
414
416
|
{#snippet children({ context })}
|
|
415
417
|
{@const snippetProps = {
|
|
@@ -253,10 +253,11 @@
|
|
|
253
253
|
tooltip={tooltip === false
|
|
254
254
|
? false
|
|
255
255
|
: {
|
|
256
|
-
mode: '
|
|
256
|
+
mode: 'quadtree',
|
|
257
257
|
onclick: onTooltipClick,
|
|
258
258
|
debug,
|
|
259
259
|
...props.tooltip?.context,
|
|
260
|
+
...(typeof tooltip === 'object' ? tooltip : null),
|
|
260
261
|
}}
|
|
261
262
|
brush={brush && (brush === true || brush.mode == undefined || brush.mode === 'integrated')
|
|
262
263
|
? {
|
|
@@ -6,13 +6,15 @@
|
|
|
6
6
|
const _TooltipContext = new Context<TooltipContextValue>('TooltipContext');
|
|
7
7
|
|
|
8
8
|
type TooltipMode =
|
|
9
|
-
| 'bisect-x'
|
|
10
|
-
| 'bisect-y'
|
|
9
|
+
| 'bisect-x' // requires values to be sorted
|
|
10
|
+
| 'bisect-y' // requires values to be sorted
|
|
11
11
|
| 'band'
|
|
12
|
-
| 'bisect-band'
|
|
12
|
+
| 'bisect-band' // requires values to be sorted
|
|
13
13
|
| 'bounds'
|
|
14
14
|
| 'voronoi'
|
|
15
15
|
| 'quadtree'
|
|
16
|
+
| 'quadtree-x' // ignores y values (constant 0)
|
|
17
|
+
| 'quadtree-y' // ignores x values (constant 0)
|
|
16
18
|
| 'manual';
|
|
17
19
|
|
|
18
20
|
export type TooltipContextValue<T = any> = {
|
|
@@ -298,7 +300,6 @@
|
|
|
298
300
|
}
|
|
299
301
|
|
|
300
302
|
// If tooltipData not provided already (voronoi, etc), attempt to find it
|
|
301
|
-
// TODO: When using bisect-x/y/band, values should be sorted. Typically they are for `x`, but not `y` (and band depends on if x or y scale)
|
|
302
303
|
if (tooltipData == null) {
|
|
303
304
|
switch (mode) {
|
|
304
305
|
case 'bisect-x': {
|
|
@@ -311,6 +312,7 @@
|
|
|
311
312
|
xValueAtPoint = scaleInvert(ctx.xScale, point.x - ctx.padding.left);
|
|
312
313
|
}
|
|
313
314
|
|
|
315
|
+
// Requires values to be sorted
|
|
314
316
|
const index = bisectX(ctx.flatData, xValueAtPoint, 1);
|
|
315
317
|
const previousValue = ctx.flatData[index - 1];
|
|
316
318
|
const currentValue = ctx.flatData[index];
|
|
@@ -322,6 +324,7 @@
|
|
|
322
324
|
// `y` value at pointer coordinate
|
|
323
325
|
const yValueAtPoint = scaleInvert(ctx.yScale, point.y - ctx.padding.top);
|
|
324
326
|
|
|
327
|
+
// Requires values to be sorted
|
|
325
328
|
const index = bisectY(ctx.flatData, yValueAtPoint, 1);
|
|
326
329
|
const previousValue = ctx.flatData[index - 1];
|
|
327
330
|
const currentValue = ctx.flatData[index];
|
|
@@ -339,6 +342,7 @@
|
|
|
339
342
|
const bandData = ctx.flatData
|
|
340
343
|
.filter((d) => ctx.x(d) === xValueAtPoint)
|
|
341
344
|
.sort(sortFunc(ctx.y as () => any)); // sort for bisect
|
|
345
|
+
// Requires values to be sorted
|
|
342
346
|
const index = bisectY(bandData, yValueAtPoint, 1);
|
|
343
347
|
const previousValue = bandData[index - 1];
|
|
344
348
|
const currentValue = bandData[index];
|
|
@@ -348,6 +352,7 @@
|
|
|
348
352
|
const bandData = ctx.flatData
|
|
349
353
|
.filter((d) => ctx.y(d) === yValueAtPoint)
|
|
350
354
|
.sort(sortFunc(ctx.x as () => any)); // sort for bisect
|
|
355
|
+
// Requires values to be sorted
|
|
351
356
|
const index = bisectX(bandData, xValueAtPoint, 1);
|
|
352
357
|
const previousValue = bandData[index - 1];
|
|
353
358
|
const currentValue = bandData[index];
|
|
@@ -358,6 +363,8 @@
|
|
|
358
363
|
break;
|
|
359
364
|
}
|
|
360
365
|
|
|
366
|
+
case 'quadtree-x':
|
|
367
|
+
case 'quadtree-y':
|
|
361
368
|
case 'quadtree': {
|
|
362
369
|
tooltipData = quadtree?.find(
|
|
363
370
|
point.x - ctx.padding.left,
|
|
@@ -406,9 +413,13 @@
|
|
|
406
413
|
}
|
|
407
414
|
|
|
408
415
|
const quadtree: Quadtree<[number, number]> | undefined = $derived.by(() => {
|
|
409
|
-
if (
|
|
416
|
+
if (['quadtree', 'quadtree-x', 'quadtree-y'].includes(mode)) {
|
|
410
417
|
return d3Quadtree()
|
|
411
418
|
.x((d) => {
|
|
419
|
+
if (mode === 'quadtree-y') {
|
|
420
|
+
return 0;
|
|
421
|
+
}
|
|
422
|
+
|
|
412
423
|
if (geoCtx.projection) {
|
|
413
424
|
const lat = ctx.x(d);
|
|
414
425
|
const long = ctx.y(d);
|
|
@@ -429,6 +440,10 @@
|
|
|
429
440
|
}
|
|
430
441
|
})
|
|
431
442
|
.y((d) => {
|
|
443
|
+
if (mode === 'quadtree-x') {
|
|
444
|
+
return 0;
|
|
445
|
+
}
|
|
446
|
+
|
|
432
447
|
if (geoCtx.projection) {
|
|
433
448
|
const lat = ctx.x(d);
|
|
434
449
|
const long = ctx.y(d);
|
|
@@ -509,7 +524,7 @@
|
|
|
509
524
|
});
|
|
510
525
|
|
|
511
526
|
const triggerPointerEvents = $derived(
|
|
512
|
-
['bisect-x', 'bisect-y', 'bisect-band', 'quadtree'].includes(mode)
|
|
527
|
+
['bisect-x', 'bisect-y', 'bisect-band', 'quadtree', 'quadtree-x', 'quadtree-y'].includes(mode)
|
|
513
528
|
);
|
|
514
529
|
|
|
515
530
|
function onPointerEnter(e: PointerEvent | MouseEvent | TouchEvent) {
|
|
@@ -646,7 +661,7 @@
|
|
|
646
661
|
{/each}
|
|
647
662
|
</g>
|
|
648
663
|
</Svg>
|
|
649
|
-
{:else if
|
|
664
|
+
{:else if ['quadtree', 'quadtree-x', 'quadtree-y'].includes(mode) && debug}
|
|
650
665
|
<Svg pointerEvents={false}>
|
|
651
666
|
<ChartClipPath>
|
|
652
667
|
<g class={layerClass('tooltip-quadtree-g')}>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
2
2
|
import type { Without } from '../../utils/types.js';
|
|
3
|
-
type TooltipMode = 'bisect-x' | 'bisect-y' | 'band' | 'bisect-band' | 'bounds' | 'voronoi' | 'quadtree' | 'manual';
|
|
3
|
+
type TooltipMode = 'bisect-x' | 'bisect-y' | 'band' | 'bisect-band' | 'bounds' | 'voronoi' | 'quadtree' | 'quadtree-x' | 'quadtree-y' | 'manual';
|
|
4
4
|
export type TooltipContextValue<T = any> = {
|
|
5
5
|
x: number;
|
|
6
6
|
y: number;
|
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