Skip to content

Commit dad8790

Browse files
committed
Refactor: Deduplicated templates
1 parent 720a493 commit dad8790

1 file changed

Lines changed: 122 additions & 163 deletions

File tree

apps/desktop/src/lib/file-explorer/pane/DualPaneExplorer.svelte

Lines changed: 122 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,7 @@
149149
let leftPaneWidthPercent = $state(50)
150150
151151
let containerElement: HTMLDivElement | undefined = $state()
152-
let leftPaneRef: FilePane | undefined = $state()
153-
let rightPaneRef: FilePane | undefined = $state()
152+
const paneRefs = $state<Record<'left' | 'right', FilePane | undefined>>({ left: undefined, right: undefined })
154153
let unlistenSettings: UnlistenFn | undefined
155154
let unlistenViewMode: UnlistenFn | undefined
156155
let unlistenVolumeMount: UnlistenFn | undefined
@@ -216,13 +215,15 @@
216215
let dropTargetFolderEl = $state<HTMLElement | null>(null)
217216
218217
// Refs for pane wrapper elements (used for hit-testing drop targets)
219-
let leftPaneWrapperEl: HTMLDivElement | undefined = $state()
220-
let rightPaneWrapperEl: HTMLDivElement | undefined = $state()
218+
const paneWrapperEls = $state<Record<'left' | 'right', HTMLDivElement | undefined>>({
219+
left: undefined,
220+
right: undefined,
221+
})
221222
222223
// Dialog state (transfer, new folder, alert, error)
223224
const dialogs = createDialogState({
224-
getLeftPaneRef: () => leftPaneRef,
225-
getRightPaneRef: () => rightPaneRef,
225+
getLeftPaneRef: () => paneRefs.left,
226+
getRightPaneRef: () => paneRefs.right,
226227
getFocusedPaneRef: () => getPaneRef(focusedPane),
227228
getShowHiddenFiles: () => showHiddenFiles,
228229
onRefocus: () => containerElement?.focus(),
@@ -236,7 +237,7 @@
236237
// --- Pane accessor helpers ---
237238
238239
function getPaneRef(pane: 'left' | 'right'): FilePane | undefined {
239-
return pane === 'left' ? leftPaneRef : rightPaneRef
240+
return paneRefs[pane]
240241
}
241242
242243
function getPanePath(pane: 'left' | 'right'): string {
@@ -283,6 +284,22 @@
283284
getActiveTab(getTabMgr(pane)).viewMode = viewMode
284285
}
285286
287+
function getPaneViewMode(pane: 'left' | 'right'): ViewMode {
288+
return pane === 'left' ? leftViewMode : rightViewMode
289+
}
290+
291+
function getPaneVolumePath(pane: 'left' | 'right'): string {
292+
return pane === 'left' ? leftVolumePath : rightVolumePath
293+
}
294+
295+
function getPaneVolumeName(pane: 'left' | 'right'): string | undefined {
296+
return pane === 'left' ? leftVolumeName : rightVolumeName
297+
}
298+
299+
function getPaneWidth(pane: 'left' | 'right'): number {
300+
return pane === 'left' ? leftPaneWidthPercent : 100 - leftPaneWidthPercent
301+
}
302+
286303
function otherPane(pane: 'left' | 'right'): 'left' | 'right' {
287304
return pane === 'left' ? 'right' : 'left'
288305
}
@@ -570,18 +587,14 @@
570587
function routeToVolumeChooser(e: KeyboardEvent): boolean {
571588
// Check if EITHER pane has a volume chooser open - if so, route events there
572589
// This is important because F1/F2 can open a volume chooser on the non-focused pane
573-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
574-
if (leftPaneRef?.isVolumeChooserOpen?.()) {
590+
for (const side of ['left', 'right'] as const) {
591+
const ref = getPaneRef(side)
575592
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
576-
if (leftPaneRef.handleVolumeChooserKeyDown?.(e)) {
577-
return true
578-
}
579-
}
580-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
581-
if (rightPaneRef?.isVolumeChooserOpen?.()) {
582-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
583-
if (rightPaneRef.handleVolumeChooserKeyDown?.(e)) {
584-
return true
593+
if (ref?.isVolumeChooserOpen?.()) {
594+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
595+
if (ref.handleVolumeChooserKeyDown?.(e)) {
596+
return true
597+
}
585598
}
586599
}
587600
return false
@@ -609,9 +622,9 @@
609622
switch (e.key) {
610623
case 'F1':
611624
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
612-
rightPaneRef?.closeVolumeChooser()
625+
getPaneRef('right')?.closeVolumeChooser()
613626
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
614-
leftPaneRef?.toggleVolumeChooser()
627+
getPaneRef('left')?.toggleVolumeChooser()
615628
return true
616629
case 'F2':
617630
startRename()
@@ -759,7 +772,7 @@
759772
760773
/** Updates drop-target highlights and overlay as the cursor moves during a drag. */
761774
function handleDragOver(position: { x: number; y: number }) {
762-
const resolved = resolveDropTarget(position.x, position.y, leftPaneWrapperEl, rightPaneWrapperEl)
775+
const resolved = resolveDropTarget(position.x, position.y, paneWrapperEls.left, paneWrapperEls.right)
763776
764777
if (resolved?.type === 'folder') {
765778
dropTargetPane = null
@@ -786,7 +799,7 @@
786799
787800
/** Handles the drop event: resolves the target and opens the transfer dialog. */
788801
function handleDrop(paths: string[], position: { x: number; y: number }) {
789-
const resolved = resolveDropTarget(position.x, position.y, leftPaneWrapperEl, rightPaneWrapperEl)
802+
const resolved = resolveDropTarget(position.x, position.y, paneWrapperEls.left, paneWrapperEls.right)
790803
const folderPath = dropTargetFolderPath
791804
792805
// Read the modifier BEFORE stopping the tracker (which resets altKeyHeld)
@@ -836,7 +849,7 @@
836849
shouldRefresh: boolean,
837850
throttleUntil: number,
838851
setThrottle: (v: number) => void,
839-
paneRef: typeof leftPaneRef,
852+
paneRef: FilePane | undefined,
840853
) {
841854
if (!shouldRefresh) return
842855
const now = Date.now()
@@ -851,8 +864,8 @@
851864
const refreshLeft = hasDescendantUpdate(paths, ensureTrailingSlash(leftPath))
852865
const refreshRight = hasDescendantUpdate(paths, ensureTrailingSlash(rightPath))
853866
854-
throttledRefresh(refreshLeft, leftThrottleUntil, (v) => (leftThrottleUntil = v), leftPaneRef)
855-
throttledRefresh(refreshRight, rightThrottleUntil, (v) => (rightThrottleUntil = v), rightPaneRef)
867+
throttledRefresh(refreshLeft, leftThrottleUntil, (v) => (leftThrottleUntil = v), getPaneRef('left'))
868+
throttledRefresh(refreshRight, rightThrottleUntil, (v) => (rightThrottleUntil = v), getPaneRef('right'))
856869
}
857870
858871
function handleResizeForDevTools() {
@@ -1208,16 +1221,18 @@
12081221
12091222
/** Cancels any active inline rename on either pane. */
12101223
export function cancelRename() {
1211-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
1212-
leftPaneRef?.cancelRename?.()
1213-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
1214-
rightPaneRef?.cancelRename?.()
1224+
for (const side of ['left', 'right'] as const) {
1225+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
1226+
getPaneRef(side)?.cancelRename?.()
1227+
}
12151228
}
12161229
12171230
/** Returns whether inline rename is active on either pane. */
12181231
export function isRenaming(): boolean {
1219-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
1220-
return (leftPaneRef?.isRenaming?.() as boolean) || (rightPaneRef?.isRenaming?.() as boolean) || false
1232+
return (['left', 'right'] as const).some((side) => {
1233+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
1234+
return getPaneRef(side)?.isRenaming?.() as boolean
1235+
})
12211236
}
12221237
12231238
/** Opens the new folder dialog. Pre-fills with the entry name under cursor. */
@@ -1496,10 +1511,10 @@
14961511
* Close volume chooser on all panes.
14971512
*/
14981513
export function closeVolumeChooser() {
1499-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
1500-
leftPaneRef?.closeVolumeChooser()
1501-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
1502-
rightPaneRef?.closeVolumeChooser()
1514+
for (const side of ['left', 'right'] as const) {
1515+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
1516+
getPaneRef(side)?.closeVolumeChooser()
1517+
}
15031518
}
15041519
15051520
/**
@@ -1897,6 +1912,74 @@
18971912
}
18981913
</script>
18991914

1915+
{#snippet paneBlock(paneId: 'left' | 'right')}
1916+
{@const tabMgr = getTabMgr(paneId)}
1917+
<div
1918+
class="pane-wrapper"
1919+
class:drop-target-active={dropTargetPane === paneId}
1920+
style="width: {getPaneWidth(paneId)}%"
1921+
bind:this={paneWrapperEls[paneId]}
1922+
>
1923+
<TabBar
1924+
tabs={getAllTabs(tabMgr)}
1925+
activeTabId={tabMgr.activeTabId}
1926+
{paneId}
1927+
maxTabs={MAX_TABS_PER_PANE}
1928+
onTabSwitch={(tabId: TabId) => {
1929+
switchToTab(paneId, tabId)
1930+
}}
1931+
onTabClose={(tabId: TabId) => {
1932+
handleTabClose(paneId, tabId)
1933+
}}
1934+
onTabMiddleClick={(tabId: TabId) => {
1935+
handleTabMiddleClick(paneId, tabId)
1936+
}}
1937+
onNewTab={() => {
1938+
handleNewTab(paneId)
1939+
}}
1940+
onContextMenu={(tabId: TabId, event: MouseEvent) => {
1941+
handleTabContextMenu(paneId, tabId, event)
1942+
}}
1943+
onPaneFocus={() => {
1944+
handleFocus(paneId)
1945+
}}
1946+
/>
1947+
<!--suppress JSUnresolvedReference -->
1948+
{#key getActiveTab(tabMgr).id}
1949+
<FilePane
1950+
bind:this={paneRefs[paneId]}
1951+
{paneId}
1952+
initialPath={getPanePath(paneId)}
1953+
volumeId={getPaneVolumeId(paneId)}
1954+
volumePath={getPaneVolumePath(paneId)}
1955+
volumeName={getPaneVolumeName(paneId)}
1956+
isFocused={focusedPane === paneId}
1957+
{showHiddenFiles}
1958+
viewMode={getPaneViewMode(paneId)}
1959+
sortBy={getPaneSort(paneId).sortBy}
1960+
sortOrder={getPaneSort(paneId).sortOrder}
1961+
directorySortMode={getDirectorySortMode()}
1962+
onPathChange={(path: string) => {
1963+
handlePathChange(paneId, path)
1964+
}}
1965+
onVolumeChange={(volumeId: string, volumePath: string, targetPath: string) =>
1966+
handleVolumeChange(paneId, volumeId, volumePath, targetPath)}
1967+
onRequestFocus={() => {
1968+
handleFocus(paneId)
1969+
}}
1970+
onSortChange={(column: SortColumn) => handleSortChange(paneId, column)}
1971+
onNetworkHostChange={(host: NetworkHost | null) => {
1972+
handleNetworkHostChange(paneId, host)
1973+
}}
1974+
onCancelLoading={() => {
1975+
handleCancelLoading(paneId)
1976+
}}
1977+
onMtpFatalError={(msg: string) => handleMtpFatalError(paneId, msg)}
1978+
/>
1979+
{/key}
1980+
</div>
1981+
{/snippet}
1982+
19001983
<!-- svelte-ignore a11y_no_noninteractive_tabindex,a11y_no_noninteractive_element_interactions -->
19011984
<div
19021985
class="dual-pane-explorer"
@@ -1908,135 +1991,11 @@
19081991
aria-label="File explorer"
19091992
>
19101993
{#if initialized}
1911-
<div
1912-
class="pane-wrapper"
1913-
class:drop-target-active={dropTargetPane === 'left'}
1914-
style="width: {leftPaneWidthPercent}%"
1915-
bind:this={leftPaneWrapperEl}
1916-
>
1917-
<TabBar
1918-
tabs={getAllTabs(leftTabMgr)}
1919-
activeTabId={leftTabMgr.activeTabId}
1920-
paneId="left"
1921-
maxTabs={MAX_TABS_PER_PANE}
1922-
onTabSwitch={(tabId: TabId) => {
1923-
switchToTab('left', tabId)
1924-
}}
1925-
onTabClose={(tabId: TabId) => {
1926-
handleTabClose('left', tabId)
1927-
}}
1928-
onTabMiddleClick={(tabId: TabId) => {
1929-
handleTabMiddleClick('left', tabId)
1930-
}}
1931-
onNewTab={() => {
1932-
handleNewTab('left')
1933-
}}
1934-
onContextMenu={(tabId: TabId, event: MouseEvent) => {
1935-
handleTabContextMenu('left', tabId, event)
1936-
}}
1937-
onPaneFocus={() => {
1938-
handleFocus('left')
1939-
}}
1940-
/>
1941-
<!--suppress JSUnresolvedReference -->
1942-
{#key getActiveTab(leftTabMgr).id}
1943-
<FilePane
1944-
bind:this={leftPaneRef}
1945-
paneId="left"
1946-
initialPath={leftPath}
1947-
volumeId={leftVolumeId}
1948-
volumePath={leftVolumePath}
1949-
volumeName={leftVolumeName}
1950-
isFocused={focusedPane === 'left'}
1951-
{showHiddenFiles}
1952-
viewMode={leftViewMode}
1953-
sortBy={leftSortBy}
1954-
sortOrder={leftSortOrder}
1955-
directorySortMode={getDirectorySortMode()}
1956-
onPathChange={(path: string) => {
1957-
handlePathChange('left', path)
1958-
}}
1959-
onVolumeChange={(volumeId: string, volumePath: string, targetPath: string) =>
1960-
handleVolumeChange('left', volumeId, volumePath, targetPath)}
1961-
onRequestFocus={() => {
1962-
handleFocus('left')
1963-
}}
1964-
onSortChange={(column: SortColumn) => handleSortChange('left', column)}
1965-
onNetworkHostChange={(host: NetworkHost | null) => {
1966-
handleNetworkHostChange('left', host)
1967-
}}
1968-
onCancelLoading={() => {
1969-
handleCancelLoading('left')
1970-
}}
1971-
onMtpFatalError={(msg: string) => handleMtpFatalError('left', msg)}
1972-
/>
1973-
{/key}
1974-
</div>
1994+
<!-- eslint-disable-next-line @typescript-eslint/no-confusing-void-expression -- Svelte {@render} syntax -->
1995+
{@render paneBlock('left')}
19751996
<PaneResizer onResize={handlePaneResize} onResizeEnd={handlePaneResizeEnd} onReset={handlePaneResizeReset} />
1976-
<div
1977-
class="pane-wrapper"
1978-
class:drop-target-active={dropTargetPane === 'right'}
1979-
style="width: {100 - leftPaneWidthPercent}%"
1980-
bind:this={rightPaneWrapperEl}
1981-
>
1982-
<TabBar
1983-
tabs={getAllTabs(rightTabMgr)}
1984-
activeTabId={rightTabMgr.activeTabId}
1985-
paneId="right"
1986-
maxTabs={MAX_TABS_PER_PANE}
1987-
onTabSwitch={(tabId: TabId) => {
1988-
switchToTab('right', tabId)
1989-
}}
1990-
onTabClose={(tabId: TabId) => {
1991-
handleTabClose('right', tabId)
1992-
}}
1993-
onTabMiddleClick={(tabId: TabId) => {
1994-
handleTabMiddleClick('right', tabId)
1995-
}}
1996-
onNewTab={() => {
1997-
handleNewTab('right')
1998-
}}
1999-
onContextMenu={(tabId: TabId, event: MouseEvent) => {
2000-
handleTabContextMenu('right', tabId, event)
2001-
}}
2002-
onPaneFocus={() => {
2003-
handleFocus('right')
2004-
}}
2005-
/>
2006-
<!--suppress JSUnresolvedReference -->
2007-
{#key getActiveTab(rightTabMgr).id}
2008-
<FilePane
2009-
bind:this={rightPaneRef}
2010-
paneId="right"
2011-
initialPath={rightPath}
2012-
volumeId={rightVolumeId}
2013-
volumePath={rightVolumePath}
2014-
volumeName={rightVolumeName}
2015-
isFocused={focusedPane === 'right'}
2016-
{showHiddenFiles}
2017-
viewMode={rightViewMode}
2018-
sortBy={rightSortBy}
2019-
sortOrder={rightSortOrder}
2020-
directorySortMode={getDirectorySortMode()}
2021-
onPathChange={(path: string) => {
2022-
handlePathChange('right', path)
2023-
}}
2024-
onVolumeChange={(volumeId: string, volumePath: string, targetPath: string) =>
2025-
handleVolumeChange('right', volumeId, volumePath, targetPath)}
2026-
onRequestFocus={() => {
2027-
handleFocus('right')
2028-
}}
2029-
onSortChange={(column: SortColumn) => handleSortChange('right', column)}
2030-
onNetworkHostChange={(host: NetworkHost | null) => {
2031-
handleNetworkHostChange('right', host)
2032-
}}
2033-
onCancelLoading={() => {
2034-
handleCancelLoading('right')
2035-
}}
2036-
onMtpFatalError={(msg: string) => handleMtpFatalError('right', msg)}
2037-
/>
2038-
{/key}
2039-
</div>
1997+
<!-- eslint-disable-next-line @typescript-eslint/no-confusing-void-expression -- Svelte {@render} syntax -->
1998+
{@render paneBlock('right')}
20401999
{:else}
20412000
<LoadingIcon />
20422001
{/if}

0 commit comments

Comments
 (0)