Skip to content

Commit 2482773

Browse files
committed
Move VirtualList into ui package and add to storybook
1 parent 46ad2ee commit 2482773

File tree

9 files changed

+87
-14
lines changed

9 files changed

+87
-14
lines changed

apps/desktop/src/components/TargetCommitList.svelte

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
import BranchHeaderIcon from '$components/BranchHeaderIcon.svelte';
33
import CommitRow from '$components/CommitRow.svelte';
44
import ReduxResult from '$components/ReduxResult.svelte';
5-
import VirtualList from '$components/VirtualList.svelte';
65
import { BASE_BRANCH_SERVICE } from '$lib/baseBranch/baseBranchService.svelte';
6+
import { SETTINGS } from '$lib/settings/userSettings';
77
import { STACK_SERVICE } from '$lib/stacks/stackService.svelte';
88
import { UI_STATE } from '$lib/state/uiState.svelte';
99
import { inject } from '@gitbutler/core/context';
1010
1111
import { TestId, TimeAgo } from '@gitbutler/ui';
12+
import VirtualList from '@gitbutler/ui/components/VirtualList.svelte';
1213
import { getColorFromBranchType } from '@gitbutler/ui/utils/getColorFromBranchType';
1314
import { onMount } from 'svelte';
1415
import type { Commit } from '$lib/branches/v3';
@@ -22,6 +23,7 @@
2223
const uiState = inject(UI_STATE);
2324
const baseBranchService = inject(BASE_BRANCH_SERVICE);
2425
const stackService = inject(STACK_SERVICE);
26+
const userSettings = inject(SETTINGS);
2527
2628
const projectState = $derived(uiState.project(projectId));
2729
const branchesState = $derived(projectState.branchesSelection);
@@ -91,7 +93,12 @@
9193
</div>
9294
</div>
9395
</div>
94-
<VirtualList items={commits} batchSize={10} onloadmore={async () => await loadMore()}>
96+
<VirtualList
97+
items={commits}
98+
batchSize={10}
99+
visibility={$userSettings.scrollbarVisibilityState}
100+
onloadmore={async () => await loadMore()}
101+
>
95102
{#snippet chunkTemplate(commits)}
96103
{#each commits as commit}
97104
{@const selected = commit.id === branchesState?.current.commitId}

apps/desktop/src/components/codegen/CodegenMessages.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import { goto } from '$app/navigation';
33
import BranchHeaderIcon from '$components/BranchHeaderIcon.svelte';
44
import ReduxResult from '$components/ReduxResult.svelte';
5-
import VirtualList from '$components/VirtualList.svelte';
65
76
import ClaudeCodeSettingsModal from '$components/codegen/ClaudeCodeSettingsModal.svelte';
87
import CodegenChatClaudeNotAvaliableBanner from '$components/codegen/CodegenChatClaudeNotAvaliableBanner.svelte';
@@ -54,6 +53,7 @@
5453
Tooltip
5554
} from '@gitbutler/ui';
5655
56+
import VirtualList from '@gitbutler/ui/components/VirtualList.svelte';
5757
import { getColorFromBranchType } from '@gitbutler/ui/utils/getColorFromBranchType';
5858
import type { ClaudeMessage, ThinkingLevel, ModelType, PermissionMode } from '$lib/codegen/types';
5959
@@ -468,6 +468,7 @@
468468
initialPosition="bottom"
469469
items={formattedMessages}
470470
batchSize={1}
471+
visibility={$userSettings.scrollbarVisibilityState}
471472
padding={{ left: 20, right: 20 }}
472473
>
473474
{#snippet chunkTemplate(messages)}

apps/desktop/src/components/projectSettings/AppearanceSettings.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
stagingBehaviorFeature,
66
type StagingBehavior
77
} from '$lib/config/uiFeatureFlags';
8-
import { SETTINGS, type ScrollbarVisilitySettings } from '$lib/settings/userSettings';
8+
import { SETTINGS } from '$lib/settings/userSettings';
99
import { inject } from '@gitbutler/core/context';
1010
import {
1111
HunkDiff,
@@ -16,6 +16,7 @@
1616
Textbox,
1717
Toggle
1818
} from '@gitbutler/ui';
19+
import type { ScrollbarVisilitySettings } from '@gitbutler/ui/components/scroll/Scrollbar.svelte';
1920
2021
const userSettings = inject(SETTINGS);
2122
const diff = `@@ -56,10 +56,10 @@

apps/desktop/src/lib/settings/userSettings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { InjectionToken } from '@gitbutler/core/context';
2+
import { type ScrollbarVisilitySettings } from '@gitbutler/ui';
23
import { get, writable, type Writable } from 'svelte/store';
34

45
const SETTINGS_KEY = 'settings-json';
56
export const SETTINGS = new InjectionToken<Writable<Settings>>('Settings');
67

7-
export type ScrollbarVisilitySettings = 'scroll' | 'hover' | 'always';
88
export type CodeEditorSettings = {
99
schemeIdentifer: string;
1010
displayName: string;

packages/ui/.storybook/preview.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Preview } from '@storybook/sveltekit';
2+
import '@gitbutler/design-core/utility';
23
import '@gitbutler/design-core/core';
34
import '../src/styles/main.css';
45
import './stories-styles.css';

apps/desktop/src/components/VirtualList.svelte renamed to packages/ui/src/lib/components/VirtualList.svelte

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,19 @@
1414
</script>
1515

1616
<script lang="ts" generics="T">
17-
import { SETTINGS } from '$lib/settings/userSettings';
18-
import { chunk } from '$lib/utils/array';
17+
import Button from '$components/Button.svelte';
18+
import ScrollableContainer from '$components/scroll/ScrollableContainer.svelte';
19+
1920
import { debounce } from '$lib/utils/debounce';
20-
import { inject } from '@gitbutler/core/context';
21-
import { Button, ScrollableContainer } from '@gitbutler/ui';
2221
2322
import { tick, untrack, type Snippet } from 'svelte';
2423
import { fade } from 'svelte/transition';
24+
import type { ScrollbarVisilitySettings } from '$components/scroll/Scrollbar.svelte';
2525
2626
type Props = {
2727
items: Array<T>;
28+
/** Items that are always included. */
29+
children?: Snippet<[]>;
2830
/** Template for group of items. */
2931
chunkTemplate: Snippet<[T[]]>;
3032
/** Number of items grouped together. */
@@ -36,6 +38,7 @@
3638
initialPosition?: 'top' | 'bottom';
3739
/** Auto-scroll to bottom when new items are added (useful for chat). */
3840
stickToBottom?: boolean;
41+
visibility: ScrollbarVisilitySettings;
3942
padding?: {
4043
left?: number;
4144
right?: number;
@@ -44,17 +47,17 @@
4447
4548
const {
4649
items,
50+
children,
4751
chunkTemplate,
4852
batchSize,
4953
onloadmore,
5054
grow,
5155
padding,
56+
visibility,
5257
initialPosition = 'top',
5358
stickToBottom = false
5459
}: Props = $props();
5560
56-
const userSettings = inject(SETTINGS);
57-
5861
// Constants
5962
const STICKY_DISTANCE = 100;
6063
const FALLBACK_HEIGHT = 65;
@@ -96,6 +99,12 @@
9699
chunks.slice(start, end).map((data, i) => ({ id: i + start, data }))
97100
);
98101
102+
function chunk<T>(arr: T[], size: number) {
103+
return Array.from({ length: Math.ceil(arr.length / size) }, (_v, i) =>
104+
arr.slice(i * size, i * size + size)
105+
);
106+
}
107+
99108
function sumHeights(startIndex: number, endIndex: number): number {
100109
let sum = 0;
101110
for (let i = startIndex; i < endIndex; i++) {
@@ -349,12 +358,12 @@
349358
<ScrollableContainer
350359
bind:viewportHeight
351360
bind:viewport
352-
whenToShow={$userSettings.scrollbarVisibilityState}
353361
onscroll={() => {
354362
recalculate(true);
355363
checkIfNearBottom();
356364
}}
357365
wide={grow}
366+
whenToShow={visibility}
358367
{padding}
359368
>
360369
<div
@@ -369,6 +378,7 @@
369378
{@render chunkTemplate?.(chunk.data)}
370379
</div>
371380
{/each}
381+
{@render children?.()}
372382
</div>
373383
</ScrollableContainer>
374384

packages/ui/src/lib/components/scroll/Scrollbar.svelte

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
bottom?: number;
66
left?: number;
77
};
8+
9+
export type ScrollbarVisilitySettings = 'scroll' | 'hover' | 'always';
810
</script>
911

1012
<script lang="ts">
@@ -18,7 +20,7 @@
1820
shift?: string;
1921
horz?: boolean;
2022
zIndex?: string;
21-
whenToShow: 'hover' | 'always' | 'scroll';
23+
whenToShow: ScrollbarVisilitySettings;
2224
onthumbdrag?: (dragging: boolean) => void;
2325
onscroll?: (e: Event) => void;
2426
onscrollexists?: (hasScroll: boolean) => void;

packages/ui/src/lib/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ export {
9999
} from '$components/scroll/ScrollableContainer.svelte';
100100
export {
101101
default as Scrollbar,
102-
type ScrollbarPaddingType
102+
type ScrollbarPaddingType,
103+
type ScrollbarVisilitySettings
103104
} from '$components/scroll/Scrollbar.svelte';
104105

105106
// Segment Control Components
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<script context="module" lang="ts">
2+
import VirtualList from '$lib/components/VirtualList.svelte';
3+
import { defineMeta } from '@storybook/addon-svelte-csf';
4+
5+
const items = Array.from({ length: 20 }, (_, i) => `item-${i + 1}`);
6+
7+
const { Story } = defineMeta({
8+
title: 'VirtualList',
9+
component: VirtualList<string>,
10+
args: {
11+
items,
12+
batchSize: 1
13+
}
14+
});
15+
</script>
16+
17+
<script lang="ts">
18+
</script>
19+
20+
<Story name="VirtualList">
21+
{#snippet template(args)}
22+
<div class="container">
23+
<VirtualList {...args} initialPosition="bottom" stickToBottom>
24+
{#snippet chunkTemplate(chunk)}
25+
{#each chunk as item}
26+
<div class="item">
27+
{item}
28+
</div>
29+
{/each}
30+
{/snippet}
31+
</VirtualList>
32+
</div>
33+
{/snippet}
34+
</Story>
35+
36+
<style>
37+
.container {
38+
display: flex;
39+
height: 320px;
40+
}
41+
42+
.item {
43+
display: flex;
44+
align-items: center;
45+
justify-content: center;
46+
width: 300px;
47+
height: 150px;
48+
border: 1px solid lightgrey;
49+
}
50+
</style>

0 commit comments

Comments
 (0)