@faintshadow/flarecharts 26.3.1
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/LICENSE +40 -0
- package/README.md +103 -0
- package/dist/charts/AreaChart.svelte +150 -0
- package/dist/charts/AreaChart.svelte.d.ts +60 -0
- package/dist/charts/BarChart.svelte +142 -0
- package/dist/charts/BarChart.svelte.d.ts +58 -0
- package/dist/charts/BoxPlotChart.svelte +138 -0
- package/dist/charts/BoxPlotChart.svelte.d.ts +56 -0
- package/dist/charts/DonutChart.svelte +129 -0
- package/dist/charts/DonutChart.svelte.d.ts +73 -0
- package/dist/charts/LineChart.svelte +149 -0
- package/dist/charts/LineChart.svelte.d.ts +63 -0
- package/dist/charts/Sparkline.svelte +87 -0
- package/dist/charts/Sparkline.svelte.d.ts +40 -0
- package/dist/charts/StackChart.svelte +157 -0
- package/dist/charts/StackChart.svelte.d.ts +69 -0
- package/dist/components/Arc.svelte +202 -0
- package/dist/components/Arc.svelte.d.ts +50 -0
- package/dist/components/Area.svelte +264 -0
- package/dist/components/Area.svelte.d.ts +54 -0
- package/dist/components/Axis.svelte +139 -0
- package/dist/components/Axis.svelte.d.ts +26 -0
- package/dist/components/Bars.svelte +192 -0
- package/dist/components/Bars.svelte.d.ts +55 -0
- package/dist/components/Box.svelte +287 -0
- package/dist/components/Box.svelte.d.ts +48 -0
- package/dist/components/Chart.svelte +207 -0
- package/dist/components/Chart.svelte.d.ts +23 -0
- package/dist/components/Crosshair.svelte +67 -0
- package/dist/components/Crosshair.svelte.d.ts +14 -0
- package/dist/components/Grid.svelte +38 -0
- package/dist/components/Grid.svelte.d.ts +14 -0
- package/dist/components/Labels.svelte +61 -0
- package/dist/components/Labels.svelte.d.ts +35 -0
- package/dist/components/Legend.svelte +81 -0
- package/dist/components/Legend.svelte.d.ts +12 -0
- package/dist/components/Line.svelte +192 -0
- package/dist/components/Line.svelte.d.ts +47 -0
- package/dist/components/PlotBand.svelte +68 -0
- package/dist/components/PlotBand.svelte.d.ts +14 -0
- package/dist/components/PlotLine.svelte +54 -0
- package/dist/components/PlotLine.svelte.d.ts +16 -0
- package/dist/components/Points.svelte +179 -0
- package/dist/components/Points.svelte.d.ts +53 -0
- package/dist/components/Svg.svelte +36 -0
- package/dist/components/Svg.svelte.d.ts +8 -0
- package/dist/components/Tooltip.svelte +211 -0
- package/dist/components/Tooltip.svelte.d.ts +44 -0
- package/dist/core/bisect.d.ts +5 -0
- package/dist/core/bisect.js +23 -0
- package/dist/core/context.svelte.d.ts +140 -0
- package/dist/core/context.svelte.js +294 -0
- package/dist/core/curves.d.ts +4 -0
- package/dist/core/curves.js +13 -0
- package/dist/core/hit.d.ts +34 -0
- package/dist/core/hit.js +43 -0
- package/dist/core/keynav.d.ts +20 -0
- package/dist/core/keynav.js +41 -0
- package/dist/core/labels.d.ts +39 -0
- package/dist/core/labels.js +27 -0
- package/dist/core/merge.d.ts +17 -0
- package/dist/core/merge.js +46 -0
- package/dist/core/motion.svelte.d.ts +31 -0
- package/dist/core/motion.svelte.js +129 -0
- package/dist/core/normalize.d.ts +35 -0
- package/dist/core/normalize.js +97 -0
- package/dist/core/options.d.ts +113 -0
- package/dist/core/options.js +36 -0
- package/dist/core/palette.d.ts +8 -0
- package/dist/core/palette.js +24 -0
- package/dist/core/responsive.d.ts +6 -0
- package/dist/core/responsive.js +19 -0
- package/dist/core/scales.d.ts +31 -0
- package/dist/core/scales.js +89 -0
- package/dist/core/stack.d.ts +19 -0
- package/dist/core/stack.js +133 -0
- package/dist/core/stats.d.ts +45 -0
- package/dist/core/stats.js +114 -0
- package/dist/core/symbols.d.ts +8 -0
- package/dist/core/symbols.js +31 -0
- package/dist/core/types.d.ts +28 -0
- package/dist/core/types.js +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +42 -0
- package/package.json +81 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Vertical line at the (snapped) x position. */
|
|
3
|
+
x?: boolean;
|
|
4
|
+
/** Horizontal line at the pointer y position. */
|
|
5
|
+
y?: boolean;
|
|
6
|
+
/** Snap the x line to the nearest data point across visible series. */
|
|
7
|
+
snap?: boolean;
|
|
8
|
+
dash?: string;
|
|
9
|
+
width?: number;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const Crosshair: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type Crosshair = ReturnType<typeof Crosshair>;
|
|
14
|
+
export default Crosshair;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getChartContext } from '../core/context.svelte.js';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
/** Draw vertical lines at x ticks. */
|
|
6
|
+
x?: boolean;
|
|
7
|
+
/** Draw horizontal lines at y ticks. */
|
|
8
|
+
y?: boolean;
|
|
9
|
+
/** Tick count hint. */
|
|
10
|
+
ticks?: number;
|
|
11
|
+
/** stroke-dasharray, e.g. "3 3". */
|
|
12
|
+
dash?: string;
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let { x = false, y = true, ticks, dash, class: klass = '' }: Props = $props();
|
|
17
|
+
|
|
18
|
+
const ctx = getChartContext();
|
|
19
|
+
|
|
20
|
+
const xLines = $derived(x ? ctx.xTicks(ticks).map(ctx.xPos).filter(Number.isFinite) : []);
|
|
21
|
+
const yLines = $derived(y ? ctx.yTicks(ticks).map(ctx.yPos).filter(Number.isFinite) : []);
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<g class="fc-grid {klass}">
|
|
25
|
+
{#each xLines as px (px)}
|
|
26
|
+
<line class="fc-grid-line fc-grid-x" x1={px} x2={px} y1="0" y2={ctx.innerHeight} stroke-dasharray={dash} />
|
|
27
|
+
{/each}
|
|
28
|
+
{#each yLines as py (py)}
|
|
29
|
+
<line class="fc-grid-line fc-grid-y" x1="0" x2={ctx.innerWidth} y1={py} y2={py} stroke-dasharray={dash} />
|
|
30
|
+
{/each}
|
|
31
|
+
</g>
|
|
32
|
+
|
|
33
|
+
<style>
|
|
34
|
+
.fc-grid-line {
|
|
35
|
+
stroke: var(--fc-grid, #e2e8f0);
|
|
36
|
+
fill: none;
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Draw vertical lines at x ticks. */
|
|
3
|
+
x?: boolean;
|
|
4
|
+
/** Draw horizontal lines at y ticks. */
|
|
5
|
+
y?: boolean;
|
|
6
|
+
/** Tick count hint. */
|
|
7
|
+
ticks?: number;
|
|
8
|
+
/** stroke-dasharray, e.g. "3 3". */
|
|
9
|
+
dash?: string;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const Grid: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type Grid = ReturnType<typeof Grid>;
|
|
14
|
+
export default Grid;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script lang="ts" generics="T">
|
|
2
|
+
import { getChartContext } from '../core/context.svelte.js';
|
|
3
|
+
import { normalizePoints } from '../core/normalize.js';
|
|
4
|
+
import type { XValue } from '../core/normalize.js';
|
|
5
|
+
import { placeLabels } from '../core/labels.js';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
data: readonly T[];
|
|
9
|
+
x?: (datum: T, index: number) => XValue;
|
|
10
|
+
y?: (datum: T, index: number) => number | null | undefined;
|
|
11
|
+
/** Label text per point; defaults to the (rounded) y value. */
|
|
12
|
+
format?: (y: number, datum: T, index: number) => string;
|
|
13
|
+
/** Distance from the anchor point, px. */
|
|
14
|
+
offset?: number;
|
|
15
|
+
fontSize?: number;
|
|
16
|
+
class?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let { data, x, y, format, offset = 8, fontSize = 10, class: klass = '' }: Props = $props();
|
|
20
|
+
|
|
21
|
+
const ctx = getChartContext();
|
|
22
|
+
|
|
23
|
+
const fmt = $derived(format ?? ((value: number) => String(Math.round(value * 100) / 100)));
|
|
24
|
+
|
|
25
|
+
const placed = $derived.by(() => {
|
|
26
|
+
const points = normalizePoints(data, { x, y });
|
|
27
|
+
const candidates = [];
|
|
28
|
+
for (const p of points) {
|
|
29
|
+
if (p.y == null) continue;
|
|
30
|
+
const px = ctx.xPos(p.x);
|
|
31
|
+
const py = ctx.yPos(p.y);
|
|
32
|
+
if (!Number.isFinite(px) || !Number.isFinite(py)) continue;
|
|
33
|
+
candidates.push({ px, py, text: fmt(p.y, p.datum, p.index) });
|
|
34
|
+
}
|
|
35
|
+
return placeLabels(candidates, {
|
|
36
|
+
bounds: { width: ctx.innerWidth, height: ctx.innerHeight },
|
|
37
|
+
offset,
|
|
38
|
+
fontSize
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<g class="fc-labels {klass}">
|
|
44
|
+
{#each placed as label (label.px + '-' + label.py)}
|
|
45
|
+
<text
|
|
46
|
+
class="fc-label"
|
|
47
|
+
x={label.x}
|
|
48
|
+
y={label.y}
|
|
49
|
+
text-anchor="middle"
|
|
50
|
+
font-size={fontSize}
|
|
51
|
+
>
|
|
52
|
+
{label.text}
|
|
53
|
+
</text>
|
|
54
|
+
{/each}
|
|
55
|
+
</g>
|
|
56
|
+
|
|
57
|
+
<style>
|
|
58
|
+
.fc-label {
|
|
59
|
+
fill: var(--fc-label, var(--fc-axis-label, #64748b));
|
|
60
|
+
}
|
|
61
|
+
</style>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { XValue } from '../core/normalize.js';
|
|
2
|
+
declare function $$render<T>(): {
|
|
3
|
+
props: {
|
|
4
|
+
data: readonly T[];
|
|
5
|
+
x?: (datum: T, index: number) => XValue;
|
|
6
|
+
y?: (datum: T, index: number) => number | null | undefined;
|
|
7
|
+
/** Label text per point; defaults to the (rounded) y value. */
|
|
8
|
+
format?: (y: number, datum: T, index: number) => string;
|
|
9
|
+
/** Distance from the anchor point, px. */
|
|
10
|
+
offset?: number;
|
|
11
|
+
fontSize?: number;
|
|
12
|
+
class?: string;
|
|
13
|
+
};
|
|
14
|
+
exports: {};
|
|
15
|
+
bindings: "";
|
|
16
|
+
slots: {};
|
|
17
|
+
events: {};
|
|
18
|
+
};
|
|
19
|
+
declare class __sveltets_Render<T> {
|
|
20
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
21
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
22
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
23
|
+
bindings(): "";
|
|
24
|
+
exports(): {};
|
|
25
|
+
}
|
|
26
|
+
interface $$IsomorphicComponent {
|
|
27
|
+
new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
28
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
29
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
30
|
+
<T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
31
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
32
|
+
}
|
|
33
|
+
declare const Labels: $$IsomorphicComponent;
|
|
34
|
+
type Labels<T> = InstanceType<typeof Labels<T>>;
|
|
35
|
+
export default Labels;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import { getChartContext } from '../core/context.svelte.js';
|
|
4
|
+
import type { SeriesEntry } from '../core/context.svelte.js';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
/** Overlay position inside the chart container. Reserve space via Chart padding. */
|
|
8
|
+
position?: 'top' | 'bottom';
|
|
9
|
+
/** Replace the default item content (swatch + label). Toggle wiring stays. */
|
|
10
|
+
item?: Snippet<[SeriesEntry]>;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let { position = 'bottom', item, class: klass = '' }: Props = $props();
|
|
15
|
+
|
|
16
|
+
const ctx = getChartContext();
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
{#if ctx.seriesEntries.length > 0}
|
|
20
|
+
<div class="fc-legend fc-legend-{position} {klass}">
|
|
21
|
+
{#each ctx.seriesEntries as entry (entry.index)}
|
|
22
|
+
<button
|
|
23
|
+
type="button"
|
|
24
|
+
class="fc-legend-item"
|
|
25
|
+
class:fc-legend-hidden={!entry.visible}
|
|
26
|
+
aria-pressed={entry.visible}
|
|
27
|
+
onclick={() => ctx.toggleSeries(entry.index)}
|
|
28
|
+
>
|
|
29
|
+
{#if item}
|
|
30
|
+
{@render item(entry)}
|
|
31
|
+
{:else}
|
|
32
|
+
<span class="fc-legend-swatch" style="background: {entry.color}"></span>
|
|
33
|
+
<span class="fc-legend-label">{entry.name ?? `Series ${entry.index + 1}`}</span>
|
|
34
|
+
{/if}
|
|
35
|
+
</button>
|
|
36
|
+
{/each}
|
|
37
|
+
</div>
|
|
38
|
+
{/if}
|
|
39
|
+
|
|
40
|
+
<style>
|
|
41
|
+
.fc-legend {
|
|
42
|
+
position: absolute;
|
|
43
|
+
left: 0;
|
|
44
|
+
right: 0;
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-wrap: wrap;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
gap: 0.125rem 0.875rem;
|
|
49
|
+
font-family: var(--fc-font, ui-sans-serif, system-ui, sans-serif);
|
|
50
|
+
}
|
|
51
|
+
.fc-legend-bottom {
|
|
52
|
+
bottom: 0;
|
|
53
|
+
}
|
|
54
|
+
.fc-legend-top {
|
|
55
|
+
top: 0;
|
|
56
|
+
}
|
|
57
|
+
.fc-legend-item {
|
|
58
|
+
display: inline-flex;
|
|
59
|
+
align-items: center;
|
|
60
|
+
gap: 6px;
|
|
61
|
+
padding: 2px 4px;
|
|
62
|
+
border: none;
|
|
63
|
+
background: none;
|
|
64
|
+
cursor: pointer;
|
|
65
|
+
font: inherit;
|
|
66
|
+
font-size: 12px;
|
|
67
|
+
color: var(--fc-legend-text, var(--fc-axis-label, #64748b));
|
|
68
|
+
}
|
|
69
|
+
.fc-legend-hidden {
|
|
70
|
+
opacity: 0.45;
|
|
71
|
+
}
|
|
72
|
+
.fc-legend-hidden .fc-legend-label {
|
|
73
|
+
text-decoration: line-through;
|
|
74
|
+
}
|
|
75
|
+
.fc-legend-swatch {
|
|
76
|
+
width: 10px;
|
|
77
|
+
height: 10px;
|
|
78
|
+
border-radius: 3px;
|
|
79
|
+
flex: none;
|
|
80
|
+
}
|
|
81
|
+
</style>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { SeriesEntry } from '../core/context.svelte.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Overlay position inside the chart container. Reserve space via Chart padding. */
|
|
5
|
+
position?: 'top' | 'bottom';
|
|
6
|
+
/** Replace the default item content (swatch + label). Toggle wiring stays. */
|
|
7
|
+
item?: Snippet<[SeriesEntry]>;
|
|
8
|
+
class?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const Legend: import("svelte").Component<Props, {}, "">;
|
|
11
|
+
type Legend = ReturnType<typeof Legend>;
|
|
12
|
+
export default Legend;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<script lang="ts" generics="T">
|
|
2
|
+
import { onDestroy, onMount } from 'svelte';
|
|
3
|
+
import { line as d3line } from 'd3-shape';
|
|
4
|
+
import { getChartContext } from '../core/context.svelte.js';
|
|
5
|
+
import { normalizePoints } from '../core/normalize.js';
|
|
6
|
+
import type { NormalizedPoint, XValue } from '../core/normalize.js';
|
|
7
|
+
import { curveFor } from '../core/curves.js';
|
|
8
|
+
import type { CurveName } from '../core/curves.js';
|
|
9
|
+
import { motionPath } from '../core/motion.svelte.js';
|
|
10
|
+
import { hitBisectX } from '../core/hit.js';
|
|
11
|
+
import { symbolFor, symbolByIndex, symbolPath } from '../core/symbols.js';
|
|
12
|
+
import type { SymbolName } from '../core/symbols.js';
|
|
13
|
+
import type { SymbolType } from 'd3-shape';
|
|
14
|
+
import type { MarkerMode } from '../core/options.js';
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
data: readonly T[];
|
|
18
|
+
x?: (datum: T, index: number) => XValue;
|
|
19
|
+
y?: (datum: T, index: number) => number | null | undefined;
|
|
20
|
+
color?: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
describePoint?: (point: NormalizedPoint<T>) => string | undefined;
|
|
24
|
+
curve?: CurveName;
|
|
25
|
+
strokeWidth?: number;
|
|
26
|
+
index?: number;
|
|
27
|
+
/** Marker visibility: 'always' | 'hover' | 'none'. */
|
|
28
|
+
markers?: MarkerMode;
|
|
29
|
+
/** Symbol shape for markers. */
|
|
30
|
+
symbol?: SymbolName | SymbolType;
|
|
31
|
+
/** Symbol area in px² (default 64). */
|
|
32
|
+
symbolSize?: number;
|
|
33
|
+
class?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let {
|
|
37
|
+
data,
|
|
38
|
+
x,
|
|
39
|
+
y,
|
|
40
|
+
color,
|
|
41
|
+
name,
|
|
42
|
+
description,
|
|
43
|
+
describePoint,
|
|
44
|
+
curve = 'linear',
|
|
45
|
+
strokeWidth = 2,
|
|
46
|
+
index: indexProp,
|
|
47
|
+
markers = 'none',
|
|
48
|
+
symbol: symbolProp,
|
|
49
|
+
symbolSize = 64,
|
|
50
|
+
class: klass = ''
|
|
51
|
+
}: Props = $props();
|
|
52
|
+
|
|
53
|
+
const ctx = getChartContext();
|
|
54
|
+
|
|
55
|
+
const points = $derived(normalizePoints(data, { x, y }));
|
|
56
|
+
const registration = ctx.registerSeries(() => ({
|
|
57
|
+
points,
|
|
58
|
+
name,
|
|
59
|
+
color: resolvedColor,
|
|
60
|
+
description,
|
|
61
|
+
describePoint: describePoint as ((p: NormalizedPoint<unknown>) => string | undefined) | undefined
|
|
62
|
+
}));
|
|
63
|
+
onDestroy(registration.unregister);
|
|
64
|
+
|
|
65
|
+
const seriesIndex = $derived(indexProp ?? registration.index);
|
|
66
|
+
const resolvedColor = $derived(ctx.seriesColor(seriesIndex, color));
|
|
67
|
+
const visible = $derived(ctx.isSeriesVisible(registration.index));
|
|
68
|
+
|
|
69
|
+
const path = $derived.by(() => {
|
|
70
|
+
if (!visible) return '';
|
|
71
|
+
const generator = d3line<NormalizedPoint<T>>()
|
|
72
|
+
.defined((p) => p.y != null && Number.isFinite(ctx.xPos(p.x)))
|
|
73
|
+
.x((p) => ctx.xPos(p.x))
|
|
74
|
+
.y((p) => ctx.yPos(p.y as number))
|
|
75
|
+
.curve(curveFor(curve));
|
|
76
|
+
return generator(points) ?? '';
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// --- markers ---
|
|
80
|
+
const resolvedSymbol = $derived(
|
|
81
|
+
symbolProp ? symbolFor(symbolProp) : symbolByIndex(seriesIndex)
|
|
82
|
+
);
|
|
83
|
+
const markerPathD = $derived(markers !== 'none' ? symbolPath(resolvedSymbol, symbolSize) : '');
|
|
84
|
+
|
|
85
|
+
interface Dot {
|
|
86
|
+
cx: number;
|
|
87
|
+
cy: number;
|
|
88
|
+
index: number;
|
|
89
|
+
}
|
|
90
|
+
const dots = $derived.by((): Dot[] => {
|
|
91
|
+
if (markers === 'none') return [];
|
|
92
|
+
const out: Dot[] = [];
|
|
93
|
+
for (const p of points) {
|
|
94
|
+
const cx = ctx.xPos(p.x);
|
|
95
|
+
if (p.y == null || !Number.isFinite(cx)) continue;
|
|
96
|
+
out.push({ cx, cy: ctx.yPos(p.y), index: p.index });
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const hoveredIndex = $derived.by(() => {
|
|
102
|
+
if (markers !== 'hover') return -1;
|
|
103
|
+
const ptr = ctx.hoverPointer;
|
|
104
|
+
if (!ptr) return -1;
|
|
105
|
+
const hit = hitBisectX(points, ctx.xPos, ctx.yPos, ptr.x);
|
|
106
|
+
return hit ? hit.point.index : -1;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
let rootEl: SVGGElement | undefined = $state();
|
|
110
|
+
let mounted = $state(false);
|
|
111
|
+
onMount(() => {
|
|
112
|
+
mounted = true;
|
|
113
|
+
});
|
|
114
|
+
const morph = motionPath(() => path, () => rootEl);
|
|
115
|
+
</script>
|
|
116
|
+
|
|
117
|
+
{#if visible}
|
|
118
|
+
<g class="fc-series {klass}" data-name={name} bind:this={rootEl}>
|
|
119
|
+
<path
|
|
120
|
+
class="fc-line"
|
|
121
|
+
class:fc-draw={mounted}
|
|
122
|
+
d={morph.d}
|
|
123
|
+
pathLength="1"
|
|
124
|
+
fill="none"
|
|
125
|
+
stroke={resolvedColor}
|
|
126
|
+
stroke-width={strokeWidth}
|
|
127
|
+
stroke-linecap="round"
|
|
128
|
+
stroke-linejoin="round"
|
|
129
|
+
/>
|
|
130
|
+
{#if markers !== 'none'}
|
|
131
|
+
{#each dots as d (d.index)}
|
|
132
|
+
<g transform="translate({d.cx},{d.cy})">
|
|
133
|
+
<path
|
|
134
|
+
class="fc-marker"
|
|
135
|
+
class:fc-marker-active={markers === 'always' || d.index === hoveredIndex}
|
|
136
|
+
d={markerPathD}
|
|
137
|
+
fill={resolvedColor}
|
|
138
|
+
/>
|
|
139
|
+
</g>
|
|
140
|
+
{/each}
|
|
141
|
+
{/if}
|
|
142
|
+
</g>
|
|
143
|
+
{/if}
|
|
144
|
+
|
|
145
|
+
<style>
|
|
146
|
+
.fc-line {
|
|
147
|
+
transition: opacity var(--fc-duration-hover, 120ms) var(--fc-ease, ease);
|
|
148
|
+
}
|
|
149
|
+
.fc-draw {
|
|
150
|
+
stroke-dasharray: 1 1;
|
|
151
|
+
animation: fc-draw-on var(--fc-duration, 500ms) var(--fc-ease, ease) both;
|
|
152
|
+
}
|
|
153
|
+
@keyframes fc-draw-on {
|
|
154
|
+
from {
|
|
155
|
+
stroke-dashoffset: 1;
|
|
156
|
+
}
|
|
157
|
+
to {
|
|
158
|
+
stroke-dashoffset: 0;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
:global(.fc-svg:has(.fc-series:hover)) .fc-series:not(:hover) .fc-line {
|
|
162
|
+
opacity: 0.35;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* markers */
|
|
166
|
+
.fc-marker {
|
|
167
|
+
opacity: 0;
|
|
168
|
+
transform: scale(0.4);
|
|
169
|
+
transform-box: fill-box;
|
|
170
|
+
transform-origin: center;
|
|
171
|
+
transition:
|
|
172
|
+
opacity var(--fc-marker-out, 300ms) var(--fc-ease, ease),
|
|
173
|
+
transform var(--fc-marker-out, 300ms) var(--fc-ease, ease);
|
|
174
|
+
}
|
|
175
|
+
.fc-marker-active {
|
|
176
|
+
opacity: 1;
|
|
177
|
+
transform: scale(1);
|
|
178
|
+
transition:
|
|
179
|
+
opacity var(--fc-marker-in, 250ms) var(--fc-ease, ease),
|
|
180
|
+
transform var(--fc-marker-in, 250ms) var(--fc-ease, ease);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
@media (prefers-reduced-motion: reduce) {
|
|
184
|
+
.fc-draw {
|
|
185
|
+
animation: none;
|
|
186
|
+
}
|
|
187
|
+
.fc-line,
|
|
188
|
+
.fc-marker {
|
|
189
|
+
transition: none;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
</style>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { NormalizedPoint, XValue } from '../core/normalize.js';
|
|
2
|
+
import type { CurveName } from '../core/curves.js';
|
|
3
|
+
import type { SymbolName } from '../core/symbols.js';
|
|
4
|
+
import type { SymbolType } from 'd3-shape';
|
|
5
|
+
import type { MarkerMode } from '../core/options.js';
|
|
6
|
+
declare function $$render<T>(): {
|
|
7
|
+
props: {
|
|
8
|
+
data: readonly T[];
|
|
9
|
+
x?: (datum: T, index: number) => XValue;
|
|
10
|
+
y?: (datum: T, index: number) => number | null | undefined;
|
|
11
|
+
color?: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
description?: string;
|
|
14
|
+
describePoint?: (point: NormalizedPoint<T>) => string | undefined;
|
|
15
|
+
curve?: CurveName;
|
|
16
|
+
strokeWidth?: number;
|
|
17
|
+
index?: number;
|
|
18
|
+
/** Marker visibility: 'always' | 'hover' | 'none'. */
|
|
19
|
+
markers?: MarkerMode;
|
|
20
|
+
/** Symbol shape for markers. */
|
|
21
|
+
symbol?: SymbolName | SymbolType;
|
|
22
|
+
/** Symbol area in px² (default 64). */
|
|
23
|
+
symbolSize?: number;
|
|
24
|
+
class?: string;
|
|
25
|
+
};
|
|
26
|
+
exports: {};
|
|
27
|
+
bindings: "";
|
|
28
|
+
slots: {};
|
|
29
|
+
events: {};
|
|
30
|
+
};
|
|
31
|
+
declare class __sveltets_Render<T> {
|
|
32
|
+
props(): ReturnType<typeof $$render<T>>['props'];
|
|
33
|
+
events(): ReturnType<typeof $$render<T>>['events'];
|
|
34
|
+
slots(): ReturnType<typeof $$render<T>>['slots'];
|
|
35
|
+
bindings(): "";
|
|
36
|
+
exports(): {};
|
|
37
|
+
}
|
|
38
|
+
interface $$IsomorphicComponent {
|
|
39
|
+
new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
|
|
40
|
+
$$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
|
|
41
|
+
} & ReturnType<__sveltets_Render<T>['exports']>;
|
|
42
|
+
<T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
|
|
43
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
44
|
+
}
|
|
45
|
+
declare const Line: $$IsomorphicComponent;
|
|
46
|
+
type Line<T> = InstanceType<typeof Line<T>>;
|
|
47
|
+
export default Line;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getChartContext } from '../core/context.svelte.js';
|
|
3
|
+
import type { XValue } from '../core/normalize.js';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Which axis the band spans: 'y' (horizontal band, default) or 'x' (vertical band). */
|
|
7
|
+
axis?: 'x' | 'y';
|
|
8
|
+
from: XValue;
|
|
9
|
+
to: XValue;
|
|
10
|
+
/** Any CSS color string — passed through untouched. */
|
|
11
|
+
color?: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
class?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let { axis = 'y', from, to, color, label, class: klass = '' }: Props = $props();
|
|
17
|
+
|
|
18
|
+
const ctx = getChartContext();
|
|
19
|
+
|
|
20
|
+
const rect = $derived.by(() => {
|
|
21
|
+
if (axis === 'y') {
|
|
22
|
+
const a = ctx.yPos(from);
|
|
23
|
+
const b = ctx.yPos(to);
|
|
24
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return null;
|
|
25
|
+
const y = Math.max(0, Math.min(a, b));
|
|
26
|
+
const y2 = Math.min(ctx.innerHeight, Math.max(a, b));
|
|
27
|
+
if (y2 <= y) return null;
|
|
28
|
+
return { x: 0, y, width: ctx.innerWidth, height: y2 - y };
|
|
29
|
+
}
|
|
30
|
+
const a = ctx.xPos(from);
|
|
31
|
+
const b = ctx.xPos(to);
|
|
32
|
+
if (!Number.isFinite(a) || !Number.isFinite(b)) return null;
|
|
33
|
+
const x = Math.max(0, Math.min(a, b));
|
|
34
|
+
const x2 = Math.min(ctx.innerWidth, Math.max(a, b));
|
|
35
|
+
if (x2 <= x) return null;
|
|
36
|
+
return { x, y: 0, width: x2 - x, height: ctx.innerHeight };
|
|
37
|
+
});
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
{#if rect}
|
|
41
|
+
<g class="fc-plot-band {klass}">
|
|
42
|
+
<rect
|
|
43
|
+
x={rect.x}
|
|
44
|
+
y={rect.y}
|
|
45
|
+
width={rect.width}
|
|
46
|
+
height={rect.height}
|
|
47
|
+
fill={color ?? 'var(--fc-band, rgba(148, 163, 184, 0.18))'}
|
|
48
|
+
/>
|
|
49
|
+
{#if label}
|
|
50
|
+
{#if axis === 'y'}
|
|
51
|
+
<text class="fc-plot-band-label" x={rect.x + rect.width - 6} y={rect.y + 14} text-anchor="end">
|
|
52
|
+
{label}
|
|
53
|
+
</text>
|
|
54
|
+
{:else}
|
|
55
|
+
<text class="fc-plot-band-label" x={rect.x + 4} y={12} text-anchor="start">
|
|
56
|
+
{label}
|
|
57
|
+
</text>
|
|
58
|
+
{/if}
|
|
59
|
+
{/if}
|
|
60
|
+
</g>
|
|
61
|
+
{/if}
|
|
62
|
+
|
|
63
|
+
<style>
|
|
64
|
+
.fc-plot-band-label {
|
|
65
|
+
fill: var(--fc-axis-label, #64748b);
|
|
66
|
+
font-size: 11px;
|
|
67
|
+
}
|
|
68
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { XValue } from '../core/normalize.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Which axis the band spans: 'y' (horizontal band, default) or 'x' (vertical band). */
|
|
4
|
+
axis?: 'x' | 'y';
|
|
5
|
+
from: XValue;
|
|
6
|
+
to: XValue;
|
|
7
|
+
/** Any CSS color string — passed through untouched. */
|
|
8
|
+
color?: string;
|
|
9
|
+
label?: string;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const PlotBand: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type PlotBand = ReturnType<typeof PlotBand>;
|
|
14
|
+
export default PlotBand;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { getChartContext } from '../core/context.svelte.js';
|
|
3
|
+
import type { XValue } from '../core/normalize.js';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
/** Which axis the value lives on: 'y' (horizontal line, default) or 'x' (vertical line). */
|
|
7
|
+
axis?: 'x' | 'y';
|
|
8
|
+
value: XValue;
|
|
9
|
+
/** Any CSS color string — passed through untouched. */
|
|
10
|
+
color?: string;
|
|
11
|
+
/** stroke-dasharray, e.g. "4 3". Solid when omitted. */
|
|
12
|
+
dash?: string;
|
|
13
|
+
width?: number;
|
|
14
|
+
label?: string;
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let { axis = 'y', value, color, dash, width = 1, label, class: klass = '' }: Props = $props();
|
|
19
|
+
|
|
20
|
+
const ctx = getChartContext();
|
|
21
|
+
|
|
22
|
+
const p = $derived(axis === 'y' ? ctx.yPos(value) : ctx.xPos(value));
|
|
23
|
+
const visible = $derived(
|
|
24
|
+
Number.isFinite(p) && p >= 0 && p <= (axis === 'y' ? ctx.innerHeight : ctx.innerWidth)
|
|
25
|
+
);
|
|
26
|
+
const stroke = $derived(color ?? 'var(--fc-plot-line, var(--fc-axis, #94a3b8))');
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
{#if visible}
|
|
30
|
+
<g class="fc-plot-line {klass}">
|
|
31
|
+
{#if axis === 'y'}
|
|
32
|
+
<line x1="0" x2={ctx.innerWidth} y1={p} y2={p} {stroke} stroke-width={width} stroke-dasharray={dash} />
|
|
33
|
+
{#if label}
|
|
34
|
+
<text class="fc-plot-line-label" x={ctx.innerWidth - 6} y={p - 5} text-anchor="end">
|
|
35
|
+
{label}
|
|
36
|
+
</text>
|
|
37
|
+
{/if}
|
|
38
|
+
{:else}
|
|
39
|
+
<line x1={p} x2={p} y1="0" y2={ctx.innerHeight} {stroke} stroke-width={width} stroke-dasharray={dash} />
|
|
40
|
+
{#if label}
|
|
41
|
+
<text class="fc-plot-line-label" x={p + 5} y={12} text-anchor="start">
|
|
42
|
+
{label}
|
|
43
|
+
</text>
|
|
44
|
+
{/if}
|
|
45
|
+
{/if}
|
|
46
|
+
</g>
|
|
47
|
+
{/if}
|
|
48
|
+
|
|
49
|
+
<style>
|
|
50
|
+
.fc-plot-line-label {
|
|
51
|
+
fill: var(--fc-axis-label, #64748b);
|
|
52
|
+
font-size: 11px;
|
|
53
|
+
}
|
|
54
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { XValue } from '../core/normalize.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Which axis the value lives on: 'y' (horizontal line, default) or 'x' (vertical line). */
|
|
4
|
+
axis?: 'x' | 'y';
|
|
5
|
+
value: XValue;
|
|
6
|
+
/** Any CSS color string — passed through untouched. */
|
|
7
|
+
color?: string;
|
|
8
|
+
/** stroke-dasharray, e.g. "4 3". Solid when omitted. */
|
|
9
|
+
dash?: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
label?: string;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
declare const PlotLine: import("svelte").Component<Props, {}, "">;
|
|
15
|
+
type PlotLine = ReturnType<typeof PlotLine>;
|
|
16
|
+
export default PlotLine;
|