@suphark/ui 0.5.0 → 0.5.1
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/package.json +1 -1
- package/src/components/ui/relative-date.tsx +39 -9
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.5.1] — 2026-09-20
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **`RelativeDate` hydration mismatch (React #418) บน production** — เดิม render "วันนี้/เมื่อวาน" บน server ด้วย "ตอนนี้" ของ server (UTC และ module ที่โหลดไว้ก่อน) ซึ่งต่างจาก browser ได้ทั้งโซนเวลาและข้ามวัน → บน Vercel หน้า `/shell` (ตารางมี RelativeDate) hydrate พังทั้งหน้าและ React render ใหม่ทั้ง tree (เห็น theme script ซ้ำ 2 ตัว) — ตอนนี้คำนวณเฉพาะฝั่ง client หลัง mount (`useSyncExternalStore` guard แบบเดียวกับ `ClientDateFormatter`) ก่อน mount แสดงไอคอน + ช่องว่างขนาดเดิม
|
|
10
|
+
- `RelativeDate`: เอาข้อความเฉพาะ supplychain ("วันที่อ้างอิงราคาจริง") ออกจาก tooltip → prop ใหม่ `titlePrefix` (ค่าเริ่มต้น "วันที่") และ `emptyText`; สีเป็น semantic token (`text-muted-foreground`) แทน `text-slate-*` ตามกฎ design system (className/iconClassName ทับได้เหมือนเดิม)
|
|
11
|
+
- showcase: launch config `showcase-prod` (:3202, `next start` หลัง build) สำหรับตรวจ production build ในเครื่อง
|
|
12
|
+
|
|
5
13
|
## [0.5.0] — 2026-09-19
|
|
6
14
|
|
|
7
15
|
ยกของที่พิสูจน์แล้วจาก Pi Done (todo/kanban) เข้าแพ็กเกจ — ไม่เปลี่ยน props เดิม
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suphark/ui",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
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 เอง",
|
|
@@ -2,39 +2,69 @@
|
|
|
2
2
|
|
|
3
3
|
import { formatRelativeThaiDate, formatThaiFullDate } from "@suphark/ui/lib/formatters";
|
|
4
4
|
import { Calendar } from "lucide-react";
|
|
5
|
+
import { useSyncExternalStore } from "react";
|
|
5
6
|
|
|
6
7
|
interface RelativeDateProps {
|
|
7
8
|
date: Date | string | number | null | undefined;
|
|
8
9
|
showIcon?: boolean;
|
|
9
10
|
className?: string;
|
|
10
11
|
iconClassName?: string;
|
|
12
|
+
/** ข้อความนำหน้าใน tooltip (ค่าเริ่มต้น "วันที่") เช่น "วันที่อ้างอิงราคา" */
|
|
13
|
+
titlePrefix?: string;
|
|
14
|
+
/** ข้อความเมื่อไม่มีวันที่ */
|
|
15
|
+
emptyText?: string;
|
|
11
16
|
}
|
|
12
17
|
|
|
18
|
+
// true เฉพาะฝั่ง client หลัง hydrate — server/ตอน hydrate ได้ false เสมอ
|
|
19
|
+
const subscribeNoop = () => () => {};
|
|
20
|
+
const useMounted = () =>
|
|
21
|
+
useSyncExternalStore(
|
|
22
|
+
subscribeNoop,
|
|
23
|
+
() => true,
|
|
24
|
+
() => false,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* วันที่แบบสัมพัทธ์ภาษาไทย ("วันนี้", "เมื่อวาน", "3 วันที่ผ่านมา") + วันที่เต็มใน tooltip
|
|
29
|
+
* คำนวณเฉพาะฝั่ง client หลัง mount: "ตอนนี้" ของ server (UTC, module โหลดไว้ก่อน) กับของ browser
|
|
30
|
+
* ต่างกันได้ทั้งโซนเวลาและข้ามวัน → ถ้า render บน server จะ hydration mismatch (เจอจริงบน Vercel)
|
|
31
|
+
*/
|
|
13
32
|
export function RelativeDate({
|
|
14
33
|
date,
|
|
15
34
|
showIcon = true,
|
|
16
|
-
className = "flex items-center gap-0.5 text-
|
|
17
|
-
iconClassName = "h-2.5 w-2.5 text-
|
|
35
|
+
className = "flex items-center gap-0.5 font-medium text-muted-foreground",
|
|
36
|
+
iconClassName = "h-2.5 w-2.5 shrink-0 text-muted-foreground/60",
|
|
37
|
+
titlePrefix = "วันที่",
|
|
38
|
+
emptyText = "ไม่ได้ระบุ",
|
|
18
39
|
}: RelativeDateProps) {
|
|
40
|
+
const mounted = useMounted();
|
|
41
|
+
|
|
19
42
|
if (!date) {
|
|
20
43
|
return (
|
|
21
44
|
<span
|
|
22
|
-
className="flex items-center gap-0.5 font-normal text-
|
|
23
|
-
title=
|
|
45
|
+
className="flex items-center gap-0.5 font-normal text-muted-foreground text-xs italic"
|
|
46
|
+
title={`${titlePrefix}: ${emptyText}`}
|
|
24
47
|
>
|
|
25
48
|
{showIcon && <Calendar className={iconClassName} />}
|
|
26
|
-
|
|
49
|
+
{emptyText}
|
|
27
50
|
</span>
|
|
28
51
|
);
|
|
29
52
|
}
|
|
30
53
|
|
|
31
|
-
|
|
32
|
-
|
|
54
|
+
// ก่อน mount แสดงตัวยึดที่ไม่ขึ้นกับเวลา (กัน layout กระโดดน้อยที่สุด)
|
|
55
|
+
if (!mounted) {
|
|
56
|
+
return (
|
|
57
|
+
<span className={className} aria-hidden="true">
|
|
58
|
+
{showIcon && <Calendar className={iconClassName} />}
|
|
59
|
+
|
|
60
|
+
</span>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
33
63
|
|
|
34
64
|
return (
|
|
35
|
-
<span className={className} title={
|
|
65
|
+
<span className={className} title={`${titlePrefix}: ${formatThaiFullDate(date)}`}>
|
|
36
66
|
{showIcon && <Calendar className={iconClassName} />}
|
|
37
|
-
{
|
|
67
|
+
{formatRelativeThaiDate(date)}
|
|
38
68
|
</span>
|
|
39
69
|
);
|
|
40
70
|
}
|