feat(toolbar): overhaul repository action bar UI and status display
This refactors the entire appearance and structure of the repository toolbar, modernizing its layout and improving visual consistency across different states. The component now dynamically displays remote tracking information (ahead/behind) directly on Pull and Push buttons for immediate user feedback. - Implemented a comprehensive CSS overhaul using grid and flexbox for better responsiveness. - Added support for German localization within the toolbar actions. - Enhanced visibility of sync status by displaying commit counts next to pull/push buttons.
This commit is contained in:
+187
-125
@@ -1,11 +1,26 @@
|
||||
<script lang="ts">
|
||||
import { CloudDownload, Download, FolderOpen, GitCompare, History, ListRestart, LoaderCircle, RefreshCw, Search, Upload } from "@lucide/svelte";
|
||||
import {
|
||||
ChevronDown,
|
||||
CloudDownload,
|
||||
Download,
|
||||
FolderOpen,
|
||||
GitCompare,
|
||||
History,
|
||||
ListRestart,
|
||||
LoaderCircle,
|
||||
RefreshCw,
|
||||
Search,
|
||||
Upload,
|
||||
} from "@lucide/svelte";
|
||||
|
||||
export let hasRepository: boolean = false;
|
||||
export let isBusy: boolean = false;
|
||||
export let operation: string = "";
|
||||
export let ahead: number = 0;
|
||||
export let behind: number = 0;
|
||||
export let autoRefreshEnabled: boolean = true;
|
||||
export let autoRefreshInFlight: boolean = false;
|
||||
export let language: "en" | "de" = "en";
|
||||
export let onFetch: () => void = () => {};
|
||||
export let onPull: () => void = () => {};
|
||||
export let onPush: () => void = () => {};
|
||||
@@ -16,141 +31,188 @@
|
||||
export let onReflog: () => void = () => {};
|
||||
export let onOpenInExplorer: () => void = () => {};
|
||||
export let onToggleAutoRefresh: () => void = () => {};
|
||||
|
||||
let historyOpen = false;
|
||||
let toolbarElement: HTMLDivElement;
|
||||
|
||||
$: isGerman = language === "de";
|
||||
|
||||
function runHistoryAction(action: () => void) {
|
||||
historyOpen = false;
|
||||
action();
|
||||
}
|
||||
|
||||
function handleWindowClick(event: MouseEvent) {
|
||||
if (historyOpen && toolbarElement && !toolbarElement.contains(event.target as Node)) {
|
||||
historyOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleWindowKeydown(event: KeyboardEvent) {
|
||||
if (event.key === "Escape" && historyOpen) {
|
||||
event.stopPropagation();
|
||||
historyOpen = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="repo-toolbar" role="toolbar" aria-label="Repository actions">
|
||||
<button
|
||||
class="tb-action"
|
||||
onclick={onOpenInExplorer}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title="Open repository in Explorer"
|
||||
aria-label="Open repository in Explorer"
|
||||
>
|
||||
<FolderOpen size={14} aria-hidden="true" />
|
||||
<span class="tb-action-label">Explorer</span>
|
||||
</button>
|
||||
<svelte:window onclick={handleWindowClick} onkeydown={handleWindowKeydown} />
|
||||
|
||||
<button
|
||||
class="tb-action"
|
||||
onclick={onSearch}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title="Global search"
|
||||
aria-label="Global search"
|
||||
>
|
||||
<Search size={14} aria-hidden="true" />
|
||||
<span class="tb-action-label">Search</span>
|
||||
</button>
|
||||
<div bind:this={toolbarElement} class="repo-toolbar" role="toolbar" aria-label={isGerman ? "Repository-Aktionen" : "Repository actions"}>
|
||||
<div class="repo-toolbar-sync">
|
||||
<span class="repo-toolbar-group-label">Sync</span>
|
||||
<div class="repo-action-group repo-sync-actions">
|
||||
<button
|
||||
class="repo-action fetch"
|
||||
onclick={onFetch}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title="Fetch"
|
||||
aria-label="Fetch"
|
||||
>
|
||||
{#if operation === "Fetching"}
|
||||
<LoaderCircle class="spin" size={15} aria-hidden="true" />
|
||||
{:else}
|
||||
<CloudDownload size={15} aria-hidden="true" />
|
||||
{/if}
|
||||
<span class="repo-action-label">Fetch</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="tb-action"
|
||||
onclick={onCompare}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title="Compare commits"
|
||||
aria-label="Compare commits"
|
||||
>
|
||||
<GitCompare size={14} aria-hidden="true" />
|
||||
<span class="tb-action-label">Compare</span>
|
||||
</button>
|
||||
<button
|
||||
class="repo-action sync-primary"
|
||||
onclick={onPull}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title="Pull"
|
||||
aria-label={behind > 0
|
||||
? `Pull, ${behind} ${isGerman ? (behind === 1 ? "Remote-Commit voraus" : "Remote-Commits voraus") : (behind === 1 ? "commit behind" : "commits behind")}`
|
||||
: "Pull"}
|
||||
>
|
||||
{#if operation === "Pulling"}
|
||||
<LoaderCircle class="spin" size={15} aria-hidden="true" />
|
||||
{:else}
|
||||
<Download size={15} aria-hidden="true" />
|
||||
{/if}
|
||||
<span class="repo-action-label">Pull</span>
|
||||
{#if behind > 0}<span class="repo-action-count behind">↓{behind}</span>{/if}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="tb-action"
|
||||
onclick={onInteractiveRebase}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title="Interactive rebase"
|
||||
aria-label="Interactive rebase"
|
||||
>
|
||||
<ListRestart size={14} aria-hidden="true" />
|
||||
<span class="tb-action-label">Rebase</span>
|
||||
</button>
|
||||
<button
|
||||
class="repo-action sync-primary"
|
||||
onclick={onPush}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title="Push"
|
||||
aria-label={ahead > 0
|
||||
? `Push, ${ahead} ${isGerman ? (ahead === 1 ? "lokaler Commit voraus" : "lokale Commits voraus") : (ahead === 1 ? "commit ahead" : "commits ahead")}`
|
||||
: "Push"}
|
||||
>
|
||||
{#if operation === "Pushing"}
|
||||
<LoaderCircle class="spin" size={15} aria-hidden="true" />
|
||||
{:else}
|
||||
<Upload size={15} aria-hidden="true" />
|
||||
{/if}
|
||||
<span class="repo-action-label">Push</span>
|
||||
{#if ahead > 0}<span class="repo-action-count ahead">↑{ahead}</span>{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
class="tb-action"
|
||||
onclick={onReflog}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title="Reflog"
|
||||
aria-label="Reflog"
|
||||
>
|
||||
<History size={14} aria-hidden="true" />
|
||||
<span class="tb-action-label">Reflog</span>
|
||||
</button>
|
||||
<div class="repo-toolbar-divider" aria-hidden="true"></div>
|
||||
|
||||
<div class="titlebar-divider" aria-hidden="true"></div>
|
||||
<div class="repo-action-group repo-inspect-actions">
|
||||
<button
|
||||
class="repo-action"
|
||||
onclick={onSearch}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title={isGerman ? "Globale Suche" : "Global search"}
|
||||
aria-label={isGerman ? "Globale Suche" : "Global search"}
|
||||
>
|
||||
<Search size={15} aria-hidden="true" />
|
||||
<span class="repo-action-label">{isGerman ? "Suchen" : "Search"}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="tb-action"
|
||||
onclick={onFetch}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title="Fetch"
|
||||
aria-label="Fetch"
|
||||
>
|
||||
{#if operation === "Fetching"}
|
||||
<LoaderCircle class="spin" size={14} aria-hidden="true" />
|
||||
{:else}
|
||||
<CloudDownload size={14} aria-hidden="true" />
|
||||
{/if}
|
||||
<span class="tb-action-label">Fetch</span>
|
||||
</button>
|
||||
<button
|
||||
class="repo-action"
|
||||
onclick={onCompare}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title={isGerman ? "Commits vergleichen" : "Compare commits"}
|
||||
aria-label={isGerman ? "Commits vergleichen" : "Compare commits"}
|
||||
>
|
||||
<GitCompare size={15} aria-hidden="true" />
|
||||
<span class="repo-action-label">{isGerman ? "Vergleichen" : "Compare"}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="tb-action"
|
||||
onclick={onPull}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title="Pull"
|
||||
aria-label="Pull"
|
||||
>
|
||||
{#if operation === "Pulling"}
|
||||
<LoaderCircle class="spin" size={14} aria-hidden="true" />
|
||||
{:else}
|
||||
<Download size={14} aria-hidden="true" />
|
||||
{/if}
|
||||
<span class="tb-action-label">Pull</span>
|
||||
</button>
|
||||
<div class="repo-history-wrap">
|
||||
<button
|
||||
class="repo-action history-trigger"
|
||||
class:active={historyOpen}
|
||||
type="button"
|
||||
onclick={() => { historyOpen = !historyOpen; }}
|
||||
disabled={!hasRepository || isBusy}
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={historyOpen}
|
||||
title={isGerman ? "Verlauf und Rebase" : "History and rebase"}
|
||||
>
|
||||
<History size={15} aria-hidden="true" />
|
||||
<span class="repo-action-label">{isGerman ? "Verlauf" : "History"}</span>
|
||||
<ChevronDown class="history-chevron" size={13} aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="tb-action"
|
||||
onclick={onPush}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title="Push"
|
||||
aria-label="Push"
|
||||
>
|
||||
{#if operation === "Pushing"}
|
||||
<LoaderCircle class="spin" size={14} aria-hidden="true" />
|
||||
{:else}
|
||||
<Upload size={14} aria-hidden="true" />
|
||||
{/if}
|
||||
<span class="tb-action-label">Push</span>
|
||||
</button>
|
||||
{#if historyOpen}
|
||||
<div class="repo-history-menu" role="menu">
|
||||
<button type="button" role="menuitem" onclick={() => runHistoryAction(onReflog)}>
|
||||
<History size={15} aria-hidden="true" />
|
||||
<span>
|
||||
<strong>Reflog</strong>
|
||||
<small>{isGerman ? "Lokale Referenzbewegungen" : "Local reference movements"}</small>
|
||||
</span>
|
||||
</button>
|
||||
<button type="button" role="menuitem" onclick={() => runHistoryAction(onInteractiveRebase)}>
|
||||
<ListRestart size={15} aria-hidden="true" />
|
||||
<span>
|
||||
<strong>{isGerman ? "Interaktiver Rebase" : "Interactive rebase"}</strong>
|
||||
<small>{isGerman ? "Commits ordnen und zusammenfassen" : "Reorder and combine commits"}</small>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="titlebar-divider" aria-hidden="true"></div>
|
||||
<div class="repo-toolbar-spacer"></div>
|
||||
<div class="repo-toolbar-divider utility" aria-hidden="true"></div>
|
||||
|
||||
<button
|
||||
class="tb-action"
|
||||
onclick={onRefresh}
|
||||
disabled={isBusy || !hasRepository}
|
||||
title="Refresh"
|
||||
aria-label="Refresh"
|
||||
>
|
||||
<RefreshCw
|
||||
class={operation === "Refreshing" ? "spin" : ""}
|
||||
size={14}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span class="tb-action-label">Refresh</span>
|
||||
</button>
|
||||
<div class="repo-action-group repo-utility-actions">
|
||||
<button
|
||||
class="repo-action"
|
||||
onclick={onOpenInExplorer}
|
||||
disabled={!hasRepository || isBusy}
|
||||
title={isGerman ? "Repository im Explorer öffnen" : "Open repository in Explorer"}
|
||||
aria-label={isGerman ? "Repository im Explorer öffnen" : "Open repository in Explorer"}
|
||||
>
|
||||
<FolderOpen size={15} aria-hidden="true" />
|
||||
<span class="repo-action-label utility-label">Explorer</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="tb-action auto-toggle"
|
||||
class:active={autoRefreshEnabled}
|
||||
onclick={onToggleAutoRefresh}
|
||||
aria-pressed={autoRefreshEnabled}
|
||||
title={autoRefreshEnabled ? "Auto-refresh on — click to disable" : "Auto-refresh off — click to enable"}
|
||||
aria-label="Toggle auto-refresh"
|
||||
>
|
||||
<RefreshCw
|
||||
class={autoRefreshInFlight ? "spin" : ""}
|
||||
size={14}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span class="tb-action-label">Auto</span>
|
||||
</button>
|
||||
<button
|
||||
class="repo-action icon-action"
|
||||
onclick={onRefresh}
|
||||
disabled={isBusy || !hasRepository}
|
||||
title={isGerman ? "Manuell aktualisieren" : "Refresh now"}
|
||||
aria-label={isGerman ? "Manuell aktualisieren" : "Refresh now"}
|
||||
>
|
||||
<RefreshCw class={operation === "Refreshing" ? "spin" : ""} size={15} aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="repo-auto-toggle"
|
||||
class:active={autoRefreshEnabled}
|
||||
onclick={onToggleAutoRefresh}
|
||||
aria-pressed={autoRefreshEnabled}
|
||||
title={autoRefreshEnabled
|
||||
? (isGerman ? "Auto-Aktualisierung ausschalten" : "Turn auto-refresh off")
|
||||
: (isGerman ? "Auto-Aktualisierung einschalten" : "Turn auto-refresh on")}
|
||||
>
|
||||
<span>{isGerman ? "Auto" : "Auto"}</span>
|
||||
<span class="repo-switch" class:busy={autoRefreshInFlight} aria-hidden="true"><span></span></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user