UNPKG

@prisma-cms/front-editor

Version:
492 lines 28.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-disable @typescript-eslint/ban-ts-ignore */ const react_1 = __importStar(require("react")); const IconButton_1 = __importDefault(require("material-ui/IconButton")); const Button_1 = __importDefault(require("material-ui/Button")); const ModeEdit_1 = __importDefault(require("material-ui-icons/ModeEdit")); const FormatClear_1 = __importDefault(require("material-ui-icons/FormatClear")); const SelectAll_1 = __importDefault(require("material-ui-icons/SelectAll")); const FormatAlignLeft_1 = __importDefault(require("material-ui-icons/FormatAlignLeft")); const FormatAlignRight_1 = __importDefault(require("material-ui-icons/FormatAlignRight")); const FormatAlignJustify_1 = __importDefault(require("material-ui-icons/FormatAlignJustify")); const FormatAlignCenter_1 = __importDefault(require("material-ui-icons/FormatAlignCenter")); const FormatBold_1 = __importDefault(require("material-ui-icons/FormatBold")); const FormatItalic_1 = __importDefault(require("material-ui-icons/FormatItalic")); const FormatUnderlined_1 = __importDefault(require("material-ui-icons/FormatUnderlined")); const FormatStrikethrough_1 = __importDefault(require("material-ui-icons/FormatStrikethrough")); const FormatListBulleted_1 = __importDefault(require("material-ui-icons/FormatListBulleted")); const FormatListNumbered_1 = __importDefault(require("material-ui-icons/FormatListNumbered")); const FormatIndentIncrease_1 = __importDefault(require("material-ui-icons/FormatIndentIncrease")); const FormatIndentDecrease_1 = __importDefault(require("material-ui-icons/FormatIndentDecrease")); const Redo_1 = __importDefault(require("material-ui-icons/Redo")); const Undo_1 = __importDefault(require("material-ui-icons/Undo")); const Save_1 = __importDefault(require("material-ui-icons/Save")); const Grid_1 = __importDefault(require("../../../../common/Grid")); const toolbar_1 = require("../../TagEditor/styles/toolbar"); const interfaces_1 = require("../interfaces"); const nodeToEditorComponentObject_1 = require("../helpers/nodeToEditorComponentObject"); const ContentEditorToolbar = (props) => { const { // selection, newContent, closestInSelection, saveChanges, editMode, setEditMode, contentEditableContainer, updateObject, experimental, } = props; const setEditModeHTML = react_1.useCallback(() => { setEditMode(editMode === interfaces_1.ContentProxyEditMode.HTML ? null : interfaces_1.ContentProxyEditMode.HTML); return true; }, [editMode, setEditMode]); const [selection, setSelection] = react_1.useState(null); const [selectionType, setSelectionType] = react_1.useState(undefined); const [contentEditableContainerSelected, setContentEditableContainerSelected,] = react_1.useState(false); // console.log('contentEditableContainerSelected', contentEditableContainerSelected); // console.log('contentEditableContainer', contentEditableContainer); const onSelectionChange = react_1.useCallback((_event) => { // const container = ref.current; const selection = document.getSelection(); // console.log('Toolbar onSelectionChange selection', selection); // if (selection2 && container && selection2.containsNode(container, true)) { // setSelection(selection2); // } // else { // setSelection(null); // } const contentEditableContainerSelected = (contentEditableContainer && (selection === null || selection === void 0 ? void 0 : selection.containsNode(contentEditableContainer, true))) || false; // console.log('Toolbar contentEditableContainerSelected', contentEditableContainerSelected); setContentEditableContainerSelected(contentEditableContainerSelected); // setSelection(selection); setSelectionType(selection === null || selection === void 0 ? void 0 : selection.type); }, [contentEditableContainer]); react_1.useEffect(() => { var _a; setSelection((_a = global.document) === null || _a === void 0 ? void 0 : _a.getSelection()); document.addEventListener('selectionchange', onSelectionChange); return () => { document.removeEventListener('selectionchange', onSelectionChange); }; }, [onSelectionChange]); // console.log('ContentEditorToolbar selection', selection); const execCommand = react_1.useCallback((commandId, showUI, value) => { return document.execCommand(commandId, showUI, value); }, []); const onButtonClick = react_1.useCallback((event) => { // return document.execCommand(event.currentTarget.name) return execCommand(event.currentTarget.name); }, [execCommand]); /** * Вставляем колонку */ const insertTableCell = react_1.useCallback((tr, index) => { return !!tr.insertCell(index); }, []); /** * Вставляем новую строку в таблицу */ const insertTableRow = react_1.useCallback((table, index, columnsCount = 1) => { const tr = table.insertRow(index); if (tr) { let i = 0; while (i < columnsCount) { insertTableCell(tr); i++; } return true; } return false; }, [insertTableCell]); const renderToolbarButtons = react_1.useMemo(() => { const hasSelection = contentEditableContainerSelected && editMode === interfaces_1.ContentProxyEditMode.HTML ? true : null; const tableControls = [ { key: 'insertTable', name: 'insertTable', title: 'Вставить таблицу', disabled: hasSelection ? false : true, onClick: () => execCommand('insertHTML', true, `<table><tbody><tr><td></td></tr></tbody></table>`), icon: (react_1.default.createElement("svg", { className: ['icon', !hasSelection ? 'disabled' : ''].join(' '), version: "1.1", xmlns: "http://www.w3.org/2000/svg", xmlnsXlink: "http://www.w3.org/1999/xlink", x: "0px", y: "0px", viewBox: "0 0 512 512", xmlSpace: "preserve" }, react_1.default.createElement("g", null, react_1.default.createElement("g", null, react_1.default.createElement("g", null, react_1.default.createElement("path", { d: "M0,0v512h256v-32h-64V352h32v-32h-32V192h128v32h32v-32h96v-32h-96V32h128v96h32V0H0z M160,480H32V352h128V480z M160,320 H32V192h128V320z M160,160H32V32h128V160z M320,160H192V32h128V160z" }), react_1.default.createElement("path", { d: "M384,256c-70.692,0-128,57.308-128,128s57.308,128,128,128s128-57.308,128-128C511.921,313.34,454.66,256.079,384,256z M384,480c-53.019,0-96-42.981-96-96s42.981-96,96-96s96,42.981,96,96C479.947,436.997,436.997,479.947,384,480z" }), react_1.default.createElement("polygon", { points: "400,336 368,336 368,368 336,368 336,400 368,400 368,432 400,432 400,400 432,400 432,368 400,368" }), react_1.default.createElement("rect", { x: "480", y: "224", width: "32", height: "32" }), react_1.default.createElement("rect", { x: "480", y: "160", width: "32", height: "32" })))), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null), react_1.default.createElement("g", null))), }, ]; if (selection) { const table = closestInSelection('table'); if (table) { const tr = closestInSelection('tr'); table && tableControls.push({ key: 'insertRow', name: 'insertRow', title: 'Вставить строку', disabled: hasSelection ? false : true, onClick: () => { const index = tr && tr.parentElement ? Array.from(tr.parentElement.childNodes).indexOf(tr) : -1; return insertTableRow(table, index + 1, tr === null || tr === void 0 ? void 0 : tr.childElementCount // selection, ); }, icon: (react_1.default.createElement("svg", { className: ['icon', !hasSelection ? 'disabled' : ''].join(' '), version: "1.1", xmlns: "http://www.w3.org/2000/svg", xmlnsXlink: "http://www.w3.org/1999/xlink", x: "0px", y: "0px", viewBox: "0 -21 512 512", xmlSpace: "preserve" }, react_1.default.createElement("path", { d: "m102.355469 277.078125-74.664063-80c-4.480468-4.820313-11.414062-6.421875-17.558594-3.96875-6.121093 2.410156-10.132812 8.320313-10.132812 14.890625v160c0 6.570312 4.011719 12.480469 10.132812 14.871094 1.898438.765625 3.882813 1.128906 5.867188 1.128906 4.351562 0 8.597656-1.769531 11.691406-5.078125l74.664063-80c5.742187-6.144531 5.742187-15.699219 0-21.84375zm0 0" }), react_1.default.createElement("path", { d: "m469.332031 341.332031h-320c-23.53125 0-42.664062 19.136719-42.664062 42.667969v42.667969c0 23.53125 19.132812 42.664062 42.664062 42.664062h320c23.53125 0 42.667969-19.132812 42.667969-42.664062v-42.667969c0-23.53125-19.136719-42.667969-42.667969-42.667969zm-320.019531 42.667969h138.6875v42.667969h-138.667969zm320.019531 42.667969h-138.664062v-42.667969h138.664062zm0 0" }), react_1.default.createElement("path", { d: "m469.332031 0h-320c-23.53125 0-42.664062 19.136719-42.664062 42.667969v149.332031c0 23.53125 19.132812 42.667969 42.664062 42.667969h320c23.53125 0 42.667969-19.136719 42.667969-42.667969v-149.332031c0-23.53125-19.136719-42.667969-42.667969-42.667969zm0 96h-138.664062v-53.332031h138.664062zm-320-53.332031h138.667969v53.332031h-138.6875zm0 96h138.667969v53.332031h-138.667969zm181.335938 53.332031v-53.332031h138.664062v53.332031zm0 0" }))), }); tr && tableControls.push({ key: 'insertCell', name: 'insertCell', title: 'Вставить колонку', disabled: hasSelection ? false : true, onClick: () => { const cell = closestInSelection('td,th'); const index = (cell === null || cell === void 0 ? void 0 : cell.parentElement) ? Array.from(cell.parentElement.childNodes).indexOf(cell) : -1; // return this.insertTableCell(tr, index + 1, selection) return insertTableCell(tr, index + 1); }, icon: (react_1.default.createElement("svg", { className: ['icon', !hasSelection ? 'disabled' : ''].join(' '), version: "1.1", xmlns: "http://www.w3.org/2000/svg", xmlnsXlink: "http://www.w3.org/1999/xlink", x: "0px", y: "0px", viewBox: "-21 0 512 512", xmlSpace: "preserve" }, react_1.default.createElement("path", { d: "m288 106.667969c-3.925781 0-7.851562-1.429688-10.921875-4.3125l-80-74.664063c-4.800781-4.480468-6.378906-11.457031-3.96875-17.558594 2.410156-6.101562 8.320313-10.132812 14.890625-10.132812h160c6.570312 0 12.480469 4.011719 14.890625 10.132812 2.410156 6.125.832031 13.078126-3.96875 17.558594l-80 74.664063c-3.070313 2.882812-6.996094 4.3125-10.921875 4.3125zm-39.402344-74.667969 39.402344 36.777344 39.402344-36.777344zm0 0" }), react_1.default.createElement("path", { d: "m432 512h-53.332031c-20.589844 0-37.335938-16.746094-37.335938-37.332031v-330.667969c0-20.585938 16.746094-37.332031 37.335938-37.332031h53.332031c20.585938 0 37.332031 16.746093 37.332031 37.332031v330.667969c0 20.585937-16.746093 37.332031-37.332031 37.332031zm-53.332031-373.332031c-2.945313 0-5.335938 2.386719-5.335938 5.332031v330.667969c0 2.941406 2.390625 5.332031 5.335938 5.332031h53.332031c2.945312 0 5.332031-2.390625 5.332031-5.332031v-330.667969c0-2.945312-2.386719-5.332031-5.332031-5.332031zm0 0" }), react_1.default.createElement("path", { d: "m197.332031 512h-160c-20.585937 0-37.332031-16.746094-37.332031-37.332031v-330.667969c0-20.585938 16.746094-37.332031 37.332031-37.332031h160c20.589844 0 37.335938 16.746093 37.335938 37.332031v330.667969c0 20.585937-16.746094 37.332031-37.335938 37.332031zm-160-373.332031c-2.941406 0-5.332031 2.386719-5.332031 5.332031v330.667969c0 2.941406 2.390625 5.332031 5.332031 5.332031h160c2.945313 0 5.335938-2.390625 5.335938-5.332031v-330.667969c0-2.945312-2.390625-5.332031-5.335938-5.332031zm0 0" }), react_1.default.createElement("path", { d: "m453.332031 325.332031h-96c-8.832031 0-16-7.167969-16-16s7.167969-16 16-16h96c8.832031 0 16 7.167969 16 16s-7.167969 16-16 16zm0 0" }), react_1.default.createElement("path", { d: "m218.667969 325.332031h-202.667969c-8.832031 0-16-7.167969-16-16s7.167969-16 16-16h202.667969c8.832031 0 16 7.167969 16 16s-7.167969 16-16 16zm0 0" }), react_1.default.createElement("path", { d: "m117.332031 512c-8.832031 0-16-7.167969-16-16v-373.332031c0-8.832031 7.167969-16 16-16s16 7.167969 16 16v373.332031c0 8.832031-7.167969 16-16 16zm0 0" }))), }); } } const editButton = { onClick: setEditModeHTML, name: 'setEditModeHTML', title: 'Редактировать как HTML', // disabled: hasSelection ? false : true, color: editMode === interfaces_1.ContentProxyEditMode.HTML ? 'primary' : undefined, disabled: false, icon: react_1.default.createElement(ModeEdit_1.default, null), }; const buttons = [ /** * Первый элемент выносим в отдельную переменную, чтобы TS правильно понял * массив каких типов используется. * Это нужно для выполнения concat(...) */ editButton, { name: 'bold', title: 'Жирный текст', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatBold_1.default, null), }, { name: 'italic', title: 'Наклонный текст', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatItalic_1.default, null), }, { name: 'underline', title: 'Подчеркнутый текст', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatUnderlined_1.default, null), }, { name: 'strikeThrough', title: 'Перечеркнутый текст', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatStrikethrough_1.default, null), }, { name: 'justifyLeft', title: 'Выровнить влево', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatAlignLeft_1.default, null), }, { name: 'justifyCenter', title: 'Выровнить по центру', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatAlignCenter_1.default, null), }, { name: 'justifyRight', title: 'Выровнить вправо', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatAlignRight_1.default, null), }, { name: 'justifyFull', title: 'Выровнить на всю ширину', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatAlignJustify_1.default, null), }, { name: 'insertUnorderedList', title: 'Ненумерованный список', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatListBulleted_1.default, null), }, { name: 'insertOrderedList', title: 'Нумерованный список', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatListNumbered_1.default, null), }, { name: 'outdent', title: 'Уменьшить отступ', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatIndentDecrease_1.default, null), }, { name: 'indent', title: 'Увеличить отступ', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatIndentIncrease_1.default, null), }, { name: 'selectAll', title: 'Выделить все', disabled: hasSelection ? false : true, icon: react_1.default.createElement(SelectAll_1.default, null), }, { name: 'removeFormat', title: 'Удалить форматирование', disabled: hasSelection ? false : true, icon: react_1.default.createElement(FormatClear_1.default, null), }, ] .concat(tableControls) .concat([ { name: 'undo', title: 'Откатить действие (Ctrl+Z)', disabled: hasSelection ? false : true, icon: react_1.default.createElement(Undo_1.default, null), }, { name: 'redo', title: 'Повторить действие (Ctrl+Y)', disabled: hasSelection ? false : true, icon: react_1.default.createElement(Redo_1.default, null), }, ]); if (newContent === null || newContent === void 0 ? void 0 : newContent.length) { buttons.push({ name: 'save', title: 'Сохранить изменения', // disabled: hasSelection ? false : true, className: 'save', icon: (react_1.default.createElement(Save_1.default, { style: { color: 'red', } })), disabled: false, onClick: saveChanges, }); } return buttons.map((n, index) => { const { disabled, name, icon, onClick, className } = n, other = __rest(n // return ( // <Grid key={name || index} item> // <IconButton // className={'TagEditorToolbar--iconButton'} // name={name} // onClick={onClick ? onClick : onButtonClick} // disabled={disabled} // {...other} // > // {icon} // </IconButton> // </Grid> // ) , ["disabled", "name", "icon", "onClick", "className"]); // return ( // <Grid key={name || index} item> // <IconButton // className={'TagEditorToolbar--iconButton'} // name={name} // onClick={onClick ? onClick : onButtonClick} // disabled={disabled} // {...other} // > // {icon} // </IconButton> // </Grid> // ) return (react_1.default.createElement(Grid_1.default, { key: name || index, item: true }, react_1.default.createElement(IconButton_1.default, Object.assign({ className: ['TagEditorToolbar--iconButton', className].join(' '), name: name, onClick: onClick ? onClick : onButtonClick, disabled: disabled }, other), icon))); }); }, [ closestInSelection, contentEditableContainerSelected, editMode, execCommand, insertTableCell, insertTableRow, newContent === null || newContent === void 0 ? void 0 : newContent.length, onButtonClick, saveChanges, selection, setEditModeHTML, ]); const onMouseDown = react_1.useCallback((event) => { event.preventDefault(); }, []); // const setEditModeReact = useCallback(() => { // setEditMode( // editMode === ContentProxyEditMode.React // ? null // : ContentProxyEditMode.React // ) // }, [editMode, setEditMode]) const addSection = react_1.useCallback(() => { /** * Нельзя просто так взять и вставить реакт-компонент в готовый HTML, чтобы он в контексте был и т.п. * Придется брать текущий HTML, вставлять в него новый элемент, * перегонять в JSON и обновлять базовый компонент. * Важно! Этого нельзя делать в contentEditable=true, потому что новый реакт-компонент * не отрендерится полноценно. */ // console.log('addSection contentEditableContainerSelected', contentEditableContainerSelected); if (!contentEditableContainerSelected || !contentEditableContainer) { return false; } const selection = document.getSelection(); let focusNode = selection === null || selection === void 0 ? void 0 : selection.focusNode; if ((focusNode === null || focusNode === void 0 ? void 0 : focusNode.nodeType) === Node.TEXT_NODE) { focusNode = focusNode.parentNode; } if (focusNode) { const div = document.createElement('div'); // div.contentEditable = "false"; const object = { name: 'Section', component: 'Section', components: [], props: {}, }; // const section = new Section({ // mode: "main", // object, // }); // @ts-ignore // div.reactComponent = section; div.editorComponentObject = object; focusNode.appendChild(div); // TODO Add carret movement into new node // div.focus(); /** * Получаем новый JSON */ const newObject = nodeToEditorComponentObject_1.nodeChildsToEditorComponentObjectComponents(contentEditableContainer); /** * Удаляем вставленный элемент во избежание дублирования */ focusNode.removeChild(div); /** * Обновляем основной компонент */ updateObject(newObject); // const section = <Section // mode="main" // object={object} // />; // const withContext = <EditorContext.Provider // value={editorContext} // > // {section} // </EditorContext.Provider> // const renderToString = ReactDOMServer.renderToString(section); // const renderToString = ReactDOMServer.renderToString(withContext); // console.log('renderToString', renderToString); // ReactDOM.unstable_renderSubtreeIntoContainer(); // ReactDOM.render(section, div) } }, [contentEditableContainer, contentEditableContainerSelected, updateObject]); return react_1.useMemo(() => { const editModes = (react_1.default.createElement(react_1.default.Fragment, null, experimental ? (react_1.default.createElement(Grid_1.default, { item: true }, react_1.default.createElement(Button_1.default, { size: "small", onClick: addSection, // color={ // editMode === ContentProxyEditMode.React ? 'primary' : undefined // } name: "addSection", // TODO Добавить корректную проверку активности кнопки. // Сейчас проблема в том, что если каретка на нередактируемом элементе, // то секция не вставляется disabled: !contentEditableContainerSelected || !!editMode || selectionType !== 'Caret' }, "Add Section"))) : null)); return (react_1.default.createElement(toolbar_1.TagEditorToolbarStyled, { contentEditable: false, onMouseDown: onMouseDown, className: "ContentEditorToolbar" }, react_1.default.createElement(Grid_1.default, { container: true }, renderToolbarButtons, editModes))); }, [ addSection, contentEditableContainerSelected, editMode, experimental, onMouseDown, renderToolbarButtons, selectionType, ]); }; exports.default = ContentEditorToolbar; //# sourceMappingURL=index.js.map