@prisma-cms/front-editor
Version:
492 lines • 28.6 kB
JavaScript
"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