@uppy/image-editor
Version:
Image editor and cropping UI
134 lines (133 loc) • 4.57 kB
JavaScript
import { jsx as _jsx } from "preact/jsx-runtime";
import { UIPlugin } from '@uppy/core';
import { h } from 'preact';
import packageJson from '../package.json' with { type: 'json' };
import Editor from './Editor.js';
import locale from './locale.js';
const defaultCropperOptions = {
viewMode: 0,
background: false,
autoCropArea: 1,
responsive: true,
minCropBoxWidth: 70,
minCropBoxHeight: 70,
croppedCanvasOptions: {},
initialAspectRatio: 0,
};
const defaultActions = {
revert: true,
rotate: true,
granularRotate: true,
flip: true,
zoomIn: true,
zoomOut: true,
cropSquare: true,
cropWidescreen: true,
cropWidescreenVertical: true,
};
const defaultOptions = {
// `quality: 1` increases the image size by orders of magnitude - 0.8 seems to be the sweet spot.
// see https://github.com/fengyuanchen/cropperjs/issues/538#issuecomment-1776279427
quality: 0.8,
actions: defaultActions,
cropperOptions: defaultCropperOptions,
};
export default class ImageEditor extends UIPlugin {
static VERSION = packageJson.version;
cropper;
constructor(uppy, opts) {
super(uppy, {
...defaultOptions,
...opts,
actions: {
...defaultActions,
...opts?.actions,
},
cropperOptions: {
...defaultCropperOptions,
...opts?.cropperOptions,
},
});
this.id = this.opts.id || 'ImageEditor';
this.title = 'Image Editor';
this.type = 'editor';
this.defaultLocale = locale;
this.i18nInit();
}
canEditFile(file) {
if (!file.type || file.isRemote) {
return false;
}
const fileTypeSpecific = file.type.split('/')[1];
if (/^(jpe?g|gif|png|bmp|webp)$/.test(fileTypeSpecific)) {
return true;
}
return false;
}
save = () => {
const saveBlobCallback = (blob) => {
const { currentImage } = this.getPluginState();
this.uppy.setFileState(currentImage.id, {
// Reinserting image's name and type, because .toBlob loses both.
data: new File([blob], currentImage.name ?? this.i18n('unnamed'), {
type: blob.type,
}),
size: blob.size,
preview: undefined,
});
const updatedFile = this.uppy.getFile(currentImage.id);
this.uppy.emit('thumbnail:request', updatedFile);
this.setPluginState({
currentImage: updatedFile,
});
this.uppy.emit('file-editor:complete', updatedFile);
};
const { currentImage } = this.getPluginState();
// Fixes black 1px lines on odd-width images.
// This should be removed when cropperjs fixes this issue.
// (See https://github.com/transloadit/uppy/issues/4305 and https://github.com/fengyuanchen/cropperjs/issues/551).
const croppedCanvas = this.cropper.getCroppedCanvas({});
if (croppedCanvas.width % 2 !== 0) {
this.cropper.setData({ width: croppedCanvas.width - 1 });
}
if (croppedCanvas.height % 2 !== 0) {
this.cropper.setData({ height: croppedCanvas.height - 1 });
}
this.cropper
.getCroppedCanvas(this.opts.cropperOptions.croppedCanvasOptions)
.toBlob(saveBlobCallback, currentImage.type, this.opts.quality);
};
storeCropperInstance = (cropper) => {
this.cropper = cropper;
};
selectFile = (file) => {
this.uppy.emit('file-editor:start', file);
this.setPluginState({
currentImage: file,
});
};
install() {
this.setPluginState({
currentImage: null,
});
const { target } = this.opts;
if (target) {
this.mount(target, this);
}
}
uninstall() {
const { currentImage } = this.getPluginState();
if (currentImage) {
const file = this.uppy.getFile(currentImage.id);
this.uppy.emit('file-editor:cancel', file);
}
this.unmount();
}
render() {
const { currentImage } = this.getPluginState();
if (currentImage === null || currentImage.isRemote) {
return null;
}
return (_jsx(Editor, { currentImage: currentImage, storeCropperInstance: this.storeCropperInstance, save: this.save, opts: this.opts, i18n: this.i18n }));
}
}