@amaui/ui-react
Version:
UI for React
797 lines (796 loc) • 49.7 kB
JavaScript
"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(' ', ' ');
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;