feat: editor preview scroll sync + preview styling fixes
This commit is contained in:
		| @@ -14,6 +14,7 @@ import { createApolloFetch } from 'apollo-fetch' | |||||||
| import { BatchHttpLink } from 'apollo-link-batch-http' | import { BatchHttpLink } from 'apollo-link-batch-http' | ||||||
| import { InMemoryCache } from 'apollo-cache-inmemory' | import { InMemoryCache } from 'apollo-cache-inmemory' | ||||||
| import Vuetify from 'vuetify' | import Vuetify from 'vuetify' | ||||||
|  | import Velocity from 'velocity-animate' | ||||||
| import Hammer from 'hammerjs' | import Hammer from 'hammerjs' | ||||||
| import store from './store' | import store from './store' | ||||||
|  |  | ||||||
| @@ -95,6 +96,8 @@ Vue.use(VeeValidate, { | |||||||
| }) | }) | ||||||
| Vue.use(Vuetify) | Vue.use(Vuetify) | ||||||
|  |  | ||||||
|  | Vue.prototype.Velocity = Velocity | ||||||
|  |  | ||||||
| // ==================================== | // ==================================== | ||||||
| // Register Vue Components | // Register Vue Components | ||||||
| // ==================================== | // ==================================== | ||||||
|   | |||||||
| @@ -141,6 +141,21 @@ const md = new MarkdownIt({ | |||||||
|   .use(mdMark) |   .use(mdMark) | ||||||
|   .use(mdImsize) |   .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 { | export default { | ||||||
|   components: { |   components: { | ||||||
|     codemirror |     codemirror | ||||||
| @@ -192,13 +207,34 @@ export default { | |||||||
|           self.$parent.save() |           self.$parent.save() | ||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
|  |       cm.on('cursorActivity', this.scrollSync) | ||||||
|       this.onCmInput(this.code) |       this.onCmInput(this.code) | ||||||
|     }, |     }, | ||||||
|     onCmInput: _.debounce(function (newContent) { |     onCmInput: _.debounce(function (newContent) { | ||||||
|  |       linesMap = [] | ||||||
|       this.previewHTML = md.render(newContent) |       this.previewHTML = md.render(newContent) | ||||||
|       this.$nextTick(function() { |       this.$nextTick(() => { | ||||||
|         Prism.highlightAllUnder(this.$refs.editorPreview) |         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) |     }, 500) | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -212,9 +248,10 @@ export default { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   &-editor { |   &-editor { | ||||||
|  |     background-color: darken(mc('grey', '900'), 4.5%); | ||||||
|     flex: 1 1 50%; |     flex: 1 1 50%; | ||||||
|     display: block; |     display: block; | ||||||
|     min-height: calc(100vh - 100px); |     height: calc(100vh - 100px); | ||||||
|     position: relative; |     position: relative; | ||||||
|  |  | ||||||
|     &-title { |     &-title { | ||||||
| @@ -229,7 +266,7 @@ export default { | |||||||
|       position: absolute; |       position: absolute; | ||||||
|       top: 0; |       top: 0; | ||||||
|       right: 0; |       right: 0; | ||||||
|       z-index: 2; |       z-index: 7; | ||||||
|       text-transform: uppercase; |       text-transform: uppercase; | ||||||
|       font-size: .7rem; |       font-size: .7rem; | ||||||
|  |  | ||||||
| @@ -243,14 +280,28 @@ export default { | |||||||
|     flex: 1 1 50%; |     flex: 1 1 50%; | ||||||
|     background-color: mc('grey', '100'); |     background-color: mc('grey', '100'); | ||||||
|     position: relative; |     position: relative; | ||||||
|     padding: 30px 1rem 1rem 1rem; |     height: calc(100vh - 100px); | ||||||
|  |     overflow: hidden; | ||||||
|  |  | ||||||
|     @include until($tablet) { |     @include until($tablet) { | ||||||
|       display: none; |       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 { |     &-title { | ||||||
|       background-color: mc('blue', '100'); |       background-color: rgba(mc('blue', '100'), .75); | ||||||
|       border-bottom-right-radius: 5px; |       border-bottom-right-radius: 5px; | ||||||
|       display: inline-flex; |       display: inline-flex; | ||||||
|       height: 30px; |       height: 30px; | ||||||
| @@ -318,6 +369,11 @@ export default { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // Fix FAB revealing under codemirror | ||||||
|  |   .speed-dial--fixed { | ||||||
|  |     z-index: 5; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| .CodeMirror { | .CodeMirror { | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ | |||||||
|  |  | ||||||
|   h1, h2, h3, h4, h5, h6 { |   h1, h2, h3, h4, h5, h6 { | ||||||
|     color: mc('blue-grey', '700'); |     color: mc('blue-grey', '700'); | ||||||
|     font-weight: 600; |     font-weight: 500; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   > * + h1, > * + h2, > * + h3, > * + h4 { |   > * + h1, > * + h2, > * + h3, > * + h4 { | ||||||
| @@ -28,6 +28,7 @@ | |||||||
|     font-size: 1.15rem; |     font-size: 1.15rem; | ||||||
|     border-bottom: 1px dotted mc('blue-grey', '100'); |     border-bottom: 1px dotted mc('blue-grey', '100'); | ||||||
|     margin-bottom: .5rem; |     margin-bottom: .5rem; | ||||||
|  |     color: mc('blue-grey', '500'); | ||||||
|   } |   } | ||||||
|   h4 { |   h4 { | ||||||
|     font-size: 1.1rem; |     font-size: 1.1rem; | ||||||
| @@ -39,10 +40,32 @@ | |||||||
|     font-size: 1.025rem; |     font-size: 1.025rem; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   // -------------------------------------------- | ||||||
|  |   // Paragraphs | ||||||
|  |   // -------------------------------------------- | ||||||
|   p + p { |   p + p { | ||||||
|     margin-top: 1rem; |     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 |   // Code Blocks | ||||||
|   // -------------------------------------------- |   // -------------------------------------------- | ||||||
| @@ -57,6 +80,7 @@ | |||||||
|       box-shadow: initial; |       box-shadow: initial; | ||||||
|       display: block; |       display: block; | ||||||
|       font-size: .85rem; |       font-size: .85rem; | ||||||
|  |       font-family: 'Source Code Pro', monospace; | ||||||
|  |  | ||||||
|       &:after, &:before { |       &:after, &:before { | ||||||
|         content: initial; |         content: initial; | ||||||
|   | |||||||
| @@ -195,6 +195,7 @@ | |||||||
|     "twemoji-awesome": "1.0.6", |     "twemoji-awesome": "1.0.6", | ||||||
|     "uglifyjs-webpack-plugin": "1.2.0", |     "uglifyjs-webpack-plugin": "1.2.0", | ||||||
|     "vee-validate": "2.0.4", |     "vee-validate": "2.0.4", | ||||||
|  |     "velocity-animate": "1.5.1", | ||||||
|     "vue": "2.5.13", |     "vue": "2.5.13", | ||||||
|     "vue-clipboards": "1.2.1", |     "vue-clipboards": "1.2.1", | ||||||
|     "vue-codemirror": "4.0.3", |     "vue-codemirror": "4.0.3", | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ html | |||||||
|  |  | ||||||
|     //- CSS |     //- CSS | ||||||
|     link(type='text/css', rel='stylesheet', href=config.site.path + 'css/bundle.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 |     //- JS | ||||||
|     script(type='text/javascript', src=config.site.path + 'js/runtime.js') |     script(type='text/javascript', src=config.site.path + 'js/runtime.js') | ||||||
|   | |||||||
| @@ -10322,6 +10322,10 @@ vee-validate@2.0.4: | |||||||
|   dependencies: |   dependencies: | ||||||
|     babel-plugin-transform-object-rest-spread "^6.26.0" |     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: | vendors@^1.0.0: | ||||||
|   version "1.0.1" |   version "1.0.1" | ||||||
|   resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" |   resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user