Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@uppy/thumbnail-generator: refactor to ESM #3734

Merged
merged 1 commit into from
May 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ module.exports = {
'packages/@uppy/status-bar/src/**/*.js',
'packages/@uppy/svelte/src/**/*.js',
'packages/@uppy/svelte/rollup.config.js',
'packages/@uppy/thumbnail-generator/src/**/*.js',
'packages/@uppy/tus/src/**/*.js',
'packages/@uppy/url/src/**/*.js',
'packages/@uppy/vue/src/**/*.js',
Expand Down
2 changes: 2 additions & 0 deletions packages/@uppy/thumbnail-generator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"license": "MIT",
"main": "lib/index.js",
"types": "types/index.d.ts",
"type": "module",
"keywords": [
"file uploader",
"uppy",
Expand All @@ -26,6 +27,7 @@
"exifr": "^7.0.0"
},
"devDependencies": {
"@jest/globals": "^27.4.2",
"namespace-emitter": "2.0.1"
},
"peerDependencies": {
Expand Down
221 changes: 114 additions & 107 deletions packages/@uppy/thumbnail-generator/src/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,110 @@
const { UIPlugin } = require('@uppy/core')
const dataURItoBlob = require('@uppy/utils/lib/dataURItoBlob')
const isObjectURL = require('@uppy/utils/lib/isObjectURL')
const isPreviewSupported = require('@uppy/utils/lib/isPreviewSupported')
const { rotation } = require('exifr/dist/mini.umd.js')
import { UIPlugin } from '@uppy/core'
import dataURItoBlob from '@uppy/utils/lib/dataURItoBlob'
import isObjectURL from '@uppy/utils/lib/isObjectURL'
import isPreviewSupported from '@uppy/utils/lib/isPreviewSupported'
import { rotation } from 'exifr/dist/mini.umd.js'

const locale = require('./locale')
import locale from './locale.js'
import packageJson from '../package.json'

/**
* Save a <canvas> element's content to a Blob object.
*
* @param {HTMLCanvasElement} canvas
* @returns {Promise}
*/
function canvasToBlob (canvas, type, quality) {
try {
canvas.getContext('2d').getImageData(0, 0, 1, 1)
} catch (err) {
if (err.code === 18) {
return Promise.reject(new Error('cannot read image, probably an svg with external resources'))
}
}

if (canvas.toBlob) {
return new Promise(resolve => {
canvas.toBlob(resolve, type, quality)
}).then((blob) => {
if (blob === null) {
throw new Error('cannot read image, probably an svg with external resources')
}
return blob
})
}
return Promise.resolve().then(() => {
return dataURItoBlob(canvas.toDataURL(type, quality), {})
}).then((blob) => {
if (blob === null) {
throw new Error('could not extract blob, probably an old browser')
}
return blob
})
}

function rotateImage (image, translate) {
let w = image.width
let h = image.height

if (translate.deg === 90 || translate.deg === 270) {
w = image.height
h = image.width
}

const canvas = document.createElement('canvas')
canvas.width = w
canvas.height = h

const context = canvas.getContext('2d')
context.translate(w / 2, h / 2)
if (translate.canvas) {
context.rotate(translate.rad)
context.scale(translate.scaleX, translate.scaleY)
}
context.drawImage(image, -image.width / 2, -image.height / 2, image.width, image.height)

return canvas
}

/**
* Make sure the image doesn’t exceed browser/device canvas limits.
* For ios with 256 RAM and ie
*/
function protect (image) {
// https://stackoverflow.com/questions/6081483/maximum-size-of-a-canvas-element

const ratio = image.width / image.height

const maxSquare = 5000000 // ios max canvas square
const maxSize = 4096 // ie max canvas dimensions

let maxW = Math.floor(Math.sqrt(maxSquare * ratio))
let maxH = Math.floor(maxSquare / Math.sqrt(maxSquare * ratio))
if (maxW > maxSize) {
maxW = maxSize
maxH = Math.round(maxW / ratio)
}
if (maxH > maxSize) {
maxH = maxSize
maxW = Math.round(ratio * maxH)
}
if (image.width > maxW) {
const canvas = document.createElement('canvas')
canvas.width = maxW
canvas.height = maxH
canvas.getContext('2d').drawImage(image, 0, 0, maxW, maxH)
return canvas
}

return image
}

/**
* The Thumbnail Generator plugin
*/

module.exports = class ThumbnailGenerator extends UIPlugin {
static VERSION = require('../package.json').version
export default class ThumbnailGenerator extends UIPlugin {
static VERSION = packageJson.version

constructor (uppy, opts) {
super(uppy, opts)
Expand Down Expand Up @@ -84,7 +177,7 @@ module.exports = class ThumbnailGenerator extends UIPlugin {
* account. If neither width nor height are given, the default dimension
* is used.
*/
getProportionalDimensions (img, width, height, rotation) {
getProportionalDimensions (img, width, height, rotation) { // eslint-disable-line no-shadow
let aspect = img.width / img.height
if (rotation === 90 || rotation === 270) {
aspect = img.height / img.width
Expand All @@ -110,39 +203,6 @@ module.exports = class ThumbnailGenerator extends UIPlugin {
}
}

/**
* Make sure the image doesn’t exceed browser/device canvas limits.
* For ios with 256 RAM and ie
*/
protect (image) {
// https://stackoverflow.com/questions/6081483/maximum-size-of-a-canvas-element

const ratio = image.width / image.height

const maxSquare = 5000000 // ios max canvas square
const maxSize = 4096 // ie max canvas dimensions

let maxW = Math.floor(Math.sqrt(maxSquare * ratio))
let maxH = Math.floor(maxSquare / Math.sqrt(maxSquare * ratio))
if (maxW > maxSize) {
maxW = maxSize
maxH = Math.round(maxW / ratio)
}
if (maxH > maxSize) {
maxH = maxSize
maxW = Math.round(ratio * maxH)
}
if (image.width > maxW) {
const canvas = document.createElement('canvas')
canvas.width = maxW
canvas.height = maxH
canvas.getContext('2d').drawImage(image, 0, 0, maxW, maxH)
image = canvas
}

return image
}

/**
* Resize an image to the target `width` and `height`.
*
Expand All @@ -152,9 +212,9 @@ module.exports = class ThumbnailGenerator extends UIPlugin {
// Resizing in steps refactored to use a solution from
// https://blog.uploadcare.com/image-resize-in-browsers-is-broken-e38eed08df01

image = this.protect(image)
let img = this.protect(image)

let steps = Math.ceil(Math.log2(image.width / targetWidth))
let steps = Math.ceil(Math.log2(img.width / targetWidth))
if (steps < 1) {
steps = 1
}
Expand All @@ -166,73 +226,14 @@ module.exports = class ThumbnailGenerator extends UIPlugin {
const canvas = document.createElement('canvas')
canvas.width = sW
canvas.height = sH
canvas.getContext('2d').drawImage(image, 0, 0, sW, sH)
image = canvas
canvas.getContext('2d').drawImage(img, 0, 0, sW, sH)
img = canvas

sW = Math.round(sW / x)
sH = Math.round(sH / x)
}

return image
}

rotateImage (image, translate) {
let w = image.width
let h = image.height

if (translate.deg === 90 || translate.deg === 270) {
w = image.height
h = image.width
}

const canvas = document.createElement('canvas')
canvas.width = w
canvas.height = h

const context = canvas.getContext('2d')
context.translate(w / 2, h / 2)
if (translate.canvas) {
context.rotate(translate.rad)
context.scale(translate.scaleX, translate.scaleY)
}
context.drawImage(image, -image.width / 2, -image.height / 2, image.width, image.height)

return canvas
}

/**
* Save a <canvas> element's content to a Blob object.
*
* @param {HTMLCanvasElement} canvas
* @returns {Promise}
*/
canvasToBlob (canvas, type, quality) {
try {
canvas.getContext('2d').getImageData(0, 0, 1, 1)
} catch (err) {
if (err.code === 18) {
return Promise.reject(new Error('cannot read image, probably an svg with external resources'))
}
}

if (canvas.toBlob) {
return new Promise(resolve => {
canvas.toBlob(resolve, type, quality)
}).then((blob) => {
if (blob === null) {
throw new Error('cannot read image, probably an svg with external resources')
}
return blob
})
}
return Promise.resolve().then(() => {
return dataURItoBlob(canvas.toDataURL(type, quality), {})
}).then((blob) => {
if (blob === null) {
throw new Error('could not extract blob, probably an old browser')
}
return blob
})
return img
}

/**
Expand All @@ -255,7 +256,7 @@ module.exports = class ThumbnailGenerator extends UIPlugin {
const current = this.uppy.getFile(this.queue.shift())
if (!current) {
this.uppy.log('[ThumbnailGenerator] file was removed before a thumbnail could be generated, but not removed from the queue. This is probably a bug', 'error')
return
return Promise.resolve()
}
return this.requestThumbnail(current)
.catch(() => {}) // eslint-disable-line node/handle-callback-err
Expand All @@ -264,6 +265,7 @@ module.exports = class ThumbnailGenerator extends UIPlugin {
this.queueProcessing = false
this.uppy.log('[ThumbnailGenerator] Emptied thumbnail queue')
this.uppy.emit('thumbnail:all-generated')
return Promise.resolve()
}

requestThumbnail (file) {
Expand Down Expand Up @@ -396,3 +398,8 @@ module.exports = class ThumbnailGenerator extends UIPlugin {
}
}
}

// TODO: remove these methods from the prototype in the next major.
ThumbnailGenerator.prototype.canvasToBlob = canvasToBlob
ThumbnailGenerator.prototype.protect = protect
ThumbnailGenerator.prototype.rotateImage = rotateImage
15 changes: 9 additions & 6 deletions packages/@uppy/thumbnail-generator/src/index.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const { UIPlugin } = require('@uppy/core')
const emitter = require('namespace-emitter')
const ThumbnailGeneratorPlugin = require('./index')
import { describe, it, expect, jest } from '@jest/globals'
import { UIPlugin } from '@uppy/core'
import emitter from 'namespace-emitter'
import ThumbnailGeneratorPlugin from './index.js'

const delay = duration => new Promise(resolve => setTimeout(resolve, duration))

Expand Down Expand Up @@ -139,6 +140,9 @@ describe('uploader/ThumbnailGeneratorPlugin', () => {
URL.revokeObjectURL = jest.fn(() => null)

try {
const file1 = { id: 1, name: 'bar.jpg', type: 'image/jpeg', data: new Blob() }
const file2 = { id: 2, name: 'bar2.jpg', type: 'image/jpeg', data: new Blob() }

plugin.createThumbnail = jest.fn(async () => {
await delay(50)
return 'blob:http://uppy.io/fake-thumbnail'
Expand All @@ -148,8 +152,6 @@ describe('uploader/ThumbnailGeneratorPlugin', () => {
if (id === 2) file2.preview = preview
})

const file1 = { id: 1, name: 'bar.jpg', type: 'image/jpeg', data: new Blob() }
const file2 = { id: 2, name: 'bar2.jpg', type: 'image/jpeg', data: new Blob() }
core.mockFile(file1.id, file1)
core.emit('file-added', file1)
core.mockFile(file2.id, file2)
Expand Down Expand Up @@ -191,7 +193,8 @@ describe('uploader/ThumbnailGeneratorPlugin', () => {
expect(file.id).toBe(expected.shift())
expect(preview).toBe(`blob:${file.id}.png`)
} catch (err) {
return reject(err)
reject(err)
return
}
if (expected.length === 0) resolve()
})
Expand Down
2 changes: 1 addition & 1 deletion packages/@uppy/thumbnail-generator/src/locale.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
export default {
strings: {
generatingThumbnails: 'Generating thumbnails...',
},
Expand Down
13 changes: 13 additions & 0 deletions website/src/docs/thumbnail-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ uppy.use(ThumbnailGenerator, {

A unique identifier for this plugin. It defaults to `'ThumbnailGenerator'`.

### `locale: {}`

<!-- eslint-disable no-restricted-globals, no-multiple-empty-lines -->

```js
export default {
strings: {
generatingThumbnails: 'Generating thumbnails...',
},
}

```

### `thumbnailWidth: 200`

Width of the resulting thumbnail. Default thumbnail dimension is 200px. Thumbnails are always proportional and not cropped. If width is provided, height is calculated automatically to match ratio.
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10149,6 +10149,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "@uppy/thumbnail-generator@workspace:packages/@uppy/thumbnail-generator"
dependencies:
"@jest/globals": ^27.4.2
"@uppy/utils": "workspace:^"
exifr: ^7.0.0
namespace-emitter: 2.0.1
Expand Down