mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-12-30 04:58:55 +03:00
tag component update, collapsable branches frontend added
This commit is contained in:
parent
c6bc57cb90
commit
e107f6b00c
93
gitbutler-ui/src/lib/components/ActiveBranchStatus.svelte
Normal file
93
gitbutler-ui/src/lib/components/ActiveBranchStatus.svelte
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Tag from '$lib/components/Tag.svelte';
|
||||||
|
import { normalizeBranchName } from '$lib/utils/branch';
|
||||||
|
import { open } from '@tauri-apps/api/shell';
|
||||||
|
import type { BaseBranch, Branch } from '$lib/vbranches/types';
|
||||||
|
|
||||||
|
export let base: BaseBranch | undefined | null;
|
||||||
|
export let branch: Branch;
|
||||||
|
export let prUrl: string | undefined;
|
||||||
|
export let isUnapplied = false;
|
||||||
|
export let hasIntegratedCommits = false;
|
||||||
|
export let isLaneCollapsed = false;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if !branch.upstream}
|
||||||
|
{#if !branch.active}
|
||||||
|
<Tag
|
||||||
|
icon="virtual-branch-small"
|
||||||
|
color="light"
|
||||||
|
help="These changes are stashed away from your working directory."
|
||||||
|
reversedDirection
|
||||||
|
verticalOrientation={isLaneCollapsed}>unapplied</Tag
|
||||||
|
>
|
||||||
|
{:else if hasIntegratedCommits}
|
||||||
|
<Tag
|
||||||
|
icon="removed-branch-small"
|
||||||
|
color="success"
|
||||||
|
help="These changes have been integrated upstream, update your workspace to make this lane disappear."
|
||||||
|
reversedDirection
|
||||||
|
verticalOrientation={isLaneCollapsed}>integrated</Tag
|
||||||
|
>
|
||||||
|
{:else}
|
||||||
|
<Tag
|
||||||
|
icon="virtual-branch-small"
|
||||||
|
color="light"
|
||||||
|
help="These changes are in your working directory."
|
||||||
|
reversedDirection
|
||||||
|
verticalOrientation={isLaneCollapsed}>virtual</Tag
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
{#if !isUnapplied}
|
||||||
|
<Tag
|
||||||
|
disabled
|
||||||
|
help="Branch name that will be used when pushing. You can change it from the lane menu."
|
||||||
|
verticalOrientation={isLaneCollapsed}
|
||||||
|
>
|
||||||
|
origin/{branch.upstreamName
|
||||||
|
? branch.upstreamName
|
||||||
|
: normalizeBranchName(branch.name)}</Tag
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<Tag
|
||||||
|
color="dark"
|
||||||
|
icon="remote-branch-small"
|
||||||
|
help="At least some of your changes have been pushed"
|
||||||
|
verticalOrientation={isLaneCollapsed}
|
||||||
|
reversedDirection>remote</Tag
|
||||||
|
>
|
||||||
|
<Tag
|
||||||
|
icon="open-link"
|
||||||
|
color="ghost"
|
||||||
|
border
|
||||||
|
clickable
|
||||||
|
shrinkable
|
||||||
|
verticalOrientation={isLaneCollapsed}
|
||||||
|
on:click={(e) => {
|
||||||
|
const url = base?.branchUrl(branch.upstream?.name);
|
||||||
|
if (url) open(url);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isLaneCollapsed ? 'View branch' : `origin/${branch.upstream?.name}`}
|
||||||
|
</Tag>
|
||||||
|
{#if prUrl}
|
||||||
|
<Tag
|
||||||
|
icon="pr-small"
|
||||||
|
color="ghost"
|
||||||
|
border
|
||||||
|
clickable
|
||||||
|
verticalOrientation={isLaneCollapsed}
|
||||||
|
on:click={(e) => {
|
||||||
|
const url = prUrl;
|
||||||
|
if (url) open(url);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
View PR
|
||||||
|
</Tag>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
@ -121,6 +121,7 @@
|
|||||||
{projectPath}
|
{projectPath}
|
||||||
{user}
|
{user}
|
||||||
{githubService}
|
{githubService}
|
||||||
|
hasNextSibling={branches.find((b) => b.order === branch.order + 1)}
|
||||||
></BranchLane>
|
></BranchLane>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -131,8 +131,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
let isLaneCollapsed: boolean;
|
let isLaneCollapsed: boolean;
|
||||||
|
|
||||||
$: console.log('collapsed', isLaneCollapsed);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if isLaneCollapsed}
|
{#if isLaneCollapsed}
|
||||||
@ -154,7 +152,6 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="branch-card-wrapper">
|
|
||||||
<div
|
<div
|
||||||
class="branch-card"
|
class="branch-card"
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
@ -295,15 +292,14 @@
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style lang="postcss">
|
<style lang="postcss">
|
||||||
.branch-card-wrapper {
|
/* .branch-card-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
} */
|
||||||
.branch-card {
|
.branch-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -432,5 +428,10 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: var(--space-12);
|
padding: var(--space-12);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
border-right: 1px solid var(--clr-theme-container-outline-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* .brach-collapsed {
|
||||||
|
display: none;
|
||||||
|
} */
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import BranchHeaderSecondaryActions from './BranchHeaderSecondaryActions.svelte';
|
import ActiveBranchStatus from './ActiveBranchStatus.svelte';
|
||||||
import BranchLabel from './BranchLabel.svelte';
|
import BranchLabel from './BranchLabel.svelte';
|
||||||
|
import BranchLanePopupMenu from './BranchLanePopupMenu.svelte';
|
||||||
// import BranchLanePopupMenu from './BranchLanePopupMenu.svelte';
|
// import BranchLanePopupMenu from './BranchLanePopupMenu.svelte';
|
||||||
import MergeButton from './MergeButton.svelte';
|
import MergeButton from './MergeButton.svelte';
|
||||||
import Tag from './Tag.svelte';
|
import Tag from './Tag.svelte';
|
||||||
|
import { clickOutside } from '$lib/clickOutside';
|
||||||
// import { clickOutside } from '$lib/clickOutside';
|
// import { clickOutside } from '$lib/clickOutside';
|
||||||
import Button from '$lib/components/Button.svelte';
|
import Button from '$lib/components/Button.svelte';
|
||||||
import Icon, { type IconColor } from '$lib/components/Icon.svelte';
|
import Icon, { type IconColor } from '$lib/components/Icon.svelte';
|
||||||
import { normalizeBranchName } from '$lib/utils/branch';
|
// import { normalizeBranchName } from '$lib/utils/branch';
|
||||||
import * as toasts from '$lib/utils/toasts';
|
import * as toasts from '$lib/utils/toasts';
|
||||||
import { tooltip } from '$lib/utils/tooltip';
|
import { tooltip } from '$lib/utils/tooltip';
|
||||||
import { open } from '@tauri-apps/api/shell';
|
// import { open } from '@tauri-apps/api/shell';
|
||||||
import toast from 'svelte-french-toast';
|
import toast from 'svelte-french-toast';
|
||||||
import type { BranchService } from '$lib/branches/service';
|
import type { BranchService } from '$lib/branches/service';
|
||||||
import type { GitHubService } from '$lib/github/service';
|
import type { GitHubService } from '$lib/github/service';
|
||||||
@ -121,18 +123,40 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if isLaneCollapsed}
|
{#if isLaneCollapsed}
|
||||||
<div class="collapsed-lane" data-remove-from-draggable data-tauri-drag-region>
|
<div class="card collapsed-lane" data-tauri-drag-region>
|
||||||
<div class="collapsed-lane__actions">
|
<div class="collapsed-lane__actions">
|
||||||
<BranchHeaderSecondaryActions
|
<div class="collapsed-lane__draggable" data-drag-handle>
|
||||||
{visible}
|
<Icon name="draggable-narrow" />
|
||||||
{isUnapplied}
|
</div>
|
||||||
{branch}
|
<Button
|
||||||
{branchController}
|
icon="unfold-lane"
|
||||||
{projectId}
|
kind="outlined"
|
||||||
bind:isLaneCollapsed
|
color="neutral"
|
||||||
bind:meatballButton
|
help="Collapse lane"
|
||||||
|
on:click={() => {
|
||||||
|
isLaneCollapsed = false;
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="collapsed-lane__info">
|
||||||
|
<h3 class="collapsed-lane__label text-base-13 text-bold">
|
||||||
|
{branch.name}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<div class="collapsed-lane__info__details">
|
||||||
|
<ActiveBranchStatus
|
||||||
|
{base}
|
||||||
|
{branch}
|
||||||
|
{isUnapplied}
|
||||||
|
{hasIntegratedCommits}
|
||||||
|
{isLaneCollapsed}
|
||||||
|
prUrl={$pr$?.htmlUrl}
|
||||||
|
/>
|
||||||
|
{#if branch.selectedForChanges}
|
||||||
|
<Tag color="pop" filled icon="target" verticalOrientation>Default branch</Tag>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="header__wrapper">
|
<div class="header__wrapper">
|
||||||
@ -146,7 +170,14 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="header__remote-branch">
|
<div class="header__remote-branch">
|
||||||
{#if !branch.upstream}
|
<ActiveBranchStatus
|
||||||
|
{base}
|
||||||
|
{branch}
|
||||||
|
{isUnapplied}
|
||||||
|
{hasIntegratedCommits}
|
||||||
|
prUrl={$pr$?.htmlUrl}
|
||||||
|
/>
|
||||||
|
<!-- {#if !branch.upstream}
|
||||||
{#if !branch.active}
|
{#if !branch.active}
|
||||||
<Tag
|
<Tag
|
||||||
icon="virtual-branch-small"
|
icon="virtual-branch-small"
|
||||||
@ -179,14 +210,9 @@
|
|||||||
: normalizeBranchName(branch.name)}</Tag
|
: normalizeBranchName(branch.name)}</Tag
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else} -->
|
||||||
<Tag
|
{#if branch.upstream}
|
||||||
color="dark"
|
<!-- <Tag
|
||||||
icon="remote-branch-small"
|
|
||||||
help="At least some of your changes have been pushed"
|
|
||||||
reversedDirection>remote</Tag
|
|
||||||
>
|
|
||||||
<Tag
|
|
||||||
icon="open-link"
|
icon="open-link"
|
||||||
color="ghost"
|
color="ghost"
|
||||||
border
|
border
|
||||||
@ -216,7 +242,7 @@
|
|||||||
>
|
>
|
||||||
View PR
|
View PR
|
||||||
</Tag>
|
</Tag>
|
||||||
{/if}
|
{/if} -->
|
||||||
{#if prIcon}
|
{#if prIcon}
|
||||||
<div
|
<div
|
||||||
class="pr-status"
|
class="pr-status"
|
||||||
@ -355,15 +381,40 @@
|
|||||||
</Button>
|
</Button>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="header__buttons">
|
<div class="header__buttons">
|
||||||
<BranchHeaderSecondaryActions
|
<Button
|
||||||
{visible}
|
icon="fold-lane"
|
||||||
{isUnapplied}
|
kind="outlined"
|
||||||
{branch}
|
color="neutral"
|
||||||
{branchController}
|
help="Collapse lane"
|
||||||
{projectId}
|
on:click={() => {
|
||||||
bind:isLaneCollapsed
|
isLaneCollapsed = true;
|
||||||
bind:meatballButton
|
}}
|
||||||
/>
|
/>
|
||||||
|
<Button
|
||||||
|
icon="kebab"
|
||||||
|
kind="outlined"
|
||||||
|
color="neutral"
|
||||||
|
on:click={() => {
|
||||||
|
console.log('meatballButton', meatballButton);
|
||||||
|
visible = !visible;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="branch-popup-menu"
|
||||||
|
use:clickOutside={{
|
||||||
|
trigger: meatballButton,
|
||||||
|
handler: () => (visible = false)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<BranchLanePopupMenu
|
||||||
|
{branchController}
|
||||||
|
{branch}
|
||||||
|
{projectId}
|
||||||
|
{isUnapplied}
|
||||||
|
bind:visible
|
||||||
|
on:action
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@ -435,12 +486,12 @@
|
|||||||
gap: var(--space-4);
|
gap: var(--space-4);
|
||||||
}
|
}
|
||||||
.draggable {
|
.draggable {
|
||||||
|
display: flex;
|
||||||
|
cursor: grab;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: var(--space-4);
|
right: var(--space-4);
|
||||||
top: var(--space-6);
|
top: var(--space-6);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
display: flex;
|
|
||||||
cursor: grab;
|
|
||||||
color: var(--clr-theme-scale-ntrl-50);
|
color: var(--clr-theme-scale-ntrl-50);
|
||||||
transition:
|
transition:
|
||||||
opacity var(--transition-slow),
|
opacity var(--transition-slow),
|
||||||
@ -450,13 +501,13 @@
|
|||||||
color: var(--clr-theme-scale-ntrl-40);
|
color: var(--clr-theme-scale-ntrl-40);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
.branch-popup-menu {
|
.branch-popup-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(100% + var(--space-4));
|
top: calc(100% + var(--space-4));
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
} */
|
}
|
||||||
|
|
||||||
.header__remote-branch {
|
.header__remote-branch {
|
||||||
color: var(--clr-theme-scale-ntrl-50);
|
color: var(--clr-theme-scale-ntrl-50);
|
||||||
@ -472,4 +523,53 @@
|
|||||||
.pr-status {
|
.pr-status {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* COLLAPSABLE LANE */
|
||||||
|
|
||||||
|
.collapsed-lane {
|
||||||
|
user-select: none;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
gap: var(--space-16);
|
||||||
|
padding: var(--space-8) var(--space-8) var(--space-16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed-lane__actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-4);
|
||||||
|
}
|
||||||
|
.collapsed-lane__draggable {
|
||||||
|
cursor: grab;
|
||||||
|
transform: rotate(90deg);
|
||||||
|
margin-bottom: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed-lane__info {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
writing-mode: vertical-rl;
|
||||||
|
gap: var(--space-8);
|
||||||
|
/* flex-direction: column-reverse; */
|
||||||
|
/* writing-mode: vertical-rl;
|
||||||
|
background-color: aquamarine; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed-lane__info__details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsed-lane__label {
|
||||||
|
color: var(--clr-theme-scale-ntrl-0);
|
||||||
|
transform: rotate(180deg);
|
||||||
|
padding: var(--space-8) 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -18,10 +18,10 @@
|
|||||||
|
|
||||||
<div style="display: contents;">
|
<div style="display: contents;">
|
||||||
<Button
|
<Button
|
||||||
icon="fold-lane"
|
icon={isLaneCollapsed ? 'unfold-lane' : 'fold-lane'}
|
||||||
kind="outlined"
|
kind="outlined"
|
||||||
color="neutral"
|
color="neutral"
|
||||||
help="Fold this lane"
|
help={isLaneCollapsed ? 'Expand lane' : 'Collapse lane'}
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
isLaneCollapsed = !isLaneCollapsed;
|
isLaneCollapsed = !isLaneCollapsed;
|
||||||
}}
|
}}
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
export let user: User | undefined;
|
export let user: User | undefined;
|
||||||
export let projectPath: string;
|
export let projectPath: string;
|
||||||
export let githubService: GitHubService;
|
export let githubService: GitHubService;
|
||||||
|
export let hasNextSibling: Branch | undefined;
|
||||||
|
|
||||||
$: selectedOwnership = writable(Ownership.fromBranch(branch));
|
$: selectedOwnership = writable(Ownership.fromBranch(branch));
|
||||||
$: selected = setSelected($selectedFiles, branch);
|
$: selected = setSelected($selectedFiles, branch);
|
||||||
@ -60,6 +61,8 @@
|
|||||||
if (!match) $selectedFiles = [];
|
if (!match) $selectedFiles = [];
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$: console.log('hasNextSibling', hasNextSibling?.name);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -103,7 +106,9 @@
|
|||||||
selectable={$commitBoxOpen && !isUnapplied}
|
selectable={$commitBoxOpen && !isUnapplied}
|
||||||
on:close={() => {
|
on:close={() => {
|
||||||
const selectedId = selected?.id;
|
const selectedId = selected?.id;
|
||||||
selectedFiles.update((fileIds) => fileIds.filter((file) => file.id != selectedId));
|
selectedFiles.update((fileIds) =>
|
||||||
|
fileIds.filter((file) => file.id != selectedId)
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Resizer
|
<Resizer
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
export let disabled = false;
|
export let disabled = false;
|
||||||
export let clickable = false;
|
export let clickable = false;
|
||||||
export let shrinkable = false;
|
export let shrinkable = false;
|
||||||
|
export let verticalOrientation = false;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@ -41,17 +42,18 @@
|
|||||||
class:disabled
|
class:disabled
|
||||||
class:shrinkable
|
class:shrinkable
|
||||||
class:iconLeft={reversedDirection}
|
class:iconLeft={reversedDirection}
|
||||||
|
class:verticalOrientation
|
||||||
class:not-button={!clickable}
|
class:not-button={!clickable}
|
||||||
on:click
|
on:click
|
||||||
role={clickable ? 'button' : undefined}
|
role={clickable ? 'button' : undefined}
|
||||||
class:clickable
|
class:clickable
|
||||||
use:tooltip={help}
|
use:tooltip={help}
|
||||||
>
|
>
|
||||||
<span class="label">
|
<span class="label" class:verticalLabel={verticalOrientation}>
|
||||||
<slot />
|
<slot />
|
||||||
</span>
|
</span>
|
||||||
{#if icon}
|
{#if icon}
|
||||||
<div class="icon">
|
<div class="icon" class:verticalIcon={verticalOrientation}>
|
||||||
<Icon name={icon} />
|
<Icon name={icon} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@ -226,4 +228,20 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.verticalOrientation {
|
||||||
|
writing-mode: vertical-rl;
|
||||||
|
height: max-content;
|
||||||
|
width: var(--size-btn-s);
|
||||||
|
padding: var(--space-4) var(--space-2);
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.verticalIcon {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.verticalLabel {
|
||||||
|
padding: var(--space-2) 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user