Commit c6c2a115 authored by Sendya's avatar Sendya

fix: FooterToolbar auto margin

parent 0f46541a
......@@ -224,8 +224,14 @@ const layoutConf = reactive({
</template>
```
### Use WaterMark
```vue
<router-view v-slot="{ Component }">
<WaterMark content="Pro Layout">
<component :is="Component" />
</WaterMark>
</router-view>
```
## Build project
......
......@@ -6,7 +6,7 @@
v-bind="state"
:loading="loading"
:breadcrumb="{ routes: breadcrumb }"
iconfont-url="//at.alicdn.com/t/font_2804900_26tw52dc2pl.js"
iconfont-url="//at.alicdn.com/t/font_2804900_nzigh7z84gc.js"
>
<template #menuHeaderRender>
<a>
......@@ -39,7 +39,7 @@
</router-link>
</template>
<template #menuExtraRender="{ collapsed }">
<a-input-search v-if="!collapsed" />
<a-input-search v-if="!collapsed" @search="handleSearch" />
</template>
<template #menuFooterRender>
<a
......@@ -67,26 +67,13 @@
</a>
</template>
<!-- custom menu-item -->
<template #menuItemRender="{ item, icon }">
<a-menu-item
:key="item.path"
:disabled="item.meta?.disabled"
:danger="item.meta?.danger"
:icon="icon"
>
<router-link :to="{ path: item.path }">
<span class="ant-pro-menu-item">
<a-badge count="5" dot>
<span class="ant-pro-menu-item-title">{{ item.meta.title }}</span>
</a-badge>
</span>
</router-link>
</a-menu-item>
</template>
<!-- content begin -->
<router-view />
<router-view v-slot="{ Component }">
<WaterMark content="Pro Layout">
<component :is="Component" />
</WaterMark>
</router-view>
<!-- content end -->
<FooterToolbar>
<template #extra>
......@@ -120,8 +107,8 @@
<script lang="ts">
import { computed, defineComponent, reactive, ref, watchEffect } from 'vue';
import { useRouter } from 'vue-router';
import { Button, Input, Switch, Select, Avatar, Space, Badge, Menu } from 'ant-design-vue';
import { getMenuData, clearMenuItem, FooterToolbar } from '@ant-design-vue/pro-layout';
import { message, Button, Input, Switch, Select, Avatar, Space, Badge, Menu } from 'ant-design-vue';
import { getMenuData, clearMenuItem, WaterMark, FooterToolbar } from '@ant-design-vue/pro-layout';
import type { RouteContextProps } from '@ant-design-vue/pro-layout';
const i18n = (t: string) => t;
......@@ -130,6 +117,8 @@ export default defineComponent({
name: 'BasicLayout',
components: {
FooterToolbar,
WaterMark,
[Button.name]: Button,
[Input.name]: Input,
[Input.Search.name]: Input.Search,
......@@ -137,7 +126,6 @@ export default defineComponent({
[Select.name]: Select,
[Select.Option.displayName!]: Select.Option,
[Space.name]: Space,
[Badge.name]: Badge,
[Avatar.name]: Avatar,
[Menu.Item.name]: Menu.Item,
......@@ -161,7 +149,7 @@ export default defineComponent({
// title: 'ProLayout',
// logo: 'https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg',
navTheme: 'dark',
layout: 'mix',
layout: 'side',
fixSiderbar: true,
});
const breadcrumb = computed(() =>
......@@ -202,6 +190,9 @@ export default defineComponent({
handlePageLoadingClick,
handleCollapsed,
handleSearch: () => {
message.info('search..');
},
};
},
});
......
import { computed, reactive, unref, defineComponent, toRefs } from 'vue';
import type { CSSProperties, PropType, ExtractPropTypes } from 'vue';
import { computed, reactive, unref, defineComponent, toRefs } from 'vue'
import type { CSSProperties, PropType, ExtractPropTypes } from 'vue'
import 'ant-design-vue/es/layout/style';
import { Layout } from 'ant-design-vue';
import { withInstall } from 'ant-design-vue/es/_util/type';
import useMediaQuery from './hooks/useMediaQuery';
import 'ant-design-vue/es/layout/style'
import { Layout } from 'ant-design-vue'
import { withInstall } from 'ant-design-vue/es/_util/type'
import useMediaQuery from './hooks/useMediaQuery'
import { defaultSettingProps } from './defaultSettings';
import { provideRouteContext, defaultRouteContext, RouteContextProps } from './RouteContext';
import SiderMenuWrapper, { siderMenuProps } from './SiderMenu';
import { WrapContent } from './WrapContent';
import globalHeaderProps from './GlobalHeader/headerProps';
import { HeaderView as Header, headerViewProps } from './Header';
import { getPropsSlot, getPropsSlotfn, PropTypes, pick } from './utils';
import type { BreadcrumbProps } from './RouteContext';
import type { CustomRender, FormatMessage, WithFalse } from './typings';
import { defaultSettingProps } from './defaultSettings'
import {
provideRouteContext,
defaultRouteContext,
RouteContextProps,
} from './RouteContext'
import SiderMenuWrapper, { siderMenuProps } from './SiderMenu'
import { WrapContent } from './WrapContent'
import globalHeaderProps from './GlobalHeader/headerProps'
import { HeaderView as Header, headerViewProps } from './Header'
import {
getPropsSlot,
getPropsSlotfn,
PropTypes,
getMenuFirstChildren,
pick,
} from './utils'
import type { BreadcrumbProps } from './RouteContext'
import type { CustomRender, FormatMessage, WithFalse } from './typings'
import PageLoading from './PageLoading';
import './BasicLayout.less';
import PageLoading from './PageLoading'
import './BasicLayout.less'
export const basicLayoutProps = {
...defaultSettingProps,
......@@ -30,7 +40,7 @@ export const basicLayoutProps = {
locale: {
type: [Function, Boolean] as PropType<WithFalse<FormatMessage>>,
default() {
return (s: string) => s;
return (s: string) => s
},
},
/**
......@@ -47,11 +57,15 @@ export const basicLayoutProps = {
default: () => null,
},
collapsedButtonRender: {
type: [Function, Object, Boolean] as PropType<WithFalse<(collapsed?: boolean) => any>>,
type: [Function, Object, Boolean] as PropType<
WithFalse<(collapsed?: boolean) => any>
>,
default: () => undefined,
},
breadcrumbRender: {
type: [Object, Function, Boolean] as PropType<WithFalse<BreadcrumbProps['itemRender']>>,
type: [Object, Function, Boolean] as PropType<
WithFalse<BreadcrumbProps['itemRender']>
>,
default: () => {},
},
headerContentRender: {
......@@ -59,18 +73,24 @@ export const basicLayoutProps = {
default: () => undefined,
},
headerRender: {
type: [Object, Function, Boolean] as PropType<WithFalse<(props: any /* HeaderProps */) => any>>,
type: [Object, Function, Boolean] as PropType<
WithFalse<(props: any /* HeaderProps */) => any>
>,
default: () => undefined,
},
footerRender: {
type: [Object, Function, Boolean] as PropType<WithFalse<(props: any /* FooterProps */) => any>>,
type: [Object, Function, Boolean] as PropType<
WithFalse<(props: any /* FooterProps */) => any>
>,
default: () => undefined,
},
colSize: PropTypes.string,
contentStyle: PropTypes.style,
};
}
export type BasicLayoutProps = Partial<ExtractPropTypes<typeof basicLayoutProps>>;
export type BasicLayoutProps = Partial<
ExtractPropTypes<typeof basicLayoutProps>
>
const ProLayout = defineComponent({
name: 'ProLayout',
......@@ -87,34 +107,49 @@ const ProLayout = defineComponent({
'menuClick',
],
setup(props, { emit, slots }) {
const isTop = computed(() => props.layout === 'top');
const siderWidth = computed(() => (props.collapsed ? props.collapsedWidth : props.siderWidth));
console.log('props', props)
const isTop = computed(() => props.layout === 'top')
const hasSide = computed(
() => props.layout === 'mix' || props.layout === 'side' || false
)
const hasSplitMenu = computed(
() => props.layout === 'mix' && props.splitMenus
)
const hasFlatMenu = computed(() => {
return hasSide.value && hasSplitMenu.value
})
const siderWidth = computed(() =>
props.collapsed ? props.collapsedWidth : props.siderWidth
)
// if on event and @event
const onCollapse = (collapsed: boolean) => {
emit('update:collapsed', collapsed);
emit('collapse', collapsed);
};
emit('update:collapsed', collapsed)
emit('collapse', collapsed)
}
const onOpenKeys = (openKeys: string[] | false) => {
emit('update:open-keys', openKeys);
emit('openKeys', openKeys);
};
emit('update:open-keys', openKeys)
emit('openKeys', openKeys)
}
const onSelect = (selectedKeys: string[] | false) => {
emit('update:selected-keys', selectedKeys);
emit('select', selectedKeys);
};
emit('update:selected-keys', selectedKeys)
emit('select', selectedKeys)
}
const onMenuHeaderClick = (e: MouseEvent) => {
emit('menuHeaderClick', e);
};
emit('menuHeaderClick', e)
}
const onMenuClick = (args: any) => {
emit('menuClick', args);
};
emit('menuClick', args)
}
const colSize = useMediaQuery();
const colSize = useMediaQuery()
const isMobile = computed(
() => (colSize.value === 'sm' || colSize.value === 'xs') && !props.disableMobile,
);
const baseClassName = computed(() => `${props.prefixCls}-basicLayout`);
() =>
(colSize.value === 'sm' || colSize.value === 'xs') &&
!props.disableMobile
)
const baseClassName = computed(() => `${props.prefixCls}-basicLayout`)
// gen className
const className = computed(() => {
return {
......@@ -124,41 +159,52 @@ const ProLayout = defineComponent({
[`${baseClassName.value}-is-children`]: props.isChildrenLayout,
[`${baseClassName.value}-fix-siderbar`]: props.fixSiderbar,
[`${baseClassName.value}-${props.layout}`]: props.layout,
};
});
}
})
// siderMenuDom 为空的时候,不需要 padding
const genLayoutStyle = reactive<CSSProperties>({
position: 'relative',
});
})
// if is some layout children, don't need min height
if (props.isChildrenLayout || (props.contentStyle && props.contentStyle.minHeight)) {
genLayoutStyle.minHeight = 0;
if (
props.isChildrenLayout ||
(props.contentStyle && props.contentStyle.minHeight)
) {
genLayoutStyle.minHeight = 0
}
const headerRender = (
p: BasicLayoutProps & {
hasSiderMenu: boolean;
headerRender: WithFalse<CustomRender>;
rightContentRender: WithFalse<CustomRender>;
hasSiderMenu: boolean
headerRender: WithFalse<CustomRender>
rightContentRender: WithFalse<CustomRender>
},
matchMenuKeys?: string[],
matchMenuKeys?: string[]
): CustomRender | null => {
if (p.headerRender === false || p.pure) {
return null;
return null
}
return <Header {...p} matchMenuKeys={matchMenuKeys || []} />;
};
return <Header {...p} matchMenuKeys={matchMenuKeys || []} />
}
const breadcrumb = computed<BreadcrumbProps>(() => ({
...props.breadcrumb,
itemRender: getPropsSlotfn(slots, props, 'breadcrumbRender'),
}));
}))
const flatMenuData = computed(
() =>
(hasFlatMenu.value &&
props.selectedKeys &&
getMenuFirstChildren(props.menuData, props.selectedKeys[0])) ||
[]
)
const routeContext = reactive<RouteContextProps>(
Object.assign(
{...defaultRouteContext},
{ ...defaultRouteContext },
pick(toRefs(props), [
'locale',
'menuData',
......@@ -173,14 +219,18 @@ const ProLayout = defineComponent({
// 'hasFooter',
// 'hasFooterToolbar',
// 'setHasFooterToolbar',
]) as any,
]) as any,
{
isMobile,
siderWidth,
breadcrumb,
flatMenuData,
hasSide,
flatMenu: hasFlatMenu,
}
)
);
provideRouteContext(routeContext);
)
provideRouteContext(routeContext)
return () => {
const {
......@@ -190,24 +240,44 @@ const ProLayout = defineComponent({
onSelect: propsOnSelect,
onMenuClick: propsOnMenuClick,
...restProps
} = props;
} = props
const collapsedButtonRender = getPropsSlotfn(slots, props, 'collapsedButtonRender');
const headerContentRender = getPropsSlot(slots, props, 'headerContentRender');
const rightContentRender = getPropsSlot(slots, props, 'rightContentRender');
const customHeaderRender = getPropsSlot(slots, props, 'headerRender');
const menuHeaderRender = getPropsSlotfn(slots, props, 'menuHeaderRender');
const menuContentRender = getPropsSlotfn(slots, props, 'menuContentRender');
const menuExtraRender = getPropsSlotfn(slots, props, 'menuExtraRender');
const menuFooterRender = getPropsSlotfn(slots, props, 'menuFooterRender');
const footerRender = getPropsSlot(slots, props, 'footerRender');
const collapsedButtonRender = getPropsSlotfn(
slots,
props,
'collapsedButtonRender'
)
const headerContentRender = getPropsSlot(
slots,
props,
'headerContentRender'
)
const rightContentRender = getPropsSlot(
slots,
props,
'rightContentRender'
)
const customHeaderRender = getPropsSlot(slots, props, 'headerRender')
const menuHeaderRender = getPropsSlotfn(slots, props, 'menuHeaderRender')
const menuContentRender = getPropsSlotfn(
slots,
props,
'menuContentRender'
)
const menuExtraRender = getPropsSlotfn(slots, props, 'menuExtraRender')
const menuFooterRender = getPropsSlotfn(slots, props, 'menuFooterRender')
const footerRender = getPropsSlot(slots, props, 'footerRender')
// menu render
const menuItemRender = getPropsSlotfn(slots, props, 'menuItemRender');
const subMenuItemRender = getPropsSlotfn(slots, props, 'subMenuItemRender');
const menuItemRender = getPropsSlotfn(slots, props, 'menuItemRender')
const subMenuItemRender = getPropsSlotfn(
slots,
props,
'subMenuItemRender'
)
const menuRenders = {
menuItemRender,
subMenuItemRender,
};
}
const headerDom = computed(() =>
headerRender(
......@@ -227,13 +297,15 @@ const ProLayout = defineComponent({
menuContentRender: menuContentRender,
headerContentRender,
headerRender: customHeaderRender,
theme: (props.navTheme || 'dark').toLocaleLowerCase().includes('dark')
theme: (props.navTheme || 'dark')
.toLocaleLowerCase()
.includes('dark')
? 'dark'
: 'light',
},
props.matchMenuKeys,
),
);
props.matchMenuKeys
)
)
return (
<>
......@@ -262,7 +334,11 @@ const ProLayout = defineComponent({
{headerDom.value}
<WrapContent
isChildrenLayout={props.isChildrenLayout}
style={props.disableContentMargin ? undefined : props.contentStyle}
style={
props.disableContentMargin
? undefined
: props.contentStyle
}
>
{props.loading ? <PageLoading /> : slots.default?.()}
</WrapContent>
......@@ -272,9 +348,9 @@ const ProLayout = defineComponent({
</div>
)}
</>
);
};
)
}
},
});
})
export default withInstall(ProLayout);
export default withInstall(ProLayout)
import './index.less';
import './index.less'
import { computed, defineComponent, onBeforeUnmount, onMounted, PropType } from 'vue';
import { RouteContextProps, useRouteContext } from '../RouteContext';
import { getMenuFirstChildren, getPropsSlot, getPropsSlotfn } from '../utils';
import type { CustomRender } from '../typings';
import {
computed,
defineComponent,
onBeforeUnmount,
onMounted,
unref,
PropType,
} from 'vue'
import { RouteContextProps, useRouteContext } from '../RouteContext'
import { getPropsSlotfn } from '../utils'
import type { CustomRender } from '../typings'
export interface FooterToolbarProps {
extra?: CustomRender | JSX.Element;
extra?: CustomRender | JSX.Element
renderContent?: (
props: FooterToolbarProps & RouteContextProps & { leftWidth?: string },
dom: CustomRender | JSX.Element,
) => CustomRender | JSX.Element;
getContainer?: (triggerNode: HTMLElement) => HTMLElement | null;
prefixCls?: string;
dom: CustomRender | JSX.Element
) => CustomRender | JSX.Element
getContainer?: (triggerNode: HTMLElement) => HTMLElement | null
prefixCls?: string
}
const footerToolbarProps = {
......@@ -25,52 +32,47 @@ const footerToolbarProps = {
type: [Function, Object] as PropType<FooterToolbarProps['getContainer']>,
},
prefixCls: { type: String as PropType<string> },
};
}
const FooterToolbar = defineComponent({
name: 'FooterToolbar',
props: footerToolbarProps,
setup(props, { slots }) {
const routeContext = useRouteContext();
const { getPrefixCls } = routeContext;
const baseClassName = props.prefixCls || getPrefixCls('footer-bar');
// matchMenuKeys
const matchMenuChildrenSize = computed(
() =>
(
(routeContext.menuData &&
getMenuFirstChildren(
routeContext.menuData,
(routeContext.selectedKeys && routeContext.selectedKeys[0]) || undefined,
)) ||
[]
).length,
);
const hasSide = computed(() => {
return routeContext.layout === 'mix' && routeContext.splitMenus
? matchMenuChildrenSize.value > 0
: true;
});
const context = useRouteContext()
const baseClassName = props.prefixCls || context.getPrefixCls('footer-bar')
const hasFlatMenu = computed(() => {
return unref(context.flatMenuData).length > 0
})
const width = computed(() => {
const { isMobile, sideWidth, layout } = routeContext;
if (!sideWidth || layout === 'top') {
return '100%';
const { isMobile, hasSide, siderWidth, layout } = context
if (!siderWidth || layout === 'top') {
return '100%'
}
if (!hasSide.value) {
return '100%';
console.log(
'x',
unref(siderWidth),
'hasFlatMenu',
unref(hasFlatMenu),
'hasSide',
unref(context.hasSide)
)
if (!hasFlatMenu.value && !unref(hasSide)) {
return '100%'
}
return isMobile ? '100%' : `calc(100% - ${sideWidth}px)`;
});
console.log('x2', unref(context.hasSide))
return isMobile ? '100%' : `calc(100% - ${siderWidth}px)`
})
onMounted(() => {
routeContext.setHasFooterToolbar && routeContext.setHasFooterToolbar(true);
});
context.setHasFooterToolbar && context.setHasFooterToolbar(true)
})
onBeforeUnmount(() => {
routeContext.setHasFooterToolbar && routeContext.setHasFooterToolbar(false);
});
context.setHasFooterToolbar && context.setHasFooterToolbar(false)
})
return () => {
const extra = getPropsSlotfn(slots, props, 'extra');
const extra = getPropsSlotfn(slots, props, 'extra')
const dom = () => {
return (
<>
......@@ -79,24 +81,24 @@ const FooterToolbar = defineComponent({
</div>
<div class={`${baseClassName}-right`}>{slots.default?.()}</div>
</>
);
};
)
}
return (
<div class={baseClassName} style={{ width: width.value }}>
{props.renderContent
? props.renderContent(
{
...props,
...routeContext,
...context,
leftWidth: width.value,
},
dom(),
dom()
)
: dom()}
</div>
);
};
)
}
},
});
})
export default FooterToolbar;
export default FooterToolbar
import { InjectionKey, provide, reactive, Ref, VNodeChild, ComputedRef } from 'vue';
import { createContext, useContext } from './hooks/context';
import { MenuDataItem, FormatMessage, WithFalse } from './typings';
import { PureSettings } from './defaultSettings';
import {
InjectionKey,
provide,
reactive,
Ref,
VNodeChild,
ComputedRef,
} from 'vue'
import { createContext, useContext } from './hooks/context'
import { MenuDataItem, FormatMessage, WithFalse } from './typings'
import { PureSettings } from './defaultSettings'
export interface Route {
path: string;
breadcrumbName: string;
children?: Omit<Route, 'children'>[];
path: string
breadcrumbName: string
children?: Omit<Route, 'children'>[]
}
export interface BreadcrumbProps {
prefixCls?: string;
routes?: Route[];
params?: any;
separator?: VNodeChild;
prefixCls?: string
routes?: Route[]
params?: any
separator?: VNodeChild
itemRender?: (opts: {
route: Route;
params: any;
routes: Array<Route>;
paths: Array<string>;
}) => VNodeChild;
route: Route
params: any
routes: Array<Route>
paths: Array<string>
}) => VNodeChild
}
export type BreadcrumbListReturn = Pick<
BreadcrumbProps,
Extract<keyof BreadcrumbProps, 'routes' | 'itemRender'>
>;
>
export interface MenuState {
selectedKeys: string[];
openKeys: string[];
selectedKeys: string[]
openKeys: string[]
}
export interface RouteContextProps extends Partial<PureSettings>, MenuState {
menuData: MenuDataItem[];
menuData: MenuDataItem[]
flatMenuData?: MenuDataItem[]
getPrefixCls?: (suffixCls?: string, customizePrefixCls?: string) => string;
locale?: WithFalse<FormatMessage>;
breadcrumb?: BreadcrumbListReturn | ComputedRef<BreadcrumbListReturn>;
isMobile?: boolean;
prefixCls?: string;
collapsed?: boolean;
hasSideMenu?: boolean;
hasHeader?: boolean;
sideWidth?: number;
headerHeight?: number;
hasFooterToolbar?: boolean;
hasFooter?: boolean;
setHasFooterToolbar?: (bool: boolean) => void;
getPrefixCls?: (suffixCls?: string, customizePrefixCls?: string) => string
locale?: WithFalse<FormatMessage>
breadcrumb?: BreadcrumbListReturn | ComputedRef<BreadcrumbListReturn>
isMobile?: boolean
prefixCls?: string
collapsed?: boolean
hasSideMenu?: boolean
hasHeader?: boolean
siderWidth?: number
headerHeight?: number
hasFooterToolbar?: boolean
hasFooter?: boolean
hasSide?: boolean
setHasFooterToolbar?: (bool: boolean) => void
/* 附加属性 */
[key: string]: any;
[key: string]: any
}
export const defaultPrefixCls = 'ant-pro';
export const defaultPrefixCls = 'ant-pro'
export const getPrefixCls = (suffixCls?: string, customizePrefixCls?: string) => {
if (customizePrefixCls) return customizePrefixCls;
return suffixCls ? `${defaultPrefixCls}-${suffixCls}` : defaultPrefixCls;
};
export const getPrefixCls = (
suffixCls?: string,
customizePrefixCls?: string
) => {
if (customizePrefixCls) return customizePrefixCls
return suffixCls ? `${defaultPrefixCls}-${suffixCls}` : defaultPrefixCls
}
// set default context
export const defaultRouteContext = reactive({
......@@ -65,22 +77,31 @@ export const defaultRouteContext = reactive({
locale: (t: string) => t,
contentWidth: 'Fluid',
hasFooterToolbar: false,
});
})
const routeContextInjectKey: InjectionKey<RouteContextProps> = Symbol('route-context');
const routeContextInjectKey: InjectionKey<RouteContextProps> =
Symbol('route-context')
export const createRouteContext = () =>
createContext<RouteContextProps>(routeContextInjectKey, 'RouteContext.Provider');
createContext<RouteContextProps>(
routeContextInjectKey,
'RouteContext.Provider'
)
export const provideRouteContext = (value: RouteContextProps | Ref<RouteContextProps>) => {
provide(routeContextInjectKey, value);
};
export const provideRouteContext = (
value: RouteContextProps | Ref<RouteContextProps>
) => {
provide(routeContextInjectKey, value)
}
export const useRouteContext = () =>
useContext<Required<RouteContextProps>>(routeContextInjectKey, defaultRouteContext);
useContext<Required<RouteContextProps>>(
routeContextInjectKey,
defaultRouteContext
)
const Provider = createRouteContext();
const Provider = createRouteContext()
export default {
Provider,
};
}
......@@ -5,24 +5,24 @@ import {
PropType,
CSSProperties,
unref,
} from 'vue';
import 'ant-design-vue/es/layout/style';
import 'ant-design-vue/es/menu/style';
import { Layout, Menu } from 'ant-design-vue';
import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue';
import BaseMenu, { baseMenuProps } from './BaseMenu';
import { WithFalse, CustomRender } from '../typings';
import { SiderProps } from './typings';
import { defaultSettingProps } from '../defaultSettings';
import { useRouteContext } from '../RouteContext';
import { PropTypes, getMenuFirstChildren } from '../utils';
import './index.less';
} from 'vue'
import 'ant-design-vue/es/layout/style'
import 'ant-design-vue/es/menu/style'
import { Layout, Menu } from 'ant-design-vue'
import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue'
import BaseMenu, { baseMenuProps } from './BaseMenu'
import { WithFalse, CustomRender } from '../typings'
import { SiderProps } from './typings'
import { defaultSettingProps } from '../defaultSettings'
import { useRouteContext } from '../RouteContext'
import { PropTypes, getMenuFirstChildren } from '../utils'
import './index.less'
const { Sider } = Layout;
const { Sider } = Layout
export type PrivateSiderMenuProps = {
matchMenuKeys?: string[];
};
matchMenuKeys?: string[]
}
export const siderMenuProps = {
...defaultSettingProps,
......@@ -40,12 +40,16 @@ export const siderMenuProps = {
collapsedWidth: PropTypes.number.def(48),
menuHeaderRender: {
type: [Function, Object] as PropType<
WithFalse<(logo: CustomRender, title: CustomRender, props?: any) => CustomRender>
WithFalse<
(logo: CustomRender, title: CustomRender, props?: any) => CustomRender
>
>,
default: () => undefined,
},
menuFooterRender: {
type: [Function, Object] as PropType<WithFalse<(props?: any) => CustomRender>>,
type: [Function, Object] as PropType<
WithFalse<(props?: any) => CustomRender>
>,
default: () => undefined,
},
menuContentRender: {
......@@ -55,11 +59,15 @@ export const siderMenuProps = {
default: () => undefined,
},
menuExtraRender: {
type: [Function, Object] as PropType<WithFalse<(props?: any) => CustomRender>>,
type: [Function, Object] as PropType<
WithFalse<(props?: any) => CustomRender>
>,
default: () => undefined,
},
collapsedButtonRender: {
type: [Function, Object, Boolean] as PropType<WithFalse<(collapsed?: boolean) => CustomRender>>,
type: [Function, Object, Boolean] as PropType<
WithFalse<(collapsed?: boolean) => CustomRender>
>,
default: () => undefined,
},
breakpoint: {
......@@ -87,49 +95,52 @@ export const siderMenuProps = {
onSelect: {
type: Function as PropType<(selectedKeys: WithFalse<string[]>) => void>,
},
};
}
export type SiderMenuProps = Partial<ExtractPropTypes<typeof siderMenuProps>>;
export type SiderMenuProps = Partial<ExtractPropTypes<typeof siderMenuProps>>
export const defaultRenderLogo = (logo?: CustomRender, logoStyle?: CSSProperties): CustomRender => {
export const defaultRenderLogo = (
logo?: CustomRender,
logoStyle?: CSSProperties
): CustomRender => {
if (!logo) {
return null;
return null
}
if (typeof logo === 'string') {
return <img src={logo} alt="logo" style={logoStyle} />;
return <img src={logo} alt="logo" style={logoStyle} />
}
if (typeof logo === 'function') {
return logo();
return logo()
}
return logo;
};
return logo
}
export const defaultRenderLogoAndTitle = (
props: SiderMenuProps,
renderKey: string | undefined = 'menuHeaderRender',
renderKey: string | undefined = 'menuHeaderRender'
): CustomRender | null => {
const {
logo = 'https://gw.alipayobjects.com/zos/antfincdn/PmY%24TNNDBI/logo.svg',
logoStyle,
title,
layout,
} = props;
const renderFunction = (props as any)[renderKey || ''];
} = props
const renderFunction = (props as any)[renderKey || '']
if (renderFunction === false) {
return null;
return null
}
const logoDom = defaultRenderLogo(logo, logoStyle);
const titleDom = <h1>{title}</h1>;
const logoDom = defaultRenderLogo(logo, logoStyle)
const titleDom = <h1>{title}</h1>
if (layout === 'mix' && renderKey === 'menuHeaderRender') {
return null;
return null
}
// call menuHeaderRender
if (typeof renderFunction === 'function') {
// when collapsed, no render title
return renderFunction(logoDom, props.collapsed ? null : titleDom, props);
return renderFunction(logoDom, props.collapsed ? null : titleDom, props)
}
if (Array.isArray(renderFunction)) {
return <>{renderFunction}</>;
return <>{renderFunction}</>
}
return (
......@@ -137,11 +148,12 @@ export const defaultRenderLogoAndTitle = (
{logoDom}
{props.collapsed ? null : titleDom}
</a>
);
};
)
}
export const defaultRenderCollapsedButton = (collapsed?: boolean): CustomRender =>
collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />;
export const defaultRenderCollapsedButton = (
collapsed?: boolean
): CustomRender => (collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />)
const SiderMenu: FC<SiderMenuProps> = (props: SiderMenuProps) => {
const {
......@@ -158,44 +170,42 @@ const SiderMenu: FC<SiderMenuProps> = (props: SiderMenuProps) => {
onOpenKeys,
onSelect,
onMenuHeaderClick,
} = props;
const context = useRouteContext();
const { getPrefixCls } = context;
const baseClassName = getPrefixCls('sider');
const hasSplitMenu = computed(() => props.layout === 'mix' && props.splitMenus);
const hasSide = computed(() => props.layout === 'mix' || props.layout === 'side' || false);
const sTheme = computed(() => (props.layout === 'mix' && 'light') || props.navTheme);
const sSideWidth = computed(() => (props.collapsed ? props.collapsedWidth : props.siderWidth));
} = props
const context = useRouteContext()
const { getPrefixCls } = context
const baseClassName = getPrefixCls('sider')
const hasSplitMenu = computed(
() => props.layout === 'mix' && props.splitMenus
)
const sTheme = computed(
() => (props.layout === 'mix' && 'light') || props.navTheme
)
const sSideWidth = computed(() =>
props.collapsed ? props.collapsedWidth : props.siderWidth
)
const classNames = computed(() => {
return {
[baseClassName]: true,
[`${baseClassName}-${sTheme.value}`]: true,
[`${baseClassName}-${props.layout}`]: true,
[`${baseClassName}-fixed`]: context.fixSiderbar,
};
});
const flatMenuData = computed(
() =>
(hasSide.value &&
hasSplitMenu.value &&
getMenuFirstChildren(context.menuData, context.selectedKeys[0])) ||
[],
);
}
})
const handleSelect = ($event: string[]) => {
if (props.onSelect) {
if (unref(hasSplitMenu)) {
props.onSelect([context.selectedKeys[0], ...$event]);
return;
props.onSelect([context.selectedKeys[0], ...$event])
return
}
props.onSelect($event);
props.onSelect($event)
}
};
}
// call menuHeaderRender
const headerDom = defaultRenderLogoAndTitle(props);
const extraDom = menuExtraRender && menuExtraRender(props);
if (hasSplitMenu.value && flatMenuData.value.length === 0) {
return null;
const headerDom = defaultRenderLogoAndTitle(props)
const extraDom = menuExtraRender && menuExtraRender(props)
if (hasSplitMenu.value && unref(context.flatMenuData).length === 0) {
return null
}
const defaultMenuDom = (
<BaseMenu
......@@ -203,7 +213,7 @@ const SiderMenu: FC<SiderMenuProps> = (props: SiderMenuProps) => {
locale={props.locale || context.locale}
theme={sTheme.value === 'realDark' ? 'dark' : sTheme.value}
mode="inline"
menuData={hasSplitMenu.value ? flatMenuData.value : context.menuData}
menuData={hasSplitMenu.value ? context.flatMenuData : context.menuData}
collapsed={props.collapsed}
openKeys={context.openKeys}
selectedKeys={context.selectedKeys}
......@@ -216,11 +226,12 @@ const SiderMenu: FC<SiderMenuProps> = (props: SiderMenuProps) => {
}}
class={`${baseClassName}-menu`}
{...{
'onUpdate:openKeys': ($event: string[]) => onOpenKeys && onOpenKeys($event),
'onUpdate:openKeys': ($event: string[]) =>
onOpenKeys && onOpenKeys($event),
'onUpdate:selectedKeys': handleSelect,
}}
/>
);
)
return (
<>
......@@ -242,14 +253,16 @@ const SiderMenu: FC<SiderMenuProps> = (props: SiderMenuProps) => {
collapsed={collapsed}
breakpoint={breakpoint || undefined}
onCollapse={(collapse: boolean) => {
if (props.isMobile) return;
onCollapse?.(collapse);
if (props.isMobile) return
onCollapse?.(collapse)
}}
collapsedWidth={collapsedWidth}
style={{
overflow: 'hidden',
paddingTop:
props.layout === 'mix' && !props.isMobile ? `${props.headerHeight}px` : undefined,
props.layout === 'mix' && !props.isMobile
? `${props.headerHeight}px`
: undefined,
}}
width={siderWidth}
theme={sTheme.value === 'realDark' ? 'dark' : sTheme.value}
......@@ -276,7 +289,8 @@ const SiderMenu: FC<SiderMenuProps> = (props: SiderMenuProps) => {
</div>
)}
<div style="flex: 1; overflow: hidden auto;">
{(menuContentRender && menuContentRender(props, defaultMenuDom)) || defaultMenuDom}
{(menuContentRender && menuContentRender(props, defaultMenuDom)) ||
defaultMenuDom}
</div>
<div class={`${baseClassName}-links`}>
{collapsedButtonRender !== false ? (
......@@ -290,7 +304,7 @@ const SiderMenu: FC<SiderMenuProps> = (props: SiderMenuProps) => {
// @ts-ignore
onClick={() => {
if (onCollapse) {
onCollapse(!props.collapsed);
onCollapse(!props.collapsed)
}
}}
>
......@@ -299,17 +313,20 @@ const SiderMenu: FC<SiderMenuProps> = (props: SiderMenuProps) => {
class={`${baseClassName}-collapsed-button`}
title={false}
>
{collapsedButtonRender && typeof collapsedButtonRender === 'function'
{collapsedButtonRender &&
typeof collapsedButtonRender === 'function'
? collapsedButtonRender(collapsed)
: collapsedButtonRender}
</Menu.Item>
</Menu>
) : null}
</div>
{menuFooterRender && <div class={`${baseClassName}-footer`}>{menuFooterRender(props)}</div>}
{menuFooterRender && (
<div class={`${baseClassName}-footer`}>{menuFooterRender(props)}</div>
)}
</Sider>
</>
);
};
)
}
export default SiderMenu;
export default SiderMenu
......@@ -197,6 +197,7 @@
.@{ant-prefix}-menu-submenu-title {
.anticon {
transition: none;
font-size: 16px;
}
}
......
......@@ -68,6 +68,7 @@ const getPixelRatio = (context: any) => {
}
const WaterMark = defineComponent({
name: 'WaterMark',
props: waterMarkProps,
setup(props, { slots }) {
const {
......@@ -145,32 +146,34 @@ const WaterMark = defineComponent({
}
})
return (
<div
style={{
position: 'relative',
}}
class={wrapperCls.value}
>
{slots.default?.()}
return () => {
return (
<div
class={waterMakrCls.value}
style={{
zIndex,
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%',
backgroundSize: `${gapX + width}px`,
pointerEvents: 'none',
backgroundRepeat: 'repeat',
backgroundImage: `url('${base64Url.value}')`,
...markStyle,
position: 'relative',
}}
/>
</div>
)
class={wrapperCls.value}
>
{slots.default?.()}
<div
class={waterMakrCls.value}
style={{
zIndex,
position: 'absolute',
left: 0,
top: 0,
width: '100%',
height: '100%',
backgroundSize: `${gapX + width}px`,
pointerEvents: 'none',
backgroundRepeat: 'repeat',
backgroundImage: `url('${base64Url.value}')`,
...markStyle,
}}
/>
</div>
)
}
},
})
......
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