@suphark/ui 0.6.1 → 0.7.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.
@@ -9,32 +9,49 @@ import {
9
9
  import { MonitorIcon, MoonIcon, SunIcon } from "lucide-react";
10
10
  import { useTheme } from "next-themes";
11
11
  import type { ReactNode } from "react";
12
+ import { useUiText } from "../ui-text";
12
13
  import type { Theme } from "./theme-provider";
13
14
 
14
- const OPTIONS: Array<{ value: Theme; label: string; icon: ReactNode }> = [
15
- { value: "light", label: "สว่าง", icon: <SunIcon /> },
16
- { value: "dark", label: "มืด", icon: <MoonIcon /> },
17
- { value: "system", label: "ตามระบบ", icon: <MonitorIcon /> },
18
- ];
15
+ const ICONS: Record<Theme, ReactNode> = {
16
+ light: <SunIcon />,
17
+ dark: <MoonIcon />,
18
+ system: <MonitorIcon />,
19
+ };
20
+ const ORDER: Theme[] = ["light", "dark", "system"];
19
21
 
20
- /** กลุ่มตัวเลือกธีมสำหรับใส่ใน DropdownMenu (เช่นเมนูผู้ใช้) — ยกจาก Pi Auth มาบน next-themes */
22
+ /**
23
+ * กลุ่มตัวเลือกธีมสำหรับใส่ใน DropdownMenu (เช่นเมนูผู้ใช้) — ยกจาก Pi Auth มาบน next-themes
24
+ * ข้อความค่าเริ่มต้นจาก `UiText.theme`; `onThemeChange` ให้แอปบันทึกธีมเป็น preference ของบัญชี (ซิงก์ข้ามเครื่อง)
25
+ */
21
26
  export function ThemeMenuItems({
22
- label = "ธีม",
27
+ label,
23
28
  labels,
29
+ onThemeChange,
24
30
  }: {
25
31
  label?: ReactNode;
26
- /** ข้อความแต่ละตัวเลือก (ค่าเริ่มต้นภาษาไทย) */
32
+ /** ข้อความแต่ละตัวเลือก (ค่าเริ่มต้นจาก UiText.theme / ไทย) */
27
33
  labels?: Partial<Record<Theme, ReactNode>>;
34
+ /** เรียกหลัง setTheme — เช่น server action บันทึก preference */
35
+ onThemeChange?: (theme: Theme) => void | Promise<void>;
28
36
  }) {
29
37
  const { theme = "system", setTheme } = useTheme();
38
+ const text = useUiText().theme;
30
39
  return (
31
40
  <DropdownMenuGroup>
32
- <DropdownMenuLabel className="text-muted-foreground">{label}</DropdownMenuLabel>
33
- <DropdownMenuRadioGroup value={theme} onValueChange={(v) => setTheme(v as Theme)}>
34
- {OPTIONS.map((o) => (
35
- <DropdownMenuRadioItem key={o.value} value={o.value}>
36
- {o.icon}
37
- {labels?.[o.value] ?? o.label}
41
+ <DropdownMenuLabel className="text-muted-foreground">
42
+ {label ?? text?.label ?? "ธีม"}
43
+ </DropdownMenuLabel>
44
+ <DropdownMenuRadioGroup
45
+ value={theme}
46
+ onValueChange={(v) => {
47
+ setTheme(v as Theme);
48
+ void onThemeChange?.(v as Theme);
49
+ }}
50
+ >
51
+ {ORDER.map((value) => (
52
+ <DropdownMenuRadioItem key={value} value={value}>
53
+ {ICONS[value]}
54
+ {labels?.[value] ?? text?.[value] ?? value}
38
55
  </DropdownMenuRadioItem>
39
56
  ))}
40
57
  </DropdownMenuRadioGroup>
@@ -30,6 +30,23 @@ export interface UiText {
30
30
  chooseTitle?: string;
31
31
  /** DialogsProvider: หัวกล่อง checklist (optional — ค่าเริ่มต้น "เลือกรายการ") */
32
32
  checklistTitle?: string;
33
+ /** CommandPalette (optional — ค่าเริ่มต้นไทยในตัว) */
34
+ palette?: Partial<{
35
+ placeholder: string;
36
+ title: string;
37
+ description: string;
38
+ empty: string;
39
+ searching: string;
40
+ pages: string;
41
+ }>;
42
+ /** OrganizationSwitcher (optional) */
43
+ switcher?: Partial<{ choose: string; organization: string }>;
44
+ /** UserMenu: aria-label ปุ่มเปิดเมนู (optional — "เมนูผู้ใช้") */
45
+ userMenu?: string;
46
+ /** UserMenu: ปุ่มออกจากระบบ (optional — "ออกจากระบบ") */
47
+ signOut?: string;
48
+ /** ThemeMenuItems: หัวกลุ่ม + ตัวเลือก (optional) */
49
+ theme?: Partial<{ label: string; light: string; dark: string; system: string }>;
33
50
  }
34
51
 
35
52
  export const UI_TEXT_TH: UiText = {
@@ -50,6 +67,18 @@ export const UI_TEXT_TH: UiText = {
50
67
  promptTitle: "กรอกข้อมูล",
51
68
  chooseTitle: "เลือกการทำรายการ",
52
69
  checklistTitle: "เลือกรายการ",
70
+ palette: {
71
+ placeholder: "ค้นหาหรือสั่งงาน…",
72
+ title: "ค้นหา",
73
+ description: "พิมพ์เพื่อค้นหาหน้าและข้อมูล",
74
+ empty: "ไม่พบผลลัพธ์",
75
+ searching: "กำลังค้นหา…",
76
+ pages: "หน้า",
77
+ },
78
+ switcher: { choose: "เลือกองค์กร", organization: "องค์กร" },
79
+ userMenu: "เมนูผู้ใช้",
80
+ signOut: "ออกจากระบบ",
81
+ theme: { label: "ธีม", light: "สว่าง", dark: "มืด", system: "ตามระบบ" },
53
82
  };
54
83
 
55
84
  export const UI_TEXT_EN: UiText = {
@@ -70,6 +99,18 @@ export const UI_TEXT_EN: UiText = {
70
99
  promptTitle: "Input required",
71
100
  chooseTitle: "Choose an action",
72
101
  checklistTitle: "Choose options",
102
+ palette: {
103
+ placeholder: "Search or jump to…",
104
+ title: "Search",
105
+ description: "Type to search pages and records",
106
+ empty: "No results",
107
+ searching: "Searching…",
108
+ pages: "Pages",
109
+ },
110
+ switcher: { choose: "Choose organization", organization: "Organization" },
111
+ userMenu: "User menu",
112
+ signOut: "Sign out",
113
+ theme: { label: "Theme", light: "Light", dark: "Dark", system: "System" },
73
114
  };
74
115
 
75
116
  const UiTextContext = createContext<UiText>(UI_TEXT_TH);
package/src/index.ts CHANGED
@@ -15,6 +15,14 @@ export type { DateRange } from "react-day-picker";
15
15
  export { toast } from "sonner";
16
16
  export { ActionForm } from "./components/shared/action-form";
17
17
  export * from "./components/shared/button";
18
+ export {
19
+ type CalendarDayContext,
20
+ type CalendarDayState,
21
+ CalendarMonthGrid,
22
+ type CalendarMonthGridProps,
23
+ WEEKDAYS_EN,
24
+ WEEKDAYS_TH,
25
+ } from "./components/shared/calendar-month-grid";
18
26
  export { ClientRedirect } from "./components/shared/client-redirect";
19
27
  export { Combobox } from "./components/shared/combobox";
20
28
  export {
@@ -60,7 +68,18 @@ export {
60
68
  type SimpleSelectOptions,
61
69
  type SimpleSelectProps,
62
70
  } from "./components/shared/form/simple-select";
63
- export { type FormAction, FormMessage, type FormState } from "./components/shared/form-message";
71
+ export {
72
+ type FormAction,
73
+ FormMessage,
74
+ type FormRenderContext,
75
+ type FormState,
76
+ } from "./components/shared/form-message";
77
+ export {
78
+ IconPicker,
79
+ type IconPickerItem,
80
+ type IconPickerProps,
81
+ type IconPickerText,
82
+ } from "./components/shared/icon-picker";
64
83
  export { Initials, initialsOf } from "./components/shared/initials";
65
84
  export { ErrorState, type RouteError } from "./components/shared/layout/error-state";
66
85
  export {
@@ -77,8 +96,10 @@ export { Notice, type NoticeTone } from "./components/shared/notice";
77
96
  export { type DecimalLike, FormattedNumber } from "./components/shared/number/formatted-number";
78
97
  export { PageHeader } from "./components/shared/page-header";
79
98
  export { PageSkeleton } from "./components/shared/page-skeleton";
99
+ export { ProgressBar, type ProgressTone } from "./components/shared/progress-bar";
80
100
  export { SecretReveal } from "./components/shared/secret-reveal";
81
101
  export { SectionCard } from "./components/shared/section-card";
102
+ export { SelectionButton } from "./components/shared/selection-button";
82
103
  export { Stat } from "./components/shared/stat";
83
104
  export { StatusBadge, statusTone, type Tone } from "./components/shared/status-badge";
84
105
  export { SwitcherChevron } from "./components/shared/switcher-chevron";