UNPKG

phx-react

Version:

PHX REACT

918 lines 48.5 kB
"use strict"; 'use client'; exports.__esModule = true; var tslib_1 = require("tslib"); var react_1 = tslib_1.__importDefault(require("react")); var react_2 = require("react"); var react_3 = require("@tiptap/react"); var starter_kit_1 = tslib_1.__importDefault(require("@tiptap/starter-kit")); var extension_underline_1 = tslib_1.__importDefault(require("@tiptap/extension-underline")); var extension_image_1 = tslib_1.__importDefault(require("@tiptap/extension-image")); var extension_heading_1 = tslib_1.__importDefault(require("@tiptap/extension-heading")); var extension_link_1 = tslib_1.__importDefault(require("@tiptap/extension-link")); var extension_text_align_1 = tslib_1.__importDefault(require("@tiptap/extension-text-align")); var extension_text_style_1 = require("@tiptap/extension-text-style"); var extension_color_1 = tslib_1.__importDefault(require("@tiptap/extension-color")); var react_colorful_1 = require("react-colorful"); var editor_constant_1 = require("./editor.constant"); require("katex/dist/katex.min.css"); var react_hook_form_1 = require("react-hook-form"); var Modal_1 = require("../Modal/Modal"); var Input_1 = require("../Input/Input"); var EditorStyle_1 = tslib_1.__importDefault(require("./styles/EditorStyle")); var emoji_picker_react_1 = tslib_1.__importStar(require("emoji-picker-react")); var extension_placeholder_1 = tslib_1.__importDefault(require("@tiptap/extension-placeholder")); var FormUpload_1 = require("../UploadFile/FormUpload"); var Image = extension_image_1["default"].extend({ addAttributes: function () { var _a; return tslib_1.__assign(tslib_1.__assign({}, (_a = this.parent) === null || _a === void 0 ? void 0 : _a.call(this)), { 'data-loading': { "default": null, renderHTML: function (attributes) { if (attributes['data-loading']) { return { 'data-loading': attributes['data-loading'] }; } return {}; } }, 'data-temp-id': { "default": null, renderHTML: function (attributes) { if (attributes['data-temp-id']) { return { 'data-temp-id': attributes['data-temp-id'] }; } return {}; } } }); } }); function TextEditor(_a) { var _this = this; var _b; var initialData = _a.initialData, onChange = _a.onChange, apiCdnUpload = _a.apiCdnUpload, height = _a.height, label = _a.label, placeholder = _a.placeholder; var _c = (0, react_2.useState)(null), editor = _c[0], setEditor = _c[1]; var _d = (0, react_2.useState)(0), forceUpdate = _d[1]; var _e = (0, react_2.useState)(false), showColorPicker = _e[0], setShowColorPicker = _e[1]; var _f = (0, react_2.useState)('#333333'), fontColor = _f[0], setFontColor = _f[1]; var pickerRef = (0, react_2.useRef)(null); var toolbarRef = (0, react_2.useRef)(null); var emojiPickerRef = (0, react_2.useRef)(null); var _g = (0, react_2.useState)(''), fontSizeInput = _g[0], setFontSizeInput = _g[1]; var _h = (0, react_2.useState)(false), isEditingFontSize = _h[0], setIsEditingFontSize = _h[1]; var _j = (0, react_2.useState)({ left: 0, top: 0 }), pickerPos = _j[0], setPickerPos = _j[1]; var _k = (0, react_2.useState)({ left: 0, top: 0 }), emojiPickerPos = _k[0], setEmojiPickerPos = _k[1]; var _l = (0, react_2.useState)(false), showLinkInput = _l[0], setShowLinkInput = _l[1]; var _m = (0, react_2.useState)(new Set()), disabledItems = _m[0], setDisabledItems = _m[1]; var _o = (0, react_2.useState)(false), showEmojiPicker = _o[0], setShowEmojiPicker = _o[1]; var _p = (0, react_2.useState)(false), openPdfModal = _p[0], setOpenPdfModal = _p[1]; var _q = (0, react_2.useState)(null), pdfFile = _q[0], setPdfFile = _q[1]; var _r = (0, react_hook_form_1.useForm)({ defaultValues: { link: '' } }), errors = _r.formState.errors, register = _r.register, handleSubmit = _r.handleSubmit, reset = _r.reset; var _s = (0, react_2.useState)(false), loadingConvertPdf = _s[0], setLoadingConvertPdf = _s[1]; // eslint-disable-next-line no-useless-escape var regexUrl = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/; var normalizeColor = function (val) { try { if (val.startsWith('#')) { return val; } if (val.startsWith('rgba')) { var _a = val .replace(/rgba\(/, '') .replace(/\)/, '') .split(',') .map(function (v) { return v.trim(); }), r = _a[0], g = _a[1], b = _a[2], a = _a[3]; var rn = parseInt(r, 10).toString(16).padStart(2, '0'); var gn = parseInt(g, 10).toString(16).padStart(2, '0'); var bn = parseInt(b, 10).toString(16).padStart(2, '0'); var an = Math.round(parseFloat(a) * 255) .toString(16) .padStart(2, '0'); return "#".concat(rn).concat(gn).concat(bn).concat(an); } if (val.startsWith('rgb')) { var _b = val .replace(/rgb\(/, '') .replace(/\)/, '') .split(',') .map(function (v) { return v.trim(); }), r = _b[0], g = _b[1], b = _b[2]; var rn = parseInt(r, 10).toString(16).padStart(2, '0'); var gn = parseInt(g, 10).toString(16).padStart(2, '0'); var bn = parseInt(b, 10).toString(16).padStart(2, '0'); return "#".concat(rn).concat(gn).concat(bn); } return val; } catch (_c) { return val; } }; var getCurrentSelectionFontSize = (0, react_2.useCallback)(function () { var _a; try { var attrSize = (_a = editor === null || editor === void 0 ? void 0 : editor.getAttributes('textStyle')) === null || _a === void 0 ? void 0 : _a.fontSize; if (attrSize) return attrSize; var sel = window.getSelection(); var node = sel === null || sel === void 0 ? void 0 : sel.anchorNode; var el = null; if (node) { el = node.nodeType === 1 ? node : node.parentElement; } if (el) { var size = window.getComputedStyle(el).fontSize; if (size) return size; } return ''; } catch (_b) { return ''; } }, [editor]); var getCurrentSelectionColor = function () { var _a; try { var attrColor = (_a = editor === null || editor === void 0 ? void 0 : editor.getAttributes('textStyle')) === null || _a === void 0 ? void 0 : _a.color; if (attrColor) { return normalizeColor(attrColor); } var sel = window.getSelection(); var node = sel === null || sel === void 0 ? void 0 : sel.anchorNode; var el = null; if (node) { el = node.nodeType === 1 ? node : node.parentElement; } if (el) { var color = window.getComputedStyle(el).color; if (color) return normalizeColor(color); } return null; } catch (_b) { return null; } }; var uploadImageFunction = function (formData) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var response; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, fetch(apiCdnUpload, { method: 'POST', body: formData })]; case 1: response = _a.sent(); if (response.ok) { return [2 /*return*/, response.json()]; } return [2 /*return*/]; } }); }); }; var handleConvertPdfToImage = function (file) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var PDFJS, fileURL, pdfDoc_1, numPages, uploadTasks, _loop_1, i, results, BATCH_SIZE, totalUploadTasks, i, batch, batchResults, sorted; var _this = this; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: setLoadingConvertPdf(true); _a.label = 1; case 1: _a.trys.push([1, , 7, 8]); PDFJS = window.pdfjsLib; fileURL = URL.createObjectURL(file); return [4 /*yield*/, PDFJS.getDocument({ url: fileURL }).promise]; case 2: pdfDoc_1 = _a.sent(); numPages = pdfDoc_1.numPages; uploadTasks = []; _loop_1 = function (i) { uploadTasks.push(function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { var page, viewport, canvas, context, blob, fileName, imageFile, link; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, pdfDoc_1.getPage(i)]; case 1: page = _a.sent(); viewport = page.getViewport({ scale: 2 }); canvas = document.createElement('canvas'); context = canvas.getContext('2d'); canvas.width = viewport.width; canvas.height = viewport.height; return [4 /*yield*/, page.render({ canvasContext: context, viewport: viewport }).promise]; case 2: _a.sent(); return [4 /*yield*/, new Promise(function (resolve) { return canvas.toBlob(function (b) { return resolve(b); }, 'image/webp'); })]; case 3: blob = _a.sent(); fileName = "".concat(file.name.replace(/\.[^/.]+$/, ''), "-page-").concat(i, ".webp"); imageFile = new File([blob], fileName, { type: 'image/webp' }); return [4 /*yield*/, uploadImage(imageFile)]; case 4: link = _a.sent(); return [2 /*return*/, { index: i, link: link }]; } }); }); }); }; for (i = 1; i <= numPages; i++) { _loop_1(i); } results = []; BATCH_SIZE = 5; totalUploadTasks = uploadTasks.length; i = 0; _a.label = 3; case 3: if (!(i < totalUploadTasks)) return [3 /*break*/, 6]; batch = uploadTasks.slice(i, i + BATCH_SIZE); return [4 /*yield*/, Promise.all(batch.map(function (fn) { return fn(); }))]; case 4: batchResults = _a.sent(); results.push.apply(results, batchResults); _a.label = 5; case 5: i += BATCH_SIZE; return [3 /*break*/, 3]; case 6: sorted = results.filter(function (r) { return r.link; }).sort(function (a, b) { return a.index - b.index; }); editor === null || editor === void 0 ? void 0 : editor.chain().focus(); sorted.forEach(function (_a) { var link = _a.link; editor === null || editor === void 0 ? void 0 : editor.chain().insertContentAt(editor.state.doc.content.size, { type: 'image', attrs: { src: link } }).run(); }); return [3 /*break*/, 8]; case 7: setLoadingConvertPdf(false); setOpenPdfModal(false); setPdfFile(null); return [7 /*endfinally*/]; case 8: return [2 /*return*/]; } }); }); }; (0, react_2.useEffect)(function () { var e = new react_3.Editor({ content: initialData, extensions: [ extension_placeholder_1["default"].configure({ placeholder: placeholder }), extension_text_style_1.TextStyle, extension_text_style_1.FontSize, extension_color_1["default"], starter_kit_1["default"].configure({ heading: false }), extension_underline_1["default"], Image, extension_heading_1["default"].configure({ levels: [1, 2, 3, 4] }), extension_link_1["default"].configure({ openOnClick: false }), extension_text_align_1["default"].configure({ types: ['heading', 'paragraph'] }), ], editorProps: { attributes: { "class": "prose editor-height px-3 py-1.5 focus:outline-none border border-t-0 border-gray-300 rounded-b-md bg-white overflow-y-auto" } } }); e.on('transaction', function () { forceUpdate(function (n) { return n + 1; }); }); e.on('update', function (_a) { var _b; var editor = _a.editor; onChange === null || onChange === void 0 ? void 0 : onChange((_b = editor.getHTML()) !== null && _b !== void 0 ? _b : ''); }); setEditor(e); return function () { e.destroy(); }; }, []); (0, react_2.useEffect)(function () { if (!editor) return; var updateDisabledItems = function () { var disabled = new Set(); if (editor.isActive('blockquote')) { disabled.add('Văn bản'); } if (editor.isActive('heading')) { disabled.add('Trích dẫn'); } setDisabledItems(disabled); }; updateDisabledItems(); editor.on('selectionUpdate', updateDisabledItems); editor.on('transaction', updateDisabledItems); return function () { editor.off('selectionUpdate', updateDisabledItems); editor.off('transaction', updateDisabledItems); }; }, [editor]); (0, react_2.useEffect)(function () { if (!editor) return; var handlePaste = function (event) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var items, _loop_2, i, state_1; var _a; return tslib_1.__generator(this, function (_b) { items = (_a = event.clipboardData) === null || _a === void 0 ? void 0 : _a.items; if (!items) return [2 /*return*/]; event.preventDefault(); _loop_2 = function (i) { var item = items[i]; if (item.type.indexOf('image') !== -1) { var file = item.getAsFile(); if (!file) return "continue"; var tempId_1 = "img-".concat(Date.now()); var tempUrl_1 = URL.createObjectURL(file); try { editor === null || editor === void 0 ? void 0 : editor.chain().focus().insertContent({ type: 'image', attrs: { src: tempUrl_1, 'data-loading': 'true', 'data-temp-id': tempId_1 } }).run(); uploadImage(file) .then(function (res) { if (!res) throw new Error('Upload failed'); editor === null || editor === void 0 ? void 0 : editor.chain().command(function (_a) { var tr = _a.tr; var targetPos = null; tr.doc.descendants(function (node, pos) { if (node.type.name === 'image' && node.attrs['data-temp-id'] === tempId_1) { targetPos = pos; return false; } return true; }); if (targetPos !== null) { var node = tr.doc.nodeAt(targetPos); if (node) { tr.setNodeMarkup(targetPos, undefined, tslib_1.__assign(tslib_1.__assign({}, node.attrs), { src: res, 'data-loading': null, 'data-temp-id': null })); return true; } } return false; }).run(); })["catch"](function (error) { console.error('Failed to upload pasted image:', error); editor === null || editor === void 0 ? void 0 : editor.chain().command(function (_a) { var tr = _a.tr; var targetPos = null; tr.doc.descendants(function (node, pos) { if (node.type.name === 'image' && node.attrs['data-temp-id'] === tempId_1) { targetPos = pos; return false; } return true; }); if (targetPos !== null) { tr["delete"](targetPos, targetPos + 1); } return true; }).run(); })["finally"](function () { URL.revokeObjectURL(tempUrl_1); }); } catch (error) { console.error('Error handling pasted image:', error); URL.revokeObjectURL(tempUrl_1); } return "break"; } }; for (i = 0; i < items.length; i++) { state_1 = _loop_2(i); if (state_1 === "break") break; } return [2 /*return*/]; }); }); }; var dom = editor.view.dom; dom.addEventListener('paste', handlePaste); return function () { dom.removeEventListener('paste', handlePaste); }; }, [editor, getCurrentSelectionFontSize]); (0, react_2.useEffect)(function () { if (!showColorPicker) return; var onDocMouseDown = function (e) { var node = pickerRef.current; var target = e.target; var isColorButton = target.closest('[data-color-button]'); var isColorPicker = node === null || node === void 0 ? void 0 : node.contains(target); if (node && !isColorPicker && !isColorButton) { setShowColorPicker(false); } }; document.addEventListener('mousedown', onDocMouseDown); return function () { document.removeEventListener('mousedown', onDocMouseDown); }; }, [showColorPicker]); (0, react_2.useEffect)(function () { if (!showEmojiPicker) return; var onDocMouseDown = function (e) { var node = emojiPickerRef.current; var target = e.target; var isEmojiPicker = node === null || node === void 0 ? void 0 : node.contains(target); if (node && !isEmojiPicker) { setShowEmojiPicker(false); } }; document.addEventListener('mousedown', onDocMouseDown); return function () { document.removeEventListener('mousedown', onDocMouseDown); }; }, [showEmojiPicker]); (0, react_2.useEffect)(function () { if (!editor || !showColorPicker) return; var updateFromSelection = function () { var current = getCurrentSelectionColor(); if (current) setFontColor(current); }; updateFromSelection(); editor.on('selectionUpdate', updateFromSelection); return function () { editor.off('selectionUpdate', updateFromSelection); }; }, [editor, showColorPicker]); (0, react_2.useEffect)(function () { if (!editor) return; var onSelection = function () { if (!isEditingFontSize) { var cur = getCurrentSelectionFontSize(); var m = cur.match(/^(\d+(?:\.\d+)?)/); setFontSizeInput(m ? m[1] : ''); } forceUpdate(function (n) { return n + 1; }); }; editor.on('selectionUpdate', onSelection); return function () { editor.off('selectionUpdate', onSelection); }; }, [editor, isEditingFontSize, getCurrentSelectionFontSize]); (0, react_2.useEffect)(function () { if (!window.pdfjsLib) { var script = document.createElement('script'); script.src = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js'; document.body.appendChild(script); } }, []); (0, react_2.useEffect)(function () { if (!editor) return; var cur = getCurrentSelectionFontSize(); var m = cur.match(/^(\d+(?:\.\d+)?)/); setFontSizeInput(m ? m[1] : ''); }, [editor]); if (!editor) return react_1["default"].createElement("p", null, "Loading editor..."); var addLink = function () { setShowLinkInput(true); }; var handleInsertLink = handleSubmit(function (data) { var link = data.link; editor === null || editor === void 0 ? void 0 : editor.chain().focus().extendMarkRange('link').setLink({ href: link }).run(); setShowLinkInput(false); reset(); }); var onMouseDownToolbar = function (e, item) { var _a; e.preventDefault(); var container = toolbarRef.current; var btnRect = e.currentTarget.getBoundingClientRect(); var contRect = container === null || container === void 0 ? void 0 : container.getBoundingClientRect(); if (item.name === 'Màu chữ') { if (contRect) { setPickerPos({ left: btnRect.left - contRect.left - 90, top: btnRect.bottom - contRect.top + 8 }); } var current = getCurrentSelectionColor(); if (current) setFontColor(current); setShowColorPicker(!showColorPicker); forceUpdate(function (x) { return x + 1; }); } else if (item.name === 'Biểu tượng cảm xúc') { if (contRect) { setEmojiPickerPos({ left: btnRect.left - contRect.left - 170, top: btnRect.bottom - contRect.top + 8 }); } setShowEmojiPicker(!showEmojiPicker); e.stopPropagation(); } else { (_a = item.action) === null || _a === void 0 ? void 0 : _a.call(item); forceUpdate(function (x) { return x + 1; }); } }; var fontSizeOptions = [ { label: '12', value: '12px' }, { label: '14', value: '14px' }, { label: '16', value: '16px' }, { label: '18', value: '18px' }, { label: '20', value: '20px' }, { label: '24', value: '24px' }, { label: '28', value: '28px' }, { label: '32', value: '32px' }, { label: '36', value: '36px' }, { label: '40', value: '40px' }, { label: '44', value: '44px' }, { label: '48', value: '48px' }, { label: '52', value: '52px' }, { label: '56', value: '56px' }, { label: '60', value: '60px' }, { label: '64', value: '64px' }, { label: '72', value: '72px' }, { label: '80', value: '80px' }, { label: '96', value: '96px' }, ]; var paragraphOptions = [ { label: 'Văn bản', value: 0 }, { label: 'Tiêu đề 1', value: 1 }, { label: 'Tiêu đề 2', value: 2 }, { label: 'Tiêu đề 3', value: 3 }, { label: 'Tiêu đề 4', value: 4 }, ]; var toolbarItems = [ { name: 'Văn bản', isDisabled: function () { return disabledItems.has('Văn bản'); }, component: function () { return (react_1["default"].createElement("select", { className: "w-24 text-xs leading-5 focus:ring-0 bg-transparent border-none rounded-md py-1 px-2 duration-200 ".concat(disabledItems.has('Văn bản') ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:bg-gray-200'), disabled: disabledItems.has('Văn bản'), onChange: function (e) { var level = Number(e.target.value); if (level === 0) { editor.chain().focus().setParagraph().run(); } else { //@ts-expect-error: nothing editor.chain().focus().setHeading({ level: level }).run(); } }, value: (function () { if (editor.isActive('heading', { level: 1 })) return 1; if (editor.isActive('heading', { level: 2 })) return 2; if (editor.isActive('heading', { level: 3 })) return 3; if (editor.isActive('heading', { level: 4 })) return 4; return 0; })() }, paragraphOptions.map(function (option) { return (react_1["default"].createElement("option", { key: option.value, value: option.value }, option.label)); }))); }, borderRight: true }, { name: 'Giảm cỡ chữ', action: function () { var currentSize = parseInt(fontSizeInput) || 12; var newSize = Math.max(1, currentSize - 1); setFontSizeInput(String(newSize)); editor.chain().focus().setFontSize("".concat(newSize, "px")).run(); forceUpdate(function (x) { return x + 1; }); }, icon: editor_constant_1.toolbar_svg.minus }, { name: 'Cỡ chữ', component: function () { return (react_1["default"].createElement("div", { className: 'relative flex items-center' }, react_1["default"].createElement("input", { type: 'number', className: 'w-10 p-0 text-xs leading-5 text-center bg-transparent bg-white border-gray-500 rounded-md focus:ring-0 focus:border-gray-500', value: fontSizeInput || isEditingFontSize ? fontSizeInput : 12, onFocus: function () { setIsEditingFontSize(true); var cur = getCurrentSelectionFontSize(); var m = cur.match(/^(\d+(?:\.\d+)?)/); setFontSizeInput(m ? m[1] : ''); }, onChange: function (e) { setFontSizeInput(e.target.value); }, onBlur: function (e) { requestAnimationFrame(function () { return setIsEditingFontSize(false); }); var raw = e.target.value.trim(); if (!raw) { setFontSizeInput(''); editor.chain().focus().setMark('textStyle', { fontSize: '12px' }).run(); } else { var num = Number(raw); if (!Number.isNaN(num) && num > 0) { num = Math.min(500, num); editor.chain().focus().setFontSize("".concat(num, "px")).run(); setFontSizeInput(num.toString()); } else { setFontSizeInput('12'); editor.chain().focus().setFontSize('12px').run(); } } }, onKeyDown: function (e) { if (e.key === 'Enter') { // eslint-disable-next-line @typescript-eslint/no-extra-semi ; e.target.blur(); } } }), isEditingFontSize && (react_1["default"].createElement("div", { className: 'absolute left-0 top-[110%] z-50 w-16 max-h-60 overflow-auto rounded-md border bg-white shadow' }, fontSizeOptions.map(function (option) { return (react_1["default"].createElement("button", { key: option.value, type: 'button', className: 'w-full px-2 py-1 text-left text-[12px] hover:bg-gray-100', onMouseDown: function (e) { e.preventDefault(); var numMatch = option.value.match(/^(\d+(?:\.\d+)?)/); setFontSizeInput(numMatch ? numMatch[1] : ''); editor.chain().focus().setFontSize(option.value).run(); setIsEditingFontSize(false); } }, option.label)); }))))); } }, { name: 'Tăng cỡ chữ', icon: editor_constant_1.toolbar_svg.plus, borderRight: true, action: function () { var currentSize = parseInt(fontSizeInput) || 12; var newSize = Math.min(500, currentSize + 1); setFontSizeInput(String(newSize)); editor.chain().focus().setFontSize("".concat(newSize, "px")).run(); forceUpdate(function (x) { return x + 1; }); } }, { name: 'In đậm', icon: editor_constant_1.toolbar_svg.bold, isActive: function () { return editor.isActive('bold'); }, action: function () { return editor.chain().focus().toggleBold().run(); } }, { name: 'In nghiêng', icon: editor_constant_1.toolbar_svg.italic, isActive: function () { return editor.isActive('italic'); }, action: function () { return editor.chain().focus().toggleItalic().run(); } }, { name: 'Gạch chân', icon: editor_constant_1.toolbar_svg.underline, isActive: function () { return editor.isActive('underline'); }, action: function () { return editor.chain().focus().toggleUnderline().run(); } }, { name: 'Màu chữ', icon: editor_constant_1.toolbar_svg.textColor, action: function () { return setShowColorPicker(function (s) { return !s; }); }, isActive: function () { return showColorPicker; }, borderRight: true }, { name: 'Căn trái', icon: editor_constant_1.toolbar_svg.alignLeft, action: function () { return editor.chain().focus().setTextAlign('left').run(); }, isActive: function () { var isLeft = editor.isActive({ textAlign: 'left' }); var isCenter = editor.isActive({ textAlign: 'center' }); var isRight = editor.isActive({ textAlign: 'right' }); var isJustify = editor.isActive({ textAlign: 'justify' }); var noAlign = !isLeft && !isCenter && !isRight && !isJustify; return isLeft || noAlign; } }, { name: 'Căn giữa', icon: editor_constant_1.toolbar_svg.alignMiddle, action: function () { return editor.chain().focus().setTextAlign('center').run(); }, isActive: function () { return editor.isActive({ textAlign: 'center' }); } }, { name: 'Căn phải', icon: editor_constant_1.toolbar_svg.alignRight, action: function () { return editor.chain().focus().setTextAlign('right').run(); }, isActive: function () { return editor.isActive({ textAlign: 'right' }); } }, { name: 'Căn đều', icon: editor_constant_1.toolbar_svg.alignJustify, action: function () { return editor.chain().focus().setTextAlign('justify').run(); }, isActive: function () { return editor.isActive({ textAlign: 'justify' }); }, borderRight: true }, { name: 'Trích dẫn', icon: editor_constant_1.toolbar_svg.quotes, isActive: function () { return editor.isActive('blockquote'); }, isDisabled: function () { return disabledItems.has('Trích dẫn'); }, action: function () { return editor.chain().focus().toggleBlockquote().run(); } }, { name: 'Danh sách không thứ tự', icon: editor_constant_1.toolbar_svg.unorderedList, isActive: function () { return editor.isActive('bulletList'); }, action: function () { return editor.chain().focus().toggleBulletList().run(); } }, { name: 'Danh sách có thứ tự', icon: editor_constant_1.toolbar_svg.orderList, isActive: function () { return editor.isActive('orderedList'); }, action: function () { return editor.chain().focus().toggleOrderedList().run(); }, borderRight: true }, { name: 'PDF', icon: editor_constant_1.toolbar_svg.pdf, action: function () { return setOpenPdfModal(true); } }, { name: 'Hình ảnh', icon: editor_constant_1.toolbar_svg.image, action: function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { var input; var _this = this; return tslib_1.__generator(this, function (_a) { input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.onchange = function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { var file, tempId_2, res_1; var _a; return tslib_1.__generator(this, function (_b) { switch (_b.label) { case 0: file = (_a = input.files) === null || _a === void 0 ? void 0 : _a[0]; if (!file) return [3 /*break*/, 2]; tempId_2 = "img-".concat(Date.now()); editor === null || editor === void 0 ? void 0 : editor.chain().focus().insertContent({ type: 'image', attrs: { src: URL.createObjectURL(file), 'data-loading': 'true', 'data-temp-id': tempId_2 } }).setTextSelection(editor.state.selection.to + 1).run(); return [4 /*yield*/, uploadImage(file)]; case 1: res_1 = _b.sent(); if (res_1) { editor === null || editor === void 0 ? void 0 : editor.chain().command(function (_a) { var tr = _a.tr; var targetPos = null; tr.doc.descendants(function (node, pos) { if (node.type.name === 'image' && node.attrs['data-temp-id'] === tempId_2) { targetPos = pos; return false; } return true; }); if (targetPos !== null) { var node = tr.doc.nodeAt(targetPos); if (node) { tr.setNodeMarkup(targetPos, undefined, tslib_1.__assign(tslib_1.__assign({}, node.attrs), { src: res_1, 'data-loading': null, 'data-temp-id': null })); } } return true; }).run(); } else { editor === null || editor === void 0 ? void 0 : editor.chain().command(function () { var _a; var node = document.querySelector("img[data-temp-id=\"".concat(tempId_2, "\"]")); if (node) { (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(node); } return true; }).run(); } _b.label = 2; case 2: return [2 /*return*/]; } }); }); }; input.click(); return [2 /*return*/]; }); }); } }, { name: 'Đường dẫn', icon: editor_constant_1.toolbar_svg.link, isActive: function () { return editor.isActive('link'); }, action: addLink, borderRight: true }, { name: 'Biểu tượng cảm xúc', icon: editor_constant_1.toolbar_svg.emoji, action: function () { return setShowEmojiPicker(!showEmojiPicker); } }, ]; var uploadImage = function (file) { return tslib_1.__awaiter(_this, void 0, void 0, function () { var formData, res, error_1; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); formData = new FormData(); formData.append('file', file); formData.append('projectId', 'test'); formData.append('moduleId', 'test'); return [4 /*yield*/, uploadImageFunction(formData)]; case 1: res = _a.sent(); if (res) { return [2 /*return*/, res.link]; } else { return [2 /*return*/, null]; } return [3 /*break*/, 3]; case 2: error_1 = _a.sent(); console.error(error_1); return [2 /*return*/, null]; case 3: return [2 /*return*/]; } }); }); }; return (react_1["default"].createElement("div", { className: 'phx-editor' }, react_1["default"].createElement(EditorStyle_1["default"], { editorHeight: height }), label && react_1["default"].createElement("label", { className: 'block mb-1 text-xs font-normal text-gray-700' }, label), react_1["default"].createElement("div", { className: 'sticky top-0 z-30' }, react_1["default"].createElement("div", { ref: toolbarRef, className: 'flex flex-wrap gap-1 p-2 bg-gray-100 border border-gray-300 rounded-t-md' }, toolbarItems.map(function (item) { var _a, _b, _c, _d; (_a = item.isActive) === null || _a === void 0 ? void 0 : _a.call(item); return item.component ? (react_1["default"].createElement("div", { className: 'flex items-center gap-x-2' }, item.component(), item.borderRight && react_1["default"].createElement("div", { className: 'w-[1px] h-5 bg-gray-300' }))) : (react_1["default"].createElement("div", { className: 'flex items-center gap-x-2' }, react_1["default"].createElement("button", { key: item.name, type: 'button', "data-color-button": 'true', onMouseDown: function (e) { return onMouseDownToolbar(e, item); }, className: "p-1 rounded-md duration-200 ".concat(((_b = item.isActive) === null || _b === void 0 ? void 0 : _b.call(item)) ? 'bg-gray-200' : '', " ").concat(((_c = item.isDisabled) === null || _c === void 0 ? void 0 : _c.call(item)) ? 'opacity-50 cursor-not-allowed' : 'hover:bg-gray-200 cursor-pointer'), disabled: (_d = item.isDisabled) === null || _d === void 0 ? void 0 : _d.call(item) }, (item === null || item === void 0 ? void 0 : item.icon) && react_1["default"].createElement("span", { dangerouslySetInnerHTML: { __html: item.icon } })), item.borderRight && react_1["default"].createElement("div", { className: 'w-[1px] h-5 bg-gray-300' }))); }), showEmojiPicker && (react_1["default"].createElement("div", { ref: emojiPickerRef, className: 'fixed z-50 p-5 bg-white border shadow sm:absolute rounded-xl', style: tslib_1.__assign({ left: '50%', top: emojiPickerPos.top, transform: 'translateX(-50%)' }, (window.innerWidth >= 640 && { left: emojiPickerPos.left, transform: 'none' })) }, react_1["default"].createElement(emoji_picker_react_1["default"], { emojiStyle: emoji_picker_react_1.EmojiStyle.APPLE, searchPlaceHolder: 'T\u00ECm ki\u1EBFm', onEmojiClick: function (emojiData) { return editor === null || editor === void 0 ? void 0 : editor.chain().focus().insertContent(emojiData.emoji).run(); }, previewConfig: { showPreview: false }, skinTonesDisabled: true }))), showColorPicker && (react_1["default"].createElement("div", { ref: pickerRef, className: 'fixed z-50 p-5 bg-white border shadow sm:absolute rounded-xl', style: tslib_1.__assign({ left: '50%', top: pickerPos.top, transform: 'translateX(-50%)' }, (window.innerWidth >= 640 && { left: pickerPos.left, transform: 'none' })) }, react_1["default"].createElement(react_colorful_1.HexAlphaColorPicker, { color: fontColor, onChange: function (color) { var norm = normalizeColor(color); setFontColor(norm); editor === null || editor === void 0 ? void 0 : editor.chain().focus().setColor(norm).setMark('textStyle', { color: "".concat(norm, " !important") }).run(); } }), react_1["default"].createElement("div", { className: 'flex items-center justify-center gap-2 mt-2' }, react_1["default"].createElement("div", { className: 'flex items-center gap-x-2 border border-gray-300 py-1.5 px-3 rounded-xl' }, react_1["default"].createElement("div", { className: 'w-6 h-6 border rounded ', style: { backgroundColor: fontColor.replace(/ !important$/, '') }, title: fontColor }), react_1["default"].createElement("input", { type: 'text', className: 'p-0 border-none !ring-0 w-[144px]', value: fontColor.replace(/ !important$/, ''), onChange: function (e) { var raw = e.target.value.startsWith('#') ? e.target.value : "#".concat(e.target.value.replace(/^#/, '')); var norm = normalizeColor(raw); setFontColor(norm); if (/^#([0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(norm)) { editor === null || editor === void 0 ? void 0 : editor.chain().focus().setColor(norm).run(); } } }))))))), react_1["default"].createElement(react_3.EditorContent, { editor: editor }), react_1["default"].createElement(Modal_1.PHXModal, { onHide: function () { setShowLinkInput(false); reset(); }, show: showLinkInput, title: 'Ch\u00E8n \u0111\u01B0\u1EDDng li\u00EAn k\u1EBFt', onPrimaryClick: handleInsertLink, closeButton: true }, react_1["default"].createElement(Input_1.PHXInput, { error: !!errors.link, errorType: 'custom-message', errorMessageCustom: (_b = errors.link) === null || _b === void 0 ? void 0 : _b.message, register: tslib_1.__assign({}, register('link', { pattern: { value: regexUrl, message: 'Đường dẫn không hợp lệ' }, required: 'Vui lòng nhập đường dẫn' })), label: 'Nh\u1EADp \u0111\u01B0\u1EDDng d\u1EABn', placeholder: 'https://example.com' })), react_1["default"].createElement(Modal_1.PHXModal, { disableSubmit: !pdfFile, show: openPdfModal, onHide: function () { return setOpenPdfModal(false); }, title: 'T\u1EA3i l\u00EAn t\u00E0i li\u1EC7u PDF', onPrimaryClick: function () { if (pdfFile) handleConvertPdfToImage(pdfFile); }, primaryLoading: loadingConvertPdf }, react_1["default"].createElement(FormUpload_1.FormUpload, { fileName: pdfFile === null || pdfFile === void 0 ? void 0 : pdfFile.name, fileType: 'pdf', helpText: 'PDF, t\u1ED1i \u0111a 20MB', isHorizontalLayout: true, handleFileChange: function (e) { setPdfFile(e.target.files[0]); } })))); } exports["default"] = TextEditor; //# sourceMappingURL=TextEditor.js.map