2
0
Эх сурвалжийг харах

feat: editor preview scroll sync + preview styling fixes

NGPixel 7 жил өмнө
parent
commit
a30b749bd0

+ 3 - 0
client/app.js

@@ -14,6 +14,7 @@ import { createApolloFetch } from 'apollo-fetch'
 import { BatchHttpLink } from 'apollo-link-batch-http'
 import { InMemoryCache } from 'apollo-cache-inmemory'
 import Vuetify from 'vuetify'
+import Velocity from 'velocity-animate'
 import Hammer from 'hammerjs'
 import store from './store'
 
@@ -95,6 +96,8 @@ Vue.use(VeeValidate, {
 })
 Vue.use(Vuetify)
 
+Vue.prototype.Velocity = Velocity
+
 // ====================================
 // Register Vue Components
 // ====================================

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

@@ -141,6 +141,21 @@ const md = new MarkdownIt({
   .use(mdMark)
   .use(mdImsize)
 
+// 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
+
 export default {
   components: {
     codemirror
@@ -192,13 +207,34 @@ export default {
           self.$parent.save()
         }
       })
+      cm.on('cursorActivity', this.scrollSync)
       this.onCmInput(this.code)
     },
     onCmInput: _.debounce(function (newContent) {
+      linesMap = []
       this.previewHTML = md.render(newContent)
-      this.$nextTick(function() {
+      this.$nextTick(() => {
         Prism.highlightAllUnder(this.$refs.editorPreview)
+        this.scrollSync(this.cm)
       })
+    }, 500),
+    /**
+     * Update scroll sync
+     */
+    scrollSync: _.debounce(function (cm) {
+      if (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)
   }
 }
@@ -212,9 +248,10 @@ export default {
   }
 
   &-editor {
+    background-color: darken(mc('grey', '900'), 4.5%);
     flex: 1 1 50%;
     display: block;
-    min-height: calc(100vh - 100px);
+    height: calc(100vh - 100px);
     position: relative;
 
     &-title {
@@ -229,7 +266,7 @@ export default {
       position: absolute;
       top: 0;
       right: 0;
-      z-index: 2;
+      z-index: 7;
       text-transform: uppercase;
       font-size: .7rem;
 
@@ -243,14 +280,28 @@ export default {
     flex: 1 1 50%;
     background-color: mc('grey', '100');
     position: relative;
-    padding: 30px 1rem 1rem 1rem;
+    height: calc(100vh - 100px);
+    overflow: hidden;
 
     @include until($tablet) {
       display: none;
     }
 
+    &-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: mc('blue', '100');
+      background-color: rgba(mc('blue', '100'), .75);
       border-bottom-right-radius: 5px;
       display: inline-flex;
       height: 30px;
@@ -318,6 +369,11 @@ export default {
       }
     }
   }
+
+  // Fix FAB revealing under codemirror
+  .speed-dial--fixed {
+    z-index: 5;
+  }
 }
 
 .CodeMirror {

+ 25 - 1
client/scss/components/markdown-content.scss

@@ -8,7 +8,7 @@
 
   h1, h2, h3, h4, h5, h6 {
     color: mc('blue-grey', '700');
-    font-weight: 600;
+    font-weight: 500;
   }
 
   > * + h1, > * + h2, > * + h3, > * + h4 {
@@ -28,6 +28,7 @@
     font-size: 1.15rem;
     border-bottom: 1px dotted mc('blue-grey', '100');
     margin-bottom: .5rem;
+    color: mc('blue-grey', '500');
   }
   h4 {
     font-size: 1.1rem;
@@ -39,10 +40,32 @@
     font-size: 1.025rem;
   }
 
+  // --------------------------------------------
+  // Paragraphs
+  // --------------------------------------------
   p + p {
     margin-top: 1rem;
   }
 
+  // --------------------------------------------
+  // Lists
+  // --------------------------------------------
+  ul, ol {
+    & + p {
+      margin-top: .5rem;
+    }
+  }
+
+  ul {
+    list-style-type: square;
+    list-style-position: inside;
+  }
+
+  ol {
+    list-style-type: decimal;
+    list-style-position: inside;
+  }
+
   // --------------------------------------------
   // Code Blocks
   // --------------------------------------------
@@ -57,6 +80,7 @@
       box-shadow: initial;
       display: block;
       font-size: .85rem;
+      font-family: 'Source Code Pro', monospace;
 
       &:after, &:before {
         content: initial;

+ 1 - 0
package.json

@@ -195,6 +195,7 @@
     "twemoji-awesome": "1.0.6",
     "uglifyjs-webpack-plugin": "1.2.0",
     "vee-validate": "2.0.4",
+    "velocity-animate": "1.5.1",
     "vue": "2.5.13",
     "vue-clipboards": "1.2.1",
     "vue-codemirror": "4.0.3",

+ 1 - 1
server/views/master.pug

@@ -23,7 +23,7 @@ html
 
     //- CSS
     link(type='text/css', rel='stylesheet', href=config.site.path + 'css/bundle.css')
-    link(type='text/css', rel='stylesheet', href='https://fonts.googleapis.com/icon?family=Material+Icons')
+    link(type='text/css', rel='stylesheet', href='https://fonts.googleapis.com/icon?family=Roboto:400,500,700|Source+Code+Pro:400,700|Material+Icons')
 
     //- JS
     script(type='text/javascript', src=config.site.path + 'js/runtime.js')

+ 4 - 0
yarn.lock

@@ -10322,6 +10322,10 @@ vee-validate@2.0.4:
   dependencies:
     babel-plugin-transform-object-rest-spread "^6.26.0"
 
+velocity-animate@1.5.1:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/velocity-animate/-/velocity-animate-1.5.1.tgz#606837047bab8fbfb59a636d1d82ecc3f7bd71a6"
+
 vendors@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"