@suphark/ui 0.18.0 → 0.20.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 (82) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/package.json +14 -13
  3. package/scripts/audit.mjs +0 -0
  4. package/src/components/app-shell/app-shell.tsx +15 -9
  5. package/src/components/app-shell/index.ts +1 -0
  6. package/src/components/app-shell/mobile-tab-bar.tsx +104 -0
  7. package/src/components/shared/action-menu.tsx +155 -0
  8. package/src/components/shared/confirm-dialog.tsx +41 -2
  9. package/src/components/shared/count-badge.tsx +41 -0
  10. package/src/components/shared/filter-bar.tsx +108 -0
  11. package/src/components/shared/form/slider-field.tsx +79 -0
  12. package/src/components/shared/form-dialog.tsx +43 -4
  13. package/src/components/shared/month-stepper.tsx +124 -0
  14. package/src/components/shared/save-status.tsx +99 -0
  15. package/src/components/shared/segmented-control.tsx +73 -0
  16. package/src/components/shared/settings-layout.tsx +170 -0
  17. package/src/components/shared/settings-row.tsx +118 -0
  18. package/src/components/shared/setup-checklist.tsx +104 -0
  19. package/src/components/shared/simple-dialog.tsx +172 -0
  20. package/src/components/shared/simple-pagination.tsx +109 -0
  21. package/src/components/shared/simple-table.tsx +93 -0
  22. package/src/components/shared/simple-tabs.tsx +118 -0
  23. package/src/components/shared/simple-tooltip.tsx +87 -0
  24. package/src/components/shared/skip-link.tsx +30 -0
  25. package/src/components/shared/stat.tsx +110 -6
  26. package/src/components/shared/ui-text.tsx +35 -0
  27. package/src/components/shared/user-identity.tsx +61 -0
  28. package/src/components/ui/accordion.tsx +2 -2
  29. package/src/components/ui/alert-dialog.tsx +4 -4
  30. package/src/components/ui/alert.tsx +2 -2
  31. package/src/components/ui/attachment.tsx +9 -9
  32. package/src/components/ui/avatar.tsx +4 -4
  33. package/src/components/ui/badge.tsx +1 -1
  34. package/src/components/ui/breadcrumb.tsx +1 -1
  35. package/src/components/ui/bubble.tsx +123 -0
  36. package/src/components/ui/button-group.tsx +2 -2
  37. package/src/components/ui/button.tsx +6 -6
  38. package/src/components/ui/calendar.tsx +9 -9
  39. package/src/components/ui/card.tsx +3 -3
  40. package/src/components/ui/chart.tsx +2 -2
  41. package/src/components/ui/checkbox.tsx +1 -1
  42. package/src/components/ui/combobox.tsx +6 -6
  43. package/src/components/ui/command.tsx +4 -4
  44. package/src/components/ui/context-menu.tsx +7 -7
  45. package/src/components/ui/dialog.tsx +4 -4
  46. package/src/components/ui/direction.tsx +6 -0
  47. package/src/components/ui/drawer.tsx +10 -10
  48. package/src/components/ui/dropdown-menu.tsx +8 -8
  49. package/src/components/ui/empty.tsx +4 -4
  50. package/src/components/ui/field.tsx +8 -8
  51. package/src/components/ui/hover-card.tsx +1 -2
  52. package/src/components/ui/input-group.tsx +3 -3
  53. package/src/components/ui/input-otp.tsx +1 -1
  54. package/src/components/ui/input.tsx +1 -1
  55. package/src/components/ui/item.tsx +4 -4
  56. package/src/components/ui/kbd.tsx +1 -1
  57. package/src/components/ui/label.tsx +1 -1
  58. package/src/components/ui/marker.tsx +67 -0
  59. package/src/components/ui/menubar.tsx +9 -9
  60. package/src/components/ui/message-scroller.tsx +129 -0
  61. package/src/components/ui/message.tsx +84 -0
  62. package/src/components/ui/native-select.tsx +1 -1
  63. package/src/components/ui/navigation-menu.tsx +6 -6
  64. package/src/components/ui/popover.tsx +1 -1
  65. package/src/components/ui/progress.tsx +2 -3
  66. package/src/components/ui/questionnaire.tsx +314 -0
  67. package/src/components/ui/radio-group.tsx +1 -2
  68. package/src/components/ui/resizable.tsx +1 -1
  69. package/src/components/ui/select.tsx +4 -4
  70. package/src/components/ui/separator.tsx +0 -1
  71. package/src/components/ui/sheet.tsx +3 -3
  72. package/src/components/ui/sidebar.tsx +14 -14
  73. package/src/components/ui/switch.tsx +2 -3
  74. package/src/components/ui/textarea.tsx +1 -1
  75. package/src/components/ui/toast.tsx +221 -0
  76. package/src/components/ui/toggle-group.tsx +2 -2
  77. package/src/components/ui/toggle.tsx +1 -1
  78. package/src/components/ui/tooltip.tsx +0 -1
  79. package/src/hooks/use-confirm.tsx +2 -0
  80. package/src/index.ts +48 -1
  81. package/src/lib/month.ts +60 -0
  82. package/src/styles.css +10 -0
package/src/index.ts CHANGED
@@ -14,6 +14,11 @@ export type { DateRange } from "react-day-picker";
14
14
  // sonner: `toast()` คู่กับ <Toaster /> — แอปไม่ต้องติดตั้ง sonner เอง
15
15
  export { toast } from "sonner";
16
16
  export { ActionForm } from "./components/shared/action-form";
17
+ export {
18
+ ActionMenu,
19
+ type ActionMenuCommand,
20
+ type ActionMenuItem,
21
+ } from "./components/shared/action-menu";
17
22
  export * from "./components/shared/button";
18
23
  export {
19
24
  type CalendarDayContext,
@@ -37,6 +42,7 @@ export {
37
42
  type ConfirmSpec,
38
43
  normalizeConfirm,
39
44
  } from "./components/shared/confirm-dialog";
45
+ export { CountBadge } from "./components/shared/count-badge";
40
46
  export { DataList, DataRow } from "./components/shared/data-list";
41
47
  export { DateRangePicker } from "./components/shared/date/date-range-picker";
42
48
  export { DescriptionList } from "./components/shared/description-list";
@@ -62,6 +68,7 @@ export { EmptyRow } from "./components/shared/empty-row";
62
68
  export { EmptyState } from "./components/shared/empty-state";
63
69
  export { EnvSnippet, randomSecret } from "./components/shared/env-snippet";
64
70
  export { type FileKind, FileTypeIcon, getFileKind } from "./components/shared/file-type-icon";
71
+ export { FilterBar, type FilterBarFilter } from "./components/shared/filter-bar";
65
72
  export { FormFieldBoolean } from "./components/shared/form/form-field-boolean";
66
73
  export { FormFieldColorSelect } from "./components/shared/form/form-field-color-select";
67
74
  export { FormFieldCombobox } from "./components/shared/form/form-field-combobox";
@@ -84,6 +91,7 @@ export {
84
91
  type SimpleSelectOptions,
85
92
  type SimpleSelectProps,
86
93
  } from "./components/shared/form/simple-select";
94
+ export { SliderField, type SliderFieldProps } from "./components/shared/form/slider-field";
87
95
  export {
88
96
  type FormAction,
89
97
  FormMessage,
@@ -107,6 +115,11 @@ export {
107
115
  type LanguageSwitchProps,
108
116
  } from "./components/shared/language/language-switch";
109
117
  export { ErrorState, type RouteError } from "./components/shared/layout/error-state";
118
+ export {
119
+ MonthStepper,
120
+ type MonthStepperProps,
121
+ useMonthCursor,
122
+ } from "./components/shared/month-stepper";
110
123
  export {
111
124
  MultiSelectCombobox,
112
125
  type MultiSelectComboboxProps,
@@ -136,10 +149,26 @@ export {
136
149
  type RuleLeaf,
137
150
  type RuleNot,
138
151
  } from "./components/shared/rule-builder";
152
+ export { type SaveState, SaveStatus, type SaveStatusText } from "./components/shared/save-status";
139
153
  export { SecretReveal } from "./components/shared/secret-reveal";
140
154
  export { SectionCard } from "./components/shared/section-card";
155
+ export { SegmentedControl, type SegmentedOption } from "./components/shared/segmented-control";
141
156
  export { SelectionButton } from "./components/shared/selection-button";
142
- export { Stat } from "./components/shared/stat";
157
+ export { SettingsLayout, type SettingsSection } from "./components/shared/settings-layout";
158
+ export { DangerZone, SettingsRow, SwitchRow } from "./components/shared/settings-row";
159
+ export { SetupChecklist, type SetupChecklistStep } from "./components/shared/setup-checklist";
160
+ export { SimpleDialog, SimpleSheet } from "./components/shared/simple-dialog";
161
+ export { paginationRange, SimplePagination } from "./components/shared/simple-pagination";
162
+ export { SimpleTable, type SimpleTableColumn } from "./components/shared/simple-table";
163
+ export {
164
+ SimpleAccordion,
165
+ type SimpleAccordionItem,
166
+ type SimpleTabItem,
167
+ SimpleTabs,
168
+ } from "./components/shared/simple-tabs";
169
+ export { SimplePopover, SimpleTooltip } from "./components/shared/simple-tooltip";
170
+ export { SkipLink } from "./components/shared/skip-link";
171
+ export { Stat, type StatDelta, StatGrid, type StatTone } from "./components/shared/stat";
143
172
  export { StatusBadge, statusTone, type Tone } from "./components/shared/status-badge";
144
173
  export { SwitcherChevron } from "./components/shared/switcher-chevron";
145
174
  export { CodeBlock, type CodeBlockProps } from "./components/shared/text/code-block";
@@ -164,6 +193,7 @@ export {
164
193
  type UploadListText,
165
194
  type UploadStatus,
166
195
  } from "./components/shared/upload-list";
196
+ export { UserIdentity, type UserIdentityProps } from "./components/shared/user-identity";
167
197
  // ---------- shadcn/ui primitives ----------
168
198
  export * from "./components/ui/accordion";
169
199
  export * from "./components/ui/alert";
@@ -172,7 +202,9 @@ export * from "./components/ui/aspect-ratio";
172
202
  export * from "./components/ui/attachment";
173
203
  export * from "./components/ui/avatar";
174
204
  export * from "./components/ui/badge";
205
+ // toast.tsx (Base UI) ไม่อยู่ใน barrel: ชื่อ toast/Toaster ชนกับ sonner ที่แพ็กเกจใช้ — import ตรงจาก @suphark/ui/components/ui/toast ถ้าต้องการ
175
206
  export * from "./components/ui/breadcrumb";
207
+ export * from "./components/ui/bubble";
176
208
  export * from "./components/ui/button";
177
209
  export * from "./components/ui/button-group";
178
210
  export * from "./components/ui/calendar";
@@ -185,6 +217,7 @@ export * from "./components/ui/command";
185
217
  export * from "./components/ui/context-menu";
186
218
  export * from "./components/ui/date-picker";
187
219
  export * from "./components/ui/dialog";
220
+ export * from "./components/ui/direction";
188
221
  export * from "./components/ui/drawer";
189
222
  export * from "./components/ui/dropdown-menu";
190
223
  export * from "./components/ui/empty";
@@ -197,12 +230,16 @@ export * from "./components/ui/input-otp";
197
230
  export * from "./components/ui/item";
198
231
  export * from "./components/ui/kbd";
199
232
  export * from "./components/ui/label";
233
+ export * from "./components/ui/marker";
200
234
  export * from "./components/ui/menubar";
235
+ export * from "./components/ui/message";
236
+ export * from "./components/ui/message-scroller";
201
237
  export * from "./components/ui/native-select";
202
238
  export * from "./components/ui/navigation-menu";
203
239
  export * from "./components/ui/pagination";
204
240
  export * from "./components/ui/popover";
205
241
  export * from "./components/ui/progress";
242
+ export * from "./components/ui/questionnaire";
206
243
  export * from "./components/ui/radio-group";
207
244
  export * from "./components/ui/relative-date";
208
245
  export * from "./components/ui/resizable";
@@ -233,6 +270,16 @@ export { useTrackedState } from "./hooks/use-tracked-state";
233
270
  export { copyText, selectContents } from "./lib/copy-text";
234
271
  export { formatBytes, formatRelativeThaiDate, formatThaiFullDate } from "./lib/formatters";
235
272
  export { getDateTimeFormat, getNumberFormat } from "./lib/intl-cache";
273
+ export {
274
+ compareMonth,
275
+ currentMonthKey,
276
+ formatMonthKey,
277
+ isMonthKey,
278
+ type MonthKey,
279
+ parseMonthKey,
280
+ shiftMonth,
281
+ toMonthKey,
282
+ } from "./lib/month";
236
283
  export {
237
284
  type AtomicPermissionRequirement,
238
285
  type CheckPermissionOptions,
@@ -0,0 +1,60 @@
1
+ import { getDateTimeFormat } from "./intl-cache";
2
+
3
+ /**
4
+ * เดือนแบบ `"YYYY-MM"` (รูปแบบเดียวกับ `CalendarMonthGrid month`) — ไม่มีวัน/เวลา จึงไม่มีปัญหาโซนเวลา
5
+ * ยกจาก DueBell `CycleMonth` ให้แอปบัญชี/รายงานใช้ร่วม
6
+ */
7
+ export type MonthKey = string;
8
+
9
+ const MONTH_KEY = /^(\d{4})-(\d{2})$/;
10
+
11
+ /** แยก `"YYYY-MM"` → ปี + เดือน (0–11); รูปแบบผิด = throw (ค่าจาก URL ให้เช็กด้วย `isMonthKey` ก่อน) */
12
+ export function parseMonthKey(key: MonthKey): { year: number; monthIndex: number } {
13
+ const m = MONTH_KEY.exec(key);
14
+ const monthIndex = m ? Number(m[2]) - 1 : Number.NaN;
15
+ if (!m || monthIndex < 0 || monthIndex > 11) {
16
+ throw new Error(`month ต้องเป็น "YYYY-MM" ได้ "${key}"`);
17
+ }
18
+ return { year: Number(m[1]), monthIndex };
19
+ }
20
+
21
+ export function isMonthKey(value: unknown): value is MonthKey {
22
+ if (typeof value !== "string") return false;
23
+ const m = MONTH_KEY.exec(value);
24
+ return !!m && Number(m[2]) >= 1 && Number(m[2]) <= 12;
25
+ }
26
+
27
+ /** Date → `"YYYY-MM"` ตามเวลาท้องถิ่นของเครื่อง */
28
+ export function toMonthKey(date: Date): MonthKey {
29
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`;
30
+ }
31
+
32
+ export function currentMonthKey(now: Date = new Date()): MonthKey {
33
+ return toMonthKey(now);
34
+ }
35
+
36
+ /** เลื่อนเดือน (ลบ = ย้อนหลัง) ข้ามปีให้เอง: shiftMonth("2026-01", -1) → "2025-12" */
37
+ export function shiftMonth(key: MonthKey, by: number): MonthKey {
38
+ const { year, monthIndex } = parseMonthKey(key);
39
+ return toMonthKey(new Date(year, monthIndex + by, 1));
40
+ }
41
+
42
+ /** เทียบเดือน: < 0 = a ก่อน b (เทียบเป็นสตริงได้เพราะเติม 0 แล้ว) */
43
+ export function compareMonth(a: MonthKey, b: MonthKey): number {
44
+ return a < b ? -1 : a > b ? 1 : 0;
45
+ }
46
+
47
+ /**
48
+ * ชื่อเดือนสำหรับแสดง — `th-TH` ได้ปี พ.ศ. ตาม Intl (`"ตุลาคม 2569"`), `en-US` ได้ ค.ศ.
49
+ * ต้องการ ค.ศ. ในภาษาไทย ใช้ locale `"th-TH-u-ca-gregory"`
50
+ */
51
+ export function formatMonthKey(
52
+ key: MonthKey,
53
+ locale = "th-TH",
54
+ style: "long" | "short" = "long",
55
+ ): string {
56
+ const { year, monthIndex } = parseMonthKey(key);
57
+ return getDateTimeFormat(locale, { month: style, year: "numeric" }).format(
58
+ new Date(year, monthIndex, 1),
59
+ );
60
+ }
package/src/styles.css CHANGED
@@ -282,6 +282,16 @@
282
282
  }
283
283
  }
284
284
 
285
+ /* ขอบล่างของจอ iPhone (home indicator) — แถบที่ fixed ติดล่าง เช่น MobileTabBar */
286
+ @utility pb-safe {
287
+ padding-bottom: env(safe-area-inset-bottom, 0px);
288
+ }
289
+
290
+ /* เว้นที่ใต้เนื้อหาให้พ้น MobileTabBar (สูง ~4rem + safe-area) — AppShell mobileTabs ใส่ให้เอง */
291
+ @utility pb-tab-bar {
292
+ padding-bottom: calc(5rem + env(safe-area-inset-bottom, 0px));
293
+ }
294
+
285
295
  @utility scrollbar-thin {
286
296
  scrollbar-width: thin;
287
297
  scrollbar-color: var(--scrollbar-thumb) transparent;