@hhgtech/hhg-components
Version:
Hello Health Group common components
971 lines (950 loc) • 41 kB
JavaScript
import { a as __awaiter } from './tslib.es6-ea4dfe68.js';
import React__default, { useRef, useState, useContext, useEffect, useCallback } from 'react';
import { B as Button } from './index-6351bdee.js';
import { I as Input } from './index-cf3c860d.js';
import { T as Text } from './index-9f5659e8.js';
import { u as useTranslations } from './index-9d21b711.js';
import debounce from 'lodash/debounce';
import { getCookie } from './miscCookieHelper.js';
import { a as useUniqueId } from './useUniqueId-4305c9aa.js';
import { T as TogetherComponentGlobalContext, b as callApiWithAuth, d as getApiPath, q as callApiWithAdminAuth, c as callApi } from './utils-40e61585.js';
import styled from '@emotion/styled';
import { theme } from './miscTheme.js';
import { PATHS, ADMIN_PATHS } from './togetherApiPaths.js';
import { B as BEARER_TOKEN_COOKIE } from './constants-f4091ce6.js';
import { c as reformatUrl, f as fixMalformedMention, r as removeFontFormat, h as highlightBadWords, a as removeEdittedBannedWord, w as wrapAnchorAroundUrls, g as getUrlsFromEditorString, A as ALLOW_DOMAIN_URL, e as escapeRegExp, i as getMentionIdsFromString, L as LIMIT_MENTION } from './post-44f3801c.js';
import { U as UserAvatar } from './index-e9423cb7.js';
import { P as PopoverMenu } from './index-8d014768.js';
const StyledCreatePostDescription = styled.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 ${theme.colors.red600};
}
}
.textarea-control {
position: relative;
border: 1px solid ${theme.colors.gray200};
border-radius: ${theme.borderRadius};
&.--focus {
border-color: ${theme.colors.primaryBase};
box-shadow: 0px 0px 2px 2px ${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 ${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: ${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: ${theme.colors.gray600};
/* font-family: 'Open Sans', sans-serif; */
font-size: 13px;
font-weight: bold;
}
.count-string {
margin-top: 5px;
color: ${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.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 ${theme.mbColors.pink};
background-color: ${theme.mbColors.pink};
&:hover:not(:disabled) {
border: solid 2px ${theme.mbColors.pink};
background-color: ${theme.colors.white};
color: ${theme.mbColors.pink};
}
}
}
}
.link-input-button {
width: auto;
height: auto;
padding: 0.4rem 1rem;
background-color: ${theme.colors.primaryBase};
&:disabled {
border-color: #bfbfbf;
background-color: #bfbfbf;
color: #fff;
cursor: not-allowed;
}
&:hover:not(:disabled) {
background-color: ${theme.colors.primaryHover};
}
}
.error-text {
padding: 0;
margin-bottom: 0.5rem;
font-weight: 600;
color: ${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: ${theme.borderRadius};
color: ${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: ${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: ${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: ${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;
(() => __awaiter(void 0, void 0, void 0, function* () {
if (typeof window === 'undefined')
return;
const QuillRes = yield import('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(/(\ )+/g, ' ')) === null || _b === void 0 ? void 0 : _b.replace(/(\u200c|‌)+/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(/(\ )+/g, ' ')) === null || _b === void 0 ? void 0 : _b.replace(/(\u200c|‌)+/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, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''').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 = fixMalformedMention(removeFontFormat(value));
if (!formatUrl)
return {
value: highlightBadWords(removedFormat, bannedWords),
};
let descriptionWithWrappedAnchor = removeEdittedBannedWord(wrapAnchorAroundUrls(removedFormat));
const urls = getUrlsFromEditorString(removedFormat);
if (urls) {
const invalidUrls = [];
let previewUrl = '';
urls.forEach((url) => {
if (!url)
return;
try {
const jsUrl = new URL(url);
if (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(escapeRegExp).join('|')})<`, 'gi'), 'data-error="true" data-url="true">$1<')
.replace(new RegExp(`data-href="(${invalidUrls
.map(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: highlightBadWords(descriptionWithWrappedAnchor, bannedWords),
};
}
return {
value: highlightBadWords(descriptionWithWrappedAnchor, bannedWords),
};
};
const RichTextEditor = ({ html, onChange, className, style, onImagePickerClick, bannedWords = [], onInvalidUrlsChange, onPreviewUrlChange, setEditorRef, isReplying, }) => {
var _a, _b, _c, _d;
const bannedWordsRef = useRef([]);
bannedWordsRef.current = bannedWords;
const [invalidUrls, setInvalidUrls] = useState([]);
const [previewUrl, setPreviewUrl] = useState(null);
const { data: { locale }, } = useContext(TogetherComponentGlobalContext);
const bannedString = (bannedWords === null || bannedWords === void 0 ? void 0 : bannedWords.join(',')) || '';
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,
});
callApi(getApiPath(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 = useRef(fetchPreviewData);
fetchPreviewDataRef.current = fetchPreviewData;
const debounceInputModify = useCallback(debounce((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), []);
useEffect(() => {
onInvalidUrlsChange && onInvalidUrlsChange(invalidUrls);
}, [invalidUrls]);
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 = useRef(onImagePickerClick);
imageHandlerRef.current = onImagePickerClick;
const toolbarId = useUniqueId();
const cachedModules = 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 = useRef(-1);
const reactQuillRef = 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 = getMentionIdsFromString(currHTML, true);
if (mentionIds.length < 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] = useState([]);
const [searchUserPosition, setSearchUserPosition] = useState({
top: 0,
left: 0,
});
const searchUser = useCallback(debounce((name, rect, excludeIds) => __awaiter(void 0, void 0, void 0, function* () {
var _e, _f, _g, _h;
if (!name || !DOMRect) {
return setFoundUsers([]);
}
const res = yield ((((_e = getCookie(BEARER_TOKEN_COOKIE)) === null || _e === void 0 ? void 0 : _e.length) || 0) > 0
? callApiWithAuth(getApiPath(PATHS.SEARCH_USER, {
_locale: locale,
name: name,
}), 'get')
: callApiWithAdminAuth(getApiPath(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 = useRef(null);
const replaceRangeRef = useRef(null);
const foundUsersListRef = useRef(null);
const [selectTagUserIndex, setSelectTagUserIndex] = useState(0);
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]);
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.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.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.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.createElement("div", { ref: foundUsersListRef, className: "mention-search-container", style: searchUserPosition }, foundUsers.map((foundUser, index) => (React__default.createElement("div", { className: `mention-search-item ${selectTagUserIndex === index ? 'item-selected' : ''}`, key: foundUser.id, onClick: (e) => {
e.preventDefault();
e.stopPropagation();
setSelectUser(foundUser.id, foundUser.name);
}, onMouseEnter: () => {
setSelectTagUserIndex(index);
} },
React__default.createElement(UserAvatar, { className: "search-avatar", username: foundUser.name, avatar: foundUser.avatar }),
React__default.createElement("div", null,
React__default.createElement(Text, { size: "p2" }, foundUser.name),
React__default.createElement(Text, { size: "p4", color: "#595959" },
"@",
foundUser.username)))))))));
};
const CustomToolbar = ({ onAddLink, id, }) => {
return (React__default.createElement("div", { id: 'toolbar-' + id, onClick: (e) => {
e.preventDefault();
e.stopPropagation();
} },
React__default.createElement("span", { className: "ql-formats" },
React__default.createElement("button", { className: "ql-bold", type: "button" }),
React__default.createElement("button", { className: "ql-italic", type: "button" }),
React__default.createElement("button", { className: "ql-underline", type: "button" })),
React__default.createElement("span", { className: "ql-formats" },
React__default.createElement("button", { className: "ql-list", value: "ordered", type: "button" }),
React__default.createElement("button", { className: "ql-list", value: "bullet", type: "button" })),
React__default.createElement("span", { className: "ql-formats" }, onAddLink && (React__default.createElement(PopoverMenu, { className: "link-tool-wrapper", toggleButtonContent: React__default.createElement("img", { className: "link-icon", src: IconLink, loading: "lazy" }), buttonProps: {
size: 'md',
color: 'transparent',
}, align: "end" }, ({ setShow }) => (React__default.createElement(LinkToolInputPopover, { onAddUrl: onAddLink, setShow: setShow })))))));
};
const URL_WARN_MAP = [
'notification.limitUrlDomain',
'notification.urlWrongFormat',
];
const LinkToolInputPopover = ({ onAddUrl, setShow }) => {
const { data: { env: { isMarryBaby }, }, } = useContext(TogetherComponentGlobalContext);
const { t } = useTranslations();
const [urlValue, setUrlValue] = useState('');
const [textValue, setTextValue] = useState('');
const [urlWarnType, setUrlWarnType] = useState(-1);
const checkUrlValid = (v) => {
const trimmedUrl = reformatUrl(v.trim());
try {
const jsUrl = new URL(trimmedUrl);
if (!jsUrl.protocol.startsWith('http')) {
setUrlWarnType(1);
return false;
}
if (!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.createElement("div", { className: "link-tool-popover" },
React__default.createElement(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.createElement(Text, { size: "c2", className: "error-text" }, t(URL_WARN_MAP[urlWarnType]))),
React__default.createElement(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.createElement("div", { "data-is-marrybaby": isMarryBaby, className: "link-input-button-wrapper" },
React__default.createElement(Button, { theme: isMarryBaby ? 'marryBaby' : 'helloSites', size: "sm", color: "primary", className: "link-input-button", onClick: () => {
if (!isValid)
return;
onAddUrl(textValue.trim(), reformatUrl(urlValue.trim()));
setShow && setShow(false);
}, isDisabled: !isValid }, t('insert')))));
};
export { IconLink as I, RichTextEditor as R, StyledCreatePostDescription as S };