refactor(layout): layout to ts (#2991)
* refactor(layout): layout to ts * refactor(layout): update types * refactor: update code style * refactor(vue-types): fix PropTypes looseBool types error * refactor: remove BasicProps types * refactor(layout): extends component typespull/2996/head
parent
147845b55f
commit
e95bac3d87
|
@ -36,9 +36,7 @@ export function withUndefined<T extends { default?: any }>(type: T): T {
|
|||
}
|
||||
|
||||
export default PropTypes as VueTypesInterface & {
|
||||
readonly looseBool: VueTypeValidableDef<boolean> & {
|
||||
default: undefined;
|
||||
};
|
||||
readonly looseBool: VueTypeValidableDef<boolean>;
|
||||
readonly style: VueTypeValidableDef<CSSProperties>;
|
||||
readonly VNodeChild: VueTypeValidableDef<VNodeTypes>;
|
||||
};
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
import classNames from '../_util/classNames';
|
||||
import { inject, provide } from 'vue';
|
||||
import { inject, provide, PropType, defineComponent } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import {
|
||||
initDefaultProps,
|
||||
getOptionProps,
|
||||
hasProp,
|
||||
getComponent,
|
||||
getSlot,
|
||||
} from '../_util/props-util';
|
||||
import { tuple } from '../_util/type';
|
||||
import { getOptionProps, hasProp, getComponent, getSlot } from '../_util/props-util';
|
||||
import initDefaultProps from '../_util/props-util/initDefaultProps';
|
||||
import BaseMixin from '../_util/BaseMixin';
|
||||
import isNumeric from '../_util/isNumeric';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
|
@ -15,6 +11,7 @@ import BarsOutlined from '@ant-design/icons-vue/BarsOutlined';
|
|||
import RightOutlined from '@ant-design/icons-vue/RightOutlined';
|
||||
import LeftOutlined from '@ant-design/icons-vue/LeftOutlined';
|
||||
import omit from 'omit.js';
|
||||
import { SiderHookProvider } from './layout';
|
||||
|
||||
const dimensionMaxMap = {
|
||||
xs: '479.98px',
|
||||
|
@ -25,7 +22,7 @@ const dimensionMaxMap = {
|
|||
xxl: '1599.98px',
|
||||
};
|
||||
|
||||
// export type CollapseType = 'clickTrigger' | 'responsive';
|
||||
export type CollapseType = 'clickTrigger' | 'responsive';
|
||||
|
||||
export const SiderProps = {
|
||||
prefixCls: PropTypes.string,
|
||||
|
@ -33,16 +30,14 @@ export const SiderProps = {
|
|||
collapsed: PropTypes.looseBool,
|
||||
defaultCollapsed: PropTypes.looseBool,
|
||||
reverseArrow: PropTypes.looseBool,
|
||||
// onCollapse?: (collapsed: boolean, type: CollapseType) => void;
|
||||
zeroWidthTriggerStyle: PropTypes.object,
|
||||
trigger: PropTypes.any,
|
||||
zeroWidthTriggerStyle: PropTypes.style,
|
||||
trigger: PropTypes.VNodeChild,
|
||||
width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
collapsedWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
breakpoint: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl', 'xxl']),
|
||||
theme: PropTypes.oneOf(['light', 'dark']).def('dark'),
|
||||
onBreakpoint: PropTypes.func,
|
||||
onCollapse: PropTypes.func,
|
||||
'onUpdate:collapsed': PropTypes.func,
|
||||
breakpoint: PropTypes.oneOf(tuple('xs', 'sm', 'md', 'lg', 'xl', 'xxl')),
|
||||
theme: PropTypes.oneOf(tuple('light', 'dark')).def('dark'),
|
||||
onBreakpoint: Function as PropType<(broken: boolean) => void>,
|
||||
onCollapse: Function as PropType<(collapsed: boolean, type: CollapseType) => void>,
|
||||
};
|
||||
|
||||
// export interface SiderState {
|
||||
|
@ -63,7 +58,7 @@ const generateId = (() => {
|
|||
};
|
||||
})();
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
name: 'ALayoutSider',
|
||||
__ANT_LAYOUT_SIDER: true,
|
||||
mixins: [BaseMixin],
|
||||
|
@ -74,17 +69,19 @@ export default {
|
|||
width: 200,
|
||||
collapsedWidth: 80,
|
||||
}),
|
||||
emits: ['breakpoint', 'update:collapsed', 'collapse'],
|
||||
data() {
|
||||
this.uniqueId = generateId('ant-sider-');
|
||||
let matchMedia;
|
||||
const uniqueId = generateId('ant-sider-');
|
||||
let matchMedia: typeof window.matchMedia;
|
||||
if (typeof window !== 'undefined') {
|
||||
matchMedia = window.matchMedia;
|
||||
}
|
||||
const props = getOptionProps(this);
|
||||
const props = getOptionProps(this) as any;
|
||||
let mql: MediaQueryList;
|
||||
if (matchMedia && props.breakpoint && props.breakpoint in dimensionMaxMap) {
|
||||
this.mql = matchMedia(`(max-width: ${dimensionMaxMap[props.breakpoint]})`);
|
||||
mql = matchMedia(`(max-width: ${dimensionMaxMap[props.breakpoint]})`);
|
||||
}
|
||||
let sCollapsed;
|
||||
let sCollapsed: boolean;
|
||||
if ('collapsed' in props) {
|
||||
sCollapsed = props.collapsed;
|
||||
} else {
|
||||
|
@ -94,6 +91,8 @@ export default {
|
|||
sCollapsed,
|
||||
below: false,
|
||||
belowShow: false,
|
||||
uniqueId,
|
||||
mql,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
|
@ -108,7 +107,7 @@ export default {
|
|||
},
|
||||
setup() {
|
||||
return {
|
||||
siderHook: inject('siderHook', {}),
|
||||
siderHook: inject<SiderHookProvider>('siderHook', {}),
|
||||
configProvider: inject('configProvider', defaultConfigProvider),
|
||||
};
|
||||
},
|
||||
|
@ -136,7 +135,7 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
responsiveHandler(mql) {
|
||||
responsiveHandler(mql: MediaQueryListEvent | MediaQueryList) {
|
||||
this.setState({ below: mql.matches });
|
||||
this.$emit('breakpoint', mql.matches);
|
||||
if (this.sCollapsed !== mql.matches) {
|
||||
|
@ -144,7 +143,7 @@ export default {
|
|||
}
|
||||
},
|
||||
|
||||
setCollapsed(collapsed, type) {
|
||||
setCollapsed(collapsed: boolean, type: CollapseType) {
|
||||
if (!hasProp(this, 'collapsed')) {
|
||||
this.setState({
|
||||
sCollapsed: collapsed,
|
||||
|
@ -176,7 +175,7 @@ export default {
|
|||
collapsedWidth,
|
||||
zeroWidthTriggerStyle,
|
||||
...others
|
||||
} = { ...getOptionProps(this), ...this.$attrs };
|
||||
} = { ...getOptionProps(this), ...this.$attrs } as any;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls('layout-sider', customizePrefixCls);
|
||||
const divProps = omit(others, [
|
||||
|
@ -188,7 +187,6 @@ export default {
|
|||
'siderHook',
|
||||
'zeroWidthTriggerStyle',
|
||||
'trigger',
|
||||
'onUpdate:collapse',
|
||||
]);
|
||||
const trigger = getComponent(this, 'trigger');
|
||||
const rawWidth = this.sCollapsed ? collapsedWidth : width;
|
||||
|
@ -241,4 +239,4 @@ export default {
|
|||
</aside>
|
||||
);
|
||||
},
|
||||
};
|
||||
});
|
|
@ -1,10 +1,11 @@
|
|||
import { App } from 'vue';
|
||||
import Layout from './layout';
|
||||
import Sider from './Sider';
|
||||
|
||||
Layout.Sider = Sider;
|
||||
|
||||
/* istanbul ignore next */
|
||||
Layout.install = function(app) {
|
||||
Layout.install = function(app: App) {
|
||||
app.component(Layout.name, Layout);
|
||||
app.component(Layout.Header.name, Layout.Header);
|
||||
app.component(Layout.Footer.name, Layout.Footer);
|
||||
|
@ -12,4 +13,6 @@ Layout.install = function(app) {
|
|||
app.component(Layout.Content.name, Layout.Content);
|
||||
return app;
|
||||
};
|
||||
export default Layout;
|
||||
export default Layout as typeof Layout & {
|
||||
readonly Sider: typeof Sider;
|
||||
};
|
|
@ -1,107 +0,0 @@
|
|||
import { inject, provide } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import classNames from '../_util/classNames';
|
||||
import { getOptionProps, getSlot } from '../_util/props-util';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
|
||||
export const BasicProps = {
|
||||
prefixCls: PropTypes.string,
|
||||
hasSider: PropTypes.looseBool,
|
||||
tagName: PropTypes.string,
|
||||
};
|
||||
|
||||
function generator({ suffixCls, tagName, name }) {
|
||||
return BasicComponent => {
|
||||
return {
|
||||
name,
|
||||
props: BasicComponent.props,
|
||||
setup() {
|
||||
return {
|
||||
configProvider: inject('configProvider', defaultConfigProvider),
|
||||
};
|
||||
},
|
||||
render() {
|
||||
const { prefixCls: customizePrefixCls } = this.$props;
|
||||
const getPrefixCls = this.configProvider.getPrefixCls;
|
||||
const prefixCls = getPrefixCls(suffixCls, customizePrefixCls);
|
||||
|
||||
const basicComponentProps = {
|
||||
prefixCls,
|
||||
...getOptionProps(this),
|
||||
tagName,
|
||||
};
|
||||
return <BasicComponent {...basicComponentProps}>{getSlot(this)}</BasicComponent>;
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const Basic = {
|
||||
props: BasicProps,
|
||||
render() {
|
||||
const { prefixCls, tagName: Tag } = this;
|
||||
const divProps = {
|
||||
class: prefixCls,
|
||||
};
|
||||
return <Tag {...divProps}>{getSlot(this)}</Tag>;
|
||||
},
|
||||
};
|
||||
|
||||
const BasicLayout = {
|
||||
props: BasicProps,
|
||||
data() {
|
||||
return {
|
||||
siders: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
provide('siderHook', {
|
||||
addSider: id => {
|
||||
this.siders = [...this.siders, id];
|
||||
},
|
||||
removeSider: id => {
|
||||
this.siders = this.siders.filter(currentId => currentId !== id);
|
||||
},
|
||||
});
|
||||
},
|
||||
render() {
|
||||
const { prefixCls, hasSider, tagName: Tag } = this;
|
||||
const divCls = classNames(prefixCls, {
|
||||
[`${prefixCls}-has-sider`]: typeof hasSider === 'boolean' ? hasSider : this.siders.length > 0,
|
||||
});
|
||||
const divProps = {
|
||||
class: divCls,
|
||||
};
|
||||
return <Tag {...divProps}>{getSlot(this)}</Tag>;
|
||||
},
|
||||
};
|
||||
|
||||
const Layout = generator({
|
||||
suffixCls: 'layout',
|
||||
tagName: 'section',
|
||||
name: 'ALayout',
|
||||
})(BasicLayout);
|
||||
|
||||
const Header = generator({
|
||||
suffixCls: 'layout-header',
|
||||
tagName: 'header',
|
||||
name: 'ALayoutHeader',
|
||||
})(Basic);
|
||||
|
||||
const Footer = generator({
|
||||
suffixCls: 'layout-footer',
|
||||
tagName: 'footer',
|
||||
name: 'ALayoutFooter',
|
||||
})(Basic);
|
||||
|
||||
const Content = generator({
|
||||
suffixCls: 'layout-content',
|
||||
tagName: 'main',
|
||||
name: 'ALayoutContent',
|
||||
})(Basic);
|
||||
|
||||
Layout.Header = Header;
|
||||
Layout.Footer = Footer;
|
||||
Layout.Content = Content;
|
||||
|
||||
export default Layout;
|
|
@ -0,0 +1,110 @@
|
|||
import { createVNode, defineComponent, inject, provide, toRefs, ref } from 'vue';
|
||||
import PropTypes from '../_util/vue-types';
|
||||
import classNames from '../_util/classNames';
|
||||
import { defaultConfigProvider } from '../config-provider';
|
||||
|
||||
export const BasicProps = {
|
||||
prefixCls: PropTypes.string,
|
||||
hasSider: PropTypes.looseBool,
|
||||
tagName: PropTypes.string,
|
||||
};
|
||||
|
||||
export interface SiderHookProvider {
|
||||
addSider?: (id: string) => void;
|
||||
removeSider?: (id: string) => void;
|
||||
}
|
||||
|
||||
type GeneratorArgument = {
|
||||
suffixCls: string;
|
||||
tagName: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
function generator({ suffixCls, tagName, name }: GeneratorArgument) {
|
||||
return (BasicComponent: typeof Basic) => {
|
||||
return defineComponent({
|
||||
name,
|
||||
props: BasicProps,
|
||||
setup(props, { slots }) {
|
||||
const { getPrefixCls } = inject('configProvider', defaultConfigProvider);
|
||||
return () => {
|
||||
const { prefixCls: customizePrefixCls } = props;
|
||||
const prefixCls = getPrefixCls(suffixCls, customizePrefixCls);
|
||||
const basicComponentProps = {
|
||||
prefixCls,
|
||||
...props,
|
||||
tagName,
|
||||
};
|
||||
return <BasicComponent {...basicComponentProps}>{slots.default?.()}</BasicComponent>;
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const Basic = defineComponent({
|
||||
props: BasicProps,
|
||||
setup(props, { slots }) {
|
||||
const { prefixCls, tagName } = toRefs(props);
|
||||
return () => createVNode(tagName.value, { class: prefixCls.value }, slots.default?.());
|
||||
},
|
||||
});
|
||||
|
||||
const BasicLayout = defineComponent({
|
||||
props: BasicProps,
|
||||
setup(props, { slots }) {
|
||||
const siders = ref<string[]>([]);
|
||||
const siderHookProvider: SiderHookProvider = {
|
||||
addSider: id => {
|
||||
siders.value = [...siders.value, id];
|
||||
},
|
||||
removeSider: id => {
|
||||
siders.value = siders.value.filter(currentId => currentId !== id);
|
||||
},
|
||||
};
|
||||
provide('siderHook', siderHookProvider);
|
||||
|
||||
return () => {
|
||||
const { prefixCls, hasSider, tagName } = props;
|
||||
const divCls = classNames(prefixCls, {
|
||||
[`${prefixCls}-has-sider`]:
|
||||
typeof hasSider === 'boolean' ? hasSider : siders.value.length > 0,
|
||||
});
|
||||
return createVNode(tagName, { class: divCls }, slots.default?.());
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const Layout = generator({
|
||||
suffixCls: 'layout',
|
||||
tagName: 'section',
|
||||
name: 'ALayout',
|
||||
})(BasicLayout);
|
||||
|
||||
const Header = generator({
|
||||
suffixCls: 'layout-header',
|
||||
tagName: 'header',
|
||||
name: 'ALayoutHeader',
|
||||
})(Basic);
|
||||
|
||||
const Footer = generator({
|
||||
suffixCls: 'layout-footer',
|
||||
tagName: 'footer',
|
||||
name: 'ALayoutFooter',
|
||||
})(Basic);
|
||||
|
||||
const Content = generator({
|
||||
suffixCls: 'layout-content',
|
||||
tagName: 'main',
|
||||
name: 'ALayoutContent',
|
||||
})(Basic);
|
||||
|
||||
Layout.Header = Header;
|
||||
Layout.Footer = Footer;
|
||||
Layout.Content = Content;
|
||||
|
||||
export default Layout as typeof Layout & {
|
||||
readonly Header: typeof Header;
|
||||
readonly Footer: typeof Footer;
|
||||
readonly Content: typeof Content;
|
||||
};
|
Loading…
Reference in New Issue