Skip to content

Commit 30a369d

Browse files
authored
Close preview without clearing selection. Toggle preview on click. (#10425)
* feat(selection): add clearPreview to clear only the preview pane * don't clear selection for commits and branches * feat(selection): add toggle behavior to list selection and pass click flag * feat(ui): clear source lane when dropping commits to prevent auto-opening * feat(ui): add allowUnselect prop to file lists to control deselection behavior * fix(stack): reset previous id on context change for auto-sele
1 parent 8c2ff25 commit 30a369d

14 files changed

+103
-15
lines changed

apps/desktop/src/components/BranchCard.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@
154154
>
155155
{#if args.type === 'stack-branch'}
156156
{@const moveHandler = args.stackId
157-
? new MoveCommitDzHandler(stackService, args.stackId, projectId)
157+
? new MoveCommitDzHandler(stackService, args.stackId, projectId, uiState)
158158
: undefined}
159159
{#if !args.prNumber && args.stackId}
160160
<PrNumberUpdater {projectId} stackId={args.stackId} {branchName} />

apps/desktop/src/components/BranchCommitList.svelte

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,11 @@
106106
let integrationModal = $state<Modal>();
107107
108108
async function handleCommitClick(commitId: string, upstream: boolean) {
109-
if (selectedCommitId !== commitId) {
109+
const currentSelection = laneState.selection.current;
110+
// Toggle: if this exact commit is already selected, clear the selection
111+
if (currentSelection?.commitId === commitId && currentSelection?.branchName === branchName) {
112+
laneState.selection.set(undefined);
113+
} else {
110114
laneState.selection.set({ branchName, commitId, upstream });
111115
}
112116
projectState.stackId.set(stackId);

apps/desktop/src/components/BranchList.svelte

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,13 @@
186186
trackingBranch={branch.remoteTrackingBranch ?? undefined}
187187
readonly={!!branch.remoteTrackingBranch}
188188
onclick={() => {
189-
uiState.lane(laneId).selection.set({ branchName });
189+
const currentSelection = uiState.lane(laneId).selection.current;
190+
// Toggle: if this branch is already selected, clear the selection
191+
if (currentSelection?.branchName === branchName && !currentSelection?.commitId) {
192+
uiState.lane(laneId).selection.set(undefined);
193+
} else {
194+
uiState.lane(laneId).selection.set({ branchName });
195+
}
190196
onselect?.();
191197
}}
192198
>

apps/desktop/src/components/ChangedFiles.svelte

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
autoselect?: boolean;
3030
ancestorMostConflictedCommitId?: string;
3131
ontoggle?: (collapsed: boolean) => void;
32+
allowUnselect?: boolean;
3233
};
3334
3435
const {
@@ -46,7 +47,8 @@
4647
resizer,
4748
autoselect,
4849
ancestorMostConflictedCommitId,
49-
ontoggle
50+
ontoggle,
51+
allowUnselect = true
5052
}: Props = $props();
5153
5254
const idSelection = inject(FILE_SELECTION_MANAGER);
@@ -91,6 +93,7 @@
9193
{conflictEntries}
9294
{draggableFiles}
9395
{ancestorMostConflictedCommitId}
96+
{allowUnselect}
9497
hideLastFileBorder={false}
9598
/>
9699
{:else}

apps/desktop/src/components/FileList.svelte

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
ancestorMostConflictedCommitId?: string;
3838
hideLastFileBorder?: boolean;
3939
onselect?: (change: TreeChange) => void;
40+
allowUnselect?: boolean;
4041
};
4142
4243
const {
@@ -50,7 +51,8 @@
5051
draggableFiles,
5152
ancestorMostConflictedCommitId,
5253
hideLastFileBorder = true,
53-
onselect
54+
onselect,
55+
allowUnselect = true
5456
}: Props = $props();
5557
5658
const focusManager = inject(FOCUS_MANAGER);
@@ -181,7 +183,17 @@
181183
function handleKeyDown(change: TreeChange, idx: number, e: KeyboardEvent) {
182184
if (e.key === 'Enter' || e.key === ' ' || e.key === 'l') {
183185
e.stopPropagation();
184-
selectFilesInList(e, change, changes, idSelection, selectedFileIds, true, idx, selectionId);
186+
selectFilesInList(
187+
e,
188+
change,
189+
changes,
190+
idSelection,
191+
selectedFileIds,
192+
true,
193+
idx,
194+
selectionId,
195+
allowUnselect
196+
);
185197
onselect?.(change);
186198
return true;
187199
}
@@ -250,7 +262,17 @@
250262
}}
251263
onclick={(e) => {
252264
e.stopPropagation();
253-
selectFilesInList(e, change, changes, idSelection, selectedFileIds, true, idx, selectionId);
265+
selectFilesInList(
266+
e,
267+
change,
268+
changes,
269+
idSelection,
270+
selectedFileIds,
271+
true,
272+
idx,
273+
selectionId,
274+
allowUnselect
275+
);
254276
onselect?.(change);
255277
}}
256278
{conflictEntries}

apps/desktop/src/components/SnapshotCard.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@
225225
listMode="list"
226226
hideLastFileBorder={false}
227227
onselect={(change) => onDiffClick(change.path)}
228+
allowUnselect={false}
228229
/>
229230
</ScrollableContainer>
230231
</SnapshotAttachment>

apps/desktop/src/components/StackView.svelte

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959
6060
let lanesSrollableEl = $state<HTMLDivElement>();
6161
62+
let isDetailsViewForcesClosed = $state(false);
63+
6264
const stackService = inject(STACK_SERVICE);
6365
const diffService = inject(DIFF_SERVICE);
6466
const uncommittedService = inject(UNCOMMITTED_SERVICE);
@@ -229,6 +231,11 @@
229231
selection.set(undefined);
230232
}
231233
234+
function onclosePreviewOnly() {
235+
// Close the details view but keep the selection intact
236+
isDetailsViewForcesClosed = true;
237+
}
238+
232239
const startCommitVisible = $derived(uncommittedService.startCommitVisible(stackId));
233240
234241
function onerror(err: unknown) {
@@ -250,7 +257,33 @@
250257
return undefined;
251258
}
252259
253-
let isDetailsViewOpen = $derived(!!(branchName || commitId || assignedKey || selectedFile));
260+
let isDetailsViewOpen = $derived(
261+
!!(branchName || commitId || assignedKey || selectedFile) && !isDetailsViewForcesClosed
262+
);
263+
264+
// Track the current selection to detect when it changes
265+
let previousSelection = $state<{ branchName?: string; commitId?: string }>({});
266+
267+
// Reset the forced closed state when selection changes to a different branch/commit
268+
$effect(() => {
269+
const currentSelection = { branchName, commitId };
270+
271+
// Check if selection actually changed
272+
if (
273+
currentSelection.branchName !== previousSelection.branchName ||
274+
currentSelection.commitId !== previousSelection.commitId
275+
) {
276+
// Selection changed, allow details view to open again
277+
isDetailsViewForcesClosed = false;
278+
previousSelection = { ...currentSelection };
279+
280+
// Clear file selections from the previous context to allow auto-selection
281+
// to work properly for the new context
282+
if (activeSelectionId) {
283+
idSelection.clear(activeSelectionId);
284+
}
285+
}
286+
});
254287
const DETAILS_RIGHT_PADDING_REM = 1.125;
255288
256289
// Function to update CSS custom property for details view width
@@ -299,7 +332,7 @@
299332
scrollContainer={selectionPreviewScrollContainer}
300333
selectionId={createWorktreeSelection({ stackId })}
301334
onclose={() => {
302-
idSelection.clear(createWorktreeSelection({ stackId: stackId }));
335+
idSelection.clearPreview(createWorktreeSelection({ stackId: stackId }));
303336
}}
304337
draggableFiles
305338
/>
@@ -317,7 +350,7 @@
317350
{/snippet}
318351

319352
{#snippet branchView(branchName: string)}
320-
<BranchView {stackId} {laneId} {projectId} {branchName} {onerror} {onclose} />
353+
<BranchView {stackId} {laneId} {projectId} {branchName} {onerror} onclose={onclosePreviewOnly} />
321354
{/snippet}
322355

323356
{#snippet commitView(branchName: string, commitId: string)}
@@ -333,7 +366,7 @@
333366
}}
334367
draggableFiles
335368
{onerror}
336-
{onclose}
369+
onclose={onclosePreviewOnly}
337370
/>
338371
{/snippet}
339372

@@ -375,6 +408,7 @@
375408
defaultValue: 16
376409
}}
377410
autoselect
411+
allowUnselect={false}
378412
/>
379413
{/snippet}
380414
</ReduxResult>
@@ -408,6 +442,7 @@
408442
maxHeight: 32,
409443
defaultValue: 16
410444
}}
445+
allowUnselect={false}
411446
/>
412447
{/snippet}
413448
</ReduxResult>
@@ -436,7 +471,7 @@
436471
use:focusable={{
437472
onKeydown: (event) => {
438473
if (event.key === 'Escape' && isDetailsViewOpen) {
439-
selection.set(undefined);
474+
onclosePreviewOnly();
440475
event.preventDefault();
441476
event.stopPropagation();
442477
return true;

apps/desktop/src/components/UnappliedBranchView.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@
107107
{selectionId}
108108
changes={changes.changes}
109109
stats={changes.stats}
110+
allowUnselect={false}
110111
/>
111112
{/snippet}
112113
</ReduxResult>

apps/desktop/src/components/UnappliedCommitView.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
{projectId}
4747
selectionId={createCommitSelection({ commitId })}
4848
changes={changes.changes}
49+
allowUnselect={false}
4950
/>
5051
{/snippet}
5152
</ReduxResult>

apps/desktop/src/components/WorkspaceView.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@
107107
draggableFiles
108108
scrollContainer={selectionPreviewScrollContainer}
109109
onclose={() => {
110-
idSelection.clear(selectionId);
110+
idSelection.clearPreview(selectionId);
111111
}}
112112
/>
113113
</ConfigurableScrollableContainer>

0 commit comments

Comments
 (0)