cortena-ui 1.3.0 → 1.4.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/README.md +74 -3
- package/dist/a2ui/catalogue-doc.d.ts +44 -0
- package/dist/a2ui/catalogue-doc.js +169 -0
- package/dist/a2ui/catalogue-doc.js.map +1 -0
- package/dist/a2ui/catalogue.d.ts +44 -0
- package/dist/a2ui/catalogue.js +942 -0
- package/dist/a2ui/catalogue.js.map +1 -0
- package/dist/a2ui/index.d.ts +7 -0
- package/dist/a2ui/message.d.ts +232 -0
- package/dist/a2ui/message.js +365 -0
- package/dist/a2ui/message.js.map +1 -0
- package/dist/a2ui/renderer.d.ts +46 -0
- package/dist/a2ui/renderer.js +252 -0
- package/dist/a2ui/renderer.js.map +1 -0
- package/dist/a2ui/views.d.ts +80 -0
- package/dist/a2ui/views.js +596 -0
- package/dist/a2ui/views.js.map +1 -0
- package/dist/a2ui.d.ts +8 -0
- package/dist/a2ui.js +7 -0
- package/dist/chart.d.ts +9 -0
- package/dist/chart.js +9 -0
- package/dist/components/accordion.d.ts +22 -0
- package/dist/components/accordion.js +63 -0
- package/dist/components/accordion.js.map +1 -0
- package/dist/components/alert.d.ts +15 -0
- package/dist/components/alert.js +64 -0
- package/dist/components/alert.js.map +1 -0
- package/dist/components/avatar.d.ts +18 -0
- package/dist/components/avatar.js +40 -0
- package/dist/components/avatar.js.map +1 -0
- package/dist/components/badge.d.ts +12 -0
- package/dist/components/badge.js +47 -0
- package/dist/components/badge.js.map +1 -0
- package/dist/components/breadcrumb.d.ts +71 -0
- package/dist/components/breadcrumb.js +123 -0
- package/dist/components/breadcrumb.js.map +1 -0
- package/dist/components/button-link.d.ts +28 -0
- package/dist/components/button-link.js +24 -0
- package/dist/components/button-link.js.map +1 -0
- package/dist/components/button.d.ts +24 -0
- package/dist/components/button.js +65 -0
- package/dist/components/button.js.map +1 -0
- package/dist/components/calendar.d.ts +28 -0
- package/dist/components/calendar.js +123 -0
- package/dist/components/calendar.js.map +1 -0
- package/dist/components/card.d.ts +32 -0
- package/dist/components/card.js +57 -0
- package/dist/components/card.js.map +1 -0
- package/dist/components/chart/chart.d.ts +71 -0
- package/dist/components/chart/chart.js +167 -0
- package/dist/components/chart/chart.js.map +1 -0
- package/dist/components/chart/container.d.ts +88 -0
- package/dist/components/chart/container.js +189 -0
- package/dist/components/chart/container.js.map +1 -0
- package/dist/components/chart/data.js +128 -0
- package/dist/components/chart/data.js.map +1 -0
- package/dist/components/chart/index.d.ts +9 -0
- package/dist/components/chart/index.js +10 -0
- package/dist/components/chart/nivo-charts.js +225 -0
- package/dist/components/chart/nivo-charts.js.map +1 -0
- package/dist/components/chart/nivo-lazy.js +50 -0
- package/dist/components/chart/nivo-lazy.js.map +1 -0
- package/dist/components/chart/nivo-theme.d.ts +156 -0
- package/dist/components/chart/nivo-theme.js +161 -0
- package/dist/components/chart/nivo-theme.js.map +1 -0
- package/dist/components/chart/recharts-charts.js +276 -0
- package/dist/components/chart/recharts-charts.js.map +1 -0
- package/dist/components/chart/recharts-lazy.js +58 -0
- package/dist/components/chart/recharts-lazy.js.map +1 -0
- package/dist/components/chart/recharts-parts.d.ts +10 -0
- package/dist/components/chart/recharts-parts.js +25 -0
- package/dist/components/chart/recharts-parts.js.map +1 -0
- package/dist/components/chart/tokens.d.ts +62 -0
- package/dist/components/chart/tokens.js +192 -0
- package/dist/components/chart/tokens.js.map +1 -0
- package/dist/components/chart/types.d.ts +114 -0
- package/dist/components/chart/types.js +27 -0
- package/dist/components/chart/types.js.map +1 -0
- package/dist/components/checkbox.d.ts +20 -0
- package/dist/components/checkbox.js +24 -0
- package/dist/components/checkbox.js.map +1 -0
- package/dist/components/chip.d.ts +17 -0
- package/dist/components/chip.js +47 -0
- package/dist/components/chip.js.map +1 -0
- package/dist/components/combobox.d.ts +76 -0
- package/dist/components/combobox.js +201 -0
- package/dist/components/combobox.js.map +1 -0
- package/dist/components/command.d.ts +50 -0
- package/dist/components/command.js +117 -0
- package/dist/components/command.js.map +1 -0
- package/dist/components/data-table/data-table.d.ts +65 -0
- package/dist/components/data-table/data-table.js +588 -0
- package/dist/components/data-table/data-table.js.map +1 -0
- package/dist/components/data-table/export.d.ts +23 -0
- package/dist/components/data-table/export.js +95 -0
- package/dist/components/data-table/export.js.map +1 -0
- package/dist/components/data-table/features.d.ts +66 -0
- package/dist/components/data-table/features.js +103 -0
- package/dist/components/data-table/features.js.map +1 -0
- package/dist/components/data-table/index.d.ts +8 -0
- package/dist/components/data-table/parts.d.ts +79 -0
- package/dist/components/data-table/parts.js +400 -0
- package/dist/components/data-table/parts.js.map +1 -0
- package/dist/components/data-table/system-columns.js +84 -0
- package/dist/components/data-table/system-columns.js.map +1 -0
- package/dist/components/data-table/types.d.ts +172 -0
- package/dist/components/data-table/use-data-table.d.ts +19 -0
- package/dist/components/data-table/use-data-table.js +171 -0
- package/dist/components/data-table/use-data-table.js.map +1 -0
- package/dist/components/data-table/use-server-source.js +200 -0
- package/dist/components/data-table/use-server-source.js.map +1 -0
- package/dist/components/date-field.d.ts +54 -0
- package/dist/components/date-field.js +235 -0
- package/dist/components/date-field.js.map +1 -0
- package/dist/components/date-picker.d.ts +42 -0
- package/dist/components/date-picker.js +125 -0
- package/dist/components/date-picker.js.map +1 -0
- package/dist/components/dialog.d.ts +36 -0
- package/dist/components/dialog.js +97 -0
- package/dist/components/dialog.js.map +1 -0
- package/dist/components/dropdown-menu.d.ts +54 -0
- package/dist/components/dropdown-menu.js +191 -0
- package/dist/components/dropdown-menu.js.map +1 -0
- package/dist/components/dropzone.d.ts +48 -0
- package/dist/components/dropzone.js +185 -0
- package/dist/components/dropzone.js.map +1 -0
- package/dist/components/empty-state.d.ts +22 -0
- package/dist/components/empty-state.js +46 -0
- package/dist/components/empty-state.js.map +1 -0
- package/dist/components/error-banner.d.ts +17 -0
- package/dist/components/error-banner.js +52 -0
- package/dist/components/error-banner.js.map +1 -0
- package/dist/components/field.d.ts +64 -0
- package/dist/components/field.js +133 -0
- package/dist/components/field.js.map +1 -0
- package/dist/components/form.d.ts +37 -0
- package/dist/components/form.js +103 -0
- package/dist/components/form.js.map +1 -0
- package/dist/components/input.d.ts +17 -0
- package/dist/components/input.js +26 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/kbd.d.ts +11 -0
- package/dist/components/kbd.js +20 -0
- package/dist/components/kbd.js.map +1 -0
- package/dist/components/label.d.ts +14 -0
- package/dist/components/label.js +28 -0
- package/dist/components/label.js.map +1 -0
- package/dist/components/loading-skeleton.d.ts +22 -0
- package/dist/components/loading-skeleton.js +123 -0
- package/dist/components/loading-skeleton.js.map +1 -0
- package/dist/components/markdown.d.ts +44 -0
- package/dist/components/markdown.js +231 -0
- package/dist/components/markdown.js.map +1 -0
- package/dist/components/page-header.d.ts +26 -0
- package/dist/components/page-header.js +53 -0
- package/dist/components/page-header.js.map +1 -0
- package/dist/components/popover.d.ts +29 -0
- package/dist/components/popover.js +74 -0
- package/dist/components/popover.js.map +1 -0
- package/dist/components/progress.d.ts +22 -0
- package/dist/components/progress.js +49 -0
- package/dist/components/progress.js.map +1 -0
- package/dist/components/radio-group.d.ts +20 -0
- package/dist/components/radio-group.js +29 -0
- package/dist/components/radio-group.js.map +1 -0
- package/dist/components/scroll-area.d.ts +25 -0
- package/dist/components/scroll-area.js +39 -0
- package/dist/components/scroll-area.js.map +1 -0
- package/dist/components/section-card.d.ts +29 -0
- package/dist/components/section-card.js +55 -0
- package/dist/components/section-card.js.map +1 -0
- package/dist/components/segmented.d.ts +27 -0
- package/dist/components/segmented.js +32 -0
- package/dist/components/segmented.js.map +1 -0
- package/dist/components/select.d.ts +62 -0
- package/dist/components/select.js +97 -0
- package/dist/components/select.js.map +1 -0
- package/dist/components/separator.d.ts +19 -0
- package/dist/components/separator.js +23 -0
- package/dist/components/separator.js.map +1 -0
- package/dist/components/sheet.d.ts +35 -0
- package/dist/components/sheet.js +131 -0
- package/dist/components/sheet.js.map +1 -0
- package/dist/components/skeleton.d.ts +12 -0
- package/dist/components/skeleton.js +22 -0
- package/dist/components/skeleton.js.map +1 -0
- package/dist/components/sortable-list.d.ts +67 -0
- package/dist/components/sortable-list.js +206 -0
- package/dist/components/sortable-list.js.map +1 -0
- package/dist/components/spinner.d.ts +29 -0
- package/dist/components/spinner.js +34 -0
- package/dist/components/spinner.js.map +1 -0
- package/dist/components/status-dot.d.ts +26 -0
- package/dist/components/status-dot.js +47 -0
- package/dist/components/status-dot.js.map +1 -0
- package/dist/components/switch.d.ts +21 -0
- package/dist/components/switch.js +21 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/tabs.d.ts +24 -0
- package/dist/components/tabs.js +60 -0
- package/dist/components/tabs.js.map +1 -0
- package/dist/components/textarea.d.ts +19 -0
- package/dist/components/textarea.js +47 -0
- package/dist/components/textarea.js.map +1 -0
- package/dist/components/toast.d.ts +47 -0
- package/dist/components/toast.js +106 -0
- package/dist/components/toast.js.map +1 -0
- package/dist/components/toolbar.d.ts +19 -0
- package/dist/components/toolbar.js +45 -0
- package/dist/components/toolbar.js.map +1 -0
- package/dist/components/tooltip.d.ts +28 -0
- package/dist/components/tooltip.js +59 -0
- package/dist/components/tooltip.js.map +1 -0
- package/dist/core.d.ts +44 -0
- package/dist/core.js +44 -0
- package/dist/data-table.d.ts +9 -0
- package/dist/data-table.js +7 -0
- package/dist/form.d.ts +7 -0
- package/dist/form.js +7 -0
- package/dist/hooks/use-cortena-theme.d.ts +98 -0
- package/dist/hooks/use-cortena-theme.js +145 -0
- package/dist/hooks/use-cortena-theme.js.map +1 -0
- package/dist/index.d.ts +75 -2678
- package/dist/index.js +66 -8598
- package/dist/lib/cn.d.ts +10 -0
- package/dist/lib/cn.js +15 -0
- package/dist/lib/cn.js.map +1 -0
- package/dist/lib/render.d.ts +23 -0
- package/dist/lib/render.js +20 -0
- package/dist/lib/render.js.map +1 -0
- package/dist/lib/shakeable.js +39 -0
- package/dist/lib/shakeable.js.map +1 -0
- package/dist/markdown.d.ts +3 -0
- package/dist/markdown.js +3 -0
- package/dist/sortable-list.d.ts +3 -0
- package/dist/sortable-list.js +3 -0
- package/package.json +37 -14
- package/src/a2ui/catalogue.ts +140 -103
- package/src/a2ui/views.tsx +5 -2
- package/src/components/alert.tsx +1 -1
- package/src/components/badge.tsx +1 -1
- package/src/components/button.tsx +1 -1
- package/src/components/chart/chart.tsx +72 -25
- package/src/components/chart/container.tsx +24 -39
- package/src/components/chart/index.tsx +4 -2
- package/src/components/chart/nivo-charts.tsx +41 -13
- package/src/components/chart/nivo-lazy.tsx +52 -0
- package/src/components/chart/recharts-charts.tsx +98 -3
- package/src/components/chart/recharts-lazy.tsx +72 -0
- package/src/components/chart/recharts-parts.tsx +34 -0
- package/src/components/chart/tokens.ts +1 -1
- package/src/components/chart/types.ts +14 -0
- package/src/components/chip.tsx +1 -1
- package/src/components/combobox.tsx +2 -2
- package/src/components/data-table/export.ts +18 -2
- package/src/components/data-table/features.ts +77 -45
- package/src/components/data-table/parts.tsx +15 -0
- package/src/components/data-table/use-data-table.ts +7 -2
- package/src/components/data-table/use-server-source.ts +17 -4
- package/src/components/date-field.tsx +1 -1
- package/src/components/error-banner.tsx +1 -1
- package/src/components/field.tsx +2 -2
- package/src/components/form.tsx +2 -2
- package/src/components/input.tsx +1 -1
- package/src/components/label.tsx +1 -1
- package/src/components/markdown.tsx +5 -4
- package/src/components/sheet.tsx +2 -2
- package/src/components/sortable-list.tsx +2 -2
- package/src/components/textarea.tsx +1 -1
- package/src/components/toast.tsx +1 -1
- package/src/entries/a2ui.ts +11 -0
- package/src/entries/chart.ts +10 -0
- package/src/entries/core.ts +91 -0
- package/src/entries/data-table.ts +9 -0
- package/src/entries/form.ts +24 -0
- package/src/entries/markdown.ts +9 -0
- package/src/entries/sortable-list.ts +13 -0
- package/src/index.ts +32 -98
- package/src/lib/shakeable.ts +33 -0
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { GripVertical } from "lucide-react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { DndContext, DragOverlay, KeyboardSensor, MouseSensor, PointerSensor, TouchSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
|
|
8
|
+
import { SortableContext, arrayMove, horizontalListSortingStrategy, sortableKeyboardCoordinates, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
9
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
10
|
+
//#region src/components/sortable-list.tsx
|
|
11
|
+
/**
|
|
12
|
+
* SortableList.
|
|
13
|
+
*
|
|
14
|
+
* A reorderable list over dnd-kit: `DndContext` + `SortableContext` around one
|
|
15
|
+
* `useSortable` item per entry. The list owns nothing — it calls `onReorder`
|
|
16
|
+
* with the next array and the consumer stores it. Pointer and keyboard sensors
|
|
17
|
+
* are wired (Space picks up, arrows move, Space drops, Escape cancels) and a
|
|
18
|
+
* `DragOverlay` paints the lifted copy with `--ds-shadow-lg` while the item
|
|
19
|
+
* left behind dims and takes the ring colour.
|
|
20
|
+
*
|
|
21
|
+
* By default the whole row is the drag activator. Pass `handle` to restrict
|
|
22
|
+
* dragging to a `<SortableHandle>` (or any element given `handleProps`) so
|
|
23
|
+
* buttons and inputs inside a row stay clickable.
|
|
24
|
+
*
|
|
25
|
+
* ## Touch
|
|
26
|
+
*
|
|
27
|
+
* A pointer drag and a touch scroll start the same way, so something has to
|
|
28
|
+
* say which one a finger means. In handle mode only the small handle is
|
|
29
|
+
* `touch-none`, so the rest of the row scrolls: nothing to decide. In
|
|
30
|
+
* whole-row mode the whole row is `touch-none`, which is why a touch list
|
|
31
|
+
* whose rows are the activators cannot be scrolled by dragging over them.
|
|
32
|
+
*
|
|
33
|
+
* `touchScroll` is the way out, and it defaults to whichever of those is
|
|
34
|
+
* already true — `true` in handle mode, `false` in whole-row mode, so existing
|
|
35
|
+
* lists behave exactly as before. Setting it `true` on a whole-row list drops
|
|
36
|
+
* `touch-none` and swaps the PointerSensor for a MouseSensor plus a
|
|
37
|
+
* TouchSensor with delay activation: a quick swipe scrolls, a press held for
|
|
38
|
+
* {@link TOUCH_DELAY_MS} picks the row up. `false` in handle mode is the
|
|
39
|
+
* opposite trade — the row never scrolls under a finger.
|
|
40
|
+
*/
|
|
41
|
+
/** How long a finger must rest on a row before it becomes a drag. */
|
|
42
|
+
const TOUCH_DELAY_MS = 250;
|
|
43
|
+
/** How far it may slip in that time and still count as a press, not a scroll. */
|
|
44
|
+
const TOUCH_TOLERANCE_PX = 8;
|
|
45
|
+
/** dnd-kit types its listeners as a bag of `Function`s; narrow them to the handlers they are. */
|
|
46
|
+
function asHandlers(listeners) {
|
|
47
|
+
return listeners ?? {};
|
|
48
|
+
}
|
|
49
|
+
const noopRef = () => {};
|
|
50
|
+
const inertHandle = { ref: noopRef };
|
|
51
|
+
const SortableItemContext = /* @__PURE__ */ React.createContext({
|
|
52
|
+
handleProps: inertHandle,
|
|
53
|
+
isDragging: false,
|
|
54
|
+
disabled: false
|
|
55
|
+
});
|
|
56
|
+
const itemClassName = /* @__PURE__ */ cn("relative rounded-[var(--ds-radius-md)] outline-none", "focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", "data-[dragging]:opacity-50 data-[dragging]:ring-2 data-[dragging]:ring-[var(--ds-ring)]");
|
|
57
|
+
function SortableItem({ id, item, handle, disabled, touchScroll, renderItem, className }) {
|
|
58
|
+
const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
|
|
59
|
+
id,
|
|
60
|
+
disabled
|
|
61
|
+
});
|
|
62
|
+
const handleProps = React.useMemo(() => ({
|
|
63
|
+
...attributes,
|
|
64
|
+
...asHandlers(listeners),
|
|
65
|
+
ref: setActivatorNodeRef
|
|
66
|
+
}), [
|
|
67
|
+
attributes,
|
|
68
|
+
listeners,
|
|
69
|
+
setActivatorNodeRef
|
|
70
|
+
]);
|
|
71
|
+
const context = React.useMemo(() => ({
|
|
72
|
+
handleProps,
|
|
73
|
+
isDragging,
|
|
74
|
+
disabled
|
|
75
|
+
}), [
|
|
76
|
+
handleProps,
|
|
77
|
+
isDragging,
|
|
78
|
+
disabled
|
|
79
|
+
]);
|
|
80
|
+
const rowActivator = handle ? void 0 : {
|
|
81
|
+
...attributes,
|
|
82
|
+
...asHandlers(listeners)
|
|
83
|
+
};
|
|
84
|
+
return /* @__PURE__ */ jsx(SortableItemContext.Provider, {
|
|
85
|
+
value: context,
|
|
86
|
+
children: /* @__PURE__ */ jsx("li", {
|
|
87
|
+
ref: setNodeRef,
|
|
88
|
+
"data-slot": "sortable-item",
|
|
89
|
+
"data-dragging": isDragging || void 0,
|
|
90
|
+
"data-disabled": disabled || void 0,
|
|
91
|
+
style: {
|
|
92
|
+
transform: CSS.Translate.toString(transform),
|
|
93
|
+
transition
|
|
94
|
+
},
|
|
95
|
+
className: cn(itemClassName, !handle && !disabled && "cursor-grab data-[dragging]:cursor-grabbing", !touchScroll && !disabled && "touch-none", className),
|
|
96
|
+
...rowActivator,
|
|
97
|
+
children: renderItem(item, {
|
|
98
|
+
handleProps,
|
|
99
|
+
isDragging
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A grip button that starts the drag for its row. Reads the row's activator
|
|
106
|
+
* props from context, so it needs no wiring beyond being rendered inside
|
|
107
|
+
* `renderItem`. Hidden (kept in layout) when the list is disabled.
|
|
108
|
+
*/
|
|
109
|
+
function SortableHandle({ className, children, ...props }) {
|
|
110
|
+
const { handleProps, disabled } = React.useContext(SortableItemContext);
|
|
111
|
+
return /* @__PURE__ */ jsx("button", {
|
|
112
|
+
type: "button",
|
|
113
|
+
"data-slot": "sortable-handle",
|
|
114
|
+
"aria-label": "Drag to reorder",
|
|
115
|
+
className: cn("inline-flex size-7 shrink-0 cursor-grab touch-none items-center justify-center", "rounded-[var(--ds-radius-sm)] text-[color:var(--ds-text-tertiary)]", "hover:bg-[var(--ds-hover)] hover:text-[color:var(--ds-muted-foreground)]", "outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", "active:cursor-grabbing disabled:pointer-events-none disabled:opacity-50", "[&_svg]:size-4 [&_svg]:pointer-events-none", disabled && "invisible", className),
|
|
116
|
+
disabled,
|
|
117
|
+
...handleProps,
|
|
118
|
+
...props,
|
|
119
|
+
children: children ?? /* @__PURE__ */ jsx(GripVertical, { "aria-hidden": true })
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function SortableList({ items, getId, onReorder, renderItem, orientation = "vertical", handle = false, disabled = false, touchScroll = handle, itemClassName: itemClass, className, ...props }) {
|
|
123
|
+
const [activeId, setActiveId] = React.useState(null);
|
|
124
|
+
const delayTouch = touchScroll && !handle;
|
|
125
|
+
const pointerSensor = useSensor(PointerSensor, { activationConstraint: { distance: 4 } });
|
|
126
|
+
const mouseSensor = useSensor(MouseSensor, { activationConstraint: { distance: 4 } });
|
|
127
|
+
const touchSensor = useSensor(TouchSensor, { activationConstraint: {
|
|
128
|
+
delay: TOUCH_DELAY_MS,
|
|
129
|
+
tolerance: TOUCH_TOLERANCE_PX
|
|
130
|
+
} });
|
|
131
|
+
const keyboardSensor = useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates });
|
|
132
|
+
const pointerSensors = useSensors(pointerSensor, keyboardSensor);
|
|
133
|
+
const splitSensors = useSensors(mouseSensor, touchSensor, keyboardSensor);
|
|
134
|
+
const sensors = delayTouch ? splitSensors : pointerSensors;
|
|
135
|
+
const ids = React.useMemo(() => items.map(getId), [items, getId]);
|
|
136
|
+
const activeItem = activeId === null ? void 0 : items[ids.indexOf(activeId)];
|
|
137
|
+
const onDragStart = React.useCallback((event) => {
|
|
138
|
+
setActiveId(event.active.id);
|
|
139
|
+
}, []);
|
|
140
|
+
const onDragEnd = React.useCallback((event) => {
|
|
141
|
+
setActiveId(null);
|
|
142
|
+
const { active, over } = event;
|
|
143
|
+
if (!over || active.id === over.id) return;
|
|
144
|
+
const from = ids.indexOf(active.id);
|
|
145
|
+
const to = ids.indexOf(over.id);
|
|
146
|
+
if (from < 0 || to < 0) return;
|
|
147
|
+
onReorder(arrayMove([...items], from, to), {
|
|
148
|
+
from,
|
|
149
|
+
to,
|
|
150
|
+
activeId: active.id,
|
|
151
|
+
overId: over.id
|
|
152
|
+
});
|
|
153
|
+
}, [
|
|
154
|
+
ids,
|
|
155
|
+
items,
|
|
156
|
+
onReorder
|
|
157
|
+
]);
|
|
158
|
+
const onDragCancel = React.useCallback(() => setActiveId(null), []);
|
|
159
|
+
return /* @__PURE__ */ jsxs(DndContext, {
|
|
160
|
+
sensors,
|
|
161
|
+
collisionDetection: closestCenter,
|
|
162
|
+
onDragStart,
|
|
163
|
+
onDragEnd,
|
|
164
|
+
onDragCancel,
|
|
165
|
+
children: [/* @__PURE__ */ jsx(SortableContext, {
|
|
166
|
+
items: ids,
|
|
167
|
+
strategy: orientation === "horizontal" ? horizontalListSortingStrategy : verticalListSortingStrategy,
|
|
168
|
+
disabled,
|
|
169
|
+
children: /* @__PURE__ */ jsx("ul", {
|
|
170
|
+
"data-slot": "sortable-list",
|
|
171
|
+
"data-orientation": orientation,
|
|
172
|
+
"data-disabled": disabled || void 0,
|
|
173
|
+
"data-touch-scroll": touchScroll ? "" : void 0,
|
|
174
|
+
className: cn("flex list-none gap-2 p-0", orientation === "horizontal" ? "flex-row flex-wrap" : "flex-col", className),
|
|
175
|
+
...props,
|
|
176
|
+
children: items.map((item, index) => /* @__PURE__ */ jsx(SortableItem, {
|
|
177
|
+
id: ids[index] ?? index,
|
|
178
|
+
item,
|
|
179
|
+
handle,
|
|
180
|
+
disabled,
|
|
181
|
+
touchScroll,
|
|
182
|
+
renderItem,
|
|
183
|
+
className: itemClass
|
|
184
|
+
}, ids[index] ?? index))
|
|
185
|
+
})
|
|
186
|
+
}), /* @__PURE__ */ jsx(DragOverlay, { children: activeItem === void 0 ? null : /* @__PURE__ */ jsx(SortableItemContext.Provider, {
|
|
187
|
+
value: {
|
|
188
|
+
handleProps: inertHandle,
|
|
189
|
+
isDragging: true,
|
|
190
|
+
disabled: false
|
|
191
|
+
},
|
|
192
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
193
|
+
"data-slot": "sortable-overlay",
|
|
194
|
+
className: cn("cursor-grabbing rounded-[var(--ds-radius-md)] bg-[var(--ds-card)]", "shadow-[var(--ds-shadow-lg)] ring-1 ring-[var(--ds-ring)]", itemClass),
|
|
195
|
+
children: renderItem(activeItem, {
|
|
196
|
+
handleProps: inertHandle,
|
|
197
|
+
isDragging: true
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
}) })]
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
//#endregion
|
|
204
|
+
export { SortableHandle, SortableList, arrayMove };
|
|
205
|
+
|
|
206
|
+
//# sourceMappingURL=sortable-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sortable-list.js","names":[],"sources":["../../src/components/sortable-list.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n DndContext,\n DragOverlay,\n KeyboardSensor,\n MouseSensor,\n PointerSensor,\n TouchSensor,\n closestCenter,\n useSensor,\n useSensors,\n type DragEndEvent,\n type DragStartEvent,\n type DraggableAttributes,\n type DraggableSyntheticListeners,\n type UniqueIdentifier,\n} from \"@dnd-kit/core\";\nimport {\n SortableContext,\n arrayMove,\n horizontalListSortingStrategy,\n sortableKeyboardCoordinates,\n useSortable,\n verticalListSortingStrategy,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport { GripVertical } from \"lucide-react\";\nimport * as React from \"react\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * SortableList.\n *\n * A reorderable list over dnd-kit: `DndContext` + `SortableContext` around one\n * `useSortable` item per entry. The list owns nothing — it calls `onReorder`\n * with the next array and the consumer stores it. Pointer and keyboard sensors\n * are wired (Space picks up, arrows move, Space drops, Escape cancels) and a\n * `DragOverlay` paints the lifted copy with `--ds-shadow-lg` while the item\n * left behind dims and takes the ring colour.\n *\n * By default the whole row is the drag activator. Pass `handle` to restrict\n * dragging to a `<SortableHandle>` (or any element given `handleProps`) so\n * buttons and inputs inside a row stay clickable.\n *\n * ## Touch\n *\n * A pointer drag and a touch scroll start the same way, so something has to\n * say which one a finger means. In handle mode only the small handle is\n * `touch-none`, so the rest of the row scrolls: nothing to decide. In\n * whole-row mode the whole row is `touch-none`, which is why a touch list\n * whose rows are the activators cannot be scrolled by dragging over them.\n *\n * `touchScroll` is the way out, and it defaults to whichever of those is\n * already true — `true` in handle mode, `false` in whole-row mode, so existing\n * lists behave exactly as before. Setting it `true` on a whole-row list drops\n * `touch-none` and swaps the PointerSensor for a MouseSensor plus a\n * TouchSensor with delay activation: a quick swipe scrolls, a press held for\n * {@link TOUCH_DELAY_MS} picks the row up. `false` in handle mode is the\n * opposite trade — the row never scrolls under a finger.\n */\n\n/** How long a finger must rest on a row before it becomes a drag. */\nconst TOUCH_DELAY_MS = 250;\n/** How far it may slip in that time and still count as a press, not a scroll. */\nconst TOUCH_TOLERANCE_PX = 8;\n\n/** Spread onto the element that should start a drag. */\nexport interface SortableHandleProps extends Partial<DraggableAttributes> {\n ref: (element: HTMLElement | null) => void;\n onKeyDown?: React.KeyboardEventHandler<HTMLElement>;\n onPointerDown?: React.PointerEventHandler<HTMLElement>;\n onMouseDown?: React.MouseEventHandler<HTMLElement>;\n onTouchStart?: React.TouchEventHandler<HTMLElement>;\n}\n\n/** dnd-kit types its listeners as a bag of `Function`s; narrow them to the handlers they are. */\nfunction asHandlers(listeners: DraggableSyntheticListeners): Partial<SortableHandleProps> {\n return (listeners ?? {}) as Partial<SortableHandleProps>;\n}\n\nexport interface SortableRenderState {\n /** Attributes, listeners and ref for a custom drag handle. */\n handleProps: SortableHandleProps;\n /** True for the item being dragged (and for the overlay copy). */\n isDragging: boolean;\n}\n\n/** What moved where, passed to `onReorder` alongside the reordered array. */\nexport interface SortableReorderDetails {\n /** Index the item came from, in the array as it was passed in. */\n from: number;\n /** Index it landed on. */\n to: number;\n /** Id of the item that moved (`getId` of the dragged item). */\n activeId: UniqueIdentifier;\n /** Id of the item it was dropped onto. */\n overId: UniqueIdentifier;\n}\n\nexport interface SortableListProps<T> extends Omit<React.ComponentProps<\"ul\">, \"children\"> {\n items: readonly T[];\n /** Stable id per item; must be unique within the list. */\n getId: (item: T) => UniqueIdentifier;\n /**\n * Called with the reordered array after a successful drop, and with the move\n * itself — `{ from, to, activeId, overId }` — for a server that persists a\n * position rather than the whole list.\n */\n onReorder: (next: T[], details: SortableReorderDetails) => void;\n renderItem: (item: T, state: SortableRenderState) => React.ReactNode;\n /** Layout axis; also selects the sorting strategy. */\n orientation?: \"vertical\" | \"horizontal\";\n /** Only a `<SortableHandle>` (or an element given `handleProps`) starts a drag. */\n handle?: boolean;\n /**\n * Let a finger scroll the page over the rows. Defaults to `handle`: `true`\n * in handle mode, `false` in whole-row mode, which is what each already did.\n * `true` on a whole-row list drops `touch-none` and uses a TouchSensor with\n * delay activation, so a swipe scrolls and a held press drags.\n */\n touchScroll?: boolean;\n /** Disables every sensor and hides the handles. */\n disabled?: boolean;\n /** Class name for each item wrapper. */\n itemClassName?: string;\n}\n\ninterface ItemContextValue {\n handleProps: SortableHandleProps;\n isDragging: boolean;\n disabled: boolean;\n}\n\nconst noopRef = () => {};\nconst inertHandle: SortableHandleProps = { ref: noopRef };\n\nconst SortableItemContext = /* @__PURE__ */ React.createContext<ItemContextValue>({\n handleProps: inertHandle,\n isDragging: false,\n disabled: false,\n});\n\nconst itemClassName = /* @__PURE__ */ cn(\n \"relative rounded-[var(--ds-radius-md)] outline-none\",\n \"focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40\",\n \"data-[dragging]:opacity-50 data-[dragging]:ring-2 data-[dragging]:ring-[var(--ds-ring)]\",\n);\n\ninterface SortableItemProps<T> {\n id: UniqueIdentifier;\n item: T;\n handle: boolean;\n disabled: boolean;\n touchScroll: boolean;\n renderItem: SortableListProps<T>[\"renderItem\"];\n className: string | undefined;\n}\n\nfunction SortableItem<T>({ id, item, handle, disabled, touchScroll, renderItem, className }: SortableItemProps<T>) {\n const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } =\n useSortable({ id, disabled });\n\n const handleProps = React.useMemo<SortableHandleProps>(\n () => ({ ...attributes, ...asHandlers(listeners), ref: setActivatorNodeRef }),\n [attributes, listeners, setActivatorNodeRef],\n );\n const context = React.useMemo<ItemContextValue>(\n () => ({ handleProps, isDragging, disabled }),\n [handleProps, isDragging, disabled],\n );\n const rowActivator = handle ? undefined : { ...attributes, ...asHandlers(listeners) };\n\n return (\n <SortableItemContext.Provider value={context}>\n <li\n ref={setNodeRef}\n data-slot=\"sortable-item\"\n data-dragging={isDragging || undefined}\n data-disabled={disabled || undefined}\n style={{ transform: CSS.Translate.toString(transform), transition }}\n className={cn(\n itemClassName,\n !handle && !disabled && \"cursor-grab data-[dragging]:cursor-grabbing\",\n // touch-action is what decides whether a finger scrolls or drags.\n !touchScroll && !disabled && \"touch-none\",\n className,\n )}\n {...rowActivator}\n >\n {renderItem(item, { handleProps, isDragging })}\n </li>\n </SortableItemContext.Provider>\n );\n}\n\n/**\n * A grip button that starts the drag for its row. Reads the row's activator\n * props from context, so it needs no wiring beyond being rendered inside\n * `renderItem`. Hidden (kept in layout) when the list is disabled.\n */\nfunction SortableHandle({ className, children, ...props }: React.ComponentProps<\"button\">) {\n const { handleProps, disabled } = React.useContext(SortableItemContext);\n return (\n <button\n type=\"button\"\n data-slot=\"sortable-handle\"\n aria-label=\"Drag to reorder\"\n className={cn(\n \"inline-flex size-7 shrink-0 cursor-grab touch-none items-center justify-center\",\n \"rounded-[var(--ds-radius-sm)] text-[color:var(--ds-text-tertiary)]\",\n \"hover:bg-[var(--ds-hover)] hover:text-[color:var(--ds-muted-foreground)]\",\n \"outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40\",\n \"active:cursor-grabbing disabled:pointer-events-none disabled:opacity-50\",\n \"[&_svg]:size-4 [&_svg]:pointer-events-none\",\n disabled && \"invisible\",\n className,\n )}\n disabled={disabled}\n {...handleProps}\n {...props}\n >\n {children ?? <GripVertical aria-hidden />}\n </button>\n );\n}\n\nfunction SortableList<T>({\n items,\n getId,\n onReorder,\n renderItem,\n orientation = \"vertical\",\n handle = false,\n disabled = false,\n touchScroll = handle,\n itemClassName: itemClass,\n className,\n ...props\n}: SortableListProps<T>) {\n const [activeId, setActiveId] = React.useState<UniqueIdentifier | null>(null);\n // Only a whole-row list needs the split sensors: in handle mode the row is\n // already scrollable because only the handle is touch-none, and swapping\n // sensors there would put a 250 ms delay on the handle for no gain.\n const delayTouch = touchScroll && !handle;\n // Every sensor is created on every render (they are hooks); which ones are\n // handed to DndContext is what changes.\n const pointerSensor = useSensor(PointerSensor, { activationConstraint: { distance: 4 } });\n const mouseSensor = useSensor(MouseSensor, { activationConstraint: { distance: 4 } });\n const touchSensor = useSensor(TouchSensor, {\n activationConstraint: { delay: TOUCH_DELAY_MS, tolerance: TOUCH_TOLERANCE_PX },\n });\n const keyboardSensor = useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates });\n const pointerSensors = useSensors(pointerSensor, keyboardSensor);\n const splitSensors = useSensors(mouseSensor, touchSensor, keyboardSensor);\n const sensors = delayTouch ? splitSensors : pointerSensors;\n const ids = React.useMemo(() => items.map(getId), [items, getId]);\n const activeItem = activeId === null ? undefined : items[ids.indexOf(activeId)];\n\n const onDragStart = React.useCallback((event: DragStartEvent) => {\n setActiveId(event.active.id);\n }, []);\n const onDragEnd = React.useCallback(\n (event: DragEndEvent) => {\n setActiveId(null);\n const { active, over } = event;\n if (!over || active.id === over.id) return;\n const from = ids.indexOf(active.id);\n const to = ids.indexOf(over.id);\n if (from < 0 || to < 0) return;\n onReorder(arrayMove([...items], from, to), {\n from,\n to,\n activeId: active.id,\n overId: over.id,\n });\n },\n [ids, items, onReorder],\n );\n const onDragCancel = React.useCallback(() => setActiveId(null), []);\n\n return (\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragStart={onDragStart}\n onDragEnd={onDragEnd}\n onDragCancel={onDragCancel}\n >\n <SortableContext\n items={ids}\n strategy={orientation === \"horizontal\" ? horizontalListSortingStrategy : verticalListSortingStrategy}\n disabled={disabled}\n >\n <ul\n data-slot=\"sortable-list\"\n data-orientation={orientation}\n data-disabled={disabled || undefined}\n data-touch-scroll={touchScroll ? \"\" : undefined}\n className={cn(\n \"flex list-none gap-2 p-0\",\n orientation === \"horizontal\" ? \"flex-row flex-wrap\" : \"flex-col\",\n className,\n )}\n {...props}\n >\n {items.map((item, index) => (\n <SortableItem\n key={ids[index] ?? index}\n id={ids[index] ?? index}\n item={item}\n handle={handle}\n disabled={disabled}\n touchScroll={touchScroll}\n renderItem={renderItem}\n className={itemClass}\n />\n ))}\n </ul>\n </SortableContext>\n <DragOverlay>\n {activeItem === undefined ? null : (\n <SortableItemContext.Provider\n value={{ handleProps: inertHandle, isDragging: true, disabled: false }}\n >\n <div\n data-slot=\"sortable-overlay\"\n className={cn(\n \"cursor-grabbing rounded-[var(--ds-radius-md)] bg-[var(--ds-card)]\",\n \"shadow-[var(--ds-shadow-lg)] ring-1 ring-[var(--ds-ring)]\",\n itemClass,\n )}\n >\n {renderItem(activeItem, { handleProps: inertHandle, isDragging: true })}\n </div>\n </SortableItemContext.Provider>\n )}\n </DragOverlay>\n </DndContext>\n );\n}\n\nexport { SortableList, SortableHandle, arrayMove };\n"],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
//#region src/components/spinner.d.ts
|
|
4
|
+
export interface SpinnerProps extends React.ComponentProps<"span"> {
|
|
5
|
+
/** Diameter in px. Defaults to 16. */
|
|
6
|
+
size?: number;
|
|
7
|
+
/**
|
|
8
|
+
* `inverse` for a spinner on a filled surface — inside a primary or
|
|
9
|
+
* destructive Button, on a toast, on any `-foreground` pairing. The default
|
|
10
|
+
* ring is `--ds-border` with a `--ds-primary` leading edge, which all but
|
|
11
|
+
* disappears on a primary fill; `inverse` draws both from
|
|
12
|
+
* `--ds-primary-foreground` instead.
|
|
13
|
+
*/
|
|
14
|
+
tone?: "default" | "inverse";
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Spinner — a ring with a coloured leading edge. Announces itself as
|
|
18
|
+
* `role="status"` with a "Loading" label; pass `aria-label` to override.
|
|
19
|
+
*
|
|
20
|
+
* ```tsx
|
|
21
|
+
* <Button disabled>
|
|
22
|
+
* <Spinner tone="inverse" /> Saving…
|
|
23
|
+
* </Button>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
declare function Spinner({ className, size, tone, style, ...props }: SpinnerProps): React.JSX.Element;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { Spinner };
|
|
29
|
+
//# sourceMappingURL=spinner.d.ts.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/spinner.tsx
|
|
6
|
+
/**
|
|
7
|
+
* Spinner — a ring with a coloured leading edge. Announces itself as
|
|
8
|
+
* `role="status"` with a "Loading" label; pass `aria-label` to override.
|
|
9
|
+
*
|
|
10
|
+
* ```tsx
|
|
11
|
+
* <Button disabled>
|
|
12
|
+
* <Spinner tone="inverse" /> Saving…
|
|
13
|
+
* </Button>
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
function Spinner({ className, size = 16, tone = "default", style, ...props }) {
|
|
17
|
+
return /* @__PURE__ */ jsx("span", {
|
|
18
|
+
"data-slot": "spinner",
|
|
19
|
+
"data-tone": tone,
|
|
20
|
+
role: "status",
|
|
21
|
+
"aria-label": "Loading",
|
|
22
|
+
className: cn("inline-block shrink-0 animate-spin rounded-[var(--ds-radius-full)] border-2", tone === "inverse" ? "border-[var(--ds-primary-foreground)]/35 border-t-[var(--ds-primary-foreground)]" : "border-[var(--ds-border)] border-t-[var(--ds-primary)]", className),
|
|
23
|
+
style: {
|
|
24
|
+
width: size,
|
|
25
|
+
height: size,
|
|
26
|
+
...style
|
|
27
|
+
},
|
|
28
|
+
...props
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { Spinner };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=spinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.js","names":[],"sources":["../../src/components/spinner.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface SpinnerProps extends React.ComponentProps<\"span\"> {\n /** Diameter in px. Defaults to 16. */\n size?: number;\n /**\n * `inverse` for a spinner on a filled surface — inside a primary or\n * destructive Button, on a toast, on any `-foreground` pairing. The default\n * ring is `--ds-border` with a `--ds-primary` leading edge, which all but\n * disappears on a primary fill; `inverse` draws both from\n * `--ds-primary-foreground` instead.\n */\n tone?: \"default\" | \"inverse\";\n}\n\n/**\n * Spinner — a ring with a coloured leading edge. Announces itself as\n * `role=\"status\"` with a \"Loading\" label; pass `aria-label` to override.\n *\n * ```tsx\n * <Button disabled>\n * <Spinner tone=\"inverse\" /> Saving…\n * </Button>\n * ```\n */\nfunction Spinner({ className, size = 16, tone = \"default\", style, ...props }: SpinnerProps) {\n return (\n <span\n data-slot=\"spinner\"\n data-tone={tone}\n role=\"status\"\n aria-label=\"Loading\"\n className={cn(\n \"inline-block shrink-0 animate-spin rounded-[var(--ds-radius-full)] border-2\",\n tone === \"inverse\"\n ? \"border-[var(--ds-primary-foreground)]/35 border-t-[var(--ds-primary-foreground)]\"\n : \"border-[var(--ds-border)] border-t-[var(--ds-primary)]\",\n className,\n )}\n style={{ width: size, height: size, ...style }}\n {...props}\n />\n );\n}\n\nexport { Spinner };\n"],"mappings":";;;;;;;;;;;;;;;AA4BA,SAAS,QAAQ,EAAE,WAAW,OAAO,IAAI,OAAO,WAAW,OAAO,GAAG,SAAuB;CAC1F,OACE,oBAAC,QAAD;EACE,aAAU;EACV,aAAW;EACX,MAAK;EACL,cAAW;EACX,WAAW,GACT,+EACA,SAAS,YACL,qFACA,0DACJ,SACF;EACA,OAAO;GAAE,OAAO;GAAM,QAAQ;GAAM,GAAG;EAAM;EAC7C,GAAI;CACL,CAAA;AAEL"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
//#region src/components/status-dot.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* The error tone is `destructive`, the same spelling Button, Alert and Badge
|
|
6
|
+
* use. `danger` is a deprecated alias of it and paints the same.
|
|
7
|
+
*/
|
|
8
|
+
type StatusDotTone = "accent" | "success" | "warning" | "destructive" |
|
|
9
|
+
/** @deprecated Use `destructive`; this alias paints the same. */
|
|
10
|
+
"danger" | "info" | "neutral";
|
|
11
|
+
export interface StatusDotProps extends Omit<React.ComponentProps<"span">, "children"> {
|
|
12
|
+
tone?: StatusDotTone;
|
|
13
|
+
/** Adds a pinging halo behind the dot for "currently active" states. */
|
|
14
|
+
live?: boolean;
|
|
15
|
+
label?: React.ReactNode;
|
|
16
|
+
/** Dot diameter in px. Defaults to 7. */
|
|
17
|
+
size?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* StatusDot — a coloured dot with an optional caption. `live` layers a ping
|
|
21
|
+
* ring in the same tone behind the dot.
|
|
22
|
+
*/
|
|
23
|
+
declare function StatusDot({ className, tone, live, label, size, style, ...props }: StatusDotProps): React.JSX.Element;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { StatusDot, type StatusDotTone };
|
|
26
|
+
//# sourceMappingURL=status-dot.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/status-dot.tsx
|
|
6
|
+
const TONE_CLASS = {
|
|
7
|
+
accent: "bg-[var(--ds-primary)]",
|
|
8
|
+
success: "bg-[var(--ds-success)]",
|
|
9
|
+
warning: "bg-[var(--ds-warning)]",
|
|
10
|
+
destructive: "bg-[var(--ds-destructive)]",
|
|
11
|
+
danger: "bg-[var(--ds-destructive)]",
|
|
12
|
+
info: "bg-[var(--ds-info)]",
|
|
13
|
+
neutral: "bg-[var(--ds-text-tertiary)]"
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* StatusDot — a coloured dot with an optional caption. `live` layers a ping
|
|
17
|
+
* ring in the same tone behind the dot.
|
|
18
|
+
*/
|
|
19
|
+
function StatusDot({ className, tone = "accent", live, label, size = 7, style, ...props }) {
|
|
20
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
21
|
+
"data-slot": "status-dot",
|
|
22
|
+
"data-tone": tone,
|
|
23
|
+
className: cn("inline-flex items-center gap-1.5", "text-[length:var(--ds-text-caption)] text-[color:var(--ds-muted-foreground)]", className),
|
|
24
|
+
...props,
|
|
25
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
26
|
+
"aria-hidden": true,
|
|
27
|
+
"data-slot": "status-dot-indicator",
|
|
28
|
+
className: "relative inline-flex shrink-0",
|
|
29
|
+
style: {
|
|
30
|
+
width: size,
|
|
31
|
+
height: size,
|
|
32
|
+
...style
|
|
33
|
+
},
|
|
34
|
+
children: [live ? /* @__PURE__ */ jsx("span", {
|
|
35
|
+
"data-slot": "status-dot-ping",
|
|
36
|
+
className: cn("absolute inset-0 animate-ping rounded-[var(--ds-radius-full)] opacity-60", TONE_CLASS[tone])
|
|
37
|
+
}) : null, /* @__PURE__ */ jsx("span", {
|
|
38
|
+
"data-slot": "status-dot-fill",
|
|
39
|
+
className: cn("relative inline-block size-full rounded-[var(--ds-radius-full)]", TONE_CLASS[tone])
|
|
40
|
+
})]
|
|
41
|
+
}), label]
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { StatusDot };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=status-dot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-dot.js","names":[],"sources":["../../src/components/status-dot.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * The error tone is `destructive`, the same spelling Button, Alert and Badge\n * use. `danger` is a deprecated alias of it and paints the same.\n */\ntype StatusDotTone =\n | \"accent\"\n | \"success\"\n | \"warning\"\n | \"destructive\"\n /** @deprecated Use `destructive`; this alias paints the same. */\n | \"danger\"\n | \"info\"\n | \"neutral\";\n\nconst TONE_CLASS: Record<StatusDotTone, string> = {\n accent: \"bg-[var(--ds-primary)]\",\n success: \"bg-[var(--ds-success)]\",\n warning: \"bg-[var(--ds-warning)]\",\n destructive: \"bg-[var(--ds-destructive)]\",\n danger: \"bg-[var(--ds-destructive)]\",\n info: \"bg-[var(--ds-info)]\",\n neutral: \"bg-[var(--ds-text-tertiary)]\",\n};\n\nexport interface StatusDotProps extends Omit<React.ComponentProps<\"span\">, \"children\"> {\n tone?: StatusDotTone;\n /** Adds a pinging halo behind the dot for \"currently active\" states. */\n live?: boolean;\n label?: React.ReactNode;\n /** Dot diameter in px. Defaults to 7. */\n size?: number;\n}\n\n/**\n * StatusDot — a coloured dot with an optional caption. `live` layers a ping\n * ring in the same tone behind the dot.\n */\nfunction StatusDot({\n className,\n tone = \"accent\",\n live,\n label,\n size = 7,\n style,\n ...props\n}: StatusDotProps) {\n return (\n <span\n data-slot=\"status-dot\"\n data-tone={tone}\n className={cn(\n \"inline-flex items-center gap-1.5\",\n \"text-[length:var(--ds-text-caption)] text-[color:var(--ds-muted-foreground)]\",\n className,\n )}\n {...props}\n >\n <span\n aria-hidden\n data-slot=\"status-dot-indicator\"\n className=\"relative inline-flex shrink-0\"\n style={{ width: size, height: size, ...style }}\n >\n {live ? (\n <span\n data-slot=\"status-dot-ping\"\n className={cn(\n \"absolute inset-0 animate-ping rounded-[var(--ds-radius-full)] opacity-60\",\n TONE_CLASS[tone],\n )}\n />\n ) : null}\n <span\n data-slot=\"status-dot-fill\"\n className={cn(\n \"relative inline-block size-full rounded-[var(--ds-radius-full)]\",\n TONE_CLASS[tone],\n )}\n />\n </span>\n {label}\n </span>\n );\n}\n\nexport { StatusDot, type StatusDotTone };\n"],"mappings":";;;;;AAmBA,MAAM,aAA4C;CAChD,QAAQ;CACR,SAAS;CACT,SAAS;CACT,aAAa;CACb,QAAQ;CACR,MAAM;CACN,SAAS;AACX;;;;;AAeA,SAAS,UAAU,EACjB,WACA,OAAO,UACP,MACA,OACA,OAAO,GACP,OACA,GAAG,SACc;CACjB,OACE,qBAAC,QAAD;EACE,aAAU;EACV,aAAW;EACX,WAAW,GACT,oCACA,gFACA,SACF;EACA,GAAI;EARN,UAAA,CAUE,qBAAC,QAAD;GACE,eAAA;GACA,aAAU;GACV,WAAU;GACV,OAAO;IAAE,OAAO;IAAM,QAAQ;IAAM,GAAG;GAAM;GAJ/C,UAAA,CAMG,OACC,oBAAC,QAAD;IACE,aAAU;IACV,WAAW,GACT,4EACA,WAAW,KACb;GACD,CAAA,IACC,MACJ,oBAAC,QAAD;IACE,aAAU;IACV,WAAW,GACT,mEACA,WAAW,KACb;GACD,CAAA,CACG;EACL,CAAA,GAAA,KACG;;AAEV"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { Switch } from "@base-ui/react/switch";
|
|
4
|
+
//#region src/components/switch.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Switch.
|
|
7
|
+
*
|
|
8
|
+
* Base UI renders a `<span role="switch">` with a hidden `<input>` beside it,
|
|
9
|
+
* so the control submits with forms and takes `checked`/`defaultChecked`
|
|
10
|
+
* exactly like a checkbox. State is exposed as `data-checked` /
|
|
11
|
+
* `data-unchecked` / `data-disabled`, which is what the classes key on.
|
|
12
|
+
*
|
|
13
|
+
* cortenaweb users: `checked` and `onCheckedChange` keep their names, but the
|
|
14
|
+
* control is now uncontrolled by default; pass `defaultChecked` if you do not
|
|
15
|
+
* hold the state yourself.
|
|
16
|
+
*/
|
|
17
|
+
export interface SwitchProps extends React.ComponentProps<typeof Switch.Root> {}
|
|
18
|
+
declare function Switch$1({ className, ...props }: SwitchProps): React.JSX.Element;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { Switch$1 as Switch };
|
|
21
|
+
//# sourceMappingURL=switch.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import { Switch } from "@base-ui/react/switch";
|
|
6
|
+
//#region src/components/switch.tsx
|
|
7
|
+
function Switch$1({ className, ...props }) {
|
|
8
|
+
return /* @__PURE__ */ jsx(Switch.Root, {
|
|
9
|
+
"data-slot": "switch",
|
|
10
|
+
className: cn("relative inline-flex h-5 w-9 shrink-0 items-center rounded-[var(--ds-radius-full)] border", "transition-[background-color,border-color,box-shadow] duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]", "outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", "data-[unchecked]:border-[var(--ds-border)] data-[unchecked]:bg-[var(--ds-input)]", "data-[checked]:border-[var(--ds-primary)] data-[checked]:bg-[var(--ds-primary)]", "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className),
|
|
11
|
+
...props,
|
|
12
|
+
children: /* @__PURE__ */ jsx(Switch.Thumb, {
|
|
13
|
+
"data-slot": "switch-thumb",
|
|
14
|
+
className: cn("pointer-events-none block size-3.5 rounded-[var(--ds-radius-full)] bg-[var(--ds-primary-foreground)]", "transition-transform duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]", "data-[unchecked]:translate-x-0.5 data-[checked]:translate-x-4.5")
|
|
15
|
+
})
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
export { Switch$1 as Switch };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=switch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"switch.js","names":["Switch","BaseSwitch"],"sources":["../../src/components/switch.tsx"],"sourcesContent":["\"use client\";\n\nimport { Switch as BaseSwitch } from \"@base-ui/react/switch\";\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * Switch.\n *\n * Base UI renders a `<span role=\"switch\">` with a hidden `<input>` beside it,\n * so the control submits with forms and takes `checked`/`defaultChecked`\n * exactly like a checkbox. State is exposed as `data-checked` /\n * `data-unchecked` / `data-disabled`, which is what the classes key on.\n *\n * cortenaweb users: `checked` and `onCheckedChange` keep their names, but the\n * control is now uncontrolled by default; pass `defaultChecked` if you do not\n * hold the state yourself.\n */\nexport interface SwitchProps extends React.ComponentProps<typeof BaseSwitch.Root> {}\n\nfunction Switch({ className, ...props }: SwitchProps) {\n return (\n <BaseSwitch.Root\n data-slot=\"switch\"\n className={cn(\n \"relative inline-flex h-5 w-9 shrink-0 items-center rounded-[var(--ds-radius-full)] border\",\n \"transition-[background-color,border-color,box-shadow] duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]\",\n \"outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40\",\n \"data-[unchecked]:border-[var(--ds-border)] data-[unchecked]:bg-[var(--ds-input)]\",\n \"data-[checked]:border-[var(--ds-primary)] data-[checked]:bg-[var(--ds-primary)]\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className,\n )}\n {...props}\n >\n <BaseSwitch.Thumb\n data-slot=\"switch-thumb\"\n className={cn(\n \"pointer-events-none block size-3.5 rounded-[var(--ds-radius-full)] bg-[var(--ds-primary-foreground)]\",\n \"transition-transform duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]\",\n \"data-[unchecked]:translate-x-0.5 data-[checked]:translate-x-4.5\",\n )}\n />\n </BaseSwitch.Root>\n );\n}\n\nexport { Switch };\n"],"mappings":";;;;;;AAoBA,SAASA,SAAO,EAAE,WAAW,GAAG,SAAsB;CACpD,OACE,oBAACC,OAAW,MAAZ;EACE,aAAU;EACV,WAAW,GACT,6FACA,sHACA,gFACA,oFACA,mFACA,kEACA,SACF;EACA,GAAI;EAEJ,UAAA,oBAACA,OAAW,OAAZ;GACE,aAAU;GACV,WAAW,GACT,wGACA,qFACA,iEACF;EACD,CAAA;CACc,CAAA;AAErB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Tabs } from "@base-ui/react/tabs";
|
|
3
|
+
//#region src/components/tabs.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Tabs — the underlined pattern (primary-coloured underline under the active
|
|
6
|
+
* tab). For the inset segmented style use Segmented.
|
|
7
|
+
*
|
|
8
|
+
* The underline is a Base UI `Tabs.Indicator` rendered inside `TabsList`, so it
|
|
9
|
+
* slides between tabs instead of snapping. Base UI exposes the active tab's
|
|
10
|
+
* box as `--active-tab-left` / `--active-tab-width` on the indicator.
|
|
11
|
+
*
|
|
12
|
+
* Radix users: `data-state="active"` is now `data-active`; `TabsTrigger` and
|
|
13
|
+
* `TabsContent` are Base UI's `Tab` and `Panel` under the shadcn names. Arrow
|
|
14
|
+
* keys activate as they move (Radix's `activationMode="automatic"`); pass
|
|
15
|
+
* `activateOnFocus={false}` on `TabsList` for manual activation.
|
|
16
|
+
*/
|
|
17
|
+
declare function Tabs$1({ className, ...props }: Tabs.Root.Props): import("react").JSX.Element;
|
|
18
|
+
declare function TabsList({ className, children, activateOnFocus, ...props }: Tabs.List.Props): import("react").JSX.Element;
|
|
19
|
+
declare function TabsIndicator({ className, ...props }: Tabs.Indicator.Props): import("react").JSX.Element;
|
|
20
|
+
declare function TabsTrigger({ className, ...props }: Tabs.Tab.Props): import("react").JSX.Element;
|
|
21
|
+
declare function TabsContent({ className, ...props }: Tabs.Panel.Props): import("react").JSX.Element;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { Tabs$1 as Tabs, TabsContent, TabsIndicator, TabsList, TabsTrigger };
|
|
24
|
+
//# sourceMappingURL=tabs.d.ts.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { Tabs } from "@base-ui/react/tabs";
|
|
6
|
+
//#region src/components/tabs.tsx
|
|
7
|
+
/**
|
|
8
|
+
* Tabs — the underlined pattern (primary-coloured underline under the active
|
|
9
|
+
* tab). For the inset segmented style use Segmented.
|
|
10
|
+
*
|
|
11
|
+
* The underline is a Base UI `Tabs.Indicator` rendered inside `TabsList`, so it
|
|
12
|
+
* slides between tabs instead of snapping. Base UI exposes the active tab's
|
|
13
|
+
* box as `--active-tab-left` / `--active-tab-width` on the indicator.
|
|
14
|
+
*
|
|
15
|
+
* Radix users: `data-state="active"` is now `data-active`; `TabsTrigger` and
|
|
16
|
+
* `TabsContent` are Base UI's `Tab` and `Panel` under the shadcn names. Arrow
|
|
17
|
+
* keys activate as they move (Radix's `activationMode="automatic"`); pass
|
|
18
|
+
* `activateOnFocus={false}` on `TabsList` for manual activation.
|
|
19
|
+
*/
|
|
20
|
+
function Tabs$1({ className, ...props }) {
|
|
21
|
+
return /* @__PURE__ */ jsx(Tabs.Root, {
|
|
22
|
+
"data-slot": "tabs",
|
|
23
|
+
className: cn("flex flex-col data-[orientation=vertical]:flex-row", className),
|
|
24
|
+
...props
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
function TabsList({ className, children, activateOnFocus = true, ...props }) {
|
|
28
|
+
return /* @__PURE__ */ jsxs(Tabs.List, {
|
|
29
|
+
"data-slot": "tabs-list",
|
|
30
|
+
activateOnFocus,
|
|
31
|
+
className: cn("relative inline-flex items-center gap-0.5 self-start", "border-[var(--ds-border-subtle)] text-[color:var(--ds-muted-foreground)]", "data-[orientation=horizontal]:border-b", "data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch data-[orientation=vertical]:border-r", className),
|
|
32
|
+
...props,
|
|
33
|
+
children: [children, /* @__PURE__ */ jsx(TabsIndicator, {})]
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function TabsIndicator({ className, ...props }) {
|
|
37
|
+
return /* @__PURE__ */ jsx(Tabs.Indicator, {
|
|
38
|
+
"data-slot": "tabs-indicator",
|
|
39
|
+
className: cn("absolute rounded-[var(--ds-radius-full)] bg-[var(--ds-primary)]", "transition-[width,height,translate] duration-[var(--ds-duration-base)] ease-[var(--ds-ease-out)]", "data-[orientation=horizontal]:-bottom-px data-[orientation=horizontal]:left-0 data-[orientation=horizontal]:h-0.5", "data-[orientation=horizontal]:w-[var(--active-tab-width,0px)] data-[orientation=horizontal]:translate-x-[var(--active-tab-left,0px)]", "data-[orientation=vertical]:-right-px data-[orientation=vertical]:top-0 data-[orientation=vertical]:w-0.5", "data-[orientation=vertical]:h-[var(--active-tab-height,0px)] data-[orientation=vertical]:translate-y-[var(--active-tab-top,0px)]", className),
|
|
40
|
+
...props
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function TabsTrigger({ className, ...props }) {
|
|
44
|
+
return /* @__PURE__ */ jsx(Tabs.Tab, {
|
|
45
|
+
"data-slot": "tabs-trigger",
|
|
46
|
+
className: cn("relative inline-flex items-center gap-1.5 whitespace-nowrap px-3.5 py-2", "text-[length:var(--ds-text-caption-lg)] font-medium text-[color:var(--ds-muted-foreground)]", "rounded-[var(--ds-radius-sm)] outline-none", "transition-colors duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]", "hover:text-[color:var(--ds-foreground)]", "focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", "data-[active]:text-[color:var(--ds-primary)]", "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", "[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", className),
|
|
47
|
+
...props
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function TabsContent({ className, ...props }) {
|
|
51
|
+
return /* @__PURE__ */ jsx(Tabs.Panel, {
|
|
52
|
+
"data-slot": "tabs-content",
|
|
53
|
+
className: cn("mt-3 flex-1 rounded-[var(--ds-radius-sm)] outline-none", "focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", className),
|
|
54
|
+
...props
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { Tabs$1 as Tabs, TabsContent, TabsIndicator, TabsList, TabsTrigger };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=tabs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tabs.js","names":["Tabs","TabsPrimitive"],"sources":["../../src/components/tabs.tsx"],"sourcesContent":["\"use client\";\n\nimport { Tabs as TabsPrimitive } from \"@base-ui/react/tabs\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * Tabs — the underlined pattern (primary-coloured underline under the active\n * tab). For the inset segmented style use Segmented.\n *\n * The underline is a Base UI `Tabs.Indicator` rendered inside `TabsList`, so it\n * slides between tabs instead of snapping. Base UI exposes the active tab's\n * box as `--active-tab-left` / `--active-tab-width` on the indicator.\n *\n * Radix users: `data-state=\"active\"` is now `data-active`; `TabsTrigger` and\n * `TabsContent` are Base UI's `Tab` and `Panel` under the shadcn names. Arrow\n * keys activate as they move (Radix's `activationMode=\"automatic\"`); pass\n * `activateOnFocus={false}` on `TabsList` for manual activation.\n */\nfunction Tabs({ className, ...props }: TabsPrimitive.Root.Props) {\n return (\n <TabsPrimitive.Root\n data-slot=\"tabs\"\n className={cn(\"flex flex-col data-[orientation=vertical]:flex-row\", className)}\n {...props}\n />\n );\n}\n\nfunction TabsList({\n className,\n children,\n activateOnFocus = true,\n ...props\n}: TabsPrimitive.List.Props) {\n return (\n <TabsPrimitive.List\n data-slot=\"tabs-list\"\n activateOnFocus={activateOnFocus}\n className={cn(\n \"relative inline-flex items-center gap-0.5 self-start\",\n \"border-[var(--ds-border-subtle)] text-[color:var(--ds-muted-foreground)]\",\n \"data-[orientation=horizontal]:border-b\",\n \"data-[orientation=vertical]:flex-col data-[orientation=vertical]:items-stretch data-[orientation=vertical]:border-r\",\n className,\n )}\n {...props}\n >\n {children}\n <TabsIndicator />\n </TabsPrimitive.List>\n );\n}\n\nfunction TabsIndicator({ className, ...props }: TabsPrimitive.Indicator.Props) {\n return (\n <TabsPrimitive.Indicator\n data-slot=\"tabs-indicator\"\n className={cn(\n \"absolute rounded-[var(--ds-radius-full)] bg-[var(--ds-primary)]\",\n \"transition-[width,height,translate] duration-[var(--ds-duration-base)] ease-[var(--ds-ease-out)]\",\n // Horizontal: an underline that slides along the bottom edge.\n \"data-[orientation=horizontal]:-bottom-px data-[orientation=horizontal]:left-0 data-[orientation=horizontal]:h-0.5\",\n \"data-[orientation=horizontal]:w-[var(--active-tab-width,0px)] data-[orientation=horizontal]:translate-x-[var(--active-tab-left,0px)]\",\n // Vertical: a bar on the trailing edge that slides down the list.\n \"data-[orientation=vertical]:-right-px data-[orientation=vertical]:top-0 data-[orientation=vertical]:w-0.5\",\n \"data-[orientation=vertical]:h-[var(--active-tab-height,0px)] data-[orientation=vertical]:translate-y-[var(--active-tab-top,0px)]\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {\n return (\n <TabsPrimitive.Tab\n data-slot=\"tabs-trigger\"\n className={cn(\n \"relative inline-flex items-center gap-1.5 whitespace-nowrap px-3.5 py-2\",\n \"text-[length:var(--ds-text-caption-lg)] font-medium text-[color:var(--ds-muted-foreground)]\",\n \"rounded-[var(--ds-radius-sm)] outline-none\",\n \"transition-colors duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]\",\n \"hover:text-[color:var(--ds-foreground)]\",\n \"focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40\",\n \"data-[active]:text-[color:var(--ds-primary)]\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n \"[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {\n return (\n <TabsPrimitive.Panel\n data-slot=\"tabs-content\"\n className={cn(\n \"mt-3 flex-1 rounded-[var(--ds-radius-sm)] outline-none\",\n \"focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent, TabsIndicator };\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAkBA,SAASA,OAAK,EAAE,WAAW,GAAG,SAAmC;CAC/D,OACE,oBAACC,KAAc,MAAf;EACE,aAAU;EACV,WAAW,GAAG,sDAAsD,SAAS;EAC7E,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,SAAS,EAChB,WACA,UACA,kBAAkB,MAClB,GAAG,SACwB;CAC3B,OACE,qBAACA,KAAc,MAAf;EACE,aAAU;EACO;EACjB,WAAW,GACT,wDACA,4EACA,0CACA,uHACA,SACF;EACA,GAAI;EAVN,UAAA,CAYG,UACD,oBAAC,eAAD,CAAgB,CAAA,CACE;;AAExB;AAEA,SAAS,cAAc,EAAE,WAAW,GAAG,SAAwC;CAC7E,OACE,oBAACA,KAAc,WAAf;EACE,aAAU;EACV,WAAW,GACT,mEACA,oGAEA,qHACA,wIAEA,6GACA,oIACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,SAAkC;CACrE,OACE,oBAACA,KAAc,KAAf;EACE,aAAU;EACV,WAAW,GACT,2EACA,+FACA,8CACA,kFACA,2CACA,mEACA,gDACA,kEACA,+DACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,SAAoC;CACvE,OACE,oBAACA,KAAc,OAAf;EACE,aAAU;EACV,WAAW,GACT,0DACA,mEACA,SACF;EACA,GAAI;CACL,CAAA;AAEL"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
//#region src/components/textarea.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Textarea.
|
|
6
|
+
*
|
|
7
|
+
* Same surface, border, focus ring and invalid state as Input so the two sit
|
|
8
|
+
* together in a form. `autoGrow` resizes the element to its content on every
|
|
9
|
+
* change (and on mount), which is how chat composers and note fields use it.
|
|
10
|
+
*/
|
|
11
|
+
declare const textareaClassName: string;
|
|
12
|
+
export interface TextareaProps extends React.ComponentProps<"textarea"> {
|
|
13
|
+
/** When true the textarea grows to fit its content instead of scrolling. */
|
|
14
|
+
autoGrow?: boolean;
|
|
15
|
+
}
|
|
16
|
+
declare function Textarea({ className, autoGrow, onChange, ref, ...props }: TextareaProps): React.JSX.Element;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { Textarea, textareaClassName };
|
|
19
|
+
//# sourceMappingURL=textarea.d.ts.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use client";
|
|
3
|
+
import { cn } from "../lib/cn.js";
|
|
4
|
+
import { jsx } from "react/jsx-runtime";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
//#region src/components/textarea.tsx
|
|
7
|
+
/**
|
|
8
|
+
* Textarea.
|
|
9
|
+
*
|
|
10
|
+
* Same surface, border, focus ring and invalid state as Input so the two sit
|
|
11
|
+
* together in a form. `autoGrow` resizes the element to its content on every
|
|
12
|
+
* change (and on mount), which is how chat composers and note fields use it.
|
|
13
|
+
*/
|
|
14
|
+
const textareaClassName = /* @__PURE__ */ cn("flex min-h-20 w-full min-w-0 rounded-[var(--ds-radius-md)] border border-[var(--ds-border)] bg-[var(--ds-muted)] px-3 py-2", "text-[length:var(--ds-text-caption-lg)] leading-[var(--ds-text-caption-lg--line-height)] text-[color:var(--ds-foreground)]", "placeholder:text-[color:var(--ds-text-tertiary)]", "transition-[border-color,box-shadow] duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]", "hover:border-[var(--ds-border-strong)]", "outline-none focus-visible:border-[var(--ds-ring)] focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", "disabled:cursor-not-allowed disabled:opacity-50", "aria-invalid:border-[var(--ds-destructive)] aria-invalid:ring-[3px] aria-invalid:ring-[var(--ds-destructive)]/20");
|
|
15
|
+
function Textarea({ className, autoGrow = false, onChange, ref, ...props }) {
|
|
16
|
+
const internalRef = React.useRef(null);
|
|
17
|
+
const setRefs = React.useCallback((node) => {
|
|
18
|
+
internalRef.current = node;
|
|
19
|
+
if (typeof ref === "function") ref(node);
|
|
20
|
+
else if (ref) ref.current = node;
|
|
21
|
+
}, [ref]);
|
|
22
|
+
const fitToContent = React.useCallback(() => {
|
|
23
|
+
const el = internalRef.current;
|
|
24
|
+
if (el && autoGrow) {
|
|
25
|
+
el.style.height = "auto";
|
|
26
|
+
el.style.height = `${el.scrollHeight}px`;
|
|
27
|
+
}
|
|
28
|
+
}, [autoGrow]);
|
|
29
|
+
React.useEffect(() => {
|
|
30
|
+
fitToContent();
|
|
31
|
+
}, [fitToContent]);
|
|
32
|
+
const handleChange = React.useCallback((event) => {
|
|
33
|
+
onChange?.(event);
|
|
34
|
+
fitToContent();
|
|
35
|
+
}, [onChange, fitToContent]);
|
|
36
|
+
return /* @__PURE__ */ jsx("textarea", {
|
|
37
|
+
"data-slot": "textarea",
|
|
38
|
+
className: cn(textareaClassName, autoGrow && "resize-none overflow-hidden", className),
|
|
39
|
+
ref: setRefs,
|
|
40
|
+
onChange: handleChange,
|
|
41
|
+
...props
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { Textarea, textareaClassName };
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=textarea.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"textarea.js","names":[],"sources":["../../src/components/textarea.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * Textarea.\n *\n * Same surface, border, focus ring and invalid state as Input so the two sit\n * together in a form. `autoGrow` resizes the element to its content on every\n * change (and on mount), which is how chat composers and note fields use it.\n */\nconst textareaClassName = /* @__PURE__ */ cn(\n \"flex min-h-20 w-full min-w-0 rounded-[var(--ds-radius-md)] border border-[var(--ds-border)] bg-[var(--ds-muted)] px-3 py-2\",\n \"text-[length:var(--ds-text-caption-lg)] leading-[var(--ds-text-caption-lg--line-height)] text-[color:var(--ds-foreground)]\",\n \"placeholder:text-[color:var(--ds-text-tertiary)]\",\n \"transition-[border-color,box-shadow] duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]\",\n \"hover:border-[var(--ds-border-strong)]\",\n \"outline-none focus-visible:border-[var(--ds-ring)] focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n \"aria-invalid:border-[var(--ds-destructive)] aria-invalid:ring-[3px] aria-invalid:ring-[var(--ds-destructive)]/20\",\n);\n\nexport interface TextareaProps extends React.ComponentProps<\"textarea\"> {\n /** When true the textarea grows to fit its content instead of scrolling. */\n autoGrow?: boolean;\n}\n\nfunction Textarea({ className, autoGrow = false, onChange, ref, ...props }: TextareaProps) {\n const internalRef = React.useRef<HTMLTextAreaElement | null>(null);\n\n const setRefs = React.useCallback(\n (node: HTMLTextAreaElement | null) => {\n internalRef.current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n ref.current = node;\n }\n },\n [ref],\n );\n\n const fitToContent = React.useCallback(() => {\n const el = internalRef.current;\n if (el && autoGrow) {\n el.style.height = \"auto\";\n el.style.height = `${el.scrollHeight}px`;\n }\n }, [autoGrow]);\n\n React.useEffect(() => {\n fitToContent();\n }, [fitToContent]);\n\n const handleChange = React.useCallback(\n (event: React.ChangeEvent<HTMLTextAreaElement>) => {\n onChange?.(event);\n fitToContent();\n },\n [onChange, fitToContent],\n );\n\n return (\n <textarea\n data-slot=\"textarea\"\n className={cn(textareaClassName, autoGrow && \"resize-none overflow-hidden\", className)}\n ref={setRefs}\n onChange={handleChange}\n {...props}\n />\n );\n}\n\nexport { Textarea, textareaClassName };\n"],"mappings":";;;;;;;;;;;;;AAYA,MAAM,oBAAoC,mBACxC,8HACA,8HACA,oDACA,qGACA,0CACA,sHACA,mDACA,kHACF;AAOA,SAAS,SAAS,EAAE,WAAW,WAAW,OAAO,UAAU,KAAK,GAAG,SAAwB;CACzF,MAAM,cAAc,MAAM,OAAmC,IAAI;CAEjE,MAAM,UAAU,MAAM,aACnB,SAAqC;EACpC,YAAY,UAAU;EACtB,IAAI,OAAO,QAAQ,YACjB,IAAI,IAAI;OACH,IAAI,KACT,IAAI,UAAU;CAElB,GACA,CAAC,GAAG,CACN;CAEA,MAAM,eAAe,MAAM,kBAAkB;EAC3C,MAAM,KAAK,YAAY;EACvB,IAAI,MAAM,UAAU;GAClB,GAAG,MAAM,SAAS;GAClB,GAAG,MAAM,SAAS,GAAG,GAAG,aAAa;EACvC;CACF,GAAG,CAAC,QAAQ,CAAC;CAEb,MAAM,gBAAgB;EACpB,aAAa;CACf,GAAG,CAAC,YAAY,CAAC;CAEjB,MAAM,eAAe,MAAM,aACxB,UAAkD;EACjD,WAAW,KAAK;EAChB,aAAa;CACf,GACA,CAAC,UAAU,YAAY,CACzB;CAEA,OACE,oBAAC,YAAD;EACE,aAAU;EACV,WAAW,GAAG,mBAAmB,YAAY,+BAA+B,SAAS;EACrF,KAAK;EACL,UAAU;EACV,GAAI;CACL,CAAA;AAEL"}
|