@assassin1717/aifelib 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +739 -0
- package/dist/index.cjs +1534 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +342 -0
- package/dist/index.d.ts +342 -0
- package/dist/index.js +1458 -0
- package/dist/index.js.map +1 -0
- package/package.json +95 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1534 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
Alert: () => Alert,
|
|
34
|
+
AppShell: () => AppShell,
|
|
35
|
+
Badge: () => Badge,
|
|
36
|
+
Button: () => Button,
|
|
37
|
+
Card: () => Card,
|
|
38
|
+
CardContent: () => CardContent,
|
|
39
|
+
CardDescription: () => CardDescription,
|
|
40
|
+
CardFooter: () => CardFooter,
|
|
41
|
+
CardHeader: () => CardHeader,
|
|
42
|
+
CardTitle: () => CardTitle,
|
|
43
|
+
Checkbox: () => Checkbox,
|
|
44
|
+
ConfirmDialog: () => ConfirmDialog,
|
|
45
|
+
Drawer: () => Drawer,
|
|
46
|
+
DropdownMenu: () => DropdownMenu,
|
|
47
|
+
EmptyState: () => EmptyState,
|
|
48
|
+
FormField: () => FormField,
|
|
49
|
+
Input: () => Input,
|
|
50
|
+
Label: () => Label,
|
|
51
|
+
Modal: () => Modal,
|
|
52
|
+
PageHeader: () => PageHeader,
|
|
53
|
+
Pagination: () => Pagination,
|
|
54
|
+
Select: () => Select,
|
|
55
|
+
Sidebar: () => Sidebar,
|
|
56
|
+
Spinner: () => Spinner,
|
|
57
|
+
TabPanel: () => TabPanel,
|
|
58
|
+
Table: () => Table,
|
|
59
|
+
TableBody: () => TableBody,
|
|
60
|
+
TableCell: () => TableCell,
|
|
61
|
+
TableEmptyState: () => TableEmptyState,
|
|
62
|
+
TableHead: () => TableHead,
|
|
63
|
+
TableHeader: () => TableHeader,
|
|
64
|
+
TableRow: () => TableRow,
|
|
65
|
+
TableToolbar: () => TableToolbar,
|
|
66
|
+
Tabs: () => Tabs,
|
|
67
|
+
Textarea: () => Textarea,
|
|
68
|
+
ToastProvider: () => ToastProvider,
|
|
69
|
+
Tooltip: () => Tooltip,
|
|
70
|
+
Topbar: () => Topbar,
|
|
71
|
+
cn: () => cn,
|
|
72
|
+
useToast: () => useToast
|
|
73
|
+
});
|
|
74
|
+
module.exports = __toCommonJS(index_exports);
|
|
75
|
+
|
|
76
|
+
// src/lib/cn.ts
|
|
77
|
+
var import_clsx = require("clsx");
|
|
78
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
79
|
+
function cn(...inputs) {
|
|
80
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/components/button/button.tsx
|
|
84
|
+
var React = __toESM(require("react"), 1);
|
|
85
|
+
var import_lucide_react = require("lucide-react");
|
|
86
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
87
|
+
var variantClasses = {
|
|
88
|
+
primary: "bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800 focus-visible:ring-blue-500",
|
|
89
|
+
secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200 active:bg-gray-300 focus-visible:ring-gray-400",
|
|
90
|
+
ghost: "bg-transparent text-gray-700 hover:bg-gray-100 active:bg-gray-200 focus-visible:ring-gray-400",
|
|
91
|
+
destructive: "bg-red-600 text-white hover:bg-red-700 active:bg-red-800 focus-visible:ring-red-500",
|
|
92
|
+
outline: "border border-gray-300 bg-white text-gray-700 hover:bg-gray-50 active:bg-gray-100 focus-visible:ring-gray-400"
|
|
93
|
+
};
|
|
94
|
+
var sizeClasses = {
|
|
95
|
+
sm: "h-8 px-3 text-xs gap-1.5",
|
|
96
|
+
md: "h-10 px-4 text-sm gap-2",
|
|
97
|
+
lg: "h-12 px-5 text-base gap-2"
|
|
98
|
+
};
|
|
99
|
+
var Button = React.forwardRef(
|
|
100
|
+
({
|
|
101
|
+
className,
|
|
102
|
+
variant = "primary",
|
|
103
|
+
size = "md",
|
|
104
|
+
loading = false,
|
|
105
|
+
fullWidth = false,
|
|
106
|
+
disabled,
|
|
107
|
+
children,
|
|
108
|
+
...props
|
|
109
|
+
}, ref) => {
|
|
110
|
+
const isDisabled = disabled || loading;
|
|
111
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
112
|
+
"button",
|
|
113
|
+
{
|
|
114
|
+
ref,
|
|
115
|
+
disabled: isDisabled,
|
|
116
|
+
"aria-busy": loading,
|
|
117
|
+
className: cn(
|
|
118
|
+
// base
|
|
119
|
+
"inline-flex items-center justify-center rounded-md font-medium",
|
|
120
|
+
"transition-colors duration-150",
|
|
121
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
|
|
122
|
+
// disabled
|
|
123
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
124
|
+
// touch target — mínimo 44px em mobile
|
|
125
|
+
"min-h-[2.75rem] sm:min-h-0",
|
|
126
|
+
variantClasses[variant],
|
|
127
|
+
sizeClasses[size],
|
|
128
|
+
fullWidth && "w-full",
|
|
129
|
+
className
|
|
130
|
+
),
|
|
131
|
+
...props,
|
|
132
|
+
children: [
|
|
133
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
134
|
+
import_lucide_react.Loader2,
|
|
135
|
+
{
|
|
136
|
+
className: "shrink-0 animate-spin",
|
|
137
|
+
size: size === "sm" ? 14 : size === "lg" ? 18 : 16,
|
|
138
|
+
"aria-hidden": "true"
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
children
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
Button.displayName = "Button";
|
|
148
|
+
|
|
149
|
+
// src/components/input/input.tsx
|
|
150
|
+
var React2 = __toESM(require("react"), 1);
|
|
151
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
152
|
+
var Input = React2.forwardRef(
|
|
153
|
+
({ className, error, startIcon, endIcon, ...props }, ref) => {
|
|
154
|
+
if (startIcon || endIcon) {
|
|
155
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative flex items-center", children: [
|
|
156
|
+
startIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "pointer-events-none absolute left-3 flex shrink-0 items-center text-gray-400", children: startIcon }),
|
|
157
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
158
|
+
"input",
|
|
159
|
+
{
|
|
160
|
+
ref,
|
|
161
|
+
className: cn(
|
|
162
|
+
inputBase,
|
|
163
|
+
error && inputError,
|
|
164
|
+
startIcon && "pl-9",
|
|
165
|
+
endIcon && "pr-9",
|
|
166
|
+
className
|
|
167
|
+
),
|
|
168
|
+
"aria-invalid": error ? true : void 0,
|
|
169
|
+
...props
|
|
170
|
+
}
|
|
171
|
+
),
|
|
172
|
+
endIcon && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "pointer-events-none absolute right-3 flex shrink-0 items-center text-gray-400", children: endIcon })
|
|
173
|
+
] });
|
|
174
|
+
}
|
|
175
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
176
|
+
"input",
|
|
177
|
+
{
|
|
178
|
+
ref,
|
|
179
|
+
className: cn(inputBase, error && inputError, className),
|
|
180
|
+
"aria-invalid": error ? true : void 0,
|
|
181
|
+
...props
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
);
|
|
186
|
+
Input.displayName = "Input";
|
|
187
|
+
var inputBase = [
|
|
188
|
+
"w-full rounded-md border border-gray-300 bg-white px-3",
|
|
189
|
+
"text-sm text-gray-900 placeholder:text-gray-400",
|
|
190
|
+
// mobile-friendly height
|
|
191
|
+
"h-11 sm:h-10",
|
|
192
|
+
"transition-colors duration-150",
|
|
193
|
+
"focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
|
|
194
|
+
"disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-gray-50"
|
|
195
|
+
].join(" ");
|
|
196
|
+
var inputError = "border-red-500 focus:ring-red-500";
|
|
197
|
+
|
|
198
|
+
// src/components/textarea/textarea.tsx
|
|
199
|
+
var React3 = __toESM(require("react"), 1);
|
|
200
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
201
|
+
var Textarea = React3.forwardRef(
|
|
202
|
+
({ className, error, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
203
|
+
"textarea",
|
|
204
|
+
{
|
|
205
|
+
ref,
|
|
206
|
+
className: cn(
|
|
207
|
+
"w-full rounded-md border border-gray-300 bg-white px-3 py-2",
|
|
208
|
+
"text-sm text-gray-900 placeholder:text-gray-400",
|
|
209
|
+
"min-h-[80px] resize-y",
|
|
210
|
+
"transition-colors duration-150",
|
|
211
|
+
"focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
|
|
212
|
+
"disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-gray-50",
|
|
213
|
+
error && "border-red-500 focus:ring-red-500",
|
|
214
|
+
className
|
|
215
|
+
),
|
|
216
|
+
"aria-invalid": error ? true : void 0,
|
|
217
|
+
...props
|
|
218
|
+
}
|
|
219
|
+
)
|
|
220
|
+
);
|
|
221
|
+
Textarea.displayName = "Textarea";
|
|
222
|
+
|
|
223
|
+
// src/components/select/select.tsx
|
|
224
|
+
var React4 = __toESM(require("react"), 1);
|
|
225
|
+
var import_lucide_react2 = require("lucide-react");
|
|
226
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
227
|
+
var Select = React4.forwardRef(
|
|
228
|
+
({ className, options, placeholder, error, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "relative", children: [
|
|
229
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
230
|
+
"select",
|
|
231
|
+
{
|
|
232
|
+
ref,
|
|
233
|
+
className: cn(
|
|
234
|
+
"w-full appearance-none rounded-md border border-gray-300 bg-white px-3 pr-9",
|
|
235
|
+
"text-sm text-gray-900",
|
|
236
|
+
// mobile-friendly height
|
|
237
|
+
"h-11 sm:h-10",
|
|
238
|
+
"transition-colors duration-150",
|
|
239
|
+
"focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent",
|
|
240
|
+
"disabled:cursor-not-allowed disabled:opacity-50 disabled:bg-gray-50",
|
|
241
|
+
error && "border-red-500 focus:ring-red-500",
|
|
242
|
+
className
|
|
243
|
+
),
|
|
244
|
+
"aria-invalid": error ? true : void 0,
|
|
245
|
+
...props,
|
|
246
|
+
children: [
|
|
247
|
+
placeholder && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: "", disabled: true, children: placeholder }),
|
|
248
|
+
options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: opt.value, disabled: opt.disabled, children: opt.label }, opt.value))
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
),
|
|
252
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
253
|
+
import_lucide_react2.ChevronDown,
|
|
254
|
+
{
|
|
255
|
+
className: "pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-gray-400",
|
|
256
|
+
size: 16,
|
|
257
|
+
"aria-hidden": "true"
|
|
258
|
+
}
|
|
259
|
+
)
|
|
260
|
+
] })
|
|
261
|
+
);
|
|
262
|
+
Select.displayName = "Select";
|
|
263
|
+
|
|
264
|
+
// src/components/checkbox/checkbox.tsx
|
|
265
|
+
var React5 = __toESM(require("react"), 1);
|
|
266
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
267
|
+
var Checkbox = React5.forwardRef(
|
|
268
|
+
({ className, label, error, id, ...props }, ref) => {
|
|
269
|
+
const generatedId = React5.useId();
|
|
270
|
+
const checkboxId = id ?? generatedId;
|
|
271
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-start gap-3", children: [
|
|
272
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "flex min-h-[44px] min-w-[44px] items-center justify-center sm:min-h-0 sm:min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
273
|
+
"input",
|
|
274
|
+
{
|
|
275
|
+
ref,
|
|
276
|
+
id: checkboxId,
|
|
277
|
+
type: "checkbox",
|
|
278
|
+
className: cn(
|
|
279
|
+
"h-4 w-4 shrink-0 rounded border-gray-300 text-blue-600",
|
|
280
|
+
"focus:ring-2 focus:ring-blue-500 focus:ring-offset-2",
|
|
281
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
282
|
+
"cursor-pointer",
|
|
283
|
+
error && "border-red-500",
|
|
284
|
+
className
|
|
285
|
+
),
|
|
286
|
+
"aria-invalid": error ? true : void 0,
|
|
287
|
+
...props
|
|
288
|
+
}
|
|
289
|
+
) }),
|
|
290
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
291
|
+
"label",
|
|
292
|
+
{
|
|
293
|
+
htmlFor: checkboxId,
|
|
294
|
+
className: "cursor-pointer select-none pt-0.5 text-sm text-gray-700",
|
|
295
|
+
children: label
|
|
296
|
+
}
|
|
297
|
+
)
|
|
298
|
+
] });
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
Checkbox.displayName = "Checkbox";
|
|
302
|
+
|
|
303
|
+
// src/components/label/label.tsx
|
|
304
|
+
var React6 = __toESM(require("react"), 1);
|
|
305
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
306
|
+
var Label = React6.forwardRef(
|
|
307
|
+
({ className, children, required, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
|
|
308
|
+
"label",
|
|
309
|
+
{
|
|
310
|
+
ref,
|
|
311
|
+
className: cn(
|
|
312
|
+
"block text-sm font-medium text-gray-700 select-none",
|
|
313
|
+
className
|
|
314
|
+
),
|
|
315
|
+
...props,
|
|
316
|
+
children: [
|
|
317
|
+
children,
|
|
318
|
+
required && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ml-1 text-red-500", "aria-hidden": "true", children: "*" })
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
)
|
|
322
|
+
);
|
|
323
|
+
Label.displayName = "Label";
|
|
324
|
+
|
|
325
|
+
// src/components/form-field/form-field.tsx
|
|
326
|
+
var React7 = __toESM(require("react"), 1);
|
|
327
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
328
|
+
function FormField({
|
|
329
|
+
label,
|
|
330
|
+
htmlFor,
|
|
331
|
+
required,
|
|
332
|
+
hint,
|
|
333
|
+
error,
|
|
334
|
+
className,
|
|
335
|
+
children
|
|
336
|
+
}) {
|
|
337
|
+
const errorId = React7.useId();
|
|
338
|
+
const hintId = React7.useId();
|
|
339
|
+
const childrenWithProps = React7.Children.map(children, (child) => {
|
|
340
|
+
if (!React7.isValidElement(child)) return child;
|
|
341
|
+
return React7.cloneElement(child, {
|
|
342
|
+
id: htmlFor ?? child.props.id,
|
|
343
|
+
error: !!error || child.props.error,
|
|
344
|
+
"aria-describedby": [error ? errorId : null, hint ? hintId : null].filter(Boolean).join(" ") || void 0
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: cn("flex flex-col gap-1.5", className), children: [
|
|
348
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Label, { ...htmlFor !== void 0 ? { htmlFor } : {}, ...required ? { required } : {}, children: label }),
|
|
349
|
+
childrenWithProps,
|
|
350
|
+
hint && !error && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { id: hintId, className: "text-xs text-gray-500", children: hint }),
|
|
351
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { id: errorId, className: "text-xs text-red-600", role: "alert", children: error })
|
|
352
|
+
] });
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/components/spinner/spinner.tsx
|
|
356
|
+
var import_lucide_react3 = require("lucide-react");
|
|
357
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
358
|
+
var sizeClasses2 = { sm: 16, md: 24, lg: 36 };
|
|
359
|
+
function Spinner({ size = "md", label = "Loading\u2026", className }) {
|
|
360
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { role: "status", "aria-label": label, className: cn("inline-flex", className), children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
361
|
+
import_lucide_react3.Loader2,
|
|
362
|
+
{
|
|
363
|
+
size: sizeClasses2[size],
|
|
364
|
+
className: "animate-spin text-current",
|
|
365
|
+
"aria-hidden": "true"
|
|
366
|
+
}
|
|
367
|
+
) });
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// src/components/badge/badge.tsx
|
|
371
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
372
|
+
var variantClasses2 = {
|
|
373
|
+
default: "bg-gray-100 text-gray-700",
|
|
374
|
+
success: "bg-green-100 text-green-700",
|
|
375
|
+
warning: "bg-yellow-100 text-yellow-700",
|
|
376
|
+
destructive: "bg-red-100 text-red-700",
|
|
377
|
+
info: "bg-blue-100 text-blue-700",
|
|
378
|
+
outline: "border border-gray-300 text-gray-700 bg-transparent"
|
|
379
|
+
};
|
|
380
|
+
function Badge({ variant = "default", className, children, ...props }) {
|
|
381
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
382
|
+
"span",
|
|
383
|
+
{
|
|
384
|
+
className: cn(
|
|
385
|
+
"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
|
|
386
|
+
variantClasses2[variant],
|
|
387
|
+
className
|
|
388
|
+
),
|
|
389
|
+
...props,
|
|
390
|
+
children
|
|
391
|
+
}
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// src/components/alert/alert.tsx
|
|
396
|
+
var import_lucide_react4 = require("lucide-react");
|
|
397
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
398
|
+
var config = {
|
|
399
|
+
info: { icon: import_lucide_react4.Info, classes: "bg-blue-50 border-blue-200 text-blue-800" },
|
|
400
|
+
success: { icon: import_lucide_react4.CheckCircle2, classes: "bg-green-50 border-green-200 text-green-800" },
|
|
401
|
+
warning: { icon: import_lucide_react4.AlertTriangle, classes: "bg-yellow-50 border-yellow-200 text-yellow-800" },
|
|
402
|
+
destructive: { icon: import_lucide_react4.XCircle, classes: "bg-red-50 border-red-200 text-red-800" }
|
|
403
|
+
};
|
|
404
|
+
function Alert({ variant = "info", title, children, className, onDismiss }) {
|
|
405
|
+
const { icon: Icon, classes } = config[variant];
|
|
406
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
407
|
+
"div",
|
|
408
|
+
{
|
|
409
|
+
role: "alert",
|
|
410
|
+
className: cn("flex gap-3 rounded-md border p-4", classes, className),
|
|
411
|
+
children: [
|
|
412
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Icon, { size: 18, className: "mt-0.5 shrink-0", "aria-hidden": "true" }),
|
|
413
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "min-w-0 flex-1 text-sm", children: [
|
|
414
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "font-medium", children: title }),
|
|
415
|
+
children && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: cn(title && "mt-1", "opacity-90"), children })
|
|
416
|
+
] }),
|
|
417
|
+
onDismiss && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
418
|
+
"button",
|
|
419
|
+
{
|
|
420
|
+
onClick: onDismiss,
|
|
421
|
+
"aria-label": "Dismiss",
|
|
422
|
+
className: "shrink-0 rounded p-0.5 opacity-60 hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-current",
|
|
423
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react4.XCircle, { size: 16, "aria-hidden": "true" })
|
|
424
|
+
}
|
|
425
|
+
)
|
|
426
|
+
]
|
|
427
|
+
}
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// src/components/card/card.tsx
|
|
432
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
433
|
+
var paddingClasses = { none: "", sm: "p-3", md: "p-5", lg: "p-7" };
|
|
434
|
+
function Card({ className, padding = "md", children, ...props }) {
|
|
435
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
436
|
+
"div",
|
|
437
|
+
{
|
|
438
|
+
className: cn(
|
|
439
|
+
"rounded-lg border border-gray-200 bg-white shadow-sm",
|
|
440
|
+
paddingClasses[padding],
|
|
441
|
+
className
|
|
442
|
+
),
|
|
443
|
+
...props,
|
|
444
|
+
children
|
|
445
|
+
}
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
function CardHeader({ className, ...props }) {
|
|
449
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn("mb-4 flex flex-col gap-1", className), ...props });
|
|
450
|
+
}
|
|
451
|
+
function CardTitle({ className, ...props }) {
|
|
452
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: cn("text-base font-semibold text-gray-900", className), ...props });
|
|
453
|
+
}
|
|
454
|
+
function CardDescription({ className, ...props }) {
|
|
455
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: cn("text-sm text-gray-500", className), ...props });
|
|
456
|
+
}
|
|
457
|
+
function CardContent({ className, ...props }) {
|
|
458
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn("text-sm text-gray-700", className), ...props });
|
|
459
|
+
}
|
|
460
|
+
function CardFooter({ className, ...props }) {
|
|
461
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
462
|
+
"div",
|
|
463
|
+
{
|
|
464
|
+
className: cn("mt-4 flex flex-wrap items-center gap-2 border-t border-gray-100 pt-4", className),
|
|
465
|
+
...props
|
|
466
|
+
}
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// src/components/modal/modal.tsx
|
|
471
|
+
var React8 = __toESM(require("react"), 1);
|
|
472
|
+
var import_react_dom = require("react-dom");
|
|
473
|
+
var import_lucide_react5 = require("lucide-react");
|
|
474
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
475
|
+
var sizeClasses3 = {
|
|
476
|
+
sm: "sm:max-w-sm",
|
|
477
|
+
md: "sm:max-w-md",
|
|
478
|
+
lg: "sm:max-w-lg",
|
|
479
|
+
xl: "sm:max-w-xl",
|
|
480
|
+
full: "sm:max-w-[95vw]"
|
|
481
|
+
};
|
|
482
|
+
function Modal({
|
|
483
|
+
open,
|
|
484
|
+
onClose,
|
|
485
|
+
title,
|
|
486
|
+
description,
|
|
487
|
+
size = "md",
|
|
488
|
+
className,
|
|
489
|
+
children,
|
|
490
|
+
footer
|
|
491
|
+
}) {
|
|
492
|
+
const overlayRef = React8.useRef(null);
|
|
493
|
+
const titleId = React8.useId();
|
|
494
|
+
const descId = React8.useId();
|
|
495
|
+
React8.useEffect(() => {
|
|
496
|
+
if (!open) return;
|
|
497
|
+
const onKey = (e) => {
|
|
498
|
+
if (e.key === "Escape") onClose();
|
|
499
|
+
};
|
|
500
|
+
document.addEventListener("keydown", onKey);
|
|
501
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
502
|
+
}, [open, onClose]);
|
|
503
|
+
React8.useEffect(() => {
|
|
504
|
+
if (open) {
|
|
505
|
+
document.body.style.overflow = "hidden";
|
|
506
|
+
return () => {
|
|
507
|
+
document.body.style.overflow = "";
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
}, [open]);
|
|
511
|
+
React8.useEffect(() => {
|
|
512
|
+
if (!open) return;
|
|
513
|
+
const el = overlayRef.current;
|
|
514
|
+
if (!el) return;
|
|
515
|
+
const focusable = el.querySelectorAll(
|
|
516
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
517
|
+
);
|
|
518
|
+
const first = focusable[0];
|
|
519
|
+
const last = focusable[focusable.length - 1];
|
|
520
|
+
first?.focus();
|
|
521
|
+
const trap = (e) => {
|
|
522
|
+
if (e.key !== "Tab") return;
|
|
523
|
+
if (e.shiftKey) {
|
|
524
|
+
if (document.activeElement === first) {
|
|
525
|
+
e.preventDefault();
|
|
526
|
+
last?.focus();
|
|
527
|
+
}
|
|
528
|
+
} else {
|
|
529
|
+
if (document.activeElement === last) {
|
|
530
|
+
e.preventDefault();
|
|
531
|
+
first?.focus();
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
document.addEventListener("keydown", trap);
|
|
536
|
+
return () => document.removeEventListener("keydown", trap);
|
|
537
|
+
}, [open]);
|
|
538
|
+
if (!open) return null;
|
|
539
|
+
return (0, import_react_dom.createPortal)(
|
|
540
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
541
|
+
"div",
|
|
542
|
+
{
|
|
543
|
+
ref: overlayRef,
|
|
544
|
+
role: "dialog",
|
|
545
|
+
"aria-modal": "true",
|
|
546
|
+
"aria-labelledby": title ? titleId : void 0,
|
|
547
|
+
"aria-describedby": description ? descId : void 0,
|
|
548
|
+
className: "fixed inset-0 z-50 flex items-end justify-center sm:items-center sm:p-4",
|
|
549
|
+
children: [
|
|
550
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
551
|
+
"div",
|
|
552
|
+
{
|
|
553
|
+
className: "absolute inset-0 bg-black/50 backdrop-blur-sm",
|
|
554
|
+
"aria-hidden": "true",
|
|
555
|
+
onClick: onClose
|
|
556
|
+
}
|
|
557
|
+
),
|
|
558
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
|
|
559
|
+
"div",
|
|
560
|
+
{
|
|
561
|
+
className: cn(
|
|
562
|
+
"relative z-10 flex w-full flex-col bg-white shadow-xl",
|
|
563
|
+
// mobile: slides from bottom, rounded top only
|
|
564
|
+
"rounded-t-2xl sm:rounded-xl",
|
|
565
|
+
// desktop: constrained width
|
|
566
|
+
sizeClasses3[size],
|
|
567
|
+
// max height with scroll
|
|
568
|
+
"max-h-[90dvh] overflow-hidden",
|
|
569
|
+
className
|
|
570
|
+
),
|
|
571
|
+
children: [
|
|
572
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-start justify-between gap-4 border-b border-gray-100 px-5 py-4", children: [
|
|
573
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "min-w-0", children: [
|
|
574
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("h2", { id: titleId, className: "text-base font-semibold text-gray-900", children: title }),
|
|
575
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { id: descId, className: "mt-0.5 text-sm text-gray-500", children: description })
|
|
576
|
+
] }),
|
|
577
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
578
|
+
"button",
|
|
579
|
+
{
|
|
580
|
+
onClick: onClose,
|
|
581
|
+
"aria-label": "Close",
|
|
582
|
+
className: "shrink-0 rounded p-1 text-gray-400 hover:text-gray-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500",
|
|
583
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react5.X, { size: 18, "aria-hidden": "true" })
|
|
584
|
+
}
|
|
585
|
+
)
|
|
586
|
+
] }),
|
|
587
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex-1 overflow-y-auto px-5 py-4", children }),
|
|
588
|
+
footer && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex flex-col-reverse gap-2 border-t border-gray-100 px-5 py-4 sm:flex-row sm:justify-end", children: footer })
|
|
589
|
+
]
|
|
590
|
+
}
|
|
591
|
+
)
|
|
592
|
+
]
|
|
593
|
+
}
|
|
594
|
+
),
|
|
595
|
+
document.body
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// src/components/confirm-dialog/confirm-dialog.tsx
|
|
600
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
601
|
+
function ConfirmDialog({
|
|
602
|
+
open,
|
|
603
|
+
onClose,
|
|
604
|
+
onConfirm,
|
|
605
|
+
title,
|
|
606
|
+
description,
|
|
607
|
+
confirmLabel = "Confirm",
|
|
608
|
+
cancelLabel = "Cancel",
|
|
609
|
+
variant = "primary",
|
|
610
|
+
loading = false
|
|
611
|
+
}) {
|
|
612
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
613
|
+
Modal,
|
|
614
|
+
{
|
|
615
|
+
open,
|
|
616
|
+
onClose,
|
|
617
|
+
title,
|
|
618
|
+
...description !== void 0 ? { description } : {},
|
|
619
|
+
size: "sm",
|
|
620
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_jsx_runtime13.Fragment, { children: [
|
|
621
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant: "ghost", onClick: onClose, disabled: loading, fullWidth: true, children: cancelLabel }),
|
|
622
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(Button, { variant, onClick: onConfirm, loading, fullWidth: true, children: confirmLabel })
|
|
623
|
+
] }),
|
|
624
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", {})
|
|
625
|
+
}
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// src/components/toast/toast.tsx
|
|
630
|
+
var import_lucide_react6 = require("lucide-react");
|
|
631
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
632
|
+
var iconMap = {
|
|
633
|
+
success: import_lucide_react6.CheckCircle2,
|
|
634
|
+
error: import_lucide_react6.XCircle,
|
|
635
|
+
warning: import_lucide_react6.AlertTriangle,
|
|
636
|
+
info: import_lucide_react6.Info
|
|
637
|
+
};
|
|
638
|
+
var styleMap = {
|
|
639
|
+
success: "border-green-200 bg-green-50 text-green-800",
|
|
640
|
+
error: "border-red-200 bg-red-50 text-red-800",
|
|
641
|
+
warning: "border-yellow-200 bg-yellow-50 text-yellow-800",
|
|
642
|
+
info: "border-blue-200 bg-blue-50 text-blue-800"
|
|
643
|
+
};
|
|
644
|
+
function ToastItem({ toast, onDismiss }) {
|
|
645
|
+
const Icon = iconMap[toast.type];
|
|
646
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
|
|
647
|
+
"div",
|
|
648
|
+
{
|
|
649
|
+
role: "status",
|
|
650
|
+
"aria-live": "polite",
|
|
651
|
+
className: cn(
|
|
652
|
+
"flex w-full items-start gap-3 rounded-lg border p-4 shadow-md",
|
|
653
|
+
"animate-in slide-in-from-bottom-2 duration-200",
|
|
654
|
+
styleMap[toast.type]
|
|
655
|
+
),
|
|
656
|
+
children: [
|
|
657
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Icon, { size: 18, className: "mt-0.5 shrink-0", "aria-hidden": "true" }),
|
|
658
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "min-w-0 flex-1 text-sm", children: [
|
|
659
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "font-medium", children: toast.title }),
|
|
660
|
+
toast.description && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "mt-0.5 opacity-80", children: toast.description })
|
|
661
|
+
] }),
|
|
662
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
663
|
+
"button",
|
|
664
|
+
{
|
|
665
|
+
onClick: () => onDismiss(toast.id),
|
|
666
|
+
"aria-label": "Dismiss",
|
|
667
|
+
className: "shrink-0 rounded p-0.5 opacity-60 hover:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-current",
|
|
668
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react6.X, { size: 14, "aria-hidden": "true" })
|
|
669
|
+
}
|
|
670
|
+
)
|
|
671
|
+
]
|
|
672
|
+
}
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// src/components/toast/toast-context.tsx
|
|
677
|
+
var React9 = __toESM(require("react"), 1);
|
|
678
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
679
|
+
var ToastContext = React9.createContext(null);
|
|
680
|
+
function ToastProvider({ children }) {
|
|
681
|
+
const [toasts, setToasts] = React9.useState([]);
|
|
682
|
+
const removeToast = React9.useCallback((id) => {
|
|
683
|
+
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
684
|
+
}, []);
|
|
685
|
+
const addToast = React9.useCallback(
|
|
686
|
+
({ type, title, description, duration = 4e3 }) => {
|
|
687
|
+
const id = crypto.randomUUID();
|
|
688
|
+
setToasts((prev) => [
|
|
689
|
+
...prev,
|
|
690
|
+
{
|
|
691
|
+
id,
|
|
692
|
+
type,
|
|
693
|
+
title,
|
|
694
|
+
...description !== void 0 ? { description } : {},
|
|
695
|
+
...duration !== void 0 ? { duration } : {}
|
|
696
|
+
}
|
|
697
|
+
]);
|
|
698
|
+
if (duration > 0) {
|
|
699
|
+
setTimeout(() => removeToast(id), duration);
|
|
700
|
+
}
|
|
701
|
+
},
|
|
702
|
+
[removeToast]
|
|
703
|
+
);
|
|
704
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(ToastContext.Provider, { value: { addToast, removeToast }, children: [
|
|
705
|
+
children,
|
|
706
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
707
|
+
"div",
|
|
708
|
+
{
|
|
709
|
+
"aria-label": "Notifications",
|
|
710
|
+
className: "fixed bottom-0 left-0 right-0 z-50 flex flex-col gap-2 p-4 sm:bottom-4 sm:left-auto sm:right-4 sm:w-96",
|
|
711
|
+
children: toasts.map((toast) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ToastItem, { toast, onDismiss: removeToast }, toast.id))
|
|
712
|
+
}
|
|
713
|
+
)
|
|
714
|
+
] });
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// src/components/toast/use-toast.ts
|
|
718
|
+
var React10 = __toESM(require("react"), 1);
|
|
719
|
+
function useToast() {
|
|
720
|
+
const ctx = React10.useContext(ToastContext);
|
|
721
|
+
if (!ctx) {
|
|
722
|
+
throw new Error("useToast must be used inside <ToastProvider>");
|
|
723
|
+
}
|
|
724
|
+
return ctx;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// src/components/table/table.tsx
|
|
728
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
729
|
+
function Table({ className, children, ...props }) {
|
|
730
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "w-full sm:overflow-x-auto sm:rounded-lg sm:border sm:border-gray-200", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
731
|
+
"table",
|
|
732
|
+
{
|
|
733
|
+
className: cn(
|
|
734
|
+
"w-full border-collapse text-sm",
|
|
735
|
+
// desktop keeps min-width so columns never crush
|
|
736
|
+
"sm:min-w-[600px]",
|
|
737
|
+
className
|
|
738
|
+
),
|
|
739
|
+
...props,
|
|
740
|
+
children
|
|
741
|
+
}
|
|
742
|
+
) });
|
|
743
|
+
}
|
|
744
|
+
function TableHeader({ className, ...props }) {
|
|
745
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
746
|
+
"thead",
|
|
747
|
+
{
|
|
748
|
+
className: cn(
|
|
749
|
+
"hidden sm:table-header-group",
|
|
750
|
+
"border-b border-gray-200 bg-gray-50",
|
|
751
|
+
className
|
|
752
|
+
),
|
|
753
|
+
...props
|
|
754
|
+
}
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
function TableBody({ className, ...props }) {
|
|
758
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
759
|
+
"tbody",
|
|
760
|
+
{
|
|
761
|
+
className: cn(
|
|
762
|
+
// mobile: stack rows as cards
|
|
763
|
+
"flex flex-col gap-3",
|
|
764
|
+
// sm+: normal tbody
|
|
765
|
+
"sm:table-row-group sm:gap-0 sm:divide-y sm:divide-gray-100 sm:bg-white",
|
|
766
|
+
className
|
|
767
|
+
),
|
|
768
|
+
...props
|
|
769
|
+
}
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
function TableRow({ className, ...props }) {
|
|
773
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
774
|
+
"tr",
|
|
775
|
+
{
|
|
776
|
+
className: cn(
|
|
777
|
+
// mobile: card
|
|
778
|
+
"block rounded-lg border border-gray-200 bg-white p-4 shadow-sm",
|
|
779
|
+
// sm+: normal row
|
|
780
|
+
"sm:table-row sm:rounded-none sm:border-0 sm:p-0 sm:shadow-none sm:transition-colors sm:hover:bg-gray-50",
|
|
781
|
+
className
|
|
782
|
+
),
|
|
783
|
+
...props
|
|
784
|
+
}
|
|
785
|
+
);
|
|
786
|
+
}
|
|
787
|
+
function TableHead({ className, align = "left", ...props }) {
|
|
788
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
789
|
+
"th",
|
|
790
|
+
{
|
|
791
|
+
scope: "col",
|
|
792
|
+
className: cn(
|
|
793
|
+
"px-4 py-3 text-xs font-semibold uppercase tracking-wide text-gray-500 whitespace-nowrap",
|
|
794
|
+
align === "center" && "text-center",
|
|
795
|
+
align === "right" && "text-right",
|
|
796
|
+
className
|
|
797
|
+
),
|
|
798
|
+
...props
|
|
799
|
+
}
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
function TableCell({ className, align = "left", label, children, ...props }) {
|
|
803
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
|
|
804
|
+
"td",
|
|
805
|
+
{
|
|
806
|
+
className: cn(
|
|
807
|
+
// mobile: each cell is a flex row — "Label value"
|
|
808
|
+
"flex items-start justify-between gap-4 py-1.5 text-gray-700",
|
|
809
|
+
"last:pb-0 first:pt-0",
|
|
810
|
+
// sm+: normal table cell
|
|
811
|
+
"sm:table-cell sm:px-4 sm:py-3",
|
|
812
|
+
align === "center" && "sm:text-center",
|
|
813
|
+
align === "right" && "sm:text-right",
|
|
814
|
+
className
|
|
815
|
+
),
|
|
816
|
+
...props,
|
|
817
|
+
children: [
|
|
818
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "shrink-0 text-xs font-semibold uppercase tracking-wide text-gray-400 sm:hidden", children: label }),
|
|
819
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: cn("sm:contents", label && "text-right sm:text-left"), children })
|
|
820
|
+
]
|
|
821
|
+
}
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// src/components/table/table-toolbar.tsx
|
|
826
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
827
|
+
function TableToolbar({ filters, actions, className }) {
|
|
828
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
|
|
829
|
+
"div",
|
|
830
|
+
{
|
|
831
|
+
className: cn(
|
|
832
|
+
"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
|
|
833
|
+
"mb-3",
|
|
834
|
+
className
|
|
835
|
+
),
|
|
836
|
+
children: [
|
|
837
|
+
filters && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex flex-wrap items-center gap-2", children: filters }),
|
|
838
|
+
actions && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex shrink-0 flex-wrap items-center gap-2 sm:ml-auto", children: actions })
|
|
839
|
+
]
|
|
840
|
+
}
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
// src/components/table/table-empty-state.tsx
|
|
845
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
846
|
+
function TableEmptyState({
|
|
847
|
+
colSpan,
|
|
848
|
+
title = "No results",
|
|
849
|
+
description,
|
|
850
|
+
action,
|
|
851
|
+
icon,
|
|
852
|
+
className
|
|
853
|
+
}) {
|
|
854
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("tr", { className: "block sm:table-row", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("td", { colSpan, className: cn("block px-4 py-12 text-center sm:table-cell", className), children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col items-center gap-3", children: [
|
|
855
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "text-gray-300", "aria-hidden": "true", children: icon }),
|
|
856
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "text-sm font-medium text-gray-600", children: title }),
|
|
857
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "max-w-xs text-xs text-gray-400", children: description }),
|
|
858
|
+
action && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "mt-1", children: action })
|
|
859
|
+
] }) }) });
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// src/components/page-header/page-header.tsx
|
|
863
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
864
|
+
function PageHeader({ title, description, prefix, actions, className }) {
|
|
865
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: cn("mb-6", className), children: [
|
|
866
|
+
prefix && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "mb-2", children: prefix }),
|
|
867
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between", children: [
|
|
868
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "min-w-0", children: [
|
|
869
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("h1", { className: "truncate text-2xl font-bold text-gray-900 sm:text-3xl", children: title }),
|
|
870
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "mt-1 text-sm text-gray-500", children: description })
|
|
871
|
+
] }),
|
|
872
|
+
actions && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "flex shrink-0 flex-wrap items-center gap-2", children: actions })
|
|
873
|
+
] })
|
|
874
|
+
] });
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
// src/components/empty-state/empty-state.tsx
|
|
878
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
879
|
+
function EmptyState({
|
|
880
|
+
icon,
|
|
881
|
+
title,
|
|
882
|
+
description,
|
|
883
|
+
action,
|
|
884
|
+
secondaryAction,
|
|
885
|
+
className
|
|
886
|
+
}) {
|
|
887
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
888
|
+
"div",
|
|
889
|
+
{
|
|
890
|
+
className: cn(
|
|
891
|
+
"flex flex-col items-center justify-center gap-4 rounded-lg border border-dashed border-gray-200 bg-gray-50 px-6 py-16 text-center",
|
|
892
|
+
className
|
|
893
|
+
),
|
|
894
|
+
children: [
|
|
895
|
+
icon && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-gray-300", "aria-hidden": "true", children: icon }),
|
|
896
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "max-w-xs", children: [
|
|
897
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "text-sm font-semibold text-gray-700", children: title }),
|
|
898
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { className: "mt-1 text-sm text-gray-400", children: description })
|
|
899
|
+
] }),
|
|
900
|
+
(action || secondaryAction) && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-col items-center gap-2 sm:flex-row", children: [
|
|
901
|
+
action,
|
|
902
|
+
secondaryAction
|
|
903
|
+
] })
|
|
904
|
+
]
|
|
905
|
+
}
|
|
906
|
+
);
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// src/components/app-shell/app-shell.tsx
|
|
910
|
+
var React12 = __toESM(require("react"), 1);
|
|
911
|
+
|
|
912
|
+
// src/components/drawer/drawer.tsx
|
|
913
|
+
var React11 = __toESM(require("react"), 1);
|
|
914
|
+
var import_react_dom2 = require("react-dom");
|
|
915
|
+
var import_lucide_react7 = require("lucide-react");
|
|
916
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
917
|
+
function Drawer({
|
|
918
|
+
open,
|
|
919
|
+
onClose,
|
|
920
|
+
title,
|
|
921
|
+
description,
|
|
922
|
+
side = "right",
|
|
923
|
+
widthClass = "sm:w-96",
|
|
924
|
+
className,
|
|
925
|
+
children,
|
|
926
|
+
footer
|
|
927
|
+
}) {
|
|
928
|
+
const overlayRef = React11.useRef(null);
|
|
929
|
+
const titleId = React11.useId();
|
|
930
|
+
const descId = React11.useId();
|
|
931
|
+
React11.useEffect(() => {
|
|
932
|
+
if (!open) return;
|
|
933
|
+
const h = (e) => {
|
|
934
|
+
if (e.key === "Escape") onClose();
|
|
935
|
+
};
|
|
936
|
+
document.addEventListener("keydown", h);
|
|
937
|
+
return () => document.removeEventListener("keydown", h);
|
|
938
|
+
}, [open, onClose]);
|
|
939
|
+
React11.useEffect(() => {
|
|
940
|
+
if (open) {
|
|
941
|
+
document.body.style.overflow = "hidden";
|
|
942
|
+
return () => {
|
|
943
|
+
document.body.style.overflow = "";
|
|
944
|
+
};
|
|
945
|
+
}
|
|
946
|
+
}, [open]);
|
|
947
|
+
React11.useEffect(() => {
|
|
948
|
+
if (!open) return;
|
|
949
|
+
const el = overlayRef.current;
|
|
950
|
+
if (!el) return;
|
|
951
|
+
const focusable = el.querySelectorAll(
|
|
952
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
953
|
+
);
|
|
954
|
+
const first = focusable[0];
|
|
955
|
+
const last = focusable[focusable.length - 1];
|
|
956
|
+
first?.focus();
|
|
957
|
+
const trap = (e) => {
|
|
958
|
+
if (e.key !== "Tab") return;
|
|
959
|
+
if (e.shiftKey) {
|
|
960
|
+
if (document.activeElement === first) {
|
|
961
|
+
e.preventDefault();
|
|
962
|
+
last?.focus();
|
|
963
|
+
}
|
|
964
|
+
} else {
|
|
965
|
+
if (document.activeElement === last) {
|
|
966
|
+
e.preventDefault();
|
|
967
|
+
first?.focus();
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
};
|
|
971
|
+
document.addEventListener("keydown", trap);
|
|
972
|
+
return () => document.removeEventListener("keydown", trap);
|
|
973
|
+
}, [open]);
|
|
974
|
+
if (!open) return null;
|
|
975
|
+
return (0, import_react_dom2.createPortal)(
|
|
976
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
977
|
+
"div",
|
|
978
|
+
{
|
|
979
|
+
ref: overlayRef,
|
|
980
|
+
role: "dialog",
|
|
981
|
+
"aria-modal": "true",
|
|
982
|
+
"aria-labelledby": title ? titleId : void 0,
|
|
983
|
+
"aria-describedby": description ? descId : void 0,
|
|
984
|
+
className: "fixed inset-0 z-50 flex",
|
|
985
|
+
children: [
|
|
986
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
987
|
+
"div",
|
|
988
|
+
{
|
|
989
|
+
className: "absolute inset-0 bg-black/50 backdrop-blur-sm",
|
|
990
|
+
"aria-hidden": "true",
|
|
991
|
+
onClick: onClose
|
|
992
|
+
}
|
|
993
|
+
),
|
|
994
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
995
|
+
"div",
|
|
996
|
+
{
|
|
997
|
+
className: cn(
|
|
998
|
+
"absolute bottom-0 left-0 right-0 z-10 flex flex-col bg-white shadow-xl",
|
|
999
|
+
"max-h-[90dvh] rounded-t-2xl",
|
|
1000
|
+
// sm+: full height side panel
|
|
1001
|
+
"sm:inset-y-0 sm:bottom-auto sm:top-0 sm:max-h-full sm:rounded-none",
|
|
1002
|
+
side === "right" ? "sm:right-0 sm:left-auto" : "sm:left-0 sm:right-auto",
|
|
1003
|
+
widthClass,
|
|
1004
|
+
className
|
|
1005
|
+
),
|
|
1006
|
+
children: [
|
|
1007
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-start justify-between gap-4 border-b border-gray-100 px-5 py-4", children: [
|
|
1008
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "min-w-0", children: [
|
|
1009
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("h2", { id: titleId, className: "text-base font-semibold text-gray-900", children: title }),
|
|
1010
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { id: descId, className: "mt-0.5 text-sm text-gray-500", children: description })
|
|
1011
|
+
] }),
|
|
1012
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1013
|
+
"button",
|
|
1014
|
+
{
|
|
1015
|
+
onClick: onClose,
|
|
1016
|
+
"aria-label": "Close",
|
|
1017
|
+
className: "shrink-0 rounded p-1 text-gray-400 hover:text-gray-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500",
|
|
1018
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react7.X, { size: 18, "aria-hidden": "true" })
|
|
1019
|
+
}
|
|
1020
|
+
)
|
|
1021
|
+
] }),
|
|
1022
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex-1 overflow-y-auto px-5 py-4", children }),
|
|
1023
|
+
footer && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex flex-col-reverse gap-2 border-t border-gray-100 px-5 py-4 sm:flex-row sm:justify-end", children: footer })
|
|
1024
|
+
]
|
|
1025
|
+
}
|
|
1026
|
+
)
|
|
1027
|
+
]
|
|
1028
|
+
}
|
|
1029
|
+
),
|
|
1030
|
+
document.body
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// src/components/sidebar/sidebar.tsx
|
|
1035
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1036
|
+
function Sidebar({ logo, groups, footer, className }) {
|
|
1037
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
1038
|
+
"aside",
|
|
1039
|
+
{
|
|
1040
|
+
className: cn(
|
|
1041
|
+
"flex h-full w-64 flex-col border-r border-gray-200 bg-white",
|
|
1042
|
+
className
|
|
1043
|
+
),
|
|
1044
|
+
"aria-label": "Sidebar navigation",
|
|
1045
|
+
children: [
|
|
1046
|
+
logo && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex h-16 shrink-0 items-center border-b border-gray-100 px-4", children: logo }),
|
|
1047
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("nav", { className: "flex-1 overflow-y-auto px-3 py-4", children: groups.map((group, gi) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: cn(gi > 0 && "mt-6"), children: [
|
|
1048
|
+
group.title && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("p", { className: "mb-1 px-2 text-xs font-semibold uppercase tracking-wider text-gray-400", children: group.title }),
|
|
1049
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("ul", { role: "list", className: "flex flex-col gap-0.5", children: group.items.map((item, ii) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(SidebarItem, { item }) }, ii)) })
|
|
1050
|
+
] }, gi)) }),
|
|
1051
|
+
footer && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "shrink-0 border-t border-gray-100 p-3", children: footer })
|
|
1052
|
+
]
|
|
1053
|
+
}
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
function SidebarItem({ item }) {
|
|
1057
|
+
const Tag = item.href ? "a" : "button";
|
|
1058
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
1059
|
+
Tag,
|
|
1060
|
+
{
|
|
1061
|
+
href: item.href,
|
|
1062
|
+
onClick: item.onClick,
|
|
1063
|
+
disabled: item.disabled,
|
|
1064
|
+
"aria-current": item.active ? "page" : void 0,
|
|
1065
|
+
className: cn(
|
|
1066
|
+
"group flex w-full items-center gap-3 rounded-md px-3 py-2 text-sm font-medium",
|
|
1067
|
+
"transition-colors duration-100",
|
|
1068
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500",
|
|
1069
|
+
// mobile touch target
|
|
1070
|
+
"min-h-[44px] sm:min-h-[36px]",
|
|
1071
|
+
item.active ? "bg-blue-50 text-blue-700" : "text-gray-600 hover:bg-gray-100 hover:text-gray-900",
|
|
1072
|
+
item.disabled && "cursor-not-allowed opacity-40 pointer-events-none"
|
|
1073
|
+
),
|
|
1074
|
+
children: [
|
|
1075
|
+
item.icon && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1076
|
+
"span",
|
|
1077
|
+
{
|
|
1078
|
+
"aria-hidden": "true",
|
|
1079
|
+
className: cn(
|
|
1080
|
+
"shrink-0",
|
|
1081
|
+
item.active ? "text-blue-600" : "text-gray-400 group-hover:text-gray-600"
|
|
1082
|
+
),
|
|
1083
|
+
children: item.icon
|
|
1084
|
+
}
|
|
1085
|
+
),
|
|
1086
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "flex-1 truncate text-left", children: item.label }),
|
|
1087
|
+
item.badge !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1088
|
+
"span",
|
|
1089
|
+
{
|
|
1090
|
+
className: cn(
|
|
1091
|
+
"shrink-0 rounded-full px-2 py-0.5 text-xs font-medium",
|
|
1092
|
+
item.active ? "bg-blue-100 text-blue-700" : "bg-gray-100 text-gray-500"
|
|
1093
|
+
),
|
|
1094
|
+
children: item.badge
|
|
1095
|
+
}
|
|
1096
|
+
)
|
|
1097
|
+
]
|
|
1098
|
+
}
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
// src/components/topbar/topbar.tsx
|
|
1103
|
+
var import_lucide_react8 = require("lucide-react");
|
|
1104
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1105
|
+
function Topbar({
|
|
1106
|
+
onMenuOpen,
|
|
1107
|
+
title,
|
|
1108
|
+
actions,
|
|
1109
|
+
hideMenuButton = false,
|
|
1110
|
+
className
|
|
1111
|
+
}) {
|
|
1112
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
1113
|
+
"header",
|
|
1114
|
+
{
|
|
1115
|
+
className: cn(
|
|
1116
|
+
"flex h-14 shrink-0 items-center gap-3 border-b border-gray-200 bg-white px-4",
|
|
1117
|
+
className
|
|
1118
|
+
),
|
|
1119
|
+
children: [
|
|
1120
|
+
!hideMenuButton && onMenuOpen && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1121
|
+
"button",
|
|
1122
|
+
{
|
|
1123
|
+
onClick: onMenuOpen,
|
|
1124
|
+
"aria-label": "Open navigation menu",
|
|
1125
|
+
className: cn(
|
|
1126
|
+
"inline-flex h-9 w-9 items-center justify-center rounded-md text-gray-500",
|
|
1127
|
+
"hover:bg-gray-100 hover:text-gray-700",
|
|
1128
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500",
|
|
1129
|
+
// visible on mobile, hidden when sidebar renders on sm+
|
|
1130
|
+
"sm:hidden"
|
|
1131
|
+
),
|
|
1132
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react8.Menu, { size: 20, "aria-hidden": "true" })
|
|
1133
|
+
}
|
|
1134
|
+
),
|
|
1135
|
+
title && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 truncate text-sm font-semibold text-gray-900 sm:text-base", children: title }),
|
|
1136
|
+
!title && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1" }),
|
|
1137
|
+
actions && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex shrink-0 items-center gap-2", children: actions })
|
|
1138
|
+
]
|
|
1139
|
+
}
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// src/components/app-shell/app-shell.tsx
|
|
1144
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1145
|
+
function AppShell({ sidebar, topbar, children, className }) {
|
|
1146
|
+
const [mobileOpen, setMobileOpen] = React12.useState(false);
|
|
1147
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: cn("flex h-dvh overflow-hidden bg-gray-50", className), children: [
|
|
1148
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "hidden sm:flex sm:shrink-0", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Sidebar, { ...sidebar }) }),
|
|
1149
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1150
|
+
Drawer,
|
|
1151
|
+
{
|
|
1152
|
+
open: mobileOpen,
|
|
1153
|
+
onClose: () => setMobileOpen(false),
|
|
1154
|
+
side: "left",
|
|
1155
|
+
widthClass: "sm:w-64",
|
|
1156
|
+
className: "p-0 sm:rounded-none",
|
|
1157
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "-mx-5 -my-4 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1158
|
+
Sidebar,
|
|
1159
|
+
{
|
|
1160
|
+
...sidebar,
|
|
1161
|
+
className: "h-full border-r-0"
|
|
1162
|
+
}
|
|
1163
|
+
) })
|
|
1164
|
+
}
|
|
1165
|
+
),
|
|
1166
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
|
|
1167
|
+
topbar !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1168
|
+
Topbar,
|
|
1169
|
+
{
|
|
1170
|
+
...topbar,
|
|
1171
|
+
onMenuOpen: () => setMobileOpen(true),
|
|
1172
|
+
hideMenuButton: false
|
|
1173
|
+
}
|
|
1174
|
+
),
|
|
1175
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("main", { className: "flex-1 overflow-y-auto p-4 sm:p-6", children })
|
|
1176
|
+
] })
|
|
1177
|
+
] });
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// src/components/tabs/tabs.tsx
|
|
1181
|
+
var React13 = __toESM(require("react"), 1);
|
|
1182
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1183
|
+
function Tabs({ tabs, value, onChange, children, className }) {
|
|
1184
|
+
const tabListRef = React13.useRef(null);
|
|
1185
|
+
const onKeyDown = (e, current) => {
|
|
1186
|
+
const enabled = tabs.filter((t) => !t.disabled);
|
|
1187
|
+
const idx = enabled.findIndex((t) => t.value === current);
|
|
1188
|
+
if (e.key === "ArrowRight") {
|
|
1189
|
+
e.preventDefault();
|
|
1190
|
+
const next = enabled[(idx + 1) % enabled.length];
|
|
1191
|
+
if (next) onChange(next.value);
|
|
1192
|
+
}
|
|
1193
|
+
if (e.key === "ArrowLeft") {
|
|
1194
|
+
e.preventDefault();
|
|
1195
|
+
const prev = enabled[(idx - 1 + enabled.length) % enabled.length];
|
|
1196
|
+
if (prev) onChange(prev.value);
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: cn("w-full", className), children: [
|
|
1200
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1201
|
+
"div",
|
|
1202
|
+
{
|
|
1203
|
+
ref: tabListRef,
|
|
1204
|
+
role: "tablist",
|
|
1205
|
+
className: "flex overflow-x-auto border-b border-gray-200 scrollbar-none",
|
|
1206
|
+
style: { scrollbarWidth: "none" },
|
|
1207
|
+
children: tabs.map((tab) => {
|
|
1208
|
+
const isActive = tab.value === value;
|
|
1209
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1210
|
+
"button",
|
|
1211
|
+
{
|
|
1212
|
+
role: "tab",
|
|
1213
|
+
id: `tab-${tab.value}`,
|
|
1214
|
+
"aria-selected": isActive,
|
|
1215
|
+
"aria-controls": `panel-${tab.value}`,
|
|
1216
|
+
disabled: tab.disabled,
|
|
1217
|
+
tabIndex: isActive ? 0 : -1,
|
|
1218
|
+
onClick: () => !tab.disabled && onChange(tab.value),
|
|
1219
|
+
onKeyDown: (e) => onKeyDown(e, tab.value),
|
|
1220
|
+
className: cn(
|
|
1221
|
+
"inline-flex shrink-0 items-center whitespace-nowrap border-b-2 px-4 py-3 text-sm font-medium",
|
|
1222
|
+
"transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-blue-500",
|
|
1223
|
+
// mobile touch target
|
|
1224
|
+
"min-h-[44px]",
|
|
1225
|
+
isActive ? "border-blue-600 text-blue-600" : "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700",
|
|
1226
|
+
tab.disabled && "cursor-not-allowed opacity-40"
|
|
1227
|
+
),
|
|
1228
|
+
children: tab.label
|
|
1229
|
+
},
|
|
1230
|
+
tab.value
|
|
1231
|
+
);
|
|
1232
|
+
})
|
|
1233
|
+
}
|
|
1234
|
+
),
|
|
1235
|
+
children
|
|
1236
|
+
] });
|
|
1237
|
+
}
|
|
1238
|
+
function TabPanel({ value, activeValue, children, className }) {
|
|
1239
|
+
const isActive = value === activeValue;
|
|
1240
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1241
|
+
"div",
|
|
1242
|
+
{
|
|
1243
|
+
role: "tabpanel",
|
|
1244
|
+
id: `panel-${value}`,
|
|
1245
|
+
"aria-labelledby": `tab-${value}`,
|
|
1246
|
+
hidden: !isActive,
|
|
1247
|
+
className: cn("py-4", className),
|
|
1248
|
+
children: isActive ? children : null
|
|
1249
|
+
}
|
|
1250
|
+
);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// src/components/dropdown-menu/dropdown-menu.tsx
|
|
1254
|
+
var React14 = __toESM(require("react"), 1);
|
|
1255
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1256
|
+
function DropdownMenu({ trigger, items, align = "right", className }) {
|
|
1257
|
+
const [open, setOpen] = React14.useState(false);
|
|
1258
|
+
const containerRef = React14.useRef(null);
|
|
1259
|
+
const menuRef = React14.useRef(null);
|
|
1260
|
+
React14.useEffect(() => {
|
|
1261
|
+
if (!open) return;
|
|
1262
|
+
const handler = (e) => {
|
|
1263
|
+
if (!containerRef.current?.contains(e.target)) setOpen(false);
|
|
1264
|
+
};
|
|
1265
|
+
document.addEventListener("mousedown", handler);
|
|
1266
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
1267
|
+
}, [open]);
|
|
1268
|
+
React14.useEffect(() => {
|
|
1269
|
+
if (!open) return;
|
|
1270
|
+
const handler = (e) => {
|
|
1271
|
+
if (e.key === "Escape") {
|
|
1272
|
+
setOpen(false);
|
|
1273
|
+
containerRef.current?.querySelector("[data-trigger]")?.focus();
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
document.addEventListener("keydown", handler);
|
|
1277
|
+
const first = menuRef.current?.querySelector("[role='menuitem']:not([disabled])");
|
|
1278
|
+
first?.focus();
|
|
1279
|
+
return () => document.removeEventListener("keydown", handler);
|
|
1280
|
+
}, [open]);
|
|
1281
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { ref: containerRef, className: cn("relative inline-block", className), children: [
|
|
1282
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1283
|
+
"span",
|
|
1284
|
+
{
|
|
1285
|
+
"data-trigger": true,
|
|
1286
|
+
onClick: () => setOpen((v) => !v),
|
|
1287
|
+
"aria-haspopup": "menu",
|
|
1288
|
+
"aria-expanded": open,
|
|
1289
|
+
className: "inline-flex",
|
|
1290
|
+
children: trigger
|
|
1291
|
+
}
|
|
1292
|
+
),
|
|
1293
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1294
|
+
"div",
|
|
1295
|
+
{
|
|
1296
|
+
ref: menuRef,
|
|
1297
|
+
role: "menu",
|
|
1298
|
+
className: cn(
|
|
1299
|
+
"absolute z-50 mt-1 min-w-[160px] overflow-hidden rounded-lg border border-gray-200 bg-white py-1 shadow-lg",
|
|
1300
|
+
align === "right" ? "right-0" : "left-0"
|
|
1301
|
+
),
|
|
1302
|
+
children: items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(React14.Fragment, { children: [
|
|
1303
|
+
item.divider && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "my-1 border-t border-gray-100" }),
|
|
1304
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
1305
|
+
"button",
|
|
1306
|
+
{
|
|
1307
|
+
role: "menuitem",
|
|
1308
|
+
disabled: item.disabled,
|
|
1309
|
+
onClick: () => {
|
|
1310
|
+
if (item.disabled) return;
|
|
1311
|
+
item.onClick?.();
|
|
1312
|
+
setOpen(false);
|
|
1313
|
+
},
|
|
1314
|
+
className: cn(
|
|
1315
|
+
"flex w-full items-center gap-2 px-4 py-2 text-left text-sm",
|
|
1316
|
+
"transition-colors duration-100",
|
|
1317
|
+
"focus-visible:outline-none focus-visible:bg-gray-50",
|
|
1318
|
+
item.destructive ? "text-red-600 hover:bg-red-50" : "text-gray-700 hover:bg-gray-50",
|
|
1319
|
+
item.disabled && "cursor-not-allowed opacity-40"
|
|
1320
|
+
),
|
|
1321
|
+
children: [
|
|
1322
|
+
item.icon && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "shrink-0 text-current", "aria-hidden": "true", children: item.icon }),
|
|
1323
|
+
item.label
|
|
1324
|
+
]
|
|
1325
|
+
}
|
|
1326
|
+
)
|
|
1327
|
+
] }, i))
|
|
1328
|
+
}
|
|
1329
|
+
)
|
|
1330
|
+
] });
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// src/components/pagination/pagination.tsx
|
|
1334
|
+
var import_lucide_react9 = require("lucide-react");
|
|
1335
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1336
|
+
function getPages(page, total, siblings) {
|
|
1337
|
+
const delta = siblings;
|
|
1338
|
+
const range = [];
|
|
1339
|
+
for (let i = Math.max(2, page - delta); i <= Math.min(total - 1, page + delta); i++) range.push(i);
|
|
1340
|
+
const pages = [1];
|
|
1341
|
+
const rangeFirst = range[0];
|
|
1342
|
+
const rangeLast = range[range.length - 1];
|
|
1343
|
+
if (rangeFirst !== void 0 && rangeFirst > 2) pages.push("\u2026");
|
|
1344
|
+
pages.push(...range);
|
|
1345
|
+
if (rangeLast !== void 0 && rangeLast < total - 1) pages.push("\u2026");
|
|
1346
|
+
if (total > 1) pages.push(total);
|
|
1347
|
+
return pages;
|
|
1348
|
+
}
|
|
1349
|
+
var btnBase = "inline-flex h-9 min-w-[36px] items-center justify-center rounded-md px-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 disabled:pointer-events-none disabled:opacity-40";
|
|
1350
|
+
function Pagination({
|
|
1351
|
+
page,
|
|
1352
|
+
totalPages,
|
|
1353
|
+
onChange,
|
|
1354
|
+
siblingCount = 1,
|
|
1355
|
+
className
|
|
1356
|
+
}) {
|
|
1357
|
+
if (totalPages <= 1) return null;
|
|
1358
|
+
const pages = getPages(page, totalPages, siblingCount);
|
|
1359
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1360
|
+
"nav",
|
|
1361
|
+
{
|
|
1362
|
+
"aria-label": "Pagination",
|
|
1363
|
+
className: cn("flex flex-wrap items-center justify-center gap-1", className),
|
|
1364
|
+
children: [
|
|
1365
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1366
|
+
"button",
|
|
1367
|
+
{
|
|
1368
|
+
onClick: () => onChange(page - 1),
|
|
1369
|
+
disabled: page === 1,
|
|
1370
|
+
"aria-label": "Previous page",
|
|
1371
|
+
className: cn(btnBase, "text-gray-600 hover:bg-gray-100"),
|
|
1372
|
+
children: [
|
|
1373
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react9.ChevronLeft, { size: 16, "aria-hidden": "true" }),
|
|
1374
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "ml-1 hidden sm:inline", children: "Prev" })
|
|
1375
|
+
]
|
|
1376
|
+
}
|
|
1377
|
+
),
|
|
1378
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "hidden sm:flex sm:items-center sm:gap-1", children: pages.map(
|
|
1379
|
+
(p, i) => p === "\u2026" ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "px-1 text-gray-400", children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react9.MoreHorizontal, { size: 16, "aria-hidden": "true" }) }, `ellipsis-${i}`) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1380
|
+
"button",
|
|
1381
|
+
{
|
|
1382
|
+
onClick: () => onChange(p),
|
|
1383
|
+
"aria-label": `Page ${p}`,
|
|
1384
|
+
"aria-current": p === page ? "page" : void 0,
|
|
1385
|
+
className: cn(
|
|
1386
|
+
btnBase,
|
|
1387
|
+
p === page ? "bg-blue-600 text-white hover:bg-blue-700" : "text-gray-600 hover:bg-gray-100"
|
|
1388
|
+
),
|
|
1389
|
+
children: p
|
|
1390
|
+
},
|
|
1391
|
+
p
|
|
1392
|
+
)
|
|
1393
|
+
) }),
|
|
1394
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("span", { className: "text-sm text-gray-500 sm:hidden", children: [
|
|
1395
|
+
page,
|
|
1396
|
+
" / ",
|
|
1397
|
+
totalPages
|
|
1398
|
+
] }),
|
|
1399
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1400
|
+
"button",
|
|
1401
|
+
{
|
|
1402
|
+
onClick: () => onChange(page + 1),
|
|
1403
|
+
disabled: page === totalPages,
|
|
1404
|
+
"aria-label": "Next page",
|
|
1405
|
+
className: cn(btnBase, "text-gray-600 hover:bg-gray-100"),
|
|
1406
|
+
children: [
|
|
1407
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "mr-1 hidden sm:inline", children: "Next" }),
|
|
1408
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_lucide_react9.ChevronRight, { size: 16, "aria-hidden": "true" })
|
|
1409
|
+
]
|
|
1410
|
+
}
|
|
1411
|
+
)
|
|
1412
|
+
]
|
|
1413
|
+
}
|
|
1414
|
+
);
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// src/components/tooltip/tooltip.tsx
|
|
1418
|
+
var React15 = __toESM(require("react"), 1);
|
|
1419
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1420
|
+
var sideClasses = {
|
|
1421
|
+
top: {
|
|
1422
|
+
wrapper: "bottom-full left-1/2 mb-2 -translate-x-1/2",
|
|
1423
|
+
tip: "top-full left-1/2 -translate-x-1/2 border-t-gray-800 border-x-transparent border-b-transparent"
|
|
1424
|
+
},
|
|
1425
|
+
bottom: {
|
|
1426
|
+
wrapper: "top-full left-1/2 mt-2 -translate-x-1/2",
|
|
1427
|
+
tip: "bottom-full left-1/2 -translate-x-1/2 border-b-gray-800 border-x-transparent border-t-transparent"
|
|
1428
|
+
},
|
|
1429
|
+
left: {
|
|
1430
|
+
wrapper: "right-full top-1/2 mr-2 -translate-y-1/2",
|
|
1431
|
+
tip: "left-full top-1/2 -translate-y-1/2 border-l-gray-800 border-y-transparent border-r-transparent"
|
|
1432
|
+
},
|
|
1433
|
+
right: {
|
|
1434
|
+
wrapper: "left-full top-1/2 ml-2 -translate-y-1/2",
|
|
1435
|
+
tip: "right-full top-1/2 -translate-y-1/2 border-r-gray-800 border-y-transparent border-l-transparent"
|
|
1436
|
+
}
|
|
1437
|
+
};
|
|
1438
|
+
function Tooltip({ content, side = "top", children, className }) {
|
|
1439
|
+
const id = React15.useId();
|
|
1440
|
+
const [visible, setVisible] = React15.useState(false);
|
|
1441
|
+
const show = () => setVisible(true);
|
|
1442
|
+
const hide = () => setVisible(false);
|
|
1443
|
+
const child = React15.cloneElement(children, {
|
|
1444
|
+
"aria-describedby": visible ? id : void 0,
|
|
1445
|
+
onMouseEnter: (e) => {
|
|
1446
|
+
show();
|
|
1447
|
+
children.props.onMouseEnter?.(e);
|
|
1448
|
+
},
|
|
1449
|
+
onMouseLeave: (e) => {
|
|
1450
|
+
hide();
|
|
1451
|
+
children.props.onMouseLeave?.(e);
|
|
1452
|
+
},
|
|
1453
|
+
onFocus: (e) => {
|
|
1454
|
+
show();
|
|
1455
|
+
children.props.onFocus?.(e);
|
|
1456
|
+
},
|
|
1457
|
+
onBlur: (e) => {
|
|
1458
|
+
hide();
|
|
1459
|
+
children.props.onBlur?.(e);
|
|
1460
|
+
}
|
|
1461
|
+
});
|
|
1462
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: "relative inline-flex", children: [
|
|
1463
|
+
child,
|
|
1464
|
+
visible && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
1465
|
+
"span",
|
|
1466
|
+
{
|
|
1467
|
+
id,
|
|
1468
|
+
role: "tooltip",
|
|
1469
|
+
className: cn(
|
|
1470
|
+
"pointer-events-none absolute z-50 whitespace-nowrap rounded bg-gray-800 px-2 py-1 text-xs text-white shadow-sm",
|
|
1471
|
+
sideClasses[side].wrapper,
|
|
1472
|
+
className
|
|
1473
|
+
),
|
|
1474
|
+
children: [
|
|
1475
|
+
content,
|
|
1476
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1477
|
+
"span",
|
|
1478
|
+
{
|
|
1479
|
+
"aria-hidden": "true",
|
|
1480
|
+
className: cn(
|
|
1481
|
+
"absolute h-0 w-0 border-4",
|
|
1482
|
+
sideClasses[side].tip
|
|
1483
|
+
)
|
|
1484
|
+
}
|
|
1485
|
+
)
|
|
1486
|
+
]
|
|
1487
|
+
}
|
|
1488
|
+
)
|
|
1489
|
+
] });
|
|
1490
|
+
}
|
|
1491
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1492
|
+
0 && (module.exports = {
|
|
1493
|
+
Alert,
|
|
1494
|
+
AppShell,
|
|
1495
|
+
Badge,
|
|
1496
|
+
Button,
|
|
1497
|
+
Card,
|
|
1498
|
+
CardContent,
|
|
1499
|
+
CardDescription,
|
|
1500
|
+
CardFooter,
|
|
1501
|
+
CardHeader,
|
|
1502
|
+
CardTitle,
|
|
1503
|
+
Checkbox,
|
|
1504
|
+
ConfirmDialog,
|
|
1505
|
+
Drawer,
|
|
1506
|
+
DropdownMenu,
|
|
1507
|
+
EmptyState,
|
|
1508
|
+
FormField,
|
|
1509
|
+
Input,
|
|
1510
|
+
Label,
|
|
1511
|
+
Modal,
|
|
1512
|
+
PageHeader,
|
|
1513
|
+
Pagination,
|
|
1514
|
+
Select,
|
|
1515
|
+
Sidebar,
|
|
1516
|
+
Spinner,
|
|
1517
|
+
TabPanel,
|
|
1518
|
+
Table,
|
|
1519
|
+
TableBody,
|
|
1520
|
+
TableCell,
|
|
1521
|
+
TableEmptyState,
|
|
1522
|
+
TableHead,
|
|
1523
|
+
TableHeader,
|
|
1524
|
+
TableRow,
|
|
1525
|
+
TableToolbar,
|
|
1526
|
+
Tabs,
|
|
1527
|
+
Textarea,
|
|
1528
|
+
ToastProvider,
|
|
1529
|
+
Tooltip,
|
|
1530
|
+
Topbar,
|
|
1531
|
+
cn,
|
|
1532
|
+
useToast
|
|
1533
|
+
});
|
|
1534
|
+
//# sourceMappingURL=index.cjs.map
|