UNPKG

@hhgtech/hhg-components

Version:
999 lines (974 loc) • 42.7 kB
'use strict'; var tslib_es6 = require('./tslib.es6-92cccef3.js'); var React = require('react'); var index$5 = require('./index-b9594844.js'); var index$4 = require('./index-da18c632.js'); var index$1 = require('./index-25f2e7a5.js'); var index$3 = require('./index-c2c283f8.js'); var debounce = require('lodash/debounce'); var miscCookieHelper = require('./miscCookieHelper.js'); var useUniqueId = require('./useUniqueId-6e2f8c19.js'); var togetherComponentGlobalContext = require('./utils-aea77f4a.js'); var styled = require('@emotion/styled'); var miscTheme = require('./miscTheme.js'); var togetherApiPaths = require('./togetherApiPaths.js'); var constants = require('./constants-bb30dda6.js'); var post = require('./post-ca5ee38c.js'); var index = require('./index-6f85be79.js'); var index$2 = require('./index-61874f19.js'); function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n["default"] = e; return Object.freeze(n); } var React__default = /*#__PURE__*/_interopDefault(React); var debounce__default = /*#__PURE__*/_interopDefault(debounce); var styled__default = /*#__PURE__*/_interopDefault(styled); const StyledCreatePostDescription = styled__default["default"].div ` input:focus, textarea:focus, [contenteditable='true'] { outline: none; } &.has-ask-doctor-option { .textarea-control { border-bottom: none; border-radius: 4px 4px 0 0; } } &.error-description { .textarea-control { border: 1px solid ${miscTheme.theme.colors.red600}; } } .textarea-control { position: relative; border: 1px solid ${miscTheme.theme.colors.gray200}; border-radius: ${miscTheme.theme.borderRadius}; &.--focus { border-color: ${miscTheme.theme.colors.primaryBase}; box-shadow: 0px 0px 2px 2px ${miscTheme.theme.colors.primary200}; } .textarea-wrapper { width: 100%; height: 200px; } .control-tool { position: absolute; bottom: 0; left: 0; display: flex; width: calc(100% - 2rem); height: 40px; align-items: center; justify-content: flex-end; border-top: 1px solid ${miscTheme.theme.colors.gray200}; margin: 0 1rem; .tool-button { display: flex; width: 2rem; height: 2rem; align-items: center; justify-content: center; background: transparent; cursor: pointer; } .image-icon { vertical-align: middle; } } } .link-preview-wrapper { position: relative; .icon-close { position: absolute; top: 5px; right: 5px; width: 24px; height: 24px; cursor: pointer; } } .image-gallery { margin-bottom: 1rem; .image-item { position: relative; overflow: hidden; width: 100%; height: 0; padding-top: 67%; margin-top: 1rem; border-radius: ${miscTheme.theme.borderRadius}; & > img { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; } .close-btn { position: absolute; top: 8px; right: 8px; left: unset; width: 24px; height: 24px; cursor: pointer; } } } .description-label { margin-bottom: 5px; color: ${miscTheme.theme.colors.gray600}; /* font-family: 'Open Sans', sans-serif; */ font-size: 13px; font-weight: bold; } .count-string { margin-top: 5px; color: ${miscTheme.theme.colors.gray500}; font-size: 11px; text-align: right; } .input-notification { display: flex; & > img { width: 16px; height: 16px; margin-top: 1.5px; margin-right: 8px; } } `; const StyledEditorContainer = styled__default["default"].div ` height: 100%; position: relative; width: 100%; display: flex; flex-direction: column; .ql-toolbar.ql-snow { top: 0; width: 100%; // position: sticky; z-index: 3; border: none; border-bottom: 1px solid #ccc; border-radius: 4px 4px 0 0; background: white; .ql-formats { display: inline-flex; align-items: center; margin-right: 0.5rem; .link-tool-wrapper { .popover-menu__content { padding: 0.5rem; transform: translateX(calc(-50% - 1rem)); max-width: calc(100vw - 4rem); ::before { right: calc(50% - 8px); } .link-tool-popover { input { padding-left: 1rem; } .link-input-url { margin-bottom: 0.5rem; } .link-input-text { margin-bottom: 0.5rem; } label { display: inline-block; margin-bottom: 0.2rem; } .link-input-button-wrapper { &[data-is-marrybaby='true'] { .link-input-button { border: solid 2px ${miscTheme.theme.mbColors.pink}; background-color: ${miscTheme.theme.mbColors.pink}; &:hover:not(:disabled) { border: solid 2px ${miscTheme.theme.mbColors.pink}; background-color: ${miscTheme.theme.colors.white}; color: ${miscTheme.theme.mbColors.pink}; } } } } .link-input-button { width: auto; height: auto; padding: 0.4rem 1rem; background-color: ${miscTheme.theme.colors.primaryBase}; &:disabled { border-color: #bfbfbf; background-color: #bfbfbf; color: #fff; cursor: not-allowed; } &:hover:not(:disabled) { background-color: ${miscTheme.theme.colors.primaryHover}; } } .error-text { padding: 0; margin-bottom: 0.5rem; font-weight: 600; color: ${miscTheme.theme.colors.red700}; } } } } } @media (max-width: 450px) { position: relative; .ql-formats { .link-tool-wrapper { position: unset; .popover-menu__content { left: 0; transform: unset; max-width: 100%; } } } } } .quill { flex: 1; overflow-y: hidden; .ql-container { display: inline-block; width: 100%; min-height: 100%; /* padding: 14px 16px; */ border: none; border-radius: ${miscTheme.theme.borderRadius}; color: ${miscTheme.theme.colors.gray800}; font-size: 1rem; line-height: 1.5; resize: none; white-space: break-spaces; .ql-editor { word-break: break-word; overflow-wrap: break-word; /* -ms-overflow-style: none; scrollbar-width: none; &::-webkit-scrollbar { display: none; } */ } mocka { padding: 2px 6px; border-radius: 1rem; &[data-url='true'] { /* background-color: #eeeeff; */ font-style: italic; color: #1890ff; cursor: pointer; outline: none; text-decoration: underline; } &[data-error='true'] { background-color: #ffeeee; color: red; text-decoration: underline; font-style: bold; } } mention { padding: 2px 6px; border-radius: 1rem; background-color: #eeeeff; color: #1890ff; cursor: pointer; outline: none; text-decoration: none; } ol, ul { padding-left: 1rem; } } } .mention-search-container { position: absolute; background: white; border-radius: ${miscTheme.theme.borderRadius}; filter: drop-shadow(0px 10px 16px rgba(0, 0, 0, 0.04)) drop-shadow(0px 2px 8px rgba(0, 0, 0, 0.04)) drop-shadow(0px 0px 1px rgba(0, 0, 0, 0.04)); overflow-y: scroll; z-index: 99; ::-webkit-scrollbar { -webkit-appearance: none; } ::-webkit-scrollbar:vertical { width: 14px; } ::-webkit-scrollbar-thumb { border-radius: ${miscTheme.theme.borderRadius}; background-color: #d9d9d9; @supports (background-clip: padding-box) { border: 3px solid rgba(0, 0, 0, 0); background-clip: padding-box; } @supports not (background-clip: padding-box) { border: 3px solid white; } } ::-webkit-scrollbar-track { background-color: #fff; border-radius: 0 6px 6px 0; border-left: 1px solid #f2f2f2; } .mention-search-item { cursor: pointer; padding: 12px 1rem; display: flex; align-items: center; &.item-selected { background-color: ${miscTheme.theme.colors.gray100}; } .search-avatar { width: 40px; height: 40px; font-size: 20px; margin-right: 20px; } } } `; var img = "data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='none'%3e%3cpath stroke='%23737373' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.75' d='M9.545 16H7.091c-.537 0-1.07-.104-1.566-.305a4.1 4.1 0 0 1-1.327-.867A3.96 3.96 0 0 1 3 12c0-1.06.431-2.078 1.198-2.828A4.14 4.14 0 0 1 7.091 8h2.454m4.38 0h2.984c.537 0 1.07.103 1.566.304a4.1 4.1 0 0 1 1.327.868c.38.371.681.812.887 1.297a3.92 3.92 0 0 1 0 3.062 4 4 0 0 1-.887 1.297c-.38.372-.831.667-1.327.867a4.2 4.2 0 0 1-1.566.305h-2.983M9 12h6'/%3e%3c/svg%3e"; var IconLink = img; let ReactQuill = null; (() => tslib_es6.__awaiter(void 0, void 0, void 0, function* () { if (typeof window === 'undefined') return; const QuillRes = yield Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('react-quill')); }); ReactQuill = QuillRes.default; const Quill = QuillRes.Quill || ReactQuill.Quill; class PreserveWhiteSpace { constructor(quill, options) { this.quill = quill; this.options = options; quill.container.style.whiteSpace = 'pre-line'; } } Quill.register('modules/preserveWhiteSpace', PreserveWhiteSpace); const Inline = Quill.import('blots/inline'); class MockABlot extends Inline { static create(options) { if (!options) return super.create(false); const node = super.create(options); MockABlot.setNodeConfigurations(node, options); return node; } static formats(node) { if (node.innerText && (node.getAttribute('data-href') || node.getAttribute('data-url') || node.getAttribute('data-error') || node.getAttribute('data-bad-word'))) { return { 'data-bad-word': node.getAttribute('data-bad-word'), 'data-error': node.getAttribute('data-error'), 'data-href': node.getAttribute('data-href'), 'data-url': node.getAttribute('data-url'), 'data-text': node.innerText || node.getAttribute('data-href'), }; } else { return false; } } /* To prevent the node from accidentally being built in different ways. */ static setNodeConfigurations(node, value) { var _a, _b; if (!value) return; if (!((_b = (_a = value === null || value === void 0 ? void 0 : value['data-text']) === null || _a === void 0 ? void 0 : _a.replace(/(\&nbsp;)+/g, ' ')) === null || _b === void 0 ? void 0 : _b.replace(/(\u200c|&zwnj;)+/g, ''))) return; if (value['data-bad-word']) node.setAttribute('data-bad-word', value['data-bad-word']); if (value['data-href']) node.setAttribute('data-href', value['data-href']); if (value['data-error']) node.setAttribute('data-error', value['data-error']); // setTimeout(() => { // if (value['data-text'] !== node.innerText && !node.innerText) // node.innerHTML = value['data-text'] // }, 200) if (value['data-url']) node.setAttribute('data-url', value['data-url']); // if (value['data-text']) node.innerText = value['data-text'] // console.log(value, node.outerHTML, node) } } MockABlot.blotName = 'mocka1'; MockABlot.tagName = 'mocka'; Quill.register(MockABlot); class MentionBlot extends Inline { static create(options) { if (!options) return super.create(false); const node = super.create(options); MentionBlot.setNodeConfigurations(node, options); return node; } static formats(node) { if (node.getAttribute('data-name') && node.getAttribute('data-id')) { return { 'data-name': node.getAttribute('data-name'), 'data-id': node.getAttribute('data-id'), }; } else { return false; } } /* To prevent the node from accidentally being built in different ways. */ static setNodeConfigurations(node, value) { var _a, _b; if (!value) return; if (!value['data-id'] || !((_b = (_a = value === null || value === void 0 ? void 0 : value['data-name']) === null || _a === void 0 ? void 0 : _a.replace(/(\&nbsp;)+/g, ' ')) === null || _b === void 0 ? void 0 : _b.replace(/(\u200c|&zwnj;)+/g, ''))) return; if (value['data-id']) node.setAttribute('data-id', value['data-id']); if (value['data-name']) { // node.setAttribute('contenteditable', false) node.setAttribute('data-name', value['data-name']); } } } MentionBlot.blotName = 'mention'; MentionBlot.tagName = 'mention'; Quill.register(MentionBlot); }))(); function getCaretCharacterOffsetWithin(element) { var _a, _b; let caretOffset = -1; if (typeof window.getSelection != 'undefined') { const sel = window.getSelection(); if ((sel === null || sel === void 0 ? void 0 : sel.rangeCount) && (sel === null || sel === void 0 ? void 0 : sel.rangeCount) > 0) { const range = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.getRangeAt(0); if (range) { const preCaretRange = range.cloneRange(); // preCaretRange.setStart(element, 0) preCaretRange.selectNodeContents(element); preCaretRange.setEnd(range.endContainer, range.endOffset); caretOffset = calculateChildNodeOffset(preCaretRange.cloneContents().childNodes, caretOffset); const parentMockAElement = (_b = range.endContainer.parentElement) === null || _b === void 0 ? void 0 : _b.closest('mocka'); const lastNodeValue = range.endContainer.nodeValue || range.endContainer.outerHTML; if (parentMockAElement && (lastNodeValue === null || lastNodeValue === void 0 ? void 0 : lastNodeValue.length)) { /// set selector to end of <mocka elemt, prevent select in the middle caretOffset = caretOffset - range.endOffset + (parentMockAElement.outerHTML.length - (parentMockAElement === null || parentMockAElement === void 0 ? void 0 : parentMockAElement.outerHTML.lastIndexOf(lastNodeValue))); } } } } return caretOffset; } function calculateChildNodeOffset(childNodes, caretOffset) { childNodes.forEach((node, index) => { const isLast = index + 1 === childNodes.length; if (node.nodeType === Node.ELEMENT_NODE) { const outerHTML = node.outerHTML; if (!isLast) { caretOffset += outerHTML.length; return; } else { caretOffset += outerHTML.indexOf('>') + 1; caretOffset = calculateChildNodeOffset(node.childNodes, caretOffset); } } else if (node.nodeType === Node.TEXT_NODE) { caretOffset += (node.nodeValue || '') .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#039;').length; } }); return caretOffset; } function getSearchName(root) { var _a, _b; let res = null; if (typeof window.getSelection != 'undefined') { const sel = window.getSelection(); if ((sel === null || sel === void 0 ? void 0 : sel.rangeCount) && (sel === null || sel === void 0 ? void 0 : sel.rangeCount) > 0) { const range = (_a = window.getSelection()) === null || _a === void 0 ? void 0 : _a.getRangeAt(0); if (range) { const preCaretRange = range.cloneRange(); // preCaretRange.setStart(element, 0) preCaretRange.selectNodeContents(root); preCaretRange.setEnd(range.endContainer, range.endOffset); const endText = (preCaretRange.endContainer.nodeValue || '').substring(0, range.endOffset); // only allow @ at start of text or has space before it const lastAnchorSymbolIndex = endText.lastIndexOf('@'); if (lastAnchorSymbolIndex + 1 < endText.length && (lastAnchorSymbolIndex === 0 || endText.slice(lastAnchorSymbolIndex - 1, lastAnchorSymbolIndex + 1) === ' @')) { const name = (_b = endText.slice(lastAnchorSymbolIndex + 1)) === null || _b === void 0 ? void 0 : _b.trim(); if (name) { preCaretRange.setStart(range.endContainer, lastAnchorSymbolIndex); const replaceRange = preCaretRange.cloneRange(); replaceRange.setStart(replaceRange.endContainer, lastAnchorSymbolIndex); res = { name, rect: preCaretRange.getBoundingClientRect(), replaceRange, }; } } } } } return res; } const inputModify = (value, opts) => { var _a; const { formatUrl, bannedWords = [] } = opts || {}; const removedFormat = post.fixMalformedMention(post.removeFontFormat(value)); if (!formatUrl) return { value: post.highlightBadWords(removedFormat, bannedWords), }; let descriptionWithWrappedAnchor = post.removeEdittedBannedWord(post.wrapAnchorAroundUrls(removedFormat)); const urls = post.getUrlsFromEditorString(removedFormat); if (urls) { const invalidUrls = []; let previewUrl = ''; urls.forEach((url) => { if (!url) return; try { const jsUrl = new URL(url); if (post.ALLOW_DOMAIN_URL.some((o) => jsUrl.origin.endsWith(o))) { // is valid } else { invalidUrls.push(url); // is not valid } } catch (_a) { invalidUrls.push(url); } }); if (invalidUrls.length > 0) { descriptionWithWrappedAnchor = descriptionWithWrappedAnchor .replace(new RegExp(`data-url="true">(${invalidUrls.map(post.escapeRegExp).join('|')})<`, 'gi'), 'data-error="true" data-url="true">$1<') .replace(new RegExp(`data-href="(${invalidUrls .map(post.escapeRegExp) .join('|')})" data-url="true">`, 'gi'), 'data-href="$1" data-error="true" data-url="true">'); } previewUrl = (_a = urls.filter((u) => invalidUrls.indexOf(u) === -1)) === null || _a === void 0 ? void 0 : _a[0]; return { invalidUrls, previewUrl, value: post.highlightBadWords(descriptionWithWrappedAnchor, bannedWords), }; } return { value: post.highlightBadWords(descriptionWithWrappedAnchor, bannedWords), }; }; const RichTextEditor = ({ html, onChange, className, style, onImagePickerClick, bannedWords = [], onInvalidUrlsChange, onPreviewUrlChange, setEditorRef, isReplying, }) => { var _a, _b, _c, _d; const bannedWordsRef = React.useRef([]); bannedWordsRef.current = bannedWords; const [invalidUrls, setInvalidUrls] = React.useState([]); const [previewUrl, setPreviewUrl] = React.useState(null); const { data: { locale }, } = React.useContext(togetherComponentGlobalContext.TogetherComponentGlobalContext); const bannedString = (bannedWords === null || bannedWords === void 0 ? void 0 : bannedWords.join(',')) || ''; React.useEffect(() => { var _a, _b; const currHTML = (_b = (_a = reactQuillRef.current) === null || _a === void 0 ? void 0 : _a.getEditor()) === null || _b === void 0 ? void 0 : _b.root.innerHTML; debounceInputModify(currHTML); }, [bannedString]); const fetchPreviewData = (url) => { if (!previewUrl || previewUrl.url !== url) { setPreviewUrl({ url, isFetching: true, }); togetherComponentGlobalContext.callApi(togetherComponentGlobalContext.getApiPath(togetherApiPaths.PATHS.FETCH_PREVIEW, { _locale: locale, }), 'POST', { data: { link: url, }, }) .then((res) => { var _a, _b, _c, _d; setPreviewUrl({ title: (_a = res === null || res === void 0 ? void 0 : res.data) === null || _a === void 0 ? void 0 : _a.title, description: (_b = res === null || res === void 0 ? void 0 : res.data) === null || _b === void 0 ? void 0 : _b.description, image: ((_c = res === null || res === void 0 ? void 0 : res.data) === null || _c === void 0 ? void 0 : _c.image) || ((_d = res === null || res === void 0 ? void 0 : res.data) === null || _d === void 0 ? void 0 : _d.logo), url, }); }) .catch(() => { setPreviewUrl(null); }); } }; const fetchPreviewDataRef = React.useRef(fetchPreviewData); fetchPreviewDataRef.current = fetchPreviewData; const debounceInputModify = React.useCallback(debounce__default["default"]((v) => { if (v) { const processedInput = inputModify(v, { bannedWords: bannedWordsRef.current, formatUrl: true, }); const { invalidUrls: _invalidUrls, previewUrl: _previewUrl, value, } = processedInput; if (_invalidUrls) { setInvalidUrls(_invalidUrls); } if (_previewUrl) { fetchPreviewDataRef.current(_previewUrl); } else setPreviewUrl(null); if (value !== v) { onChange === null || onChange === void 0 ? void 0 : onChange(value); } } }, 500), []); React.useEffect(() => { onInvalidUrlsChange && onInvalidUrlsChange(invalidUrls); }, [invalidUrls]); React.useEffect(() => { if (onPreviewUrlChange) { if (previewUrl === null || previewUrl === void 0 ? void 0 : previewUrl.url) onPreviewUrlChange({ title: previewUrl.title || '', url: previewUrl.url || '', image: previewUrl.image || '', description: previewUrl.description || '', }); else onPreviewUrlChange(null); } }, [previewUrl]); const imageHandlerRef = React.useRef(onImagePickerClick); imageHandlerRef.current = onImagePickerClick; const toolbarId = useUniqueId.useUniqueId(); const cachedModules = React.useRef({ toolbar: { handlers: { image: () => { var _a; return (_a = imageHandlerRef.current) === null || _a === void 0 ? void 0 : _a.call(imageHandlerRef); }, link: () => false, }, container: '#toolbar-' + toolbarId, }, // toolbar: [ // ['bold', 'italic', 'underline'], // [{ list: 'ordered' }, { list: 'bullet' }], // ], preserveWhiteSpace: true, }); const contentEditableCaretPos = React.useRef(-1); const reactQuillRef = React.useRef(null); setEditorRef && ((_b = (_a = reactQuillRef.current) === null || _a === void 0 ? void 0 : _a.getEditor()) === null || _b === void 0 ? void 0 : _b.root) && setEditorRef((_d = (_c = reactQuillRef.current) === null || _c === void 0 ? void 0 : _c.getEditor()) === null || _d === void 0 ? void 0 : _d.root); const handleSelectionChange = () => { var _a, _b, _c, _d, _e, _f, _g, _h; if ((_b = (_a = reactQuillRef.current) === null || _a === void 0 ? void 0 : _a.getEditor()) === null || _b === void 0 ? void 0 : _b.root) { const newCaretPos = getCaretCharacterOffsetWithin((_d = (_c = reactQuillRef.current) === null || _c === void 0 ? void 0 : _c.getEditor()) === null || _d === void 0 ? void 0 : _d.root); const currHTML = (_f = (_e = reactQuillRef.current) === null || _e === void 0 ? void 0 : _e.getEditor()) === null || _f === void 0 ? void 0 : _f.root.innerHTML; // in case of clicking on a div outside editor while focus, we get caret position too large if (newCaretPos >= 0 && newCaretPos < currHTML.length) { const mentionIds = post.getMentionIdsFromString(currHTML, true); if (mentionIds.length < post.LIMIT_MENTION - (isReplying ? 1 : 0)) { const searchData = getSearchName((_h = (_g = reactQuillRef.current) === null || _g === void 0 ? void 0 : _g.getEditor()) === null || _h === void 0 ? void 0 : _h.root); if (searchData) { searchUser(searchData.name, searchData.rect); replaceRangeRef.current = searchData.replaceRange; } else { replaceRangeRef.current = null; // to avoid race condition, we reuse this function to clear foundUsers searchUser('', null); } } else { replaceRangeRef.current = null; // to avoid race condition, we reuse this function to clear foundUsers searchUser('', null); } // const insertIndex = newCaretPos + 1 // const currHTML = reactQuillRef.current?.getEditor()?.root.innerHTML // console.log( // currHTML?.substring(0, insertIndex) || '', // '========', // currHTML?.substring(insertIndex) || '', // ) contentEditableCaretPos.current = newCaretPos; } } else { setFoundUsers([]); } }; const [foundUsers, setFoundUsers] = React.useState([]); const [searchUserPosition, setSearchUserPosition] = React.useState({ top: 0, left: 0, }); const searchUser = React.useCallback(debounce__default["default"]((name, rect, excludeIds) => tslib_es6.__awaiter(void 0, void 0, void 0, function* () { var _e, _f, _g, _h; if (!name || !DOMRect) { return setFoundUsers([]); } const res = yield ((((_e = miscCookieHelper.getCookie(constants.BEARER_TOKEN_COOKIE)) === null || _e === void 0 ? void 0 : _e.length) || 0) > 0 ? togetherComponentGlobalContext.callApiWithAuth(togetherComponentGlobalContext.getApiPath(togetherApiPaths.PATHS.SEARCH_USER, { _locale: locale, name: name, }), 'get') : togetherComponentGlobalContext.callApiWithAdminAuth(togetherComponentGlobalContext.getApiPath(togetherApiPaths.ADMIN_PATHS.SEARCH_USER, { _locale: locale, name: name, }), 'get')); setSelectTagUserIndex(0); setFoundUsers((_g = (_f = res === null || res === void 0 ? void 0 : res.data) === null || _f === void 0 ? void 0 : _f.users) === null || _g === void 0 ? void 0 : _g.filter((u) => !(excludeIds === null || excludeIds === void 0 ? void 0 : excludeIds.includes(String(u.id))))); const textNodeRect = rect; const containerRect = (_h = containerRef.current) === null || _h === void 0 ? void 0 : _h.getBoundingClientRect(); if (textNodeRect && containerRect) { const idealWidth = 343; const idealHeight = 256; const styleConfig = { top: textNodeRect.bottom - containerRect.top, width: `min(343px, 100%)`, maxHeight: idealHeight, }; const tempRightWidth = containerRect.width - (textNodeRect.left - containerRect.left); if (tempRightWidth < idealWidth) { styleConfig.right = 0; } else { styleConfig.left = textNodeRect.left - containerRect.left; } setSearchUserPosition(styleConfig); } }), 500), []); const setSelectUser = (id, name) => { var _a, _b, _c; if (replaceRangeRef.current) { replaceRangeRef.current.deleteContents(); const newMention = document.createElement('mention'); newMention.setAttribute('data-id', id); newMention.setAttribute('data-name', name); // newMention.setAttribute('contenteditable', 'false') newMention.innerText = name; const newPostText = document.createTextNode(' '); replaceRangeRef.current.insertNode(newPostText); replaceRangeRef.current.insertNode(newMention); const editorRoot = (_b = (_a = reactQuillRef.current) === null || _a === void 0 ? void 0 : _a.getEditor()) === null || _b === void 0 ? void 0 : _b.root; editorRoot.focus(); const range = (_c = window.getSelection()) === null || _c === void 0 ? void 0 : _c.getRangeAt(0); range === null || range === void 0 ? void 0 : range.setStart(replaceRangeRef.current.endContainer, replaceRangeRef.current.endOffset); range === null || range === void 0 ? void 0 : range.setEnd(replaceRangeRef.current.endContainer, replaceRangeRef.current.endOffset); } setFoundUsers([]); }; const containerRef = React.useRef(null); const replaceRangeRef = React.useRef(null); const foundUsersListRef = React.useRef(null); const [selectTagUserIndex, setSelectTagUserIndex] = React.useState(0); React.useEffect(() => { // convenient behavior, scroll item into view when select using keyboard if (foundUsersListRef.current) { const parentElement = foundUsersListRef.current; const childElement = parentElement.children.item(selectTagUserIndex); if (childElement) { const containerRect = parentElement.getBoundingClientRect(); const childRect = childElement.getBoundingClientRect(); if (childElement.offsetTop < parentElement.scrollTop) parentElement.scrollTop = childElement.offsetTop; else if (childElement.offsetTop + childRect.height > parentElement.scrollTop + containerRect.height) { parentElement.scrollTop = childElement.offsetTop - containerRect.height + childRect.height; } } } }, [selectTagUserIndex]); React.useEffect(() => { const clickHandler = function (e) { var _a; if (!((_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.contains(e.target))) { // Clicked outside the box setFoundUsers([]); } }; window.addEventListener('click', clickHandler, { passive: true, }); return () => { window.removeEventListener('click', clickHandler); }; }, []); return (React__default["default"].createElement(StyledEditorContainer, { ref: containerRef, className: className, style: style, onKeyDownCapture: (e) => { // convenient behavior, use arrow to select tag user and enter to choose if (foundUsers === null || foundUsers === void 0 ? void 0 : foundUsers.length) { let preventKey = false; if (e.key === 'ArrowDown') { preventKey = true; setSelectTagUserIndex((s) => s + 1 >= foundUsers.length ? 0 : s + 1); } else if (e.key === 'Escape') { preventKey = true; setSelectTagUserIndex(0); setFoundUsers([]); } else if (e.key === 'ArrowUp') { preventKey = true; setSelectTagUserIndex((s) => s - 1 < 0 ? foundUsers.length - 1 : s - 1); } else if (e.key === 'Enter' || e.key === 'Tab') { const user = foundUsers[selectTagUserIndex]; if (user) { preventKey = true; setSelectUser(user.id, user.name); } } if (preventKey) { e.preventDefault(); return false; } } } }, React__default["default"].createElement(CustomToolbar, { id: toolbarId, onAddLink: (text, href) => { var _a, _b; const insertIndex = contentEditableCaretPos.current + 1; const currHTML = (_b = (_a = reactQuillRef.current) === null || _a === void 0 ? void 0 : _a.getEditor()) === null || _b === void 0 ? void 0 : _b.root.innerHTML; const newHTML = ((currHTML === null || currHTML === void 0 ? void 0 : currHTML.substring(0, insertIndex)) || '') + `<mocka data-href="${href}" data-url="true">${text}</mocka> ` + ((currHTML === null || currHTML === void 0 ? void 0 : currHTML.substring(insertIndex)) || ''); onChange === null || onChange === void 0 ? void 0 : onChange(newHTML); debounceInputModify(newHTML); } }), ReactQuill && (React__default["default"].createElement(ReactQuill, { ref: reactQuillRef, theme: "snow", value: html, onChange: (t) => { onChange === null || onChange === void 0 ? void 0 : onChange(t); debounceInputModify(t); handleSelectionChange(); }, onChangeSelection: () => { handleSelectionChange(); }, modules: cachedModules.current, formats: [ 'mocka1', 'mention', 'id', 'key', 'bold', 'italic', 'underline', 'list', 'bullet', ] })), !!(foundUsers === null || foundUsers === void 0 ? void 0 : foundUsers.length) && (React__default["default"].createElement("div", { ref: foundUsersListRef, className: "mention-search-container", style: searchUserPosition }, foundUsers.map((foundUser, index$2) => (React__default["default"].createElement("div", { className: `mention-search-item ${selectTagUserIndex === index$2 ? 'item-selected' : ''}`, key: foundUser.id, onClick: (e) => { e.preventDefault(); e.stopPropagation(); setSelectUser(foundUser.id, foundUser.name); }, onMouseEnter: () => { setSelectTagUserIndex(index$2); } }, React__default["default"].createElement(index.UserAvatar, { className: "search-avatar", username: foundUser.name, avatar: foundUser.avatar }), React__default["default"].createElement("div", null, React__default["default"].createElement(index$1.Text, { size: "p2" }, foundUser.name), React__default["default"].createElement(index$1.Text, { size: "p4", color: "#595959" }, "@", foundUser.username))))))))); }; const CustomToolbar = ({ onAddLink, id, }) => { return (React__default["default"].createElement("div", { id: 'toolbar-' + id, onClick: (e) => { e.preventDefault(); e.stopPropagation(); } }, React__default["default"].createElement("span", { className: "ql-formats" }, React__default["default"].createElement("button", { className: "ql-bold", type: "button" }), React__default["default"].createElement("button", { className: "ql-italic", type: "button" }), React__default["default"].createElement("button", { className: "ql-underline", type: "button" })), React__default["default"].createElement("span", { className: "ql-formats" }, React__default["default"].createElement("button", { className: "ql-list", value: "ordered", type: "button" }), React__default["default"].createElement("button", { className: "ql-list", value: "bullet", type: "button" })), React__default["default"].createElement("span", { className: "ql-formats" }, onAddLink && (React__default["default"].createElement(index$2.PopoverMenu, { className: "link-tool-wrapper", toggleButtonContent: React__default["default"].createElement("img", { className: "link-icon", src: IconLink, loading: "lazy" }), buttonProps: { size: 'md', color: 'transparent', }, align: "end" }, ({ setShow }) => (React__default["default"].createElement(LinkToolInputPopover, { onAddUrl: onAddLink, setShow: setShow }))))))); }; const URL_WARN_MAP = [ 'notification.limitUrlDomain', 'notification.urlWrongFormat', ]; const LinkToolInputPopover = ({ onAddUrl, setShow }) => { const { data: { env: { isMarryBaby }, }, } = React.useContext(togetherComponentGlobalContext.TogetherComponentGlobalContext); const { t } = index$3.useTranslations(); const [urlValue, setUrlValue] = React.useState(''); const [textValue, setTextValue] = React.useState(''); const [urlWarnType, setUrlWarnType] = React.useState(-1); const checkUrlValid = (v) => { const trimmedUrl = post.reformatUrl(v.trim()); try { const jsUrl = new URL(trimmedUrl); if (!jsUrl.protocol.startsWith('http')) { setUrlWarnType(1); return false; } if (!post.ALLOW_DOMAIN_URL.some((o) => jsUrl.origin.endsWith('/' + o) || jsUrl.origin.endsWith('.' + o))) { setUrlWarnType(0); return false; } setUrlWarnType(-1); return true; } catch (_a) { setUrlWarnType(1); return false; } }; const isValid = urlValue.trim() && textValue.trim() && urlWarnType < 0; return (React__default["default"].createElement("div", { className: "link-tool-popover" }, React__default["default"].createElement(index$4.Input, { name: "url", value: urlValue, size: "md", label: "URL", placeholder: t('placeholder.enterUrlHere'), type: "text", className: "link-input-url", onChange: (v) => { setUrlValue(v); checkUrlValid(v); } }), urlWarnType >= 0 && (React__default["default"].createElement(index$1.Text, { size: "c2", className: "error-text" }, t(URL_WARN_MAP[urlWarnType]))), React__default["default"].createElement(index$4.Input, { name: "text", value: textValue, size: "md", label: t('createPost.urlText'), placeholder: t('placeholder.enterUrlTextHere'), type: "text", className: "link-input-text", onChange: (v) => setTextValue(v) }), React__default["default"].createElement("div", { "data-is-marrybaby": isMarryBaby, className: "link-input-button-wrapper" }, React__default["default"].createElement(index$5.Button, { theme: isMarryBaby ? 'marryBaby' : 'helloSites', size: "sm", color: "primary", className: "link-input-button", onClick: () => { if (!isValid) return; onAddUrl(textValue.trim(), post.reformatUrl(urlValue.trim())); setShow && setShow(false); }, isDisabled: !isValid }, t('insert'))))); }; exports.IconLink = IconLink; exports.RichTextEditor = RichTextEditor; exports.StyledCreatePostDescription = StyledCreatePostDescription;