refactor(components): [tabs] simplify logic with hooks (#10224)

This commit is contained in:
zz 2022-10-26 13:01:22 +08:00 committed by GitHub
parent bfb8e26ed8
commit af874ea93e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 57 additions and 47 deletions

View File

@ -341,6 +341,45 @@ describe('Tabs.vue', () => {
expect(panesWrapper.length).toEqual(2)
})
test('tab order', async () => {
const editableTabs = ref([
{
title: 'Tab 1',
name: '1',
content: 'Tab 1 content',
},
{
title: 'Tab 2',
name: '2',
content: 'Tab 2 content',
},
])
const wrapper = mount(() => (
<Tabs ref="tabs" type="card">
{editableTabs.value.map((item) => (
<TabPane
label={item.title}
key={item.name}
name={item.name}
></TabPane>
))}
</Tabs>
))
editableTabs.value.splice(1, 0, {
title: 'Tab 3',
name: '3',
content: 'Tab 3 content',
})
await nextTick()
const items = wrapper.findAll('.el-tabs__item')
editableTabs.value.forEach((tab, index) => {
expect(items[index].element.textContent).toEqual(tab.title)
})
})
test('closable in tab-pane', async () => {
const wrapper = mount(() => (
<Tabs type="card" ref="tabs">

View File

@ -6,8 +6,6 @@ import {
provide,
ref,
renderSlot,
shallowReactive,
shallowRef,
watch,
} from 'vue'
import {
@ -21,9 +19,12 @@ import { EVENT_CODE, UPDATE_MODEL_EVENT } from '@element-plus/constants'
import ElIcon from '@element-plus/components/icon'
import { Plus } from '@element-plus/icons-vue'
import { tabsRootContextKey } from '@element-plus/tokens'
import { useDeprecated, useNamespace } from '@element-plus/hooks'
import {
useDeprecated,
useNamespace,
useOrderedChildren,
} from '@element-plus/hooks'
import TabNav from './tab-nav'
import { getOrderedPanes } from './utils/pane'
import type { TabNavInstance } from './tab-nav'
import type { TabsPaneContext } from '@element-plus/tokens'
@ -85,13 +86,15 @@ export default defineComponent({
emits: tabsEmits,
setup(props, { emit, slots, expose }) {
const vm = getCurrentInstance()!
const ns = useNamespace('tabs')
const {
children: panes,
addChild: registerPane,
removeChild: unregisterPane,
} = useOrderedChildren<TabsPaneContext>(getCurrentInstance()!, 'ElTabPane')
const nav$ = ref<TabNavInstance>()
const panes = shallowReactive<TabsPanes>({})
const orderedPanes = shallowRef<TabsPaneContext[]>([])
const currentName = ref<TabPaneName>(
props.modelValue ?? props.activeName ?? '0'
)
@ -171,24 +174,12 @@ export default defineComponent({
nav$.value?.scrollToActiveTab()
})
{
const registerPane = (pane: TabsPaneContext) => {
panes[pane.uid] = pane
orderedPanes.value = getOrderedPanes(vm, panes)
}
const unregisterPane = (uid: number) => {
delete panes[uid]
orderedPanes.value = getOrderedPanes(vm, panes)
}
provide(tabsRootContextKey, {
props,
currentName,
registerPane,
unregisterPane,
})
}
provide(tabsRootContextKey, {
props,
currentName,
registerPane,
unregisterPane,
})
expose({
currentName,
@ -219,7 +210,7 @@ export default defineComponent({
currentName={currentName.value}
editable={props.editable}
type={props.type}
panes={orderedPanes.value}
panes={panes.value}
stretch={props.stretch}
onTabClick={handleTabClick}
onTabRemove={handleTabRemove}

View File

@ -1,20 +0,0 @@
import { flattedChildren, isVNode } from '@element-plus/utils'
import type { ComponentInternalInstance, VNode } from 'vue'
import type { TabsPanes } from '../tabs'
export const getTabPanes = (vm: ComponentInternalInstance) => {
const nodes = flattedChildren(vm.subTree)
return nodes.filter(
(n): n is VNode =>
isVNode(n) && (n.type as any)?.name === 'ElTabPane' && !!n.component
)
}
export const getOrderedPanes = (
vm: ComponentInternalInstance,
panes: TabsPanes
) => {
const nodes = getTabPanes(vm)
const uids = nodes.map((n) => n.component!.uid)
return uids.map((uid) => panes[uid]).filter((p) => !!p)
}