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

feat: added custom image resolver #253

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
54 changes: 53 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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.<string>` | 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.<string>} - valid url
*/
resolveUrlByFileData(fileData) {
// your ajax request for image url resolving
return MyAjax.download(fileData).then((data) => {
return URL.createObjectURL(data);
})
}
}
}
}
}

...
});
```
52 changes: 52 additions & 0 deletions src/imageResolver.js
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
41 changes: 36 additions & 5 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -41,6 +42,7 @@
* @property {string} file.url — image URL
*/

import ImageResolver from './imageResolver';
import './index.css';

import Ui from './ui';
Expand All @@ -63,16 +65,20 @@ import { IconAddBorder, IconStretch, IconAddBackground, IconPicture } from '@cod
* @property {object} [uploader] - optional custom uploader
* @property {function(File): Promise.<UploadResponseFormat>} [uploader.uploadByFile] - method that upload image by File
* @property {function(string): Promise.<UploadResponseFormat>} [uploader.uploadByUrl] - method that upload image by URL
* @property {object} [imageResolver] - optional custom image data resolver
* @property {function(any): Promise.<string>} [imageResolver.resolveUrlByFileData] - method that resolves image url by file data
*/

/**
* @typedef {object} UploadResponseFormat
* @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 {
/**
Expand Down Expand Up @@ -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
*/
Expand Down Expand Up @@ -205,7 +220,7 @@ export default class ImageTool {
* @public
*/
validate(savedData) {
return savedData.file && savedData.file.url;
return savedData.file;
}

/**
Expand Down Expand Up @@ -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));
}
}

Expand Down Expand Up @@ -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
*
Expand Down
2 changes: 0 additions & 2 deletions src/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ export default class Ui {
*/
eventName = 'loadeddata';
}

/**
* Compose tag with defined attributes
*
Expand Down Expand Up @@ -250,4 +249,3 @@ export default class Ui {
this.nodes.wrapper.classList.toggle(`${this.CSS.wrapper}--${tuneName}`, status);
}
}

1 change: 0 additions & 1 deletion src/uploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,3 @@ export default class Uploader {
});
}
}

Loading