@suphark/ui 0.21.0 → 0.23.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 CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  รูปแบบตาม [Keep a Changelog](https://keepachangelog.com/) · semver: patch = หน้าตา/บั๊ก, minor = component/prop ใหม่, major = เปลี่ยน/ลบ props
4
4
 
5
+ ## [0.23.0] — 2026-10-02
6
+
7
+ ### Added
8
+
9
+ - `ProviderButtons`: prop `badgePlacement` — ค่าเริ่มต้น `"corner"` = badge (เช่น "ใช้ล่าสุด") เป็นป้ายเล็กทับขอบบนขวาของปุ่มแบบ Clerk "Last used" ไม่กินที่ข้อความ; `"inline"` = ท้ายข้อความแบบ 0.21.0; ปุ่มเต็มกว้าง `py-2` กันป้ายชนบรรทัดแรก — ขอจากเจ้าของผ่าน Pi Auth (0.22.0 ที่ publish ไปไม่มีส่วนนี้)
10
+
11
+ ## [0.22.0] — 2026-10-02
12
+
13
+ ### Added
14
+
15
+ - `ProviderButtons`: prop `text` ต่อ provider (ข้อความเต็มของปุ่มแทน "ดำเนินการต่อด้วย {label}" เช่น ชื่อ SSO ยาว "Estar (Microsoft 365)"); ปุ่มเต็มกว้าง (1 ตัว / `layout="list"`) **ขึ้นบรรทัดใหม่แทนตัดเป็น …** ; ตาราง 2 คอลัมน์ยังตัด แต่ `aria-label`/`title` เป็นข้อความเต็ม — ขอจาก Pi Auth
16
+ - showcase: demo `/components/auth-layout` ปุ่ม SSO ใช้ `text` ยาว
17
+
5
18
  ## [0.21.0] — 2026-10-02
6
19
 
7
20
  ตัวสำเร็จรูปของ primitive ใหม่จาก shadcn (0.20.0) — เพิ่มอย่างเดียว
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suphark/ui",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
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 เอง",
@@ -31,6 +31,8 @@ export interface AuthProvider {
31
31
  id: string;
32
32
  /** ชื่อ provider เช่น "Google" — ปุ่มแสดง "ดำเนินการต่อด้วย Google" (UiText.auth.continueWith) */
33
33
  label: string;
34
+ /** ข้อความเต็มของปุ่มแทน "ดำเนินการต่อด้วย {label}" (ทุก layout) เช่น "Estar (Microsoft 365)" */
35
+ text?: ReactNode;
34
36
  /** โลโก้ของ provider (svg) */
35
37
  icon?: ReactNode;
36
38
  onClick?: () => void;
@@ -45,16 +47,20 @@ export interface AuthProvider {
45
47
  * ปุ่มเข้าสู่ระบบด้วย provider (Google / Microsoft / SSO ขององค์กร)
46
48
  * 1 ตัว = ปุ่มเต็มกว้างพร้อมข้อความ · 2 ตัวขึ้นไป = ตาราง 2 คอลัมน์ (`layout="list"` บังคับเรียงลงพร้อมข้อความเต็ม)
47
49
  * `pendingId` = ตัวที่กำลังพาไป (ปุ่มนั้นหมุน ตัวอื่นปิด กันกดซ้ำ)
50
+ * ปุ่มเต็มกว้าง (1 ตัว / list) ข้อความยาวขึ้นบรรทัดใหม่ได้ ไม่ตัดทิ้ง · ตาราง 2 คอลัมน์ตัดด้วย … (ชื่อเต็มอยู่ใน aria-label/title)
48
51
  */
49
52
  export function ProviderButtons({
50
53
  providers,
51
54
  pendingId,
52
55
  layout = "auto",
56
+ badgePlacement = "corner",
53
57
  className,
54
58
  }: {
55
59
  providers: ReadonlyArray<AuthProvider>;
56
60
  pendingId?: string | null;
57
61
  layout?: "auto" | "list";
62
+ /** ตำแหน่ง badge: corner = ป้ายเล็กทับขอบบนขวาแบบ Clerk "Last used" (ไม่กินที่ข้อความ) · inline = ท้ายข้อความ */
63
+ badgePlacement?: "corner" | "inline";
58
64
  className?: string;
59
65
  }) {
60
66
  const text = useUiText().auth;
@@ -62,26 +68,45 @@ export function ProviderButtons({
62
68
  return (
63
69
  <div className={cn("grid gap-2", grid ? "grid-cols-2" : "grid-cols-1", className)}>
64
70
  {providers.map((p) => {
65
- const label = grid
66
- ? p.label
67
- : (text?.continueWith ?? "ดำเนินการต่อด้วย {provider}").replace("{provider}", p.label);
71
+ const full = (text?.continueWith ?? "ดำเนินการต่อด้วย {provider}").replace(
72
+ "{provider}",
73
+ p.label,
74
+ );
75
+ const label = p.text ?? (grid ? p.label : full);
68
76
  const content = (
69
77
  <>
70
78
  {p.icon}
71
- <span className="truncate">{label}</span>
72
- {p.badge && <span className="ml-auto text-label text-muted-foreground">{p.badge}</span>}
79
+ <span className={grid ? "truncate" : "min-w-0 text-balance"}>{label}</span>
80
+ {p.badge &&
81
+ (badgePlacement === "corner" ? (
82
+ <span
83
+ data-slot="provider-badge"
84
+ className="pointer-events-none absolute top-0 right-2 -translate-y-1/2 rounded-full border bg-background px-1.5 py-0.5 text-label leading-none text-muted-foreground"
85
+ >
86
+ {p.badge}
87
+ </span>
88
+ ) : (
89
+ <span className="ml-auto shrink-0 text-label text-muted-foreground">{p.badge}</span>
90
+ ))}
73
91
  </>
74
92
  );
93
+ // ปุ่มเต็มกว้าง: ให้ข้อความยาวขึ้นบรรทัดใหม่ได้ (Button ตั้ง whitespace-nowrap + h-8)
94
+ const buttonClass = cn(
95
+ "w-full",
96
+ !grid && "h-auto min-h-8 py-2 whitespace-normal",
97
+ p.badge && badgePlacement === "corner" && "relative",
98
+ );
99
+ const a11y = grid ? { "aria-label": full, title: full } : {};
75
100
  const busy = pendingId === p.id;
76
101
  const disabled = p.disabled || (pendingId != null && !busy);
77
102
  return p.href ? (
78
103
  <Button
79
104
  key={p.id}
80
105
  variant="outline"
81
- className="w-full"
106
+ className={buttonClass}
82
107
  disabled={disabled}
83
108
  nativeButton={false}
84
- render={<a href={p.href} aria-label={grid ? `${label}` : undefined} />}
109
+ render={<a href={p.href} {...a11y} />}
85
110
  >
86
111
  {content}
87
112
  </Button>
@@ -90,8 +115,9 @@ export function ProviderButtons({
90
115
  key={p.id}
91
116
  type="button"
92
117
  variant="outline"
93
- className="w-full"
118
+ className={buttonClass}
94
119
  disabled={disabled || busy}
120
+ {...a11y}
95
121
  onClick={p.onClick}
96
122
  >
97
123
  {content}