gov-layout 1.3.7 → 1.3.8

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 CHANGED
@@ -15,11 +15,8 @@ npm install gov-layout gov-token-css
15
15
  ```css
16
16
  /* ใน globals.css */
17
17
  @import "gov-token-css";
18
- @import "gov-layout/styles.css";
19
18
  ```
20
19
 
21
- > ใช้ `gov-layout/styles.css` เพื่อให้ class สีเช่น `text-text-primary` ทำงาน แม้ไม่ได้ import component จาก `gov-layout`
22
-
23
20
  ---
24
21
 
25
22
  ## 📦 Components ทั้งหมด
@@ -111,7 +108,7 @@ const menuItems: MenuItem[] = [
111
108
 
112
109
  ## 1. StaffSidebar (เจ้าหน้าที่)
113
110
 
114
- Sidebar ฝั่งซ้ายแบบ fixed — รองรับพับ/กาง (collapsible)
111
+ Sidebar ฝั่งซ้ายแบบ fixed — รองรับพับ/กาง (collapsible) + **รองรับมือถือแบบ Drawer** 🆕
115
112
 
116
113
  ### ตัวอย่างใช้งาน
117
114
 
@@ -214,6 +211,70 @@ const menuItems: MenuItem[] = [
214
211
  | `collapsible` | `boolean?` | `false` | เปิดโหมดพับ/กาง |
215
212
  | `isOpen` | `boolean?` | - | controlled open/close |
216
213
  | `onToggle` | `() => void?` | - | callback เมื่อกดพับ/กาง |
214
+ | `mobileBreakpoint` | `number?` | `768` | breakpoint (px) สำหรับสลับเป็นโหมดมือถือ 🆕 |
215
+
216
+ ### 📱 Mobile Responsive (v1.3.5+) 🆕
217
+
218
+ เมื่อหน้าจอ **แคบกว่า `mobileBreakpoint`** (default: 768px) sidebar จะสลับเป็นโหมดมือถืออัตโนมัติ:
219
+
220
+ ```
221
+ ┌─────────────────────────────┐
222
+ │ 🟢 ชื่อองค์กร [☰] │ ← Mobile Header (sticky top)
223
+ ├─────────────────────────────┤
224
+ │ │
225
+ │ เนื้อหาหลัก │
226
+ │ │
227
+ └─────────────────────────────┘
228
+
229
+ กดปุ่ม ☰ →
230
+
231
+ ┌──────────────────┬──────────┐
232
+ │ 🟢 ชื่อองค์กร [✕]│▒▒▒▒▒▒▒▒▒│ ← Drawer + Overlay
233
+ │──────────────────│▒▒▒▒▒▒▒▒▒│
234
+ │ ☐ แดชบอร์ด │▒▒▒▒▒▒▒▒▒│
235
+ │ ☐ ตรวจสอบคำร้อง │▒▒▒▒▒▒▒▒▒│
236
+ │ ☐ รายงาน │▒▒▒▒▒▒▒▒▒│
237
+ │ ... │▒▒▒▒▒▒▒▒▒│
238
+ │──────────────────│▒▒▒▒▒▒▒▒▒│
239
+ │ 👤 ผู้ใช้ [🚪] │▒▒▒▒▒▒▒▒▒│
240
+ └──────────────────┴──────────┘
241
+ ```
242
+
243
+ **ฟีเจอร์โหมดมือถือ:**
244
+
245
+ - ✅ **Mobile Header** — แถบด้านบนสีเขียวแสดงโลโก้ + ชื่อองค์กร + ปุ่ม ☰
246
+ - ✅ **Drawer slide-in** — เลื่อนเข้าจากซ้ายพร้อม animation
247
+ - ✅ **Overlay backdrop** — พื้นหลังมืดเมื่อเปิด drawer (กดปิดได้)
248
+ - ✅ **ปุ่มปิด (✕)** — อยู่ใน header ของ drawer
249
+ - ✅ **ปิดอัตโนมัติ** — เมื่อกดเมนูแล้ว drawer ปิดเอง
250
+ - ✅ **ESC key** — กด Escape ปิด drawer
251
+ - ✅ **Lock scroll** — ล็อก body scroll เมื่อ drawer เปิด
252
+ - ✅ **ไม่ต้องเขียนโค้ดเพิ่ม** — ใช้ `<StaffSidebar>` ตัวเดียว ตรวจจับอัตโนมัติ
253
+
254
+ **ไม่ต้องแก้โค้ดเดิม** — แค่อัพเดตเวอร์ชัน ระบบจะตรวจจับขนาดหน้าจอให้อัตโนมัติ:
255
+
256
+ ```tsx
257
+ // ใช้ StaffSidebar เหมือนเดิม — mobile responsive ทำงานอัตโนมัติ
258
+ <StaffSidebar
259
+ orgName="เทศบาลตำบลหลักเมือง"
260
+ orgSubtitle="จังหวัดราชบุรี"
261
+ menuItems={menuItems}
262
+ user={user}
263
+ roleLabel="เจ้าหน้าที่"
264
+ currentPath={currentPath}
265
+ onNavigate={(path) => router.push(path)}
266
+ onLogout={() => signOut()}
267
+ />
268
+
269
+ // หรือปรับ breakpoint เอง
270
+ <StaffSidebar mobileBreakpoint={1024} ... />
271
+ ```
272
+
273
+ > 💡 **Layout `marginLeft`:** บนมือถือ sidebar ไม่กินพื้นที่ → ใส่ `marginLeft: 0` ให้ main content
274
+ >
275
+ > ```tsx
276
+ > <main style={{ marginLeft: isMobile ? 0 : 280 }}>{children}</main>
277
+ > ```
217
278
 
218
279
  ### Features
219
280
 
@@ -225,7 +286,8 @@ const menuItems: MenuItem[] = [
225
286
  - ✅ Default ตั้งค่าระบบ + ช่วยเหลือ (override ได้)
226
287
  - ✅ โปรไฟล์ + ออกจากระบบ ล่างสุดเสมอ
227
288
  - ✅ `dividerAfter` เส้นคั่นระหว่างกลุ่ม
228
- - ✅ ใช้ Standard Avatar Placeholder กรณีที่ไม่มีรูปโปรไฟล์หรือโหลดรูปไม่สำเร็จ (v1.3.2+) 🆕
289
+ - ✅ ใช้ Standard Avatar Placeholder กรณีที่ไม่มีรูปโปรไฟล์หรือโหลดรูปไม่สำเร็จ (v1.3.2+)
290
+ - ✅ **Mobile Responsive Drawer** — สลับเป็น slide-in drawer บนมือถืออัตโนมัติ (v1.3.5+) 🆕
229
291
 
230
292
  ---
231
293
 
@@ -489,10 +551,25 @@ type FontSizeKey = 'xsmall' | 'small' | 'medium' | 'large' | 'xlarge';
489
551
 
490
552
  ```tsx
491
553
  'use client';
554
+ import { useState, useEffect } from 'react';
492
555
  import { StaffSidebar, SettingsPanel } from 'gov-layout';
493
556
 
557
+ // Hook ตรวจจับมือถือ (ใช้ breakpoint เดียวกับ StaffSidebar)
558
+ function useIsMobile(breakpoint = 768) {
559
+ const [isMobile, setIsMobile] = useState(false);
560
+ useEffect(() => {
561
+ const mql = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
562
+ setIsMobile(mql.matches);
563
+ const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches);
564
+ mql.addEventListener('change', handler);
565
+ return () => mql.removeEventListener('change', handler);
566
+ }, [breakpoint]);
567
+ return isMobile;
568
+ }
569
+
494
570
  export default function AdminLayout({ children }) {
495
571
  const [currentPath, setCurrentPath] = useState('/');
572
+ const isMobile = useIsMobile();
496
573
 
497
574
  return (
498
575
  <div style={{ display: 'flex' }}>
@@ -509,7 +586,8 @@ export default function AdminLayout({ children }) {
509
586
  onProfile={() => setCurrentPath('/profile')}
510
587
  collapsible
511
588
  />
512
- <main style={{ marginLeft: 280, flex: 1, padding: 32 }}>
589
+ {/* มือถือ: marginLeft=0 เพราะ sidebar เป็น drawer */}
590
+ <main style={{ marginLeft: isMobile ? 0 : 280, flex: 1, padding: 32 }}>
513
591
  {currentPath === '/settings' ? (
514
592
  <SettingsPanel showTheme={false} />
515
593
  ) : (
package/dist/index.d.mts CHANGED
@@ -53,8 +53,9 @@ interface StaffSidebarProps {
53
53
  onExpandRequest?: () => void;
54
54
  }
55
55
 
56
- declare function StaffSidebar({ orgLogo, orgName, orgSubtitle, menuItems, bottomMenuItems, user, roleLabel, onNavigate, onLogout, onProfile, currentPath, width, className, collapsible, isOpen: controlledIsOpen, onToggle, onExpandRequest, topOffset, }: StaffSidebarProps & {
56
+ declare function StaffSidebar({ orgLogo, orgName, orgSubtitle, menuItems, bottomMenuItems, user, roleLabel, onNavigate, onLogout, onProfile, currentPath, width, className, collapsible, isOpen: controlledIsOpen, onToggle, onExpandRequest, topOffset, mobileBreakpoint, }: StaffSidebarProps & {
57
57
  topOffset?: number | string;
58
+ mobileBreakpoint?: number;
58
59
  }): react_jsx_runtime.JSX.Element;
59
60
 
60
61
  interface SidebarHeaderProps {
package/dist/index.d.ts CHANGED
@@ -53,8 +53,9 @@ interface StaffSidebarProps {
53
53
  onExpandRequest?: () => void;
54
54
  }
55
55
 
56
- declare function StaffSidebar({ orgLogo, orgName, orgSubtitle, menuItems, bottomMenuItems, user, roleLabel, onNavigate, onLogout, onProfile, currentPath, width, className, collapsible, isOpen: controlledIsOpen, onToggle, onExpandRequest, topOffset, }: StaffSidebarProps & {
56
+ declare function StaffSidebar({ orgLogo, orgName, orgSubtitle, menuItems, bottomMenuItems, user, roleLabel, onNavigate, onLogout, onProfile, currentPath, width, className, collapsible, isOpen: controlledIsOpen, onToggle, onExpandRequest, topOffset, mobileBreakpoint, }: StaffSidebarProps & {
57
57
  topOffset?: number | string;
58
+ mobileBreakpoint?: number;
58
59
  }): react_jsx_runtime.JSX.Element;
59
60
 
60
61
  interface SidebarHeaderProps {
package/dist/index.js CHANGED
@@ -3,30 +3,7 @@
3
3
  var react = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
 
6
- // #style-inject:#style-inject
7
- function styleInject(css, { insertAt } = {}) {
8
- if (typeof document === "undefined") return;
9
- const head = document.head || document.getElementsByTagName("head")[0];
10
- const style = document.createElement("style");
11
- style.type = "text/css";
12
- if (insertAt === "top") {
13
- if (head.firstChild) {
14
- head.insertBefore(style, head.firstChild);
15
- } else {
16
- head.appendChild(style);
17
- }
18
- } else {
19
- head.appendChild(style);
20
- }
21
- if (style.styleSheet) {
22
- style.styleSheet.cssText = css;
23
- } else {
24
- style.appendChild(document.createTextNode(css));
25
- }
26
- }
27
-
28
- // src/styles.css
29
- styleInject(".text-primary,\n.text-text-primary {\n color: var(--color-alias-text-colors-primary, #060d26);\n}\n.text-secondary,\n.text-text-secondary {\n color: var(--color-alias-text-colors-secondary, #1e7d55);\n}\n.text-tertiary,\n.text-text-tertiary {\n color: var(--color-alias-text-colors-tertiary, #475272);\n}\n.text-placeholder,\n.text-text-placeholder {\n color: var(--color-alias-text-colors-placeholder, #707993);\n}\n.text-critical,\n.text-text-critical {\n color: var(--color-alias-text-colors-critical, #f21515);\n}\nhtml.dark .text-primary,\nhtml.dark .text-text-primary {\n color: var(--color-alias-text-colors-primary, #060d26) !important;\n}\nhtml.dark .text-secondary,\nhtml.dark .text-text-secondary {\n color: var(--color-alias-text-colors-secondary, #1e7d55) !important;\n}\nhtml.dark .text-tertiary,\nhtml.dark .text-text-tertiary {\n color: var(--color-alias-text-colors-tertiary, #475272) !important;\n}\nhtml.dark .text-placeholder,\nhtml.dark .text-text-placeholder {\n color: var(--color-alias-text-colors-placeholder, #707993) !important;\n}\nhtml.dark .text-critical,\nhtml.dark .text-text-critical {\n color: var(--color-alias-text-colors-critical, #f21515) !important;\n}\n");
6
+ // src/sidebar/StaffSidebar.tsx
30
7
  function useDarkMode() {
31
8
  const [isDark, setIsDark] = react.useState(false);
32
9
  react.useEffect(() => {
@@ -903,6 +880,18 @@ function SidebarUserProfile({
903
880
  }
904
881
  );
905
882
  }
883
+ function useIsMobile(breakpoint = 768) {
884
+ const [isMobile, setIsMobile] = react.useState(false);
885
+ react.useEffect(() => {
886
+ if (typeof window === "undefined") return;
887
+ const mql = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
888
+ setIsMobile(mql.matches);
889
+ const handler = (e) => setIsMobile(e.matches);
890
+ mql.addEventListener("change", handler);
891
+ return () => mql.removeEventListener("change", handler);
892
+ }, [breakpoint]);
893
+ return isMobile;
894
+ }
906
895
  function ToggleIcon({ isOpen }) {
907
896
  return /* @__PURE__ */ jsxRuntime.jsx(
908
897
  "svg",
@@ -923,6 +912,21 @@ function ToggleIcon({ isOpen }) {
923
912
  }
924
913
  );
925
914
  }
915
+ function HamburgerIcon() {
916
+ return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "24", height: "18", viewBox: "0 0 27 18", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
917
+ "path",
918
+ {
919
+ d: "M1.1875 17.8426C0.851042 17.8426 0.569077 17.7287 0.341604 17.501C0.113868 17.2735 0 16.9914 0 16.6547C0 16.3182 0.113868 16.0363 0.341604 15.8088C0.569077 15.5816 0.851042 15.468 1.1875 15.468H25.7292C26.0656 15.468 26.3476 15.5817 26.5751 15.8092C26.8028 16.0369 26.9167 16.3192 26.9167 16.6559C26.9167 16.9923 26.8028 17.2743 26.5751 17.5018C26.3476 17.729 26.0656 17.8426 25.7292 17.8426H1.1875ZM1.1875 10.1088C0.851042 10.1088 0.569077 9.99492 0.341604 9.76719C0.113868 9.53945 0 9.25735 0 8.9209C0 8.58417 0.113868 8.30221 0.341604 8.075C0.569077 7.84753 0.851042 7.73379 1.1875 7.73379H25.7292C26.0656 7.73379 26.3476 7.84766 26.5751 8.0754C26.8028 8.30313 26.9167 8.58523 26.9167 8.92169C26.9167 9.25841 26.8028 9.54037 26.5751 9.76758C26.3476 9.99506 26.0656 10.1088 25.7292 10.1088H1.1875ZM1.1875 2.3746C0.851042 2.3746 0.569077 2.26087 0.341604 2.0334C0.113868 1.80566 0 1.52343 0 1.18671C0 0.850249 0.113868 0.568284 0.341604 0.340812C0.569077 0.113604 0.851042 0 1.1875 0H25.7292C26.0656 0 26.3476 0.113868 26.5751 0.341604C26.8028 0.569077 26.9167 0.851174 26.9167 1.1879C26.9167 1.52435 26.8028 1.80632 26.5751 2.03379C26.3476 2.261 26.0656 2.3746 25.7292 2.3746H1.1875Z",
920
+ fill: "currentColor"
921
+ }
922
+ ) });
923
+ }
924
+ function CloseIcon() {
925
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
926
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
927
+ /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
928
+ ] });
929
+ }
926
930
  function HelpCircleIcon2() {
927
931
  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: [
928
932
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
@@ -958,10 +962,13 @@ function StaffSidebar({
958
962
  isOpen: controlledIsOpen,
959
963
  onToggle,
960
964
  onExpandRequest,
961
- topOffset = 0
965
+ topOffset = 0,
966
+ mobileBreakpoint = 768
962
967
  }) {
963
968
  const isDark = useDarkMode();
969
+ const isMobile = useIsMobile(mobileBreakpoint);
964
970
  const [internalOpen, setInternalOpen] = react.useState(true);
971
+ const [mobileDrawerOpen, setMobileDrawerOpen] = react.useState(false);
965
972
  const sidebarOpen = controlledIsOpen !== void 0 ? controlledIsOpen : internalOpen;
966
973
  const collapsed = collapsible && !sidebarOpen;
967
974
  const collapsedWidth = "64px";
@@ -985,6 +992,283 @@ function StaffSidebar({
985
992
  }
986
993
  }
987
994
  };
995
+ const openDrawer = react.useCallback(() => setMobileDrawerOpen(true), []);
996
+ const closeDrawer = react.useCallback(() => setMobileDrawerOpen(false), []);
997
+ const handleMobileNavigate = react.useCallback((path) => {
998
+ onNavigate(path);
999
+ if (isMobile) closeDrawer();
1000
+ }, [onNavigate, isMobile, closeDrawer]);
1001
+ react.useEffect(() => {
1002
+ if (isMobile && mobileDrawerOpen) {
1003
+ const originalOverflow = document.body.style.overflow;
1004
+ document.body.style.overflow = "hidden";
1005
+ return () => {
1006
+ document.body.style.overflow = originalOverflow;
1007
+ };
1008
+ }
1009
+ }, [isMobile, mobileDrawerOpen]);
1010
+ react.useEffect(() => {
1011
+ if (!isMobile || !mobileDrawerOpen) return;
1012
+ const handler = (e) => {
1013
+ if (e.key === "Escape") closeDrawer();
1014
+ };
1015
+ window.addEventListener("keydown", handler);
1016
+ return () => window.removeEventListener("keydown", handler);
1017
+ }, [isMobile, mobileDrawerOpen, closeDrawer]);
1018
+ if (isMobile) {
1019
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1020
+ /* @__PURE__ */ jsxRuntime.jsxs(
1021
+ "header",
1022
+ {
1023
+ style: {
1024
+ position: "sticky",
1025
+ top: 0,
1026
+ left: 0,
1027
+ right: 0,
1028
+ height: "60px",
1029
+ background: "linear-gradient(to bottom left, #3AAE7D, #346D55)",
1030
+ display: "flex",
1031
+ alignItems: "center",
1032
+ justifyContent: "space-between",
1033
+ padding: "0 16px",
1034
+ zIndex: 49,
1035
+ boxShadow: "0 2px 8px rgba(0,0,0,0.15)"
1036
+ },
1037
+ children: [
1038
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "12px", flex: 1, minWidth: 0 }, children: [
1039
+ orgLogo ? /* @__PURE__ */ jsxRuntime.jsx(
1040
+ "img",
1041
+ {
1042
+ src: orgLogo,
1043
+ alt: orgName,
1044
+ style: {
1045
+ width: "40px",
1046
+ height: "40px",
1047
+ borderRadius: "50%",
1048
+ objectFit: "cover",
1049
+ flexShrink: 0,
1050
+ border: "2px solid rgba(255,255,255,0.3)"
1051
+ }
1052
+ }
1053
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1054
+ width: "40px",
1055
+ height: "40px",
1056
+ borderRadius: "50%",
1057
+ background: "rgba(255,255,255,0.2)",
1058
+ display: "flex",
1059
+ alignItems: "center",
1060
+ justifyContent: "center",
1061
+ color: "#fff",
1062
+ fontWeight: 700,
1063
+ fontSize: "16px",
1064
+ flexShrink: 0
1065
+ }, children: orgName.slice(0, 2) }),
1066
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0 }, children: [
1067
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: {
1068
+ color: "#fff",
1069
+ fontWeight: 700,
1070
+ fontSize: "15px",
1071
+ lineHeight: "20px",
1072
+ margin: 0,
1073
+ overflow: "hidden",
1074
+ textOverflow: "ellipsis",
1075
+ whiteSpace: "nowrap"
1076
+ }, children: orgName }),
1077
+ orgSubtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { style: {
1078
+ color: "rgba(255,255,255,0.75)",
1079
+ fontSize: "12px",
1080
+ lineHeight: "16px",
1081
+ margin: 0,
1082
+ overflow: "hidden",
1083
+ textOverflow: "ellipsis",
1084
+ whiteSpace: "nowrap"
1085
+ }, children: orgSubtitle })
1086
+ ] })
1087
+ ] }),
1088
+ /* @__PURE__ */ jsxRuntime.jsx(
1089
+ "button",
1090
+ {
1091
+ onClick: openDrawer,
1092
+ "aria-label": "\u0E40\u0E1B\u0E34\u0E14\u0E40\u0E21\u0E19\u0E39",
1093
+ style: {
1094
+ width: "44px",
1095
+ height: "44px",
1096
+ borderRadius: "10px",
1097
+ border: "none",
1098
+ background: "rgba(255,255,255,0.12)",
1099
+ cursor: "pointer",
1100
+ display: "flex",
1101
+ alignItems: "center",
1102
+ justifyContent: "center",
1103
+ color: "#ffffff",
1104
+ flexShrink: 0,
1105
+ transition: "background-color 0.15s ease",
1106
+ WebkitTapHighlightColor: "transparent"
1107
+ },
1108
+ children: /* @__PURE__ */ jsxRuntime.jsx(HamburgerIcon, {})
1109
+ }
1110
+ )
1111
+ ]
1112
+ }
1113
+ ),
1114
+ /* @__PURE__ */ jsxRuntime.jsx(
1115
+ "div",
1116
+ {
1117
+ onClick: closeDrawer,
1118
+ style: {
1119
+ position: "fixed",
1120
+ inset: 0,
1121
+ background: "rgba(0,0,0,0.45)",
1122
+ zIndex: 99,
1123
+ opacity: mobileDrawerOpen ? 1 : 0,
1124
+ pointerEvents: mobileDrawerOpen ? "auto" : "none",
1125
+ transition: "opacity 0.3s ease",
1126
+ WebkitTapHighlightColor: "transparent"
1127
+ }
1128
+ }
1129
+ ),
1130
+ /* @__PURE__ */ jsxRuntime.jsxs(
1131
+ "aside",
1132
+ {
1133
+ className,
1134
+ style: {
1135
+ position: "fixed",
1136
+ top: 0,
1137
+ left: 0,
1138
+ bottom: 0,
1139
+ width: "min(85vw, 320px)",
1140
+ background: isDark ? "#1e293b" : "#fff",
1141
+ zIndex: 100,
1142
+ display: "flex",
1143
+ flexDirection: "column",
1144
+ overflow: "hidden",
1145
+ boxShadow: mobileDrawerOpen ? "4px 0 24px rgba(0,0,0,0.2)" : "none",
1146
+ transform: mobileDrawerOpen ? "translateX(0)" : "translateX(-100%)",
1147
+ transition: "transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
1148
+ color: isDark ? "#ffffff" : void 0
1149
+ },
1150
+ children: [
1151
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
1152
+ display: "flex",
1153
+ alignItems: "center",
1154
+ justifyContent: "space-between",
1155
+ padding: "16px 16px 16px 20px",
1156
+ background: "linear-gradient(to bottom left, #3AAE7D, #346D55)",
1157
+ flexShrink: 0
1158
+ }, children: [
1159
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "12px", flex: 1, minWidth: 0 }, children: [
1160
+ orgLogo ? /* @__PURE__ */ jsxRuntime.jsx(
1161
+ "img",
1162
+ {
1163
+ src: orgLogo,
1164
+ alt: orgName,
1165
+ style: {
1166
+ width: "44px",
1167
+ height: "44px",
1168
+ borderRadius: "50%",
1169
+ objectFit: "cover",
1170
+ flexShrink: 0,
1171
+ border: "2px solid rgba(255,255,255,0.3)"
1172
+ }
1173
+ }
1174
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
1175
+ width: "44px",
1176
+ height: "44px",
1177
+ borderRadius: "50%",
1178
+ background: "rgba(255,255,255,0.2)",
1179
+ display: "flex",
1180
+ alignItems: "center",
1181
+ justifyContent: "center",
1182
+ color: "#fff",
1183
+ fontWeight: 700,
1184
+ fontSize: "16px",
1185
+ flexShrink: 0
1186
+ }, children: orgName.slice(0, 2) }),
1187
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0 }, children: [
1188
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: {
1189
+ color: "#fff",
1190
+ fontWeight: 700,
1191
+ fontSize: "15px",
1192
+ lineHeight: "20px",
1193
+ margin: 0,
1194
+ overflow: "hidden",
1195
+ textOverflow: "ellipsis",
1196
+ whiteSpace: "nowrap"
1197
+ }, children: orgName }),
1198
+ orgSubtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { style: {
1199
+ color: "rgba(255,255,255,0.75)",
1200
+ fontSize: "12px",
1201
+ lineHeight: "16px",
1202
+ margin: 0
1203
+ }, children: orgSubtitle })
1204
+ ] })
1205
+ ] }),
1206
+ /* @__PURE__ */ jsxRuntime.jsx(
1207
+ "button",
1208
+ {
1209
+ onClick: closeDrawer,
1210
+ "aria-label": "\u0E1B\u0E34\u0E14\u0E40\u0E21\u0E19\u0E39",
1211
+ style: {
1212
+ width: "40px",
1213
+ height: "40px",
1214
+ borderRadius: "10px",
1215
+ border: "none",
1216
+ background: "rgba(255,255,255,0.15)",
1217
+ cursor: "pointer",
1218
+ display: "flex",
1219
+ alignItems: "center",
1220
+ justifyContent: "center",
1221
+ color: "#ffffff",
1222
+ flexShrink: 0,
1223
+ transition: "background-color 0.15s ease",
1224
+ WebkitTapHighlightColor: "transparent"
1225
+ },
1226
+ children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {})
1227
+ }
1228
+ )
1229
+ ] }),
1230
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, overflowY: "auto", minHeight: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1231
+ SidebarMenu,
1232
+ {
1233
+ menuItems,
1234
+ onItemClick: handleMobileNavigate,
1235
+ currentPath,
1236
+ collapsed: false
1237
+ }
1238
+ ) }),
1239
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flexShrink: 0 }, children: [
1240
+ resolvedBottomMenu && resolvedBottomMenu.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1241
+ /* @__PURE__ */ jsxRuntime.jsx("hr", { style: {
1242
+ border: "none",
1243
+ borderTop: `1px solid ${isDark ? "#374151" : "var(--color-border-colors-neutral, #c8cedd)"}`,
1244
+ margin: "4px 12px"
1245
+ } }),
1246
+ /* @__PURE__ */ jsxRuntime.jsx(
1247
+ SidebarMenu,
1248
+ {
1249
+ menuItems: resolvedBottomMenu,
1250
+ onItemClick: handleMobileNavigate,
1251
+ currentPath,
1252
+ collapsed: false
1253
+ }
1254
+ )
1255
+ ] }),
1256
+ /* @__PURE__ */ jsxRuntime.jsx(
1257
+ SidebarUserProfile,
1258
+ {
1259
+ user,
1260
+ roleLabel,
1261
+ onLogout,
1262
+ onProfile,
1263
+ collapsed: false
1264
+ }
1265
+ )
1266
+ ] })
1267
+ ]
1268
+ }
1269
+ )
1270
+ ] });
1271
+ }
988
1272
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
989
1273
  /* @__PURE__ */ jsxRuntime.jsxs(
990
1274
  "aside",
@@ -1100,7 +1384,7 @@ function BellIcon2() {
1100
1384
  }
1101
1385
  ) });
1102
1386
  }
1103
- function HamburgerIcon() {
1387
+ function HamburgerIcon2() {
1104
1388
  return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "24", height: "18", viewBox: "0 0 27 18", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx(
1105
1389
  "path",
1106
1390
  {
@@ -1616,7 +1900,7 @@ function UserHeader({
1616
1900
  onMouseLeave: (e) => {
1617
1901
  e.currentTarget.style.backgroundColor = "transparent";
1618
1902
  },
1619
- children: /* @__PURE__ */ jsxRuntime.jsx(HamburgerIcon, {})
1903
+ children: /* @__PURE__ */ jsxRuntime.jsx(HamburgerIcon2, {})
1620
1904
  }
1621
1905
  )
1622
1906
  ] })