File size: 1,530 Bytes
72bda43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<script lang="ts">
	import type { Snippet } from "svelte";

	interface Props {
		icon: Snippet;
		iconBg?: string;
		iconRing?: string;
		hasNext?: boolean;
		loading?: boolean;
		children: Snippet;
	}

	let {
		icon,
		iconBg = "bg-gray-50 dark:bg-gray-800",
		iconRing = "ring-gray-100 dark:ring-gray-700",
		hasNext = false,
		loading = false,
		children,
	}: Props = $props();
</script>

<div class="group flex gap-2 has-[+.prose]:mb-1.5 [.prose+&]:mt-3">
	<!-- Left column: icon + connector line -->
	<div class="flex w-[22px] flex-shrink-0 flex-col items-center">
		<div
			class="relative z-0 flex h-[22px] w-[22px] items-center justify-center rounded-md ring-1 {iconBg} {iconRing}"
		>
			{@render icon()}
			{#if loading}
				<svg
					class="pointer-events-none absolute inset-0 h-[22px] w-[22px]"
					viewBox="0 0 22 22"
					fill="none"
					xmlns="http://www.w3.org/2000/svg"
				>
					<rect
						x="0.5"
						y="0.5"
						width="21"
						height="21"
						rx="5.5"
						class="loading-path stroke-current text-purple-500/20"
						stroke-width="1"
						fill="none"
					/>
				</svg>
			{/if}
		</div>
		{#if hasNext}
			<div class="my-1 w-px flex-1 bg-gray-200 dark:bg-gray-700"></div>
		{/if}
	</div>

	<!-- Right column: content -->
	<div class="min-w-0 flex-1 pb-2 pt-px">
		{@render children()}
	</div>
</div>

<style>
	@keyframes loading {
		to {
			stroke-dashoffset: -100;
		}
	}

	.loading-path {
		stroke-dasharray: 60 40;
		animation: loading 2s cubic-bezier(0.4, 0, 0.2, 1) infinite;
	}
</style>