UNPKG

@prisma-cms/editor

Version:
425 lines 20.2 kB
"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