refactor(checkbox): support vue3

This commit is contained in:
07akioni 2020-09-28 14:18:14 +08:00
parent aa61c7c5f6
commit b460a9f4b8
15 changed files with 217 additions and 178 deletions

View File

@ -1,13 +1,13 @@
# 基础用法
```html
<n-checkbox v-model="value">复选框</n-checkbox>
<n-checkbox v-model="value"/>
<n-checkbox v-model="value" :disabled="disabled">复选框</n-checkbox>
<n-checkbox v-model:checked="value">复选框</n-checkbox>
<n-checkbox v-model:checked="value"/>
<n-checkbox v-model:checked="value" :disabled="disabled">复选框</n-checkbox>
<n-button @click="disabled = !disabled" size="small">禁用</n-button>
```
```js
export default {
data() {
data () {
return {
value: false,
disabled: true

View File

@ -1,11 +1,11 @@
# 受控状态
```html
<n-checkbox :checked="value">复选框</n-checkbox>
<n-switch v-model="value"/>
<n-switch v-model:value="value"/>
```
```js
export default {
data() {
data () {
return {
value: false
}

View File

@ -1,7 +1,7 @@
# 事件
```html
<n-checkbox v-model="value" @change="handleChange" label="事件" />
<n-checkbox-group v-model="cities" @change="handleChange">
<n-checkbox :checked="checked" @update:checked="handleCheckedChange" label="事件" />
<n-checkbox-group :value="cities" @update:value="handleUpdateValue">
<n-checkbox value="Beijing" label="北京" />
<n-checkbox value="Shanghai" label="上海" />
<n-checkbox value="Guangzhou" label="广州" />
@ -10,15 +10,21 @@
```
```js
export default {
inject: ['message'],
data () {
return {
value: false,
checked: false,
cities: null
}
},
methods: {
handleChange (v) {
this.$NMessage.info(JSON.stringify(v))
handleCheckedChange (checked) {
this.checked = checked
this.message.info(JSON.stringify(checked))
},
handleUpdateValue (value) {
this.cities = value
this.message.info(JSON.stringify(value))
}
}
}

View File

@ -1,7 +1,7 @@
# 栅格
和栅格一起使用。
```html
<n-checkbox-group v-model="value">
<n-checkbox-group v-model:value="value">
<n-row>
<n-col :span="12">
<n-checkbox value="Prosperity" label="富强" />

View File

@ -1,6 +1,6 @@
# 选项组
```html
<n-checkbox-group v-model="cities">
<n-checkbox-group v-model:value="cities">
<n-checkbox value="Beijing" label="北京" />
<n-checkbox value="Shanghai" label="上海" />
<n-checkbox value="Guangzhou" label="广州" />

View File

@ -1,8 +1,8 @@
# 部分选中
```html
<n-checkbox v-model="value" :indeterminate="indeterminate">复选框</n-checkbox>
<n-checkbox v-model="value" :indeterminate="indeterminate"/>
<n-checkbox v-model="value" :indeterminate="indeterminate" disabled/>
<n-checkbox v-model:checked="value" :indeterminate="indeterminate">复选框</n-checkbox>
<n-checkbox v-model:checked="value" :indeterminate="indeterminate"/>
<n-checkbox v-model:checked="value" :indeterminate="indeterminate" disabled/>
<n-button @click="value = !value" size="small">选中</n-button>
<n-button @click="indeterminate = !indeterminate" size="small">部分选中</n-button>
```

View File

@ -1,5 +1,6 @@
# 复选框 Checkbox
Check it out。
## 演示
```demo
basic
@ -9,16 +10,6 @@ indeterminate
controlled
event
```
## V-model
### Checkbox V-model
|Prop|Event|
|-|-|
|change|checked|
### Checkbox Group V-model
|Prop|Event|
|-|-|
|change|value|
## Props
### Checkbox Props
@ -29,6 +20,7 @@ event
|checked|`boolean`|`false`||
|disabled|`boolean`|`false`||
|label|`string \| function`|`null`|可以是渲染函数|
|on-update:checked|`(checked: boolean)`|`undefined`||
### Checkbox Group Props
|名称|类型|默认值|说明|
@ -36,6 +28,7 @@ event
|theme|`'light' \| 'dark' \| null \| string`|`null`||
|value|`Array<string \| number>`|`null`||
|disabled|`boolean`|`false`||
|on-update:value|`(value: string \| number)`|`undefined`||
## Slots
### Checkbox Slots
@ -53,14 +46,3 @@ event
|名称|参数|说明|
|-|-|-|
|default|`()`||
## Events
### Checkbox Events
|名称|参数|说明|
|-|-|-|
|change|`(checked: boolean)`||
### Checkbox Group Events
|名称|参数|说明|
|-|-|-|
|change|`(value: string \| number)`||

View File

@ -102,7 +102,8 @@ export function onFontReady (callback) {
}
export function useInjectionCollection (injectionName, collectionKey, valueRef) {
const injection = inject(injectionName)
const injection = inject(injectionName, null)
if (injection === null) return
if (!(collectionKey in injection)) {
injection[collectionKey] = []
}

View File

@ -1,6 +1,6 @@
/* istanbul ignore file */
import NCheckbox from './src/Checkbox.vue'
import NCheckboxGroup from './src/CheckboxGroup.vue'
import NCheckboxGroup from './src/CheckboxGroup.js'
NCheckbox.install = function (app, naive) {
app.component(naive.componentPrefix + NCheckbox.name, NCheckbox)

View File

@ -51,17 +51,18 @@
</template>
<script>
import { computed, inject } from 'vue'
import withapp from '../../_mixins/withapp'
import themeable from '../../_mixins/themeable'
import asformitem from '../../_mixins/asformitem'
import collectable from '../../_mixins/collectable'
import simulatedComputed from '../../_mixins/simulatedComputed'
import render from '../../_utils/vue/render'
import CheckMark from './CheckMark'
import LineMark from './LineMark'
import NIconSwitchTransition from '../../_transition/IconSwitchTransition'
import usecssr from '../../_mixins/usecssr'
import styles from './styles'
import { useMemo } from '../../_utils/composition'
import { warn } from '../../_utils/naive/warn'
export default {
name: 'Checkbox',
@ -79,14 +80,9 @@ export default {
mixins: [
withapp,
themeable,
asformitem(
{
change: 'change',
blur: 'blur',
focus: 'focus'
},
'medium',
function () {
asformitem({
defaultSize: 'medium',
syntheticSize () {
const size = this.size
if (size) return size
const NCheckboxGroup = this.NCheckboxGroup
@ -103,25 +99,9 @@ export default {
}
return 'medium'
}
),
collectable('NCheckboxGroup', 'collectedCheckboxValues'),
simulatedComputed({
renderSafeChecked: {
default: false,
get () {
return this.syntheticChecked
},
deps: [
'syntheticChecked'
]
}
}),
usecssr(styles)
],
model: {
prop: 'checked',
event: 'change'
},
props: {
size: {
validator (value) {
@ -145,50 +125,81 @@ export default {
type: Boolean,
default: false
},
label: {
type: [String, Function],
default: null
},
onClick: {
type: Function,
default: undefined
},
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:checked': {
type: Function,
default: undefined
},
// private
tableHeader: {
type: Boolean,
default: false
},
label: {
type: [String, Function],
default: null
// deprecated
onChange: {
validator () {
warn('checkbox', '`on-change` is deprecated, please use `on-update:checked` instead.')
return true
},
default: undefined
}
},
setup (props) {
const NCheckboxGroup = inject('NCheckboxGroup', null)
const syntheticCheckedRef = computed(() => {
if (NCheckboxGroup) {
const groupValueSet = NCheckboxGroup.valueSet
if (groupValueSet) {
return groupValueSet.has(props.value)
}
return false
} else {
return props.checked
}
})
return {
renderSafeChecked: useMemo(() => syntheticCheckedRef.value, [
syntheticCheckedRef
])
}
},
computed: {
syntheticDisabled () {
if (this.disabled || (this.NCheckboxGroup && this.NCheckboxGroup.disabled)) return true
return false
},
syntheticChecked () {
if (this.NCheckboxGroup) {
const checkboxGroupValueSet = this.NCheckboxGroup.valueAsSet
if (checkboxGroupValueSet) {
return checkboxGroupValueSet.has(this.value)
}
return false
} else {
return this.checked
}
}
},
methods: {
registerValue (value = undefined, oldValue = undefined) {
if (this.NCheckboxGroup) {
const values = new Set(this.NCheckboxGroup.collectedCheckboxValues)
if (oldValue !== undefined) values.delete(oldValue)
if (value !== undefined) values.add(value)
this.NCheckboxGroup.collectedCheckboxValues = Array.from(values)
}
},
toggle () {
if (this.NCheckboxGroup) {
this.NCheckboxGroup.toggleCheckbox(!this.syntheticChecked, this.value)
this.NCheckboxGroup.toggleCheckbox(!this.renderSafeChecked, this.value)
} else {
this.$emit('change', !this.syntheticChecked, this.syntheticChecked)
const {
onChange,
'onUpdate:checked': onUpdateChecked,
__triggerFormInput,
__triggerFormChange
} = this
const nextChecked = !this.renderSafeChecked
if (onUpdateChecked) onUpdateChecked(nextChecked)
if (onChange) onChange(nextChecked) // deprecated
__triggerFormInput()
__triggerFormChange()
}
},
handleClick (e) {
this.$emit('click', e)
const {
onClick
} = this
if (onClick) onClick(e)
if (!this.syntheticDisabled) {
this.toggle()
}

View File

@ -0,0 +1,104 @@
import { h } from 'vue'
import withapp from '../../_mixins/withapp'
import themeable from '../../_mixins/themeable'
import asformitem from '../../_mixins/asformitem'
import { getSlot } from '../../_utils/vue'
import { warn } from '../../_utils/naive/warn'
export default {
name: 'CheckboxGroup',
mixins: [
withapp,
themeable,
asformitem()
],
provide () {
return {
NCheckboxGroup: this
}
},
props: {
size: {
validator (value) {
return ['small', 'medium', 'large'].includes(value)
},
default: null
},
value: {
type: Array,
default: null
},
disabled: {
type: Boolean,
default: false
},
// eslint-disable-next-line vue/prop-name-casing
'onUpdate:value': {
type: Function,
default: undefined
},
// deprecated
onChange: {
validator () {
if (__DEV__) warn('checkbox-group', '`on-change` is deprecated, please use `on-update:value` instead.')
return true
},
default: undefined
}
},
computed: {
valueSet () {
if (Array.isArray(this.value)) return new Set(this.value)
return null
}
},
methods: {
toggleCheckbox (checked, checkboxValue) {
const {
onChange,
'onUpdate:value': onUpdateValue,
__triggerFormInput,
__triggerFormChange
} = this
if (Array.isArray(this.value)) {
let groupValue = Array.from(this.value)
const index = groupValue.findIndex(value => value === checkboxValue)
if (checked) {
if (!~index) {
groupValue.push(checkboxValue)
if (onUpdateValue) onUpdateValue(groupValue)
__triggerFormInput()
__triggerFormChange()
// deprecated
if (onChange) onChange(groupValue)
}
} else {
if (~index) {
groupValue.splice(index, 1)
if (onUpdateValue) onUpdateValue(groupValue)
if (onChange) onChange(groupValue) // deprecated
__triggerFormInput()
__triggerFormChange()
}
}
} else {
if (checked) {
if (onUpdateValue) onUpdateValue([checkboxValue])
if (onChange) onChange([checkboxValue]) // deprecated
__triggerFormInput()
__triggerFormChange()
} else {
if (onUpdateValue) onUpdateValue([])
if (onChange) onChange([]) // deprecated
__triggerFormInput()
__triggerFormChange()
}
}
}
},
render () {
return h('div', {
class: 'n-checkbox-group'
}, getSlot(this))
}
}

View File

@ -1,84 +0,0 @@
<script>
import withapp from '../../_mixins/withapp'
import themeable from '../../_mixins/themeable'
import asformitem from '../../_mixins/asformitem'
import getDefaultSlot from '../../_utils/vue/getDefaultSlot'
export default {
name: 'CheckboxGroup',
mixins: [
withapp,
themeable,
asformitem()
],
provide () {
return {
NFormItem: null,
NCheckboxGroup: this
}
},
model: {
prop: 'value',
event: 'change'
},
props: {
size: {
validator (value) {
return ['small', 'medium', 'large'].includes(value)
},
default: null
},
value: {
type: Array,
default: null
},
disabled: {
type: Boolean,
default: false
}
},
data () {
return {
collectedCheckboxValues: []
}
},
computed: {
valueAsSet () {
if (Array.isArray(this.value)) return new Set(this.value)
return null
}
},
methods: {
toggleCheckbox (checked, checkboxValue) {
if (Array.isArray(this.value)) {
let groupValue = Array.from(this.value)
const collectedCheckboxValues = new Set(this.collectedCheckboxValues)
groupValue = groupValue.filter(value => collectedCheckboxValues.has(value))
const index = groupValue.findIndex(value => value === checkboxValue)
if (checked) {
if (!~index) {
groupValue.push(checkboxValue)
this.$emit('change', groupValue)
}
} else {
if (~index) {
groupValue.splice(index, 1)
this.$emit('change', groupValue)
}
}
} else {
if (checked) {
this.$emit('change', [checkboxValue])
} else {
this.$emit('change', [])
}
}
}
},
render (h) {
return h('div', {
staticClass: 'n-checkbox-group'
}, getDefaultSlot(this))
}
}
</script>

View File

@ -88,11 +88,22 @@ export default {
theme: {
validator: createValidator(['string']),
default: null
},
onClick: {
validator: createValidator(['object']),
default: undefined
},
onChange: {
validator: createValidator(['object']),
default: undefined
}
},
methods: {
handleClick (e) {
this.$emit('click', e)
const {
onClick
} = this
if (onClick) onClick(e)
if (!this.disabled) {
this.toggle()
}
@ -103,7 +114,10 @@ export default {
}
},
toggle () {
this.$emit('change', !this.checked, this.checked)
const {
onChange
} = this
if (onChange) onChange(!this.checked)
},
handleKeyDownSpace (e) {
e.preventDefault()

View File

@ -59,7 +59,7 @@
</template>
<script>
import NCheckboxGroup from '../../../checkbox/src/CheckboxGroup'
import NCheckboxGroup from '../../../checkbox/src/CheckboxGroup.js'
import NCheckbox from '../../../checkbox/src/Checkbox'
import NRadioGroup from '../../../radio/src/RadioGroup'
import NRadio from '../../../radio/src/Radio'

View File

@ -29,7 +29,12 @@ placeable 进行了大调整
- [x] button-group
- [x] card
- [ ] cascader
- [ ] checkbox
- [x] checkbox
- deprecate
- `on-change` => `on-update:checked`
- checkbox-group
- deprecate
- `on-change` => `on-update:value`
- [x] code
- [x] collapse
- deprecate