@onesy/ui-react
Version:
UI for React
878 lines • 72.2 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("@onesy/utils");
const style_react_1 = require("@onesy/style-react");
const IconMaterialFormatAlignLeftW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialFormatAlignLeftW100"));
const IconMaterialFormatAlignCenterW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialFormatAlignCenterW100"));
const IconMaterialFormatAlignRightW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialFormatAlignRightW100"));
const IconMaterialFormatAlignJustifyW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialFormatAlignJustifyW100"));
const IconMaterialFormatColorTextW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialFormatColorTextW100"));
const IconMaterialFormatColorFillW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialFormatColorFillW100"));
const IconMaterialFormatListNumberedW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialFormatListNumberedW100"));
const IconMaterialFormatListBulletedW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialFormatListBulletedW100"));
const IconMaterialSuperscriptW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialSuperscriptW100"));
const IconMaterialSubscriptW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialSubscriptW100"));
const IconMaterialFormatItalicW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialFormatItalicW100"));
const IconMaterialFormatUnderlinedW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialFormatUnderlinedW100"));
const IconMaterialStrikethroughSW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialStrikethroughSW100"));
const IconMaterialFormatBoldW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialFormatBoldW100"));
const IconMaterialAddLinkW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialAddLinkW100"));
const IconMaterialLinkOffW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialLinkOffW100"));
const IconMaterialFormatClearW100_1 = __importDefault(require("@onesy/icons-material-rounded-react/IconMaterialFormatClearW100"));
const ColorTextField_1 = __importDefault(require("../ColorTextField"));
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 Divider_1 = __importDefault(require("../Divider"));
const utils_2 = require("../utils");
const useStyle = (0, style_react_1.style)(theme => ({
root: {
minHeight: '20px',
width: '100%',
'& .onesy-TextField-input': Object.assign({ wordBreak: 'break-word', color: theme.methods.palette.color.value('primary', 10) }, theme.typography.values.b2),
'& ul, & ol': {
listStylePosition: 'inside'
}
},
root_type: {
minHeight: '20px',
width: '100%',
cursor: 'text',
'& .onesy-TextField-input': Object.assign({ wordBreak: 'break-word', color: theme.methods.palette.color.value('primary', 10) }, theme.typography.values.b2),
'& ul, & ol': {
listStylePosition: 'inside'
}
},
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],
maxWidth: 'calc(100vw - 24px)',
rowGap: '4px'
},
textMiniMenuAdditionalMenu: {
minWidth: 'clamp(140px, 90%, 250px)',
zIndex: '1514',
boxShadow: theme.shadows.values.default[2]
},
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'
},
input: {
'&.onesy-TextField-root': {
flex: '1 1 auto !important'
}
},
palette: {
padding: theme.methods.space.value(1.5, 'px'),
borderRadius: theme.methods.shape.radius.value(1, 'px'),
boxShadow: theme.shadows.values.default[2]
},
paletteItem: {
position: 'relative',
width: '17px',
height: '17px',
cursor: 'pointer',
borderRadius: theme.methods.shape.radius.value(40, 'px'),
boxShadow: theme.shadows.values.default[1],
transition: theme.methods.transitions.make('box-shadow'),
'&:hover': {
boxShadow: theme.shadows.values.default[2],
}
},
textFieldColor: {
'&.onesy-ColorTextField-root': {
flex: '1 1 auto'
}
},
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: 'onesy-SmartTextField' });
const SmartTextField = react_1.default.forwardRef((props_, ref) => {
var _a;
const theme = (0, style_react_1.useOnesyTheme)();
const l = theme.l;
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.onesySmartTextField) === 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 Divider = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.Divider) || Divider_1.default; }, [theme]);
const ColorTextField = react_1.default.useMemo(() => { var _a; return ((_a = theme === null || theme === void 0 ? void 0 : theme.elements) === null || _a === void 0 ? void 0 : _a.ColorTextField) || ColorTextField_1.default; }, [theme]);
const { tonal = true, color = 'default', 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, miniMenuExtended, HelperTextProps, ColorTextFieldProps, className } = props, other = __rest(props, ["tonal", "color", "version", "size", "valueDefault", "value", "inputProps", "onChange", "error", "name", "placeholder", "edit", "mention", "multiline", "onChangeMention", "optionsMention", "error", "helperText", "mentionLabel", "onKeyDown", "pasteText", "readOnly", "additional", "miniMenuExtended", "HelperTextProps", "ColorTextFieldProps", "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(null),
input: react_1.default.useRef(null),
searchElement: react_1.default.useRef(null),
error: react_1.default.useRef(null),
search: react_1.default.useRef(null),
open: react_1.default.useRef(null),
openMenu: react_1.default.useRef(null),
selectionMenu: react_1.default.useRef(null),
caret: react_1.default.useRef(null),
value: react_1.default.useRef(null),
version: react_1.default.useRef(version),
optionsMention: react_1.default.useRef(optionsMention),
validateMeta: react_1.default.useRef({}),
range: react_1.default.useRef(null),
inputValues: react_1.default.useRef(null),
miniMenu: react_1.default.useRef(null),
miniMenuElements: {
color: react_1.default.useRef(null),
colorPalette: react_1.default.useRef(null),
background: react_1.default.useRef(null),
backgroundPalette: react_1.default.useRef(null),
linkAdd: react_1.default.useRef(null),
linkAddInput: react_1.default.useRef(null),
linkRemove: react_1.default.useRef(null)
},
elements: {
color: react_1.default.useRef(null),
background: react_1.default.useRef(null),
linkAdd: react_1.default.useRef(null),
linkRemove: react_1.default.useRef(null),
quote: react_1.default.useRef(null),
image: react_1.default.useRef(null),
video: react_1.default.useRef(null),
videoYoutube: react_1.default.useRef(null),
table: react_1.default.useRef(null),
drawing: react_1.default.useRef(null),
drawingSvg: react_1.default.useRef(null),
drawingSize: react_1.default.useRef(null),
drawingSelect: react_1.default.useRef(null),
drawingPalette: react_1.default.useRef(null),
code: react_1.default.useRef(null)
},
textSelected: react_1.default.useRef(null),
textSelection: react_1.default.useRef(null),
rootDocument: react_1.default.useRef(null),
rootWindow: react_1.default.useRef(null),
edit: react_1.default.useRef(edit),
multiline: react_1.default.useRef(multiline),
readOnly: react_1.default.useRef(readOnly),
onChange: react_1.default.useRef(onChange),
props: react_1.default.useRef(props)
};
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;
refs.props.current = props;
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(() => {
setOptionsMention([...(optionsMention_ || [])]);
}, [(0, utils_1.hash)(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.onesyVersion === 'mention-user') {
// Replace the child if label & textContent don't match
if (child.dataset.onesyLabel !== 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 onesy-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.onesyVersion) === '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.onesyVersion) === '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 = ['onesy-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.onesyLabel = `@${itemName}`;
span.dataset.onesyVersion = `mention-${versionMention}`;
span.dataset.onesyObject = `${versionMention}`;
span.dataset.onesyId = 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(() => ((0, utils_1.is)('function', mentionLabel) ? mentionLabel(optionsMention, { 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: optionsMention === null || optionsMention === void 0 ? void 0 : optionsMention.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));
}) })) }))), [optionsMention]);
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 'align-left':
refs.rootDocument.current.execCommand('justifyLeft');
if (query('justifyLeft'))
setTextSelected(values => [...values.filter(item => !item.includes('align')), 'align-left']);
else
setTextSelected(values => values.filter(item => item !== 'align-left'));
break;
case 'align-center':
refs.rootDocument.current.execCommand('justifyCenter');
if (query('justifyCenter'))
setTextSelected(values => [...values.filter(item => !item.includes('align')), 'align-center']);
else
setTextSelected(values => values.filter(item => item !== 'align-center'));
break;
case 'align-right':
refs.rootDocument.current.execCommand('justifyRight');
if (query('justifyRight'))
setTextSelected(values => [...values.filter(item => !item.includes('align')), 'align-right']);
else
setTextSelected(values => values.filter(item => item !== 'align-right'));
break;
case 'align-justify':
refs.rootDocument.current.execCommand('justifyFull');
if (query('justifyFull'))
setTextSelected(values => [...values.filter(item => !item.includes('align')), 'align-justify']);
else
setTextSelected(values => values.filter(item => item !== 'align-justify'));
break;
case 'superscript':
refs.rootDocument.current.execCommand('superscript');
if (query('superscript'))
setTextSelected(values => [...values, 'superscript']);
else
setTextSelected(values => values.filter(item => item !== 'superscript'));
break;
case 'subscript':
refs.rootDocument.current.execCommand('subscript');
if (query('subscript'))
setTextSelected(values => [...values, 'subscript']);
else
setTextSelected(values => values.filter(item => item !== 'subscript'));
break;
case 'indent':
refs.rootDocument.current.execCommand('indent');
break;
case 'outdent':
refs.rootDocument.current.execCommand('outdent');
break;
case 'font-version':
refs.rootDocument.current.execCommand('formatBlock', undefined, argument);
break;
case 'font-family':
refs.rootDocument.current.execCommand('styleWithCSS', true);
refs.rootDocument.current.execCommand('fontName', undefined, argument);
refs.rootDocument.current.execCommand('styleWithCSS', false);
break;
case 'font-size':
refs.rootDocument.current.execCommand('styleWithCSS', true);
refs.rootDocument.current.execCommand('fontSize', undefined, argument);
refs.rootDocument.current.execCommand('styleWithCSS', false);
break;
case 'font-color':
refs.rootDocument.current.execCommand('styleWithCSS', true);
refs.rootDocument.current.execCommand('foreColor', undefined, argument);
refs.rootDocument.current.execCommand('styleWithCSS', false);
break;
case 'font-background':
refs.rootDocument.current.execCommand('styleWithCSS', true);
refs.rootDocument.current.execCommand('backColor', undefined, argument);
refs.rootDocument.current.execCommand('styleWithCSS', false);
break;
case 'list-ordered':
refs.rootDocument.current.execCommand('insertOrderedList');
if (query('insertOrderedList'))
setTextSelected(values => [...values.filter(item => !item.includes('list')), 'list-ordered']);
else
setTextSelected(values => values.filter(item => item !== 'list-ordered'));
break;
case 'list-unordered':
refs.rootDocument.current.execCommand('insertUnorderedList');
if (query('insertUnorderedList'))
setTextSelected(values => [...values.filter(item => !item.includes('list')), 'list-unordered']);
else
setTextSelected(values => values.filter(item => item !== 'list-unordered'));
break;
case 'horizontal-rule':
refs.rootDocument.current.execCommand('insertHorizontalRule');
break;
case 'link-add':
refs.rootDocument.current.execCommand('createLink', undefined, argument);
break;
case 'link-remove':
refs.rootDocument.current.execCommand('unlink');
break;
case 'image':
refs.rootDocument.current.execCommand('insertImage', undefined, argument);
break;
case 'html':
refs.rootDocument.current.execCommand('insertHTML', undefined, argument);
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 PaletteItem = react_1.default.useCallback((propsItem) => {
const { color: color_ } = propsItem, other_ = __rest(propsItem, ["color"]);
return ((0, jsx_runtime_1.jsx)("span", Object.assign({ className: (0, style_react_1.classNames)([
(0, utils_2.staticClassName)('RichTextEditor', theme) && [
'onesy-RichTextEditor-palette-item'
],
classes.paletteItem
]), style: {
background: color_
} }, other_)));
}, []);
const Palette = react_1.default.useCallback(react_1.default.forwardRef((propsPalette, ref_) => {
const { version: version_, onUpdate: onUpdate_, onClose } = propsPalette, other_ = __rest(propsPalette, ["version", "onUpdate", "onClose"]);
const onUpdateColor = itemColor => {
if (refs.range.current) {
const selection_ = refs.rootWindow.current.getSelection();
selection_.removeAllRanges();
selection_.addRange(refs.range.current);
}
onUpdate_(itemColor);
onClose();
};
return ((0, jsx_runtime_1.jsxs)(Line, Object.assign({ ref: ref_, gap: 1, direction: 'column', tonal: tonal, color: refs.props.current.color !== undefined ? refs.props.current.color : 'themed', Component: Surface, className: (0, style_react_1.classNames)([
(0, utils_2.staticClassName)('RichTextEditor', theme) && [
'onesy-RichTextEditor-palette'
],
classes.palette
]) }, other_, { children: [(0, jsx_runtime_1.jsxs)(Line, Object.assign({ gap: 0.5, style: {
maxHeight: 136,
padding: '10px 10px 0',
overflow: 'hidden auto'
} }, { children: [(0, jsx_runtime_1.jsxs)(Line, Object.assign({ gap: 0.5, direction: 'row', style: {
width: '100%'
} }, { children: [(0, jsx_runtime_1.jsx)(PaletteItem, { color: '#000000', onClick: () => {
onUpdateColor('#000000');
} }), (0, jsx_runtime_1.jsx)(PaletteItem, { color: '#ffffff', onClick: () => {
onUpdateColor('#ffffff');
} })] })), Object.keys(style_react_1.colors).filter(item => !['black', 'white'].includes(item)).map((item, index) => ((0, jsx_runtime_1.jsx)(Line, Object.assign({ gap: 0.5, direction: 'row', style: {
width: '100%'
} }, { children: Object.keys(style_react_1.colors[item]).map((item_, index_) => ((0, jsx_runtime_1.jsx)(PaletteItem, { color: style_react_1.colors[item][item_], onClick: () => {
onUpdateColor(style_react_1.colors[item][item_]);
} }, index_))) }), index)))] })), (0, jsx_runtime_1.jsx)(Divider, {}), (0, jsx_runtime_1.jsxs)(Line, Object.assign({ gap: 0.5, direction: 'row', align: 'center', fullWidth: true, style: {
padding: '0px 10px 10px'
} }, { children: [(0, jsx_runtime_1.jsx)(ColorTextField, Object.assign({ tonal: tonal, color: color, name: l('Custom color'), version: 'outlined', size: 'small', value: refs.inputValues.current[version_], onChange: valueNew => updateInputValues(version_, valueNew) }, ColorTextFieldProps, { className: (0, style_react_1.classNames)([
(0, utils_2.staticClassName)('RichTextEditor', theme) && [
'onesy-RichTextEditor-text-field-color'
],
ColorTextFieldProps === null || ColorTextFieldProps === void 0 ? void 0 : ColorTextFieldProps.className,
classes.textFieldColor
]) })), (0, jsx_runtime_1.jsx)(Button, Object.assign({ tonal: tonal, color: 'inherit', version: 'text', size: 'small', onClick: () => {
if (refs.range.current) {
const selection_ = refs.rootWindow.current.getSelection();
selection_.removeAllRanges();
selection_.addRange(refs.range.current);
}
onUpdate_(refs.inputValues.current[version_]);
onClose();
} }, { children: "Apply" }))] }))] })));
}), []);
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, placeholder: placeholderInputProps, InputComponent = TextField, InputProps } = propsInput, other_ = __rest(propsInput, ["label", "labelButton", "value", "onChange", "onClick", "placeholder", "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({ name: labelInput, version: 'outlined', size: 'small', valueDefault: value__, onChange: onChange__, placeholder: placeholderInputProps, flex: true }, In