Update sidebar header to match design

This commit is contained in:
Mattias Granlund 2023-11-22 18:34:48 +01:00
parent ad4544674b
commit 2cefd8493c
8 changed files with 223 additions and 104 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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;