@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
package/dist/tag.js ADDED
@@ -0,0 +1,158 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useState, useRef } from "react";
5
+ const TAG_BG = {
6
+ primary: "bg-primary-container text-(--on-primary-container)",
7
+ emerald: "bg-emerald-100 text-emerald-700",
8
+ amber: "bg-amber-100 text-amber-700",
9
+ rose: "bg-rose-100 text-rose-700",
10
+ red: "bg-red-100 text-red-700",
11
+ violet: "bg-violet-100 text-violet-700",
12
+ blue: "bg-blue-100 text-blue-700",
13
+ slate: "bg-surface-container text-on-surface-variant",
14
+ cyan: "bg-cyan-100 text-cyan-700"
15
+ };
16
+ function Tag({
17
+ label,
18
+ onRemove,
19
+ color = "primary",
20
+ size = "md",
21
+ icon,
22
+ selected = false,
23
+ onClick
24
+ }) {
25
+ const sizeClass = size === "sm" ? "px-2 py-0.5 text-xs rounded-md" : "px-3 py-1 text-sm rounded-lg";
26
+ const colorClass = TAG_BG[color];
27
+ const selectedClass = selected ? "ring-2 ring-current" : "";
28
+ const clickableClass = onClick ? "cursor-pointer" : "";
29
+ return /* @__PURE__ */ jsxs(
30
+ "span",
31
+ {
32
+ onClick,
33
+ role: onClick ? "button" : void 0,
34
+ tabIndex: onClick ? 0 : void 0,
35
+ onKeyDown: onClick ? (e) => {
36
+ if (e.key === "Enter" || e.key === " ") {
37
+ e.preventDefault();
38
+ onClick();
39
+ }
40
+ } : void 0,
41
+ className: `inline-flex items-center gap-1.5 font-medium ${sizeClass} ${colorClass} ${selectedClass} ${clickableClass}`,
42
+ children: [
43
+ icon && /* @__PURE__ */ jsx(
44
+ "span",
45
+ {
46
+ className: `material-symbols-outlined ${size === "sm" ? "text-xs" : "text-sm"}`,
47
+ "aria-hidden": "true",
48
+ children: icon
49
+ }
50
+ ),
51
+ label,
52
+ onRemove && /* @__PURE__ */ jsx(
53
+ "button",
54
+ {
55
+ type: "button",
56
+ onClick: (e) => {
57
+ e.stopPropagation();
58
+ onRemove();
59
+ },
60
+ "aria-label": `Remove ${label}`,
61
+ className: "ml-0.5 rounded hover:bg-black/10 transition-colors p-0.5",
62
+ children: /* @__PURE__ */ jsx("span", { className: `material-symbols-outlined ${size === "sm" ? "text-xs" : "text-sm"} leading-none`, children: "close" })
63
+ }
64
+ )
65
+ ]
66
+ }
67
+ );
68
+ }
69
+ function TagGroup({
70
+ value,
71
+ onChange,
72
+ placeholder = "Add a tag...",
73
+ label,
74
+ maxTags
75
+ }) {
76
+ const [inputValue, setInputValue] = useState("");
77
+ const inputRef = useRef(null);
78
+ const inputId = label ? `tag-group-${label.replace(/\s+/g, "-").toLowerCase()}` : void 0;
79
+ function addTag(raw) {
80
+ const trimmed = raw.trim();
81
+ if (!trimmed) return;
82
+ if (maxTags !== void 0 && value.length >= maxTags) return;
83
+ if (value.includes(trimmed)) return;
84
+ onChange([...value, trimmed]);
85
+ setInputValue("");
86
+ }
87
+ function removeTag(index) {
88
+ onChange(value.filter((_, i) => i !== index));
89
+ }
90
+ function handleKeyDown(e) {
91
+ if (e.key === "Enter" || e.key === ",") {
92
+ e.preventDefault();
93
+ addTag(inputValue);
94
+ } else if (e.key === "Backspace" && inputValue === "" && value.length > 0) {
95
+ removeTag(value.length - 1);
96
+ }
97
+ }
98
+ function handleBlur() {
99
+ if (inputValue.trim()) {
100
+ addTag(inputValue);
101
+ }
102
+ }
103
+ const atMax = maxTags !== void 0 && value.length >= maxTags;
104
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
105
+ label && /* @__PURE__ */ jsx(
106
+ "label",
107
+ {
108
+ htmlFor: inputId,
109
+ className: "text-xs font-bold text-on-surface-variant uppercase tracking-widest px-1",
110
+ children: label
111
+ }
112
+ ),
113
+ /* @__PURE__ */ jsxs(
114
+ "div",
115
+ {
116
+ className: "flex flex-wrap gap-1.5 p-2 bg-surface-container-low border border-transparent rounded-xl focus-within:ring-2 focus-within:ring-primary/20 focus-within:border-primary transition-all min-h-12 cursor-text",
117
+ onClick: () => {
118
+ var _a;
119
+ return (_a = inputRef.current) == null ? void 0 : _a.focus();
120
+ },
121
+ children: [
122
+ value.map((tag, index) => /* @__PURE__ */ jsx(
123
+ Tag,
124
+ {
125
+ label: tag,
126
+ size: "sm",
127
+ onRemove: () => removeTag(index)
128
+ },
129
+ tag
130
+ )),
131
+ !atMax && /* @__PURE__ */ jsx(
132
+ "input",
133
+ {
134
+ ref: inputRef,
135
+ id: inputId,
136
+ type: "text",
137
+ value: inputValue,
138
+ onChange: (e) => setInputValue(e.target.value),
139
+ onKeyDown: handleKeyDown,
140
+ onBlur: handleBlur,
141
+ placeholder: value.length === 0 ? placeholder : "",
142
+ className: "flex-1 min-w-24 bg-transparent outline-none text-sm py-1 px-1 placeholder:text-outline/40"
143
+ }
144
+ ),
145
+ atMax && value.length > 0 && /* @__PURE__ */ jsxs("span", { className: "flex-1 min-w-24 text-xs text-on-surface-variant py-1.5 px-1 opacity-60", children: [
146
+ "Max ",
147
+ maxTags,
148
+ " tags"
149
+ ] })
150
+ ]
151
+ }
152
+ )
153
+ ] });
154
+ }
155
+ export {
156
+ Tag,
157
+ TagGroup
158
+ };
@@ -0,0 +1,19 @@
1
+ interface TimePickerProps {
2
+ label?: string;
3
+ value: string;
4
+ onChange: (value: string) => void;
5
+ onBlur?: () => void;
6
+ placeholder?: string;
7
+ hourLabel?: string;
8
+ minuteLabel?: string;
9
+ quickSelectLabel?: string;
10
+ clearLabel?: string;
11
+ confirmLabel?: string;
12
+ required?: boolean;
13
+ error?: string;
14
+ icon?: string;
15
+ name?: string;
16
+ }
17
+ declare function TimePicker({ label, value, onChange, onBlur, placeholder, hourLabel, minuteLabel, quickSelectLabel, clearLabel, confirmLabel, required, error, icon, name, }: TimePickerProps): React.ReactNode;
18
+
19
+ export { TimePicker };
@@ -0,0 +1,222 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useState, useRef, useEffect, useCallback } from "react";
5
+ const QUICK_TIMES = [
6
+ "06:00",
7
+ "07:00",
8
+ "08:00",
9
+ "09:00",
10
+ "10:00",
11
+ "11:00",
12
+ "12:00",
13
+ "13:00",
14
+ "14:00",
15
+ "15:00",
16
+ "16:00",
17
+ "17:00",
18
+ "18:00",
19
+ "19:00",
20
+ "20:00",
21
+ "21:00"
22
+ ];
23
+ function TimePicker({
24
+ label,
25
+ value,
26
+ onChange,
27
+ onBlur,
28
+ placeholder = "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E27\u0E25\u0E32",
29
+ hourLabel = "\u0E0A\u0E31\u0E48\u0E27\u0E42\u0E21\u0E07",
30
+ minuteLabel = "\u0E19\u0E32\u0E17\u0E35",
31
+ quickSelectLabel = "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E23\u0E47\u0E27",
32
+ clearLabel = "\u0E25\u0E49\u0E32\u0E07",
33
+ confirmLabel = "\u0E15\u0E01\u0E25\u0E07",
34
+ required,
35
+ error,
36
+ icon = "schedule",
37
+ name
38
+ }) {
39
+ const [open, setOpen] = useState(false);
40
+ const containerRef = useRef(null);
41
+ const hourRef = useRef(null);
42
+ const minRef = useRef(null);
43
+ const [hour, setHour] = useState(() => value ? parseInt(value.split(":")[0]) : -1);
44
+ const [minute, setMinute] = useState(() => value ? parseInt(value.split(":")[1]) : -1);
45
+ useEffect(() => {
46
+ if (value && value.includes(":")) {
47
+ setHour(parseInt(value.split(":")[0]));
48
+ setMinute(parseInt(value.split(":")[1]));
49
+ }
50
+ }, [value]);
51
+ useEffect(() => {
52
+ if (!open) return;
53
+ function handleClick(e) {
54
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
55
+ setOpen(false);
56
+ onBlur == null ? void 0 : onBlur();
57
+ }
58
+ }
59
+ function handleKeyDown(e) {
60
+ if (e.key === "Escape") {
61
+ setOpen(false);
62
+ onBlur == null ? void 0 : onBlur();
63
+ }
64
+ }
65
+ document.addEventListener("mousedown", handleClick);
66
+ document.addEventListener("keydown", handleKeyDown);
67
+ return () => {
68
+ document.removeEventListener("mousedown", handleClick);
69
+ document.removeEventListener("keydown", handleKeyDown);
70
+ };
71
+ }, [open, onBlur]);
72
+ useEffect(() => {
73
+ if (!open) return;
74
+ requestAnimationFrame(() => {
75
+ if (hourRef.current && hour >= 0) {
76
+ const el = hourRef.current.querySelector(`[data-hour="${hour}"]`);
77
+ el == null ? void 0 : el.scrollIntoView({ block: "center" });
78
+ }
79
+ if (minRef.current && minute >= 0) {
80
+ const el = minRef.current.querySelector(`[data-min="${minute}"]`);
81
+ el == null ? void 0 : el.scrollIntoView({ block: "center" });
82
+ }
83
+ });
84
+ }, [open, hour, minute]);
85
+ const commit = useCallback((h, m) => {
86
+ if (h >= 0 && m >= 0) {
87
+ onChange(`${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}`);
88
+ }
89
+ }, [onChange]);
90
+ const selectHour = useCallback((h) => {
91
+ setHour(h);
92
+ const m = minute >= 0 ? minute : 0;
93
+ setMinute(m);
94
+ commit(h, m);
95
+ }, [minute, commit]);
96
+ const selectMinute = useCallback((m) => {
97
+ setMinute(m);
98
+ const h = hour >= 0 ? hour : 8;
99
+ setHour(h);
100
+ commit(h, m);
101
+ }, [hour, commit]);
102
+ const selectQuick = useCallback((t) => {
103
+ const [h, m] = t.split(":").map(Number);
104
+ setHour(h);
105
+ setMinute(m);
106
+ onChange(t);
107
+ setOpen(false);
108
+ onBlur == null ? void 0 : onBlur();
109
+ }, [onChange, onBlur]);
110
+ const displayValue = hour >= 0 && minute >= 0 ? `${String(hour).padStart(2, "0")}:${String(minute).padStart(2, "0")}` : "";
111
+ const triggerId = label ? `timepicker-${label.replace(/\s+/g, "-").toLowerCase()}` : void 0;
112
+ return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-col gap-2", ref: containerRef, children: [
113
+ name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value }),
114
+ label && /* @__PURE__ */ jsxs("label", { htmlFor: triggerId, className: "text-xs font-bold text-on-surface-variant uppercase tracking-widest px-1", children: [
115
+ label,
116
+ required && /* @__PURE__ */ jsx("span", { className: "text-red-500 ml-0.5", children: "*" })
117
+ ] }),
118
+ /* @__PURE__ */ jsxs(
119
+ "button",
120
+ {
121
+ id: triggerId,
122
+ type: "button",
123
+ "aria-expanded": open,
124
+ "aria-haspopup": "dialog",
125
+ "aria-required": required,
126
+ onClick: () => setOpen(!open),
127
+ className: `relative w-full bg-surface-container-low border rounded-xl py-4 px-6 text-left transition-all font-medium outline-none active:scale-[0.99] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 ${icon ? "pl-12" : ""} ${open ? "bg-surface ring-2 ring-primary/20 border-primary" : error ? "border-red-400 bg-red-50/30" : "border-transparent hover:border-outline-variant"}`,
128
+ children: [
129
+ icon && /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined absolute left-4 top-1/2 -translate-y-1/2 text-on-surface-variant", children: icon }),
130
+ displayValue ? /* @__PURE__ */ jsx("span", { className: "text-on-surface", children: displayValue }) : /* @__PURE__ */ jsx("span", { className: "text-outline/40", children: placeholder })
131
+ ]
132
+ }
133
+ ),
134
+ error && /* @__PURE__ */ jsxs("p", { className: "text-xs text-red-500 px-1 flex items-center gap-1", children: [
135
+ /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined text-sm", children: "error" }),
136
+ error
137
+ ] }),
138
+ open && /* @__PURE__ */ jsxs(
139
+ "div",
140
+ {
141
+ className: "absolute top-full mt-1 z-50 bg-surface rounded-2xl shadow-2xl border border-outline-variant/30 w-75 animate-in fade-in slide-in-from-top-2 duration-150 overflow-hidden",
142
+ children: [
143
+ /* @__PURE__ */ jsxs("div", { className: "flex border-b border-outline-variant/20", children: [
144
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 border-r border-outline-variant/20", children: [
145
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-bold text-on-surface-variant uppercase tracking-widest text-center py-2 bg-surface-variant/30", children: hourLabel }),
146
+ /* @__PURE__ */ jsx("div", { ref: hourRef, className: "h-48 overflow-y-auto scrollbar-hide", children: Array.from({ length: 24 }, (_, i) => /* @__PURE__ */ jsx(
147
+ "button",
148
+ {
149
+ type: "button",
150
+ "data-hour": i,
151
+ onClick: () => selectHour(i),
152
+ className: `w-full py-2.5 text-center text-sm font-semibold transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 ${hour === i ? "bg-primary text-on-primary" : "text-on-surface hover:bg-surface-variant"}`,
153
+ children: String(i).padStart(2, "0")
154
+ },
155
+ i
156
+ )) })
157
+ ] }),
158
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
159
+ /* @__PURE__ */ jsx("div", { className: "text-[10px] font-bold text-on-surface-variant uppercase tracking-widest text-center py-2 bg-surface-variant/30", children: minuteLabel }),
160
+ /* @__PURE__ */ jsx("div", { ref: minRef, className: "h-48 overflow-y-auto scrollbar-hide", children: Array.from({ length: 12 }, (_, i) => i * 5).map((m) => /* @__PURE__ */ jsx(
161
+ "button",
162
+ {
163
+ type: "button",
164
+ "data-min": m,
165
+ onClick: () => selectMinute(m),
166
+ className: `w-full py-2.5 text-center text-sm font-semibold transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 ${minute === m ? "bg-primary text-on-primary" : "text-on-surface hover:bg-surface-variant"}`,
167
+ children: String(m).padStart(2, "0")
168
+ },
169
+ m
170
+ )) })
171
+ ] })
172
+ ] }),
173
+ /* @__PURE__ */ jsxs("div", { className: "p-3", children: [
174
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-bold text-on-surface-variant uppercase tracking-widest mb-2", children: quickSelectLabel }),
175
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-4 gap-1", children: QUICK_TIMES.map((t) => /* @__PURE__ */ jsx(
176
+ "button",
177
+ {
178
+ type: "button",
179
+ onClick: () => selectQuick(t),
180
+ className: `py-1.5 rounded-lg text-xs font-semibold transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 ${displayValue === t ? "bg-primary text-on-primary" : "bg-surface-variant/40 text-on-surface hover:bg-surface-variant"}`,
181
+ children: t
182
+ },
183
+ t
184
+ )) })
185
+ ] }),
186
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-t border-outline-variant/20", children: [
187
+ /* @__PURE__ */ jsx(
188
+ "button",
189
+ {
190
+ type: "button",
191
+ onClick: () => {
192
+ onChange("");
193
+ setHour(-1);
194
+ setMinute(-1);
195
+ setOpen(false);
196
+ onBlur == null ? void 0 : onBlur();
197
+ },
198
+ className: "text-xs font-bold text-on-surface-variant hover:text-red-500 transition-colors px-2 py-1 rounded-lg hover:bg-red-50 active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30",
199
+ children: clearLabel
200
+ }
201
+ ),
202
+ /* @__PURE__ */ jsx(
203
+ "button",
204
+ {
205
+ type: "button",
206
+ onClick: () => {
207
+ setOpen(false);
208
+ onBlur == null ? void 0 : onBlur();
209
+ },
210
+ className: "text-xs font-bold text-primary hover:bg-primary-container transition-colors px-3 py-1.5 rounded-lg active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30",
211
+ children: confirmLabel
212
+ }
213
+ )
214
+ ] })
215
+ ]
216
+ }
217
+ )
218
+ ] });
219
+ }
220
+ export {
221
+ TimePicker
222
+ };
@@ -0,0 +1,15 @@
1
+ interface TimelineItem {
2
+ id: string;
3
+ title: string;
4
+ description?: string;
5
+ timestamp?: string;
6
+ icon?: string;
7
+ color?: "primary" | "emerald" | "amber" | "red" | "slate";
8
+ }
9
+ interface TimelineProps {
10
+ items: TimelineItem[];
11
+ className?: string;
12
+ }
13
+ declare function Timeline({ items, className }: TimelineProps): React.ReactNode;
14
+
15
+ export { Timeline, type TimelineItem };
@@ -0,0 +1,49 @@
1
+ import "./chunk-ORMEWXMH.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ const colorClasses = {
4
+ primary: "bg-primary/10 text-primary",
5
+ emerald: "bg-emerald-100 text-emerald-600",
6
+ amber: "bg-amber-100 text-amber-600",
7
+ red: "bg-red-100 text-red-600",
8
+ slate: "bg-surface-container text-on-surface-variant"
9
+ };
10
+ function Timeline({ items, className = "" }) {
11
+ return /* @__PURE__ */ jsxs("div", { className: `relative ${className}`, children: [
12
+ items.length > 1 && /* @__PURE__ */ jsx(
13
+ "div",
14
+ {
15
+ className: "absolute w-0.5 bg-outline-variant/30",
16
+ style: { left: "15px", top: "16px", bottom: "16px" }
17
+ }
18
+ ),
19
+ /* @__PURE__ */ jsx("div", { className: "space-y-6", children: items.map((item) => {
20
+ var _a, _b;
21
+ const icon = (_a = item.icon) != null ? _a : "radio_button_checked";
22
+ const color = (_b = item.color) != null ? _b : "primary";
23
+ return /* @__PURE__ */ jsxs("div", { className: "flex gap-4 relative", children: [
24
+ /* @__PURE__ */ jsx(
25
+ "div",
26
+ {
27
+ className: `w-8 h-8 rounded-full flex items-center justify-center shrink-0 z-10 ${colorClasses[color]}`,
28
+ children: /* @__PURE__ */ jsx(
29
+ "span",
30
+ {
31
+ className: "material-symbols-outlined",
32
+ style: { fontSize: "16px" },
33
+ children: icon
34
+ }
35
+ )
36
+ }
37
+ ),
38
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 pt-0.5", children: [
39
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-on-surface", children: item.title }),
40
+ item.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-on-surface-variant mt-0.5", children: item.description }),
41
+ item.timestamp && /* @__PURE__ */ jsx("p", { className: "text-xs text-on-surface-variant mt-1", children: item.timestamp })
42
+ ] })
43
+ ] }, item.id);
44
+ }) })
45
+ ] });
46
+ }
47
+ export {
48
+ Timeline
49
+ };
@@ -0,0 +1,18 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface ToastContextValue {
4
+ toast: {
5
+ success: (message: string, duration?: number) => void;
6
+ error: (message: string, duration?: number) => void;
7
+ warning: (message: string, duration?: number) => void;
8
+ info: (message: string, duration?: number) => void;
9
+ };
10
+ }
11
+ declare function ToastProvider({ children, dismissLabel, regionLabel, }: {
12
+ children: React.ReactNode;
13
+ dismissLabel?: string;
14
+ regionLabel?: string;
15
+ }): react_jsx_runtime.JSX.Element;
16
+ declare function useToast(): ToastContextValue;
17
+
18
+ export { ToastProvider, useToast };
package/dist/toast.js ADDED
@@ -0,0 +1,108 @@
1
+ "use client";
2
+ import {
3
+ __spreadProps,
4
+ __spreadValues
5
+ } from "./chunk-ORMEWXMH.js";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { createContext, useCallback, useContext, useMemo, useState, useRef } from "react";
8
+ const ToastContext = createContext(null);
9
+ const ICON_MAP = {
10
+ success: "check_circle",
11
+ error: "error",
12
+ warning: "warning",
13
+ info: "info"
14
+ };
15
+ const COLOR_MAP = {
16
+ success: "bg-emerald-50 border-emerald-200 text-emerald-800",
17
+ error: "bg-red-50 border-red-200 text-red-800",
18
+ warning: "bg-amber-50 border-amber-200 text-amber-800",
19
+ info: "bg-blue-50 border-blue-200 text-blue-800"
20
+ };
21
+ const ICON_COLOR_MAP = {
22
+ success: "text-emerald-500",
23
+ error: "text-red-500",
24
+ warning: "text-amber-500",
25
+ info: "text-blue-500"
26
+ };
27
+ function ToastItemUI({ item, onDismiss, dismissLabel }) {
28
+ return /* @__PURE__ */ jsxs(
29
+ "div",
30
+ {
31
+ className: `flex items-center gap-3 px-4 py-3 rounded-xl border shadow-lg backdrop-blur-sm max-w-sm w-full transition-[opacity,transform] duration-300 ${COLOR_MAP[item.type]} ${item.leaving ? "opacity-0 translate-x-8" : "opacity-100 translate-x-0"}`,
32
+ style: { animation: item.leaving ? void 0 : "slideInRight 0.3s ease-out" },
33
+ children: [
34
+ /* @__PURE__ */ jsx(
35
+ "span",
36
+ {
37
+ "aria-hidden": "true",
38
+ className: `material-symbols-outlined text-xl shrink-0 ${ICON_COLOR_MAP[item.type]}`,
39
+ style: { fontVariationSettings: "'FILL' 1" },
40
+ children: ICON_MAP[item.type]
41
+ }
42
+ ),
43
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium flex-1 leading-relaxed", children: item.message }),
44
+ /* @__PURE__ */ jsx(
45
+ "button",
46
+ {
47
+ type: "button",
48
+ onClick: () => onDismiss(item.id),
49
+ "aria-label": dismissLabel,
50
+ className: "shrink-0 p-0.5 rounded hover:bg-black/5 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary",
51
+ children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "material-symbols-outlined text-base opacity-50", children: "close" })
52
+ }
53
+ )
54
+ ]
55
+ }
56
+ );
57
+ }
58
+ function ToastProvider({
59
+ children,
60
+ dismissLabel = "\u0E1B\u0E34\u0E14",
61
+ regionLabel = "\u0E01\u0E32\u0E23\u0E41\u0E08\u0E49\u0E07\u0E40\u0E15\u0E37\u0E2D\u0E19"
62
+ }) {
63
+ const [toasts, setToasts] = useState([]);
64
+ const counterRef = useRef(0);
65
+ const dismiss = useCallback((id) => {
66
+ setToasts((prev) => prev.map((t) => t.id === id ? __spreadProps(__spreadValues({}, t), { leaving: true }) : t));
67
+ setTimeout(() => {
68
+ setToasts((prev) => prev.filter((t) => t.id !== id));
69
+ }, 300);
70
+ }, []);
71
+ const addToast = useCallback(
72
+ (type, message, duration = 4e3) => {
73
+ const id = `toast-${++counterRef.current}`;
74
+ setToasts((prev) => [...prev.slice(-4), { id, type, message, duration }]);
75
+ setTimeout(() => dismiss(id), duration);
76
+ },
77
+ [dismiss]
78
+ );
79
+ const toast = useMemo(() => ({
80
+ success: (msg, dur) => addToast("success", msg, dur),
81
+ error: (msg, dur) => addToast("error", msg, dur),
82
+ warning: (msg, dur) => addToast("warning", msg, dur),
83
+ info: (msg, dur) => addToast("info", msg, dur)
84
+ }), [addToast]);
85
+ return /* @__PURE__ */ jsxs(ToastContext.Provider, { value: { toast }, children: [
86
+ children,
87
+ /* @__PURE__ */ jsx(
88
+ "div",
89
+ {
90
+ role: "region",
91
+ "aria-live": "polite",
92
+ "aria-atomic": "false",
93
+ "aria-label": regionLabel,
94
+ className: "fixed top-4 right-4 z-[100] flex flex-col gap-2 pointer-events-none",
95
+ children: toasts.map((item) => /* @__PURE__ */ jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx(ToastItemUI, { item, onDismiss: dismiss, dismissLabel }) }, item.id))
96
+ }
97
+ )
98
+ ] });
99
+ }
100
+ function useToast() {
101
+ const ctx = useContext(ToastContext);
102
+ if (!ctx) throw new Error("useToast must be used inside ToastProvider");
103
+ return ctx;
104
+ }
105
+ export {
106
+ ToastProvider,
107
+ useToast
108
+ };
@@ -0,0 +1,12 @@
1
+ interface ToggleSwitchProps {
2
+ checked: boolean;
3
+ onChange: (next: boolean) => void;
4
+ disabled?: boolean;
5
+ size?: "sm" | "md";
6
+ ariaLabel?: string;
7
+ name?: string;
8
+ required?: boolean;
9
+ }
10
+ declare function ToggleSwitch({ checked, onChange, disabled, size, ariaLabel, name, required, }: ToggleSwitchProps): React.ReactNode;
11
+
12
+ export { ToggleSwitch };
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import "./chunk-ORMEWXMH.js";
3
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
4
+ function ToggleSwitch({
5
+ checked,
6
+ onChange,
7
+ disabled = false,
8
+ size = "md",
9
+ ariaLabel,
10
+ name,
11
+ required = false
12
+ }) {
13
+ const dims = size === "sm" ? { track: "h-5 w-9", thumb: "h-4 w-4", on: "translate-x-4", off: "translate-x-0.5" } : { track: "h-6 w-11", thumb: "h-5 w-5", on: "translate-x-5", off: "translate-x-0.5" };
14
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
15
+ name && /* @__PURE__ */ jsx("input", { type: "hidden", name, value: checked ? "1" : "0", required }),
16
+ /* @__PURE__ */ jsx(
17
+ "button",
18
+ {
19
+ type: "button",
20
+ role: "switch",
21
+ "aria-checked": checked,
22
+ "aria-label": ariaLabel,
23
+ "aria-required": required,
24
+ disabled,
25
+ onClick: () => onChange(!checked),
26
+ className: `relative inline-flex ${dims.track} items-center rounded-full transition-colors shrink-0 disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 focus-visible:ring-offset-2 ${checked ? "bg-primary" : "bg-outline"}`,
27
+ children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: `inline-block ${dims.thumb} transform rounded-full bg-white shadow-sm transition-transform ${checked ? dims.on : dims.off}` })
28
+ }
29
+ )
30
+ ] });
31
+ }
32
+ export {
33
+ ToggleSwitch
34
+ };
@@ -0,0 +1,9 @@
1
+ interface TooltipProps {
2
+ content: React.ReactNode;
3
+ children: React.ReactElement;
4
+ placement?: "top" | "bottom" | "left" | "right";
5
+ delay?: number;
6
+ }
7
+ declare function Tooltip({ content, children, placement, delay, }: TooltipProps): React.ReactNode;
8
+
9
+ export { Tooltip };