Skip to content

Commit ec7bebb

Browse files
committed
Global selecty buttons
1 parent bc1541a commit ec7bebb

File tree

5 files changed

+154
-48
lines changed

5 files changed

+154
-48
lines changed

apps/desktop/src/components/v3/BranchCard.svelte

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import BranchHeaderContextMenu from '$components/v3/BranchHeaderContextMenu.svelte';
88
import PrNumberUpdater from '$components/v3/PrNumberUpdater.svelte';
99
import WorktreeChangesFileList from '$components/v3/WorktreeChangesFileList.svelte';
10+
import WorktreeChangesSelectAll from '$components/v3/WorktreeChangesSelectAll.svelte';
1011
import { MoveCommitDzHandler, StartCommitDzHandler } from '$lib/commits/dropHandler';
1112
import { assignedChangesFocusableId } from '$lib/focus/focusManager.svelte';
1213
import { focusable } from '$lib/focus/focusable.svelte';
@@ -91,6 +92,10 @@
9192
const selected = $derived(selection?.branchName === branchName);
9293
const isPushed = $derived(!!(args.type === 'draft-branch' ? undefined : args.trackingBranch));
9394
95+
const projectState = $derived(uiState.project(projectId));
96+
const drawerPage = $derived(projectState.drawerPage.get());
97+
const isCommitting = $derived(drawerPage.current === 'new-commit');
98+
9499
const changesKeyResult = $derived(worktreeService.getChangesKey(projectId));
95100
const hunkAssignments = $derived(
96101
changesKeyResult.current
@@ -264,7 +269,15 @@
264269
class="assigned-changes"
265270
use:focusable={{ id: assignedChangesFocusableId(args.stackId) }}
266271
>
267-
<p class="text-14 text-bold assigned-changes__title">Assigned changes:</p>
272+
<div class="assigned-changes__title">
273+
{#if isCommitting}
274+
<WorktreeChangesSelectAll
275+
{projectId}
276+
group={{ type: 'grouped', stackId: args.stackId }}
277+
/>
278+
{/if}
279+
<p class="text-14 text-bold">Assigned changes:</p>
280+
</div>
268281
<WorktreeChangesFileList
269282
{projectId}
270283
listMode="list"
@@ -289,7 +302,10 @@
289302
}
290303
291304
.assigned-changes__title {
305+
display: flex;
292306
margin: 8px;
307+
margin-left: 14px;
308+
gap: 8px;
293309
}
294310
295311
.branch-card {

apps/desktop/src/components/v3/WorktreeChanges.svelte

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import ReduxResult from '$components/ReduxResult.svelte';
66
import FileListMode from '$components/v3/FileListMode.svelte';
77
import WorktreeChangesFileList from '$components/v3/WorktreeChangesFileList.svelte';
8+
import WorktreeChangesSelectAll from '$components/v3/WorktreeChangesSelectAll.svelte';
89
import WorktreeTipsFooter from '$components/v3/WorktreeTipsFooter.svelte';
910
import noChanges from '$lib/assets/illustrations/no-changes.svg?raw';
1011
import { createCommitStore } from '$lib/commits/contexts';
@@ -23,7 +24,6 @@
2324
import { getContext, inject } from '@gitbutler/shared/context';
2425
import Badge from '@gitbutler/ui/Badge.svelte';
2526
import Button from '@gitbutler/ui/Button.svelte';
26-
import Checkbox from '@gitbutler/ui/Checkbox.svelte';
2727
import { stickyHeader } from '@gitbutler/ui/utils/stickyHeader';
2828
import { isDefined } from '@gitbutler/ui/utils/typeguards';
2929
import { untrack } from 'svelte';
@@ -61,12 +61,6 @@
6161
const changesResult = $derived(worktreeService.getChanges(projectId));
6262
const affectedPaths = $derived(changesResult.current.data?.map((c) => c.path));
6363
64-
const filesFullySelected = $derived(
65-
changeSelection.every(affectedPaths ?? [], (f) => f.type === 'full')
66-
);
67-
68-
const filesPartiallySelected = $derived(!noChangesSelected && !filesFullySelected);
69-
7064
// TODO: Make this go away.
7165
createCommitStore(undefined);
7266
@@ -111,14 +105,6 @@
111105
}
112106
}
113107
114-
function toggleGlobalCheckbox() {
115-
if (noChangesSelected) {
116-
selectEverything();
117-
return;
118-
}
119-
changeSelection.clear();
120-
}
121-
122108
let listHeaderHeight = $state(0);
123109
let listFooterHeight = $state(0);
124110
@@ -172,12 +158,7 @@
172158
>
173159
<div class="worktree-header__general">
174160
{#if isCommitting}
175-
<Checkbox
176-
checked={filesPartiallySelected || filesFullySelected}
177-
indeterminate={filesPartiallySelected}
178-
small
179-
onchange={toggleGlobalCheckbox}
180-
/>
161+
<WorktreeChangesSelectAll {projectId} group={{ type: 'ungrouped' }} />
181162
{/if}
182163
<div class="worktree-header__title truncate">
183164
<h3 class="text-14 text-semibold truncate">Uncommitted</h3>

apps/desktop/src/components/v3/WorktreeChangesFileList.svelte

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
<script lang="ts">
22
import ReduxResult from '$components/ReduxResult.svelte';
33
import FileList from '$components/v3/FileList.svelte';
4-
import {
5-
DiffService,
6-
hunkGroupToKey,
7-
type HunkAssignments,
8-
type HunkGroup
9-
} from '$lib/hunks/diffService.svelte';
4+
import { DiffService, type HunkGroup } from '$lib/hunks/diffService.svelte';
5+
import { filterChangesByGroup } from '$lib/selection/changeSelection.svelte';
106
import { UiState } from '$lib/state/uiState.svelte';
117
import { WorktreeService } from '$lib/worktree/worktreeService.svelte';
128
import { getContext } from '@gitbutler/shared/context';
13-
import type { TreeChange } from '$lib/hunks/change';
149
1510
// TODO: Look into whether it's important to pass the stackId through
1611
type Props = {
@@ -27,31 +22,14 @@
2722
const uiState = getContext(UiState);
2823
const projectState = $derived(uiState.project(projectId));
2924
const drawerPage = $derived(projectState.drawerPage.get());
25+
const isCommitting = $derived(drawerPage.current === 'new-commit');
3026
const changesResult = $derived(worktreeService.getChanges(projectId));
3127
const changesKeyResult = $derived(worktreeService.getChangesKey(projectId));
3228
const assignments = $derived(
3329
changesKeyResult.current
3430
? diffService.hunkAssignments(projectId, changesKeyResult.current)
3531
: undefined
3632
);
37-
38-
const isCommitting = $derived(drawerPage.current === 'new-commit');
39-
40-
function filter(changes: TreeChange[], assignments: HunkAssignments) {
41-
const stackGroup = assignments.get(hunkGroupToKey(group));
42-
43-
if (!stackGroup) return [];
44-
45-
const filteredChanges = [];
46-
for (const change of changes) {
47-
const pathGroup = stackGroup.get(change.path);
48-
if (pathGroup) {
49-
filteredChanges.push(change);
50-
}
51-
}
52-
53-
return filteredChanges;
54-
}
5533
</script>
5634

5735
<ReduxResult {projectId} result={changesResult.current}>
@@ -63,7 +41,7 @@
6341
selectionId={{ type: 'worktree', group }}
6442
showCheckboxes={isCommitting}
6543
{projectId}
66-
changes={filter(changes, assignments)}
44+
changes={filterChangesByGroup(changes, group, assignments)}
6745
{listMode}
6846
{active}
6947
{group}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<script lang="ts">
2+
import { DiffService, type HunkGroup } from '$lib/hunks/diffService.svelte';
3+
import {
4+
allAssignedToCurrentGroupSelected,
5+
ChangeSelectionService,
6+
deselectAllForChangeInGroup,
7+
filterChangesByGroup,
8+
selectAllForChangeInGroup,
9+
someAssignedToCurrentGroupSelected
10+
} from '$lib/selection/changeSelection.svelte';
11+
import { WorktreeService } from '$lib/worktree/worktreeService.svelte';
12+
import { getContext } from '@gitbutler/shared/context';
13+
import Checkbox from '@gitbutler/ui/Checkbox.svelte';
14+
15+
type Props = {
16+
projectId: string;
17+
group: HunkGroup;
18+
};
19+
20+
const { projectId, group }: Props = $props();
21+
22+
const worktreeService = getContext(WorktreeService);
23+
const diffService = getContext(DiffService);
24+
const changeSelection = getContext(ChangeSelectionService);
25+
const changesResult = $derived(worktreeService.getChanges(projectId));
26+
const changesKeyResult = $derived(worktreeService.getChangesKey(projectId));
27+
const assignmentsResult = $derived(
28+
changesKeyResult.current
29+
? diffService.hunkAssignments(projectId, changesKeyResult.current)
30+
: undefined
31+
);
32+
const filteredChanges = $derived(
33+
assignmentsResult?.current.data && changesResult?.current?.data
34+
? filterChangesByGroup(changesResult.current.data, group, assignmentsResult.current.data)
35+
: []
36+
);
37+
const selectedFiles = $derived(
38+
Object.fromEntries(
39+
filteredChanges.map((change) => [change.path, changeSelection.getById(change.path)])
40+
)
41+
);
42+
43+
const checkStatus = $derived.by((): 'checked' | 'indeterminate' | 'unchecked' => {
44+
if (!assignmentsResult?.current.data) return 'unchecked';
45+
if (!changesResult.current.data) return 'unchecked';
46+
47+
const assignments = assignmentsResult.current.data;
48+
49+
if (
50+
filteredChanges.every((change) =>
51+
allAssignedToCurrentGroupSelected(
52+
change,
53+
group,
54+
assignments,
55+
selectedFiles[change.path]?.current
56+
)
57+
)
58+
) {
59+
return 'checked';
60+
}
61+
62+
if (
63+
filteredChanges.some((change) =>
64+
someAssignedToCurrentGroupSelected(
65+
change,
66+
group,
67+
assignments,
68+
selectedFiles[change.path]?.current
69+
)
70+
)
71+
) {
72+
return 'indeterminate';
73+
}
74+
75+
return 'unchecked';
76+
});
77+
78+
function onCheck() {
79+
if (!assignmentsResult?.current.data) return;
80+
if (!changesResult.current.data) return;
81+
82+
if (checkStatus === 'checked' || checkStatus === 'indeterminate') {
83+
for (const change of filteredChanges) {
84+
deselectAllForChangeInGroup(
85+
change,
86+
group,
87+
assignmentsResult.current.data,
88+
selectedFiles[change.path]?.current,
89+
changeSelection
90+
);
91+
}
92+
} else {
93+
for (const change of filteredChanges) {
94+
selectAllForChangeInGroup(
95+
change,
96+
group,
97+
assignmentsResult.current.data,
98+
selectedFiles[change.path]?.current,
99+
changeSelection
100+
);
101+
}
102+
}
103+
}
104+
</script>
105+
106+
<Checkbox
107+
small
108+
checked={checkStatus === 'checked' || checkStatus === 'indeterminate'}
109+
indeterminate={checkStatus === 'indeterminate'}
110+
onchange={onCheck}
111+
/>

apps/desktop/src/lib/selection/changeSelection.svelte.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,3 +407,23 @@ function getAllAssignments(
407407

408408
return headers;
409409
}
410+
411+
export function filterChangesByGroup(
412+
changes: TreeChange[],
413+
group: HunkGroup,
414+
assignments: HunkAssignments
415+
) {
416+
const stackGroup = assignments.get(hunkGroupToKey(group));
417+
418+
if (!stackGroup) return [];
419+
420+
const filteredChanges = [];
421+
for (const change of changes) {
422+
const pathGroup = stackGroup.get(change.path);
423+
if (pathGroup) {
424+
filteredChanges.push(change);
425+
}
426+
}
427+
428+
return filteredChanges;
429+
}

0 commit comments

Comments
 (0)