Pārlūkot izejas kodu

feat: add editor selection + code editor + grapejs POC

Nicolas Giard 6 gadi atpakaļ
vecāks
revīzija
20f2fd29fe

+ 3 - 3
client/app.js

@@ -156,9 +156,9 @@ let bootstrap = () => {
   // Load Icons
   // ====================================
 
-  import(/* webpackChunkName: "icons" */ './svg/icons.svg').then(icons => {
-    document.body.insertAdjacentHTML('beforeend', icons.default)
-  })
+  // import(/* webpackChunkName: "icons" */ './svg/icons.svg').then(icons => {
+  //   document.body.insertAdjacentHTML('beforeend', icons.default)
+  // })
 }
 
 window.boot.onDOMReady(bootstrap)

+ 49 - 3
client/components/editor.vue

@@ -26,7 +26,7 @@
           v-icon(color='blue', :left='$vuetify.breakpoint.lgAndUp') sort_by_alpha
           span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ $t('editor:page') }}
     v-content
-      editor-code
+      component(:is='currentEditor')
       component(:is='currentModal')
       v-dialog(v-model='dialogProgress', persistent, max-width='350')
         v-card(color='blue darken-3', dark)
@@ -38,6 +38,39 @@
               )
             .subheading {{ $t('editor:save.processing') }}
             .caption.blue--text.text--lighten-3 {{ $t('editor:save.pleaseWait') }}
+      v-dialog(v-model='dialogEditorSelector', persistent, max-width='550')
+        v-card(color='blue darken-3', dark)
+          v-card-text.text-xs-center.py-4
+            .subheading Which editor do you want to use?
+            v-container(grid-list-lg, fluid)
+              v-layout(row, wrap, justify-center)
+                v-flex(xs4)
+                  v-card(
+                    hover
+                    light
+                    ripple
+                    )
+                    v-card-text.text-xs-center(@click='selectEditor("code")')
+                      v-icon(large, color='primary') code
+                      .body-2.mt-2 Code
+                v-flex(xs4)
+                  v-card(
+                    hover
+                    light
+                    ripple
+                    )
+                    v-card-text.text-xs-center(@click='selectEditor("markdown")')
+                      v-icon(large, color='primary') list_alt
+                      .body-2.mt-2 Markdown
+                v-flex(xs4)
+                  v-card.grey(
+                    hover
+                    light
+                    ripple
+                    )
+                    v-card-text.text-xs-center(@click='selectEditor("wysiwyg")')
+                      v-icon(large, color='grey darken-1') web
+                      .body-2.mt-2.grey--text.text--darken-2 Visual Builder
 
     v-snackbar(
       :color='notification.style'
@@ -69,12 +102,16 @@ export default {
   components: {
     AtomSpinner,
     editorCode: () => import(/* webpackChunkName: "editor-code" */ './editor/editor-code.vue'),
+    editorMarkdown: () => import(/* webpackChunkName: "editor-markdown" */ './editor/editor-markdown.vue'),
+    editorWysiwyg: () => import(/* webpackChunkName: "editor-wysiwyg" */ './editor/editor-wysiwyg.vue'),
     editorModalProperties: () => import(/* webpackChunkName: "editor" */ './editor/editor-modal-properties.vue')
   },
   data() {
     return {
       currentModal: '',
-      dialogProgress: false
+      currentEditor: '',
+      dialogProgress: false,
+      dialogEditorSelector: false
     }
   },
   computed: {
@@ -85,11 +122,20 @@ export default {
   mounted() {
     if (this.mode === 'create') {
       _.delay(() => {
-        this.openModal('properties')
+        this.dialogEditorSelector = true
       }, 500)
     }
   },
   methods: {
+    selectEditor(name) {
+      this.currentEditor = `editor${_.startCase(name)}`
+      this.dialogEditorSelector = false
+      if (this.mode === 'create') {
+        _.delay(() => {
+          this.openModal('properties')
+        }, 500)
+      }
+    },
     openModal(name) {
       this.currentModal = `editorModal${_.startCase(name)}`
     },

+ 5 - 246
client/components/editor/editor-code.vue

@@ -1,74 +1,8 @@
 <template lang='pug'>
   .editor-code
-    v-toolbar.editor-code-toolbar(dense, color='primary', dark)
-      v-tooltip(top)
-        v-btn(icon, slot='activator').mx-0
-          v-icon format_bold
-        span Bold
-      v-tooltip(top)
-        v-btn(icon, slot='activator').mx-0
-          v-icon format_italic
-        span Italic
-      v-tooltip(top)
-        v-btn(icon, slot='activator').mx-0
-          v-icon format_strikethrough
-        span Strikethrough
-      v-menu(offset-y, open-on-hover)
-        v-btn(icon, slot='activator').mx-0
-          v-icon font_download
-        v-list
-          v-list-tile(v-for='(n, idx) in 6', @click='', :key='idx')
-            v-list-tile-action
-              v-icon font_download
-            v-list-tile-title Heading {{n}}
-      v-tooltip(top)
-        v-btn(icon, slot='activator').mx-0
-          v-icon format_quote
-        span Blockquote
-      v-tooltip(top)
-        v-btn(icon, slot='activator').mx-0
-          v-icon format_list_bulleted
-        span Unordered List
-      v-tooltip(top)
-        v-btn(icon, slot='activator').mx-0
-          v-icon format_list_numbered
-        span Ordered List
-      v-tooltip(top)
-        v-btn(icon, slot='activator').mx-0
-          v-icon insert_link
-        span Link
-      v-tooltip(top)
-        v-btn(icon, slot='activator').mx-0
-          v-icon space_bar
-        span Inline Code
-      v-tooltip(top)
-        v-btn(icon, slot='activator').mx-0
-          v-icon code
-        span Code Block
-      v-tooltip(top)
-        v-btn(icon, slot='activator').mx-0
-          v-icon remove
-        span Horizontal Bar
-
     .editor-code-main
       .editor-code-editor
-        .editor-code-editor-title(v-if='previewShown', @click='previewShown = false') Editor
-        .editor-code-editor-title(v-else='previewShown', @click='previewShown = true'): v-icon(dark) drag_indicator
         codemirror(ref='cm', v-model='code', :options='cmOptions', @ready='onCmReady', @input='onCmInput')
-      transition(name='editor-code-preview')
-        .editor-code-preview(v-if='previewShown')
-          .editor-code-preview-title(@click='previewShown = false') Preview
-          .editor-code-preview-content.markdown-content(ref='editorPreview', v-html='previewHTML')
-
-      v-speed-dial(v-model='fabInsertMenu', :open-on-hover='true', direction='top', transition='slide-y-reverse-transition', fixed, left, bottom)
-        v-btn(color='blue', fab, dark, v-model='fabInsertMenu', slot='activator')
-          v-icon add_circle
-          v-icon close
-        v-btn(color='teal', fab, dark): v-icon image
-        v-btn(color='pink', fab, dark): v-icon insert_drive_file
-        v-btn(color='red', fab, dark): v-icon play_circle_outline
-        v-btn(color='purple', fab, dark): v-icon multiline_chart
-        v-btn(color='indigo', fab, dark): v-icon functions
 </template>
 
 <script>
@@ -95,20 +29,6 @@ import 'codemirror/addon/search/matchesonscrollbar.js'
 import 'codemirror/addon/search/searchcursor.js'
 import 'codemirror/addon/search/match-highlighter.js'
 
-// Markdown-it
-import MarkdownIt from 'markdown-it'
-import mdEmoji from 'markdown-it-emoji'
-import mdTaskLists from 'markdown-it-task-lists'
-import mdExpandTabs from 'markdown-it-expand-tabs'
-import mdAbbr from 'markdown-it-abbr'
-import mdSup from 'markdown-it-sup'
-import mdSub from 'markdown-it-sub'
-import mdMark from 'markdown-it-mark'
-import mdImsize from 'markdown-it-imsize'
-
-// Prism (Syntax Highlighting)
-import Prism from '@/libs/prism/prism.js'
-
 // ========================================
 // INIT
 // ========================================
@@ -116,44 +36,6 @@ import Prism from '@/libs/prism/prism.js'
 // Platform detection
 const CtrlKey = /Mac/.test(navigator.platform) ? 'Cmd' : 'Ctrl'
 
-// Markdown Instance
-const md = new MarkdownIt({
-  html: true,
-  breaks: true,
-  linkify: true,
-  typography: true,
-  highlight(str, lang) {
-    return `<pre class="line-numbers"><code class="language-${lang}">${str}</code></pre>`
-  }
-})
-  .use(mdEmoji)
-  .use(mdTaskLists)
-  .use(mdExpandTabs)
-  .use(mdAbbr)
-  .use(mdSup)
-  .use(mdSub)
-  .use(mdMark)
-  .use(mdImsize)
-
-// ========================================
-// HELPER FUNCTIONS
-// ========================================
-
-// Inject line numbers for preview scroll sync
-let linesMap = []
-function injectLineNumbers (tokens, idx, options, env, slf) {
-  let line
-  if (tokens[idx].map && tokens[idx].level === 0) {
-    line = tokens[idx].map[0]
-    tokens[idx].attrJoin('class', 'line')
-    tokens[idx].attrSet('data-line', String(line))
-    linesMap.push(line)
-  }
-  return slf.renderToken(tokens, idx, options, env, slf)
-}
-md.renderer.rules.paragraph_open = injectLineNumbers
-md.renderer.rules.heading_open = injectLineNumbers
-
 // ========================================
 // Vue Component
 // ========================================
@@ -164,11 +46,10 @@ export default {
   },
   data() {
     return {
-      fabInsertMenu: false,
-      code: '# Header 1\n\nSample **Text**\nhttp://wiki.js.org\n:rocket: :) :( :| :P\n\n## Header 2\nSample Text\n\n```javascript\nvar test = require("test");\n\n// some comment\nconst foo = bar(\'param\') + 1.234;\n```\n\n### Header 3\nLorem *ipsum* ~~text~~',
+      code: '<h1>Title</h1>\n\n<p>Some text here</p>',
       cmOptions: {
         tabSize: 2,
-        mode: 'text/markdown',
+        mode: 'text/html',
         theme: 'wikijs-dark',
         lineNumbers: true,
         lineWrapping: true,
@@ -178,9 +59,7 @@ export default {
           annotateScrollbar: true
         },
         viewportMargin: 50
-      },
-      previewShown: true,
-      previewHTML: ''
+      }
     }
   },
   computed: {
@@ -206,55 +85,13 @@ export default {
         self.$parent.save()
       })
 
-      cm.setSize(null, 'calc(100vh - 100px)')
+      cm.setSize(null, 'calc(100vh - 64px)')
       cm.setOption('extraKeys', keyBindings)
-      cm.on('cursorActivity', cm => {
-        this.toolbarSync(cm)
-        this.scrollSync(cm)
-      })
       this.onCmInput(this.code)
     },
     onCmInput: _.debounce(function (newContent) {
-      linesMap = []
       this.$store.set('editor/content', newContent)
-      this.previewHTML = md.render(newContent)
-      this.$nextTick(() => {
-        Prism.highlightAllUnder(this.$refs.editorPreview)
-        this.scrollSync(this.cm)
-      })
-    }, 500),
-    /**
-     * Update toolbar state
-     */
-    toolbarSync(cm) {
-      const pos = cm.getCursor('start')
-      const token = cm.getTokenAt(pos)
-
-      if (!token.type) { return }
-
-      console.info(token)
-    },
-    /**
-     * Update scroll sync
-     */
-    scrollSync: _.debounce(function (cm) {
-      if (!this.previewShown || cm.somethingSelected()) { return }
-      let currentLine = cm.getCursor().line
-      if (currentLine < 3) {
-        this.Velocity(this.$refs.editorPreview, 'stop', true)
-        this.Velocity(this.$refs.editorPreview.firstChild, 'scroll', { offset: '-50', duration: 1000, container: this.$refs.editorPreview })
-      } else {
-        let closestLine = _.findLast(linesMap, n => n <= currentLine)
-        let destElm = this.$refs.editorPreview.querySelector(`[data-line='${closestLine}']`)
-        if (destElm) {
-          this.Velocity(this.$refs.editorPreview, 'stop', true)
-          this.Velocity(destElm, 'scroll', { offset: '-100', duration: 1000, container: this.$refs.editorPreview })
-        }
-      }
-    }, 500),
-    toggleAround (before, after) {
-
-    }
+    }, 500)
   }
 }
 </script>
@@ -296,84 +133,6 @@ export default {
     }
   }
 
-  &-preview {
-    flex: 1 1 50%;
-    background-color: mc('grey', '100');
-    position: relative;
-    height: calc(100vh - 100px);
-    overflow: hidden;
-
-    @include until($tablet) {
-      display: none;
-    }
-
-    &-enter-active, &-leave-active {
-      transition: max-width .5s ease;
-      max-width: 50vw;
-
-      .editor-code-preview-content {
-        width: 50vw;
-        overflow:hidden;
-      }
-    }
-    &-enter, &-leave-to {
-      max-width: 0;
-    }
-
-    &-content {
-      height: calc(100vh - 100px);
-      overflow-y: scroll;
-      padding: 30px 1rem 1rem 1rem;
-      width: calc(100% + 1rem + 17px)
-      // -ms-overflow-style: none;
-
-      // &::-webkit-scrollbar {
-      //   width: 0px;
-      //   background: transparent;
-      // }
-    }
-
-    &-title {
-      background-color: rgba(mc('blue', '100'), .75);
-      border-bottom-right-radius: 5px;
-      display: inline-flex;
-      height: 30px;
-      justify-content: center;
-      align-items: center;
-      padding: 0 1rem;
-      color: mc('blue', '800');
-      position: absolute;
-      top: 0;
-      left: 0;
-      z-index: 2;
-      text-transform: uppercase;
-      font-size: .7rem;
-      cursor: pointer;
-    }
-  }
-
-  &-toolbar {
-    background-color: mc('blue', '700');
-    background-image: linear-gradient(to bottom, mc('blue', '700') 0%, mc('blue','800') 100%);
-    color: #FFF;
-
-    .v-toolbar__content {
-      padding-left: 16px;
-
-      @include until($tablet) {
-        padding-left: 8px;
-      }
-    }
-  }
-
-  // ==========================================
-  // Fix FAB revealing under codemirror
-  // ==========================================
-
-  .speed-dial--fixed {
-    z-index: 8;
-  }
-
   // ==========================================
   // CODE MIRROR
   // ==========================================

+ 497 - 0
client/components/editor/editor-markdown.vue

@@ -0,0 +1,497 @@
+<template lang='pug'>
+  .editor-code
+    v-toolbar.editor-code-toolbar(dense, color='primary', dark)
+      v-tooltip(top)
+        v-btn(icon, slot='activator').mx-0
+          v-icon format_bold
+        span Bold
+      v-tooltip(top)
+        v-btn(icon, slot='activator').mx-0
+          v-icon format_italic
+        span Italic
+      v-tooltip(top)
+        v-btn(icon, slot='activator').mx-0
+          v-icon format_strikethrough
+        span Strikethrough
+      v-menu(offset-y, open-on-hover)
+        v-btn(icon, slot='activator').mx-0
+          v-icon font_download
+        v-list
+          v-list-tile(v-for='(n, idx) in 6', @click='', :key='idx')
+            v-list-tile-action
+              v-icon font_download
+            v-list-tile-title Heading {{n}}
+      v-tooltip(top)
+        v-btn(icon, slot='activator').mx-0
+          v-icon format_quote
+        span Blockquote
+      v-tooltip(top)
+        v-btn(icon, slot='activator').mx-0
+          v-icon format_list_bulleted
+        span Unordered List
+      v-tooltip(top)
+        v-btn(icon, slot='activator').mx-0
+          v-icon format_list_numbered
+        span Ordered List
+      v-tooltip(top)
+        v-btn(icon, slot='activator').mx-0
+          v-icon insert_link
+        span Link
+      v-tooltip(top)
+        v-btn(icon, slot='activator').mx-0
+          v-icon space_bar
+        span Inline Code
+      v-tooltip(top)
+        v-btn(icon, slot='activator').mx-0
+          v-icon code
+        span Code Block
+      v-tooltip(top)
+        v-btn(icon, slot='activator').mx-0
+          v-icon remove
+        span Horizontal Bar
+
+    .editor-code-main
+      .editor-code-editor
+        .editor-code-editor-title(v-if='previewShown', @click='previewShown = false') Editor
+        .editor-code-editor-title(v-else='previewShown', @click='previewShown = true'): v-icon(dark) drag_indicator
+        codemirror(ref='cm', v-model='code', :options='cmOptions', @ready='onCmReady', @input='onCmInput')
+      transition(name='editor-code-preview')
+        .editor-code-preview(v-if='previewShown')
+          .editor-code-preview-title(@click='previewShown = false') Preview
+          .editor-code-preview-content.markdown-content(ref='editorPreview', v-html='previewHTML')
+
+      v-speed-dial(v-model='fabInsertMenu', :open-on-hover='true', direction='top', transition='slide-y-reverse-transition', fixed, left, bottom)
+        v-btn(color='blue', fab, dark, v-model='fabInsertMenu', slot='activator')
+          v-icon add_circle
+          v-icon close
+        v-btn(color='teal', fab, dark): v-icon image
+        v-btn(color='pink', fab, dark): v-icon insert_drive_file
+        v-btn(color='red', fab, dark): v-icon play_circle_outline
+        v-btn(color='purple', fab, dark): v-icon multiline_chart
+        v-btn(color='indigo', fab, dark): v-icon functions
+</template>
+
+<script>
+import _ from 'lodash'
+
+// ========================================
+// IMPORTS
+// ========================================
+
+// Code Mirror
+import { codemirror } from 'vue-codemirror'
+import 'codemirror/lib/codemirror.css'
+
+// Language
+import 'codemirror/mode/markdown/markdown.js'
+
+// Addons
+import 'codemirror/addon/selection/active-line.js'
+import 'codemirror/addon/display/fullscreen.js'
+import 'codemirror/addon/display/fullscreen.css'
+import 'codemirror/addon/selection/mark-selection.js'
+import 'codemirror/addon/scroll/annotatescrollbar.js'
+import 'codemirror/addon/search/matchesonscrollbar.js'
+import 'codemirror/addon/search/searchcursor.js'
+import 'codemirror/addon/search/match-highlighter.js'
+
+// Markdown-it
+import MarkdownIt from 'markdown-it'
+import mdEmoji from 'markdown-it-emoji'
+import mdTaskLists from 'markdown-it-task-lists'
+import mdExpandTabs from 'markdown-it-expand-tabs'
+import mdAbbr from 'markdown-it-abbr'
+import mdSup from 'markdown-it-sup'
+import mdSub from 'markdown-it-sub'
+import mdMark from 'markdown-it-mark'
+import mdImsize from 'markdown-it-imsize'
+
+// Prism (Syntax Highlighting)
+import Prism from '@/libs/prism/prism.js'
+
+// ========================================
+// INIT
+// ========================================
+
+// Platform detection
+const CtrlKey = /Mac/.test(navigator.platform) ? 'Cmd' : 'Ctrl'
+
+// Markdown Instance
+const md = new MarkdownIt({
+  html: true,
+  breaks: true,
+  linkify: true,
+  typography: true,
+  highlight(str, lang) {
+    return `<pre class="line-numbers"><code class="language-${lang}">${str}</code></pre>`
+  }
+})
+  .use(mdEmoji)
+  .use(mdTaskLists)
+  .use(mdExpandTabs)
+  .use(mdAbbr)
+  .use(mdSup)
+  .use(mdSub)
+  .use(mdMark)
+  .use(mdImsize)
+
+// ========================================
+// HELPER FUNCTIONS
+// ========================================
+
+// Inject line numbers for preview scroll sync
+let linesMap = []
+function injectLineNumbers (tokens, idx, options, env, slf) {
+  let line
+  if (tokens[idx].map && tokens[idx].level === 0) {
+    line = tokens[idx].map[0]
+    tokens[idx].attrJoin('class', 'line')
+    tokens[idx].attrSet('data-line', String(line))
+    linesMap.push(line)
+  }
+  return slf.renderToken(tokens, idx, options, env, slf)
+}
+md.renderer.rules.paragraph_open = injectLineNumbers
+md.renderer.rules.heading_open = injectLineNumbers
+
+// ========================================
+// Vue Component
+// ========================================
+
+export default {
+  components: {
+    codemirror
+  },
+  data() {
+    return {
+      fabInsertMenu: false,
+      code: '# Header 1\n\nSample **Text**\nhttp://wiki.js.org\n:rocket: :) :( :| :P\n\n## Header 2\nSample Text\n\n```javascript\nvar test = require("test");\n\n// some comment\nconst foo = bar(\'param\') + 1.234;\n```\n\n### Header 3\nLorem *ipsum* ~~text~~',
+      cmOptions: {
+        tabSize: 2,
+        mode: 'text/markdown',
+        theme: 'wikijs-dark',
+        lineNumbers: true,
+        lineWrapping: true,
+        line: true,
+        styleActiveLine: true,
+        highlightSelectionMatches: {
+          annotateScrollbar: true
+        },
+        viewportMargin: 50
+      },
+      previewShown: true,
+      previewHTML: ''
+    }
+  },
+  computed: {
+    cm() {
+      return this.$refs.cm.codemirror
+    },
+    isMobile() {
+      return this.$vuetify.breakpoint.smAndDown
+    }
+  },
+  methods: {
+    onCmReady(cm) {
+      let self = this
+      const keyBindings = {
+        'F11' (cm) {
+          cm.setOption('fullScreen', !cm.getOption('fullScreen'))
+        },
+        'Esc' (cm) {
+          if (cm.getOption('fullScreen')) cm.setOption('fullScreen', false)
+        }
+      }
+      _.set(keyBindings, `${CtrlKey}-S`, cm => {
+        self.$parent.save()
+      })
+
+      cm.setSize(null, 'calc(100vh - 100px)')
+      cm.setOption('extraKeys', keyBindings)
+      cm.on('cursorActivity', cm => {
+        this.toolbarSync(cm)
+        this.scrollSync(cm)
+      })
+      this.onCmInput(this.code)
+    },
+    onCmInput: _.debounce(function (newContent) {
+      linesMap = []
+      this.$store.set('editor/content', newContent)
+      this.previewHTML = md.render(newContent)
+      this.$nextTick(() => {
+        Prism.highlightAllUnder(this.$refs.editorPreview)
+        this.scrollSync(this.cm)
+      })
+    }, 500),
+    /**
+     * Update toolbar state
+     */
+    toolbarSync(cm) {
+      const pos = cm.getCursor('start')
+      const token = cm.getTokenAt(pos)
+
+      if (!token.type) { return }
+
+      console.info(token)
+    },
+    /**
+     * Update scroll sync
+     */
+    scrollSync: _.debounce(function (cm) {
+      if (!this.previewShown || cm.somethingSelected()) { return }
+      let currentLine = cm.getCursor().line
+      if (currentLine < 3) {
+        this.Velocity(this.$refs.editorPreview, 'stop', true)
+        this.Velocity(this.$refs.editorPreview.firstChild, 'scroll', { offset: '-50', duration: 1000, container: this.$refs.editorPreview })
+      } else {
+        let closestLine = _.findLast(linesMap, n => n <= currentLine)
+        let destElm = this.$refs.editorPreview.querySelector(`[data-line='${closestLine}']`)
+        if (destElm) {
+          this.Velocity(this.$refs.editorPreview, 'stop', true)
+          this.Velocity(destElm, 'scroll', { offset: '-100', duration: 1000, container: this.$refs.editorPreview })
+        }
+      }
+    }, 500),
+    toggleAround (before, after) {
+
+    }
+  }
+}
+</script>
+
+<style lang='scss'>
+.editor-code {
+  &-main {
+    display: flex;
+    width: 100%;
+  }
+
+  &-editor {
+    background-color: darken(mc('grey', '900'), 4.5%);
+    flex: 1 1 50%;
+    display: block;
+    height: calc(100vh - 96px);
+    position: relative;
+
+    &-title {
+      background-color: mc('grey', '800');
+      border-bottom-left-radius: 5px;
+      display: inline-flex;
+      height: 30px;
+      justify-content: center;
+      align-items: center;
+      padding: 0 1rem;
+      color: mc('grey', '500');
+      position: absolute;
+      top: 0;
+      right: 0;
+      z-index: 7;
+      text-transform: uppercase;
+      font-size: .7rem;
+      cursor: pointer;
+
+      @include until($tablet) {
+        display: none;
+      }
+    }
+  }
+
+  &-preview {
+    flex: 1 1 50%;
+    background-color: mc('grey', '100');
+    position: relative;
+    height: calc(100vh - 100px);
+    overflow: hidden;
+
+    @include until($tablet) {
+      display: none;
+    }
+
+    &-enter-active, &-leave-active {
+      transition: max-width .5s ease;
+      max-width: 50vw;
+
+      .editor-code-preview-content {
+        width: 50vw;
+        overflow:hidden;
+      }
+    }
+    &-enter, &-leave-to {
+      max-width: 0;
+    }
+
+    &-content {
+      height: calc(100vh - 100px);
+      overflow-y: scroll;
+      padding: 30px 1rem 1rem 1rem;
+      width: calc(100% + 1rem + 17px)
+      // -ms-overflow-style: none;
+
+      // &::-webkit-scrollbar {
+      //   width: 0px;
+      //   background: transparent;
+      // }
+    }
+
+    &-title {
+      background-color: rgba(mc('blue', '100'), .75);
+      border-bottom-right-radius: 5px;
+      display: inline-flex;
+      height: 30px;
+      justify-content: center;
+      align-items: center;
+      padding: 0 1rem;
+      color: mc('blue', '800');
+      position: absolute;
+      top: 0;
+      left: 0;
+      z-index: 2;
+      text-transform: uppercase;
+      font-size: .7rem;
+      cursor: pointer;
+    }
+  }
+
+  &-toolbar {
+    background-color: mc('blue', '700');
+    background-image: linear-gradient(to bottom, mc('blue', '700') 0%, mc('blue','800') 100%);
+    color: #FFF;
+
+    .v-toolbar__content {
+      padding-left: 16px;
+
+      @include until($tablet) {
+        padding-left: 8px;
+      }
+    }
+  }
+
+  // ==========================================
+  // Fix FAB revealing under codemirror
+  // ==========================================
+
+  .speed-dial--fixed {
+    z-index: 8;
+  }
+
+  // ==========================================
+  // CODE MIRROR
+  // ==========================================
+
+  .CodeMirror {
+    height: auto;
+
+    .cm-header-1 {
+      font-size: 1.5rem;
+    }
+    .cm-header-2 {
+      font-size: 1.25rem;
+    }
+    .cm-header-3 {
+      font-size: 1.15rem;
+    }
+    .cm-header-4 {
+      font-size: 1.1rem;
+    }
+    .cm-header-5 {
+      font-size: 1.05rem;
+    }
+    .cm-header-6 {
+      font-size: 1.025rem;
+    }
+  }
+
+  .CodeMirror-focused .cm-matchhighlight {
+    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAFklEQVQI12NgYGBgkKzc8x9CMDAwAAAmhwSbidEoSQAAAABJRU5ErkJggg==);
+    background-position: bottom;
+    background-repeat: repeat-x;
+  }
+  .cm-matchhighlight {
+    background-color: mc('grey', '800');
+  }
+  .CodeMirror-selection-highlight-scrollbar {
+    background-color: mc('green', '600');
+  }
+
+  .cm-s-wikijs-dark.CodeMirror {
+    background: darken(mc('grey','900'), 3%);
+    color: #e0e0e0;
+  }
+  .cm-s-wikijs-dark div.CodeMirror-selected {
+    background: mc('blue','800');
+  }
+  .cm-s-wikijs-dark .cm-matchhighlight {
+    background: mc('blue','800');
+  }
+  .cm-s-wikijs-dark .CodeMirror-line::selection, .cm-s-wikijs-dark .CodeMirror-line > span::selection, .cm-s-wikijs-dark .CodeMirror-line > span > span::selection {
+    background: mc('red', '500');
+  }
+  .cm-s-wikijs-dark .CodeMirror-line::-moz-selection, .cm-s-wikijs-dark .CodeMirror-line > span::-moz-selection, .cm-s-wikijs-dark .CodeMirror-line > span > span::-moz-selection {
+    background: mc('red', '500');
+  }
+  .cm-s-wikijs-dark .CodeMirror-gutters {
+    background: darken(mc('grey','900'), 6%);
+    border-right: 1px solid mc('grey','900');
+  }
+  .cm-s-wikijs-dark .CodeMirror-guttermarker {
+    color: #ac4142;
+  }
+  .cm-s-wikijs-dark .CodeMirror-guttermarker-subtle {
+    color: #505050;
+  }
+  .cm-s-wikijs-dark .CodeMirror-linenumber {
+    color: mc('grey','800');
+  }
+  .cm-s-wikijs-dark .CodeMirror-cursor {
+    border-left: 1px solid #b0b0b0;
+  }
+  .cm-s-wikijs-dark span.cm-comment {
+    color: mc('orange','800');
+  }
+  .cm-s-wikijs-dark span.cm-atom {
+    color: #aa759f;
+  }
+  .cm-s-wikijs-dark span.cm-number {
+    color: #aa759f;
+  }
+  .cm-s-wikijs-dark span.cm-property, .cm-s-wikijs-dark span.cm-attribute {
+    color: #90a959;
+  }
+  .cm-s-wikijs-dark span.cm-keyword {
+    color: #ac4142;
+  }
+  .cm-s-wikijs-dark span.cm-string {
+    color: #f4bf75;
+  }
+  .cm-s-wikijs-dark span.cm-variable {
+    color: #90a959;
+  }
+  .cm-s-wikijs-dark span.cm-variable-2 {
+    color: #6a9fb5;
+  }
+  .cm-s-wikijs-dark span.cm-def {
+    color: #d28445;
+  }
+  .cm-s-wikijs-dark span.cm-bracket {
+    color: #e0e0e0;
+  }
+  .cm-s-wikijs-dark span.cm-tag {
+    color: #ac4142;
+  }
+  .cm-s-wikijs-dark span.cm-link {
+    color: #aa759f;
+  }
+  .cm-s-wikijs-dark span.cm-error {
+    background: #ac4142;
+    color: #b0b0b0;
+  }
+  .cm-s-wikijs-dark .CodeMirror-activeline-background {
+    background: mc('grey','900');
+  }
+  .cm-s-wikijs-dark .CodeMirror-matchingbracket {
+    text-decoration: underline;
+    color: white !important;
+  }
+
+}
+</style>

+ 62 - 0
client/components/editor/editor-wysiwyg.vue

@@ -0,0 +1,62 @@
+<template lang='pug'>
+  .editor-wysiwyg
+    div(ref='editor')
+
+</template>
+
+<script>
+import 'grapesjs/dist/css/grapes.min.css'
+import grapesjs from 'grapesjs'
+
+let editor
+
+export default {
+  mounted() {
+    editor = grapesjs.init({
+      container: this.$refs.editor,
+      width: 'auto',
+      height: 'calc(100vh - 64px)',
+      storageManager: { type: null },
+      // panels: { defaults: [] }
+      blockManager: {
+        blocks: [
+          {
+            id: 'section', // id is mandatory
+            label: '<b>Section</b>', // You can use HTML/SVG inside labels
+            attributes: { class: 'gjs-block-section' },
+            content: `<section>
+              <h1>This is a simple title</h1>
+              <div>This is just a Lorem text: Lorem ipsum dolor sit amet</div>
+            </section>`
+          }, {
+            id: 'text',
+            label: 'Text',
+            content: '<div data-gjs-type="text">Insert your text here</div>',
+          }, {
+            id: 'image',
+            label: 'Image',
+            // Select the component once it's dropped
+            select: true,
+            // You can pass components as a JSON instead of a simple HTML string,
+            // in this case we also use a defined component type `image`
+            content: { type: 'image' },
+            // This triggers `active` event on dropped components and the `image`
+            // reacts by opening the AssetManager
+            activate: true
+          }
+        ]
+      }
+    })
+  }
+}
+</script>
+
+<style lang="scss">
+
+.gjs-block {
+  width: auto;
+  height: auto;
+  min-height: auto;
+}
+
+</style>

+ 14 - 2
dev/webpack/webpack.dev.js

@@ -121,7 +121,8 @@ module.exports = {
       {
         test: /\.svg$/,
         exclude: [
-          path.join(process.cwd(), 'client/svg')
+          path.join(process.cwd(), 'client/svg'),
+          path.join(process.cwd(), 'node_modules/grapesjs')
         ],
         use: [
           {
@@ -136,7 +137,8 @@ module.exports = {
       {
         test: /\.svg$/,
         include: [
-          path.join(process.cwd(), 'client/svg')
+          path.join(process.cwd(), 'client/svg'),
+          path.join(process.cwd(), 'node_modules/grapesjs/src/styles/fonts/main-fonts.svg')
         ],
         use: [
           {
@@ -152,6 +154,16 @@ module.exports = {
           { loader: 'graphql-tag/loader' }
         ]
       },
+      {
+        test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
+        use: [{
+          loader: 'file-loader',
+          options: {
+            name: '[name].[ext]',
+            outputPath: 'fonts/'
+          }
+        }]
+      },
       {
         test: /.jsx$/,
         loader: 'babel-loader',

+ 2 - 0
package.json

@@ -203,6 +203,7 @@
     "eslint-plugin-standard": "4.0.0",
     "eslint-plugin-vue": "4.7.1",
     "file-loader": "2.0.0",
+    "grapesjs": "0.14.29",
     "graphiql": "0.11.11",
     "graphql-persisted-document-loader": "1.0.1",
     "graphql-tag": "^2.9.2",
@@ -230,6 +231,7 @@
     "raw-loader": "0.5.1",
     "react": "16.5.0",
     "react-dom": "16.5.0",
+    "resolve-url-loader": "3.0.0",
     "sass-loader": "7.1.0",
     "sass-resources-loader": "1.3.3",
     "script-ext-html-webpack-plugin": "2.0.1",

+ 1 - 1
server/modules/editor/markdown/definition.yml

@@ -1,6 +1,6 @@
 key: markdown
 title: Markdown
-description: Default Markdown editor
+description: Basic Markdown editor
 contentType: markdown
 author: requarks.io
 props: {}

+ 6 - 0
server/modules/editor/wysiwyg/definition.yml

@@ -0,0 +1,6 @@
+key: wysiwyg
+title: WYSIWYG
+description: Advanced Visual HTML Builder
+contentType: html
+author: requarks.io
+props: {}

+ 247 - 10
yarn.lock

@@ -1350,6 +1350,18 @@ acorn@^5.0.3, acorn@^5.6.0, acorn@^5.6.2:
   version "5.7.1"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8"
 
+adjust-sourcemap-loader@^1.1.0:
+  version "1.2.0"
+  resolved "http://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-1.2.0.tgz#e33fde95e50db9f2a802e3647e311d2fc5000c69"
+  dependencies:
+    assert "^1.3.0"
+    camelcase "^1.2.1"
+    loader-utils "^1.1.0"
+    lodash.assign "^4.0.1"
+    lodash.defaults "^3.1.2"
+    object-path "^0.9.2"
+    regex-parser "^2.2.9"
+
 ajv-errors@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.0.tgz#ecf021fa108fd17dfb5e6b383f2dd233e31ffc59"
@@ -1904,6 +1916,10 @@ argparse@^1.0.7:
   dependencies:
     sprintf-js "~1.0.2"
 
+arity-n@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745"
+
 arr-diff@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
@@ -1990,7 +2006,7 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
 
-assert@^1.1.1:
+assert@^1.1.1, assert@^1.3.0:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91"
   dependencies:
@@ -2291,6 +2307,25 @@ babylon@^6.0.18, babylon@^6.18.0:
   version "6.18.0"
   resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
 
+backbone-undo@^0.2.5:
+  version "0.2.5"
+  resolved "https://registry.yarnpkg.com/backbone-undo/-/backbone-undo-0.2.5.tgz#55b25230f90319ca622465e89a80248b893c2ce2"
+  dependencies:
+    backbone "1.0.0 - 1.2.1"
+    underscore "1.4.4 - 1.8.3"
+
+"backbone@1.0.0 - 1.2.1":
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/backbone/-/backbone-1.2.1.tgz#d7219c5ed49e5e131dbffaf25c96d6d2cc3ca03e"
+  dependencies:
+    underscore ">=1.7.0"
+
+backbone@^1.3.3:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/backbone/-/backbone-1.3.3.tgz#4cc80ea7cb1631ac474889ce40f2f8bc683b2999"
+  dependencies:
+    underscore ">=1.8.3"
+
 backo2@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
@@ -2752,7 +2787,7 @@ camelcase-keys@^2.0.0:
     camelcase "^2.0.0"
     map-obj "^1.0.0"
 
-camelcase@^1.0.2:
+camelcase@^1.0.2, camelcase@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
 
@@ -2817,6 +2852,10 @@ caseless@~0.12.0:
   version "0.12.0"
   resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
 
+cash-dom@^1.3.7:
+  version "1.3.7"
+  resolved "https://registry.yarnpkg.com/cash-dom/-/cash-dom-1.3.7.tgz#4e09f98bc7b3339665b48a579b4bb93189ccc3f7"
+
 caw@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/caw/-/caw-2.0.1.tgz#6c3ca071fc194720883c2dc5da9b074bfc7e9e95"
@@ -3117,6 +3156,10 @@ code-point-at@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
 
+codemirror-formatting@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/codemirror-formatting/-/codemirror-formatting-1.0.0.tgz#879cc1fdd9018343c1d5511769ce5360d705ebf2"
+
 codemirror-graphql@^0.6.11:
   version "0.6.12"
   resolved "https://registry.yarnpkg.com/codemirror-graphql/-/codemirror-graphql-0.6.12.tgz#91a273fe5188857524a30221d06e645b4ca41f00"
@@ -3128,6 +3171,10 @@ codemirror@^5.26.0, codemirror@^5.32.0:
   version "5.38.0"
   resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.38.0.tgz#26a9551446e51dbdde36aabe60f72469724fd332"
 
+codemirror@^5.39.0:
+  version "5.40.0"
+  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.40.0.tgz#2f5ed47366e514f41349ba0fe34daaa39be4e257"
+
 collection-visit@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@@ -3259,6 +3306,12 @@ component-emitter@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
 
+compose-function@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f"
+  dependencies:
+    arity-n "^1.0.4"
+
 compressible@~2.0.14:
   version "2.0.14"
   resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.14.tgz#326c5f507fbb055f54116782b969a81b67a29da7"
@@ -3363,10 +3416,20 @@ continuation-local-storage@^3.1.4:
     async-listener "^0.6.0"
     emitter-listener "^1.1.1"
 
+convert-source-map@^0.3.3:
+  version "0.3.5"
+  resolved "http://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190"
+
 convert-source-map@^1.1.0:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
 
+convert-source-map@^1.5.1:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"
+  dependencies:
+    safe-buffer "~5.1.1"
+
 cookie-parser@1.4.3:
   version "1.4.3"
   resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.3.tgz#0fe31fa19d000b95f4aadf1f53fdc2b8a203baa5"
@@ -3675,6 +3738,15 @@ css-what@2.1:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd"
 
+css@^2.0.0:
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
+  dependencies:
+    inherits "^2.0.3"
+    source-map "^0.6.1"
+    source-map-resolve "^0.5.2"
+    urix "^0.1.0"
+
 cssdb@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-3.1.0.tgz#e1efa0d8831e9e655cadd499a89266d3565d9697"
@@ -3783,6 +3855,12 @@ cyclist@~0.2.2:
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
 
+d@1:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f"
+  dependencies:
+    es5-ext "^0.10.9"
+
 dashdash@^1.12.0, dashdash@^1.14.0:
   version "1.14.1"
   resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
@@ -4353,6 +4431,29 @@ es-to-primitive@^1.1.1:
     is-date-object "^1.0.1"
     is-symbol "^1.0.1"
 
+es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14:
+  version "0.10.46"
+  resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.46.tgz#efd99f67c5a7ec789baa3daa7f79870388f7f572"
+  dependencies:
+    es6-iterator "~2.0.3"
+    es6-symbol "~3.1.1"
+    next-tick "1"
+
+es6-iterator@^2.0.3, es6-iterator@~2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
+  dependencies:
+    d "1"
+    es5-ext "^0.10.35"
+    es6-symbol "^3.1.1"
+
+es6-symbol@^3.1.1, es6-symbol@~3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
+  dependencies:
+    d "1"
+    es5-ext "~0.10.14"
+
 escape-html@~1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
@@ -5040,6 +5141,10 @@ follow-redirects@^1.3.0:
   dependencies:
     debug "^3.1.0"
 
+font-awesome@^4.7.0:
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133"
+
 for-in@^0.1.3:
   version "0.1.8"
   resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1"
@@ -5415,6 +5520,21 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
 
+grapesjs@0.14.29:
+  version "0.14.29"
+  resolved "https://registry.yarnpkg.com/grapesjs/-/grapesjs-0.14.29.tgz#7a8216ebd26871279ad06f3bb513538eb0e95ec1"
+  dependencies:
+    backbone "^1.3.3"
+    backbone-undo "^0.2.5"
+    cash-dom "^1.3.7"
+    codemirror "^5.39.0"
+    codemirror-formatting "^1.0.0"
+    font-awesome "^4.7.0"
+    keymaster "^1.6.2"
+    promise-polyfill "^8.0.0"
+    spectrum-colorpicker "^1.8.0"
+    underscore "^1.9.1"
+
 graphiql@0.11.11:
   version "0.11.11"
   resolved "https://registry.yarnpkg.com/graphiql/-/graphiql-0.11.11.tgz#eeaf9a38927dbe8c6ecbf81e700735e16ec50e71"
@@ -6769,6 +6889,10 @@ keygrip@~1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.2.tgz#ad3297c557069dea8bcfe7a4fa491b75c5ddeb91"
 
+keymaster@^1.6.2:
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/keymaster/-/keymaster-1.6.2.tgz#e1ae54d0ea9488f9f60b66b668f02e9a1946c6eb"
+
 keyv@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373"
@@ -6994,11 +7118,50 @@ lodash-es@^4.17.5, lodash-es@^4.2.1:
   version "4.17.10"
   resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.10.tgz#62cd7104cdf5dd87f235a837f0ede0e8e5117e05"
 
+lodash._baseassign@^3.0.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
+  dependencies:
+    lodash._basecopy "^3.0.0"
+    lodash.keys "^3.0.0"
+
+lodash._basecopy@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
+
+lodash._bindcallback@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
+
+lodash._createassigner@^3.0.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11"
+  dependencies:
+    lodash._bindcallback "^3.0.0"
+    lodash._isiterateecall "^3.0.0"
+    lodash.restparam "^3.0.0"
+
+lodash._getnative@^3.0.0:
+  version "3.9.1"
+  resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
+
+lodash._isiterateecall@^3.0.0:
+  version "3.0.9"
+  resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c"
+
 lodash._reinterpolate@~3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
 
-lodash.assign@^4.2.0:
+lodash.assign@^3.0.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa"
+  dependencies:
+    lodash._baseassign "^3.0.0"
+    lodash._createassigner "^3.0.0"
+    lodash.keys "^3.0.0"
+
+lodash.assign@^4.0.1, lodash.assign@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
 
@@ -7022,7 +7185,14 @@ lodash.debounce@^4.0.8:
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
 
-lodash.defaults@^4.2.0:
+lodash.defaults@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-3.1.2.tgz#c7308b18dbf8bc9372d701a73493c61192bd2e2c"
+  dependencies:
+    lodash.assign "^3.0.0"
+    lodash.restparam "^3.0.0"
+
+lodash.defaults@^4.0.0, lodash.defaults@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
 
@@ -7050,6 +7220,14 @@ lodash.includes@^4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
 
+lodash.isarguments@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
+
+lodash.isarray@^3.0.0:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
+
 lodash.isboolean@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
@@ -7082,6 +7260,14 @@ lodash.kebabcase@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36"
 
+lodash.keys@^3.0.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
+  dependencies:
+    lodash._getnative "^3.0.0"
+    lodash.isarguments "^3.0.0"
+    lodash.isarray "^3.0.0"
+
 lodash.keys@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.2.0.tgz#a08602ac12e4fb83f91fc1fb7a360a4d9ba35205"
@@ -7118,6 +7304,10 @@ lodash.repeat@^4.0.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/lodash.repeat/-/lodash.repeat-4.1.0.tgz#fc7de8131d8c8ac07e4b49f74ffe829d1f2bec44"
 
+lodash.restparam@^3.0.0:
+  version "3.6.1"
+  resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
+
 lodash.sample@^4.2.1:
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/lodash.sample/-/lodash.sample-4.2.1.tgz#5e4291b0c753fa1abeb0aab8fb29df1b66f07f6d"
@@ -7778,6 +7968,10 @@ neo-async@^2.5.0:
   version "2.5.1"
   resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee"
 
+next-tick@1:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c"
+
 nice-try@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
@@ -8124,6 +8318,10 @@ object-path@^0.11.4:
   version "0.11.4"
   resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949"
 
+object-path@^0.9.2:
+  version "0.9.2"
+  resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.9.2.tgz#0fd9a74fc5fad1ae3968b586bda5c632bd6c05a5"
+
 object-visit@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
@@ -9710,6 +9908,10 @@ promise-polyfill@^6.0.1:
   version "6.1.0"
   resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-6.1.0.tgz#dfa96943ea9c121fca4de9b5868cb39d3472e057"
 
+promise-polyfill@^8.0.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.1.0.tgz#30059da54d1358ce905ac581f287e184aedf995d"
+
 promise@7.x, promise@^7.0.1, promise@^7.1.1:
   version "7.3.1"
   resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
@@ -10348,6 +10550,10 @@ regex-not@^1.0.0, regex-not@^1.0.2:
     extend-shallow "^3.0.2"
     safe-regex "^1.1.0"
 
+regex-parser@^2.2.9:
+  version "2.2.9"
+  resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.9.tgz#a372f45a248b62976a568037c1b6e60a60599192"
+
 regexpp@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.0.tgz#b2a7534a85ca1b033bcf5ce9ff8e56d4e0755365"
@@ -10572,6 +10778,22 @@ resolve-from@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
 
+resolve-url-loader@3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.0.0.tgz#c47eeca1fc8d62b08dc2eef12b3af5af3e775c74"
+  dependencies:
+    adjust-sourcemap-loader "^1.1.0"
+    camelcase "^4.1.0"
+    compose-function "^3.0.3"
+    convert-source-map "^1.5.1"
+    es6-iterator "^2.0.3"
+    loader-utils "^1.1.0"
+    lodash.defaults "^4.0.0"
+    postcss "^7.0.0"
+    rework "^1.0.1"
+    rework-visit "^1.0.0"
+    source-map "^0.5.7"
+
 resolve-url@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
@@ -10607,6 +10829,17 @@ retry@0.6.0:
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/retry/-/retry-0.6.0.tgz#1c010713279a6fd1e8def28af0c3ff1871caa537"
 
+rework-visit@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a"
+
+rework@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/rework/-/rework-1.0.1.tgz#30806a841342b54510aa4110850cd48534144aa7"
+  dependencies:
+    convert-source-map "^0.3.3"
+    css "^2.0.0"
+
 rgb-hex@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/rgb-hex/-/rgb-hex-2.1.0.tgz#c773c5fe2268a25578d92539a82a7a5ce53beda6"
@@ -11021,7 +11254,7 @@ source-list-map@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085"
 
-source-map-resolve@^0.5.0:
+source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
   version "0.5.2"
   resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259"
   dependencies:
@@ -11084,6 +11317,10 @@ spdx-license-ids@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87"
 
+spectrum-colorpicker@^1.8.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/spectrum-colorpicker/-/spectrum-colorpicker-1.8.0.tgz#b926cf5002c0a77860b5f8351e1c093c65200107"
+
 split-string@^3.0.1, split-string@^3.0.2:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@@ -11767,11 +12004,15 @@ undefsafe@^2.0.2:
   dependencies:
     debug "^2.2.0"
 
+"underscore@1.4.4 - 1.8.3", underscore@~1.8.3:
+  version "1.8.3"
+  resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
+
 underscore@1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8"
 
-underscore@^1.7.0:
+underscore@>=1.7.0, underscore@>=1.8.3, underscore@^1.7.0, underscore@^1.9.1:
   version "1.9.1"
   resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
 
@@ -11779,10 +12020,6 @@ underscore@~1.5.1:
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.5.2.tgz#1335c5e4f5e6d33bbb4b006ba8c86a00f556de08"
 
-underscore@~1.8.3:
-  version "1.8.3"
-  resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
-
 unicode-canonical-property-names-ecmascript@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"