├── .gitignore ├── LICENSE ├── README.md ├── dist ├── grapesjs-plugin-filestack.css └── grapesjs-plugin-filestack.min.js ├── index.html ├── package-lock.json ├── package.json ├── src ├── index.js └── style │ └── main.scss └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | private/ 3 | img/ 4 | js/ 5 | node_modules/ 6 | .eslintrc 7 | *.log 8 | _index.html 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-current, Artur Arseniev 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | - Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | - Redistributions in binary form must reproduce the above copyright notice, this 10 | list of conditions and the following disclaimer in the documentation and/or 11 | other materials provided with the distribution. 12 | - Neither the name "GrapesJS" nor the names of its contributors may be 13 | used to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [GrapesJS Filestack](http://grapesjs.com/demo.html) 2 | 3 | This plugin replaces the default file uploader with the one from Filestack 4 | 5 | Demo: http://grapesjs.com/demo.html 6 | 7 |

GrapesJS

8 |
9 | 10 | 11 | 12 | ## Summary 13 | 14 | * Plugin 15 | * Name: `gjs-plugin-filestack` 16 | * Options: 17 | * `key` Filestack's API key (**required**) 18 | * `btnEl` Custom button element which triggers Filestack modal 19 | * `btnText` Text for the button in case the custom one is not provided, default: `Add images` 20 | * `filestackOpts` Filestack's options, default: `{accept: 'image/*', maxFiles: 10}` 21 | * `onComplete` On complete upload callback, eg. `onComplete: (blobs, assets) => {...}` 22 | *blobs* Array of Objects, eg. [{url:'...', filename: 'name.jpeg', ...}] 23 | *assets* Array of inserted assets 24 | 25 | ## Prerequisites 26 | 27 | - [filestack-js](https://github.com/filestack/filestack-js) **up to v0.11.5 only** (not compatible with filestack-js v1 and above) 28 | 29 | ## Download 30 | 31 | * `npm i grapesjs-plugin-filestack` 32 | * Latest release link https://github.com/artf/grapesjs-plugin-filestack/releases/latest 33 | 34 | 35 | 36 | ## Usage 37 | 38 | ```html 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 | 47 | 56 | ``` 57 | 58 | 59 | 60 | ## Development 61 | 62 | Clone the repository 63 | 64 | ```sh 65 | $ git clone https://github.com/artf/grapesjs-plugin-filestack.git 66 | $ cd grapesjs-plugin-filestack 67 | ``` 68 | 69 | Install it 70 | 71 | ```sh 72 | $ npm i 73 | ``` 74 | 75 | The plugin relies on GrapesJS via `peerDependencies` so you have to install it manually (without adding it to package.json) 76 | 77 | ```sh 78 | $ npm i grapesjs --no-save 79 | ``` 80 | 81 | Start the dev server 82 | 83 | ```sh 84 | $ npm start 85 | ``` 86 | 87 | 88 | 89 | ## License 90 | 91 | BSD 3-Clause 92 | -------------------------------------------------------------------------------- /dist/grapesjs-plugin-filestack.css: -------------------------------------------------------------------------------- 1 | .gjs-am-assets-cont .gjs-am-asset-image { 2 | border-bottom: none; 3 | float: left; 4 | width: 20%; 5 | height: 150px; 6 | box-sizing: border-box; 7 | border-radius: 3px; 8 | overflow: hidden; } 9 | 10 | .gjs-am-preview-cont, 11 | .gjs-am-assets-cont #gjs-am-meta, 12 | .gjs-am-assets-cont #gjs-am-preview-cont { 13 | width: 100%; } 14 | 15 | .gjs-am-assets-cont #gjs-am-preview-cont { 16 | height: 100px; } 17 | 18 | .gjs-am-assets-cont #gjs-am-close { 19 | right: 10px; 20 | top: 10px; 21 | z-index: 1; 22 | line-height: 0; } 23 | 24 | .gjs-btn-filestack { 25 | margin-bottom: 10px; } 26 | 27 | .gjs-am-assets-cont { 28 | height: 400px; } 29 | 30 | .gjs-am-assets { 31 | height: 345px; 32 | background-color: rgba(0, 0, 0, 0.1); 33 | padding: 5px; 34 | margin: 0 -10px; } 35 | -------------------------------------------------------------------------------- /dist/grapesjs-plugin-filestack.min.js: -------------------------------------------------------------------------------- 1 | /*! grapesjs-plugin-filestack - 0.1.1 */ 2 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("grapesjs")):"function"==typeof define&&define.amd?define(["grapesjs"],t):"object"==typeof exports?exports["grapesjs-plugin-filestack"]=t(require("grapesjs")):e["grapesjs-plugin-filestack"]=t(e.grapesjs)}(this,function(e){return function(e){function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=0)}([function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(1),o=function(e){return e&&e.__esModule?e:{default:e}}(r);t.default=o.default.plugins.add("gjs-plugin-filestack",function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t,r=e.getConfig(),o=r.stylePrefix||"",i=void 0,s={key:"",btnEl:"",btnText:"Add images",filestackOpts:{accept:"image/*",maxFiles:10},onComplete:function(e,t){}};for(var a in s)a in n||(n[a]=s[a]);if(!filestack)throw new Error("Filestack instance not found");if(!n.key)throw new Error("Filestack's API key not found");var u=filestack.init(n.key);e.on("run:open-assets",function(){var t=e.Modal,r=t.getContentEl(),s=r.querySelector("."+o+"am-file-uploader"),a=r.querySelector("."+o+"am-assets-header"),c=r.querySelector("."+o+"am-assets-cont");s&&(s.style.display="none"),a&&(a.style.display="none"),c.style.width="100%",i||(i=n.btnEl,i||(i=document.createElement("button"),i.className=o+"btn-prim "+o+"btn-filestack",i.innerHTML=n.btnText),i.onclick=function(){u.pick(n.filestackOpts).then(function(e){var t=e.filesUploaded,r=t instanceof Array?t:[t],o=l(r);n.onComplete(r,o)})}),c.insertBefore(i,a)});var l=function(t){var n=t.map(function(e){return e.src=e.url,e});return e.AssetManager.add(n)}})},function(t,n){t.exports=e}])}); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | GrapesJS Newsletter Editor 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 |
18 | 19 |
20 | 21 | 22 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grapesjs-plugin-filestack", 3 | "version": "0.1.1", 4 | "description": "Enable Filestack uploader inside the Asset Manager", 5 | "main": "dist/grapesjs-plugin-filestack.min.js", 6 | "scripts": { 7 | "lint": "eslint src", 8 | "build": "npm run v:patch && webpack --env.production", 9 | "build:css": "node-sass src/style/main.scss dist/grapesjs-plugin-filestack.css", 10 | "v:patch": "npm version --no-git-tag-version patch", 11 | "start": "webpack-dev-server --open --progress --colors & npm run build:css -- -w" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/artf/grapesjs-plugin-filestack.git" 16 | }, 17 | "keywords": [ 18 | "grapesjs", 19 | "plugin", 20 | "filestack", 21 | "upload", 22 | "asset" 23 | ], 24 | "author": "Artur Arseniev", 25 | "license": "BSD-3-Clause", 26 | "babel": { 27 | "presets": [ 28 | "env" 29 | ], 30 | "plugins": [ 31 | "transform-object-rest-spread" 32 | ] 33 | }, 34 | "peerDependencies": { 35 | "grapesjs": "0.x" 36 | }, 37 | "devDependencies": { 38 | "babel-core": "^6.26.0", 39 | "babel-loader": "^7.1.2", 40 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 41 | "babel-preset-env": "^1.6.1", 42 | "html-webpack-plugin": "^2.30.1", 43 | "node-sass": "^3.13.0", 44 | "eslint": "^4.1.1", 45 | "webpack": "^3.8.1", 46 | "webpack-dev-server": "^2.9.4" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import grapesjs from 'grapesjs'; 2 | 3 | export default grapesjs.plugins.add('gjs-plugin-filestack', (editor, opts = {}) => { 4 | let c = opts; 5 | let config = editor.getConfig(); 6 | let pfx = config.stylePrefix || ''; 7 | let btnEl; 8 | 9 | let defaults = { 10 | // Filestack's API key 11 | key: '', 12 | 13 | // Custom button element which triggers Filestack modal 14 | btnEl: '', 15 | 16 | // Text for the button in case the custom one is not provided 17 | btnText: 'Add images', 18 | 19 | // Filestack's options 20 | filestackOpts: { 21 | accept: 'image/*', 22 | maxFiles: 10 23 | }, 24 | 25 | // On complete upload callback 26 | // blobs - Array of Objects, eg. [{url:'...', filename: 'name.jpeg', ...}] 27 | // assets - Array of inserted assets 28 | // for debug: console.log(JSON.stringify(blobs)); 29 | onComplete: (blobs, assets) => {}, 30 | }; 31 | 32 | // Load defaults 33 | for (let name in defaults) { 34 | if (!(name in c)) 35 | c[name] = defaults[name]; 36 | } 37 | 38 | if(!filestack) { 39 | throw new Error('Filestack instance not found'); 40 | } 41 | 42 | if(!c.key){ 43 | throw new Error("Filestack's API key not found"); 44 | } 45 | 46 | const fsClient = filestack.init(c.key); 47 | 48 | 49 | // When the Asset Manager modal is opened 50 | editor.on('run:open-assets', () => { 51 | const modal = editor.Modal; 52 | const modalBody = modal.getContentEl(); 53 | const uploader = modalBody.querySelector('.' + pfx + 'am-file-uploader'); 54 | const assetsHeader = modalBody.querySelector('.' + pfx + 'am-assets-header'); 55 | const assetsBody = modalBody.querySelector('.' + pfx + 'am-assets-cont'); 56 | 57 | uploader && (uploader.style.display = 'none'); 58 | assetsHeader && (assetsHeader.style.display = 'none'); 59 | assetsBody.style.width = '100%'; 60 | 61 | // Instance button if not yet exists 62 | if(!btnEl) { 63 | btnEl = c.btnEl; 64 | 65 | if(!btnEl) { 66 | btnEl = document.createElement('button'); 67 | btnEl.className = pfx + 'btn-prim ' + pfx + 'btn-filestack'; 68 | btnEl.innerHTML = c.btnText; 69 | } 70 | 71 | btnEl.onclick = () => { 72 | fsClient.pick(c.filestackOpts).then((objs) => { 73 | const blob = objs.filesUploaded; 74 | const blobs = blob instanceof Array ? blob : [blob]; 75 | let assets = addAssets(blobs); 76 | c.onComplete(blobs, assets); 77 | }); 78 | }; 79 | } 80 | 81 | assetsBody.insertBefore(btnEl, assetsHeader); 82 | }); 83 | 84 | /** 85 | * Add new assets to the editor 86 | * @param {Array} files 87 | */ 88 | const addAssets = (files) => { 89 | const urls = files.map((file) => { 90 | file.src = file.url; 91 | return file; 92 | }); 93 | return editor.AssetManager.add(urls); 94 | }; 95 | 96 | }); 97 | -------------------------------------------------------------------------------- /src/style/main.scss: -------------------------------------------------------------------------------- 1 | $pfx: 'gjs-'; 2 | $pfxam: $pfx + 'am-'; 3 | 4 | .#{$pfxam}assets-cont .#{$pfxam}asset-image { 5 | border-bottom: none; 6 | float: left; 7 | width: 20%; 8 | height: 150px; 9 | box-sizing: border-box; 10 | border-radius: 3px; 11 | overflow: hidden; 12 | } 13 | 14 | .#{$pfxam}preview-cont, 15 | .#{$pfxam}assets-cont ##{$pfxam}meta, 16 | .#{$pfxam}assets-cont ##{$pfxam}preview-cont { 17 | width: 100%; 18 | } 19 | 20 | .#{$pfxam}assets-cont ##{$pfxam}preview-cont { 21 | height: 100px; 22 | } 23 | 24 | .#{$pfxam}assets-cont ##{$pfxam}close { 25 | right: 10px; 26 | top: 10px; 27 | z-index: 1; 28 | line-height: 0; 29 | } 30 | 31 | .#{$pfx}btn-filestack { 32 | margin-bottom: 10px; 33 | } 34 | 35 | .#{$pfxam}assets-cont { 36 | height: 400px; 37 | } 38 | 39 | .#{$pfxam}assets { 40 | height: 345px; 41 | background-color: rgba(0, 0, 0, 0.1); 42 | padding: 5px; 43 | margin: 0 -10px; 44 | } 45 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 2 | const pkg = require('./package.json'); 3 | const webpack = require('webpack'); 4 | const fs = require('fs'); 5 | const name = pkg.name; 6 | let plugins = []; 7 | 8 | module.exports = (env = {}) => { 9 | if (env.production) { 10 | plugins = [ 11 | new webpack.optimize.UglifyJsPlugin({ minimize: true, compressor: { warnings: false }}), 12 | new webpack.BannerPlugin(`${name} - ${pkg.version}`), 13 | ] 14 | } else { 15 | const index = 'index.html'; 16 | const indexDev = '_' + index; 17 | plugins.push(new HtmlWebpackPlugin({ 18 | template: fs.existsSync(indexDev) ? indexDev : index 19 | })); 20 | } 21 | 22 | return { 23 | entry: './src', 24 | output: { 25 | filename: `./dist/${name}.min.js`, 26 | library: name, 27 | libraryTarget: 'umd', 28 | }, 29 | module: { 30 | loaders: [{ 31 | test: /\.js$/, 32 | loader: 'babel-loader', 33 | include: /src/, 34 | }], 35 | }, 36 | externals: {'grapesjs': 'grapesjs'}, 37 | plugins: plugins, 38 | }; 39 | } 40 | --------------------------------------------------------------------------------