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

View File

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

View File

@ -1,28 +1,20 @@
import { defineComponent, inject } from 'vue';
import type { RadioProps } from './Radio';
import { defineComponent } from 'vue';
import Radio, { radioProps } from './Radio';
import useConfigInject from '../_util/hooks/useConfigInject';
import type { RadioGroupContext } from './interface';
import { useProvideRadioOptionTypeContext } from './context';
export default defineComponent({
name: 'ARadioButton',
props: radioProps(),
setup(props, { slots }) {
const { prefixCls } = useConfigInject('radio-button', props);
const radioGroupContext = inject<RadioGroupContext>('radioGroupContext', undefined);
useProvideRadioOptionTypeContext('button');
return () => {
const rProps: RadioProps = {
...props,
prefixCls: prefixCls.value,
};
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>;
return (
<Radio {...props} prefixCls={prefixCls.value} type="radio">
{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 {
stateValue: Ref;
props: RadioProps;
onRadioChange: (e: RadioChangeEvent) => void;
onChange: (e: RadioChangeEvent) => void;
value: Ref<any>;
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;
content: '\a0';
}
&&-in-form-item {
input[type='radio'] {
width: 14px;
height: 14px;
}
}
}
.@{radio-prefix-cls} {

View File

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