refactor:checkbox (#6248)

* refactor:checkbox

* docs:update & refactor: checkbox type
pull/6266/head^2
果冻橙 2 years ago committed by GitHub
parent 4ccb1c3e19
commit 19ec975deb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -11,6 +11,9 @@ import useConfigInject from '../config-provider/hooks/useConfigInject';
import type { CheckboxChangeEvent, CheckboxProps } from './interface';
import { CheckboxGroupContextKey, checkboxProps } from './interface';
// CSSINJS
import useStyle from './style';
export default defineComponent({
compatConfig: { MODE: 3 },
name: 'ACheckbox',
@ -22,6 +25,10 @@ export default defineComponent({
const formItemContext = useInjectFormItemContext();
const formItemInputContext = FormItemInputContext.useInject();
const { prefixCls, direction } = useConfigInject('checkbox', props);
// style
const [wrapSSR, hashId] = useStyle(prefixCls);
const checkboxGroup = inject(CheckboxGroupContextKey, undefined);
const uniId = Symbol('checkboxUniId');
@ -90,12 +97,16 @@ export default defineComponent({
[`${prefixCls.value}-wrapper-in-form-item`]: formItemInputContext.isFormItemInput,
},
className,
hashId.value,
);
const checkboxClass = classNames({
const checkboxClass = classNames(
{
[`${prefixCls.value}-indeterminate`]: indeterminate,
});
},
hashId.value,
);
const ariaChecked = indeterminate ? 'mixed' : undefined;
return (
return wrapSSR(
<label
class={classString}
style={style as CSSProperties}
@ -109,7 +120,7 @@ export default defineComponent({
ref={checkboxRef}
/>
{children.length ? <span>{children}</span> : null}
</label>
</label>,
);
};
},

@ -5,14 +5,23 @@ import useConfigInject from '../config-provider/hooks/useConfigInject';
import type { CheckboxOptionType } from './interface';
import { CheckboxGroupContextKey, checkboxGroupProps } from './interface';
// CSSINJS
import useStyle from './style';
export default defineComponent({
compatConfig: { MODE: 3 },
name: 'ACheckboxGroup',
inheritAttrs: false,
props: checkboxGroupProps(),
// emits: ['change', 'update:value'],
setup(props, { slots, emit, expose }) {
setup(props, { slots, attrs, emit, expose }) {
const formItemContext = useInjectFormItemContext();
const { prefixCls, direction } = useConfigInject('checkbox', props);
const groupPrefixCls = computed(() => `${prefixCls.value}-group`);
// style
const [wrapSSR, hashId] = useStyle(groupPrefixCls);
const mergedValue = ref((props.value === undefined ? props.defaultValue : props.value) || []);
watch(
() => props.value,
@ -87,7 +96,6 @@ export default defineComponent({
return () => {
const { id = formItemContext.id.value } = props;
let children = null;
const groupPrefixCls = `${prefixCls.value}-group`;
if (options.value && options.value.length > 0) {
children = options.value.map(option => (
<Checkbox
@ -98,19 +106,25 @@ export default defineComponent({
value={option.value}
checked={mergedValue.value.indexOf(option.value) !== -1}
onChange={option.onChange}
class={`${groupPrefixCls}-item`}
class={`${groupPrefixCls.value}-item`}
>
{option.label === undefined ? slots.label?.(option) : option.label}
</Checkbox>
));
}
return (
return wrapSSR(
<div
class={[groupPrefixCls, { [`${groupPrefixCls}-rtl`]: direction.value === 'rtl' }]}
{...attrs}
class={[
groupPrefixCls.value,
{ [`${groupPrefixCls.value}-rtl`]: direction.value === 'rtl' },
attrs.class,
hashId.value,
]}
id={id}
>
{children || slots.default?.()}
</div>
</div>,
);
};
},

@ -2,7 +2,7 @@
category: Components
type: Data Entry
title: Checkbox
cover: https://gw.alipayobjects.com/zos/alicdn/8nbVbHEm_/CheckBox.svg
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*DzgiRbW3khIAAAAAAAAAAAAADrJ8AQ/original
---
Checkbox component.

@ -3,7 +3,7 @@ category: Components
subtitle: 多选框
type: 数据录入
title: Checkbox
cover: https://gw.alipayobjects.com/zos/alicdn/8nbVbHEm_/CheckBox.svg
cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*DzgiRbW3khIAAAAAAAAAAAAADrJ8AQ/original
---
多选框。

@ -1,7 +1,8 @@
import type { ExtractPropTypes, InjectionKey, PropType, Ref } from 'vue';
import type { ExtractPropTypes, InjectionKey, Ref } from 'vue';
import type { MouseEventHandler } from '../_util/EventInterface';
import type { VueNode } from '../_util/type';
import PropTypes from '../_util/vue-types';
import { booleanType, functionType, stringType, arrayType } from '../_util/type';
export type CheckboxValueType = string | number | boolean;
export interface CheckboxOptionType {
@ -27,10 +28,9 @@ export const abstractCheckboxGroupProps = () => {
return {
name: String,
prefixCls: String,
options: {
type: Array as PropType<Array<CheckboxOptionType | string | number>>,
default: () => [] as Array<CheckboxOptionType | string | number>,
},
options: arrayType<Array<CheckboxOptionType | string | number>>(
[] as Array<CheckboxOptionType | string | number>,
),
disabled: Boolean,
id: String,
};
@ -39,12 +39,10 @@ export const abstractCheckboxGroupProps = () => {
export const checkboxGroupProps = () => {
return {
...abstractCheckboxGroupProps(),
defaultValue: { type: Array as PropType<Array<CheckboxValueType>> },
value: { type: Array as PropType<Array<CheckboxValueType>> },
onChange: { type: Function as PropType<(checkedValue: Array<CheckboxValueType>) => void> },
'onUpdate:value': {
type: Function as PropType<(checkedValue: Array<CheckboxValueType>) => void>,
},
defaultValue: arrayType<Array<CheckboxValueType>>(),
value: arrayType<Array<CheckboxValueType>>(),
onChange: functionType<(checkedValue: Array<CheckboxValueType>) => void>(),
'onUpdate:value': functionType<(checkedValue: Array<CheckboxValueType>) => void>(),
};
};
@ -53,27 +51,27 @@ export type CheckboxGroupProps = Partial<ExtractPropTypes<ReturnType<typeof chec
export const abstractCheckboxProps = () => {
return {
prefixCls: String,
defaultChecked: { type: Boolean, default: undefined },
checked: { type: Boolean, default: undefined },
disabled: { type: Boolean, default: undefined },
isGroup: { type: Boolean, default: undefined },
defaultChecked: booleanType(),
checked: booleanType(),
disabled: booleanType(),
isGroup: booleanType(),
value: PropTypes.any,
name: String,
id: String,
indeterminate: { type: Boolean, default: undefined },
type: { type: String, default: 'checkbox' },
autofocus: { type: Boolean, default: undefined },
onChange: Function as PropType<(e: CheckboxChangeEvent) => void>,
'onUpdate:checked': Function as PropType<(checked: boolean) => void>,
onClick: Function as PropType<MouseEventHandler>,
skipGroup: { type: Boolean, default: false },
indeterminate: booleanType(),
type: stringType('checkbox'),
autofocus: booleanType(),
onChange: functionType<(e: CheckboxChangeEvent) => void>(),
'onUpdate:checked': functionType<(checked: boolean) => void>(),
onClick: functionType<MouseEventHandler>(),
skipGroup: booleanType(false),
};
};
export const checkboxProps = () => {
return {
...abstractCheckboxProps(),
indeterminate: { type: Boolean, default: false },
indeterminate: booleanType(false),
};
};

@ -1,3 +1,291 @@
import '../../style/index.less';
import './index.less';
// deps-lint-skip: form
import { Keyframes } from '../../_util/cssinjs';
import type { FullToken, GenerateStyle } from '../../theme/internal';
import { genComponentStyleHook, mergeToken } from '../../theme/internal';
import { genFocusOutline, resetComponent } from '../../_style';
export interface ComponentToken {}
interface CheckboxToken extends FullToken<'Checkbox'> {
checkboxCls: string;
checkboxSize: number;
}
// ============================== Motion ==============================
const antCheckboxEffect = new Keyframes('antCheckboxEffect', {
'0%': {
transform: 'scale(1)',
opacity: 0.5,
},
'100%': {
transform: 'scale(1.6)',
opacity: 0,
},
});
// ============================== Styles ==============================
export const genCheckboxStyle: GenerateStyle<CheckboxToken> = token => {
const { checkboxCls } = token;
const wrapperCls = `${checkboxCls}-wrapper`;
return [
// ===================== Basic =====================
{
// Group
[`${checkboxCls}-group`]: {
...resetComponent(token),
display: 'inline-flex',
},
// Wrapper
[wrapperCls]: {
...resetComponent(token),
display: 'inline-flex',
alignItems: 'baseline',
cursor: 'pointer',
// Fix checkbox & radio in flex align #30260
'&:after': {
display: 'inline-block',
width: 0,
overflow: 'hidden',
content: "'\\a0'",
},
// Checkbox near checkbox
[`& + ${wrapperCls}`]: {
marginInlineStart: token.marginXS,
},
[`&${wrapperCls}-in-form-item`]: {
'input[type="checkbox"]': {
width: 14, // FIXME: magic
height: 14, // FIXME: magic
},
},
},
// Wrapper > Checkbox
[checkboxCls]: {
...resetComponent(token),
top: '0.2em',
position: 'relative',
whiteSpace: 'nowrap',
lineHeight: 1,
cursor: 'pointer',
// Wrapper > Checkbox > input
[`${checkboxCls}-input`]: {
position: 'absolute',
inset: 0,
zIndex: 1,
width: '100%',
height: '100%',
cursor: 'pointer',
opacity: 0,
[`&:focus-visible + ${checkboxCls}-inner`]: {
...genFocusOutline(token),
},
},
// Wrapper > Checkbox > inner
[`${checkboxCls}-inner`]: {
boxSizing: 'border-box',
position: 'relative',
top: 0,
insetInlineStart: 0,
display: 'block',
width: token.checkboxSize,
height: token.checkboxSize,
direction: 'ltr',
backgroundColor: token.colorBgContainer,
border: `${token.lineWidth}px ${token.lineType} ${token.colorBorder}`,
borderRadius: token.borderRadiusSM,
borderCollapse: 'separate',
transition: `all ${token.motionDurationSlow}`,
'&:after': {
boxSizing: 'border-box',
position: 'absolute',
top: '50%',
insetInlineStart: '21.5%',
display: 'table',
width: (token.checkboxSize / 14) * 5,
height: (token.checkboxSize / 14) * 8,
border: `${token.lineWidthBold}px solid ${token.colorWhite}`,
borderTop: 0,
borderInlineStart: 0,
transform: 'rotate(45deg) scale(0) translate(-50%,-50%)',
opacity: 0,
content: '""',
transition: `all ${token.motionDurationFast} ${token.motionEaseInBack}, opacity ${token.motionDurationFast}`,
},
},
// Wrapper > Checkbox + Text
'& + span': {
paddingInlineStart: token.paddingXS,
paddingInlineEnd: token.paddingXS,
},
},
},
// ================= Indeterminate =================
{
[checkboxCls]: {
'&-indeterminate': {
// Wrapper > Checkbox > inner
[`${checkboxCls}-inner`]: {
'&:after': {
top: '50%',
insetInlineStart: '50%',
width: token.fontSizeLG / 2,
height: token.fontSizeLG / 2,
backgroundColor: token.colorPrimary,
border: 0,
transform: 'translate(-50%, -50%) scale(1)',
opacity: 1,
content: '""',
},
},
},
},
},
// ===================== Hover =====================
{
// Wrapper
[`${wrapperCls}:hover ${checkboxCls}:after`]: {
visibility: 'visible',
},
// Wrapper & Wrapper > Checkbox
[`
${wrapperCls}:not(${wrapperCls}-disabled),
${checkboxCls}:not(${checkboxCls}-disabled)
`]: {
[`&:hover ${checkboxCls}-inner`]: {
borderColor: token.colorPrimary,
},
},
[`${wrapperCls}:not(${wrapperCls}-disabled)`]: {
[`&:hover ${checkboxCls}-checked:not(${checkboxCls}-disabled) ${checkboxCls}-inner`]: {
backgroundColor: token.colorPrimaryHover,
borderColor: 'transparent',
},
[`&:hover ${checkboxCls}-checked:not(${checkboxCls}-disabled):after`]: {
borderColor: token.colorPrimaryHover,
},
},
},
// ==================== Checked ====================
{
// Wrapper > Checkbox
[`${checkboxCls}-checked`]: {
[`${checkboxCls}-inner`]: {
backgroundColor: token.colorPrimary,
borderColor: token.colorPrimary,
'&:after': {
opacity: 1,
transform: 'rotate(45deg) scale(1) translate(-50%,-50%)',
transition: `all ${token.motionDurationMid} ${token.motionEaseOutBack} ${token.motionDurationFast}`,
},
},
// Checked Effect
'&:after': {
position: 'absolute',
top: 0,
insetInlineStart: 0,
width: '100%',
height: '100%',
borderRadius: token.borderRadiusSM,
visibility: 'hidden',
border: `${token.lineWidthBold}px solid ${token.colorPrimary}`,
animationName: antCheckboxEffect,
animationDuration: token.motionDurationSlow,
animationTimingFunction: 'ease-in-out',
animationFillMode: 'backwards',
content: '""',
transition: `all ${token.motionDurationSlow}`,
},
},
[`
${wrapperCls}-checked:not(${wrapperCls}-disabled),
${checkboxCls}-checked:not(${checkboxCls}-disabled)
`]: {
[`&:hover ${checkboxCls}-inner`]: {
backgroundColor: token.colorPrimaryHover,
borderColor: 'transparent',
},
[`&:hover ${checkboxCls}:after`]: {
borderColor: token.colorPrimaryHover,
},
},
},
// ==================== Disable ====================
{
// Wrapper
[`${wrapperCls}-disabled`]: {
cursor: 'not-allowed',
},
// Wrapper > Checkbox
[`${checkboxCls}-disabled`]: {
// Wrapper > Checkbox > input
[`&, ${checkboxCls}-input`]: {
cursor: 'not-allowed',
// Disabled for native input to enable Tooltip event handler
// ref: https://github.com/ant-design/ant-design/issues/39822#issuecomment-1365075901
pointerEvents: 'none',
},
// Wrapper > Checkbox > inner
[`${checkboxCls}-inner`]: {
background: token.colorBgContainerDisabled,
borderColor: token.colorBorder,
'&:after': {
borderColor: token.colorTextDisabled,
},
},
'&:after': {
display: 'none',
},
'& + span': {
color: token.colorTextDisabled,
},
[`&${checkboxCls}-indeterminate ${checkboxCls}-inner::after`]: {
background: token.colorTextDisabled,
},
},
},
];
};
// ============================== Export ==============================
export function getStyle(prefixCls: string, token: FullToken<'Checkbox'>) {
const checkboxToken: CheckboxToken = mergeToken<CheckboxToken>(token, {
checkboxCls: `.${prefixCls}`,
checkboxSize: token.controlInteractiveSize,
});
return [genCheckboxStyle(checkboxToken)];
}
export default genComponentStyleHook('Checkbox', (token, { prefixCls }) => [
getStyle(prefixCls, token),
]);

@ -1,7 +1,7 @@
// import './button/style';
// import './icon/style';
import './radio/style';
import './checkbox/style';
// import './checkbox/style';
// import './grid/style';
// import './tag/style';
// import './rate/style';

@ -8,7 +8,7 @@ import type { ComponentToken as ButtonComponentToken } from '../../button/style'
import type { ComponentToken as CardComponentToken } from '../../card/style';
import type { ComponentToken as CarouselComponentToken } from '../../carousel/style';
// import type { ComponentToken as CascaderComponentToken } from '../../cascader/style';
// import type { ComponentToken as CheckboxComponentToken } from '../../checkbox/style';
import type { ComponentToken as CheckboxComponentToken } from '../../checkbox/style';
// import type { ComponentToken as CollapseComponentToken } from '../../collapse/style';
import type { ComponentToken as DatePickerComponentToken } from '../../date-picker/style';
import type { ComponentToken as DividerComponentToken } from '../../divider/style';
@ -62,7 +62,7 @@ export interface ComponentTokenMap {
Card?: CardComponentToken;
Carousel?: CarouselComponentToken;
// Cascader?: CascaderComponentToken;
// Checkbox?: CheckboxComponentToken;
Checkbox?: CheckboxComponentToken;
// Collapse?: CollapseComponentToken;
Comment?: {};
DatePicker?: DatePickerComponentToken;

Loading…
Cancel
Save