UNPKG

monday-ui-react-core

Version:

Official monday.com UI resources for application development in React.js

243 lines (217 loc) • 6.06 kB
import React, { useRef, forwardRef, useLayoutEffect, useCallback, useState, useEffect } from "react"; import PropTypes from "prop-types"; import cx from "classnames"; import autosize from "autosize"; import { isEnterEvent, isEscapeEvent, isTabEvent, isArrowUpEvent, isArrowDownEvent, isArrowLeftEvent, isArrowRightEvent } from "../../utils/dom-event-utils"; import useMergeRefs from "../../hooks/useMergeRefs"; import "./EditableInput.scss"; export const TEXTAREA_TYPE = "textarea"; const isTextArea = inputType => { return TEXTAREA_TYPE === inputType; }; const EditableInput = forwardRef( ( { className, inputType, autoSize, id, tabIndex, autoComplete, maxLength, placeholder, onClick, onKeyPress, shouldFocusOnMount, selectOnMount, value, ignoreBlurClass, onFinishEditing, onFocus, onBlur, isValidValue, onChange, onError, onSuccess, onKeyDown, onTabHandler, onCancelEditing, textareaSubmitOnEnter, onArrowKeyDown }, ref ) => { // State const [valueState, setValueState] = useState(value || ""); // Refs const componentRef = useRef(null); const mergedRef = useMergeRefs({ refs: [ref, componentRef] }); // Callbacks const autosizeIfNeeded = useCallback(() => { if (componentRef.current && autoSize && isTextArea(inputType)) { autosize(componentRef.current); } }, [componentRef, autoSize, inputType]); const focus = useCallback(() => { if (componentRef.current) { componentRef.current.focus(); } }, [componentRef]); const onFocusCallback = useCallback( event => { if (onFocus) { onFocus(event); } }, [onFocus] ); const onBlurCallback = useCallback( event => { const shouldIgnoreBlur = (el, ignoreClass) => { return el && ignoreBlurClass && el.classList.contains(ignoreClass); }; const { relatedTarget } = event; if (shouldIgnoreBlur(relatedTarget, ignoreBlurClass)) { return; } const enrichedEvent = event; enrichedEvent.origin = "blur"; if (onFinishEditing) { onFinishEditing(valueState, enrichedEvent); } if (onBlur) { onBlur(enrichedEvent); } }, [ignoreBlurClass, valueState, onFinishEditing, onBlur] ); const onChangeCallback = useCallback( event => { const { value: newValue } = event.target; if (!isValidValue || isValidValue(newValue)) { setValueState(newValue); onChange && onChange(newValue); onSuccess && onSuccess(); } else { onError && onError(); } }, [isValidValue, onChange, onError, onSuccess] ); const select = useCallback(() => { if (componentRef.current) { componentRef.current.select(); } }, [componentRef]); const moveCaretAtEnd = useCallback(() => { if (componentRef.current) { componentRef.current.value = ""; componentRef.current.value = valueState; } }, [componentRef, valueState]); const onKeyDownCallback = useCallback( event => { if (onKeyDown) { return onKeyDown(event, valueState); } if (onTabHandler && isTabEvent(event) && !isTextArea(inputType)) { event.preventDefault(); return onTabHandler(valueState, event); } if (onFinishEditing && isEnterEvent(event) && (!isTextArea(inputType) || textareaSubmitOnEnter)) { onFinishEditing(valueState, event); } if (onCancelEditing && isEscapeEvent(event)) { onCancelEditing(event); } if ( onArrowKeyDown && (isArrowUpEvent(event) || isArrowDownEvent(event) || isArrowLeftEvent(event) || isArrowRightEvent(event)) ) { onArrowKeyDown(valueState, event); } }, [ onKeyDown, inputType, valueState, onTabHandler, textareaSubmitOnEnter, onFinishEditing, onCancelEditing, onArrowKeyDown ] ); // Callbacks END // Effects useLayoutEffect(() => { if (shouldFocusOnMount) focus(); autosizeIfNeeded(); selectOnMount ? select() : moveCaretAtEnd(); }, []); useEffect(() => { setValueState(value); }, [value]); const rows = isTextArea(inputType) && autoSize ? "1" : undefined; const InputType = inputType; return ( <InputType ref={mergedRef} id={id} className={cx("editable-input--wrapper", className, { "no-resize": autoSize })} onChange={onChangeCallback} onKeyDown={onKeyDownCallback} onBlur={onBlurCallback} onFocus={onFocusCallback} onClick={onClick} onKeyPress={onKeyPress} value={valueState} placeholder={placeholder} dir="auto" tabIndex={tabIndex} autoComplete={autoComplete ? "on" : "off"} rows={rows} maxLength={maxLength} /> ); } ); EditableInput.propTypes = { className: PropTypes.string, placeholder: PropTypes.string, inputType: PropTypes.oneOf("input", "textarea"), autoSize: PropTypes.bool, autoComplete: PropTypes.bool, maxLength: PropTypes.number, shouldFocusOnMount: PropTypes.bool, isValidValue: PropTypes.func, onFinishEditing: PropTypes.func, onArrowKeyDown: PropTypes.func, onCancelEditing: PropTypes.func, textareaSubmitOnEnter: PropTypes.bool }; EditableInput.defaultProps = { className: "", placeholder: "", inputType: "input", autoSize: false, autoComplete: true, maxLength: undefined, shouldFocusOnMount: true, isValidValue: undefined, onFinishEditing: undefined, onArrowKeyDown: undefined, onCancelEditing: undefined, textareaSubmitOnEnter: false }; export default EditableInput;