@fictjs/ui-primitives 0.1.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/LICENSE +21 -0
- package/README.md +181 -0
- package/dist/index.cjs +5091 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1123 -0
- package/dist/index.d.ts +1123 -0
- package/dist/index.js +4907 -0
- package/dist/index.js.map +1 -0
- package/docs/README.md +39 -0
- package/docs/accessibility.md +50 -0
- package/docs/api-reference.md +200 -0
- package/docs/architecture.md +113 -0
- package/docs/components/core/accessible-icon.md +23 -0
- package/docs/components/core/id.md +26 -0
- package/docs/components/core/portal.md +30 -0
- package/docs/components/core/presence.md +27 -0
- package/docs/components/core/primitive.md +22 -0
- package/docs/components/core/separator.md +25 -0
- package/docs/components/core/slot.md +25 -0
- package/docs/components/core/visually-hidden.md +21 -0
- package/docs/components/disclosure/accordion.md +33 -0
- package/docs/components/disclosure/collapsible.md +29 -0
- package/docs/components/disclosure/navigation-menu.md +43 -0
- package/docs/components/disclosure/tabs.md +35 -0
- package/docs/components/feedback/toast.md +60 -0
- package/docs/components/form/calendar.md +35 -0
- package/docs/components/form/controls.md +52 -0
- package/docs/components/form/date-picker.md +44 -0
- package/docs/components/form/form-field.md +39 -0
- package/docs/components/form/inputs.md +99 -0
- package/docs/components/interaction/dismissable-layer.md +28 -0
- package/docs/components/interaction/focus-scope.md +27 -0
- package/docs/components/interaction/live-region.md +26 -0
- package/docs/components/interaction/popper.md +30 -0
- package/docs/components/interaction/roving-focus.md +27 -0
- package/docs/components/interaction/scroll-lock.md +22 -0
- package/docs/components/layout/layout.md +61 -0
- package/docs/components/menu/context-menu.md +44 -0
- package/docs/components/menu/dropdown-menu.md +62 -0
- package/docs/components/menu/menubar.md +38 -0
- package/docs/components/overlay/alert-dialog.md +46 -0
- package/docs/components/overlay/command-palette.md +54 -0
- package/docs/components/overlay/dialog.md +69 -0
- package/docs/components/overlay/hover-card.md +25 -0
- package/docs/components/overlay/popover.md +36 -0
- package/docs/components/overlay/tooltip.md +28 -0
- package/docs/examples.md +155 -0
- package/docs/release.md +60 -0
- package/docs/testing.md +36 -0
- package/package.json +89 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4907 @@
|
|
|
1
|
+
// src/internal/event.ts
|
|
2
|
+
function composeEventHandlers(original, extra, options = { checkDefaultPrevented: true }) {
|
|
3
|
+
return (event) => {
|
|
4
|
+
original?.(event);
|
|
5
|
+
if (!options.checkDefaultPrevented || !event.defaultPrevented) {
|
|
6
|
+
extra?.(event);
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/internal/ref.ts
|
|
12
|
+
function assignRef(ref, value) {
|
|
13
|
+
if (!ref) return;
|
|
14
|
+
if (typeof ref === "function") {
|
|
15
|
+
ref(value);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
ref.current = value;
|
|
19
|
+
}
|
|
20
|
+
function composeRefs(...refs) {
|
|
21
|
+
return (node) => {
|
|
22
|
+
for (const ref of refs) {
|
|
23
|
+
assignRef(ref, node);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/internal/vnode.ts
|
|
29
|
+
function isEventKey(key) {
|
|
30
|
+
return key.startsWith("on") && key.length > 2;
|
|
31
|
+
}
|
|
32
|
+
function mergeClassName(a, b) {
|
|
33
|
+
if (!a) return b;
|
|
34
|
+
if (!b) return a;
|
|
35
|
+
return `${String(a)} ${String(b)}`;
|
|
36
|
+
}
|
|
37
|
+
function mergeStyle(a, b) {
|
|
38
|
+
if (!a) return b;
|
|
39
|
+
if (!b) return a;
|
|
40
|
+
if (typeof a === "string" || typeof b === "string") {
|
|
41
|
+
return `${String(a)}; ${String(b)}`;
|
|
42
|
+
}
|
|
43
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
44
|
+
return { ...a, ...b };
|
|
45
|
+
}
|
|
46
|
+
return b;
|
|
47
|
+
}
|
|
48
|
+
function mergePropValue(key, targetValue, sourceValue) {
|
|
49
|
+
if (key === "class" || key === "className") {
|
|
50
|
+
return mergeClassName(targetValue, sourceValue);
|
|
51
|
+
}
|
|
52
|
+
if (key === "style") {
|
|
53
|
+
return mergeStyle(targetValue, sourceValue);
|
|
54
|
+
}
|
|
55
|
+
if (key === "ref") {
|
|
56
|
+
return composeRefs(
|
|
57
|
+
targetValue,
|
|
58
|
+
sourceValue
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
if (isEventKey(key)) {
|
|
62
|
+
if (typeof targetValue === "function" || typeof sourceValue === "function") {
|
|
63
|
+
return composeEventHandlers(
|
|
64
|
+
targetValue,
|
|
65
|
+
sourceValue
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return sourceValue;
|
|
70
|
+
}
|
|
71
|
+
function isVNodeLike(value) {
|
|
72
|
+
return typeof value === "object" && value !== null && "type" in value && "props" in value && !(value instanceof Node);
|
|
73
|
+
}
|
|
74
|
+
function mergeVNodeProps(target, source) {
|
|
75
|
+
const next = { ...target ?? {} };
|
|
76
|
+
for (const [key, sourceValue] of Object.entries(source)) {
|
|
77
|
+
if (key === "children" && sourceValue === void 0) continue;
|
|
78
|
+
const targetValue = next[key];
|
|
79
|
+
next[key] = mergePropValue(key, targetValue, sourceValue);
|
|
80
|
+
}
|
|
81
|
+
return next;
|
|
82
|
+
}
|
|
83
|
+
function cloneVNodeWithProps(vnode, props, children) {
|
|
84
|
+
const merged = mergeVNodeProps(vnode.props, props);
|
|
85
|
+
if (children !== void 0) {
|
|
86
|
+
merged.children = children;
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
type: vnode.type,
|
|
90
|
+
props: merged,
|
|
91
|
+
key: vnode.key
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// src/components/core/slot.ts
|
|
96
|
+
function getFirstChild(children) {
|
|
97
|
+
if (Array.isArray(children)) {
|
|
98
|
+
return children.find((child) => child !== null && child !== void 0 && child !== false) ?? null;
|
|
99
|
+
}
|
|
100
|
+
return children ?? null;
|
|
101
|
+
}
|
|
102
|
+
function Slot(props) {
|
|
103
|
+
const { children, ...slotProps } = props;
|
|
104
|
+
if (children === void 0 || children === null) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
const child = getFirstChild(children);
|
|
108
|
+
if (!child) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (!isVNodeLike(child)) {
|
|
112
|
+
return child;
|
|
113
|
+
}
|
|
114
|
+
return cloneVNodeWithProps(child, slotProps);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// src/components/core/primitive.ts
|
|
118
|
+
function Primitive(props) {
|
|
119
|
+
const { as = "div", asChild = false, children, ...rest } = props;
|
|
120
|
+
if (asChild) {
|
|
121
|
+
return Slot({ ...rest, children });
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
type: as,
|
|
125
|
+
props: {
|
|
126
|
+
...rest,
|
|
127
|
+
children
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function createPrimitive(tag) {
|
|
132
|
+
return (props) => Primitive({ ...props, as: tag });
|
|
133
|
+
}
|
|
134
|
+
var PrimitiveElements = {
|
|
135
|
+
div: createPrimitive("div"),
|
|
136
|
+
span: createPrimitive("span"),
|
|
137
|
+
button: createPrimitive("button"),
|
|
138
|
+
input: createPrimitive("input"),
|
|
139
|
+
label: createPrimitive("label"),
|
|
140
|
+
form: createPrimitive("form"),
|
|
141
|
+
ul: createPrimitive("ul"),
|
|
142
|
+
li: createPrimitive("li"),
|
|
143
|
+
nav: createPrimitive("nav"),
|
|
144
|
+
section: createPrimitive("section"),
|
|
145
|
+
article: createPrimitive("article")
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// src/internal/accessor.ts
|
|
149
|
+
function read(value, fallback) {
|
|
150
|
+
if (typeof value === "function") {
|
|
151
|
+
return value();
|
|
152
|
+
}
|
|
153
|
+
if (value === void 0) {
|
|
154
|
+
return fallback;
|
|
155
|
+
}
|
|
156
|
+
return value;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/components/core/presence.ts
|
|
160
|
+
function Presence(props) {
|
|
161
|
+
return () => {
|
|
162
|
+
const present = read(props.present, true);
|
|
163
|
+
const forceMount = read(props.forceMount, false);
|
|
164
|
+
if (!present && !forceMount) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
if (typeof props.children === "function") {
|
|
168
|
+
return props.children({ present });
|
|
169
|
+
}
|
|
170
|
+
return props.children ?? null;
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/components/core/portal.ts
|
|
175
|
+
import {
|
|
176
|
+
createContext,
|
|
177
|
+
createPortal,
|
|
178
|
+
createElement,
|
|
179
|
+
useContext,
|
|
180
|
+
createMemo,
|
|
181
|
+
onMount
|
|
182
|
+
} from "@fictjs/runtime";
|
|
183
|
+
var PortalContainerContext = createContext(
|
|
184
|
+
() => typeof document !== "undefined" ? document.body : null
|
|
185
|
+
);
|
|
186
|
+
function resolveContainer(container) {
|
|
187
|
+
if (container === void 0) {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
return read(container, null) ?? null;
|
|
191
|
+
}
|
|
192
|
+
function PortalHost(props) {
|
|
193
|
+
const container = createMemo(() => resolveContainer(props.container));
|
|
194
|
+
return {
|
|
195
|
+
type: PortalContainerContext.Provider,
|
|
196
|
+
props: {
|
|
197
|
+
value: container,
|
|
198
|
+
children: props.children
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function Portal(props) {
|
|
203
|
+
if (read(props.disabled, false)) {
|
|
204
|
+
return props.children ?? null;
|
|
205
|
+
}
|
|
206
|
+
const hostContainerAccessor = useContext(PortalContainerContext);
|
|
207
|
+
onMount(() => {
|
|
208
|
+
const container = (props.container !== void 0 ? resolveContainer(props.container) : hostContainerAccessor()) ?? (typeof document !== "undefined" ? document.body : null);
|
|
209
|
+
if (!container) return;
|
|
210
|
+
createPortal(
|
|
211
|
+
container,
|
|
212
|
+
() => typeof props.children === "function" ? props.children() : props.children ?? null,
|
|
213
|
+
createElement
|
|
214
|
+
);
|
|
215
|
+
});
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/components/core/visually-hidden.ts
|
|
220
|
+
var visuallyHiddenStyle = {
|
|
221
|
+
position: "absolute",
|
|
222
|
+
border: "0",
|
|
223
|
+
width: "1px",
|
|
224
|
+
height: "1px",
|
|
225
|
+
padding: "0",
|
|
226
|
+
margin: "-1px",
|
|
227
|
+
overflow: "hidden",
|
|
228
|
+
clip: "rect(0, 0, 0, 0)",
|
|
229
|
+
whiteSpace: "nowrap"
|
|
230
|
+
};
|
|
231
|
+
function VisuallyHidden(props) {
|
|
232
|
+
const { as = "span", children, style, ...rest } = props;
|
|
233
|
+
return {
|
|
234
|
+
type: as,
|
|
235
|
+
props: {
|
|
236
|
+
...rest,
|
|
237
|
+
style: typeof style === "object" && style !== null ? { ...visuallyHiddenStyle, ...style } : visuallyHiddenStyle,
|
|
238
|
+
children
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/components/core/separator.ts
|
|
244
|
+
function Separator(props) {
|
|
245
|
+
const orientation = read(props.orientation, "horizontal");
|
|
246
|
+
const decorative = read(props.decorative, false);
|
|
247
|
+
const tag = props.as ?? "div";
|
|
248
|
+
return {
|
|
249
|
+
type: tag,
|
|
250
|
+
props: {
|
|
251
|
+
...props,
|
|
252
|
+
as: void 0,
|
|
253
|
+
role: decorative ? "presentation" : "separator",
|
|
254
|
+
"aria-orientation": orientation,
|
|
255
|
+
"aria-hidden": decorative || void 0,
|
|
256
|
+
"data-orientation": orientation
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// src/components/core/accessible-icon.ts
|
|
262
|
+
function AccessibleIcon(props) {
|
|
263
|
+
const { label, children, ...rest } = props;
|
|
264
|
+
return {
|
|
265
|
+
type: "span",
|
|
266
|
+
props: {
|
|
267
|
+
...rest,
|
|
268
|
+
children: [
|
|
269
|
+
{
|
|
270
|
+
type: "span",
|
|
271
|
+
props: {
|
|
272
|
+
"aria-hidden": "true",
|
|
273
|
+
children
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
type: VisuallyHidden,
|
|
278
|
+
props: {
|
|
279
|
+
children: label
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
]
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/internal/ids.ts
|
|
288
|
+
import { createContext as createContext2, hasContext, useContext as useContext2 } from "@fictjs/runtime";
|
|
289
|
+
var IdContext = createContext2(null);
|
|
290
|
+
var globalCounters = /* @__PURE__ */ new Map();
|
|
291
|
+
function nextCounter(prefix) {
|
|
292
|
+
const current = globalCounters.get(prefix) ?? 0;
|
|
293
|
+
const next = current + 1;
|
|
294
|
+
globalCounters.set(prefix, next);
|
|
295
|
+
return next;
|
|
296
|
+
}
|
|
297
|
+
function tryReadIdContext() {
|
|
298
|
+
try {
|
|
299
|
+
if (!hasContext(IdContext)) {
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
return useContext2(IdContext);
|
|
303
|
+
} catch {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function createId(prefix = "fict-ui") {
|
|
308
|
+
return `${prefix}-${nextCounter(prefix)}`;
|
|
309
|
+
}
|
|
310
|
+
function useId(id, prefix = "fict-ui") {
|
|
311
|
+
if (id) return id;
|
|
312
|
+
const context = tryReadIdContext();
|
|
313
|
+
if (context) {
|
|
314
|
+
return `${context.prefix}-${context.next()}`;
|
|
315
|
+
}
|
|
316
|
+
return createId(prefix);
|
|
317
|
+
}
|
|
318
|
+
function IdProvider(props) {
|
|
319
|
+
const parent = tryReadIdContext();
|
|
320
|
+
let localCounter = 0;
|
|
321
|
+
const prefix = props.prefix ?? parent?.prefix ?? "fict-ui";
|
|
322
|
+
const context = {
|
|
323
|
+
prefix,
|
|
324
|
+
next: () => {
|
|
325
|
+
localCounter += 1;
|
|
326
|
+
return localCounter;
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
return {
|
|
330
|
+
type: IdContext.Provider,
|
|
331
|
+
props: {
|
|
332
|
+
value: context,
|
|
333
|
+
children: props.children
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// src/components/disclosure/collapsible.ts
|
|
339
|
+
import { createContext as createContext3, useContext as useContext3 } from "@fictjs/runtime";
|
|
340
|
+
|
|
341
|
+
// src/internal/state.ts
|
|
342
|
+
import { createSignal } from "@fictjs/runtime/advanced";
|
|
343
|
+
function createControllableState(options) {
|
|
344
|
+
const uncontrolled = createSignal(options.defaultValue);
|
|
345
|
+
const isControlled = () => options.value !== void 0;
|
|
346
|
+
const get = () => {
|
|
347
|
+
if (isControlled()) {
|
|
348
|
+
return read(options.value, options.defaultValue);
|
|
349
|
+
}
|
|
350
|
+
return uncontrolled();
|
|
351
|
+
};
|
|
352
|
+
const set = (next) => {
|
|
353
|
+
const prev = get();
|
|
354
|
+
const equals = options.equals ?? Object.is;
|
|
355
|
+
if (equals(prev, next)) return;
|
|
356
|
+
if (!isControlled()) {
|
|
357
|
+
uncontrolled(next);
|
|
358
|
+
}
|
|
359
|
+
options.onChange?.(next);
|
|
360
|
+
};
|
|
361
|
+
return { get, set, isControlled };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// src/components/disclosure/collapsible.ts
|
|
365
|
+
var CollapsibleContext = createContext3(null);
|
|
366
|
+
function useCollapsibleContext(component) {
|
|
367
|
+
const context = useContext3(CollapsibleContext);
|
|
368
|
+
if (!context) {
|
|
369
|
+
throw new Error(`${component} must be used inside CollapsibleRoot`);
|
|
370
|
+
}
|
|
371
|
+
return context;
|
|
372
|
+
}
|
|
373
|
+
function CollapsibleRoot(props) {
|
|
374
|
+
const state = createControllableState({
|
|
375
|
+
value: props.open,
|
|
376
|
+
defaultValue: props.defaultOpen ?? false,
|
|
377
|
+
onChange: props.onOpenChange
|
|
378
|
+
});
|
|
379
|
+
const context = {
|
|
380
|
+
open: () => state.get(),
|
|
381
|
+
setOpen: (open) => state.set(open),
|
|
382
|
+
disabled: () => props.disabled ?? false
|
|
383
|
+
};
|
|
384
|
+
return {
|
|
385
|
+
type: CollapsibleContext.Provider,
|
|
386
|
+
props: {
|
|
387
|
+
value: context,
|
|
388
|
+
children: props.children
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function CollapsibleTrigger(props) {
|
|
393
|
+
const context = useCollapsibleContext("CollapsibleTrigger");
|
|
394
|
+
const tag = props.as ?? "button";
|
|
395
|
+
return Primitive({
|
|
396
|
+
...props,
|
|
397
|
+
as: tag,
|
|
398
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
399
|
+
disabled: () => context.disabled(),
|
|
400
|
+
"aria-expanded": () => context.open(),
|
|
401
|
+
"data-state": () => context.open() ? "open" : "closed",
|
|
402
|
+
onClick: (event) => {
|
|
403
|
+
;
|
|
404
|
+
props.onClick?.(event);
|
|
405
|
+
if (event.defaultPrevented) return;
|
|
406
|
+
if (context.disabled()) return;
|
|
407
|
+
context.setOpen(!context.open());
|
|
408
|
+
},
|
|
409
|
+
children: props.children
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
function CollapsibleContent(props) {
|
|
413
|
+
const context = useCollapsibleContext("CollapsibleContent");
|
|
414
|
+
return {
|
|
415
|
+
type: "div",
|
|
416
|
+
props: {
|
|
417
|
+
"data-collapsible-content-wrapper": "",
|
|
418
|
+
children: () => context.open() || props.forceMount ? {
|
|
419
|
+
type: "div",
|
|
420
|
+
props: {
|
|
421
|
+
...props,
|
|
422
|
+
forceMount: void 0,
|
|
423
|
+
"data-state": () => context.open() ? "open" : "closed",
|
|
424
|
+
"data-collapsible-content": "",
|
|
425
|
+
children: props.children
|
|
426
|
+
}
|
|
427
|
+
} : null
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/components/disclosure/accordion.ts
|
|
433
|
+
import { createContext as createContext4, useContext as useContext4 } from "@fictjs/runtime";
|
|
434
|
+
var AccordionContext = createContext4(null);
|
|
435
|
+
var AccordionItemContext = createContext4(null);
|
|
436
|
+
function useAccordionContext(component) {
|
|
437
|
+
const context = useContext4(AccordionContext);
|
|
438
|
+
if (!context) {
|
|
439
|
+
throw new Error(`${component} must be used inside AccordionRoot`);
|
|
440
|
+
}
|
|
441
|
+
return context;
|
|
442
|
+
}
|
|
443
|
+
function useAccordionItemContext(component) {
|
|
444
|
+
const context = useContext4(AccordionItemContext);
|
|
445
|
+
if (!context) {
|
|
446
|
+
throw new Error(`${component} must be used inside AccordionItem`);
|
|
447
|
+
}
|
|
448
|
+
return context;
|
|
449
|
+
}
|
|
450
|
+
function normalize(value) {
|
|
451
|
+
if (value === void 0) return [];
|
|
452
|
+
return Array.isArray(value) ? value : [value];
|
|
453
|
+
}
|
|
454
|
+
function AccordionRoot(props) {
|
|
455
|
+
const type = props.type ?? "single";
|
|
456
|
+
const initialValues = normalize(props.defaultValue);
|
|
457
|
+
const valuesState = createControllableState({
|
|
458
|
+
value: props.value === void 0 ? void 0 : typeof props.value === "function" ? (() => normalize(props.value())) : normalize(props.value),
|
|
459
|
+
defaultValue: initialValues,
|
|
460
|
+
onChange: (next) => {
|
|
461
|
+
props.onValueChange?.(type === "single" ? next[0] ?? "" : next);
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
const context = {
|
|
465
|
+
type: () => type,
|
|
466
|
+
values: () => valuesState.get(),
|
|
467
|
+
toggle: (value) => {
|
|
468
|
+
const current = valuesState.get();
|
|
469
|
+
const exists = current.includes(value);
|
|
470
|
+
if (type === "single") {
|
|
471
|
+
if (exists) {
|
|
472
|
+
if (props.collapsible ?? false) {
|
|
473
|
+
valuesState.set([]);
|
|
474
|
+
}
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
valuesState.set([value]);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (exists) {
|
|
481
|
+
valuesState.set(current.filter((item) => item !== value));
|
|
482
|
+
} else {
|
|
483
|
+
valuesState.set([...current, value]);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
return {
|
|
488
|
+
type: AccordionContext.Provider,
|
|
489
|
+
props: {
|
|
490
|
+
value: context,
|
|
491
|
+
children: props.children
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
function AccordionItem(props) {
|
|
496
|
+
const accordion = useAccordionContext("AccordionItem");
|
|
497
|
+
const tag = props.as ?? "div";
|
|
498
|
+
const itemContext = {
|
|
499
|
+
value: props.value,
|
|
500
|
+
open: () => accordion.values().includes(props.value)
|
|
501
|
+
};
|
|
502
|
+
return {
|
|
503
|
+
type: AccordionItemContext.Provider,
|
|
504
|
+
props: {
|
|
505
|
+
value: itemContext,
|
|
506
|
+
children: {
|
|
507
|
+
type: CollapsibleRoot,
|
|
508
|
+
props: {
|
|
509
|
+
open: () => itemContext.open(),
|
|
510
|
+
onOpenChange: (open) => {
|
|
511
|
+
const shouldOpen = open;
|
|
512
|
+
if (shouldOpen !== itemContext.open()) {
|
|
513
|
+
accordion.toggle(props.value);
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
children: Primitive({
|
|
517
|
+
...props,
|
|
518
|
+
value: void 0,
|
|
519
|
+
as: tag,
|
|
520
|
+
"data-accordion-item": props.value,
|
|
521
|
+
"data-state": () => itemContext.open() ? "open" : "closed",
|
|
522
|
+
children: props.children
|
|
523
|
+
})
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
function AccordionTrigger(props) {
|
|
530
|
+
useAccordionContext("AccordionTrigger");
|
|
531
|
+
useAccordionItemContext("AccordionTrigger");
|
|
532
|
+
return {
|
|
533
|
+
type: CollapsibleTrigger,
|
|
534
|
+
props
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
function AccordionContent(props) {
|
|
538
|
+
return {
|
|
539
|
+
type: CollapsibleContent,
|
|
540
|
+
props
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// src/components/disclosure/tabs.ts
|
|
545
|
+
import { createContext as createContext6, useContext as useContext6 } from "@fictjs/runtime";
|
|
546
|
+
|
|
547
|
+
// src/components/interaction/roving-focus.ts
|
|
548
|
+
import { createContext as createContext5, createEffect, onCleanup, useContext as useContext5 } from "@fictjs/runtime";
|
|
549
|
+
import { createSignal as createSignal2 } from "@fictjs/runtime/advanced";
|
|
550
|
+
import { useEventListener } from "@fictjs/hooks";
|
|
551
|
+
var RovingContext = createContext5(null);
|
|
552
|
+
function RovingFocusGroup(props) {
|
|
553
|
+
const currentIdSignal = createSignal2(null);
|
|
554
|
+
const items = [];
|
|
555
|
+
const groupNode = createSignal2(null);
|
|
556
|
+
const syncDomState = (activeId) => {
|
|
557
|
+
for (const item of items) {
|
|
558
|
+
const isActive = item.id === activeId && !item.disabled;
|
|
559
|
+
item.element.tabIndex = isActive ? 0 : -1;
|
|
560
|
+
if (isActive) {
|
|
561
|
+
item.element.setAttribute("data-active", "");
|
|
562
|
+
} else {
|
|
563
|
+
item.element.removeAttribute("data-active");
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
const moveFocus = (direction) => {
|
|
568
|
+
const activeItems = context.getItems().filter((item) => !item.disabled);
|
|
569
|
+
if (activeItems.length === 0) return;
|
|
570
|
+
const currentId = context.currentId();
|
|
571
|
+
const currentIndex = activeItems.findIndex((item) => item.id === currentId);
|
|
572
|
+
const baseIndex = currentIndex >= 0 ? currentIndex : 0;
|
|
573
|
+
let nextIndex = baseIndex + direction;
|
|
574
|
+
if (nextIndex < 0) {
|
|
575
|
+
nextIndex = context.loop() ? activeItems.length - 1 : 0;
|
|
576
|
+
} else if (nextIndex >= activeItems.length) {
|
|
577
|
+
nextIndex = context.loop() ? 0 : activeItems.length - 1;
|
|
578
|
+
}
|
|
579
|
+
const nextItem = activeItems[nextIndex];
|
|
580
|
+
if (!nextItem) return;
|
|
581
|
+
context.setCurrentId(nextItem.id);
|
|
582
|
+
nextItem.element.focus();
|
|
583
|
+
};
|
|
584
|
+
const focusFirst2 = () => {
|
|
585
|
+
const first = context.getItems().find((item) => !item.disabled);
|
|
586
|
+
if (!first) return;
|
|
587
|
+
context.setCurrentId(first.id);
|
|
588
|
+
first.element.focus();
|
|
589
|
+
};
|
|
590
|
+
const focusLast = () => {
|
|
591
|
+
const reversed = context.getItems().slice().reverse();
|
|
592
|
+
const last = reversed.find((item) => !item.disabled);
|
|
593
|
+
if (!last) return;
|
|
594
|
+
context.setCurrentId(last.id);
|
|
595
|
+
last.element.focus();
|
|
596
|
+
};
|
|
597
|
+
const context = {
|
|
598
|
+
orientation: () => read(props.orientation, "horizontal"),
|
|
599
|
+
loop: () => read(props.loop, true),
|
|
600
|
+
currentId: () => currentIdSignal(),
|
|
601
|
+
setCurrentId: (id) => {
|
|
602
|
+
currentIdSignal(id);
|
|
603
|
+
syncDomState(id);
|
|
604
|
+
},
|
|
605
|
+
register: (item) => {
|
|
606
|
+
items.push(item);
|
|
607
|
+
if (!currentIdSignal() && !item.disabled) {
|
|
608
|
+
context.setCurrentId(item.id);
|
|
609
|
+
} else {
|
|
610
|
+
syncDomState(currentIdSignal());
|
|
611
|
+
}
|
|
612
|
+
return () => {
|
|
613
|
+
const index = items.findIndex((entry) => entry.id === item.id);
|
|
614
|
+
if (index >= 0) {
|
|
615
|
+
items.splice(index, 1);
|
|
616
|
+
}
|
|
617
|
+
if (currentIdSignal() === item.id) {
|
|
618
|
+
context.setCurrentId(items.find((entry) => !entry.disabled)?.id ?? null);
|
|
619
|
+
} else {
|
|
620
|
+
syncDomState(currentIdSignal());
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
},
|
|
624
|
+
getItems: () => items.slice(),
|
|
625
|
+
moveFocus,
|
|
626
|
+
focusFirst: focusFirst2,
|
|
627
|
+
focusLast
|
|
628
|
+
};
|
|
629
|
+
const onKeyDown = (event) => {
|
|
630
|
+
const orientation = context.orientation();
|
|
631
|
+
const key = event.key;
|
|
632
|
+
if (orientation === "horizontal") {
|
|
633
|
+
if (key === "ArrowRight") {
|
|
634
|
+
event.preventDefault();
|
|
635
|
+
moveFocus(1);
|
|
636
|
+
} else if (key === "ArrowLeft") {
|
|
637
|
+
event.preventDefault();
|
|
638
|
+
moveFocus(-1);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
if (orientation === "vertical") {
|
|
642
|
+
if (key === "ArrowDown") {
|
|
643
|
+
event.preventDefault();
|
|
644
|
+
moveFocus(1);
|
|
645
|
+
} else if (key === "ArrowUp") {
|
|
646
|
+
event.preventDefault();
|
|
647
|
+
moveFocus(-1);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (key === "Home") {
|
|
651
|
+
event.preventDefault();
|
|
652
|
+
context.focusFirst();
|
|
653
|
+
}
|
|
654
|
+
if (key === "End") {
|
|
655
|
+
event.preventDefault();
|
|
656
|
+
context.focusLast();
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
const onDocumentKeyDown = (event) => {
|
|
660
|
+
const target = event.target;
|
|
661
|
+
const group = groupNode();
|
|
662
|
+
if (!group || !(target instanceof Node)) return;
|
|
663
|
+
if (!group.contains(target)) return;
|
|
664
|
+
onKeyDown(event);
|
|
665
|
+
};
|
|
666
|
+
const targetDocument = () => groupNode()?.ownerDocument ?? (typeof document !== "undefined" ? document : null);
|
|
667
|
+
useEventListener(targetDocument, "keydown", onDocumentKeyDown, { capture: true });
|
|
668
|
+
return {
|
|
669
|
+
type: RovingContext.Provider,
|
|
670
|
+
props: {
|
|
671
|
+
value: context,
|
|
672
|
+
children: {
|
|
673
|
+
type: "div",
|
|
674
|
+
props: {
|
|
675
|
+
...props,
|
|
676
|
+
orientation: void 0,
|
|
677
|
+
loop: void 0,
|
|
678
|
+
role: "group",
|
|
679
|
+
"data-roving-focus-group": "",
|
|
680
|
+
ref: (node) => {
|
|
681
|
+
groupNode(node);
|
|
682
|
+
const refProp = props.ref;
|
|
683
|
+
if (typeof refProp === "function") {
|
|
684
|
+
refProp(node);
|
|
685
|
+
} else if (refProp) {
|
|
686
|
+
refProp.current = node;
|
|
687
|
+
}
|
|
688
|
+
},
|
|
689
|
+
onKeyDown,
|
|
690
|
+
children: props.children
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
function RovingFocusItem(props) {
|
|
697
|
+
const context = useContext5(RovingContext);
|
|
698
|
+
if (!context) {
|
|
699
|
+
throw new Error("RovingFocusItem must be used inside RovingFocusGroup");
|
|
700
|
+
}
|
|
701
|
+
const id = useId(props.id, "rf-item");
|
|
702
|
+
const tag = props.as ?? "button";
|
|
703
|
+
const itemNode = createSignal2(null);
|
|
704
|
+
let cleanup = null;
|
|
705
|
+
const onFocus = (event) => {
|
|
706
|
+
props.onFocus?.(event);
|
|
707
|
+
context.setCurrentId(id);
|
|
708
|
+
};
|
|
709
|
+
const onKeyDown = (event) => {
|
|
710
|
+
props.onKeyDown?.(event);
|
|
711
|
+
if (event.defaultPrevented) return;
|
|
712
|
+
const orientation = context.orientation();
|
|
713
|
+
if (orientation === "horizontal") {
|
|
714
|
+
if (event.key === "ArrowRight") {
|
|
715
|
+
event.preventDefault();
|
|
716
|
+
context.moveFocus(1);
|
|
717
|
+
} else if (event.key === "ArrowLeft") {
|
|
718
|
+
event.preventDefault();
|
|
719
|
+
context.moveFocus(-1);
|
|
720
|
+
}
|
|
721
|
+
} else if (orientation === "vertical") {
|
|
722
|
+
if (event.key === "ArrowDown") {
|
|
723
|
+
event.preventDefault();
|
|
724
|
+
context.moveFocus(1);
|
|
725
|
+
} else if (event.key === "ArrowUp") {
|
|
726
|
+
event.preventDefault();
|
|
727
|
+
context.moveFocus(-1);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
if (event.key === "Home") {
|
|
731
|
+
event.preventDefault();
|
|
732
|
+
context.focusFirst();
|
|
733
|
+
}
|
|
734
|
+
if (event.key === "End") {
|
|
735
|
+
event.preventDefault();
|
|
736
|
+
context.focusLast();
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
useEventListener(() => itemNode(), "keydown", onKeyDown);
|
|
740
|
+
createEffect(() => {
|
|
741
|
+
const node = itemNode();
|
|
742
|
+
cleanup?.();
|
|
743
|
+
cleanup = null;
|
|
744
|
+
if (!node) return;
|
|
745
|
+
cleanup = context.register({
|
|
746
|
+
id,
|
|
747
|
+
element: node,
|
|
748
|
+
disabled: read(props.disabled, false)
|
|
749
|
+
});
|
|
750
|
+
onCleanup(() => {
|
|
751
|
+
cleanup?.();
|
|
752
|
+
cleanup = null;
|
|
753
|
+
});
|
|
754
|
+
});
|
|
755
|
+
const register = (node) => {
|
|
756
|
+
itemNode(node);
|
|
757
|
+
};
|
|
758
|
+
return Primitive({
|
|
759
|
+
...props,
|
|
760
|
+
as: tag,
|
|
761
|
+
disabled: read(props.disabled, false),
|
|
762
|
+
ref: composeRefs(props.ref, register),
|
|
763
|
+
tabIndex: -1,
|
|
764
|
+
"data-roving-focus-item": "",
|
|
765
|
+
onFocus,
|
|
766
|
+
children: props.children
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// src/components/disclosure/tabs.ts
|
|
771
|
+
var TabsContext = createContext6(null);
|
|
772
|
+
function useTabsContext(component) {
|
|
773
|
+
const context = useContext6(TabsContext);
|
|
774
|
+
if (!context) {
|
|
775
|
+
throw new Error(`${component} must be used inside TabsRoot`);
|
|
776
|
+
}
|
|
777
|
+
return context;
|
|
778
|
+
}
|
|
779
|
+
function TabsRoot(props) {
|
|
780
|
+
const valueState = createControllableState({
|
|
781
|
+
value: props.value,
|
|
782
|
+
defaultValue: props.defaultValue ?? "",
|
|
783
|
+
onChange: props.onValueChange
|
|
784
|
+
});
|
|
785
|
+
const baseId = useId(props.id, "tabs");
|
|
786
|
+
const context = {
|
|
787
|
+
value: () => valueState.get(),
|
|
788
|
+
setValue: (value) => valueState.set(value),
|
|
789
|
+
orientation: () => props.orientation ?? "horizontal",
|
|
790
|
+
baseId
|
|
791
|
+
};
|
|
792
|
+
return {
|
|
793
|
+
type: TabsContext.Provider,
|
|
794
|
+
props: {
|
|
795
|
+
value: context,
|
|
796
|
+
children: props.children
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
function TabsList(props) {
|
|
801
|
+
const context = useTabsContext("TabsList");
|
|
802
|
+
return {
|
|
803
|
+
type: "div",
|
|
804
|
+
props: {
|
|
805
|
+
...props,
|
|
806
|
+
role: "tablist",
|
|
807
|
+
"aria-orientation": () => context.orientation(),
|
|
808
|
+
"data-tabs-list": "",
|
|
809
|
+
children: {
|
|
810
|
+
type: RovingFocusGroup,
|
|
811
|
+
props: {
|
|
812
|
+
orientation: () => context.orientation(),
|
|
813
|
+
loop: true,
|
|
814
|
+
children: props.children
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
function TabsTrigger(props) {
|
|
821
|
+
const context = useTabsContext("TabsTrigger");
|
|
822
|
+
const tag = props.as ?? "button";
|
|
823
|
+
const selected = () => context.value() === props.value;
|
|
824
|
+
return Primitive({
|
|
825
|
+
...props,
|
|
826
|
+
as: tag,
|
|
827
|
+
value: void 0,
|
|
828
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
829
|
+
role: "tab",
|
|
830
|
+
id: `${context.baseId}-trigger-${props.value}`,
|
|
831
|
+
"aria-controls": `${context.baseId}-content-${props.value}`,
|
|
832
|
+
"aria-selected": selected,
|
|
833
|
+
"data-state": () => selected() ? "active" : "inactive",
|
|
834
|
+
onClick: (event) => {
|
|
835
|
+
;
|
|
836
|
+
props.onClick?.(event);
|
|
837
|
+
if (!event.defaultPrevented) {
|
|
838
|
+
context.setValue(props.value);
|
|
839
|
+
}
|
|
840
|
+
},
|
|
841
|
+
children: props.children
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
function TabsContent(props) {
|
|
845
|
+
const context = useTabsContext("TabsContent");
|
|
846
|
+
return {
|
|
847
|
+
type: "div",
|
|
848
|
+
props: {
|
|
849
|
+
"data-tabs-content-wrapper": props.value,
|
|
850
|
+
children: () => context.value() === props.value || props.forceMount ? {
|
|
851
|
+
type: "div",
|
|
852
|
+
props: {
|
|
853
|
+
...props,
|
|
854
|
+
value: void 0,
|
|
855
|
+
forceMount: void 0,
|
|
856
|
+
role: "tabpanel",
|
|
857
|
+
id: `${context.baseId}-content-${props.value}`,
|
|
858
|
+
"aria-labelledby": `${context.baseId}-trigger-${props.value}`,
|
|
859
|
+
"data-state": () => context.value() === props.value ? "active" : "inactive",
|
|
860
|
+
"data-tabs-content": props.value,
|
|
861
|
+
children: props.children
|
|
862
|
+
}
|
|
863
|
+
} : null
|
|
864
|
+
}
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// src/components/disclosure/navigation-menu.ts
|
|
869
|
+
import { createContext as createContext7, useContext as useContext7 } from "@fictjs/runtime";
|
|
870
|
+
import { createSignal as createSignal3 } from "@fictjs/runtime/advanced";
|
|
871
|
+
var NavigationMenuRootContext = createContext7(null);
|
|
872
|
+
var NavigationMenuItemContext = createContext7(null);
|
|
873
|
+
function useNavigationRootContext(component) {
|
|
874
|
+
const context = useContext7(NavigationMenuRootContext);
|
|
875
|
+
if (!context) {
|
|
876
|
+
throw new Error(`${component} must be used inside NavigationMenuRoot`);
|
|
877
|
+
}
|
|
878
|
+
return context;
|
|
879
|
+
}
|
|
880
|
+
function useNavigationItemContext(component) {
|
|
881
|
+
const context = useContext7(NavigationMenuItemContext);
|
|
882
|
+
if (!context) {
|
|
883
|
+
throw new Error(`${component} must be used inside NavigationMenuItem`);
|
|
884
|
+
}
|
|
885
|
+
return context;
|
|
886
|
+
}
|
|
887
|
+
function NavigationMenuRoot(props) {
|
|
888
|
+
const activeItemSignal = createSignal3(null);
|
|
889
|
+
const context = {
|
|
890
|
+
activeItem: () => activeItemSignal(),
|
|
891
|
+
setActiveItem: (value) => activeItemSignal(value)
|
|
892
|
+
};
|
|
893
|
+
return {
|
|
894
|
+
type: NavigationMenuRootContext.Provider,
|
|
895
|
+
props: {
|
|
896
|
+
value: context,
|
|
897
|
+
children: {
|
|
898
|
+
type: "nav",
|
|
899
|
+
props: {
|
|
900
|
+
...props,
|
|
901
|
+
"data-navigation-menu-root": "",
|
|
902
|
+
children: props.children
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
function NavigationMenuList(props) {
|
|
909
|
+
return {
|
|
910
|
+
type: "ul",
|
|
911
|
+
props: {
|
|
912
|
+
...props,
|
|
913
|
+
"data-navigation-menu-list": "",
|
|
914
|
+
children: props.children
|
|
915
|
+
}
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
function NavigationMenuItem(props) {
|
|
919
|
+
const value = useId(props.value, "navigation-menu-item");
|
|
920
|
+
const tag = props.as ?? "li";
|
|
921
|
+
return {
|
|
922
|
+
type: NavigationMenuItemContext.Provider,
|
|
923
|
+
props: {
|
|
924
|
+
value: { value },
|
|
925
|
+
children: Primitive({
|
|
926
|
+
...props,
|
|
927
|
+
value: void 0,
|
|
928
|
+
as: tag,
|
|
929
|
+
"data-navigation-menu-item": value,
|
|
930
|
+
children: props.children
|
|
931
|
+
})
|
|
932
|
+
}
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
function NavigationMenuTrigger(props) {
|
|
936
|
+
const root = useNavigationRootContext("NavigationMenuTrigger");
|
|
937
|
+
const item = useNavigationItemContext("NavigationMenuTrigger");
|
|
938
|
+
const tag = props.as ?? "button";
|
|
939
|
+
const open = () => root.activeItem() === item.value;
|
|
940
|
+
return Primitive({
|
|
941
|
+
...props,
|
|
942
|
+
as: tag,
|
|
943
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
944
|
+
"aria-expanded": open,
|
|
945
|
+
"aria-haspopup": "menu",
|
|
946
|
+
"data-state": () => open() ? "open" : "closed",
|
|
947
|
+
onClick: (event) => {
|
|
948
|
+
;
|
|
949
|
+
props.onClick?.(event);
|
|
950
|
+
if (event.defaultPrevented) return;
|
|
951
|
+
root.setActiveItem(open() ? null : item.value);
|
|
952
|
+
},
|
|
953
|
+
children: props.children
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
function NavigationMenuContent(props) {
|
|
957
|
+
const root = useNavigationRootContext("NavigationMenuContent");
|
|
958
|
+
const item = useNavigationItemContext("NavigationMenuContent");
|
|
959
|
+
const open = () => root.activeItem() === item.value;
|
|
960
|
+
return {
|
|
961
|
+
type: "div",
|
|
962
|
+
props: {
|
|
963
|
+
"data-navigation-menu-content-wrapper": item.value,
|
|
964
|
+
children: () => open() || props.forceMount ? {
|
|
965
|
+
type: "div",
|
|
966
|
+
props: {
|
|
967
|
+
...props,
|
|
968
|
+
forceMount: void 0,
|
|
969
|
+
role: "menu",
|
|
970
|
+
"data-state": () => open() ? "open" : "closed",
|
|
971
|
+
"data-navigation-menu-content": item.value,
|
|
972
|
+
children: props.children
|
|
973
|
+
}
|
|
974
|
+
} : null
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
function NavigationMenuLink(props) {
|
|
979
|
+
const tag = props.as ?? "a";
|
|
980
|
+
return Primitive({
|
|
981
|
+
...props,
|
|
982
|
+
as: tag,
|
|
983
|
+
"data-navigation-menu-link": "",
|
|
984
|
+
children: props.children
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
function NavigationMenuIndicator(props) {
|
|
988
|
+
return {
|
|
989
|
+
type: "div",
|
|
990
|
+
props: {
|
|
991
|
+
...props,
|
|
992
|
+
"data-navigation-menu-indicator": "",
|
|
993
|
+
children: props.children
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
function NavigationMenuViewport(props) {
|
|
998
|
+
return {
|
|
999
|
+
type: "div",
|
|
1000
|
+
props: {
|
|
1001
|
+
...props,
|
|
1002
|
+
"data-navigation-menu-viewport": "",
|
|
1003
|
+
children: props.children
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// src/components/form/label.ts
|
|
1009
|
+
function Label(props) {
|
|
1010
|
+
return {
|
|
1011
|
+
type: "label",
|
|
1012
|
+
props: {
|
|
1013
|
+
...props,
|
|
1014
|
+
"data-label": "",
|
|
1015
|
+
children: props.children
|
|
1016
|
+
}
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// src/components/form/checkbox.ts
|
|
1021
|
+
function Checkbox(props) {
|
|
1022
|
+
const checkedState = createControllableState({
|
|
1023
|
+
value: props.checked,
|
|
1024
|
+
defaultValue: props.defaultChecked ?? false,
|
|
1025
|
+
onChange: props.onCheckedChange
|
|
1026
|
+
});
|
|
1027
|
+
return {
|
|
1028
|
+
type: "button",
|
|
1029
|
+
props: {
|
|
1030
|
+
...props,
|
|
1031
|
+
type: "button",
|
|
1032
|
+
role: "checkbox",
|
|
1033
|
+
disabled: props.disabled,
|
|
1034
|
+
"aria-checked": () => checkedState.get(),
|
|
1035
|
+
"data-state": () => checkedState.get() ? "checked" : "unchecked",
|
|
1036
|
+
"data-checkbox": "",
|
|
1037
|
+
onClick: (event) => {
|
|
1038
|
+
;
|
|
1039
|
+
props.onClick?.(event);
|
|
1040
|
+
if (event.defaultPrevented || props.disabled) return;
|
|
1041
|
+
checkedState.set(!checkedState.get());
|
|
1042
|
+
},
|
|
1043
|
+
children: [
|
|
1044
|
+
props.children,
|
|
1045
|
+
props.name ? {
|
|
1046
|
+
type: "input",
|
|
1047
|
+
props: {
|
|
1048
|
+
type: "checkbox",
|
|
1049
|
+
hidden: true,
|
|
1050
|
+
tabIndex: -1,
|
|
1051
|
+
name: props.name,
|
|
1052
|
+
value: props.value,
|
|
1053
|
+
checked: () => checkedState.get(),
|
|
1054
|
+
required: props.required,
|
|
1055
|
+
readOnly: true
|
|
1056
|
+
}
|
|
1057
|
+
} : null
|
|
1058
|
+
]
|
|
1059
|
+
}
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// src/components/form/radio-group.ts
|
|
1064
|
+
import { createContext as createContext8, useContext as useContext8 } from "@fictjs/runtime";
|
|
1065
|
+
var RadioGroupContext = createContext8(null);
|
|
1066
|
+
function useRadioGroupContext(component) {
|
|
1067
|
+
const context = useContext8(RadioGroupContext);
|
|
1068
|
+
if (!context) {
|
|
1069
|
+
throw new Error(`${component} must be used inside RadioGroup`);
|
|
1070
|
+
}
|
|
1071
|
+
return context;
|
|
1072
|
+
}
|
|
1073
|
+
function RadioGroup(props) {
|
|
1074
|
+
const valueState = createControllableState({
|
|
1075
|
+
value: props.value,
|
|
1076
|
+
defaultValue: props.defaultValue ?? "",
|
|
1077
|
+
onChange: props.onValueChange
|
|
1078
|
+
});
|
|
1079
|
+
const context = {
|
|
1080
|
+
value: () => valueState.get(),
|
|
1081
|
+
setValue: (value) => valueState.set(value),
|
|
1082
|
+
name: props.name
|
|
1083
|
+
};
|
|
1084
|
+
return {
|
|
1085
|
+
type: RadioGroupContext.Provider,
|
|
1086
|
+
props: {
|
|
1087
|
+
value: context,
|
|
1088
|
+
children: {
|
|
1089
|
+
type: "div",
|
|
1090
|
+
props: {
|
|
1091
|
+
...props,
|
|
1092
|
+
role: "radiogroup",
|
|
1093
|
+
"data-radio-group": "",
|
|
1094
|
+
children: props.children
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
function RadioItem(props) {
|
|
1101
|
+
const group = useRadioGroupContext("RadioItem");
|
|
1102
|
+
const checked = () => group.value() === props.value;
|
|
1103
|
+
const tag = props.as ?? "button";
|
|
1104
|
+
const hiddenInput = group.name ? {
|
|
1105
|
+
type: "input",
|
|
1106
|
+
props: {
|
|
1107
|
+
type: "radio",
|
|
1108
|
+
hidden: true,
|
|
1109
|
+
tabIndex: -1,
|
|
1110
|
+
name: group.name,
|
|
1111
|
+
checked,
|
|
1112
|
+
value: props.value,
|
|
1113
|
+
readOnly: true
|
|
1114
|
+
}
|
|
1115
|
+
} : null;
|
|
1116
|
+
if (props.asChild) {
|
|
1117
|
+
return [
|
|
1118
|
+
Primitive({
|
|
1119
|
+
...props,
|
|
1120
|
+
as: tag,
|
|
1121
|
+
type: props.type,
|
|
1122
|
+
role: "radio",
|
|
1123
|
+
disabled: props.disabled,
|
|
1124
|
+
"aria-checked": checked,
|
|
1125
|
+
"data-state": () => checked() ? "checked" : "unchecked",
|
|
1126
|
+
"data-radio-item": "",
|
|
1127
|
+
onClick: (event) => {
|
|
1128
|
+
;
|
|
1129
|
+
props.onClick?.(event);
|
|
1130
|
+
if (event.defaultPrevented || props.disabled) return;
|
|
1131
|
+
group.setValue(props.value);
|
|
1132
|
+
},
|
|
1133
|
+
children: props.children
|
|
1134
|
+
}),
|
|
1135
|
+
hiddenInput
|
|
1136
|
+
];
|
|
1137
|
+
}
|
|
1138
|
+
return Primitive({
|
|
1139
|
+
...props,
|
|
1140
|
+
as: tag,
|
|
1141
|
+
type: tag === "button" ? "button" : props.type,
|
|
1142
|
+
role: "radio",
|
|
1143
|
+
disabled: props.disabled,
|
|
1144
|
+
"aria-checked": checked,
|
|
1145
|
+
"data-state": () => checked() ? "checked" : "unchecked",
|
|
1146
|
+
"data-radio-item": "",
|
|
1147
|
+
onClick: (event) => {
|
|
1148
|
+
;
|
|
1149
|
+
props.onClick?.(event);
|
|
1150
|
+
if (event.defaultPrevented || props.disabled) return;
|
|
1151
|
+
group.setValue(props.value);
|
|
1152
|
+
},
|
|
1153
|
+
children: [props.children, hiddenInput]
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
// src/components/form/switch.ts
|
|
1158
|
+
function Switch(props) {
|
|
1159
|
+
const checkedState = createControllableState({
|
|
1160
|
+
value: props.checked,
|
|
1161
|
+
defaultValue: props.defaultChecked ?? false,
|
|
1162
|
+
onChange: props.onCheckedChange
|
|
1163
|
+
});
|
|
1164
|
+
return {
|
|
1165
|
+
type: "button",
|
|
1166
|
+
props: {
|
|
1167
|
+
...props,
|
|
1168
|
+
type: "button",
|
|
1169
|
+
role: "switch",
|
|
1170
|
+
disabled: props.disabled,
|
|
1171
|
+
"aria-checked": () => checkedState.get(),
|
|
1172
|
+
"data-state": () => checkedState.get() ? "checked" : "unchecked",
|
|
1173
|
+
"data-switch": "",
|
|
1174
|
+
onClick: (event) => {
|
|
1175
|
+
;
|
|
1176
|
+
props.onClick?.(event);
|
|
1177
|
+
if (event.defaultPrevented || props.disabled) return;
|
|
1178
|
+
checkedState.set(!checkedState.get());
|
|
1179
|
+
},
|
|
1180
|
+
children: [
|
|
1181
|
+
props.children,
|
|
1182
|
+
props.name ? {
|
|
1183
|
+
type: "input",
|
|
1184
|
+
props: {
|
|
1185
|
+
type: "checkbox",
|
|
1186
|
+
hidden: true,
|
|
1187
|
+
tabIndex: -1,
|
|
1188
|
+
name: props.name,
|
|
1189
|
+
value: props.value,
|
|
1190
|
+
checked: () => checkedState.get(),
|
|
1191
|
+
readOnly: true
|
|
1192
|
+
}
|
|
1193
|
+
} : null
|
|
1194
|
+
]
|
|
1195
|
+
}
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
function SwitchThumb(props) {
|
|
1199
|
+
return {
|
|
1200
|
+
type: "span",
|
|
1201
|
+
props: {
|
|
1202
|
+
...props,
|
|
1203
|
+
"data-switch-thumb": "",
|
|
1204
|
+
children: props.children
|
|
1205
|
+
}
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
// src/components/form/toggle.ts
|
|
1210
|
+
import { createContext as createContext9, useContext as useContext9 } from "@fictjs/runtime";
|
|
1211
|
+
var ToggleGroupContext = createContext9(null);
|
|
1212
|
+
function normalize2(value) {
|
|
1213
|
+
if (value === void 0) return [];
|
|
1214
|
+
return Array.isArray(value) ? value : [value];
|
|
1215
|
+
}
|
|
1216
|
+
function Toggle(props) {
|
|
1217
|
+
const pressedState = createControllableState({
|
|
1218
|
+
value: props.pressed,
|
|
1219
|
+
defaultValue: props.defaultPressed ?? false,
|
|
1220
|
+
onChange: props.onPressedChange
|
|
1221
|
+
});
|
|
1222
|
+
return {
|
|
1223
|
+
type: "button",
|
|
1224
|
+
props: {
|
|
1225
|
+
...props,
|
|
1226
|
+
type: "button",
|
|
1227
|
+
disabled: props.disabled,
|
|
1228
|
+
"aria-pressed": () => pressedState.get(),
|
|
1229
|
+
"data-state": () => pressedState.get() ? "on" : "off",
|
|
1230
|
+
"data-toggle": "",
|
|
1231
|
+
onClick: (event) => {
|
|
1232
|
+
;
|
|
1233
|
+
props.onClick?.(event);
|
|
1234
|
+
if (event.defaultPrevented || props.disabled) return;
|
|
1235
|
+
pressedState.set(!pressedState.get());
|
|
1236
|
+
},
|
|
1237
|
+
children: props.children
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
}
|
|
1241
|
+
function ToggleGroup(props) {
|
|
1242
|
+
const type = props.type ?? "single";
|
|
1243
|
+
const valuesState = createControllableState({
|
|
1244
|
+
value: props.value === void 0 ? void 0 : typeof props.value === "function" ? (() => normalize2(props.value())) : normalize2(props.value),
|
|
1245
|
+
defaultValue: normalize2(props.defaultValue),
|
|
1246
|
+
onChange: (next) => {
|
|
1247
|
+
props.onValueChange?.(type === "single" ? next[0] ?? "" : next);
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
const context = {
|
|
1251
|
+
type: () => type,
|
|
1252
|
+
values: () => valuesState.get(),
|
|
1253
|
+
toggle: (value) => {
|
|
1254
|
+
const current = valuesState.get();
|
|
1255
|
+
const exists = current.includes(value);
|
|
1256
|
+
if (type === "single") {
|
|
1257
|
+
valuesState.set(exists ? [] : [value]);
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
if (exists) {
|
|
1261
|
+
valuesState.set(current.filter((item) => item !== value));
|
|
1262
|
+
} else {
|
|
1263
|
+
valuesState.set([...current, value]);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
return {
|
|
1268
|
+
type: ToggleGroupContext.Provider,
|
|
1269
|
+
props: {
|
|
1270
|
+
value: context,
|
|
1271
|
+
children: {
|
|
1272
|
+
type: "div",
|
|
1273
|
+
props: {
|
|
1274
|
+
...props,
|
|
1275
|
+
role: "group",
|
|
1276
|
+
"data-toggle-group": "",
|
|
1277
|
+
children: props.children
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
}
|
|
1283
|
+
function ToggleGroupItem(props) {
|
|
1284
|
+
const group = useContext9(ToggleGroupContext);
|
|
1285
|
+
if (!group) {
|
|
1286
|
+
throw new Error("ToggleGroupItem must be used inside ToggleGroup");
|
|
1287
|
+
}
|
|
1288
|
+
const pressed = () => group.values().includes(props.value);
|
|
1289
|
+
const tag = props.as ?? "button";
|
|
1290
|
+
return Primitive({
|
|
1291
|
+
...props,
|
|
1292
|
+
as: tag,
|
|
1293
|
+
type: !props.asChild && tag === "button" ? "button" : props.type,
|
|
1294
|
+
disabled: props.disabled,
|
|
1295
|
+
"aria-pressed": pressed,
|
|
1296
|
+
"data-state": () => pressed() ? "on" : "off",
|
|
1297
|
+
"data-toggle-group-item": "",
|
|
1298
|
+
onClick: (event) => {
|
|
1299
|
+
;
|
|
1300
|
+
props.onClick?.(event);
|
|
1301
|
+
if (event.defaultPrevented || props.disabled) return;
|
|
1302
|
+
group.toggle(props.value);
|
|
1303
|
+
},
|
|
1304
|
+
children: props.children
|
|
1305
|
+
});
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// src/components/form/slider.ts
|
|
1309
|
+
function Slider(props) {
|
|
1310
|
+
const valueState = createControllableState({
|
|
1311
|
+
value: props.value,
|
|
1312
|
+
defaultValue: props.defaultValue ?? 0,
|
|
1313
|
+
onChange: props.onValueChange
|
|
1314
|
+
});
|
|
1315
|
+
return {
|
|
1316
|
+
type: "input",
|
|
1317
|
+
props: {
|
|
1318
|
+
...props,
|
|
1319
|
+
type: "range",
|
|
1320
|
+
min: props.min ?? 0,
|
|
1321
|
+
max: props.max ?? 100,
|
|
1322
|
+
step: props.step ?? 1,
|
|
1323
|
+
value: () => valueState.get(),
|
|
1324
|
+
disabled: props.disabled,
|
|
1325
|
+
"data-slider": "",
|
|
1326
|
+
onInput: (event) => {
|
|
1327
|
+
;
|
|
1328
|
+
props.onInput?.(event);
|
|
1329
|
+
const target = event.target;
|
|
1330
|
+
if (!target) return;
|
|
1331
|
+
valueState.set(Number(target.value));
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
function RangeSlider(props) {
|
|
1337
|
+
const valueState = createControllableState({
|
|
1338
|
+
value: props.value,
|
|
1339
|
+
defaultValue: props.defaultValue ?? [0, 100],
|
|
1340
|
+
onChange: props.onValueChange
|
|
1341
|
+
});
|
|
1342
|
+
const min = props.min ?? 0;
|
|
1343
|
+
const max = props.max ?? 100;
|
|
1344
|
+
const step = props.step ?? 1;
|
|
1345
|
+
const setStart = (nextStart) => {
|
|
1346
|
+
const [, end] = valueState.get();
|
|
1347
|
+
valueState.set([Math.min(nextStart, end), end]);
|
|
1348
|
+
};
|
|
1349
|
+
const setEnd = (nextEnd) => {
|
|
1350
|
+
const [start] = valueState.get();
|
|
1351
|
+
valueState.set([start, Math.max(start, nextEnd)]);
|
|
1352
|
+
};
|
|
1353
|
+
return {
|
|
1354
|
+
type: "div",
|
|
1355
|
+
props: {
|
|
1356
|
+
...props,
|
|
1357
|
+
"data-range-slider": "",
|
|
1358
|
+
children: [
|
|
1359
|
+
{
|
|
1360
|
+
type: "input",
|
|
1361
|
+
props: {
|
|
1362
|
+
type: "range",
|
|
1363
|
+
min,
|
|
1364
|
+
max,
|
|
1365
|
+
step,
|
|
1366
|
+
value: () => valueState.get()[0],
|
|
1367
|
+
disabled: props.disabled,
|
|
1368
|
+
"data-range-slider-start": "",
|
|
1369
|
+
onInput: (event) => {
|
|
1370
|
+
;
|
|
1371
|
+
props.onInput?.(event);
|
|
1372
|
+
const target = event.target;
|
|
1373
|
+
if (!target) return;
|
|
1374
|
+
setStart(Number(target.value));
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
},
|
|
1378
|
+
{
|
|
1379
|
+
type: "input",
|
|
1380
|
+
props: {
|
|
1381
|
+
type: "range",
|
|
1382
|
+
min,
|
|
1383
|
+
max,
|
|
1384
|
+
step,
|
|
1385
|
+
value: () => valueState.get()[1],
|
|
1386
|
+
disabled: props.disabled,
|
|
1387
|
+
"data-range-slider-end": "",
|
|
1388
|
+
onInput: (event) => {
|
|
1389
|
+
;
|
|
1390
|
+
props.onInput?.(event);
|
|
1391
|
+
const target = event.target;
|
|
1392
|
+
if (!target) return;
|
|
1393
|
+
setEnd(Number(target.value));
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
]
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
// src/components/form/calendar.ts
|
|
1403
|
+
import { createContext as createContext10, useContext as useContext10 } from "@fictjs/runtime";
|
|
1404
|
+
function toDate(value) {
|
|
1405
|
+
if (value === null || value === void 0) return null;
|
|
1406
|
+
const next = value instanceof Date ? new Date(value.getTime()) : new Date(value);
|
|
1407
|
+
return Number.isNaN(next.getTime()) ? null : next;
|
|
1408
|
+
}
|
|
1409
|
+
function normalizeDate(date) {
|
|
1410
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
1411
|
+
}
|
|
1412
|
+
function normalizeMonth(date) {
|
|
1413
|
+
return new Date(date.getFullYear(), date.getMonth(), 1);
|
|
1414
|
+
}
|
|
1415
|
+
function clampWeekday(value) {
|
|
1416
|
+
if (!Number.isFinite(value)) return 0;
|
|
1417
|
+
const next = Math.floor(value);
|
|
1418
|
+
if (next < 0) return 0;
|
|
1419
|
+
if (next > 6) return 6;
|
|
1420
|
+
return next;
|
|
1421
|
+
}
|
|
1422
|
+
function isSameDay(a, b) {
|
|
1423
|
+
if (!a || !b) return a === b;
|
|
1424
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
1425
|
+
}
|
|
1426
|
+
function isSameMonth(a, b) {
|
|
1427
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth();
|
|
1428
|
+
}
|
|
1429
|
+
function addMonths(month, offset) {
|
|
1430
|
+
return normalizeMonth(new Date(month.getFullYear(), month.getMonth() + offset, 1));
|
|
1431
|
+
}
|
|
1432
|
+
function formatDateKey(date) {
|
|
1433
|
+
const y = date.getFullYear();
|
|
1434
|
+
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
1435
|
+
const d = String(date.getDate()).padStart(2, "0");
|
|
1436
|
+
return `${y}-${m}-${d}`;
|
|
1437
|
+
}
|
|
1438
|
+
function resolveDate(value) {
|
|
1439
|
+
const next = toDate(read(value, void 0));
|
|
1440
|
+
return next ? normalizeDate(next) : null;
|
|
1441
|
+
}
|
|
1442
|
+
function buildVisibleDays(month, weekStartsOn) {
|
|
1443
|
+
const firstOfMonth = normalizeMonth(month);
|
|
1444
|
+
const firstWeekday = firstOfMonth.getDay();
|
|
1445
|
+
const offset = (firstWeekday - weekStartsOn + 7) % 7;
|
|
1446
|
+
const start = new Date(firstOfMonth.getFullYear(), firstOfMonth.getMonth(), 1 - offset);
|
|
1447
|
+
return Array.from({ length: 42 }, (_, index) => normalizeDate(new Date(start.getFullYear(), start.getMonth(), start.getDate() + index)));
|
|
1448
|
+
}
|
|
1449
|
+
function weekdayLabels(locale, format, weekStartsOn) {
|
|
1450
|
+
const formatter = new Intl.DateTimeFormat(locale, { weekday: format });
|
|
1451
|
+
const start = new Date(2026, 0, 4);
|
|
1452
|
+
return Array.from({ length: 7 }, (_, index) => {
|
|
1453
|
+
const day = new Date(start.getFullYear(), start.getMonth(), start.getDate() + (weekStartsOn + index) % 7);
|
|
1454
|
+
return formatter.format(day);
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
var CalendarContext = createContext10(null);
|
|
1458
|
+
function useCalendarContext(component) {
|
|
1459
|
+
const context = useContext10(CalendarContext);
|
|
1460
|
+
if (!context) {
|
|
1461
|
+
throw new Error(`${component} must be used inside CalendarRoot`);
|
|
1462
|
+
}
|
|
1463
|
+
return context;
|
|
1464
|
+
}
|
|
1465
|
+
function CalendarRoot(props) {
|
|
1466
|
+
const initialValue = toDate(props.defaultValue);
|
|
1467
|
+
const initialMonth = normalizeMonth(toDate(props.defaultMonth) ?? initialValue ?? /* @__PURE__ */ new Date());
|
|
1468
|
+
const baseId = useId(props.id, "calendar");
|
|
1469
|
+
const valueState = createControllableState({
|
|
1470
|
+
value: props.value === void 0 ? void 0 : (() => resolveDate(props.value)),
|
|
1471
|
+
defaultValue: initialValue ? normalizeDate(initialValue) : null,
|
|
1472
|
+
onChange: props.onValueChange,
|
|
1473
|
+
equals: (a, b) => isSameDay(a, b)
|
|
1474
|
+
});
|
|
1475
|
+
const monthState = createControllableState({
|
|
1476
|
+
value: props.month === void 0 ? void 0 : (() => {
|
|
1477
|
+
const next = toDate(read(props.month, void 0));
|
|
1478
|
+
return normalizeMonth(next ?? initialMonth);
|
|
1479
|
+
}),
|
|
1480
|
+
defaultValue: initialMonth,
|
|
1481
|
+
onChange: props.onMonthChange,
|
|
1482
|
+
equals: (a, b) => isSameMonth(a, b)
|
|
1483
|
+
});
|
|
1484
|
+
const context = {
|
|
1485
|
+
baseId,
|
|
1486
|
+
gridId: `${baseId}-grid`,
|
|
1487
|
+
value: () => valueState.get(),
|
|
1488
|
+
setValue: (value) => valueState.set(value ? normalizeDate(value) : null),
|
|
1489
|
+
month: () => normalizeMonth(monthState.get()),
|
|
1490
|
+
setMonth: (month) => monthState.set(normalizeMonth(month)),
|
|
1491
|
+
locale: () => read(props.locale, "en-US"),
|
|
1492
|
+
weekStartsOn: () => clampWeekday(read(props.weekStartsOn, 0)),
|
|
1493
|
+
weekdayFormat: () => read(props.weekdayFormat, "short"),
|
|
1494
|
+
showOutsideDays: () => read(props.showOutsideDays, true),
|
|
1495
|
+
disabled: (date) => props.disabled?.(normalizeDate(date)) ?? false
|
|
1496
|
+
};
|
|
1497
|
+
const defaultChildren = [
|
|
1498
|
+
{
|
|
1499
|
+
type: CalendarHeader,
|
|
1500
|
+
props: {
|
|
1501
|
+
children: [
|
|
1502
|
+
{ type: CalendarPrevButton, props: { "data-calendar-prev-button": "", children: "Prev" } },
|
|
1503
|
+
{ type: CalendarTitle, props: {} },
|
|
1504
|
+
{ type: CalendarNextButton, props: { "data-calendar-next-button": "", children: "Next" } }
|
|
1505
|
+
]
|
|
1506
|
+
}
|
|
1507
|
+
},
|
|
1508
|
+
{ type: CalendarGrid, props: {} }
|
|
1509
|
+
];
|
|
1510
|
+
return {
|
|
1511
|
+
type: CalendarContext.Provider,
|
|
1512
|
+
props: {
|
|
1513
|
+
value: context,
|
|
1514
|
+
children: {
|
|
1515
|
+
type: "div",
|
|
1516
|
+
props: {
|
|
1517
|
+
...props,
|
|
1518
|
+
id: baseId,
|
|
1519
|
+
value: void 0,
|
|
1520
|
+
defaultValue: void 0,
|
|
1521
|
+
onValueChange: void 0,
|
|
1522
|
+
month: void 0,
|
|
1523
|
+
defaultMonth: void 0,
|
|
1524
|
+
onMonthChange: void 0,
|
|
1525
|
+
locale: void 0,
|
|
1526
|
+
weekStartsOn: void 0,
|
|
1527
|
+
weekdayFormat: void 0,
|
|
1528
|
+
showOutsideDays: void 0,
|
|
1529
|
+
disabled: void 0,
|
|
1530
|
+
"data-calendar-root": "",
|
|
1531
|
+
children: props.children ?? defaultChildren
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
var Calendar = CalendarRoot;
|
|
1538
|
+
function CalendarHeader(props) {
|
|
1539
|
+
return {
|
|
1540
|
+
type: "div",
|
|
1541
|
+
props: {
|
|
1542
|
+
...props,
|
|
1543
|
+
"data-calendar-header": "",
|
|
1544
|
+
children: props.children
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
function CalendarTitle(props) {
|
|
1549
|
+
const context = useCalendarContext("CalendarTitle");
|
|
1550
|
+
return {
|
|
1551
|
+
type: "span",
|
|
1552
|
+
props: {
|
|
1553
|
+
...props,
|
|
1554
|
+
"data-calendar-title": "",
|
|
1555
|
+
children: props.children ?? (() => new Intl.DateTimeFormat(context.locale(), {
|
|
1556
|
+
month: "long",
|
|
1557
|
+
year: "numeric"
|
|
1558
|
+
}).format(context.month()))
|
|
1559
|
+
}
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
function CalendarPrevButton(props) {
|
|
1563
|
+
const context = useCalendarContext("CalendarPrevButton");
|
|
1564
|
+
const tag = props.as ?? "button";
|
|
1565
|
+
return Primitive({
|
|
1566
|
+
...props,
|
|
1567
|
+
as: tag,
|
|
1568
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
1569
|
+
"aria-controls": context.gridId,
|
|
1570
|
+
"data-calendar-prev": "",
|
|
1571
|
+
onClick: (event) => {
|
|
1572
|
+
;
|
|
1573
|
+
props.onClick?.(event);
|
|
1574
|
+
if (event.defaultPrevented) return;
|
|
1575
|
+
context.setMonth(addMonths(context.month(), -1));
|
|
1576
|
+
},
|
|
1577
|
+
children: props.children ?? "Prev"
|
|
1578
|
+
});
|
|
1579
|
+
}
|
|
1580
|
+
function CalendarNextButton(props) {
|
|
1581
|
+
const context = useCalendarContext("CalendarNextButton");
|
|
1582
|
+
const tag = props.as ?? "button";
|
|
1583
|
+
return Primitive({
|
|
1584
|
+
...props,
|
|
1585
|
+
as: tag,
|
|
1586
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
1587
|
+
"aria-controls": context.gridId,
|
|
1588
|
+
"data-calendar-next": "",
|
|
1589
|
+
onClick: (event) => {
|
|
1590
|
+
;
|
|
1591
|
+
props.onClick?.(event);
|
|
1592
|
+
if (event.defaultPrevented) return;
|
|
1593
|
+
context.setMonth(addMonths(context.month(), 1));
|
|
1594
|
+
},
|
|
1595
|
+
children: props.children ?? "Next"
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
function CalendarGrid(props) {
|
|
1599
|
+
const context = useCalendarContext("CalendarGrid");
|
|
1600
|
+
return {
|
|
1601
|
+
type: "div",
|
|
1602
|
+
props: {
|
|
1603
|
+
...props,
|
|
1604
|
+
showOutsideDays: void 0,
|
|
1605
|
+
onDaySelect: void 0,
|
|
1606
|
+
id: props.id ?? context.gridId,
|
|
1607
|
+
role: "grid",
|
|
1608
|
+
"data-calendar-grid": "",
|
|
1609
|
+
children: () => {
|
|
1610
|
+
const month = context.month();
|
|
1611
|
+
const weekStartsOn = context.weekStartsOn();
|
|
1612
|
+
const showOutside = read(props.showOutsideDays, context.showOutsideDays());
|
|
1613
|
+
const labels = weekdayLabels(context.locale(), context.weekdayFormat(), weekStartsOn);
|
|
1614
|
+
const days = buildVisibleDays(month, weekStartsOn);
|
|
1615
|
+
const header = {
|
|
1616
|
+
type: "div",
|
|
1617
|
+
props: {
|
|
1618
|
+
role: "row",
|
|
1619
|
+
"data-calendar-weekdays": "",
|
|
1620
|
+
children: labels.map((label, index) => ({
|
|
1621
|
+
type: "span",
|
|
1622
|
+
props: {
|
|
1623
|
+
role: "columnheader",
|
|
1624
|
+
"data-calendar-weekday": index,
|
|
1625
|
+
children: label
|
|
1626
|
+
}
|
|
1627
|
+
}))
|
|
1628
|
+
}
|
|
1629
|
+
};
|
|
1630
|
+
const rows = Array.from({ length: 6 }, (_, weekIndex) => {
|
|
1631
|
+
const start = weekIndex * 7;
|
|
1632
|
+
const weekDays = days.slice(start, start + 7);
|
|
1633
|
+
return {
|
|
1634
|
+
type: "div",
|
|
1635
|
+
props: {
|
|
1636
|
+
role: "row",
|
|
1637
|
+
"data-calendar-week": weekIndex,
|
|
1638
|
+
children: weekDays.map((day) => {
|
|
1639
|
+
const key = formatDateKey(day);
|
|
1640
|
+
const outsideMonth = !isSameMonth(day, month);
|
|
1641
|
+
const selected = isSameDay(context.value(), day);
|
|
1642
|
+
const disabled = context.disabled(day);
|
|
1643
|
+
if (!showOutside && outsideMonth) {
|
|
1644
|
+
return {
|
|
1645
|
+
type: "span",
|
|
1646
|
+
props: {
|
|
1647
|
+
role: "gridcell",
|
|
1648
|
+
"data-calendar-day-placeholder": key,
|
|
1649
|
+
"aria-hidden": "true",
|
|
1650
|
+
children: ""
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
return {
|
|
1655
|
+
type: "button",
|
|
1656
|
+
props: {
|
|
1657
|
+
type: "button",
|
|
1658
|
+
role: "gridcell",
|
|
1659
|
+
"aria-selected": selected,
|
|
1660
|
+
disabled,
|
|
1661
|
+
"data-calendar-day": key,
|
|
1662
|
+
"data-state": selected ? "selected" : "idle",
|
|
1663
|
+
"data-outside-month": outsideMonth ? "true" : void 0,
|
|
1664
|
+
onClick: (event) => {
|
|
1665
|
+
;
|
|
1666
|
+
props.onDaySelect?.(day, event);
|
|
1667
|
+
if (event.defaultPrevented || disabled) return;
|
|
1668
|
+
context.setValue(day);
|
|
1669
|
+
context.setMonth(day);
|
|
1670
|
+
},
|
|
1671
|
+
children: String(day.getDate())
|
|
1672
|
+
}
|
|
1673
|
+
};
|
|
1674
|
+
})
|
|
1675
|
+
}
|
|
1676
|
+
};
|
|
1677
|
+
});
|
|
1678
|
+
return [header, ...rows];
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
// src/components/form/date-picker.ts
|
|
1685
|
+
import { createContext as createContext13, useContext as useContext13 } from "@fictjs/runtime";
|
|
1686
|
+
|
|
1687
|
+
// src/components/overlay/popover.ts
|
|
1688
|
+
import { createContext as createContext12, useContext as useContext12 } from "@fictjs/runtime";
|
|
1689
|
+
import { createRef } from "@fictjs/runtime";
|
|
1690
|
+
|
|
1691
|
+
// src/components/interaction/dismissable-layer.ts
|
|
1692
|
+
import { onDestroy, onMount as onMount2 } from "@fictjs/runtime";
|
|
1693
|
+
import { createSignal as createSignal4 } from "@fictjs/runtime/advanced";
|
|
1694
|
+
import { useEventListener as useEventListener2 } from "@fictjs/hooks";
|
|
1695
|
+
var activeLayers = [];
|
|
1696
|
+
function isTopLayer(node) {
|
|
1697
|
+
if (!node) return false;
|
|
1698
|
+
return activeLayers[activeLayers.length - 1] === node;
|
|
1699
|
+
}
|
|
1700
|
+
function DismissableLayer(props) {
|
|
1701
|
+
const node = createSignal4(null);
|
|
1702
|
+
const dismiss = () => {
|
|
1703
|
+
if (read(props.disabled, false)) return;
|
|
1704
|
+
props.onDismiss?.();
|
|
1705
|
+
};
|
|
1706
|
+
const onKeyDown = (event) => {
|
|
1707
|
+
if (!isTopLayer(node())) return;
|
|
1708
|
+
if (event.key !== "Escape") return;
|
|
1709
|
+
props.onEscapeKeyDown?.(event);
|
|
1710
|
+
if (!event.defaultPrevented) {
|
|
1711
|
+
dismiss();
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
const onPointerDown = (event) => {
|
|
1715
|
+
const layer = node();
|
|
1716
|
+
if (!layer || !isTopLayer(layer)) return;
|
|
1717
|
+
const target = event.target;
|
|
1718
|
+
const isOutside = !target || !layer.contains(target);
|
|
1719
|
+
if (!isOutside) return;
|
|
1720
|
+
props.onInteractOutside?.(event);
|
|
1721
|
+
props.onPointerDownOutside?.(event);
|
|
1722
|
+
if (!event.defaultPrevented) {
|
|
1723
|
+
dismiss();
|
|
1724
|
+
}
|
|
1725
|
+
};
|
|
1726
|
+
const onFocusIn = (event) => {
|
|
1727
|
+
const layer = node();
|
|
1728
|
+
if (!layer || !isTopLayer(layer)) return;
|
|
1729
|
+
const target = event.target;
|
|
1730
|
+
const isOutside = !target || !layer.contains(target);
|
|
1731
|
+
if (!isOutside) return;
|
|
1732
|
+
props.onInteractOutside?.(event);
|
|
1733
|
+
props.onFocusOutside?.(event);
|
|
1734
|
+
if (!event.defaultPrevented) {
|
|
1735
|
+
dismiss();
|
|
1736
|
+
}
|
|
1737
|
+
};
|
|
1738
|
+
const targetDocument = () => node()?.ownerDocument ?? (typeof document !== "undefined" ? document : null);
|
|
1739
|
+
const keydownListener = useEventListener2(targetDocument, "keydown", onKeyDown, {
|
|
1740
|
+
capture: true,
|
|
1741
|
+
immediate: false
|
|
1742
|
+
});
|
|
1743
|
+
const pointerDownListener = useEventListener2(targetDocument, "pointerdown", onPointerDown, {
|
|
1744
|
+
capture: true,
|
|
1745
|
+
immediate: false
|
|
1746
|
+
});
|
|
1747
|
+
const focusInListener = useEventListener2(targetDocument, "focusin", onFocusIn, {
|
|
1748
|
+
capture: true,
|
|
1749
|
+
immediate: false
|
|
1750
|
+
});
|
|
1751
|
+
onMount2(() => {
|
|
1752
|
+
const layer = node();
|
|
1753
|
+
if (!layer) return;
|
|
1754
|
+
activeLayers.push(layer);
|
|
1755
|
+
keydownListener.start();
|
|
1756
|
+
pointerDownListener.start();
|
|
1757
|
+
focusInListener.start();
|
|
1758
|
+
});
|
|
1759
|
+
onDestroy(() => {
|
|
1760
|
+
keydownListener.stop();
|
|
1761
|
+
pointerDownListener.stop();
|
|
1762
|
+
focusInListener.stop();
|
|
1763
|
+
const layer = node();
|
|
1764
|
+
if (!layer) return;
|
|
1765
|
+
const index = activeLayers.indexOf(layer);
|
|
1766
|
+
if (index >= 0) {
|
|
1767
|
+
activeLayers.splice(index, 1);
|
|
1768
|
+
}
|
|
1769
|
+
});
|
|
1770
|
+
return {
|
|
1771
|
+
type: "div",
|
|
1772
|
+
props: {
|
|
1773
|
+
...props,
|
|
1774
|
+
disabled: void 0,
|
|
1775
|
+
onDismiss: void 0,
|
|
1776
|
+
onEscapeKeyDown: void 0,
|
|
1777
|
+
onInteractOutside: void 0,
|
|
1778
|
+
onPointerDownOutside: void 0,
|
|
1779
|
+
onFocusOutside: void 0,
|
|
1780
|
+
ref: (el) => {
|
|
1781
|
+
node(el);
|
|
1782
|
+
},
|
|
1783
|
+
"data-dismissable-layer": "",
|
|
1784
|
+
children: props.children
|
|
1785
|
+
}
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
// src/components/interaction/popper.ts
|
|
1790
|
+
import { createContext as createContext11, createEffect as createEffect2, useContext as useContext11 } from "@fictjs/runtime";
|
|
1791
|
+
import { createSignal as createSignal5 } from "@fictjs/runtime/advanced";
|
|
1792
|
+
import { useEventListener as useEventListener3 } from "@fictjs/hooks";
|
|
1793
|
+
var PopperContext = createContext11(null);
|
|
1794
|
+
function computePosition(anchorRect, contentRect, side, align, sideOffset, alignOffset) {
|
|
1795
|
+
let x;
|
|
1796
|
+
let y;
|
|
1797
|
+
if (side === "bottom") {
|
|
1798
|
+
y = anchorRect.bottom + sideOffset;
|
|
1799
|
+
} else if (side === "top") {
|
|
1800
|
+
y = anchorRect.top - contentRect.height - sideOffset;
|
|
1801
|
+
} else {
|
|
1802
|
+
y = anchorRect.top;
|
|
1803
|
+
}
|
|
1804
|
+
if (side === "right") {
|
|
1805
|
+
x = anchorRect.right + sideOffset;
|
|
1806
|
+
} else if (side === "left") {
|
|
1807
|
+
x = anchorRect.left - contentRect.width - sideOffset;
|
|
1808
|
+
} else {
|
|
1809
|
+
x = anchorRect.left;
|
|
1810
|
+
}
|
|
1811
|
+
if (side === "top" || side === "bottom") {
|
|
1812
|
+
if (align === "center") {
|
|
1813
|
+
x = anchorRect.left + (anchorRect.width - contentRect.width) / 2;
|
|
1814
|
+
} else if (align === "end") {
|
|
1815
|
+
x = anchorRect.right - contentRect.width;
|
|
1816
|
+
}
|
|
1817
|
+
x += alignOffset;
|
|
1818
|
+
} else {
|
|
1819
|
+
if (align === "center") {
|
|
1820
|
+
y = anchorRect.top + (anchorRect.height - contentRect.height) / 2;
|
|
1821
|
+
} else if (align === "end") {
|
|
1822
|
+
y = anchorRect.bottom - contentRect.height;
|
|
1823
|
+
}
|
|
1824
|
+
y += alignOffset;
|
|
1825
|
+
}
|
|
1826
|
+
const placement = align === "center" ? side : `${side}-${align}`;
|
|
1827
|
+
return { x, y, placement };
|
|
1828
|
+
}
|
|
1829
|
+
function PopperRoot(props) {
|
|
1830
|
+
const anchorNode = createSignal5(null);
|
|
1831
|
+
const contentNode = createSignal5(null);
|
|
1832
|
+
const currentPlacement = createSignal5("bottom");
|
|
1833
|
+
const context = {
|
|
1834
|
+
anchor: () => anchorNode(),
|
|
1835
|
+
setAnchor: (node) => {
|
|
1836
|
+
anchorNode(node);
|
|
1837
|
+
},
|
|
1838
|
+
content: () => contentNode(),
|
|
1839
|
+
setContent: (node) => {
|
|
1840
|
+
contentNode(node);
|
|
1841
|
+
},
|
|
1842
|
+
placement: () => currentPlacement(),
|
|
1843
|
+
setPlacement: (placement) => {
|
|
1844
|
+
currentPlacement(placement);
|
|
1845
|
+
}
|
|
1846
|
+
};
|
|
1847
|
+
return {
|
|
1848
|
+
type: PopperContext.Provider,
|
|
1849
|
+
props: {
|
|
1850
|
+
value: context,
|
|
1851
|
+
children: props.children
|
|
1852
|
+
}
|
|
1853
|
+
};
|
|
1854
|
+
}
|
|
1855
|
+
function PopperAnchor(props) {
|
|
1856
|
+
const context = useContext11(PopperContext);
|
|
1857
|
+
if (!context) {
|
|
1858
|
+
throw new Error("PopperAnchor must be used inside PopperRoot");
|
|
1859
|
+
}
|
|
1860
|
+
return {
|
|
1861
|
+
type: "div",
|
|
1862
|
+
props: {
|
|
1863
|
+
...props,
|
|
1864
|
+
ref: (node) => {
|
|
1865
|
+
context.setAnchor(node);
|
|
1866
|
+
if (typeof props.ref === "function") {
|
|
1867
|
+
props.ref(node);
|
|
1868
|
+
}
|
|
1869
|
+
},
|
|
1870
|
+
"data-popper-anchor": "",
|
|
1871
|
+
children: props.children
|
|
1872
|
+
}
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
function PopperContent(props) {
|
|
1876
|
+
const context = useContext11(PopperContext);
|
|
1877
|
+
if (!context) {
|
|
1878
|
+
throw new Error("PopperContent must be used inside PopperRoot");
|
|
1879
|
+
}
|
|
1880
|
+
const side = props.side ?? "bottom";
|
|
1881
|
+
const align = props.align ?? "center";
|
|
1882
|
+
const sideOffset = props.sideOffset ?? 8;
|
|
1883
|
+
const alignOffset = props.alignOffset ?? 0;
|
|
1884
|
+
const strategy = props.strategy ?? "absolute";
|
|
1885
|
+
const contentId = useId(props.id, "popper-content");
|
|
1886
|
+
const update = () => {
|
|
1887
|
+
const anchor = context.anchor();
|
|
1888
|
+
const content = context.content();
|
|
1889
|
+
if (!anchor || !content) return;
|
|
1890
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
1891
|
+
const contentRect = content.getBoundingClientRect();
|
|
1892
|
+
const { x, y, placement } = computePosition(
|
|
1893
|
+
anchorRect,
|
|
1894
|
+
contentRect,
|
|
1895
|
+
side,
|
|
1896
|
+
align,
|
|
1897
|
+
sideOffset,
|
|
1898
|
+
alignOffset
|
|
1899
|
+
);
|
|
1900
|
+
content.style.position = strategy;
|
|
1901
|
+
content.style.left = `${Math.round(x)}px`;
|
|
1902
|
+
content.style.top = `${Math.round(y)}px`;
|
|
1903
|
+
content.dataset.placement = placement;
|
|
1904
|
+
context.setPlacement(placement);
|
|
1905
|
+
};
|
|
1906
|
+
createEffect2(() => {
|
|
1907
|
+
context.anchor();
|
|
1908
|
+
context.content();
|
|
1909
|
+
update();
|
|
1910
|
+
});
|
|
1911
|
+
const onWindowUpdate = () => update();
|
|
1912
|
+
const targetDocument = () => context.anchor()?.ownerDocument ?? (typeof document !== "undefined" ? document : null);
|
|
1913
|
+
const targetWindow = () => targetDocument()?.defaultView ?? null;
|
|
1914
|
+
useEventListener3(targetWindow, "resize", onWindowUpdate);
|
|
1915
|
+
useEventListener3(targetDocument, "scroll", onWindowUpdate, { capture: true });
|
|
1916
|
+
return {
|
|
1917
|
+
type: "div",
|
|
1918
|
+
props: {
|
|
1919
|
+
...props,
|
|
1920
|
+
side: void 0,
|
|
1921
|
+
align: void 0,
|
|
1922
|
+
sideOffset: void 0,
|
|
1923
|
+
alignOffset: void 0,
|
|
1924
|
+
strategy: void 0,
|
|
1925
|
+
id: contentId,
|
|
1926
|
+
ref: (node) => {
|
|
1927
|
+
context.setContent(node);
|
|
1928
|
+
if (typeof props.ref === "function") {
|
|
1929
|
+
props.ref(node);
|
|
1930
|
+
}
|
|
1931
|
+
},
|
|
1932
|
+
role: props.role ?? "dialog",
|
|
1933
|
+
"data-popper-content": "",
|
|
1934
|
+
"data-placement": () => context.placement(),
|
|
1935
|
+
children: props.children
|
|
1936
|
+
}
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1939
|
+
function PopperArrow(props) {
|
|
1940
|
+
const size = props.size ?? 8;
|
|
1941
|
+
return {
|
|
1942
|
+
type: "span",
|
|
1943
|
+
props: {
|
|
1944
|
+
...props,
|
|
1945
|
+
size: void 0,
|
|
1946
|
+
"data-popper-arrow": "",
|
|
1947
|
+
style: {
|
|
1948
|
+
width: `${size}px`,
|
|
1949
|
+
height: `${size}px`,
|
|
1950
|
+
display: "inline-block",
|
|
1951
|
+
transform: "rotate(45deg)",
|
|
1952
|
+
...typeof props.style === "object" && props.style !== null ? props.style : {}
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
// src/components/overlay/popover.ts
|
|
1959
|
+
var PopoverContext = createContext12(null);
|
|
1960
|
+
function usePopoverContext(component) {
|
|
1961
|
+
const context = useContext12(PopoverContext);
|
|
1962
|
+
if (!context) {
|
|
1963
|
+
throw new Error(`${component} must be used inside PopoverRoot`);
|
|
1964
|
+
}
|
|
1965
|
+
return context;
|
|
1966
|
+
}
|
|
1967
|
+
function PopoverRoot(props) {
|
|
1968
|
+
const openState = createControllableState({
|
|
1969
|
+
value: props.open,
|
|
1970
|
+
defaultValue: props.defaultOpen ?? false,
|
|
1971
|
+
onChange: props.onOpenChange
|
|
1972
|
+
});
|
|
1973
|
+
const baseId = useId(props.id, "popover");
|
|
1974
|
+
const context = {
|
|
1975
|
+
open: () => openState.get(),
|
|
1976
|
+
setOpen: (value) => openState.set(value),
|
|
1977
|
+
contentId: `${baseId}-content`,
|
|
1978
|
+
triggerRef: createRef()
|
|
1979
|
+
};
|
|
1980
|
+
return {
|
|
1981
|
+
type: PopoverContext.Provider,
|
|
1982
|
+
props: {
|
|
1983
|
+
value: context,
|
|
1984
|
+
children: {
|
|
1985
|
+
type: PopperRoot,
|
|
1986
|
+
props: {
|
|
1987
|
+
children: props.children
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
};
|
|
1992
|
+
}
|
|
1993
|
+
function PopoverTrigger(props) {
|
|
1994
|
+
const context = usePopoverContext("PopoverTrigger");
|
|
1995
|
+
const tag = props.as ?? "button";
|
|
1996
|
+
const refProp = props.ref;
|
|
1997
|
+
return {
|
|
1998
|
+
type: PopperAnchor,
|
|
1999
|
+
props: {
|
|
2000
|
+
children: Primitive({
|
|
2001
|
+
...props,
|
|
2002
|
+
as: tag,
|
|
2003
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
2004
|
+
ref: composeRefs(context.triggerRef, refProp),
|
|
2005
|
+
"aria-haspopup": props["aria-haspopup"] ?? "dialog",
|
|
2006
|
+
"aria-expanded": () => context.open(),
|
|
2007
|
+
"aria-controls": context.contentId,
|
|
2008
|
+
"data-state": () => context.open() ? "open" : "closed",
|
|
2009
|
+
onClick: (event) => {
|
|
2010
|
+
props.onClick?.(event);
|
|
2011
|
+
if (event.defaultPrevented) return;
|
|
2012
|
+
context.setOpen(!context.open());
|
|
2013
|
+
},
|
|
2014
|
+
children: props.children
|
|
2015
|
+
})
|
|
2016
|
+
}
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
function buildPopoverContent(context, props) {
|
|
2020
|
+
return {
|
|
2021
|
+
type: DismissableLayer,
|
|
2022
|
+
props: {
|
|
2023
|
+
onEscapeKeyDown: props.onEscapeKeyDown,
|
|
2024
|
+
onInteractOutside: props.onInteractOutside,
|
|
2025
|
+
onPointerDownOutside: props.onPointerDownOutside,
|
|
2026
|
+
onFocusOutside: props.onFocusOutside,
|
|
2027
|
+
onDismiss: () => {
|
|
2028
|
+
props.onDismiss?.();
|
|
2029
|
+
context.setOpen(false);
|
|
2030
|
+
},
|
|
2031
|
+
children: {
|
|
2032
|
+
type: PopperContent,
|
|
2033
|
+
props: {
|
|
2034
|
+
...props,
|
|
2035
|
+
portal: void 0,
|
|
2036
|
+
forceMount: void 0,
|
|
2037
|
+
onDismiss: void 0,
|
|
2038
|
+
onEscapeKeyDown: void 0,
|
|
2039
|
+
onInteractOutside: void 0,
|
|
2040
|
+
onPointerDownOutside: void 0,
|
|
2041
|
+
onFocusOutside: void 0,
|
|
2042
|
+
id: props.id ?? context.contentId,
|
|
2043
|
+
role: props.role ?? "dialog",
|
|
2044
|
+
"data-popover-content": "",
|
|
2045
|
+
"data-state": () => context.open() ? "open" : "closed",
|
|
2046
|
+
children: props.children
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
};
|
|
2051
|
+
}
|
|
2052
|
+
function PopoverContent(props) {
|
|
2053
|
+
const context = usePopoverContext("PopoverContent");
|
|
2054
|
+
const content = buildPopoverContent(context, props);
|
|
2055
|
+
const child = () => context.open() || props.forceMount ? content : null;
|
|
2056
|
+
if (props.portal ?? true) {
|
|
2057
|
+
return {
|
|
2058
|
+
type: Portal,
|
|
2059
|
+
props: {
|
|
2060
|
+
children: child
|
|
2061
|
+
}
|
|
2062
|
+
};
|
|
2063
|
+
}
|
|
2064
|
+
return {
|
|
2065
|
+
type: "div",
|
|
2066
|
+
props: {
|
|
2067
|
+
"data-popover-inline-container": "",
|
|
2068
|
+
children: child
|
|
2069
|
+
}
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
function PopoverClose(props) {
|
|
2073
|
+
const context = usePopoverContext("PopoverClose");
|
|
2074
|
+
const tag = props.as ?? "button";
|
|
2075
|
+
return Primitive({
|
|
2076
|
+
...props,
|
|
2077
|
+
as: tag,
|
|
2078
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
2079
|
+
onClick: (event) => {
|
|
2080
|
+
props.onClick?.(event);
|
|
2081
|
+
if (event.defaultPrevented) return;
|
|
2082
|
+
context.setOpen(false);
|
|
2083
|
+
},
|
|
2084
|
+
children: props.children
|
|
2085
|
+
});
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
// src/components/form/date-picker.ts
|
|
2089
|
+
function toDate2(value) {
|
|
2090
|
+
if (value === null || value === void 0) return null;
|
|
2091
|
+
const next = value instanceof Date ? new Date(value.getTime()) : new Date(value);
|
|
2092
|
+
return Number.isNaN(next.getTime()) ? null : next;
|
|
2093
|
+
}
|
|
2094
|
+
function sameDay(a, b) {
|
|
2095
|
+
if (!a || !b) return a === b;
|
|
2096
|
+
return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
2097
|
+
}
|
|
2098
|
+
var DatePickerContext = createContext13(null);
|
|
2099
|
+
function useDatePickerContext(component) {
|
|
2100
|
+
const context = useContext13(DatePickerContext);
|
|
2101
|
+
if (!context) {
|
|
2102
|
+
throw new Error(`${component} must be used inside DatePickerRoot`);
|
|
2103
|
+
}
|
|
2104
|
+
return context;
|
|
2105
|
+
}
|
|
2106
|
+
function DatePickerRoot(props) {
|
|
2107
|
+
const baseId = useId(props.id, "date-picker");
|
|
2108
|
+
const valueState = createControllableState({
|
|
2109
|
+
value: props.value === void 0 ? void 0 : (() => {
|
|
2110
|
+
const next = toDate2(read(props.value, void 0));
|
|
2111
|
+
return next ? new Date(next.getFullYear(), next.getMonth(), next.getDate()) : null;
|
|
2112
|
+
}),
|
|
2113
|
+
defaultValue: toDate2(props.defaultValue),
|
|
2114
|
+
onChange: props.onValueChange,
|
|
2115
|
+
equals: (a, b) => sameDay(a, b)
|
|
2116
|
+
});
|
|
2117
|
+
const openState = createControllableState({
|
|
2118
|
+
value: props.open,
|
|
2119
|
+
defaultValue: props.defaultOpen ?? false,
|
|
2120
|
+
onChange: props.onOpenChange
|
|
2121
|
+
});
|
|
2122
|
+
const context = {
|
|
2123
|
+
baseId,
|
|
2124
|
+
value: () => valueState.get(),
|
|
2125
|
+
setValue: (value) => valueState.set(value),
|
|
2126
|
+
open: () => openState.get(),
|
|
2127
|
+
setOpen: (open) => openState.set(open),
|
|
2128
|
+
locale: () => read(props.locale, "en-US")
|
|
2129
|
+
};
|
|
2130
|
+
return {
|
|
2131
|
+
type: DatePickerContext.Provider,
|
|
2132
|
+
props: {
|
|
2133
|
+
value: context,
|
|
2134
|
+
children: {
|
|
2135
|
+
type: PopoverRoot,
|
|
2136
|
+
props: {
|
|
2137
|
+
id: baseId,
|
|
2138
|
+
open: () => context.open(),
|
|
2139
|
+
onOpenChange: (open) => context.setOpen(open),
|
|
2140
|
+
children: props.children
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
};
|
|
2145
|
+
}
|
|
2146
|
+
function DatePickerTrigger(props) {
|
|
2147
|
+
return {
|
|
2148
|
+
type: PopoverTrigger,
|
|
2149
|
+
props: {
|
|
2150
|
+
...props,
|
|
2151
|
+
"data-date-picker-trigger": "",
|
|
2152
|
+
children: props.children ?? { type: DatePickerValue, props: {} }
|
|
2153
|
+
}
|
|
2154
|
+
};
|
|
2155
|
+
}
|
|
2156
|
+
function DatePickerValue(props) {
|
|
2157
|
+
const context = useDatePickerContext("DatePickerValue");
|
|
2158
|
+
return {
|
|
2159
|
+
type: "span",
|
|
2160
|
+
props: {
|
|
2161
|
+
...props,
|
|
2162
|
+
locale: void 0,
|
|
2163
|
+
formatOptions: void 0,
|
|
2164
|
+
placeholder: void 0,
|
|
2165
|
+
"data-date-picker-value": "",
|
|
2166
|
+
children: props.children ?? (() => {
|
|
2167
|
+
const value = context.value();
|
|
2168
|
+
if (!value) {
|
|
2169
|
+
return props.placeholder ?? "";
|
|
2170
|
+
}
|
|
2171
|
+
return new Intl.DateTimeFormat(props.locale ?? context.locale(), props.formatOptions ?? { dateStyle: "medium" }).format(value);
|
|
2172
|
+
})
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2176
|
+
function DatePickerContent(props) {
|
|
2177
|
+
const context = useDatePickerContext("DatePickerContent");
|
|
2178
|
+
return {
|
|
2179
|
+
type: PopoverContent,
|
|
2180
|
+
props: {
|
|
2181
|
+
...props,
|
|
2182
|
+
closeOnSelect: void 0,
|
|
2183
|
+
id: props.id ?? `${context.baseId}-content`,
|
|
2184
|
+
role: props.role ?? "dialog",
|
|
2185
|
+
"data-date-picker-content": "",
|
|
2186
|
+
children: props.children ?? {
|
|
2187
|
+
type: DatePickerCalendar,
|
|
2188
|
+
props: {
|
|
2189
|
+
closeOnSelect: props.closeOnSelect
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
};
|
|
2194
|
+
}
|
|
2195
|
+
function DatePickerCalendar(props) {
|
|
2196
|
+
const context = useDatePickerContext("DatePickerCalendar");
|
|
2197
|
+
return {
|
|
2198
|
+
type: CalendarRoot,
|
|
2199
|
+
props: {
|
|
2200
|
+
...props,
|
|
2201
|
+
closeOnSelect: void 0,
|
|
2202
|
+
locale: props.locale ?? context.locale,
|
|
2203
|
+
value: () => context.value(),
|
|
2204
|
+
onValueChange: (value) => {
|
|
2205
|
+
props.onValueChange?.(value);
|
|
2206
|
+
context.setValue(value);
|
|
2207
|
+
if ((props.closeOnSelect ?? true) && value) {
|
|
2208
|
+
context.setOpen(false);
|
|
2209
|
+
}
|
|
2210
|
+
},
|
|
2211
|
+
"data-date-picker-calendar": "",
|
|
2212
|
+
children: props.children
|
|
2213
|
+
}
|
|
2214
|
+
};
|
|
2215
|
+
}
|
|
2216
|
+
|
|
2217
|
+
// src/components/form/select.ts
|
|
2218
|
+
import { createContext as createContext14, useContext as useContext14 } from "@fictjs/runtime";
|
|
2219
|
+
var SelectContext = createContext14(null);
|
|
2220
|
+
function useSelectContext(component) {
|
|
2221
|
+
const context = useContext14(SelectContext);
|
|
2222
|
+
if (!context) {
|
|
2223
|
+
throw new Error(`${component} must be used inside SelectRoot`);
|
|
2224
|
+
}
|
|
2225
|
+
return context;
|
|
2226
|
+
}
|
|
2227
|
+
function SelectRoot(props) {
|
|
2228
|
+
const valueState = createControllableState({
|
|
2229
|
+
value: props.value,
|
|
2230
|
+
defaultValue: props.defaultValue ?? "",
|
|
2231
|
+
onChange: props.onValueChange
|
|
2232
|
+
});
|
|
2233
|
+
const openState = createControllableState({
|
|
2234
|
+
value: props.open,
|
|
2235
|
+
defaultValue: props.defaultOpen ?? false,
|
|
2236
|
+
onChange: props.onOpenChange
|
|
2237
|
+
});
|
|
2238
|
+
const context = {
|
|
2239
|
+
value: () => valueState.get(),
|
|
2240
|
+
setValue: (value) => valueState.set(value),
|
|
2241
|
+
open: () => openState.get(),
|
|
2242
|
+
setOpen: (open) => openState.set(open),
|
|
2243
|
+
disabled: () => props.disabled ?? false
|
|
2244
|
+
};
|
|
2245
|
+
return {
|
|
2246
|
+
type: SelectContext.Provider,
|
|
2247
|
+
props: {
|
|
2248
|
+
value: context,
|
|
2249
|
+
children: props.children
|
|
2250
|
+
}
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
function SelectTrigger(props) {
|
|
2254
|
+
const context = useSelectContext("SelectTrigger");
|
|
2255
|
+
const tag = props.as ?? "button";
|
|
2256
|
+
return Primitive({
|
|
2257
|
+
...props,
|
|
2258
|
+
as: tag,
|
|
2259
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
2260
|
+
disabled: () => context.disabled(),
|
|
2261
|
+
"aria-haspopup": "listbox",
|
|
2262
|
+
"aria-expanded": () => context.open(),
|
|
2263
|
+
"data-state": () => context.open() ? "open" : "closed",
|
|
2264
|
+
"data-select-trigger": "",
|
|
2265
|
+
onClick: (event) => {
|
|
2266
|
+
;
|
|
2267
|
+
props.onClick?.(event);
|
|
2268
|
+
if (event.defaultPrevented || context.disabled()) return;
|
|
2269
|
+
context.setOpen(!context.open());
|
|
2270
|
+
},
|
|
2271
|
+
children: props.children
|
|
2272
|
+
});
|
|
2273
|
+
}
|
|
2274
|
+
function SelectValue(props) {
|
|
2275
|
+
const context = useSelectContext("SelectValue");
|
|
2276
|
+
return {
|
|
2277
|
+
type: "span",
|
|
2278
|
+
props: {
|
|
2279
|
+
...props,
|
|
2280
|
+
"data-select-value": "",
|
|
2281
|
+
children: () => context.value() || props.placeholder || ""
|
|
2282
|
+
}
|
|
2283
|
+
};
|
|
2284
|
+
}
|
|
2285
|
+
function SelectContent(props) {
|
|
2286
|
+
const context = useSelectContext("SelectContent");
|
|
2287
|
+
return {
|
|
2288
|
+
type: "div",
|
|
2289
|
+
props: {
|
|
2290
|
+
"data-select-content-wrapper": "",
|
|
2291
|
+
children: () => context.open() || props.forceMount ? {
|
|
2292
|
+
type: "div",
|
|
2293
|
+
props: {
|
|
2294
|
+
...props,
|
|
2295
|
+
forceMount: void 0,
|
|
2296
|
+
role: "listbox",
|
|
2297
|
+
"data-select-content": "",
|
|
2298
|
+
children: props.children
|
|
2299
|
+
}
|
|
2300
|
+
} : null
|
|
2301
|
+
}
|
|
2302
|
+
};
|
|
2303
|
+
}
|
|
2304
|
+
function SelectItem(props) {
|
|
2305
|
+
const context = useSelectContext("SelectItem");
|
|
2306
|
+
const selected = () => context.value() === props.value;
|
|
2307
|
+
const tag = props.as ?? "button";
|
|
2308
|
+
return Primitive({
|
|
2309
|
+
...props,
|
|
2310
|
+
as: tag,
|
|
2311
|
+
type: !props.asChild && tag === "button" ? "button" : props.type,
|
|
2312
|
+
role: "option",
|
|
2313
|
+
"aria-selected": selected,
|
|
2314
|
+
"data-state": () => selected() ? "checked" : "unchecked",
|
|
2315
|
+
"data-select-item": props.value,
|
|
2316
|
+
onClick: (event) => {
|
|
2317
|
+
;
|
|
2318
|
+
props.onClick?.(event);
|
|
2319
|
+
if (event.defaultPrevented) return;
|
|
2320
|
+
context.setValue(props.value);
|
|
2321
|
+
context.setOpen(false);
|
|
2322
|
+
},
|
|
2323
|
+
children: props.children
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
// src/components/form/combobox.ts
|
|
2328
|
+
import { createContext as createContext15, useContext as useContext15 } from "@fictjs/runtime";
|
|
2329
|
+
import { createSignal as createSignal6 } from "@fictjs/runtime/advanced";
|
|
2330
|
+
var ComboboxContext = createContext15(null);
|
|
2331
|
+
function useComboboxContext(component) {
|
|
2332
|
+
const context = useContext15(ComboboxContext);
|
|
2333
|
+
if (!context) {
|
|
2334
|
+
throw new Error(`${component} must be used inside ComboboxRoot`);
|
|
2335
|
+
}
|
|
2336
|
+
return context;
|
|
2337
|
+
}
|
|
2338
|
+
function ComboboxRoot(props) {
|
|
2339
|
+
const valueState = createControllableState({
|
|
2340
|
+
value: props.value,
|
|
2341
|
+
defaultValue: props.defaultValue ?? "",
|
|
2342
|
+
onChange: props.onValueChange
|
|
2343
|
+
});
|
|
2344
|
+
const openState = createControllableState({
|
|
2345
|
+
value: props.open,
|
|
2346
|
+
defaultValue: props.defaultOpen ?? false,
|
|
2347
|
+
onChange: props.onOpenChange
|
|
2348
|
+
});
|
|
2349
|
+
const querySignal = createSignal6("");
|
|
2350
|
+
const context = {
|
|
2351
|
+
value: () => valueState.get(),
|
|
2352
|
+
setValue: (value) => valueState.set(value),
|
|
2353
|
+
open: () => openState.get(),
|
|
2354
|
+
setOpen: (open) => openState.set(open),
|
|
2355
|
+
query: () => querySignal(),
|
|
2356
|
+
setQuery: (query) => querySignal(query)
|
|
2357
|
+
};
|
|
2358
|
+
return {
|
|
2359
|
+
type: ComboboxContext.Provider,
|
|
2360
|
+
props: {
|
|
2361
|
+
value: context,
|
|
2362
|
+
children: props.children
|
|
2363
|
+
}
|
|
2364
|
+
};
|
|
2365
|
+
}
|
|
2366
|
+
function ComboboxInput(props) {
|
|
2367
|
+
const context = useComboboxContext("ComboboxInput");
|
|
2368
|
+
return {
|
|
2369
|
+
type: "input",
|
|
2370
|
+
props: {
|
|
2371
|
+
...props,
|
|
2372
|
+
type: "text",
|
|
2373
|
+
role: "combobox",
|
|
2374
|
+
"aria-expanded": () => context.open(),
|
|
2375
|
+
"aria-autocomplete": "list",
|
|
2376
|
+
value: () => context.query() || context.value(),
|
|
2377
|
+
"data-combobox-input": "",
|
|
2378
|
+
onFocus: (event) => {
|
|
2379
|
+
;
|
|
2380
|
+
props.onFocus?.(event);
|
|
2381
|
+
context.setOpen(true);
|
|
2382
|
+
},
|
|
2383
|
+
onInput: (event) => {
|
|
2384
|
+
;
|
|
2385
|
+
props.onInput?.(event);
|
|
2386
|
+
const target = event.target;
|
|
2387
|
+
if (!target) return;
|
|
2388
|
+
context.setQuery(target.value);
|
|
2389
|
+
context.setOpen(true);
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
};
|
|
2393
|
+
}
|
|
2394
|
+
function ComboboxList(props) {
|
|
2395
|
+
const context = useComboboxContext("ComboboxList");
|
|
2396
|
+
return {
|
|
2397
|
+
type: "div",
|
|
2398
|
+
props: {
|
|
2399
|
+
"data-combobox-list-wrapper": "",
|
|
2400
|
+
children: () => context.open() || props.forceMount ? {
|
|
2401
|
+
type: "div",
|
|
2402
|
+
props: {
|
|
2403
|
+
...props,
|
|
2404
|
+
forceMount: void 0,
|
|
2405
|
+
role: "listbox",
|
|
2406
|
+
"data-combobox-list": "",
|
|
2407
|
+
children: props.children
|
|
2408
|
+
}
|
|
2409
|
+
} : null
|
|
2410
|
+
}
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2413
|
+
function ComboboxItem(props) {
|
|
2414
|
+
const context = useComboboxContext("ComboboxItem");
|
|
2415
|
+
const tag = props.as ?? "button";
|
|
2416
|
+
const matches = () => {
|
|
2417
|
+
const query = context.query().trim().toLowerCase();
|
|
2418
|
+
if (!query) return true;
|
|
2419
|
+
const text = typeof props.children === "string" ? props.children : Array.isArray(props.children) ? props.children.join(" ") : props.value;
|
|
2420
|
+
return String(text).toLowerCase().includes(query);
|
|
2421
|
+
};
|
|
2422
|
+
return {
|
|
2423
|
+
type: "div",
|
|
2424
|
+
props: {
|
|
2425
|
+
"data-combobox-item-wrapper": props.value,
|
|
2426
|
+
children: () => matches() ? Primitive({
|
|
2427
|
+
...props,
|
|
2428
|
+
as: tag,
|
|
2429
|
+
type: !props.asChild && tag === "button" ? "button" : props.type,
|
|
2430
|
+
role: "option",
|
|
2431
|
+
"data-combobox-item": props.value,
|
|
2432
|
+
onClick: (event) => {
|
|
2433
|
+
;
|
|
2434
|
+
props.onClick?.(event);
|
|
2435
|
+
if (event.defaultPrevented) return;
|
|
2436
|
+
context.setValue(props.value);
|
|
2437
|
+
context.setQuery(props.value);
|
|
2438
|
+
context.setOpen(false);
|
|
2439
|
+
},
|
|
2440
|
+
children: props.children
|
|
2441
|
+
}) : null
|
|
2442
|
+
}
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2446
|
+
// src/components/form/form-field.ts
|
|
2447
|
+
import { createContext as createContext16, useContext as useContext16 } from "@fictjs/runtime";
|
|
2448
|
+
var FieldContext = createContext16(null);
|
|
2449
|
+
function useFieldContext(component) {
|
|
2450
|
+
const context = useContext16(FieldContext);
|
|
2451
|
+
if (!context) {
|
|
2452
|
+
throw new Error(`${component} must be used inside FormField`);
|
|
2453
|
+
}
|
|
2454
|
+
return context;
|
|
2455
|
+
}
|
|
2456
|
+
function Form(props) {
|
|
2457
|
+
return {
|
|
2458
|
+
type: "form",
|
|
2459
|
+
props: {
|
|
2460
|
+
...props,
|
|
2461
|
+
"data-form": "",
|
|
2462
|
+
children: props.children
|
|
2463
|
+
}
|
|
2464
|
+
};
|
|
2465
|
+
}
|
|
2466
|
+
function FormField(props) {
|
|
2467
|
+
const fieldId = useId(props.id ?? (props.name ? `field-${props.name}` : void 0), "field");
|
|
2468
|
+
const context = {
|
|
2469
|
+
controlId: fieldId,
|
|
2470
|
+
descriptionId: `${fieldId}-description`,
|
|
2471
|
+
messageId: `${fieldId}-message`
|
|
2472
|
+
};
|
|
2473
|
+
return {
|
|
2474
|
+
type: FieldContext.Provider,
|
|
2475
|
+
props: {
|
|
2476
|
+
value: context,
|
|
2477
|
+
children: props.children
|
|
2478
|
+
}
|
|
2479
|
+
};
|
|
2480
|
+
}
|
|
2481
|
+
function FormLabel(props) {
|
|
2482
|
+
const field = useFieldContext("FormLabel");
|
|
2483
|
+
return {
|
|
2484
|
+
type: Label,
|
|
2485
|
+
props: {
|
|
2486
|
+
...props,
|
|
2487
|
+
htmlFor: props.htmlFor ?? field.controlId,
|
|
2488
|
+
"data-form-label": "",
|
|
2489
|
+
children: props.children
|
|
2490
|
+
}
|
|
2491
|
+
};
|
|
2492
|
+
}
|
|
2493
|
+
function FormControl(props) {
|
|
2494
|
+
const field = useFieldContext("FormControl");
|
|
2495
|
+
const tag = props.as ?? "input";
|
|
2496
|
+
return {
|
|
2497
|
+
type: tag,
|
|
2498
|
+
props: {
|
|
2499
|
+
...props,
|
|
2500
|
+
as: void 0,
|
|
2501
|
+
id: props.id ?? field.controlId,
|
|
2502
|
+
"aria-describedby": `${field.descriptionId} ${field.messageId}`,
|
|
2503
|
+
"data-form-control": "",
|
|
2504
|
+
children: props.children
|
|
2505
|
+
}
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
function FormDescription(props) {
|
|
2509
|
+
const field = useFieldContext("FormDescription");
|
|
2510
|
+
return {
|
|
2511
|
+
type: "p",
|
|
2512
|
+
props: {
|
|
2513
|
+
...props,
|
|
2514
|
+
id: props.id ?? field.descriptionId,
|
|
2515
|
+
"data-form-description": "",
|
|
2516
|
+
children: props.children
|
|
2517
|
+
}
|
|
2518
|
+
};
|
|
2519
|
+
}
|
|
2520
|
+
function FormMessage(props) {
|
|
2521
|
+
const field = useFieldContext("FormMessage");
|
|
2522
|
+
return {
|
|
2523
|
+
type: "p",
|
|
2524
|
+
props: {
|
|
2525
|
+
...props,
|
|
2526
|
+
id: props.id ?? field.messageId,
|
|
2527
|
+
role: props.role ?? "alert",
|
|
2528
|
+
"data-form-message": "",
|
|
2529
|
+
children: props.children
|
|
2530
|
+
}
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
// src/components/interaction/focus-scope.ts
|
|
2535
|
+
import { onDestroy as onDestroy2, onMount as onMount3 } from "@fictjs/runtime";
|
|
2536
|
+
import { createSignal as createSignal7 } from "@fictjs/runtime/advanced";
|
|
2537
|
+
import { useEventListener as useEventListener4 } from "@fictjs/hooks";
|
|
2538
|
+
|
|
2539
|
+
// src/internal/dom.ts
|
|
2540
|
+
function getFocusableCandidates(container) {
|
|
2541
|
+
const selectors = [
|
|
2542
|
+
"a[href]",
|
|
2543
|
+
"button:not([disabled])",
|
|
2544
|
+
"input:not([disabled])",
|
|
2545
|
+
"select:not([disabled])",
|
|
2546
|
+
"textarea:not([disabled])",
|
|
2547
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
2548
|
+
'[contenteditable="true"]'
|
|
2549
|
+
];
|
|
2550
|
+
return Array.from(container.querySelectorAll(selectors.join(","))).filter((element) => {
|
|
2551
|
+
if (element.hasAttribute("disabled")) return false;
|
|
2552
|
+
if (element.getAttribute("aria-hidden") === "true") return false;
|
|
2553
|
+
if (element.tabIndex < 0) return false;
|
|
2554
|
+
const style = window.getComputedStyle(element);
|
|
2555
|
+
return style.display !== "none" && style.visibility !== "hidden";
|
|
2556
|
+
});
|
|
2557
|
+
}
|
|
2558
|
+
function focusFirst(container) {
|
|
2559
|
+
const [first] = getFocusableCandidates(container);
|
|
2560
|
+
first?.focus();
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
// src/components/interaction/focus-scope.ts
|
|
2564
|
+
function FocusScope(props) {
|
|
2565
|
+
const node = createSignal7(null);
|
|
2566
|
+
let previousFocused = null;
|
|
2567
|
+
const onKeyDown = (event) => {
|
|
2568
|
+
if (event.key !== "Tab") return;
|
|
2569
|
+
const scope = node();
|
|
2570
|
+
if (!scope) return;
|
|
2571
|
+
const trapped = read(props.trapped, true);
|
|
2572
|
+
if (!trapped) return;
|
|
2573
|
+
const candidates = getFocusableCandidates(scope);
|
|
2574
|
+
if (candidates.length === 0) {
|
|
2575
|
+
event.preventDefault();
|
|
2576
|
+
return;
|
|
2577
|
+
}
|
|
2578
|
+
const first = candidates[0];
|
|
2579
|
+
const last = candidates[candidates.length - 1];
|
|
2580
|
+
const active = (scope.ownerDocument ?? document).activeElement;
|
|
2581
|
+
if (event.shiftKey) {
|
|
2582
|
+
if (active === first || !scope.contains(active)) {
|
|
2583
|
+
if (read(props.loop, true)) {
|
|
2584
|
+
event.preventDefault();
|
|
2585
|
+
last.focus();
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
return;
|
|
2589
|
+
}
|
|
2590
|
+
if (active === last) {
|
|
2591
|
+
if (read(props.loop, true)) {
|
|
2592
|
+
event.preventDefault();
|
|
2593
|
+
first.focus();
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
};
|
|
2597
|
+
const targetDocument = () => node()?.ownerDocument ?? (typeof document !== "undefined" ? document : null);
|
|
2598
|
+
useEventListener4(targetDocument, "keydown", onKeyDown, { capture: true });
|
|
2599
|
+
onMount3(() => {
|
|
2600
|
+
const scope = node();
|
|
2601
|
+
if (!scope) return;
|
|
2602
|
+
const doc = targetDocument();
|
|
2603
|
+
if (!doc) return;
|
|
2604
|
+
previousFocused = doc.activeElement;
|
|
2605
|
+
if (read(props.autoFocus, true)) {
|
|
2606
|
+
const mountEvent = new CustomEvent("fict-focus-scope.mount");
|
|
2607
|
+
props.onMountAutoFocus?.(mountEvent);
|
|
2608
|
+
if (!mountEvent.defaultPrevented) {
|
|
2609
|
+
focusFirst(scope);
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
});
|
|
2613
|
+
onDestroy2(() => {
|
|
2614
|
+
if (read(props.restoreFocus, true) && previousFocused instanceof HTMLElement) {
|
|
2615
|
+
const unmountEvent = new CustomEvent("fict-focus-scope.unmount");
|
|
2616
|
+
props.onUnmountAutoFocus?.(unmountEvent);
|
|
2617
|
+
if (!unmountEvent.defaultPrevented) {
|
|
2618
|
+
previousFocused.focus();
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
});
|
|
2622
|
+
return {
|
|
2623
|
+
type: "div",
|
|
2624
|
+
props: {
|
|
2625
|
+
...props,
|
|
2626
|
+
trapped: void 0,
|
|
2627
|
+
loop: void 0,
|
|
2628
|
+
autoFocus: void 0,
|
|
2629
|
+
restoreFocus: void 0,
|
|
2630
|
+
onMountAutoFocus: void 0,
|
|
2631
|
+
onUnmountAutoFocus: void 0,
|
|
2632
|
+
ref: (el) => {
|
|
2633
|
+
node(el);
|
|
2634
|
+
},
|
|
2635
|
+
"data-focus-scope": "",
|
|
2636
|
+
children: props.children
|
|
2637
|
+
}
|
|
2638
|
+
};
|
|
2639
|
+
}
|
|
2640
|
+
function FocusTrap(props) {
|
|
2641
|
+
return {
|
|
2642
|
+
type: FocusScope,
|
|
2643
|
+
props: {
|
|
2644
|
+
...props,
|
|
2645
|
+
trapped: true
|
|
2646
|
+
}
|
|
2647
|
+
};
|
|
2648
|
+
}
|
|
2649
|
+
|
|
2650
|
+
// src/components/interaction/scroll-lock.ts
|
|
2651
|
+
import { createEffect as createEffect3, onDestroy as onDestroy3 } from "@fictjs/runtime";
|
|
2652
|
+
var lockCount = 0;
|
|
2653
|
+
var previousOverflow = "";
|
|
2654
|
+
var previousPaddingRight = "";
|
|
2655
|
+
function lockBodyScroll() {
|
|
2656
|
+
if (typeof document === "undefined") return;
|
|
2657
|
+
if (lockCount === 0) {
|
|
2658
|
+
const body = document.body;
|
|
2659
|
+
previousOverflow = body.style.overflow;
|
|
2660
|
+
previousPaddingRight = body.style.paddingRight;
|
|
2661
|
+
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
2662
|
+
body.style.overflow = "hidden";
|
|
2663
|
+
if (scrollbarWidth > 0) {
|
|
2664
|
+
body.style.paddingRight = `${scrollbarWidth}px`;
|
|
2665
|
+
}
|
|
2666
|
+
}
|
|
2667
|
+
lockCount += 1;
|
|
2668
|
+
}
|
|
2669
|
+
function unlockBodyScroll() {
|
|
2670
|
+
if (typeof document === "undefined") return;
|
|
2671
|
+
if (lockCount === 0) return;
|
|
2672
|
+
lockCount -= 1;
|
|
2673
|
+
if (lockCount === 0) {
|
|
2674
|
+
const body = document.body;
|
|
2675
|
+
body.style.overflow = previousOverflow;
|
|
2676
|
+
body.style.paddingRight = previousPaddingRight;
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
function ScrollLock(props) {
|
|
2680
|
+
let locked = false;
|
|
2681
|
+
createEffect3(() => {
|
|
2682
|
+
const enabled = read(props.enabled, true);
|
|
2683
|
+
if (enabled && !locked) {
|
|
2684
|
+
lockBodyScroll();
|
|
2685
|
+
locked = true;
|
|
2686
|
+
return;
|
|
2687
|
+
}
|
|
2688
|
+
if (!enabled && locked) {
|
|
2689
|
+
unlockBodyScroll();
|
|
2690
|
+
locked = false;
|
|
2691
|
+
}
|
|
2692
|
+
});
|
|
2693
|
+
onDestroy3(() => {
|
|
2694
|
+
if (locked) {
|
|
2695
|
+
unlockBodyScroll();
|
|
2696
|
+
locked = false;
|
|
2697
|
+
}
|
|
2698
|
+
});
|
|
2699
|
+
return null;
|
|
2700
|
+
}
|
|
2701
|
+
|
|
2702
|
+
// src/components/interaction/live-region.ts
|
|
2703
|
+
import { createContext as createContext17, useContext as useContext17 } from "@fictjs/runtime";
|
|
2704
|
+
var LiveRegionContext = createContext17(null);
|
|
2705
|
+
function LiveRegionProvider(props) {
|
|
2706
|
+
let politeNode = null;
|
|
2707
|
+
let assertiveNode = null;
|
|
2708
|
+
let pendingPolite = null;
|
|
2709
|
+
let pendingAssertive = null;
|
|
2710
|
+
const writeRegion = (node, message) => {
|
|
2711
|
+
if (!node) return;
|
|
2712
|
+
node.textContent = "";
|
|
2713
|
+
queueMicrotask(() => {
|
|
2714
|
+
if (node) {
|
|
2715
|
+
node.textContent = message;
|
|
2716
|
+
}
|
|
2717
|
+
});
|
|
2718
|
+
};
|
|
2719
|
+
const announce = (message, options) => {
|
|
2720
|
+
const politeness = options?.politeness ?? "polite";
|
|
2721
|
+
if (politeness === "assertive") {
|
|
2722
|
+
if (assertiveNode) {
|
|
2723
|
+
writeRegion(assertiveNode, message);
|
|
2724
|
+
} else {
|
|
2725
|
+
pendingAssertive = message;
|
|
2726
|
+
}
|
|
2727
|
+
return;
|
|
2728
|
+
}
|
|
2729
|
+
if (politeNode) {
|
|
2730
|
+
writeRegion(politeNode, message);
|
|
2731
|
+
} else {
|
|
2732
|
+
pendingPolite = message;
|
|
2733
|
+
}
|
|
2734
|
+
};
|
|
2735
|
+
return {
|
|
2736
|
+
type: LiveRegionContext.Provider,
|
|
2737
|
+
props: {
|
|
2738
|
+
value: { announce },
|
|
2739
|
+
children: [
|
|
2740
|
+
props.children,
|
|
2741
|
+
{
|
|
2742
|
+
type: VisuallyHidden,
|
|
2743
|
+
props: {
|
|
2744
|
+
"aria-live": "polite",
|
|
2745
|
+
"aria-atomic": "true",
|
|
2746
|
+
"data-live-region": "polite",
|
|
2747
|
+
ref: (node) => {
|
|
2748
|
+
politeNode = node;
|
|
2749
|
+
if (node && pendingPolite) {
|
|
2750
|
+
writeRegion(node, pendingPolite);
|
|
2751
|
+
pendingPolite = null;
|
|
2752
|
+
}
|
|
2753
|
+
},
|
|
2754
|
+
children: ""
|
|
2755
|
+
}
|
|
2756
|
+
},
|
|
2757
|
+
{
|
|
2758
|
+
type: VisuallyHidden,
|
|
2759
|
+
props: {
|
|
2760
|
+
"aria-live": "assertive",
|
|
2761
|
+
"aria-atomic": "true",
|
|
2762
|
+
"data-live-region": "assertive",
|
|
2763
|
+
ref: (node) => {
|
|
2764
|
+
assertiveNode = node;
|
|
2765
|
+
if (node && pendingAssertive) {
|
|
2766
|
+
writeRegion(node, pendingAssertive);
|
|
2767
|
+
pendingAssertive = null;
|
|
2768
|
+
}
|
|
2769
|
+
},
|
|
2770
|
+
children: ""
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
]
|
|
2774
|
+
}
|
|
2775
|
+
};
|
|
2776
|
+
}
|
|
2777
|
+
function useAnnouncer() {
|
|
2778
|
+
const context = useContext17(LiveRegionContext);
|
|
2779
|
+
if (!context) {
|
|
2780
|
+
throw new Error("useAnnouncer must be used inside LiveRegionProvider");
|
|
2781
|
+
}
|
|
2782
|
+
return context.announce;
|
|
2783
|
+
}
|
|
2784
|
+
function Announce(props) {
|
|
2785
|
+
const announce = useAnnouncer();
|
|
2786
|
+
if (props.message) {
|
|
2787
|
+
announce(props.message, { politeness: props.politeness });
|
|
2788
|
+
}
|
|
2789
|
+
return null;
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2792
|
+
// src/components/layout/scroll-area.ts
|
|
2793
|
+
import { createContext as createContext18, useContext as useContext18 } from "@fictjs/runtime";
|
|
2794
|
+
var ScrollAreaContext = createContext18(null);
|
|
2795
|
+
function useScrollAreaContext(component) {
|
|
2796
|
+
const context = useContext18(ScrollAreaContext);
|
|
2797
|
+
if (!context) {
|
|
2798
|
+
throw new Error(`${component} must be used inside ScrollArea`);
|
|
2799
|
+
}
|
|
2800
|
+
return context;
|
|
2801
|
+
}
|
|
2802
|
+
function ScrollArea(props) {
|
|
2803
|
+
const context = {
|
|
2804
|
+
viewportRef: { current: null }
|
|
2805
|
+
};
|
|
2806
|
+
return {
|
|
2807
|
+
type: ScrollAreaContext.Provider,
|
|
2808
|
+
props: {
|
|
2809
|
+
value: context,
|
|
2810
|
+
children: {
|
|
2811
|
+
type: "div",
|
|
2812
|
+
props: {
|
|
2813
|
+
...props,
|
|
2814
|
+
"data-scroll-area": "",
|
|
2815
|
+
children: props.children
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
function ScrollAreaViewport(props) {
|
|
2822
|
+
const context = useScrollAreaContext("ScrollAreaViewport");
|
|
2823
|
+
return {
|
|
2824
|
+
type: "div",
|
|
2825
|
+
props: {
|
|
2826
|
+
...props,
|
|
2827
|
+
ref: (node) => {
|
|
2828
|
+
context.viewportRef.current = node;
|
|
2829
|
+
},
|
|
2830
|
+
style: {
|
|
2831
|
+
overflow: "auto",
|
|
2832
|
+
...typeof props.style === "object" && props.style !== null ? props.style : {}
|
|
2833
|
+
},
|
|
2834
|
+
"data-scroll-area-viewport": "",
|
|
2835
|
+
children: props.children
|
|
2836
|
+
}
|
|
2837
|
+
};
|
|
2838
|
+
}
|
|
2839
|
+
function ScrollAreaScrollbar(props) {
|
|
2840
|
+
const orientation = props.orientation ?? "vertical";
|
|
2841
|
+
return {
|
|
2842
|
+
type: "div",
|
|
2843
|
+
props: {
|
|
2844
|
+
...props,
|
|
2845
|
+
orientation: void 0,
|
|
2846
|
+
"data-orientation": orientation,
|
|
2847
|
+
"data-scroll-area-scrollbar": "",
|
|
2848
|
+
children: props.children
|
|
2849
|
+
}
|
|
2850
|
+
};
|
|
2851
|
+
}
|
|
2852
|
+
function ScrollAreaThumb(props) {
|
|
2853
|
+
return {
|
|
2854
|
+
type: "div",
|
|
2855
|
+
props: {
|
|
2856
|
+
...props,
|
|
2857
|
+
"data-scroll-area-thumb": "",
|
|
2858
|
+
children: props.children
|
|
2859
|
+
}
|
|
2860
|
+
};
|
|
2861
|
+
}
|
|
2862
|
+
|
|
2863
|
+
// src/components/layout/resizable.ts
|
|
2864
|
+
import { createSignal as createSignal8 } from "@fictjs/runtime/advanced";
|
|
2865
|
+
import { useEventListener as useEventListener5 } from "@fictjs/hooks";
|
|
2866
|
+
function clamp(value, min, max) {
|
|
2867
|
+
return Math.min(max, Math.max(min, value));
|
|
2868
|
+
}
|
|
2869
|
+
function ResizablePanelGroup(props) {
|
|
2870
|
+
const direction = props.direction ?? "horizontal";
|
|
2871
|
+
return {
|
|
2872
|
+
type: "div",
|
|
2873
|
+
props: {
|
|
2874
|
+
...props,
|
|
2875
|
+
direction: void 0,
|
|
2876
|
+
"data-resizable-panel-group": "",
|
|
2877
|
+
"data-direction": direction,
|
|
2878
|
+
style: {
|
|
2879
|
+
display: "flex",
|
|
2880
|
+
flexDirection: direction === "horizontal" ? "row" : "column",
|
|
2881
|
+
...typeof props.style === "object" && props.style !== null ? props.style : {}
|
|
2882
|
+
},
|
|
2883
|
+
children: props.children
|
|
2884
|
+
}
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
function ResizablePanel(props) {
|
|
2888
|
+
const defaultSize = props.defaultSize ?? 50;
|
|
2889
|
+
return {
|
|
2890
|
+
type: "div",
|
|
2891
|
+
props: {
|
|
2892
|
+
...props,
|
|
2893
|
+
defaultSize: void 0,
|
|
2894
|
+
minSize: void 0,
|
|
2895
|
+
maxSize: void 0,
|
|
2896
|
+
"data-resizable-panel": "",
|
|
2897
|
+
style: {
|
|
2898
|
+
flexBasis: `${defaultSize}%`,
|
|
2899
|
+
flexGrow: 0,
|
|
2900
|
+
flexShrink: 0,
|
|
2901
|
+
overflow: "auto",
|
|
2902
|
+
...typeof props.style === "object" && props.style !== null ? props.style : {}
|
|
2903
|
+
},
|
|
2904
|
+
children: props.children
|
|
2905
|
+
}
|
|
2906
|
+
};
|
|
2907
|
+
}
|
|
2908
|
+
function ResizableHandle(props) {
|
|
2909
|
+
let node = null;
|
|
2910
|
+
const dragState = createSignal8(null);
|
|
2911
|
+
const targetWindow = () => typeof window !== "undefined" ? window : null;
|
|
2912
|
+
const onMove = (moveEvent) => {
|
|
2913
|
+
const state = dragState();
|
|
2914
|
+
if (!state) return;
|
|
2915
|
+
const currentPos = state.direction === "horizontal" ? moveEvent.clientX : moveEvent.clientY;
|
|
2916
|
+
const delta = currentPos - state.startPos;
|
|
2917
|
+
const nextPrevPx = state.prevStart + delta;
|
|
2918
|
+
const nextNextPx = state.nextStart - delta;
|
|
2919
|
+
const nextPrevPercent = clamp(nextPrevPx / state.groupSize * 100, state.minPrev, state.maxPrev);
|
|
2920
|
+
const nextNextPercent = clamp(nextNextPx / state.groupSize * 100, state.minNext, state.maxNext);
|
|
2921
|
+
state.prev.style.flexBasis = `${nextPrevPercent}%`;
|
|
2922
|
+
state.next.style.flexBasis = `${nextNextPercent}%`;
|
|
2923
|
+
};
|
|
2924
|
+
const moveListener = useEventListener5(targetWindow, "pointermove", onMove, {
|
|
2925
|
+
immediate: false
|
|
2926
|
+
});
|
|
2927
|
+
const upListener = useEventListener5(
|
|
2928
|
+
targetWindow,
|
|
2929
|
+
"pointerup",
|
|
2930
|
+
() => {
|
|
2931
|
+
dragState(null);
|
|
2932
|
+
moveListener.stop();
|
|
2933
|
+
upListener.stop();
|
|
2934
|
+
},
|
|
2935
|
+
{ immediate: false }
|
|
2936
|
+
);
|
|
2937
|
+
const onPointerDown = (event) => {
|
|
2938
|
+
if (!node) return;
|
|
2939
|
+
const group = node.parentElement;
|
|
2940
|
+
const prev = node.previousElementSibling;
|
|
2941
|
+
const next = node.nextElementSibling;
|
|
2942
|
+
if (!group || !prev || !next) return;
|
|
2943
|
+
const direction = group.getAttribute("data-direction") ?? "horizontal";
|
|
2944
|
+
const groupRect = group.getBoundingClientRect();
|
|
2945
|
+
const prevRect = prev.getBoundingClientRect();
|
|
2946
|
+
const nextRect = next.getBoundingClientRect();
|
|
2947
|
+
const startPos = direction === "horizontal" ? event.clientX : event.clientY;
|
|
2948
|
+
const prevStart = direction === "horizontal" ? prevRect.width : prevRect.height;
|
|
2949
|
+
const nextStart = direction === "horizontal" ? nextRect.width : nextRect.height;
|
|
2950
|
+
const groupSize = direction === "horizontal" ? groupRect.width : groupRect.height;
|
|
2951
|
+
const minPrev = Number(prev.getAttribute("data-min-size") ?? "5");
|
|
2952
|
+
const minNext = Number(next.getAttribute("data-min-size") ?? "5");
|
|
2953
|
+
const maxPrev = Number(prev.getAttribute("data-max-size") ?? "95");
|
|
2954
|
+
const maxNext = Number(next.getAttribute("data-max-size") ?? "95");
|
|
2955
|
+
dragState({
|
|
2956
|
+
direction,
|
|
2957
|
+
startPos,
|
|
2958
|
+
prevStart,
|
|
2959
|
+
nextStart,
|
|
2960
|
+
groupSize,
|
|
2961
|
+
minPrev,
|
|
2962
|
+
minNext,
|
|
2963
|
+
maxPrev,
|
|
2964
|
+
maxNext,
|
|
2965
|
+
prev,
|
|
2966
|
+
next
|
|
2967
|
+
});
|
|
2968
|
+
moveListener.start();
|
|
2969
|
+
upListener.start();
|
|
2970
|
+
};
|
|
2971
|
+
return {
|
|
2972
|
+
type: "div",
|
|
2973
|
+
props: {
|
|
2974
|
+
...props,
|
|
2975
|
+
withHandle: void 0,
|
|
2976
|
+
ref: (el) => {
|
|
2977
|
+
node = el;
|
|
2978
|
+
},
|
|
2979
|
+
role: "separator",
|
|
2980
|
+
tabIndex: 0,
|
|
2981
|
+
"data-resizable-handle": "",
|
|
2982
|
+
onPointerDown,
|
|
2983
|
+
children: props.withHandle ? {
|
|
2984
|
+
type: "div",
|
|
2985
|
+
props: {
|
|
2986
|
+
"data-resizable-handle-grip": ""
|
|
2987
|
+
}
|
|
2988
|
+
} : null
|
|
2989
|
+
}
|
|
2990
|
+
};
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
// src/components/layout/aspect-ratio.ts
|
|
2994
|
+
function AspectRatio(props) {
|
|
2995
|
+
const ratio = props.ratio ?? 1;
|
|
2996
|
+
const paddingBottom = `${1 / ratio * 100}%`;
|
|
2997
|
+
return {
|
|
2998
|
+
type: "div",
|
|
2999
|
+
props: {
|
|
3000
|
+
...props,
|
|
3001
|
+
ratio: void 0,
|
|
3002
|
+
"data-aspect-ratio": "",
|
|
3003
|
+
style: {
|
|
3004
|
+
position: "relative",
|
|
3005
|
+
width: "100%",
|
|
3006
|
+
paddingBottom,
|
|
3007
|
+
...typeof props.style === "object" && props.style !== null ? props.style : {}
|
|
3008
|
+
},
|
|
3009
|
+
children: {
|
|
3010
|
+
type: "div",
|
|
3011
|
+
props: {
|
|
3012
|
+
style: {
|
|
3013
|
+
position: "absolute",
|
|
3014
|
+
inset: 0
|
|
3015
|
+
},
|
|
3016
|
+
"data-aspect-ratio-content": "",
|
|
3017
|
+
children: props.children
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
};
|
|
3022
|
+
}
|
|
3023
|
+
|
|
3024
|
+
// src/components/layout/progress.ts
|
|
3025
|
+
function Progress(props) {
|
|
3026
|
+
const max = props.max ?? 100;
|
|
3027
|
+
const value = Math.min(max, Math.max(0, props.value ?? 0));
|
|
3028
|
+
return {
|
|
3029
|
+
type: "div",
|
|
3030
|
+
props: {
|
|
3031
|
+
...props,
|
|
3032
|
+
value: void 0,
|
|
3033
|
+
max: void 0,
|
|
3034
|
+
role: "progressbar",
|
|
3035
|
+
"aria-valuemin": 0,
|
|
3036
|
+
"aria-valuemax": max,
|
|
3037
|
+
"aria-valuenow": value,
|
|
3038
|
+
"data-progress": "",
|
|
3039
|
+
children: {
|
|
3040
|
+
type: "div",
|
|
3041
|
+
props: {
|
|
3042
|
+
"data-progress-indicator": "",
|
|
3043
|
+
style: {
|
|
3044
|
+
width: `${value / max * 100}%`
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
3051
|
+
function Meter(props) {
|
|
3052
|
+
const min = props.min ?? 0;
|
|
3053
|
+
const max = props.max ?? 100;
|
|
3054
|
+
const value = Math.min(max, Math.max(min, props.value ?? 0));
|
|
3055
|
+
return {
|
|
3056
|
+
type: "meter",
|
|
3057
|
+
props: {
|
|
3058
|
+
...props,
|
|
3059
|
+
min,
|
|
3060
|
+
max,
|
|
3061
|
+
value,
|
|
3062
|
+
low: props.low,
|
|
3063
|
+
high: props.high,
|
|
3064
|
+
optimum: props.optimum,
|
|
3065
|
+
"data-meter": "",
|
|
3066
|
+
children: props.children
|
|
3067
|
+
}
|
|
3068
|
+
};
|
|
3069
|
+
}
|
|
3070
|
+
|
|
3071
|
+
// src/components/layout/skeleton.ts
|
|
3072
|
+
function Skeleton(props) {
|
|
3073
|
+
const loading = props.loading ?? true;
|
|
3074
|
+
return {
|
|
3075
|
+
type: "div",
|
|
3076
|
+
props: {
|
|
3077
|
+
...props,
|
|
3078
|
+
loading: void 0,
|
|
3079
|
+
"aria-busy": loading ? "true" : "false",
|
|
3080
|
+
"data-skeleton": "",
|
|
3081
|
+
children: loading ? {
|
|
3082
|
+
type: "span",
|
|
3083
|
+
props: {
|
|
3084
|
+
"data-skeleton-placeholder": "",
|
|
3085
|
+
children: props.children ?? null
|
|
3086
|
+
}
|
|
3087
|
+
} : props.children
|
|
3088
|
+
}
|
|
3089
|
+
};
|
|
3090
|
+
}
|
|
3091
|
+
|
|
3092
|
+
// src/components/layout/focus-visible.ts
|
|
3093
|
+
import { createContext as createContext19, useContext as useContext19 } from "@fictjs/runtime";
|
|
3094
|
+
import { createSignal as createSignal9 } from "@fictjs/runtime/advanced";
|
|
3095
|
+
import { useEventListener as useEventListener6 } from "@fictjs/hooks";
|
|
3096
|
+
var KeyboardModeContext = createContext19({
|
|
3097
|
+
isKeyboardMode: () => false
|
|
3098
|
+
});
|
|
3099
|
+
function KeyboardModeProvider(props) {
|
|
3100
|
+
const keyboardModeSignal = createSignal9(false);
|
|
3101
|
+
const targetDocument = () => typeof document !== "undefined" ? document : null;
|
|
3102
|
+
useEventListener6(targetDocument, "keydown", () => keyboardModeSignal(true), { capture: true });
|
|
3103
|
+
useEventListener6(targetDocument, "pointerdown", () => keyboardModeSignal(false), { capture: true });
|
|
3104
|
+
const context = {
|
|
3105
|
+
isKeyboardMode: () => keyboardModeSignal()
|
|
3106
|
+
};
|
|
3107
|
+
return {
|
|
3108
|
+
type: KeyboardModeContext.Provider,
|
|
3109
|
+
props: {
|
|
3110
|
+
value: context,
|
|
3111
|
+
children: props.children
|
|
3112
|
+
}
|
|
3113
|
+
};
|
|
3114
|
+
}
|
|
3115
|
+
function useKeyboardMode() {
|
|
3116
|
+
const context = useContext19(KeyboardModeContext);
|
|
3117
|
+
return context.isKeyboardMode;
|
|
3118
|
+
}
|
|
3119
|
+
function FocusVisible(props) {
|
|
3120
|
+
const isKeyboardMode = useKeyboardMode();
|
|
3121
|
+
const tag = props.as ?? "div";
|
|
3122
|
+
return {
|
|
3123
|
+
type: tag,
|
|
3124
|
+
props: {
|
|
3125
|
+
...props,
|
|
3126
|
+
as: void 0,
|
|
3127
|
+
"data-focus-visible": () => isKeyboardMode() ? "true" : "false",
|
|
3128
|
+
children: props.children
|
|
3129
|
+
}
|
|
3130
|
+
};
|
|
3131
|
+
}
|
|
3132
|
+
|
|
3133
|
+
// src/components/overlay/dialog.ts
|
|
3134
|
+
import { createContext as createContext20, useContext as useContext20 } from "@fictjs/runtime";
|
|
3135
|
+
import { createRef as createRef2 } from "@fictjs/runtime";
|
|
3136
|
+
var DialogContext = createContext20(null);
|
|
3137
|
+
function useDialogContext(component) {
|
|
3138
|
+
const context = useContext20(DialogContext);
|
|
3139
|
+
if (!context) {
|
|
3140
|
+
throw new Error(`${component} must be used inside DialogRoot`);
|
|
3141
|
+
}
|
|
3142
|
+
return context;
|
|
3143
|
+
}
|
|
3144
|
+
function shouldRender(open, forceMount) {
|
|
3145
|
+
return open || !!forceMount;
|
|
3146
|
+
}
|
|
3147
|
+
function DialogRoot(props) {
|
|
3148
|
+
const openState = createControllableState({
|
|
3149
|
+
value: props.open,
|
|
3150
|
+
defaultValue: props.defaultOpen ?? false,
|
|
3151
|
+
onChange: props.onOpenChange
|
|
3152
|
+
});
|
|
3153
|
+
const baseId = useId(props.id, "dialog");
|
|
3154
|
+
const context = {
|
|
3155
|
+
open: () => openState.get(),
|
|
3156
|
+
setOpen: (value) => openState.set(value),
|
|
3157
|
+
modal: () => props.modal ?? true,
|
|
3158
|
+
contentId: `${baseId}-content`,
|
|
3159
|
+
titleId: `${baseId}-title`,
|
|
3160
|
+
descriptionId: `${baseId}-description`,
|
|
3161
|
+
triggerRef: createRef2(),
|
|
3162
|
+
contentRef: createRef2()
|
|
3163
|
+
};
|
|
3164
|
+
return {
|
|
3165
|
+
type: DialogContext.Provider,
|
|
3166
|
+
props: {
|
|
3167
|
+
value: context,
|
|
3168
|
+
children: props.children
|
|
3169
|
+
}
|
|
3170
|
+
};
|
|
3171
|
+
}
|
|
3172
|
+
function DialogPortal(props) {
|
|
3173
|
+
const context = useDialogContext("DialogPortal");
|
|
3174
|
+
return {
|
|
3175
|
+
type: Portal,
|
|
3176
|
+
props: {
|
|
3177
|
+
container: props.container,
|
|
3178
|
+
children: () => shouldRender(context.open(), props.forceMount) ? props.children ?? null : null
|
|
3179
|
+
}
|
|
3180
|
+
};
|
|
3181
|
+
}
|
|
3182
|
+
function DialogTrigger(props) {
|
|
3183
|
+
const context = useDialogContext("DialogTrigger");
|
|
3184
|
+
const tag = props.as ?? "button";
|
|
3185
|
+
const refProp = props.ref;
|
|
3186
|
+
const onClick = (event) => {
|
|
3187
|
+
props.onClick?.(event);
|
|
3188
|
+
if (event.defaultPrevented) return;
|
|
3189
|
+
context.setOpen(!context.open());
|
|
3190
|
+
};
|
|
3191
|
+
return Primitive({
|
|
3192
|
+
...props,
|
|
3193
|
+
as: tag,
|
|
3194
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
3195
|
+
ref: composeRefs(context.triggerRef, refProp),
|
|
3196
|
+
"aria-haspopup": "dialog",
|
|
3197
|
+
"aria-expanded": () => context.open(),
|
|
3198
|
+
"aria-controls": context.contentId,
|
|
3199
|
+
"data-state": () => context.open() ? "open" : "closed",
|
|
3200
|
+
onClick,
|
|
3201
|
+
children: props.children
|
|
3202
|
+
});
|
|
3203
|
+
}
|
|
3204
|
+
function DialogOverlay(props) {
|
|
3205
|
+
const context = useDialogContext("DialogOverlay");
|
|
3206
|
+
const overlayNode = {
|
|
3207
|
+
type: "div",
|
|
3208
|
+
props: {
|
|
3209
|
+
...props,
|
|
3210
|
+
forceMount: void 0,
|
|
3211
|
+
role: "presentation",
|
|
3212
|
+
"data-state": () => context.open() ? "open" : "closed",
|
|
3213
|
+
"data-dialog-overlay": "",
|
|
3214
|
+
onClick: (event) => {
|
|
3215
|
+
props.onClick?.(event);
|
|
3216
|
+
if (event.defaultPrevented) return;
|
|
3217
|
+
context.setOpen(false);
|
|
3218
|
+
},
|
|
3219
|
+
children: props.children
|
|
3220
|
+
}
|
|
3221
|
+
};
|
|
3222
|
+
return {
|
|
3223
|
+
type: DialogPortal,
|
|
3224
|
+
props: {
|
|
3225
|
+
forceMount: props.forceMount,
|
|
3226
|
+
children: overlayNode
|
|
3227
|
+
}
|
|
3228
|
+
};
|
|
3229
|
+
}
|
|
3230
|
+
function buildDialogContentNode(context, props) {
|
|
3231
|
+
const refProp = props.ref;
|
|
3232
|
+
return {
|
|
3233
|
+
type: DismissableLayer,
|
|
3234
|
+
props: {
|
|
3235
|
+
onEscapeKeyDown: props.onEscapeKeyDown,
|
|
3236
|
+
onInteractOutside: props.onInteractOutside,
|
|
3237
|
+
onPointerDownOutside: props.onPointerDownOutside,
|
|
3238
|
+
onFocusOutside: props.onFocusOutside,
|
|
3239
|
+
onDismiss: () => {
|
|
3240
|
+
props.onDismiss?.();
|
|
3241
|
+
context.setOpen(false);
|
|
3242
|
+
},
|
|
3243
|
+
children: {
|
|
3244
|
+
type: FocusScope,
|
|
3245
|
+
props: {
|
|
3246
|
+
trapped: () => context.modal(),
|
|
3247
|
+
loop: true,
|
|
3248
|
+
autoFocus: true,
|
|
3249
|
+
restoreFocus: true,
|
|
3250
|
+
children: [
|
|
3251
|
+
context.modal() ? { type: ScrollLock, props: { enabled: true } } : null,
|
|
3252
|
+
{
|
|
3253
|
+
type: "div",
|
|
3254
|
+
props: {
|
|
3255
|
+
...props,
|
|
3256
|
+
portal: void 0,
|
|
3257
|
+
forceMount: void 0,
|
|
3258
|
+
onDismiss: void 0,
|
|
3259
|
+
onEscapeKeyDown: void 0,
|
|
3260
|
+
onInteractOutside: void 0,
|
|
3261
|
+
onPointerDownOutside: void 0,
|
|
3262
|
+
onFocusOutside: void 0,
|
|
3263
|
+
ref: composeRefs(context.contentRef, refProp),
|
|
3264
|
+
id: props.id ?? context.contentId,
|
|
3265
|
+
role: props.role ?? "dialog",
|
|
3266
|
+
"aria-modal": context.modal() ? "true" : void 0,
|
|
3267
|
+
"aria-labelledby": props["aria-labelledby"] ?? context.titleId,
|
|
3268
|
+
"aria-describedby": props["aria-describedby"] ?? context.descriptionId,
|
|
3269
|
+
"data-state": () => context.open() ? "open" : "closed",
|
|
3270
|
+
"data-dialog-content": "",
|
|
3271
|
+
children: props.children
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
]
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
};
|
|
3279
|
+
}
|
|
3280
|
+
function DialogContent(props) {
|
|
3281
|
+
const context = useDialogContext("DialogContent");
|
|
3282
|
+
const node = buildDialogContentNode(context, props);
|
|
3283
|
+
if (props.portal ?? true) {
|
|
3284
|
+
return {
|
|
3285
|
+
type: DialogPortal,
|
|
3286
|
+
props: {
|
|
3287
|
+
forceMount: props.forceMount,
|
|
3288
|
+
children: node
|
|
3289
|
+
}
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
3292
|
+
return {
|
|
3293
|
+
type: "div",
|
|
3294
|
+
props: {
|
|
3295
|
+
"data-dialog-inline-container": "",
|
|
3296
|
+
children: () => shouldRender(context.open(), props.forceMount) ? node : null
|
|
3297
|
+
}
|
|
3298
|
+
};
|
|
3299
|
+
}
|
|
3300
|
+
function DialogClose(props) {
|
|
3301
|
+
const context = useDialogContext("DialogClose");
|
|
3302
|
+
const tag = props.as ?? "button";
|
|
3303
|
+
return Primitive({
|
|
3304
|
+
...props,
|
|
3305
|
+
as: tag,
|
|
3306
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
3307
|
+
onClick: (event) => {
|
|
3308
|
+
props.onClick?.(event);
|
|
3309
|
+
if (event.defaultPrevented) return;
|
|
3310
|
+
context.setOpen(false);
|
|
3311
|
+
},
|
|
3312
|
+
children: props.children
|
|
3313
|
+
});
|
|
3314
|
+
}
|
|
3315
|
+
function DialogTitle(props) {
|
|
3316
|
+
const context = useDialogContext("DialogTitle");
|
|
3317
|
+
return {
|
|
3318
|
+
type: "h2",
|
|
3319
|
+
props: {
|
|
3320
|
+
...props,
|
|
3321
|
+
id: props.id ?? context.titleId,
|
|
3322
|
+
"data-dialog-title": "",
|
|
3323
|
+
children: props.children
|
|
3324
|
+
}
|
|
3325
|
+
};
|
|
3326
|
+
}
|
|
3327
|
+
function DialogDescription(props) {
|
|
3328
|
+
const context = useDialogContext("DialogDescription");
|
|
3329
|
+
return {
|
|
3330
|
+
type: "p",
|
|
3331
|
+
props: {
|
|
3332
|
+
...props,
|
|
3333
|
+
id: props.id ?? context.descriptionId,
|
|
3334
|
+
"data-dialog-description": "",
|
|
3335
|
+
children: props.children
|
|
3336
|
+
}
|
|
3337
|
+
};
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
// src/components/overlay/alert-dialog.ts
|
|
3341
|
+
function AlertDialogRoot(props) {
|
|
3342
|
+
return {
|
|
3343
|
+
type: DialogRoot,
|
|
3344
|
+
props: {
|
|
3345
|
+
...props,
|
|
3346
|
+
modal: true
|
|
3347
|
+
}
|
|
3348
|
+
};
|
|
3349
|
+
}
|
|
3350
|
+
var AlertDialogTrigger = DialogTrigger;
|
|
3351
|
+
var AlertDialogPortal = DialogPortal;
|
|
3352
|
+
var AlertDialogOverlay = DialogOverlay;
|
|
3353
|
+
var AlertDialogTitle = DialogTitle;
|
|
3354
|
+
var AlertDialogDescription = DialogDescription;
|
|
3355
|
+
function AlertDialogContent(props) {
|
|
3356
|
+
return {
|
|
3357
|
+
type: DialogContent,
|
|
3358
|
+
props: {
|
|
3359
|
+
...props,
|
|
3360
|
+
role: props.role ?? "alertdialog"
|
|
3361
|
+
}
|
|
3362
|
+
};
|
|
3363
|
+
}
|
|
3364
|
+
function AlertDialogAction(props) {
|
|
3365
|
+
return {
|
|
3366
|
+
type: DialogClose,
|
|
3367
|
+
props
|
|
3368
|
+
};
|
|
3369
|
+
}
|
|
3370
|
+
function AlertDialogCancel(props) {
|
|
3371
|
+
return {
|
|
3372
|
+
type: DialogClose,
|
|
3373
|
+
props
|
|
3374
|
+
};
|
|
3375
|
+
}
|
|
3376
|
+
|
|
3377
|
+
// src/components/overlay/tooltip.ts
|
|
3378
|
+
import { createContext as createContext21, useContext as useContext21 } from "@fictjs/runtime";
|
|
3379
|
+
import { useDebounceFn } from "@fictjs/hooks";
|
|
3380
|
+
var TooltipProviderContext = createContext21({
|
|
3381
|
+
delayDuration: 700
|
|
3382
|
+
});
|
|
3383
|
+
var TooltipRootContext = createContext21(null);
|
|
3384
|
+
function useTooltipRootContext(component) {
|
|
3385
|
+
const context = useContext21(TooltipRootContext);
|
|
3386
|
+
if (!context) {
|
|
3387
|
+
throw new Error(`${component} must be used inside TooltipRoot`);
|
|
3388
|
+
}
|
|
3389
|
+
return context;
|
|
3390
|
+
}
|
|
3391
|
+
function TooltipProvider(props) {
|
|
3392
|
+
return {
|
|
3393
|
+
type: TooltipProviderContext.Provider,
|
|
3394
|
+
props: {
|
|
3395
|
+
value: {
|
|
3396
|
+
delayDuration: props.delayDuration ?? 700
|
|
3397
|
+
},
|
|
3398
|
+
children: props.children
|
|
3399
|
+
}
|
|
3400
|
+
};
|
|
3401
|
+
}
|
|
3402
|
+
function TooltipRoot(props) {
|
|
3403
|
+
const provider = useContext21(TooltipProviderContext);
|
|
3404
|
+
const openState = createControllableState({
|
|
3405
|
+
value: props.open,
|
|
3406
|
+
defaultValue: props.defaultOpen ?? false,
|
|
3407
|
+
onChange: props.onOpenChange
|
|
3408
|
+
});
|
|
3409
|
+
const baseId = useId(props.id, "tooltip");
|
|
3410
|
+
const openDebounced = useDebounceFn(
|
|
3411
|
+
() => {
|
|
3412
|
+
openState.set(true);
|
|
3413
|
+
},
|
|
3414
|
+
props.delayDuration ?? provider.delayDuration
|
|
3415
|
+
);
|
|
3416
|
+
const clearSchedule = () => openDebounced.cancel();
|
|
3417
|
+
const scheduleOpen = () => openDebounced.run();
|
|
3418
|
+
const context = {
|
|
3419
|
+
open: () => openState.get(),
|
|
3420
|
+
setOpen: (value) => openState.set(value),
|
|
3421
|
+
delayDuration: () => props.delayDuration ?? provider.delayDuration,
|
|
3422
|
+
contentId: `${baseId}-content`,
|
|
3423
|
+
scheduleOpen,
|
|
3424
|
+
clearSchedule
|
|
3425
|
+
};
|
|
3426
|
+
return {
|
|
3427
|
+
type: TooltipRootContext.Provider,
|
|
3428
|
+
props: {
|
|
3429
|
+
value: context,
|
|
3430
|
+
children: {
|
|
3431
|
+
type: PopperRoot,
|
|
3432
|
+
props: {
|
|
3433
|
+
children: props.children
|
|
3434
|
+
}
|
|
3435
|
+
}
|
|
3436
|
+
}
|
|
3437
|
+
};
|
|
3438
|
+
}
|
|
3439
|
+
function TooltipTrigger(props) {
|
|
3440
|
+
const context = useTooltipRootContext("TooltipTrigger");
|
|
3441
|
+
const tag = props.as ?? "button";
|
|
3442
|
+
const refProp = props.ref;
|
|
3443
|
+
let cleanupListeners = null;
|
|
3444
|
+
const open = (event) => {
|
|
3445
|
+
context.scheduleOpen();
|
|
3446
|
+
if (event instanceof PointerEvent) props.onPointerEnter?.(event);
|
|
3447
|
+
if (event instanceof MouseEvent) {
|
|
3448
|
+
props.onMouseEnter?.(event);
|
|
3449
|
+
props.onMouseOver?.(event);
|
|
3450
|
+
}
|
|
3451
|
+
};
|
|
3452
|
+
const close = (event) => {
|
|
3453
|
+
context.clearSchedule();
|
|
3454
|
+
context.setOpen(false);
|
|
3455
|
+
if (event instanceof PointerEvent) props.onPointerLeave?.(event);
|
|
3456
|
+
if (event instanceof MouseEvent) {
|
|
3457
|
+
props.onMouseLeave?.(event);
|
|
3458
|
+
props.onMouseOut?.(event);
|
|
3459
|
+
}
|
|
3460
|
+
};
|
|
3461
|
+
const onFocus = (event) => {
|
|
3462
|
+
props.onFocus?.(event);
|
|
3463
|
+
context.scheduleOpen();
|
|
3464
|
+
};
|
|
3465
|
+
const onBlur = (event) => {
|
|
3466
|
+
props.onBlur?.(event);
|
|
3467
|
+
context.clearSchedule();
|
|
3468
|
+
context.setOpen(false);
|
|
3469
|
+
};
|
|
3470
|
+
const registerRef = (node) => {
|
|
3471
|
+
cleanupListeners?.();
|
|
3472
|
+
cleanupListeners = null;
|
|
3473
|
+
if (typeof refProp === "function") {
|
|
3474
|
+
refProp(node);
|
|
3475
|
+
} else if (refProp) {
|
|
3476
|
+
refProp.current = node;
|
|
3477
|
+
}
|
|
3478
|
+
if (!node) return;
|
|
3479
|
+
node.addEventListener("pointerenter", open);
|
|
3480
|
+
node.addEventListener("pointerleave", close);
|
|
3481
|
+
node.addEventListener("mouseover", open);
|
|
3482
|
+
node.addEventListener("mouseout", close);
|
|
3483
|
+
node.addEventListener("focus", onFocus);
|
|
3484
|
+
node.addEventListener("blur", onBlur);
|
|
3485
|
+
cleanupListeners = () => {
|
|
3486
|
+
node.removeEventListener("pointerenter", open);
|
|
3487
|
+
node.removeEventListener("pointerleave", close);
|
|
3488
|
+
node.removeEventListener("mouseover", open);
|
|
3489
|
+
node.removeEventListener("mouseout", close);
|
|
3490
|
+
node.removeEventListener("focus", onFocus);
|
|
3491
|
+
node.removeEventListener("blur", onBlur);
|
|
3492
|
+
};
|
|
3493
|
+
};
|
|
3494
|
+
return {
|
|
3495
|
+
type: PopperAnchor,
|
|
3496
|
+
props: {
|
|
3497
|
+
children: Primitive({
|
|
3498
|
+
...props,
|
|
3499
|
+
as: tag,
|
|
3500
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
3501
|
+
ref: registerRef,
|
|
3502
|
+
"aria-describedby": () => context.open() ? context.contentId : void 0,
|
|
3503
|
+
"data-state": () => context.open() ? "open" : "closed",
|
|
3504
|
+
children: props.children
|
|
3505
|
+
})
|
|
3506
|
+
}
|
|
3507
|
+
};
|
|
3508
|
+
}
|
|
3509
|
+
function TooltipContent(props) {
|
|
3510
|
+
const context = useTooltipRootContext("TooltipContent");
|
|
3511
|
+
const contentNode = {
|
|
3512
|
+
type: PopperContent,
|
|
3513
|
+
props: {
|
|
3514
|
+
...props,
|
|
3515
|
+
forceMount: void 0,
|
|
3516
|
+
portal: void 0,
|
|
3517
|
+
id: props.id ?? context.contentId,
|
|
3518
|
+
role: props.role ?? "tooltip",
|
|
3519
|
+
"data-tooltip-content": "",
|
|
3520
|
+
"data-state": () => context.open() ? "open" : "closed",
|
|
3521
|
+
onPointerEnter: (event) => {
|
|
3522
|
+
props.onPointerEnter?.(event);
|
|
3523
|
+
context.clearSchedule();
|
|
3524
|
+
},
|
|
3525
|
+
onPointerLeave: (event) => {
|
|
3526
|
+
props.onPointerLeave?.(event);
|
|
3527
|
+
context.setOpen(false);
|
|
3528
|
+
},
|
|
3529
|
+
onMouseEnter: (event) => {
|
|
3530
|
+
props.onMouseEnter?.(event);
|
|
3531
|
+
context.clearSchedule();
|
|
3532
|
+
},
|
|
3533
|
+
onMouseLeave: (event) => {
|
|
3534
|
+
props.onMouseLeave?.(event);
|
|
3535
|
+
context.setOpen(false);
|
|
3536
|
+
},
|
|
3537
|
+
onMouseOver: (event) => {
|
|
3538
|
+
props.onMouseOver?.(event);
|
|
3539
|
+
context.clearSchedule();
|
|
3540
|
+
},
|
|
3541
|
+
onMouseOut: (event) => {
|
|
3542
|
+
props.onMouseOut?.(event);
|
|
3543
|
+
context.setOpen(false);
|
|
3544
|
+
},
|
|
3545
|
+
children: props.children
|
|
3546
|
+
}
|
|
3547
|
+
};
|
|
3548
|
+
const child = () => context.open() || props.forceMount ? contentNode : null;
|
|
3549
|
+
if (props.portal ?? true) {
|
|
3550
|
+
return {
|
|
3551
|
+
type: Portal,
|
|
3552
|
+
props: {
|
|
3553
|
+
children: child
|
|
3554
|
+
}
|
|
3555
|
+
};
|
|
3556
|
+
}
|
|
3557
|
+
return {
|
|
3558
|
+
type: "div",
|
|
3559
|
+
props: {
|
|
3560
|
+
"data-tooltip-inline-container": "",
|
|
3561
|
+
children: child
|
|
3562
|
+
}
|
|
3563
|
+
};
|
|
3564
|
+
}
|
|
3565
|
+
|
|
3566
|
+
// src/components/overlay/hover-card.ts
|
|
3567
|
+
import { createContext as createContext22, useContext as useContext22 } from "@fictjs/runtime";
|
|
3568
|
+
import { useDebounceFn as useDebounceFn2 } from "@fictjs/hooks";
|
|
3569
|
+
var HoverCardContext = createContext22(null);
|
|
3570
|
+
function useHoverCardContext(component) {
|
|
3571
|
+
const context = useContext22(HoverCardContext);
|
|
3572
|
+
if (!context) {
|
|
3573
|
+
throw new Error(`${component} must be used inside HoverCardRoot`);
|
|
3574
|
+
}
|
|
3575
|
+
return context;
|
|
3576
|
+
}
|
|
3577
|
+
function HoverCardRoot(props) {
|
|
3578
|
+
const openState = createControllableState({
|
|
3579
|
+
value: props.open,
|
|
3580
|
+
defaultValue: props.defaultOpen ?? false,
|
|
3581
|
+
onChange: props.onOpenChange
|
|
3582
|
+
});
|
|
3583
|
+
const baseId = useId(props.id, "hover-card");
|
|
3584
|
+
const openDebounced = useDebounceFn2(
|
|
3585
|
+
() => {
|
|
3586
|
+
openState.set(true);
|
|
3587
|
+
},
|
|
3588
|
+
props.openDelay ?? 200
|
|
3589
|
+
);
|
|
3590
|
+
const closeDebounced = useDebounceFn2(
|
|
3591
|
+
() => {
|
|
3592
|
+
openState.set(false);
|
|
3593
|
+
},
|
|
3594
|
+
props.closeDelay ?? 200
|
|
3595
|
+
);
|
|
3596
|
+
const clearTimers = () => {
|
|
3597
|
+
openDebounced.cancel();
|
|
3598
|
+
closeDebounced.cancel();
|
|
3599
|
+
};
|
|
3600
|
+
const scheduleOpen = () => {
|
|
3601
|
+
closeDebounced.cancel();
|
|
3602
|
+
openDebounced.run();
|
|
3603
|
+
};
|
|
3604
|
+
const scheduleClose = () => {
|
|
3605
|
+
openDebounced.cancel();
|
|
3606
|
+
closeDebounced.run();
|
|
3607
|
+
};
|
|
3608
|
+
const context = {
|
|
3609
|
+
open: () => openState.get(),
|
|
3610
|
+
setOpen: (value) => openState.set(value),
|
|
3611
|
+
contentId: `${baseId}-content`,
|
|
3612
|
+
scheduleOpen,
|
|
3613
|
+
scheduleClose,
|
|
3614
|
+
clearTimers
|
|
3615
|
+
};
|
|
3616
|
+
return {
|
|
3617
|
+
type: HoverCardContext.Provider,
|
|
3618
|
+
props: {
|
|
3619
|
+
value: context,
|
|
3620
|
+
children: {
|
|
3621
|
+
type: PopperRoot,
|
|
3622
|
+
props: {
|
|
3623
|
+
children: props.children
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
};
|
|
3628
|
+
}
|
|
3629
|
+
function HoverCardTrigger(props) {
|
|
3630
|
+
const context = useHoverCardContext("HoverCardTrigger");
|
|
3631
|
+
const tag = props.as ?? "button";
|
|
3632
|
+
const refProp = props.ref;
|
|
3633
|
+
let cleanupListeners = null;
|
|
3634
|
+
const open = (event) => {
|
|
3635
|
+
context.scheduleOpen();
|
|
3636
|
+
if (event instanceof PointerEvent) props.onPointerEnter?.(event);
|
|
3637
|
+
if (event instanceof MouseEvent) {
|
|
3638
|
+
props.onMouseEnter?.(event);
|
|
3639
|
+
props.onMouseOver?.(event);
|
|
3640
|
+
}
|
|
3641
|
+
};
|
|
3642
|
+
const close = (event) => {
|
|
3643
|
+
context.scheduleClose();
|
|
3644
|
+
if (event instanceof PointerEvent) props.onPointerLeave?.(event);
|
|
3645
|
+
if (event instanceof MouseEvent) {
|
|
3646
|
+
props.onMouseLeave?.(event);
|
|
3647
|
+
props.onMouseOut?.(event);
|
|
3648
|
+
}
|
|
3649
|
+
};
|
|
3650
|
+
const onFocus = (event) => {
|
|
3651
|
+
props.onFocus?.(event);
|
|
3652
|
+
context.scheduleOpen();
|
|
3653
|
+
};
|
|
3654
|
+
const onBlur = (event) => {
|
|
3655
|
+
props.onBlur?.(event);
|
|
3656
|
+
context.scheduleClose();
|
|
3657
|
+
};
|
|
3658
|
+
const registerRef = (node) => {
|
|
3659
|
+
cleanupListeners?.();
|
|
3660
|
+
cleanupListeners = null;
|
|
3661
|
+
if (typeof refProp === "function") {
|
|
3662
|
+
refProp(node);
|
|
3663
|
+
} else if (refProp) {
|
|
3664
|
+
refProp.current = node;
|
|
3665
|
+
}
|
|
3666
|
+
if (!node) return;
|
|
3667
|
+
node.addEventListener("pointerenter", open);
|
|
3668
|
+
node.addEventListener("pointerleave", close);
|
|
3669
|
+
node.addEventListener("mouseover", open);
|
|
3670
|
+
node.addEventListener("mouseout", close);
|
|
3671
|
+
node.addEventListener("focus", onFocus);
|
|
3672
|
+
node.addEventListener("blur", onBlur);
|
|
3673
|
+
cleanupListeners = () => {
|
|
3674
|
+
node.removeEventListener("pointerenter", open);
|
|
3675
|
+
node.removeEventListener("pointerleave", close);
|
|
3676
|
+
node.removeEventListener("mouseover", open);
|
|
3677
|
+
node.removeEventListener("mouseout", close);
|
|
3678
|
+
node.removeEventListener("focus", onFocus);
|
|
3679
|
+
node.removeEventListener("blur", onBlur);
|
|
3680
|
+
};
|
|
3681
|
+
};
|
|
3682
|
+
return {
|
|
3683
|
+
type: PopperAnchor,
|
|
3684
|
+
props: {
|
|
3685
|
+
children: Primitive({
|
|
3686
|
+
...props,
|
|
3687
|
+
as: tag,
|
|
3688
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
3689
|
+
ref: registerRef,
|
|
3690
|
+
"aria-describedby": () => context.open() ? context.contentId : void 0,
|
|
3691
|
+
"data-state": () => context.open() ? "open" : "closed",
|
|
3692
|
+
children: props.children
|
|
3693
|
+
})
|
|
3694
|
+
}
|
|
3695
|
+
};
|
|
3696
|
+
}
|
|
3697
|
+
function HoverCardContent(props) {
|
|
3698
|
+
const context = useHoverCardContext("HoverCardContent");
|
|
3699
|
+
const contentNode = {
|
|
3700
|
+
type: PopperContent,
|
|
3701
|
+
props: {
|
|
3702
|
+
...props,
|
|
3703
|
+
forceMount: void 0,
|
|
3704
|
+
portal: void 0,
|
|
3705
|
+
id: props.id ?? context.contentId,
|
|
3706
|
+
role: props.role ?? "dialog",
|
|
3707
|
+
"data-hover-card-content": "",
|
|
3708
|
+
"data-state": () => context.open() ? "open" : "closed",
|
|
3709
|
+
onPointerEnter: (event) => {
|
|
3710
|
+
props.onPointerEnter?.(event);
|
|
3711
|
+
context.clearTimers();
|
|
3712
|
+
},
|
|
3713
|
+
onPointerLeave: (event) => {
|
|
3714
|
+
props.onPointerLeave?.(event);
|
|
3715
|
+
context.scheduleClose();
|
|
3716
|
+
},
|
|
3717
|
+
onMouseEnter: (event) => {
|
|
3718
|
+
props.onMouseEnter?.(event);
|
|
3719
|
+
context.clearTimers();
|
|
3720
|
+
},
|
|
3721
|
+
onMouseLeave: (event) => {
|
|
3722
|
+
props.onMouseLeave?.(event);
|
|
3723
|
+
context.scheduleClose();
|
|
3724
|
+
},
|
|
3725
|
+
onMouseOver: (event) => {
|
|
3726
|
+
props.onMouseOver?.(event);
|
|
3727
|
+
context.clearTimers();
|
|
3728
|
+
},
|
|
3729
|
+
onMouseOut: (event) => {
|
|
3730
|
+
props.onMouseOut?.(event);
|
|
3731
|
+
context.scheduleClose();
|
|
3732
|
+
},
|
|
3733
|
+
children: props.children
|
|
3734
|
+
}
|
|
3735
|
+
};
|
|
3736
|
+
const child = () => context.open() || props.forceMount ? contentNode : null;
|
|
3737
|
+
if (props.portal ?? true) {
|
|
3738
|
+
return {
|
|
3739
|
+
type: Portal,
|
|
3740
|
+
props: {
|
|
3741
|
+
children: child
|
|
3742
|
+
}
|
|
3743
|
+
};
|
|
3744
|
+
}
|
|
3745
|
+
return {
|
|
3746
|
+
type: "div",
|
|
3747
|
+
props: {
|
|
3748
|
+
"data-hover-card-inline-container": "",
|
|
3749
|
+
children: child
|
|
3750
|
+
}
|
|
3751
|
+
};
|
|
3752
|
+
}
|
|
3753
|
+
|
|
3754
|
+
// src/components/overlay/command-palette.ts
|
|
3755
|
+
import { createContext as createContext23, onDestroy as onDestroy4, onMount as onMount4, useContext as useContext23 } from "@fictjs/runtime";
|
|
3756
|
+
import { createSignal as createSignal10 } from "@fictjs/runtime/advanced";
|
|
3757
|
+
function normalizeText(node) {
|
|
3758
|
+
if (node === null || node === void 0 || node === false) return "";
|
|
3759
|
+
if (typeof node === "string" || typeof node === "number") return String(node);
|
|
3760
|
+
if (Array.isArray(node)) {
|
|
3761
|
+
return node.map((item) => normalizeText(item)).join(" ").trim();
|
|
3762
|
+
}
|
|
3763
|
+
return "";
|
|
3764
|
+
}
|
|
3765
|
+
function matchCommand(query, item) {
|
|
3766
|
+
const normalized = query.trim().toLowerCase();
|
|
3767
|
+
if (!normalized) return true;
|
|
3768
|
+
const searchable = [item.value, item.text, ...item.keywords].map((value) => value.toLowerCase()).join(" ");
|
|
3769
|
+
return searchable.includes(normalized);
|
|
3770
|
+
}
|
|
3771
|
+
var CommandPaletteContext = createContext23(null);
|
|
3772
|
+
function useCommandPaletteContext(component) {
|
|
3773
|
+
const context = useContext23(CommandPaletteContext);
|
|
3774
|
+
if (!context) {
|
|
3775
|
+
throw new Error(`${component} must be used inside CommandPaletteRoot`);
|
|
3776
|
+
}
|
|
3777
|
+
return context;
|
|
3778
|
+
}
|
|
3779
|
+
function CommandPaletteRoot(props) {
|
|
3780
|
+
const baseId = useId(props.id, "command-palette");
|
|
3781
|
+
const openState = createControllableState({
|
|
3782
|
+
value: props.open,
|
|
3783
|
+
defaultValue: props.defaultOpen ?? false,
|
|
3784
|
+
onChange: props.onOpenChange
|
|
3785
|
+
});
|
|
3786
|
+
const valueState = createControllableState({
|
|
3787
|
+
value: props.value,
|
|
3788
|
+
defaultValue: props.defaultValue ?? "",
|
|
3789
|
+
onChange: props.onValueChange
|
|
3790
|
+
});
|
|
3791
|
+
const queryState = createControllableState({
|
|
3792
|
+
value: props.query,
|
|
3793
|
+
defaultValue: props.defaultQuery ?? "",
|
|
3794
|
+
onChange: props.onQueryChange
|
|
3795
|
+
});
|
|
3796
|
+
const itemsSignal = createSignal10([]);
|
|
3797
|
+
const context = {
|
|
3798
|
+
listId: `${baseId}-list`,
|
|
3799
|
+
open: () => openState.get(),
|
|
3800
|
+
setOpen: (open) => {
|
|
3801
|
+
openState.set(open);
|
|
3802
|
+
if (!open && (props.resetQueryOnClose ?? true)) {
|
|
3803
|
+
queryState.set("");
|
|
3804
|
+
}
|
|
3805
|
+
},
|
|
3806
|
+
value: () => valueState.get(),
|
|
3807
|
+
setValue: (value) => valueState.set(value),
|
|
3808
|
+
query: () => queryState.get(),
|
|
3809
|
+
setQuery: (query) => queryState.set(query),
|
|
3810
|
+
registerItem: (item) => {
|
|
3811
|
+
itemsSignal([...itemsSignal(), item]);
|
|
3812
|
+
return () => {
|
|
3813
|
+
itemsSignal(itemsSignal().filter((entry) => entry !== item));
|
|
3814
|
+
};
|
|
3815
|
+
},
|
|
3816
|
+
hasMatches: () => {
|
|
3817
|
+
const items = itemsSignal();
|
|
3818
|
+
if (items.length === 0) return false;
|
|
3819
|
+
return items.some((item) => matchCommand(queryState.get(), item));
|
|
3820
|
+
}
|
|
3821
|
+
};
|
|
3822
|
+
return {
|
|
3823
|
+
type: CommandPaletteContext.Provider,
|
|
3824
|
+
props: {
|
|
3825
|
+
value: context,
|
|
3826
|
+
children: {
|
|
3827
|
+
type: DialogRoot,
|
|
3828
|
+
props: {
|
|
3829
|
+
id: baseId,
|
|
3830
|
+
modal: true,
|
|
3831
|
+
open: () => context.open(),
|
|
3832
|
+
onOpenChange: (open) => context.setOpen(open),
|
|
3833
|
+
children: props.children
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
};
|
|
3838
|
+
}
|
|
3839
|
+
function CommandPaletteTrigger(props) {
|
|
3840
|
+
return {
|
|
3841
|
+
type: DialogTrigger,
|
|
3842
|
+
props: {
|
|
3843
|
+
...props,
|
|
3844
|
+
"data-command-trigger": "",
|
|
3845
|
+
children: props.children
|
|
3846
|
+
}
|
|
3847
|
+
};
|
|
3848
|
+
}
|
|
3849
|
+
function CommandPaletteContent(props) {
|
|
3850
|
+
return {
|
|
3851
|
+
type: DialogContent,
|
|
3852
|
+
props: {
|
|
3853
|
+
...props,
|
|
3854
|
+
role: props.role ?? "dialog",
|
|
3855
|
+
"data-command-content": "",
|
|
3856
|
+
children: props.children
|
|
3857
|
+
}
|
|
3858
|
+
};
|
|
3859
|
+
}
|
|
3860
|
+
function CommandPaletteInput(props) {
|
|
3861
|
+
const context = useCommandPaletteContext("CommandPaletteInput");
|
|
3862
|
+
return {
|
|
3863
|
+
type: "input",
|
|
3864
|
+
props: {
|
|
3865
|
+
...props,
|
|
3866
|
+
type: "text",
|
|
3867
|
+
role: "combobox",
|
|
3868
|
+
"aria-expanded": () => context.open(),
|
|
3869
|
+
"aria-controls": context.listId,
|
|
3870
|
+
value: () => context.query(),
|
|
3871
|
+
"data-command-input": "",
|
|
3872
|
+
onFocus: (event) => {
|
|
3873
|
+
;
|
|
3874
|
+
props.onFocus?.(event);
|
|
3875
|
+
if (!event.defaultPrevented) {
|
|
3876
|
+
context.setOpen(true);
|
|
3877
|
+
}
|
|
3878
|
+
},
|
|
3879
|
+
onInput: (event) => {
|
|
3880
|
+
;
|
|
3881
|
+
props.onInput?.(event);
|
|
3882
|
+
const target = event.target;
|
|
3883
|
+
if (!target) return;
|
|
3884
|
+
context.setQuery(target.value);
|
|
3885
|
+
},
|
|
3886
|
+
onKeyDown: (event) => {
|
|
3887
|
+
;
|
|
3888
|
+
props.onKeyDown?.(event);
|
|
3889
|
+
if (event.defaultPrevented) return;
|
|
3890
|
+
if (event.key === "ArrowDown") {
|
|
3891
|
+
const list = document.getElementById(context.listId);
|
|
3892
|
+
const firstItem = list?.querySelector("[data-command-item]");
|
|
3893
|
+
firstItem?.focus();
|
|
3894
|
+
event.preventDefault();
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
};
|
|
3899
|
+
}
|
|
3900
|
+
function CommandPaletteList(props) {
|
|
3901
|
+
const context = useCommandPaletteContext("CommandPaletteList");
|
|
3902
|
+
return {
|
|
3903
|
+
type: "div",
|
|
3904
|
+
props: {
|
|
3905
|
+
...props,
|
|
3906
|
+
id: props.id ?? context.listId,
|
|
3907
|
+
role: "listbox",
|
|
3908
|
+
"data-command-list": "",
|
|
3909
|
+
children: {
|
|
3910
|
+
type: RovingFocusGroup,
|
|
3911
|
+
props: {
|
|
3912
|
+
orientation: "vertical",
|
|
3913
|
+
loop: true,
|
|
3914
|
+
children: props.children
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
};
|
|
3919
|
+
}
|
|
3920
|
+
function CommandPaletteItem(props) {
|
|
3921
|
+
const context = useCommandPaletteContext("CommandPaletteItem");
|
|
3922
|
+
const tag = props.as ?? "button";
|
|
3923
|
+
const text = normalizeText(props.children) || props.value;
|
|
3924
|
+
let cleanup = null;
|
|
3925
|
+
onMount4(() => {
|
|
3926
|
+
cleanup = context.registerItem({
|
|
3927
|
+
value: props.value,
|
|
3928
|
+
text,
|
|
3929
|
+
keywords: props.keywords ?? []
|
|
3930
|
+
});
|
|
3931
|
+
});
|
|
3932
|
+
onDestroy4(() => {
|
|
3933
|
+
cleanup?.();
|
|
3934
|
+
cleanup = null;
|
|
3935
|
+
});
|
|
3936
|
+
const matches = () => matchCommand(context.query(), {
|
|
3937
|
+
value: props.value,
|
|
3938
|
+
text,
|
|
3939
|
+
keywords: props.keywords ?? []
|
|
3940
|
+
});
|
|
3941
|
+
return {
|
|
3942
|
+
type: "div",
|
|
3943
|
+
props: {
|
|
3944
|
+
"data-command-item-wrapper": props.value,
|
|
3945
|
+
children: () => matches() ? Primitive({
|
|
3946
|
+
...props,
|
|
3947
|
+
as: tag,
|
|
3948
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
3949
|
+
role: props.role ?? "option",
|
|
3950
|
+
"aria-selected": () => context.value() === props.value,
|
|
3951
|
+
"data-state": () => context.value() === props.value ? "selected" : "idle",
|
|
3952
|
+
"data-command-item": props.value,
|
|
3953
|
+
onClick: (event) => {
|
|
3954
|
+
;
|
|
3955
|
+
props.onClick?.(event);
|
|
3956
|
+
props.onSelect?.(props.value, event);
|
|
3957
|
+
if (event.defaultPrevented) return;
|
|
3958
|
+
context.setValue(props.value);
|
|
3959
|
+
if (!props.keepOpen) {
|
|
3960
|
+
context.setOpen(false);
|
|
3961
|
+
}
|
|
3962
|
+
},
|
|
3963
|
+
children: props.children
|
|
3964
|
+
}) : null
|
|
3965
|
+
}
|
|
3966
|
+
};
|
|
3967
|
+
}
|
|
3968
|
+
function CommandPaletteEmpty(props) {
|
|
3969
|
+
const context = useCommandPaletteContext("CommandPaletteEmpty");
|
|
3970
|
+
return {
|
|
3971
|
+
type: "div",
|
|
3972
|
+
props: {
|
|
3973
|
+
...props,
|
|
3974
|
+
"data-command-empty": "",
|
|
3975
|
+
children: () => !context.hasMatches() ? props.children ?? "No results found." : null
|
|
3976
|
+
}
|
|
3977
|
+
};
|
|
3978
|
+
}
|
|
3979
|
+
function CommandPaletteGroup(props) {
|
|
3980
|
+
return {
|
|
3981
|
+
type: "div",
|
|
3982
|
+
props: {
|
|
3983
|
+
...props,
|
|
3984
|
+
heading: void 0,
|
|
3985
|
+
role: props.role ?? "group",
|
|
3986
|
+
"data-command-group": "",
|
|
3987
|
+
children: [
|
|
3988
|
+
props.heading ? {
|
|
3989
|
+
type: "div",
|
|
3990
|
+
props: {
|
|
3991
|
+
"data-command-group-heading": "",
|
|
3992
|
+
children: props.heading
|
|
3993
|
+
}
|
|
3994
|
+
} : null,
|
|
3995
|
+
props.children
|
|
3996
|
+
]
|
|
3997
|
+
}
|
|
3998
|
+
};
|
|
3999
|
+
}
|
|
4000
|
+
var CommandPaletteSeparator = Separator;
|
|
4001
|
+
var CommandPaletteClose = DialogClose;
|
|
4002
|
+
|
|
4003
|
+
// src/components/menu/dropdown-menu.ts
|
|
4004
|
+
import { createContext as createContext24, useContext as useContext24 } from "@fictjs/runtime";
|
|
4005
|
+
var DropdownMenuContext = createContext24(null);
|
|
4006
|
+
var DropdownRadioContext = createContext24(null);
|
|
4007
|
+
function useDropdownMenuContext(component) {
|
|
4008
|
+
const context = useContext24(DropdownMenuContext);
|
|
4009
|
+
if (!context) {
|
|
4010
|
+
throw new Error(`${component} must be used inside DropdownMenuRoot`);
|
|
4011
|
+
}
|
|
4012
|
+
return context;
|
|
4013
|
+
}
|
|
4014
|
+
function DropdownMenuRoot(props) {
|
|
4015
|
+
const state = createControllableState({
|
|
4016
|
+
value: props.open,
|
|
4017
|
+
defaultValue: props.defaultOpen ?? false,
|
|
4018
|
+
onChange: props.onOpenChange
|
|
4019
|
+
});
|
|
4020
|
+
const context = {
|
|
4021
|
+
open: () => state.get(),
|
|
4022
|
+
setOpen: (open) => state.set(open)
|
|
4023
|
+
};
|
|
4024
|
+
return {
|
|
4025
|
+
type: DropdownMenuContext.Provider,
|
|
4026
|
+
props: {
|
|
4027
|
+
value: context,
|
|
4028
|
+
children: {
|
|
4029
|
+
type: PopoverRoot,
|
|
4030
|
+
props: {
|
|
4031
|
+
open: () => context.open(),
|
|
4032
|
+
onOpenChange: (open) => context.setOpen(open),
|
|
4033
|
+
children: props.children
|
|
4034
|
+
}
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
};
|
|
4038
|
+
}
|
|
4039
|
+
function DropdownMenuTrigger(props) {
|
|
4040
|
+
return {
|
|
4041
|
+
type: PopoverTrigger,
|
|
4042
|
+
props
|
|
4043
|
+
};
|
|
4044
|
+
}
|
|
4045
|
+
function DropdownMenuContent(props) {
|
|
4046
|
+
return {
|
|
4047
|
+
type: PopoverContent,
|
|
4048
|
+
props: {
|
|
4049
|
+
...props,
|
|
4050
|
+
role: "menu",
|
|
4051
|
+
"aria-orientation": "vertical",
|
|
4052
|
+
"data-dropdown-menu-content": "",
|
|
4053
|
+
children: {
|
|
4054
|
+
type: RovingFocusGroup,
|
|
4055
|
+
props: {
|
|
4056
|
+
orientation: "vertical",
|
|
4057
|
+
loop: true,
|
|
4058
|
+
children: props.children
|
|
4059
|
+
}
|
|
4060
|
+
}
|
|
4061
|
+
}
|
|
4062
|
+
};
|
|
4063
|
+
}
|
|
4064
|
+
function DropdownMenuItem(props) {
|
|
4065
|
+
const context = useDropdownMenuContext("DropdownMenuItem");
|
|
4066
|
+
const tag = props.as ?? "button";
|
|
4067
|
+
return Primitive({
|
|
4068
|
+
...props,
|
|
4069
|
+
as: tag,
|
|
4070
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
4071
|
+
role: props.role ?? "menuitem",
|
|
4072
|
+
"data-dropdown-menu-item": "",
|
|
4073
|
+
onClick: (event) => {
|
|
4074
|
+
;
|
|
4075
|
+
props.onClick?.(event);
|
|
4076
|
+
props.onSelect?.(event);
|
|
4077
|
+
if (event.defaultPrevented) return;
|
|
4078
|
+
if (!props.keepOpen) {
|
|
4079
|
+
context.setOpen(false);
|
|
4080
|
+
}
|
|
4081
|
+
},
|
|
4082
|
+
children: props.children
|
|
4083
|
+
});
|
|
4084
|
+
}
|
|
4085
|
+
function DropdownMenuCheckboxItem(props) {
|
|
4086
|
+
const checkedState = createControllableState({
|
|
4087
|
+
value: props.checked,
|
|
4088
|
+
defaultValue: props.defaultChecked ?? false,
|
|
4089
|
+
onChange: props.onCheckedChange
|
|
4090
|
+
});
|
|
4091
|
+
return {
|
|
4092
|
+
type: DropdownMenuItem,
|
|
4093
|
+
props: {
|
|
4094
|
+
...props,
|
|
4095
|
+
keepOpen: props.keepOpen ?? true,
|
|
4096
|
+
role: "menuitemcheckbox",
|
|
4097
|
+
"aria-checked": () => checkedState.get(),
|
|
4098
|
+
"data-checked": () => checkedState.get() ? "true" : "false",
|
|
4099
|
+
onSelect: (event) => {
|
|
4100
|
+
props.onSelect?.(event);
|
|
4101
|
+
if (event.defaultPrevented) return;
|
|
4102
|
+
checkedState.set(!checkedState.get());
|
|
4103
|
+
},
|
|
4104
|
+
children: props.children
|
|
4105
|
+
}
|
|
4106
|
+
};
|
|
4107
|
+
}
|
|
4108
|
+
function DropdownMenuRadioGroup(props) {
|
|
4109
|
+
const valueState = createControllableState({
|
|
4110
|
+
value: props.value,
|
|
4111
|
+
defaultValue: props.defaultValue ?? "",
|
|
4112
|
+
onChange: props.onValueChange
|
|
4113
|
+
});
|
|
4114
|
+
const context = {
|
|
4115
|
+
value: () => valueState.get(),
|
|
4116
|
+
setValue: (value) => valueState.set(value)
|
|
4117
|
+
};
|
|
4118
|
+
return {
|
|
4119
|
+
type: DropdownRadioContext.Provider,
|
|
4120
|
+
props: {
|
|
4121
|
+
value: context,
|
|
4122
|
+
children: props.children
|
|
4123
|
+
}
|
|
4124
|
+
};
|
|
4125
|
+
}
|
|
4126
|
+
function DropdownMenuRadioItem(props) {
|
|
4127
|
+
const radio = useContext24(DropdownRadioContext);
|
|
4128
|
+
if (!radio) {
|
|
4129
|
+
throw new Error("DropdownMenuRadioItem must be used inside DropdownMenuRadioGroup");
|
|
4130
|
+
}
|
|
4131
|
+
return {
|
|
4132
|
+
type: DropdownMenuItem,
|
|
4133
|
+
props: {
|
|
4134
|
+
...props,
|
|
4135
|
+
keepOpen: props.keepOpen ?? true,
|
|
4136
|
+
role: "menuitemradio",
|
|
4137
|
+
"aria-checked": () => radio.value() === props.value,
|
|
4138
|
+
"data-checked": () => radio.value() === props.value ? "true" : "false",
|
|
4139
|
+
onSelect: (event) => {
|
|
4140
|
+
props.onSelect?.(event);
|
|
4141
|
+
if (event.defaultPrevented) return;
|
|
4142
|
+
radio.setValue(props.value);
|
|
4143
|
+
},
|
|
4144
|
+
children: props.children
|
|
4145
|
+
}
|
|
4146
|
+
};
|
|
4147
|
+
}
|
|
4148
|
+
function DropdownMenuSub(props) {
|
|
4149
|
+
const state = createControllableState({
|
|
4150
|
+
value: props.open,
|
|
4151
|
+
defaultValue: props.defaultOpen ?? false,
|
|
4152
|
+
onChange: props.onOpenChange
|
|
4153
|
+
});
|
|
4154
|
+
return {
|
|
4155
|
+
type: PopoverRoot,
|
|
4156
|
+
props: {
|
|
4157
|
+
open: () => state.get(),
|
|
4158
|
+
onOpenChange: (open) => state.set(open),
|
|
4159
|
+
children: props.children
|
|
4160
|
+
}
|
|
4161
|
+
};
|
|
4162
|
+
}
|
|
4163
|
+
function DropdownMenuSubTrigger(props) {
|
|
4164
|
+
return {
|
|
4165
|
+
type: PopoverTrigger,
|
|
4166
|
+
props: {
|
|
4167
|
+
asChild: true,
|
|
4168
|
+
"aria-haspopup": "menu",
|
|
4169
|
+
children: {
|
|
4170
|
+
type: DropdownMenuItem,
|
|
4171
|
+
props: {
|
|
4172
|
+
...props,
|
|
4173
|
+
keepOpen: true,
|
|
4174
|
+
"aria-haspopup": props["aria-haspopup"] ?? "menu",
|
|
4175
|
+
"aria-expanded": props["aria-expanded"],
|
|
4176
|
+
"data-dropdown-menu-sub-trigger": "",
|
|
4177
|
+
children: props.children
|
|
4178
|
+
}
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
4181
|
+
};
|
|
4182
|
+
}
|
|
4183
|
+
function DropdownMenuSubContent(props) {
|
|
4184
|
+
return {
|
|
4185
|
+
type: PopoverContent,
|
|
4186
|
+
props: {
|
|
4187
|
+
...props,
|
|
4188
|
+
role: "menu",
|
|
4189
|
+
side: props.side ?? "right",
|
|
4190
|
+
align: props.align ?? "start",
|
|
4191
|
+
"aria-orientation": "vertical",
|
|
4192
|
+
"data-dropdown-menu-sub-content": "",
|
|
4193
|
+
children: {
|
|
4194
|
+
type: RovingFocusGroup,
|
|
4195
|
+
props: {
|
|
4196
|
+
orientation: "vertical",
|
|
4197
|
+
loop: true,
|
|
4198
|
+
children: props.children
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
}
|
|
4202
|
+
};
|
|
4203
|
+
}
|
|
4204
|
+
function DropdownMenuLabel(props) {
|
|
4205
|
+
return {
|
|
4206
|
+
type: "div",
|
|
4207
|
+
props: {
|
|
4208
|
+
...props,
|
|
4209
|
+
role: props.role ?? "presentation",
|
|
4210
|
+
"data-dropdown-menu-label": "",
|
|
4211
|
+
children: props.children
|
|
4212
|
+
}
|
|
4213
|
+
};
|
|
4214
|
+
}
|
|
4215
|
+
var DropdownMenuSeparator = Separator;
|
|
4216
|
+
|
|
4217
|
+
// src/components/menu/context-menu.ts
|
|
4218
|
+
import { createContext as createContext25, useContext as useContext25 } from "@fictjs/runtime";
|
|
4219
|
+
import { createSignal as createSignal11 } from "@fictjs/runtime/advanced";
|
|
4220
|
+
import { useEventListener as useEventListener7 } from "@fictjs/hooks";
|
|
4221
|
+
var ContextMenuContext = createContext25(null);
|
|
4222
|
+
function useContextMenuContext(component) {
|
|
4223
|
+
const context = useContext25(ContextMenuContext);
|
|
4224
|
+
if (!context) {
|
|
4225
|
+
throw new Error(`${component} must be used inside ContextMenuRoot`);
|
|
4226
|
+
}
|
|
4227
|
+
return context;
|
|
4228
|
+
}
|
|
4229
|
+
function ContextMenuRoot(props) {
|
|
4230
|
+
const state = createControllableState({
|
|
4231
|
+
value: props.open,
|
|
4232
|
+
defaultValue: props.defaultOpen ?? false,
|
|
4233
|
+
onChange: props.onOpenChange
|
|
4234
|
+
});
|
|
4235
|
+
const position = createSignal11({ x: 0, y: 0 });
|
|
4236
|
+
const context = {
|
|
4237
|
+
open: () => state.get(),
|
|
4238
|
+
setOpen: (open) => state.set(open),
|
|
4239
|
+
x: () => position().x,
|
|
4240
|
+
y: () => position().y,
|
|
4241
|
+
setPosition: (x, y) => position({ x, y })
|
|
4242
|
+
};
|
|
4243
|
+
return {
|
|
4244
|
+
type: ContextMenuContext.Provider,
|
|
4245
|
+
props: {
|
|
4246
|
+
value: context,
|
|
4247
|
+
children: props.children
|
|
4248
|
+
}
|
|
4249
|
+
};
|
|
4250
|
+
}
|
|
4251
|
+
function ContextMenuTrigger(props) {
|
|
4252
|
+
const context = useContextMenuContext("ContextMenuTrigger");
|
|
4253
|
+
const tag = props.as ?? "div";
|
|
4254
|
+
const refProp = props.ref;
|
|
4255
|
+
const triggerNode = createSignal11(null);
|
|
4256
|
+
const handleContextMenu = (event) => {
|
|
4257
|
+
;
|
|
4258
|
+
props.onContextMenu?.(event);
|
|
4259
|
+
if (event.defaultPrevented) return;
|
|
4260
|
+
event.preventDefault();
|
|
4261
|
+
context.setPosition(event.clientX, event.clientY);
|
|
4262
|
+
context.setOpen(true);
|
|
4263
|
+
};
|
|
4264
|
+
const contextMenuListener = useEventListener7(
|
|
4265
|
+
() => triggerNode(),
|
|
4266
|
+
"contextmenu",
|
|
4267
|
+
handleContextMenu,
|
|
4268
|
+
{ immediate: false }
|
|
4269
|
+
);
|
|
4270
|
+
const registerRef = (node) => {
|
|
4271
|
+
contextMenuListener.stop();
|
|
4272
|
+
if (typeof refProp === "function") {
|
|
4273
|
+
refProp(node);
|
|
4274
|
+
} else if (refProp) {
|
|
4275
|
+
refProp.current = node;
|
|
4276
|
+
}
|
|
4277
|
+
triggerNode(node);
|
|
4278
|
+
if (node) {
|
|
4279
|
+
contextMenuListener.start();
|
|
4280
|
+
}
|
|
4281
|
+
};
|
|
4282
|
+
return Primitive({
|
|
4283
|
+
...props,
|
|
4284
|
+
as: tag,
|
|
4285
|
+
ref: registerRef,
|
|
4286
|
+
onContextMenu: handleContextMenu,
|
|
4287
|
+
children: props.children
|
|
4288
|
+
});
|
|
4289
|
+
}
|
|
4290
|
+
function ContextMenuContent(props) {
|
|
4291
|
+
const context = useContextMenuContext("ContextMenuContent");
|
|
4292
|
+
const content = {
|
|
4293
|
+
type: DismissableLayer,
|
|
4294
|
+
props: {
|
|
4295
|
+
onEscapeKeyDown: props.onEscapeKeyDown,
|
|
4296
|
+
onInteractOutside: props.onInteractOutside,
|
|
4297
|
+
onPointerDownOutside: props.onPointerDownOutside,
|
|
4298
|
+
onFocusOutside: props.onFocusOutside,
|
|
4299
|
+
onDismiss: () => {
|
|
4300
|
+
props.onDismiss?.();
|
|
4301
|
+
context.setOpen(false);
|
|
4302
|
+
},
|
|
4303
|
+
children: {
|
|
4304
|
+
type: "div",
|
|
4305
|
+
props: {
|
|
4306
|
+
...props,
|
|
4307
|
+
forceMount: void 0,
|
|
4308
|
+
portal: void 0,
|
|
4309
|
+
onDismiss: void 0,
|
|
4310
|
+
onEscapeKeyDown: void 0,
|
|
4311
|
+
onInteractOutside: void 0,
|
|
4312
|
+
onPointerDownOutside: void 0,
|
|
4313
|
+
onFocusOutside: void 0,
|
|
4314
|
+
role: "menu",
|
|
4315
|
+
"data-context-menu-content": "",
|
|
4316
|
+
style: {
|
|
4317
|
+
position: "fixed",
|
|
4318
|
+
left: `${context.x()}px`,
|
|
4319
|
+
top: `${context.y()}px`
|
|
4320
|
+
},
|
|
4321
|
+
children: {
|
|
4322
|
+
type: RovingFocusGroup,
|
|
4323
|
+
props: {
|
|
4324
|
+
orientation: "vertical",
|
|
4325
|
+
loop: true,
|
|
4326
|
+
children: props.children
|
|
4327
|
+
}
|
|
4328
|
+
}
|
|
4329
|
+
}
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
};
|
|
4333
|
+
const child = () => context.open() || props.forceMount ? content : null;
|
|
4334
|
+
if (props.portal ?? true) {
|
|
4335
|
+
return {
|
|
4336
|
+
type: Portal,
|
|
4337
|
+
props: {
|
|
4338
|
+
children: child
|
|
4339
|
+
}
|
|
4340
|
+
};
|
|
4341
|
+
}
|
|
4342
|
+
return {
|
|
4343
|
+
type: "div",
|
|
4344
|
+
props: {
|
|
4345
|
+
"data-context-menu-inline-container": "",
|
|
4346
|
+
children: child
|
|
4347
|
+
}
|
|
4348
|
+
};
|
|
4349
|
+
}
|
|
4350
|
+
function ContextMenuItem(props) {
|
|
4351
|
+
const context = useContextMenuContext("ContextMenuItem");
|
|
4352
|
+
const tag = props.as ?? "button";
|
|
4353
|
+
return Primitive({
|
|
4354
|
+
...props,
|
|
4355
|
+
as: tag,
|
|
4356
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
4357
|
+
role: props.role ?? "menuitem",
|
|
4358
|
+
"data-context-menu-item": "",
|
|
4359
|
+
onClick: (event) => {
|
|
4360
|
+
;
|
|
4361
|
+
props.onClick?.(event);
|
|
4362
|
+
props.onSelect?.(event);
|
|
4363
|
+
if (event.defaultPrevented) return;
|
|
4364
|
+
if (!props.keepOpen) {
|
|
4365
|
+
context.setOpen(false);
|
|
4366
|
+
}
|
|
4367
|
+
},
|
|
4368
|
+
children: props.children
|
|
4369
|
+
});
|
|
4370
|
+
}
|
|
4371
|
+
function ContextMenuSub(props) {
|
|
4372
|
+
const state = createControllableState({
|
|
4373
|
+
value: props.open,
|
|
4374
|
+
defaultValue: props.defaultOpen ?? false,
|
|
4375
|
+
onChange: props.onOpenChange
|
|
4376
|
+
});
|
|
4377
|
+
return {
|
|
4378
|
+
type: PopoverRoot,
|
|
4379
|
+
props: {
|
|
4380
|
+
open: () => state.get(),
|
|
4381
|
+
onOpenChange: (open) => state.set(open),
|
|
4382
|
+
children: props.children
|
|
4383
|
+
}
|
|
4384
|
+
};
|
|
4385
|
+
}
|
|
4386
|
+
function ContextMenuSubTrigger(props) {
|
|
4387
|
+
return {
|
|
4388
|
+
type: PopoverTrigger,
|
|
4389
|
+
props: {
|
|
4390
|
+
asChild: true,
|
|
4391
|
+
"aria-haspopup": "menu",
|
|
4392
|
+
children: {
|
|
4393
|
+
type: ContextMenuItem,
|
|
4394
|
+
props: {
|
|
4395
|
+
...props,
|
|
4396
|
+
keepOpen: true,
|
|
4397
|
+
"aria-haspopup": props["aria-haspopup"] ?? "menu",
|
|
4398
|
+
"aria-expanded": props["aria-expanded"],
|
|
4399
|
+
"data-context-menu-sub-trigger": "",
|
|
4400
|
+
children: props.children
|
|
4401
|
+
}
|
|
4402
|
+
}
|
|
4403
|
+
}
|
|
4404
|
+
};
|
|
4405
|
+
}
|
|
4406
|
+
function ContextMenuSubContent(props) {
|
|
4407
|
+
return {
|
|
4408
|
+
type: PopoverContent,
|
|
4409
|
+
props: {
|
|
4410
|
+
...props,
|
|
4411
|
+
role: "menu",
|
|
4412
|
+
side: props.side ?? "right",
|
|
4413
|
+
align: props.align ?? "start",
|
|
4414
|
+
"aria-orientation": "vertical",
|
|
4415
|
+
"data-context-menu-sub-content": "",
|
|
4416
|
+
children: {
|
|
4417
|
+
type: RovingFocusGroup,
|
|
4418
|
+
props: {
|
|
4419
|
+
orientation: "vertical",
|
|
4420
|
+
loop: true,
|
|
4421
|
+
children: props.children
|
|
4422
|
+
}
|
|
4423
|
+
}
|
|
4424
|
+
}
|
|
4425
|
+
};
|
|
4426
|
+
}
|
|
4427
|
+
|
|
4428
|
+
// src/components/menu/menubar.ts
|
|
4429
|
+
import { createContext as createContext26, useContext as useContext26 } from "@fictjs/runtime";
|
|
4430
|
+
import { createSignal as createSignal12 } from "@fictjs/runtime/advanced";
|
|
4431
|
+
var MenubarRootContext = createContext26(null);
|
|
4432
|
+
var MenubarMenuContext = createContext26(null);
|
|
4433
|
+
function useMenubarRootContext(component) {
|
|
4434
|
+
const context = useContext26(MenubarRootContext);
|
|
4435
|
+
if (!context) {
|
|
4436
|
+
throw new Error(`${component} must be used inside MenubarRoot`);
|
|
4437
|
+
}
|
|
4438
|
+
return context;
|
|
4439
|
+
}
|
|
4440
|
+
function useMenubarMenuContext(component) {
|
|
4441
|
+
const context = useContext26(MenubarMenuContext);
|
|
4442
|
+
if (!context) {
|
|
4443
|
+
throw new Error(`${component} must be used inside MenubarMenu`);
|
|
4444
|
+
}
|
|
4445
|
+
return context;
|
|
4446
|
+
}
|
|
4447
|
+
function MenubarRoot(props) {
|
|
4448
|
+
const activeMenuSignal = createSignal12(null);
|
|
4449
|
+
const context = {
|
|
4450
|
+
activeMenu: () => activeMenuSignal(),
|
|
4451
|
+
setActiveMenu: (id) => activeMenuSignal(id)
|
|
4452
|
+
};
|
|
4453
|
+
return {
|
|
4454
|
+
type: MenubarRootContext.Provider,
|
|
4455
|
+
props: {
|
|
4456
|
+
value: context,
|
|
4457
|
+
children: {
|
|
4458
|
+
type: "div",
|
|
4459
|
+
props: {
|
|
4460
|
+
...props,
|
|
4461
|
+
role: "menubar",
|
|
4462
|
+
"data-menubar-root": "",
|
|
4463
|
+
children: {
|
|
4464
|
+
type: RovingFocusGroup,
|
|
4465
|
+
props: {
|
|
4466
|
+
orientation: "horizontal",
|
|
4467
|
+
loop: true,
|
|
4468
|
+
children: props.children
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
}
|
|
4473
|
+
}
|
|
4474
|
+
};
|
|
4475
|
+
}
|
|
4476
|
+
function MenubarMenu(props) {
|
|
4477
|
+
const id = useId(props.value, "menubar-menu");
|
|
4478
|
+
return {
|
|
4479
|
+
type: MenubarMenuContext.Provider,
|
|
4480
|
+
props: {
|
|
4481
|
+
value: { id },
|
|
4482
|
+
children: {
|
|
4483
|
+
type: "div",
|
|
4484
|
+
props: {
|
|
4485
|
+
"data-menubar-menu": id,
|
|
4486
|
+
children: props.children
|
|
4487
|
+
}
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
};
|
|
4491
|
+
}
|
|
4492
|
+
function MenubarTrigger(props) {
|
|
4493
|
+
const root = useMenubarRootContext("MenubarTrigger");
|
|
4494
|
+
const menu = useMenubarMenuContext("MenubarTrigger");
|
|
4495
|
+
const tag = props.as ?? "button";
|
|
4496
|
+
const open = () => root.activeMenu() === menu.id;
|
|
4497
|
+
return Primitive({
|
|
4498
|
+
...props,
|
|
4499
|
+
as: tag,
|
|
4500
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
4501
|
+
role: "menuitem",
|
|
4502
|
+
"aria-haspopup": "menu",
|
|
4503
|
+
"aria-expanded": open,
|
|
4504
|
+
"data-state": () => open() ? "open" : "closed",
|
|
4505
|
+
"data-menubar-trigger": menu.id,
|
|
4506
|
+
onClick: (event) => {
|
|
4507
|
+
;
|
|
4508
|
+
props.onClick?.(event);
|
|
4509
|
+
if (event.defaultPrevented) return;
|
|
4510
|
+
root.setActiveMenu(open() ? null : menu.id);
|
|
4511
|
+
},
|
|
4512
|
+
onMouseOver: (event) => {
|
|
4513
|
+
;
|
|
4514
|
+
props.onMouseOver?.(event);
|
|
4515
|
+
if (event.defaultPrevented) return;
|
|
4516
|
+
root.setActiveMenu(menu.id);
|
|
4517
|
+
},
|
|
4518
|
+
children: props.children
|
|
4519
|
+
});
|
|
4520
|
+
}
|
|
4521
|
+
function MenubarContent(props) {
|
|
4522
|
+
const root = useMenubarRootContext("MenubarContent");
|
|
4523
|
+
const menu = useMenubarMenuContext("MenubarContent");
|
|
4524
|
+
const open = () => root.activeMenu() === menu.id;
|
|
4525
|
+
return {
|
|
4526
|
+
type: "div",
|
|
4527
|
+
props: {
|
|
4528
|
+
"data-menubar-content-wrapper": menu.id,
|
|
4529
|
+
children: () => open() || props.forceMount ? {
|
|
4530
|
+
type: "div",
|
|
4531
|
+
props: {
|
|
4532
|
+
...props,
|
|
4533
|
+
forceMount: void 0,
|
|
4534
|
+
role: "menu",
|
|
4535
|
+
"data-menubar-content": menu.id,
|
|
4536
|
+
children: props.children
|
|
4537
|
+
}
|
|
4538
|
+
} : null
|
|
4539
|
+
}
|
|
4540
|
+
};
|
|
4541
|
+
}
|
|
4542
|
+
function MenubarItem(props) {
|
|
4543
|
+
const root = useMenubarRootContext("MenubarItem");
|
|
4544
|
+
const tag = props.as ?? "button";
|
|
4545
|
+
return Primitive({
|
|
4546
|
+
...props,
|
|
4547
|
+
as: tag,
|
|
4548
|
+
type: !props.asChild && tag === "button" ? props.type ?? "button" : props.type,
|
|
4549
|
+
role: props.role ?? "menuitem",
|
|
4550
|
+
"data-menubar-item": "",
|
|
4551
|
+
onClick: (event) => {
|
|
4552
|
+
;
|
|
4553
|
+
props.onClick?.(event);
|
|
4554
|
+
props.onSelect?.(event);
|
|
4555
|
+
if (!event.defaultPrevented) {
|
|
4556
|
+
root.setActiveMenu(null);
|
|
4557
|
+
}
|
|
4558
|
+
},
|
|
4559
|
+
children: props.children
|
|
4560
|
+
});
|
|
4561
|
+
}
|
|
4562
|
+
|
|
4563
|
+
// src/components/feedback/toast.ts
|
|
4564
|
+
import { createContext as createContext27, onDestroy as onDestroy5, useContext as useContext27 } from "@fictjs/runtime";
|
|
4565
|
+
import { createSignal as createSignal13 } from "@fictjs/runtime/advanced";
|
|
4566
|
+
import { useDebounceFn as useDebounceFn3 } from "@fictjs/hooks";
|
|
4567
|
+
var ToastContext = createContext27(null);
|
|
4568
|
+
function useToastContext(component) {
|
|
4569
|
+
const context = useContext27(ToastContext);
|
|
4570
|
+
if (!context) {
|
|
4571
|
+
throw new Error(`${component} must be used inside ToastProvider`);
|
|
4572
|
+
}
|
|
4573
|
+
return context;
|
|
4574
|
+
}
|
|
4575
|
+
function ToastProvider(props) {
|
|
4576
|
+
const toastsSignal = createSignal13([]);
|
|
4577
|
+
const timers = /* @__PURE__ */ new Map();
|
|
4578
|
+
const dismiss = (id) => {
|
|
4579
|
+
toastsSignal(toastsSignal().filter((toast) => toast.id !== id));
|
|
4580
|
+
const timer = timers.get(id);
|
|
4581
|
+
if (timer) {
|
|
4582
|
+
timer.cancel();
|
|
4583
|
+
timers.delete(id);
|
|
4584
|
+
}
|
|
4585
|
+
};
|
|
4586
|
+
const show = (toast) => {
|
|
4587
|
+
const id = toast.id ?? createId("toast");
|
|
4588
|
+
const existingTimer = timers.get(id);
|
|
4589
|
+
existingTimer?.cancel();
|
|
4590
|
+
timers.delete(id);
|
|
4591
|
+
const nextToast = {
|
|
4592
|
+
id,
|
|
4593
|
+
title: toast.title,
|
|
4594
|
+
description: toast.description,
|
|
4595
|
+
duration: toast.duration
|
|
4596
|
+
};
|
|
4597
|
+
toastsSignal([...toastsSignal(), nextToast]);
|
|
4598
|
+
const timer = useDebounceFn3(
|
|
4599
|
+
() => {
|
|
4600
|
+
dismiss(id);
|
|
4601
|
+
},
|
|
4602
|
+
toast.duration ?? props.duration ?? 5e3
|
|
4603
|
+
);
|
|
4604
|
+
timer.run();
|
|
4605
|
+
timers.set(id, timer);
|
|
4606
|
+
return id;
|
|
4607
|
+
};
|
|
4608
|
+
onDestroy5(() => {
|
|
4609
|
+
for (const timer of timers.values()) {
|
|
4610
|
+
timer.cancel();
|
|
4611
|
+
}
|
|
4612
|
+
timers.clear();
|
|
4613
|
+
});
|
|
4614
|
+
const context = {
|
|
4615
|
+
toasts: () => toastsSignal(),
|
|
4616
|
+
show,
|
|
4617
|
+
dismiss,
|
|
4618
|
+
duration: () => props.duration ?? 5e3
|
|
4619
|
+
};
|
|
4620
|
+
return {
|
|
4621
|
+
type: ToastContext.Provider,
|
|
4622
|
+
props: {
|
|
4623
|
+
value: context,
|
|
4624
|
+
children: props.children
|
|
4625
|
+
}
|
|
4626
|
+
};
|
|
4627
|
+
}
|
|
4628
|
+
function useToast() {
|
|
4629
|
+
const context = useToastContext("useToast");
|
|
4630
|
+
return {
|
|
4631
|
+
show: context.show,
|
|
4632
|
+
dismiss: context.dismiss,
|
|
4633
|
+
toasts: context.toasts
|
|
4634
|
+
};
|
|
4635
|
+
}
|
|
4636
|
+
function ToastViewport(props) {
|
|
4637
|
+
const context = useToastContext("ToastViewport");
|
|
4638
|
+
return {
|
|
4639
|
+
type: "div",
|
|
4640
|
+
props: {
|
|
4641
|
+
...props,
|
|
4642
|
+
role: "region",
|
|
4643
|
+
"aria-live": "polite",
|
|
4644
|
+
"aria-relevant": "additions text",
|
|
4645
|
+
"data-toast-viewport": "",
|
|
4646
|
+
children: [
|
|
4647
|
+
props.children,
|
|
4648
|
+
{
|
|
4649
|
+
type: "div",
|
|
4650
|
+
props: {
|
|
4651
|
+
children: () => context.toasts().map((toast) => ({
|
|
4652
|
+
type: ToastRoot,
|
|
4653
|
+
props: {
|
|
4654
|
+
id: toast.id,
|
|
4655
|
+
title: toast.title,
|
|
4656
|
+
description: toast.description,
|
|
4657
|
+
duration: toast.duration ?? context.duration()
|
|
4658
|
+
}
|
|
4659
|
+
}))
|
|
4660
|
+
}
|
|
4661
|
+
}
|
|
4662
|
+
]
|
|
4663
|
+
}
|
|
4664
|
+
};
|
|
4665
|
+
}
|
|
4666
|
+
function ToastRoot(props) {
|
|
4667
|
+
const context = useContext27(ToastContext);
|
|
4668
|
+
const close = () => {
|
|
4669
|
+
if (props.id && context) {
|
|
4670
|
+
context.dismiss(props.id);
|
|
4671
|
+
}
|
|
4672
|
+
props.onOpenChange?.(false);
|
|
4673
|
+
};
|
|
4674
|
+
return {
|
|
4675
|
+
type: "div",
|
|
4676
|
+
props: {
|
|
4677
|
+
...props,
|
|
4678
|
+
role: props.role ?? "status",
|
|
4679
|
+
"data-toast-root": "",
|
|
4680
|
+
children: [
|
|
4681
|
+
props.title ? { type: ToastTitle, props: { children: props.title } } : null,
|
|
4682
|
+
props.description ? { type: ToastDescription, props: { children: props.description } } : null,
|
|
4683
|
+
props.children,
|
|
4684
|
+
{
|
|
4685
|
+
type: ToastClose,
|
|
4686
|
+
props: {
|
|
4687
|
+
onClick: () => close(),
|
|
4688
|
+
children: props["data-close-label"] ?? "Dismiss"
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
]
|
|
4692
|
+
}
|
|
4693
|
+
};
|
|
4694
|
+
}
|
|
4695
|
+
function ToastTitle(props) {
|
|
4696
|
+
return {
|
|
4697
|
+
type: "div",
|
|
4698
|
+
props: {
|
|
4699
|
+
...props,
|
|
4700
|
+
"data-toast-title": "",
|
|
4701
|
+
children: props.children
|
|
4702
|
+
}
|
|
4703
|
+
};
|
|
4704
|
+
}
|
|
4705
|
+
function ToastDescription(props) {
|
|
4706
|
+
return {
|
|
4707
|
+
type: "div",
|
|
4708
|
+
props: {
|
|
4709
|
+
...props,
|
|
4710
|
+
"data-toast-description": "",
|
|
4711
|
+
children: props.children
|
|
4712
|
+
}
|
|
4713
|
+
};
|
|
4714
|
+
}
|
|
4715
|
+
function ToastAction(props) {
|
|
4716
|
+
return {
|
|
4717
|
+
type: "button",
|
|
4718
|
+
props: {
|
|
4719
|
+
...props,
|
|
4720
|
+
type: "button",
|
|
4721
|
+
"data-toast-action": "",
|
|
4722
|
+
"aria-label": props.altText,
|
|
4723
|
+
children: [props.children, { type: VisuallyHidden, props: { children: props.altText } }]
|
|
4724
|
+
}
|
|
4725
|
+
};
|
|
4726
|
+
}
|
|
4727
|
+
function ToastClose(props) {
|
|
4728
|
+
const tag = props.as ?? "button";
|
|
4729
|
+
return Primitive({
|
|
4730
|
+
...props,
|
|
4731
|
+
as: tag,
|
|
4732
|
+
type: !props.asChild && tag === "button" ? "button" : props.type,
|
|
4733
|
+
"data-toast-close": "",
|
|
4734
|
+
onClick: (event) => {
|
|
4735
|
+
props.onClick?.(event);
|
|
4736
|
+
},
|
|
4737
|
+
children: props.children ?? "Close"
|
|
4738
|
+
});
|
|
4739
|
+
}
|
|
4740
|
+
export {
|
|
4741
|
+
AccessibleIcon,
|
|
4742
|
+
AccordionContent,
|
|
4743
|
+
AccordionItem,
|
|
4744
|
+
AccordionRoot,
|
|
4745
|
+
AccordionTrigger,
|
|
4746
|
+
AlertDialogAction,
|
|
4747
|
+
AlertDialogCancel,
|
|
4748
|
+
AlertDialogContent,
|
|
4749
|
+
AlertDialogDescription,
|
|
4750
|
+
AlertDialogOverlay,
|
|
4751
|
+
AlertDialogPortal,
|
|
4752
|
+
AlertDialogRoot,
|
|
4753
|
+
AlertDialogTitle,
|
|
4754
|
+
AlertDialogTrigger,
|
|
4755
|
+
Announce,
|
|
4756
|
+
AspectRatio,
|
|
4757
|
+
Calendar,
|
|
4758
|
+
CalendarGrid,
|
|
4759
|
+
CalendarHeader,
|
|
4760
|
+
CalendarNextButton,
|
|
4761
|
+
CalendarPrevButton,
|
|
4762
|
+
CalendarRoot,
|
|
4763
|
+
CalendarTitle,
|
|
4764
|
+
Checkbox,
|
|
4765
|
+
CollapsibleContent,
|
|
4766
|
+
CollapsibleRoot,
|
|
4767
|
+
CollapsibleTrigger,
|
|
4768
|
+
ComboboxInput,
|
|
4769
|
+
ComboboxItem,
|
|
4770
|
+
ComboboxList,
|
|
4771
|
+
ComboboxRoot,
|
|
4772
|
+
CommandPaletteClose,
|
|
4773
|
+
CommandPaletteContent,
|
|
4774
|
+
CommandPaletteEmpty,
|
|
4775
|
+
CommandPaletteGroup,
|
|
4776
|
+
CommandPaletteInput,
|
|
4777
|
+
CommandPaletteItem,
|
|
4778
|
+
CommandPaletteList,
|
|
4779
|
+
CommandPaletteRoot,
|
|
4780
|
+
CommandPaletteSeparator,
|
|
4781
|
+
CommandPaletteTrigger,
|
|
4782
|
+
ContextMenuContent,
|
|
4783
|
+
ContextMenuItem,
|
|
4784
|
+
ContextMenuRoot,
|
|
4785
|
+
ContextMenuSub,
|
|
4786
|
+
ContextMenuSubContent,
|
|
4787
|
+
ContextMenuSubTrigger,
|
|
4788
|
+
ContextMenuTrigger,
|
|
4789
|
+
DatePickerCalendar,
|
|
4790
|
+
DatePickerContent,
|
|
4791
|
+
DatePickerRoot,
|
|
4792
|
+
DatePickerTrigger,
|
|
4793
|
+
DatePickerValue,
|
|
4794
|
+
DialogClose,
|
|
4795
|
+
DialogContent,
|
|
4796
|
+
DialogDescription,
|
|
4797
|
+
DialogOverlay,
|
|
4798
|
+
DialogPortal,
|
|
4799
|
+
DialogRoot,
|
|
4800
|
+
DialogTitle,
|
|
4801
|
+
DialogTrigger,
|
|
4802
|
+
DismissableLayer,
|
|
4803
|
+
DropdownMenuCheckboxItem,
|
|
4804
|
+
DropdownMenuContent,
|
|
4805
|
+
DropdownMenuItem,
|
|
4806
|
+
DropdownMenuLabel,
|
|
4807
|
+
DropdownMenuRadioGroup,
|
|
4808
|
+
DropdownMenuRadioItem,
|
|
4809
|
+
DropdownMenuRoot,
|
|
4810
|
+
DropdownMenuSeparator,
|
|
4811
|
+
DropdownMenuSub,
|
|
4812
|
+
DropdownMenuSubContent,
|
|
4813
|
+
DropdownMenuSubTrigger,
|
|
4814
|
+
DropdownMenuTrigger,
|
|
4815
|
+
FocusScope,
|
|
4816
|
+
FocusTrap,
|
|
4817
|
+
FocusVisible,
|
|
4818
|
+
Form,
|
|
4819
|
+
FormControl,
|
|
4820
|
+
FormDescription,
|
|
4821
|
+
FormField,
|
|
4822
|
+
FormLabel,
|
|
4823
|
+
FormMessage,
|
|
4824
|
+
HoverCardContent,
|
|
4825
|
+
HoverCardRoot,
|
|
4826
|
+
HoverCardTrigger,
|
|
4827
|
+
IdProvider,
|
|
4828
|
+
KeyboardModeProvider,
|
|
4829
|
+
Label,
|
|
4830
|
+
LiveRegionProvider,
|
|
4831
|
+
MenubarContent,
|
|
4832
|
+
MenubarItem,
|
|
4833
|
+
MenubarMenu,
|
|
4834
|
+
MenubarRoot,
|
|
4835
|
+
MenubarTrigger,
|
|
4836
|
+
Meter,
|
|
4837
|
+
NavigationMenuContent,
|
|
4838
|
+
NavigationMenuIndicator,
|
|
4839
|
+
NavigationMenuItem,
|
|
4840
|
+
NavigationMenuLink,
|
|
4841
|
+
NavigationMenuList,
|
|
4842
|
+
NavigationMenuRoot,
|
|
4843
|
+
NavigationMenuTrigger,
|
|
4844
|
+
NavigationMenuViewport,
|
|
4845
|
+
PopoverClose,
|
|
4846
|
+
PopoverContent,
|
|
4847
|
+
PopoverRoot,
|
|
4848
|
+
PopoverTrigger,
|
|
4849
|
+
PopperAnchor,
|
|
4850
|
+
PopperArrow,
|
|
4851
|
+
PopperContent,
|
|
4852
|
+
PopperRoot,
|
|
4853
|
+
Portal,
|
|
4854
|
+
PortalHost,
|
|
4855
|
+
Presence,
|
|
4856
|
+
Primitive,
|
|
4857
|
+
PrimitiveElements,
|
|
4858
|
+
Progress,
|
|
4859
|
+
RadioGroup,
|
|
4860
|
+
RadioItem,
|
|
4861
|
+
RangeSlider,
|
|
4862
|
+
ResizableHandle,
|
|
4863
|
+
ResizablePanel,
|
|
4864
|
+
ResizablePanelGroup,
|
|
4865
|
+
RovingFocusGroup,
|
|
4866
|
+
RovingFocusItem,
|
|
4867
|
+
ScrollArea,
|
|
4868
|
+
ScrollAreaScrollbar,
|
|
4869
|
+
ScrollAreaThumb,
|
|
4870
|
+
ScrollAreaViewport,
|
|
4871
|
+
ScrollLock,
|
|
4872
|
+
SelectContent,
|
|
4873
|
+
SelectItem,
|
|
4874
|
+
SelectRoot,
|
|
4875
|
+
SelectTrigger,
|
|
4876
|
+
SelectValue,
|
|
4877
|
+
Separator,
|
|
4878
|
+
Skeleton,
|
|
4879
|
+
Slider,
|
|
4880
|
+
Slot,
|
|
4881
|
+
Switch,
|
|
4882
|
+
SwitchThumb,
|
|
4883
|
+
TabsContent,
|
|
4884
|
+
TabsList,
|
|
4885
|
+
TabsRoot,
|
|
4886
|
+
TabsTrigger,
|
|
4887
|
+
ToastAction,
|
|
4888
|
+
ToastClose,
|
|
4889
|
+
ToastDescription,
|
|
4890
|
+
ToastProvider,
|
|
4891
|
+
ToastRoot,
|
|
4892
|
+
ToastTitle,
|
|
4893
|
+
ToastViewport,
|
|
4894
|
+
Toggle,
|
|
4895
|
+
ToggleGroup,
|
|
4896
|
+
ToggleGroupItem,
|
|
4897
|
+
TooltipContent,
|
|
4898
|
+
TooltipProvider,
|
|
4899
|
+
TooltipRoot,
|
|
4900
|
+
TooltipTrigger,
|
|
4901
|
+
VisuallyHidden,
|
|
4902
|
+
useAnnouncer,
|
|
4903
|
+
useId,
|
|
4904
|
+
useKeyboardMode,
|
|
4905
|
+
useToast
|
|
4906
|
+
};
|
|
4907
|
+
//# sourceMappingURL=index.js.map
|