feat: markdown editor UI fix + insert media (wip)

pull/795/head
Nick 6 years ago
parent efe8322d33
commit 342747c860

@ -57,72 +57,74 @@
v-list-tile-content Images & Files v-list-tile-content Images & Files
v-toolbar-title(:class='{ "ml-2": $vuetify.breakpoint.mdAndUp, "ml-0": $vuetify.breakpoint.smAndDown }') v-toolbar-title(:class='{ "ml-2": $vuetify.breakpoint.mdAndUp, "ml-0": $vuetify.breakpoint.smAndDown }')
span.subheading {{title}} span.subheading {{title}}
v-flex(md4, v-if='searchIsShown && $vuetify.breakpoint.mdAndUp') v-flex(md4, v-if='$vuetify.breakpoint.mdAndUp')
v-toolbar.nav-header-inner(color='black', dark, flat) v-toolbar.nav-header-inner(color='black', dark, flat)
transition(name='navHeaderSearch') slot(name='mid')
v-text-field( transition(name='navHeaderSearch', v-if='searchIsShown')
ref='searchField', v-text-field(
v-if='searchIsShown && $vuetify.breakpoint.mdAndUp', ref='searchField',
v-model='search', v-if='searchIsShown && $vuetify.breakpoint.mdAndUp',
color='white', v-model='search',
label='Search...', color='white',
single-line, label='Search...',
solo single-line,
flat solo
hide-details, flat
prepend-inner-icon='search', hide-details,
:loading='searchIsLoading', prepend-inner-icon='search',
@keyup.enter='searchEnter' :loading='searchIsLoading',
@keyup.esc='searchClose' @keyup.enter='searchEnter'
@focus='searchFocus' @keyup.esc='searchClose'
@blur='searchBlur' @focus='searchFocus'
@keyup.down='searchMove(`down`)' @blur='searchBlur'
@keyup.up='searchMove(`up`)' @keyup.down='searchMove(`down`)'
) @keyup.up='searchMove(`up`)'
v-progress-linear(
indeterminate,
slot='progress',
height='2',
color='blue'
) )
v-menu( v-progress-linear(
v-model='searchAdvMenuShown' indeterminate,
left slot='progress',
offset-y height='2',
min-width='450' color='blue'
:close-on-content-click='false'
nudge-bottom='7'
nudge-right='5'
)
v-btn.nav-header-search-adv(icon, outline, color='grey darken-2', slot='activator')
v-icon(color='white') expand_more
v-card.radius-0(dark)
v-toolbar(flat, color='grey darken-4', dense)
v-icon.mr-2 search
v-subheader.pl-0 Advanced Search
v-spacer
v-chip(label, small, color='primary') Coming soon
v-card-text.pa-4
v-checkbox.mt-0(
label='Restrict to current language'
color='white'
v-model='searchRestrictLocale'
hide-details
) )
v-checkbox( v-menu(
label='Search below current path only' v-model='searchAdvMenuShown'
color='white' left
v-model='searchRestrictPath' offset-y
hide-details min-width='450'
) :close-on-content-click='false'
v-divider nudge-bottom='7'
v-card-actions.grey.darken-3-d4 nudge-right='5'
v-btn(depressed, color='grey darken-3', block) v-if='searchIsShown'
v-icon(left) chevron_right )
span Save as defaults v-btn.nav-header-search-adv(icon, outline, color='grey darken-2', slot='activator')
v-btn(depressed, color='grey darken-3', block) v-icon(color='white') expand_more
v-icon(left) cached v-card.radius-0(dark)
span Reset v-toolbar(flat, color='grey darken-4', dense)
v-icon.mr-2 search
v-subheader.pl-0 Advanced Search
v-spacer
v-chip(label, small, color='primary') Coming soon
v-card-text.pa-4
v-checkbox.mt-0(
label='Restrict to current language'
color='white'
v-model='searchRestrictLocale'
hide-details
)
v-checkbox(
label='Search below current path only'
color='white'
v-model='searchRestrictPath'
hide-details
)
v-divider
v-card-actions.grey.darken-3-d4
v-btn(depressed, color='grey darken-3', block)
v-icon(left) chevron_right
span Save as defaults
v-btn(depressed, color='grey darken-3', block)
v-icon(left) cached
span Reset
v-flex(xs6, :md4='searchIsShown', :md6='!searchIsShown') v-flex(xs6, :md4='searchIsShown', :md6='!searchIsShown')
v-toolbar.nav-header-inner(color='black', dark, flat) v-toolbar.nav-header-inner(color='black', dark, flat)
v-spacer v-spacer

@ -1,6 +1,10 @@
<template lang="pug"> <template lang="pug">
v-app.editor(:dark='darkMode') v-app.editor(:dark='darkMode')
nav-header(dense) nav-header(dense)
template(slot='mid')
v-spacer
.subheading {{currentPageTitle}}
v-spacer
template(slot='actions') template(slot='actions')
v-btn( v-btn(
outline outline
@ -32,6 +36,7 @@
editor-modal-properties(v-model='dialogProps') editor-modal-properties(v-model='dialogProps')
editor-modal-editorselect(v-model='dialogEditorSelector') editor-modal-editorselect(v-model='dialogEditorSelector')
editor-modal-unsaved(v-model='dialogUnsaved', @discard='exitGo') editor-modal-unsaved(v-model='dialogUnsaved', @discard='exitGo')
component(:is='activeModal')
loader(v-model='dialogProgress', :title='$t(`editor:save.processing`)', :subtitle='$t(`editor:save.pleaseWait`)') loader(v-model='dialogProgress', :title='$t(`editor:save.processing`)', :subtitle='$t(`editor:save.pleaseWait`)')
v-snackbar( v-snackbar(
@ -70,7 +75,8 @@ export default {
editorWysiwyg: () => import(/* webpackChunkName: "editor-wysiwyg", webpackMode: "lazy" */ './editor/editor-wysiwyg.vue'), editorWysiwyg: () => import(/* webpackChunkName: "editor-wysiwyg", webpackMode: "lazy" */ './editor/editor-wysiwyg.vue'),
editorModalEditorselect: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-editorselect.vue'), editorModalEditorselect: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-editorselect.vue'),
editorModalProperties: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-properties.vue'), editorModalProperties: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-properties.vue'),
editorModalUnsaved: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-unsaved.vue') editorModalUnsaved: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-unsaved.vue'),
editorModalMedia: () => import(/* webpackChunkName: "editor", webpackMode: "eager" */ './editor/editor-modal-media.vue')
}, },
props: { props: {
locale: { locale: {
@ -126,10 +132,12 @@ export default {
computed: { computed: {
currentEditor: sync('editor/editor'), currentEditor: sync('editor/editor'),
darkMode: get('site/dark'), darkMode: get('site/dark'),
activeModal: sync('editor/activeModal'),
mode: get('editor/mode'), mode: get('editor/mode'),
notification: get('notification'), notification: get('notification'),
notificationState: sync('notification@isActive'), notificationState: sync('notification@isActive'),
welcomeMode() { return this.mode === `create` && this.path === `home` } welcomeMode() { return this.mode === `create` && this.path === `home` },
currentPageTitle: get('page/title')
}, },
watch: { watch: {
currentEditor(newValue, oldValue) { currentEditor(newValue, oldValue) {

@ -1,6 +1,6 @@
<template lang='pug'> <template lang='pug'>
.editor-markdown .editor-markdown
v-toolbar.editor-markdown-toolbar(dense, color='primary', dark) v-toolbar.editor-markdown-toolbar(dense, color='primary', dark, flat)
v-tooltip(top) v-tooltip(top)
v-btn(icon, slot='activator').mx-0 v-btn(icon, slot='activator').mx-0
v-icon format_bold v-icon format_bold
@ -49,8 +49,33 @@
v-btn(icon, slot='activator').mx-0 v-btn(icon, slot='activator').mx-0
v-icon remove v-icon remove
span Horizontal Bar span Horizontal Bar
.editor-markdown-main .editor-markdown-main
.editor-markdown-sidebar
v-tooltip(right)
v-btn(icon, slot='activator', dark, @click='toggleModal(`editorModalMedia`)').mx-0
v-icon(:color='activeModal === `editorModalMedia` ? `teal` : ``') image
span Insert Media
v-tooltip(right)
v-btn(icon, slot='activator', dark).mx-0
v-icon insert_drive_file
span Insert File
v-tooltip(right)
v-btn(icon, slot='activator', dark).mx-0
v-icon play_circle_outline
span Insert Video
v-tooltip(right)
v-btn(icon, slot='activator', dark).mx-0
v-icon multiline_chart
span Insert Diagram
v-tooltip(right)
v-btn(icon, slot='activator', dark).mx-0
v-icon functions
span Insert Math Expression
v-spacer
v-tooltip(right)
v-btn(icon, slot='activator', dark).mx-0
v-icon border_outer
span Table Helper
.editor-markdown-editor .editor-markdown-editor
.editor-markdown-editor-title(v-if='previewShown', @click='previewShown = false') Editor .editor-markdown-editor-title(v-if='previewShown', @click='previewShown = false') Editor
.editor-markdown-editor-title(v-else='previewShown', @click='previewShown = true'): v-icon(dark) drag_indicator .editor-markdown-editor-title(v-else='previewShown', @click='previewShown = true'): v-icon(dark) drag_indicator
@ -60,19 +85,16 @@
.editor-markdown-preview-title(@click='previewShown = false') Preview .editor-markdown-preview-title(@click='previewShown = false') Preview
.editor-markdown-preview-content.contents(ref='editorPreview', v-html='previewHTML') .editor-markdown-preview-content.contents(ref='editorPreview', v-html='previewHTML')
//- v-speed-dial.editor-markdown-insert(v-model='fabInsertMenu', :open-on-hover='true', direction='top', transition='slide-y-reverse-transition', fixed, bottom, :right='!previewShown || $vuetify.breakpoint.smAndDown') v-system-bar.editor-markdown-sysbar(dark, status, color='grey darken-3')
//- v-btn(color='blue', fab, dark, v-model='fabInsertMenu', slot='activator') .caption.editor-markdown-sysbar-locale {{locale.toUpperCase()}}
//- v-icon add_circle .caption.px-3 /{{path}}
//- v-icon close v-spacer
//- v-btn(color='teal', fab, dark): v-icon image .caption Markdown
//- v-btn(color='pink', fab, dark): v-icon insert_drive_file
//- v-btn(color='red', fab, dark): v-icon play_circle_outline
//- v-btn(color='purple', fab, dark): v-icon multiline_chart
//- v-btn(color='indigo', fab, dark): v-icon functions
</template> </template>
<script> <script>
import _ from 'lodash' import _ from 'lodash'
import { get, sync } from 'vuex-pathify'
// ======================================== // ========================================
// IMPORTS // IMPORTS
@ -191,9 +213,15 @@ export default {
}, },
isMobile() { isMobile() {
return this.$vuetify.breakpoint.smAndDown return this.$vuetify.breakpoint.smAndDown
} },
locale: get('page/locale'),
path: get('page/path'),
activeModal: sync('editor/activeModal')
}, },
methods: { methods: {
toggleModal(key) {
this.activeModal = (this.activeModal === key) ? '' : key
},
onCmReady(cm) { onCmReady(cm) {
let self = this let self = this
const keyBindings = { const keyBindings = {
@ -208,7 +236,7 @@ export default {
self.$parent.save() self.$parent.save()
}) })
cm.setSize(null, 'calc(100vh - 112px)') cm.setSize(null, 'calc(100vh - 112px - 24px)')
cm.setOption('extraKeys', keyBindings) cm.setOption('extraKeys', keyBindings)
cm.on('cursorActivity', cm => { cm.on('cursorActivity', cm => {
this.toolbarSync(cm) this.toolbarSync(cm)
@ -262,6 +290,9 @@ export default {
</script> </script>
<style lang='scss'> <style lang='scss'>
$editor-height: calc(100vh - 112px - 24px);
.editor-markdown { .editor-markdown {
&-main { &-main {
display: flex; display: flex;
@ -272,7 +303,7 @@ export default {
background-color: darken(mc('grey', '900'), 4.5%); background-color: darken(mc('grey', '900'), 4.5%);
flex: 1 1 50%; flex: 1 1 50%;
display: block; display: block;
height: calc(100vh - 112px); height: $editor-height;
position: relative; position: relative;
&-title { &-title {
@ -302,7 +333,7 @@ export default {
flex: 1 1 50%; flex: 1 1 50%;
background-color: mc('grey', '100'); background-color: mc('grey', '100');
position: relative; position: relative;
height: calc(100vh - 112px); height: $editor-height;
overflow: hidden; overflow: hidden;
@at-root .theme--dark & { @at-root .theme--dark & {
@ -327,7 +358,7 @@ export default {
} }
&-content { &-content {
height: calc(100vh - 112px); height: $editor-height;
overflow-y: scroll; overflow-y: scroll;
padding: 1rem 1rem 1rem 0; padding: 1rem 1rem 1rem 0;
width: calc(100% + 1rem + 17px) width: calc(100% + 1rem + 17px)
@ -364,7 +395,7 @@ export default {
color: #FFF; color: #FFF;
.v-toolbar__content { .v-toolbar__content {
padding-left: 16px; padding-left: 78px;
@include until($tablet) { @include until($tablet) {
padding-left: 8px; padding-left: 8px;
@ -379,6 +410,30 @@ export default {
} }
} }
&-sidebar {
background-color: mc('grey', '900');
width: 64px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
padding: 24px 0;
}
&-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;
}
}
// ========================================== // ==========================================
// Fix FAB revealing under codemirror // Fix FAB revealing under codemirror
// ========================================== // ==========================================

@ -0,0 +1,69 @@
<template lang='pug'>
v-card.editor-modal-media.animated.fadeInLeft(flat, tile)
v-container.pa-3(grid-list-lg, fluid)
v-layout(row, wrap)
v-flex(xs3)
v-card.radius-7.animated.fadeInLeft.wait-p1s(light)
v-card-text
file-pond(
name='mediaUpload'
ref='pond'
label-idle='Drop files here...'
allow-multiple='true'
accepted-file-types='image/jpeg, image/png'
:files='files'
)
v-flex(xs9)
v-card.radius-7.animated.fadeInLeft.wait-p3s(light)
v-card-text Beep boop
</template>
<script>
// import _ from 'lodash'
// import { sync } from 'vuex-pathify'
import vueFilePond from 'vue-filepond'
import 'filepond/dist/filepond.min.css'
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css'
import FilePondPluginFileValidateType from 'filepond-plugin-file-validate-type'
import FilePondPluginImagePreview from 'filepond-plugin-image-preview'
const FilePond = vueFilePond(FilePondPluginFileValidateType, FilePondPluginImagePreview)
export default {
components: {
FilePond
},
props: {
value: {
type: Boolean,
default: false
}
},
data() {
return {
files: []
}
},
computed: {
isShown: {
get() { return this.value },
set(val) { this.$emit('input', val) }
}
},
methods: {
}
}
</script>
<style lang='scss'>
.editor-modal-media {
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;
}
</style>

@ -3,7 +3,8 @@ import { make } from 'vuex-pathify'
const state = { const state = {
editor: '', editor: '',
content: '', content: '',
mode: 'create' mode: 'create',
activeModal: ''
} }
export default { export default {

@ -216,6 +216,9 @@
"eslint-plugin-standard": "4.0.0", "eslint-plugin-standard": "4.0.0",
"eslint-plugin-vue": "5.1.0", "eslint-plugin-vue": "5.1.0",
"file-loader": "3.0.1", "file-loader": "3.0.1",
"filepond": "4.2.0",
"filepond-plugin-file-validate-type": "1.2.2",
"filepond-plugin-image-preview": "4.0.3",
"filesize.js": "1.0.2", "filesize.js": "1.0.2",
"grapesjs": "0.14.52", "grapesjs": "0.14.52",
"graphiql": "0.12.0", "graphiql": "0.12.0",
@ -264,6 +267,7 @@
"vue-chartjs": "3.4.0", "vue-chartjs": "3.4.0",
"vue-clipboards": "1.2.4", "vue-clipboards": "1.2.4",
"vue-codemirror": "4.0.6", "vue-codemirror": "4.0.6",
"vue-filepond": "5.0.0",
"vue-hot-reload-api": "2.3.1", "vue-hot-reload-api": "2.3.1",
"vue-loader": "15.6.2", "vue-loader": "15.6.2",
"vue-material-design-icons": "3.0.0", "vue-material-design-icons": "3.0.0",

@ -5110,6 +5110,21 @@ file-uri-to-path@1:
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
filepond-plugin-file-validate-type@1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/filepond-plugin-file-validate-type/-/filepond-plugin-file-validate-type-1.2.2.tgz#8ac215b893b6beef28d82874cddcff6cd3069451"
integrity sha512-ZBVU9a18EWhfk3kkqULp7GX3KcSSAlZbLFqlquJ5VTerecL1BkUlQ/cpFJ5EucAdJ1Qdi1zEyPQ+e7JLxL+5Rg==
filepond-plugin-image-preview@4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/filepond-plugin-image-preview/-/filepond-plugin-image-preview-4.0.3.tgz#9e8a6561455751498da29e4a6501ab828dc0d4a2"
integrity sha512-umWUAM9Jd2W66/bcT/KWxBh1mmcSJxYi/L0aSxJSRnEebOGtJlNZ3y9nnB6MMNmt+Ybi9ttto6mmc4OPyfbfuQ==
filepond@4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/filepond/-/filepond-4.2.0.tgz#93852e933cc8acd9a60efeb5ef93fbbff15d1fef"
integrity sha512-JTSvxTQGbCXMZGoPOIjCKImv+Al3Y5z3f6gRoUGlQdqpnMHdnwOV0WG3hRCVBDN64ctAN3pgKtofkWfsnwwoTA==
filesize.js@1.0.2: filesize.js@1.0.2:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/filesize.js/-/filesize.js-1.0.2.tgz#934c013395a71804875cf11e6f1ffe211c3f2192" resolved "https://registry.yarnpkg.com/filesize.js/-/filesize.js-1.0.2.tgz#934c013395a71804875cf11e6f1ffe211c3f2192"
@ -13158,6 +13173,11 @@ vue-eslint-parser@^4.0.2:
esquery "^1.0.1" esquery "^1.0.1"
lodash "^4.17.11" lodash "^4.17.11"
vue-filepond@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/vue-filepond/-/vue-filepond-5.0.0.tgz#c3d9362fc41ee9dcb44f7962c1d41ff8e29e8cd2"
integrity sha512-8ka3ms8WU0vNQKSL1jgF24tCQUw+pA0CLgRRHe9AZ3l1yrehSXGEZZBQbC374PgzFW8CkJOr3cpM+7ZBNSVBvQ==
vue-hot-reload-api@2.3.1, vue-hot-reload-api@^2.3.0: vue-hot-reload-api@2.3.1, vue-hot-reload-api@^2.3.0:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.1.tgz#b2d3d95402a811602380783ea4f566eb875569a2" resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.1.tgz#b2d3d95402a811602380783ea4f566eb875569a2"

Loading…
Cancel
Save