UNPKG

@limetech/lime-elements

Version:
143 lines (142 loc) 5.87 kB
import { Plugin } from 'prosemirror-state'; import translate from '../../../../../global/translations'; import { applyImageStyles } from './node'; const MIN_WIDTH = 10; export const createImageViewPlugin = (language) => { return new Plugin({ props: { nodeViews: { image: (node, view, getPos) => { return new ImageView(node, view, getPos, language); }, }, }, }); }; class ImageView { constructor(node, view, getPos, language) { this.createResizeHandle = (position) => { const handle = document.createElement('div'); handle.className = `resize-handle ${position}`; handle.setAttribute('role', 'slider'); handle.setAttribute('aria-label', translate.get('editor-image-view.resize-handle', this.language)); handle.setAttribute('tabindex', '0'); handle.setAttribute('aria-valuemin', MIN_WIDTH.toString()); handle.setAttribute('aria-valuenow', this.img.offsetWidth.toString()); handle.setAttribute('aria-valuetext', `${this.img.offsetWidth} pixels`); handle.setAttribute('aria-grabbed', 'false'); handle.addEventListener('pointerdown', (e) => { handle.setAttribute('aria-grabbed', 'true'); this.onResizeStart(e, position); }); return handle; }; this.onResizeStart = (event, position) => { event.preventDefault(); const handle = event.target; const startX = event.clientX; const startWidth = this.img.offsetWidth; const onPointerMove = (e) => { const delta = e.clientX - startX; const widthDelta = position === 'top-left' ? -delta : delta; const newWidth = Math.max(MIN_WIDTH, startWidth + widthDelta); this.img.style.width = `${newWidth}px`; const handles = this.dom.querySelectorAll('.resize-handle'); for (const resizeHandle of handles) { resizeHandle.setAttribute('aria-valuenow', newWidth.toString()); resizeHandle.setAttribute('aria-valuetext', `${newWidth} pixels`); } }; const onPointerUp = () => { window.removeEventListener('pointermove', onPointerMove); window.removeEventListener('pointerup', onPointerUp); handle.setAttribute('aria-grabbed', 'false'); this.persistDimensions(); }; window.addEventListener('pointermove', onPointerMove); window.addEventListener('pointerup', onPointerUp); }; this.createLoadingState = () => { this.dom.setAttribute('aria-live', 'polite'); this.dom.setAttribute('aria-busy', 'true'); this.dom.setAttribute('aria-label', translate.get('editor-image-view.loading', this.language, { filename: this.node.attrs.alt || 'file', })); const spinnerElement = document.createElement('limel-linear-progress'); spinnerElement.setAttribute('indeterminate', 'true'); this.dom.append(spinnerElement); }; this.createSuccessState = () => { this.dom.setAttribute('aria-live', 'polite'); this.dom.setAttribute('aria-busy', 'false'); this.dom.setAttribute('aria-label', translate.get('editor-image-view.success', this.language, { filename: this.node.attrs.alt || 'file', })); const bottomRightHandle = this.createResizeHandle('bottom-right'); const topLeftHandle = this.createResizeHandle('top-left'); this.dom.append(bottomRightHandle); this.dom.append(topLeftHandle); }; this.createFailedState = () => { this.dom.setAttribute('aria-live', 'assertive'); this.dom.setAttribute('aria-busy', 'false'); this.dom.setAttribute('aria-label', translate.get('editor-image-view.failed', this.language, { filename: this.node.attrs.alt || 'file', })); }; this.cleanUpPreviousState = () => { for (const child of this.dom.childNodes) { if (!(child instanceof HTMLImageElement)) { child.remove(); } } }; this.transitionBetweenStates = () => { var _a; this.cleanUpPreviousState(); this.dom.className = `image-wrapper state-${this.node.attrs.state}`; const stateHandlers = { loading: this.createLoadingState, success: this.createSuccessState, failed: this.createFailedState, }; const state = this.node.attrs.state; (_a = stateHandlers[state]) === null || _a === void 0 ? void 0 : _a.call(stateHandlers); }; this.transitioningBetweenSuccessStates = (newNode) => { return (this.node.attrs.state === 'success' && newNode.attrs.state === 'success'); }; this.node = node; this.view = view; this.getPos = getPos; this.language = language; this.dom = document.createElement('div'); this.dom.className = `image-wrapper state-${node.attrs.state}`; this.img = document.createElement('img'); this.img.src = node.attrs.src; this.img.alt = node.attrs.alt; applyImageStyles(this.img, node); this.img.addEventListener('load', () => { this.persistDimensions(); }); this.dom.append(this.img); this.transitionBetweenStates(); } persistDimensions() { this.view.dispatch(this.view.state.tr.setNodeMarkup(this.getPos(), undefined, Object.assign(Object.assign({}, this.node.attrs), { height: `${this.img.offsetHeight}px`, width: `${this.img.offsetWidth}px`, minHeight: `${this.img.offsetHeight}px`, minWidth: `${this.img.offsetWidth}px` }))); } // Ensure that the existing NodeView is reused rather than recreated. // Recreating the NodeView will cause flickering between states. update(node) { if (!this.transitioningBetweenSuccessStates(node)) { this.img.src = node.attrs.src; this.img.alt = node.attrs.alt; applyImageStyles(this.img, node); } this.node = node; this.transitionBetweenStates(); return true; } } //# sourceMappingURL=view.js.map