Commit 99e8fe37 authored by Sendya's avatar Sendya Committed by 言肆

refactor: component props

parent 0c8e30a9
...@@ -44,6 +44,7 @@ const BasicLayout = defineComponent({ ...@@ -44,6 +44,7 @@ const BasicLayout = defineComponent({
); );
const updateSelectedMenu = () => { const updateSelectedMenu = () => {
console.log('route', route.matched.concat());
const matched = route.matched.concat().map(item => item.path); const matched = route.matched.concat().map(item => item.path);
matched.shift(); matched.shift();
state.selectedKeys = matched; state.selectedKeys = matched;
...@@ -98,6 +99,9 @@ const BasicLayout = defineComponent({ ...@@ -98,6 +99,9 @@ const BasicLayout = defineComponent({
state.openKeys = $event; state.openKeys = $event;
}} }}
onSelect={updateSelectedMenu} onSelect={updateSelectedMenu}
onMenuHeaderClick={e => {
console.log('onMenuHeaderClick', e);
}}
rightContentRender={props => ( rightContentRender={props => (
<div <div
class={['right-content', `${props.layout}-${props.navTheme}`]} class={['right-content', `${props.layout}-${props.navTheme}`]}
...@@ -108,13 +112,13 @@ const BasicLayout = defineComponent({ ...@@ -108,13 +112,13 @@ const BasicLayout = defineComponent({
</span> </span>
</div> </div>
)} )}
menuHeaderRender={() => ( // menuHeaderRender={() => (
<a> // <a>
<img src="https://gw.alipayobjects.com/zos/antfincdn/PmY%24TNNDBI/logo.svg" /> // <img src="https://gw.alipayobjects.com/zos/antfincdn/PmY%24TNNDBI/logo.svg" />
{state.collapsed && state.layout !== 'mix' ? null : <h1>Pro Preview</h1>} // {state.collapsed && state.layout !== 'mix' ? null : <h1>Pro Preview</h1>}
</a> // </a>
)} // )}
menuItemRender={item => { /* menuItemRender={item => {
return ( return (
<Menu.Item inlineIndent={24} key={item.path}> <Menu.Item inlineIndent={24} key={item.path}>
<router-link to={{ ...item.meta, path: item.path }}> <router-link to={{ ...item.meta, path: item.path }}>
...@@ -130,7 +134,7 @@ const BasicLayout = defineComponent({ ...@@ -130,7 +134,7 @@ const BasicLayout = defineComponent({
{children} {children}
</Menu.SubMenu> </Menu.SubMenu>
); );
}} }} */
breadcrumbRender={({ route: r, routes, paths }) => breadcrumbRender={({ route: r, routes, paths }) =>
routes.indexOf(r) === routes.length - 1 ? ( routes.indexOf(r) === routes.length - 1 ? (
<span>{i18n(r.breadcrumbName)}</span> <span>{i18n(r.breadcrumbName)}</span>
......
This diff is collapsed.
import { computed, CSSProperties, FunctionalComponent } from 'vue'; import { computed, FunctionalComponent, PropType, ExtractPropTypes } from 'vue';
import { PureSettings } from '../defaultSettings'; import { defaultSettingProps } from '../defaultSettings';
import { CustomRender, MenuDataItem, WithFalse } from '../typings'; import { CustomRender, Theme, MenuDataItem, WithFalse } from '../typings';
import { import {
siderMenuProps,
SiderMenuProps, SiderMenuProps,
PrivateSiderMenuProps,
defaultRenderLogo, defaultRenderLogo,
defaultRenderLogoAndTitle, defaultRenderLogoAndTitle,
defaultRenderCollapsedButton, defaultRenderCollapsedButton,
} from '../SiderMenu/SiderMenu'; } from '../SiderMenu/SiderMenu';
import { TopNavHeader } from '../TopNavHeader'; import { TopNavHeader } from '../TopNavHeader';
import { clearMenuItem } from '../utils'; import { clearMenuItem, PropTypes } from '../utils';
import type { HeaderViewProps } from '../Header'; import type { HeaderViewProps } from '../Header';
import './index.less';
import { useRouteContext } from '../RouteContext'; import { useRouteContext } from '../RouteContext';
export interface GlobalHeaderProps extends Partial<PureSettings> {
collapsed?: boolean; import './index.less';
onCollapse?: (collapsed: boolean) => void;
isMobile?: boolean; export const globalHeaderProps = {
logo?: CustomRender; ...defaultSettingProps,
menuRender?: WithFalse<(props: HeaderViewProps, defaultDom: CustomRender) => CustomRender>;
rightContentRender?: WithFalse<(props: HeaderViewProps) => CustomRender>; prefixCls: PropTypes.string.def('ant-pro'),
className?: string; collapsed: PropTypes.looseBool,
prefixCls?: string; isMobile: PropTypes.looseBool,
menuData?: MenuDataItem[]; logo: siderMenuProps.logo,
onMenuHeaderClick?: (e: MouseEvent) => void; logoStyle: siderMenuProps.logoStyle,
style?: CSSProperties; headerTheme: {
menuHeaderRender?: SiderMenuProps['menuHeaderRender']; type: String as PropType<Theme>,
collapsedButtonRender?: SiderMenuProps['collapsedButtonRender']; default: 'dark',
splitMenus?: boolean; },
onOpenKeys?: (openKeys: WithFalse<string[]>) => void; menuData: {
onSelect?: (selectedKeys: WithFalse<string[]>) => void; type: Array as PropType<MenuDataItem[]>,
} default: () => [],
},
splitMenus: siderMenuProps.splitMenus,
menuRender: {
type: [Object, Function] as PropType<
WithFalse<(props: any /* HeaderViewProps */, defaultDom: CustomRender) => CustomRender>
>,
default: () => undefined,
},
menuHeaderRender: siderMenuProps.menuHeaderRender,
rightContentRender: {
type: [Object, Function] as PropType<WithFalse<(props: any) => CustomRender>>,
default: () => undefined,
},
collapsedButtonRender: siderMenuProps.collapsedButtonRender,
matchMenuKeys: siderMenuProps.matchMenuKeys,
// events
onMenuHeaderClick: PropTypes.func,
onCollapse: siderMenuProps.onCollapse,
onOpenKeys: siderMenuProps.onOpenKeys,
onSelect: siderMenuProps.onSelect,
};
export type GlobalHeaderProps = ExtractPropTypes<typeof globalHeaderProps>;
const renderLogo = ( const renderLogo = (
menuHeaderRender: SiderMenuProps['menuHeaderRender'], menuHeaderRender: SiderMenuProps['menuHeaderRender'],
...@@ -45,10 +68,7 @@ const renderLogo = ( ...@@ -45,10 +68,7 @@ const renderLogo = (
return logoDom; return logoDom;
}; };
export const GlobalHeader: FunctionalComponent<GlobalHeaderProps & PrivateSiderMenuProps> = ( export const GlobalHeader: FunctionalComponent<GlobalHeaderProps> = (props, { slots }) => {
props,
{ slots },
) => {
const { const {
isMobile, isMobile,
logo, logo,
...@@ -126,5 +146,6 @@ export const GlobalHeader: FunctionalComponent<GlobalHeaderProps & PrivateSiderM ...@@ -126,5 +146,6 @@ export const GlobalHeader: FunctionalComponent<GlobalHeaderProps & PrivateSiderM
</div> </div>
); );
}; };
GlobalHeader.emits = ['menuHeaderClick', 'collapse', 'openKeys', 'select'];
export default GlobalHeader; export default GlobalHeader;
import { defineComponent, computed, toRefs } from 'vue'; import { defineComponent, computed, toRefs, PropType, ExtractPropTypes } 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 { GlobalHeader, GlobalHeaderProps } from './GlobalHeader'; import { GlobalHeader, globalHeaderProps, GlobalHeaderProps } from './GlobalHeader';
import { TopNavHeader } from './TopNavHeader'; import { TopNavHeader } from './TopNavHeader';
import { useRouteContext } from './RouteContext'; import { useRouteContext } from './RouteContext';
import { CustomRender, WithFalse } from './typings'; import { CustomRender, WithFalse } from './typings';
import { clearMenuItem } from './utils'; import { clearMenuItem, PropTypes } from './utils';
import './Header.less'; import './Header.less';
const { Header } = Layout; const { Header } = Layout;
interface HeaderViewState { export const headerViewProps = {
visible: boolean; ...globalHeaderProps,
} headerRender: {
type: [Object, Function] as PropType<
export type HeaderViewProps = GlobalHeaderProps & { WithFalse<(props: any, defaultDom: CustomRender) => CustomRender>
isMobile?: boolean; >,
collapsed?: boolean; default: () => undefined,
logo?: CustomRender; },
headerTitleRender: {
headerRender?: WithFalse<(props: HeaderViewProps, defaultDom: CustomRender) => CustomRender>; type: [Object, Function] as PropType<
headerTitleRender?: WithFalse<(props: HeaderViewProps, defaultDom: CustomRender) => CustomRender>; WithFalse<(props: any, defaultDom: CustomRender) => CustomRender>
headerContentRender?: WithFalse<(props: HeaderViewProps) => CustomRender>; >,
siderWidth?: number; default: () => undefined,
hasSiderMenu?: boolean; },
headerContentRender: {
type: [Object, Function] as PropType<WithFalse<(props: any) => CustomRender>>,
default: () => undefined,
},
hasSiderMenu: PropTypes.looseBool,
siderWidth: PropTypes.number.def(208),
}; };
export const headerProps = [
'prefixCls', export type HeaderViewProps = Partial<ExtractPropTypes<typeof headerViewProps> & GlobalHeaderProps>;
'collapsed',
'onCollapse',
'openKeys',
'selectedKeys',
'isMobile',
'logo',
'title',
'menuRender',
'rightContentRender',
'menuData',
'menuHeaderRender',
'splitMenus',
'headerRender',
'headerTitleRender',
'headerContentRender',
'siderWidth',
'hasSiderMenu',
'fixedHeader',
'headerHeight',
'headerTheme',
'layout',
'navTheme',
'onSelect',
'onOpenChange',
'onOpenKeys',
];
export const HeaderView = defineComponent({ export const HeaderView = defineComponent({
inheritAttrs: false, inheritAttrs: false,
name: 'HeaderView', name: 'HeaderView',
props: headerProps, props: headerViewProps,
setup(props /*Required<HeaderViewProps> */) { setup(props) {
const { const {
prefixCls, prefixCls,
isMobile, isMobile,
......
...@@ -10,26 +10,9 @@ ...@@ -10,26 +10,9 @@
.@{pro-layout-page-container} { .@{pro-layout-page-container} {
&-warp { &-warp {
background-color: @component-background; background-color: @component-background;
.@{ant-prefix}-tabs-nav { .@{ant-prefix}-tabs-bar {
margin: 0; margin: 0;
} }
// fix antd@4 upgrade PageHeader style.
.@{ant-prefix}-page-header-heading {
.@{ant-prefix}-page-header-back {
margin: 12px 0;
margin-right: 16px;
}
.@{ant-prefix}-page-header-heading-title,
.@{ant-prefix}-page-header-heading-extra {
margin: 4px 0;
}
.@{ant-prefix}-page-header-heading-sub-title {
margin: 9px 12px 9px 0;
}
.@{ant-prefix}-page-header-heading-tags {
margin: 9px 0;
}
}
} }
&-ghost { &-ghost {
.@{pro-layout-page-container}-warp { .@{pro-layout-page-container}-warp {
......
This diff is collapsed.
import { InjectionKey, reactive } from 'vue'; import { InjectionKey, reactive } from 'vue';
import { createContext, useContext } from './hooks/context'; import { createContext, useContext } from './hooks/context';
import { MenuDataItem, CustomRender } from './typings'; import { MenuDataItem, FormatMessage, WithFalse, CustomRender } from './typings';
import { PureSettings } from './defaultSettings'; import { PureSettings } from './defaultSettings';
export interface Route { export interface Route {
path: string; path: string;
...@@ -35,7 +35,7 @@ export interface MenuState { ...@@ -35,7 +35,7 @@ export interface MenuState {
export interface RouteContextProps extends Partial<PureSettings>, MenuState { export interface RouteContextProps extends Partial<PureSettings>, MenuState {
getPrefixCls?: (suffixCls?: string, customizePrefixCls?: string) => string; getPrefixCls?: (suffixCls?: string, customizePrefixCls?: string) => string;
i18n?: (t: string) => string; locale?: WithFalse<FormatMessage>;
breadcrumb?: BreadcrumbListReturn; breadcrumb?: BreadcrumbListReturn;
menuData: MenuDataItem[]; menuData: MenuDataItem[];
...@@ -62,7 +62,7 @@ export const getPrefixCls = (suffixCls?: string, customizePrefixCls?: string) => ...@@ -62,7 +62,7 @@ export const getPrefixCls = (suffixCls?: string, customizePrefixCls?: string) =>
// set default context // set default context
const defaultRouteContext = reactive({ const defaultRouteContext = reactive({
getPrefixCls, getPrefixCls,
i18n: (t: string) => t, locale: (t: string) => t,
contentWidth: 'Fluid', contentWidth: 'Fluid',
hasFooterToolbar: false, hasFooterToolbar: false,
setHasFooterToolbar: (bool: boolean) => (defaultRouteContext.hasFooterToolbar = bool), setHasFooterToolbar: (bool: boolean) => (defaultRouteContext.hasFooterToolbar = bool),
......
...@@ -8,15 +8,22 @@ import { ...@@ -8,15 +8,22 @@ import {
PropType, PropType,
isVNode, isVNode,
toRefs, toRefs,
ExtractPropTypes,
} from 'vue'; } from 'vue';
import { createFromIconfontCN } from '@ant-design/icons-vue'; import { createFromIconfontCN } from '@ant-design/icons-vue';
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 defaultSettings, { PureSettings } from '../defaultSettings'; import { defaultSettingProps, defaultSettings } from '../defaultSettings';
import { isImg, isUrl } from '../utils'; import { isImg, isUrl } from '../utils';
import { MenuMode, SelectInfo, OpenEventHandler } from './typings'; import { MenuMode, SelectInfo, OpenEventHandler } from './typings';
import { MenuDataItem, MenuTheme, FormatMessage, CustomRender, WithFalse } from '../typings'; import {
import { PrivateSiderMenuProps } from './SiderMenu'; MenuDataItem,
MenuTheme,
FormatMessage,
CustomRender,
LayoutType,
WithFalse,
} from '../typings';
import './index.less'; import './index.less';
export { MenuMode, SelectInfo, OpenEventHandler }; export { MenuMode, SelectInfo, OpenEventHandler };
...@@ -25,53 +32,37 @@ export function useRootSubmenuKeys(menus: MenuDataItem[]): ComputedRef<string[]> ...@@ -25,53 +32,37 @@ export function useRootSubmenuKeys(menus: MenuDataItem[]): ComputedRef<string[]>
return computed(() => menus.map(it => it.path)); return computed(() => menus.map(it => it.path));
} }
// ts typo export interface CustomMenuRender {
interface CustomMenuRender {
menuItemRender?: WithFalse<(item: MenuDataItem) => CustomRender>; menuItemRender?: WithFalse<(item: MenuDataItem) => CustomRender>;
subMenuItemRender?: WithFalse<(item: MenuDataItem, children?: CustomRender[]) => CustomRender>; subMenuItemRender?: WithFalse<(item: MenuDataItem, children?: CustomRender[]) => CustomRender>;
} }
export interface BaseMenuProps
extends Partial<PureSettings>,
PrivateSiderMenuProps,
CustomMenuRender {
menuProps?: Record<string, any>;
prefixCls?: string;
collapsed?: boolean;
splitMenus?: boolean;
isMobile?: boolean;
menuData?: MenuDataItem[];
mode?: MenuMode;
onCollapse?: (collapsed: boolean) => void;
openKeys?: WithFalse<string[]> | undefined;
selectedKeys?: WithFalse<string[]> | undefined;
handleOpenChange?: (openKeys: string[]) => void;
theme?: MenuTheme | 'realDark';
i18n?: FormatMessage;
}
// vue props // vue props
export const baseMenuProps = { export const baseMenuProps = {
locale: Boolean, ...defaultSettingProps,
prefixCls: { prefixCls: {
type: String as PropType<string | undefined>, type: String as PropType<string | undefined>,
default: () => 'ant-pro', default: () => 'ant-pro',
}, },
i18n: { locale: {
type: Function as PropType<FormatMessage>, type: [Function, Object, Boolean] as PropType<WithFalse<FormatMessage>>,
default: (t: string): string => t, default: (t: string): string => t,
}, },
menuData: Array as PropType<MenuDataItem[]>, menuData: {
type: Array as PropType<MenuDataItem[]>,
default: () => [],
},
// top-nav-header: horizontal // top-nav-header: horizontal
mode: { mode: {
type: String as PropType<MenuMode>, type: String as PropType<MenuMode>,
default: 'inline', default: 'inline',
}, },
theme: { theme: {
type: String as PropType<BaseMenuProps['theme']>, type: String as PropType<MenuTheme | 'realDark'>,
default: 'dark', default: 'dark',
}, },
layout: { layout: {
type: String as PropType<BaseMenuProps['layout']>, type: String as PropType<LayoutType>,
default: 'side', default: 'side',
}, },
collapsed: { collapsed: {
...@@ -87,24 +78,30 @@ export const baseMenuProps = { ...@@ -87,24 +78,30 @@ export const baseMenuProps = {
default: undefined, default: undefined,
}, },
menuProps: { menuProps: {
type: Object as PropType<BaseMenuProps['menuProps']>, type: Object as PropType<Record<string, any>>,
default: () => null, default: () => null,
}, },
menuItemRender: { menuItemRender: {
type: [Function, Boolean] as PropType<BaseMenuProps['menuItemRender']>, type: [Function, Boolean] as PropType<CustomMenuRender['menuItemRender']>,
default: () => false, default: () => false,
}, },
subMenuItemRender: { subMenuItemRender: {
type: [Function, Boolean] as PropType<BaseMenuProps['subMenuItemRender']>, type: [Function, Boolean] as PropType<CustomMenuRender['subMenuItemRender']>,
default: () => false, default: () => false,
}, },
}; };
export type BaseMenuProps = ExtractPropTypes<typeof baseMenuProps>;
const IconFont = createFromIconfontCN({ const IconFont = createFromIconfontCN({
scriptUrl: defaultSettings.iconfontUrl, scriptUrl: defaultSettings.iconfontUrl,
}); });
const LazyIcon = (props: { icon: VNode | string; iconPrefixes?: string; prefixCls?: string }) => { const LazyIcon = (props: {
icon: VNodeChild | string;
iconPrefixes?: string;
prefixCls?: string;
}) => {
const { icon, iconPrefixes = 'icon-', prefixCls = 'ant-pro' } = props; const { icon, iconPrefixes = 'icon-', prefixCls = 'ant-pro' } = props;
if (!icon) { if (!icon) {
return null; return null;
...@@ -120,7 +117,7 @@ const LazyIcon = (props: { icon: VNode | string; iconPrefixes?: string; prefixCl ...@@ -120,7 +117,7 @@ const LazyIcon = (props: { icon: VNode | string; iconPrefixes?: string; prefixCl
if (isVNode(icon)) { if (isVNode(icon)) {
return icon; return icon;
} }
const DynamicIcon = resolveComponent(icon) as any; const DynamicIcon = resolveComponent(icon as string) as any;
return (typeof LazyIcon === 'function' && <DynamicIcon />) || null; return (typeof LazyIcon === 'function' && <DynamicIcon />) || null;
}; };
...@@ -156,8 +153,8 @@ class MenuUtil { ...@@ -156,8 +153,8 @@ class MenuUtil {
this.getNavMenuItems(item.children, true), this.getNavMenuItems(item.children, true),
) as VNode; ) as VNode;
} }
const { prefixCls, i18n } = this.props; const { prefixCls, locale } = this.props;
const menuTitle = (i18n && i18n(item.meta?.title)) || item.meta?.title; const menuTitle = (locale && locale(item.meta?.title)) || item.meta?.title;
const defaultTitle = item.meta?.icon ? ( const defaultTitle = item.meta?.icon ? (
<span class={`${prefixCls}-menu-item`}> <span class={`${prefixCls}-menu-item`}>
<span class={`${prefixCls}-menu-item-title`}>{menuTitle}</span> <span class={`${prefixCls}-menu-item-title`}>{menuTitle}</span>
...@@ -166,12 +163,14 @@ class MenuUtil { ...@@ -166,12 +163,14 @@ class MenuUtil {
<span class={`${prefixCls}-menu-item`}>{menuTitle}</span> <span class={`${prefixCls}-menu-item`}>{menuTitle}</span>
); );
const MenuComponent = item.meta?.type === 'group' ? Menu.ItemGroup : Menu.SubMenu; const hasGroup = item.meta?.type === 'group';
const MenuComponent = hasGroup ? Menu.ItemGroup : Menu.SubMenu;
return ( return (
<MenuComponent <MenuComponent
title={defaultTitle} title={defaultTitle}
key={item.path} key={item.path}
icon={item.meta?.type === 'group' ? null : <LazyIcon icon={item.meta?.icon} />} icon={hasGroup ? null : <LazyIcon icon={item.meta?.icon} />}
> >
{this.getNavMenuItems(item.children, true)} {this.getNavMenuItems(item.children, true)}
</MenuComponent> </MenuComponent>
...@@ -201,8 +200,8 @@ class MenuUtil { ...@@ -201,8 +200,8 @@ class MenuUtil {
const props = { to: { name: item.name } }; const props = { to: { name: item.name } };
const attrs = hasUrl || target ? { ...item.meta, href: item.path, target: target } : {}; const attrs = hasUrl || target ? { ...item.meta, href: item.path, target: target } : {};
const { prefixCls, i18n } = this.props; const { prefixCls, locale } = this.props;
const menuTitle = (i18n && i18n(item.meta?.title)) || item.meta?.title; const menuTitle = (locale && locale(item.meta?.title)) || item.meta?.title;
const defaultTitle = item.meta?.icon ? ( const defaultTitle = item.meta?.icon ? (
<CustomTag {...attrs} {...props} class={`${prefixCls}-menu-item`}> <CustomTag {...attrs} {...props} class={`${prefixCls}-menu-item`}>
<span class={`${prefixCls}-menu-item-title`}>{menuTitle}</span> <span class={`${prefixCls}-menu-item-title`}>{menuTitle}</span>
...@@ -234,7 +233,6 @@ export default defineComponent({ ...@@ -234,7 +233,6 @@ export default defineComponent({
const menuUtil = new MenuUtil(props); const menuUtil = new MenuUtil(props);
const handleOpenChange = (openKeys: string[]): void => { const handleOpenChange = (openKeys: string[]): void => {
console.log('openChange', '....');
emit('update:openKeys', openKeys); emit('update:openKeys', openKeys);
}; };
const handleSelect = (params: { const handleSelect = (params: {
...@@ -257,6 +255,7 @@ export default defineComponent({ ...@@ -257,6 +255,7 @@ export default defineComponent({
theme={props.theme as 'dark' | 'light'} theme={props.theme as 'dark' | 'light'}
openKeys={props.openKeys === false ? [] : props.openKeys} openKeys={props.openKeys === false ? [] : props.openKeys}
selectedKeys={props.selectedKeys || []} selectedKeys={props.selectedKeys || []}
// @ts-ignore
onOpenChange={handleOpenChange} onOpenChange={handleOpenChange}
onSelect={handleSelect} onSelect={handleSelect}
{...props.menuProps} {...props.menuProps}
......
import { FunctionalComponent, computed } from 'vue'; import {
FunctionalComponent as FC,
computed,
ExtractPropTypes,
PropType,
CSSProperties,
} 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 '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 { FormatMessage, WithFalse, CustomRender } from '../typings'; import { FormatMessage, WithFalse, CustomRender } from '../typings';
import { SiderProps } from './typings'; import { SiderProps } from './typings';
import { defaultSettingProps } from '../defaultSettings';
import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue'; import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue';
import { useRouteContext } from '../RouteContext'; import { useRouteContext } from '../RouteContext';
import { getMenuFirstChildren } from '../utils'; import { PropTypes, getMenuFirstChildren } from '../utils';
import './index.less'; import './index.less';
const { Sider } = Layout; const { Sider } = Layout;
...@@ -17,28 +24,73 @@ export type PrivateSiderMenuProps = { ...@@ -17,28 +24,73 @@ export type PrivateSiderMenuProps = {
matchMenuKeys?: string[]; matchMenuKeys?: string[];
}; };
export interface SiderMenuProps export const siderMenuProps = {
extends Pick<BaseMenuProps, Exclude<keyof BaseMenuProps, ['onCollapse']>> { ...defaultSettingProps,
logo?: CustomRender; ...baseMenuProps,
siderWidth?: number; logo: {
collapsedWidth?: number; type: [Object, String, Function] as PropType<CustomRender>,
menuHeaderRender?: WithFalse< default: () => undefined,
(logo: CustomRender, title: CustomRender, props?: SiderMenuProps) => CustomRender },
>; logoStyle: {
menuFooterRender?: WithFalse<(props?: SiderMenuProps) => CustomRender>; type: Object as PropType<CSSProperties>,
menuContentRender?: WithFalse<(props: SiderMenuProps, defaultDom: CustomRender) => CustomRender>; default: () => undefined,
menuExtraRender?: WithFalse<(props: SiderMenuProps) => CustomRender>; },
collapsedButtonRender?: WithFalse<(collapsed?: boolean) => CustomRender>; siderWidth: PropTypes.number.def(208),
breakpoint?: SiderProps['breakpoint'] | false; headerHeight: PropTypes.number.def(48),
onMenuHeaderClick?: (e: MouseEvent) => void; collapsedWidth: PropTypes.number.def(48),
fixed?: boolean; menuHeaderRender: {
hide?: boolean; type: [Function, Object] as PropType<
onCollapse?: (collapsed: boolean) => void; WithFalse<(logo: CustomRender, title: CustomRender, props?: any) => CustomRender>
onOpenKeys?: (openKeys: WithFalse<string[]>) => void; >,
onSelect?: (selectedKeys: WithFalse<string[]>) => void; default: () => undefined,
} },
menuFooterRender: {
type: [Function, Object] as PropType<WithFalse<(props?: any) => CustomRender>>,
default: () => undefined,
},
menuContentRender: {
type: [Function, Object] as PropType<
WithFalse<(props: any, defaultDom: CustomRender) => CustomRender>
>,
default: () => undefined,
},
menuExtraRender: {
type: [Function, Object] as PropType<WithFalse<(props?: any) => CustomRender>>,
default: () => undefined,
},
collapsedButtonRender: {
type: [Function, Object] as PropType<WithFalse<(collapsed?: boolean) => CustomRender>>,
default: () => undefined,
},
breakpoint: {
type: [Object, Boolean] as PropType<SiderProps['breakpoint'] | false>,
default: () => false,
},
isMobile: PropTypes.looseBool,
splitMenus: PropTypes.looseBool,
fixed: PropTypes.looseBool,
hide: PropTypes.looseBool,
matchMenuKeys: {
type: Array as PropType<string[]>,
default: () => [],
},
export const defaultRenderLogo = (logo?: CustomRender): CustomRender => { // events
onMenuHeaderClick: PropTypes.func,
onCollapse: {
type: Function as PropType<(collapsed: boolean) => void>,
},
onOpenKeys: {
type: Function as PropType<(openKeys: WithFalse<string[]>) => void>,
},
onSelect: {
type: Function as PropType<(selectedKeys: WithFalse<string[]>) => void>,
},
};
export type SiderMenuProps = Partial<ExtractPropTypes<typeof siderMenuProps>>;
export const defaultRenderLogo = (logo?: CustomRender, logoStyle?: CSSProperties): CustomRender => {
if (!logo) { if (!logo) {
return null; return null;
} }
...@@ -57,6 +109,7 @@ export const defaultRenderLogoAndTitle = ( ...@@ -57,6 +109,7 @@ export const defaultRenderLogoAndTitle = (
): CustomRender | null => { ): CustomRender | null => {
const { const {
logo = 'https://gw.alipayobjects.com/zos/antfincdn/PmY%24TNNDBI/logo.svg', logo = 'https://gw.alipayobjects.com/zos/antfincdn/PmY%24TNNDBI/logo.svg',
logoStyle,
title, title,
layout, layout,
} = props; } = props;
...@@ -64,7 +117,7 @@ export const defaultRenderLogoAndTitle = ( ...@@ -64,7 +117,7 @@ export const defaultRenderLogoAndTitle = (
if (renderFunction === false) { if (renderFunction === false) {
return null; return null;
} }
const logoDom = defaultRenderLogo(logo); const logoDom = defaultRenderLogo(logo, logoStyle);
const titleDom = <h1>{title}</h1>; const titleDom = <h1>{title}</h1>;
// call menuHeaderRender // call menuHeaderRender
if (renderFunction) { if (renderFunction) {
...@@ -85,7 +138,7 @@ export const defaultRenderLogoAndTitle = ( ...@@ -85,7 +138,7 @@ export const defaultRenderLogoAndTitle = (
export const defaultRenderCollapsedButton = (collapsed?: boolean): CustomRender => export const defaultRenderCollapsedButton = (collapsed?: boolean): CustomRender =>
collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />; collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />;
const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) => { const SiderMenu: FC<SiderMenuProps> = (props: SiderMenuProps) => {
const { const {
collapsed, collapsed,
siderWidth, siderWidth,
...@@ -99,20 +152,19 @@ const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) = ...@@ -99,20 +152,19 @@ const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) =
onCollapse, onCollapse,
onOpenKeys, onOpenKeys,
onSelect, onSelect,
onMenuHeaderClick,
} = props; } = props;
const context = useRouteContext(); const context = useRouteContext();
const { getPrefixCls, i18n } = context; const { getPrefixCls, locale } = context;
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 sTheme = computed(() => (props.layout === 'mix' && 'light') || props.navTheme);
const runtimeSideWidth = computed(() => const sSideWidth = computed(() => (props.collapsed ? props.collapsedWidth : props.siderWidth));
props.collapsed ? props.collapsedWidth : props.siderWidth,
);
const classNames = computed(() => { const classNames = computed(() => {
return { return {
[baseClassName]: true, [baseClassName]: true,
[`${baseClassName}-${runtimeTheme.value}`]: true, [`${baseClassName}-${sTheme.value}`]: true,
[`${baseClassName}-${props.layout}`]: true, [`${baseClassName}-${props.layout}`]: true,
[`${baseClassName}-fixed`]: context.fixSiderbar, [`${baseClassName}-fixed`]: context.fixSiderbar,
}; };
...@@ -130,8 +182,8 @@ const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) = ...@@ -130,8 +182,8 @@ const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) =
const defaultMenuDom = ( const defaultMenuDom = (
<BaseMenu <BaseMenu
prefixCls={getPrefixCls()} prefixCls={getPrefixCls()}
i18n={i18n as FormatMessage} locale={props.locale}
theme={runtimeTheme.value === 'realDark' ? 'dark' : runtimeTheme.value} theme={sTheme.value === 'realDark' ? 'dark' : sTheme.value}
mode="inline" mode="inline"
menuData={hasSide.value ? flatMenuData.value : context.menuData} menuData={hasSide.value ? flatMenuData.value : context.menuData}
collapsed={props.collapsed} collapsed={props.collapsed}
...@@ -155,24 +207,44 @@ const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) = ...@@ -155,24 +207,44 @@ const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) =
{context.fixSiderbar && ( {context.fixSiderbar && (
<div <div
style={{ style={{
width: `${runtimeSideWidth.value}px`, width: `${sSideWidth.value}px`,
overflow: 'hidden', overflow: 'hidden',
flex: `0 0 ${runtimeSideWidth.value}px`, flex: `0 0 ${sSideWidth.value}px`,
maxWidth: `${runtimeSideWidth.value}px`, maxWidth: `${sSideWidth.value}px`,
minWidth: `${runtimeSideWidth.value}px`, minWidth: `${sSideWidth.value}px`,
transition: `background-color 0.3s, min-width 0.3s, max-width 0.3s cubic-bezier(0.645, 0.045, 0.355, 1)`,
}} }}
/> />
)} )}
<Sider <Sider
class={classNames.value} collapsible
theme={runtimeTheme.value === 'realDark' ? 'dark' : runtimeTheme.value} trigger={null}
width={siderWidth}
breakpoint={breakpoint || undefined}
collapsed={collapsed} collapsed={collapsed}
collapsible={false} breakpoint={breakpoint || undefined}
onCollapse={collapse => {
if (props.isMobile) return;
onCollapse?.(collapse);
}}
collapsedWidth={collapsedWidth} collapsedWidth={collapsedWidth}
style={{
overflow: 'hidden',
paddingTop:
props.layout === 'mix' && !props.isMobile ? `${props.headerHeight}px` : undefined,
}}
width={siderWidth}
theme={sTheme.value === 'realDark' ? 'dark' : sTheme.value}
class={classNames.value}
> >
<div class={`${baseClassName}-logo`}>{headerDom}</div> {headerDom && (
<div
class={`${baseClassName}-logo`}
onClick={props.layout !== 'mix' ? onMenuHeaderClick : undefined}
id="logo"
style={props?.logoStyle}
>
{headerDom}
</div>
)}
{extraDom && ( {extraDom && (
<div class={`${baseClassName}-extra ${!headerDom && `${baseClassName}-extra-no-logo`}`}> <div class={`${baseClassName}-extra ${!headerDom && `${baseClassName}-extra-no-logo`}`}>
{extraDom} {extraDom}
...@@ -185,7 +257,7 @@ const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) = ...@@ -185,7 +257,7 @@ const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) =
<Menu <Menu
class={`${baseClassName}-link-menu`} class={`${baseClassName}-link-menu`}
inlineIndent={16} inlineIndent={16}
theme={runtimeTheme.value as 'light' | 'dark'} theme={sTheme.value as 'light' | 'dark'}
selectedKeys={[]} selectedKeys={[]}
openKeys={[]} openKeys={[]}
mode="inline" mode="inline"
......
import { FunctionalComponent } from 'vue'; import { FunctionalComponent as FC } from 'vue';
import 'ant-design-vue/es/drawer/style'; import 'ant-design-vue/es/drawer/style';
import Drawer from 'ant-design-vue/es/drawer'; import Drawer from 'ant-design-vue/es/drawer';
import SiderMenu, { SiderMenuProps, PrivateSiderMenuProps } from './SiderMenu'; import SiderMenu, { siderMenuProps, SiderMenuProps, PrivateSiderMenuProps } from './SiderMenu';
export type SiderMenuWrapperProps = SiderMenuProps & Partial<PrivateSiderMenuProps>; export type SiderMenuWrapperProps = Partial<SiderMenuProps> & Partial<PrivateSiderMenuProps>;
const SiderMenuWrapper: FunctionalComponent<SiderMenuWrapperProps> = (props, { attrs }) => { const SiderMenuWrapper: FC<SiderMenuWrapperProps> = (props, { attrs }) => {
return props.isMobile ? ( return props.isMobile ? (
<Drawer <Drawer
visible={!props.collapsed} visible={!props.collapsed}
...@@ -33,7 +33,15 @@ const SiderMenuWrapper: FunctionalComponent<SiderMenuWrapperProps> = (props, { a ...@@ -33,7 +33,15 @@ const SiderMenuWrapper: FunctionalComponent<SiderMenuWrapperProps> = (props, { a
); );
}; };
SiderMenuWrapper.inheritAttrs = true; SiderMenuWrapper.inheritAttrs = false;
SiderMenuWrapper.displayName = 'SiderMenuWrapper'; SiderMenuWrapper.displayName = 'SiderMenuWrapper';
export {
SiderMenu,
SiderMenuProps,
PrivateSiderMenuProps,
// vue props
siderMenuProps,
};
export default SiderMenuWrapper; export default SiderMenuWrapper;
import { ref, computed, FunctionalComponent } from 'vue'; import { ref, computed, FunctionalComponent, ExtractPropTypes } from 'vue';
import { import {
siderMenuProps,
SiderMenuProps, SiderMenuProps,
defaultRenderLogoAndTitle, defaultRenderLogoAndTitle,
PrivateSiderMenuProps, PrivateSiderMenuProps,
} from '../SiderMenu/SiderMenu'; } from '../SiderMenu/SiderMenu';
import BaseMenu from '../SiderMenu/BaseMenu'; import BaseMenu from '../SiderMenu/BaseMenu';
import { GlobalHeaderProps } from '../GlobalHeader'; import { globalHeaderProps } from '../GlobalHeader';
import { default as ResizeObserver } from 'ant-design-vue/es/vc-resize-observer'; import { default as ResizeObserver } from 'ant-design-vue/es/vc-resize-observer';
import { FormatMessage } from '../typings'; import { FormatMessage } from '../typings';
import './index.less';
import { useRouteContext } from '../RouteContext'; import { useRouteContext } from '../RouteContext';
import './index.less';
export type TopNavHeaderProps = SiderMenuProps & GlobalHeaderProps & PrivateSiderMenuProps & {}; export const topNavHeaderProps = {
...siderMenuProps,
...globalHeaderProps,
};
export type TopNavHeaderProps = Partial<ExtractPropTypes<typeof topNavHeaderProps>> &
Partial<SiderMenuProps>;
const RightContent: FunctionalComponent<TopNavHeaderProps> = ({ rightContentRender, ...props }) => { const RightContent: FunctionalComponent<TopNavHeaderProps> = ({ rightContentRender, ...props }) => {
const rightSize = ref<number | string>('auto'); const rightSize = ref<number | string>('auto');
...@@ -63,7 +69,7 @@ export const TopNavHeader: FunctionalComponent<TopNavHeaderProps> = props => { ...@@ -63,7 +69,7 @@ export const TopNavHeader: FunctionalComponent<TopNavHeaderProps> = props => {
const context = useRouteContext(); const context = useRouteContext();
const prefixCls = `${propPrefixCls || 'ant-pro'}-top-nav-header`; const prefixCls = `${propPrefixCls || 'ant-pro'}-top-nav-header`;
const headerDom = defaultRenderLogoAndTitle( const headerDom = defaultRenderLogoAndTitle(
{ ...props, collapsed: false }, { ...props, collapsed: false } as SiderMenuProps,
// REMARK:: Any time render header title // REMARK:: Any time render header title
// layout === 'mix' ? 'headerTitleRender' : undefined, // layout === 'mix' ? 'headerTitleRender' : undefined,
layout !== 'side' ? 'headerTitleRender' : undefined, layout !== 'side' ? 'headerTitleRender' : undefined,
...@@ -87,7 +93,7 @@ export const TopNavHeader: FunctionalComponent<TopNavHeaderProps> = props => { ...@@ -87,7 +93,7 @@ export const TopNavHeader: FunctionalComponent<TopNavHeaderProps> = props => {
<div style={{ flex: 1 }} class={`${prefixCls}-menu`}> <div style={{ flex: 1 }} class={`${prefixCls}-menu`}>
<BaseMenu <BaseMenu
prefixCls={propPrefixCls} prefixCls={propPrefixCls}
i18n={context.i18n as FormatMessage} locale={context.locale as FormatMessage}
theme={props.theme === 'realDark' ? 'dark' : props.theme} theme={props.theme === 'realDark' ? 'dark' : props.theme}
mode={props.mode} mode={props.mode}
collapsed={props.collapsed} collapsed={props.collapsed}
...@@ -106,3 +112,5 @@ export const TopNavHeader: FunctionalComponent<TopNavHeaderProps> = props => { ...@@ -106,3 +112,5 @@ export const TopNavHeader: FunctionalComponent<TopNavHeaderProps> = props => {
</div> </div>
); );
}; };
TopNavHeader.inheritAttrs = false;
import { MenuTheme, ContentWidth } from './typings'; import type { PropType, ExtractPropTypes } from 'vue';
import type { MenuTheme, ContentWidth } from './typings';
export interface RenderSetting { export interface RenderSetting {
headerRender?: false; headerRender?: false;
...@@ -6,7 +7,6 @@ export interface RenderSetting { ...@@ -6,7 +7,6 @@ export interface RenderSetting {
menuRender?: false; menuRender?: false;
menuHeaderRender?: false; menuHeaderRender?: false;
} }
export interface RenderSetting { export interface RenderSetting {
headerRender?: false; headerRender?: false;
footerRender?: false; footerRender?: false;
...@@ -56,7 +56,7 @@ export interface PureSettings { ...@@ -56,7 +56,7 @@ export interface PureSettings {
export type ProSettings = PureSettings & RenderSetting; export type ProSettings = PureSettings & RenderSetting;
const defaultSettings: ProSettings = { export const defaultSettings = {
navTheme: 'dark', navTheme: 'dark',
layout: 'side', layout: 'side',
contentWidth: 'Fluid', contentWidth: 'Fluid',
...@@ -71,4 +71,51 @@ const defaultSettings: ProSettings = { ...@@ -71,4 +71,51 @@ const defaultSettings: ProSettings = {
primaryColor: '#1890ff', primaryColor: '#1890ff',
}; };
export default defaultSettings; export const defaultSettingProps = {
navTheme: {
type: String as PropType<PureSettings['navTheme']>,
default: defaultSettings.navTheme,
},
layout: {
type: String as PropType<PureSettings['layout']>,
default: defaultSettings.layout,
},
contentWidth: {
type: String as PropType<PureSettings['contentWidth']>,
default: defaultSettings.contentWidth,
},
fixedHeader: {
type: Boolean as PropType<PureSettings['fixedHeader']>,
default: defaultSettings.fixedHeader,
},
fixSiderbar: {
type: Boolean as PropType<PureSettings['fixSiderbar']>,
default: defaultSettings.fixSiderbar,
},
menu: {
type: Object as PropType<PureSettings['menu']>,
default: () => {
return {
locale: true,
};
},
},
headerHeight: {
type: Number as PropType<PureSettings['headerHeight']>,
default: defaultSettings.headerHeight,
},
title: {
type: String as PropType<PureSettings['title']>,
default: () => defaultSettings.title,
},
iconfontUrl: {
type: String as PropType<PureSettings['iconfontUrl']>,
default: () => defaultSettings.iconfontUrl,
},
primaryColor: {
type: String as PropType<PureSettings['primaryColor']>,
default: () => defaultSettings.primaryColor,
},
};
export type ProSettingsProps = ExtractPropTypes<typeof defaultSettingProps>;
...@@ -24,4 +24,4 @@ export { ...@@ -24,4 +24,4 @@ export {
} from './SiderMenu/BaseMenu'; } from './SiderMenu/BaseMenu';
export { default as WaterMark } from './WaterMark/index'; export { default as WaterMark } from './WaterMark/index';
export { default } from './BasicLayout'; export { default, BasicLayoutProps } from './BasicLayout';
import type { Slot, VNode } from 'vue'; import type { Slot, VNode, VNodeChild } from 'vue';
export type MenuTheme = 'dark' | 'light'; export type Theme = 'dark' | 'light';
export type MenuTheme = Theme;
export type LayoutType = 'side' | 'top' | 'mix'; export type LayoutType = 'side' | 'top' | 'mix';
...@@ -66,6 +68,7 @@ export type WithFalse<T> = T | false; ...@@ -66,6 +68,7 @@ export type WithFalse<T> = T | false;
export type CustomRender = export type CustomRender =
| Slot | Slot
| VNodeChild
| VNode | VNode
| ((...props: any[]) => Slot) | ((...props: any[]) => Slot)
| ((...props: any[]) => VNode) | ((...props: any[]) => VNode)
......
import 'vue';
type EventHandler = (...args: any[]) => void;
declare module 'vue' {
interface ComponentCustomProps {
role?: string;
tabindex?: number | string;
// should be removed after Vue supported component events typing
// see: https://github.com/vuejs/vue-next/issues/1553
// https://github.com/vuejs/vue-next/issues/3029
onBlur?: EventHandler;
onOpen?: EventHandler;
onEdit?: EventHandler;
onLoad?: EventHandler;
onClose?: EventHandler;
onFocus?: EventHandler;
onInput?: EventHandler;
onClick?: EventHandler;
onPress?: EventHandler;
onCancel?: EventHandler;
onChange?: EventHandler;
onDelete?: EventHandler;
onScroll?: EventHandler;
onSubmit?: EventHandler;
onSelect?: EventHandler;
onConfirm?: EventHandler;
onPreview?: EventHandler;
onKeypress?: EventHandler;
onTouchend?: EventHandler;
onTouchmove?: EventHandler;
onTouchstart?: EventHandler;
onTouchcancel?: EventHandler;
onMouseenter?: EventHandler;
onMouseleave?: EventHandler;
onMousemove?: EventHandler;
onKeydown?: EventHandler;
onKeyup?: EventHandler;
onDeselect?: EventHandler;
}
}
...@@ -4,22 +4,29 @@ exports[`BasicLayout 🥩 base use 1`] = ` ...@@ -4,22 +4,29 @@ exports[`BasicLayout 🥩 base use 1`] = `
<div class="ant-pro-basicLayout ant-pro-basicLayout-side"> <div class="ant-pro-basicLayout ant-pro-basicLayout-side">
<section class="ant-layout ant-pro-basicLayout"> <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;"> <aside style="overflow: hidden; flex: 0 0 208px; max-width: 208px; min-width: 208px; width: 208px;" class="ant-layout-sider ant-layout-sider-light ant-pro-sider ant-pro-sider-light ant-pro-sider-side">
<div class="ant-layout-sider-children"> <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"> <div class="ant-pro-sider-logo" id="logo"><a><img src="https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg" alt="logo">
<h1>Pro Tests</h1> <h1>Pro Tests</h1>
</a></div> </a></div>
<!---->
<div style="flex: 1; overflow: hidden auto;"> <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> <ul class="ant-menu ant-menu-root ant-menu-inline ant-menu-light ant-pro-sider-menu" style="width: 100%;" data-hack-store-update="[object Object]" role="menu" data-menu-list="true">
<!---->
<!---->
</ul>
</div> </div>
<div class="ant-pro-sider-links"> <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"> <ul class="ant-menu ant-menu-root ant-menu-inline ant-menu-light ant-pro-sider-link-menu" data-hack-store-update="[object Object]" role="menu" data-menu-list="true">
<!----> <!---->
<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 class="ant-menu-item ant-pro-sider-collapsed-button ant-menu-item-only-child" style="padding-left: 16px;" role="menuitem" tabindex="-1" data-menu-id="collapsed-button" aria-disabled="false">
<!----> <!----><span class="ant-menu-title-content"><span role="img" aria-label="menu-fold" class="anticon anticon-menu-fold"><svg focusable="false" class="" data-icon="menu-fold" width="1em" height="1em" fill="currentColor" aria-hidden="true" viewBox="64 64 896 896"><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></span>
</li> </li>
<!---->
<!---->
</ul> </ul>
</div> </div>
<!---->
</div> </div>
<!----> <!---->
</aside> </aside>
...@@ -33,10 +40,10 @@ exports[`BasicLayout 🥩 base use 1`] = ` ...@@ -33,10 +40,10 @@ exports[`BasicLayout 🥩 base use 1`] = `
<div style="flex: 1;"> <div style="flex: 1;">
<!----> <!---->
</div> </div>
<div class="custom-header-right-content"><span>custom-right-content</span></div> <div><span>custom-right-content</span></div>
</div> </div>
</header> </header>
<main class="ant-layout-content ant-pro-basicLayout-content ant-pro-basicLayout-has-header" ischildrenlayout="false" style="min-height: 300px;"> <main class="ant-layout-content ant-pro-basicLayout-content ant-pro-basicLayout-has-header" style="min-height: 300px;">
<div>content</div> <div>content</div>
</main> </main>
<div>custom-footer</div> <div>custom-footer</div>
......
import './_utils/mock-func'; import './_utils/mock-func';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { PropType } from 'vue'; import { PropType } from 'vue';
import BasicLayout, { BasicLayoutProps } from '../src/BasicLayout'; import BasicLayout, { BasicLayoutProps } from '../src';
const title = 'Pro Tests'; const title = 'Pro Tests';
const logoSrc = 'https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg'; const logoSrc = 'https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg';
...@@ -18,11 +18,13 @@ describe('BasicLayout', () => { ...@@ -18,11 +18,13 @@ describe('BasicLayout', () => {
navTheme="light" navTheme="light"
contentWidth="Fluid" contentWidth="Fluid"
contentStyle={{ minHeight: '300px' }} contentStyle={{ minHeight: '300px' }}
rightContentRender={() => ( rightContentRender={() => {
<div class="custom-header-right-content"> return (
<span>custom-right-content</span> <div>
</div> <span>custom-right-content</span>
)} </div>
);
}}
footerRender={() => <div>custom-footer</div>} footerRender={() => <div>custom-footer</div>}
> >
<div>content</div> <div>content</div>
...@@ -37,6 +39,7 @@ describe('BasicLayout', () => { ...@@ -37,6 +39,7 @@ describe('BasicLayout', () => {
const wrapper = mount({ const wrapper = mount({
render() { render() {
return ( return (
// @ts-ignore
<BasicLayout title={title} logo={logoSrc}> <BasicLayout title={title} logo={logoSrc}>
<div>content</div> <div>content</div>
</BasicLayout> </BasicLayout>
...@@ -64,7 +67,10 @@ describe('BasicLayout', () => { ...@@ -64,7 +67,10 @@ describe('BasicLayout', () => {
}, },
render() { render() {
return ( return (
<BasicLayout navTheme={this.theme} layout={this.layout}> <BasicLayout
navTheme={this.theme as BasicLayoutProps['navTheme']}
layout={this.layout as BasicLayoutProps['layout']}
>
<div>content</div> <div>content</div>
</BasicLayout> </BasicLayout>
); );
......
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import PageContainer from '../src/PageContainer'; import { PageContainer } from '../src';
import { Tag, Button } from 'ant-design-vue'; import { Tag, Button } from 'ant-design-vue';
import { sleep } from './utils'; import { sleep } from './utils';
...@@ -40,15 +40,30 @@ describe('PageContainer', () => { ...@@ -40,15 +40,30 @@ describe('PageContainer', () => {
}; };
it('🥩 base use', () => { it('🥩 base use', () => {
const wrapper = mount({ const wrapper = mount(
render() { {
return ( render() {
<PageContainer {...props}> return (
<div>PageContent</div> <PageContainer {...props}>
</PageContainer> <div>PageContent</div>
); </PageContainer>
);
},
}, },
}); {
global: {
mocks: {
window: {
ResizeObserver: class {
observe() {}
unobserve() {}
disconnect() {}
},
},
},
},
},
);
expect(wrapper.html()).toMatchSnapshot(); expect(wrapper.html()).toMatchSnapshot();
}); });
...@@ -64,6 +79,12 @@ describe('PageContainer', () => { ...@@ -64,6 +79,12 @@ describe('PageContainer', () => {
}); });
it('😄 render content,extraContent', async () => { it('😄 render content,extraContent', async () => {
mount(PageContainer, {
props: {
content: <div class="my-test-content">MyTestContent</div>,
extraContent: <span>extra right content</span>,
},
});
const wrapper = mount({ const wrapper = mount({
render() { render() {
return ( return (
......
...@@ -2417,7 +2417,7 @@ ...@@ -2417,7 +2417,7 @@
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.1.1.tgz#2287cfc3dc20e5b20aeb65c2c3a56533bdca801c" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.1.1.tgz#2287cfc3dc20e5b20aeb65c2c3a56533bdca801c"
integrity sha512-g+4pzAw7PYSjARtLBoDq6DmcblX8i9KJHSCnyM5VDDFFifUaUT9iHbFpOF/KOizQ9f7QAqU2JH3Y6aXjzUMhVA== integrity sha512-g+4pzAw7PYSjARtLBoDq6DmcblX8i9KJHSCnyM5VDDFFifUaUT9iHbFpOF/KOizQ9f7QAqU2JH3Y6aXjzUMhVA==
"@vue/test-utils@^2.0.0-beta.2": "@vue/test-utils@^2.0.0-rc.6":
version "2.0.0-rc.6" version "2.0.0-rc.6"
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.0.0-rc.6.tgz#d0aac24d20450d379e183f70542c0822670b8783" resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.0.0-rc.6.tgz#d0aac24d20450d379e183f70542c0822670b8783"
integrity sha512-0cnQBVH589PwgqWpyv1fgCAz+9Ram/MsvN3ZEAEVXi1aPuhUa22EudGc0WezQ9PKwR+L40NrBmt3JBXE2tSRRQ== integrity sha512-0cnQBVH589PwgqWpyv1fgCAz+9Ram/MsvN3ZEAEVXi1aPuhUa22EudGc0WezQ9PKwR+L40NrBmt3JBXE2tSRRQ==
...@@ -2835,10 +2835,10 @@ ansi-wrap@0.1.0, ansi-wrap@^0.1.0: ...@@ -2835,10 +2835,10 @@ ansi-wrap@0.1.0, ansi-wrap@^0.1.0:
resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf"
integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768=
ant-design-vue@^2.2.0-beta.2: ant-design-vue@^2.2.0-beta.3:
version "2.2.0-beta.2" version "2.2.0-beta.3"
resolved "https://registry.yarnpkg.com/ant-design-vue/-/ant-design-vue-2.2.0-beta.2.tgz#f6a97245a81e4aabfb792c3ff17292919a2112a7" resolved "https://registry.yarnpkg.com/ant-design-vue/-/ant-design-vue-2.2.0-beta.3.tgz#ef01b99b8b823a00ace4d373539bfd393b96a640"
integrity sha512-Dqpc1J3aG/beww/Hb8g3I+eDAW+zfxvQNFuyZBmlI7pKAIV6GZOOtQKI4oq8wsvxr32TP/Xl4/ffKN2Z03aefQ== integrity sha512-bFcAZK4mUaiHQwYLof+quY/6X2HBwEAYXcEEe2bOzAZZ8o3dTalpkEBAYSIPAGuQ+swdWRGGgEMIVJoeyW36oA==
dependencies: dependencies:
"@ant-design-vue/use" "^0.0.1-0" "@ant-design-vue/use" "^0.0.1-0"
"@ant-design/icons-vue" "^6.0.0" "@ant-design/icons-vue" "^6.0.0"
......
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