Files
helix-engage/src/components/base/select/select-item.tsx
2026-03-23 16:41:58 +05:30

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>
);
};