refactor(components): refactor tabs

This commit is contained in:
Chen
2021-11-07 10:48:02 +08:00
committed by 三咲智子
parent 336c937e9d
commit bb6fad313e
9 changed files with 154 additions and 125 deletions

View File

@@ -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'

View File

@@ -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<Pane[]>(Array),
default: () => [],
},
} as const)
export type TabBar = ExtractPropTypes<typeof tabBar>

View File

@@ -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<Pane[]>,
default: () => [] as Pane[],
},
},
props: tabBar,
setup(props) {
const rootTabs = inject<RootTabs>('rootTabs')
if (!rootTabs) {

View File

@@ -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<Pane[]>(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<ITabType>(String),
default: '',
},
stretch: Boolean,
} as const)
export type TabNavProps = ExtractPropTypes<typeof tabNavProps>

View File

@@ -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<HTMLElement>
interface Scrollable {
next?: boolean
prev?: number
}
import type { RootTabs } from './token'
export default defineComponent({
name: 'ElTabNav',
components: {
TabBar,
},
props: {
panes: {
type: Array as PropType<Pane[]>,
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<ITabType>,
default: '',
},
stretch: Boolean,
},
props: tabNavProps,
setup() {
const rootTabs = inject<RootTabs>('rootTabs')
if (!rootTabs) {

View File

@@ -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<typeof tabPaneProps>

View File

@@ -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<string>(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,

View File

@@ -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<ITabType>(String),
default: '',
},
activeName: {
type: String,
default: '',
},
closable: Boolean,
addable: Boolean,
modelValue: {
type: String,
default: '',
},
editable: Boolean,
tabPosition: {
type: definePropType<ITabPosition>(String),
default: 'top',
},
beforeLeave: {
type: definePropType<BeforeLeave>(Function),
},
stretch: Boolean,
} as const)
export type TabsProps = ExtractPropTypes<typeof tabsProps>
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<ITabType>,
default: '',
},
activeName: {
type: String,
default: '',
},
closable: Boolean,
addable: Boolean,
modelValue: {
type: String,
default: '',
},
editable: Boolean,
tabPosition: {
type: String as PropType<ITabPosition>,
default: 'top',
},
beforeLeave: {
type: Function as PropType<BeforeLeave>,
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<typeof TabNav>(null)
const currentName = ref(props.modelValue || props.activeName || '0')
const panes = ref([])
const instance = getCurrentInstance()
const panes: Ref<Pane[]> = ref([])
const instance = getCurrentInstance()!
const paneStatesMap = {}
provide<RootTabs>('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(

View File

@@ -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<string>
}
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<string>
active: ComputedRef<boolean>
index: Ref<string>