UNPKG

@neo4j-ndl/react

Version:

React implementation of Neo4j Design System

181 lines 9.62 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InlineEdit = void 0; const jsx_runtime_1 = require("react/jsx-runtime"); /** * * Copyright (c) "Neo4j" * Neo4j Sweden AB [http://neo4j.com] * * This file is part of Neo4j. * * Neo4j is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ const base_1 = require("@neo4j-ndl/base"); const react_1 = require("react"); const react_aria_1 = require("react-aria"); const defaultImports_1 = require("../_common/defaultImports"); const utils_1 = require("../_common/utils"); const icon_button_1 = require("../icon-button"); const icons_1 = require("../icons"); const tooltip_1 = require("../tooltip"); const typography_1 = require("../typography"); function getIconSize(variant) { switch (variant) { case 'h1': case 'h2': return '24'; case 'h3': case 'h4': case 'subheading-large': return '20'; default: return '16'; } } function getErrorIconSize(variant) { switch (variant) { case 'h1': case 'h2': return '24'; case 'h3': case 'h4': return '20'; default: return '16'; } } function isTypescalePropertyObj(obj) { return Boolean(obj && typeof obj === 'object' && ('lineHeight' in obj)); } function getMinHeight(variant) { let key; switch (variant) { case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': key = variant; break; default: key = `n-${variant}`; break; } const value = base_1.typescale[key][1]; if (isTypescalePropertyObj(value)) { return value.lineHeight; } // Subheading medium is the default, will never reach here, // just to satisfy the TS compiler return '1.5rem'; } exports.InlineEdit = (0, react_1.forwardRef)(function InlineEdit(props, ref) { const { label, defaultValue, variant = 'subheading-medium', isEditing = undefined, onConfirm, onCancel, errorText, hasEditIcon, isDisabled = false, inputProps = {}, typographyProps = {}, isCancellingOnBlur = false, validate, htmlAttributes, className, style, placeholder, } = props; const Component = props.as || 'div'; const [hasEditMode, setEditMode] = (0, react_1.useState)(false); const [internalErrorText, setErrorText] = (0, react_1.useState)(errorText); const [value, setValue] = (0, react_1.useState)(defaultValue); (0, react_1.useEffect)(() => { // Reset the value if the default value changes setValue(defaultValue); }, [defaultValue]); const iconSize = getIconSize(variant); const hasError = Boolean(internalErrorText); const hasLabel = Boolean(label); const { focusWithinProps } = (0, react_aria_1.useFocusWithin)({ onBlurWithin: () => { if (isCancellingOnBlur) { onCancelHandler(); } }, }); const resetToDefaults = (0, react_1.useCallback)(() => { setValue(defaultValue); setErrorText(undefined); setEditMode(false); }, [defaultValue]); const onConfirmHandler = (0, react_1.useCallback)(() => __awaiter(this, void 0, void 0, function* () { if (validate) { const validation = validate(value); let isValid; if (validation instanceof Promise) { isValid = yield validation; } else { isValid = validation; } if (typeof isValid === 'string') { setErrorText(isValid); return; } } onConfirm === null || onConfirm === void 0 ? void 0 : onConfirm(value); resetToDefaults(); }), [onConfirm, validate, value, resetToDefaults]); const onCancelHandler = (0, react_1.useCallback)(() => { onCancel === null || onCancel === void 0 ? void 0 : onCancel(); resetToDefaults(); }, [onCancel, resetToDefaults]); const inputID = (0, react_1.useMemo)(() => { return inputProps.id || (0, utils_1.randomId)(12); }, [inputProps.id]); return ((0, jsx_runtime_1.jsxs)(Component, Object.assign({}, focusWithinProps, { className: (0, defaultImports_1.classNames)(`n-${variant} ndl-inline-edit`, className, { 'ndl-disabled': isDisabled, }), style: style, ref: ref }, htmlAttributes, { children: [hasLabel && (0, jsx_runtime_1.jsx)("label", { htmlFor: inputID, children: label }), (isEditing !== null && isEditing !== void 0 ? isEditing : hasEditMode) ? ((0, jsx_runtime_1.jsxs)("div", { className: (0, defaultImports_1.classNames)('ndl-inline-edit-container', { 'ndl-inline-edit-error': hasError, }), children: [(0, jsx_runtime_1.jsx)("input", Object.assign({ // eslint-disable-next-line jsx-a11y/no-autofocus autoFocus: true, value: value, onChange: (e) => setValue(e.target.value), id: inputID }, inputProps, { // Enforce line-height to be the same as the typography's line-height className: (0, defaultImports_1.classNames)(`n-${variant}`, inputProps.className), // Custom padding for avoiding icon overlap style: Object.assign({ paddingRight: hasError ? `${parseInt(getErrorIconSize(variant)) + 4}px` : undefined }, inputProps.style), onKeyDown: (e) => { if (['Enter', 'Escape'].includes(e.key)) { e.key === 'Enter' && onConfirmHandler(); e.key === 'Escape' && onCancelHandler(); e.preventDefault(); e.stopPropagation(); } } })), hasError && ((0, jsx_runtime_1.jsxs)(tooltip_1.Tooltip, { type: "simple", children: [(0, jsx_runtime_1.jsx)(tooltip_1.Tooltip.Trigger, { children: (0, jsx_runtime_1.jsx)(icons_1.ExclamationCircleIconSolid, { "data-testid": "ndl-inline-edit-error-icon", "aria-label": "Error message", style: { width: `${getErrorIconSize(variant)}px`, height: `${getErrorIconSize(variant)}px`, }, className: "ndl-inline-edit-error-icon" }) }), (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip.Content, { children: internalErrorText })] })), (0, jsx_runtime_1.jsxs)("div", { className: "ndl-inline-edit-buttons", children: [(0, jsx_runtime_1.jsx)(icon_button_1.IconButton, { "data-testid": "ndl-confirm-button", ariaLabel: "Accept input's value change", onClick: onConfirmHandler, size: "small", isFloating: true, htmlAttributes: { 'data-testid': 'ndl-confirm-button', }, children: (0, jsx_runtime_1.jsx)(icons_1.CheckIconOutline, {}) }), (0, jsx_runtime_1.jsx)(icon_button_1.IconButton, { "data-testid": "ndl-cancel-button", ariaLabel: "Ignore input's value change", onClick: onCancelHandler, size: "small", isFloating: true, htmlAttributes: { 'data-testid': 'ndl-cancel-button', }, children: (0, jsx_runtime_1.jsx)(icons_1.XMarkIconOutline, {}) })] })] })) : ((0, jsx_runtime_1.jsxs)("button", { className: (0, defaultImports_1.classNames)('ndl-inline-idle-container', { 'ndl-disabled': isDisabled, 'n-text-palette-neutral-text-weaker': !defaultValue && placeholder, }), style: { minHeight: getMinHeight(variant), }, onClick: () => !isDisabled && setEditMode(!hasEditMode), children: [(0, jsx_runtime_1.jsx)(typography_1.Typography, Object.assign({}, typographyProps, { variant: variant, children: defaultValue || placeholder })), hasEditIcon && ((0, jsx_runtime_1.jsx)(icons_1.PencilIconOutline, { style: { width: `${iconSize}px`, height: `${iconSize}px`, flexShrink: 0, } }))] }))] }), defaultValue)); }); //# sourceMappingURL=InlineEdit.js.map