@bysages/react 0.0.1 → 0.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/README.md +2 -2
- package/dist/index.d.mts +1161 -105
- package/dist/index.mjs +2779 -255
- package/package.json +9 -6
package/dist/index.mjs
CHANGED
|
@@ -1,32 +1,42 @@
|
|
|
1
1
|
import { injectComponentStyle } from "@bysages/core";
|
|
2
2
|
import { Accordion as Accordion$1 } from "@ark-ui/react/accordion";
|
|
3
|
-
import { renderHtml } from "@tanstack/markdown/html";
|
|
4
|
-
import { Children, cloneElement, createContext, isValidElement, useContext, useEffect, useLayoutEffect, useMemo, useReducer, useRef } from "react";
|
|
5
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Children, cloneElement, createContext, isValidElement, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
5
|
+
import { Field as Field$1, useFieldContext } from "@ark-ui/react/field";
|
|
6
|
+
import { Popover as Popover$1 } from "@ark-ui/react/popover";
|
|
7
|
+
import { Portal } from "@ark-ui/react/portal";
|
|
6
8
|
import { Collapsible as Collapsible$1 } from "@ark-ui/react/collapsible";
|
|
7
|
-
import {
|
|
9
|
+
import { renderHtml } from "@tanstack/markdown/html";
|
|
8
10
|
import { AngleSlider as AngleSlider$1 } from "@ark-ui/react/angle-slider";
|
|
11
|
+
import { createFileTreeCollection, createGridCollection, createTreeCollection, useAsyncList, useListCollection, useListCollection as useListCollection$1, useListSelection } from "@ark-ui/react/collection";
|
|
12
|
+
import { Combobox as Combobox$1 } from "@ark-ui/react/combobox";
|
|
9
13
|
import { Avatar as Avatar$1 } from "@ark-ui/react/avatar";
|
|
10
14
|
import { DatePicker as DatePicker$1 } from "@ark-ui/react/date-picker";
|
|
15
|
+
import { useEnvironmentContext } from "@ark-ui/react/environment";
|
|
16
|
+
import { useFilter, useLocaleContext } from "@ark-ui/react/locale";
|
|
17
|
+
import * as cascade from "@zag-js/cascade-select";
|
|
18
|
+
import { normalizeProps, useMachine } from "@zag-js/react";
|
|
11
19
|
import { Carousel as Carousel$1 } from "@ark-ui/react/carousel";
|
|
12
20
|
import { Checkbox as Checkbox$1 } from "@ark-ui/react/checkbox";
|
|
13
21
|
import { Clipboard as Clipboard$1 } from "@ark-ui/react/clipboard";
|
|
22
|
+
import { ClientOnly } from "@ark-ui/react/client-only";
|
|
14
23
|
import { ColorPicker as ColorPicker$1 } from "@ark-ui/react/color-picker";
|
|
15
|
-
import { Combobox as Combobox$1 } from "@ark-ui/react/combobox";
|
|
16
|
-
import { DateInput as DateInput$1 } from "@ark-ui/react/date-input";
|
|
17
24
|
import { Dialog as Dialog$1 } from "@ark-ui/react/dialog";
|
|
25
|
+
import { DateInput as DateInput$1 } from "@ark-ui/react/date-input";
|
|
26
|
+
import { DownloadTrigger, useDownload } from "@ark-ui/react/download-trigger";
|
|
18
27
|
import { Drawer as Drawer$1 } from "@ark-ui/react/drawer";
|
|
19
28
|
import { Editable as Editable$1 } from "@ark-ui/react/editable";
|
|
20
29
|
import { Fieldset as Fieldset$1 } from "@ark-ui/react/fieldset";
|
|
21
30
|
import { FileUpload as FileUpload$1 } from "@ark-ui/react/file-upload";
|
|
22
31
|
import { FloatingPanel as FloatingPanel$1 } from "@ark-ui/react/floating-panel";
|
|
32
|
+
import { FocusTrap } from "@ark-ui/react/focus-trap";
|
|
23
33
|
import { Format as Format$1 } from "@ark-ui/react/format";
|
|
24
34
|
import { Frame as Frame$1 } from "@ark-ui/react/frame";
|
|
25
35
|
import { Highlight as Highlight$1 } from "@ark-ui/react/highlight";
|
|
26
36
|
import { HoverCard as HoverCard$1 } from "@ark-ui/react/hover-card";
|
|
27
37
|
import { ImageCropper as ImageCropper$1 } from "@ark-ui/react/image-cropper";
|
|
28
38
|
import { JsonTreeView as JsonTreeView$1 } from "@ark-ui/react/json-tree-view";
|
|
29
|
-
import { Listbox as Listbox$1
|
|
39
|
+
import { Listbox as Listbox$1 } from "@ark-ui/react/listbox";
|
|
30
40
|
import { Marquee as Marquee$1 } from "@ark-ui/react/marquee";
|
|
31
41
|
import { Menu as Menu$1 } from "@ark-ui/react/menu";
|
|
32
42
|
import { NavigationMenu as NavigationMenu$1 } from "@ark-ui/react/navigation-menu";
|
|
@@ -34,14 +44,14 @@ import { NumberInput as NumberInput$1 } from "@ark-ui/react/number-input";
|
|
|
34
44
|
import { Pagination as Pagination$1 } from "@ark-ui/react/pagination";
|
|
35
45
|
import { PasswordInput as PasswordInput$1 } from "@ark-ui/react/password-input";
|
|
36
46
|
import { PinInput as PinInput$1 } from "@ark-ui/react/pin-input";
|
|
37
|
-
import {
|
|
47
|
+
import { Presence, PresenceProvider, usePresence, usePresenceContext } from "@ark-ui/react/presence";
|
|
38
48
|
import { Progress as Progress$1 } from "@ark-ui/react/progress";
|
|
39
49
|
import { QrCode as QrCode$1 } from "@ark-ui/react/qr-code";
|
|
40
50
|
import { RadioGroup as RadioGroup$1 } from "@ark-ui/react/radio-group";
|
|
41
51
|
import { RatingGroup as RatingGroup$1 } from "@ark-ui/react/rating-group";
|
|
42
52
|
import { ScrollArea as ScrollArea$1 } from "@ark-ui/react/scroll-area";
|
|
43
53
|
import { SegmentGroup as SegmentGroup$1 } from "@ark-ui/react/segment-group";
|
|
44
|
-
import { Select as Select$1 } from "@ark-ui/react/select";
|
|
54
|
+
import { Select as Select$1, createListCollection } from "@ark-ui/react/select";
|
|
45
55
|
import { SignaturePad as SignaturePad$1 } from "@ark-ui/react/signature-pad";
|
|
46
56
|
import { Slider as Slider$1 } from "@ark-ui/react/slider";
|
|
47
57
|
import { Splitter as Splitter$1 } from "@ark-ui/react/splitter";
|
|
@@ -59,7 +69,7 @@ import { Toggle as Toggle$1 } from "@ark-ui/react/toggle";
|
|
|
59
69
|
import { ToggleGroup as ToggleGroup$1 } from "@ark-ui/react/toggle-group";
|
|
60
70
|
import { Tooltip as Tooltip$1 } from "@ark-ui/react/tooltip";
|
|
61
71
|
import { Tour as Tour$1, useTour } from "@ark-ui/react/tour";
|
|
62
|
-
import { TreeView as TreeView$1 } from "@ark-ui/react/tree-view";
|
|
72
|
+
import { TreeView as TreeView$1, createTreeCollection as createTreeCollection$1 } from "@ark-ui/react/tree-view";
|
|
63
73
|
export * from "@bysages/core";
|
|
64
74
|
//#region src/components/accordion/index.tsx
|
|
65
75
|
/** Ark's Accordion, dressed in the paper-and-ink system: a ruled sheet
|
|
@@ -68,6 +78,21 @@ export * from "@bysages/core";
|
|
|
68
78
|
const Accordion = Accordion$1;
|
|
69
79
|
injectComponentStyle("accordion");
|
|
70
80
|
//#endregion
|
|
81
|
+
//#region src/components/affix/index.tsx
|
|
82
|
+
function Affix({ offsetTop = "0px", offsetBottom = "0px", ...rest }) {
|
|
83
|
+
return /* @__PURE__ */ jsx("div", {
|
|
84
|
+
...rest,
|
|
85
|
+
style: {
|
|
86
|
+
...rest.style,
|
|
87
|
+
top: offsetTop,
|
|
88
|
+
bottom: offsetBottom
|
|
89
|
+
},
|
|
90
|
+
"data-scope": "affix",
|
|
91
|
+
"data-part": "root"
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
injectComponentStyle("affix");
|
|
95
|
+
//#endregion
|
|
71
96
|
//#region src/components/button/index.tsx
|
|
72
97
|
function Button({ variant = "solid", tone = "ink", size = "md", square = false, asChild = false, disabled = false, type = "button", children, ...rest }) {
|
|
73
98
|
const partProps = {
|
|
@@ -93,12 +118,31 @@ function Button({ variant = "solid", tone = "ink", size = "md", square = false,
|
|
|
93
118
|
}
|
|
94
119
|
injectComponentStyle("button");
|
|
95
120
|
//#endregion
|
|
96
|
-
//#region src/components/
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
121
|
+
//#region src/components/ai-action/index.tsx
|
|
122
|
+
function Action({ label, children, ...rest }) {
|
|
123
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
124
|
+
variant: "ghost",
|
|
125
|
+
size: "sm",
|
|
126
|
+
square: true,
|
|
127
|
+
"aria-label": label,
|
|
128
|
+
title: label,
|
|
129
|
+
...rest,
|
|
130
|
+
children
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
injectComponentStyle("ai");
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/components/ai-message/index.tsx
|
|
136
|
+
function Message({ role = "assistant", children, ...rest }) {
|
|
137
|
+
return /* @__PURE__ */ jsx("article", {
|
|
138
|
+
...rest,
|
|
139
|
+
"data-scope": "ai",
|
|
140
|
+
"data-part": "message",
|
|
141
|
+
"data-role": role,
|
|
142
|
+
children
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
injectComponentStyle("ai");
|
|
102
146
|
//#endregion
|
|
103
147
|
//#region src/components/field/index.tsx
|
|
104
148
|
/** Ark's Field, dressed in the paper-and-ink system: a tracked label, a
|
|
@@ -108,38 +152,308 @@ injectComponentStyle("collapsible");
|
|
|
108
152
|
const Field = Field$1;
|
|
109
153
|
injectComponentStyle("field");
|
|
110
154
|
//#endregion
|
|
111
|
-
//#region src/components/
|
|
112
|
-
/**
|
|
113
|
-
* the
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* `
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
155
|
+
//#region src/components/mentions/use-mentions.ts
|
|
156
|
+
/** The @-detection machinery behind a mention field: watch the caret,
|
|
157
|
+
* offer the matching candidates, and answer for the keys that drive
|
|
158
|
+
* them. The field element arrives as a getter because hosts wrap it
|
|
159
|
+
* differently (a bare textarea, a field part's ref).
|
|
160
|
+
*
|
|
161
|
+
* `onKeydown` answers whether the vessel consumed the key, so a host
|
|
162
|
+
* with its own Enter semantics — a composer that sends — can yield to
|
|
163
|
+
* the candidates first and act only when the vessel stands down. */
|
|
164
|
+
function useMentions(options, textarea, handlers) {
|
|
165
|
+
const [popupOpen, setPopupOpen] = useState(false);
|
|
166
|
+
const [matches, setMatches] = useState([]);
|
|
167
|
+
const [active, setActive] = useState(0);
|
|
168
|
+
const anchorIndex = useRef(0);
|
|
169
|
+
const caretIndex = useRef(0);
|
|
170
|
+
const resolve = () => typeof options === "function" ? options() : options;
|
|
171
|
+
const closePopup = () => {
|
|
172
|
+
setPopupOpen(false);
|
|
173
|
+
setMatches([]);
|
|
174
|
+
};
|
|
175
|
+
const detect = () => {
|
|
176
|
+
const text = handlers.getText();
|
|
177
|
+
const caret = textarea()?.selectionStart ?? text.length;
|
|
178
|
+
caretIndex.current = caret;
|
|
179
|
+
const head = text.slice(0, caret);
|
|
180
|
+
const char = resolve().trigger ?? "@";
|
|
181
|
+
const anchor = head.lastIndexOf(char);
|
|
182
|
+
const token = anchor === -1 ? "" : head.slice(anchor + char.length);
|
|
183
|
+
if (anchor === -1 || /\s/.test(token)) {
|
|
184
|
+
closePopup();
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const found = resolve().items.filter((entry) => entry.label.toLowerCase().startsWith(token.toLowerCase()));
|
|
188
|
+
setMatches(found);
|
|
189
|
+
setActive(0);
|
|
190
|
+
anchorIndex.current = anchor;
|
|
191
|
+
setPopupOpen(found.length > 0);
|
|
192
|
+
};
|
|
193
|
+
const insert = (entry) => {
|
|
194
|
+
const text = handlers.getText();
|
|
195
|
+
const char = resolve().trigger ?? "@";
|
|
196
|
+
const next = text.slice(0, anchorIndex.current) + char + entry.label + " " + text.slice(caretIndex.current);
|
|
197
|
+
handlers.setText(next);
|
|
198
|
+
closePopup();
|
|
199
|
+
setTimeout(() => {
|
|
200
|
+
const el = textarea();
|
|
201
|
+
if (!el) return;
|
|
202
|
+
const caret = anchorIndex.current + char.length + entry.label.length + 1;
|
|
203
|
+
el.focus();
|
|
204
|
+
el.setSelectionRange(caret, caret);
|
|
205
|
+
}, 0);
|
|
206
|
+
};
|
|
207
|
+
/** Feed the field's input events through here — the vessel watches
|
|
208
|
+
* the text as it lands, from the element itself. */
|
|
209
|
+
const onInput = () => detect();
|
|
210
|
+
/** Arrows move, Enter inserts, Escape dismisses. Answers `true` when
|
|
211
|
+
* the vessel consumed the key. */
|
|
212
|
+
const onKeydown = (event) => {
|
|
213
|
+
if (!popupOpen || matches.length === 0) return false;
|
|
214
|
+
if (event.key === "ArrowDown") {
|
|
215
|
+
event.preventDefault();
|
|
216
|
+
setActive((current) => (current + 1) % matches.length);
|
|
217
|
+
} else if (event.key === "ArrowUp") {
|
|
218
|
+
event.preventDefault();
|
|
219
|
+
setActive((current) => (current - 1 + matches.length) % matches.length);
|
|
220
|
+
} else if (event.key === "Enter") {
|
|
221
|
+
event.preventDefault();
|
|
222
|
+
insert(matches[active]);
|
|
223
|
+
} else if (event.key === "Escape") closePopup();
|
|
224
|
+
else return false;
|
|
225
|
+
return true;
|
|
226
|
+
};
|
|
227
|
+
return {
|
|
228
|
+
open: popupOpen,
|
|
229
|
+
matches,
|
|
230
|
+
active,
|
|
231
|
+
setActive,
|
|
232
|
+
insert,
|
|
233
|
+
close: closePopup,
|
|
234
|
+
onInput,
|
|
235
|
+
onKeydown
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/components/mentions/index.tsx
|
|
240
|
+
/** The vessel: the candidates themselves as a floating card. The anchor
|
|
241
|
+
* is virtual — a live rectangle off the host's field — so a host keeps
|
|
242
|
+
* its own anatomy (the textarea rides where the host puts it) and the
|
|
243
|
+
* vessel still points at the right place. Shares the detection state
|
|
244
|
+
* with the host through `useMentions`. */
|
|
245
|
+
function MentionsVessel({ open = false, matches = [], active = 0, anchor = null, onInsert, onActiveChange, onOpenChange }) {
|
|
246
|
+
return /* @__PURE__ */ jsx(Popover$1.Root, {
|
|
247
|
+
open,
|
|
248
|
+
onOpenChange: (details) => onOpenChange?.(details.open),
|
|
249
|
+
positioning: {
|
|
250
|
+
placement: "bottom-start",
|
|
251
|
+
getAnchorRect: () => anchor?.getBoundingClientRect() ?? null
|
|
252
|
+
},
|
|
253
|
+
children: /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { children: /* @__PURE__ */ jsx(Popover$1.Content, {
|
|
254
|
+
asChild: true,
|
|
255
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
256
|
+
"data-scope": "mentions",
|
|
257
|
+
"data-part": "popup",
|
|
258
|
+
children: matches.map((entry, index) => /* @__PURE__ */ jsx("div", {
|
|
259
|
+
"data-scope": "mentions",
|
|
260
|
+
"data-part": "option",
|
|
261
|
+
"data-active": index === active ? "" : void 0,
|
|
262
|
+
onMouseEnter: () => onActiveChange?.(index),
|
|
263
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
264
|
+
onClick: () => onInsert?.(entry),
|
|
265
|
+
children: entry.label
|
|
266
|
+
}, entry.value))
|
|
267
|
+
})
|
|
268
|
+
}) }) })
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function Mentions({ items = [], value, trigger = "@", placeholder, autoresize = false, invalid = false, onValueChange, children, ...rest }) {
|
|
272
|
+
const [internal, setInternal] = useState("");
|
|
273
|
+
const fieldRef = useRef(null);
|
|
274
|
+
const el = () => fieldRef.current;
|
|
275
|
+
const current = value ?? internal;
|
|
276
|
+
const mentions = useMentions({
|
|
277
|
+
items,
|
|
278
|
+
trigger
|
|
279
|
+
}, el, {
|
|
280
|
+
getText: () => el()?.value ?? current,
|
|
281
|
+
setText: (next) => {
|
|
282
|
+
setInternal(next);
|
|
283
|
+
onValueChange?.(next);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
const handleChange = (event) => {
|
|
287
|
+
const node = event.currentTarget;
|
|
288
|
+
setInternal(node.value);
|
|
289
|
+
onValueChange?.(node.value);
|
|
290
|
+
mentions.onInput();
|
|
291
|
+
};
|
|
292
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
122
293
|
...rest,
|
|
123
|
-
"data-scope": "
|
|
124
|
-
"data-part":
|
|
125
|
-
children
|
|
294
|
+
"data-scope": "mentions",
|
|
295
|
+
"data-part": "root",
|
|
296
|
+
children: [
|
|
297
|
+
/* @__PURE__ */ jsx(Field.Textarea, {
|
|
298
|
+
ref: fieldRef,
|
|
299
|
+
autoresize,
|
|
300
|
+
rows: 3,
|
|
301
|
+
placeholder,
|
|
302
|
+
value: current,
|
|
303
|
+
onChange: handleChange,
|
|
304
|
+
onKeyDown: (event) => mentions.onKeydown(event),
|
|
305
|
+
"data-invalid": invalid || void 0,
|
|
306
|
+
"data-scope": "mentions",
|
|
307
|
+
"data-part": "textarea"
|
|
308
|
+
}),
|
|
309
|
+
/* @__PURE__ */ jsx(MentionsVessel, {
|
|
310
|
+
open: mentions.open,
|
|
311
|
+
matches: mentions.matches,
|
|
312
|
+
active: mentions.active,
|
|
313
|
+
anchor: el(),
|
|
314
|
+
onInsert: mentions.insert,
|
|
315
|
+
onActiveChange: mentions.setActive,
|
|
316
|
+
onOpenChange: (open) => {
|
|
317
|
+
if (!open) mentions.close();
|
|
318
|
+
}
|
|
319
|
+
}),
|
|
320
|
+
children
|
|
321
|
+
]
|
|
126
322
|
});
|
|
127
|
-
Component.displayName = "Ai" + name;
|
|
128
|
-
return Component;
|
|
129
323
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
324
|
+
injectComponentStyle("mentions");
|
|
325
|
+
//#endregion
|
|
326
|
+
//#region src/components/ai-prompt-input/index.tsx
|
|
327
|
+
const arrowUpGlyph = /* @__PURE__ */ jsx("svg", {
|
|
328
|
+
viewBox: "0 0 16 16",
|
|
329
|
+
width: 14,
|
|
330
|
+
height: 14,
|
|
331
|
+
"aria-hidden": "true",
|
|
332
|
+
fill: "none",
|
|
333
|
+
stroke: "currentColor",
|
|
334
|
+
strokeWidth: 1.5,
|
|
335
|
+
strokeLinecap: "square",
|
|
336
|
+
children: /* @__PURE__ */ jsx("path", { d: "M8 13V3M3.5 7.5 8 3l4.5 4.5" })
|
|
133
337
|
});
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
338
|
+
const stopGlyph = /* @__PURE__ */ jsx("svg", {
|
|
339
|
+
viewBox: "0 0 16 16",
|
|
340
|
+
width: 14,
|
|
341
|
+
height: 14,
|
|
342
|
+
"aria-hidden": "true",
|
|
343
|
+
children: /* @__PURE__ */ jsx("rect", {
|
|
344
|
+
x: 4.5,
|
|
345
|
+
y: 4.5,
|
|
346
|
+
width: 7,
|
|
347
|
+
height: 7,
|
|
348
|
+
fill: "currentColor"
|
|
349
|
+
})
|
|
140
350
|
});
|
|
351
|
+
/** An empty slot leaves no part in the anatomy — the bare vessel keeps
|
|
352
|
+
* its single-line posture. */
|
|
353
|
+
function row(part, content) {
|
|
354
|
+
if (content == null || Array.isArray(content) && content.length === 0) return null;
|
|
355
|
+
return /* @__PURE__ */ jsx("div", {
|
|
356
|
+
"data-scope": "ai",
|
|
357
|
+
"data-part": part,
|
|
358
|
+
children: content
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
function PromptInput({ value, onValueChange, onSubmit, onStop, placeholder = "Send a message", disabled = false, busy = false, mentions, header, leading, trailing, footer, footerEnd, ...rest }) {
|
|
362
|
+
const fieldRef = useRef(null);
|
|
363
|
+
const mentionState = useMentions(() => ({
|
|
364
|
+
items: mentions?.items ?? [],
|
|
365
|
+
trigger: mentions?.trigger
|
|
366
|
+
}), () => fieldRef.current, {
|
|
367
|
+
getText: () => fieldRef.current?.value ?? value,
|
|
368
|
+
setText: (next) => onValueChange?.(next)
|
|
369
|
+
});
|
|
370
|
+
const mentionsActive = mentions != null;
|
|
371
|
+
const submit = () => {
|
|
372
|
+
const text = value.trim();
|
|
373
|
+
if (!text || disabled || busy) return;
|
|
374
|
+
onSubmit?.(text);
|
|
375
|
+
onValueChange?.("");
|
|
376
|
+
};
|
|
377
|
+
const seal = busy ? /* @__PURE__ */ jsx(Button, {
|
|
378
|
+
variant: "solid",
|
|
379
|
+
size: "sm",
|
|
380
|
+
square: true,
|
|
381
|
+
type: "button",
|
|
382
|
+
"aria-label": "Stop",
|
|
383
|
+
disabled,
|
|
384
|
+
onClick: () => onStop?.(),
|
|
385
|
+
children: stopGlyph
|
|
386
|
+
}) : /* @__PURE__ */ jsx(Button, {
|
|
387
|
+
variant: "solid",
|
|
388
|
+
size: "sm",
|
|
389
|
+
square: true,
|
|
390
|
+
type: "submit",
|
|
391
|
+
"aria-label": "Send",
|
|
392
|
+
disabled: disabled || !value.trim(),
|
|
393
|
+
children: arrowUpGlyph
|
|
394
|
+
});
|
|
395
|
+
const hasFooter = footer != null && (!Array.isArray(footer) || footer.length > 0);
|
|
396
|
+
return /* @__PURE__ */ jsxs("form", {
|
|
397
|
+
...rest,
|
|
398
|
+
"data-scope": "ai",
|
|
399
|
+
"data-part": "prompt",
|
|
400
|
+
onSubmit: (event) => {
|
|
401
|
+
event.preventDefault();
|
|
402
|
+
submit();
|
|
403
|
+
},
|
|
404
|
+
children: [
|
|
405
|
+
row("prompt-header", header),
|
|
406
|
+
/* @__PURE__ */ jsxs("div", {
|
|
407
|
+
"data-scope": "ai",
|
|
408
|
+
"data-part": "prompt-main",
|
|
409
|
+
children: [
|
|
410
|
+
row("prompt-leading", leading),
|
|
411
|
+
/* @__PURE__ */ jsx(Field.Root, { children: /* @__PURE__ */ jsx(Field.Textarea, {
|
|
412
|
+
ref: fieldRef,
|
|
413
|
+
autoresize: true,
|
|
414
|
+
rows: 1,
|
|
415
|
+
value,
|
|
416
|
+
placeholder,
|
|
417
|
+
disabled,
|
|
418
|
+
onChange: (event) => {
|
|
419
|
+
onValueChange?.(event.target.value);
|
|
420
|
+
mentionState.onInput();
|
|
421
|
+
},
|
|
422
|
+
onKeyDown: (event) => {
|
|
423
|
+
if (mentionState.onKeydown(event)) return;
|
|
424
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
425
|
+
event.preventDefault();
|
|
426
|
+
submit();
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}) }),
|
|
430
|
+
mentionsActive ? /* @__PURE__ */ jsx(MentionsVessel, {
|
|
431
|
+
open: mentionState.open,
|
|
432
|
+
matches: mentionState.matches,
|
|
433
|
+
active: mentionState.active,
|
|
434
|
+
anchor: fieldRef.current,
|
|
435
|
+
onInsert: mentionState.insert,
|
|
436
|
+
onActiveChange: mentionState.setActive,
|
|
437
|
+
onOpenChange: (open) => {
|
|
438
|
+
if (!open) mentionState.close();
|
|
439
|
+
}
|
|
440
|
+
}) : null,
|
|
441
|
+
row("prompt-trailing", hasFooter ? trailing : trailing ?? seal)
|
|
442
|
+
]
|
|
443
|
+
}),
|
|
444
|
+
hasFooter ? /* @__PURE__ */ jsxs("div", {
|
|
445
|
+
"data-scope": "ai",
|
|
446
|
+
"data-part": "prompt-footer",
|
|
447
|
+
children: [footer, row("prompt-end", footerEnd == null ? seal : /* @__PURE__ */ jsxs(Fragment, { children: [footerEnd, seal] }))]
|
|
448
|
+
}) : null
|
|
449
|
+
]
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
injectComponentStyle("ai");
|
|
453
|
+
//#endregion
|
|
454
|
+
//#region src/components/ai/chevron.tsx
|
|
141
455
|
/** The folding chevron the shared indicator turns. */
|
|
142
|
-
const chevron$
|
|
456
|
+
const chevron$2 = /* @__PURE__ */ jsx("svg", {
|
|
143
457
|
viewBox: "0 0 16 16",
|
|
144
458
|
fill: "none",
|
|
145
459
|
"aria-hidden": "true",
|
|
@@ -151,62 +465,111 @@ const chevron$1 = /* @__PURE__ */ jsx("svg", {
|
|
|
151
465
|
strokeLinejoin: "round"
|
|
152
466
|
})
|
|
153
467
|
});
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
function Response({ content, ...rest }) {
|
|
164
|
-
return /* @__PURE__ */ jsx("div", {
|
|
165
|
-
...rest,
|
|
166
|
-
"data-scope": "ai",
|
|
167
|
-
"data-part": "response",
|
|
168
|
-
dangerouslySetInnerHTML: { __html: renderHtml(content) }
|
|
169
|
-
});
|
|
170
|
-
}
|
|
468
|
+
//#endregion
|
|
469
|
+
//#region src/components/collapsible/index.tsx
|
|
470
|
+
/** Ark's Collapsible, dressed in the paper-and-ink system: one control on
|
|
471
|
+
* the paper, its panel dissolving open to the machine's measured height.
|
|
472
|
+
* The API is Ark's own — Root, Trigger, Content, Indicator. */
|
|
473
|
+
const Collapsible = Collapsible$1;
|
|
474
|
+
injectComponentStyle("collapsible");
|
|
475
|
+
//#endregion
|
|
476
|
+
//#region src/components/ai-reasoning/index.tsx
|
|
171
477
|
function Reasoning({ label = "Thinking", children, ...rest }) {
|
|
172
478
|
return /* @__PURE__ */ jsxs(Collapsible.Root, {
|
|
173
479
|
...rest,
|
|
174
480
|
"data-ai": "reasoning",
|
|
175
|
-
children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, { children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx(Collapsible.Indicator, { children: chevron$
|
|
481
|
+
children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, { children: [/* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx(Collapsible.Indicator, { children: chevron$2 })] }), /* @__PURE__ */ jsx(Collapsible.Content, { children: /* @__PURE__ */ jsx("div", {
|
|
176
482
|
"data-scope": "ai",
|
|
177
483
|
"data-part": "reasoning-content",
|
|
178
484
|
children
|
|
179
485
|
}) })]
|
|
180
486
|
});
|
|
181
487
|
}
|
|
182
|
-
|
|
183
|
-
|
|
488
|
+
injectComponentStyle("ai");
|
|
489
|
+
//#endregion
|
|
490
|
+
//#region src/components/ai-response/code-copy.ts
|
|
491
|
+
/** The two glyphs a copy affordance needs: the stamp and its
|
|
492
|
+
* confirmation. Inline SVG keeps the enhancement framework-agnostic —
|
|
493
|
+
* it lives beside the renderer's generated code HTML. */
|
|
494
|
+
const COPY_CODE_ICON = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`;
|
|
495
|
+
const COPIED_CODE_ICON = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>`;
|
|
496
|
+
/** How long the confirmation stays before the stamp returns. */
|
|
497
|
+
const REVERT_MS = 1500;
|
|
498
|
+
/** Make sure every fenced block under `host` carries its copy stamp.
|
|
499
|
+
* The stamp anchors to a wrapper outside the scrolling `pre` — inside,
|
|
500
|
+
* it would fix to the overflow area and travel out of view with the
|
|
501
|
+
* code. The renderer rebuilds the markdown as one HTML string on every
|
|
502
|
+
* stream tick, so this stays idempotent and cheap enough to re-run
|
|
503
|
+
* after each patch. */
|
|
504
|
+
function decorateCodeCopy(host, copyLabel) {
|
|
505
|
+
for (const pre of host.querySelectorAll("pre")) {
|
|
506
|
+
let body = pre.parentElement;
|
|
507
|
+
if (!body || body.getAttribute("data-part") !== "code-body") {
|
|
508
|
+
body = document.createElement("div");
|
|
509
|
+
body.setAttribute("data-scope", "ai");
|
|
510
|
+
body.setAttribute("data-part", "code-body");
|
|
511
|
+
pre.replaceWith(body);
|
|
512
|
+
body.appendChild(pre);
|
|
513
|
+
}
|
|
514
|
+
let button = body.querySelector("[data-part=\"code-copy\"]");
|
|
515
|
+
if (!button) {
|
|
516
|
+
button = document.createElement("button");
|
|
517
|
+
button.type = "button";
|
|
518
|
+
button.setAttribute("data-scope", "ai");
|
|
519
|
+
button.setAttribute("data-part", "code-copy");
|
|
520
|
+
button.innerHTML = COPY_CODE_ICON;
|
|
521
|
+
body.appendChild(button);
|
|
522
|
+
}
|
|
523
|
+
button.title = copyLabel;
|
|
524
|
+
button.setAttribute("aria-label", copyLabel);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
/** The one delegated click for the whole response: copy the block's
|
|
528
|
+
* text and confirm in place, then return the stamp after a beat. */
|
|
529
|
+
async function clickCodeCopy(event, copyLabel, copiedLabel) {
|
|
530
|
+
const button = event.target?.closest("[data-part=\"code-copy\"]");
|
|
531
|
+
const pre = button?.closest("[data-part=\"code-body\"]")?.querySelector("pre");
|
|
532
|
+
if (!button || !pre) return;
|
|
533
|
+
await navigator.clipboard.writeText(pre.textContent ?? "");
|
|
534
|
+
button.innerHTML = COPIED_CODE_ICON;
|
|
535
|
+
button.setAttribute("data-state", "copied");
|
|
536
|
+
button.title = copiedLabel;
|
|
537
|
+
button.setAttribute("aria-label", copiedLabel);
|
|
538
|
+
setTimeout(() => {
|
|
539
|
+
if (!button.isConnected) return;
|
|
540
|
+
button.innerHTML = COPY_CODE_ICON;
|
|
541
|
+
button.removeAttribute("data-state");
|
|
542
|
+
button.title = copyLabel;
|
|
543
|
+
button.setAttribute("aria-label", copyLabel);
|
|
544
|
+
}, REVERT_MS);
|
|
545
|
+
}
|
|
546
|
+
//#endregion
|
|
547
|
+
//#region src/components/ai-response/index.tsx
|
|
548
|
+
function Response({ content, highlighter, copyLabel = "Copy code", copiedLabel = "Copied", ...rest }) {
|
|
549
|
+
const root = useRef(null);
|
|
550
|
+
useEffect(() => {
|
|
551
|
+
const host = root.current;
|
|
552
|
+
if (host) decorateCodeCopy(host, copyLabel);
|
|
553
|
+
}, [
|
|
554
|
+
content,
|
|
555
|
+
highlighter,
|
|
556
|
+
copyLabel
|
|
557
|
+
]);
|
|
558
|
+
const onClick = (event) => {
|
|
559
|
+
clickCodeCopy(event.nativeEvent, copyLabel, copiedLabel);
|
|
560
|
+
};
|
|
561
|
+
return /* @__PURE__ */ jsx("div", {
|
|
184
562
|
...rest,
|
|
185
|
-
|
|
186
|
-
"data-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
"data-scope": "ai",
|
|
191
|
-
"data-part": "tool-status",
|
|
192
|
-
children: status.charAt(0).toUpperCase() + status.slice(1)
|
|
193
|
-
}) : null,
|
|
194
|
-
/* @__PURE__ */ jsx(Collapsible.Indicator, { children: chevron$1 })
|
|
195
|
-
] }), /* @__PURE__ */ jsx(Collapsible.Content, { children: /* @__PURE__ */ jsxs("div", {
|
|
196
|
-
"data-scope": "ai",
|
|
197
|
-
"data-part": "tool-body",
|
|
198
|
-
children: [input ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
199
|
-
"data-scope": "ai",
|
|
200
|
-
"data-part": "tool-label",
|
|
201
|
-
children: "Input"
|
|
202
|
-
}), /* @__PURE__ */ jsx("pre", { children: input })] }) : null, output ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
203
|
-
"data-scope": "ai",
|
|
204
|
-
"data-part": "tool-label",
|
|
205
|
-
children: "Output"
|
|
206
|
-
}), /* @__PURE__ */ jsx("pre", { children: output })] }) : null]
|
|
207
|
-
}) })]
|
|
563
|
+
ref: root,
|
|
564
|
+
"data-scope": "ai",
|
|
565
|
+
"data-part": "response",
|
|
566
|
+
onClick,
|
|
567
|
+
dangerouslySetInnerHTML: { __html: renderHtml(content, highlighter ? { highlighter } : void 0) }
|
|
208
568
|
});
|
|
209
569
|
}
|
|
570
|
+
injectComponentStyle("ai");
|
|
571
|
+
//#endregion
|
|
572
|
+
//#region src/components/ai-source/index.tsx
|
|
210
573
|
function Source({ href, children, ...rest }) {
|
|
211
574
|
return /* @__PURE__ */ jsx("li", {
|
|
212
575
|
"data-scope": "ai",
|
|
@@ -220,17 +583,17 @@ function Source({ href, children, ...rest }) {
|
|
|
220
583
|
})
|
|
221
584
|
});
|
|
222
585
|
}
|
|
223
|
-
function
|
|
224
|
-
return /* @__PURE__ */ jsx(
|
|
225
|
-
variant: "ghost",
|
|
226
|
-
size: "sm",
|
|
227
|
-
square: true,
|
|
228
|
-
"aria-label": label,
|
|
229
|
-
title: label,
|
|
586
|
+
function Sources({ children, ...rest }) {
|
|
587
|
+
return /* @__PURE__ */ jsx("ol", {
|
|
230
588
|
...rest,
|
|
589
|
+
"data-scope": "ai",
|
|
590
|
+
"data-part": "sources",
|
|
231
591
|
children
|
|
232
592
|
});
|
|
233
593
|
}
|
|
594
|
+
injectComponentStyle("ai");
|
|
595
|
+
//#endregion
|
|
596
|
+
//#region src/components/ai-suggestion/index.tsx
|
|
234
597
|
function Suggestion({ prompt, onSelect, ...rest }) {
|
|
235
598
|
return /* @__PURE__ */ jsx(Button, {
|
|
236
599
|
variant: "outline",
|
|
@@ -240,76 +603,196 @@ function Suggestion({ prompt, onSelect, ...rest }) {
|
|
|
240
603
|
children: prompt
|
|
241
604
|
});
|
|
242
605
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
onValueChange?.("");
|
|
249
|
-
};
|
|
250
|
-
return /* @__PURE__ */ jsxs("form", {
|
|
606
|
+
injectComponentStyle("ai");
|
|
607
|
+
//#endregion
|
|
608
|
+
//#region src/components/ai-tool/index.tsx
|
|
609
|
+
function Tool({ name, label, status, input, output, ...rest }) {
|
|
610
|
+
return /* @__PURE__ */ jsxs(Collapsible.Root, {
|
|
251
611
|
...rest,
|
|
252
|
-
"data-
|
|
253
|
-
"data-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
disabled,
|
|
264
|
-
onChange: (event) => onValueChange?.(event.target.value),
|
|
265
|
-
onKeyDown: (event) => {
|
|
266
|
-
if (event.key === "Enter" && !event.shiftKey) {
|
|
267
|
-
event.preventDefault();
|
|
268
|
-
send();
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}) }), /* @__PURE__ */ jsx("div", {
|
|
612
|
+
"data-ai": "tool",
|
|
613
|
+
"data-status": status,
|
|
614
|
+
children: [/* @__PURE__ */ jsxs(Collapsible.Trigger, { children: [
|
|
615
|
+
/* @__PURE__ */ jsx("span", { children: label ?? name }),
|
|
616
|
+
status ? /* @__PURE__ */ jsx("span", {
|
|
617
|
+
"data-scope": "ai",
|
|
618
|
+
"data-part": "tool-status",
|
|
619
|
+
children: status.charAt(0).toUpperCase() + status.slice(1)
|
|
620
|
+
}) : null,
|
|
621
|
+
/* @__PURE__ */ jsx(Collapsible.Indicator, { children: chevron$2 })
|
|
622
|
+
] }), /* @__PURE__ */ jsx(Collapsible.Content, { children: /* @__PURE__ */ jsxs("div", {
|
|
272
623
|
"data-scope": "ai",
|
|
273
|
-
"data-part": "
|
|
274
|
-
children: /* @__PURE__ */ jsx(
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
"
|
|
280
|
-
|
|
281
|
-
children:
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
height: 14,
|
|
285
|
-
"aria-hidden": "true",
|
|
286
|
-
fill: "none",
|
|
287
|
-
stroke: "currentColor",
|
|
288
|
-
strokeWidth: 1.5,
|
|
289
|
-
strokeLinecap: "square",
|
|
290
|
-
children: /* @__PURE__ */ jsx("path", { d: "M8 13V3M3.5 7.5 8 3l4.5 4.5" })
|
|
291
|
-
})
|
|
292
|
-
})
|
|
293
|
-
})]
|
|
624
|
+
"data-part": "tool-body",
|
|
625
|
+
children: [input ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
626
|
+
"data-scope": "ai",
|
|
627
|
+
"data-part": "tool-label",
|
|
628
|
+
children: "Input"
|
|
629
|
+
}), /* @__PURE__ */ jsx("pre", { children: input })] }) : null, output ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
|
|
630
|
+
"data-scope": "ai",
|
|
631
|
+
"data-part": "tool-label",
|
|
632
|
+
children: "Output"
|
|
633
|
+
}), /* @__PURE__ */ jsx("pre", { children: output })] }) : null]
|
|
634
|
+
}) })]
|
|
294
635
|
});
|
|
295
636
|
}
|
|
637
|
+
injectComponentStyle("ai");
|
|
638
|
+
//#endregion
|
|
639
|
+
//#region src/components/ai/index.tsx
|
|
640
|
+
/** A conversation column: Root is the log, Message carries a role, and
|
|
641
|
+
* the speaking parts — Response, Reasoning, Tool, Sources — part the
|
|
642
|
+
* stream. The interactive folds are the shared Collapsible wearing a
|
|
643
|
+
* `data-ai` marker, so the machine work is never ours. Parts stay
|
|
644
|
+
* agnostic of any client; consumers map their message format (e.g. the
|
|
645
|
+
* `UIMessage` parts re-exported here) onto these primitives. */
|
|
646
|
+
function part$12(name, tag, extra = {}) {
|
|
647
|
+
const Tag = tag;
|
|
648
|
+
const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
|
|
649
|
+
...extra,
|
|
650
|
+
...rest,
|
|
651
|
+
"data-scope": "ai",
|
|
652
|
+
"data-part": name.toLowerCase(),
|
|
653
|
+
children
|
|
654
|
+
});
|
|
655
|
+
Component.displayName = "Ai" + name;
|
|
656
|
+
return Component;
|
|
657
|
+
}
|
|
658
|
+
/** The log itself: the column every stroke lands in, a landmark to
|
|
659
|
+
* screen readers. */
|
|
660
|
+
const Conversation = part$12("Conversation", "div", {
|
|
661
|
+
role: "log",
|
|
662
|
+
"aria-label": "Conversation"
|
|
663
|
+
});
|
|
664
|
+
/** The bubble's inner measure — content that belongs to neither side
|
|
665
|
+
* specifically. */
|
|
666
|
+
const MessageContent = part$12("Content", "div");
|
|
667
|
+
/** The quiet row under a message — copy, retry, feedback. */
|
|
668
|
+
const Actions$4 = part$12("Actions", "div");
|
|
669
|
+
/** The while-it-works whisper for the in-flight turns. */
|
|
670
|
+
const Loader = part$12("Loader", "span", {
|
|
671
|
+
role: "status",
|
|
672
|
+
"aria-label": "Loading"
|
|
673
|
+
});
|
|
674
|
+
/** The whole family under one handle — `Ai.Conversation`,
|
|
675
|
+
* `Ai.Message`, and the rest, exactly as before the split. */
|
|
296
676
|
const Ai = Object.assign(Conversation, {
|
|
297
677
|
Conversation,
|
|
298
|
-
Message,
|
|
299
678
|
MessageContent,
|
|
679
|
+
Actions: Actions$4,
|
|
680
|
+
Loader,
|
|
681
|
+
Message,
|
|
300
682
|
Response,
|
|
301
683
|
Reasoning,
|
|
302
684
|
Tool,
|
|
303
685
|
Sources,
|
|
304
686
|
Source,
|
|
305
|
-
Actions: Actions$3,
|
|
306
687
|
Action,
|
|
307
688
|
Suggestion,
|
|
308
|
-
PromptInput
|
|
309
|
-
Loader
|
|
689
|
+
PromptInput
|
|
310
690
|
});
|
|
311
691
|
injectComponentStyle("ai");
|
|
312
692
|
//#endregion
|
|
693
|
+
//#region src/components/ai-attachments/index.tsx
|
|
694
|
+
const imageGlyph = /* @__PURE__ */ jsxs("svg", {
|
|
695
|
+
viewBox: "0 0 16 16",
|
|
696
|
+
"aria-hidden": "true",
|
|
697
|
+
fill: "none",
|
|
698
|
+
stroke: "currentColor",
|
|
699
|
+
strokeWidth: 1.5,
|
|
700
|
+
strokeLinecap: "square",
|
|
701
|
+
children: [
|
|
702
|
+
/* @__PURE__ */ jsx("rect", {
|
|
703
|
+
x: 2.5,
|
|
704
|
+
y: 3.5,
|
|
705
|
+
width: 11,
|
|
706
|
+
height: 9
|
|
707
|
+
}),
|
|
708
|
+
/* @__PURE__ */ jsx("path", { d: "M2.5 10.5 6 7l3 3 2-1.5 2.5 2" }),
|
|
709
|
+
/* @__PURE__ */ jsx("circle", {
|
|
710
|
+
cx: 6,
|
|
711
|
+
cy: 6,
|
|
712
|
+
r: .75,
|
|
713
|
+
fill: "currentColor",
|
|
714
|
+
stroke: "none"
|
|
715
|
+
})
|
|
716
|
+
]
|
|
717
|
+
});
|
|
718
|
+
const fileGlyph = /* @__PURE__ */ jsxs("svg", {
|
|
719
|
+
viewBox: "0 0 16 16",
|
|
720
|
+
"aria-hidden": "true",
|
|
721
|
+
fill: "none",
|
|
722
|
+
stroke: "currentColor",
|
|
723
|
+
strokeWidth: 1.5,
|
|
724
|
+
strokeLinecap: "square",
|
|
725
|
+
children: [/* @__PURE__ */ jsx("path", { d: "M4 2.5h5l3 3V13.5H4z" }), /* @__PURE__ */ jsx("path", { d: "M9 2.5v3h3" })]
|
|
726
|
+
});
|
|
727
|
+
const removeGlyph = /* @__PURE__ */ jsx("svg", {
|
|
728
|
+
viewBox: "0 0 16 16",
|
|
729
|
+
width: 12,
|
|
730
|
+
height: 12,
|
|
731
|
+
"aria-hidden": "true",
|
|
732
|
+
fill: "none",
|
|
733
|
+
stroke: "currentColor",
|
|
734
|
+
strokeWidth: 1.5,
|
|
735
|
+
strokeLinecap: "square",
|
|
736
|
+
children: /* @__PURE__ */ jsx("path", { d: "M4 4l8 8M12 4l-8 8" })
|
|
737
|
+
});
|
|
738
|
+
const IMAGE_EXTS = [
|
|
739
|
+
"png",
|
|
740
|
+
"jpg",
|
|
741
|
+
"jpeg",
|
|
742
|
+
"gif",
|
|
743
|
+
"webp",
|
|
744
|
+
"svg",
|
|
745
|
+
"avif",
|
|
746
|
+
"bmp",
|
|
747
|
+
"ico"
|
|
748
|
+
];
|
|
749
|
+
const isImage = (name) => IMAGE_EXTS.includes(name.split(".").pop()?.toLowerCase() ?? "");
|
|
750
|
+
const humanSize = (bytes) => {
|
|
751
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
752
|
+
const units = [
|
|
753
|
+
"KB",
|
|
754
|
+
"MB",
|
|
755
|
+
"GB"
|
|
756
|
+
];
|
|
757
|
+
let value = bytes;
|
|
758
|
+
let unit = -1;
|
|
759
|
+
do {
|
|
760
|
+
value /= 1024;
|
|
761
|
+
unit += 1;
|
|
762
|
+
} while (value >= 1024 && unit < units.length - 1);
|
|
763
|
+
return `${value.toFixed(1)} ${units[unit]}`;
|
|
764
|
+
};
|
|
765
|
+
function Attachment({ name, size, status = "ready", onRemove, ...rest }) {
|
|
766
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
767
|
+
...rest,
|
|
768
|
+
"data-scope": "ai",
|
|
769
|
+
"data-part": "attachment",
|
|
770
|
+
"data-status": status,
|
|
771
|
+
children: [
|
|
772
|
+
isImage(name) ? imageGlyph : fileGlyph,
|
|
773
|
+
/* @__PURE__ */ jsx("span", { children: name }),
|
|
774
|
+
size !== void 0 ? /* @__PURE__ */ jsx("span", { children: humanSize(size) }) : null,
|
|
775
|
+
/* @__PURE__ */ jsx("button", {
|
|
776
|
+
type: "button",
|
|
777
|
+
"data-remove": "",
|
|
778
|
+
"aria-label": `Remove ${name}`,
|
|
779
|
+
onClick: () => onRemove?.(),
|
|
780
|
+
children: removeGlyph
|
|
781
|
+
})
|
|
782
|
+
]
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
/** The row the files ride in — a wrapping line of chips. */
|
|
786
|
+
function Attachments({ children, ...rest }) {
|
|
787
|
+
return /* @__PURE__ */ jsx("span", {
|
|
788
|
+
...rest,
|
|
789
|
+
"data-scope": "ai",
|
|
790
|
+
"data-part": "attachments",
|
|
791
|
+
children
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
injectComponentStyle("ai");
|
|
795
|
+
//#endregion
|
|
313
796
|
//#region src/components/alert/index.tsx
|
|
314
797
|
const StatusContext = createContext("ink");
|
|
315
798
|
function glyph(status) {
|
|
@@ -345,7 +828,7 @@ function AlertRoot({ status = "ink", children, ...rest }) {
|
|
|
345
828
|
})
|
|
346
829
|
});
|
|
347
830
|
}
|
|
348
|
-
function Icon$
|
|
831
|
+
function Icon$2() {
|
|
349
832
|
const status = useContext(StatusContext);
|
|
350
833
|
return /* @__PURE__ */ jsx("span", {
|
|
351
834
|
"data-scope": "alert",
|
|
@@ -379,7 +862,7 @@ function Description$5({ children, ...rest }) {
|
|
|
379
862
|
}
|
|
380
863
|
const Alert = Object.assign(AlertRoot, {
|
|
381
864
|
Root: AlertRoot,
|
|
382
|
-
Icon: Icon$
|
|
865
|
+
Icon: Icon$2,
|
|
383
866
|
Body: Body$2,
|
|
384
867
|
Title: Title$4,
|
|
385
868
|
Description: Description$5
|
|
@@ -394,11 +877,56 @@ injectComponentStyle("alert");
|
|
|
394
877
|
const AngleSlider = AngleSlider$1;
|
|
395
878
|
injectComponentStyle("angle-slider");
|
|
396
879
|
//#endregion
|
|
880
|
+
//#region src/components/aspect-ratio/index.tsx
|
|
881
|
+
function AspectRatio({ ratio = "1 / 1", ...rest }) {
|
|
882
|
+
return /* @__PURE__ */ jsx("div", {
|
|
883
|
+
...rest,
|
|
884
|
+
style: {
|
|
885
|
+
...rest.style,
|
|
886
|
+
"--bs-aspect-ratio": ratio
|
|
887
|
+
},
|
|
888
|
+
"data-scope": "aspect-ratio",
|
|
889
|
+
"data-part": "root"
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
injectComponentStyle("aspect-ratio");
|
|
893
|
+
//#endregion
|
|
894
|
+
//#region src/components/autocomplete/index.tsx
|
|
895
|
+
function AutoComplete({ value = "", items = [], placeholder, filter, onValueChange, ...rest }) {
|
|
896
|
+
const { collection, filter: filterItems } = useListCollection$1({
|
|
897
|
+
initialItems: items,
|
|
898
|
+
filter: (item, input) => filter ? filter(item, input) : item.toLowerCase().includes(input.toLowerCase())
|
|
899
|
+
});
|
|
900
|
+
return /* @__PURE__ */ jsxs(Combobox$1.Root, {
|
|
901
|
+
...rest,
|
|
902
|
+
collection,
|
|
903
|
+
inputValue: value,
|
|
904
|
+
allowCustomValue: true,
|
|
905
|
+
onValueChange: (details) => {
|
|
906
|
+
const [first] = details.value;
|
|
907
|
+
if (first != null) onValueChange?.(first);
|
|
908
|
+
},
|
|
909
|
+
onInputValueChange: (details) => {
|
|
910
|
+
filterItems(details.inputValue);
|
|
911
|
+
onValueChange?.(details.inputValue);
|
|
912
|
+
},
|
|
913
|
+
positioning: { sameWidth: true },
|
|
914
|
+
children: [/* @__PURE__ */ jsx(Combobox$1.Control, { children: /* @__PURE__ */ jsx(Combobox$1.Input, { placeholder }) }), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Combobox$1.Positioner, { children: /* @__PURE__ */ jsxs(Combobox$1.Content, { children: [/* @__PURE__ */ jsx(Combobox$1.Empty, { children: "No matches" }), collection.items.map((item) => /* @__PURE__ */ jsx(Combobox$1.Item, {
|
|
915
|
+
item,
|
|
916
|
+
children: /* @__PURE__ */ jsx(Combobox$1.ItemText, { children: item })
|
|
917
|
+
}, item))] }) }) })]
|
|
918
|
+
});
|
|
919
|
+
}
|
|
920
|
+
injectComponentStyle("combobox");
|
|
921
|
+
//#endregion
|
|
397
922
|
//#region src/components/avatar/index.tsx
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
923
|
+
function AvatarRoot({ size, ...rest }) {
|
|
924
|
+
return /* @__PURE__ */ jsx(Avatar$1.Root, {
|
|
925
|
+
...rest,
|
|
926
|
+
"data-size": size
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
const Avatar = Object.assign({}, Avatar$1, { Root: AvatarRoot });
|
|
402
930
|
injectComponentStyle("avatar");
|
|
403
931
|
//#endregion
|
|
404
932
|
//#region src/components/avatar-group/index.tsx
|
|
@@ -412,6 +940,83 @@ function AvatarGroup({ children, ...rest }) {
|
|
|
412
940
|
}
|
|
413
941
|
injectComponentStyle("avatar-group");
|
|
414
942
|
//#endregion
|
|
943
|
+
//#region src/components/back-top/index.tsx
|
|
944
|
+
/** The single glyph a way-home control needs: one stroke pointing up. */
|
|
945
|
+
function chevronUp() {
|
|
946
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
947
|
+
width: 16,
|
|
948
|
+
height: 16,
|
|
949
|
+
viewBox: "0 0 24 24",
|
|
950
|
+
fill: "none",
|
|
951
|
+
stroke: "currentColor",
|
|
952
|
+
strokeWidth: 1.75,
|
|
953
|
+
strokeLinecap: "round",
|
|
954
|
+
strokeLinejoin: "round",
|
|
955
|
+
"aria-hidden": "true",
|
|
956
|
+
children: /* @__PURE__ */ jsx("path", { d: "m6 14 6-6 6 6" })
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* A way home: after the page has scrolled past `threshold`, a small
|
|
961
|
+
* floating control rises at the page's corner and returns the reader to
|
|
962
|
+
* the top. The scroll itself stays native — `window.scrollTo` defers to
|
|
963
|
+
* the stylesheet's `scroll-behavior: smooth`, which reduced motion turns
|
|
964
|
+
* back into an instant jump (a moored control asks the media query
|
|
965
|
+
* directly, since a scroller of its own has no stylesheet to defer to).
|
|
966
|
+
* The button stays mounted either way so the entrance is a transition,
|
|
967
|
+
* never a pop.
|
|
968
|
+
*
|
|
969
|
+
* The control itself is the shared `Button` (outline, square) — the
|
|
970
|
+
* paper, hairline and halo are its; this family owns only the floating
|
|
971
|
+
* and the entrance.
|
|
972
|
+
*/
|
|
973
|
+
function BackTop({ threshold = 400, label = "Back to top", scrollEl, ...rest }) {
|
|
974
|
+
const [visible, setVisible] = useState(false);
|
|
975
|
+
useEffect(() => {
|
|
976
|
+
if (typeof window === "undefined") return;
|
|
977
|
+
const el = scrollEl?.() ?? null;
|
|
978
|
+
const onScroll = () => setVisible((el ? el.scrollTop : window.scrollY) > threshold);
|
|
979
|
+
onScroll();
|
|
980
|
+
if (el) el.addEventListener("scroll", onScroll, { passive: true });
|
|
981
|
+
else window.addEventListener("scroll", onScroll, { passive: true });
|
|
982
|
+
return () => {
|
|
983
|
+
if (el) el.removeEventListener("scroll", onScroll);
|
|
984
|
+
else window.removeEventListener("scroll", onScroll);
|
|
985
|
+
};
|
|
986
|
+
}, [threshold, scrollEl]);
|
|
987
|
+
const toTop = () => {
|
|
988
|
+
const el = scrollEl?.();
|
|
989
|
+
if (!el) {
|
|
990
|
+
window.scrollTo({ top: 0 });
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
994
|
+
el.scrollTo({
|
|
995
|
+
top: 0,
|
|
996
|
+
behavior: reduce ? "auto" : "smooth"
|
|
997
|
+
});
|
|
998
|
+
};
|
|
999
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1000
|
+
"data-scope": "back-top",
|
|
1001
|
+
"data-part": "root",
|
|
1002
|
+
"data-state": visible ? "shown" : "hidden",
|
|
1003
|
+
...scrollEl ? { "data-container": "" } : {},
|
|
1004
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
1005
|
+
...rest,
|
|
1006
|
+
variant: "outline",
|
|
1007
|
+
square: true,
|
|
1008
|
+
size: "lg",
|
|
1009
|
+
type: "button",
|
|
1010
|
+
"aria-label": label,
|
|
1011
|
+
"aria-hidden": visible ? void 0 : "true",
|
|
1012
|
+
tabIndex: visible ? 0 : -1,
|
|
1013
|
+
onClick: toTop,
|
|
1014
|
+
children: rest.children ?? chevronUp()
|
|
1015
|
+
})
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
injectComponentStyle("back-top");
|
|
1019
|
+
//#endregion
|
|
415
1020
|
//#region src/components/badge/index.tsx
|
|
416
1021
|
function Badge({ tone = "ink", variant = "solid", ...rest }) {
|
|
417
1022
|
return /* @__PURE__ */ jsx("span", {
|
|
@@ -428,7 +1033,7 @@ injectComponentStyle("badge");
|
|
|
428
1033
|
/** A trail of waymarks: Root wraps the nav, List the ordered trail, and
|
|
429
1034
|
* each Item carries a Link — or the Current page — parted by a quiet
|
|
430
1035
|
* Separator. Links take href and the rest through attributes. */
|
|
431
|
-
function part$
|
|
1036
|
+
function part$11(name, tag, extra = {}, fallback) {
|
|
432
1037
|
const Tag = tag;
|
|
433
1038
|
const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
|
|
434
1039
|
...extra,
|
|
@@ -440,28 +1045,50 @@ function part$8(name, tag, extra = {}, fallback) {
|
|
|
440
1045
|
Component.displayName = "Breadcrumb" + name;
|
|
441
1046
|
return Component;
|
|
442
1047
|
}
|
|
443
|
-
const Root$
|
|
444
|
-
const List = part$
|
|
445
|
-
const Item$
|
|
446
|
-
const Link$1 = part$
|
|
447
|
-
const Current = part$
|
|
448
|
-
const Separator$1 = part$
|
|
449
|
-
const Breadcrumb = Object.assign(Root$
|
|
450
|
-
Root: Root$
|
|
451
|
-
List,
|
|
452
|
-
Item: Item$
|
|
1048
|
+
const Root$12 = part$11("Root", "nav", { "aria-label": "Breadcrumb" });
|
|
1049
|
+
const List$1 = part$11("List", "ol");
|
|
1050
|
+
const Item$4 = part$11("Item", "li");
|
|
1051
|
+
const Link$1 = part$11("Link", "a");
|
|
1052
|
+
const Current = part$11("Current", "span", { "aria-current": "page" });
|
|
1053
|
+
const Separator$1 = part$11("Separator", "span", { "aria-hidden": "true" }, "/");
|
|
1054
|
+
const Breadcrumb = Object.assign(Root$12, {
|
|
1055
|
+
Root: Root$12,
|
|
1056
|
+
List: List$1,
|
|
1057
|
+
Item: Item$4,
|
|
453
1058
|
Link: Link$1,
|
|
454
1059
|
Current,
|
|
455
1060
|
Separator: Separator$1
|
|
456
1061
|
});
|
|
457
1062
|
injectComponentStyle("breadcrumb");
|
|
458
1063
|
//#endregion
|
|
1064
|
+
//#region src/components/button-group/index.tsx
|
|
1065
|
+
/**
|
|
1066
|
+
* Buttons fused into one control: the group owns only the joinery, so
|
|
1067
|
+
* members keep every variant they were given — a solid action can sit
|
|
1068
|
+
* beside an outline one and the seam still reads. Selection belongs to
|
|
1069
|
+
* the toggle group; this is layout alone.
|
|
1070
|
+
*/
|
|
1071
|
+
function Root$11({ orientation = "horizontal", size, radius, children, ...rest }) {
|
|
1072
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1073
|
+
...rest,
|
|
1074
|
+
role: "group",
|
|
1075
|
+
"data-scope": "button-group",
|
|
1076
|
+
"data-part": "root",
|
|
1077
|
+
"data-orientation": orientation,
|
|
1078
|
+
"data-size": size,
|
|
1079
|
+
"data-radius": radius,
|
|
1080
|
+
children
|
|
1081
|
+
});
|
|
1082
|
+
}
|
|
1083
|
+
const ButtonGroup = Object.assign(Root$11, { Root: Root$11 });
|
|
1084
|
+
injectComponentStyle("button-group");
|
|
1085
|
+
//#endregion
|
|
459
1086
|
//#region src/components/banner/index.tsx
|
|
460
1087
|
/** A page-level notice, spoken across the full measure: a wash of the
|
|
461
1088
|
* status pigment, one heavier hairline on the leading edge, and room
|
|
462
1089
|
* for actions and a quiet close. Ink is the neutral register; the four
|
|
463
1090
|
* semantic pigments are fixed. */
|
|
464
|
-
const Root$
|
|
1091
|
+
const Root$10 = ({ status = "ink", children, ...rest }) => /* @__PURE__ */ jsx("div", {
|
|
465
1092
|
...rest,
|
|
466
1093
|
role: status === "ink" ? void 0 : "status",
|
|
467
1094
|
"data-scope": "banner",
|
|
@@ -469,7 +1096,7 @@ const Root$6 = ({ status = "ink", children, ...rest }) => /* @__PURE__ */ jsx("d
|
|
|
469
1096
|
"data-status": status,
|
|
470
1097
|
children
|
|
471
1098
|
});
|
|
472
|
-
function part$
|
|
1099
|
+
function part$10(name, tag) {
|
|
473
1100
|
const Tag = tag;
|
|
474
1101
|
const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
|
|
475
1102
|
...rest,
|
|
@@ -480,14 +1107,14 @@ function part$7(name, tag) {
|
|
|
480
1107
|
Component.displayName = "Banner" + name;
|
|
481
1108
|
return Component;
|
|
482
1109
|
}
|
|
483
|
-
const Icon = part$
|
|
484
|
-
const Body$1 = part$
|
|
485
|
-
const Title$3 = part$
|
|
486
|
-
const Description$4 = part$
|
|
487
|
-
const Actions$
|
|
1110
|
+
const Icon$1 = part$10("Icon", "span");
|
|
1111
|
+
const Body$1 = part$10("Body", "div");
|
|
1112
|
+
const Title$3 = part$10("Title", "p");
|
|
1113
|
+
const Description$4 = part$10("Description", "p");
|
|
1114
|
+
const Actions$3 = part$10("Actions", "div");
|
|
488
1115
|
/** The quiet close: a plain square-cut button; dismissal stays the
|
|
489
1116
|
* consumer's state. */
|
|
490
|
-
const Close = (
|
|
1117
|
+
const Close = (rest) => /* @__PURE__ */ jsx("button", {
|
|
491
1118
|
...rest,
|
|
492
1119
|
type: rest.type ?? "button",
|
|
493
1120
|
"aria-label": rest["aria-label"] ?? "Dismiss",
|
|
@@ -505,19 +1132,19 @@ const Close = ({ children, ...rest }) => /* @__PURE__ */ jsx("button", {
|
|
|
505
1132
|
})
|
|
506
1133
|
})
|
|
507
1134
|
});
|
|
508
|
-
const Banner = Object.assign(Root$
|
|
509
|
-
Root: Root$
|
|
510
|
-
Icon,
|
|
1135
|
+
const Banner = Object.assign(Root$10, {
|
|
1136
|
+
Root: Root$10,
|
|
1137
|
+
Icon: Icon$1,
|
|
511
1138
|
Body: Body$1,
|
|
512
1139
|
Title: Title$3,
|
|
513
1140
|
Description: Description$4,
|
|
514
|
-
Actions: Actions$
|
|
1141
|
+
Actions: Actions$3,
|
|
515
1142
|
Close
|
|
516
1143
|
});
|
|
517
1144
|
injectComponentStyle("banner");
|
|
518
1145
|
//#endregion
|
|
519
1146
|
//#region src/components/calendar/index.tsx
|
|
520
|
-
function chevron(dir) {
|
|
1147
|
+
function chevron$1(dir) {
|
|
521
1148
|
return /* @__PURE__ */ jsx("svg", {
|
|
522
1149
|
width: 16,
|
|
523
1150
|
height: 16,
|
|
@@ -551,9 +1178,9 @@ function Calendar({ value, min, max, onValueChange, children, ...rest }) {
|
|
|
551
1178
|
"data-scope": "calendar",
|
|
552
1179
|
"data-part": "header",
|
|
553
1180
|
children: /* @__PURE__ */ jsxs(DatePicker$1.ViewControl, { children: [
|
|
554
|
-
/* @__PURE__ */ jsx(DatePicker$1.PrevTrigger, { children: chevron("left") }),
|
|
1181
|
+
/* @__PURE__ */ jsx(DatePicker$1.PrevTrigger, { children: chevron$1("left") }),
|
|
555
1182
|
/* @__PURE__ */ jsx(DatePicker$1.Context, { children: (dp) => /* @__PURE__ */ jsx(DatePicker$1.ViewTrigger, { children: title(dp) }) }),
|
|
556
|
-
/* @__PURE__ */ jsx(DatePicker$1.NextTrigger, { children: chevron("right") })
|
|
1183
|
+
/* @__PURE__ */ jsx(DatePicker$1.NextTrigger, { children: chevron$1("right") })
|
|
557
1184
|
] })
|
|
558
1185
|
});
|
|
559
1186
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -606,12 +1233,13 @@ function Calendar({ value, min, max, onValueChange, children, ...rest }) {
|
|
|
606
1233
|
});
|
|
607
1234
|
}
|
|
608
1235
|
injectComponentStyle("calendar");
|
|
1236
|
+
injectComponentStyle("date-picker");
|
|
609
1237
|
//#endregion
|
|
610
1238
|
//#region src/components/card/index.tsx
|
|
611
1239
|
/** A vessel: round, resting at the first elevation, one hairline for its
|
|
612
1240
|
* edge. Root, Header, Title, Description, Content, Footer — sections
|
|
613
1241
|
* carry their own whitespace, so any subset composes. */
|
|
614
|
-
function part$
|
|
1242
|
+
function part$9(name, tag) {
|
|
615
1243
|
const Tag = tag;
|
|
616
1244
|
const Component = ({ asChild, children, ...rest }) => {
|
|
617
1245
|
const partProps = {
|
|
@@ -623,29 +1251,253 @@ function part$6(name, tag) {
|
|
|
623
1251
|
const child = Children.only(children);
|
|
624
1252
|
return isValidElement(child) ? cloneElement(child, partProps) : null;
|
|
625
1253
|
}
|
|
626
|
-
return /* @__PURE__ */ jsx(Tag, {
|
|
627
|
-
...partProps,
|
|
628
|
-
children
|
|
1254
|
+
return /* @__PURE__ */ jsx(Tag, {
|
|
1255
|
+
...partProps,
|
|
1256
|
+
children
|
|
1257
|
+
});
|
|
1258
|
+
};
|
|
1259
|
+
Component.displayName = "Card" + name;
|
|
1260
|
+
return Component;
|
|
1261
|
+
}
|
|
1262
|
+
const Root$9 = part$9("Root", "section");
|
|
1263
|
+
const Header$1 = part$9("Header", "header");
|
|
1264
|
+
const Title$2 = part$9("Title", "h3");
|
|
1265
|
+
const Description$3 = part$9("Description", "p");
|
|
1266
|
+
const Content$3 = part$9("Content", "div");
|
|
1267
|
+
const Footer$1 = part$9("Footer", "footer");
|
|
1268
|
+
const Card = Object.assign(Root$9, {
|
|
1269
|
+
Root: Root$9,
|
|
1270
|
+
Header: Header$1,
|
|
1271
|
+
Title: Title$2,
|
|
1272
|
+
Description: Description$3,
|
|
1273
|
+
Content: Content$3,
|
|
1274
|
+
Footer: Footer$1
|
|
1275
|
+
});
|
|
1276
|
+
injectComponentStyle("card");
|
|
1277
|
+
//#endregion
|
|
1278
|
+
//#region src/components/input/index.tsx
|
|
1279
|
+
function Input({ value, size = "md", invalid = false, onValueChange, ...rest }) {
|
|
1280
|
+
const field = useFieldContext();
|
|
1281
|
+
const fieldProps = field?.getInputProps() ?? {};
|
|
1282
|
+
return /* @__PURE__ */ jsx("input", {
|
|
1283
|
+
...fieldProps,
|
|
1284
|
+
...rest,
|
|
1285
|
+
...value !== void 0 ? { value } : {},
|
|
1286
|
+
"data-scope": "input",
|
|
1287
|
+
"data-part": "root",
|
|
1288
|
+
"data-size": size,
|
|
1289
|
+
"data-invalid": invalid || field?.invalid ? "" : void 0,
|
|
1290
|
+
onChange: (event) => onValueChange?.(event.target.value)
|
|
1291
|
+
});
|
|
1292
|
+
}
|
|
1293
|
+
injectComponentStyle("input");
|
|
1294
|
+
//#endregion
|
|
1295
|
+
//#region src/components/cascade-select/index.tsx
|
|
1296
|
+
const chevronDown$2 = /* @__PURE__ */ jsx("svg", {
|
|
1297
|
+
viewBox: "0 0 24 24",
|
|
1298
|
+
fill: "none",
|
|
1299
|
+
stroke: "currentColor",
|
|
1300
|
+
strokeWidth: 2,
|
|
1301
|
+
strokeLinecap: "round",
|
|
1302
|
+
strokeLinejoin: "round",
|
|
1303
|
+
"aria-hidden": "true",
|
|
1304
|
+
children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
|
|
1305
|
+
});
|
|
1306
|
+
const chevronRight = /* @__PURE__ */ jsx("svg", {
|
|
1307
|
+
viewBox: "0 0 24 24",
|
|
1308
|
+
fill: "none",
|
|
1309
|
+
stroke: "currentColor",
|
|
1310
|
+
strokeWidth: 2,
|
|
1311
|
+
strokeLinecap: "round",
|
|
1312
|
+
strokeLinejoin: "round",
|
|
1313
|
+
"aria-hidden": "true",
|
|
1314
|
+
children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
|
|
1315
|
+
});
|
|
1316
|
+
const checkGlyph$2 = /* @__PURE__ */ jsx("svg", {
|
|
1317
|
+
viewBox: "0 0 24 24",
|
|
1318
|
+
fill: "none",
|
|
1319
|
+
stroke: "currentColor",
|
|
1320
|
+
strokeWidth: 3,
|
|
1321
|
+
strokeLinecap: "round",
|
|
1322
|
+
strokeLinejoin: "round",
|
|
1323
|
+
"aria-hidden": "true",
|
|
1324
|
+
children: /* @__PURE__ */ jsx("path", { d: "m5 12.5 5 5L19 7" })
|
|
1325
|
+
});
|
|
1326
|
+
function CascadeSelect({ value, data, placeholder = "Select…", highlightTrigger, filterable = false, multiple = false, disabled = false, onValueChange, ...rest }) {
|
|
1327
|
+
const id = useId();
|
|
1328
|
+
const locale = useLocaleContext();
|
|
1329
|
+
const env = useEnvironmentContext();
|
|
1330
|
+
const collection = cascade.collection({
|
|
1331
|
+
nodeToValue: (node) => node.value,
|
|
1332
|
+
nodeToString: (node) => node.label,
|
|
1333
|
+
nodeToChildren: (node) => node.children ?? [],
|
|
1334
|
+
rootNode: {
|
|
1335
|
+
value: "ROOT",
|
|
1336
|
+
label: "",
|
|
1337
|
+
children: data
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
1340
|
+
const service = useMachine(cascade.machine, {
|
|
1341
|
+
id,
|
|
1342
|
+
collection,
|
|
1343
|
+
dir: locale.dir,
|
|
1344
|
+
getRootNode: env.getRootNode.bind(env),
|
|
1345
|
+
disabled: disabled || void 0,
|
|
1346
|
+
multiple: multiple || void 0,
|
|
1347
|
+
...value !== void 0 ? { value } : {},
|
|
1348
|
+
...highlightTrigger != null ? { highlightTrigger } : {},
|
|
1349
|
+
onValueChange(details) {
|
|
1350
|
+
onValueChange?.(details.value);
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1353
|
+
const api = cascade.connect(service, normalizeProps);
|
|
1354
|
+
const [query, setQuery] = useState("");
|
|
1355
|
+
const filterFns = useFilter({ sensitivity: "base" });
|
|
1356
|
+
const filtering = filterable && query.trim().length > 0;
|
|
1357
|
+
/** The corridor's answer to a query: every leaf whose route matches —
|
|
1358
|
+
* the query may land on any hop, and the whole route still shows. */
|
|
1359
|
+
const matchPaths = useMemo(() => {
|
|
1360
|
+
if (!filtering) return [];
|
|
1361
|
+
const q = query.trim();
|
|
1362
|
+
const hits = [];
|
|
1363
|
+
const walk = (nodes, path, labels, matched) => {
|
|
1364
|
+
for (const node of nodes ?? []) {
|
|
1365
|
+
const hit = matched || filterFns.contains(node.label, q);
|
|
1366
|
+
const nextPath = [...path, node.value];
|
|
1367
|
+
const nextLabels = [...labels, node.label];
|
|
1368
|
+
if (node.children?.length) walk(node.children, nextPath, nextLabels, hit);
|
|
1369
|
+
else if (hit) hits.push({
|
|
1370
|
+
path: nextPath,
|
|
1371
|
+
labels: nextLabels
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
walk(data, [], [], false);
|
|
1376
|
+
return hits;
|
|
1377
|
+
}, [
|
|
1378
|
+
filtering,
|
|
1379
|
+
query,
|
|
1380
|
+
filterFns,
|
|
1381
|
+
data
|
|
1382
|
+
]);
|
|
1383
|
+
const isSelected = (path) => (value ?? []).some((p) => p.join("/") === path.join("/"));
|
|
1384
|
+
function pickMatch(path) {
|
|
1385
|
+
if (multiple) {
|
|
1386
|
+
const current = value ?? [];
|
|
1387
|
+
const key = path.join("/");
|
|
1388
|
+
const next = current.some((p) => p.join("/") === key) ? current.filter((p) => p.join("/") !== key) : [...current, path];
|
|
1389
|
+
api.setValue(next);
|
|
1390
|
+
} else {
|
|
1391
|
+
api.selectValue(path);
|
|
1392
|
+
api.setOpen(false);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
/** The machine's `valueAsString` only updates through its select
|
|
1396
|
+
* event, so an externally-set `value` would render the placeholder
|
|
1397
|
+
* forever — resolve the labels from the data instead. */
|
|
1398
|
+
function labelsFor(path) {
|
|
1399
|
+
const out = [];
|
|
1400
|
+
let nodes = data;
|
|
1401
|
+
for (const entry of path) {
|
|
1402
|
+
const node = nodes?.find((n) => n.value === entry);
|
|
1403
|
+
if (!node) return null;
|
|
1404
|
+
out.push(node.label);
|
|
1405
|
+
nodes = node.children;
|
|
1406
|
+
}
|
|
1407
|
+
return out;
|
|
1408
|
+
}
|
|
1409
|
+
const display = (value ?? []).map((path) => labelsFor(path)?.join(" / ")).filter((label) => label != null).join(", ");
|
|
1410
|
+
/** One column per walk of the highlighted path — the list binds to
|
|
1411
|
+
* the node, the next column hangs off its highlighted child. */
|
|
1412
|
+
function renderColumn(node, indexPath, valuePath) {
|
|
1413
|
+
const nodeProps = {
|
|
1414
|
+
item: node,
|
|
1415
|
+
indexPath,
|
|
1416
|
+
value: valuePath
|
|
1417
|
+
};
|
|
1418
|
+
const nodeState = api.getItemState(nodeProps);
|
|
1419
|
+
const children = collection.getNodeChildren(node);
|
|
1420
|
+
const list = /* @__PURE__ */ jsx("ul", {
|
|
1421
|
+
...api.getListProps(nodeProps),
|
|
1422
|
+
children: children.map((item, index) => {
|
|
1423
|
+
const itemValue = collection.getNodeValue(item);
|
|
1424
|
+
const itemProps = {
|
|
1425
|
+
item,
|
|
1426
|
+
indexPath: [...indexPath, index],
|
|
1427
|
+
value: [...valuePath, itemValue]
|
|
1428
|
+
};
|
|
1429
|
+
const itemState = api.getItemState(itemProps);
|
|
1430
|
+
return /* @__PURE__ */ jsxs("li", {
|
|
1431
|
+
...api.getItemProps(itemProps),
|
|
1432
|
+
children: [
|
|
1433
|
+
/* @__PURE__ */ jsx("span", {
|
|
1434
|
+
...api.getItemTextProps(itemProps),
|
|
1435
|
+
children: item.label
|
|
1436
|
+
}),
|
|
1437
|
+
itemState.hasChildren ? /* @__PURE__ */ jsx("span", {
|
|
1438
|
+
"data-part": "branch-indicator",
|
|
1439
|
+
"aria-hidden": "true",
|
|
1440
|
+
children: chevronRight
|
|
1441
|
+
}) : null,
|
|
1442
|
+
/* @__PURE__ */ jsx("span", {
|
|
1443
|
+
...api.getItemIndicatorProps(itemProps),
|
|
1444
|
+
children: checkGlyph$2
|
|
1445
|
+
})
|
|
1446
|
+
]
|
|
1447
|
+
}, itemValue);
|
|
1448
|
+
})
|
|
629
1449
|
});
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
1450
|
+
const next = nodeState.highlightedChild;
|
|
1451
|
+
return [list, next != null && collection.isBranchNode(next) ? renderColumn(next, [...indexPath, nodeState.highlightedIndex], [...valuePath, collection.getNodeValue(next)]) : null];
|
|
1452
|
+
}
|
|
1453
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1454
|
+
...rest,
|
|
1455
|
+
...api.getRootProps(),
|
|
1456
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1457
|
+
...api.getControlProps(),
|
|
1458
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
1459
|
+
...api.getTriggerProps(),
|
|
1460
|
+
onClick: (event) => {
|
|
1461
|
+
api.getTriggerProps().onClick?.(event);
|
|
1462
|
+
setQuery("");
|
|
1463
|
+
},
|
|
1464
|
+
disabled: disabled || void 0,
|
|
1465
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1466
|
+
...api.getValueTextProps(),
|
|
1467
|
+
children: display || placeholder
|
|
1468
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1469
|
+
...api.getIndicatorProps(),
|
|
1470
|
+
children: chevronDown$2
|
|
1471
|
+
})]
|
|
1472
|
+
})
|
|
1473
|
+
}), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx("div", {
|
|
1474
|
+
...api.getPositionerProps(),
|
|
1475
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1476
|
+
...api.getContentProps(),
|
|
1477
|
+
children: [filterable ? /* @__PURE__ */ jsx("div", {
|
|
1478
|
+
"data-part": "search",
|
|
1479
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
1480
|
+
size: "sm",
|
|
1481
|
+
value: query,
|
|
1482
|
+
onValueChange: setQuery,
|
|
1483
|
+
placeholder: "Filter…",
|
|
1484
|
+
"aria-label": "Filter options"
|
|
1485
|
+
})
|
|
1486
|
+
}) : null, filtering ? matchPaths.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
1487
|
+
"data-part": "empty",
|
|
1488
|
+
children: "Nothing matches"
|
|
1489
|
+
}) : matchPaths.map((hit) => /* @__PURE__ */ jsx("button", {
|
|
1490
|
+
type: "button",
|
|
1491
|
+
"data-part": "match",
|
|
1492
|
+
"data-selected": isSelected(hit.path) || void 0,
|
|
1493
|
+
onClick: () => pickMatch(hit.path),
|
|
1494
|
+
children: hit.labels.join(" / ")
|
|
1495
|
+
}, hit.path.join("/"))) : renderColumn(collection.rootNode, [], [])]
|
|
1496
|
+
})
|
|
1497
|
+
}) })]
|
|
1498
|
+
});
|
|
633
1499
|
}
|
|
634
|
-
|
|
635
|
-
const Header = part$6("Header", "header");
|
|
636
|
-
const Title$2 = part$6("Title", "h3");
|
|
637
|
-
const Description$3 = part$6("Description", "p");
|
|
638
|
-
const Content$1 = part$6("Content", "div");
|
|
639
|
-
const Footer = part$6("Footer", "footer");
|
|
640
|
-
const Card = Object.assign(Root$5, {
|
|
641
|
-
Root: Root$5,
|
|
642
|
-
Header,
|
|
643
|
-
Title: Title$2,
|
|
644
|
-
Description: Description$3,
|
|
645
|
-
Content: Content$1,
|
|
646
|
-
Footer
|
|
647
|
-
});
|
|
648
|
-
injectComponentStyle("card");
|
|
1500
|
+
injectComponentStyle("cascade-select");
|
|
649
1501
|
//#endregion
|
|
650
1502
|
//#region src/components/carousel/index.tsx
|
|
651
1503
|
/** Ark's Carousel, dressed in the paper-and-ink system: slides ride in one
|
|
@@ -664,6 +1516,59 @@ injectComponentStyle("carousel");
|
|
|
664
1516
|
const Checkbox = Checkbox$1;
|
|
665
1517
|
injectComponentStyle("checkbox");
|
|
666
1518
|
//#endregion
|
|
1519
|
+
//#region src/components/checkbox-group/index.tsx
|
|
1520
|
+
const checkGlyph$1 = /* @__PURE__ */ jsx("svg", {
|
|
1521
|
+
viewBox: "0 0 24 24",
|
|
1522
|
+
fill: "none",
|
|
1523
|
+
stroke: "currentColor",
|
|
1524
|
+
strokeWidth: 3,
|
|
1525
|
+
strokeLinecap: "round",
|
|
1526
|
+
strokeLinejoin: "round",
|
|
1527
|
+
"aria-hidden": "true",
|
|
1528
|
+
children: /* @__PURE__ */ jsx("path", { d: "m5 12.5 5 5L19 7" })
|
|
1529
|
+
});
|
|
1530
|
+
/**
|
|
1531
|
+
* One question, many answers: a labelled stack (or row) of the seal-cut
|
|
1532
|
+
* checkboxes bound to a single array. Toggling a box adds or removes its
|
|
1533
|
+
* value; the group itself is semantics (`role="group"`), the boxes stay
|
|
1534
|
+
* the machine-driven originals. Inside a `Field.Root` the group picks up
|
|
1535
|
+
* the field context, so the invalid and disabled states a Form routes to
|
|
1536
|
+
* its name dress every box at once.
|
|
1537
|
+
*/
|
|
1538
|
+
function CheckboxGroup({ value = [], options, layout = "vertical", invalid = false, disabled = false, onValueChange, ...rest }) {
|
|
1539
|
+
const field = useFieldContext();
|
|
1540
|
+
const selected = new Set(value);
|
|
1541
|
+
const isInvalid = invalid || field?.invalid === true;
|
|
1542
|
+
const isDisabled = disabled || field?.disabled === true;
|
|
1543
|
+
function toggle(option) {
|
|
1544
|
+
const next = new Set(selected);
|
|
1545
|
+
if (next.has(option)) next.delete(option);
|
|
1546
|
+
else next.add(option);
|
|
1547
|
+
onValueChange?.([...next]);
|
|
1548
|
+
}
|
|
1549
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1550
|
+
...rest,
|
|
1551
|
+
role: "group",
|
|
1552
|
+
"data-scope": "checkbox-group",
|
|
1553
|
+
"data-part": "root",
|
|
1554
|
+
"data-layout": layout,
|
|
1555
|
+
"data-invalid": isInvalid ? "" : void 0,
|
|
1556
|
+
children: options.map((option) => /* @__PURE__ */ jsxs(Checkbox$1.Root, {
|
|
1557
|
+
checked: selected.has(option.value),
|
|
1558
|
+
invalid: isInvalid,
|
|
1559
|
+
disabled: isDisabled || option.disabled === true,
|
|
1560
|
+
onCheckedChange: () => toggle(option.value),
|
|
1561
|
+
children: [
|
|
1562
|
+
/* @__PURE__ */ jsx(Checkbox$1.Control, { children: /* @__PURE__ */ jsx(Checkbox$1.Indicator, { children: checkGlyph$1 }) }),
|
|
1563
|
+
/* @__PURE__ */ jsx(Checkbox$1.Label, { children: option.label }),
|
|
1564
|
+
/* @__PURE__ */ jsx(Checkbox$1.HiddenInput, {})
|
|
1565
|
+
]
|
|
1566
|
+
}, option.value))
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
injectComponentStyle("checkbox-group");
|
|
1570
|
+
injectComponentStyle("checkbox");
|
|
1571
|
+
//#endregion
|
|
667
1572
|
//#region src/components/chip/index.tsx
|
|
668
1573
|
function Chip({ value, max, tone = "ink", variant = "solid", ...rest }) {
|
|
669
1574
|
const text = max != null && value > max ? `${max}+` : String(value);
|
|
@@ -707,6 +1612,227 @@ injectComponentStyle("color-picker");
|
|
|
707
1612
|
const Combobox = Combobox$1;
|
|
708
1613
|
injectComponentStyle("combobox");
|
|
709
1614
|
//#endregion
|
|
1615
|
+
//#region src/components/command/index.tsx
|
|
1616
|
+
function Command({ items = [], placeholder, open, autoFilter = true, inputValue, emptyText = "No matching commands", onSelect, onOpenChange, onInputValueChange }) {
|
|
1617
|
+
const [internalOpen, setInternalOpen] = useState(false);
|
|
1618
|
+
const [listOpen, setListOpen] = useState(true);
|
|
1619
|
+
const [fieldText, setFieldText] = useState("");
|
|
1620
|
+
const { collection, set, filter } = useListCollection$1({
|
|
1621
|
+
initialItems: items.map((entry) => entry.value),
|
|
1622
|
+
filter: (value, input) => {
|
|
1623
|
+
const entry = items.find((candidate) => candidate.value === value);
|
|
1624
|
+
if (!entry) return false;
|
|
1625
|
+
const query = input.toLowerCase();
|
|
1626
|
+
return entry.label.toLowerCase().includes(query) || value.toLowerCase().includes(query);
|
|
1627
|
+
}
|
|
1628
|
+
});
|
|
1629
|
+
useEffect(() => {
|
|
1630
|
+
set(items.map((entry) => entry.value));
|
|
1631
|
+
if (autoFilter) filter(fieldText);
|
|
1632
|
+
}, [items]);
|
|
1633
|
+
const currentOpen = open ?? internalOpen;
|
|
1634
|
+
useEffect(() => {
|
|
1635
|
+
if (currentOpen) setListOpen(true);
|
|
1636
|
+
}, [currentOpen]);
|
|
1637
|
+
const setOpen = (value) => {
|
|
1638
|
+
if (open === void 0) setInternalOpen(value);
|
|
1639
|
+
onOpenChange?.(value);
|
|
1640
|
+
};
|
|
1641
|
+
const available = new Set(collection.items);
|
|
1642
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1643
|
+
for (const entry of items) {
|
|
1644
|
+
if (!available.has(entry.value)) continue;
|
|
1645
|
+
const key = entry.group ?? "";
|
|
1646
|
+
if (!groups.has(key)) groups.set(key, []);
|
|
1647
|
+
groups.get(key).push(entry);
|
|
1648
|
+
}
|
|
1649
|
+
const row = (entry) => /* @__PURE__ */ jsx(Combobox$1.Item, {
|
|
1650
|
+
item: entry.value,
|
|
1651
|
+
asChild: true,
|
|
1652
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1653
|
+
"data-scope": "command",
|
|
1654
|
+
"data-part": "item",
|
|
1655
|
+
children: [/* @__PURE__ */ jsx(Combobox$1.ItemText, {
|
|
1656
|
+
asChild: true,
|
|
1657
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
1658
|
+
"data-scope": "command",
|
|
1659
|
+
"data-part": "item-label",
|
|
1660
|
+
children: entry.label
|
|
1661
|
+
})
|
|
1662
|
+
}), entry.hint ? /* @__PURE__ */ jsx("span", {
|
|
1663
|
+
"data-scope": "command",
|
|
1664
|
+
"data-part": "item-hint",
|
|
1665
|
+
children: entry.hint
|
|
1666
|
+
}) : void 0]
|
|
1667
|
+
})
|
|
1668
|
+
}, entry.value);
|
|
1669
|
+
return /* @__PURE__ */ jsx(Dialog$1.Root, {
|
|
1670
|
+
open: currentOpen,
|
|
1671
|
+
onOpenChange: (details) => setOpen(details.open),
|
|
1672
|
+
children: /* @__PURE__ */ jsxs(Portal, { children: [/* @__PURE__ */ jsx(Dialog$1.Backdrop, {}), /* @__PURE__ */ jsx(Dialog$1.Positioner, {
|
|
1673
|
+
asChild: true,
|
|
1674
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1675
|
+
"data-scope": "command",
|
|
1676
|
+
"data-part": "positioner",
|
|
1677
|
+
children: /* @__PURE__ */ jsx(Dialog$1.Content, {
|
|
1678
|
+
asChild: true,
|
|
1679
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1680
|
+
"data-scope": "command",
|
|
1681
|
+
"data-part": "content",
|
|
1682
|
+
children: /* @__PURE__ */ jsxs(Combobox$1.Root, {
|
|
1683
|
+
collection,
|
|
1684
|
+
inputValue,
|
|
1685
|
+
open: listOpen,
|
|
1686
|
+
onOpenChange: (details) => setListOpen(details.open),
|
|
1687
|
+
inputBehavior: "autohighlight",
|
|
1688
|
+
loopFocus: true,
|
|
1689
|
+
onInputValueChange: (details) => {
|
|
1690
|
+
setFieldText(details.inputValue);
|
|
1691
|
+
if (autoFilter) filter(details.inputValue);
|
|
1692
|
+
onInputValueChange?.(details.inputValue);
|
|
1693
|
+
},
|
|
1694
|
+
onValueChange: (details) => {
|
|
1695
|
+
const [first] = details.value;
|
|
1696
|
+
if (first == null) return;
|
|
1697
|
+
onSelect?.(first);
|
|
1698
|
+
setOpen(false);
|
|
1699
|
+
},
|
|
1700
|
+
children: [/* @__PURE__ */ jsx(Combobox$1.Input, {
|
|
1701
|
+
asChild: true,
|
|
1702
|
+
children: /* @__PURE__ */ jsx("input", {
|
|
1703
|
+
type: "text",
|
|
1704
|
+
placeholder,
|
|
1705
|
+
"data-scope": "command",
|
|
1706
|
+
"data-part": "input"
|
|
1707
|
+
})
|
|
1708
|
+
}), /* @__PURE__ */ jsx(Combobox$1.Content, {
|
|
1709
|
+
asChild: true,
|
|
1710
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1711
|
+
"data-scope": "command",
|
|
1712
|
+
"data-part": "list",
|
|
1713
|
+
children: [Array.from(groups, ([group, entries]) => group === "" ? entries.map(row) : /* @__PURE__ */ jsx(Combobox$1.ItemGroup, {
|
|
1714
|
+
asChild: true,
|
|
1715
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
1716
|
+
"data-scope": "command",
|
|
1717
|
+
"data-part": "group",
|
|
1718
|
+
children: [/* @__PURE__ */ jsx(Combobox$1.ItemGroupLabel, {
|
|
1719
|
+
asChild: true,
|
|
1720
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1721
|
+
"data-scope": "command",
|
|
1722
|
+
"data-part": "group-label",
|
|
1723
|
+
children: group
|
|
1724
|
+
})
|
|
1725
|
+
}), entries.map(row)]
|
|
1726
|
+
})
|
|
1727
|
+
}, group)), groups.size === 0 ? /* @__PURE__ */ jsx("div", {
|
|
1728
|
+
"data-scope": "command",
|
|
1729
|
+
"data-part": "empty",
|
|
1730
|
+
children: emptyText
|
|
1731
|
+
}) : null]
|
|
1732
|
+
})
|
|
1733
|
+
})]
|
|
1734
|
+
})
|
|
1735
|
+
})
|
|
1736
|
+
})
|
|
1737
|
+
})
|
|
1738
|
+
})] })
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
injectComponentStyle("command");
|
|
1742
|
+
injectComponentStyle("dialog");
|
|
1743
|
+
//#endregion
|
|
1744
|
+
//#region src/components/comment/index.tsx
|
|
1745
|
+
function Comment({ author, datetime, avatar, actions, children, ...rest }) {
|
|
1746
|
+
return /* @__PURE__ */ jsxs("article", {
|
|
1747
|
+
...rest,
|
|
1748
|
+
"data-scope": "comment",
|
|
1749
|
+
"data-part": "root",
|
|
1750
|
+
children: [avatar ? /* @__PURE__ */ jsx("div", {
|
|
1751
|
+
"data-scope": "comment",
|
|
1752
|
+
"data-part": "avatar",
|
|
1753
|
+
children: avatar
|
|
1754
|
+
}) : null, /* @__PURE__ */ jsxs("div", {
|
|
1755
|
+
"data-scope": "comment",
|
|
1756
|
+
"data-part": "body",
|
|
1757
|
+
children: [
|
|
1758
|
+
author || datetime ? /* @__PURE__ */ jsxs("header", {
|
|
1759
|
+
"data-scope": "comment",
|
|
1760
|
+
"data-part": "header",
|
|
1761
|
+
children: [author ? /* @__PURE__ */ jsx("span", {
|
|
1762
|
+
"data-scope": "comment",
|
|
1763
|
+
"data-part": "author",
|
|
1764
|
+
children: author
|
|
1765
|
+
}) : null, datetime ? /* @__PURE__ */ jsx("time", {
|
|
1766
|
+
"data-scope": "comment",
|
|
1767
|
+
"data-part": "datetime",
|
|
1768
|
+
dateTime: datetime,
|
|
1769
|
+
children: datetime
|
|
1770
|
+
}) : null]
|
|
1771
|
+
}) : null,
|
|
1772
|
+
/* @__PURE__ */ jsx("div", {
|
|
1773
|
+
"data-scope": "comment",
|
|
1774
|
+
"data-part": "content",
|
|
1775
|
+
children
|
|
1776
|
+
}),
|
|
1777
|
+
actions ? /* @__PURE__ */ jsx("div", {
|
|
1778
|
+
"data-scope": "comment",
|
|
1779
|
+
"data-part": "actions",
|
|
1780
|
+
children: actions
|
|
1781
|
+
}) : null
|
|
1782
|
+
]
|
|
1783
|
+
})]
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
injectComponentStyle("comment");
|
|
1787
|
+
//#endregion
|
|
1788
|
+
//#region src/components/config-provider/index.tsx
|
|
1789
|
+
const ConfigContextImpl = createContext({});
|
|
1790
|
+
/** The nearest provider's snapshot, or an empty one when no provider
|
|
1791
|
+
* wraps the caller — the hook the date and format families will consume
|
|
1792
|
+
* to localize their output. */
|
|
1793
|
+
function useConfig() {
|
|
1794
|
+
return useContext(ConfigContextImpl);
|
|
1795
|
+
}
|
|
1796
|
+
/**
|
|
1797
|
+
* The declarative host for global configuration: one element that both
|
|
1798
|
+
* carries the token layer's attributes — `[data-density]` and
|
|
1799
|
+
* `[data-accent]` fire on any element — and provides the same values to
|
|
1800
|
+
* descendants through `useConfig`, so interactive behavior (formatting,
|
|
1801
|
+
* messages) and visual theming stay one decision.
|
|
1802
|
+
*/
|
|
1803
|
+
function ConfigProvider({ density, accent, dir, locale, children, ...rest }) {
|
|
1804
|
+
return /* @__PURE__ */ jsx(ConfigContextImpl.Provider, {
|
|
1805
|
+
value: {
|
|
1806
|
+
density,
|
|
1807
|
+
accent,
|
|
1808
|
+
dir,
|
|
1809
|
+
locale
|
|
1810
|
+
},
|
|
1811
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1812
|
+
...rest,
|
|
1813
|
+
"data-scope": "config-provider",
|
|
1814
|
+
"data-part": "root",
|
|
1815
|
+
...density != null ? { "data-density": density } : {},
|
|
1816
|
+
...accent != null ? { "data-accent": accent } : {},
|
|
1817
|
+
...dir != null ? { dir } : {},
|
|
1818
|
+
...locale != null ? { lang: locale } : {},
|
|
1819
|
+
children
|
|
1820
|
+
})
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
//#endregion
|
|
1824
|
+
//#region src/components/container/index.tsx
|
|
1825
|
+
function Container({ size = "readable", padding = true, ...rest }) {
|
|
1826
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1827
|
+
...rest,
|
|
1828
|
+
"data-scope": "container",
|
|
1829
|
+
"data-part": "root",
|
|
1830
|
+
"data-size": size,
|
|
1831
|
+
"data-padding": padding ? "" : void 0
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1834
|
+
injectComponentStyle("container");
|
|
1835
|
+
//#endregion
|
|
710
1836
|
//#region src/components/date-input/index.tsx
|
|
711
1837
|
/** Ark's DateInput, dressed in the paper-and-ink system: a segmented
|
|
712
1838
|
* field where the focused segment takes the flat ink fill. The API is
|
|
@@ -725,6 +1851,35 @@ injectComponentStyle("date-input");
|
|
|
725
1851
|
const DatePicker = DatePicker$1;
|
|
726
1852
|
injectComponentStyle("date-picker");
|
|
727
1853
|
//#endregion
|
|
1854
|
+
//#region src/components/descriptions/index.tsx
|
|
1855
|
+
function DescriptionsRoot({ layout = "horizontal", children, ...rest }) {
|
|
1856
|
+
return /* @__PURE__ */ jsx("dl", {
|
|
1857
|
+
...rest,
|
|
1858
|
+
"data-scope": "descriptions",
|
|
1859
|
+
"data-part": "root",
|
|
1860
|
+
"data-layout": layout,
|
|
1861
|
+
children
|
|
1862
|
+
});
|
|
1863
|
+
}
|
|
1864
|
+
function part$8(name, tag) {
|
|
1865
|
+
const Tag = tag;
|
|
1866
|
+
const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
|
|
1867
|
+
...rest,
|
|
1868
|
+
"data-scope": "descriptions",
|
|
1869
|
+
"data-part": name.toLowerCase(),
|
|
1870
|
+
children
|
|
1871
|
+
});
|
|
1872
|
+
Component.displayName = "Descriptions" + name;
|
|
1873
|
+
return Component;
|
|
1874
|
+
}
|
|
1875
|
+
const Descriptions = {
|
|
1876
|
+
Root: DescriptionsRoot,
|
|
1877
|
+
Item: part$8("Item", "div"),
|
|
1878
|
+
Term: part$8("Term", "dt"),
|
|
1879
|
+
Detail: part$8("Detail", "dd")
|
|
1880
|
+
};
|
|
1881
|
+
injectComponentStyle("descriptions");
|
|
1882
|
+
//#endregion
|
|
728
1883
|
//#region src/components/dialog/index.tsx
|
|
729
1884
|
/** Ark's Dialog, dressed in the paper-and-ink system: the sheet dissolves
|
|
730
1885
|
* in on elevation, the backdrop fades, and nested overlays stack through
|
|
@@ -742,6 +1897,81 @@ injectComponentStyle("dialog");
|
|
|
742
1897
|
const Drawer = Drawer$1;
|
|
743
1898
|
injectComponentStyle("drawer");
|
|
744
1899
|
//#endregion
|
|
1900
|
+
//#region src/components/dynamic-input/index.tsx
|
|
1901
|
+
/**
|
|
1902
|
+
* A column of entry rows: one Input per line, each with a quiet remove
|
|
1903
|
+
* seal, and an add row at the tail. The list is controlled — every edit
|
|
1904
|
+
* reports a fresh array through `onValueChange` and the caller's array
|
|
1905
|
+
* stays the only truth. Deleting never drops below `min`, adding never
|
|
1906
|
+
* grows past `max`, and the group always keeps one row: an entry list
|
|
1907
|
+
* that emptied itself would leave the reader no place to type.
|
|
1908
|
+
*/
|
|
1909
|
+
function DynamicInput({ value, min = 0, max, placeholder, addLabel = "Add entry", disabled = false, invalid = false, onValueChange, children: _children, ...rest }) {
|
|
1910
|
+
const values = value ?? [""];
|
|
1911
|
+
const update = (index, next) => {
|
|
1912
|
+
const rows = values.slice();
|
|
1913
|
+
rows[index] = next;
|
|
1914
|
+
onValueChange?.(rows);
|
|
1915
|
+
};
|
|
1916
|
+
const remove = (index) => {
|
|
1917
|
+
const rows = values.filter((_, i) => i !== index);
|
|
1918
|
+
onValueChange?.(rows.length > 0 ? rows : [""]);
|
|
1919
|
+
};
|
|
1920
|
+
const add = () => {
|
|
1921
|
+
onValueChange?.([...values, ""]);
|
|
1922
|
+
};
|
|
1923
|
+
const canRemove = values.length > Math.max(min, 1);
|
|
1924
|
+
const canAdd = max === void 0 || values.length < max;
|
|
1925
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1926
|
+
...rest,
|
|
1927
|
+
"data-scope": "dynamic-input",
|
|
1928
|
+
"data-part": "root",
|
|
1929
|
+
children: [values.map((value, index) => /* @__PURE__ */ jsxs("div", {
|
|
1930
|
+
"data-scope": "dynamic-input",
|
|
1931
|
+
"data-part": "row",
|
|
1932
|
+
children: [/* @__PURE__ */ jsx(Input, {
|
|
1933
|
+
value,
|
|
1934
|
+
placeholder,
|
|
1935
|
+
disabled,
|
|
1936
|
+
invalid,
|
|
1937
|
+
onValueChange: (next) => update(index, next)
|
|
1938
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
1939
|
+
variant: "ghost",
|
|
1940
|
+
square: true,
|
|
1941
|
+
disabled: disabled || !canRemove,
|
|
1942
|
+
"aria-label": `Remove entry ${index + 1}`,
|
|
1943
|
+
onClick: () => remove(index),
|
|
1944
|
+
children: crossIcon()
|
|
1945
|
+
})]
|
|
1946
|
+
}, index)), /* @__PURE__ */ jsx("div", {
|
|
1947
|
+
"data-scope": "dynamic-input",
|
|
1948
|
+
"data-part": "add",
|
|
1949
|
+
children: /* @__PURE__ */ jsxs(Button, {
|
|
1950
|
+
variant: "ghost",
|
|
1951
|
+
disabled: disabled || !canAdd,
|
|
1952
|
+
onClick: () => add(),
|
|
1953
|
+
children: ["+", addLabel]
|
|
1954
|
+
})
|
|
1955
|
+
})]
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
/** The one glyph a remove seal needs: a single crossing stroke. */
|
|
1959
|
+
function crossIcon() {
|
|
1960
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
1961
|
+
width: 16,
|
|
1962
|
+
height: 16,
|
|
1963
|
+
viewBox: "0 0 24 24",
|
|
1964
|
+
fill: "none",
|
|
1965
|
+
stroke: "currentColor",
|
|
1966
|
+
strokeWidth: 1.75,
|
|
1967
|
+
strokeLinecap: "round",
|
|
1968
|
+
strokeLinejoin: "round",
|
|
1969
|
+
"aria-hidden": "true",
|
|
1970
|
+
children: /* @__PURE__ */ jsx("path", { d: "M18 6 6 18M6 6l12 12" })
|
|
1971
|
+
});
|
|
1972
|
+
}
|
|
1973
|
+
injectComponentStyle("dynamic-input");
|
|
1974
|
+
//#endregion
|
|
745
1975
|
//#region src/components/editable/index.tsx
|
|
746
1976
|
/** Ark's Editable, dressed in the paper-and-ink system: bare ink while
|
|
747
1977
|
* reading, the full field recipe while editing. The API is Ark's own —
|
|
@@ -750,11 +1980,27 @@ injectComponentStyle("drawer");
|
|
|
750
1980
|
const Editable = Editable$1;
|
|
751
1981
|
injectComponentStyle("editable");
|
|
752
1982
|
//#endregion
|
|
1983
|
+
//#region src/components/ellipsis/index.tsx
|
|
1984
|
+
function Ellipsis({ lines = 1, ...rest }) {
|
|
1985
|
+
const multiline = lines > 1;
|
|
1986
|
+
return /* @__PURE__ */ jsx("span", {
|
|
1987
|
+
...rest,
|
|
1988
|
+
style: multiline ? {
|
|
1989
|
+
...rest.style,
|
|
1990
|
+
"--bs-ellipsis-lines": String(lines)
|
|
1991
|
+
} : rest.style,
|
|
1992
|
+
"data-scope": "ellipsis",
|
|
1993
|
+
"data-part": "root",
|
|
1994
|
+
"data-multiline": multiline ? "" : void 0
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
injectComponentStyle("ellipsis");
|
|
1998
|
+
//#endregion
|
|
753
1999
|
//#region src/components/empty/index.tsx
|
|
754
2000
|
/** An empty state: the page holds its breath. Root centers the column,
|
|
755
2001
|
* Visual carries the mark, Title and Description carry the ink, Actions
|
|
756
2002
|
* the way out. Any subset composes. */
|
|
757
|
-
function part$
|
|
2003
|
+
function part$7(name, tag) {
|
|
758
2004
|
const Tag = tag;
|
|
759
2005
|
const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
|
|
760
2006
|
...rest,
|
|
@@ -765,17 +2011,17 @@ function part$5(name, tag) {
|
|
|
765
2011
|
Component.displayName = "Empty" + name;
|
|
766
2012
|
return Component;
|
|
767
2013
|
}
|
|
768
|
-
const Root$
|
|
769
|
-
const Visual = part$
|
|
770
|
-
const Title$1 = part$
|
|
771
|
-
const Description$2 = part$
|
|
772
|
-
const Actions$
|
|
773
|
-
const Empty = Object.assign(Root$
|
|
774
|
-
Root: Root$
|
|
2014
|
+
const Root$8 = part$7("Root", "div");
|
|
2015
|
+
const Visual = part$7("Visual", "div");
|
|
2016
|
+
const Title$1 = part$7("Title", "h3");
|
|
2017
|
+
const Description$2 = part$7("Description", "p");
|
|
2018
|
+
const Actions$2 = part$7("Actions", "div");
|
|
2019
|
+
const Empty = Object.assign(Root$8, {
|
|
2020
|
+
Root: Root$8,
|
|
775
2021
|
Visual,
|
|
776
2022
|
Title: Title$1,
|
|
777
2023
|
Description: Description$2,
|
|
778
|
-
Actions: Actions$
|
|
2024
|
+
Actions: Actions$2
|
|
779
2025
|
});
|
|
780
2026
|
injectComponentStyle("empty");
|
|
781
2027
|
//#endregion
|
|
@@ -796,6 +2042,85 @@ injectComponentStyle("fieldset");
|
|
|
796
2042
|
const FileUpload = FileUpload$1;
|
|
797
2043
|
injectComponentStyle("file-upload");
|
|
798
2044
|
//#endregion
|
|
2045
|
+
//#region src/components/float-button/index.tsx
|
|
2046
|
+
/** Context for {@link FloatButtonContextValue}, exported so a custom
|
|
2047
|
+
* part can read the group's state. */
|
|
2048
|
+
const FLOAT_BUTTON_CONTEXT = createContext(null);
|
|
2049
|
+
/**
|
|
2050
|
+
* A floating action and its fanned-out alternatives — FAB and speed
|
|
2051
|
+
* dial in one family. The Root moors the group at a page corner and
|
|
2052
|
+
* holds the openness (mirrored from `open` when controlled); the
|
|
2053
|
+
* Trigger flips it; each Item is a round action with its name surfacing
|
|
2054
|
+
* beside it while the group is open. The buttons are the shared recipe
|
|
2055
|
+
* (vessels here: round); this family owns only the mooring, the fan
|
|
2056
|
+
* and the fold.
|
|
2057
|
+
*/
|
|
2058
|
+
function Root$7({ open: openProp, placement = "bottom-end", onOpenChange, children, ...rest }) {
|
|
2059
|
+
const [inner, setInner] = useState(false);
|
|
2060
|
+
const open = openProp !== void 0 ? openProp : inner;
|
|
2061
|
+
const setOpen = (value) => {
|
|
2062
|
+
setInner(value);
|
|
2063
|
+
onOpenChange?.(value);
|
|
2064
|
+
};
|
|
2065
|
+
return /* @__PURE__ */ jsx(FLOAT_BUTTON_CONTEXT.Provider, {
|
|
2066
|
+
value: {
|
|
2067
|
+
open,
|
|
2068
|
+
toggle: () => setOpen(!open),
|
|
2069
|
+
close: () => setOpen(false)
|
|
2070
|
+
},
|
|
2071
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2072
|
+
...rest,
|
|
2073
|
+
"data-scope": "float-button",
|
|
2074
|
+
"data-part": "root",
|
|
2075
|
+
"data-placement": placement,
|
|
2076
|
+
"data-state": open ? "open" : "closed",
|
|
2077
|
+
children
|
|
2078
|
+
})
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
function Trigger({ label, children }) {
|
|
2082
|
+
const context = useContext(FLOAT_BUTTON_CONTEXT);
|
|
2083
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
2084
|
+
variant: "solid",
|
|
2085
|
+
square: true,
|
|
2086
|
+
size: "lg",
|
|
2087
|
+
onClick: () => context?.toggle(),
|
|
2088
|
+
"aria-label": label || "Actions",
|
|
2089
|
+
"aria-expanded": context ? context.open : false,
|
|
2090
|
+
children
|
|
2091
|
+
});
|
|
2092
|
+
}
|
|
2093
|
+
function Item$2({ label, disabled = false, onClick, children }) {
|
|
2094
|
+
const context = useContext(FLOAT_BUTTON_CONTEXT);
|
|
2095
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2096
|
+
"data-scope": "float-button",
|
|
2097
|
+
"data-part": "item",
|
|
2098
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
2099
|
+
variant: "outline",
|
|
2100
|
+
square: true,
|
|
2101
|
+
size: "md",
|
|
2102
|
+
disabled,
|
|
2103
|
+
"aria-label": label,
|
|
2104
|
+
onClick: () => {
|
|
2105
|
+
onClick?.();
|
|
2106
|
+
context?.close();
|
|
2107
|
+
},
|
|
2108
|
+
children
|
|
2109
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
2110
|
+
"data-scope": "float-button",
|
|
2111
|
+
"data-part": "item-label",
|
|
2112
|
+
"aria-hidden": "true",
|
|
2113
|
+
children: label
|
|
2114
|
+
})]
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2117
|
+
const FloatButton = Object.assign(Root$7, {
|
|
2118
|
+
Root: Root$7,
|
|
2119
|
+
Trigger,
|
|
2120
|
+
Item: Item$2
|
|
2121
|
+
});
|
|
2122
|
+
injectComponentStyle("float-button");
|
|
2123
|
+
//#endregion
|
|
799
2124
|
//#region src/components/floating-panel/index.tsx
|
|
800
2125
|
/** Ark's FloatingPanel, dressed in the paper-and-ink system: the shared
|
|
801
2126
|
* popup vessel let loose — a draggable, resizable sheet whose header is the
|
|
@@ -805,6 +2130,134 @@ injectComponentStyle("file-upload");
|
|
|
805
2130
|
const FloatingPanel = FloatingPanel$1;
|
|
806
2131
|
injectComponentStyle("floating-panel");
|
|
807
2132
|
//#endregion
|
|
2133
|
+
//#region src/components/form/index.tsx
|
|
2134
|
+
const FormContext = createContext(null);
|
|
2135
|
+
/** Walk a Standard Schema issue's path back to the dotted name the
|
|
2136
|
+
* matching FormField declared. */
|
|
2137
|
+
function issueName(issue) {
|
|
2138
|
+
return (issue.path ?? []).map((segment) => typeof segment === "object" ? segment.key : segment).join(".");
|
|
2139
|
+
}
|
|
2140
|
+
/**
|
|
2141
|
+
* The validation scheduler: one error map, a submit that validates
|
|
2142
|
+
* before it fires, live re-validation on the events the `validateOn`
|
|
2143
|
+
* prop names, and the imperative handle. The schema is any Standard
|
|
2144
|
+
* Schema (valibot, zod, arktype, …) — none are bundled; a `validate`
|
|
2145
|
+
* function composes with it for the cases schemas can't express. Errors
|
|
2146
|
+
* reach their field by name, through FormField.
|
|
2147
|
+
*/
|
|
2148
|
+
function Form({ state, schema, validate, validateOn = [
|
|
2149
|
+
"input",
|
|
2150
|
+
"change",
|
|
2151
|
+
"blur"
|
|
2152
|
+
], disabled = false, onSubmit, onError, ref, children, ...rest }) {
|
|
2153
|
+
const [errors, setErrors] = useState(() => /* @__PURE__ */ new Map());
|
|
2154
|
+
const run = useRef(0);
|
|
2155
|
+
async function runValidate() {
|
|
2156
|
+
const seq = ++run.current;
|
|
2157
|
+
const next = /* @__PURE__ */ new Map();
|
|
2158
|
+
if (validate) for (const error of await validate(state)) next.set(error.name, error.message);
|
|
2159
|
+
if (schema) {
|
|
2160
|
+
const result = await schema["~standard"].validate(state);
|
|
2161
|
+
for (const issue of result.issues ?? []) {
|
|
2162
|
+
const name = issueName(issue);
|
|
2163
|
+
if (!next.has(name)) next.set(name, issue.message);
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
if (seq === run.current) setErrors(next);
|
|
2167
|
+
return next;
|
|
2168
|
+
}
|
|
2169
|
+
function clear(name) {
|
|
2170
|
+
if (!name) {
|
|
2171
|
+
setErrors(/* @__PURE__ */ new Map());
|
|
2172
|
+
return;
|
|
2173
|
+
}
|
|
2174
|
+
setErrors((prev) => {
|
|
2175
|
+
const next = new Map(prev);
|
|
2176
|
+
for (const key of next.keys()) if (typeof name === "string" ? key === name : name.test(key)) next.delete(key);
|
|
2177
|
+
return next;
|
|
2178
|
+
});
|
|
2179
|
+
}
|
|
2180
|
+
function setErrorsFrom(list) {
|
|
2181
|
+
setErrors(new Map(list.map((error) => [error.name, error.message])));
|
|
2182
|
+
}
|
|
2183
|
+
function getErrors(name) {
|
|
2184
|
+
const all = [...errors].map(([entry, message]) => ({
|
|
2185
|
+
name: entry,
|
|
2186
|
+
message
|
|
2187
|
+
}));
|
|
2188
|
+
if (!name) return all;
|
|
2189
|
+
return all.filter((error) => typeof name === "string" ? error.name === name : name.test(error.name));
|
|
2190
|
+
}
|
|
2191
|
+
useImperativeHandle(ref, () => ({
|
|
2192
|
+
validate: runValidate,
|
|
2193
|
+
clear,
|
|
2194
|
+
setErrors: setErrorsFrom,
|
|
2195
|
+
getErrors,
|
|
2196
|
+
errors
|
|
2197
|
+
}));
|
|
2198
|
+
function handleSubmit(event) {
|
|
2199
|
+
event.preventDefault();
|
|
2200
|
+
runValidate().then((map) => {
|
|
2201
|
+
if (map.size === 0) {
|
|
2202
|
+
onSubmit?.(state);
|
|
2203
|
+
return;
|
|
2204
|
+
}
|
|
2205
|
+
onError?.([...map].map(([name, message]) => ({
|
|
2206
|
+
name,
|
|
2207
|
+
message
|
|
2208
|
+
})));
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
const timer = useRef(void 0);
|
|
2212
|
+
function onDelegated(kind, event) {
|
|
2213
|
+
if (!validateOn.includes(kind)) return;
|
|
2214
|
+
const target = event.target;
|
|
2215
|
+
if (!target || !("value" in target)) return;
|
|
2216
|
+
clearTimeout(timer.current);
|
|
2217
|
+
timer.current = setTimeout(() => void runValidate(), kind === "blur" ? 0 : 300);
|
|
2218
|
+
}
|
|
2219
|
+
return /* @__PURE__ */ jsx(FormContext.Provider, {
|
|
2220
|
+
value: {
|
|
2221
|
+
errors,
|
|
2222
|
+
disabled
|
|
2223
|
+
},
|
|
2224
|
+
children: /* @__PURE__ */ jsx("form", {
|
|
2225
|
+
...rest,
|
|
2226
|
+
"data-scope": "form",
|
|
2227
|
+
"data-part": "root",
|
|
2228
|
+
noValidate: true,
|
|
2229
|
+
onSubmit: handleSubmit,
|
|
2230
|
+
onInput: (event) => onDelegated("input", event),
|
|
2231
|
+
onChange: (event) => onDelegated("change", event),
|
|
2232
|
+
onBlurCapture: (event) => onDelegated("blur", event),
|
|
2233
|
+
children
|
|
2234
|
+
})
|
|
2235
|
+
});
|
|
2236
|
+
}
|
|
2237
|
+
function FormField({ name, label, hint, required = false, invalid = false, disabled = false, children, ...rest }) {
|
|
2238
|
+
const form = useContext(FormContext);
|
|
2239
|
+
const formError = name ? form?.errors.get(name) : void 0;
|
|
2240
|
+
const isInvalid = invalid || formError != null;
|
|
2241
|
+
const isDisabled = disabled || (form?.disabled ?? false);
|
|
2242
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2243
|
+
...rest,
|
|
2244
|
+
"data-form-field": name,
|
|
2245
|
+
children: /* @__PURE__ */ jsxs(Field$1.Root, {
|
|
2246
|
+
invalid: isInvalid,
|
|
2247
|
+
required,
|
|
2248
|
+
disabled: isDisabled,
|
|
2249
|
+
children: [
|
|
2250
|
+
label ? /* @__PURE__ */ jsx(Field$1.Label, { children: label }) : null,
|
|
2251
|
+
children,
|
|
2252
|
+
hint && formError == null ? /* @__PURE__ */ jsx(Field$1.HelperText, { children: hint }) : null,
|
|
2253
|
+
formError != null ? /* @__PURE__ */ jsx(Field$1.ErrorText, { children: formError }) : null
|
|
2254
|
+
]
|
|
2255
|
+
})
|
|
2256
|
+
});
|
|
2257
|
+
}
|
|
2258
|
+
injectComponentStyle("form");
|
|
2259
|
+
injectComponentStyle("field");
|
|
2260
|
+
//#endregion
|
|
808
2261
|
//#region src/components/format/index.tsx
|
|
809
2262
|
/** Ark's Format, dressed in the paper-and-ink system: Intl formatting for
|
|
810
2263
|
* numbers, currency, bytes and relative time. The components render bare
|
|
@@ -823,6 +2276,34 @@ const Format = Format$1;
|
|
|
823
2276
|
* to hook. The API is Ark's own. */
|
|
824
2277
|
const Frame = Frame$1;
|
|
825
2278
|
//#endregion
|
|
2279
|
+
//#region src/components/grid/index.tsx
|
|
2280
|
+
/** The same named steps of the space ramp the stack uses — one
|
|
2281
|
+
* vocabulary of distance across the layout primitives. */
|
|
2282
|
+
const gapVars$2 = {
|
|
2283
|
+
none: "0",
|
|
2284
|
+
xs: "var(--bs-gap-xs)",
|
|
2285
|
+
sm: "var(--bs-gap-sm)",
|
|
2286
|
+
md: "var(--bs-gap-md)",
|
|
2287
|
+
lg: "var(--bs-gap-lg)",
|
|
2288
|
+
xl: "var(--bs-gap-xl)"
|
|
2289
|
+
};
|
|
2290
|
+
function Grid({ columns = 12, gap = "md", minChildWidth, ...rest }) {
|
|
2291
|
+
const style = {
|
|
2292
|
+
...rest.style,
|
|
2293
|
+
"--bs-grid-columns": String(columns),
|
|
2294
|
+
"--bs-grid-gap": gapVars$2[gap] ?? gapVars$2.md,
|
|
2295
|
+
...minChildWidth != null ? { "--bs-grid-min-child-width": minChildWidth } : {}
|
|
2296
|
+
};
|
|
2297
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2298
|
+
...rest,
|
|
2299
|
+
style,
|
|
2300
|
+
"data-scope": "grid",
|
|
2301
|
+
"data-part": "root",
|
|
2302
|
+
"data-autofill": minChildWidth != null ? "" : void 0
|
|
2303
|
+
});
|
|
2304
|
+
}
|
|
2305
|
+
injectComponentStyle("grid");
|
|
2306
|
+
//#endregion
|
|
826
2307
|
//#region src/components/highlight/index.tsx
|
|
827
2308
|
/** Ark's Highlight, dressed in the paper-and-ink system: query hits are
|
|
828
2309
|
* strokes of pigment on the page — a quiet tint of the accent behind the
|
|
@@ -838,6 +2319,74 @@ injectComponentStyle("highlight");
|
|
|
838
2319
|
const HoverCard = HoverCard$1;
|
|
839
2320
|
injectComponentStyle("hover-card");
|
|
840
2321
|
//#endregion
|
|
2322
|
+
//#region src/components/icon/index.tsx
|
|
2323
|
+
function Icon({ size = "inherit", label, children, ...rest }) {
|
|
2324
|
+
return /* @__PURE__ */ jsx("span", {
|
|
2325
|
+
...rest,
|
|
2326
|
+
role: label != null ? "img" : void 0,
|
|
2327
|
+
"aria-label": label,
|
|
2328
|
+
"aria-hidden": label != null ? void 0 : "true",
|
|
2329
|
+
"data-scope": "icon",
|
|
2330
|
+
"data-part": "root",
|
|
2331
|
+
"data-size": size,
|
|
2332
|
+
children
|
|
2333
|
+
});
|
|
2334
|
+
}
|
|
2335
|
+
injectComponentStyle("icon");
|
|
2336
|
+
//#endregion
|
|
2337
|
+
//#region src/components/image/index.tsx
|
|
2338
|
+
/** The mark for a source that never arrived: a quiet mountain-and-sun,
|
|
2339
|
+
* drawn in the stylesheet's stroke and hidden from the reader. */
|
|
2340
|
+
function placeholderGlyph() {
|
|
2341
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
2342
|
+
viewBox: "0 0 24 24",
|
|
2343
|
+
fill: "none",
|
|
2344
|
+
stroke: "currentColor",
|
|
2345
|
+
"aria-hidden": "true",
|
|
2346
|
+
children: [
|
|
2347
|
+
/* @__PURE__ */ jsx("rect", {
|
|
2348
|
+
x: 3,
|
|
2349
|
+
y: 4,
|
|
2350
|
+
width: 18,
|
|
2351
|
+
height: 16,
|
|
2352
|
+
rx: 1.5
|
|
2353
|
+
}),
|
|
2354
|
+
/* @__PURE__ */ jsx("circle", {
|
|
2355
|
+
cx: 9,
|
|
2356
|
+
cy: 10,
|
|
2357
|
+
r: 1.5
|
|
2358
|
+
}),
|
|
2359
|
+
/* @__PURE__ */ jsx("path", { d: "m5.5 17.5 4.5-5 3 3.5 2.5-3 3 4.5" })
|
|
2360
|
+
]
|
|
2361
|
+
});
|
|
2362
|
+
}
|
|
2363
|
+
function Image({ src, alt = "", fit = "cover", loading = "lazy", fallback, ...rest }) {
|
|
2364
|
+
const [state, setState] = useState("loading");
|
|
2365
|
+
useEffect(() => setState("loading"), [src]);
|
|
2366
|
+
return /* @__PURE__ */ jsxs("figure", {
|
|
2367
|
+
...rest,
|
|
2368
|
+
"data-scope": "image",
|
|
2369
|
+
"data-part": "root",
|
|
2370
|
+
"data-state": state,
|
|
2371
|
+
"data-fit": fit,
|
|
2372
|
+
children: [/* @__PURE__ */ jsx("img", {
|
|
2373
|
+
"data-scope": "image",
|
|
2374
|
+
"data-part": "img",
|
|
2375
|
+
src,
|
|
2376
|
+
alt,
|
|
2377
|
+
loading,
|
|
2378
|
+
decoding: "async",
|
|
2379
|
+
onLoad: () => setState("loaded"),
|
|
2380
|
+
onError: () => setState("error")
|
|
2381
|
+
}), state === "error" ? /* @__PURE__ */ jsx("div", {
|
|
2382
|
+
"data-scope": "image",
|
|
2383
|
+
"data-part": "fallback",
|
|
2384
|
+
children: fallback ?? placeholderGlyph()
|
|
2385
|
+
}) : null]
|
|
2386
|
+
});
|
|
2387
|
+
}
|
|
2388
|
+
injectComponentStyle("image");
|
|
2389
|
+
//#endregion
|
|
841
2390
|
//#region src/components/image-cropper/index.tsx
|
|
842
2391
|
/** Ark's ImageCropper, dressed in the paper-and-ink system: a rounded vessel
|
|
843
2392
|
* holding the photograph, one lit selection window framed by light hairlines.
|
|
@@ -846,6 +2395,136 @@ injectComponentStyle("hover-card");
|
|
|
846
2395
|
const ImageCropper = ImageCropper$1;
|
|
847
2396
|
injectComponentStyle("image-cropper");
|
|
848
2397
|
//#endregion
|
|
2398
|
+
//#region src/components/image-viewer/index.tsx
|
|
2399
|
+
const MIN_SCALE = .5;
|
|
2400
|
+
const MAX_SCALE = 3;
|
|
2401
|
+
const SCALE_STEP = .25;
|
|
2402
|
+
/** The toolbar's line-drawn glyphs, fresh per render. */
|
|
2403
|
+
const TOOL_GLYPHS = {
|
|
2404
|
+
zoomIn: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2405
|
+
/* @__PURE__ */ jsx("circle", {
|
|
2406
|
+
cx: 11,
|
|
2407
|
+
cy: 11,
|
|
2408
|
+
r: 7
|
|
2409
|
+
}),
|
|
2410
|
+
/* @__PURE__ */ jsx("path", { d: "m16.2 16.2 4.8 4.8" }),
|
|
2411
|
+
/* @__PURE__ */ jsx("path", { d: "M8 11h6" }),
|
|
2412
|
+
/* @__PURE__ */ jsx("path", { d: "M11 8v6" })
|
|
2413
|
+
] }),
|
|
2414
|
+
zoomOut: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2415
|
+
/* @__PURE__ */ jsx("circle", {
|
|
2416
|
+
cx: 11,
|
|
2417
|
+
cy: 11,
|
|
2418
|
+
r: 7
|
|
2419
|
+
}),
|
|
2420
|
+
/* @__PURE__ */ jsx("path", { d: "m16.2 16.2 4.8 4.8" }),
|
|
2421
|
+
/* @__PURE__ */ jsx("path", { d: "M8 11h6" })
|
|
2422
|
+
] }),
|
|
2423
|
+
rotate: () => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "M20.49 12A8.5 8.5 0 1 1 18 6.06" }), /* @__PURE__ */ jsx("path", { d: "M20.5 3.5v4h-4" })] }),
|
|
2424
|
+
close: () => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("path", { d: "m6 6 12 12" }), /* @__PURE__ */ jsx("path", { d: "M18 6 6 18" })] })
|
|
2425
|
+
};
|
|
2426
|
+
function ImageViewer({ src, alt = "", open, zoomable = true, onOpenChange }) {
|
|
2427
|
+
const [localOpen, setLocalOpen] = useState(false);
|
|
2428
|
+
const [scale, setScale] = useState(1);
|
|
2429
|
+
const [rotation, setRotation] = useState(0);
|
|
2430
|
+
const isOpen = open ?? localOpen;
|
|
2431
|
+
const setOpen = (value) => {
|
|
2432
|
+
if (open === void 0) setLocalOpen(value);
|
|
2433
|
+
onOpenChange?.(value);
|
|
2434
|
+
};
|
|
2435
|
+
useEffect(() => {
|
|
2436
|
+
if (isOpen) {
|
|
2437
|
+
setScale(1);
|
|
2438
|
+
setRotation(0);
|
|
2439
|
+
}
|
|
2440
|
+
}, [isOpen]);
|
|
2441
|
+
function zoom(step) {
|
|
2442
|
+
setScale((current) => Math.min(MAX_SCALE, Math.max(MIN_SCALE, current + step)));
|
|
2443
|
+
}
|
|
2444
|
+
function rotate() {
|
|
2445
|
+
setRotation((current) => (current + 90) % 360);
|
|
2446
|
+
}
|
|
2447
|
+
function toolButton(label, glyph, onClick) {
|
|
2448
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
2449
|
+
variant: "ghost",
|
|
2450
|
+
square: true,
|
|
2451
|
+
size: "lg",
|
|
2452
|
+
"aria-label": label,
|
|
2453
|
+
onClick,
|
|
2454
|
+
children: /* @__PURE__ */ jsx("svg", {
|
|
2455
|
+
viewBox: "0 0 24 24",
|
|
2456
|
+
fill: "none",
|
|
2457
|
+
stroke: "currentColor",
|
|
2458
|
+
strokeWidth: 1.5,
|
|
2459
|
+
strokeLinecap: "round",
|
|
2460
|
+
strokeLinejoin: "round",
|
|
2461
|
+
"aria-hidden": "true",
|
|
2462
|
+
children: glyph()
|
|
2463
|
+
})
|
|
2464
|
+
});
|
|
2465
|
+
}
|
|
2466
|
+
return /* @__PURE__ */ jsx(Dialog$1.Root, {
|
|
2467
|
+
open: isOpen,
|
|
2468
|
+
onOpenChange: (details) => setOpen(details.open),
|
|
2469
|
+
lazyMount: true,
|
|
2470
|
+
unmountOnExit: true,
|
|
2471
|
+
children: /* @__PURE__ */ jsxs(Portal, { children: [/* @__PURE__ */ jsx(Dialog$1.Backdrop, { className: "bs-image-viewer-backdrop" }), /* @__PURE__ */ jsx(Dialog$1.Positioner, {
|
|
2472
|
+
className: "bs-image-viewer-positioner",
|
|
2473
|
+
children: /* @__PURE__ */ jsxs(Dialog$1.Content, {
|
|
2474
|
+
className: "bs-image-viewer-content",
|
|
2475
|
+
"aria-label": alt || "Image preview",
|
|
2476
|
+
onClick: (event) => {
|
|
2477
|
+
if (event.target === event.currentTarget) setOpen(false);
|
|
2478
|
+
},
|
|
2479
|
+
children: [/* @__PURE__ */ jsx("img", {
|
|
2480
|
+
"data-scope": "image-viewer",
|
|
2481
|
+
"data-part": "viewport",
|
|
2482
|
+
src,
|
|
2483
|
+
alt,
|
|
2484
|
+
style: { transform: `scale(${scale}) rotate(${rotation}deg)` }
|
|
2485
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
2486
|
+
"data-scope": "image-viewer",
|
|
2487
|
+
"data-part": "toolbar",
|
|
2488
|
+
children: /* @__PURE__ */ jsxs(ButtonGroup, { children: [
|
|
2489
|
+
zoomable ? [toolButton("Zoom in", TOOL_GLYPHS.zoomIn, () => zoom(SCALE_STEP)), toolButton("Zoom out", TOOL_GLYPHS.zoomOut, () => zoom(-.25))] : null,
|
|
2490
|
+
toolButton("Rotate 90 degrees", TOOL_GLYPHS.rotate, rotate),
|
|
2491
|
+
toolButton("Close", TOOL_GLYPHS.close, () => setOpen(false))
|
|
2492
|
+
] })
|
|
2493
|
+
})]
|
|
2494
|
+
})
|
|
2495
|
+
})] })
|
|
2496
|
+
});
|
|
2497
|
+
}
|
|
2498
|
+
injectComponentStyle("image-viewer");
|
|
2499
|
+
//#endregion
|
|
2500
|
+
//#region src/components/input-group/index.tsx
|
|
2501
|
+
function part$6(name) {
|
|
2502
|
+
const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx("div", {
|
|
2503
|
+
...rest,
|
|
2504
|
+
"data-scope": "input-group",
|
|
2505
|
+
"data-part": name.toLowerCase(),
|
|
2506
|
+
children
|
|
2507
|
+
});
|
|
2508
|
+
Component.displayName = "InputGroup" + name;
|
|
2509
|
+
return Component;
|
|
2510
|
+
}
|
|
2511
|
+
/**
|
|
2512
|
+
* Merged controls: attachments and the entry fused into one seal. The
|
|
2513
|
+
* Root draws the single hairline and carries the group's focus halo;
|
|
2514
|
+
* the Addon is a recessed cell for the reader's fixed words — a scheme,
|
|
2515
|
+
* a unit, a quiet button — placed before or after the entry. Put our
|
|
2516
|
+
* Input (or Textarea) inside and its own border and halo step aside in
|
|
2517
|
+
* favor of the group's; the stylesheet does the merging, the wrapper
|
|
2518
|
+
* adds no visuals of its own.
|
|
2519
|
+
*/
|
|
2520
|
+
const Root$6 = part$6("Root");
|
|
2521
|
+
const Addon = part$6("Addon");
|
|
2522
|
+
const InputGroup = Object.assign(Root$6, {
|
|
2523
|
+
Root: Root$6,
|
|
2524
|
+
Addon
|
|
2525
|
+
});
|
|
2526
|
+
injectComponentStyle("input-group");
|
|
2527
|
+
//#endregion
|
|
849
2528
|
//#region src/components/json-tree-view/index.tsx
|
|
850
2529
|
/** Ark's JsonTreeView, dressed in the paper-and-ink system: the tree-view
|
|
851
2530
|
* recipes re-scoped, with value nodes reading as tabular data. The API is
|
|
@@ -864,6 +2543,135 @@ function Kbd({ children, ...rest }) {
|
|
|
864
2543
|
}
|
|
865
2544
|
injectComponentStyle("kbd");
|
|
866
2545
|
//#endregion
|
|
2546
|
+
//#region src/components/layout/index.tsx
|
|
2547
|
+
const LAYOUT_CONTEXT = createContext(null);
|
|
2548
|
+
/**
|
|
2549
|
+
* The application skeleton: Root, Header, Sider, Content, Footer —
|
|
2550
|
+
* the admin arrangement dressed in the paper-and-ink surfaces. The
|
|
2551
|
+
* root is a full-height grid; a declared `sider` turns the columns
|
|
2552
|
+
* around to meet whichever edge the rail stands on.
|
|
2553
|
+
*/
|
|
2554
|
+
function Root$5({ sider, children, ...rest }) {
|
|
2555
|
+
return /* @__PURE__ */ jsx(LAYOUT_CONTEXT.Provider, {
|
|
2556
|
+
value: { siderPlacement: () => sider },
|
|
2557
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2558
|
+
...rest,
|
|
2559
|
+
"data-scope": "layout",
|
|
2560
|
+
"data-part": "root",
|
|
2561
|
+
"data-sider": sider,
|
|
2562
|
+
children
|
|
2563
|
+
})
|
|
2564
|
+
});
|
|
2565
|
+
}
|
|
2566
|
+
/** A semantic block of the skeleton — header, footer, and the flow's
|
|
2567
|
+
* main content each land on their native element and their grid area. */
|
|
2568
|
+
function region(name, tag) {
|
|
2569
|
+
const Tag = tag;
|
|
2570
|
+
const Component = ({ children, ...rest }) => /* @__PURE__ */ jsx(Tag, {
|
|
2571
|
+
...rest,
|
|
2572
|
+
"data-scope": "layout",
|
|
2573
|
+
"data-part": name.toLowerCase(),
|
|
2574
|
+
children
|
|
2575
|
+
});
|
|
2576
|
+
Component.displayName = "Layout" + name;
|
|
2577
|
+
return Component;
|
|
2578
|
+
}
|
|
2579
|
+
const Header = region("Header", "header");
|
|
2580
|
+
const Content$2 = region("Content", "main");
|
|
2581
|
+
const Footer = region("Footer", "footer");
|
|
2582
|
+
/** One step of the arrow-key resize, in px. */
|
|
2583
|
+
const RESIZE_STEP = 16;
|
|
2584
|
+
function Sider({ collapsed = false, width: widthProp, collapsedWidth = "3.5rem", resizable = false, minWidth = "12rem", maxWidth = "24rem", style, onCollapsedChange, onWidthChange, children, ...rest }) {
|
|
2585
|
+
const layout = useContext(LAYOUT_CONTEXT);
|
|
2586
|
+
const rail = useRef(null);
|
|
2587
|
+
const [dragging, setDragging] = useState(false);
|
|
2588
|
+
const [innerWidth, setInnerWidth] = useState(widthProp ?? "16rem");
|
|
2589
|
+
const width = widthProp !== void 0 ? widthProp : innerWidth;
|
|
2590
|
+
const prevCollapsed = useRef(collapsed);
|
|
2591
|
+
useEffect(() => {
|
|
2592
|
+
if (prevCollapsed.current === collapsed) return;
|
|
2593
|
+
prevCollapsed.current = collapsed;
|
|
2594
|
+
onCollapsedChange?.(collapsed);
|
|
2595
|
+
}, [collapsed, onCollapsedChange]);
|
|
2596
|
+
const innerWidthRef = useRef(innerWidth);
|
|
2597
|
+
innerWidthRef.current = innerWidth;
|
|
2598
|
+
useEffect(() => {
|
|
2599
|
+
if (widthProp === void 0 || widthProp === innerWidthRef.current) return;
|
|
2600
|
+
setInnerWidth(widthProp);
|
|
2601
|
+
onWidthChange?.(widthProp);
|
|
2602
|
+
}, [widthProp, onWidthChange]);
|
|
2603
|
+
const applyWidth = (next) => {
|
|
2604
|
+
setInnerWidth(next);
|
|
2605
|
+
onWidthChange?.(next);
|
|
2606
|
+
};
|
|
2607
|
+
const dragStart = useRef({
|
|
2608
|
+
x: 0,
|
|
2609
|
+
width: 0
|
|
2610
|
+
});
|
|
2611
|
+
const onPointerDown = (event) => {
|
|
2612
|
+
setDragging(true);
|
|
2613
|
+
dragStart.current = {
|
|
2614
|
+
x: event.clientX,
|
|
2615
|
+
width: rail.current ? rail.current.getBoundingClientRect().width : 0
|
|
2616
|
+
};
|
|
2617
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2618
|
+
};
|
|
2619
|
+
const onPointerMove = (event) => {
|
|
2620
|
+
if (!dragging) return;
|
|
2621
|
+
const towardFlow = (layout?.siderPlacement() ?? "start") === "end" ? -1 : 1;
|
|
2622
|
+
applyWidth(`${Math.round(dragStart.current.width + towardFlow * (event.clientX - dragStart.current.x))}px`);
|
|
2623
|
+
};
|
|
2624
|
+
const onPointerUp = (event) => {
|
|
2625
|
+
setDragging(false);
|
|
2626
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
2627
|
+
};
|
|
2628
|
+
const onKeyDown = (event) => {
|
|
2629
|
+
if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
|
|
2630
|
+
event.preventDefault();
|
|
2631
|
+
const widen = (layout?.siderPlacement() ?? "start") === "end" ? event.key === "ArrowLeft" : event.key === "ArrowRight";
|
|
2632
|
+
const current = rail.current ? rail.current.getBoundingClientRect().width : 0;
|
|
2633
|
+
applyWidth(`${Math.round(current + (widen ? RESIZE_STEP : -16))}px`);
|
|
2634
|
+
};
|
|
2635
|
+
const handle = resizable && !collapsed ? /* @__PURE__ */ jsx("div", {
|
|
2636
|
+
"data-scope": "layout",
|
|
2637
|
+
"data-part": "sider-resize",
|
|
2638
|
+
"data-dragging": dragging ? "" : void 0,
|
|
2639
|
+
role: "separator",
|
|
2640
|
+
"aria-orientation": "vertical",
|
|
2641
|
+
tabIndex: 0,
|
|
2642
|
+
"aria-label": "Resize sidebar",
|
|
2643
|
+
"aria-valuenow": Math.round(rail.current ? rail.current.getBoundingClientRect().width : 0),
|
|
2644
|
+
onPointerDown,
|
|
2645
|
+
onPointerMove,
|
|
2646
|
+
onPointerUp,
|
|
2647
|
+
onKeyDown
|
|
2648
|
+
}) : null;
|
|
2649
|
+
return /* @__PURE__ */ jsxs("aside", {
|
|
2650
|
+
...rest,
|
|
2651
|
+
ref: rail,
|
|
2652
|
+
style: {
|
|
2653
|
+
...style,
|
|
2654
|
+
"--bs-layout-sider-width": collapsed ? collapsedWidth : width,
|
|
2655
|
+
"--bs-layout-sider-min": collapsed ? collapsedWidth : minWidth,
|
|
2656
|
+
"--bs-layout-sider-max": collapsed ? collapsedWidth : maxWidth
|
|
2657
|
+
},
|
|
2658
|
+
"data-scope": "layout",
|
|
2659
|
+
"data-part": "sider",
|
|
2660
|
+
"data-collapsed": collapsed ? "" : void 0,
|
|
2661
|
+
"data-dragging": dragging ? "" : void 0,
|
|
2662
|
+
"data-resizable": resizable ? "" : void 0,
|
|
2663
|
+
children: [typeof children === "function" ? children({ collapsed }) : children, handle]
|
|
2664
|
+
});
|
|
2665
|
+
}
|
|
2666
|
+
const Layout = Object.assign(Root$5, {
|
|
2667
|
+
Root: Root$5,
|
|
2668
|
+
Header,
|
|
2669
|
+
Sider,
|
|
2670
|
+
Content: Content$2,
|
|
2671
|
+
Footer
|
|
2672
|
+
});
|
|
2673
|
+
injectComponentStyle("layout");
|
|
2674
|
+
//#endregion
|
|
867
2675
|
//#region src/components/link/index.tsx
|
|
868
2676
|
/** A link is ink in the accent's voice: quiet at rest, deepening under
|
|
869
2677
|
* the hand, the halo at focus. The underline follows the prose —
|
|
@@ -879,6 +2687,69 @@ function Link({ underline = "hover", children, ...rest }) {
|
|
|
879
2687
|
}
|
|
880
2688
|
injectComponentStyle("link");
|
|
881
2689
|
//#endregion
|
|
2690
|
+
//#region src/components/list/index.tsx
|
|
2691
|
+
/** A ledger of rows: Root is the list, Item one row, Leading the mark
|
|
2692
|
+
* before the words, Content the title and its quiet echo, Actions the
|
|
2693
|
+
* way out. The bordered variant draws the hairlines; the hoverable
|
|
2694
|
+
* variant gives every row the wash — and any row the caller makes
|
|
2695
|
+
* clickable (role="button") answers the pointer on its own. */
|
|
2696
|
+
function part$5(name, tag) {
|
|
2697
|
+
const Tag = tag;
|
|
2698
|
+
function Component({ children, ...rest }) {
|
|
2699
|
+
return /* @__PURE__ */ jsx(Tag, {
|
|
2700
|
+
...rest,
|
|
2701
|
+
"data-scope": "list",
|
|
2702
|
+
"data-part": name.toLowerCase(),
|
|
2703
|
+
children
|
|
2704
|
+
});
|
|
2705
|
+
}
|
|
2706
|
+
Component.displayName = "List" + name;
|
|
2707
|
+
return Component;
|
|
2708
|
+
}
|
|
2709
|
+
function Root$4({ bordered = false, hoverable = false, children, ...rest }) {
|
|
2710
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
2711
|
+
...rest,
|
|
2712
|
+
"data-scope": "list",
|
|
2713
|
+
"data-part": "root",
|
|
2714
|
+
"data-bordered": bordered || void 0,
|
|
2715
|
+
"data-hoverable": hoverable || void 0,
|
|
2716
|
+
children
|
|
2717
|
+
});
|
|
2718
|
+
}
|
|
2719
|
+
Root$4.displayName = "ListRoot";
|
|
2720
|
+
const Item$1 = part$5("Item", "li");
|
|
2721
|
+
const Leading = part$5("Leading", "div");
|
|
2722
|
+
const Actions$1 = part$5("Actions", "div");
|
|
2723
|
+
function Content$1({ title, description, children, ...rest }) {
|
|
2724
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2725
|
+
...rest,
|
|
2726
|
+
"data-scope": "list",
|
|
2727
|
+
"data-part": "content",
|
|
2728
|
+
children: [
|
|
2729
|
+
title ? /* @__PURE__ */ jsx("div", {
|
|
2730
|
+
"data-scope": "list",
|
|
2731
|
+
"data-part": "title",
|
|
2732
|
+
children: title
|
|
2733
|
+
}) : null,
|
|
2734
|
+
description ? /* @__PURE__ */ jsx("div", {
|
|
2735
|
+
"data-scope": "list",
|
|
2736
|
+
"data-part": "description",
|
|
2737
|
+
children: description
|
|
2738
|
+
}) : null,
|
|
2739
|
+
children
|
|
2740
|
+
]
|
|
2741
|
+
});
|
|
2742
|
+
}
|
|
2743
|
+
Content$1.displayName = "ListContent";
|
|
2744
|
+
const List = Object.assign(Root$4, {
|
|
2745
|
+
Root: Root$4,
|
|
2746
|
+
Item: Item$1,
|
|
2747
|
+
Leading,
|
|
2748
|
+
Content: Content$1,
|
|
2749
|
+
Actions: Actions$1
|
|
2750
|
+
});
|
|
2751
|
+
injectComponentStyle("list");
|
|
2752
|
+
//#endregion
|
|
882
2753
|
//#region src/components/listbox/index.tsx
|
|
883
2754
|
/** Ark's Listbox, dressed in the paper-and-ink system: quiet rows of ink
|
|
884
2755
|
* where the checked row alone takes the flat primary fill. The API is
|
|
@@ -895,6 +2766,32 @@ injectComponentStyle("listbox");
|
|
|
895
2766
|
const Marquee = Marquee$1;
|
|
896
2767
|
injectComponentStyle("marquee");
|
|
897
2768
|
//#endregion
|
|
2769
|
+
//#region src/components/masonry/index.tsx
|
|
2770
|
+
/** The same named steps of the space ramp the stack uses — one
|
|
2771
|
+
* vocabulary of distance across the layout primitives. */
|
|
2772
|
+
const gapVars$1 = {
|
|
2773
|
+
none: "0",
|
|
2774
|
+
xs: "var(--bs-gap-xs)",
|
|
2775
|
+
sm: "var(--bs-gap-sm)",
|
|
2776
|
+
md: "var(--bs-gap-md)",
|
|
2777
|
+
lg: "var(--bs-gap-lg)",
|
|
2778
|
+
xl: "var(--bs-gap-xl)"
|
|
2779
|
+
};
|
|
2780
|
+
function Masonry({ columns = 3, gap = "md", ...rest }) {
|
|
2781
|
+
const style = {
|
|
2782
|
+
...rest.style,
|
|
2783
|
+
"--bs-masonry-columns": String(columns),
|
|
2784
|
+
"--bs-masonry-gap": gapVars$1[gap] ?? gapVars$1.md
|
|
2785
|
+
};
|
|
2786
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2787
|
+
...rest,
|
|
2788
|
+
style,
|
|
2789
|
+
"data-scope": "masonry",
|
|
2790
|
+
"data-part": "root"
|
|
2791
|
+
});
|
|
2792
|
+
}
|
|
2793
|
+
injectComponentStyle("masonry");
|
|
2794
|
+
//#endregion
|
|
898
2795
|
//#region src/components/menu/index.tsx
|
|
899
2796
|
/** Ark's Menu, dressed in the paper-and-ink system: a quiet paper vessel on
|
|
900
2797
|
* elevation, hover as light on the row, checked items as the flat ink fill.
|
|
@@ -904,6 +2801,36 @@ injectComponentStyle("marquee");
|
|
|
904
2801
|
const Menu = Menu$1;
|
|
905
2802
|
injectComponentStyle("menu");
|
|
906
2803
|
//#endregion
|
|
2804
|
+
//#region src/components/menubar/index.tsx
|
|
2805
|
+
function Menubar({ items, onSelect, children, ...rest }) {
|
|
2806
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2807
|
+
...rest,
|
|
2808
|
+
"data-scope": "menubar",
|
|
2809
|
+
"data-part": "root",
|
|
2810
|
+
role: "menubar",
|
|
2811
|
+
children: [items.map((group) => /* @__PURE__ */ jsxs(Menu$1.Root, {
|
|
2812
|
+
positioning: { placement: "bottom-start" },
|
|
2813
|
+
children: [/* @__PURE__ */ jsx(Menu$1.Trigger, {
|
|
2814
|
+
asChild: true,
|
|
2815
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
2816
|
+
type: "button",
|
|
2817
|
+
"data-scope": "menubar",
|
|
2818
|
+
"data-part": "trigger",
|
|
2819
|
+
children: group.label
|
|
2820
|
+
})
|
|
2821
|
+
}), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Menu$1.Positioner, { children: /* @__PURE__ */ jsx(Menu$1.Content, { children: group.items.map((entry) => /* @__PURE__ */ jsx(Menu$1.Item, {
|
|
2822
|
+
value: entry.value,
|
|
2823
|
+
disabled: entry.disabled,
|
|
2824
|
+
"data-danger": entry.danger ? "" : void 0,
|
|
2825
|
+
onSelect: () => onSelect?.(entry.value),
|
|
2826
|
+
children: /* @__PURE__ */ jsx(Menu$1.ItemText, { children: entry.label })
|
|
2827
|
+
}, entry.value)) }) }) })]
|
|
2828
|
+
}, group.label)), children]
|
|
2829
|
+
});
|
|
2830
|
+
}
|
|
2831
|
+
injectComponentStyle("menubar");
|
|
2832
|
+
injectComponentStyle("menu");
|
|
2833
|
+
//#endregion
|
|
907
2834
|
//#region src/components/meter/index.tsx
|
|
908
2835
|
function part$4(name, tag) {
|
|
909
2836
|
const Tag = tag;
|
|
@@ -918,7 +2845,7 @@ function part$4(name, tag) {
|
|
|
918
2845
|
}
|
|
919
2846
|
const Label$2 = part$4("Label", "span");
|
|
920
2847
|
const ValueText = part$4("ValueText", "span");
|
|
921
|
-
const Track = ({
|
|
2848
|
+
const Track = ({ ...rest }) => /* @__PURE__ */ jsx("span", {
|
|
922
2849
|
...rest,
|
|
923
2850
|
"data-scope": "meter",
|
|
924
2851
|
"data-part": "track",
|
|
@@ -1000,6 +2927,43 @@ injectComponentStyle("password-input");
|
|
|
1000
2927
|
const PinInput = PinInput$1;
|
|
1001
2928
|
injectComponentStyle("pin-input");
|
|
1002
2929
|
//#endregion
|
|
2930
|
+
//#region src/components/popconfirm/index.tsx
|
|
2931
|
+
function Popconfirm({ message, confirmText = "Confirm", cancelText = "Cancel", children, onConfirm, onCancel }) {
|
|
2932
|
+
const [open, setOpen] = useState(false);
|
|
2933
|
+
function settle(confirmed) {
|
|
2934
|
+
setOpen(false);
|
|
2935
|
+
(confirmed ? onConfirm : onCancel)?.();
|
|
2936
|
+
}
|
|
2937
|
+
return /* @__PURE__ */ jsxs(Popover$1.Root, {
|
|
2938
|
+
open,
|
|
2939
|
+
onOpenChange: (details) => setOpen(details.open),
|
|
2940
|
+
positioning: { placement: "top" },
|
|
2941
|
+
children: [/* @__PURE__ */ jsx(Popover$1.Trigger, {
|
|
2942
|
+
asChild: true,
|
|
2943
|
+
children
|
|
2944
|
+
}), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { children: /* @__PURE__ */ jsxs(Popover$1.Content, {
|
|
2945
|
+
className: "bs-popconfirm",
|
|
2946
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
2947
|
+
"data-part": "message",
|
|
2948
|
+
children: message
|
|
2949
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
2950
|
+
"data-part": "actions",
|
|
2951
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
2952
|
+
variant: "ghost",
|
|
2953
|
+
size: "sm",
|
|
2954
|
+
onClick: () => settle(false),
|
|
2955
|
+
children: cancelText
|
|
2956
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
2957
|
+
size: "sm",
|
|
2958
|
+
onClick: () => settle(true),
|
|
2959
|
+
children: confirmText
|
|
2960
|
+
})]
|
|
2961
|
+
})]
|
|
2962
|
+
}) }) })]
|
|
2963
|
+
});
|
|
2964
|
+
}
|
|
2965
|
+
injectComponentStyle("popconfirm");
|
|
2966
|
+
//#endregion
|
|
1003
2967
|
//#region src/components/popover/index.tsx
|
|
1004
2968
|
/** Ark's Popover, dressed in the paper-and-ink system: a paper vessel that
|
|
1005
2969
|
* dissolves in on elevation, anchored to its trigger by a whisker arrow. The
|
|
@@ -1016,6 +2980,59 @@ injectComponentStyle("popover");
|
|
|
1016
2980
|
const Progress = Progress$1;
|
|
1017
2981
|
injectComponentStyle("progress");
|
|
1018
2982
|
//#endregion
|
|
2983
|
+
//#region src/components/progress-group/index.tsx
|
|
2984
|
+
function ProgressGroup({ segments, max, showLegend = true, ...rest }) {
|
|
2985
|
+
const total = max ?? segments.reduce((sum, segment) => sum + segment.value, 0);
|
|
2986
|
+
const share = (value) => total > 0 ? `${value / total * 100}%` : "0%";
|
|
2987
|
+
const nameOf = (segment, index) => segment.label ?? segment.pigment ?? `Segment ${index + 1}`;
|
|
2988
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2989
|
+
...rest,
|
|
2990
|
+
"data-scope": "progress-group",
|
|
2991
|
+
"data-part": "root",
|
|
2992
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
2993
|
+
"data-scope": "progress-group",
|
|
2994
|
+
"data-part": "track",
|
|
2995
|
+
children: segments.map((segment, index) => /* @__PURE__ */ jsx("div", {
|
|
2996
|
+
"data-scope": "progress-group",
|
|
2997
|
+
"data-part": "segment",
|
|
2998
|
+
"data-pigment": segment.pigment,
|
|
2999
|
+
role: "progressbar",
|
|
3000
|
+
"aria-valuenow": segment.value,
|
|
3001
|
+
"aria-valuemin": 0,
|
|
3002
|
+
"aria-valuemax": total,
|
|
3003
|
+
"aria-label": nameOf(segment, index),
|
|
3004
|
+
style: { inlineSize: share(segment.value) }
|
|
3005
|
+
}, index))
|
|
3006
|
+
}), showLegend ? /* @__PURE__ */ jsx("div", {
|
|
3007
|
+
"data-scope": "progress-group",
|
|
3008
|
+
"data-part": "legend",
|
|
3009
|
+
children: segments.map((segment, index) => /* @__PURE__ */ jsxs("div", {
|
|
3010
|
+
"data-scope": "progress-group",
|
|
3011
|
+
"data-part": "legend-item",
|
|
3012
|
+
"data-pigment": segment.pigment,
|
|
3013
|
+
children: [
|
|
3014
|
+
/* @__PURE__ */ jsx("span", {
|
|
3015
|
+
"data-scope": "progress-group",
|
|
3016
|
+
"data-part": "swatch",
|
|
3017
|
+
"aria-hidden": "true"
|
|
3018
|
+
}),
|
|
3019
|
+
/* @__PURE__ */ jsx("span", {
|
|
3020
|
+
"data-scope": "progress-group",
|
|
3021
|
+
"data-part": "legend-label",
|
|
3022
|
+
children: nameOf(segment, index)
|
|
3023
|
+
}),
|
|
3024
|
+
/* @__PURE__ */ jsx("span", {
|
|
3025
|
+
"data-scope": "progress-group",
|
|
3026
|
+
"data-part": "legend-value",
|
|
3027
|
+
children: String(segment.value)
|
|
3028
|
+
})
|
|
3029
|
+
]
|
|
3030
|
+
}, index))
|
|
3031
|
+
}) : null]
|
|
3032
|
+
});
|
|
3033
|
+
}
|
|
3034
|
+
injectComponentStyle("progress-group");
|
|
3035
|
+
//#endregion
|
|
1019
3036
|
//#region src/components/qr-code/index.tsx
|
|
1020
3037
|
/** Ark's QrCode, dressed in the paper-and-ink system: the pattern prints in
|
|
1021
3038
|
* ink on the page, with an optional paper badge and a seal-cut download
|
|
@@ -1103,6 +3120,71 @@ injectComponentStyle("skeleton");
|
|
|
1103
3120
|
const Slider = Slider$1;
|
|
1104
3121
|
injectComponentStyle("slider");
|
|
1105
3122
|
//#endregion
|
|
3123
|
+
//#region src/components/split-button/index.tsx
|
|
3124
|
+
/**
|
|
3125
|
+
* A primary action with its alternatives one seam away: the main button
|
|
3126
|
+
* fires `onClick`, the fitted arrow opens a paper vessel of the same
|
|
3127
|
+
* register whose entries report `onSelect` with their value. The arrow
|
|
3128
|
+
* is the menu machine's trigger grafting the button recipe — the
|
|
3129
|
+
* machine keeps the element (positioning, focus, `data-state`), the
|
|
3130
|
+
* button stylesheet's trigger register dresses it. The popup keeps the
|
|
3131
|
+
* menu parts untouched.
|
|
3132
|
+
*/
|
|
3133
|
+
function SplitButton({ label, items = [], variant = "solid", tone = "ink", size = "md", disabled = false, onClick, onSelect, ...rest }) {
|
|
3134
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
3135
|
+
...rest,
|
|
3136
|
+
"data-scope": "split-button",
|
|
3137
|
+
"data-part": "root",
|
|
3138
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
3139
|
+
variant,
|
|
3140
|
+
tone,
|
|
3141
|
+
size,
|
|
3142
|
+
disabled,
|
|
3143
|
+
onClick,
|
|
3144
|
+
children: label
|
|
3145
|
+
}), /* @__PURE__ */ jsxs(Menu$1.Root, {
|
|
3146
|
+
positioning: { placement: "bottom-end" },
|
|
3147
|
+
children: [/* @__PURE__ */ jsx(Menu$1.Trigger, {
|
|
3148
|
+
asChild: true,
|
|
3149
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
3150
|
+
type: "button",
|
|
3151
|
+
disabled,
|
|
3152
|
+
"data-variant": variant,
|
|
3153
|
+
"data-tone": tone,
|
|
3154
|
+
"data-size": size,
|
|
3155
|
+
"data-square": "true",
|
|
3156
|
+
"data-motion": "ink-ripple lit",
|
|
3157
|
+
"aria-label": "More actions",
|
|
3158
|
+
children: chevronDown$1()
|
|
3159
|
+
})
|
|
3160
|
+
}), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Menu$1.Positioner, { children: /* @__PURE__ */ jsx(Menu$1.Content, { children: items.map((entry) => /* @__PURE__ */ jsx(Menu$1.Item, {
|
|
3161
|
+
value: entry.value,
|
|
3162
|
+
disabled: entry.disabled,
|
|
3163
|
+
"data-danger": entry.danger ? "" : void 0,
|
|
3164
|
+
onSelect: () => onSelect?.(entry.value),
|
|
3165
|
+
children: /* @__PURE__ */ jsx(Menu$1.ItemText, { children: entry.label })
|
|
3166
|
+
}, entry.value)) }) }) })]
|
|
3167
|
+
})]
|
|
3168
|
+
});
|
|
3169
|
+
}
|
|
3170
|
+
/** One stroke pointing down: all a fitted dropdown arrow needs. */
|
|
3171
|
+
function chevronDown$1() {
|
|
3172
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
3173
|
+
width: 16,
|
|
3174
|
+
height: 16,
|
|
3175
|
+
viewBox: "0 0 24 24",
|
|
3176
|
+
fill: "none",
|
|
3177
|
+
stroke: "currentColor",
|
|
3178
|
+
strokeWidth: 1.75,
|
|
3179
|
+
strokeLinecap: "round",
|
|
3180
|
+
strokeLinejoin: "round",
|
|
3181
|
+
"aria-hidden": "true",
|
|
3182
|
+
children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
|
|
3183
|
+
});
|
|
3184
|
+
}
|
|
3185
|
+
injectComponentStyle("split-button");
|
|
3186
|
+
injectComponentStyle("menu");
|
|
3187
|
+
//#endregion
|
|
1106
3188
|
//#region src/components/splitter/index.tsx
|
|
1107
3189
|
/** Ark's Splitter, dressed in the paper-and-ink system: panels divide on
|
|
1108
3190
|
* a hairline and a small paper-seal thumb answers the hand. The API is
|
|
@@ -1110,6 +3192,35 @@ injectComponentStyle("slider");
|
|
|
1110
3192
|
const Splitter = Splitter$1;
|
|
1111
3193
|
injectComponentStyle("splitter");
|
|
1112
3194
|
//#endregion
|
|
3195
|
+
//#region src/components/stack/index.tsx
|
|
3196
|
+
/** Whitespace chosen by name: the named steps point at the space ramp so
|
|
3197
|
+
* siblings are held apart by one token, never by ad-hoc margins. */
|
|
3198
|
+
const gapVars = {
|
|
3199
|
+
none: "0",
|
|
3200
|
+
xs: "var(--bs-gap-xs)",
|
|
3201
|
+
sm: "var(--bs-gap-sm)",
|
|
3202
|
+
md: "var(--bs-gap-md)",
|
|
3203
|
+
lg: "var(--bs-gap-lg)",
|
|
3204
|
+
xl: "var(--bs-gap-xl)"
|
|
3205
|
+
};
|
|
3206
|
+
function Stack({ direction = "column", gap = "md", wrap = false, align, justify, ...rest }) {
|
|
3207
|
+
const style = {
|
|
3208
|
+
...rest.style,
|
|
3209
|
+
"--bs-stack-gap": gapVars[gap] ?? gapVars.md,
|
|
3210
|
+
alignItems: align,
|
|
3211
|
+
justifyContent: justify,
|
|
3212
|
+
flexWrap: wrap ? "wrap" : void 0
|
|
3213
|
+
};
|
|
3214
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3215
|
+
...rest,
|
|
3216
|
+
style,
|
|
3217
|
+
"data-scope": "stack",
|
|
3218
|
+
"data-part": "root",
|
|
3219
|
+
"data-direction": direction
|
|
3220
|
+
});
|
|
3221
|
+
}
|
|
3222
|
+
injectComponentStyle("stack");
|
|
3223
|
+
//#endregion
|
|
1113
3224
|
//#region src/components/page-header/index.tsx
|
|
1114
3225
|
/** The page's face: an eyebrow whisper, a serif title, one line of
|
|
1115
3226
|
* description, and the actions resting beside the title on the same
|
|
@@ -1255,6 +3366,36 @@ const TagsInput = TagsInput$1;
|
|
|
1255
3366
|
injectComponentStyle("tags-input");
|
|
1256
3367
|
//#endregion
|
|
1257
3368
|
//#region src/components/table/index.tsx
|
|
3369
|
+
/** The pagination bar's arrows — thin chevrons for the row of seals. */
|
|
3370
|
+
function pageGlyph(direction) {
|
|
3371
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
3372
|
+
viewBox: "0 0 24 24",
|
|
3373
|
+
width: 14,
|
|
3374
|
+
height: 14,
|
|
3375
|
+
fill: "none",
|
|
3376
|
+
stroke: "currentColor",
|
|
3377
|
+
strokeWidth: 2,
|
|
3378
|
+
strokeLinecap: "round",
|
|
3379
|
+
strokeLinejoin: "round",
|
|
3380
|
+
"aria-hidden": "true",
|
|
3381
|
+
children: /* @__PURE__ */ jsx("path", { d: direction === "start" ? "m15 18-6-6 6-6" : "m9 18 6-6-6-6" })
|
|
3382
|
+
});
|
|
3383
|
+
}
|
|
3384
|
+
/** The page-size select's pointing chevron. */
|
|
3385
|
+
function chevronDownGlyph() {
|
|
3386
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
3387
|
+
viewBox: "0 0 24 24",
|
|
3388
|
+
width: 14,
|
|
3389
|
+
height: 14,
|
|
3390
|
+
fill: "none",
|
|
3391
|
+
stroke: "currentColor",
|
|
3392
|
+
strokeWidth: 2.5,
|
|
3393
|
+
strokeLinecap: "round",
|
|
3394
|
+
strokeLinejoin: "round",
|
|
3395
|
+
"aria-hidden": "true",
|
|
3396
|
+
children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
|
|
3397
|
+
});
|
|
3398
|
+
}
|
|
1258
3399
|
const SELECT_COL_ID = "__select";
|
|
1259
3400
|
const SELECT_COL_WIDTH = 48;
|
|
1260
3401
|
/** The list holding `id` and the index inside it, or null when absent. */
|
|
@@ -1334,7 +3475,7 @@ function SelectBox(props) {
|
|
|
1334
3475
|
}
|
|
1335
3476
|
function DataTable(rawProps) {
|
|
1336
3477
|
const props = rawProps;
|
|
1337
|
-
const { selectable = false, sortable = true, filterable = false, tree = false, virtual: virtualProp = false, rowHeight = 40, paginated = false, pageSize = 10, pageSizeOptions = [
|
|
3478
|
+
const { selectable = false, sortable = true, filterable = false, showToolbar = true, tree = false, virtual: virtualProp = false, rowHeight = 40, paginated = false, pageSize = 10, pageSizeOptions = [
|
|
1338
3479
|
10,
|
|
1339
3480
|
20,
|
|
1340
3481
|
50
|
|
@@ -1702,7 +3843,7 @@ function DataTable(rawProps) {
|
|
|
1702
3843
|
onDragLeave: canDrag ? onColDragLeave : void 0,
|
|
1703
3844
|
onDrop: canDrag ? (e) => onColDrop(column, e) : void 0,
|
|
1704
3845
|
onDragEnd: canDrag ? onColDragEnd : void 0,
|
|
1705
|
-
children: [header.isPlaceholder ? null : /* @__PURE__ */ jsx(FlexRender, { header }), canFilter ? /* @__PURE__ */ jsx("input", {
|
|
3846
|
+
children: [header.isPlaceholder || header.column.columnDef.header === "" ? null : /* @__PURE__ */ jsx(FlexRender, { header }), canFilter ? /* @__PURE__ */ jsx("input", {
|
|
1706
3847
|
type: "text",
|
|
1707
3848
|
"data-scope": "table",
|
|
1708
3849
|
"data-part": "header-filter",
|
|
@@ -1809,55 +3950,53 @@ function DataTable(rawProps) {
|
|
|
1809
3950
|
const paginationBar = paginated ? (() => {
|
|
1810
3951
|
const pagination = table.atoms.pagination.get();
|
|
1811
3952
|
const rowCount = table.getRowCount();
|
|
1812
|
-
const
|
|
3953
|
+
const sizeItems = createListCollection({ items: pageSizeOptions.map((size) => ({
|
|
3954
|
+
label: `${size} / page`,
|
|
3955
|
+
value: String(size)
|
|
3956
|
+
})) });
|
|
3957
|
+
const pageSize = /* @__PURE__ */ jsxs(Select.Root, {
|
|
3958
|
+
collection: sizeItems,
|
|
3959
|
+
value: [String(pagination.pageSize)],
|
|
3960
|
+
onValueChange: (details) => table.setPageSize(Number(details.value[0])),
|
|
3961
|
+
positioning: { placement: "top-start" },
|
|
3962
|
+
children: [/* @__PURE__ */ jsx(Select.Control, { children: /* @__PURE__ */ jsxs(Select.Trigger, {
|
|
3963
|
+
"aria-label": "Rows per page",
|
|
3964
|
+
children: [/* @__PURE__ */ jsx(Select.ValueText, {}), /* @__PURE__ */ jsx(Select.Indicator, { children: chevronDownGlyph() })]
|
|
3965
|
+
}) }), /* @__PURE__ */ jsx(Select.Positioner, { children: /* @__PURE__ */ jsx(Select.Content, { children: sizeItems.items.map((item) => /* @__PURE__ */ jsxs(Select.Item, {
|
|
3966
|
+
item,
|
|
3967
|
+
children: [/* @__PURE__ */ jsx(Select.ItemText, { children: item.label }), /* @__PURE__ */ jsx(Select.ItemIndicator, { children: "✓" })]
|
|
3968
|
+
}, item.value)) }) })]
|
|
3969
|
+
});
|
|
3970
|
+
const pager = /* @__PURE__ */ jsxs(Pagination.Root, {
|
|
3971
|
+
count: rowCount,
|
|
3972
|
+
pageSize: pagination.pageSize,
|
|
3973
|
+
page: pagination.pageIndex + 1,
|
|
3974
|
+
onPageChange: (details) => table.setPageIndex(details.page - 1),
|
|
3975
|
+
children: [
|
|
3976
|
+
/* @__PURE__ */ jsx(Pagination.PrevTrigger, { children: pageGlyph("start") }),
|
|
3977
|
+
/* @__PURE__ */ jsx(Pagination.Context, { children: (scope) => scope.pages.map((page, index) => page.type === "ellipsis" ? /* @__PURE__ */ jsx(Pagination.Ellipsis, { index }, `e${index}`) : /* @__PURE__ */ jsx(Pagination.Item, {
|
|
3978
|
+
type: "page",
|
|
3979
|
+
value: page.value,
|
|
3980
|
+
children: String(page.value)
|
|
3981
|
+
}, page.value)) }),
|
|
3982
|
+
/* @__PURE__ */ jsx(Pagination.NextTrigger, { children: pageGlyph("end") })
|
|
3983
|
+
]
|
|
3984
|
+
});
|
|
1813
3985
|
return /* @__PURE__ */ jsxs("div", {
|
|
1814
3986
|
"data-scope": "table",
|
|
1815
3987
|
"data-part": "pagination",
|
|
1816
|
-
children: [
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
/* @__PURE__ */ jsx("button", {
|
|
1826
|
-
type: "button",
|
|
1827
|
-
"data-scope": "table",
|
|
1828
|
-
"data-part": "page-button",
|
|
1829
|
-
disabled: !table.getCanNextPage(),
|
|
1830
|
-
onClick: () => table.nextPage(),
|
|
1831
|
-
children: "Next"
|
|
1832
|
-
}),
|
|
1833
|
-
/* @__PURE__ */ jsx("select", {
|
|
1834
|
-
"data-scope": "table",
|
|
1835
|
-
"data-part": "page-size",
|
|
1836
|
-
"aria-label": "Rows per page",
|
|
1837
|
-
value: pagination.pageSize,
|
|
1838
|
-
onChange: (e) => table.setPageSize(Number(e.target.value)),
|
|
1839
|
-
children: pageSizeOptions.map((size) => /* @__PURE__ */ jsxs("option", {
|
|
1840
|
-
value: size,
|
|
1841
|
-
children: [size, " / page"]
|
|
1842
|
-
}, size))
|
|
1843
|
-
}),
|
|
1844
|
-
/* @__PURE__ */ jsxs("span", {
|
|
1845
|
-
"data-scope": "table",
|
|
1846
|
-
"data-part": "page-status",
|
|
1847
|
-
children: [
|
|
1848
|
-
"Page ",
|
|
1849
|
-
pagination.pageIndex + 1,
|
|
1850
|
-
" of ",
|
|
1851
|
-
pageCount,
|
|
1852
|
-
" · ",
|
|
1853
|
-
rowCount,
|
|
1854
|
-
" rows"
|
|
1855
|
-
]
|
|
1856
|
-
})
|
|
1857
|
-
]
|
|
3988
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
3989
|
+
"data-scope": "table",
|
|
3990
|
+
"data-part": "page-status",
|
|
3991
|
+
children: [rowCount, " rows"]
|
|
3992
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
3993
|
+
"data-scope": "table",
|
|
3994
|
+
"data-part": "page-nav",
|
|
3995
|
+
children: [pageSize, pager]
|
|
3996
|
+
})]
|
|
1858
3997
|
});
|
|
1859
3998
|
})() : null;
|
|
1860
|
-
const toolbar = filterable ? /* @__PURE__ */ jsx("div", {
|
|
3999
|
+
const toolbar = filterable && showToolbar ? /* @__PURE__ */ jsx("div", {
|
|
1861
4000
|
"data-scope": "table",
|
|
1862
4001
|
"data-part": "toolbar",
|
|
1863
4002
|
children: /* @__PURE__ */ jsx("input", {
|
|
@@ -1902,6 +4041,22 @@ function DataTable(rawProps) {
|
|
|
1902
4041
|
}
|
|
1903
4042
|
injectComponentStyle("table");
|
|
1904
4043
|
//#endregion
|
|
4044
|
+
//#region src/components/textarea/index.tsx
|
|
4045
|
+
function Textarea({ value, invalid = false, onValueChange, ...rest }) {
|
|
4046
|
+
const field = useFieldContext();
|
|
4047
|
+
const fieldProps = field?.getTextareaProps() ?? {};
|
|
4048
|
+
return /* @__PURE__ */ jsx("textarea", {
|
|
4049
|
+
...fieldProps,
|
|
4050
|
+
...rest,
|
|
4051
|
+
...value !== void 0 ? { value } : {},
|
|
4052
|
+
"data-scope": "textarea",
|
|
4053
|
+
"data-part": "root",
|
|
4054
|
+
"data-invalid": invalid || field?.invalid ? "" : void 0,
|
|
4055
|
+
onChange: (event) => onValueChange?.(event.target.value)
|
|
4056
|
+
});
|
|
4057
|
+
}
|
|
4058
|
+
injectComponentStyle("textarea");
|
|
4059
|
+
//#endregion
|
|
1905
4060
|
//#region src/components/timeline/index.tsx
|
|
1906
4061
|
/** A line of moments: Root is the ordered thread, Item one moment on it,
|
|
1907
4062
|
* Marker the point where the thread passes, Content what the moment
|
|
@@ -2016,6 +4171,299 @@ injectComponentStyle("tooltip");
|
|
|
2016
4171
|
const Tour = Tour$1;
|
|
2017
4172
|
injectComponentStyle("tour");
|
|
2018
4173
|
//#endregion
|
|
4174
|
+
//#region src/components/transfer/index.tsx
|
|
4175
|
+
const checkGlyph = /* @__PURE__ */ jsx("svg", {
|
|
4176
|
+
viewBox: "0 0 24 24",
|
|
4177
|
+
fill: "none",
|
|
4178
|
+
stroke: "currentColor",
|
|
4179
|
+
strokeWidth: 3,
|
|
4180
|
+
strokeLinecap: "round",
|
|
4181
|
+
strokeLinejoin: "round",
|
|
4182
|
+
"aria-hidden": "true",
|
|
4183
|
+
children: /* @__PURE__ */ jsx("path", { d: "m5 12.5 5 5L19 7" })
|
|
4184
|
+
});
|
|
4185
|
+
function arrowGlyph(direction) {
|
|
4186
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
4187
|
+
viewBox: "0 0 24 24",
|
|
4188
|
+
fill: "none",
|
|
4189
|
+
stroke: "currentColor",
|
|
4190
|
+
strokeWidth: 1.75,
|
|
4191
|
+
strokeLinecap: "round",
|
|
4192
|
+
strokeLinejoin: "round",
|
|
4193
|
+
"aria-hidden": "true",
|
|
4194
|
+
children: /* @__PURE__ */ jsx("path", { d: direction === "right" ? "M5 12h14m-6-6 6 6-6 6" : "M19 12H5m6-6-6 6 6 6" })
|
|
4195
|
+
});
|
|
4196
|
+
}
|
|
4197
|
+
/**
|
|
4198
|
+
* Two ledgers and a crossing: items sit in the source column until the
|
|
4199
|
+
* reader checks them and walks them across — and back, the same way.
|
|
4200
|
+
* `value` is the target column's value list; everything else in `data`
|
|
4201
|
+
* stays on the left. `searchable` adds a filter line to each panel.
|
|
4202
|
+
*/
|
|
4203
|
+
function Transfer({ value = [], data, titles = ["Source", "Target"], searchable = false, disabled = false, onValueChange, ...rest }) {
|
|
4204
|
+
const [checkedSource, setCheckedSource] = useState(() => /* @__PURE__ */ new Set());
|
|
4205
|
+
const [checkedTarget, setCheckedTarget] = useState(() => /* @__PURE__ */ new Set());
|
|
4206
|
+
const [sourceQuery, setSourceQuery] = useState("");
|
|
4207
|
+
const [targetQuery, setTargetQuery] = useState("");
|
|
4208
|
+
const target = new Set(value);
|
|
4209
|
+
function panelItems(values, inTarget, query) {
|
|
4210
|
+
return values.filter((item) => item.label.toLowerCase().includes(query.toLowerCase()) && target.has(item.value) === inTarget);
|
|
4211
|
+
}
|
|
4212
|
+
function toggle(side, option) {
|
|
4213
|
+
(side === "source" ? setCheckedSource : setCheckedTarget)((prev) => {
|
|
4214
|
+
const next = new Set(prev);
|
|
4215
|
+
if (next.has(option)) next.delete(option);
|
|
4216
|
+
else next.add(option);
|
|
4217
|
+
return next;
|
|
4218
|
+
});
|
|
4219
|
+
}
|
|
4220
|
+
function move(toTarget) {
|
|
4221
|
+
const moving = toTarget ? checkedSource : checkedTarget;
|
|
4222
|
+
if (moving.size === 0) return;
|
|
4223
|
+
const next = toTarget ? [...value, ...moving].filter((entry, index, all) => all.indexOf(entry) === index) : value.filter((entry) => !moving.has(entry));
|
|
4224
|
+
if (toTarget) setCheckedSource(/* @__PURE__ */ new Set());
|
|
4225
|
+
else setCheckedTarget(/* @__PURE__ */ new Set());
|
|
4226
|
+
onValueChange?.(next);
|
|
4227
|
+
}
|
|
4228
|
+
function panel(side, title, query, onQuery, checked, items) {
|
|
4229
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4230
|
+
"data-scope": "transfer",
|
|
4231
|
+
"data-part": "panel",
|
|
4232
|
+
"data-side": side,
|
|
4233
|
+
children: [
|
|
4234
|
+
/* @__PURE__ */ jsxs("div", {
|
|
4235
|
+
"data-scope": "transfer",
|
|
4236
|
+
"data-part": "head",
|
|
4237
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
4238
|
+
"data-scope": "transfer",
|
|
4239
|
+
"data-part": "title",
|
|
4240
|
+
children: title
|
|
4241
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
4242
|
+
"data-scope": "transfer",
|
|
4243
|
+
"data-part": "count",
|
|
4244
|
+
children: items.length
|
|
4245
|
+
})]
|
|
4246
|
+
}),
|
|
4247
|
+
searchable ? /* @__PURE__ */ jsx("div", {
|
|
4248
|
+
"data-scope": "transfer",
|
|
4249
|
+
"data-part": "search",
|
|
4250
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
4251
|
+
size: "sm",
|
|
4252
|
+
value: query,
|
|
4253
|
+
onValueChange: onQuery,
|
|
4254
|
+
placeholder: "Filter…",
|
|
4255
|
+
"aria-label": `Filter ${title}`
|
|
4256
|
+
})
|
|
4257
|
+
}) : null,
|
|
4258
|
+
/* @__PURE__ */ jsx("div", {
|
|
4259
|
+
"data-scope": "transfer",
|
|
4260
|
+
"data-part": "list",
|
|
4261
|
+
children: items.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
4262
|
+
"data-scope": "transfer",
|
|
4263
|
+
"data-part": "empty",
|
|
4264
|
+
children: "Nothing here"
|
|
4265
|
+
}) : items.map((item) => {
|
|
4266
|
+
const locked = disabled || item.disabled === true;
|
|
4267
|
+
return /* @__PURE__ */ jsxs(Checkbox$1.Root, {
|
|
4268
|
+
checked: checked.has(item.value),
|
|
4269
|
+
disabled: locked,
|
|
4270
|
+
onCheckedChange: () => toggle(side, item.value),
|
|
4271
|
+
children: [
|
|
4272
|
+
/* @__PURE__ */ jsx(Checkbox$1.Control, { children: /* @__PURE__ */ jsx(Checkbox$1.Indicator, { children: checkGlyph }) }),
|
|
4273
|
+
/* @__PURE__ */ jsx(Checkbox$1.Label, {
|
|
4274
|
+
"data-part": "label",
|
|
4275
|
+
children: item.label
|
|
4276
|
+
}),
|
|
4277
|
+
/* @__PURE__ */ jsx(Checkbox$1.HiddenInput, {})
|
|
4278
|
+
]
|
|
4279
|
+
}, item.value);
|
|
4280
|
+
})
|
|
4281
|
+
})
|
|
4282
|
+
]
|
|
4283
|
+
});
|
|
4284
|
+
}
|
|
4285
|
+
const items = panelItems(data, false, sourceQuery);
|
|
4286
|
+
const targetItems = panelItems(data, true, targetQuery);
|
|
4287
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4288
|
+
...rest,
|
|
4289
|
+
"data-scope": "transfer",
|
|
4290
|
+
"data-part": "root",
|
|
4291
|
+
children: [
|
|
4292
|
+
panel("source", titles[0], sourceQuery, setSourceQuery, checkedSource, items),
|
|
4293
|
+
/* @__PURE__ */ jsxs("div", {
|
|
4294
|
+
"data-scope": "transfer",
|
|
4295
|
+
"data-part": "operations",
|
|
4296
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
4297
|
+
variant: "outline",
|
|
4298
|
+
size: "sm",
|
|
4299
|
+
square: true,
|
|
4300
|
+
disabled: checkedSource.size === 0 || disabled,
|
|
4301
|
+
"aria-label": "Move right",
|
|
4302
|
+
onClick: () => move(true),
|
|
4303
|
+
children: arrowGlyph("right")
|
|
4304
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
4305
|
+
variant: "outline",
|
|
4306
|
+
size: "sm",
|
|
4307
|
+
square: true,
|
|
4308
|
+
disabled: checkedTarget.size === 0 || disabled,
|
|
4309
|
+
"aria-label": "Move left",
|
|
4310
|
+
onClick: () => move(false),
|
|
4311
|
+
children: arrowGlyph("left")
|
|
4312
|
+
})]
|
|
4313
|
+
}),
|
|
4314
|
+
panel("target", titles[1], targetQuery, setTargetQuery, checkedTarget, targetItems)
|
|
4315
|
+
]
|
|
4316
|
+
});
|
|
4317
|
+
}
|
|
4318
|
+
injectComponentStyle("transfer");
|
|
4319
|
+
injectComponentStyle("checkbox");
|
|
4320
|
+
//#endregion
|
|
4321
|
+
//#region src/components/tree-select/index.tsx
|
|
4322
|
+
const chevron = /* @__PURE__ */ jsx("svg", {
|
|
4323
|
+
viewBox: "0 0 24 24",
|
|
4324
|
+
fill: "none",
|
|
4325
|
+
stroke: "currentColor",
|
|
4326
|
+
strokeWidth: 2,
|
|
4327
|
+
strokeLinecap: "round",
|
|
4328
|
+
strokeLinejoin: "round",
|
|
4329
|
+
"aria-hidden": "true",
|
|
4330
|
+
children: /* @__PURE__ */ jsx("path", { d: "m9 18 6-6-6-6" })
|
|
4331
|
+
});
|
|
4332
|
+
const chevronDown = /* @__PURE__ */ jsx("span", {
|
|
4333
|
+
"data-part": "chevron",
|
|
4334
|
+
"aria-hidden": "true",
|
|
4335
|
+
children: chevron
|
|
4336
|
+
});
|
|
4337
|
+
function TreeSelectRow({ node, indexPath }) {
|
|
4338
|
+
return /* @__PURE__ */ jsx(TreeView$1.NodeProvider, {
|
|
4339
|
+
node,
|
|
4340
|
+
indexPath,
|
|
4341
|
+
children: node.children ? /* @__PURE__ */ jsxs(TreeView$1.Branch, { children: [/* @__PURE__ */ jsxs(TreeView$1.BranchControl, { children: [/* @__PURE__ */ jsx(TreeView$1.BranchIndicator, { children: chevron }), /* @__PURE__ */ jsx(TreeView$1.BranchText, { children: node.label })] }), /* @__PURE__ */ jsxs(TreeView$1.BranchContent, { children: [/* @__PURE__ */ jsx(TreeView$1.BranchIndentGuide, {}), node.children.map((child, index) => /* @__PURE__ */ jsx(TreeSelectRow, {
|
|
4342
|
+
node: child,
|
|
4343
|
+
indexPath: [...indexPath, index]
|
|
4344
|
+
}, child.value))] })] }) : /* @__PURE__ */ jsx(TreeView$1.Item, {
|
|
4345
|
+
asChild: true,
|
|
4346
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
4347
|
+
style: {
|
|
4348
|
+
display: "flex",
|
|
4349
|
+
inlineSize: "100%"
|
|
4350
|
+
},
|
|
4351
|
+
children: /* @__PURE__ */ jsx(TreeView$1.ItemText, { children: node.label })
|
|
4352
|
+
})
|
|
4353
|
+
})
|
|
4354
|
+
});
|
|
4355
|
+
}
|
|
4356
|
+
function TreeSelect({ value, data, placeholder = "Select…", filterable = false, disabled = false, onValueChange, ...rest }) {
|
|
4357
|
+
const [open, setOpen] = useState(false);
|
|
4358
|
+
const [query, setQuery] = useState("");
|
|
4359
|
+
const filterFns = useFilter({ sensitivity: "base" });
|
|
4360
|
+
const collection = useMemo(() => createTreeCollection$1({
|
|
4361
|
+
nodeToValue: (node) => node.value,
|
|
4362
|
+
nodeToString: (node) => node.label,
|
|
4363
|
+
rootNode: {
|
|
4364
|
+
value: "ROOT",
|
|
4365
|
+
label: "",
|
|
4366
|
+
children: data
|
|
4367
|
+
}
|
|
4368
|
+
}), [data]);
|
|
4369
|
+
const filtering = filterable && query.trim().length > 0;
|
|
4370
|
+
/** The collection pruned to the matches — each hit keeping its
|
|
4371
|
+
* ancestors, so a deep match still reads inside its hierarchy. */
|
|
4372
|
+
const visibleCollection = useMemo(() => filtering ? collection.filter((node) => filterFns.contains(node.label, query.trim())) : collection, [
|
|
4373
|
+
filtering,
|
|
4374
|
+
collection,
|
|
4375
|
+
filterFns,
|
|
4376
|
+
query
|
|
4377
|
+
]);
|
|
4378
|
+
const firstLevel = data.map((node) => node.value);
|
|
4379
|
+
const expandedWhileFiltering = useMemo(() => {
|
|
4380
|
+
if (!filtering) return void 0;
|
|
4381
|
+
const values = [];
|
|
4382
|
+
const walk = (nodes) => {
|
|
4383
|
+
for (const node of nodes) if (node.children?.length) {
|
|
4384
|
+
values.push(node.value);
|
|
4385
|
+
walk(node.children);
|
|
4386
|
+
}
|
|
4387
|
+
};
|
|
4388
|
+
walk(visibleCollection.rootNode.children ?? []);
|
|
4389
|
+
return values;
|
|
4390
|
+
}, [filtering, visibleCollection]);
|
|
4391
|
+
let found;
|
|
4392
|
+
const walk = (nodes) => {
|
|
4393
|
+
for (const node of nodes ?? []) {
|
|
4394
|
+
if (node.value === value) found = node.label;
|
|
4395
|
+
walk(node.children);
|
|
4396
|
+
}
|
|
4397
|
+
};
|
|
4398
|
+
walk(data);
|
|
4399
|
+
const label = found;
|
|
4400
|
+
function pick(details) {
|
|
4401
|
+
const [selected] = details.selectedValue;
|
|
4402
|
+
if (selected == null) return;
|
|
4403
|
+
onValueChange?.(selected);
|
|
4404
|
+
setOpen(false);
|
|
4405
|
+
}
|
|
4406
|
+
return /* @__PURE__ */ jsxs(Popover$1.Root, {
|
|
4407
|
+
...rest,
|
|
4408
|
+
open,
|
|
4409
|
+
onOpenChange: (details) => {
|
|
4410
|
+
setOpen(details.open);
|
|
4411
|
+
if (!details.open) setQuery("");
|
|
4412
|
+
},
|
|
4413
|
+
positioning: {
|
|
4414
|
+
sameWidth: true,
|
|
4415
|
+
placement: "bottom-start"
|
|
4416
|
+
},
|
|
4417
|
+
children: [/* @__PURE__ */ jsx(Popover$1.Trigger, {
|
|
4418
|
+
asChild: true,
|
|
4419
|
+
disabled,
|
|
4420
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
4421
|
+
type: "button",
|
|
4422
|
+
"data-scope": "tree-select",
|
|
4423
|
+
"data-part": "control",
|
|
4424
|
+
"data-open": open ? "" : void 0,
|
|
4425
|
+
"data-placeholder": label == null ? "" : void 0,
|
|
4426
|
+
disabled,
|
|
4427
|
+
children: [label ?? placeholder, chevronDown]
|
|
4428
|
+
})
|
|
4429
|
+
}), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { children: /* @__PURE__ */ jsxs(Popover$1.Content, {
|
|
4430
|
+
"data-scope": "tree-select",
|
|
4431
|
+
"data-part": "content",
|
|
4432
|
+
children: [filterable ? /* @__PURE__ */ jsx("div", {
|
|
4433
|
+
"data-scope": "tree-select",
|
|
4434
|
+
"data-part": "search",
|
|
4435
|
+
children: /* @__PURE__ */ jsx(Input, {
|
|
4436
|
+
size: "sm",
|
|
4437
|
+
value: query,
|
|
4438
|
+
onValueChange: setQuery,
|
|
4439
|
+
placeholder: "Filter…",
|
|
4440
|
+
"aria-label": "Filter options"
|
|
4441
|
+
})
|
|
4442
|
+
}) : null, /* @__PURE__ */ jsx("div", {
|
|
4443
|
+
"data-scope": "tree-select",
|
|
4444
|
+
"data-part": "body",
|
|
4445
|
+
children: (visibleCollection.rootNode.children ?? []).length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
4446
|
+
"data-scope": "tree-select",
|
|
4447
|
+
"data-part": "empty",
|
|
4448
|
+
children: "Nothing matches"
|
|
4449
|
+
}) : /* @__PURE__ */ jsx(TreeView$1.Root, {
|
|
4450
|
+
collection: visibleCollection,
|
|
4451
|
+
selectionMode: "single",
|
|
4452
|
+
selectedValue: value ? [value] : [],
|
|
4453
|
+
...filtering ? { expandedValue: expandedWhileFiltering } : { defaultExpandedValue: firstLevel },
|
|
4454
|
+
onSelectionChange: pick,
|
|
4455
|
+
children: /* @__PURE__ */ jsx(TreeView$1.Tree, { children: visibleCollection.rootNode.children?.map((node, index) => /* @__PURE__ */ jsx(TreeSelectRow, {
|
|
4456
|
+
node,
|
|
4457
|
+
indexPath: [index]
|
|
4458
|
+
}, node.value)) })
|
|
4459
|
+
})
|
|
4460
|
+
})]
|
|
4461
|
+
}) }) })]
|
|
4462
|
+
});
|
|
4463
|
+
}
|
|
4464
|
+
injectComponentStyle("tree-select");
|
|
4465
|
+
injectComponentStyle("tree-view");
|
|
4466
|
+
//#endregion
|
|
2019
4467
|
//#region src/components/tree-view/index.tsx
|
|
2020
4468
|
/** Ark's TreeView, dressed in the paper-and-ink system: quiet rows where
|
|
2021
4469
|
* selection is pure light on the paper, one hairline plumb line per depth,
|
|
@@ -2027,4 +4475,80 @@ injectComponentStyle("tour");
|
|
|
2027
4475
|
const TreeView = TreeView$1;
|
|
2028
4476
|
injectComponentStyle("tree-view");
|
|
2029
4477
|
//#endregion
|
|
2030
|
-
|
|
4478
|
+
//#region src/components/watermark/index.tsx
|
|
4479
|
+
/** Font-relative measures ride the root's own measure; absolute units
|
|
4480
|
+
* pass through untouched — the canvas needs a number, the prop may
|
|
4481
|
+
* speak in CSS. */
|
|
4482
|
+
function toPx(size) {
|
|
4483
|
+
const root = Number.parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
|
|
4484
|
+
const value = Number.parseFloat(size) || 0;
|
|
4485
|
+
if (size.includes("em")) return value * root;
|
|
4486
|
+
return value;
|
|
4487
|
+
}
|
|
4488
|
+
/**
|
|
4489
|
+
* The paper bears its seal beneath the content: a canvas draws the
|
|
4490
|
+
* text once into a tile (rotated, faint, crisp at the screen's pixel
|
|
4491
|
+
* density) and the marks layer repeats it over the children. The seal
|
|
4492
|
+
* redraws itself when a prop turns — and it never takes a pointer.
|
|
4493
|
+
*/
|
|
4494
|
+
function Watermark({ content, opacity = .06, rotate = -20, fontSize = "0.875rem", children, ...rest }) {
|
|
4495
|
+
const [root, setRoot] = useState(null);
|
|
4496
|
+
const [tile, setTile] = useState("");
|
|
4497
|
+
useEffect(() => {
|
|
4498
|
+
if (!root) return;
|
|
4499
|
+
const canvas = document.createElement("canvas");
|
|
4500
|
+
const paint = canvas.getContext("2d");
|
|
4501
|
+
if (!paint) return;
|
|
4502
|
+
const styles = getComputedStyle(root);
|
|
4503
|
+
const fontSizePx = toPx(fontSize);
|
|
4504
|
+
const font = `${styles.fontWeight} ${fontSizePx}px ${styles.fontFamily}`;
|
|
4505
|
+
paint.font = font;
|
|
4506
|
+
const textWidth = paint.measureText(content).width;
|
|
4507
|
+
const margin = fontSizePx;
|
|
4508
|
+
const boxWidth = Math.ceil(textWidth + margin * 2);
|
|
4509
|
+
const boxHeight = Math.ceil(fontSizePx + margin * 2);
|
|
4510
|
+
const radian = rotate * Math.PI / 180;
|
|
4511
|
+
const cos = Math.cos(radian);
|
|
4512
|
+
const sin = Math.sin(radian);
|
|
4513
|
+
const width = Math.ceil(Math.abs(boxWidth * cos) + Math.abs(boxHeight * sin));
|
|
4514
|
+
const height = Math.ceil(Math.abs(boxWidth * sin) + Math.abs(boxHeight * cos));
|
|
4515
|
+
const dpr = window.devicePixelRatio || 1;
|
|
4516
|
+
canvas.width = width * dpr;
|
|
4517
|
+
canvas.height = height * dpr;
|
|
4518
|
+
paint.scale(dpr, dpr);
|
|
4519
|
+
paint.translate(width / 2, height / 2);
|
|
4520
|
+
paint.rotate(radian);
|
|
4521
|
+
paint.globalAlpha = opacity;
|
|
4522
|
+
paint.font = font;
|
|
4523
|
+
paint.textAlign = "center";
|
|
4524
|
+
paint.textBaseline = "middle";
|
|
4525
|
+
paint.fillStyle = styles.getPropertyValue("--bs-color-text-tertiary").trim() || styles.color;
|
|
4526
|
+
paint.fillText(content, 0, 0);
|
|
4527
|
+
setTile(`url(${canvas.toDataURL()})`);
|
|
4528
|
+
}, [
|
|
4529
|
+
root,
|
|
4530
|
+
content,
|
|
4531
|
+
opacity,
|
|
4532
|
+
rotate,
|
|
4533
|
+
fontSize
|
|
4534
|
+
]);
|
|
4535
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
4536
|
+
...rest,
|
|
4537
|
+
ref: setRoot,
|
|
4538
|
+
"data-scope": "watermark",
|
|
4539
|
+
"data-part": "root",
|
|
4540
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
4541
|
+
"data-scope": "watermark",
|
|
4542
|
+
"data-part": "content",
|
|
4543
|
+
children
|
|
4544
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
4545
|
+
"data-scope": "watermark",
|
|
4546
|
+
"data-part": "marks",
|
|
4547
|
+
"aria-hidden": "true",
|
|
4548
|
+
style: { backgroundImage: tile }
|
|
4549
|
+
})]
|
|
4550
|
+
});
|
|
4551
|
+
}
|
|
4552
|
+
injectComponentStyle("watermark");
|
|
4553
|
+
//#endregion
|
|
4554
|
+
export { Accordion, Action, Action as AiAction, Affix, Ai, Attachment as AiAttachment, Attachment, Attachments as AiAttachments, Attachments, Message as AiMessage, Message, PromptInput as AiPromptInput, PromptInput, Reasoning as AiReasoning, Reasoning, Response as AiResponse, Response, Source as AiSource, Source, Sources as AiSources, Sources, Suggestion as AiSuggestion, Suggestion, Tool as AiTool, Tool, Alert, AngleSlider, AspectRatio, AutoComplete, Avatar, AvatarGroup, BackTop, Badge, Banner, Breadcrumb, Button, ButtonGroup, Calendar, Card, Carousel, CascadeSelect, Checkbox, CheckboxGroup, Chip, ClientOnly, Clipboard, Collapsible, ColorPicker, Combobox, Command, Comment, ConfigProvider, Container, DataTable, DateInput, DatePicker, Descriptions, DescriptionsRoot, Dialog, DownloadTrigger, Drawer, DynamicInput, Editable, Ellipsis, Empty, FLOAT_BUTTON_CONTEXT, Field, Fieldset, FileUpload, FlexRender, FloatButton, FloatingPanel, FocusTrap, Form, FormField, Format, Frame, Grid, Highlight, HoverCard, Icon, Image, ImageCropper, ImageViewer, Input, InputGroup, JsonTreeView, Kbd, Layout, Link, List, Listbox, Marquee, Masonry, Mentions, MentionsVessel, Menu, Menubar, Meter, NavigationMenu, NumberInput, PageHeader, Pagination, PasswordInput, PinInput, Popconfirm, Popover, Presence, PresenceProvider, Progress, ProgressGroup, QrCode, RadioGroup, RatingGroup, ScrollArea, SegmentGroup, Select, Separator, SignaturePad, Skeleton, Slider, Spinner, SplitButton, Splitter, Stack, Stat, Steps, Swap, Switch, Tabs, TagsInput, Textarea, Timeline, Timer, Toast, Toaster, Toc, Toggle, ToggleGroup, Tooltip, Tour, Transfer, TreeSelect, TreeView, Typography, Watermark, createColumnHelper, createFileTreeCollection, createGridCollection, createToaster, createTreeCollection, useAsyncList, useConfig, useDownload, useListCollection, useListSelection, useMentions, usePresence, usePresenceContext, useTour };
|