feat: mk editor toolbar + help dialog + fixes

This commit is contained in:
Nick
2019-05-05 21:21:27 -04:00
parent ca4e0ada30
commit de1dddbc90
17 changed files with 418 additions and 131 deletions
+1 -1
View File
@@ -126,6 +126,6 @@ export default {
display: flex;
justify-content: flex-start;
align-items: center;
font-family: 'Source Sans Pro', sans-serif;
font-family: 'Roboto Mono', monospace;
}
</style>
+4 -4
View File
@@ -3,7 +3,7 @@
nav-header(dense)
template(slot='mid')
v-spacer
.subheading {{currentPageTitle}}
.subheading.grey--text {{currentPageTitle}}
v-spacer
template(slot='actions')
v-btn(
@@ -32,7 +32,7 @@
v-icon(color='red', :left='$vuetify.breakpoint.lgAndUp') close
span.white--text(v-if='$vuetify.breakpoint.lgAndUp') {{ $t('editor:close') }}
v-content
component(:is='currentEditor')
component(:is='currentEditor', :save='save')
editor-modal-properties(v-model='dialogProps')
editor-modal-editorselect(v-model='dialogEditorSelector')
editor-modal-unsaved(v-model='dialogUnsaved', @discard='exitGo')
@@ -199,7 +199,7 @@ export default {
resp = _.get(resp, 'data.pages.create', {})
if (_.get(resp, 'responseResult.succeeded')) {
this.$store.commit('showNotification', {
message: this.$t('editor:save.success'),
message: this.$t('editor:save.createSuccess'),
style: 'success',
icon: 'check'
})
@@ -234,7 +234,7 @@ export default {
resp = _.get(resp, 'data.pages.update', {})
if (_.get(resp, 'responseResult.succeeded')) {
this.$store.commit('showNotification', {
message: this.$t('editor:save.success'),
message: this.$t('editor:save.updateSuccess'),
style: 'success',
icon: 'check'
})
+72 -16
View File
@@ -124,8 +124,8 @@
v-icon crop_free
span Distraction Free Mode
v-tooltip(right, color='teal')
v-btn(icon, slot='activator', dark, disabled).mx-0
v-icon help
v-btn(icon, slot='activator', dark, @click='toggleHelp').mx-0
v-icon(:color='helpShown ? `teal` : ``') help
span Markdown Formatting Help
.editor-markdown-editor
.editor-markdown-editor-title(v-if='previewShown', @click='previewShown = false') Editor
@@ -141,11 +141,16 @@
.caption.px-3 /{{path}}
v-spacer
.caption Markdown
v-spacer
.caption Ln {{cursorPos.line + 1}}, Col {{cursorPos.ch + 1}}
markdown-help(v-if='helpShown')
</template>
<script>
import _ from 'lodash'
import { get, sync } from 'vuex-pathify'
import markdownHelp from './markdown/help.vue'
// ========================================
// IMPORTS
@@ -232,7 +237,13 @@ md.renderer.rules.heading_open = injectLineNumbers
export default {
components: {
codemirror
codemirror,
markdownHelp
},
props: {
save: {
type: Function
}
},
data() {
return {
@@ -251,8 +262,10 @@ export default {
},
viewportMargin: 50
},
cursorPos: { ch: 0, line: 1 },
previewShown: true,
previewHTML: ''
previewHTML: '',
helpShown: true
}
},
computed: {
@@ -281,13 +294,34 @@ export default {
}
}
_.set(keyBindings, `${CtrlKey}-S`, cm => {
self.$parent.save()
this.save()
return false
})
_.set(keyBindings, `${CtrlKey}-B`, cm => {
this.toggleMarkup({ start: `**` })
return false
})
_.set(keyBindings, `${CtrlKey}-I`, cm => {
this.toggleMarkup({ start: `*` })
return false
})
_.set(keyBindings, `${CtrlKey}-Alt-Right`, cm => {
let lvl = this.getHeaderLevel(cm)
if (lvl >= 6) { lvl = 5 }
this.setHeaderLine(lvl + 1)
return false
})
_.set(keyBindings, `${CtrlKey}-Alt-Left`, cm => {
let lvl = this.getHeaderLevel(cm)
if (lvl <= 1) { lvl = 2 }
this.setHeaderLine(lvl - 1)
return false
})
cm.setSize(null, 'calc(100vh - 112px - 24px)')
cm.setOption('extraKeys', keyBindings)
cm.on('cursorActivity', cm => {
this.toolbarSync(cm)
this.positionSync(cm)
this.scrollSync(cm)
})
this.onCmInput(this.code)
@@ -298,20 +332,19 @@ export default {
this.previewHTML = md.render(newContent)
this.$nextTick(() => {
Prism.highlightAllUnder(this.$refs.editorPreview)
Array.from(this.$refs.editorPreview.querySelectorAll('pre.line-numbers')).map(pre => pre.classList.add('prismjs'))
this.scrollSync(this.cm)
})
}, 500),
/**
* Update toolbar state
* Update cursor state
*/
toolbarSync(cm) {
// const pos = cm.getCursor('start')
// const token = cm.getTokenAt(pos)
// if (!token.type) { return }
// console.info(token)
positionSync(cm) {
this.cursorPos = cm.getCursor('head')
},
/**
* Wrap selection with start / end tags
*/
toggleMarkup({ start, end }) {
if (!end) { end = start }
if (!this.cm.doc.somethingSelected()) {
@@ -323,6 +356,9 @@ export default {
}
this.cm.doc.replaceSelections(this.cm.doc.getSelections().map(s => start + s + end))
},
/**
* Set current line as header
*/
setHeaderLine(lvl) {
const curLine = this.cm.doc.getCursor('head').line
let lineContent = this.cm.doc.getLine(curLine)
@@ -333,11 +369,31 @@ export default {
lineContent = _.times(lvl, n => '#').join('') + ` ` + lineContent
this.cm.doc.replaceRange(lineContent, { line: curLine, ch: 0 }, { line: curLine, ch: lineLength })
},
/**
* Get the header lever of the current line
*/
getHeaderLevel(cm) {
const curLine = this.cm.doc.getCursor('head').line
let lineContent = this.cm.doc.getLine(curLine)
let lvl = 0
const result = lineContent.match(/^(#+) /)
if (result) {
lvl = _.get(result, '[1]', '').length
}
return lvl
},
/**
* Insert content after current line
*/
insertAfter({ content, newLine }) {
const curLine = this.cm.doc.getCursor('to').line
const lineLength = this.cm.doc.getLine(curLine).length
this.cm.doc.replaceRange(newLine ? `\n${content}\n` : content, { line: curLine, ch: lineLength + 1 })
},
/**
* Insert content before current line
*/
insertBeforeEachLine({ content, after }) {
let lines = []
if (!this.cm.doc.somethingSelected()) {
@@ -381,8 +437,8 @@ export default {
}
}
}, 500),
toggleAround (before, after) {
toggleHelp () {
this.helpShown = !this.helpShown
},
toggleFullscreen () {
this.cm.setOption('fullScreen', true)
+335
View File
@@ -0,0 +1,335 @@
<template lang='pug'>
v-card.editor-markdown-help.animated.fadeInLeft(flat, tile)
v-container.pa-3(grid-list-lg, fluid)
v-layout(row, wrap)
v-flex(xs4)
v-card.radius-7.animated.fadeInUp(light)
v-card-text
.d-flex
v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
v-icon.mr-3(color='teal') info
.body-2.teal--text Markdown Reference
.body-2.mt-3 Bold
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div **Lorem ipsum**
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
.caption: strong Lorem ipsum
.body-2.mt-3 Italic
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div *Lorem ipsum*
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
.caption: em Lorem ipsum
.body-2.mt-3 Strikethrough
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div ~~Lorem ipsum~~
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
.caption(style='text-decoration: line-through;') Lorem ipsum
.body-2.mt-3 Headers
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div # Header 1
div ## Header 2
div ### Header 3
div #### Header 4
div ##### Header 5
div ###### Header 6
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
div(style='font-weight: 700; font-size: 24px;') Header 1
div(style='font-weight: 700; font-size: 22px;') Header 2
div(style='font-weight: 700; font-size: 20px;') Header 3
div(style='font-weight: 700; font-size: 18px;') Header 4
div(style='font-weight: 700; font-size: 16px;') Header 5
div(style='font-weight: 700; font-size: 14px;') Header 6
.body-2.mt-3 Unordered Lists
.caption.grey--text.text--darken-1: em You can also use the asterisk symbol instead of the dash.
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div - Unordered List Item 1
div - Unordered List Item 2
div - Unordered List Item 3
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
ul
li Unordered List Item 1
li Unordered List Item 2
li Unordered List Item 3
.body-2.mt-3 Ordered Lists
.caption.grey--text.text--darken-1: em Even though we prefix all lines with #[strong 1.], the output will be correctly numbered automatically.
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div 1. Ordered List Item 1
div 1. Ordered List Item 2
div 1. Ordered List Item 3
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
ol
li Unordered List Item 1
li Unordered List Item 2
li Unordered List Item 3
v-flex(xs4)
v-card.radius-7.animated.fadeInUp.wait-p1s(light)
v-card-text
.d-flex
v-toolbar.radius-7(color='teal lighten-5', dense, flat, height='44')
v-icon.mr-3(color='teal') info
.body-2.teal--text Markdown Reference (continued)
.body-2.mt-3 Superscript
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div Lorem ^ipsum^
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
.caption Lorem #[sup ipsum]
.body-2.mt-3 Subscript
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div Lorem ~ipsum~
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
.caption: em Lorem #[sub ipsum]
.body-2.mt-3 Horizontal Line
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div Lorem ipsum
div ---
div Dolor sit amet
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
.caption Lorem ipsum
v-divider.my-2
.caption Dolor sit amet
.body-2.mt-3 Inline Code
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div Lorem `ipsum dolor sit` amet
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
.caption Lorem #[code ipsum dolor sit] amet
.body-2.mt-3 Code Blocks
.caption.grey--text.text--darken-1: em In the example below, #[strong js] defines the syntax highlighting language to use. It can be omitted.
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div ```js
div function main () {
div.pl-3 echo 'Lorem ipsum'
div }
div ```
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text.contents
pre.prismjs.line-numbers.language-js
code.language-js
span.token.keyword function
span.token.function main
span.token.punctuation (
span.token.punctuation )
span.token.punctuation {#[br]
| echo
span.token.string 'Lorem ipsum'#[br]
span.token.punctuation }
span.line-numbers-rows(aria-hidden='true')
span
span
span
.body-2.mt-3 Blockquotes
v-layout(row)
v-flex(xs6)
v-card.editor-markdown-help-source(flat)
v-card-text
div &gt; Lorem ipsum
div &gt; dolor sit amet
div &gt; consectetur adipiscing elit
v-icon chevron_right
v-flex(xs6)
v-card.editor-markdown-help-result(flat)
v-card-text
blockquote(style='border: 1px solid #263238; border-radius: .5rem; padding: 1rem 24px;') Lorem ipsum#[br]dolor sit amet#[br]consectetur adipiscing elit
v-flex(xs4)
v-card.radius-7.animated.fadeInUp.wait-p2s(light)
v-card-text
v-toolbar.radius-7(color='teal lighten-5', dense, flat)
v-icon.mr-3(color='teal') keyboard
.body-2.teal--text Keyboard Shortcuts
v-list.editor-markdown-help-kbd(two-line, dense)
v-list-tile
v-list-tile-content.body-2 Bold
v-list-tile-action #[kbd {{ctrlKey}}] + #[kbd B]
v-divider
v-list-tile
v-list-tile-content.body-2 Italic
v-list-tile-action #[kbd {{ctrlKey}}] + #[kbd I]
v-divider
v-list-tile
v-list-tile-content.body-2 Increase Header Level
v-list-tile-action #[kbd {{ctrlKey}}] + #[kbd {{altKey}}] + #[kbd Right]
v-divider
v-list-tile
v-list-tile-content.body-2 Decrease Header Level
v-list-tile-action #[kbd {{ctrlKey}}] + #[kbd {{altKey}}] + #[kbd Left]
v-divider
v-list-tile
v-list-tile-content.body-2 Save
v-list-tile-action #[kbd {{ctrlKey}}] + #[kbd S]
v-divider
v-list-tile
v-list-tile-content.body-2 Undo
v-list-tile-action #[kbd {{ctrlKey}}] + #[kbd Z]
v-divider
v-list-tile
v-list-tile-content.body-2 Redo
v-list-tile-action #[kbd {{ctrlKey}}] + #[kbd Y]
v-divider
v-list-tile
v-list-tile-content
v-list-tile-title.body-2 Distraction Free Mode
v-list-tile-sub-title Press <kbd>Esc</kbd> to exit.
v-list-tile-action #[kbd F11]
v-card.radius-7.animated.fadeInUp.wait-p3s.mt-3(light)
v-card-text
v-toolbar.radius-7(color='teal lighten-5', dense, flat)
v-icon.mr-3(color='teal') mouse
.body-2.teal--text Multi-Selection
v-list.editor-markdown-help-kbd(two-line, dense)
v-list-tile
v-list-tile-content.body-2 Multiple Cursors
v-list-tile-action #[kbd {{ctrlKey}}] + Left Click
v-divider
v-list-tile
v-list-tile-content.body-2 Select Region
v-list-tile-action #[kbd {{ctrlKey}}] + #[kbd {{altKey}}] + Left Click
v-divider
v-list-tile
v-list-tile-content.body-2 Deselect
v-list-tile-action #[kbd Esc]
</template>
<script>
export default {
computed: {
ctrlKey() { return /Mac/.test(navigator.platform) ? 'Cmd' : 'Ctrl' },
altKey() { return /Mac/.test(navigator.platform) ? 'Option' : 'Alt' }
}
}
</script>
<style lang='scss'>
.editor-markdown-help {
position: fixed;
top: 112px;
left: 64px;
z-index: 10;
width: calc(100vw - 64px - 17px);
height: calc(100vh - 112px - 24px);
background-color: rgba(darken(mc('grey', '900'), 3%), .9) !important;
overflow: auto;
&-source {
background-color: mc('blue-grey', '900') !important;
border-radius: 7px;
font-family: 'Roboto Mono', monospace;
font-size: 14px;
color: #FFF !important;
}
&-result {
background-color: mc('blue-grey', '50') !important;
border-radius: 7px;
font-size: 14px;
code {
display: inline-block;
background-color: mc('pink', '50');
box-shadow: none;
font-size: inherit;
}
.contents {
padding-bottom: 16px;
}
.prismjs {
margin: 0;
}
}
&-kbd {
.v-list__tile__action {
flex-direction: row;
align-items: center;
kbd {
margin: 0 5px;
display: inline-block;
border: 1px solid #ccc;
border-radius: 4px;
padding: 0.1em 0.5em;
margin: 0 0.2em;
box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #fff inset;
background-color: #f7f7f7;
color: mc('grey', '700');
font-size: 12px;
}
}
}
&-ref {
.v-list__tile__action {
flex-direction: row;
align-items: center;
}
}
}
</style>
+4 -28
View File
@@ -22,14 +22,6 @@ @font-face {
font-style: normal;
}
@font-face {
font-family: 'Source Sans Pro';
src: url('/fonts/SourceSansPro-Bold.woff2') format('woff2'),
url('/fonts/SourceSansPro-Bold.woff') format('woff');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-Regular.woff2') format('woff2'),
@@ -46,22 +38,6 @@ @font-face {
font-style: italic;
}
@font-face {
font-family: 'Source Sans Pro';
src: url('/fonts/SourceSansPro-BoldItalic.woff2') format('woff2'),
url('/fonts/SourceSansPro-BoldItalic.woff') format('woff');
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: 'Source Sans Pro';
src: url('/fonts/SourceSansPro-Regular.woff2') format('woff2'),
url('/fonts/SourceSansPro-Regular.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Roboto';
src: url('/fonts/Roboto-Medium.woff2') format('woff2'),
@@ -71,9 +47,9 @@ @font-face {
}
@font-face {
font-family: 'Source Sans Pro';
src: url('/fonts/SourceSansPro-Italic.woff2') format('woff2'),
url('/fonts/SourceSansPro-Italic.woff') format('woff');
font-family: 'Roboto Mono';
src: url('/fonts/RobotoMono-Regular.woff2') format('woff2'),
url('/fonts/RobotoMono-Regular.woff') format('woff');
font-weight: normal;
font-style: italic;
font-style: normal;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-80
View File
@@ -1,80 +0,0 @@
@font-face {
font-family: 'Roboto';
src: url('Roboto-MediumItalic.woff2') format('woff2'),
url('Roboto-MediumItalic.woff') format('woff');
font-weight: 500;
font-style: italic;
}
@font-face {
font-family: 'Roboto';
src: url('Roboto-Italic.woff2') format('woff2'),
url('Roboto-Italic.woff') format('woff');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'Roboto';
src: url('Roboto-Bold.woff2') format('woff2'),
url('Roboto-Bold.woff') format('woff');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'Source Sans Pro';
src: url('SourceSansPro-Bold.woff2') format('woff2'),
url('SourceSansPro-Bold.woff') format('woff');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'Roboto';
src: url('Roboto-Regular.woff2') format('woff2'),
url('Roboto-Regular.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Roboto';
src: url('Roboto-BoldItalic.woff2') format('woff2'),
url('Roboto-BoldItalic.woff') format('woff');
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: 'Source Sans Pro';
src: url('SourceSansPro-BoldItalic.woff2') format('woff2'),
url('SourceSansPro-BoldItalic.woff') format('woff');
font-weight: bold;
font-style: italic;
}
@font-face {
font-family: 'Source Sans Pro';
src: url('SourceSansPro-Regular.woff2') format('woff2'),
url('SourceSansPro-Regular.woff') format('woff');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Roboto';
src: url('Roboto-Medium.woff2') format('woff2'),
url('Roboto-Medium.woff') format('woff');
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: 'Source Sans Pro';
src: url('SourceSansPro-Italic.woff2') format('woff2'),
url('SourceSansPro-Italic.woff') format('woff');
font-weight: normal;
font-style: italic;
}
+2 -2
View File
@@ -256,7 +256,7 @@ .contents {
background-color: mc('indigo', '50');
padding: 0 5px;
color: mc('indigo', '800');
font-family: 'Source Code Pro', monospace;
font-family: 'Roboto Mono', monospace;
font-weight: normal;
font-size: 1rem;
box-shadow: none;
@@ -281,7 +281,7 @@ .contents {
box-shadow: initial;
display: block;
font-size: .85rem;
font-family: 'Source Code Pro', monospace;
font-family: 'Roboto Mono', monospace;
&:after, &:before {
content: initial;