nextcloud-notes/src/components/Note.vue

308 lines
6.3 KiB
Vue
Raw Normal View History

2019-05-22 21:40:03 +02:00
<template>
2019-06-07 16:07:53 +02:00
<AppContent :class="{ loading: loading || isManualSave, 'icon-error': !loading && (!note || note.error), 'sidebar-open': sidebarOpen }">
2019-10-25 15:33:29 +02:00
<div v-if="!loading && note && !note.error && !note.deleting"
id="note-container"
class="note-container"
:class="{ fullscreen: fullscreen }"
2019-05-22 21:40:03 +02:00
>
<div class="note-editor">
<div v-show="!note.content" class="placeholder">
2019-06-07 16:07:53 +02:00
{{ preview ? t('notes', 'Empty note') : t('notes', 'Write …') }}
2019-05-22 21:40:03 +02:00
</div>
2019-06-07 16:07:53 +02:00
<ThePreview v-if="preview" :value="note.content" />
<TheEditor v-else :value="note.content" @input="onEdit" />
2019-05-22 21:40:03 +02:00
</div>
<span class="action-buttons">
2019-06-07 16:07:53 +02:00
<Actions :open.sync="actionsOpen" menu-align="right">
<ActionButton v-show="!sidebarOpen && !fullscreen"
icon="icon-details"
@click="onToggleSidebar"
>
{{ t('notes', 'Details') }}
</ActionButton>
2019-06-09 22:15:37 +02:00
<ActionButton v-if="!preview"
2019-06-07 16:07:53 +02:00
icon="icon-toggle"
@click="onTogglePreview"
>
{{ t('notes', 'Preview') }}
</ActionButton>
2019-06-09 22:15:37 +02:00
<ActionButton v-else
2019-06-07 16:07:53 +02:00
icon="icon-rename"
@click="onTogglePreview"
>
{{ t('notes', 'Edit') }}
</ActionButton>
<ActionButton
icon="icon-fullscreen"
:class="{ active: fullscreen }"
@click="onToggleDistractionFree"
>
2019-09-14 11:00:31 +02:00
{{ fullscreen ? t('notes', 'Exit full screen') : t('notes', 'Full screen') }}
2019-06-07 16:07:53 +02:00
</ActionButton>
</Actions>
2019-06-09 22:15:37 +02:00
<button v-show="note.saveError"
v-tooltip.right="t('notes', 'Save failed. Click to retry.')"
class="action-error icon-error-color"
@click="onManualSave"
/>
2019-05-22 21:40:03 +02:00
</span>
</div>
</AppContent>
</template>
<script>
import {
2019-06-07 16:07:53 +02:00
Actions,
ActionButton,
2019-05-22 21:40:03 +02:00
AppContent,
Tooltip,
2019-10-25 14:00:07 +02:00
} from '@nextcloud/vue'
2020-02-19 21:35:19 +01:00
import { fetchNote, saveNote, saveNoteManually } from '../NotesService'
import { closeNavbar } from '../nextcloud'
2019-05-22 21:40:03 +02:00
import TheEditor from './EditorEasyMDE'
2019-06-07 16:07:53 +02:00
import ThePreview from './EditorMarkdownIt'
2019-05-22 21:40:03 +02:00
import store from '../store'
export default {
name: 'Note',
components: {
2019-06-07 16:07:53 +02:00
Actions,
ActionButton,
2019-05-22 21:40:03 +02:00
AppContent,
TheEditor,
2019-06-07 16:07:53 +02:00
ThePreview,
2019-05-22 21:40:03 +02:00
},
directives: {
tooltip: Tooltip,
},
props: {
noteId: {
type: String,
required: true,
},
},
data: function() {
return {
loading: false,
fullscreen: false,
2019-06-07 16:07:53 +02:00
preview: false,
actionsOpen: false,
2019-05-22 21:40:03 +02:00
}
},
computed: {
note() {
return store.getters.getNote(parseInt(this.noteId))
},
title() {
return this.note ? this.note.title : ''
},
isManualSave() {
return store.state.isManualSave
},
2019-06-07 16:07:53 +02:00
sidebarOpen() {
return store.state.sidebarOpen
},
2019-05-22 21:40:03 +02:00
},
watch: {
// call again the method if the route changes
'$route': 'fetchData',
title: 'onUpdateTitle',
},
created() {
this.fetchData()
// TODO move the following from jQuery to plain JS
$(document).bind('webkitfullscreenchange mozfullscreenchange fullscreenchange', this.onDetectFullscreen)
$(document).bind('keypress.notes.save', this.onKeyPress)
},
destroyed() {
$(document).unbind('keypress.notes.save')
store.commit('setSidebarOpen', false)
this.onUpdateTitle(null)
},
methods: {
fetchData() {
store.commit('setSidebarOpen', false)
2020-01-03 12:23:56 +01:00
closeNavbar()
2019-05-22 21:40:03 +02:00
this.onUpdateTitle(this.title)
this.loading = true
2019-06-07 16:07:53 +02:00
this.preview = false
2020-02-19 21:35:19 +01:00
fetchNote(this.noteId)
2019-05-22 21:40:03 +02:00
.then((note) => {
if (note.errorMessage) {
OC.Notification.showTemporary(note.errorMessage)
}
})
.catch(() => {
// note not found
})
2020-02-19 21:35:19 +01:00
.then(() => {
2019-05-22 21:40:03 +02:00
this.loading = false
})
},
onUpdateTitle(title) {
const defaultTitle = store.state.documentTitle
if (title) {
document.title = title + ' - ' + defaultTitle
} else {
document.title = defaultTitle
}
},
2019-06-07 16:07:53 +02:00
onTogglePreview() {
this.preview = !this.preview
this.actionsOpen = false
},
2019-05-22 21:40:03 +02:00
onDetectFullscreen() {
this.fullscreen = document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen
},
onToggleDistractionFree() {
function launchIntoFullscreen(element) {
if (element.requestFullscreen) {
element.requestFullscreen()
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen()
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullscreen()
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen()
}
}
function exitFullscreen() {
if (document.exitFullscreen) {
document.exitFullscreen()
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen()
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen()
}
}
if (this.fullscreen) {
exitFullscreen()
} else {
launchIntoFullscreen(document.getElementById('note-container'))
}
2019-06-07 16:07:53 +02:00
this.actionsOpen = false
2019-05-22 21:40:03 +02:00
},
onToggleSidebar() {
store.commit('setSidebarOpen', !store.state.sidebarOpen)
2019-06-07 16:07:53 +02:00
this.actionsOpen = false
2019-05-22 21:40:03 +02:00
},
onEdit(newContent) {
if (this.note.content !== newContent) {
const note = {
...this.note,
content: newContent,
unsaved: true,
}
store.commit('add', note)
2020-02-19 21:35:19 +01:00
setTimeout(saveNote.bind(this, note.id), 1000)
2019-05-22 21:40:03 +02:00
}
},
onKeyPress(event) {
if (event.ctrlKey || event.metaKey) {
switch (String.fromCharCode(event.which).toLowerCase()) {
case 's':
event.preventDefault()
this.onManualSave()
break
}
}
},
onManualSave() {
2020-02-19 21:35:19 +01:00
saveNoteManually(this.note.id)
2019-05-22 21:40:03 +02:00
},
},
}
</script>
<style scoped>
.note-container {
min-height: 100%;
width: 100%;
background-color: var(--color-main-background);
}
.note-editor {
max-width: 47em;
font-size: 16px;
2019-06-07 16:07:53 +02:00
padding: 1em;
2019-05-22 21:40:03 +02:00
}
/* center editor on large screens */
@media (min-width: 1600px) {
.note-editor {
margin: 0 auto;
}
.note-container {
padding-right: 250px;
2019-06-07 16:07:53 +02:00
transition-duration: var(--animation-quick);
transition-property: padding-right;
}
.sidebar-open .note-container {
padding-right: 0px;
2019-05-22 21:40:03 +02:00
}
}
/* distraction free styles */
.note-container.fullscreen {
width: 100vw;
height: 100vh;
overflow-y: auto;
padding: 0;
}
.note-container.fullscreen .note-editor {
margin: 0 auto;
}
/* placeholder */
.placeholder {
position: absolute;
2019-06-07 16:07:53 +02:00
padding: 1em;
2019-05-22 21:40:03 +02:00
opacity: 0.5;
}
/* main editor button */
.action-buttons {
position: fixed;
2019-06-07 16:07:53 +02:00
top: 50px;
2019-05-22 21:40:03 +02:00
right: 20px;
2019-06-09 22:15:37 +02:00
width: 44px;
2019-06-07 16:07:53 +02:00
margin-top: 1em;
2019-05-22 21:40:03 +02:00
z-index: 2000;
}
2019-06-09 22:15:37 +02:00
.action-buttons .action-error {
width: 44px;
height: 44px;
}
2019-06-07 16:07:53 +02:00
.note-container.fullscreen .action-buttons {
top: 0px;
}
2019-05-22 21:40:03 +02:00
.action-buttons button {
padding: 15px;
}
</style>