chakra-ui
Version:
Responsive and accessible React UI components built with React and Emotion
266 lines (224 loc) • 7.37 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
/** @jsx jsx */
import { jsx } from "@emotion/core";
import propTypes from "prop-types";
import { createContext, useContext, useEffect, useRef, useState } from "react";
import PseudoBox from "../PseudoBox";
import Box from "../Box";
var EditableContext = createContext();
var Editable = function Editable(_ref) {
var valueProp = _ref.value,
defaultValue = _ref.defaultValue,
isDisabled = _ref.isDisabled,
onChange = _ref.onChange,
isEditingProp = _ref.isEditing,
onCancel = _ref.onCancel,
onSubmit = _ref.onSubmit,
_ref$selectAllOnFocus = _ref.selectAllOnFocus,
selectAllOnFocus = _ref$selectAllOnFocus === void 0 ? true : _ref$selectAllOnFocus,
submitOnBlur = _ref.submitOnBlur,
isPreviewFocusable = _ref.isPreviewFocusable,
_ref$placeholder = _ref.placeholder,
placeholder = _ref$placeholder === void 0 ? "Click to edit..." : _ref$placeholder,
children = _ref.children,
rest = _objectWithoutProperties(_ref, ["value", "defaultValue", "isDisabled", "onChange", "isEditing", "onCancel", "onSubmit", "selectAllOnFocus", "submitOnBlur", "isPreviewFocusable", "placeholder", "children"]);
var _useState = useState(isEditingProp && !isDisabled),
_useState2 = _slicedToArray(_useState, 2),
isEditing = _useState2[0],
setIsEditing = _useState2[1];
var _useRef = useRef(valueProp != null),
isControlled = _useRef.current;
var _useState3 = useState(defaultValue || ""),
_useState4 = _slicedToArray(_useState3, 2),
value = _useState4[0],
setValue = _useState4[1];
var _value = isControlled ? valueProp : value;
var _useState5 = useState(_value),
_useState6 = _slicedToArray(_useState5, 2),
previousValue = _useState6[0],
setPreviousValue = _useState6[1];
var inputRef = useRef(null);
var onRequestEdit = function onRequestEdit(event) {
if (!isDisabled) {
setIsEditing(true);
}
};
useEffect(function () {
if (isEditing && inputRef.current) {
inputRef.current.focus();
selectAllOnFocus && inputRef.current.select();
}
}, [isEditing, selectAllOnFocus]);
var handleCancel = function handleCancel() {
setIsEditing(false);
setValue(previousValue);
if (value !== previousValue) {
onChange && onChange(previousValue);
}
onCancel && onCancel(previousValue);
};
var handleSubmit = function handleSubmit() {
setIsEditing(false);
setPreviousValue(value);
onSubmit && onSubmit(value);
};
var handleChange = function handleChange(event) {
var value = event.target.value;
if (!isControlled) {
setValue(value);
}
onChange && onChange(value);
};
var handleKeyDown = function handleKeyDown(event) {
var key = event.key;
if (key === "Escape") {
handleCancel();
return;
}
if (key === "Enter") {
handleSubmit();
}
};
var handleFocus = function handleFocus(event) {
if (selectAllOnFocus) {
inputRef.current.select();
}
};
var childContext = {
inputRef: inputRef,
isEditing: isEditing,
isDisabled: isDisabled,
placeholder: placeholder,
onRequestEdit: onRequestEdit,
submitOnBlur: submitOnBlur,
isPreviewFocusable: isPreviewFocusable,
value: _value,
onKeyDown: handleKeyDown,
onChange: handleChange,
onSubmit: handleSubmit,
onCancel: handleCancel,
onFocus: handleFocus
};
return jsx(EditableContext.Provider, {
value: childContext
}, jsx(Box, rest, typeof children === "function" ? children({
isEditing: isEditing,
onSubmit: handleSubmit,
onCancel: handleCancel,
onRequestEdit: onRequestEdit
}) : children));
};
var sharedProps = {
fontSize: "inherit",
fontWeight: "inherit",
textAlign: "inherit",
bg: "transparent",
transition: "all 0.2s",
borderRadius: "md",
px: "3px",
mx: "-3px"
};
export var EditablePreview = function EditablePreview(props) {
var _useContext = useContext(EditableContext),
isEditing = _useContext.isEditing,
isDisabled = _useContext.isDisabled,
value = _useContext.value,
onRequestEdit = _useContext.onRequestEdit,
placeholder = _useContext.placeholder,
isPreviewFocusable = _useContext.isPreviewFocusable;
var hasValue = value != null && value !== "";
var getTabIndex = function getTabIndex() {
if ((!isEditing || !isDisabled) && isPreviewFocusable) {
return 0;
}
return null;
};
var styleProps = _extends({}, sharedProps, {
cursor: "text",
display: "inline-block",
opacity: !hasValue ? 0.6 : undefined
});
if (isEditing) {
return null;
}
return jsx(PseudoBox, _extends({
as: "span",
"aria-disabled": isDisabled,
tabIndex: getTabIndex(),
onFocus: onRequestEdit
}, styleProps, props), hasValue ? value : placeholder);
};
export var EditableInput = function EditableInput(props) {
var _useContext2 = useContext(EditableContext),
inputRef = _useContext2.inputRef,
isEditing = _useContext2.isEditing,
onChange = _useContext2.onChange,
onKeyDown = _useContext2.onKeyDown,
value = _useContext2.value,
onSubmit = _useContext2.onSubmit,
submitOnBlur = _useContext2.submitOnBlur,
placeholder = _useContext2.placeholder;
var styleProps = _extends({}, sharedProps, {
width: "full",
_placeholder: {
opacity: "0.6"
}
});
if (!isEditing) {
return null;
}
return jsx(PseudoBox, _extends({
as: "input",
ref: inputRef,
onBlur: function onBlur() {
submitOnBlur && onSubmit();
},
value: value,
placeholder: placeholder,
onChange: onChange,
onKeyDown: onKeyDown
}, styleProps, props));
};
process.env.NODE_ENV !== "production" ? Editable.propTypes = {
/** Text value of the controlled input */
value: propTypes.string,
/** Default text value of uncontrolled input. */
defaultValue: propTypes.string,
/**
* Whether the text can be edited.
* @default false
*/
isDisabled: propTypes.bool,
/**
* Whether the component should start with the edit mode active
* If `true`, the input is shown by default.
* @default false
*/
isEditing: propTypes.bool,
/** Callback invoked when user changes input in any way. */
onChange: propTypes.func,
/** Callback invoked when user cancels input with the `Esc` key. Receives last confirmed value. */
onCancel: propTypes.func,
/** Callback invoked when user confirms value with `enter` key or by blurring input. */
onSubmit: propTypes.func,
/** Callback invoked after the user enters edit mode. */
onEdit: propTypes.func,
/**
* If `true`, the input's text will be highlighted on focus.
* @default false
*/
selectAllOnFocus: propTypes.bool,
/**
* Placeholder text when the value is empty.
* @default "Click to Edit"
*/
placeholder: propTypes.string,
/** The content of the Editable
* Ideally only `EditablePreview` and `EditableInput` should
* be the children (but you add other elements too)
*/
children: propTypes.node
} : void 0;
export default Editable;