Few fixes
This commit is contained in:
parent
8f743dd1b5
commit
c07fc7135e
@ -20,93 +20,5 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import 'common/variables.scss';
|
||||
@import 'common/markdownHighlighting';
|
||||
@import 'common/prism';
|
||||
@import 'common/flex';
|
||||
@import 'common/base';
|
||||
|
||||
body {
|
||||
background-color: #f3f3f3;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
tab-size: 4;
|
||||
text-rendering: auto;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
* {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.text-input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
padding: 3px 12px;
|
||||
font-size: 22px;
|
||||
line-height: 1.5;
|
||||
color: inherit;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 0;
|
||||
border-radius: $border-radius-base;
|
||||
}
|
||||
|
||||
.button {
|
||||
color: #333;
|
||||
background-color: transparent;
|
||||
display: inline-block;
|
||||
height: 36px;
|
||||
padding: 3px 12px;
|
||||
margin-bottom: 0;
|
||||
font-size: 22px;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
-ms-touch-action: manipulation;
|
||||
touch-action: manipulation;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
background-image: none;
|
||||
border: 0;
|
||||
border-radius: $border-radius-base;
|
||||
|
||||
&:focus {
|
||||
color: #333;
|
||||
background-color: transparent;
|
||||
|
||||
&:active,
|
||||
& {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@import 'common/app';
|
||||
</style>
|
||||
|
@ -34,7 +34,6 @@ export default {
|
||||
'toggleNavigationBar',
|
||||
'toggleEditor',
|
||||
'toggleSidePreview',
|
||||
'toggleSideBar',
|
||||
'toggleStatusBar',
|
||||
]),
|
||||
};
|
||||
|
@ -23,7 +23,6 @@ export default {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.editor__inner {
|
||||
|
18
src/components/Explorer.vue
Normal file
18
src/components/Explorer.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<div class="explorer">
|
||||
<div class="side-title">
|
||||
<div class="side-title__text">
|
||||
Explorer
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import 'common/variables.scss';
|
||||
</style>
|
13
src/components/ExplorerItem.vue
Normal file
13
src/components/ExplorerItem.vue
Normal file
@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div class="explorer-item">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import 'common/variables.scss';
|
||||
</style>
|
@ -1,38 +1,42 @@
|
||||
<template>
|
||||
<div class="layout">
|
||||
<div class="layout__panel layout__panel--inner-1" :style="{ top: inner1Y + 'px', height: inner1Height + 'px' }">
|
||||
<div class="layout__panel layout__panel--inner-2" :style="{ height: inner2Height + 'px' }">
|
||||
<div class="layout__panel layout__panel--inner-3" :style="{ left: inner3X + 'px', width: inner3Width + 'px' }">
|
||||
<div class="layout__panel layout__panel--button-bar">
|
||||
<div class="layout__panel flex flex--row">
|
||||
<div class="layout__panel layout__panel--explorer" v-show="showExplorer" :style="{ width: explorerWidth + 'px' }">
|
||||
<explorer></explorer>
|
||||
</div>
|
||||
<div class="layout__panel flex flex--column" :style="{ width: innerWidth + 'px' }">
|
||||
<div class="layout__panel layout__panel--navigation-bar" v-show="showNavigationBar || !showEditor" :style="{ height: navigationBarHeight + 'px' }">
|
||||
<navigation-bar></navigation-bar>
|
||||
</div>
|
||||
<div class="layout__panel flex flex--row" :style="{ height: innerHeight + 'px' }">
|
||||
<div class="layout__panel layout__panel--editor" v-show="showEditor" :style="{ width: editorWidth + 'px', 'font-size': fontSize + 'px' }">
|
||||
<editor></editor>
|
||||
</div>
|
||||
<div class="layout__panel layout__panel--button-bar" v-show="showEditor" :style="{ width: buttonBarWidth + 'px' }">
|
||||
<button-bar></button-bar>
|
||||
</div>
|
||||
<div class="layout__panel layout__panel--preview" v-show="showSidePreview || !showEditor" :style="{ width: previewWidth + 'px', 'font-size': fontSize + 'px' }">
|
||||
<preview></preview>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout__panel layout__panel--editor" v-show="showEditor" :style="{ width: editorWidth + 'px', 'font-size': fontSize + 'px' }">
|
||||
<editor></editor>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout__panel layout__panel--status-bar" :style="{ top: statusBarY + 'px' }">
|
||||
<div class="layout__panel layout__panel--status-bar" v-show="showStatusBar" :style="{ height: statusBarHeight + 'px' }">
|
||||
<status-bar></status-bar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout__panel layout__panel--navigation-bar" :style="{ top: navigationBarY + 'px' }">
|
||||
<navigation-bar></navigation-bar>
|
||||
</div>
|
||||
<div class="layout__panel layout__panel--side-bar" :style="{ left: sideBarX + 'px' }">
|
||||
<div class="layout__panel layout__panel--side-bar" v-show="showSideBar" :style="{ width: sideBarWidth + 'px' }">
|
||||
<side-bar></side-bar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import NavigationBar from './NavigationBar';
|
||||
import SideBar from './SideBar';
|
||||
import ButtonBar from './ButtonBar';
|
||||
import StatusBar from './StatusBar';
|
||||
import Explorer from './Explorer';
|
||||
import SideBar from './SideBar';
|
||||
import Editor from './Editor';
|
||||
import Preview from './Preview';
|
||||
import editorSvc from '../services/editorSvc';
|
||||
@ -41,24 +45,28 @@ import constants from '../services/constants';
|
||||
export default {
|
||||
components: {
|
||||
NavigationBar,
|
||||
SideBar,
|
||||
ButtonBar,
|
||||
StatusBar,
|
||||
Explorer,
|
||||
SideBar,
|
||||
Editor,
|
||||
Preview,
|
||||
},
|
||||
computed: mapState('layout', {
|
||||
explorerWidth: 'explorerWidth',
|
||||
sideBarWidth: 'sideBarWidth',
|
||||
navigationBarHeight: 'navigationBarHeight',
|
||||
buttonBarWidth: 'buttonBarWidth',
|
||||
statusBarHeight: 'statusBarHeight',
|
||||
showEditor: 'showEditor',
|
||||
showSidePreview: 'showSidePreview',
|
||||
showNavigationBar: 'showNavigationBar',
|
||||
showStatusBar: 'showStatusBar',
|
||||
showSideBar: 'showSideBar',
|
||||
showExplorer: 'showExplorer',
|
||||
fontSize: 'fontSize',
|
||||
inner1Y: 'inner1Y',
|
||||
inner1Height: 'inner1Height',
|
||||
inner2Height: 'inner2Height',
|
||||
inner3X: 'inner3X',
|
||||
inner3Width: 'inner3Width',
|
||||
navigationBarY: 'navigationBarY',
|
||||
sideBarX: 'sideBarX',
|
||||
statusBarY: 'statusBarY',
|
||||
innerWidth: 'innerWidth',
|
||||
innerHeight: 'innerHeight',
|
||||
previewWidth: 'previewWidth',
|
||||
editorWidth: 'editorWidth',
|
||||
}),
|
||||
@ -127,49 +135,45 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.layout__panel {
|
||||
.layout {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.layout__panel--inner-1 {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.layout__panel--button-bar {
|
||||
/* buttonBarWidth */
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.layout__panel--preview {
|
||||
/* buttonBarWidth */
|
||||
left: 30px;
|
||||
}
|
||||
|
||||
.layout__panel--status-bar {
|
||||
/* statusBarHeight */
|
||||
height: 20px;
|
||||
background-color: #007acc;
|
||||
}
|
||||
|
||||
.layout__panel--side-bar {
|
||||
/* sideBarWidth */
|
||||
width: 280px;
|
||||
.layout__panel {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
-webkit-flex: none;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.layout__panel--navigation-bar {
|
||||
/* navigationBarHeight */
|
||||
height: 44px;
|
||||
background-color: #2c2c2c;
|
||||
}
|
||||
|
||||
.layout__panel--status-bar {
|
||||
background-color: #007acc;
|
||||
}
|
||||
|
||||
.layout__panel--editor {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.layout__panel--explorer {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.layout__panel--button-bar,
|
||||
.layout__panel--status-bar,
|
||||
.layout__panel--side-bar,
|
||||
.layout__panel--navigation-bar {
|
||||
.app--loading & > * {
|
||||
display: none !important;
|
||||
opacity: 0.5;
|
||||
|
||||
/* Hack to disable mouse focus */
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,13 +1,24 @@
|
||||
<template>
|
||||
<div class="navigation-bar" v-bind:class="{'navigation-bar--editor': showEditor}">
|
||||
<div class="navigation-bar__inner navigation-bar__inner--left navigation-bar__inner--button">
|
||||
<button class="navigation-bar__button button" @click="toggleExplorer()">
|
||||
<icon-menu></icon-menu>
|
||||
</button>
|
||||
</div>
|
||||
<div class="navigation-bar__inner navigation-bar__inner--right navigation-bar__inner--button">
|
||||
<button class="navigation-bar__button button" @click="toggleExplorer()">
|
||||
<icon-settings></icon-settings>
|
||||
</button>
|
||||
</div>
|
||||
<div class="navigation-bar__inner navigation-bar__inner--right flex flex--row">
|
||||
<div class="navigation-bar__spinner">
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
<div class="navigation-bar__title navigation-bar__title--text text-input" v-bind:style="{maxWidth: titleMaxWidth + 'px'}"></div>
|
||||
<input class="navigation-bar__title navigation-bar__title--input text-input" v-bind:class="{'navigation-bar__title--focus': titleFocus, 'navigation-bar__title--scrolling': titleScrolling}" v-bind:style="{maxWidth: titleMaxWidth + 'px'}" @focus="editTitle(true)" @blur="editTitle(false)" @keyup.enter="submitTitle()" @keyup.esc="submitTitle(true)" v-model.lazy.trim="title">
|
||||
<div class="navigation-bar__title navigation-bar__title--fake text-input"></div>
|
||||
<div class="navigation-bar__title navigation-bar__title--text text-input" v-bind:style="{maxWidth: titleMaxWidth + 'px'}">{{title}}</div>
|
||||
<input class="navigation-bar__title navigation-bar__title--input text-input" v-bind:class="{'navigation-bar__title--focus': titleFocus, 'navigation-bar__title--scrolling': titleScrolling}" v-bind:style="{width: titleWidth + 'px'}" @focus="editTitle(true)" @blur="editTitle(false)" @keyup.enter="submitTitle()" @keyup.esc="submitTitle(true)" v-on:mouseenter="titleHover = true" v-on:mouseleave="titleHover = false" v-model="title">
|
||||
</div>
|
||||
<div class="navigation-bar__inner navigation-bar__inner--left">
|
||||
<div class="navigation-bar__inner navigation-bar__inner--edit-buttons">
|
||||
<button class="navigation-bar__button button" @click="pagedownClick('bold')">
|
||||
<icon-format-bold></icon-format-bold>
|
||||
</button>
|
||||
@ -49,12 +60,14 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'vuex';
|
||||
import { mapState, mapActions } from 'vuex';
|
||||
import editorSvc from '../services/editorSvc';
|
||||
import animationSvc from '../services/animationSvc';
|
||||
|
||||
export default {
|
||||
data: () => ({
|
||||
mounted: false,
|
||||
title: '',
|
||||
titleFocus: false,
|
||||
titleHover: false,
|
||||
}),
|
||||
@ -63,13 +76,15 @@ export default {
|
||||
showEditor: 'showEditor',
|
||||
titleMaxWidth: 'titleMaxWidth',
|
||||
}),
|
||||
title: {
|
||||
get() {
|
||||
return this.$store.getters['files/current'].name;
|
||||
},
|
||||
set(name) {
|
||||
this.$store.dispatch('files/patchCurrent', { name });
|
||||
},
|
||||
titleWidth() {
|
||||
if (!this.mounted) {
|
||||
return 0;
|
||||
}
|
||||
this.titleFakeElt.textContent = this.title;
|
||||
const width = this.titleFakeElt.getBoundingClientRect().width + 1; // 1px for the caret
|
||||
return width < this.titleMaxWidth
|
||||
? width
|
||||
: this.titleMaxWidth;
|
||||
},
|
||||
titleScrolling() {
|
||||
const result = this.titleHover && !this.titleFocus;
|
||||
@ -91,6 +106,10 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions('layout', [
|
||||
'toggleExplorer',
|
||||
'toggleSideBar',
|
||||
]),
|
||||
pagedownClick(name) {
|
||||
editorSvc.pagedownEditor.uiManager.doClick(name);
|
||||
},
|
||||
@ -98,39 +117,33 @@ export default {
|
||||
this.titleFocus = toggle;
|
||||
if (toggle) {
|
||||
this.titleInputElt.setSelectionRange(0, this.titleInputElt.value.length);
|
||||
} else {
|
||||
const title = this.title.trim();
|
||||
if (title) {
|
||||
this.$store.dispatch('files/patchCurrent', { name: title });
|
||||
} else {
|
||||
this.title = this.$store.getters['files/current'].name;
|
||||
}
|
||||
}
|
||||
},
|
||||
submitTitle(reset) {
|
||||
if (reset) {
|
||||
this.titleInputElt.value = '';
|
||||
this.title = '';
|
||||
}
|
||||
this.titleInputElt.blur();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.titleInputElt = this.$el.querySelector('.navigation-bar__title--input');
|
||||
const titleTextElt = this.$el.querySelector('.navigation-bar__title--text');
|
||||
|
||||
const adjustWidth = () => {
|
||||
titleTextElt.textContent = this.titleInputElt.value;
|
||||
const width = titleTextElt.getBoundingClientRect().width + 1; // 1px for the caret
|
||||
this.titleInputElt.style.width = `${width}px`;
|
||||
};
|
||||
|
||||
this.titleInputElt.addEventListener('keyup', adjustWidth);
|
||||
this.titleInputElt.addEventListener('input', adjustWidth);
|
||||
created() {
|
||||
this.$store.watch(
|
||||
() => this.$store.getters['files/current'].name,
|
||||
adjustWidth, {
|
||||
immediate: true,
|
||||
});
|
||||
|
||||
this.titleInputElt.addEventListener('mouseenter', () => {
|
||||
this.titleHover = true;
|
||||
});
|
||||
this.titleInputElt.addEventListener('mouseleave', () => {
|
||||
this.titleHover = false;
|
||||
});
|
||||
(name) => {
|
||||
this.title = name;
|
||||
}, { immediate: true });
|
||||
},
|
||||
mounted() {
|
||||
this.titleFakeElt = this.$el.querySelector('.navigation-bar__title--fake');
|
||||
this.titleInputElt = this.$el.querySelector('.navigation-bar__title--input');
|
||||
this.mounted = true;
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@ -142,14 +155,26 @@ export default {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 4px 15px 0;
|
||||
padding-top: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.navigation-bar__inner--left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.navigation-bar__inner--right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.navigation-bar__inner--button {
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
.navigation-bar__inner--edit-buttons {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.navigation-bar__button {
|
||||
display: inline-block;
|
||||
width: 34px;
|
||||
@ -174,26 +199,29 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-bar__title--fake {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
width: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.navigation-bar__title--text {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.navigation-bar__title--input,
|
||||
.navigation-bar__inner--left {
|
||||
.navigation-bar--editor & {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navigation-bar--editor {
|
||||
.navigation-bar__title--text {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.navigation-bar__title--input,
|
||||
.navigation-bar__inner--left {
|
||||
.navigation-bar__inner--edit-buttons,
|
||||
.navigation-bar__inner--button {
|
||||
display: none;
|
||||
|
||||
.navigation-bar--editor & {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
118
src/components/common/app.scss
Normal file
118
src/components/common/app.scss
Normal file
@ -0,0 +1,118 @@
|
||||
@import './variables.scss';
|
||||
@import './base';
|
||||
@import './markdownHighlighting';
|
||||
|
||||
body {
|
||||
background-color: #f3f3f3;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
tab-size: 4;
|
||||
text-rendering: auto;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
* {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.text-input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
padding: 3px 12px;
|
||||
font-size: 22px;
|
||||
line-height: 1.5;
|
||||
color: inherit;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 0;
|
||||
border-radius: $border-radius-base;
|
||||
}
|
||||
|
||||
.button {
|
||||
color: #333;
|
||||
background-color: transparent;
|
||||
display: inline-block;
|
||||
height: 36px;
|
||||
padding: 3px 12px;
|
||||
margin-bottom: 0;
|
||||
font-size: 22px;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
-ms-touch-action: manipulation;
|
||||
touch-action: manipulation;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
background-image: none;
|
||||
border: 0;
|
||||
border-radius: $border-radius-base;
|
||||
|
||||
&:focus {
|
||||
color: #333;
|
||||
background-color: transparent;
|
||||
|
||||
&:active,
|
||||
& {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex--row {
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-flex-direction: row;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flex--column {
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.side-title {
|
||||
height: 44px;
|
||||
line-height: 44px;
|
||||
padding: 0 10px;
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
.side-title__text {
|
||||
text-transform: uppercase;
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
@import '../../node_modules/katex/dist/katex.css';
|
||||
@import './variables.scss';
|
||||
@import './fonts.scss';
|
||||
@import './prism';
|
||||
|
||||
@include normalize();
|
||||
|
||||
|
@ -1,19 +0,0 @@
|
||||
.flex {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -moz-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.flex--row {
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-flex-direction: row;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flex--column {
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-flex-direction: column;
|
||||
flex-direction: column;
|
||||
}
|
@ -127,6 +127,7 @@
|
||||
.h6 {
|
||||
font-weight: $editor-font-weight-bold;
|
||||
|
||||
&,
|
||||
* {
|
||||
line-height: $line-height-title;
|
||||
}
|
||||
|
5
src/icons/Menu.vue
Normal file
5
src/icons/Menu.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" class="icon" width="100%" height="100%" viewBox="0 0 24.00 24.00" enable-background="new 0 0 24.00 24.00" xml:space="preserve">
|
||||
<path fill="#000000" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 3,6L 21,6L 21,8L 3,8L 3,6 Z M 3,11L 21,11L 21,13L 3,13L 3,11 Z M 3,16L 21,16L 21,18L 3,18L 3,16 Z "/>
|
||||
</svg>
|
||||
</template>
|
5
src/icons/Settings.vue
Normal file
5
src/icons/Settings.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" baseProfile="full" class="icon" width="100%" height="100%" viewBox="0 0 24.00 24.00" enable-background="new 0 0 24.00 24.00" xml:space="preserve">
|
||||
<path fill="#000000" fill-opacity="1" stroke-width="0.2" stroke-linejoin="round" d="M 11.9994,15.498C 10.0664,15.498 8.49939,13.931 8.49939,11.998C 8.49939,10.0651 10.0664,8.49805 11.9994,8.49805C 13.9324,8.49805 15.4994,10.0651 15.4994,11.998C 15.4994,13.931 13.9324,15.498 11.9994,15.498 Z M 19.4284,12.9741C 19.4704,12.6531 19.4984,12.329 19.4984,11.998C 19.4984,11.6671 19.4704,11.343 19.4284,11.022L 21.5414,9.36804C 21.7294,9.21606 21.7844,8.94604 21.6594,8.73004L 19.6594,5.26605C 19.5354,5.05005 19.2734,4.96204 19.0474,5.04907L 16.5584,6.05206C 16.0424,5.65607 15.4774,5.32104 14.8684,5.06903L 14.4934,2.41907C 14.4554,2.18103 14.2484,1.99805 13.9994,1.99805L 9.99939,1.99805C 9.74939,1.99805 9.5434,2.18103 9.5054,2.41907L 9.1304,5.06805C 8.52039,5.32104 7.95538,5.65607 7.43939,6.05206L 4.95139,5.04907C 4.7254,4.96204 4.46338,5.05005 4.33939,5.26605L 2.33939,8.73004C 2.21439,8.94604 2.26938,9.21606 2.4574,9.36804L 4.5694,11.022C 4.5274,11.342 4.49939,11.6671 4.49939,11.998C 4.49939,12.329 4.5274,12.6541 4.5694,12.9741L 2.4574,14.6271C 2.26938,14.78 2.21439,15.05 2.33939,15.2661L 4.33939,18.73C 4.46338,18.946 4.7254,19.0341 4.95139,18.947L 7.4404,17.944C 7.95639,18.34 8.52139,18.675 9.1304,18.9271L 9.5054,21.577C 9.5434,21.8151 9.74939,21.998 9.99939,21.998L 13.9994,21.998C 14.2484,21.998 14.4554,21.8151 14.4934,21.577L 14.8684,18.9271C 15.4764,18.6741 16.0414,18.34 16.5574,17.9431L 19.0474,18.947C 19.2734,19.0341 19.5354,18.946 19.6594,18.73L 21.6594,15.2661C 21.7844,15.05 21.7294,14.78 21.5414,14.6271L 19.4284,12.9741 Z "/>
|
||||
</svg>
|
||||
</template>
|
@ -15,6 +15,8 @@ import StatusBar from './StatusBar';
|
||||
import NavigationBar from './NavigationBar';
|
||||
import SidePreview from './SidePreview';
|
||||
import Eye from './Eye';
|
||||
import Menu from './Menu';
|
||||
import Settings from './Settings';
|
||||
|
||||
Vue.component('iconFormatBold', FormatBold);
|
||||
Vue.component('iconFormatItalic', FormatItalic);
|
||||
@ -32,3 +34,5 @@ Vue.component('iconStatusBar', StatusBar);
|
||||
Vue.component('iconNavigationBar', NavigationBar);
|
||||
Vue.component('iconSidePreview', SidePreview);
|
||||
Vue.component('iconEye', Eye);
|
||||
Vue.component('iconMenu', Menu);
|
||||
Vue.component('iconSettings', Settings);
|
||||
|
@ -21,7 +21,7 @@ function getStorePrefixFromType(type) {
|
||||
return store.state[prefix] && prefix;
|
||||
}
|
||||
|
||||
const deletedMarkerMaxAge = 1000;
|
||||
const deleteMarkerMaxAge = 1000;
|
||||
|
||||
class Connection {
|
||||
constructor() {
|
||||
@ -67,6 +67,9 @@ class Connection {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a connection asynchronously.
|
||||
*/
|
||||
createTx(cb) {
|
||||
if (!this.db) {
|
||||
this.getTxCbs.push(cb);
|
||||
@ -78,10 +81,13 @@ class Connection {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
// Open transaction in read/write will prevent conflict with other tabs
|
||||
const tx = this.db.transaction(this.db.objectStoreNames, 'readwrite');
|
||||
tx.onerror = (evt) => {
|
||||
dbg('Rollback transaction', evt);
|
||||
};
|
||||
// Read the current txCounter
|
||||
const dbStore = tx.objectStore(dbStoreName);
|
||||
const request = dbStore.get('txCounter');
|
||||
request.onsuccess = () => {
|
||||
@ -101,6 +107,11 @@ export default {
|
||||
updatedMap: Object.create(null),
|
||||
connection: new Connection(),
|
||||
|
||||
/**
|
||||
* Return a promise that is resolved once the synchronization between the store and the localDb
|
||||
* is finished. Effectively, open a transaction, then read and apply all changes from the DB
|
||||
* since previous transaction, then write all changes from the store.
|
||||
*/
|
||||
sync() {
|
||||
return new Promise((resolve) => {
|
||||
const storeItemMap = {};
|
||||
@ -117,11 +128,14 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Read and apply all changes from the DB since previous transaction.
|
||||
*/
|
||||
readAll(storeItemMap, tx, cb) {
|
||||
let resetMap;
|
||||
|
||||
// We may have missed some deleted markers
|
||||
if (this.lastTx && tx.txCounter - this.lastTx > deletedMarkerMaxAge) {
|
||||
// We may have missed some delete markers
|
||||
if (this.lastTx && tx.txCounter - this.lastTx > deleteMarkerMaxAge) {
|
||||
// Delete all dirty store items (user was asleep anyway...)
|
||||
resetMap = true;
|
||||
// And retrieve everything from DB
|
||||
@ -138,8 +152,8 @@ export default {
|
||||
if (cursor) {
|
||||
const item = cursor.value;
|
||||
items.push(item);
|
||||
// Remove old deleted markers
|
||||
if (!item.updated && tx.txCounter - item.tx > deletedMarkerMaxAge) {
|
||||
// Remove old delete markers
|
||||
if (!item.updated && tx.txCounter - item.tx > deleteMarkerMaxAge) {
|
||||
itemsToDelete.push(item);
|
||||
}
|
||||
cursor.continue();
|
||||
@ -162,30 +176,28 @@ export default {
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Write all changes from the store since previous transaction.
|
||||
*/
|
||||
writeAll(storeItemMap, tx) {
|
||||
this.lastTx = tx.txCounter;
|
||||
const dbStore = tx.objectStore(dbStoreName);
|
||||
|
||||
// Remove deleted store items
|
||||
const storedIds = Object.keys(this.updatedMap);
|
||||
const storedIdsLen = storedIds.length;
|
||||
for (let i = 0; i < storedIdsLen; i += 1) {
|
||||
const id = storedIds[i];
|
||||
Object.keys(this.updatedMap).forEach((id) => {
|
||||
if (!storeItemMap[id]) {
|
||||
// Put a deleted marker to notify other tabs
|
||||
// Put a delete marker to notify other tabs
|
||||
dbStore.put({
|
||||
id,
|
||||
tx: this.lastTx,
|
||||
});
|
||||
delete this.updatedMap[id];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Put changes
|
||||
const storeItemIds = Object.keys(storeItemMap);
|
||||
const storeItemIdsLen = storeItemIds.length;
|
||||
for (let i = 0; i < storeItemIdsLen; i += 1) {
|
||||
const storeItem = storeItemMap[storeItemIds[i]];
|
||||
Object.keys(storeItemMap).forEach((id) => {
|
||||
const storeItem = storeItemMap[id];
|
||||
// Store object has changed
|
||||
if (this.updatedMap[storeItem.id] !== storeItem.updated) {
|
||||
const item = {
|
||||
@ -196,9 +208,12 @@ export default {
|
||||
dbStore.put(item);
|
||||
this.updatedMap[item.id] = item.updated;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Read and apply one DB change.
|
||||
*/
|
||||
readDbItem(dbItem, storeItemMap) {
|
||||
const existingStoreItem = storeItemMap[dbItem.id];
|
||||
if (!dbItem.updated) {
|
||||
|
@ -38,8 +38,8 @@ store.watch(
|
||||
return store.state.files.itemMap[fileId];
|
||||
}))
|
||||
.then((currentFile) => {
|
||||
store.commit('files/patchItem', { id: currentFile.id });
|
||||
store.commit('files/setCurrentId', currentFile.id);
|
||||
store.dispatch('files/patchCurrent', {}); // Update `updated` field to make it the mostRecent
|
||||
}),
|
||||
{
|
||||
immediate: true,
|
||||
|
@ -1,12 +1,7 @@
|
||||
const navigationBarHeight = 44;
|
||||
const sideBarWidth = 280;
|
||||
const editorMinWidth = 280;
|
||||
const buttonBarWidth = 30;
|
||||
const statusBarHeight = 20;
|
||||
const outOfScreenMargin = 50;
|
||||
const minPadding = 20;
|
||||
const navigationBarSpaceWidth = 30;
|
||||
const navigationBarLeftWidth = 500;
|
||||
const navigationBarLeftWidth = 570;
|
||||
const maxTitleMaxWidth = 800;
|
||||
const minTitleMaxWidth = 200;
|
||||
|
||||
@ -22,24 +17,25 @@ const toggler = (propertyName, setterName) => ({ state, commit, dispatch }, show
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
// Constants
|
||||
explorerWidth: 280,
|
||||
sideBarWidth: 280,
|
||||
navigationBarHeight: 44,
|
||||
buttonBarWidth: 30,
|
||||
statusBarHeight: 20,
|
||||
// Configuration
|
||||
showNavigationBar: true,
|
||||
showEditor: true,
|
||||
showSidePreview: true,
|
||||
showSideBar: false,
|
||||
showStatusBar: true,
|
||||
showSideBar: false,
|
||||
showExplorer: false,
|
||||
editorWidthFactor: 1,
|
||||
fontSizeFactor: 1,
|
||||
// Style
|
||||
fontSize: 0,
|
||||
inner1Y: 0,
|
||||
inner1Height: 0,
|
||||
inner2Height: 0,
|
||||
inner3X: 0,
|
||||
inner3Width: 0,
|
||||
navigationBarY: 0,
|
||||
sideBarX: 0,
|
||||
statusBarY: 0,
|
||||
innerWidth: 0,
|
||||
innerHeight: 0,
|
||||
editorWidth: 0,
|
||||
editorPadding: 0,
|
||||
previewWidth: 0,
|
||||
@ -50,19 +46,14 @@ export default {
|
||||
setShowNavigationBar: setter('showNavigationBar'),
|
||||
setShowEditor: setter('showEditor'),
|
||||
setShowSidePreview: setter('showSidePreview'),
|
||||
setShowSideBar: setter('showSideBar'),
|
||||
setShowStatusBar: setter('showStatusBar'),
|
||||
setShowSideBar: setter('showSideBar'),
|
||||
setShowExplorer: setter('showExplorer'),
|
||||
setEditorWidthFactor: setter('editorWidthFactor'),
|
||||
setFontSizeFactor: setter('fontSizeFactor'),
|
||||
setFontSize: setter('fontSize'),
|
||||
setInner1Y: setter('inner1Y'),
|
||||
setInner1Height: setter('inner1Height'),
|
||||
setInner2Height: setter('inner2Height'),
|
||||
setInner3X: setter('inner3X'),
|
||||
setInner3Width: setter('inner3Width'),
|
||||
setNavigationBarY: setter('navigationBarY'),
|
||||
setSideBarX: setter('sideBarX'),
|
||||
setStatusBarY: setter('statusBarY'),
|
||||
setInnerWidth: setter('innerWidth'),
|
||||
setInnerHeight: setter('innerHeight'),
|
||||
setEditorWidth: setter('editorWidth'),
|
||||
setEditorPadding: setter('editorPadding'),
|
||||
setPreviewWidth: setter('previewWidth'),
|
||||
@ -73,35 +64,39 @@ export default {
|
||||
toggleNavigationBar: toggler('showNavigationBar', 'setShowNavigationBar'),
|
||||
toggleEditor: toggler('showEditor', 'setShowEditor'),
|
||||
toggleSidePreview: toggler('showSidePreview', 'setShowSidePreview'),
|
||||
toggleSideBar: toggler('showSideBar', 'setShowSideBar'),
|
||||
toggleStatusBar: toggler('showStatusBar', 'setShowStatusBar'),
|
||||
toggleSideBar: toggler('showSideBar', 'setShowSideBar'),
|
||||
toggleExplorer: toggler('showExplorer', 'setShowExplorer'),
|
||||
updateStyle({ state, commit, dispatch }) {
|
||||
const bodyWidth = document.body.clientWidth;
|
||||
const bodyHeight = document.body.clientHeight;
|
||||
|
||||
const showNavigationBar = !state.showEditor || state.showNavigationBar;
|
||||
const inner1Y = showNavigationBar
|
||||
? navigationBarHeight
|
||||
: 0;
|
||||
const inner1Height = bodyHeight - inner1Y;
|
||||
const inner2Height = state.showStatusBar
|
||||
? inner1Height - statusBarHeight
|
||||
: inner1Height;
|
||||
const navigationBarY = showNavigationBar
|
||||
? 0
|
||||
: -navigationBarHeight - outOfScreenMargin;
|
||||
const sideBarX = state.showSideBar
|
||||
? bodyWidth - sideBarWidth
|
||||
: bodyWidth + outOfScreenMargin;
|
||||
const statusBarY = state.showStatusBar
|
||||
? inner2Height
|
||||
: inner2Height + outOfScreenMargin;
|
||||
|
||||
let doublePanelWidth = bodyWidth - buttonBarWidth;
|
||||
if (state.showSideBar) {
|
||||
doublePanelWidth -= sideBarWidth;
|
||||
let innerHeight = bodyHeight;
|
||||
if (showNavigationBar) {
|
||||
innerHeight -= state.navigationBarHeight;
|
||||
}
|
||||
if (state.showStatusBar) {
|
||||
innerHeight -= state.statusBarHeight;
|
||||
}
|
||||
|
||||
let innerWidth = bodyWidth;
|
||||
if (state.showSideBar) {
|
||||
innerWidth -= state.sideBarWidth;
|
||||
}
|
||||
if (state.showExplorer) {
|
||||
innerWidth -= state.explorerWidth;
|
||||
}
|
||||
let doublePanelWidth = innerWidth - state.buttonBarWidth;
|
||||
if (doublePanelWidth < editorMinWidth) {
|
||||
if (state.showSideBar) {
|
||||
dispatch('toggleSideBar', false);
|
||||
return;
|
||||
}
|
||||
if (state.showExplorer) {
|
||||
dispatch('toggleExplorer', false);
|
||||
return;
|
||||
}
|
||||
doublePanelWidth = editorMinWidth;
|
||||
}
|
||||
const splitPanel = state.showEditor && state.showSidePreview;
|
||||
@ -109,10 +104,6 @@ export default {
|
||||
dispatch('toggleSidePreview', false);
|
||||
return;
|
||||
}
|
||||
if (state.showSideBar && bodyWidth < editorMinWidth + sideBarWidth) {
|
||||
dispatch('toggleSideBar', false);
|
||||
return;
|
||||
}
|
||||
|
||||
let fontSize = 18;
|
||||
let textWidth = 990;
|
||||
@ -130,32 +121,22 @@ export default {
|
||||
fontSize *= state.fontSizeFactor;
|
||||
|
||||
const panelWidth = doublePanelWidth / 2;
|
||||
let inner3X = panelWidth;
|
||||
if (!splitPanel) {
|
||||
inner3X = state.showEditor
|
||||
? doublePanelWidth
|
||||
: -buttonBarWidth;
|
||||
}
|
||||
const inner3Width = splitPanel
|
||||
? panelWidth + buttonBarWidth
|
||||
: doublePanelWidth + buttonBarWidth;
|
||||
|
||||
const previewWidth = splitPanel
|
||||
? panelWidth
|
||||
: bodyWidth;
|
||||
const previewWidth = splitPanel ?
|
||||
panelWidth :
|
||||
innerWidth;
|
||||
let previewPadding = (previewWidth - textWidth) / 2;
|
||||
if (previewPadding < minPadding) {
|
||||
previewPadding = minPadding;
|
||||
}
|
||||
const editorWidth = splitPanel
|
||||
? panelWidth
|
||||
: doublePanelWidth;
|
||||
const editorWidth = splitPanel ?
|
||||
panelWidth :
|
||||
doublePanelWidth;
|
||||
let editorPadding = (editorWidth - textWidth) / 2;
|
||||
if (editorPadding < minPadding) {
|
||||
editorPadding = minPadding;
|
||||
}
|
||||
|
||||
let titleMaxWidth = bodyWidth - navigationBarSpaceWidth;
|
||||
let titleMaxWidth = innerWidth - navigationBarSpaceWidth;
|
||||
if (state.showEditor) {
|
||||
titleMaxWidth -= navigationBarLeftWidth;
|
||||
}
|
||||
@ -163,14 +144,8 @@ export default {
|
||||
titleMaxWidth = Math.max(titleMaxWidth, minTitleMaxWidth);
|
||||
|
||||
commit('setFontSize', fontSize);
|
||||
commit('setInner1Y', inner1Y);
|
||||
commit('setInner1Height', inner1Height);
|
||||
commit('setInner2Height', inner2Height);
|
||||
commit('setInner3X', inner3X);
|
||||
commit('setInner3Width', inner3Width);
|
||||
commit('setNavigationBarY', navigationBarY);
|
||||
commit('setSideBarX', sideBarX);
|
||||
commit('setStatusBarY', statusBarY);
|
||||
commit('setInnerWidth', innerWidth);
|
||||
commit('setInnerHeight', innerHeight);
|
||||
commit('setPreviewWidth', previewWidth);
|
||||
commit('setPreviewPadding', previewPadding);
|
||||
commit('setEditorWidth', editorWidth);
|
||||
|
Loading…
Reference in New Issue
Block a user