@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.
Files changed (145) hide show
  1. package/README.md +46 -0
  2. package/dist/accordion.d.ts +18 -0
  3. package/dist/accordion.js +76 -0
  4. package/dist/agreement-modal.d.ts +16 -0
  5. package/dist/agreement-modal.js +67 -0
  6. package/dist/alert.d.ts +13 -0
  7. package/dist/alert.js +47 -0
  8. package/dist/auth-hero.d.ts +7 -0
  9. package/dist/auth-hero.js +63 -0
  10. package/dist/avatar.d.ts +21 -0
  11. package/dist/avatar.js +114 -0
  12. package/dist/badge.d.ts +13 -0
  13. package/dist/badge.js +36 -0
  14. package/dist/banner.d.ts +14 -0
  15. package/dist/banner.js +57 -0
  16. package/dist/breadcrumb.d.ts +15 -0
  17. package/dist/breadcrumb.js +37 -0
  18. package/dist/button.d.ts +15 -0
  19. package/dist/button.js +81 -0
  20. package/dist/card.d.ts +30 -0
  21. package/dist/card.js +66 -0
  22. package/dist/change-summary-modal.d.ts +35 -0
  23. package/dist/change-summary-modal.js +128 -0
  24. package/dist/channel-badge.d.ts +8 -0
  25. package/dist/channel-badge.js +17 -0
  26. package/dist/checkbox.d.ts +28 -0
  27. package/dist/checkbox.js +108 -0
  28. package/dist/chunk-ORMEWXMH.js +37 -0
  29. package/dist/color-picker.d.ts +15 -0
  30. package/dist/color-picker.js +159 -0
  31. package/dist/confirm-dialog.d.ts +23 -0
  32. package/dist/confirm-dialog.js +108 -0
  33. package/dist/copy-button.d.ts +13 -0
  34. package/dist/copy-button.js +69 -0
  35. package/dist/dashed-add-button.d.ts +8 -0
  36. package/dist/dashed-add-button.js +24 -0
  37. package/dist/data-table.d.ts +27 -0
  38. package/dist/data-table.js +152 -0
  39. package/dist/date-picker.d.ts +19 -0
  40. package/dist/date-picker.js +234 -0
  41. package/dist/date-range-picker.d.ts +25 -0
  42. package/dist/date-range-picker.js +456 -0
  43. package/dist/dev-auto-fill.d.ts +12 -0
  44. package/dist/dev-auto-fill.js +22 -0
  45. package/dist/divider.d.ts +10 -0
  46. package/dist/divider.js +44 -0
  47. package/dist/drawer.d.ts +16 -0
  48. package/dist/drawer.js +111 -0
  49. package/dist/dropdown-menu.d.ts +20 -0
  50. package/dist/dropdown-menu.js +94 -0
  51. package/dist/empty-state.d.ts +13 -0
  52. package/dist/empty-state.js +24 -0
  53. package/dist/file-upload.d.ts +32 -0
  54. package/dist/file-upload.js +212 -0
  55. package/dist/filter-tabs.d.ts +16 -0
  56. package/dist/filter-tabs.js +30 -0
  57. package/dist/footer-action-bar.d.ts +21 -0
  58. package/dist/footer-action-bar.js +95 -0
  59. package/dist/form-input.d.ts +16 -0
  60. package/dist/form-input.js +58 -0
  61. package/dist/form-textarea.d.ts +13 -0
  62. package/dist/form-textarea.js +41 -0
  63. package/dist/icon-button.d.ts +12 -0
  64. package/dist/icon-button.js +54 -0
  65. package/dist/icon-picker.d.ts +15 -0
  66. package/dist/icon-picker.js +311 -0
  67. package/dist/icon-wrapper.d.ts +15 -0
  68. package/dist/icon-wrapper.js +52 -0
  69. package/dist/image-upload.d.ts +24 -0
  70. package/dist/image-upload.js +122 -0
  71. package/dist/index.d.ts +71 -0
  72. package/dist/index.js +155 -0
  73. package/dist/kbd.d.ts +15 -0
  74. package/dist/kbd.js +27 -0
  75. package/dist/mobile-preview.d.ts +36 -0
  76. package/dist/mobile-preview.js +167 -0
  77. package/dist/modal.d.ts +19 -0
  78. package/dist/modal.js +110 -0
  79. package/dist/multi-select.d.ts +30 -0
  80. package/dist/multi-select.js +261 -0
  81. package/dist/number-input.d.ts +21 -0
  82. package/dist/number-input.js +129 -0
  83. package/dist/otp-input.d.ts +13 -0
  84. package/dist/otp-input.js +114 -0
  85. package/dist/page-header.d.ts +15 -0
  86. package/dist/page-header.js +43 -0
  87. package/dist/page-state.d.ts +14 -0
  88. package/dist/page-state.js +29 -0
  89. package/dist/pagination.d.ts +20 -0
  90. package/dist/pagination.js +87 -0
  91. package/dist/popover.d.ts +11 -0
  92. package/dist/popover.js +70 -0
  93. package/dist/preview-drawer.d.ts +33 -0
  94. package/dist/preview-drawer.js +74 -0
  95. package/dist/progress-bar.d.ts +15 -0
  96. package/dist/progress-bar.js +56 -0
  97. package/dist/qr-code-display.d.ts +10 -0
  98. package/dist/qr-code-display.js +43 -0
  99. package/dist/radio-group.d.ts +19 -0
  100. package/dist/radio-group.js +78 -0
  101. package/dist/rating.d.ts +12 -0
  102. package/dist/rating.js +123 -0
  103. package/dist/rich-editor.d.ts +13 -0
  104. package/dist/rich-editor.js +97 -0
  105. package/dist/search-bar.d.ts +14 -0
  106. package/dist/search-bar.js +64 -0
  107. package/dist/section-header.d.ts +12 -0
  108. package/dist/section-header.js +41 -0
  109. package/dist/segmented-control.d.ts +24 -0
  110. package/dist/segmented-control.js +38 -0
  111. package/dist/select-picker.d.ts +24 -0
  112. package/dist/select-picker.js +157 -0
  113. package/dist/skeleton.d.ts +14 -0
  114. package/dist/skeleton.js +53 -0
  115. package/dist/slider.d.ts +17 -0
  116. package/dist/slider.js +151 -0
  117. package/dist/spinner.d.ts +13 -0
  118. package/dist/spinner.js +38 -0
  119. package/dist/stat-card.d.ts +20 -0
  120. package/dist/stat-card.js +87 -0
  121. package/dist/stats-summary.d.ts +13 -0
  122. package/dist/stats-summary.js +28 -0
  123. package/dist/status-badge.d.ts +19 -0
  124. package/dist/status-badge.js +41 -0
  125. package/dist/stepper.d.ts +12 -0
  126. package/dist/stepper.js +89 -0
  127. package/dist/tabs.d.ts +18 -0
  128. package/dist/tabs.js +70 -0
  129. package/dist/tag.d.ts +23 -0
  130. package/dist/tag.js +158 -0
  131. package/dist/time-picker.d.ts +19 -0
  132. package/dist/time-picker.js +222 -0
  133. package/dist/timeline.d.ts +15 -0
  134. package/dist/timeline.js +49 -0
  135. package/dist/toast.d.ts +18 -0
  136. package/dist/toast.js +108 -0
  137. package/dist/toggle-switch.d.ts +12 -0
  138. package/dist/toggle-switch.js +34 -0
  139. package/dist/tooltip.d.ts +9 -0
  140. package/dist/tooltip.js +69 -0
  141. package/dist/trip-day-map-lazy.d.ts +15 -0
  142. package/dist/trip-day-map-lazy.js +16 -0
  143. package/dist/trip-day-map.d.ts +15 -0
  144. package/dist/trip-day-map.js +62 -0
  145. package/package.json +73 -0
@@ -0,0 +1,20 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface DropdownItem {
4
+ label: string;
5
+ icon?: string;
6
+ onClick?: () => void;
7
+ href?: string;
8
+ variant?: "default" | "danger";
9
+ disabled?: boolean;
10
+ divider?: boolean;
11
+ }
12
+ interface DropdownMenuProps {
13
+ trigger: React.ReactNode;
14
+ items: DropdownItem[];
15
+ align?: "left" | "right";
16
+ className?: string;
17
+ }
18
+ declare function DropdownMenu({ trigger, items, align, className, }: DropdownMenuProps): react_jsx_runtime.JSX.Element;
19
+
20
+ export { type DropdownItem, DropdownMenu };
@@ -0,0 +1,94 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useState, useEffect, useRef } from "react";
5
+ function DropdownMenu({
6
+ trigger,
7
+ items,
8
+ align = "left",
9
+ className = ""
10
+ }) {
11
+ const [open, setOpen] = useState(false);
12
+ const containerRef = useRef(null);
13
+ useEffect(() => {
14
+ if (!open) return;
15
+ function handleMouseDown(e) {
16
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
17
+ setOpen(false);
18
+ }
19
+ }
20
+ function handleKeyDown(e) {
21
+ if (e.key === "Escape") {
22
+ setOpen(false);
23
+ }
24
+ }
25
+ document.addEventListener("mousedown", handleMouseDown);
26
+ document.addEventListener("keydown", handleKeyDown);
27
+ return () => {
28
+ document.removeEventListener("mousedown", handleMouseDown);
29
+ document.removeEventListener("keydown", handleKeyDown);
30
+ };
31
+ }, [open]);
32
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: `relative inline-block ${className}`, children: [
33
+ /* @__PURE__ */ jsx(
34
+ "div",
35
+ {
36
+ onClick: () => setOpen((prev) => !prev),
37
+ "aria-haspopup": "menu",
38
+ "aria-expanded": open,
39
+ children: trigger
40
+ }
41
+ ),
42
+ open && /* @__PURE__ */ jsx(
43
+ "div",
44
+ {
45
+ role: "menu",
46
+ className: `absolute top-full mt-1 z-50 bg-white rounded-xl shadow-xl border border-outline-variant/20 overflow-hidden min-w-40 py-1 ${align === "right" ? "right-0" : "left-0"}`,
47
+ children: items.map((item, index) => {
48
+ const baseItemClass = `flex items-center gap-2.5 px-3 py-2.5 text-sm transition-colors duration-150 w-full text-left active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30`;
49
+ const variantClass = item.variant === "danger" ? "text-red-600 hover:bg-red-50" : "text-on-surface hover:bg-surface-container-low";
50
+ const disabledClass = item.disabled ? "opacity-40 cursor-not-allowed pointer-events-none" : "";
51
+ return /* @__PURE__ */ jsxs("div", { children: [
52
+ item.divider && /* @__PURE__ */ jsx("div", { className: "border-t border-outline-variant/20 my-1" }),
53
+ item.href && !item.disabled ? /* @__PURE__ */ jsxs(
54
+ "a",
55
+ {
56
+ role: "menuitem",
57
+ href: item.href,
58
+ className: `${baseItemClass} ${variantClass} ${disabledClass}`,
59
+ onClick: () => setOpen(false),
60
+ children: [
61
+ item.icon && /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-base", "aria-hidden": "true", children: item.icon }),
62
+ /* @__PURE__ */ jsx("span", { children: item.label })
63
+ ]
64
+ }
65
+ ) : /* @__PURE__ */ jsxs(
66
+ "button",
67
+ {
68
+ role: "menuitem",
69
+ type: "button",
70
+ disabled: item.disabled,
71
+ "aria-disabled": item.disabled,
72
+ onClick: () => {
73
+ var _a;
74
+ if (!item.disabled) {
75
+ (_a = item.onClick) == null ? void 0 : _a.call(item);
76
+ setOpen(false);
77
+ }
78
+ },
79
+ className: `${baseItemClass} ${variantClass} ${disabledClass}`,
80
+ children: [
81
+ item.icon && /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-base", "aria-hidden": "true", children: item.icon }),
82
+ /* @__PURE__ */ jsx("span", { children: item.label })
83
+ ]
84
+ }
85
+ )
86
+ ] }, index);
87
+ })
88
+ }
89
+ )
90
+ ] });
91
+ }
92
+ export {
93
+ DropdownMenu
94
+ };
@@ -0,0 +1,13 @@
1
+ interface EmptyStateProps {
2
+ icon: string;
3
+ title: string;
4
+ description: string;
5
+ actionLabel?: string;
6
+ actionHref?: string;
7
+ onAction?: () => void;
8
+ actionIcon?: string;
9
+ actionVariant?: "primary" | "secondary";
10
+ }
11
+ declare function EmptyState({ icon, title, description, actionLabel, actionHref, onAction, actionIcon, actionVariant, }: EmptyStateProps): React.ReactNode;
12
+
13
+ export { EmptyState };
@@ -0,0 +1,24 @@
1
+ import "./chunk-ORMEWXMH.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Button } from "./button";
4
+ function EmptyState({
5
+ icon,
6
+ title,
7
+ description,
8
+ actionLabel,
9
+ actionHref,
10
+ onAction,
11
+ actionIcon = "add",
12
+ actionVariant = "primary"
13
+ }) {
14
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-20 px-6 text-center", children: [
15
+ /* @__PURE__ */ jsx("div", { className: "w-20 h-20 rounded-3xl bg-surface-container-low flex items-center justify-center mb-6", children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-4xl text-on-surface-variant opacity-40", "aria-hidden": "true", children: icon }) }),
16
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-bold text-on-surface mb-2", children: title }),
17
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-on-surface-variant max-w-sm mb-8 leading-relaxed", children: description }),
18
+ actionLabel && actionHref && /* @__PURE__ */ jsx("a", { href: actionHref, children: /* @__PURE__ */ jsx(Button, { variant: actionVariant, icon: actionIcon, children: actionLabel }) }),
19
+ actionLabel && onAction && !actionHref && /* @__PURE__ */ jsx(Button, { variant: actionVariant, icon: actionIcon, onClick: onAction, children: actionLabel })
20
+ ] });
21
+ }
22
+ export {
23
+ EmptyState
24
+ };
@@ -0,0 +1,32 @@
1
+ interface UploadedFile {
2
+ name: string;
3
+ size: number;
4
+ url: string;
5
+ type: string;
6
+ }
7
+ interface FileUploadProps {
8
+ value?: UploadedFile[];
9
+ onChange?: (files: UploadedFile[]) => void;
10
+ onUpload: (file: File) => Promise<string>;
11
+ onUploadProgress?: (fileName: string, progress: number) => void;
12
+ label?: string;
13
+ hint?: string;
14
+ error?: string;
15
+ accept?: string;
16
+ maxSizeMB?: number;
17
+ maxFiles?: number;
18
+ multiple?: boolean;
19
+ disabled?: boolean;
20
+ name?: string;
21
+ dragText?: string;
22
+ uploadingText?: string;
23
+ maxFilesReachedText?: (max: number) => string;
24
+ limitHintText?: (mb: number, max: number) => string;
25
+ maxFilesErrorText?: (max: number) => string;
26
+ maxSizeErrorText?: (fileName: string, mb: number) => string;
27
+ uploadAreaLabel?: string;
28
+ removeFileLabel?: (name: string) => string;
29
+ }
30
+ declare function FileUpload({ value, onChange, onUpload, onUploadProgress, label, hint, error, accept, maxSizeMB, maxFiles, multiple, disabled, name, dragText, uploadingText, maxFilesReachedText, limitHintText, maxFilesErrorText, maxSizeErrorText, uploadAreaLabel, removeFileLabel, }: FileUploadProps): React.ReactNode;
31
+
32
+ export { FileUpload, type UploadedFile };
@@ -0,0 +1,212 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useRef, useState } from "react";
5
+ import { Spinner } from "./spinner";
6
+ function formatBytes(bytes) {
7
+ if (bytes < 1024) return `${bytes} B`;
8
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
9
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
10
+ }
11
+ function getFileIcon(type) {
12
+ if (type === "application/pdf") return "picture_as_pdf";
13
+ if (type === "application/msword" || type === "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
14
+ return "description";
15
+ if (type === "text/csv" || type === "application/vnd.ms-excel" || type === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
16
+ return "table_chart";
17
+ return "attach_file";
18
+ }
19
+ function FileUpload({
20
+ value = [],
21
+ onChange,
22
+ onUpload,
23
+ onUploadProgress,
24
+ label,
25
+ hint,
26
+ error,
27
+ accept = "*",
28
+ maxSizeMB = 10,
29
+ maxFiles = 5,
30
+ multiple = true,
31
+ disabled = false,
32
+ name,
33
+ dragText = "\u0E25\u0E32\u0E01\u0E44\u0E1F\u0E25\u0E4C\u0E21\u0E32\u0E27\u0E32\u0E07\u0E17\u0E35\u0E48\u0E19\u0E35\u0E48 \u0E2B\u0E23\u0E37\u0E2D\u0E04\u0E25\u0E34\u0E01\u0E40\u0E1E\u0E37\u0E48\u0E2D\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E1F\u0E25\u0E4C",
34
+ uploadingText = "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E2D\u0E31\u0E1B\u0E42\u0E2B\u0E25\u0E14...",
35
+ maxFilesReachedText = (max) => `\u0E04\u0E23\u0E1A ${max} \u0E44\u0E1F\u0E25\u0E4C\u0E41\u0E25\u0E49\u0E27`,
36
+ limitHintText = (mb, max) => `\u0E44\u0E1F\u0E25\u0E4C\u0E2A\u0E39\u0E07\u0E2A\u0E38\u0E14 ${mb} MB \xB7 \u0E2A\u0E39\u0E07\u0E2A\u0E38\u0E14 ${max} \u0E44\u0E1F\u0E25\u0E4C`,
37
+ maxFilesErrorText = (max) => `\u0E2A\u0E32\u0E21\u0E32\u0E23\u0E16\u0E2D\u0E31\u0E1B\u0E42\u0E2B\u0E25\u0E14\u0E44\u0E14\u0E49\u0E2A\u0E39\u0E07\u0E2A\u0E38\u0E14 ${max} \u0E44\u0E1F\u0E25\u0E4C`,
38
+ maxSizeErrorText = (fileName, mb) => `\u0E44\u0E1F\u0E25\u0E4C ${fileName} \u0E21\u0E35\u0E02\u0E19\u0E32\u0E14\u0E40\u0E01\u0E34\u0E19 ${mb} MB`,
39
+ uploadAreaLabel = "Upload files",
40
+ removeFileLabel = (name2) => `Remove ${name2}`
41
+ }) {
42
+ const inputRef = useRef(null);
43
+ const [dragging, setDragging] = useState(false);
44
+ const [internalError, setInternalError] = useState(null);
45
+ const [uploadingRows, setUploadingRows] = useState([]);
46
+ const displayError = error != null ? error : internalError;
47
+ const handleFiles = async (files) => {
48
+ if (!files || files.length === 0) return;
49
+ setInternalError(null);
50
+ const incoming = Array.from(files);
51
+ if (value.length + incoming.length > maxFiles) {
52
+ setInternalError(maxFilesErrorText(maxFiles));
53
+ return;
54
+ }
55
+ for (const file of incoming) {
56
+ if (file.size > maxSizeMB * 1024 * 1024) {
57
+ setInternalError(maxSizeErrorText(file.name, maxSizeMB));
58
+ return;
59
+ }
60
+ }
61
+ const rows = incoming.map((f) => ({
62
+ id: `${Date.now()}-${f.name}`,
63
+ name: f.name
64
+ }));
65
+ setUploadingRows((prev) => [...prev, ...rows]);
66
+ const results = [];
67
+ await Promise.all(
68
+ incoming.map(async (file, i) => {
69
+ onUploadProgress == null ? void 0 : onUploadProgress(file.name, 0);
70
+ try {
71
+ const url = await onUpload(file);
72
+ results.push({ name: file.name, size: file.size, url, type: file.type });
73
+ onUploadProgress == null ? void 0 : onUploadProgress(file.name, 100);
74
+ } finally {
75
+ const rowId = rows[i].id;
76
+ setUploadingRows((prev) => prev.filter((r) => r.id !== rowId));
77
+ }
78
+ })
79
+ );
80
+ if (results.length > 0) {
81
+ onChange == null ? void 0 : onChange([...value, ...results]);
82
+ }
83
+ if (inputRef.current) inputRef.current.value = "";
84
+ };
85
+ const handleRemove = (index) => {
86
+ const next = value.filter((_, i) => i !== index);
87
+ onChange == null ? void 0 : onChange(next);
88
+ };
89
+ const handleDragOver = (e) => {
90
+ e.preventDefault();
91
+ if (!disabled) setDragging(true);
92
+ };
93
+ const handleDragLeave = (e) => {
94
+ e.preventDefault();
95
+ setDragging(false);
96
+ };
97
+ const handleDrop = (e) => {
98
+ e.preventDefault();
99
+ setDragging(false);
100
+ if (disabled) return;
101
+ handleFiles(e.dataTransfer.files);
102
+ };
103
+ const canUploadMore = value.length + uploadingRows.length < maxFiles;
104
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
105
+ label && /* @__PURE__ */ jsx("label", { className: "text-sm font-semibold text-on-surface", children: label }),
106
+ /* @__PURE__ */ jsxs(
107
+ "div",
108
+ {
109
+ role: "button",
110
+ tabIndex: disabled ? -1 : 0,
111
+ "aria-label": uploadAreaLabel,
112
+ className: [
113
+ "border-2 border-dashed rounded-2xl p-8 text-center transition-colors cursor-pointer select-none",
114
+ dragging ? "border-primary bg-primary/5" : displayError ? "border-red-400 bg-surface-container-low/50" : "border-outline-variant/40 bg-surface-container-low/50",
115
+ disabled || !canUploadMore ? "opacity-50 cursor-not-allowed pointer-events-none" : "hover:border-primary/50 hover:bg-primary/5"
116
+ ].join(" "),
117
+ onClick: () => {
118
+ var _a;
119
+ return !disabled && canUploadMore && ((_a = inputRef.current) == null ? void 0 : _a.click());
120
+ },
121
+ onKeyDown: (e) => {
122
+ var _a;
123
+ if ((e.key === "Enter" || e.key === " ") && !disabled && canUploadMore)
124
+ (_a = inputRef.current) == null ? void 0 : _a.click();
125
+ },
126
+ onDragOver: handleDragOver,
127
+ onDragLeave: handleDragLeave,
128
+ onDrop: handleDrop,
129
+ children: [
130
+ /* @__PURE__ */ jsx(
131
+ "span",
132
+ {
133
+ className: "material-symbols-outlined text-on-surface-variant mb-2 block",
134
+ style: { fontSize: "40px" },
135
+ children: "cloud_upload"
136
+ }
137
+ ),
138
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-on-surface", children: canUploadMore ? dragText : maxFilesReachedText(maxFiles) }),
139
+ hint && /* @__PURE__ */ jsx("p", { className: "text-xs text-on-surface-variant mt-1", children: hint }),
140
+ canUploadMore && /* @__PURE__ */ jsx("p", { className: "text-xs text-on-surface-variant mt-1", children: limitHintText(maxSizeMB, maxFiles) })
141
+ ]
142
+ }
143
+ ),
144
+ /* @__PURE__ */ jsx(
145
+ "input",
146
+ {
147
+ ref: inputRef,
148
+ type: "file",
149
+ accept,
150
+ multiple,
151
+ disabled,
152
+ className: "hidden",
153
+ onChange: (e) => handleFiles(e.target.files)
154
+ }
155
+ ),
156
+ displayError && /* @__PURE__ */ jsxs("p", { className: "text-xs text-red-500 flex items-center gap-1", children: [
157
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", style: { fontSize: "14px" }, children: "error" }),
158
+ displayError
159
+ ] }),
160
+ (value.length > 0 || uploadingRows.length > 0) && /* @__PURE__ */ jsxs("ul", { className: "flex flex-col gap-2", children: [
161
+ value.map((file, i) => /* @__PURE__ */ jsxs(
162
+ "li",
163
+ {
164
+ className: "flex items-center gap-3 p-3 bg-surface-container-low rounded-xl",
165
+ children: [
166
+ name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: file.url }),
167
+ /* @__PURE__ */ jsx(
168
+ "span",
169
+ {
170
+ className: "material-symbols-outlined text-primary shrink-0",
171
+ style: { fontSize: "22px" },
172
+ children: getFileIcon(file.type)
173
+ }
174
+ ),
175
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
176
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-on-surface truncate", children: file.name }),
177
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-on-surface-variant", children: formatBytes(file.size) })
178
+ ] }),
179
+ !disabled && /* @__PURE__ */ jsx(
180
+ "button",
181
+ {
182
+ type: "button",
183
+ onClick: () => handleRemove(i),
184
+ className: "shrink-0 p-1 rounded-lg text-on-surface-variant hover:bg-surface-container hover:text-red-500 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
185
+ "aria-label": removeFileLabel(file.name),
186
+ children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", style: { fontSize: "18px" }, children: "close" })
187
+ }
188
+ )
189
+ ]
190
+ },
191
+ `${file.name}-${i}`
192
+ )),
193
+ uploadingRows.map((row) => /* @__PURE__ */ jsxs(
194
+ "li",
195
+ {
196
+ className: "flex items-center gap-3 p-3 bg-surface-container-low rounded-xl",
197
+ children: [
198
+ /* @__PURE__ */ jsx(Spinner, { size: "sm", color: "primary", className: "shrink-0" }),
199
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
200
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-on-surface truncate", children: row.name }),
201
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-on-surface-variant", children: uploadingText })
202
+ ] })
203
+ ]
204
+ },
205
+ row.id
206
+ ))
207
+ ] })
208
+ ] });
209
+ }
210
+ export {
211
+ FileUpload
212
+ };
@@ -0,0 +1,16 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface FilterTab {
4
+ id: string;
5
+ label: string;
6
+ icon?: string;
7
+ count?: number;
8
+ }
9
+ interface FilterTabsProps {
10
+ tabs: FilterTab[];
11
+ activeTab: string;
12
+ onTabChange: (tabId: string) => void;
13
+ }
14
+ declare function FilterTabs({ tabs, activeTab, onTabChange, }: FilterTabsProps): react_jsx_runtime.JSX.Element;
15
+
16
+ export { type FilterTab, FilterTabs };
@@ -0,0 +1,30 @@
1
+ import "./chunk-ORMEWXMH.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ function FilterTabs({
4
+ tabs,
5
+ activeTab,
6
+ onTabChange
7
+ }) {
8
+ return /* @__PURE__ */ jsx("div", { role: "tablist", className: "flex items-center gap-2 border-b border-outline-variant/20 overflow-x-auto", children: tabs.map((tab) => {
9
+ const isActive = activeTab === tab.id;
10
+ return /* @__PURE__ */ jsxs(
11
+ "button",
12
+ {
13
+ type: "button",
14
+ role: "tab",
15
+ "aria-selected": isActive,
16
+ onClick: () => onTabChange(tab.id),
17
+ className: `flex items-center gap-2 px-4 py-3 text-sm font-medium border-b-2 whitespace-nowrap transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary ${isActive ? "border-primary text-primary" : "border-transparent text-on-surface-variant hover:text-primary"}`,
18
+ children: [
19
+ tab.icon && /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-lg", "aria-hidden": "true", children: tab.icon }),
20
+ /* @__PURE__ */ jsx("span", { children: tab.label }),
21
+ tab.count !== void 0 && /* @__PURE__ */ jsx("span", { className: "ml-1 text-xs px-2 py-0.5 rounded-full bg-surface-container", children: tab.count })
22
+ ]
23
+ },
24
+ tab.id
25
+ );
26
+ }) });
27
+ }
28
+ export {
29
+ FilterTabs
30
+ };
@@ -0,0 +1,21 @@
1
+ interface FooterActionBarProps {
2
+ backHref?: string;
3
+ backLabel: string;
4
+ backIcon?: string;
5
+ onBack?: () => void;
6
+ onSaveDraft?: () => void;
7
+ saveDraftLabel?: string;
8
+ savingText?: string;
9
+ savingDraft?: boolean;
10
+ middleSlot?: React.ReactNode;
11
+ nextHref?: string;
12
+ nextLabel: string;
13
+ nextIcon?: string;
14
+ nextVariant?: "primary" | "success";
15
+ onNext?: () => void;
16
+ disabled?: boolean;
17
+ loading?: boolean;
18
+ }
19
+ declare function FooterActionBar({ backHref, backLabel, onBack, backIcon, onSaveDraft, saveDraftLabel, savingText, savingDraft, middleSlot, nextHref, nextLabel, nextIcon, nextVariant, onNext, disabled, loading, }: FooterActionBarProps): React.ReactNode;
20
+
21
+ export { FooterActionBar };
@@ -0,0 +1,95 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ function FooterActionBar({
5
+ backHref,
6
+ backLabel,
7
+ onBack,
8
+ backIcon = "arrow_back",
9
+ onSaveDraft,
10
+ saveDraftLabel = "\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01\u0E23\u0E48\u0E32\u0E07",
11
+ savingText = "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E1A\u0E31\u0E19\u0E17\u0E36\u0E01...",
12
+ savingDraft = false,
13
+ middleSlot,
14
+ nextHref,
15
+ nextLabel,
16
+ nextIcon = "arrow_forward",
17
+ nextVariant = "primary",
18
+ onNext,
19
+ disabled = false,
20
+ loading = false
21
+ }) {
22
+ const nextColor = nextVariant === "success" ? "bg-green-600 text-white shadow-xl shadow-green-600/25" : "bg-primary text-on-primary shadow-xl shadow-primary/25";
23
+ const nextContent = /* @__PURE__ */ jsxs(Fragment, { children: [
24
+ loading ? /* @__PURE__ */ jsx("div", { className: "w-5 h-5 border-2 border-white/40 border-t-white rounded-full animate-spin" }) : nextVariant === "success" ? /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", style: { fontVariationSettings: "'FILL' 1" }, "aria-hidden": "true", children: "rocket_launch" }) : null,
25
+ nextLabel,
26
+ !loading && nextVariant === "primary" && /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined group-hover:translate-x-1 transition-transform", "aria-hidden": "true", children: nextIcon })
27
+ ] });
28
+ return /* @__PURE__ */ jsxs("div", { className: "sticky top-32 z-19 bg-surface/95 backdrop-blur-sm border-b border-outline-variant/30 px-4 md:px-8 py-3 flex flex-col md:flex-row items-center justify-between gap-3 flex-wrap", children: [
29
+ onBack ? /* @__PURE__ */ jsxs(
30
+ "button",
31
+ {
32
+ type: "button",
33
+ onClick: onBack,
34
+ className: "w-full md:w-auto px-6 py-2.5 rounded-full text-sm font-bold text-on-surface-variant hover:bg-surface-container-low transition-colors flex items-center justify-center gap-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30",
35
+ children: [
36
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": "true", children: backIcon }),
37
+ " ",
38
+ backLabel
39
+ ]
40
+ }
41
+ ) : backHref ? /* @__PURE__ */ jsxs(
42
+ "a",
43
+ {
44
+ href: backHref,
45
+ className: "w-full md:w-auto px-6 py-2.5 rounded-full text-sm font-bold text-on-surface-variant hover:bg-surface-container-low transition-colors flex items-center justify-center gap-2",
46
+ children: [
47
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": "true", children: backIcon }),
48
+ " ",
49
+ backLabel
50
+ ]
51
+ }
52
+ ) : /* @__PURE__ */ jsx("div", {}),
53
+ middleSlot && /* @__PURE__ */ jsx("div", { className: "flex-1 flex justify-center", children: middleSlot }),
54
+ /* @__PURE__ */ jsxs("div", { className: "w-full md:w-auto flex flex-col sm:flex-row items-center gap-4", children: [
55
+ onSaveDraft && /* @__PURE__ */ jsx(
56
+ "button",
57
+ {
58
+ type: "button",
59
+ onClick: onSaveDraft,
60
+ disabled: savingDraft || loading,
61
+ className: "w-full sm:w-auto px-6 py-2.5 rounded-full text-sm font-bold text-primary hover:bg-primary/5 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30",
62
+ children: savingDraft ? /* @__PURE__ */ jsxs(Fragment, { children: [
63
+ /* @__PURE__ */ jsx("div", { className: "w-4 h-4 border-2 border-primary/40 border-t-primary rounded-full animate-spin" }),
64
+ " ",
65
+ savingText
66
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
67
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-lg", "aria-hidden": "true", children: "save" }),
68
+ " ",
69
+ saveDraftLabel
70
+ ] })
71
+ }
72
+ ),
73
+ nextHref && !disabled ? /* @__PURE__ */ jsx(
74
+ "a",
75
+ {
76
+ href: nextHref,
77
+ className: `w-full sm:w-auto px-8 py-3 rounded-full font-bold text-sm hover:scale-[1.02] active:scale-[0.98] transition-colors flex items-center justify-center gap-3 group ${nextColor}`,
78
+ children: nextContent
79
+ }
80
+ ) : /* @__PURE__ */ jsx(
81
+ "button",
82
+ {
83
+ type: "button",
84
+ onClick: onNext,
85
+ disabled: disabled || loading,
86
+ className: `w-full sm:w-auto px-8 py-3 rounded-full font-bold text-sm transition-colors flex items-center justify-center gap-3 group focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 ${nextColor} ${disabled || loading ? "opacity-60 cursor-not-allowed" : "hover:scale-[1.02] active:scale-[0.98]"}`,
87
+ children: nextContent
88
+ }
89
+ )
90
+ ] })
91
+ ] });
92
+ }
93
+ export {
94
+ FooterActionBar
95
+ };
@@ -0,0 +1,16 @@
1
+ import * as React$1 from 'react';
2
+
3
+ interface FormInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "required"> {
4
+ label?: string;
5
+ icon?: string;
6
+ error?: string;
7
+ required?: boolean;
8
+ hint?: string;
9
+ inputId?: string;
10
+ requiredText?: string;
11
+ showPasswordLabel?: string;
12
+ hidePasswordLabel?: string;
13
+ }
14
+ declare const FormInput: React$1.ForwardRefExoticComponent<FormInputProps & React$1.RefAttributes<HTMLInputElement>>;
15
+
16
+ export { FormInput };
@@ -0,0 +1,58 @@
1
+ "use client";
2
+ import {
3
+ __objRest,
4
+ __spreadValues
5
+ } from "./chunk-ORMEWXMH.js";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { forwardRef, useState } from "react";
8
+ const FormInput = forwardRef(
9
+ (_a, ref) => {
10
+ var _b = _a, { label, icon, error, required, hint, type, inputId, id, className = "", requiredText = "(\u0E08\u0E33\u0E40\u0E1B\u0E47\u0E19)", showPasswordLabel = "\u0E41\u0E2A\u0E14\u0E07\u0E23\u0E2B\u0E31\u0E2A\u0E1C\u0E48\u0E32\u0E19", hidePasswordLabel = "\u0E0B\u0E48\u0E2D\u0E19\u0E23\u0E2B\u0E31\u0E2A\u0E1C\u0E48\u0E32\u0E19" } = _b, props = __objRest(_b, ["label", "icon", "error", "required", "hint", "type", "inputId", "id", "className", "requiredText", "showPasswordLabel", "hidePasswordLabel"]);
11
+ var _a2;
12
+ const isPassword = type === "password";
13
+ const [showPassword, setShowPassword] = useState(false);
14
+ const resolvedId = (_a2 = inputId != null ? inputId : id) != null ? _a2 : label ? label.replace(/\s+/g, "-").toLowerCase() : void 0;
15
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
16
+ label && /* @__PURE__ */ jsxs("label", { htmlFor: resolvedId, className: "text-xs font-bold text-on-surface-variant uppercase tracking-widest px-1", children: [
17
+ label,
18
+ required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", "aria-hidden": "true", children: "*" }),
19
+ required && /* @__PURE__ */ jsx("span", { className: "sr-only", children: requiredText })
20
+ ] }),
21
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
22
+ icon && /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined absolute left-4 top-1/2 -translate-y-1/2 text-on-surface-variant", "aria-hidden": "true", children: icon }),
23
+ /* @__PURE__ */ jsx(
24
+ "input",
25
+ __spreadValues({
26
+ ref,
27
+ id: resolvedId,
28
+ type: isPassword && showPassword ? "text" : type,
29
+ "aria-required": required,
30
+ "aria-invalid": error ? true : void 0,
31
+ "aria-describedby": error ? `${resolvedId}-error` : hint ? `${resolvedId}-hint` : void 0,
32
+ className: `w-full bg-surface-container-low border rounded-xl py-4 px-6 focus-visible:bg-surface focus-visible:ring-2 focus-visible:ring-primary/20 focus-visible:border-primary transition-[border-color,box-shadow,background-color] text-on-surface font-medium placeholder:text-outline/40 outline-none ${icon ? "pl-12" : ""} ${isPassword ? "pr-12" : ""} ${error ? "border-red-400 bg-red-50/30" : "border-transparent"} ${className}`
33
+ }, props)
34
+ ),
35
+ isPassword && /* @__PURE__ */ jsx(
36
+ "button",
37
+ {
38
+ type: "button",
39
+ onClick: () => setShowPassword(!showPassword),
40
+ className: "absolute right-2 top-1/2 -translate-y-1/2 w-9 h-9 flex items-center justify-center text-outline hover:text-on-surface hover:bg-surface-container rounded-full transition-colors",
41
+ "aria-label": showPassword ? hidePasswordLabel : showPasswordLabel,
42
+ tabIndex: -1,
43
+ children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-xl leading-none", children: showPassword ? "visibility_off" : "visibility" })
44
+ }
45
+ )
46
+ ] }),
47
+ hint && !error && /* @__PURE__ */ jsx("p", { id: `${resolvedId}-hint`, className: "text-[11px] text-on-surface-variant px-1", children: hint }),
48
+ error && /* @__PURE__ */ jsxs("p", { id: `${resolvedId}-error`, className: "text-xs text-red-500 px-1 flex items-center gap-1", role: "alert", children: [
49
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-sm", "aria-hidden": "true", children: "error" }),
50
+ error
51
+ ] })
52
+ ] });
53
+ }
54
+ );
55
+ FormInput.displayName = "FormInput";
56
+ export {
57
+ FormInput
58
+ };
@@ -0,0 +1,13 @@
1
+ import * as React$1 from 'react';
2
+
3
+ interface FormTextareaProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, "required"> {
4
+ label?: string;
5
+ error?: string;
6
+ required?: boolean;
7
+ hint?: string;
8
+ inputId?: string;
9
+ requiredText?: string;
10
+ }
11
+ declare const FormTextarea: React$1.ForwardRefExoticComponent<FormTextareaProps & React$1.RefAttributes<HTMLTextAreaElement>>;
12
+
13
+ export { FormTextarea };