Merge branch 'master' into askpass-pipe-windows-fix

This commit is contained in:
Nico Domino 2024-06-19 22:37:09 +02:00 committed by GitHub
commit 6d5730d558
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 447 additions and 267 deletions

View File

@ -1,3 +1,9 @@
# https://github.com/actions/labeler#create-githublabeleryml # https://github.com/actions/labeler#basic-examples
rust: ["crates/**/*"]
svelte: ["app/**/*"] rust:
- changed-files:
- any-glob-to-any-file: crates/**/*
svelte:
- changed-files:
- any-glob-to-any-file: app/**/*

View File

@ -1,16 +1,19 @@
# https://github.com/actions/labeler#create-workflow # https://github.com/actions/labeler#create-workflow
name: Label Pull Requests name: Label Pull Requests
on: on:
pull_request_target: pull_request_target:
jobs: jobs:
prs: labeler:
name: Triage permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/labeler@v4 - uses: actions/checkout@v4
with: with:
repo-token: "${{ secrets.GITHUB_TOKEN }}" repository: "gitbutlerapp/gitbutler"
configuration-path: ".github/pr-labeler.yml" - uses: actions/labeler@v5
with:
configuration-path: '.github/pr-labeler.yml'

View File

@ -147,12 +147,6 @@
); );
} }
} }
let branchFiles: BranchFiles | undefined;
function onBottomReached() {
branchFiles?.loadMore();
}
</script> </script>
{#if $isLaneCollapsed} {#if $isLaneCollapsed}
@ -181,8 +175,6 @@
top: 12, top: 12,
bottom: 12 bottom: 12
}} }}
bottomBuffer={300}
on:bottomReached={onBottomReached}
> >
<div <div
bind:this={rsViewport} bind:this={rsViewport}
@ -243,7 +235,6 @@
{isUnapplied} {isUnapplied}
showCheckboxes={$commitBoxOpen} showCheckboxes={$commitBoxOpen}
allowMultiple allowMultiple
bind:this={branchFiles}
/> />
{#if branch.active && branch.conflicted} {#if branch.active && branch.conflicted}
<div class="card-notifications"> <div class="card-notifications">
@ -264,6 +255,7 @@
<CommitDialog <CommitDialog
projectId={project.id} projectId={project.id}
expanded={commitBoxOpen} expanded={commitBoxOpen}
hasSectionsAfter={branch.commits.length > 0}
on:action={(e) => { on:action={(e) => {
if (e.detail === 'generate-branch-name') { if (e.detail === 'generate-branch-name') {
generateBranchName(); generateBranchName();
@ -283,7 +275,7 @@
</div> </div>
{:else} {:else}
<div class="no-changes" data-dnd-ignore> <div class="no-changes" data-dnd-ignore>
<EmptyStatePlaceholder image={noChangesSvg} width="11rem" hasBottomShift={false}> <EmptyStatePlaceholder image={noChangesSvg} width="11rem" hasBottomMargin={false}>
<svelte:fragment slot="caption" <svelte:fragment slot="caption"
>No uncommitted changes on this branch</svelte:fragment >No uncommitted changes on this branch</svelte:fragment
> >
@ -292,10 +284,12 @@
{/if} {/if}
</div> </div>
<div class="card-commits">
<CommitList {isUnapplied} /> <CommitList {isUnapplied} />
<BranchFooter {isUnapplied} /> <BranchFooter {isUnapplied} />
</div> </div>
</div> </div>
</div>
</ScrollableContainer> </ScrollableContainer>
<div class="divider-line"> <div class="divider-line">
<Resizer <Resizer
@ -355,13 +349,18 @@
.card { .card {
flex: 1; flex: 1;
overflow: hidden; /* overflow: hidden; */
/* border: 1px solid var(--clr-border-2);
border-radius: var(--radius-m); */
} }
.branch-card__files { .branch-card__files {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
/* border-left: 1px solid var(--clr-border-2);
border-right: 1px solid var(--clr-border-2);
border-radius: var(--radius-m) var(--radius-m) 0 0; */
} }
.card-notifications { .card-notifications {

View File

@ -18,12 +18,6 @@
function unselectAllFiles() { function unselectAllFiles() {
fileIdSelection.clear(); fileIdSelection.clear();
} }
let branchFilesList: BranchFilesList | undefined;
export function loadMore() {
branchFilesList?.loadMore();
}
</script> </script>
<div <div
@ -38,21 +32,12 @@
on:click={unselectAllFiles} on:click={unselectAllFiles}
> >
{#if files.length > 0} {#if files.length > 0}
<BranchFilesList <BranchFilesList {allowMultiple} {readonly} {files} {showCheckboxes} {isUnapplied} />
bind:this={branchFilesList}
{allowMultiple}
{readonly}
{files}
{showCheckboxes}
{isUnapplied}
/>
{/if} {/if}
</div> </div>
<style lang="postcss"> <style lang="postcss">
.branch-files { .branch-files {
flex: 1; flex: 1;
background: var(--clr-bg-1);
/* padding: 0 14px 14px; */
} }
</style> </style>

View File

@ -71,6 +71,9 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 14px; padding: 14px;
border-bottom: none;
border-radius: var(--radius-m) var(--radius-m) 0 0;
background-color: var(--clr-bg-1);
} }
.header__title { .header__title {
display: flex; display: flex;

View File

@ -2,6 +2,7 @@
import BranchFilesHeader from './BranchFilesHeader.svelte'; import BranchFilesHeader from './BranchFilesHeader.svelte';
import Button from './Button.svelte'; import Button from './Button.svelte';
import FileListItem from './FileListItem.svelte'; import FileListItem from './FileListItem.svelte';
import LazyloadContainer from './LazyloadContainer.svelte';
import TextBox from '$lib/components/TextBox.svelte'; import TextBox from '$lib/components/TextBox.svelte';
import { copyToClipboard } from '$lib/utils/clipboard'; import { copyToClipboard } from '$lib/utils/clipboard';
import { getContext } from '$lib/utils/context'; import { getContext } from '$lib/utils/context';
@ -64,11 +65,21 @@
style="ghost" style="ghost"
outline outline
on:mousedown={() => copyToClipboard(mergeDiffCommand + $commit.id.slice(0, 7))} on:mousedown={() => copyToClipboard(mergeDiffCommand + $commit.id.slice(0, 7))}
></Button> />
</div> </div>
</div> </div>
{/if} {/if}
{#if displayedFiles.length > 0}
<!-- Maximum amount for initial render is 100 files
`minTriggerCount` set to 80 in order to start the loading a bit earlier. -->
<LazyloadContainer
minTriggerCount={80}
ontrigger={() => {
console.log('loading more files...');
loadMore();
}}
>
{#each displayedFiles as file (file.id)} {#each displayedFiles as file (file.id)}
<FileListItem <FileListItem
{file} {file}
@ -85,6 +96,8 @@
}} }}
/> />
{/each} {/each}
</LazyloadContainer>
{/if}
<style lang="postcss"> <style lang="postcss">
.merge-commit-error { .merge-commit-error {

View File

@ -5,6 +5,7 @@
import { PromptService } from '$lib/backend/prompt'; import { PromptService } from '$lib/backend/prompt';
import { project } from '$lib/testing/fixtures'; import { project } from '$lib/testing/fixtures';
import { getContext, getContextStore } from '$lib/utils/context'; import { getContext, getContextStore } from '$lib/utils/context';
import { intersectionObserver } from '$lib/utils/intersectionObserver';
import { BranchController } from '$lib/vbranches/branchController'; import { BranchController } from '$lib/vbranches/branchController';
import { getLocalCommits, getRemoteCommits, getUnknownCommits } from '$lib/vbranches/contexts'; import { getLocalCommits, getRemoteCommits, getUnknownCommits } from '$lib/vbranches/contexts';
import { Branch } from '$lib/vbranches/types'; import { Branch } from '$lib/vbranches/types';
@ -25,6 +26,7 @@
const unknownCommits = getUnknownCommits(); const unknownCommits = getUnknownCommits();
let isLoading: boolean; let isLoading: boolean;
let isInViewport = false;
$: canBePushed = $localCommits.length !== 0 || $unknownCommits.length !== 0; $: canBePushed = $localCommits.length !== 0 || $unknownCommits.length !== 0;
$: hasUnknownCommits = $unknownCommits.length > 0; $: hasUnknownCommits = $unknownCommits.length > 0;
@ -33,7 +35,25 @@
</script> </script>
{#if !isUnapplied && hasCommits} {#if !isUnapplied && hasCommits}
<div class="actions"> <div
class="actions"
class:sticky={canBePushed}
class:not-in-viewport={!isInViewport}
use:intersectionObserver={{
callback: (entry) => {
if (entry.isIntersecting) {
isInViewport = true;
} else {
isInViewport = false;
}
},
options: {
root: null,
rootMargin: '-1px',
threshold: 1
}
}}
>
{#if canBePushed} {#if canBePushed}
{#if $prompt} {#if $prompt}
<PassphraseBox prompt={$prompt} error={$promptError} /> <PassphraseBox prompt={$prompt} error={$promptError} />
@ -77,13 +97,14 @@
.actions { .actions {
background: var(--clr-bg-1); background: var(--clr-bg-1);
padding: 16px; padding: 16px;
border-top: 1px solid var(--clr-border-2);
border-radius: 0 0 var(--radius-m) var(--radius-m);
} }
/* EMPTY STATE */ /* EMPTY STATE */
.empty-state { .empty-state {
display: flex; display: flex;
/* justify-content: space-between; */
align-items: center; align-items: center;
gap: 20px; gap: 20px;
} }
@ -96,4 +117,16 @@
color: var(--clr-text-3); color: var(--clr-text-3);
flex: 1; flex: 1;
} }
/* MODIFIERS */
.sticky {
z-index: var(--z-lifted);
position: sticky;
bottom: 0;
}
.not-in-viewport {
border-radius: 0;
/* background-color: aquamarine; */
}
</style> </style>

View File

@ -17,8 +17,8 @@
</script> </script>
<span <span
use:useResize={(frame) => { use:useResize={(e) => {
inputWidth = `${Math.round(frame.width)}px`; inputWidth = `${Math.round(e.frame.width)}px`;
}} }}
class="branch-name-mesure-el text-base-14 text-bold" class="branch-name-mesure-el text-base-14 text-bold"
bind:this={mesureEl}>{name}</span bind:this={mesureEl}>{name}</span

View File

@ -103,7 +103,11 @@
</script> </script>
<Modal bind:this={commitMessageModal} width="small"> <Modal bind:this={commitMessageModal} width="small">
<CommitMessageInput bind:commitMessage={description} bind:valid={commitMessageValid} /> <CommitMessageInput
bind:commitMessage={description}
bind:valid={commitMessageValid}
isExpanded={true}
/>
{#snippet controls(close)} {#snippet controls(close)}
<Button style="ghost" outline on:click={close}>Cancel</Button> <Button style="ghost" outline on:click={close}>Cancel</Button>
<Button <Button

View File

@ -1,17 +1,17 @@
<script lang="ts"> <script lang="ts">
import Button from './Button.svelte'; import Button from './Button.svelte';
import CommitMessageInput from '$lib/components/CommitMessageInput.svelte'; import CommitMessageInput from '$lib/components/CommitMessageInput.svelte';
import { projectRunCommitHooks, persistedCommitMessage } from '$lib/config/config'; import { persistedCommitMessage, projectRunCommitHooks } from '$lib/config/config';
import { getContext, getContextStore } from '$lib/utils/context'; import { getContext, getContextStore } from '$lib/utils/context';
import { intersectionObserver } from '$lib/utils/intersectionObserver';
import { BranchController } from '$lib/vbranches/branchController'; import { BranchController } from '$lib/vbranches/branchController';
import { Ownership } from '$lib/vbranches/ownership'; import { Ownership } from '$lib/vbranches/ownership';
import { Branch } from '$lib/vbranches/types'; import { Branch } from '$lib/vbranches/types';
import { quintOut } from 'svelte/easing';
import { slide } from 'svelte/transition';
import type { Writable } from 'svelte/store'; import type { Writable } from 'svelte/store';
export let projectId: string; export let projectId: string;
export let expanded: Writable<boolean>; export let expanded: Writable<boolean>;
export let hasSectionsAfter: boolean;
const branchController = getContext(BranchController); const branchController = getContext(BranchController);
const selectedOwnership = getContextStore(Ownership); const selectedOwnership = getContextStore(Ownership);
@ -21,8 +21,8 @@
const commitMessage = persistedCommitMessage(projectId, $branch.id); const commitMessage = persistedCommitMessage(projectId, $branch.id);
let isCommitting = false; let isCommitting = false;
let commitMessageValid = false; let commitMessageValid = false;
let isInViewport = false;
async function commit() { async function commit() {
const message = $commitMessage; const message = $commitMessage;
@ -41,17 +41,32 @@
} }
</script> </script>
<div class="commit-box" class:commit-box__expanded={$expanded}> <div
{#if $expanded} class="commit-box"
<div class="commit-box__expander" transition:slide={{ duration: 150, easing: quintOut }}> class:not-in-viewport={!isInViewport}
class:no-sections-after={!hasSectionsAfter}
use:intersectionObserver={{
callback: (entry) => {
if (entry.isIntersecting) {
isInViewport = true;
} else {
isInViewport = false;
}
},
options: {
root: null,
rootMargin: '-1px',
threshold: 1
}
}}
>
<CommitMessageInput <CommitMessageInput
bind:commitMessage={$commitMessage} bind:commitMessage={$commitMessage}
bind:valid={commitMessageValid} bind:valid={commitMessageValid}
isExpanded={$expanded}
{commit} {commit}
/> />
</div> <div class="actions" class:commit-box__actions-expanded={$expanded}>
{/if}
<div class="actions">
{#if $expanded && !isCommitting} {#if $expanded && !isCommitting}
<Button <Button
style="ghost" style="ghost"
@ -87,27 +102,31 @@
<style lang="postcss"> <style lang="postcss">
.commit-box { .commit-box {
position: sticky;
bottom: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 12px;
padding: 14px; padding: 14px;
background: var(--clr-bg-1); background: var(--clr-bg-1);
border-top: 1px solid var(--clr-border-2); border-top: 1px solid var(--clr-border-2);
transition: background-color var(--transition-medium); transition: background-color var(--transition-medium);
} }
.commit-box__expander {
display: flex;
flex-direction: column;
margin-bottom: 12px;
}
.actions { .actions {
display: flex; display: flex;
justify-content: right; justify-content: right;
gap: 6px; gap: 6px;
} }
.commit-box__expanded { /* MODIFIERS */
background-color: var(--clr-bg-2); .not-in-viewport {
z-index: var(--z-ground);
}
.no-sections-after {
border-radius: 0 0 var(--radius-m) var(--radius-m);
} }
</style> </style>

View File

@ -264,7 +264,7 @@
<!-- BASE --> <!-- BASE -->
<div class="base-row-container" class:base-row-container_unfolded={baseIsUnfolded}> <div class="base-row-container" class:base-row-container_unfolded={baseIsUnfolded}>
<div <div
class="commit-group base-row" class="base-row"
tabindex="0" tabindex="0"
role="button" role="button"
on:click|stopPropagation={() => (baseIsUnfolded = !baseIsUnfolded)} on:click|stopPropagation={() => (baseIsUnfolded = !baseIsUnfolded)}
@ -303,7 +303,7 @@
flex-direction: column; flex-direction: column;
background-color: var(--clr-bg-2); background-color: var(--clr-bg-2);
border-top: 1px solid var(--clr-border-2); border-top: 1px solid var(--clr-border-2);
border-bottom: 1px solid var(--clr-border-2); /* border-bottom: 1px solid var(--clr-border-2); */
--base-top-margin: 8px; --base-top-margin: 8px;
--base-icon-top: 16px; --base-icon-top: 16px;
@ -313,10 +313,10 @@
--avatar-top: 16px; --avatar-top: 16px;
} }
.commit-group { /* .commit-group {
/* padding-right: 14px; padding-right: 14px;
padding-left: 8px; */ padding-left: 8px;
} } */
/* BASE ROW */ /* BASE ROW */

View File

@ -19,11 +19,13 @@
import { getContext, getContextStore } from '$lib/utils/context'; import { getContext, getContextStore } from '$lib/utils/context';
import { tooltip } from '$lib/utils/tooltip'; import { tooltip } from '$lib/utils/tooltip';
import { useAutoHeight } from '$lib/utils/useAutoHeight'; import { useAutoHeight } from '$lib/utils/useAutoHeight';
import { useResize } from '$lib/utils/useResize';
import { Ownership } from '$lib/vbranches/ownership'; import { Ownership } from '$lib/vbranches/ownership';
import { Branch, LocalFile } from '$lib/vbranches/types'; import { Branch, LocalFile } from '$lib/vbranches/types';
import { createEventDispatcher, onMount } from 'svelte'; import { createEventDispatcher, onMount } from 'svelte';
import { fly } from 'svelte/transition'; import { fly } from 'svelte/transition';
export let isExpanded: boolean;
export let commitMessage: string; export let commitMessage: string;
export let valid: boolean = false; export let valid: boolean = false;
export let commit: (() => void) | undefined = undefined; export let commit: (() => void) | undefined = undefined;
@ -101,7 +103,14 @@
}); });
</script> </script>
<div class="commit-box__textarea-wrapper text-input"> {#if isExpanded}
<div
class="commit-box__textarea-wrapper text-input"
use:useResize={() => {
useAutoHeight(titleTextArea);
useAutoHeight(descriptionTextArea);
}}
>
<textarea <textarea
value={title} value={title}
placeholder="Commit summary" placeholder="Commit summary"
@ -169,6 +178,7 @@
<div <div
class="commit-box__texarea-actions" class="commit-box__texarea-actions"
class:commit-box-actions_expanded={isExpanded}
use:tooltip={$aiGenEnabled && aiConfigurationValid use:tooltip={$aiGenEnabled && aiConfigurationValid
? '' ? ''
: 'You must be logged in or have provided your own API key and have summary generation enabled to use this feature'} : 'You must be logged in or have provided your own API key and have summary generation enabled to use this feature'}
@ -203,6 +213,7 @@
</DropDownButton> </DropDownButton>
</div> </div>
</div> </div>
{/if}
<style lang="postcss"> <style lang="postcss">
.commit-box__textarea-wrapper { .commit-box__textarea-wrapper {
@ -211,6 +222,12 @@
padding: 0 0 48px; padding: 0 0 48px;
flex-direction: column; flex-direction: column;
gap: 4px; gap: 4px;
overflow: hidden;
animation: expand-box 0.2s ease forwards;
/* props to animate on mount */
/* display: none;
max-height: 0;
overflow: hidden; */
} }
.commit-box__textarea { .commit-box__textarea {
@ -221,6 +238,7 @@
gap: 16px; gap: 16px;
background: none; background: none;
resize: none; resize: none;
&:focus { &:focus {
outline: none; outline: none;
} }
@ -252,8 +270,45 @@
.commit-box__texarea-actions { .commit-box__texarea-actions {
position: absolute; position: absolute;
display: flex;
right: 12px; right: 12px;
bottom: 12px; bottom: 12px;
/* props to animate on mount */
display: none;
opacity: 0;
transform: translateY(10px);
}
/* MODIFIERS */
/* .commit-box_expanded {
display: flex;
animation: expand-box 0.2s ease forwards;
} */
@keyframes expand-box {
from {
max-height: 0;
padding: 0 0 0;
}
to {
max-height: 600px;
padding: 0 0 48px;
}
}
.commit-box-actions_expanded {
display: flex;
animation: expand-actions 0.25s ease forwards;
animation-delay: 0.1s;
}
@keyframes expand-actions {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
} }
</style> </style>

View File

@ -1,14 +1,14 @@
<script lang="ts"> <script lang="ts">
export let image: string; export let image: string;
export let width: string = '18rem'; export let width: string = '18rem';
export let hasBottomShift: boolean = true; export let hasBottomMargin: boolean = true;
</script> </script>
<div class="empty-state-container"> <div class="empty-state-container">
<div <div
class="empty-state" class="empty-state"
style:max-width={width} style:max-width={width}
style:margin-bottom={hasBottomShift ? '48px' : '0'} style:margin-bottom={hasBottomMargin ? '48px' : '0'}
> >
<div class="empty-state__image"> <div class="empty-state__image">
{@html image} {@html image}

View File

@ -104,6 +104,13 @@
}} }}
role="button" role="button"
tabindex="0" tabindex="0"
on:contextmenu|preventDefault={async (e) => {
if (fileIdSelection.has(file.id, $commit?.id)) {
popupMenu.openByMouse(e, { files: await $selectedFiles });
} else {
popupMenu.openByMouse(e, { files: [file] });
}
}}
use:draggable={{ use:draggable={{
data: $selectedFiles.then( data: $selectedFiles.then(
(files) => new DraggableFile($branch?.id || '', file, $commit, files) (files) => new DraggableFile($branch?.id || '', file, $commit, files)
@ -112,13 +119,6 @@
viewportId: 'board-viewport', viewportId: 'board-viewport',
selector: '.selected-draggable' selector: '.selected-draggable'
}} }}
on:contextmenu|preventDefault={async (e) => {
if (fileIdSelection.has(file.id, $commit?.id)) {
popupMenu.openByMouse(e, { files: await $selectedFiles });
} else {
popupMenu.openByMouse(e, { files: [file] });
}
}}
> >
{#if showCheckbox} {#if showCheckbox}
<Checkbox <Checkbox

View File

@ -4,6 +4,7 @@
import FileCard from './FileCard.svelte'; import FileCard from './FileCard.svelte';
import FullviewLoading from './FullviewLoading.svelte'; import FullviewLoading from './FullviewLoading.svelte';
import Icon from './Icon.svelte'; import Icon from './Icon.svelte';
import LazyloadContainer from './LazyloadContainer.svelte';
import ScrollableContainer from './ScrollableContainer.svelte'; import ScrollableContainer from './ScrollableContainer.svelte';
import SnapshotCard from './SnapshotCard.svelte'; import SnapshotCard from './SnapshotCard.svelte';
import emptyFolderSvg from '$lib/assets/empty-state/empty-folder.svg?raw'; import emptyFolderSvg from '$lib/assets/empty-state/empty-folder.svg?raw';
@ -140,9 +141,16 @@
<!-- SNAPSHOTS --> <!-- SNAPSHOTS -->
{#if $snapshots.length > 0} {#if $snapshots.length > 0}
<ScrollableContainer on:bottomReached={onLastInView}> <ScrollableContainer>
<div class="container"> <div class="container">
<!-- SNAPSHOTS FEED --> <!-- SNAPSHOTS FEED -->
<LazyloadContainer
minTriggerCount={30}
ontrigger={() => {
console.log('load more snapshots…');
onLastInView();
}}
>
{#each $snapshots as entry, idx (entry.id)} {#each $snapshots as entry, idx (entry.id)}
{@const withinRestoreItems = findRestorationRanges($snapshots)} {@const withinRestoreItems = findRestorationRanges($snapshots)}
{#if idx === 0 || createdOnDay(entry.createdAt) !== createdOnDay($snapshots[idx - 1].createdAt)} {#if idx === 0 || createdOnDay(entry.createdAt) !== createdOnDay($snapshots[idx - 1].createdAt)}
@ -185,6 +193,7 @@
/> />
{/if} {/if}
{/each} {/each}
</LazyloadContainer>
<!-- LOAD MORE --> <!-- LOAD MORE -->
{#if $loading} {#if $loading}

View File

@ -0,0 +1,51 @@
<script lang="ts">
import { onMount } from 'svelte';
interface Props {
children: any;
minTriggerCount: number;
ontrigger: (lastChild: Element) => void;
}
let { children, minTriggerCount, ontrigger }: Props = $props();
let lazyContainerEl: HTMLDivElement;
onMount(() => {
const containerChildren = lazyContainerEl.children;
if (containerChildren.length < minTriggerCount) return;
const iObserver = new IntersectionObserver((entries) => {
const lastChild = containerChildren[containerChildren.length - 1];
if (entries[0].target === lastChild && entries[0].isIntersecting) {
ontrigger(lastChild);
}
});
const mObserver = new MutationObserver(() => {
const lastChild = containerChildren[containerChildren.length - 1];
if (lastChild) {
iObserver.observe(lastChild);
}
});
iObserver.observe(containerChildren[containerChildren.length - 1]);
mObserver.observe(lazyContainerEl, { childList: true });
return () => {
iObserver.disconnect();
mObserver.disconnect();
};
});
</script>
<div class="lazy-container" bind:this={lazyContainerEl}>
{@render children()}
</div>
<style>
.lazy-container {
display: contents;
}
</style>

View File

@ -18,12 +18,9 @@
export let shift = '0'; export let shift = '0';
export let thickness = '0.563rem'; export let thickness = '0.563rem';
// How much of a buffer there should be before we consider the bottom reached
export let bottomBuffer = 0;
let observer: ResizeObserver; let observer: ResizeObserver;
const dispatch = createEventDispatcher<{ dragging: boolean; bottomReached: boolean }>(); const dispatch = createEventDispatcher<{ dragging: boolean }>();
onMount(() => { onMount(() => {
observer = new ResizeObserver(() => { observer = new ResizeObserver(() => {
@ -46,14 +43,6 @@
> >
<div <div
bind:this={viewport} bind:this={viewport}
on:scroll={(e) => {
const target = e.currentTarget;
scrolled = target.scrollTop !== 0;
if (target.scrollTop + target.clientHeight + bottomBuffer >= target.scrollHeight) {
dispatch('bottomReached', true);
}
}}
class="viewport hide-native-scrollbar" class="viewport hide-native-scrollbar"
style:height style:height
style:overflow-y={scrollable ? 'auto' : 'hidden'} style:overflow-y={scrollable ? 'auto' : 'hidden'}

View File

@ -42,7 +42,7 @@
let filterText: string | undefined = undefined; let filterText: string | undefined = undefined;
let filteredItems: Selectable[] = items; let filteredItems: Selectable[] = items;
function filterItems(items: Selectable[], filterText: string | undefined) { const filterItems = throttle((items: Selectable[], filterText: string | undefined) => {
if (!filterText) { if (!filterText) {
return items; return items;
} }
@ -52,7 +52,7 @@
if (!isStr(property)) return false; if (!isStr(property)) return false;
return property.includes(filterText); return property.includes(filterText);
}); });
} }, INPUT_THROTTLE_TIME);
$: filteredItems = filterItems(items, filterText); $: filteredItems = filterItems(items, filterText);
@ -111,13 +111,13 @@
} }
} }
const handleChar = throttle((char: string) => { function handleChar(char: string) {
highlightIndex = undefined; highlightIndex = undefined;
filterText ??= ''; filterText ??= '';
filterText += char; filterText += char;
}, INPUT_THROTTLE_TIME); }
const handleDelete = throttle(() => { function handleDelete() {
if (filterText === undefined) return; if (filterText === undefined) return;
if (filterText.length === 1) { if (filterText.length === 1) {
@ -126,7 +126,7 @@
} }
filterText = filterText.slice(0, -1); filterText = filterText.slice(0, -1);
}, INPUT_THROTTLE_TIME); }
function handleKeyDown(e: CustomEvent<KeyboardEvent>) { function handleKeyDown(e: CustomEvent<KeyboardEvent>) {
if (!listOpen) { if (!listOpen) {

View File

@ -29,7 +29,11 @@
return `${createdOnDay(date)}, ${toHumanReadableTime(date)}`; return `${createdOnDay(date)}, ${toHumanReadableTime(date)}`;
} }
const dispatch = createEventDispatcher<{ restoreClick: void; diffClick: string }>(); const dispatch = createEventDispatcher<{
restoreClick: void;
diffClick: string;
visible: void;
}>();
function camelToTitleCase(str: string | undefined) { function camelToTitleCase(str: string | undefined) {
if (!str) return ''; if (!str) return '';

View File

@ -48,8 +48,8 @@
dispatch('change', e.currentTarget.value); dispatch('change', e.currentTarget.value);
useAutoHeight(e.currentTarget); useAutoHeight(e.currentTarget);
}} }}
use:useResize={() => { use:useResize={(e) => {
useAutoHeight(textareaElement); useAutoHeight(e.currentTarget as HTMLTextAreaElement);
}} }}
on:focus={(e) => useAutoHeight(e.currentTarget)} on:focus={(e) => useAutoHeight(e.currentTarget)}
style:max-height={maxHeight ? pxToRem(maxHeight) : undefined} style:max-height={maxHeight ? pxToRem(maxHeight) : undefined}

View File

@ -0,0 +1,27 @@
export function intersectionObserver(
node: Element,
{
isDisabled,
callback,
options
}: {
isDisabled?: boolean;
callback: (entry: IntersectionObserverEntry, observer: IntersectionObserver) => void;
options?: IntersectionObserverInit;
}
) {
if (isDisabled) return;
const observer = new IntersectionObserver(
([entry], observer) => callback(entry, observer),
options
);
observer.observe(node);
return {
destroy() {
observer.disconnect();
}
};
}

View File

@ -1,14 +1,17 @@
export function useResize( export function useResize(
element: HTMLElement, element: HTMLElement,
callback: (frame: { width: number; height: number }) => void callback: (data: { currentTarget: HTMLElement; frame: { width: number; height: number } }) => void
) { ) {
const resizeObserver = new ResizeObserver((entries) => { const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) { for (const entry of entries) {
const { inlineSize, blockSize } = entry.borderBoxSize[0]; const { inlineSize, blockSize } = entry.borderBoxSize[0];
callback({ callback({
currentTarget: element,
frame: {
width: Math.round(inlineSize), width: Math.round(inlineSize),
height: Math.round(blockSize) height: Math.round(blockSize)
}
}); });
} }
}); });

View File

@ -1,23 +0,0 @@
export function useStickyPinned(
element: HTMLElement,
callback: (isPinned: boolean, element: HTMLElement) => void
) {
const observer = new IntersectionObserver(
([entry]) => {
callback(entry.intersectionRatio < 1, element);
console.log('sticky pinned', element, entry.intersectionRatio);
},
{
threshold: [1]
}
);
observer.observe(element);
return {
destroy() {
observer.disconnect();
}
};
}