~james/core

core/web/src/lib/components/ui/GraphCell.svelte -rw-r--r-- 2.7 KiB
452f17b7 — james chore(import): snapshot james.dad.town/core at f70c71c7696a 15 hours ago
                                                                                
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<script module lang="ts">
	import { tv, type VariantProps } from "tailwind-variants";

	export type Band = "none" | "first" | "middle" | "last" | "only";

	// `--graph-dot` : dot border color (default to `--graph-line`)
	// `--graph-fill` : dot fill color (default to `--color-background-default`)
	// `--graph-line` : line color (default to `--color-border-strong`)
	export const graphCell = tv({
		slots: {
			root: [
				"relative flex w-fit justify-center",
				"before:absolute before:top-0 before:h-5 before:border-l before:border-(--line)",
				"after:absolute after:top-5 after:bottom-0 after:border-l after:border-(--line)"
			],
			dot: "z-10 size-1.75 rounded-full border border-(--dot)"
		},
		variants: {
			band: {
				none: { root: "before:hidden after:hidden" },
				first: { root: "before:hidden" },
				middle: {},
				last: { root: "after:hidden" },
				only: { root: "before:hidden after:hidden" }
			},
			direction: {
				vertical: {},
				horizontal: { root: "origin-[--spacing(5)_--spacing(5)] rotate-90" }
			},
			fill: {
				true: { dot: "bg-(--dot)" },
				false: { dot: "bg-(--fill)" }
			},
			tail: {
				true: {},
				false: {}
			}
		},
		compoundVariants: [
			{
				tail: true,
				band: ["last", "only"],
				class: {
					root: [
						"after:block after:w-px after:border-l-0",
						"after:bg-linear-to-b",
						"after:from-(--line)",
						"after:to-transparent"
					]
				}
			}
		],
		defaultVariants: {
			band: "middle",
			fill: false,
			tail: false
		}
	});

	export type GraphCellVariants = VariantProps<typeof graphCell>;
</script>

<script lang="ts">
	interface Props {
		band?: Band;
		direction?: GraphCellVariants["direction"];
		/** solid dot in `--graph-dot`, else hollow in `--graph-fill` */
		fill?: boolean;
		/** on `last` / `only`, trail the lower line off in a fade -- the run continues */
		tail?: boolean;
		class?: string;
		style?: string;
	}

	let {
		band = "middle",
		direction = "vertical",
		fill = false,
		tail = false,
		class: className,
		style,
		...rest
	}: Props = $props();

	const classes = $derived(graphCell({ band, direction, fill, tail }));
</script>

<div class="graph-cell {classes.root({ class: className })}" {style} {...rest}>
	<div class="flex size-10 items-center justify-center">
		{#if band !== "none"}
			<div class={classes.dot()}></div>
		{/if}
	</div>
</div>

<style>
	.graph-cell {
		/* the fallbacks resolve once here instead of inline on every utility. they can't be
		   declared as `--graph-line` etc. directly -- a declaration on this element would beat
		   an ancestor's, and the caller could never set the colour from above. */
		--line: var(--graph-line, var(--color-border-strong));
		--dot: var(--graph-dot, var(--line));
		--fill: var(--graph-fill, var(--color-background-default));
	}
</style>