phx-react
Version:
PHX REACT
918 lines • 48.5 kB
JavaScript
;
'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