@gov-components/layout-header-sidebar 1.0.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/README.md ADDED
@@ -0,0 +1,228 @@
1
+ # Gov Components Layout Header Sidebar
2
+
3
+ Reusable Header and Sidebar layout components designed for Next.js applications, pre-styled with Tailwind CSS. Perfect for government
4
+
5
+ ## Features
6
+
7
+ - **Responsive Sidebar**: Automatically collapses on mobile, togglable on desktop.
8
+ - **Notification System**: Built-in notification bell with dropdown list.
9
+ - **User Profile**: Display user avatar, name, and role.
10
+ - **Tailwind CSS**: Beautifully styled using Tailwind utility classes.
11
+ - **TypeScript**: Fully typed for excellent developer experience.
12
+
13
+ ## Requirements
14
+
15
+ - React 18+
16
+ - Next.js 13+ (App Router recommended)
17
+ - TailwindCSS 3 (Optional, styles are bundled)
18
+
19
+ ## Installation
20
+
21
+ Install the package via npm:
22
+
23
+ ```bash
24
+ npm install @gov-components/layout-header-sidebar @gov-components/design-tokens
25
+ ```
26
+
27
+ ## Setup (Important!)
28
+
29
+ This library relies on `@gov-components/design-tokens` for styling. Ensure you have imported the design tokens in your **root layout** (usually `app/layout.tsx`):
30
+
31
+ ```tsx
32
+ // app/layout.tsx
33
+ import "@gov-components/design-tokens/dist/tokens-core.css"; // Essential styles
34
+ import "@gov-components/design-tokens/dist/typography.css"; // Fonts & Typo
35
+ // ... other imports
36
+ ```
37
+
38
+ If you are using Next.js Transpile Packages (recommended for avoiding build errors):
39
+
40
+ ```js
41
+ // next.config.ts
42
+ const nextConfig = {
43
+ transpilePackages: ['@gov-components/layout-header-sidebar', '@gov-components/design-tokens'],
44
+ };
45
+ ```
46
+
47
+ ## Usage Guide
48
+
49
+ ### 1. Basic Layout Setup
50
+
51
+ Combine `Header` and `SharedSidebar` to create a standard dashboard layout.
52
+
53
+ ```tsx
54
+ // app/page.tsx
55
+ 'use client';
56
+
57
+ import { Header, SharedSidebar, toggleSidebar, MenuItem } from '@gov-components/layout-header-sidebar';
58
+ import { useRouter } from 'next/navigation';
59
+
60
+ export default function Dashboard() {
61
+ const router = useRouter();
62
+
63
+ // 1. Define User Data
64
+ const user = {
65
+ firstName: 'John',
66
+ lastName: 'Doe',
67
+ pictureUrl: 'https://example.com/avatar.jpg',
68
+ role: 'ADMIN' // Optional role badge
69
+ };
70
+
71
+ // 2. Define Menu Items
72
+ const menuItems: MenuItem[] = [
73
+ {
74
+ id: 'dashboard',
75
+ title: 'Dashboard',
76
+ path: '/dashboard',
77
+ icon: <span>📊</span> // Can be any React Node (SVG, Icon component)
78
+ },
79
+ {
80
+ id: 'settings',
81
+ title: 'Settings',
82
+ path: '/settings',
83
+ icon: <span>⚙️</span>
84
+ }
85
+ ];
86
+
87
+ return (
88
+ <div className="min-h-screen bg-gray-50">
89
+ {/* Header Component */}
90
+ <Header
91
+ user={user}
92
+ sidebarId="main-sidebar" // Must match sidebarId in SharedSidebar
93
+ />
94
+
95
+ {/* Sidebar Component */}
96
+ <SharedSidebar
97
+ user={user}
98
+ menuItems={menuItems}
99
+ sidebarId="main-sidebar"
100
+ profilePath="/profile"
101
+ roleLabel="Administrator"
102
+ roleColor="purple" // teal, purple, orange, blue, green
103
+ onNavigate={(path) => router.push(path)}
104
+ onLogout={() => console.log('User logged out')}
105
+ />
106
+
107
+ {/* Main Content Area */}
108
+ <main className="lg:pl-64 pt-20 p-8 transition-all duration-300">
109
+ <h1>Welcome to Dashboard</h1>
110
+ </main>
111
+ </div>
112
+ );
113
+ }
114
+ ```
115
+
116
+ ### 2. Using Notifications
117
+
118
+ The Header component accepts a `notifications` array to display a dropdown.
119
+
120
+ ```tsx
121
+ import { Header, NotificationItem } from '@gov-components/layout-header-sidebar';
122
+
123
+ const notifications: NotificationItem[] = [
124
+ {
125
+ id: 1,
126
+ title: 'System Update',
127
+ description: 'System maintenance scheduled at midnight.',
128
+ date: '10 mins ago',
129
+ type: 'info', // 'info' | 'success' | 'reminder'
130
+ isRead: false
131
+ },
132
+ {
133
+ id: 2,
134
+ title: 'Task Completed',
135
+ description: 'Your export task has finished successfully.',
136
+ date: '1 hour ago',
137
+ type: 'success',
138
+ isRead: true
139
+ }
140
+ ];
141
+
142
+ // In your render:
143
+ <Header
144
+ user={user}
145
+ notifications={notifications}
146
+ />
147
+ ```
148
+
149
+ ### 3. Toggling Sidebar Programmatically
150
+
151
+ You can control the sidebar state from anywhere in your app using the `toggleSidebar` utility.
152
+
153
+ ```tsx
154
+ import { toggleSidebar } from '@gov-components/layout-header-sidebar';
155
+
156
+ // Toggle default sidebar ('sidebar', 'sidebar-overlay')
157
+ <button onClick={() => toggleSidebar()}>Menu</button>
158
+
159
+ // Toggle specific sidebar ID
160
+ <button onClick={() => toggleSidebar('main-sidebar')}>Menu</button>
161
+ ```
162
+
163
+ ## API Reference
164
+
165
+ ### `<Header />`
166
+
167
+ Top navigation bar showing user profile, notification bell, and hamburger menu (mobile).
168
+
169
+ | Prop | Type | Default | Description |
170
+ |------|------|---------|-------------|
171
+ | `user` | `User` | - | User information object. |
172
+ | `notifications` | `NotificationItem[]` | `[]` | List of items for notification dropdown. |
173
+ | `sidebarId` | `string` | `'sidebar'` | ID used to toggle the sidebar. |
174
+ | `overlayId` | `string` | `'sidebar-overlay'` | ID used to toggle the overlay. |
175
+ | `notificationBell` | `ReactNode` | - | Custom component to replace the bell icon. |
176
+
177
+ ### `<SharedSidebar />`
178
+
179
+ Responsive sidebar navigation with user profile section.
180
+
181
+ | Prop | Type | Default | Description |
182
+ |------|------|---------|-------------|
183
+ | `user` | `User` | - | User information object. |
184
+ | `menuItems` | `MenuItem[]` | `[]` | Array of navigation items. |
185
+ | `onNavigate` | `(path: string) => void` | - | Callback when a menu item is clicked. |
186
+ | `onLogout` | `() => void` | - | Callback when logout button is clicked. |
187
+ | `roleLabel` | `string` | - | Text to display below user name (e.g. "Admin"). |
188
+ | `roleColor` | `string` | `'orange'` | Badge color (`teal`, `blue`, `orange`, `purple`, `green`). |
189
+ | `profilePath` | `string` | - | URL path for the user profile page. |
190
+ | `sidebarId` | `string` | `'sidebar'` | DOM ID for the sidebar container. |
191
+ | `overlayId` | `string` | `'sidebar-overlay'` | DOM ID for the backdrop overlay. |
192
+
193
+ ### Type Definitions
194
+
195
+ **User**
196
+ ```ts
197
+ interface User {
198
+ firstName?: string;
199
+ lastName?: string;
200
+ pictureUrl?: string;
201
+ }
202
+ ```
203
+
204
+ **MenuItem**
205
+ ```ts
206
+ interface MenuItem {
207
+ id: string;
208
+ title: string;
209
+ path: string;
210
+ icon?: React.ReactNode;
211
+ }
212
+ ```
213
+
214
+ **NotificationItem**
215
+ ```ts
216
+ interface NotificationItem {
217
+ id: string | number;
218
+ title: string;
219
+ description: string;
220
+ date: string;
221
+ type: 'reminder' | 'success' | 'info';
222
+ isRead?: boolean;
223
+ }
224
+ ```
225
+
226
+ ## License
227
+
228
+ MIT
@@ -0,0 +1,92 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface NotificationItem {
4
+ id: string | number;
5
+ title: string;
6
+ description: string;
7
+ date: string;
8
+ type: 'info' | 'success' | 'warning' | 'error' | 'reminder';
9
+ isRead: boolean;
10
+ }
11
+
12
+ interface MenuItem {
13
+ id: string;
14
+ title: string;
15
+ path: string;
16
+ icon: React.ReactNode;
17
+ }
18
+ interface MenuListProps {
19
+ menuItems: MenuItem[];
20
+ onItemClick: (path: string) => void;
21
+ itemClassName?: string;
22
+ }
23
+ declare function MenuList({ menuItems, onItemClick, itemClassName }: MenuListProps): react_jsx_runtime.JSX.Element;
24
+
25
+ interface User$1 {
26
+ id?: string | number;
27
+ firstName?: string;
28
+ lastName?: string;
29
+ pictureUrl?: string;
30
+ role?: string;
31
+ }
32
+ interface SharedSidebarProps {
33
+ user: User$1 | null;
34
+ menuItems: MenuItem[];
35
+ sidebarId?: string;
36
+ overlayId?: string;
37
+ roleLabel: string;
38
+ roleColor?: string;
39
+ onNavigate: (path: string) => void;
40
+ onLogout: () => void;
41
+ className?: string;
42
+ menuItemClassName?: string;
43
+ logoutButtonClassName?: string;
44
+ userProfileClassName?: string;
45
+ userAvatarClassName?: string;
46
+ userTextClassName?: string;
47
+ }
48
+ declare function SharedSidebar({ user, menuItems, sidebarId, overlayId, roleLabel, roleColor, onNavigate, onLogout, className, menuItemClassName, logoutButtonClassName, userProfileClassName, userAvatarClassName, userTextClassName }: SharedSidebarProps): react_jsx_runtime.JSX.Element;
49
+
50
+ interface User {
51
+ id?: string | number;
52
+ firstName?: string;
53
+ lastName?: string;
54
+ pictureUrl?: string;
55
+ role?: string;
56
+ }
57
+ interface UserProfileProps {
58
+ user: User | null;
59
+ roleLabel: string;
60
+ roleColor?: string;
61
+ className?: string;
62
+ avatarClassName?: string;
63
+ textClassName?: string;
64
+ }
65
+ declare function UserProfile({ user, roleLabel, roleColor, className, avatarClassName, textClassName }: UserProfileProps): react_jsx_runtime.JSX.Element | null;
66
+
67
+ interface LogoutButtonProps {
68
+ onLogout: () => void;
69
+ className?: string;
70
+ }
71
+ declare function LogoutButton({ onLogout, className }: LogoutButtonProps): react_jsx_runtime.JSX.Element;
72
+
73
+ /**
74
+ * Toggle sidebar visibility (Right side)
75
+ * @param sidebarId - ID of the sidebar element
76
+ * @param overlayId - ID of the overlay element
77
+ */
78
+ declare function toggleSidebar(sidebarId?: string, overlayId?: string): void;
79
+ /**
80
+ * Close sidebar (Right side)
81
+ * @param sidebarId - ID of the sidebar element
82
+ * @param overlayId - ID of the overlay element
83
+ */
84
+ declare function closeSidebar(sidebarId?: string, overlayId?: string): void;
85
+ /**
86
+ * Open sidebar (Right side)
87
+ * @param sidebarId - ID of the sidebar element
88
+ * @param overlayId - ID of the overlay element
89
+ */
90
+ declare function openSidebar(sidebarId?: string, overlayId?: string): void;
91
+
92
+ export { LogoutButton, type MenuItem, MenuList, type NotificationItem, SharedSidebar, UserProfile, closeSidebar, openSidebar, toggleSidebar };
@@ -0,0 +1,92 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ interface NotificationItem {
4
+ id: string | number;
5
+ title: string;
6
+ description: string;
7
+ date: string;
8
+ type: 'info' | 'success' | 'warning' | 'error' | 'reminder';
9
+ isRead: boolean;
10
+ }
11
+
12
+ interface MenuItem {
13
+ id: string;
14
+ title: string;
15
+ path: string;
16
+ icon: React.ReactNode;
17
+ }
18
+ interface MenuListProps {
19
+ menuItems: MenuItem[];
20
+ onItemClick: (path: string) => void;
21
+ itemClassName?: string;
22
+ }
23
+ declare function MenuList({ menuItems, onItemClick, itemClassName }: MenuListProps): react_jsx_runtime.JSX.Element;
24
+
25
+ interface User$1 {
26
+ id?: string | number;
27
+ firstName?: string;
28
+ lastName?: string;
29
+ pictureUrl?: string;
30
+ role?: string;
31
+ }
32
+ interface SharedSidebarProps {
33
+ user: User$1 | null;
34
+ menuItems: MenuItem[];
35
+ sidebarId?: string;
36
+ overlayId?: string;
37
+ roleLabel: string;
38
+ roleColor?: string;
39
+ onNavigate: (path: string) => void;
40
+ onLogout: () => void;
41
+ className?: string;
42
+ menuItemClassName?: string;
43
+ logoutButtonClassName?: string;
44
+ userProfileClassName?: string;
45
+ userAvatarClassName?: string;
46
+ userTextClassName?: string;
47
+ }
48
+ declare function SharedSidebar({ user, menuItems, sidebarId, overlayId, roleLabel, roleColor, onNavigate, onLogout, className, menuItemClassName, logoutButtonClassName, userProfileClassName, userAvatarClassName, userTextClassName }: SharedSidebarProps): react_jsx_runtime.JSX.Element;
49
+
50
+ interface User {
51
+ id?: string | number;
52
+ firstName?: string;
53
+ lastName?: string;
54
+ pictureUrl?: string;
55
+ role?: string;
56
+ }
57
+ interface UserProfileProps {
58
+ user: User | null;
59
+ roleLabel: string;
60
+ roleColor?: string;
61
+ className?: string;
62
+ avatarClassName?: string;
63
+ textClassName?: string;
64
+ }
65
+ declare function UserProfile({ user, roleLabel, roleColor, className, avatarClassName, textClassName }: UserProfileProps): react_jsx_runtime.JSX.Element | null;
66
+
67
+ interface LogoutButtonProps {
68
+ onLogout: () => void;
69
+ className?: string;
70
+ }
71
+ declare function LogoutButton({ onLogout, className }: LogoutButtonProps): react_jsx_runtime.JSX.Element;
72
+
73
+ /**
74
+ * Toggle sidebar visibility (Right side)
75
+ * @param sidebarId - ID of the sidebar element
76
+ * @param overlayId - ID of the overlay element
77
+ */
78
+ declare function toggleSidebar(sidebarId?: string, overlayId?: string): void;
79
+ /**
80
+ * Close sidebar (Right side)
81
+ * @param sidebarId - ID of the sidebar element
82
+ * @param overlayId - ID of the overlay element
83
+ */
84
+ declare function closeSidebar(sidebarId?: string, overlayId?: string): void;
85
+ /**
86
+ * Open sidebar (Right side)
87
+ * @param sidebarId - ID of the sidebar element
88
+ * @param overlayId - ID of the overlay element
89
+ */
90
+ declare function openSidebar(sidebarId?: string, overlayId?: string): void;
91
+
92
+ export { LogoutButton, type MenuItem, MenuList, type NotificationItem, SharedSidebar, UserProfile, closeSidebar, openSidebar, toggleSidebar };
package/dist/index.js ADDED
@@ -0,0 +1,214 @@
1
+ 'use strict';
2
+
3
+ require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ // src/header/Header.tsx
7
+
8
+ // src/sidebar/utils.ts
9
+ function toggleSidebar(sidebarId = "sidebar", overlayId = "sidebar-overlay") {
10
+ const sidebar = document.getElementById(sidebarId);
11
+ const overlay = document.getElementById(overlayId);
12
+ if (sidebar && overlay) {
13
+ const isOpen = sidebar.classList.contains("translate-x-0");
14
+ if (isOpen) {
15
+ sidebar.classList.remove("translate-x-0");
16
+ sidebar.classList.add("translate-x-full");
17
+ overlay.classList.add("hidden");
18
+ } else {
19
+ sidebar.classList.remove("translate-x-full");
20
+ sidebar.classList.add("translate-x-0");
21
+ overlay.classList.remove("hidden");
22
+ }
23
+ }
24
+ }
25
+ function closeSidebar(sidebarId = "sidebar", overlayId = "sidebar-overlay") {
26
+ const sidebar = document.getElementById(sidebarId);
27
+ const overlay = document.getElementById(overlayId);
28
+ if (sidebar) {
29
+ sidebar.classList.remove("translate-x-0");
30
+ sidebar.classList.add("translate-x-full");
31
+ }
32
+ if (overlay) {
33
+ overlay.classList.add("hidden");
34
+ }
35
+ }
36
+ function openSidebar(sidebarId = "sidebar", overlayId = "sidebar-overlay") {
37
+ const sidebar = document.getElementById(sidebarId);
38
+ const overlay = document.getElementById(overlayId);
39
+ if (sidebar) {
40
+ sidebar.classList.remove("translate-x-full");
41
+ sidebar.classList.add("translate-x-0");
42
+ }
43
+ if (overlay) {
44
+ overlay.classList.remove("hidden");
45
+ }
46
+ }
47
+ function UserProfile({
48
+ user,
49
+ roleLabel,
50
+ roleColor = "orange",
51
+ className = "",
52
+ avatarClassName = "",
53
+ textClassName = ""
54
+ }) {
55
+ if (!user) return null;
56
+ const getFullName = () => {
57
+ if (user.firstName && user.lastName) {
58
+ return `${user.firstName} ${user.lastName}`;
59
+ }
60
+ return user.firstName || user.lastName || "\u0E1C\u0E39\u0E49\u0E43\u0E0A\u0E49";
61
+ };
62
+ const getInitial = () => {
63
+ return user.firstName?.charAt(0) || user.lastName?.charAt(0) || "?";
64
+ };
65
+ const getRoleColorClass = () => {
66
+ const colors = {
67
+ orange: "bg-orange-500",
68
+ teal: "bg-teal-500",
69
+ blue: "bg-blue-500",
70
+ green: "bg-green-500",
71
+ purple: "bg-purple-500"
72
+ };
73
+ return colors[roleColor] || "bg-orange-500";
74
+ };
75
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `p-5 border-b border-gray-200 ${className}`, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
76
+ user.pictureUrl ? /* @__PURE__ */ jsxRuntime.jsx(
77
+ "img",
78
+ {
79
+ src: user.pictureUrl,
80
+ alt: getFullName(),
81
+ width: 56,
82
+ height: 56,
83
+ className: `w-14 h-14 rounded-full shadow-lg object-cover ${avatarClassName}`
84
+ }
85
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-14 h-14 bg-gradient-to-br from-teal-400 to-teal-600 rounded-full flex items-center justify-center text-white font-bold shadow-lg text-xl ${avatarClassName}`, children: getInitial() }),
86
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex-1 min-w-0 ${textClassName}`, children: [
87
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-bold text-gray-900 truncate text-base", children: getFullName() }),
88
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-gray-500 flex items-center gap-1", children: [
89
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `w-2 h-2 ${getRoleColorClass()} rounded-full` }),
90
+ roleLabel
91
+ ] })
92
+ ] })
93
+ ] }) });
94
+ }
95
+ function MenuList({ menuItems, onItemClick, itemClassName = "" }) {
96
+ return /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "flex-1 p-5 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-1", children: menuItems.map((item) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
97
+ "button",
98
+ {
99
+ onClick: () => onItemClick(item.path),
100
+ className: `w-full flex items-center gap-4 px-4 py-3.5 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors ${itemClassName}`,
101
+ children: [
102
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-8 h-8 flex items-center justify-center text-gray-600 flex-shrink-0", children: item.icon }),
103
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-left font-medium text-base", children: item.title })
104
+ ]
105
+ }
106
+ ) }, item.id)) }) });
107
+ }
108
+ function LogoutButton({ onLogout, className = "" }) {
109
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-5 border-t border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsxs(
110
+ "button",
111
+ {
112
+ onClick: onLogout,
113
+ className: `w-full flex items-center gap-4 px-4 py-3.5 rounded-lg text-red-600 hover:bg-red-50 transition-colors ${className}`,
114
+ children: [
115
+ /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "w-5 h-5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx(
116
+ "path",
117
+ {
118
+ strokeLinecap: "round",
119
+ strokeLinejoin: "round",
120
+ strokeWidth: 2,
121
+ d: "M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
122
+ }
123
+ ) }),
124
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 text-left font-medium text-base", children: "\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A" })
125
+ ]
126
+ }
127
+ ) });
128
+ }
129
+ function SharedSidebar({
130
+ user,
131
+ menuItems,
132
+ sidebarId = "sidebar",
133
+ overlayId = "sidebar-overlay",
134
+ roleLabel,
135
+ roleColor = "orange",
136
+ onNavigate,
137
+ onLogout,
138
+ // Styles
139
+ className = "",
140
+ menuItemClassName = "",
141
+ logoutButtonClassName = "",
142
+ userProfileClassName = "",
143
+ userAvatarClassName = "",
144
+ userTextClassName = ""
145
+ }) {
146
+ const closeSidebar2 = () => {
147
+ const sidebar = document.getElementById(sidebarId);
148
+ if (sidebar) {
149
+ sidebar.classList.add("translate-x-full");
150
+ sidebar.classList.remove("translate-x-0");
151
+ }
152
+ const overlay = document.getElementById(overlayId);
153
+ if (overlay) {
154
+ overlay.classList.add("hidden");
155
+ }
156
+ };
157
+ const handleMenuItemClick = (path) => {
158
+ onNavigate(path);
159
+ closeSidebar2();
160
+ };
161
+ const handleLogout = () => {
162
+ closeSidebar2();
163
+ onLogout();
164
+ };
165
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
166
+ /* @__PURE__ */ jsxRuntime.jsx(
167
+ "div",
168
+ {
169
+ id: overlayId,
170
+ className: "fixed inset-0 bg-black/50 z-40 hidden",
171
+ onClick: closeSidebar2
172
+ }
173
+ ),
174
+ /* @__PURE__ */ jsxRuntime.jsxs(
175
+ "aside",
176
+ {
177
+ id: sidebarId,
178
+ className: `fixed top-0 right-0 h-full w-[80vw] max-w-80 bg-white shadow-2xl z-50 transform translate-x-full transition-transform duration-300 flex flex-col ${className}`,
179
+ children: [
180
+ /* @__PURE__ */ jsxRuntime.jsx(
181
+ UserProfile,
182
+ {
183
+ user,
184
+ roleLabel,
185
+ roleColor,
186
+ className: userProfileClassName,
187
+ avatarClassName: userAvatarClassName,
188
+ textClassName: userTextClassName
189
+ }
190
+ ),
191
+ /* @__PURE__ */ jsxRuntime.jsx(
192
+ MenuList,
193
+ {
194
+ menuItems,
195
+ onItemClick: handleMenuItemClick,
196
+ itemClassName: menuItemClassName
197
+ }
198
+ ),
199
+ /* @__PURE__ */ jsxRuntime.jsx(LogoutButton, { onLogout: handleLogout, className: logoutButtonClassName })
200
+ ]
201
+ }
202
+ )
203
+ ] });
204
+ }
205
+
206
+ exports.LogoutButton = LogoutButton;
207
+ exports.MenuList = MenuList;
208
+ exports.SharedSidebar = SharedSidebar;
209
+ exports.UserProfile = UserProfile;
210
+ exports.closeSidebar = closeSidebar;
211
+ exports.openSidebar = openSidebar;
212
+ exports.toggleSidebar = toggleSidebar;
213
+ //# sourceMappingURL=index.js.map
214
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sidebar/utils.ts","../src/sidebar/UserProfile.tsx","../src/sidebar/MenuList.tsx","../src/sidebar/LogoutButton.tsx","../src/sidebar/SharedSidebar.tsx"],"names":["jsx","jsxs","closeSidebar","Fragment"],"mappings":";;;;;;;;AAKO,SAAS,aAAA,CAAc,SAAA,GAAoB,SAAA,EAAW,SAAA,GAAoB,iBAAA,EAAmB;AAChG,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,SAAS,CAAA;AACjD,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,SAAS,CAAA;AAEjD,EAAA,IAAI,WAAW,OAAA,EAAS;AACpB,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,SAAA,CAAU,QAAA,CAAS,eAAe,CAAA;AAEzD,IAAA,IAAI,MAAA,EAAQ;AAER,MAAA,OAAA,CAAQ,SAAA,CAAU,OAAO,eAAe,CAAA;AACxC,MAAA,OAAA,CAAQ,SAAA,CAAU,IAAI,kBAAkB,CAAA;AACxC,MAAA,OAAA,CAAQ,SAAA,CAAU,IAAI,QAAQ,CAAA;AAAA,IAClC,CAAA,MAAO;AAEH,MAAA,OAAA,CAAQ,SAAA,CAAU,OAAO,kBAAkB,CAAA;AAC3C,MAAA,OAAA,CAAQ,SAAA,CAAU,IAAI,eAAe,CAAA;AACrC,MAAA,OAAA,CAAQ,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,IACrC;AAAA,EACJ;AACJ;AAOO,SAAS,YAAA,CAAa,SAAA,GAAoB,SAAA,EAAW,SAAA,GAAoB,iBAAA,EAAmB;AAC/F,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,SAAS,CAAA;AACjD,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,SAAS,CAAA;AAEjD,EAAA,IAAI,OAAA,EAAS;AACT,IAAA,OAAA,CAAQ,SAAA,CAAU,OAAO,eAAe,CAAA;AACxC,IAAA,OAAA,CAAQ,SAAA,CAAU,IAAI,kBAAkB,CAAA;AAAA,EAC5C;AAEA,EAAA,IAAI,OAAA,EAAS;AACT,IAAA,OAAA,CAAQ,SAAA,CAAU,IAAI,QAAQ,CAAA;AAAA,EAClC;AACJ;AAOO,SAAS,WAAA,CAAY,SAAA,GAAoB,SAAA,EAAW,SAAA,GAAoB,iBAAA,EAAmB;AAC9F,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,SAAS,CAAA;AACjD,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,SAAS,CAAA;AAEjD,EAAA,IAAI,OAAA,EAAS;AACT,IAAA,OAAA,CAAQ,SAAA,CAAU,OAAO,kBAAkB,CAAA;AAC3C,IAAA,OAAA,CAAQ,SAAA,CAAU,IAAI,eAAe,CAAA;AAAA,EACzC;AAEA,EAAA,IAAI,OAAA,EAAS;AACT,IAAA,OAAA,CAAQ,SAAA,CAAU,OAAO,QAAQ,CAAA;AAAA,EACrC;AACJ;AC1Ce,SAAR,WAAA,CAA6B;AAAA,EAChC,IAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA,GAAY,QAAA;AAAA,EACZ,SAAA,GAAY,EAAA;AAAA,EACZ,eAAA,GAAkB,EAAA;AAAA,EAClB,aAAA,GAAgB;AACpB,CAAA,EAAqB;AACjB,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAElB,EAAA,MAAM,cAAc,MAAM;AACtB,IAAA,IAAI,IAAA,CAAK,SAAA,IAAa,IAAA,CAAK,QAAA,EAAU;AACjC,MAAA,OAAO,CAAA,EAAG,IAAA,CAAK,SAAS,CAAA,CAAA,EAAI,KAAK,QAAQ,CAAA,CAAA;AAAA,IAC7C;AACA,IAAA,OAAO,IAAA,CAAK,SAAA,IAAa,IAAA,CAAK,QAAA,IAAY,sCAAA;AAAA,EAC9C,CAAA;AAEA,EAAA,MAAM,aAAa,MAAM;AACrB,IAAA,OAAO,IAAA,CAAK,WAAW,MAAA,CAAO,CAAC,KAAK,IAAA,CAAK,QAAA,EAAU,MAAA,CAAO,CAAC,CAAA,IAAK,GAAA;AAAA,EACpE,CAAA;AAEA,EAAA,MAAM,oBAAoB,MAAM;AAC5B,IAAA,MAAM,MAAA,GAAiC;AAAA,MACnC,MAAA,EAAQ,eAAA;AAAA,MACR,IAAA,EAAM,aAAA;AAAA,MACN,IAAA,EAAM,aAAA;AAAA,MACN,KAAA,EAAO,cAAA;AAAA,MACP,MAAA,EAAQ;AAAA,KACZ;AACA,IAAA,OAAO,MAAA,CAAO,SAAS,CAAA,IAAK,eAAA;AAAA,EAChC,CAAA;AAEA,EAAA,uBACIA,cAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,CAAA,6BAAA,EAAgC,SAAS,CAAA,CAAA,EACrD,QAAA,kBAAAC,eAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yBAAA,EACV,QAAA,EAAA;AAAA,IAAA,IAAA,CAAK,6BACFD,cAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACG,KAAK,IAAA,CAAK,UAAA;AAAA,QACV,KAAK,WAAA,EAAY;AAAA,QACjB,KAAA,EAAO,EAAA;AAAA,QACP,MAAA,EAAQ,EAAA;AAAA,QACR,SAAA,EAAW,iDAAiD,eAAe,CAAA;AAAA;AAAA,KAC/E,mBAEAA,cAAAA,CAAC,KAAA,EAAA,EAAI,WAAW,CAAA,2IAAA,EAA8I,eAAe,CAAA,CAAA,EACxK,QAAA,EAAA,UAAA,EAAW,EAChB,CAAA;AAAA,oBAEJC,eAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAW,CAAA,eAAA,EAAkB,aAAa,CAAA,CAAA,EAC3C,QAAA,EAAA;AAAA,sBAAAD,cAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,4CAAA,EACR,uBAAY,EACjB,CAAA;AAAA,sBACAC,eAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,+CAAA,EACT,QAAA,EAAA;AAAA,wBAAAD,eAAC,MAAA,EAAA,EAAK,SAAA,EAAW,CAAA,QAAA,EAAW,iBAAA,EAAmB,CAAA,aAAA,CAAA,EAAiB,CAAA;AAAA,QAC/D;AAAA,OAAA,EACL;AAAA,KAAA,EACJ;AAAA,GAAA,EACJ,CAAA,EACJ,CAAA;AAER;ACjEe,SAAR,SAA0B,EAAE,SAAA,EAAW,WAAA,EAAa,aAAA,GAAgB,IAAG,EAAkB;AAC5F,EAAA,uBACIA,cAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,4BAAA,EACX,0BAAAA,cAAAA,CAAC,IAAA,EAAA,EAAG,SAAA,EAAU,WAAA,EACT,oBAAU,GAAA,CAAI,CAAC,yBACZA,cAAAA,CAAC,QACG,QAAA,kBAAAC,eAAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACG,OAAA,EAAS,MAAM,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA;AAAA,MACpC,SAAA,EAAW,0GAA0G,aAAa,CAAA,CAAA;AAAA,MAElI,QAAA,EAAA;AAAA,wBAAAD,cAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,sEAAA,EAAwE,eAAK,IAAA,EAAK,CAAA;AAAA,wBACjGA,cAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,wCAAA,EACX,eAAK,KAAA,EACV;AAAA;AAAA;AAAA,GACJ,EAAA,EATK,IAAA,CAAK,EAUd,CACH,GACL,CAAA,EACJ,CAAA;AAER;AC5Be,SAAR,YAAA,CAA8B,EAAE,QAAA,EAAU,SAAA,GAAY,IAAG,EAAsB;AAClF,EAAA,uBACIA,cAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,gCACX,QAAA,kBAAAC,eAAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACG,OAAA,EAAS,QAAA;AAAA,MACT,SAAA,EAAW,wGAAwG,SAAS,CAAA,CAAA;AAAA,MAE5H,QAAA,EAAA;AAAA,wBAAAD,cAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,SAAA,EAAU,OAAA,EAAQ,aAAY,IAAA,EAAK,MAAA,EAAO,MAAA,EAAO,cAAA,EAC5D,QAAA,kBAAAA,cAAAA;AAAA,UAAC,MAAA;AAAA,UAAA;AAAA,YACG,aAAA,EAAc,OAAA;AAAA,YACd,cAAA,EAAe,OAAA;AAAA,YACf,WAAA,EAAa,CAAA;AAAA,YACb,CAAA,EAAE;AAAA;AAAA,SACN,EACJ,CAAA;AAAA,wBACAA,cAAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,0CAAyC,QAAA,EAAA,8DAAA,EAAU;AAAA;AAAA;AAAA,GACvE,EACJ,CAAA;AAER;ACUe,SAAR,aAAA,CAA+B;AAAA,EAClC,IAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA,GAAY,SAAA;AAAA,EACZ,SAAA,GAAY,iBAAA;AAAA,EAEZ,SAAA;AAAA,EACA,SAAA,GAAY,QAAA;AAAA,EACZ,UAAA;AAAA,EACA,QAAA;AAAA;AAAA,EAEA,SAAA,GAAY,EAAA;AAAA,EACZ,iBAAA,GAAoB,EAAA;AAAA,EACpB,qBAAA,GAAwB,EAAA;AAAA,EACxB,oBAAA,GAAuB,EAAA;AAAA,EACvB,mBAAA,GAAsB,EAAA;AAAA,EACtB,iBAAA,GAAoB;AACxB,CAAA,EAAuB;AAEnB,EAAA,MAAME,gBAAe,MAAM;AACvB,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,SAAS,CAAA;AACjD,IAAA,IAAI,OAAA,EAAS;AACT,MAAA,OAAA,CAAQ,SAAA,CAAU,IAAI,kBAAkB,CAAA;AACxC,MAAA,OAAA,CAAQ,SAAA,CAAU,OAAO,eAAe,CAAA;AAAA,IAC5C;AACA,IAAA,MAAM,OAAA,GAAU,QAAA,CAAS,cAAA,CAAe,SAAS,CAAA;AACjD,IAAA,IAAI,OAAA,EAAS;AACT,MAAA,OAAA,CAAQ,SAAA,CAAU,IAAI,QAAQ,CAAA;AAAA,IAClC;AAAA,EACJ,CAAA;AAEA,EAAA,MAAM,mBAAA,GAAsB,CAAC,IAAA,KAAiB;AAC1C,IAAA,UAAA,CAAW,IAAI,CAAA;AACf,IAAAA,aAAAA,EAAa;AAAA,EACjB,CAAA;AAEA,EAAA,MAAM,eAAe,MAAM;AACvB,IAAAA,aAAAA,EAAa;AACb,IAAA,QAAA,EAAS;AAAA,EACb,CAAA;AAEA,EAAA,uBACID,gBAAAE,mBAAA,EAAA,EAEI,QAAA,EAAA;AAAA,oBAAAH,cAAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACG,EAAA,EAAI,SAAA;AAAA,QACJ,SAAA,EAAU,uCAAA;AAAA,QACV,OAAA,EAASE;AAAA;AAAA,KACb;AAAA,oBAGAD,eAAAA;AAAA,MAAC,OAAA;AAAA,MAAA;AAAA,QACG,EAAA,EAAI,SAAA;AAAA,QACJ,SAAA,EAAW,oJAAoJ,SAAS,CAAA,CAAA;AAAA,QAGxK,QAAA,EAAA;AAAA,0BAAAD,cAAAA;AAAA,YAAC,WAAA;AAAA,YAAA;AAAA,cACG,IAAA;AAAA,cACA,SAAA;AAAA,cACA,SAAA;AAAA,cACA,SAAA,EAAW,oBAAA;AAAA,cACX,eAAA,EAAiB,mBAAA;AAAA,cACjB,aAAA,EAAe;AAAA;AAAA,WACnB;AAAA,0BAGAA,cAAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACG,SAAA;AAAA,cACA,WAAA,EAAa,mBAAA;AAAA,cACb,aAAA,EAAe;AAAA;AAAA,WACnB;AAAA,0BAGAA,cAAAA,CAAC,YAAA,EAAA,EAAa,QAAA,EAAU,YAAA,EAAc,WAAW,qBAAA,EAAuB;AAAA;AAAA;AAAA;AAC5E,GAAA,EACJ,CAAA;AAER","file":"index.js","sourcesContent":["/**\r\n * Toggle sidebar visibility (Right side)\r\n * @param sidebarId - ID of the sidebar element\r\n * @param overlayId - ID of the overlay element\r\n */\r\nexport function toggleSidebar(sidebarId: string = 'sidebar', overlayId: string = 'sidebar-overlay') {\r\n const sidebar = document.getElementById(sidebarId);\r\n const overlay = document.getElementById(overlayId);\r\n\r\n if (sidebar && overlay) {\r\n const isOpen = sidebar.classList.contains('translate-x-0');\r\n\r\n if (isOpen) {\r\n // Close sidebar (slide to right)\r\n sidebar.classList.remove('translate-x-0');\r\n sidebar.classList.add('translate-x-full');\r\n overlay.classList.add('hidden');\r\n } else {\r\n // Open sidebar (slide from right)\r\n sidebar.classList.remove('translate-x-full');\r\n sidebar.classList.add('translate-x-0');\r\n overlay.classList.remove('hidden');\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Close sidebar (Right side)\r\n * @param sidebarId - ID of the sidebar element\r\n * @param overlayId - ID of the overlay element\r\n */\r\nexport function closeSidebar(sidebarId: string = 'sidebar', overlayId: string = 'sidebar-overlay') {\r\n const sidebar = document.getElementById(sidebarId);\r\n const overlay = document.getElementById(overlayId);\r\n\r\n if (sidebar) {\r\n sidebar.classList.remove('translate-x-0');\r\n sidebar.classList.add('translate-x-full');\r\n }\r\n\r\n if (overlay) {\r\n overlay.classList.add('hidden');\r\n }\r\n}\r\n\r\n/**\r\n * Open sidebar (Right side)\r\n * @param sidebarId - ID of the sidebar element\r\n * @param overlayId - ID of the overlay element\r\n */\r\nexport function openSidebar(sidebarId: string = 'sidebar', overlayId: string = 'sidebar-overlay') {\r\n const sidebar = document.getElementById(sidebarId);\r\n const overlay = document.getElementById(overlayId);\r\n\r\n if (sidebar) {\r\n sidebar.classList.remove('translate-x-full');\r\n sidebar.classList.add('translate-x-0');\r\n }\r\n\r\n if (overlay) {\r\n overlay.classList.remove('hidden');\r\n }\r\n}\r\n","'use client';\r\n\r\ninterface User {\r\n id?: string | number;\r\n firstName?: string;\r\n lastName?: string;\r\n pictureUrl?: string;\r\n role?: string;\r\n}\r\n\r\ninterface UserProfileProps {\r\n user: User | null;\r\n roleLabel: string;\r\n roleColor?: string;\r\n // Styling\r\n className?: string;\r\n avatarClassName?: string;\r\n textClassName?: string;\r\n}\r\n\r\nexport default function UserProfile({\r\n user,\r\n roleLabel,\r\n roleColor = 'orange',\r\n className = '',\r\n avatarClassName = '',\r\n textClassName = ''\r\n}: UserProfileProps) {\r\n if (!user) return null;\r\n\r\n const getFullName = () => {\r\n if (user.firstName && user.lastName) {\r\n return `${user.firstName} ${user.lastName}`;\r\n }\r\n return user.firstName || user.lastName || 'ผู้ใช้';\r\n };\r\n\r\n const getInitial = () => {\r\n return user.firstName?.charAt(0) || user.lastName?.charAt(0) || '?';\r\n };\r\n\r\n const getRoleColorClass = () => {\r\n const colors: Record<string, string> = {\r\n orange: 'bg-orange-500',\r\n teal: 'bg-teal-500',\r\n blue: 'bg-blue-500',\r\n green: 'bg-green-500',\r\n purple: 'bg-purple-500',\r\n };\r\n return colors[roleColor] || 'bg-orange-500';\r\n };\r\n\r\n return (\r\n <div className={`p-5 border-b border-gray-200 ${className}`}>\r\n <div className=\"flex items-center gap-3\">\r\n {user.pictureUrl ? (\r\n <img\r\n src={user.pictureUrl}\r\n alt={getFullName()}\r\n width={56}\r\n height={56}\r\n className={`w-14 h-14 rounded-full shadow-lg object-cover ${avatarClassName}`}\r\n />\r\n ) : (\r\n <div className={`w-14 h-14 bg-gradient-to-br from-teal-400 to-teal-600 rounded-full flex items-center justify-center text-white font-bold shadow-lg text-xl ${avatarClassName}`}>\r\n {getInitial()}\r\n </div>\r\n )}\r\n <div className={`flex-1 min-w-0 ${textClassName}`}>\r\n <p className=\"font-bold text-gray-900 truncate text-base\">\r\n {getFullName()}\r\n </p>\r\n <p className=\"text-sm text-gray-500 flex items-center gap-1\">\r\n <span className={`w-2 h-2 ${getRoleColorClass()} rounded-full`}></span>\r\n {roleLabel}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","'use client';\r\n\r\nexport interface MenuItem {\r\n id: string;\r\n title: string;\r\n path: string;\r\n icon: React.ReactNode;\r\n}\r\n\r\ninterface MenuListProps {\r\n menuItems: MenuItem[];\r\n onItemClick: (path: string) => void;\r\n itemClassName?: string;\r\n}\r\n\r\nexport default function MenuList({ menuItems, onItemClick, itemClassName = '' }: MenuListProps) {\r\n return (\r\n <nav className=\"flex-1 p-5 overflow-y-auto\">\r\n <ul className=\"space-y-1\">\r\n {menuItems.map((item) => (\r\n <li key={item.id}>\r\n <button\r\n onClick={() => onItemClick(item.path)}\r\n className={`w-full flex items-center gap-4 px-4 py-3.5 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors ${itemClassName}`}\r\n >\r\n <div className=\"w-8 h-8 flex items-center justify-center text-gray-600 flex-shrink-0\">{item.icon}</div>\r\n <span className=\"flex-1 text-left font-medium text-base\">\r\n {item.title}\r\n </span>\r\n </button>\r\n </li>\r\n ))}\r\n </ul>\r\n </nav>\r\n );\r\n}\r\n","'use client';\r\n\r\ninterface LogoutButtonProps {\r\n onLogout: () => void;\r\n className?: string;\r\n}\r\n\r\nexport default function LogoutButton({ onLogout, className = '' }: LogoutButtonProps) {\r\n return (\r\n <div className=\"p-5 border-t border-gray-200\">\r\n <button\r\n onClick={onLogout}\r\n className={`w-full flex items-center gap-4 px-4 py-3.5 rounded-lg text-red-600 hover:bg-red-50 transition-colors ${className}`}\r\n >\r\n <svg className=\"w-5 h-5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\">\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n strokeWidth={2}\r\n d=\"M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1\"\r\n />\r\n </svg>\r\n <span className=\"flex-1 text-left font-medium text-base\">ออกจากระบบ</span>\r\n </button>\r\n </div>\r\n );\r\n}\r\n","'use client';\r\n\r\nimport UserProfile from './UserProfile';\r\n\r\nimport MenuList from './MenuList';\r\nimport LogoutButton from './LogoutButton';\r\nimport type { MenuItem } from './MenuList';\r\n\r\ninterface User {\r\n id?: string | number;\r\n firstName?: string;\r\n lastName?: string;\r\n pictureUrl?: string;\r\n role?: string;\r\n}\r\n\r\ninterface SharedSidebarProps {\r\n user: User | null;\r\n menuItems: MenuItem[];\r\n sidebarId?: string;\r\n overlayId?: string;\r\n\r\n roleLabel: string;\r\n roleColor?: string;\r\n onNavigate: (path: string) => void;\r\n onLogout: () => void;\r\n // Customization Props\r\n className?: string; // Sidebar container\r\n menuItemClassName?: string;\r\n logoutButtonClassName?: string;\r\n // UserProfile Customization via Sidebar\r\n userProfileClassName?: string;\r\n userAvatarClassName?: string;\r\n userTextClassName?: string;\r\n}\r\n\r\nexport default function SharedSidebar({\r\n user,\r\n menuItems,\r\n sidebarId = 'sidebar',\r\n overlayId = 'sidebar-overlay',\r\n\r\n roleLabel,\r\n roleColor = 'orange',\r\n onNavigate,\r\n onLogout,\r\n // Styles\r\n className = '',\r\n menuItemClassName = '',\r\n logoutButtonClassName = '',\r\n userProfileClassName = '',\r\n userAvatarClassName = '',\r\n userTextClassName = ''\r\n}: SharedSidebarProps) {\r\n\r\n const closeSidebar = () => {\r\n const sidebar = document.getElementById(sidebarId);\r\n if (sidebar) {\r\n sidebar.classList.add('translate-x-full');\r\n sidebar.classList.remove('translate-x-0');\r\n }\r\n const overlay = document.getElementById(overlayId);\r\n if (overlay) {\r\n overlay.classList.add('hidden');\r\n }\r\n };\r\n\r\n const handleMenuItemClick = (path: string) => {\r\n onNavigate(path);\r\n closeSidebar();\r\n };\r\n\r\n const handleLogout = () => {\r\n closeSidebar();\r\n onLogout();\r\n };\r\n\r\n return (\r\n <>\r\n {/* Overlay */}\r\n <div\r\n id={overlayId}\r\n className=\"fixed inset-0 bg-black/50 z-40 hidden\"\r\n onClick={closeSidebar}\r\n />\r\n\r\n {/* Sidebar - Hidden by default, opened via toggle button in header */}\r\n <aside\r\n id={sidebarId}\r\n className={`fixed top-0 right-0 h-full w-[80vw] max-w-80 bg-white shadow-2xl z-50 transform translate-x-full transition-transform duration-300 flex flex-col ${className}`}>\r\n\r\n {/* User Profile Section */}\r\n <UserProfile\r\n user={user}\r\n roleLabel={roleLabel}\r\n roleColor={roleColor}\r\n className={userProfileClassName}\r\n avatarClassName={userAvatarClassName}\r\n textClassName={userTextClassName}\r\n />\r\n\r\n {/* Menu Items */}\r\n <MenuList\r\n menuItems={menuItems}\r\n onItemClick={handleMenuItemClick}\r\n itemClassName={menuItemClassName}\r\n />\r\n\r\n {/* Logout Button */}\r\n <LogoutButton onLogout={handleLogout} className={logoutButtonClassName} />\r\n </aside>\r\n </>\r\n );\r\n}\r\n\r\n// Re-export MenuItem type for convenience\r\nexport type { MenuItem };\r\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,206 @@
1
+ import 'react';
2
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
3
+
4
+ // src/header/Header.tsx
5
+
6
+ // src/sidebar/utils.ts
7
+ function toggleSidebar(sidebarId = "sidebar", overlayId = "sidebar-overlay") {
8
+ const sidebar = document.getElementById(sidebarId);
9
+ const overlay = document.getElementById(overlayId);
10
+ if (sidebar && overlay) {
11
+ const isOpen = sidebar.classList.contains("translate-x-0");
12
+ if (isOpen) {
13
+ sidebar.classList.remove("translate-x-0");
14
+ sidebar.classList.add("translate-x-full");
15
+ overlay.classList.add("hidden");
16
+ } else {
17
+ sidebar.classList.remove("translate-x-full");
18
+ sidebar.classList.add("translate-x-0");
19
+ overlay.classList.remove("hidden");
20
+ }
21
+ }
22
+ }
23
+ function closeSidebar(sidebarId = "sidebar", overlayId = "sidebar-overlay") {
24
+ const sidebar = document.getElementById(sidebarId);
25
+ const overlay = document.getElementById(overlayId);
26
+ if (sidebar) {
27
+ sidebar.classList.remove("translate-x-0");
28
+ sidebar.classList.add("translate-x-full");
29
+ }
30
+ if (overlay) {
31
+ overlay.classList.add("hidden");
32
+ }
33
+ }
34
+ function openSidebar(sidebarId = "sidebar", overlayId = "sidebar-overlay") {
35
+ const sidebar = document.getElementById(sidebarId);
36
+ const overlay = document.getElementById(overlayId);
37
+ if (sidebar) {
38
+ sidebar.classList.remove("translate-x-full");
39
+ sidebar.classList.add("translate-x-0");
40
+ }
41
+ if (overlay) {
42
+ overlay.classList.remove("hidden");
43
+ }
44
+ }
45
+ function UserProfile({
46
+ user,
47
+ roleLabel,
48
+ roleColor = "orange",
49
+ className = "",
50
+ avatarClassName = "",
51
+ textClassName = ""
52
+ }) {
53
+ if (!user) return null;
54
+ const getFullName = () => {
55
+ if (user.firstName && user.lastName) {
56
+ return `${user.firstName} ${user.lastName}`;
57
+ }
58
+ return user.firstName || user.lastName || "\u0E1C\u0E39\u0E49\u0E43\u0E0A\u0E49";
59
+ };
60
+ const getInitial = () => {
61
+ return user.firstName?.charAt(0) || user.lastName?.charAt(0) || "?";
62
+ };
63
+ const getRoleColorClass = () => {
64
+ const colors = {
65
+ orange: "bg-orange-500",
66
+ teal: "bg-teal-500",
67
+ blue: "bg-blue-500",
68
+ green: "bg-green-500",
69
+ purple: "bg-purple-500"
70
+ };
71
+ return colors[roleColor] || "bg-orange-500";
72
+ };
73
+ return /* @__PURE__ */ jsx("div", { className: `p-5 border-b border-gray-200 ${className}`, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
74
+ user.pictureUrl ? /* @__PURE__ */ jsx(
75
+ "img",
76
+ {
77
+ src: user.pictureUrl,
78
+ alt: getFullName(),
79
+ width: 56,
80
+ height: 56,
81
+ className: `w-14 h-14 rounded-full shadow-lg object-cover ${avatarClassName}`
82
+ }
83
+ ) : /* @__PURE__ */ jsx("div", { className: `w-14 h-14 bg-gradient-to-br from-teal-400 to-teal-600 rounded-full flex items-center justify-center text-white font-bold shadow-lg text-xl ${avatarClassName}`, children: getInitial() }),
84
+ /* @__PURE__ */ jsxs("div", { className: `flex-1 min-w-0 ${textClassName}`, children: [
85
+ /* @__PURE__ */ jsx("p", { className: "font-bold text-gray-900 truncate text-base", children: getFullName() }),
86
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-gray-500 flex items-center gap-1", children: [
87
+ /* @__PURE__ */ jsx("span", { className: `w-2 h-2 ${getRoleColorClass()} rounded-full` }),
88
+ roleLabel
89
+ ] })
90
+ ] })
91
+ ] }) });
92
+ }
93
+ function MenuList({ menuItems, onItemClick, itemClassName = "" }) {
94
+ return /* @__PURE__ */ jsx("nav", { className: "flex-1 p-5 overflow-y-auto", children: /* @__PURE__ */ jsx("ul", { className: "space-y-1", children: menuItems.map((item) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
95
+ "button",
96
+ {
97
+ onClick: () => onItemClick(item.path),
98
+ className: `w-full flex items-center gap-4 px-4 py-3.5 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors ${itemClassName}`,
99
+ children: [
100
+ /* @__PURE__ */ jsx("div", { className: "w-8 h-8 flex items-center justify-center text-gray-600 flex-shrink-0", children: item.icon }),
101
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-left font-medium text-base", children: item.title })
102
+ ]
103
+ }
104
+ ) }, item.id)) }) });
105
+ }
106
+ function LogoutButton({ onLogout, className = "" }) {
107
+ return /* @__PURE__ */ jsx("div", { className: "p-5 border-t border-gray-200", children: /* @__PURE__ */ jsxs(
108
+ "button",
109
+ {
110
+ onClick: onLogout,
111
+ className: `w-full flex items-center gap-4 px-4 py-3.5 rounded-lg text-red-600 hover:bg-red-50 transition-colors ${className}`,
112
+ children: [
113
+ /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", children: /* @__PURE__ */ jsx(
114
+ "path",
115
+ {
116
+ strokeLinecap: "round",
117
+ strokeLinejoin: "round",
118
+ strokeWidth: 2,
119
+ d: "M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
120
+ }
121
+ ) }),
122
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-left font-medium text-base", children: "\u0E2D\u0E2D\u0E01\u0E08\u0E32\u0E01\u0E23\u0E30\u0E1A\u0E1A" })
123
+ ]
124
+ }
125
+ ) });
126
+ }
127
+ function SharedSidebar({
128
+ user,
129
+ menuItems,
130
+ sidebarId = "sidebar",
131
+ overlayId = "sidebar-overlay",
132
+ roleLabel,
133
+ roleColor = "orange",
134
+ onNavigate,
135
+ onLogout,
136
+ // Styles
137
+ className = "",
138
+ menuItemClassName = "",
139
+ logoutButtonClassName = "",
140
+ userProfileClassName = "",
141
+ userAvatarClassName = "",
142
+ userTextClassName = ""
143
+ }) {
144
+ const closeSidebar2 = () => {
145
+ const sidebar = document.getElementById(sidebarId);
146
+ if (sidebar) {
147
+ sidebar.classList.add("translate-x-full");
148
+ sidebar.classList.remove("translate-x-0");
149
+ }
150
+ const overlay = document.getElementById(overlayId);
151
+ if (overlay) {
152
+ overlay.classList.add("hidden");
153
+ }
154
+ };
155
+ const handleMenuItemClick = (path) => {
156
+ onNavigate(path);
157
+ closeSidebar2();
158
+ };
159
+ const handleLogout = () => {
160
+ closeSidebar2();
161
+ onLogout();
162
+ };
163
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
164
+ /* @__PURE__ */ jsx(
165
+ "div",
166
+ {
167
+ id: overlayId,
168
+ className: "fixed inset-0 bg-black/50 z-40 hidden",
169
+ onClick: closeSidebar2
170
+ }
171
+ ),
172
+ /* @__PURE__ */ jsxs(
173
+ "aside",
174
+ {
175
+ id: sidebarId,
176
+ className: `fixed top-0 right-0 h-full w-[80vw] max-w-80 bg-white shadow-2xl z-50 transform translate-x-full transition-transform duration-300 flex flex-col ${className}`,
177
+ children: [
178
+ /* @__PURE__ */ jsx(
179
+ UserProfile,
180
+ {
181
+ user,
182
+ roleLabel,
183
+ roleColor,
184
+ className: userProfileClassName,
185
+ avatarClassName: userAvatarClassName,
186
+ textClassName: userTextClassName
187
+ }
188
+ ),
189
+ /* @__PURE__ */ jsx(
190
+ MenuList,
191
+ {
192
+ menuItems,
193
+ onItemClick: handleMenuItemClick,
194
+ itemClassName: menuItemClassName
195
+ }
196
+ ),
197
+ /* @__PURE__ */ jsx(LogoutButton, { onLogout: handleLogout, className: logoutButtonClassName })
198
+ ]
199
+ }
200
+ )
201
+ ] });
202
+ }
203
+
204
+ export { LogoutButton, MenuList, SharedSidebar, UserProfile, closeSidebar, openSidebar, toggleSidebar };
205
+ //# sourceMappingURL=index.mjs.map
206
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/sidebar/utils.ts","../src/sidebar/UserProfile.tsx","../src/sidebar/MenuList.tsx","../src/sidebar/LogoutButton.tsx","../src/sidebar/SharedSidebar.tsx"],"names":["jsx","jsxs","closeSidebar"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\r\n * Toggle sidebar visibility (Right side)\r\n * @param sidebarId - ID of the sidebar element\r\n * @param overlayId - ID of the overlay element\r\n */\r\nexport function toggleSidebar(sidebarId: string = 'sidebar', overlayId: string = 'sidebar-overlay') {\r\n const sidebar = document.getElementById(sidebarId);\r\n const overlay = document.getElementById(overlayId);\r\n\r\n if (sidebar && overlay) {\r\n const isOpen = sidebar.classList.contains('translate-x-0');\r\n\r\n if (isOpen) {\r\n // Close sidebar (slide to right)\r\n sidebar.classList.remove('translate-x-0');\r\n sidebar.classList.add('translate-x-full');\r\n overlay.classList.add('hidden');\r\n } else {\r\n // Open sidebar (slide from right)\r\n sidebar.classList.remove('translate-x-full');\r\n sidebar.classList.add('translate-x-0');\r\n overlay.classList.remove('hidden');\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Close sidebar (Right side)\r\n * @param sidebarId - ID of the sidebar element\r\n * @param overlayId - ID of the overlay element\r\n */\r\nexport function closeSidebar(sidebarId: string = 'sidebar', overlayId: string = 'sidebar-overlay') {\r\n const sidebar = document.getElementById(sidebarId);\r\n const overlay = document.getElementById(overlayId);\r\n\r\n if (sidebar) {\r\n sidebar.classList.remove('translate-x-0');\r\n sidebar.classList.add('translate-x-full');\r\n }\r\n\r\n if (overlay) {\r\n overlay.classList.add('hidden');\r\n }\r\n}\r\n\r\n/**\r\n * Open sidebar (Right side)\r\n * @param sidebarId - ID of the sidebar element\r\n * @param overlayId - ID of the overlay element\r\n */\r\nexport function openSidebar(sidebarId: string = 'sidebar', overlayId: string = 'sidebar-overlay') {\r\n const sidebar = document.getElementById(sidebarId);\r\n const overlay = document.getElementById(overlayId);\r\n\r\n if (sidebar) {\r\n sidebar.classList.remove('translate-x-full');\r\n sidebar.classList.add('translate-x-0');\r\n }\r\n\r\n if (overlay) {\r\n overlay.classList.remove('hidden');\r\n }\r\n}\r\n","'use client';\r\n\r\ninterface User {\r\n id?: string | number;\r\n firstName?: string;\r\n lastName?: string;\r\n pictureUrl?: string;\r\n role?: string;\r\n}\r\n\r\ninterface UserProfileProps {\r\n user: User | null;\r\n roleLabel: string;\r\n roleColor?: string;\r\n // Styling\r\n className?: string;\r\n avatarClassName?: string;\r\n textClassName?: string;\r\n}\r\n\r\nexport default function UserProfile({\r\n user,\r\n roleLabel,\r\n roleColor = 'orange',\r\n className = '',\r\n avatarClassName = '',\r\n textClassName = ''\r\n}: UserProfileProps) {\r\n if (!user) return null;\r\n\r\n const getFullName = () => {\r\n if (user.firstName && user.lastName) {\r\n return `${user.firstName} ${user.lastName}`;\r\n }\r\n return user.firstName || user.lastName || 'ผู้ใช้';\r\n };\r\n\r\n const getInitial = () => {\r\n return user.firstName?.charAt(0) || user.lastName?.charAt(0) || '?';\r\n };\r\n\r\n const getRoleColorClass = () => {\r\n const colors: Record<string, string> = {\r\n orange: 'bg-orange-500',\r\n teal: 'bg-teal-500',\r\n blue: 'bg-blue-500',\r\n green: 'bg-green-500',\r\n purple: 'bg-purple-500',\r\n };\r\n return colors[roleColor] || 'bg-orange-500';\r\n };\r\n\r\n return (\r\n <div className={`p-5 border-b border-gray-200 ${className}`}>\r\n <div className=\"flex items-center gap-3\">\r\n {user.pictureUrl ? (\r\n <img\r\n src={user.pictureUrl}\r\n alt={getFullName()}\r\n width={56}\r\n height={56}\r\n className={`w-14 h-14 rounded-full shadow-lg object-cover ${avatarClassName}`}\r\n />\r\n ) : (\r\n <div className={`w-14 h-14 bg-gradient-to-br from-teal-400 to-teal-600 rounded-full flex items-center justify-center text-white font-bold shadow-lg text-xl ${avatarClassName}`}>\r\n {getInitial()}\r\n </div>\r\n )}\r\n <div className={`flex-1 min-w-0 ${textClassName}`}>\r\n <p className=\"font-bold text-gray-900 truncate text-base\">\r\n {getFullName()}\r\n </p>\r\n <p className=\"text-sm text-gray-500 flex items-center gap-1\">\r\n <span className={`w-2 h-2 ${getRoleColorClass()} rounded-full`}></span>\r\n {roleLabel}\r\n </p>\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n}\r\n","'use client';\r\n\r\nexport interface MenuItem {\r\n id: string;\r\n title: string;\r\n path: string;\r\n icon: React.ReactNode;\r\n}\r\n\r\ninterface MenuListProps {\r\n menuItems: MenuItem[];\r\n onItemClick: (path: string) => void;\r\n itemClassName?: string;\r\n}\r\n\r\nexport default function MenuList({ menuItems, onItemClick, itemClassName = '' }: MenuListProps) {\r\n return (\r\n <nav className=\"flex-1 p-5 overflow-y-auto\">\r\n <ul className=\"space-y-1\">\r\n {menuItems.map((item) => (\r\n <li key={item.id}>\r\n <button\r\n onClick={() => onItemClick(item.path)}\r\n className={`w-full flex items-center gap-4 px-4 py-3.5 rounded-lg text-gray-700 hover:bg-gray-50 transition-colors ${itemClassName}`}\r\n >\r\n <div className=\"w-8 h-8 flex items-center justify-center text-gray-600 flex-shrink-0\">{item.icon}</div>\r\n <span className=\"flex-1 text-left font-medium text-base\">\r\n {item.title}\r\n </span>\r\n </button>\r\n </li>\r\n ))}\r\n </ul>\r\n </nav>\r\n );\r\n}\r\n","'use client';\r\n\r\ninterface LogoutButtonProps {\r\n onLogout: () => void;\r\n className?: string;\r\n}\r\n\r\nexport default function LogoutButton({ onLogout, className = '' }: LogoutButtonProps) {\r\n return (\r\n <div className=\"p-5 border-t border-gray-200\">\r\n <button\r\n onClick={onLogout}\r\n className={`w-full flex items-center gap-4 px-4 py-3.5 rounded-lg text-red-600 hover:bg-red-50 transition-colors ${className}`}\r\n >\r\n <svg className=\"w-5 h-5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\">\r\n <path\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n strokeWidth={2}\r\n d=\"M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1\"\r\n />\r\n </svg>\r\n <span className=\"flex-1 text-left font-medium text-base\">ออกจากระบบ</span>\r\n </button>\r\n </div>\r\n );\r\n}\r\n","'use client';\r\n\r\nimport UserProfile from './UserProfile';\r\n\r\nimport MenuList from './MenuList';\r\nimport LogoutButton from './LogoutButton';\r\nimport type { MenuItem } from './MenuList';\r\n\r\ninterface User {\r\n id?: string | number;\r\n firstName?: string;\r\n lastName?: string;\r\n pictureUrl?: string;\r\n role?: string;\r\n}\r\n\r\ninterface SharedSidebarProps {\r\n user: User | null;\r\n menuItems: MenuItem[];\r\n sidebarId?: string;\r\n overlayId?: string;\r\n\r\n roleLabel: string;\r\n roleColor?: string;\r\n onNavigate: (path: string) => void;\r\n onLogout: () => void;\r\n // Customization Props\r\n className?: string; // Sidebar container\r\n menuItemClassName?: string;\r\n logoutButtonClassName?: string;\r\n // UserProfile Customization via Sidebar\r\n userProfileClassName?: string;\r\n userAvatarClassName?: string;\r\n userTextClassName?: string;\r\n}\r\n\r\nexport default function SharedSidebar({\r\n user,\r\n menuItems,\r\n sidebarId = 'sidebar',\r\n overlayId = 'sidebar-overlay',\r\n\r\n roleLabel,\r\n roleColor = 'orange',\r\n onNavigate,\r\n onLogout,\r\n // Styles\r\n className = '',\r\n menuItemClassName = '',\r\n logoutButtonClassName = '',\r\n userProfileClassName = '',\r\n userAvatarClassName = '',\r\n userTextClassName = ''\r\n}: SharedSidebarProps) {\r\n\r\n const closeSidebar = () => {\r\n const sidebar = document.getElementById(sidebarId);\r\n if (sidebar) {\r\n sidebar.classList.add('translate-x-full');\r\n sidebar.classList.remove('translate-x-0');\r\n }\r\n const overlay = document.getElementById(overlayId);\r\n if (overlay) {\r\n overlay.classList.add('hidden');\r\n }\r\n };\r\n\r\n const handleMenuItemClick = (path: string) => {\r\n onNavigate(path);\r\n closeSidebar();\r\n };\r\n\r\n const handleLogout = () => {\r\n closeSidebar();\r\n onLogout();\r\n };\r\n\r\n return (\r\n <>\r\n {/* Overlay */}\r\n <div\r\n id={overlayId}\r\n className=\"fixed inset-0 bg-black/50 z-40 hidden\"\r\n onClick={closeSidebar}\r\n />\r\n\r\n {/* Sidebar - Hidden by default, opened via toggle button in header */}\r\n <aside\r\n id={sidebarId}\r\n className={`fixed top-0 right-0 h-full w-[80vw] max-w-80 bg-white shadow-2xl z-50 transform translate-x-full transition-transform duration-300 flex flex-col ${className}`}>\r\n\r\n {/* User Profile Section */}\r\n <UserProfile\r\n user={user}\r\n roleLabel={roleLabel}\r\n roleColor={roleColor}\r\n className={userProfileClassName}\r\n avatarClassName={userAvatarClassName}\r\n textClassName={userTextClassName}\r\n />\r\n\r\n {/* Menu Items */}\r\n <MenuList\r\n menuItems={menuItems}\r\n onItemClick={handleMenuItemClick}\r\n itemClassName={menuItemClassName}\r\n />\r\n\r\n {/* Logout Button */}\r\n <LogoutButton onLogout={handleLogout} className={logoutButtonClassName} />\r\n </aside>\r\n </>\r\n );\r\n}\r\n\r\n// Re-export MenuItem type for convenience\r\nexport type { MenuItem };\r\n"]}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@gov-components/layout-header-sidebar",
3
+ "version": "1.0.0",
4
+ "description": "Reusable Header and Sidebar components for React/Next.js projects",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsup",
20
+ "dev": "tsup --watch",
21
+ "lint": "eslint ."
22
+ },
23
+ "keywords": [
24
+ "react",
25
+ "components",
26
+ "sidebar",
27
+ "header",
28
+ "tailwind"
29
+ ],
30
+ "author": "Jakprim",
31
+ "license": "MIT",
32
+ "peerDependencies": {
33
+ "react": ">=18",
34
+ "react-dom": ">=18"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^20.12.7",
38
+ "@types/react": "^18.3.1",
39
+ "autoprefixer": "^10.4.23",
40
+ "postcss": "^8.5.6",
41
+ "react": "^18.3.1",
42
+ "react-dom": "^18.3.1",
43
+ "tailwindcss": "^3.4.17",
44
+ "tsup": "^8.0.2",
45
+ "typescript": "^5.4.5"
46
+ },
47
+ "dependencies": {
48
+ "@gov-components/design-tokens": "^1.0.13"
49
+ }
50
+ }