@heyramzi/ui 6.0.1 → 7.0.0

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.
Files changed (114) hide show
  1. package/COMPONENTS.md +7 -26
  2. package/README.md +12 -5
  3. package/package.json +14 -16
  4. package/scripts/sync-shadcn.mjs +12 -3
  5. package/src/agents/agent-avatar.svelte +639 -0
  6. package/src/agents/agent-cast.svelte +173 -0
  7. package/src/agents/demo.svelte +78 -0
  8. package/src/agents/index.ts +11 -0
  9. package/src/agents/types.ts +72 -0
  10. package/{LICENSE → src/agents/vendor/LICENSE} +1 -1
  11. package/src/agents/vendor/color.ts +155 -0
  12. package/src/agents/vendor/draw.ts +840 -0
  13. package/src/agents/vendor/engine.ts +845 -0
  14. package/src/agents/vendor/mesh.ts +838 -0
  15. package/src/agents/vendor/plastic.ts +4958 -0
  16. package/src/agents/vendor/presets.ts +103 -0
  17. package/src/agents/vendor/shapes.ts +41 -0
  18. package/src/agents/vendor/ticker.ts +67 -0
  19. package/src/agents/vendor/types.ts +261 -0
  20. package/src/agents/vendor/wear.ts +1699 -0
  21. package/src/ai/index.ts +0 -18
  22. package/src/animations/README.md +3 -3
  23. package/src/lib/sliding-pill.ts +68 -0
  24. package/src/lib/theme-transition.ts +4 -2
  25. package/src/shadcn/code-block/code-block-actions.svelte +23 -0
  26. package/src/shadcn/code-block/code-block-content.svelte +55 -0
  27. package/src/shadcn/code-block/code-block-copy-button.svelte +59 -0
  28. package/src/shadcn/code-block/code-block-footer.svelte +20 -0
  29. package/src/shadcn/code-block/code-block-header.svelte +23 -0
  30. package/src/shadcn/code-block/code-block-line.svelte +71 -0
  31. package/src/shadcn/code-block/code-block-title.svelte +23 -0
  32. package/src/shadcn/code-block/code-block.svelte +47 -0
  33. package/src/shadcn/code-block/index.ts +42 -0
  34. package/src/shadcn/hooks/highlighter.ts +132 -0
  35. package/src/shadcn/hooks/use-highlighted-code.svelte.ts +98 -0
  36. package/src/shadcn/kbd/index.ts +10 -0
  37. package/src/shadcn/kbd/kbd-group.svelte +20 -0
  38. package/src/shadcn/kbd/kbd.svelte +23 -0
  39. package/src/shadcn/message-scroller/index.ts +28 -0
  40. package/src/shadcn/message-scroller/message-scroller-button.svelte +52 -0
  41. package/src/shadcn/message-scroller/message-scroller-content.svelte +17 -0
  42. package/src/shadcn/message-scroller/message-scroller-item.svelte +22 -0
  43. package/src/shadcn/message-scroller/message-scroller-provider.svelte +7 -0
  44. package/src/shadcn/message-scroller/message-scroller-viewport.svelte +20 -0
  45. package/src/shadcn/message-scroller/message-scroller.svelte +20 -0
  46. package/src/shadcn/tabs/tabs-list.svelte +13 -2
  47. package/src/shadcn/tabs/tabs-trigger.svelte +1 -1
  48. package/src/shadcn/trace/index.ts +29 -0
  49. package/src/shadcn/trace/trace-collapsible-content.svelte +29 -0
  50. package/src/shadcn/trace/trace-collapsible-trigger.svelte +29 -0
  51. package/src/shadcn/trace/trace-collapsible.svelte +20 -0
  52. package/src/shadcn/trace/trace-description.svelte +20 -0
  53. package/src/shadcn/trace/trace-icon.svelte +24 -0
  54. package/src/shadcn/trace/trace-line.svelte +31 -0
  55. package/src/shadcn/trace/trace-title.svelte +20 -0
  56. package/src/shadcn/trace/trace.svelte +23 -0
  57. package/src/showcase/AiSection.svelte +0 -7
  58. package/src/showcase/demos/shadcn/code-block.svelte +80 -0
  59. package/src/showcase/demos/shadcn/index.ts +24 -0
  60. package/src/showcase/demos/shadcn/message-scroller.svelte +44 -0
  61. package/src/showcase/demos/shadcn/trace.svelte +51 -0
  62. package/src/showcase/kit.ts +1 -2
  63. package/src/ai/conversation/Conversation.svelte +0 -28
  64. package/src/ai/conversation/ConversationContent.svelte +0 -35
  65. package/src/ai/conversation/ConversationEmptyState.svelte +0 -46
  66. package/src/ai/conversation/ConversationScrollButton.svelte +0 -51
  67. package/src/ai/conversation/index.ts +0 -33
  68. package/src/ai/conversation/stick-to-bottom-context.svelte.ts +0 -131
  69. package/src/charts/animated-value.css +0 -7
  70. package/src/charts/animated-value.svelte +0 -110
  71. package/src/charts/area-chart.css +0 -29
  72. package/src/charts/area-chart.svelte +0 -195
  73. package/src/charts/bar-chart.css +0 -119
  74. package/src/charts/bar-chart.svelte +0 -269
  75. package/src/charts/charts.css +0 -201
  76. package/src/charts/crosshair.svelte +0 -35
  77. package/src/charts/donut-chart.css +0 -56
  78. package/src/charts/donut-chart.svelte +0 -158
  79. package/src/charts/edge-fade.svelte +0 -43
  80. package/src/charts/edge-fade.ts +0 -16
  81. package/src/charts/figure.css +0 -100
  82. package/src/charts/figure.svelte +0 -122
  83. package/src/charts/gauge-chart.css +0 -94
  84. package/src/charts/gauge-chart.svelte +0 -303
  85. package/src/charts/hover-band.test.ts +0 -30
  86. package/src/charts/hover-band.ts +0 -21
  87. package/src/charts/hover-focus.svelte +0 -85
  88. package/src/charts/index.ts +0 -30
  89. package/src/charts/legend.css +0 -131
  90. package/src/charts/legend.svelte +0 -112
  91. package/src/charts/line-chart.css +0 -26
  92. package/src/charts/line-chart.svelte +0 -256
  93. package/src/charts/motion.ts +0 -19
  94. package/src/charts/number-chart.css +0 -168
  95. package/src/charts/number-chart.svelte +0 -177
  96. package/src/charts/primitives/chart-container.svelte +0 -57
  97. package/src/charts/primitives/chart-style.svelte +0 -36
  98. package/src/charts/primitives/chart-tooltip.svelte +0 -161
  99. package/src/charts/primitives/chart-utils.ts +0 -54
  100. package/src/charts/primitives/index.ts +0 -11
  101. package/src/charts/reveal.svelte +0 -61
  102. package/src/charts/ring-chart.css +0 -96
  103. package/src/charts/ring-chart.svelte +0 -158
  104. package/src/charts/sparkline.css +0 -45
  105. package/src/charts/sparkline.svelte +0 -140
  106. package/src/charts/stat-card.css +0 -103
  107. package/src/charts/stat-card.svelte +0 -267
  108. package/src/charts/time-series-chart.css +0 -28
  109. package/src/charts/time-series-chart.svelte +0 -179
  110. package/src/charts/trend-pill.css +0 -38
  111. package/src/charts/trend-pill.svelte +0 -48
  112. package/src/lib/hooks/is-mobile.svelte.ts +0 -11
  113. package/src/showcase/ChartsSection.svelte +0 -458
  114. package/src/showcase/demos/ai/conversation.svelte +0 -59
@@ -1,177 +0,0 @@
1
- <script lang="ts" module>
2
- /**
3
- * Which way a metric has to move to be good news.
4
- */
5
-
6
- /*
7
- * `neutral` is the honest answer for a figure that is neither on its own: spend is not a win or a loss until the return is beside it.
8
- */
9
- export type NumberDirection = "up-good" | "down-good" | "neutral";
10
- </script>
11
-
12
- <script lang="ts">
13
- // =========================
14
- // IMPORTS
15
- // =========================
16
- import "./number-chart.css";
17
- import { Minus, TrendDown, TrendUp } from "phosphor-svelte";
18
- import { cn } from "@heyramzi/utils";
19
- import type { Snippet } from "svelte";
20
- import AnimatedValue from "./animated-value.svelte";
21
-
22
- // =========================
23
- // TYPES
24
- // =========================
25
- type Props = {
26
- /** Metric name, above the figure. */
27
- label: string;
28
- /**
29
- * The figure.
30
- */
31
-
32
- /*
33
- * A number counts up and is formatted here; a string is taken as already formatted and is printed as it stands, which is the only way to draw a total
34
- */
35
-
36
- /*
37
- * that mixes two currencies.
38
- */
39
- value: number | string;
40
- /** Formatter for a numeric `value`. Wins over `formatOptions`. */
41
- format?: (v: number) => string;
42
- /** Intl options for a numeric `value` (currency, compact, percent). */
43
- formatOptions?: Intl.NumberFormatOptions;
44
- /** BCP-47 locale for `formatOptions`. */
45
- locale?: string;
46
- /**
47
- * Change against the period before this one, as a percentage. `null` is
48
- * "nothing to compare against", which is not zero and is drawn as a dash.
49
- */
50
- delta?: number | null;
51
- /** Which way `delta` has to point to be good news. */
52
- direction?: NumberDirection;
53
- /** Movement below this many points is noise and draws as a dash. */
54
- deltaThreshold?: number;
55
- /**
56
- * The chart. A fraction from 0 to 1 that gives the figure its scale: a share of a total, a run against a target, a rate against its ceiling.
57
- */
58
-
59
- /*
60
- * Omitted, the number stands on its own and no track is drawn.
61
- */
62
- share?: number;
63
- /** What the track measures, e.g. "of revenue". Only with `share`. */
64
- shareLabel?: string;
65
- /** Track colour. Defaults to the first series token. */
66
- color?: string;
67
- /** md is a figure in a row; lg is the one figure the screen exists to answer. */
68
- size?: "md" | "lg";
69
- /** A quiet line under the figure: the window it covers, or why it is a dash. */
70
- caption?: string;
71
- /**
72
- * Drop the card chrome. For a figure that sits in a band or a panel of its
73
- * own, where a second border would draw a box inside a box.
74
- */
75
- bare?: boolean;
76
- /** Leading icon before the label. */
77
- icon?: Snippet;
78
- /** Rendered at the right of the label row. */
79
- trailing?: Snippet;
80
- class?: string;
81
- };
82
-
83
- // =========================
84
- // PROPS
85
- // =========================
86
- let {
87
- label,
88
- value,
89
- format,
90
- formatOptions,
91
- locale,
92
- delta = null,
93
- direction = "neutral",
94
- deltaThreshold = 0.5,
95
- share,
96
- shareLabel,
97
- color = "var(--chart-1)",
98
- size = "md",
99
- caption,
100
- bare = false,
101
- icon,
102
- trailing,
103
- class: className,
104
- }: Props = $props();
105
-
106
- // =========================
107
- // DERIVED
108
- // =========================
109
- // Under half a point either way is noise, not a movement, and eight figures
110
- // each reporting a 0% move is eight pieces of chrome saying nothing.
111
- const move = $derived(delta != null && Math.abs(delta) >= deltaThreshold ? delta : null);
112
- // The arrow follows the sign and the colour follows the news, because a cost
113
- // that fell is a green figure with a down arrow. One glyph cannot say both.
114
- const tone = $derived.by(() => {
115
- if (move == null || direction === "neutral") return "flat";
116
- const good = direction === "up-good" ? move > 0 : move < 0;
117
- return good ? "up" : "down";
118
- });
119
- const pct = $derived(share == null ? null : Math.max(0, Math.min(1, share)));
120
- </script>
121
-
122
- <!-- =========================== -->
123
- <!-- NUMBER CHART -->
124
- <!-- =========================== -->
125
- <div class={cn("hr-number-chart", className)} data-size={size} data-bare={bare ? "" : undefined}>
126
- <p class="hr-number-label">
127
- {#if icon}<span class="hr-number-icon" style:color>{@render icon()}</span>{/if}
128
- <span class="hr-number-label-text">{label}</span>
129
- {#if trailing}<span class="hr-number-trailing">{@render trailing()}</span>{/if}
130
- </p>
131
-
132
- <div class="hr-number-row">
133
- <span class="hr-number-value">
134
- {#if typeof value === "number"}
135
- <AnimatedValue {value} {format} {formatOptions} {locale} duration={600} />
136
- {:else}
137
- {value}
138
- {/if}
139
- </span>
140
- {#if move != null}
141
- <span class="hr-number-delta" data-tone={tone}>
142
- {#if move > 0}
143
- <TrendUp class="hr-number-arrow" weight="bold" />
144
- {:else}
145
- <TrendDown class="hr-number-arrow" weight="bold" />
146
- {/if}
147
- <span class="hr-number-delta-text">{Math.abs(move).toFixed(0)}%</span>
148
- </span>
149
- {:else}
150
- <span class="hr-number-delta" data-tone="none" aria-hidden="true">
151
- <Minus class="hr-number-arrow" />
152
- </span>
153
- {/if}
154
- </div>
155
-
156
- {#if pct != null}
157
- <!-- WHY the number gets a rail and not a sparkline: one figure has no series to plot, and the question a lone number cannot answer is "out of -->
158
- <!-- what". The rail answers exactly that and nothing else. -->
159
- <div
160
- class="hr-number-track"
161
- role="img"
162
- aria-label="{label}: {Math.round(pct * 100)}% {shareLabel ?? 'of the total'}"
163
- >
164
- <span class="hr-number-fill" style:width="{pct * 100}%" style:background={color}></span>
165
- </div>
166
- {/if}
167
-
168
- {#if shareLabel && pct != null}
169
- <p class="hr-number-caption">
170
- <span class="hr-number-share">{Math.round(pct * 100)}%</span>
171
- {shareLabel}
172
- </p>
173
- {/if}
174
- {#if caption}
175
- <p class="hr-number-caption">{caption}</p>
176
- {/if}
177
- </div>
@@ -1,57 +0,0 @@
1
- <script lang="ts">
2
- import type { HTMLAttributes } from "svelte/elements";
3
- import { cn, type WithElementRef } from "@heyramzi/utils";
4
- import ChartStyle from "./chart-style.svelte";
5
- import { type ChartConfig, setChartContext } from "./chart-utils.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
- }: WithElementRef<HTMLAttributes<HTMLElement>> & {
17
- config: ChartConfig;
18
- } = $props();
19
-
20
- const chartId = $derived(`chart-${id || uid.replace(/:/g, "")}`);
21
-
22
- setChartContext({
23
- get config() {
24
- return config;
25
- },
26
- });
27
- </script>
28
-
29
- <div
30
- bind:this={ref}
31
- data-chart={chartId}
32
- data-slot="chart"
33
- class={cn(
34
- "flex aspect-video justify-center overflow-visible text-xs",
35
- "[&_.stroke-white]:stroke-transparent",
36
- "[&_.lc-line]:stroke-border/50",
37
- "[&_.lc-highlight-line]:stroke-0",
38
- "[&_.lc-area-path]:opacity-100 [&_.lc-highlight-line]:opacity-100 [&_.lc-highlight-point]:opacity-100 [&_.lc-spline-path]:opacity-100 [&_.lc-text-svg]:overflow-visible [&_.lc-text]:text-xs",
39
- "[&_.lc-axis-tick]:stroke-0",
40
- "[&_.lc-rule-x-line:not(.lc-grid-x-rule)]:stroke-0 [&_.lc-rule-y-line:not(.lc-grid-y-rule)]:stroke-0",
41
- "[&_.lc-grid-x-radial-line]:stroke-border [&_.lc-grid-x-radial-circle]:stroke-border",
42
- "[&_.lc-grid-y-radial-line]:stroke-border [&_.lc-grid-y-radial-circle]:stroke-border",
43
- "[&_.lc-legend-swatch-button]:items-center [&_.lc-legend-swatch-button]:gap-1.5",
44
- "[&_.lc-legend-swatch-group]:items-center [&_.lc-legend-swatch-group]:gap-4",
45
- "[&_.lc-legend-swatch]:size-2.5 [&_.lc-legend-swatch]:rounded-[2px]",
46
- "[&_.lc-labels-text:not([fill])]:fill-foreground [&_text]:stroke-transparent",
47
- "[&_.lc-axis-tick-label]:fill-muted-foreground [&_.lc-axis-tick-label]:font-normal",
48
- "[&_.lc-tooltip-rects-g]:fill-transparent",
49
- "[&_.lc-layout-svg-g]:fill-transparent",
50
- "[&_.lc-root-container]:w-full",
51
- className,
52
- )}
53
- {...restProps}
54
- >
55
- <ChartStyle id={chartId} {config} />
56
- {@render children?.()}
57
- </div>
@@ -1,36 +0,0 @@
1
- <script lang="ts">
2
- import { type ChartConfig, THEMES } 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(([, c]) => c.theme || c.color) : null,
8
- );
9
-
10
- const themeContents = $derived.by(() => {
11
- if (!colorConfig || !colorConfig.length) return;
12
-
13
- const blocks: string[] = [];
14
- for (const [_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 value = itemConfig.theme?.[theme] || itemConfig.color;
19
- return value ? `\t--color-${key}: ${value};` : null;
20
- });
21
-
22
- content += `${color.join("\n")}\n}`;
23
- blocks.push(content);
24
- }
25
-
26
- return blocks.join("\n");
27
- });
28
- </script>
29
-
30
- {#if themeContents}
31
- {#key id}
32
- <svelte:element this={"style"}>
33
- {themeContents}
34
- </svelte:element>
35
- {/key}
36
- {/if}
@@ -1,161 +0,0 @@
1
- <script lang="ts">
2
- import { getChartContext, Tooltip as TooltipPrimitive } from "layerchart";
3
- import type { Snippet } from "svelte";
4
- import type { HTMLAttributes } from "svelte/elements";
5
- import { cn, type WithElementRef, type WithoutChildren } from "@heyramzi/utils";
6
- import {
7
- getPayloadConfigFromPayload,
8
- type TooltipSeriesItem,
9
- useChart,
10
- } from "./chart-utils.js";
11
-
12
- function defaultFormatter(value: unknown, _series: TooltipSeriesItem[]) {
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
- }: WithoutChildren<WithElementRef<HTMLAttributes<HTMLDivElement>>> & {
31
- hideLabel?: boolean;
32
- label?: string;
33
- indicator?: "line" | "dot" | "dashed";
34
- nameKey?: string;
35
- labelKey?: string;
36
- hideIndicator?: boolean;
37
- labelClassName?: string;
38
- labelFormatter?:
39
- | ((value: unknown, series: TooltipSeriesItem[]) => string | number | Snippet)
40
- | null;
41
- formatter?: Snippet<
42
- [
43
- {
44
- value: unknown;
45
- name: string;
46
- item: TooltipSeriesItem;
47
- index: number;
48
- series: TooltipSeriesItem[];
49
- },
50
- ]
51
- >;
52
- } = $props();
53
-
54
- const chart = useChart();
55
- const ctx = getChartContext();
56
-
57
- const tooltipSeries = $derived(ctx.tooltip.series as TooltipSeriesItem[]);
58
-
59
- const formattedLabel = $derived.by(() => {
60
- if (hideLabel || !tooltipSeries?.length) return null;
61
-
62
- const [item] = tooltipSeries;
63
- const key = labelKey ?? item?.label ?? item?.key ?? "value";
64
-
65
- const itemConfig = getPayloadConfigFromPayload(chart.config, item, key);
66
-
67
- const value =
68
- !labelKey && typeof label === "string"
69
- ? (chart.config[label as keyof typeof chart.config]?.label ?? label)
70
- : (itemConfig?.label ?? item.label);
71
-
72
- if (value === undefined) return null;
73
- if (!labelFormatter) return value;
74
- return labelFormatter(value, tooltipSeries);
75
- });
76
-
77
- const nestLabel = $derived(tooltipSeries.length === 1 && indicator !== "dot");
78
- </script>
79
-
80
- {#snippet TooltipLabel()}
81
- {#if formattedLabel}
82
- <div class={cn("font-medium", labelClassName)}>
83
- {#if typeof formattedLabel === "function"}
84
- {@render formattedLabel()}
85
- {:else}
86
- {formattedLabel}
87
- {/if}
88
- </div>
89
- {/if}
90
- {/snippet}
91
-
92
- <TooltipPrimitive.Root variant="none">
93
- <div
94
- class={cn(
95
- "border-border/50 bg-background grid min-w-[9rem] items-start gap-1.5 rounded-lg border px-2.5 py-1.5 text-xs shadow-xl",
96
- className,
97
- )}
98
- {...restProps}
99
- >
100
- {#if !nestLabel}
101
- {@render TooltipLabel()}
102
- {/if}
103
- <div class="grid gap-1.5">
104
- {#each tooltipSeries as item, i (item.key + i)}
105
- {@const key = `${nameKey || item.key || "value"}`}
106
- {@const itemConfig = getPayloadConfigFromPayload(chart.config, item, key)}
107
- {@const indicatorColor = color || item.color}
108
- <div
109
- class={cn(
110
- "[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:size-2.5",
111
- indicator === "dot" && "items-center",
112
- )}
113
- >
114
- {#if formatter && item.value !== undefined && item.key}
115
- {@render formatter({
116
- value: item.value,
117
- name: item.label || item.key,
118
- item,
119
- index: i,
120
- series: tooltipSeries,
121
- })}
122
- {:else}
123
- {#if itemConfig?.icon}
124
- <itemConfig.icon />
125
- {:else if !hideIndicator}
126
- <div
127
- style="--color-bg: {indicatorColor}; --color-border: {indicatorColor};"
128
- class={cn("border-(--color-border) bg-(--color-bg) shrink-0 rounded-[2px]", {
129
- "size-2.5": indicator === "dot",
130
- "h-full w-1": indicator === "line",
131
- "w-0 border-[1.5px] border-dashed bg-transparent": indicator === "dashed",
132
- "my-0.5": nestLabel && indicator === "dashed",
133
- })}
134
- ></div>
135
- {/if}
136
- <div
137
- class={cn(
138
- "flex flex-1 shrink-0 justify-between leading-none",
139
- nestLabel ? "items-end" : "items-center",
140
- )}
141
- >
142
- <div class="grid gap-1.5">
143
- {#if nestLabel}
144
- {@render TooltipLabel()}
145
- {/if}
146
- <span class="text-muted-foreground">
147
- {itemConfig?.label || item.label || item.key}
148
- </span>
149
- </div>
150
- {#if item.value !== undefined}
151
- <span class="text-foreground font-mono font-medium tabular-nums">
152
- {typeof item.value === "number" ? item.value.toLocaleString() : item.value}
153
- </span>
154
- {/if}
155
- </div>
156
- {/if}
157
- </div>
158
- {/each}
159
- </div>
160
- </div>
161
- </TooltipPrimitive.Root>
@@ -1,54 +0,0 @@
1
- import { type Component, getContext, setContext } from "svelte";
2
-
3
- export const THEMES = { light: "", dark: ".dark" } as const;
4
-
5
- export type ChartConfig = {
6
- [k in string]: {
7
- label?: string;
8
- icon?: Component;
9
- } & (
10
- | { color?: string; theme?: never }
11
- | { color?: never; theme: Record<keyof typeof THEMES, string> }
12
- );
13
- };
14
-
15
- export type TooltipSeriesItem = {
16
- key: string;
17
- label: string;
18
- value: unknown;
19
- color: string | undefined;
20
- visible: boolean;
21
- config: unknown;
22
- };
23
-
24
- export function getPayloadConfigFromPayload(
25
- config: ChartConfig,
26
- item: TooltipSeriesItem,
27
- key: string,
28
- ) {
29
- if (typeof item !== "object" || item === null) return undefined;
30
-
31
- let configLabelKey: string = key;
32
-
33
- if (item.key === key) {
34
- configLabelKey = item.key;
35
- } else if (item.label === key) {
36
- configLabelKey = item.label;
37
- }
38
-
39
- return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config];
40
- }
41
-
42
- type ChartContextValue = {
43
- config: ChartConfig;
44
- };
45
-
46
- const chartContextKey = Symbol("chart-context");
47
-
48
- export function setChartContext(value: ChartContextValue) {
49
- return setContext(chartContextKey, value);
50
- }
51
-
52
- export function useChart() {
53
- return getContext<ChartContextValue>(chartContextKey);
54
- }
@@ -1,11 +0,0 @@
1
- export { default as ChartContainer } from "./chart-container.svelte";
2
- export { default as ChartTooltip } from "./chart-tooltip.svelte";
3
- export { default as ChartStyle } from "./chart-style.svelte";
4
- export {
5
- type ChartConfig,
6
- type TooltipSeriesItem,
7
- getPayloadConfigFromPayload,
8
- useChart,
9
- setChartContext,
10
- THEMES,
11
- } from "./chart-utils.js";
@@ -1,61 +0,0 @@
1
- <script lang="ts">
2
- // =========================
3
- // IMPORTS
4
- // =========================
5
- import { getChartContext } from "layerchart";
6
- import { onMount, type Snippet } from "svelte";
7
- import { expoInOut } from "svelte/easing";
8
- import { prefersReducedMotion, Tween } from "svelte/motion";
9
- import { REVEAL_MS, tokenMs } from "./motion.js";
10
-
11
- // =========================
12
- // TYPES
13
- // =========================
14
- type Props = {
15
- children: Snippet;
16
- /** Headroom around the plot so a stroke at the series max is not cut. */
17
- bleed?: number;
18
- };
19
-
20
- // =========================
21
- // PROPS
22
- // =========================
23
- let { children, bleed = 6 }: Props = $props();
24
-
25
- // =========================
26
- // STATE
27
- // =========================
28
- const ctx = getChartContext();
29
- const id = `hr-reveal-${Math.random().toString(36).slice(2, 8)}`;
30
- const progress = new Tween(0, { easing: expoInOut });
31
- let rect = $state<SVGRectElement>();
32
-
33
- // =========================
34
- // LIFECYCLE
35
- // =========================
36
- onMount(() => {
37
- const duration =
38
- prefersReducedMotion.current || !rect ? 0 : tokenMs(rect, "--chart-motion-reveal", REVEAL_MS);
39
- progress.set(1, { duration });
40
- });
41
- </script>
42
-
43
- <!-- =========================== -->
44
- <!-- REVEAL -->
45
- <!-- =========================== -->
46
- <!-- WHY a tweened width and not scaleX: scaleX needs a transform origin, and the
47
- rect has to keep tracking the plot width through a resize. -->
48
- <defs>
49
- <clipPath {id}>
50
- <rect
51
- bind:this={rect}
52
- x={-bleed}
53
- y={-bleed}
54
- width={Math.max(0, (ctx.width + bleed * 2) * progress.current)}
55
- height={ctx.height + bleed * 2}
56
- />
57
- </clipPath>
58
- </defs>
59
- <g clip-path="url(#{id})">
60
- {@render children()}
61
- </g>
@@ -1,96 +0,0 @@
1
- /* Styles for RingChart. Plain stylesheet (see area-chart.css for why).
2
- Selectors are namespaced under `.hr-ring` (the generic ones below would
3
- otherwise collide with GaugeChart's identical names once they are no longer
4
- Svelte-scoped). */
5
-
6
- .hr-ring {
7
- position: relative;
8
- display: inline-grid;
9
- place-items: center;
10
- aspect-ratio: 1;
11
- container-type: size;
12
- }
13
-
14
- .hr-ring svg {
15
- grid-area: 1 / 1;
16
- width: 100%;
17
- height: 100%;
18
- overflow: visible;
19
- }
20
-
21
- .hr-ring .track {
22
- opacity: 0.14;
23
- }
24
-
25
- .hr-ring .fill {
26
- stroke-dasharray: 100;
27
- cursor: pointer;
28
- outline: none;
29
- transition:
30
- stroke-dashoffset var(--chart-motion-slow, 1.1s)
31
- var(--chart-ease-out, cubic-bezier(0.22, 1, 0.36, 1)),
32
- opacity 200ms ease;
33
- }
34
-
35
- .hr-ring .fill.animate {
36
- animation: hr-ring-sweep var(--chart-motion-reveal, 1100ms)
37
- var(--chart-ease-reveal, cubic-bezier(0.85, 0, 0.15, 1)) both;
38
- }
39
-
40
- .hr-ring.glow .fill:hover {
41
- filter: drop-shadow(0 0 5px var(--ring-color));
42
- }
43
-
44
- @keyframes hr-ring-sweep {
45
- from {
46
- stroke-dashoffset: 100;
47
- }
48
- }
49
-
50
- .hr-ring .center {
51
- grid-area: 1 / 1;
52
- display: flex;
53
- flex-direction: column;
54
- align-items: center;
55
- justify-content: center;
56
- /* WHY: keep the readout inside the innermost ring's clear center so the
57
- number never collides with the green ring. */
58
- max-width: 40cqmin;
59
- pointer-events: none;
60
- text-align: center;
61
- }
62
-
63
- .hr-ring .value {
64
- font-size: max(0.75rem, 9.5cqmin);
65
- font-weight: 650;
66
- line-height: 1;
67
- letter-spacing: -0.02em;
68
- font-variant-numeric: tabular-nums;
69
- color: var(--foreground);
70
- }
71
-
72
- .hr-ring .label {
73
- margin-top: 1.5cqmin;
74
- font-size: max(0.5rem, 5cqmin);
75
- font-weight: 500;
76
- color: var(--muted-foreground);
77
- animation: hr-ring-label-in 180ms ease-out both;
78
- }
79
-
80
- @keyframes hr-ring-label-in {
81
- from {
82
- opacity: 0;
83
- transform: translateY(2px);
84
- }
85
- }
86
-
87
- @media (prefers-reduced-motion: reduce) {
88
- .hr-ring .fill,
89
- .hr-ring .fill.animate {
90
- transition: opacity 120ms ease;
91
- animation: none;
92
- }
93
- .hr-ring .label {
94
- animation: none;
95
- }
96
- }