feat: implement relative time feedback and improve tooltip customization

This commit is contained in:
2026-01-29 16:44:06 -06:00
parent e41b970d6e
commit 2bc6fbdf30
2 changed files with 83 additions and 31 deletions
+69 -28
View File
@@ -1,34 +1,75 @@
<script lang="ts"> <script lang="ts">
export interface SearchMeta { import { onMount } from "svelte";
totalCount: number; import SimpleTooltip from "$lib/components/SimpleTooltip.svelte";
durationMs: number; import { relativeTime } from "$lib/time";
timestamp: Date;
}
let { meta }: { meta: SearchMeta | null } = $props(); export interface SearchMeta {
totalCount: number;
durationMs: number;
timestamp: Date;
}
let formattedTime = $derived( let { meta }: { meta: SearchMeta | null } = $props();
meta
? meta.timestamp.toLocaleTimeString(undefined, {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
})
: ""
);
let countLabel = $derived(meta ? meta.totalCount.toLocaleString() : ""); let now = $state(new Date());
let resultNoun = $derived(meta ? (meta.totalCount !== 1 ? "results" : "result") : "");
let durationLabel = $derived(meta ? `${Math.round(meta.durationMs)}ms` : ""); let formattedTime = $derived(
meta
? meta.timestamp.toLocaleTimeString(undefined, {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
})
: "",
);
let relativeTimeResult = $derived(
meta ? relativeTime(meta.timestamp, now) : null,
);
let relativeTimeText = $derived(relativeTimeResult?.text ?? "");
let countLabel = $derived(meta ? meta.totalCount.toLocaleString() : "");
let resultNoun = $derived(
meta ? (meta.totalCount !== 1 ? "results" : "result") : "",
);
let durationLabel = $derived(
meta ? `${Math.round(meta.durationMs)}ms` : "",
);
let tooltipText = $derived(
meta ? `${relativeTimeText} · ${formattedTime}` : "",
);
onMount(() => {
let nowTimeoutId: ReturnType<typeof setTimeout> | null = null;
function scheduleNowTick() {
const delay = relativeTimeResult?.nextUpdateMs ?? 1000;
nowTimeoutId = setTimeout(() => {
now = new Date();
scheduleNowTick();
}, delay);
}
scheduleNowTick();
return () => {
if (nowTimeoutId) clearTimeout(nowTimeoutId);
};
});
</script> </script>
{#if meta} <SimpleTooltip
<p text={tooltipText}
class="pl-1 text-xs" contentClass="whitespace-nowrap text-[12px] px-2 py-1"
title="Last searched at {formattedTime}" triggerClass="self-start"
> sideOffset={0}
<span class="text-muted-foreground/70">{countLabel}</span> >
<span class="text-muted-foreground/35">{resultNoun} in</span> <span
<span class="text-muted-foreground/70">{durationLabel}</span> class="pl-1 text-xs transition-opacity duration-200"
</p> style:opacity={meta ? 1 : 0}
{/if} >
<span class="text-muted-foreground/70">{countLabel}</span>
<span class="text-muted-foreground/35">{resultNoun} in</span>
<span class="text-muted-foreground/70">{durationLabel}</span>
</span>
</SimpleTooltip>
+14 -3
View File
@@ -1,30 +1,41 @@
<script lang="ts"> <script lang="ts">
import { Tooltip } from "bits-ui"; import { Tooltip } from "bits-ui";
import type { Snippet } from "svelte"; import type { Snippet } from "svelte";
import { cn } from "$lib/utils";
let { let {
text, text,
delay = 150, delay = 150,
side = "top", side = "top",
passthrough = false, passthrough = false,
triggerClass = "",
contentClass = "",
sideOffset = 6,
children, children,
}: { }: {
text: string; text: string;
delay?: number; delay?: number;
side?: "top" | "bottom" | "left" | "right"; side?: "top" | "bottom" | "left" | "right";
passthrough?: boolean; passthrough?: boolean;
triggerClass?: string;
contentClass?: string;
sideOffset?: number;
children: Snippet; children: Snippet;
} = $props(); } = $props();
</script> </script>
<Tooltip.Root delayDuration={delay} disableHoverableContent={passthrough}> <Tooltip.Root delayDuration={delay} disableHoverableContent={passthrough}>
<Tooltip.Trigger> <Tooltip.Trigger>
{@render children()} {#snippet child({ props })}
<span class={triggerClass} {...props}>
{@render children()}
</span>
{/snippet}
</Tooltip.Trigger> </Tooltip.Trigger>
<Tooltip.Content <Tooltip.Content
{side} {side}
sideOffset={6} {sideOffset}
class="z-50 bg-card text-card-foreground text-xs border border-border rounded-md px-2.5 py-1.5 shadow-md whitespace-pre-line max-w-max" class={cn("z-50 bg-card text-card-foreground text-xs border border-border rounded-md px-2.5 py-1.5 shadow-sm whitespace-pre-line max-w-max text-left", contentClass)}
> >
{text} {text}
</Tooltip.Content> </Tooltip.Content>