feat(phase-4-09): create new components — FolderRow, FolderBreadcrumb, FolderDeleteModal, ShareModal, DocumentPreviewModal, SearchBar, SortControls, AuditLogTab
- FolderRow: inline rename, three-dot menu, delete/rename callbacks, outside-click close - FolderBreadcrumb: truncation at depth > 4, nav aria-label, ol structure - FolderDeleteModal: role=dialog, warning icon, doc count in body, Keep/Delete buttons - ShareModal: handle input, recipients list with revoke, 404/409 error handling - DocumentPreviewModal: iframe with proxy URL only (never presigned), Escape/overlay close - SearchBar: role=search, aria-label, Escape clears - SortControls: aria-pressed, direction indicator, toggle vs switch logic - AuditLogTab: filters, paginated table, CSV export via window.location.href - api/client.js: add adminListAuditLog function
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<!-- Overlay -->
|
||||
<div
|
||||
ref="overlayRef"
|
||||
class="fixed inset-0 bg-black/60 z-50 flex flex-col"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="Document preview"
|
||||
@click="handleOverlayClick"
|
||||
>
|
||||
<!-- Header bar -->
|
||||
<div class="bg-white border-b border-gray-200 px-6 py-3 flex items-center justify-between shrink-0">
|
||||
<span class="text-sm font-medium text-gray-900 truncate max-w-xs">{{ doc.filename || doc.original_name }}</span>
|
||||
<button
|
||||
@click="emit('close')"
|
||||
aria-label="Close preview"
|
||||
class="text-gray-400 hover:text-gray-600 transition-colors ml-4 shrink-0"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- iframe content -->
|
||||
<div class="flex-1 overflow-hidden">
|
||||
<iframe
|
||||
class="w-full h-full border-0"
|
||||
:src="proxyUrl"
|
||||
title="Document preview"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
doc: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close'])
|
||||
|
||||
const overlayRef = ref(null)
|
||||
|
||||
const proxyUrl = computed(() => `/api/documents/${props.doc.id}/content`)
|
||||
|
||||
function handleOverlayClick(e) {
|
||||
if (e.target === overlayRef.value) {
|
||||
emit('close')
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeydown(e) {
|
||||
if (e.key === 'Escape') {
|
||||
emit('close')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', handleKeydown)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('keydown', handleKeydown)
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user