feat(tabs): add on-before-leave prop (#1394)

* feat(tabs): add on-before-leave prop

* feat: demo引入属性优化

* feat: 优化tab加载

* feat: 优化tab加载

* feat: 处理新tab打开中断旧导航

Co-authored-by: unknown <liyang@xiaoyouzi.com>
This commit is contained in:
小魔王 2021-10-20 23:22:40 +08:00 committed by GitHub
parent 7686b69e2a
commit 2c530b56da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 143 additions and 1 deletions

View File

@ -1,5 +1,11 @@
# CHANGELOG
## Pending
### Feats
- `n-tabs` add `on-before-leave` prop, closes [#1337](https://github.com/TuSimple/naive-ui/issues/1337).
## 2.19.9 (2021-10-18)
### Fixes

View File

@ -1,5 +1,11 @@
# CHANGELOG
## Pending
### Feats
- `n-tabs` 新增 `on-before-leave` 属性,关闭 [#1337](https://github.com/TuSimple/naive-ui/issues/1337)
## 2.19.9 (2021-10-18)
### Fixes

View File

@ -0,0 +1,33 @@
# Hook function before switching tab
```html
<n-tabs
type="line"
@before-leave="handleBeforeLeave"
@update:value="handleUpdateValue"
>
<n-tab-pane name="oasis" tab="Oasis">Wonderwall</n-tab-pane>
<n-tab-pane name="the beatles" tab="the Beatles">Hey Jude</n-tab-pane>
<n-tab-pane name="jay chou" tab="My Favour">My faviur</n-tab-pane>
</n-tabs>
```
```js
import { defineComponent } from 'vue'
import { useMessage } from 'naive-ui'
export default defineComponent({
setup () {
const message = useMessage()
return {
handleBeforeLeave: (tab, oldTab) => {
if (tab === 'jay chou') message.info('Can not leave!')
return tab !== 'jay chou'
},
handleUpdateValue: (activeName) => {
message.info(activeName)
}
}
}
})
```

View File

@ -14,6 +14,7 @@ prefix
display-directive
addable
line-debug
before-leave
```
## API
@ -33,6 +34,7 @@ line-debug
| type | `'bar' \| 'line' \| 'card' \| 'segment'` | `'bar'` | Tabs type. |
| value | `string \| number` | `undefined` | Value in controlled mode. |
| on-add | `() => void` | `undefined` | Callback function triggered when add tag. |
| on-before-leave | `(activeName: string \| number, oldActiveName: string \| number \| null) => boolean \| Promise<boolean>` | `undefined` | hook function before switching tab. |
| on-close | `(name: string \| number) => void` | `undefined` | Callback function triggered when close tag. |
| on-update:value | `(value: string \| number) => void` | `undefined` | Callback function triggered when the value changes. |

View File

@ -0,0 +1,36 @@
# 切换标签前的钩子函数
```html
<n-tabs
type="line"
@before-leave="handleBeforeLeave"
@update:value="handleUpdateValue"
>
<n-tab-pane name="oasis" tab="Oasis"> Oasis </n-tab-pane>
<n-tab-pane name="oasi11s" tab="Oasis111"> Oasis </n-tab-pane>
<n-tab-pane name="the beatles" tab="the Beatles">the beatles</n-tab-pane>
<n-tab-pane name="jay chou" tab="My Favour">My faviur</n-tab-pane>
</n-tabs>
```
```js
import { defineComponent, ref } from 'vue'
import { useMessage } from 'naive-ui'
export default defineComponent({
setup () {
const message = useMessage()
const OasisRef = ref('正在加载中...')
return {
Oasis: OasisRef,
handleBeforeLeave: (tab, oldTab) => {
if (tab === 'jay chou') message.info('Can not leave!')
return tab !== 'jay chou'
},
handleUpdateValue: (activeName) => {
message.info(activeName)
}
}
}
})
```

View File

@ -16,6 +16,7 @@ addable
line-debug
style-inherit-debug
shadow-debug
before-leave
```
## API
@ -35,6 +36,7 @@ shadow-debug
| type | `'bar' \| 'line' \| 'card' \| 'segment'` | `'bar'` | 标签类型 |
| value | `string \| number` | `undefined` | 受控模式下的值 |
| on-add | `() => void` | `undefined` | 添加标签的回调函数 |
| on-before-leave | `(activeName: string \| number, oldActiveName: string \| number \| null) => boolean \| Promise<boolean>` | `undefined` | 切换标签之前的钩子函数 |
| on-close | `(name: string \| number) => void` | `undefined` | 关闭标签的回调函数 |
| on-update:value | `(value: string \| number) => void` | `undefined` | 选中发生改变时的回调函数 |

View File

@ -21,8 +21,11 @@ export default defineComponent({
typeRef,
closableRef,
tabStyleRef,
tabIdRef,
handleAdd,
handleBeforeLeave,
handleTabClick,
setTabId,
handleClose
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
} = inject(tabsInjectionKey)!
@ -48,7 +51,28 @@ export default defineComponent({
handleAdd()
return
}
handleTabClick(props.name)
const id = tabIdRef.value ? tabIdRef.value.id + 1 : 1
setTabId(props.name, id)
if (props.name !== valueRef.value) {
const result = handleBeforeLeave(props.name, valueRef.value)
if (typeof result === 'boolean') {
if (result) handleTabClick(props.name)
} else {
result.then(
() => {
if (
tabIdRef.value.tab === props.name &&
tabIdRef.value.id === id
) {
handleTabClick(props.name)
}
},
() => {
// not allowed to change tab
}
)
}
}
}
}
},

View File

@ -28,6 +28,8 @@ import {
Addable,
OnClose,
OnCloseImpl,
BeforeLeave,
BeforeLeaveImpl,
tabsInjectionKey,
TabsType
} from './interface'
@ -58,6 +60,7 @@ const tabsProps = {
type: Number,
default: 0
},
onBeforeLeave: [Function, Array] as PropType<MaybeArray<BeforeLeave>>,
onAdd: Function as PropType<() => void>,
'onUpdate:value': [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
onUpdateValue: [Function, Array] as PropType<MaybeArray<OnUpdateValue>>,
@ -135,6 +138,10 @@ export default defineComponent({
compitableValueRef,
uncontrolledValueRef
)
const tabIdRef = ref<{ tab: string | number | null, id: number }>({
tab: mergedValueRef.value,
id: 0
})
const tabWrapperStyleRef = computed(() => {
if (!props.justifyContent || props.type === 'card') return undefined
@ -177,9 +184,20 @@ export default defineComponent({
updateBarStyle(tabEl)
}
}
function handleBeforeLeave (
activeName: string | number,
oldActiveName: string | number | null
): boolean | Promise<boolean> {
const { onBeforeLeave } = props
if (!onBeforeLeave) return true
return (onBeforeLeave as BeforeLeaveImpl)(activeName, oldActiveName)
}
function handleTabClick (panelName: string | number): void {
doUpdateValue(panelName)
}
function setTabId (tab: string | number, id: number): void {
tabIdRef.value = { tab, id }
}
function doUpdateValue (panelName: string | number): void {
const {
onActiveNameChange,
@ -275,7 +293,10 @@ export default defineComponent({
typeRef: toRef(props, 'type'),
closableRef: toRef(props, 'closable'),
valueRef: mergedValueRef,
tabIdRef,
handleBeforeLeave,
handleTabClick,
setTabId,
handleClose,
handleAdd
})

View File

@ -8,6 +8,15 @@ export type OnUpdateValueImpl = (value: string | number) => void
export type OnClose = (name: string & number) => void
export type OnCloseImpl = (name: string | number) => void
export type BeforeLeave = (
activeName: string & number,
oldActiveName: string & number & null
) => boolean | Promise<boolean>
export type BeforeLeaveImpl = (
activeName: string | number,
oldActiveName: string | number | null
) => boolean | Promise<boolean>
export interface TabsInjection {
mergedClsPrefixRef: Ref<string>
valueRef: Ref<string | number | null>
@ -15,7 +24,10 @@ export interface TabsInjection {
closableRef: Ref<boolean>
tabStyleRef: Ref<string | CSSProperties | undefined>
paneStyleRef: Ref<string | CSSProperties | undefined>
tabIdRef: Ref<{ tab: string | number | null, id: number }>
handleBeforeLeave: BeforeLeaveImpl
handleTabClick: (panelName: string | number) => void
setTabId: (tab: string | number, id: number) => void
handleClose: (panelName: string | number) => void
handleAdd: () => void
}