Start time grid #62
89
frontend/src/components/Choicebox/Choicebox.tsx
Normal file
89
frontend/src/components/Choicebox/Choicebox.tsx
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import type { InputHTMLAttributes } from "react";
|
||||||
|
import clsx from "clsx";
|
||||||
|
|
||||||
|
export interface ChoiceboxProps
|
||||||
|
extends Omit<InputHTMLAttributes<HTMLInputElement>, "type"> {
|
||||||
|
primaryText: string;
|
||||||
|
secondaryText?: string;
|
||||||
|
unavailable?: boolean;
|
||||||
|
fitContent?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseClasses = clsx(
|
||||||
|
"flex items-center gap-(--spacing-md)",
|
||||||
|
"border-[length:var(--border-width-sm)]",
|
||||||
|
"rounded-(--border-radius-md)",
|
||||||
|
"p-(--padding-md)",
|
||||||
|
);
|
||||||
|
|
||||||
|
const availableClasses = clsx(
|
||||||
|
"bg-sky-20 border-sky-100",
|
||||||
|
"cursor-pointer",
|
||||||
|
// Hover
|
||||||
|
"hover:bg-sky-35",
|
||||||
|
// Focus
|
||||||
|
"focus-within:bg-sky-20 focus-within:border-primary",
|
||||||
|
"focus-within:outline focus-within:outline-sky-100 focus-within:outline-[length:var(--border-width-lg)]",
|
||||||
|
// Selected
|
||||||
|
"has-[:checked]:bg-sky-70 has-[:checked]:border-secondary",
|
||||||
|
// Selected + Focus
|
||||||
|
"has-[:checked]:focus-within:bg-sky-35",
|
||||||
|
);
|
||||||
|
|
||||||
|
const unavailableClasses = clsx(
|
||||||
|
"bg-base-canvas border-base-ink-soft",
|
||||||
|
"cursor-not-allowed",
|
||||||
|
);
|
||||||
|
|
||||||
|
const radioClasses = clsx(
|
||||||
|
"appearance-none shrink-0",
|
||||||
|
"w-[var(--font-size-body-md)] h-[var(--font-size-body-md)]",
|
||||||
|
"rounded-full",
|
||||||
|
"border-[length:var(--border-width-sm)] border-base-ink-medium",
|
||||||
|
"bg-base-canvas",
|
||||||
|
"outline-none",
|
||||||
|
// Selected
|
||||||
|
"checked:border-primary",
|
||||||
|
"checked:bg-[radial-gradient(circle,var(--color-primary)_50%,var(--color-base-canvas)_50%)]",
|
||||||
|
);
|
||||||
|
|
||||||
|
export default function Choicebox({
|
||||||
|
primaryText,
|
||||||
|
secondaryText,
|
||||||
|
unavailable = false,
|
||||||
|
fitContent = false,
|
||||||
|
className = "",
|
||||||
|
disabled,
|
||||||
|
...props
|
||||||
|
}: ChoiceboxProps) {
|
||||||
|
const isDisabled = unavailable || disabled;
|
||||||
|
const textColorClass = unavailable ? "text-base-ink-placeholder" : "text-primary";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<label
|
||||||
|
className={clsx(
|
||||||
|
baseClasses,
|
||||||
|
unavailable ? unavailableClasses : availableClasses,
|
||||||
|
fitContent ? "w-fit" : "w-full",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col flex-1 min-w-0">
|
||||||
|
<span className={clsx("body-bold-lg break-words", textColorClass)}>
|
||||||
|
{primaryText}
|
||||||
|
</span>
|
||||||
|
{secondaryText && (
|
||||||
|
<span className={clsx("body-light-sm break-words", textColorClass)}>
|
||||||
|
{secondaryText}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
className={radioClasses}
|
||||||
|
disabled={isDisabled}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user