@limetech/lime-elements
Version:
143 lines (142 loc) • 5.87 kB
JavaScript
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