refactor: radio context

pull/5820/head
tangjinzhou 2022-05-21 16:29:11 +08:00
parent a435e2c090
commit 44be8722f8
7 changed files with 82 additions and 40 deletions

View File

@ -1,4 +1,4 @@
import { provide, nextTick, defineComponent, ref, watch } from 'vue'; import { nextTick, defineComponent, ref, watch, computed } from 'vue';
import type { PropType, ExtractPropTypes } 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';
@ -7,6 +7,7 @@ import useConfigInject from '../_util/hooks/useConfigInject';
import { tuple } from '../_util/type'; import { tuple } from '../_util/type';
import type { RadioChangeEvent, RadioGroupButtonStyle, RadioGroupOptionType } from './interface'; import type { RadioChangeEvent, RadioGroupButtonStyle, RadioGroupOptionType } from './interface';
import { useInjectFormItemContext } from '../form/FormItemContext'; import { useInjectFormItemContext } from '../form/FormItemContext';
import { useProvideRadioGroupContext } from './context';
const RadioGroupSizeTypes = tuple('large', 'default', 'small'); const RadioGroupSizeTypes = tuple('large', 'default', 'small');
@ -74,14 +75,16 @@ export default defineComponent({
}); });
}; };
provide('radioGroupContext', { useProvideRadioGroupContext({
onRadioChange, onChange: onRadioChange,
stateValue, value: stateValue,
props, disabled: computed(() => props.disabled),
name: computed(() => props.name),
optionType: computed(() => props.optionType),
}); });
return () => { return () => {
const { options, optionType, buttonStyle, id = formItemContext.id.value } = props; const { options, buttonStyle, id = formItemContext.id.value } = props;
const groupPrefixCls = `${prefixCls.value}-group`; const groupPrefixCls = `${prefixCls.value}-group`;
@ -92,14 +95,12 @@ export default defineComponent({
let children = null; let children = null;
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' || typeof option === 'number') { if (typeof option === 'string' || typeof option === 'number') {
return ( return (
<Radio <Radio
key={option} key={option}
prefixCls={optionsPrefixCls} prefixCls={prefixCls.value}
disabled={props.disabled} disabled={props.disabled}
value={option} value={option}
checked={stateValue.value === option} checked={stateValue.value === option}
@ -112,7 +113,7 @@ export default defineComponent({
return ( return (
<Radio <Radio
key={`radio-group-value-options-${value}`} key={`radio-group-value-options-${value}`}
prefixCls={optionsPrefixCls} prefixCls={prefixCls.value}
disabled={disabled || props.disabled} disabled={disabled || props.disabled}
value={value} value={value}
checked={stateValue.value === value} checked={stateValue.value === value}

View File

@ -1,13 +1,14 @@
import type { ExtractPropTypes, PropType } from 'vue'; import type { ExtractPropTypes, PropType } from 'vue';
import { defineComponent, inject, ref } from 'vue'; import { computed, defineComponent, ref } from 'vue';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import VcCheckbox from '../vc-checkbox/Checkbox'; import VcCheckbox from '../vc-checkbox/Checkbox';
import classNames from '../_util/classNames'; import classNames from '../_util/classNames';
import useConfigInject from '../_util/hooks/useConfigInject'; import useConfigInject from '../_util/hooks/useConfigInject';
import type { RadioChangeEvent, RadioGroupContext } from './interface'; import type { RadioChangeEvent } from './interface';
import { useInjectFormItemContext } from '../form/FormItemContext'; import { FormItemInputContext, useInjectFormItemContext } from '../form/FormItemContext';
import omit from '../_util/omit'; import omit from '../_util/omit';
import type { FocusEventHandler, MouseEventHandler } from '../_util/EventInterface'; import type { FocusEventHandler, MouseEventHandler } from '../_util/EventInterface';
import { useInjectRadioGroupContext, useInjectRadioOptionTypeContext } from './context';
export const radioProps = () => ({ export const radioProps = () => ({
prefixCls: String, prefixCls: String,
@ -31,13 +32,19 @@ export type RadioProps = Partial<ExtractPropTypes<ReturnType<typeof radioProps>>
export default defineComponent({ export default defineComponent({
name: 'ARadio', name: 'ARadio',
props: radioProps(), props: radioProps(),
// emits: ['update:checked', 'update:value', 'change', 'blur', 'focus'],
setup(props, { emit, expose, slots }) { setup(props, { emit, expose, slots }) {
const formItemContext = useInjectFormItemContext(); const formItemContext = useInjectFormItemContext();
const formItemInputContext = FormItemInputContext.useInject();
const radioOptionTypeContext = useInjectRadioOptionTypeContext();
const radioGroupContext = useInjectRadioGroupContext();
const vcCheckbox = ref<HTMLElement>(); const vcCheckbox = ref<HTMLElement>();
const radioGroupContext = inject<RadioGroupContext>('radioGroupContext', undefined);
const { prefixCls, direction } = useConfigInject('radio', props);
const { prefixCls: radioPrefixCls, direction } = useConfigInject('radio', props);
const prefixCls = computed(() =>
(radioGroupContext?.optionType.value || radioOptionTypeContext) === 'button'
? `${radioPrefixCls.value}-button`
: radioPrefixCls.value,
);
const focus = () => { const focus = () => {
vcCheckbox.value.focus(); vcCheckbox.value.focus();
}; };
@ -58,8 +65,8 @@ export default defineComponent({
const onChange = (e: RadioChangeEvent) => { const onChange = (e: RadioChangeEvent) => {
emit('change', e); emit('change', e);
if (radioGroupContext && radioGroupContext.onRadioChange) { if (radioGroupContext && radioGroupContext.onChange) {
radioGroupContext.onRadioChange(e); radioGroupContext.onChange(e);
} }
}; };
@ -74,10 +81,10 @@ export default defineComponent({
}; };
if (radioGroup) { if (radioGroup) {
rProps.name = radioGroup.props.name; rProps.name = radioGroup.name.value;
rProps.onChange = onChange; rProps.onChange = onChange;
rProps.checked = props.value === radioGroup.stateValue.value; rProps.checked = props.value === radioGroup.value.value;
rProps.disabled = props.disabled || radioGroup.props.disabled; rProps.disabled = props.disabled || radioGroup.disabled.value;
} else { } else {
rProps.onChange = handleChange; rProps.onChange = handleChange;
} }
@ -86,6 +93,7 @@ export default defineComponent({
[`${prefixCls.value}-wrapper-checked`]: rProps.checked, [`${prefixCls.value}-wrapper-checked`]: rProps.checked,
[`${prefixCls.value}-wrapper-disabled`]: rProps.disabled, [`${prefixCls.value}-wrapper-disabled`]: rProps.disabled,
[`${prefixCls.value}-wrapper-rtl`]: direction.value === 'rtl', [`${prefixCls.value}-wrapper-rtl`]: direction.value === 'rtl',
[`${prefixCls.value}-wrapper-in-form-item`]: formItemInputContext.isFormItemInput,
}); });
return ( return (

View File

@ -1,28 +1,20 @@
import { defineComponent, inject } from 'vue'; import { defineComponent } from 'vue';
import type { RadioProps } from './Radio';
import Radio, { radioProps } from './Radio'; import Radio, { radioProps } from './Radio';
import useConfigInject from '../_util/hooks/useConfigInject'; import useConfigInject from '../_util/hooks/useConfigInject';
import type { RadioGroupContext } from './interface'; import { useProvideRadioOptionTypeContext } from './context';
export default defineComponent({ export default defineComponent({
name: 'ARadioButton', name: 'ARadioButton',
props: radioProps(), props: radioProps(),
setup(props, { slots }) { setup(props, { slots }) {
const { prefixCls } = useConfigInject('radio-button', props); const { prefixCls } = useConfigInject('radio-button', props);
const radioGroupContext = inject<RadioGroupContext>('radioGroupContext', undefined); useProvideRadioOptionTypeContext('button');
return () => { return () => {
const rProps: RadioProps = { return (
...props, <Radio {...props} prefixCls={prefixCls.value} type="radio">
prefixCls: prefixCls.value, {slots.default?.()}
}; </Radio>
);
if (radioGroupContext) {
rProps.onChange = radioGroupContext.onRadioChange;
rProps.checked = rProps.value === radioGroupContext.stateValue.value;
rProps.disabled = rProps.disabled || radioGroupContext.props.disabled;
}
return <Radio {...rProps}>{slots.default?.()}</Radio>;
}; };
}, },
}); });

View File

@ -0,0 +1,23 @@
import type { InjectionKey } from 'vue';
import { inject, provide } from 'vue';
import type { RadioGroupContext, RadioOptionTypeContextProps } from './interface';
const radioGroupContextKey: InjectionKey<RadioGroupContext> = Symbol('radioGroupContextKey');
export const useProvideRadioGroupContext = (props: RadioGroupContext) => {
provide(radioGroupContextKey, props);
};
export const useInjectRadioGroupContext = () => {
return inject(radioGroupContextKey, undefined);
};
const radioOptionTypeContextKey: InjectionKey<RadioOptionTypeContextProps> = Symbol(
'radioOptionTypeContextKey',
);
export const useProvideRadioOptionTypeContext = (props: RadioOptionTypeContextProps) => {
provide(radioOptionTypeContextKey, props);
};
export const useInjectRadioOptionTypeContext = () => {
return inject(radioOptionTypeContextKey, undefined);
};

View File

@ -15,7 +15,17 @@ export interface RadioChangeEvent {
} }
export interface RadioGroupContext { export interface RadioGroupContext {
stateValue: Ref; onChange: (e: RadioChangeEvent) => void;
props: RadioProps; value: Ref<any>;
onRadioChange: (e: RadioChangeEvent) => void; disabled: Ref<boolean>;
name: Ref<string>;
/**
* Control the appearance for Radio to display as button or not
*
* @default 'default'
* @internal
*/
optionType?: Ref<RadioGroupOptionType>;
} }
export type RadioOptionTypeContextProps = RadioGroupOptionType;

View File

@ -42,6 +42,13 @@
overflow: hidden; overflow: hidden;
content: '\a0'; content: '\a0';
} }
&&-in-form-item {
input[type='radio'] {
width: 14px;
height: 14px;
}
}
} }
.@{radio-prefix-cls} { .@{radio-prefix-cls} {

View File

@ -1,2 +1,3 @@
import '../../style/index.less'; import '../../style/index.less';
import './index.less'; import './index.less';
// deps-lint-skip: form