refactor(radio): use composition api (#4720)

* refactor(radio): use composition api

* docs: update

* chore: update

* docs: update

* Update Group.tsx

Co-authored-by: tangjinzhou <415800467@qq.com>
pull/4723/head
ajuner 2021-10-01 14:53:37 +08:00 committed by GitHub
parent f653955c97
commit cf3fe6b9e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 325 additions and 250 deletions

View File

@ -1,133 +1,133 @@
import { provide, inject, nextTick, defineComponent } from 'vue'; import { provide, nextTick, defineComponent, ref, watch, onBeforeMount } from 'vue';
import type { PropType, ExtractPropTypes } from 'vue';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import Radio from './Radio'; import Radio from './Radio';
import { getOptionProps, filterEmpty, hasProp, getSlot } from '../_util/props-util'; import useConfigInject from '../_util/hooks/useConfigInject';
import { defaultConfigProvider } from '../config-provider';
import { tuple } from '../_util/type'; import { tuple } from '../_util/type';
import type { RadioChangeEvent } from './interface'; import type { RadioChangeEvent } from './interface';
import { useInjectFormItemContext } from '../form/FormItemContext'; import { useInjectFormItemContext } from '../form/FormItemContext';
export default defineComponent({ const RadioGroupSizeTypes = tuple('large', 'default', 'small');
name: 'ARadioGroup',
props: { export type RadioGroupSize = typeof RadioGroupSizeTypes[number];
const RadioGroupOptionTypes = tuple('default', 'button');
export type RadioGroupOption = typeof RadioGroupOptionTypes[number];
export type RadioGroupChildOption = {
label: string;
value: string;
disabled?: boolean;
};
const radioGroupProps = {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
defaultValue: PropTypes.any,
value: PropTypes.any, value: PropTypes.any,
size: PropTypes.oneOf(tuple('large', 'default', 'small')).def('default'), size: PropTypes.oneOf(RadioGroupSizeTypes).def('default'),
options: PropTypes.array, options: {
type: Array as PropType<Array<String | RadioGroupChildOption>>,
},
disabled: PropTypes.looseBool, disabled: PropTypes.looseBool,
name: PropTypes.string, name: PropTypes.string,
buttonStyle: PropTypes.string.def('outline'), buttonStyle: PropTypes.string.def('outline'),
onChange: PropTypes.func,
id: PropTypes.string, id: PropTypes.string,
}, optionType: PropTypes.oneOf(RadioGroupOptionTypes).def('default'),
};
export type RadioGroupProps = Partial<ExtractPropTypes<typeof radioGroupProps>>;
export default defineComponent({
name: 'ARadioGroup',
props: radioGroupProps,
emits: ['update:value', 'change'], emits: ['update:value', 'change'],
setup() { setup(props, { slots, emit }) {
const formItemContext = useInjectFormItemContext(); const formItemContext = useInjectFormItemContext();
return { const { prefixCls } = useConfigInject('radio', props);
formItemContext, const stateValue = ref(props.value === undefined ? props.defaultValue : props.value);
updatingValue: false, const updatingValue = ref<boolean>(false);
configProvider: inject('configProvider', defaultConfigProvider), watch(
radioGroupContext: null, () => props.value,
}; val => {
stateValue.value = val;
updatingValue.value = false;
}, },
data() { );
const { value, defaultValue } = this;
return { const onRadioChange = (ev: RadioChangeEvent) => {
stateValue: value === undefined ? defaultValue : value, const lastValue = stateValue.value;
};
},
watch: {
value(val) {
this.updatingValue = false;
this.stateValue = val;
},
},
// computed: {
// radioOptions() {
// const { disabled } = this;
// return this.options.map(option => {
// return typeof option === 'string'
// ? { label: option, value: option }
// : { ...option, disabled: option.disabled === undefined ? disabled : option.disabled };
// });
// },
// },
created() {
this.radioGroupContext = provide('radioGroupContext', this);
},
methods: {
onRadioChange(ev: RadioChangeEvent) {
const lastValue = this.stateValue;
const { value } = ev.target; const { value } = ev.target;
if (!hasProp(this, 'value')) {
this.stateValue = value; if (!('value' in props)) {
stateValue.value = value;
} }
// nextTick for https://github.com/vueComponent/ant-design-vue/issues/1280 // nextTick for https://github.com/vueComponent/ant-design-vue/issues/1280
if (!this.updatingValue && value !== lastValue) { if (!updatingValue.value && value !== lastValue) {
this.updatingValue = true; updatingValue.value = true;
this.$emit('update:value', value); emit('update:value', value);
this.$emit('change', ev); emit('change', ev);
this.formItemContext.onFieldChange(); formItemContext.onFieldChange();
} }
nextTick(() => { nextTick(() => {
this.updatingValue = false; updatingValue.value = false;
}); });
}, };
},
render() { provide('radioGroupContext', {
const props = getOptionProps(this); onRadioChange,
const { stateValue,
prefixCls: customizePrefixCls, props,
options, });
buttonStyle,
id = this.formItemContext.id.value, return () => {
} = props; const { options, optionType, buttonStyle, id = formItemContext.id.value } = props;
const { getPrefixCls } = this.configProvider;
const prefixCls = getPrefixCls('radio', customizePrefixCls); const groupPrefixCls = `${prefixCls.value}-group`;
const groupPrefixCls = `${prefixCls}-group`;
const classString = classNames(groupPrefixCls, `${groupPrefixCls}-${buttonStyle}`, { const classString = classNames(groupPrefixCls, `${groupPrefixCls}-${buttonStyle}`, {
[`${groupPrefixCls}-${props.size}`]: props.size, [`${groupPrefixCls}-${props.size}`]: props.size,
}); });
let children = filterEmpty(getSlot(this)); let children = null;
// options, 使
if (options && options.length > 0) { if (options && options.length > 0) {
const optionsPrefixCls =
optionType === 'button' ? `${prefixCls.value}-button` : prefixCls.value;
children = options.map(option => { children = options.map(option => {
if (typeof option === 'string') { if (typeof option === 'string') {
return ( return (
<Radio <Radio
key={option} key={option}
prefixCls={prefixCls} prefixCls={optionsPrefixCls}
disabled={props.disabled} disabled={props.disabled}
value={option} value={option}
checked={this.stateValue === option} checked={stateValue.value === option}
> >
{option} {option}
</Radio> </Radio>
); );
} }
const { value, disabled, label } = option as RadioGroupChildOption;
return ( return (
<Radio <Radio
key={`radio-group-value-options-${option.value}`} key={`radio-group-value-options-${value}`}
prefixCls={prefixCls} prefixCls={optionsPrefixCls}
disabled={option.disabled || props.disabled} disabled={disabled || props.disabled}
value={option.value} value={value}
checked={this.stateValue === option.value} checked={stateValue.value === value}
> >
{option.label} {label}
</Radio> </Radio>
); );
}); });
} else {
children = slots.default?.();
} }
return ( return (
<div class={classString} id={id}> <div class={classString} id={id}>
{children} {children}
</div> </div>
); );
};
}, },
}); });

View File

@ -1,16 +1,14 @@
import type { ExtractPropTypes } from 'vue'; import type { ExtractPropTypes } from 'vue';
import { defineComponent, inject } from 'vue'; import { defineComponent, inject, ref } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import VcCheckbox from '../vc-checkbox'; import VcCheckbox from '../vc-checkbox';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import { getOptionProps } from '../_util/props-util'; import useConfigInject from '../_util/hooks/useConfigInject';
import { defaultConfigProvider } from '../config-provider'; import type { RadioChangeEvent, RadioGroupContext } from './interface';
import type { RadioChangeEvent } from './interface';
import { useInjectFormItemContext } from '../form/FormItemContext'; import { useInjectFormItemContext } from '../form/FormItemContext';
export const radioProps = { export const radioProps = {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
defaultChecked: PropTypes.looseBool,
checked: PropTypes.looseBool, checked: PropTypes.looseBool,
disabled: PropTypes.looseBool, disabled: PropTypes.looseBool,
isGroup: PropTypes.looseBool, isGroup: PropTypes.looseBool,
@ -30,72 +28,67 @@ export default defineComponent({
name: 'ARadio', name: 'ARadio',
props: radioProps, props: radioProps,
emits: ['update:checked', 'update:value', 'change', 'blur', 'focus'], emits: ['update:checked', 'update:value', 'change', 'blur', 'focus'],
setup() { setup(props, { emit, expose, slots }) {
const formItemContext = useInjectFormItemContext(); const formItemContext = useInjectFormItemContext();
return { const vcCheckbox = ref<HTMLElement>();
configProvider: inject('configProvider', defaultConfigProvider), const radioGroupContext = inject<RadioGroupContext>('radioGroupContext');
radioGroupContext: inject('radioGroupContext', null), const { prefixCls } = useConfigInject('radio', props);
formItemContext,
};
},
methods: {
focus() {
(this.$refs.vcCheckbox as HTMLInputElement).focus();
},
blur() {
(this.$refs.vcCheckbox as HTMLInputElement).blur();
},
handleChange(event: RadioChangeEvent) {
const targetChecked = event.target.checked;
this.$emit('update:checked', targetChecked);
this.$emit('update:value', targetChecked);
this.$emit('change', event);
this.formItemContext.onFieldChange();
},
onChange2(e: RadioChangeEvent) {
this.$emit('change', e);
if (this.radioGroupContext && this.radioGroupContext.onRadioChange) {
this.radioGroupContext.onRadioChange(e);
}
},
},
render() { const focus = () => {
const { $slots, radioGroupContext: radioGroup } = this; vcCheckbox.value.focus();
const props = getOptionProps(this); };
const {
prefixCls: customizePrefixCls, const blur = () => {
id = this.formItemContext.id.value, vcCheckbox.value.blur();
...restProps };
} = props;
const { getPrefixCls } = this.configProvider; expose({ focus, blur });
const prefixCls = getPrefixCls('radio', customizePrefixCls);
const handleChange = (event: RadioChangeEvent) => {
const targetChecked = event.target.checked;
emit('update:checked', targetChecked);
emit('update:value', targetChecked);
emit('change', event);
formItemContext.onFieldChange();
};
const onChange = (e: RadioChangeEvent) => {
emit('change', e);
if (radioGroupContext && radioGroupContext.onRadioChange) {
radioGroupContext.onRadioChange(e);
}
};
return () => {
const radioGroup = radioGroupContext;
const { prefixCls: customizePrefixCls, id = formItemContext.id.value, ...restProps } = props;
const rProps: RadioProps = { const rProps: RadioProps = {
prefixCls, prefixCls: prefixCls.value,
id, id,
...restProps, ...restProps,
}; };
if (radioGroup) { if (radioGroup) {
rProps.name = radioGroup.name; rProps.name = radioGroup.props.name;
rProps.onChange = this.onChange2; rProps.onChange = onChange;
rProps.checked = props.value === radioGroup.stateValue; rProps.checked = props.value === radioGroup.stateValue.value;
rProps.disabled = props.disabled || radioGroup.disabled; rProps.disabled = props.disabled || radioGroup.props.disabled;
} else { } else {
rProps.onChange = this.handleChange; rProps.onChange = handleChange;
} }
const wrapperClassString = classNames({ const wrapperClassString = classNames({
[`${prefixCls}-wrapper`]: true, [`${prefixCls.value}-wrapper`]: true,
[`${prefixCls}-wrapper-checked`]: rProps.checked, [`${prefixCls.value}-wrapper-checked`]: rProps.checked,
[`${prefixCls}-wrapper-disabled`]: rProps.disabled, [`${prefixCls.value}-wrapper-disabled`]: rProps.disabled,
}); });
return ( return (
<label class={wrapperClassString}> <label class={wrapperClassString}>
<VcCheckbox {...rProps} ref="vcCheckbox" /> <VcCheckbox {...rProps} ref={vcCheckbox} />
{$slots.default && <span>{$slots.default()}</span>} {slots.default && <span>{slots.default()}</span>}
</label> </label>
); );
};
}, },
}); });

View File

@ -1,35 +1,28 @@
import { defineComponent, inject } from 'vue'; import { defineComponent, inject } from 'vue';
import type { RadioProps } from './Radio'; import type { RadioProps } from './Radio';
import Radio, { radioProps } from './Radio'; import Radio, { radioProps } from './Radio';
import { getOptionProps, getSlot } from '../_util/props-util'; import useConfigInject from '../_util/hooks/useConfigInject';
import { defaultConfigProvider } from '../config-provider'; import type { RadioGroupContext } from './interface';
export default defineComponent({ export default defineComponent({
name: 'ARadioButton', name: 'ARadioButton',
props: { props: radioProps,
...radioProps, setup(props: RadioProps, { slots }) {
}, const { prefixCls } = useConfigInject('radio-button', props);
setup() { const radioGroupContext = inject<RadioGroupContext>('radioGroupContext');
return {
configProvider: inject('configProvider', defaultConfigProvider),
radioGroupContext: inject<any>('radioGroupContext', {}),
};
},
render() {
const props = getOptionProps(this) as RadioProps;
const { prefixCls: customizePrefixCls, ...otherProps } = props;
const { getPrefixCls } = this.configProvider;
const prefixCls = getPrefixCls('radio-button', customizePrefixCls);
return () => {
const rProps: RadioProps = { const rProps: RadioProps = {
prefixCls, ...props,
...otherProps, prefixCls: prefixCls.value,
}; };
if (this.radioGroupContext) {
rProps.onChange = this.radioGroupContext.onRadioChange; if (radioGroupContext) {
rProps.checked = props.value === this.radioGroupContext.stateValue; rProps.onChange = radioGroupContext.onRadioChange;
rProps.disabled = props.disabled || this.radioGroupContext.disabled; rProps.checked = rProps.value === radioGroupContext.stateValue.value;
rProps.disabled = rProps.disabled || radioGroupContext.props.disabled;
} }
return <Radio {...rProps}>{getSlot(this)}</Radio>; return <Radio {...rProps}>{slots.default?.()}</Radio>;
};
}, },
}); });

View File

@ -41,13 +41,16 @@ exports[`renders ./components/radio/demo/radioGroup.vue correctly 1`] = `
</div> </div>
`; `;
exports[`renders ./components/radio/demo/radioGroup-more.vue correctly 1`] = `<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper ant-radio-wrapper-checked" style="display: block; height: 30px; line-height: 30px;"><span class="ant-radio ant-radio-checked"><input type="radio" class="ant-radio-input" value="1"><span class="ant-radio-inner"></span></span><span>Option A</span></label><label class="ant-radio-wrapper" style="display: block; height: 30px; line-height: 30px;"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="2"><span class="ant-radio-inner"></span></span><span>Option B</span></label><label class="ant-radio-wrapper" style="display: block; height: 30px; line-height: 30px;"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="3"><span class="ant-radio-inner"></span></span><span>Option C</span></label><label class="ant-radio-wrapper" style="display: block; height: 30px; line-height: 30px;"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="4"><span class="ant-radio-inner"></span></span><span> More... <!--v-if--></span></label></div>`; exports[`renders ./components/radio/demo/radioGroup-more.vue correctly 1`] = `<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper ant-radio-wrapper-checked" style="display: flex; height: 30px; line-height: 30px;"><span class="ant-radio ant-radio-checked"><input type="radio" class="ant-radio-input" value="1"><span class="ant-radio-inner"></span></span><span>Option A</span></label><label class="ant-radio-wrapper" style="display: flex; height: 30px; line-height: 30px;"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="2"><span class="ant-radio-inner"></span></span><span>Option B</span></label><label class="ant-radio-wrapper" style="display: flex; height: 30px; line-height: 30px;"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="3"><span class="ant-radio-inner"></span></span><span>Option C</span></label><label class="ant-radio-wrapper" style="display: flex; height: 30px; line-height: 30px;"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="4"><span class="ant-radio-inner"></span></span><span> More... <!--v-if--></span></label></div>`;
exports[`renders ./components/radio/demo/radioGroup-options.vue correctly 1`] = ` exports[`renders ./components/radio/demo/radioGroup-options.vue correctly 1`] = `
<div> <div>
<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper ant-radio-wrapper-checked"><span class="ant-radio ant-radio-checked"><input type="radio" class="ant-radio-input" value="Apple"><span class="ant-radio-inner"></span></span><span>Apple</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="Pear"><span class="ant-radio-inner"></span></span><span>Pear</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="Orange"><span class="ant-radio-inner"></span></span><span>Orange</span></label></div><br> <div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper ant-radio-wrapper-checked"><span class="ant-radio ant-radio-checked"><input type="radio" class="ant-radio-input" value="Apple"><span class="ant-radio-inner"></span></span><span>Apple</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="Pear"><span class="ant-radio-inner"></span></span><span>Pear</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="Orange"><span class="ant-radio-inner"></span></span><span>Orange</span></label></div><br>
<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper ant-radio-wrapper-checked"><span class="ant-radio ant-radio-checked"><input type="radio" class="ant-radio-input" value="Apple"><span class="ant-radio-inner"></span></span><span>Apple</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="Pear"><span class="ant-radio-inner"></span></span><span>Pear</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="Orange"><span class="ant-radio-inner"></span></span><span>Orange</span></label></div><br> <div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper ant-radio-wrapper-checked"><span class="ant-radio ant-radio-checked"><input type="radio" class="ant-radio-input" value="Apple"><span class="ant-radio-inner"></span></span><span>Apple</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="Pear"><span class="ant-radio-inner"></span></span><span>Pear</span></label><label class="ant-radio-wrapper ant-radio-wrapper-disabled"><span class="ant-radio ant-radio-disabled"><input type="radio" disabled="" class="ant-radio-input" value="Orange"><span class="ant-radio-inner"></span></span><span>Orange</span></label></div><br>
<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper ant-radio-wrapper-checked ant-radio-wrapper-disabled"><span class="ant-radio ant-radio-checked ant-radio-disabled"><input type="radio" disabled="" class="ant-radio-input" value="Apple"><span class="ant-radio-inner"></span></span><span>Apple</span></label><label class="ant-radio-wrapper ant-radio-wrapper-disabled"><span class="ant-radio ant-radio-disabled"><input type="radio" disabled="" class="ant-radio-input" value="Pear"><span class="ant-radio-inner"></span></span><span>Pear</span></label><label class="ant-radio-wrapper ant-radio-wrapper-disabled"><span class="ant-radio ant-radio-disabled"><input type="radio" disabled="" class="ant-radio-input" value="Orange"><span class="ant-radio-inner"></span></span><span>Orange</span></label></div> <div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper ant-radio-wrapper-checked ant-radio-wrapper-disabled"><span class="ant-radio ant-radio-checked ant-radio-disabled"><input type="radio" disabled="" class="ant-radio-input" value="Apple"><span class="ant-radio-inner"></span></span><span>Apple</span></label><label class="ant-radio-wrapper ant-radio-wrapper-disabled"><span class="ant-radio ant-radio-disabled"><input type="radio" disabled="" class="ant-radio-input" value="Pear"><span class="ant-radio-inner"></span></span><span>Pear</span></label><label class="ant-radio-wrapper ant-radio-wrapper-disabled"><span class="ant-radio ant-radio-disabled"><input type="radio" disabled="" class="ant-radio-input" value="Orange"><span class="ant-radio-inner"></span></span><span>Orange</span></label></div><br>
<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"><span class="ant-radio-button ant-radio-button-checked"><input type="radio" class="ant-radio-button-input" value="Apple"><span class="ant-radio-button-inner"></span></span><span>Apple</span></label><label class="ant-radio-button-wrapper"><span class="ant-radio-button"><input type="radio" class="ant-radio-button-input" value="Pear"><span class="ant-radio-button-inner"></span></span><span>Pear</span></label><label class="ant-radio-button-wrapper"><span class="ant-radio-button"><input type="radio" class="ant-radio-button-input" value="Orange"><span class="ant-radio-button-inner"></span></span><span>Orange</span></label></div><br>
<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-button-wrapper ant-radio-button-wrapper-checked"><span class="ant-radio-button ant-radio-button-checked"><input type="radio" class="ant-radio-button-input" value="Apple"><span class="ant-radio-button-inner"></span></span><span>Apple</span></label><label class="ant-radio-button-wrapper"><span class="ant-radio-button"><input type="radio" class="ant-radio-button-input" value="Pear"><span class="ant-radio-button-inner"></span></span><span>Pear</span></label><label class="ant-radio-button-wrapper ant-radio-button-wrapper-disabled"><span class="ant-radio-button ant-radio-button-disabled"><input type="radio" disabled="" class="ant-radio-button-input" value="Orange"><span class="ant-radio-button-inner"></span></span><span>Orange</span></label></div><br>
<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-button-wrapper ant-radio-button-wrapper-checked ant-radio-button-wrapper-disabled"><span class="ant-radio-button ant-radio-button-checked ant-radio-button-disabled"><input type="radio" disabled="" class="ant-radio-button-input" value="Apple"><span class="ant-radio-button-inner"></span></span><span>Apple</span></label><label class="ant-radio-button-wrapper ant-radio-button-wrapper-disabled"><span class="ant-radio-button ant-radio-button-disabled"><input type="radio" disabled="" class="ant-radio-button-input" value="Pear"><span class="ant-radio-button-inner"></span></span><span>Pear</span></label><label class="ant-radio-button-wrapper ant-radio-button-wrapper-disabled"><span class="ant-radio-button ant-radio-button-disabled"><input type="radio" disabled="" class="ant-radio-button-input" value="Orange"><span class="ant-radio-button-inner"></span></span><span>Orange</span></label></div><br>
</div> </div>
`; `;

View File

@ -2,7 +2,7 @@
exports[`Radio all children should have a name property 1`] = `<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper"><span class="ant-radio"><input name="radiogroup" type="radio" class="ant-radio-input" value="A"><span class="ant-radio-inner"></span></span><span>A</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input name="radiogroup" type="radio" class="ant-radio-input" value="B"><span class="ant-radio-inner"></span></span><span>B</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input name="radiogroup" type="radio" class="ant-radio-input" value="C"><span class="ant-radio-inner"></span></span><span>C</span></label></div>`; exports[`Radio all children should have a name property 1`] = `<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper"><span class="ant-radio"><input name="radiogroup" type="radio" class="ant-radio-input" value="A"><span class="ant-radio-inner"></span></span><span>A</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input name="radiogroup" type="radio" class="ant-radio-input" value="B"><span class="ant-radio-inner"></span></span><span>B</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input name="radiogroup" type="radio" class="ant-radio-input" value="C"><span class="ant-radio-inner"></span></span><span>C</span></label></div>`;
exports[`Radio fire change events when value changes 1`] = `<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper ant-radio-wrapper-checked"><span class="ant-radio ant-radio-checked"><input type="radio" class="ant-radio-input" value="A"><span class="ant-radio-inner"></span></span><span>A</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="B"><span class="ant-radio-inner"></span></span><span>B</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="C"><span class="ant-radio-inner"></span></span><span>C</span></label></div>`; exports[`Radio fire change events when value changes 1`] = `<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="A"><span class="ant-radio-inner"></span></span><span>A</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="B"><span class="ant-radio-inner"></span></span><span>B</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="C"><span class="ant-radio-inner"></span></span><span>C</span></label></div>`;
exports[`Radio fire change events when value changes 2`] = `<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper ant-radio-wrapper-checked"><span class="ant-radio ant-radio-checked"><input type="radio" class="ant-radio-input" value="A"><span class="ant-radio-inner"></span></span><span>A</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="B"><span class="ant-radio-inner"></span></span><span>B</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="C"><span class="ant-radio-inner"></span></span><span>C</span></label></div>`; exports[`Radio fire change events when value changes 2`] = `<div class="ant-radio-group ant-radio-group-outline ant-radio-group-default"><label class="ant-radio-wrapper ant-radio-wrapper-checked"><span class="ant-radio ant-radio-checked"><input type="radio" class="ant-radio-input" value="A"><span class="ant-radio-inner"></span></span><span>A</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="B"><span class="ant-radio-inner"></span></span><span>B</span></label><label class="ant-radio-wrapper"><span class="ant-radio"><input type="radio" class="ant-radio-input" value="C"><span class="ant-radio-inner"></span></span><span>C</span></label></div>`;

View File

@ -46,4 +46,3 @@ export default defineComponent({
}, },
}); });
</script> </script>
```

View File

@ -32,7 +32,7 @@ export default defineComponent({
setup() { setup() {
const value = ref<number>(1); const value = ref<number>(1);
const radioStyle = reactive({ const radioStyle = reactive({
display: 'block', display: 'flex',
height: '30px', height: '30px',
lineHeight: '30px', lineHeight: '30px',
}); });

View File

@ -19,9 +19,16 @@ Render radios by configuring `options`.
<div> <div>
<a-radio-group v-model:value="value1" :options="plainOptions" /> <a-radio-group v-model:value="value1" :options="plainOptions" />
<br /> <br />
<a-radio-group v-model:value="value2" :options="options" /> <a-radio-group v-model:value="value2" :options="optionsWithDisabled" />
<br />
<a-radio-group v-model:value="value3" :options="plainOptions" disabled />
<br />
<a-radio-group v-model:value="value1" option-type="button" :options="plainOptions" />
<br />
<a-radio-group v-model:value="value2" option-type="button" :options="optionsWithDisabled" />
<br />
<a-radio-group v-model:value="value3" option-type="button" :options="plainOptions" disabled />
<br /> <br />
<a-radio-group v-model:value="value3" :options="optionsWithDisabled" disabled />
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -35,7 +42,7 @@ const options = [
const optionsWithDisabled = [ const optionsWithDisabled = [
{ label: 'Apple', value: 'Apple' }, { label: 'Apple', value: 'Apple' },
{ label: 'Pear', value: 'Pear' }, { label: 'Pear', value: 'Pear' },
{ label: 'Orange', value: 'Orange', disabled: false }, { label: 'Orange', value: 'Orange', disabled: true },
]; ];
export default defineComponent({ export default defineComponent({
data() { data() {

View File

@ -14,7 +14,7 @@ Radio.
## API ## API
### Radio ### Radio/Radio.Button
| Property | Description | Type | Default | | Property | Description | Type | Default |
| --- | --- | --- | --- | | --- | --- | --- | --- |
@ -27,15 +27,15 @@ Radio.
Radio group can wrap a group of `Radio` Radio group can wrap a group of `Radio`
| Property | Description | Type | Default | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| defaultValue | Default selected value | any | - | | buttonStyle | style type of radio button | `outline` \| `solid` | `outline` | |
| disabled | Disable all radio buttons | boolean | false | | disabled | Disable all radio buttons | boolean | false | |
| name | The `name` property of all `input[type="radio"]` children | string | - | | name | The `name` property of all `input[type="radio"]` children | string | - | |
| options | set children optional | string\[] \| Array&lt;{ label: string value: string disabled?: boolean }> | - | | options | set children optional | string\[] \| Array&lt;{ label: string value: string disabled?: boolean }> | - | |
| size | size for radio button style | `large` \| `default` \| `small` | `default` | | optionType | Set Radio optionType | `default` \| `button` | `default` | 3.0.0 |
| value(v-model) | Used for setting the currently selected value. | any | - | | size | size for radio button style | `large` \| `default` \| `small` | `default` | |
| buttonStyle | style type of radio button | `outline` \| `solid` | `outline` | | value(v-model) | Used for setting the currently selected value. | any | - | |
### RadioGroup Events ### RadioGroup Events

View File

@ -15,27 +15,28 @@ cover: https://gw.alipayobjects.com/zos/alicdn/8cYb5seNB/Radio.svg
## API ## API
### Radio ### Radio/Radio.Button
| 参数 | 说明 | 类型 | 默认值 | | 参数 | 说明 | 类型 | 默认值 |
| ---------------- | --------------------------------- | ------- | ------ | | ---------------- | --------------------------------- | ------- | ------ |
| autofocus | 自动获取焦点 | boolean | false | | autofocus | 自动获取焦点 | boolean | false |
| checked(v-model) | 指定当前是否选中 | boolean | false | | checked(v-model) | 指定当前是否选中 | boolean | false |
| disabled | 禁用 Radio | boolean | false |
| value | 根据 value 进行比较,判断是否选中 | any | - | | value | 根据 value 进行比较,判断是否选中 | any | - |
### RadioGroup ### RadioGroup
单选框组合,用于包裹一组 `Radio` 单选框组合,用于包裹一组 `Radio`
| 参数 | 说明 | 类型 | 默认值 | | 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | | --- | --- | --- | --- | --- |
| defaultValue | 默认选中的值 | any | - | | buttonStyle | RadioButton 的风格样式,目前有描边和填色两种风格 | `outline` \| `solid` | `outline` | |
| disabled | 禁选所有子单选器 | boolean | false | | disabled | 禁选所有子单选器 | boolean | false | |
| name | RadioGroup 下所有 `input[type="radio"]``name` 属性 | string | - | | name | RadioGroup 下所有 `input[type="radio"]``name` 属性 | string | - | |
| options | 以配置形式设置子元素 | string\[] \| Array&lt;{ label: string value: string disabled?: boolean }> | - | | options | 以配置形式设置子元素 | string\[] \| Array&lt;{ label: string value: string disabled?: boolean }> | - | |
| size | 大小,只对按钮样式生效 | `large` \| `default` \| `small` | `default` | | optionType | 用于设置 Radio `options` 类型 | `default` \| `button` | `default` | 3.0.0 |
| value(v-model) | 用于设置当前选中的值 | any | - | | size | 大小,只对按钮样式生效 | `large` \| `default` \| `small` | `default` | |
| buttonStyle | RadioButton 的风格样式,目前有描边和填色两种风格 | `outline` \| `solid` | `outline` | | value(v-model) | 用于设置当前选中的值 | any | - | |
### RadioGroup 事件 ### RadioGroup 事件

View File

@ -1,5 +1,5 @@
import type { RadioProps } from './Radio'; import type { RadioProps } from './Radio';
import type { Ref } from 'vue';
export interface RadioChangeEventTarget extends RadioProps { export interface RadioChangeEventTarget extends RadioProps {
checked: boolean; checked: boolean;
} }
@ -10,3 +10,9 @@ export interface RadioChangeEvent {
preventDefault: () => void; preventDefault: () => void;
nativeEvent: MouseEvent; nativeEvent: MouseEvent;
} }
export interface RadioGroupContext {
stateValue: Ref;
props: RadioProps;
onRadioChange: (e: RadioChangeEvent) => void;
}

View File

@ -5,34 +5,47 @@
@radio-group-prefix-cls: ~'@{radio-prefix-cls}-group'; @radio-group-prefix-cls: ~'@{radio-prefix-cls}-group';
@radio-inner-prefix-cls: ~'@{radio-prefix-cls}-inner'; @radio-inner-prefix-cls: ~'@{radio-prefix-cls}-inner';
@radio-duration: 0.3s; @radio-duration: 0.3s;
@radio-focus-shadow: 0 0 0 3px fade(@radio-dot-color, 8%); @radio-focus-shadow: 0 0 0 3px @primary-1;
@radio-button-focus-shadow: @radio-focus-shadow; @radio-button-focus-shadow: @radio-focus-shadow;
.@{radio-group-prefix-cls} { .@{radio-group-prefix-cls} {
.reset-component(); .reset-component();
display: inline-block; display: inline-block;
font-size: 0;
.@{ant-prefix}-badge-count {
z-index: 1;
}
> .@{ant-prefix}-badge:not(:first-child) > .@{radio-prefix-cls}-button-wrapper {
border-left: none;
}
} }
// 一般状态 // 一般状态
.@{radio-prefix-cls}-wrapper { .@{radio-prefix-cls}-wrapper {
.reset-component(); .reset-component();
position: relative; position: relative;
display: inline-block; display: inline-flex;
margin-right: 8px; align-items: center;
white-space: nowrap; margin-right: @radio-wrapper-margin-right;
cursor: pointer; cursor: pointer;
&::after {
display: inline-block;
width: 0;
overflow: hidden;
content: '\a0';
}
} }
.@{radio-prefix-cls} { .@{radio-prefix-cls} {
.reset-component(); .reset-component();
position: relative; position: relative;
top: @radio-top;
display: inline-block; display: inline-block;
line-height: 1;
white-space: nowrap;
vertical-align: sub;
outline: none; outline: none;
cursor: pointer; cursor: pointer;
@ -67,12 +80,10 @@
&-inner { &-inner {
&::after { &::after {
@radio-dot-size: @radio-size - 8px;
position: absolute; position: absolute;
top: ((@radio-size - @radio-dot-size) / 2) - @radio-border-width; top: ((@radio-size - @radio-dot-size) / 2) - @radio-border-width;
left: ((@radio-size - @radio-dot-size) / 2) - @radio-border-width; left: ((@radio-size - @radio-dot-size) / 2) - @radio-border-width;
display: table; display: block;
width: @radio-dot-size; width: @radio-dot-size;
height: @radio-dot-size; height: @radio-dot-size;
background-color: @radio-dot-color; background-color: @radio-dot-color;
@ -95,7 +106,7 @@
border-color: @border-color-base; border-color: @border-color-base;
border-style: solid; border-style: solid;
border-width: @radio-border-width; border-width: @radio-border-width;
border-radius: 100px; border-radius: 50%;
transition: all @radio-duration; transition: all @radio-duration;
} }
@ -124,6 +135,8 @@
} }
.@{radio-prefix-cls}-disabled { .@{radio-prefix-cls}-disabled {
cursor: not-allowed;
.@{radio-inner-prefix-cls} { .@{radio-inner-prefix-cls} {
background-color: @input-disabled-bg; background-color: @input-disabled-bg;
border-color: @border-color-base !important; border-color: @border-color-base !important;
@ -155,13 +168,14 @@ span.@{radio-prefix-cls} + * {
margin: 0; margin: 0;
padding: 0 @padding-md - 1px; padding: 0 @padding-md - 1px;
color: @radio-button-color; color: @radio-button-color;
font-size: @font-size-base;
line-height: @btn-height-base - 2px; line-height: @btn-height-base - 2px;
background: @radio-button-bg; background: @radio-button-bg;
border: @border-width-base @border-style-base @border-color-base; border: @border-width-base @border-style-base @border-color-base;
// strange align fix for chrome but works // strange align fix for chrome but works
// https://gw.alipayobjects.com/zos/rmsportal/VFTfKXJuogBAXcvfAUWJ.gif // https://gw.alipayobjects.com/zos/rmsportal/VFTfKXJuogBAXcvfAUWJ.gif
border-top-width: @border-width-base + 0.02px; border-top-width: @border-width-base + 0.02px;
border-left: 0; border-left-width: 0;
cursor: pointer; cursor: pointer;
transition: color 0.3s, background 0.3s, border-color 0.3s, box-shadow 0.3s; transition: color 0.3s, background 0.3s, border-color 0.3s, box-shadow 0.3s;
@ -170,10 +184,12 @@ span.@{radio-prefix-cls} + * {
} }
> .@{radio-prefix-cls}-button { > .@{radio-prefix-cls}-button {
display: block; position: absolute;
width: 0; top: 0;
height: 0; left: 0;
margin-left: 0; z-index: -1;
width: 100%;
height: 100%;
} }
.@{radio-group-prefix-cls}-large & { .@{radio-group-prefix-cls}-large & {
@ -203,6 +219,7 @@ span.@{radio-prefix-cls} + * {
content: ''; content: '';
} }
} }
&:first-child { &:first-child {
border-left: @border-width-base @border-style-base @border-color-base; border-left: @border-width-base @border-style-base @border-color-base;
border-radius: @border-radius-base 0 0 @border-radius-base; border-radius: @border-radius-base 0 0 @border-radius-base;
@ -324,9 +341,4 @@ span.@{radio-prefix-cls} + * {
} }
} }
// Firefox hack @import './rtl';
@supports (-moz-appearance: meterbar) and (background-blend-mode: difference, normal) {
.@{radio-prefix-cls} {
vertical-align: text-bottom;
}
}

View File

@ -0,0 +1,61 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@radio-prefix-cls: ~'@{ant-prefix}-radio';
@radio-group-prefix-cls: ~'@{radio-prefix-cls}-group';
@radio-prefix-cls-button-wrapper: ~'@{radio-prefix-cls}-button-wrapper';
.@{radio-group-prefix-cls} {
&&-rtl {
direction: rtl;
}
}
// 一般状态
.@{radio-prefix-cls}-wrapper {
&&-rtl {
margin-right: 0;
margin-left: @radio-wrapper-margin-right;
direction: rtl;
}
}
.@{radio-prefix-cls-button-wrapper} {
&&-rtl {
border-right-width: 0;
border-left-width: @border-width-base;
}
&:not(:first-child) {
&::before {
.@{radio-prefix-cls-button-wrapper}.@{radio-prefix-cls-button-wrapper}-rtl& {
right: -1px;
left: 0;
}
}
}
&:first-child {
.@{radio-prefix-cls-button-wrapper}.@{radio-prefix-cls-button-wrapper}-rtl& {
border-right: @border-width-base @border-style-base @border-color-base;
border-radius: 0 @border-radius-base @border-radius-base 0;
}
.@{radio-prefix-cls-button-wrapper}-checked:not([class*=~"' @{radio-prefix-cls}-button-wrapper-disabled'"])& {
border-right-color: @radio-button-hover-color;
}
}
&:last-child {
.@{radio-prefix-cls-button-wrapper}.@{radio-prefix-cls-button-wrapper}-rtl& {
border-radius: @border-radius-base 0 0 @border-radius-base;
}
}
&-disabled {
&:first-child {
.@{radio-prefix-cls-button-wrapper}.@{radio-prefix-cls-button-wrapper}-rtl& {
border-right-color: @border-color-base;
}
}
}
}