mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage
synced 2026-04-11 18:28:15 +00:00
94 lines
4.0 KiB
TypeScript
94 lines
4.0 KiB
TypeScript
import { isValidElement, useContext } from "react";
|
|
import { faCheck } from "@fortawesome/pro-duotone-svg-icons";
|
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
|
import type { ListBoxItemProps as AriaListBoxItemProps } from "react-aria-components";
|
|
import { ListBoxItem as AriaListBoxItem, Text as AriaText } from "react-aria-components";
|
|
import { Avatar } from "@/components/base/avatar/avatar";
|
|
import { cx } from "@/utils/cx";
|
|
import { isReactComponent } from "@/utils/is-react-component";
|
|
import type { SelectItemType } from "./select";
|
|
import { SelectContext } from "./select";
|
|
|
|
const sizes = {
|
|
sm: "p-2 pr-2.5",
|
|
md: "p-2.5 pl-2",
|
|
};
|
|
|
|
interface SelectItemProps extends Omit<AriaListBoxItemProps<SelectItemType>, "id">, SelectItemType {}
|
|
|
|
export const SelectItem = ({ label, id, value, avatarUrl, supportingText, isDisabled, icon: Icon, className, children, ...props }: SelectItemProps) => {
|
|
const { size } = useContext(SelectContext);
|
|
|
|
const labelOrChildren = label || (typeof children === "string" ? children : "");
|
|
const textValue = supportingText ? labelOrChildren + " " + supportingText : labelOrChildren;
|
|
|
|
return (
|
|
<AriaListBoxItem
|
|
id={id}
|
|
value={
|
|
value ?? {
|
|
id,
|
|
label: labelOrChildren,
|
|
avatarUrl,
|
|
supportingText,
|
|
isDisabled,
|
|
icon: Icon,
|
|
}
|
|
}
|
|
textValue={textValue}
|
|
isDisabled={isDisabled}
|
|
{...props}
|
|
className={(state) => cx("w-full px-1.5 py-px outline-hidden", typeof className === "function" ? className(state) : className)}
|
|
>
|
|
{(state) => (
|
|
<div
|
|
className={cx(
|
|
"flex cursor-pointer items-center gap-2 rounded-md outline-hidden select-none",
|
|
state.isSelected && "bg-active",
|
|
state.isDisabled && "cursor-not-allowed",
|
|
state.isFocused && "bg-primary_hover",
|
|
state.isFocusVisible && "ring-2 ring-focus-ring ring-inset",
|
|
|
|
// Icon styles
|
|
"*:data-icon:size-5 *:data-icon:shrink-0 *:data-icon:text-fg-quaternary",
|
|
state.isDisabled && "*:data-icon:text-fg-disabled",
|
|
|
|
sizes[size],
|
|
)}
|
|
>
|
|
{avatarUrl ? (
|
|
<Avatar aria-hidden="true" size="xs" src={avatarUrl} alt={label} />
|
|
) : isReactComponent(Icon) ? (
|
|
<Icon data-icon aria-hidden="true" />
|
|
) : isValidElement(Icon) ? (
|
|
Icon
|
|
) : null}
|
|
|
|
<div className="flex w-full min-w-0 flex-1 flex-wrap gap-x-2">
|
|
<AriaText
|
|
slot="label"
|
|
className={cx("truncate text-md font-medium whitespace-nowrap text-primary", state.isDisabled && "text-disabled")}
|
|
>
|
|
{label || (typeof children === "function" ? children(state) : children)}
|
|
</AriaText>
|
|
|
|
{supportingText && (
|
|
<AriaText slot="description" className={cx("text-md whitespace-nowrap text-tertiary", state.isDisabled && "text-disabled")}>
|
|
{supportingText}
|
|
</AriaText>
|
|
)}
|
|
</div>
|
|
|
|
{state.isSelected && (
|
|
<FontAwesomeIcon
|
|
icon={faCheck}
|
|
aria-hidden="true"
|
|
className={cx("ml-auto text-fg-brand-primary", size === "sm" ? "size-4" : "size-5", state.isDisabled && "text-fg-disabled")}
|
|
/>
|
|
)}
|
|
</div>
|
|
)}
|
|
</AriaListBoxItem>
|
|
);
|
|
};
|