├── .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 |
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 |
--------------------------------------------------------------------------------