Skip to content

Commit

Permalink
feat(checkbox): new checkbox
Browse files Browse the repository at this point in the history
  • Loading branch information
jarmywang committed Mar 21, 2023
1 parent 2844cd2 commit 07aa9e6
Show file tree
Hide file tree
Showing 26 changed files with 362 additions and 347 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
"@vueuse/core": "^8.2.5",
"dayjs": "^1.10.7",
"lodash": "^4.17.21",
"tdesign-icons-vue-next": "^0.1.3"
"tdesign-icons-vue-next": "^0.1.8"
},
"peerDependencies": {
"vue": "^3.2.6"
Expand Down Expand Up @@ -159,7 +159,7 @@
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-vue": "^6.0.0",
"rollup-pluginutils": "^2.8.2",
"tdesign-icons-view": "^0.1.0",
"tdesign-icons-view": "^0.1.6",
"tdesign-publish-cli": "^0.0.10",
"tdesign-site-components": "^0.11.19",
"tslib": "^2.3.1",
Expand Down
21 changes: 21 additions & 0 deletions src/checkbox/checkbox-group.en-US.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
:: BASE_DOC ::

## API

### CheckboxGroup Props

name | type | default | description | required
-- | -- | -- | -- | --
disabled | Boolean | - | \- | N
max | Number | undefined | \- | N
name | String | - | \- | N
options | Array | - | Typescript:`Array<CheckboxOption>` `type CheckboxOption = string \| number \| CheckboxOptionObj` `interface CheckboxOptionObj { label?: string \| TNode; value?: string \| number; disabled?: boolean; name?: string; checkAll?: true }`[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts)[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/checkbox/type.ts) | N
value | Array | [] | `v-model` and `v-model:value` is supported。Typescript:`T` `type CheckboxGroupValue = Array<string \| number \| boolean>`[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/checkbox/type.ts) | N
defaultValue | Array | [] | uncontrolled property。Typescript:`T` `type CheckboxGroupValue = Array<string \| number \| boolean>`[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/checkbox/type.ts) | N
onChange | Function | | Typescript:`(value: T, context: CheckboxGroupChangeContext) => void`<br/>[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/checkbox/type.ts)。<br/>`interface CheckboxGroupChangeContext { e: Event; current: string \| number; option: CheckboxOption \| TdCheckboxProps; type: 'check' \| 'uncheck' }`<br/> | N

### CheckboxGroup Events

name | params | description
-- | -- | --
change | `(value: T, context: CheckboxGroupChangeContext)` | [see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/checkbox/type.ts)。<br/>`interface CheckboxGroupChangeContext { e: Event; current: string \| number; option: CheckboxOption \| TdCheckboxProps; type: 'check' \| 'uncheck' }`<br/>
21 changes: 21 additions & 0 deletions src/checkbox/checkbox-group.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
:: BASE_DOC ::

## API

### CheckboxGroup Props

名称 | 类型 | 默认值 | 说明 | 必传
-- | -- | -- | -- | --
disabled | Boolean | - | 是否禁用组件,默认为 false。CheckboxGroup.disabled 优先级低于 Checkbox.disabled | N
max | Number | undefined | 支持最多选中的数量 | N
name | String | - | 统一设置内部复选框 HTML 属性 | N
options | Array | - | 以配置形式设置子元素。示例1:`['北京', '上海']` ,示例2: `[{ label: '全选', checkAll: true }, { label: '上海', value: 'shanghai' }]`。checkAll 值为 true 表示当前选项为「全选选项」。TS 类型:`Array<CheckboxOption>` `type CheckboxOption = string \| number \| CheckboxOptionObj` `interface CheckboxOptionObj { label?: string \| TNode; value?: string \| number; disabled?: boolean; name?: string; checkAll?: true }`[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts)[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/checkbox/type.ts) | N
value | Array | [] | 选中值。支持语法糖 `v-model``v-model:value`。TS 类型:`T` `type CheckboxGroupValue = Array<string \| number \| boolean>`[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/checkbox/type.ts) | N
defaultValue | Array | [] | 选中值。非受控属性。TS 类型:`T` `type CheckboxGroupValue = Array<string \| number \| boolean>`[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/checkbox/type.ts) | N
onChange | Function | | TS 类型:`(value: T, context: CheckboxGroupChangeContext) => void`<br/>值变化时触发。`context.current` 表示当前变化的数据项,如果是全选则为空;`context.type` 表示引起选中数据变化的是选中或是取消选中,`context.option` 表示当前变化的数据项。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/checkbox/type.ts)。<br/>`interface CheckboxGroupChangeContext { e: Event; current: string \| number; option: CheckboxOption \| TdCheckboxProps; type: 'check' \| 'uncheck' }`<br/> | N

### CheckboxGroup Events

名称 | 参数 | 描述
-- | -- | --
change | `(value: T, context: CheckboxGroupChangeContext)` | 值变化时触发。`context.current` 表示当前变化的数据项,如果是全选则为空;`context.type` 表示引起选中数据变化的是选中或是取消选中,`context.option` 表示当前变化的数据项。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/checkbox/type.ts)。<br/>`interface CheckboxGroupChangeContext { e: Event; current: string \| number; option: CheckboxOption \| TdCheckboxProps; type: 'check' \| 'uncheck' }`<br/>
23 changes: 17 additions & 6 deletions src/checkbox/group.vue → src/checkbox/checkbox-group.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,21 @@
<checkbox
v-for="(item, idx) in optionList"
:key="idx"
:name="item.name"
:label="item.label"
:name="item.name || ''"
:label="item.label || item.text || ''"
:value="item.value"
:check-all="item.checkAll"
:block="item.block || true"
:checked="item.checked || false"
:content="item.content || ''"
:content-disabled="item.contentDisabled || false"
:icon="item.icon || 'circle'"
:indeterminate="item.indeterminate || false"
:disabled="item.disabled"
:max-content-row="item.maxContentRow || 5"
:max-label-row="item.maxLabelRow || 3"
:readonly="item.readonly || false"
:placement="item.placement || 'left'"
/>
</span>
</div>
Expand All @@ -20,7 +31,7 @@ import config from '../config';
import CheckboxProps from './checkbox-group-props';
import Checkbox from './checkbox.vue';
import { CheckboxGroupValue, TdCheckboxGroupProps, TdCheckboxProps } from './type';
import { useDefault } from '../shared';
import { useDefault } from '@/shared';
import { getOptions, setCheckAllStatus } from './hooks';
const { prefix } = config;
Expand All @@ -39,7 +50,7 @@ export default defineComponent({
emits: ['update:value', 'update:modelValue', 'change'],
setup(props: any, context) {
const { isArray } = Array;
const [innerValue, setinnerValue] = useDefault<CheckboxGroupValue, TdCheckboxGroupProps>(
const [innerValue, setInnerValue] = useDefault<CheckboxGroupValue, TdCheckboxGroupProps>(
props,
context.emit,
'value',
Expand Down Expand Up @@ -80,7 +91,7 @@ export default defineComponent({
const i = val.indexOf(currentValue);
val.splice(i, 1);
}
setinnerValue(val, {
setInnerValue(val, {
e: data.e,
current: data.option.value,
type: data.checked ? 'check' : 'uncheck',
Expand All @@ -102,7 +113,7 @@ export default defineComponent({
};
const onCheckAllChange = (checked: boolean, context: { e: Event; source?: 't-checkbox' }) => {
const value = checked ? getAllCheckboxValue() : [];
setinnerValue(value, {
setInnerValue(value, {
e: context.e,
type: checked ? 'check' : 'uncheck',
current: undefined,
Expand Down
2 changes: 1 addition & 1 deletion src/checkbox/checkbox.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ content | String / Slot / Function | - | Typescript:`string \| TNode`。[see m
contentDisabled | Boolean | - | \- | N
default | String / Slot / Function | - | Typescript:`string \| TNode`[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
disabled | Boolean | undefined | \- | N
icon | Array | - | Typescript:`Array<TNode>`[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
icon | String / Array | 'circle' | Typescript:`'circle' \| 'line' \| 'rectangle' \| Array<TNode>`[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
indeterminate | Boolean | false | \- | N
label | String / Slot / Function | - | Typescript:`string \| TNode`[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
maxContentRow | Number | 5 | \- | N
Expand Down
2 changes: 1 addition & 1 deletion src/checkbox/checkbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ content | String / Slot / Function | - | 多选框内容。TS 类型:`string \
contentDisabled | Boolean | - | 是否禁用组件内容(content)触发选中 | N
default | String / Slot / Function | - | 多选框内容,同 label。TS 类型:`string \| TNode`[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
disabled | Boolean | undefined | 是否禁用组件。如果父组件存在 CheckboxGroup,默认值由 CheckboxGroup.disabled 控制。Checkbox.disabled 优先级高于 CheckboxGroup.disabled | N
icon | Array | - | 自定义选中图标和非选中图标。示例[选中态图标,非选中态图标]。TS 类型:`Array<TNode>`[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
icon | String / Array | 'circle' | 自定义选中图标和非选中图标。使用 Array 时表示[选中态图标,非选中态图标]使用 String 时,值为 circle 表示填充圆形图标、值为 line 表示描边型图标、值为 rectangle 表示填充矩形图标。。TS 类型:`'circle' \| 'line' \| 'rectangle' \| Array<TNode>`[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
indeterminate | Boolean | false | 是否为半选 | N
label | String / Slot / Function | - | 主文案。TS 类型:`string \| TNode`[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
maxContentRow | Number | 5 | 内容最大行数限制 | N
Expand Down
177 changes: 89 additions & 88 deletions src/checkbox/checkbox.vue
Original file line number Diff line number Diff line change
@@ -1,70 +1,81 @@
<template>
<div :class="componentClass">
<div :class="`${flagName}__content-wrap`">
<span v-if="placement === 'left'" :class="`${flagName}__icon-left`">
<input
type="checkbox"
:name="name"
:class="`${flagName}__original-left`"
:value="value"
:disabled="isDisabled"
:readonly="readonly"
:checked="isChecked"
:indeterminate="indeterminate"
@click="handleChange"
/>
<t-node v-if="!indeterminate" :content="checkIcons[isChecked ? 0 : 1]" />
<minus-circle-filled-icon v-else />
</span>
<span
v-if="labelContent || checkboxContent"
:class="{ [`${flagName}__label`]: true, [`${flagName}__label-left`]: placement === 'right' }"
@click="(e) => handleChange(e, 'content')"
<div
:class="{
[`${name}`]: true,
[`${name}--${placement}`]: true,
[`${name}--checked`]: isChecked,
[`${name}--block`]: block,
}"
@click="handleChange"
>
<div
:class="{
[`${name}__icon`]: true,
[`${name}__icon--${placement}`]: true,
[`${name}__icon--checked`]: isChecked,
[`${name}__icon--disabled`]: isDisabled,
}"
>
<div v-if="isIconArray" :class="`${name}__icon`">
<t-node :content="checkIcons[isChecked ? 0 : 1]" :class="`${name}__icon-wrapper`" />
</div>
<template v-else>
<t-node v-if="isChecked" :content="checkedIcon" :class="`${name}__icon-wrapper`" />
<template v-else>
<div
v-if="icon === 'circle' || icon === 'rectangle'"
:class="{ [`${name}__icon-${icon}`]: true, [`${name}__icon-${icon}--disabled`]: isDisabled }"
/>
<div v-if="icon === 'line'" class="placeholder" />
</template>
</template>
</div>
<div :class="`${name}__content`" @click.stop="(e) => handleChange(e, 'text')">
<div
:class="{
[`${name}__title`]: true,
[`${name}__title--checked`]: isChecked,
[`${name}__title--disabled`]: isDisabled,
}"
:style="{ '-webkit-line-clamp': maxLabelRow }"
>
<span v-if="labelContent" :style="labelStyle">
<t-node :content="labelContent" />
</span>
<span v-if="checkboxContent" :class="`${flagName}__description`" :style="contentStyle">
<t-node :content="checkboxContent" />
</span>
</span>

<span v-if="placement === 'right'" :class="`${flagName}__icon-right`">
<input
type="checkbox"
:name="name"
:class="`${flagName}__original-right`"
:value="value"
:disabled="isDisabled"
:readonly="readonly"
:checked="isChecked"
:indeterminate="indeterminate"
@click="handleChange"
/>
<t-node v-if="!indeterminate" :content="checkIcons[isChecked ? 0 : 1]" />
<minus-circle-filled-icon v-else />
</span>
<t-node :content="labelContent" />
</div>
<div
:class="{
[`${name}__description`]: true,
[`${name}__description--disabled`]: isDisabled,
}"
:style="{ '-webkit-line-clamp': maxContentRow }"
>
<t-node :content="checkboxContent" />
</div>
</div>
<!--下边框 -->
<div v-if="!borderless" :class="`${flagName}__border ${flagName}__border--${placement}`"></div>
<div v-if="!borderless" :class="`${name}__border ${name}__border--${placement}`" />
</div>
</template>

<script lang="ts">
import { inject, computed, defineComponent, getCurrentInstance, h, toRefs, CSSProperties } from 'vue';
import { MinusCircleFilledIcon, CheckCircleFilledIcon, CircleIcon } from 'tdesign-icons-vue-next';
import config from '../config';
import { defineComponent, h, toRefs, computed, inject, getCurrentInstance } from 'vue';
import {
CheckCircleFilledIcon,
CircleIcon,
MinusCircleFilledIcon,
MinusRectangleFilledIcon,
CheckRectangleFilledIcon,
CheckIcon,
} from 'tdesign-icons-vue-next';
import config from '@/config';
import CheckboxProps from './props';
import { renderContent, renderTNode, TNode, useDefault, useVModel } from '../shared';
import { TdCheckboxProps } from './type';
import ClASSNAMES from '../shared/constants';
import { renderContent, renderTNode, TNode, useDefault } from '@/shared';
import { TdCheckboxProps } from '@/checkbox/type';
const { prefix } = config;
const name = `${prefix}-checkbox`;
export default defineComponent({
name,
components: { TNode, MinusCircleFilledIcon },
components: { TNode },
props: {
...CheckboxProps,
borderless: {
Expand All @@ -74,8 +85,6 @@ export default defineComponent({
},
emits: ['update:checked', 'update:modelValue', 'change'],
setup(props, context) {
const flagName = name;
const checkIcons = props.icon || [h(CheckCircleFilledIcon), h(CircleIcon)];
const [innerChecked, setInnerChecked] = useDefault<boolean, TdCheckboxProps>(
props,
context.emit,
Expand All @@ -85,18 +94,37 @@ export default defineComponent({
const internalInstance = getCurrentInstance();
const checkboxGroup: any = inject('checkboxGroup', undefined);
const labelContent = computed(() => renderContent(internalInstance, 'label', 'default'));
const checkboxContent = computed(() => renderTNode(internalInstance, 'content'));
const indeterminate = computed<boolean>(() => {
if (props.checkAll && checkboxGroup != null) return checkboxGroup.checkAllStatus.value === 'indeterminate';
return props.indeterminate;
});
const isIconArray = Array.isArray(props.icon);
const checkIcons = computed(() => {
if (isIconArray && props.icon.length > 1) {
return props.icon.map((icon) =>
typeof icon === 'string' ? h('img', { class: `${name}__icon-image`, src: icon }) : icon,
);
}
return [h(CheckCircleFilledIcon), h(CircleIcon)];
});
const checkedIcon = computed(() => {
if (props.icon === 'circle') return indeterminate.value ? h(MinusCircleFilledIcon) : h(CheckCircleFilledIcon);
if (props.icon === 'rectangle')
return indeterminate.value ? h(MinusRectangleFilledIcon) : h(CheckRectangleFilledIcon);
if (props.icon === 'line') return indeterminate.value ? h(MinusCircleFilledIcon) : h(CheckIcon);
return null;
});
const isChecked = computed(() => {
if (props.checkAll) return checkboxGroup?.checkAllStatus.value === 'checked';
if (checkboxGroup != null && props.value != null) {
return !!checkboxGroup.checkedSet.value?.has(props.value);
}
return innerChecked.value;
});
Expand All @@ -107,34 +135,9 @@ export default defineComponent({
return !!checkboxGroup?.disabled.value;
});
const componentClass = computed(() => [
`${flagName}`,
{
[ClASSNAMES.STATUS.checked]: isChecked.value,
[ClASSNAMES.STATUS.disabled]: isDisabled.value,
[ClASSNAMES.STATUS.indeterminate]: indeterminate.value,
},
]);
const getLimitRowStyle = (row: number): CSSProperties => ({
display: '-webkit-box',
overflow: 'hidden',
WebkitBoxOrient: 'vertical',
WebkitLineClamp: row,
});
const labelStyle = computed(() => ({
color: isDisabled.value ? '#dcdcdc' : 'inherit',
...getLimitRowStyle(props.maxLabelRow),
}));
const contentStyle = computed(() => ({
...getLimitRowStyle(props.maxContentRow),
}));
const handleChange = (e: Event, source?: string) => {
if (isDisabled.value) return;
if (source === 'content' && props.contentDisabled) return;
if (source === 'text' && props.contentDisabled) return;
const value = !isChecked.value;
setInnerChecked(value, { e });
Expand All @@ -143,18 +146,16 @@ export default defineComponent({
checkboxGroup.onCheckedChange({ checked: value, checkAll: props.checkAll, e, option: props });
}
};
return {
...toRefs(props),
isChecked,
name,
checkedIcon,
isIconArray,
checkIcons,
labelContent,
labelStyle,
checkboxContent,
contentStyle,
isChecked,
isDisabled,
flagName,
componentClass,
indeterminate,
handleChange,
};
Expand Down
Loading

0 comments on commit 07aa9e6

Please sign in to comment.