@leduchai/vmeta-ui-shell 0.1.4 → 0.1.5

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/dist/index.cjs CHANGED
@@ -60,6 +60,7 @@ function VmetaSidebar({
60
60
  activePath,
61
61
  collapsed = false,
62
62
  storageKey = `vmeta:${app.appId}:menu-groups`,
63
+ idPrefix,
63
64
  account,
64
65
  footer,
65
66
  showCollapse = false,
@@ -67,6 +68,8 @@ function VmetaSidebar({
67
68
  onNavigate,
68
69
  onCollapsedChange
69
70
  }) {
71
+ const generatedId = (0, import_react.useId)().replace(/:/g, "");
72
+ const groupIdPrefix = idPrefix ?? `vmeta-menu-${app.appId}-${generatedId}`;
70
73
  const visibleGroups = (0, import_react.useMemo)(
71
74
  () => groups.map((group) => ({
72
75
  ...group,
@@ -128,14 +131,14 @@ function VmetaSidebar({
128
131
  className: "vmeta-menu-group__toggle",
129
132
  onClick: () => toggleGroup(group.key),
130
133
  "aria-expanded": open,
131
- "aria-controls": `vmeta-menu-group-${group.key}`,
134
+ "aria-controls": `${groupIdPrefix}-${group.key}`,
132
135
  children: [
133
136
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: group.label }),
134
137
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: `vmeta-menu-group__chevron ${open ? "is-open" : ""}`, "aria-hidden": "true", children: "\u2304" })
135
138
  ]
136
139
  }
137
140
  ),
138
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { id: `vmeta-menu-group-${group.key}`, className: `vmeta-menu-group__items ${open ? "is-open" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: group.children.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VmetaMenuButton, { item, active: isMenuItemActive(item, activePath), onNavigate: navigate }, item.key)) }) })
141
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { id: `${groupIdPrefix}-${group.key}`, className: `vmeta-menu-group__items ${open ? "is-open" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: group.children.map((item) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VmetaMenuButton, { item, active: isMenuItemActive(item, activePath), onNavigate: navigate }, item.key)) }) })
139
142
  ] }, group.key);
140
143
  }) }),
141
144
  footer && !collapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "vmeta-sidebar__footer", children: footer }),
@@ -196,15 +199,24 @@ function VmetaAppShell({
196
199
  canAccess,
197
200
  onNavigate
198
201
  }) {
199
- const [collapsed, setCollapsed] = (0, import_react.useState)(() => readStoredBoolean(storageKey, false));
202
+ const [collapsed, setCollapsed] = (0, import_react.useState)(false);
200
203
  const [menuOpen, setMenuOpen] = (0, import_react.useState)(false);
204
+ (0, import_react.useEffect)(() => {
205
+ setCollapsed(readStoredBoolean(storageKey, false));
206
+ }, [storageKey]);
201
207
  const updateCollapsed = (value) => {
202
208
  setCollapsed(value);
203
209
  writeStorage(storageKey, String(value));
204
210
  };
205
211
  const navigate = (item) => {
206
212
  setMenuOpen(false);
207
- onNavigate?.(item);
213
+ if (onNavigate) {
214
+ onNavigate(item);
215
+ return;
216
+ }
217
+ if (typeof window === "undefined") return;
218
+ if (item.external) window.open(item.href, "_blank", "noopener,noreferrer");
219
+ else window.location.assign(item.href);
208
220
  };
209
221
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", { className: `vmeta-app-shell ${collapsed ? "is-collapsed" : ""}`, children: [
210
222
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
package/dist/index.d.cts CHANGED
@@ -48,6 +48,7 @@ interface VmetaSidebarProps {
48
48
  activePath: string;
49
49
  collapsed?: boolean;
50
50
  storageKey?: string;
51
+ idPrefix?: string;
51
52
  account?: VmetaAccount;
52
53
  footer?: ReactNode;
53
54
  showCollapse?: boolean;
@@ -55,7 +56,7 @@ interface VmetaSidebarProps {
55
56
  onNavigate?: (item: VmetaMenuItem) => void;
56
57
  onCollapsedChange?: (collapsed: boolean) => void;
57
58
  }
58
- declare function VmetaSidebar({ app, groups, activePath, collapsed, storageKey, account, footer, showCollapse, canAccess, onNavigate, onCollapsedChange, }: VmetaSidebarProps): react_jsx_runtime.JSX.Element;
59
+ declare function VmetaSidebar({ app, groups, activePath, collapsed, storageKey, idPrefix, account, footer, showCollapse, canAccess, onNavigate, onCollapsedChange, }: VmetaSidebarProps): react_jsx_runtime.JSX.Element;
59
60
  interface VmetaAppShellProps {
60
61
  app: VmetaAppIdentity;
61
62
  groups: VmetaMenuGroup[];
package/dist/index.d.ts CHANGED
@@ -48,6 +48,7 @@ interface VmetaSidebarProps {
48
48
  activePath: string;
49
49
  collapsed?: boolean;
50
50
  storageKey?: string;
51
+ idPrefix?: string;
51
52
  account?: VmetaAccount;
52
53
  footer?: ReactNode;
53
54
  showCollapse?: boolean;
@@ -55,7 +56,7 @@ interface VmetaSidebarProps {
55
56
  onNavigate?: (item: VmetaMenuItem) => void;
56
57
  onCollapsedChange?: (collapsed: boolean) => void;
57
58
  }
58
- declare function VmetaSidebar({ app, groups, activePath, collapsed, storageKey, account, footer, showCollapse, canAccess, onNavigate, onCollapsedChange, }: VmetaSidebarProps): react_jsx_runtime.JSX.Element;
59
+ declare function VmetaSidebar({ app, groups, activePath, collapsed, storageKey, idPrefix, account, footer, showCollapse, canAccess, onNavigate, onCollapsedChange, }: VmetaSidebarProps): react_jsx_runtime.JSX.Element;
59
60
  interface VmetaAppShellProps {
60
61
  app: VmetaAppIdentity;
61
62
  groups: VmetaMenuGroup[];
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/index.tsx
4
- import { useEffect, useMemo, useState } from "react";
4
+ import { useEffect, useId, useMemo, useState } from "react";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  var allowAllPermissions = () => true;
7
7
  function isMenuItemActive(item, activePath) {
@@ -35,6 +35,7 @@ function VmetaSidebar({
35
35
  activePath,
36
36
  collapsed = false,
37
37
  storageKey = `vmeta:${app.appId}:menu-groups`,
38
+ idPrefix,
38
39
  account,
39
40
  footer,
40
41
  showCollapse = false,
@@ -42,6 +43,8 @@ function VmetaSidebar({
42
43
  onNavigate,
43
44
  onCollapsedChange
44
45
  }) {
46
+ const generatedId = useId().replace(/:/g, "");
47
+ const groupIdPrefix = idPrefix ?? `vmeta-menu-${app.appId}-${generatedId}`;
45
48
  const visibleGroups = useMemo(
46
49
  () => groups.map((group) => ({
47
50
  ...group,
@@ -103,14 +106,14 @@ function VmetaSidebar({
103
106
  className: "vmeta-menu-group__toggle",
104
107
  onClick: () => toggleGroup(group.key),
105
108
  "aria-expanded": open,
106
- "aria-controls": `vmeta-menu-group-${group.key}`,
109
+ "aria-controls": `${groupIdPrefix}-${group.key}`,
107
110
  children: [
108
111
  /* @__PURE__ */ jsx("span", { children: group.label }),
109
112
  /* @__PURE__ */ jsx("span", { className: `vmeta-menu-group__chevron ${open ? "is-open" : ""}`, "aria-hidden": "true", children: "\u2304" })
110
113
  ]
111
114
  }
112
115
  ),
113
- /* @__PURE__ */ jsx("div", { id: `vmeta-menu-group-${group.key}`, className: `vmeta-menu-group__items ${open ? "is-open" : ""}`, children: /* @__PURE__ */ jsx("div", { children: group.children.map((item) => /* @__PURE__ */ jsx(VmetaMenuButton, { item, active: isMenuItemActive(item, activePath), onNavigate: navigate }, item.key)) }) })
116
+ /* @__PURE__ */ jsx("div", { id: `${groupIdPrefix}-${group.key}`, className: `vmeta-menu-group__items ${open ? "is-open" : ""}`, children: /* @__PURE__ */ jsx("div", { children: group.children.map((item) => /* @__PURE__ */ jsx(VmetaMenuButton, { item, active: isMenuItemActive(item, activePath), onNavigate: navigate }, item.key)) }) })
114
117
  ] }, group.key);
115
118
  }) }),
116
119
  footer && !collapsed && /* @__PURE__ */ jsx("div", { className: "vmeta-sidebar__footer", children: footer }),
@@ -171,15 +174,24 @@ function VmetaAppShell({
171
174
  canAccess,
172
175
  onNavigate
173
176
  }) {
174
- const [collapsed, setCollapsed] = useState(() => readStoredBoolean(storageKey, false));
177
+ const [collapsed, setCollapsed] = useState(false);
175
178
  const [menuOpen, setMenuOpen] = useState(false);
179
+ useEffect(() => {
180
+ setCollapsed(readStoredBoolean(storageKey, false));
181
+ }, [storageKey]);
176
182
  const updateCollapsed = (value) => {
177
183
  setCollapsed(value);
178
184
  writeStorage(storageKey, String(value));
179
185
  };
180
186
  const navigate = (item) => {
181
187
  setMenuOpen(false);
182
- onNavigate?.(item);
188
+ if (onNavigate) {
189
+ onNavigate(item);
190
+ return;
191
+ }
192
+ if (typeof window === "undefined") return;
193
+ if (item.external) window.open(item.href, "_blank", "noopener,noreferrer");
194
+ else window.location.assign(item.href);
183
195
  };
184
196
  return /* @__PURE__ */ jsxs("main", { className: `vmeta-app-shell ${collapsed ? "is-collapsed" : ""}`, children: [
185
197
  /* @__PURE__ */ jsx(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leduchai/vmeta-ui-shell",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Application shell, sidebar, menu va header dung chung cua VMETA.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -21,7 +21,7 @@
21
21
  "build": "tsup src/index.tsx --format esm,cjs --dts --clean --external react --external react-dom && node ../../scripts/copy-file.mjs src/styles.css dist/styles.css"
22
22
  },
23
23
  "dependencies": {
24
- "@leduchai/vmeta-ui-tokens": "0.1.4"
24
+ "@leduchai/vmeta-ui-tokens": "0.1.5"
25
25
  },
26
26
  "peerDependencies": {
27
27
  "react": ">=18 <20",