Files
kandev/apps/web/lib/utils/selector-options.ts
2026-09-10 18:14:05 +01:00

58 lines
1.9 KiB
TypeScript

/** Return a new list with the current option first when it is present. */
export function prioritizeSelectedOption<T>(
options: readonly T[],
selectedValue: string | null | undefined,
getValue: (option: T) => string,
): T[] {
if (!selectedValue) return [...options];
const selectedIndex = options.findIndex((option) => getValue(option) === selectedValue);
if (selectedIndex <= 0) return [...options];
const selected = options[selectedIndex];
if (selected === undefined) return [...options];
return [selected, ...options.slice(0, selectedIndex), ...options.slice(selectedIndex + 1)];
}
export function prioritizeValueOption<T extends { value: string }>(
options: readonly T[],
selectedValue: string | null | undefined,
): T[] {
return prioritizeSelectedOption(options, selectedValue, (option) => option.value);
}
export function prioritizeIdOption<T extends { id: string }>(
options: readonly T[],
selectedValue: string | null | undefined,
): T[] {
return prioritizeSelectedOption(options, selectedValue, (option) => option.id);
}
export function selectorOptionClassName(
selected: boolean,
disabled = false,
touchTarget = false,
): string {
const heightClass = touchTarget
? "min-h-7 max-md:min-h-12 [@media(pointer:coarse)]:min-h-12"
: "min-h-7 max-md:min-h-11 [@media(pointer:coarse)]:min-h-11";
return [
`relative ${heightClass} border border-transparent pr-7`,
selected &&
"border-primary/50 bg-card font-medium data-[selected=true]:ring-2 data-[selected=true]:ring-primary/40",
disabled && "opacity-40 cursor-not-allowed",
]
.filter(Boolean)
.join(" ");
}
export function configClassName(selected: boolean): string {
return [
"h-auto min-h-11 w-full min-w-0 cursor-pointer justify-start border border-transparent px-2 py-2 text-left sm:min-h-9",
selected && "border-primary/50 bg-card font-medium hover:bg-card",
]
.filter(Boolean)
.join(" ");
}