@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,41 @@
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 } from "react";
8
+ const FormTextarea = forwardRef(
9
+ (_a, ref) => {
10
+ var _b = _a, { label, error, required, hint, inputId, id, className = "", requiredText = "(\u0E08\u0E33\u0E40\u0E1B\u0E47\u0E19)" } = _b, props = __objRest(_b, ["label", "error", "required", "hint", "inputId", "id", "className", "requiredText"]);
11
+ var _a2;
12
+ const resolvedId = (_a2 = inputId != null ? inputId : id) != null ? _a2 : label ? label.replace(/\s+/g, "-").toLowerCase() : void 0;
13
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
14
+ label && /* @__PURE__ */ jsxs("label", { htmlFor: resolvedId, className: "text-xs font-bold text-on-surface-variant uppercase tracking-widest px-1", children: [
15
+ label,
16
+ required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", "aria-hidden": "true", children: "*" }),
17
+ required && /* @__PURE__ */ jsx("span", { className: "sr-only", children: requiredText })
18
+ ] }),
19
+ /* @__PURE__ */ jsx(
20
+ "textarea",
21
+ __spreadValues({
22
+ ref,
23
+ id: resolvedId,
24
+ "aria-required": required,
25
+ "aria-invalid": error ? true : void 0,
26
+ "aria-describedby": error ? `${resolvedId}-error` : hint ? `${resolvedId}-hint` : void 0,
27
+ 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 resize-y ${error ? "border-red-400 bg-red-50/30" : "border-transparent"} ${className}`
28
+ }, props)
29
+ ),
30
+ hint && !error && /* @__PURE__ */ jsx("p", { id: `${resolvedId}-hint`, className: "text-[11px] text-on-surface-variant px-1", children: hint }),
31
+ error && /* @__PURE__ */ jsxs("p", { id: `${resolvedId}-error`, className: "text-xs text-red-500 px-1 flex items-center gap-1", role: "alert", children: [
32
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-sm", "aria-hidden": "true", children: "error" }),
33
+ error
34
+ ] })
35
+ ] });
36
+ }
37
+ );
38
+ FormTextarea.displayName = "FormTextarea";
39
+ export {
40
+ FormTextarea
41
+ };
@@ -0,0 +1,12 @@
1
+ import * as React$1 from 'react';
2
+
3
+ type IconButtonVariant = "default" | "ghost" | "primary" | "danger";
4
+ interface IconButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
5
+ icon: string;
6
+ variant?: IconButtonVariant;
7
+ size?: "sm" | "md";
8
+ loading?: boolean;
9
+ }
10
+ declare const IconButton: React$1.ForwardRefExoticComponent<IconButtonProps & React$1.RefAttributes<HTMLButtonElement>>;
11
+
12
+ export { IconButton, type IconButtonVariant };
@@ -0,0 +1,54 @@
1
+ "use client";
2
+ import {
3
+ __objRest,
4
+ __spreadProps,
5
+ __spreadValues
6
+ } from "./chunk-ORMEWXMH.js";
7
+ import { jsx } from "react/jsx-runtime";
8
+ import { forwardRef } from "react";
9
+ const variants = {
10
+ default: "bg-surface-container-low text-on-surface-variant hover:bg-surface-container hover:text-on-surface",
11
+ ghost: "bg-transparent text-on-surface-variant hover:bg-surface-container hover:text-on-surface",
12
+ primary: "bg-surface-container-low text-primary hover:bg-primary hover:text-on-primary",
13
+ danger: "bg-surface-container-low text-on-surface-variant hover:bg-red-50 hover:text-red-600"
14
+ };
15
+ const sizes = {
16
+ sm: "w-8 h-8 text-base",
17
+ md: "w-10 h-10 text-xl"
18
+ };
19
+ const IconButton = forwardRef(
20
+ (_a, ref) => {
21
+ var _b = _a, { icon, variant = "default", size = "md", loading = false, className = "", disabled } = _b, props = __objRest(_b, ["icon", "variant", "size", "loading", "className", "disabled"]);
22
+ return /* @__PURE__ */ jsx(
23
+ "button",
24
+ __spreadProps(__spreadValues({
25
+ ref,
26
+ type: "button",
27
+ disabled: disabled || loading,
28
+ className: [
29
+ "rounded-full flex items-center justify-center shrink-0",
30
+ "transition-all duration-150",
31
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40",
32
+ "active:scale-[0.92]",
33
+ "disabled:opacity-40 disabled:cursor-not-allowed disabled:pointer-events-none",
34
+ sizes[size],
35
+ variants[variant],
36
+ className
37
+ ].filter(Boolean).join(" ")
38
+ }, props), {
39
+ children: /* @__PURE__ */ jsx(
40
+ "span",
41
+ {
42
+ className: `material-symbols-outlined leading-none ${loading ? "animate-spin" : ""}`,
43
+ "aria-hidden": "true",
44
+ children: loading ? "progress_activity" : icon
45
+ }
46
+ )
47
+ })
48
+ );
49
+ }
50
+ );
51
+ IconButton.displayName = "IconButton";
52
+ export {
53
+ IconButton
54
+ };
@@ -0,0 +1,15 @@
1
+ interface IconPickerProps {
2
+ value: string;
3
+ onChange: (value: string) => void;
4
+ open: boolean;
5
+ onClose: () => void;
6
+ title?: string;
7
+ emptyText?: string;
8
+ searchPlaceholder?: string;
9
+ searchLabel?: string;
10
+ closeAriaLabel?: string;
11
+ categories?: Record<string, string[]>;
12
+ }
13
+ declare function IconPicker({ value, onChange, open, onClose, title, emptyText, searchPlaceholder, searchLabel, closeAriaLabel, categories, }: IconPickerProps): React.ReactNode;
14
+
15
+ export { IconPicker };
@@ -0,0 +1,311 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useState, useRef, useEffect } from "react";
5
+ import { IconButton } from "./icon-button";
6
+ const ICON_LIBRARIES = [
7
+ { id: "material", label: "Material Symbols", prefix: "" }
8
+ ];
9
+ const MATERIAL_CATEGORIES = {
10
+ "\u0E17\u0E31\u0E48\u0E27\u0E44\u0E1B": [
11
+ "dashboard",
12
+ "home",
13
+ "settings",
14
+ "search",
15
+ "menu",
16
+ "close",
17
+ "add",
18
+ "remove",
19
+ "check",
20
+ "check_circle",
21
+ "error",
22
+ "warning",
23
+ "info",
24
+ "help",
25
+ "visibility",
26
+ "visibility_off",
27
+ "refresh",
28
+ "more_vert",
29
+ "more_horiz",
30
+ "arrow_back",
31
+ "arrow_forward",
32
+ "expand_more",
33
+ "expand_less",
34
+ "launch",
35
+ "open_in_new"
36
+ ],
37
+ "\u0E1C\u0E39\u0E49\u0E43\u0E0A\u0E49": [
38
+ "person",
39
+ "group",
40
+ "people",
41
+ "admin_panel_settings",
42
+ "manage_accounts",
43
+ "badge",
44
+ "account_circle",
45
+ "face",
46
+ "supervisor_account",
47
+ "person_add",
48
+ "person_remove",
49
+ "groups",
50
+ "support_agent",
51
+ "engineering",
52
+ "verified_user"
53
+ ],
54
+ "\u0E01\u0E32\u0E23\u0E19\u0E33\u0E17\u0E32\u0E07": [
55
+ "route",
56
+ "flight_takeoff",
57
+ "flight_land",
58
+ "directions_car",
59
+ "directions_bus",
60
+ "train",
61
+ "directions_boat",
62
+ "airport_shuttle",
63
+ "luggage",
64
+ "map",
65
+ "location_on",
66
+ "explore",
67
+ "near_me",
68
+ "navigation",
69
+ "travel_explore",
70
+ "landscape"
71
+ ],
72
+ "\u0E01\u0E32\u0E23\u0E40\u0E07\u0E34\u0E19": [
73
+ "payments",
74
+ "credit_card",
75
+ "account_balance",
76
+ "receipt_long",
77
+ "paid",
78
+ "price_check",
79
+ "currency_exchange",
80
+ "savings",
81
+ "shopping_cart",
82
+ "card_membership",
83
+ "layers",
84
+ "speed",
85
+ "trending_up",
86
+ "trending_down"
87
+ ],
88
+ "\u0E40\u0E19\u0E37\u0E49\u0E2D\u0E2B\u0E32": [
89
+ "article",
90
+ "description",
91
+ "text_snippet",
92
+ "edit_note",
93
+ "post_add",
94
+ "photo_library",
95
+ "image",
96
+ "video_library",
97
+ "folder",
98
+ "cloud_upload",
99
+ "file_copy",
100
+ "attach_file",
101
+ "link",
102
+ "campaign",
103
+ "newspaper"
104
+ ],
105
+ "\u0E01\u0E32\u0E23\u0E2A\u0E37\u0E48\u0E2D\u0E2A\u0E32\u0E23": [
106
+ "mail",
107
+ "chat",
108
+ "forum",
109
+ "comment",
110
+ "notifications",
111
+ "notifications_active",
112
+ "send",
113
+ "call",
114
+ "contact_phone",
115
+ "sms",
116
+ "inbox",
117
+ "forward_to_inbox",
118
+ "mark_email_read",
119
+ "announcement",
120
+ "share"
121
+ ],
122
+ "\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25 & \u0E27\u0E34\u0E40\u0E04\u0E23\u0E32\u0E30\u0E2B\u0E4C": [
123
+ "analytics",
124
+ "assessment",
125
+ "bar_chart",
126
+ "pie_chart",
127
+ "show_chart",
128
+ "timeline",
129
+ "insights",
130
+ "summarize",
131
+ "leaderboard",
132
+ "query_stats",
133
+ "monitoring",
134
+ "data_usage",
135
+ "storage",
136
+ "database",
137
+ "table_chart"
138
+ ],
139
+ "\u0E23\u0E30\u0E1A\u0E1A": [
140
+ "settings",
141
+ "tune",
142
+ "build",
143
+ "code",
144
+ "terminal",
145
+ "dns",
146
+ "cloud",
147
+ "security",
148
+ "lock",
149
+ "vpn_key",
150
+ "api",
151
+ "integration_instructions",
152
+ "monitor_heart",
153
+ "health_and_safety",
154
+ "shield",
155
+ "task_alt"
156
+ ],
157
+ "\u0E01\u0E32\u0E23\u0E08\u0E31\u0E14\u0E01\u0E32\u0E23": [
158
+ "confirmation_number",
159
+ "assignment",
160
+ "checklist",
161
+ "event",
162
+ "calendar_month",
163
+ "schedule",
164
+ "alarm",
165
+ "bookmark",
166
+ "star",
167
+ "flag",
168
+ "label",
169
+ "category",
170
+ "inventory_2",
171
+ "package_2",
172
+ "local_shipping"
173
+ ]
174
+ };
175
+ function IconPicker({
176
+ value,
177
+ onChange,
178
+ open,
179
+ onClose,
180
+ title = "\u0E40\u0E25\u0E37\u0E2D\u0E01 Icon",
181
+ emptyText = "\u0E44\u0E21\u0E48\u0E1E\u0E1A icon",
182
+ searchPlaceholder = "\u0E04\u0E49\u0E19\u0E2B\u0E32 icon...",
183
+ searchLabel = "\u0E04\u0E49\u0E19\u0E2B\u0E32 icon",
184
+ closeAriaLabel = "\u0E1B\u0E34\u0E14",
185
+ categories
186
+ }) {
187
+ const activeCategories = categories != null ? categories : MATERIAL_CATEGORIES;
188
+ const [library, setLibrary] = useState("material");
189
+ const [category, setCategory] = useState(Object.keys(activeCategories)[0]);
190
+ const [search, setSearch] = useState("");
191
+ const dialogRef = useRef(null);
192
+ useEffect(() => {
193
+ var _a;
194
+ if (!open) return;
195
+ (_a = dialogRef.current) == null ? void 0 : _a.focus();
196
+ const handleKeyDown = (e) => {
197
+ if (e.key === "Escape") {
198
+ onClose();
199
+ return;
200
+ }
201
+ if (e.key === "Tab") {
202
+ const dialog = dialogRef.current;
203
+ if (!dialog) return;
204
+ const focusable = dialog.querySelectorAll('button, input, [tabindex]:not([tabindex="-1"])');
205
+ const first = focusable[0];
206
+ const last = focusable[focusable.length - 1];
207
+ if (e.shiftKey) {
208
+ if (document.activeElement === first) {
209
+ e.preventDefault();
210
+ last == null ? void 0 : last.focus();
211
+ }
212
+ } else {
213
+ if (document.activeElement === last) {
214
+ e.preventDefault();
215
+ first == null ? void 0 : first.focus();
216
+ }
217
+ }
218
+ }
219
+ };
220
+ document.addEventListener("keydown", handleKeyDown);
221
+ return () => document.removeEventListener("keydown", handleKeyDown);
222
+ }, [open, onClose]);
223
+ if (!open) return null;
224
+ const categoryKeys = Object.keys(activeCategories);
225
+ const icons = search ? Object.values(activeCategories).flat().filter((icon) => icon.includes(search.toLowerCase())) : activeCategories[category] || [];
226
+ return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4", children: [
227
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-black/40 backdrop-blur-sm cursor-pointer", onClick: onClose }),
228
+ /* @__PURE__ */ jsxs(
229
+ "div",
230
+ {
231
+ ref: dialogRef,
232
+ role: "dialog",
233
+ "aria-modal": "true",
234
+ "aria-labelledby": "icon-picker-title",
235
+ tabIndex: -1,
236
+ className: "relative bg-surface rounded-2xl shadow-2xl w-full max-w-2xl max-h-[80vh] flex flex-col overflow-hidden focus:outline-none",
237
+ children: [
238
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b border-outline-variant/30", children: [
239
+ /* @__PURE__ */ jsxs("div", { children: [
240
+ /* @__PURE__ */ jsx("h3", { id: "icon-picker-title", className: "text-lg font-bold text-on-surface", children: title }),
241
+ value && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-1", children: [
242
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "material-symbols-outlined text-lg text-primary", children: value }),
243
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-mono text-on-surface-variant", children: value })
244
+ ] })
245
+ ] }),
246
+ /* @__PURE__ */ jsx(IconButton, { icon: "close", variant: "ghost", size: "sm", onClick: onClose, "aria-label": closeAriaLabel })
247
+ ] }),
248
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-5 py-3 border-b border-outline-variant/30", children: [
249
+ ICON_LIBRARIES.map((lib) => /* @__PURE__ */ jsx(
250
+ "button",
251
+ {
252
+ type: "button",
253
+ onClick: () => setLibrary(lib.id),
254
+ "aria-pressed": library === lib.id,
255
+ className: `px-3 py-1.5 rounded-lg text-xs font-semibold transition-colors ${library === lib.id ? "bg-primary text-on-primary" : "bg-surface-container text-on-surface-variant hover:bg-surface-container-high"}`,
256
+ children: lib.label
257
+ },
258
+ lib.id
259
+ )),
260
+ /* @__PURE__ */ jsxs("div", { className: "relative flex-1 ml-2", children: [
261
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-on-surface-variant text-lg", children: "search" }),
262
+ /* @__PURE__ */ jsx(
263
+ "input",
264
+ {
265
+ type: "search",
266
+ "aria-label": searchLabel,
267
+ value: search,
268
+ onChange: (e) => setSearch(e.target.value),
269
+ placeholder: searchPlaceholder,
270
+ className: "w-full pl-10 pr-4 py-1.5 bg-surface-container-low border border-outline-variant rounded-lg text-sm outline-none focus:border-primary transition-[border-color]"
271
+ }
272
+ )
273
+ ] })
274
+ ] }),
275
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
276
+ !search && /* @__PURE__ */ jsx("div", { className: "w-40 shrink-0 border-r border-outline-variant/30 overflow-y-auto py-2", children: categoryKeys.map((cat) => /* @__PURE__ */ jsx(
277
+ "button",
278
+ {
279
+ type: "button",
280
+ onClick: () => setCategory(cat),
281
+ className: `w-full text-left px-4 py-2 text-xs font-medium transition-colors ${category === cat ? "text-primary bg-primary/10 font-semibold" : "text-on-surface-variant hover:bg-surface-container-low"}`,
282
+ children: cat
283
+ },
284
+ cat
285
+ )) }),
286
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto p-4", children: icons.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-center py-8 text-on-surface-variant text-sm", children: emptyText }) : /* @__PURE__ */ jsx("div", { className: "grid grid-cols-6 sm:grid-cols-8 md:grid-cols-10 gap-1", children: icons.map((icon) => /* @__PURE__ */ jsxs(
287
+ "button",
288
+ {
289
+ type: "button",
290
+ onClick: () => {
291
+ onChange(icon);
292
+ onClose();
293
+ },
294
+ "aria-label": icon,
295
+ className: `flex flex-col items-center gap-1 p-2 rounded-lg transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary ${value === icon ? "bg-primary/10 ring-2 ring-primary" : "hover:bg-surface-container"}`,
296
+ children: [
297
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "material-symbols-outlined text-2xl text-on-surface", children: icon }),
298
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "text-[8px] text-on-surface-variant truncate w-full text-center", children: icon })
299
+ ]
300
+ },
301
+ icon
302
+ )) }) })
303
+ ] })
304
+ ]
305
+ }
306
+ )
307
+ ] });
308
+ }
309
+ export {
310
+ IconPicker
311
+ };
@@ -0,0 +1,15 @@
1
+ type AppColor = "primary" | "emerald" | "amber" | "rose" | "red" | "violet" | "blue" | "slate" | "cyan";
2
+ /** @deprecated use AppColor */
3
+ type IconWrapperColor = AppColor;
4
+ interface IconWrapperProps {
5
+ icon: string;
6
+ color?: AppColor;
7
+ size?: "sm" | "md" | "lg";
8
+ filled?: boolean;
9
+ className?: string;
10
+ }
11
+ declare const appColorMap: Record<AppColor, string>;
12
+ declare const appColorFg: Record<AppColor, string>;
13
+ declare function IconWrapper({ icon, color, size, filled, className, }: IconWrapperProps): React.ReactNode;
14
+
15
+ export { type AppColor, IconWrapper, type IconWrapperColor, appColorFg, appColorMap };
@@ -0,0 +1,52 @@
1
+ import "./chunk-ORMEWXMH.js";
2
+ import { jsx } from "react/jsx-runtime";
3
+ const sizeMap = {
4
+ sm: { wrapper: "w-8 h-8 rounded-lg", icon: "text-lg" },
5
+ md: { wrapper: "w-10 h-10 rounded-xl", icon: "text-xl" },
6
+ lg: { wrapper: "w-12 h-12 rounded-2xl", icon: "text-3xl" }
7
+ };
8
+ const appColorMap = {
9
+ primary: "bg-primary/10 text-primary",
10
+ emerald: "bg-emerald-100 text-emerald-600",
11
+ amber: "bg-amber-100 text-amber-600",
12
+ rose: "bg-rose-100 text-rose-600",
13
+ red: "bg-red-100 text-red-600",
14
+ violet: "bg-violet-100 text-violet-600",
15
+ blue: "bg-blue-100 text-blue-600",
16
+ slate: "bg-surface-container text-on-surface-variant",
17
+ cyan: "bg-cyan-100 text-cyan-600"
18
+ };
19
+ const appColorFg = {
20
+ primary: "text-primary",
21
+ emerald: "text-emerald-600",
22
+ amber: "text-amber-600",
23
+ rose: "text-rose-600",
24
+ red: "text-red-600",
25
+ violet: "text-violet-600",
26
+ blue: "text-blue-600",
27
+ slate: "text-on-surface-variant",
28
+ cyan: "text-cyan-600"
29
+ };
30
+ function IconWrapper({
31
+ icon,
32
+ color = "primary",
33
+ size = "md",
34
+ filled = false,
35
+ className = ""
36
+ }) {
37
+ const s = sizeMap[size];
38
+ return /* @__PURE__ */ jsx("div", { className: `${s.wrapper} flex items-center justify-center ${appColorMap[color]} ${className}`, children: /* @__PURE__ */ jsx(
39
+ "span",
40
+ {
41
+ "aria-hidden": "true",
42
+ className: `material-symbols-outlined ${s.icon}`,
43
+ style: filled ? { fontVariationSettings: "'FILL' 1" } : void 0,
44
+ children: icon
45
+ }
46
+ ) });
47
+ }
48
+ export {
49
+ IconWrapper,
50
+ appColorFg,
51
+ appColorMap
52
+ };
@@ -0,0 +1,24 @@
1
+ interface ImageUploadProps {
2
+ value?: string | null;
3
+ onChange?: (url: string | null) => void;
4
+ onUpload: (file: File) => Promise<string>;
5
+ onUploadProgress?: (progress: number) => void;
6
+ onBrowseLibrary?: () => void;
7
+ label?: string;
8
+ hint?: string;
9
+ error?: string;
10
+ accept?: string;
11
+ maxSizeMB?: number;
12
+ aspect?: "square" | "video" | "wide";
13
+ disabled?: boolean;
14
+ uploadLabel?: string;
15
+ libraryLabel?: string;
16
+ deleteLabel?: string;
17
+ uploadingText?: string;
18
+ invalidTypeText?: string;
19
+ maxSizeErrorText?: (mb: number) => string;
20
+ uploadFailText?: string;
21
+ }
22
+ declare function ImageUpload({ value, onChange, onUpload, onUploadProgress, onBrowseLibrary, label, hint, error: externalError, accept, maxSizeMB, aspect, disabled, uploadLabel, libraryLabel, deleteLabel, uploadingText, invalidTypeText, maxSizeErrorText, uploadFailText, }: ImageUploadProps): React.ReactNode;
23
+
24
+ export { ImageUpload };
@@ -0,0 +1,122 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ import { useRef, useState } from "react";
5
+ import { Button } from "./button";
6
+ function ImageUpload({
7
+ value,
8
+ onChange,
9
+ onUpload,
10
+ onUploadProgress,
11
+ onBrowseLibrary,
12
+ label,
13
+ hint,
14
+ error: externalError,
15
+ accept = "image/jpeg,image/png,image/webp,image/gif",
16
+ maxSizeMB = 5,
17
+ aspect = "square",
18
+ disabled = false,
19
+ uploadLabel = "\u0E2D\u0E31\u0E1B\u0E42\u0E2B\u0E25\u0E14",
20
+ libraryLabel = "\u0E04\u0E25\u0E31\u0E07\u0E2A\u0E37\u0E48\u0E2D",
21
+ deleteLabel = "\u0E25\u0E1A",
22
+ uploadingText = "\u0E01\u0E33\u0E25\u0E31\u0E07\u0E2D\u0E31\u0E1B\u0E42\u0E2B\u0E25\u0E14...",
23
+ invalidTypeText = "\u0E1B\u0E23\u0E30\u0E40\u0E20\u0E17\u0E44\u0E1F\u0E25\u0E4C\u0E44\u0E21\u0E48\u0E23\u0E2D\u0E07\u0E23\u0E31\u0E1A",
24
+ maxSizeErrorText = (mb) => `\u0E02\u0E19\u0E32\u0E14\u0E44\u0E1F\u0E25\u0E4C\u0E15\u0E49\u0E2D\u0E07\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 ${mb} MB`,
25
+ uploadFailText = "\u0E2D\u0E31\u0E1B\u0E42\u0E2B\u0E25\u0E14\u0E44\u0E21\u0E48\u0E2A\u0E33\u0E40\u0E23\u0E47\u0E08 \u0E01\u0E23\u0E38\u0E13\u0E32\u0E25\u0E2D\u0E07\u0E43\u0E2B\u0E21\u0E48"
26
+ }) {
27
+ const inputRef = useRef(null);
28
+ const inputId = useRef(`image-upload-${Math.random().toString(36).slice(2)}`).current;
29
+ const [uploading, setUploading] = useState(false);
30
+ const [internalError, setInternalError] = useState(null);
31
+ const [hovering, setHovering] = useState(false);
32
+ const error = externalError != null ? externalError : internalError;
33
+ const containerClass = aspect === "square" ? "w-32 h-32" : "aspect-video w-full";
34
+ const handleFileChange = async (e) => {
35
+ var _a;
36
+ const file = (_a = e.target.files) == null ? void 0 : _a[0];
37
+ if (!file) return;
38
+ setInternalError(null);
39
+ const acceptedTypes = accept.split(",").map((t) => t.trim());
40
+ if (!acceptedTypes.includes(file.type)) {
41
+ setInternalError(invalidTypeText);
42
+ return;
43
+ }
44
+ if (file.size > maxSizeMB * 1024 * 1024) {
45
+ setInternalError(maxSizeErrorText(maxSizeMB));
46
+ return;
47
+ }
48
+ try {
49
+ setUploading(true);
50
+ onUploadProgress == null ? void 0 : onUploadProgress(0);
51
+ const url = await onUpload(file);
52
+ onUploadProgress == null ? void 0 : onUploadProgress(100);
53
+ onChange == null ? void 0 : onChange(url);
54
+ } catch (e2) {
55
+ setInternalError(uploadFailText);
56
+ } finally {
57
+ setUploading(false);
58
+ if (inputRef.current) inputRef.current.value = "";
59
+ }
60
+ };
61
+ const handleRemove = () => {
62
+ onChange == null ? void 0 : onChange(null);
63
+ setInternalError(null);
64
+ };
65
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
66
+ label && /* @__PURE__ */ jsx("label", { htmlFor: inputId, className: "text-sm font-semibold text-on-surface", children: label }),
67
+ /* @__PURE__ */ jsxs(
68
+ "div",
69
+ {
70
+ className: `relative border-2 border-dashed rounded-2xl overflow-hidden bg-surface transition-colors ${containerClass} ${error ? "border-red-400" : "border-outline-variant/40"} ${disabled ? "opacity-50 cursor-not-allowed" : ""}`,
71
+ onMouseEnter: () => setHovering(true),
72
+ onMouseLeave: () => setHovering(false),
73
+ children: [
74
+ uploading ? /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-2 bg-surface", children: [
75
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined animate-spin text-primary", style: { fontSize: "28px" }, children: "progress_activity" }),
76
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-on-surface-variant font-medium", children: uploadingText })
77
+ ] }) : value ? /* @__PURE__ */ jsxs(Fragment, { children: [
78
+ /* @__PURE__ */ jsx("img", { src: value, alt: "uploaded", className: "w-full h-full object-cover" }),
79
+ hovering && !disabled && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex items-center justify-center gap-2 bg-black/40", children: [
80
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", icon: "upload", onClick: () => {
81
+ var _a;
82
+ return (_a = inputRef.current) == null ? void 0 : _a.click();
83
+ }, children: uploadLabel }),
84
+ onBrowseLibrary && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", icon: "photo_library", onClick: onBrowseLibrary, children: libraryLabel }),
85
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "danger", icon: "delete", onClick: handleRemove, children: deleteLabel })
86
+ ] })
87
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center gap-2 p-3 text-center", children: [
88
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-on-surface-variant", style: { fontSize: "32px" }, children: "add_photo_alternate" }),
89
+ hint && aspect !== "square" && /* @__PURE__ */ jsx("p", { className: "text-xs text-on-surface-variant", children: hint }),
90
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
91
+ /* @__PURE__ */ jsx(Button, { size: "sm", variant: "primary", disabled, onClick: () => {
92
+ var _a;
93
+ return (_a = inputRef.current) == null ? void 0 : _a.click();
94
+ }, children: uploadLabel }),
95
+ onBrowseLibrary && /* @__PURE__ */ jsx(Button, { size: "sm", variant: "secondary", disabled, onClick: onBrowseLibrary, children: libraryLabel })
96
+ ] })
97
+ ] }),
98
+ /* @__PURE__ */ jsx(
99
+ "input",
100
+ {
101
+ ref: inputRef,
102
+ id: inputId,
103
+ type: "file",
104
+ accept,
105
+ disabled,
106
+ className: "hidden",
107
+ onChange: handleFileChange
108
+ }
109
+ )
110
+ ]
111
+ }
112
+ ),
113
+ hint && aspect === "square" && !error && /* @__PURE__ */ jsx("p", { className: "text-xs text-on-surface-variant", children: hint }),
114
+ error && /* @__PURE__ */ jsxs("p", { className: "text-xs text-red-500 flex items-center gap-1", children: [
115
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", style: { fontSize: "14px" }, children: "error" }),
116
+ error
117
+ ] })
118
+ ] });
119
+ }
120
+ export {
121
+ ImageUpload
122
+ };