Init Repo
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
import { ref, computed, onMounted, onUnmounted, type Ref, type ComputedRef } from 'vue'
|
||||
|
||||
export interface UseDetailPanelOptions {
|
||||
/**
|
||||
* Function to check if any dialogs are currently open
|
||||
* This prevents keyboard shortcuts from triggering when dialogs are active
|
||||
*/
|
||||
isDialogOpen?: () => boolean
|
||||
|
||||
/**
|
||||
* Session storage key for persisting auto-enable state
|
||||
* If not provided, state won't be persisted
|
||||
*/
|
||||
sessionStorageKey?: string
|
||||
}
|
||||
|
||||
export interface UseDetailPanelReturn<T> {
|
||||
/** Auto-enable toggle state - controls whether panel shows automatically on selection */
|
||||
isDetailPanelEnabled: Ref<boolean>
|
||||
|
||||
/** Manual visibility control - controlled by keyboard shortcuts */
|
||||
isDetailPanelVisible: Ref<boolean>
|
||||
|
||||
/** Currently selected entity */
|
||||
selectedEntity: Ref<T | null>
|
||||
|
||||
/** Mobile detail sheet visibility */
|
||||
showMobileDetail: Ref<boolean>
|
||||
|
||||
/** Computed property that determines if panel should be shown */
|
||||
showPanel: ComputedRef<boolean>
|
||||
|
||||
/** Toggle the auto-enable state */
|
||||
toggleDetailPanelEnabled: () => void
|
||||
|
||||
/** Close the detail panel and clear selection */
|
||||
closeDetailPanel: () => void
|
||||
|
||||
/** Select an entity and optionally show mobile detail */
|
||||
selectEntity: (entity: T) => void
|
||||
|
||||
/** Handle row click with proper selection logic */
|
||||
handleRowClick: (entity: T, event?: MouseEvent) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Composable for consistent detail panel behavior across entity browsers
|
||||
*
|
||||
* Provides:
|
||||
* - Auto-enable toggle functionality
|
||||
* - Manual keyboard control ('i' key)
|
||||
* - Mobile sheet support
|
||||
* - Session persistence
|
||||
* - Consistent selection behavior
|
||||
*/
|
||||
export function useDetailPanel<T extends { id: number }>(
|
||||
options: UseDetailPanelOptions = {}
|
||||
): UseDetailPanelReturn<T> {
|
||||
const { isDialogOpen, sessionStorageKey } = options
|
||||
|
||||
// State variables
|
||||
const isDetailPanelEnabled = ref(true) // Auto-enable toggle state
|
||||
const isDetailPanelVisible = ref(false) // Manual visibility control
|
||||
const selectedEntity = ref<T | null>(null)
|
||||
const showMobileDetail = ref(false)
|
||||
|
||||
// Load persisted auto-enable state from session storage
|
||||
if (sessionStorageKey) {
|
||||
const stored = sessionStorage.getItem(sessionStorageKey)
|
||||
if (stored !== null) {
|
||||
try {
|
||||
isDetailPanelEnabled.value = JSON.parse(stored)
|
||||
} catch {
|
||||
// Fall back to default if parsing fails
|
||||
isDetailPanelEnabled.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Combined panel visibility logic (OR condition)
|
||||
const showPanel = computed(() =>
|
||||
selectedEntity.value && (isDetailPanelEnabled.value || isDetailPanelVisible.value)
|
||||
)
|
||||
|
||||
// Keyboard shortcut handler
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
// Only handle 'i' key when an entity is selected
|
||||
if (
|
||||
event.key.toLowerCase() === 'i' &&
|
||||
selectedEntity.value
|
||||
) {
|
||||
// Don't trigger if dialogs are open
|
||||
if (isDialogOpen && isDialogOpen()) {
|
||||
return
|
||||
}
|
||||
|
||||
// Don't trigger if user is typing in an input field
|
||||
const target = event.target as HTMLElement
|
||||
if (target && (
|
||||
target.tagName === 'INPUT' ||
|
||||
target.tagName === 'TEXTAREA' ||
|
||||
target.contentEditable === 'true'
|
||||
)) {
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
|
||||
// Toggle detail panel visibility
|
||||
if (window.innerWidth >= 1024) {
|
||||
// Desktop: toggle manual visibility
|
||||
isDetailPanelVisible.value = !isDetailPanelVisible.value
|
||||
} else {
|
||||
// Mobile: toggle mobile detail sheet
|
||||
showMobileDetail.value = !showMobileDetail.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Methods
|
||||
const toggleDetailPanelEnabled = () => {
|
||||
isDetailPanelEnabled.value = !isDetailPanelEnabled.value
|
||||
|
||||
// Persist to session storage if key provided
|
||||
if (sessionStorageKey) {
|
||||
sessionStorage.setItem(sessionStorageKey, JSON.stringify(isDetailPanelEnabled.value))
|
||||
}
|
||||
}
|
||||
|
||||
const closeDetailPanel = () => {
|
||||
selectedEntity.value = null
|
||||
showMobileDetail.value = false
|
||||
isDetailPanelVisible.value = false
|
||||
}
|
||||
|
||||
const selectEntity = (entity: T) => {
|
||||
selectedEntity.value = entity
|
||||
// Show mobile detail sheet on small screens if auto-enabled
|
||||
if (isDetailPanelEnabled.value && window.innerWidth < 1024) {
|
||||
showMobileDetail.value = true
|
||||
}
|
||||
// Don't reset manual visibility when selecting a new entity
|
||||
// This allows the panel to stay open when manually activated with 'i' key
|
||||
}
|
||||
|
||||
const handleRowClick = (entity: T, event?: MouseEvent) => {
|
||||
// Don't handle row clicks if any dialog is open
|
||||
if (isDialogOpen && isDialogOpen()) {
|
||||
return
|
||||
}
|
||||
|
||||
// Single click selects the entity
|
||||
// If detail panel is auto-enabled, it will automatically show due to the reactive condition
|
||||
selectedEntity.value = entity
|
||||
|
||||
// Don't reset manual visibility when clicking a row
|
||||
// This preserves manual panel state when activated with 'i' key
|
||||
|
||||
// Show mobile detail sheet on small screens if auto-enabled
|
||||
if (isDetailPanelEnabled.value && window.innerWidth < 1024) {
|
||||
showMobileDetail.value = true
|
||||
}
|
||||
}
|
||||
|
||||
// Lifecycle hooks for keyboard event handling
|
||||
onMounted(() => {
|
||||
document.addEventListener('keydown', handleKeyDown)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('keydown', handleKeyDown)
|
||||
})
|
||||
|
||||
return {
|
||||
isDetailPanelEnabled,
|
||||
isDetailPanelVisible,
|
||||
selectedEntity,
|
||||
showMobileDetail,
|
||||
showPanel,
|
||||
toggleDetailPanelEnabled,
|
||||
closeDetailPanel,
|
||||
selectEntity,
|
||||
handleRowClick
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user