├── frontend
├── .browserslistrc
├── babel.config.js
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── assets
│ │ ├── logo.png
│ │ ├── scripts
│ │ │ └── evan-custom.js
│ │ ├── icons
│ │ │ ├── search-icon.svg
│ │ │ ├── column-icon.svg
│ │ │ ├── more-vertical-icon.svg
│ │ │ ├── add-square-icon.svg
│ │ │ ├── info-icons.svg
│ │ │ ├── add-icon.svg
│ │ │ ├── handler-icon.svg
│ │ │ ├── trash-icon.svg
│ │ │ ├── spacer-icon.svg
│ │ │ ├── eye-icon-white.svg
│ │ │ ├── image-icon.svg
│ │ │ ├── settings-icon.svg
│ │ │ ├── carousel-icon.svg
│ │ │ ├── progressbar-icon.svg
│ │ │ ├── lists-icon.svg
│ │ │ ├── heading-icon.svg
│ │ │ ├── button-icon.svg
│ │ │ ├── gallery-icon.svg
│ │ │ ├── text-icon.svg
│ │ │ ├── builder-icon-white.svg
│ │ │ └── social-icon.svg
│ │ ├── styles
│ │ │ └── evan-custom.css
│ │ ├── dummy-section-data.json
│ │ └── builder-data.json
│ ├── views
│ │ ├── Home.vue
│ │ ├── About.vue
│ │ └── Builder.vue
│ ├── store
│ │ ├── index.js
│ │ └── modules
│ │ │ ├── layouts.js
│ │ │ ├── builder-sidebar.js
│ │ │ └── page.js
│ ├── components
│ │ ├── builder
│ │ │ ├── BuilderMain.vue
│ │ │ ├── BuilderSidebarSearchbar.vue
│ │ │ ├── BuilderTopbar.vue
│ │ │ ├── BuilderSidebar.vue
│ │ │ ├── BuilderSidebarElements.vue
│ │ │ ├── BuilderSidebarSettings.vue
│ │ │ └── BuilderSidebarTopnav.vue
│ │ ├── widgets
│ │ │ ├── common
│ │ │ │ ├── SpacerWidget.vue
│ │ │ │ ├── ImageWidget.vue
│ │ │ │ ├── TextWidget.vue
│ │ │ │ ├── HeadingWidget.vue
│ │ │ │ ├── ButtonWidget.vue
│ │ │ │ └── ListWidget.vue
│ │ │ ├── WidgetEditText.vue
│ │ │ └── SettingsWidget.vue
│ │ ├── utility
│ │ │ ├── TextEdit.vue
│ │ │ ├── TextAlign.vue
│ │ │ ├── VerticalContent.vue
│ │ │ ├── ColorPicker.vue
│ │ │ ├── HorizontalContent.vue
│ │ │ ├── WidthType.vue
│ │ │ ├── RangeNumber.vue
│ │ │ ├── UploadImage.vue
│ │ │ └── ListText.vue
│ │ ├── TopNav.vue
│ │ ├── section
│ │ │ ├── SectionAdd.vue
│ │ │ ├── SectionList.vue
│ │ │ └── SectionItem.vue
│ │ ├── column
│ │ │ ├── ColumnSettings.vue
│ │ │ ├── ColumnList.vue
│ │ │ ├── ColumnItem.vue
│ │ │ └── ColumnLayout.vue
│ │ └── element
│ │ │ ├── ElementList.vue
│ │ │ └── ElementItem.vue
│ ├── main.js
│ ├── router
│ │ └── index.js
│ └── App.vue
├── .gitignore
├── .eslintrc.js
├── README.md
└── package.json
└── README.md
/frontend/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 |
--------------------------------------------------------------------------------
/frontend/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ["@vue/cli-plugin-babel/preset"]
3 | };
4 |
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eevan7a9/webpage-builder-elementor/HEAD/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eevan7a9/webpage-builder-elementor/HEAD/frontend/src/assets/logo.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # webpage-builder
2 |
3 | An attempt to mimic Elementor's page building functionality
4 |
5 | **[View the demo](https://elementor.netlify.app/)**
--------------------------------------------------------------------------------
/frontend/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
16 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 |
--------------------------------------------------------------------------------
/frontend/src/assets/scripts/evan-custom.js:
--------------------------------------------------------------------------------
1 | // get latest/highest/largest of objects id from array of objects
2 | export const getLatestId = array =>
3 | Math.max.apply(
4 | Math,
5 | array.map(item => item.id)
6 | );
7 |
8 | export const toTimestamp = strDate => {
9 | var datum = Date.parse(strDate);
10 | return datum / 1000;
11 | };
12 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/search-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Vuex from "vuex";
3 | import builderSidebar from "./modules/builder-sidebar.js";
4 | import page from "./modules/page.js";
5 | import layouts from "./modules/layouts.js";
6 |
7 | Vue.use(Vuex);
8 |
9 | export default new Vuex.Store({
10 | modules: {
11 | builderSidebar,
12 | page,
13 | layouts
14 | }
15 | });
16 |
--------------------------------------------------------------------------------
/frontend/src/store/modules/layouts.js:
--------------------------------------------------------------------------------
1 | import builderData from "@/assets/builder-data.json";
2 |
3 | const state = {
4 | layoutChoices: builderData.layouts
5 | };
6 | const getters = {
7 | getLayouts: state => state.layoutChoices
8 | };
9 | const actions = {};
10 | const mutations = {};
11 |
12 | export default {
13 | state,
14 | getters,
15 | actions,
16 | mutations
17 | };
18 |
--------------------------------------------------------------------------------
/frontend/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | extends: ["plugin:vue/essential", "@vue/prettier"],
7 | rules: {
8 | "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
9 | "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
10 | },
11 | parserOptions: {
12 | parser: "babel-eslint"
13 | }
14 | };
15 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/column-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/more-vertical-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/add-square-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/info-icons.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/components/builder/BuilderMain.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
16 |
22 |
--------------------------------------------------------------------------------
/frontend/src/components/widgets/common/SpacerWidget.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/frontend/README.md:
--------------------------------------------------------------------------------
1 | # frontend
2 |
3 | ## Project setup
4 | ```
5 | npm install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | npm run serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | npm run build
16 | ```
17 |
18 | ### Run your tests
19 | ```
20 | npm run test
21 | ```
22 |
23 | ### Lints and fixes files
24 | ```
25 | npm run lint
26 | ```
27 |
28 | ### Customize configuration
29 | See [Configuration Reference](https://cli.vuejs.org/config/).
30 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/add-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/styles/evan-custom.css:
--------------------------------------------------------------------------------
1 | /*
2 | VueDraggable styles start
3 | */
4 | .flip-list-move {
5 | transition: transform 0.5s;
6 | }
7 | .flip-list-enter-active,
8 | .flip-list-leave-active {
9 | transition: all 1s;
10 | }
11 | .flip-list-enter, .flip-list-leave-to /* .list-leave-active below version 2.1.8 */ {
12 | opacity: 0;
13 | transform: translateY(30px);
14 | }
15 |
16 | .no-move {
17 | transition: transform 0s;
18 | }
19 | .ghost {
20 | opacity: 0.5;
21 | background: #c8ebfb;
22 | }
23 | /*
24 | VueDraggable styles ends
25 | */
26 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/handler-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | frontend
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/frontend/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import App from "./App.vue";
3 | import router from "./router";
4 | import store from "./store";
5 | import BootstrapVue from "bootstrap-vue";
6 | import "bootstrap/dist/css/bootstrap.css";
7 | import "bootstrap-vue/dist/bootstrap-vue.css";
8 | import "@/assets/styles/evan-custom.css";
9 |
10 | import VueSweetalert2 from "vue-sweetalert2";
11 |
12 | import "sweetalert2/dist/sweetalert2.min.css";
13 |
14 | Vue.use(VueSweetalert2);
15 | Vue.use(BootstrapVue);
16 | Vue.config.productionTip = false;
17 |
18 | new Vue({
19 | router,
20 | store,
21 | render: h => h(App)
22 | }).$mount("#app");
23 |
--------------------------------------------------------------------------------
/frontend/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
PageBuilder: Elementor Clone
5 |
6 | Just a simple project, an attempt to mimic
7 | Elementor's page building
8 | functionality
9 |
10 |
11 | Source code:
12 | check it here
13 |
14 |
15 |
16 |
17 |
25 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/trash-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/components/utility/TextEdit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
13 |
14 |
15 |
16 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/spacer-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import VueRouter from "vue-router";
3 | import Home from "../views/Home.vue";
4 |
5 | Vue.use(VueRouter);
6 |
7 | const routes = [
8 | {
9 | path: "/",
10 | name: "builder",
11 | component: () => import("../views/Builder.vue")
12 | },
13 | {
14 | path: "/home",
15 | name: "home",
16 | component: Home
17 | },
18 | {
19 | path: "/about",
20 | name: "about",
21 | // route level code-splitting
22 | // this generates a separate chunk (about.[hash].js) for this route
23 | // which is lazy-loaded when the route is visited.
24 | component: () =>
25 | import(/* webpackChunkName: "about" */ "../views/About.vue")
26 | }
27 | ];
28 |
29 | const router = new VueRouter({
30 | routes
31 | });
32 |
33 | export default router;
34 |
--------------------------------------------------------------------------------
/frontend/src/components/widgets/common/ImageWidget.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
12 |
13 |
14 |
15 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/eye-icon-white.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/components/utility/TextAlign.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
36 |
--------------------------------------------------------------------------------
/frontend/src/components/utility/VerticalContent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
36 |
--------------------------------------------------------------------------------
/frontend/src/components/utility/ColorPicker.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
35 |
36 |
41 |
--------------------------------------------------------------------------------
/frontend/src/components/utility/HorizontalContent.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
36 |
--------------------------------------------------------------------------------
/frontend/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
15 |
46 |
--------------------------------------------------------------------------------
/frontend/src/components/utility/WidthType.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
11 | *{{ notice }}
12 |
13 |
14 |
15 |
16 |
42 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "bootstrap": "^4.4.1",
12 | "bootstrap-vue": "^2.1.0",
13 | "core-js": "^3.4.4",
14 | "vue": "^2.6.11",
15 | "vue-color": "^2.7.0",
16 | "vue-router": "^3.1.3",
17 | "vue-sweetalert2": "^3.0.1",
18 | "vuedraggable": "^2.23.2",
19 | "vuex": "^3.1.2"
20 | },
21 | "devDependencies": {
22 | "@vue/cli-plugin-babel": "^4.1.0",
23 | "@vue/cli-plugin-eslint": "^4.1.0",
24 | "@vue/cli-service": "^4.1.0",
25 | "@vue/eslint-config-prettier": "^5.0.0",
26 | "babel-eslint": "^10.0.3",
27 | "eslint": "^5.16.0",
28 | "eslint-plugin-prettier": "^3.1.1",
29 | "eslint-plugin-vue": "^5.0.0",
30 | "node-sass": "^4.14.1",
31 | "prettier": "^1.19.1",
32 | "sass-loader": "^8.0.0",
33 | "vue-template-compiler": "^2.6.10"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/image-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/settings-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/carousel-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/components/utility/RangeNumber.vue:
--------------------------------------------------------------------------------
1 |
2 |
29 |
30 |
31 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/frontend/src/components/TopNav.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PageBuilder
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Builder
19 |
20 |
21 | About
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
49 |
--------------------------------------------------------------------------------
/frontend/src/components/builder/BuilderSidebarSearchbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
21 |
22 |
23 |
39 |
40 |
57 |
--------------------------------------------------------------------------------
/frontend/src/components/builder/BuilderTopbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
23 |
24 |
25 |
26 |
37 |
38 |
49 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/progressbar-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/views/Builder.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
38 |
39 |
55 |
--------------------------------------------------------------------------------
/frontend/src/components/utility/UploadImage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 | Selected file: {{ file ? file.name : "" }}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/frontend/src/components/widgets/common/TextWidget.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ content.text }}
4 |
(content.text = e)"
9 | @editOff="$emit('editOff', false)"
10 | v-if="onEdit"
11 | />
12 |
13 |
14 |
15 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/frontend/src/components/widgets/common/HeadingWidget.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{ content.text }}
4 | (content.text = e)"
9 | @editOff="$emit('editOff', false)"
10 | v-if="onEdit"
11 | />
12 |
13 |
14 |
15 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/lists-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/components/section/SectionAdd.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
Add Section
19 |
20 |
21 |
22 |
30 |
31 |
61 |
--------------------------------------------------------------------------------
/frontend/src/components/section/SectionList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/frontend/src/components/widgets/common/ButtonWidget.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/frontend/src/components/column/ColumnSettings.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | (column.style.fill = e)"
8 | />
9 |
10 | (column.style.y = e)"
13 | />
14 | (column.style.padding = e)"
18 | min="0"
19 | max="200"
20 | step="0"
21 | v-if="column.style.padding"
22 | />
23 |
24 |
30 |
31 |
32 |
33 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/heading-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/components/column/ColumnList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/frontend/src/components/builder/BuilderSidebar.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
34 |
35 |
69 |
--------------------------------------------------------------------------------
/frontend/src/components/widgets/common/ListWidget.vue:
--------------------------------------------------------------------------------
1 |
2 |
26 |
27 |
28 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/button-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/gallery-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/components/utility/ListText.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
20 |
21 |
37 |
38 |
39 |
40 | (list.text = e)"
44 | />
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/frontend/src/components/builder/BuilderSidebarElements.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
22 |
23 |
GENERAL ELEMENTS
24 |
25 |
30 |
36 |
Not Available
37 |
38 |
39 |
40 |
41 |
42 |
54 |
55 |
83 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/text-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/components/builder/BuilderSidebarSettings.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | *Select a "Widget or Column" from one of your Sections, to show
7 | available Settings here.
8 |
9 |

10 |
11 |
44 |
45 |
46 |
47 |
48 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/frontend/src/components/builder/BuilderSidebarTopnav.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
25 |
26 |
27 |
32 | ELEMENTS
33 |
34 |
39 | Settings
40 |
41 |
42 |
43 |
44 |
45 |
56 |
57 |
92 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/builder-icon-white.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/store/modules/builder-sidebar.js:
--------------------------------------------------------------------------------
1 | import builderData from "@/assets/builder-data.json";
2 |
3 | const state = {
4 | sidebarStatus: true,
5 | builderSidebarTab: "elements", // sidebar elements | styles
6 | widgets: JSON.parse(JSON.stringify(builderData.widgets)),
7 | settings: {
8 | content: false,
9 | tabOpen: 0,
10 | widget: {},
11 | column: {}
12 | }
13 | };
14 | const getters = {
15 | isSidebarOpen: state => state.sidebarStatus,
16 | getBuilderSidebar: state => state.builderSidebarTab,
17 | getWidgets: state => state.widgets,
18 | getSettings: state => state.settings
19 | };
20 | const actions = {
21 | toggleBuilderSidebar: ({ commit }) => {
22 | commit("setSidebar");
23 | },
24 | toggleSidebarTab: ({ commit }, value) => {
25 | commit("setBuilderSidebar", value);
26 | },
27 | fetchWidgets: ({ commit }, name) => {
28 | if (name) {
29 | const common = JSON.parse(
30 | JSON.stringify(builderData.widgets.common)
31 | ).filter(widget =>
32 | widget.name.toLowerCase().includes(name.toLowerCase())
33 | );
34 | const general = JSON.parse(
35 | JSON.stringify(builderData.widgets.general)
36 | ).filter(widget =>
37 | widget.name.toLowerCase().includes(name.toLowerCase())
38 | );
39 | commit("setWidgets", { common: common, general: general });
40 | } else {
41 | commit("setWidgets", builderData.widgets);
42 | }
43 | },
44 | selectWidget: ({ commit }, { widget, column }) => {
45 | commit("setSelectedWidget", { widget: widget, column: column });
46 | },
47 | selectColumn: ({ commit }, column) => {
48 | commit("setSelectedColumn", column);
49 | }
50 | };
51 | const mutations = {
52 | setSidebar: state => (state.sidebarStatus = !state.sidebarStatus),
53 | setBuilderSidebar: (state, tab) => (state.builderSidebarTab = tab),
54 | setWidgets: (state, widgets) => {
55 | state.widgets.common = widgets.common;
56 | state.widgets.general = widgets.general;
57 | },
58 | setSelectedWidget: (state, data) => {
59 | if (data.widget || data.column) {
60 | state.settings.widget = data.widget;
61 | state.settings.column = data.column;
62 | state.settings.tabOpen = 0;
63 | state.settings.content = true;
64 | } else {
65 | state.settings.widget = {};
66 | state.settings.column = {};
67 | state.settings.content = false;
68 | }
69 | },
70 | setSelectedColumn: (state, column) => {
71 | state.settings.column = column;
72 | state.settings.widget = {};
73 | if (column.id) {
74 | state.settings.content = true;
75 | state.settings.tabOpen = 1;
76 | } else {
77 | state.settings.content = false;
78 | }
79 | }
80 | };
81 |
82 | export default {
83 | state,
84 | getters,
85 | actions,
86 | mutations
87 | };
88 |
--------------------------------------------------------------------------------
/frontend/src/components/widgets/WidgetEditText.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
24 |
25 |
26 |
27 |
94 |
95 |
111 |
--------------------------------------------------------------------------------
/frontend/src/components/element/ElementList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
15 |
16 |
21 |
22 |
27 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
95 |
96 |
111 |
--------------------------------------------------------------------------------
/frontend/src/components/column/ColumnItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
19 |
27 |
28 |
33 |
34 |
35 |
36 |
37 |
38 |
81 |
82 |
140 |
--------------------------------------------------------------------------------
/frontend/src/components/section/SectionItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
19 |
27 |
28 |
39 |
40 |
41 |
42 |
87 |
88 |
140 |
--------------------------------------------------------------------------------
/frontend/src/components/column/ColumnLayout.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Choose Layout
5 |
6 |
7 |
8 |
Dynamic Layout
9 |
1 to 12
10 |
15 |
16 |
22 |
Layout
23 |
{{ layout.label }}
24 |
33 |
34 |
35 |
36 |
37 |
38 |
79 |
80 |
147 |
--------------------------------------------------------------------------------
/frontend/src/components/widgets/SettingsWidget.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | (widgets.text = e)"
9 | v-if="widgets.hasOwnProperty('text')"
10 | />
11 |
12 | (widgets.style.textAlign = e)"
15 | v-if="widgets.style.textAlign"
16 | />
17 | (widgets.style.horizontal = e)"
20 | v-if="widgets.style.horizontal"
21 | />
22 | (widgets.src = e)"
24 | v-if="widgets.hasOwnProperty('src')"
25 | :imageSrc="widgets.src"
26 | />
27 |
28 | (widgets.style.fontSize = e)"
32 | min="0"
33 | max="200"
34 | step="0"
35 | v-if="widgets.style.fontSize"
36 | />
37 | (widgets.style.fontWeight = e)"
41 | min="100"
42 | max="900"
43 | step="100"
44 | v-if="widgets.style.fontWeight"
45 | />
46 | (widgets.style.padding = e)"
50 | min="0"
51 | max="200"
52 | step="0"
53 | v-if="widgets.style.padding"
54 | />
55 | (widgets.style.margin = e)"
59 | min="0"
60 | max="200"
61 | step="0"
62 | v-if="widgets.style.margin"
63 | />
64 | (widgets.style.borderRadius = e)"
68 | min="0"
69 | max="200"
70 | step="0"
71 | v-if="widgets.style.borderRadius"
72 | />
73 | (widgets.style.height = e + 'px')"
77 | min="50"
78 | max="500"
79 | step="0"
80 | v-if="widgets.style.height"
81 | />
82 | (widgets.style.width = e + 'px')"
86 | min="50"
87 | max="500"
88 | step="0"
89 | v-if="widgets.style.width"
90 | />
91 |
92 |
98 |
104 |
105 |
106 |
107 |
138 |
139 |
148 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/social-icon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/components/element/ElementItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
51 |
52 |
53 |
118 |
119 |
184 |
--------------------------------------------------------------------------------
/frontend/src/store/modules/page.js:
--------------------------------------------------------------------------------
1 | import { getLatestId, toTimestamp } from "@/assets/scripts/evan-custom.js";
2 | import dummySectionData from "@/assets/dummy-section-data.json";
3 |
4 | const state = {
5 | sections: dummySectionData.sections
6 | };
7 |
8 | const getters = {
9 | getSections: state => state.sections
10 | };
11 |
12 | const actions = {
13 | addSection: ({ commit, state }) => {
14 | // we get the id of the highest/latest id in sections
15 | let latestSectionId = getLatestId(state.sections);
16 | latestSectionId = latestSectionId > 0 ? latestSectionId + 1 : 1;
17 | const newSection = {
18 | id: latestSectionId,
19 | columns: []
20 | };
21 | commit("insertSection", newSection);
22 | },
23 | deleteSection: ({ commit }, id) => {
24 | commit("removeSection", id);
25 | },
26 | updateSections: ({ commit }, value) => {
27 | commit("setSections", value);
28 | },
29 | addSectionColumns: ({ commit }, { layout, sectionId }) => {
30 | let idCounter = 1;
31 | layout.columns.forEach(col => {
32 | col.id = toTimestamp(new Date()) + idCounter;
33 | idCounter++;
34 | });
35 | commit("setSectionColumns", { id: sectionId, sectionLayout: layout });
36 | },
37 |
38 | // Sections Columns *************************************
39 |
40 | updateColumns: ({ commit }, { column, sectionId }) => {
41 | commit("setColumns", { item: column, sectionId: sectionId });
42 | },
43 | deleteColumnContent: ({ commit }, { columnId, sectionId }) => {
44 | commit("removeColumnContent", {
45 | columnId: columnId,
46 | sectionId: sectionId
47 | });
48 | },
49 | addColumn: ({ commit }, { column, sectionId }) => {
50 | commit("insertColumn", {
51 | column: column,
52 | sectionId: sectionId
53 | });
54 | },
55 |
56 | // Sections Elements *************************************
57 |
58 | updateElements: ({ commit }, { elements, columnId, sectionId }) => {
59 | // we find the newly added element
60 | const foundElement = elements.find(el => el.new == true);
61 | if (foundElement) {
62 | foundElement.id = toTimestamp(new Date()); // assign new unique ID
63 | foundElement.new = false; // element is now not new
64 | }
65 | commit("setElements", {
66 | item: elements,
67 | columnId: columnId,
68 | sectionId: sectionId
69 | });
70 | },
71 | deleteElements: ({ commit }, { elementId, columnId, sectionId }) => {
72 | commit("removeElement", {
73 | elementId: elementId,
74 | columnId: columnId,
75 | sectionId: sectionId
76 | });
77 | }
78 | };
79 |
80 | const mutations = {
81 | // SECTION STARTS
82 | setSections: (state, sections) => (state.sections = sections),
83 | insertSection: (state, section) => state.sections.push(section),
84 | removeSection: (state, id) =>
85 | (state.sections = state.sections.filter(section => section.id != id)),
86 | setSectionColumns: (state, section) => {
87 | const foundSection = state.sections.find(sec => sec.id == section.id);
88 | if (foundSection) {
89 | foundSection.label = section.sectionLayout.label;
90 | foundSection.layout = section.sectionLayout.layout;
91 | foundSection.columns = section.sectionLayout.columns;
92 | }
93 | },
94 | // COLUMNS STARTS
95 | setColumns: (state, column) => {
96 | const foundSection = state.sections.find(sec => sec.id == column.sectionId);
97 | if (foundSection) {
98 | foundSection.columns = column.item;
99 | }
100 | },
101 | removeColumnContent: (state, data) => {
102 | const foundSection = state.sections.find(sec => sec.id == data.sectionId);
103 | if (foundSection) {
104 | if (foundSection.layout) {
105 | const foundColumn = foundSection.columns.find(
106 | col => col.id == data.columnId
107 | );
108 | if (foundColumn) {
109 | foundColumn.elements = [];
110 | }
111 | } else {
112 | foundSection.columns = foundSection.columns.filter(
113 | col => col.id != data.columnId
114 | );
115 | }
116 | }
117 | },
118 | insertColumn: (state, data) => {
119 | const foundSection = state.sections.find(sec => sec.id == data.sectionId);
120 | if (foundSection) {
121 | foundSection.columns.push(data.column);
122 | }
123 | },
124 | // ElEMENTSS START
125 | setElements: (state, element) => {
126 | const foundSection = state.sections.find(
127 | sec => sec.id == element.sectionId
128 | );
129 | if (foundSection) {
130 | const foundColumn = foundSection.columns.find(
131 | col => col.id == element.columnId
132 | );
133 | if (foundColumn) {
134 | foundColumn.elements = element.item;
135 | }
136 | }
137 | },
138 | removeElement: (state, data) => {
139 | const foundSection = state.sections.find(sec => sec.id == data.sectionId);
140 | if (foundSection) {
141 | const foundColumn = foundSection.columns.find(
142 | col => col.id == data.columnId
143 | );
144 | if (foundColumn) {
145 | foundColumn.elements = foundColumn.elements.filter(
146 | el => el.id != data.elementId
147 | );
148 | }
149 | }
150 | }
151 | };
152 |
153 | export default {
154 | state,
155 | getters,
156 | actions,
157 | mutations
158 | };
159 |
--------------------------------------------------------------------------------
/frontend/src/assets/dummy-section-data.json:
--------------------------------------------------------------------------------
1 | {
2 | "sections": [
3 | {
4 | "id": 1,
5 | "columns": [
6 | {
7 | "id": 1579850578,
8 | "grid": null,
9 | "style": {
10 | "fill": true,
11 | "y": "start",
12 | "x": "center",
13 | "background": "#000000",
14 | "padding": "0"
15 | },
16 | "elements": [
17 | {
18 | "id": 1579850724,
19 | "name": "Image",
20 | "widget": "ImageWidget",
21 | "new": false,
22 | "icon": "image-icon.svg",
23 | "content": {
24 | "src": "https://via.placeholder.com/350x350",
25 | "style": {
26 | "width": "400px",
27 | "borderRadius": "0",
28 | "padding": "2",
29 | "margin": "2",
30 | "horizontal": "center"
31 | }
32 | }
33 | },
34 | {
35 | "id": 1579850728,
36 | "name": "Text Editor",
37 | "widget": "TextWidget",
38 | "new": false,
39 | "icon": "text-icon.svg",
40 | "content": {
41 | "text": "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...",
42 | "style": {
43 | "fontSize": "16",
44 | "fontWeight": "400",
45 | "textAlign": "center",
46 | "borderRadius": "0",
47 | "color": "#FBFBFB",
48 | "background": "transparent",
49 | "padding": "10",
50 | "margin": "2"
51 | }
52 | }
53 | }
54 | ]
55 | },
56 | {
57 | "id": 1579850579,
58 | "grid": null,
59 | "style": {
60 | "fill": false,
61 | "y": "start",
62 | "x": "center",
63 | "background": "none",
64 | "padding": "0"
65 | },
66 | "elements": [
67 | {
68 | "id": 1579850583,
69 | "name": "Heading",
70 | "widget": "HeadingWidget",
71 | "new": false,
72 | "icon": "heading-icon.svg",
73 | "content": {
74 | "text": "What is This?\n",
75 | "style": {
76 | "fontSize": "40",
77 | "fontWeight": "900",
78 | "textAlign": "center",
79 | "borderRadius": "0",
80 | "color": "#FFFFFF",
81 | "background": "#000000",
82 | "padding": "10",
83 | "margin": "2"
84 | }
85 | }
86 | },
87 | {
88 | "id": 1579850604,
89 | "name": "Text Editor",
90 | "widget": "TextWidget",
91 | "new": false,
92 | "icon": "text-icon.svg",
93 | "content": {
94 | "text": "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...",
95 | "style": {
96 | "fontSize": "16",
97 | "fontWeight": "400",
98 | "textAlign": "center",
99 | "borderRadius": "0",
100 | "color": "#333",
101 | "background": "transparent",
102 | "padding": "10",
103 | "margin": "2"
104 | }
105 | }
106 | },
107 | {
108 | "id": 1579850681,
109 | "name": "List",
110 | "widget": "ListWidget",
111 | "new": false,
112 | "icon": "lists-icon.svg",
113 | "content": {
114 | "title": "Phasellus eu risus fringilla, sodales odio at, iaculis ex.",
115 | "list": [
116 | {
117 | "id": 1,
118 | "text": "Lorem ipsum dolor sit amet",
119 | "class": ""
120 | },
121 | {
122 | "id": 2,
123 | "text": "In ut nulla posuere, euismod sapien at",
124 | "class": ""
125 | }
126 | ],
127 | "style": {
128 | "fontSize": "16",
129 | "fontWeight": "600",
130 | "textAlign": "left",
131 | "borderRadius": "0",
132 | "color": "#333",
133 | "background": "transparent",
134 | "padding": "10",
135 | "margin": "2",
136 | "listStyle": "none",
137 | "horizontal": "center"
138 | }
139 | }
140 | },
141 | {
142 | "id": 1579850685,
143 | "name": "Button",
144 | "widget": "ButtonWidget",
145 | "new": false,
146 | "icon": "button-icon.svg",
147 | "content": {
148 | "text": "Subscribe",
149 | "link": "https://github.com/eevan7a9/",
150 | "style": {
151 | "fontSize": "16",
152 | "fontWeight": "600",
153 | "borderRadius": "24",
154 | "color": "#FFFFFF",
155 | "background": "#E60009",
156 | "padding": "26",
157 | "margin": "2",
158 | "horizontal": "center"
159 | }
160 | }
161 | }
162 | ]
163 | },
164 | {
165 | "id": 1579850581,
166 | "grid": null,
167 | "style": {
168 | "fill": false,
169 | "y": "center",
170 | "x": "center",
171 | "background": "none",
172 | "padding": "0"
173 | },
174 | "elements": [
175 | {
176 | "id": 1579850630,
177 | "name": "Heading",
178 | "widget": "HeadingWidget",
179 | "new": false,
180 | "icon": "heading-icon.svg",
181 | "content": {
182 | "text": "I can do This:\n",
183 | "style": {
184 | "fontSize": "40",
185 | "fontWeight": "900",
186 | "textAlign": "center",
187 | "borderRadius": "0",
188 | "color": "#333",
189 | "background": "transparent",
190 | "padding": "10",
191 | "margin": "2"
192 | }
193 | }
194 | },
195 | {
196 | "id": 1579850625,
197 | "name": "List",
198 | "widget": "ListWidget",
199 | "new": false,
200 | "icon": "lists-icon.svg",
201 | "content": {
202 | "title": "Phasellus eu risus fringilla, sodales odio at, iaculis ex.",
203 | "list": [
204 | {
205 | "id": 1,
206 | "text": "Lorem ipsum dolor sit amet",
207 | "class": ""
208 | },
209 | {
210 | "id": 2,
211 | "text": "In ut nulla posuere, euismod sapien at",
212 | "class": ""
213 | }
214 | ],
215 | "style": {
216 | "fontSize": "11",
217 | "fontWeight": "300",
218 | "textAlign": "left",
219 | "borderRadius": "0",
220 | "color": "#333",
221 | "background": "transparent",
222 | "padding": "27",
223 | "margin": "2",
224 | "listStyle": "none",
225 | "horizontal": "center"
226 | }
227 | }
228 | }
229 | ]
230 | }
231 | ],
232 | "label": "1 to 12",
233 | "layout": false
234 | },
235 | {
236 | "id": 2,
237 | "columns": [
238 | {
239 | "id": 1579850768,
240 | "grid": null,
241 | "style": {
242 | "fill": true,
243 | "y": "start",
244 | "x": "center",
245 | "background": "none",
246 | "padding": "0"
247 | },
248 | "elements": []
249 | },
250 | {
251 | "id": 1579851136,
252 | "grid": null,
253 | "style": {
254 | "fill": true,
255 | "y": "center",
256 | "x": "center",
257 | "background": "none",
258 | "padding": "0"
259 | },
260 | "elements": [
261 | {
262 | "id": 1579851141,
263 | "name": "Heading",
264 | "widget": "HeadingWidget",
265 | "new": false,
266 | "icon": "heading-icon.svg",
267 | "content": {
268 | "text": "Heading Text",
269 | "style": {
270 | "fontSize": "40",
271 | "fontWeight": "900",
272 | "textAlign": "center",
273 | "borderRadius": "0",
274 | "color": "#333",
275 | "background": "transparent",
276 | "padding": "10",
277 | "margin": "2"
278 | }
279 | }
280 | },
281 | {
282 | "id": 1579851138,
283 | "name": "Text Editor",
284 | "widget": "TextWidget",
285 | "new": false,
286 | "icon": "text-icon.svg",
287 | "content": {
288 | "text": "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...",
289 | "style": {
290 | "fontSize": "16",
291 | "fontWeight": "400",
292 | "textAlign": "center",
293 | "borderRadius": "0",
294 | "color": "#333",
295 | "background": "transparent",
296 | "padding": "10",
297 | "margin": "2"
298 | }
299 | }
300 | }
301 | ]
302 | }
303 | ],
304 | "label": "1 to 12",
305 | "layout": false
306 | },
307 | {
308 | "id": 3,
309 | "columns": []
310 | }
311 | ]
312 | }
--------------------------------------------------------------------------------
/frontend/src/assets/builder-data.json:
--------------------------------------------------------------------------------
1 | {
2 | "widgets": {
3 | "common": [
4 | {
5 | "id": 1,
6 | "name": "Heading",
7 | "widget": "HeadingWidget",
8 | "new": true,
9 | "icon": "heading-icon.svg",
10 | "content": {
11 | "text": "Heading Text",
12 | "style": {
13 | "fontSize": "40",
14 | "fontWeight": "900",
15 | "textAlign": "center",
16 | "borderRadius": "0",
17 | "color": "#333",
18 | "background": "transparent",
19 | "padding": "10",
20 | "margin": "2"
21 | }
22 | }
23 | },
24 | {
25 | "id": 2,
26 | "name": "Text Editor",
27 | "widget": "TextWidget",
28 | "new": true,
29 | "icon": "text-icon.svg",
30 | "content": {
31 | "text": "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit...",
32 | "style": {
33 | "fontSize": "16",
34 | "fontWeight": "400",
35 | "textAlign": "center",
36 | "borderRadius": "0",
37 | "color": "#333",
38 | "background": "transparent",
39 | "padding": "10",
40 | "margin": "2"
41 | }
42 | }
43 | },
44 | {
45 | "id": 3,
46 | "name": "List",
47 | "widget": "ListWidget",
48 | "new": true,
49 | "icon": "lists-icon.svg",
50 | "content": {
51 | "title": "Phasellus eu risus fringilla, sodales odio at, iaculis ex.",
52 | "list": [
53 | {
54 | "id": 1,
55 | "text": "Lorem ipsum dolor sit amet",
56 | "class": ""
57 | },
58 | {
59 | "id": 2,
60 | "text": "In ut nulla posuere, euismod sapien at",
61 | "class": ""
62 | }
63 | ],
64 | "style": {
65 | "fontSize": "16",
66 | "fontWeight": "600",
67 | "textAlign": "left",
68 | "borderRadius": "0",
69 | "color": "#333",
70 | "background": "transparent",
71 | "padding": "10",
72 | "margin": "2",
73 | "listStyle": "none",
74 | "horizontal": "center"
75 | }
76 | }
77 | },
78 | {
79 | "id": 4,
80 | "name": "Button",
81 | "widget": "ButtonWidget",
82 | "new": true,
83 | "icon": "button-icon.svg",
84 | "content": {
85 | "text": "Button",
86 | "link": "https://github.com/eevan7a9/",
87 | "style": {
88 | "fontSize": "16",
89 | "fontWeight": "600",
90 | "borderRadius": "5",
91 | "color": "#333",
92 | "background": "#007bff",
93 | "padding": "10",
94 | "margin": "2",
95 | "horizontal": "center"
96 | }
97 | }
98 | },
99 | {
100 | "id": 5,
101 | "name": "Image",
102 | "widget": "ImageWidget",
103 | "new": true,
104 | "icon": "image-icon.svg",
105 | "content": {
106 | "src": "https://i.picsum.photos/id/866/400/300.jpg",
107 | "style": {
108 | "width": "400px",
109 | "borderRadius": "0",
110 | "padding": "2",
111 | "margin": "2",
112 | "horizontal": "center"
113 | }
114 | }
115 | },
116 | {
117 | "id": 6,
118 | "name": "Spacer",
119 | "widget": "SpacerWidget",
120 | "new": true,
121 | "icon": "spacer-icon.svg",
122 | "content": {
123 | "style": {
124 | "height": "100px",
125 | "background": "#F3F3EE"
126 | }
127 | }
128 | }
129 | ],
130 | "general": [
131 | {
132 | "id": 1,
133 | "name": "Carousel",
134 | "widget": "CarouselWidget",
135 | "new": true,
136 | "icon": "carousel-icon.svg",
137 | "content": {}
138 | },
139 | {
140 | "id": 2,
141 | "name": "Social Icon",
142 | "widget": "SocialIconWidget",
143 | "new": true,
144 | "icon": "social-icon.svg",
145 | "content": {}
146 | },
147 | {
148 | "id": 3,
149 | "name": "Progress Bar",
150 | "widget": "ProgressBarWidget",
151 | "new": true,
152 | "icon": "progressbar-icon.svg",
153 | "content": {}
154 | },
155 | {
156 | "id": 4,
157 | "name": "Image Gallery",
158 | "widget": "GalleryWidget",
159 | "new": true,
160 | "icon": "gallery-icon.svg",
161 | "content": {}
162 | }
163 | ]
164 | },
165 | "layouts": [
166 | {
167 | "label": "6/12 ~ 6/12",
168 | "layout": true,
169 | "columns": [
170 | {
171 | "id": 1,
172 | "grid": "col-md-6",
173 | "style": {
174 | "fill": true,
175 | "y": "start",
176 | "x": "center",
177 | "background": "none",
178 | "padding": "0"
179 | },
180 | "elements": []
181 | },
182 | {
183 | "id": 2,
184 | "grid": "col-md-6",
185 | "style": {
186 | "fill": true,
187 | "y": "start",
188 | "x": "center",
189 | "background": "none",
190 | "padding": "0"
191 | },
192 | "elements": []
193 | }
194 | ]
195 | },
196 | {
197 | "label": "4/12 ~ 8/12",
198 | "layout": true,
199 | "columns": [
200 | {
201 | "id": 1,
202 | "grid": "col-md-4",
203 | "style": {
204 | "fill": true,
205 | "y": "start",
206 | "x": "center",
207 | "background": "none",
208 | "padding": "0"
209 | },
210 | "elements": []
211 | },
212 | {
213 | "id": 2,
214 | "grid": "col-md-8",
215 | "style": {
216 | "fill": true,
217 | "y": "start",
218 | "x": "center",
219 | "background": "none",
220 | "padding": "0"
221 | },
222 | "elements": []
223 | }
224 | ]
225 | },
226 | {
227 | "label": "3/12 ~ 9/12",
228 | "layout": true,
229 | "columns": [
230 | {
231 | "id": 1,
232 | "grid": "col-md-3",
233 | "style": {
234 | "fill": true,
235 | "y": "start",
236 | "x": "center",
237 | "background": "none",
238 | "padding": "0"
239 | },
240 | "elements": []
241 | },
242 | {
243 | "id": 2,
244 | "grid": "col-md-9",
245 | "style": {
246 | "fill": true,
247 | "y": "start",
248 | "x": "center",
249 | "background": "none",
250 | "padding": "0"
251 | },
252 | "elements": []
253 | }
254 | ]
255 | },
256 | {
257 | "label": "3/12(2) ~ 6/12",
258 | "layout": true,
259 | "columns": [
260 | {
261 | "id": 1,
262 | "grid": "col-md-3",
263 | "style": {
264 | "fill": true,
265 | "y": "start",
266 | "x": "center",
267 | "background": "none",
268 | "padding": "0"
269 | },
270 | "elements": []
271 | },
272 | {
273 | "id": 2,
274 | "grid": "col-md-6",
275 | "style": {
276 | "fill": true,
277 | "y": "start",
278 | "x": "center",
279 | "background": "none",
280 | "padding": "0"
281 | },
282 | "elements": []
283 | },
284 | {
285 | "id": 3,
286 | "grid": "col-md-3",
287 | "style": {
288 | "fill": true,
289 | "y": "start",
290 | "x": "center",
291 | "background": "none",
292 | "padding": "0"
293 | },
294 | "elements": []
295 | }
296 | ]
297 | },
298 | {
299 | "label": "2/12 ~ 4/12 ~ 6/12",
300 | "layout": true,
301 | "columns": [
302 | {
303 | "id": 1,
304 | "grid": "col-md-2",
305 | "style": {
306 | "fill": true,
307 | "y": "start",
308 | "x": "center",
309 | "background": "none",
310 | "padding": "0"
311 | },
312 | "elements": []
313 | },
314 | {
315 | "id": 2,
316 | "grid": "col-md-4",
317 | "style": {
318 | "fill": true,
319 | "y": "start",
320 | "x": "center",
321 | "background": "none",
322 | "padding": "0"
323 | },
324 | "elements": []
325 | },
326 | {
327 | "id": 3,
328 | "grid": "col-md-6",
329 | "style": {
330 | "fill": true,
331 | "y": "start",
332 | "x": "center",
333 | "background": "none",
334 | "padding": "0"
335 | },
336 | "elements": []
337 | }
338 | ]
339 | },
340 | {
341 | "label": "2/12 ~ 5/12(2)",
342 | "layout": true,
343 | "columns": [
344 | {
345 | "id": 1,
346 | "grid": "col-md-2",
347 | "style": {
348 | "fill": true,
349 | "y": "start",
350 | "x": "center",
351 | "background": "none",
352 | "padding": "0"
353 | },
354 | "elements": []
355 | },
356 | {
357 | "id": 2,
358 | "grid": "col-md-5",
359 | "style": {
360 | "fill": true,
361 | "y": "start",
362 | "x": "center",
363 | "background": "none",
364 | "padding": "0"
365 | },
366 | "elements": []
367 | },
368 | {
369 | "id": 3,
370 | "grid": "col-md-5",
371 | "style": {
372 | "fill": true,
373 | "y": "start",
374 | "x": "center",
375 | "background": "none",
376 | "padding": "0"
377 | },
378 | "elements": []
379 | }
380 | ]
381 | },
382 | {
383 | "label": "2/12(3) ~ 6/12",
384 | "layout": true,
385 | "columns": [
386 | {
387 | "id": 1,
388 | "grid": "col-md-2",
389 | "style": {
390 | "fill": true,
391 | "y": "start",
392 | "x": "center",
393 | "background": "none",
394 | "padding": "0"
395 | },
396 | "elements": []
397 | },
398 | {
399 | "id": 2,
400 | "grid": "col-md-2",
401 | "style": {
402 | "fill": true,
403 | "y": "start",
404 | "x": "center",
405 | "background": "none",
406 | "padding": "0"
407 | },
408 | "elements": []
409 | },
410 | {
411 | "id": 3,
412 | "grid": "col-md-2",
413 | "style": {
414 | "fill": true,
415 | "y": "start",
416 | "x": "center",
417 | "background": "none",
418 | "padding": "0"
419 | },
420 | "elements": []
421 | },
422 | {
423 | "id": 4,
424 | "grid": "col-md-6",
425 | "style": {
426 | "fill": true,
427 | "y": "start",
428 | "x": "center",
429 | "background": "none",
430 | "padding": "0"
431 | },
432 | "elements": []
433 | }
434 | ]
435 | }
436 | ]
437 | }
--------------------------------------------------------------------------------