@gx-design-vue/pro-layout 0.1.0-alpha.6 → 0.1.0-alpha.7

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.
Files changed (43) hide show
  1. package/dist/ProLayout.js +50 -38
  2. package/dist/components/Breadcrumb/index.js +6 -22
  3. package/dist/components/CollapseButton/index.js +3 -3
  4. package/dist/components/CollapseButton/interface.d.ts +0 -1
  5. package/dist/components/Header/index.js +9 -5
  6. package/dist/components/Header/interface.d.ts +6 -0
  7. package/dist/components/Logo/index.d.ts +1 -1
  8. package/dist/components/Logo/index.js +4 -6
  9. package/dist/components/Logo/interface.d.ts +1 -2
  10. package/dist/components/{Sider → Menu}/SiderMenu.d.ts +4 -2
  11. package/dist/components/{Sider → Menu}/SiderMenu.js +18 -26
  12. package/dist/components/Menu/iconRender.d.ts +10 -0
  13. package/dist/components/Menu/iconRender.js +23 -0
  14. package/dist/components/Menu/index.js +3 -17
  15. package/dist/components/{Sider/style.d.ts → Menu/style/siderMenu.d.ts} +1 -1
  16. package/dist/components/{Sider/style.js → Menu/style/siderMenu.js} +6 -6
  17. package/dist/components/PageContainer/PageHeader.d.ts +0 -3
  18. package/dist/components/PageContainer/PageHeader.js +5 -13
  19. package/dist/components/PageContainer/index.js +4 -7
  20. package/dist/components/PageTransition/index.d.ts +2 -2
  21. package/dist/components/PageTransition/index.js +1 -1
  22. package/dist/components/Sider/index.js +58 -16
  23. package/dist/components/Sider/interface.d.ts +6 -3
  24. package/dist/components/Tabs/contextMenu.js +12 -10
  25. package/dist/components/Tabs/index.js +21 -17
  26. package/dist/context/index.d.ts +4 -2
  27. package/dist/defaultConfig.d.ts +1 -1
  28. package/dist/defaultConfig.js +4 -4
  29. package/dist/hooks/useLayoutBase.d.ts +10 -9
  30. package/dist/hooks/useLayoutBase.js +5 -1
  31. package/dist/hooks/useTabs.d.ts +2 -6
  32. package/dist/hooks/useTabs.js +4 -6
  33. package/dist/index.d.ts +2 -2
  34. package/dist/interface.d.ts +18 -21
  35. package/dist/interface.js +0 -2
  36. package/dist/pro-layout.esm.js +1268 -1759
  37. package/dist/pro-layout.js +15 -1
  38. package/dist/style/logo.js +1 -2
  39. package/dist/style/sider.js +29 -14
  40. package/dist/theme/augment.d.ts +6 -6
  41. package/dist/theme/interface/components.d.ts +6 -6
  42. package/dist/utils/themeComponents.d.ts +1 -1
  43. package/package.json +8 -9
package/dist/ProLayout.js CHANGED
@@ -13,7 +13,7 @@ import { normalizeSectionConfig } from "./utils/config.js";
13
13
  import { computed, createVNode, defineComponent, mergeDefaults, ref } from "vue";
14
14
  import { ThemeContext, useThemeContext } from "@gx-design-vue/context";
15
15
  import { GProConfigProvider, useProToken } from "@gx-design-vue/pro-provider";
16
- import { classNames, getSlot, getSlotsProps } from "@gx-design-vue/pro-utils";
16
+ import { classNames, getSlotsProps } from "@gx-design-vue/pro-utils";
17
17
  import { useDark, useMediaQuery, useVModel } from "@vueuse/core";
18
18
  import { ConfigProvider, Layout } from "antdv-next";
19
19
  import { useBaseConfig } from "antdv-next/config-provider/context";
@@ -112,18 +112,39 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
112
112
  }
113
113
  if (target.path && router.currentRoute.value.fullPath !== target.path) router.push(target.path);
114
114
  }
115
+ const logoConfig = computed(() => normalizeSectionConfig(props.logo, DEFAULT_LAYOUT_CONFIG.logo));
116
+ const collapseConfig = computed(() => normalizeSectionConfig(props.collapse, DEFAULT_LAYOUT_CONFIG.collapse));
117
+ const footerConfig = computed(() => normalizeSectionConfig(props.footerConfig, DEFAULT_LAYOUT_CONFIG.footerConfig));
118
+ const breadcrumbConfig = computed(() => normalizeSectionConfig(props.breadcrumb, DEFAULT_LAYOUT_CONFIG.breadcrumb));
119
+ const pageContainerConfig = computed(() => ({
120
+ ...DEFAULT_LAYOUT_CONFIG.pageContainerConfig,
121
+ ...props.pageContainer || {}
122
+ }));
115
123
  const tabsConf = computed(() => normalizeSectionConfig(props.tabsConfig, DEFAULT_LAYOUT_CONFIG.tabsConfig));
116
124
  const tabsStateConfig = computed(() => tabsConf.value ?? {});
125
+ const tabsModel = useVModel(props, "tabs", emit, {
126
+ passive: true,
127
+ defaultValue: []
128
+ });
129
+ const collapsedWidth = computed(() => collapseConfig.value?.width ?? DEFAULT_LAYOUT_CONFIG.collapse.width);
130
+ /** Sider 实际渲染宽度:0 表示不可见,否则 = 折叠/展开的真实宽度 */
131
+ const siderWidth = computed(() => {
132
+ const width = props.sider?.width ?? DEFAULT_LAYOUT_CONFIG.sider.width;
133
+ if (!width) return 0;
134
+ if (contentFullscreen.value) return 0;
135
+ if (![
136
+ "side",
137
+ "mix",
138
+ "wide"
139
+ ].includes(layout.value ?? "")) return 0;
140
+ if (props.sider?.hideWhenEmpty && menuState.siderMenus.value.length === 0) return 0;
141
+ return collapsed.value ? collapsedWidth.value : width;
142
+ });
117
143
  const tabsState = useTabsState({
118
- tabs: useVModel(props, "tabs", emit, {
119
- passive: true,
120
- defaultValue: []
121
- }),
144
+ tabs: tabsModel,
122
145
  config: tabsStateConfig,
123
146
  flatMenus: menuState.flatMenus,
124
- collapsed,
125
- siderWidth: props.sider?.width ?? DEFAULT_LAYOUT_CONFIG.sider.width,
126
- collapseWidth: props.collapseConfig ? props.collapseConfig.width ?? DEFAULT_LAYOUT_CONFIG.collapseConfig.width : DEFAULT_LAYOUT_CONFIG.collapseConfig.width,
147
+ siderWidth,
127
148
  isMobile: computed(() => isMobile.value),
128
149
  contentFullscreen,
129
150
  onTabsChange: (val) => {
@@ -163,14 +184,6 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
163
184
  function handleLogoClick() {
164
185
  emit("logoClick");
165
186
  }
166
- const logoConfig = computed(() => normalizeSectionConfig(props.logoConfig, DEFAULT_LAYOUT_CONFIG.logoConfig));
167
- const collapseConfig = computed(() => normalizeSectionConfig(props.collapseConfig, DEFAULT_LAYOUT_CONFIG.collapseConfig));
168
- const footerConfig = computed(() => normalizeSectionConfig(props.footerConfig, DEFAULT_LAYOUT_CONFIG.footerConfig));
169
- const breadcrumbConfig = computed(() => normalizeSectionConfig(props.breadcrumbConfig, DEFAULT_LAYOUT_CONFIG.breadcrumbConfig));
170
- const pageContainerConfig = computed(() => ({
171
- ...DEFAULT_LAYOUT_CONFIG.pageContainerConfig,
172
- ...props.pageContainerConfig || {}
173
- }));
174
187
  const showHeader = computed(() => !contentFullscreen.value && layout.value !== "simple" && props.header?.height !== 0);
175
188
  const showSider = computed(() => !contentFullscreen.value && [
176
189
  "side",
@@ -194,6 +207,8 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
194
207
  })),
195
208
  menuState,
196
209
  logoConfig,
210
+ siderWidth,
211
+ collapsedWidth,
197
212
  breadcrumbConfig,
198
213
  pageContainerConfig,
199
214
  watermark: computed(() => props.watermark),
@@ -227,17 +242,7 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
227
242
  keys: LAYOUT_SLOT_KEYS,
228
243
  render: false
229
244
  });
230
- const normalized = Object.fromEntries(Object.entries(merged).map(([key, value]) => [key, typeof value === "function" ? value : () => value]));
231
- if (slots.logo) normalized.logo = slots.logo;
232
- if (props.collapseConfig) {
233
- const collapseIconSlot = getSlot({
234
- slots,
235
- props: { collapseIcon: props.collapseConfig.icon },
236
- key: "collapseIcon"
237
- });
238
- if (collapseIconSlot) normalized.collapseIcon = collapseIconSlot;
239
- }
240
- return normalized;
245
+ return Object.fromEntries(Object.entries(merged).map(([key, value]) => [key, typeof value === "function" ? value : () => value]));
241
246
  }
242
247
  return () => {
243
248
  if (props.pure) return slots.default?.();
@@ -253,12 +258,17 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
253
258
  "showSider": showSider.value,
254
259
  "proClasses": props.proClasses,
255
260
  "proStyles": props.proStyles
256
- }, { default: () => [showSider.value && createVNode(LayoutSider$1, {
261
+ }, { default: () => [showSider.value && siderWidth.value && createVNode(LayoutSider$1, {
257
262
  "onSelect": handleSelect,
258
263
  "onOpenChange": handleOpenChange,
259
264
  "onLogoClick": handleLogoClick,
260
265
  "onUpdate:collapsed": handleCollapsedUpdate
261
- }, layoutSlots), createVNode("div", {
266
+ }, {
267
+ ...layoutSlots,
268
+ logo: slots.logo,
269
+ collapse: slots.collapse,
270
+ collapseIcon: slots.collapseIcon
271
+ }), createVNode("div", {
262
272
  "style": { position: "relative" },
263
273
  "class": `${prefixCls.value}-container`,
264
274
  "data-testid": "pro-layout-container"
@@ -267,7 +277,13 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
267
277
  "onLogoClick": handleLogoClick,
268
278
  "onUpdate:collapsed": handleCollapsedUpdate,
269
279
  "onSelect": handleSelect
270
- }, layoutSlots),
280
+ }, {
281
+ ...layoutSlots,
282
+ logo: slots.logo,
283
+ collapse: slots.collapse,
284
+ breadcrumb: slots.breadcrumb,
285
+ collapseIcon: slots.collapseIcon
286
+ }),
271
287
  tabsConf.value && createVNode(LayoutTabs, {
272
288
  "config": tabsConf.value,
273
289
  "onReloadPage": () => emit("reloadPage"),
@@ -364,7 +380,7 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
364
380
  type: Object,
365
381
  required: false
366
382
  },
367
- breadcrumbConfig: {
383
+ breadcrumb: {
368
384
  type: [Object, Boolean],
369
385
  required: false,
370
386
  default: void 0
@@ -374,17 +390,17 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
374
390
  required: false,
375
391
  default: void 0
376
392
  },
377
- logoConfig: {
393
+ logo: {
378
394
  type: [Object, Boolean],
379
395
  required: false,
380
396
  default: void 0
381
397
  },
382
- collapseConfig: {
398
+ collapse: {
383
399
  type: [Object, Boolean],
384
400
  required: false,
385
401
  default: void 0
386
402
  },
387
- pageContainerConfig: {
403
+ pageContainer: {
388
404
  type: Object,
389
405
  required: false
390
406
  },
@@ -444,10 +460,6 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
444
460
  type: Function,
445
461
  required: false
446
462
  },
447
- breadcrumb: {
448
- type: Function,
449
- required: false
450
- },
451
463
  tabBarItem: {
452
464
  type: Function,
453
465
  required: false
@@ -1,31 +1,15 @@
1
1
  import { useLayoutBase } from "../../hooks/useLayoutBase.js";
2
- import { Fragment, computed, createTextVNode, createVNode, defineComponent, isVNode } from "vue";
3
- import { classNames, isImg, isString, isUrl } from "@gx-design-vue/pro-utils";
2
+ import { renderMenuIcon } from "../Menu/iconRender.js";
3
+ import { Fragment, computed, createTextVNode, createVNode, defineComponent } from "vue";
4
+ import { classNames } from "@gx-design-vue/pro-utils";
4
5
  import { Dropdown } from "antdv-next";
5
- import { DownOutlined } from "@antdv-next/icons";
6
6
  import { GIcon } from "@gx-design-vue/icon";
7
7
  //#region src/components/Breadcrumb/index.tsx
8
8
  /** 首页占位路由名 */
9
9
  const BREADCRUMB_HOME_KEY = "__breadcrumb_home__";
10
- /**
11
- * 渲染面包屑图标:兼容 VNode / 组件 / 图片地址 / 图标名。
12
- * 复用 LayoutMenu 的 renderMenuIcon 模式。
13
- */
14
- function renderBreadcrumbIcon(icon) {
15
- if (!icon) return null;
16
- if (isVNode(icon)) return icon;
17
- if (isString(icon)) {
18
- if (isUrl(icon) || isImg(icon)) return createVNode("img", {
19
- "src": icon,
20
- "alt": "icon"
21
- }, null);
22
- return createVNode(GIcon, { "type": icon }, null);
23
- }
24
- return null;
25
- }
26
10
  /** 渲染默认面包屑项内容(图标 + 标题) */
27
11
  function renderBreadcrumbItemContent(prefixCls, route) {
28
- const iconNode = renderBreadcrumbIcon(route.meta?.icon);
12
+ const iconNode = renderMenuIcon(route.meta?.icon);
29
13
  return createVNode(Fragment, null, [iconNode && createVNode("span", { "class": `${prefixCls}-item-icon` }, [iconNode]), createVNode("span", null, [route.meta?.title])]);
30
14
  }
31
15
  /**
@@ -94,11 +78,11 @@ const Breadcrumb = /* @__PURE__ */ defineComponent((props, { slots }) => {
94
78
  items: dropdownChildren.map((child) => ({
95
79
  key: child.name,
96
80
  label: child.meta?.title ?? child.name,
97
- icon: renderBreadcrumbIcon(child.meta?.icon)
81
+ icon: renderMenuIcon(child.meta?.icon)
98
82
  })),
99
83
  selectedKeys: dropdownSelectedKeys,
100
84
  onClick: ({ key }) => handleDropdownMenuClick(String(key))
101
- } }, { default: () => [createVNode("span", { "class": `${breadcrumbCls}-overlay-link` }, [itemContent, createVNode(DownOutlined, null, null)])] }) : createVNode("span", {
85
+ } }, { default: () => [createVNode("span", { "class": `${breadcrumbCls}-overlay-link` }, [itemContent, createVNode(GIcon, { "type": "DownOutlined" }, null)])] }) : createVNode("span", {
102
86
  "class": classNames(`${breadcrumbCls}-link`, isLast && `${breadcrumbCls}-link-last`),
103
87
  "onClick": () => handleItemClick(route, isLast)
104
88
  }, [itemContent])]), !isLast && createVNode("li", { "class": `${breadcrumbCls}-separator` }, [createTextVNode("/")])]);
@@ -1,7 +1,8 @@
1
1
  import { useLayoutBase } from "../../hooks/useLayoutBase.js";
2
+ import { ArrowSvgIcon } from "./Arrow.js";
2
3
  import { computed, createVNode, defineComponent } from "vue";
3
4
  import { classNames } from "@gx-design-vue/pro-utils";
4
- import { MenuFoldOutlined, MenuUnfoldOutlined } from "@antdv-next/icons";
5
+ import { GIcon } from "@gx-design-vue/icon";
5
6
  //#region src/components/CollapseButton/index.tsx
6
7
  const LayoutCollapseButton = /* @__PURE__ */ defineComponent((props, { emit, slots }) => {
7
8
  const { prefixCls, collapseConfig } = useLayoutBase();
@@ -11,7 +12,6 @@ const LayoutCollapseButton = /* @__PURE__ */ defineComponent((props, { emit, slo
11
12
  emit("click", event);
12
13
  }
13
14
  return () => {
14
- if (slots.default) return slots.default();
15
15
  return createVNode("div", {
16
16
  "class": collapseButtonClassNames.value,
17
17
  "role": "button",
@@ -24,7 +24,7 @@ const LayoutCollapseButton = /* @__PURE__ */ defineComponent((props, { emit, slo
24
24
  handleClick(event);
25
25
  }
26
26
  }
27
- }, [collapseConfig?.value ? slots.icon?.({ collapsed: props.collapsed }) : props.collapsed ? createVNode(MenuUnfoldOutlined, null, null) : createVNode(MenuFoldOutlined, null, null)]);
27
+ }, [slots.icon?.({ collapsed: props.collapsed }) || placement.value === "sider" ? createVNode(ArrowSvgIcon, null, null) : createVNode(GIcon, { "type": props.collapsed ? "MenuUnfoldOutlined" : "MenuFoldOutlined" }, null)]);
28
28
  };
29
29
  }, {
30
30
  props: {
@@ -14,7 +14,6 @@ interface LayoutCollapseButtonSlots {
14
14
  icon?: (props: {
15
15
  collapsed: boolean;
16
16
  }) => any;
17
- default?: () => any;
18
17
  }
19
18
  //#endregion
20
19
  export { LayoutCollapseButtonEmits, LayoutCollapseButtonEmitsProps, LayoutCollapseButtonProps, LayoutCollapseButtonSlots };
@@ -63,7 +63,8 @@ const Header = /* @__PURE__ */ defineComponent((props, { emit, slots }) => {
63
63
  items,
64
64
  routes
65
65
  };
66
- const customBreadcrumb = breadcrumbRender.value?.(slotProps) ?? slots.breadcrumb?.(slotProps);
66
+ const breadcrumb = slots.breadcrumb || breadcrumbConfig.value?.render;
67
+ const customBreadcrumb = breadcrumbRender.value?.(slotProps) ?? breadcrumb?.(slotProps);
67
68
  if (customBreadcrumb) return createVNode("div", {
68
69
  "class": classNames(`${prefixCls.value}-header-breadcrumb`, proClasses.value.breadcrumb),
69
70
  "style": proStyles.value.breadcrumb
@@ -76,6 +77,9 @@ const Header = /* @__PURE__ */ defineComponent((props, { emit, slots }) => {
76
77
  }
77
78
  return () => {
78
79
  const showHeaderMenu = ["top", "mix"].includes(layout.value ?? "");
80
+ const logoRender = slots.logo || logoConfig.value?.render;
81
+ const collapseRender = slots.collapse || collapseConfig.value?.render;
82
+ const collapseIconRender = slots.collapseIcon || collapseConfig.value?.icon;
79
83
  return createVNode(Fragment, null, [header.value?.fixed && createVNode(LayoutHeader, { "style": {
80
84
  background: "transparent",
81
85
  lineHeight: unit(header.value?.height),
@@ -88,14 +92,14 @@ const Header = /* @__PURE__ */ defineComponent((props, { emit, slots }) => {
88
92
  }
89
93
  }, { default: () => [createVNode("div", { "class": `${prefixCls.value}-header-main` }, [
90
94
  createVNode("div", { "class": `${prefixCls.value}-header-main-left` }, [
91
- showLogo.value && (slots.logo?.({
95
+ showLogo.value && logoConfig.value && (typeof logoRender === "function" ? logoRender?.({
92
96
  collapsed: collapsed.value,
93
97
  title: logoTitle.value
94
- }) ?? (logoConfig.value ? createVNode(LayoutLogo, { "onClick": handleLogoClick }, null) : null)),
95
- collapseConfig.value && collapseConfig.value.placement === "header" && (slots.collapseButton?.({ collapsed: collapsed.value }) ?? createVNode(LayoutCollapseButton, {
98
+ }) : createVNode(LayoutLogo, { "onClick": handleLogoClick }, slots)),
99
+ collapseConfig.value && collapseConfig.value.placement === "header" && (collapseRender?.({ collapsed: collapsed.value }) ?? createVNode(LayoutCollapseButton, {
96
100
  "collapsed": collapsed.value,
97
101
  "onClick": handleToggleCollapsed
98
- }, { icon: collapseConfig.value.icon })),
102
+ }, { icon: collapseIconRender })),
99
103
  renderBreadcrumb()
100
104
  ]),
101
105
  slots.headerContent?.({
@@ -20,6 +20,12 @@ interface LayoutHeaderSlots {
20
20
  collapsed: boolean;
21
21
  title?: string;
22
22
  }) => any;
23
+ collapse?: (props: {
24
+ collapsed: boolean;
25
+ }) => any;
26
+ collapseIcon?: (props: {
27
+ collapsed: boolean;
28
+ }) => any;
23
29
  breadcrumb?: (props: LayoutBreadcrumbSlotProps) => any;
24
30
  headerContent?: (props: LayoutHeaderSlotProps) => any;
25
31
  headerRight?: (props: LayoutHeaderSlotProps) => any;
@@ -4,7 +4,7 @@ import { SlotsType } from "vue";
4
4
  import { CustomRender } from "@gx-design-vue/pro-utils";
5
5
 
6
6
  //#region src/components/Logo/index.d.ts
7
- declare function defaultRenderLogo(logo?: CustomRender | false): CustomRender;
7
+ declare function defaultRenderLogo(logo?: CustomRender | false): any;
8
8
  declare const LayoutLogo: _$vue.DefineSetupFnComponent<LayoutLogoEmitsProps, LayoutLogoEmits, SlotsType<LayoutLogoSlots>, LayoutLogoEmitsProps, _$vue.PublicProps>;
9
9
  //#endregion
10
10
  export { LayoutLogo as default, defaultRenderLogo };
@@ -18,7 +18,7 @@ const LayoutLogo = /* @__PURE__ */ defineComponent((_props, { emit, slots }) =>
18
18
  if (!logoConfig?.value) return "";
19
19
  return logoConfig?.value.title;
20
20
  });
21
- const logoSource = computed(() => !logoConfig?.value ? false : logoConfig?.value.logo);
21
+ const logoSource = computed(() => logoConfig?.value.image);
22
22
  const hideTitle = computed(() => collapsed.value && ["side", "simple"].includes(layout.value ?? ""));
23
23
  const showLogo = computed(() => !collapsed.value || !!collapsedWidth.value);
24
24
  const logoClassNames = computed(() => classNames(`${prefixCls.value}-logo`, proClasses.value.logo, {
@@ -29,15 +29,13 @@ const LayoutLogo = /* @__PURE__ */ defineComponent((_props, { emit, slots }) =>
29
29
  emit("click");
30
30
  }
31
31
  return () => {
32
- if (slots.default) return slots.default();
33
- const logo = slots.logo ? slots.logo() : defaultRenderLogo(logoSource.value);
34
- const hasLogo = logo !== null && logo !== void 0 && logo !== false;
32
+ const image = slots.image ? slots.image() : defaultRenderLogo(logoSource.value);
35
33
  const titleDom = title.value && !hideTitle.value ? createVNode("h1", null, [title.value]) : null;
36
34
  return createVNode("div", {
37
35
  "class": logoClassNames.value,
38
36
  "style": proStyles.value.logo,
39
37
  "onClick": handleClick
40
- }, [createVNode("a", null, [showLogo.value && hasLogo && createVNode("span", {
38
+ }, [createVNode("a", null, [showLogo.value && image && createVNode("span", {
41
39
  "class": proClasses.value.logoImage,
42
40
  "style": {
43
41
  display: "inline-flex",
@@ -45,7 +43,7 @@ const LayoutLogo = /* @__PURE__ */ defineComponent((_props, { emit, slots }) =>
45
43
  fontSize: 22,
46
44
  ...proStyles.value.logoImage
47
45
  }
48
- }, [logo]), titleDom])]);
46
+ }, [image]), titleDom])]);
49
47
  };
50
48
  }, {
51
49
  props: { onClick: {
@@ -7,8 +7,7 @@ interface LayoutLogoEmitsProps {
7
7
  }
8
8
  type LayoutLogoProps = LayoutLogoEmitsProps;
9
9
  interface LayoutLogoSlots {
10
- logo?: () => any;
11
- default?: () => any;
10
+ image?: () => any;
12
11
  }
13
12
  //#endregion
14
13
  export { LayoutLogoEmits, LayoutLogoEmitsProps, LayoutLogoProps, LayoutLogoSlots };
@@ -1,10 +1,10 @@
1
- import { LayoutMenuEmits, LayoutMenuSlots } from "../Menu/interface.js";
1
+ import { LayoutMenuEmits, LayoutMenuSlots } from "./interface.js";
2
2
  import { LayoutClassNamesType, LayoutMenuItemSlotProps, LayoutMenuRoute, LayoutStylesType } from "../../interface.js";
3
3
  import * as _$vue from "vue";
4
4
  import { CSSProperties, SlotsType } from "vue";
5
5
  import { CustomRender } from "@gx-design-vue/pro-utils";
6
6
 
7
- //#region src/components/Sider/SiderMenu.d.ts
7
+ //#region src/components/Menu/SiderMenu.d.ts
8
8
  interface SiderMenuProps {
9
9
  class?: string;
10
10
  prefixCls?: string;
@@ -15,6 +15,8 @@ interface SiderMenuProps {
15
15
  collapsed?: boolean;
16
16
  proClasses?: LayoutClassNamesType;
17
17
  proStyles?: LayoutStylesType;
18
+ /** 由外部(如 Sider)传入,用于获取包裹 SiderMenu 的滚动容器 DOM 引用 */
19
+ getScrollContainer?: () => HTMLElement | undefined;
18
20
  menuItemIcon?: (slotProps: LayoutMenuItemSlotProps) => CustomRender;
19
21
  menuItemLabel?: (slotProps: LayoutMenuItemSlotProps) => CustomRender;
20
22
  menuItemExtra?: (slotProps: LayoutMenuItemSlotProps) => CustomRender;
@@ -1,16 +1,16 @@
1
1
  import { useLayoutBase } from "../../hooks/useLayoutBase.js";
2
+ import { renderMenuIcon } from "./iconRender.js";
2
3
  import { buildSiderProMenuTokens } from "../../utils/themeComponents.js";
3
- import { LAYOUT_MENU_SLOT_KEYS } from "../Menu/interface.js";
4
- import style_default from "./style.js";
5
- import { Transition, computed, createVNode, defineComponent, h, isVNode, mergeProps, nextTick, onBeforeUnmount, ref, vShow, watch, withDirectives } from "vue";
4
+ import { LAYOUT_MENU_SLOT_KEYS } from "./interface.js";
5
+ import siderMenu_default from "./style/siderMenu.js";
6
+ import { Transition, computed, createVNode, defineComponent, isVNode, mergeProps, nextTick, onBeforeUnmount, ref, vShow, watch, withDirectives } from "vue";
6
7
  import { useProToken } from "@gx-design-vue/pro-provider";
7
- import { classNames, getSlotsProps, isImg, isString, isUrl } from "@gx-design-vue/pro-utils";
8
+ import { classNames, getSlotsProps } from "@gx-design-vue/pro-utils";
8
9
  import { ConfigProvider, Popover } from "antdv-next";
9
10
  import { useBaseConfig } from "antdv-next/config-provider/context";
10
11
  import useCSSVarCls from "antdv-next/config-provider/hooks/useCSSVarCls";
11
- import { GIcon } from "@gx-design-vue/icon";
12
12
  import { omit } from "es-toolkit";
13
- //#region src/components/Sider/SiderMenu.tsx
13
+ //#region src/components/Menu/SiderMenu.tsx
14
14
  function _isSlot(s) {
15
15
  return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s);
16
16
  }
@@ -47,19 +47,6 @@ function getPathKey(path) {
47
47
  function getMenuTitleSymbol(title) {
48
48
  return title ? title.charAt(0).toUpperCase() : null;
49
49
  }
50
- function renderMenuIcon(icon) {
51
- if (!icon) return null;
52
- if (isVNode(icon)) return icon;
53
- if (isString(icon)) {
54
- if (isUrl(icon) || isImg(icon)) return createVNode("img", {
55
- "src": icon,
56
- "alt": "icon"
57
- }, null);
58
- return createVNode(GIcon, { "type": icon }, null);
59
- }
60
- if (typeof icon === "function" || typeof icon === "object") return h(icon);
61
- return null;
62
- }
63
50
  function getVisibleChildren(route) {
64
51
  return route.children?.filter((child) => !child.meta?.hideInMenu) ?? [];
65
52
  }
@@ -130,7 +117,7 @@ const SiderMenuContent = /* @__PURE__ */ defineComponent((props, { emit, slots }
130
117
  const { proClasses, proStyles, collapsed } = useLayoutBase(props);
131
118
  const { prefixCls } = useBaseConfig("pro-sider-menu", props);
132
119
  const rootCls = useCSSVarCls(prefixCls);
133
- const [hashId, cssVarCls] = style_default(prefixCls, rootCls);
120
+ const [hashId, cssVarCls] = siderMenu_default(prefixCls, rootCls);
134
121
  const { proLayoutToken } = useProToken();
135
122
  const popupOpenPath = ref([]);
136
123
  const menuRef = ref();
@@ -146,7 +133,7 @@ const SiderMenuContent = /* @__PURE__ */ defineComponent((props, { emit, slots }
146
133
  keys: LAYOUT_MENU_SLOT_KEYS,
147
134
  render: false
148
135
  }));
149
- const menuClassNames = computed(() => classNames(`${prefixCls.value}`, proClasses.value.menu, hashId.value, cssVarCls.value, rootCls.value, { [`${prefixCls.value}-collapsed`]: collapsed.value }, props.class));
136
+ const menuClassNames = computed(() => classNames(`${prefixCls.value}`, `${prefixCls.value}-root`, proClasses.value.menu, hashId.value, cssVarCls.value, rootCls.value, { [`${prefixCls.value}-collapsed`]: collapsed.value }, props.class));
150
137
  const popupClassName = computed(() => classNames(`${prefixCls.value}-popover`, hashId.value, cssVarCls.value, rootCls.value));
151
138
  function isPopupInteraction(ctx) {
152
139
  return collapsed.value || ctx.popup;
@@ -246,7 +233,7 @@ const SiderMenuContent = /* @__PURE__ */ defineComponent((props, { emit, slots }
246
233
  if (hasInitialScrolled.value || hasSiderMenuInitialAutoScrolled || collapsed.value) return;
247
234
  const menuEl = menuRef.value;
248
235
  if (!menuEl) return;
249
- const scrollContainer = menuEl.closest(`.${prefixCls.value}-container`);
236
+ const scrollContainer = props.getScrollContainer?.() ?? menuEl.closest(`.${prefixCls.value}-container`);
250
237
  const selectedItem = menuEl.querySelector(`.${prefixCls.value}-item-selected`);
251
238
  if (!scrollContainer || !selectedItem) return;
252
239
  hasInitialScrolled.value = true;
@@ -376,10 +363,7 @@ const SiderMenuContent = /* @__PURE__ */ defineComponent((props, { emit, slots }
376
363
  renderIcon(route, item, firstLevelCollapsed),
377
364
  !firstLevelCollapsed && renderLabel(route, item),
378
365
  !firstLevelCollapsed && renderExtra(route, item),
379
- !firstLevelCollapsed && createVNode("span", {
380
- "class": `${prefixCls.value}-arrow`,
381
- "aria-hidden": "true"
382
- }, null)
366
+ !firstLevelCollapsed && createVNode("i", { "class": `${prefixCls.value}-arrow` }, null)
383
367
  ]);
384
368
  }
385
369
  function renderSubMenu(route, children, ctx) {
@@ -540,6 +524,10 @@ const SiderMenuContent = /* @__PURE__ */ defineComponent((props, { emit, slots }
540
524
  type: Object,
541
525
  required: false
542
526
  },
527
+ getScrollContainer: {
528
+ type: Function,
529
+ required: false
530
+ },
543
531
  menuItemIcon: {
544
532
  type: Function,
545
533
  required: false
@@ -632,6 +620,10 @@ const SiderMenu = /* @__PURE__ */ defineComponent((props, { emit, slots }) => {
632
620
  type: Object,
633
621
  required: false
634
622
  },
623
+ getScrollContainer: {
624
+ type: Function,
625
+ required: false
626
+ },
635
627
  menuItemIcon: {
636
628
  type: Function,
637
629
  required: false
@@ -0,0 +1,10 @@
1
+ import { VNode } from "vue";
2
+
3
+ //#region src/components/Menu/iconRender.d.ts
4
+ /**
5
+ * 渲染菜单/面包屑图标:兼容 VNode / 组件 / 图片地址 / 图标名。
6
+ * 被 LayoutMenu、SiderMenu、Breadcrumb 等组件共用。
7
+ */
8
+ declare function renderMenuIcon(icon: any): VNode | string | null;
9
+ //#endregion
10
+ export { renderMenuIcon };
@@ -0,0 +1,23 @@
1
+ import { createVNode, h, isVNode } from "vue";
2
+ import { isImg, isString, isUrl } from "@gx-design-vue/pro-utils";
3
+ import { GIcon } from "@gx-design-vue/icon";
4
+ //#region src/components/Menu/iconRender.tsx
5
+ /**
6
+ * 渲染菜单/面包屑图标:兼容 VNode / 组件 / 图片地址 / 图标名。
7
+ * 被 LayoutMenu、SiderMenu、Breadcrumb 等组件共用。
8
+ */
9
+ function renderMenuIcon(icon) {
10
+ if (!icon) return null;
11
+ if (isVNode(icon)) return icon;
12
+ if (isString(icon)) {
13
+ if (isUrl(icon) || isImg(icon)) return createVNode("img", {
14
+ "src": icon,
15
+ "alt": "icon"
16
+ }, null);
17
+ return createVNode(GIcon, { "type": icon }, null);
18
+ }
19
+ if (typeof icon === "function" || typeof icon === "object") return h(icon);
20
+ return null;
21
+ }
22
+ //#endregion
23
+ export { renderMenuIcon };
@@ -1,31 +1,17 @@
1
1
  import { useLayoutBase } from "../../hooks/useLayoutBase.js";
2
+ import { renderMenuIcon } from "./iconRender.js";
2
3
  import { buildHeaderMenuTokens, buildSiderMenuTokens } from "../../utils/themeComponents.js";
3
4
  import { buildMenuItems } from "./convert.js";
4
5
  import { LAYOUT_MENU_SLOT_KEYS } from "./interface.js";
5
- import { computed, createVNode, defineComponent, h, isVNode } from "vue";
6
+ import { computed, createVNode, defineComponent } from "vue";
6
7
  import { useProToken } from "@gx-design-vue/pro-provider";
7
- import { classNames, getSlotsProps, isImg, isString, isUrl } from "@gx-design-vue/pro-utils";
8
+ import { classNames, getSlotsProps } from "@gx-design-vue/pro-utils";
8
9
  import { ConfigProvider, Menu } from "antdv-next";
9
- import { GIcon } from "@gx-design-vue/icon";
10
10
  //#region src/components/Menu/index.tsx
11
11
  /** 取菜单项首字母(折叠态无图标时的兜底展示)。 */
12
12
  function getMenuTitleSymbol(title) {
13
13
  return title ? title.charAt(0).toUpperCase() : null;
14
14
  }
15
- /** 渲染菜单图标:兼容 VNode / 组件 / 图片地址 / 图标名。 */
16
- function renderMenuIcon(icon) {
17
- if (!icon) return null;
18
- if (isVNode(icon)) return icon;
19
- if (isString(icon)) {
20
- if (isUrl(icon) || isImg(icon)) return createVNode("img", {
21
- "src": icon,
22
- "alt": "icon"
23
- }, null);
24
- return createVNode(GIcon, { "type": icon }, null);
25
- }
26
- if (typeof icon === "function" || typeof icon === "object") return h(icon);
27
- return null;
28
- }
29
15
  /**
30
16
  * 将携带 `route` 的菜单项扁平为 `key → route`,供 `select` 事件回填 `LayoutSelectInfo.route`。
31
17
  */
@@ -2,7 +2,7 @@ import * as _$vue from "vue";
2
2
  import { CSSProperties } from "vue";
3
3
  import { GetDefaultToken } from "antdv-next/dist/theme/internal";
4
4
 
5
- //#region src/components/Sider/style.d.ts
5
+ //#region src/components/Menu/style/siderMenu.d.ts
6
6
  /** Component only token. Which will handle additional calculation of alias token */
7
7
  interface ComponentToken {
8
8
  /**
@@ -1,6 +1,6 @@
1
1
  import { proGenStyleHooks, setAlpha } from "@gx-design-vue/pro-provider";
2
2
  import { unit as unit$1 } from "@antdv-next/cssinjs";
3
- //#region src/components/Sider/style.ts
3
+ //#region src/components/Menu/style/siderMenu.ts
4
4
  const prepareComponentToken = (token) => {
5
5
  const { colorPrimary, colorError, colorTextDisabled, colorErrorBg, colorText, colorTextDescription, colorBgContainer, colorFillAlter, colorFillContent, lineWidth, lineWidthBold, controlItemBgActive, colorBgTextHover, controlHeightLG, lineHeight, colorBgElevated, marginXXS, padding, fontSize, controlHeightSM, fontSizeLG, colorTextLightSolid, colorErrorHover } = token;
6
6
  const activeBarWidth = token.activeBarWidth ?? 0;
@@ -103,15 +103,16 @@ const genSiderMenuStyle = (token) => {
103
103
  return {
104
104
  [`${componentCls}`]: {
105
105
  boxSizing: "border-box",
106
- width: "100%",
107
106
  margin: 0,
108
107
  padding: 0,
109
108
  color: token.siderColorTextMenu || token.itemColor,
110
109
  fontSize: token.fontSize,
111
- lineHeight: token.lineHeight,
110
+ lineHeight: 0,
112
111
  listStyle: "none",
112
+ overflow: "hidden",
113
113
  background: token.siderColorMenuBackground || token.itemBg || "transparent",
114
114
  outline: "none",
115
+ transition: `width ${token.motionDurationSlow} cubic-bezier(0.2, 0, 0, 1) 0s`,
115
116
  "ul, ol": {
116
117
  margin: 0,
117
118
  padding: 0,
@@ -263,7 +264,6 @@ const genSiderMenuStyle = (token) => {
263
264
  top: "50%",
264
265
  insetInlineEnd: token.margin,
265
266
  width: siderMenuArrowSize,
266
- height: siderMenuArrowSize,
267
267
  color: "currentcolor",
268
268
  transform: "translateY(-50%)",
269
269
  transition: ["transform", "opacity"].map((prop) => `${prop} ${token.motionDurationSlow}`).join(","),
@@ -363,6 +363,6 @@ const genSiderMenuStyle = (token) => {
363
363
  }
364
364
  };
365
365
  };
366
- var style_default = proGenStyleHooks("ProSiderMenu", genSiderMenuStyle, prepareComponentToken);
366
+ var siderMenu_default = proGenStyleHooks("ProSiderMenu", genSiderMenuStyle, prepareComponentToken);
367
367
  //#endregion
368
- export { style_default as default, prepareComponentToken };
368
+ export { siderMenu_default as default, prepareComponentToken };
@@ -1,7 +1,6 @@
1
1
  import { BreadcrumbItemType, LayoutBreadcrumbConfig, LayoutBreadcrumbSlotProps, LayoutMenuRoute, PageContainerClassNamesType, PageContainerStylesType } from "../../interface.js";
2
2
  import * as _$vue from "vue";
3
3
  import { SlotsType } from "vue";
4
- import { CustomRender } from "@gx-design-vue/pro-utils";
5
4
 
6
5
  //#region src/components/PageContainer/PageHeader.d.ts
7
6
  interface PageHeaderProps {
@@ -14,8 +13,6 @@ interface PageHeaderProps {
14
13
  /** 面包屑数据 */
15
14
  breadcrumbItems: BreadcrumbItemType[];
16
15
  breadcrumbRoutes: LayoutMenuRoute[];
17
- /** 自定义面包屑渲染 */
18
- breadcrumb?: (slotProps: LayoutBreadcrumbSlotProps) => CustomRender;
19
16
  /** 语义样式 */
20
17
  proStyles?: PageContainerStylesType;
21
18
  proClasses?: PageContainerClassNamesType;