feat: editor preview scroll sync + preview styling fixes

This commit is contained in:
NGPixel 2018-02-25 15:54:35 -05:00
parent 6eb290781d
commit a30b749bd0
6 changed files with 95 additions and 7 deletions

View File

@ -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
// ==================================== // ====================================

View File

@ -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 {

View File

@ -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;

View File

@ -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",

View File

@ -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')

View File

@ -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"