@kedao/editor
Version:
Rich Text Editor Based On Draft.js
285 lines • 15.6 kB
JavaScript
import React from 'react';
import { v4 as uuidv4 } from 'uuid';
import { ContentUtils } from '@kedao/utils';
import getEditorControls from '../../../configs/controls';
import LinkEditor from '../LinkEditor';
import HeadingPicker from '../Headings';
import TextColorPicker from '../TextColor';
import FontSizePicker from '../FontSize';
import LineHeightPicker from '../LineHeight';
import FontFamilyPicker from '../FontFamily';
import TextAlign from '../TextAlign';
import EmojiPicker from '../EmojiPicker';
import LetterSpacingPicker from '../LetterSpacing';
import TextIndent from '../TextIndent';
import DropDown from '../../../components/common/DropDown';
import { showModal } from '../../../components/common/Modal';
import { getExtensionControls } from '../../../helpers/extension';
import './style.scss';
const commandHookMap = {
'inline-style': 'toggle-inline-style',
'block-type': 'change-block-type',
'editor-method': 'exec-editor-command'
};
const exclusiveInlineStyles = {
superscript: 'subscript',
subscript: 'superscript'
};
const mergeControls = (commonProps, builtControls, extensionControls, extendControls) => {
const customExtendControls = extendControls.map((item) => typeof item === 'function' ? item(commonProps) : item);
if (extensionControls.length === 0 && customExtendControls.length === 0) {
return builtControls;
}
return builtControls
.map((item) => {
return (customExtendControls.find((subItem) => {
return subItem.replace === (item.key || item);
}) ||
extensionControls.find((subItem) => {
return subItem.replace === (item.key || item);
}) ||
item);
})
.concat(extensionControls.length ? 'separator' : '')
.concat(extensionControls.filter((item) => {
return !item.replace;
}))
.concat(customExtendControls.filter((item) => {
return typeof item === 'string' || !item.replace;
}));
};
export default class ControlBar extends React.Component {
constructor() {
super(...arguments);
this.allControls = [];
this.mediaLibiraryModal = null;
this.extendedModals = {};
this.openFinder = () => {
if (!this.props.finder || !this.props.finder.ReactComponent) {
return false;
}
if (this.props.hooks('open-kedao-finder')() === false) {
return false;
}
const mediaProps = this.props.media;
const MediaLibrary = this.props.finder.ReactComponent;
this.mediaLibiraryModal = showModal({
title: this.props.language.controls.mediaLibirary,
language: this.props.language,
width: 640,
showFooter: false,
onClose: mediaProps.onClose,
component: (React.createElement(MediaLibrary, { accepts: mediaProps.accepts, onCancel: this.closeFinder, onInsert: this.insertMedias, onChange: mediaProps.onChange, externals: mediaProps.externals, onBeforeSelect: this.bindFinderHook('select-medias'), onBeforeDeselect: this.bindFinderHook('deselect-medias'), onBeforeRemove: this.bindFinderHook('remove-medias'), onBeforeInsert: this.bindFinderHook('insert-medias'), onFileSelect: this.bindFinderHook('select-files') }))
});
return true;
};
this.bindFinderHook = (hookName) => (...params) => {
return this.props.hooks(hookName, params[0])(...params);
};
this.insertMedias = (medias) => {
this.props.editor.setValue(ContentUtils.insertMedias(this.props.editorState, medias));
this.props.editor.requestFocus();
if (this.props.media.onInsert) {
this.props.media.onInsert(medias);
}
this.closeFinder();
};
this.closeFinder = () => {
if (this.props.media.onCancel) {
this.props.media.onCancel();
}
if (this.mediaLibiraryModal) {
this.mediaLibiraryModal.close();
}
};
}
componentDidUpdate() {
const { language } = this.props;
this.allControls.forEach((item) => {
var _a;
if (item.type === 'modal') {
if (((_a = item.modal) === null || _a === void 0 ? void 0 : _a.id) && this.extendedModals[item.modal.id]) {
this.extendedModals[item.modal.id].update(Object.assign(Object.assign({}, item.modal), { language }));
}
}
});
}
getControlItemClassName(data) {
let className = 'control-item button';
const { type, command } = data;
if (type === 'inline-style' &&
ContentUtils.selectionHasInlineStyle(this.props.editorState, command)) {
className += ' active';
}
else if (type === 'block-type' &&
ContentUtils.getSelectionBlockType(this.props.editorState) === command) {
className += ' active';
}
else if (type === 'entity' &&
ContentUtils.getSelectionEntityType(this.props.editorState) === command) {
className += ' active';
}
return className;
}
applyControl(command, type, data = {}) {
let hookCommand = command;
const hookReturns = this.props.hooks(commandHookMap[type] || type, hookCommand)(hookCommand);
let editorState = this.props.editorState;
if (hookReturns === false) {
return false;
}
if (typeof hookReturns === 'string') {
hookCommand = hookReturns;
}
if (type === 'inline-style') {
const exclusiveInlineStyle = exclusiveInlineStyles[hookCommand];
if (exclusiveInlineStyle &&
ContentUtils.selectionHasInlineStyle(editorState, exclusiveInlineStyle)) {
editorState = ContentUtils.toggleSelectionInlineStyle(editorState, exclusiveInlineStyle);
}
this.props.editor.setValue(ContentUtils.toggleSelectionInlineStyle(editorState, hookCommand));
}
if (type === 'block-type') {
this.props.editor.setValue(ContentUtils.toggleSelectionBlockType(editorState, hookCommand));
}
if (type === 'entity') {
this.props.editor.setValue(ContentUtils.toggleSelectionEntity(editorState, {
type: hookCommand,
mutability: data.mutability || 'MUTABLE',
data: data.data || {}
}));
}
if (type === 'editor-method' && this.props.editor[hookCommand]) {
this.props.editor[hookCommand]();
}
return this.props.editor;
}
preventDefault(event) {
const tagName = event.target.tagName.toLowerCase();
if (tagName === 'input' || tagName === 'label') {
// ...
}
else {
event.preventDefault();
}
}
render() {
const { allowInsertLinkText, className, colorPicker, colorPickerAutoHide, colorPickerTheme, colors, controls, defaultLinkTarget, editor, editorId, editorState, emojis, extendControls, fontFamilies, fontSizes, getContainerNode, headings, hooks, language, letterSpacings, lineHeights, media, style, textAligns, textBackgroundColor } = this.props;
const currentBlockType = ContentUtils.getSelectionBlockType(editorState);
const commonProps = {
editor,
editorId,
editorState,
language,
getContainerNode,
hooks
};
const renderedControls = [];
const editorControls = getEditorControls(language, editor);
const extensionControls = getExtensionControls(editorId);
const allControls = mergeControls(commonProps, controls, extensionControls, extendControls);
this.allControls = allControls;
return (React.createElement("div", { className: `bf-controlbar ${className || ''}`, style: style, onMouseDown: this.preventDefault, role: "button", tabIndex: 0 }, allControls.map((item) => {
const itemKey = typeof item === 'string' ? item : item.key;
if (typeof itemKey !== 'string') {
return null;
}
if (renderedControls.includes(itemKey)) {
return null;
}
if (itemKey.toLowerCase() === 'separator') {
return React.createElement("span", { key: uuidv4(), className: "separator-line" });
}
let controlItem = editorControls.find((subItem) => {
return subItem.key.toLowerCase() === itemKey.toLowerCase();
});
if (typeof item !== 'string') {
controlItem = Object.assign(Object.assign({}, controlItem), item);
}
if (!controlItem) {
return null;
}
renderedControls.push(itemKey);
if (controlItem.type === 'headings') {
return (React.createElement(HeadingPicker, Object.assign({ key: uuidv4(), headings: headings, current: currentBlockType, onChange: (command) => this.applyControl(command, 'block-type') }, commonProps)));
}
if (controlItem.type === 'text-color') {
return (React.createElement(TextColorPicker, Object.assign({ key: uuidv4(), colors: colors, colorPicker: colorPicker, theme: colorPickerTheme, autoHide: colorPickerAutoHide, enableBackgroundColor: textBackgroundColor }, commonProps)));
}
if (controlItem.type === 'font-size') {
return (React.createElement(FontSizePicker, Object.assign({ key: uuidv4(), fontSizes: fontSizes, defaultCaption: controlItem.title }, commonProps)));
}
if (controlItem.type === 'line-height') {
return (React.createElement(LineHeightPicker, Object.assign({ key: uuidv4(), lineHeights: lineHeights, defaultCaption: controlItem.title }, commonProps)));
}
if (controlItem.type === 'letter-spacing') {
return (React.createElement(LetterSpacingPicker, Object.assign({ key: uuidv4(), letterSpacings: letterSpacings, defaultCaption: controlItem.title }, commonProps)));
}
if (controlItem.type === 'text-indent') {
return (React.createElement(TextIndent, Object.assign({ key: uuidv4(), defaultCaption: controlItem.title }, commonProps)));
}
if (controlItem.type === 'font-family') {
return (React.createElement(FontFamilyPicker, Object.assign({ key: uuidv4(), fontFamilies: fontFamilies, defaultCaption: controlItem.title }, commonProps)));
}
if (controlItem.type === 'emoji') {
return (React.createElement(EmojiPicker, Object.assign({ key: uuidv4(), emojis: emojis, defaultCaption: controlItem.text }, commonProps)));
}
if (controlItem.type === 'link') {
return (React.createElement(LinkEditor, Object.assign({ key: uuidv4(), defaultLinkTarget: defaultLinkTarget, allowInsertLinkText: allowInsertLinkText }, commonProps)));
}
if (controlItem.type === 'text-align') {
return (React.createElement(TextAlign, Object.assign({ key: uuidv4(), textAligns: textAligns }, commonProps)));
}
if (controlItem.type === 'media') {
if (!media.image && !media.video && !media.audio) {
return null;
}
return (React.createElement("button", { type: "button", key: uuidv4(), "data-title": controlItem.title, disabled: controlItem.disabled, className: "control-item media button", onClick: this.openFinder }, controlItem.text));
}
if (controlItem.type === 'dropdown') {
return (React.createElement(DropDown, Object.assign({ key: uuidv4(), className: `control-item extend-control-item dropdown ${controlItem.className || ''}`, caption: controlItem.text, htmlCaption: controlItem.html, showArrow: controlItem.showArrow, title: controlItem.title, arrowActive: controlItem.arrowActive, theme: controlItem.theme, autoHide: controlItem.autoHide, disabled: controlItem.disabled, ref: controlItem.ref }, commonProps), controlItem.component));
}
if (controlItem.type === 'modal') {
return (React.createElement("button", { type: "button", key: uuidv4(), "data-title": controlItem.title, disabled: controlItem.disabled, className: `control-item extend-control-item button ${controlItem.className || ''}`, dangerouslySetInnerHTML: controlItem.html ? { __html: controlItem.html } : null, onClick: (event) => {
var _a;
if ((_a = controlItem.modal) === null || _a === void 0 ? void 0 : _a.id) {
if (this.extendedModals[controlItem.modal.id]) {
this.extendedModals[controlItem.modal.id].active = true;
this.extendedModals[controlItem.modal.id].update(Object.assign(Object.assign({}, controlItem.modal), { language }));
}
else {
this.extendedModals[controlItem.modal.id] = showModal(Object.assign(Object.assign({}, controlItem.modal), { language }));
if (controlItem.modal.onCreate) {
controlItem.modal.onCreate(this.extendedModals[controlItem.modal.id]);
}
}
}
if (controlItem.onClick) {
controlItem.onClick(event);
}
} }, !controlItem.html ? controlItem.text : null));
}
if (controlItem.type === 'component') {
return (React.createElement("div", { key: uuidv4(), className: `component-wrapper ${controlItem.className || ''}` }, controlItem.component));
}
if (controlItem.type === 'button') {
return (React.createElement("button", { type: "button", key: uuidv4(), "data-title": controlItem.title, disabled: controlItem.disabled, className: `control-item button ${controlItem.className || ''}`, dangerouslySetInnerHTML: controlItem.html ? { __html: controlItem.html } : null, onClick: (event) => { var _a; return (_a = controlItem.onClick) === null || _a === void 0 ? void 0 : _a.call(controlItem, event); } }, !controlItem.html ? controlItem.text : null));
}
if (controlItem) {
let disabled = false;
if (controlItem.command === 'undo') {
disabled = editorState.getUndoStack().size === 0;
}
else if (controlItem.command === 'redo') {
disabled = editorState.getRedoStack().size === 0;
}
return (React.createElement("button", { type: "button", key: uuidv4(), disabled: disabled, "data-title": controlItem.title, className: this.getControlItemClassName({
type: controlItem.type,
command: controlItem.command
}), onClick: () => this.applyControl(controlItem.command, controlItem.type, controlItem.data) }, controlItem.text));
}
return null;
})));
}
}
//# sourceMappingURL=index.js.map