diff --git a/packages/ui/src/lib/components/SegmentControl/Segment.svelte b/packages/ui/src/lib/components/SegmentControl/Segment.svelte
new file mode 100644
index 000000000..2af14996b
--- /dev/null
+++ b/packages/ui/src/lib/components/SegmentControl/Segment.svelte
@@ -0,0 +1,104 @@
+
+
+
+
+
diff --git a/packages/ui/src/lib/components/SegmentControl/SegmentedControl.svelte b/packages/ui/src/lib/components/SegmentControl/SegmentedControl.svelte
new file mode 100644
index 000000000..5fb3c962a
--- /dev/null
+++ b/packages/ui/src/lib/components/SegmentControl/SegmentedControl.svelte
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
diff --git a/packages/ui/src/lib/components/SegmentControl/segment.ts b/packages/ui/src/lib/components/SegmentControl/segment.ts
new file mode 100644
index 000000000..00d8003a7
--- /dev/null
+++ b/packages/ui/src/lib/components/SegmentControl/segment.ts
@@ -0,0 +1,15 @@
+import type { Writable } from 'svelte/store';
+
+export interface SegmentItem {
+ id: string;
+ index: number;
+ disabled: boolean;
+}
+export interface SegmentContext {
+ focusedSegmentIndex: Writable;
+ selectedSegmentIndex: Writable;
+ length: Writable;
+ setIndex(): number;
+ addSegment(segment: SegmentItem): void;
+ setSelected(index: number): void;
+}
diff --git a/packages/ui/src/lib/components/TextBox.svelte b/packages/ui/src/lib/components/TextBox.svelte
new file mode 100644
index 000000000..f45ea294c
--- /dev/null
+++ b/packages/ui/src/lib/components/TextBox.svelte
@@ -0,0 +1,59 @@
+
+
+
+ {#if icon && iconPosition == 'left'}
+
+ {/if}
+ dispatch('input', e.currentTarget.value)}
+ />
+ {#if icon && iconPosition == 'right'}
+
+ {/if}
+
+
+
diff --git a/packages/ui/src/routes/[projectId]/navigation/BranchFilter.svelte b/packages/ui/src/routes/[projectId]/navigation/BranchFilter.svelte
index 3e345bfaa..ab49713c1 100644
--- a/packages/ui/src/routes/[projectId]/navigation/BranchFilter.svelte
+++ b/packages/ui/src/routes/[projectId]/navigation/BranchFilter.svelte
@@ -1,11 +1,45 @@
-
-
-
-
+
+
+
+
textFilter$.next(e.detail)} />
+
+ onSelect(e.detail)} wide selectedIndex={0}>
+ {#each options as option}
+ {option.name}
+ {/each}
+
+
+
+
diff --git a/packages/ui/src/routes/[projectId]/navigation/Branches.svelte b/packages/ui/src/routes/[projectId]/navigation/Branches.svelte
index 3c02a1708..b1a6168e8 100644
--- a/packages/ui/src/routes/[projectId]/navigation/Branches.svelte
+++ b/packages/ui/src/routes/[projectId]/navigation/Branches.svelte
@@ -8,6 +8,9 @@
import { SETTINGS_CONTEXT, type SettingsStore } from '$lib/settings/userSettings';
import SectionHeader from './SectionHeader.svelte';
import { accordion } from './accordion';
+ import BranchFilter, { type TypeFilter } from './BranchFilter.svelte';
+ import { BehaviorSubject, combineLatest } from 'rxjs';
+ import type { CombinedBranch } from '$lib/branches/types';
const userSettings = getContext
(SETTINGS_CONTEXT);
@@ -15,7 +18,14 @@
export let projectId: string;
export let expanded = false;
+ export const textFilter$ = new BehaviorSubject(undefined);
+ export const typeFilter$ = new BehaviorSubject('all');
+
$: branches$ = branchService.branches$;
+ $: filteredBranches$ = combineLatest(
+ [branchService.branches$, typeFilter$, textFilter$],
+ (branches, type, search) => searchFilter(typeFilter(branches, type), search)
+ );
let viewport: HTMLElement;
let contents: HTMLElement;
@@ -24,6 +34,22 @@
const onScroll: UIEventHandler = (e) => {
scrolled = e.currentTarget.scrollTop != 0;
};
+
+ function typeFilter(branches: CombinedBranch[], type: TypeFilter): CombinedBranch[] {
+ switch (type) {
+ case 'all':
+ return branches;
+ case 'branch':
+ return branches.filter((b) => b.branch && !b.pr);
+ case 'pr':
+ return branches.filter((b) => b.pr);
+ }
+ }
+
+ function searchFilter(branches: CombinedBranch[], search: string | undefined) {
+ if (search == undefined) return branches;
+ return branches.filter((b) => b.displayName.includes(search));
+ }
{#if expanded}
@@ -50,9 +76,10 @@
style:height={`${$userSettings.vbranchExpandableHeight}px`}
>