mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2025-01-06 01:27:24 +03:00
refactor: move remote branches out of tray and into their own component
This commit is contained in:
parent
b916eb8afe
commit
3f74c66fb7
159
packages/ui/src/routes/repo/[projectId]/RemoteBranches.svelte
Normal file
159
packages/ui/src/routes/repo/[projectId]/RemoteBranches.svelte
Normal file
@ -0,0 +1,159 @@
|
||||
<script lang="ts">
|
||||
import { Link } from '$lib/components';
|
||||
import { IconGitBranch, IconRemote } from '$lib/icons';
|
||||
import IconHelp from '$lib/icons/IconHelp.svelte';
|
||||
import Scrollbar from '$lib/components/Scrollbar.svelte';
|
||||
import Tooltip from '$lib/components/Tooltip/Tooltip.svelte';
|
||||
import { IconTriangleDown, IconTriangleUp } from '$lib/icons';
|
||||
import TimeAgo from '$lib/components/TimeAgo/TimeAgo.svelte';
|
||||
import { accordion } from './accordion';
|
||||
import type { CustomStore, RemoteBranch, BaseBranch, Branch } from '$lib/vbranches/types';
|
||||
import type { Readable } from '@square/svelte-store';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
selection: {
|
||||
branch: RemoteBranch;
|
||||
i: number;
|
||||
offset: number;
|
||||
};
|
||||
}>();
|
||||
|
||||
export let remoteBranchStore: CustomStore<RemoteBranch[] | undefined>;
|
||||
let rbViewport: HTMLElement;
|
||||
let rbContents: HTMLElement;
|
||||
let rbSection: HTMLElement;
|
||||
export let peekTrayExpanded = false;
|
||||
export let selectedItem: Readable<Branch | RemoteBranch | BaseBranch | undefined> | undefined;
|
||||
|
||||
$: remoteBranchesState = remoteBranchStore?.state;
|
||||
|
||||
let open = false;
|
||||
|
||||
function select(branch: RemoteBranch, i: number) {
|
||||
const element = rbContents.children[i] as HTMLDivElement;
|
||||
const offset = element.offsetTop + rbSection.offsetTop - rbViewport.scrollTop;
|
||||
dispatch('selection', { branch, i, offset });
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="bg-color-4 border-color-4 flex items-center justify-between border-b border-t px-2 py-1 pr-1"
|
||||
>
|
||||
<div class="flex flex-row place-items-center space-x-2">
|
||||
<div class="text-color-2 font-bold">Remote Branches</div>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.gitbutler.com/features/virtual-branches/remote-branches"
|
||||
>
|
||||
<IconHelp class="text-color-3 h-3 w-3" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex h-4 w-4 justify-around">
|
||||
<button class="h-full w-full" on:click={() => (open = !open)}>
|
||||
{#if open}
|
||||
<IconTriangleUp />
|
||||
{:else}
|
||||
<IconTriangleDown />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div bind:this={rbSection} use:accordion={open} class="border-color-5 relative flex-grow border-b">
|
||||
<div
|
||||
bind:this={rbViewport}
|
||||
on:scroll
|
||||
class="hide-native-scrollbar flex max-h-full flex-grow flex-col overflow-y-scroll overscroll-none"
|
||||
>
|
||||
<div bind:this={rbContents}>
|
||||
{#if $remoteBranchesState.isLoading}
|
||||
<div class="px-2 py-1">loading...</div>
|
||||
{:else if $remoteBranchesState.isError}
|
||||
<div class="px-2 py-1">Something went wrong</div>
|
||||
{:else if !$remoteBranchStore || $remoteBranchStore.length == 0}
|
||||
<div class="p-4">
|
||||
<p class="text-color-3 mb-2">
|
||||
There are no local or remote Git branches that can be imported as virtual branches
|
||||
</p>
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.gitbutler.com/features/virtual-branches/remote-branches"
|
||||
>
|
||||
Learn more
|
||||
</Link>
|
||||
</div>
|
||||
{:else if $remoteBranchStore}
|
||||
{#each $remoteBranchStore as branch, i}
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={() => select(branch, i)}
|
||||
on:keypress={() => select(branch, i)}
|
||||
class:bg-color-4={$selectedItem == branch && peekTrayExpanded}
|
||||
class="border-color-4 flex flex-col justify-between gap-1 border-b px-2 py-1 pt-2 -outline-offset-2 outline-blue-200 last:border-b focus:outline-2"
|
||||
>
|
||||
<div class="flex flex-row items-center gap-x-2 pr-1">
|
||||
<div class="text-color-3">
|
||||
{#if branch.name.match('refs/remotes')}
|
||||
<Tooltip
|
||||
label="This is a remote branch that you don't have a virtual branch tracking yet"
|
||||
>
|
||||
<IconRemote class="h-4 w-4" />
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<Tooltip label="This is a local branch that is not a virtual branch yet">
|
||||
<IconGitBranch class="h-4 w-4" />
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="text-color-2 flex-grow truncate" title={branch.name}>
|
||||
{branch.name
|
||||
.replace('refs/remotes/', '')
|
||||
.replace('origin/', '')
|
||||
.replace('refs/heads/', '')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row justify-between space-x-2 rounded p-1 pr-1">
|
||||
<div class="text-color-4 flex-grow-0 text-sm">
|
||||
<TimeAgo date={branch.lastCommitTs()} />
|
||||
</div>
|
||||
<div class="flex flex-grow-0 flex-row space-x-2">
|
||||
<Tooltip
|
||||
label="This branch has {branch.ahead()} commits not on your base branch and your base has {branch.behind} commits not on this branch yet"
|
||||
>
|
||||
<div class="bg-color-3 text-color-3 rounded-lg px-2 text-sm">
|
||||
{branch.ahead()} / {branch.behind}
|
||||
</div>
|
||||
</Tooltip>
|
||||
{#await branch.isMergeable then isMergeable}
|
||||
{#if !isMergeable}
|
||||
<div class="font-bold text-red-500" title="Can't be merged">!</div>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
<div
|
||||
class="isolate flex flex-grow justify-end -space-x-2 overflow-hidden transition duration-300 ease-in-out hover:space-x-1 hover:transition hover:ease-in"
|
||||
>
|
||||
{#each branch.authors() as author}
|
||||
<img
|
||||
class="relative z-30 inline-block h-4 w-4 rounded-full ring-1 ring-white dark:ring-black"
|
||||
title="Gravatar for {author.email}"
|
||||
alt="Gravatar for {author.email}"
|
||||
srcset="{author.gravatarUrl} 2x"
|
||||
width="100"
|
||||
height="100"
|
||||
on:error
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<Scrollbar viewport={rbViewport} contents={rbContents} width="0.5rem" />
|
||||
</div>
|
@ -1,7 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Link } from '$lib/components';
|
||||
import { Branch, BaseBranch, RemoteBranch, type CustomStore } from '$lib/vbranches/types';
|
||||
import { IconBranch, IconGitBranch, IconRemote } from '$lib/icons';
|
||||
import { IconBranch } from '$lib/icons';
|
||||
import { IconTriangleDown, IconTriangleUp } from '$lib/icons';
|
||||
import { accordion } from './accordion';
|
||||
import { SETTINGS_CONTEXT, type SettingsStore } from '$lib/userSettings';
|
||||
@ -9,7 +8,6 @@
|
||||
import type { BranchController } from '$lib/vbranches/branchController';
|
||||
import Tooltip from '$lib/components/Tooltip/Tooltip.svelte';
|
||||
import Scrollbar from '$lib/components/Scrollbar.svelte';
|
||||
import IconHelp from '$lib/icons/IconHelp.svelte';
|
||||
import { derived, get, type Readable } from '@square/svelte-store';
|
||||
import PeekTray from './PeekTray.svelte';
|
||||
import IconRefresh from '$lib/icons/IconRefresh.svelte';
|
||||
@ -23,6 +21,7 @@
|
||||
import IconChevronRightSmall from '$lib/icons/IconChevronRightSmall.svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { computedAddedRemoved } from '$lib/vbranches/fileStatus';
|
||||
import RemoteBranches from './RemoteBranches.svelte';
|
||||
|
||||
export let branchesWithContentStore: CustomStore<Branch[] | undefined>;
|
||||
export let remoteBranchStore: CustomStore<RemoteBranch[] | undefined>;
|
||||
@ -34,27 +33,26 @@
|
||||
export let peekTrayExpanded = false;
|
||||
|
||||
$: branchesState = branchesWithContentStore?.state;
|
||||
$: remoteBranchesState = remoteBranchStore?.state;
|
||||
|
||||
const userSettings = getContext<SettingsStore>(SETTINGS_CONTEXT);
|
||||
|
||||
let yourBranchesOpen = true;
|
||||
let remoteBranchesOpen = true;
|
||||
|
||||
let applyConflictedModal: Modal;
|
||||
|
||||
let vbViewport: HTMLElement;
|
||||
let vbContents: HTMLElement;
|
||||
let rbViewport: HTMLElement;
|
||||
let rbContents: HTMLElement;
|
||||
let rbSection: HTMLElement;
|
||||
let baseContents: HTMLElement;
|
||||
|
||||
let selectedItem: Readable<Branch | RemoteBranch | BaseBranch | undefined> | undefined;
|
||||
let overlayOffsetTop = 0;
|
||||
let fetching = false;
|
||||
|
||||
function select(detail: Branch | RemoteBranch | BaseBranch | undefined, i: number): void {
|
||||
function select(
|
||||
detail: Branch | RemoteBranch | BaseBranch | undefined,
|
||||
i: number,
|
||||
offset?: number
|
||||
): void {
|
||||
if (peekTrayExpanded && selectedItem && detail == get(selectedItem)) {
|
||||
peekTrayExpanded = false;
|
||||
return;
|
||||
@ -69,8 +67,7 @@
|
||||
selectedItem = derived(remoteBranchStore, (branches) =>
|
||||
branches?.find((remoteBranch) => remoteBranch.sha == detail.sha)
|
||||
);
|
||||
const element = rbContents.children[i] as HTMLDivElement;
|
||||
overlayOffsetTop = element.offsetTop + rbSection.offsetTop - rbViewport.scrollTop;
|
||||
overlayOffsetTop = offset || overlayOffsetTop;
|
||||
} else if (detail instanceof BaseBranch) {
|
||||
selectedItem = baseBranchStore;
|
||||
overlayOffsetTop = baseContents.offsetTop;
|
||||
@ -308,130 +305,13 @@
|
||||
/>
|
||||
|
||||
<!-- Remote branches -->
|
||||
<div
|
||||
class="bg-color-4 border-color-4 flex items-center justify-between border-b border-t px-2 py-1 pr-1"
|
||||
>
|
||||
<div class="flex flex-row place-items-center space-x-2">
|
||||
<div class="text-color-2 font-bold">Remote Branches</div>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.gitbutler.com/features/virtual-branches/remote-branches"
|
||||
>
|
||||
<IconHelp class="text-color-3 h-3 w-3" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex h-4 w-4 justify-around">
|
||||
<button class="h-full w-full" on:click={() => (remoteBranchesOpen = !remoteBranchesOpen)}>
|
||||
{#if remoteBranchesOpen}
|
||||
<IconTriangleUp />
|
||||
{:else}
|
||||
<IconTriangleDown />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
bind:this={rbSection}
|
||||
use:accordion={remoteBranchesOpen}
|
||||
class="border-color-5 relative flex-grow border-b"
|
||||
>
|
||||
<div
|
||||
bind:this={rbViewport}
|
||||
on:scroll={onScroll}
|
||||
class="hide-native-scrollbar flex max-h-full flex-grow flex-col overflow-y-scroll overscroll-none"
|
||||
>
|
||||
<div bind:this={rbContents}>
|
||||
{#if $remoteBranchesState.isLoading}
|
||||
<div class="px-2 py-1">loading...</div>
|
||||
{:else if $remoteBranchesState.isError}
|
||||
<div class="px-2 py-1">Something went wrong</div>
|
||||
{:else if !$remoteBranchStore || $remoteBranchStore.length == 0}
|
||||
<div class="p-4">
|
||||
<p class="text-color-3 mb-2">
|
||||
There are no local or remote Git branches that can be imported as virtual branches
|
||||
</p>
|
||||
<Link
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="https://docs.gitbutler.com/features/virtual-branches/remote-branches"
|
||||
>
|
||||
Learn more
|
||||
</Link>
|
||||
</div>
|
||||
{:else if $remoteBranchStore}
|
||||
{#each $remoteBranchStore as branch, i}
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:click={() => select(branch, i)}
|
||||
on:keypress={() => select(branch, i)}
|
||||
class:bg-color-4={$selectedItem == branch && peekTrayExpanded}
|
||||
class="border-color-4 flex flex-col justify-between gap-1 border-b px-2 py-1 pt-2 -outline-offset-2 outline-blue-200 last:border-b focus:outline-2"
|
||||
>
|
||||
<div class="flex flex-row items-center gap-x-2 pr-1">
|
||||
<div class="text-color-3">
|
||||
{#if branch.name.match('refs/remotes')}
|
||||
<Tooltip
|
||||
label="This is a remote branch that you don't have a virtual branch tracking yet"
|
||||
>
|
||||
<IconRemote class="h-4 w-4" />
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<Tooltip label="This is a local branch that is not a virtual branch yet">
|
||||
<IconGitBranch class="h-4 w-4" />
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="text-color-2 flex-grow truncate" title={branch.name}>
|
||||
{branch.name
|
||||
.replace('refs/remotes/', '')
|
||||
.replace('origin/', '')
|
||||
.replace('refs/heads/', '')}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row justify-between space-x-2 rounded p-1 pr-1">
|
||||
<div class="text-color-4 flex-grow-0 text-sm">
|
||||
<TimeAgo date={branch.lastCommitTs()} />
|
||||
</div>
|
||||
<div class="flex flex-grow-0 flex-row space-x-2">
|
||||
<Tooltip
|
||||
label="This branch has {branch.ahead()} commits not on your base branch and your base has {branch.behind} commits not on this branch yet"
|
||||
>
|
||||
<div class="bg-color-3 text-color-3 rounded-lg px-2 text-sm">
|
||||
{branch.ahead()} / {branch.behind}
|
||||
</div>
|
||||
</Tooltip>
|
||||
{#await branch.isMergeable then isMergeable}
|
||||
{#if !isMergeable}
|
||||
<div class="font-bold text-red-500" title="Can't be merged">!</div>
|
||||
{/if}
|
||||
{/await}
|
||||
</div>
|
||||
<div
|
||||
class="isolate flex flex-grow justify-end -space-x-2 overflow-hidden transition duration-300 ease-in-out hover:space-x-1 hover:transition hover:ease-in"
|
||||
>
|
||||
{#each branch.authors() as author}
|
||||
<img
|
||||
class="relative z-30 inline-block h-4 w-4 rounded-full ring-1 ring-white dark:ring-black"
|
||||
title="Gravatar for {author.email}"
|
||||
alt="Gravatar for {author.email}"
|
||||
srcset="{author.gravatarUrl} 2x"
|
||||
width="100"
|
||||
height="100"
|
||||
on:error
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<Scrollbar viewport={rbViewport} contents={rbContents} width="0.5rem" />
|
||||
</div>
|
||||
<RemoteBranches
|
||||
on:scroll={onScroll}
|
||||
on:selection={(e) => select(e.detail.branch, e.detail.i, e.detail.offset)}
|
||||
{remoteBranchStore}
|
||||
{peekTrayExpanded}
|
||||
{selectedItem}
|
||||
></RemoteBranches>
|
||||
</div>
|
||||
|
||||
<Modal width="small" bind:this={applyConflictedModal}>
|
||||
|
Loading…
Reference in New Issue
Block a user