@suphark/ui 0.5.0 → 0.6.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.
@@ -0,0 +1,53 @@
1
+ "use client";
2
+
3
+ import { useSyncExternalStore } from "react";
4
+
5
+ /**
6
+ * นาฬิกากลางต่อ interval — ทุก component ที่ขอ interval เท่ากันแชร์ `setInterval` ตัวเดียว
7
+ * (ตาราง 50 แถวที่แสดง "x นาทีที่แล้ว" เดิมมี 50 timers) — เริ่มเมื่อมีผู้ใช้ตัวแรก หยุดเมื่อตัวสุดท้ายถอด
8
+ */
9
+ type Ticker = { listeners: Set<() => void>; tick: number; handle: number | null };
10
+ const tickers = new Map<number, Ticker>();
11
+
12
+ function getTicker(intervalMs: number): Ticker {
13
+ let t = tickers.get(intervalMs);
14
+ if (!t) {
15
+ t = { listeners: new Set(), tick: 0, handle: null };
16
+ tickers.set(intervalMs, t);
17
+ }
18
+ return t;
19
+ }
20
+
21
+ function subscribe(intervalMs: number, listener: () => void): () => void {
22
+ const t = getTicker(intervalMs);
23
+ t.listeners.add(listener);
24
+ if (t.handle === null) {
25
+ t.handle = window.setInterval(() => {
26
+ t.tick += 1;
27
+ for (const l of t.listeners) l();
28
+ }, intervalMs);
29
+ }
30
+ return () => {
31
+ t.listeners.delete(listener);
32
+ if (t.listeners.size === 0 && t.handle !== null) {
33
+ window.clearInterval(t.handle);
34
+ t.handle = null;
35
+ }
36
+ };
37
+ }
38
+
39
+ const subscribeNoop = () => () => {};
40
+ const zero = () => 0;
41
+
42
+ /**
43
+ * คืนตัวเลขที่เพิ่มทุก `intervalMs` (ใช้เป็น dep ให้คำนวณใหม่) — `0`/undefined = ไม่ tick
44
+ * บน server ได้ 0 เสมอ
45
+ */
46
+ export function useTicker(intervalMs: number | undefined): number {
47
+ const active = typeof intervalMs === "number" && intervalMs > 0;
48
+ return useSyncExternalStore(
49
+ active ? (l) => subscribe(intervalMs, l) : subscribeNoop,
50
+ active ? () => getTicker(intervalMs).tick : zero,
51
+ zero,
52
+ );
53
+ }
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@
13
13
  export type { DateRange } from "react-day-picker";
14
14
  // sonner: `toast()` คู่กับ <Toaster /> — แอปไม่ต้องติดตั้ง sonner เอง
15
15
  export { toast } from "sonner";
16
+ export { ActionForm } from "./components/shared/action-form";
16
17
  export * from "./components/shared/button";
17
18
  export { ClientRedirect } from "./components/shared/client-redirect";
18
19
  export { Combobox } from "./components/shared/combobox";
@@ -51,6 +52,7 @@ export {
51
52
  export { FormFieldRadioCards } from "./components/shared/form/form-field-radio-cards";
52
53
  export { FormFieldRadioGroup } from "./components/shared/form/form-field-radio-group";
53
54
  export { FormFieldSelect, type SelectOption } from "./components/shared/form/form-field-select";
55
+ export { CheckboxField, LabeledField } from "./components/shared/form/labeled-field";
54
56
  export {
55
57
  SimpleSelect,
56
58
  type SimpleSelectGroup,
@@ -58,7 +60,7 @@ export {
58
60
  type SimpleSelectOptions,
59
61
  type SimpleSelectProps,
60
62
  } from "./components/shared/form/simple-select";
61
- export { FormMessage, type FormState } from "./components/shared/form-message";
63
+ export { type FormAction, FormMessage, type FormState } from "./components/shared/form-message";
62
64
  export { Initials, initialsOf } from "./components/shared/initials";
63
65
  export { ErrorState, type RouteError } from "./components/shared/layout/error-state";
64
66
  export {
@@ -156,9 +158,12 @@ export * from "./components/ui/tooltip";
156
158
  export { useConfirm } from "./hooks/use-confirm";
157
159
  export { useCopyToClipboard } from "./hooks/use-copy-to-clipboard";
158
160
  export { useIsMobile } from "./hooks/use-mobile";
161
+ export { useMounted } from "./hooks/use-mounted";
162
+ export { useTicker } from "./hooks/use-ticker";
159
163
  export { useTrackedState } from "./hooks/use-tracked-state";
160
164
  export { copyText, selectContents } from "./lib/copy-text";
161
165
  export { formatRelativeThaiDate, formatThaiFullDate } from "./lib/formatters";
166
+ export { getDateTimeFormat, getNumberFormat } from "./lib/intl-cache";
162
167
  export {
163
168
  type AtomicPermissionRequirement,
164
169
  type CheckPermissionOptions,
@@ -0,0 +1,31 @@
1
+ /**
2
+ * cache ตัว formatter ของ Intl — constructor ของ Intl.NumberFormat/DateTimeFormat แพง (โหลดข้อมูล locale)
3
+ * ตารางที่มีเซลล์ตัวเลข/วันที่หลายร้อยช่องจะสร้างซ้ำทุกเซลล์ทุก render ถ้าไม่ cache
4
+ * key = locale + options (JSON) — options ของเราเป็น object เล็ก ๆ ไม่มี key แปลก
5
+ */
6
+ const numberFormats = new Map<string, Intl.NumberFormat>();
7
+ const dateTimeFormats = new Map<string, Intl.DateTimeFormat>();
8
+
9
+ function keyOf(locale: string, options: object | undefined): string {
10
+ return `${locale}|${options ? JSON.stringify(options) : ""}`;
11
+ }
12
+
13
+ export function getNumberFormat(locale: string, options?: Intl.NumberFormatOptions) {
14
+ const key = keyOf(locale, options);
15
+ let f = numberFormats.get(key);
16
+ if (!f) {
17
+ f = new Intl.NumberFormat(locale, options);
18
+ numberFormats.set(key, f);
19
+ }
20
+ return f;
21
+ }
22
+
23
+ export function getDateTimeFormat(locale: string, options?: Intl.DateTimeFormatOptions) {
24
+ const key = keyOf(locale, options);
25
+ let f = dateTimeFormats.get(key);
26
+ if (!f) {
27
+ f = new Intl.DateTimeFormat(locale, options);
28
+ dateTimeFormats.set(key, f);
29
+ }
30
+ return f;
31
+ }
package/src/next.ts CHANGED
@@ -15,6 +15,7 @@ export {
15
15
  PageBreadcrumb,
16
16
  type PageBreadcrumbItem,
17
17
  } from "./components/shared/navigation/page-breadcrumb";
18
+ export { Stepper, type StepperStep, type StepStatus } from "./components/shared/navigation/stepper";
18
19
  export { type RowAction, type RowActionItem, RowActions } from "./components/shared/row-actions";
19
20
  export { RowMenu, type RowMenuDialog, type RowMenuItem } from "./components/shared/row-menu";
20
21
  export {