@suphark/ui 0.4.2 → 0.4.3
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 +8 -0
- package/README.md +18 -1
- package/package.json +1 -1
- package/src/components/shared/button/confirm-action-button.tsx +1 -1
- package/src/components/shared/confirm-dialog.tsx +2 -0
- package/src/components/shared/row-actions.tsx +1 -1
- package/src/hooks/use-confirm.tsx +63 -0
- package/src/index.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
รูปแบบตาม [Keep a Changelog](https://keepachangelog.com/) · semver: patch = หน้าตา/บั๊ก, minor = component/prop ใหม่, major = เปลี่ยน/ลบ props
|
|
4
4
|
|
|
5
|
+
## [0.4.3] — 2026-09-19
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **`useConfirm()`** (barrel) — `window.confirm` แบบ promise บน ConfirmDialog สำหรับโค้ดฝั่ง client ที่ไม่ใช่ server action (ยกมาจาก DueBell ที่ย้าย window.confirm 8 จุดด้วย hook นี้): `const { confirm, confirmDialog } = useConfirm(); if (!(await confirm({ description, destructive: true }))) return;` แล้ว render `{confirmDialog}`; เรียกซ้ำขณะเปิดอยู่ → คำขอเดิม resolve(false)
|
|
10
|
+
- `ConfirmSpec` object รับ `destructive?` (RowActions/ConfirmActionButton ใช้เป็นตัวทับค่าเดิมของรายการ/variant)
|
|
11
|
+
- README: ตาราง "ยืนยันก่อนทำ — ห้ามใช้ window.confirm" (เลือก RowActions.confirm / ConfirmActionButton / useConfirm / ConfirmDialog) และหมายเหตุ Turbopack ค้าง `Package path ./styles.css is exported … but no valid target file` หลังอัปเดตแพ็กเกจ = cache ของ dev server ให้ลบ `.next`
|
|
12
|
+
|
|
5
13
|
## [0.4.2] — 2026-09-19
|
|
6
14
|
|
|
7
15
|
### Added
|
package/README.md
CHANGED
|
@@ -51,11 +51,15 @@ import { ThemeProvider, Toaster } from "@suphark/ui";
|
|
|
51
51
|
|
|
52
52
|
แอปจะ type-check source ของแพ็กเกจด้วย — แพ็กเกจผ่าน `strict` + `noUncheckedIndexedAccess` + `verbatimModuleSyntax` อยู่แล้ว
|
|
53
53
|
|
|
54
|
+
### หลังอัปเดตเวอร์ชันแพ็กเกจ
|
|
55
|
+
|
|
56
|
+
ถ้า Turbopack ค้าง `Package path ./styles.css is exported … but no valid target file` (หรือ resolve entry อื่นไม่ได้) หลัง `pnpm install` เวอร์ชันใหม่ — เป็น cache ของ dev server ไม่ใช่ปัญหาแพ็กเกจ: หยุด dev server แล้วลบ `.next` ของแอป
|
|
57
|
+
|
|
54
58
|
## การ import
|
|
55
59
|
|
|
56
60
|
```ts
|
|
57
61
|
// ทุกอย่างที่ไม่พึ่ง Next — primitives, shared, theme, hooks, cn
|
|
58
|
-
import { Button, Card, FormFieldInput, StatusBadge, ThemeToggle, cn } from "@suphark/ui";
|
|
62
|
+
import { Button, Card, FormFieldInput, StatusBadge, ThemeToggle, ConfirmDialog, useConfirm, cn } from "@suphark/ui";
|
|
59
63
|
|
|
60
64
|
// ของที่ใช้ next/navigation หรือ next/link + โครงแอป
|
|
61
65
|
import { AppShell, UserMenu, filterNav, FormDialog, RowActions, LinkButton, RefreshButton, PageShell } from "@suphark/ui/next";
|
|
@@ -140,6 +144,19 @@ export default async function Layout({ children }) {
|
|
|
140
144
|
<div style={{ "--brand": tenant.color, "--brand-foreground": "oklch(0.985 0 0)" } as React.CSSProperties}>…</div>
|
|
141
145
|
```
|
|
142
146
|
|
|
147
|
+
### ยืนยันก่อนทำ — ห้ามใช้ `window.confirm`
|
|
148
|
+
|
|
149
|
+
webview/automation บางตัวปิด `window.confirm` ทิ้ง (กดแล้วไม่เกิดอะไร) และไม่เข้า design system — ใช้ 3 ตัวนี้แทน
|
|
150
|
+
|
|
151
|
+
| สถานการณ์ | ใช้ |
|
|
152
|
+
| --- | --- |
|
|
153
|
+
| รายการในเมนู … ท้ายแถว | `RowActions` / `RowMenu` prop `confirm` (string หรือ `{ title, description, confirmLabel }`) |
|
|
154
|
+
| ปุ่มเดี่ยวเรียก server action | `ConfirmActionButton` (`@suphark/ui/next`) — `confirm` + `action` + `data` |
|
|
155
|
+
| โค้ด client อื่น ๆ (fetch/mutation เอง) | `const { confirm, confirmDialog } = useConfirm(); if (!(await confirm({ description, destructive: true }))) return;` แล้ว render `{confirmDialog}` |
|
|
156
|
+
| คุม state เอง | `ConfirmDialog` controlled |
|
|
157
|
+
|
|
158
|
+
ข้อความค่าเริ่มต้น (หัว/ปุ่ม) มาจาก `UiText.confirmTitle` / `UiText.confirm`
|
|
159
|
+
|
|
143
160
|
### สิทธิ์ (ActionButton/LinkButton `permission` prop)
|
|
144
161
|
|
|
145
162
|
แพ็กเกจไม่รู้จักระบบสิทธิ์ของแอป — ครอบด้วย `UiPermissionProvider` แล้วส่งฟังก์ชัน `check(requirement, options)` ของแอปเข้ามา ถ้าไม่ครอบ = อนุญาตทุกอย่าง
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suphark/ui",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Design system กลางของ Suphark apps: shadcn/ui (base-nova, Base UI, Tailwind v4) + shared components + token/ฟอนต์ Noto Sans Thai Looped — ส่งเป็น source ให้แอป Next transpile เอง",
|
|
@@ -36,7 +36,7 @@ export function ConfirmActionButton({
|
|
|
36
36
|
const [open, setOpen] = useState(false);
|
|
37
37
|
const [pending, start] = useTransition();
|
|
38
38
|
const spec = normalizeConfirm(confirm);
|
|
39
|
-
const destructive = variant === "destructive";
|
|
39
|
+
const destructive = spec.destructive ?? variant === "destructive";
|
|
40
40
|
return (
|
|
41
41
|
<>
|
|
42
42
|
<ActionButton
|
|
@@ -22,6 +22,8 @@ export type ConfirmSpec =
|
|
|
22
22
|
description: ReactNode;
|
|
23
23
|
confirmLabel?: string;
|
|
24
24
|
cancelLabel?: string;
|
|
25
|
+
/** ปุ่มยืนยันสีแดง (RowActions ใช้ `destructive` ของรายการเป็นค่าเริ่มต้นอยู่แล้ว) */
|
|
26
|
+
destructive?: boolean;
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
/** แปลง ConfirmSpec เป็น object เสมอ */
|
|
@@ -107,7 +107,7 @@ export function RowActions({ items, label }: { items: RowAction[]; label?: strin
|
|
|
107
107
|
description={spec.description}
|
|
108
108
|
confirmLabel={spec.confirmLabel}
|
|
109
109
|
cancelLabel={spec.cancelLabel}
|
|
110
|
-
destructive={confirming.destructive}
|
|
110
|
+
destructive={spec.destructive ?? confirming.destructive}
|
|
111
111
|
onConfirm={() => {
|
|
112
112
|
const it = confirming;
|
|
113
113
|
setConfirming(null);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ConfirmDialog,
|
|
5
|
+
type ConfirmSpec,
|
|
6
|
+
normalizeConfirm,
|
|
7
|
+
} from "@suphark/ui/components/shared/confirm-dialog";
|
|
8
|
+
import { type ReactNode, useCallback, useState } from "react";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* `window.confirm` แบบ promise บน ConfirmDialog — สำหรับโค้ดฝั่ง client ที่ไม่ใช่ server action
|
|
12
|
+
* (เช่น กดลบแล้วเรียก fetch/mutation เอง) — ยกมาจาก DueBell
|
|
13
|
+
*
|
|
14
|
+
* const { confirm, confirmDialog } = useConfirm();
|
|
15
|
+
* async function remove() {
|
|
16
|
+
* if (!(await confirm({ description: "ลบรายการนี้?", destructive: true }))) return;
|
|
17
|
+
* …
|
|
18
|
+
* }
|
|
19
|
+
* return <>{confirmDialog}<Button onClick={remove}>ลบ</Button></>;
|
|
20
|
+
*
|
|
21
|
+
* ต้อง render `confirmDialog` ในต้นไม้ของ component ที่เรียก hook (ครั้งเดียวต่อ hook)
|
|
22
|
+
* เรียก `confirm` ซ้ำขณะกล่องเปิดอยู่ → คำขอเดิม resolve(false) แล้วแสดงคำขอใหม่แทน
|
|
23
|
+
*/
|
|
24
|
+
export function useConfirm(): {
|
|
25
|
+
confirm: (spec: ConfirmSpec) => Promise<boolean>;
|
|
26
|
+
confirmDialog: ReactNode;
|
|
27
|
+
} {
|
|
28
|
+
const [request, setRequest] = useState<{
|
|
29
|
+
options: Exclude<ConfirmSpec, string>;
|
|
30
|
+
resolve: (ok: boolean) => void;
|
|
31
|
+
} | null>(null);
|
|
32
|
+
|
|
33
|
+
const confirm = useCallback(
|
|
34
|
+
(spec: ConfirmSpec) =>
|
|
35
|
+
new Promise<boolean>((resolve) => {
|
|
36
|
+
setRequest((prev) => {
|
|
37
|
+
prev?.resolve(false);
|
|
38
|
+
return { options: normalizeConfirm(spec), resolve };
|
|
39
|
+
});
|
|
40
|
+
}),
|
|
41
|
+
[],
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
function settle(ok: boolean) {
|
|
45
|
+
request?.resolve(ok);
|
|
46
|
+
setRequest(null);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const confirmDialog = request ? (
|
|
50
|
+
<ConfirmDialog
|
|
51
|
+
open
|
|
52
|
+
onOpenChange={(open) => !open && settle(false)}
|
|
53
|
+
title={request.options.title}
|
|
54
|
+
description={request.options.description}
|
|
55
|
+
confirmLabel={request.options.confirmLabel}
|
|
56
|
+
cancelLabel={request.options.cancelLabel}
|
|
57
|
+
destructive={request.options.destructive}
|
|
58
|
+
onConfirm={() => settle(true)}
|
|
59
|
+
/>
|
|
60
|
+
) : null;
|
|
61
|
+
|
|
62
|
+
return { confirm, confirmDialog };
|
|
63
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -142,6 +142,7 @@ export * from "./components/ui/toggle";
|
|
|
142
142
|
export * from "./components/ui/toggle-group";
|
|
143
143
|
export * from "./components/ui/tooltip";
|
|
144
144
|
// ---------- lib / hooks ----------
|
|
145
|
+
export { useConfirm } from "./hooks/use-confirm";
|
|
145
146
|
export { useCopyToClipboard } from "./hooks/use-copy-to-clipboard";
|
|
146
147
|
export { useIsMobile } from "./hooks/use-mobile";
|
|
147
148
|
export { copyText, selectContents } from "./lib/copy-text";
|