@kedao/editor
Version:
Rich Text Editor Based On Draft.js
405 lines • 18.2 kB
JavaScript
import React from 'react';
import { v4 as uuidv4 } from 'uuid';
import { ContentUtils } from '@kedao/utils';
import { imageControlItems } from '../../../configs/controls';
import Switch from '../../../components/common/Switch';
import './style.scss';
class Image extends React.Component {
constructor() {
super(...arguments);
this.state = {
toolbarVisible: false,
toolbarOffset: 0,
linkEditorVisible: false,
sizeEditorVisible: false,
tempLink: null,
tempWidth: null,
tempHeight: null
};
this.imageElement = React.createRef();
this.mediaEmbederInstance = React.createRef();
this.toolbarElement = React.createRef();
this.changeSize = (e) => {
const type = this.reSizeType;
if (!this.initialLeft) {
this.initialLeft = e.screenX;
this.initialTop = e.screenY;
}
if (type === 'rightbottom') {
this.initialHeight += e.screenY - this.initialTop;
this.initialWidth += e.screenX - this.initialLeft;
}
if (type === 'leftbottom') {
this.initialHeight += e.screenY - this.initialTop;
this.initialWidth += -e.screenX + this.initialLeft;
}
this.initialLeft = e.screenX;
this.initialTop = e.screenY;
};
this.moveImage = (e) => {
this.changeSize(e);
this.setState({
tempWidth: Math.abs(this.initialWidth),
tempHeight: Math.abs(this.initialHeight)
});
};
this.upImage = () => {
const { imageEqualRatio } = this.props;
if (imageEqualRatio) {
this.confirmImageSizeEqualRatio();
}
else {
this.confirmImageSize();
}
document.removeEventListener('mousemove', this.moveImage);
document.removeEventListener('mouseup', this.upImage);
};
this.repareChangeSize = (type) => (e) => {
var _a;
this.reSizeType = type;
const imageRect = (_a = this.imageElement.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
this.initialTop = 0;
this.initialLeft = 0;
this.initialWidth = imageRect.width;
this.initialHeight = imageRect.height;
this.zoom = imageRect.width / imageRect.height;
e.preventDefault();
document.addEventListener('mousemove', this.moveImage);
document.addEventListener('mouseup', this.upImage);
};
this.preventDragEvent = (event) => {
event.stopPropagation();
event.preventDefault();
};
this.handleDragStart = () => {
if (this.props.editor.editorProps.readOnly ||
this.props.editor.editorProps.disabled) {
return false;
}
window.__KEDAO_DRAGING__IMAGE__ = {
block: this.props.block,
mediaData: Object.assign({ type: 'IMAGE' }, this.props.mediaData)
};
this.setState({
toolbarVisible: false
}, () => {
this.unlockEditor();
});
return true;
};
this.handleDragEnd = () => {
window.__KEDAO_DRAGING__IMAGE__ = null;
return false;
};
this.executeCommand = (command) => {
if (typeof command === 'string') {
const [method, param] = command.split('|');
if (this[method]) {
this[method](param);
}
}
else if (typeof command === 'function') {
command(this.props.block, this.props.mediaData, this.props.editor.getValue());
}
};
this.removeImage = () => {
this.props.editor.setValue(ContentUtils.removeBlock(this.props.editor.getValue(), this.props.block));
this.unlockEditor();
};
this.toggleLinkEditor = () => {
this.setState((prevState) => ({
linkEditorVisible: !prevState.linkEditorVisible,
sizeEditorVisible: false
}));
};
this.toggleSizeEditor = () => {
this.setState((prevState) => ({
linkEditorVisible: false,
sizeEditorVisible: !prevState.sizeEditorVisible
}));
};
this.handleLinkInputKeyDown = (e) => {
if (e.keyCode === 13) {
this.confirmImageLink();
}
};
this.setImageLink = (e) => {
this.setState({ tempLink: e.currentTarget.value });
};
this.confirmImageLink = () => {
let { tempLink: link } = this.state;
const hookReturns = this.props.hooks('set-image-link', link)(link);
if (hookReturns === false) {
return false;
}
if (typeof hookReturns === 'string') {
link = hookReturns;
}
if (link !== null) {
this.props.editor.setValue(ContentUtils.setMediaData(this.props.editor.getValue(), this.props.entityKey, { link }));
window.setImmediate(this.props.editor.forceRender);
}
return true;
};
this.handleSizeInputKeyDown = (e) => {
if (e.keyCode === 13) {
this.confirmImageSize();
}
};
this.setImageWidth = ({ currentTarget }) => {
let { value } = currentTarget;
if (value && !isNaN(value)) {
value += 'px';
}
this.setState({
tempWidth: value
});
};
this.setImageHeight = ({ currentTarget }) => {
let { value } = currentTarget;
if (value && !isNaN(value)) {
value += 'px';
}
this.setState({
tempHeight: value
});
};
this.confirmImageSize = () => {
const { tempWidth: width, tempHeight: height } = this.state;
let newImageSize = {};
if (width !== null) {
newImageSize.width = width;
}
if (height !== null) {
newImageSize.height = height;
}
const hookReturns = this.props.hooks('set-image-size', newImageSize)(newImageSize);
if (hookReturns === false) {
return false;
}
if (hookReturns && (hookReturns.width || hookReturns.height)) {
newImageSize = hookReturns;
}
this.props.editor.setValue(ContentUtils.setMediaData(this.props.editor.getValue(), this.props.entityKey, newImageSize));
window.setImmediate(this.props.editor.forceRender);
return true;
};
this.confirmImageSizeEqualRatio = () => {
const { tempWidth: width, tempHeight: height } = this.state;
let equalWidth;
let equalHeight;
let newImageSize = {};
// 宽度过大 图片等比缩放
if (width / height > this.zoom) {
equalWidth = Math.floor(height * this.zoom);
this.setState({
tempWidth: equalWidth
});
equalHeight = height;
}
else if (width / height < this.zoom) {
equalHeight = Math.floor(width / this.zoom);
this.setState({
tempHeight: equalHeight
});
equalWidth = width;
}
if (equalWidth !== null) {
newImageSize.width = equalWidth;
}
if (equalHeight !== null) {
newImageSize.height = equalHeight;
}
const hookReturns = this.props.hooks('set-image-size', newImageSize)(newImageSize);
if (hookReturns === false) {
return false;
}
if (hookReturns && (hookReturns.width || hookReturns.height)) {
newImageSize = hookReturns;
}
this.props.editor.setValue(ContentUtils.setMediaData(this.props.editor.getValue(), this.props.entityKey, newImageSize));
window.setImmediate(this.props.editor.forceRender);
return true;
};
this.setImageFloat = (float) => {
let newFloat = float;
const hookReturns = this.props.hooks('set-image-float', newFloat)(newFloat);
if (hookReturns === false) {
return false;
}
if (typeof hookReturns === 'string') {
newFloat = hookReturns;
}
this.props.editor.setValue(ContentUtils.setMediaPosition(this.props.editor.getValue(), this.props.block, { newFloat }));
this.unlockEditor();
return true;
};
this.setImageAlignment = (alignment) => {
let newAlignment = alignment;
const hookReturns = this.props.hooks('set-image-alignment', newAlignment)(newAlignment);
if (hookReturns === false) {
return false;
}
if (typeof hookReturns === 'string') {
newAlignment = hookReturns;
}
this.props.editor.setValue(ContentUtils.setMediaPosition(this.props.editor.getValue(), this.props.block, { newAlignment }));
this.unlockEditor();
return true;
};
this.showToolbar = (event) => {
if (this.props.editor.editorProps.readOnly ||
this.props.editor.editorProps.disabled) {
return false;
}
event.preventDefault();
if (!this.state.toolbarVisible) {
this.setState({
toolbarVisible: true
}, () => {
this.lockEditor();
this.setState({ toolbarOffset: this.calcToolbarOffset() });
});
}
return true;
};
this.hideToolbar = (event) => {
event.preventDefault();
this.setState({
toolbarVisible: false
}, () => {
this.unlockEditor();
// this.props.editor.requestFocus()
});
};
}
lockEditor() {
this.props.editor.lockOrUnlockEditor(true);
}
unlockEditor() {
this.props.editor.lockOrUnlockEditor(false);
}
calcToolbarOffset() {
var _a, _b;
const { getContainerNode, containerNode } = this.props;
const container = getContainerNode ? getContainerNode() : containerNode;
if (!container) {
return 0;
}
const viewRect = container
.querySelector('.bf-content')
.getBoundingClientRect();
const toolbarRect = (_a = this.toolbarElement.current) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect();
const imageRect = (_b = this.imageElement.current) === null || _b === void 0 ? void 0 : _b.getBoundingClientRect();
const right = viewRect.right -
(imageRect.right - imageRect.width / 2 + toolbarRect.width / 2);
const left = imageRect.left +
imageRect.width / 2 -
toolbarRect.width / 2 -
viewRect.left;
if (right < 10) {
return right - 10;
}
else if (left < 10) {
return left * -1 + 10;
}
else {
return 0;
}
}
setImageLinkTarget(linkTarget) {
let newLinkTarget;
const hookReturns = this.props.hooks('set-image-link-target', linkTarget)(linkTarget);
if (hookReturns === false) {
return false;
}
if (typeof hookReturns === 'string') {
newLinkTarget = hookReturns;
}
newLinkTarget = newLinkTarget === '_blank' ? '' : '_blank';
this.props.editor.setValue(ContentUtils.setMediaData(this.props.editor.getValue(), this.props.entityKey, { newLinkTarget }));
window.setImmediate(this.props.editor.forceRender);
return true;
}
render() {
const { mediaData, language, imageControls, imageResizable } = this.props;
const { toolbarVisible, toolbarOffset, linkEditorVisible, sizeEditorVisible, tempWidth, tempHeight } = this.state;
const blockData = this.props.block.getData();
const float = blockData.get('float');
let alignment = blockData.get('alignment');
const { url, link, linkTarget, width, height, meta } = mediaData;
const imageStyles = {};
let clearFix = false;
if (float) {
alignment = null;
}
else if (alignment === 'left') {
imageStyles.float = 'left';
clearFix = true;
}
else if (alignment === 'right') {
imageStyles.float = 'right';
clearFix = true;
}
else if (alignment === 'center') {
imageStyles.textAlign = 'center';
}
else {
imageStyles.float = 'left';
clearFix = true;
}
const renderedControlItems = imageControls.map((item) => {
if (typeof item === 'string' && imageControlItems[item]) {
return (React.createElement("a", { className: item === 'link' && link ? 'active' : '', role: "presentation", key: uuidv4(), onClick: () => this.executeCommand(imageControlItems[item].command) }, imageControlItems[item].text));
}
else if (item && (item.render || item.text)) {
return item.render
? (item.render(mediaData, this.props.block))
: (React.createElement("a", { key: uuidv4(), role: "presentation", onClick: () => item.onClick && this.executeCommand(item.onClick) }, item.text));
}
else {
return null;
}
});
return (React.createElement("div", { className: "bf-media" },
React.createElement("div", { style: imageStyles, draggable: true, onMouseEnter: this.showToolbar, onMouseMove: this.showToolbar, onMouseLeave: this.hideToolbar, onDragStart: this.handleDragStart, onDragEnd: this.handleDragEnd, ref: this.mediaEmbederInstance, className: "bf-image" },
toolbarVisible
? (React.createElement("div", { style: { marginLeft: toolbarOffset }, ref: this.toolbarElement, "data-float": float, "data-align": alignment, className: "bf-media-toolbar" },
linkEditorVisible
? (React.createElement("div", { className: "bf-image-link-editor" },
React.createElement("div", { className: "editor-input-group" },
React.createElement("input", { type: "text", placeholder: language.linkEditor.inputWithEnterPlaceHolder, onKeyDown: this.handleLinkInputKeyDown, onChange: this.setImageLink, defaultValue: link }),
React.createElement("button", { type: "button", onClick: this.confirmImageLink }, language.base.confirm)),
React.createElement("div", { className: "switch-group" },
React.createElement(Switch, { active: linkTarget === '_blank', onClick: () => this.setImageLinkTarget(linkTarget) }),
React.createElement("label", null, language.linkEditor.openInNewWindow))))
: null,
sizeEditorVisible
? (React.createElement("div", { className: "bf-image-size-editor" },
React.createElement("div", { className: "editor-input-group" },
React.createElement("input", { type: "text", placeholder: language.base.width, onKeyDown: this.handleSizeInputKeyDown, onChange: this.setImageWidth, defaultValue: width }),
React.createElement("input", { type: "text", placeholder: language.base.height, onKeyDown: this.handleSizeInputKeyDown, onChange: this.setImageHeight, defaultValue: height }),
React.createElement("button", { type: "button", onClick: this.confirmImageSize }, language.base.confirm))))
: null,
renderedControlItems,
React.createElement("i", { style: { marginLeft: toolbarOffset * -1 }, className: "bf-media-toolbar-arrow" })))
: null,
React.createElement("div", { style: {
position: 'relative',
width: `${width}px`,
height: `${height}px`,
display: 'inline-block'
} },
React.createElement("img", Object.assign({ ref: this.imageElement, src: url, alt: "Alt", width: width, height: height }, meta)),
toolbarVisible && imageResizable
? (React.createElement("div", { role: "presentation", className: "bf-csize-icon right-bottom", onMouseDown: this.repareChangeSize('rightbottom') }))
: null,
toolbarVisible && imageResizable
? (React.createElement("div", { role: "presentation", className: "bf-csize-icon left-bottom", onMouseDown: this.repareChangeSize('leftbottom') }))
: null,
React.createElement("div", { className: `bf-pre-csize ${this.reSizeType}`, style: { width: `${tempWidth}px`, height: `${tempHeight}px` } }))),
clearFix && (React.createElement("div", { className: "clearfix", style: { clear: 'both', height: 0, lineHeight: 0, float: 'none' } }))));
}
}
export default Image;
//# sourceMappingURL=index.js.map