kedao
Version:
Rich Text Editor Based On Draft.js
192 lines (191 loc) • 7.66 kB
JavaScript
import React from 'react';
import Immutable, { Map } from 'immutable';
import { DefaultDraftBlockRenderMap, CompositeDecorator } from 'draft-js';
import Image from '../../components/Image';
import Video from '../../components/Video';
import Audio from '../../components/Audio';
import Embed from '../../components/Embed';
import HorizontalLine from '../../components/HorizontalLine';
import { removeBlock } from '..';
import CombineDecorators from 'draft-js-multidecorators';
import Link from '../../components/Link';
import { classNameParser } from '../style';
import styles from "./style.module.css";
const cls = classNameParser(styles);
export const getBlockRenderMap = (blockRenderMap) => {
let customBlockRenderMap = Map({
atomic: {
element: ''
},
'code-block': {
element: 'code',
wrapper: React.createElement("pre", null)
}
});
try {
if (blockRenderMap) {
customBlockRenderMap = customBlockRenderMap.merge(blockRenderMap);
}
customBlockRenderMap = DefaultDraftBlockRenderMap.merge(customBlockRenderMap);
}
catch (error) {
console.warn(error);
}
return customBlockRenderMap;
};
export const getBlockRendererFn = (superProps, customBlockRendererFn) => (block) => {
var _a;
const { value, onChange, extendAtomics, imageEqualRatio, readOnly, imageResizable, imageControls, lock, getContainerNode, refresh } = superProps;
const renderAtomicBlock = ({ contentState }) => {
const entityKey = block.getEntityAt(0);
if (!entityKey) {
return null;
}
const entity = contentState.getEntity(entityKey);
const mediaData = entity.getData();
const mediaType = entity.getType();
const handleRemove = () => {
onChange === null || onChange === void 0 ? void 0 : onChange(removeBlock(value, block));
};
const mediaProps = {
// block: props.block,
mediaData,
// entityKey,
onRemove: handleRemove,
editorState: value,
contentState: value.getCurrentContent()
};
if (mediaType === 'IMAGE') {
return (React.createElement(Image, Object.assign({}, mediaProps, { imageEqualRatio: imageEqualRatio, entityKey: entityKey, readOnly: readOnly, block: block, imageResizable: imageResizable, imageControls: imageControls, lock: lock, getContainerNode: getContainerNode, value: value, onChange: onChange, refresh: refresh })));
}
if (mediaType === 'AUDIO') {
return React.createElement(Audio, Object.assign({}, mediaProps));
}
if (mediaType === 'VIDEO') {
return React.createElement(Video, Object.assign({}, mediaProps));
}
if (mediaType === 'EMBED') {
return React.createElement(Embed, Object.assign({}, mediaProps));
}
if (mediaType === 'HR') {
return React.createElement(HorizontalLine, Object.assign({}, mediaProps));
}
if (extendAtomics) {
const atomic = extendAtomics.find(item => {
return item.type === mediaType;
});
if (atomic) {
const Component = atomic.component;
return React.createElement(Component, Object.assign({}, mediaProps));
}
}
return null;
};
const blockType = block.getType();
const customRenderer = (_a = customBlockRendererFn === null || customBlockRendererFn === void 0 ? void 0 : customBlockRendererFn(block, { editorState: value })) !== null && _a !== void 0 ? _a : null;
if (customRenderer) {
return customRenderer;
}
if (blockType === 'atomic') {
return {
component: renderAtomicBlock,
editable: false
};
}
return null;
};
export const getBlockStyleFn = customBlockStyleFn => block => {
const blockAlignment = block.getData() && block.getData().get('textAlign');
const blockIndent = block.getData() && block.getData().get('textIndent');
const blockFloat = block.getData() && block.getData().get('float');
const className = [
blockAlignment && cls(`kedao-alignment-${blockAlignment}`),
blockIndent && cls(`kedao-text-indent-${blockIndent}`),
blockFloat && cls(`kedao-float-${blockFloat}`),
customBlockStyleFn === null || customBlockStyleFn === void 0 ? void 0 : customBlockStyleFn(block)
]
.filter(Boolean)
.join(' ');
return className;
};
export const getCustomStyleMap = (customStyleMap = {}) => {
return Object.assign({ SUPERSCRIPT: {
position: 'relative',
top: '-8px',
fontSize: '11px'
}, SUBSCRIPT: {
position: 'relative',
bottom: '-8px',
fontSize: '11px'
} }, customStyleMap);
};
const getStyleValue = style => style.split('-')[1];
export const getCustomStyleFn = options => (styles, block) => {
let output = {};
const { fontFamilies, unitExportFn, customStyleFn } = options;
output = customStyleFn ? customStyleFn(styles, block, output) : {};
styles.forEach(style => {
if (style.indexOf('COLOR-') === 0) {
output.color = `#${getStyleValue(style)}`;
}
else if (style.indexOf('BGCOLOR-') === 0) {
output.backgroundColor = `#${getStyleValue(style)}`;
}
else if (style.indexOf('FONTSIZE-') === 0) {
output.fontSize = unitExportFn(getStyleValue(style), 'font-size', 'editor');
}
else if (style.indexOf('LINEHEIGHT-') === 0) {
output.lineHeight = unitExportFn(getStyleValue(style), 'line-height', 'editor');
}
else if (style.indexOf('LETTERSPACING-') === 0) {
output.letterSpacing = unitExportFn(getStyleValue(style), 'letter-spacing', 'editor');
}
else if (style.indexOf('TEXTINDENT-') === 0) {
output.textIndent = unitExportFn(getStyleValue(style), 'text-indent', 'editor');
}
else if (style.indexOf('FONTFAMILY-') === 0) {
output.fontFamily =
(fontFamilies.find(item => item.name.toUpperCase() === getStyleValue(style)) || {}).family || '';
}
});
return output;
};
const KEY_SEPARATOR = '-';
CombineDecorators.prototype.getDecorations = function getDecorations(block, contentState) {
const decorations = Array(block.getText().length).fill(null);
this.decorators.forEach((decorator, i) => {
decorator.getDecorations(block, contentState).forEach((key, offset) => {
if (!key) {
return;
}
decorations[offset] = i + KEY_SEPARATOR + key;
});
});
return Immutable.List(decorations);
};
const builtinDecorators = [
{
type: 'entity',
decorator: {
key: 'LINK',
component: Link
}
}
];
const createStrategy = (type) => (block, callback, contentState) => {
block.findEntityRanges((character) => {
const entityKey = character.getEntity();
return (entityKey !== null && contentState.getEntity(entityKey).getType() === type);
}, callback);
};
export const getDecorators = () => {
return new CombineDecorators([
// combine decorators created with strategy
new CompositeDecorator([]),
// combine decorators for entities
new CompositeDecorator(builtinDecorators.map(item => ({
strategy: createStrategy(item.decorator.key),
component: item.decorator.component
})))
]);
};