diff --git a/components/checkbox/Checkbox.tsx b/components/checkbox/Checkbox.tsx
index 211a0e83f..0a5388c89 100644
--- a/components/checkbox/Checkbox.tsx
+++ b/components/checkbox/Checkbox.tsx
@@ -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(
+ {
+ [`${prefixCls.value}-indeterminate`]: indeterminate,
+ },
+ hashId.value,
);
- const checkboxClass = classNames({
- [`${prefixCls.value}-indeterminate`]: indeterminate,
- });
const ariaChecked = indeterminate ? 'mixed' : undefined;
- return (
+ return wrapSSR(
{children.length ? {children} : null}
-
+ ,
);
};
},
diff --git a/components/checkbox/Group.tsx b/components/checkbox/Group.tsx
index d341f1fe2..6b89fe12d 100644
--- a/components/checkbox/Group.tsx
+++ b/components/checkbox/Group.tsx
@@ -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 => (
{option.label === undefined ? slots.label?.(option) : option.label}
));
}
- return (
+ return wrapSSR(
{children || slots.default?.()}
-
+ ,
);
};
},
diff --git a/components/checkbox/index.en-US.md b/components/checkbox/index.en-US.md
index d4f6a5364..3bd1450a6 100644
--- a/components/checkbox/index.en-US.md
+++ b/components/checkbox/index.en-US.md
@@ -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.
diff --git a/components/checkbox/index.zh-CN.md b/components/checkbox/index.zh-CN.md
index b962a53a4..549cb6dbd 100644
--- a/components/checkbox/index.zh-CN.md
+++ b/components/checkbox/index.zh-CN.md
@@ -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
---
多选框。
diff --git a/components/checkbox/interface.ts b/components/checkbox/interface.ts
index e1599dd17..aabe849ba 100644
--- a/components/checkbox/interface.ts
+++ b/components/checkbox/interface.ts
@@ -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>,
- default: () => [] as Array,
- },
+ options: arrayType>(
+ [] as Array,
+ ),
disabled: Boolean,
id: String,
};
@@ -39,12 +39,10 @@ export const abstractCheckboxGroupProps = () => {
export const checkboxGroupProps = () => {
return {
...abstractCheckboxGroupProps(),
- defaultValue: { type: Array as PropType> },
- value: { type: Array as PropType> },
- onChange: { type: Function as PropType<(checkedValue: Array) => void> },
- 'onUpdate:value': {
- type: Function as PropType<(checkedValue: Array) => void>,
- },
+ defaultValue: arrayType>(),
+ value: arrayType>(),
+ onChange: functionType<(checkedValue: Array) => void>(),
+ 'onUpdate:value': functionType<(checkedValue: Array) => void>(),
};
};
@@ -53,27 +51,27 @@ export type CheckboxGroupProps = Partial {
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,
- 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(),
+ skipGroup: booleanType(false),
};
};
export const checkboxProps = () => {
return {
...abstractCheckboxProps(),
- indeterminate: { type: Boolean, default: false },
+ indeterminate: booleanType(false),
};
};
diff --git a/components/checkbox/style/index.tsx b/components/checkbox/style/index.tsx
index d53a9fa2e..f6d8906ac 100644
--- a/components/checkbox/style/index.tsx
+++ b/components/checkbox/style/index.tsx
@@ -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 = 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(token, {
+ checkboxCls: `.${prefixCls}`,
+ checkboxSize: token.controlInteractiveSize,
+ });
+
+ return [genCheckboxStyle(checkboxToken)];
+}
+
+export default genComponentStyleHook('Checkbox', (token, { prefixCls }) => [
+ getStyle(prefixCls, token),
+]);
diff --git a/components/style.ts b/components/style.ts
index 2db0b31f3..117b4a222 100644
--- a/components/style.ts
+++ b/components/style.ts
@@ -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';
diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts
index 65d87fe58..40a4de653 100644
--- a/components/theme/interface/components.ts
+++ b/components/theme/interface/components.ts
@@ -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;