@svadmin/ui 0.40.3 → 0.40.4

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.
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { captureAdminContext, getResources, canAccessAsync } from '@svadmin/core';
2
+ import { captureAdminContext, getResources, canAccessAsync, getAccessControlProvider } from '@svadmin/core';
3
3
  import type { Identity, MenuItem } from '@svadmin/core';
4
4
  import { getPath } from '../router-state.svelte.js';
5
5
  import { useTranslation } from '@svadmin/core/i18n';
@@ -103,6 +103,62 @@
103
103
  items: NavItem[];
104
104
  }
105
105
 
106
+ async function hasCustomMenuAccess(menuItem: MenuItem): Promise<boolean> {
107
+ const resource = menuItem.meta?.resource;
108
+ if (!resource) return true;
109
+
110
+ try {
111
+ const { can } = await canAccessAsync(resource, menuItem.meta?.action ?? 'list');
112
+ return can;
113
+ } catch {
114
+ // Permission-provider failures must not expose protected navigation.
115
+ return false;
116
+ }
117
+ }
118
+
119
+ function isVisibleCustomMenuItem(candidate: MenuItem | null): candidate is MenuItem {
120
+ return candidate !== null;
121
+ }
122
+
123
+ function snapshotCustomMenuItems(menuItems: MenuItem[]): MenuItem[] {
124
+ return menuItems.map((menuItem) => ({
125
+ ...menuItem,
126
+ meta: menuItem.meta ? { ...menuItem.meta } : undefined,
127
+ children: menuItem.children ? snapshotCustomMenuItems(menuItem.children) : undefined,
128
+ }));
129
+ }
130
+
131
+ async function visibleCustomMenuItem(menuItem: MenuItem): Promise<MenuItem | null> {
132
+ if (menuItem.meta?.hidden || !await hasCustomMenuAccess(menuItem)) return null;
133
+ if (!menuItem.children) return menuItem;
134
+
135
+ const children = await visibleCustomMenuItems(menuItem.children);
136
+ if (children.length === 0 && !menuItem.href) return null;
137
+ return { ...menuItem, children };
138
+ }
139
+
140
+ async function visibleCustomMenuItems(menuItems: MenuItem[]): Promise<MenuItem[]> {
141
+ const filteredItems = await Promise.all(menuItems.map(visibleCustomMenuItem));
142
+ return filteredItems.filter(isVisibleCustomMenuItem);
143
+ }
144
+
145
+ let customMenuItems = $state.raw<MenuItem[]>([]);
146
+
147
+ $effect(() => {
148
+ const configuredMenu = menu?.length ? snapshotCustomMenuItems(menu) : [];
149
+ // Provider swaps must invalidate checks nested below public menu groups.
150
+ getAccessControlProvider();
151
+ customMenuItems = [];
152
+ if (configuredMenu.length === 0) return;
153
+
154
+ let cancelled = false;
155
+ void visibleCustomMenuItems(configuredMenu).then((visibleItems) => {
156
+ if (!cancelled) customMenuItems = visibleItems;
157
+ });
158
+
159
+ return () => { cancelled = true; };
160
+ });
161
+
106
162
  let navItems = $state.raw<NavItem[]>([]);
107
163
 
108
164
  $effect(() => {
@@ -239,7 +295,7 @@
239
295
  <nav aria-label="Main menu" class="pb-4" class:px-[10px]={!collapsed} class:px-2={collapsed}>
240
296
  {#if menu && menu.length > 0}
241
297
  <div class="space-y-[2px]">
242
- {#each menu as item, _i (_i)}
298
+ {#each customMenuItems as item (item.name)}
243
299
  <SidebarItem {item} currentPath={path} {collapsed} depth={0} />
244
300
  {/each}
245
301
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svadmin/ui",
3
- "version": "0.40.3",
3
+ "version": "0.40.4",
4
4
  "description": "Pre-built admin UI components — AdminApp, AutoTable, AutoForm, Sidebar, Layout",
5
5
  "type": "module",
6
6
  "sideEffects": [
@@ -39,11 +39,11 @@
39
39
  "./components/*": "./dist/components/*"
40
40
  },
41
41
  "peerDependencies": {
42
- "@svadmin/core": "^0.34.1",
42
+ "@svadmin/core": "^0.34.2",
43
43
  "@tanstack/svelte-query": "^6.1.38",
44
44
  "highlight.js": "^11.11.1",
45
- "isomorphic-dompurify": "^3.20.0",
46
- "marked": "^18.0.7",
45
+ "isomorphic-dompurify": "^3.22.0",
46
+ "marked": "^18.0.9",
47
47
  "marked-highlight": "^2.2.4",
48
48
  "svelte": "^5.56.8"
49
49
  },
@@ -75,15 +75,15 @@
75
75
  },
76
76
  "dependencies": {
77
77
  "@floating-ui/dom": "^1.8.0",
78
- "@lucide/svelte": "^1.28.0",
79
- "@tanstack/svelte-store": "^0.12.0",
80
- "@tanstack/svelte-table": "9.0.0",
81
- "@tanstack/table-core": "9.0.0",
78
+ "@lucide/svelte": "^1.30.0",
79
+ "@tanstack/svelte-store": "^0.12.1",
80
+ "@tanstack/svelte-table": "9.1.2",
81
+ "@tanstack/table-core": "9.1.2",
82
82
  "bits-ui": "^2.18.1",
83
83
  "clsx": "^2.1.1",
84
84
  "svelte-sonner": "^1.1.1",
85
85
  "tailwind-merge": "^3.6.0",
86
- "tailwind-variants": "^3.3.0"
86
+ "tailwind-variants": "^3.3.1"
87
87
  },
88
88
  "license": "MIT",
89
89
  "author": "zuohuadong",
@@ -97,12 +97,12 @@
97
97
  "url": "https://github.com/zuohuadong/svadmin/issues"
98
98
  },
99
99
  "devDependencies": {
100
- "@happy-dom/global-registrator": "^20.11.1",
101
- "@sveltejs/vite-plugin-svelte": "^7.2.0",
100
+ "@happy-dom/global-registrator": "^20.11.2",
101
+ "@sveltejs/vite-plugin-svelte": "^7.3.0",
102
102
  "@testing-library/dom": "^10.4.1",
103
103
  "@testing-library/svelte": "^5.4.2",
104
- "@testing-library/user-event": "^14.6.1",
105
- "happy-dom": "^20.11.1",
104
+ "@testing-library/user-event": "^14.6.3",
105
+ "happy-dom": "^20.11.2",
106
106
  "vitest": "^4.1.10"
107
107
  },
108
108
  "scripts": {