diff --git a/README.md b/README.md index a0d59f39..19665815 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ Image Tool supports these configuration parameters: | captionPlaceholder | `string` | (default: `Caption`) Placeholder for Caption input | | buttonContent | `string` | Allows to override HTML content of «Select file» button | | uploader | `{{uploadByFile: function, uploadByUrl: function}}` | Optional custom uploading methods. See details below. | +| imageResolver | `{{resolveUrlByFileData: function}}` | Optional custom image resolving method. See details below. | | actions | `array` | Array with custom actions to show in the tool's settings menu. See details below. | Note that if you don't implement your custom uploader methods, the `endpoints` param is required. @@ -178,7 +179,8 @@ The response of your uploader **should** cover the following format: **success** - uploading status. 1 for successful, 0 for failed -**file** - uploaded file data. **Must** contain an `url` field with full public path to the uploaded image. +**file** - uploaded file data. **Must** contain an `url` field with full public path to the uploaded image or data key for using it in custom image resolver, or you +can use additional fields for custom resolving. Also, can contain any additional fields you want to store. For example, width, height, id etc. All additional fields will be saved at the `file` object of output data. @@ -281,3 +283,53 @@ var editor = EditorJS({ ... }); ``` + +## Providing custom image url resolving methods + +As mentioned at the Config Params section, you have an ability to provide own custom image resolving method. +It is a quite simple: implement `resolveUrlByFileData` method and pass it via `imageResolver` config param. +Method must return a Promise that resolves with url, which we can pass to the image element and show it + + +| Method | Arguments | Return value | Description | +| -------------- | --------- | ------------- | ------------| +| resolveUrlByFileData | `fileData`| `Promise.` | Resolve image url by file data | + +`fileData` - any data, which your server returns after uploading image. + +Example: + +```js +import ImageTool from '@editorjs/image'; + +var editor = EditorJS({ + ... + + tools: { + ... + image: { + class: ImageTool, + config: { + /** + * Custom image resolver + */ + imageResolver: { + /** + * Resolve image url by file data. + * @param {any} fileData - data required for image url resolving + * @return {Promise.} - valid url + */ + resolveUrlByFileData(fileData) { + // your ajax request for image url resolving + return MyAjax.download(fileData).then((data) => { + return URL.createObjectURL(data); + }) + } + } + } + } + } + + ... +}); +``` \ No newline at end of file diff --git a/src/imageResolver.js b/src/imageResolver.js new file mode 100644 index 00000000..a0754852 --- /dev/null +++ b/src/imageResolver.js @@ -0,0 +1,52 @@ +import isPromise from './utils/isPromise'; + +/** + * Module for file image resolving. Handle case, when you have to use custom image resolving method + */ +export default class ImageResolver { + /** + * @param {object} params - image resolver module params + * @param {ImageConfig} params.config - image tool config + * @param {Function} params.onError - callback for resolving errors + */ + constructor({ config, onResolve, onError }) { + this.config = config; + this.onError = onError; + } + + /** + * Try to resolve image url by file data and fill it using stored data + * + * @param {any} fileData - file data, from which file url need to be resolved + */ + async resolveUrlByFileData(fileData) { + /** + * Check that custom url resolver passed + */ + if (this.config.imageResolver && typeof this.config.imageResolver.resolveUrlByFileData === 'function') { + const resolveFileData = this.config.imageResolver.resolveUrlByFileData(fileData); + + if (!isPromise(resolveFileData)) { + console.warn('Custom downloader method download should return a Promise'); + } + + try { + /** + * Return resolver url + */ + return await resolveFileData; + } catch (error) { + this.onError(error); + } + } else { + /** + * If there is no custom resolve method, fileData must have correct url property, which have no need in resolving + */ + if (!fileData.url) { + this.onError('Incorrect data: file data should contain url'); + } + + return fileData.url; + } + } +} diff --git a/src/index.js b/src/index.js index ff00b214..24e56050 100644 --- a/src/index.js +++ b/src/index.js @@ -11,6 +11,7 @@ * 2) uploader.js — module that has methods for sending files via AJAX: from device, by URL or File pasting * 3) ui.js — module for UI manipulations: render, showing preloader, etc * 4) tunes.js — working with Block Tunes: render buttons, handle clicks + * 5) imageResolver.js - module, which helps to use custom image resolver via stored file data key * * For debug purposes there is a testing server * that can save uploaded files and return a Response {@link UploadResponseFormat} @@ -41,6 +42,7 @@ * @property {string} file.url — image URL */ +import ImageResolver from './imageResolver'; import './index.css'; import Ui from './ui'; @@ -63,6 +65,8 @@ import { IconAddBorder, IconStretch, IconAddBackground, IconPicture } from '@cod * @property {object} [uploader] - optional custom uploader * @property {function(File): Promise.} [uploader.uploadByFile] - method that upload image by File * @property {function(string): Promise.} [uploader.uploadByUrl] - method that upload image by URL + * @property {object} [imageResolver] - optional custom image data resolver + * @property {function(any): Promise.} [imageResolver.resolveUrlByFileData] - method that resolves image url by file data */ /** @@ -70,9 +74,11 @@ import { IconAddBorder, IconStretch, IconAddBackground, IconPicture } from '@cod * @description This format expected from backend on file uploading * @property {number} success - 1 for successful uploading, 0 for failure * @property {object} file - Object with file data. - * 'url' is required, + * 'url' contains image url + * or you can store file data in additional fields for custom resolver * also can contain any additional data that will be saved and passed back - * @property {string} file.url - [Required] image source URL + * this data can be used for custom downloading + * @property {string} file.url - image source URL */ export default class ImageTool { /** @@ -152,8 +158,17 @@ export default class ImageTool { buttonContent: config.buttonContent || '', uploader: config.uploader || undefined, actions: config.actions || [], + imageResolver: config.imageResolver || undefined, }; + /** + * Module for image resolving + */ + this.imageResolver = new ImageResolver({ + config: this.config, + onError: this.resolvingFileError, + }); + /** * Module for file uploading */ @@ -205,7 +220,7 @@ export default class ImageTool { * @public */ validate(savedData) { - return savedData.file && savedData.file.url; + return savedData.file; } /** @@ -382,8 +397,8 @@ export default class ImageTool { set image(file) { this._data.file = file || {}; - if (file && file.url) { - this.ui.fillImage(file.url); + if (file) { + this.imageResolver.resolveUrlByFileData(file).then(url => this.ui.fillImage(url)); } } @@ -420,6 +435,22 @@ export default class ImageTool { this.ui.hidePreloader(); } + /** + * Handle image resolver errors + * + * @private + * @param {string} errorText - resolving error text + * @returns {void} + */ + resolvingFileError(errorText) { + console.log('Image Tool: resolving failed because of', errorText); + + this.api.notifier.show({ + message: this.api.i18n.t('Couldn’t resolve image. Please try another.'), + style: 'error', + }); + } + /** * Callback fired when Block Tune is activated * diff --git a/src/ui.js b/src/ui.js index 8609ef21..9676a071 100644 --- a/src/ui.js +++ b/src/ui.js @@ -188,7 +188,6 @@ export default class Ui { */ eventName = 'loadeddata'; } - /** * Compose tag with defined attributes * @@ -250,4 +249,3 @@ export default class Ui { this.nodes.wrapper.classList.toggle(`${this.CSS.wrapper}--${tuneName}`, status); } } - diff --git a/src/uploader.js b/src/uploader.js index bfa6e2a5..d42e96d4 100644 --- a/src/uploader.js +++ b/src/uploader.js @@ -176,4 +176,3 @@ export default class Uploader { }); } } -