Skip to content

Commit

sudo: selects drawn as a native select with our own chevron, the browser's arrow hidden

syntaqxcommitted Parent3defd24Browse files
1 file+18−20/1 viewed
+18−2
33 * sets a `style` attribute: the content security policy allows no inline
44 * styles, so sizes and colours that vary are drawn as SVG attributes.
55 */
6−import { AlertTriangle, CheckCircle2, Info } from "lucide-react";
6+import { AlertTriangle, CheckCircle2, ChevronDown, Info } from "lucide-react";
77 import type { ComponentProps, ReactNode } from "react";
88 import { Link, type LinkProps, useRouteLoaderData } from "react-router";
99
5353 return <textarea {...NOT_A_CREDENTIAL} {...props} className={`${CONTROL} ${className}`} />;
5454 }
5555
56+/** Classes that size or place the control, which belong on its wrapper. */
57+const LAYOUT = /^(?:[a-z0-9]+:)*(?:w-|min-w-|max-w-|flex-|shrink|grow|basis-|self-|col-|row-|order-|m[trblxy]?-)/;
58+
59+/**
60+ * A native select drawn as shadcn's NativeSelect: the browser's own arrow
61+ * hidden, a chevron of ours in its place. Native because sudo ships no
62+ * JavaScript, so it still opens, types to search and works on phones.
63+ */
5664 export function Select({ className = "", ...props }: ComponentProps<"select">) {
57− return <select {...props} className={`${CONTROL} ${className}`} />;
65+ const classes = className.split(/\s+/).filter(Boolean);
66+ const outer = classes.filter((name) => LAYOUT.test(name)).join(" ");
67+ const inner = classes.filter((name) => !LAYOUT.test(name)).join(" ");
68+ return (
69+ <span className={`relative block w-full ${outer}`}>
70+ <select {...props} className={`${CONTROL} cursor-pointer appearance-none pr-9 ${inner}`} />
71+ <ChevronDown aria-hidden className="pointer-events-none absolute top-1/2 right-3 size-4 -translate-y-1/2 text-muted" />
72+ </span>
73+ );
5874 }
5975
6076 type Variant = "primary" | "quiet" | "danger" | "lavender";