@archetypeai/ds-ui-svelte-labs 0.7.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 +21 -0
- package/README.md +120 -0
- package/dist/primitives/aspect-ratio/aspect-ratio.svelte +8 -0
- package/dist/primitives/aspect-ratio/aspect-ratio.svelte.d.ts +4 -0
- package/dist/primitives/aspect-ratio/index.d.ts +4 -0
- package/dist/primitives/aspect-ratio/index.js +6 -0
- package/dist/primitives/aspect-ratio/types.d.ts +2 -0
- package/dist/primitives/aspect-ratio/types.js +1 -0
- package/dist/primitives/chart/chart-container.svelte +36 -0
- package/dist/primitives/chart/chart-container.svelte.d.ts +4 -0
- package/dist/primitives/chart/chart-style.svelte +37 -0
- package/dist/primitives/chart/chart-style.svelte.d.ts +8 -0
- package/dist/primitives/chart/chart-tooltip.svelte +136 -0
- package/dist/primitives/chart/chart-tooltip.svelte.d.ts +4 -0
- package/dist/primitives/chart/chart-utils.d.ts +45 -0
- package/dist/primitives/chart/chart-utils.js +52 -0
- package/dist/primitives/chart/index.d.ts +7 -0
- package/dist/primitives/chart/index.js +7 -0
- package/dist/primitives/chart/scatter-tooltip.svelte +40 -0
- package/dist/primitives/chart/scatter-tooltip.svelte.d.ts +4 -0
- package/dist/primitives/chart/types.d.ts +33 -0
- package/dist/primitives/chart/types.js +45 -0
- package/dist/primitives/kbd/index.d.ts +5 -0
- package/dist/primitives/kbd/index.js +7 -0
- package/dist/primitives/kbd/kbd-group.svelte +10 -0
- package/dist/primitives/kbd/kbd-group.svelte.d.ts +4 -0
- package/dist/primitives/kbd/kbd.svelte +10 -0
- package/dist/primitives/kbd/kbd.svelte.d.ts +4 -0
- package/dist/primitives/kbd/types.d.ts +6 -0
- package/dist/primitives/kbd/types.js +8 -0
- package/dist/primitives/logo/index.d.ts +4 -0
- package/dist/primitives/logo/index.js +6 -0
- package/dist/primitives/logo/logo.svelte +22 -0
- package/dist/primitives/logo/logo.svelte.d.ts +4 -0
- package/dist/primitives/logo/types.d.ts +25 -0
- package/dist/primitives/logo/types.js +14 -0
- package/dist/primitives/menubar/index.d.ts +4 -0
- package/dist/primitives/menubar/index.js +6 -0
- package/dist/primitives/menubar/menubar.svelte +67 -0
- package/dist/primitives/menubar/menubar.svelte.d.ts +4 -0
- package/dist/primitives/menubar/types.d.ts +10 -0
- package/dist/primitives/menubar/types.js +5 -0
- package/dist/primitives/scatter-chart/index.d.ts +4 -0
- package/dist/primitives/scatter-chart/index.js +6 -0
- package/dist/primitives/scatter-chart/scatter-chart.svelte +147 -0
- package/dist/primitives/scatter-chart/scatter-chart.svelte.d.ts +5 -0
- package/dist/primitives/scatter-chart/types.d.ts +34 -0
- package/dist/primitives/scatter-chart/types.js +6 -0
- package/dist/primitives/sensor-chart/index.d.ts +4 -0
- package/dist/primitives/sensor-chart/index.js +6 -0
- package/dist/primitives/sensor-chart/sensor-chart.svelte +138 -0
- package/dist/primitives/sensor-chart/sensor-chart.svelte.d.ts +4 -0
- package/dist/primitives/sensor-chart/types.d.ts +27 -0
- package/dist/primitives/sensor-chart/types.js +6 -0
- package/dist/primitives/slider/index.d.ts +4 -0
- package/dist/primitives/slider/index.js +6 -0
- package/dist/primitives/slider/slider.svelte +47 -0
- package/dist/primitives/slider/slider.svelte.d.ts +4 -0
- package/dist/primitives/slider/types.d.ts +7 -0
- package/dist/primitives/slider/types.js +14 -0
- package/dist/primitives/switch/index.d.ts +4 -0
- package/dist/primitives/switch/index.js +6 -0
- package/dist/primitives/switch/switch.svelte +28 -0
- package/dist/primitives/switch/switch.svelte.d.ts +4 -0
- package/dist/primitives/switch/types.d.ts +45 -0
- package/dist/primitives/switch/types.js +27 -0
- package/dist/primitives/toggle/index.d.ts +4 -0
- package/dist/primitives/toggle/index.js +6 -0
- package/dist/primitives/toggle/toggle.svelte +24 -0
- package/dist/primitives/toggle/toggle.svelte.d.ts +4 -0
- package/dist/primitives/toggle/types.d.ts +39 -0
- package/dist/primitives/toggle/types.js +19 -0
- package/dist/primitives/utils.d.ts +4 -0
- package/dist/primitives/utils.js +29 -0
- package/dist/primitives/video-player/index.d.ts +4 -0
- package/dist/primitives/video-player/index.js +6 -0
- package/dist/primitives/video-player/types.d.ts +49 -0
- package/dist/primitives/video-player/types.js +32 -0
- package/dist/primitives/video-player/video-player.svelte +139 -0
- package/dist/primitives/video-player/video-player.svelte.d.ts +4 -0
- package/package.json +127 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Archetype AI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# @archetypeai/ds-ui-svelte-labs
|
|
2
|
+
|
|
3
|
+
Labs primitives of the Archetype AI design system - the **experimental 0.x tier** built on top of the frozen [`@archetypeai/ds-ui-svelte-console`](./DS-UI-SVELTE-CONSOLE.md) base. Svelte 5 primitives styled with Tailwind v4 and `tailwind-variants`, wired to the `@archetypeai/ds-lib-tokens` theme, composing console primitives (Button, Card, Badge) where they need stable building blocks. TypeScript source ships as `.svelte` files (no precompilation) so consumers get full type-checking and IDE go-to-definition.
|
|
4
|
+
|
|
5
|
+
Labs is versioned 0.x: breaking changes are in-contract between minor versions. Components that prove themselves graduate toward console; console itself never changes to accommodate labs.
|
|
6
|
+
|
|
7
|
+
Like console, the labs flavor ships two ways from the same source:
|
|
8
|
+
|
|
9
|
+
- **npm package** (this document's default) - how apps **consume** primitives: they stay in `node_modules`, imported per-subpath, updated by version bump. The package is the source of truth.
|
|
10
|
+
- **shadcn-svelte registry** - how primitives get **modified or extended**: pull a primitive's editable source into your app, change it, then port the change back into the package.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm i @archetypeai/ds-ui-svelte-labs @archetypeai/ds-ui-svelte-console
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Then install the peer dependencies shared by all subpaths:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm i svelte tailwindcss bits-ui tailwind-variants tailwind-merge clsx @lucide/svelte @archetypeai/ds-lib-tokens
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Per-subpath peer dependencies
|
|
25
|
+
|
|
26
|
+
The chart-family subpaths additionally require heavy visualization peers that the other subpaths never load (they are declared as optional peers, so npm does not auto-install them):
|
|
27
|
+
|
|
28
|
+
| Subpath | Extra peers |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `primitives/chart` | `layerchart` |
|
|
31
|
+
| `primitives/scatter-chart` | `layerchart`, `d3-scale` |
|
|
32
|
+
| `primitives/sensor-chart` | `layerchart`, `d3-scale`, `d3-shape` |
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
npm i layerchart@2.0.0-next.46 d3-scale d3-shape
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`layerchart` is pinned to an exact 2.0 pre-release - prerelease versions break APIs between builds, so install the pinned version (labs is 0.x - churn here is in-contract).
|
|
39
|
+
|
|
40
|
+
## Tailwind v4 setup (mandatory)
|
|
41
|
+
|
|
42
|
+
Tailwind v4 does not scan `node_modules` by default. Without the `@source` directives below, every class in this package is purged at consumer build time and components render unstyled.
|
|
43
|
+
|
|
44
|
+
In your consumer's `app.css`, in this order:
|
|
45
|
+
|
|
46
|
+
```css
|
|
47
|
+
@import 'tailwindcss';
|
|
48
|
+
@import '@archetypeai/ds-lib-tokens/theme.css';
|
|
49
|
+
@source "../node_modules/@archetypeai/ds-ui-svelte-console/dist";
|
|
50
|
+
@source "../node_modules/@archetypeai/ds-ui-svelte-labs/dist";
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Order matters:
|
|
54
|
+
|
|
55
|
+
- `tailwindcss` registers the engine.
|
|
56
|
+
- `@archetypeai/ds-lib-tokens/theme.css` declares the CSS variables that component classes consume.
|
|
57
|
+
- The two `@source` directives make Tailwind scan both packages - labs components compose console primitives, so both dists must be scanned.
|
|
58
|
+
|
|
59
|
+
Adjust the `@source` paths if your `app.css` lives elsewhere - they must resolve to each package's `dist/` directory.
|
|
60
|
+
|
|
61
|
+
## TypeScript setup
|
|
62
|
+
|
|
63
|
+
In your consumer's `tsconfig.json` (or `jsconfig.json`), set:
|
|
64
|
+
|
|
65
|
+
```jsonc
|
|
66
|
+
{
|
|
67
|
+
"compilerOptions": {
|
|
68
|
+
"moduleResolution": "bundler"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`"node16"` and `"nodenext"` also work. Without one of these, the `types` condition in this package's `exports` map is bypassed, types fall back to the `default` JS file, and editor features degrade.
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
Each primitive lives at its own subpath. There is no barrel `import { Logo } from '@archetypeai/ds-ui-svelte-labs'` - subpaths keep `layerchart` and the d3 packages out of bundles for consumers who never touch the chart primitives.
|
|
78
|
+
|
|
79
|
+
```svelte
|
|
80
|
+
<script lang="ts">
|
|
81
|
+
import Logo from '@archetypeai/ds-ui-svelte-labs/primitives/logo'
|
|
82
|
+
import Menubar from '@archetypeai/ds-ui-svelte-labs/primitives/menubar'
|
|
83
|
+
import Slider from '@archetypeai/ds-ui-svelte-labs/primitives/slider'
|
|
84
|
+
|
|
85
|
+
let volume = $state(50)
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<Menubar />
|
|
89
|
+
<Logo size="lg" />
|
|
90
|
+
<Slider type="single" bind:value={volume} max={100} />
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Per-primitive subpaths
|
|
94
|
+
|
|
95
|
+
| Primitive | Import path |
|
|
96
|
+
|---|---|
|
|
97
|
+
| AspectRatio | `@archetypeai/ds-ui-svelte-labs/primitives/aspect-ratio` |
|
|
98
|
+
| Chart | `@archetypeai/ds-ui-svelte-labs/primitives/chart` |
|
|
99
|
+
| Kbd | `@archetypeai/ds-ui-svelte-labs/primitives/kbd` |
|
|
100
|
+
| Logo | `@archetypeai/ds-ui-svelte-labs/primitives/logo` |
|
|
101
|
+
| Menubar | `@archetypeai/ds-ui-svelte-labs/primitives/menubar` |
|
|
102
|
+
| ScatterChart | `@archetypeai/ds-ui-svelte-labs/primitives/scatter-chart` |
|
|
103
|
+
| SensorChart | `@archetypeai/ds-ui-svelte-labs/primitives/sensor-chart` |
|
|
104
|
+
| Slider | `@archetypeai/ds-ui-svelte-labs/primitives/slider` |
|
|
105
|
+
| Switch | `@archetypeai/ds-ui-svelte-labs/primitives/switch` |
|
|
106
|
+
| Toggle | `@archetypeai/ds-ui-svelte-labs/primitives/toggle` |
|
|
107
|
+
| VideoPlayer | `@archetypeai/ds-ui-svelte-labs/primitives/video-player` |
|
|
108
|
+
|
|
109
|
+
Class-merging and element-ref type helpers re-export from console at `@archetypeai/ds-ui-svelte-labs/primitives/utils` (a single `cn`/`twMerge` instance exists in the tree), alongside the labs domain helpers (`healthTier`, `formatMMSS`, `clamp`).
|
|
110
|
+
|
|
111
|
+
## Extending console primitives
|
|
112
|
+
|
|
113
|
+
Labs adapts console primitives without ever changing console, using this escape-hatch ladder (in order):
|
|
114
|
+
|
|
115
|
+
1. `class` prop overrides on the console component
|
|
116
|
+
2. `data-slot` attribute selectors in consumer CSS
|
|
117
|
+
3. `tv({ extend })` on console's exported variant clusters
|
|
118
|
+
4. (rare) a labs wrapper component
|
|
119
|
+
|
|
120
|
+
This document is the source for the package README (copied by `prepack`); the full design-system docs live in the repository's [readme](https://github.com/archetypeai/design-system).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { AspectRatio as AspectRatioPrimitive } from 'bits-ui';
|
|
3
|
+
import type { AspectRatioProps } from './types.js';
|
|
4
|
+
|
|
5
|
+
let { ref = $bindable(null), ...restProps }: AspectRatioProps = $props();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<AspectRatioPrimitive.Root bind:ref data-slot="aspect-ratio" {...restProps} />
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../utils.js';
|
|
3
|
+
import ChartStyle from './chart-style.svelte';
|
|
4
|
+
import { setChartContext } from './chart-utils.js';
|
|
5
|
+
import { chartContainerVariants, type ChartContainerProps } from './types.js';
|
|
6
|
+
|
|
7
|
+
const uid = $props.id();
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
ref = $bindable(null),
|
|
11
|
+
id = uid,
|
|
12
|
+
class: className,
|
|
13
|
+
children,
|
|
14
|
+
config,
|
|
15
|
+
...restProps
|
|
16
|
+
}: ChartContainerProps = $props();
|
|
17
|
+
|
|
18
|
+
let chartId = $derived(`chart-${id || uid.replace(/:/g, '')}`);
|
|
19
|
+
|
|
20
|
+
setChartContext({
|
|
21
|
+
get config() {
|
|
22
|
+
return config;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<div
|
|
28
|
+
bind:this={ref}
|
|
29
|
+
data-chart={chartId}
|
|
30
|
+
data-slot="chart"
|
|
31
|
+
class={cn(chartContainerVariants(), className)}
|
|
32
|
+
{...restProps}
|
|
33
|
+
>
|
|
34
|
+
<ChartStyle id={chartId} {config} />
|
|
35
|
+
{@render children?.()}
|
|
36
|
+
</div>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { THEMES, type ChartConfig } from './chart-utils.js';
|
|
3
|
+
|
|
4
|
+
let { id, config }: { id: string; config: ChartConfig } = $props();
|
|
5
|
+
|
|
6
|
+
const colorConfig = $derived(
|
|
7
|
+
config ? Object.entries(config).filter(([, config]) => config.theme || config.color) : null
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
const themeContents = $derived.by(() => {
|
|
11
|
+
if (!colorConfig || !colorConfig.length) return;
|
|
12
|
+
|
|
13
|
+
const themeContents = [];
|
|
14
|
+
for (let [_theme, prefix] of Object.entries(THEMES)) {
|
|
15
|
+
let content = `${prefix} [data-chart=${id}] {\n`;
|
|
16
|
+
const color = colorConfig.map(([key, itemConfig]) => {
|
|
17
|
+
const theme = _theme as keyof typeof itemConfig.theme;
|
|
18
|
+
const color = itemConfig.theme?.[theme] || itemConfig.color;
|
|
19
|
+
return color ? `\t--color-${key}: ${color};` : null;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
content += color.join('\n') + '\n}';
|
|
23
|
+
|
|
24
|
+
themeContents.push(content);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return themeContents.join('\n');
|
|
28
|
+
});
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
{#if themeContents}
|
|
32
|
+
{#key id}
|
|
33
|
+
<svelte:element this={'style'}>
|
|
34
|
+
{themeContents}
|
|
35
|
+
</svelte:element>
|
|
36
|
+
{/key}
|
|
37
|
+
{/if}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ChartConfig } from './chart-utils.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
id: string;
|
|
4
|
+
config: ChartConfig;
|
|
5
|
+
};
|
|
6
|
+
declare const ChartStyle: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
|
+
type ChartStyle = ReturnType<typeof ChartStyle>;
|
|
8
|
+
export default ChartStyle;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../utils.js';
|
|
3
|
+
import {
|
|
4
|
+
getPayloadConfigFromPayload,
|
|
5
|
+
getChartContext,
|
|
6
|
+
type TooltipPayload
|
|
7
|
+
} from './chart-utils.js';
|
|
8
|
+
import { chartTooltipVariants, type ChartTooltipProps } from './types.js';
|
|
9
|
+
import { getTooltipContext, Tooltip as TooltipPrimitive } from 'layerchart';
|
|
10
|
+
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
function defaultFormatter(value: any, _payload: TooltipPayload[]) {
|
|
13
|
+
return `${value}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let {
|
|
17
|
+
ref = $bindable(null),
|
|
18
|
+
class: className,
|
|
19
|
+
hideLabel = false,
|
|
20
|
+
indicator = 'dot',
|
|
21
|
+
hideIndicator = false,
|
|
22
|
+
labelKey,
|
|
23
|
+
label,
|
|
24
|
+
labelFormatter = defaultFormatter,
|
|
25
|
+
labelClassName,
|
|
26
|
+
formatter,
|
|
27
|
+
nameKey,
|
|
28
|
+
color,
|
|
29
|
+
...restProps
|
|
30
|
+
}: ChartTooltipProps = $props();
|
|
31
|
+
|
|
32
|
+
const chart = getChartContext();
|
|
33
|
+
const tooltipCtx = getTooltipContext();
|
|
34
|
+
|
|
35
|
+
const formattedLabel = $derived.by(() => {
|
|
36
|
+
if (hideLabel || !tooltipCtx.payload?.length) return null;
|
|
37
|
+
|
|
38
|
+
const [item] = tooltipCtx.payload;
|
|
39
|
+
const key = labelKey ?? item?.label ?? item?.name ?? 'value';
|
|
40
|
+
|
|
41
|
+
const itemConfig = getPayloadConfigFromPayload(chart.config, item, key);
|
|
42
|
+
|
|
43
|
+
const value =
|
|
44
|
+
!labelKey && typeof label === 'string'
|
|
45
|
+
? (chart.config[label]?.label ?? label)
|
|
46
|
+
: (itemConfig?.label ?? item.label);
|
|
47
|
+
|
|
48
|
+
if (value === undefined) return null;
|
|
49
|
+
if (!labelFormatter) return value;
|
|
50
|
+
return labelFormatter(value, tooltipCtx.payload);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const nestLabel = $derived(tooltipCtx.payload.length === 1 && indicator !== 'dot');
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
{#snippet TooltipLabel()}
|
|
57
|
+
{#if formattedLabel}
|
|
58
|
+
<div class={cn('font-medium', labelClassName)}>
|
|
59
|
+
{#if typeof formattedLabel === 'function'}
|
|
60
|
+
{@render formattedLabel()}
|
|
61
|
+
{:else}
|
|
62
|
+
{formattedLabel}
|
|
63
|
+
{/if}
|
|
64
|
+
</div>
|
|
65
|
+
{/if}
|
|
66
|
+
{/snippet}
|
|
67
|
+
|
|
68
|
+
<TooltipPrimitive.Root variant="none">
|
|
69
|
+
<div
|
|
70
|
+
bind:this={ref}
|
|
71
|
+
data-slot="chart-tooltip"
|
|
72
|
+
class={cn(chartTooltipVariants(), className)}
|
|
73
|
+
{...restProps}
|
|
74
|
+
>
|
|
75
|
+
{#if !nestLabel}
|
|
76
|
+
{@render TooltipLabel()}
|
|
77
|
+
{/if}
|
|
78
|
+
<div class="grid gap-1.5">
|
|
79
|
+
{#each tooltipCtx.payload as item, i (item.key + i)}
|
|
80
|
+
{@const key = `${nameKey || item.key || item.name || 'value'}`}
|
|
81
|
+
{@const itemConfig = getPayloadConfigFromPayload(chart.config, item, key)}
|
|
82
|
+
{@const indicatorColor = color || item.payload?.color || item.color}
|
|
83
|
+
<div
|
|
84
|
+
class={cn(
|
|
85
|
+
'[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:size-2.5',
|
|
86
|
+
indicator === 'dot' && 'items-center'
|
|
87
|
+
)}
|
|
88
|
+
>
|
|
89
|
+
{#if formatter && item.value !== undefined && item.name}
|
|
90
|
+
{@render formatter({
|
|
91
|
+
value: item.value,
|
|
92
|
+
name: item.name,
|
|
93
|
+
item,
|
|
94
|
+
index: i,
|
|
95
|
+
payload: tooltipCtx.payload
|
|
96
|
+
})}
|
|
97
|
+
{:else}
|
|
98
|
+
{#if itemConfig?.icon}
|
|
99
|
+
<itemConfig.icon />
|
|
100
|
+
{:else if !hideIndicator}
|
|
101
|
+
<div
|
|
102
|
+
style="--color-bg: {indicatorColor}; --color-border: {indicatorColor};"
|
|
103
|
+
class={cn('border-border shrink-0 rounded-[2px] bg-(--color-bg)', {
|
|
104
|
+
'size-2.5': indicator === 'dot',
|
|
105
|
+
'h-full w-1': indicator === 'line',
|
|
106
|
+
'w-0 border-[1.5px] border-dashed bg-transparent': indicator === 'dashed',
|
|
107
|
+
'my-0.5': nestLabel && indicator === 'dashed'
|
|
108
|
+
})}
|
|
109
|
+
></div>
|
|
110
|
+
{/if}
|
|
111
|
+
<div
|
|
112
|
+
class={cn(
|
|
113
|
+
'flex flex-1 shrink-0 justify-between leading-none',
|
|
114
|
+
nestLabel ? 'items-end' : 'items-center'
|
|
115
|
+
)}
|
|
116
|
+
>
|
|
117
|
+
<div class="grid gap-1.5">
|
|
118
|
+
{#if nestLabel}
|
|
119
|
+
{@render TooltipLabel()}
|
|
120
|
+
{/if}
|
|
121
|
+
<span class="text-muted-foreground">
|
|
122
|
+
{itemConfig?.label || item.name}
|
|
123
|
+
</span>
|
|
124
|
+
</div>
|
|
125
|
+
{#if item.value !== undefined}
|
|
126
|
+
<span class="text-foreground font-mono font-medium tabular-nums">
|
|
127
|
+
{item.value.toLocaleString()}
|
|
128
|
+
</span>
|
|
129
|
+
{/if}
|
|
130
|
+
</div>
|
|
131
|
+
{/if}
|
|
132
|
+
</div>
|
|
133
|
+
{/each}
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</TooltipPrimitive.Root>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Tooltip } from 'layerchart';
|
|
2
|
+
import { type Component, type ComponentProps, type Snippet } from 'svelte';
|
|
3
|
+
export declare const THEMES: {
|
|
4
|
+
readonly light: "";
|
|
5
|
+
readonly dark: ".dark";
|
|
6
|
+
};
|
|
7
|
+
export declare const CHART_COLORS: readonly ["var(--chart-1)", "var(--chart-2)", "var(--chart-3)", "var(--chart-4)", "var(--chart-5)"];
|
|
8
|
+
export type ChartConfig = {
|
|
9
|
+
[k in string]: {
|
|
10
|
+
label?: string;
|
|
11
|
+
icon?: Component;
|
|
12
|
+
} & ({
|
|
13
|
+
color?: string;
|
|
14
|
+
theme?: never;
|
|
15
|
+
} | {
|
|
16
|
+
color?: never;
|
|
17
|
+
theme: Record<keyof typeof THEMES, string>;
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
export type ChartSeries = {
|
|
21
|
+
key: string;
|
|
22
|
+
label?: string;
|
|
23
|
+
color?: string;
|
|
24
|
+
};
|
|
25
|
+
export type ChartAxis = 'both' | 'x' | 'y' | 'none';
|
|
26
|
+
export type ExtractSnippetParams<T> = T extends Snippet<[infer P]> ? P : never;
|
|
27
|
+
export type TooltipPayload = ExtractSnippetParams<ComponentProps<typeof Tooltip.Root>['children']>['payload'][number];
|
|
28
|
+
export declare function getPayloadConfigFromPayload(config: ChartConfig, payload: TooltipPayload, key: string): ({
|
|
29
|
+
label?: string;
|
|
30
|
+
icon?: Component;
|
|
31
|
+
} & ({
|
|
32
|
+
color?: string;
|
|
33
|
+
theme?: never;
|
|
34
|
+
} | {
|
|
35
|
+
color?: never;
|
|
36
|
+
theme: Record<keyof typeof THEMES, string>;
|
|
37
|
+
})) | undefined;
|
|
38
|
+
type ChartContextValue = {
|
|
39
|
+
config: ChartConfig;
|
|
40
|
+
};
|
|
41
|
+
export declare function setChartContext(value: ChartContextValue): ChartContextValue;
|
|
42
|
+
export declare function getChartContext(): ChartContextValue;
|
|
43
|
+
export declare function buildChartConfig(series: ChartSeries[]): ChartConfig;
|
|
44
|
+
export declare function slidingWindow<T>(data: T[], maxPoints?: number): T[];
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { getContext, setContext } from 'svelte';
|
|
2
|
+
// constants: themes for chart colors
|
|
3
|
+
export const THEMES = { light: '', dark: '.dark' };
|
|
4
|
+
// constants: chart colors
|
|
5
|
+
export const CHART_COLORS = [
|
|
6
|
+
'var(--chart-1)',
|
|
7
|
+
'var(--chart-2)',
|
|
8
|
+
'var(--chart-3)',
|
|
9
|
+
'var(--chart-4)',
|
|
10
|
+
'var(--chart-5)'
|
|
11
|
+
];
|
|
12
|
+
// utility: extracts item config from payload
|
|
13
|
+
export function getPayloadConfigFromPayload(config, payload, key) {
|
|
14
|
+
if (typeof payload !== 'object' || payload === null)
|
|
15
|
+
return undefined;
|
|
16
|
+
const payloadPayload = 'payload' in payload && typeof payload.payload === 'object' && payload.payload !== null
|
|
17
|
+
? payload.payload
|
|
18
|
+
: undefined;
|
|
19
|
+
let configLabelKey = key;
|
|
20
|
+
if (payload.key === key) {
|
|
21
|
+
configLabelKey = payload.key;
|
|
22
|
+
}
|
|
23
|
+
else if (payload.name === key) {
|
|
24
|
+
configLabelKey = payload.name;
|
|
25
|
+
}
|
|
26
|
+
else if (key in payload && typeof payload[key] === 'string') {
|
|
27
|
+
configLabelKey = payload[key];
|
|
28
|
+
}
|
|
29
|
+
else if (payloadPayload !== undefined &&
|
|
30
|
+
key in payloadPayload &&
|
|
31
|
+
typeof payloadPayload[key] === 'string') {
|
|
32
|
+
configLabelKey = payloadPayload[key];
|
|
33
|
+
}
|
|
34
|
+
return configLabelKey in config ? config[configLabelKey] : config[key];
|
|
35
|
+
}
|
|
36
|
+
const chartContextKey = Symbol('chart-context');
|
|
37
|
+
// utility: sets chart context
|
|
38
|
+
export function setChartContext(value) {
|
|
39
|
+
return setContext(chartContextKey, value);
|
|
40
|
+
}
|
|
41
|
+
// utility: gets chart context
|
|
42
|
+
export function getChartContext() {
|
|
43
|
+
return getContext(chartContextKey);
|
|
44
|
+
}
|
|
45
|
+
// utility: builds chart config
|
|
46
|
+
export function buildChartConfig(series) {
|
|
47
|
+
return Object.fromEntries(series.map((s) => [s.key, { label: s.label, color: s.color }]));
|
|
48
|
+
}
|
|
49
|
+
// utility: creates a sliding window of data
|
|
50
|
+
export function slidingWindow(data, maxPoints) {
|
|
51
|
+
return maxPoints && data.length > maxPoints ? data.slice(-maxPoints) : data;
|
|
52
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import ChartContainer from './chart-container.svelte';
|
|
2
|
+
import ChartTooltip from './chart-tooltip.svelte';
|
|
3
|
+
import ScatterTooltip from './scatter-tooltip.svelte';
|
|
4
|
+
export { getPayloadConfigFromPayload, type ChartAxis, type ChartConfig, type ChartSeries, type TooltipPayload } from './chart-utils.js';
|
|
5
|
+
export { chartContainerVariants, chartTooltipVariants, scatterTooltipVariants, type ChartContainerProps, type ChartTooltipIndicator, type ChartTooltipProps, type ScatterTooltipProps } from './types.js';
|
|
6
|
+
export { ChartContainer, ChartTooltip, ScatterTooltip, ChartContainer as Container, ChartTooltip as Tooltip };
|
|
7
|
+
export default ChartContainer;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import ChartContainer from './chart-container.svelte';
|
|
2
|
+
import ChartTooltip from './chart-tooltip.svelte';
|
|
3
|
+
import ScatterTooltip from './scatter-tooltip.svelte';
|
|
4
|
+
export { getPayloadConfigFromPayload } from './chart-utils.js';
|
|
5
|
+
export { chartContainerVariants, chartTooltipVariants, scatterTooltipVariants } from './types.js';
|
|
6
|
+
export { ChartContainer, ChartTooltip, ScatterTooltip, ChartContainer as Container, ChartTooltip as Tooltip };
|
|
7
|
+
export default ChartContainer;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '../utils.js';
|
|
3
|
+
import { scatterTooltipVariants, type ScatterTooltipProps } from './types.js';
|
|
4
|
+
import { getTooltipContext, Tooltip } from 'layerchart';
|
|
5
|
+
|
|
6
|
+
let { class: className }: ScatterTooltipProps = $props();
|
|
7
|
+
|
|
8
|
+
const tooltipCtx = getTooltipContext();
|
|
9
|
+
|
|
10
|
+
// layerchart can provide data in different ways depending on chart type
|
|
11
|
+
const point = $derived.by(() => {
|
|
12
|
+
// try direct data property first
|
|
13
|
+
if (tooltipCtx.data?.x !== undefined) return tooltipCtx.data;
|
|
14
|
+
// try payload array (like other charts use)
|
|
15
|
+
if (tooltipCtx.payload?.[0]?.payload) return tooltipCtx.payload[0].payload;
|
|
16
|
+
if (tooltipCtx.payload?.[0]) return tooltipCtx.payload[0];
|
|
17
|
+
return null;
|
|
18
|
+
});
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<Tooltip.Root variant="none">
|
|
22
|
+
<div data-slot="scatter-tooltip" class={cn(scatterTooltipVariants(), className)}>
|
|
23
|
+
{#if point}
|
|
24
|
+
<div class="flex items-center justify-between gap-4">
|
|
25
|
+
<span class="text-muted-foreground">x</span>
|
|
26
|
+
<span class="text-foreground font-mono font-medium tabular-nums"
|
|
27
|
+
>{point.x?.toFixed(3) ?? '–'}</span
|
|
28
|
+
>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="flex items-center justify-between gap-4">
|
|
31
|
+
<span class="text-muted-foreground">y</span>
|
|
32
|
+
<span class="text-foreground font-mono font-medium tabular-nums"
|
|
33
|
+
>{point.y?.toFixed(3) ?? '–'}</span
|
|
34
|
+
>
|
|
35
|
+
</div>
|
|
36
|
+
{:else}
|
|
37
|
+
<span class="text-muted-foreground">no data</span>
|
|
38
|
+
{/if}
|
|
39
|
+
</div>
|
|
40
|
+
</Tooltip.Root>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type WithElementRef, type WithoutChildren } from '../utils.js';
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import type { ChartConfig, TooltipPayload } from './chart-utils.js';
|
|
5
|
+
export declare const chartContainerVariants: import("tailwind-variants").TVReturnType<{} | {} | {}, undefined, string[], {} | {}, undefined, import("tailwind-variants").TVReturnType<unknown, undefined, string[], unknown, unknown, undefined>>;
|
|
6
|
+
export declare const chartTooltipVariants: import("tailwind-variants").TVReturnType<{} | {} | {}, undefined, "border-border/50 bg-background grid min-w-36 items-start gap-1.5 rounded-xs border px-2.5 py-1.5 text-xs shadow-xl", {} | {}, undefined, import("tailwind-variants").TVReturnType<unknown, undefined, "border-border/50 bg-background grid min-w-36 items-start gap-1.5 rounded-xs border px-2.5 py-1.5 text-xs shadow-xl", unknown, unknown, undefined>>;
|
|
7
|
+
export declare const scatterTooltipVariants: import("tailwind-variants").TVReturnType<{} | {} | {}, undefined, "border-border/50 bg-background grid min-w-32 items-start gap-1.5 rounded-xs border px-2.5 py-1.5 text-xs shadow-xl", {} | {}, undefined, import("tailwind-variants").TVReturnType<unknown, undefined, "border-border/50 bg-background grid min-w-32 items-start gap-1.5 rounded-xs border px-2.5 py-1.5 text-xs shadow-xl", unknown, unknown, undefined>>;
|
|
8
|
+
export type ChartContainerProps = WithElementRef<HTMLAttributes<HTMLElement>> & {
|
|
9
|
+
config: ChartConfig;
|
|
10
|
+
};
|
|
11
|
+
export type ChartTooltipIndicator = 'line' | 'dot' | 'dashed';
|
|
12
|
+
export type ChartTooltipProps = WithoutChildren<WithElementRef<HTMLAttributes<HTMLDivElement>>> & {
|
|
13
|
+
hideLabel?: boolean;
|
|
14
|
+
label?: string;
|
|
15
|
+
indicator?: ChartTooltipIndicator;
|
|
16
|
+
nameKey?: string;
|
|
17
|
+
labelKey?: string;
|
|
18
|
+
hideIndicator?: boolean;
|
|
19
|
+
labelClassName?: string;
|
|
20
|
+
labelFormatter?: ((value: any, payload: TooltipPayload[]) => string | number | Snippet) | null;
|
|
21
|
+
formatter?: Snippet<[
|
|
22
|
+
{
|
|
23
|
+
value: unknown;
|
|
24
|
+
name: string;
|
|
25
|
+
item: TooltipPayload;
|
|
26
|
+
index: number;
|
|
27
|
+
payload: TooltipPayload[];
|
|
28
|
+
}
|
|
29
|
+
]>;
|
|
30
|
+
};
|
|
31
|
+
export type ScatterTooltipProps = {
|
|
32
|
+
class?: string;
|
|
33
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {} from '../utils.js';
|
|
2
|
+
import { tv } from 'tailwind-variants';
|
|
3
|
+
export const chartContainerVariants = tv({
|
|
4
|
+
base: [
|
|
5
|
+
'flex aspect-video justify-center overflow-visible text-xs',
|
|
6
|
+
// Overrides
|
|
7
|
+
//
|
|
8
|
+
// Stroke around dots/marks when hovering
|
|
9
|
+
'[&_.lc-highlight-point]:stroke-transparent',
|
|
10
|
+
// override the default stroke color of lines
|
|
11
|
+
'[&_.lc-line]:stroke-border/50',
|
|
12
|
+
// by default, layerchart shows a line intersecting the point when hovering, this hides that
|
|
13
|
+
'[&_.lc-highlight-line]:stroke-0',
|
|
14
|
+
// by default, when you hover a point on a stacked series chart, it will drop the opacity
|
|
15
|
+
// of the other series, this overrides that
|
|
16
|
+
'[&_.lc-area-path]:opacity-100 [&_.lc-highlight-line]:opacity-100 [&_.lc-highlight-point]:opacity-100 [&_.lc-spline-path]:opacity-100 [&_.lc-text]:text-xs [&_.lc-text-svg]:overflow-visible',
|
|
17
|
+
// We don't want the little tick lines between the axis labels and the chart, so we remove
|
|
18
|
+
// the stroke. The alternative is to manually disable `tickMarks` on the x/y axis of every
|
|
19
|
+
// chart.
|
|
20
|
+
'[&_.lc-axis-tick]:stroke-0',
|
|
21
|
+
// We don't want to display the rule on the x/y axis, as there is already going to be
|
|
22
|
+
// a grid line there and rule ends up overlapping the marks because it is rendered after
|
|
23
|
+
// the marks
|
|
24
|
+
'[&_.lc-rule-x-line:not(.lc-grid-x-rule)]:stroke-0 [&_.lc-rule-y-line:not(.lc-grid-y-rule)]:stroke-0',
|
|
25
|
+
'[&_.lc-grid-x-radial-line]:stroke-border [&_.lc-grid-x-radial-circle]:stroke-border',
|
|
26
|
+
'[&_.lc-grid-y-radial-line]:stroke-border [&_.lc-grid-y-radial-circle]:stroke-border',
|
|
27
|
+
// Legend adjustments
|
|
28
|
+
'[&_.lc-legend-swatch-button]:items-center [&_.lc-legend-swatch-button]:gap-1.5',
|
|
29
|
+
'[&_.lc-legend-swatch-group]:items-center [&_.lc-legend-swatch-group]:gap-4',
|
|
30
|
+
'[&_.lc-legend-swatch]:size-2.5 [&_.lc-legend-swatch]:rounded-[2px]',
|
|
31
|
+
// Labels
|
|
32
|
+
'[&_.lc-labels-text:not([fill])]:fill-foreground [&_text]:stroke-transparent',
|
|
33
|
+
// Tick labels on th x/y axes
|
|
34
|
+
'[&_.lc-axis-tick-label]:fill-muted-foreground [&_.lc-axis-tick-label]:font-normal',
|
|
35
|
+
'[&_.lc-tooltip-rects-g]:fill-transparent',
|
|
36
|
+
'[&_.lc-layout-svg-g]:fill-transparent',
|
|
37
|
+
'[&_.lc-root-container]:w-full'
|
|
38
|
+
]
|
|
39
|
+
});
|
|
40
|
+
export const chartTooltipVariants = tv({
|
|
41
|
+
base: 'border-border/50 bg-background grid min-w-36 items-start gap-1.5 rounded-xs border px-2.5 py-1.5 text-xs shadow-xl'
|
|
42
|
+
});
|
|
43
|
+
export const scatterTooltipVariants = tv({
|
|
44
|
+
base: 'border-border/50 bg-background grid min-w-32 items-start gap-1.5 rounded-xs border px-2.5 py-1.5 text-xs shadow-xl'
|
|
45
|
+
});
|