gov-layout 1.1.6 → 1.1.7
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/README.md +102 -6
- package/dist/index.js +20 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +20 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# gov-layout
|
|
2
2
|
|
|
3
|
-
Government Layout Components — Staff Sidebar + User Header/Sidebar
|
|
3
|
+
Government Layout Components — Staff Sidebar + User Header/Sidebar + Settings Panel
|
|
4
4
|
|
|
5
5
|
> ใช้คู่กับ `gov-token-css` เพื่อให้สีตรงตาม Design System
|
|
6
6
|
|
|
@@ -16,7 +16,7 @@ npm install gov-layout gov-token-css
|
|
|
16
16
|
|
|
17
17
|
### 1. StaffSidebar (เจ้าหน้าที่)
|
|
18
18
|
|
|
19
|
-
Sidebar ฝั่งซ้ายแบบ fixed สำหรับหน้า admin
|
|
19
|
+
Sidebar ฝั่งซ้ายแบบ fixed สำหรับหน้า admin — รองรับพับ/กาง (collapsible)
|
|
20
20
|
|
|
21
21
|
```tsx
|
|
22
22
|
import { StaffSidebar } from 'gov-layout';
|
|
@@ -56,7 +56,7 @@ const bottomMenu: MenuItem[] = [
|
|
|
56
56
|
currentPath="/services/water"
|
|
57
57
|
onNavigate={(path) => router.push(path)}
|
|
58
58
|
onLogout={() => signOut()}
|
|
59
|
-
|
|
59
|
+
collapsible // ← เปิดโหมดพับ/กาง
|
|
60
60
|
/>
|
|
61
61
|
```
|
|
62
62
|
|
|
@@ -66,12 +66,15 @@ const bottomMenu: MenuItem[] = [
|
|
|
66
66
|
- ✅ Active item highlight
|
|
67
67
|
- ✅ Bottom menu (ตั้งค่าระบบ)
|
|
68
68
|
- ✅ User profile + logout ข้างล่าง
|
|
69
|
+
- ✅ **Collapsible** — พับเป็น icon-only 64px, กางเป็น 280px
|
|
70
|
+
- ปุ่ม toggle อยู่ขอบขวาบน (10%) ของ sidebar
|
|
71
|
+
- Tooltip แสดงชื่อเมนูเมื่อพับ
|
|
69
72
|
|
|
70
73
|
---
|
|
71
74
|
|
|
72
75
|
### 2. UserHeader (ผู้ใช้ทั่วไป)
|
|
73
76
|
|
|
74
|
-
Header ด้านบนพร้อม notification bell
|
|
77
|
+
Header ด้านบนพร้อม notification bell — badge แสดง 99+ เมื่อเกิน 99
|
|
75
78
|
|
|
76
79
|
```tsx
|
|
77
80
|
import { UserHeader } from 'gov-layout';
|
|
@@ -96,6 +99,11 @@ const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
|
|
96
99
|
/>
|
|
97
100
|
```
|
|
98
101
|
|
|
102
|
+
**Features:**
|
|
103
|
+
- ✅ Notification bell พร้อม badge (แสดง 99+ เมื่อเกิน 99)
|
|
104
|
+
- ✅ Notification dropdown แบบ scroll ได้
|
|
105
|
+
- ✅ ปุ่มเปิด sidebar
|
|
106
|
+
|
|
99
107
|
---
|
|
100
108
|
|
|
101
109
|
### 3. UserSidebar (ผู้ใช้ทั่วไป)
|
|
@@ -109,7 +117,7 @@ import { UserSidebar } from 'gov-layout';
|
|
|
109
117
|
isOpen={isSidebarOpen}
|
|
110
118
|
onClose={() => setIsSidebarOpen(false)}
|
|
111
119
|
user={{ firstName: 'Kaimuk', lastName: 'Jakprim', pictureUrl: '/avatar.jpg' }}
|
|
112
|
-
roleLabel="ผู้สูงอายุ"
|
|
120
|
+
roleLabel="ผู้สูงอายุ" // ← กำหนดตาม role ของแต่ละระบบ
|
|
113
121
|
menuItems={[
|
|
114
122
|
{ id: 'profile', title: 'ข้อมูลส่วนตัว', icon: <UserIcon />, path: '/profile' },
|
|
115
123
|
{ id: 'services', title: 'บริการหลัก', icon: <HomeIcon />, path: '/services' },
|
|
@@ -120,6 +128,91 @@ import { UserSidebar } from 'gov-layout';
|
|
|
120
128
|
/>
|
|
121
129
|
```
|
|
122
130
|
|
|
131
|
+
> **หมายเหตุ:** `roleLabel` ไม่ได้ fix ไว้ แต่ละระบบส่งค่าเองได้ เช่น "ผู้สูงอายุ", "ผู้ใช้ปกติ", "อาสาสมัคร"
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### 4. SettingsPanel (ตั้งค่าระบบ) 🆕
|
|
136
|
+
|
|
137
|
+
หน้าตั้งค่าพร้อมปรับขนาดตัวอักษร (5 ระดับ) + โหมดสว่าง/มืด
|
|
138
|
+
|
|
139
|
+
#### ขั้นตอนการใช้งาน
|
|
140
|
+
|
|
141
|
+
**1) ครอบ `SettingsProvider` ที่ root layout:**
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
// app/providers.tsx — ต้องเป็น 'use client'
|
|
145
|
+
'use client';
|
|
146
|
+
import { SettingsProvider } from 'gov-layout';
|
|
147
|
+
|
|
148
|
+
export default function Providers({ children }: { children: React.ReactNode }) {
|
|
149
|
+
return <SettingsProvider>{children}</SettingsProvider>;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// app/layout.tsx
|
|
153
|
+
import Providers from './providers';
|
|
154
|
+
|
|
155
|
+
export default function RootLayout({ children }) {
|
|
156
|
+
return (
|
|
157
|
+
<html lang="th">
|
|
158
|
+
<body>
|
|
159
|
+
<Providers>{children}</Providers>
|
|
160
|
+
</body>
|
|
161
|
+
</html>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**2) วาง `SettingsPanel` ในหน้าตั้งค่า:**
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
import { SettingsPanel } from 'gov-layout';
|
|
170
|
+
|
|
171
|
+
// ผู้ใช้ทั่วไป — ได้ทั้ง font + theme
|
|
172
|
+
<SettingsPanel />
|
|
173
|
+
|
|
174
|
+
// เจ้าหน้าที่ — แค่ปรับขนาดฟอนต์
|
|
175
|
+
<SettingsPanel showTheme={false} />
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**3) ใช้ `useSettings()` hook ถ้าอยากอ่าน/เปลี่ยนค่าเอง:**
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
import { useSettings } from 'gov-layout';
|
|
182
|
+
|
|
183
|
+
function MyComponent() {
|
|
184
|
+
const { theme, toggleTheme, fontSize, setFontSize } = useSettings();
|
|
185
|
+
return <p>ธีม: {theme} | ฟอนต์: {fontSize}</p>;
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### ขนาดตัวอักษร (5 ระดับ)
|
|
190
|
+
|
|
191
|
+
| ค่า | Label | Scale |
|
|
192
|
+
|-----|-------|-------|
|
|
193
|
+
| `xsmall` | เล็กมาก | ×0.8 |
|
|
194
|
+
| `small` | เล็ก | ×0.9 |
|
|
195
|
+
| `medium` | กลาง (default) | ×1.0 |
|
|
196
|
+
| `large` | ใหญ่ | ×1.2 |
|
|
197
|
+
| `xlarge` | ใหญ่มาก | ×1.4 |
|
|
198
|
+
|
|
199
|
+
#### หลักการทำงาน
|
|
200
|
+
|
|
201
|
+
- **Theme** — เพิ่ม/ลบ class `dark` บน `<html>` → ใช้ CSS `html.dark` selector จัดสี
|
|
202
|
+
- **Font size** — ปรับ `body.style.zoom`, `root.style.fontSize`, design token CSS variables ตาม scale
|
|
203
|
+
- ค่าเก็บใน **localStorage** (`app-theme`, `app-font-size`) จำค่าได้เมื่อ reload
|
|
204
|
+
|
|
205
|
+
#### Dark mode CSS ที่ต้องเพิ่มในโปรเจกต์
|
|
206
|
+
|
|
207
|
+
```css
|
|
208
|
+
/* globals.css */
|
|
209
|
+
html.dark body { background-color: #0f172a; color: #f1f5f9; }
|
|
210
|
+
html.dark aside { background-color: #1e293b !important; }
|
|
211
|
+
html.dark header { background-color: #1e293b !important; }
|
|
212
|
+
html.dark h1, html.dark h2, html.dark h3 { color: #f1f5f9 !important; }
|
|
213
|
+
html.dark p { color: #94a3b8 !important; }
|
|
214
|
+
```
|
|
215
|
+
|
|
123
216
|
---
|
|
124
217
|
|
|
125
218
|
## 🔧 Sub-Components
|
|
@@ -177,6 +270,9 @@ interface NotificationItem {
|
|
|
177
270
|
type: 'info' | 'success' | 'warning' | 'error' | 'reminder';
|
|
178
271
|
isRead: boolean;
|
|
179
272
|
}
|
|
273
|
+
|
|
274
|
+
type Theme = 'light' | 'dark';
|
|
275
|
+
type FontSizeKey = 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge';
|
|
180
276
|
```
|
|
181
277
|
|
|
182
278
|
---
|
|
@@ -188,7 +284,7 @@ interface NotificationItem {
|
|
|
188
284
|
export default function AdminLayout({ children }) {
|
|
189
285
|
return (
|
|
190
286
|
<div style={{ display: 'flex' }}>
|
|
191
|
-
<StaffSidebar ... />
|
|
287
|
+
<StaffSidebar collapsible ... />
|
|
192
288
|
<main style={{ marginLeft: '280px', flex: 1 }}>
|
|
193
289
|
{children}
|
|
194
290
|
</main>
|
package/dist/index.js
CHANGED
|
@@ -469,6 +469,23 @@ function ToggleIcon({ isOpen }) {
|
|
|
469
469
|
}
|
|
470
470
|
);
|
|
471
471
|
}
|
|
472
|
+
function HelpCircleIcon() {
|
|
473
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
474
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
475
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
|
|
476
|
+
/* @__PURE__ */ jsxRuntime.jsx("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
|
|
477
|
+
] });
|
|
478
|
+
}
|
|
479
|
+
function GearIcon() {
|
|
480
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
481
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "3" }),
|
|
482
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" })
|
|
483
|
+
] });
|
|
484
|
+
}
|
|
485
|
+
var DEFAULT_BOTTOM_MENU = [
|
|
486
|
+
{ id: "help", title: "\u0E0A\u0E48\u0E27\u0E22\u0E40\u0E2B\u0E25\u0E37\u0E2D", icon: /* @__PURE__ */ jsxRuntime.jsx(HelpCircleIcon, {}), path: "/help" },
|
|
487
|
+
{ id: "settings", title: "\u0E15\u0E31\u0E49\u0E07\u0E04\u0E48\u0E32\u0E23\u0E30\u0E1A\u0E1A", icon: /* @__PURE__ */ jsxRuntime.jsx(GearIcon, {}), path: "/settings" }
|
|
488
|
+
];
|
|
472
489
|
function StaffSidebar({
|
|
473
490
|
orgLogo,
|
|
474
491
|
orgName,
|
|
@@ -491,6 +508,7 @@ function StaffSidebar({
|
|
|
491
508
|
const collapsed = collapsible && !sidebarOpen;
|
|
492
509
|
const collapsedWidth = "64px";
|
|
493
510
|
const currentWidth = collapsed ? collapsedWidth : width;
|
|
511
|
+
const resolvedBottomMenu = bottomMenuItems !== void 0 ? bottomMenuItems : DEFAULT_BOTTOM_MENU;
|
|
494
512
|
const handleToggle = () => {
|
|
495
513
|
if (onToggle) {
|
|
496
514
|
onToggle();
|
|
@@ -536,10 +554,10 @@ function StaffSidebar({
|
|
|
536
554
|
collapsed
|
|
537
555
|
}
|
|
538
556
|
),
|
|
539
|
-
|
|
557
|
+
resolvedBottomMenu && resolvedBottomMenu.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
540
558
|
SidebarMenu,
|
|
541
559
|
{
|
|
542
|
-
menuItems:
|
|
560
|
+
menuItems: resolvedBottomMenu,
|
|
543
561
|
onItemClick: onNavigate,
|
|
544
562
|
currentPath,
|
|
545
563
|
collapsed
|