import { Platform } from 'react-native'; import { cva, type VariantProps } from 'class-variance-authority'; import { TRANSITION_COLORS } from '../../styles/constants'; // ── Root (row): density, separator, interactive feedback ─────────────────── // // Web hover/active/focus paint an absolutely positioned bg-content-primary overlay at // --opacity-hover (8%) / --opacity-hover-overlay (20%) / --opacity-selected (12%), plus a focus // ring on focus-visible. State is driven by the data attributes the primitive root emits // (data-hover / data-active / data-focus-visible) — or by the composed child on `asChild` // (e.g. Link emits the same attributes). Native active uses a flat tint because ::before // pseudo-elements are web-only. // // TODO: Handle native background color hover/focus transitions export const listItemRootVariants = cva( [ 'relative flex-row self-stretch w-full overflow-clip px-5 bg-transparent', 'data-[disabled=true]:opacity-[var(--opacity-disabled)] data-[disabled=true]:pointer-events-none', TRANSITION_COLORS, Platform.select({ web: [ 'outline-none', 'before:pointer-events-none before:absolute before:inset-0 before:bg-content-primary before:opacity-0 before:content-[""]', 'before:transition-opacity before:duration-150 before:ease-out', 'data-[hover=true]:before:opacity-[var(--opacity-hover)]', // Pressed wins over hover via the compound selector (higher specificity), mirroring Button. 'data-[active=true]:data-[hover=true]:before:opacity-[var(--opacity-hover-overlay)]', 'data-[focus-visible=true]:before:opacity-[var(--opacity-selected)]', 'data-[focus-visible=true]:ring-2 data-[focus-visible=true]:ring-stroke-focus data-[focus-visible=true]:ring-offset-2', ].join(' '), default: 'data-[active=true]:bg-surface-primary-hover', }), ], { variants: { size: { default: 'gap-3 py-3 min-h-16', compact: 'gap-3 py-2 min-h-12', }, showSeparator: { true: [ 'border-b border-solid border-stroke-secondary', Platform.select({ web: 'before:-bottom-px', default: '' }), ].join(' '), false: 'border-b-0', }, crossAlign: { center: 'items-center', start: 'items-start', }, }, defaultVariants: { size: 'default', showSeparator: true, crossAlign: 'center', }, }, ); // ── Leading / trailing slots ───────────────────────────────────────────────── export const listItemLeadingSlotVariants = cva([ 'relative z-10 flex shrink-0 items-center justify-center', ]); export const listItemTrailingSlotVariants = cva( ['relative z-10 flex shrink-0 flex-row justify-end gap-2'], { variants: { crossAlign: { center: 'items-center', start: 'items-start self-start', }, }, defaultVariants: { crossAlign: 'center', }, }, ); // ── Content stack (title / description / meta) ─────────────────────────────── export const listItemContentVariants = cva('relative z-10 flex min-w-0 flex-1 flex-col gap-1', { variants: { crossAlign: { center: 'justify-center', start: 'justify-start', }, }, defaultVariants: { crossAlign: 'center', }, }); // ── Typography ─────────────────────────────────────────────────────────────── export const listItemTitleVariants = cva('body-md font-medium text-content-primary'); export const listItemDescriptionVariants = cva('body-sm text-content-secondary'); export const listItemMetaVariants = cva('body-sm text-content-tertiary'); // ── Section header (group label row inside lists / cards) ─────────────────── export const listItemSectionHeaderVariants = cva( [ 'flex-row items-center justify-between gap-2 border-b border-solid px-4 py-3', 'border-stroke-secondary', ], { variants: { showDivider: { true: ['border-t-4 border-solid border-t-surface-brand-strong/40', 'border-x-0'], false: 'border-t-0', }, }, defaultVariants: { showDivider: true, }, }, ); /** Default section-title typography; applied by `ListItem.SectionHeader` for string/number children. Exported for rare custom composition. */ export const listItemSectionHeaderLabelVariants = cva( 'text-xs font-semibold uppercase tracking-wide text-content-secondary', ); export type ListItemVariantProps = VariantProps;