@pichetch08/trip-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/dist/accordion.d.ts +18 -0
- package/dist/accordion.js +76 -0
- package/dist/agreement-modal.d.ts +16 -0
- package/dist/agreement-modal.js +67 -0
- package/dist/alert.d.ts +13 -0
- package/dist/alert.js +47 -0
- package/dist/auth-hero.d.ts +7 -0
- package/dist/auth-hero.js +63 -0
- package/dist/avatar.d.ts +21 -0
- package/dist/avatar.js +114 -0
- package/dist/badge.d.ts +13 -0
- package/dist/badge.js +36 -0
- package/dist/banner.d.ts +14 -0
- package/dist/banner.js +57 -0
- package/dist/breadcrumb.d.ts +15 -0
- package/dist/breadcrumb.js +37 -0
- package/dist/button.d.ts +15 -0
- package/dist/button.js +81 -0
- package/dist/card.d.ts +30 -0
- package/dist/card.js +66 -0
- package/dist/change-summary-modal.d.ts +35 -0
- package/dist/change-summary-modal.js +128 -0
- package/dist/channel-badge.d.ts +8 -0
- package/dist/channel-badge.js +17 -0
- package/dist/checkbox.d.ts +28 -0
- package/dist/checkbox.js +108 -0
- package/dist/chunk-ORMEWXMH.js +37 -0
- package/dist/color-picker.d.ts +15 -0
- package/dist/color-picker.js +159 -0
- package/dist/confirm-dialog.d.ts +23 -0
- package/dist/confirm-dialog.js +108 -0
- package/dist/copy-button.d.ts +13 -0
- package/dist/copy-button.js +69 -0
- package/dist/dashed-add-button.d.ts +8 -0
- package/dist/dashed-add-button.js +24 -0
- package/dist/data-table.d.ts +27 -0
- package/dist/data-table.js +152 -0
- package/dist/date-picker.d.ts +19 -0
- package/dist/date-picker.js +234 -0
- package/dist/date-range-picker.d.ts +25 -0
- package/dist/date-range-picker.js +456 -0
- package/dist/dev-auto-fill.d.ts +12 -0
- package/dist/dev-auto-fill.js +22 -0
- package/dist/divider.d.ts +10 -0
- package/dist/divider.js +44 -0
- package/dist/drawer.d.ts +16 -0
- package/dist/drawer.js +111 -0
- package/dist/dropdown-menu.d.ts +20 -0
- package/dist/dropdown-menu.js +94 -0
- package/dist/empty-state.d.ts +13 -0
- package/dist/empty-state.js +24 -0
- package/dist/file-upload.d.ts +32 -0
- package/dist/file-upload.js +212 -0
- package/dist/filter-tabs.d.ts +16 -0
- package/dist/filter-tabs.js +30 -0
- package/dist/footer-action-bar.d.ts +21 -0
- package/dist/footer-action-bar.js +95 -0
- package/dist/form-input.d.ts +16 -0
- package/dist/form-input.js +58 -0
- package/dist/form-textarea.d.ts +13 -0
- package/dist/form-textarea.js +41 -0
- package/dist/icon-button.d.ts +12 -0
- package/dist/icon-button.js +54 -0
- package/dist/icon-picker.d.ts +15 -0
- package/dist/icon-picker.js +311 -0
- package/dist/icon-wrapper.d.ts +15 -0
- package/dist/icon-wrapper.js +52 -0
- package/dist/image-upload.d.ts +24 -0
- package/dist/image-upload.js +122 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.js +155 -0
- package/dist/kbd.d.ts +15 -0
- package/dist/kbd.js +27 -0
- package/dist/mobile-preview.d.ts +36 -0
- package/dist/mobile-preview.js +167 -0
- package/dist/modal.d.ts +19 -0
- package/dist/modal.js +110 -0
- package/dist/multi-select.d.ts +30 -0
- package/dist/multi-select.js +261 -0
- package/dist/number-input.d.ts +21 -0
- package/dist/number-input.js +129 -0
- package/dist/otp-input.d.ts +13 -0
- package/dist/otp-input.js +114 -0
- package/dist/page-header.d.ts +15 -0
- package/dist/page-header.js +43 -0
- package/dist/page-state.d.ts +14 -0
- package/dist/page-state.js +29 -0
- package/dist/pagination.d.ts +20 -0
- package/dist/pagination.js +87 -0
- package/dist/popover.d.ts +11 -0
- package/dist/popover.js +70 -0
- package/dist/preview-drawer.d.ts +33 -0
- package/dist/preview-drawer.js +74 -0
- package/dist/progress-bar.d.ts +15 -0
- package/dist/progress-bar.js +56 -0
- package/dist/qr-code-display.d.ts +10 -0
- package/dist/qr-code-display.js +43 -0
- package/dist/radio-group.d.ts +19 -0
- package/dist/radio-group.js +78 -0
- package/dist/rating.d.ts +12 -0
- package/dist/rating.js +123 -0
- package/dist/rich-editor.d.ts +13 -0
- package/dist/rich-editor.js +97 -0
- package/dist/search-bar.d.ts +14 -0
- package/dist/search-bar.js +64 -0
- package/dist/section-header.d.ts +12 -0
- package/dist/section-header.js +41 -0
- package/dist/segmented-control.d.ts +24 -0
- package/dist/segmented-control.js +38 -0
- package/dist/select-picker.d.ts +24 -0
- package/dist/select-picker.js +157 -0
- package/dist/skeleton.d.ts +14 -0
- package/dist/skeleton.js +53 -0
- package/dist/slider.d.ts +17 -0
- package/dist/slider.js +151 -0
- package/dist/spinner.d.ts +13 -0
- package/dist/spinner.js +38 -0
- package/dist/stat-card.d.ts +20 -0
- package/dist/stat-card.js +87 -0
- package/dist/stats-summary.d.ts +13 -0
- package/dist/stats-summary.js +28 -0
- package/dist/status-badge.d.ts +19 -0
- package/dist/status-badge.js +41 -0
- package/dist/stepper.d.ts +12 -0
- package/dist/stepper.js +89 -0
- package/dist/tabs.d.ts +18 -0
- package/dist/tabs.js +70 -0
- package/dist/tag.d.ts +23 -0
- package/dist/tag.js +158 -0
- package/dist/time-picker.d.ts +19 -0
- package/dist/time-picker.js +222 -0
- package/dist/timeline.d.ts +15 -0
- package/dist/timeline.js +49 -0
- package/dist/toast.d.ts +18 -0
- package/dist/toast.js +108 -0
- package/dist/toggle-switch.d.ts +12 -0
- package/dist/toggle-switch.js +34 -0
- package/dist/tooltip.d.ts +9 -0
- package/dist/tooltip.js +69 -0
- package/dist/trip-day-map-lazy.d.ts +15 -0
- package/dist/trip-day-map-lazy.js +16 -0
- package/dist/trip-day-map.d.ts +15 -0
- package/dist/trip-day-map.js +62 -0
- package/package.json +73 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
__spreadProps,
|
|
4
|
+
__spreadValues
|
|
5
|
+
} from "./chunk-ORMEWXMH.js";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { useState, useRef } from "react";
|
|
8
|
+
const DEFAULT_PRESETS = [
|
|
9
|
+
"#ef4444",
|
|
10
|
+
"#f97316",
|
|
11
|
+
"#eab308",
|
|
12
|
+
"#22c55e",
|
|
13
|
+
"#14b8a6",
|
|
14
|
+
"#3b82f6",
|
|
15
|
+
"#8b5cf6",
|
|
16
|
+
"#ec4899",
|
|
17
|
+
"#64748b",
|
|
18
|
+
"#000000",
|
|
19
|
+
"#ffffff",
|
|
20
|
+
"#f1f5f9"
|
|
21
|
+
];
|
|
22
|
+
function isValidHex(value) {
|
|
23
|
+
return /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value);
|
|
24
|
+
}
|
|
25
|
+
function ColorPicker({
|
|
26
|
+
label,
|
|
27
|
+
value,
|
|
28
|
+
onChange,
|
|
29
|
+
presets = DEFAULT_PRESETS,
|
|
30
|
+
showHexInput = true,
|
|
31
|
+
showPalette = true,
|
|
32
|
+
error,
|
|
33
|
+
required,
|
|
34
|
+
hexErrorText = "\u0E01\u0E23\u0E38\u0E13\u0E32\u0E01\u0E23\u0E2D\u0E01 hex color \u0E17\u0E35\u0E48\u0E16\u0E39\u0E01\u0E15\u0E49\u0E2D\u0E07 \u0E40\u0E0A\u0E48\u0E19 #3b82f6",
|
|
35
|
+
paletteLabel = "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E08\u0E32\u0E19\u0E2A\u0E35"
|
|
36
|
+
}) {
|
|
37
|
+
const [inputValue, setInputValue] = useState(value);
|
|
38
|
+
const [inputError, setInputError] = useState(null);
|
|
39
|
+
const inputRef = useRef(null);
|
|
40
|
+
const nativePickerRef = useRef(null);
|
|
41
|
+
const applyColor = (hex) => {
|
|
42
|
+
setInputValue(hex);
|
|
43
|
+
setInputError(null);
|
|
44
|
+
onChange(hex);
|
|
45
|
+
};
|
|
46
|
+
const handleInputChange = (e) => {
|
|
47
|
+
const raw = e.target.value;
|
|
48
|
+
setInputValue(raw);
|
|
49
|
+
const normalized = raw.startsWith("#") ? raw : `#${raw}`;
|
|
50
|
+
if (isValidHex(normalized)) {
|
|
51
|
+
setInputError(null);
|
|
52
|
+
onChange(normalized);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
const handleInputBlur = () => {
|
|
56
|
+
const normalized = inputValue.startsWith("#") ? inputValue : `#${inputValue}`;
|
|
57
|
+
if (!isValidHex(normalized)) {
|
|
58
|
+
setInputError(hexErrorText);
|
|
59
|
+
setInputValue(value);
|
|
60
|
+
} else {
|
|
61
|
+
setInputError(null);
|
|
62
|
+
onChange(normalized);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const displayError = error != null ? error : inputError;
|
|
66
|
+
const inputId = label ? `colorpicker-${label.replace(/\s+/g, "-").toLowerCase()}` : void 0;
|
|
67
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
68
|
+
label && /* @__PURE__ */ jsxs("label", { htmlFor: inputId, className: "text-sm font-medium text-on-surface", children: [
|
|
69
|
+
label,
|
|
70
|
+
required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-1", children: "*" })
|
|
71
|
+
] }),
|
|
72
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-6 gap-2", children: presets.map((hex) => {
|
|
73
|
+
const isSelected = value.toLowerCase() === hex.toLowerCase();
|
|
74
|
+
return /* @__PURE__ */ jsx(
|
|
75
|
+
"button",
|
|
76
|
+
{
|
|
77
|
+
type: "button",
|
|
78
|
+
"aria-label": hex,
|
|
79
|
+
onClick: () => applyColor(hex),
|
|
80
|
+
className: [
|
|
81
|
+
"w-8 h-8 rounded-full cursor-pointer border-2 transition-all hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 active:scale-95",
|
|
82
|
+
isSelected ? "border-primary ring-2 ring-primary/30 scale-110" : "border-transparent hover:border-outline-variant"
|
|
83
|
+
].join(" "),
|
|
84
|
+
style: { backgroundColor: hex }
|
|
85
|
+
},
|
|
86
|
+
hex
|
|
87
|
+
);
|
|
88
|
+
}) }),
|
|
89
|
+
showHexInput && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
90
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
91
|
+
/* @__PURE__ */ jsx(
|
|
92
|
+
"div",
|
|
93
|
+
{
|
|
94
|
+
className: "w-10 h-10 rounded-xl border border-outline-variant/30 shadow-sm shrink-0 cursor-pointer hover:ring-2 hover:ring-primary/30 transition-all",
|
|
95
|
+
style: { backgroundColor: isValidHex(value) ? value : "#ffffff" },
|
|
96
|
+
"aria-hidden": "true",
|
|
97
|
+
onClick: () => {
|
|
98
|
+
var _a;
|
|
99
|
+
return showPalette && ((_a = nativePickerRef.current) == null ? void 0 : _a.click());
|
|
100
|
+
},
|
|
101
|
+
title: showPalette ? paletteLabel : void 0
|
|
102
|
+
}
|
|
103
|
+
),
|
|
104
|
+
showPalette && /* @__PURE__ */ jsx(
|
|
105
|
+
"input",
|
|
106
|
+
{
|
|
107
|
+
ref: nativePickerRef,
|
|
108
|
+
type: "color",
|
|
109
|
+
value: isValidHex(value) ? value : "#3b82f6",
|
|
110
|
+
onChange: (e) => applyColor(e.target.value),
|
|
111
|
+
className: "absolute inset-0 opacity-0 w-full h-full cursor-pointer",
|
|
112
|
+
tabIndex: -1,
|
|
113
|
+
"aria-label": paletteLabel
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
] }),
|
|
117
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 relative", children: [
|
|
118
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center border border-outline-variant/40 rounded-xl overflow-hidden bg-surface-container-low focus-within:ring-2 focus-within:ring-primary/30 focus-within:border-primary", children: [
|
|
119
|
+
/* @__PURE__ */ jsx("span", { className: "pl-3 pr-1 text-sm font-mono text-on-surface-variant select-none", children: "#" }),
|
|
120
|
+
/* @__PURE__ */ jsx(
|
|
121
|
+
"input",
|
|
122
|
+
{
|
|
123
|
+
ref: inputRef,
|
|
124
|
+
id: inputId,
|
|
125
|
+
type: "text",
|
|
126
|
+
value: inputValue.startsWith("#") ? inputValue.slice(1) : inputValue,
|
|
127
|
+
onChange: (e) => handleInputChange(__spreadProps(__spreadValues({}, e), {
|
|
128
|
+
target: __spreadProps(__spreadValues({}, e.target), { value: `#${e.target.value}` })
|
|
129
|
+
})),
|
|
130
|
+
onBlur: handleInputBlur,
|
|
131
|
+
placeholder: "3b82f6",
|
|
132
|
+
maxLength: 7,
|
|
133
|
+
className: "flex-1 py-2 pr-3 text-sm font-mono bg-transparent outline-none text-on-surface placeholder:text-on-surface-variant/40",
|
|
134
|
+
"aria-invalid": !!displayError
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
] }),
|
|
138
|
+
displayError && /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-red-500", children: displayError })
|
|
139
|
+
] }),
|
|
140
|
+
showPalette && /* @__PURE__ */ jsx(
|
|
141
|
+
"button",
|
|
142
|
+
{
|
|
143
|
+
type: "button",
|
|
144
|
+
onClick: () => {
|
|
145
|
+
var _a;
|
|
146
|
+
return (_a = nativePickerRef.current) == null ? void 0 : _a.click();
|
|
147
|
+
},
|
|
148
|
+
className: "w-10 h-10 rounded-xl flex items-center justify-center bg-surface-container-low border border-outline-variant/40 text-on-surface-variant hover:bg-surface-container hover:text-on-surface transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 shrink-0",
|
|
149
|
+
"aria-label": paletteLabel,
|
|
150
|
+
title: paletteLabel,
|
|
151
|
+
children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", style: { fontSize: "20px" }, children: "palette" })
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
] })
|
|
155
|
+
] });
|
|
156
|
+
}
|
|
157
|
+
export {
|
|
158
|
+
ColorPicker
|
|
159
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
interface ConfirmDialogProps {
|
|
2
|
+
open: boolean;
|
|
3
|
+
/** Called when dialog should close. Accepts both staff's onOpenChange and admin's onClose patterns. */
|
|
4
|
+
onOpenChange?: (open: boolean) => void;
|
|
5
|
+
onClose?: () => void;
|
|
6
|
+
onConfirm: () => void;
|
|
7
|
+
title: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
confirmLabel?: string;
|
|
10
|
+
/** Alias for confirmLabel (backward compat). */
|
|
11
|
+
confirmText?: string;
|
|
12
|
+
cancelLabel?: string;
|
|
13
|
+
/** Alias for cancelLabel (backward compat). */
|
|
14
|
+
cancelText?: string;
|
|
15
|
+
/** "danger" maps to isDangerous=true. */
|
|
16
|
+
variant?: "danger" | "default";
|
|
17
|
+
/** Alias for variant="danger" (backward compat). */
|
|
18
|
+
isDangerous?: boolean;
|
|
19
|
+
isLoading?: boolean;
|
|
20
|
+
}
|
|
21
|
+
declare function ConfirmDialog({ open, onOpenChange, onClose, onConfirm, title, description, confirmLabel, confirmText, cancelLabel, cancelText, variant, isDangerous, isLoading, }: ConfirmDialogProps): React.ReactNode;
|
|
22
|
+
|
|
23
|
+
export { ConfirmDialog };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import "./chunk-ORMEWXMH.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { useEffect, useRef } from "react";
|
|
5
|
+
import { Button } from "./button";
|
|
6
|
+
function ConfirmDialog({
|
|
7
|
+
open,
|
|
8
|
+
onOpenChange,
|
|
9
|
+
onClose,
|
|
10
|
+
onConfirm,
|
|
11
|
+
title,
|
|
12
|
+
description,
|
|
13
|
+
confirmLabel,
|
|
14
|
+
confirmText = "\u0E22\u0E37\u0E19\u0E22\u0E31\u0E19",
|
|
15
|
+
cancelLabel,
|
|
16
|
+
cancelText = "\u0E22\u0E01\u0E40\u0E25\u0E34\u0E01",
|
|
17
|
+
variant = "default",
|
|
18
|
+
isDangerous = false,
|
|
19
|
+
isLoading = false
|
|
20
|
+
}) {
|
|
21
|
+
const dialogRef = useRef(null);
|
|
22
|
+
const cancelRef = useRef(null);
|
|
23
|
+
const confirmBtn = confirmLabel != null ? confirmLabel : confirmText;
|
|
24
|
+
const cancelBtn = cancelLabel != null ? cancelLabel : cancelText;
|
|
25
|
+
const isDanger = isDangerous || variant === "danger";
|
|
26
|
+
const handleClose = () => {
|
|
27
|
+
onOpenChange == null ? void 0 : onOpenChange(false);
|
|
28
|
+
onClose == null ? void 0 : onClose();
|
|
29
|
+
};
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
var _a;
|
|
32
|
+
if (!open) return;
|
|
33
|
+
(_a = cancelRef.current) == null ? void 0 : _a.focus();
|
|
34
|
+
const onKey = (e) => {
|
|
35
|
+
if (e.key === "Escape") {
|
|
36
|
+
e.preventDefault();
|
|
37
|
+
handleClose();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (e.key !== "Tab" || !dialogRef.current) return;
|
|
41
|
+
const els = Array.from(
|
|
42
|
+
dialogRef.current.querySelectorAll(
|
|
43
|
+
'button:not([disabled]), [tabindex]:not([tabindex="-1"])'
|
|
44
|
+
)
|
|
45
|
+
);
|
|
46
|
+
if (els.length === 0) return;
|
|
47
|
+
const first = els[0], last = els[els.length - 1];
|
|
48
|
+
if (e.shiftKey && document.activeElement === first) {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
last.focus();
|
|
51
|
+
} else if (!e.shiftKey && document.activeElement === last) {
|
|
52
|
+
e.preventDefault();
|
|
53
|
+
first.focus();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
window.addEventListener("keydown", onKey);
|
|
57
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
58
|
+
}, [open]);
|
|
59
|
+
if (!open) return null;
|
|
60
|
+
return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-100 flex items-center justify-center p-4", children: [
|
|
61
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/50 cursor-pointer", onClick: handleClose }),
|
|
62
|
+
/* @__PURE__ */ jsxs(
|
|
63
|
+
"div",
|
|
64
|
+
{
|
|
65
|
+
ref: dialogRef,
|
|
66
|
+
role: "alertdialog",
|
|
67
|
+
"aria-modal": "true",
|
|
68
|
+
"aria-labelledby": "confirm-dialog-title",
|
|
69
|
+
"aria-describedby": description ? "confirm-dialog-desc" : void 0,
|
|
70
|
+
className: "relative bg-surface rounded-2xl shadow-2xl w-full max-w-sm p-6 space-y-4",
|
|
71
|
+
children: [
|
|
72
|
+
/* @__PURE__ */ jsx("h3", { id: "confirm-dialog-title", className: "text-lg font-bold text-on-surface", children: title }),
|
|
73
|
+
description && /* @__PURE__ */ jsx("p", { id: "confirm-dialog-desc", className: "text-sm text-on-surface-variant whitespace-pre-line", children: description }),
|
|
74
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-3 pt-2", children: [
|
|
75
|
+
/* @__PURE__ */ jsx(
|
|
76
|
+
Button,
|
|
77
|
+
{
|
|
78
|
+
ref: cancelRef,
|
|
79
|
+
variant: "outline",
|
|
80
|
+
className: "flex-1",
|
|
81
|
+
onClick: handleClose,
|
|
82
|
+
disabled: isLoading,
|
|
83
|
+
children: cancelBtn
|
|
84
|
+
}
|
|
85
|
+
),
|
|
86
|
+
/* @__PURE__ */ jsx(
|
|
87
|
+
Button,
|
|
88
|
+
{
|
|
89
|
+
variant: isDanger ? "danger" : "primary",
|
|
90
|
+
className: "flex-1",
|
|
91
|
+
onClick: () => {
|
|
92
|
+
onConfirm();
|
|
93
|
+
handleClose();
|
|
94
|
+
},
|
|
95
|
+
disabled: isLoading,
|
|
96
|
+
loading: isLoading,
|
|
97
|
+
children: confirmBtn
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
] })
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
] });
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
ConfirmDialog
|
|
108
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface CopyButtonProps {
|
|
4
|
+
text: string;
|
|
5
|
+
label?: string;
|
|
6
|
+
copiedLabel?: string;
|
|
7
|
+
variant?: "button" | "icon";
|
|
8
|
+
size?: "sm" | "md";
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
declare function CopyButton({ text, label, copiedLabel, variant, size, className, }: CopyButtonProps): react_jsx_runtime.JSX.Element;
|
|
12
|
+
|
|
13
|
+
export { CopyButton };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import "./chunk-ORMEWXMH.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { useState, useRef } from "react";
|
|
5
|
+
function CopyButton({
|
|
6
|
+
text,
|
|
7
|
+
label = "\u0E04\u0E31\u0E14\u0E25\u0E2D\u0E01",
|
|
8
|
+
copiedLabel = "\u0E04\u0E31\u0E14\u0E25\u0E2D\u0E01\u0E41\u0E25\u0E49\u0E27!",
|
|
9
|
+
variant = "button",
|
|
10
|
+
size = "md",
|
|
11
|
+
className = ""
|
|
12
|
+
}) {
|
|
13
|
+
const [copied, setCopied] = useState(false);
|
|
14
|
+
const timeoutRef = useRef(null);
|
|
15
|
+
const handleCopy = async () => {
|
|
16
|
+
try {
|
|
17
|
+
await navigator.clipboard.writeText(text);
|
|
18
|
+
setCopied(true);
|
|
19
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
20
|
+
timeoutRef.current = setTimeout(() => setCopied(false), 2e3);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
if (variant === "icon") {
|
|
25
|
+
return /* @__PURE__ */ jsx(
|
|
26
|
+
"button",
|
|
27
|
+
{
|
|
28
|
+
type: "button",
|
|
29
|
+
onClick: handleCopy,
|
|
30
|
+
"aria-label": copied ? copiedLabel : label,
|
|
31
|
+
className: [
|
|
32
|
+
"p-2 rounded-lg transition-colors active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30",
|
|
33
|
+
copied ? "text-emerald-600 bg-emerald-100" : "text-on-surface-variant hover:text-on-surface hover:bg-surface-container",
|
|
34
|
+
className
|
|
35
|
+
].filter(Boolean).join(" "),
|
|
36
|
+
children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", style: { fontSize: "20px" }, "aria-hidden": "true", children: copied ? "check" : "content_copy" })
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
const sizeClasses = size === "sm" ? "px-3 py-1.5 text-xs rounded-lg" : "px-4 py-2 text-sm rounded-xl";
|
|
41
|
+
return /* @__PURE__ */ jsxs(
|
|
42
|
+
"button",
|
|
43
|
+
{
|
|
44
|
+
type: "button",
|
|
45
|
+
onClick: handleCopy,
|
|
46
|
+
className: [
|
|
47
|
+
"inline-flex items-center gap-1.5 font-medium transition-colors active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30",
|
|
48
|
+
sizeClasses,
|
|
49
|
+
copied ? "bg-emerald-100 text-emerald-700" : "bg-surface-container text-on-surface-variant hover:bg-surface-container-high",
|
|
50
|
+
className
|
|
51
|
+
].filter(Boolean).join(" "),
|
|
52
|
+
children: [
|
|
53
|
+
/* @__PURE__ */ jsx(
|
|
54
|
+
"span",
|
|
55
|
+
{
|
|
56
|
+
className: "material-symbols-outlined",
|
|
57
|
+
style: { fontSize: size === "sm" ? "14px" : "16px" },
|
|
58
|
+
"aria-hidden": "true",
|
|
59
|
+
children: copied ? "check" : "content_copy"
|
|
60
|
+
}
|
|
61
|
+
),
|
|
62
|
+
copied ? copiedLabel : label
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
CopyButton
|
|
69
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import "./chunk-ORMEWXMH.js";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
function DashedAddButton({
|
|
5
|
+
children,
|
|
6
|
+
onClick,
|
|
7
|
+
className = ""
|
|
8
|
+
}) {
|
|
9
|
+
return /* @__PURE__ */ jsxs(
|
|
10
|
+
"button",
|
|
11
|
+
{
|
|
12
|
+
type: "button",
|
|
13
|
+
onClick,
|
|
14
|
+
className: `w-full py-4 border-2 border-dashed border-primary/30 rounded-xl text-xs font-bold text-primary uppercase tracking-widest hover:bg-primary/5 transition-colors flex items-center justify-center gap-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary ${className}`,
|
|
15
|
+
children: [
|
|
16
|
+
/* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-sm", "aria-hidden": "true", children: "add_circle" }),
|
|
17
|
+
children
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
export {
|
|
23
|
+
DashedAddButton
|
|
24
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface Column<T> {
|
|
4
|
+
key: keyof T;
|
|
5
|
+
label: string;
|
|
6
|
+
render?: (value: any, item: T) => React.ReactNode;
|
|
7
|
+
sortable?: boolean;
|
|
8
|
+
width?: string;
|
|
9
|
+
}
|
|
10
|
+
interface DataTableProps<T> {
|
|
11
|
+
columns: Column<T>[];
|
|
12
|
+
data: T[];
|
|
13
|
+
rowKey: keyof T;
|
|
14
|
+
onRowClick?: (item: T) => void;
|
|
15
|
+
actions?: (item: T) => React.ReactNode;
|
|
16
|
+
isLoading?: boolean;
|
|
17
|
+
emptyMessage?: string;
|
|
18
|
+
loadingText?: string;
|
|
19
|
+
actionsLabel?: string;
|
|
20
|
+
selectAllLabel?: string;
|
|
21
|
+
selectRowLabel?: (key: string) => string;
|
|
22
|
+
selectable?: boolean;
|
|
23
|
+
onSelectionChange?: (selected: any[]) => void;
|
|
24
|
+
}
|
|
25
|
+
declare function DataTable<T>({ columns, data, rowKey, onRowClick, actions, isLoading, emptyMessage, loadingText, actionsLabel, selectAllLabel, selectRowLabel, selectable, onSelectionChange, }: DataTableProps<T>): react_jsx_runtime.JSX.Element;
|
|
26
|
+
|
|
27
|
+
export { type Column, DataTable };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import {
|
|
3
|
+
__spreadProps,
|
|
4
|
+
__spreadValues
|
|
5
|
+
} from "./chunk-ORMEWXMH.js";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
function DataTable({
|
|
9
|
+
columns,
|
|
10
|
+
data,
|
|
11
|
+
rowKey,
|
|
12
|
+
onRowClick,
|
|
13
|
+
actions,
|
|
14
|
+
isLoading = false,
|
|
15
|
+
emptyMessage = "\u0E44\u0E21\u0E48\u0E1E\u0E1A\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25",
|
|
16
|
+
loadingText = "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E42\u0E2B\u0E25\u0E14...",
|
|
17
|
+
actionsLabel = "\u0E08\u0E31\u0E14\u0E01\u0E32\u0E23",
|
|
18
|
+
selectAllLabel = "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
|
|
19
|
+
selectRowLabel = (key) => `\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E41\u0E16\u0E27\u0E17\u0E35\u0E48 ${key}`,
|
|
20
|
+
selectable = false,
|
|
21
|
+
onSelectionChange
|
|
22
|
+
}) {
|
|
23
|
+
const [sortKey, setSortKey] = useState(null);
|
|
24
|
+
const [sortDirection, setSortDirection] = useState("asc");
|
|
25
|
+
const [selectedRows, setSelectedRows] = useState(/* @__PURE__ */ new Set());
|
|
26
|
+
const handleSort = (key) => {
|
|
27
|
+
if (sortKey === key) {
|
|
28
|
+
setSortDirection(sortDirection === "asc" ? "desc" : "asc");
|
|
29
|
+
} else {
|
|
30
|
+
setSortKey(key);
|
|
31
|
+
setSortDirection("asc");
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const handleSelectAll = () => {
|
|
35
|
+
if (selectedRows.size === data.length) {
|
|
36
|
+
setSelectedRows(/* @__PURE__ */ new Set());
|
|
37
|
+
onSelectionChange == null ? void 0 : onSelectionChange([]);
|
|
38
|
+
} else {
|
|
39
|
+
const newSelected = new Set(data.map((item) => item[rowKey]));
|
|
40
|
+
setSelectedRows(newSelected);
|
|
41
|
+
onSelectionChange == null ? void 0 : onSelectionChange(Array.from(newSelected));
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const handleSelectRow = (item) => {
|
|
45
|
+
const key = item[rowKey];
|
|
46
|
+
const newSelected = new Set(selectedRows);
|
|
47
|
+
if (newSelected.has(key)) {
|
|
48
|
+
newSelected.delete(key);
|
|
49
|
+
} else {
|
|
50
|
+
newSelected.add(key);
|
|
51
|
+
}
|
|
52
|
+
setSelectedRows(newSelected);
|
|
53
|
+
onSelectionChange == null ? void 0 : onSelectionChange(Array.from(newSelected));
|
|
54
|
+
};
|
|
55
|
+
const normalizedColumns = columns.map((col) => {
|
|
56
|
+
var _a;
|
|
57
|
+
return __spreadProps(__spreadValues({}, col), {
|
|
58
|
+
sortable: (_a = col.sortable) != null ? _a : true
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
const sortedData = sortKey ? [...data].sort((a, b) => {
|
|
62
|
+
const aVal = a[sortKey];
|
|
63
|
+
const bVal = b[sortKey];
|
|
64
|
+
if (aVal < bVal) return sortDirection === "asc" ? -1 : 1;
|
|
65
|
+
if (aVal > bVal) return sortDirection === "asc" ? 1 : -1;
|
|
66
|
+
return 0;
|
|
67
|
+
}) : data;
|
|
68
|
+
if (isLoading) {
|
|
69
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full p-8 text-center", role: "status", "aria-live": "polite", "aria-label": loadingText, children: [
|
|
70
|
+
/* @__PURE__ */ jsx("span", { className: "material-symbols-outlined animate-spin inline-block", "aria-hidden": "true", children: "progress_activity" }),
|
|
71
|
+
/* @__PURE__ */ jsx("p", { className: "mt-2 text-on-surface-variant", children: loadingText })
|
|
72
|
+
] });
|
|
73
|
+
}
|
|
74
|
+
if (!data || data.length === 0) {
|
|
75
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full p-8 text-center", children: [
|
|
76
|
+
/* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-4xl text-outline mb-2 block", "aria-hidden": "true", children: "inbox" }),
|
|
77
|
+
/* @__PURE__ */ jsx("p", { className: "text-on-surface-variant", children: emptyMessage })
|
|
78
|
+
] });
|
|
79
|
+
}
|
|
80
|
+
const colCount = columns.length + (selectable ? 1 : 0) + (actions ? 1 : 0);
|
|
81
|
+
return /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-left text-sm", "aria-colcount": colCount, children: [
|
|
82
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-surface-container-low text-xs uppercase tracking-wider text-on-surface-variant font-bold", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
83
|
+
selectable && /* @__PURE__ */ jsx("th", { className: "px-6 py-4 w-12", scope: "col", children: /* @__PURE__ */ jsx(
|
|
84
|
+
"input",
|
|
85
|
+
{
|
|
86
|
+
type: "checkbox",
|
|
87
|
+
checked: selectedRows.size === data.length,
|
|
88
|
+
onChange: handleSelectAll,
|
|
89
|
+
"aria-label": selectAllLabel,
|
|
90
|
+
className: "w-4 h-4 rounded border-outline-variant text-primary accent-primary cursor-pointer"
|
|
91
|
+
}
|
|
92
|
+
) }),
|
|
93
|
+
normalizedColumns.map((col) => /* @__PURE__ */ jsx(
|
|
94
|
+
"th",
|
|
95
|
+
{
|
|
96
|
+
scope: "col",
|
|
97
|
+
"aria-sort": col.sortable ? sortKey === col.key ? sortDirection === "asc" ? "ascending" : "descending" : "none" : void 0,
|
|
98
|
+
className: `px-6 py-4 ${col.width || ""} ${col.sortable ? "cursor-pointer hover:bg-surface-container" : ""}`,
|
|
99
|
+
onClick: () => col.sortable && handleSort(col.key),
|
|
100
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
101
|
+
col.label,
|
|
102
|
+
col.sortable && sortKey === col.key && /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-xs leading-none", "aria-hidden": "true", children: sortDirection === "asc" ? "arrow_upward" : "arrow_downward" })
|
|
103
|
+
] })
|
|
104
|
+
},
|
|
105
|
+
String(col.key)
|
|
106
|
+
)),
|
|
107
|
+
actions && /* @__PURE__ */ jsx("th", { className: "px-6 py-4 text-right", scope: "col", children: actionsLabel })
|
|
108
|
+
] }) }),
|
|
109
|
+
/* @__PURE__ */ jsx("tbody", { children: sortedData.map((item) => /* @__PURE__ */ jsxs(
|
|
110
|
+
"tr",
|
|
111
|
+
{
|
|
112
|
+
className: "border-t border-outline-variant/20 hover:bg-surface-container-lowest transition-colors cursor-pointer",
|
|
113
|
+
onClick: () => onRowClick == null ? void 0 : onRowClick(item),
|
|
114
|
+
children: [
|
|
115
|
+
selectable && /* @__PURE__ */ jsx(
|
|
116
|
+
"td",
|
|
117
|
+
{
|
|
118
|
+
className: "px-6 py-4",
|
|
119
|
+
onClick: (e) => {
|
|
120
|
+
e.stopPropagation();
|
|
121
|
+
handleSelectRow(item);
|
|
122
|
+
},
|
|
123
|
+
children: /* @__PURE__ */ jsx(
|
|
124
|
+
"input",
|
|
125
|
+
{
|
|
126
|
+
type: "checkbox",
|
|
127
|
+
checked: selectedRows.has(item[rowKey]),
|
|
128
|
+
onChange: () => handleSelectRow(item),
|
|
129
|
+
"aria-label": selectRowLabel(String(item[rowKey])),
|
|
130
|
+
className: "w-4 h-4 rounded border-outline-variant text-primary accent-primary cursor-pointer"
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
),
|
|
135
|
+
normalizedColumns.map((col) => /* @__PURE__ */ jsx("td", { className: "px-6 py-4", children: col.render ? col.render(item[col.key], item) : String(item[col.key]) }, String(col.key))),
|
|
136
|
+
actions && /* @__PURE__ */ jsx(
|
|
137
|
+
"td",
|
|
138
|
+
{
|
|
139
|
+
className: "px-6 py-4 text-right",
|
|
140
|
+
onClick: (e) => e.stopPropagation(),
|
|
141
|
+
children: actions(item)
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
String(item[rowKey])
|
|
147
|
+
)) })
|
|
148
|
+
] }) });
|
|
149
|
+
}
|
|
150
|
+
export {
|
|
151
|
+
DataTable
|
|
152
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface DatePickerProps {
|
|
2
|
+
label?: string;
|
|
3
|
+
value: string;
|
|
4
|
+
onChange: (value: string) => void;
|
|
5
|
+
placeholder?: string;
|
|
6
|
+
todayLabel?: string;
|
|
7
|
+
clearLabel?: string;
|
|
8
|
+
min?: string;
|
|
9
|
+
max?: string;
|
|
10
|
+
required?: boolean;
|
|
11
|
+
error?: string;
|
|
12
|
+
icon?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
dayNames?: string[];
|
|
15
|
+
monthNames?: string[];
|
|
16
|
+
}
|
|
17
|
+
declare function DatePicker({ label, value, onChange, placeholder, todayLabel, clearLabel, min, max, required, error, icon, name, dayNames, monthNames, }: DatePickerProps): React.ReactNode;
|
|
18
|
+
|
|
19
|
+
export { DatePicker };
|