Implement issue and patch list fuzzy finder

This commit is contained in:
Rūdolfs Ošiņš
2025-04-01 17:16:03 +02:00
parent d01cf9b433
commit 83390aed59
8 changed files with 325 additions and 96 deletions
+35 -17
View File
@@ -10,6 +10,7 @@
} from "@app/lib/utils";
import { push } from "@app/lib/router";
import Border from "./Border.svelte";
import Icon from "./Icon.svelte";
import Id from "./Id.svelte";
import InlineTitle from "./InlineTitle.svelte";
@@ -17,19 +18,21 @@
import NodeId from "./NodeId.svelte";
interface Props {
compact?: boolean;
focussed?: boolean;
issue: Issue;
rid: string;
status: IssueStatus;
selected?: boolean;
compact?: boolean;
status: IssueStatus;
}
const {
compact = false,
focussed,
issue,
rid,
status,
selected = false,
compact = false,
status,
}: Props = $props();
</script>
@@ -37,7 +40,6 @@
.issue-teaser {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.25rem;
min-height: 5rem;
background-color: var(--color-background-float);
@@ -67,16 +69,7 @@
}
</style>
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
tabindex="0"
role="button"
class="issue-teaser"
class:selected
style:align-items="flex-start"
onclick={() => {
void push({ resource: "repo.issue", rid, issue: issue.id, status });
}}>
{#snippet issueSnippet()}
<div class="global-flex" style:align-items="flex-start">
<div
class="global-counter status"
@@ -102,7 +95,7 @@
</div>
</div>
<div class="global-flex">
<div class="global-flex" style:margin-left="auto">
{#if !compact}
{#each issue.labels as label}
<Label {label} />
@@ -116,4 +109,29 @@
</div>
{/if}
</div>
</div>
{/snippet}
{#if focussed}
<Border
styleBackgroundColor="var(--color-background-float)"
styleDisplay="flex"
styleAlignItems="flex-start"
styleMinHeight="5rem"
stylePadding="1rem"
variant="secondary">
{@render issueSnippet()}
</Border>
{:else}
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
tabindex="0"
role="button"
class="issue-teaser"
class:selected
style:align-items="flex-start"
onclick={() => {
void push({ resource: "repo.issue", rid, issue: issue.id, status });
}}>
{@render issueSnippet()}
</div>
{/if}
+41 -22
View File
@@ -18,9 +18,11 @@
import InlineTitle from "@app/components/InlineTitle.svelte";
import Label from "@app/components/Label.svelte";
import NodeId from "@app/components/NodeId.svelte";
import Border from "./Border.svelte";
interface Props {
compact?: boolean;
focussed?: boolean;
loadPatch?: (patchId: string) => Promise<void>;
patch: Patch;
rid: string;
@@ -30,6 +32,7 @@
const {
compact = false,
focussed,
loadPatch,
patch,
rid,
@@ -72,26 +75,7 @@
}
</style>
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
tabindex="0"
role="button"
class:selected
class="patch-teaser"
style:align-items="flex-start"
onclick={async () => {
if (loadPatch) {
await loadPatch(patch.id);
} else {
void push({
resource: "repo.patch",
rid,
patch: patch.id,
status,
reviewId: undefined,
});
}
}}>
{#snippet patchSnippet()}
<div class="global-flex" style:align-items="flex-start">
<div
class="global-counter status"
@@ -116,7 +100,7 @@
</div>
</div>
<div class="global-flex">
<div class="global-flex" style:margin-left="auto">
{#if !compact}
{#await invoke<Stats>( "diff_stats", { rid, base: patch.base, head: patch.head }, ) then stats}
<DiffStatBadge {stats} />
@@ -134,4 +118,39 @@
{patch.revisionCount}
</div>
</div>
</div>
{/snippet}
{#if focussed}
<Border
styleBackgroundColor="var(--color-background-float)"
styleDisplay="flex"
styleAlignItems="flex-start"
styleMinHeight="5rem"
stylePadding="1rem"
variant="secondary">
{@render patchSnippet()}
</Border>
{:else}
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
tabindex="0"
role="button"
class:selected
class="patch-teaser"
style:align-items="flex-start"
onclick={async () => {
if (loadPatch) {
await loadPatch(patch.id);
} else {
void push({
resource: "repo.patch",
rid,
patch: patch.id,
status,
reviewId: undefined,
});
}
}}>
{@render patchSnippet()}
</div>
{/if}
+21 -16
View File
@@ -7,36 +7,38 @@
import Border from "./Border.svelte";
interface Props {
name?: string;
placeholder?: string;
value?: string;
type?: string;
autofocus?: boolean;
autoselect?: boolean;
disabled?: boolean;
onSubmit?: () => void;
onDismiss?: () => void;
valid?: boolean;
oninput?: FormEventHandler<HTMLInputElement>;
keyShortcuts?: string;
left?: Snippet;
name?: string;
onDismiss?: () => void;
onFocus?: () => void;
onSubmit?: () => void;
oninput?: FormEventHandler<HTMLInputElement>;
placeholder?: string;
type?: string;
valid?: boolean;
value?: string;
}
/* eslint-disable prefer-const */
let {
name,
placeholder,
value = $bindable(undefined),
type = "text",
autofocus = false,
autoselect = false,
disabled = false,
onSubmit,
onDismiss,
valid = true,
oninput,
keyShortcuts,
left,
name,
onDismiss,
onFocus,
onSubmit,
oninput,
placeholder,
type = "text",
valid = true,
value = $bindable(undefined),
}: Props = $props();
/* eslint-enable prefer-const */
@@ -102,6 +104,9 @@
style:padding={left ? "0.25rem 0.75rem 0.25rem 0" : "0.25rem 0.75rem"}
aria-keyshortcuts={keyShortcuts}
onfocus={() => {
if (onFocus) {
onFocus();
}
focussed = true;
}}
onblur={() => {