UNPKG

react-edit-text

Version:

Simple editable text component for React

435 lines (395 loc) 11.8 kB
import classnames from 'classnames'; import React from 'react'; import { jsx, jsxs } from 'react/jsx-runtime'; import PropTypes from 'prop-types'; function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } const EditIcon = () => { return /*#__PURE__*/jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 512 512", children: /*#__PURE__*/jsx("path", { d: "M421.7 220.3L188.5 453.4L154.6 419.5L158.1 416H112C103.2 416 96 408.8 96 400V353.9L92.51 357.4C87.78 362.2 84.31 368 82.42 374.4L59.44 452.6L137.6 429.6C143.1 427.7 149.8 424.2 154.6 419.5L188.5 453.4C178.1 463.8 165.2 471.5 151.1 475.6L30.77 511C22.35 513.5 13.24 511.2 7.03 504.1C.8198 498.8-1.502 489.7 .976 481.2L36.37 360.9C40.53 346.8 48.16 333.9 58.57 323.5L291.7 90.34L421.7 220.3zM492.7 58.75C517.7 83.74 517.7 124.3 492.7 149.3L444.3 197.7L314.3 67.72L362.7 19.32C387.7-5.678 428.3-5.678 453.3 19.32L492.7 58.75z" }) }); }; var styles = {"label":"_4GdcU","textareaView":"_G0k44","shared":"_i5--j","placeholder":"_TDklp","inline":"_gmkRL","readonly":"_-wzeg","displayContainer":"_IYz6Z","editButton":"_NGZSv"}; const _excluded$1 = ["props", "inputRef", "handleBlur", "handleKeydown", "handleFocus", "inputClassName"]; const Input = _ref => { let { props: { id, inline, style, type, name }, inputRef, handleBlur, handleKeydown, handleFocus, inputClassName } = _ref, rest = _objectWithoutPropertiesLoose(_ref, _excluded$1); return /*#__PURE__*/jsx("input", _extends({ id: id, className: classnames(styles.shared, inputClassName, { [styles.inline]: inline }), style: style, ref: inputRef, type: type, name: name, onBlur: handleBlur, onKeyDown: handleKeydown, autoFocus: true, onFocus: handleFocus }, rest)); }; const sharedPropTypes = { id: PropTypes.string, name: PropTypes.string, className: PropTypes.string, value: PropTypes.string, formatDisplayText: PropTypes.func, defaultValue: PropTypes.string, placeholder: PropTypes.string, onSave: PropTypes.func, onChange: PropTypes.func, onEditMode: PropTypes.func, onBlur: PropTypes.func, style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), readonly: PropTypes.bool, inputClassName: PropTypes.string }; const EditTextPropTypes = _extends({}, sharedPropTypes, { type: PropTypes.string, inline: PropTypes.bool, showEditButton: PropTypes.bool, editButtonContent: PropTypes.any, editButtonProps: PropTypes.object }); const EditTextareaPropTypes = _extends({}, sharedPropTypes, { rows: PropTypes.oneOfType([PropTypes.number, PropTypes.oneOf(['auto'])]) }); function EditText({ id, name, className, placeholder = '', inline = false, style = {}, readonly = false, type = 'text', value, defaultValue, formatDisplayText = x => x, onEditMode = () => {}, onChange = () => {}, onSave = () => {}, onBlur = () => {}, showEditButton = false, editButtonContent = /*#__PURE__*/jsx(EditIcon, {}), editButtonProps = {}, inputClassName }) { const inputRef = React.useRef(null); const [changeEvent, setChangeEvent] = React.useState({}); const [previousValue, setPreviousValue] = React.useState(''); const [savedText, setSavedText] = React.useState(''); const [editMode, setEditMode] = React.useState(false); React.useEffect(() => { if (defaultValue !== undefined) { setPreviousValue(defaultValue); setSavedText(defaultValue); } }, [defaultValue]); React.useEffect(() => { if (value !== undefined) { setSavedText(value); if (!editMode) { setPreviousValue(value); } } }, [value, editMode]); const handleClickDisplay = () => { if (readonly || showEditButton) return; setEditMode(true); onEditMode(); }; const handleClickEditButton = () => { setEditMode(true); onEditMode(); }; const handleBlur = (save = true) => { if (inputRef.current) { const { name: inputName, value: inputValue } = inputRef.current; if (save && previousValue !== inputValue) { onSave({ name: inputName, value: inputValue, previousValue: previousValue }); setSavedText(inputValue); setPreviousValue(inputValue); } else if (!save) { onChange(_extends({}, changeEvent, { target: changeEvent.target ? _extends({}, changeEvent.target, { value: previousValue }) : { value: previousValue } })); } setEditMode(false); onBlur(); } }; const handleKeydown = e => { if (e.keyCode === 13 || e.charCode === 13) { handleBlur(); } else if (e.keyCode === 27 || e.charCode === 27) { handleBlur(false); } }; const handleFocus = e => { if (type === 'text') { e.currentTarget.setSelectionRange(e.currentTarget.value.length, e.currentTarget.value.length); } }; const renderDisplayMode = () => { return /*#__PURE__*/jsxs("div", { className: classnames(styles.displayContainer, { [styles.inline]: inline }), children: [/*#__PURE__*/jsx("div", { id: id, className: classnames(styles.label, styles.shared, { [styles.placeholder]: placeholder && !savedText, [styles.inline]: inline, [styles.readonly]: readonly || showEditButton }, className), onClick: handleClickDisplay, style: style, "aria-label": "display component", children: formatDisplayText(savedText) || placeholder }), showEditButton && !readonly && /*#__PURE__*/jsx("button", _extends({ type: "button", className: styles.editButton }, editButtonProps, { onClick: handleClickEditButton, children: editButtonContent }))] }); }; const renderEditMode = controlled => { const sharedProps = { inputRef: inputRef, handleBlur: handleBlur, handleKeydown: handleKeydown, handleFocus: handleFocus, props: { id, inline, style, type, name }, ['aria-label']: 'input component' }; return controlled ? /*#__PURE__*/jsx(Input, _extends({}, sharedProps, { value: value, onChange: e => { setChangeEvent(e); onChange(e); }, inputClassName: inputClassName })) : /*#__PURE__*/jsx(Input, _extends({}, sharedProps, { defaultValue: savedText, inputClassName: inputClassName })); }; return !readonly && editMode ? renderEditMode(value !== undefined && onChange !== undefined) : renderDisplayMode(); } EditText.propTypes = EditTextPropTypes; const _excluded = ["props", "inputRef", "handleBlur", "handleKeydown", "inputClassName"]; const Textarea = _ref => { let { props: { id, rows, style, name }, inputRef, handleBlur, handleKeydown, inputClassName } = _ref, rest = _objectWithoutPropertiesLoose(_ref, _excluded); return /*#__PURE__*/jsx("textarea", _extends({ id: id, className: classnames(styles.shared, inputClassName), style: style, ref: inputRef, rows: rows, name: name, onBlur: handleBlur, onKeyDown: handleKeydown, autoFocus: true, onFocus: e => e.currentTarget.setSelectionRange(e.currentTarget.value.length, e.currentTarget.value.length) }, rest)); }; const splitLines = val => val ? val.split(/\r?\n/) : []; function EditTextarea({ id, rows = 3, name, className, placeholder = '', style = {}, readonly = false, value, defaultValue, formatDisplayText = x => x, onEditMode = () => {}, onChange = () => {}, onSave = () => {}, onBlur = () => {}, inputClassName }) { const inputRef = React.useRef(null); const [changeEvent, setChangeEvent] = React.useState({}); const [previousValue, setPreviousValue] = React.useState(''); const [savedText, setSavedText] = React.useState(''); const [editMode, setEditMode] = React.useState(false); React.useEffect(() => { if (defaultValue !== undefined) { setPreviousValue(defaultValue); setSavedText(defaultValue); } }, [defaultValue]); React.useEffect(() => { if (value !== undefined) { setSavedText(value); if (!editMode) { setPreviousValue(value); } } }, [value, editMode]); const handleClick = () => { if (readonly) return; setEditMode(true); onEditMode(); }; const handleBlur = (save = true) => { if (inputRef.current) { const { name: inputName, value: inputValue } = inputRef.current; if (save && previousValue !== inputValue) { onSave({ name: inputName, value: inputValue, previousValue: previousValue }); setSavedText(inputValue); setPreviousValue(inputValue); } else if (!save) { onChange(_extends({}, changeEvent, { target: changeEvent.target ? _extends({}, changeEvent.target, { value: previousValue }) : { value: previousValue } })); } setEditMode(false); onBlur(); } }; const handleKeydown = e => { if (e.keyCode === 27 || e.charCode === 27) { handleBlur(false); } }; const renderDisplayMode = () => { const textLines = splitLines(formatDisplayText(savedText)); return /*#__PURE__*/jsx("div", { id: id, className: classnames(styles.shared, styles.textareaView, { [styles.placeholder]: placeholder && !savedText, [styles.readonly]: readonly }, className), onClick: handleClick, style: _extends({}, style, { height: rows === 'auto' ? 'auto' : `${rows * 24 + 16}px` }), "aria-label": "display component", children: textLines.length > 0 ? textLines.map((text, index) => /*#__PURE__*/jsxs(React.Fragment, { children: [/*#__PURE__*/jsx("span", { children: text }), /*#__PURE__*/jsx("br", {})] }, index)) : /*#__PURE__*/jsx("span", { children: placeholder }) }); }; const renderEditMode = controlled => { const sharedProps = { inputRef: inputRef, handleBlur: handleBlur, handleKeydown: handleKeydown, props: { id, rows, style, name }, ['aria-label']: 'textarea component' }; return controlled ? /*#__PURE__*/jsx(Textarea, _extends({}, sharedProps, { value: value, onChange: e => { setChangeEvent(e); onChange(e); }, inputClassName: inputClassName })) : /*#__PURE__*/jsx(Textarea, _extends({}, sharedProps, { defaultValue: savedText, inputClassName: inputClassName })); }; return !readonly && editMode ? renderEditMode(value !== undefined && onChange !== undefined) : renderDisplayMode(); } EditTextarea.propTypes = EditTextareaPropTypes; export { EditText, EditTextarea }; //# sourceMappingURL=index.modern.mjs.map