2
0
Nick 5 жил өмнө
parent
commit
7634bd266d

+ 1 - 1
client/components/admin.vue

@@ -128,7 +128,7 @@ import { get, sync } from 'vuex-pathify'
 
 import statsQuery from 'gql/admin/dashboard/dashboard-query-stats.gql'
 
-import adminStore from '@/store/admin'
+import adminStore from '../store/admin'
 
 /* global WIKI */
 

+ 5 - 4
client/components/editor.vue

@@ -51,7 +51,7 @@ import { Base64 } from 'js-base64'
 import createPageMutation from 'gql/editor/create.gql'
 import updatePageMutation from 'gql/editor/update.gql'
 
-import editorStore from '@/store/editor'
+import editorStore from '../store/editor'
 
 /* global WIKI */
 
@@ -62,6 +62,7 @@ export default {
   components: {
     AtomSpinner,
     editorCode: () => import(/* webpackChunkName: "editor-code", webpackMode: "lazy" */ './editor/editor-code.vue'),
+    editorCkeditor: () => import(/* webpackChunkName: "editor-ckeditor", webpackMode: "lazy" */ './editor/editor-ckeditor.vue'),
     editorMarkdown: () => import(/* webpackChunkName: "editor-markdown", webpackMode: "lazy" */ './editor/editor-markdown.vue'),
     editorWysiwyg: () => import(/* webpackChunkName: "editor-wysiwyg", webpackMode: "lazy" */ './editor/editor-wysiwyg.vue'),
     editorModalEditorselect: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-editorselect.vue'),
@@ -153,7 +154,7 @@ export default {
   mounted() {
     this.$store.set('editor/mode', this.initMode || 'create')
 
-    this.initContentParsed = this.initContent ? Base64.decode(this.initContent) : '# Header\n\nYour content here'
+    this.initContentParsed = this.initContent ? Base64.decode(this.initContent) : ''
     this.$store.set('editor/content', this.initContentParsed)
     if (this.mode === 'create') {
       _.delay(() => {
@@ -194,7 +195,7 @@ export default {
             variables: {
               content: this.$store.get('editor/content'),
               description: this.$store.get('page/description'),
-              editor: 'markdown',
+              editor: this.$store.get('editor/editorKey'),
               locale: this.$store.get('page/locale'),
               isPrivate: false,
               isPublished: this.$store.get('page/isPublished'),
@@ -230,7 +231,7 @@ export default {
               id: this.$store.get('page/id'),
               content: this.$store.get('editor/content'),
               description: this.$store.get('page/description'),
-              editor: 'markdown',
+              editor: this.$store.get('editor/editorKey'),
               locale: this.$store.get('page/locale'),
               isPrivate: false,
               isPublished: this.$store.get('page/isPublished'),

+ 187 - 0
client/components/editor/editor-ckeditor.vue

@@ -0,0 +1,187 @@
+<template lang='pug'>
+  .editor-ckeditor
+    div(ref='toolbarContainer')
+    div.contents(ref='editor')
+    v-system-bar.editor-ckeditor-sysbar(dark, status, color='grey darken-3')
+      .caption.editor-ckeditor-sysbar-locale {{locale.toUpperCase()}}
+      .caption.px-3 /{{path}}
+      template(v-if='$vuetify.breakpoint.mdAndUp')
+        v-spacer
+        .caption Visual Editor
+        v-spacer
+        .caption {{stats.characters}} Chars, {{stats.words}} Words
+</template>
+
+<script>
+import _ from 'lodash'
+import { get, sync } from 'vuex-pathify'
+import DecoupledEditor from '@requarks/ckeditor5'
+
+export default {
+  props: {
+    save: {
+      type: Function,
+      default: () => {}
+    }
+  },
+  data() {
+    return {
+      editor: null,
+      stats: {
+        characters: 0,
+        words: 0
+      },
+      content: ''
+    }
+  },
+  computed: {
+    isMobile() {
+      return this.$vuetify.breakpoint.smAndDown
+    },
+    locale: get('page/locale'),
+    path: get('page/path'),
+    activeModal: sync('editor/activeModal')
+  },
+  methods: {
+  },
+  async mounted () {
+    this.$store.set('editor/editorKey', 'ckeditor')
+
+    this.editor = await DecoupledEditor.create(this.$refs.editor, {
+      placeholder: 'Type the page content here',
+      wordCount: {
+        onUpdate: stats => {
+          this.stats = {
+            characters: stats.characters,
+            words: stats.words
+          }
+        }
+      }
+    })
+    this.$refs.toolbarContainer.appendChild(this.editor.ui.view.toolbar.element)
+
+    if (this.mode !== 'create') {
+      this.editor.setData(this.$store.get('editor/content'))
+    }
+
+    this.editor.model.document.on('change:data', _.debounce(evt => {
+      this.$store.set('editor/content', this.editor.getData())
+    }, 300))
+
+    this.$root.$on('editorInsert', opts => {
+      console.info(opts)
+      switch (opts.kind) {
+        case 'IMAGE':
+          this.editor.execute('imageInsert', {
+            source: opts.path
+          })
+          break
+        case 'BINARY':
+          this.insertAtCursor({
+            content: `[${opts.text}](${opts.path})`
+          })
+          break
+      }
+    })
+  },
+  beforeDestroy () {
+    if (this.editor) {
+      this.editor.destroy()
+      this.editor = null
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+
+$editor-height: calc(100vh - 64px - 24px);
+$editor-height-mobile: calc(100vh - 56px - 16px);
+
+.editor-ckeditor {
+  background-color: mc('grey', '200');
+  flex: 1 1 50%;
+  display: flex;
+  flex-flow: column nowrap;
+  height: $editor-height;
+  max-height: $editor-height;
+  position: relative;
+
+  @at-root .theme--dark & {
+    background-color: mc('grey', '900');
+  }
+
+  @include until($tablet) {
+    height: $editor-height-mobile;
+    max-height: $editor-height-mobile;
+  }
+
+  &-sysbar {
+    padding-left: 0;
+
+    &-locale {
+      background-color: rgba(255,255,255,.25);
+      display:inline-flex;
+      padding: 0 12px;
+      height: 24px;
+      width: 63px;
+      justify-content: center;
+      align-items: center;
+    }
+  }
+
+  .ck.ck-toolbar {
+    border: none;
+    justify-content: center;
+    background-color: mc('grey', '300');
+    color: #FFF;
+  }
+
+  > .ck-editor__editable {
+    background-color: mc('grey', '100');
+    overflow-y: auto;
+    overflow-x: hidden;
+    padding: 2rem;
+    box-shadow: 0 0 5px hsla(0, 0, 0, .1);
+    margin: 1rem auto 0;
+    width: calc(100vw - 256px - 16vw);
+    min-height: calc(100vh - 64px - 24px - 1rem - 40px);
+    border-radius: 5px;
+
+    @at-root .theme--dark & {
+      background-color: #303030;
+      color: #FFF;
+    }
+
+    @include until($widescreen) {
+      width: calc(100vw - 2rem);
+      margin: 1rem 1rem 0 1rem;
+      min-height: calc(100vh - 64px - 24px - 1rem - 40px);
+    }
+
+    @include until($tablet) {
+      width: 100%;
+      margin: 0;
+      min-height: calc(100vh - 56px - 24px - 76px);
+    }
+
+    &.ck.ck-editor__editable:not(.ck-editor__nested-editable).ck-focused {
+      border-color: #FFF;
+      box-shadow: 0 0 10px rgba(mc('blue', '700'), .25);
+
+      @at-root .theme--dark & {
+        border-color: #444;
+        border-bottom: none;
+        box-shadow: 0 0 10px rgba(#000, .25);
+      }
+    }
+
+    &.ck .ck-editor__nested-editable.ck-editor__nested-editable_focused,
+    &.ck .ck-editor__nested-editable:focus,
+    .ck-widget.table td.ck-editor__nested-editable.ck-editor__nested-editable_focused,
+    .ck-widget.table th.ck-editor__nested-editable.ck-editor__nested-editable_focused {
+      background-color: mc('grey', '900');
+    }
+  }
+}
+</style>

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

@@ -296,6 +296,7 @@ export default {
     },
     locale: get('page/locale'),
     path: get('page/path'),
+    mode: get('editor/mode'),
     activeModal: sync('editor/activeModal')
   },
   methods: {
@@ -456,6 +457,12 @@ export default {
     }
   },
   mounted() {
+    this.$store.set('editor/editorKey', 'markdown')
+
+    if (this.mode === 'create') {
+      this.$store.set('editor/content', '# Header\nYour content here')
+    }
+
     // Initialize CodeMirror
 
     this.cm = CodeMirror.fromTextArea(this.$refs.cm, {

+ 16 - 6
client/components/editor/editor-modal-editorselect.vue

@@ -48,17 +48,27 @@
                   img(src='/svg/icon-table.svg', alt='Tabular', style='width: 36px;')
                   .body-2.grey--text.mt-2.text--darken-2 Tabular
                   .caption.grey--text.text--darken-1 Excel-like
+            //- v-flex(xs4)
+            //-   v-card.radius-7.grey(
+            //-     hover
+            //-     light
+            //-     ripple
+            //-     disabled
+            //-     )
+            //-     v-card-text.text-center(@click='selectEditor("wysiwyg")')
+            //-       img(src='/svg/icon-open-in-browser.svg', alt='Visual Builder', style='width: 36px;')
+            //-       .body-2.mt-2.grey--text.text--darken-2 Visual Builder
+            //-       .caption.grey--text.text--darken-1 Drag-n-drop
             v-flex(xs4)
-              v-card.radius-7.grey(
+              v-card.radius-7(
                 hover
                 light
                 ripple
-                disabled
                 )
-                v-card-text.text-center(@click='selectEditor("wysiwyg")')
-                  img(src='/svg/icon-open-in-browser.svg', alt='Visual Builder', style='width: 36px;')
-                  .body-2.mt-2.grey--text.text--darken-2 Visual Builder
-                  .caption.grey--text.text--darken-1 Drag-n-drop
+                v-card-text.text-center(@click='selectEditor("ckeditor")')
+                  img(src='/svg/icon-open-in-browser.svg', alt='Visual Editor', style='width: 36px;')
+                  .body-2.mt-2.primary--text Visual Editor
+                  .caption.grey--text Rich-text WYSIWYG
             v-flex(xs4)
               v-card.radius-7.grey(
                 hover

+ 0 - 1
client/components/editor/editor-modal-properties.vue

@@ -144,7 +144,6 @@
                     :close-on-content-click='false'
                     v-model='isPublishEndShown'
                     :return-value.sync='publishEndDate'
-                    full-width
                     width='460px'
                     :disabled='!isPublished || true'
                     )

+ 4 - 4
client/components/editor/editor-modal-unsaved.vue

@@ -2,14 +2,14 @@
   v-dialog(v-model='isShown', max-width='550')
     v-card.wiki-form
       .dialog-header.is-short.is-red
-        v-icon.mr-2(color='white') warning
+        v-icon.mr-2(color='white') mdi-alert
         span {{$t('editor:unsaved.title')}}
-      v-card-text
+      v-card-text.pt-4
         .body-2 {{$t('editor:unsaved.body')}}
       v-card-chin
         v-spacer
-        v-btn(flat, @click='isShown = false') {{$t('common:actions.cancel')}}
-        v-btn(color='red', @click='discard', dark) {{$t('common:actions.discardChanges')}}
+        v-btn(text, @click='isShown = false') {{$t('common:actions.cancel')}}
+        v-btn.px-4(color='red', @click='discard', dark) {{$t('common:actions.discardChanges')}}
 </template>
 
 <script>

+ 1 - 0
client/scss/base/base.scss

@@ -1,6 +1,7 @@
 html {
   box-sizing: border-box;
   height: 100%;
+  overflow-y: auto !important;
 }
 *, *:before, *:after {
   box-sizing: inherit;

+ 1 - 0
client/store/editor.js

@@ -2,6 +2,7 @@ import { make } from 'vuex-pathify'
 
 const state = {
   editor: '',
+  editorKey: '',
   content: '',
   mode: 'create',
   activeModal: '',

+ 21 - 1
client/themes/default/scss/app.scss

@@ -32,7 +32,7 @@
       padding-right: 3px;
 
       &::after {
-        font-family: 'Material Design Icons';
+        font-family: 'Material Design Icons', sans-serif;
         font-size: 24px/1;
         padding-left: 3px;
         display: inline-block;
@@ -605,6 +605,26 @@
     }
   }
 
+  figure.image {
+    margin: 1rem 24px 0 24px;
+
+    img {
+      margin: 0 auto;
+    }
+    figcaption {
+      padding: 4px 1rem;
+      text-align: center;
+      font-size: 12px;
+      color: mc('grey', '700');
+      background-color: mc('grey', '100');
+
+      @at-root .theme--dark & {
+        color: mc('grey', '400');
+        background-color: mc('grey', '800');
+      }
+    }
+  }
+
   // ---------------------------------
   // DETAILS
   // ---------------------------------

+ 29 - 28
package.json

@@ -39,10 +39,10 @@
     "@exlinc/keycloak-passport": "1.0.2",
     "algoliasearch": "3.34.0",
     "apollo-fetch": "0.7.0",
-    "apollo-server": "2.9.2",
-    "apollo-server-express": "2.9.2",
+    "apollo-server": "2.9.3",
+    "apollo-server-express": "2.9.3",
     "auto-load": "3.0.4",
-    "aws-sdk": "2.521.0",
+    "aws-sdk": "2.524.0",
     "axios": "0.19.0",
     "azure-search-client": "3.1.5",
     "bcryptjs-then": "1.0.1",
@@ -60,7 +60,7 @@
     "custom-error-instance": "2.1.1",
     "dependency-graph": "0.8.0",
     "diff": "4.0.1",
-    "diff2html": "2.11.2",
+    "diff2html": "2.11.3",
     "dotize": "0.3.0",
     "elasticsearch6": "npm:@elastic/elasticsearch@6",
     "elasticsearch7": "npm:@elastic/elasticsearch@7",
@@ -68,7 +68,7 @@
     "express": "4.17.1",
     "express-brute": "1.0.1",
     "express-session": "1.16.2",
-    "file-type": "12.2.0",
+    "file-type": "12.3.0",
     "filesize": "4.1.2",
     "fs-extra": "8.1.0",
     "getos": "3.1.1",
@@ -79,7 +79,7 @@
     "graphql-tools": "4.0.5",
     "highlight.js": "9.15.10",
     "i18next": "17.0.13",
-    "i18next-express-middleware": "1.8.1",
+    "i18next-express-middleware": "1.8.2",
     "i18next-node-fs-backend": "2.1.3",
     "image-size": "0.7.4",
     "js-base64": "2.5.1",
@@ -167,10 +167,10 @@
     "yargs": "14.0.0"
   },
   "devDependencies": {
-    "@babel/cli": "^7.5.0",
-    "@babel/core": "^7.5.4",
+    "@babel/cli": "^7.6.0",
+    "@babel/core": "^7.6.0",
     "@babel/plugin-proposal-class-properties": "^7.5.0",
-    "@babel/plugin-proposal-decorators": "^7.4.4",
+    "@babel/plugin-proposal-decorators": "^7.6.0",
     "@babel/plugin-proposal-export-namespace-from": "^7.5.2",
     "@babel/plugin-proposal-function-sent": "^7.5.0",
     "@babel/plugin-proposal-json-strings": "^7.2.0",
@@ -178,21 +178,22 @@
     "@babel/plugin-proposal-throw-expressions": "^7.2.0",
     "@babel/plugin-syntax-dynamic-import": "^7.2.0",
     "@babel/plugin-syntax-import-meta": "^7.2.0",
-    "@babel/polyfill": "^7.4.4",
-    "@babel/preset-env": "^7.5.4",
-    "@mdi/font": "4.2.95",
+    "@babel/polyfill": "^7.6.0",
+    "@babel/preset-env": "^7.6.0",
+    "@mdi/font": "4.3.95",
     "@panter/vue-i18next": "0.15.1",
+    "@requarks/ckeditor5": "12.4.0-wiki.11",
     "@vue/babel-preset-app": "3.11.0",
     "animate-sass": "0.8.2",
     "animated-number-vue": "1.0.0",
     "apollo-cache-inmemory": "1.6.3",
     "apollo-client": "2.6.4",
-    "apollo-link": "1.2.12",
-    "apollo-link-batch-http": "1.2.12",
-    "apollo-link-error": "1.1.11",
-    "apollo-link-http": "1.5.15",
+    "apollo-link": "1.2.13",
+    "apollo-link-batch-http": "1.2.13",
+    "apollo-link-error": "1.1.12",
+    "apollo-link-http": "1.5.16",
     "apollo-link-persisted-queries": "0.2.2",
-    "apollo-link-ws": "1.0.18",
+    "apollo-link-ws": "1.0.19",
     "apollo-utilities": "1.3.2",
     "autoprefixer": "9.6.1",
     "babel-eslint": "10.0.3",
@@ -217,20 +218,20 @@
     "eslint-config-requarks": "1.0.7",
     "eslint-config-standard": "14.1.0",
     "eslint-plugin-import": "2.18.2",
-    "eslint-plugin-node": "9.2.0",
+    "eslint-plugin-node": "10.0.0",
     "eslint-plugin-promise": "4.2.1",
     "eslint-plugin-standard": "4.0.1",
     "eslint-plugin-vue": "5.2.3",
     "fibers": "4.0.1",
     "file-loader": "4.2.0",
-    "filepond": "4.5.1",
+    "filepond": "4.6.1",
     "filepond-plugin-file-validate-type": "1.2.4",
     "filesize.js": "1.0.2",
-    "grapesjs": "0.15.3",
+    "grapesjs": "0.15.5",
     "graphiql": "0.14.2",
     "graphql-persisted-document-loader": "1.0.1",
     "graphql-tag": "^2.10.1",
-    "graphql-voyager": "1.0.0-rc.27",
+    "graphql-voyager": "1.0.0-rc.28",
     "hammerjs": "2.0.8",
     "html-webpack-plugin": "4.0.0-beta.8",
     "html-webpack-pug-plugin": "2.0.0",
@@ -265,7 +266,7 @@
     "script-ext-html-webpack-plugin": "2.1.4",
     "simple-progress-webpack-plugin": "1.1.2",
     "style-loader": "1.0.0",
-    "terser": "4.2.1",
+    "terser": "4.3.0",
     "twemoji-awesome": "1.0.6",
     "url-loader": "2.1.0",
     "vee-validate": "2.2.15",
@@ -287,19 +288,19 @@
     "vue-tour": "1.1.0",
     "vue2-animate": "2.1.2",
     "vuedraggable": "2.23.0",
-    "vuescroll": "4.14.0",
-    "vuetify": "2.0.11",
+    "vuescroll": "4.14.3",
+    "vuetify": "2.0.15",
     "vuetify-loader": "1.3.0",
     "vuex": "3.1.1",
-    "vuex-pathify": "1.2.4",
+    "vuex-pathify": "1.4.0",
     "vuex-persistedstate": "2.5.4",
     "webpack": "4.39.3",
     "webpack-bundle-analyzer": "3.4.1",
-    "webpack-cli": "3.3.7",
-    "webpack-dev-middleware": "3.7.0",
+    "webpack-cli": "3.3.8",
+    "webpack-dev-middleware": "3.7.1",
     "webpack-hot-middleware": "2.25.0",
     "webpack-merge": "4.2.2",
-    "webpack-subresource-integrity": "1.3.2",
+    "webpack-subresource-integrity": "1.3.3",
     "webpackbar": "4.0.0",
     "whatwg-fetch": "3.0.0",
     "write-file-webpack-plugin": "4.5.1",

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

@@ -0,0 +1,6 @@
+key: ckeditor
+title: Visual Editor
+description: Rich-text WYSIWYG Editor
+contentType: html
+author: requarks.io
+props: {}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 338 - 196
yarn.lock


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно