Cleaned up some display issues that probably still need work
Added some colors to the notes and basic support for displaying the colors on the main list Added a toggle to disable the fancy text editor and just use a basic textarea Added some mobile styles with much better support for smaller screens Added tag suggestions based on user input, excluding tags from current note, only using tags user has put into system Cleaned and refactored a bunch of stuff
This commit is contained in:
parent
e6c16f3d37
commit
e52ae65a42
@ -28,7 +28,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
|
|||||||
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
|
{ from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
hot: true,
|
// hot: true,
|
||||||
contentBase: false, // since we use CopyWebpackPlugin.
|
contentBase: false, // since we use CopyWebpackPlugin.
|
||||||
compress: true,
|
compress: true,
|
||||||
host: HOST || config.dev.host,
|
host: HOST || config.dev.host,
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
/*body, h3, h2, h1, p {
|
||||||
|
color: #3d3d3d;
|
||||||
|
}*/
|
||||||
|
|
||||||
.clickable {
|
.clickable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@ -6,5 +10,9 @@
|
|||||||
font-family: 'Open Sans' !important;
|
font-family: 'Open Sans' !important;
|
||||||
font-size: 1.3rem !important;
|
font-size: 1.3rem !important;
|
||||||
background: white;
|
background: white;
|
||||||
height: calc(100% - 177px);
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.ui.white.button {
|
||||||
|
background: #FFF;
|
||||||
}
|
}
|
@ -1,21 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="InputNotes" class="master-note-edit" :class="[ 'position-'+position ]" @keyup.esc="close">
|
<div id="InputNotes" class="master-note-edit" :class="[ 'position-'+position ]" @keyup.esc="close" :style="{'background-color':color}">
|
||||||
|
|
||||||
|
|
||||||
|
<div v-if="fancyInput == 1" class="textarea-height no-flow">
|
||||||
<ckeditor ref="main-edit"
|
<ckeditor ref="main-edit"
|
||||||
:editor="editor" @ready="onReady" v-model="noteText" :config="editorConfig" v-on:blur="save"></ckeditor>
|
: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 class="ui buttons">
|
||||||
<div class="ui green button">{{statusText}}</div>
|
<div class="ui right floated green button">{{statusText}}</div>
|
||||||
<div class="ui button">Delete</div>
|
<div class="ui button">Delete</div>
|
||||||
<div v-on:click="close" class="ui button">Close (ESC)</div>
|
<div @click="close" class="ui button">Close (ESC)</div>
|
||||||
|
<div @click="onToggleFancyInput" class="ui button">
|
||||||
|
Fancy ({{fancyInput?'On':'Off'}})
|
||||||
</div>
|
</div>
|
||||||
<p>
|
</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>
|
||||||
|
<!-- <p>
|
||||||
Last Updated: {{$helpers.timeAgo(updated)}}
|
Last Updated: {{$helpers.timeAgo(updated)}}
|
||||||
</p>
|
</p> -->
|
||||||
|
|
||||||
<div class="ui segment">
|
|
||||||
<note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/>
|
<note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ui segment" v-if="false">
|
<div class="ui segment" v-if="false">
|
||||||
Block formatting
|
Block formatting
|
||||||
@ -62,6 +90,8 @@
|
|||||||
updated: 'Never',
|
updated: 'Never',
|
||||||
editDebounce: null,
|
editDebounce: null,
|
||||||
keyPressesCounter: 0,
|
keyPressesCounter: 0,
|
||||||
|
fancyInput: 0, //Default to basic text edit. Upgrade if set to 1
|
||||||
|
color: '#FFF',
|
||||||
|
|
||||||
editor: DecoupledEditor,
|
editor: DecoupledEditor,
|
||||||
editorConfig: {
|
editorConfig: {
|
||||||
@ -97,18 +127,45 @@
|
|||||||
this.loadNote(this.noteid)
|
this.loadNote(this.noteid)
|
||||||
},
|
},
|
||||||
methods: {
|
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']
|
||||||
|
|
||||||
|
if(this.color == "rgb(255, 255, 255)" || this.color == '#FFF'){
|
||||||
|
this.color = null
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lastNoteHash = 0 //Update hash to force note update on next save
|
||||||
|
this.save()
|
||||||
|
},
|
||||||
loadNote(noteId){
|
loadNote(noteId){
|
||||||
let vm = this
|
let vm = this
|
||||||
//Component is activated with NoteId in place, lookup text with associated ID
|
//Component is activated with NoteId in place, lookup text with associated ID
|
||||||
if(this.$store.getters.getLoggedIn){
|
if(this.$store.getters.getLoggedIn){
|
||||||
axios.post('/api/notes/get', {'noteId': noteId})
|
axios.post('/api/notes/get', {'noteId': noteId})
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
||||||
//Set up local data
|
//Set up local data
|
||||||
vm.currentNoteId = noteId
|
vm.currentNoteId = noteId
|
||||||
vm.noteText = response.data.text
|
vm.noteText = response.data.text
|
||||||
vm.updated = response.data.updated
|
vm.updated = response.data.updated
|
||||||
vm.lastNoteHash = vm.hashString(response.data.text)
|
vm.lastNoteHash = vm.hashString(response.data.text)
|
||||||
|
|
||||||
|
if(response.data.raw_input == 1){
|
||||||
|
this.fancyInput = 1
|
||||||
|
}
|
||||||
|
|
||||||
//Put focus on note, at the end of the note text
|
//Put focus on note, at the end of the note text
|
||||||
vm.$nextTick(() => {
|
vm.$nextTick(() => {
|
||||||
// vm.$refs['custom-input'].focus()
|
// vm.$refs['custom-input'].focus()
|
||||||
@ -138,17 +195,7 @@
|
|||||||
//Insert 5 spaces when tab is pressed
|
//Insert 5 spaces when tab is pressed
|
||||||
viewDocument.on( 'keyup', ( evt, data ) => {
|
viewDocument.on( 'keyup', ( evt, data ) => {
|
||||||
|
|
||||||
//Each note, save after 5 seconds, focus lost or 30 characters typed.
|
vm.onKeyup(event)
|
||||||
clearTimeout(vm.editDebounce)
|
|
||||||
vm.editDebounce = setTimeout(() => {
|
|
||||||
vm.save()
|
|
||||||
}, 5000)
|
|
||||||
//Save after 20 keystrokes
|
|
||||||
vm.keyPressesCounter = (vm.keyPressesCounter + 1)
|
|
||||||
if(vm.keyPressesCounter > 30){
|
|
||||||
vm.keyPressesCounter = 0
|
|
||||||
vm.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
//Optional data bindings for tab key
|
//Optional data bindings for tab key
|
||||||
if( (data.keyCode == 9) && viewDocument.isFocused ){
|
if( (data.keyCode == 9) && viewDocument.isFocused ){
|
||||||
@ -163,19 +210,34 @@
|
|||||||
|
|
||||||
} );
|
} );
|
||||||
},
|
},
|
||||||
|
onKeyup(){
|
||||||
|
let vm = this
|
||||||
|
//Each note, save after 5 seconds, focus lost or 30 characters typed.
|
||||||
|
clearTimeout(vm.editDebounce)
|
||||||
|
vm.editDebounce = setTimeout(() => {
|
||||||
|
vm.save()
|
||||||
|
}, 5000)
|
||||||
|
//Save after 20 keystrokes
|
||||||
|
vm.keyPressesCounter = (vm.keyPressesCounter + 1)
|
||||||
|
if(vm.keyPressesCounter > 30){
|
||||||
|
vm.keyPressesCounter = 0
|
||||||
|
vm.save()
|
||||||
|
}
|
||||||
|
},
|
||||||
save(){
|
save(){
|
||||||
|
|
||||||
clearTimeout(this.editDebounce)
|
clearTimeout(this.editDebounce)
|
||||||
|
|
||||||
//Don't save note if its hash doesn't change
|
//Don't save note if its hash doesn't change
|
||||||
if(this.lastNoteHash == this.hashString(this.noteText)){
|
if( this.lastNoteHash == this.hashString(this.noteText) ){
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const postData = {
|
const postData = {
|
||||||
'noteId':this.currentNoteId,
|
'noteId':this.currentNoteId,
|
||||||
'text': this.noteText
|
'text': this.noteText,
|
||||||
|
'fancyInput': this.fancyInput,
|
||||||
|
'color': this.color
|
||||||
}
|
}
|
||||||
|
|
||||||
let vm = this
|
let vm = this
|
||||||
@ -203,6 +265,7 @@
|
|||||||
return hash;
|
return hash;
|
||||||
},
|
},
|
||||||
close(){
|
close(){
|
||||||
|
this.save()
|
||||||
this.$bus.$emit('close_active_note', this.position)
|
this.$bus.$emit('close_active_note', this.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,11 +274,30 @@
|
|||||||
|
|
||||||
<style type="text/css" scoped>
|
<style type="text/css" scoped>
|
||||||
|
|
||||||
|
.textarea-height {
|
||||||
|
height: calc(100% - 177px);
|
||||||
|
}
|
||||||
|
.no-flow {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.raw-edit {
|
||||||
|
font-family: 'Open Sans' !important;
|
||||||
|
font-size: 1.3rem !important;
|
||||||
|
background: white;
|
||||||
|
width: 100%;
|
||||||
|
resize: none;
|
||||||
|
padding: 15px;
|
||||||
|
border: 1px solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* container styles change based on mobile and number of open screens */
|
||||||
.master-note-edit {
|
.master-note-edit {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
background: white;
|
background: white;
|
||||||
height: 99vh;
|
height: 100vh;
|
||||||
box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
|
box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
|
||||||
}
|
}
|
||||||
/* One note open, in the middle of the screen */
|
/* One note open, in the middle of the screen */
|
||||||
@ -223,6 +305,15 @@
|
|||||||
left: 30%;
|
left: 30%;
|
||||||
right: 1%;
|
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 */
|
/* Two Notes Open, each takes up 50% of the space */
|
||||||
.master-note-edit.position-1 {
|
.master-note-edit.position-1 {
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="ui icon large label" v-for="tag in tags">
|
<div class="ui icon large label" v-for="tag in tags" :class="{ 'green':(newTagInput == tag.text) }">
|
||||||
{{ucWords(tag.text)}} <i class="delete icon" v-on:click="removeTag(tag.id)"></i>
|
{{ucWords(tag.text)}} <i class="delete icon" v-on:click="removeTag(tag.id)"></i>
|
||||||
</div>
|
</div>
|
||||||
<input v-model="newTagInput" v-on:keyup.enter="addTag" placeholder="Add Tag" />
|
<div class="ui form">
|
||||||
<div class="ui divider"></div>
|
<input v-model="newTagInput" placeholder="Add Tag"
|
||||||
|
v-on:keydown="tagInput"
|
||||||
|
v-on:keyup="onKeyup"
|
||||||
|
v-on:blur="onBlur"
|
||||||
|
/>
|
||||||
|
<div class="suggestion-box" v-if="suggestions.length > 0">
|
||||||
|
<div class="suggestion-item" v-for="(item, index) in suggestions" :class="{ 'active':(index == selection) }" v-on:click="onClickTag(index)">
|
||||||
|
{{ucWords(item.text)}} <span class="suggestion-tip" v-if="index == selection">Press Enter to add</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -18,7 +28,11 @@
|
|||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
tags: null,
|
tags: null,
|
||||||
newTagInput: ''
|
newTagInput: '',
|
||||||
|
typeDebounce: null,
|
||||||
|
|
||||||
|
suggestions: [],
|
||||||
|
selection: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
beforeMount(){
|
beforeMount(){
|
||||||
@ -37,9 +51,80 @@
|
|||||||
vm.tags = response.data
|
vm.tags = response.data
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
addTag(event){
|
tagInput(event){
|
||||||
|
let vm = this
|
||||||
|
|
||||||
|
if(this.newTagInput.length == 0){
|
||||||
|
this.clearSuggestions()
|
||||||
|
}
|
||||||
|
|
||||||
|
//Cancel any of the following key events
|
||||||
|
const code = event.keyCode
|
||||||
|
if([38, 40, 13, 9].includes(code)){
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
|
||||||
|
//Up - 38 - Go Up suggestion
|
||||||
|
if(code == 40){
|
||||||
|
this.selection ++
|
||||||
|
if(this.selection >= this.suggestions.length){
|
||||||
|
this.selection = -1 //No selection made
|
||||||
|
}
|
||||||
|
console.log('Current Selection Index: ', this.selection)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//Down - 40 - Go Down Suggestion
|
||||||
|
if(code == 38){
|
||||||
|
this.selection --
|
||||||
|
if(this.selection < -1){
|
||||||
|
this.selection = this.suggestions.length -1 //No selection made
|
||||||
|
}
|
||||||
|
console.log('Current Selection Index: ', this.selection)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Enter - 13 - Execute addTag() function
|
||||||
|
if(code == 13){
|
||||||
|
//If an item from list is selected, make that the text
|
||||||
|
if(this.selection > -1){
|
||||||
|
this.newTagInput = this.suggestions[vm.selection].text
|
||||||
|
}
|
||||||
|
this.addTag()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
//Tab - 9 - Fill space with suggestion
|
||||||
|
|
||||||
|
//Anything else, perform search
|
||||||
|
clearTimeout(this.typeDebounce)
|
||||||
|
this.typeDebounce = setTimeout( () => {
|
||||||
|
if(event.target.value.length > 0){
|
||||||
|
|
||||||
|
const postData = {
|
||||||
|
'tagText':vm.newTagInput,
|
||||||
|
'noteId':vm.noteId
|
||||||
|
}
|
||||||
|
|
||||||
|
axios.post('/api/tags/suggest', postData)
|
||||||
|
.then(response => {
|
||||||
|
|
||||||
|
vm.suggestions = response.data
|
||||||
|
vm.selection = -1 //Nothing selected
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
|
},
|
||||||
|
onClickTag(index){
|
||||||
|
this.newTagInput = this.suggestions[index].text
|
||||||
|
this.addTag()
|
||||||
|
},
|
||||||
|
addTag(){
|
||||||
|
|
||||||
|
//Don't add blank or empty tags
|
||||||
|
if(this.newTagInput == ''){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//post to -> /api/addtagtonote
|
|
||||||
let postData = {
|
let postData = {
|
||||||
'tagText':this.newTagInput,
|
'tagText':this.newTagInput,
|
||||||
'noteId':this.noteId
|
'noteId':this.noteId
|
||||||
@ -48,9 +133,21 @@
|
|||||||
axios.post('/api/tags/addtonote', postData)
|
axios.post('/api/tags/addtonote', postData)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
vm.newTagInput = ''
|
vm.newTagInput = ''
|
||||||
|
vm.clearSuggestions()
|
||||||
vm.getTags()
|
vm.getTags()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
onKeyup(){
|
||||||
|
if(this.newTagInput == ''){
|
||||||
|
this.clearSuggestions()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onBlur(){
|
||||||
|
let vm = this
|
||||||
|
setTimeout(i => {
|
||||||
|
vm.clearSuggestions()
|
||||||
|
}, 200)
|
||||||
|
},
|
||||||
removeTag(tagId){
|
removeTag(tagId){
|
||||||
|
|
||||||
let postData = {
|
let postData = {
|
||||||
@ -63,6 +160,10 @@
|
|||||||
vm.getTags()
|
vm.getTags()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
clearSuggestions(){
|
||||||
|
this.suggestions = []
|
||||||
|
this.selection = -1 //No selections
|
||||||
|
},
|
||||||
ucWords(str){
|
ucWords(str){
|
||||||
return (str + '')
|
return (str + '')
|
||||||
.replace(/^(.)|\s+(.)/g, function ($1) {
|
.replace(/^(.)|\s+(.)/g, function ($1) {
|
||||||
@ -72,3 +173,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<style type="text/css" scoped>
|
||||||
|
.suggestion-box {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 38px;
|
||||||
|
width: 300px;
|
||||||
|
box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
|
||||||
|
}
|
||||||
|
.suggestion-item {
|
||||||
|
width: 100%;
|
||||||
|
height: 40px;
|
||||||
|
padding: 10px 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
.suggestion-item.active {
|
||||||
|
background: green;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.suggestion-item + .suggestion-item {
|
||||||
|
border-top: 1px solid #DDD;
|
||||||
|
}
|
||||||
|
.suggestion-tip {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
</style>
|
42
client/src/components/NoteTitleDisplayCard.vue
Normal file
42
client/src/components/NoteTitleDisplayCard.vue
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<div class="ui clickable segment" @click="onClick(note.id)" :style="{'background-color':color, 'color':fontColor}">
|
||||||
|
<h3>{{note.text}}</h3>
|
||||||
|
<p>Edited: {{$helpers.timeAgo(note.updated)}}</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'NoteTitleDisplayCard',
|
||||||
|
props: [ 'onClick', 'data' ],
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
note: null,
|
||||||
|
color: '#FFF',
|
||||||
|
fontColor: '#000'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeMount(){
|
||||||
|
this.note = this.data
|
||||||
|
if(this.note.color != null && this.note.color != '#FFF'){
|
||||||
|
this.color = this.note.color
|
||||||
|
this.fontColor = '#FFF'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
yup(){
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style type="text/css" scoped>
|
||||||
|
.suggestion-box {
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
@ -2,25 +2,45 @@
|
|||||||
<div>
|
<div>
|
||||||
|
|
||||||
|
|
||||||
<div class="ui grid">
|
<div class="ui equal width grid">
|
||||||
|
|
||||||
|
<!-- mobile search menu -->
|
||||||
|
<div class="ui mobile only row">
|
||||||
|
<!-- Small screen new note button -->
|
||||||
|
<div class="ui four wide column">
|
||||||
|
<div @click="createNote" class="ui fluid green icon button">
|
||||||
|
<i class="plus icon"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ui twelve wide column">
|
||||||
|
<div class="ui form">
|
||||||
|
<input v-model="searchTerm" @keyup="searchKeyUp" @:keyup.enter="search" placeholder="Search Notes" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- search menu -->
|
<!-- search menu -->
|
||||||
<div class="ui row">
|
<div class="ui large screen only row">
|
||||||
|
|
||||||
<div class="ui two wide column">
|
<div class="ui two wide column">
|
||||||
<div @click="createNote" class="ui fluid green button">
|
<div @click="createNote" class="ui fluid green button">
|
||||||
<i class="plus icon"></i>
|
<i class="plus icon"></i>
|
||||||
New Note
|
New Note
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui five wide column">
|
<div class="ui five wide column">
|
||||||
<div class="ui form">
|
<div class="ui form">
|
||||||
<input v-model="searchTerm" @keyup="searchKeyUp" @:keyup.enter="search" placeholder="Search Notes" />
|
<input v-model="searchTerm" @keyup="searchKeyUp" @:keyup.enter="search" placeholder="Search Notes" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="ui row">
|
<div class="ui row">
|
||||||
<div class="ui two wide column">
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- tags display -->
|
||||||
|
<div class="ui two wide large screen only column">
|
||||||
<div class="ui basic fluid button" @click="reset">Reset</div>
|
<div class="ui basic fluid button" @click="reset">Reset</div>
|
||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui clickable basic fluid large label" v-for="tag in commonTags" @click="toggleTagFilter(tag.id)"
|
<div class="ui clickable basic fluid large label" v-for="tag in commonTags" @click="toggleTagFilter(tag.id)"
|
||||||
@ -28,13 +48,17 @@
|
|||||||
{{ucWords(tag.text)}} <div class="detail">{{tag.usages}}</div>
|
{{ucWords(tag.text)}} <div class="detail">{{tag.usages}}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ui fourteen wide column">
|
|
||||||
|
<!-- Note title cards -->
|
||||||
|
<div class="ui fourteen wide computer sixteen wide mobile column">
|
||||||
<h2>Notes ({{notes.length}})</h2>
|
<h2>Notes ({{notes.length}})</h2>
|
||||||
<div v-if="notes !== null">
|
<div v-if="notes !== null">
|
||||||
<div class="ui vertical segment clickable" v-for="note in notes" v-on:click="openNote(note.id)">
|
<note-title-display-card
|
||||||
<h3>{{note.text}}</h3>
|
v-for="note in notes"
|
||||||
<p>Edited: {{$helpers.timeAgo(note.updated)}}</p>
|
:onClick="openNote"
|
||||||
</div>
|
:data="note"
|
||||||
|
:key="note.id + note.color"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -55,6 +79,7 @@
|
|||||||
name: 'SearchBar',
|
name: 'SearchBar',
|
||||||
components: {
|
components: {
|
||||||
'input-notes': require('./InputNotes.vue').default,
|
'input-notes': require('./InputNotes.vue').default,
|
||||||
|
'note-title-display-card': require('./NoteTitleDisplayCard.vue').default,
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
@ -16,13 +16,13 @@ Notes.create = (userId, noteText) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Notes.update = (userId, noteId, noteText) => {
|
Notes.update = (userId, noteId, noteText, fancyInput, color) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
const now = Math.round((+new Date)/1000)
|
const now = Math.round((+new Date)/1000)
|
||||||
|
|
||||||
db.promise()
|
db.promise()
|
||||||
.query('UPDATE notes SET text = ?, updated = ? WHERE id = ? AND user = ? LIMIT 1', [noteText, now, noteId, userId])
|
.query('UPDATE notes SET text = ?, raw_input = ?, updated = ?, color = ? WHERE id = ? AND user = ? LIMIT 1', [noteText, fancyInput, now, color, noteId, userId])
|
||||||
.then((rows, fields) => {
|
.then((rows, fields) => {
|
||||||
resolve(rows[0])
|
resolve(rows[0])
|
||||||
})
|
})
|
||||||
@ -39,7 +39,7 @@ Notes.delete = (userId, noteId) => {
|
|||||||
Notes.get = (userId, noteId) => {
|
Notes.get = (userId, noteId) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
db.promise()
|
db.promise()
|
||||||
.query('SELECT text, updated FROM notes WHERE user = ? AND id = ? LIMIT 1', [userId,noteId])
|
.query('SELECT text, updated, raw_input FROM notes WHERE user = ? AND id = ? LIMIT 1', [userId,noteId])
|
||||||
.then((rows, fields) => {
|
.then((rows, fields) => {
|
||||||
resolve(rows[0][0])
|
resolve(rows[0][0])
|
||||||
})
|
})
|
||||||
@ -64,7 +64,7 @@ Notes.search = (userId, searchQuery, searchTags) => {
|
|||||||
|
|
||||||
//Default note lookup gets all notes
|
//Default note lookup gets all notes
|
||||||
let noteSearchQuery = `
|
let noteSearchQuery = `
|
||||||
SELECT notes.id, SUBSTRING(text, 1, 200) as text, updated
|
SELECT notes.id, SUBSTRING(text, 1, 200) as text, updated, color
|
||||||
FROM notes
|
FROM notes
|
||||||
LEFT JOIN notes_tags ON (notes.id = notes_tags.note_id)
|
LEFT JOIN notes_tags ON (notes.id = notes_tags.note_id)
|
||||||
WHERE user = ?`
|
WHERE user = ?`
|
||||||
|
@ -106,3 +106,26 @@ Tags.lookup = (tagText) => {
|
|||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Suggest note tags - don't suggest tags already on note
|
||||||
|
Tags.suggest = (userId, noteId, tagText) => {
|
||||||
|
|
||||||
|
tagText += '%'
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
db.promise()
|
||||||
|
.query(`SELECT text FROM notes_tags
|
||||||
|
JOIN tags ON notes_tags.tag_id = tags.id
|
||||||
|
WHERE notes_tags.user_id = ?
|
||||||
|
AND tags.text LIKE ?
|
||||||
|
AND notes_tags.tag_id NOT IN (
|
||||||
|
SELECT notes_tags.tag_id FROM notes_tags WHERE notes_tags.note_id = ?
|
||||||
|
)
|
||||||
|
GROUP BY text
|
||||||
|
LIMIT 6;`, [userId, tagText, noteId])
|
||||||
|
.then((rows, fields) => {
|
||||||
|
resolve(rows[0]) //Return new ID
|
||||||
|
})
|
||||||
|
.catch(console.log)
|
||||||
|
})
|
||||||
|
}
|
@ -30,7 +30,7 @@ router.post('/create', function (req, res) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.post('/update', function (req, res) {
|
router.post('/update', function (req, res) {
|
||||||
Notes.update(userId, req.body.noteId, req.body.text)
|
Notes.update(userId, req.body.noteId, req.body.text, req.body.fancyInput, req.body.color)
|
||||||
.then( id => res.send({id}) )
|
.then( id => res.send({id}) )
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -13,6 +13,12 @@ router.use(function setUserId (req, res, next) {
|
|||||||
next()
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
//Get the latest notes the user has created
|
||||||
|
router.post('/suggest', function (req, res) {
|
||||||
|
Tags.suggest(userId, req.body.noteId, req.body.tagText)
|
||||||
|
.then( data => res.send(data) )
|
||||||
|
})
|
||||||
|
|
||||||
//Get the latest notes the user has created
|
//Get the latest notes the user has created
|
||||||
router.post('/addtonote', function (req, res) {
|
router.post('/addtonote', function (req, res) {
|
||||||
Tags.addToNote(userId, req.body.noteId, req.body.tagText.toLowerCase())
|
Tags.addToNote(userId, req.body.noteId, req.body.tagText.toLowerCase())
|
||||||
|
Loading…
Reference in New Issue
Block a user