diff --git a/.gitignore b/.gitignore index 641d5eb..ca00cfd 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ node_modules/* npm-debug.log .idea/ .DS_Store -dist +!dist diff --git a/README.md b/README.md index b956e71..e49ee19 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Attaches Tool supports these configuration parameters: | buttonText | `string` | (default: `Select file`) Placeholder for file upload button | | errorMessage | `string` | (default: `File upload failed`) Message to show if file upload failed | | additionalRequestHeaders | `object` | (default: `{}`) Object with any custom headers which will be added to request. Example: `{"X-CSRF-TOKEN": "W5fe2...hR8d1"}` | +| downloadButton | `{onClick: function}` |Option custom onClick event handler to customize the onclick event of download icon| ## Output data @@ -176,3 +177,51 @@ var editor = EditorJS({ ... }); ``` + +## Providing custom download button event handlers + +As mentioned at the Config Params section, you have an ability to provide own custom onClick event handler for the download button. +It is a quite simple: implement `onClick` method and pass them via `downloadButton` config param. +This method will be invoked when the download button is clicked. + + +| Method | Arguments | Return value | Description | +| -------------- | --------- | -------------| ------------| +| onClick | `MouseEvent`, `Object` | `void` | Handle the click event for the download button, allowing custom logic | + +Example: + +```js +import AttachesTool from '@editorjs/attaches'; + +var editor = EditorJS({ + ... + + tools: { + ... + attaches: { + class: AttachesTool, + config: { + /** + * Custom download button handler + */ + downloadButton: { + /** + * Custom logic to execute when download button is clicked + * @param {MouseEvent} event - The click event object + * @param {Object} data - The file data object, containing information such as url, name, size, extension + */ + onClick: function(event, data) { + // Prevent the default download behavior + event.preventDefault(); + + // Custom download logic here + console.log('File download initiated:', data.file.url); + } + } + } + } + } + ... +}); +``` diff --git a/dist/bundle.js b/dist/bundle.js new file mode 100644 index 0000000..e19c176 --- /dev/null +++ b/dist/bundle.js @@ -0,0 +1 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.AttachesTool=t():e.AttachesTool=t()}(window,(function(){return function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=6)}([function(e,t,n){window,e.exports=function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}return n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=3)}([function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";(function(e){var o=n(2),r=setTimeout;function i(){}function a(e){if(!(this instanceof a))throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("not a function");this._state=0,this._handled=!1,this._value=void 0,this._deferreds=[],d(e,this)}function s(e,t){for(;3===e._state;)e=e._value;0!==e._state?(e._handled=!0,a._immediateFn((function(){var n=1===e._state?t.onFulfilled:t.onRejected;if(null!==n){var o;try{o=n(e._value)}catch(e){return void l(t.promise,e)}c(t.promise,o)}else(1===e._state?c:l)(t.promise,e._value)}))):e._deferreds.push(t)}function c(e,t){try{if(t===e)throw new TypeError("A promise cannot be resolved with itself.");if(t&&("object"==typeof t||"function"==typeof t)){var n=t.then;if(t instanceof a)return e._state=3,e._value=t,void u(e);if("function"==typeof n)return void d((o=n,r=t,function(){o.apply(r,arguments)}),e)}e._state=1,e._value=t,u(e)}catch(t){l(e,t)}var o,r}function l(e,t){e._state=2,e._value=t,u(e)}function u(e){2===e._state&&0===e._deferreds.length&&a._immediateFn((function(){e._handled||a._unhandledRejectionFn(e._value)}));for(var t=0,n=e._deferreds.length;t0&&void 0!==arguments[0]?arguments[0]:{};if(e.url&&"string"!=typeof e.url)throw new Error("Url must be a string");if(e.url=e.url||"",e.method&&"string"!=typeof e.method)throw new Error("`method` must be a string or null");if(e.method=e.method?e.method.toUpperCase():"GET",e.headers&&"object"!==o(e.headers))throw new Error("`headers` must be an object or null");if(e.headers=e.headers||{},e.type&&("string"!=typeof e.type||!Object.values(r).includes(e.type)))throw new Error("`type` must be taken from module's «contentType» library");if(e.progress&&"function"!=typeof e.progress)throw new Error("`progress` must be a function or null");if(e.progress=e.progress||function(e){},e.beforeSend=e.beforeSend||function(e){},e.ratio&&"number"!=typeof e.ratio)throw new Error("`ratio` must be a number");if(e.ratio<0||e.ratio>100)throw new Error("`ratio` must be in a 0-100 interval");if(e.ratio=e.ratio||90,e.accept&&"string"!=typeof e.accept)throw new Error("`accept` must be a string with a list of allowed mime-types");if(e.accept=e.accept||"*/*",e.multiple&&"boolean"!=typeof e.multiple)throw new Error("`multiple` must be a true or false");if(e.multiple=e.multiple||!1,e.fieldName&&"string"!=typeof e.fieldName)throw new Error("`fieldName` must be a string");return e.fieldName=e.fieldName||"files",e},c=function(e){switch(e.method){case"GET":var t=l(e.data,r.URLENCODED);delete e.data,e.url=/\?/.test(e.url)?e.url+"&"+t:e.url+"?"+t;break;case"POST":case"PUT":case"DELETE":case"UPDATE":var n=function(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}).type||r.JSON}(e);(f.isFormData(e.data)||f.isFormElement(e.data))&&(n=r.FORM),e.data=l(e.data,n),n!==d.contentType.FORM&&(e.headers["content-type"]=n)}return e},l=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};switch(arguments.length>1?arguments[1]:void 0){case r.URLENCODED:return f.urlEncode(e);case r.JSON:return f.jsonEncode(e);case r.FORM:return f.formEncode(e);default:return e}},u=function(e){return e>=200&&e<300},{contentType:r={URLENCODED:"application/x-www-form-urlencoded; charset=utf-8",FORM:"multipart/form-data",JSON:"application/json; charset=utf-8"},request:i,get:function(e){return e.method="GET",i(e)},post:a,transport:function(e){return e=s(e),f.selectFiles(e).then((function(t){for(var n=new FormData,o=0;o=0&&(e._idleTimeoutId=setTimeout((function(){e._onTimeout&&e._onTimeout()}),t))},n(6),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,n(0))},function(e,t,n){(function(e,t){!function(e,n){"use strict";if(!e.setImmediate){var o,r,i,a,s,c=1,l={},u=!1,f=e.document,d=Object.getPrototypeOf&&Object.getPrototypeOf(e);d=d&&d.setTimeout?d:e,"[object process]"==={}.toString.call(e.process)?o=function(e){t.nextTick((function(){h(e)}))}:function(){if(e.postMessage&&!e.importScripts){var t=!0,n=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=n,t}}()?(a="setImmediate$"+Math.random()+"$",s=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(a)&&h(+t.data.slice(a.length))},e.addEventListener?e.addEventListener("message",s,!1):e.attachEvent("onmessage",s),o=function(t){e.postMessage(a+t,"*")}):e.MessageChannel?((i=new MessageChannel).port1.onmessage=function(e){h(e.data)},o=function(e){i.port2.postMessage(e)}):f&&"onreadystatechange"in f.createElement("script")?(r=f.documentElement,o=function(e){var t=f.createElement("script");t.onreadystatechange=function(){h(e),t.onreadystatechange=null,r.removeChild(t),t=null},r.appendChild(t)}):o=function(e){setTimeout(h,0,e)},d.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n1)for(var n=1;n HTMLElement")}},{key:"isObject",value:function(e){return"[object Object]"===Object.prototype.toString.call(e)}},{key:"isFormData",value:function(e){return e instanceof FormData}},{key:"isFormElement",value:function(e){return e instanceof HTMLFormElement}},{key:"selectFiles",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return new Promise((function(t,n){var o=document.createElement("INPUT");o.type="file",e.multiple&&o.setAttribute("multiple","multiple"),e.accept&&o.setAttribute("accept",e.accept),o.style.display="none",document.body.appendChild(o),o.addEventListener("change",(function(e){var n=e.target.files;t(n),document.body.removeChild(o)}),!1),o.click()}))}},{key:"parseHeaders",value:function(e){var t=e.trim().split(/[\r\n]+/),n={};return t.forEach((function(e){var t=e.split(": "),o=t.shift(),r=t.join(": ");o&&(n[o]=r)})),n}}])&&o(t,n),e}()},function(e,t){var n=function(e){return encodeURIComponent(e).replace(/[!'()*]/g,escape).replace(/%20/g,"+")},o=function(e,t,r,i){return t=t||null,r=r||"&",i=i||null,e?function(e){for(var t=new Array,n=0;n=0&&f.splice(t,1)}function y(e){var t=document.createElement("style");return void 0===e.attrs.type&&(e.attrs.type="text/css"),g(t,e.attrs),m(e,t),t}function g(e,t){Object.keys(t).forEach((function(n){e.setAttribute(n,t[n])}))}function b(e,t){var n,o,r,i;if(t.transform&&e.css){if(!(i=t.transform(e.css)))return function(){};e.css=i}if(t.singleton){var a=u++;n=l||(l=y(t)),o=_.bind(null,n,a,!1),r=_.bind(null,n,a,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(n=function(e){var t=document.createElement("link");return void 0===e.attrs.type&&(e.attrs.type="text/css"),e.attrs.rel="stylesheet",g(t,e.attrs),m(e,t),t}(t),o=C.bind(null,n,t),r=function(){v(n),n.href&&URL.revokeObjectURL(n.href)}):(n=y(t),o=S.bind(null,n),r=function(){v(n)});return o(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;o(e=t)}else r()}}e.exports=function(e,t){if("undefined"!=typeof DEBUG&&DEBUG&&"object"!=typeof document)throw new Error("The style-loader cannot be used in a non-browser environment");(t=t||{}).attrs="object"==typeof t.attrs?t.attrs:{},t.singleton||"boolean"==typeof t.singleton||(t.singleton=a()),t.insertInto||(t.insertInto="head"),t.insertAt||(t.insertAt="bottom");var n=h(e,t);return p(n,t),function(e){for(var o=[],r=0;re.length)&&(t=e.length);for(var n=0,o=new Array(t);n1&&void 0!==arguments[1]?arguments[1]:null,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=document.createElement(e);Array.isArray(n)?(t=r.classList).add.apply(t,s(n)):n&&r.classList.add(n);for(var i in o)r[i]=o[i];return r}function u(e){return 0===Object.keys(e).length}const f='';function d(e,t){for(var n=0;n4&&(a=n.substring(0,4)+"…");var s=l("div",this.CSS.fileIconLabel,{textContent:a,title:n});o&&(s.style.backgroundColor=o),r.appendChild(s)}else i.innerHTML=f;this.nodes.wrapper.appendChild(r)}},{key:"removeLoader",value:function(){var e=this;setTimeout((function(){return e.nodes.wrapper.classList.remove(e.CSS.wrapperLoading,e.CSS.loader)}),500)}},{key:"showFileData",value:function(){var e=this;this.nodes.wrapper.classList.add(this.CSS.wrapperWithFile);var t=this.data,n=t.file,o=t.title;this.appendFileIcon(n);var r=l("div",this.CSS.fileInfo);if(this.nodes.title=l("div",this.CSS.title,{contentEditable:!1===this.readOnly}),this.nodes.title.dataset.placeholder=this.api.i18n.t("File title"),this.nodes.title.textContent=o||"",r.appendChild(this.nodes.title),n.size){var i,a,s=l("div",this.CSS.size);Math.log10(+n.size)>=6?(i="MiB",a=n.size/Math.pow(2,20)):(i="KiB",a=n.size/Math.pow(2,10)),s.textContent=a.toFixed(1),s.setAttribute("data-size",i),r.appendChild(s)}if(this.nodes.wrapper.appendChild(r),void 0!==n.url){var c=l("a",this.CSS.downloadButton,{innerHTML:'',href:n.url,target:"_blank",rel:"nofollow noindex noreferrer"});c.addEventListener("click",(function(t){!function(e,t,n){t&&t.downloadButton&&t.downloadButton.onClick&&"function"==typeof t.downloadButton.onClick&&t.downloadButton.onClick(e,n)}(t,e.config,e.data)})),this.nodes.wrapper.appendChild(c)}}},{key:"uploadingFailed",value:function(e){this.api.notifier.show({message:e,style:"error"}),this.removeLoader()}},{key:"data",get:function(){return this._data},set:function(e){var t=e.file,n=e.title;this._data={file:t,title:n}}}])&&d(t.prototype,n),o&&d(t,o),e}()}]).default})); \ No newline at end of file diff --git a/package.json b/package.json index d12beb5..8e67060 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@editorjs/attaches", - "version": "1.3.0", + "version": "1.3.1", "repository": "https://github.com/editor-js/attaches", "license": "MIT", "scripts": { @@ -43,5 +43,9 @@ }, "dependencies": { "@codexteam/icons": "^0.0.4" + }, + "volta": { + "node": "16.20.2", + "yarn": "1.22.22" } } diff --git a/src/index.js b/src/index.js index 72061aa..1ec67c6 100644 --- a/src/index.js +++ b/src/index.js @@ -4,6 +4,7 @@ import Uploader from './uploader'; import { make, moveCaretToTheEnd, isEmpty } from './utils/dom'; import { getExtensionFromFileName } from './utils/file'; import { IconChevronDown, IconFile } from '@codexteam/icons'; +import {onClickDownloadButton} from './utils/customEvents'; const LOADER_TIMEOUT = 500; @@ -47,6 +48,8 @@ const LOADER_TIMEOUT = 500; * @property {string} errorMessage - message to show if file uploading failed * @property {object} [uploader] - optional custom uploader * @property {function(File): Promise.} [uploader.uploadByFile] - custom method that upload file and returns response + * @property {object} [downloadButton] - download button configuration + * @property {function(Event, AttachesToolData): void} [downloadButton.onClick] - custom method that handles download button click */ /** @@ -90,6 +93,7 @@ export default class AttachesTool { buttonText: config.buttonText || 'Select file to upload', errorMessage: config.errorMessage || 'File upload failed', uploader: config.uploader || undefined, + downloadButton: config.downloadButton || undefined, additionalRequestHeaders: config.additionalRequestHeaders || {}, }; @@ -444,6 +448,14 @@ export default class AttachesTool { rel: 'nofollow noindex noreferrer', }); + /** + * Registering the config.downloadButton.onClick handler. + * The function onClickDownloadButton will invoke the this.config.downloadButton.onClick function if it exists. + */ + downloadIcon.addEventListener('click', (event) =>{ + onClickDownloadButton(event, this.config, this.data); + }); + this.nodes.wrapper.appendChild(downloadIcon); } } diff --git a/src/utils/customEvents.js b/src/utils/customEvents.js new file mode 100644 index 0000000..4566d3b --- /dev/null +++ b/src/utils/customEvents.js @@ -0,0 +1,13 @@ +/** + * Registers the config.downloadButton.onClick function to the click event of the download button + * if it is defined. + * + * @param {MouseEvent} event - The click event. + * @param {Object} config - The configuration object. + * @param {AttachesToolData} data - The data object. + */ +export function onClickDownloadButton(event, config, data) { + if (config && config.downloadButton && config.downloadButton.onClick && typeof config.downloadButton.onClick === 'function') { + config.downloadButton.onClick(event, data); + } +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index d6ab070..d9ab1f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1551,8 +1551,8 @@ camelcase@^5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" caniuse-lite@^1.0.30001280: - version "1.0.30001282" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001282.tgz#38c781ee0a90ccfe1fe7fefd00e43f5ffdcb96fd" + version "1.0.30001650" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001650.tgz" caseless@~0.12.0: version "0.12.0"