Created a uniform menu for notes that works on mobile
Added list sorting Added shared notes Fixed some little bugs here and there
This commit is contained in:
@@ -17,30 +17,23 @@
|
||||
|
||||
<div class="note-menu">
|
||||
|
||||
<div class="nm-button" v-on:click="close">
|
||||
<i class="close icon"></i>
|
||||
</div>
|
||||
<div class="nm-button" v-on:click="toggleList('ol')">
|
||||
<i class="list ol icon"></i>
|
||||
</div>
|
||||
<div class="nm-button" v-on:click="toggleList('ul')">
|
||||
<i class="tasks icon"></i>
|
||||
</div>
|
||||
<div class="nm-button" v-on:click="toggleBold()">
|
||||
<i class="bold icon"></i>
|
||||
</div>
|
||||
<nm-button v-on:click.native="close" icon="close" />
|
||||
|
||||
<div class="nm-button" v-on:click="toggleItalic()">
|
||||
<i class="quote left icon"></i>
|
||||
</div>
|
||||
<nm-button v-on:click.native="toggleList('ol')" icon="list ol" />
|
||||
|
||||
<nm-button v-on:click.native="toggleList('ul')" icon="tasks" />
|
||||
|
||||
<nm-button v-on:click.native="toggleBold()" icon="bold" />
|
||||
|
||||
<nm-button v-on:click.native="toggleItalic()" icon="quote left" />
|
||||
|
||||
<div class="nm-button" v-on:click="modifyFont('2.286em') ">
|
||||
<i class="text height icon"></i>
|
||||
</div>
|
||||
<nm-button v-on:click.native="modifyFont('1.4em')" icon="text height" />
|
||||
|
||||
<div v-if="usersOnNote > 1" class="nm-button">
|
||||
<i class="green user circle icon"></i>{{usersOnNote}}
|
||||
</div>
|
||||
<nm-button v-on:click.native="undoCustom()" icon="undo" />
|
||||
|
||||
<nm-button v-if="usersOnNote > 1" icon="green user circle" />
|
||||
|
||||
<nm-button icon="ellipsis horizontal" v-on:click.native="showNoteOptions = !showNoteOptions" />
|
||||
|
||||
</div>
|
||||
|
||||
@@ -51,47 +44,136 @@
|
||||
<div class="ui green button">{{statusText}}</div>
|
||||
</span>
|
||||
|
||||
<color-picker
|
||||
v-if="colorPickerVisible"
|
||||
:location="colorPickerLocation"
|
||||
@changeColor="onChangeColor"
|
||||
@close="onCloseColorChanger"
|
||||
:style-object="styleObject"
|
||||
/>
|
||||
|
||||
<!-- Note options on the bottom of note -->
|
||||
<div class="all-settings" :class="{ 'low-settings':!extraToolbarsVisible }">
|
||||
|
||||
<div class="note-menu">
|
||||
|
||||
<!-- <note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/><br> -->
|
||||
<div class="note-menu shrink-icons-on-mobile">
|
||||
|
||||
<!-- Pin Button -->
|
||||
<div @click="onToggleArchived" class="nm-button">
|
||||
<i class="archive icon" :class="{green:(archived == 1)}"></i>
|
||||
{{(archived == 1)?'Archived':'Archive'}}
|
||||
</div>
|
||||
<!-- archive button -->
|
||||
<div @click="onTogglePinned" class="nm-button">
|
||||
<i class="pin icon" :class="{green:(pinned == 1)}"></i>
|
||||
{{(pinned == 1)?'Pinned':'Pin'}}
|
||||
</div>
|
||||
<!-- colors button -->
|
||||
<span class="nm-button" v-on:click="showColorPicker">
|
||||
<i class="paint brush icon"></i>
|
||||
Colors
|
||||
</span>
|
||||
<nm-button
|
||||
v-on:click.native="onToggleArchived"
|
||||
:icon="(archived == 1)?'green archive':'archive'"
|
||||
:text="(archived == 1)?'Archived':'Archive'"
|
||||
tip="Show in archive"
|
||||
:showText="true"
|
||||
></nm-button>
|
||||
|
||||
<!-- archive button -->
|
||||
<nm-button
|
||||
v-on:click.native="onTogglePinned"
|
||||
:icon="(pinned == 1)?'green pin':'pin'"
|
||||
:text="(pinned == 1)?'Pinned':'Pin'"
|
||||
tip="Pin to top of list"
|
||||
:showText="true"
|
||||
></nm-button>
|
||||
|
||||
<!-- colors button -->
|
||||
<nm-button
|
||||
v-on:click.native="showColorPicker"
|
||||
icon="paint brush"
|
||||
text="Colors"
|
||||
tip="Colors"
|
||||
></nm-button>
|
||||
|
||||
<!-- add images panel -->
|
||||
<nm-button
|
||||
v-on:click.native="showFilesSideMenu = !showFilesSideMenu"
|
||||
icon="image"
|
||||
text="Images"
|
||||
tip="Images"
|
||||
></nm-button>
|
||||
|
||||
<!-- Tags -->
|
||||
<nm-button
|
||||
v-on:click.native="showTagSlideMenu = !showTagSlideMenu"
|
||||
icon="tags"
|
||||
text="Tags"
|
||||
tip="Tags"
|
||||
></nm-button>
|
||||
|
||||
<!-- attachment button -->
|
||||
<div class="nm-button" v-on:click="openEditAttachment">
|
||||
<i class="folder icon"></i> Files
|
||||
</div>
|
||||
<!-- file upload button -->
|
||||
<file-upload-button class="nm-button" :noteId="noteid" />
|
||||
<file-upload-button
|
||||
class="nm-button"
|
||||
:noteId="noteid" />
|
||||
|
||||
<!-- files button -->
|
||||
<nm-button
|
||||
v-on:click.native="openEditAttachment"
|
||||
icon="folder"
|
||||
text="Files"
|
||||
tip="Files on Note"
|
||||
:showText="true"
|
||||
></nm-button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="shade" v-on:click="showAllSettings = false"></div> -->
|
||||
|
||||
<!-- Side slide menus for colors, tags and images -->
|
||||
|
||||
<side-slide-menu v-if="colorPickerVisible" v-on:close="colorPickerVisible = false" name="colors">
|
||||
<color-picker
|
||||
@changeColor="onChangeColor"
|
||||
@close="onCloseColorChanger"
|
||||
:style-object="styleObject"
|
||||
/>
|
||||
</side-slide-menu>
|
||||
|
||||
<side-slide-menu v-if="showTagSlideMenu" v-on:close="showTagSlideMenu = false" name="tags">
|
||||
<div class="ui basic segment">
|
||||
<note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/>
|
||||
</div>
|
||||
</side-slide-menu>
|
||||
|
||||
<side-slide-menu v-if="showFilesSideMenu" v-on:close="showFilesSideMenu = false" name="images">
|
||||
<div class="ui basic segment">
|
||||
<simple-attachment-note
|
||||
v-on:close="showFilesSideMenu = false"
|
||||
:note-id="noteid"
|
||||
:squire-editor="editor">
|
||||
</simple-attachment-note>
|
||||
</div>
|
||||
</side-slide-menu>
|
||||
|
||||
<side-slide-menu v-if="showNoteOptions" v-on:close="showNoteOptions = false" name="note-options">
|
||||
<div class="ui basic padded segment">
|
||||
<div class="ui grid">
|
||||
<div class="sixteen wide column">
|
||||
<h2>Additional Note Options</h2>
|
||||
</div>
|
||||
<div class="sixteen wide column">
|
||||
<div class="ui labeled icon fluid basic button" v-on:click="sortList">
|
||||
<i class="sort amount up icon"></i>
|
||||
Sort List items (Move checked to bottom)
|
||||
</div>
|
||||
</div>
|
||||
<div class="eight wide column">
|
||||
<div class="ui labeled icon fluid basic button" v-on:click="deleteCompletedListItems">
|
||||
<i class="trash icon"></i>
|
||||
Delete Checked Items
|
||||
</div>
|
||||
</div>
|
||||
<div class="eight wide column">
|
||||
<div class="ui labeled icon fluid basic button" v-on:click="uncheckAllListItems">
|
||||
<i class="list ul icon"></i>
|
||||
Uncheck all Checked items
|
||||
</div>
|
||||
</div>
|
||||
<div class="sixteen wide column">
|
||||
<div class="ui labeled icon fluid basic button" v-on:click="undoCustom">
|
||||
<i class="undo icon"></i>
|
||||
Undo last change
|
||||
</div>
|
||||
</div>
|
||||
<div class="sixteen wide column" v-if="rawTextId > 0">
|
||||
<share-note-component
|
||||
:note-id="noteid"
|
||||
:raw-text-id="rawTextId"
|
||||
:share-username="shareUsername"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</side-slide-menu>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
@@ -105,10 +187,15 @@
|
||||
name: 'InputNotes',
|
||||
props: [ 'noteid', 'position' ],
|
||||
components:{
|
||||
'note-tag-edit': require('@/components/NoteTagEdit.vue').default,
|
||||
'color-picker': require('@/components/ColorPicker.vue').default,
|
||||
'file-upload-button': require('@/components/FileUploadButton.vue').default,
|
||||
'delete-button': require('@/components/NoteDeleteButtonComponent.vue').default,
|
||||
'note-tag-edit': () => import('@/components/NoteTagEdit.vue'),
|
||||
'color-picker': () => import('@/components/ColorPicker.vue'),
|
||||
'file-upload-button': () => import('@/components/FileUploadButton.vue'),
|
||||
// 'delete-button': () => import('@/components/NoteDeleteButtonComponent.vue'),
|
||||
'side-slide-menu': () => import('@/components/SideSlideMenuComponent.vue'),
|
||||
'simple-attachment-note': () => import('@/components/SimpleAttachmentNoteComponent.vue'),
|
||||
'share-note-component': () => import('@/components/ShareNoteComponent.vue'),
|
||||
|
||||
'nm-button':require('@/components/NoteMenuButtonComponent.vue').default
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
@@ -116,6 +203,8 @@
|
||||
loadingMessage: 'Loading Note',
|
||||
currentNoteId: 0,
|
||||
noteText: '',
|
||||
rawTextId: 0,
|
||||
shareUsername: null,
|
||||
diffNoteText: '',
|
||||
statusText: 'Saved',
|
||||
lastNoteHash: null,
|
||||
@@ -130,7 +219,7 @@
|
||||
styleObject: { 'noteText':null,'noteBackground':null, 'noteIcon':null, 'iconColor':null }, //Style object. Determines colors and badges
|
||||
|
||||
sizeDown: false, //Used to animate close state
|
||||
colorPickerVisible: false,
|
||||
|
||||
colorPickerLocation: null,
|
||||
|
||||
tinymce: null, //Initialized editor instance
|
||||
@@ -145,6 +234,10 @@
|
||||
usersOnNote: 0,
|
||||
|
||||
extraToolbarsVisible: true,
|
||||
showTagSlideMenu: false,
|
||||
colorPickerVisible: false,
|
||||
showFilesSideMenu: false,
|
||||
showNoteOptions: false,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -168,12 +261,13 @@
|
||||
if(this.noteid == noteId && this.editor){
|
||||
this.editor.moveCursorToEnd()
|
||||
this.editor.insertHTML(imageCode)
|
||||
this.save()
|
||||
}
|
||||
})
|
||||
},
|
||||
beforeDestroy(){
|
||||
|
||||
this.$io.emit('leave_room', this.noteid)
|
||||
this.$io.emit('leave_room', this.rawTextId)
|
||||
|
||||
document.removeEventListener('visibilitychange', this.checkForUpdatedNote)
|
||||
|
||||
@@ -189,24 +283,7 @@
|
||||
this.$nextTick(() => {
|
||||
|
||||
this.loadNote(this.noteid)
|
||||
|
||||
//Tell server to push this note into a room
|
||||
this.$io.emit('join_room', this.noteid)
|
||||
|
||||
this.$io.on('update_user_count', userCount => {
|
||||
this.usersOnNote = userCount
|
||||
})
|
||||
|
||||
//Server will hand deliver diffs from other notes to this one
|
||||
this.$io.on('incoming_diff', incomingDiffData => {
|
||||
this.patchText(incomingDiffData)
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
},
|
||||
methods: {
|
||||
initSquire(){
|
||||
@@ -215,7 +292,7 @@
|
||||
this.editor = new Squire( this.$refs.squirebox, {blockTag: 'p' })
|
||||
this.setText(this.noteText)
|
||||
|
||||
//Open links when clicked in editor
|
||||
//Click Event - Open links when clicked in editor or toggle checks
|
||||
this.editor.addEventListener('click', e => {
|
||||
|
||||
//Link clicked in editor - open link
|
||||
@@ -284,6 +361,7 @@
|
||||
},
|
||||
//If nothing is selected, select the entire line
|
||||
selectLineIfNoSelect(){
|
||||
|
||||
//Select entire line if range is not set
|
||||
let selection = this.editor.getSelection()
|
||||
|
||||
@@ -297,6 +375,7 @@
|
||||
}
|
||||
},
|
||||
modifyFont(inSize){
|
||||
|
||||
this.selectLineIfNoSelect()
|
||||
|
||||
let fontInfo = this.editor.getFontInfo()
|
||||
@@ -338,6 +417,149 @@
|
||||
this.editor.italic()
|
||||
}
|
||||
},
|
||||
undoCustom(){
|
||||
//The same as pressing CTRL + Z
|
||||
// this.editor.focus()
|
||||
// document.execCommand("undo", false, null)
|
||||
this.editor.undo()
|
||||
},
|
||||
uncheckAllListItems(){
|
||||
//
|
||||
// Uncheck All List Items
|
||||
//
|
||||
|
||||
//Close menu if user is on mobile, then sort list
|
||||
if(this.$store.getters.getIsUserOnMobile){
|
||||
this.showNoteOptions = false
|
||||
}
|
||||
|
||||
//Fetch the container
|
||||
let container = document.getElementById('squire-id')
|
||||
|
||||
Array.from( container.getElementsByClassName('active') ).forEach(item => {
|
||||
item.classList.remove('active');
|
||||
})
|
||||
},
|
||||
deleteCompletedListItems(){
|
||||
//
|
||||
// Delete Completed List Items
|
||||
//
|
||||
|
||||
//Close menu if user is on mobile, then sort list
|
||||
if(this.$store.getters.getIsUserOnMobile){
|
||||
this.showNoteOptions = false
|
||||
}
|
||||
|
||||
//Fetch the container
|
||||
let container = document.getElementById('squire-id')
|
||||
|
||||
//Go through each item, on first level, look for Unordered Lists
|
||||
container.childNodes.forEach( (node) => {
|
||||
if(node.nodeName == 'UL'){
|
||||
|
||||
//Create two categories, done and not done list items
|
||||
let undoneElements = document.createDocumentFragment()
|
||||
|
||||
//Go through each item in each list we found
|
||||
node.childNodes.forEach( (checkListItem, index) => {
|
||||
|
||||
//Skip Embedded lists, they are handled with the list item above them. Keep lists with intented items together
|
||||
if(checkListItem.nodeName == 'UL'){
|
||||
return
|
||||
}
|
||||
|
||||
//Check if list item has active class
|
||||
const checkedItem = checkListItem.classList.contains('active')
|
||||
|
||||
//Check if the next item is a list, Keep lists with intented items together
|
||||
let sublist = null
|
||||
if(node.childNodes[index+1] && node.childNodes[index+1].nodeName == 'UL'){
|
||||
sublist = node.childNodes[index+1]
|
||||
}
|
||||
|
||||
//Push checked items and their sub lists to the done set
|
||||
if(!checkedItem){
|
||||
|
||||
undoneElements.appendChild( checkListItem.cloneNode(true) )
|
||||
if(sublist){
|
||||
undoneElements.appendChild( sublist.cloneNode(true) )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
//Remove all HTML from node, push unfinished items, then finished below them
|
||||
node.innerHTML = null
|
||||
node.appendChild(undoneElements)
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
sortList(){
|
||||
//
|
||||
// Sort list, checked at the bottom, unchecked at the top
|
||||
//
|
||||
|
||||
//Close menu if user is on mobile, then sort list
|
||||
if(this.$store.getters.getIsUserOnMobile){
|
||||
this.showNoteOptions = false
|
||||
}
|
||||
|
||||
//Fetch the container
|
||||
let container = document.getElementById('squire-id')
|
||||
|
||||
//Go through each item, on first level, look for Unordered Lists
|
||||
container.childNodes.forEach( (node) => {
|
||||
if(node.nodeName == 'UL'){
|
||||
|
||||
//Create two categories, done and not done list items
|
||||
let doneElements = document.createDocumentFragment()
|
||||
let undoneElements = document.createDocumentFragment()
|
||||
|
||||
//Go through each item in each list we found
|
||||
node.childNodes.forEach( (checkListItem, index) => {
|
||||
|
||||
//Skip Embedded lists, they are handled with the list item above them. Keep lists with intented items together
|
||||
if(checkListItem.nodeName == 'UL'){
|
||||
return
|
||||
}
|
||||
|
||||
//Check if list item has active class
|
||||
const checkedItem = checkListItem.classList.contains('active')
|
||||
|
||||
//Check if the next item is a list, Keep lists with intented items together
|
||||
let sublist = null
|
||||
if(node.childNodes[index+1] && node.childNodes[index+1].nodeName == 'UL'){
|
||||
sublist = node.childNodes[index+1]
|
||||
}
|
||||
|
||||
//Push checked items and their sub lists to the done set
|
||||
if(checkedItem){
|
||||
|
||||
doneElements.appendChild( checkListItem.cloneNode(true) )
|
||||
if(sublist){
|
||||
doneElements.appendChild( sublist.cloneNode(true) )
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
undoneElements.appendChild( checkListItem.cloneNode(true) )
|
||||
if(sublist){
|
||||
undoneElements.appendChild( sublist.cloneNode(true) )
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
//Remove all HTML from node, push unfinished items, then finished below them
|
||||
node.innerHTML = null
|
||||
node.appendChild(undoneElements)
|
||||
node.appendChild(doneElements)
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
setText(inText){
|
||||
|
||||
this.editor.setHTML(inText)
|
||||
@@ -349,13 +571,16 @@
|
||||
return this.editor.getHTML()
|
||||
},
|
||||
showColorPicker(event){
|
||||
|
||||
this.colorPickerVisible = !this.colorPickerVisible
|
||||
this.colorPickerLocation = {'x':event.clientX, 'y':event.clientY}
|
||||
},
|
||||
openEditAttachment(){
|
||||
|
||||
this.$router.push('/attachments/note/'+this.currentNoteId)
|
||||
},
|
||||
onTogglePinned(){
|
||||
|
||||
if(this.pinned == 0){
|
||||
this.pinned = 1
|
||||
} else {
|
||||
@@ -366,6 +591,7 @@
|
||||
this.save()
|
||||
},
|
||||
onToggleArchived(){
|
||||
|
||||
if(this.archived == 0){
|
||||
this.archived = 1
|
||||
} else {
|
||||
@@ -376,6 +602,7 @@
|
||||
this.save()
|
||||
},
|
||||
onCloseColorChanger(){
|
||||
|
||||
this.colorPickerVisible = false
|
||||
},
|
||||
onChangeColor(newStyleObject){
|
||||
@@ -403,6 +630,8 @@
|
||||
|
||||
//Set up local data
|
||||
vm.currentNoteId = noteId
|
||||
this.rawTextId = response.data.rawTextId
|
||||
this.shareUsername = response.data.shareUsername
|
||||
|
||||
vm.noteText = response.data.text
|
||||
vm.diffNoteText = response.data.text
|
||||
@@ -423,7 +652,8 @@
|
||||
this.loading = false
|
||||
|
||||
vm.$nextTick(() => {
|
||||
// this.initTinyMce()
|
||||
|
||||
this.setupWebSockets()
|
||||
this.initSquire()
|
||||
})
|
||||
|
||||
@@ -473,8 +703,8 @@
|
||||
|
||||
// console.log(patch_text)
|
||||
this.$io.emit('note_diff', {
|
||||
id:this.currentNoteId,
|
||||
diff:patch_text
|
||||
id: this.rawTextId,
|
||||
diff: patch_text
|
||||
})
|
||||
|
||||
|
||||
@@ -575,9 +805,6 @@
|
||||
|
||||
}, 20)
|
||||
// }
|
||||
|
||||
|
||||
|
||||
},
|
||||
xpath(el) {
|
||||
//Skip things we can't use
|
||||
@@ -592,12 +819,13 @@
|
||||
const sames = [].filter.call(el.parentNode.children, function (x) { return x.tagName == el.tagName })
|
||||
return this.xpath(el.parentNode) + '/' + el.tagName.toLowerCase() + (sames.length > 1 ? '['+([].indexOf.call(sames, el)+1)+']' : '')
|
||||
},
|
||||
getElementByXPath(xpath) {
|
||||
getElementByXPath(xpath){
|
||||
|
||||
return new XPathEvaluator()
|
||||
.createExpression(xpath)
|
||||
.evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE) .singleNodeValue
|
||||
},
|
||||
onKeyup(event){
|
||||
onKeyup(event){
|
||||
|
||||
this.statusText = 'Save'
|
||||
|
||||
@@ -661,7 +889,6 @@
|
||||
})
|
||||
// }, 300)
|
||||
})
|
||||
|
||||
},
|
||||
checkForUpdatedNote(){
|
||||
|
||||
@@ -669,7 +896,7 @@
|
||||
|
||||
//If user leaves page then returns to page, reload the first batch
|
||||
if(this.lastVisibilityState == 'hidden' && document.visibilityState == 'visible'){
|
||||
console.log('Checking for note updates after visibility change.')
|
||||
// console.log('Checking for note updates after visibility change.')
|
||||
|
||||
const postData = {
|
||||
noteId:this.currentNoteId,
|
||||
@@ -726,7 +953,19 @@
|
||||
return
|
||||
}, 300)
|
||||
})
|
||||
},
|
||||
setupWebSockets(){
|
||||
//Tell server to push this note into a room
|
||||
this.$io.emit('join_room', this.rawTextId)
|
||||
|
||||
this.$io.on('update_user_count', userCount => {
|
||||
this.usersOnNote = userCount
|
||||
})
|
||||
|
||||
//Server will hand deliver diffs from other notes to this one
|
||||
this.$io.on('incoming_diff', incomingDiffData => {
|
||||
this.patchText(incomingDiffData)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -734,38 +973,6 @@
|
||||
|
||||
<style type="text/css" scoped>
|
||||
|
||||
/* squire note menu button */
|
||||
|
||||
.note-menu {
|
||||
width: 100%;
|
||||
display: block;
|
||||
background: #221f2b;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
border: 1px solid #534c68;
|
||||
}
|
||||
.note-menu > .nm-button {
|
||||
padding: 10px 10px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
font-size: 1.1em;
|
||||
vertical-align: middle;
|
||||
/*height: 40px;*/
|
||||
display: table-cell;
|
||||
}
|
||||
.nm-button > i {
|
||||
font-size: 1.1em;
|
||||
margin: 0;
|
||||
}
|
||||
.nm-button:hover {
|
||||
background-color: #534c68;
|
||||
color: white;
|
||||
}
|
||||
.nm-button + .nm-button {
|
||||
border-left: 1px solid #534c68;
|
||||
}
|
||||
|
||||
/* squire styles */
|
||||
|
||||
/*Settings manager styles */
|
||||
|
Reference in New Issue
Block a user