mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
refactor(components): refactor tabs
This commit is contained in:
@@ -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'
|
||||
|
||||
12
packages/components/tabs/src/tab-bar.ts
Normal file
12
packages/components/tabs/src/tab-bar.ts
Normal 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>
|
||||
@@ -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) {
|
||||
|
||||
38
packages/components/tabs/src/tab-nav.ts
Normal file
38
packages/components/tabs/src/tab-nav.ts
Normal 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>
|
||||
@@ -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) {
|
||||
|
||||
18
packages/components/tabs/src/tab-pane.ts
Normal file
18
packages/components/tabs/src/tab-pane.ts
Normal 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>
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user