SolidScribe/client/src/components/NoteInputPanel.vue

352 lines
8.9 KiB
Vue
Raw Normal View History

<template>
<div
id="InputNotes" class="master-note-edit"
@keyup.esc="close"
:class="[{'size-down':(sizeDown == true)}, 'position-'+position ]"
:style="{'background-color':color, 'color':fontColor}"
>
<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>
<!-- <p>
Last Updated: {{$helpers.timeAgo(updated)}}
</p> -->
<note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/>
</div>
</template>
<script>
import axios from 'axios'
import DecoupledEditor from '@ckeditor/ckeditor5-build-decoupled-document';
export default {
name: 'InputNotes',
props: [ 'noteid', 'position' ],
components:{
'note-tag-edit': require('@/components/NoteTagEdit.vue').default
},
data(){
return {
currentNoteId: 0,
noteText: '',
statusText: 'Saved',
lastNoteHash: null,
saveDebounce: null, //Prevent save from being called numerous times quickly
lastSaved: 0,
updated: 'Never',
editDebounce: null,
keyPressesCounter: 0,
fancyInput: 0, //Default to basic text edit. Upgrade if set to 1
color: '#FFF',
fontColor: '#000',
sizeDown: false,
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: {
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']
this.fontColor = '#FFF'
if(this.color == "rgb(255, 255, 255)" || this.color == '#FFF'){
this.color = null
this.fontColor = null
}
this.lastNoteHash = 0 //Update hash to force note update on next save
this.save()
},
loadNote(noteId){
let vm = this
//Component is activated with NoteId in place, lookup text with associated ID
if(this.$store.getters.getLoggedIn){
axios.post('/api/notes/get', {'noteId': noteId})
.then(response => {
//Set up local data
vm.currentNoteId = noteId
vm.noteText = response.data.text
vm.updated = response.data.updated
vm.lastNoteHash = vm.hashString(response.data.text)
vm.color = response.data.color
this.fontColor = '#FFF'
if(this.color == "rgb(255, 255, 255)" || this.color == '#FFF' || this.color == null){
this.color = null
this.fontColor = null
}
if(response.data.raw_input == 1){
this.fancyInput = 1
}
//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 ) => {
vm.onKeyup(event)
//Optional data bindings for tab key
if( (data.keyCode == 9) && viewDocument.isFocused ){
//Insert 5 spaces to simulate tab
//editor.execute( 'input', { text: " " } );
evt.stop(); // Prevent executing the default handler.
data.preventDefault();
view.scrollToTheSelection();
}
} );
},
//Used by simple editor
onKeyup(){
let vm = this
vm.statusText = 'Modified'
//Each note, save after 5 seconds, focus lost or 30 characters typed.
clearTimeout(vm.editDebounce)
vm.editDebounce = setTimeout(() => {
vm.save()
}, 5000)
//Save after 30 keystrokes
vm.keyPressesCounter = (vm.keyPressesCounter + 1)
if(vm.keyPressesCounter > 30){
vm.keyPressesCounter = 0
vm.save()
}
},
save(){
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
}
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'
axios.post('/api/notes/update', postData).then( response => {
vm.statusText = 'Saved'
vm.updated = Math.round((+new Date)/1000)
//Update last saved note hash
vm.lastNoteHash = vm.hashString(vm.noteText)
resolve(true)
})
}, 500)
})
},
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(){
this.sizeDown = true
this.save().then( () => {
this.$bus.$emit('close_active_note', this.position)
})
}
}
}
</script>
<style type="text/css" scoped>
.no-flow {
overflow: hidden;
}
.raw-edit {
font-family: 'Open Sans' !important;
font-size: 1.3rem !important;
background: rgba(0,0,0,0);
width: 100%;
resize: none;
padding: 15px;
border: 1px solid;
}
/* container styles change based on mobile and number of open screens */
.master-note-edit {
position: fixed;
bottom: 0;
background: var(--background_color);
/*color: var(--text_color);*/
height: 100vh;
box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
}
/* One note open, in the middle of the screen */
.master-note-edit.position-0 {
left: 30%;
right: 1%;
}
@media only screen and (max-width: 740px) {
.master-note-edit.position-0 {
left: 0;
right: 0;
top: 0;
bottom: 0;
}
}
/* 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%;
}
.size-down {
animation: size-down 0.5s linear both;
}
@keyframes size-down {
0% {
opacity: 1;
top: 0;
}
100% {
opacity: 0;
top: 405vh;
}
}
</style>