2019-07-19 13:51:57 -07:00
|
|
|
<template>
|
2019-07-29 00:22:47 -07:00
|
|
|
<div
|
|
|
|
id="InputNotes" class="master-note-edit"
|
|
|
|
@keyup.esc="close"
|
|
|
|
:class="[{'size-down':(sizeDown == true)}, 'position-'+position ]"
|
|
|
|
:style="{'background-color':color, 'color':fontColor}"
|
|
|
|
>
|
2019-07-19 13:51:57 -07:00
|
|
|
|
2019-07-29 00:22:47 -07:00
|
|
|
<div v-if="fancyInput == 1" class="textarea-height no-flow">
|
|
|
|
<ckeditor ref="main-edit"
|
|
|
|
:editor="editor" @ready="onReady" v-model="noteText" :config="editorConfig" v-on:blur="save" />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<textarea
|
|
|
|
class="textarea-height raw-edit"
|
|
|
|
v-if="fancyInput == 0"
|
|
|
|
v-model="noteText"
|
|
|
|
v-on:blur="save"
|
|
|
|
v-on:keyup="onKeyup"
|
|
|
|
/>
|
|
|
|
|
|
|
|
<div class="ui buttons">
|
|
|
|
<div @click="close" class="ui button">Close + Save (ESC)</div>
|
|
|
|
<div class="ui button">Delete</div>
|
|
|
|
|
|
|
|
<div @click="onToggleFancyInput" class="ui button">
|
|
|
|
Fancy ({{fancyInput?'On':'Off'}})
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="ui buttons">
|
|
|
|
<button @click="onChangeColor" class="ui icon white button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon red button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon orange button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon yellow button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon olive button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon green button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon teal button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon blue button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon violet button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon purple button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon pink button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon brown button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon grey button"></button>
|
|
|
|
<button @click="onChangeColor" class="ui icon black button"></button>
|
|
|
|
</div>
|
|
|
|
<div class="ui right floated green button">{{statusText}}</div>
|
2019-07-21 09:28:07 -07:00
|
|
|
<!-- <p>
|
2019-07-29 00:22:47 -07:00
|
|
|
Last Updated: {{$helpers.timeAgo(updated)}}
|
|
|
|
</p> -->
|
2019-07-19 13:51:57 -07:00
|
|
|
|
2019-07-29 00:22:47 -07:00
|
|
|
<note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/>
|
2019-07-19 13:51:57 -07:00
|
|
|
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
|
|
import axios from 'axios'
|
|
|
|
import DecoupledEditor from '@ckeditor/ckeditor5-build-decoupled-document';
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
name: 'InputNotes',
|
2019-07-20 16:07:22 -07:00
|
|
|
props: [ 'noteid', 'position' ],
|
2019-07-19 13:51:57 -07:00
|
|
|
components:{
|
2019-07-30 12:10:31 -07:00
|
|
|
'note-tag-edit': require('@/components/NoteTagEdit.vue').default
|
2019-07-19 13:51:57 -07:00
|
|
|
},
|
|
|
|
data(){
|
|
|
|
return {
|
|
|
|
currentNoteId: 0,
|
|
|
|
noteText: '',
|
2019-07-29 00:22:47 -07:00
|
|
|
statusText: 'Saved',
|
2019-07-19 13:51:57 -07:00
|
|
|
lastNoteHash: null,
|
2019-07-24 11:06:50 -07:00
|
|
|
saveDebounce: null, //Prevent save from being called numerous times quickly
|
2019-07-20 16:07:22 -07:00
|
|
|
lastSaved: 0,
|
2019-07-19 13:51:57 -07:00
|
|
|
updated: 'Never',
|
|
|
|
editDebounce: null,
|
|
|
|
keyPressesCounter: 0,
|
2019-07-21 09:28:07 -07:00
|
|
|
fancyInput: 0, //Default to basic text edit. Upgrade if set to 1
|
|
|
|
color: '#FFF',
|
2019-07-24 11:06:50 -07:00
|
|
|
fontColor: '#000',
|
2019-07-29 00:22:47 -07:00
|
|
|
sizeDown: false,
|
2019-07-19 13:51:57 -07:00
|
|
|
|
|
|
|
editor: DecoupledEditor,
|
|
|
|
editorConfig: {
|
|
|
|
startupFocus: 'end',
|
|
|
|
toolbar: ["alignment", "fontSize", "removeHighlight", "highlight", "bold", "italic", "strikethrough", "underline", "blockQuote", "heading", "link", "numberedList", "bulletedList", "insertTable", "|", "undo", "redo"]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
noteid:function(newVal, oldVal){
|
|
|
|
|
|
|
|
if(newVal == this.currentNoteId){
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if(newVal == oldVal){
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this.currentNoteId = newVal
|
|
|
|
this.loadNote(this.currentNoteId)
|
|
|
|
|
|
|
|
}
|
|
|
|
},
|
|
|
|
beforeMount(){
|
|
|
|
|
|
|
|
},
|
|
|
|
beforeDestroy(){
|
|
|
|
|
|
|
|
},
|
|
|
|
mounted: function() {
|
|
|
|
|
|
|
|
this.loadNote(this.noteid)
|
|
|
|
},
|
|
|
|
methods: {
|
2019-07-21 09:28:07 -07:00
|
|
|
onToggleFancyInput(){
|
|
|
|
|
|
|
|
if(this.fancyInput == 0){
|
|
|
|
this.fancyInput = 1
|
|
|
|
} else {
|
|
|
|
this.fancyInput = 0;
|
|
|
|
}
|
|
|
|
//Update last note hash, this will tell note to save next update
|
|
|
|
this.lastNoteHash = 0
|
|
|
|
},
|
|
|
|
onChangeColor(event){
|
|
|
|
//Grab the color of the button clicked
|
|
|
|
const style = getComputedStyle(event.target)
|
|
|
|
this.color = style['background-color']
|
2019-07-24 11:06:50 -07:00
|
|
|
this.fontColor = '#FFF'
|
2019-07-21 09:28:07 -07:00
|
|
|
|
|
|
|
if(this.color == "rgb(255, 255, 255)" || this.color == '#FFF'){
|
|
|
|
this.color = null
|
2019-07-29 00:22:47 -07:00
|
|
|
this.fontColor = null
|
2019-07-21 09:28:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
this.lastNoteHash = 0 //Update hash to force note update on next save
|
|
|
|
this.save()
|
|
|
|
},
|
2019-07-19 13:51:57 -07:00
|
|
|
loadNote(noteId){
|
|
|
|
let vm = this
|
|
|
|
//Component is activated with NoteId in place, lookup text with associated ID
|
|
|
|
if(this.$store.getters.getLoggedIn){
|
2019-07-30 12:21:12 -07:00
|
|
|
axios.post('/api/note/get', {'noteId': noteId})
|
2019-07-19 13:51:57 -07:00
|
|
|
.then(response => {
|
2019-07-21 09:28:07 -07:00
|
|
|
|
2019-07-19 13:51:57 -07:00
|
|
|
//Set up local data
|
|
|
|
vm.currentNoteId = noteId
|
|
|
|
vm.noteText = response.data.text
|
|
|
|
vm.updated = response.data.updated
|
|
|
|
vm.lastNoteHash = vm.hashString(response.data.text)
|
2019-07-24 11:06:50 -07:00
|
|
|
vm.color = response.data.color
|
|
|
|
|
|
|
|
this.fontColor = '#FFF'
|
|
|
|
if(this.color == "rgb(255, 255, 255)" || this.color == '#FFF' || this.color == null){
|
|
|
|
this.color = null
|
2019-07-29 00:22:47 -07:00
|
|
|
this.fontColor = null
|
2019-07-24 11:06:50 -07:00
|
|
|
}
|
2019-07-19 13:51:57 -07:00
|
|
|
|
2019-07-21 09:28:07 -07:00
|
|
|
if(response.data.raw_input == 1){
|
|
|
|
this.fancyInput = 1
|
|
|
|
}
|
|
|
|
|
2019-07-19 13:51:57 -07:00
|
|
|
//Put focus on note, at the end of the note text
|
|
|
|
vm.$nextTick(() => {
|
|
|
|
// vm.$refs['custom-input'].focus()
|
|
|
|
})
|
|
|
|
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
console.log('Could not fetch note')
|
|
|
|
}
|
|
|
|
},
|
|
|
|
onReady(editor){
|
|
|
|
|
|
|
|
let vm = this
|
|
|
|
|
|
|
|
// Insert the toolbar before the editable area.
|
|
|
|
editor.ui.getEditableElement().parentElement.insertBefore(
|
|
|
|
editor.ui.view.toolbar.element,
|
|
|
|
editor.ui.getEditableElement()
|
|
|
|
);
|
|
|
|
|
|
|
|
editor.editing.view.focus()
|
|
|
|
|
|
|
|
// const editor = this.editor;
|
|
|
|
const view = editor.editing.view;
|
|
|
|
const viewDocument = view.document;
|
|
|
|
|
|
|
|
//Insert 5 spaces when tab is pressed
|
|
|
|
viewDocument.on( 'keyup', ( evt, data ) => {
|
|
|
|
|
2019-07-21 09:28:07 -07:00
|
|
|
vm.onKeyup(event)
|
2019-07-19 13:51:57 -07:00
|
|
|
|
2019-07-20 16:07:22 -07:00
|
|
|
//Optional data bindings for tab key
|
2019-07-19 13:51:57 -07:00
|
|
|
if( (data.keyCode == 9) && viewDocument.isFocused ){
|
|
|
|
|
|
|
|
//Insert 5 spaces to simulate tab
|
2019-07-20 16:07:22 -07:00
|
|
|
//editor.execute( 'input', { text: " " } );
|
2019-07-19 13:51:57 -07:00
|
|
|
|
|
|
|
evt.stop(); // Prevent executing the default handler.
|
|
|
|
data.preventDefault();
|
|
|
|
view.scrollToTheSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
} );
|
|
|
|
},
|
2019-07-29 00:22:47 -07:00
|
|
|
//Used by simple editor
|
2019-07-21 09:28:07 -07:00
|
|
|
onKeyup(){
|
|
|
|
let vm = this
|
2019-07-29 00:22:47 -07:00
|
|
|
vm.statusText = 'Modified'
|
2019-07-21 09:28:07 -07:00
|
|
|
//Each note, save after 5 seconds, focus lost or 30 characters typed.
|
|
|
|
clearTimeout(vm.editDebounce)
|
|
|
|
vm.editDebounce = setTimeout(() => {
|
|
|
|
vm.save()
|
|
|
|
}, 5000)
|
2019-07-29 00:22:47 -07:00
|
|
|
//Save after 30 keystrokes
|
2019-07-21 09:28:07 -07:00
|
|
|
vm.keyPressesCounter = (vm.keyPressesCounter + 1)
|
|
|
|
if(vm.keyPressesCounter > 30){
|
|
|
|
vm.keyPressesCounter = 0
|
|
|
|
vm.save()
|
|
|
|
}
|
|
|
|
},
|
2019-07-19 13:51:57 -07:00
|
|
|
save(){
|
2019-07-29 00:22:47 -07:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
clearTimeout(this.editDebounce)
|
|
|
|
|
|
|
|
//Don't save note if its hash doesn't change
|
|
|
|
if( this.lastNoteHash == this.hashString(this.noteText) ){
|
|
|
|
setTimeout(() => {
|
|
|
|
resolve(true)
|
|
|
|
return
|
|
|
|
}, 500)
|
|
|
|
}
|
|
|
|
|
|
|
|
const postData = {
|
|
|
|
'noteId':this.currentNoteId,
|
|
|
|
'text': this.noteText,
|
|
|
|
'fancyInput': this.fancyInput,
|
|
|
|
'color': this.color
|
|
|
|
}
|
2019-07-20 16:07:22 -07:00
|
|
|
|
2019-07-29 00:22:47 -07:00
|
|
|
let vm = this
|
|
|
|
//Only save every 1 second
|
|
|
|
clearTimeout(this.saveDebounce)
|
|
|
|
this.saveDebounce = setTimeout(() => {
|
|
|
|
//Only notify user if saving - may help with debugging in the future
|
|
|
|
vm.statusText = 'Saving'
|
2019-07-30 12:21:12 -07:00
|
|
|
axios.post('/api/note/update', postData).then( response => {
|
2019-07-29 00:22:47 -07:00
|
|
|
vm.statusText = 'Saved'
|
|
|
|
vm.updated = Math.round((+new Date)/1000)
|
|
|
|
|
|
|
|
//Update last saved note hash
|
|
|
|
vm.lastNoteHash = vm.hashString(vm.noteText)
|
|
|
|
resolve(true)
|
|
|
|
})
|
|
|
|
}, 500)
|
|
|
|
})
|
2019-07-24 11:06:50 -07:00
|
|
|
|
2019-07-19 13:51:57 -07:00
|
|
|
},
|
|
|
|
hashString(text){
|
|
|
|
var hash = 0;
|
|
|
|
if (text.length == 0) {
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
for (let i = 0; i < text.length; i++) {
|
|
|
|
let char = text.charCodeAt(i);
|
|
|
|
hash = ((hash<<5)-hash)+char;
|
|
|
|
hash = hash & hash; // Convert to 32bit integer
|
|
|
|
}
|
|
|
|
return hash;
|
|
|
|
},
|
|
|
|
close(){
|
2019-07-29 00:22:47 -07:00
|
|
|
this.sizeDown = true
|
|
|
|
this.save().then( () => {
|
|
|
|
this.$bus.$emit('close_active_note', this.position)
|
|
|
|
})
|
|
|
|
|
2019-07-19 13:51:57 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style type="text/css" scoped>
|
|
|
|
|
2019-07-29 00:22:47 -07:00
|
|
|
|
2019-07-21 09:28:07 -07:00
|
|
|
.no-flow {
|
|
|
|
overflow: hidden;
|
|
|
|
}
|
|
|
|
|
|
|
|
.raw-edit {
|
|
|
|
font-family: 'Open Sans' !important;
|
|
|
|
font-size: 1.3rem !important;
|
2019-07-29 00:22:47 -07:00
|
|
|
background: rgba(0,0,0,0);
|
2019-07-21 09:28:07 -07:00
|
|
|
width: 100%;
|
|
|
|
resize: none;
|
|
|
|
padding: 15px;
|
|
|
|
border: 1px solid;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* container styles change based on mobile and number of open screens */
|
2019-07-19 13:51:57 -07:00
|
|
|
.master-note-edit {
|
|
|
|
position: fixed;
|
|
|
|
bottom: 0;
|
2019-07-29 00:22:47 -07:00
|
|
|
background: var(--background_color);
|
|
|
|
/*color: var(--text_color);*/
|
2019-07-21 09:28:07 -07:00
|
|
|
height: 100vh;
|
2019-07-19 13:51:57 -07:00
|
|
|
box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
|
|
|
|
}
|
2019-07-20 16:07:22 -07:00
|
|
|
/* One note open, in the middle of the screen */
|
|
|
|
.master-note-edit.position-0 {
|
|
|
|
left: 30%;
|
|
|
|
right: 1%;
|
|
|
|
}
|
2019-07-21 09:28:07 -07:00
|
|
|
@media only screen and (max-width: 740px) {
|
|
|
|
.master-note-edit.position-0 {
|
|
|
|
left: 0;
|
|
|
|
right: 0;
|
|
|
|
top: 0;
|
|
|
|
bottom: 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-20 16:07:22 -07:00
|
|
|
/* Two Notes Open, each takes up 50% of the space */
|
|
|
|
.master-note-edit.position-1 {
|
|
|
|
left: 50%;
|
|
|
|
right: 0%;
|
|
|
|
}
|
|
|
|
.master-note-edit.position-2 {
|
|
|
|
left: 0%;
|
|
|
|
right: 50%;
|
|
|
|
}
|
2019-07-19 13:51:57 -07:00
|
|
|
|
2019-07-29 00:22:47 -07:00
|
|
|
.size-down {
|
|
|
|
animation: size-down 0.5s linear both;
|
|
|
|
}
|
|
|
|
|
|
|
|
@keyframes size-down {
|
|
|
|
0% {
|
|
|
|
opacity: 1;
|
|
|
|
top: 0;
|
|
|
|
}
|
|
|
|
100% {
|
|
|
|
opacity: 0;
|
|
|
|
top: 405vh;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-19 13:51:57 -07:00
|
|
|
</style>
|