refactor(collapse): support vue3

This commit is contained in:
07akioni 2020-09-19 00:10:16 +08:00
parent cf33f6c586
commit c016bad589
14 changed files with 129 additions and 106 deletions

View File

@ -1,7 +1,7 @@
# 手风琴
像一个手风琴
```html
<n-collapse v-model="activeNames" accordion>
<n-collapse v-model:expandedNames="activeNames" accordion>
<n-collapse-item title="动态类型" name="1">
<div>Python</div>
</n-collapse-item>

View File

@ -2,7 +2,7 @@
使用 `arrow-placement` 来设定箭头的位置。
```html
<n-collapse
v-model="activeNames"
v-model:expandedNames="activeNames"
arrow-placement="right"
>
<n-collapse-item title="青铜" name="1">

View File

@ -1,6 +1,6 @@
# 基础
```html
<n-collapse v-model="activeNames">
<n-collapse v-model:expandedNames="activeNames">
<n-collapse-item title="青铜" name="1">
<div>可以</div>
</n-collapse-item>

View File

@ -1,9 +1,9 @@
# 显示指令
设定 `display-directive``if``show` 来控制 `n-collapse-item` 里面的 DOM 是否保持。
```html
<n-collapse v-model="activeNames" display-directive="show">
<n-collapse v-model:expandedNames="activeNames" display-directive="show">
<n-collapse-item title="绿灯" name="1">
<n-collapse v-model="activeNames2">
<n-collapse v-model:expandedNames="activeNames2">
<n-collapse-item title="常亮" name="1">
<div>通过</div>
</n-collapse-item>

View File

@ -9,11 +9,6 @@ nested
display-directive
item-header-click
```
## V-model
|Prop|Event|
|-|-|
|expanded-names|expanded-names-change|
## Props
### Collapse Props
|名称|类型|默认值|说明|
@ -23,6 +18,8 @@ item-header-click
|display-directive|`'if' \| 'show'`|`'if'`|内部 `n-collapse-item` 在控制内容是否渲染时使用的指令,`'if'` 对应 `v-if``'show'` 对应 `v-show`|
|expanded-names|`Array<string \| number>`|`null`||
|theme|`'light' \| 'dark' \| null \| string`|`null`||
|on-update-expanded-names|`(expandedNames: Array<string>) => any`|`() => {}`||
|on-item-header-click|`(data: { name: string, expanded: boolean, event: MouseEvent }) => any`|`() => {}`||
### Collapse Item Props
@ -45,9 +42,3 @@ item-header-click
|header|`()`||
|arrow|`(options: { collapsed: boolean })`||
## Event
### Collapse Event
|名称|参数|说明|
|-|-|-|
|expanded-names-change|`(expandedNames: Array<string>)`||
|item-header-click|`(data: { name: string, expanded: boolean, event: MouseEvent })`||

View File

@ -1,7 +1,7 @@
# 点击标题
```html
<n-collapse
v-model="activeNames"
v-model:expandedNames="activeNames"
@item-header-click="handleItemHeaderClick"
>
<n-collapse-item name="1">
@ -26,6 +26,7 @@
```
```js
export default {
inject: ['message'],
data () {
return {
activeNames: []
@ -36,7 +37,7 @@ export default {
name,
expanded
}) {
this.$NMessage.info(`Name: ${name}, Expanded: ${expanded}`)
this.message.info(`Name: ${name}, Expanded: ${expanded}`)
}
}
}

View File

@ -1,9 +1,9 @@
# 嵌套
可以嵌套。
```html
<n-collapse v-model="activeNames">
<n-collapse v-model:expandedNames="activeNames">
<n-collapse-item title="绿灯" name="1">
<n-collapse v-model="activeNames2">
<n-collapse v-model:expandedNames="activeNames2">
<n-collapse-item title="常亮" name="1">
<div>通过</div>
</n-collapse-item>

View File

@ -28,8 +28,13 @@ body {
max-width: 680px;
}
.api-table .n-table__td:first-child {
.api-table .n-table__td:nth-child(1),
.api-table .n-table__td:nth-child(2) {
font-size: 13px;
white-space: nowrap;
font-family: naive-ui-mono, monospace;
}
.api-table .n-text.n-text--code {
font-size: 13px;
}

View File

@ -1,5 +1,5 @@
/* istanbul ignore file */
import Collapse from './src/Collapse.vue'
import Collapse from './src/Collapse.js'
import CollapseItem from './src/CollapseItem.vue'
Collapse.install = function (app, naive) {

View File

@ -1,8 +1,7 @@
<script>
import { h, markRaw } from 'vue'
import intersection from 'lodash-es/intersection'
import withapp from '../../_mixins/withapp'
import themeable from '../../_mixins/themeable'
import getDefaultSlot from '../../_utils/vue/getDefaultSlot'
import usecssr from '../../_mixins/usecssr'
import styles from './styles/index.js'
@ -18,10 +17,6 @@ export default {
themeable,
usecssr(styles)
],
model: {
prop: 'expandedNames',
event: 'expanded-names-change'
},
props: {
expandedNames: {
type: [Array, String],
@ -42,50 +37,73 @@ export default {
return ['if', 'show'].includes(value)
},
default: 'if'
},
onItemHeaderClick: {
type: Function,
default: () => {}
},
'onUpdate:expandedNames': {
type: Function,
default: () => {}
},
// deprecated
onExpandedNamesChange: {
type: Function,
default: () => {}
}
},
data () {
setup () {
return {
collectedItemNames: []
collectedItemNames: markRaw([])
}
},
methods: {
toggleItem (collapse, name, event) {
if (this.accordion) {
if (collapse) {
this.$emit('expanded-names-change', [name])
this.$emit('item-header-click', { name, expanded: true, event })
this['onUpdate:expandedNames']([name])
this.onExpandedNamesChange([name])
this.onItemHeaderClick({ name, expanded: true, event })
} else {
this.$emit('expanded-names-change', [])
this.$emit('item-header-click', { name, expanded: false, event })
this['onUpdate:expandedNames']([])
this.onExpandedNamesChange([])
this.onItemHeaderClick({ name, expanded: false, event })
}
} else {
if (!Array.isArray(this.expandedNames)) {
this.$emit('expanded-names-change', [name])
this.$emit('item-header-click', { name, expanded: true, event })
this['onUpdate:expandedNames']([name])
this.onExpandedNamesChange([name])
this.onItemHeaderClick({ name, expanded: true, event })
} else {
const activeNames = intersection(this.expandedNames, this.collectedItemNames)
const index = activeNames.findIndex(activeName => name === activeName)
if (~index) {
activeNames.splice(index, 1)
this.$emit('expanded-names-change', activeNames)
this.$emit('item-header-click', { name, expanded: false, event })
this['onUpdate:expandedNames'](activeNames)
this.onExpandedNamesChange(activeNames)
this.onItemHeaderClick({ name, expanded: false, event })
} else {
activeNames.push(name)
this.$emit('expanded-names-change', activeNames)
this.$emit('item-header-click', { name, expanded: true, event })
this['onUpdate:expandedNames'](activeNames)
this.onExpandedNamesChange(activeNames)
this.onItemHeaderClick({ name, expanded: true, event })
}
}
}
}
},
render (h) {
render () {
const { syntheticTheme } = this
return h('div', {
staticClass: 'n-collapse',
class: {
[`n-${this.syntheticTheme}-theme`]: this.syntheticTheme
}
}, getDefaultSlot(this))
class: [
'n-collapse',
{
[`n-${syntheticTheme}-theme`]: syntheticTheme
}
]
}, {
...this.$slots
})
}
}
</script>

View File

@ -19,7 +19,7 @@
<div class="n-collapse-item-arrow">
<slot name="arrow" :collapsed="collapsed">
<n-icon type="ios-arrow-forward">
<ios-arrow-forward />
<arrow-icon />
</n-icon>
</slot>
</div>
@ -37,19 +37,19 @@
</template>
<script>
import { toRef } from 'vue'
import NIcon from '../../icon'
import iosArrowForward from '../../_icons/ios-arrow-forward'
import collectable from '../../_mixins/collectable'
import NCollapseItemContent from './CollapseItemContent'
import ArrowIcon from '../../_icons/ios-arrow-forward'
import NCollapseItemContent from './CollapseItemContent.js'
import { useInjectionCollection } from '../../_utils/composition'
export default {
name: 'CollapseItem',
components: {
NIcon,
NCollapseItemContent,
iosArrowForward
ArrowIcon
},
mixins: [collectable('NCollapse', 'collectedItemNames', 'name')],
inject: {
NCollapse: {
default: null
@ -71,20 +71,23 @@ export default {
default: null
}
},
setup (props) {
useInjectionCollection('NCollapse', 'collectedItemNames', toRef(props, 'name'))
},
computed: {
syntheticDisplayDirective () {
const displayDirective = this.displayDirective
const { displayDirective, NCollapse } = this
if (displayDirective !== null) {
return this.NCollapse.displayDirective
} else {
return displayDirective
} else {
return NCollapse.displayDirective
}
},
arrowPlacement () {
return this.NCollapse.arrowPlacement
},
collapsed () {
const NCollapse = this.NCollapse
const { NCollapse } = this
if (NCollapse && Array.isArray(NCollapse.expandedNames)) {
const itemName = this.name
return !~NCollapse.expandedNames.findIndex(name => name === itemName)
@ -94,9 +97,13 @@ export default {
},
methods: {
handleClick (e) {
const NCollapse = this.NCollapse
const { NCollapse } = this
if (NCollapse) {
NCollapse.toggleItem(this.collapsed, this.name, e)
NCollapse.toggleItem(
this.collapsed,
this.name,
e
)
}
}
}

View File

@ -0,0 +1,43 @@
import { h, withDirectives, vShow } from 'vue'
import NFadeInHeightExpandTransition from '../../_transition/FadeInHeightExpandTransition'
export default {
name: 'NCollapseItemContent',
props: {
displayDirective: {
validator () {
// already validated outside
return true
},
required: true
},
show: {
validator () {
// already validated outside
return true
},
required: true
}
},
render () {
const { show, displayDirective } = this
const useVShow = displayDirective === 'show'
const directives = useVShow ? [
[vShow, show]
] : []
return h(NFadeInHeightExpandTransition, null, {
default: () => (useVShow || show) ? withDirectives(
h('div', {
class: 'n-collapse-item__content-wrapper'
}, [
h('div', {
class: 'n-collapse-item__content-inner'
}, {
...this.$slots
})
]),
directives
) : null
})
}
}

View File

@ -1,46 +0,0 @@
<script>
import NFadeInHeightExpandTransition from '../../_transition/FadeInHeightExpandTransition'
import getDefaultSlot from '../../_utils/vue/getDefaultSlot'
export default {
name: 'NCollapseItemContent',
components: {
NFadeInHeightExpandTransition
},
props: {
displayDirective: {
validator (value) {
// already validated outside
return true
},
required: true
},
show: {
validator (value) {
// already validated outside
return true
},
required: true
}
},
render (h) {
const defaultSlot = getDefaultSlot(this)
const vIf = this.displayDirective === 'if'
return h('n-fade-in-height-expand-transition', (vIf && !this.show) ? [] : [
h('div', {
staticClass: 'n-collapse-item__content-wrapper',
directives: [
!vIf ? {
name: 'show',
value: this.show
} : null
].filter(v => v)
}, [
h('div', {
staticClass: 'n-collapse-item__content-inner'
}, defaultSlot)
])
])
}
}
</script>

View File

@ -27,7 +27,11 @@ placeable 进行了大调整
- [ ] cascader
- [ ] checkbox
- [ ] code
- [ ] collapse
- [x] collapse
- deprecate
- `on-expanded-names-change` => `on-update:expanded-names`
- removed
- `v-model` => `v-model:expanded-names`
- [ ] config-consumer
- [ ] config-provider
- [ ] confirm