@fanvue/ui 2.11.0 → 2.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -100,7 +100,7 @@ const Autocomplete = React__namespace.forwardRef((props, ref) => {
100
100
  "div",
101
101
  {
102
102
  className: cn.cn(
103
- "flex flex-wrap items-center overflow-hidden rounded-sm border bg-neutral-alphas-100 has-focus-visible:outline-none motion-safe:transition-colors",
103
+ "flex flex-wrap items-center overflow-hidden rounded-sm border bg-neutral-alphas-100 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors",
104
104
  error ? "border-error-content" : "border-transparent",
105
105
  !disabled && !error && "hover:border-neutral-alphas-400",
106
106
  ac.isOpen && !error && !disabled && "border-neutral-alphas-400",
@@ -1 +1 @@
1
- {"version":3,"file":"Autocomplete.cjs","sources":["../../../../src/components/Autocomplete/Autocomplete.tsx"],"sourcesContent":["import * as Popover from \"@radix-ui/react-popover\";\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { FLOATING_CONTENT_COLLISION_PADDING } from \"@/utils/floatingContentCollisionPadding\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\nimport { SpinnerIcon } from \"../Icons/SpinnerIcon\";\nimport { AutocompleteDropdownContent } from \"./AutocompleteDropdownContent\";\nimport { AutocompleteTag } from \"./AutocompleteTag\";\nimport { useAutocomplete } from \"./useAutocomplete\";\n\nexport type AutocompleteSize = \"48\" | \"40\" | \"32\";\n\nexport interface AutocompleteOption {\n value: string;\n label?: string;\n disabled?: boolean;\n}\n\ninterface AutocompleteBaseProps {\n label?: string;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n helperText?: string;\n /** @default \"48\" */\n size?: AutocompleteSize;\n /** @default false */\n error?: boolean;\n errorMessage?: string;\n placeholder?: string;\n leftIcon?: React.ReactNode;\n /** @default false */\n fullWidth?: boolean;\n /** @default false */\n disabled?: boolean;\n /** @default false */\n clearable?: boolean;\n clearAriaLabel?: string;\n id?: string;\n className?: string;\n options: AutocompleteOption[];\n inputValue?: string;\n onInputChange?: (value: string) => void;\n filterFn?: (option: AutocompleteOption, query: string) => boolean;\n /** @default false */\n creatable?: boolean;\n creatableLabel?: (inputValue: string) => string;\n onCreate?: (inputValue: string) => void;\n /** @default false */\n loading?: boolean;\n loadingText?: string;\n emptyText?: string;\n renderOption?: (\n option: AutocompleteOption,\n state: { selected: boolean; active: boolean },\n ) => React.ReactNode;\n renderTag?: (option: AutocompleteOption, onRemove: () => void) => React.ReactNode;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\ninterface AutocompleteSingleProps extends AutocompleteBaseProps {\n multiple?: false;\n value?: string | null;\n defaultValue?: string | null;\n onChange?: (value: string | null) => void;\n}\n\ninterface AutocompleteMultiProps extends AutocompleteBaseProps {\n multiple: true;\n value?: string[];\n defaultValue?: string[];\n onChange?: (values: string[]) => void;\n}\n\nexport type AutocompleteProps = AutocompleteSingleProps | AutocompleteMultiProps;\n\nconst CONTAINER_HEIGHT: Record<AutocompleteSize, string> = {\n \"48\": \"min-h-12\",\n \"40\": \"min-h-10\",\n \"32\": \"min-h-8\",\n};\n\nconst INPUT_SIZE_CLASSES: Record<AutocompleteSize, string> = {\n \"48\": \"typography-regular-body-lg\",\n \"40\": \"typography-regular-body-lg\",\n \"32\": \"typography-regular-body-md\",\n};\n\nconst PADDING_CLASSES: Record<AutocompleteSize, string> = {\n \"48\": \"px-4 py-1.5 gap-3\",\n \"40\": \"px-4 py-1 gap-3\",\n \"32\": \"px-3 py-1 gap-2\",\n};\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"Autocomplete: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Conditional JSX branches in the render template\nexport const Autocomplete = React.forwardRef<HTMLInputElement, AutocompleteProps>((props, ref) => {\n const {\n label,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n placeholder,\n leftIcon,\n fullWidth = false,\n disabled = false,\n clearable = false,\n clearAriaLabel = \"Clear\",\n className,\n loading = false,\n loadingText,\n emptyText = \"No results\",\n renderOption,\n renderTag,\n } = props;\n\n const ac = useAutocomplete(props);\n\n React.useImperativeHandle(ref, () => ac.inputRef.current as HTMLInputElement);\n\n warnMissingAccessibleName(label, ariaLabel, ariaLabelledby);\n\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n return (\n <Popover.Root open={ac.isOpen && !disabled} onOpenChange={ac.handleOpenChange}>\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-autocomplete-root=\"\"\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={ac.inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <Popover.Anchor asChild>\n {/* biome-ignore lint/a11y/noStaticElementInteractions: Container delegates click to the inner input */}\n {/* biome-ignore lint/a11y/useKeyWithClickEvents: Keyboard interaction is handled by the inner combobox input */}\n <div\n className={cn(\n \"flex flex-wrap items-center overflow-hidden rounded-sm border bg-neutral-alphas-100 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n ac.isOpen && !error && !disabled && \"border-neutral-alphas-400\",\n CONTAINER_HEIGHT[size],\n PADDING_CLASSES[size],\n disabled && \"opacity-50\",\n )}\n onClick={ac.handleContainerClick}\n >\n {leftIcon && (\n <div className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\">\n {leftIcon}\n </div>\n )}\n\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-1.5\">\n {ac.isMulti &&\n ac.selectedMultiOptions.map((opt) => (\n <AutocompleteTag\n key={opt.value}\n option={opt}\n disabled={disabled}\n onRemove={() => ac.toggleMulti(opt.value)}\n renderTag={renderTag}\n />\n ))}\n\n <input\n ref={ac.inputRef}\n id={ac.inputId}\n role=\"combobox\"\n type=\"text\"\n disabled={disabled}\n aria-expanded={ac.isOpen}\n aria-controls={ac.isOpen ? ac.listboxId : undefined}\n aria-activedescendant={ac.activeDescendantId}\n aria-autocomplete=\"list\"\n aria-describedby={bottomText ? ac.helperTextId : undefined}\n aria-invalid={error || undefined}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n autoComplete=\"off\"\n placeholder={\n ac.isMulti && ac.selectedMultiValues.length > 0 ? undefined : placeholder\n }\n value={ac.displayInputValue}\n onChange={ac.handleInputChange}\n onKeyDown={ac.handleKeyDown}\n onFocus={ac.handleFocus}\n onBlur={ac.handleBlur}\n className={cn(\n \"min-w-[40px] flex-1 truncate bg-transparent text-content-primary no-underline placeholder:text-content-secondary placeholder:opacity-40 focus:outline-none disabled:cursor-not-allowed\",\n INPUT_SIZE_CLASSES[size],\n )}\n />\n </div>\n\n <div className=\"flex shrink-0 items-center gap-1\">\n {loading && <SpinnerIcon className=\"size-4 animate-spin text-content-secondary\" />}\n {clearable && ac.hasClearableValue && !disabled && (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={clearAriaLabel}\n className=\"flex size-5 shrink-0 cursor-pointer items-center justify-center text-content-secondary hover:text-content-primary active:scale-95\"\n onClick={ac.handleClear}\n >\n <CloseIcon className=\"size-4\" />\n </button>\n )}\n <div className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\">\n <ChevronDownIcon\n className={cn(\"size-5 transition-transform\", ac.isOpen && \"rotate-180\")}\n />\n </div>\n </div>\n </div>\n </Popover.Anchor>\n\n <Popover.Portal>\n <Popover.Content\n sideOffset={4}\n collisionPadding={FLOATING_CONTENT_COLLISION_PADDING}\n onOpenAutoFocus={(e) => e.preventDefault()}\n onCloseAutoFocus={(e) => e.preventDefault()}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\" }}\n className={cn(\n \"w-max min-w-(--radix-popper-anchor-width) max-w-(--radix-popover-content-available-width) overflow-hidden rounded-sm border border-neutral-alphas-200 bg-bg-primary text-content-primary shadow-[0_4px_16px_rgba(0,0,0,0.10)]\",\n \"data-[state=closed]:animate-out data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n )}\n >\n <div\n ref={ac.listRef}\n id={ac.listboxId}\n role=\"listbox\"\n aria-label={label ?? ariaLabel ?? \"Options\"}\n aria-multiselectable={ac.isMulti || undefined}\n className=\"max-h-60 overflow-y-auto p-1\"\n >\n <AutocompleteDropdownContent\n loading={loading}\n loadingText={loadingText}\n emptyText={emptyText}\n visibleOptions={ac.visibleOptions}\n listboxId={ac.listboxId}\n activeIndex={ac.activeIndex}\n isMulti={ac.isMulti}\n selectedMultiValues={ac.selectedMultiValues}\n selectedValue={ac.selectedValue}\n onSelect={ac.handleSelect}\n onMouseEnter={ac.setActiveIndex}\n renderOption={renderOption}\n />\n </div>\n </Popover.Content>\n </Popover.Portal>\n\n {bottomText && (\n <p\n id={ac.helperTextId}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {bottomText}\n </p>\n )}\n </div>\n </Popover.Root>\n );\n});\n\nAutocomplete.displayName = \"Autocomplete\";\n"],"names":["React","useAutocomplete","jsx","Popover","jsxs","cn","AutocompleteTag","SpinnerIcon","CloseIcon","ChevronDownIcon","FLOATING_CONTENT_COLLISION_PADDING","AutocompleteDropdownContent"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8EA,MAAM,mBAAqD;AAAA,EACzD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,MAAM,qBAAuD;AAAA,EAC3D,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,MAAM,kBAAoD;AAAA,EACxD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,SAAS,0BAA0B,OAAgB,WAAoB,gBAAyB;AAC9F,MAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,QAAI,CAAC,SAAS,CAAC,aAAa,CAAC,gBAAgB;AAC3C,cAAQ;AAAA,QACN;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AACF;AAGO,MAAM,eAAeA,iBAAM,WAAgD,CAAC,OAAO,QAAQ;AAChG,QAAM;AAAA,IACJ;AAAA,IACA,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB;AAAA,IACA,OAAO;AAAA,IACP,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,EAAA,IACE;AAEJ,QAAM,KAAKC,gBAAAA,gBAAgB,KAAK;AAEhCD,mBAAM,oBAAoB,KAAK,MAAM,GAAG,SAAS,OAA2B;AAE5E,4BAA0B,OAAO,WAAW,cAAc;AAE1D,QAAM,aAAa,SAAS,eAAe,eAAe;AAE1D,SACEE,2BAAAA,IAACC,4BAAQ,MAAR,EAAa,MAAM,GAAG,UAAU,CAAC,UAAU,cAAc,GAAG,kBAC3D,UAAAC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC,GAAAA,GAAG,iBAAiB,aAAa,UAAU,SAAS;AAAA,MAC/D,0BAAuB;AAAA,MACvB,iBAAe,WAAW,KAAK;AAAA,MAC/B,cAAY,QAAQ,KAAK;AAAA,MAExB,UAAA;AAAA,QAAA,SACCH,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,SAAS,GAAG;AAAA,YACZ,WAAU;AAAA,YAET,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAILA,2BAAAA,IAACC,4BAAQ,QAAR,EAAe,SAAO,MAGrB,UAAAC,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAWC,GAAAA;AAAAA,cACT;AAAA,cACA,QAAQ,yBAAyB;AAAA,cACjC,CAAC,YAAY,CAAC,SAAS;AAAA,cACvB,GAAG,UAAU,CAAC,SAAS,CAAC,YAAY;AAAA,cACpC,iBAAiB,IAAI;AAAA,cACrB,gBAAgB,IAAI;AAAA,cACpB,YAAY;AAAA,YAAA;AAAA,YAEd,SAAS,GAAG;AAAA,YAEX,UAAA;AAAA,cAAA,YACCH,2BAAAA,IAAC,OAAA,EAAI,WAAU,2EACZ,UAAA,UACH;AAAA,cAGFE,2BAAAA,KAAC,OAAA,EAAI,WAAU,sDACZ,UAAA;AAAA,gBAAA,GAAG,WACF,GAAG,qBAAqB,IAAI,CAAC,QAC3BF,2BAAAA;AAAAA,kBAACI,gBAAAA;AAAAA,kBAAA;AAAA,oBAEC,QAAQ;AAAA,oBACR;AAAA,oBACA,UAAU,MAAM,GAAG,YAAY,IAAI,KAAK;AAAA,oBACxC;AAAA,kBAAA;AAAA,kBAJK,IAAI;AAAA,gBAAA,CAMZ;AAAA,gBAEHJ,2BAAAA;AAAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,KAAK,GAAG;AAAA,oBACR,IAAI,GAAG;AAAA,oBACP,MAAK;AAAA,oBACL,MAAK;AAAA,oBACL;AAAA,oBACA,iBAAe,GAAG;AAAA,oBAClB,iBAAe,GAAG,SAAS,GAAG,YAAY;AAAA,oBAC1C,yBAAuB,GAAG;AAAA,oBAC1B,qBAAkB;AAAA,oBAClB,oBAAkB,aAAa,GAAG,eAAe;AAAA,oBACjD,gBAAc,SAAS;AAAA,oBACvB,cAAY;AAAA,oBACZ,mBAAiB;AAAA,oBACjB,cAAa;AAAA,oBACb,aACE,GAAG,WAAW,GAAG,oBAAoB,SAAS,IAAI,SAAY;AAAA,oBAEhE,OAAO,GAAG;AAAA,oBACV,UAAU,GAAG;AAAA,oBACb,WAAW,GAAG;AAAA,oBACd,SAAS,GAAG;AAAA,oBACZ,QAAQ,GAAG;AAAA,oBACX,WAAWG,GAAAA;AAAAA,sBACT;AAAA,sBACA,mBAAmB,IAAI;AAAA,oBAAA;AAAA,kBACzB;AAAA,gBAAA;AAAA,cACF,GACF;AAAA,cAEAD,2BAAAA,KAAC,OAAA,EAAI,WAAU,oCACZ,UAAA;AAAA,gBAAA,WAAWF,2BAAAA,IAACK,YAAAA,aAAA,EAAY,WAAU,6CAAA,CAA6C;AAAA,gBAC/E,aAAa,GAAG,qBAAqB,CAAC,YACrCL,2BAAAA;AAAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,MAAK;AAAA,oBACL,UAAU;AAAA,oBACV,cAAY;AAAA,oBACZ,WAAU;AAAA,oBACV,SAAS,GAAG;AAAA,oBAEZ,UAAAA,2BAAAA,IAACM,UAAAA,WAAA,EAAU,WAAU,SAAA,CAAS;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAGlCN,2BAAAA,IAAC,OAAA,EAAI,WAAU,2EACb,UAAAA,2BAAAA;AAAAA,kBAACO,gBAAAA;AAAAA,kBAAA;AAAA,oBACC,WAAWJ,GAAAA,GAAG,+BAA+B,GAAG,UAAU,YAAY;AAAA,kBAAA;AAAA,gBAAA,EACxE,CACF;AAAA,cAAA,EAAA,CACF;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA,GAEJ;AAAA,QAEAH,2BAAAA,IAACC,4BAAQ,QAAR,EACC,UAAAD,2BAAAA;AAAAA,UAACC,4BAAQ;AAAA,UAAR;AAAA,YACC,YAAY;AAAA,YACZ,kBAAkBO,gCAAAA;AAAAA,YAClB,iBAAiB,CAAC,MAAM,EAAE,eAAA;AAAA,YAC1B,kBAAkB,CAAC,MAAM,EAAE,eAAA;AAAA,YAC3B,OAAO,EAAE,QAAQ,sCAAA;AAAA,YACjB,WAAWL,GAAAA;AAAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,cACA;AAAA,YAAA;AAAA,YAGF,UAAAH,2BAAAA;AAAAA,cAAC;AAAA,cAAA;AAAA,gBACC,KAAK,GAAG;AAAA,gBACR,IAAI,GAAG;AAAA,gBACP,MAAK;AAAA,gBACL,cAAY,SAAS,aAAa;AAAA,gBAClC,wBAAsB,GAAG,WAAW;AAAA,gBACpC,WAAU;AAAA,gBAEV,UAAAA,2BAAAA;AAAAA,kBAACS,4BAAAA;AAAAA,kBAAA;AAAA,oBACC;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA,gBAAgB,GAAG;AAAA,oBACnB,WAAW,GAAG;AAAA,oBACd,aAAa,GAAG;AAAA,oBAChB,SAAS,GAAG;AAAA,oBACZ,qBAAqB,GAAG;AAAA,oBACxB,eAAe,GAAG;AAAA,oBAClB,UAAU,GAAG;AAAA,oBACb,cAAc,GAAG;AAAA,oBACjB;AAAA,kBAAA;AAAA,gBAAA;AAAA,cACF;AAAA,YAAA;AAAA,UACF;AAAA,QAAA,GAEJ;AAAA,QAEC,cACCT,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,IAAI,GAAG;AAAA,YACP,WAAWG,GAAAA;AAAAA,cACT;AAAA,cACA,QAAQ,uBAAuB;AAAA,YAAA;AAAA,YAGhC,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAAA;AAAA,EAAA,GAGN;AAEJ,CAAC;AAED,aAAa,cAAc;;"}
1
+ {"version":3,"file":"Autocomplete.cjs","sources":["../../../../src/components/Autocomplete/Autocomplete.tsx"],"sourcesContent":["import * as Popover from \"@radix-ui/react-popover\";\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { FLOATING_CONTENT_COLLISION_PADDING } from \"@/utils/floatingContentCollisionPadding\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\nimport { SpinnerIcon } from \"../Icons/SpinnerIcon\";\nimport { AutocompleteDropdownContent } from \"./AutocompleteDropdownContent\";\nimport { AutocompleteTag } from \"./AutocompleteTag\";\nimport { useAutocomplete } from \"./useAutocomplete\";\n\nexport type AutocompleteSize = \"48\" | \"40\" | \"32\";\n\nexport interface AutocompleteOption {\n value: string;\n label?: string;\n disabled?: boolean;\n}\n\ninterface AutocompleteBaseProps {\n label?: string;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n helperText?: string;\n /** @default \"48\" */\n size?: AutocompleteSize;\n /** @default false */\n error?: boolean;\n errorMessage?: string;\n placeholder?: string;\n leftIcon?: React.ReactNode;\n /** @default false */\n fullWidth?: boolean;\n /** @default false */\n disabled?: boolean;\n /** @default false */\n clearable?: boolean;\n clearAriaLabel?: string;\n id?: string;\n className?: string;\n options: AutocompleteOption[];\n inputValue?: string;\n onInputChange?: (value: string) => void;\n filterFn?: (option: AutocompleteOption, query: string) => boolean;\n /** @default false */\n creatable?: boolean;\n creatableLabel?: (inputValue: string) => string;\n onCreate?: (inputValue: string) => void;\n /** @default false */\n loading?: boolean;\n loadingText?: string;\n emptyText?: string;\n renderOption?: (\n option: AutocompleteOption,\n state: { selected: boolean; active: boolean },\n ) => React.ReactNode;\n renderTag?: (option: AutocompleteOption, onRemove: () => void) => React.ReactNode;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\ninterface AutocompleteSingleProps extends AutocompleteBaseProps {\n multiple?: false;\n value?: string | null;\n defaultValue?: string | null;\n onChange?: (value: string | null) => void;\n}\n\ninterface AutocompleteMultiProps extends AutocompleteBaseProps {\n multiple: true;\n value?: string[];\n defaultValue?: string[];\n onChange?: (values: string[]) => void;\n}\n\nexport type AutocompleteProps = AutocompleteSingleProps | AutocompleteMultiProps;\n\nconst CONTAINER_HEIGHT: Record<AutocompleteSize, string> = {\n \"48\": \"min-h-12\",\n \"40\": \"min-h-10\",\n \"32\": \"min-h-8\",\n};\n\nconst INPUT_SIZE_CLASSES: Record<AutocompleteSize, string> = {\n \"48\": \"typography-regular-body-lg\",\n \"40\": \"typography-regular-body-lg\",\n \"32\": \"typography-regular-body-md\",\n};\n\nconst PADDING_CLASSES: Record<AutocompleteSize, string> = {\n \"48\": \"px-4 py-1.5 gap-3\",\n \"40\": \"px-4 py-1 gap-3\",\n \"32\": \"px-3 py-1 gap-2\",\n};\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"Autocomplete: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Conditional JSX branches in the render template\nexport const Autocomplete = React.forwardRef<HTMLInputElement, AutocompleteProps>((props, ref) => {\n const {\n label,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n placeholder,\n leftIcon,\n fullWidth = false,\n disabled = false,\n clearable = false,\n clearAriaLabel = \"Clear\",\n className,\n loading = false,\n loadingText,\n emptyText = \"No results\",\n renderOption,\n renderTag,\n } = props;\n\n const ac = useAutocomplete(props);\n\n React.useImperativeHandle(ref, () => ac.inputRef.current as HTMLInputElement);\n\n warnMissingAccessibleName(label, ariaLabel, ariaLabelledby);\n\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n return (\n <Popover.Root open={ac.isOpen && !disabled} onOpenChange={ac.handleOpenChange}>\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-autocomplete-root=\"\"\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={ac.inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <Popover.Anchor asChild>\n {/* biome-ignore lint/a11y/noStaticElementInteractions: Container delegates click to the inner input */}\n {/* biome-ignore lint/a11y/useKeyWithClickEvents: Keyboard interaction is handled by the inner combobox input */}\n <div\n className={cn(\n \"flex flex-wrap items-center overflow-hidden rounded-sm border bg-neutral-alphas-100 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n ac.isOpen && !error && !disabled && \"border-neutral-alphas-400\",\n CONTAINER_HEIGHT[size],\n PADDING_CLASSES[size],\n disabled && \"opacity-50\",\n )}\n onClick={ac.handleContainerClick}\n >\n {leftIcon && (\n <div className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\">\n {leftIcon}\n </div>\n )}\n\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-1.5\">\n {ac.isMulti &&\n ac.selectedMultiOptions.map((opt) => (\n <AutocompleteTag\n key={opt.value}\n option={opt}\n disabled={disabled}\n onRemove={() => ac.toggleMulti(opt.value)}\n renderTag={renderTag}\n />\n ))}\n\n <input\n ref={ac.inputRef}\n id={ac.inputId}\n role=\"combobox\"\n type=\"text\"\n disabled={disabled}\n aria-expanded={ac.isOpen}\n aria-controls={ac.isOpen ? ac.listboxId : undefined}\n aria-activedescendant={ac.activeDescendantId}\n aria-autocomplete=\"list\"\n aria-describedby={bottomText ? ac.helperTextId : undefined}\n aria-invalid={error || undefined}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n autoComplete=\"off\"\n placeholder={\n ac.isMulti && ac.selectedMultiValues.length > 0 ? undefined : placeholder\n }\n value={ac.displayInputValue}\n onChange={ac.handleInputChange}\n onKeyDown={ac.handleKeyDown}\n onFocus={ac.handleFocus}\n onBlur={ac.handleBlur}\n className={cn(\n \"min-w-[40px] flex-1 truncate bg-transparent text-content-primary no-underline placeholder:text-content-secondary placeholder:opacity-40 focus:outline-none disabled:cursor-not-allowed\",\n INPUT_SIZE_CLASSES[size],\n )}\n />\n </div>\n\n <div className=\"flex shrink-0 items-center gap-1\">\n {loading && <SpinnerIcon className=\"size-4 animate-spin text-content-secondary\" />}\n {clearable && ac.hasClearableValue && !disabled && (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={clearAriaLabel}\n className=\"flex size-5 shrink-0 cursor-pointer items-center justify-center text-content-secondary hover:text-content-primary active:scale-95\"\n onClick={ac.handleClear}\n >\n <CloseIcon className=\"size-4\" />\n </button>\n )}\n <div className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\">\n <ChevronDownIcon\n className={cn(\"size-5 transition-transform\", ac.isOpen && \"rotate-180\")}\n />\n </div>\n </div>\n </div>\n </Popover.Anchor>\n\n <Popover.Portal>\n <Popover.Content\n sideOffset={4}\n collisionPadding={FLOATING_CONTENT_COLLISION_PADDING}\n onOpenAutoFocus={(e) => e.preventDefault()}\n onCloseAutoFocus={(e) => e.preventDefault()}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\" }}\n className={cn(\n \"w-max min-w-(--radix-popper-anchor-width) max-w-(--radix-popover-content-available-width) overflow-hidden rounded-sm border border-neutral-alphas-200 bg-bg-primary text-content-primary shadow-[0_4px_16px_rgba(0,0,0,0.10)]\",\n \"data-[state=closed]:animate-out data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n )}\n >\n <div\n ref={ac.listRef}\n id={ac.listboxId}\n role=\"listbox\"\n aria-label={label ?? ariaLabel ?? \"Options\"}\n aria-multiselectable={ac.isMulti || undefined}\n className=\"max-h-60 overflow-y-auto p-1\"\n >\n <AutocompleteDropdownContent\n loading={loading}\n loadingText={loadingText}\n emptyText={emptyText}\n visibleOptions={ac.visibleOptions}\n listboxId={ac.listboxId}\n activeIndex={ac.activeIndex}\n isMulti={ac.isMulti}\n selectedMultiValues={ac.selectedMultiValues}\n selectedValue={ac.selectedValue}\n onSelect={ac.handleSelect}\n onMouseEnter={ac.setActiveIndex}\n renderOption={renderOption}\n />\n </div>\n </Popover.Content>\n </Popover.Portal>\n\n {bottomText && (\n <p\n id={ac.helperTextId}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {bottomText}\n </p>\n )}\n </div>\n </Popover.Root>\n );\n});\n\nAutocomplete.displayName = \"Autocomplete\";\n"],"names":["React","useAutocomplete","jsx","Popover","jsxs","cn","AutocompleteTag","SpinnerIcon","CloseIcon","ChevronDownIcon","FLOATING_CONTENT_COLLISION_PADDING","AutocompleteDropdownContent"],"mappings":"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"}
@@ -26,20 +26,39 @@ const SIZE_CLASS = {
26
26
  24: "size-6",
27
27
  32: "size-8"
28
28
  };
29
+ const LAYER_TRANSITION = "motion-safe:transition-opacity motion-safe:duration-150 motion-safe:ease-in-out";
30
+ const renderPath = (p) => p.sw !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
31
+ "path",
32
+ {
33
+ d: p.d,
34
+ stroke: "currentColor",
35
+ strokeWidth: p.sw,
36
+ strokeLinecap: "round",
37
+ strokeLinejoin: "round"
38
+ },
39
+ p.d
40
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
41
+ "path",
42
+ {
43
+ d: p.d,
44
+ fill: "currentColor",
45
+ fillRule: p.eo ? "evenodd" : void 0,
46
+ clipRule: p.eo ? "evenodd" : void 0
47
+ },
48
+ p.d
49
+ );
29
50
  const BaseIcon = React__namespace.forwardRef(
30
51
  ({ variants, size = 24, filled, className, ...props }, ref) => {
31
52
  const v = variants[size];
32
- let paths = v?.outlined ?? [];
33
- if (filled) {
34
- if (v?.filled) {
35
- paths = v.filled;
36
- } else if (process.env.NODE_ENV !== "production" && v) {
37
- console.warn(
38
- `[fanvue/ui] Icon at size=${size} has no 'filled' variant; falling back to outlined.`
39
- );
40
- }
53
+ const outlined = v?.outlined ?? [];
54
+ const filledPaths = v?.filled;
55
+ const showFilled = !!(filled && filledPaths);
56
+ if (filled && !filledPaths && v && process.env.NODE_ENV !== "production") {
57
+ console.warn(
58
+ `[fanvue/ui] Icon at size=${size} has no 'filled' variant; falling back to outlined.`
59
+ );
41
60
  }
42
- return /* @__PURE__ */ jsxRuntime.jsx(
61
+ return /* @__PURE__ */ jsxRuntime.jsxs(
43
62
  "svg",
44
63
  {
45
64
  ref,
@@ -48,28 +67,10 @@ const BaseIcon = React__namespace.forwardRef(
48
67
  "aria-hidden": "true",
49
68
  className: cn.cn(SIZE_CLASS[size], className),
50
69
  ...props,
51
- children: paths.map(
52
- (p) => p.sw !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
53
- "path",
54
- {
55
- d: p.d,
56
- stroke: "currentColor",
57
- strokeWidth: p.sw,
58
- strokeLinecap: "round",
59
- strokeLinejoin: "round"
60
- },
61
- p.d
62
- ) : /* @__PURE__ */ jsxRuntime.jsx(
63
- "path",
64
- {
65
- d: p.d,
66
- fill: "currentColor",
67
- fillRule: p.eo ? "evenodd" : void 0,
68
- clipRule: p.eo ? "evenodd" : void 0
69
- },
70
- p.d
71
- )
72
- )
70
+ children: [
71
+ /* @__PURE__ */ jsxRuntime.jsx("g", { className: cn.cn(LAYER_TRANSITION, showFilled ? "opacity-0" : "opacity-100"), children: outlined.map(renderPath) }),
72
+ filledPaths && /* @__PURE__ */ jsxRuntime.jsx("g", { className: cn.cn(LAYER_TRANSITION, showFilled ? "opacity-100" : "opacity-0"), children: filledPaths.map(renderPath) })
73
+ ]
73
74
  }
74
75
  );
75
76
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BaseIcon.cjs","sources":["../../../../src/components/Icons/BaseIcon.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport type { BaseIconProps, IconSize, IconVariants } from \"./types\";\n\nconst SIZE_CLASS: Record<IconSize, string> = {\n 16: \"size-4\",\n 24: \"size-6\",\n 32: \"size-8\",\n};\n\ninterface InternalBaseIconProps extends BaseIconProps {\n variants: IconVariants;\n}\n\n/**\n * Internal renderer for prop-based icons. Each public icon (e.g. `HeartIcon`)\n * is a thin wrapper that supplies its own `variants` table and re-exports\n * {@link BaseIconProps} as `*IconProps`. Not exported from the package.\n */\nexport const BaseIcon = React.forwardRef<SVGSVGElement, InternalBaseIconProps>(\n ({ variants, size = 24, filled, className, ...props }, ref) => {\n const v = variants[size];\n let paths = v?.outlined ?? [];\n if (filled) {\n if (v?.filled) {\n paths = v.filled;\n } else if (process.env.NODE_ENV !== \"production\" && v) {\n console.warn(\n `[fanvue/ui] Icon at size=${size} has no 'filled' variant; falling back to outlined.`,\n );\n }\n }\n return (\n <svg\n ref={ref}\n viewBox={`0 0 ${size} ${size}`}\n fill=\"none\"\n aria-hidden=\"true\"\n className={cn(SIZE_CLASS[size], className)}\n {...props}\n >\n {paths.map((p) =>\n p.sw !== undefined ? (\n <path\n key={p.d}\n d={p.d}\n stroke=\"currentColor\"\n strokeWidth={p.sw}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n ) : (\n <path\n key={p.d}\n d={p.d}\n fill=\"currentColor\"\n fillRule={p.eo ? \"evenodd\" : undefined}\n clipRule={p.eo ? \"evenodd\" : undefined}\n />\n ),\n )}\n </svg>\n );\n },\n);\n\nBaseIcon.displayName = \"BaseIcon\";\n"],"names":["React","jsx","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAIA,MAAM,aAAuC;AAAA,EAC3C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAWO,MAAM,WAAWA,iBAAM;AAAA,EAC5B,CAAC,EAAE,UAAU,OAAO,IAAI,QAAQ,WAAW,GAAG,MAAA,GAAS,QAAQ;AAC7D,UAAM,IAAI,SAAS,IAAI;AACvB,QAAI,QAAQ,GAAG,YAAY,CAAA;AAC3B,QAAI,QAAQ;AACV,UAAI,GAAG,QAAQ;AACb,gBAAQ,EAAE;AAAA,MACZ,WAAW,QAAQ,IAAI,aAAa,gBAAgB,GAAG;AACrD,gBAAQ;AAAA,UACN,4BAA4B,IAAI;AAAA,QAAA;AAAA,MAEpC;AAAA,IACF;AACA,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,SAAS,OAAO,IAAI,IAAI,IAAI;AAAA,QAC5B,MAAK;AAAA,QACL,eAAY;AAAA,QACZ,WAAWC,GAAAA,GAAG,WAAW,IAAI,GAAG,SAAS;AAAA,QACxC,GAAG;AAAA,QAEH,UAAA,MAAM;AAAA,UAAI,CAAC,MACV,EAAE,OAAO,SACPD,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cAEC,GAAG,EAAE;AAAA,cACL,QAAO;AAAA,cACP,aAAa,EAAE;AAAA,cACf,eAAc;AAAA,cACd,gBAAe;AAAA,YAAA;AAAA,YALV,EAAE;AAAA,UAAA,IAQTA,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cAEC,GAAG,EAAE;AAAA,cACL,MAAK;AAAA,cACL,UAAU,EAAE,KAAK,YAAY;AAAA,cAC7B,UAAU,EAAE,KAAK,YAAY;AAAA,YAAA;AAAA,YAJxB,EAAE;AAAA,UAAA;AAAA,QAKT;AAAA,MAEJ;AAAA,IAAA;AAAA,EAGN;AACF;AAEA,SAAS,cAAc;;"}
1
+ {"version":3,"file":"BaseIcon.cjs","sources":["../../../../src/components/Icons/BaseIcon.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport type { BaseIconProps, IconPath, IconSize, IconVariants } from \"./types\";\n\nconst SIZE_CLASS: Record<IconSize, string> = {\n 16: \"size-4\",\n 24: \"size-6\",\n 32: \"size-8\",\n};\n\nconst LAYER_TRANSITION =\n \"motion-safe:transition-opacity motion-safe:duration-150 motion-safe:ease-in-out\";\n\ninterface InternalBaseIconProps extends BaseIconProps {\n variants: IconVariants;\n}\n\nconst renderPath = (p: IconPath) =>\n p.sw !== undefined ? (\n <path\n key={p.d}\n d={p.d}\n stroke=\"currentColor\"\n strokeWidth={p.sw}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n ) : (\n <path\n key={p.d}\n d={p.d}\n fill=\"currentColor\"\n fillRule={p.eo ? \"evenodd\" : undefined}\n clipRule={p.eo ? \"evenodd\" : undefined}\n />\n );\n\n/**\n * Internal renderer for prop-based icons. Each public icon (e.g. `HeartIcon`)\n * is a thin wrapper that supplies its own `variants` table and re-exports\n * {@link BaseIconProps} as `*IconProps`. Not exported from the package.\n *\n * When a `filled` variant exists, both layers are rendered simultaneously and\n * the `filled` prop crossfades between them. The same `<svg>` and `<g>` nodes\n * persist across toggles so the transition is visible rather than an instant\n * remount.\n */\nexport const BaseIcon = React.forwardRef<SVGSVGElement, InternalBaseIconProps>(\n ({ variants, size = 24, filled, className, ...props }, ref) => {\n const v = variants[size];\n const outlined = v?.outlined ?? [];\n const filledPaths = v?.filled;\n const showFilled = !!(filled && filledPaths);\n\n if (filled && !filledPaths && v && process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[fanvue/ui] Icon at size=${size} has no 'filled' variant; falling back to outlined.`,\n );\n }\n\n return (\n <svg\n ref={ref}\n viewBox={`0 0 ${size} ${size}`}\n fill=\"none\"\n aria-hidden=\"true\"\n className={cn(SIZE_CLASS[size], className)}\n {...props}\n >\n <g className={cn(LAYER_TRANSITION, showFilled ? \"opacity-0\" : \"opacity-100\")}>\n {outlined.map(renderPath)}\n </g>\n {filledPaths && (\n <g className={cn(LAYER_TRANSITION, showFilled ? \"opacity-100\" : \"opacity-0\")}>\n {filledPaths.map(renderPath)}\n </g>\n )}\n </svg>\n );\n },\n);\n\nBaseIcon.displayName = \"BaseIcon\";\n"],"names":["jsx","React","jsxs","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAIA,MAAM,aAAuC;AAAA,EAC3C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,MAAM,mBACJ;AAMF,MAAM,aAAa,CAAC,MAClB,EAAE,OAAO,SACPA,2BAAAA;AAAAA,EAAC;AAAA,EAAA;AAAA,IAEC,GAAG,EAAE;AAAA,IACL,QAAO;AAAA,IACP,aAAa,EAAE;AAAA,IACf,eAAc;AAAA,IACd,gBAAe;AAAA,EAAA;AAAA,EALV,EAAE;AAMT,IAEAA,2BAAAA;AAAAA,EAAC;AAAA,EAAA;AAAA,IAEC,GAAG,EAAE;AAAA,IACL,MAAK;AAAA,IACL,UAAU,EAAE,KAAK,YAAY;AAAA,IAC7B,UAAU,EAAE,KAAK,YAAY;AAAA,EAAA;AAAA,EAJxB,EAAE;AAKT;AAaG,MAAM,WAAWC,iBAAM;AAAA,EAC5B,CAAC,EAAE,UAAU,OAAO,IAAI,QAAQ,WAAW,GAAG,MAAA,GAAS,QAAQ;AAC7D,UAAM,IAAI,SAAS,IAAI;AACvB,UAAM,WAAW,GAAG,YAAY,CAAA;AAChC,UAAM,cAAc,GAAG;AACvB,UAAM,aAAa,CAAC,EAAE,UAAU;AAEhC,QAAI,UAAU,CAAC,eAAe,KAAK,QAAQ,IAAI,aAAa,cAAc;AACxE,cAAQ;AAAA,QACN,4BAA4B,IAAI;AAAA,MAAA;AAAA,IAEpC;AAEA,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,SAAS,OAAO,IAAI,IAAI,IAAI;AAAA,QAC5B,MAAK;AAAA,QACL,eAAY;AAAA,QACZ,WAAWC,GAAAA,GAAG,WAAW,IAAI,GAAG,SAAS;AAAA,QACxC,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAAH,2BAAAA,IAAC,KAAA,EAAE,WAAWG,GAAAA,GAAG,kBAAkB,aAAa,cAAc,aAAa,GACxE,UAAA,SAAS,IAAI,UAAU,EAAA,CAC1B;AAAA,UACC,eACCH,2BAAAA,IAAC,KAAA,EAAE,WAAWG,GAAAA,GAAG,kBAAkB,aAAa,gBAAgB,WAAW,GACxE,UAAA,YAAY,IAAI,UAAU,EAAA,CAC7B;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,SAAS,cAAc;;"}
@@ -98,7 +98,7 @@ const Select = React__namespace.forwardRef(
98
98
  "aria-describedby": bottomText ? helperTextId : void 0,
99
99
  "aria-invalid": error || void 0,
100
100
  className: cn.cn(
101
- "flex w-full cursor-pointer items-center justify-between rounded-sm border bg-neutral-alphas-50 outline-none motion-safe:transition-colors",
101
+ "flex w-full cursor-pointer items-center justify-between rounded-sm border bg-neutral-alphas-50 outline-none motion-safe:transition-colors focus-visible:shadow-focus-ring",
102
102
  TRIGGER_HEIGHT[size],
103
103
  TRIGGER_PADDING_X[size],
104
104
  TRIGGER_GAP[size],
@@ -1 +1 @@
1
- {"version":3,"file":"Select.cjs","sources":["../../../../src/components/Select/Select.tsx"],"sourcesContent":["import * as SelectPrimitive from \"@radix-ui/react-select\";\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { FLOATING_CONTENT_COLLISION_PADDING } from \"@/utils/floatingContentCollisionPadding\";\nimport { CheckIcon } from \"../Icons/CheckIcon\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\n\n/** Select field height in pixels. */\nexport type SelectSize = \"48\" | \"40\" | \"32\";\n\ntype SelectContextValue = {\n size: SelectSize;\n error: boolean;\n disabled?: boolean;\n};\n\nconst SelectContext = React.createContext<SelectContextValue>({\n size: \"48\",\n error: false,\n});\n\nconst TRIGGER_HEIGHT: Record<SelectSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst TRIGGER_PADDING_X: Record<SelectSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst TRIGGER_GAP: Record<SelectSize, string> = {\n \"48\": \"gap-3\",\n \"40\": \"gap-3\",\n \"32\": \"gap-2\",\n};\n\nconst TRIGGER_TYPOGRAPHY: Record<SelectSize, string> = {\n \"48\": \"typography-regular-body-lg\",\n \"40\": \"typography-regular-body-lg\",\n \"32\": \"typography-regular-body-md\",\n};\n\nexport interface SelectProps extends Omit<SelectPrimitive.SelectProps, \"dir\"> {\n /** Label text displayed above the trigger. Also used as the accessible name. */\n label?: string;\n /** Accessible name applied directly to the trigger button when no visible `label` is provided. */\n \"aria-label\"?: string;\n /** ID of an external element that labels the trigger button. */\n \"aria-labelledby\"?: string;\n /** Helper text displayed below the trigger. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the select field in pixels. @default \"48\" */\n size?: SelectSize;\n /** Whether the field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the trigger. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Placeholder shown when no value is selected. */\n placeholder?: string;\n /** Icon element displayed at the left side of the trigger. */\n leftIcon?: React.ReactNode;\n /** Whether the field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Wraps the `className` of the outermost container div. */\n className?: string;\n /** HTML `id` applied to the trigger button. Auto-generated if omitted. */\n id?: string;\n}\n\n/**\n * A select field with optional label, helper/error text, and an icon slot,\n * built on Radix UI Select for full accessibility and keyboard navigation.\n *\n * Pair with {@link SelectContent} and {@link SelectItem} to provide options.\n *\n * @example\n * ```tsx\n * <Select label=\"Country\" placeholder=\"Select a country\">\n * <SelectContent>\n * <SelectItem value=\"us\">United States</SelectItem>\n * <SelectItem value=\"uk\">United Kingdom</SelectItem>\n * </SelectContent>\n * </Select>\n * ```\n */\nexport const Select = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Trigger>,\n SelectProps\n>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n placeholder,\n leftIcon,\n fullWidth = false,\n className,\n id,\n disabled,\n children,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const triggerId = id ?? generatedId;\n const helperTextId = `${triggerId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n return (\n <SelectContext.Provider value={{ size, error, disabled }}>\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={triggerId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <SelectPrimitive.Root disabled={disabled} {...props}>\n <SelectPrimitive.Trigger\n ref={ref}\n id={triggerId}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-describedby={bottomText ? helperTextId : undefined}\n aria-invalid={error || undefined}\n className={cn(\n \"flex w-full cursor-pointer items-center justify-between rounded-sm border bg-neutral-alphas-50 outline-none motion-safe:transition-colors\",\n TRIGGER_HEIGHT[size],\n TRIGGER_PADDING_X[size],\n TRIGGER_GAP[size],\n TRIGGER_TYPOGRAPHY[size],\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled &&\n !error &&\n \"hover:border-neutral-alphas-400 data-[state=open]:border-neutral-alphas-400\",\n disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n {leftIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\"\n data-testid=\"left-icon\"\n >\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 flex-1 truncate text-left text-content-primary [&>[data-placeholder]]:text-content-secondary [&>[data-placeholder]]:opacity-40\">\n <SelectPrimitive.Value placeholder={placeholder} />\n </span>\n </div>\n\n <SelectPrimitive.Icon asChild>\n <ChevronDownIcon />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n\n {children}\n </SelectPrimitive.Root>\n\n {bottomText && (\n <p\n id={helperTextId}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {bottomText}\n </p>\n )}\n </div>\n </SelectContext.Provider>\n );\n },\n);\n\nSelect.displayName = \"Select\";\n\nexport interface SelectContentProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> {}\n\n/**\n * The dropdown panel rendered inside a portal. Place {@link SelectItem} elements\n * (and optionally {@link SelectGroup} / {@link SelectLabel}) as children.\n */\nexport const SelectContent = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Content>,\n SelectContentProps\n>(\n (\n {\n className,\n children,\n position = \"popper\",\n sideOffset = 4,\n collisionPadding = FLOATING_CONTENT_COLLISION_PADDING,\n style,\n ...props\n },\n ref,\n ) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n className={cn(\n \"relative w-max min-w-(--radix-select-trigger-width) max-w-(--radix-select-content-available-width) overflow-hidden rounded-sm border border-neutral-alphas-200 bg-bg-primary text-content-primary shadow-[0_4px_16px_rgba(0,0,0,0.10)]\",\n \"data-[state=closed]:animate-out data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.Viewport className=\"max-h-[var(--radix-select-content-available-height)] overflow-y-auto p-1\">\n {children}\n </SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n ),\n);\n\nSelectContent.displayName = \"SelectContent\";\n\nexport interface SelectItemProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> {}\n\n/**\n * An individual option inside {@link SelectContent}.\n */\nexport const SelectItem = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Item>,\n SelectItemProps\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"typography-regular-body-lg relative flex w-full cursor-pointer select-none items-center gap-2 rounded-xs py-2 pr-2 pl-3 text-content-primary outline-none\",\n \"focus:bg-neutral-alphas-100 data-disabled:pointer-events-none data-disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n <SelectPrimitive.ItemIndicator className=\"ml-auto flex size-4 shrink-0 items-center justify-center\">\n <CheckIcon className=\"size-4 text-content-primary\" />\n </SelectPrimitive.ItemIndicator>\n </SelectPrimitive.Item>\n));\n\nSelectItem.displayName = \"SelectItem\";\n\n/** Props for {@link SelectGroup}. */\nexport type SelectGroupProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Group>;\n\n/**\n * Groups related {@link SelectItem} elements under a {@link SelectLabel}.\n */\nexport const SelectGroup = SelectPrimitive.Group;\nSelectGroup.displayName = \"SelectGroup\";\n\nexport interface SelectLabelProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> {}\n\n/**\n * A non-interactive label shown above a {@link SelectGroup}.\n */\nexport const SelectLabel = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Label>,\n SelectLabelProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn(\"typography-semibold-body-sm px-3 py-1.5 text-content-secondary\", className)}\n {...props}\n />\n));\n\nSelectLabel.displayName = \"SelectLabel\";\n\nexport interface SelectSeparatorProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> {}\n\n/** A horizontal rule that visually separates groups in {@link SelectContent}. */\nexport const SelectSeparator = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Separator>,\n SelectSeparatorProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-neutral-alphas-200\", className)}\n {...props}\n />\n));\n\nSelectSeparator.displayName = \"SelectSeparator\";\n"],"names":["React","jsx","jsxs","cn","SelectPrimitive","ChevronDownIcon","FLOATING_CONTENT_COLLISION_PADDING","CheckIcon"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,MAAM,gBAAgBA,iBAAM,cAAkC;AAAA,EAC5D,MAAM;AAAA,EACN,OAAO;AACT,CAAC;AAED,MAAM,iBAA6C;AAAA,EACjD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,MAAM,oBAAgD;AAAA,EACpD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,MAAM,cAA0C;AAAA,EAC9C,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,MAAM,qBAAiD;AAAA,EACrD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AA6CO,MAAM,SAASA,iBAAM;AAAA,EAI1B,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc;AAAA,IACd,mBAAmB;AAAA,IACnB,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,cAAcA,iBAAM,MAAA;AAC1B,UAAM,YAAY,MAAM;AACxB,UAAM,eAAe,GAAG,SAAS;AACjC,UAAM,aAAa,SAAS,eAAe,eAAe;AAE1D,WACEC,+BAAC,cAAc,UAAd,EAAuB,OAAO,EAAE,MAAM,OAAO,YAC5C,UAAAC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWC,GAAAA,GAAG,iBAAiB,aAAa,UAAU,SAAS;AAAA,QAC/D,iBAAe,WAAW,KAAK;AAAA,QAC/B,cAAY,QAAQ,KAAK;AAAA,QAExB,UAAA;AAAA,UAAA,SACCF,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,SAAS;AAAA,cACT,WAAU;AAAA,cAET,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,0CAIJG,2BAAgB,MAAhB,EAAqB,UAAqB,GAAG,OAC5C,UAAA;AAAA,YAAAF,2BAAAA;AAAAA,cAACE,2BAAgB;AAAA,cAAhB;AAAA,gBACC;AAAA,gBACA,IAAI;AAAA,gBACJ,cAAY;AAAA,gBACZ,mBAAiB;AAAA,gBACjB,oBAAkB,aAAa,eAAe;AAAA,gBAC9C,gBAAc,SAAS;AAAA,gBACvB,WAAWD,GAAAA;AAAAA,kBACT;AAAA,kBACA,eAAe,IAAI;AAAA,kBACnB,kBAAkB,IAAI;AAAA,kBACtB,YAAY,IAAI;AAAA,kBAChB,mBAAmB,IAAI;AAAA,kBACvB,QAAQ,yBAAyB;AAAA,kBACjC,CAAC,YACC,CAAC,SACD;AAAA,kBACF,YAAY;AAAA,gBAAA;AAAA,gBAGd,UAAA;AAAA,kBAAAD,2BAAAA,KAAC,OAAA,EAAI,WAAU,mCACZ,UAAA;AAAA,oBAAA,YACCD,2BAAAA;AAAAA,sBAAC;AAAA,sBAAA;AAAA,wBACC,WAAU;AAAA,wBACV,eAAY;AAAA,wBAEX,UAAA;AAAA,sBAAA;AAAA,oBAAA;AAAA,oBAGLA,2BAAAA,IAAC,UAAK,WAAU,0IACd,yCAACG,2BAAgB,OAAhB,EAAsB,YAAA,CAA0B,EAAA,CACnD;AAAA,kBAAA,GACF;AAAA,kBAEAH,+BAACG,2BAAgB,MAAhB,EAAqB,SAAO,MAC3B,UAAAH,2BAAAA,IAACI,mCAAgB,EAAA,CACnB;AAAA,gBAAA;AAAA,cAAA;AAAA,YAAA;AAAA,YAGD;AAAA,UAAA,GACH;AAAA,UAEC,cACCJ,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,IAAI;AAAA,cACJ,WAAWE,GAAAA;AAAAA,gBACT;AAAA,gBACA,QAAQ,uBAAuB;AAAA,cAAA;AAAA,cAGhC,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,QACH;AAAA,MAAA;AAAA,IAAA,GAGN;AAAA,EAEJ;AACF;AAEA,OAAO,cAAc;AASd,MAAM,gBAAgBH,iBAAM;AAAA,EAIjC,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,aAAa;AAAA,IACb,mBAAmBM,gCAAAA;AAAAA,IACnB;AAAA,IACA,GAAG;AAAA,EAAA,GAEL,QAEAL,2BAAAA,IAACG,2BAAgB,QAAhB,EACC,UAAAH,2BAAAA;AAAAA,IAACG,2BAAgB;AAAA,IAAhB;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,EAAE,QAAQ,uCAAuC,GAAG,MAAA;AAAA,MAC3D,WAAWD,GAAAA;AAAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,MAEJ,yCAACC,2BAAgB,UAAhB,EAAyB,WAAU,4EACjC,SAAA,CACH;AAAA,IAAA;AAAA,EAAA,EACF,CACF;AAEJ;AAEA,cAAc,cAAc;AAQrB,MAAM,aAAaJ,iBAAM,WAG9B,CAAC,EAAE,WAAW,UAAU,GAAG,SAAS,QACpCE,2BAAAA;AAAAA,EAACE,2BAAgB;AAAA,EAAhB;AAAA,IACC;AAAA,IACA,WAAWD,GAAAA;AAAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,IAEJ,UAAA;AAAA,MAAAF,2BAAAA,IAACG,2BAAgB,UAAhB,EAA0B,SAAA,CAAS;AAAA,MACpCH,2BAAAA,IAACG,2BAAgB,eAAhB,EAA8B,WAAU,4DACvC,UAAAH,2BAAAA,IAACM,UAAAA,WAAA,EAAU,WAAU,8BAAA,CAA8B,EAAA,CACrD;AAAA,IAAA;AAAA,EAAA;AACF,CACD;AAED,WAAW,cAAc;AAQlB,MAAM,cAAcH,2BAAgB;AAC3C,YAAY,cAAc;AAQnB,MAAM,cAAcJ,iBAAM,WAG/B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1BC,2BAAAA;AAAAA,EAACG,2BAAgB;AAAA,EAAhB;AAAA,IACC;AAAA,IACA,WAAWD,GAAAA,GAAG,kEAAkE,SAAS;AAAA,IACxF,GAAG;AAAA,EAAA;AACN,CACD;AAED,YAAY,cAAc;AAMnB,MAAM,kBAAkBH,iBAAM,WAGnC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1BC,2BAAAA;AAAAA,EAACG,2BAAgB;AAAA,EAAhB;AAAA,IACC;AAAA,IACA,WAAWD,GAAAA,GAAG,yCAAyC,SAAS;AAAA,IAC/D,GAAG;AAAA,EAAA;AACN,CACD;AAED,gBAAgB,cAAc;;;;;;;"}
1
+ {"version":3,"file":"Select.cjs","sources":["../../../../src/components/Select/Select.tsx"],"sourcesContent":["import * as SelectPrimitive from \"@radix-ui/react-select\";\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { FLOATING_CONTENT_COLLISION_PADDING } from \"@/utils/floatingContentCollisionPadding\";\nimport { CheckIcon } from \"../Icons/CheckIcon\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\n\n/** Select field height in pixels. */\nexport type SelectSize = \"48\" | \"40\" | \"32\";\n\ntype SelectContextValue = {\n size: SelectSize;\n error: boolean;\n disabled?: boolean;\n};\n\nconst SelectContext = React.createContext<SelectContextValue>({\n size: \"48\",\n error: false,\n});\n\nconst TRIGGER_HEIGHT: Record<SelectSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst TRIGGER_PADDING_X: Record<SelectSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst TRIGGER_GAP: Record<SelectSize, string> = {\n \"48\": \"gap-3\",\n \"40\": \"gap-3\",\n \"32\": \"gap-2\",\n};\n\nconst TRIGGER_TYPOGRAPHY: Record<SelectSize, string> = {\n \"48\": \"typography-regular-body-lg\",\n \"40\": \"typography-regular-body-lg\",\n \"32\": \"typography-regular-body-md\",\n};\n\nexport interface SelectProps extends Omit<SelectPrimitive.SelectProps, \"dir\"> {\n /** Label text displayed above the trigger. Also used as the accessible name. */\n label?: string;\n /** Accessible name applied directly to the trigger button when no visible `label` is provided. */\n \"aria-label\"?: string;\n /** ID of an external element that labels the trigger button. */\n \"aria-labelledby\"?: string;\n /** Helper text displayed below the trigger. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the select field in pixels. @default \"48\" */\n size?: SelectSize;\n /** Whether the field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the trigger. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Placeholder shown when no value is selected. */\n placeholder?: string;\n /** Icon element displayed at the left side of the trigger. */\n leftIcon?: React.ReactNode;\n /** Whether the field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Wraps the `className` of the outermost container div. */\n className?: string;\n /** HTML `id` applied to the trigger button. Auto-generated if omitted. */\n id?: string;\n}\n\n/**\n * A select field with optional label, helper/error text, and an icon slot,\n * built on Radix UI Select for full accessibility and keyboard navigation.\n *\n * Pair with {@link SelectContent} and {@link SelectItem} to provide options.\n *\n * @example\n * ```tsx\n * <Select label=\"Country\" placeholder=\"Select a country\">\n * <SelectContent>\n * <SelectItem value=\"us\">United States</SelectItem>\n * <SelectItem value=\"uk\">United Kingdom</SelectItem>\n * </SelectContent>\n * </Select>\n * ```\n */\nexport const Select = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Trigger>,\n SelectProps\n>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n placeholder,\n leftIcon,\n fullWidth = false,\n className,\n id,\n disabled,\n children,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const triggerId = id ?? generatedId;\n const helperTextId = `${triggerId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n return (\n <SelectContext.Provider value={{ size, error, disabled }}>\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={triggerId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <SelectPrimitive.Root disabled={disabled} {...props}>\n <SelectPrimitive.Trigger\n ref={ref}\n id={triggerId}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-describedby={bottomText ? helperTextId : undefined}\n aria-invalid={error || undefined}\n className={cn(\n \"flex w-full cursor-pointer items-center justify-between rounded-sm border bg-neutral-alphas-50 outline-none motion-safe:transition-colors focus-visible:shadow-focus-ring\",\n TRIGGER_HEIGHT[size],\n TRIGGER_PADDING_X[size],\n TRIGGER_GAP[size],\n TRIGGER_TYPOGRAPHY[size],\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled &&\n !error &&\n \"hover:border-neutral-alphas-400 data-[state=open]:border-neutral-alphas-400\",\n disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n {leftIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\"\n data-testid=\"left-icon\"\n >\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 flex-1 truncate text-left text-content-primary [&>[data-placeholder]]:text-content-secondary [&>[data-placeholder]]:opacity-40\">\n <SelectPrimitive.Value placeholder={placeholder} />\n </span>\n </div>\n\n <SelectPrimitive.Icon asChild>\n <ChevronDownIcon />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n\n {children}\n </SelectPrimitive.Root>\n\n {bottomText && (\n <p\n id={helperTextId}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {bottomText}\n </p>\n )}\n </div>\n </SelectContext.Provider>\n );\n },\n);\n\nSelect.displayName = \"Select\";\n\nexport interface SelectContentProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> {}\n\n/**\n * The dropdown panel rendered inside a portal. Place {@link SelectItem} elements\n * (and optionally {@link SelectGroup} / {@link SelectLabel}) as children.\n */\nexport const SelectContent = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Content>,\n SelectContentProps\n>(\n (\n {\n className,\n children,\n position = \"popper\",\n sideOffset = 4,\n collisionPadding = FLOATING_CONTENT_COLLISION_PADDING,\n style,\n ...props\n },\n ref,\n ) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n className={cn(\n \"relative w-max min-w-(--radix-select-trigger-width) max-w-(--radix-select-content-available-width) overflow-hidden rounded-sm border border-neutral-alphas-200 bg-bg-primary text-content-primary shadow-[0_4px_16px_rgba(0,0,0,0.10)]\",\n \"data-[state=closed]:animate-out data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.Viewport className=\"max-h-[var(--radix-select-content-available-height)] overflow-y-auto p-1\">\n {children}\n </SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n ),\n);\n\nSelectContent.displayName = \"SelectContent\";\n\nexport interface SelectItemProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> {}\n\n/**\n * An individual option inside {@link SelectContent}.\n */\nexport const SelectItem = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Item>,\n SelectItemProps\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"typography-regular-body-lg relative flex w-full cursor-pointer select-none items-center gap-2 rounded-xs py-2 pr-2 pl-3 text-content-primary outline-none\",\n \"focus:bg-neutral-alphas-100 data-disabled:pointer-events-none data-disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n <SelectPrimitive.ItemIndicator className=\"ml-auto flex size-4 shrink-0 items-center justify-center\">\n <CheckIcon className=\"size-4 text-content-primary\" />\n </SelectPrimitive.ItemIndicator>\n </SelectPrimitive.Item>\n));\n\nSelectItem.displayName = \"SelectItem\";\n\n/** Props for {@link SelectGroup}. */\nexport type SelectGroupProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Group>;\n\n/**\n * Groups related {@link SelectItem} elements under a {@link SelectLabel}.\n */\nexport const SelectGroup = SelectPrimitive.Group;\nSelectGroup.displayName = \"SelectGroup\";\n\nexport interface SelectLabelProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> {}\n\n/**\n * A non-interactive label shown above a {@link SelectGroup}.\n */\nexport const SelectLabel = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Label>,\n SelectLabelProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn(\"typography-semibold-body-sm px-3 py-1.5 text-content-secondary\", className)}\n {...props}\n />\n));\n\nSelectLabel.displayName = \"SelectLabel\";\n\nexport interface SelectSeparatorProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> {}\n\n/** A horizontal rule that visually separates groups in {@link SelectContent}. */\nexport const SelectSeparator = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Separator>,\n SelectSeparatorProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-neutral-alphas-200\", className)}\n {...props}\n />\n));\n\nSelectSeparator.displayName = \"SelectSeparator\";\n"],"names":["React","jsx","jsxs","cn","SelectPrimitive","ChevronDownIcon","FLOATING_CONTENT_COLLISION_PADDING","CheckIcon"],"mappings":"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"}
@@ -51,7 +51,7 @@ const CLEAR_BUTTON_RIGHT = {
51
51
  };
52
52
  function getContainerClassName(size, error, disabled) {
53
53
  return cn.cn(
54
- "relative rounded-sm border bg-neutral-alphas-50 has-focus-visible:outline-none motion-safe:transition-colors",
54
+ "relative rounded-sm border bg-neutral-alphas-50 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors",
55
55
  error ? "border-error-content" : "border-transparent",
56
56
  !disabled && !error && "hover:border-neutral-alphas-400",
57
57
  CONTAINER_MIN_HEIGHT[size],
@@ -1 +1 @@
1
- {"version":3,"file":"TextArea.cjs","sources":["../../../../src/components/TextArea/TextArea.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CheckOutlineIcon } from \"../Icons/CheckOutlineIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\n\n/** Text area height in pixels. */\nexport type TextAreaSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextAreaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, \"size\"> {\n /** Label text displayed above the textarea. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the textarea. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Minimum height of the text area in pixels. @default \"48\" */\n size?: TextAreaSize;\n /** Whether the text area is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the textarea. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text area is validated. @default false */\n validated?: boolean;\n /** Whether the text area stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Whether to show a clear button when text is present. @default false */\n showClearButton?: boolean;\n /** Callback fired when the clear button is clicked. Note: `onChange` is also called with an empty value when clearing. */\n onClear?: () => void;\n /** Minimum number of rows (lines) for the textarea. */\n minRows?: number;\n /** Maximum number of rows (lines) for the textarea. */\n maxRows?: number;\n /** Whether the textarea can be resized by the user. @default true */\n resizable?: boolean;\n}\n\nconst CONTAINER_MIN_HEIGHT: Record<TextAreaSize, string> = {\n \"48\": \"min-h-12\",\n \"40\": \"min-h-10\",\n \"32\": \"min-h-8\",\n};\n\nconst TEXTAREA_SIZE_CLASSES: Record<TextAreaSize, string> = {\n \"48\": \"py-3 typography-regular-body-lg autofill-body-lg\",\n \"40\": \"py-2 typography-regular-body-lg autofill-body-lg\",\n \"32\": \"py-2 typography-regular-body-md autofill-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextAreaSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst PADDING_RIGHT_WITH_CLEAR: Record<TextAreaSize, string> = {\n \"48\": \"pr-11\",\n \"40\": \"pr-11\",\n \"32\": \"pr-10\",\n};\n\nconst CLEAR_BUTTON_RIGHT: Record<TextAreaSize, string> = {\n \"48\": \"right-4 top-3\",\n \"40\": \"right-4 top-2\",\n \"32\": \"right-3 top-2\",\n};\n\nfunction getContainerClassName(size: TextAreaSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative rounded-sm border bg-neutral-alphas-50 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_MIN_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getTextareaClassName(\n size: TextAreaSize,\n hasClearButton: boolean,\n hasMinRows: boolean,\n resizable: boolean,\n) {\n return cn(\n \"h-full w-full bg-transparent text-content-primary no-underline placeholder:text-content-secondary focus:outline-none disabled:cursor-not-allowed\",\n resizable ? \"resize-y\" : \"resize-none\",\n !hasMinRows && \"min-h-[80px]\",\n TEXTAREA_SIZE_CLASSES[size],\n PADDING_HORIZONTAL[size],\n hasClearButton ? PADDING_RIGHT_WITH_CLEAR[size] : \"\",\n );\n}\n\nfunction TextAreaHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextArea: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\nfunction calculateMaxHeight(size: TextAreaSize, maxRows?: number): string | undefined {\n if (!maxRows) return undefined;\n\n // Line height is 24px for body-1 (sizes 48 and 40) and 20px for body-2 (size 32)\n const lineHeight = size === \"32\" ? 20 : 24;\n // py-2 = 8px, py-3 = 12px\n const verticalPadding = size === \"32\" ? 8 : size === \"40\" ? 8 : 12;\n\n return `${lineHeight * maxRows + verticalPadding * 2}px`;\n}\n\nfunction useTextAreaValue(\n value: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"value\"],\n defaultValue: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"defaultValue\"],\n showClearButton: boolean,\n onChange?: React.ChangeEventHandler<HTMLTextAreaElement>,\n onClear?: () => void,\n textareaRef?: React.RefObject<HTMLTextAreaElement | null>,\n) {\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? \"\");\n const resolvedValue = value !== undefined ? value : internalValue;\n\n const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInternalValue(e.target.value);\n onChange?.(e);\n };\n\n const handleClear = () => {\n setInternalValue(\"\");\n\n if (onChange && textareaRef?.current) {\n const syntheticEvent = {\n target: { ...textareaRef.current, value: \"\" },\n currentTarget: { ...textareaRef.current, value: \"\" },\n } as React.ChangeEvent<HTMLTextAreaElement>;\n onChange(syntheticEvent);\n }\n\n onClear?.();\n };\n\n return {\n resolvedValue,\n displayValue: showClearButton ? resolvedValue : value,\n resolvedDefaultValue: showClearButton ? undefined : defaultValue,\n handleChange,\n handleClear,\n };\n}\n\n/**\n * A multi-line text input with optional label, helper/error text, and clear button.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextArea\n * label=\"Description\"\n * placeholder=\"Enter your description...\"\n * showClearButton\n * error={!!descError}\n * errorMessage={descError}\n * />\n * ```\n */\nexport const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n className,\n id,\n disabled,\n fullWidth = false,\n showClearButton = false,\n onClear,\n value,\n defaultValue,\n onChange,\n minRows,\n maxRows,\n resizable = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n const maxHeight = calculateMaxHeight(size, maxRows);\n\n const internalRef = React.useRef<HTMLTextAreaElement>(null);\n\n const { resolvedValue, displayValue, resolvedDefaultValue, handleChange, handleClear } =\n useTextAreaValue(value, defaultValue, showClearButton, onChange, onClear, internalRef);\n\n const mergedRef = (node: HTMLTextAreaElement | null) => {\n internalRef.current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n }\n };\n\n const showClear = showClearButton && resolvedValue !== \"\" && !disabled;\n const showValidated = validated && !showClear;\n const ariaDescribedBy = bottomText ? helperTextId : undefined;\n const textareaStyle = maxHeight ? { maxHeight } : undefined;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n <textarea\n ref={mergedRef}\n id={inputId}\n disabled={disabled}\n aria-describedby={ariaDescribedBy}\n aria-invalid={error || undefined}\n className={getTextareaClassName(size, showClear, !!minRows, resizable)}\n value={displayValue}\n defaultValue={resolvedDefaultValue}\n onChange={handleChange}\n rows={minRows}\n style={textareaStyle}\n {...props}\n />\n\n {showClear && (\n <IconButton\n variant=\"tertiary\"\n size=\"24\"\n icon={<CloseIcon />}\n aria-label=\"Clear text\"\n onClick={handleClear}\n className={cn(\n \"absolute flex size-5 items-center justify-center self-end\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n />\n )}\n {showValidated && (\n <div\n className={cn(\n \"pointer-events-none absolute flex size-5 items-center justify-center\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n >\n <CheckOutlineIcon className=\"text-success-content\" />\n </div>\n )}\n </div>\n\n {bottomText && (\n <TextAreaHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextAreaHelperText>\n )}\n </div>\n );\n },\n);\n\nTextArea.displayName = \"TextArea\";\n"],"names":["cn","jsx","React","jsxs","IconButton","CloseIcon","CheckOutlineIcon"],"mappings":"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"}
1
+ {"version":3,"file":"TextArea.cjs","sources":["../../../../src/components/TextArea/TextArea.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CheckOutlineIcon } from \"../Icons/CheckOutlineIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\n\n/** Text area height in pixels. */\nexport type TextAreaSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextAreaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, \"size\"> {\n /** Label text displayed above the textarea. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the textarea. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Minimum height of the text area in pixels. @default \"48\" */\n size?: TextAreaSize;\n /** Whether the text area is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the textarea. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text area is validated. @default false */\n validated?: boolean;\n /** Whether the text area stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Whether to show a clear button when text is present. @default false */\n showClearButton?: boolean;\n /** Callback fired when the clear button is clicked. Note: `onChange` is also called with an empty value when clearing. */\n onClear?: () => void;\n /** Minimum number of rows (lines) for the textarea. */\n minRows?: number;\n /** Maximum number of rows (lines) for the textarea. */\n maxRows?: number;\n /** Whether the textarea can be resized by the user. @default true */\n resizable?: boolean;\n}\n\nconst CONTAINER_MIN_HEIGHT: Record<TextAreaSize, string> = {\n \"48\": \"min-h-12\",\n \"40\": \"min-h-10\",\n \"32\": \"min-h-8\",\n};\n\nconst TEXTAREA_SIZE_CLASSES: Record<TextAreaSize, string> = {\n \"48\": \"py-3 typography-regular-body-lg autofill-body-lg\",\n \"40\": \"py-2 typography-regular-body-lg autofill-body-lg\",\n \"32\": \"py-2 typography-regular-body-md autofill-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextAreaSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst PADDING_RIGHT_WITH_CLEAR: Record<TextAreaSize, string> = {\n \"48\": \"pr-11\",\n \"40\": \"pr-11\",\n \"32\": \"pr-10\",\n};\n\nconst CLEAR_BUTTON_RIGHT: Record<TextAreaSize, string> = {\n \"48\": \"right-4 top-3\",\n \"40\": \"right-4 top-2\",\n \"32\": \"right-3 top-2\",\n};\n\nfunction getContainerClassName(size: TextAreaSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative rounded-sm border bg-neutral-alphas-50 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_MIN_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getTextareaClassName(\n size: TextAreaSize,\n hasClearButton: boolean,\n hasMinRows: boolean,\n resizable: boolean,\n) {\n return cn(\n \"h-full w-full bg-transparent text-content-primary no-underline placeholder:text-content-secondary focus:outline-none disabled:cursor-not-allowed\",\n resizable ? \"resize-y\" : \"resize-none\",\n !hasMinRows && \"min-h-[80px]\",\n TEXTAREA_SIZE_CLASSES[size],\n PADDING_HORIZONTAL[size],\n hasClearButton ? PADDING_RIGHT_WITH_CLEAR[size] : \"\",\n );\n}\n\nfunction TextAreaHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextArea: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\nfunction calculateMaxHeight(size: TextAreaSize, maxRows?: number): string | undefined {\n if (!maxRows) return undefined;\n\n // Line height is 24px for body-1 (sizes 48 and 40) and 20px for body-2 (size 32)\n const lineHeight = size === \"32\" ? 20 : 24;\n // py-2 = 8px, py-3 = 12px\n const verticalPadding = size === \"32\" ? 8 : size === \"40\" ? 8 : 12;\n\n return `${lineHeight * maxRows + verticalPadding * 2}px`;\n}\n\nfunction useTextAreaValue(\n value: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"value\"],\n defaultValue: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"defaultValue\"],\n showClearButton: boolean,\n onChange?: React.ChangeEventHandler<HTMLTextAreaElement>,\n onClear?: () => void,\n textareaRef?: React.RefObject<HTMLTextAreaElement | null>,\n) {\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? \"\");\n const resolvedValue = value !== undefined ? value : internalValue;\n\n const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInternalValue(e.target.value);\n onChange?.(e);\n };\n\n const handleClear = () => {\n setInternalValue(\"\");\n\n if (onChange && textareaRef?.current) {\n const syntheticEvent = {\n target: { ...textareaRef.current, value: \"\" },\n currentTarget: { ...textareaRef.current, value: \"\" },\n } as React.ChangeEvent<HTMLTextAreaElement>;\n onChange(syntheticEvent);\n }\n\n onClear?.();\n };\n\n return {\n resolvedValue,\n displayValue: showClearButton ? resolvedValue : value,\n resolvedDefaultValue: showClearButton ? undefined : defaultValue,\n handleChange,\n handleClear,\n };\n}\n\n/**\n * A multi-line text input with optional label, helper/error text, and clear button.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextArea\n * label=\"Description\"\n * placeholder=\"Enter your description...\"\n * showClearButton\n * error={!!descError}\n * errorMessage={descError}\n * />\n * ```\n */\nexport const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n className,\n id,\n disabled,\n fullWidth = false,\n showClearButton = false,\n onClear,\n value,\n defaultValue,\n onChange,\n minRows,\n maxRows,\n resizable = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n const maxHeight = calculateMaxHeight(size, maxRows);\n\n const internalRef = React.useRef<HTMLTextAreaElement>(null);\n\n const { resolvedValue, displayValue, resolvedDefaultValue, handleChange, handleClear } =\n useTextAreaValue(value, defaultValue, showClearButton, onChange, onClear, internalRef);\n\n const mergedRef = (node: HTMLTextAreaElement | null) => {\n internalRef.current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n }\n };\n\n const showClear = showClearButton && resolvedValue !== \"\" && !disabled;\n const showValidated = validated && !showClear;\n const ariaDescribedBy = bottomText ? helperTextId : undefined;\n const textareaStyle = maxHeight ? { maxHeight } : undefined;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n <textarea\n ref={mergedRef}\n id={inputId}\n disabled={disabled}\n aria-describedby={ariaDescribedBy}\n aria-invalid={error || undefined}\n className={getTextareaClassName(size, showClear, !!minRows, resizable)}\n value={displayValue}\n defaultValue={resolvedDefaultValue}\n onChange={handleChange}\n rows={minRows}\n style={textareaStyle}\n {...props}\n />\n\n {showClear && (\n <IconButton\n variant=\"tertiary\"\n size=\"24\"\n icon={<CloseIcon />}\n aria-label=\"Clear text\"\n onClick={handleClear}\n className={cn(\n \"absolute flex size-5 items-center justify-center self-end\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n />\n )}\n {showValidated && (\n <div\n className={cn(\n \"pointer-events-none absolute flex size-5 items-center justify-center\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n >\n <CheckOutlineIcon className=\"text-success-content\" />\n </div>\n )}\n </div>\n\n {bottomText && (\n <TextAreaHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextAreaHelperText>\n )}\n </div>\n );\n },\n);\n\nTextArea.displayName = \"TextArea\";\n"],"names":["cn","jsx","React","jsxs","IconButton","CloseIcon","CheckOutlineIcon"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,MAAM,uBAAqD;AAAA,EACzD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,MAAM,wBAAsD;AAAA,EAC1D,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,MAAM,qBAAmD;AAAA,EACvD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,MAAM,2BAAyD;AAAA,EAC7D,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,MAAM,qBAAmD;AAAA,EACvD,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,SAAS,sBAAsB,MAAoB,OAAgB,UAAoB;AACrF,SAAOA,GAAAA;AAAAA,IACL;AAAA,IACA,QAAQ,yBAAyB;AAAA,IACjC,CAAC,YAAY,CAAC,SAAS;AAAA,IACvB,qBAAqB,IAAI;AAAA,IACzB,YAAY;AAAA,EAAA;AAEhB;AAEA,SAAS,qBACP,MACA,gBACA,YACA,WACA;AACA,SAAOA,GAAAA;AAAAA,IACL;AAAA,IACA,YAAY,aAAa;AAAA,IACzB,CAAC,cAAc;AAAA,IACf,sBAAsB,IAAI;AAAA,IAC1B,mBAAmB,IAAI;AAAA,IACvB,iBAAiB,yBAAyB,IAAI,IAAI;AAAA,EAAA;AAEtD;AAEA,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA,WAAWD,GAAAA;AAAAA,QACT;AAAA,QACA,QAAQ,uBAAuB;AAAA,MAAA;AAAA,MAGhC;AAAA,IAAA;AAAA,EAAA;AAGP;AAEA,SAAS,0BAA0B,OAAgB,WAAoB,gBAAyB;AAC9F,MAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,QAAI,CAAC,SAAS,CAAC,aAAa,CAAC,gBAAgB;AAC3C,cAAQ;AAAA,QACN;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,MAAoB,SAAsC;AACpF,MAAI,CAAC,QAAS,QAAO;AAGrB,QAAM,aAAa,SAAS,OAAO,KAAK;AAExC,QAAM,kBAAkB,SAAS,OAAO,IAAI,SAAS,OAAO,IAAI;AAEhE,SAAO,GAAG,aAAa,UAAU,kBAAkB,CAAC;AACtD;AAEA,SAAS,iBACP,OACA,cACA,iBACA,UACA,SACA,aACA;AACA,QAAM,CAAC,eAAe,gBAAgB,IAAIE,iBAAM,SAAS,gBAAgB,EAAE;AAC3E,QAAM,gBAAgB,UAAU,SAAY,QAAQ;AAEpD,QAAM,eAAe,CAAC,MAA8C;AAClE,qBAAiB,EAAE,OAAO,KAAK;AAC/B,eAAW,CAAC;AAAA,EACd;AAEA,QAAM,cAAc,MAAM;AACxB,qBAAiB,EAAE;AAEnB,QAAI,YAAY,aAAa,SAAS;AACpC,YAAM,iBAAiB;AAAA,QACrB,QAAQ,EAAE,GAAG,YAAY,SAAS,OAAO,GAAA;AAAA,QACzC,eAAe,EAAE,GAAG,YAAY,SAAS,OAAO,GAAA;AAAA,MAAG;AAErD,eAAS,cAAc;AAAA,IACzB;AAEA,cAAA;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,cAAc,kBAAkB,gBAAgB;AAAA,IAChD,sBAAsB,kBAAkB,SAAY;AAAA,IACpD;AAAA,IACA;AAAA,EAAA;AAEJ;AAmBO,MAAM,WAAWA,iBAAM;AAAA,EAC5B,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,QAAQ;AAAA,IACR;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,cAAcA,iBAAM,MAAA;AAC1B,UAAM,UAAU,MAAM;AACtB,UAAM,eAAe,GAAG,OAAO;AAC/B,UAAM,aAAa,SAAS,eAAe,eAAe;AAC1D,UAAM,YAAY,mBAAmB,MAAM,OAAO;AAElD,UAAM,cAAcA,iBAAM,OAA4B,IAAI;AAE1D,UAAM,EAAE,eAAe,cAAc,sBAAsB,cAAc,YAAA,IACvE,iBAAiB,OAAO,cAAc,iBAAiB,UAAU,SAAS,WAAW;AAEvF,UAAM,YAAY,CAAC,SAAqC;AACtD,kBAAY,UAAU;AACtB,UAAI,OAAO,QAAQ,YAAY;AAC7B,YAAI,IAAI;AAAA,MACV,WAAW,KAAK;AACb,YAA2D,UAAU;AAAA,MACxE;AAAA,IACF;AAEA,UAAM,YAAY,mBAAmB,kBAAkB,MAAM,CAAC;AAC9D,UAAM,gBAAgB,aAAa,CAAC;AACpC,UAAM,kBAAkB,aAAa,eAAe;AACpD,UAAM,gBAAgB,YAAY,EAAE,UAAA,IAAc;AAElD,8BAA0B,OAAO,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC;AAE9E,WACEC,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWH,GAAAA,GAAG,iBAAiB,aAAa,UAAU,SAAS;AAAA,QAC/D,iBAAe,WAAW,KAAK;AAAA,QAC/B,cAAY,QAAQ,KAAK;AAAA,QAExB,UAAA;AAAA,UAAA,SACCC,2BAAAA;AAAAA,YAAC;AAAA,YAAA;AAAA,cACC,SAAS;AAAA,cACT,WAAU;AAAA,cAET,UAAA;AAAA,YAAA;AAAA,UAAA;AAAA,0CAIJ,OAAA,EAAI,WAAW,sBAAsB,MAAM,OAAO,QAAQ,GACzD,UAAA;AAAA,YAAAA,2BAAAA;AAAAA,cAAC;AAAA,cAAA;AAAA,gBACC,KAAK;AAAA,gBACL,IAAI;AAAA,gBACJ;AAAA,gBACA,oBAAkB;AAAA,gBAClB,gBAAc,SAAS;AAAA,gBACvB,WAAW,qBAAqB,MAAM,WAAW,CAAC,CAAC,SAAS,SAAS;AAAA,gBACrE,OAAO;AAAA,gBACP,cAAc;AAAA,gBACd,UAAU;AAAA,gBACV,MAAM;AAAA,gBACN,OAAO;AAAA,gBACN,GAAG;AAAA,cAAA;AAAA,YAAA;AAAA,YAGL,aACCA,2BAAAA;AAAAA,cAACG,WAAAA;AAAAA,cAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,MAAK;AAAA,gBACL,qCAAOC,UAAAA,WAAA,EAAU;AAAA,gBACjB,cAAW;AAAA,gBACX,SAAS;AAAA,gBACT,WAAWL,GAAAA;AAAAA,kBACT;AAAA,kBACA,mBAAmB,IAAI;AAAA,gBAAA;AAAA,cACzB;AAAA,YAAA;AAAA,YAGH,iBACCC,2BAAAA;AAAAA,cAAC;AAAA,cAAA;AAAA,gBACC,WAAWD,GAAAA;AAAAA,kBACT;AAAA,kBACA,mBAAmB,IAAI;AAAA,gBAAA;AAAA,gBAGzB,UAAAC,2BAAAA,IAACK,iBAAAA,kBAAA,EAAiB,WAAU,uBAAA,CAAuB;AAAA,cAAA;AAAA,YAAA;AAAA,UACrD,GAEJ;AAAA,UAEC,cACCL,2BAAAA,IAAC,oBAAA,EAAmB,IAAI,cAAc,OACnC,UAAA,WAAA,CACH;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,SAAS,cAAc;;"}
@@ -50,7 +50,7 @@ const ICON_INSET = {
50
50
  };
51
51
  function getContainerClassName(size, error, disabled) {
52
52
  return cn.cn(
53
- "relative overflow-hidden rounded-sm border bg-neutral-alphas-50 has-focus-visible:outline-none motion-safe:transition-colors",
53
+ "relative overflow-hidden rounded-sm border bg-neutral-alphas-50 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors",
54
54
  error ? "border-error-content" : "border-transparent",
55
55
  !disabled && !error && "hover:border-neutral-alphas-400",
56
56
  CONTAINER_HEIGHT[size],
@@ -1 +1 @@
1
- {"version":3,"file":"TextField.cjs","sources":["../../../../src/components/TextField/TextField.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { CheckOutlineIcon } from \"@/index\";\nimport { cn } from \"../../utils/cn\";\n\n/** Text field height in pixels. */\nexport type TextFieldSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextFieldProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\" | \"prefix\"> {\n /** Label text displayed above the input. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the input. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the text field in pixels. @default \"48\" */\n size?: TextFieldSize;\n /** Whether the text field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the input. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text field is validated. @default false */\n validated?: boolean;\n /** Icon element displayed at the left side of the input. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed at the right side of the input. */\n rightIcon?: React.ReactNode;\n /** Whether the text field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n}\n\nconst CONTAINER_HEIGHT: Record<TextFieldSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst INPUT_SIZE_CLASSES: Record<TextFieldSize, string> = {\n \"48\": \"py-3 typography-regular-body-lg autofill-body-lg\",\n \"40\": \"py-2 typography-regular-body-lg autofill-body-lg\",\n \"32\": \"py-2 typography-regular-body-md autofill-body-md\",\n};\n\nconst INPUT_PL: Record<TextFieldSize, { default: string; withIcon: string }> = {\n \"48\": { default: \"pl-4\", withIcon: \"pl-10\" },\n \"40\": { default: \"pl-4\", withIcon: \"pl-10\" },\n \"32\": { default: \"pl-3\", withIcon: \"pl-9\" },\n};\n\nconst INPUT_PR: Record<TextFieldSize, { default: string; withIcon: string }> = {\n \"48\": { default: \"pr-4\", withIcon: \"pr-10\" },\n \"40\": { default: \"pr-4\", withIcon: \"pr-10\" },\n \"32\": { default: \"pr-3\", withIcon: \"pr-9\" },\n};\n\nconst ICON_INSET: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nfunction getContainerClassName(size: TextFieldSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative overflow-hidden rounded-sm border bg-neutral-alphas-50 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getInputClassName(size: TextFieldSize, hasLeftIcon: boolean, hasRightIcon: boolean) {\n return cn(\n \"h-full w-full rounded-sm bg-transparent text-content-primary no-underline placeholder:text-content-secondary focus:outline-none disabled:cursor-not-allowed\",\n INPUT_SIZE_CLASSES[size],\n hasLeftIcon ? INPUT_PL[size].withIcon : INPUT_PL[size].default,\n hasRightIcon ? INPUT_PR[size].withIcon : INPUT_PR[size].default,\n );\n}\n\nfunction TextFieldHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-regular-body-sm px-2 pt-2 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextField: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n/**\n * A text input field with optional label, helper/error text, and icon slots.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextField\n * label=\"Email\"\n * placeholder=\"you@example.com\"\n * error={!!emailError}\n * errorMessage={emailError}\n * />\n * ```\n */\nexport const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n leftIcon,\n rightIcon,\n className,\n id,\n disabled,\n fullWidth = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n <input\n ref={ref}\n id={inputId}\n disabled={disabled}\n aria-describedby={bottomText ? helperTextId : undefined}\n aria-invalid={error || undefined}\n className={cn(\n getInputClassName(size, !!leftIcon, !!(rightIcon || validated)),\n \"[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n\n {leftIcon && (\n <div\n className={cn(\n \"pointer-events-none absolute inset-y-0 left-0 flex items-center text-content-secondary\",\n ICON_INSET[size],\n )}\n >\n <div className=\"flex size-4 shrink-0 items-center justify-center\">{leftIcon}</div>\n </div>\n )}\n\n {(rightIcon || validated) && (\n <div\n className={cn(\n \"absolute inset-y-0 right-0 flex items-center gap-2 text-content-secondary\",\n ICON_INSET[size],\n )}\n >\n {rightIcon && (\n <div className=\"flex size-4 shrink-0 items-center justify-center\">{rightIcon}</div>\n )}\n {validated && (\n <div className=\"pointer-events-none flex size-4 shrink-0 items-center justify-center\">\n <CheckOutlineIcon className=\"text-success-content\" />\n </div>\n )}\n </div>\n )}\n </div>\n\n {bottomText && (\n <TextFieldHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextFieldHelperText>\n )}\n </div>\n );\n },\n);\n\nTextField.displayName = \"TextField\";\n"],"names":["cn","jsx","React","jsxs","CheckOutlineIcon"],"mappings":"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"}
1
+ {"version":3,"file":"TextField.cjs","sources":["../../../../src/components/TextField/TextField.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { CheckOutlineIcon } from \"@/index\";\nimport { cn } from \"../../utils/cn\";\n\n/** Text field height in pixels. */\nexport type TextFieldSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextFieldProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\" | \"prefix\"> {\n /** Label text displayed above the input. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the input. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the text field in pixels. @default \"48\" */\n size?: TextFieldSize;\n /** Whether the text field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the input. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text field is validated. @default false */\n validated?: boolean;\n /** Icon element displayed at the left side of the input. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed at the right side of the input. */\n rightIcon?: React.ReactNode;\n /** Whether the text field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n}\n\nconst CONTAINER_HEIGHT: Record<TextFieldSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst INPUT_SIZE_CLASSES: Record<TextFieldSize, string> = {\n \"48\": \"py-3 typography-regular-body-lg autofill-body-lg\",\n \"40\": \"py-2 typography-regular-body-lg autofill-body-lg\",\n \"32\": \"py-2 typography-regular-body-md autofill-body-md\",\n};\n\nconst INPUT_PL: Record<TextFieldSize, { default: string; withIcon: string }> = {\n \"48\": { default: \"pl-4\", withIcon: \"pl-10\" },\n \"40\": { default: \"pl-4\", withIcon: \"pl-10\" },\n \"32\": { default: \"pl-3\", withIcon: \"pl-9\" },\n};\n\nconst INPUT_PR: Record<TextFieldSize, { default: string; withIcon: string }> = {\n \"48\": { default: \"pr-4\", withIcon: \"pr-10\" },\n \"40\": { default: \"pr-4\", withIcon: \"pr-10\" },\n \"32\": { default: \"pr-3\", withIcon: \"pr-9\" },\n};\n\nconst ICON_INSET: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nfunction getContainerClassName(size: TextFieldSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative overflow-hidden rounded-sm border bg-neutral-alphas-50 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getInputClassName(size: TextFieldSize, hasLeftIcon: boolean, hasRightIcon: boolean) {\n return cn(\n \"h-full w-full rounded-sm bg-transparent text-content-primary no-underline placeholder:text-content-secondary focus:outline-none disabled:cursor-not-allowed\",\n INPUT_SIZE_CLASSES[size],\n hasLeftIcon ? INPUT_PL[size].withIcon : INPUT_PL[size].default,\n hasRightIcon ? INPUT_PR[size].withIcon : INPUT_PR[size].default,\n );\n}\n\nfunction TextFieldHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-regular-body-sm px-2 pt-2 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextField: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n/**\n * A text input field with optional label, helper/error text, and icon slots.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextField\n * label=\"Email\"\n * placeholder=\"you@example.com\"\n * error={!!emailError}\n * errorMessage={emailError}\n * />\n * ```\n */\nexport const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n leftIcon,\n rightIcon,\n className,\n id,\n disabled,\n fullWidth = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n <input\n ref={ref}\n id={inputId}\n disabled={disabled}\n aria-describedby={bottomText ? helperTextId : undefined}\n aria-invalid={error || undefined}\n className={cn(\n getInputClassName(size, !!leftIcon, !!(rightIcon || validated)),\n \"[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n\n {leftIcon && (\n <div\n className={cn(\n \"pointer-events-none absolute inset-y-0 left-0 flex items-center text-content-secondary\",\n ICON_INSET[size],\n )}\n >\n <div className=\"flex size-4 shrink-0 items-center justify-center\">{leftIcon}</div>\n </div>\n )}\n\n {(rightIcon || validated) && (\n <div\n className={cn(\n \"absolute inset-y-0 right-0 flex items-center gap-2 text-content-secondary\",\n ICON_INSET[size],\n )}\n >\n {rightIcon && (\n <div className=\"flex size-4 shrink-0 items-center justify-center\">{rightIcon}</div>\n )}\n {validated && (\n <div className=\"pointer-events-none flex size-4 shrink-0 items-center justify-center\">\n <CheckOutlineIcon className=\"text-success-content\" />\n </div>\n )}\n </div>\n )}\n </div>\n\n {bottomText && (\n <TextFieldHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextFieldHelperText>\n )}\n </div>\n );\n },\n);\n\nTextField.displayName = \"TextField\";\n"],"names":["cn","jsx","React","jsxs","CheckOutlineIcon"],"mappings":"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"}
@@ -80,7 +80,7 @@ const Autocomplete = React.forwardRef((props, ref) => {
80
80
  "div",
81
81
  {
82
82
  className: cn(
83
- "flex flex-wrap items-center overflow-hidden rounded-sm border bg-neutral-alphas-100 has-focus-visible:outline-none motion-safe:transition-colors",
83
+ "flex flex-wrap items-center overflow-hidden rounded-sm border bg-neutral-alphas-100 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors",
84
84
  error ? "border-error-content" : "border-transparent",
85
85
  !disabled && !error && "hover:border-neutral-alphas-400",
86
86
  ac.isOpen && !error && !disabled && "border-neutral-alphas-400",
@@ -1 +1 @@
1
- {"version":3,"file":"Autocomplete.mjs","sources":["../../../src/components/Autocomplete/Autocomplete.tsx"],"sourcesContent":["import * as Popover from \"@radix-ui/react-popover\";\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { FLOATING_CONTENT_COLLISION_PADDING } from \"@/utils/floatingContentCollisionPadding\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\nimport { SpinnerIcon } from \"../Icons/SpinnerIcon\";\nimport { AutocompleteDropdownContent } from \"./AutocompleteDropdownContent\";\nimport { AutocompleteTag } from \"./AutocompleteTag\";\nimport { useAutocomplete } from \"./useAutocomplete\";\n\nexport type AutocompleteSize = \"48\" | \"40\" | \"32\";\n\nexport interface AutocompleteOption {\n value: string;\n label?: string;\n disabled?: boolean;\n}\n\ninterface AutocompleteBaseProps {\n label?: string;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n helperText?: string;\n /** @default \"48\" */\n size?: AutocompleteSize;\n /** @default false */\n error?: boolean;\n errorMessage?: string;\n placeholder?: string;\n leftIcon?: React.ReactNode;\n /** @default false */\n fullWidth?: boolean;\n /** @default false */\n disabled?: boolean;\n /** @default false */\n clearable?: boolean;\n clearAriaLabel?: string;\n id?: string;\n className?: string;\n options: AutocompleteOption[];\n inputValue?: string;\n onInputChange?: (value: string) => void;\n filterFn?: (option: AutocompleteOption, query: string) => boolean;\n /** @default false */\n creatable?: boolean;\n creatableLabel?: (inputValue: string) => string;\n onCreate?: (inputValue: string) => void;\n /** @default false */\n loading?: boolean;\n loadingText?: string;\n emptyText?: string;\n renderOption?: (\n option: AutocompleteOption,\n state: { selected: boolean; active: boolean },\n ) => React.ReactNode;\n renderTag?: (option: AutocompleteOption, onRemove: () => void) => React.ReactNode;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\ninterface AutocompleteSingleProps extends AutocompleteBaseProps {\n multiple?: false;\n value?: string | null;\n defaultValue?: string | null;\n onChange?: (value: string | null) => void;\n}\n\ninterface AutocompleteMultiProps extends AutocompleteBaseProps {\n multiple: true;\n value?: string[];\n defaultValue?: string[];\n onChange?: (values: string[]) => void;\n}\n\nexport type AutocompleteProps = AutocompleteSingleProps | AutocompleteMultiProps;\n\nconst CONTAINER_HEIGHT: Record<AutocompleteSize, string> = {\n \"48\": \"min-h-12\",\n \"40\": \"min-h-10\",\n \"32\": \"min-h-8\",\n};\n\nconst INPUT_SIZE_CLASSES: Record<AutocompleteSize, string> = {\n \"48\": \"typography-regular-body-lg\",\n \"40\": \"typography-regular-body-lg\",\n \"32\": \"typography-regular-body-md\",\n};\n\nconst PADDING_CLASSES: Record<AutocompleteSize, string> = {\n \"48\": \"px-4 py-1.5 gap-3\",\n \"40\": \"px-4 py-1 gap-3\",\n \"32\": \"px-3 py-1 gap-2\",\n};\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"Autocomplete: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Conditional JSX branches in the render template\nexport const Autocomplete = React.forwardRef<HTMLInputElement, AutocompleteProps>((props, ref) => {\n const {\n label,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n placeholder,\n leftIcon,\n fullWidth = false,\n disabled = false,\n clearable = false,\n clearAriaLabel = \"Clear\",\n className,\n loading = false,\n loadingText,\n emptyText = \"No results\",\n renderOption,\n renderTag,\n } = props;\n\n const ac = useAutocomplete(props);\n\n React.useImperativeHandle(ref, () => ac.inputRef.current as HTMLInputElement);\n\n warnMissingAccessibleName(label, ariaLabel, ariaLabelledby);\n\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n return (\n <Popover.Root open={ac.isOpen && !disabled} onOpenChange={ac.handleOpenChange}>\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-autocomplete-root=\"\"\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={ac.inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <Popover.Anchor asChild>\n {/* biome-ignore lint/a11y/noStaticElementInteractions: Container delegates click to the inner input */}\n {/* biome-ignore lint/a11y/useKeyWithClickEvents: Keyboard interaction is handled by the inner combobox input */}\n <div\n className={cn(\n \"flex flex-wrap items-center overflow-hidden rounded-sm border bg-neutral-alphas-100 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n ac.isOpen && !error && !disabled && \"border-neutral-alphas-400\",\n CONTAINER_HEIGHT[size],\n PADDING_CLASSES[size],\n disabled && \"opacity-50\",\n )}\n onClick={ac.handleContainerClick}\n >\n {leftIcon && (\n <div className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\">\n {leftIcon}\n </div>\n )}\n\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-1.5\">\n {ac.isMulti &&\n ac.selectedMultiOptions.map((opt) => (\n <AutocompleteTag\n key={opt.value}\n option={opt}\n disabled={disabled}\n onRemove={() => ac.toggleMulti(opt.value)}\n renderTag={renderTag}\n />\n ))}\n\n <input\n ref={ac.inputRef}\n id={ac.inputId}\n role=\"combobox\"\n type=\"text\"\n disabled={disabled}\n aria-expanded={ac.isOpen}\n aria-controls={ac.isOpen ? ac.listboxId : undefined}\n aria-activedescendant={ac.activeDescendantId}\n aria-autocomplete=\"list\"\n aria-describedby={bottomText ? ac.helperTextId : undefined}\n aria-invalid={error || undefined}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n autoComplete=\"off\"\n placeholder={\n ac.isMulti && ac.selectedMultiValues.length > 0 ? undefined : placeholder\n }\n value={ac.displayInputValue}\n onChange={ac.handleInputChange}\n onKeyDown={ac.handleKeyDown}\n onFocus={ac.handleFocus}\n onBlur={ac.handleBlur}\n className={cn(\n \"min-w-[40px] flex-1 truncate bg-transparent text-content-primary no-underline placeholder:text-content-secondary placeholder:opacity-40 focus:outline-none disabled:cursor-not-allowed\",\n INPUT_SIZE_CLASSES[size],\n )}\n />\n </div>\n\n <div className=\"flex shrink-0 items-center gap-1\">\n {loading && <SpinnerIcon className=\"size-4 animate-spin text-content-secondary\" />}\n {clearable && ac.hasClearableValue && !disabled && (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={clearAriaLabel}\n className=\"flex size-5 shrink-0 cursor-pointer items-center justify-center text-content-secondary hover:text-content-primary active:scale-95\"\n onClick={ac.handleClear}\n >\n <CloseIcon className=\"size-4\" />\n </button>\n )}\n <div className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\">\n <ChevronDownIcon\n className={cn(\"size-5 transition-transform\", ac.isOpen && \"rotate-180\")}\n />\n </div>\n </div>\n </div>\n </Popover.Anchor>\n\n <Popover.Portal>\n <Popover.Content\n sideOffset={4}\n collisionPadding={FLOATING_CONTENT_COLLISION_PADDING}\n onOpenAutoFocus={(e) => e.preventDefault()}\n onCloseAutoFocus={(e) => e.preventDefault()}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\" }}\n className={cn(\n \"w-max min-w-(--radix-popper-anchor-width) max-w-(--radix-popover-content-available-width) overflow-hidden rounded-sm border border-neutral-alphas-200 bg-bg-primary text-content-primary shadow-[0_4px_16px_rgba(0,0,0,0.10)]\",\n \"data-[state=closed]:animate-out data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n )}\n >\n <div\n ref={ac.listRef}\n id={ac.listboxId}\n role=\"listbox\"\n aria-label={label ?? ariaLabel ?? \"Options\"}\n aria-multiselectable={ac.isMulti || undefined}\n className=\"max-h-60 overflow-y-auto p-1\"\n >\n <AutocompleteDropdownContent\n loading={loading}\n loadingText={loadingText}\n emptyText={emptyText}\n visibleOptions={ac.visibleOptions}\n listboxId={ac.listboxId}\n activeIndex={ac.activeIndex}\n isMulti={ac.isMulti}\n selectedMultiValues={ac.selectedMultiValues}\n selectedValue={ac.selectedValue}\n onSelect={ac.handleSelect}\n onMouseEnter={ac.setActiveIndex}\n renderOption={renderOption}\n />\n </div>\n </Popover.Content>\n </Popover.Portal>\n\n {bottomText && (\n <p\n id={ac.helperTextId}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {bottomText}\n </p>\n )}\n </div>\n </Popover.Root>\n );\n});\n\nAutocomplete.displayName = \"Autocomplete\";\n"],"names":["Popover"],"mappings":"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"}
1
+ {"version":3,"file":"Autocomplete.mjs","sources":["../../../src/components/Autocomplete/Autocomplete.tsx"],"sourcesContent":["import * as Popover from \"@radix-ui/react-popover\";\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { FLOATING_CONTENT_COLLISION_PADDING } from \"@/utils/floatingContentCollisionPadding\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\nimport { SpinnerIcon } from \"../Icons/SpinnerIcon\";\nimport { AutocompleteDropdownContent } from \"./AutocompleteDropdownContent\";\nimport { AutocompleteTag } from \"./AutocompleteTag\";\nimport { useAutocomplete } from \"./useAutocomplete\";\n\nexport type AutocompleteSize = \"48\" | \"40\" | \"32\";\n\nexport interface AutocompleteOption {\n value: string;\n label?: string;\n disabled?: boolean;\n}\n\ninterface AutocompleteBaseProps {\n label?: string;\n \"aria-label\"?: string;\n \"aria-labelledby\"?: string;\n helperText?: string;\n /** @default \"48\" */\n size?: AutocompleteSize;\n /** @default false */\n error?: boolean;\n errorMessage?: string;\n placeholder?: string;\n leftIcon?: React.ReactNode;\n /** @default false */\n fullWidth?: boolean;\n /** @default false */\n disabled?: boolean;\n /** @default false */\n clearable?: boolean;\n clearAriaLabel?: string;\n id?: string;\n className?: string;\n options: AutocompleteOption[];\n inputValue?: string;\n onInputChange?: (value: string) => void;\n filterFn?: (option: AutocompleteOption, query: string) => boolean;\n /** @default false */\n creatable?: boolean;\n creatableLabel?: (inputValue: string) => string;\n onCreate?: (inputValue: string) => void;\n /** @default false */\n loading?: boolean;\n loadingText?: string;\n emptyText?: string;\n renderOption?: (\n option: AutocompleteOption,\n state: { selected: boolean; active: boolean },\n ) => React.ReactNode;\n renderTag?: (option: AutocompleteOption, onRemove: () => void) => React.ReactNode;\n open?: boolean;\n defaultOpen?: boolean;\n onOpenChange?: (open: boolean) => void;\n}\n\ninterface AutocompleteSingleProps extends AutocompleteBaseProps {\n multiple?: false;\n value?: string | null;\n defaultValue?: string | null;\n onChange?: (value: string | null) => void;\n}\n\ninterface AutocompleteMultiProps extends AutocompleteBaseProps {\n multiple: true;\n value?: string[];\n defaultValue?: string[];\n onChange?: (values: string[]) => void;\n}\n\nexport type AutocompleteProps = AutocompleteSingleProps | AutocompleteMultiProps;\n\nconst CONTAINER_HEIGHT: Record<AutocompleteSize, string> = {\n \"48\": \"min-h-12\",\n \"40\": \"min-h-10\",\n \"32\": \"min-h-8\",\n};\n\nconst INPUT_SIZE_CLASSES: Record<AutocompleteSize, string> = {\n \"48\": \"typography-regular-body-lg\",\n \"40\": \"typography-regular-body-lg\",\n \"32\": \"typography-regular-body-md\",\n};\n\nconst PADDING_CLASSES: Record<AutocompleteSize, string> = {\n \"48\": \"px-4 py-1.5 gap-3\",\n \"40\": \"px-4 py-1 gap-3\",\n \"32\": \"px-3 py-1 gap-2\",\n};\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"Autocomplete: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Conditional JSX branches in the render template\nexport const Autocomplete = React.forwardRef<HTMLInputElement, AutocompleteProps>((props, ref) => {\n const {\n label,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n placeholder,\n leftIcon,\n fullWidth = false,\n disabled = false,\n clearable = false,\n clearAriaLabel = \"Clear\",\n className,\n loading = false,\n loadingText,\n emptyText = \"No results\",\n renderOption,\n renderTag,\n } = props;\n\n const ac = useAutocomplete(props);\n\n React.useImperativeHandle(ref, () => ac.inputRef.current as HTMLInputElement);\n\n warnMissingAccessibleName(label, ariaLabel, ariaLabelledby);\n\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n return (\n <Popover.Root open={ac.isOpen && !disabled} onOpenChange={ac.handleOpenChange}>\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-autocomplete-root=\"\"\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={ac.inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <Popover.Anchor asChild>\n {/* biome-ignore lint/a11y/noStaticElementInteractions: Container delegates click to the inner input */}\n {/* biome-ignore lint/a11y/useKeyWithClickEvents: Keyboard interaction is handled by the inner combobox input */}\n <div\n className={cn(\n \"flex flex-wrap items-center overflow-hidden rounded-sm border bg-neutral-alphas-100 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n ac.isOpen && !error && !disabled && \"border-neutral-alphas-400\",\n CONTAINER_HEIGHT[size],\n PADDING_CLASSES[size],\n disabled && \"opacity-50\",\n )}\n onClick={ac.handleContainerClick}\n >\n {leftIcon && (\n <div className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\">\n {leftIcon}\n </div>\n )}\n\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-1.5\">\n {ac.isMulti &&\n ac.selectedMultiOptions.map((opt) => (\n <AutocompleteTag\n key={opt.value}\n option={opt}\n disabled={disabled}\n onRemove={() => ac.toggleMulti(opt.value)}\n renderTag={renderTag}\n />\n ))}\n\n <input\n ref={ac.inputRef}\n id={ac.inputId}\n role=\"combobox\"\n type=\"text\"\n disabled={disabled}\n aria-expanded={ac.isOpen}\n aria-controls={ac.isOpen ? ac.listboxId : undefined}\n aria-activedescendant={ac.activeDescendantId}\n aria-autocomplete=\"list\"\n aria-describedby={bottomText ? ac.helperTextId : undefined}\n aria-invalid={error || undefined}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n autoComplete=\"off\"\n placeholder={\n ac.isMulti && ac.selectedMultiValues.length > 0 ? undefined : placeholder\n }\n value={ac.displayInputValue}\n onChange={ac.handleInputChange}\n onKeyDown={ac.handleKeyDown}\n onFocus={ac.handleFocus}\n onBlur={ac.handleBlur}\n className={cn(\n \"min-w-[40px] flex-1 truncate bg-transparent text-content-primary no-underline placeholder:text-content-secondary placeholder:opacity-40 focus:outline-none disabled:cursor-not-allowed\",\n INPUT_SIZE_CLASSES[size],\n )}\n />\n </div>\n\n <div className=\"flex shrink-0 items-center gap-1\">\n {loading && <SpinnerIcon className=\"size-4 animate-spin text-content-secondary\" />}\n {clearable && ac.hasClearableValue && !disabled && (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={clearAriaLabel}\n className=\"flex size-5 shrink-0 cursor-pointer items-center justify-center text-content-secondary hover:text-content-primary active:scale-95\"\n onClick={ac.handleClear}\n >\n <CloseIcon className=\"size-4\" />\n </button>\n )}\n <div className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\">\n <ChevronDownIcon\n className={cn(\"size-5 transition-transform\", ac.isOpen && \"rotate-180\")}\n />\n </div>\n </div>\n </div>\n </Popover.Anchor>\n\n <Popover.Portal>\n <Popover.Content\n sideOffset={4}\n collisionPadding={FLOATING_CONTENT_COLLISION_PADDING}\n onOpenAutoFocus={(e) => e.preventDefault()}\n onCloseAutoFocus={(e) => e.preventDefault()}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\" }}\n className={cn(\n \"w-max min-w-(--radix-popper-anchor-width) max-w-(--radix-popover-content-available-width) overflow-hidden rounded-sm border border-neutral-alphas-200 bg-bg-primary text-content-primary shadow-[0_4px_16px_rgba(0,0,0,0.10)]\",\n \"data-[state=closed]:animate-out data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n )}\n >\n <div\n ref={ac.listRef}\n id={ac.listboxId}\n role=\"listbox\"\n aria-label={label ?? ariaLabel ?? \"Options\"}\n aria-multiselectable={ac.isMulti || undefined}\n className=\"max-h-60 overflow-y-auto p-1\"\n >\n <AutocompleteDropdownContent\n loading={loading}\n loadingText={loadingText}\n emptyText={emptyText}\n visibleOptions={ac.visibleOptions}\n listboxId={ac.listboxId}\n activeIndex={ac.activeIndex}\n isMulti={ac.isMulti}\n selectedMultiValues={ac.selectedMultiValues}\n selectedValue={ac.selectedValue}\n onSelect={ac.handleSelect}\n onMouseEnter={ac.setActiveIndex}\n renderOption={renderOption}\n />\n </div>\n </Popover.Content>\n </Popover.Portal>\n\n {bottomText && (\n <p\n id={ac.helperTextId}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {bottomText}\n </p>\n )}\n </div>\n </Popover.Root>\n );\n});\n\nAutocomplete.displayName = \"Autocomplete\";\n"],"names":["Popover"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx } from "react/jsx-runtime";
2
+ import { jsxs, jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../../utils/cn.mjs";
5
5
  const SIZE_CLASS = {
@@ -7,20 +7,39 @@ const SIZE_CLASS = {
7
7
  24: "size-6",
8
8
  32: "size-8"
9
9
  };
10
+ const LAYER_TRANSITION = "motion-safe:transition-opacity motion-safe:duration-150 motion-safe:ease-in-out";
11
+ const renderPath = (p) => p.sw !== void 0 ? /* @__PURE__ */ jsx(
12
+ "path",
13
+ {
14
+ d: p.d,
15
+ stroke: "currentColor",
16
+ strokeWidth: p.sw,
17
+ strokeLinecap: "round",
18
+ strokeLinejoin: "round"
19
+ },
20
+ p.d
21
+ ) : /* @__PURE__ */ jsx(
22
+ "path",
23
+ {
24
+ d: p.d,
25
+ fill: "currentColor",
26
+ fillRule: p.eo ? "evenodd" : void 0,
27
+ clipRule: p.eo ? "evenodd" : void 0
28
+ },
29
+ p.d
30
+ );
10
31
  const BaseIcon = React.forwardRef(
11
32
  ({ variants, size = 24, filled, className, ...props }, ref) => {
12
33
  const v = variants[size];
13
- let paths = v?.outlined ?? [];
14
- if (filled) {
15
- if (v?.filled) {
16
- paths = v.filled;
17
- } else if (process.env.NODE_ENV !== "production" && v) {
18
- console.warn(
19
- `[fanvue/ui] Icon at size=${size} has no 'filled' variant; falling back to outlined.`
20
- );
21
- }
34
+ const outlined = v?.outlined ?? [];
35
+ const filledPaths = v?.filled;
36
+ const showFilled = !!(filled && filledPaths);
37
+ if (filled && !filledPaths && v && process.env.NODE_ENV !== "production") {
38
+ console.warn(
39
+ `[fanvue/ui] Icon at size=${size} has no 'filled' variant; falling back to outlined.`
40
+ );
22
41
  }
23
- return /* @__PURE__ */ jsx(
42
+ return /* @__PURE__ */ jsxs(
24
43
  "svg",
25
44
  {
26
45
  ref,
@@ -29,28 +48,10 @@ const BaseIcon = React.forwardRef(
29
48
  "aria-hidden": "true",
30
49
  className: cn(SIZE_CLASS[size], className),
31
50
  ...props,
32
- children: paths.map(
33
- (p) => p.sw !== void 0 ? /* @__PURE__ */ jsx(
34
- "path",
35
- {
36
- d: p.d,
37
- stroke: "currentColor",
38
- strokeWidth: p.sw,
39
- strokeLinecap: "round",
40
- strokeLinejoin: "round"
41
- },
42
- p.d
43
- ) : /* @__PURE__ */ jsx(
44
- "path",
45
- {
46
- d: p.d,
47
- fill: "currentColor",
48
- fillRule: p.eo ? "evenodd" : void 0,
49
- clipRule: p.eo ? "evenodd" : void 0
50
- },
51
- p.d
52
- )
53
- )
51
+ children: [
52
+ /* @__PURE__ */ jsx("g", { className: cn(LAYER_TRANSITION, showFilled ? "opacity-0" : "opacity-100"), children: outlined.map(renderPath) }),
53
+ filledPaths && /* @__PURE__ */ jsx("g", { className: cn(LAYER_TRANSITION, showFilled ? "opacity-100" : "opacity-0"), children: filledPaths.map(renderPath) })
54
+ ]
54
55
  }
55
56
  );
56
57
  }
@@ -1 +1 @@
1
- {"version":3,"file":"BaseIcon.mjs","sources":["../../../src/components/Icons/BaseIcon.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport type { BaseIconProps, IconSize, IconVariants } from \"./types\";\n\nconst SIZE_CLASS: Record<IconSize, string> = {\n 16: \"size-4\",\n 24: \"size-6\",\n 32: \"size-8\",\n};\n\ninterface InternalBaseIconProps extends BaseIconProps {\n variants: IconVariants;\n}\n\n/**\n * Internal renderer for prop-based icons. Each public icon (e.g. `HeartIcon`)\n * is a thin wrapper that supplies its own `variants` table and re-exports\n * {@link BaseIconProps} as `*IconProps`. Not exported from the package.\n */\nexport const BaseIcon = React.forwardRef<SVGSVGElement, InternalBaseIconProps>(\n ({ variants, size = 24, filled, className, ...props }, ref) => {\n const v = variants[size];\n let paths = v?.outlined ?? [];\n if (filled) {\n if (v?.filled) {\n paths = v.filled;\n } else if (process.env.NODE_ENV !== \"production\" && v) {\n console.warn(\n `[fanvue/ui] Icon at size=${size} has no 'filled' variant; falling back to outlined.`,\n );\n }\n }\n return (\n <svg\n ref={ref}\n viewBox={`0 0 ${size} ${size}`}\n fill=\"none\"\n aria-hidden=\"true\"\n className={cn(SIZE_CLASS[size], className)}\n {...props}\n >\n {paths.map((p) =>\n p.sw !== undefined ? (\n <path\n key={p.d}\n d={p.d}\n stroke=\"currentColor\"\n strokeWidth={p.sw}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n ) : (\n <path\n key={p.d}\n d={p.d}\n fill=\"currentColor\"\n fillRule={p.eo ? \"evenodd\" : undefined}\n clipRule={p.eo ? \"evenodd\" : undefined}\n />\n ),\n )}\n </svg>\n );\n },\n);\n\nBaseIcon.displayName = \"BaseIcon\";\n"],"names":[],"mappings":";;;;AAIA,MAAM,aAAuC;AAAA,EAC3C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAWO,MAAM,WAAW,MAAM;AAAA,EAC5B,CAAC,EAAE,UAAU,OAAO,IAAI,QAAQ,WAAW,GAAG,MAAA,GAAS,QAAQ;AAC7D,UAAM,IAAI,SAAS,IAAI;AACvB,QAAI,QAAQ,GAAG,YAAY,CAAA;AAC3B,QAAI,QAAQ;AACV,UAAI,GAAG,QAAQ;AACb,gBAAQ,EAAE;AAAA,MACZ,WAAW,QAAQ,IAAI,aAAa,gBAAgB,GAAG;AACrD,gBAAQ;AAAA,UACN,4BAA4B,IAAI;AAAA,QAAA;AAAA,MAEpC;AAAA,IACF;AACA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,SAAS,OAAO,IAAI,IAAI,IAAI;AAAA,QAC5B,MAAK;AAAA,QACL,eAAY;AAAA,QACZ,WAAW,GAAG,WAAW,IAAI,GAAG,SAAS;AAAA,QACxC,GAAG;AAAA,QAEH,UAAA,MAAM;AAAA,UAAI,CAAC,MACV,EAAE,OAAO,SACP;AAAA,YAAC;AAAA,YAAA;AAAA,cAEC,GAAG,EAAE;AAAA,cACL,QAAO;AAAA,cACP,aAAa,EAAE;AAAA,cACf,eAAc;AAAA,cACd,gBAAe;AAAA,YAAA;AAAA,YALV,EAAE;AAAA,UAAA,IAQT;AAAA,YAAC;AAAA,YAAA;AAAA,cAEC,GAAG,EAAE;AAAA,cACL,MAAK;AAAA,cACL,UAAU,EAAE,KAAK,YAAY;AAAA,cAC7B,UAAU,EAAE,KAAK,YAAY;AAAA,YAAA;AAAA,YAJxB,EAAE;AAAA,UAAA;AAAA,QAKT;AAAA,MAEJ;AAAA,IAAA;AAAA,EAGN;AACF;AAEA,SAAS,cAAc;"}
1
+ {"version":3,"file":"BaseIcon.mjs","sources":["../../../src/components/Icons/BaseIcon.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport type { BaseIconProps, IconPath, IconSize, IconVariants } from \"./types\";\n\nconst SIZE_CLASS: Record<IconSize, string> = {\n 16: \"size-4\",\n 24: \"size-6\",\n 32: \"size-8\",\n};\n\nconst LAYER_TRANSITION =\n \"motion-safe:transition-opacity motion-safe:duration-150 motion-safe:ease-in-out\";\n\ninterface InternalBaseIconProps extends BaseIconProps {\n variants: IconVariants;\n}\n\nconst renderPath = (p: IconPath) =>\n p.sw !== undefined ? (\n <path\n key={p.d}\n d={p.d}\n stroke=\"currentColor\"\n strokeWidth={p.sw}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n />\n ) : (\n <path\n key={p.d}\n d={p.d}\n fill=\"currentColor\"\n fillRule={p.eo ? \"evenodd\" : undefined}\n clipRule={p.eo ? \"evenodd\" : undefined}\n />\n );\n\n/**\n * Internal renderer for prop-based icons. Each public icon (e.g. `HeartIcon`)\n * is a thin wrapper that supplies its own `variants` table and re-exports\n * {@link BaseIconProps} as `*IconProps`. Not exported from the package.\n *\n * When a `filled` variant exists, both layers are rendered simultaneously and\n * the `filled` prop crossfades between them. The same `<svg>` and `<g>` nodes\n * persist across toggles so the transition is visible rather than an instant\n * remount.\n */\nexport const BaseIcon = React.forwardRef<SVGSVGElement, InternalBaseIconProps>(\n ({ variants, size = 24, filled, className, ...props }, ref) => {\n const v = variants[size];\n const outlined = v?.outlined ?? [];\n const filledPaths = v?.filled;\n const showFilled = !!(filled && filledPaths);\n\n if (filled && !filledPaths && v && process.env.NODE_ENV !== \"production\") {\n console.warn(\n `[fanvue/ui] Icon at size=${size} has no 'filled' variant; falling back to outlined.`,\n );\n }\n\n return (\n <svg\n ref={ref}\n viewBox={`0 0 ${size} ${size}`}\n fill=\"none\"\n aria-hidden=\"true\"\n className={cn(SIZE_CLASS[size], className)}\n {...props}\n >\n <g className={cn(LAYER_TRANSITION, showFilled ? \"opacity-0\" : \"opacity-100\")}>\n {outlined.map(renderPath)}\n </g>\n {filledPaths && (\n <g className={cn(LAYER_TRANSITION, showFilled ? \"opacity-100\" : \"opacity-0\")}>\n {filledPaths.map(renderPath)}\n </g>\n )}\n </svg>\n );\n },\n);\n\nBaseIcon.displayName = \"BaseIcon\";\n"],"names":[],"mappings":";;;;AAIA,MAAM,aAAuC;AAAA,EAC3C,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAEA,MAAM,mBACJ;AAMF,MAAM,aAAa,CAAC,MAClB,EAAE,OAAO,SACP;AAAA,EAAC;AAAA,EAAA;AAAA,IAEC,GAAG,EAAE;AAAA,IACL,QAAO;AAAA,IACP,aAAa,EAAE;AAAA,IACf,eAAc;AAAA,IACd,gBAAe;AAAA,EAAA;AAAA,EALV,EAAE;AAMT,IAEA;AAAA,EAAC;AAAA,EAAA;AAAA,IAEC,GAAG,EAAE;AAAA,IACL,MAAK;AAAA,IACL,UAAU,EAAE,KAAK,YAAY;AAAA,IAC7B,UAAU,EAAE,KAAK,YAAY;AAAA,EAAA;AAAA,EAJxB,EAAE;AAKT;AAaG,MAAM,WAAW,MAAM;AAAA,EAC5B,CAAC,EAAE,UAAU,OAAO,IAAI,QAAQ,WAAW,GAAG,MAAA,GAAS,QAAQ;AAC7D,UAAM,IAAI,SAAS,IAAI;AACvB,UAAM,WAAW,GAAG,YAAY,CAAA;AAChC,UAAM,cAAc,GAAG;AACvB,UAAM,aAAa,CAAC,EAAE,UAAU;AAEhC,QAAI,UAAU,CAAC,eAAe,KAAK,QAAQ,IAAI,aAAa,cAAc;AACxE,cAAQ;AAAA,QACN,4BAA4B,IAAI;AAAA,MAAA;AAAA,IAEpC;AAEA,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,SAAS,OAAO,IAAI,IAAI,IAAI;AAAA,QAC5B,MAAK;AAAA,QACL,eAAY;AAAA,QACZ,WAAW,GAAG,WAAW,IAAI,GAAG,SAAS;AAAA,QACxC,GAAG;AAAA,QAEJ,UAAA;AAAA,UAAA,oBAAC,KAAA,EAAE,WAAW,GAAG,kBAAkB,aAAa,cAAc,aAAa,GACxE,UAAA,SAAS,IAAI,UAAU,EAAA,CAC1B;AAAA,UACC,eACC,oBAAC,KAAA,EAAE,WAAW,GAAG,kBAAkB,aAAa,gBAAgB,WAAW,GACxE,UAAA,YAAY,IAAI,UAAU,EAAA,CAC7B;AAAA,QAAA;AAAA,MAAA;AAAA,IAAA;AAAA,EAIR;AACF;AAEA,SAAS,cAAc;"}
@@ -78,7 +78,7 @@ const Select = React.forwardRef(
78
78
  "aria-describedby": bottomText ? helperTextId : void 0,
79
79
  "aria-invalid": error || void 0,
80
80
  className: cn(
81
- "flex w-full cursor-pointer items-center justify-between rounded-sm border bg-neutral-alphas-50 outline-none motion-safe:transition-colors",
81
+ "flex w-full cursor-pointer items-center justify-between rounded-sm border bg-neutral-alphas-50 outline-none motion-safe:transition-colors focus-visible:shadow-focus-ring",
82
82
  TRIGGER_HEIGHT[size],
83
83
  TRIGGER_PADDING_X[size],
84
84
  TRIGGER_GAP[size],
@@ -1 +1 @@
1
- {"version":3,"file":"Select.mjs","sources":["../../../src/components/Select/Select.tsx"],"sourcesContent":["import * as SelectPrimitive from \"@radix-ui/react-select\";\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { FLOATING_CONTENT_COLLISION_PADDING } from \"@/utils/floatingContentCollisionPadding\";\nimport { CheckIcon } from \"../Icons/CheckIcon\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\n\n/** Select field height in pixels. */\nexport type SelectSize = \"48\" | \"40\" | \"32\";\n\ntype SelectContextValue = {\n size: SelectSize;\n error: boolean;\n disabled?: boolean;\n};\n\nconst SelectContext = React.createContext<SelectContextValue>({\n size: \"48\",\n error: false,\n});\n\nconst TRIGGER_HEIGHT: Record<SelectSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst TRIGGER_PADDING_X: Record<SelectSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst TRIGGER_GAP: Record<SelectSize, string> = {\n \"48\": \"gap-3\",\n \"40\": \"gap-3\",\n \"32\": \"gap-2\",\n};\n\nconst TRIGGER_TYPOGRAPHY: Record<SelectSize, string> = {\n \"48\": \"typography-regular-body-lg\",\n \"40\": \"typography-regular-body-lg\",\n \"32\": \"typography-regular-body-md\",\n};\n\nexport interface SelectProps extends Omit<SelectPrimitive.SelectProps, \"dir\"> {\n /** Label text displayed above the trigger. Also used as the accessible name. */\n label?: string;\n /** Accessible name applied directly to the trigger button when no visible `label` is provided. */\n \"aria-label\"?: string;\n /** ID of an external element that labels the trigger button. */\n \"aria-labelledby\"?: string;\n /** Helper text displayed below the trigger. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the select field in pixels. @default \"48\" */\n size?: SelectSize;\n /** Whether the field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the trigger. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Placeholder shown when no value is selected. */\n placeholder?: string;\n /** Icon element displayed at the left side of the trigger. */\n leftIcon?: React.ReactNode;\n /** Whether the field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Wraps the `className` of the outermost container div. */\n className?: string;\n /** HTML `id` applied to the trigger button. Auto-generated if omitted. */\n id?: string;\n}\n\n/**\n * A select field with optional label, helper/error text, and an icon slot,\n * built on Radix UI Select for full accessibility and keyboard navigation.\n *\n * Pair with {@link SelectContent} and {@link SelectItem} to provide options.\n *\n * @example\n * ```tsx\n * <Select label=\"Country\" placeholder=\"Select a country\">\n * <SelectContent>\n * <SelectItem value=\"us\">United States</SelectItem>\n * <SelectItem value=\"uk\">United Kingdom</SelectItem>\n * </SelectContent>\n * </Select>\n * ```\n */\nexport const Select = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Trigger>,\n SelectProps\n>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n placeholder,\n leftIcon,\n fullWidth = false,\n className,\n id,\n disabled,\n children,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const triggerId = id ?? generatedId;\n const helperTextId = `${triggerId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n return (\n <SelectContext.Provider value={{ size, error, disabled }}>\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={triggerId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <SelectPrimitive.Root disabled={disabled} {...props}>\n <SelectPrimitive.Trigger\n ref={ref}\n id={triggerId}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-describedby={bottomText ? helperTextId : undefined}\n aria-invalid={error || undefined}\n className={cn(\n \"flex w-full cursor-pointer items-center justify-between rounded-sm border bg-neutral-alphas-50 outline-none motion-safe:transition-colors\",\n TRIGGER_HEIGHT[size],\n TRIGGER_PADDING_X[size],\n TRIGGER_GAP[size],\n TRIGGER_TYPOGRAPHY[size],\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled &&\n !error &&\n \"hover:border-neutral-alphas-400 data-[state=open]:border-neutral-alphas-400\",\n disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n {leftIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\"\n data-testid=\"left-icon\"\n >\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 flex-1 truncate text-left text-content-primary [&>[data-placeholder]]:text-content-secondary [&>[data-placeholder]]:opacity-40\">\n <SelectPrimitive.Value placeholder={placeholder} />\n </span>\n </div>\n\n <SelectPrimitive.Icon asChild>\n <ChevronDownIcon />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n\n {children}\n </SelectPrimitive.Root>\n\n {bottomText && (\n <p\n id={helperTextId}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {bottomText}\n </p>\n )}\n </div>\n </SelectContext.Provider>\n );\n },\n);\n\nSelect.displayName = \"Select\";\n\nexport interface SelectContentProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> {}\n\n/**\n * The dropdown panel rendered inside a portal. Place {@link SelectItem} elements\n * (and optionally {@link SelectGroup} / {@link SelectLabel}) as children.\n */\nexport const SelectContent = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Content>,\n SelectContentProps\n>(\n (\n {\n className,\n children,\n position = \"popper\",\n sideOffset = 4,\n collisionPadding = FLOATING_CONTENT_COLLISION_PADDING,\n style,\n ...props\n },\n ref,\n ) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n className={cn(\n \"relative w-max min-w-(--radix-select-trigger-width) max-w-(--radix-select-content-available-width) overflow-hidden rounded-sm border border-neutral-alphas-200 bg-bg-primary text-content-primary shadow-[0_4px_16px_rgba(0,0,0,0.10)]\",\n \"data-[state=closed]:animate-out data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.Viewport className=\"max-h-[var(--radix-select-content-available-height)] overflow-y-auto p-1\">\n {children}\n </SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n ),\n);\n\nSelectContent.displayName = \"SelectContent\";\n\nexport interface SelectItemProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> {}\n\n/**\n * An individual option inside {@link SelectContent}.\n */\nexport const SelectItem = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Item>,\n SelectItemProps\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"typography-regular-body-lg relative flex w-full cursor-pointer select-none items-center gap-2 rounded-xs py-2 pr-2 pl-3 text-content-primary outline-none\",\n \"focus:bg-neutral-alphas-100 data-disabled:pointer-events-none data-disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n <SelectPrimitive.ItemIndicator className=\"ml-auto flex size-4 shrink-0 items-center justify-center\">\n <CheckIcon className=\"size-4 text-content-primary\" />\n </SelectPrimitive.ItemIndicator>\n </SelectPrimitive.Item>\n));\n\nSelectItem.displayName = \"SelectItem\";\n\n/** Props for {@link SelectGroup}. */\nexport type SelectGroupProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Group>;\n\n/**\n * Groups related {@link SelectItem} elements under a {@link SelectLabel}.\n */\nexport const SelectGroup = SelectPrimitive.Group;\nSelectGroup.displayName = \"SelectGroup\";\n\nexport interface SelectLabelProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> {}\n\n/**\n * A non-interactive label shown above a {@link SelectGroup}.\n */\nexport const SelectLabel = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Label>,\n SelectLabelProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn(\"typography-semibold-body-sm px-3 py-1.5 text-content-secondary\", className)}\n {...props}\n />\n));\n\nSelectLabel.displayName = \"SelectLabel\";\n\nexport interface SelectSeparatorProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> {}\n\n/** A horizontal rule that visually separates groups in {@link SelectContent}. */\nexport const SelectSeparator = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Separator>,\n SelectSeparatorProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-neutral-alphas-200\", className)}\n {...props}\n />\n));\n\nSelectSeparator.displayName = \"SelectSeparator\";\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Select.mjs","sources":["../../../src/components/Select/Select.tsx"],"sourcesContent":["import * as SelectPrimitive from \"@radix-ui/react-select\";\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { FLOATING_CONTENT_COLLISION_PADDING } from \"@/utils/floatingContentCollisionPadding\";\nimport { CheckIcon } from \"../Icons/CheckIcon\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\n\n/** Select field height in pixels. */\nexport type SelectSize = \"48\" | \"40\" | \"32\";\n\ntype SelectContextValue = {\n size: SelectSize;\n error: boolean;\n disabled?: boolean;\n};\n\nconst SelectContext = React.createContext<SelectContextValue>({\n size: \"48\",\n error: false,\n});\n\nconst TRIGGER_HEIGHT: Record<SelectSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst TRIGGER_PADDING_X: Record<SelectSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst TRIGGER_GAP: Record<SelectSize, string> = {\n \"48\": \"gap-3\",\n \"40\": \"gap-3\",\n \"32\": \"gap-2\",\n};\n\nconst TRIGGER_TYPOGRAPHY: Record<SelectSize, string> = {\n \"48\": \"typography-regular-body-lg\",\n \"40\": \"typography-regular-body-lg\",\n \"32\": \"typography-regular-body-md\",\n};\n\nexport interface SelectProps extends Omit<SelectPrimitive.SelectProps, \"dir\"> {\n /** Label text displayed above the trigger. Also used as the accessible name. */\n label?: string;\n /** Accessible name applied directly to the trigger button when no visible `label` is provided. */\n \"aria-label\"?: string;\n /** ID of an external element that labels the trigger button. */\n \"aria-labelledby\"?: string;\n /** Helper text displayed below the trigger. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the select field in pixels. @default \"48\" */\n size?: SelectSize;\n /** Whether the field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the trigger. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Placeholder shown when no value is selected. */\n placeholder?: string;\n /** Icon element displayed at the left side of the trigger. */\n leftIcon?: React.ReactNode;\n /** Whether the field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Wraps the `className` of the outermost container div. */\n className?: string;\n /** HTML `id` applied to the trigger button. Auto-generated if omitted. */\n id?: string;\n}\n\n/**\n * A select field with optional label, helper/error text, and an icon slot,\n * built on Radix UI Select for full accessibility and keyboard navigation.\n *\n * Pair with {@link SelectContent} and {@link SelectItem} to provide options.\n *\n * @example\n * ```tsx\n * <Select label=\"Country\" placeholder=\"Select a country\">\n * <SelectContent>\n * <SelectItem value=\"us\">United States</SelectItem>\n * <SelectItem value=\"uk\">United Kingdom</SelectItem>\n * </SelectContent>\n * </Select>\n * ```\n */\nexport const Select = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Trigger>,\n SelectProps\n>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n placeholder,\n leftIcon,\n fullWidth = false,\n className,\n id,\n disabled,\n children,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const triggerId = id ?? generatedId;\n const helperTextId = `${triggerId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n return (\n <SelectContext.Provider value={{ size, error, disabled }}>\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={triggerId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <SelectPrimitive.Root disabled={disabled} {...props}>\n <SelectPrimitive.Trigger\n ref={ref}\n id={triggerId}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-describedby={bottomText ? helperTextId : undefined}\n aria-invalid={error || undefined}\n className={cn(\n \"flex w-full cursor-pointer items-center justify-between rounded-sm border bg-neutral-alphas-50 outline-none motion-safe:transition-colors focus-visible:shadow-focus-ring\",\n TRIGGER_HEIGHT[size],\n TRIGGER_PADDING_X[size],\n TRIGGER_GAP[size],\n TRIGGER_TYPOGRAPHY[size],\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled &&\n !error &&\n \"hover:border-neutral-alphas-400 data-[state=open]:border-neutral-alphas-400\",\n disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n {leftIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\"\n data-testid=\"left-icon\"\n >\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 flex-1 truncate text-left text-content-primary [&>[data-placeholder]]:text-content-secondary [&>[data-placeholder]]:opacity-40\">\n <SelectPrimitive.Value placeholder={placeholder} />\n </span>\n </div>\n\n <SelectPrimitive.Icon asChild>\n <ChevronDownIcon />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n\n {children}\n </SelectPrimitive.Root>\n\n {bottomText && (\n <p\n id={helperTextId}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {bottomText}\n </p>\n )}\n </div>\n </SelectContext.Provider>\n );\n },\n);\n\nSelect.displayName = \"Select\";\n\nexport interface SelectContentProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> {}\n\n/**\n * The dropdown panel rendered inside a portal. Place {@link SelectItem} elements\n * (and optionally {@link SelectGroup} / {@link SelectLabel}) as children.\n */\nexport const SelectContent = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Content>,\n SelectContentProps\n>(\n (\n {\n className,\n children,\n position = \"popper\",\n sideOffset = 4,\n collisionPadding = FLOATING_CONTENT_COLLISION_PADDING,\n style,\n ...props\n },\n ref,\n ) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n className={cn(\n \"relative w-max min-w-(--radix-select-trigger-width) max-w-(--radix-select-content-available-width) overflow-hidden rounded-sm border border-neutral-alphas-200 bg-bg-primary text-content-primary shadow-[0_4px_16px_rgba(0,0,0,0.10)]\",\n \"data-[state=closed]:animate-out data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.Viewport className=\"max-h-[var(--radix-select-content-available-height)] overflow-y-auto p-1\">\n {children}\n </SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n ),\n);\n\nSelectContent.displayName = \"SelectContent\";\n\nexport interface SelectItemProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> {}\n\n/**\n * An individual option inside {@link SelectContent}.\n */\nexport const SelectItem = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Item>,\n SelectItemProps\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"typography-regular-body-lg relative flex w-full cursor-pointer select-none items-center gap-2 rounded-xs py-2 pr-2 pl-3 text-content-primary outline-none\",\n \"focus:bg-neutral-alphas-100 data-disabled:pointer-events-none data-disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n <SelectPrimitive.ItemIndicator className=\"ml-auto flex size-4 shrink-0 items-center justify-center\">\n <CheckIcon className=\"size-4 text-content-primary\" />\n </SelectPrimitive.ItemIndicator>\n </SelectPrimitive.Item>\n));\n\nSelectItem.displayName = \"SelectItem\";\n\n/** Props for {@link SelectGroup}. */\nexport type SelectGroupProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Group>;\n\n/**\n * Groups related {@link SelectItem} elements under a {@link SelectLabel}.\n */\nexport const SelectGroup = SelectPrimitive.Group;\nSelectGroup.displayName = \"SelectGroup\";\n\nexport interface SelectLabelProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> {}\n\n/**\n * A non-interactive label shown above a {@link SelectGroup}.\n */\nexport const SelectLabel = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Label>,\n SelectLabelProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn(\"typography-semibold-body-sm px-3 py-1.5 text-content-secondary\", className)}\n {...props}\n />\n));\n\nSelectLabel.displayName = \"SelectLabel\";\n\nexport interface SelectSeparatorProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> {}\n\n/** A horizontal rule that visually separates groups in {@link SelectContent}. */\nexport const SelectSeparator = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Separator>,\n SelectSeparatorProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-neutral-alphas-200\", className)}\n {...props}\n />\n));\n\nSelectSeparator.displayName = \"SelectSeparator\";\n"],"names":[],"mappings":"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"}
@@ -32,7 +32,7 @@ const CLEAR_BUTTON_RIGHT = {
32
32
  };
33
33
  function getContainerClassName(size, error, disabled) {
34
34
  return cn(
35
- "relative rounded-sm border bg-neutral-alphas-50 has-focus-visible:outline-none motion-safe:transition-colors",
35
+ "relative rounded-sm border bg-neutral-alphas-50 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors",
36
36
  error ? "border-error-content" : "border-transparent",
37
37
  !disabled && !error && "hover:border-neutral-alphas-400",
38
38
  CONTAINER_MIN_HEIGHT[size],
@@ -1 +1 @@
1
- {"version":3,"file":"TextArea.mjs","sources":["../../../src/components/TextArea/TextArea.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CheckOutlineIcon } from \"../Icons/CheckOutlineIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\n\n/** Text area height in pixels. */\nexport type TextAreaSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextAreaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, \"size\"> {\n /** Label text displayed above the textarea. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the textarea. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Minimum height of the text area in pixels. @default \"48\" */\n size?: TextAreaSize;\n /** Whether the text area is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the textarea. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text area is validated. @default false */\n validated?: boolean;\n /** Whether the text area stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Whether to show a clear button when text is present. @default false */\n showClearButton?: boolean;\n /** Callback fired when the clear button is clicked. Note: `onChange` is also called with an empty value when clearing. */\n onClear?: () => void;\n /** Minimum number of rows (lines) for the textarea. */\n minRows?: number;\n /** Maximum number of rows (lines) for the textarea. */\n maxRows?: number;\n /** Whether the textarea can be resized by the user. @default true */\n resizable?: boolean;\n}\n\nconst CONTAINER_MIN_HEIGHT: Record<TextAreaSize, string> = {\n \"48\": \"min-h-12\",\n \"40\": \"min-h-10\",\n \"32\": \"min-h-8\",\n};\n\nconst TEXTAREA_SIZE_CLASSES: Record<TextAreaSize, string> = {\n \"48\": \"py-3 typography-regular-body-lg autofill-body-lg\",\n \"40\": \"py-2 typography-regular-body-lg autofill-body-lg\",\n \"32\": \"py-2 typography-regular-body-md autofill-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextAreaSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst PADDING_RIGHT_WITH_CLEAR: Record<TextAreaSize, string> = {\n \"48\": \"pr-11\",\n \"40\": \"pr-11\",\n \"32\": \"pr-10\",\n};\n\nconst CLEAR_BUTTON_RIGHT: Record<TextAreaSize, string> = {\n \"48\": \"right-4 top-3\",\n \"40\": \"right-4 top-2\",\n \"32\": \"right-3 top-2\",\n};\n\nfunction getContainerClassName(size: TextAreaSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative rounded-sm border bg-neutral-alphas-50 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_MIN_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getTextareaClassName(\n size: TextAreaSize,\n hasClearButton: boolean,\n hasMinRows: boolean,\n resizable: boolean,\n) {\n return cn(\n \"h-full w-full bg-transparent text-content-primary no-underline placeholder:text-content-secondary focus:outline-none disabled:cursor-not-allowed\",\n resizable ? \"resize-y\" : \"resize-none\",\n !hasMinRows && \"min-h-[80px]\",\n TEXTAREA_SIZE_CLASSES[size],\n PADDING_HORIZONTAL[size],\n hasClearButton ? PADDING_RIGHT_WITH_CLEAR[size] : \"\",\n );\n}\n\nfunction TextAreaHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextArea: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\nfunction calculateMaxHeight(size: TextAreaSize, maxRows?: number): string | undefined {\n if (!maxRows) return undefined;\n\n // Line height is 24px for body-1 (sizes 48 and 40) and 20px for body-2 (size 32)\n const lineHeight = size === \"32\" ? 20 : 24;\n // py-2 = 8px, py-3 = 12px\n const verticalPadding = size === \"32\" ? 8 : size === \"40\" ? 8 : 12;\n\n return `${lineHeight * maxRows + verticalPadding * 2}px`;\n}\n\nfunction useTextAreaValue(\n value: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"value\"],\n defaultValue: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"defaultValue\"],\n showClearButton: boolean,\n onChange?: React.ChangeEventHandler<HTMLTextAreaElement>,\n onClear?: () => void,\n textareaRef?: React.RefObject<HTMLTextAreaElement | null>,\n) {\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? \"\");\n const resolvedValue = value !== undefined ? value : internalValue;\n\n const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInternalValue(e.target.value);\n onChange?.(e);\n };\n\n const handleClear = () => {\n setInternalValue(\"\");\n\n if (onChange && textareaRef?.current) {\n const syntheticEvent = {\n target: { ...textareaRef.current, value: \"\" },\n currentTarget: { ...textareaRef.current, value: \"\" },\n } as React.ChangeEvent<HTMLTextAreaElement>;\n onChange(syntheticEvent);\n }\n\n onClear?.();\n };\n\n return {\n resolvedValue,\n displayValue: showClearButton ? resolvedValue : value,\n resolvedDefaultValue: showClearButton ? undefined : defaultValue,\n handleChange,\n handleClear,\n };\n}\n\n/**\n * A multi-line text input with optional label, helper/error text, and clear button.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextArea\n * label=\"Description\"\n * placeholder=\"Enter your description...\"\n * showClearButton\n * error={!!descError}\n * errorMessage={descError}\n * />\n * ```\n */\nexport const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n className,\n id,\n disabled,\n fullWidth = false,\n showClearButton = false,\n onClear,\n value,\n defaultValue,\n onChange,\n minRows,\n maxRows,\n resizable = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n const maxHeight = calculateMaxHeight(size, maxRows);\n\n const internalRef = React.useRef<HTMLTextAreaElement>(null);\n\n const { resolvedValue, displayValue, resolvedDefaultValue, handleChange, handleClear } =\n useTextAreaValue(value, defaultValue, showClearButton, onChange, onClear, internalRef);\n\n const mergedRef = (node: HTMLTextAreaElement | null) => {\n internalRef.current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n }\n };\n\n const showClear = showClearButton && resolvedValue !== \"\" && !disabled;\n const showValidated = validated && !showClear;\n const ariaDescribedBy = bottomText ? helperTextId : undefined;\n const textareaStyle = maxHeight ? { maxHeight } : undefined;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n <textarea\n ref={mergedRef}\n id={inputId}\n disabled={disabled}\n aria-describedby={ariaDescribedBy}\n aria-invalid={error || undefined}\n className={getTextareaClassName(size, showClear, !!minRows, resizable)}\n value={displayValue}\n defaultValue={resolvedDefaultValue}\n onChange={handleChange}\n rows={minRows}\n style={textareaStyle}\n {...props}\n />\n\n {showClear && (\n <IconButton\n variant=\"tertiary\"\n size=\"24\"\n icon={<CloseIcon />}\n aria-label=\"Clear text\"\n onClick={handleClear}\n className={cn(\n \"absolute flex size-5 items-center justify-center self-end\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n />\n )}\n {showValidated && (\n <div\n className={cn(\n \"pointer-events-none absolute flex size-5 items-center justify-center\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n >\n <CheckOutlineIcon className=\"text-success-content\" />\n </div>\n )}\n </div>\n\n {bottomText && (\n <TextAreaHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextAreaHelperText>\n )}\n </div>\n );\n },\n);\n\nTextArea.displayName = \"TextArea\";\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TextArea.mjs","sources":["../../../src/components/TextArea/TextArea.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { IconButton } from \"../IconButton/IconButton\";\nimport { CheckOutlineIcon } from \"../Icons/CheckOutlineIcon\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\n\n/** Text area height in pixels. */\nexport type TextAreaSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextAreaProps\n extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaElement>, \"size\"> {\n /** Label text displayed above the textarea. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the textarea. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Minimum height of the text area in pixels. @default \"48\" */\n size?: TextAreaSize;\n /** Whether the text area is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the textarea. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text area is validated. @default false */\n validated?: boolean;\n /** Whether the text area stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Whether to show a clear button when text is present. @default false */\n showClearButton?: boolean;\n /** Callback fired when the clear button is clicked. Note: `onChange` is also called with an empty value when clearing. */\n onClear?: () => void;\n /** Minimum number of rows (lines) for the textarea. */\n minRows?: number;\n /** Maximum number of rows (lines) for the textarea. */\n maxRows?: number;\n /** Whether the textarea can be resized by the user. @default true */\n resizable?: boolean;\n}\n\nconst CONTAINER_MIN_HEIGHT: Record<TextAreaSize, string> = {\n \"48\": \"min-h-12\",\n \"40\": \"min-h-10\",\n \"32\": \"min-h-8\",\n};\n\nconst TEXTAREA_SIZE_CLASSES: Record<TextAreaSize, string> = {\n \"48\": \"py-3 typography-regular-body-lg autofill-body-lg\",\n \"40\": \"py-2 typography-regular-body-lg autofill-body-lg\",\n \"32\": \"py-2 typography-regular-body-md autofill-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextAreaSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst PADDING_RIGHT_WITH_CLEAR: Record<TextAreaSize, string> = {\n \"48\": \"pr-11\",\n \"40\": \"pr-11\",\n \"32\": \"pr-10\",\n};\n\nconst CLEAR_BUTTON_RIGHT: Record<TextAreaSize, string> = {\n \"48\": \"right-4 top-3\",\n \"40\": \"right-4 top-2\",\n \"32\": \"right-3 top-2\",\n};\n\nfunction getContainerClassName(size: TextAreaSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative rounded-sm border bg-neutral-alphas-50 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_MIN_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getTextareaClassName(\n size: TextAreaSize,\n hasClearButton: boolean,\n hasMinRows: boolean,\n resizable: boolean,\n) {\n return cn(\n \"h-full w-full bg-transparent text-content-primary no-underline placeholder:text-content-secondary focus:outline-none disabled:cursor-not-allowed\",\n resizable ? \"resize-y\" : \"resize-none\",\n !hasMinRows && \"min-h-[80px]\",\n TEXTAREA_SIZE_CLASSES[size],\n PADDING_HORIZONTAL[size],\n hasClearButton ? PADDING_RIGHT_WITH_CLEAR[size] : \"\",\n );\n}\n\nfunction TextAreaHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-regular-body-sm px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextArea: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\nfunction calculateMaxHeight(size: TextAreaSize, maxRows?: number): string | undefined {\n if (!maxRows) return undefined;\n\n // Line height is 24px for body-1 (sizes 48 and 40) and 20px for body-2 (size 32)\n const lineHeight = size === \"32\" ? 20 : 24;\n // py-2 = 8px, py-3 = 12px\n const verticalPadding = size === \"32\" ? 8 : size === \"40\" ? 8 : 12;\n\n return `${lineHeight * maxRows + verticalPadding * 2}px`;\n}\n\nfunction useTextAreaValue(\n value: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"value\"],\n defaultValue: React.TextareaHTMLAttributes<HTMLTextAreaElement>[\"defaultValue\"],\n showClearButton: boolean,\n onChange?: React.ChangeEventHandler<HTMLTextAreaElement>,\n onClear?: () => void,\n textareaRef?: React.RefObject<HTMLTextAreaElement | null>,\n) {\n const [internalValue, setInternalValue] = React.useState(defaultValue ?? \"\");\n const resolvedValue = value !== undefined ? value : internalValue;\n\n const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInternalValue(e.target.value);\n onChange?.(e);\n };\n\n const handleClear = () => {\n setInternalValue(\"\");\n\n if (onChange && textareaRef?.current) {\n const syntheticEvent = {\n target: { ...textareaRef.current, value: \"\" },\n currentTarget: { ...textareaRef.current, value: \"\" },\n } as React.ChangeEvent<HTMLTextAreaElement>;\n onChange(syntheticEvent);\n }\n\n onClear?.();\n };\n\n return {\n resolvedValue,\n displayValue: showClearButton ? resolvedValue : value,\n resolvedDefaultValue: showClearButton ? undefined : defaultValue,\n handleChange,\n handleClear,\n };\n}\n\n/**\n * A multi-line text input with optional label, helper/error text, and clear button.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextArea\n * label=\"Description\"\n * placeholder=\"Enter your description...\"\n * showClearButton\n * error={!!descError}\n * errorMessage={descError}\n * />\n * ```\n */\nexport const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n className,\n id,\n disabled,\n fullWidth = false,\n showClearButton = false,\n onClear,\n value,\n defaultValue,\n onChange,\n minRows,\n maxRows,\n resizable = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n const maxHeight = calculateMaxHeight(size, maxRows);\n\n const internalRef = React.useRef<HTMLTextAreaElement>(null);\n\n const { resolvedValue, displayValue, resolvedDefaultValue, handleChange, handleClear } =\n useTextAreaValue(value, defaultValue, showClearButton, onChange, onClear, internalRef);\n\n const mergedRef = (node: HTMLTextAreaElement | null) => {\n internalRef.current = node;\n if (typeof ref === \"function\") {\n ref(node);\n } else if (ref) {\n (ref as React.MutableRefObject<HTMLTextAreaElement | null>).current = node;\n }\n };\n\n const showClear = showClearButton && resolvedValue !== \"\" && !disabled;\n const showValidated = validated && !showClear;\n const ariaDescribedBy = bottomText ? helperTextId : undefined;\n const textareaStyle = maxHeight ? { maxHeight } : undefined;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n <textarea\n ref={mergedRef}\n id={inputId}\n disabled={disabled}\n aria-describedby={ariaDescribedBy}\n aria-invalid={error || undefined}\n className={getTextareaClassName(size, showClear, !!minRows, resizable)}\n value={displayValue}\n defaultValue={resolvedDefaultValue}\n onChange={handleChange}\n rows={minRows}\n style={textareaStyle}\n {...props}\n />\n\n {showClear && (\n <IconButton\n variant=\"tertiary\"\n size=\"24\"\n icon={<CloseIcon />}\n aria-label=\"Clear text\"\n onClick={handleClear}\n className={cn(\n \"absolute flex size-5 items-center justify-center self-end\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n />\n )}\n {showValidated && (\n <div\n className={cn(\n \"pointer-events-none absolute flex size-5 items-center justify-center\",\n CLEAR_BUTTON_RIGHT[size],\n )}\n >\n <CheckOutlineIcon className=\"text-success-content\" />\n </div>\n )}\n </div>\n\n {bottomText && (\n <TextAreaHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextAreaHelperText>\n )}\n </div>\n );\n },\n);\n\nTextArea.displayName = \"TextArea\";\n"],"names":[],"mappings":"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"}
@@ -31,7 +31,7 @@ const ICON_INSET = {
31
31
  };
32
32
  function getContainerClassName(size, error, disabled) {
33
33
  return cn(
34
- "relative overflow-hidden rounded-sm border bg-neutral-alphas-50 has-focus-visible:outline-none motion-safe:transition-colors",
34
+ "relative overflow-hidden rounded-sm border bg-neutral-alphas-50 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors",
35
35
  error ? "border-error-content" : "border-transparent",
36
36
  !disabled && !error && "hover:border-neutral-alphas-400",
37
37
  CONTAINER_HEIGHT[size],
@@ -1 +1 @@
1
- {"version":3,"file":"TextField.mjs","sources":["../../../src/components/TextField/TextField.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { CheckOutlineIcon } from \"@/index\";\nimport { cn } from \"../../utils/cn\";\n\n/** Text field height in pixels. */\nexport type TextFieldSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextFieldProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\" | \"prefix\"> {\n /** Label text displayed above the input. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the input. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the text field in pixels. @default \"48\" */\n size?: TextFieldSize;\n /** Whether the text field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the input. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text field is validated. @default false */\n validated?: boolean;\n /** Icon element displayed at the left side of the input. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed at the right side of the input. */\n rightIcon?: React.ReactNode;\n /** Whether the text field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n}\n\nconst CONTAINER_HEIGHT: Record<TextFieldSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst INPUT_SIZE_CLASSES: Record<TextFieldSize, string> = {\n \"48\": \"py-3 typography-regular-body-lg autofill-body-lg\",\n \"40\": \"py-2 typography-regular-body-lg autofill-body-lg\",\n \"32\": \"py-2 typography-regular-body-md autofill-body-md\",\n};\n\nconst INPUT_PL: Record<TextFieldSize, { default: string; withIcon: string }> = {\n \"48\": { default: \"pl-4\", withIcon: \"pl-10\" },\n \"40\": { default: \"pl-4\", withIcon: \"pl-10\" },\n \"32\": { default: \"pl-3\", withIcon: \"pl-9\" },\n};\n\nconst INPUT_PR: Record<TextFieldSize, { default: string; withIcon: string }> = {\n \"48\": { default: \"pr-4\", withIcon: \"pr-10\" },\n \"40\": { default: \"pr-4\", withIcon: \"pr-10\" },\n \"32\": { default: \"pr-3\", withIcon: \"pr-9\" },\n};\n\nconst ICON_INSET: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nfunction getContainerClassName(size: TextFieldSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative overflow-hidden rounded-sm border bg-neutral-alphas-50 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getInputClassName(size: TextFieldSize, hasLeftIcon: boolean, hasRightIcon: boolean) {\n return cn(\n \"h-full w-full rounded-sm bg-transparent text-content-primary no-underline placeholder:text-content-secondary focus:outline-none disabled:cursor-not-allowed\",\n INPUT_SIZE_CLASSES[size],\n hasLeftIcon ? INPUT_PL[size].withIcon : INPUT_PL[size].default,\n hasRightIcon ? INPUT_PR[size].withIcon : INPUT_PR[size].default,\n );\n}\n\nfunction TextFieldHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-regular-body-sm px-2 pt-2 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextField: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n/**\n * A text input field with optional label, helper/error text, and icon slots.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextField\n * label=\"Email\"\n * placeholder=\"you@example.com\"\n * error={!!emailError}\n * errorMessage={emailError}\n * />\n * ```\n */\nexport const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n leftIcon,\n rightIcon,\n className,\n id,\n disabled,\n fullWidth = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n <input\n ref={ref}\n id={inputId}\n disabled={disabled}\n aria-describedby={bottomText ? helperTextId : undefined}\n aria-invalid={error || undefined}\n className={cn(\n getInputClassName(size, !!leftIcon, !!(rightIcon || validated)),\n \"[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n\n {leftIcon && (\n <div\n className={cn(\n \"pointer-events-none absolute inset-y-0 left-0 flex items-center text-content-secondary\",\n ICON_INSET[size],\n )}\n >\n <div className=\"flex size-4 shrink-0 items-center justify-center\">{leftIcon}</div>\n </div>\n )}\n\n {(rightIcon || validated) && (\n <div\n className={cn(\n \"absolute inset-y-0 right-0 flex items-center gap-2 text-content-secondary\",\n ICON_INSET[size],\n )}\n >\n {rightIcon && (\n <div className=\"flex size-4 shrink-0 items-center justify-center\">{rightIcon}</div>\n )}\n {validated && (\n <div className=\"pointer-events-none flex size-4 shrink-0 items-center justify-center\">\n <CheckOutlineIcon className=\"text-success-content\" />\n </div>\n )}\n </div>\n )}\n </div>\n\n {bottomText && (\n <TextFieldHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextFieldHelperText>\n )}\n </div>\n );\n },\n);\n\nTextField.displayName = \"TextField\";\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TextField.mjs","sources":["../../../src/components/TextField/TextField.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { CheckOutlineIcon } from \"@/index\";\nimport { cn } from \"../../utils/cn\";\n\n/** Text field height in pixels. */\nexport type TextFieldSize = \"48\" | \"40\" | \"32\";\n\nexport interface TextFieldProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, \"size\" | \"prefix\"> {\n /** Label text displayed above the input. Also used as the accessible name. */\n label?: string;\n /** Helper text displayed below the input. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the text field in pixels. @default \"48\" */\n size?: TextFieldSize;\n /** Whether the text field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the input. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Whether the text field is validated. @default false */\n validated?: boolean;\n /** Icon element displayed at the left side of the input. */\n leftIcon?: React.ReactNode;\n /** Icon element displayed at the right side of the input. */\n rightIcon?: React.ReactNode;\n /** Whether the text field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n}\n\nconst CONTAINER_HEIGHT: Record<TextFieldSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst INPUT_SIZE_CLASSES: Record<TextFieldSize, string> = {\n \"48\": \"py-3 typography-regular-body-lg autofill-body-lg\",\n \"40\": \"py-2 typography-regular-body-lg autofill-body-lg\",\n \"32\": \"py-2 typography-regular-body-md autofill-body-md\",\n};\n\nconst INPUT_PL: Record<TextFieldSize, { default: string; withIcon: string }> = {\n \"48\": { default: \"pl-4\", withIcon: \"pl-10\" },\n \"40\": { default: \"pl-4\", withIcon: \"pl-10\" },\n \"32\": { default: \"pl-3\", withIcon: \"pl-9\" },\n};\n\nconst INPUT_PR: Record<TextFieldSize, { default: string; withIcon: string }> = {\n \"48\": { default: \"pr-4\", withIcon: \"pr-10\" },\n \"40\": { default: \"pr-4\", withIcon: \"pr-10\" },\n \"32\": { default: \"pr-3\", withIcon: \"pr-9\" },\n};\n\nconst ICON_INSET: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nfunction getContainerClassName(size: TextFieldSize, error: boolean, disabled?: boolean) {\n return cn(\n \"relative overflow-hidden rounded-sm border bg-neutral-alphas-50 has-focus-visible:shadow-focus-ring has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-content\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-alphas-400\",\n CONTAINER_HEIGHT[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getInputClassName(size: TextFieldSize, hasLeftIcon: boolean, hasRightIcon: boolean) {\n return cn(\n \"h-full w-full rounded-sm bg-transparent text-content-primary no-underline placeholder:text-content-secondary focus:outline-none disabled:cursor-not-allowed\",\n INPUT_SIZE_CLASSES[size],\n hasLeftIcon ? INPUT_PL[size].withIcon : INPUT_PL[size].default,\n hasRightIcon ? INPUT_PR[size].withIcon : INPUT_PR[size].default,\n );\n}\n\nfunction TextFieldHelperText({\n id,\n error,\n children,\n}: {\n id: string;\n error: boolean;\n children: React.ReactNode;\n}) {\n return (\n <p\n id={id}\n className={cn(\n \"typography-regular-body-sm px-2 pt-2 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {children}\n </p>\n );\n}\n\nfunction warnMissingAccessibleName(label?: string, ariaLabel?: string, ariaLabelledBy?: string) {\n if (process.env.NODE_ENV !== \"production\") {\n if (!label && !ariaLabel && !ariaLabelledBy) {\n console.warn(\n \"TextField: no accessible name provided. Pass a `label`, `aria-label`, or `aria-labelledby` prop.\",\n );\n }\n }\n}\n\n/**\n * A text input field with optional label, helper/error text, and icon slots.\n *\n * Provide at least one of `label`, `aria-label`, or `aria-labelledby` for\n * accessibility — a console warning is emitted in development if none are set.\n *\n * @example\n * ```tsx\n * <TextField\n * label=\"Email\"\n * placeholder=\"you@example.com\"\n * error={!!emailError}\n * errorMessage={emailError}\n * />\n * ```\n */\nexport const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n validated = false,\n leftIcon,\n rightIcon,\n className,\n id,\n disabled,\n fullWidth = false,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const inputId = id || generatedId;\n const helperTextId = `${inputId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n warnMissingAccessibleName(label, props[\"aria-label\"], props[\"aria-labelledby\"]);\n\n return (\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={inputId}\n className=\"typography-semibold-body-sm px-1 pt-1 pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n <input\n ref={ref}\n id={inputId}\n disabled={disabled}\n aria-describedby={bottomText ? helperTextId : undefined}\n aria-invalid={error || undefined}\n className={cn(\n getInputClassName(size, !!leftIcon, !!(rightIcon || validated)),\n \"[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n\n {leftIcon && (\n <div\n className={cn(\n \"pointer-events-none absolute inset-y-0 left-0 flex items-center text-content-secondary\",\n ICON_INSET[size],\n )}\n >\n <div className=\"flex size-4 shrink-0 items-center justify-center\">{leftIcon}</div>\n </div>\n )}\n\n {(rightIcon || validated) && (\n <div\n className={cn(\n \"absolute inset-y-0 right-0 flex items-center gap-2 text-content-secondary\",\n ICON_INSET[size],\n )}\n >\n {rightIcon && (\n <div className=\"flex size-4 shrink-0 items-center justify-center\">{rightIcon}</div>\n )}\n {validated && (\n <div className=\"pointer-events-none flex size-4 shrink-0 items-center justify-center\">\n <CheckOutlineIcon className=\"text-success-content\" />\n </div>\n )}\n </div>\n )}\n </div>\n\n {bottomText && (\n <TextFieldHelperText id={helperTextId} error={error}>\n {bottomText}\n </TextFieldHelperText>\n )}\n </div>\n );\n },\n);\n\nTextField.displayName = \"TextField\";\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanvue/ui",
3
- "version": "2.11.0",
3
+ "version": "2.12.1",
4
4
  "description": "React component library built with Tailwind CSS for Fanvue ecosystem",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org",