Skip to content

Commit 7edcac8

Browse files
committed
Fix: cursor went out of Full view sometimes.
1 parent 29eb6fe commit 7edcac8

1 file changed

Lines changed: 62 additions & 55 deletions

File tree

apps/desktop/src/lib/file-explorer/FullList.svelte

Lines changed: 62 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -215,17 +215,8 @@
215215
}
216216
</script>
217217

218-
<div
219-
class="full-list"
220-
class:is-focused={isFocused}
221-
bind:this={scrollContainer}
222-
bind:clientHeight={containerHeight}
223-
onscroll={handleScroll}
224-
tabindex="-1"
225-
role="listbox"
226-
aria-activedescendant={cursorIndex >= 0 ? `file-${String(cursorIndex)}` : undefined}
227-
>
228-
<!-- Header row with sortable columns -->
218+
<div class="full-list-container" class:is-focused={isFocused}>
219+
<!-- Header row with sortable columns (outside scroll container for correct height calculation) -->
229220
<div class="header-row">
230221
<span class="header-icon"></span>
231222
<SortableHeader
@@ -251,51 +242,69 @@
251242
onClick={onSortChange ?? (() => {})}
252243
/>
253244
</div>
254-
<!-- Spacer div provides accurate scrollbar for full list size -->
255-
<div class="virtual-spacer" style="height: {virtualWindow.totalSize}px;">
256-
<!-- Visible window positioned with translateY -->
257-
<div class="virtual-window" style="transform: translateY({virtualWindow.offset}px);">
258-
{#each visibleFiles as { file, globalIndex } (file.path)}
259-
{@const syncIcon = getSyncIconPath(syncStatusMap[file.path])}
260-
<!-- svelte-ignore a11y_interactive_supports_focus -->
261-
<div
262-
id={`file-${String(globalIndex)}`}
263-
class="file-entry"
264-
class:is-under-cursor={globalIndex === cursorIndex}
265-
class:is-selected={selectedIndices.has(globalIndex)}
266-
onmousedown={(e: MouseEvent) => {
267-
handleMouseDown(e, globalIndex)
268-
}}
269-
ondblclick={() => {
270-
handleDoubleClick(globalIndex)
271-
}}
272-
oncontextmenu={(e: MouseEvent) => {
273-
e.preventDefault()
274-
onSelect(globalIndex)
275-
onContextMenu?.(file)
276-
}}
277-
role="option"
278-
aria-selected={globalIndex === cursorIndex}
279-
>
280-
<FileIcon {file} {syncIcon} />
281-
<span class="col-name">{file.name}</span>
282-
<span class="col-size" title={file.size !== undefined ? formatHumanReadable(file.size) : ''}>
283-
{#if file.isDirectory}
284-
<span class="size-dir">&lt;dir&gt;</span>
285-
{:else if file.size !== undefined}
286-
{#each formatSizeTriads(file.size) as triad, i (i)}
287-
<span class={triad.tierClass}>{triad.value}</span>
288-
{/each}
289-
{/if}
290-
</span>
291-
<span class="col-date">{formatDateShort(file.modifiedAt)}</span>
292-
</div>
293-
{/each}
245+
<!-- Scrollable file list -->
246+
<div
247+
class="full-list"
248+
bind:this={scrollContainer}
249+
bind:clientHeight={containerHeight}
250+
onscroll={handleScroll}
251+
tabindex="-1"
252+
role="listbox"
253+
aria-activedescendant={cursorIndex >= 0 ? `file-${String(cursorIndex)}` : undefined}
254+
>
255+
<!-- Spacer div provides accurate scrollbar for full list size -->
256+
<div class="virtual-spacer" style="height: {virtualWindow.totalSize}px;">
257+
<!-- Visible window positioned with translateY -->
258+
<div class="virtual-window" style="transform: translateY({virtualWindow.offset}px);">
259+
{#each visibleFiles as { file, globalIndex } (file.path)}
260+
{@const syncIcon = getSyncIconPath(syncStatusMap[file.path])}
261+
<!-- svelte-ignore a11y_interactive_supports_focus -->
262+
<div
263+
id={`file-${String(globalIndex)}`}
264+
class="file-entry"
265+
class:is-under-cursor={globalIndex === cursorIndex}
266+
class:is-selected={selectedIndices.has(globalIndex)}
267+
onmousedown={(e: MouseEvent) => {
268+
handleMouseDown(e, globalIndex)
269+
}}
270+
ondblclick={() => {
271+
handleDoubleClick(globalIndex)
272+
}}
273+
oncontextmenu={(e: MouseEvent) => {
274+
e.preventDefault()
275+
onSelect(globalIndex)
276+
onContextMenu?.(file)
277+
}}
278+
role="option"
279+
aria-selected={globalIndex === cursorIndex}
280+
>
281+
<FileIcon {file} {syncIcon} />
282+
<span class="col-name">{file.name}</span>
283+
<span class="col-size" title={file.size !== undefined ? formatHumanReadable(file.size) : ''}>
284+
{#if file.isDirectory}
285+
<span class="size-dir">&lt;dir&gt;</span>
286+
{:else if file.size !== undefined}
287+
{#each formatSizeTriads(file.size) as triad, i (i)}
288+
<span class={triad.tierClass}>{triad.value}</span>
289+
{/each}
290+
{/if}
291+
</span>
292+
<span class="col-date">{formatDateShort(file.modifiedAt)}</span>
293+
</div>
294+
{/each}
295+
</div>
294296
</div>
295297
</div>
296298
</div>
297299

298300
<style>
301+
.full-list-container {
302+
display: flex;
303+
flex-direction: column;
304+
height: 100%;
305+
width: 100%;
306+
}
307+
299308
.full-list {
300309
overflow-y: auto;
301310
overflow-x: hidden;
@@ -304,8 +313,6 @@
304313
line-height: 1;
305314
flex: 1;
306315
outline: none;
307-
display: flex;
308-
flex-direction: column;
309316
}
310317
311318
.header-row {
@@ -344,7 +351,7 @@
344351
background-color: rgba(204, 228, 247, 0.1);
345352
}
346353
347-
.full-list.is-focused .file-entry.is-under-cursor {
354+
.full-list-container.is-focused .file-entry.is-under-cursor {
348355
background-color: var(--color-cursor-focused-bg);
349356
}
350357
@@ -398,7 +405,7 @@
398405
}
399406
400407
/* Selection color is preserved even under cursor */
401-
.full-list.is-focused .file-entry.is-under-cursor.is-selected .col-name {
408+
.full-list-container.is-focused .file-entry.is-under-cursor.is-selected .col-name {
402409
color: var(--color-selection-fg);
403410
}
404411

0 commit comments

Comments
 (0)