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