/** * ✔ bindchange */ import { JSX, useRef, forwardRef, ReactNode, useContext, useMemo, useEffect, createElement } from 'react' import { View, NativeSyntheticEvent, ViewStyle } from 'react-native' import { warn } from '@mpxjs/utils' import { FormContext, FormFieldValue, RadioGroupContext, GroupValue } from './context' import useInnerProps, { getCustomEvent } from './getInnerListeners' import useNodesRef, { HandlerRef } from './useNodesRef' import { useLayout, useTransformStyle, wrapChildren, extendObject } from './utils' import Portal from './mpx-portal' export interface RadioGroupProps { name: string style?: ViewStyle & Record 'enable-offset'?: boolean 'enable-var'?: boolean 'external-var-context'?: Record 'parent-font-size'?: number 'parent-width'?: number 'parent-height'?: number children: ReactNode bindchange?: (evt: NativeSyntheticEvent | unknown) => void } const radioGroup = forwardRef< HandlerRef, RadioGroupProps >((props, ref): JSX.Element => { const { style = {}, 'enable-var': enableVar, 'external-var-context': externalVarContext, 'parent-font-size': parentFontSize, 'parent-width': parentWidth, 'parent-height': parentHeight } = props const propsRef = useRef({}) propsRef.current = props const formContext = useContext(FormContext) let formValuesMap: Map | undefined if (formContext) { formValuesMap = formContext.formValuesMap } const groupValue: GroupValue = useRef({}).current const defaultStyle = { flexDirection: 'row', flexWrap: 'wrap' } const styleObj = extendObject({}, defaultStyle, style) const { hasPositionFixed, hasSelfPercent, normalStyle, hasVarDec, varContextRef, setWidth, setHeight } = useTransformStyle(styleObj, { enableVar, externalVarContext, parentFontSize, parentWidth, parentHeight }) const nodeRef = useRef(null) useNodesRef(props, ref, nodeRef, { style: normalStyle }) const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef }) const getValue = (): string | undefined => { for (const key in groupValue) { if (groupValue[key].checked) { return key } } } const resetValue = () => { Object.keys(groupValue).forEach((key) => { groupValue[key].checked = false groupValue[key].setValue(false) }) } if (formValuesMap) { if (!props.name) { warn('If a form component is used, the name attribute is required.') } else { formValuesMap.set(props.name, { getValue, resetValue }) } } useEffect(() => { return () => { if (formValuesMap && props.name) { formValuesMap.delete(props.name) } } }, []) const contextValue = useMemo(() => { const notifyChange = ( evt: NativeSyntheticEvent ) => { const { bindchange } = propsRef.current bindchange && bindchange( getCustomEvent( 'tap', evt, { layoutRef, detail: { value: getValue() } }, propsRef.current ) ) } return { groupValue, notifyChange } }, []) const innerProps = useInnerProps( extendObject( {}, props, layoutProps, { ref: nodeRef, style: extendObject({}, normalStyle, layoutStyle) } ), ['name'], { layoutRef } ) const finalComponent = createElement(View, innerProps, createElement( RadioGroupContext.Provider, { value: contextValue }, wrapChildren( props, { hasVarDec, varContext: varContextRef.current } ) ) ) if (hasPositionFixed) { return createElement(Portal, null, finalComponent) } return finalComponent }) radioGroup.displayName = 'MpxRadioGroup' export default radioGroup