refactor: breadcrumb button by ts

pull/2992/head
tanjinzhou 2020-10-13 18:04:02 +08:00
parent 1503364d6b
commit d7426e11d4
16 changed files with 104 additions and 78 deletions

View File

@ -1,10 +1,17 @@
import { inject, cloneVNode } from 'vue'; import { inject, cloneVNode, defineComponent, PropType } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { filterEmpty, getComponent, getSlot } from '../_util/props-util'; import { filterEmpty, getComponent, getSlot } from '../_util/props-util';
import warning from '../_util/warning'; import warning from '../_util/warning';
import { defaultConfigProvider } from '../config-provider'; import { defaultConfigProvider } from '../config-provider';
import BreadcrumbItem from './BreadcrumbItem'; import BreadcrumbItem from './BreadcrumbItem';
import Menu from '../menu'; import Menu from '../menu';
import { Omit, VueNode } from '../_util/type';
export interface Route {
path: string;
breadcrumbName: string;
children?: Omit<Route, 'children'>[];
}
const Route = PropTypes.shape({ const Route = PropTypes.shape({
path: PropTypes.string, path: PropTypes.string,
@ -14,13 +21,20 @@ const Route = PropTypes.shape({
const BreadcrumbProps = { const BreadcrumbProps = {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
routes: PropTypes.arrayOf(Route), routes: {type: Array as PropType<Route[]>},
params: PropTypes.any, params: PropTypes.any,
separator: PropTypes.any, separator: PropTypes.VNodeChild,
itemRender: PropTypes.func, itemRender: {
type: Function as PropType<(
route: Route,
params: any,
routes: Array<Route>,
paths: Array<string>,
) => VueNode>
},
}; };
function getBreadcrumbName(route, params) { function getBreadcrumbName(route: Route, params: any) {
if (!route.breadcrumbName) { if (!route.breadcrumbName) {
return null; return null;
} }
@ -31,8 +45,14 @@ function getBreadcrumbName(route, params) {
); );
return name; return name;
} }
function defaultItemRender(opt: {route: Route, params: any, routes: Route[], paths: string[]}): VueNode {
const { route, params, routes, paths } = opt
const isLastItem = routes.indexOf(route) === routes.length - 1;
const name = getBreadcrumbName(route, params);
return isLastItem ? <span>{name}</span> : <a href={`#/${paths.join('/')}`}>{name}</a>;
}
export default { export default defineComponent({
name: 'ABreadcrumb', name: 'ABreadcrumb',
props: BreadcrumbProps, props: BreadcrumbProps,
setup() { setup() {
@ -41,12 +61,7 @@ export default {
}; };
}, },
methods: { methods: {
defaultItemRender({ route, params, routes, paths }) { getPath(path: string, params: any) {
const isLastItem = routes.indexOf(route) === routes.length - 1;
const name = getBreadcrumbName(route, params);
return isLastItem ? <span>{name}</span> : <a href={`#/${paths.join('/')}`}>{name}</a>;
},
getPath(path, params) {
path = (path || '').replace(/^\//, ''); path = (path || '').replace(/^\//, '');
Object.keys(params).forEach(key => { Object.keys(params).forEach(key => {
path = path.replace(`:${key}`, params[key]); path = path.replace(`:${key}`, params[key]);
@ -54,7 +69,7 @@ export default {
return path; return path;
}, },
addChildPath(paths, childPath, params) { addChildPath(paths: string[], childPath: string = '', params: any) {
const originalPaths = [...paths]; const originalPaths = [...paths];
const path = this.getPath(childPath, params); const path = this.getPath(childPath, params);
if (path) { if (path) {
@ -63,9 +78,9 @@ export default {
return originalPaths; return originalPaths;
}, },
genForRoutes({ routes = [], params = {}, separator, itemRender = this.defaultItemRender }) { genForRoutes({ routes = [], params = {}, separator, itemRender = defaultItemRender }: any) {
const paths = []; const paths = [];
return routes.map(route => { return routes.map((route: Route) => {
const path = this.getPath(route.path, params); const path = this.getPath(route.path, params);
if (path) { if (path) {
@ -104,14 +119,14 @@ export default {
}, },
}, },
render() { render() {
let crumbs; let crumbs: VueNode[];
const { prefixCls: customizePrefixCls, routes, params = {}, $slots } = this; const { prefixCls: customizePrefixCls, routes, params = {}, $slots } = this;
const getPrefixCls = this.configProvider.getPrefixCls; const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls); const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls);
const children = filterEmpty(getSlot(this)); const children = filterEmpty(getSlot(this));
const separator = getComponent(this, 'separator'); const separator = getComponent(this, 'separator');
const itemRender = this.itemRender || $slots.itemRender || this.defaultItemRender; const itemRender = this.itemRender || $slots.itemRender || defaultItemRender;
if (routes && routes.length > 0) { if (routes && routes.length > 0) {
// generated by route // generated by route
crumbs = this.genForRoutes({ crumbs = this.genForRoutes({
@ -133,4 +148,4 @@ export default {
} }
return <div class={prefixCls}>{crumbs}</div>; return <div class={prefixCls}>{crumbs}</div>;
}, },
}; });

View File

@ -1,18 +1,18 @@
import { inject } from 'vue'; import { defineComponent, inject } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { hasProp, getComponent, getSlot } from '../_util/props-util'; import { hasProp, getComponent, getSlot } from '../_util/props-util';
import { defaultConfigProvider } from '../config-provider'; import { defaultConfigProvider } from '../config-provider';
import DropDown from '../dropdown/dropdown'; import DropDown from '../dropdown/dropdown';
import DownOutlined from '@ant-design/icons-vue/DownOutlined'; import DownOutlined from '@ant-design/icons-vue/DownOutlined';
export default { export default defineComponent({
name: 'ABreadcrumbItem', name: 'ABreadcrumbItem',
__ANT_BREADCRUMB_ITEM: true, __ANT_BREADCRUMB_ITEM: true,
props: { props: {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
href: PropTypes.string, href: PropTypes.string,
separator: PropTypes.any.def('/'), separator: PropTypes.VNodeChild.def('/'),
overlay: PropTypes.any, overlay: PropTypes.VNodeChild,
}, },
setup() { setup() {
return { return {
@ -24,7 +24,7 @@ export default {
* if overlay is have * if overlay is have
* Wrap a DropDown * Wrap a DropDown
*/ */
renderBreadcrumbNode(breadcrumbItem, prefixCls) { renderBreadcrumbNode(breadcrumbItem: any, prefixCls: string) {
const overlay = getComponent(this, 'overlay'); const overlay = getComponent(this, 'overlay');
if (overlay) { if (overlay) {
return ( return (
@ -65,4 +65,4 @@ export default {
} }
return null; return null;
}, },
}; });

View File

@ -1,9 +1,9 @@
import { inject } from 'vue'; import { defineComponent, inject } from 'vue';
import { defaultConfigProvider } from '../config-provider'; import { defaultConfigProvider } from '../config-provider';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { getSlot } from '../_util/props-util'; import { getSlot } from '../_util/props-util';
export default { export default defineComponent({
name: 'ABreadcrumbSeparator', name: 'ABreadcrumbSeparator',
__ANT_BREADCRUMB_SEPARATOR: true, __ANT_BREADCRUMB_SEPARATOR: true,
props: { props: {
@ -22,4 +22,4 @@ export default {
const children = getSlot(this); const children = getSlot(this);
return <span class={`${prefixCls}-separator`}>{children || '/'}</span>; return <span class={`${prefixCls}-separator`}>{children || '/'}</span>;
}, },
}; });

View File

@ -1,3 +1,4 @@
import { App } from 'vue';
import Breadcrumb from './Breadcrumb'; import Breadcrumb from './Breadcrumb';
import BreadcrumbItem from './BreadcrumbItem'; import BreadcrumbItem from './BreadcrumbItem';
import BreadcrumbSeparator from './BreadcrumbSeparator'; import BreadcrumbSeparator from './BreadcrumbSeparator';
@ -6,7 +7,7 @@ Breadcrumb.Item = BreadcrumbItem;
Breadcrumb.Separator = BreadcrumbSeparator; Breadcrumb.Separator = BreadcrumbSeparator;
/* istanbul ignore next */ /* istanbul ignore next */
Breadcrumb.install = function(app) { Breadcrumb.install = function(app: App) {
app.component(Breadcrumb.name, Breadcrumb); app.component(Breadcrumb.name, Breadcrumb);
app.component(BreadcrumbItem.name, BreadcrumbItem); app.component(BreadcrumbItem.name, BreadcrumbItem);
app.component(BreadcrumbSeparator.name, BreadcrumbSeparator); app.component(BreadcrumbSeparator.name, BreadcrumbSeparator);

View File

@ -1,18 +1,15 @@
import { inject } from 'vue'; import { defineComponent, inject } from 'vue';
import { filterEmpty, getSlot } from '../_util/props-util'; import { filterEmpty, getSlot } from '../_util/props-util';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { defaultConfigProvider } from '../config-provider'; import { defaultConfigProvider } from '../config-provider';
import { tuple } from '../_util/type';
const ButtonGroupProps = { const ButtonGroupProps = {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
size: { size: PropTypes.oneOf(tuple('small', 'large', 'default')),
validator(value) {
return ['small', 'large', 'default'].includes(value);
},
},
}; };
export { ButtonGroupProps }; export { ButtonGroupProps };
export default { export default defineComponent({
name: 'AButtonGroup', name: 'AButtonGroup',
props: ButtonGroupProps, props: ButtonGroupProps,
setup() { setup() {
@ -53,4 +50,4 @@ export default {
}; };
return <div class={classes}>{filterEmpty(getSlot(this))}</div>; return <div class={classes}>{filterEmpty(getSlot(this))}</div>;
}, },
}; });

View File

@ -1,4 +1,4 @@
import { inject, Text } from 'vue'; import { defineComponent, inject, Text, VNode } from 'vue';
import Wave from '../_util/wave'; import Wave from '../_util/wave';
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined'; import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined';
import buttonTypes from './buttonTypes'; import buttonTypes from './buttonTypes';
@ -8,7 +8,7 @@ import { defaultConfigProvider } from '../config-provider';
const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/; const rxTwoCNChar = /^[\u4e00-\u9fa5]{2}$/;
const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar); const isTwoCNChar = rxTwoCNChar.test.bind(rxTwoCNChar);
const props = buttonTypes(); const props = buttonTypes();
export default { export default defineComponent({
name: 'AButton', name: 'AButton',
inheritAttrs: false, inheritAttrs: false,
__ANT_BUTTON: true, __ANT_BUTTON: true,
@ -16,11 +16,12 @@ export default {
setup() { setup() {
return { return {
configProvider: inject('configProvider', defaultConfigProvider), configProvider: inject('configProvider', defaultConfigProvider),
children: [],
iconCom: undefined,
delayTimeout: undefined
}; };
}, },
data() { data() {
this.children = [];
this.iconCom = undefined;
return { return {
sizeMap: { sizeMap: {
large: 'lg', large: 'lg',
@ -67,7 +68,7 @@ export default {
ghost, ghost,
block, block,
$attrs, $attrs,
} = this; } = this
const getPrefixCls = this.configProvider.getPrefixCls; const getPrefixCls = this.configProvider.getPrefixCls;
const prefixCls = getPrefixCls('btn', customizePrefixCls); const prefixCls = getPrefixCls('btn', customizePrefixCls);
const autoInsertSpace = this.configProvider.autoInsertSpaceInButton !== false; const autoInsertSpace = this.configProvider.autoInsertSpaceInButton !== false;
@ -87,7 +88,7 @@ export default {
} }
const iconType = sLoading ? 'loading' : this.iconCom; const iconType = sLoading ? 'loading' : this.iconCom;
return { return {
[$attrs.class]: $attrs.class, [$attrs.class as string]: $attrs.class,
[`${prefixCls}`]: true, [`${prefixCls}`]: true,
[`${prefixCls}-${type}`]: type, [`${prefixCls}-${type}`]: type,
[`${prefixCls}-${shape}`]: shape, [`${prefixCls}-${shape}`]: shape,
@ -101,7 +102,7 @@ export default {
}, },
fixTwoCNChar() { fixTwoCNChar() {
// Fix for HOC usage like <FormatMessage /> // Fix for HOC usage like <FormatMessage />
const node = this.$refs.buttonNode; const node = this.$refs.buttonNode as HTMLElement;
if (!node) { if (!node) {
return; return;
} }
@ -114,17 +115,17 @@ export default {
this.hasTwoCNChar = false; this.hasTwoCNChar = false;
} }
}, },
handleClick(event) { handleClick(event: Event) {
const { sLoading } = this.$data; const { sLoading } = this.$data;
if (sLoading) { if (sLoading) {
return; return;
} }
this.$emit('click', event); this.$emit('click', event);
}, },
insertSpace(child, needInserted) { insertSpace(child: VNode, needInserted: boolean) {
const SPACE = needInserted ? ' ' : ''; const SPACE = needInserted ? ' ' : '';
if (child.type === Text) { if (child.type === Text) {
let text = child.children.trim(); let text = (child.children as string).trim();
if (isTwoCNChar(text)) { if (isTwoCNChar(text)) {
text = text.split('').join(SPACE); text = text.split('').join(SPACE);
} }
@ -179,4 +180,4 @@ export default {
return <Wave ref="wave">{buttonNode}</Wave>; return <Wave ref="wave">{buttonNode}</Wave>;
}, },
}; });

View File

@ -1,15 +0,0 @@
import PropTypes, { withUndefined } from '../_util/vue-types';
export default () => ({
prefixCls: PropTypes.string,
type: PropTypes.string,
htmlType: PropTypes.oneOf(['button', 'submit', 'reset']).def('button'),
// icon: PropTypes.string,
shape: PropTypes.oneOf(['circle', 'circle-outline', 'round']),
size: PropTypes.oneOf(['small', 'large', 'default']).def('default'),
loading: withUndefined(PropTypes.oneOfType([PropTypes.looseBool, PropTypes.object])),
disabled: PropTypes.looseBool,
ghost: PropTypes.looseBool,
block: PropTypes.looseBool,
icon: PropTypes.any,
onClick: PropTypes.func,
});

View File

@ -0,0 +1,26 @@
import { tuple } from '../_util/type';
import PropTypes, { withUndefined } from '../_util/vue-types';
const ButtonTypes = tuple('default', 'primary', 'ghost', 'dashed', 'danger', 'link');
export type ButtonType = typeof ButtonTypes[number];
const ButtonShapes = tuple('circle', 'circle-outline', 'round');
export type ButtonShape = typeof ButtonShapes[number];
const ButtonSizes = tuple('large', 'default', 'small');
export type ButtonSize = typeof ButtonSizes[number];
const ButtonHTMLTypes = tuple('submit', 'button', 'reset');
export type ButtonHTMLType = typeof ButtonHTMLTypes[number];
export default () => ({
prefixCls: PropTypes.string,
type: PropTypes.oneOf(ButtonTypes),
htmlType: PropTypes.oneOf(ButtonHTMLTypes).def('button'),
// icon: PropTypes.string,
shape: PropTypes.oneOf(ButtonShapes),
size: PropTypes.oneOf(ButtonSizes).def('default'),
loading: withUndefined(PropTypes.oneOfType([PropTypes.looseBool, PropTypes.object])),
disabled: PropTypes.looseBool,
ghost: PropTypes.looseBool,
block: PropTypes.looseBool,
icon: PropTypes.VNodeChild,
onClick: PropTypes.func,
});

View File

@ -1,10 +1,11 @@
import { App } from 'vue';
import Button from './button'; import Button from './button';
import ButtonGroup from './button-group'; import ButtonGroup from './button-group';
Button.Group = ButtonGroup; Button.Group = ButtonGroup;
/* istanbul ignore next */ /* istanbul ignore next */
Button.install = function(app) { Button.install = function(app: App) {
app.component(Button.name, Button); app.component(Button.name, Button);
app.component(ButtonGroup.name, ButtonGroup); app.component(ButtonGroup.name, ButtonGroup);
}; };

View File

@ -142,7 +142,7 @@ const ConfigProvider = defineComponent({
const renderProvider = (legacyLocale: Locale) => { const renderProvider = (legacyLocale: Locale) => {
return ( return (
<LocaleProvider locale={props.locale || legacyLocale} _ANT_MARK__={ANT_MARK}> <LocaleProvider locale={props.locale || legacyLocale} ANT_MARK__={ANT_MARK}>
{slots.default?.()} {slots.default?.()}
</LocaleProvider> </LocaleProvider>
); );

View File

@ -1,4 +1,4 @@
import { provide, inject, cloneVNode } from 'vue'; import { provide, inject, cloneVNode, defineComponent } from 'vue';
import RcDropdown from '../vc-dropdown/src/index'; import RcDropdown from '../vc-dropdown/src/index';
import DropdownButton from './dropdown-button'; import DropdownButton from './dropdown-button';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
@ -16,7 +16,7 @@ import { defaultConfigProvider } from '../config-provider';
import RightOutlined from '@ant-design/icons-vue/RightOutlined'; import RightOutlined from '@ant-design/icons-vue/RightOutlined';
const DropdownProps = getDropdownProps(); const DropdownProps = getDropdownProps();
const Dropdown = { const Dropdown = defineComponent({
name: 'ADropdown', name: 'ADropdown',
inheritAttrs: false, inheritAttrs: false,
props: { props: {
@ -107,7 +107,7 @@ const Dropdown = {
}; };
return <RcDropdown {...dropdownProps}>{dropdownTrigger}</RcDropdown>; return <RcDropdown {...dropdownProps}>{dropdownTrigger}</RcDropdown>;
}, },
}; });
Dropdown.Button = DropdownButton; Dropdown.Button = DropdownButton;
export default Dropdown; export default Dropdown;

View File

@ -22,7 +22,7 @@ export interface Locale {
export interface LocaleProviderProps { export interface LocaleProviderProps {
locale: Locale; locale: Locale;
children?: VNode | VNode[]; children?: VNode | VNode[];
_ANT_MARK__?: string; ANT_MARK__?: string;
} }
export const ANT_MARK = 'internalMark'; export const ANT_MARK = 'internalMark';
@ -41,11 +41,11 @@ const LocaleProvider = defineComponent({
locale: { locale: {
type: Object, type: Object,
}, },
_ANT_MARK__: PropTypes.string, ANT_MARK__: PropTypes.string,
}, },
data() { data() {
warning( warning(
this._ANT_MARK__ === ANT_MARK, this.ANT_MARK__ === ANT_MARK,
'LocaleProvider', 'LocaleProvider',
'`LocaleProvider` is deprecated. Please use `locale` with `ConfigProvider` instead', '`LocaleProvider` is deprecated. Please use `locale` with `ConfigProvider` instead',
); );
@ -58,7 +58,7 @@ const LocaleProvider = defineComponent({
}, },
setup(props) { setup(props) {
warning( warning(
props._ANT_MARK__ === ANT_MARK, props.ANT_MARK__ === ANT_MARK,
'LocaleProvider', 'LocaleProvider',
'`LocaleProvider` is deprecated. Please use `locale` with `ConfigProvider` instead', '`LocaleProvider` is deprecated. Please use `locale` with `ConfigProvider` instead',
); );
@ -67,7 +67,7 @@ const LocaleProvider = defineComponent({
...props.locale, ...props.locale,
exist: true, exist: true,
}, },
_ANT_MARK__: ANT_MARK, ANT_MARK__: ANT_MARK,
}; };
provide('localeData', data); provide('localeData', data);
return data; return data;

View File

@ -1,9 +1,9 @@
import { inject } from 'vue'; import { defineComponent, inject } from 'vue';
import { Item, itemProps } from '../vc-menu'; import { Item, itemProps } from '../vc-menu';
import { getOptionProps, getSlot } from '../_util/props-util'; import { getOptionProps, getSlot } from '../_util/props-util';
import Tooltip from '../tooltip'; import Tooltip from '../tooltip';
function noop() {} function noop() {}
export default { export default defineComponent({
name: 'MenuItem', name: 'MenuItem',
inheritAttrs: false, inheritAttrs: false,
props: itemProps, props: itemProps,
@ -56,4 +56,4 @@ export default {
const item = <Item {...itemProps}>{children}</Item>; const item = <Item {...itemProps}>{children}</Item>;
return <Tooltip {...toolTipProps}>{item}</Tooltip>; return <Tooltip {...toolTipProps}>{item}</Tooltip>;
}, },
}; });

View File

@ -1,4 +1,4 @@
import { inject, provide, toRef } from 'vue'; import { defineComponent, inject, provide, toRef } from 'vue';
import omit from 'omit.js'; import omit from 'omit.js';
import VcMenu, { Divider, ItemGroup } from '../vc-menu'; import VcMenu, { Divider, ItemGroup } from '../vc-menu';
import SubMenu from './SubMenu'; import SubMenu from './SubMenu';
@ -47,7 +47,7 @@ export const menuProps = {
'onUpdate:openKeys': PropTypes.func, 'onUpdate:openKeys': PropTypes.func,
}; };
const Menu = { const Menu = defineComponent({
name: 'AMenu', name: 'AMenu',
inheritAttrs: false, inheritAttrs: false,
props: menuProps, props: menuProps,
@ -294,7 +294,7 @@ const Menu = {
return <VcMenu {...menuProps} class={menuClassName} />; return <VcMenu {...menuProps} class={menuClassName} />;
}, },
}; });
/* istanbul ignore next */ /* istanbul ignore next */
Menu.install = function(app) { Menu.install = function(app) {