@jameskabz/nextcraft-ui 0.3.0 → 0.5.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/dist/index.cjs CHANGED
@@ -30,11 +30,45 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ AppShell: () => AppShell,
34
+ AuthLayout: () => AuthLayout,
35
+ Breadcrumbs: () => Breadcrumbs,
36
+ Container: () => Container,
37
+ CraftBadge: () => CraftBadge,
33
38
  CraftButton: () => CraftButton,
39
+ CraftCard: () => CraftCard,
40
+ CraftCheckbox: () => CraftCheckbox,
41
+ CraftConfirmDialog: () => CraftConfirmDialog,
42
+ CraftCreateEditDrawer: () => CraftCreateEditDrawer,
43
+ CraftCurrencyInput: () => CraftCurrencyInput,
44
+ CraftDataTable: () => CraftDataTable,
45
+ CraftDatePicker: () => CraftDatePicker,
46
+ CraftDrawer: () => CraftDrawer,
47
+ CraftEmptyState: () => CraftEmptyState,
48
+ CraftFilterBar: () => CraftFilterBar,
49
+ CraftForm: () => CraftForm,
50
+ CraftFormBuilder: () => CraftFormBuilder,
51
+ CraftFormField: () => CraftFormField,
34
52
  CraftInput: () => CraftInput,
53
+ CraftModal: () => CraftModal,
54
+ CraftNumberInput: () => CraftNumberInput,
55
+ CraftPagination: () => CraftPagination,
56
+ CraftSelect: () => CraftSelect,
57
+ CraftSkeleton: () => CraftSkeleton,
58
+ CraftSubmitButton: () => CraftSubmitButton,
59
+ CraftSwitch: () => CraftSwitch,
60
+ CraftTabs: () => CraftTabs,
61
+ CraftTextarea: () => CraftTextarea,
62
+ CraftToastHost: () => CraftToastHost,
63
+ CraftTooltip: () => CraftTooltip,
35
64
  GlassCard: () => GlassCard,
65
+ Grid: () => Grid,
66
+ PageHeader: () => PageHeader,
67
+ Sidebar: () => Sidebar,
36
68
  ThemeProvider: () => ThemeProvider,
37
69
  ThemeSwitcher: () => ThemeSwitcher,
70
+ TopNav: () => TopNav,
71
+ useCraftToast: () => useCraftToast,
38
72
  useTheme: () => useTheme
39
73
  });
40
74
  module.exports = __toCommonJS(index_exports);
@@ -53,7 +87,7 @@ var sizeClasses = {
53
87
  };
54
88
  var variantClasses = {
55
89
  solid: "bg-gradient-to-br from-[rgb(var(--nc-accent-1))] via-[rgb(var(--nc-accent-2))] to-[rgb(var(--nc-accent-3))] text-white shadow-[0_12px_30px_rgb(var(--nc-accent-1)/0.45)] hover:shadow-[0_16px_36px_rgb(var(--nc-accent-1)/0.6)] hover:scale-[1.02] active:scale-[0.98]",
56
- ghost: "bg-white/5 text-white hover:bg-white/10 backdrop-blur-sm border border-white/10 hover:border-white/20",
90
+ ghost: "bg-[color:rgb(var(--nc-surface)/0.12)] text-[rgb(var(--nc-fg))] hover:bg-[color:rgb(var(--nc-surface)/0.18)] backdrop-blur-sm border border-[rgb(var(--nc-border)/0.35)] hover:border-[color:rgb(var(--nc-border)/0.5)]",
57
91
  outline: "bg-transparent text-[color:rgb(var(--nc-accent-1))] border-2 border-[color:rgb(var(--nc-accent-1)/0.5)] hover:border-[color:rgb(var(--nc-accent-1))] hover:bg-[color:rgb(var(--nc-accent-1)/0.1)]",
58
92
  gradient: "bg-gradient-to-r from-[rgb(var(--nc-accent-1))] via-[rgb(var(--nc-accent-2))] to-[rgb(var(--nc-accent-3))] text-white shadow-[0_12px_30px_rgb(var(--nc-accent-2)/0.45)] hover:shadow-[0_16px_36px_rgb(var(--nc-accent-2)/0.6)] hover:scale-[1.02] active:scale-[0.98]"
59
93
  };
@@ -102,7 +136,7 @@ function GlassCard({
102
136
  "div",
103
137
  {
104
138
  className: cn(
105
- "relative overflow-hidden rounded-3xl p-6 text-white",
139
+ "relative overflow-hidden rounded-3xl p-6 text-[rgb(var(--nc-fg))]",
106
140
  "shadow-[0_8px_32px_rgba(0,0,0,0.3)]",
107
141
  "transition-all duration-300",
108
142
  "hover:shadow-[0_8px_40px_rgba(0,0,0,0.4)]",
@@ -131,21 +165,21 @@ var inputSizeClasses = {
131
165
  var CraftInput = React.forwardRef(
132
166
  ({ className, tone, inputSize = "md", glow = true, icon, ...props }, ref) => {
133
167
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "relative w-full", "data-nc-theme": tone, children: [
134
- icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "absolute left-4 top-1/2 -translate-y-1/2 text-white/50", children: icon }),
168
+ icon && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "absolute left-4 top-1/2 -translate-y-1/2 text-[rgb(var(--nc-fg-soft))]", children: icon }),
135
169
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
136
170
  "input",
137
171
  {
138
172
  ref,
139
173
  className: cn(
140
- "w-full rounded-2xl border-2 bg-white/5 text-white backdrop-blur-xl",
174
+ "w-full rounded-2xl border-2 bg-[rgb(var(--nc-surface)/0.08)] text-[rgb(var(--nc-fg))] backdrop-blur-xl",
141
175
  "shadow-[inset_0_2px_8px_rgba(0,0,0,0.3)]",
142
176
  "focus:outline-none focus:ring-4",
143
177
  "transition-all duration-300",
144
178
  "disabled:opacity-50 disabled:cursor-not-allowed",
145
179
  inputSizeClasses[inputSize],
146
- "border-[rgb(var(--nc-accent-1)/0.3)]",
180
+ "border-[rgb(var(--nc-border)/0.35)]",
147
181
  "focus:border-[rgb(var(--nc-accent-1)/0.8)] focus:ring-[rgb(var(--nc-accent-1)/0.3)]",
148
- "placeholder:text-[rgb(var(--nc-accent-soft)/0.4)]",
182
+ "placeholder:text-[rgb(var(--nc-fg-soft))]",
149
183
  glow ? "focus:shadow-[0_0_30px_-5px_var(--glow-color)]" : "",
150
184
  icon ? "pl-12" : "",
151
185
  className
@@ -161,9 +195,2456 @@ var CraftInput = React.forwardRef(
161
195
  );
162
196
  CraftInput.displayName = "CraftInput";
163
197
 
164
- // src/theme/theme-context.tsx
198
+ // src/components/craft-textarea.tsx
165
199
  var React2 = __toESM(require("react"), 1);
166
200
  var import_jsx_runtime4 = require("react/jsx-runtime");
201
+ var CraftTextarea = React2.forwardRef(
202
+ ({ className, tone, rows = 4, ...props }, ref) => {
203
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "relative w-full", "data-nc-theme": tone, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
204
+ "textarea",
205
+ {
206
+ ref,
207
+ rows,
208
+ className: cn(
209
+ "w-full rounded-2xl border-2 bg-[rgb(var(--nc-surface)/0.08)] text-[rgb(var(--nc-fg))] backdrop-blur-xl",
210
+ "shadow-[inset_0_2px_8px_rgba(0,0,0,0.3)]",
211
+ "focus:outline-none focus:ring-4",
212
+ "transition-all duration-300",
213
+ "disabled:opacity-50 disabled:cursor-not-allowed",
214
+ "border-[rgb(var(--nc-border)/0.35)]",
215
+ "focus:border-[rgb(var(--nc-accent-1)/0.8)] focus:ring-[rgb(var(--nc-accent-1)/0.3)]",
216
+ "placeholder:text-[rgb(var(--nc-fg-soft))]",
217
+ "px-5 py-3 text-base",
218
+ className
219
+ ),
220
+ style: {
221
+ "--glow-color": "rgb(var(--nc-accent-1) / 0.5)"
222
+ },
223
+ ...props
224
+ }
225
+ ) });
226
+ }
227
+ );
228
+ CraftTextarea.displayName = "CraftTextarea";
229
+
230
+ // src/components/craft-select.tsx
231
+ var React3 = __toESM(require("react"), 1);
232
+ var import_jsx_runtime5 = require("react/jsx-runtime");
233
+ var CraftSelect = React3.forwardRef(
234
+ ({ className, tone, children, ...props }, ref) => {
235
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative w-full", "data-nc-theme": tone, children: [
236
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
237
+ "select",
238
+ {
239
+ ref,
240
+ className: cn(
241
+ "w-full appearance-none rounded-2xl border-2 bg-[rgb(var(--nc-surface)/0.08)] text-[rgb(var(--nc-fg))] backdrop-blur-xl",
242
+ "shadow-[inset_0_2px_8px_rgba(0,0,0,0.3)]",
243
+ "focus:outline-none focus:ring-4",
244
+ "transition-all duration-300",
245
+ "disabled:opacity-50 disabled:cursor-not-allowed",
246
+ "border-[rgb(var(--nc-border)/0.35)]",
247
+ "focus:border-[rgb(var(--nc-accent-1)/0.8)] focus:ring-[rgb(var(--nc-accent-1)/0.3)]",
248
+ "px-5 py-3 pr-10 text-base",
249
+ className
250
+ ),
251
+ ...props,
252
+ children
253
+ }
254
+ ),
255
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
256
+ "svg",
257
+ {
258
+ className: "pointer-events-none absolute right-4 top-1/2 h-4 w-4 -translate-y-1/2 text-[rgb(var(--nc-fg-soft))]",
259
+ viewBox: "0 0 20 20",
260
+ fill: "currentColor",
261
+ "aria-hidden": "true",
262
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
263
+ "path",
264
+ {
265
+ fillRule: "evenodd",
266
+ d: "M5.23 7.21a.75.75 0 011.06.02L10 10.94l3.71-3.7a.75.75 0 111.06 1.06l-4.24 4.24a.75.75 0 01-1.06 0L5.21 8.29a.75.75 0 01.02-1.08z",
267
+ clipRule: "evenodd"
268
+ }
269
+ )
270
+ }
271
+ )
272
+ ] });
273
+ }
274
+ );
275
+ CraftSelect.displayName = "CraftSelect";
276
+
277
+ // src/components/craft-checkbox.tsx
278
+ var React4 = __toESM(require("react"), 1);
279
+ var import_jsx_runtime6 = require("react/jsx-runtime");
280
+ var CraftCheckbox = React4.forwardRef(
281
+ ({ className, tone, label, description, ...props }, ref) => {
282
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
283
+ "label",
284
+ {
285
+ className: cn(
286
+ "flex items-start gap-3 text-sm text-[rgb(var(--nc-fg))]",
287
+ props.disabled ? "opacity-60" : "cursor-pointer",
288
+ className
289
+ ),
290
+ "data-nc-theme": tone,
291
+ children: [
292
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "relative mt-0.5", children: [
293
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
294
+ "input",
295
+ {
296
+ ref,
297
+ type: "checkbox",
298
+ className: "peer sr-only",
299
+ ...props
300
+ }
301
+ ),
302
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
303
+ "span",
304
+ {
305
+ className: cn(
306
+ "flex h-5 w-5 items-center justify-center rounded-md border-2",
307
+ "border-[rgb(var(--nc-border)/0.45)] bg-[rgb(var(--nc-surface)/0.08)]",
308
+ "transition-all duration-200",
309
+ "peer-checked:border-[rgb(var(--nc-accent-1))] peer-checked:bg-[rgb(var(--nc-accent-1)/0.25)]",
310
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-[rgb(var(--nc-accent-1)/0.5)]"
311
+ ),
312
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
313
+ "svg",
314
+ {
315
+ className: "h-3 w-3 text-[rgb(var(--nc-fg))] opacity-0 transition-opacity peer-checked:opacity-100",
316
+ viewBox: "0 0 20 20",
317
+ fill: "currentColor",
318
+ "aria-hidden": "true",
319
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
320
+ "path",
321
+ {
322
+ fillRule: "evenodd",
323
+ d: "M16.704 5.29a1 1 0 010 1.415l-7.2 7.2a1 1 0 01-1.415 0l-3.2-3.2a1 1 0 111.415-1.415l2.492 2.493 6.493-6.493a1 1 0 011.415 0z",
324
+ clipRule: "evenodd"
325
+ }
326
+ )
327
+ }
328
+ )
329
+ }
330
+ )
331
+ ] }),
332
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "space-y-1", children: [
333
+ label && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "block font-medium text-[rgb(var(--nc-fg))]", children: label }),
334
+ description && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "block text-xs text-[rgb(var(--nc-fg-muted))]", children: description })
335
+ ] })
336
+ ]
337
+ }
338
+ );
339
+ }
340
+ );
341
+ CraftCheckbox.displayName = "CraftCheckbox";
342
+
343
+ // src/components/craft-switch.tsx
344
+ var React5 = __toESM(require("react"), 1);
345
+ var import_jsx_runtime7 = require("react/jsx-runtime");
346
+ var CraftSwitch = React5.forwardRef(
347
+ ({ className, tone, label, ...props }, ref) => {
348
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
349
+ "label",
350
+ {
351
+ className: cn(
352
+ "inline-flex items-center gap-3 text-sm text-[rgb(var(--nc-fg))]",
353
+ props.disabled ? "opacity-60" : "cursor-pointer",
354
+ className
355
+ ),
356
+ "data-nc-theme": tone,
357
+ children: [
358
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("input", { ref, type: "checkbox", className: "peer sr-only", ...props }),
359
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
360
+ "span",
361
+ {
362
+ className: cn(
363
+ "relative h-6 w-11 rounded-full border-2 border-[rgb(var(--nc-border)/0.35)] bg-[rgb(var(--nc-surface)/0.08)]",
364
+ "transition-all duration-200",
365
+ "peer-focus-visible:ring-2 peer-focus-visible:ring-[rgb(var(--nc-accent-1)/0.5)]",
366
+ "peer-checked:border-[rgb(var(--nc-accent-1)/0.6)] peer-checked:bg-[rgb(var(--nc-accent-1)/0.25)]"
367
+ ),
368
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
369
+ "span",
370
+ {
371
+ className: cn(
372
+ "absolute left-0.5 top-0.5 h-4 w-4 rounded-full bg-[rgb(var(--nc-surface-muted)/0.9)]",
373
+ "transition-all duration-200",
374
+ "peer-checked:translate-x-5 peer-checked:bg-[rgb(var(--nc-surface-muted))]"
375
+ )
376
+ }
377
+ )
378
+ }
379
+ ),
380
+ label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children: label })
381
+ ]
382
+ }
383
+ );
384
+ }
385
+ );
386
+ CraftSwitch.displayName = "CraftSwitch";
387
+
388
+ // src/components/craft-badge.tsx
389
+ var import_jsx_runtime8 = require("react/jsx-runtime");
390
+ var variantClasses2 = {
391
+ solid: "bg-[color:rgb(var(--nc-accent-1))] text-white shadow-[0_10px_20px_rgb(var(--nc-accent-1)/0.35)]",
392
+ soft: "bg-[color:rgb(var(--nc-accent-1)/0.2)] text-[rgb(var(--nc-fg))]",
393
+ outline: "border border-[color:rgb(var(--nc-accent-1)/0.6)] text-[rgb(var(--nc-fg))]"
394
+ };
395
+ function CraftBadge({
396
+ className,
397
+ variant = "soft",
398
+ tone,
399
+ ...props
400
+ }) {
401
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
402
+ "span",
403
+ {
404
+ className: cn(
405
+ "inline-flex items-center rounded-full px-3 py-1 text-xs font-semibold uppercase tracking-wide",
406
+ variantClasses2[variant],
407
+ className
408
+ ),
409
+ "data-nc-theme": tone,
410
+ ...props
411
+ }
412
+ );
413
+ }
414
+
415
+ // src/components/craft-card.tsx
416
+ var import_jsx_runtime9 = require("react/jsx-runtime");
417
+ function CraftCard({ className, tone, elevated = true, ...props }) {
418
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
419
+ "div",
420
+ {
421
+ className: cn(
422
+ "rounded-3xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.08)] p-6 text-[rgb(var(--nc-fg))] backdrop-blur-xl",
423
+ elevated && "shadow-[0_18px_40px_rgba(0,0,0,0.35)]",
424
+ "transition-all duration-300",
425
+ className
426
+ ),
427
+ "data-nc-theme": tone,
428
+ ...props
429
+ }
430
+ );
431
+ }
432
+
433
+ // src/components/craft-modal.tsx
434
+ var React6 = __toESM(require("react"), 1);
435
+ var import_react_dom = require("react-dom");
436
+ var import_jsx_runtime10 = require("react/jsx-runtime");
437
+ var FOCUSABLE_SELECTORS = [
438
+ "a[href]",
439
+ "button:not([disabled])",
440
+ "textarea:not([disabled])",
441
+ "input:not([disabled])",
442
+ "select:not([disabled])",
443
+ "[tabindex]:not([tabindex='-1'])"
444
+ ].join(",");
445
+ function useFocusTrap(active) {
446
+ const ref = React6.useRef(null);
447
+ React6.useEffect(() => {
448
+ if (!active || !ref.current) return;
449
+ const root = ref.current;
450
+ const getFocusable = () => Array.from(root.querySelectorAll(FOCUSABLE_SELECTORS));
451
+ const focusables = getFocusable();
452
+ if (focusables.length) {
453
+ focusables[0].focus();
454
+ } else {
455
+ root.focus();
456
+ }
457
+ const handleKeyDown = (event) => {
458
+ if (event.key !== "Tab") return;
459
+ const items = getFocusable();
460
+ if (!items.length) return;
461
+ const first = items[0];
462
+ const last = items[items.length - 1];
463
+ const activeEl = document.activeElement;
464
+ if (event.shiftKey && activeEl === first) {
465
+ event.preventDefault();
466
+ last.focus();
467
+ } else if (!event.shiftKey && activeEl === last) {
468
+ event.preventDefault();
469
+ first.focus();
470
+ }
471
+ };
472
+ root.addEventListener("keydown", handleKeyDown);
473
+ return () => root.removeEventListener("keydown", handleKeyDown);
474
+ }, [active]);
475
+ return ref;
476
+ }
477
+ function CraftModal({
478
+ open,
479
+ defaultOpen = false,
480
+ onOpenChange,
481
+ tone,
482
+ title,
483
+ description,
484
+ children,
485
+ trigger,
486
+ footer,
487
+ className
488
+ }) {
489
+ const [uncontrolledOpen, setUncontrolledOpen] = React6.useState(defaultOpen);
490
+ const isControlled = typeof open === "boolean";
491
+ const isOpen = isControlled ? open : uncontrolledOpen;
492
+ const setOpen = React6.useCallback(
493
+ (next) => {
494
+ if (!isControlled) {
495
+ setUncontrolledOpen(next);
496
+ }
497
+ onOpenChange == null ? void 0 : onOpenChange(next);
498
+ },
499
+ [isControlled, onOpenChange]
500
+ );
501
+ React6.useEffect(() => {
502
+ if (!isOpen) return;
503
+ const handleKey = (event) => {
504
+ if (event.key === "Escape") setOpen(false);
505
+ };
506
+ document.addEventListener("keydown", handleKey);
507
+ return () => document.removeEventListener("keydown", handleKey);
508
+ }, [isOpen, setOpen]);
509
+ const ref = useFocusTrap(isOpen);
510
+ const content = isOpen ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "fixed inset-0 z-50 flex items-center justify-center px-4 py-8", children: [
511
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
512
+ "div",
513
+ {
514
+ className: "absolute inset-0 backdrop-blur-sm",
515
+ onClick: () => setOpen(false)
516
+ }
517
+ ),
518
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
519
+ "div",
520
+ {
521
+ ref,
522
+ tabIndex: -1,
523
+ className: cn(
524
+ "relative z-10 w-full max-w-7xl rounded-3xl border border-[rgb(var(--nc-border)/0.45)] p-6 text-[rgb(var(--nc-fg))] shadow-[0_20px_60px_rgba(0,0,0,0.45)] backdrop-blur-2xl",
525
+ "max-h-[calc(100vh-1rem)] overflow-y-auto",
526
+ className
527
+ ),
528
+ "data-nc-theme": tone,
529
+ children: [
530
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-start justify-between gap-4", children: [
531
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "space-y-1", children: [
532
+ title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h3", { className: "text-2xl font-semibold", children: title }),
533
+ description && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-[rgb(var(--nc-fg-muted))]", children: description })
534
+ ] }),
535
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
536
+ "button",
537
+ {
538
+ className: "rounded-full border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] p-2 text-[rgb(var(--nc-fg-soft))] transition hover:text-[rgb(var(--nc-fg))]",
539
+ onClick: () => setOpen(false),
540
+ "aria-label": "Close",
541
+ children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("svg", { viewBox: "0 0 20 20", className: "h-4 w-4", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" }) })
542
+ }
543
+ )
544
+ ] }),
545
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-5 space-y-4", children }),
546
+ footer && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "mt-6", children: footer })
547
+ ]
548
+ }
549
+ )
550
+ ] }) : null;
551
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
552
+ trigger && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
553
+ "span",
554
+ {
555
+ onClick: () => setOpen(true),
556
+ onKeyDown: (event) => {
557
+ if (event.key === "Enter" || event.key === " ") setOpen(true);
558
+ },
559
+ role: "button",
560
+ tabIndex: 0,
561
+ className: "inline-flex",
562
+ children: trigger
563
+ }
564
+ ),
565
+ typeof document !== "undefined" && content ? (0, import_react_dom.createPortal)(content, document.body) : content
566
+ ] });
567
+ }
568
+
569
+ // src/components/craft-drawer.tsx
570
+ var React7 = __toESM(require("react"), 1);
571
+ var import_react_dom2 = require("react-dom");
572
+ var import_jsx_runtime11 = require("react/jsx-runtime");
573
+ function CraftDrawer({
574
+ open,
575
+ defaultOpen = false,
576
+ onOpenChange,
577
+ tone,
578
+ side = "left",
579
+ title,
580
+ children,
581
+ trigger,
582
+ footer,
583
+ className
584
+ }) {
585
+ const [uncontrolledOpen, setUncontrolledOpen] = React7.useState(defaultOpen);
586
+ const isControlled = typeof open === "boolean";
587
+ const isOpen = isControlled ? open : uncontrolledOpen;
588
+ const setOpen = React7.useCallback(
589
+ (next) => {
590
+ if (!isControlled) setUncontrolledOpen(next);
591
+ onOpenChange == null ? void 0 : onOpenChange(next);
592
+ },
593
+ [isControlled, onOpenChange]
594
+ );
595
+ React7.useEffect(() => {
596
+ if (!isOpen) return;
597
+ const handleKey = (event) => {
598
+ if (event.key === "Escape") setOpen(false);
599
+ };
600
+ document.addEventListener("keydown", handleKey);
601
+ return () => document.removeEventListener("keydown", handleKey);
602
+ }, [isOpen, setOpen]);
603
+ const content = isOpen ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "fixed inset-0 z-50 overflow-hidden", children: [
604
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
605
+ "div",
606
+ {
607
+ className: "absolute inset-0 backdrop-blur-sm",
608
+ onClick: () => setOpen(false)
609
+ }
610
+ ),
611
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
612
+ "div",
613
+ {
614
+ className: cn(
615
+ "absolute top-0 h-full w-full max-w-md border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] text-[rgb(var(--nc-fg))] shadow-[0_20px_60px_rgba(0,0,0,0.45)] backdrop-blur-2xl",
616
+ side === "right" ? "right-0" : "left-0",
617
+ className
618
+ ),
619
+ "data-nc-theme": tone,
620
+ children: [
621
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between border-b border-[rgb(var(--nc-border)/0.3)] p-6", children: [
622
+ title && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: "text-xl font-semibold", children: title }),
623
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
624
+ "button",
625
+ {
626
+ className: "rounded-full border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] p-2 text-[rgb(var(--nc-fg-soft))] transition hover:text-[rgb(var(--nc-fg))]",
627
+ onClick: () => setOpen(false),
628
+ "aria-label": "Close",
629
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("svg", { viewBox: "0 0 20 20", className: "h-4 w-4", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("path", { d: "M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" }) })
630
+ }
631
+ )
632
+ ] }),
633
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "p-6 space-y-4 overflow-y-auto h-[calc(100%-5.5rem)]", children }),
634
+ footer && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "border-t border-[rgb(var(--nc-border)/0.3)] p-6", children: footer })
635
+ ]
636
+ }
637
+ )
638
+ ] }) : null;
639
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
640
+ trigger && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
641
+ "span",
642
+ {
643
+ onClick: () => setOpen(true),
644
+ onKeyDown: (event) => {
645
+ if (event.key === "Enter" || event.key === " ") setOpen(true);
646
+ },
647
+ role: "button",
648
+ tabIndex: 0,
649
+ className: "inline-flex",
650
+ children: trigger
651
+ }
652
+ ),
653
+ typeof document !== "undefined" && content ? (0, import_react_dom2.createPortal)(content, document.body) : content
654
+ ] });
655
+ }
656
+
657
+ // src/components/craft-tabs.tsx
658
+ var React8 = __toESM(require("react"), 1);
659
+ var import_jsx_runtime12 = require("react/jsx-runtime");
660
+ function CraftTabs({
661
+ value,
662
+ defaultValue,
663
+ onValueChange,
664
+ tone,
665
+ tabs,
666
+ panels,
667
+ className
668
+ }) {
669
+ var _a, _b;
670
+ const fallback = (_b = (_a = tabs[0]) == null ? void 0 : _a.value) != null ? _b : "";
671
+ const [uncontrolledValue, setUncontrolledValue] = React8.useState(
672
+ defaultValue != null ? defaultValue : fallback
673
+ );
674
+ const isControlled = value !== void 0;
675
+ const activeValue = isControlled ? value : uncontrolledValue;
676
+ const setValue = React8.useCallback(
677
+ (next) => {
678
+ if (!isControlled) setUncontrolledValue(next);
679
+ onValueChange == null ? void 0 : onValueChange(next);
680
+ },
681
+ [isControlled, onValueChange]
682
+ );
683
+ const onKeyDown = (event) => {
684
+ if (!tabs.length) return;
685
+ const currentIndex = tabs.findIndex((tab) => tab.value === activeValue);
686
+ if (event.key === "ArrowRight") {
687
+ event.preventDefault();
688
+ const next = tabs[(currentIndex + 1) % tabs.length];
689
+ setValue(next.value);
690
+ }
691
+ if (event.key === "ArrowLeft") {
692
+ event.preventDefault();
693
+ const next = tabs[(currentIndex - 1 + tabs.length) % tabs.length];
694
+ setValue(next.value);
695
+ }
696
+ };
697
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: cn("space-y-4", className), "data-nc-theme": tone, children: [
698
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
699
+ "div",
700
+ {
701
+ className: "inline-flex flex-wrap items-center gap-2 rounded-full border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.08)] p-2",
702
+ role: "tablist",
703
+ onKeyDown,
704
+ children: tabs.map((tab) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
705
+ "button",
706
+ {
707
+ role: "tab",
708
+ "aria-selected": activeValue === tab.value,
709
+ onClick: () => setValue(tab.value),
710
+ className: cn(
711
+ "rounded-full px-4 py-2 text-sm font-semibold transition-all",
712
+ activeValue === tab.value ? "bg-[rgb(var(--nc-accent-1)/0.65)] text-white shadow-[0_7px_5px_rgb(var(--nc-accent-1)/0.35)]" : "text-[rgb(var(--nc-fg-muted))] hover:text-[rgb(var(--nc-fg))]"
713
+ ),
714
+ children: tab.label
715
+ },
716
+ tab.value
717
+ ))
718
+ }
719
+ ),
720
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "rounded-2xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.08)] p-4 text-[rgb(var(--nc-fg))]", children: panels[activeValue] })
721
+ ] });
722
+ }
723
+
724
+ // src/components/craft-tooltip.tsx
725
+ var React9 = __toESM(require("react"), 1);
726
+ var import_jsx_runtime13 = require("react/jsx-runtime");
727
+ function CraftTooltip({ content, tone, children, side = "top" }) {
728
+ const [open, setOpen] = React9.useState(false);
729
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
730
+ "span",
731
+ {
732
+ className: "relative inline-flex",
733
+ onMouseEnter: () => setOpen(true),
734
+ onMouseLeave: () => setOpen(false),
735
+ onFocus: () => setOpen(true),
736
+ onBlur: () => setOpen(false),
737
+ children: [
738
+ children,
739
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
740
+ "span",
741
+ {
742
+ className: cn(
743
+ "pointer-events-none absolute z-20 whitespace-nowrap rounded-lg border border-white/10 bg-black/80 px-3 py-2 text-xs text-white shadow-lg transition-all",
744
+ "backdrop-blur-xl",
745
+ open ? "opacity-100 translate-y-0" : "opacity-0 translate-y-1",
746
+ side === "top" && "bottom-full left-1/2 -translate-x-1/2 -translate-y-2",
747
+ side === "bottom" && "top-full left-1/2 -translate-x-1/2 translate-y-2",
748
+ side === "left" && "right-full top-1/2 -translate-y-1/2 -translate-x-2",
749
+ side === "right" && "left-full top-1/2 -translate-y-1/2 translate-x-2"
750
+ ),
751
+ "data-nc-theme": tone,
752
+ role: "tooltip",
753
+ children: content
754
+ }
755
+ )
756
+ ]
757
+ }
758
+ );
759
+ }
760
+
761
+ // src/components/craft-toast.tsx
762
+ var React10 = __toESM(require("react"), 1);
763
+ var import_jsx_runtime14 = require("react/jsx-runtime");
764
+ var variantClasses3 = {
765
+ info: "border-[color:rgb(var(--nc-accent-1)/0.4)]",
766
+ success: "border-emerald-400/40",
767
+ warning: "border-amber-400/40",
768
+ error: "border-rose-400/40"
769
+ };
770
+ function useCraftToast() {
771
+ const [toasts, setToasts] = React10.useState([]);
772
+ const push = React10.useCallback((toast) => {
773
+ const id = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
774
+ setToasts((prev) => [...prev, { ...toast, id }]);
775
+ return id;
776
+ }, []);
777
+ const remove = React10.useCallback((id) => {
778
+ setToasts((prev) => prev.filter((toast) => toast.id !== id));
779
+ }, []);
780
+ return { toasts, push, remove };
781
+ }
782
+ function CraftToastHost({ toasts, onDismiss, tone }) {
783
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
784
+ "div",
785
+ {
786
+ className: "fixed right-6 top-6 z-50 flex w-full max-w-sm flex-col gap-3",
787
+ "data-nc-theme": tone,
788
+ children: toasts.map((toast) => {
789
+ var _a;
790
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
791
+ "div",
792
+ {
793
+ className: cn(
794
+ "rounded-2xl border bg-[rgb(var(--nc-surface)/0.12)] p-4 text-[rgb(var(--nc-fg))] shadow-[0_15px_35px_rgba(0,0,0,0.35)] backdrop-blur-xl",
795
+ variantClasses3[(_a = toast.variant) != null ? _a : "info"]
796
+ ),
797
+ children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-start justify-between gap-4", children: [
798
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
799
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-sm font-semibold", children: toast.title }),
800
+ toast.description && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "text-xs text-[rgb(var(--nc-fg-muted))]", children: toast.description })
801
+ ] }),
802
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
803
+ "button",
804
+ {
805
+ className: "text-[rgb(var(--nc-fg-soft))] hover:text-[rgb(var(--nc-fg))]",
806
+ onClick: () => onDismiss(toast.id),
807
+ children: "\u2715"
808
+ }
809
+ )
810
+ ] })
811
+ },
812
+ toast.id
813
+ );
814
+ })
815
+ }
816
+ );
817
+ }
818
+
819
+ // src/components/craft-skeleton.tsx
820
+ var import_jsx_runtime15 = require("react/jsx-runtime");
821
+ function CraftSkeleton({ className, tone, ...props }) {
822
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
823
+ "div",
824
+ {
825
+ className: cn(
826
+ "relative overflow-hidden rounded-2xl bg-[rgb(var(--nc-surface)/0.12)]",
827
+ "after:absolute after:inset-0 after:-translate-x-full after:bg-linear-to-r after:from-transparent after:via-white/20 after:to-transparent",
828
+ "after:animate-[shimmer_1.6s_infinite]",
829
+ className
830
+ ),
831
+ "data-nc-theme": tone,
832
+ ...props
833
+ }
834
+ );
835
+ }
836
+
837
+ // src/components/craft-empty-state.tsx
838
+ var import_jsx_runtime16 = require("react/jsx-runtime");
839
+ function CraftEmptyState({
840
+ className,
841
+ tone,
842
+ title,
843
+ description,
844
+ icon,
845
+ action,
846
+ ...props
847
+ }) {
848
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
849
+ "div",
850
+ {
851
+ className: cn(
852
+ "rounded-3xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.08)] p-8 text-center text-[rgb(var(--nc-fg))] backdrop-blur-xl",
853
+ "shadow-[0_18px_40px_rgba(0,0,0,0.25)]",
854
+ className
855
+ ),
856
+ "data-nc-theme": tone,
857
+ ...props,
858
+ children: [
859
+ icon && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-2xl bg-[rgb(var(--nc-accent-1)/0.2)] text-[rgb(var(--nc-accent-1))]", children: icon }),
860
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("h3", { className: "text-xl font-semibold", children: title }),
861
+ description && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "mt-2 text-sm text-[rgb(var(--nc-fg-muted))]", children: description }),
862
+ action && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "mt-6 flex justify-center", children: action })
863
+ ]
864
+ }
865
+ );
866
+ }
867
+
868
+ // src/components/craft-date-picker.tsx
869
+ var React11 = __toESM(require("react"), 1);
870
+ var import_jsx_runtime17 = require("react/jsx-runtime");
871
+ var WEEK_DAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
872
+ function formatDate(date) {
873
+ const year = date.getFullYear();
874
+ const month = `${date.getMonth() + 1}`.padStart(2, "0");
875
+ const day = `${date.getDate()}`.padStart(2, "0");
876
+ return `${year}-${month}-${day}`;
877
+ }
878
+ function parseDate(value) {
879
+ if (!value) return null;
880
+ const [year, month, day] = value.split("-").map(Number);
881
+ if (!year || !month || !day) return null;
882
+ return new Date(year, month - 1, day);
883
+ }
884
+ function isSameDay(a, b) {
885
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
886
+ }
887
+ function isOutsideRange(date, min, max) {
888
+ const minDate = parseDate(min);
889
+ const maxDate = parseDate(max);
890
+ if (minDate && date < minDate) return true;
891
+ if (maxDate && date > maxDate) return true;
892
+ return false;
893
+ }
894
+ function CraftDatePicker({
895
+ value,
896
+ defaultValue,
897
+ onChange,
898
+ tone,
899
+ min,
900
+ max,
901
+ placeholder = "Select date",
902
+ className
903
+ }) {
904
+ const [open, setOpen] = React11.useState(false);
905
+ const [uncontrolledValue, setUncontrolledValue] = React11.useState(defaultValue != null ? defaultValue : "");
906
+ const isControlled = value !== void 0;
907
+ const selectedValue = isControlled ? value != null ? value : "" : uncontrolledValue;
908
+ const selectedDate = parseDate(selectedValue);
909
+ const initialMonth = selectedDate != null ? selectedDate : /* @__PURE__ */ new Date();
910
+ const [viewDate, setViewDate] = React11.useState(initialMonth);
911
+ React11.useEffect(() => {
912
+ if (selectedDate) setViewDate(selectedDate);
913
+ }, [selectedDate]);
914
+ const wrapperRef = React11.useRef(null);
915
+ React11.useEffect(() => {
916
+ if (!open) return;
917
+ const handleClick = (event) => {
918
+ var _a;
919
+ if (!((_a = wrapperRef.current) == null ? void 0 : _a.contains(event.target))) {
920
+ setOpen(false);
921
+ }
922
+ };
923
+ const handleKey = (event) => {
924
+ if (event.key === "Escape") setOpen(false);
925
+ };
926
+ document.addEventListener("mousedown", handleClick);
927
+ document.addEventListener("keydown", handleKey);
928
+ return () => {
929
+ document.removeEventListener("mousedown", handleClick);
930
+ document.removeEventListener("keydown", handleKey);
931
+ };
932
+ }, [open]);
933
+ const setValue = React11.useCallback(
934
+ (next) => {
935
+ if (!isControlled) setUncontrolledValue(next);
936
+ onChange == null ? void 0 : onChange(next);
937
+ },
938
+ [isControlled, onChange]
939
+ );
940
+ const monthStart = new Date(viewDate.getFullYear(), viewDate.getMonth(), 1);
941
+ const monthEnd = new Date(viewDate.getFullYear(), viewDate.getMonth() + 1, 0);
942
+ const startDay = monthStart.getDay();
943
+ const daysInMonth = monthEnd.getDate();
944
+ const cells = Array.from({ length: startDay + daysInMonth }, (_, i) => {
945
+ const dayNumber = i - startDay + 1;
946
+ if (dayNumber < 1) return null;
947
+ return new Date(viewDate.getFullYear(), viewDate.getMonth(), dayNumber);
948
+ });
949
+ const handleDaySelect = (date) => {
950
+ if (isOutsideRange(date, min, max)) return;
951
+ const next = formatDate(date);
952
+ setValue(next);
953
+ setOpen(false);
954
+ };
955
+ const handleKeyDown = (event) => {
956
+ if (!open) return;
957
+ if (!selectedDate) return;
958
+ const next = new Date(selectedDate);
959
+ if (event.key === "ArrowRight") next.setDate(next.getDate() + 1);
960
+ if (event.key === "ArrowLeft") next.setDate(next.getDate() - 1);
961
+ if (event.key === "ArrowDown") next.setDate(next.getDate() + 7);
962
+ if (event.key === "ArrowUp") next.setDate(next.getDate() - 7);
963
+ if (event.key === "Enter") {
964
+ event.preventDefault();
965
+ handleDaySelect(selectedDate);
966
+ return;
967
+ }
968
+ if (next.getTime() !== selectedDate.getTime()) {
969
+ event.preventDefault();
970
+ if (!isOutsideRange(next, min, max)) {
971
+ setValue(formatDate(next));
972
+ setViewDate(next);
973
+ }
974
+ }
975
+ };
976
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "relative w-full", "data-nc-theme": tone, ref: wrapperRef, children: [
977
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
978
+ "button",
979
+ {
980
+ type: "button",
981
+ onClick: () => setOpen((prev) => !prev),
982
+ className: cn(
983
+ "flex w-full items-center justify-between rounded-2xl border-2 bg-[rgb(var(--nc-surface)/0.08)] px-5 py-3 text-left text-base text-[rgb(var(--nc-fg))] backdrop-blur-xl",
984
+ "shadow-[inset_0_2px_8px_rgba(0,0,0,0.3)]",
985
+ "transition-all duration-300",
986
+ "border-[rgb(var(--nc-border)/0.35)]",
987
+ "focus:outline-none focus:ring-4 focus:ring-[rgb(var(--nc-accent-1)/0.3)]",
988
+ className
989
+ ),
990
+ children: [
991
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: selectedValue ? "text-[rgb(var(--nc-fg))]" : "text-[rgb(var(--nc-fg-soft))]", children: selectedValue || placeholder }),
992
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("svg", { className: "h-4 w-4 text-[rgb(var(--nc-fg-soft))]", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("path", { d: "M6 2a1 1 0 011 1v1h6V3a1 1 0 112 0v1h1a2 2 0 012 2v10a2 2 0 01-2 2H4a2 2 0 01-2-2V6a2 2 0 012-2h1V3a1 1 0 011-1zm10 6H4v8h12V8z" }) })
993
+ ]
994
+ }
995
+ ),
996
+ open && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
997
+ "div",
998
+ {
999
+ className: cn(
1000
+ "absolute left-0 top-full z-20 mt-3 w-full rounded-3xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/1.52)] p-4 text-[rgb(var(--nc-fg))] shadow-[0_20px_60px_rgba(0,0,0,0.55)] backdrop-blur-10xl"
1001
+ ),
1002
+ onKeyDown: handleKeyDown,
1003
+ tabIndex: -1,
1004
+ children: [
1005
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex items-center justify-between", children: [
1006
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1007
+ "button",
1008
+ {
1009
+ type: "button",
1010
+ className: "rounded-xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] px-3 py-1 text-sm text-[rgb(var(--nc-fg))]",
1011
+ onClick: () => setViewDate(
1012
+ new Date(viewDate.getFullYear(), viewDate.getMonth() - 1, 1)
1013
+ ),
1014
+ children: "Prev"
1015
+ }
1016
+ ),
1017
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-sm font-semibold", children: viewDate.toLocaleString(void 0, { month: "long", year: "numeric" }) }),
1018
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1019
+ "button",
1020
+ {
1021
+ type: "button",
1022
+ className: "rounded-xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] px-3 py-1 text-sm text-[rgb(var(--nc-fg))]",
1023
+ onClick: () => setViewDate(
1024
+ new Date(viewDate.getFullYear(), viewDate.getMonth() + 1, 1)
1025
+ ),
1026
+ children: "Next"
1027
+ }
1028
+ )
1029
+ ] }),
1030
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "mt-4 grid grid-cols-7 gap-2 text-xs text-[rgb(var(--nc-fg-muted))]", children: WEEK_DAYS.map((day) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "text-center", children: day }, day)) }),
1031
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "mt-2 grid grid-cols-7 gap-2", children: cells.map((date, index) => {
1032
+ if (!date) return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", {}, `empty-${index}`);
1033
+ const disabled = isOutsideRange(date, min, max);
1034
+ const selected = selectedDate && isSameDay(date, selectedDate);
1035
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1036
+ "button",
1037
+ {
1038
+ type: "button",
1039
+ onClick: () => handleDaySelect(date),
1040
+ disabled,
1041
+ className: cn(
1042
+ "rounded-lg py-2 text-sm transition-all",
1043
+ selected ? "bg-[rgb(var(--nc-accent-1)/0.3)] text-[rgb(var(--nc-fg))]" : "text-[rgb(var(--nc-fg-muted))] hover:bg-[rgb(var(--nc-surface)/0.12)]",
1044
+ disabled && "opacity-40 hover:bg-transparent"
1045
+ ),
1046
+ children: date.getDate()
1047
+ },
1048
+ date.toISOString()
1049
+ );
1050
+ }) })
1051
+ ]
1052
+ }
1053
+ )
1054
+ ] });
1055
+ }
1056
+
1057
+ // src/components/craft-number-input.tsx
1058
+ var React12 = __toESM(require("react"), 1);
1059
+ var import_jsx_runtime18 = require("react/jsx-runtime");
1060
+ var CraftNumberInput = React12.forwardRef(({ className, tone, ...props }, ref) => {
1061
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "relative w-full", "data-nc-theme": tone, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
1062
+ "input",
1063
+ {
1064
+ ref,
1065
+ type: "number",
1066
+ className: cn(
1067
+ "w-full rounded-2xl border-2 bg-[rgb(var(--nc-surface)/0.08)] text-[rgb(var(--nc-fg))] backdrop-blur-xl",
1068
+ "shadow-[inset_0_2px_8px_rgba(0,0,0,0.3)]",
1069
+ "focus:outline-none focus:ring-4",
1070
+ "transition-all duration-300",
1071
+ "disabled:opacity-50 disabled:cursor-not-allowed",
1072
+ "border-[rgb(var(--nc-border)/0.35)]",
1073
+ "focus:border-[rgb(var(--nc-accent-1)/0.8)] focus:ring-[rgb(var(--nc-accent-1)/0.3)]",
1074
+ "px-5 py-3 text-base",
1075
+ className
1076
+ ),
1077
+ ...props
1078
+ }
1079
+ ) });
1080
+ });
1081
+ CraftNumberInput.displayName = "CraftNumberInput";
1082
+
1083
+ // src/components/craft-currency-input.tsx
1084
+ var React13 = __toESM(require("react"), 1);
1085
+ var import_jsx_runtime19 = require("react/jsx-runtime");
1086
+ var CraftCurrencyInput = React13.forwardRef(({ className, tone, currencySymbol = "$", ...props }, ref) => {
1087
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "relative w-full", "data-nc-theme": tone, children: [
1088
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "pointer-events-none absolute left-4 top-1/2 -translate-y-1/2 text-[rgb(var(--nc-fg-soft))]", children: currencySymbol }),
1089
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
1090
+ "input",
1091
+ {
1092
+ ref,
1093
+ type: "text",
1094
+ inputMode: "decimal",
1095
+ className: cn(
1096
+ "w-full rounded-2xl border-2 bg-[rgb(var(--nc-surface)/0.08)] text-[rgb(var(--nc-fg))] backdrop-blur-xl",
1097
+ "shadow-[inset_0_2px_8px_rgba(0,0,0,0.3)]",
1098
+ "focus:outline-none focus:ring-4",
1099
+ "transition-all duration-300",
1100
+ "disabled:opacity-50 disabled:cursor-not-allowed",
1101
+ "border-[rgb(var(--nc-border)/0.35)]",
1102
+ "focus:border-[rgb(var(--nc-accent-1)/0.8)] focus:ring-[rgb(var(--nc-accent-1)/0.3)]",
1103
+ "placeholder:text-[rgb(var(--nc-fg-soft))]",
1104
+ "px-5 py-3 pl-9 text-base",
1105
+ className
1106
+ ),
1107
+ ...props
1108
+ }
1109
+ )
1110
+ ] });
1111
+ });
1112
+ CraftCurrencyInput.displayName = "CraftCurrencyInput";
1113
+
1114
+ // src/components/craft-form.tsx
1115
+ var React14 = __toESM(require("react"), 1);
1116
+ var import_react_hook_form2 = require("react-hook-form");
1117
+
1118
+ // src/components/craft-submit-button.tsx
1119
+ var import_react_hook_form = require("react-hook-form");
1120
+ var import_jsx_runtime20 = require("react/jsx-runtime");
1121
+ function CraftSubmitButton({
1122
+ className,
1123
+ tone,
1124
+ loading,
1125
+ loadingLabel = "Submitting...",
1126
+ disableWhenInvalid = true,
1127
+ disabled,
1128
+ children,
1129
+ ...props
1130
+ }) {
1131
+ var _a, _b, _c, _d;
1132
+ const form = (0, import_react_hook_form.useFormContext)();
1133
+ const isSubmitting = (_b = loading != null ? loading : (_a = form == null ? void 0 : form.formState) == null ? void 0 : _a.isSubmitting) != null ? _b : false;
1134
+ const isValid = (_d = (_c = form == null ? void 0 : form.formState) == null ? void 0 : _c.isValid) != null ? _d : true;
1135
+ const isDisabled = disabled || isSubmitting || disableWhenInvalid && !isValid;
1136
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1137
+ "button",
1138
+ {
1139
+ type: "submit",
1140
+ className: cn(
1141
+ "relative inline-flex items-center justify-center gap-2 rounded-xl px-6 py-2 text-sm font-semibold",
1142
+ "bg-linear-to-br from-[rgb(var(--nc-accent-1))] via-[rgb(var(--nc-accent-2))] to-[rgb(var(--nc-accent-3))]",
1143
+ "text-white shadow-[0_12px_30px_rgb(var(--nc-accent-1)/0.35)]",
1144
+ "transition-all duration-200",
1145
+ "hover:shadow-[0_16px_36px_rgb(var(--nc-accent-1)/0.5)] hover:scale-[1.02] active:scale-[0.98]",
1146
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[rgb(var(--nc-accent-1)/0.6)]",
1147
+ "disabled:opacity-60 disabled:cursor-not-allowed disabled:hover:scale-100",
1148
+ className
1149
+ ),
1150
+ "data-nc-theme": tone,
1151
+ disabled: isDisabled,
1152
+ ...props,
1153
+ children: [
1154
+ isSubmitting && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "inline-flex h-4 w-4 animate-spin rounded-full border-2 border-white/60 border-t-white" }),
1155
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: isSubmitting ? loadingLabel : children })
1156
+ ]
1157
+ }
1158
+ );
1159
+ }
1160
+
1161
+ // src/components/craft-form.tsx
1162
+ var import_jsx_runtime21 = require("react/jsx-runtime");
1163
+ function CraftForm({
1164
+ form,
1165
+ onSubmit,
1166
+ open,
1167
+ defaultOpen = false,
1168
+ onOpenChange,
1169
+ trigger,
1170
+ title,
1171
+ description,
1172
+ submitLabel = "Save",
1173
+ cancelLabel = "Cancel",
1174
+ tone,
1175
+ className,
1176
+ children,
1177
+ footer,
1178
+ disableSubmitWhenInvalid = true,
1179
+ closeOnSubmit = true,
1180
+ formClassName
1181
+ }) {
1182
+ const [uncontrolledOpen, setUncontrolledOpen] = React14.useState(defaultOpen);
1183
+ const isControlled = typeof open === "boolean";
1184
+ const isOpen = isControlled ? open : uncontrolledOpen;
1185
+ const setOpen = React14.useCallback(
1186
+ (next) => {
1187
+ if (!isControlled) setUncontrolledOpen(next);
1188
+ onOpenChange == null ? void 0 : onOpenChange(next);
1189
+ },
1190
+ [isControlled, onOpenChange]
1191
+ );
1192
+ const formId = React14.useId();
1193
+ const handleSubmit = form.handleSubmit(async (values) => {
1194
+ await onSubmit(values);
1195
+ if (closeOnSubmit) setOpen(false);
1196
+ });
1197
+ const footerContent = footer != null ? footer : /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-wrap items-center justify-end gap-3", children: [
1198
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CraftButton, { type: "button", variant: "ghost", onClick: () => setOpen(false), children: cancelLabel }),
1199
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1200
+ CraftSubmitButton,
1201
+ {
1202
+ form: formId,
1203
+ disableWhenInvalid: disableSubmitWhenInvalid,
1204
+ children: submitLabel
1205
+ }
1206
+ )
1207
+ ] });
1208
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_hook_form2.FormProvider, { ...form, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1209
+ CraftModal,
1210
+ {
1211
+ open: isOpen,
1212
+ onOpenChange: setOpen,
1213
+ trigger,
1214
+ title,
1215
+ description,
1216
+ tone,
1217
+ className,
1218
+ footer: footerContent,
1219
+ children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
1220
+ "form",
1221
+ {
1222
+ id: formId,
1223
+ onSubmit: handleSubmit,
1224
+ className: cn("space-y-5", formClassName),
1225
+ children
1226
+ }
1227
+ )
1228
+ }
1229
+ ) });
1230
+ }
1231
+
1232
+ // src/components/craft-form-builder.tsx
1233
+ var React15 = __toESM(require("react"), 1);
1234
+ var import_react_hook_form4 = require("react-hook-form");
1235
+
1236
+ // src/components/craft-form-field.tsx
1237
+ var import_react_hook_form3 = require("react-hook-form");
1238
+ var import_jsx_runtime22 = require("react/jsx-runtime");
1239
+ function getFieldError(errors, name) {
1240
+ if (!errors || typeof errors !== "object") return void 0;
1241
+ const segments = name.split(".");
1242
+ let current = errors;
1243
+ for (const segment of segments) {
1244
+ if (!current || typeof current !== "object") return void 0;
1245
+ current = current[segment];
1246
+ }
1247
+ return current;
1248
+ }
1249
+ var baseInputClass = "w-full rounded-2xl border-2 bg-[rgb(var(--nc-surface)/0.08)] text-[rgb(var(--nc-fg))] backdrop-blur-xl shadow-[inset_0_2px_8px_rgba(0,0,0,0.3)] focus:outline-none focus:ring-4 transition-all duration-300 disabled:opacity-50 disabled:cursor-not-allowed border-[rgb(var(--nc-border)/0.35)] focus:border-[rgb(var(--nc-accent-1)/0.8)] focus:ring-[rgb(var(--nc-accent-1)/0.3)] px-5 py-3 text-base placeholder:text-[rgb(var(--nc-fg-soft))]";
1250
+ function CraftFormField({
1251
+ name,
1252
+ label,
1253
+ description,
1254
+ type = "text",
1255
+ options = [],
1256
+ placeholder,
1257
+ tone,
1258
+ className,
1259
+ inputClassName,
1260
+ labelClassName,
1261
+ descriptionClassName,
1262
+ rules,
1263
+ disabled,
1264
+ fieldProps
1265
+ }) {
1266
+ const { register, control, formState } = (0, import_react_hook_form3.useFormContext)();
1267
+ const error = getFieldError(formState.errors, name);
1268
+ const errorMessage = typeof (error == null ? void 0 : error.message) === "string" ? error.message : void 0;
1269
+ if (type === "hidden") {
1270
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("input", { type: "hidden", ...register(name, rules) });
1271
+ }
1272
+ const labelNode = label ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1273
+ "label",
1274
+ {
1275
+ htmlFor: name,
1276
+ className: cn(
1277
+ "text-sm font-semibold text-[rgb(var(--nc-fg))]",
1278
+ labelClassName
1279
+ ),
1280
+ children: label
1281
+ }
1282
+ ) : null;
1283
+ const descriptionNode = description ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1284
+ "p",
1285
+ {
1286
+ className: cn(
1287
+ "text-xs text-[rgb(var(--nc-fg-muted))]",
1288
+ descriptionClassName
1289
+ ),
1290
+ children: description
1291
+ }
1292
+ ) : null;
1293
+ const errorNode = errorMessage ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "text-xs text-[rgb(var(--nc-accent-3))]", children: errorMessage }) : null;
1294
+ const renderInput = () => {
1295
+ if (type === "textarea") {
1296
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1297
+ CraftTextarea,
1298
+ {
1299
+ id: name,
1300
+ placeholder,
1301
+ tone,
1302
+ className: inputClassName,
1303
+ disabled,
1304
+ ...fieldProps,
1305
+ ...register(name, rules)
1306
+ }
1307
+ );
1308
+ }
1309
+ if (type === "select" || type === "multiselect") {
1310
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
1311
+ CraftSelect,
1312
+ {
1313
+ id: name,
1314
+ tone,
1315
+ className: inputClassName,
1316
+ multiple: type === "multiselect",
1317
+ disabled,
1318
+ ...fieldProps,
1319
+ ...register(name, rules),
1320
+ children: [
1321
+ placeholder && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("option", { value: "", disabled: true, children: placeholder }),
1322
+ options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1323
+ "option",
1324
+ {
1325
+ value: option.value,
1326
+ disabled: option.disabled,
1327
+ children: option.label
1328
+ },
1329
+ option.value
1330
+ ))
1331
+ ]
1332
+ }
1333
+ );
1334
+ }
1335
+ if (type === "checkbox") {
1336
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1337
+ CraftCheckbox,
1338
+ {
1339
+ tone,
1340
+ label,
1341
+ description,
1342
+ disabled,
1343
+ ...fieldProps,
1344
+ ...register(name, rules)
1345
+ }
1346
+ );
1347
+ }
1348
+ if (type === "switch") {
1349
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1350
+ CraftSwitch,
1351
+ {
1352
+ tone,
1353
+ label,
1354
+ disabled,
1355
+ ...fieldProps,
1356
+ ...register(name, rules)
1357
+ }
1358
+ );
1359
+ }
1360
+ if (type === "date") {
1361
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1362
+ import_react_hook_form3.Controller,
1363
+ {
1364
+ control,
1365
+ name,
1366
+ rules,
1367
+ render: ({ field }) => {
1368
+ var _a;
1369
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1370
+ CraftDatePicker,
1371
+ {
1372
+ value: (_a = field.value) != null ? _a : "",
1373
+ onChange: field.onChange,
1374
+ tone,
1375
+ placeholder,
1376
+ ...fieldProps
1377
+ }
1378
+ );
1379
+ }
1380
+ }
1381
+ );
1382
+ }
1383
+ if (type === "number") {
1384
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1385
+ CraftNumberInput,
1386
+ {
1387
+ id: name,
1388
+ tone,
1389
+ placeholder,
1390
+ className: inputClassName,
1391
+ disabled,
1392
+ ...fieldProps,
1393
+ ...register(name, rules)
1394
+ }
1395
+ );
1396
+ }
1397
+ if (type === "currency") {
1398
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1399
+ CraftCurrencyInput,
1400
+ {
1401
+ id: name,
1402
+ tone,
1403
+ placeholder,
1404
+ className: inputClassName,
1405
+ disabled,
1406
+ ...fieldProps,
1407
+ ...register(name, rules)
1408
+ }
1409
+ );
1410
+ }
1411
+ if (type === "radio") {
1412
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "grid gap-3", children: options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
1413
+ "label",
1414
+ {
1415
+ className: cn(
1416
+ "flex items-center gap-3 rounded-2xl border border-[rgb(var(--nc-border)/0.35)] bg-[rgb(var(--nc-surface)/0.08)] px-4 py-3 text-sm text-[rgb(var(--nc-fg))]",
1417
+ "transition-all duration-200",
1418
+ "focus-within:ring-2 focus-within:ring-[rgb(var(--nc-accent-1)/0.5)]",
1419
+ option.disabled ? "opacity-60" : "cursor-pointer"
1420
+ ),
1421
+ "data-nc-theme": tone,
1422
+ children: [
1423
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1424
+ "input",
1425
+ {
1426
+ type: "radio",
1427
+ value: option.value,
1428
+ disabled: option.disabled || disabled,
1429
+ className: "h-4 w-4 accent-[rgb(var(--nc-accent-1))]",
1430
+ ...fieldProps,
1431
+ ...register(name, rules)
1432
+ }
1433
+ ),
1434
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { children: option.label })
1435
+ ]
1436
+ },
1437
+ option.value
1438
+ )) });
1439
+ }
1440
+ if (type === "range" || type === "slider") {
1441
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1442
+ "input",
1443
+ {
1444
+ id: name,
1445
+ type: "range",
1446
+ className: cn(
1447
+ "w-full accent-[rgb(var(--nc-accent-1))]",
1448
+ inputClassName
1449
+ ),
1450
+ disabled,
1451
+ ...fieldProps,
1452
+ ...register(name, rules)
1453
+ }
1454
+ );
1455
+ }
1456
+ if (type === "file" || type === "multifile") {
1457
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1458
+ "input",
1459
+ {
1460
+ id: name,
1461
+ type: "file",
1462
+ multiple: type === "multifile",
1463
+ className: cn(
1464
+ baseInputClass,
1465
+ "file:mr-4 file:rounded-xl file:border-0 file:bg-[rgb(var(--nc-surface)/0.35)] file:px-4 file:py-2 file:text-sm file:font-semibold file:text-[rgb(var(--nc-fg))]",
1466
+ inputClassName
1467
+ ),
1468
+ disabled,
1469
+ ...fieldProps,
1470
+ ...register(name, rules)
1471
+ }
1472
+ );
1473
+ }
1474
+ const inputType = type === "search" || type === "password" || type === "email" || type === "tel" || type === "url" || type === "time" || type === "datetime-local" || type === "month" || type === "week" || type === "color" ? type : "text";
1475
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
1476
+ CraftInput,
1477
+ {
1478
+ id: name,
1479
+ type: inputType,
1480
+ placeholder,
1481
+ tone,
1482
+ className: inputClassName,
1483
+ disabled,
1484
+ ...fieldProps,
1485
+ ...register(name, rules)
1486
+ }
1487
+ );
1488
+ };
1489
+ const showLabel = type !== "checkbox" && type !== "switch";
1490
+ const showDescriptionAbove = type !== "checkbox" && type !== "switch";
1491
+ const showDescriptionBelow = type === "switch";
1492
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: cn("space-y-2", className), "data-nc-theme": tone, children: [
1493
+ showLabel ? labelNode : null,
1494
+ showDescriptionAbove ? descriptionNode : null,
1495
+ renderInput(),
1496
+ showDescriptionBelow ? descriptionNode : null,
1497
+ errorNode
1498
+ ] });
1499
+ }
1500
+
1501
+ // src/components/craft-form-builder.tsx
1502
+ var import_jsx_runtime23 = require("react/jsx-runtime");
1503
+ function defaultValueForField(field) {
1504
+ var _a, _b, _c, _d;
1505
+ if (field.defaultValue !== void 0) return field.defaultValue;
1506
+ switch (field.type) {
1507
+ case "checkbox":
1508
+ case "switch":
1509
+ return false;
1510
+ case "number":
1511
+ case "slider":
1512
+ case "range":
1513
+ return (_a = field.min) != null ? _a : 0;
1514
+ case "multifile":
1515
+ return [];
1516
+ case "file":
1517
+ return null;
1518
+ case "multiselect":
1519
+ return [];
1520
+ case "radio":
1521
+ return (_d = (_c = (_b = field.options) == null ? void 0 : _b[0]) == null ? void 0 : _c.value) != null ? _d : "";
1522
+ default:
1523
+ return "";
1524
+ }
1525
+ }
1526
+ function buildDefaultValues(fields, initialData) {
1527
+ const values = {};
1528
+ fields.forEach((field) => {
1529
+ const initialValue = initialData == null ? void 0 : initialData[field.name];
1530
+ if (initialValue !== void 0 && initialValue !== null) {
1531
+ values[field.name] = initialValue;
1532
+ } else {
1533
+ values[field.name] = defaultValueForField(field);
1534
+ }
1535
+ });
1536
+ return values;
1537
+ }
1538
+ function buildRules(field, getValues) {
1539
+ var _a;
1540
+ const rules = { ...field.rules };
1541
+ const mergeValidate = (current, next) => {
1542
+ if (!current) return next;
1543
+ if (typeof current === "function") {
1544
+ return (value) => {
1545
+ const result = current(
1546
+ value,
1547
+ getValues()
1548
+ );
1549
+ if (result !== true) return result;
1550
+ return next(value);
1551
+ };
1552
+ }
1553
+ if (typeof current === "object") {
1554
+ return (value) => {
1555
+ const entries = Object.entries(current);
1556
+ for (const [, validator] of entries) {
1557
+ const result = validator(
1558
+ value,
1559
+ getValues()
1560
+ );
1561
+ if (result !== true) return result;
1562
+ }
1563
+ return next(value);
1564
+ };
1565
+ }
1566
+ return next;
1567
+ };
1568
+ if (field.required && field.type !== "hidden") {
1569
+ if (field.type === "checkbox" || field.type === "switch") {
1570
+ rules.validate = mergeValidate(
1571
+ rules.validate,
1572
+ (value) => {
1573
+ var _a2;
1574
+ return value ? true : `${String((_a2 = field.label) != null ? _a2 : field.name)} is required`;
1575
+ }
1576
+ );
1577
+ } else if (field.type === "multiselect") {
1578
+ rules.validate = mergeValidate(
1579
+ rules.validate,
1580
+ (value) => {
1581
+ var _a2;
1582
+ return Array.isArray(value) && value.length > 0 ? true : `${String((_a2 = field.label) != null ? _a2 : field.name)} is required`;
1583
+ }
1584
+ );
1585
+ } else if (field.type === "file") {
1586
+ rules.validate = mergeValidate(
1587
+ rules.validate,
1588
+ (value) => {
1589
+ var _a2;
1590
+ return value instanceof FileList && value.length > 0 ? true : `${String((_a2 = field.label) != null ? _a2 : field.name)} is required`;
1591
+ }
1592
+ );
1593
+ } else if (field.type === "multifile") {
1594
+ rules.validate = mergeValidate(
1595
+ rules.validate,
1596
+ (value) => {
1597
+ var _a2;
1598
+ return Array.isArray(value) && value.length > 0 ? true : `${String((_a2 = field.label) != null ? _a2 : field.name)} is required`;
1599
+ }
1600
+ );
1601
+ } else {
1602
+ rules.required = `${String((_a = field.label) != null ? _a : field.name)} is required`;
1603
+ }
1604
+ }
1605
+ if (field.min !== void 0) {
1606
+ rules.min = { value: field.min, message: `Min ${field.min}` };
1607
+ }
1608
+ if (field.max !== void 0) {
1609
+ rules.max = { value: field.max, message: `Max ${field.max}` };
1610
+ }
1611
+ if (field.type === "email") {
1612
+ rules.pattern = {
1613
+ value: /^[^\s@]+@[^\s@]+\.[^\s@]+$/,
1614
+ message: "Please enter a valid email address"
1615
+ };
1616
+ }
1617
+ if (field.type === "url") {
1618
+ rules.pattern = {
1619
+ value: /^https?:\/\/.+/,
1620
+ message: "Please enter a valid URL"
1621
+ };
1622
+ }
1623
+ if (field.validate) {
1624
+ rules.validate = mergeValidate(
1625
+ rules.validate,
1626
+ (value) => {
1627
+ var _a2;
1628
+ return (_a2 = field.validate) == null ? void 0 : _a2.call(field, value, getValues());
1629
+ }
1630
+ );
1631
+ }
1632
+ return rules;
1633
+ }
1634
+ function CraftFormBuilder({
1635
+ title = "Form",
1636
+ description,
1637
+ fields,
1638
+ initialData = null,
1639
+ open,
1640
+ defaultOpen = false,
1641
+ onOpenChange,
1642
+ trigger,
1643
+ submitLabel = "Submit",
1644
+ cancelLabel = "Cancel",
1645
+ resetLabel = "Reset",
1646
+ showReset = true,
1647
+ showCancel = true,
1648
+ tone,
1649
+ className,
1650
+ formClassName,
1651
+ loading = false,
1652
+ disableSubmitWhenInvalid = true,
1653
+ closeOnSubmit = true,
1654
+ closeOnCancel = true,
1655
+ onSubmit,
1656
+ onReset,
1657
+ onCancel,
1658
+ customValidation
1659
+ }) {
1660
+ const [uncontrolledOpen, setUncontrolledOpen] = React15.useState(defaultOpen);
1661
+ const isControlled = typeof open === "boolean";
1662
+ const isOpen = isControlled ? open : uncontrolledOpen;
1663
+ const setOpen = React15.useCallback(
1664
+ (next) => {
1665
+ if (!isControlled) setUncontrolledOpen(next);
1666
+ onOpenChange == null ? void 0 : onOpenChange(next);
1667
+ },
1668
+ [isControlled, onOpenChange]
1669
+ );
1670
+ const defaultValues = React15.useMemo(
1671
+ () => buildDefaultValues(fields, initialData),
1672
+ [fields, initialData]
1673
+ );
1674
+ const form = (0, import_react_hook_form4.useForm)({
1675
+ mode: "onChange",
1676
+ defaultValues
1677
+ });
1678
+ const formId = React15.useId();
1679
+ React15.useEffect(() => {
1680
+ form.reset(defaultValues);
1681
+ }, [defaultValues, form]);
1682
+ const handleSubmit = form.handleSubmit(async (values) => {
1683
+ if (customValidation) {
1684
+ const customErrors = customValidation(values);
1685
+ if (customErrors && Object.keys(customErrors).length > 0) {
1686
+ Object.entries(customErrors).forEach(([key, message]) => {
1687
+ if (message) {
1688
+ form.setError(key, {
1689
+ type: "custom",
1690
+ message: String(message)
1691
+ });
1692
+ }
1693
+ });
1694
+ return;
1695
+ }
1696
+ }
1697
+ await onSubmit(values);
1698
+ if (closeOnSubmit) setOpen(false);
1699
+ });
1700
+ const handleReset = () => {
1701
+ form.reset(defaultValues);
1702
+ onReset == null ? void 0 : onReset();
1703
+ };
1704
+ const handleCancel = () => {
1705
+ onCancel == null ? void 0 : onCancel();
1706
+ if (closeOnCancel) setOpen(false);
1707
+ };
1708
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_hook_form4.FormProvider, { ...form, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1709
+ CraftModal,
1710
+ {
1711
+ open: isOpen,
1712
+ onOpenChange: setOpen,
1713
+ trigger,
1714
+ title,
1715
+ description,
1716
+ tone,
1717
+ className,
1718
+ footer: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-wrap items-center justify-end gap-3", children: [
1719
+ showReset && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1720
+ CraftButton,
1721
+ {
1722
+ type: "button",
1723
+ variant: "outline",
1724
+ onClick: handleReset,
1725
+ disabled: loading,
1726
+ children: resetLabel
1727
+ }
1728
+ ),
1729
+ showCancel && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1730
+ CraftButton,
1731
+ {
1732
+ type: "button",
1733
+ variant: "ghost",
1734
+ onClick: handleCancel,
1735
+ disabled: loading,
1736
+ children: cancelLabel
1737
+ }
1738
+ ),
1739
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1740
+ CraftSubmitButton,
1741
+ {
1742
+ loading,
1743
+ disableWhenInvalid: disableSubmitWhenInvalid,
1744
+ form: formId,
1745
+ children: submitLabel
1746
+ }
1747
+ )
1748
+ ] }),
1749
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1750
+ "form",
1751
+ {
1752
+ id: formId,
1753
+ onSubmit: handleSubmit,
1754
+ className: cn("space-y-5", formClassName),
1755
+ children: fields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "space-y-2", children: [
1756
+ field.helpText && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { className: "text-xs text-[rgb(var(--nc-fg-muted))]", children: field.helpText }),
1757
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
1758
+ CraftFormField,
1759
+ {
1760
+ name: field.name,
1761
+ label: field.label,
1762
+ description: field.description,
1763
+ type: field.type,
1764
+ placeholder: field.placeholder,
1765
+ options: field.options,
1766
+ tone,
1767
+ disabled: field.disabled || loading,
1768
+ rules: buildRules(field, form.getValues),
1769
+ fieldProps: {
1770
+ min: field.min,
1771
+ max: field.max,
1772
+ step: field.step,
1773
+ rows: field.rows,
1774
+ accept: field.accept,
1775
+ multiple: field.type === "multifile",
1776
+ ...field.fieldProps
1777
+ }
1778
+ }
1779
+ )
1780
+ ] }, field.name))
1781
+ }
1782
+ )
1783
+ }
1784
+ ) });
1785
+ }
1786
+
1787
+ // src/components/craft-confirm-dialog.tsx
1788
+ var React16 = __toESM(require("react"), 1);
1789
+ var import_jsx_runtime24 = require("react/jsx-runtime");
1790
+ function CraftConfirmDialog({
1791
+ open,
1792
+ defaultOpen = false,
1793
+ onOpenChange,
1794
+ tone,
1795
+ title = "Confirm action",
1796
+ description,
1797
+ confirmLabel = "Confirm",
1798
+ cancelLabel = "Cancel",
1799
+ onConfirm,
1800
+ trigger,
1801
+ className,
1802
+ confirmVariant = "solid"
1803
+ }) {
1804
+ const [uncontrolledOpen, setUncontrolledOpen] = React16.useState(defaultOpen);
1805
+ const isControlled = typeof open === "boolean";
1806
+ const isOpen = isControlled ? open : uncontrolledOpen;
1807
+ const setOpen = React16.useCallback(
1808
+ (next) => {
1809
+ if (!isControlled) setUncontrolledOpen(next);
1810
+ onOpenChange == null ? void 0 : onOpenChange(next);
1811
+ },
1812
+ [isControlled, onOpenChange]
1813
+ );
1814
+ const [isLoading, setIsLoading] = React16.useState(false);
1815
+ const handleConfirm = async () => {
1816
+ if (!onConfirm) {
1817
+ setOpen(false);
1818
+ return;
1819
+ }
1820
+ setIsLoading(true);
1821
+ await onConfirm();
1822
+ setIsLoading(false);
1823
+ setOpen(false);
1824
+ };
1825
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1826
+ CraftModal,
1827
+ {
1828
+ open: isOpen,
1829
+ onOpenChange: setOpen,
1830
+ trigger,
1831
+ title,
1832
+ description,
1833
+ tone,
1834
+ className: cn("max-w-md", className),
1835
+ footer: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-wrap items-center justify-end gap-3", children: [
1836
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1837
+ CraftButton,
1838
+ {
1839
+ type: "button",
1840
+ variant: "ghost",
1841
+ onClick: () => setOpen(false),
1842
+ children: cancelLabel
1843
+ }
1844
+ ),
1845
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
1846
+ CraftButton,
1847
+ {
1848
+ type: "button",
1849
+ variant: confirmVariant,
1850
+ disabled: isLoading,
1851
+ onClick: handleConfirm,
1852
+ children: isLoading ? "Working..." : confirmLabel
1853
+ }
1854
+ )
1855
+ ] }),
1856
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "text-sm text-[rgb(var(--nc-fg-muted))]", children: description })
1857
+ }
1858
+ );
1859
+ }
1860
+
1861
+ // src/components/craft-create-edit-drawer.tsx
1862
+ var React17 = __toESM(require("react"), 1);
1863
+ var import_react_hook_form5 = require("react-hook-form");
1864
+ var import_jsx_runtime25 = require("react/jsx-runtime");
1865
+ function CraftCreateEditDrawer({
1866
+ mode = "create",
1867
+ form,
1868
+ onSubmit,
1869
+ open,
1870
+ defaultOpen = false,
1871
+ onOpenChange,
1872
+ trigger,
1873
+ title,
1874
+ description,
1875
+ submitLabel,
1876
+ cancelLabel = "Cancel",
1877
+ tone,
1878
+ className,
1879
+ children,
1880
+ footer,
1881
+ disableSubmitWhenInvalid = true,
1882
+ closeOnSubmit = true,
1883
+ side = "right"
1884
+ }) {
1885
+ const [uncontrolledOpen, setUncontrolledOpen] = React17.useState(defaultOpen);
1886
+ const isControlled = typeof open === "boolean";
1887
+ const isOpen = isControlled ? open : uncontrolledOpen;
1888
+ const setOpen = React17.useCallback(
1889
+ (next) => {
1890
+ if (!isControlled) setUncontrolledOpen(next);
1891
+ onOpenChange == null ? void 0 : onOpenChange(next);
1892
+ },
1893
+ [isControlled, onOpenChange]
1894
+ );
1895
+ const formId = React17.useId();
1896
+ const handleSubmit = form.handleSubmit(async (values) => {
1897
+ await onSubmit(values);
1898
+ if (closeOnSubmit) setOpen(false);
1899
+ });
1900
+ const resolvedTitle = title != null ? title : mode === "create" ? "Create item" : "Edit item";
1901
+ const resolvedSubmitLabel = submitLabel != null ? submitLabel : mode === "create" ? "Create" : "Save changes";
1902
+ const footerContent = footer != null ? footer : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-wrap items-center justify-end gap-3", children: [
1903
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(CraftButton, { type: "button", variant: "ghost", onClick: () => setOpen(false), children: cancelLabel }),
1904
+ /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1905
+ CraftSubmitButton,
1906
+ {
1907
+ form: formId,
1908
+ disableWhenInvalid: disableSubmitWhenInvalid,
1909
+ children: resolvedSubmitLabel
1910
+ }
1911
+ )
1912
+ ] });
1913
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_hook_form5.FormProvider, { ...form, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
1914
+ CraftDrawer,
1915
+ {
1916
+ open: isOpen,
1917
+ onOpenChange: setOpen,
1918
+ trigger,
1919
+ title: resolvedTitle,
1920
+ tone,
1921
+ side,
1922
+ className: cn("flex flex-col", className),
1923
+ footer: footerContent,
1924
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("form", { id: formId, onSubmit: handleSubmit, className: "space-y-5", children: [
1925
+ description && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-sm text-[rgb(var(--nc-fg-muted))]", children: description }),
1926
+ children
1927
+ ] })
1928
+ }
1929
+ ) });
1930
+ }
1931
+
1932
+ // src/components/craft-filter-bar.tsx
1933
+ var import_jsx_runtime26 = require("react/jsx-runtime");
1934
+ function CraftFilterBar({
1935
+ title,
1936
+ description,
1937
+ searchValue,
1938
+ onSearchChange,
1939
+ searchPlaceholder = "Search...",
1940
+ actions,
1941
+ filters,
1942
+ tone,
1943
+ className
1944
+ }) {
1945
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
1946
+ "div",
1947
+ {
1948
+ className: cn(
1949
+ "rounded-3xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] p-4 text-[rgb(var(--nc-fg))] shadow-[0_12px_36px_rgba(0,0,0,0.2)] backdrop-blur-2xl",
1950
+ className
1951
+ ),
1952
+ "data-nc-theme": tone,
1953
+ children: [
1954
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [
1955
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { children: [
1956
+ title && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("h3", { className: "text-lg font-semibold", children: title }),
1957
+ description && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { className: "text-sm text-[rgb(var(--nc-fg-muted))]", children: description })
1958
+ ] }),
1959
+ actions && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "flex items-center gap-3", children: actions })
1960
+ ] }),
1961
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "mt-4 grid gap-4 md:grid-cols-[minmax(0,1fr)_auto]", children: [
1962
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
1963
+ CraftInput,
1964
+ {
1965
+ type: "search",
1966
+ placeholder: searchPlaceholder,
1967
+ value: searchValue != null ? searchValue : "",
1968
+ onChange: (event) => onSearchChange == null ? void 0 : onSearchChange(event.target.value),
1969
+ tone
1970
+ }
1971
+ ),
1972
+ filters && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "flex flex-wrap items-center gap-3", children: filters })
1973
+ ] })
1974
+ ]
1975
+ }
1976
+ );
1977
+ }
1978
+
1979
+ // src/components/craft-data-table.tsx
1980
+ var React18 = __toESM(require("react"), 1);
1981
+
1982
+ // src/components/craft-pagination.tsx
1983
+ var import_jsx_runtime27 = require("react/jsx-runtime");
1984
+ function getPageNumbers(pageIndex, pageCount, maxButtons = 5) {
1985
+ if (pageCount <= maxButtons) {
1986
+ return Array.from({ length: pageCount }, (_, i) => i);
1987
+ }
1988
+ const pages = [];
1989
+ const start = Math.max(0, pageIndex - 1);
1990
+ const end = Math.min(pageCount - 1, pageIndex + 1);
1991
+ pages.push(0);
1992
+ if (start > 1) pages.push("ellipsis");
1993
+ for (let i = start; i <= end; i += 1) {
1994
+ if (i !== 0 && i !== pageCount - 1) pages.push(i);
1995
+ }
1996
+ if (end < pageCount - 2) pages.push("ellipsis");
1997
+ pages.push(pageCount - 1);
1998
+ return pages;
1999
+ }
2000
+ function CraftPagination({
2001
+ pageIndex,
2002
+ pageCount,
2003
+ onPageChange,
2004
+ canPrevious = pageIndex > 0,
2005
+ canNext = pageIndex < pageCount - 1,
2006
+ pageSize,
2007
+ pageSizeOptions = [10, 20, 50],
2008
+ onPageSizeChange,
2009
+ tone,
2010
+ className
2011
+ }) {
2012
+ const pages = getPageNumbers(pageIndex, pageCount);
2013
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
2014
+ "div",
2015
+ {
2016
+ className: cn(
2017
+ "flex flex-wrap items-center justify-between gap-4",
2018
+ className
2019
+ ),
2020
+ "data-nc-theme": tone,
2021
+ children: [
2022
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-2", children: [
2023
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2024
+ "button",
2025
+ {
2026
+ type: "button",
2027
+ className: cn(
2028
+ "rounded-xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] px-3 py-2 text-xs text-[rgb(var(--nc-fg))] transition",
2029
+ "hover:bg-[rgb(var(--nc-surface)/0.2)]",
2030
+ !canPrevious && "opacity-50 cursor-not-allowed"
2031
+ ),
2032
+ onClick: () => onPageChange(Math.max(pageIndex - 1, 0)),
2033
+ disabled: !canPrevious,
2034
+ children: "Prev"
2035
+ }
2036
+ ),
2037
+ pages.map(
2038
+ (page, index) => page === "ellipsis" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "px-2 text-[rgb(var(--nc-fg-muted))]", children: "..." }, `ellipsis-${index}`) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2039
+ "button",
2040
+ {
2041
+ type: "button",
2042
+ className: cn(
2043
+ "rounded-xl border px-3 py-2 text-xs transition",
2044
+ page === pageIndex ? "border-[rgb(var(--nc-accent-1)/0.6)] bg-[rgb(var(--nc-accent-1)/0.2)] text-[rgb(var(--nc-fg))]" : "border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] text-[rgb(var(--nc-fg-muted))] hover:text-[rgb(var(--nc-fg))] hover:bg-[rgb(var(--nc-surface)/0.2)]"
2045
+ ),
2046
+ onClick: () => onPageChange(page),
2047
+ children: page + 1
2048
+ },
2049
+ page
2050
+ )
2051
+ ),
2052
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2053
+ "button",
2054
+ {
2055
+ type: "button",
2056
+ className: cn(
2057
+ "rounded-xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] px-3 py-2 text-xs text-[rgb(var(--nc-fg))] transition",
2058
+ "hover:bg-[rgb(var(--nc-surface)/0.2)]",
2059
+ !canNext && "opacity-50 cursor-not-allowed"
2060
+ ),
2061
+ onClick: () => onPageChange(Math.min(pageIndex + 1, pageCount - 1)),
2062
+ disabled: !canNext,
2063
+ children: "Next"
2064
+ }
2065
+ )
2066
+ ] }),
2067
+ onPageSizeChange && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "flex items-center gap-2 text-xs text-[rgb(var(--nc-fg-muted))]", children: [
2068
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { children: "Rows" }),
2069
+ /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
2070
+ "select",
2071
+ {
2072
+ className: "rounded-xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] px-2 py-1 text-xs text-[rgb(var(--nc-fg))]",
2073
+ value: pageSize,
2074
+ onChange: (event) => onPageSizeChange(Number(event.target.value)),
2075
+ children: pageSizeOptions.map((size) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("option", { value: size, children: size }, size))
2076
+ }
2077
+ )
2078
+ ] })
2079
+ ]
2080
+ }
2081
+ );
2082
+ }
2083
+
2084
+ // src/components/craft-data-table.tsx
2085
+ var import_jsx_runtime28 = require("react/jsx-runtime");
2086
+ function getColumnValue(column, row) {
2087
+ if (typeof column.accessor === "function") return column.accessor(row);
2088
+ const record = row;
2089
+ if (typeof column.accessor === "string") return record[column.accessor];
2090
+ return record[column.id];
2091
+ }
2092
+ function normalizeValue(value) {
2093
+ if (value === null || value === void 0) return "";
2094
+ if (typeof value === "number") return value;
2095
+ if (typeof value === "string") return value.toLowerCase();
2096
+ if (value instanceof Date) return value.getTime();
2097
+ return String(value).toLowerCase();
2098
+ }
2099
+ function CraftDataTable({
2100
+ data,
2101
+ columns,
2102
+ tone,
2103
+ className,
2104
+ loading = false,
2105
+ emptyState,
2106
+ toolbar,
2107
+ enableSorting = true,
2108
+ enableFiltering = true,
2109
+ enableColumnVisibility = true,
2110
+ enableRowSelection = true,
2111
+ enablePagination = true,
2112
+ showGlobalFilter,
2113
+ manualSorting = false,
2114
+ manualFiltering = false,
2115
+ manualPagination = false,
2116
+ sortBy,
2117
+ onSortChange,
2118
+ filters,
2119
+ onFiltersChange,
2120
+ globalFilter,
2121
+ onGlobalFilterChange,
2122
+ columnVisibility,
2123
+ onColumnVisibilityChange,
2124
+ selectedRowIds,
2125
+ onRowSelectionChange,
2126
+ getRowId,
2127
+ pageIndex,
2128
+ pageSize = 10,
2129
+ pageCount,
2130
+ onPageChange,
2131
+ onPageSizeChange
2132
+ }) {
2133
+ const [internalSort, setInternalSort] = React18.useState(null);
2134
+ const [internalFilters, setInternalFilters] = React18.useState({});
2135
+ const [internalGlobalFilter, setInternalGlobalFilter] = React18.useState("");
2136
+ const [internalVisibility, setInternalVisibility] = React18.useState(
2137
+ () => columns.reduce((acc, column) => {
2138
+ acc[column.id] = !column.hidden;
2139
+ return acc;
2140
+ }, {})
2141
+ );
2142
+ const [internalSelection, setInternalSelection] = React18.useState({});
2143
+ const [internalPageIndex, setInternalPageIndex] = React18.useState(0);
2144
+ const [showColumns, setShowColumns] = React18.useState(false);
2145
+ const resolvedSort = sortBy != null ? sortBy : internalSort;
2146
+ const resolvedFilters = filters != null ? filters : internalFilters;
2147
+ const resolvedGlobalFilter = globalFilter != null ? globalFilter : internalGlobalFilter;
2148
+ const resolvedVisibility = columnVisibility != null ? columnVisibility : internalVisibility;
2149
+ const resolvedSelection = selectedRowIds != null ? selectedRowIds : internalSelection;
2150
+ const resolvedPageIndex = pageIndex != null ? pageIndex : internalPageIndex;
2151
+ const setSort = (next) => {
2152
+ if (sortBy === void 0) setInternalSort(next);
2153
+ onSortChange == null ? void 0 : onSortChange(next);
2154
+ };
2155
+ const setFilters = (next) => {
2156
+ if (filters === void 0) setInternalFilters(next);
2157
+ onFiltersChange == null ? void 0 : onFiltersChange(next);
2158
+ };
2159
+ const setVisibility = (next) => {
2160
+ if (columnVisibility === void 0) setInternalVisibility(next);
2161
+ onColumnVisibilityChange == null ? void 0 : onColumnVisibilityChange(next);
2162
+ };
2163
+ const setSelection = (next) => {
2164
+ if (selectedRowIds === void 0) setInternalSelection(next);
2165
+ onRowSelectionChange == null ? void 0 : onRowSelectionChange(next);
2166
+ };
2167
+ const setPageIndex = React18.useCallback(
2168
+ (next) => {
2169
+ if (pageIndex === void 0) setInternalPageIndex(next);
2170
+ onPageChange == null ? void 0 : onPageChange(next);
2171
+ },
2172
+ [pageIndex, onPageChange]
2173
+ );
2174
+ const visibleColumns = columns.filter(
2175
+ (column) => resolvedVisibility[column.id] !== false
2176
+ );
2177
+ const filteredData = React18.useMemo(() => {
2178
+ if (manualFiltering) return data;
2179
+ const globalValue = resolvedGlobalFilter.trim();
2180
+ return data.filter((row) => {
2181
+ if (globalValue) {
2182
+ const matchesGlobal = columns.some((column) => {
2183
+ const value = normalizeValue(getColumnValue(column, row));
2184
+ return String(value).includes(globalValue.toLowerCase());
2185
+ });
2186
+ if (!matchesGlobal) return false;
2187
+ }
2188
+ return Object.entries(resolvedFilters).every(([columnId, value]) => {
2189
+ if (!value) return true;
2190
+ const column = columns.find((col) => col.id === columnId);
2191
+ if (!column) return true;
2192
+ const cellValue = normalizeValue(getColumnValue(column, row));
2193
+ return String(cellValue).includes(value.toLowerCase());
2194
+ });
2195
+ });
2196
+ }, [columns, data, manualFiltering, resolvedFilters, resolvedGlobalFilter]);
2197
+ const sortedData = React18.useMemo(() => {
2198
+ if (manualSorting || !resolvedSort) return filteredData;
2199
+ const column = columns.find((col) => col.id === resolvedSort.id);
2200
+ if (!column) return filteredData;
2201
+ const sorted = [...filteredData].sort((a, b) => {
2202
+ const valueA = normalizeValue(getColumnValue(column, a));
2203
+ const valueB = normalizeValue(getColumnValue(column, b));
2204
+ if (typeof valueA === "number" && typeof valueB === "number") {
2205
+ return valueA - valueB;
2206
+ }
2207
+ return String(valueA).localeCompare(String(valueB));
2208
+ });
2209
+ return resolvedSort.desc ? sorted.reverse() : sorted;
2210
+ }, [columns, filteredData, manualSorting, resolvedSort]);
2211
+ const resolvedPageCount = manualPagination ? Math.max(pageCount != null ? pageCount : 1, 1) : Math.max(Math.ceil(sortedData.length / pageSize), 1);
2212
+ React18.useEffect(() => {
2213
+ if (resolvedPageIndex > resolvedPageCount - 1) {
2214
+ setPageIndex(Math.max(resolvedPageCount - 1, 0));
2215
+ }
2216
+ }, [resolvedPageCount, resolvedPageIndex, setPageIndex]);
2217
+ const pagedData = React18.useMemo(() => {
2218
+ if (!enablePagination || manualPagination) return sortedData;
2219
+ const start = resolvedPageIndex * pageSize;
2220
+ return sortedData.slice(start, start + pageSize);
2221
+ }, [enablePagination, manualPagination, pageSize, resolvedPageIndex, sortedData]);
2222
+ const rowIdFor = React18.useCallback(
2223
+ (row, index) => {
2224
+ var _a;
2225
+ return (_a = getRowId == null ? void 0 : getRowId(row, index)) != null ? _a : String(index);
2226
+ },
2227
+ [getRowId]
2228
+ );
2229
+ const pageStartIndex = enablePagination && !manualPagination ? resolvedPageIndex * pageSize : 0;
2230
+ const pageRowIds = pagedData.map(
2231
+ (row, index) => rowIdFor(row, pageStartIndex + index)
2232
+ );
2233
+ const allSelected = pageRowIds.length > 0 && pageRowIds.every((id) => resolvedSelection[id]);
2234
+ const someSelected = pageRowIds.some((id) => resolvedSelection[id]);
2235
+ const headerCheckboxRef = React18.useRef(null);
2236
+ React18.useEffect(() => {
2237
+ if (headerCheckboxRef.current) {
2238
+ headerCheckboxRef.current.indeterminate = someSelected && !allSelected;
2239
+ }
2240
+ }, [someSelected, allSelected]);
2241
+ const toggleSort = (column) => {
2242
+ if (!enableSorting || column.sortable === false) return;
2243
+ const current = resolvedSort;
2244
+ if (!current || current.id !== column.id) {
2245
+ setSort({ id: column.id, desc: false });
2246
+ return;
2247
+ }
2248
+ if (!current.desc) {
2249
+ setSort({ id: column.id, desc: true });
2250
+ return;
2251
+ }
2252
+ setSort(null);
2253
+ };
2254
+ const emptyContent = emptyState != null ? emptyState : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "text-center text-sm text-[rgb(var(--nc-fg-muted))]", children: "No results found." });
2255
+ const resolvedShowGlobalFilter = showGlobalFilter != null ? showGlobalFilter : enableFiltering && !toolbar;
2256
+ const setGlobalFilter = (next) => {
2257
+ if (globalFilter === void 0) setInternalGlobalFilter(next);
2258
+ onGlobalFilterChange == null ? void 0 : onGlobalFilterChange(next);
2259
+ };
2260
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: cn("space-y-4", className), "data-nc-theme": tone, children: [
2261
+ toolbar,
2262
+ resolvedShowGlobalFilter && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "flex items-center justify-between gap-3 rounded-2xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] px-3 py-2 text-sm text-[rgb(var(--nc-fg))]", children: [
2263
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-xs text-[rgb(var(--nc-fg-muted))]", children: "Global filter" }),
2264
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2265
+ "input",
2266
+ {
2267
+ type: "search",
2268
+ value: resolvedGlobalFilter,
2269
+ onChange: (event) => setGlobalFilter(event.target.value),
2270
+ placeholder: "Search all columns...",
2271
+ className: "w-full max-w-xs rounded-xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.18)] px-3 py-2 text-xs text-[rgb(var(--nc-fg))]"
2272
+ }
2273
+ )
2274
+ ] }),
2275
+ enableColumnVisibility && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "relative flex justify-end", children: [
2276
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2277
+ "button",
2278
+ {
2279
+ type: "button",
2280
+ className: "rounded-xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] px-3 py-2 text-xs text-[rgb(var(--nc-fg))] transition hover:bg-[rgb(var(--nc-surface)/0.2)]",
2281
+ onClick: () => setShowColumns((prev) => !prev),
2282
+ children: "Columns"
2283
+ }
2284
+ ),
2285
+ showColumns && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "absolute right-0 top-10 z-20 w-48 rounded-2xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.2)] p-3 shadow-[0_12px_30px_rgba(0,0,0,0.35)] backdrop-blur-2xl", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "grid gap-2", children: columns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
2286
+ "label",
2287
+ {
2288
+ className: "flex items-center gap-2 text-xs text-[rgb(var(--nc-fg))]",
2289
+ children: [
2290
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2291
+ "input",
2292
+ {
2293
+ type: "checkbox",
2294
+ className: "h-4 w-4 accent-[rgb(var(--nc-accent-1))]",
2295
+ checked: resolvedVisibility[column.id] !== false,
2296
+ onChange: (event) => setVisibility({
2297
+ ...resolvedVisibility,
2298
+ [column.id]: event.target.checked
2299
+ })
2300
+ }
2301
+ ),
2302
+ column.header
2303
+ ]
2304
+ },
2305
+ column.id
2306
+ )) }) })
2307
+ ] }),
2308
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "overflow-hidden rounded-3xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.08)] shadow-[0_18px_50px_rgba(0,0,0,0.35)] backdrop-blur-2xl", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("table", { className: "w-full border-collapse text-left text-sm", children: [
2309
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("thead", { className: "bg-[rgb(var(--nc-surface)/0.12)] text-[rgb(var(--nc-fg-muted))]", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("tr", { children: [
2310
+ enableRowSelection && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("th", { className: "w-12 px-4 py-3", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2311
+ "input",
2312
+ {
2313
+ ref: headerCheckboxRef,
2314
+ type: "checkbox",
2315
+ className: "h-4 w-4 accent-[rgb(var(--nc-accent-1))]",
2316
+ checked: allSelected,
2317
+ onChange: (event) => {
2318
+ const next = { ...resolvedSelection };
2319
+ pageRowIds.forEach((id) => {
2320
+ next[id] = event.target.checked;
2321
+ });
2322
+ setSelection(next);
2323
+ }
2324
+ }
2325
+ ) }),
2326
+ visibleColumns.map((column) => {
2327
+ var _a;
2328
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
2329
+ "th",
2330
+ {
2331
+ className: cn(
2332
+ "px-4 py-3 text-xs font-semibold uppercase tracking-[0.2em]",
2333
+ column.headerClassName
2334
+ ),
2335
+ style: { width: column.width },
2336
+ children: [
2337
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
2338
+ "button",
2339
+ {
2340
+ type: "button",
2341
+ className: cn(
2342
+ "flex items-center gap-2",
2343
+ enableSorting && column.sortable !== false ? "cursor-pointer" : "cursor-default"
2344
+ ),
2345
+ onClick: () => toggleSort(column),
2346
+ children: [
2347
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { children: column.header }),
2348
+ (resolvedSort == null ? void 0 : resolvedSort.id) === column.id && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "text-[rgb(var(--nc-accent-1))]", children: resolvedSort.desc ? "\u2193" : "\u2191" })
2349
+ ]
2350
+ }
2351
+ ),
2352
+ enableFiltering && column.filterable !== false && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2353
+ "input",
2354
+ {
2355
+ type: "text",
2356
+ value: (_a = resolvedFilters[column.id]) != null ? _a : "",
2357
+ onChange: (event) => setFilters({
2358
+ ...resolvedFilters,
2359
+ [column.id]: event.target.value
2360
+ }),
2361
+ placeholder: "Filter",
2362
+ className: "mt-2 w-full rounded-xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.18)] px-2 py-1 text-xs text-[rgb(var(--nc-fg))]"
2363
+ }
2364
+ )
2365
+ ]
2366
+ },
2367
+ column.id
2368
+ );
2369
+ })
2370
+ ] }) }),
2371
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("tbody", { className: "text-[rgb(var(--nc-fg))]", children: [
2372
+ loading && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2373
+ "td",
2374
+ {
2375
+ colSpan: visibleColumns.length + (enableRowSelection ? 1 : 0),
2376
+ className: "px-4 py-10 text-center text-sm text-[rgb(var(--nc-fg-muted))]",
2377
+ children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "inline-flex items-center gap-2", children: [
2378
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-[rgb(var(--nc-fg-muted))] border-t-transparent" }),
2379
+ "Loading data..."
2380
+ ] })
2381
+ }
2382
+ ) }),
2383
+ !loading && pagedData.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2384
+ "td",
2385
+ {
2386
+ colSpan: visibleColumns.length + (enableRowSelection ? 1 : 0),
2387
+ className: "px-4 py-10",
2388
+ children: emptyContent
2389
+ }
2390
+ ) }),
2391
+ !loading && pagedData.map((row, rowIndex) => {
2392
+ const rowId = rowIdFor(row, pageStartIndex + rowIndex);
2393
+ const isSelected = resolvedSelection[rowId];
2394
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
2395
+ "tr",
2396
+ {
2397
+ className: cn(
2398
+ "border-t border-[rgb(var(--nc-border)/0.15)]",
2399
+ isSelected && "bg-[rgb(var(--nc-accent-1)/0.08)]"
2400
+ ),
2401
+ children: [
2402
+ enableRowSelection && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("td", { className: "px-4 py-4", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2403
+ "input",
2404
+ {
2405
+ type: "checkbox",
2406
+ className: "h-4 w-4 accent-[rgb(var(--nc-accent-1))]",
2407
+ checked: isSelected,
2408
+ onChange: (event) => setSelection({
2409
+ ...resolvedSelection,
2410
+ [rowId]: event.target.checked
2411
+ })
2412
+ }
2413
+ ) }),
2414
+ visibleColumns.map((column) => {
2415
+ var _a;
2416
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2417
+ "td",
2418
+ {
2419
+ className: cn(
2420
+ "px-4 py-4",
2421
+ column.align === "center" && "text-center",
2422
+ column.align === "right" && "text-right",
2423
+ column.cellClassName
2424
+ ),
2425
+ children: column.cell ? column.cell(row) : String((_a = getColumnValue(column, row)) != null ? _a : "")
2426
+ },
2427
+ column.id
2428
+ );
2429
+ })
2430
+ ]
2431
+ },
2432
+ rowId
2433
+ );
2434
+ })
2435
+ ] })
2436
+ ] }) }),
2437
+ enablePagination && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
2438
+ CraftPagination,
2439
+ {
2440
+ pageIndex: resolvedPageIndex,
2441
+ pageCount: resolvedPageCount,
2442
+ onPageChange: setPageIndex,
2443
+ pageSize,
2444
+ onPageSizeChange,
2445
+ tone
2446
+ }
2447
+ )
2448
+ ] });
2449
+ }
2450
+
2451
+ // src/components/layout/app-shell.tsx
2452
+ var import_jsx_runtime29 = require("react/jsx-runtime");
2453
+ function AppShell({ className, sidebar, topNav, children, ...props }) {
2454
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
2455
+ "div",
2456
+ {
2457
+ className: cn(
2458
+ "grid min-h-screen grid-cols-1 gap-6 bg-background p-6 lg:grid-cols-[260px_1fr]",
2459
+ className
2460
+ ),
2461
+ ...props,
2462
+ children: [
2463
+ sidebar && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "h-full lg:sticky lg:top-6 lg:self-start lg:max-h-[calc(100vh-3rem)] lg:overflow-y-auto", children: sidebar }),
2464
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-col gap-6", children: [
2465
+ topNav && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "lg:sticky lg:top-6 lg:z-20", children: topNav }),
2466
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("main", { className: "flex-1", children })
2467
+ ] })
2468
+ ]
2469
+ }
2470
+ );
2471
+ }
2472
+
2473
+ // src/components/layout/sidebar.tsx
2474
+ var import_jsx_runtime30 = require("react/jsx-runtime");
2475
+ function Sidebar({ className, title, items, footer, ...props }) {
2476
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
2477
+ "aside",
2478
+ {
2479
+ className: cn(
2480
+ "flex h-full w-full flex-col gap-6 rounded-3xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.08)] p-6 text-[rgb(var(--nc-fg))] backdrop-blur-xl",
2481
+ className
2482
+ ),
2483
+ ...props,
2484
+ children: [
2485
+ title && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "text-lg font-semibold", children: title }),
2486
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("nav", { className: "flex flex-col gap-2", children: items.map((item, index) => {
2487
+ var _a;
2488
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
2489
+ "a",
2490
+ {
2491
+ href: (_a = item.href) != null ? _a : "#",
2492
+ className: cn(
2493
+ "flex items-center gap-3 rounded-2xl px-3 py-2 text-sm transition",
2494
+ item.active ? "bg-[rgb(var(--nc-accent-1)/0.25)] text-[rgb(var(--nc-fg))]" : "text-[rgb(var(--nc-fg-muted))] hover:bg-[rgb(var(--nc-surface)/0.12)] hover:text-[rgb(var(--nc-fg))]"
2495
+ ),
2496
+ children: [
2497
+ item.icon,
2498
+ /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { children: item.label })
2499
+ ]
2500
+ },
2501
+ `${item.label}-${index}`
2502
+ );
2503
+ }) }),
2504
+ footer && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "mt-auto pt-4", children: footer })
2505
+ ]
2506
+ }
2507
+ );
2508
+ }
2509
+
2510
+ // src/components/layout/top-nav.tsx
2511
+ var import_jsx_runtime31 = require("react/jsx-runtime");
2512
+ function TopNav({ className, title, actions, breadcrumb, ...props }) {
2513
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
2514
+ "header",
2515
+ {
2516
+ className: cn(
2517
+ "flex flex-wrap items-center justify-between gap-4 rounded-3xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.08)] px-6 py-4 text-[rgb(var(--nc-fg))] backdrop-blur-xl",
2518
+ className
2519
+ ),
2520
+ ...props,
2521
+ children: [
2522
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "space-y-1", children: [
2523
+ breadcrumb,
2524
+ title && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "text-xl font-semibold", children: title })
2525
+ ] }),
2526
+ actions && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "flex flex-wrap gap-3", children: actions })
2527
+ ]
2528
+ }
2529
+ );
2530
+ }
2531
+
2532
+ // src/components/layout/page-header.tsx
2533
+ var import_jsx_runtime32 = require("react/jsx-runtime");
2534
+ function PageHeader({
2535
+ className,
2536
+ title,
2537
+ description,
2538
+ actions,
2539
+ ...props
2540
+ }) {
2541
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
2542
+ "div",
2543
+ {
2544
+ className: cn("flex flex-wrap items-start justify-between gap-6", className),
2545
+ ...props,
2546
+ children: [
2547
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "space-y-2", children: [
2548
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("h1", { className: "text-3xl font-bold text-[rgb(var(--nc-fg))]", children: title }),
2549
+ description && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "text-[rgb(var(--nc-fg-muted))]", children: description })
2550
+ ] }),
2551
+ actions && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "flex flex-wrap gap-3", children: actions })
2552
+ ]
2553
+ }
2554
+ );
2555
+ }
2556
+
2557
+ // src/components/layout/breadcrumbs.tsx
2558
+ var import_jsx_runtime33 = require("react/jsx-runtime");
2559
+ function Breadcrumbs({ className, items, ...props }) {
2560
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("nav", { className: cn("flex items-center text-sm text-[rgb(var(--nc-fg-muted))]", className), ...props, children: items.map((item, index) => {
2561
+ const content = item.href ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("a", { href: item.href, className: "transition hover:text-[rgb(var(--nc-fg))]", children: item.label }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "text-[rgb(var(--nc-fg))]", children: item.label });
2562
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("span", { className: "flex items-center", children: [
2563
+ content,
2564
+ index < items.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "mx-2 text-[rgb(var(--nc-fg-soft))]", children: "/" })
2565
+ ] }, `${item.label}-${index}`);
2566
+ }) });
2567
+ }
2568
+
2569
+ // src/components/layout/auth-layout.tsx
2570
+ var import_jsx_runtime34 = require("react/jsx-runtime");
2571
+ function AuthLayout({
2572
+ className,
2573
+ title,
2574
+ description,
2575
+ footer,
2576
+ graphic,
2577
+ children,
2578
+ ...props
2579
+ }) {
2580
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
2581
+ "div",
2582
+ {
2583
+ className: cn(
2584
+ "grid min-h-screen grid-cols-1 bg-background",
2585
+ "lg:grid-cols-[1.1fr_0.9fr]",
2586
+ className
2587
+ ),
2588
+ ...props,
2589
+ children: [
2590
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex flex-col justify-center px-6 py-16 sm:px-12", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "mx-auto w-full max-w-md space-y-6", children: [
2591
+ (title || description) && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "space-y-2", children: [
2592
+ title && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("h1", { className: "text-3xl font-bold text-[rgb(var(--nc-fg))]", children: title }),
2593
+ description && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-[rgb(var(--nc-fg-muted))]", children: description })
2594
+ ] }),
2595
+ children,
2596
+ footer && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "text-sm text-[rgb(var(--nc-fg-muted))]", children: footer })
2597
+ ] }) }),
2598
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "hidden items-center justify-center border-l border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.08)] p-12 text-[rgb(var(--nc-fg))] lg:flex", children: graphic != null ? graphic : /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "max-w-sm space-y-4 text-center", children: [
2599
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("h2", { className: "text-2xl font-semibold", children: "Crafted experiences" }),
2600
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-[rgb(var(--nc-fg-muted))]", children: "Build authentication flows that feel premium and cohesive." })
2601
+ ] }) })
2602
+ ]
2603
+ }
2604
+ );
2605
+ }
2606
+
2607
+ // src/components/layout/container.tsx
2608
+ var import_jsx_runtime35 = require("react/jsx-runtime");
2609
+ var sizeClasses2 = {
2610
+ sm: "max-w-3xl",
2611
+ md: "max-w-5xl",
2612
+ lg: "max-w-6xl",
2613
+ xl: "max-w-7xl"
2614
+ };
2615
+ function Container({ className, size = "lg", ...props }) {
2616
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
2617
+ "div",
2618
+ {
2619
+ className: cn("mx-auto w-full px-4 sm:px-6 lg:px-8", sizeClasses2[size], className),
2620
+ ...props
2621
+ }
2622
+ );
2623
+ }
2624
+
2625
+ // src/components/layout/grid.tsx
2626
+ var import_jsx_runtime36 = require("react/jsx-runtime");
2627
+ var colClasses = {
2628
+ 1: "grid-cols-1",
2629
+ 2: "grid-cols-1 md:grid-cols-2",
2630
+ 3: "grid-cols-1 md:grid-cols-2 lg:grid-cols-3",
2631
+ 4: "grid-cols-1 md:grid-cols-2 lg:grid-cols-4",
2632
+ 5: "grid-cols-1 md:grid-cols-2 lg:grid-cols-5",
2633
+ 6: "grid-cols-1 md:grid-cols-3 lg:grid-cols-6"
2634
+ };
2635
+ var gapClasses = {
2636
+ sm: "gap-4",
2637
+ md: "gap-6",
2638
+ lg: "gap-8",
2639
+ xl: "gap-10"
2640
+ };
2641
+ function Grid({ className, columns = 3, gap = "md", ...props }) {
2642
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: cn("grid", colClasses[columns], gapClasses[gap], className), ...props });
2643
+ }
2644
+
2645
+ // src/theme/theme-context.tsx
2646
+ var React19 = __toESM(require("react"), 1);
2647
+ var import_jsx_runtime37 = require("react/jsx-runtime");
167
2648
  var THEME_NAMES = [
168
2649
  "aurora",
169
2650
  "ember",
@@ -171,7 +2652,7 @@ var THEME_NAMES = [
171
2652
  "midnight",
172
2653
  "cosmic"
173
2654
  ];
174
- var ThemeContext = React2.createContext(null);
2655
+ var ThemeContext = React19.createContext(null);
175
2656
  var DEFAULT_THEME_KEY = "nextcraft-theme";
176
2657
  var DEFAULT_MODE_KEY = "nextcraft-mode";
177
2658
  function ThemeProvider({
@@ -181,9 +2662,9 @@ function ThemeProvider({
181
2662
  storageKeyTheme = DEFAULT_THEME_KEY,
182
2663
  storageKeyMode = DEFAULT_MODE_KEY
183
2664
  }) {
184
- const [theme, setTheme] = React2.useState(defaultTheme);
185
- const [mode, setMode] = React2.useState(defaultMode);
186
- React2.useEffect(() => {
2665
+ const [theme, setTheme] = React19.useState(defaultTheme);
2666
+ const [mode, setMode] = React19.useState(defaultMode);
2667
+ React19.useEffect(() => {
187
2668
  if (typeof window === "undefined") return;
188
2669
  try {
189
2670
  const storedTheme = window.localStorage.getItem(storageKeyTheme);
@@ -193,7 +2674,7 @@ function ThemeProvider({
193
2674
  } catch {
194
2675
  }
195
2676
  }, [storageKeyTheme, storageKeyMode]);
196
- React2.useEffect(() => {
2677
+ React19.useEffect(() => {
197
2678
  if (typeof window === "undefined") return;
198
2679
  try {
199
2680
  window.localStorage.setItem(storageKeyTheme, theme);
@@ -201,7 +2682,7 @@ function ThemeProvider({
201
2682
  } catch {
202
2683
  }
203
2684
  }, [theme, mode, storageKeyTheme, storageKeyMode]);
204
- React2.useEffect(() => {
2685
+ React19.useEffect(() => {
205
2686
  if (typeof document === "undefined") return;
206
2687
  const root = document.documentElement;
207
2688
  root.dataset.ncTheme = theme;
@@ -221,14 +2702,14 @@ function ThemeProvider({
221
2702
  mediaQuery.addListener(applySystem);
222
2703
  return () => mediaQuery.removeListener(applySystem);
223
2704
  }, [theme, mode]);
224
- const value = React2.useMemo(
2705
+ const value = React19.useMemo(
225
2706
  () => ({ theme, mode, setTheme, setMode }),
226
2707
  [theme, mode]
227
2708
  );
228
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ThemeContext.Provider, { value, children });
2709
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ThemeContext.Provider, { value, children });
229
2710
  }
230
2711
  function useTheme() {
231
- const context = React2.useContext(ThemeContext);
2712
+ const context = React19.useContext(ThemeContext);
232
2713
  if (!context) {
233
2714
  throw new Error("useTheme must be used within ThemeProvider");
234
2715
  }
@@ -236,40 +2717,40 @@ function useTheme() {
236
2717
  }
237
2718
 
238
2719
  // src/components/theme-switcher.tsx
239
- var import_jsx_runtime5 = require("react/jsx-runtime");
2720
+ var import_jsx_runtime38 = require("react/jsx-runtime");
240
2721
  var MODE_OPTIONS = ["system", "light", "dark"];
241
2722
  function ThemeSwitcher({ className, showLabels = true, ...props }) {
242
2723
  const { theme, mode, setTheme, setMode } = useTheme();
243
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2724
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
244
2725
  "div",
245
2726
  {
246
2727
  className: cn(
247
- "flex flex-wrap items-center gap-3 rounded-2xl border border-white/10 bg-white/5 px-4 py-3 text-sm text-white shadow-[inset_0_1px_0_rgba(255,255,255,0.06)]",
2728
+ "flex flex-wrap items-center gap-3 rounded-2xl border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.08)] px-4 py-3 text-sm text-[rgb(var(--nc-fg))] shadow-[inset_0_1px_0_rgba(255,255,255,0.06)]",
248
2729
  className
249
2730
  ),
250
2731
  ...props,
251
2732
  children: [
252
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2", children: [
253
- showLabels && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-white/70", children: "Theme" }),
254
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2733
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("label", { className: "flex items-center gap-2", children: [
2734
+ showLabels && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "text-[rgb(var(--nc-fg-muted))]", children: "Theme" }),
2735
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
255
2736
  "select",
256
2737
  {
257
- className: "rounded-lg border border-white/10 bg-white/10 px-3 py-1 text-white outline-none focus:ring-2 focus:ring-[rgb(var(--nc-accent-1)/0.5)]",
2738
+ className: "rounded-lg border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] px-3 py-1 text-[rgb(var(--nc-fg))] outline-none focus:ring-2 focus:ring-[rgb(var(--nc-accent-1)/0.5)]",
258
2739
  value: theme,
259
2740
  onChange: (event) => setTheme(event.target.value),
260
- children: THEME_NAMES.map((name) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value: name, className: "text-slate-900", children: name }, name))
2741
+ children: THEME_NAMES.map((name) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("option", { value: name, className: "text-slate-900", children: name }, name))
261
2742
  }
262
2743
  )
263
2744
  ] }),
264
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "flex items-center gap-2", children: [
265
- showLabels && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "text-white/70", children: "Mode" }),
266
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2745
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("label", { className: "flex items-center gap-2", children: [
2746
+ showLabels && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "text-[rgb(var(--nc-fg-muted))]", children: "Mode" }),
2747
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
267
2748
  "select",
268
2749
  {
269
- className: "rounded-lg border border-white/10 bg-white/10 px-3 py-1 text-white outline-none focus:ring-2 focus:ring-[rgb(var(--nc-accent-1)/0.5)]",
2750
+ className: "rounded-lg border border-[rgb(var(--nc-border)/0.3)] bg-[rgb(var(--nc-surface)/0.12)] px-3 py-1 text-[rgb(var(--nc-fg))] outline-none focus:ring-2 focus:ring-[rgb(var(--nc-accent-1)/0.5)]",
270
2751
  value: mode,
271
2752
  onChange: (event) => setMode(event.target.value),
272
- children: MODE_OPTIONS.map((value) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("option", { value, className: "text-slate-900", children: value }, value))
2753
+ children: MODE_OPTIONS.map((value) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("option", { value, className: "text-slate-900", children: value }, value))
273
2754
  }
274
2755
  )
275
2756
  ] })
@@ -279,11 +2760,45 @@ function ThemeSwitcher({ className, showLabels = true, ...props }) {
279
2760
  }
280
2761
  // Annotate the CommonJS export names for ESM import in node:
281
2762
  0 && (module.exports = {
2763
+ AppShell,
2764
+ AuthLayout,
2765
+ Breadcrumbs,
2766
+ Container,
2767
+ CraftBadge,
282
2768
  CraftButton,
2769
+ CraftCard,
2770
+ CraftCheckbox,
2771
+ CraftConfirmDialog,
2772
+ CraftCreateEditDrawer,
2773
+ CraftCurrencyInput,
2774
+ CraftDataTable,
2775
+ CraftDatePicker,
2776
+ CraftDrawer,
2777
+ CraftEmptyState,
2778
+ CraftFilterBar,
2779
+ CraftForm,
2780
+ CraftFormBuilder,
2781
+ CraftFormField,
283
2782
  CraftInput,
2783
+ CraftModal,
2784
+ CraftNumberInput,
2785
+ CraftPagination,
2786
+ CraftSelect,
2787
+ CraftSkeleton,
2788
+ CraftSubmitButton,
2789
+ CraftSwitch,
2790
+ CraftTabs,
2791
+ CraftTextarea,
2792
+ CraftToastHost,
2793
+ CraftTooltip,
284
2794
  GlassCard,
2795
+ Grid,
2796
+ PageHeader,
2797
+ Sidebar,
285
2798
  ThemeProvider,
286
2799
  ThemeSwitcher,
2800
+ TopNav,
2801
+ useCraftToast,
287
2802
  useTheme
288
2803
  });
289
2804
  //# sourceMappingURL=index.cjs.map