~james/core

core/web/src/lib/components/ui/rangeMode.ts -rw-r--r-- 1.3 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
import type { Range } from "#lib/components/ui/RangeSelector.svelte";

export type RangeMode =
	| "resize"
	| "resize-top"
	| "resize-top-push"
	| "resize-bottom"
	| "resize-bottom-push"
	| "select-top"
	| "select-bottom";

type Options = {
	cur: Range;
	minLength: number;
	count: number;
	/** which end of `cur` follows the pointer -- "resize" only */
	moving: "start" | "end";
};

/** the span for pointing at row `at`, or null when it won't fit the list or `minLength` */
export function rangeFor(mode: RangeMode, at: number, o: Options): Range | null {
	const { cur, minLength, count } = o;

	const fit = (start: number, end: number) =>
		start >= 0 && end < count && end - start + 1 >= minLength ? { start, end } : null;

	switch (mode) {
		case "resize": {
			const fixed = o.moving === "start" ? cur.end : cur.start;
			return fit(Math.min(at, fixed), Math.max(at, fixed));
		}
		// the sub-minLength span past the other handle is what keeps these one-sided
		case "resize-top":
			return fit(at, cur.end);
		case "resize-bottom":
			return fit(cur.start, at);
		case "resize-top-push":
			return fit(at, Math.max(cur.end, at + minLength - 1));
		case "resize-bottom-push":
			return fit(Math.min(cur.start, at - minLength + 1), at);
		case "select-top":
			return fit(at, at + minLength - 1);
		case "select-bottom":
			return fit(at - minLength + 1, at);
	}
}