Skip to content

Commit

Permalink
fix: router-context
Browse files Browse the repository at this point in the history
  • Loading branch information
sendya committed Dec 11, 2020
1 parent 9bdab4c commit e0039d2
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 152 deletions.
58 changes: 30 additions & 28 deletions examples/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'ant-design-vue/dist/antd.less';
import { createApp, defineComponent, reactive } from 'vue';
import { createApp, defineComponent, watch, ref } from 'vue';
import { RouterLink } from './mock-router';
import { Button, Avatar, message } from 'ant-design-vue';
import { default as ProLayout } from '../src/';
Expand All @@ -12,49 +12,54 @@ const BasicLayout = defineComponent({
name: 'BasicLayout',
inheritAttrs: false,
setup(_, { attrs }) {
const [menuState] = useMenuState({
const [ state, RouteContextProvider ] = createRouteContext({
collapsed: false,
openKeys: [],

openKeys: ['/dashboard', '/form'],
onOpenKeys: (keys: string[]) => (state.openKeys = keys),
selectedKeys: ['/welcome'],
});
onSelectedKeys: (keys: string[]) => (state.selectedKeys = keys),

const [ routeContext, RouteContextProvider ] = createRouteContext({
isMobile: false,
menuData: [],
fixSiderbar: false,
fixedHeader: false,
menuData: menus,
sideWidth: 208,
hasSideMenu: true,
hasHeader: true,
hasFooterToolbar: false,
setHasFooterToolbar: (has: boolean) => (routeContext.hasFooterToolbar = has),
setHasFooterToolbar: (has: boolean) => (state.hasFooterToolbar = has),
});

const cacheOpenKeys = ref<string[]>([]);
watch(
() => state.collapsed,
(collapsed: boolean) => {
if (collapsed) {
cacheOpenKeys.value = state.openKeys;
state.openKeys = [];
} else {
state.openKeys = cacheOpenKeys.value;
}
}
);

return () => (
<RouteContextProvider>
<ProLayout
{...attrs}
v-model={[menuState.collapsed, 'collapsed']}
v-model={[state.collapsed, 'collapsed']}
title={'Pro Layout'}
layout={'mix'}
theme={'light'}
layout={'side'}
navTheme={'dark'}
i18n={(key: string) => key}
isMobile={false}
menuData={menus}
matchMenuKeys={[]}
isMobile={state.isMobile}
fixSiderbar={state.fixSiderbar}
fixedHeader={state.fixedHeader}
contentWidth={'Fixed'}
primaryColor={'#1890ff'}
contentStyle={{ minHeight: '500px' }}
siderWidth={208}
openKeys={menuState.openKeys}
selectedKeys={menuState.selectedKeys}
{...{
'onUpdate:openKeys':$event => {
$event && (menuState.openKeys = $event);
},
'onUpdate:selectedKeys': $event => {
$event && (menuState.selectedKeys = $event);
}
}}
siderWidth={state.sideWidth}
v-slots={{
rightContentRender: () => (
<div style="color: #FFF;margin-right: 16px;">
Expand All @@ -64,11 +69,8 @@ const BasicLayout = defineComponent({
menuHeaderRender: () => (
<a>
<img src="https://gw.alipayobjects.com/zos/antfincdn/PmY%24TNNDBI/logo.svg" />
{menuState.collapsed ? null : (<h1>Pro Layout</h1>)}
{state.collapsed ? null : (<h1>Pro Layout</h1>)}
</a>
),
footerRender: () => (
<div>123</div>
)
}}
>
Expand Down
163 changes: 76 additions & 87 deletions src/BasicLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { default as SiderMenuWrapper, SiderMenuWrapperProps } from './SiderMenu'
import { WrapContent } from './WrapContent';
import { default as Header, HeaderViewProps } from './Header';
import { RenderVNodeType, WithFalse } from './typings';
import { getComponentOrSlot } from './utils';
import { getComponentOrSlot, PropRenderType, PropTypes } from './utils';
import useMergedState from './hooks/useMergedState';
import './BasicLayout.less';

Expand Down Expand Up @@ -141,44 +141,14 @@ const ProLayout: FunctionalComponent<BasicLayoutProps> = (props, { emit, slots,
{ !isTop.value && (<SiderMenuWrapper
{...props}
menuHeaderRender={menuHeaderRenderFunc || (menuHeaderRenderSlot && (() => menuHeaderRenderSlot()))}
onSelect={handleSelect}
onOpenChange={handleOpenChange}
onCollapse={handleCollapse}
/>)}
<Layout>
{headerDom}
<WrapContent style={props.contentStyle}>
{slots.default?.()}
</WrapContent>
{ footerRender && footerRender || footerRender !== false && (
<GlobalFooter
links={[
{
key: '1',
title: 'Pro Layout',
href: 'https://www.github.com/vueComponent/pro-layout',
blankTarget: true,
},
{
key: '2',
title: 'Github',
href: 'https://www.github.com/vueComponent/ant-design-vue-pro',
blankTarget: true,
},
{
key: '3',
title: '@Sendya',
href: 'https://www.github.com/sendya/',
blankTarget: true,
},
]}
copyright={
<a href="https://github.com/vueComponent" target="_blank">
vueComponent
</a>
}
/>
)}
{ footerRender !== false && (footerRender && footerRender)}
</Layout>
</Layout>
</div>
Expand All @@ -191,61 +161,80 @@ ProLayout.inheritAttrs = false;
ProLayout.displayName = 'ProLayout';
ProLayout.emits = ['update:collapsed', 'update:openKeys', 'update:selectedKeys'];
ProLayout.props = {
prefixCls: {
type: String,
default: 'ant-pro',
},
title: String,
colSize: String,
isChildrenLayout: Boolean,
isMobile: Boolean,
fixSiderbar: {
type: Boolean,
default: () => false,
},
fixedHeader: {
type: Boolean,
default: () => false,
},
layout: String,
openKeys: Array,
selectedKeys: Array,
collapsed: Boolean,
menuData: Array,
contentStyle: Object,
theme: String,
headerTheme: {
type: String,
defualt: 'light',
},
navTheme: {
type: String,
default: 'light',
},
headerRender: {
type: [Function, Boolean],
default: () => undefined,
},
footerRender: {
type: [Function, Boolean],
default: () => undefined,
},
menuRender: {
type: [Function, Boolean],
default: () => undefined,
},
menuHeaderRender: {
type: [Function, Boolean],
default: () => undefined,
},
rightContentRender: {
type: [Function, Boolean],
default: () => undefined,
},
rightContent: {
type: [Function, Boolean],
default: () => undefined,
},
prefixCls: PropTypes.string.def('ant-pro'),
title: PropTypes.VNodeChild.def('Ant Design Pro'),
logo: PropTypes.VNodeChild,
/* 是否删除掉所有的自带界面 */
pure: PropTypes.bool,
/* layout 的加载态 */
loading: PropTypes.bool,
/* 用于生成菜单和面包屑 请从 RouterContext 注入 */
// menuData: PropTypes.array,

// location: PropTypes.string,
// Custom render
menuHeaderRender: PropRenderType,
menuFooterRender: PropRenderType,
menuExtraRender: PropRenderType,
/* 自定义头的 render 方法 (props: BasicLayoutProps) => VNode */
headerRender: PropRenderType,
/* 自定义头标题的方法,mix 模式下生效 (props: BasicLayoutProps) => VNode */
headerTitleRender: PropRenderType,
/* 自定义头内容的方法 (props: BasicLayoutProps) => VNode */
headerContentRender: PropRenderType,
/* 自定义头右部的 render 方法 (props: HeaderViewProps) => VNode */
rightContentRender: PropRenderType,
/* 自定义 collapsed button 的方法 (collapsed: boolean) => VNode */
collapsedButtonRender: PropRenderType,
/* 自定义页脚的 render 方法 (props: BasicLayoutProps) => VNode */
footerRender: PropRenderType,
/* 自定义页面标题的显示方法 (props: BasicLayoutProps) => VNode */
pageTitleRender: PropRenderType,
/* 自定义菜单的 render 方法 (props: HeaderViewProps) => VNode */
menuRender: PropRenderType,
/* 自定义菜单项的 render 方法 */
menuItemRender: PropRenderType,
/* 自定义拥有子菜单菜单项的 render 方法 */
subMenuItemRender: PropRenderType,
/* 自定义面包屑的数据 */
breadcrumbRender: PropRenderType,

// Event
onMenuHeaderClick: PropTypes.func,
onTopMixMenuHeaderClick: PropTypes.func,

// settings
contentStyle: PropTypes.style,
layout: PropTypes.string.def('side'),
contentWidth: PropTypes.string.def('Fluid'),
/* 导航的主题,side 和 mix 模式下是左侧菜单的主题,top 模式下是顶部菜单 */
navTheme: PropTypes.string.def('dark'),
/* 顶部导航的主题,mix 模式生效 */
headerTheme: PropTypes.string.def('dark'),
/* 是否固定导航 */
fixSiderbar: PropTypes.bool,
/* 是否固定 header 到顶部 */
fixedHeader: PropTypes.bool,
/* 触发响应式布局的断点 https://ant.design/components/grid-cn/#Col */
breakpoint: PropTypes.string.def('lg'),
/* 关于 menu 的配置,暂时只有 locale,locale 可以关闭 menu 的自带的全球化 */
menu: PropTypes.object,
/* 传递到 antd menu 组件的 props */
menuProps: PropTypes.object,
/* 使用 IconFont 的图标配置 */
iconfontUrl: PropTypes.string,
/* 当前 layout 的语言设置 */
locale: PropTypes.func.def((key: string) => key),
// settings
/* 侧边菜单宽度 */
siderWidth: PropTypes.number.def(208),
/* 控制菜单的收起和展开 */
collapsed: PropTypes.bool,
/* 菜单的折叠收起事件 (collapsed: boolean) => void */
onCollapse: PropTypes.func,
// onPageChange // 请使用 vue-router 监听
/* 禁止自动切换到移动页面 */
disableMobile: PropTypes.bool,
} as any;

export default withInstall(ProLayout);
7 changes: 5 additions & 2 deletions src/GlobalHeader/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { TopNavHeader } from '../TopNavHeader';
import { clearMenuItem } from '../utils';
import type { HeaderViewProps } from '../Header';
import './index.less';
import { useProProvider } from '../ProProvider';

export interface GlobalHeaderProps extends Partial<PureSettings> {
collapsed?: boolean;
Expand Down Expand Up @@ -52,8 +53,10 @@ export const GlobalHeader: FunctionalComponent<GlobalHeaderProps & PrivateSiderM
headerTheme = 'dark',
splitMenus,
menuData,
prefixCls,
prefixCls: customPrefixCls,
} = props;
const { getPrefixCls } = useProProvider();
const prefixCls = customPrefixCls || getPrefixCls();
const baseClassName = computed(() => `${prefixCls}-global-header`);
const className = computed(() => {
return {
Expand Down Expand Up @@ -89,7 +92,7 @@ export const GlobalHeader: FunctionalComponent<GlobalHeaderProps & PrivateSiderM
{isMobile && renderLogo(menuHeaderRender, logoDom)}
{isMobile && collapsedButtonRender && (
<span
class={`${baseClassName.value}-collapsed-button`}
class={`${baseClassName.value}-collapsed-button`}
onClick={() => {
if (onCollapse) {
onCollapse(!collapsed);
Expand Down
35 changes: 30 additions & 5 deletions src/RouteContext.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,32 @@
import { InjectionKey } from 'vue';
import { InjectionKey, VNodeChild } from 'vue';
import { createContext, useContext } from './hooks/context';
import { MenuDataItem } from './typings';
import { PureSettings } from './defaultSettings';
export interface Route {
path: string;
breadcrumbName: string;
children?: Omit<Route, 'children'>[];
}
export interface BreadcrumbProps {
prefixCls?: string;
routes?: Route[];
params?: any;
separator?: VNodeChild;
itemRender?: (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[];
onSelectedKeys: (key: string[]) => void;
onOpenKeys: (key: string[]) => void;
}

export interface RouteContextProps extends Partial<PureSettings> {
breadcrumb?: any;
menuData?: any[];
export interface RouteContextProps extends Partial<PureSettings>, MenuState {
breadcrumb?: BreadcrumbListReturn;
menuData?: MenuDataItem[];
isMobile?: boolean;
prefixCls?: string;
collapsed?: boolean;
Expand All @@ -14,11 +36,14 @@ export interface RouteContextProps extends Partial<PureSettings> {
hasFooterToolbar?: boolean;
hasFooter?: boolean;
setHasFooterToolbar?: (bool: boolean) => void;
/* 附加属性 */
[key: string]: any;
}

const routeContextInjectKey: InjectionKey<RouteContextProps> = Symbol();

export const createRouteContext = (context: RouteContextProps) =>
createContext<RouteContextProps>(context, routeContextInjectKey);

export const useRouteContext = () => useContext<RouteContextProps>(routeContextInjectKey);
export const useRouteContext = () =>
useContext<RouteContextProps>(routeContextInjectKey);
Loading

0 comments on commit e0039d2

Please sign in to comment.