UNPKG

@amaui/ui-react

Version:
797 lines (796 loc) 49.7 kB
"use strict"; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = __importDefault(require("react")); const utils_1 = require("@amaui/utils"); const style_react_1 = require("@amaui/style-react"); const IconMaterialFormatItalicW100_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialFormatItalicW100")); const IconMaterialFormatUnderlinedW100_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialFormatUnderlinedW100")); const IconMaterialStrikethroughSW100_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialStrikethroughSW100")); const IconMaterialFormatBoldW100_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialFormatBoldW100")); const IconMaterialAddLinkW100_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialAddLinkW100")); const IconMaterialLinkOffW100_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialLinkOffW100")); const IconMaterialFormatClearW100_1 = __importDefault(require("@amaui/icons-material-rounded-react/IconMaterialFormatClearW100")); const TextField_1 = __importDefault(require("../TextField/TextField")); const Line_1 = __importDefault(require("../Line")); const List_1 = __importDefault(require("../List")); const ListItem_1 = __importDefault(require("../ListItem")); const Avatar_1 = __importDefault(require("../Avatar")); const Type_1 = __importDefault(require("../Type")); const Surface_1 = __importDefault(require("../Surface")); const Button_1 = __importDefault(require("../Button")); const Append_1 = __importDefault(require("../Append")); const Fade_1 = __importDefault(require("../Fade")); const Tooltip_1 = __importDefault(require("../Tooltip")); const ToggleButton_1 = __importDefault(require("../ToggleButton")); const ClickListener_1 = __importDefault(require("../ClickListener")); const ToggleButtons_1 = __importDefault(require("../ToggleButtons")); const Menu_1 = __importDefault(require("../Menu")); const utils_2 = require("../utils"); const useStyle = (0, style_react_1.style)(theme => ({ root: { minHeight: '20px', width: '100%', '& .amaui-TextField-input': Object.assign({ wordBreak: 'break-word', color: theme.methods.palette.color.value('primary', 10) }, theme.typography.values.b2) }, root_type: { minHeight: '20px', width: '100%', cursor: 'text', '& .amaui-TextField-input': Object.assign({ wordBreak: 'break-word', color: theme.methods.palette.color.value('primary', 10) }, theme.typography.values.b2) }, menu: { width: '100vw', maxWidth: '240px', borderRadius: theme.methods.shape.radius.value(2.5), boxShadow: '0px 4px 32px 0px rgba(0, 0, 0, 0.04)', background: theme.palette.light ? theme.palette.color.neutral[99] : theme.palette.color.neutral[20] }, list: { maxHeight: '240px', overflow: 'hidden auto' }, textMiniMenuWrapper: { paddingBlock: theme.methods.space.value(1, 'px') }, textMiniMenu: { padding: theme.methods.space.value(1.5, 'px'), borderRadius: theme.methods.shape.radius.value(140, 'px'), boxShadow: theme.shadows.values.default[2] }, textMiniMenuAdditionalMenu: { width: 'clamp(140px, 90%, 250px)', zIndex: '1514' }, inputWrapper: { padding: theme.methods.space.value(1, 'px'), borderRadius: theme.methods.shape.radius.value(1, 'px'), boxShadow: theme.shadows.values.default[2] }, singleLine: { whiteSpace: 'nowrap', overflow: 'hidden' }, inputLink: { '&.amaui-TextField-root': { flex: '1 1 auto !important' } }, helperText: { display: 'inline-flex', color: theme.palette.text.default.secondary, userSelect: 'none' }, error_color: { color: [theme.palette.light ? theme.palette.color.error[40] : theme.palette.color.error[80], '!important'] }, error_hover_color: { color: [theme.palette.light ? theme.palette.color.error[20] : theme.palette.color.error[90], '!important'] } }), { name: 'amaui-SmartTextField' }); const SmartTextField = react_1.default.forwardRef((props_, ref) => { var _a; const theme = (0, style_react_1.useAmauiTheme)(); const props = react_1.default.useMemo(() => { var _a, _b, _c, _d, _e, _f, _g, _h; return (Object.assign(Object.assign(Object.assign({}, (_d = (_c = (_b = (_a = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _a === void 0 ? void 0 : _a.elements) === null || _b === void 0 ? void 0 : _b.all) === null || _c === void 0 ? void 0 : _c.props) === null || _d === void 0 ? void 0 : _d.default), (_h = (_g = (_f = (_e = theme === null || theme === void 0 ? void 0 : theme.ui) === null || _e === void 0 ? void 0 : _e.elements) === null || _f === void 0 ? void 0 : _f.amauiSmartTextField) === null || _g === void 0 ? void 0 : _g.props) === null || _h === void 0 ? void 0 : _h.default), props_)); }, [props_]); const Line = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Line) || Line_1.default; }, [theme]); const TextField = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.TextField) || TextField_1.default; }, [theme]); const List = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.List) || List_1.default; }, [theme]); const ListItem = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.ListItem) || ListItem_1.default; }, [theme]); const Avatar = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Avatar) || Avatar_1.default; }, [theme]); const Type = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Type) || Type_1.default; }, [theme]); const Surface = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Surface) || Surface_1.default; }, [theme]); const Button = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Button) || Button_1.default; }, [theme]); const Append = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Append) || Append_1.default; }, [theme]); const Fade = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Fade) || Fade_1.default; }, [theme]); const Tooltip = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Tooltip) || Tooltip_1.default; }, [theme]); const ToggleButton = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.ToggleButton) || ToggleButton_1.default; }, [theme]); const ClickListener = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.ClickListener) || ClickListener_1.default; }, [theme]); const ToggleButtons = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.ToggleButtons) || ToggleButtons_1.default; }, [theme]); const Menu = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Menu) || Menu_1.default; }, [theme]); const { version = 'text', size = 'regular', valueDefault, value: value_, inputProps, onChange, error: error_, name, placeholder, edit, mention, multiline, onChangeMention, optionsMention: optionsMention_, error, helperText, mentionLabel, onKeyDown: onKeyDown_, pasteText = true, readOnly, additional, HelperTextProps, className } = props, other = __rest(props, ["version", "size", "valueDefault", "value", "inputProps", "onChange", "error", "name", "placeholder", "edit", "mention", "multiline", "onChangeMention", "optionsMention", "error", "helperText", "mentionLabel", "onKeyDown", "pasteText", "readOnly", "additional", "HelperTextProps", "className"]); const { classes } = useStyle(); const [value, setValue] = react_1.default.useState(valueDefault || ''); const [openMenu, setOpenMenu] = react_1.default.useState(false); const [offset, setOffset] = react_1.default.useState([0, 8]); const [selectionMenu, setSelectionMenu] = react_1.default.useState(); const [optionsMention, setOptionsMention] = react_1.default.useState([]); const [textSelection, setTextSelection] = react_1.default.useState(); const [textSelected, setTextSelected] = react_1.default.useState([]); const [open, setOpen] = react_1.default.useState({}); const [inputValues, setInputValues] = react_1.default.useState({}); const refs = { root: react_1.default.useRef(undefined), input: react_1.default.useRef(undefined), searchElement: react_1.default.useRef(undefined), error: react_1.default.useRef(undefined), search: react_1.default.useRef(undefined), open: react_1.default.useRef(undefined), openMenu: react_1.default.useRef(undefined), selectionMenu: react_1.default.useRef(undefined), caret: react_1.default.useRef(undefined), value: react_1.default.useRef(undefined), version: react_1.default.useRef(version), optionsMention: react_1.default.useRef(optionsMention), validateMeta: react_1.default.useRef({}), range: react_1.default.useRef(undefined), inputValues: react_1.default.useRef(undefined), miniMenu: react_1.default.useRef(undefined), miniMenuElements: { color: react_1.default.useRef(undefined), colorPalette: react_1.default.useRef(undefined), background: react_1.default.useRef(undefined), backgroundPalette: react_1.default.useRef(undefined), linkAdd: react_1.default.useRef(undefined), linkAddInput: react_1.default.useRef(undefined), linkRemove: react_1.default.useRef(undefined) }, textSelected: react_1.default.useRef(undefined), textSelection: react_1.default.useRef(undefined), rootDocument: react_1.default.useRef(), rootWindow: react_1.default.useRef(), edit: react_1.default.useRef(edit), multiline: react_1.default.useRef(multiline), readOnly: react_1.default.useRef(readOnly), onChange: react_1.default.useRef(onChange) }; refs.value.current = value; refs.version.current = version; refs.open.current = open; refs.openMenu.current = openMenu; refs.selectionMenu.current = selectionMenu; refs.optionsMention.current = optionsMention; refs.inputValues.current = inputValues; refs.textSelected.current = textSelected; refs.textSelection.current = textSelection; refs.edit.current = edit; refs.multiline.current = multiline; refs.readOnly.current = readOnly; refs.onChange.current = onChange; const rootDocument = (0, utils_1.isEnvironment)('browser') && (((_a = refs.root.current) === null || _a === void 0 ? void 0 : _a.ownerDocument) || window.document); const rootWindow = rootDocument && (rootDocument.defaultView || window); refs.rootDocument.current = rootDocument; refs.rootWindow.current = rootWindow; const init = react_1.default.useCallback(() => { if (version === 'text') { const valueNew = valueDefault || value || ''; if (refs.root.current) { refs.root.current.innerHTML = valueNew; } } }, [version, value, valueDefault]); react_1.default.useEffect(() => { // init init(); }, []); react_1.default.useEffect(() => { if (optionsMention !== optionsMention_) setOptionsMention(optionsMention_); }, [optionsMention_]); react_1.default.useEffect(() => { const valuePrevious = (refs.root.current.innerHTML || '').replaceAll('&nbsp;', ' '); const valueNew = (0, utils_1.textToInnerHTML)(value_); if (value_ !== undefined && valuePrevious !== valueNew) validate(!value_ ? '' : (0, utils_2.sanitize)(value_)); }, [value_]); // Save selection to revive, use it on execComand react_1.default.useEffect(() => { var _a, _b; const selection_ = refs.rootWindow.current.getSelection(); if (selection_ && selection_.anchorNode && !((_b = (_a = selection_.anchorNode) === null || _a === void 0 ? void 0 : _a.className) === null || _b === void 0 ? void 0 : _b.includes('TextField'))) refs.range.current = selection_.getRangeAt(0); }, [open]); const updateInputValues = (property, itemValue) => { setInputValues((values) => (Object.assign(Object.assign({}, values), { [property]: itemValue }))); }; const updateOpen = (property, itemValue) => { setOpen((values) => (Object.assign(Object.assign({}, values), { [property]: itemValue }))); }; const validateElement = react_1.default.useCallback((element) => { if (element === null || element === void 0 ? void 0 : element.children) { Array.from(element === null || element === void 0 ? void 0 : element.children).forEach((child) => { // Any tag if (child.dataset.amauiVersion === 'mention-user') { // Replace the child if label & textContent don't match if (child.dataset.amauiLabel !== child.innerHTML) { element.replaceChild(window.document.createTextNode(child.textContent), child); refs.validateMeta.current.restore = true; } } if (child === null || child === void 0 ? void 0 : child.children) validateElement(child); }); } }, []); const validate = react_1.default.useCallback((valueNew = undefined) => { // Save caret position refs.caret.current = utils_2.caret.save(refs.root.current); // Update the root innerHTML if (valueNew !== undefined) refs.root.current.innerHTML = valueNew; refs.validateMeta.current.restore = false; // Find all span amaui-mention children // if they aren't valid, replace them with text node of their textContent validateElement(refs.root.current); if (refs.validateMeta.current.restore) utils_2.caret.restore(refs.root.current, refs.caret.current); return refs.root.current.innerHTML; }, []); const getAtSearchData = react_1.default.useCallback(() => { var _a, _b, _c, _d, _e; if (refs.root.current) { const windowSelection = window.document.getSelection(); if (!(windowSelection && windowSelection.rangeCount > 0)) return; const range = windowSelection === null || windowSelection === void 0 ? void 0 : windowSelection.getRangeAt(0); if (!range) return; // Only allow for text not in span user mention element if (((_c = (_b = (_a = windowSelection.anchorNode) === null || _a === void 0 ? void 0 : _a.parentElement) === null || _b === void 0 ? void 0 : _b.dataset) === null || _c === void 0 ? void 0 : _c.amauiVersion) === 'mention-user') return ''; // Text if (!['text', '#text'].includes((_d = range === null || range === void 0 ? void 0 : range.commonAncestorContainer) === null || _d === void 0 ? void 0 : _d.nodeName)) return ''; const text = ((_e = windowSelection.anchorNode) === null || _e === void 0 ? void 0 : _e.textContent) || ''; const start = text.slice(0, range.endOffset).split('').reverse().join(''); let result = []; for (const valueText of start) { // All characters other than space, numbers if (!!valueText.match(/[^<>\\/\n\r\t]/)) result.unshift(valueText); else break; if (['@'].includes(valueText)) break; } result = result.includes('@') ? result.join('') : ''; // Allow 1 space only if (result && result.match(/\s{2}/g)) return ''; return result; } }, []); refs.search.current = getAtSearchData(); const onInput = react_1.default.useCallback((event) => { if (refs.root.current) { let valueInput = refs.root.current.innerHTML || ''; if (refs.root.current.textContent === '') { valueInput = ''; refs.root.current.innerHTML = ''; } if (!refs.multiline.current) { const previous = valueInput; valueInput = valueInput.replace(/\r?\n|\r/gm, ''); if (previous !== valueInput) refs.root.current.innerHTML = valueInput; } valueInput = (0, utils_1.innerHTMLToText)(valueInput); setValue(valueInput); if ((0, utils_1.is)('function', refs.onChange.current)) refs.onChange.current(valueInput, event); } }, []); react_1.default.useEffect(() => { if (mention && refs.search.current !== undefined) onChangeMention === null || onChangeMention === void 0 ? void 0 : onChangeMention(refs.search.current, '@'); }, [mention, refs.search.current]); react_1.default.useEffect(() => { const method = () => { if (window.document.activeElement === refs.input.current) onInput(); }; window.document.addEventListener('selectionchange', method); onChangeMention === null || onChangeMention === void 0 ? void 0 : onChangeMention(refs.search.current, '@'); return () => { window.document.removeEventListener('selectionchange', method); }; }, []); const updateSelection = react_1.default.useCallback(() => { const windowSelection = window.document.getSelection(); if (!(windowSelection && windowSelection.rangeCount > 0)) return; const range = windowSelection === null || windowSelection === void 0 ? void 0 : windowSelection.getRangeAt(0); if (range) setSelectionMenu(range === null || range === void 0 ? void 0 : range.getBoundingClientRect()); }, []); const onOpenSearch = react_1.default.useCallback(async () => { if (!refs.openMenu.current && !!refs.optionsMention.current.length) { // open setOpenMenu(true); // selection updateSelection(); } }, []); const onCloseSearch = react_1.default.useCallback(() => { if (refs.openMenu.current) { setOpenMenu(false); setSelectionMenu(null); } }, []); const updateOffset = react_1.default.useCallback(() => { var _a; setOffset([-(((_a = refs.searchElement.current) === null || _a === void 0 ? void 0 : _a.clientWidth) || 0), 8]); }, []); const onFocus = react_1.default.useCallback(() => { // setFocus(true); }, []); const onBlur = react_1.default.useCallback(() => { // setFocus(false); }, []); const onKeyDown = react_1.default.useCallback((event) => { var _a; const windowSelection = window.getSelection(); if (windowSelection) { // if (event.key === 'Enter') { // event.preventDefault(); // const anchor = windowSelection.anchorNode as HTMLElement; // const element = anchor.parentElement; // const multiple = anchor.tagName === 'P' && anchor.children.length === 1 && anchor.children[0].tagName === 'BR'; // window.document.execCommand('insertHTML', false, `<p><br /></p>`.repeat(multiple ? 2 : 1)); // const range = new Range(); // const rangeStartElement = ((multiple ? anchor : element) as any)?.nextElementSibling; // if (rangeStartElement) range.setStart(rangeStartElement, 0); // windowSelection.removeAllRanges(); // windowSelection.addRange(range); // } if (event.key === 'ArrowRight') { // If in user mention span // move out of it with adding new empty space root node after the user mention span // only if caret is at the end of the span, & span is last child of it's parent if (!(windowSelection && windowSelection.rangeCount > 0)) return; const range = windowSelection.getRangeAt(0); const caretElement = windowSelection.anchorNode.parentElement; const caretElementParent = caretElement.parentElement; const caretElementParentChildNodes = Array.from(caretElementParent.childNodes); if (((_a = caretElement === null || caretElement === void 0 ? void 0 : caretElement.dataset) === null || _a === void 0 ? void 0 : _a.amauiVersion) === 'mention-user' && range.endOffset === windowSelection.anchorNode.textContent.length && caretElementParentChildNodes[caretElementParentChildNodes.length - 1] === caretElement) { caretElementParent.insertBefore(window.document.createTextNode('\u00A0'), caretElement.nextElementSibling); } } } if (!refs.multiline.current) { if (event.key === 'Enter') { event.preventDefault(); } } if (!refs.edit.current && ((event.metaKey && (utils_2.keyboardStyleCommands.includes(event.key))) || (event.ctrlKey && (utils_2.keyboardStyleCommands.includes(event.key))))) { event.preventDefault(); } if ((0, utils_1.is)('function', onKeyDown_)) onKeyDown_(event); }, [onKeyDown_]); const onPaste = react_1.default.useCallback((event) => { // event.preventDefault(); // const text = event.clipboardData?.getData('text/plain'); // window.document.execCommand('insertText', false, text); }, []); const onPasteText = react_1.default.useCallback((event) => { var _a; event.preventDefault(); const text = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.getData('text/plain'); window.document.execCommand('insertText', false, text); }, []); const onDrop = react_1.default.useCallback((event) => { event.preventDefault(); }, []); const addTag = react_1.default.useCallback((item, versionMention = 'user') => { // Save caret position refs.caret.current = utils_2.caret.save(refs.root.current); const itemName = item.name; const classesElement = ['amaui-mention']; // Query text node that equals selection anchorNode // and replace its textContent's search value with new span const windowSelection = window.document.getSelection(); if (!(windowSelection && windowSelection.rangeCount > 0)) return; const range = windowSelection === null || windowSelection === void 0 ? void 0 : windowSelection.getRangeAt(0); if (!range) return; const textNode = windowSelection.anchorNode; const textContent = textNode.textContent; // Bug fix // only be able to insert data within the input // if by accident selection went outside the input // basically do nothing with it if (refs.root.current.contains(textNode.parentElement)) { const end = range.endOffset; const start = end - refs.search.current.length; const pre = textContent.slice(0, start); const post = textContent.slice(end); // Insert before anchorNode pre, item mention, and post if (pre) textNode.parentElement.insertBefore(window.document.createTextNode(pre), windowSelection.anchorNode); const span = window.document.createElement('span'); span.className = classesElement.join(' '); span.dataset.amauiLabel = `@${itemName}`; span.dataset.amauiVersion = `mention-${versionMention}`; span.dataset.amauiObject = `${versionMention}`; span.dataset.amauiId = item.id; span.innerHTML = `@${itemName}`; textNode.parentElement.insertBefore(span, windowSelection.anchorNode); // 1 space only following the item mention textNode.parentElement.insertBefore(window.document.createTextNode('\u00A0'), windowSelection.anchorNode); if (post) textNode.parentElement.insertBefore(window.document.createTextNode(post), windowSelection.anchorNode); // Remove the text node textNode.remove(); // Invoke onChange method with new value const valueInput = (0, utils_1.innerHTMLToText)(refs.root.current.innerHTML); if ((0, utils_1.is)('function', refs.onChange.current)) refs.onChange.current(valueInput, { target: refs.root.current }); // Update the caret position to be outside the span mention // for amount of added characters + 1 space const added = `@${itemName}`.length - refs.search.current.length + 1; refs.caret.current.start = refs.caret.current.end += added; utils_2.caret.restore(refs.root.current, refs.caret.current); } // Close the search onCloseSearch(); }, [onChange]); // If users response & not open, open // else if no users response & opened, close the search react_1.default.useEffect(() => { if (mention) { if (refs.openMenu.current && !optionsMention.length) onCloseSearch(); else if (!refs.openMenu.current && refs.search.current && !!optionsMention.length) onOpenSearch(); } }, [mention, optionsMention]); react_1.default.useEffect(() => { if (mention) { // Validate validate(); // Update // open, selection & offset setTimeout(() => { refs.search.current ? onOpenSearch() : onCloseSearch(); if (refs.openMenu.current) updateSelection(); updateOffset(); }); } }, [mention, value]); react_1.default.useEffect(() => { // setError(error_); }, [error_]); const label = react_1.default.useCallback(() => { var _a; return ((0, utils_1.is)('function', mentionLabel) ? mentionLabel(refs.optionsMention.current, { addTag }) : (0, jsx_runtime_1.jsx)(Line, Object.assign({ className: classes.menu }, { children: (0, jsx_runtime_1.jsx)(List, Object.assign({ size: 'small', className: classes.list }, { children: (_a = refs.optionsMention.current) === null || _a === void 0 ? void 0 : _a.map((item, index) => { var _a; return ((0, jsx_runtime_1.jsx)(ListItem, { start: ((0, jsx_runtime_1.jsx)(Avatar, Object.assign({ color: (0, utils_1.stringToColor)(item.name), size: 'small' }, { children: (_a = item.name) === null || _a === void 0 ? void 0 : _a.slice(0, 1) }))), primary: ((0, jsx_runtime_1.jsx)(Type, Object.assign({ version: 'l3' }, { children: item.name }))), onClick: () => addTag(item), button: true }, index)); }) })) }))); }, []); const paste = async () => { const valueClipboard = await navigator.clipboard.read(); if (valueClipboard) { let values = ''; for (const item of Array.from(valueClipboard)) { const valueItem = await item.getType('text/html'); values += await valueItem.text(); } rootDocument.execCommand('insertHTML', undefined, values); } }; const query = (command) => { return (0, utils_1.parse)(refs.rootDocument.current.queryCommandValue(command)); }; const textQueryUpdate = () => { const selected = []; const updateOptionValues = [ { name: 'italic', command: 'italic' }, { name: 'underline', command: 'underline' }, { name: 'bold', command: 'bold' }, { name: 'strike-line', command: 'strikeThrough' } ]; updateOptionValues.forEach(item => { if (query(item.command)) selected.push(item.name); }); setTextSelected(selected); }; const onMouseUp = react_1.default.useCallback(() => { if (!refs.edit.current) return; const selection_ = refs.rootWindow.current.getSelection(); if (!selection_) return; if (!selection_.anchorNode || !refs.root.current.contains(selection_.anchorNode)) return setTextSelection(null); setTimeout(() => { var _a; const rect = selection_.getRangeAt(0).getBoundingClientRect(); setTextSelection(Math.round(rect.width) ? { selection: rect, element: (_a = selection_.anchorNode) === null || _a === void 0 ? void 0 : _a.parentElement } : null); textQueryUpdate(); }, 140); }, []); const onMouseDown = react_1.default.useCallback(() => { if (!refs.edit.current) return; textQueryUpdate(); }, []); const onKeyUp = react_1.default.useCallback(() => { if (!refs.edit.current) return; textQueryUpdate(); }, []); const textMethod = react_1.default.useCallback((command) => (argument) => { var _a; switch (command) { // updates case 'italic': refs.rootDocument.current.execCommand('italic'); if (query('italic')) setTextSelected((values) => [...values, 'italic']); else setTextSelected((values) => values.filter((item) => item !== 'italic')); break; case 'underline': refs.rootDocument.current.execCommand('underline'); if (query('underline')) setTextSelected((values) => [...values, 'underline']); else setTextSelected((values) => values.filter((item) => item !== 'underline')); break; case 'bold': refs.rootDocument.current.execCommand('bold'); if (query('bold')) setTextSelected((values) => [...values, 'bold']); else setTextSelected((values) => values.filter((item) => item !== 'bold')); break; case 'strike-line': refs.rootDocument.current.execCommand('strikeThrough'); if (query('strikeThrough')) setTextSelected((values) => [...values, 'strike-line']); else setTextSelected((values) => values.filter((item) => item !== 'strike-line')); break; case 'link-add': refs.rootDocument.current.execCommand('createLink', undefined, argument); break; case 'link-remove': refs.rootDocument.current.execCommand('unlink'); break; // actions case 'copy': refs.rootDocument.current.execCommand('copy'); break; case 'cut': refs.rootDocument.current.execCommand('cut'); break; case 'paste': if (refs.rootDocument.current.queryCommandSupported('paste')) refs.rootDocument.current.execCommand('paste'); else paste(); break; case 'delete': refs.rootDocument.current.execCommand('delete'); break; case 'clear': refs.rootDocument.current.execCommand('removeFormat'); break; case 'select-all': refs.rootDocument.current.execCommand('selectAll'); break; case 'undo': refs.rootDocument.current.execCommand('undo'); break; case 'redo': refs.rootDocument.current.execCommand('redo'); break; default: break; } const element = (_a = refs.textSelection.current) === null || _a === void 0 ? void 0 : _a.element; if (element) { let valueElement = element.innerHTML || ''; if (element.textContent === '') { valueElement = ''; element.innerHTML = ''; } if (valueElement) valueElement = (0, utils_1.innerHTMLToText)(valueElement); } textQueryUpdate(); }, []); const AppendProps = { padding: [14, 14] }; const IconProps = { size: 'small' }; const TooltipProps = { position: 'bottom', interactive: false }; const ToggleButtonsProps = { tonal: false, color: 'default', version: 'text', border: false }; const ToggleButtonProps = { size: 'small' }; const Input = react_1.default.useCallback(react_1.default.forwardRef((propsInput, ref_) => { const { label: labelInput, labelButton, value: value__, onChange: onChange__, onClick, InputComponent = TextField, InputProps } = propsInput, other_ = __rest(propsInput, ["label", "labelButton", "value", "onChange", "onClick", "InputComponent", "InputProps"]); return ((0, jsx_runtime_1.jsx)(Line, Object.assign({ ref: ref_, gap: 1, direction: 'column', color: 'themed', Component: Surface }, other_, { className: (0, style_react_1.classNames)([ other_ === null || other_ === void 0 ? void 0 : other_.className, classes.inputWrapper ]) }, { children: (0, jsx_runtime_1.jsxs)(Line, Object.assign({ gap: 0.5, direction: 'row', align: 'center', style: { width: '100%' } }, { children: [(0, jsx_runtime_1.jsx)(InputComponent, Object.assign({ label: labelInput, version: 'outlined', size: 'small', valueDefault: value__, onChange: onChange__ }, InputProps, { style: { width: 'unset', flex: '1 1 auto' } })), (0, jsx_runtime_1.jsx)(Button, Object.assign({ color: 'inherit', version: 'text', size: 'small', onClick: onClick }, { children: labelButton }))] })) }))); }), []); const WrapperAppend = react_1.default.useCallback((propsWrapper) => { const { open: open_, element, anchorElement, onClose, children: childrenWrapperAppend } = propsWrapper, other_ = __rest(propsWrapper, ["open", "element", "anchorElement", "onClose", "children"]); return ((0, jsx_runtime_1.jsx)(Append, Object.assign({ open: open_, element: ((0, jsx_runtime_1.jsx)("div", Object.assign({ className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('SmartTextField', theme) && [ 'amaui-SmartTextField-mini-menu-additional' ], classes.textMiniMenuAdditionalMenu ]) }, { children: (0, jsx_runtime_1.jsx)(Fade, Object.assign({ in: open_, add: true }, { children: react_1.default.cloneElement(element) })) }))), anchorElement: anchorElement, portal: true, alignment: 'center', position: 'bottom' }, AppendProps, { children: react_1.default.cloneElement(childrenWrapperAppend, Object.assign(Object.assign({}, other_), childrenWrapperAppend.props)) }))); }, []); const WrapperToggleButton = react_1.default.useCallback(react_1.default.forwardRef((propsWrapperToggleButton, ref_) => { const { open: open_, label: labelWrapperToogleButton, children: childrenWrapperToggleButton } = propsWrapperToggleButton, other_ = __rest(propsWrapperToggleButton, ["open", "label", "children"]); return ((0, jsx_runtime_1.jsx)(Tooltip, Object.assign({ open: open_ !== undefined ? open_ : undefined, label: labelWrapperToogleButton }, TooltipProps, { children: react_1.default.cloneElement(childrenWrapperToggleButton, Object.assign(Object.assign({}, other_), childrenWrapperToggleButton.props)) }))); }), []); const updateElements = { 'italic': ((0, jsx_runtime_1.jsx)(WrapperToggleButton, Object.assign({ label: 'Italic' }, { children: (0, jsx_runtime_1.jsx)(ToggleButton, Object.assign({}, ToggleButtonProps, { selected: refs.textSelected.current.includes('italic'), onClick: textMethod('italic') }, { children: (0, jsx_runtime_1.jsx)(IconMaterialFormatItalicW100_1.default, Object.assign({}, IconProps)) })) }))), 'underline': ((0, jsx_runtime_1.jsx)(WrapperToggleButton, Object.assign({ label: 'Underline' }, { children: (0, jsx_runtime_1.jsx)(ToggleButton, Object.assign({}, ToggleButtonProps, { selected: refs.textSelected.current.includes('underline'), onClick: textMethod('underline') }, { children: (0, jsx_runtime_1.jsx)(IconMaterialFormatUnderlinedW100_1.default, Object.assign({}, IconProps)) })) }))), 'bold': ((0, jsx_runtime_1.jsx)(WrapperToggleButton, Object.assign({ label: 'Bold', onClick: textMethod('bold') }, { children: (0, jsx_runtime_1.jsx)(ToggleButton, Object.assign({}, ToggleButtonProps, { selected: refs.textSelected.current.includes('bold') }, { children: (0, jsx_runtime_1.jsx)(IconMaterialFormatBoldW100_1.default, Object.assign({}, IconProps)) })) }))), 'strike-line': ((0, jsx_runtime_1.jsx)(WrapperToggleButton, Object.assign({ label: 'Strike Line', onClick: textMethod('strike-line') }, { children: (0, jsx_runtime_1.jsx)(ToggleButton, Object.assign({}, ToggleButtonProps, { selected: refs.textSelected.current.includes('strike-line') }, { children: (0, jsx_runtime_1.jsx)(IconMaterialStrikethroughSW100_1.default, Object.assign({}, IconProps)) })) }))), 'link-add': ((0, jsx_runtime_1.jsx)(WrapperAppend, Object.assign({ open: refs.open.current.linkMiniMenu, anchorElement: refs.miniMenuElements.linkAdd, element: ((0, jsx_runtime_1.jsx)(ClickListener, Object.assign({ onClickOutside: () => updateOpen('linkMiniMenu', false), include: [refs.miniMenuElements.linkAdd] }, { children: (0, jsx_runtime_1.jsx)(Input, { ref: refs.miniMenuElements.linkAddInput, label: 'Link', labelButton: 'Add', value: refs.inputValues.current.link, onChange: (valueNew) => updateInputValues('link', valueNew), onClick: () => { if (refs.range.current) { const selection_ = refs.rootWindow.current.getSelection(); if (!selection_) return; selection_.removeAllRanges(); selection_.addRange(refs.range.current); } textMethod('link-add')(refs.inputValues.current.link); updateOpen('linkMiniMenu', false); updateInputValues('link', ''); }, className: classes.inputLink }) }))) }, { children: (0, jsx_runtime_1.jsx)(WrapperToggleButton, Object.assign({ label: 'Insert Link', open: refs.open.current.linkMiniMenu ? false : undefined }, { children: (0, jsx_runtime_1.jsx)(ToggleButton, Object.assign({ ref: refs.miniMenuElements.linkAdd }, ToggleButtonProps, { selected: refs.open.current.linkMiniMenu, onClick: () => updateOpen('linkMiniMenu', !refs.open.current.linkMiniMenu) }, { children: (0, jsx_runtime_1.jsx)(IconMaterialAddLinkW100_1.default, Object.assign({}, IconProps)) })) })) }))), 'link-remove': ((0, jsx_runtime_1.jsx)(WrapperToggleButton, Object.assign({ label: 'Remove Link' }, { children: (0, jsx_runtime_1.jsx)(ToggleButton, Object.assign({}, ToggleButtonProps, { onClick: textMethod('link-remove') }, { children: (0, jsx_runtime_1.jsx)(IconMaterialLinkOffW100_1.default, Object.assign({}, IconProps)) })) }))) }; const actionElements = { 'clear': ((0, jsx_runtime_1.jsx)(WrapperToggleButton, Object.assign({ label: 'Clear' }, { children: (0, jsx_runtime_1.jsx)(ToggleButton, Object.assign({}, ToggleButtonProps, { onClick: textMethod('clear') }, { children: (0, jsx_runtime_1.jsx)(IconMaterialFormatClearW100_1.default, Object.assign({}, IconProps)) })) }))) }; const miniMenu = react_1.default.useMemo(() => { return ((0, jsx_runtime_1.jsx)(Append, Object.assign({ open: !!textSelection, element: (body) => { const { ref: ref_, values, style: styleAppend } = body; return ((0, jsx_runtime_1.jsx)("div", Object.assign({ ref: item => { if (ref_) { if ((0, utils_1.is)('function', ref_)) ref_(item); else ref_.current = item; refs.miniMenu.current = item; } }, style: Object.assign(Object.assign(Object.assign({}, (((values === null || values === void 0 ? void 0 : values.x) === 0 && (values === null || values === void 0 ? void 0 : values.y) === 0) ? { visibility: 'hidden' } : undefined)), styleAppend), { zIndex: 1500 }), className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('SmartTextField', theme) && [ 'amaui-SmartTextField-mini-menu-wrapper' ], classes.textMiniMenuWrapper ]) }, { children: (0, jsx_runtime_1.jsx)(Fade, Object.assign({ in: !!textSelection, add: true }, { children: (0, jsx_runtime_1.jsx)(ClickListener, Object.assign({ onClickOutside: () => { setTextSelection(null); }, include: [refs.miniMenu, refs.miniMenu.current, refs.miniMenuElements.linkAddInput, refs.miniMenuElements.linkAddInput.current] }, { children: (0, jsx_runtime_1.jsxs)(Line, Object.assign({ gap: 2, direction: 'row', align: 'center', justify: 'flex-start', role: 'toolbar', color: 'themed', "aria-label": 'Mini menu', Component: Surface, className: (0, style_react_1.classNames)([ classes.textMiniMenu ]) }, { children: [(0, jsx_runtime_1.jsxs)(ToggleButtons, Object.assign({}, ToggleButtonsProps, { children: [updateElements['italic'], updateElements['underline'], updateElements['bold'], updateElements['strike-line']] })), (0, jsx_runtime_1.jsxs)(ToggleButtons, Object.assign({}, ToggleButtonsProps, { children: [updateElements['link-add'], updateElements['link-remove']] })), (0, jsx_runtime_1.jsx)(ToggleButtons, Object.assign({}, ToggleButtonsProps, { children: actionElements['clear'] }))] })) })) })) }))); }, parent: refs.root.current, anchor: textSelection === null || textSelection === void 0 ? void 0 : textSelection.selection, portal: true, alignment: 'center', position: 'bottom', clearOnClose: true }, AppendProps))); }, [open, textSelection, textSelected]); const menu = react_1.default.useMemo(() => { return ((0, jsx_runtime_1.jsx)(Menu, { open: openMenu, position: 'top', alignment: 'start', offset: offset, anchor: selectionMenu, onClose: onCloseSearch, label: label(), interactive: true })); }, [openMenu, selectionMenu, label]); let main = ((0, jsx_runtime_1.jsx)(TextField, Object.assign({ ref: (item) => { if (ref) { if ((0, utils_1.is)('function', ref)) ref(item); else ref.current = item; } refs.root.current = item; }, version: 'text', size: size, minRows: 12, multiline: true, enabled: true, fullWidth: true, onInput: onInput, onKeyDown: onKeyDown, onKeyUp: onKeyUp, onFocus: onFocus, onBlur: onBlur, readOnly: readOnly, inputProps: Object.assign(Object.assign({ version: 'b2', contentEditable: true, spellCheck: false, onPaste: pasteText ? onPasteText : onPaste, onDrop, onMouseUp, onMouseDown, 'data-placeholder': placeholder !== undefined ? placeholder : name }, inputProps), { ref: (item) => { refs.input.current = item; if (inputProps === null || inputProps === void 0 ? void 0 : inputProps.ref) { if ((0, utils_1.is)('function', inputProps.ref)) inputProps.ref(item); else inputProps.ref.current = item; } } }), InputComponent: Type, className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('SmartTextField', theme) && [ 'amaui-SmartTextField-root', `amaui-SmartTextField-version-${version}`, `amaui-SmartTextField-size-${size}` ], className, classes.root ]) }, additional, other))); if (version === 'text') main = ((0, jsx_runtime_1.jsx)(Type, Object.assign({ ref: (item) => { if (ref) { if ((0, utils_1.is)('function', ref)) ref(item); else ref.current = item; } if (inputProps === null || inputProps === void 0 ? void 0 : inputProps.ref) { if ((0, utils_1.is)('function', inputProps.ref)) inputProps.ref(item); else inputProps.ref.current = item; } refs.root.current = item; refs.input.current = item; }, onInput: onInput, onKeyDown: onKeyDown, onKeyUp: onKeyUp, onFocus: onFocus, onBlur: onBlur, onPaste: pasteText ? onPasteText : onPaste, onDrop: onDrop, onMouseUp: onMouseUp, onMouseDown: onMouseDown, spellCheck: false, "data-placeholder": placeholder !== undefined ? placeholder : name }, inputProps, { contentEditable: !readOnly, className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('SmartTextField', theme) && [ 'amaui-SmartTextField-root', `amaui-SmartTextField-version-${version}`, `amaui-SmartTextField-size-${size}` ], className, classes.root_type, !multiline && classes.singleLine ]) }, additional, other))); return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [main, version === 'text' && (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: helperText && (0, utils_1.is)('string', helperText) ? ((0, jsx_runtime_1.jsx)(Type, Object.assign({ version: size === 'large' ? 'b1' : size === 'regular' ? 'b2' : 'b3' }, HelperTextProps, { className: (0, style_react_1.classNames)([ (0, utils_2.staticClassName)('SmartTextField', theme) && [ 'amaui-SmartTextField-helper-text', error && 'amaui-SmartTextField-error' ], HelperTextProps === null || HelperTextProps === void 0 ? void 0 : HelperTextProps.className, classes.helperText, error && classes.error_color ]) }, { children: (0, utils_1.textToInnerHTML)(helperText) }))) : helperText }), miniMenu, menu] }); }); SmartTextField.displayName = 'amaui-SmartTextField'; exports.default = SmartTextField;