Skip to content

Commit cf3fe6b

Browse files
ajunertangjinzhou
andauthored
refactor(radio): use composition api (#4720)
* refactor(radio): use composition api * docs: update * chore: update * docs: update * Update Group.tsx Co-authored-by: tangjinzhou <[email protected]>
1 parent f653955 commit cf3fe6b

13 files changed

+323
-248
lines changed

components/radio/Group.tsx

+106-106
Original file line numberDiff line numberDiff line change
@@ -1,133 +1,133 @@
1-
import { provide, inject, nextTick, defineComponent } from 'vue';
1+
import { provide, nextTick, defineComponent, ref, watch, onBeforeMount } from 'vue';
2+
import type { PropType, ExtractPropTypes } from 'vue';
23
import classNames from '../_util/classNames';
34
import PropTypes from '../_util/vue-types';
45
import Radio from './Radio';
5-
import { getOptionProps, filterEmpty, hasProp, getSlot } from '../_util/props-util';
6-
import { defaultConfigProvider } from '../config-provider';
6+
import useConfigInject from '../_util/hooks/useConfigInject';
77
import { tuple } from '../_util/type';
88
import type { RadioChangeEvent } from './interface';
99
import { useInjectFormItemContext } from '../form/FormItemContext';
1010

11+
const RadioGroupSizeTypes = tuple('large', 'default', 'small');
12+
13+
export type RadioGroupSize = typeof RadioGroupSizeTypes[number];
14+
15+
const RadioGroupOptionTypes = tuple('default', 'button');
16+
17+
export type RadioGroupOption = typeof RadioGroupOptionTypes[number];
18+
19+
export type RadioGroupChildOption = {
20+
label: string;
21+
value: string;
22+
disabled?: boolean;
23+
};
24+
25+
const radioGroupProps = {
26+
prefixCls: PropTypes.string,
27+
value: PropTypes.any,
28+
size: PropTypes.oneOf(RadioGroupSizeTypes).def('default'),
29+
options: {
30+
type: Array as PropType<Array<String | RadioGroupChildOption>>,
31+
},
32+
disabled: PropTypes.looseBool,
33+
name: PropTypes.string,
34+
buttonStyle: PropTypes.string.def('outline'),
35+
id: PropTypes.string,
36+
optionType: PropTypes.oneOf(RadioGroupOptionTypes).def('default'),
37+
};
38+
39+
export type RadioGroupProps = Partial<ExtractPropTypes<typeof radioGroupProps>>;
40+
1141
export default defineComponent({
1242
name: 'ARadioGroup',
13-
props: {
14-
prefixCls: PropTypes.string,
15-
defaultValue: PropTypes.any,
16-
value: PropTypes.any,
17-
size: PropTypes.oneOf(tuple('large', 'default', 'small')).def('default'),
18-
options: PropTypes.array,
19-
disabled: PropTypes.looseBool,
20-
name: PropTypes.string,
21-
buttonStyle: PropTypes.string.def('outline'),
22-
onChange: PropTypes.func,
23-
id: PropTypes.string,
24-
},
43+
props: radioGroupProps,
2544
emits: ['update:value', 'change'],
26-
setup() {
45+
setup(props, { slots, emit }) {
2746
const formItemContext = useInjectFormItemContext();
28-
return {
29-
formItemContext,
30-
updatingValue: false,
31-
configProvider: inject('configProvider', defaultConfigProvider),
32-
radioGroupContext: null,
33-
};
34-
},
35-
data() {
36-
const { value, defaultValue } = this;
37-
return {
38-
stateValue: value === undefined ? defaultValue : value,
39-
};
40-
},
41-
watch: {
42-
value(val) {
43-
this.updatingValue = false;
44-
this.stateValue = val;
45-
},
46-
},
47-
// computed: {
48-
// radioOptions() {
49-
// const { disabled } = this;
50-
// return this.options.map(option => {
51-
// return typeof option === 'string'
52-
// ? { label: option, value: option }
53-
// : { ...option, disabled: option.disabled === undefined ? disabled : option.disabled };
54-
// });
55-
// },
56-
// },
57-
created() {
58-
this.radioGroupContext = provide('radioGroupContext', this);
59-
},
60-
methods: {
61-
onRadioChange(ev: RadioChangeEvent) {
62-
const lastValue = this.stateValue;
47+
const { prefixCls } = useConfigInject('radio', props);
48+
const stateValue = ref(props.value === undefined ? props.defaultValue : props.value);
49+
const updatingValue = ref<boolean>(false);
50+
watch(
51+
() => props.value,
52+
val => {
53+
stateValue.value = val;
54+
updatingValue.value = false;
55+
},
56+
);
57+
58+
const onRadioChange = (ev: RadioChangeEvent) => {
59+
const lastValue = stateValue.value;
6360
const { value } = ev.target;
64-
if (!hasProp(this, 'value')) {
65-
this.stateValue = value;
61+
62+
if (!('value' in props)) {
63+
stateValue.value = value;
6664
}
6765
// nextTick for https://github.com/vueComponent/ant-design-vue/issues/1280
68-
if (!this.updatingValue && value !== lastValue) {
69-
this.updatingValue = true;
70-
this.$emit('update:value', value);
71-
this.$emit('change', ev);
72-
this.formItemContext.onFieldChange();
66+
if (!updatingValue.value && value !== lastValue) {
67+
updatingValue.value = true;
68+
emit('update:value', value);
69+
emit('change', ev);
70+
formItemContext.onFieldChange();
7371
}
7472
nextTick(() => {
75-
this.updatingValue = false;
73+
updatingValue.value = false;
7674
});
77-
},
78-
},
79-
render() {
80-
const props = getOptionProps(this);
81-
const {
82-
prefixCls: customizePrefixCls,
83-
options,
84-
buttonStyle,
85-
id = this.formItemContext.id.value,
86-
} = props;
87-
const { getPrefixCls } = this.configProvider;
88-
const prefixCls = getPrefixCls('radio', customizePrefixCls);
89-
90-
const groupPrefixCls = `${prefixCls}-group`;
91-
const classString = classNames(groupPrefixCls, `${groupPrefixCls}-${buttonStyle}`, {
92-
[`${groupPrefixCls}-${props.size}`]: props.size,
75+
};
76+
77+
provide('radioGroupContext', {
78+
onRadioChange,
79+
stateValue,
80+
props,
9381
});
9482

95-
let children = filterEmpty(getSlot(this));
83+
return () => {
84+
const { options, optionType, buttonStyle, id = formItemContext.id.value } = props;
85+
86+
const groupPrefixCls = `${prefixCls.value}-group`;
9687

97-
// 如果存在 options, 优先使用
98-
if (options && options.length > 0) {
99-
children = options.map(option => {
100-
if (typeof option === 'string') {
88+
const classString = classNames(groupPrefixCls, `${groupPrefixCls}-${buttonStyle}`, {
89+
[`${groupPrefixCls}-${props.size}`]: props.size,
90+
});
91+
92+
let children = null;
93+
if (options && options.length > 0) {
94+
const optionsPrefixCls =
95+
optionType === 'button' ? `${prefixCls.value}-button` : prefixCls.value;
96+
children = options.map(option => {
97+
if (typeof option === 'string') {
98+
return (
99+
<Radio
100+
key={option}
101+
prefixCls={optionsPrefixCls}
102+
disabled={props.disabled}
103+
value={option}
104+
checked={stateValue.value === option}
105+
>
106+
{option}
107+
</Radio>
108+
);
109+
}
110+
const { value, disabled, label } = option as RadioGroupChildOption;
101111
return (
102112
<Radio
103-
key={option}
104-
prefixCls={prefixCls}
105-
disabled={props.disabled}
106-
value={option}
107-
checked={this.stateValue === option}
113+
key={`radio-group-value-options-${value}`}
114+
prefixCls={optionsPrefixCls}
115+
disabled={disabled || props.disabled}
116+
value={value}
117+
checked={stateValue.value === value}
108118
>
109-
{option}
119+
{label}
110120
</Radio>
111121
);
112-
}
113-
return (
114-
<Radio
115-
key={`radio-group-value-options-${option.value}`}
116-
prefixCls={prefixCls}
117-
disabled={option.disabled || props.disabled}
118-
value={option.value}
119-
checked={this.stateValue === option.value}
120-
>
121-
{option.label}
122-
</Radio>
123-
);
124-
});
125-
}
126-
127-
return (
128-
<div class={classString} id={id}>
129-
{children}
130-
</div>
131-
);
122+
});
123+
} else {
124+
children = slots.default?.();
125+
}
126+
return (
127+
<div class={classString} id={id}>
128+
{children}
129+
</div>
130+
);
131+
};
132132
},
133133
});

components/radio/Radio.tsx

+57-64
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
11
import type { ExtractPropTypes } from 'vue';
2-
import { defineComponent, inject } from 'vue';
2+
import { defineComponent, inject, ref } from 'vue';
33
import PropTypes from '../_util/vue-types';
44
import VcCheckbox from '../vc-checkbox';
55
import classNames from '../_util/classNames';
6-
import { getOptionProps } from '../_util/props-util';
7-
import { defaultConfigProvider } from '../config-provider';
8-
import type { RadioChangeEvent } from './interface';
6+
import useConfigInject from '../_util/hooks/useConfigInject';
7+
import type { RadioChangeEvent, RadioGroupContext } from './interface';
98
import { useInjectFormItemContext } from '../form/FormItemContext';
109

1110
export const radioProps = {
1211
prefixCls: PropTypes.string,
13-
defaultChecked: PropTypes.looseBool,
1412
checked: PropTypes.looseBool,
1513
disabled: PropTypes.looseBool,
1614
isGroup: PropTypes.looseBool,
@@ -30,72 +28,67 @@ export default defineComponent({
3028
name: 'ARadio',
3129
props: radioProps,
3230
emits: ['update:checked', 'update:value', 'change', 'blur', 'focus'],
33-
setup() {
31+
setup(props, { emit, expose, slots }) {
3432
const formItemContext = useInjectFormItemContext();
35-
return {
36-
configProvider: inject('configProvider', defaultConfigProvider),
37-
radioGroupContext: inject('radioGroupContext', null),
38-
formItemContext,
33+
const vcCheckbox = ref<HTMLElement>();
34+
const radioGroupContext = inject<RadioGroupContext>('radioGroupContext');
35+
const { prefixCls } = useConfigInject('radio', props);
36+
37+
const focus = () => {
38+
vcCheckbox.value.focus();
3939
};
40-
},
41-
methods: {
42-
focus() {
43-
(this.$refs.vcCheckbox as HTMLInputElement).focus();
44-
},
45-
blur() {
46-
(this.$refs.vcCheckbox as HTMLInputElement).blur();
47-
},
48-
handleChange(event: RadioChangeEvent) {
40+
41+
const blur = () => {
42+
vcCheckbox.value.blur();
43+
};
44+
45+
expose({ focus, blur });
46+
47+
const handleChange = (event: RadioChangeEvent) => {
4948
const targetChecked = event.target.checked;
50-
this.$emit('update:checked', targetChecked);
51-
this.$emit('update:value', targetChecked);
52-
this.$emit('change', event);
53-
this.formItemContext.onFieldChange();
54-
},
55-
onChange2(e: RadioChangeEvent) {
56-
this.$emit('change', e);
57-
if (this.radioGroupContext && this.radioGroupContext.onRadioChange) {
58-
this.radioGroupContext.onRadioChange(e);
49+
emit('update:checked', targetChecked);
50+
emit('update:value', targetChecked);
51+
emit('change', event);
52+
formItemContext.onFieldChange();
53+
};
54+
55+
const onChange = (e: RadioChangeEvent) => {
56+
emit('change', e);
57+
if (radioGroupContext && radioGroupContext.onRadioChange) {
58+
radioGroupContext.onRadioChange(e);
5959
}
60-
},
61-
},
60+
};
6261

63-
render() {
64-
const { $slots, radioGroupContext: radioGroup } = this;
65-
const props = getOptionProps(this);
66-
const {
67-
prefixCls: customizePrefixCls,
68-
id = this.formItemContext.id.value,
69-
...restProps
70-
} = props;
71-
const { getPrefixCls } = this.configProvider;
72-
const prefixCls = getPrefixCls('radio', customizePrefixCls);
62+
return () => {
63+
const radioGroup = radioGroupContext;
64+
const { prefixCls: customizePrefixCls, id = formItemContext.id.value, ...restProps } = props;
7365

74-
const rProps: RadioProps = {
75-
prefixCls,
76-
id,
77-
...restProps,
78-
};
66+
const rProps: RadioProps = {
67+
prefixCls: prefixCls.value,
68+
id,
69+
...restProps,
70+
};
7971

80-
if (radioGroup) {
81-
rProps.name = radioGroup.name;
82-
rProps.onChange = this.onChange2;
83-
rProps.checked = props.value === radioGroup.stateValue;
84-
rProps.disabled = props.disabled || radioGroup.disabled;
85-
} else {
86-
rProps.onChange = this.handleChange;
87-
}
88-
const wrapperClassString = classNames({
89-
[`${prefixCls}-wrapper`]: true,
90-
[`${prefixCls}-wrapper-checked`]: rProps.checked,
91-
[`${prefixCls}-wrapper-disabled`]: rProps.disabled,
92-
});
72+
if (radioGroup) {
73+
rProps.name = radioGroup.props.name;
74+
rProps.onChange = onChange;
75+
rProps.checked = props.value === radioGroup.stateValue.value;
76+
rProps.disabled = props.disabled || radioGroup.props.disabled;
77+
} else {
78+
rProps.onChange = handleChange;
79+
}
80+
const wrapperClassString = classNames({
81+
[`${prefixCls.value}-wrapper`]: true,
82+
[`${prefixCls.value}-wrapper-checked`]: rProps.checked,
83+
[`${prefixCls.value}-wrapper-disabled`]: rProps.disabled,
84+
});
9385

94-
return (
95-
<label class={wrapperClassString}>
96-
<VcCheckbox {...rProps} ref="vcCheckbox" />
97-
{$slots.default && <span>{$slots.default()}</span>}
98-
</label>
99-
);
86+
return (
87+
<label class={wrapperClassString}>
88+
<VcCheckbox {...rProps} ref={vcCheckbox} />
89+
{slots.default && <span>{slots.default()}</span>}
90+
</label>
91+
);
92+
};
10093
},
10194
});

0 commit comments

Comments
 (0)