Add "Create new..." item to the note selector dialog

This commit is contained in:
Jonatan Heyman 2024-12-05 23:20:13 +01:00
parent 9c057b120a
commit fe6a410e95
3 changed files with 147 additions and 98 deletions

View File

@ -24,7 +24,16 @@
}, },
async mounted() { async mounted() {
this.$refs.nameInput.focus() if (!!this.createNoteParams.name) {
this.name = this.createNoteParams.name
this.$refs.nameInput.focus()
this.$nextTick(() => {
this.$refs.nameInput.select()
})
} else {
this.$refs.nameInput.focus()
}
this.updateNotes() this.updateNotes()
// build directory tree // build directory tree
@ -67,7 +76,7 @@
...mapState(useNotesStore, [ ...mapState(useNotesStore, [
"notes", "notes",
"currentNotePath", "currentNotePath",
"createNoteMode", "createNoteParams",
]), ]),
currentNoteDirectory() { currentNoteDirectory() {
@ -82,7 +91,7 @@
}, },
dialogTitle() { dialogTitle() {
return this.createNoteMode === "currentBlock" ? "New Note from Block" : "New Note" return this.createNoteParams.mode === "currentBlock" ? "New Note from Block" : "New Note"
}, },
}, },
@ -147,12 +156,12 @@
return return
} }
console.log("Creating note", path) console.log("Creating note", path)
if (this.createNoteMode === "currentBlock") { if (this.createNoteParams.mode === "currentBlock") {
this.createNewNoteFromActiveBlock(path, this.name) this.createNewNoteFromActiveBlock(path, this.name)
} else if (this.createNoteMode === "new") { } else if (this.createNoteParams.mode === "new") {
this.createNewNote(path, this.name) this.createNewNote(path, this.name)
} else { } else {
throw new Error("Unknown createNoteMode: " + this.createNoteMode) throw new Error("Unknown createNote Mode: " + this.createNoteParams.mode)
} }
this.$emit("close") this.$emit("close")

View File

@ -60,13 +60,15 @@
}, },
filteredItems() { filteredItems() {
let items
if (this.filter === "") { if (this.filter === "") {
return this.orderedItems items = this.orderedItems
} else { } else {
const searchResults = fuzzysort.go(this.filter, this.items, { const searchResults = fuzzysort.go(this.filter, this.items, {
keys: ["name", "folder"], keys: ["name", "folder"],
}) })
return searchResults.map((result) => { items = searchResults.map((result) => {
const obj = {...result.obj} const obj = {...result.obj}
const nameHighlight = result[0].highlight("<b>", "</b>") const nameHighlight = result[0].highlight("<b>", "</b>")
const folderHighlight = result[1].highlight("<b>", "</b>") const folderHighlight = result[1].highlight("<b>", "</b>")
@ -75,6 +77,15 @@
return obj return obj
}) })
} }
const newNoteItem = {
name: "Create new…",
createNew:true,
}
return [
...items,
newNoteItem,
]
}, },
}, },
@ -83,6 +94,7 @@
"updateNotes", "updateNotes",
"editNote", "editNote",
"deleteNote", "deleteNote",
"openCreateNote",
]), ]),
buildItems() { buildItems() {
@ -99,7 +111,6 @@
onKeydown(event) { onKeydown(event) {
if (event.key === "Escape") { if (event.key === "Escape") {
console.log("escape")
event.preventDefault() event.preventDefault()
if (this.actionButton !== 0) { if (this.actionButton !== 0) {
this.hideActionButtons() this.hideActionButtons()
@ -112,8 +123,8 @@
if (this.filteredItems.length === 0) { if (this.filteredItems.length === 0) {
return return
} }
const path = this.filteredItems[this.selected].path const item = this.filteredItems[this.selected]
if (event.key === "ArrowDown") { if (event.key === "ArrowDown") {
if (this.selected === this.filteredItems.length - 1) { if (this.selected === this.filteredItems.length - 1) {
this.selected = 0 this.selected = 0
@ -121,11 +132,9 @@
this.selected = Math.min(this.selected + 1, this.filteredItems.length - 1) this.selected = Math.min(this.selected + 1, this.filteredItems.length - 1)
} }
event.preventDefault() event.preventDefault()
if (this.selected === this.filteredItems.length - 1) { this.$nextTick(() => {
this.$refs.container.scrollIntoView({block: "end"}) this.$refs.container.querySelector(".selected").scrollIntoView({block: "nearest"})
} else { })
this.$refs.item[this.selected].scrollIntoView({block: "nearest"})
}
this.actionButton = 0 this.actionButton = 0
} else if (event.key === "ArrowUp") { } else if (event.key === "ArrowUp") {
if (this.selected === 0) { if (this.selected === 0) {
@ -134,28 +143,32 @@
this.selected = Math.max(this.selected - 1, 0) this.selected = Math.max(this.selected - 1, 0)
} }
event.preventDefault() event.preventDefault()
if (this.selected === 0) { this.$nextTick(() => {
this.$refs.container.scrollIntoView({block: "start"}) this.$refs.container.querySelector(".selected").scrollIntoView({block: "nearest"})
} else { })
this.$refs.item[this.selected].scrollIntoView({block: "nearest"})
}
this.actionButton = 0 this.actionButton = 0
} else if (event.key === "ArrowRight" && path !== SCRATCH_FILE_NAME) { } else if (event.key === "ArrowRight" && this.itemHasActionButtons(item)) {
event.preventDefault() event.preventDefault()
this.actionButton = Math.min(2, this.actionButton + 1) this.actionButton = Math.min(2, this.actionButton + 1)
} else if (event.key === "ArrowLeft" && path !== SCRATCH_FILE_NAME) { } else if (event.key === "ArrowLeft" && this.itemHasActionButtons(item)) {
event.preventDefault() event.preventDefault()
this.actionButton = Math.max(0, this.actionButton - 1) this.actionButton = Math.max(0, this.actionButton - 1)
this.deleteConfirm = false this.deleteConfirm = false
} else if (event.key === "Enter") { } else if (event.key === "Enter") {
event.preventDefault() event.preventDefault()
if (this.actionButton === 1) { if (item.createNew) {
console.log("edit file:", path) if (this.filteredItems.length === 1) {
this.editNote(path) this.openCreateNote("new", this.filter)
} else {
this.openCreateNote("new", "")
}
} else if (this.actionButton === 1) {
//console.log("edit file:", path)
this.editNote(item.path)
} else if (this.actionButton === 2) { } else if (this.actionButton === 2) {
this.deleteConfirmNote(path) this.deleteConfirmNote(item.path)
} else { } else {
this.selectItem(path) this.selectItem(item.path)
} }
} }
}, },
@ -164,6 +177,10 @@
this.$emit("openNote", path) this.$emit("openNote", path)
}, },
itemHasActionButtons(item) {
return !item.createNew && item.path !== SCRATCH_FILE_NAME
},
onInput(event) { onInput(event) {
// reset selection // reset selection
this.selected = 0 this.selected = 0
@ -178,9 +195,11 @@
getItemClass(item, idx) { getItemClass(item, idx) {
return { return {
"item": true,
"selected": idx === this.selected, "selected": idx === this.selected,
"action-buttons-visible": this.actionButton > 0, "action-buttons-visible": this.actionButton > 0,
"scratch": item.scratch, "scratch": item.scratch,
"new-note": item.createNew,
} }
}, },
@ -198,7 +217,7 @@
async deleteConfirmNote(path) { async deleteConfirmNote(path) {
if (this.deleteConfirm) { if (this.deleteConfirm) {
console.log("delete file:", path) //console.log("delete file:", path)
await this.deleteNote(path) await this.deleteNote(path)
this.hideActionButtons() this.hideActionButtons()
this.buildItems() this.buildItems()
@ -214,8 +233,8 @@
</script> </script>
<template> <template>
<div class="scroller"> <form class="note-selector" tabindex="-1" @focusout="onFocusOut" ref="container">
<form class="note-selector" tabindex="-1" @focusout="onFocusOut" ref="container"> <div class="input-container">
<input <input
type="text" type="text"
ref="input" ref="input"
@ -224,57 +243,55 @@
v-model="filter" v-model="filter"
autocomplete="off" autocomplete="off"
/> />
<ul class="items"> </div>
<li <div class="scroller">
<ul class="items" ref="itemsContainer">
<template
v-for="item, idx in filteredItems" v-for="item, idx in filteredItems"
:key="item.path" :key="item.path"
:class="getItemClass(item, idx)"
@click="selectItem(item.path)"
ref="item"
> >
<span class="name" v-html="item.name" /> <li v-if="item.createNew" class="line-separator"></li>
<span class="path" v-html="item.folder" /> <li
<span :class="{'action-buttons':true, 'visible':actionButton > 0 && idx === selected}"> :class="getItemClass(item, idx)"
<button @click="selectItem(item.path)"
v-if="actionButton > 0 && idx === selected" ref="item"
:class="{'selected':actionButton === 1}" >
@click.stop.prevent="editNote(item.path)" <span class="name" v-html="item.name" />
>Edit</button> <span class="path" v-html="item.folder" />
<button <span :class="{'action-buttons':true, 'visible':actionButton > 0 && idx === selected}">
v-if="actionButton > 0 && idx === selected" <button
:class="{'delete':true, 'selected':actionButton === 2, 'confirm':deleteConfirm}" v-if="actionButton > 0 && idx === selected"
@click.stop.prevent="deleteConfirmNote(item.path)" :class="{'selected':actionButton === 1}"
> @click.stop.prevent="editNote(item.path)"
<template v-if="deleteConfirm"> >Edit</button>
Really Delete? <button
</template> v-if="actionButton > 0 && idx === selected"
<template v-else> :class="{'delete':true, 'selected':actionButton === 2, 'confirm':deleteConfirm}"
Delete @click.stop.prevent="deleteConfirmNote(item.path)"
</template> >
</button> <template v-if="deleteConfirm">
<button Really Delete?
class="show-actions" </template>
v-if="item.path !== SCRATCH_FILE_NAME && (actionButton === 0 || idx !== selected)" <template v-else>
@click.stop.prevent="showActionButtons(idx)" Delete
></button> </template>
</span> </button>
</li> <button
class="show-actions"
v-if="itemHasActionButtons(item) && (actionButton === 0 || idx !== selected)"
@click.stop.prevent="showActionButtons(idx)"
></button>
</span>
</li>
</template>
</ul> </ul>
</form> </div>
</div> </form>
</template> </template>
<style scoped lang="sass"> <style scoped lang="sass">
.scroller
//overflow: auto
//position: fixed
//top: 0
//left: 0
//bottom: 0
//right: 0
.note-selector .note-selector
font-size: 13px font-size: 13px
padding: 10px
//background: #48b57e //background: #48b57e
background: #efefef background: #efefef
position: absolute position: absolute
@ -294,37 +311,52 @@
+webapp-mobile +webapp-mobile
max-width: calc(100% - 80px) max-width: calc(100% - 80px)
input .input-container
background: #fff padding: 10px
padding: 4px 5px input
border: 1px solid #ccc background: #fff
box-sizing: border-box padding: 4px 5px
border-radius: 2px border: 1px solid #ccc
width: 100% box-sizing: border-box
margin-bottom: 10px border-radius: 2px
&:focus width: 100%
outline: none
border: 1px solid #fff
outline: 2px solid #48b57e
+dark-mode
background: #3b3b3b
color: rgba(255,255,255, 0.9)
border: 1px solid #5a5a5a
&:focus &:focus
border: 1px solid #3b3b3b outline: none
+webapp-mobile border: 1px solid #fff
font-size: 16px outline: 2px solid #48b57e
max-width: 100% +dark-mode
background: #3b3b3b
color: rgba(255,255,255, 0.9)
border: 1px solid #5a5a5a
&:focus
border: 1px solid #3b3b3b
+webapp-mobile
font-size: 16px
max-width: 100%
.scroller
overflow-y: auto
padding: 0 10px 5px 10px
.items .items
overflow-y: auto > li.line-separator
> li height: 1px
background: rgba(0,0,0, 0.05)
margin-left: -10px
margin-right: -10px
margin-top: 3px
margin-bottom: 3px
+dark-mode
background: rgba(255,255,255, 0.1)
> li.item
position: relative position: relative
border-radius: 3px border-radius: 3px
padding: 3px 12px padding: 3px 12px
line-height: 18px line-height: 18px
display: flex display: flex
align-items: center align-items: center
scroll-margin-top: 6px
scroll-margin-bottom: 6px
&:hover &:hover
background: #e2e2e2 background: #e2e2e2
.action-buttons .show-actions .action-buttons .show-actions
@ -357,6 +389,8 @@
color: rgba(255,255,255, 0.65) color: rgba(255,255,255, 0.65)
&.scratch &.scratch
font-weight: 600 font-weight: 600
&.new-note
font-size: 12px
.name .name
margin-right: 12px margin-right: 12px
flex-shrink: 0 flex-shrink: 0

View File

@ -18,7 +18,10 @@ export const useNotesStore = defineStore("notes", {
currentCursorLine: null, currentCursorLine: null,
currentSelectionSize: null, currentSelectionSize: null,
libraryId: 0, libraryId: 0,
createNoteMode: "new", createNoteParams: {
mode: "new",
nameSuggestion: ""
},
showNoteSelector: false, showNoteSelector: false,
showLanguageSelector: false, showLanguageSelector: false,
@ -52,10 +55,13 @@ export const useNotesStore = defineStore("notes", {
this.closeDialog() this.closeDialog()
this.showNoteSelector = true this.showNoteSelector = true
}, },
openCreateNote(createMode) { openCreateNote(createMode, nameSuggestion) {
createMode = createMode || "new" createMode = createMode || "new"
this.closeDialog() this.closeDialog()
this.createNoteMode = createMode this.createNoteParams = {
mode: createMode || "new",
name: nameSuggestion || ""
}
this.showCreateNote = true this.showCreateNote = true
}, },
closeDialog() { closeDialog() {