@tker-react/layout 0.2.14 → 0.2.15
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 +99 -76
- package/dist/index.d.mts +8 -6
- package/dist/index.d.ts +8 -6
- package/dist/index.mjs +18 -32
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
React 后台管理布局框架,提供菜单、面包屑、标签页、侧边栏等布局能力。
|
|
4
4
|
|
|
5
5
|
- **无内置 adapter**:不依赖特定 UI 库,所有 UI 渲染由使用者通过 adapter 组件控制
|
|
6
|
-
- **React Context 共享状态**:`<Layout>` 内置 Provider
|
|
7
|
-
- **路由解耦**:不直接依赖路由库,通过 `setNavigateAdapter` 和 `
|
|
6
|
+
- **React Context 共享状态**:`<Layout>` 内置 Provider
|
|
7
|
+
- **路由解耦**:不直接依赖路由库,通过 `setNavigateAdapter` 和 `<Layout>` 的 `activePath`/`activeFullPath` props 与路由层对接
|
|
8
|
+
- **细粒度重渲染**:`activePath` 通过独立 context 传播,路由变化时仅菜单、面包屑、标签页重渲染,`<Outlet />` 不受影响
|
|
8
9
|
|
|
9
10
|
## 安装
|
|
10
11
|
|
|
@@ -26,59 +27,66 @@ import "@tker-react/layout/layout.css";
|
|
|
26
27
|
|
|
27
28
|
```tsx
|
|
28
29
|
// layouts/AppLayout.tsx
|
|
29
|
-
import {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
import {
|
|
31
|
+
Layout,
|
|
32
|
+
setMenus,
|
|
33
|
+
setMenuAdapter,
|
|
34
|
+
setTabAdapter,
|
|
35
|
+
setBreadcrumbAdapter,
|
|
36
|
+
setLogoAdapter,
|
|
37
|
+
setToolbarAdapter,
|
|
38
|
+
setUserAvatarAdapter,
|
|
39
|
+
setHomePath,
|
|
40
|
+
setNavigateAdapter,
|
|
41
|
+
} from "@tker-react/layout";
|
|
42
|
+
import { Outlet, useRouterState } from "@tanstack/react-router";
|
|
43
|
+
import { useMemo } from "react";
|
|
44
|
+
|
|
45
|
+
// adapter 组件和菜单数据
|
|
46
|
+
import MenuAdapter from "../adapters/MenuAdapter";
|
|
47
|
+
import TabAdapter from "../adapters/TabAdapter";
|
|
48
|
+
import BreadcrumbAdapter from "../adapters/BreadcrumbAdapter";
|
|
49
|
+
import LogoAdapter from "../adapters/LogoAdapter";
|
|
50
|
+
import ToolbarAdapter from "../adapters/ToolbarAdapter";
|
|
51
|
+
import UserAvatarAdapter from "../adapters/UserAvatarAdapter";
|
|
52
|
+
import { router } from "../router";
|
|
53
|
+
|
|
54
|
+
// -- 静态配置:模块顶层调用即可 --
|
|
55
|
+
setMenuAdapter(MenuAdapter);
|
|
56
|
+
setTabAdapter(TabAdapter);
|
|
57
|
+
setBreadcrumbAdapter(BreadcrumbAdapter);
|
|
58
|
+
setLogoAdapter(LogoAdapter);
|
|
59
|
+
setToolbarAdapter(ToolbarAdapter);
|
|
60
|
+
setUserAvatarAdapter(UserAvatarAdapter);
|
|
61
|
+
setHomePath("/dashboard");
|
|
62
|
+
setNavigateAdapter((path) => router.navigate({ to: path }));
|
|
63
|
+
setMenus([
|
|
64
|
+
{ path: "/dashboard", title: "仪表盘" },
|
|
65
|
+
{
|
|
66
|
+
path: "/users",
|
|
67
|
+
title: "用户管理",
|
|
68
|
+
children: [
|
|
69
|
+
{ path: "/users/list", title: "用户列表" },
|
|
70
|
+
{ path: "/users/detail", title: "用户详情" },
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
]);
|
|
32
74
|
|
|
33
75
|
export function AppLayout() {
|
|
34
|
-
return (
|
|
35
|
-
<Layout>
|
|
36
|
-
<SetupLayout />
|
|
37
|
-
<Outlet />
|
|
38
|
-
</Layout>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function SetupLayout() {
|
|
43
|
-
const layout = useLayout();
|
|
44
|
-
const router = useRouter();
|
|
45
76
|
const pathname = useRouterState({ select: (s) => s.location.pathname });
|
|
46
77
|
const search = useRouterState({ select: (s) => s.location.searchStr });
|
|
47
78
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
layout.setUserAvatarAdapter(UserAvatarAdapter);
|
|
56
|
-
layout.setMenus([
|
|
57
|
-
{ path: "/dashboard", title: "仪表盘" },
|
|
58
|
-
{
|
|
59
|
-
path: "/users",
|
|
60
|
-
title: "用户管理",
|
|
61
|
-
children: [
|
|
62
|
-
{ path: "/users", title: "用户列表" },
|
|
63
|
-
{ path: "/users/detail", title: "用户详情" },
|
|
64
|
-
],
|
|
65
|
-
},
|
|
66
|
-
]);
|
|
67
|
-
layout.setHomePath("/dashboard");
|
|
68
|
-
}, []);
|
|
69
|
-
|
|
70
|
-
// setNavigateAdapter 存储 ref,可以直接在 render 中调用
|
|
71
|
-
layout.setNavigateAdapter((path) => router.navigate({ to: path }));
|
|
72
|
-
|
|
73
|
-
useLayoutEffect(() => {
|
|
74
|
-
layout.setActivePath(pathname, pathname + search);
|
|
75
|
-
}, [pathname, search]);
|
|
76
|
-
|
|
77
|
-
return null;
|
|
79
|
+
const outlet = useMemo(() => <Outlet />, []);
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<Layout activeFullPath={pathname + search} activePath={pathname}>
|
|
83
|
+
{outlet}
|
|
84
|
+
</Layout>
|
|
85
|
+
);
|
|
78
86
|
}
|
|
79
87
|
```
|
|
80
88
|
|
|
81
|
-
>
|
|
89
|
+
> **注意**:adapter、菜单等静态配置在模块顶层调用一次即可(只写 module 变量)。`activePath`/`activeFullPath` 通过 props 传入 `<Layout>`。`<Outlet />` 用 `useMemo` 稳定引用,配合 `LayoutInner` 的 `React.memo` 避免路由变化时 `Outlet` 被 Layout 层牵连重渲染。
|
|
82
90
|
|
|
83
91
|
### 2. 定义路由并挂载
|
|
84
92
|
|
|
@@ -429,62 +437,65 @@ function UserAvatarAdapter({ onSettings, onLogout }: UserAvatarAdapterProps) {
|
|
|
429
437
|
|
|
430
438
|
## 使用不同路由库
|
|
431
439
|
|
|
432
|
-
|
|
440
|
+
静态配置(adapter、菜单)在模块顶层调用不变。以下展示各路由器 `activePath` 的对接方式。
|
|
433
441
|
|
|
434
442
|
### TanStack Router
|
|
435
443
|
|
|
436
444
|
```tsx
|
|
437
|
-
//
|
|
438
|
-
|
|
445
|
+
// AppLayout 中的路由同步部分
|
|
446
|
+
setNavigateAdapter((path) => router.navigate({ to: path }));
|
|
447
|
+
|
|
439
448
|
const pathname = useRouterState({ select: (s) => s.location.pathname });
|
|
440
449
|
const search = useRouterState({ select: (s) => s.location.searchStr });
|
|
441
450
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
451
|
+
return (
|
|
452
|
+
<Layout activeFullPath={pathname + search} activePath={pathname}>
|
|
453
|
+
{outlet}
|
|
454
|
+
</Layout>
|
|
455
|
+
);
|
|
447
456
|
```
|
|
448
457
|
|
|
449
458
|
### React Router v6+
|
|
450
459
|
|
|
451
460
|
```tsx
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
const navigate = useNavigate();
|
|
461
|
+
// AppLayout 中的路由同步部分
|
|
462
|
+
setNavigateAdapter((path) => navigate(path));
|
|
455
463
|
|
|
456
|
-
|
|
464
|
+
const location = useLocation();
|
|
457
465
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
}
|
|
466
|
+
return (
|
|
467
|
+
<Layout activeFullPath={location.pathname + location.search} activePath={location.pathname}>
|
|
468
|
+
{outlet}
|
|
469
|
+
</Layout>
|
|
470
|
+
);
|
|
461
471
|
```
|
|
462
472
|
|
|
463
473
|
### Next.js App Router
|
|
464
474
|
|
|
465
475
|
```tsx
|
|
466
|
-
//
|
|
476
|
+
// AppLayout 中的路由同步部分
|
|
477
|
+
setNavigateAdapter((path) => router.push(path));
|
|
478
|
+
|
|
467
479
|
const pathname = usePathname();
|
|
468
480
|
const searchParams = useSearchParams();
|
|
469
|
-
const router = useRouter();
|
|
470
|
-
|
|
471
|
-
layout.setNavigateAdapter((path) => router.push(path));
|
|
472
|
-
|
|
473
481
|
const fullPath = pathname + (searchParams.toString() ? `?${searchParams}` : "");
|
|
474
482
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
483
|
+
return (
|
|
484
|
+
<Layout activeFullPath={fullPath} activePath={pathname}>
|
|
485
|
+
{outlet}
|
|
486
|
+
</Layout>
|
|
487
|
+
);
|
|
478
488
|
```
|
|
479
489
|
|
|
480
490
|
## API
|
|
481
491
|
|
|
482
|
-
###
|
|
492
|
+
### 模块级 setter
|
|
493
|
+
|
|
494
|
+
所有配置通过模块级 setter 设置,在 import 阶段调用即可(不需要 hook),内部存储为 module 变量。
|
|
483
495
|
|
|
484
|
-
|
|
|
496
|
+
| 函数 | 说明 |
|
|
485
497
|
|------|------|
|
|
486
498
|
| `setMenus(data)` | 设置菜单数据 |
|
|
487
|
-
| `setActivePath(path, fullPath?)` | 设置当前激活路径,自动触发面包屑更新和标签页打开 |
|
|
488
499
|
| `setMenuAdapter(component)` | 注册菜单适配器组件 |
|
|
489
500
|
| `setTabAdapter(component)` | 注册标签页适配器组件 |
|
|
490
501
|
| `setBreadcrumbAdapter(component)` | 注册面包屑适配器组件 |
|
|
@@ -492,11 +503,23 @@ useLayoutEffect(() => {
|
|
|
492
503
|
| `setToolbarAdapter(component)` | 注册工具栏适配器组件 |
|
|
493
504
|
| `setUserAvatarAdapter(component)` | 注册用户头像适配器组件 |
|
|
494
505
|
| `setNavigateAdapter(fn)` | 设置导航回调,Layout 内部点击菜单/tab/面包屑时调用 |
|
|
495
|
-
| `
|
|
496
|
-
| `setLayoutMode(mode)` | 设置布局模式:`"side-menu"` 或 `"top-menu"` |
|
|
506
|
+
| `setLayoutMode(mode)` | 设置布局模式:`"side-menu"` 或 `"top-menu"`,默认 `"side-menu"` |
|
|
497
507
|
| `setHomePath(path)` | 设置首页路径,首页 tab 固定在最左侧 |
|
|
498
508
|
| `setMaxTabs(n)` | 设置最大标签页数量,超出后关闭最早的 |
|
|
499
|
-
| `
|
|
509
|
+
| `setExpandedWidth(width)` | 侧边栏展开宽度,默认 `"200px"` |
|
|
510
|
+
| `setCollapsedWidth(width)` | 侧边栏折叠宽度,默认 `"64px"` |
|
|
511
|
+
|
|
512
|
+
### 组件
|
|
513
|
+
|
|
514
|
+
| 组件 | Props | 说明 |
|
|
515
|
+
|------|-------|------|
|
|
516
|
+
| `<Layout>` | `children`, `activePath?`, `activeFullPath?` | 布局容器,内置所有 Provider。`activePath` 为当前路由路径,`activeFullPath` 为含 query string 的完整路径 |
|
|
517
|
+
|
|
518
|
+
### Hook
|
|
519
|
+
|
|
520
|
+
| Hook | 说明 |
|
|
521
|
+
|------|------|
|
|
522
|
+
| `useActivePathContext()` | 获取当前 activePath,通常仅在自定义 adapter 时使用 |
|
|
500
523
|
|
|
501
524
|
### Adapter 组件 Props
|
|
502
525
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import * as react from 'react';
|
|
2
|
-
import { ReactNode, ComponentType } from 'react';
|
|
3
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode, ComponentType } from 'react';
|
|
4
3
|
|
|
5
4
|
interface LayoutProps {
|
|
6
5
|
children?: ReactNode;
|
|
6
|
+
activePath?: string;
|
|
7
|
+
activeFullPath?: string;
|
|
7
8
|
}
|
|
8
|
-
declare
|
|
9
|
+
declare function Layout({ children, ...props }: LayoutProps): react_jsx_runtime.JSX.Element;
|
|
9
10
|
|
|
10
11
|
interface BreadcrumbItem {
|
|
11
12
|
path: string;
|
|
@@ -73,8 +74,10 @@ interface LayoutAdapters {
|
|
|
73
74
|
|
|
74
75
|
interface LayoutProviderProps {
|
|
75
76
|
children?: ReactNode;
|
|
77
|
+
activePath?: string;
|
|
78
|
+
activeFullPath?: string;
|
|
76
79
|
}
|
|
77
|
-
declare function LayoutProvider({ children }: LayoutProviderProps): react_jsx_runtime.JSX.Element;
|
|
80
|
+
declare function LayoutProvider({ children, activePath, activeFullPath, }: LayoutProviderProps): react_jsx_runtime.JSX.Element;
|
|
78
81
|
|
|
79
82
|
declare function setMenus(data: MenuItem[]): void;
|
|
80
83
|
declare function setMenuAdapter(c: ComponentType<MenuAdapterProps>): void;
|
|
@@ -89,7 +92,6 @@ declare function setLayoutMode(mode: "side-menu" | "top-menu"): void;
|
|
|
89
92
|
declare function setExpandedWidth(width: string): void;
|
|
90
93
|
declare function setCollapsedWidth(width: string): void;
|
|
91
94
|
declare function setNavigateAdapter(fn: (path: string) => void): void;
|
|
92
|
-
declare function setActivePath(path: string, fullPath?: string): void;
|
|
93
95
|
|
|
94
|
-
export { Layout, LayoutProvider,
|
|
96
|
+
export { Layout, LayoutProvider, setBreadcrumbAdapter, setCollapsedWidth, setExpandedWidth, setHomePath, setLayoutMode, setLogoAdapter, setMaxTabs, setMenuAdapter, setMenus, setNavigateAdapter, setTabAdapter, setToolbarAdapter, setUserAvatarAdapter };
|
|
95
97
|
export type { BreadcrumbAdapterProps, BreadcrumbItem, LayoutAdapters, LogoAdapterProps, MenuAdapterProps, MenuItem, TabAdapterProps, TabItem, UserAvatarAdapterProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import * as react from 'react';
|
|
2
|
-
import { ReactNode, ComponentType } from 'react';
|
|
3
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { ReactNode, ComponentType } from 'react';
|
|
4
3
|
|
|
5
4
|
interface LayoutProps {
|
|
6
5
|
children?: ReactNode;
|
|
6
|
+
activePath?: string;
|
|
7
|
+
activeFullPath?: string;
|
|
7
8
|
}
|
|
8
|
-
declare
|
|
9
|
+
declare function Layout({ children, ...props }: LayoutProps): react_jsx_runtime.JSX.Element;
|
|
9
10
|
|
|
10
11
|
interface BreadcrumbItem {
|
|
11
12
|
path: string;
|
|
@@ -73,8 +74,10 @@ interface LayoutAdapters {
|
|
|
73
74
|
|
|
74
75
|
interface LayoutProviderProps {
|
|
75
76
|
children?: ReactNode;
|
|
77
|
+
activePath?: string;
|
|
78
|
+
activeFullPath?: string;
|
|
76
79
|
}
|
|
77
|
-
declare function LayoutProvider({ children }: LayoutProviderProps): react_jsx_runtime.JSX.Element;
|
|
80
|
+
declare function LayoutProvider({ children, activePath, activeFullPath, }: LayoutProviderProps): react_jsx_runtime.JSX.Element;
|
|
78
81
|
|
|
79
82
|
declare function setMenus(data: MenuItem[]): void;
|
|
80
83
|
declare function setMenuAdapter(c: ComponentType<MenuAdapterProps>): void;
|
|
@@ -89,7 +92,6 @@ declare function setLayoutMode(mode: "side-menu" | "top-menu"): void;
|
|
|
89
92
|
declare function setExpandedWidth(width: string): void;
|
|
90
93
|
declare function setCollapsedWidth(width: string): void;
|
|
91
94
|
declare function setNavigateAdapter(fn: (path: string) => void): void;
|
|
92
|
-
declare function setActivePath(path: string, fullPath?: string): void;
|
|
93
95
|
|
|
94
|
-
export { Layout, LayoutProvider,
|
|
96
|
+
export { Layout, LayoutProvider, setBreadcrumbAdapter, setCollapsedWidth, setExpandedWidth, setHomePath, setLayoutMode, setLogoAdapter, setMaxTabs, setMenuAdapter, setMenus, setNavigateAdapter, setTabAdapter, setToolbarAdapter, setUserAvatarAdapter };
|
|
95
97
|
export type { BreadcrumbAdapterProps, BreadcrumbItem, LayoutAdapters, LogoAdapterProps, MenuAdapterProps, MenuItem, TabAdapterProps, TabItem, UserAvatarAdapterProps };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./layout.css";
|
|
2
2
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
3
|
-
import { useRef, useCallback, useMemo, useState,
|
|
3
|
+
import { useRef, useCallback, useMemo, useState, useEffect, createContext, useContext, memo } from 'react';
|
|
4
4
|
|
|
5
5
|
function findMenuItemByPath(menuData, path) {
|
|
6
6
|
for (const item of menuData) {
|
|
@@ -112,29 +112,6 @@ function consumeLayoutConfig() {
|
|
|
112
112
|
navigateAdapter: _navigateAdapter
|
|
113
113
|
};
|
|
114
114
|
}
|
|
115
|
-
let _activePath = typeof window !== "undefined" ? window.location.pathname : "";
|
|
116
|
-
const _pathParamsMap = /* @__PURE__ */ new Map();
|
|
117
|
-
let _activePathListeners = [];
|
|
118
|
-
function setActivePath(path, fullPath) {
|
|
119
|
-
if (fullPath && fullPath !== path) {
|
|
120
|
-
_pathParamsMap.set(path, fullPath);
|
|
121
|
-
}
|
|
122
|
-
if (path === _activePath) return;
|
|
123
|
-
_activePath = path;
|
|
124
|
-
_activePathListeners.forEach((l) => l());
|
|
125
|
-
}
|
|
126
|
-
function subscribeActivePath(listener) {
|
|
127
|
-
_activePathListeners.push(listener);
|
|
128
|
-
return () => {
|
|
129
|
-
_activePathListeners = _activePathListeners.filter((l) => l !== listener);
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
function getActivePathSnapshot() {
|
|
133
|
-
return _activePath;
|
|
134
|
-
}
|
|
135
|
-
function getFullPathByActivePath(path) {
|
|
136
|
-
return _pathParamsMap.get(path) || path;
|
|
137
|
-
}
|
|
138
115
|
|
|
139
116
|
const LayoutContext = createContext(null);
|
|
140
117
|
const LayoutInteractionContext = createContext(null);
|
|
@@ -158,7 +135,11 @@ const ActivePathContext = createContext("");
|
|
|
158
135
|
function useActivePathContext() {
|
|
159
136
|
return useContext(ActivePathContext);
|
|
160
137
|
}
|
|
161
|
-
function LayoutProvider({
|
|
138
|
+
function LayoutProvider({
|
|
139
|
+
children,
|
|
140
|
+
activePath = "",
|
|
141
|
+
activeFullPath
|
|
142
|
+
}) {
|
|
162
143
|
const config = consumeLayoutConfig();
|
|
163
144
|
const navigateRef = useRef(null);
|
|
164
145
|
navigateRef.current = config.navigateAdapter;
|
|
@@ -197,7 +178,12 @@ function LayoutProvider({ children }) {
|
|
|
197
178
|
menuDataRef.current = config.menus;
|
|
198
179
|
const layoutModeRef = useRef(config.layoutMode);
|
|
199
180
|
layoutModeRef.current = config.layoutMode;
|
|
200
|
-
const
|
|
181
|
+
const pathParamsMap = useRef(/* @__PURE__ */ new Map());
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
if (activeFullPath && activeFullPath !== activePath) {
|
|
184
|
+
pathParamsMap.current.set(activePath, activeFullPath);
|
|
185
|
+
}
|
|
186
|
+
}, [activePath, activeFullPath]);
|
|
201
187
|
const toggleMenuOpen = useCallback((path, forceOpen) => {
|
|
202
188
|
setOpenKeys((prev) => {
|
|
203
189
|
const next = new Set(prev);
|
|
@@ -228,7 +214,7 @@ function LayoutProvider({ children }) {
|
|
|
228
214
|
toggleMenuOpen(path);
|
|
229
215
|
}
|
|
230
216
|
} else {
|
|
231
|
-
const fullPath =
|
|
217
|
+
const fullPath = pathParamsMap.current.get(path) || path;
|
|
232
218
|
if (window.location.pathname + window.location.search !== fullPath) {
|
|
233
219
|
navigateRef.current?.(fullPath);
|
|
234
220
|
}
|
|
@@ -245,7 +231,7 @@ function LayoutProvider({ children }) {
|
|
|
245
231
|
setCollapsedState(nextCollapsed);
|
|
246
232
|
}, []);
|
|
247
233
|
const getFullPath = useCallback(
|
|
248
|
-
(path) =>
|
|
234
|
+
(path) => pathParamsMap.current.get(path) || path,
|
|
249
235
|
[]
|
|
250
236
|
);
|
|
251
237
|
const isConcretePage = useCallback(
|
|
@@ -623,8 +609,8 @@ const LayoutInner = memo(function LayoutInner2({ children }) {
|
|
|
623
609
|
] })
|
|
624
610
|
] });
|
|
625
611
|
});
|
|
626
|
-
|
|
627
|
-
return /* @__PURE__ */ jsx(LayoutProvider, { children: /* @__PURE__ */ jsx(LayoutInner, { children }) });
|
|
628
|
-
}
|
|
612
|
+
function Layout({ children, ...props }) {
|
|
613
|
+
return /* @__PURE__ */ jsx(LayoutProvider, { ...props, children: /* @__PURE__ */ jsx(LayoutInner, { children }) });
|
|
614
|
+
}
|
|
629
615
|
|
|
630
|
-
export { Layout, LayoutProvider,
|
|
616
|
+
export { Layout, LayoutProvider, setBreadcrumbAdapter, setCollapsedWidth, setExpandedWidth, setHomePath, setLayoutMode, setLogoAdapter, setMaxTabs, setMenuAdapter, setMenus, setNavigateAdapter, setTabAdapter, setToolbarAdapter, setUserAvatarAdapter };
|