Unverified Commit 5bddea71 authored by Sendya's avatar Sendya

fix: Menu, BasicLayout, VNodeType

parent ec3c7128
...@@ -13,9 +13,9 @@ Ant Design Pro Layout ...@@ -13,9 +13,9 @@ Ant Design Pro Layout
```bash ```bash
# yarn # yarn
yarn add @ant-design-vue/pro-layout yarn add @ant-design-vue/pro-layout@next
# npm # npm
npm i @ant-design-vue/pro-layout -S npm i @ant-design-vue/pro-layout@next -S
``` ```
## Basic Usage ## Basic Usage
...@@ -46,7 +46,7 @@ After that, you can use pro-layout in your Vue components as simply as this: ...@@ -46,7 +46,7 @@ After that, you can use pro-layout in your Vue components as simply as this:
<script> <script>
import { defineComponent, reactive } from 'vue'; import { defineComponent, reactive } from 'vue';
import ProLayout, { createRouteContext } from '@ant-design-vue/pro-layout'; import { createRouteContext } from '@ant-design-vue/pro-layout';
const [ RouteContextProvider ] = createRouteContext(); const [ RouteContextProvider ] = createRouteContext();
...@@ -56,9 +56,7 @@ export default defineComponent({ ...@@ -56,9 +56,7 @@ export default defineComponent({
collapsed: false, collapsed: false,
openKeys: ['/dashboard'], openKeys: ['/dashboard'],
setOpenKeys: (keys) => (state.openKeys = keys),
selectedKeys: ['/welcome'], selectedKeys: ['/welcome'],
setSelectedKeys: (keys) => (state.selectedKeys = keys),
isMobile: false, isMobile: false,
fixSiderbar: false, fixSiderbar: false,
...@@ -76,7 +74,6 @@ export default defineComponent({ ...@@ -76,7 +74,6 @@ export default defineComponent({
} }
}, },
components: { components: {
ProLayout,
RouteContextProvider, RouteContextProvider,
} }
}); });
...@@ -95,9 +92,7 @@ export default defineComponent({ ...@@ -95,9 +92,7 @@ export default defineComponent({
collapsed: false, collapsed: false,
openKeys: ['/dashboard'], openKeys: ['/dashboard'],
setOpenKeys: (keys: string[]) => (state.openKeys = keys),
selectedKeys: ['/welcome'], selectedKeys: ['/welcome'],
setSelectedKeys: (keys: string[]) => (state.selectedKeys = keys),
isMobile: false, isMobile: false,
fixSiderbar: false, fixSiderbar: false,
......
...@@ -3,7 +3,7 @@ module.exports = { ...@@ -3,7 +3,7 @@ module.exports = {
test: { test: {
presets: [['@babel/preset-env', { targets: { node: true } }]], presets: [['@babel/preset-env', { targets: { node: true } }]],
plugins: [ plugins: [
'@vue/babel-plugin-jsx', ['@vue/babel-plugin-jsx', { mergeProps: false }],
'@babel/plugin-proposal-optional-chaining', '@babel/plugin-proposal-optional-chaining',
'@babel/plugin-transform-object-assign', '@babel/plugin-transform-object-assign',
'@babel/plugin-proposal-object-rest-spread', '@babel/plugin-proposal-object-rest-spread',
......
import { RouteProps } from '../src/typings'; export const menus = [
export const menus: RouteProps[] = [
{ {
path: '/welcome', path: '/welcome',
name: 'welcome', name: 'welcome',
......
import { createApp, reactive } from 'vue';
import { default as ProLayout, createRouteContext, RouteContextProps } from '../src/';
import { RouterLink } from './mock-router';
import { menus } from './menus';
import registerIcons from './_util/icons';
const SimpleDemo = {
setup() {
const [RouteContextProvider] = createRouteContext();
const appState = reactive<RouteContextProps>({
selectedKeys: [],
openKeys: [],
collapsed: true,
menuData: menus,
});
return () => (
<RouteContextProvider value={appState}>
<ProLayout
title="Pro Tests"
logo="https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg"
layout="side"
navTheme="light"
contentWidth="Fluid"
contentStyle={{ minHeight: '300px' }}
collapsed={appState.collapsed}
onCollapse={collapsed => {
appState.collapsed = collapsed;
}}
onSelect={(selectedKeys: string[] | false) => {
selectedKeys && (appState.selectedKeys = selectedKeys);
}}
onOpenKeys={(openKeys: string[] | false) => {
console.log('onOpenKeys', openKeys);
openKeys && (appState.openKeys = openKeys);
}}
footerRender={() => <div>custom-footer</div>}
v-slots={{
rightContentRender: props => (
<div class="custom-header-right-content">
<span>custom-right-content</span>
</div>
),
}}
>
<div>content</div>
</ProLayout>
</RouteContextProvider>
);
},
};
const app = createApp(SimpleDemo);
app.use(ProLayout);
app.use(RouterLink);
app.use(registerIcons);
app.mount('#__vue-content>div');
import { computed, FunctionalComponent, CSSProperties, VNodeChild, VNode, unref } from 'vue'; import { computed, FunctionalComponent, CSSProperties, unref } from 'vue';
import 'ant-design-vue/es/layout/style'; import 'ant-design-vue/es/layout/style';
import Layout from 'ant-design-vue/es/layout'; import Layout from 'ant-design-vue/es/layout';
import { withInstall } from 'ant-design-vue/es/_util/type'; import { withInstall } from 'ant-design-vue/es/_util/type';
...@@ -6,8 +6,8 @@ import { default as ProProvider, ProProviderData } from './ProProvider'; ...@@ -6,8 +6,8 @@ import { default as ProProvider, ProProviderData } from './ProProvider';
import { default as SiderMenuWrapper, SiderMenuWrapperProps } from './SiderMenu'; import { default as SiderMenuWrapper, SiderMenuWrapperProps } from './SiderMenu';
import { WrapContent } from './WrapContent'; import { WrapContent } from './WrapContent';
import { default as Header, HeaderViewProps } from './Header'; import { default as Header, HeaderViewProps } from './Header';
import { RenderVNodeType, WithFalse } from './typings'; import { VNodeType, CustomRender, WithFalse } from './typings';
import { getComponentOrSlot, PropRenderType, PropTypes } from './utils'; import { getCustomRender, PropRenderType, PropTypes } from './utils';
import useMediaQuery from './hooks/useMediaQuery'; import useMediaQuery from './hooks/useMediaQuery';
import './BasicLayout.less'; import './BasicLayout.less';
...@@ -19,7 +19,7 @@ export type BasicLayoutProps = SiderMenuWrapperProps & ...@@ -19,7 +19,7 @@ export type BasicLayoutProps = SiderMenuWrapperProps &
/** /**
*@name logo url *@name logo url
*/ */
logo?: string | RenderVNodeType | WithFalse<string | RenderVNodeType>; logo?: VNodeType;
loading?: boolean; loading?: boolean;
...@@ -29,11 +29,9 @@ export type BasicLayoutProps = SiderMenuWrapperProps & ...@@ -29,11 +29,9 @@ export type BasicLayoutProps = SiderMenuWrapperProps &
onCollapse?: (collapsed: boolean) => void; onCollapse?: (collapsed: boolean) => void;
footerRender?: WithFalse< footerRender?: WithFalse<(props: any /* FooterProps */) => VNodeType>;
(props: any /* FooterProps */, defaultDom: RenderVNodeType) => RenderVNodeType
>;
headerRender?: WithFalse<(props: any /* HeaderProps */) => RenderVNodeType>; headerRender?: WithFalse<(props: any /* HeaderProps */) => VNodeType>;
colSize?: string; colSize?: string;
/** /**
...@@ -68,19 +66,16 @@ const ProLayout: FunctionalComponent<BasicLayoutProps> = (props, { emit, slots } ...@@ -68,19 +66,16 @@ const ProLayout: FunctionalComponent<BasicLayoutProps> = (props, { emit, slots }
const isTop = computed(() => layout === 'top'); const isTop = computed(() => layout === 'top');
// const isSide = computed(() => layout === 'side'); // const isSide = computed(() => layout === 'side');
// const isMix = computed(() => layout === 'mix'); // const isMix = computed(() => layout === 'mix');
// if on event and @event
const handleCollapse = (collapsed: boolean) => { const onCollapse =
propsOnCollapse && propsOnCollapse(collapsed); (propsOnCollapse && propsOnCollapse) ||
emit('update:collapsed', collapsed); ((collapsed: boolean) => emit('update:collapsed', collapsed));
}; const onOpenKeys =
const handleOpenKeys = (openKeys: string[] | false): void => { (propsOnOpenKeys && propsOnOpenKeys) ||
propsOnOpenKeys && propsOnOpenKeys(openKeys); ((openKeys: string[] | false) => emit('update:open-keys', openKeys));
emit('update:open-keys', openKeys); const onSelect =
}; (propsOnSelect && propsOnSelect) ||
const handleSelect = (selectedKeys: string[] | false): void => { ((selectedKeys: string[] | false) => emit('update:selected-keys', selectedKeys));
propsOnSelect && propsOnSelect(selectedKeys);
emit('update:selected-keys', selectedKeys);
};
const colSize = useMediaQuery(); const colSize = useMediaQuery();
const isMobile = computed( const isMobile = computed(
() => (colSize.value === 'sm' || colSize.value === 'xs') && !props.disableMobile, () => (colSize.value === 'sm' || colSize.value === 'xs') && !props.disableMobile,
...@@ -115,42 +110,39 @@ const ProLayout: FunctionalComponent<BasicLayoutProps> = (props, { emit, slots } ...@@ -115,42 +110,39 @@ const ProLayout: FunctionalComponent<BasicLayoutProps> = (props, { emit, slots }
const headerRender = ( const headerRender = (
props: BasicLayoutProps & { props: BasicLayoutProps & {
hasSiderMenu: boolean; hasSiderMenu: boolean;
customHeaderRender: VNodeChild | false; customHeaderRender: WithFalse<CustomRender>;
rightContentRender: VNodeChild | VNode | false; rightContentRender: WithFalse<CustomRender>;
}, },
matchMenuKeys: string[], matchMenuKeys: string[],
): RenderVNodeType => { ): VNodeType => {
if (props.headerRender === false || props.pure) { if (props.headerRender === false || props.pure) {
return null; return null;
} }
return <Header matchMenuKeys={matchMenuKeys} {...props} headerHeight={48} />; return <Header matchMenuKeys={matchMenuKeys} {...props} headerHeight={48} />;
}; };
const rightContentRender = getComponentOrSlot(props, slots, 'rightContentRender') as any; const rightContentRender = getCustomRender(props, slots, 'rightContentRender');
const customHeaderRender = getComponentOrSlot(props, slots, 'headerRender'); const customHeaderRender = getCustomRender(props, slots, 'headerRender');
const menuHeaderRenderFunc = props['menuHeaderRender']; const menuHeaderRender = getCustomRender(props, slots, 'menuHeaderRender');
const menuHeaderRenderSlot = slots['menuHeaderRender']; const footerRender = getCustomRender(props, slots, 'footerRender');
// const menuRender = getCustomRender(props, slots, 'menuRender');
const headerDom = headerRender( const headerDom = headerRender(
{ {
...props, ...props,
hasSiderMenu: !isTop.value, hasSiderMenu: !isTop.value,
menuData, menuData,
isMobile: unref(isMobile), isMobile: unref(isMobile),
onCollapse: handleCollapse, onCollapse,
onSelect: handleSelect, onOpenKeys,
onOpenKeys: handleOpenKeys, onSelect,
customHeaderRender, customHeaderRender,
rightContentRender, rightContentRender,
headerTitleRender: headerTitleRender: menuHeaderRender,
menuHeaderRenderFunc || (menuHeaderRenderSlot && (() => menuHeaderRenderSlot())),
theme: (navTheme || 'dark').toLocaleLowerCase().includes('dark') ? 'dark' : 'light', theme: (navTheme || 'dark').toLocaleLowerCase().includes('dark') ? 'dark' : 'light',
}, },
matchMenuKeys, matchMenuKeys,
); );
const footerRender = getComponentOrSlot(props, slots, 'footerRender');
// const menuRender = getComponentOrSlot(props, slots, 'menuRender');
// const menuHeaderRender = getComponentOrSlot(props, slots, 'menuHeaderRender');
return ( return (
<ProProvider i18n={defaultI18nRender}> <ProProvider i18n={defaultI18nRender}>
{props.pure ? ( {props.pure ? (
...@@ -162,12 +154,10 @@ const ProLayout: FunctionalComponent<BasicLayoutProps> = (props, { emit, slots } ...@@ -162,12 +154,10 @@ const ProLayout: FunctionalComponent<BasicLayoutProps> = (props, { emit, slots }
<SiderMenuWrapper <SiderMenuWrapper
{...props} {...props}
isMobile={isMobile.value} isMobile={isMobile.value}
menuHeaderRender={ menuHeaderRender={menuHeaderRender}
menuHeaderRenderFunc || (menuHeaderRenderSlot && (() => menuHeaderRenderSlot())) onCollapse={onCollapse}
} onSelect={onSelect}
onCollapse={handleCollapse} onOpenKeys={onOpenKeys}
onSelect={handleSelect}
onOpenKeys={handleOpenKeys}
/> />
)} )}
<Layout style={genLayoutStyle}> <Layout style={genLayoutStyle}>
...@@ -178,7 +168,7 @@ const ProLayout: FunctionalComponent<BasicLayoutProps> = (props, { emit, slots } ...@@ -178,7 +168,7 @@ const ProLayout: FunctionalComponent<BasicLayoutProps> = (props, { emit, slots }
> >
{slots.default?.()} {slots.default?.()}
</WrapContent> </WrapContent>
{footerRender !== false && footerRender && footerRender} {footerRender && footerRender(props)}
</Layout> </Layout>
</Layout> </Layout>
</div> </div>
...@@ -266,6 +256,8 @@ ProLayout.props = { ...@@ -266,6 +256,8 @@ ProLayout.props = {
collapsed: PropTypes.bool, collapsed: PropTypes.bool,
/* 菜单的折叠收起事件 (collapsed: boolean) => void */ /* 菜单的折叠收起事件 (collapsed: boolean) => void */
onCollapse: PropTypes.func, onCollapse: PropTypes.func,
onSelect: PropTypes.func,
onOpenKeys: PropTypes.func,
// onPageChange // 请使用 vue-router 监听 // onPageChange // 请使用 vue-router 监听
/* 禁止自动切换到移动页面 */ /* 禁止自动切换到移动页面 */
disableMobile: PropTypes.bool, disableMobile: PropTypes.bool,
......
import { computed, CSSProperties, FunctionalComponent } from 'vue'; import { computed, CSSProperties, FunctionalComponent } from 'vue';
import { PureSettings } from '../defaultSettings'; import { PureSettings } from '../defaultSettings';
import { RenderVNodeType, MenuDataItem, WithFalse } from '../typings'; import { VNodeType, MenuDataItem, WithFalse } from '../typings';
import { import {
SiderMenuProps, SiderMenuProps,
PrivateSiderMenuProps, PrivateSiderMenuProps,
...@@ -18,9 +18,9 @@ export interface GlobalHeaderProps extends Partial<PureSettings> { ...@@ -18,9 +18,9 @@ export interface GlobalHeaderProps extends Partial<PureSettings> {
collapsed?: boolean; collapsed?: boolean;
onCollapse?: (collapsed: boolean) => void; onCollapse?: (collapsed: boolean) => void;
isMobile?: boolean; isMobile?: boolean;
logo?: RenderVNodeType; logo?: VNodeType;
menuRender?: WithFalse<(props: HeaderViewProps, defaultDom: RenderVNodeType) => RenderVNodeType>; menuRender?: WithFalse<(props: HeaderViewProps, defaultDom: VNodeType) => VNodeType>;
rightContentRender?: WithFalse<(props: HeaderViewProps) => RenderVNodeType>; rightContentRender?: WithFalse<(props: HeaderViewProps) => VNodeType>;
className?: string; className?: string;
prefixCls?: string; prefixCls?: string;
menuData?: MenuDataItem[]; menuData?: MenuDataItem[];
...@@ -33,10 +33,7 @@ export interface GlobalHeaderProps extends Partial<PureSettings> { ...@@ -33,10 +33,7 @@ export interface GlobalHeaderProps extends Partial<PureSettings> {
onSelect?: (selectedKeys: WithFalse<string[]>) => void; onSelect?: (selectedKeys: WithFalse<string[]>) => void;
} }
const renderLogo = ( const renderLogo = (menuHeaderRender: SiderMenuProps['menuHeaderRender'], logoDom: VNodeType) => {
menuHeaderRender: SiderMenuProps['menuHeaderRender'],
logoDom: RenderVNodeType,
) => {
if (menuHeaderRender === false) { if (menuHeaderRender === false) {
return null; return null;
} }
......
...@@ -5,7 +5,7 @@ import Layout from 'ant-design-vue/es/layout'; ...@@ -5,7 +5,7 @@ import Layout from 'ant-design-vue/es/layout';
import { GlobalHeader, GlobalHeaderProps } from './GlobalHeader'; import { GlobalHeader, GlobalHeaderProps } from './GlobalHeader';
import { TopNavHeader } from './TopNavHeader'; import { TopNavHeader } from './TopNavHeader';
import { useRouteContext } from './RouteContext'; import { useRouteContext } from './RouteContext';
import { RenderVNodeType, WithFalse } from './typings'; import { VNodeType, WithFalse } from './typings';
import { clearMenuItem } from './utils'; import { clearMenuItem } from './utils';
import './Header.less'; import './Header.less';
...@@ -18,15 +18,11 @@ interface HeaderViewState { ...@@ -18,15 +18,11 @@ interface HeaderViewState {
export type HeaderViewProps = GlobalHeaderProps & { export type HeaderViewProps = GlobalHeaderProps & {
isMobile?: boolean; isMobile?: boolean;
collapsed?: boolean; collapsed?: boolean;
logo?: RenderVNodeType; logo?: VNodeType;
headerRender?: WithFalse< headerRender?: WithFalse<(props: HeaderViewProps, defaultDom: VNodeType) => VNodeType>;
(props: HeaderViewProps, defaultDom: RenderVNodeType) => RenderVNodeType headerTitleRender?: WithFalse<(props: HeaderViewProps, defaultDom: VNodeType) => VNodeType>;
>; headerContentRender?: WithFalse<(props: HeaderViewProps) => VNodeType>;
headerTitleRender?: WithFalse<
(props: HeaderViewProps, defaultDom: RenderVNodeType) => RenderVNodeType
>;
headerContentRender?: WithFalse<(props: HeaderViewProps) => RenderVNodeType>;
siderWidth?: number; siderWidth?: number;
hasSiderMenu?: boolean; hasSiderMenu?: boolean;
}; };
...@@ -66,8 +62,6 @@ export const HeaderView = defineComponent({ ...@@ -66,8 +62,6 @@ export const HeaderView = defineComponent({
setup(props: HeaderViewProps) { setup(props: HeaderViewProps) {
const { const {
prefixCls, prefixCls,
headerRender,
headerContentRender,
isMobile, isMobile,
fixedHeader, fixedHeader,
hasSiderMenu, hasSiderMenu,
...@@ -96,7 +90,7 @@ export const HeaderView = defineComponent({ ...@@ -96,7 +90,7 @@ export const HeaderView = defineComponent({
const renderContent = () => { const renderContent = () => {
let defaultDom = ( let defaultDom = (
<GlobalHeader {...props} onCollapse={onCollapse.value} menuData={clearMenuData.value}> <GlobalHeader {...props} onCollapse={onCollapse.value} menuData={clearMenuData.value}>
{headerContentRender && headerContentRender.value && headerContentRender.value(props)} {props.headerContentRender && props.headerContentRender(props)}
</GlobalHeader> </GlobalHeader>
); );
if (isTop.value && !isMobile.value) { if (isTop.value && !isMobile.value) {
...@@ -110,8 +104,8 @@ export const HeaderView = defineComponent({ ...@@ -110,8 +104,8 @@ export const HeaderView = defineComponent({
/> />
); );
} }
if (headerRender.value && typeof headerRender.value === 'function') { if (props.headerRender) {
return headerRender.value(props, defaultDom); return props.headerRender(props, defaultDom);
} }
return defaultDom; return defaultDom;
}; };
......
...@@ -117,7 +117,6 @@ class MenuUtil { ...@@ -117,7 +117,6 @@ class MenuUtil {
constructor(props: BaseMenuProps) { constructor(props: BaseMenuProps) {
this.props = props; this.props = props;
console.log('MenuUtil constructor', new Date());
} }
getNavMenuItems = (menusData: MenuDataItem[] = [], isChildren: boolean) => { getNavMenuItems = (menusData: MenuDataItem[] = [], isChildren: boolean) => {
......
...@@ -4,7 +4,7 @@ import Layout from 'ant-design-vue/es/layout'; ...@@ -4,7 +4,7 @@ import Layout from 'ant-design-vue/es/layout';
import 'ant-design-vue/es/menu/style'; import 'ant-design-vue/es/menu/style';
import Menu from 'ant-design-vue/es/menu'; import Menu from 'ant-design-vue/es/menu';
import BaseMenu, { BaseMenuProps } from './BaseMenu'; import BaseMenu, { BaseMenuProps } from './BaseMenu';
import { WithFalse, RenderVNodeType } from '../typings'; import { WithFalse, VNodeType } from '../typings';
import { SiderProps } from './typings'; import { SiderProps } from './typings';
import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue'; import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue';
import { useProProvider } from '../ProProvider'; import { useProProvider } from '../ProProvider';
...@@ -20,27 +20,26 @@ export type PrivateSiderMenuProps = { ...@@ -20,27 +20,26 @@ export type PrivateSiderMenuProps = {
export interface SiderMenuProps export interface SiderMenuProps
extends Pick<BaseMenuProps, Exclude<keyof BaseMenuProps, ['onCollapse']>> { extends Pick<BaseMenuProps, Exclude<keyof BaseMenuProps, ['onCollapse']>> {
logo?: RenderVNodeType; logo?: VNodeType;
siderWidth?: number; siderWidth?: number;
collapsedWidth?: number; collapsedWidth?: number;
menuHeaderRender?: WithFalse< menuHeaderRender?: WithFalse<
(logo: RenderVNodeType, title: RenderVNodeType, props?: SiderMenuProps) => RenderVNodeType (logo: VNodeType, title: VNodeType, props?: SiderMenuProps) => VNodeType
>; >;
menuFooterRender?: WithFalse<(props?: SiderMenuProps) => RenderVNodeType>; menuFooterRender?: WithFalse<(props?: SiderMenuProps) => VNodeType>;
menuContentRender?: WithFalse< menuContentRender?: WithFalse<(props: SiderMenuProps, defaultDom: VNodeType) => VNodeType>;
(props: SiderMenuProps, defaultDom: RenderVNodeType) => RenderVNodeType menuExtraRender?: WithFalse<(props: SiderMenuProps) => VNodeType>;
>; collapsedButtonRender?: WithFalse<(collapsed?: boolean) => VNodeType>;
menuExtraRender?: WithFalse<(props: SiderMenuProps) => RenderVNodeType>;
collapsedButtonRender?: WithFalse<(collapsed?: boolean) => RenderVNodeType>;
breakpoint?: SiderProps['breakpoint'] | false; breakpoint?: SiderProps['breakpoint'] | false;
onMenuHeaderClick?: (e: MouseEvent) => void; onMenuHeaderClick?: (e: MouseEvent) => void;
fixed?: boolean; fixed?: boolean;
hide?: boolean; hide?: boolean;
onCollapse?: (collapsed: boolean) => void;
onOpenKeys?: (openKeys: WithFalse<string[]>) => void; onOpenKeys?: (openKeys: WithFalse<string[]>) => void;
onSelect?: (selectedKeys: WithFalse<string[]>) => void; onSelect?: (selectedKeys: WithFalse<string[]>) => void;
} }
export const defaultRenderLogo = (logo: RenderVNodeType): RenderVNodeType => { export const defaultRenderLogo = (logo: VNodeType): VNodeType => {
if (typeof logo === 'string') { if (typeof logo === 'string') {
return <img src={logo} alt="logo" />; return <img src={logo} alt="logo" />;
} }
...@@ -53,7 +52,7 @@ export const defaultRenderLogo = (logo: RenderVNodeType): RenderVNodeType => { ...@@ -53,7 +52,7 @@ export const defaultRenderLogo = (logo: RenderVNodeType): RenderVNodeType => {
export const defaultRenderLogoAndTitle = ( export const defaultRenderLogoAndTitle = (
props: SiderMenuProps, props: SiderMenuProps,
renderKey: string | undefined = 'menuHeaderRender', renderKey: string | undefined = 'menuHeaderRender',
): RenderVNodeType => { ): VNodeType => {
const { const {
logo = 'https://gw.alipayobjects.com/zos/antfincdn/PmY%24TNNDBI/logo.svg', logo = 'https://gw.alipayobjects.com/zos/antfincdn/PmY%24TNNDBI/logo.svg',
title, title,
...@@ -81,7 +80,7 @@ export const defaultRenderLogoAndTitle = ( ...@@ -81,7 +80,7 @@ export const defaultRenderLogoAndTitle = (
); );
}; };
export const defaultRenderCollapsedButton = (collapsed?: boolean): RenderVNodeType => export const defaultRenderCollapsedButton = (collapsed?: boolean): VNodeType =>
collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />; collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />;
const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) => { const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) => {
...@@ -102,7 +101,6 @@ const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) = ...@@ -102,7 +101,6 @@ const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) =
const { getPrefixCls } = useProProvider(); const { getPrefixCls } = useProProvider();
const context = useRouteContext(); const context = useRouteContext();
const baseClassName = getPrefixCls('sider'); const baseClassName = getPrefixCls('sider');
// const isMix = computed(() => props.layout === 'mix'); // const isMix = computed(() => props.layout === 'mix');
// const fixed = computed(() => context.fixSiderbar); // const fixed = computed(() => context.fixSiderbar);
const runtimeTheme = computed(() => (props.layout === 'mix' && 'light') || props.navTheme); const runtimeTheme = computed(() => (props.layout === 'mix' && 'light') || props.navTheme);
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
border-right: 0; border-right: 0;
transition: background-color 0.3s, min-width 0.3s, transition: background-color 0.3s, min-width 0.3s,
max-width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); max-width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
z-index: 9; z-index: 100;
&.@{ant-prefix}-menu-vertical .@{ant-prefix}-menu-item:not(:last-child), &.@{ant-prefix}-menu-vertical .@{ant-prefix}-menu-item:not(:last-child),
&.@{ant-prefix}-menu-vertical-left .@{ant-prefix}-menu-item:not(:last-child), &.@{ant-prefix}-menu-vertical-left .@{ant-prefix}-menu-item:not(:last-child),
......
import { VNode } from 'vue'; import { VNode } from 'vue';
// define global types // define global types
export type RenderVNodeType = VNode | Element | JSX.Element; export type VNodeType = WithFalse<string | VNode | JSX.Element>;
export type MenuTheme = 'dark' | 'light'; export type MenuTheme = 'dark' | 'light';
...@@ -67,4 +67,6 @@ export interface MenuDataItem { ...@@ -67,4 +67,6 @@ export interface MenuDataItem {
export type WithFalse<T> = T | false; export type WithFalse<T> = T | false;
export type CustomRender = (...args: any[]) => VNode | VNode[];
export type FormatMessage = (message: string) => string; export type FormatMessage = (message: string) => string;
import { Slots, VNodeChild } from 'vue'; import { Slots, VNodeChild } from 'vue';
import { MenuDataItem } from '../typings'; import { CustomRender, MenuDataItem } from '../typings';
export { getComponent } from 'ant-design-vue/es/_util/props-util'; export { getComponent } from 'ant-design-vue/es/_util/props-util';
export { default as PropTypes } from 'ant-design-vue/es/_util/vue-types'; export { default as PropTypes } from 'ant-design-vue/es/_util/vue-types';
...@@ -12,6 +12,18 @@ export function getComponentOrSlot(props: any, slots: Slots, name: string): VNod ...@@ -12,6 +12,18 @@ export function getComponentOrSlot(props: any, slots: Slots, name: string): VNod
return typeof comp === 'function' ? comp() : (comp && (comp as VNodeChild)) || false; return typeof comp === 'function' ? comp() : (comp && (comp as VNodeChild)) || false;
} }
export function getCustomRender(props: any, slots: Slots, name: string): CustomRender | false {
const propRender = props[name];
if (propRender === false) {
return false;
}
if (propRender) {
return propRender;
}
const slotVNode = slots[name || 'default'];
return slotVNode;
}
export function warn(valid: boolean, message: string) { export function warn(valid: boolean, message: string) {
// Support uglify // Support uglify
if (process.env.NODE_ENV !== 'production' && !valid && console !== undefined) { if (process.env.NODE_ENV !== 'production' && !valid && console !== undefined) {
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BasicLayout 🥩 base use 1`] = `
<div class="ant-pro-basicLayout ant-pro-basicLayout-side">
<section class="ant-layout ant-pro-basicLayout">
<!---->
<aside class="ant-pro-sider ant-pro-sider-light ant-pro-sider-side ant-layout-sider ant-layout-sider-light" style="flex: 0 0 208px; max-width: 208px; min-width: 208px; width: 208px;">
<div class="ant-layout-sider-children">
<div class="ant-pro-sider-logo"><a><img src="https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg" alt="logo">
<h1>Pro Tests</h1>
</a></div>
<div style="flex: 1; overflow: hidden auto;">
<ul role="menu" class="ant-pro-sider-menu ant-menu-light ant-menu-root ant-menu ant-menu-inline" style="width: 100%;"></ul>
</div>
<div class="ant-pro-sider-links">
<ul role="menu" class="ant-pro-sider-link-menu ant-menu-light ant-menu-root ant-menu ant-menu-inline">
<!---->
<li role="menuitem" style="padding-left: 16px;" class="ant-pro-sider-collapsed-button ant-menu-item"><span role="img" aria-label="menu-fold" class="anticon anticon-menu-fold"><svg class="" data-icon="menu-fold" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896" focusable="false"><path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM115.4 518.9L271.7 642c5.8 4.6 14.4.5 14.4-6.9V388.9c0-7.4-8.5-11.5-14.4-6.9L115.4 505.1a8.74 8.74 0 000 13.8z"></path></svg></span>
<!---->
</li>
</ul>
</div>
</div>
<!---->
</aside>
<section class="ant-layout" style="position: relative; min-height: 0;">
<!---->
<header class="ant-layout-header" style="padding: 0px; height: 48px; line-height: 48px; width: 100%; z-index: 19;">
<div class="ant-pro-global-header ant-pro-global-header-layout-side">
<!---->
<!---->
<!---->
<div style="flex: 1;">
<!---->
</div>
<div class="custom-header-right-content"><span>custom-right-content</span></div>
</div>
</header>
<main class="ant-layout-content ant-pro-basicLayout-content ant-pro-basicLayout-has-header" style="min-height: 300px;">
<div>content</div>
</main>
<div>custom-footer</div>
</section>
</section>
</div>
`;
window.matchMedia = jest.fn().mockImplementation(query => {
return {
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
};
});
import { mount, shallowMount } from '@vue/test-utils'; import './_utils/mock-func';
import { mount } from '@vue/test-utils';
import BasicLayout from '../src/BasicLayout'; import BasicLayout from '../src/BasicLayout';
describe('BasicLayout', () => { const title = 'Pro Tests';
const logoSrc = 'https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg';
describe('BasicLayout', () => {
it('🥩 base use', () => { it('🥩 base use', () => {
// const wrapper = mount({ const wrapper = mount({
// render() { render() {
// return ( return (
// <BasicLayout /> <BasicLayout
// ) title={title}
// }, logo={logoSrc}
// }); layout="side"
// console.log(wrapper.html()); navTheme="light"
contentWidth="Fluid"
contentStyle={{ minHeight: '300px' }}
rightContentRender={() => (
<div class="custom-header-right-content">
<span>custom-right-content</span>
</div>
)}
footerRender={() => <div>custom-footer</div>}
>
<div>content</div>
</BasicLayout>
);
},
});
expect(wrapper.html()).toMatchSnapshot();
}); });
it('😄 custom title, logo', () => {
const wrapper = mount({
render() {
return (
<BasicLayout title={title} logo={logoSrc}>
<div>content</div>
</BasicLayout>
);
},
});
const renderTitle = wrapper.find('.ant-pro-sider-logo h1');
const renderLogo = wrapper.find('.ant-pro-sider-logo img');
expect(renderTitle.element.innerHTML).toEqual(title);
expect(renderLogo.attributes()).toHaveProperty('src', logoSrc);
});
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment