diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 4abd6d9..47d57cb 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -57,6 +57,8 @@ const globalShortcutHandler = (event: KeyboardEvent) => {
if (
event.key === 'ArrowUp' ||
event.key === 'ArrowDown' ||
+ event.key === 'ArrowLeft' ||
+ event.key === 'ArrowRight' ||
(c && event.code === 'Space')
) {
event.preventDefault()
@@ -65,8 +67,17 @@ const globalShortcutHandler = (event: KeyboardEvent) => {
}
//console.log("key pressed", event)
// For up/down implement custom fast repeat
- if (event.key === 'ArrowUp') vert = keyup ? 0 : event.altKey ? -10 : -1
- else if (event.key === 'ArrowDown') vert = keyup ? 0 : event.altKey ? 10 : 1
+ let stride = 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
else if (!keyup && event.key === 'f' && (event.ctrlKey || event.metaKey)) {
headerMain.value!.toggleSearchInput()
@@ -83,6 +94,10 @@ const globalShortcutHandler = (event: KeyboardEvent) => {
else if (!keyup && event.key === 'a' && (event.ctrlKey || event.metaKey)) {
fileExplorer.toggleSelectAll()
}
+ // G toggles Gallery
+ else if (keyup && event.key === 'g') {
+ store.gallery = !store.gallery
+ }
// Keys 1-3 to sort columns
else if (
!input &&
diff --git a/frontend/src/components/FileExplorer.vue b/frontend/src/components/FileExplorer.vue
index cdd4b10..16d2132 100644
--- a/frontend/src/components/FileExplorer.vue
+++ b/frontend/src/components/FileExplorer.vue
@@ -28,11 +28,11 @@
-
+ |
{ if (doc.dir) (ev.target as HTMLElement).click() }"
>{{ doc.name }}
-
+
|
@@ -142,18 +142,18 @@ defineExpose({
if (order) store.toggleSort(order as SortOrder)
},
isCursor() {
- return store.cursor !== null && editing.value === null
+ return store.cursor && editing.value === null
},
cursorRename() {
editing.value = store.cursor
},
cursorSelect() {
- const doc = store.cursor
- if (!doc) return
- if (store.selected.has(doc.key)) {
- store.selected.delete(doc.key)
+ const key = store.cursor
+ if (!key) return
+ if (store.selected.has(key)) {
+ store.selected.delete(key)
} else {
- store.selected.add(doc.key)
+ store.selected.add(key)
}
this.cursorMove(1)
},
@@ -161,17 +161,17 @@ defineExpose({
// Move cursor up or down (keyboard navigation)
const docs = props.documents
if (docs.length === 0) {
- store.cursor = null
+ 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 !== null ? docs.indexOf(store.cursor) : docs.length
+ store.cursor ? docs.find(doc => doc.key === 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
+ 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]
@@ -201,18 +201,18 @@ const focusBreadcrumb = () => {
let scrolltimer: any = null
let scrolltr: any = null
watchEffect(() => {
- if (store.cursor && store.cursor !== editing.value) editing.value = null
- if (editing.value) store.cursor = editing.value
+ if (store.cursor && store.cursor !== editing.value?.key) editing.value = null
+ if (editing.value) store.cursor = editing.value?.key
if (store.cursor) {
const a = document.querySelector(
- `#file-${store.cursor.key} .name a`
+ `#file-${store.cursor} .name a`
) as HTMLAnchorElement | null
if (a) a.focus()
}
})
watchEffect(() => {
if (!props.documents.length && store.cursor) {
- store.cursor = null
+ store.cursor = ''
focusBreadcrumb()
}
})
@@ -286,7 +286,7 @@ const allSelected = computed({
const loc = computed(() => props.path.join('/'))
const contextMenu = (ev: MouseEvent, doc: Doc) => {
- store.cursor = doc
+ store.cursor = doc.key
ContextMenu.showContextMenu({
x: ev.x, y: ev.y, items: [
{ label: 'Rename', onClick: () => { editing.value = doc } },
diff --git a/frontend/src/components/Gallery.vue b/frontend/src/components/Gallery.vue
index e2ed9a2..04527e2 100644
--- a/frontend/src/components/Gallery.vue
+++ b/frontend/src/components/Gallery.vue
@@ -51,43 +51,6 @@ const rename = (doc: Doc, newName: string) => {
}
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({
newFolder() {
const now = Math.floor(Date.now() / 1000)
@@ -109,22 +72,57 @@ defineExpose({
if (order) store.toggleSort(order as SortOrder)
},
isCursor() {
- return store.cursor !== null && editing.value === null
+ return store.cursor && editing.value === null
},
cursorRename() {
- editing.value = store.cursor
+ editing.value = props.documents.find(doc => doc.key === store.cursor) ?? null
},
cursorSelect() {
- const doc = store.cursor
- if (!doc) return
- if (store.selected.has(doc.key)) {
- store.selected.delete(doc.key)
+ const key = store.cursor
+ if (!key) return
+ if (store.selected.has(key)) {
+ store.selected.delete(key)
} else {
- store.selected.add(doc.key)
+ store.selected.add(key)
}
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 el = document.querySelector('.breadcrumb') as HTMLElement | null
@@ -133,18 +131,18 @@ const focusBreadcrumb = () => {
let scrolltimer: any = null
let scrolltr: any = null
watchEffect(() => {
- if (store.cursor && store.cursor !== editing.value) editing.value = null
- if (editing.value) store.cursor = editing.value
+ if (store.cursor && store.cursor !== editing.value?.key) editing.value = null
+ if (editing.value) store.cursor = editing.value.key
if (store.cursor) {
const a = document.querySelector(
- `#file-${store.cursor.key} .name a`
+ `#file-${store.cursor} a`
) as HTMLAnchorElement | null
if (a) a.focus()
}
})
watchEffect(() => {
if (!props.documents.length && store.cursor) {
- store.cursor = null
+ store.cursor = ''
focusBreadcrumb()
}
})
@@ -218,7 +216,7 @@ const allSelected = computed({
const loc = computed(() => props.path.join('/'))
const contextMenu = (ev: MouseEvent, doc: Doc) => {
- store.cursor = doc
+ store.cursor = doc.key
ContextMenu.showContextMenu({
x: ev.x, y: ev.y, items: [
{ label: 'Rename', onClick: () => { editing.value = doc } },
diff --git a/frontend/src/components/GalleryFigure.vue b/frontend/src/components/GalleryFigure.vue
index d27116b..b0ee900 100644
--- a/frontend/src/components/GalleryFigure.vue
+++ b/frontend/src/components/GalleryFigure.vue
@@ -1,19 +1,17 @@
{
+ store.cursor = store.cursor === doc.key ? '' : doc.key
if (media) { media.play(); ev.preventDefault() }
}"
>
-