feat(10-09): plumb keyboard shortcut ref chain — DropZone→SearchBar→StorageBrowser→FileManagerView

- DropZone: defineExpose({ triggerInput }) to expose file picker trigger
- SearchBar: add inputEl ref + defineExpose({ focus }) for / shortcut
- StorageBrowser: add dropZoneRef + searchBarRef; expand defineExpose to include triggerUpload, focusSearch, clearSearch
- FileManagerView: add defineExpose delegating all four methods to browserRef via optional chaining
This commit is contained in:
curo1305
2026-06-15 20:35:38 +02:00
parent 089af90e6d
commit aaa0532af9
4 changed files with 29 additions and 4 deletions
@@ -1,6 +1,7 @@
<template> <template>
<div role="search"> <div role="search">
<input <input
ref="inputEl"
:value="modelValue" :value="modelValue"
type="search" type="search"
:placeholder="placeholder" :placeholder="placeholder"
@@ -13,6 +14,8 @@
</template> </template>
<script setup> <script setup>
import { ref } from 'vue'
defineProps({ defineProps({
modelValue: { modelValue: {
type: String, type: String,
@@ -25,4 +28,8 @@ defineProps({
}) })
const emit = defineEmits(['update:modelValue']) const emit = defineEmits(['update:modelValue'])
const inputEl = ref(null)
defineExpose({ focus() { inputEl.value?.focus() } })
</script> </script>
@@ -10,7 +10,7 @@
@navigate="$emit('breadcrumb-navigate', $event)" @navigate="$emit('breadcrumb-navigate', $event)"
/> />
<div class="ml-auto flex items-center gap-2 shrink-0"> <div class="ml-auto flex items-center gap-2 shrink-0">
<SearchBar v-if="showSearch" :model-value="searchQuery" @update:modelValue="$emit('search-change', $event)" /> <SearchBar v-if="showSearch" ref="searchBarRef" :model-value="searchQuery" @update:modelValue="$emit('search-change', $event)" />
<SortControls <SortControls
v-if="showSearch" v-if="showSearch"
:sort="sortField" :sort="sortField"
@@ -36,7 +36,7 @@
<!-- Upload zone --> <!-- Upload zone -->
<div class="px-6 pt-5 pb-3"> <div class="px-6 pt-5 pb-3">
<DropZone @files-selected="$emit('upload', $event)" /> <DropZone ref="dropZoneRef" @files-selected="$emit('upload', $event)" />
<UploadProgress :items="uploadQueue" /> <UploadProgress :items="uploadQueue" />
</div> </div>
@@ -321,6 +321,11 @@ function topicColor(name) {
return props.topicColorFn(name) return props.topicColorFn(name)
} }
// ── Child component refs ──────────────────────────────────────────────────────
const dropZoneRef = ref(null)
const searchBarRef = ref(null)
// ── New folder inline input ─────────────────────────────────────────────────── // ── New folder inline input ───────────────────────────────────────────────────
const showNewFolderInput = ref(false) const showNewFolderInput = ref(false)
@@ -346,8 +351,12 @@ async function submitNewFolder() {
emit('folder-create', { name, onError: (msg) => { newFolderError.value = msg }, onSuccess: cancelNewFolder }) emit('folder-create', { name, onError: (msg) => { newFolderError.value = msg }, onSuccess: cancelNewFolder })
} }
/** Called by the parent view when the toolbar "New folder" button is clicked. */ defineExpose({
defineExpose({ startNewFolder }) startNewFolder,
triggerUpload: () => dropZoneRef.value?.triggerInput(),
focusSearch: () => searchBarRef.value?.focus(),
clearSearch: () => emit('search-change', ''),
})
// ── Rename ──────────────────────────────────────────────────────────────────── // ── Rename ────────────────────────────────────────────────────────────────────
@@ -48,6 +48,8 @@ function triggerInput() {
inputRef.value?.click() inputRef.value?.click()
} }
defineExpose({ triggerInput })
function onDrop(e) { function onDrop(e) {
dragging.value = false dragging.value = false
const files = Array.from(e.dataTransfer?.files || []) const files = Array.from(e.dataTransfer?.files || [])
+7
View File
@@ -180,4 +180,11 @@ async function doDeleteDoc(docId) {
function topicColor(name) { function topicColor(name) {
return topicsStore.topics.find(t => t.name === name)?.color ?? '#6366f1' return topicsStore.topics.find(t => t.name === name)?.color ?? '#6366f1'
} }
defineExpose({
focusSearch: () => browserRef.value?.focusSearch?.(),
triggerUpload: () => browserRef.value?.triggerUpload?.(),
startNewFolder: () => browserRef.value?.startNewFolder?.(),
clearSearch: () => browserRef.value?.clearSearch?.(),
})
</script> </script>