// AlignUI Select v0.0.0 'use client'; import * as React from 'react'; import * as ScrollAreaPrimitives from '@radix-ui/react-scroll-area'; import * as SelectPrimitives from '@radix-ui/react-select'; import { Slottable } from '@radix-ui/react-slot'; import { ChevronDown, Check } from 'lucide-react'; import { cn } from '@/utils/cn'; import type { PolymorphicComponentProps } from '@/utils/polymorphic'; import { tv, type VariantProps } from '@/utils/tv'; export const selectVariants = tv({ slots: { triggerRoot: [ // base 'group/trigger bg-bg-white-0 shadow-regular-xs ring-stroke-soft-200 min-w-0 shrink-0 ring-1 outline-none ring-inset', 'text-paragraph-sm text-text-strong-950', 'flex items-center text-left', 'transition duration-200 ease-out', // hover 'hover:bg-bg-weak-50 hover:ring-transparent', // focus 'focus:shadow-button-important-focus focus:ring-stroke-strong-950 focus:outline-none', 'focus:text-text-strong-950 data-[placeholder]:focus:text-text-strong-950', // disabled 'disabled:bg-bg-weak-50 disabled:text-text-disabled-300 data-[placeholder]:disabled:text-text-disabled-300 disabled:pointer-events-none disabled:shadow-none disabled:ring-transparent', // placeholder state 'data-[placeholder]:text-text-sub-600', ], triggerArrow: [ // base 'ml-auto size-5 shrink-0', 'transition duration-200 ease-out', // placeholder state 'group-data-[placeholder]/trigger:text-text-soft-400', // filled state 'text-text-sub-600', // hover 'group-hover/trigger:text-text-sub-600 group-data-[placeholder]/trigger:group-hover:text-text-sub-600', // focus 'group-focus/trigger:text-text-strong-950 group-data-[placeholder]/trigger:group-focus/trigger:text-text-strong-950', // disabled 'group-disabled/trigger:text-text-disabled-300 group-data-[placeholder]/trigger:group-disabled/trigger:text-text-disabled-300', // open 'group-data-[state=open]/trigger:rotate-180', ], triggerIcon: [ // base 'text-text-sub-600 h-5 w-auto min-w-0 shrink-0 object-contain', 'transition duration-200 ease-out', // placeholder state 'group-data-[placeholder]/trigger:text-text-soft-400', // hover 'group-hover/trigger:text-text-sub-600 group-data-[placeholder]/trigger:group-hover:text-text-sub-600', // disabled 'group-disabled/trigger:text-text-disabled-300 group-data-[placeholder]/trigger:group-disabled/trigger:text-text-disabled-300', 'group-disabled/trigger:[&:not(.lucide)]:opacity-[.48]', ], selectItemIcon: [ 'text-text-sub-600 size-5 shrink-0 bg-[length:1.25rem]', // 'group-has-[&]-ml-0.5', // disabled '[[data-disabled]_&]:text-text-disabled-300 [[data-disabled]_&:not(.lucide)]:opacity-[.48]', ], }, variants: { size: { medium: {}, small: {}, xsmall: {}, }, variant: { default: { triggerRoot: 'w-full', }, compact: { triggerRoot: 'w-auto', }, compactForInput: { triggerRoot: [ // base 'w-auto rounded-none shadow-none ring-0', // focus 'focus:bg-bg-weak-50 focus:shadow-none focus:ring-0 focus:ring-transparent', ], }, inline: { triggerRoot: [ // base 'text-text-sub-600 h-5 min-h-5 w-auto gap-0 rounded-none bg-transparent p-0 shadow-none ring-0', // hover 'hover:text-text-strong-950 hover:bg-transparent', // focus 'focus:shadow-none', // open 'data-[state=open]:text-text-strong-950', ], triggerIcon: [ // base 'text-text-soft-400 mr-1.5', // hover 'group-hover/trigger:text-text-sub-600', // open 'group-data-[state=open]/trigger:text-text-sub-600', ], triggerArrow: [ // base 'ml-0.5', // hover 'group-hover/trigger:text-text-strong-950', // open 'group-data-[state=open]/trigger:text-text-strong-950', ], selectItemIcon: 'text-text-soft-400 group-hover/trigger:text-text-sub-600', }, }, hasError: { true: { triggerRoot: [ // base 'ring-error-base', // focus 'focus:shadow-button-error-focus focus:ring-error-base', ], }, }, }, compoundVariants: [ //#region default { size: 'medium', variant: 'default', class: { triggerRoot: 'rounded-10 h-10 min-h-10 gap-2 pr-2.5 pl-3', }, }, { size: 'small', variant: 'default', class: { triggerRoot: 'h-9 min-h-9 gap-2 rounded-lg pr-2 pl-2.5', }, }, { size: 'xsmall', variant: 'default', class: { triggerRoot: 'h-8 min-h-8 gap-1.5 rounded-lg pr-1.5 pl-2', }, }, //#endregion //#region compact { size: 'medium', variant: 'compact', class: { triggerRoot: 'rounded-10 h-10 gap-1 pr-2.5 pl-3', triggerIcon: '-ml-0.5', selectItemIcon: 'group-has-[&]/trigger:-ml-0.5', }, }, { size: 'small', variant: 'compact', class: { triggerRoot: 'h-9 gap-1 rounded-lg pr-2 pl-3', triggerIcon: '-ml-0.5', selectItemIcon: 'group-has-[&]/trigger:-ml-0.5', }, }, { size: 'xsmall', variant: 'compact', class: { triggerRoot: 'h-8 gap-0.5 rounded-lg pr-1.5 pl-2.5', triggerIcon: '-ml-0.5 size-4', selectItemIcon: 'size-4 bg-[length:1rem] group-has-[&]/trigger:-ml-0.5', }, }, //#endregion //#region compactForInput { size: 'medium', variant: 'compactForInput', class: { triggerRoot: 'pr-2 pl-2.5', triggerIcon: 'mr-2', triggerArrow: 'ml-0.5', }, }, { size: 'small', variant: 'compactForInput', class: { triggerRoot: 'px-2', triggerIcon: 'mr-2', triggerArrow: 'ml-0.5', }, }, { size: 'xsmall', variant: 'compactForInput', class: { triggerRoot: 'pr-1.5 pl-2', triggerIcon: 'mr-1.5 size-4', triggerArrow: 'ml-0.5', selectItemIcon: 'size-4 bg-[length:1rem]', }, }, //#endregion ], defaultVariants: { variant: 'default', size: 'medium', }, }); type SelectContextType = Pick< VariantProps, 'variant' | 'size' | 'hasError' >; const SelectContext = React.createContext({ size: 'medium', variant: 'default', hasError: false, }); const useSelectContext = () => React.useContext(SelectContext); const SelectRoot = ({ size = 'medium', variant = 'default', hasError, ...rest }: React.ComponentProps & SelectContextType) => { return ( ); }; SelectRoot.displayName = 'SelectRoot'; const SelectGroup = SelectPrimitives.Group; SelectGroup.displayName = 'SelectGroup'; const SelectValue = SelectPrimitives.Value; SelectValue.displayName = 'SelectValue'; const SelectSeparator = SelectPrimitives.Separator; SelectSeparator.displayName = 'SelectSeparator'; const SelectGroupLabel = SelectPrimitives.Label; SelectGroupLabel.displayName = 'SelectGroupLabel'; const SELECT_TRIGGER_ICON_NAME = 'SelectTriggerIcon'; const SelectTrigger = React.forwardRef< React.ComponentRef, React.ComponentPropsWithoutRef >(({ className, children, ...rest }, forwardedRef) => { const { size, variant, hasError } = useSelectContext(); const { triggerRoot, triggerArrow } = selectVariants({ size, variant, hasError, }); return ( {children} ); }); SelectTrigger.displayName = 'SelectTrigger'; function TriggerIcon({ as, className, ...rest }: PolymorphicComponentProps) { const Component = as || 'div'; const { size, variant, hasError } = useSelectContext(); const { triggerIcon } = selectVariants({ size, variant, hasError }); return ; } TriggerIcon.displayName = SELECT_TRIGGER_ICON_NAME; const SelectContent = React.forwardRef< React.ComponentRef, React.ComponentPropsWithoutRef >( ( { className, position = 'popper', children, sideOffset = 8, collisionPadding = 8, ...rest }, forwardedRef, ) => ( {children} ), ); SelectContent.displayName = 'SelectContent'; const SelectItem = React.forwardRef< React.ComponentRef, React.ComponentPropsWithoutRef >(({ className, children, ...rest }, forwardedRef) => { const { size } = useSelectContext(); return ( {typeof children === 'string' ? ( {children} ) : ( children )} ); }); SelectItem.displayName = 'SelectItem'; function SelectItemIcon({ as, className, ...rest }: PolymorphicComponentProps) { const { size, variant } = useSelectContext(); const { selectItemIcon } = selectVariants({ size, variant }); const Component = as || 'div'; return ( ); } export { SelectRoot as Root, SelectContent as Content, SelectGroup as Group, SelectGroupLabel as GroupLabel, SelectItem as Item, SelectItemIcon as ItemIcon, SelectSeparator as Separator, SelectTrigger as Trigger, TriggerIcon, SelectValue as Value, };