Updated branch switch on the Settings page

- copy updated
- layout updated
- select component max-height fixed
This commit is contained in:
Pavel Laptev 2024-05-04 00:41:15 +02:00
parent fb6dd2429e
commit c0cc85688f
6 changed files with 134 additions and 148 deletions

View File

@ -75,6 +75,8 @@
e.stopPropagation(); e.stopPropagation();
}} }}
> >
{isLaneCollapsed ? 'View branch' : `${$baseBranch.actualPushRemoteName()}/${$branch.upstreamName}`} {isLaneCollapsed
? 'View branch'
: `${$baseBranch.actualPushRemoteName()}/${$branch.upstreamName}`}
</Tag> </Tag>
{/if} {/if}

View File

@ -27,60 +27,57 @@
} }
let isSwitching = false; let isSwitching = false;
// Fetch the remote branches reactively
let remoteBranchesPromise: Promise<
{
name: string;
}[]
>;
$: {
if (project) {
remoteBranchesPromise = getRemoteBranches(project.id);
}
}
function uniqueRemotes(remoteBranches: { name: string }[]) { function uniqueRemotes(remoteBranches: { name: string }[]) {
return Array.from(new Set(remoteBranches.map((b) => b.name.split('/')[0]))).map((r) => ({ name: r })); return Array.from(new Set(remoteBranches.map((b) => b.name.split('/')[0]))).map((r) => ({
name: r
}));
} }
async function onSetBaseBranchClick() { async function onSetBaseBranchClick() {
if (!selectedBranch) return; if (!selectedBranch) return;
// while target is setting, display loading isSwitching = true; // Indicate switching in progress
isSwitching = true;
if (selectedRemote) { if (selectedRemote) {
await branchController await branchController.setTarget(selectedBranch.name, selectedRemote.name).finally(() => {
.setTarget(selectedBranch.name, selectedRemote.name)
.finally(() => {
isSwitching = false; isSwitching = false;
}); });
} else { } else {
await branchController await branchController.setTarget(selectedBranch.name).finally(() => {
.setTarget(selectedBranch.name)
.finally(() => {
isSwitching = false; isSwitching = false;
}); });
} }
} }
</script> </script>
{#await remoteBranchesPromise}
<InfoMessage filled outlined={false} icon="info">
<svelte:fragment slot="content">Loading remote branches...</svelte:fragment>
</InfoMessage>
{:then remoteBranches}
{#if remoteBranches.length > 0}
<Section spacer> <Section spacer>
<SectionCard labelFor="targetBranch" orientation="column"> <SectionCard>
<svelte:fragment slot="title">Current Target Branch</svelte:fragment> <svelte:fragment slot="title">Remote configuration</svelte:fragment>
<svelte:fragment slot="caption"> <svelte:fragment slot="caption">
Your target branch is what you consider "production". This is where you Lets you choose where to push code and set the target branch for contributions. The target
want to integrate any branches that you create. Normally something like branch is usually the "production" branch like 'origin/master' or 'upstream/main.' This
'origin/master' or 'upstream/main'. section helps ensure your code goes to the correct remote and branch for integration.
</svelte:fragment> </svelte:fragment>
<div class="inputs-group">
{#if isSwitching}
<InfoMessage filled outlined={false} style="pop" icon="info">
<svelte:fragment slot="title">
Switching target branch...
</svelte:fragment>
</InfoMessage>
{:else}
{#await getRemoteBranches(project.id)}
loading remote branches...
{:then remoteBranches}
{#if remoteBranches.length == 0}
<InfoMessage filled outlined={false} style="error" icon="error">
<svelte:fragment slot="title">
You don't have any remote branches.
</svelte:fragment>
</InfoMessage>
{:else}
<div class="inputs-row">
<Select <Select
items={remoteBranches} items={remoteBranches}
bind:value={selectedBranch} bind:value={selectedBranch}
@ -88,32 +85,21 @@
labelId="name" labelId="name"
disabled={targetChangeDisabled} disabled={targetChangeDisabled}
wide={true} wide={true}
label="Current target branch"
> >
<SelectItem slot="template" let:item let:selected {selected}> <SelectItem slot="template" let:item let:selected {selected}>
{item.name} {item.name}
</SelectItem> </SelectItem>
</Select> </Select>
<Button
size="cta"
style="ghost"
kind="solid"
on:click={onSetBaseBranchClick}
id="set-base-branch"
loading={isSwitching}
disabled={(selectedBranch.name === $baseBranch.branchName) || targetChangeDisabled}
>
Change Target Branch
</Button>
</div>
{#if uniqueRemotes(remoteBranches).length > 1} {#if uniqueRemotes(remoteBranches).length > 1}
Create branches on remote:
<Select <Select
items={uniqueRemotes(remoteBranches)} items={uniqueRemotes(remoteBranches)}
bind:value={selectedRemote} bind:value={selectedRemote}
itemId="name" itemId="name"
labelId="name" labelId="name"
disabled={targetChangeDisabled} disabled={targetChangeDisabled}
label="Create branches on remote"
> >
<SelectItem slot="template" let:item let:selected {selected}> <SelectItem slot="template" let:item let:selected {selected}>
{item.name} {item.name}
@ -121,16 +107,6 @@
</Select> </Select>
{/if} {/if}
{/if}
{:catch}
<InfoMessage filled outlined={true} style="error" icon="error">
<svelte:fragment slot="title">
We got an error trying to list your remote branches
</svelte:fragment>
</InfoMessage>
{/await}
{/if}
{#if $activeBranches && targetChangeDisabled} {#if $activeBranches && targetChangeDisabled}
<InfoMessage filled outlined={false} icon="info"> <InfoMessage filled outlined={false} icon="info">
<svelte:fragment slot="content"> <svelte:fragment slot="content">
@ -140,22 +116,26 @@
before switching the base branch. before switching the base branch.
</svelte:fragment> </svelte:fragment>
</InfoMessage> </InfoMessage>
{:else}
<Button
size="cta"
style="ghost"
kind="solid"
on:click={onSetBaseBranchClick}
id="set-base-branch"
loading={isSwitching}
disabled={selectedBranch.name === $baseBranch.branchName || targetChangeDisabled}
>
{isSwitching ? 'Switching branches...' : 'Update configuration'}
</Button>
{/if} {/if}
</div>
</SectionCard> </SectionCard>
</Section> </Section>
{/if}
<style> {:catch}
.inputs-group { <InfoMessage filled outlined={true} style="error" icon="error">
display: flex; <svelte:fragment slot="title"
flex-direction: column; >We got an error trying to list your remote branches</svelte:fragment
gap: var(--size-16); >
width: 100%; </InfoMessage>
} {/await}
.inputs-row {
display: flex;
justify-content: space-between;
gap: var(--size-16);
}
</style>

View File

@ -26,7 +26,6 @@
let dragHandle: any; let dragHandle: any;
let clone: any; let clone: any;
let isSwitching = false;
</script> </script>
{#if $activeBranchesError} {#if $activeBranchesError}

View File

@ -36,7 +36,7 @@
let loading = false; let loading = false;
let selectedBranch = getBestBranch(remoteBranches); let selectedBranch = getBestBranch(remoteBranches);
function getBestBranch(branches: { name: string; }[]): { name: string } { function getBestBranch(branches: { name: string }[]): { name: string } {
// Function to calculate the rank of a branch // Function to calculate the rank of a branch
// eslint-disable-next-line func-style // eslint-disable-next-line func-style
const calculateRank = (branch: string): number => { const calculateRank = (branch: string): number => {
@ -64,7 +64,10 @@
// split all the branches by the first '/' and gather the unique remote names // split all the branches by the first '/' and gather the unique remote names
// then turn remotes into an array of objects with a 'name' and 'value' key // then turn remotes into an array of objects with a 'name' and 'value' key
let remotes = Array.from(new Set(remoteBranches.map((b) => b.name.split('/')[0]))).map((r) => ({ name: r, value: r })); let remotes = Array.from(new Set(remoteBranches.map((b) => b.name.split('/')[0]))).map((r) => ({
name: r,
value: r
}));
let selectedRemote = remotes[0]; let selectedRemote = remotes[0];
// if there's an 'origin', select it by default // if there's an 'origin', select it by default
@ -95,8 +98,8 @@
</Select> </Select>
{#if remotes.length > 1} {#if remotes.length > 1}
<p class="text-base-body-12"> <p class="text-base-body-12">
You have branches from multiple remotes. If you want to specify a push target for You have branches from multiple remotes. If you want to specify a push target for creating
creating branches that is different from your production branch, change it here. branches that is different from your production branch, change it here.
</p> </p>
<Select items={remotes} bind:value={selectedRemote} itemId="name" labelId="name"> <Select items={remotes} bind:value={selectedRemote} itemId="name" labelId="name">
<SelectItem slot="template" let:item let:selected {selected}> <SelectItem slot="template" let:item let:selected {selected}>

View File

@ -2,6 +2,7 @@
import ScrollableContainer from './ScrollableContainer.svelte'; import ScrollableContainer from './ScrollableContainer.svelte';
import TextBox from './TextBox.svelte'; import TextBox from './TextBox.svelte';
import { clickOutside } from '$lib/clickOutside'; import { clickOutside } from '$lib/clickOutside';
import { pxToRem } from '$lib/utils/pxToRem';
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
export let id: undefined | string = undefined; export let id: undefined | string = undefined;
@ -15,6 +16,7 @@
export let value: any = undefined; export let value: any = undefined;
export let selectedItemId: any = undefined; export let selectedItemId: any = undefined;
export let placeholder = ''; export let placeholder = '';
export let maxHeight: number | undefined = 260;
$: if (selectedItemId) value = items.find((item) => item[itemId] == selectedItemId); $: if (selectedItemId) value = items.find((item) => item[itemId] == selectedItemId);
@ -25,7 +27,6 @@
let listOpen = false; let listOpen = false;
let element: HTMLElement; let element: HTMLElement;
let options: HTMLDivElement; let options: HTMLDivElement;
let maxHeight = 200;
function handleItemClick(item: any) { function handleItemClick(item: any) {
if (item?.selectable === false) return; if (item?.selectable === false) return;
@ -35,6 +36,7 @@
listOpen = false; listOpen = false;
} }
function setMaxHeight() { function setMaxHeight() {
if (maxHeight) return;
maxHeight = window.innerHeight - element.getBoundingClientRect().bottom - maxPadding; maxHeight = window.innerHeight - element.getBoundingClientRect().bottom - maxPadding;
} }
@ -73,7 +75,7 @@
class="options card" class="options card"
style:display={listOpen ? undefined : 'none'} style:display={listOpen ? undefined : 'none'}
bind:this={options} bind:this={options}
style:max-height={`${maxHeight}px`} style:max-height={maxHeight && pxToRem(maxHeight)}
use:clickOutside={{ use:clickOutside={{
trigger: element, trigger: element,
handler: () => (listOpen = !listOpen), handler: () => (listOpen = !listOpen),

View File

@ -1,11 +1,12 @@
<script lang="ts"> <script lang="ts">
import Spacer from '../Spacer.svelte'; import Spacer from '../Spacer.svelte';
export let spacer = false; export let spacer = false;
export let gap = 'var(--size-20)';
const SLOTS = $$props.$$slots; const SLOTS = $$props.$$slots;
</script> </script>
<div class="settings-section"> <div class="settings-section" style="gap: {gap}">
{#if SLOTS.top} {#if SLOTS.top}
<slot name="top" /> <slot name="top" />
{/if} {/if}
@ -36,7 +37,6 @@
.settings-section { .settings-section {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--size-20);
} }
.description { .description {