Refactoring cursor to be stored in store as key only. A few issues remain.
This commit is contained in:
parent
8da141744e
commit
e20b04189f
|
@ -57,6 +57,8 @@ const globalShortcutHandler = (event: KeyboardEvent) => {
|
||||||
if (
|
if (
|
||||||
event.key === 'ArrowUp' ||
|
event.key === 'ArrowUp' ||
|
||||||
event.key === 'ArrowDown' ||
|
event.key === 'ArrowDown' ||
|
||||||
|
event.key === 'ArrowLeft' ||
|
||||||
|
event.key === 'ArrowRight' ||
|
||||||
(c && event.code === 'Space')
|
(c && event.code === 'Space')
|
||||||
) {
|
) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
@ -65,8 +67,17 @@ const globalShortcutHandler = (event: KeyboardEvent) => {
|
||||||
}
|
}
|
||||||
//console.log("key pressed", event)
|
//console.log("key pressed", event)
|
||||||
// For up/down implement custom fast repeat
|
// For up/down implement custom fast repeat
|
||||||
if (event.key === 'ArrowUp') vert = keyup ? 0 : event.altKey ? -10 : -1
|
let stride = 1
|
||||||
else if (event.key === 'ArrowDown') vert = keyup ? 0 : event.altKey ? 10 : 1
|
if (store.gallery) {
|
||||||
|
const grid = document.querySelector('.gallery') as HTMLElement
|
||||||
|
stride = getComputedStyle(grid).gridTemplateColumns.split(' ').length
|
||||||
|
}
|
||||||
|
else if (event.altKey) stride *= 10
|
||||||
|
// Long if-else machina for all keys we handle here
|
||||||
|
if (event.key === 'ArrowUp') vert = stride * (keyup ? 0 : -1)
|
||||||
|
else if (event.key === 'ArrowDown') vert = stride * (keyup ? 0 : 1)
|
||||||
|
else if (event.key === 'ArrowLeft') vert = keyup ? 0 : -1
|
||||||
|
else if (event.key === 'ArrowRight') vert = keyup ? 0 : 1
|
||||||
// Find: process on keydown so that we can bypass the built-in search hotkey
|
// Find: process on keydown so that we can bypass the built-in search hotkey
|
||||||
else if (!keyup && event.key === 'f' && (event.ctrlKey || event.metaKey)) {
|
else if (!keyup && event.key === 'f' && (event.ctrlKey || event.metaKey)) {
|
||||||
headerMain.value!.toggleSearchInput()
|
headerMain.value!.toggleSearchInput()
|
||||||
|
@ -83,6 +94,10 @@ const globalShortcutHandler = (event: KeyboardEvent) => {
|
||||||
else if (!keyup && event.key === 'a' && (event.ctrlKey || event.metaKey)) {
|
else if (!keyup && event.key === 'a' && (event.ctrlKey || event.metaKey)) {
|
||||||
fileExplorer.toggleSelectAll()
|
fileExplorer.toggleSelectAll()
|
||||||
}
|
}
|
||||||
|
// G toggles Gallery
|
||||||
|
else if (keyup && event.key === 'g') {
|
||||||
|
store.gallery = !store.gallery
|
||||||
|
}
|
||||||
// Keys 1-3 to sort columns
|
// Keys 1-3 to sort columns
|
||||||
else if (
|
else if (
|
||||||
!input &&
|
!input &&
|
||||||
|
|
|
@ -28,11 +28,11 @@
|
||||||
|
|
||||||
<tr
|
<tr
|
||||||
:id="`file-${doc.key}`"
|
:id="`file-${doc.key}`"
|
||||||
:class="{ file: !doc.dir, folder: doc.dir, cursor: store.cursor === doc }"
|
:class="{ file: !doc.dir, folder: doc.dir, cursor: store.cursor === doc.key }"
|
||||||
@click="store.cursor = store.cursor === doc ? null : doc"
|
@click="store.cursor = store.cursor === doc.key ? '' : doc.key"
|
||||||
@contextmenu.prevent="contextMenu($event, doc)"
|
@contextmenu.prevent="contextMenu($event, doc)"
|
||||||
>
|
>
|
||||||
<td class="selection" @click.up.stop="store.cursor = store.cursor === doc ? doc : null">
|
<td class="selection" @click.up.stop="store.cursor = store.cursor === doc.key ? doc.key : ''">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
|
@ -53,12 +53,12 @@
|
||||||
:href="doc.url"
|
:href="doc.url"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
@contextmenu.prevent
|
@contextmenu.prevent
|
||||||
@focus.stop="store.cursor = doc"
|
@focus.stop="store.cursor = doc.key"
|
||||||
@keyup.left="router.back()"
|
@keyup.left="router.back()"
|
||||||
@keyup.right.stop="ev => { if (doc.dir) (ev.target as HTMLElement).click() }"
|
@keyup.right.stop="ev => { if (doc.dir) (ev.target as HTMLElement).click() }"
|
||||||
>{{ doc.name }}</a
|
>{{ doc.name }}</a
|
||||||
>
|
>
|
||||||
<button tabindex=-1 v-if="cursor == doc" class="rename-button" @click="() => (editing = doc)">🖊️</button>
|
<button tabindex=-1 v-if="store.cursor == doc.key" class="rename-button" @click="() => (editing = doc)">🖊️</button>
|
||||||
</template>
|
</template>
|
||||||
</td>
|
</td>
|
||||||
<FileModified :doc=doc :key=nowkey />
|
<FileModified :doc=doc :key=nowkey />
|
||||||
|
@ -142,18 +142,18 @@ defineExpose({
|
||||||
if (order) store.toggleSort(order as SortOrder)
|
if (order) store.toggleSort(order as SortOrder)
|
||||||
},
|
},
|
||||||
isCursor() {
|
isCursor() {
|
||||||
return store.cursor !== null && editing.value === null
|
return store.cursor && editing.value === null
|
||||||
},
|
},
|
||||||
cursorRename() {
|
cursorRename() {
|
||||||
editing.value = store.cursor
|
editing.value = store.cursor
|
||||||
},
|
},
|
||||||
cursorSelect() {
|
cursorSelect() {
|
||||||
const doc = store.cursor
|
const key = store.cursor
|
||||||
if (!doc) return
|
if (!key) return
|
||||||
if (store.selected.has(doc.key)) {
|
if (store.selected.has(key)) {
|
||||||
store.selected.delete(doc.key)
|
store.selected.delete(key)
|
||||||
} else {
|
} else {
|
||||||
store.selected.add(doc.key)
|
store.selected.add(key)
|
||||||
}
|
}
|
||||||
this.cursorMove(1)
|
this.cursorMove(1)
|
||||||
},
|
},
|
||||||
|
@ -161,17 +161,17 @@ defineExpose({
|
||||||
// Move cursor up or down (keyboard navigation)
|
// Move cursor up or down (keyboard navigation)
|
||||||
const docs = props.documents
|
const docs = props.documents
|
||||||
if (docs.length === 0) {
|
if (docs.length === 0) {
|
||||||
store.cursor = null
|
store.cursor = ''
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const N = docs.length
|
const N = docs.length
|
||||||
const mod = (a: number, b: number) => ((a % b) + b) % b
|
const mod = (a: number, b: number) => ((a % b) + b) % b
|
||||||
const increment = (i: number, d: number) => mod(i + d, N + 1)
|
const increment = (i: number, d: number) => mod(i + d, N + 1)
|
||||||
const index =
|
const index =
|
||||||
store.cursor !== null ? docs.indexOf(store.cursor) : docs.length
|
store.cursor ? docs.find(doc => doc.key === store.cursor) : docs.length
|
||||||
const moveto = increment(index, d)
|
const moveto = increment(index, d)
|
||||||
store.cursor = docs[moveto] ?? null
|
store.cursor = docs[moveto]?.key ?? ''
|
||||||
const tr = store.cursor ? document.getElementById(`file-${store.cursor.key}`) : null
|
const tr = store.cursor ? document.getElementById(`file-${store.cursor}`) : ''
|
||||||
if (select) {
|
if (select) {
|
||||||
// Go forwards, possibly wrapping over the end; the last entry is not toggled
|
// Go forwards, possibly wrapping over the end; the last entry is not toggled
|
||||||
let [begin, end] = d > 0 ? [index, moveto] : [moveto, index]
|
let [begin, end] = d > 0 ? [index, moveto] : [moveto, index]
|
||||||
|
@ -201,18 +201,18 @@ const focusBreadcrumb = () => {
|
||||||
let scrolltimer: any = null
|
let scrolltimer: any = null
|
||||||
let scrolltr: any = null
|
let scrolltr: any = null
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (store.cursor && store.cursor !== editing.value) editing.value = null
|
if (store.cursor && store.cursor !== editing.value?.key) editing.value = null
|
||||||
if (editing.value) store.cursor = editing.value
|
if (editing.value) store.cursor = editing.value?.key
|
||||||
if (store.cursor) {
|
if (store.cursor) {
|
||||||
const a = document.querySelector(
|
const a = document.querySelector(
|
||||||
`#file-${store.cursor.key} .name a`
|
`#file-${store.cursor} .name a`
|
||||||
) as HTMLAnchorElement | null
|
) as HTMLAnchorElement | null
|
||||||
if (a) a.focus()
|
if (a) a.focus()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (!props.documents.length && store.cursor) {
|
if (!props.documents.length && store.cursor) {
|
||||||
store.cursor = null
|
store.cursor = ''
|
||||||
focusBreadcrumb()
|
focusBreadcrumb()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -286,7 +286,7 @@ const allSelected = computed({
|
||||||
const loc = computed(() => props.path.join('/'))
|
const loc = computed(() => props.path.join('/'))
|
||||||
|
|
||||||
const contextMenu = (ev: MouseEvent, doc: Doc) => {
|
const contextMenu = (ev: MouseEvent, doc: Doc) => {
|
||||||
store.cursor = doc
|
store.cursor = doc.key
|
||||||
ContextMenu.showContextMenu({
|
ContextMenu.showContextMenu({
|
||||||
x: ev.x, y: ev.y, items: [
|
x: ev.x, y: ev.y, items: [
|
||||||
{ label: 'Rename', onClick: () => { editing.value = doc } },
|
{ label: 'Rename', onClick: () => { editing.value = doc } },
|
||||||
|
|
|
@ -51,43 +51,6 @@ const rename = (doc: Doc, newName: string) => {
|
||||||
}
|
}
|
||||||
doc.name = newName // We should get an update from watch but this is quicker
|
doc.name = newName // We should get an update from watch but this is quicker
|
||||||
}
|
}
|
||||||
const cursorMove = (d: number, select = false) => {
|
|
||||||
// Move cursor up or down (keyboard navigation)
|
|
||||||
const docs = props.documents
|
|
||||||
if (docs.length === 0) {
|
|
||||||
store.cursor = null
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const N = docs.length
|
|
||||||
const mod = (a: number, b: number) => ((a % b) + b) % b
|
|
||||||
const increment = (i: number, d: number) => mod(i + d, N + 1)
|
|
||||||
const index =
|
|
||||||
store.cursor !== null ? docs.indexOf(store.cursor) : docs.length
|
|
||||||
const moveto = increment(index, d)
|
|
||||||
store.cursor = docs[moveto] ?? null
|
|
||||||
const tr = store.cursor ? document.getElementById(`file-${store.cursor.key}`) : null
|
|
||||||
if (select) {
|
|
||||||
// Go forwards, possibly wrapping over the end; the last entry is not toggled
|
|
||||||
let [begin, end] = d > 0 ? [index, moveto] : [moveto, index]
|
|
||||||
for (let p = begin; p !== end; p = increment(p, 1)) {
|
|
||||||
if (p === N) continue
|
|
||||||
const key = docs[p].key
|
|
||||||
if (store.selected.has(key)) store.selected.delete(key)
|
|
||||||
else store.selected.add(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// @ts-ignore
|
|
||||||
scrolltr = tr
|
|
||||||
if (!scrolltimer) {
|
|
||||||
scrolltimer = setTimeout(() => {
|
|
||||||
if (scrolltr)
|
|
||||||
scrolltr.scrollIntoView({ block: 'center', behavior: 'smooth' })
|
|
||||||
scrolltimer = null
|
|
||||||
}, 300)
|
|
||||||
}
|
|
||||||
if (moveto === N) focusBreadcrumb()
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
newFolder() {
|
newFolder() {
|
||||||
const now = Math.floor(Date.now() / 1000)
|
const now = Math.floor(Date.now() / 1000)
|
||||||
|
@ -109,22 +72,57 @@ defineExpose({
|
||||||
if (order) store.toggleSort(order as SortOrder)
|
if (order) store.toggleSort(order as SortOrder)
|
||||||
},
|
},
|
||||||
isCursor() {
|
isCursor() {
|
||||||
return store.cursor !== null && editing.value === null
|
return store.cursor && editing.value === null
|
||||||
},
|
},
|
||||||
cursorRename() {
|
cursorRename() {
|
||||||
editing.value = store.cursor
|
editing.value = props.documents.find(doc => doc.key === store.cursor) ?? null
|
||||||
},
|
},
|
||||||
cursorSelect() {
|
cursorSelect() {
|
||||||
const doc = store.cursor
|
const key = store.cursor
|
||||||
if (!doc) return
|
if (!key) return
|
||||||
if (store.selected.has(doc.key)) {
|
if (store.selected.has(key)) {
|
||||||
store.selected.delete(doc.key)
|
store.selected.delete(key)
|
||||||
} else {
|
} else {
|
||||||
store.selected.add(doc.key)
|
store.selected.add(key)
|
||||||
}
|
}
|
||||||
this.cursorMove(1)
|
this.cursorMove(1)
|
||||||
},
|
},
|
||||||
cursorMove,
|
cursorMove(d: number, select = false) {
|
||||||
|
// Move cursor up or down (keyboard navigation)
|
||||||
|
const docs = props.documents
|
||||||
|
if (docs.length === 0) {
|
||||||
|
store.cursor = ''
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const N = docs.length
|
||||||
|
const mod = (a: number, b: number) => ((a % b) + b) % b
|
||||||
|
const increment = (i: number, d: number) => mod(i + d, N + 1)
|
||||||
|
const index =
|
||||||
|
store.cursor ? docs.findIndex(doc => doc.key === store.cursor) : docs.length
|
||||||
|
const moveto = increment(index, d)
|
||||||
|
store.cursor = docs[moveto]?.key ?? ''
|
||||||
|
const tr = store.cursor ? document.getElementById(`file-${store.cursor}`) : ''
|
||||||
|
if (select) {
|
||||||
|
// Go forwards, possibly wrapping over the end; the last entry is not toggled
|
||||||
|
let [begin, end] = d > 0 ? [index, moveto] : [moveto, index]
|
||||||
|
for (let p = begin; p !== end; p = increment(p, 1)) {
|
||||||
|
if (p === N) continue
|
||||||
|
const key = docs[p].key
|
||||||
|
if (store.selected.has(key)) store.selected.delete(key)
|
||||||
|
else store.selected.add(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
scrolltr = tr
|
||||||
|
if (!scrolltimer) {
|
||||||
|
scrolltimer = setTimeout(() => {
|
||||||
|
if (scrolltr)
|
||||||
|
scrolltr.scrollIntoView({ block: 'center', behavior: 'smooth' })
|
||||||
|
scrolltimer = null
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
if (moveto === N) focusBreadcrumb()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
const focusBreadcrumb = () => {
|
const focusBreadcrumb = () => {
|
||||||
const el = document.querySelector('.breadcrumb') as HTMLElement | null
|
const el = document.querySelector('.breadcrumb') as HTMLElement | null
|
||||||
|
@ -133,18 +131,18 @@ const focusBreadcrumb = () => {
|
||||||
let scrolltimer: any = null
|
let scrolltimer: any = null
|
||||||
let scrolltr: any = null
|
let scrolltr: any = null
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (store.cursor && store.cursor !== editing.value) editing.value = null
|
if (store.cursor && store.cursor !== editing.value?.key) editing.value = null
|
||||||
if (editing.value) store.cursor = editing.value
|
if (editing.value) store.cursor = editing.value.key
|
||||||
if (store.cursor) {
|
if (store.cursor) {
|
||||||
const a = document.querySelector(
|
const a = document.querySelector(
|
||||||
`#file-${store.cursor.key} .name a`
|
`#file-${store.cursor} a`
|
||||||
) as HTMLAnchorElement | null
|
) as HTMLAnchorElement | null
|
||||||
if (a) a.focus()
|
if (a) a.focus()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (!props.documents.length && store.cursor) {
|
if (!props.documents.length && store.cursor) {
|
||||||
store.cursor = null
|
store.cursor = ''
|
||||||
focusBreadcrumb()
|
focusBreadcrumb()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -218,7 +216,7 @@ const allSelected = computed({
|
||||||
const loc = computed(() => props.path.join('/'))
|
const loc = computed(() => props.path.join('/'))
|
||||||
|
|
||||||
const contextMenu = (ev: MouseEvent, doc: Doc) => {
|
const contextMenu = (ev: MouseEvent, doc: Doc) => {
|
||||||
store.cursor = doc
|
store.cursor = doc.key
|
||||||
ContextMenu.showContextMenu({
|
ContextMenu.showContextMenu({
|
||||||
x: ev.x, y: ev.y, items: [
|
x: ev.x, y: ev.y, items: [
|
||||||
{ label: 'Rename', onClick: () => { editing.value = doc } },
|
{ label: 'Rename', onClick: () => { editing.value = doc } },
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
<template>
|
<template>
|
||||||
<a
|
<a
|
||||||
|
:id="`file-${doc.key}`"
|
||||||
:href="doc.url"
|
:href="doc.url"
|
||||||
tabindex=0
|
tabindex=0
|
||||||
|
:class="{ file: !doc.dir, folder: doc.dir, cursor: store.cursor === doc.key }"
|
||||||
@contextmenu.prevent
|
@contextmenu.prevent
|
||||||
@focus.stop="store.cursor = doc"
|
@focus.stop="store.cursor = doc.key"
|
||||||
@click="ev => {
|
@click="ev => {
|
||||||
|
store.cursor = store.cursor === doc.key ? '' : doc.key
|
||||||
if (media) { media.play(); ev.preventDefault() }
|
if (media) { media.play(); ev.preventDefault() }
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<figure
|
<figure>
|
||||||
:id="`file-${doc.key}`"
|
|
||||||
:class="{ file: !doc.dir, folder: doc.dir, cursor: store.cursor === doc }"
|
|
||||||
@click="store.cursor = store.cursor === doc ? null : doc"
|
|
||||||
@click.up.stop="store.cursor = store.cursor === doc ? doc : null"
|
|
||||||
>
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<MediaPreview ref=media :doc="doc" />
|
<MediaPreview ref=media :doc="doc" />
|
||||||
<caption>
|
<caption>
|
||||||
|
@ -58,7 +56,7 @@ figure caption {
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
text-shadow: 0 0 .2rem #000, 0 0 1rem #000;
|
text-shadow: 0 0 .2rem #000, 0 0 1rem #000;
|
||||||
}
|
}
|
||||||
figure.cursor caption {
|
.cursor caption {
|
||||||
background: var(--accent-color);
|
background: var(--accent-color);
|
||||||
}
|
}
|
||||||
caption {
|
caption {
|
||||||
|
|
|
@ -24,7 +24,7 @@ export const useMainStore = defineStore({
|
||||||
error: '' as string,
|
error: '' as string,
|
||||||
connected: false,
|
connected: false,
|
||||||
gallery: false,
|
gallery: false,
|
||||||
cursor: shallowRef<Doc | null>(null),
|
cursor: '' as string,
|
||||||
server: {} as Record<string, any>,
|
server: {} as Record<string, any>,
|
||||||
prefs: {
|
prefs: {
|
||||||
sortListing: '' as SortOrder,
|
sortListing: '' as SortOrder,
|
||||||
|
|
Loading…
Reference in New Issue
Block a user