@onesy/ui-react
Version:
UI for React
1,214 lines (1,186 loc) • 60.6 kB
JavaScript
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
const _excluded = ["ref", "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"],
_excluded2 = ["color"],
_excluded3 = ["ref", "version", "onUpdate", "onClose"],
_excluded4 = ["ref", "label", "labelButton", "value", "onChange", "onClick", "placeholder", "InputComponent", "InputProps"],
_excluded5 = ["open", "element", "anchorElement", "onClose", "children"],
_excluded6 = ["ref", "open", "name", "children"];
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import React from 'react';
import { hash, innerHTMLToText, is, isEnvironment, parse, stringToColor, textToInnerHTML } from '@onesy/utils';
import { classNames, colors, style as styleMethod, useOnesyTheme } from '@onesy/style-react';
import IconMaterialFormatAlignLeft from '@onesy/icons-material-rounded-react/IconMaterialFormatAlignLeftW100';
import IconMaterialFormatAlignCenter from '@onesy/icons-material-rounded-react/IconMaterialFormatAlignCenterW100';
import IconMaterialFormatAlignRight from '@onesy/icons-material-rounded-react/IconMaterialFormatAlignRightW100';
import IconMaterialFormatAlignJustify from '@onesy/icons-material-rounded-react/IconMaterialFormatAlignJustifyW100';
import IconMaterialFormatColorText from '@onesy/icons-material-rounded-react/IconMaterialFormatColorTextW100';
import IconMaterialFormatColorFill from '@onesy/icons-material-rounded-react/IconMaterialFormatColorFillW100';
import IconMaterialFormatListNumbered from '@onesy/icons-material-rounded-react/IconMaterialFormatListNumberedW100';
import IconMaterialFormatListBulleted from '@onesy/icons-material-rounded-react/IconMaterialFormatListBulletedW100';
import IconMaterialSuperscript from '@onesy/icons-material-rounded-react/IconMaterialSuperscriptW100';
import IconMaterialSubscript from '@onesy/icons-material-rounded-react/IconMaterialSubscriptW100';
import IconMaterialFormatItalic from '@onesy/icons-material-rounded-react/IconMaterialFormatItalicW100';
import IconMaterialFormatUnderlined from '@onesy/icons-material-rounded-react/IconMaterialFormatUnderlinedW100';
import IconMaterialStrikethroughS from '@onesy/icons-material-rounded-react/IconMaterialStrikethroughSW100';
import IconMaterialFormatBold from '@onesy/icons-material-rounded-react/IconMaterialFormatBoldW100';
import IconMaterialAddLink from '@onesy/icons-material-rounded-react/IconMaterialAddLinkW100';
import IconMaterialLinkOff from '@onesy/icons-material-rounded-react/IconMaterialLinkOffW100';
import IconMaterialFormatClear from '@onesy/icons-material-rounded-react/IconMaterialFormatClearW100';
import ColorTextFieldElement from '../ColorTextField';
import TextFieldElement from '../TextField/TextField';
import LineElement from '../Line';
import ListElement from '../List';
import ListItemElement from '../ListItem';
import AvatarElement from '../Avatar';
import TypeElement from '../Type';
import SurfaceElement from '../Surface';
import ButtonElement from '../Button';
import AppendElement from '../Append';
import FadeElement from '../Fade';
import TooltipElement from '../Tooltip';
import ToggleButtonElement from '../ToggleButton';
import ClickListenerElement from '../ClickListener';
import ToggleButtonsElement from '../ToggleButtons';
import MenuElement from '../Menu';
import DividerElement from '../Divider';
import { sanitize, caret, keyboardStyleCommands, staticClassName } from '../utils';
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
const useStyle = styleMethod(theme => ({
root: {
minHeight: '20px',
width: '100%',
'& .onesy-TextField-input': _objectSpread({
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': _objectSpread({
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 = props_ => {
const theme = useOnesyTheme();
const l = theme.l;
const props = _objectSpread(_objectSpread(_objectSpread({}, theme?.ui?.elements?.all?.props?.default), theme?.ui?.elements?.onesySmartTextField?.props?.default), props_);
const Line = theme?.elements?.Line || LineElement;
const TextField = theme?.elements?.TextField || TextFieldElement;
const List = theme?.elements?.List || ListElement;
const ListItem = theme?.elements?.ListItem || ListItemElement;
const Avatar = theme?.elements?.Avatar || AvatarElement;
const Type = theme?.elements?.Type || TypeElement;
const Surface = theme?.elements?.Surface || SurfaceElement;
const Button = theme?.elements?.Button || ButtonElement;
const Append = theme?.elements?.Append || AppendElement;
const Fade = theme?.elements?.Fade || FadeElement;
const Tooltip = theme?.elements?.Tooltip || TooltipElement;
const ToggleButton = theme?.elements?.ToggleButton || ToggleButtonElement;
const ClickListener = theme?.elements?.ClickListener || ClickListenerElement;
const ToggleButtons = theme?.elements?.ToggleButtons || ToggleButtonsElement;
const Menu = theme?.elements?.Menu || MenuElement;
const Divider = theme?.elements?.Divider || DividerElement;
const ColorTextField = theme?.elements?.ColorTextField || ColorTextFieldElement;
const {
ref,
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 = _objectWithoutProperties(props, _excluded);
const {
classes
} = useStyle();
const [value, setValue] = React.useState(valueDefault || '');
const [openMenu, setOpenMenu] = React.useState(false);
const [offset, setOffset] = React.useState([0, 8]);
const [selectionMenu, setSelectionMenu] = React.useState();
const [optionsMention, setOptionsMention] = React.useState([]);
const [textSelection, setTextSelection] = React.useState();
const [textSelected, setTextSelected] = React.useState([]);
const [open, setOpen] = React.useState({});
const [inputValues, setInputValues] = React.useState({});
const refs = {
root: React.useRef(null),
input: React.useRef(null),
searchElement: React.useRef(null),
error: React.useRef(null),
search: React.useRef(null),
open: React.useRef(null),
openMenu: React.useRef(null),
selectionMenu: React.useRef(null),
caret: React.useRef(null),
value: React.useRef(null),
version: React.useRef(version),
optionsMention: React.useRef(optionsMention),
validateMeta: React.useRef({}),
range: React.useRef(null),
inputValues: React.useRef(null),
miniMenu: React.useRef(null),
miniMenuElements: {
color: React.useRef(null),
colorPalette: React.useRef(null),
background: React.useRef(null),
backgroundPalette: React.useRef(null),
linkAdd: React.useRef(null),
linkAddInput: React.useRef(null),
linkRemove: React.useRef(null)
},
elements: {
color: React.useRef(null),
background: React.useRef(null),
linkAdd: React.useRef(null),
linkRemove: React.useRef(null),
quote: React.useRef(null),
image: React.useRef(null),
video: React.useRef(null),
videoYoutube: React.useRef(null),
table: React.useRef(null),
drawing: React.useRef(null),
drawingSvg: React.useRef(null),
drawingSize: React.useRef(null),
drawingSelect: React.useRef(null),
drawingPalette: React.useRef(null),
code: React.useRef(null)
},
textSelected: React.useRef(null),
textSelection: React.useRef(null),
rootDocument: React.useRef(null),
rootWindow: React.useRef(null),
edit: React.useRef(edit),
multiline: React.useRef(multiline),
readOnly: React.useRef(readOnly),
onChange: React.useRef(onChange),
props: React.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 = isEnvironment('browser') && (refs.root.current?.ownerDocument || window.document);
const rootWindow = rootDocument && (rootDocument.defaultView || window);
refs.rootDocument.current = rootDocument;
refs.rootWindow.current = rootWindow;
const init = () => {
if (version === 'text') {
const valueNew = valueDefault || value || '';
if (refs.root.current) {
refs.root.current.innerHTML = valueNew;
}
}
};
React.useEffect(() => {
// init
init();
}, []);
React.useEffect(() => {
setOptionsMention([...(optionsMention_ || [])]);
}, [hash(optionsMention_)]);
React.useEffect(() => {
const valuePrevious = (refs.root.current.innerHTML || '').replaceAll(' ', ' ');
const valueNew_0 = textToInnerHTML(value_);
if (value_ !== undefined && valuePrevious !== valueNew_0) validate(!value_ ? '' : sanitize(value_));
}, [value_]);
// Save selection to revive, use it on execComand
React.useEffect(() => {
const selection_ = refs.rootWindow.current.getSelection();
if (selection_ && selection_.anchorNode && !selection_.anchorNode?.className?.includes('TextField')) refs.range.current = selection_.getRangeAt(0);
}, [open]);
const updateInputValues = (property, itemValue) => {
setInputValues(values => _objectSpread(_objectSpread({}, values), {}, {
[property]: itemValue
}));
};
const updateOpen = (property_0, itemValue_0) => {
setOpen(values_0 => _objectSpread(_objectSpread({}, values_0), {}, {
[property_0]: itemValue_0
}));
};
const validateElement = element => {
if (element?.children) {
Array.from(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?.children) validateElement(child);
});
}
};
const validate = (valueNew_1 = undefined) => {
// Save caret position
refs.caret.current = caret.save(refs.root.current);
// Update the root innerHTML
if (valueNew_1 !== undefined) refs.root.current.innerHTML = valueNew_1;
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) caret.restore(refs.root.current, refs.caret.current);
return refs.root.current.innerHTML;
};
const getAtSearchData = () => {
if (refs.root.current) {
const windowSelection = window.document.getSelection();
if (!(windowSelection && windowSelection.rangeCount > 0)) return;
const range = windowSelection?.getRangeAt(0);
if (!range) return;
// Only allow for text not in span user mention element
if (windowSelection.anchorNode?.parentElement?.dataset?.onesyVersion === 'mention-user') return '';
// Text
if (!['text', '#text'].includes(range?.commonAncestorContainer?.nodeName)) return '';
const text = windowSelection.anchorNode?.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 = 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 = innerHTMLToText(valueInput);
setValue(valueInput);
if (is('function', refs.onChange.current)) refs.onChange.current(valueInput, event);
}
};
React.useEffect(() => {
if (mention && refs.search.current !== undefined) onChangeMention?.(refs.search.current, '@');
}, [mention, refs.search.current]);
React.useEffect(() => {
const method = () => {
if (window.document.activeElement === refs.input.current) onInput();
};
window.document.addEventListener('selectionchange', method);
onChangeMention?.(refs.search.current, '@');
return () => {
window.document.removeEventListener('selectionchange', method);
};
}, []);
const updateSelection = () => {
const windowSelection_0 = window.document.getSelection();
if (!(windowSelection_0 && windowSelection_0.rangeCount > 0)) return;
const range_0 = windowSelection_0?.getRangeAt(0);
if (range_0) setSelectionMenu(range_0?.getBoundingClientRect());
};
const onOpenSearch = async () => {
if (!refs.openMenu.current && !!refs.optionsMention.current.length) {
// open
setOpenMenu(true);
// selection
updateSelection();
}
};
const onCloseSearch = () => {
if (refs.openMenu.current) {
setOpenMenu(false);
setSelectionMenu(null);
}
};
const updateOffset = () => {
setOffset([-(refs.searchElement.current?.clientWidth || 0), 8]);
};
const onFocus = () => {
// setFocus(true);
};
const onBlur = () => {
// setFocus(false);
};
const onKeyDown = event_0 => {
const windowSelection_1 = window.getSelection();
if (windowSelection_1) {
// 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_0.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_1 && windowSelection_1.rangeCount > 0)) return;
const range_1 = windowSelection_1.getRangeAt(0);
const caretElement = windowSelection_1.anchorNode.parentElement;
const caretElementParent = caretElement.parentElement;
const caretElementParentChildNodes = Array.from(caretElementParent.childNodes);
if (caretElement?.dataset?.onesyVersion === 'mention-user' && range_1.endOffset === windowSelection_1.anchorNode.textContent.length && caretElementParentChildNodes[caretElementParentChildNodes.length - 1] === caretElement) {
caretElementParent.insertBefore(window.document.createTextNode('\u00A0'), caretElement.nextElementSibling);
}
}
}
if (!refs.multiline.current) {
if (event_0.key === 'Enter') {
event_0.preventDefault();
}
}
if (!refs.edit.current && (event_0.metaKey && keyboardStyleCommands.includes(event_0.key) || event_0.ctrlKey && keyboardStyleCommands.includes(event_0.key))) {
event_0.preventDefault();
}
if (is('function', onKeyDown_)) onKeyDown_(event_0);
};
const onPaste = event_1 => {
// event.preventDefault();
// const text = event.clipboardData?.getData('text/plain');
// window.document.execCommand('insertText', false, text);
};
const onPasteText = event_2 => {
event_2.preventDefault();
const text_0 = event_2.clipboardData?.getData('text/plain');
window.document.execCommand('insertText', false, text_0);
};
const onDrop = event_3 => {
event_3.preventDefault();
};
const addTag = (item, versionMention = 'user') => {
// Save caret position
refs.caret.current = 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_2 = window.document.getSelection();
if (!(windowSelection_2 && windowSelection_2.rangeCount > 0)) return;
const range_2 = windowSelection_2?.getRangeAt(0);
if (!range_2) return;
const textNode = windowSelection_2.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_2.endOffset;
const start_0 = end - refs.search.current.length;
const pre = textContent.slice(0, start_0);
const post = textContent.slice(end);
// Insert before anchorNode pre, item mention, and post
if (pre) textNode.parentElement.insertBefore(window.document.createTextNode(pre), windowSelection_2.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_2.anchorNode);
// 1 space only following the item mention
textNode.parentElement.insertBefore(window.document.createTextNode('\u00A0'), windowSelection_2.anchorNode);
if (post) textNode.parentElement.insertBefore(window.document.createTextNode(post), windowSelection_2.anchorNode);
// Remove the text node
textNode.remove();
// Invoke onChange method with new value
const valueInput_0 = innerHTMLToText(refs.root.current.innerHTML);
if (is('function', refs.onChange.current)) refs.onChange.current(valueInput_0, {
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;
caret.restore(refs.root.current, refs.caret.current);
}
// Close the search
onCloseSearch();
};
// If users response & not open, open
// else if no users response & opened, close the search
React.useEffect(() => {
if (mention) {
if (refs.openMenu.current && !optionsMention.length) onCloseSearch();else if (!refs.openMenu.current && refs.search.current && !!optionsMention.length) onOpenSearch();
}
}, [mention, optionsMention]);
React.useEffect(() => {
if (mention) {
// Validate
validate();
// Update
// open, selection & offset
setTimeout(() => {
refs.search.current ? onOpenSearch() : onCloseSearch();
if (refs.openMenu.current) updateSelection();
updateOffset();
});
}
}, [mention, value]);
React.useEffect(() => {
// setError(error_);
}, [error_]);
const label = () => is('function', mentionLabel) ? mentionLabel(optionsMention, {
addTag
}) : /*#__PURE__*/_jsx(Line, {
className: classes.menu,
children: /*#__PURE__*/_jsx(List, {
size: "small",
className: classes.list,
children: optionsMention?.map((item_0, index) => /*#__PURE__*/_jsx(ListItem, {
start: /*#__PURE__*/_jsx(Avatar, {
color: stringToColor(item_0.name),
size: "small",
children: item_0.name?.slice(0, 1)
}),
primary: /*#__PURE__*/_jsx(Type, {
version: "l3",
children: item_0.name
}),
onClick: () => addTag(item_0),
button: true
}, index))
})
});
const paste = async () => {
const valueClipboard = await navigator.clipboard.read();
if (valueClipboard) {
let values_1 = '';
for (const item_1 of Array.from(valueClipboard)) {
const valueItem = await item_1.getType('text/html');
values_1 += await valueItem.text();
}
rootDocument.execCommand('insertHTML', undefined, values_1);
}
};
const query = command => {
return 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_2 => {
if (query(item_2.command)) selected.push(item_2.name);
});
setTextSelected(selected);
};
const onMouseUp = () => {
if (!refs.edit.current) return;
const selection__0 = refs.rootWindow.current.getSelection();
if (!selection__0) return;
if (!selection__0.anchorNode || !refs.root.current.contains(selection__0.anchorNode)) return setTextSelection(null);
setTimeout(() => {
const rect = selection__0.getRangeAt(0).getBoundingClientRect();
setTextSelection(Math.round(rect.width) ? {
selection: rect,
element: selection__0.anchorNode?.parentElement
} : null);
textQueryUpdate();
}, 140);
};
const onMouseDown = () => {
if (!refs.edit.current) return;
textQueryUpdate();
};
const onKeyUp = () => {
if (!refs.edit.current) return;
textQueryUpdate();
};
const textMethod = command_0 => argument => {
switch (command_0) {
// updates
case 'italic':
refs.rootDocument.current.execCommand('italic');
if (query('italic')) setTextSelected(values_24 => [...values_24, 'italic']);else setTextSelected(values_25 => values_25.filter(item_20 => item_20 !== 'italic'));
break;
case 'underline':
refs.rootDocument.current.execCommand('underline');
if (query('underline')) setTextSelected(values_22 => [...values_22, 'underline']);else setTextSelected(values_23 => values_23.filter(item_19 => item_19 !== 'underline'));
break;
case 'bold':
refs.rootDocument.current.execCommand('bold');
if (query('bold')) setTextSelected(values_20 => [...values_20, 'bold']);else setTextSelected(values_21 => values_21.filter(item_18 => item_18 !== 'bold'));
break;
case 'strike-line':
refs.rootDocument.current.execCommand('strikeThrough');
if (query('strikeThrough')) setTextSelected(values_18 => [...values_18, 'strike-line']);else setTextSelected(values_19 => values_19.filter(item_17 => item_17 !== 'strike-line'));
break;
case 'align-left':
refs.rootDocument.current.execCommand('justifyLeft');
if (query('justifyLeft')) setTextSelected(values_16 => [...values_16.filter(item_15 => !item_15.includes('align')), 'align-left']);else setTextSelected(values_17 => values_17.filter(item_16 => item_16 !== 'align-left'));
break;
case 'align-center':
refs.rootDocument.current.execCommand('justifyCenter');
if (query('justifyCenter')) setTextSelected(values_14 => [...values_14.filter(item_13 => !item_13.includes('align')), 'align-center']);else setTextSelected(values_15 => values_15.filter(item_14 => item_14 !== 'align-center'));
break;
case 'align-right':
refs.rootDocument.current.execCommand('justifyRight');
if (query('justifyRight')) setTextSelected(values_12 => [...values_12.filter(item_11 => !item_11.includes('align')), 'align-right']);else setTextSelected(values_13 => values_13.filter(item_12 => item_12 !== 'align-right'));
break;
case 'align-justify':
refs.rootDocument.current.execCommand('justifyFull');
if (query('justifyFull')) setTextSelected(values_10 => [...values_10.filter(item_9 => !item_9.includes('align')), 'align-justify']);else setTextSelected(values_11 => values_11.filter(item_10 => item_10 !== 'align-justify'));
break;
case 'superscript':
refs.rootDocument.current.execCommand('superscript');
if (query('superscript')) setTextSelected(values_8 => [...values_8, 'superscript']);else setTextSelected(values_9 => values_9.filter(item_8 => item_8 !== 'superscript'));
break;
case 'subscript':
refs.rootDocument.current.execCommand('subscript');
if (query('subscript')) setTextSelected(values_6 => [...values_6, 'subscript']);else setTextSelected(values_7 => values_7.filter(item_7 => item_7 !== '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_4 => [...values_4.filter(item_5 => !item_5.includes('list')), 'list-ordered']);else setTextSelected(values_5 => values_5.filter(item_6 => item_6 !== 'list-ordered'));
break;
case 'list-unordered':
refs.rootDocument.current.execCommand('insertUnorderedList');
if (query('insertUnorderedList')) setTextSelected(values_2 => [...values_2.filter(item_3 => !item_3.includes('list')), 'list-unordered']);else setTextSelected(values_3 => values_3.filter(item_4 => item_4 !== '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_0 = refs.textSelection.current?.element;
if (element_0) {
let valueElement = element_0.innerHTML || '';
if (element_0.textContent === '') {
valueElement = '';
element_0.innerHTML = '';
}
if (valueElement) valueElement = innerHTMLToText(valueElement);
}
textQueryUpdate();
};
const PaletteItem = propsItem => {
const {
color: color_
} = propsItem,
other_ = _objectWithoutProperties(propsItem, _excluded2);
return /*#__PURE__*/_jsx("span", _objectSpread({
className: classNames([staticClassName('RichTextEditor', theme) && ['onesy-RichTextEditor-palette-item'], classes.paletteItem]),
style: {
background: color_
}
}, other_));
};
const Palette = propsPalette => {
const {
ref: ref_,
version: version_,
onUpdate: onUpdate_,
onClose
} = propsPalette,
other__0 = _objectWithoutProperties(propsPalette, _excluded3);
const onUpdateColor = itemColor => {
if (refs.range.current) {
const selection__1 = refs.rootWindow.current.getSelection();
selection__1.removeAllRanges();
selection__1.addRange(refs.range.current);
}
onUpdate_(itemColor);
onClose();
};
return /*#__PURE__*/_jsxs(Line, _objectSpread(_objectSpread({
ref: ref_,
gap: 1,
direction: "column",
tonal: tonal,
color: refs.props.current.color !== undefined ? refs.props.current.color : 'themed',
Component: Surface,
className: classNames([staticClassName('RichTextEditor', theme) && ['onesy-RichTextEditor-palette'], classes.palette])
}, other__0), {}, {
children: [/*#__PURE__*/_jsxs(Line, {
gap: 0.5,
style: {
maxHeight: 136,
padding: '10px 10px 0',
overflow: 'hidden auto'
},
children: [/*#__PURE__*/_jsxs(Line, {
gap: 0.5,
direction: "row",
style: {
width: '100%'
},
children: [/*#__PURE__*/_jsx(PaletteItem, {
color: "#000000",
onClick: () => {
onUpdateColor('#000000');
}
}), /*#__PURE__*/_jsx(PaletteItem, {
color: "#ffffff",
onClick: () => {
onUpdateColor('#ffffff');
}
})]
}), Object.keys(colors).filter(item_21 => !['black', 'white'].includes(item_21)).map((item_22, index_0) => /*#__PURE__*/_jsx(Line, {
gap: 0.5,
direction: "row",
style: {
width: '100%'
},
children: Object.keys(colors[item_22]).map((item_, index_) => /*#__PURE__*/_jsx(PaletteItem, {
color: colors[item_22][item_],
onClick: () => {
onUpdateColor(colors[item_22][item_]);
}
}, index_))
}, index_0))]
}), /*#__PURE__*/_jsx(Divider, {}), /*#__PURE__*/_jsxs(Line, {
gap: 0.5,
direction: "row",
align: "center",
fullWidth: true,
style: {
padding: '0px 10px 10px'
},
children: [/*#__PURE__*/_jsx(ColorTextField, _objectSpread(_objectSpread({
tonal: tonal,
color: color,
name: l('Custom color'),
version: "outlined",
size: "small",
value: refs.inputValues.current[version_],
onChange: valueNew_2 => updateInputValues(version_, valueNew_2)
}, ColorTextFieldProps), {}, {
className: classNames([staticClassName('RichTextEditor', theme) && ['onesy-RichTextEditor-text-field-color'], ColorTextFieldProps?.className, classes.textFieldColor])
})), /*#__PURE__*/_jsx(Button, {
tonal: tonal,
color: "inherit",
version: "text",
size: "small",
onClick: () => {
if (refs.range.current) {
const selection__2 = refs.rootWindow.current.getSelection();
selection__2.removeAllRanges();
selection__2.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 = propsInput => {
const {
ref: ref__0,
label: labelInput,
labelButton,
value: value__,
onChange: onChange__,
onClick,
placeholder: placeholderInputProps,
InputComponent = TextField,
InputProps
} = propsInput,
other__1 = _objectWithoutProperties(propsInput, _excluded4);
return /*#__PURE__*/_jsx(Line, _objectSpread(_objectSpread({
ref: ref__0,
gap: 1,
direction: "column",
color: "themed",
Component: Surface
}, other__1), {}, {
className: classNames([other__1?.className, classes.inputWrapper]),
children: /*#__PURE__*/_jsxs(Line, {
gap: 0.5,
direction: "row",
align: "center",
style: {
width: '100%'
},
children: [/*#__PURE__*/_jsx(InputComponent, _objectSpread(_objectSpread({
name: labelInput,
version: "outlined",
size: "small",
valueDefault: value__,
onChange: onChange__,
placeholder: placeholderInputProps,
flex: true
}, InputProps), {}, {
style: {
width: 'unset',
flex: '1 1 auto'
}
})), /*#__PURE__*/_jsx(Button, {
color: "inherit",
version: "text",
size: "small",
onClick: onClick,
children: labelButton
})]
})
}));
};
const WrapperAppend = propsWrapper => {
const {
open: open_,
element: element_1,
anchorElement,
onClose: onClose_0,
children: childrenWrapperAppend
} = propsWrapper,
other__2 = _objectWithoutProperties(propsWrapper, _excluded5);
return /*#__PURE__*/_jsx(Append, _objectSpread(_objectSpread({
open: open_,
element: /*#__PURE__*/_jsx("div", {
className: classNames([staticClassName('SmartTextField', theme) && ['onesy-SmartTextField-mini-menu-additional'], classes.textMiniMenuAdditionalMenu]),
children: /*#__PURE__*/_jsx(Fade, {
in: open_,
add: true,
children: /*#__PURE__*/React.cloneElement(element_1)
})
}),
anchorElement: anchorElement,
portal: true,
alignment: "center",
position: "bottom"
}, AppendProps), {}, {
children: /*#__PURE__*/React.cloneElement(childrenWrapperAppend, _objectSpread(_objectSpread({}, other__2), childrenWrapperAppend.props))
}));
};
const WrapperToggleButton = propsWrapperToggleButton => {
const {
ref: ref__1,
open: open__0,
name: nameWrapperToogleButton,
children: childrenWrapperToggleButton
} = propsWrapperToggleButton,
other__3 = _objectWithoutProperties(propsWrapperToggleButton, _excluded6);
return /*#__PURE__*/_jsx(Tooltip, _objectSpread(_objectSpread({
ref: ref__1,
open: open__0 !== undefined ? open__0 : undefined,
name: nameWrapperToogleButton
}, TooltipProps), {}, {
children: /*#__PURE__*/React.cloneElement(childrenWrapperToggleButton, _objectSpread(_objectSpread({}, other__3), childrenWrapperToggleButton.props))
}));
};
const updateElements = {
'italic': /*#__PURE__*/_jsx(WrapperToggleButton, {
name: l('Italic'),
children: /*#__PURE__*/_jsx(ToggleButton, _objectSpread(_objectSpread({}, ToggleButtonProps), {}, {
selected: refs.textSelected.current.includes('italic'),
onClick: textMethod('italic'),
children: /*#__PURE__*/_jsx(IconMaterialFormatItalic, _objectSpread({}, IconProps))
}))
}),
'underline': /*#__PURE__*/_jsx(WrapperToggleButton, {
name: l('Underline'),
children: /*#__PURE__*/_jsx(ToggleButton, _objectSpread(_objectSpread({}, ToggleButtonProps), {}, {
selected: refs.textSelected.current.includes('underline'),
onClick: textMethod('underline'),
children: /*#__PURE__*/_jsx(IconMaterialFormatUnderlined, _objectSpread({}, IconProps))
}))
}),
'bold': /*#__PURE__*/_jsx(WrapperToggleButton, {
name: l('Bold'),
onClick: textMethod('bold'),
children: /*#__PURE__*/_jsx(ToggleButton, _objectSpread(_objectSpread({}, ToggleButtonProps), {}, {
selected: refs.textSelected.current.includes('bold'),
children: /*#__PURE__*/_jsx(IconMaterialFormatBold, _objectSpread({}, IconProps))
}))
}),
'superscript': /*#__PURE__*/_jsx(WrapperToggleButton, {
name: l('Superscript'),
onClick: textMethod('superscript'),
children: /*#__PURE__*/_jsx(ToggleButton, _objectSpread(_objectSpread({}, ToggleButtonProps), {}, {
selected: refs.textSelected.current.includes('superscript'),
children: /*#__PURE__*/_jsx(IconMaterialSuperscript, _objectSpread({}, IconProps))
}))
}),
'subscript': /*#__PURE__*/_jsx(WrapperToggleButton, {
name: l('Subscript'),
onClick: textMethod('subscript'),
children: /*#__PURE__*/_jsx(ToggleButton, _objectSpread(_objectSpread({}, ToggleButtonProps), {}, {
selected: refs.textSelected.current.includes('subscript'),
children: /*#__PURE__*/_jsx(IconMaterialSubscript, _objectSpread({}, IconProps))
}))
}),
'strike-line': /*#__PURE__*/_jsx(WrapperToggleButton, {
name: l('Strike Line'),
onClick: textMethod('strike-line'),
children: /*#__PURE__*/_jsx(ToggleButton, _objectSpread(_objectSpread({}, ToggleButtonProps), {}, {
selected: refs.textSelected.current.includes('strike-line'),
children: /*#__PURE__*/_jsx(IconMaterialStrikethroughS, _objectSpread({}, IconProps))
}))
}),
'link-add': /*#__PURE__*/_jsx(WrapperAppend, {
open: refs.open.current.linkMiniMenu,
anchorElement: refs.miniMenuElements.linkAdd,
element: /*#__PURE__*/_jsx(ClickListener, {
onClickOutside: () => updateOpen('linkMiniMenu', false),
include: [refs.miniMenuElements.linkAdd],
children: /*#__PURE__*/_jsx(Input, {
ref: refs.miniMenuElements.linkAddInput,
name: l('Link'),
labelButton: l('Add'),
value: refs.inputValues.current.link,
onChange: valueNew_3 => updateInputValues('link', valueNew_3),
placeholder: l('URL'),
onClick: () => {
if (refs.range.current) {
const selection__3 = refs.rootWindow.current.getSelection();
if (!selection__3) return;
selection__3.removeAllRanges();
selection__3.addRange(refs.range.current);
}
textMethod('link-add')(refs.inputValues.current.link);
updateOpen('linkMiniMenu', false);
updateInputValues('link', '');
},
className: classes.input
})
}),
children: /*#__PURE__*/_jsx(WrapperToggleButton, {
name: l('Insert Link'),
open: refs.open.current.linkMiniMenu ? false : undefined,
children: /*#__PURE__*/_jsx(ToggleButton, _objectSpread(_objectSpread({
ref: refs.miniMenuElements.linkAdd
}, ToggleButtonProps), {}, {
selected: refs.open.current.linkMiniMenu,
onClick: () => updateOpen('linkMiniMenu', !refs.open.current.linkMiniMenu),
children: /*#__PURE__*/_jsx(IconMaterialAddLink, _objectSpread({}, IconProps))
}))
})
}),
'link-remove': /*#__PURE__*/_jsx(WrapperToggleButton, {
name: l('Remove Link'),
children: /*#__PURE__*/_jsx(ToggleButton, _objectSpread(_objectSpread({}, ToggleButtonProps), {}, {
onClick: textMethod('link-remove'),
children: /*#__PURE__*/_jsx(IconMaterialLinkOff, _objectSpread({}, IconProps))
}))
}),
'font-color': /*#__PURE__*/_jsx(WrapperAppend, {
open: refs.open.current.color,
anchorElement: refs.elements.color.current,
element: /*#__PURE__*/_jsx(ClickListener, {
onClickOutside: () => updateOpen('color', false),
include: [refs.elements.color.current],
children: /*#__PURE__*/_jsx(Palette, {
version: "font-color",
onClose: () => updateOpen('color', false),
onUpdate: () => {
if (refs.range.current) {
const selection__4 = refs.rootWindow.current.getSelection();
selection__4.removeAllRanges();
selection__4.addRange(refs.range.current);
}
textMethod('font-color');
}
})
}),
children: /*#__PURE__*/_jsx(WrapperToggleButton, {
name: l('Text Color'),
open: refs.open.current.color ? false : undefined,
children: /*#__PURE__*/_jsx(ToggleButton, _objectSpread(_objectSpread({
ref: refs.elements.color
}, ToggleButtonProps), {}, {
selected: refs.open.current.color,
onClick: () => updateOpen('color', !refs.open.current.color),
children: /*#__PURE__*/_jsx(IconMaterialFormatColorText, _objectSpread({}, IconProps))
}))
})
}),
'font-color-mini-menu': /*#__PURE__*/_jsx(WrapperAppend, {
open: refs.open.current.colorMiniMenu,
anchorElement: refs.miniMenuElements.color,
element: /*#__PURE__*/_jsx(ClickListener, {
onClickOutside: () => updateOpen('colorMiniMenu', false),
include: [refs.miniMenuElements.color],
children: /*#__PURE__*/_jsx(Palette, {
ref: refs.miniMenuElements.colorPalette,
version: "font-color",
onClose: () => updateOpen('colorMiniMenu', false),
onUpdate: textMethod('font-color')
})
}),
children: /*#__PURE__*/_jsx(WrapperToggleButton, {
name: l('Text Color'),
open: refs.open.current.colorMiniMenu ? false : undefined,
children: /*#__PURE__*/_jsx(ToggleButton, _objectSpread(_objectSpread({
ref: refs.miniMenuElements.color
}, ToggleButtonProps), {}, {
selected: refs.open.current.colorMiniMenu,
onClick: () => updateOpen('colorMiniMenu', !refs.open.current.colorMiniMenu),
children: /*#__PURE__*/_jsx(IconMaterialFormatColorText, _objectSpread({}, IconProps))
}))
})
}),
'font-background': /*#__PURE__*/_jsx(WrapperAppend, {
open: refs.open.current.background,
anchorElement: refs.elements.background.current,
element: /*#__PURE__*/_jsx(ClickListener, {
onClickOutside: () => updateOpen('background', false),
include: [refs.elements.background.current],
children: /*#__PURE__*/_jsx(Palette, {
version: "font-background",
onClose: () => updateOpen('background', false),
onUpdate: textMethod('font-background')
})
}),
children: /*#__PURE__*/_jsx(WrapperToggleButton, {
name: l('Background Color'),
open: refs.open.current.background ? false : undefined,
children: /*#__PURE__*/_jsx(ToggleButton, _objectSpread(_objectSpread({
ref: refs.elements.background
}, ToggleButtonProps), {}, {
selected: refs.open.current.background,
onClick: () => updateOpen('background', !refs.open.current.background),
children: /*#__PURE__*/_jsx(IconMaterialFormatColorFill, _objectSpread({}, IconProps))
}))
})
}),
'font-background-mini-menu': /*#__PURE__*/_jsx(WrapperAppend, {
open: refs.open.current.backgroundMiniMenu,
anchorElement: refs.miniMenuElements.background,
element: /*#__PURE__*/_jsx(ClickListener, {
onClickOutside: () => updateOpen('backgroundMiniMenu', false),
include: [refs.miniMenuElements.background],
children: /*#__PURE__*/_jsx(Palette, {
ref: refs.miniMenuElements.backgroundPalette,
version: "font-background",
onClose: () => updateOpen('backgroundMiniMenu', false),
onUpdate: textMethod('font-background')
})
}),
children: /*#__PURE__*/_jsx(WrapperToggleButton, {
name: l('Text Color'),
open: refs.open.current.backgroundMiniMenu ? false : undefined,
children: /*#__PURE__*/_jsx(ToggleButton, _objectSpread(_objectSpread({
ref: refs.miniMenuEleme