@prisma-cms/editor
Version:
Editor for @prisma-cms
425 lines • 20.2 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (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 __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PrismaEditor = exports.styles = void 0;
const react_1 = __importStar(require("react"));
// import PropTypes from "prop-types";
const draft_js_android_fix_1 = require("draft-js-android-fix");
// import {
// changeDepth,
// handleNewLine,
// blockRenderMap,
// getCustomStyleMap,
// extractInlineStyle,
// getSelectedBlocksType,
// } from 'draftjs-utils';
const immutable_1 = require("immutable");
// import getLinkDecorator from './decorators/Link';
const withStyles_1 = __importDefault(require("material-ui/styles/withStyles"));
const IconButton_1 = __importDefault(require("material-ui/IconButton"));
const Grid_1 = __importDefault(require("material-ui/Grid"));
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 FormatListBulleted_1 = __importDefault(require("material-ui-icons/FormatListBulleted"));
const FormatListNumbered_1 = __importDefault(require("material-ui-icons/FormatListNumbered"));
const Code_1 = __importDefault(require("material-ui-icons/Code"));
// import ListControl from "./controls/List";
const ToggleBlockType_1 = __importDefault(require("./controls/ToggleBlockType"));
const Link_1 = __importStar(require("./controls/Link"));
const Code_2 = __importDefault(require("./controls/Code/"));
const insertTextBlock_1 = require("./modifiers/insertTextBlock");
// import { removeTextBlock } from './modifiers/removeTextBlock';
const LinkComponent_1 = __importDefault(require("./LinkComponent"));
const image_1 = __importDefault(require("./blocks/image"));
__exportStar(require("./interfaces"), exports);
// const {
// hasCommandModifier
// } = KeyBindingUtil;
const { toggleInlineStyle,
// onTab,
} = draft_js_android_fix_1.RichUtils;
exports.styles = {
root: {
'&.PrismaEditor--editable': {
'& .DraftEditor-root': {
'& > .DraftEditor-editorContainer': {
'& > .public-DraftEditor-content': {
'& > div[data-contents=true]': {
border: '1px solid #ddd',
padding: 3,
},
},
},
},
},
'& .DraftEditor-root': {
'& > .DraftEditor-editorContainer': {
'& > .public-DraftEditor-content': {
'& figure': {
margin: '15px auto',
},
},
},
},
},
menu: {
padding: 2,
border: '1px solid #eee',
marginBottom: 5,
},
iconButton: {
height: '2rem',
width: '2rem',
'& svg': {
height: '1.2rem',
fontSize: '1.2rem',
},
},
};
class PrismaEditor extends react_1.PureComponent {
constructor(props) {
super(props);
this.getCompositeDecorator = () => {
var _a;
const decoratorsProps = (_a = this.props.decorators) !== null && _a !== void 0 ? _a : [];
const decorators = [
...decoratorsProps,
(0, Link_1.decorator)({
props: {
onEditStart: this.onEditStart,
onEditEnd: this.onEditEnd,
onChange: this.onChange,
getEditorState: this.getEditorState,
isReadOnly: this.isReadOnly,
Component: this.props.LinkComponent,
},
}),
];
return new draft_js_android_fix_1.CompositeDecorator(decorators);
// return new CompositeDecorator([]);
};
this.getEditorState = () => {
return this.state.editorState;
};
this.isReadOnly = () => {
return this.props.readOnly;
};
this.onChange = (editorState) => {
const { editorState: prevState } = this.state;
this.setState({
editorState,
}, () => {
const { onChange } = this.props;
/**
* Only if content modified
*/
const currentContent = editorState.getCurrentContent();
if (onChange && currentContent !== prevState.getCurrentContent()) {
const rawContent = (0, draft_js_android_fix_1.convertToRaw)(currentContent);
Object.assign(this.state, {
rawContent,
});
onChange && onChange(rawContent, editorState);
}
});
};
this.blockRenderer = (block) => {
if (block.getType() === 'atomic') {
return {
component: Code_2.default,
editable: false,
props: {
allow_edit: !this.isReadOnly(),
onStartEdit: this.onEditStart,
onFinishEdit: (_blockKey, newContentState) => {
const { editorState } = this.state;
const newEditorState = draft_js_android_fix_1.EditorState.push(editorState, newContentState, 'change-block-type');
this.onChange(newEditorState);
this.onEditEnd();
},
_insertText: this._insertText,
onRemove: (blockKey) => this._removeTeX(blockKey),
},
};
}
else if (block.getType() === 'image') {
return {
component: image_1.default,
editable: false,
props: {},
};
}
return null;
};
this._insertText = () => {
this.setState({
liveTeXEdits: (0, immutable_1.Map)(),
editorState: (0, insertTextBlock_1.insertTextBlock)(this.state.editorState),
});
};
this._removeTeX = (blockKey) => {
const { editorState } = this.state;
const contentState = editorState.getCurrentContent();
const newBlockMap = contentState.getBlockMap().delete(blockKey); // this is the important one that actually deletes a block
const newContentState = contentState.set('blockMap', newBlockMap);
// const newEditorState = EditorState.push(editorState, newContentState, 'remove-block')
const newEditorState = draft_js_android_fix_1.EditorState.push(editorState, newContentState, 'split-block');
this.onChange(newEditorState);
this.onEditEnd();
return;
};
this.insertCodeBlock = () => {
this._insertText();
};
this.onEditStart = () => {
const { inEditBlocksCount = 0 } = this.state;
this.setState({
inEditBlocksCount: inEditBlocksCount + 1,
});
};
this.onEditEnd = () => {
const { inEditBlocksCount = 0 } = this.state;
this.setState({
inEditBlocksCount: inEditBlocksCount > 0 ? inEditBlocksCount - 1 : inEditBlocksCount,
});
};
this.handleKeyCommand = (command) => {
if (command === 'myeditor-save') {
// Perform a request to save your contents, set
// a new `editorState`, etc.
return 'handled';
}
switch (command) {
case 'bold':
return this.toggleInlineStyle('BOLD');
case 'italic':
return this.toggleInlineStyle('ITALIC');
case 'underline':
return this.toggleInlineStyle('UNDERLINE');
default:
}
return 'not-handled';
};
const { value } = props;
const { editorState, rawContent } = this.initState(value);
this.state = Object.assign(Object.assign({}, this.state), { editorState,
rawContent, allowRender: value && typeof value === 'object', blockRenderMap: this.getBlockRenderMap() });
}
componentDidMount() {
super.componentDidMount && super.componentDidMount();
if (!this.state.allowRender &&
global.document !== undefined &&
process.env.NODE_ENV !== 'test') {
this.setState({
allowRender: true,
});
}
}
// initState(value: PrismaCmsEditorRawContent | string | undefined) {
initState(value) {
let editorState;
const rawContent = value;
const compositeDecorator = this.getCompositeDecorator();
if (value) {
if (typeof value === 'object') {
const rawContentState = value;
const contentState = (0, draft_js_android_fix_1.convertFromRaw)(rawContentState);
editorState = draft_js_android_fix_1.EditorState.createWithContent(contentState, compositeDecorator);
}
// else if (typeof value === 'string' && global.document !== undefined) {
// const blocks = convertFromHTML(value)
// // const contentState = ContentState.createFromBlockArray(blocks);
// const contentState = ContentState.createFromBlockArray(
// blocks.contentBlocks
// )
// editorState = EditorState.createWithContent(
// contentState,
// compositeDecorator
// )
// }
}
if (!editorState) {
editorState = draft_js_android_fix_1.EditorState.createEmpty(compositeDecorator);
}
return {
editorState,
rawContent,
};
}
componentDidUpdate(prevProps) {
const { value: prevValue, readOnly: prevReadOnly } = prevProps;
const { value, readOnly } = this.props;
const { rawContent } = this.state;
/**
* Приходится отслеживать несколько условий для обновления стейта, чтобы перерендеривался.
* Надо будет переработать логику
*/
// eslint-disable-next-line no-empty
if (readOnly !== prevReadOnly && readOnly) {
}
else if (
// ((value !== undefined && rawContent !== undefined) && value !== rawContent && value !== prevValue)
value !== undefined &&
value !== rawContent &&
value !== prevValue
// ((value !== undefined) && value !== prevValue)
// || readOnly !== prevReadOnly
) {
/**
* Важно! Сейчас рассчитано только на сброс кеша или
* любое другое изменение входящего значения.
* В onChange обязательно надо присваивать rawContent.
* Это надо будет переделать. Правильней всего в любом случае отправлять во вне editorState,
* а там пусть решает изменился contentState или нет (не путать с editorState, см. onChange).
*/
const { editorState } = this.initState(value);
this.setState({
editorState,
}, () => {
this.onChange(editorState);
});
// const {
// editorState,
// } = this.initState(value);
// this.onChange(editorState);
}
}
// keyBinding(event: React.KeyboardEvent<{}>): DraftEditorCommand | null {
// return getDefaultKeyBinding(event)
// }
getBlockRenderMap() {
var _a;
const { plugins = [], defaultBlockRenderMap } = this.props;
let blockRenderMap = plugins
.filter((plug) => plug.blockRenderMap !== undefined)
.reduce((maps, plug) => maps.merge(plug.blockRenderMap), (0, immutable_1.Map)({}));
if (defaultBlockRenderMap) {
blockRenderMap = draft_js_android_fix_1.DefaultDraftBlockRenderMap.merge(blockRenderMap);
}
const blockRenderMapProps = (_a = this.props.blockRenderMap) !== null && _a !== void 0 ? _a : null;
if (blockRenderMapProps) {
blockRenderMap = blockRenderMap.merge(blockRenderMapProps);
}
return blockRenderMap;
}
toggleInlineStyle(style) {
this.onChange(toggleInlineStyle(this.state.editorState, style));
return 'handled';
}
render() {
const { editorKey, classes, readOnly,
// decorators,
// plugins,
// onChange,
// value,
className, show_toolbar,
// ...other
} = this.props;
const { editorState, inEditBlocksCount, allowRender, blockRenderMap } = this.state;
// const selectionState = editorState.getSelection();
if (!allowRender) {
return null;
}
return (react_1.default.createElement("div", { className: [
className,
classes === null || classes === void 0 ? void 0 : classes.root,
!readOnly ? 'PrismaEditor--editable' : '',
].join(' ') },
!readOnly && show_toolbar ? (react_1.default.createElement("div", { className: classes === null || classes === void 0 ? void 0 : classes.menu },
react_1.default.createElement(Grid_1.default, { container: true },
react_1.default.createElement(Grid_1.default, { item: true },
react_1.default.createElement(IconButton_1.default
// TODO remove arrow function
// eslint-disable-next-line react/jsx-no-bind
, {
// TODO remove arrow function
// eslint-disable-next-line react/jsx-no-bind
onClick: () => this.toggleInlineStyle('BOLD'), className: classes === null || classes === void 0 ? void 0 : classes.iconButton },
react_1.default.createElement(FormatBold_1.default, null))),
react_1.default.createElement(Grid_1.default, { item: true },
react_1.default.createElement(IconButton_1.default
// TODO remove arrow function
// eslint-disable-next-line react/jsx-no-bind
, {
// TODO remove arrow function
// eslint-disable-next-line react/jsx-no-bind
onClick: () => this.toggleInlineStyle('ITALIC'), className: classes === null || classes === void 0 ? void 0 : classes.iconButton },
react_1.default.createElement(FormatItalic_1.default, null))),
react_1.default.createElement(Grid_1.default, { item: true },
react_1.default.createElement(IconButton_1.default
// TODO remove arrow function
// eslint-disable-next-line react/jsx-no-bind
, {
// TODO remove arrow function
// eslint-disable-next-line react/jsx-no-bind
onClick: () => this.toggleInlineStyle('UNDERLINE'), className: classes === null || classes === void 0 ? void 0 : classes.iconButton },
react_1.default.createElement(FormatUnderlined_1.default, null))),
react_1.default.createElement(Grid_1.default, { item: true },
react_1.default.createElement(ToggleBlockType_1.default, { editorState: editorState, onChange: this.onChange, blockType: "unordered-list-item", className: classes === null || classes === void 0 ? void 0 : classes.iconButton, icon: FormatListBulleted_1.default })),
react_1.default.createElement(Grid_1.default, { item: true },
react_1.default.createElement(ToggleBlockType_1.default, { editorState: editorState, onChange: this.onChange, blockType: "ordered-list-item", className: classes === null || classes === void 0 ? void 0 : classes.iconButton, icon: FormatListNumbered_1.default })),
react_1.default.createElement(Grid_1.default, { item: true },
react_1.default.createElement(Link_1.default, { className: classes === null || classes === void 0 ? void 0 : classes.iconButton, editorState: editorState, onChange: this.onChange })),
react_1.default.createElement(Grid_1.default, { item: true },
react_1.default.createElement(IconButton_1.default, { className: classes === null || classes === void 0 ? void 0 : classes.iconButton, onClick: this.insertCodeBlock },
react_1.default.createElement(Code_1.default, null)))))) : null,
react_1.default.createElement(draft_js_android_fix_1.Editor, { editorKey: editorKey, editorState: editorState, readOnly: readOnly || inEditBlocksCount > 0, onChange: this.onChange, handleKeyCommand: this.handleKeyCommand,
// keyBindingFn={this.keyBinding}
keyBindingFn: draft_js_android_fix_1.getDefaultKeyBinding,
// blockRenderMap={this.getBlockRenderMap()}
blockRenderMap: blockRenderMap, blockRendererFn: this.blockRenderer })));
}
}
exports.PrismaEditor = PrismaEditor;
// static propTypes = {
// classes: PropTypes.object.isRequired,
// value: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
// readOnly: PropTypes.bool.isRequired,
// spellCheck: PropTypes.bool.isRequired,
// decorators: PropTypes.array,
// defaultBlockRenderMap: PropTypes.bool.isRequired,
// plugins: PropTypes.array.isRequired,
// show_toolbar: PropTypes.bool.isRequired,
// LinkComponent: PropTypes.func.isRequired,
// }
PrismaEditor.defaultProps = {
readOnly: true,
spellCheck: true,
decorators: [],
defaultBlockRenderMap: true,
plugins: [],
show_toolbar: true,
LinkComponent: LinkComponent_1.default,
};
// @ts-expect-error types
exports.default = (0, withStyles_1.default)(exports.styles)((props) => (react_1.default.createElement(PrismaEditor, Object.assign({}, props))));
//# sourceMappingURL=index.js.map