Skip to content

Commit

Permalink
feat(comp:tabs): support addIcon slot (#1566)
Browse files Browse the repository at this point in the history
  • Loading branch information
danranVm committed May 29, 2023
1 parent 08e1821 commit dcd9c1a
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 104 deletions.
1 change: 1 addition & 0 deletions packages/components/tabs/docs/Api.zh.md
Expand Up @@ -44,5 +44,6 @@ export interface TabsData<K = VKey> extends TabProps {
| --- | --- | --- | --- |
| `title` | 标题插槽 | `{ key:VKey, disabled:boolean, selected:boolean, title: string }` | - |
| `content` | 内容插槽 | `{key:VKey, content: any, selected: boolean}` | - |
| `addIcon` | 新增图标 | - | 也可以用于自定义其他操作 |

若是通过`dataSource`进行渲染的,可以通过设置`customTitle``customContent`字段,自定义插槽进行渲染,[参考](/components/tabs/zh?tab=demo#components-tabs-custom-tab)
6 changes: 3 additions & 3 deletions packages/components/tabs/src/Tabs.tsx
Expand Up @@ -5,7 +5,7 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { computed, defineComponent, normalizeClass, provide } from 'vue'
import { computed, defineComponent, normalizeClass, provide, reactive } from 'vue'

import { isNil, isString } from 'lodash-es'

Expand Down Expand Up @@ -36,7 +36,7 @@ export default defineComponent({
const [closedKeys, setClosedKeys] = useState<VKey[]>([])

// 存储每个标签的尺寸和偏移
const navAttrMap = new Map<VKey, { offset: number; size: number }>()
const navAttrs = reactive<Record<VKey, { offset: number; size: number }>>({})

const handleTabClick = async (key: VKey, evt: Event) => {
const result = await callEmit(props.onBeforeLeave, key, selectedKey.value)
Expand Down Expand Up @@ -69,7 +69,7 @@ export default defineComponent({
mergedDataSource,
isHorizontal,
closedKeys,
navAttrMap,
navAttrs,
handleTabClick,
handleTabClose,
setSelectedKey,
Expand Down
10 changes: 7 additions & 3 deletions packages/components/tabs/src/composables/useSizeObservable.ts
Expand Up @@ -34,7 +34,7 @@ export function useSizeObservable(
addBtnRef: ShallowRef<HTMLElement | undefined>,
operationsRef: ShallowRef<HTMLElement | undefined>,
isHorizontal: ComputedRef<boolean>,
navAttrMap: Map<VKey, { offset: number; size: number }>,
navAttrs: Record<VKey, { offset: number; size: number } | undefined>,
closedKeys: ComputedRef<VKey[]>,
): SizeObservableContext {
const [wrapperSize, setWrapperSize] = useState(0)
Expand Down Expand Up @@ -150,10 +150,14 @@ export function useSizeObservable(
const oldSet = new Set(old)
const closeTabKey = [...curSet].find(item => !oldSet.has(item))
if (closeTabKey !== undefined) {
const closeTabAttr = navAttrMap.get(closeTabKey) || { size: 0, offset: 0 }
const closeTabAttr = navAttrs[closeTabKey] || { size: 0, offset: 0 }
const { size: closeTabSize, offset: closeTabOffset } = closeTabAttr
let nextTabOffset = 0
for (const { offset } of navAttrMap.values()) {
for (const attr of Object.values(navAttrs)) {
if (!attr) {
continue
}
const { offset } = attr
if (offset > closeTabOffset) {
nextTabOffset = offset
break
Expand Down
31 changes: 13 additions & 18 deletions packages/components/tabs/src/contents/TabNav.tsx
Expand Up @@ -35,7 +35,7 @@ export default defineComponent({
mergedPrefixCls,
handleTabClick,
handleTabClose,
navAttrMap,
navAttrs,
isHorizontal,
} = inject(tabsToken)!

Expand All @@ -46,25 +46,20 @@ export default defineComponent({

const elementRef = shallowRef<HTMLElement>()

const setNavAttr = (el: HTMLElement) => {
navAttrMap.set(key, {
size: el[sizeProp.value] + getMarginSize(el, isHorizontal.value),
offset: el[offsetProp.value],
})
const setNavAttr = (el: HTMLElement | undefined) => {
let attr = undefined
if (el) {
attr = {
size: el[sizeProp.value] + getMarginSize(el, isHorizontal.value),
offset: el[offsetProp.value],
}
}
navAttrs[key] = attr
}

onUpdated(() => {
const target = elementRef.value as HTMLElement
setNavAttr(target)
})

onBeforeUnmount(() => {
navAttrMap.delete(key)
})

onMounted(() => {
elementRef.value && setNavAttr(elementRef.value)
})
onMounted(() => setNavAttr(elementRef.value))
onUpdated(() => setNavAttr(elementRef.value))
onBeforeUnmount(() => setNavAttr(undefined))

const classes = computed(() => {
const { disabled, selected } = props
Expand Down
48 changes: 23 additions & 25 deletions packages/components/tabs/src/contents/TabNavWrapper.tsx
Expand Up @@ -29,7 +29,7 @@ export default defineComponent({
mergedDataSource,
isHorizontal,
closedKeys,
navAttrMap,
navAttrs,
} = inject(tabsToken)!

const wrapperRef = shallowRef<HTMLElement>()
Expand All @@ -55,7 +55,7 @@ export default defineComponent({
addBtnRef,
operationsRef,
isHorizontal,
navAttrMap,
navAttrs,
closedKeys,
)

Expand All @@ -65,8 +65,9 @@ export default defineComponent({
})

const moreNavDataSource = computed(() => {
// TODO: 性能优化
return allNavDataSource.value.reduce((acc: SelectData[], data) => {
const attr = navAttrMap.get(data.key)
const attr = navAttrs[data.key]
if (
attr &&
// 将没有展示完全的 nav 全部放入到moreSelectPane中
Expand Down Expand Up @@ -122,17 +123,18 @@ export default defineComponent({
return () => {
const { selectedKey } = props
const { addable, type } = tabsProps
const prefixCls = mergedPrefixCls.value

return (
<div class={classes.value} ref={wrapperRef}>
<div
class={[
`${mergedPrefixCls.value}-nav`,
firstShow.value ? `${mergedPrefixCls.value}-nav-first-show` : '',
lastShow.value ? `${mergedPrefixCls.value}-nav-last-show` : '',
`${prefixCls}-nav`,
firstShow.value ? `${prefixCls}-nav-first-show` : '',
lastShow.value ? `${prefixCls}-nav-last-show` : '',
]}
>
<div ref={navRef} style={navStyle.value} class={`${mergedPrefixCls.value}-nav-list`}>
<div ref={navRef} style={navStyle.value} class={`${prefixCls}-nav-list`}>
{allNavDataSource.value.map(data => {
const { key, content, customContent, customTitle = 'title', ...navProps } = data
const titleSlot = isString(customTitle) ? slots[customTitle] : customTitle
Expand All @@ -147,25 +149,19 @@ export default defineComponent({
)
})}
{addable && (
<div
<button
ref={addBtnRef}
class={[
`${mergedPrefixCls.value}-nav-tab-add`,
hasScroll.value && `${mergedPrefixCls.value}-nav-tab-add-hidden`,
]}
class={[`${prefixCls}-nav-tab-add`, hasScroll.value && `${prefixCls}-nav-tab-add-hidden`]}
onClick={handleAdd}
>
<IxIcon name="plus" class={`${mergedPrefixCls.value}-nav-add-icon`}></IxIcon>
</div>
{slots.addIcon ? slots.addIcon() : <IxIcon name="plus" class={`${prefixCls}-nav-add-icon`} />}
</button>
)}
</div>
</div>
<div
ref={operationsRef}
class={[
`${mergedPrefixCls.value}-nav-operations`,
!hasScroll.value && `${mergedPrefixCls.value}-nav-operations-hidden`,
]}
class={[`${prefixCls}-nav-operations`, !hasScroll.value && `${prefixCls}-nav-operations-hidden`]}
>
<IxPopover
trigger="hover"
Expand All @@ -174,19 +170,21 @@ export default defineComponent({
{...{
'onUpdate:visible': setMoreSelectPaneVisible,
}}
class={`${mergedPrefixCls.value}-nav-operations-popover`}
class={`${prefixCls}-nav-operations-popover`}
v-slots={{
content: () => (
<MoreSelectPane visible={moreSelectPaneVisible.value} dataSource={moreNavDataSource.value} />
),
content: () => {
return <MoreSelectPane visible={moreSelectPaneVisible.value} dataSource={moreNavDataSource.value} />
},
}}
>
<IxButton icon="more" mode="text" shape="square"></IxButton>
</IxPopover>
{addable && <IxButton icon="plus" mode="text" shape="square" onClick={handleAdd}></IxButton>}
{addable && (
<IxButton v-slots={{ icon: slots.addIcon }} icon="plus" mode="text" shape="square" onClick={handleAdd} />
)}
</div>
{type !== 'segment' && <div class={`${mergedPrefixCls.value}-nav-border`}></div>}
{type === 'line' && <div class={`${mergedPrefixCls.value}-nav-bar`} style={navBarStyle.value}></div>}
{type !== 'segment' && <div class={`${prefixCls}-nav-border`}></div>}
{type === 'line' && <div class={`${prefixCls}-nav-bar`} style={navBarStyle.value}></div>}
</div>
)
}
Expand Down
2 changes: 1 addition & 1 deletion packages/components/tabs/src/tokens.ts
Expand Up @@ -15,7 +15,7 @@ export interface TabsContext {
mergedDataSource: ComputedRef<TabsData[]>
isHorizontal: ComputedRef<boolean>
closedKeys: ComputedRef<VKey[]>
navAttrMap: Map<VKey, { offset: number; size: number }>
navAttrs: Record<VKey, { offset: number; size: number } | undefined>
handleTabClick: (key: VKey, evt: Event) => Promise<void>
handleTabClose: (key: VKey) => Promise<void>
setSelectedKey: (value: VKey) => void
Expand Down

0 comments on commit dcd9c1a

Please sign in to comment.