Unverified Commit 5443a32c authored by Sendya's avatar Sendya

fix: tests, typo, ts

parent 25185efd
......@@ -2,26 +2,21 @@ import { Button, Card, Space, Switch } from 'ant-design-vue';
import 'ant-design-vue/dist/antd.less';
import { createApp, reactive } from 'vue';
import FooterToolbar, { FooterToolbarProps } from '../src/FooterToolbar';
import { createRouteContext } from '../src/RouteContext';
import { useRouteContext } from '../src/RouteContext';
const DemoComponent = {
setup() {
const routeContext = useRouteContext();
const state = reactive({
name: 'value',
hasFooterToolbar: 'unset' as string | boolean,
toolbarProps: {} as FooterToolbarProps,
name: '',
toolbarProps: {
extra: undefined,
renderContent: undefined,
} as FooterToolbarProps,
});
const setToolbarProps = tProps => {
state.toolbarProps = { ...state.toolbarProps, ...tProps };
};
const [ routeContext, RouteContextProvider ] = createRouteContext({
hasFooterToolbar: false,
setHasFooterToolbar: v => {
state.hasFooterToolbar = v;
routeContext.hasFooterToolbar = v;
},
});
console.log('routeContext', routeContext);
return () => (
<div class="components">
......@@ -43,17 +38,14 @@ const DemoComponent = {
checked={!!state.toolbarProps.extra}
onClick={() => {
state.name = new Date().getTime().toString();
const extra = !state.toolbarProps.extra ? (
state.toolbarProps.extra = !state.toolbarProps.extra ? (
<img
src="https://gw.alipayobjects.com/zos/antfincdn/PmY%24TNNDBI/logo.svg"
alt="logo"
width="32"
height="32"
/>
) : (
undefined
);
setToolbarProps({ extra });
) : undefined;
}}
/>
<Switch
......@@ -62,41 +54,25 @@ const DemoComponent = {
checked={!!state.toolbarProps.renderContent}
onClick={() => {
state.name = new Date().getTime().toString();
const renderContent = !state.toolbarProps.renderContent
state.toolbarProps.renderContent = !state.toolbarProps.renderContent
? () => 'home_toolbar'
: undefined;
setToolbarProps({ renderContent });
}}
/>
</Space>
<div style={{ margin: '12px 0' }}>
state
<pre>
{JSON.stringify(
{
name: state.name,
toolbarProps: Object.fromEntries(
Object.entries(state.toolbarProps).map(([k, v]) => [k, typeof v]),
),
},
null,
2,
)}
</pre>
<pre>{JSON.stringify(state, null, 2)}</pre>
routeContext:
<pre>{JSON.stringify(routeContext, null, 2)}</pre>
</div>
</Card>
<div>
<RouteContextProvider>
{routeContext.hasFooterToolbar && (
<FooterToolbar {...state.toolbarProps}>
<Button type="primary">
right
</Button>
</FooterToolbar>
)}
</RouteContextProvider>
{routeContext.hasFooterToolbar && (
<FooterToolbar {...state.toolbarProps}>
<Button type="primary">right</Button>
</FooterToolbar>
)}
</div>
</div>
);
......
import { createApp, defineComponent, reactive } from 'vue';
import { createApp, defineComponent } from 'vue';
import { Card } from 'ant-design-vue';
import { ContentWidth } from '../src/typings';
import GlobalFooter from '../src/GlobalFooter';
import 'ant-design-vue/dist/antd.less';
const GlobalFooterDemo = defineComponent({
setup () {
const state = reactive({
contentWidth: 'Fixed' as ContentWidth
})
setup() {
return () => (
<div class="components" style={{ background: 'rgb(240, 240, 240)', paddingBottom: '20px' }}>
<Card style={{ marginBottom: '24px', background: 'rgb(244,244,244)' }}>
......@@ -35,13 +29,17 @@ const GlobalFooterDemo = defineComponent({
title: '@Sendya',
href: 'https://www.github.com/sendya/',
blankTarget: true,
}
},
]}
copyright={(<a href="https://github.com/vueComponent" target="_blank">vueComponent</a>)}
copyright={
<a href="https://github.com/vueComponent" target="_blank">
vueComponent
</a>
}
/>
</div>
);
}
},
});
createApp(GlobalFooterDemo).mount('#__vue-content>div');
import { createApp, defineComponent, reactive } from 'vue';
import { Card, Space, Button } from 'ant-design-vue';
import { ContentWidth } from '../src/typings';
import GridContent from '../src/GridContent';
import GridContent, { GridContentProps } from '../src/GridContent';
import 'ant-design-vue/dist/antd.less';
const GridContentDemo = defineComponent({
setup () {
const state = reactive({
contentWidth: 'Fixed' as ContentWidth
})
setup() {
const state = reactive<GridContentProps>({
contentWidth: 'Fixed',
});
return () => (
<div class="components" style={{ background: 'rgb(240, 240, 240)', paddingBottom: '20px' }}>
......@@ -26,7 +26,10 @@ const GridContentDemo = defineComponent({
</Button>
</Space>
</Card>
<GridContent contentWidth={state.contentWidth} style={{ background: 'rgb(220, 220, 220)', padding: '22px' }}>
<GridContent
contentWidth={state.contentWidth}
style={{ background: 'rgb(220, 220, 220)', padding: '22px' }}
>
Content
<br />
...
......@@ -37,11 +40,12 @@ const GridContentDemo = defineComponent({
<br />
...
<br />
...<br />
...
<br />
</GridContent>
</div>
);
}
},
});
createApp(GridContentDemo).mount('#__vue-content>div');
......@@ -2,20 +2,12 @@ import { createApp, defineComponent, reactive } from 'vue';
import 'ant-design-vue/dist/antd.less';
import { Button, Descriptions, Space, Statistic, Tag, message } from 'ant-design-vue';
import { PageContainer } from '../src/PageContainer';
import { default as ProProvider } from '../src/ProProvider';
import { createRouteContext } from '../src/RouteContext';
import { LikeOutlined } from '@ant-design/icons-vue';
import './index.less';
const App = defineComponent({
name: 'App',
setup: function() {
const [ RouteContextProvider ] = createRouteContext({
hasSideMenu: true,
collapsed: true,
isMobile: false,
menuData: [],
});
setup: function () {
const routes = [
{
path: 'index',
......@@ -37,62 +29,60 @@ const App = defineComponent({
return () => (
<div class="demo-page-box" style="padding: 20px; background: #ccc;">
<ProProvider prefixCls={'ant-pro'} contentWidth={'Fixed'}>
<RouteContextProvider value={{}}>
<div style="background: #fff; height: 500px;">
<PageContainer
title="Title"
subTitle="This is a subtitle"
breadcrumb={{ routes }}
onBack={() => message.info('@back click')}
tags={['Tag 1', 'Tag 2'].map(tag => (<Tag color="blue">{tag}</Tag>))}
extra={[
<Button key="3">操作</Button>,
<Button key="2">操作</Button>,
<Button key="1" type="primary">
主操作
</Button>,
]}
content={
<Descriptions size="small" column={{ md: 2 }}>
<Descriptions.Item label="创建人">张三</Descriptions.Item>
<Descriptions.Item label="联系方式">
<a>421421</a>
</Descriptions.Item>
<Descriptions.Item label="创建时间">2017-01-10</Descriptions.Item>
<Descriptions.Item label="更新时间">2017-10-10</Descriptions.Item>
<Descriptions.Item label="备注">中国浙江省杭州市西湖区古翠路</Descriptions.Item>
</Descriptions>
}
extraContent={
<Space size={24}>
<Statistic title="Feedback" value={1128} prefix={<LikeOutlined />} />
<Statistic title="Unmerged" value={93} suffix="/ 100" />
</Space>
}
tabList={[
{ key: '1', tab: 'Details' },
{ key: '2', tab: 'Rule' },
]}
tabProps={{
type: 'card',
}}
tabActiveKey={state.tabActiveKey}
onTabChange={(key: string) => {
state.tabActiveKey = key;
}}
footer={[
<Button key="3">重置</Button>,
<Button key="2" type="primary">
提交
</Button>,
]}
>
<div>Page Content</div>
</PageContainer>
</div>
</RouteContextProvider>
</ProProvider>
<div style="background: #fff; height: 500px;">
<PageContainer
title="Title"
subTitle="This is a subtitle"
breadcrumb={{ routes }}
onBack={() => message.info('@back click')}
tags={['Tag 1', 'Tag 2'].map(tag => (
<Tag color="blue">{tag}</Tag>
))}
extra={[
<Button key="3">操作</Button>,
<Button key="2">操作</Button>,
<Button key="1" type="primary">
主操作
</Button>,
]}
content={
<Descriptions size="small" column={{ md: 2 }}>
<Descriptions.Item label="创建人">张三</Descriptions.Item>
<Descriptions.Item label="联系方式">
<a>421421</a>
</Descriptions.Item>
<Descriptions.Item label="创建时间">2017-01-10</Descriptions.Item>
<Descriptions.Item label="更新时间">2017-10-10</Descriptions.Item>
<Descriptions.Item label="备注">中国浙江省杭州市西湖区古翠路</Descriptions.Item>
</Descriptions>
}
extraContent={
<Space size={24}>
<Statistic title="Feedback" value={1128} prefix={<LikeOutlined />} />
<Statistic title="Unmerged" value={93} suffix="/ 100" />
</Space>
}
tabList={[
{ key: '1', tab: 'Details' },
{ key: '2', tab: 'Rule' },
]}
tabProps={{
type: 'card',
}}
tabActiveKey={state.tabActiveKey}
onTabChange={(key: string) => {
state.tabActiveKey = key;
}}
footer={[
<Button key="3">重置</Button>,
<Button key="2" type="primary">
提交
</Button>,
]}
>
<div>Page Content</div>
</PageContainer>
</div>
</div>
);
},
......
import { createApp, defineComponent, inject, reactive, toRefs } from 'vue';
import { Card, Space, Button } from 'ant-design-vue';
import { ContentWidth } from '../src/typings';
import { warning } from '../src/utils';
import { default as ProProvider, useProProvider } from '../src/ProProvider';
import 'ant-design-vue/dist/antd.less';
import GridContent from '../src/GridContent';
const trans = {
'render.test.i18n.hello': 'Hello My Friends',
};
const trans2 = {
'render.test.i18n.hello': 'Hello My Dear Friends',
};
const i18nRender = (t: string): string => {
return trans[t];
};
const i18nRender2 = (t: string): string => {
return trans2[t];
};
const ProProviderDemo = defineComponent({
setup() {
const state = reactive({
i18nRender,
contentWidth: 'Fixed' as ContentWidth,
});
return () => (
<>
<ProProvider i18n={state.i18nRender} contentWidth={state.contentWidth}>
<h2># BasicLayout</h2>
<div
class="components"
style={{ background: 'rgb(240, 240, 240)', paddingBottom: '20px' }}
>
<Card style={{ marginBottom: '24px', background: 'rgb(244,244,244)' }}>
<Space size="middle">
Change Current Config:
<Button
type="primary"
onClick={() => {
state.i18nRender = (Math.random() > 0.5 && i18nRender2) || i18nRender;
state.contentWidth = state.contentWidth === 'Fixed' ? 'Fluid' : 'Fixed';
}}
>
{state.contentWidth}
</Button>
</Space>
<div class="env">state.contentWidth: {JSON.stringify(state.contentWidth)}</div>
</Card>
<TestChildComponent style={{ background: 'rgb(220, 220, 220)', padding: '22px' }} />
</div>
</ProProvider>
</>
);
},
});
const TestChildComponent = defineComponent({
setup() {
const config = useProProvider();
const prefixCls = config.getPrefixCls('child-component');
return () => {
const { i18n, contentWidth } = config;
return (
<GridContent contentWidth={contentWidth}>
<div class={prefixCls}>
<p>TestChildComponent:</p>
<div>contentWidth: {contentWidth} , <a>Fixed: 1200px; Fluid: auto width;</a></div>
<p>{i18n('render.test.i18n.hello')}</p>
</div>
</GridContent>
);
};
},
});
const app = createApp(ProProviderDemo);
app.mount('#__vue-content>div');
import { createApp, defineComponent, ref, reactive, toRaw, onMounted } from 'vue';
import { Card, Space, Button } from 'ant-design-vue';
import { createRouteContext, useRouteContext, RouteContextProps } from '../src/RouteContext';
import 'ant-design-vue/dist/antd.less';
const DemoComponent = {
setup() {
const state = reactive({
name: 'value',
});
const context = reactive<RouteContextProps>({
menuData: [],
selectedKeys: [],
openKeys: [],
collapsed: false,
});
const [ RouteContextProvider ] = createRouteContext();
return () => (
<div class="components">
<h2># Template</h2>
<Card style={{ marginBottom: '24px', background: 'rgb(244,244,244)' }}>
<Space size="middle">
<Button
type="primary"
onClick={() => {
state.name = new Date().getTime().toString()
context.collapsed = !context.collapsed
context.menuData = [
{
path: `/dashboard/${state.name}`,
name: `${state.name}`,
meta: { title: `custom title - ${state.name}` },
}
]
}}
>
Change Value
</Button>
</Space>
<div style={{ margin: '12px 0' }}>
<p>state.name: { JSON.stringify(state.name) }</p>
routeContext:
<pre>{ JSON.stringify(context, null, 4) }</pre>
</div>
</Card>
<div class="demo" style="background: rgb(244,244,244);">
<RouteContextProvider value={context}>
<TestChildComponent />
</RouteContextProvider>
</div>
</div>
);
},
};
const TestChildComponent = defineComponent({
setup () {
const routeContext = useRouteContext();
console.log('TestChildComponent.routeContext', routeContext);
return () => {
const { menuData, collapsed } = routeContext
return (
<div class="test-child-component">
menuData:
<pre>
{JSON.stringify(menuData, null, 4)}
</pre>
<p>
collapsed: {collapsed.toString()}
</p>
</div>
)
}
}
});
const app = createApp(DemoComponent);
app.mount('#__vue-content>div');
.side-menu-demo {
.ant-pro-basicLayout {
.trigger {
padding: 0 24px;
font-size: 18px;
}
}
}
import { createApp, reactive } from 'vue';
import 'ant-design-vue/dist/antd.less';
import './side-menu.less';
import { Layout, Input, Space, Switch, message } from 'ant-design-vue';
import { menus } from './menus';
import { RouterLink } from './mock-router';
import { default as SiderMenuWrapper } from '../src/SiderMenu';
import { createRouteContext, RouteContextProps } from '../src/RouteContext';
import * as Icon from '@ant-design/icons-vue';
import { MenuTheme } from '../src/typings';
const DemoComponent = {
setup() {
const state = reactive<RouteContextProps>({
collapsed: false,
openKeys: ['/dashboard', '/form'],
setOpenKeys: (keys: string[]) => (state.openKeys = keys),
selectedKeys: ['/welcome'],
setSelectedKeys: (keys: string[]) => (state.selectedKeys = keys),
isMobile: false,
fixSiderbar: false,
fixedHeader: false,
menuData: [...menus],
sideWidth: 208,
hasSideMenu: true,
hasHeader: true,
hasFooterToolbar: false,
setHasFooterToolbar: (has: boolean) => (state.hasFooterToolbar = has),
})
const [ RouteContextProvider ] = createRouteContext();
const myState = reactive({
theme: 'light',
});
const handleCollapse = (collapsed: boolean) => {
state.collapsed = collapsed;
}
return () => (
<div class="components">
<h2># SideMenu</h2>
<div style="margin: 16px;">
<Space>
<Switch checked-children="dark" un-checked-children="light" checked={state.theme === 'dark'} onChange={() => { (myState.theme = myState.theme === 'dark' ? 'light' : 'dark')}} />
</Space>
</div>
<div class="demo" style="background: rgb(244,244,244); min-height: 400px;">
<div class="container side-menu-demo">
<RouteContextProvider value={state}>
<Layout class="ant-pro-basicLayout">
<SiderMenuWrapper
title={'Pro Layout'}
layout={'side'}
navTheme={myState.theme as MenuTheme}
isMobile={false}
collapsed={state.collapsed}
// openKeys={menuState.openKeys}
// selectedKeys={menuState.selectedKeys}
onCollapse={handleCollapse}
matchMenuKeys={[]}
contentWidth={'Fixed'}
primaryColor={'#1890ff'}
siderWidth={208}
menuExtraRender={(props) => !props.collapsed ? (
<div>
<Input.Search placeholder="Search.." style={{ width: '100%' }} onSearch={(value: string) => {
message.info(`Search click: ${value}`)
}} />
</div>
) : null}
// menuFooterRender={(props) => props.collapsed ? undefined : (
// <div style="color: #fff; padding: 8px 16px; overflow: hidden;">
// <span>自定义页脚</span>
// </div>
// )}
/>
<Layout>
<Layout.Header style="background: #fff; padding: 0; height: 48px; line-height: 48px;"></Layout.Header>
<Layout.Content
style={{
margin: '24px 16px',
padding: '24px',
background: '#fff',
minHeight: '280px',
}}
>
<div>Context</div>
</Layout.Content>
</Layout>
</Layout>
</RouteContextProvider>
</div>
</div>
</div>
);
},
};
const app = createApp(DemoComponent);
const filterIcons = ['default', 'createFromIconfontCN', 'getTwoToneColor', 'setTwoToneColor'];
Object.keys(Icon)
.filter(k => !filterIcons.includes(k))
.forEach(k => {
app.component(Icon[k].displayName, Icon[k]);
});
app.use(RouterLink).mount('#__vue-content>div');
import { createApp, reactive } from 'vue';
import { default as ProLayout, createRouteContext, RouteContextProps } from '../src/';
import { default as ProLayout, RouteContextProps } from '../src/';
import { RouterLink } from './mock-router';
import { menus } from './menus';
......@@ -7,46 +7,34 @@ import registerIcons from './_util/icons';
const SimpleDemo = {
setup() {
const [RouteContextProvider] = createRouteContext();
const appState = reactive<RouteContextProps>({
collapsed: false,
selectedKeys: [],
openKeys: [],
collapsed: true,
menuData: menus,
});
return () => (
<RouteContextProvider value={appState}>
<ProLayout
title="Pro Tests"
logo="https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg"
layout="side"
navTheme="light"
contentWidth="Fluid"
contentStyle={{ minHeight: '300px' }}
collapsed={appState.collapsed}
onCollapse={collapsed => {
appState.collapsed = collapsed;
}}
onSelect={(selectedKeys: string[] | false) => {
selectedKeys && (appState.selectedKeys = selectedKeys);
}}
onOpenKeys={(openKeys: string[] | false) => {
console.log('onOpenKeys', openKeys);
openKeys && (appState.openKeys = openKeys);
}}
footerRender={() => <div>custom-footer</div>}
v-slots={{
rightContentRender: props => (
<div class="custom-header-right-content">
<span>custom-right-content</span>
</div>
),
}}
>
<div>content</div>
</ProLayout>
</RouteContextProvider>
<ProLayout
title="Pro Tests"
logo="https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg"
onSelect={(selectedKeys: string[] | false) => {
selectedKeys && (appState.selectedKeys = selectedKeys);
}}
onOpenKeys={(openKeys: string[] | false) => {
console.log('onOpenKeys', openKeys);
openKeys && (appState.openKeys = openKeys);
}}
footerRender={() => <div>custom-footer</div>}
v-slots={{
rightContentRender: () => (
<div class="custom-header-right-content">
<span>custom-right-content</span>
</div>
),
}}
>
<div>content</div>
</ProLayout>
);
},
};
......
......@@ -2,28 +2,23 @@ import { computed, CSSProperties, reactive, unref, provide, defineComponent, toR
import 'ant-design-vue/es/layout/style';
import Layout from 'ant-design-vue/es/layout';
import { withInstall } from 'ant-design-vue/es/_util/type';
import { RouteContextProps } from './RouteContext';
import { getPrefixCls, RouteContextProps } from './RouteContext';
import { default as SiderMenuWrapper, SiderMenuWrapperProps } from './SiderMenu';
import { WrapContent } from './WrapContent';
import { default as Header, HeaderViewProps } from './Header';
import { VNodeType, CustomRender, WithFalse } from './typings';
import { getCustomRender, getMenuFirstChildren, PropRenderType, PropTypes } from './utils';
import { CustomRender, WithFalse } from './typings';
import { getCustomRender, PropRenderType, PropTypes } from './utils';
import omit from 'omit.js';
import useMediaQuery from './hooks/useMediaQuery';
import './BasicLayout.less';
export const defaultPrefixCls = 'ant-pro';
const getPrefixCls = (suffixCls?: string, customizePrefixCls?: string) => {
if (customizePrefixCls) return customizePrefixCls;
return suffixCls ? `${defaultPrefixCls}-${suffixCls}` : defaultPrefixCls;
};
export type BasicLayoutProps = SiderMenuWrapperProps &
HeaderViewProps & {
pure?: boolean;
/**
*@name logo url
*/
logo?: VNodeType;
logo?: CustomRender;
loading?: boolean;
......@@ -33,9 +28,9 @@ export type BasicLayoutProps = SiderMenuWrapperProps &
onCollapse?: (collapsed: boolean) => void;
footerRender?: WithFalse<(props: any /* FooterProps */) => VNodeType>;
footerRender?: WithFalse<(props: any /* FooterProps */) => CustomRender>;
headerRender?: WithFalse<(props: any /* HeaderProps */) => VNodeType>;
headerRender?: WithFalse<(props: any /* HeaderProps */) => CustomRender>;
colSize?: string;
/**
......@@ -109,7 +104,7 @@ const ProLayout = defineComponent({
rightContentRender: WithFalse<CustomRender>;
},
matchMenuKeys?: string[],
): VNodeType | null => {
): CustomRender | null => {
if (p.headerRender === false || p.pure) {
return null;
}
......@@ -161,7 +156,6 @@ const ProLayout = defineComponent({
const restProps = computed(() => omit(props, ['onCollapse', 'onOpenKeys', 'onSelect']));
provide('route-context', routeContext);
return () => (
<>
{pure.value ? (
......
import { computed, defineComponent, onBeforeUnmount, onMounted, PropType, VNodeChild } from 'vue';
import { RouteContextProps, useRouteContext } from '../RouteContext';
import { getMenuFirstChildren } from '../utils';
import './index.less';
import { computed, defineComponent, onBeforeUnmount, onMounted, PropType } from 'vue';
import { RouteContextProps, useRouteContext } from '../RouteContext';
import { getMenuFirstChildren, PropTypes } from '../utils';
import type { CustomRender } from '../typings';
export interface FooterToolbarProps {
extra?: VNodeChild | JSX.Element;
extra?: CustomRender | JSX.Element;
renderContent?: (
props: FooterToolbarProps & RouteContextProps & { leftWidth?: string },
dom: JSX.Element,
) => VNodeChild | JSX.Element;
dom: CustomRender | JSX.Element,
) => CustomRender | JSX.Element;
getContainer?: (triggerNode: HTMLElement) => HTMLElement | null;
prefixCls?: string;
}
const FooterToolbarProps = {
extra: { type: Object as PropType<VNodeChild> },
const footerToolbarProps = {
extra: {
type: Object as PropType<FooterToolbarProps['extra']>,
},
renderContent: {
type: Function as PropType<FooterToolbarProps['renderContent']>,
},
......@@ -26,7 +29,7 @@ const FooterToolbarProps = {
const FooterToolbar = defineComponent({
name: 'FooterToolbar',
props: FooterToolbarProps,
props: footerToolbarProps,
setup(props, ctx) {
const { slots } = ctx;
const routeContext = useRouteContext();
......
import { computed, CSSProperties, FunctionalComponent } from 'vue';
import { PureSettings } from '../defaultSettings';
import { VNodeType, MenuDataItem, WithFalse } from '../typings';
import { CustomRender, MenuDataItem, WithFalse } from '../typings';
import {
SiderMenuProps,
PrivateSiderMenuProps,
......@@ -12,15 +12,14 @@ import { TopNavHeader } from '../TopNavHeader';
import { clearMenuItem } from '../utils';
import type { HeaderViewProps } from '../Header';
import './index.less';
import { useProProvider } from '../ProProvider';
import { useRouteContext } from '../RouteContext';
export interface GlobalHeaderProps extends Partial<PureSettings> {
collapsed?: boolean;
onCollapse?: (collapsed: boolean) => void;
isMobile?: boolean;
logo?: VNodeType;
menuRender?: WithFalse<(props: HeaderViewProps, defaultDom: VNodeType) => VNodeType>;
rightContentRender?: WithFalse<(props: HeaderViewProps) => VNodeType>;
logo?: CustomRender;
menuRender?: WithFalse<(props: HeaderViewProps, defaultDom: CustomRender) => CustomRender>;
rightContentRender?: WithFalse<(props: HeaderViewProps) => CustomRender>;
className?: string;
prefixCls?: string;
menuData?: MenuDataItem[];
......@@ -33,7 +32,10 @@ export interface GlobalHeaderProps extends Partial<PureSettings> {
onSelect?: (selectedKeys: WithFalse<string[]>) => void;
}
const renderLogo = (menuHeaderRender: SiderMenuProps['menuHeaderRender'], logoDom: VNodeType) => {
const renderLogo = (
menuHeaderRender: SiderMenuProps['menuHeaderRender'],
logoDom: CustomRender,
) => {
if (menuHeaderRender === false) {
return null;
}
......@@ -63,7 +65,7 @@ export const GlobalHeader: FunctionalComponent<GlobalHeaderProps & PrivateSiderM
menuData,
prefixCls: customPrefixCls,
} = props;
const { getPrefixCls } = useProProvider();
const { getPrefixCls } = useRouteContext();
const prefixCls = customPrefixCls || getPrefixCls();
const baseClassName = computed(() => `${prefixCls}-global-header`);
const className = computed(() => {
......
import './GridContent.less';
import { FunctionalComponent, CSSProperties } from 'vue';
import { useProProvider } from '../ProProvider';
import { PureSettings } from '../defaultSettings';
import './GridContent.less';
import { useRouteContext } from '../RouteContext';
export interface GridContentProps {
contentWidth?: PureSettings['contentWidth'];
......@@ -10,7 +11,7 @@ export interface GridContentProps {
}
const GridContent: FunctionalComponent<GridContentProps> = (props, { slots }) => {
const { contentWidth, getPrefixCls } = useProProvider();
const { contentWidth, getPrefixCls } = useRouteContext();
const customPrefixCls = props.prefixCls || getPrefixCls();
const customContentWidth = props.contentWidth || contentWidth;
return (
......
......@@ -5,7 +5,7 @@ import Layout from 'ant-design-vue/es/layout';
import { GlobalHeader, GlobalHeaderProps } from './GlobalHeader';
import { TopNavHeader } from './TopNavHeader';
import { useRouteContext } from './RouteContext';
import { VNodeType, WithFalse } from './typings';
import { CustomRender, WithFalse } from './typings';
import { clearMenuItem } from './utils';
import './Header.less';
......@@ -18,11 +18,11 @@ interface HeaderViewState {
export type HeaderViewProps = GlobalHeaderProps & {
isMobile?: boolean;
collapsed?: boolean;
logo?: VNodeType;
logo?: CustomRender;
headerRender?: WithFalse<(props: HeaderViewProps, defaultDom: VNodeType) => VNodeType>;
headerTitleRender?: WithFalse<(props: HeaderViewProps, defaultDom: VNodeType) => VNodeType>;
headerContentRender?: WithFalse<(props: HeaderViewProps) => VNodeType>;
headerRender?: WithFalse<(props: HeaderViewProps, defaultDom: CustomRender) => CustomRender>;
headerTitleRender?: WithFalse<(props: HeaderViewProps, defaultDom: CustomRender) => CustomRender>;
headerContentRender?: WithFalse<(props: HeaderViewProps) => CustomRender>;
siderWidth?: number;
hasSiderMenu?: boolean;
};
......@@ -59,7 +59,7 @@ export const HeaderView = defineComponent({
inheritAttrs: false,
name: 'HeaderView',
props: headerProps,
setup(props: HeaderViewProps) {
setup(props /*Required<HeaderViewProps> */) {
const {
prefixCls,
isMobile,
......
......@@ -6,7 +6,6 @@ import { PageHeaderProps } from './interfaces/PageHeader';
import { AffixProps } from './interfaces/Affix';
/* replace antd ts define end */
import { useRouteContext, RouteContextProps } from '../RouteContext';
import { useProProvider } from '../ProProvider';
import 'ant-design-vue/es/affix/style';
import Affix from 'ant-design-vue/es/affix';
import 'ant-design-vue/es/page-header/style';
......@@ -167,9 +166,8 @@ const defaultPageHeaderRender = (
export const PageContainer: FunctionalComponent<PageContainerProps> = (props, { slots }) => {
const { loading, footer, affixProps, ghost, fixedHeader } = props; // toRefs(props);
const { getPrefixCls } = useProProvider();
const value = useRouteContext();
const { getPrefixCls } = value;
const prefixCls = props.prefixCls || getPrefixCls();
const prefixedClassName = `${prefixCls}-page-container`; // computed(() => `${prefixCls}-page-container`);
......
import {
App,
defineComponent,
InjectionKey,
PropType,
provide,
inject,
reactive,
readonly,
SetupContext,
toRefs,
} from 'vue';
import { ContentWidth } from '../typings';
export const defaultPrefixCls = 'ant-pro';
export interface ProProviderData {
getPrefixCls: (suffixCls?: string, customizePrefixCls?: string) => string;
i18n: (t: string) => string;
contentWidth: ContentWidth;
}
export const defaultProProviderProps: ProProviderData = {
getPrefixCls: (suffixCls?: string, customizePrefixCls?: string) => {
if (customizePrefixCls) return customizePrefixCls;
return suffixCls ? `${defaultPrefixCls}-${suffixCls}` : defaultPrefixCls;
},
i18n: (t: string): string => t,
contentWidth: 'Fluid',
};
export const injectProConfigKey: InjectionKey<ProProviderData> = Symbol();
const ProProvider = defineComponent({
name: 'ProProvider',
props: {
prefixCls: {
type: String as PropType<string>,
default: 'ant-pro',
},
contentWidth: {
type: String as PropType<ContentWidth>,
default: 'Fluid',
},
i18n: {
type: Function as PropType<(t: string) => string>,
default: (t: string): string => t,
},
},
setup(props, { slots }: SetupContext) {
const { prefixCls, i18n, contentWidth } = toRefs(props);
const getPrefixCls = (suffixCls?: string, customizePrefixCls?: string): string => {
if (customizePrefixCls) return customizePrefixCls;
return suffixCls ? `${prefixCls.value}-${suffixCls}` : prefixCls.value;
};
const context = reactive({
i18n,
contentWidth,
getPrefixCls,
});
provide(injectProConfigKey, readonly(context));
return () => slots.default?.();
},
});
ProProvider.install = function (app: App) {
app.component(ProProvider.name, ProProvider);
};
export const useProProvider = (): ProProviderData => {
return inject(injectProConfigKey, defaultProProviderProps);
};
export default ProProvider;
import { InjectionKey, VNodeChild } from 'vue';
import { InjectionKey, reactive, VNodeChild } from 'vue';
import { createContext, useContext } from './hooks/context';
import { MenuDataItem } from './typings';
import { PureSettings } from './defaultSettings';
......@@ -34,8 +34,8 @@ export interface MenuState {
}
export interface RouteContextProps extends Partial<PureSettings>, MenuState {
getPrefixCls: (suffixCls?: string, customizePrefixCls?: string) => string;
i18n: (t: string) => string;
getPrefixCls?: (suffixCls?: string, customizePrefixCls?: string) => string;
i18n?: (t: string) => string;
breadcrumb?: BreadcrumbListReturn;
menuData: MenuDataItem[];
......@@ -52,13 +52,32 @@ export interface RouteContextProps extends Partial<PureSettings>, MenuState {
[key: string]: any;
}
export const defaultPrefixCls = 'ant-pro';
export const getPrefixCls = (suffixCls?: string, customizePrefixCls?: string) => {
if (customizePrefixCls) return customizePrefixCls;
return suffixCls ? `${defaultPrefixCls}-${suffixCls}` : defaultPrefixCls;
};
// set default context
const defaultRouteContext = reactive({
getPrefixCls,
i18n: (t: string) => t,
contentWidth: 'Fluid',
hasFooterToolbar: false,
setHasFooterToolbar: (bool: boolean) => (defaultRouteContext.hasFooterToolbar = bool),
});
const routeContextInjectKey: InjectionKey<RouteContextProps> = Symbol('route-context');
export const createRouteContext = () =>
createContext<RouteContextProps>(routeContextInjectKey, 'RouteContext.Provider');
export const useRouteContext = () =>
useContext<RouteContextProps>('route-context' /* routeContextInjectKey */);
useContext<Required<RouteContextProps>>(
'route-context' /* routeContextInjectKey */,
defaultRouteContext,
);
const Provider = createRouteContext();
......
......@@ -127,7 +127,7 @@ class MenuUtil {
if (Array.isArray(item.children) && item.children.length > 0 && !item?.meta?.hideInMenu) {
const { prefixCls, i18n } = this.props;
const menuTitle = (i18n && i18n(item.meta?.title)) || item.meta?.title;
const defaultTitle = item?.meta.icon ? (
const defaultTitle = item.meta?.icon ? (
<span class={`${prefixCls}-menu-item`}>
{!isChildren && <LazyIcon icon={item.meta.icon} />}
<span class={`${prefixCls}-menu-item-title`}>{menuTitle}</span>
......@@ -157,7 +157,7 @@ class MenuUtil {
getMenuItem = (item: MenuDataItem, isChildren: boolean) => {
const meta = Object.assign({}, item.meta);
const target = meta.target || null;
const target = (meta.target || null) as string | null;
const hasUrl = isUrl(item.path);
const CustomTag: any = resolveComponent((target && 'a') || 'router-link');
const props = { to: { name: item.name } };
......@@ -165,7 +165,7 @@ class MenuUtil {
const { prefixCls, i18n } = this.props;
const menuTitle = (i18n && i18n(item.meta?.title)) || item.meta?.title;
const defaultTitle = item?.meta.icon ? (
const defaultTitle = item.meta?.icon ? (
<CustomTag {...attrs} {...props}>
<span class={`${prefixCls}-menu-item`}>
{!isChildren && <LazyIcon icon={item.meta.icon} />}
......@@ -198,7 +198,7 @@ export default defineComponent({
const isInline = computed(() => mode.value === 'inline');
const menuUtil = new MenuUtil(props);
const handleOpenChange: OpenEventHandler = (openKeys: string[]): void => {
const handleOpenChange = (openKeys: string[]): void => {
emit('update:openKeys', openKeys);
};
const handleSelect = (params: {
......
......@@ -4,7 +4,7 @@ import Layout from 'ant-design-vue/es/layout';
import 'ant-design-vue/es/menu/style';
import Menu from 'ant-design-vue/es/menu';
import BaseMenu, { BaseMenuProps } from './BaseMenu';
import { WithFalse, VNodeType } from '../typings';
import { WithFalse, CustomRender } from '../typings';
import { SiderProps } from './typings';
import { MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons-vue';
import { useRouteContext } from '../RouteContext';
......@@ -19,16 +19,16 @@ export type PrivateSiderMenuProps = {
export interface SiderMenuProps
extends Pick<BaseMenuProps, Exclude<keyof BaseMenuProps, ['onCollapse']>> {
logo?: VNodeType;
logo?: CustomRender;
siderWidth?: number;
collapsedWidth?: number;
menuHeaderRender?: WithFalse<
(logo: VNodeType, title: VNodeType, props?: SiderMenuProps) => VNodeType
(logo: CustomRender, title: CustomRender, props?: SiderMenuProps) => CustomRender
>;
menuFooterRender?: WithFalse<(props?: SiderMenuProps) => VNodeType>;
menuContentRender?: WithFalse<(props: SiderMenuProps, defaultDom: VNodeType) => VNodeType>;
menuExtraRender?: WithFalse<(props: SiderMenuProps) => VNodeType>;
collapsedButtonRender?: WithFalse<(collapsed?: boolean) => VNodeType>;
menuFooterRender?: WithFalse<(props?: SiderMenuProps) => CustomRender>;
menuContentRender?: WithFalse<(props: SiderMenuProps, defaultDom: CustomRender) => CustomRender>;
menuExtraRender?: WithFalse<(props: SiderMenuProps) => CustomRender>;
collapsedButtonRender?: WithFalse<(collapsed?: boolean) => CustomRender>;
breakpoint?: SiderProps['breakpoint'] | false;
onMenuHeaderClick?: (e: MouseEvent) => void;
fixed?: boolean;
......@@ -38,7 +38,10 @@ export interface SiderMenuProps
onSelect?: (selectedKeys: WithFalse<string[]>) => void;
}
export const defaultRenderLogo = (logo: VNodeType): VNodeType => {
export const defaultRenderLogo = (logo?: CustomRender): CustomRender => {
if (!logo) {
return null;
}
if (typeof logo === 'string') {
return <img src={logo} alt="logo" />;
}
......@@ -51,7 +54,7 @@ export const defaultRenderLogo = (logo: VNodeType): VNodeType => {
export const defaultRenderLogoAndTitle = (
props: SiderMenuProps,
renderKey: string | undefined = 'menuHeaderRender',
): VNodeType | null => {
): CustomRender | null => {
const {
logo = 'https://gw.alipayobjects.com/zos/antfincdn/PmY%24TNNDBI/logo.svg',
title,
......@@ -79,7 +82,7 @@ export const defaultRenderLogoAndTitle = (
);
};
export const defaultRenderCollapsedButton = (collapsed?: boolean): VNodeType =>
export const defaultRenderCollapsedButton = (collapsed?: boolean): CustomRender =>
collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />;
const SiderMenu: FunctionalComponent<SiderMenuProps> = (props: SiderMenuProps) => {
......
......@@ -32,7 +32,7 @@ export const createContext = <T>(
},
});
return ContextProvider;
return ContextProvider as any;
};
export const useContext = <T>(
......
import { ref } from 'vue';
export const MediaQueryEnum = {
export const MediaQueryEnum /* : {
[key: string]: {
matchMedia: string;
minWidth?: number;
maxWidth?: number;
};
} */ = {
xs: {
maxWidth: 575,
matchMedia: '(max-width: 575px)',
......@@ -60,7 +66,7 @@ const useMedia = () => {
const colSpan = ref<string>(getScreenClassName());
Object.keys(MediaQueryEnum).forEach(key => {
const { matchMedia } = MediaQueryEnum[key];
const { matchMedia } = MediaQueryEnum[key as MediaQueryKey];
const query = window.matchMedia(matchMedia);
if (query.matches) {
colSpan.value = key;
......
import { VNode } from 'vue';
// define global types
export type VNodeType = WithFalse<string | VNode | JSX.Element>;
import type { Slot, VNode } from 'vue';
export type MenuTheme = 'dark' | 'light';
......@@ -67,6 +64,14 @@ export interface MenuDataItem {
export type WithFalse<T> = T | false;
export type CustomRender = (...args: any[]) => VNode | VNode[];
export type CustomRender =
| Slot
| VNode
| ((...props: any) => Slot)
| ((...props: any) => VNode)
| ((...args: any[]) => VNode)
| VNode[]
| JSX.Element
| null;
export type FormatMessage = (message: string) => string;
export type FormatMessage = (message?: string) => string;
......@@ -12,7 +12,11 @@ export function getComponentOrSlot(props: any, slots: Slots, name: string): VNod
return typeof comp === 'function' ? comp() : (comp && (comp as VNodeChild)) || false;
}
export function getCustomRender(props: any, slots: Slots, name: string): CustomRender | false {
export function getCustomRender(
props: any,
slots: Slots,
name: string,
): any | /* | CustomRender */ false | null {
const propRender = props[name];
if (propRender === false) {
return false;
......@@ -21,7 +25,7 @@ export function getCustomRender(props: any, slots: Slots, name: string): CustomR
return propRender;
}
const slotVNode = slots[name || 'default'];
return slotVNode;
return slotVNode || null;
}
export function warn(valid: boolean, message: string) {
......@@ -70,7 +74,7 @@ export function flatMap(menusData: MenuDataItem[]): MenuDataItem[] {
delete finalItem.children;
return finalItem;
})
.filter(item => item);
.filter(item => item) as any[];
}
export function getMenuFirstChildren(menus: MenuDataItem[], key?: string) {
......
......@@ -36,7 +36,7 @@ exports[`BasicLayout 🥩 base use 1`] = `
<div class="custom-header-right-content"><span>custom-right-content</span></div>
</div>
</header>
<main class="ant-layout-content ant-pro-basicLayout-content ant-pro-basicLayout-has-header" style="min-height: 300px;">
<main class="ant-layout-content ant-pro-basicLayout-content ant-pro-basicLayout-has-header" ischildrenlayout="false" style="min-height: 300px;">
<div>content</div>
</main>
<div>custom-footer</div>
......
......@@ -38,7 +38,7 @@ exports[`PageContainer 🥩 base use 1`] = `
</div>
</div>
</div>
<div class="ant-pro-footer-bar">
<div class="ant-pro-footer-bar" style="width: 100%;">
<div class="ant-pro-footer-bar-left">
<!---->
</div>
......
import { mount, shallowMount } from '@vue/test-utils';
// import BasicLayout from '../src/BasicLayout';
import ProProvider from '../src/ProProvider';
import { mount } from '@vue/test-utils';
import GlobalFooter from '../src/GlobalFooter';
import { link } from 'fs';
const testLinks = [
{
......@@ -22,18 +19,23 @@ const testLinks = [
title: '@Sendya',
href: 'https://www.github.com/sendya/',
blankTarget: true,
}
]
},
];
describe('GlobalFooter', () => {
it('🥩 base use', () => {
const wrapper = mount({
render() {
return (<GlobalFooter
links={testLinks}
copyright={(<a href="https://github.com/vueComponent" target="_blank">vueComponent</a>)}
/>);
return (
<GlobalFooter
links={testLinks}
copyright={
<a href="https://github.com/vueComponent" target="_blank">
vueComponent
</a>
}
/>
);
},
});
expect(wrapper.html()).toMatchSnapshot();
......@@ -43,13 +45,17 @@ describe('GlobalFooter', () => {
const wrapper = mount({
render() {
return (
<GlobalFooter links={[{
key: '1',
title: 'Pro Layout',
href: 'https://www.github.com/vueComponent/pro-layout',
blankTarget: true,
}]} />
)
<GlobalFooter
links={[
{
key: '1',
title: 'Pro Layout',
href: 'https://www.github.com/vueComponent/pro-layout',
blankTarget: true,
},
]}
/>
);
},
});
const links = wrapper.findAll('.ant-pro-global-footer-links a');
......@@ -59,12 +65,12 @@ describe('GlobalFooter', () => {
it('😄 custom copyright', () => {
const wrapper = mount({
render() {
return (
<GlobalFooter copyright={(<a href="#copyright">vueComponent</a>)} />
);
return <GlobalFooter copyright={<a href="#copyright">vueComponent</a>} />;
},
});
expect(wrapper.find('.ant-pro-global-footer-copyright a').attributes()).toHaveProperty('href', '#copyright');
expect(wrapper.find('.ant-pro-global-footer-copyright a').attributes()).toHaveProperty(
'href',
'#copyright',
);
});
});
......@@ -2,18 +2,15 @@ import { mount } from '@vue/test-utils';
import GridContent from '../src/GridContent';
describe('GridContent', () => {
it('shoul render with empty children', () => {
const wrapper = mount(GridContent, { });
const wrapper = mount(GridContent, {});
expect(wrapper.html()).toMatchSnapshot();
});
it('shoul render with prop Fixed', () => {
const wrapper = mount({
setup() {
return () => (
<GridContent contentWidth="Fixed"></GridContent>
);
return () => <GridContent contentWidth="Fixed"></GridContent>;
},
});
expect(wrapper.classes().indexOf('wide') > -1).toBe(true);
......@@ -29,6 +26,8 @@ describe('GridContent', () => {
);
},
});
expect(wrapper.find('.ant-pro-grid-content-children').element.innerHTML === '<div>children</div>').toBe(true);
expect(
wrapper.find('.ant-pro-grid-content-children').element.innerHTML === '<div>children</div>',
).toBe(true);
});
});
import './_utils/mock-func';
import { mount, shallowMount } from '@vue/test-utils';
import { reactive } from 'vue';
import BasicLayout from '../src/BasicLayout';
import { mount } from '@vue/test-utils';
import { PropType } from 'vue';
import BasicLayout, { BasicLayoutProps } from '../src/BasicLayout';
const title = 'Pro Tests';
const logoSrc = 'https://alicdn.antdv.com/v2/assets/logo.1ef800a8.svg';
......@@ -54,11 +54,11 @@ describe('BasicLayout', () => {
const wrapper = mount({
props: {
theme: {
type: String,
type: String as PropType<BasicLayoutProps['navTheme']>,
default: 'light',
},
layout: {
type: String,
type: String as PropType<BasicLayoutProps['layout']>,
default: 'mix',
},
},
......
......@@ -4,7 +4,6 @@ import { Tag, Button } from 'ant-design-vue';
import { sleep } from './utils';
describe('PageContainer', () => {
const routes = [
{
path: 'index',
......@@ -21,20 +20,24 @@ describe('PageContainer', () => {
];
const props = {
title: 'Title',
subTitle: "This is a subtitle",
subTitle: 'This is a subtitle',
breadcrumb: { routes },
onBack: () => {},
tags: ['Tag 1', 'Tag 2'].map(tag => (<Tag color="blue">{tag}</Tag>)),
extra: [<Button key="1" type="primary">主操作</Button>,],
content: (<div>content</div>),
extraContent: (<div>extraContent</div>),
tags: ['Tag 1', 'Tag 2'].map(tag => <Tag color="blue">{tag}</Tag>),
extra: [
<Button key="1" type="primary">
主操作
</Button>,
],
content: <div>content</div>,
extraContent: <div>extraContent</div>,
footer: [
<Button key="3">重置</Button>,
<Button key="2" type="primary">
提交
</Button>,
],
}
};
it('🥩 base use', () => {
const wrapper = mount({
......@@ -52,12 +55,7 @@ describe('PageContainer', () => {
it('😄 custom title,subTitle', () => {
const wrapper = mount({
render() {
return (
<PageContainer
title="Title"
subTitle="SubTitle"
/>
);
return <PageContainer title="Title" subTitle="SubTitle" />;
},
});
......@@ -89,7 +87,9 @@ describe('PageContainer', () => {
render() {
return (
<PageContainer
tags={['Tag 1', 'Tag 2'].map(tag => (<Tag color="blue">{tag}</Tag>))}
tags={['Tag 1', 'Tag 2'].map(tag => (
<Tag color="blue">{tag}</Tag>
))}
/>
);
},
......
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