feat(history): make branch filter groups collapsible
Add collapsible local and remote branch groups to the branch visibility dialog, including toggle buttons and selection counts. Introduce comprehensive dialog styling and responsive rules to match the app UI. Opening the dialog now defaults to local open and remote closed for faster access. - Add CSS for branch-filter dialog layout, theming, and responsiveness - Implement group toggles with chevrons and visible selected counts - Default to local group open and remote group closed on dialog open
This commit is contained in:
+149
@@ -8523,3 +8523,152 @@ input:focus, textarea:focus, select:focus { box-shadow: 0 0 0 3px color-mix(in s
|
|||||||
:root[data-appearance="custom"] .graph-gutter {
|
:root[data-appearance="custom"] .graph-gutter {
|
||||||
background: var(--color-surface-dim);
|
background: var(--color-surface-dim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Branch visibility dialog ---------------------------------------------
|
||||||
|
Local and remote branches share one visual and interaction language. */
|
||||||
|
.branch-filter-backdrop,
|
||||||
|
:root[data-theme="light"] .branch-filter-backdrop {
|
||||||
|
padding: 24px;
|
||||||
|
background: var(--app-dialog-backdrop);
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-dialog,
|
||||||
|
:root[data-theme="light"] .branch-filter-dialog {
|
||||||
|
width: min(560px, 100%);
|
||||||
|
max-height: min(680px, calc(100vh - 48px));
|
||||||
|
border-color: var(--color-border);
|
||||||
|
background: var(--app-dialog-bg);
|
||||||
|
box-shadow: var(--app-dialog-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-dialog-head {
|
||||||
|
min-height: 64px;
|
||||||
|
padding: 12px 14px;
|
||||||
|
border-bottom-color: var(--color-border);
|
||||||
|
background: var(--app-dialog-chrome);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-dialog-head h3 {
|
||||||
|
color: var(--color-ink);
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-summary {
|
||||||
|
min-height: 48px;
|
||||||
|
padding: 8px 14px;
|
||||||
|
border-bottom-color: var(--color-border);
|
||||||
|
color: var(--color-ink-muted);
|
||||||
|
background: var(--color-surface-dim);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-actions button {
|
||||||
|
min-height: 28px;
|
||||||
|
border-color: var(--color-border);
|
||||||
|
color: var(--color-ink-muted);
|
||||||
|
background: var(--color-surface-raised);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-actions button:hover:not(:disabled) {
|
||||||
|
border-color: var(--color-border-input);
|
||||||
|
color: var(--color-ink);
|
||||||
|
background: var(--color-surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-dialog-list {
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
background: var(--color-surface-alt);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-group {
|
||||||
|
display: grid;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
background: var(--color-surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-group-toggle {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 16px minmax(0, 1fr) auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 36px;
|
||||||
|
padding: 0 10px;
|
||||||
|
border: 0;
|
||||||
|
color: var(--color-ink-muted);
|
||||||
|
background: var(--color-surface-dim);
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-group-toggle:hover:not(:disabled) {
|
||||||
|
color: var(--color-ink);
|
||||||
|
background: var(--color-surface-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-group-toggle > svg {
|
||||||
|
color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-group-toggle > span {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 850;
|
||||||
|
letter-spacing: 0.09em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-group-toggle > em {
|
||||||
|
min-width: 34px;
|
||||||
|
padding: 2px 5px;
|
||||||
|
border: 1px solid var(--color-border-subtle);
|
||||||
|
color: var(--color-ink-faint);
|
||||||
|
background: var(--color-surface-raised);
|
||||||
|
font-size: 9px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 800;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-group-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 6px;
|
||||||
|
border-top: 1px solid var(--color-border-subtle);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-option,
|
||||||
|
:root[data-theme="light"] .branch-filter-option,
|
||||||
|
:root[data-theme="light"] .branch-filter-option.remote {
|
||||||
|
min-height: 34px;
|
||||||
|
border-color: color-mix(in srgb, var(--color-accent) 32%, var(--color-border));
|
||||||
|
color: var(--color-ink);
|
||||||
|
background: color-mix(in srgb, var(--color-accent) 8%, var(--color-surface-raised));
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-option:hover,
|
||||||
|
:root[data-theme="light"] .branch-filter-option:hover {
|
||||||
|
border-color: color-mix(in srgb, var(--color-accent) 52%, var(--color-border));
|
||||||
|
color: var(--color-ink);
|
||||||
|
background: color-mix(in srgb, var(--color-accent) 13%, var(--color-surface-hover));
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-option.muted,
|
||||||
|
:root[data-theme="light"] .branch-filter-option.muted {
|
||||||
|
border-color: var(--color-border-subtle);
|
||||||
|
color: var(--color-ink-faint);
|
||||||
|
background: var(--color-surface-raised);
|
||||||
|
opacity: 0.72;
|
||||||
|
}
|
||||||
|
|
||||||
|
.branch-filter-option input {
|
||||||
|
accent-color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 560px) {
|
||||||
|
.branch-filter-backdrop { padding: 8px; }
|
||||||
|
.branch-filter-summary { align-items: stretch; flex-direction: column; }
|
||||||
|
.branch-filter-actions { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
||||||
|
}
|
||||||
|
|||||||
@@ -117,6 +117,8 @@
|
|||||||
let customVisibleBranches = $state<Set<string>>(new Set());
|
let customVisibleBranches = $state<Set<string>>(new Set());
|
||||||
let loadedVisibilityRepository = $state("");
|
let loadedVisibilityRepository = $state("");
|
||||||
let branchDialogOpen = $state(false);
|
let branchDialogOpen = $state(false);
|
||||||
|
let localBranchGroupOpen = $state(true);
|
||||||
|
let remoteBranchGroupOpen = $state(false);
|
||||||
let expandedRefsCommitHash = $state("");
|
let expandedRefsCommitHash = $state("");
|
||||||
let panelElement = $state<HTMLElement | null>(null);
|
let panelElement = $state<HTMLElement | null>(null);
|
||||||
let contextCommit = $state<GitCommit | null>(null);
|
let contextCommit = $state<GitCommit | null>(null);
|
||||||
@@ -428,6 +430,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openBranchDialog() {
|
function openBranchDialog() {
|
||||||
|
localBranchGroupOpen = true;
|
||||||
|
remoteBranchGroupOpen = false;
|
||||||
branchDialogOpen = true;
|
branchDialogOpen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1202,7 +1206,19 @@
|
|||||||
|
|
||||||
<div class="branch-filter-dialog-list">
|
<div class="branch-filter-dialog-list">
|
||||||
{#if localBranchNames.length > 0}
|
{#if localBranchNames.length > 0}
|
||||||
<span class="branch-filter-group-label">Local</span>
|
<section class="branch-filter-group">
|
||||||
|
<button
|
||||||
|
class="branch-filter-group-toggle"
|
||||||
|
type="button"
|
||||||
|
aria-expanded={localBranchGroupOpen}
|
||||||
|
onclick={() => { localBranchGroupOpen = !localBranchGroupOpen; }}
|
||||||
|
>
|
||||||
|
{#if localBranchGroupOpen}<ChevronDown size={14} aria-hidden="true" />{:else}<ChevronRight size={14} aria-hidden="true" />{/if}
|
||||||
|
<span>Local</span>
|
||||||
|
<em>{localBranchNames.filter(branchIsVisible).length}/{localBranchNames.length}</em>
|
||||||
|
</button>
|
||||||
|
{#if localBranchGroupOpen}
|
||||||
|
<div class="branch-filter-group-list">
|
||||||
{#each localBranchNames as branch}
|
{#each localBranchNames as branch}
|
||||||
<label class:muted={!branchIsVisible(branch)} class="branch-filter-option" title={branch}>
|
<label class:muted={!branchIsVisible(branch)} class="branch-filter-option" title={branch}>
|
||||||
<input
|
<input
|
||||||
@@ -1214,11 +1230,26 @@
|
|||||||
<span>{branch}</span>
|
<span>{branch}</span>
|
||||||
</label>
|
</label>
|
||||||
{/each}
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
{#if remoteBranchNames.length > 0}
|
{#if remoteBranchNames.length > 0}
|
||||||
<span class="branch-filter-group-label">Remote</span>
|
<section class="branch-filter-group">
|
||||||
|
<button
|
||||||
|
class="branch-filter-group-toggle"
|
||||||
|
type="button"
|
||||||
|
aria-expanded={remoteBranchGroupOpen}
|
||||||
|
onclick={() => { remoteBranchGroupOpen = !remoteBranchGroupOpen; }}
|
||||||
|
>
|
||||||
|
{#if remoteBranchGroupOpen}<ChevronDown size={14} aria-hidden="true" />{:else}<ChevronRight size={14} aria-hidden="true" />{/if}
|
||||||
|
<span>Remote</span>
|
||||||
|
<em>{remoteBranchNames.filter(branchIsVisible).length}/{remoteBranchNames.length}</em>
|
||||||
|
</button>
|
||||||
|
{#if remoteBranchGroupOpen}
|
||||||
|
<div class="branch-filter-group-list">
|
||||||
{#each remoteBranchNames as branch}
|
{#each remoteBranchNames as branch}
|
||||||
<label class:muted={!branchIsVisible(branch)} class="branch-filter-option remote" title={branch}>
|
<label class:muted={!branchIsVisible(branch)} class="branch-filter-option" title={branch}>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={branchIsVisible(branch)}
|
checked={branchIsVisible(branch)}
|
||||||
@@ -1228,6 +1259,9 @@
|
|||||||
<span>{branch}</span>
|
<span>{branch}</span>
|
||||||
</label>
|
</label>
|
||||||
{/each}
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user