@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.
- package/CHANGELOG.md +57 -0
- package/package.json +1 -1
- package/src/components/app-shell/app-nav.tsx +24 -8
- package/src/components/data-table/data-table-faceted-filter.tsx +14 -15
- package/src/components/data-table/data-table-row-actions.tsx +2 -1
- package/src/components/data-table/use-data-table.ts +7 -11
- package/src/components/pwa/pull-to-refresh.tsx +11 -5
- package/src/components/shared/action-form.tsx +78 -0
- package/src/components/shared/form/form-field-combobox.tsx +14 -5
- package/src/components/shared/form/form-field-radio-cards.tsx +61 -24
- package/src/components/shared/form/labeled-field.tsx +61 -0
- package/src/components/shared/form-dialog.tsx +1 -3
- package/src/components/shared/form-message.tsx +3 -0
- package/src/components/shared/navigation/stepper.tsx +96 -0
- package/src/components/shared/number/formatted-number.tsx +39 -102
- package/src/components/shared/theme/theme-toggle.tsx +8 -5
- package/src/components/shared/time/client-date-formatter.tsx +47 -154
- package/src/components/ui/relative-date.tsx +30 -9
- package/src/hooks/use-mounted.ts +16 -0
- package/src/hooks/use-ticker.ts +53 -0
- package/src/index.ts +6 -1
- package/src/lib/intl-cache.ts +31 -0
- package/src/next.ts +1 -0
|
@@ -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 {
|