mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2025-01-04 15:53:30 +03:00
Update sidebar header to match design
This commit is contained in:
parent
ad4544674b
commit
2cefd8493c
@ -1,97 +1,105 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import type { Project } from '$lib/backend/projects';
|
||||
import Badge from '$lib/components/Badge.svelte';
|
||||
import Button from '$lib/components/Button.svelte';
|
||||
import IconButton from '$lib/components/IconButton.svelte';
|
||||
import TimeAgo from '$lib/components/TimeAgo.svelte';
|
||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||
import type { PrService } from '$lib/github/pullrequest';
|
||||
import Icon from '$lib/icons/Icon.svelte';
|
||||
import IconGithub from '$lib/icons/IconGithub.svelte';
|
||||
import IconRefresh from '$lib/icons/IconRefresh.svelte';
|
||||
import type { BranchController } from '$lib/vbranches/branchController';
|
||||
import type { BaseBranchService } from '$lib/vbranches/branchStoresCache';
|
||||
|
||||
export let project: Project;
|
||||
export let branchController: BranchController;
|
||||
export let baseBranchService: BaseBranchService;
|
||||
export let prService: PrService;
|
||||
|
||||
$: base$ = baseBranchService.base$;
|
||||
$: selected = $page.url.href.endsWith('/base');
|
||||
|
||||
let baseContents: HTMLElement;
|
||||
let fetching = false;
|
||||
let loading = false;
|
||||
</script>
|
||||
|
||||
<a
|
||||
href="/{project.id}/base"
|
||||
class="relative flex flex-grow items-center gap-x-2 rounded-md px-3 py-1 text-lg"
|
||||
style:background-color={selected ? 'var(--bg-surface-highlight)' : undefined}
|
||||
class="card"
|
||||
style:background-color={selected ? 'var(--clr-theme-container-pale)' : undefined}
|
||||
bind:this={baseContents}
|
||||
>
|
||||
<div class="flex items-center gap-1">
|
||||
{#if $base$?.remoteUrl.includes('github.com')}
|
||||
<IconGithub class="h-4 w-4" />
|
||||
{:else}
|
||||
<Icon name="branch" />
|
||||
{/if}
|
||||
<div class="card__icon">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="16" height="16" rx="4" fill="#FB7D61" />
|
||||
<path d="M8 4L12 8L8 12L4 8L8 4Z" fill="white" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="font-semibold">
|
||||
{$base$?.branchName}
|
||||
</div>
|
||||
|
||||
{#if ($base$?.behind || 0) > 0}
|
||||
<Tooltip label="Unmerged upstream commits">
|
||||
<div
|
||||
class="flex h-4 w-4 items-center justify-center rounded-full text-base font-bold"
|
||||
style:background-color="var(--bg-surface-highlight)"
|
||||
>
|
||||
{$base$?.behind}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip label="Merge upstream commits into common base">
|
||||
<Button
|
||||
height="small"
|
||||
color="purple"
|
||||
{loading}
|
||||
on:click={async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
loading = true;
|
||||
try {
|
||||
await branchController.updateBaseBranch();
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}}
|
||||
>
|
||||
merge
|
||||
</Button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
<IconButton
|
||||
class="items-center justify-center align-top "
|
||||
on:click={async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
fetching = true;
|
||||
await branchController.fetchFromTarget().finally(() => {
|
||||
fetching = false;
|
||||
prService.reload();
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div class:animate-spin={fetching}>
|
||||
<IconRefresh class="h-4 w-4" />
|
||||
<div class="card__content">
|
||||
<div class="card__row_1 text-base-13 font-bold">
|
||||
<span>Trunk</span>
|
||||
<Tooltip label="Unmerged upstream commits">
|
||||
<Badge count={$base$?.behind || 0} />
|
||||
</Tooltip>
|
||||
{#if ($base$?.behind || 0) > 0}
|
||||
<Tooltip label="Merge upstream commits into common base">
|
||||
<Button
|
||||
height="small"
|
||||
color="purple"
|
||||
{loading}
|
||||
on:click={async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
loading = true;
|
||||
try {
|
||||
await branchController.updateBaseBranch();
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}}
|
||||
>
|
||||
merge
|
||||
</Button>
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
</IconButton>
|
||||
<div class="card__row_2 text-base-12">
|
||||
{#if $base$?.remoteUrl.includes('github.com')}
|
||||
<IconGithub class="h-4 w-4" />
|
||||
{:else}
|
||||
<Icon name="branch" />
|
||||
{/if}
|
||||
{$base$?.branchName}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="text-color-3 py-0.5 pl-9 text-sm">
|
||||
<Tooltip label="Last fetch from upstream">
|
||||
{#if $base$?.fetchedAt}
|
||||
<TimeAgo date={$base$.fetchedAt} />
|
||||
{/if}
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.card {
|
||||
display: flex;
|
||||
gap: var(--space-10);
|
||||
padding: var(--space-8);
|
||||
border-radius: var(--m, 6px);
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--clr-theme-container-pale);
|
||||
}
|
||||
}
|
||||
.card__icon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.card__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-8);
|
||||
}
|
||||
.card__row_1 {
|
||||
display: flex;
|
||||
gap: var(--space-6);
|
||||
align-items: center;
|
||||
color: var(--clr-theme-scale-ntrl-10);
|
||||
}
|
||||
.card__row_2 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-4);
|
||||
color: var(--clr-theme-scale-ntrl-40);
|
||||
}
|
||||
</style>
|
||||
|
@ -4,15 +4,32 @@
|
||||
import type iconsJson from '$lib/icons/icons.json';
|
||||
|
||||
export let href: string;
|
||||
export let icon: keyof typeof iconsJson;
|
||||
export let icon: keyof typeof iconsJson | undefined = undefined;
|
||||
$: selected = $page.url.href.includes(href);
|
||||
</script>
|
||||
|
||||
<a
|
||||
{href}
|
||||
class="mx-4 flex items-center gap-x-1 rounded px-3 py-2 font-semibold"
|
||||
style:background-color={selected ? 'var(--bg-surface-highlight)' : undefined}
|
||||
class="domain-button font-semibold"
|
||||
style:background-color={selected ? 'var(--clr-theme-container-pale)' : undefined}
|
||||
>
|
||||
<Icon name={icon} class="text-color-4 mr-1 inline h-4 w-4 align-middle" />
|
||||
{#if icon}
|
||||
<Icon name={icon} class="text-color-4 mr-1 inline h-4 w-4 align-middle" />
|
||||
{/if}
|
||||
<slot />
|
||||
</a>
|
||||
|
||||
<style lang="postcss">
|
||||
.domain-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-10);
|
||||
border-radius: var(--radius-m);
|
||||
background-color: var(--clr-theme-container-light);
|
||||
padding: var(--space-10);
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--clr-theme-container-pale);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -18,7 +18,7 @@
|
||||
style:border-color="var(--border-surface)"
|
||||
>
|
||||
<div class="flex items-center gap-x-1">
|
||||
<Link href="/" class="p-1"><IconHome /></Link>
|
||||
<Link href="/" class="p-1"><Icon name="home" /></Link>
|
||||
<Tooltip label="Send feedback">
|
||||
<button class="p-1 align-middle" on:click={() => events.emit('openSendIssueModal')}>
|
||||
<Icon name="mail"></Icon>
|
||||
|
64
packages/ui/src/routes/[projectId]/navigation/Header.svelte
Normal file
64
packages/ui/src/routes/[projectId]/navigation/Header.svelte
Normal file
@ -0,0 +1,64 @@
|
||||
<script lang="ts">
|
||||
import IconButton from '$lib/components/IconButton.svelte';
|
||||
import TimeAgo from '$lib/components/TimeAgo.svelte';
|
||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||
import type { PrService } from '$lib/github/pullrequest';
|
||||
import IconRefresh from '$lib/icons/IconRefresh.svelte';
|
||||
import type { BranchController } from '$lib/vbranches/branchController';
|
||||
import type { BaseBranchService } from '$lib/vbranches/branchStoresCache';
|
||||
|
||||
export let branchController: BranchController;
|
||||
export let prService: PrService;
|
||||
export let baseBranchService: BaseBranchService;
|
||||
|
||||
$: base$ = baseBranchService.base$;
|
||||
|
||||
let fetching = false;
|
||||
</script>
|
||||
|
||||
<div data-tauri-drag-region class="header">
|
||||
<div class="header__sync text-base-11 font-semibold">
|
||||
<IconButton
|
||||
class="items-center justify-center align-top "
|
||||
on:click={async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
fetching = true;
|
||||
await branchController.fetchFromTarget().finally(() => {
|
||||
fetching = false;
|
||||
prService.reload();
|
||||
});
|
||||
}}
|
||||
>
|
||||
<div class:animate-spin={fetching}>
|
||||
<IconRefresh class="h-4 w-4" />
|
||||
</div>
|
||||
</IconButton>
|
||||
<Tooltip label="Last fetch from upstream">
|
||||
{#if $base$?.fetchedAt}
|
||||
<TimeAgo date={$base$.fetchedAt} />
|
||||
{/if}
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.header {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
justify-content: right;
|
||||
color: var(--clr-theme-scale-ntrl-50);
|
||||
}
|
||||
.header__sync {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-4);
|
||||
padding-top: var(--space-2);
|
||||
padding-bottom: var(--space-2);
|
||||
padding-left: var(--space-6);
|
||||
padding-right: var(--space-6);
|
||||
background: var(--clr-theme-container-pale);
|
||||
border-radius: var(--radius-m);
|
||||
}
|
||||
</style>
|
@ -16,6 +16,7 @@
|
||||
import ProjectSelector from './ProjectSelector.svelte';
|
||||
import Branches from './Branches.svelte';
|
||||
import type { BranchService } from '$lib/branches/service';
|
||||
import Header from './Header.svelte';
|
||||
|
||||
export let vbranchService: VirtualBranchService;
|
||||
export let branchService: BranchService;
|
||||
@ -40,20 +41,39 @@
|
||||
role="menu"
|
||||
tabindex="0"
|
||||
>
|
||||
<div class="flex h-8 flex-shrink-0" data-tauri-drag-region>
|
||||
<!-- Top spacer & drag region -->
|
||||
</div>
|
||||
<div class="relative mx-4 mb-4 mt-1">
|
||||
<div class="domains">
|
||||
<Header {branchController} {baseBranchService} {prService} />
|
||||
<ProjectSelector {project} {projectService} />
|
||||
</div>
|
||||
<div class="mx-4 mb-4 mt-1">
|
||||
<BaseBranchCard {project} {baseBranchService} {branchController} {prService} />
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<DomainButton href={`/${project.id}/board`} icon="branch">Applied branches</DomainButton>
|
||||
<div class="flex flex-col gap-1">
|
||||
<BaseBranchCard {project} {baseBranchService} {branchController} />
|
||||
<DomainButton href={`/${project.id}/board`}>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<rect width="16" height="16" rx="4" fill="#797FE6" />
|
||||
<path d="M5 8.8H11V4" stroke="white" stroke-width="2" />
|
||||
<path d="M5 12V8.44444V4" stroke="white" stroke-width="2" />
|
||||
</svg>
|
||||
|
||||
Applied branches
|
||||
</DomainButton>
|
||||
</div>
|
||||
</div>
|
||||
<Branches projectId={project.id} {branchService} grow={!stashExpanded} />
|
||||
<StashedBranches {project} {branchController} {vbranchService} bind:expanded={stashExpanded} />
|
||||
<Footer {user} projectId={project.id} />
|
||||
<AppUpdater {update} />
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.domains {
|
||||
padding-top: var(--space-14);
|
||||
padding-bottom: var(--space-24);
|
||||
padding-left: var(--space-16);
|
||||
padding-right: var(--space-16);
|
||||
}
|
||||
</style>
|
||||
|
@ -15,24 +15,33 @@
|
||||
let popup: ProjectsPopup;
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="relative"
|
||||
use:clickOutside={() => {
|
||||
popup.hide();
|
||||
}}
|
||||
>
|
||||
<button
|
||||
class="flex w-full items-center justify-between rounded-md p-3"
|
||||
style:background-color="var(--bg-surface-highlight)"
|
||||
on:click={(e) => {
|
||||
popup.toggle();
|
||||
e.preventDefault();
|
||||
<div class="wrapper">
|
||||
<div
|
||||
class="relative"
|
||||
use:clickOutside={() => {
|
||||
popup.hide();
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-grow items-center gap-1 font-bold">
|
||||
{project.title}
|
||||
</div>
|
||||
<Icon name="select-chevron" class="align-top" />
|
||||
</button>
|
||||
<ProjectsPopup bind:this={popup} projects={$projects$} />
|
||||
<button
|
||||
class="flex w-full items-center justify-between rounded-md p-3"
|
||||
style:background-color="var(--bg-surface-highlight)"
|
||||
on:click={(e) => {
|
||||
popup.toggle();
|
||||
e.preventDefault();
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-grow items-center gap-1 font-bold">
|
||||
{project.title}
|
||||
</div>
|
||||
<Icon name="select-chevron" class="align-top" />
|
||||
</button>
|
||||
<ProjectsPopup bind:this={popup} projects={$projects$} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
margin-top: var(--space-10);
|
||||
margin-bottom: var(--space-16);
|
||||
}
|
||||
</style>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Badge from '$lib/components/Badge.svelte';
|
||||
import IconTriangleDown from '$lib/icons/IconTriangleDown.svelte';
|
||||
import Icon from '$lib/icons/Icon.svelte';
|
||||
|
||||
export let scrolled: boolean;
|
||||
export let count: string | number | undefined;
|
||||
@ -23,7 +23,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
{#if expandable}
|
||||
<IconTriangleDown class={expanded ? '-rotate-180' : ''} />
|
||||
<Icon name={expanded ? 'chevron-down' : 'chevron-top'} />
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
|
@ -205,6 +205,7 @@
|
||||
--space-8: 8px;
|
||||
--space-10: 10px;
|
||||
--space-12: 12px;
|
||||
--space-14: 16px;
|
||||
--space-16: 16px;
|
||||
--space-20: 20px;
|
||||
--space-24: 24px;
|
||||
|
Loading…
Reference in New Issue
Block a user