From bb6fad313e787e1925952ee6f1c63c27d892f664 Mon Sep 17 00:00:00 2001 From: Chen <1207145565@qq.com> Date: Sun, 7 Nov 2021 10:48:02 +0800 Subject: [PATCH] refactor(components): refactor tabs --- packages/components/tabs/index.ts | 5 + packages/components/tabs/src/tab-bar.ts | 12 +++ packages/components/tabs/src/tab-bar.vue | 12 +-- packages/components/tabs/src/tab-nav.ts | 38 ++++++++ packages/components/tabs/src/tab-nav.vue | 43 ++------- packages/components/tabs/src/tab-pane.ts | 18 ++++ packages/components/tabs/src/tab-pane.vue | 17 +--- packages/components/tabs/src/tabs.ts | 108 +++++++++++++--------- packages/components/tabs/src/token.ts | 26 +----- 9 files changed, 154 insertions(+), 125 deletions(-) create mode 100644 packages/components/tabs/src/tab-bar.ts create mode 100644 packages/components/tabs/src/tab-nav.ts create mode 100644 packages/components/tabs/src/tab-pane.ts diff --git a/packages/components/tabs/index.ts b/packages/components/tabs/index.ts index a64181d819..aadf20979e 100644 --- a/packages/components/tabs/index.ts +++ b/packages/components/tabs/index.ts @@ -7,3 +7,8 @@ export const ElTabs = withInstall(Tabs, { }) export default ElTabs export const ElTabPane = withNoopInstall(TabPane) + +export * from './src/tabs' +export * from './src/tab-bar' +export * from './src/tab-nav' +export * from './src/tab-pane' diff --git a/packages/components/tabs/src/tab-bar.ts b/packages/components/tabs/src/tab-bar.ts new file mode 100644 index 0000000000..72c47287f4 --- /dev/null +++ b/packages/components/tabs/src/tab-bar.ts @@ -0,0 +1,12 @@ +import { buildProps, definePropType } from '@element-plus/utils/props' +import type { Pane } from './token' +import type { ExtractPropTypes } from 'vue' + +export const tabBar = buildProps({ + tabs: { + type: definePropType(Array), + default: () => [], + }, +} as const) + +export type TabBar = ExtractPropTypes diff --git a/packages/components/tabs/src/tab-bar.vue b/packages/components/tabs/src/tab-bar.vue index 1f62dd7ea7..c1ccd73627 100644 --- a/packages/components/tabs/src/tab-bar.vue +++ b/packages/components/tabs/src/tab-bar.vue @@ -16,21 +16,17 @@ import { } from 'vue' import { capitalize } from '@vue/shared' import { Resize } from '@element-plus/directives' -import type { Pane, RootTabs } from './token' +import { tabBar } from './tab-bar' +import type { RootTabs } from './token' -import type { CSSProperties, PropType } from 'vue' +import type { CSSProperties } from 'vue' export default defineComponent({ name: 'ElTabBar', directives: { Resize, }, - props: { - tabs: { - type: Array as PropType, - default: () => [] as Pane[], - }, - }, + props: tabBar, setup(props) { const rootTabs = inject('rootTabs') if (!rootTabs) { diff --git a/packages/components/tabs/src/tab-nav.ts b/packages/components/tabs/src/tab-nav.ts new file mode 100644 index 0000000000..abf3590202 --- /dev/null +++ b/packages/components/tabs/src/tab-nav.ts @@ -0,0 +1,38 @@ +import { NOOP } from '@vue/shared' +import { buildProps, definePropType } from '@element-plus/utils/props' +import type { ITabType, Pane } from './token' +import type { ExtractPropTypes } from 'vue' + +export interface Scrollable { + next?: boolean + prev?: number +} + +export const tabNavProps = buildProps({ + panes: { + type: definePropType(Array), + default: () => [], + }, + currentName: { + type: String, + default: '', + }, + editable: Boolean, + onTabClick: { + type: definePropType<(tab: Pane, tabName: string, ev: Event) => void>( + Function + ), + default: NOOP, + }, + onTabRemove: { + type: definePropType<(tab: Pane, ev: Event) => void>(Function), + default: NOOP, + }, + type: { + type: definePropType(String), + default: '', + }, + stretch: Boolean, +} as const) + +export type TabNavProps = ExtractPropTypes diff --git a/packages/components/tabs/src/tab-nav.vue b/packages/components/tabs/src/tab-nav.vue index 2e90b6ff20..4d50b105fd 100644 --- a/packages/components/tabs/src/tab-nav.vue +++ b/packages/components/tabs/src/tab-nav.vue @@ -9,7 +9,7 @@ import { onMounted, onBeforeUnmount, } from 'vue' -import { NOOP, capitalize } from '@vue/shared' +import { capitalize } from '@vue/shared' import { EVENT_CODE } from '@element-plus/utils/aria' import { addResizeListener, @@ -20,50 +20,19 @@ import { throwError } from '@element-plus/utils/error' import { ElIcon } from '@element-plus/components/icon' import { ArrowLeft, ArrowRight, Close } from '@element-plus/icons' import TabBar from './tab-bar.vue' +import { tabNavProps } from './tab-nav' +import type { RefElement } from '@element-plus/utils/types' +import type { Scrollable } from './tab-nav' import type { ResizableElement } from '@element-plus/utils/resize-event' -import type { PropType } from 'vue' -import type { Nullable } from '@element-plus/utils/types' -import type { RootTabs, Pane, ITabType } from './token' - -type RefElement = Nullable - -interface Scrollable { - next?: boolean - prev?: number -} +import type { RootTabs } from './token' export default defineComponent({ name: 'ElTabNav', components: { TabBar, }, - props: { - panes: { - type: Array as PropType, - default: () => [] as Pane[], - }, - currentName: { - type: String, - default: '', - }, - editable: Boolean, - onTabClick: { - type: Function as PropType< - (tab: Pane, tabName: string, ev: Event) => void - >, - default: NOOP, - }, - onTabRemove: { - type: Function as PropType<(tab: Pane, ev: Event) => void>, - default: NOOP, - }, - type: { - type: String as PropType, - default: '', - }, - stretch: Boolean, - }, + props: tabNavProps, setup() { const rootTabs = inject('rootTabs') if (!rootTabs) { diff --git a/packages/components/tabs/src/tab-pane.ts b/packages/components/tabs/src/tab-pane.ts new file mode 100644 index 0000000000..be5184dc32 --- /dev/null +++ b/packages/components/tabs/src/tab-pane.ts @@ -0,0 +1,18 @@ +import { buildProps } from '@element-plus/utils/props' +import type { ExtractPropTypes } from 'vue' + +export const tabPaneProps = buildProps({ + label: { + type: String, + default: '', + }, + name: { + type: String, + default: '', + }, + closable: Boolean, + disabled: Boolean, + lazy: Boolean, +} as const) + +export type TabPaneProps = ExtractPropTypes diff --git a/packages/components/tabs/src/tab-pane.vue b/packages/components/tabs/src/tab-pane.vue index a48e2054a4..b75368b639 100644 --- a/packages/components/tabs/src/tab-pane.vue +++ b/packages/components/tabs/src/tab-pane.vue @@ -20,23 +20,12 @@ import { getCurrentInstance, watch, } from 'vue' +import { tabPaneProps } from './tab-pane' import type { RootTabs, UpdatePaneStateCallback } from './token' export default defineComponent({ name: 'ElTabPane', - props: { - label: { - type: String, - default: '', - }, - name: { - type: String, - default: '', - }, - closable: Boolean, - disabled: Boolean, - lazy: Boolean, - }, + props: tabPaneProps, setup(props) { const index = ref(null) const loaded = ref(false) @@ -67,7 +56,7 @@ export default defineComponent({ if (val) loaded.value = true }) - const instance = getCurrentInstance() + const instance = getCurrentInstance()! updatePaneState({ uid: instance.uid, instance, diff --git a/packages/components/tabs/src/tabs.ts b/packages/components/tabs/src/tabs.ts index befc95bdba..aa82b15eab 100644 --- a/packages/components/tabs/src/tabs.ts +++ b/packages/components/tabs/src/tabs.ts @@ -8,18 +8,26 @@ import { onUpdated, provide, ref, + renderSlot, watch, } from 'vue' import { isPromise } from '@vue/shared' import { EVENT_CODE } from '@element-plus/utils/aria' import ElIcon from '@element-plus/components/icon' import { Plus } from '@element-plus/icons' +import { buildProps, definePropType } from '@element-plus/utils/props' +import { INPUT_EVENT, UPDATE_MODEL_EVENT } from '@element-plus/utils/constants' import TabNav from './tab-nav.vue' -import type { Component, ComponentInternalInstance, PropType, VNode } from 'vue' +import type { + Component, + ComponentInternalInstance, + VNode, + ExtractPropTypes, + Ref, +} from 'vue' import type { BeforeLeave, - IElTabsProps, ITabType, ITabPosition, Pane, @@ -27,48 +35,60 @@ import type { UpdatePaneStateCallback, } from './token' +export const tabsProps = buildProps({ + type: { + type: definePropType(String), + default: '', + }, + activeName: { + type: String, + default: '', + }, + closable: Boolean, + addable: Boolean, + modelValue: { + type: String, + default: '', + }, + editable: Boolean, + tabPosition: { + type: definePropType(String), + default: 'top', + }, + beforeLeave: { + type: definePropType(Function), + }, + stretch: Boolean, +} as const) +export type TabsProps = ExtractPropTypes + +export const tabsEmits = { + [UPDATE_MODEL_EVENT]: (tabName: string) => typeof tabName === 'string', + + [INPUT_EVENT]: (tabName: string) => typeof tabName === 'string', + + 'tab-click': (tab: Pane, ev: Event) => ev instanceof Event, + + edit: (paneName: string | null, action: 'remove' | 'add') => + action === 'remove' || action === 'add', + + 'tab-remove': (paneName: string) => typeof paneName === 'string', + + 'tab-add': () => true, +} + +export type TabsEmits = typeof tabsEmits + export default defineComponent({ name: 'ElTabs', components: { TabNav }, - props: { - type: { - type: String as PropType, - default: '', - }, - activeName: { - type: String, - default: '', - }, - closable: Boolean, - addable: Boolean, - modelValue: { - type: String, - default: '', - }, - editable: Boolean, - tabPosition: { - type: String as PropType, - default: 'top', - }, - beforeLeave: { - type: Function as PropType, - default: null, - }, - stretch: Boolean, - }, - emits: [ - 'tab-click', - 'edit', - 'tab-remove', - 'tab-add', - 'input', - 'update:modelValue', - ], - setup(props: IElTabsProps, ctx) { + props: tabsProps, + emits: tabsEmits, + setup(props: TabsProps, ctx) { const nav$ = ref(null) const currentName = ref(props.modelValue || props.activeName || '0') - const panes = ref([]) - const instance = getCurrentInstance() + const panes: Ref = ref([]) + const instance = getCurrentInstance()! const paneStatesMap = {} provide('rootTabs', { @@ -154,8 +174,8 @@ export default defineComponent({ const changeCurrentName = (value) => { currentName.value = value - ctx.emit('input', value) - ctx.emit('update:modelValue', value) + ctx.emit(INPUT_EVENT, value) + ctx.emit(UPDATE_MODEL_EVENT, value) } const setCurrentName = (value) => { @@ -179,13 +199,13 @@ export default defineComponent({ } } - const handleTabClick = (tab, tabName, event) => { + const handleTabClick = (tab: Pane, tabName: string, event: Event) => { if (tab.props.disabled) return setCurrentName(tabName) ctx.emit('tab-click', tab, event) } - const handleTabRemove = (pane, ev) => { + const handleTabRemove = (pane: Pane, ev: Event) => { if (pane.props.disabled) return ev.stopPropagation() ctx.emit('edit', pane.props.name, 'remove') @@ -272,7 +292,7 @@ export default defineComponent({ { class: 'el-tabs__content', }, - this.$slots?.default() + [renderSlot(this.$slots, 'default')] ) return h( diff --git a/packages/components/tabs/src/token.ts b/packages/components/tabs/src/token.ts index dbba558fc7..cbd8338f2a 100644 --- a/packages/components/tabs/src/token.ts +++ b/packages/components/tabs/src/token.ts @@ -1,3 +1,5 @@ +import type { TabPaneProps } from './tab-pane' +import type { TabsProps } from './tabs' import type { ComponentInternalInstance, ComputedRef, Ref } from 'vue' export type BeforeLeave = ( @@ -9,35 +11,15 @@ export type ITabType = 'card' | 'border-card' | '' export type ITabPosition = 'top' | 'right' | 'bottom' | 'left' export type UpdatePaneStateCallback = (pane: Pane) => void -export interface IElTabsProps { - type: ITabType - activeName: string - closable: boolean - addable: boolean - modelValue: string - editable: boolean - tabPosition: ITabPosition - beforeLeave: BeforeLeave - stretch: boolean -} - export interface RootTabs { - props: IElTabsProps + props: TabsProps currentName: Ref } -export interface IElPaneProps { - label: string - name: string - closable: boolean - disabled: boolean - lazy: boolean -} - export interface Pane { uid: number instance: ComponentInternalInstance - props: IElPaneProps + props: TabPaneProps paneName: ComputedRef active: ComputedRef index: Ref