UNPKG

@onehat/ui

Version:
147 lines (130 loc) 4.03 kB
import { forwardRef, useState, useEffect, useRef, useContext, useCallback, } from 'react'; import natsort from 'natsort'; import useForceUpdate from '../../Hooks/useForceUpdate.js'; import FieldSetContext from '../../Contexts/FieldSetContext.js'; import _ from 'lodash'; // This HOC gives the component value props, primarily for a Form Field. export default function withValue(WrappedComponent) { return forwardRef((props, ref) => { if (props.disableWithValue || props.alreadyHasWithValue) { return <WrappedComponent {...props} ref={ref} />; } const { onChangeValue, value, startingValue = null, isValueAlwaysArray = false, isValueAsStringifiedJson = false, // withComponent self, // withData Repository, idIx, } = props, forceUpdate = useForceUpdate(), childRef = useRef({}), onChangeValueRef = useRef(), localValueRef = useRef(startingValue || value), fieldSetOnChangeValueRef = useRef(), fieldSetContext = useContext(FieldSetContext), fieldSetRegisterChild = fieldSetContext?.registerChild, fieldSetOnChangeValue = fieldSetContext?.onChangeValue, getLocalValue = () => { return localValueRef.current; }, setLocalValue = (value) => { localValueRef.current = value; forceUpdate(); }, setValueRef = useRef((newValue) => { // NOTE: We useRef so that this function stays current after renders if (isValueAlwaysArray && !_.isArray(newValue)) { newValue = _.isNil(newValue) ? [] : [newValue]; } if (_.isArray(newValue)) { const sortFn = natsort.default || natsort; // was having trouble with webpack and this solves it // TODO: sort by the sortProperty, whatever that is, instead of just value newValue.sort(sortFn()); // Only sort if we're using id/text arrangement. Otherwise, keep sort order as specified in Repository. } if (isValueAsStringifiedJson) { newValue = JSON.stringify(newValue); } if (newValue === getLocalValue()) { return; } setLocalValue(newValue); if (onChangeValueRef.current) { onChangeValueRef.current(newValue, childRef.current); } if (fieldSetOnChangeValueRef.current) { fieldSetOnChangeValueRef.current(newValue, childRef.current); } }), setValue = (args) => { setValueRef.current(args); }, onChangeSelection = (selection) => { let value = null, values; if (selection.length) { if (Repository) { if (selection.length === 1) { value = selection[0].id; } else { values = _.map(selection, (entity) => entity.id); } } else { if (selection.length === 1) { value = selection[0][idIx]; } else { values = _.map(selection, (item) => item[idIx]); } } } if (values) { value = values; } setValue(value); }; // Ensure these passed functions stay current after render onChangeValueRef.current = onChangeValue; fieldSetOnChangeValueRef.current = fieldSetOnChangeValue; useEffect(() => { if (!_.isEqual(value, getLocalValue())) { setLocalValue(value); } }, [value]); if (fieldSetRegisterChild) { useEffect(() => { fieldSetRegisterChild({ childRef: childRef.current, value, setValue: setValueRef.current, }); }, []); } if (self) { self.setValue = setValue; self.value = getLocalValue(); } // Convert localValue to normal JS primitives for field components let convertedValue = getLocalValue(); if (_.isString(convertedValue) && isValueAsStringifiedJson && !_.isNil(convertedValue)) { convertedValue = JSON.parse(convertedValue); } if (isValueAlwaysArray) { if (_.isEmpty(convertedValue) || _.isNil(convertedValue)) { convertedValue = []; } } return <WrappedComponent {...props} disableWithValue={false} alreadyHasWithValue={true} ref={ref} value={convertedValue} setValue={setValueRef.current} onChangeSelection={onChangeSelection} />; }); }