├── .gitignore
├── LICENSE
├── README.md
├── dist
└── grapesjs-plugin-bootstrap.min.js
├── index.html
├── package-lock.json
├── package.json
├── src
├── blocks
│ ├── basics.js
│ ├── components.js
│ ├── index.js
│ └── layout.js
├── components
│ ├── alert.js
│ ├── basic.js
│ ├── container.js
│ ├── dropdown.js
│ ├── grid.js
│ ├── index.js
│ ├── label.js
│ ├── media.js
│ ├── panel.js
│ ├── thumbnail.js
│ └── well.js
├── constants.js
├── devices.js
├── index.js
├── traits
│ ├── index.js
│ └── selectClass.js
└── utils.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | private/
3 | node_modules/
4 | .eslintrc
5 | *.log
6 | _index.html
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017, (YOUR NAME)
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 Plugin Twitter Bootstrap
2 |
3 | Plugin Grapesjs with Twitter Bootstrap v3.3.7
4 |
5 | ### Usage
6 | 1. Clone this repository `git clone https://github.com/olivmonnier/grapesjs-plugin-bootstrap.git`
7 | 1. Replace in all files `grapesjs-plugin-bootstrap` with your plugin name
8 | 1. Update `package.json`
9 | 1. Install dependencies `npm i` and run the local server `npm start`
10 | 1. Start creating your plugin from `src/index.js`
11 | 1. Show some gif/demo if possible
12 | 1. Update README
13 | 1. When you're ready update the production file `npm run build`
14 | 1. Publish
15 |
16 |
17 |
18 | ## Summary
19 |
20 | * Plugin name: `grapesjs-plugin-bootstrap`
21 | * Components
22 | * `alert`
23 | * `container`
24 | * `dropdown`
25 | * `grid`
26 | * `label`
27 | * `media`
28 | * `panel`
29 | * `thumbnail`
30 | * `well`
31 | * Blocks
32 | * `address`
33 | * `blockquote`
34 | * `button`
35 | * `header`
36 | * `image`
37 | * `link`
38 | * `list`
39 | * `paragraph`
40 | * `alert`
41 | * `dropdown`
42 | * `media`
43 | * `panel`
44 | * `thumbnail`
45 | * `well`
46 | * `container`
47 | * `row`
48 | * `column`
49 | * `columns-2`
50 | * `columns-3`
51 | * `columns-4`
52 | * `columns-4-8`
53 | * `columns-8-4`
54 | ...
55 |
56 |
57 |
58 | ## Download
59 |
60 | * GIT
61 | * `git clone https://github.com/olivmonnier/grapesjs-plugin-bootstrap.git`
62 |
63 |
64 | ## Usage
65 |
66 | ```html
67 |
68 |
69 |
70 |
71 |
72 |
73 |
85 | ```
86 |
87 |
88 |
89 |
90 |
91 | ## Development
92 |
93 | Clone the repository
94 |
95 | ```sh
96 | $ git clone https://github.com/olivmonnier/grapesjs-plugin-bootstrap.git
97 | $ cd grapesjs-plugin-bootstrap
98 | ```
99 |
100 | Install dependencies
101 |
102 | ```sh
103 | $ npm i
104 | ```
105 |
106 | Start the dev server
107 |
108 | ```sh
109 | $ npm start
110 | ```
111 |
112 | ## License
113 |
114 | BSD 3-Clause
115 |
--------------------------------------------------------------------------------
/dist/grapesjs-plugin-bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*! grapesjs-plugin-bootstrap - 0.1.351 */
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-bootstrap"]=t(require("grapesjs")):e["grapesjs-plugin-bootstrap"]=t(e.grapesjs)}(this,function(e){return function(e){function t(n){if(a[n])return a[n].exports;var o=a[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var a={};return t.m=e,t.c=a,t.d=function(e,a,n){t.o(e,a)||Object.defineProperty(e,a,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var a=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(a,"a",a),a},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=1)}([function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.capitalize=function(e){return e.charAt(0).toUpperCase()+e.slice(1)},t.getModel=function(e,t){return e.DomComponents.getType(t).model},t.getView=function(e,t){return e.DomComponents.getType(t).view}},function(e,t,a){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=Object.assign||function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{},a=o({},d.default,t);a.addBasicStyle&&e.addComponents('\n \n '),(0,r.default)(e,a),(0,v.default)(e,a),(0,p.default)(e,a),(0,f.default)(e,a)})},function(t,a){t.exports=e},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={addBasicStyle:!0,blocks:["address","alert","blockquote","button","container","column","columns-2","columns-3","columns-4","columns-4-8","columns-8-4","dropdown","header","image","label","link","list","media","panel","paragraph","row","thumbnail","well"],category:{basics:"Basics",components:"Components",layout:"Layout"}}},function(e,t,a){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=a(5),l=n(o),s=a(6),i=n(s),d=a(7),c=n(d),r=a(8),u=n(r),p=a(9),m=n(p),f=a(10),g=n(f),v=a(11),b=n(v),y=a(12),h=n(y),w=a(13),x=n(w),j=a(14),C=n(j);t.default=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};(0,l.default)(e,t),(0,i.default)(e,t),(0,c.default)(e,t),(0,u.default)(e,t),(0,m.default)(e,t),(0,g.default)(e,t),(0,b.default)(e,t),(0,h.default)(e,t),(0,x.default)(e,t),(0,C.default)(e,t)}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=a(0);t.default=function(e){var t=(arguments.length>1&&void 0!==arguments[1]&&arguments[1],e.DomComponents),a=t.getType("text"),o=a.model,l=a.view,s=["success","info","warning","danger"];t.addType("alert",{model:o.extend({defaults:Object.assign({},o.prototype.defaults,{"custom-name":"Alert",attributes:{role:"alert"},classes:["alert"],traits:o.prototype.defaults.traits.concat([{type:"select-class",label:"Context",options:s.map(function(e){return{value:"alert-"+e,name:(0,n.capitalize)(e)}})}])})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("alert"))return{type:"alert"}}}),view:l})}},function(e,t,a){"use strict";function n(e){if(Array.isArray(e)){for(var t=0,a=Array(e.length);t1&&void 0!==arguments[1]&&arguments[1],e.DomComponents),a=(0,o.getModel)(e,"default"),l=(0,o.getView)(e,"default"),s=(0,o.getModel)(e,"text"),i=(0,o.getView)(e,"text"),d=(0,o.getModel)(e,"image"),c=(0,o.getView)(e,"image"),r=(0,o.getModel)(e,"link"),u=(0,o.getView)(e,"link"),p=["primary","success","info","warning","danger"],m=["left","center","right","justify"],f=["lowercase","uppercase","capitalize"],g=["rounded","circle","thumbnail"],v=[["lg","large"],["sm","small"],["xs","extra small"]];t.addType("default",{model:a.extend({defaults:Object.assign({},a.prototype.defaults,{traits:a.prototype.defaults.traits.concat([{type:"select-class",label:"Float",options:[{value:"",name:"None"},{value:"pull-left",name:"Left"},{value:"pull-right",name:"Right"}]},{type:"select-class",label:"Color",options:[{value:"",name:"None"}].concat(n(["muted"].concat(p).map(function(e){return{value:"text-"+e,name:(0,o.capitalize)(e)}})))},{type:"select-class",label:"Background",options:[{value:"",name:"None"}].concat(n(p.map(function(e){return{value:"bg-"+e,name:(0,o.capitalize)(e)}})))}])})}),view:l}),a=(0,o.getModel)(e,"default"),l=(0,o.getView)(e,"default"),t.addType("image",{model:d.extend({defaults:Object.assign({},d.prototype.defaults,{"custom-name":"Image",attributes:{src:"https://dummyimage.com/450x250/999/222"},traits:[{type:"text",label:"Source (URL)",name:"src"},{type:"text",label:"Alternate text",name:"alt"},{type:"select-class",label:"Responsive",options:[{value:"",name:"No"},{value:"img-responsive",name:"Yes"}]},{type:"select-class",label:"Shape",options:[{value:"",name:"none"}].concat(n(g.map(function(e){return{value:"img-"+e,name:(0,o.capitalize)(e)}})))}]})},{isComponent:function(e){if(e&&e.tagName&&"IMG"===e.tagName)return{type:"image"}}}),view:c}),t.addType("text",{model:a.extend({defaults:Object.assign({},s.prototype.defaults,{droppable:!0,traits:a.prototype.defaults.traits.concat([{type:"select-class",label:"Alignment",options:[].concat(n(m.map(function(e){return{value:"text-"+e,name:(0,o.capitalize)(e)}})),[{value:"text-nowrap",name:"No wrap"}])},{type:"select-class",label:"Transform",options:[{value:"",name:"None"}].concat(n(f.map(function(e){return{value:"text-"+e,name:(0,o.capitalize)(e)}})))}])})}),view:i}),s=(0,o.getModel)(e,"text"),i=(0,o.getView)(e,"text"),t.addType("header",{model:s.extend({defaults:Object.assign({},s.prototype.defaults,{"custom-name":"Header",tagName:"h1",traits:s.prototype.defaults.traits.concat([{type:"select",options:[{value:"h1",name:"One (largest)"},{value:"h2",name:"Two"},{value:"h3",name:"Three"},{value:"h4",name:"Four"},{value:"h5",name:"Five"},{value:"h6",name:"Six (smallest)"}],label:"Size",name:"tagName",changeProp:1}])})},{isComponent:function(e){if(e&&e.tagName&&["H1","H2","H3","H4","H5","H6"].includes(e.tagName))return{type:"header"}}}),view:i}),t.addType("link",{model:r.extend({defaults:Object.assign({},r.prototype.defaults,{traits:[{type:"text",name:"id",label:"Id",placeholder:"eg. Text here"}].concat(r.prototype.defaults.traits,[{type:"select",label:"Toggles",name:"data-toggle",options:[{value:"",name:"None"},{value:"button",name:"Self"},{value:"collapse",name:"Collapse"},{value:"dropdown",name:"Dropdown"}],changeProp:1}])})}),view:u}),r=(0,o.getModel)(e,"link"),u=(0,o.getView)(e,"link"),t.addType("button",{model:r.extend({defaults:Object.assign({},r.prototype.defaults,{"custom-name":"Button",attributes:{role:"button"},classes:["btn"],traits:r.prototype.defaults.traits.concat([{type:"select-class",label:"Context",options:[{value:"btn-default",name:"Default"}].concat(n(p.map(function(e){return{value:"btn-"+e,name:(0,o.capitalize)(e)}})))},{type:"select-class",label:"Size",options:[{value:"",name:"Default"}].concat(n(v.map(function(e){return{value:"btn-"+e[0],name:(0,o.capitalize)(e[1])}})))},{type:"select-class",label:"Width",options:[{value:"",name:"Inline"},{value:"btn-block",name:"Block"}]}])},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("btn"))return{type:"button"}}})}),view:u}),t.addType("list",{model:a.extend({defaults:Object.assign({},a.prototype.defaults,{"custom-name":"List",droppable:!0,traits:a.prototype.defaults.traits.concat([{type:"select",label:"Type",name:"tagName",options:[{value:"ul",name:"Unordered"},{value:"ol",name:"Ordered"}],changeProp:1},{type:"select-class",label:"Style",options:[{value:"",name:"none"},{value:"list-unstyled",name:"Unstyled"},{value:"list-inline",name:"Inline"}]}])})},{isComponent:function(e){if(e&&e.tagName&&("UL"===e.tagName||"OL"===e.tagName))return{type:"list"}}}),view:l}),t.addType("list-item",{model:s.extend({defaults:Object.assign({},s.prototype.defaults,{"custom-name":"Item",tagName:"li",draggable:"ul, ol"})},{isComponent:function(e){if(e&&e.tagName&&"LI"===e.tagName)return{type:"list-item"}}}),view:i}),t.addType("paragraph",{model:s.extend({defaults:Object.assign({},s.prototype.defaults,{"custom-name":"Paragraph",tagName:"p",traits:s.prototype.defaults.traits.concat([{type:"select-class",label:"Lead",options:[{value:"",name:"No"},{value:"lead",name:"Yes"}]}])})},{isComponent:function(e){if(e&&e.tagName&&"P"===e.tagName)return{type:"paragraph"}}}),view:i}),t.addType("blockquote",{model:a.extend({defaults:Object.assign({},a.prototype.defaults,{tagName:"blockquote",traits:a.prototype.defaults.traits.concat([{type:"select-class",label:"Reversed",options:[{value:"",name:"No"},{value:"blockquote-reverse",name:"Yes"}]}])})},{isComponent:function(e){if(e&&e.tagName&&"BLOCKQUOTE"===e.tagName)return{type:"blockquote"}}}),view:l})}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=(arguments.length>1&&void 0!==arguments[1]&&arguments[1],e.DomComponents),a=t.getType("default"),n=a.model,o=a.view;t.addType("container",{model:n.extend({defaults:Object.assign({},n.prototype.defaults,{"custom-name":"Container",tagName:"div",droppable:!0,traits:n.prototype.defaults.traits.concat([{type:"select-class",label:"Type",options:[{value:"container",name:"Fixed"},{value:"container-fluid",name:"Fluid"}]}])})},{isComponent:function(e){if(e&&e.classList&&(e.classList.contains("container")||e.classList.contains("container-fluid")))return{type:"container"}}}),view:o})}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=(arguments.length>1&&void 0!==arguments[1]&&arguments[1],e.DomComponents),a=t.getType("default"),n=a.model,o=a.view,l=t.getType("text"),s=l.model,i=l.view,d=t.getType("link"),c=d.model,r=t.getType("list-item"),u=r.model,p=r.view;t.addType("dropdown",{model:n.extend({defaults:Object.assign({},n.prototype.defaults,{"custom-name":"Dropdown",droppable:"a, button, .dropdown-menu",traits:n.prototype.defaults.traits.concat([{type:"select-class",label:"State",options:[{value:"",name:"Closed"},{value:"open",name:"Open"}]}])})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("dropdown"))return{type:"dropdown"}}}),view:o}),t.addType("dropdown-toggle",{model:s.extend({defaults:Object.assign({},s.prototype.defaults,{"custom-name":"Dropdown Toggle",draggable:".dropdown",droppable:!0,traits:[{type:"checkbox",name:"aria-haspopup",label:"Popup"},{type:"checkbox",name:"aria-expanded",label:"Expanded"},{type:"select",label:"Type",name:"tagName",options:[{value:"button",name:"Button"},{value:"a",name:"Link"}],changeProp:1}]}),init:function(){this.listenTo(this,"change:tagName",this.changeTag)},changeTag:function(e){var t=this.get("attributes"),a=this.get("traits"),n=["tagName","aria-haspopup","aria-expanded"];a.models=a.models.filter(function(e){return n.indexOf(e.get("name"))>=0}),"a"===this.get("tagName")?a.add(c.prototype.defaults.traits):t.href&&delete t.href,this.set("attributes",Object.assign({},t)),this.sm.trigger("change:selectedComponent")}},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("dropdown-toggle"))return{type:"dropdown-toggle"}}}),view:i}),t.addType("dropdown-menu",{model:n.extend({defaults:Object.assign({},n.prototype.defaults,{"custom-name":"Dropdown Menu",draggable:".dropdown, .btn-group",droppable:"li"})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("dropdown-menu"))return{type:"dropdown-menu"}}}),view:o}),t.addType("dropdown-item",{model:u.extend({defaults:Object.assign({},u.prototype.defaults,{"custom-name":"Dropdown Item",draggable:".dropdown-menu"})},{isComponent:function(e){var t=e.parentNode;if(t&&t.classList&&t.classList.contains("dropdown-menu"))return{type:"dropdown-item"}}}),view:p})}},function(e,t,a){"use strict";function n(e){if(Array.isArray(e)){for(var t=0,a=Array(e.length);t1&&void 0!==arguments[1]&&arguments[1],e.DomComponents),a=t.getType("default"),o=a.model,l=a.view,s=[1,2,3,4,5,6,7,8,9,10,11,12];t.addType("row",{model:o.extend({defaults:Object.assign({},o.prototype.defaults,{"custom-name":"Row",tagName:"div",draggable:".container, .container-fluid",droppable:'[class*="col-xs"], [class*="col-sm"], [class*="col-md"], [class*="col-lg"]'})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("row"))return{type:"row"}}}),view:l}),t.addType("column",{model:o.extend({defaults:Object.assign({},o.prototype.defaults,{"custom-name":"Column",draggable:".row",droppable:!0,traits:o.prototype.defaults.traits.concat([{type:"select-class",options:[{value:"",name:"None"}].concat(n(s.map(function(e){return{value:"col-xs-"+e,name:e+"/12"}}))),label:"XS size"},{type:"select-class",options:[{value:"",name:"None"}].concat(n(s.map(function(e){return{value:"col-sm-"+e,name:e+"/12"}}))),label:"SM size"},{type:"select-class",options:[{value:"",name:"None"}].concat(n(s.map(function(e){return{value:"col-md-"+e,name:e+"/12"}}))),label:"MD size"},{type:"select-class",options:[{value:"",name:"None"}].concat(n(s.map(function(e){return{value:"col-lg-"+e,name:e+"/12"}}))),label:"LG size"},{type:"select-class",options:[{value:"",name:"None"}].concat(n(s.map(function(e){return{value:"col-xs-offset-"+e,name:e+"/12"}}))),label:"XS offset"},{type:"select-class",options:[{value:"",name:"None"}].concat(n(s.map(function(e){return{value:"col-sm-offset-"+e,name:e+"/12"}}))),label:"SM offset"},{type:"select-class",options:[{value:"",name:"None"}].concat(n(s.map(function(e){return{value:"col-md-offset-"+e,name:e+"/12"}}))),label:"MD offset"},{type:"select-class",options:[{value:"",name:"None"}].concat(n(s.map(function(e){return{value:"col-lg-offset-"+e,name:e+"/12"}}))),label:"LG offset"}])})},{isComponent:function(e){if(e&&e.className&&e.className.match(/col-(xs|sm|md|lg)-\d+/))return{type:"column"}}}),view:l})}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=a(0);t.default=function(e){var t=(arguments.length>1&&void 0!==arguments[1]&&arguments[1],e.DomComponents),a=t.getType("text"),o=a.model,l=a.view,s=["default","primary","success","info","warning","danger"];t.addType("label",{model:o.extend({defaults:Object.assign({},o.prototype.defaults,{"custom-name":"Label",classes:["label"],traits:[{type:"select-class",label:"Context",options:s.map(function(e){return{value:"label-"+e,name:(0,n.capitalize)(e)}})}]})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("label"))return{type:"label"}}}),view:l})}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=a(0);t.default=function(e){var t=(arguments.length>1&&void 0!==arguments[1]&&arguments[1],e.DomComponents),a=t.getType("default"),o=a.model,l=a.view,s=t.getType("header"),i=s.model,d=s.view,c=["left","right"],r=["top","middle","bottom"];t.addType("media",{model:o.extend({defaults:Object.assign({},o.prototype.defaults,{"custom-name":"Media",classes:["media"],droppable:".media-left, .media-right, .media-body"})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("media"))return{type:"media"}}}),view:l}),t.addType("media-side",{model:o.extend({defaults:Object.assign({},o.prototype.defaults,{"custom-name":"Media Side",draggable:".media",traits:[{type:"select-class",label:"Side",options:c.map(function(e){return{value:"media-"+e,name:(0,n.capitalize)(e)}})},{type:"select-class",label:"Position",options:r.map(function(e){return{value:"media-"+e,name:(0,n.capitalize)(e)}})}]})},{isComponent:function(e){if(e&&e.className&&e.className.match(/media-(left|right)/))return{type:"media-side"}}}),view:l}),t.addType("media-body",{model:o.extend({defaults:Object.assign({},o.prototype.defaults,{"custom-name":"Media Body",draggable:".media"})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("media-body"))return{type:"media-body"}}}),view:l}),t.addType("media-heading",{model:i.extend({defaults:Object.assign({},i.prototype.defaults,{"custom-name":"Media Heading",draggable:".media-body"})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("media-heading"))return{type:"media-heading"}}}),view:d})}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=a(0);t.default=function(e){var t=(arguments.length>1&&void 0!==arguments[1]&&arguments[1],e.DomComponents),a=t.getType("default"),o=a.model,l=a.view,s=t.getType("header"),i=s.model,d=s.view,c=["default","primary","success","info","warning","danger"];t.addType("panel",{model:o.extend({defaults:Object.assign({},o.prototype.defaults,{"custom-name":"Panel",droppable:".panel-heading, .panel-body, .panel-footer, .table, .list-group",traits:o.prototype.defaults.traits.concat([{type:"select-class",label:"Context",name:"context",options:c.map(function(e){return{value:"panel-"+e,name:(0,n.capitalize)(e)}})}])})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("panel"))return{type:"panel"}}}),view:l}),t.addType("panel-body",{model:o.extend({defaults:Object.assign({},o.prototype.defaults,{"custom-name":"Panel Body",draggable:".panel"})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("panel-body"))return{type:"panel-body"}}}),view:l}),t.addType("panel-footer",{model:o.extend({defaults:Object.assign({},o.prototype.defaults,{"custom-name":"Panel Footer",draggable:".panel"})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("panel-footer"))return{type:"panel-footer"}}}),view:l}),t.addType("panel-heading",{model:o.extend({defaults:Object.assign({},o.prototype.defaults,{"custom-name":"Panel Heading",draggable:".panel"})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("panel-heading"))return{type:"panel-heading"}}}),view:l}),t.addType("panel-title",{model:i.extend({defaults:Object.assign({},i.prototype.defaults,{"custom-name":"Panel Title",draggable:".panel-heading"})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("panel-title"))return{type:"panel-title"}}}),view:d})}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=(arguments.length>1&&void 0!==arguments[1]&&arguments[1],e.DomComponents),a=t.getType("default"),n=a.model,o=a.view;t.addType("thumbnail",{model:n.extend({defaults:Object.assign({},n.prototype.defaults,{"custom-name":"Thumbnail",droppable:"img, .caption"})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("thumbnail"))return{type:"thumbnail"}}}),view:o}),t.addType("thumbnail-caption",{model:n.extend({defaults:Object.assign({},n.prototype.defaults,{"custom-name":"Thumbnail Caption",draggable:".thumbnail"})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("caption")&&e.parentNode.classList.contains("thumbnail"))return{type:"thumbnail-caption"}}}),view:o})}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=(arguments.length>1&&void 0!==arguments[1]&&arguments[1],e.DomComponents),a=t.getType("default"),n=a.model,o=a.view;t.addType("well",{model:n.extend({defaults:Object.assign({},n.prototype.defaults,{"custom-name":"Well",traits:n.prototype.defaults.traits.concat([{type:"select-class",label:"Size",name:"size",options:[{value:"",name:"Default"},{value:"well-sm",name:"Small"},{value:"well-lg",name:"Large"}]}])})},{isComponent:function(e){if(e&&e.classList&&e.classList.contains("well"))return{type:"well"}}}),view:o})}},function(e,t,a){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=a(16),l=n(o),s=a(17),i=n(s),d=a(18),c=n(d);t.default=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};(0,l.default)(e,t),(0,i.default)(e,t),(0,c.default)(e,t)}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},a=e.BlockManager,n=t.blocks,o=t.category,l=o.basics,s=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return n.indexOf(e)>=0?a.add(e,t):null};s("address",{label:"Address",category:l,attributes:{class:"fa fa-location-arrow"},content:"\n \n Twitter, Inc. \n 1355 Market Street, Suite 900 \n San Francisco, CA 94103 \n Phone: (123) 456-7890\n \n "}),s("blockquote",{label:"Blockquote",category:l,attributes:{class:"fa fa-quote-left"},content:'\n \n Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.
\n Someone famous in Source Title \n \n '}),s("button",{label:"Button",category:l,attributes:{class:"fa fa-link"},content:{type:"button",content:"Button"}}),s("header",{label:"Header",category:l,attributes:{class:"fa fa-header"},content:{type:"header",content:"Insert your header text here"}}),s("image",{label:"Image",category:l,attributes:{class:"fa fa-picture-o"},content:{type:"image"}}),s("link",{label:"Link",category:l,attributes:{class:"fa fa-link"},content:{type:"link",content:"Link"}}),s("list",{label:"List",category:l,attributes:{class:"fa fa-list"},content:"\n \n Item 1 \n Item 2 \n Item 3 \n \n "}),s("paragraph",{label:"Paragraph",category:l,attributes:{class:"fa fa-align-left"},content:{type:"paragraph",content:"Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt."}})}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},a=e.BlockManager,n=t.blocks,o=t.category,l=o.components,s=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return n.indexOf(e)>=0?a.add(e,t):null};s("alert",{label:"Alert",category:l,attributes:{class:"fa fa-exclamation-triangle"},content:'\n \n Well done! You successfully read this important alert message.\n
\n '}),s("dropdown",{label:"Dropdown",category:l,attributes:{class:"fa fa-caret-square-o-down"},content:'\n \n \n Dropdown \n \n \n \n
\n '}),s("media",{label:"Media",category:l,content:'\n \n '}),s("panel",{label:"Panel",category:l,attributes:{class:"fa fa-window-maximize"},content:'\n \n '}),s("thumbnail",{label:"Thumbnail",category:l,content:'\n \n
\n
\n
Thumbnail label \n
Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
\n
Button
\n
\n
\n '}),s("well",{label:"Well",category:l,attributes:{class:"fa fa-square"},content:'\n \n '})}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},a=e.BlockManager,n=t.blocks,o=t.category,l=o.layout,s=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return n.indexOf(e)>=0?a.add(e,t):null};s("container",{label:"Container",category:l,attributes:{class:"fa fa-square-o"},content:{type:"container",classes:["container"]}}),s("row",{label:"Row",category:l,attributes:{class:"fa fa-minus"},content:{type:"row",classes:["row"]}}),s("column",{label:"Column",category:l,attributes:{class:"fa fa-columns"},content:{type:"column",classes:["col-md-12"]}}),s("columns-2",{label:"2 Columns",category:l,attributes:{class:"fa fa-columns"},content:'\n \n '}),s("columns-3",{label:"3 Columns",category:l,attributes:{class:"fa fa-columns"},content:'\n \n '}),s("columns-4",{label:"4 Columns",category:l,attributes:{class:"fa fa-columns"},content:'\n \n '}),s("columns-4-8",{label:"2 Columns 4/8",category:l,attributes:{class:"fa fa-columns"},content:'\n \n '}),s("columns-8-4",{label:"2 Columns 8/4",category:l,attributes:{class:"fa fa-columns"},content:'\n \n '})}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=(arguments.length>1&&void 0!==arguments[1]&&arguments[1],e.DeviceManager);t.add("Extra Small","575px"),t.add("Small","767px"),t.add("Medium","991px"),t.add("Large","1199px"),t.add("Extra Large","100%");var a=e.Panels,n=e.Commands;a.addPanel({id:"devices-buttons"}).get("buttons").add([{id:"deviceXl",command:"set-device-xl",className:"fa fa-desktop",text:"XL",attributes:{title:"Extra Large"},active:1},{id:"deviceLg",command:"set-device-lg",className:"fa fa-desktop",attributes:{title:"Large"}},{id:"deviceMd",command:"set-device-md",className:"fa fa-tablet",attributes:{title:"Medium"}},{id:"deviceSm",command:"set-device-sm",className:"fa fa-mobile",attributes:{title:"Small"}},{id:"deviceXs",command:"set-device-xs",className:"fa fa-mobile",attributes:{title:"Extra Small"}}]),n.add("set-device-xs",{run:function(e){e.setDevice("Extra Small")}}),n.add("set-device-sm",{run:function(e){e.setDevice("Small")}}),n.add("set-device-md",{run:function(e){e.setDevice("Medium")}}),n.add("set-device-lg",{run:function(e){e.setDevice("Large")}}),n.add("set-device-xl",{run:function(e){e.setDevice("Extra Large")}})}},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};(0,o.default)(e,t)};var n=a(21),o=function(e){return e&&e.__esModule?e:{default:e}}(n)},function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){arguments.length>1&&void 0!==arguments[1]&&arguments[1];e.TraitManager.addType("select-class",{events:{change:"onChange"},onValueChange:function(){for(var e=this.model.get("options").map(function(e){return e.value}),t=0;t0)for(var a=e[t].split(" "),n=0;n0&&this.target.removeClass(a[n]);var o=this.model.get("value");if(o.length>0&&"GJS_NO_CLASS"!==o)for(var l=o.split(" "),s=0;s
2 |
3 |
4 |
5 | GrapesJS Plugin Boilerplate
6 |
7 |
8 |
9 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | This is a demo content from index.html. For the development, you shouldn't edit this file, instead you can
23 | copy and rename it to _index.html, on next server start the new file will be served, and it will be ignored by git.
24 |
25 |
26 |
27 |
28 |
29 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "grapesjs-plugin-bootstrap",
3 | "version": "0.1.351",
4 | "description": "GrapesJS Plugin Bootstrap",
5 | "main": "dist/grapesjs-plugin-bootstrap.min.js",
6 | "scripts": {
7 | "lint": "eslint src",
8 | "v:patch": "npm version --no-git-tag-version patch",
9 | "build": "npm run v:patch && npm run standard && webpack --env.production",
10 | "start": "webpack-dev-server --open --progress --colors"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": ""
15 | },
16 | "keywords": [
17 | "grapesjs",
18 | "plugin",
19 | "boilerplate",
20 | "wysiwyg"
21 | ],
22 | "author": "olivmonnier",
23 | "license": "BSD-3-Clause",
24 | "babel": {
25 | "presets": [
26 | "env"
27 | ],
28 | "plugins": [
29 | "transform-object-rest-spread"
30 | ]
31 | },
32 | "devDependencies": {
33 | "babel-core": "^6.26.3",
34 | "babel-loader": "^7.1.5",
35 | "babel-plugin-transform-object-rest-spread": "^6.26.0",
36 | "babel-preset-env": "^1.7.0",
37 | "eslint": "^5.5.0",
38 | "html-webpack-plugin": "^3.2.0",
39 | "webpack": "^4.18.0",
40 | "webpack-cli": "^3.1.0",
41 | "webpack-dev-server": "^3.1.8"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/blocks/basics.js:
--------------------------------------------------------------------------------
1 | export default (editor, config = {}) => {
2 | const bm = editor.BlockManager
3 | const { blocks, category } = config
4 | const { basics } = category
5 | const addBlock = (name = '', attr = {}) => (blocks.indexOf(name) >= 0) ? bm.add(name, attr) : null
6 |
7 | // Address
8 | addBlock('address', {
9 | label: 'Address',
10 | category: basics,
11 | attributes: { class: 'fa fa-location-arrow' },
12 | content: `
13 |
14 | Twitter, Inc.
15 | 1355 Market Street, Suite 900
16 | San Francisco, CA 94103
17 | Phone: (123) 456-7890
18 |
19 | `
20 | })
21 |
22 | addBlock('blockquote', {
23 | label: 'Blockquote',
24 | category: basics,
25 | attributes: { class: 'fa fa-quote-left' },
26 | content: `
27 |
28 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante.
29 | Someone famous in Source Title
30 |
31 | `
32 | })
33 |
34 | // Button
35 | addBlock('button', {
36 | label: 'Button',
37 | category: basics,
38 | attributes: { class: 'fa fa-link' },
39 | content: {
40 | type: 'button',
41 | content: 'Button'
42 | }
43 | })
44 |
45 | // Header
46 | addBlock('header', {
47 | label: 'Header',
48 | category: basics,
49 | attributes: { class: 'fa fa-header' },
50 | content: {
51 | type: 'header',
52 | content: 'Insert your header text here'
53 | }
54 | })
55 |
56 | addBlock('image', {
57 | label: 'Image',
58 | category: basics,
59 | attributes: { class: 'fa fa-picture-o' },
60 | content: {
61 | type: 'image'
62 | }
63 | })
64 |
65 | addBlock('link', {
66 | label: 'Link',
67 | category: basics,
68 | attributes: { class: 'fa fa-link' },
69 | content: {
70 | type: 'link',
71 | content: 'Link'
72 | }
73 | })
74 |
75 | addBlock('list', {
76 | label: 'List',
77 | category: basics,
78 | attributes: { class: 'fa fa-list' },
79 | content: `
80 |
81 | Item 1
82 | Item 2
83 | Item 3
84 |
85 | `
86 | })
87 |
88 | addBlock('paragraph', {
89 | label: 'Paragraph',
90 | category: basics,
91 | attributes: { class: 'fa fa-align-left' },
92 | content: {
93 | type: 'paragraph',
94 | content:
95 | 'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.'
96 | }
97 | })
98 | }
99 |
--------------------------------------------------------------------------------
/src/blocks/components.js:
--------------------------------------------------------------------------------
1 | export default (editor, config = {}) => {
2 | const bm = editor.BlockManager
3 | const { blocks, category } = config
4 | const { components } = category
5 | const addBlock = (name = '', attr = {}) => (blocks.indexOf(name) >= 0) ? bm.add(name, attr) : null
6 |
7 | addBlock('alert', {
8 | label: 'Alert',
9 | category: components,
10 | attributes: { class: 'fa fa-exclamation-triangle' },
11 | content: `
12 |
13 | Well done! You successfully read this important alert message.
14 |
15 | `
16 | })
17 |
18 | // Dropdown
19 | addBlock('dropdown', {
20 | label: 'Dropdown',
21 | category: components,
22 | attributes: { class: 'fa fa-caret-square-o-down' },
23 | content: `
24 |
25 |
26 | Dropdown
27 |
28 |
29 |
34 |
35 | `
36 | })
37 |
38 | // Media
39 | addBlock('media', {
40 | label: 'Media',
41 | category: components,
42 | content: `
43 |
54 | `
55 | })
56 |
57 | // Panel
58 | addBlock('panel', {
59 | label: 'Panel',
60 | category: components,
61 | attributes: { class: 'fa fa-window-maximize' },
62 | content: `
63 |
64 |
65 |
Heading
66 |
67 |
70 |
73 |
74 | `
75 | })
76 |
77 | // Thumbnail
78 | addBlock('thumbnail', {
79 | label: 'Thumbnail',
80 | category: components,
81 | content: `
82 |
83 |
84 |
85 |
Thumbnail label
86 |
Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
87 |
Button
88 |
89 |
90 | `
91 | })
92 |
93 | // Well
94 | addBlock('well', {
95 | label: 'Well',
96 | category: components,
97 | attributes: { class: 'fa fa-square' },
98 | content: `
99 |
102 | `
103 | })
104 | }
105 |
--------------------------------------------------------------------------------
/src/blocks/index.js:
--------------------------------------------------------------------------------
1 | import basics from './basics'
2 | import components from './components'
3 | import layout from './layout'
4 |
5 | export default (editor, config = {}) => {
6 | basics(editor, config)
7 | components(editor, config)
8 | layout(editor, config)
9 | }
10 |
--------------------------------------------------------------------------------
/src/blocks/layout.js:
--------------------------------------------------------------------------------
1 | export default (editor, config = {}) => {
2 | const bm = editor.BlockManager
3 | const { blocks, category } = config
4 | const { layout } = category
5 | const addBlock = (name = '', attr = {}) => (blocks.indexOf(name) >= 0) ? bm.add(name, attr) : null
6 |
7 | // Container
8 | addBlock('container', {
9 | label: 'Container',
10 | category: layout,
11 | attributes: { class: 'fa fa-square-o' },
12 | content: {
13 | type: 'container',
14 | classes: ['container']
15 | }
16 | })
17 |
18 | // Row
19 | addBlock('row', {
20 | label: 'Row',
21 | category: layout,
22 | attributes: { class: 'fa fa-minus' },
23 | content: {
24 | type: 'row',
25 | classes: ['row']
26 | }
27 | })
28 |
29 | // Column
30 | addBlock('column', {
31 | label: 'Column',
32 | category: layout,
33 | attributes: { class: 'fa fa-columns' },
34 | content: {
35 | type: 'column',
36 | classes: ['col-md-12']
37 | }
38 | })
39 |
40 | // Columns 2
41 | addBlock('columns-2', {
42 | label: '2 Columns',
43 | category: layout,
44 | attributes: { class: 'fa fa-columns' },
45 | content: `
46 |
50 | `
51 | })
52 |
53 | // Columns 3
54 | addBlock('columns-3', {
55 | label: '3 Columns',
56 | category: layout,
57 | attributes: { class: 'fa fa-columns' },
58 | content: `
59 |
64 | `
65 | })
66 |
67 | // Columns 4
68 | addBlock('columns-4', {
69 | label: '4 Columns',
70 | category: layout,
71 | attributes: { class: 'fa fa-columns' },
72 | content: `
73 |
79 | `
80 | })
81 |
82 | // Columns 4/8
83 | addBlock('columns-4-8', {
84 | label: '2 Columns 4/8',
85 | category: layout,
86 | attributes: { class: 'fa fa-columns' },
87 | content: `
88 |
92 | `
93 | })
94 |
95 | // Columns 8/4
96 | addBlock('columns-8-4', {
97 | label: '2 Columns 8/4',
98 | category: layout,
99 | attributes: { class: 'fa fa-columns' },
100 | content: `
101 |
105 | `
106 | })
107 | }
108 |
--------------------------------------------------------------------------------
/src/components/alert.js:
--------------------------------------------------------------------------------
1 | import { capitalize } from '../utils'
2 |
3 | export default (editor, config = {}) => {
4 | const domc = editor.DomComponents
5 | const textType = domc.getType('text')
6 | const textModel = textType.model
7 | const textView = textType.view
8 | const contexts = ['success', 'info', 'warning', 'danger']
9 |
10 | domc.addType('alert', {
11 | model: textModel.extend({
12 | defaults: Object.assign({}, textModel.prototype.defaults, {
13 | 'custom-name': 'Alert',
14 | attributes: {
15 | role: 'alert'
16 | },
17 | classes: ['alert'],
18 | traits: textModel.prototype.defaults.traits.concat([
19 | {
20 | type: 'select-class',
21 | label: 'Context',
22 | options: contexts.map(context => ({ value: `alert-${context}`, name: capitalize(context) }))
23 | }
24 | ])
25 | })
26 | }, {
27 | isComponent (el) {
28 | if (el && el.classList && el.classList.contains('alert')) {
29 | return { type: 'alert' }
30 | }
31 | }
32 | }),
33 | view: textView
34 | })
35 | }
36 |
--------------------------------------------------------------------------------
/src/components/basic.js:
--------------------------------------------------------------------------------
1 | import { capitalize, getModel, getView } from '../utils'
2 |
3 | export default (editor, config = {}) => {
4 | const domc = editor.DomComponents
5 | let defaultModel = getModel(editor, 'default')
6 | let defaultView = getView(editor, 'default')
7 | let textModel = getModel(editor, 'text')
8 | let textView = getView(editor, 'text')
9 | let imgModel = getModel(editor, 'image')
10 | let imgView = getView(editor, 'image')
11 | let linkModel = getModel(editor, 'link')
12 | let linkView = getView(editor, 'link')
13 |
14 | const contexts = ['primary', 'success', 'info', 'warning', 'danger']
15 | const alignments = ['left', 'center', 'right', 'justify']
16 | const textStyles = ['lowercase', 'uppercase', 'capitalize']
17 | const imgShapes = ['rounded', 'circle', 'thumbnail']
18 | const sizes = [
19 | ['lg', 'large'],
20 | ['sm', 'small'],
21 | ['xs', 'extra small']
22 | ]
23 |
24 | domc.addType('default', {
25 | model: defaultModel.extend({
26 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
27 | traits: defaultModel.prototype.defaults.traits.concat([
28 | {
29 | type: 'select-class',
30 | label: 'Float',
31 | options: [
32 | { value: '', name: 'None' },
33 | { value: 'pull-left', name: 'Left' },
34 | { value: 'pull-right', name: 'Right' }
35 | ]
36 | },
37 | {
38 | type: 'select-class',
39 | label: 'Color',
40 | options: [
41 | { value: '', name: 'None' },
42 | ...['muted'].concat(contexts).map(context => ({ value: `text-${context}`, name: capitalize(context) }))
43 | ]
44 | },
45 | {
46 | type: 'select-class',
47 | label: 'Background',
48 | options: [
49 | { value: '', name: 'None' },
50 | ...contexts.map(context => ({ value: `bg-${context}`, name: capitalize(context) }))
51 | ]
52 | }
53 | ])
54 | })
55 | }),
56 | view: defaultView
57 | })
58 |
59 | defaultModel = getModel(editor, 'default')
60 | defaultView = getView(editor, 'default')
61 |
62 | domc.addType('image', {
63 | model: imgModel.extend({
64 | defaults: Object.assign({}, imgModel.prototype.defaults, {
65 | 'custom-name': 'Image',
66 | attributes: {
67 | src: 'https://dummyimage.com/450x250/999/222'
68 | },
69 | traits: [
70 | {
71 | type: 'text',
72 | label: 'Source (URL)',
73 | name: 'src'
74 | },
75 | {
76 | type: 'text',
77 | label: 'Alternate text',
78 | name: 'alt'
79 | },
80 | {
81 | type: 'select-class',
82 | label: 'Responsive',
83 | options: [
84 | { value: '', name: 'No' },
85 | { value: 'img-responsive', name: 'Yes' }
86 | ]
87 | },
88 | {
89 | type: 'select-class',
90 | label: 'Shape',
91 | options: [
92 | { value: '', name: 'none' },
93 | ...imgShapes.map(shape => ({ value: `img-${shape}`, name: capitalize(shape) }))
94 | ]
95 | }
96 | ]
97 | })
98 | }, {
99 | isComponent (el) {
100 | if (el && el.tagName && el.tagName === 'IMG') {
101 | return { type: 'image' }
102 | }
103 | }
104 | }),
105 | view: imgView
106 | })
107 |
108 | domc.addType('text', {
109 | model: defaultModel.extend({
110 | defaults: Object.assign({}, textModel.prototype.defaults, {
111 | droppable: true,
112 | traits: defaultModel.prototype.defaults.traits.concat([
113 | {
114 | type: 'select-class',
115 | label: 'Alignment',
116 | options: [
117 | ...alignments.map(align => ({ value: `text-${align}`, name: capitalize(align) })),
118 | { value: 'text-nowrap', name: 'No wrap' }
119 | ]
120 | },
121 | {
122 | type: 'select-class',
123 | label: 'Transform',
124 | options: [
125 | { value: '', name: 'None' },
126 | ...textStyles.map(style => ({ value: `text-${style}`, name: capitalize(style) }))
127 | ]
128 | }
129 | ])
130 | })
131 | }),
132 | view: textView
133 | })
134 |
135 | textModel = getModel(editor, 'text')
136 | textView = getView(editor, 'text')
137 |
138 | domc.addType('header', {
139 | model: textModel.extend(
140 | {
141 | defaults: Object.assign({}, textModel.prototype.defaults, {
142 | 'custom-name': 'Header',
143 | tagName: 'h1',
144 | traits: textModel.prototype.defaults.traits.concat([
145 | {
146 | type: 'select',
147 | options: [
148 | { value: 'h1', name: 'One (largest)' },
149 | { value: 'h2', name: 'Two' },
150 | { value: 'h3', name: 'Three' },
151 | { value: 'h4', name: 'Four' },
152 | { value: 'h5', name: 'Five' },
153 | { value: 'h6', name: 'Six (smallest)' }
154 | ],
155 | label: 'Size',
156 | name: 'tagName',
157 | changeProp: 1
158 | }
159 | ])
160 | })
161 | },
162 | {
163 | isComponent (el) {
164 | if (
165 | el &&
166 | el.tagName &&
167 | ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(el.tagName)
168 | ) {
169 | return { type: 'header' }
170 | }
171 | }
172 | }
173 | ),
174 | view: textView
175 | })
176 |
177 | domc.addType('link', {
178 | model: linkModel.extend({
179 | defaults: Object.assign({}, linkModel.prototype.defaults, {
180 | traits: [
181 | {
182 | type: 'text',
183 | name: 'id',
184 | label: 'Id',
185 | placeholder: 'eg. Text here'
186 | }].concat(linkModel.prototype.defaults.traits, [
187 | {
188 | type: 'select',
189 | label: 'Toggles',
190 | name: 'data-toggle',
191 | options: [
192 | {value: '', name: 'None'},
193 | {value: 'button', name: 'Self'},
194 | {value: 'collapse', name: 'Collapse'},
195 | {value: 'dropdown', name: 'Dropdown'}
196 | ],
197 | changeProp: 1
198 | }
199 | ])
200 | })
201 | }),
202 | view: linkView
203 | })
204 |
205 | linkModel = getModel(editor, 'link')
206 | linkView = getView(editor, 'link')
207 |
208 | domc.addType('button', {
209 | model: linkModel.extend({
210 | defaults: Object.assign({}, linkModel.prototype.defaults, {
211 | 'custom-name': 'Button',
212 | attributes: {
213 | role: 'button'
214 | },
215 | classes: ['btn'],
216 | traits: linkModel.prototype.defaults.traits.concat([
217 | {
218 | type: 'select-class',
219 | label: 'Context',
220 | options: [
221 | { value: 'btn-default', name: 'Default' },
222 | ...contexts.map(context => ({ value: `btn-${context}`, name: capitalize(context) }))
223 | ]
224 | },
225 | {
226 | type: 'select-class',
227 | label: 'Size',
228 | options: [
229 | { value: '', name: 'Default' },
230 | ...sizes.map(size => ({ value: `btn-${size[0]}`, name: capitalize(size[1]) }))
231 | ]
232 | },
233 | {
234 | type: 'select-class',
235 | label: 'Width',
236 | options: [
237 | { value: '', name: 'Inline' },
238 | { value: 'btn-block', name: 'Block' }
239 | ]
240 | }
241 | ])
242 | }, {
243 | isComponent (el) {
244 | if (el && el.classList && el.classList.contains('btn')) {
245 | return { type: 'button' }
246 | }
247 | }
248 | })
249 | }),
250 | view: linkView
251 | })
252 |
253 | domc.addType('list', {
254 | model: defaultModel.extend({
255 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
256 | 'custom-name': 'List',
257 | droppable: true,
258 | traits: defaultModel.prototype.defaults.traits.concat([
259 | {
260 | type: 'select',
261 | label: 'Type',
262 | name: 'tagName',
263 | options: [
264 | { value: 'ul', name: 'Unordered' },
265 | { value: 'ol', name: 'Ordered' }
266 | ],
267 | changeProp: 1
268 | },
269 | {
270 | type: 'select-class',
271 | label: 'Style',
272 | options: [
273 | { value: '', name: 'none' },
274 | { value: 'list-unstyled', name: 'Unstyled' },
275 | { value: 'list-inline', name: 'Inline' }
276 | ]
277 | }
278 | ])
279 | })
280 | }, {
281 | isComponent (el) {
282 | if (el && el.tagName && (el.tagName === 'UL' || el.tagName === 'OL')) {
283 | return { type: 'list' }
284 | }
285 | }
286 | }),
287 | view: defaultView
288 | })
289 |
290 | domc.addType('list-item', {
291 | model: textModel.extend({
292 | defaults: Object.assign({}, textModel.prototype.defaults, {
293 | 'custom-name': 'Item',
294 | tagName: 'li',
295 | draggable: 'ul, ol'
296 | })
297 | }, {
298 | isComponent (el) {
299 | if (el && el.tagName && el.tagName === 'LI') {
300 | return { type: 'list-item' }
301 | }
302 | }
303 | }),
304 | view: textView
305 | })
306 |
307 | domc.addType('paragraph', {
308 | model: textModel.extend({
309 | defaults: Object.assign({}, textModel.prototype.defaults, {
310 | 'custom-name': 'Paragraph',
311 | tagName: 'p',
312 | traits: textModel.prototype.defaults.traits.concat([
313 | {
314 | type: 'select-class',
315 | label: 'Lead',
316 | options: [
317 | { value: '', name: 'No' },
318 | { value: 'lead', name: 'Yes' }
319 | ]
320 | }
321 | ])
322 | })
323 | }, {
324 | isComponent (el) {
325 | if (el && el.tagName && el.tagName === 'P') {
326 | return { type: 'paragraph' }
327 | }
328 | }
329 | }),
330 | view: textView
331 | })
332 |
333 | domc.addType('blockquote', {
334 | model: defaultModel.extend({
335 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
336 | tagName: 'blockquote',
337 | traits: defaultModel.prototype.defaults.traits.concat([
338 | {
339 | type: 'select-class',
340 | label: 'Reversed',
341 | options: [
342 | { value: '', name: 'No' },
343 | { value: 'blockquote-reverse', name: 'Yes' }
344 | ]
345 | }
346 | ])
347 | })
348 | }, {
349 | isComponent (el) {
350 | if (el && el.tagName && el.tagName === 'BLOCKQUOTE') {
351 | return { type: 'blockquote' }
352 | }
353 | }
354 | }),
355 | view: defaultView
356 | })
357 | }
358 |
--------------------------------------------------------------------------------
/src/components/container.js:
--------------------------------------------------------------------------------
1 | export default (editor, config = {}) => {
2 | const domc = editor.DomComponents
3 | const defaultType = domc.getType('default')
4 | const defaultModel = defaultType.model
5 | const defaultView = defaultType.view
6 |
7 | domc.addType('container', {
8 | model: defaultModel.extend({
9 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
10 | 'custom-name': 'Container',
11 | tagName: 'div',
12 | droppable: true,
13 | traits: defaultModel.prototype.defaults.traits.concat([
14 | {
15 | type: 'select-class',
16 | label: 'Type',
17 | options: [
18 | { value: 'container', name: 'Fixed' },
19 | { value: 'container-fluid', name: 'Fluid' }
20 | ]
21 | }
22 | ])
23 | })
24 | }, {
25 | isComponent (el) {
26 | if (el && el.classList && (el.classList.contains('container') || el.classList.contains('container-fluid'))) {
27 | return { type: 'container' }
28 | }
29 | }
30 | }),
31 | view: defaultView
32 | })
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/dropdown.js:
--------------------------------------------------------------------------------
1 | export default (editor, config = {}) => {
2 | const domc = editor.DomComponents
3 | const defaultType = domc.getType('default')
4 | const defaultModel = defaultType.model
5 | const defaultView = defaultType.view
6 | const textType = domc.getType('text')
7 | const textModel = textType.model
8 | const textView = textType.view
9 | const linkType = domc.getType('link')
10 | const linkModel = linkType.model
11 | const listItemType = domc.getType('list-item')
12 | const listItemModel = listItemType.model
13 | const listItemView = listItemType.view
14 |
15 | domc.addType('dropdown', {
16 | model: defaultModel.extend({
17 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
18 | 'custom-name': 'Dropdown',
19 | droppable: 'a, button, .dropdown-menu',
20 | traits: defaultModel.prototype.defaults.traits.concat([
21 | {
22 | type: 'select-class',
23 | label: 'State',
24 | options: [
25 | { value: '', name: 'Closed' },
26 | { value: 'open', name: 'Open' }
27 | ]
28 | }
29 | ])
30 | })
31 | }, {
32 | isComponent (el) {
33 | if (el && el.classList && el.classList.contains('dropdown')) {
34 | return { type: 'dropdown' }
35 | }
36 | }
37 | }),
38 | view: defaultView
39 | })
40 |
41 | domc.addType('dropdown-toggle', {
42 | model: textModel.extend({
43 | defaults: Object.assign({}, textModel.prototype.defaults, {
44 | 'custom-name': 'Dropdown Toggle',
45 | draggable: '.dropdown',
46 | droppable: true,
47 | traits: [
48 | {
49 | type: 'checkbox',
50 | name: 'aria-haspopup',
51 | label: 'Popup'
52 | },
53 | {
54 | type: 'checkbox',
55 | name: 'aria-expanded',
56 | label: 'Expanded'
57 | },
58 | {
59 | type: 'select',
60 | label: 'Type',
61 | name: 'tagName',
62 | options: [
63 | { value: 'button', name: 'Button' },
64 | { value: 'a', name: 'Link' }
65 | ],
66 | changeProp: 1
67 | }
68 | ]
69 | }),
70 | init () {
71 | this.listenTo(this, 'change:tagName', this.changeTag)
72 | },
73 | changeTag (el) {
74 | const attrs = this.get('attributes')
75 | const traits = this.get('traits')
76 | const traitsToKeep = ['tagName', 'aria-haspopup', 'aria-expanded']
77 |
78 | traits.models = traits.models.filter(model => traitsToKeep.indexOf(model.get('name')) >= 0)
79 |
80 | if (this.get('tagName') === 'a') {
81 | traits.add(linkModel.prototype.defaults.traits)
82 | } else {
83 | if (attrs.href) delete attrs.href
84 | }
85 |
86 | this.set('attributes', Object.assign({}, attrs))
87 | this.sm.trigger('change:selectedComponent')
88 | }
89 | }, {
90 | isComponent (el) {
91 | if (el && el.classList && el.classList.contains('dropdown-toggle')) {
92 | return { type: 'dropdown-toggle' }
93 | }
94 | }
95 | }),
96 | view: textView
97 | })
98 |
99 | domc.addType('dropdown-menu', {
100 | model: defaultModel.extend({
101 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
102 | 'custom-name': 'Dropdown Menu',
103 | draggable: '.dropdown, .btn-group',
104 | droppable: 'li'
105 | })
106 | }, {
107 | isComponent (el) {
108 | if (el && el.classList && el.classList.contains('dropdown-menu')) {
109 | return { type: 'dropdown-menu' }
110 | }
111 | }
112 | }),
113 | view: defaultView
114 | })
115 |
116 | domc.addType('dropdown-item', {
117 | model: listItemModel.extend({
118 | defaults: Object.assign({}, listItemModel.prototype.defaults, {
119 | 'custom-name': 'Dropdown Item',
120 | draggable: '.dropdown-menu'
121 | })
122 | }, {
123 | isComponent (el) {
124 | const parent = el.parentNode
125 | if (parent && parent.classList && parent.classList.contains('dropdown-menu')) {
126 | return { type: 'dropdown-item' }
127 | }
128 | }
129 | }),
130 | view: listItemView
131 | })
132 | }
133 |
--------------------------------------------------------------------------------
/src/components/grid.js:
--------------------------------------------------------------------------------
1 | export default (editor, config = {}) => {
2 | const domc = editor.DomComponents
3 | const defaultType = domc.getType('default')
4 | const defaultModel = defaultType.model
5 | const defaultView = defaultType.view
6 | const cols = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
7 |
8 | domc.addType('row', {
9 | model: defaultModel.extend({
10 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
11 | 'custom-name': 'Row',
12 | tagName: 'div',
13 | draggable: '.container, .container-fluid',
14 | droppable: '[class*="col-xs"], [class*="col-sm"], [class*="col-md"], [class*="col-lg"]'
15 | })
16 | }, {
17 | isComponent (el) {
18 | if (el && el.classList && el.classList.contains('row')) {
19 | return { type: 'row' }
20 | }
21 | }
22 | }),
23 | view: defaultView
24 | })
25 |
26 | domc.addType('column', {
27 | model: defaultModel.extend({
28 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
29 | 'custom-name': 'Column',
30 | draggable: '.row',
31 | droppable: true,
32 | traits: defaultModel.prototype.defaults.traits.concat([{
33 | type: 'select-class',
34 | options: [
35 | {value: '', name: 'None'},
36 | ...cols.map((i) => ({ value: `col-xs-${i}`, name: `${i}/12` }))
37 | ],
38 | label: 'XS size'
39 | }, {
40 | type: 'select-class',
41 | options: [
42 | {value: '', name: 'None'},
43 | ...cols.map((i) => ({ value: `col-sm-${i}`, name: `${i}/12` }))
44 | ],
45 | label: 'SM size'
46 | }, {
47 | type: 'select-class',
48 | options: [
49 | {value: '', name: 'None'},
50 | ...cols.map((i) => ({ value: `col-md-${i}`, name: `${i}/12` }))
51 | ],
52 | label: 'MD size'
53 | }, {
54 | type: 'select-class',
55 | options: [
56 | {value: '', name: 'None'},
57 | ...cols.map((i) => ({ value: `col-lg-${i}`, name: `${i}/12` }))
58 | ],
59 | label: 'LG size'
60 | }, {
61 | type: 'select-class',
62 | options: [
63 | {value: '', name: 'None'},
64 | ...cols.map((i) => ({ value: `col-xs-offset-${i}`, name: `${i}/12` }))
65 | ],
66 | label: 'XS offset'
67 | }, {
68 | type: 'select-class',
69 | options: [
70 | {value: '', name: 'None'},
71 | ...cols.map((i) => ({ value: `col-sm-offset-${i}`, name: `${i}/12` }))
72 | ],
73 | label: 'SM offset'
74 | }, {
75 | type: 'select-class',
76 | options: [
77 | {value: '', name: 'None'},
78 | ...cols.map((i) => ({ value: `col-md-offset-${i}`, name: `${i}/12` }))
79 | ],
80 | label: 'MD offset'
81 | }, {
82 | type: 'select-class',
83 | options: [
84 | {value: '', name: 'None'},
85 | ...cols.map((i) => ({ value: `col-lg-offset-${i}`, name: `${i}/12` }))
86 | ],
87 | label: 'LG offset'
88 | }])
89 | })
90 | }, {
91 | isComponent (el) {
92 | if (el && el.className && el.className.match(/col-(xs|sm|md|lg)-\d+/)) {
93 | return { type: 'column' }
94 | }
95 | }
96 | }),
97 | view: defaultView
98 | })
99 | }
100 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | import alert from './alert'
2 | import basic from './basic'
3 | import container from './container'
4 | import dropdown from './dropdown'
5 | import grid from './grid'
6 | import label from './label'
7 | import media from './media'
8 | import panel from './panel'
9 | import thumbnail from './thumbnail'
10 | import well from './well'
11 |
12 | export default (editor, config = {}) => {
13 | alert(editor, config)
14 | basic(editor, config)
15 | container(editor, config)
16 | dropdown(editor, config)
17 | grid(editor, config)
18 | label(editor, config)
19 | media(editor, config)
20 | panel(editor, config)
21 | thumbnail(editor, config)
22 | well(editor, config)
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/label.js:
--------------------------------------------------------------------------------
1 | import { capitalize } from '../utils'
2 |
3 | export default (editor, config = {}) => {
4 | const domc = editor.DomComponents
5 | const textType = domc.getType('text')
6 | const textModel = textType.model
7 | const textView = textType.view
8 | const contexts = ['default', 'primary', 'success', 'info', 'warning', 'danger']
9 |
10 | domc.addType('label', {
11 | model: textModel.extend({
12 | defaults: Object.assign({}, textModel.prototype.defaults, {
13 | 'custom-name': 'Label',
14 | classes: ['label'],
15 | traits: [
16 | {
17 | type: 'select-class',
18 | label: 'Context',
19 | options: contexts.map(context => ({ value: `label-${context}`, name: capitalize(context) }))
20 | }
21 | ]
22 | })
23 | }, {
24 | isComponent (el) {
25 | if (el && el.classList && el.classList.contains('label')) {
26 | return { type: 'label' }
27 | }
28 | }
29 | }),
30 | view: textView
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/src/components/media.js:
--------------------------------------------------------------------------------
1 | import { capitalize } from '../utils'
2 |
3 | export default (editor, config = {}) => {
4 | const domc = editor.DomComponents
5 | const defaultType = domc.getType('default')
6 | const defaultModel = defaultType.model
7 | const defaultView = defaultType.view
8 | const headerType = domc.getType('header')
9 | const headerModel = headerType.model
10 | const headerView = headerType.view
11 | const sides = ['left', 'right']
12 | const positions = ['top', 'middle', 'bottom']
13 |
14 | domc.addType('media', {
15 | model: defaultModel.extend({
16 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
17 | 'custom-name': 'Media',
18 | classes: ['media'],
19 | droppable: '.media-left, .media-right, .media-body'
20 | })
21 | }, {
22 | isComponent (el) {
23 | if (el && el.classList && el.classList.contains('media')) {
24 | return { type: 'media' }
25 | }
26 | }
27 | }),
28 | view: defaultView
29 | })
30 |
31 | domc.addType('media-side', {
32 | model: defaultModel.extend({
33 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
34 | 'custom-name': 'Media Side',
35 | draggable: '.media',
36 | traits: [
37 | {
38 | type: 'select-class',
39 | label: 'Side',
40 | options: sides.map(side => ({ value: `media-${side}`, name: capitalize(side) }))
41 | },
42 | {
43 | type: 'select-class',
44 | label: 'Position',
45 | options: positions.map(position => ({ value: `media-${position}`, name: capitalize(position) }))
46 | }
47 | ]
48 | })
49 | }, {
50 | isComponent (el) {
51 | if (el && el.className && el.className.match(/media-(left|right)/)) {
52 | return { type: 'media-side' }
53 | }
54 | }
55 | }),
56 | view: defaultView
57 | })
58 |
59 | domc.addType('media-body', {
60 | model: defaultModel.extend({
61 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
62 | 'custom-name': 'Media Body',
63 | draggable: '.media'
64 | })
65 | }, {
66 | isComponent (el) {
67 | if (el && el.classList && el.classList.contains('media-body')) {
68 | return { type: 'media-body' }
69 | }
70 | }
71 | }),
72 | view: defaultView
73 | })
74 |
75 | domc.addType('media-heading', {
76 | model: headerModel.extend({
77 | defaults: Object.assign({}, headerModel.prototype.defaults, {
78 | 'custom-name': 'Media Heading',
79 | draggable: '.media-body'
80 | })
81 | }, {
82 | isComponent (el) {
83 | if (el && el.classList && el.classList.contains('media-heading')) {
84 | return { type: 'media-heading' }
85 | }
86 | }
87 | }),
88 | view: headerView
89 | })
90 | }
91 |
--------------------------------------------------------------------------------
/src/components/panel.js:
--------------------------------------------------------------------------------
1 | import { capitalize } from '../utils'
2 |
3 | export default (editor, config = {}) => {
4 | const domc = editor.DomComponents
5 | const defaultType = domc.getType('default')
6 | const defaultModel = defaultType.model
7 | const defaultView = defaultType.view
8 | const headerType = domc.getType('header')
9 | const headerModel = headerType.model
10 | const headerView = headerType.view
11 | const contextList = ['default', 'primary', 'success', 'info', 'warning', 'danger']
12 |
13 | domc.addType('panel', {
14 | model: defaultModel.extend({
15 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
16 | 'custom-name': 'Panel',
17 | droppable: '.panel-heading, .panel-body, .panel-footer, .table, .list-group',
18 | traits: defaultModel.prototype.defaults.traits.concat([{
19 | type: 'select-class',
20 | label: 'Context',
21 | name: 'context',
22 | options: contextList.map(val =>
23 | ({ value: `panel-${val}`, name: capitalize(val) }))
24 | }])
25 | })
26 | }, {
27 | isComponent (el) {
28 | if (el && el.classList && el.classList.contains('panel')) {
29 | return { type: 'panel' }
30 | }
31 | }
32 | }),
33 | view: defaultView
34 | })
35 |
36 | domc.addType('panel-body', {
37 | model: defaultModel.extend({
38 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
39 | 'custom-name': 'Panel Body',
40 | draggable: '.panel'
41 | })
42 | }, {
43 | isComponent (el) {
44 | if (el && el.classList && el.classList.contains('panel-body')) {
45 | return { type: 'panel-body' }
46 | }
47 | }
48 | }),
49 | view: defaultView
50 | })
51 |
52 | domc.addType('panel-footer', {
53 | model: defaultModel.extend({
54 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
55 | 'custom-name': 'Panel Footer',
56 | draggable: '.panel'
57 | })
58 | }, {
59 | isComponent (el) {
60 | if (el && el.classList && el.classList.contains('panel-footer')) {
61 | return { type: 'panel-footer' }
62 | }
63 | }
64 | }),
65 | view: defaultView
66 | })
67 |
68 | domc.addType('panel-heading', {
69 | model: defaultModel.extend({
70 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
71 | 'custom-name': 'Panel Heading',
72 | draggable: '.panel'
73 | })
74 | }, {
75 | isComponent (el) {
76 | if (el && el.classList && el.classList.contains('panel-heading')) {
77 | return { type: 'panel-heading' }
78 | }
79 | }
80 | }),
81 | view: defaultView
82 | })
83 |
84 | domc.addType('panel-title', {
85 | model: headerModel.extend({
86 | defaults: Object.assign({}, headerModel.prototype.defaults, {
87 | 'custom-name': 'Panel Title',
88 | draggable: '.panel-heading'
89 | })
90 | }, {
91 | isComponent (el) {
92 | if (el && el.classList && el.classList.contains('panel-title')) {
93 | return { type: 'panel-title' }
94 | }
95 | }
96 | }),
97 | view: headerView
98 | })
99 | }
100 |
--------------------------------------------------------------------------------
/src/components/thumbnail.js:
--------------------------------------------------------------------------------
1 | export default (editor, config = {}) => {
2 | const domc = editor.DomComponents
3 | const defaultType = domc.getType('default')
4 | const defaultModel = defaultType.model
5 | const defaultView = defaultType.view
6 |
7 | domc.addType('thumbnail', {
8 | model: defaultModel.extend({
9 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
10 | 'custom-name': 'Thumbnail',
11 | droppable: 'img, .caption'
12 | })
13 | }, {
14 | isComponent (el) {
15 | if (el && el.classList && el.classList.contains('thumbnail')) {
16 | return { type: 'thumbnail' }
17 | }
18 | }
19 | }),
20 | view: defaultView
21 | })
22 |
23 | domc.addType('thumbnail-caption', {
24 | model: defaultModel.extend({
25 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
26 | 'custom-name': 'Thumbnail Caption',
27 | draggable: '.thumbnail'
28 | })
29 | }, {
30 | isComponent (el) {
31 | if (el && el.classList && el.classList.contains('caption') && el.parentNode.classList.contains('thumbnail')) {
32 | return { type: 'thumbnail-caption' }
33 | }
34 | }
35 | }),
36 | view: defaultView
37 | })
38 | }
39 |
--------------------------------------------------------------------------------
/src/components/well.js:
--------------------------------------------------------------------------------
1 | export default (editor, config = {}) => {
2 | const domc = editor.DomComponents
3 | const defaultType = domc.getType('default')
4 | const defaultModel = defaultType.model
5 | const defaultView = defaultType.view
6 |
7 | domc.addType('well', {
8 | model: defaultModel.extend({
9 | defaults: Object.assign({}, defaultModel.prototype.defaults, {
10 | 'custom-name': 'Well',
11 | traits: defaultModel.prototype.defaults.traits.concat([{
12 | type: 'select-class',
13 | label: 'Size',
14 | name: 'size',
15 | options: [
16 | { value: '', name: 'Default' },
17 | { value: 'well-sm', name: 'Small' },
18 | { value: 'well-lg', name: 'Large' }
19 | ]
20 | }])
21 | })
22 | }, {
23 | isComponent (el) {
24 | if (el && el.classList && el.classList.contains('well')) {
25 | return { type: 'well' }
26 | }
27 | }
28 | }),
29 | view: defaultView
30 | })
31 | }
32 |
--------------------------------------------------------------------------------
/src/constants.js:
--------------------------------------------------------------------------------
1 | export default {
2 | addBasicStyle: true,
3 | blocks: [
4 | 'address', 'alert', 'blockquote', 'button', 'container', 'column', 'columns-2', 'columns-3', 'columns-4', 'columns-4-8',
5 | 'columns-8-4', 'dropdown', 'header', 'image', 'label', 'link', 'list', 'media', 'panel', 'paragraph', 'row', 'thumbnail',
6 | 'well'
7 | ],
8 | category: {
9 | basics: 'Basics',
10 | components: 'Components',
11 | layout: 'Layout'
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/devices.js:
--------------------------------------------------------------------------------
1 | export default (editor, config = {}) => {
2 | const deviceManager = editor.DeviceManager
3 | deviceManager.add('Extra Small', '575px')
4 | deviceManager.add('Small', '767px')
5 | deviceManager.add('Medium', '991px')
6 | deviceManager.add('Large', '1199px')
7 | deviceManager.add('Extra Large', '100%')
8 |
9 | const panels = editor.Panels
10 | const commands = editor.Commands
11 | const panelDevices = panels.addPanel({ id: 'devices-buttons' })
12 | const deviceBtns = panelDevices.get('buttons')
13 |
14 | deviceBtns.add([
15 | {
16 | id: 'deviceXl',
17 | command: 'set-device-xl',
18 | className: 'fa fa-desktop',
19 | text: 'XL',
20 | attributes: { title: 'Extra Large' },
21 | active: 1
22 | },
23 | {
24 | id: 'deviceLg',
25 | command: 'set-device-lg',
26 | className: 'fa fa-desktop',
27 | attributes: { title: 'Large' }
28 | },
29 | {
30 | id: 'deviceMd',
31 | command: 'set-device-md',
32 | className: 'fa fa-tablet',
33 | attributes: { title: 'Medium' }
34 | },
35 | {
36 | id: 'deviceSm',
37 | command: 'set-device-sm',
38 | className: 'fa fa-mobile',
39 | attributes: { title: 'Small' }
40 | },
41 | {
42 | id: 'deviceXs',
43 | command: 'set-device-xs',
44 | className: 'fa fa-mobile',
45 | attributes: { title: 'Extra Small' }
46 | }
47 | ])
48 |
49 | commands.add('set-device-xs', {
50 | run: function (editor) {
51 | editor.setDevice('Extra Small')
52 | }
53 | })
54 | commands.add('set-device-sm', {
55 | run: function (editor) {
56 | editor.setDevice('Small')
57 | }
58 | })
59 | commands.add('set-device-md', {
60 | run: function (editor) {
61 | editor.setDevice('Medium')
62 | }
63 | })
64 | commands.add('set-device-lg', {
65 | run: function (editor) {
66 | editor.setDevice('Large')
67 | }
68 | })
69 | commands.add('set-device-xl', {
70 | run: function (editor) {
71 | editor.setDevice('Extra Large')
72 | }
73 | })
74 | }
75 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import grapesjs from 'grapesjs'
2 | import constants from './constants'
3 | import loadComponents from './components'
4 | import loadBlocks from './blocks'
5 | import loadDevices from './devices'
6 | import loadTraits from './traits'
7 |
8 | export default grapesjs.plugins.add('grapesjs-plugin-bootstrap', (editor, opts = {}) => {
9 | const options = { ...constants, ...opts }
10 |
11 | if (options.addBasicStyle) {
12 | editor.addComponents(`
13 |
21 | `)
22 | }
23 |
24 | // Add components
25 | loadComponents(editor, options)
26 |
27 | // Add traits
28 | loadTraits(editor, options)
29 |
30 | // Add blocks
31 | loadBlocks(editor, options)
32 |
33 | // Add devices
34 | loadDevices(editor, options)
35 | })
36 |
--------------------------------------------------------------------------------
/src/traits/index.js:
--------------------------------------------------------------------------------
1 | import selectClass from './selectClass'
2 |
3 | export default function (editor, config = {}) {
4 | selectClass(editor, config)
5 | }
6 |
--------------------------------------------------------------------------------
/src/traits/selectClass.js:
--------------------------------------------------------------------------------
1 | export default function (editor, config = {}) {
2 | const trm = editor.TraitManager
3 |
4 | trm.addType('select-class', {
5 | events: {
6 | 'change': 'onChange'
7 | },
8 |
9 | onValueChange: function () {
10 | let classes = this.model.get('options').map(opt => opt.value)
11 | for (let i = 0; i < classes.length; i++) {
12 | if (classes[i].length > 0) {
13 | let classesN = classes[i].split(' ')
14 | for (let j = 0; j < classesN.length; j++) {
15 | if (classesN[j].length > 0) {
16 | this.target.removeClass(classesN[j])
17 | }
18 | }
19 | }
20 | }
21 | const value = this.model.get('value')
22 | if (value.length > 0 && value !== 'GJS_NO_CLASS') {
23 | const valueN = value.split(' ')
24 | for (let i = 0; i < valueN.length; i++) {
25 | this.target.addClass(valueN[i])
26 | }
27 | }
28 | this.target.em.trigger('change:selectedComponent')
29 | },
30 |
31 | getInputEl: function () {
32 | if (!this.inputEl) {
33 | const model = this.model
34 | const options = model.get('options') || []
35 | const input = document.createElement('select')
36 | const targetViewEl = this.target.view.el
37 |
38 | for (let i = 0; i < options.length; i++) {
39 | let name = options[i].name
40 | let value = options[i].value
41 |
42 | if (value === '') {
43 | value = 'GJS_NO_CLASS'
44 | }
45 |
46 | const option = document.createElement('option')
47 | option.text = name
48 | option.value = value
49 |
50 | if (targetViewEl.classList.contains(value)) {
51 | option.setAttribute('selected', 'selected')
52 | }
53 | input.append(option)
54 | }
55 |
56 | this.inputEl = input
57 | }
58 | return this.inputEl
59 | }
60 | })
61 | }
62 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | export const capitalize = str => str.charAt(0).toUpperCase() + str.slice(1)
2 |
3 | export const getModel = (editor, type) => editor.DomComponents.getType(type).model
4 |
5 | export const getView = (editor, type) => editor.DomComponents.getType(type).view
6 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const HtmlWebpackPlugin = require('html-webpack-plugin');
2 | const pkg = require('./package.json');
3 | const webpack = require('webpack');
4 | const path = require('path');
5 | const fs = require('fs');
6 | const name = pkg.name;
7 | let plugins = [];
8 |
9 | module.exports = (env = {}) => {
10 | const isProd = env.production;
11 |
12 | if (isProd) {
13 | plugins = [
14 | new webpack.BannerPlugin(`${name} - ${pkg.version}`),
15 | ]
16 | } else {
17 | const index = 'index.html';
18 | const indexDev = '_' + index;
19 | plugins.push(new HtmlWebpackPlugin({
20 | template: fs.existsSync(indexDev) ? indexDev : index,
21 | inject: false,
22 | }));
23 | }
24 |
25 | return {
26 | entry: './src',
27 | mode: isProd ? 'production' : 'development',
28 | devtool: isProd ? 'source-map' : 'cheap-module-eval-source-map',
29 | output: {
30 | path: path.resolve(__dirname),
31 | filename: `dist/${name}.min.js`,
32 | library: name,
33 | libraryTarget: 'umd',
34 | },
35 | module: {
36 | rules: [{
37 | test: /\.js$/,
38 | loader: 'babel-loader',
39 | include: /src/,
40 | }],
41 | },
42 | externals: {'grapesjs': 'grapesjs'},
43 | plugins: plugins,
44 | };
45 | }
46 |
--------------------------------------------------------------------------------