UNPKG

@kedao/editor

Version:

Rich Text Editor Based On Draft.js

405 lines 18.2 kB
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