1453 lines
39 KiB
Vue
1453 lines
39 KiB
Vue
<template>
|
|
<!-- change class to .master-note-edit to have it popup on the screen.
|
|
@keyup.esc="closeButtonAction()" -->
|
|
<div class="master-note-edit">
|
|
|
|
<!-- Edit Menus -->
|
|
|
|
<div class="menu-top-half" :class="{ 'hide-text':(openNotes > 1) }">
|
|
|
|
<div class="edit-button" v-on:click="colorpicker = true" data-tooltip="Text Color" data-position="bottom center">
|
|
<i class="font icon"></i>
|
|
<div class="font-color-bar" :style="{'background':lastUsedColor}"></div>
|
|
<span>Color</span>
|
|
</div>
|
|
|
|
<div class="edit-button" v-on:click="toggleBold()" :data-tooltip="`Bold\n(CTRL + b)`" data-position="bottom center" :class="{'edit-active':activeBold}">
|
|
<i class="bold icon"></i>
|
|
<span>Bold</span>
|
|
</div>
|
|
|
|
<div class="edit-button" v-on:click="toggleItalic()" :data-tooltip="`Italic\n(CRTL + i)`" data-position="bottom center" :class="{'edit-active':activeItalics}">
|
|
<i class="italic icon"></i>
|
|
<span>Italic</span>
|
|
</div>
|
|
|
|
<div class="edit-button" v-on:click="toggleUnderline()" :data-tooltip="`Underline\n(CRTL + u)`" data-position="bottom center" :class="{'edit-active':activeUnderline}">
|
|
<i class="underline icon"></i>
|
|
<span>Underline</span>
|
|
</div>
|
|
|
|
<div class="edit-button" v-on:click="modifyCode('1.4em')" data-tooltip="Quote" data-position="bottom center" :class="{'edit-active':activeCode}">
|
|
<i class="quote right icon"></i>
|
|
<span>Quote</span>
|
|
</div>
|
|
|
|
<div class="edit-button" v-on:click="modifyFont('0.9em')" data-tooltip="Sub Title" data-position="bottom center" :class="{'edit-active':activeSubTitle}">
|
|
<i class="small text height icon"></i>
|
|
<span>Small Text</span>
|
|
</div>
|
|
|
|
<div class="edit-button" v-on:click="modifyFont('1.4em')" data-tooltip="Title" data-position="bottom center" :class="{'edit-active':activeTitle}">
|
|
<i class="text height icon"></i>
|
|
<span>Title</span>
|
|
</div>
|
|
|
|
<div class="edit-button" v-on:click="removeFormatting()" data-tooltip="Remove Formatting" data-position="bottom center">
|
|
<i class="remove format icon"></i>
|
|
</div>
|
|
|
|
<div class="edit-divide"></div>
|
|
|
|
<div class="edit-button" v-on:click="indentText" :data-tooltip="`Indent\n(TAB)`" data-position="bottom center">
|
|
<i class="indent icon"></i>
|
|
<span>Indent</span>
|
|
</div>
|
|
<div class="edit-button" v-on:click="outdentText" :data-tooltip="`Un-Indent\n(SHIFT + TAB)`" data-position="bottom center">
|
|
<i class="outdent icon"></i>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="menu-bottom-half" :class="{ 'hide-text':(openNotes > 1) }">
|
|
|
|
<!--
|
|
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/table`)" data-tooltip="Insert Table" data-position="bottom center">
|
|
<i class="border all icon"></i>
|
|
</div> -->
|
|
|
|
|
|
<div class="edit-button" v-on:click="toggleList('ul')" :data-tooltip="`Task List\n(CTRL + SHIFT + 8)`" data-position="top center" :class="{'edit-active':activeToDo}">
|
|
<i class="tasks icon"></i>
|
|
<span>To-Do</span>
|
|
</div>
|
|
<div class="edit-button" v-on:click="toggleList('ol')" :data-tooltip="`Ordered List\n(CTRL + SHIFT + 9)`" data-position="top center" :class="{'edit-active':activeList}">
|
|
<i class="list ol icon"></i>
|
|
<span>List</span>
|
|
</div>
|
|
|
|
<div class="edit-divide"></div>
|
|
|
|
<div class="edit-button" v-on:click="insertDivide()" data-tooltip="Insert Divide" data-position="top center">
|
|
<i class="grip lines icon"></i>
|
|
<span>Divide</span>
|
|
</div>
|
|
|
|
|
|
<div class="edit-divide"></div>
|
|
|
|
<!--
|
|
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/colors`)" data-tooltip="Note Color" data-position="top center" :style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}">
|
|
<i class="paint brush icon"></i>
|
|
</div> -->
|
|
<!-- <div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/tags`)" data-tooltip="Tags" data-position="top center">
|
|
<i class="tags icon"></i>
|
|
</div> -->
|
|
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/images`)" data-tooltip="Images" data-position="top center">
|
|
<i class="image icon"></i>
|
|
<span>Images</span>
|
|
</div>
|
|
|
|
<file-upload-button
|
|
data-tooltip="Upload File" data-position="top center"
|
|
class="edit-button"
|
|
:noteId="noteid" />
|
|
|
|
<div class="edit-divide"></div>
|
|
|
|
<div class="edit-button" v-on:click="$router.push(`/notes/open/${noteid}/menu/options`)" data-tooltip="More Options" data-position="top center">
|
|
|
|
<i class="ellipsis horizontal icon"></i>
|
|
|
|
</div>
|
|
|
|
<div class="edit-divide"></div>
|
|
|
|
<div class="edit-button" v-on:click="undoCustom()" :data-tooltip="`Undo\n(CTRL + z)`" data-position="top center">
|
|
<i class="reply icon"></i>
|
|
</div>
|
|
|
|
<div class="edit-button done-button" v-on:click="closeButtonAction()" :data-tooltip="`Close\n(ESC)`" data-position="top center">
|
|
<!-- <i class="green close icon"></i> -->
|
|
<span class="ui green text">Done</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="input-container-wrapper"
|
|
:class="{ 'side-menu-open':sideMenuOpen }">
|
|
|
|
<!-- Squire box grows -->
|
|
<div class="note-wrapper">
|
|
|
|
<!-- Loading indicator -->
|
|
<transition name="fade">
|
|
<div v-if="loading && forceShowLoading" class="loading-note" :style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText']}">
|
|
<div class="loading-text">
|
|
<loading-icon :message="loadingMessage" />
|
|
</div>
|
|
</div>
|
|
</transition>
|
|
|
|
<!-- Title input area -->
|
|
<textarea
|
|
ref="titleTextarea"
|
|
v-on:keyup="titleResize"
|
|
v-on:keydown="titleResize"
|
|
@keydown.enter.exact.prevent="editor.focus(); editor.moveCursorToEnd()"
|
|
rows="1"
|
|
:style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText'] }"
|
|
v-on:blur="save" type="text" v-model="noteTitle" placeholder="Title" class="stealth-input glint">
|
|
</textarea>
|
|
|
|
<!-- close button giant -->
|
|
<div v-if="!$store.getters.getIsUserOnMobile" class="large-close-button" v-on:click="closeButtonAction()">
|
|
<i class="fitted green close icon"></i>
|
|
</div>
|
|
|
|
<!-- tags on the side, only show on desktop -->
|
|
<div class="note-mini-tag-area"
|
|
v-on:click="$router.push(`/notes/open/${noteid}/menu/tags`)"
|
|
:style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText'] }">
|
|
|
|
<span class="add-mini-tag" v-if="allTags.length == 0">
|
|
<i class="tags icon"></i>Add Tags
|
|
</span>
|
|
<span v-for="tag in allTags" class="active-mini-tag">
|
|
#{{ tag }}
|
|
</span>
|
|
<span class="active-mini-tag" v-if="allTags.length > 0">
|
|
+
|
|
</span>
|
|
|
|
<span class="status-menu" v-on:click=" hash=0; save()">
|
|
|
|
<span v-if="diffsApplied > 0">
|
|
+{{ diffsApplied }} Unsaved Changes
|
|
</span>
|
|
|
|
<span v-if="usersOnNote > 1" :data-tooltip="`Viewers`" data-position="left center">
|
|
<i class="green eye icon"></i> {{ usersOnNote }}
|
|
</span>
|
|
|
|
<span v-if="statusText == 'modified'" data-position="left center" data-tooltip="Modified">
|
|
<i class="grey asterisk icon"></i>
|
|
</span>
|
|
<span v-if="statusText == 'saving'" data-position="left center" data-tooltip="Saving">
|
|
<i class="grey upload icon"></i>
|
|
</span>
|
|
<span v-if="statusText == 'saved'" data-position="left center" data-tooltip="Saved">
|
|
<i class="grey check icon"></i>
|
|
</span>
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
<!-- Squire Box -->
|
|
<div
|
|
id="squire-id"
|
|
class="squire-box"
|
|
ref="squirebox"
|
|
:style="{ 'background-color':styleObject['noteBackground'], 'color':styleObject['noteText'] }"
|
|
placeholder="Type Note Here"></div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- color picker -->
|
|
<color-tooltip
|
|
v-if="colorpicker"
|
|
:last-used-color="lastUsedColor"
|
|
v-on:color="color => modifyColor(color)"
|
|
v-on:close="colorpicker = false"
|
|
/>
|
|
|
|
<!-- Side slide menus for colors, tags, images and other options -->
|
|
<!-- <side-slide-menu v-if="colors" v-on:close="colors = false" name="colors">
|
|
<color-picker
|
|
@changeColor="onChangeColor"
|
|
@close="colors = false; $router.go(-1)"
|
|
:style-object="styleObject"
|
|
/>
|
|
</side-slide-menu> -->
|
|
|
|
<!-- tag edit menu -->
|
|
<side-slide-menu v-if="tags" v-on:close="tags = false; fetchNoteTags()" name="tags">
|
|
<div class="ui basic segment">
|
|
<note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/>
|
|
</div>
|
|
</side-slide-menu>
|
|
|
|
<!-- images menu -->
|
|
<side-slide-menu v-if="images" v-on:close="images = false" name="images">
|
|
<div class="ui basic segment">
|
|
<simple-attachment-note
|
|
:note-id="noteid"
|
|
:squire-editor="editor">
|
|
</simple-attachment-note>
|
|
</div>
|
|
</side-slide-menu>
|
|
|
|
<side-slide-menu v-if="options" v-on:close="options = false" name="note-options">
|
|
<div class="ui basic padded segment">
|
|
<div class="ui compact stackable grid">
|
|
|
|
<div class="eight wide column">
|
|
<div class="ui dividing header">
|
|
Note Options
|
|
</div>
|
|
<div class="ui labeled icon fluid basic button" v-on:click="onToggleArchived()">
|
|
<i class="archive icon" :class="{'green':(archived == 1)}"></i>
|
|
<span v-if="archived == 1">Un-Archive Note</span>
|
|
<span v-if="archived != 1">Archive Note</span>
|
|
</div>
|
|
<div class="ui labeled icon fluid basic button" v-on:click="onTogglePinned">
|
|
<i class="pin icon" :class="{'green':(pinned == 1)}"></i>
|
|
<span v-if="pinned == 1">Un-Pin Note</span>
|
|
<span v-if="pinned != 1">Pin Note</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="eight wide column">
|
|
<div class="ui dividing header">
|
|
List Options
|
|
</div>
|
|
<div class="ui labeled icon fluid basic button" v-on:click="sortList">
|
|
<i class="sort amount up icon"></i>
|
|
Sort List (Complete to bottom)
|
|
</div>
|
|
<div class="ui labeled icon fluid basic button" v-on:click="uncheckAllListItems">
|
|
<i class="list ul icon"></i>
|
|
Uncheck All
|
|
</div>
|
|
<div class="ui labeled icon fluid basic button" v-on:click="deleteCompletedListItems">
|
|
<i class="trash icon"></i>
|
|
Delete Checked
|
|
</div>
|
|
</div>
|
|
|
|
<div class="eight wide column">
|
|
<div class="ui dividing header">
|
|
Calculate Line
|
|
</div>
|
|
<p>
|
|
Calculates algebra before '='
|
|
</p>
|
|
<div class="ui labeled icon fluid basic button" v-on:click="calculateMath">
|
|
<i class="calculator icon"></i>
|
|
Calculate Simple Math
|
|
</div>
|
|
</div>
|
|
|
|
<div class="eight wide column">
|
|
<!-- data-tooltip="Files on note" -->
|
|
<div class="ui dividing header">
|
|
Note Attachments & Links
|
|
</div>
|
|
<p>
|
|
Attachment & Link Count {{ attachmentCount }}
|
|
</p>
|
|
<div v-on:click="openEditAttachment" class="ui labeled icon fluid basic button">
|
|
<i class="folder icon"></i>
|
|
View all Attachments & Links
|
|
</div>
|
|
</div>
|
|
|
|
<color-picker
|
|
@changeColor="onChangeColor"
|
|
@close="colors = false; $router.go(-1)"
|
|
:style-object="styleObject"
|
|
/>
|
|
|
|
<div class="sixteen wide column" v-if="rawTextId > 0">
|
|
<div class="ui dividing header">
|
|
Share Note
|
|
</div>
|
|
<share-note-component
|
|
:note-id="noteid"
|
|
:raw-text-id="rawTextId"
|
|
:share-username="shareUsername"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</side-slide-menu>
|
|
|
|
<!-- create table option -->
|
|
<side-slide-menu v-if="table" v-on:close="table = false;" name="table" :style-object="styleObject">
|
|
<div class="ui basic segment">
|
|
<h2>Insert Table</h2>
|
|
<div class="table-tic-table">
|
|
<div v-for="i in 10">
|
|
<div v-for="j in 10" class="tabletic" v-on:click="insertTable(i,j)">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</side-slide-menu>
|
|
|
|
<!-- Show side shades if user is on desktop only -->
|
|
<!-- <div class="full-focus-shade shade1"
|
|
:class="{ 'fade-me-out':sizeDown }"
|
|
v-on:click="closeButtonAction()"></div> -->
|
|
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
const Squire = require('../assets/squire.js')
|
|
|
|
import axios from 'axios'
|
|
// const crypto = require('crypto')
|
|
const DiffMatchPatch = require('../../../server/helpers/DiffMatchPatch')
|
|
const dmp = new DiffMatchPatch.diff_match_patch()
|
|
|
|
import SquireButtonFunctions from '@/mixins/SquireButtonFunctions.js'
|
|
|
|
export default {
|
|
name: 'NoteInputPanel',
|
|
props: [ 'noteid', 'position', 'openMenu', 'urlData', 'openNotes'],
|
|
components:{
|
|
'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'),
|
|
|
|
'color-tooltip':require('@/components/TextColorTooltipComponent.vue').default,
|
|
'nm-button':require('@/components/NoteMenuButtonComponent.vue').default,
|
|
'loading-icon':require('@/components/LoadingIconComponent.vue').default,
|
|
},
|
|
mixins:[ SquireButtonFunctions ],
|
|
data(){
|
|
return {
|
|
loading: true,
|
|
forceShowLoading: false,
|
|
loadingMessage: 'Loading Note',
|
|
currentNoteId: 0,
|
|
modified: false,
|
|
noteText: '',
|
|
noteTitle: '',
|
|
rawTextId: 0,
|
|
created: '',
|
|
updated: '',
|
|
shareUsername: null,
|
|
// diffNoteText: '',
|
|
statusText: 'saved',
|
|
lastNoteHash: null,
|
|
saveDebounce: null, //Prevent save from being called numerous times quickly
|
|
updated: 'Never',
|
|
lastInteractionTimestamp:null, //Tracks when note was loaded and last saved/refreshed
|
|
editDebounce: null,
|
|
textChangedDebounce: null,
|
|
keyPressesCounter: 0, //Determine keys pressed between saves
|
|
pinned: 0,
|
|
archived: 0,
|
|
attachmentCount: 0,
|
|
styleObject: { 'noteText':null,'noteBackground':null, 'noteIcon':null, 'iconColor':null }, //Style object. Determines colors and badges
|
|
|
|
sizeDown: false, //Used to animate close state
|
|
|
|
//Settings vars
|
|
lastVisibilityState: null,
|
|
|
|
//All the squire settings
|
|
editor: null,
|
|
usersOnNote: 0,
|
|
|
|
sideMenuOpen: false,
|
|
tags: false,
|
|
colors: false,
|
|
images: false,
|
|
options: false,
|
|
colorpicker: false,
|
|
table: false,
|
|
|
|
//Diff text/sync text variables
|
|
diffTextTimeout: null,
|
|
diffsApplied: null,
|
|
|
|
//Used to restore caret position
|
|
lastRange: null,
|
|
startOffset: 0,
|
|
|
|
//Tag Display
|
|
allTags: [],
|
|
noteTags: [],
|
|
}
|
|
},
|
|
watch: {
|
|
urlData(newVal, oldVal){
|
|
|
|
//Handle changes in URL to
|
|
|
|
if(newVal.id == undefined || newVal.id != this.noteid){
|
|
// this.closeButtonAction()
|
|
}
|
|
|
|
//Reset all note menus on URL change
|
|
this.sideMenuOpen = false
|
|
this.colors = false
|
|
this.tags = false
|
|
this.options = false
|
|
this.images = false
|
|
this.table = false
|
|
|
|
//If a menu value is set, open it
|
|
if(newVal.openMenu && newVal.id == this.noteid){
|
|
//Only modify menu boolean if its defined
|
|
if(typeof this[newVal.openMenu] == 'boolean'){
|
|
this.sideMenuOpen = true
|
|
this[newVal.openMenu] = true
|
|
}
|
|
}
|
|
}
|
|
},
|
|
beforeMount(){
|
|
|
|
this.$bus.$on('new_file_upload', ({noteId, imageCode}) => {
|
|
if(this.noteid == noteId && this.editor){
|
|
this.editor.moveCursorToEnd()
|
|
this.editor.insertHTML(imageCode)
|
|
this.save()
|
|
}
|
|
})
|
|
|
|
this.$bus.$on('close_note_by_id', (noteId) => {
|
|
if(noteId == this.noteid){
|
|
this.closeButtonAction()
|
|
}
|
|
})
|
|
},
|
|
beforeDestroy(){
|
|
|
|
this.$io.emit('leave_room', this.rawTextId)
|
|
|
|
this.$bus.$off('new_file_upload')
|
|
|
|
this.destroyWebSockets()
|
|
|
|
document.removeEventListener('visibilitychange', this.checkForUpdatedNote)
|
|
|
|
//Obliterate squire instance
|
|
this.editor.destroy()
|
|
|
|
// trigger save actions and reindex
|
|
this.close()
|
|
|
|
},
|
|
mounted: function() {
|
|
|
|
//Show loading if note has not loaded in 500ms
|
|
setTimeout(()=>{
|
|
this.forceShowLoading = true
|
|
}, 500)
|
|
|
|
document.addEventListener('visibilitychange', this.checkForUpdatedNote)
|
|
|
|
//Init squire as early as possible
|
|
if(this.editor && this.editor.destroy){
|
|
this.editor.destroy()
|
|
}
|
|
this.editor = new Squire( this.$refs.squirebox, {blockTag: 'p' })
|
|
|
|
this.$nextTick(() => {
|
|
//Setup Squire first chance we get
|
|
this.loadNote(this.noteid)
|
|
})
|
|
},
|
|
methods: {
|
|
testModify(){
|
|
const text = document.getElementById('squire-id').children[0].innerHTML
|
|
const prepended = 'Hello-> ' + text
|
|
document.getElementById('squire-id').children[0].innerHTML = prepended
|
|
|
|
const text1 = document.getElementById('squire-id').children[2].innerHTML
|
|
const prepended1 = 'Hey-> ' + text1
|
|
document.getElementById('squire-id').children[2].innerHTML = prepended1
|
|
},
|
|
simulateTyping(index = 0){
|
|
|
|
const words = ['lets','see','how','big','of','a','list','we','can','make','and','if','we','can','simulate','multiple','users','typing','its','probably','going','to','be','a','shitty','shit','show','but','whatever','~','You all','ever','seen','a','full','size','zebra','eat','a','big','fat','mound','of','grass','let','me','tell','you','man,','those','freaking','things','can','chew.','I','mean','like','really','chew.','Not','just','a','little,','but','a','lot.','~','Do not','believe','me.','Shoot','on','down','to','your','local','zoo','with','a','fat','mound','of','grass.','~','In','other','news,','I','really','enjoy','testing','things.','I am','just','going','to','keep','on','typing','until','its','so','annoying','that','you','give','up','on','trying','to','type','other','things.','~','Other','people','do not','get','to','type!','Only','me!','Fuck','off','other','users.','Its','all','about','me','up','in','here.','And','~','Zebra','Grass.',
|
|
]
|
|
|
|
const nextWord = words[index % (words.length)] + ' '
|
|
let letterObjects = []
|
|
let totalTime = 0
|
|
nextWord.split('').forEach(letter => {
|
|
|
|
const letterTime = Math.floor(Math.random() * 80) + 20
|
|
totalTime += letterTime
|
|
|
|
setTimeout(() => {
|
|
const newText = this.getText().slice(0,-8)
|
|
if(letter == ' '){
|
|
letter = ' <br></p>'
|
|
}
|
|
if(letter == '~'){
|
|
letter = '<br></p><p><br></p><p><br></p>'
|
|
}
|
|
const addedLetter = newText + letter
|
|
this.setText(addedLetter)
|
|
this.onKeyup()
|
|
|
|
}, totalTime)
|
|
})
|
|
|
|
setTimeout(() => {
|
|
|
|
if(index < words.length-1){
|
|
const more = index + 1
|
|
this.simulateTyping(more)
|
|
}
|
|
|
|
}, totalTime + 40)
|
|
},
|
|
fetchNoteTags(){
|
|
axios.post('/api/tag/fornote', {'noteId': this.noteid})
|
|
.then(({data}) => {
|
|
|
|
//Setup note tags from string
|
|
this.allTags = data.tags ? data.tags.split(',') : []
|
|
|
|
})
|
|
},
|
|
initSquire(){
|
|
|
|
//Set up squire and load note text
|
|
this.setText(this.noteText)
|
|
|
|
//focus on open, not on mobile, it causes the keyboard to pop up, thats annoying
|
|
if(!this.$store.getters.getIsUserOnMobile){
|
|
this.editor.focus()
|
|
this.editor.moveCursorToEnd()
|
|
}
|
|
|
|
//Set up websockets after squire is set up
|
|
setTimeout(() => {
|
|
this.setupWebSockets()
|
|
}, 500)
|
|
|
|
this.editor.addEventListener('cursor', e => {
|
|
|
|
//Save range to replace cursor if someone else makes an update
|
|
this.lastRange = e.range
|
|
this.startOffset = parseInt(e.range.startOffset)
|
|
return
|
|
})
|
|
|
|
//Change button states on editor when element is active
|
|
//eg; Bold button turns green when on bold text
|
|
this.editor.addEventListener('pathChange', e => this.pathChangeEvent(e))
|
|
|
|
//Click Event - Open links when clicked in editor or toggle checks
|
|
this.editor.addEventListener('click', e => {
|
|
|
|
//Link clicked in editor - open link
|
|
if(e.target.nodeName == 'A' && e.target.href){
|
|
window.open(e.target.href)
|
|
}
|
|
|
|
//List Item clicked in editor - toggle link state
|
|
if(e.target.nodeName == 'LI'){
|
|
|
|
let el = e.target
|
|
|
|
//If the offset is triggered with a negative offset, it means the before element was clicked
|
|
if(e.offsetX < -5){
|
|
|
|
//Area before element was clicked, they clicked the checkbox
|
|
if (el.className === 'active'){
|
|
el.className = 'inactive';
|
|
} else {
|
|
el.className = 'active';
|
|
}
|
|
//Trigger keyup event to save list changes
|
|
this.onKeyup(e)
|
|
|
|
//Will hide keyboard if clicked on mobile
|
|
if(this.$store.getters.getIsUserOnMobile){
|
|
|
|
setTimeout(() => {
|
|
|
|
document.activeElement.blur()
|
|
e.preventDefault()
|
|
return
|
|
|
|
}, 25)
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
})
|
|
|
|
this.editor.addEventListener('keydown', event => {
|
|
|
|
//Tab to increase quote level, tab + shigt to decrease quote level
|
|
const keyCode = event.key
|
|
if(keyCode == 'Tab'){
|
|
|
|
if(event.shiftKey){
|
|
this.outdentText()
|
|
} else {
|
|
this.indentText()
|
|
}
|
|
|
|
event.preventDefault()
|
|
return false
|
|
}
|
|
|
|
//Save on pressing CTRL/CMD + S
|
|
if(keyCode == 's' && (event.ctrlKey || event.metaKey) ){
|
|
|
|
this.$bus.$emit('notification', 'Note Saved')
|
|
this.save()
|
|
|
|
event.preventDefault()
|
|
return false
|
|
}
|
|
|
|
//Prevent new list items from having
|
|
this.$nextTick( () => {
|
|
//Wait a moment to get item under cursor
|
|
let selection = this.editor.getSelection()
|
|
let container = selection.commonAncestorContainer
|
|
|
|
//If user hit enter on a list, make sure the next list item isn't active
|
|
if(container.nodeName == 'LI' && event.keyCode == 13 && container.classList){
|
|
container.classList.remove('active')
|
|
}
|
|
})
|
|
})
|
|
|
|
//Bind event handlers
|
|
this.editor.addEventListener('keyup', event => {
|
|
|
|
this.onKeyup(event)
|
|
})
|
|
|
|
// this.editor.addEventListener("dragstart", e => {
|
|
// console.log('Dragging')
|
|
// console.log(e)
|
|
// if(){}
|
|
// });
|
|
|
|
//Show and hide additional toolbars
|
|
// this.editor.addEventListener('focus', e => {
|
|
// })
|
|
// this.editor.addEventListener('blur', e => {
|
|
// })
|
|
},
|
|
openEditAttachment(){
|
|
|
|
this.$router.push('/attachments/note/'+this.currentNoteId)
|
|
},
|
|
onTogglePinned(){
|
|
|
|
if(this.pinned == 0){
|
|
this.pinned = 1
|
|
} else {
|
|
this.pinned = 0;
|
|
}
|
|
//Update last note hash, this will tell note to save next update
|
|
this.lastNoteHash = 0
|
|
this.save()
|
|
},
|
|
onToggleArchived(){
|
|
|
|
if(this.archived == 0){
|
|
this.archived = 1
|
|
} else {
|
|
this.archived = 0;
|
|
}
|
|
//Update last note hash, this will tell note to save next update
|
|
this.lastNoteHash = 0
|
|
this.save()
|
|
},
|
|
onChangeColor(newStyleObject){
|
|
|
|
//Set new style object for note, page will use some styles, styles will be saved to database
|
|
this.styleObject = newStyleObject
|
|
|
|
this.lastNoteHash = 0 //Update hash to force note update on next save
|
|
this.save()
|
|
},
|
|
loadNote(noteId){
|
|
|
|
//Generate a random loading message
|
|
let mod = ['Gently','Calmly','Lovingly','Quickly','Diligently','','','','','','','','','','','','','','']
|
|
let doing = ['Loading','Loading','Getting','Fetching','Grabbing','Sequencing','Organizing','Untangling','Processing','Refining','Extracting','Fusing','Pruning','Expanding','Enlarging','Transfiguring','Quantizing','Ingratiating','Lumping']
|
|
let thing = ['Note','Note','Note','Note','Data','Text','Document','Algorithm','Buffer','Client','Download','File','Frame','Graphics','Hardware','HTML','Interface','Logic','Mainframe','Memory','Media','Nodes','Network','Chaos']
|
|
|
|
let p1 = mod[Math.floor(Math.random() * mod.length)]
|
|
let p2 = doing[Math.floor(Math.random() * doing.length)]
|
|
let p3 = thing[Math.floor(Math.random() * thing.length)]
|
|
this.loadingMessage = `${p1} ${p2} ${p3}`
|
|
|
|
//Component is activated with NoteId in place, lookup text with associated ID
|
|
if(this.$store.getters.getLoggedIn){
|
|
axios.post('/api/note/get', { 'noteId': this.noteid })
|
|
.then(response => {
|
|
|
|
//Block notes you don't have access to from opening
|
|
if(response.data === false){
|
|
this.$bus.$emit('notification', 'Error opening Note')
|
|
this.close()
|
|
return
|
|
}
|
|
|
|
//Setup all responsive vue data
|
|
this.setupLoadedNoteData(response)
|
|
|
|
this.loading = false
|
|
|
|
this.$nextTick(() => {
|
|
|
|
//Adjust note title size after load
|
|
this.titleResize()
|
|
this.initSquire()
|
|
})
|
|
|
|
})
|
|
.catch(error => { this.$bus.$emit('notification', 'Failed to Open Note') })
|
|
} else {
|
|
console.log('Could not fetch note')
|
|
}
|
|
},
|
|
setupLoadedNoteData(response){
|
|
//All the data returned by the server, setup locally in vue component
|
|
|
|
//Set up local data
|
|
this.currentNoteId = this.noteid
|
|
this.rawTextId = response.data.rawTextId
|
|
this.shareUsername = response.data.shareUsername
|
|
|
|
this.created = response.data.created
|
|
this.updated = response.data.updated
|
|
this.lastInteractionTimestamp = +new Date
|
|
this.noteTitle = ''
|
|
if(response.data.title){
|
|
this.noteTitle = response.data.title
|
|
}
|
|
|
|
this.noteText = response.data.text
|
|
this.lastNoteHash = this.hashString( response.data.text )
|
|
// this.diffNoteText = response.data.text
|
|
|
|
//Setup note tags
|
|
this.allTags = response.data.tags ? response.data.tags.split(','):[]
|
|
|
|
//Set up note colors
|
|
if(response.data.color){
|
|
this.styleObject = JSON.parse(response.data.color)
|
|
}
|
|
|
|
if(response.data.pinned != null){
|
|
this.pinned = response.data.pinned
|
|
}
|
|
this.archived = response.data.archived
|
|
this.attachmentCount = response.data.attachment_count
|
|
|
|
return true
|
|
|
|
},
|
|
//Called on squire event for keyup
|
|
diffText(event){
|
|
|
|
//Diff the changed lines only
|
|
|
|
let oldText = this.noteText
|
|
// let newText = this.getText()
|
|
let newText = document.getElementById('squire-id').innerHTML
|
|
|
|
const diff = dmp.diff_main(oldText, newText)
|
|
// dmp.diff_cleanupSemantic(diff)
|
|
const patch_list = dmp.patch_make(oldText, newText, diff);
|
|
const patch_text = dmp.patch_toText(patch_list);
|
|
|
|
if(patch_text == ''){ return }
|
|
|
|
//Save computed diff text
|
|
this.noteText = newText
|
|
|
|
let newPatch = {
|
|
id: this.rawTextId,
|
|
diff: patch_text,
|
|
}
|
|
|
|
this.$io.emit('note_diff', newPatch)
|
|
},
|
|
patchText(incomingPatchs){
|
|
return new Promise((resolve, reject) => {
|
|
|
|
if(incomingPatchs == null){ return resolve(true) }
|
|
if(incomingPatchs.length == 0){ return resolve(true) }
|
|
|
|
// let currentText = this.getText()
|
|
let currentText = document.getElementById('squire-id').innerHTML
|
|
|
|
//Convert text of all new patches into patches array
|
|
let patches = []
|
|
incomingPatchs.forEach(patch => {
|
|
|
|
if(patch.time <= this.updated){
|
|
return
|
|
}
|
|
|
|
patches.push(...dmp.patch_fromText(patch.diff))
|
|
})
|
|
|
|
if(patches.length == 0){
|
|
return resolve(true)
|
|
}
|
|
|
|
var results = dmp.patch_apply(patches, currentText);
|
|
let newText = results[0]
|
|
|
|
this.noteText = newText
|
|
// this.editor.setHTML(newText)
|
|
document.getElementById('squire-id').innerHTML = newText
|
|
|
|
return resolve(true)
|
|
})
|
|
},
|
|
onKeyup(event){
|
|
|
|
this.statusText = 'modified'
|
|
|
|
// Small debounce on diff generation
|
|
clearTimeout(this.diffTextTimeout)
|
|
this.diffTextTimeout = setTimeout(() => {
|
|
this.diffText()
|
|
}, 25)
|
|
|
|
//Save after x seconds
|
|
clearTimeout(this.editDebounce)
|
|
this.editDebounce = setTimeout(() => {
|
|
this.save()
|
|
}, 5 * 1000)
|
|
|
|
//Save after x keystrokes
|
|
this.keyPressesCounter = (this.keyPressesCounter + 1)
|
|
if(this.keyPressesCounter > 60){
|
|
this.keyPressesCounter = 0
|
|
this.save()
|
|
}
|
|
|
|
},
|
|
save(force = false){
|
|
return new Promise((resolve, reject) => {
|
|
//Clear other debounced events to prevent double calling of save
|
|
// clearTimeout(this.editDebounce)
|
|
|
|
if(this.statusText == 'saving'){
|
|
return resolve(true)
|
|
}
|
|
|
|
//Don't save note if its hash doesn't change
|
|
const currentNoteText = this.getText()
|
|
const currentHash = this.hashString( currentNoteText )
|
|
if( this.lastNoteHash == currentHash){
|
|
this.statusText = 'saved'
|
|
return resolve(true)
|
|
}
|
|
|
|
//If user accidentally clears note, it won't delete it
|
|
if(currentNoteText == ''){
|
|
return resolve(true)
|
|
}
|
|
|
|
//tell websockets to truncate history at this save
|
|
this.$io.emit('truncate_diffs_at_save', {'rawTextId':this.rawTextId, 'hash':currentHash })
|
|
|
|
const postData = {
|
|
'noteId': this.currentNoteId,
|
|
'title': this.noteTitle,
|
|
'text': currentNoteText,
|
|
'color': JSON.stringify(this.styleObject), //Save little json color object
|
|
'pinned': this.pinned,
|
|
'archived': this.archived,
|
|
'hash': currentHash,
|
|
}
|
|
|
|
this.statusText = 'saving'
|
|
axios.post('/api/note/update', postData).then( response => {
|
|
this.statusText = 'saved'
|
|
this.updated = +new Date
|
|
this.lastInteractionTimestamp = +new Date
|
|
this.modified = true
|
|
this.diffsApplied = 0
|
|
|
|
//Update last saved note hash
|
|
this.lastNoteHash = currentHash
|
|
return resolve(true)
|
|
})
|
|
.catch(error => { this.$bus.$emit('notification', 'Failed to Save Note') })
|
|
})
|
|
},
|
|
checkForUpdatedNote(){
|
|
|
|
const now = +new Date
|
|
//Only check every 3 seconds
|
|
const checkForUpdateTimeout = now - this.lastInteractionTimestamp > (2 * 1000)
|
|
|
|
//If user leaves page then returns to page, reload the first batch
|
|
if(this.lastVisibilityState == 'hidden' && document.visibilityState == 'visible' && checkForUpdateTimeout){
|
|
|
|
//Focus Regained on Note, check for update
|
|
axios.post('/api/note/get', { 'noteId': this.noteid })
|
|
.then(response => {
|
|
|
|
const serverTextHash = this.hashString( response.data.text )
|
|
|
|
if(this.lastNoteHash != serverTextHash){
|
|
// console.log('note was changed UPDATE THAT BITCH!!!!')
|
|
this.setupLoadedNoteData(response)
|
|
|
|
//Manually set squire text to show
|
|
this.setText(this.noteText)
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
//Keep track of visibility change and last interaction time
|
|
this.lastVisibilityState = document.visibilityState
|
|
this.lastInteractionTimestamp = +new Date
|
|
|
|
},
|
|
hashString(inText){
|
|
|
|
let text = this.noteTitle + inText
|
|
|
|
let hash = 0;
|
|
if (text == null || 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;
|
|
},
|
|
closeButtonAction(playAnimation = false){
|
|
this.sizeDown = playAnimation
|
|
const animationTimeout = (playAnimation ? 300 : 0)
|
|
//This timeout allows animation to play before closing
|
|
setTimeout(() => {
|
|
// this.$router.push('/notes')
|
|
this.close()
|
|
}, animationTimeout)
|
|
},
|
|
close(){
|
|
|
|
// force = true
|
|
|
|
// console.log(`Close Note ${this.noteid} -> force: ${force}, modified: ${this.modified}`)
|
|
|
|
//Skip everything if foce close is true. Note will just die.
|
|
if(this.currentNoteId == 0){ return }
|
|
|
|
this.loadingMessage = 'Saving...'
|
|
this.loading = true
|
|
|
|
this.save().then( result => {
|
|
|
|
//If note was modified, trigger reindex on close
|
|
if(this.modified){
|
|
axios.post('/api/note/reindex')
|
|
}
|
|
|
|
this.$bus.$emit('close_active_note', {
|
|
noteId: this.noteid, modified: this.modified
|
|
})
|
|
return
|
|
})
|
|
},
|
|
destroyWebSockets(){
|
|
// this.$io.removeListener('past_diffs')
|
|
// this.$io.removeListener('update_user_count')
|
|
// this.$io.removeListener('incoming_diff')
|
|
},
|
|
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
|
|
})
|
|
|
|
//Apply all diffs since last save
|
|
this.$io.on('past_diffs', diffSinceLastUpdate => {
|
|
|
|
if(diffSinceLastUpdate != null){
|
|
this.diffsApplied = diffSinceLastUpdate.length
|
|
// console.log('Got Diffs Total -> ', diffSinceLastUpdate)
|
|
}
|
|
this.patchText(diffSinceLastUpdate)
|
|
})
|
|
|
|
this.$io.on('incoming_diff', incomingDiff => {
|
|
|
|
//Save current caret position
|
|
//Find index of child element based on past range
|
|
const element = window.getSelection().getRangeAt(0).startContainer.parentNode
|
|
const textLines = document.getElementById('squire-id').children
|
|
const childIndex = [...textLines].indexOf(element)
|
|
|
|
this.patchText([incomingDiff])
|
|
.then(() => {
|
|
|
|
if(childIndex == -1){
|
|
console.log('Cursor position lost. Div being updated was lost.')
|
|
return
|
|
}
|
|
|
|
//Reset caret position
|
|
//Find child index of old range and create a new one
|
|
let allChildren = document.getElementById('squire-id').children
|
|
const newLine = allChildren[childIndex].firstChild
|
|
let range = document.createRange()
|
|
range.setStart(newLine, this.startOffset)
|
|
range.setEnd(newLine, this.startOffset)
|
|
this.editor.setSelection(range)
|
|
})
|
|
})
|
|
|
|
},
|
|
titleResize(){
|
|
//Resize the title field
|
|
let element = this.$refs.titleTextarea
|
|
if(element){
|
|
|
|
element.style.height = 'auto'
|
|
element.style.height = (element.scrollHeight) +'px'
|
|
}
|
|
},
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style type="text/css" scoped>
|
|
|
|
.status-menu {
|
|
position: absolute;
|
|
right: 10px;
|
|
z-index: 1019;
|
|
text-align: right;
|
|
}
|
|
|
|
.font-color-bar {
|
|
/*width: calc(100% - 8px);*/
|
|
height: 3px;
|
|
position: absolute;
|
|
bottom: 2px;
|
|
right: 4px;
|
|
left: 4px;
|
|
z-index: 0;
|
|
background: linear-gradient(
|
|
45deg,
|
|
rgba(255, 0, 0, 1) 0%,
|
|
rgba(208, 222, 33, 1) 20%,
|
|
rgba(63, 218, 216, 1) 40%,
|
|
rgba(28, 127, 238, 1) 60%,
|
|
rgba(186, 12, 248, 1) 80%,
|
|
rgba(255, 0, 0, 1) 100%
|
|
);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.large-close-button {
|
|
position: absolute;
|
|
min-height: 55px;
|
|
top: 0px;
|
|
right: 0px;
|
|
font-size: 23px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 0 20px 0 10px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.full-focus-shade {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: var(--small_element_bg_color);
|
|
z-index: 999;
|
|
cursor: pointer;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
/* squire styles */
|
|
.input-container-wrapper {
|
|
position: absolute;
|
|
top: 0;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
overflow-y: scroll;
|
|
overflow-x: hidden;
|
|
scrollbar-width: none;
|
|
scrollbar-color: transparent transparent;
|
|
overscroll-behavior: contain;
|
|
|
|
background-color: var(--border_color);
|
|
}
|
|
.note-wrapper {
|
|
background-color: var(--border_color);
|
|
position: relative;
|
|
margin: 50px auto;
|
|
|
|
max-width: 1100px;
|
|
}
|
|
|
|
.note-mini-tag-area {
|
|
width: 100%;
|
|
padding: 5px 15px 0 15px;
|
|
cursor: pointer;
|
|
position: relative;
|
|
background: var(--small_element_bg_color);
|
|
}
|
|
.add-mini-tag {
|
|
color: var(--border_color);
|
|
}
|
|
.active-mini-tag {
|
|
display: inline-block;
|
|
opacity: 0.9;
|
|
color: var(--main-accent);
|
|
}
|
|
.active-mini-tag + .active-mini-tag {
|
|
margin-left: 15px;
|
|
}
|
|
|
|
|
|
@keyframes blinker {
|
|
50% {
|
|
opacity: 0.2;
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
Edit Menu Styles START
|
|
|
|
*/
|
|
.menu-top-half, .menu-bottom-half {
|
|
display: flex;
|
|
justify-content: center;
|
|
position: absolute;
|
|
z-index: 1001;
|
|
background-color: green;
|
|
border-radius: 3px;
|
|
padding: 5px 5px;
|
|
background-color: var(--menu-background);
|
|
|
|
left: 0;
|
|
right: 0;
|
|
}
|
|
.menu-top-half{
|
|
top: 0;
|
|
}
|
|
.menu-bottom-half {
|
|
bottom: 0;
|
|
}
|
|
.menu-top-half.hide-text .edit-button > span:not(.ui),
|
|
.menu-bottom-half.hide-text .edit-button > span:not(.ui)
|
|
{
|
|
display: none;
|
|
}
|
|
.edit-button {
|
|
background-color: var(--small_element_bg_color);
|
|
color: var(--menu-text);
|
|
display: inline-block;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
font-size: 1em;
|
|
box-shadow: 0 0 1px 0 #c4c4c4;
|
|
margin: 0 3px 0;
|
|
padding: 6px 12px 0;
|
|
text-align: center;
|
|
min-width: 25px;
|
|
min-height: 30px;
|
|
/*flex-basis: 100%;*/
|
|
white-space: nowrap;
|
|
}
|
|
.edit-button > i {
|
|
font-size: 1em;
|
|
padding: 0;
|
|
margin: 0;
|
|
}
|
|
.edit-button > span {
|
|
padding: 0 8px;
|
|
}
|
|
.edit-button:hover {
|
|
background-color: var(--menu-accent);
|
|
}
|
|
.edit-active {
|
|
background-color: var(--main-accent);
|
|
color: white;
|
|
}
|
|
.edit-divide {
|
|
display: inline-block;
|
|
height: 15px;
|
|
width: 7px;
|
|
padding: 0;
|
|
}
|
|
@media only screen and (max-width: 740px) {
|
|
.edit-button {
|
|
font-size: 1.2em;
|
|
}
|
|
.menu-top-half, .menu-bottom-half {
|
|
padding: 3px 2px;
|
|
left: 0;
|
|
right: 0;
|
|
transform: none;
|
|
border-radius: 0;
|
|
}
|
|
.menu-bottom-half {
|
|
position: fixed;
|
|
z-index: 100000;
|
|
}
|
|
.note-wrapper {
|
|
margin-bottom: 100px;
|
|
margin-top: 38px;
|
|
}
|
|
}
|
|
/*
|
|
|
|
Edit Menu Styles END
|
|
|
|
*/
|
|
|
|
.stealth-input {
|
|
width: 100%;
|
|
padding: 15px;
|
|
background-color: var(--small_element_bg_color );
|
|
border: none;
|
|
border-bottom: 2px solid var(--main-accent);
|
|
font-size: 1.7em;
|
|
color: var(--text_color);
|
|
caret-color: var(--main-accent);
|
|
resize: none;
|
|
overflow: hidden;
|
|
/*margin: 0;*/
|
|
outline: none;
|
|
display: block;
|
|
|
|
margin-left: auto;
|
|
margin-right: auto;
|
|
max-width: 1100px;
|
|
|
|
}
|
|
|
|
/*Settings manager styles */
|
|
.all-settings {
|
|
background: #221f2b;
|
|
z-index: 99;
|
|
flex-grow: 0;
|
|
}
|
|
/*End Settings manager styles */
|
|
|
|
/* container styles change based on mobile and number of open screens */
|
|
/* .master-note-edit {
|
|
position: fixed;
|
|
bottom: 0;
|
|
height: 100vh;
|
|
z-index: 1001;
|
|
left: 15%;
|
|
right: 15%;
|
|
|
|
overflow-y: scroll;
|
|
overflow-x: hidden;
|
|
scrollbar-width: none;
|
|
scrollbar-color: transparent transparent;
|
|
}*/
|
|
.loading-note {
|
|
position: absolute;
|
|
top: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
min-height: 300px;
|
|
/*background: var(--small_element_bg_color);*/
|
|
/*opacity: 0.;*/
|
|
z-index: 1;
|
|
}
|
|
.loading-text {
|
|
margin: 0;
|
|
position: absolute;
|
|
top: 200px;
|
|
left: 50%;
|
|
margin-right: -50%;
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
|
|
/* One note open, in the middle of the screen */
|
|
.side-menu-open {
|
|
left: calc(50% + 10px) !important;
|
|
right: calc(0% + 10px) !important;
|
|
}
|
|
/*weird inbetween size for tables*/
|
|
@media only screen and (max-width: 875px) {
|
|
|
|
}
|
|
@media only screen and (max-width: 830px) {
|
|
.shade1, .shade2 {
|
|
right: 150%;
|
|
}
|
|
.edit-divide {
|
|
display: none;
|
|
}
|
|
.edit-button {
|
|
padding: 6px 0px 0;
|
|
}
|
|
.edit-button > span:not(.ui) {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* animations START */
|
|
|
|
.slide-out-top {
|
|
animation: slide-out-top 0.5s ease;
|
|
}
|
|
|
|
@keyframes slide-out-top {
|
|
0% {
|
|
top: 0;
|
|
}
|
|
100% {
|
|
top: -100px;
|
|
}
|
|
}
|
|
|
|
.size-down {
|
|
animation: size-down 0.5s ease;
|
|
}
|
|
|
|
@keyframes size-down {
|
|
0% {
|
|
top: 0;
|
|
}
|
|
100% {
|
|
top: 150vh;
|
|
}
|
|
}
|
|
|
|
.fade-me-out {
|
|
animation: fade-me-out 0.5s ease;
|
|
}
|
|
|
|
@keyframes fade-me-out {
|
|
0% {
|
|
opacity: 1;
|
|
}
|
|
100% {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
|
|
.slide-out-right {
|
|
animation: slide-out-right 0.5s ease;
|
|
}
|
|
|
|
@keyframes slide-out-right {
|
|
0% {
|
|
right: 85%;
|
|
}
|
|
100% {
|
|
right: 150%;
|
|
}
|
|
}
|
|
|
|
/* Fade out transition animation */
|
|
.fade-enter {
|
|
/*opacity: 0;*/
|
|
}
|
|
|
|
.fade-enter-active {
|
|
/*transition: opacity 0.7s;*/
|
|
}
|
|
|
|
.fade-leave {
|
|
/* opacity: 0; */
|
|
}
|
|
|
|
.fade-leave-active {
|
|
transition: opacity 0.7s;
|
|
opacity: 0;
|
|
}
|
|
/* animations END */
|
|
|
|
</style> |