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
52 changes: 51 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. |
| downloader | `{{download: function}}` | Optional custom downloading 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,7 @@ 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 downloader.
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 +282,52 @@ var editor = EditorJS({
...
});
```

## Providing custom downloading method

As mentioned at the Config Params section, you have an ability to provide own custom downloading method.
It is a quite simple: implement `download` method and pass it via `downloader` config param.
Method must return a Promise that resolves with url, which we can pass to the element and show it
slaveeks marked this conversation as resolved.
Show resolved Hide resolved


| Method | Arguments | Return value | Description |
| -------------- | --------- | ------------- | ------------|
| download | `fileData`| `{Promise.<string>}` | Download file by file data convert it and return valid file url |

Example:

```js
import ImageTool from '@editorjs/image';

var editor = EditorJS({
...

tools: {
...
image: {
class: ImageTool,
config: {
/**
* Custom downloader
*/
downloader: {
neSpecc marked this conversation as resolved.
Show resolved Hide resolved
/**
* Download file from the server using file data.
* @param {string} fileData - data required for downloading
* @return {Promise.<string>} - valid url
*/
uploadByU
download(fileData) {
neSpecc marked this conversation as resolved.
Show resolved Hide resolved
// your ajax request for downloading
return MyAjax.download(fileData).then((data) => {
return URL.createObjectURL(data);
})
}
}
}
}
}

...
});
```
51 changes: 51 additions & 0 deletions src/downloader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import isPromise from './utils/isPromise';

/**
* Module for file downloading. Handle case, when you have to use custom downloading method
*/
export default class Downloader {
slaveeks marked this conversation as resolved.
Show resolved Hide resolved
/**
* @param {object} params - downloader module params
* @param {ImageConfig} params.config - image tool config
* @param {Function} params.onDownload - callback which is called, when file is downloaded
* @param {Function} params.onError - callback for downloading errors
*/
constructor({ config, onDownload, onError }) {
this.config = config;
this.onDownload = onDownload;
this.onError = onError;
}

/**
* Try to download file data and fill it using stored data
*
* @param {string} fileData - may be some key for custom downloading or url
*/
download(fileData) {
neSpecc marked this conversation as resolved.
Show resolved Hide resolved
slaveeks marked this conversation as resolved.
Show resolved Hide resolved
/**
* Check that custom downloader passed
*/
if (this.config.downloader && typeof this.config.downloader.download === 'function') {
const downloadData = this.config.downloader.download(fileData);

if (!isPromise(downloadData)) {
console.warn('Custom downloader method download should return a Promise');
}

downloadData.then((response) => {
/**
* Call callback for successful downloading with url
*/
this.onDownload(response);
}).catch((error) => {
this.onError(error);
});
} else {
/**
* If there is no custom download method, fileData is correct url
* We only need to call callback
*/
this.onDownload(fileData);
}
}
}
43 changes: 41 additions & 2 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) downloader.js - module, which helps to use custom downloader using 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 Downloader from './downloader';
import './index.css';

import Ui from './ui';
Expand All @@ -63,6 +65,8 @@ 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} [downloader] - optional custom downloader
* @property {function(string): Promise.<string>} [downloader.download] - method that download image by data key
*/

/**
Expand All @@ -72,7 +76,7 @@ import { IconAddBorder, IconStretch, IconAddBackground, IconPicture } from '@cod
* @property {object} file - Object with file data.
* 'url' is required,
* also can contain any additional data that will be saved and passed back
* @property {string} file.url - [Required] image source URL
* @property {string} file.url - [Required] image source URL or file data key to load it via custom downloader
*/
export default class ImageTool {
/**
Expand Down Expand Up @@ -152,6 +156,7 @@ export default class ImageTool {
buttonContent: config.buttonContent || '',
uploader: config.uploader || undefined,
actions: config.actions || [],
downloader: config.downloader || undefined,
};

/**
Expand Down Expand Up @@ -179,6 +184,15 @@ export default class ImageTool {
readOnly,
});

/**
* Module for file downloading
*/
this.downloader = new Downloader({
config: this.config,
onDownload: (url) => this.onDownload(url),
onError: this.downloadingFileError,
});

/**
* Set saved state
*/
Expand Down Expand Up @@ -383,7 +397,7 @@ export default class ImageTool {
this._data.file = file || {};

if (file && file.url) {
this.ui.fillImage(file.url);
this.downloader.download(file.url);
}
}

Expand Down Expand Up @@ -420,6 +434,31 @@ export default class ImageTool {
this.ui.hidePreloader();
}

/**
* Handle downloader errors
*
* @private
* @param {string} errorText - downloading error text
* @returns {void}
*/
downloadingFileError(errorText) {
console.log('Image Tool: downloading failed because of', errorText);

this.api.notifier.show({
message: this.api.i18n.t('Couldn’t download image. Please try another.'),
style: 'error',
});
}

/**
* File downloading callback, fills file data into image
*
* @param {*} url - file url after downloading
*/
onDownload(url) {
this.ui.fillImage(url);
}

/**
* 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