feat(Checkbox): add indeterminate status (#12216)

* feat(Checkbox): add indeterminate status

* chore: update

* chore: update test

* chore: update

* chore: update

* chore: update
This commit is contained in:
Gavin 2023-08-27 15:21:44 +08:00 committed by GitHub
parent 26fd87930b
commit 37f4500e3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 378 additions and 4 deletions

View File

@ -25,6 +25,10 @@ const [name, bem] = createNamespace('checkbox');
export const checkboxProps = extend({}, checkerProps, {
shape: String as PropType<CheckerShape>,
bindGroup: truthProp,
indeterminate: {
type: Boolean as PropType<boolean | null>,
default: null,
},
});
export type CheckboxProps = ExtractPropTypes<typeof checkboxProps>;
@ -80,11 +84,15 @@ export default defineComponent({
} else {
emit('update:modelValue', newValue);
}
if (props.indeterminate !== null) emit('change', newValue);
};
watch(
() => props.modelValue,
(value) => emit('change', value),
(value) => {
if (props.indeterminate === null) emit('change', value);
},
);
useExpose<CheckboxExpose>({ toggle, props, checked });

View File

@ -45,6 +45,10 @@ export default defineComponent({
parent: Object as PropType<CheckerParent | null>,
checked: Boolean,
bindGroup: truthProp,
indeterminate: {
type: Boolean as PropType<boolean | null>,
default: null,
},
}),
emits: ['click', 'toggle'],
@ -106,7 +110,7 @@ export default defineComponent({
};
const renderIcon = () => {
const { bem, checked } = props;
const { bem, checked, indeterminate } = props;
const iconSize = props.iconSize || getParentProp('iconSize');
return (
@ -114,7 +118,7 @@ export default defineComponent({
ref={iconRef}
class={bem('icon', [
shape.value,
{ disabled: disabled.value, checked },
{ disabled: disabled.value, checked, indeterminate },
])}
style={
shape.value !== 'dot'
@ -129,7 +133,10 @@ export default defineComponent({
{slots.icon ? (
slots.icon({ checked, disabled: disabled.value })
) : shape.value !== 'dot' ? (
<Icon name="success" style={iconStyle.value} />
<Icon
name={indeterminate ? 'minus' : 'success'}
style={iconStyle.value}
/>
) : (
<div
class={bem('icon--dot__icon')}

View File

@ -265,6 +265,58 @@ export default {
};
```
### indeterminate
```html
<van-checkbox
v-model="isCheckAll"
:indeterminate="isIndeterminate"
@change="checkAllChange"
>
Check All
</van-checkbox>
<van-checkbox-group v-model="checkedResult" @change="checkedResultChange">
<van-checkbox v-for="item in list" :key="item" :name="item">
Checkbox {{ item }}
</van-checkbox>
</van-checkbox-group>
```
```js
import { ref } from 'vue';
export default {
setup() {
const list = ['a', 'b', 'c', 'd']
const isCheckAll = ref(false);
const checkedResult = ref(['a', 'b', 'd']);
const isIndeterminate = ref(true);
const checkAllChange = (val: boolean) => {
checkedResult.value = val ? list : []
isIndeterminate.value = false
}
const checkedResultChange = (value: string[]) => {
const checkedCount = value.length
isCheckAll.value = checkedCount === list.length
isIndeterminate.value = checkedCount > 0 && checkedCount < list.length
}
return {
list,
isCheckAll,
checkedResult,
checkAllChange,
isIndeterminate,
checkedResultChange
};
},
};
```
## API
### Checkbox Props
@ -280,6 +332,7 @@ export default {
| icon-size | Icon size | _number \| string_ | `20px` |
| checked-color | Checked color | _string_ | `#1989fa` |
| bind-group | Whether to bind with CheckboxGroup | _boolean_ | `true` |
| indeterminate | Whether indeterminate status | _boolean_ | `false` |
### CheckboxGroup Props

View File

@ -282,6 +282,60 @@ export default {
};
```
### 不确定状态
通过 `indeterminate` 设置复选框是否为不确定状态。
```html
<van-checkbox
v-model="isCheckAll"
:indeterminate="isIndeterminate"
@change="checkAllChange"
>
全选
</van-checkbox>
<van-checkbox-group v-model="checkedResult" @change="checkedResultChange">
<van-checkbox v-for="item in list" :key="item" :name="item">
复选框 {{ item }}
</van-checkbox>
</van-checkbox-group>
```
```js
import { ref } from 'vue';
export default {
setup() {
const list = ['a', 'b', 'c', 'd']
const isCheckAll = ref(false);
const checkedResult = ref(['a', 'b', 'd']);
const isIndeterminate = ref(true);
const checkAllChange = (val: boolean) => {
checkedResult.value = val ? list : []
isIndeterminate.value = false
}
const checkedResultChange = (value: string[]) => {
const checkedCount = value.length
isCheckAll.value = checkedCount === list.length
isIndeterminate.value = checkedCount > 0 && checkedCount < list.length
}
return {
list,
isCheckAll,
checkedResult,
checkAllChange,
isIndeterminate,
checkedResultChange
};
},
};
```
## API
### Checkbox Props
@ -297,6 +351,7 @@ export default {
| icon-size | 图标大小,默认单位为 `px` | _number \| string_ | `20px` |
| checked-color | 选中状态颜色 | _string_ | `#1989fa` |
| bind-group | 是否与复选框组绑定 | _boolean_ | `true` |
| indeterminate | 是否为不确定状态 | _boolean_ | `false` |
### CheckboxGroup Props

View File

@ -26,6 +26,7 @@ const t = useTranslate({
inverse: '反选',
horizontal: '水平排列',
disableLabel: '禁用文本点击',
indeterminate: '不确定状态',
},
'en-US': {
checkbox: 'Checkbox',
@ -42,6 +43,7 @@ const t = useTranslate({
inverse: 'Inverse',
horizontal: 'Horizontal',
disableLabel: 'Disable label click',
indeterminate: 'indeterminate',
},
});
@ -49,6 +51,8 @@ const state = reactive({
checkbox1: true,
checkbox2: true,
checkbox3: true,
isCheckAll: false,
isIndeterminate: true,
checkboxLabel: true,
checkboxIcon: true,
leftLabel: false,
@ -57,10 +61,13 @@ const state = reactive({
checkboxShape: ['a', 'b'],
result2: [],
result3: [],
result4: ['a', 'b', 'd'],
checkAllResult: [],
horizontalResult: [],
});
const list = ['a', 'b', 'c', 'd'];
const activeIcon = cdnURL('user-active.png');
const inactiveIcon = cdnURL('user-inactive.png');
@ -78,6 +85,17 @@ const checkAll = () => {
const toggleAll = () => {
group.value?.toggleAll();
};
const checkAllChange = (val: boolean) => {
state.result4 = val ? list : [];
state.isIndeterminate = false;
};
const checkedResultChange = (value: string[]) => {
const checkedCount = value.length;
state.isCheckAll = checkedCount === list.length;
state.isIndeterminate = checkedCount > 0 && checkedCount < list.length;
};
</script>
<template>
@ -190,6 +208,22 @@ const toggleAll = () => {
</van-cell-group>
</van-checkbox-group>
</demo-block>
<demo-block :title="t('indeterminate')">
<van-checkbox
v-model="state.isCheckAll"
:indeterminate="state.isIndeterminate"
@change="checkAllChange"
>
{{ t('checkAll') }}
</van-checkbox>
<div class="divider" />
<van-checkbox-group v-model="state.result4" @change="checkedResultChange">
<van-checkbox v-for="item in list" :key="item" :name="item">
{{ t('checkbox') }} {{ item }}
</van-checkbox>
</van-checkbox-group>
</demo-block>
</template>
<style lang="less">
@ -220,4 +254,10 @@ const toggleAll = () => {
margin-top: -8px;
}
}
.divider {
margin: 20px;
height: 1px;
background: #ccc;
}
</style>

View File

@ -56,6 +56,17 @@
}
}
&--indeterminate {
.van-icon {
display: flex;
align-items: center;
justify-content: center;
color: var(--van-white);
border-color: var(--van-checkbox-checked-icon-color);
background-color: var(--van-checkbox-checked-icon-color);
}
}
&--checked {
.van-icon {
color: var(--van-white);

View File

@ -611,4 +611,128 @@ exports[`should render demo and match snapshot 1`] = `
</div>
</div>
</div>
<div>
<!--[-->
<div
role="checkbox"
class="van-checkbox"
tabindex="0"
aria-checked="false"
>
<!--[-->
<div
class="van-checkbox__icon van-checkbox__icon--round van-checkbox__icon--indeterminate"
style
>
<i
class="van-badge__wrapper van-icon van-icon-minus"
style
>
<!--[-->
</i>
</div>
<span class="van-checkbox__label">
<!--[-->
Check All
</span>
</div>
<div class="divider">
</div>
<div class="van-checkbox-group">
<!--[-->
<!--[-->
<div
role="checkbox"
class="van-checkbox"
tabindex="0"
aria-checked="true"
>
<!--[-->
<div
class="van-checkbox__icon van-checkbox__icon--round van-checkbox__icon--checked"
style
>
<i
class="van-badge__wrapper van-icon van-icon-success"
style
>
<!--[-->
</i>
</div>
<span class="van-checkbox__label">
<!--[-->
Checkbox a
</span>
</div>
<div
role="checkbox"
class="van-checkbox"
tabindex="0"
aria-checked="true"
>
<!--[-->
<div
class="van-checkbox__icon van-checkbox__icon--round van-checkbox__icon--checked"
style
>
<i
class="van-badge__wrapper van-icon van-icon-success"
style
>
<!--[-->
</i>
</div>
<span class="van-checkbox__label">
<!--[-->
Checkbox b
</span>
</div>
<div
role="checkbox"
class="van-checkbox"
tabindex="0"
aria-checked="false"
>
<!--[-->
<div
class="van-checkbox__icon van-checkbox__icon--round"
style
>
<i
class="van-badge__wrapper van-icon van-icon-success"
style
>
<!--[-->
</i>
</div>
<span class="van-checkbox__label">
<!--[-->
Checkbox c
</span>
</div>
<div
role="checkbox"
class="van-checkbox"
tabindex="0"
aria-checked="true"
>
<!--[-->
<div
class="van-checkbox__icon van-checkbox__icon--round van-checkbox__icon--checked"
style
>
<i
class="van-badge__wrapper van-icon van-icon-success"
style
>
<!--[-->
</i>
</div>
<span class="van-checkbox__label">
<!--[-->
Checkbox d
</span>
</div>
</div>
</div>
`;

View File

@ -390,4 +390,80 @@ exports[`should render demo and match snapshot 1`] = `
</div>
</div>
</div>
<div>
<div
role="checkbox"
class="van-checkbox"
tabindex="0"
aria-checked="false"
>
<div class="van-checkbox__icon van-checkbox__icon--round van-checkbox__icon--indeterminate">
<i class="van-badge__wrapper van-icon van-icon-minus">
</i>
</div>
<span class="van-checkbox__label">
Check All
</span>
</div>
<div class="divider">
</div>
<div class="van-checkbox-group">
<div
role="checkbox"
class="van-checkbox"
tabindex="0"
aria-checked="true"
>
<div class="van-checkbox__icon van-checkbox__icon--round van-checkbox__icon--checked">
<i class="van-badge__wrapper van-icon van-icon-success">
</i>
</div>
<span class="van-checkbox__label">
Checkbox a
</span>
</div>
<div
role="checkbox"
class="van-checkbox"
tabindex="0"
aria-checked="true"
>
<div class="van-checkbox__icon van-checkbox__icon--round van-checkbox__icon--checked">
<i class="van-badge__wrapper van-icon van-icon-success">
</i>
</div>
<span class="van-checkbox__label">
Checkbox b
</span>
</div>
<div
role="checkbox"
class="van-checkbox"
tabindex="0"
aria-checked="false"
>
<div class="van-checkbox__icon van-checkbox__icon--round">
<i class="van-badge__wrapper van-icon van-icon-success">
</i>
</div>
<span class="van-checkbox__label">
Checkbox c
</span>
</div>
<div
role="checkbox"
class="van-checkbox"
tabindex="0"
aria-checked="true"
>
<div class="van-checkbox__icon van-checkbox__icon--round van-checkbox__icon--checked">
<i class="van-badge__wrapper van-icon van-icon-success">
</i>
</div>
<span class="van-checkbox__label">
Checkbox d
</span>
</div>
</div>
</div>
`;