tag component update, collapsable branches frontend added

This commit is contained in:
Pavel Laptev 2024-02-09 23:18:10 +01:00 committed by GitButler
parent c6bc57cb90
commit e107f6b00c
7 changed files with 397 additions and 179 deletions

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

View File

@ -121,6 +121,7 @@
{projectPath}
{user}
{githubService}
hasNextSibling={branches.find((b) => b.order === branch.order + 1)}
></BranchLane>
</div>
{/each}

View File

@ -131,8 +131,6 @@
}
let isLaneCollapsed: boolean;
$: console.log('collapsed', isLaneCollapsed);
</script>
{#if isLaneCollapsed}
@ -154,156 +152,154 @@
/>
</div>
{:else}
<div class="branch-card-wrapper">
<div
class="branch-card"
data-tauri-drag-region
class:target-branch={branch.active && branch.selectedForChanges}
>
<div
class="branch-card"
data-tauri-drag-region
class:target-branch={branch.active && branch.selectedForChanges}
bind:this={rsViewport}
style:width={`${laneWidth || $defaultBranchWidthRem}rem`}
class="branch-card__contents"
>
<div
bind:this={rsViewport}
style:width={`${laneWidth || $defaultBranchWidthRem}rem`}
class="branch-card__contents"
>
<BranchHeader
{isUnapplied}
{branchController}
{branch}
{base}
{githubService}
{branchService}
bind:isLaneCollapsed
projectId={project.id}
on:action={(e) => {
if (e.detail == 'generate-branch-name') {
generateBranchName();
}
}}
/>
<!-- DROPZONES -->
<DropzoneOverlay class="cherrypick-dz-marker" label="Apply here" />
<DropzoneOverlay class="lane-dz-marker" label="Move here" />
<div
class="branch-card__dropzone-wrapper"
use:dropzone={{
hover: 'cherrypick-dz-hover',
active: 'cherrypick-dz-active',
accepts: acceptCherrypick,
onDrop: onCherrypicked,
disabled: isUnapplied
}}
use:dropzone={{
hover: 'lane-dz-hover',
active: 'lane-dz-active',
accepts: acceptBranchDrop,
onDrop: onBranchDrop,
disabled: isUnapplied
}}
>
<DropzoneOverlay class="cherrypick-dz-marker" label="Apply here" />
<DropzoneOverlay class="lane-dz-marker" label="Move here" />
{#if branch.files?.length > 0}
<div class="card">
<BranchFiles
{branch}
{isUnapplied}
{selectedOwnership}
{selectedFiles}
showCheckboxes={$commitBoxOpen}
/>
{#if branch.active}
<CommitDialog
projectId={project.id}
{branchController}
{branch}
{cloud}
{selectedOwnership}
{user}
bind:expanded={commitBoxOpen}
on:action={(e) => {
if (e.detail == 'generate-branch-name') {
generateBranchName();
}
}}
/>
{/if}
</div>
{:else if branch.commits.length == 0}
<div class="new-branch card" data-dnd-ignore>
<div class="new-branch__content">
<div class="new-branch__image">
<ImgThemed
imgSet={{
light: '/images/lane-new-light.webp',
dark: '/images/lane-new-dark.webp'
}}
/>
</div>
<h2 class="new-branch__title text-base-body-15 text-semibold">
This is a new branch.
</h2>
<p class="new-branch__caption text-base-body-13">
You can drag and drop files or parts of files here.
</p>
</div>
</div>
{:else}
<!-- attention: these markers have custom css at the bottom of thise file -->
<div class="no-changes card" data-dnd-ignore>
<div class="new-branch__content">
<div class="new-branch__image">
<ImgThemed
imgSet={{
light: '/images/lane-no-changes-light.webp',
dark: '/images/lane-no-changes-dark.webp'
}}
/>
</div>
<h2 class="new-branch__caption text-base-body-13">
No uncommitted changes<br />on this branch
</h2>
</div>
</div>
{/if}
</div>
<BranchCommits
{base}
{branch}
{project}
{githubService}
{branchController}
{branchService}
{branchCount}
{isUnapplied}
{selectedFiles}
/>
</div>
</div>
<div class="divider-line">
<Resizer
viewport={rsViewport}
direction="right"
inside={$selectedFiles.length > 0}
minWidth={320}
sticky
on:width={(e) => {
laneWidth = e.detail / (16 * $userSettings.zoom);
lscache.set(laneWidthKey + branch.id, laneWidth, 7 * 1440); // 7 day ttl
$defaultBranchWidthRem = laneWidth;
<BranchHeader
{isUnapplied}
{branchController}
{branch}
{base}
{githubService}
{branchService}
bind:isLaneCollapsed
projectId={project.id}
on:action={(e) => {
if (e.detail == 'generate-branch-name') {
generateBranchName();
}
}}
/>
<!-- DROPZONES -->
<DropzoneOverlay class="cherrypick-dz-marker" label="Apply here" />
<DropzoneOverlay class="lane-dz-marker" label="Move here" />
<div
class="branch-card__dropzone-wrapper"
use:dropzone={{
hover: 'cherrypick-dz-hover',
active: 'cherrypick-dz-active',
accepts: acceptCherrypick,
onDrop: onCherrypicked,
disabled: isUnapplied
}}
use:dropzone={{
hover: 'lane-dz-hover',
active: 'lane-dz-active',
accepts: acceptBranchDrop,
onDrop: onBranchDrop,
disabled: isUnapplied
}}
>
<DropzoneOverlay class="cherrypick-dz-marker" label="Apply here" />
<DropzoneOverlay class="lane-dz-marker" label="Move here" />
{#if branch.files?.length > 0}
<div class="card">
<BranchFiles
{branch}
{isUnapplied}
{selectedOwnership}
{selectedFiles}
showCheckboxes={$commitBoxOpen}
/>
{#if branch.active}
<CommitDialog
projectId={project.id}
{branchController}
{branch}
{cloud}
{selectedOwnership}
{user}
bind:expanded={commitBoxOpen}
on:action={(e) => {
if (e.detail == 'generate-branch-name') {
generateBranchName();
}
}}
/>
{/if}
</div>
{:else if branch.commits.length == 0}
<div class="new-branch card" data-dnd-ignore>
<div class="new-branch__content">
<div class="new-branch__image">
<ImgThemed
imgSet={{
light: '/images/lane-new-light.webp',
dark: '/images/lane-new-dark.webp'
}}
/>
</div>
<h2 class="new-branch__title text-base-body-15 text-semibold">
This is a new branch.
</h2>
<p class="new-branch__caption text-base-body-13">
You can drag and drop files or parts of files here.
</p>
</div>
</div>
{:else}
<!-- attention: these markers have custom css at the bottom of thise file -->
<div class="no-changes card" data-dnd-ignore>
<div class="new-branch__content">
<div class="new-branch__image">
<ImgThemed
imgSet={{
light: '/images/lane-no-changes-light.webp',
dark: '/images/lane-no-changes-dark.webp'
}}
/>
</div>
<h2 class="new-branch__caption text-base-body-13">
No uncommitted changes<br />on this branch
</h2>
</div>
</div>
{/if}
</div>
<BranchCommits
{base}
{branch}
{project}
{githubService}
{branchController}
{branchService}
{branchCount}
{isUnapplied}
{selectedFiles}
/>
</div>
</div>
<div class="divider-line">
<Resizer
viewport={rsViewport}
direction="right"
inside={$selectedFiles.length > 0}
minWidth={320}
sticky
on:width={(e) => {
laneWidth = e.detail / (16 * $userSettings.zoom);
lscache.set(laneWidthKey + branch.id, laneWidth, 7 * 1440); // 7 day ttl
$defaultBranchWidthRem = laneWidth;
}}
/>
</div>
{/if}
<style lang="postcss">
.branch-card-wrapper {
/* .branch-card-wrapper {
position: relative;
display: flex;
height: 100%;
}
} */
.branch-card {
height: 100%;
position: relative;
@ -432,5 +428,10 @@
flex-direction: column;
padding: var(--space-12);
height: 100%;
border-right: 1px solid var(--clr-theme-container-outline-light);
}
/* .brach-collapsed {
display: none;
} */
</style>

View File

@ -1,16 +1,18 @@
<script lang="ts">
import BranchHeaderSecondaryActions from './BranchHeaderSecondaryActions.svelte';
import ActiveBranchStatus from './ActiveBranchStatus.svelte';
import BranchLabel from './BranchLabel.svelte';
import BranchLanePopupMenu from './BranchLanePopupMenu.svelte';
// import BranchLanePopupMenu from './BranchLanePopupMenu.svelte';
import MergeButton from './MergeButton.svelte';
import Tag from './Tag.svelte';
import { clickOutside } from '$lib/clickOutside';
// import { clickOutside } from '$lib/clickOutside';
import Button from '$lib/components/Button.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 { 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 type { BranchService } from '$lib/branches/service';
import type { GitHubService } from '$lib/github/service';
@ -121,18 +123,40 @@
</script>
{#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">
<BranchHeaderSecondaryActions
{visible}
{isUnapplied}
{branch}
{branchController}
{projectId}
bind:isLaneCollapsed
bind:meatballButton
<div class="collapsed-lane__draggable" data-drag-handle>
<Icon name="draggable-narrow" />
</div>
<Button
icon="unfold-lane"
kind="outlined"
color="neutral"
help="Collapse lane"
on:click={() => {
isLaneCollapsed = false;
}}
/>
</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>
{:else}
<div class="header__wrapper">
@ -146,7 +170,14 @@
/>
</div>
<div class="header__remote-branch">
{#if !branch.upstream}
<ActiveBranchStatus
{base}
{branch}
{isUnapplied}
{hasIntegratedCommits}
prUrl={$pr$?.htmlUrl}
/>
<!-- {#if !branch.upstream}
{#if !branch.active}
<Tag
icon="virtual-branch-small"
@ -179,14 +210,9 @@
: normalizeBranchName(branch.name)}</Tag
>
{/if}
{:else}
<Tag
color="dark"
icon="remote-branch-small"
help="At least some of your changes have been pushed"
reversedDirection>remote</Tag
>
<Tag
{:else} -->
{#if branch.upstream}
<!-- <Tag
icon="open-link"
color="ghost"
border
@ -216,7 +242,7 @@
>
View PR
</Tag>
{/if}
{/if} -->
{#if prIcon}
<div
class="pr-status"
@ -355,15 +381,40 @@
</Button>
{:else}
<div class="header__buttons">
<BranchHeaderSecondaryActions
{visible}
{isUnapplied}
{branch}
{branchController}
{projectId}
bind:isLaneCollapsed
bind:meatballButton
<Button
icon="fold-lane"
kind="outlined"
color="neutral"
help="Collapse lane"
on:click={() => {
isLaneCollapsed = true;
}}
/>
<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>
{/if}
</div>
@ -435,12 +486,12 @@
gap: var(--space-4);
}
.draggable {
display: flex;
cursor: grab;
position: absolute;
right: var(--space-4);
top: var(--space-6);
opacity: 0;
display: flex;
cursor: grab;
color: var(--clr-theme-scale-ntrl-50);
transition:
opacity var(--transition-slow),
@ -450,13 +501,13 @@
color: var(--clr-theme-scale-ntrl-40);
}
}
/*
.branch-popup-menu {
position: absolute;
top: calc(100% + var(--space-4));
right: 0;
z-index: 10;
} */
}
.header__remote-branch {
color: var(--clr-theme-scale-ntrl-50);
@ -472,4 +523,53 @@
.pr-status {
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>

View File

@ -18,10 +18,10 @@
<div style="display: contents;">
<Button
icon="fold-lane"
icon={isLaneCollapsed ? 'unfold-lane' : 'fold-lane'}
kind="outlined"
color="neutral"
help="Fold this lane"
help={isLaneCollapsed ? 'Expand lane' : 'Collapse lane'}
on:click={() => {
isLaneCollapsed = !isLaneCollapsed;
}}

View File

@ -34,6 +34,7 @@
export let user: User | undefined;
export let projectPath: string;
export let githubService: GitHubService;
export let hasNextSibling: Branch | undefined;
$: selectedOwnership = writable(Ownership.fromBranch(branch));
$: selected = setSelected($selectedFiles, branch);
@ -60,6 +61,8 @@
if (!match) $selectedFiles = [];
return match;
}
$: console.log('hasNextSibling', hasNextSibling?.name);
</script>
<div
@ -103,7 +106,9 @@
selectable={$commitBoxOpen && !isUnapplied}
on:close={() => {
const selectedId = selected?.id;
selectedFiles.update((fileIds) => fileIds.filter((file) => file.id != selectedId));
selectedFiles.update((fileIds) =>
fileIds.filter((file) => file.id != selectedId)
);
}}
/>
<Resizer

View File

@ -24,6 +24,7 @@
export let disabled = false;
export let clickable = false;
export let shrinkable = false;
export let verticalOrientation = false;
</script>
<div
@ -41,17 +42,18 @@
class:disabled
class:shrinkable
class:iconLeft={reversedDirection}
class:verticalOrientation
class:not-button={!clickable}
on:click
role={clickable ? 'button' : undefined}
class:clickable
use:tooltip={help}
>
<span class="label">
<span class="label" class:verticalLabel={verticalOrientation}>
<slot />
</span>
{#if icon}
<div class="icon">
<div class="icon" class:verticalIcon={verticalOrientation}>
<Icon name={icon} />
</div>
{/if}
@ -226,4 +228,20 @@
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>