@heyramzi/ui 2.1.0 → 2.2.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.
- package/package.json +7 -8
- package/scripts/sync-shadcn.mjs +3 -4
- package/src/agents/agent-avatar.svelte +604 -575
- package/src/agents/agent-cast.svelte +179 -183
- package/src/agents/demo.svelte +125 -113
- package/src/agents/helpers.ts +23 -17
- package/src/agents/index.ts +4 -4
- package/src/agents/page-state.ts +6 -6
- package/src/agents/types.ts +6 -6
- package/src/ai/ai-button/AiButton.svelte +4 -12
- package/src/ai/generation-progress/GenerationProgress.svelte +10 -3
- package/src/ai/prompt-input/PromptInputTextarea.svelte +1 -2
- package/src/ai/response/Response.css +2 -3
- package/src/animations/avatar-group-hover.css +2 -16
- package/src/animations/card-tilt.css +3 -6
- package/src/animations/digit-pop-in.css +2 -15
- package/src/animations/dropdown.css +4 -17
- package/src/animations/input-clear-dissolve.css +5 -18
- package/src/animations/input-shake.css +9 -41
- package/src/animations/learn-more-hover.css +17 -9
- package/src/animations/like-button.css +2 -4
- package/src/animations/page-side-by-side.css +2 -10
- package/src/animations/plus-menu-morph.css +2 -5
- package/src/animations/shimmer-text.css +5 -18
- package/src/animations/spinning-counter.css +2 -6
- package/src/animations/success-check.css +2 -15
- package/src/animations/thinking-states.css +2 -7
- package/src/brand/brand.test.ts +21 -5
- package/src/brand/index.ts +4 -10
- package/src/canvas/card/card-frame.svelte +43 -39
- package/src/canvas/card/resize-handles.svelte +89 -80
- package/src/canvas/chrome/chrome-zoom.svelte +101 -100
- package/src/canvas/chrome/lock-badge.svelte +8 -8
- package/src/canvas/chrome/side-handles.svelte +24 -19
- package/src/canvas/chrome/size-chip.svelte +30 -30
- package/src/canvas/chrome/snap-guides.svelte +20 -20
- package/src/canvas/components/icons/ToolIcon.svelte +8 -8
- package/src/canvas/components/shared/svg/PreviewSvg.svelte +19 -19
- package/src/canvas/components/shared/svg/ShapeOutline.svelte +71 -48
- package/src/canvas/components/shared/svg/StrokeNode.svelte +21 -27
- package/src/canvas/components/shared/svg/TextRule.svelte +25 -17
- package/src/canvas/edges/bend-handle.svelte +46 -46
- package/src/canvas/edges/edge-gradient.svelte +10 -10
- package/src/canvas/edges/gradient-edge.svelte +45 -29
- package/src/canvas/editor/lock.test.ts +6 -1
- package/src/canvas/editor/style.ts +7 -1
- package/src/canvas/headless/svg-figure.test.ts +5 -1
- package/src/canvas/lod/lod-switch.svelte +111 -106
- package/src/canvas/pen/PenField.svelte +87 -81
- package/src/canvas/pen/PenOverlay.svelte +127 -127
- package/src/canvas/pen/ShapeNode.svelte +31 -32
- package/src/canvas/pen/TextNode.svelte +54 -53
- package/src/canvas/pen/host-field.test.svelte +7 -7
- package/src/canvas/pen/shapes.ts +7 -1
- package/src/canvas/screen/GlyphIcon.svelte +22 -17
- package/src/canvas/screen/PartView.svelte +138 -72
- package/src/canvas/screen/ScreenBody.svelte +81 -60
- package/src/canvas/screen/ScreenCard.svelte +20 -20
- package/src/canvas/ui/CanvasToolbar.svelte +173 -85
- package/src/canvas/ui/FrameLayer.svelte +59 -59
- package/src/canvas/ui/PenLayer.svelte +39 -30
- package/src/canvas/ui/StylePanel.svelte +119 -102
- package/src/canvas/ui/canvas-grid.svelte +137 -129
- package/src/charts/animated-value.svelte +1 -1
- package/src/charts/area-chart.css +2 -5
- package/src/charts/area-chart.svelte +1 -1
- package/src/charts/bar-chart.css +2 -3
- package/src/charts/bar-chart.svelte +1 -1
- package/src/charts/charts.css +5 -17
- package/src/charts/crosshair.svelte +1 -1
- package/src/charts/dashboard/dashboard-empty.svelte +25 -25
- package/src/charts/dashboard/dashboard-section.svelte +24 -24
- package/src/charts/dashboard/data-panel.svelte +50 -50
- package/src/charts/dashboard/data-sort-head.svelte +56 -56
- package/src/charts/dashboard/data-table.svelte +138 -138
- package/src/charts/dashboard/panel-grid.svelte +30 -30
- package/src/charts/dashboard/period-filter.svelte +25 -25
- package/src/charts/dashboard/rating-chip.svelte +19 -19
- package/src/charts/dashboard/stat-band.svelte +32 -32
- package/src/charts/dashboard/stat-tile.svelte +106 -99
- package/src/charts/donut-chart.svelte +1 -1
- package/src/charts/edge-fade.svelte +1 -1
- package/src/charts/figure.svelte +1 -1
- package/src/charts/gauge-chart.css +4 -7
- package/src/charts/gauge-chart.svelte +1 -1
- package/src/charts/hover-focus.svelte +1 -1
- package/src/charts/legend.css +1 -3
- package/src/charts/legend.svelte +1 -1
- package/src/charts/line-chart.svelte +1 -1
- package/src/charts/number-chart.css +6 -9
- package/src/charts/number-chart.svelte +1 -1
- package/src/charts/reference-line.svelte +19 -19
- package/src/charts/reveal.svelte +1 -1
- package/src/charts/ring-chart.css +2 -4
- package/src/charts/ring-chart.svelte +1 -1
- package/src/charts/sparkline.svelte +5 -2
- package/src/charts/stat-card.svelte +1 -1
- package/src/charts/time-series-chart.css +2 -5
- package/src/charts/trend-pill.css +2 -4
- package/src/charts/trend-pill.svelte +1 -1
- package/src/components/copy/copy-button.svelte +93 -98
- package/src/components/copy/copy-button.test.ts +10 -2
- package/src/components/data-table/infinite-scroll.svelte +42 -42
- package/src/components/data-table/pagination.svelte +34 -22
- package/src/components/data-table/row-arrow.svelte +19 -16
- package/src/components/data-table/row-context-menu.svelte +52 -52
- package/src/components/data-table/sortable-head.svelte +25 -25
- package/src/components/data-table/table-skeleton.svelte +41 -36
- package/src/components/docs/doc-page.svelte +18 -9
- package/src/components/docs/on-this-page.svelte +16 -3
- package/src/components/docs/scroll-spy.svelte.ts +2 -3
- package/src/components/field-row/field-row.svelte +10 -8
- package/src/components/hold-to-confirm/hold-to-confirm.svelte +5 -1
- package/src/components/icon/icon.svelte +14 -14
- package/src/components/language-flag/language-flag.svelte +3 -1
- package/src/components/list-page/detail-empty.svelte +34 -34
- package/src/components/list-page/detail-header.svelte +55 -47
- package/src/components/list-page/detail-panel.svelte +51 -59
- package/src/components/list-page/detail-row.svelte +19 -19
- package/src/components/list-page/detail-stack.svelte +21 -21
- package/src/components/list-page/empty-state.svelte +30 -30
- package/src/components/list-page/filter-item.svelte +20 -20
- package/src/components/list-page/filter-popover.svelte +60 -55
- package/src/components/list-page/grouped-list.svelte +4 -2
- package/src/components/list-page/index.ts +5 -1
- package/src/components/list-page/list-page-shell.svelte +2 -5
- package/src/components/list-page/master-detail.svelte +87 -88
- package/src/components/list-page/notice-bar.svelte +38 -38
- package/src/components/list-page/prev-next.svelte +63 -63
- package/src/components/list-page/rail-body.svelte +18 -18
- package/src/components/list-page/rail-empty.svelte +23 -30
- package/src/components/list-page/rail-head.svelte +24 -24
- package/src/components/list-page/rail-row.svelte +77 -73
- package/src/components/list-page/section-host.ts +2 -3
- package/src/components/locale-switch/locale-switch.svelte +41 -41
- package/src/components/mark/mark.svelte +34 -38
- package/src/components/open-in/index.ts +1 -5
- package/src/components/open-in/open-in.svelte +40 -40
- package/src/components/picker/mark-picker.svelte +49 -49
- package/src/components/picker/picker.svelte +5 -3
- package/src/components/section-header/section-header.test.ts +7 -1
- package/src/components/section-header/section-label.svelte +20 -20
- package/src/components/segmented/animated-tabs.svelte +31 -2
- package/src/components/segmented/collapse.test.ts +2 -1
- package/src/components/segmented/index.ts +2 -0
- package/src/components/segmented/segment.svelte +52 -49
- package/src/components/segmented/segmented-filter.svelte +79 -80
- package/src/components/segmented/segmented-nav.svelte +34 -40
- package/src/components/segmented/segmented-overflow.svelte +53 -50
- package/src/components/segmented/segmented-track.svelte +62 -46
- package/src/components/segmented/segmented.ts +2 -3
- package/src/components/star-rating-input/star-rating-input.svelte +103 -103
- package/src/components/svg/svg.svelte +4 -4
- package/src/components/table-view/table-view.svelte +17 -2
- package/src/components/table-view/table-view.test.ts +7 -1
- package/src/components/table-view/types.ts +11 -11
- package/src/components/tags-input/index.ts +13 -0
- package/src/components/tags-input/tags-input.svelte +305 -0
- package/src/components/tags-input/tags.test.ts +64 -0
- package/src/components/tags-input/tags.ts +142 -0
- package/src/components/theme/index.ts +2 -4
- package/src/components/theme/theme-provider.svelte +1 -3
- package/src/components/toolbar/toolbar-search.svelte +25 -25
- package/src/components/toolbar/toolbar.svelte +11 -7
- package/src/components/toolbar/toolbar.test.ts +10 -1
- package/src/design/index.css +21 -78
- package/src/design/polish.css +26 -35
- package/src/editor/editor-toolbar.svelte +104 -99
- package/src/editor/editor.svelte +296 -266
- package/src/editor/image-toolbar.svelte +20 -22
- package/src/editor/index.ts +1 -4
- package/src/editor/inline-editor.svelte +280 -238
- package/src/editor/markdown.ts +15 -15
- package/src/editor/renderer.svelte +71 -71
- package/src/editor/slash-menu.svelte +24 -27
- package/src/effects/animated-beam/diagrams/BeamSvg.svelte +1 -1
- package/src/effects/image-comparison/ImageComparison.svelte +11 -1
- package/src/effects/interactive-hover-button/InteractiveHoverButton.svelte +59 -46
- package/src/effects/magic-card/MagicCard.css +14 -7
- package/src/effects/marquee/Marquee.svelte +3 -5
- package/src/effects/reveal/observe.ts +0 -3
- package/src/effects/spotlight/Spotlight.svelte +1 -1
- package/src/effects/theme-toggle/ThemeToggle.css +25 -22
- package/src/effects/theme-toggle/ThemeToggle.svelte +6 -7
- package/src/gallery.test.ts +14 -4
- package/src/inbox/README.md +1 -3
- package/src/inbox/inbox.test.ts +181 -138
- package/src/inbox/index.ts +20 -20
- package/src/inbox/logic.ts +114 -111
- package/src/inbox/thread-inbox.svelte +227 -151
- package/src/inbox/types.ts +130 -108
- package/src/lib/theme-transition.ts +2 -4
- package/src/lib/tone.ts +2 -4
- package/src/lib/utils.ts +2 -2
- package/src/modules.test.ts +79 -69
- package/src/onboarding/define-tours.test.ts +55 -40
- package/src/onboarding/define-tours.ts +23 -23
- package/src/onboarding/demo.svelte +24 -5
- package/src/onboarding/index.ts +1 -8
- package/src/onboarding/onboarding-provider.svelte +19 -7
- package/src/onboarding/onboarding.svelte.ts +16 -16
- package/src/onboarding/pick.test.ts +61 -52
- package/src/onboarding/pick.ts +16 -13
- package/src/onboarding/stores.test.ts +80 -80
- package/src/onboarding/stores.ts +41 -39
- package/src/onboarding/tour-card.svelte +30 -5
- package/src/onboarding/tour-runner.svelte +12 -3
- package/src/onboarding/types.ts +20 -12
- package/src/onboarding/wait.ts +22 -22
- package/src/panel/side-panel.svelte +94 -93
- package/src/record/demo-data.ts +291 -225
- package/src/record/field-section.svelte +38 -34
- package/src/record/fields.ts +148 -96
- package/src/record/format.ts +63 -56
- package/src/record/index.ts +49 -35
- package/src/record/info-list.svelte +145 -114
- package/src/record/kinds.ts +31 -31
- package/src/record/logic.ts +216 -214
- package/src/record/next-up.svelte +45 -34
- package/src/record/project-list.svelte +56 -50
- package/src/record/record-demo.svelte +130 -110
- package/src/record/record-header.svelte +37 -28
- package/src/record/record-shell.svelte +54 -54
- package/src/record/record-stats.svelte +25 -25
- package/src/record/record-timeline.svelte +134 -114
- package/src/record/record.test.ts +430 -309
- package/src/record/steps-rail.svelte +225 -216
- package/src/record/types.ts +234 -215
- package/src/record/where-we-are.svelte +62 -58
- package/src/shadcn/button/button.svelte +1 -1
- package/src/shadcn/card/card-title.svelte +1 -5
- package/src/shadcn/hooks/use-highlighted-code.svelte.ts +2 -13
- package/src/shadcn/sheet/sheet-content.svelte +2 -4
- package/src/shadcn/table/table-cell.svelte +2 -3
- package/src/shadcn/utilities.css +3 -8
- package/src/slides/Controls.svelte +150 -124
- package/src/slides/Deck.svelte +243 -238
- package/src/slides/Fragment.svelte +64 -57
- package/src/slides/Overview.svelte +106 -101
- package/src/slides/Presenter.svelte +300 -266
- package/src/slides/Slide.svelte +185 -170
- package/src/slides/SlideList.svelte +16 -12
- package/src/slides/ambient.d.ts +1 -1
- package/src/slides/bridges.ts +42 -23
- package/src/slides/deck-controller.svelte.ts +47 -35
- package/src/slides/demo-slides.svelte +330 -306
- package/src/slides/demo.svelte +164 -160
- package/src/slides/export/capture.ts +20 -16
- package/src/slides/export/ffmpeg.ts +43 -38
- package/src/slides/export/index.ts +5 -5
- package/src/slides/export/launch.ts +3 -1
- package/src/slides/export/pdf.ts +30 -27
- package/src/slides/export/types.ts +10 -8
- package/src/slides/index.ts +11 -11
- package/src/slides/labels.ts +22 -19
- package/src/slides/layout.ts +9 -2
- package/src/slides/model.ts +4 -4
- package/src/slides/navigation.ts +43 -31
- package/src/slides/theme.css +5 -7
- package/src/slides/types.ts +9 -10
- package/src/status/define-statuses.ts +17 -24
- package/src/status/index.ts +5 -5
- package/src/status/status-badge.svelte +3 -1
- package/src/status/status-dot.svelte +1 -3
- package/src/status/status-icon.svelte +1 -1
- package/src/status/status-types.ts +21 -21
- package/src/tasks/board-order.ts +2 -4
- package/src/tasks/demo.svelte +189 -31
- package/src/tasks/detail/detail-demo.svelte +73 -13
- package/src/tasks/detail/detail-types.ts +45 -45
- package/src/tasks/detail/task-detail.svelte +40 -10
- package/src/tasks/detail/task-property.svelte +8 -2
- package/src/tasks/detail/task-thread.svelte +53 -12
- package/src/tasks/gantt/gantt-bar.svelte +17 -6
- package/src/tasks/gantt/gantt-grid.svelte +40 -9
- package/src/tasks/gantt/gantt-group-row.svelte +8 -2
- package/src/tasks/gantt/gantt-header.svelte +9 -3
- package/src/tasks/gantt/gantt-task-row.svelte +26 -6
- package/src/tasks/gantt/gantt.css +2 -1
- package/src/tasks/gantt/gantt.test.ts +3 -1
- package/src/tasks/gantt/logic.test.ts +110 -20
- package/src/tasks/gantt/logic.ts +42 -6
- package/src/tasks/inbox/inbox-card.svelte +13 -5
- package/src/tasks/inbox/inbox-draft.svelte +1 -1
- package/src/tasks/inbox/inbox-logic.ts +6 -40
- package/src/tasks/inbox/inbox-statuses.ts +23 -23
- package/src/tasks/inbox/inbox-trace.svelte +9 -3
- package/src/tasks/inbox/inbox-types.ts +54 -58
- package/src/tasks/inbox/index.ts +22 -21
- package/src/tasks/index.ts +49 -52
- package/src/tasks/kanban-board.svelte +16 -12
- package/src/tasks/kanban-types.ts +2 -3
- package/src/tasks/presets/sales.ts +6 -6
- package/src/tasks/task-gantt.svelte +28 -8
- package/src/tasks/task-list.svelte +41 -17
- package/src/tasks/time/duration.ts +40 -40
- package/src/tasks/time/time-chip.svelte +9 -2
- package/src/tasks/time/time-dot.svelte +1 -1
- package/src/tasks/time/time-ring.svelte +11 -3
- package/src/tasks/time/time-tracker.svelte +57 -15
- package/src/tasks/time/time-types.ts +57 -57
- package/src/tasks/use-kanban-dnd.svelte.ts +2 -3
- package/src/thread/index.ts +7 -7
- package/src/thread/logic.ts +36 -33
- package/src/thread/thread.svelte +82 -16
- package/src/thread/thread.test.ts +58 -47
- package/src/thread/types.ts +68 -68
- package/src/tokens/craft.css +17 -53
- package/src/tokens/index.css +3 -13
- package/src/tokens/palette.css +13 -11
- package/src/tokens/primitives.css +10 -23
- package/src/tasks/inbox/inbox.svelte +0 -203
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
<script lang="ts" module>
|
|
2
|
-
/** A target the data is judged against: a deal, a budget, a limit. */
|
|
3
|
-
export type ChartReference = {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
};
|
|
2
|
+
/** A target the data is judged against: a deal, a budget, a limit. */
|
|
3
|
+
export type ChartReference = {
|
|
4
|
+
value: number;
|
|
5
|
+
/** Already formatted, figure included: "Deal €61,500". */
|
|
6
|
+
label: string;
|
|
7
|
+
/** A status token when crossing it means something, e.g. `var(--destructive)`. */
|
|
8
|
+
color?: string;
|
|
9
|
+
};
|
|
10
10
|
</script>
|
|
11
11
|
|
|
12
12
|
<script lang="ts">
|
|
13
|
-
/*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
import "./reference-line.css";
|
|
18
|
-
import { AnnotationLine } from "layerchart";
|
|
13
|
+
/*
|
|
14
|
+
* A dashed rule across the plot at a fixed value, labelled at its start.
|
|
15
|
+
* Drawn inside a layerchart `<Svg>`; the chart widens its domain to keep it in view.
|
|
16
|
+
*/
|
|
17
|
+
import "./reference-line.css";
|
|
18
|
+
import { AnnotationLine } from "layerchart";
|
|
19
19
|
|
|
20
|
-
interface Props {
|
|
21
|
-
|
|
22
|
-
}
|
|
20
|
+
interface Props {
|
|
21
|
+
reference: ChartReference;
|
|
22
|
+
}
|
|
23
23
|
|
|
24
|
-
let { reference }: Props = $props();
|
|
24
|
+
let { reference }: Props = $props();
|
|
25
25
|
|
|
26
|
-
const stroke = $derived(reference.color ?? "var(--foreground)");
|
|
26
|
+
const stroke = $derived(reference.color ?? "var(--foreground)");
|
|
27
27
|
</script>
|
|
28
28
|
|
|
29
29
|
<g class="hr-reference-line" style:--hr-reference-color={stroke}>
|
package/src/charts/reveal.svelte
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
otherwise collide with GaugeChart's identical names once they are no longer
|
|
4
|
-
Svelte-scoped). */
|
|
1
|
+
/* RingChart styles, namespaced under `.hr-ring` because unscoped, the generic names
|
|
2
|
+
below collide with GaugeChart's (area-chart.css says why it's plain CSS). */
|
|
5
3
|
|
|
6
4
|
.hr-ring {
|
|
7
5
|
position: relative;
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
bare?: boolean;
|
|
33
33
|
formatValue?: (v: number) => string;
|
|
34
34
|
class?: string;
|
|
35
|
-
}
|
|
35
|
+
}
|
|
36
36
|
|
|
37
37
|
// =========================
|
|
38
38
|
// PROPS
|
|
@@ -96,7 +96,10 @@
|
|
|
96
96
|
onpointerleave={() => (active = null)}
|
|
97
97
|
>
|
|
98
98
|
{#if data.length > 0}
|
|
99
|
-
<div
|
|
99
|
+
<div
|
|
100
|
+
class="hr-sparkline-chart"
|
|
101
|
+
style:height={typeof height === "number" ? `${height}px` : height}
|
|
102
|
+
>
|
|
100
103
|
<Chart {data} x={xKey} y={yKey} {padding} tooltipContext={{ mode: "bisect-x" }}>
|
|
101
104
|
<Svg>
|
|
102
105
|
<LinearGradient id={uid} vertical>
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
`?type=style&lang.css` virtual module that Tailwind v4's Vite plugin mis-parses
|
|
4
|
-
when this package is consumed as source from node_modules (tailwindlabs/
|
|
5
|
-
tailwindcss#20000). Selectors are namespaced under `.hr-ts-chart`. */
|
|
1
|
+
/* A plain stylesheet, not a `<style>` block, because Tailwind v4's Vite plugin mis-parses that
|
|
2
|
+
virtual module when this package is consumed from node_modules (tailwindcss#20000). */
|
|
6
3
|
|
|
7
4
|
.hr-ts-chart {
|
|
8
5
|
width: 100%;
|
|
@@ -11,10 +11,8 @@
|
|
|
11
11
|
border-radius: 999px;
|
|
12
12
|
border: 1px solid color-mix(in srgb, var(--stat-trend, currentColor) 22%, transparent);
|
|
13
13
|
background: color-mix(in srgb, var(--stat-trend, currentColor) 10%, transparent);
|
|
14
|
-
/*
|
|
15
|
-
|
|
16
|
-
it on a light page and lightens it on a dark one, so one rule serves both
|
|
17
|
-
themes and the package needs to know nothing about which is on. */
|
|
14
|
+
/* The raw hue measured 3.5:1 on its own wash, so it mixes toward --foreground,
|
|
15
|
+
which fixes the contrast in both themes with one rule. */
|
|
18
16
|
color: color-mix(
|
|
19
17
|
in oklab,
|
|
20
18
|
var(--stat-trend, var(--muted-foreground, oklch(0.5 0.01 286))) 72%,
|
|
@@ -1,101 +1,98 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
/**
|
|
3
|
-
* The one copy control. No `text` prop draws the icon button; `text` draws the
|
|
4
|
-
* chip. `agent` marks a payload written for a model.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// =========================
|
|
8
|
-
// IMPORTS
|
|
9
|
-
// =========================
|
|
10
|
-
import { Check, Copy, StarFour } from "phosphor-svelte";
|
|
11
|
-
import { cn } from "../../lib/utils.js";
|
|
12
|
-
import { buttonVariants } from "../../shadcn/button/index.js";
|
|
13
|
-
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
14
|
-
import { copyText } from "./clipboard.js";
|
|
15
|
-
|
|
16
|
-
// =========================
|
|
17
|
-
// TYPES
|
|
18
|
-
// =========================
|
|
19
|
-
interface Props extends Omit<HTMLButtonAttributes, "value" | "onclick" | "children"> {
|
|
20
|
-
/**
|
|
21
|
-
* What lands on the clipboard. A function runs on the click, so a long list
|
|
22
|
-
* doesn't build every prompt up front. It may return a promise.
|
|
23
|
-
*/
|
|
24
|
-
value: string | (() => string | Promise<string>);
|
|
25
|
-
/** Names the thing being copied, for a screen reader and the tooltip. */
|
|
26
|
-
label?: string;
|
|
27
|
-
/** Announced when the write lands; see `announce`. */
|
|
28
|
-
copiedLabel?: string;
|
|
29
|
-
/** The chip's visible text. Its presence is what makes this a chip. */
|
|
30
|
-
text?: string;
|
|
31
|
-
/** Marks the payload as an instruction for an AI rather than a raw string. */
|
|
32
|
-
agent?: boolean;
|
|
33
|
-
/**
|
|
34
|
-
* `sm` matches `OpenIn`, for the icon row inside a calendar chip. `md` is
|
|
35
|
-
* the 28px icon `Button`, for a toolbar that sits beside other icon buttons.
|
|
36
|
-
*/
|
|
37
|
-
size?: "default" | "md" | "sm";
|
|
38
|
-
/**
|
|
39
|
-
* The icon button's `Button` variant: ghost unless it sits beside an outline
|
|
40
|
-
* one. `inline` is for the chip: it takes the surrounding text's font and
|
|
41
|
-
* colour, and the icon trails it.
|
|
42
|
-
*/
|
|
43
|
-
variant?: "ghost" | "outline" | "inline";
|
|
44
2
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* to `copiedLabel` (write it in full) and has no region, for a page that
|
|
48
|
-
* renders hundreds of these.
|
|
3
|
+
* The one copy control. No `text` prop draws the icon button; `text` draws the
|
|
4
|
+
* chip. `agent` marks a payload written for a model.
|
|
49
5
|
*/
|
|
50
|
-
announce?: "region" | "name";
|
|
51
|
-
/** Runs after a write that landed, for a toast the call site owns. */
|
|
52
|
-
onCopied?: () => void;
|
|
53
|
-
class?: string;
|
|
54
|
-
}
|
|
55
6
|
|
|
56
|
-
// =========================
|
|
57
|
-
//
|
|
58
|
-
// =========================
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
7
|
+
// =========================
|
|
8
|
+
// IMPORTS
|
|
9
|
+
// =========================
|
|
10
|
+
import { Check, Copy, StarFour } from "phosphor-svelte";
|
|
11
|
+
import { cn } from "../../lib/utils.js";
|
|
12
|
+
import { buttonVariants } from "../../shadcn/button/index.js";
|
|
13
|
+
import type { HTMLButtonAttributes } from "svelte/elements";
|
|
14
|
+
import { copyText } from "./clipboard.js";
|
|
15
|
+
|
|
16
|
+
// =========================
|
|
17
|
+
// TYPES
|
|
18
|
+
// =========================
|
|
19
|
+
interface Props extends Omit<HTMLButtonAttributes, "value" | "onclick" | "children"> {
|
|
20
|
+
/**
|
|
21
|
+
* What lands on the clipboard. A function runs on the click, so a long list
|
|
22
|
+
* doesn't build every prompt up front. It may return a promise.
|
|
23
|
+
*/
|
|
24
|
+
value: string | (() => string | Promise<string>);
|
|
25
|
+
/** Names the thing being copied, for a screen reader and the tooltip. */
|
|
26
|
+
label?: string;
|
|
27
|
+
/** Announced when the write lands; see `announce`. */
|
|
28
|
+
copiedLabel?: string;
|
|
29
|
+
/** The chip's visible text. Its presence is what makes this a chip. */
|
|
30
|
+
text?: string;
|
|
31
|
+
/** Marks the payload as an instruction for an AI rather than a raw string. */
|
|
32
|
+
agent?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* `sm` matches `OpenIn`, for the icon row inside a calendar chip. `md` is
|
|
35
|
+
* the 28px icon `Button`, for a toolbar that sits beside other icon buttons.
|
|
36
|
+
*/
|
|
37
|
+
size?: "default" | "md" | "sm";
|
|
38
|
+
/**
|
|
39
|
+
* Ghost unless it sits beside an outline button; `inline` is for the chip,
|
|
40
|
+
* taking the surrounding text's font and colour.
|
|
41
|
+
*/
|
|
42
|
+
variant?: "ghost" | "outline" | "inline";
|
|
43
|
+
/**
|
|
44
|
+
* `region` announces `copiedLabel` in a live region; `name` swaps the button's
|
|
45
|
+
* name to it (write it in full), for a page rendering hundreds of these.
|
|
46
|
+
*/
|
|
47
|
+
announce?: "region" | "name";
|
|
48
|
+
/** Runs after a write that landed, for a toast the call site owns. */
|
|
49
|
+
onCopied?: () => void;
|
|
50
|
+
class?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// =========================
|
|
54
|
+
// PROPS
|
|
55
|
+
// =========================
|
|
56
|
+
let {
|
|
57
|
+
value,
|
|
58
|
+
label = "Copy",
|
|
59
|
+
copiedLabel = "Copied",
|
|
60
|
+
text,
|
|
61
|
+
agent = false,
|
|
62
|
+
size = "default",
|
|
63
|
+
variant = "ghost",
|
|
64
|
+
announce = "region",
|
|
65
|
+
onCopied,
|
|
66
|
+
class: className,
|
|
67
|
+
...restProps
|
|
68
|
+
}: Props = $props();
|
|
72
69
|
|
|
73
|
-
// =========================
|
|
74
|
-
// STATE
|
|
75
|
-
// =========================
|
|
76
|
-
let copied = $state(false);
|
|
77
|
-
let resetAt: ReturnType<typeof setTimeout> | undefined;
|
|
70
|
+
// =========================
|
|
71
|
+
// STATE
|
|
72
|
+
// =========================
|
|
73
|
+
let copied = $state(false);
|
|
74
|
+
let resetAt: ReturnType<typeof setTimeout> | undefined;
|
|
78
75
|
|
|
79
|
-
// =========================
|
|
80
|
-
// DERIVED
|
|
81
|
-
// =========================
|
|
82
|
-
const inline = $derived(text !== undefined && variant === "inline");
|
|
76
|
+
// =========================
|
|
77
|
+
// DERIVED
|
|
78
|
+
// =========================
|
|
79
|
+
const inline = $derived(text !== undefined && variant === "inline");
|
|
83
80
|
|
|
84
|
-
// =========================
|
|
85
|
-
// FUNCTIONS
|
|
86
|
-
// =========================
|
|
87
|
-
async function copy(event: MouseEvent) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
81
|
+
// =========================
|
|
82
|
+
// FUNCTIONS
|
|
83
|
+
// =========================
|
|
84
|
+
async function copy(event: MouseEvent) {
|
|
85
|
+
// Rows and cards are clickable; taking the payload out of one must not also
|
|
86
|
+
// open the record it belongs to.
|
|
87
|
+
event.stopPropagation();
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
const payload = typeof value === "function" ? await value() : value;
|
|
90
|
+
if (!(await copyText(payload))) return;
|
|
91
|
+
copied = true;
|
|
92
|
+
clearTimeout(resetAt);
|
|
93
|
+
resetAt = setTimeout(() => (copied = false), 1400);
|
|
94
|
+
onCopied?.();
|
|
95
|
+
}
|
|
99
96
|
</script>
|
|
100
97
|
|
|
101
98
|
<!-- =========================== -->
|
|
@@ -113,10 +110,10 @@ async function copy(event: MouseEvent) {
|
|
|
113
110
|
? size === "sm"
|
|
114
111
|
? "grid size-5 place-items-center rounded-md hover:bg-accent"
|
|
115
112
|
: buttonVariants({
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
113
|
+
variant: variant === "outline" ? "outline" : "ghost",
|
|
114
|
+
size: size === "md" ? "sm" : "default",
|
|
115
|
+
icon: true,
|
|
116
|
+
})
|
|
120
117
|
: inline
|
|
121
118
|
? "inline-flex cursor-copy items-baseline gap-1.5 transition-colors hover:text-foreground"
|
|
122
119
|
: "flex items-center gap-1 rounded px-1.5 py-0.5 font-mono text-xs tabular-nums text-muted-foreground/70 transition-colors hover:bg-muted hover:text-foreground",
|
|
@@ -154,9 +151,7 @@ async function copy(event: MouseEvent) {
|
|
|
154
151
|
<Copy
|
|
155
152
|
class={cn(
|
|
156
153
|
"col-start-1 row-start-1 size-full transition-[opacity,color] duration-[var(--duration-fast)] ease-[var(--ease-smooth-out)]",
|
|
157
|
-
inline
|
|
158
|
-
? "text-current"
|
|
159
|
-
: "text-muted-foreground group-hover/copy:text-foreground",
|
|
154
|
+
inline ? "text-current" : "text-muted-foreground group-hover/copy:text-foreground",
|
|
160
155
|
copied ? "opacity-0" : "opacity-100",
|
|
161
156
|
)}
|
|
162
157
|
aria-hidden="true"
|
|
@@ -51,14 +51,22 @@ describe("CopyButton", () => {
|
|
|
51
51
|
|
|
52
52
|
it("names what it copies, so the button is not just a value", () => {
|
|
53
53
|
stubClipboard();
|
|
54
|
-
const { button, done } = render({
|
|
54
|
+
const { button, done } = render({
|
|
55
|
+
value: "abc123",
|
|
56
|
+
text: "abc123",
|
|
57
|
+
label: "Copy API key abc123",
|
|
58
|
+
});
|
|
55
59
|
expect(button.getAttribute("aria-label")).toBe("Copy API key abc123");
|
|
56
60
|
done();
|
|
57
61
|
});
|
|
58
62
|
|
|
59
63
|
it("announces in a live region by default and keeps its name", async () => {
|
|
60
64
|
stubClipboard();
|
|
61
|
-
const { button, target, done } = render({
|
|
65
|
+
const { button, target, done } = render({
|
|
66
|
+
value: "x",
|
|
67
|
+
label: "Copy id",
|
|
68
|
+
copiedLabel: "Copied",
|
|
69
|
+
});
|
|
62
70
|
await click(button);
|
|
63
71
|
expect(target.querySelector("[aria-live]")?.textContent).toBe("Copied");
|
|
64
72
|
expect(button.getAttribute("aria-label")).toBe("Copy id");
|
|
@@ -1,52 +1,52 @@
|
|
|
1
1
|
<script lang="ts" generics="T">
|
|
2
|
-
// ========================= IMPORTS
|
|
3
|
-
import type { Snippet } from "svelte";
|
|
4
|
-
import { Spinner } from "../../shadcn/spinner/index.js";
|
|
2
|
+
// ========================= IMPORTS
|
|
3
|
+
import type { Snippet } from "svelte";
|
|
4
|
+
import { Spinner } from "../../shadcn/spinner/index.js";
|
|
5
5
|
|
|
6
|
-
// ========================= TYPES
|
|
7
|
-
interface Props {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
6
|
+
// ========================= TYPES
|
|
7
|
+
interface Props {
|
|
8
|
+
items: T[];
|
|
9
|
+
isDone: boolean;
|
|
10
|
+
isLoading: boolean;
|
|
11
|
+
children: Snippet<[{ items: T[] }]>;
|
|
12
|
+
/** Skeleton snippet rendered while loading more */
|
|
13
|
+
skeleton?: Snippet;
|
|
14
|
+
onLoadMore: () => void;
|
|
15
|
+
threshold?: number;
|
|
16
|
+
loadingClass?: string;
|
|
17
|
+
}
|
|
18
18
|
|
|
19
|
-
// ========================= PROPS
|
|
20
|
-
let {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}: Props = $props();
|
|
19
|
+
// ========================= PROPS
|
|
20
|
+
let {
|
|
21
|
+
items,
|
|
22
|
+
isDone,
|
|
23
|
+
isLoading,
|
|
24
|
+
children,
|
|
25
|
+
skeleton,
|
|
26
|
+
onLoadMore,
|
|
27
|
+
threshold = 200,
|
|
28
|
+
loadingClass,
|
|
29
|
+
}: Props = $props();
|
|
30
30
|
|
|
31
|
-
// ========================= STATE
|
|
32
|
-
let sentinelEl: HTMLDivElement | undefined = $state();
|
|
31
|
+
// ========================= STATE
|
|
32
|
+
let sentinelEl: HTMLDivElement | undefined = $state();
|
|
33
33
|
|
|
34
|
-
// ========================= EFFECTS
|
|
35
|
-
$effect(() => {
|
|
36
|
-
|
|
34
|
+
// ========================= EFFECTS
|
|
35
|
+
$effect(() => {
|
|
36
|
+
if (!sentinelEl || isDone) return;
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
const observer = new IntersectionObserver(
|
|
39
|
+
(entries) => {
|
|
40
|
+
if (entries[0]?.isIntersecting && !isLoading && !isDone) {
|
|
41
|
+
onLoadMore();
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
{ rootMargin: `${threshold}px` },
|
|
45
|
+
);
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
});
|
|
47
|
+
observer.observe(sentinelEl);
|
|
48
|
+
return () => observer.disconnect();
|
|
49
|
+
});
|
|
50
50
|
</script>
|
|
51
51
|
|
|
52
52
|
<!-- =========================== -->
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { CaretLeft, CaretRight } from "phosphor-svelte";
|
|
3
|
-
import { Button } from "../../shadcn/button/index.js";
|
|
4
|
-
import { totalPages as calcTotalPages } from "./data-table.js";
|
|
2
|
+
import { CaretLeft, CaretRight } from "phosphor-svelte";
|
|
3
|
+
import { Button } from "../../shadcn/button/index.js";
|
|
4
|
+
import { totalPages as calcTotalPages } from "./data-table.js";
|
|
5
5
|
|
|
6
|
-
// ========================= TYPES
|
|
7
|
-
interface Props {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
6
|
+
// ========================= TYPES
|
|
7
|
+
interface Props {
|
|
8
|
+
page: number;
|
|
9
|
+
pageSize: number;
|
|
10
|
+
total: number;
|
|
11
|
+
onPageChange?: (page: number) => void;
|
|
12
|
+
/** If provided, renders <a> tags instead of buttons (for server-side pagination) */
|
|
13
|
+
buildHref?: (page: number) => string;
|
|
14
|
+
}
|
|
15
15
|
|
|
16
|
-
// ========================= PROPS
|
|
17
|
-
let { page, pageSize, total, onPageChange, buildHref }: Props = $props();
|
|
16
|
+
// ========================= PROPS
|
|
17
|
+
let { page, pageSize, total, onPageChange, buildHref }: Props = $props();
|
|
18
18
|
|
|
19
|
-
// ========================= DERIVED
|
|
20
|
-
let pages = $derived(calcTotalPages(total, pageSize));
|
|
21
|
-
let hasPrev = $derived(page > 1);
|
|
22
|
-
let hasNext = $derived(page < pages);
|
|
23
|
-
let start = $derived((page - 1) * pageSize + 1);
|
|
24
|
-
let end = $derived(Math.min(page * pageSize, total));
|
|
19
|
+
// ========================= DERIVED
|
|
20
|
+
let pages = $derived(calcTotalPages(total, pageSize));
|
|
21
|
+
let hasPrev = $derived(page > 1);
|
|
22
|
+
let hasNext = $derived(page < pages);
|
|
23
|
+
let start = $derived((page - 1) * pageSize + 1);
|
|
24
|
+
let end = $derived(Math.min(page * pageSize, total));
|
|
25
25
|
</script>
|
|
26
26
|
|
|
27
27
|
{#if pages > 1}
|
|
@@ -49,11 +49,23 @@ let end = $derived(Math.min(page * pageSize, total));
|
|
|
49
49
|
</Button>
|
|
50
50
|
{/if}
|
|
51
51
|
{:else}
|
|
52
|
-
<Button
|
|
52
|
+
<Button
|
|
53
|
+
variant="outline"
|
|
54
|
+
size="sm"
|
|
55
|
+
icon
|
|
56
|
+
disabled={!hasPrev}
|
|
57
|
+
onclick={() => onPageChange?.(page - 1)}
|
|
58
|
+
>
|
|
53
59
|
<CaretLeft class="size-4" />
|
|
54
60
|
</Button>
|
|
55
61
|
<span class="mx-2 tabular-nums">{page} / {pages}</span>
|
|
56
|
-
<Button
|
|
62
|
+
<Button
|
|
63
|
+
variant="outline"
|
|
64
|
+
size="sm"
|
|
65
|
+
icon
|
|
66
|
+
disabled={!hasNext}
|
|
67
|
+
onclick={() => onPageChange?.(page + 1)}
|
|
68
|
+
>
|
|
57
69
|
<CaretRight class="size-4" />
|
|
58
70
|
</Button>
|
|
59
71
|
{/if}
|
|
@@ -1,28 +1,31 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
// =========================
|
|
3
|
-
// IMPORTS
|
|
4
|
-
// =========================
|
|
5
|
-
import { CaretRight } from "phosphor-svelte";
|
|
6
|
-
import { cn } from "../../lib/utils.js";
|
|
2
|
+
// =========================
|
|
3
|
+
// IMPORTS
|
|
4
|
+
// =========================
|
|
5
|
+
import { CaretRight } from "phosphor-svelte";
|
|
6
|
+
import { cn } from "../../lib/utils.js";
|
|
7
7
|
|
|
8
|
-
// =========================
|
|
9
|
-
// TYPES
|
|
10
|
-
// =========================
|
|
11
|
-
interface Props {
|
|
12
|
-
|
|
13
|
-
}
|
|
8
|
+
// =========================
|
|
9
|
+
// TYPES
|
|
10
|
+
// =========================
|
|
11
|
+
interface Props {
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
14
|
|
|
15
|
-
// =========================
|
|
16
|
-
// PROPS
|
|
17
|
-
// =========================
|
|
18
|
-
let { class: className }: Props = $props();
|
|
15
|
+
// =========================
|
|
16
|
+
// PROPS
|
|
17
|
+
// =========================
|
|
18
|
+
let { class: className }: Props = $props();
|
|
19
19
|
</script>
|
|
20
20
|
|
|
21
21
|
<!-- =========================== -->
|
|
22
22
|
<!-- MARKUP -->
|
|
23
23
|
<!-- =========================== -->
|
|
24
24
|
<CaretRight
|
|
25
|
-
class={cn(
|
|
25
|
+
class={cn(
|
|
26
|
+
"size-3.5 text-muted-foreground opacity-0 transition-opacity group-hover/row:opacity-100",
|
|
27
|
+
className,
|
|
28
|
+
)}
|
|
26
29
|
weight="bold"
|
|
27
30
|
aria-hidden="true"
|
|
28
31
|
/>
|