@fanvue/ui 1.18.1 → 1.18.2

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.
Files changed (24) hide show
  1. package/dist/cjs/components/Accordion/AccordionContent.cjs +11 -3
  2. package/dist/cjs/components/Accordion/AccordionContent.cjs.map +1 -1
  3. package/dist/cjs/components/Accordion/AccordionItem.cjs +1 -5
  4. package/dist/cjs/components/Accordion/AccordionItem.cjs.map +1 -1
  5. package/dist/cjs/components/Autocomplete/useAutocomplete.cjs.map +1 -1
  6. package/dist/cjs/components/BottomNavigation/BottomNavigation.cjs +5 -17
  7. package/dist/cjs/components/BottomNavigation/BottomNavigation.cjs.map +1 -1
  8. package/dist/cjs/components/BottomNavigation/BottomNavigationAction.cjs +6 -22
  9. package/dist/cjs/components/BottomNavigation/BottomNavigationAction.cjs.map +1 -1
  10. package/dist/cjs/components/TextField/TextField.cjs +4 -0
  11. package/dist/cjs/components/TextField/TextField.cjs.map +1 -1
  12. package/dist/components/Accordion/AccordionContent.mjs +11 -3
  13. package/dist/components/Accordion/AccordionContent.mjs.map +1 -1
  14. package/dist/components/Accordion/AccordionItem.mjs +1 -5
  15. package/dist/components/Accordion/AccordionItem.mjs.map +1 -1
  16. package/dist/components/Autocomplete/useAutocomplete.mjs.map +1 -1
  17. package/dist/components/BottomNavigation/BottomNavigation.mjs +5 -17
  18. package/dist/components/BottomNavigation/BottomNavigation.mjs.map +1 -1
  19. package/dist/components/BottomNavigation/BottomNavigationAction.mjs +6 -22
  20. package/dist/components/BottomNavigation/BottomNavigationAction.mjs.map +1 -1
  21. package/dist/components/TextField/TextField.mjs +4 -0
  22. package/dist/components/TextField/TextField.mjs.map +1 -1
  23. package/dist/index.d.ts +2 -6
  24. package/package.json +1 -1
@@ -29,14 +29,22 @@ const AccordionContent = React__namespace.forwardRef(({ className, children, noP
29
29
  ref,
30
30
  className: cn.cn(
31
31
  "overflow-hidden",
32
- "typography-regular-body-md text-foreground-secondary",
33
32
  "motion-safe:data-[state=closed]:animate-accordion-collapse",
34
33
  "motion-safe:data-[state=open]:animate-accordion-expand",
35
- !noPadding && "px-3 pb-3",
36
34
  className
37
35
  ),
38
36
  ...props,
39
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 overflow-wrap-anywhere", children })
37
+ children: /* @__PURE__ */ jsxRuntime.jsx(
38
+ "div",
39
+ {
40
+ className: cn.cn(
41
+ "overflow-wrap-anywhere min-w-0",
42
+ "typography-regular-body-md text-foreground-secondary",
43
+ !noPadding && "px-3 pt-2 pb-3"
44
+ ),
45
+ children
46
+ }
47
+ )
40
48
  }
41
49
  ));
42
50
  AccordionContent.displayName = "AccordionContent";
@@ -1 +1 @@
1
- {"version":3,"file":"AccordionContent.cjs","sources":["../../../../src/components/Accordion/AccordionContent.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link AccordionContent} panel component. */\nexport type AccordionContentProps = React.ComponentPropsWithoutRef<\n typeof AccordionPrimitive.Content\n> & {\n /** Remove the default inner padding (`px-3 pb-3`). Useful when you need custom content layout. */\n noPadding?: boolean;\n};\n\n/** Renders the collapsible content panel for an {@link AccordionItem}. Animates open and closed. */\nexport const AccordionContent = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Content>,\n AccordionContentProps\n>(({ className, children, noPadding, ...props }, ref) => (\n <AccordionPrimitive.Content\n ref={ref}\n className={cn(\n \"overflow-hidden\",\n \"typography-regular-body-md text-foreground-secondary\",\n \"motion-safe:data-[state=closed]:animate-accordion-collapse\",\n \"motion-safe:data-[state=open]:animate-accordion-expand\",\n !noPadding && \"px-3 pb-3\",\n className,\n )}\n {...props}\n >\n <div className=\"min-w-0 overflow-wrap-anywhere\">{children}</div>\n </AccordionPrimitive.Content>\n));\n\nAccordionContent.displayName = \"AccordionContent\";\n"],"names":["React","jsx","AccordionPrimitive","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAaO,MAAM,mBAAmBA,iBAAM,WAGpC,CAAC,EAAE,WAAW,UAAU,WAAW,GAAG,SAAS,QAC/CC,2BAAAA;AAAAA,EAACC,8BAAmB;AAAA,EAAnB;AAAA,IACC;AAAA,IACA,WAAWC,GAAAA;AAAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,CAAC,aAAa;AAAA,MACd;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,IAEJ,UAAAF,2BAAAA,IAAC,OAAA,EAAI,WAAU,kCAAkC,SAAA,CAAS;AAAA,EAAA;AAC5D,CACD;AAED,iBAAiB,cAAc;;"}
1
+ {"version":3,"file":"AccordionContent.cjs","sources":["../../../../src/components/Accordion/AccordionContent.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link AccordionContent} panel component. */\nexport type AccordionContentProps = React.ComponentPropsWithoutRef<\n typeof AccordionPrimitive.Content\n> & {\n /** Remove the default inner padding (`px-3 pb-3`). Useful when you need custom content layout. */\n noPadding?: boolean;\n};\n\n/** Renders the collapsible content panel for an {@link AccordionItem}. Animates open and closed. */\nexport const AccordionContent = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Content>,\n AccordionContentProps\n>(({ className, children, noPadding, ...props }, ref) => (\n <AccordionPrimitive.Content\n ref={ref}\n className={cn(\n \"overflow-hidden\",\n \"motion-safe:data-[state=closed]:animate-accordion-collapse\",\n \"motion-safe:data-[state=open]:animate-accordion-expand\",\n className,\n )}\n {...props}\n >\n <div\n className={cn(\n \"overflow-wrap-anywhere min-w-0\",\n \"typography-regular-body-md text-foreground-secondary\",\n !noPadding && \"px-3 pt-2 pb-3\",\n )}\n >\n {children}\n </div>\n </AccordionPrimitive.Content>\n));\n\nAccordionContent.displayName = \"AccordionContent\";\n"],"names":["React","jsx","AccordionPrimitive","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAaO,MAAM,mBAAmBA,iBAAM,WAGpC,CAAC,EAAE,WAAW,UAAU,WAAW,GAAG,SAAS,QAC/CC,2BAAAA;AAAAA,EAACC,8BAAmB;AAAA,EAAnB;AAAA,IACC;AAAA,IACA,WAAWC,GAAAA;AAAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,IAEJ,UAAAF,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAWE,GAAAA;AAAAA,UACT;AAAA,UACA;AAAA,UACA,CAAC,aAAa;AAAA,QAAA;AAAA,QAGf;AAAA,MAAA;AAAA,IAAA;AAAA,EACH;AACF,CACD;AAED,iBAAiB,cAAc;;"}
@@ -27,11 +27,7 @@ const AccordionItem = React__namespace.forwardRef(({ className, ...props }, ref)
27
27
  AccordionPrimitive__namespace.Item,
28
28
  {
29
29
  ref,
30
- className: cn.cn(
31
- "overflow-hidden rounded-xl bg-surface-container",
32
- "border border-neutral-200",
33
- className
34
- ),
30
+ className: cn.cn("rounded-xl bg-surface-container", "border border-neutral-200", className),
35
31
  ...props
36
32
  }
37
33
  ));
@@ -1 +1 @@
1
- {"version":3,"file":"AccordionItem.cjs","sources":["../../../../src/components/Accordion/AccordionItem.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link AccordionItem} component. */\nexport type AccordionItemProps = React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>;\n\n/** A single collapsible section within an {@link Accordion}. Contains an {@link AccordionTrigger} and {@link AccordionContent}. */\nexport const AccordionItem = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Item>,\n AccordionItemProps\n>(({ className, ...props }, ref) => (\n <AccordionPrimitive.Item\n ref={ref}\n className={cn(\n \"overflow-hidden rounded-xl bg-surface-container\",\n \"border border-neutral-200\",\n className,\n )}\n {...props}\n />\n));\n\nAccordionItem.displayName = \"AccordionItem\";\n"],"names":["React","jsx","AccordionPrimitive","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAQO,MAAM,gBAAgBA,iBAAM,WAGjC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1BC,2BAAAA;AAAAA,EAACC,8BAAmB;AAAA,EAAnB;AAAA,IACC;AAAA,IACA,WAAWC,GAAAA;AAAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AAED,cAAc,cAAc;;"}
1
+ {"version":3,"file":"AccordionItem.cjs","sources":["../../../../src/components/Accordion/AccordionItem.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link AccordionItem} component. */\nexport type AccordionItemProps = React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>;\n\n/** A single collapsible section within an {@link Accordion}. Contains an {@link AccordionTrigger} and {@link AccordionContent}. */\nexport const AccordionItem = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Item>,\n AccordionItemProps\n>(({ className, ...props }, ref) => (\n <AccordionPrimitive.Item\n ref={ref}\n className={cn(\"rounded-xl bg-surface-container\", \"border border-neutral-200\", className)}\n {...props}\n />\n));\n\nAccordionItem.displayName = \"AccordionItem\";\n"],"names":["React","jsx","AccordionPrimitive","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAQO,MAAM,gBAAgBA,iBAAM,WAGjC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1BC,2BAAAA;AAAAA,EAACC,8BAAmB;AAAA,EAAnB;AAAA,IACC;AAAA,IACA,WAAWC,GAAAA,GAAG,mCAAmC,6BAA6B,SAAS;AAAA,IACtF,GAAG;AAAA,EAAA;AACN,CACD;AAED,cAAc,cAAc;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAutocomplete.cjs","sources":["../../../../src/components/Autocomplete/useAutocomplete.ts"],"sourcesContent":["import * as React from \"react\";\nimport type { AutocompleteOption, AutocompleteProps } from \"./Autocomplete\";\nimport { CREATE_PREFIX, defaultFilter, getLabel } from \"./constants\";\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Combobox hook managing interconnected controlled/uncontrolled state\nexport function useAutocomplete(props: AutocompleteProps) {\n const {\n id,\n options,\n disabled = false,\n filterFn,\n creatable = false,\n creatableLabel,\n onCreate,\n inputValue: inputValueProp,\n onInputChange,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n } = props;\n\n const generatedId = React.useId();\n const inputId = id ?? generatedId;\n const helperTextId = `${inputId}-helper`;\n const listboxId = `${inputId}-listbox`;\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const listRef = React.useRef<HTMLDivElement>(null);\n\n const isMulti = props.multiple === true;\n\n // --- single-select state ---\n const [internalValue, setInternalValue] = React.useState<string | null>(\n (!isMulti && props.defaultValue) || null,\n );\n const selectedValue: string | null = !isMulti\n ? props.value !== undefined\n ? props.value\n : internalValue\n : null;\n\n // --- multi-select state ---\n const [internalMultiValue, setInternalMultiValue] = React.useState<string[]>(\n isMulti && props.defaultValue ? props.defaultValue : [],\n );\n const selectedMultiValues: string[] = isMulti\n ? props.value !== undefined\n ? props.value\n : internalMultiValue\n : [];\n\n // --- input text ---\n const [internalInputValue, setInternalInputValue] = React.useState(\"\");\n const searchText = inputValueProp !== undefined ? inputValueProp : internalInputValue;\n\n // --- open state ---\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n const isOpen = openProp !== undefined ? openProp : internalOpen;\n\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [openProp, onOpenChange],\n );\n\n // --- active index ---\n const [activeIndex, setActiveIndex] = React.useState(-1);\n\n // --- filtering ---\n const filter = filterFn ?? defaultFilter;\n\n const filteredOptions = React.useMemo(() => {\n if (!searchText) return options;\n return options.filter((o) => filter(o, searchText));\n }, [options, searchText, filter]);\n\n const showCreate =\n creatable &&\n searchText.length > 0 &&\n !options.some((o) => (o.label ?? o.value).toLowerCase() === searchText.toLowerCase());\n\n const visibleOptions = React.useMemo(() => {\n if (!showCreate) return filteredOptions;\n const createOption: AutocompleteOption = {\n value: `${CREATE_PREFIX}${searchText}`,\n label: creatableLabel ? creatableLabel(searchText) : searchText,\n };\n return [...filteredOptions, createOption];\n }, [filteredOptions, showCreate, searchText, creatableLabel]);\n\n const prevOptionsLengthRef = React.useRef(visibleOptions.length);\n const prevSearchTextRef = React.useRef(searchText);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentionally reset only when search text or option count actually changes\n React.useEffect(() => {\n if (\n searchText !== prevSearchTextRef.current ||\n visibleOptions.length !== prevOptionsLengthRef.current\n ) {\n setActiveIndex(-1);\n prevSearchTextRef.current = searchText;\n prevOptionsLengthRef.current = visibleOptions.length;\n }\n }, [searchText, visibleOptions.length]);\n\n // --- scroll active option into view ---\n React.useEffect(() => {\n if (activeIndex < 0 || !listRef.current) return;\n const el = listRef.current.querySelector(`[data-option-index=\"${activeIndex}\"]`);\n if (typeof el?.scrollIntoView === \"function\") {\n el.scrollIntoView({ block: \"nearest\" });\n }\n }, [activeIndex]);\n\n // --- helpers ---\n function clearInputText() {\n if (inputValueProp === undefined) setInternalInputValue(\"\");\n onInputChange?.(\"\");\n }\n\n function selectSingle(val: string | null) {\n if (!isMulti && props.value === undefined) setInternalValue(val);\n if (!isMulti) (props as { onChange?: (v: string | null) => void }).onChange?.(val);\n }\n\n function toggleMulti(val: string) {\n const next = selectedMultiValues.includes(val)\n ? selectedMultiValues.filter((v) => v !== val)\n : [...selectedMultiValues, val];\n if (isMulti && props.value === undefined) setInternalMultiValue(next);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.(next);\n }\n\n function handleSelect(option: AutocompleteOption) {\n if (option.value.startsWith(CREATE_PREFIX)) {\n const raw = option.value.slice(CREATE_PREFIX.length);\n onCreate?.(raw);\n if (isMulti) {\n toggleMulti(raw);\n } else {\n selectSingle(raw);\n setOpen(false);\n }\n clearInputText();\n return;\n }\n\n if (isMulti) {\n toggleMulti(option.value);\n clearInputText();\n } else {\n selectSingle(option.value);\n clearInputText();\n setOpen(false);\n }\n }\n\n function handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {\n const val = e.target.value;\n if (inputValueProp === undefined) setInternalInputValue(val);\n onInputChange?.(val);\n if (!isOpen) setOpen(true);\n }\n\n function findNextEnabled(start: number, direction: 1 | -1): number | null {\n let idx = start;\n while (idx >= 0 && idx < visibleOptions.length && visibleOptions[idx]?.disabled) {\n idx += direction;\n }\n return idx >= 0 && idx < visibleOptions.length ? idx : null;\n }\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Flat switch is clearer than splitting into separate handler functions\n function handleKeyDown(e: React.KeyboardEvent) {\n if (disabled) return;\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev + 1, 1) ?? prev);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev - 1, -1) ?? prev);\n break;\n }\n case \"Enter\": {\n if (isOpen && activeIndex >= 0 && activeIndex < visibleOptions.length) {\n e.preventDefault();\n const opt = visibleOptions[activeIndex];\n if (opt && !opt.disabled) handleSelect(opt);\n }\n break;\n }\n case \"Escape\": {\n e.preventDefault();\n if (isOpen) setOpen(false);\n break;\n }\n case \"Backspace\": {\n if (isMulti && searchText === \"\" && selectedMultiValues.length > 0) {\n const lastVal = selectedMultiValues[selectedMultiValues.length - 1];\n if (lastVal !== undefined) toggleMulti(lastVal);\n }\n break;\n }\n case \"Home\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(0, 1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n case \"End\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(visibleOptions.length - 1, -1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n }\n }\n\n function handleClear(e: React.MouseEvent) {\n e.stopPropagation();\n if (isMulti) {\n if (props.value === undefined) setInternalMultiValue([]);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.([]);\n } else {\n selectSingle(null);\n }\n clearInputText();\n inputRef.current?.focus();\n }\n\n function handleBlur(e: React.FocusEvent<HTMLInputElement>) {\n const container = e.currentTarget.closest(\"[data-autocomplete-root]\");\n if (container && e.relatedTarget instanceof Node && container.contains(e.relatedTarget)) {\n return;\n }\n if (\n e.relatedTarget instanceof Node &&\n (e.relatedTarget as Element).closest?.(\"[data-radix-popper-content-wrapper]\")\n ) {\n return;\n }\n if (isOpen) setOpen(false);\n }\n\n function handleFocus() {\n if (!disabled && !isOpen) setOpen(true);\n }\n\n function handleContainerClick() {\n if (!disabled) {\n inputRef.current?.focus();\n if (!isOpen) setOpen(true);\n }\n }\n\n function handleOpenChange(next: boolean) {\n setOpen(next);\n if (!next) {\n clearInputText();\n setActiveIndex(-1);\n }\n }\n\n const selectedOption = React.useMemo(\n () => options.find((o) => o.value === selectedValue),\n [options, selectedValue],\n );\n\n const selectedMultiOptions = React.useMemo(\n () =>\n selectedMultiValues\n .map((v) => options.find((o) => o.value === v))\n .filter(Boolean) as AutocompleteOption[],\n [options, selectedMultiValues],\n );\n\n const hasClearableValue = isMulti ? selectedMultiValues.length > 0 : selectedValue != null;\n\n const displayInputValue = React.useMemo(() => {\n if (searchText) return searchText;\n if (isMulti || isOpen) return \"\";\n return selectedOption ? getLabel(selectedOption) : \"\";\n }, [searchText, isMulti, isOpen, selectedOption]);\n\n const activeDescendantId = activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : undefined;\n\n return {\n inputId,\n helperTextId,\n listboxId,\n inputRef,\n listRef,\n isMulti,\n isOpen,\n selectedValue,\n selectedMultiValues,\n selectedMultiOptions,\n searchText,\n visibleOptions,\n activeIndex,\n activeDescendantId,\n hasClearableValue,\n displayInputValue,\n setActiveIndex,\n handleSelect,\n handleInputChange,\n handleKeyDown,\n handleClear,\n handleBlur,\n handleFocus,\n handleContainerClick,\n handleOpenChange,\n toggleMulti,\n };\n}\n"],"names":["React","defaultFilter","CREATE_PREFIX","getLabel"],"mappings":"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"}
1
+ {"version":3,"file":"useAutocomplete.cjs","sources":["../../../../src/components/Autocomplete/useAutocomplete.ts"],"sourcesContent":["import * as React from \"react\";\nimport type { AutocompleteOption, AutocompleteProps } from \"./Autocomplete\";\nimport { CREATE_PREFIX, defaultFilter, getLabel } from \"./constants\";\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Combobox hook managing interconnected controlled/uncontrolled state\nexport function useAutocomplete(props: AutocompleteProps) {\n const {\n id,\n options,\n disabled = false,\n filterFn,\n creatable = false,\n creatableLabel,\n onCreate,\n inputValue: inputValueProp,\n onInputChange,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n } = props;\n\n const generatedId = React.useId();\n const inputId = id ?? generatedId;\n const helperTextId = `${inputId}-helper`;\n const listboxId = `${inputId}-listbox`;\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const listRef = React.useRef<HTMLDivElement>(null);\n\n const isMulti = props.multiple === true;\n\n // --- single-select state ---\n const [internalValue, setInternalValue] = React.useState<string | null>(\n (!isMulti && props.defaultValue) || null,\n );\n const selectedValue: string | null = !isMulti\n ? props.value !== undefined\n ? props.value\n : internalValue\n : null;\n\n // --- multi-select state ---\n const [internalMultiValue, setInternalMultiValue] = React.useState<string[]>(\n isMulti && props.defaultValue ? props.defaultValue : [],\n );\n const selectedMultiValues: string[] = isMulti\n ? props.value !== undefined\n ? props.value\n : internalMultiValue\n : [];\n\n // --- input text ---\n const [internalInputValue, setInternalInputValue] = React.useState(\"\");\n const searchText = inputValueProp !== undefined ? inputValueProp : internalInputValue;\n\n // --- open state ---\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n const isOpen = openProp !== undefined ? openProp : internalOpen;\n\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [openProp, onOpenChange],\n );\n\n // --- active index ---\n const [activeIndex, setActiveIndex] = React.useState(-1);\n\n // --- filtering ---\n const filter = filterFn ?? defaultFilter;\n\n const filteredOptions = React.useMemo(() => {\n if (!searchText) return options;\n return options.filter((o) => filter(o, searchText));\n }, [options, searchText, filter]);\n\n const showCreate =\n creatable &&\n searchText.length > 0 &&\n !options.some((o) => (o.label ?? o.value).toLowerCase() === searchText.toLowerCase());\n\n const visibleOptions = React.useMemo(() => {\n if (!showCreate) return filteredOptions;\n const createOption: AutocompleteOption = {\n value: `${CREATE_PREFIX}${searchText}`,\n label: creatableLabel ? creatableLabel(searchText) : searchText,\n };\n return [...filteredOptions, createOption];\n }, [filteredOptions, showCreate, searchText, creatableLabel]);\n\n const prevOptionsLengthRef = React.useRef(visibleOptions.length);\n const prevSearchTextRef = React.useRef(searchText);\n\n React.useEffect(() => {\n if (\n searchText !== prevSearchTextRef.current ||\n visibleOptions.length !== prevOptionsLengthRef.current\n ) {\n setActiveIndex(-1);\n prevSearchTextRef.current = searchText;\n prevOptionsLengthRef.current = visibleOptions.length;\n }\n }, [searchText, visibleOptions.length]);\n\n // --- scroll active option into view ---\n React.useEffect(() => {\n if (activeIndex < 0 || !listRef.current) return;\n const el = listRef.current.querySelector(`[data-option-index=\"${activeIndex}\"]`);\n if (typeof el?.scrollIntoView === \"function\") {\n el.scrollIntoView({ block: \"nearest\" });\n }\n }, [activeIndex]);\n\n // --- helpers ---\n function clearInputText() {\n if (inputValueProp === undefined) setInternalInputValue(\"\");\n onInputChange?.(\"\");\n }\n\n function selectSingle(val: string | null) {\n if (!isMulti && props.value === undefined) setInternalValue(val);\n if (!isMulti) (props as { onChange?: (v: string | null) => void }).onChange?.(val);\n }\n\n function toggleMulti(val: string) {\n const next = selectedMultiValues.includes(val)\n ? selectedMultiValues.filter((v) => v !== val)\n : [...selectedMultiValues, val];\n if (isMulti && props.value === undefined) setInternalMultiValue(next);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.(next);\n }\n\n function handleSelect(option: AutocompleteOption) {\n if (option.value.startsWith(CREATE_PREFIX)) {\n const raw = option.value.slice(CREATE_PREFIX.length);\n onCreate?.(raw);\n if (isMulti) {\n toggleMulti(raw);\n } else {\n selectSingle(raw);\n setOpen(false);\n }\n clearInputText();\n return;\n }\n\n if (isMulti) {\n toggleMulti(option.value);\n clearInputText();\n } else {\n selectSingle(option.value);\n clearInputText();\n setOpen(false);\n }\n }\n\n function handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {\n const val = e.target.value;\n if (inputValueProp === undefined) setInternalInputValue(val);\n onInputChange?.(val);\n if (!isOpen) setOpen(true);\n }\n\n function findNextEnabled(start: number, direction: 1 | -1): number | null {\n let idx = start;\n while (idx >= 0 && idx < visibleOptions.length && visibleOptions[idx]?.disabled) {\n idx += direction;\n }\n return idx >= 0 && idx < visibleOptions.length ? idx : null;\n }\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Flat switch is clearer than splitting into separate handler functions\n function handleKeyDown(e: React.KeyboardEvent) {\n if (disabled) return;\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev + 1, 1) ?? prev);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev - 1, -1) ?? prev);\n break;\n }\n case \"Enter\": {\n if (isOpen && activeIndex >= 0 && activeIndex < visibleOptions.length) {\n e.preventDefault();\n const opt = visibleOptions[activeIndex];\n if (opt && !opt.disabled) handleSelect(opt);\n }\n break;\n }\n case \"Escape\": {\n e.preventDefault();\n if (isOpen) setOpen(false);\n break;\n }\n case \"Backspace\": {\n if (isMulti && searchText === \"\" && selectedMultiValues.length > 0) {\n const lastVal = selectedMultiValues[selectedMultiValues.length - 1];\n if (lastVal !== undefined) toggleMulti(lastVal);\n }\n break;\n }\n case \"Home\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(0, 1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n case \"End\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(visibleOptions.length - 1, -1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n }\n }\n\n function handleClear(e: React.MouseEvent) {\n e.stopPropagation();\n if (isMulti) {\n if (props.value === undefined) setInternalMultiValue([]);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.([]);\n } else {\n selectSingle(null);\n }\n clearInputText();\n inputRef.current?.focus();\n }\n\n function handleBlur(e: React.FocusEvent<HTMLInputElement>) {\n const container = e.currentTarget.closest(\"[data-autocomplete-root]\");\n if (container && e.relatedTarget instanceof Node && container.contains(e.relatedTarget)) {\n return;\n }\n if (\n e.relatedTarget instanceof Node &&\n (e.relatedTarget as Element).closest?.(\"[data-radix-popper-content-wrapper]\")\n ) {\n return;\n }\n if (isOpen) setOpen(false);\n }\n\n function handleFocus() {\n if (!disabled && !isOpen) setOpen(true);\n }\n\n function handleContainerClick() {\n if (!disabled) {\n inputRef.current?.focus();\n if (!isOpen) setOpen(true);\n }\n }\n\n function handleOpenChange(next: boolean) {\n setOpen(next);\n if (!next) {\n clearInputText();\n setActiveIndex(-1);\n }\n }\n\n const selectedOption = React.useMemo(\n () => options.find((o) => o.value === selectedValue),\n [options, selectedValue],\n );\n\n const selectedMultiOptions = React.useMemo(\n () =>\n selectedMultiValues\n .map((v) => options.find((o) => o.value === v))\n .filter(Boolean) as AutocompleteOption[],\n [options, selectedMultiValues],\n );\n\n const hasClearableValue = isMulti ? selectedMultiValues.length > 0 : selectedValue != null;\n\n const displayInputValue = React.useMemo(() => {\n if (searchText) return searchText;\n if (isMulti || isOpen) return \"\";\n return selectedOption ? getLabel(selectedOption) : \"\";\n }, [searchText, isMulti, isOpen, selectedOption]);\n\n const activeDescendantId = activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : undefined;\n\n return {\n inputId,\n helperTextId,\n listboxId,\n inputRef,\n listRef,\n isMulti,\n isOpen,\n selectedValue,\n selectedMultiValues,\n selectedMultiOptions,\n searchText,\n visibleOptions,\n activeIndex,\n activeDescendantId,\n hasClearableValue,\n displayInputValue,\n setActiveIndex,\n handleSelect,\n handleInputChange,\n handleKeyDown,\n handleClear,\n handleBlur,\n handleFocus,\n handleContainerClick,\n handleOpenChange,\n toggleMulti,\n };\n}\n"],"names":["React","defaultFilter","CREATE_PREFIX","getLabel"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAKO,SAAS,gBAAgB,OAA0B;AACxD,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ;AAAA,IACA,MAAM;AAAA,IACN,cAAc;AAAA,IACd;AAAA,EAAA,IACE;AAEJ,QAAM,cAAcA,iBAAM,MAAA;AAC1B,QAAM,UAAU,MAAM;AACtB,QAAM,eAAe,GAAG,OAAO;AAC/B,QAAM,YAAY,GAAG,OAAO;AAE5B,QAAM,WAAWA,iBAAM,OAAyB,IAAI;AACpD,QAAM,UAAUA,iBAAM,OAAuB,IAAI;AAEjD,QAAM,UAAU,MAAM,aAAa;AAGnC,QAAM,CAAC,eAAe,gBAAgB,IAAIA,iBAAM;AAAA,IAC7C,CAAC,WAAW,MAAM,gBAAiB;AAAA,EAAA;AAEtC,QAAM,gBAA+B,CAAC,UAClC,MAAM,UAAU,SACd,MAAM,QACN,gBACF;AAGJ,QAAM,CAAC,oBAAoB,qBAAqB,IAAIA,iBAAM;AAAA,IACxD,WAAW,MAAM,eAAe,MAAM,eAAe,CAAA;AAAA,EAAC;AAExD,QAAM,sBAAgC,UAClC,MAAM,UAAU,SACd,MAAM,QACN,qBACF,CAAA;AAGJ,QAAM,CAAC,oBAAoB,qBAAqB,IAAIA,iBAAM,SAAS,EAAE;AACrE,QAAM,aAAa,mBAAmB,SAAY,iBAAiB;AAGnE,QAAM,CAAC,cAAc,eAAe,IAAIA,iBAAM,SAAS,WAAW;AAClE,QAAM,SAAS,aAAa,SAAY,WAAW;AAEnD,QAAM,UAAUA,iBAAM;AAAA,IACpB,CAAC,SAAkB;AACjB,UAAI,aAAa,OAAW,iBAAgB,IAAI;AAChD,qBAAe,IAAI;AAAA,IACrB;AAAA,IACA,CAAC,UAAU,YAAY;AAAA,EAAA;AAIzB,QAAM,CAAC,aAAa,cAAc,IAAIA,iBAAM,SAAS,EAAE;AAGvD,QAAM,SAAS,YAAYC,UAAAA;AAE3B,QAAM,kBAAkBD,iBAAM,QAAQ,MAAM;AAC1C,QAAI,CAAC,WAAY,QAAO;AACxB,WAAO,QAAQ,OAAO,CAAC,MAAM,OAAO,GAAG,UAAU,CAAC;AAAA,EACpD,GAAG,CAAC,SAAS,YAAY,MAAM,CAAC;AAEhC,QAAM,aACJ,aACA,WAAW,SAAS,KACpB,CAAC,QAAQ,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,kBAAkB,WAAW,aAAa;AAEtF,QAAM,iBAAiBA,iBAAM,QAAQ,MAAM;AACzC,QAAI,CAAC,WAAY,QAAO;AACxB,UAAM,eAAmC;AAAA,MACvC,OAAO,GAAGE,uBAAa,GAAG,UAAU;AAAA,MACpC,OAAO,iBAAiB,eAAe,UAAU,IAAI;AAAA,IAAA;AAEvD,WAAO,CAAC,GAAG,iBAAiB,YAAY;AAAA,EAC1C,GAAG,CAAC,iBAAiB,YAAY,YAAY,cAAc,CAAC;AAE5D,QAAM,uBAAuBF,iBAAM,OAAO,eAAe,MAAM;AAC/D,QAAM,oBAAoBA,iBAAM,OAAO,UAAU;AAEjDA,mBAAM,UAAU,MAAM;AACpB,QACE,eAAe,kBAAkB,WACjC,eAAe,WAAW,qBAAqB,SAC/C;AACA,qBAAe,EAAE;AACjB,wBAAkB,UAAU;AAC5B,2BAAqB,UAAU,eAAe;AAAA,IAChD;AAAA,EACF,GAAG,CAAC,YAAY,eAAe,MAAM,CAAC;AAGtCA,mBAAM,UAAU,MAAM;AACpB,QAAI,cAAc,KAAK,CAAC,QAAQ,QAAS;AACzC,UAAM,KAAK,QAAQ,QAAQ,cAAc,uBAAuB,WAAW,IAAI;AAC/E,QAAI,OAAO,IAAI,mBAAmB,YAAY;AAC5C,SAAG,eAAe,EAAE,OAAO,UAAA,CAAW;AAAA,IACxC;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAGhB,WAAS,iBAAiB;AACxB,QAAI,mBAAmB,OAAW,uBAAsB,EAAE;AAC1D,oBAAgB,EAAE;AAAA,EACpB;AAEA,WAAS,aAAa,KAAoB;AACxC,QAAI,CAAC,WAAW,MAAM,UAAU,yBAA4B,GAAG;AAC/D,QAAI,CAAC,QAAU,OAAoD,WAAW,GAAG;AAAA,EACnF;AAEA,WAAS,YAAY,KAAa;AAChC,UAAM,OAAO,oBAAoB,SAAS,GAAG,IACzC,oBAAoB,OAAO,CAAC,MAAM,MAAM,GAAG,IAC3C,CAAC,GAAG,qBAAqB,GAAG;AAChC,QAAI,WAAW,MAAM,UAAU,8BAAiC,IAAI;AACpE,QAAI,QAAU,OAA+C,WAAW,IAAI;AAAA,EAC9E;AAEA,WAAS,aAAa,QAA4B;AAChD,QAAI,OAAO,MAAM,WAAWE,UAAAA,aAAa,GAAG;AAC1C,YAAM,MAAM,OAAO,MAAM,MAAMA,UAAAA,cAAc,MAAM;AACnD,iBAAW,GAAG;AACd,UAAI,SAAS;AACX,oBAAY,GAAG;AAAA,MACjB,OAAO;AACL,qBAAa,GAAG;AAChB,gBAAQ,KAAK;AAAA,MACf;AACA,qBAAA;AACA;AAAA,IACF;AAEA,QAAI,SAAS;AACX,kBAAY,OAAO,KAAK;AACxB,qBAAA;AAAA,IACF,OAAO;AACL,mBAAa,OAAO,KAAK;AACzB,qBAAA;AACA,cAAQ,KAAK;AAAA,IACf;AAAA,EACF;AAEA,WAAS,kBAAkB,GAAwC;AACjE,UAAM,MAAM,EAAE,OAAO;AACrB,QAAI,mBAAmB,OAAW,uBAAsB,GAAG;AAC3D,oBAAgB,GAAG;AACnB,QAAI,CAAC,OAAQ,SAAQ,IAAI;AAAA,EAC3B;AAEA,WAAS,gBAAgB,OAAe,WAAkC;AACxE,QAAI,MAAM;AACV,WAAO,OAAO,KAAK,MAAM,eAAe,UAAU,eAAe,GAAG,GAAG,UAAU;AAC/E,aAAO;AAAA,IACT;AACA,WAAO,OAAO,KAAK,MAAM,eAAe,SAAS,MAAM;AAAA,EACzD;AAGA,WAAS,cAAc,GAAwB;AAC7C,QAAI,SAAU;AAEd,YAAQ,EAAE,KAAA;AAAA,MACR,KAAK,aAAa;AAChB,UAAE,eAAA;AACF,YAAI,CAAC,QAAQ;AACX,kBAAQ,IAAI;AACZ;AAAA,QACF;AACA,uBAAe,CAAC,SAAS,gBAAgB,OAAO,GAAG,CAAC,KAAK,IAAI;AAC7D;AAAA,MACF;AAAA,MACA,KAAK,WAAW;AACd,UAAE,eAAA;AACF,YAAI,CAAC,QAAQ;AACX,kBAAQ,IAAI;AACZ;AAAA,QACF;AACA,uBAAe,CAAC,SAAS,gBAAgB,OAAO,GAAG,EAAE,KAAK,IAAI;AAC9D;AAAA,MACF;AAAA,MACA,KAAK,SAAS;AACZ,YAAI,UAAU,eAAe,KAAK,cAAc,eAAe,QAAQ;AACrE,YAAE,eAAA;AACF,gBAAM,MAAM,eAAe,WAAW;AACtC,cAAI,OAAO,CAAC,IAAI,uBAAuB,GAAG;AAAA,QAC5C;AACA;AAAA,MACF;AAAA,MACA,KAAK,UAAU;AACb,UAAE,eAAA;AACF,YAAI,gBAAgB,KAAK;AACzB;AAAA,MACF;AAAA,MACA,KAAK,aAAa;AAChB,YAAI,WAAW,eAAe,MAAM,oBAAoB,SAAS,GAAG;AAClE,gBAAM,UAAU,oBAAoB,oBAAoB,SAAS,CAAC;AAClE,cAAI,YAAY,OAAW,aAAY,OAAO;AAAA,QAChD;AACA;AAAA,MACF;AAAA,MACA,KAAK,QAAQ;AACX,YAAI,QAAQ;AACV,YAAE,eAAA;AACF,gBAAM,MAAM,gBAAgB,GAAG,CAAC;AAChC,cAAI,QAAQ,KAAM,gBAAe,GAAG;AAAA,QACtC;AACA;AAAA,MACF;AAAA,MACA,KAAK,OAAO;AACV,YAAI,QAAQ;AACV,YAAE,eAAA;AACF,gBAAM,MAAM,gBAAgB,eAAe,SAAS,GAAG,EAAE;AACzD,cAAI,QAAQ,KAAM,gBAAe,GAAG;AAAA,QACtC;AACA;AAAA,MACF;AAAA,IAAA;AAAA,EAEJ;AAEA,WAAS,YAAY,GAAqB;AACxC,MAAE,gBAAA;AACF,QAAI,SAAS;AACX,UAAI,MAAM,UAAU,OAAW,uBAAsB,CAAA,CAAE;AACvD,UAAI,QAAU,OAA+C,WAAW,EAAE;AAAA,IAC5E,OAAO;AACL,mBAAa,IAAI;AAAA,IACnB;AACA,mBAAA;AACA,aAAS,SAAS,MAAA;AAAA,EACpB;AAEA,WAAS,WAAW,GAAuC;AACzD,UAAM,YAAY,EAAE,cAAc,QAAQ,0BAA0B;AACpE,QAAI,aAAa,EAAE,yBAAyB,QAAQ,UAAU,SAAS,EAAE,aAAa,GAAG;AACvF;AAAA,IACF;AACA,QACE,EAAE,yBAAyB,QAC1B,EAAE,cAA0B,UAAU,qCAAqC,GAC5E;AACA;AAAA,IACF;AACA,QAAI,gBAAgB,KAAK;AAAA,EAC3B;AAEA,WAAS,cAAc;AACrB,QAAI,CAAC,YAAY,CAAC,gBAAgB,IAAI;AAAA,EACxC;AAEA,WAAS,uBAAuB;AAC9B,QAAI,CAAC,UAAU;AACb,eAAS,SAAS,MAAA;AAClB,UAAI,CAAC,OAAQ,SAAQ,IAAI;AAAA,IAC3B;AAAA,EACF;AAEA,WAAS,iBAAiB,MAAe;AACvC,YAAQ,IAAI;AACZ,QAAI,CAAC,MAAM;AACT,qBAAA;AACA,qBAAe,EAAE;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,iBAAiBF,iBAAM;AAAA,IAC3B,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,UAAU,aAAa;AAAA,IACnD,CAAC,SAAS,aAAa;AAAA,EAAA;AAGzB,QAAM,uBAAuBA,iBAAM;AAAA,IACjC,MACE,oBACG,IAAI,CAAC,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,EAC7C,OAAO,OAAO;AAAA,IACnB,CAAC,SAAS,mBAAmB;AAAA,EAAA;AAG/B,QAAM,oBAAoB,UAAU,oBAAoB,SAAS,IAAI,iBAAiB;AAEtF,QAAM,oBAAoBA,iBAAM,QAAQ,MAAM;AAC5C,QAAI,WAAY,QAAO;AACvB,QAAI,WAAW,OAAQ,QAAO;AAC9B,WAAO,iBAAiBG,UAAAA,SAAS,cAAc,IAAI;AAAA,EACrD,GAAG,CAAC,YAAY,SAAS,QAAQ,cAAc,CAAC;AAEhD,QAAM,qBAAqB,eAAe,IAAI,GAAG,SAAS,WAAW,WAAW,KAAK;AAErF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;;"}
@@ -21,27 +21,15 @@ function _interopNamespaceDefault(e) {
21
21
  return Object.freeze(n);
22
22
  }
23
23
  const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
24
- const BottomNavigationContext = React__namespace.createContext({
25
- showLabelsOnlyWhenActive: false,
26
- hideLabels: false
27
- });
24
+ const BottomNavigationContext = React__namespace.createContext({});
28
25
  function useBottomNavigationContext() {
29
26
  return React__namespace.useContext(BottomNavigationContext);
30
27
  }
31
28
  const BottomNavigation = React__namespace.forwardRef(
32
- ({
33
- className,
34
- children,
35
- value,
36
- onValueChange,
37
- showLabelsOnlyWhenActive = false,
38
- hideLabels = false,
39
- hideOnDesktop = false,
40
- ...props
41
- }, ref) => {
29
+ ({ className, children, value, onValueChange, hideOnDesktop = false, ...props }, ref) => {
42
30
  const contextValue = React__namespace.useMemo(
43
- () => ({ value, onValueChange, showLabelsOnlyWhenActive, hideLabels }),
44
- [value, onValueChange, showLabelsOnlyWhenActive, hideLabels]
31
+ () => ({ value, onValueChange }),
32
+ [value, onValueChange]
45
33
  );
46
34
  return /* @__PURE__ */ jsxRuntime.jsx(BottomNavigationContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(
47
35
  "nav",
@@ -50,7 +38,7 @@ const BottomNavigation = React__namespace.forwardRef(
50
38
  className: cn.cn(
51
39
  "fixed inset-x-0 bottom-0",
52
40
  "flex h-[calc(env(safe-area-inset-bottom,0px)+68px)] items-center justify-around",
53
- "border-neutral-200 border-t bg-surface-page/[.82] backdrop-blur-[16px]",
41
+ "border-neutral-200 border-t bg-surface-page",
54
42
  "pb-[env(safe-area-inset-bottom,0px)]",
55
43
  hideOnDesktop && "md:hidden",
56
44
  className
@@ -1 +1 @@
1
- {"version":3,"file":"BottomNavigation.cjs","sources":["../../../../src/components/BottomNavigation/BottomNavigation.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport interface BottomNavigationProps extends React.HTMLAttributes<HTMLElement> {\n /** The currently selected action value. */\n value?: string;\n /** Called when the selected action changes. */\n onValueChange?: (value: string) => void;\n /** When `true`, labels are only shown on the active action. @default false */\n showLabelsOnlyWhenActive?: boolean;\n /** When `true`, all labels are hidden. @default false */\n hideLabels?: boolean;\n /** When `true`, the navigation bar is hidden on viewports wider than `md` (768 px). @default false */\n hideOnDesktop?: boolean;\n}\n\ninterface BottomNavigationContextValue {\n value?: string;\n onValueChange?: (value: string) => void;\n showLabelsOnlyWhenActive: boolean;\n hideLabels: boolean;\n}\n\nconst BottomNavigationContext = React.createContext<BottomNavigationContextValue>({\n showLabelsOnlyWhenActive: false,\n hideLabels: false,\n});\n\nexport function useBottomNavigationContext(): BottomNavigationContextValue {\n return React.useContext(BottomNavigationContext);\n}\n\nexport const BottomNavigation = React.forwardRef<HTMLElement, BottomNavigationProps>(\n (\n {\n className,\n children,\n value,\n onValueChange,\n showLabelsOnlyWhenActive = false,\n hideLabels = false,\n hideOnDesktop = false,\n ...props\n },\n ref,\n ) => {\n const contextValue = React.useMemo<BottomNavigationContextValue>(\n () => ({ value, onValueChange, showLabelsOnlyWhenActive, hideLabels }),\n [value, onValueChange, showLabelsOnlyWhenActive, hideLabels],\n );\n\n return (\n <BottomNavigationContext.Provider value={contextValue}>\n <nav\n ref={ref}\n className={cn(\n \"fixed inset-x-0 bottom-0\",\n \"flex h-[calc(env(safe-area-inset-bottom,0px)+68px)] items-center justify-around\",\n \"border-neutral-200 border-t bg-surface-page/[.82] backdrop-blur-[16px]\",\n \"pb-[env(safe-area-inset-bottom,0px)]\",\n hideOnDesktop && \"md:hidden\",\n className,\n )}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...props.style }}\n {...props}\n >\n {children}\n </nav>\n </BottomNavigationContext.Provider>\n );\n },\n);\n\nBottomNavigation.displayName = \"BottomNavigation\";\n"],"names":["React","jsx","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAuBA,MAAM,0BAA0BA,iBAAM,cAA4C;AAAA,EAChF,0BAA0B;AAAA,EAC1B,YAAY;AACd,CAAC;AAEM,SAAS,6BAA2D;AACzE,SAAOA,iBAAM,WAAW,uBAAuB;AACjD;AAEO,MAAM,mBAAmBA,iBAAM;AAAA,EACpC,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,2BAA2B;AAAA,IAC3B,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,eAAeA,iBAAM;AAAA,MACzB,OAAO,EAAE,OAAO,eAAe,0BAA0B,WAAA;AAAA,MACzD,CAAC,OAAO,eAAe,0BAA0B,UAAU;AAAA,IAAA;AAG7D,WACEC,2BAAAA,IAAC,wBAAwB,UAAxB,EAAiC,OAAO,cACvC,UAAAA,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,iBAAiB;AAAA,UACjB;AAAA,QAAA;AAAA,QAEF,OAAO,EAAE,QAAQ,uCAAuC,GAAG,MAAM,MAAA;AAAA,QAChE,GAAG;AAAA,QAEH;AAAA,MAAA;AAAA,IAAA,GAEL;AAAA,EAEJ;AACF;AAEA,iBAAiB,cAAc;;;"}
1
+ {"version":3,"file":"BottomNavigation.cjs","sources":["../../../../src/components/BottomNavigation/BottomNavigation.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport interface BottomNavigationProps extends React.HTMLAttributes<HTMLElement> {\n /** The currently selected action value. */\n value?: string;\n /** Called when the selected action changes. */\n onValueChange?: (value: string) => void;\n /** When `true`, the navigation bar is hidden on viewports wider than `md` (768 px). @default false */\n hideOnDesktop?: boolean;\n}\n\ninterface BottomNavigationContextValue {\n value?: string;\n onValueChange?: (value: string) => void;\n}\n\nconst BottomNavigationContext = React.createContext<BottomNavigationContextValue>({});\n\nexport function useBottomNavigationContext(): BottomNavigationContextValue {\n return React.useContext(BottomNavigationContext);\n}\n\nexport const BottomNavigation = React.forwardRef<HTMLElement, BottomNavigationProps>(\n ({ className, children, value, onValueChange, hideOnDesktop = false, ...props }, ref) => {\n const contextValue = React.useMemo<BottomNavigationContextValue>(\n () => ({ value, onValueChange }),\n [value, onValueChange],\n );\n\n return (\n <BottomNavigationContext.Provider value={contextValue}>\n <nav\n ref={ref}\n className={cn(\n \"fixed inset-x-0 bottom-0\",\n \"flex h-[calc(env(safe-area-inset-bottom,0px)+68px)] items-center justify-around\",\n \"border-neutral-200 border-t bg-surface-page\",\n \"pb-[env(safe-area-inset-bottom,0px)]\",\n hideOnDesktop && \"md:hidden\",\n className,\n )}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...props.style }}\n {...props}\n >\n {children}\n </nav>\n </BottomNavigationContext.Provider>\n );\n },\n);\n\nBottomNavigation.displayName = \"BottomNavigation\";\n"],"names":["React","jsx","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAiBA,MAAM,0BAA0BA,iBAAM,cAA4C,EAAE;AAE7E,SAAS,6BAA2D;AACzE,SAAOA,iBAAM,WAAW,uBAAuB;AACjD;AAEO,MAAM,mBAAmBA,iBAAM;AAAA,EACpC,CAAC,EAAE,WAAW,UAAU,OAAO,eAAe,gBAAgB,OAAO,GAAG,MAAA,GAAS,QAAQ;AACvF,UAAM,eAAeA,iBAAM;AAAA,MACzB,OAAO,EAAE,OAAO;MAChB,CAAC,OAAO,aAAa;AAAA,IAAA;AAGvB,WACEC,2BAAAA,IAAC,wBAAwB,UAAxB,EAAiC,OAAO,cACvC,UAAAA,2BAAAA;AAAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAWC,GAAAA;AAAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,iBAAiB;AAAA,UACjB;AAAA,QAAA;AAAA,QAEF,OAAO,EAAE,QAAQ,uCAAuC,GAAG,MAAM,MAAA;AAAA,QAChE,GAAG;AAAA,QAEH;AAAA,MAAA;AAAA,IAAA,GAEL;AAAA,EAEJ;AACF;AAEA,iBAAiB,cAAc;;;"}
@@ -24,14 +24,8 @@ function _interopNamespaceDefault(e) {
24
24
  }
25
25
  const React__namespace = /* @__PURE__ */ _interopNamespaceDefault(React);
26
26
  const BottomNavigationAction = React__namespace.forwardRef(({ className, value, icon, label, badge, onClick, asChild = false, children, ...props }, ref) => {
27
- const {
28
- value: selectedValue,
29
- onValueChange,
30
- showLabelsOnlyWhenActive,
31
- hideLabels
32
- } = BottomNavigation.useBottomNavigationContext();
27
+ const { value: selectedValue, onValueChange } = BottomNavigation.useBottomNavigationContext();
33
28
  const isActive = selectedValue === value;
34
- const showLabel = !hideLabels && (!showLabelsOnlyWhenActive || isActive);
35
29
  const handleClick = (e) => {
36
30
  onValueChange?.(value);
37
31
  onClick?.(e);
@@ -43,13 +37,13 @@ const BottomNavigationAction = React__namespace.forwardRef(({ className, value,
43
37
  ref,
44
38
  ...!asChild && { type: "button" },
45
39
  "aria-current": isActive ? "page" : void 0,
46
- "aria-label": !showLabel && label ? label : void 0,
40
+ "aria-label": label,
47
41
  "data-state": isActive ? "active" : "inactive",
48
42
  className: cn.cn(
49
43
  "relative flex min-w-0 flex-1 cursor-pointer flex-col items-center justify-center gap-0.5 overflow-hidden px-2 py-2",
44
+ "text-foreground-default",
50
45
  "motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out",
51
46
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-page",
52
- isActive ? "text-brand-accent-default" : "text-foreground-tertiary hover:text-foreground-secondary",
53
47
  className
54
48
  ),
55
49
  onClick: handleClick,
@@ -57,19 +51,9 @@ const BottomNavigationAction = React__namespace.forwardRef(({ className, value,
57
51
  children: [
58
52
  asChild && /* @__PURE__ */ jsxRuntime.jsx(reactSlot.Slottable, { children }),
59
53
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative inline-flex", children: [
60
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex size-7", "aria-hidden": "true", children: icon }),
61
- badge && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -end-1 -top-0.5", children: badge })
62
- ] }),
63
- showLabel && label && /* @__PURE__ */ jsxRuntime.jsx(
64
- "span",
65
- {
66
- className: cn.cn(
67
- "typography-semibold-body-xs min-w-0 max-w-full truncate",
68
- isActive ? "text-brand-accent-default" : "text-foreground-tertiary"
69
- ),
70
- children: label
71
- }
72
- )
54
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex items-center justify-center [&>svg]:size-7", "aria-hidden": "true", children: icon }),
55
+ badge && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -end-1 -top-2.5", children: badge })
56
+ ] })
73
57
  ]
74
58
  }
75
59
  );
@@ -1 +1 @@
1
- {"version":3,"file":"BottomNavigationAction.cjs","sources":["../../../../src/components/BottomNavigation/BottomNavigationAction.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { useBottomNavigationContext } from \"./BottomNavigation\";\n\nexport interface BottomNavigationActionProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, \"value\"> {\n /** Unique value that identifies this action. */\n value: string;\n /** Icon element displayed above the label. */\n icon: React.ReactElement;\n /** Text label displayed below the icon. */\n label?: string;\n /** Optional badge element (e.g. {@link Count}) rendered at the top-end corner of the icon. */\n badge?: React.ReactNode;\n /** Merge props onto a child element instead of rendering a `<button>`. @default false */\n asChild?: boolean;\n}\n\nexport const BottomNavigationAction = React.forwardRef<\n HTMLButtonElement,\n BottomNavigationActionProps\n>(({ className, value, icon, label, badge, onClick, asChild = false, children, ...props }, ref) => {\n const {\n value: selectedValue,\n onValueChange,\n showLabelsOnlyWhenActive,\n hideLabels,\n } = useBottomNavigationContext();\n\n const isActive = selectedValue === value;\n const showLabel = !hideLabels && (!showLabelsOnlyWhenActive || isActive);\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onValueChange?.(value);\n onClick?.(e);\n };\n\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n {...(!asChild && { type: \"button\" as const })}\n aria-current={isActive ? \"page\" : undefined}\n aria-label={!showLabel && label ? label : undefined}\n data-state={isActive ? \"active\" : \"inactive\"}\n className={cn(\n \"relative flex min-w-0 flex-1 cursor-pointer flex-col items-center justify-center gap-0.5 overflow-hidden px-2 py-2\",\n \"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-page\",\n isActive\n ? \"text-brand-accent-default\"\n : \"text-foreground-tertiary hover:text-foreground-secondary\",\n className,\n )}\n onClick={handleClick}\n {...props}\n >\n {asChild && <Slottable>{children}</Slottable>}\n <span className=\"relative inline-flex\">\n <span className=\"flex size-7\" aria-hidden=\"true\">\n {icon}\n </span>\n {badge && <span className=\"absolute -end-1 -top-0.5\">{badge}</span>}\n </span>\n {showLabel && label && (\n <span\n className={cn(\n \"typography-semibold-body-xs min-w-0 max-w-full truncate\",\n isActive ? \"text-brand-accent-default\" : \"text-foreground-tertiary\",\n )}\n >\n {label}\n </span>\n )}\n </Comp>\n );\n});\n\nBottomNavigationAction.displayName = \"BottomNavigationAction\";\n"],"names":["React","useBottomNavigationContext","Slot","jsxs","cn","jsx","Slottable"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAmBO,MAAM,yBAAyBA,iBAAM,WAG1C,CAAC,EAAE,WAAW,OAAO,MAAM,OAAO,OAAO,SAAS,UAAU,OAAO,UAAU,GAAG,MAAA,GAAS,QAAQ;AACjG,QAAM;AAAA,IACJ,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACEC,4CAAA;AAEJ,QAAM,WAAW,kBAAkB;AACnC,QAAM,YAAY,CAAC,eAAe,CAAC,4BAA4B;AAE/D,QAAM,cAAc,CAAC,MAA2C;AAC9D,oBAAgB,KAAK;AACrB,cAAU,CAAC;AAAA,EACb;AAEA,QAAM,OAAO,UAAUC,UAAAA,OAAO;AAE9B,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACC,GAAI,CAAC,WAAW,EAAE,MAAM,SAAA;AAAA,MACzB,gBAAc,WAAW,SAAS;AAAA,MAClC,cAAY,CAAC,aAAa,QAAQ,QAAQ;AAAA,MAC1C,cAAY,WAAW,WAAW;AAAA,MAClC,WAAWC,GAAAA;AAAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA,WACI,8BACA;AAAA,QACJ;AAAA,MAAA;AAAA,MAEF,SAAS;AAAA,MACR,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,WAAWC,2BAAAA,IAACC,uBAAW,SAAA,CAAS;AAAA,QACjCH,2BAAAA,KAAC,QAAA,EAAK,WAAU,wBACd,UAAA;AAAA,UAAAE,+BAAC,QAAA,EAAK,WAAU,eAAc,eAAY,QACvC,UAAA,MACH;AAAA,UACC,SAASA,2BAAAA,IAAC,QAAA,EAAK,WAAU,4BAA4B,UAAA,MAAA,CAAM;AAAA,QAAA,GAC9D;AAAA,QACC,aAAa,SACZA,2BAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAWD,GAAAA;AAAAA,cACT;AAAA,cACA,WAAW,8BAA8B;AAAA,YAAA;AAAA,YAG1C,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAAA;AAAA,EAAA;AAIR,CAAC;AAED,uBAAuB,cAAc;;"}
1
+ {"version":3,"file":"BottomNavigationAction.cjs","sources":["../../../../src/components/BottomNavigation/BottomNavigationAction.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { useBottomNavigationContext } from \"./BottomNavigation\";\n\nexport interface BottomNavigationActionProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, \"value\"> {\n /** Unique value that identifies this action. */\n value: string;\n /** Icon element displayed in the action. */\n icon: React.ReactElement;\n /** Accessible label applied as `aria-label`. */\n label?: string;\n /** Optional badge element (e.g. {@link Count}) rendered at the top-end corner of the icon. */\n badge?: React.ReactNode;\n /** Merge props onto a child element instead of rendering a `<button>`. @default false */\n asChild?: boolean;\n}\n\nexport const BottomNavigationAction = React.forwardRef<\n HTMLButtonElement,\n BottomNavigationActionProps\n>(({ className, value, icon, label, badge, onClick, asChild = false, children, ...props }, ref) => {\n const { value: selectedValue, onValueChange } = useBottomNavigationContext();\n\n const isActive = selectedValue === value;\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onValueChange?.(value);\n onClick?.(e);\n };\n\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n {...(!asChild && { type: \"button\" as const })}\n aria-current={isActive ? \"page\" : undefined}\n aria-label={label}\n data-state={isActive ? \"active\" : \"inactive\"}\n className={cn(\n \"relative flex min-w-0 flex-1 cursor-pointer flex-col items-center justify-center gap-0.5 overflow-hidden px-2 py-2\",\n \"text-foreground-default\",\n \"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-page\",\n className,\n )}\n onClick={handleClick}\n {...props}\n >\n {asChild && <Slottable>{children}</Slottable>}\n <span className=\"relative inline-flex\">\n <span className=\"flex items-center justify-center [&>svg]:size-7\" aria-hidden=\"true\">\n {icon}\n </span>\n {badge && <span className=\"absolute -end-1 -top-2.5\">{badge}</span>}\n </span>\n </Comp>\n );\n});\n\nBottomNavigationAction.displayName = \"BottomNavigationAction\";\n"],"names":["React","useBottomNavigationContext","Slot","jsxs","cn","jsx","Slottable"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAmBO,MAAM,yBAAyBA,iBAAM,WAG1C,CAAC,EAAE,WAAW,OAAO,MAAM,OAAO,OAAO,SAAS,UAAU,OAAO,UAAU,GAAG,MAAA,GAAS,QAAQ;AACjG,QAAM,EAAE,OAAO,eAAe,cAAA,IAAkBC,iBAAAA,2BAAA;AAEhD,QAAM,WAAW,kBAAkB;AAEnC,QAAM,cAAc,CAAC,MAA2C;AAC9D,oBAAgB,KAAK;AACrB,cAAU,CAAC;AAAA,EACb;AAEA,QAAM,OAAO,UAAUC,UAAAA,OAAO;AAE9B,SACEC,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACC,GAAI,CAAC,WAAW,EAAE,MAAM,SAAA;AAAA,MACzB,gBAAc,WAAW,SAAS;AAAA,MAClC,cAAY;AAAA,MACZ,cAAY,WAAW,WAAW;AAAA,MAClC,WAAWC,GAAAA;AAAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,SAAS;AAAA,MACR,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,WAAWC,2BAAAA,IAACC,uBAAW,SAAA,CAAS;AAAA,QACjCH,2BAAAA,KAAC,QAAA,EAAK,WAAU,wBACd,UAAA;AAAA,UAAAE,+BAAC,QAAA,EAAK,WAAU,mDAAkD,eAAY,QAC3E,UAAA,MACH;AAAA,UACC,SAASA,2BAAAA,IAAC,QAAA,EAAK,WAAU,4BAA4B,UAAA,MAAA,CAAM;AAAA,QAAA,EAAA,CAC9D;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;AAED,uBAAuB,cAAc;;"}
@@ -56,6 +56,10 @@ function getContainerClassName(size, error, disabled) {
56
56
  function getInputClassName(size) {
57
57
  return cn.cn(
58
58
  "h-full min-w-0 flex-1 rounded-xl bg-transparent text-foreground-default no-underline placeholder:text-foreground-secondary placeholder:opacity-40 focus:outline-none disabled:cursor-not-allowed",
59
+ // Override browser autofill background so it fills the full container consistently
60
+ "[&:-webkit-autofill]:[box-shadow:inset_0_0_0_1000px_var(--color-neutral-100)] [&:-webkit-autofill]:[-webkit-text-fill-color:var(--color-foreground-default)]",
61
+ "[&:-webkit-autofill:hover]:[box-shadow:inset_0_0_0_1000px_var(--color-neutral-100)]",
62
+ "[&:-webkit-autofill:focus]:[box-shadow:inset_0_0_0_1000px_var(--color-neutral-100)]",
59
63
  INPUT_SIZE_CLASSES[size]
60
64
  );
61
65
  }
@@ -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\",\n \"40\": \"py-2 typography-regular-body-lg\",\n \"32\": \"py-2 typography-regular-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst ICON_SPACING: Record<TextFieldSize, string> = {\n \"48\": \"gap-3\",\n \"40\": \"gap-3\",\n \"32\": \"gap-2\",\n};\n\nfunction getContainerClassName(size: TextFieldSize, error: boolean, disabled?: boolean) {\n return cn(\n \"flex items-center rounded-xl border bg-neutral-100 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-default\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-400\",\n CONTAINER_HEIGHT[size],\n PADDING_HORIZONTAL[size],\n ICON_SPACING[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getInputClassName(size: TextFieldSize) {\n return cn(\n \"h-full min-w-0 flex-1 rounded-xl bg-transparent text-foreground-default no-underline placeholder:text-foreground-secondary placeholder:opacity-40 focus:outline-none disabled:cursor-not-allowed\",\n INPUT_SIZE_CLASSES[size],\n );\n}\n\nfunction TextFieldIcon({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"flex size-5 shrink-0 items-center justify-center text-foreground-secondary\">\n {children}\n </div>\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-1 pb-0.5\",\n error ? \"text-error-default\" : \"text-foreground-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-foreground-default\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n {leftIcon && <TextFieldIcon>{leftIcon}</TextFieldIcon>}\n\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),\n // Hide native clear button for input[type=\"search\"] in WebKit browsers (Safari/Chrome)\n \"[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n\n {rightIcon && <TextFieldIcon>{rightIcon}</TextFieldIcon>}\n {validated && (\n <TextFieldIcon>\n <CheckOutlineIcon className=\"text-success-default\" />\n </TextFieldIcon>\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\",\n \"40\": \"py-2 typography-regular-body-lg\",\n \"32\": \"py-2 typography-regular-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst ICON_SPACING: Record<TextFieldSize, string> = {\n \"48\": \"gap-3\",\n \"40\": \"gap-3\",\n \"32\": \"gap-2\",\n};\n\nfunction getContainerClassName(size: TextFieldSize, error: boolean, disabled?: boolean) {\n return cn(\n \"flex items-center rounded-xl border bg-neutral-100 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-default\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-400\",\n CONTAINER_HEIGHT[size],\n PADDING_HORIZONTAL[size],\n ICON_SPACING[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getInputClassName(size: TextFieldSize) {\n return cn(\n \"h-full min-w-0 flex-1 rounded-xl bg-transparent text-foreground-default no-underline placeholder:text-foreground-secondary placeholder:opacity-40 focus:outline-none disabled:cursor-not-allowed\",\n // Override browser autofill background so it fills the full container consistently\n \"[&:-webkit-autofill]:[box-shadow:inset_0_0_0_1000px_var(--color-neutral-100)] [&:-webkit-autofill]:[-webkit-text-fill-color:var(--color-foreground-default)]\",\n \"[&:-webkit-autofill:hover]:[box-shadow:inset_0_0_0_1000px_var(--color-neutral-100)]\",\n \"[&:-webkit-autofill:focus]:[box-shadow:inset_0_0_0_1000px_var(--color-neutral-100)]\",\n INPUT_SIZE_CLASSES[size],\n );\n}\n\nfunction TextFieldIcon({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"flex size-5 shrink-0 items-center justify-center text-foreground-secondary\">\n {children}\n </div>\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-1 pb-0.5\",\n error ? \"text-error-default\" : \"text-foreground-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-foreground-default\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n {leftIcon && <TextFieldIcon>{leftIcon}</TextFieldIcon>}\n\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),\n // Hide native clear button for input[type=\"search\"] in WebKit browsers (Safari/Chrome)\n \"[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n\n {rightIcon && <TextFieldIcon>{rightIcon}</TextFieldIcon>}\n {validated && (\n <TextFieldIcon>\n <CheckOutlineIcon className=\"text-success-default\" />\n </TextFieldIcon>\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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"}
@@ -9,14 +9,22 @@ const AccordionContent = React.forwardRef(({ className, children, noPadding, ...
9
9
  ref,
10
10
  className: cn(
11
11
  "overflow-hidden",
12
- "typography-regular-body-md text-foreground-secondary",
13
12
  "motion-safe:data-[state=closed]:animate-accordion-collapse",
14
13
  "motion-safe:data-[state=open]:animate-accordion-expand",
15
- !noPadding && "px-3 pb-3",
16
14
  className
17
15
  ),
18
16
  ...props,
19
- children: /* @__PURE__ */ jsx("div", { className: "min-w-0 overflow-wrap-anywhere", children })
17
+ children: /* @__PURE__ */ jsx(
18
+ "div",
19
+ {
20
+ className: cn(
21
+ "overflow-wrap-anywhere min-w-0",
22
+ "typography-regular-body-md text-foreground-secondary",
23
+ !noPadding && "px-3 pt-2 pb-3"
24
+ ),
25
+ children
26
+ }
27
+ )
20
28
  }
21
29
  ));
22
30
  AccordionContent.displayName = "AccordionContent";
@@ -1 +1 @@
1
- {"version":3,"file":"AccordionContent.mjs","sources":["../../../src/components/Accordion/AccordionContent.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link AccordionContent} panel component. */\nexport type AccordionContentProps = React.ComponentPropsWithoutRef<\n typeof AccordionPrimitive.Content\n> & {\n /** Remove the default inner padding (`px-3 pb-3`). Useful when you need custom content layout. */\n noPadding?: boolean;\n};\n\n/** Renders the collapsible content panel for an {@link AccordionItem}. Animates open and closed. */\nexport const AccordionContent = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Content>,\n AccordionContentProps\n>(({ className, children, noPadding, ...props }, ref) => (\n <AccordionPrimitive.Content\n ref={ref}\n className={cn(\n \"overflow-hidden\",\n \"typography-regular-body-md text-foreground-secondary\",\n \"motion-safe:data-[state=closed]:animate-accordion-collapse\",\n \"motion-safe:data-[state=open]:animate-accordion-expand\",\n !noPadding && \"px-3 pb-3\",\n className,\n )}\n {...props}\n >\n <div className=\"min-w-0 overflow-wrap-anywhere\">{children}</div>\n </AccordionPrimitive.Content>\n));\n\nAccordionContent.displayName = \"AccordionContent\";\n"],"names":[],"mappings":";;;;;AAaO,MAAM,mBAAmB,MAAM,WAGpC,CAAC,EAAE,WAAW,UAAU,WAAW,GAAG,SAAS,QAC/C;AAAA,EAAC,mBAAmB;AAAA,EAAnB;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,CAAC,aAAa;AAAA,MACd;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,IAEJ,UAAA,oBAAC,OAAA,EAAI,WAAU,kCAAkC,SAAA,CAAS;AAAA,EAAA;AAC5D,CACD;AAED,iBAAiB,cAAc;"}
1
+ {"version":3,"file":"AccordionContent.mjs","sources":["../../../src/components/Accordion/AccordionContent.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link AccordionContent} panel component. */\nexport type AccordionContentProps = React.ComponentPropsWithoutRef<\n typeof AccordionPrimitive.Content\n> & {\n /** Remove the default inner padding (`px-3 pb-3`). Useful when you need custom content layout. */\n noPadding?: boolean;\n};\n\n/** Renders the collapsible content panel for an {@link AccordionItem}. Animates open and closed. */\nexport const AccordionContent = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Content>,\n AccordionContentProps\n>(({ className, children, noPadding, ...props }, ref) => (\n <AccordionPrimitive.Content\n ref={ref}\n className={cn(\n \"overflow-hidden\",\n \"motion-safe:data-[state=closed]:animate-accordion-collapse\",\n \"motion-safe:data-[state=open]:animate-accordion-expand\",\n className,\n )}\n {...props}\n >\n <div\n className={cn(\n \"overflow-wrap-anywhere min-w-0\",\n \"typography-regular-body-md text-foreground-secondary\",\n !noPadding && \"px-3 pt-2 pb-3\",\n )}\n >\n {children}\n </div>\n </AccordionPrimitive.Content>\n));\n\nAccordionContent.displayName = \"AccordionContent\";\n"],"names":[],"mappings":";;;;;AAaO,MAAM,mBAAmB,MAAM,WAGpC,CAAC,EAAE,WAAW,UAAU,WAAW,GAAG,SAAS,QAC/C;AAAA,EAAC,mBAAmB;AAAA,EAAnB;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,IAEJ,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA,CAAC,aAAa;AAAA,QAAA;AAAA,QAGf;AAAA,MAAA;AAAA,IAAA;AAAA,EACH;AACF,CACD;AAED,iBAAiB,cAAc;"}
@@ -7,11 +7,7 @@ const AccordionItem = React.forwardRef(({ className, ...props }, ref) => /* @__P
7
7
  AccordionPrimitive.Item,
8
8
  {
9
9
  ref,
10
- className: cn(
11
- "overflow-hidden rounded-xl bg-surface-container",
12
- "border border-neutral-200",
13
- className
14
- ),
10
+ className: cn("rounded-xl bg-surface-container", "border border-neutral-200", className),
15
11
  ...props
16
12
  }
17
13
  ));
@@ -1 +1 @@
1
- {"version":3,"file":"AccordionItem.mjs","sources":["../../../src/components/Accordion/AccordionItem.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link AccordionItem} component. */\nexport type AccordionItemProps = React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>;\n\n/** A single collapsible section within an {@link Accordion}. Contains an {@link AccordionTrigger} and {@link AccordionContent}. */\nexport const AccordionItem = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Item>,\n AccordionItemProps\n>(({ className, ...props }, ref) => (\n <AccordionPrimitive.Item\n ref={ref}\n className={cn(\n \"overflow-hidden rounded-xl bg-surface-container\",\n \"border border-neutral-200\",\n className,\n )}\n {...props}\n />\n));\n\nAccordionItem.displayName = \"AccordionItem\";\n"],"names":[],"mappings":";;;;;AAQO,MAAM,gBAAgB,MAAM,WAGjC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,mBAAmB;AAAA,EAAnB;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AAED,cAAc,cAAc;"}
1
+ {"version":3,"file":"AccordionItem.mjs","sources":["../../../src/components/Accordion/AccordionItem.tsx"],"sourcesContent":["import * as AccordionPrimitive from \"@radix-ui/react-accordion\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\n/** Props for the {@link AccordionItem} component. */\nexport type AccordionItemProps = React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>;\n\n/** A single collapsible section within an {@link Accordion}. Contains an {@link AccordionTrigger} and {@link AccordionContent}. */\nexport const AccordionItem = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Item>,\n AccordionItemProps\n>(({ className, ...props }, ref) => (\n <AccordionPrimitive.Item\n ref={ref}\n className={cn(\"rounded-xl bg-surface-container\", \"border border-neutral-200\", className)}\n {...props}\n />\n));\n\nAccordionItem.displayName = \"AccordionItem\";\n"],"names":[],"mappings":";;;;;AAQO,MAAM,gBAAgB,MAAM,WAGjC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,mBAAmB;AAAA,EAAnB;AAAA,IACC;AAAA,IACA,WAAW,GAAG,mCAAmC,6BAA6B,SAAS;AAAA,IACtF,GAAG;AAAA,EAAA;AACN,CACD;AAED,cAAc,cAAc;"}
@@ -1 +1 @@
1
- {"version":3,"file":"useAutocomplete.mjs","sources":["../../../src/components/Autocomplete/useAutocomplete.ts"],"sourcesContent":["import * as React from \"react\";\nimport type { AutocompleteOption, AutocompleteProps } from \"./Autocomplete\";\nimport { CREATE_PREFIX, defaultFilter, getLabel } from \"./constants\";\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Combobox hook managing interconnected controlled/uncontrolled state\nexport function useAutocomplete(props: AutocompleteProps) {\n const {\n id,\n options,\n disabled = false,\n filterFn,\n creatable = false,\n creatableLabel,\n onCreate,\n inputValue: inputValueProp,\n onInputChange,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n } = props;\n\n const generatedId = React.useId();\n const inputId = id ?? generatedId;\n const helperTextId = `${inputId}-helper`;\n const listboxId = `${inputId}-listbox`;\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const listRef = React.useRef<HTMLDivElement>(null);\n\n const isMulti = props.multiple === true;\n\n // --- single-select state ---\n const [internalValue, setInternalValue] = React.useState<string | null>(\n (!isMulti && props.defaultValue) || null,\n );\n const selectedValue: string | null = !isMulti\n ? props.value !== undefined\n ? props.value\n : internalValue\n : null;\n\n // --- multi-select state ---\n const [internalMultiValue, setInternalMultiValue] = React.useState<string[]>(\n isMulti && props.defaultValue ? props.defaultValue : [],\n );\n const selectedMultiValues: string[] = isMulti\n ? props.value !== undefined\n ? props.value\n : internalMultiValue\n : [];\n\n // --- input text ---\n const [internalInputValue, setInternalInputValue] = React.useState(\"\");\n const searchText = inputValueProp !== undefined ? inputValueProp : internalInputValue;\n\n // --- open state ---\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n const isOpen = openProp !== undefined ? openProp : internalOpen;\n\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [openProp, onOpenChange],\n );\n\n // --- active index ---\n const [activeIndex, setActiveIndex] = React.useState(-1);\n\n // --- filtering ---\n const filter = filterFn ?? defaultFilter;\n\n const filteredOptions = React.useMemo(() => {\n if (!searchText) return options;\n return options.filter((o) => filter(o, searchText));\n }, [options, searchText, filter]);\n\n const showCreate =\n creatable &&\n searchText.length > 0 &&\n !options.some((o) => (o.label ?? o.value).toLowerCase() === searchText.toLowerCase());\n\n const visibleOptions = React.useMemo(() => {\n if (!showCreate) return filteredOptions;\n const createOption: AutocompleteOption = {\n value: `${CREATE_PREFIX}${searchText}`,\n label: creatableLabel ? creatableLabel(searchText) : searchText,\n };\n return [...filteredOptions, createOption];\n }, [filteredOptions, showCreate, searchText, creatableLabel]);\n\n const prevOptionsLengthRef = React.useRef(visibleOptions.length);\n const prevSearchTextRef = React.useRef(searchText);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: intentionally reset only when search text or option count actually changes\n React.useEffect(() => {\n if (\n searchText !== prevSearchTextRef.current ||\n visibleOptions.length !== prevOptionsLengthRef.current\n ) {\n setActiveIndex(-1);\n prevSearchTextRef.current = searchText;\n prevOptionsLengthRef.current = visibleOptions.length;\n }\n }, [searchText, visibleOptions.length]);\n\n // --- scroll active option into view ---\n React.useEffect(() => {\n if (activeIndex < 0 || !listRef.current) return;\n const el = listRef.current.querySelector(`[data-option-index=\"${activeIndex}\"]`);\n if (typeof el?.scrollIntoView === \"function\") {\n el.scrollIntoView({ block: \"nearest\" });\n }\n }, [activeIndex]);\n\n // --- helpers ---\n function clearInputText() {\n if (inputValueProp === undefined) setInternalInputValue(\"\");\n onInputChange?.(\"\");\n }\n\n function selectSingle(val: string | null) {\n if (!isMulti && props.value === undefined) setInternalValue(val);\n if (!isMulti) (props as { onChange?: (v: string | null) => void }).onChange?.(val);\n }\n\n function toggleMulti(val: string) {\n const next = selectedMultiValues.includes(val)\n ? selectedMultiValues.filter((v) => v !== val)\n : [...selectedMultiValues, val];\n if (isMulti && props.value === undefined) setInternalMultiValue(next);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.(next);\n }\n\n function handleSelect(option: AutocompleteOption) {\n if (option.value.startsWith(CREATE_PREFIX)) {\n const raw = option.value.slice(CREATE_PREFIX.length);\n onCreate?.(raw);\n if (isMulti) {\n toggleMulti(raw);\n } else {\n selectSingle(raw);\n setOpen(false);\n }\n clearInputText();\n return;\n }\n\n if (isMulti) {\n toggleMulti(option.value);\n clearInputText();\n } else {\n selectSingle(option.value);\n clearInputText();\n setOpen(false);\n }\n }\n\n function handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {\n const val = e.target.value;\n if (inputValueProp === undefined) setInternalInputValue(val);\n onInputChange?.(val);\n if (!isOpen) setOpen(true);\n }\n\n function findNextEnabled(start: number, direction: 1 | -1): number | null {\n let idx = start;\n while (idx >= 0 && idx < visibleOptions.length && visibleOptions[idx]?.disabled) {\n idx += direction;\n }\n return idx >= 0 && idx < visibleOptions.length ? idx : null;\n }\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Flat switch is clearer than splitting into separate handler functions\n function handleKeyDown(e: React.KeyboardEvent) {\n if (disabled) return;\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev + 1, 1) ?? prev);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev - 1, -1) ?? prev);\n break;\n }\n case \"Enter\": {\n if (isOpen && activeIndex >= 0 && activeIndex < visibleOptions.length) {\n e.preventDefault();\n const opt = visibleOptions[activeIndex];\n if (opt && !opt.disabled) handleSelect(opt);\n }\n break;\n }\n case \"Escape\": {\n e.preventDefault();\n if (isOpen) setOpen(false);\n break;\n }\n case \"Backspace\": {\n if (isMulti && searchText === \"\" && selectedMultiValues.length > 0) {\n const lastVal = selectedMultiValues[selectedMultiValues.length - 1];\n if (lastVal !== undefined) toggleMulti(lastVal);\n }\n break;\n }\n case \"Home\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(0, 1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n case \"End\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(visibleOptions.length - 1, -1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n }\n }\n\n function handleClear(e: React.MouseEvent) {\n e.stopPropagation();\n if (isMulti) {\n if (props.value === undefined) setInternalMultiValue([]);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.([]);\n } else {\n selectSingle(null);\n }\n clearInputText();\n inputRef.current?.focus();\n }\n\n function handleBlur(e: React.FocusEvent<HTMLInputElement>) {\n const container = e.currentTarget.closest(\"[data-autocomplete-root]\");\n if (container && e.relatedTarget instanceof Node && container.contains(e.relatedTarget)) {\n return;\n }\n if (\n e.relatedTarget instanceof Node &&\n (e.relatedTarget as Element).closest?.(\"[data-radix-popper-content-wrapper]\")\n ) {\n return;\n }\n if (isOpen) setOpen(false);\n }\n\n function handleFocus() {\n if (!disabled && !isOpen) setOpen(true);\n }\n\n function handleContainerClick() {\n if (!disabled) {\n inputRef.current?.focus();\n if (!isOpen) setOpen(true);\n }\n }\n\n function handleOpenChange(next: boolean) {\n setOpen(next);\n if (!next) {\n clearInputText();\n setActiveIndex(-1);\n }\n }\n\n const selectedOption = React.useMemo(\n () => options.find((o) => o.value === selectedValue),\n [options, selectedValue],\n );\n\n const selectedMultiOptions = React.useMemo(\n () =>\n selectedMultiValues\n .map((v) => options.find((o) => o.value === v))\n .filter(Boolean) as AutocompleteOption[],\n [options, selectedMultiValues],\n );\n\n const hasClearableValue = isMulti ? selectedMultiValues.length > 0 : selectedValue != null;\n\n const displayInputValue = React.useMemo(() => {\n if (searchText) return searchText;\n if (isMulti || isOpen) return \"\";\n return selectedOption ? getLabel(selectedOption) : \"\";\n }, [searchText, isMulti, isOpen, selectedOption]);\n\n const activeDescendantId = activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : undefined;\n\n return {\n inputId,\n helperTextId,\n listboxId,\n inputRef,\n listRef,\n isMulti,\n isOpen,\n selectedValue,\n selectedMultiValues,\n selectedMultiOptions,\n searchText,\n visibleOptions,\n activeIndex,\n activeDescendantId,\n hasClearableValue,\n displayInputValue,\n setActiveIndex,\n handleSelect,\n handleInputChange,\n handleKeyDown,\n handleClear,\n handleBlur,\n handleFocus,\n handleContainerClick,\n handleOpenChange,\n toggleMulti,\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useAutocomplete.mjs","sources":["../../../src/components/Autocomplete/useAutocomplete.ts"],"sourcesContent":["import * as React from \"react\";\nimport type { AutocompleteOption, AutocompleteProps } from \"./Autocomplete\";\nimport { CREATE_PREFIX, defaultFilter, getLabel } from \"./constants\";\n\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Combobox hook managing interconnected controlled/uncontrolled state\nexport function useAutocomplete(props: AutocompleteProps) {\n const {\n id,\n options,\n disabled = false,\n filterFn,\n creatable = false,\n creatableLabel,\n onCreate,\n inputValue: inputValueProp,\n onInputChange,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n } = props;\n\n const generatedId = React.useId();\n const inputId = id ?? generatedId;\n const helperTextId = `${inputId}-helper`;\n const listboxId = `${inputId}-listbox`;\n\n const inputRef = React.useRef<HTMLInputElement>(null);\n const listRef = React.useRef<HTMLDivElement>(null);\n\n const isMulti = props.multiple === true;\n\n // --- single-select state ---\n const [internalValue, setInternalValue] = React.useState<string | null>(\n (!isMulti && props.defaultValue) || null,\n );\n const selectedValue: string | null = !isMulti\n ? props.value !== undefined\n ? props.value\n : internalValue\n : null;\n\n // --- multi-select state ---\n const [internalMultiValue, setInternalMultiValue] = React.useState<string[]>(\n isMulti && props.defaultValue ? props.defaultValue : [],\n );\n const selectedMultiValues: string[] = isMulti\n ? props.value !== undefined\n ? props.value\n : internalMultiValue\n : [];\n\n // --- input text ---\n const [internalInputValue, setInternalInputValue] = React.useState(\"\");\n const searchText = inputValueProp !== undefined ? inputValueProp : internalInputValue;\n\n // --- open state ---\n const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n const isOpen = openProp !== undefined ? openProp : internalOpen;\n\n const setOpen = React.useCallback(\n (next: boolean) => {\n if (openProp === undefined) setInternalOpen(next);\n onOpenChange?.(next);\n },\n [openProp, onOpenChange],\n );\n\n // --- active index ---\n const [activeIndex, setActiveIndex] = React.useState(-1);\n\n // --- filtering ---\n const filter = filterFn ?? defaultFilter;\n\n const filteredOptions = React.useMemo(() => {\n if (!searchText) return options;\n return options.filter((o) => filter(o, searchText));\n }, [options, searchText, filter]);\n\n const showCreate =\n creatable &&\n searchText.length > 0 &&\n !options.some((o) => (o.label ?? o.value).toLowerCase() === searchText.toLowerCase());\n\n const visibleOptions = React.useMemo(() => {\n if (!showCreate) return filteredOptions;\n const createOption: AutocompleteOption = {\n value: `${CREATE_PREFIX}${searchText}`,\n label: creatableLabel ? creatableLabel(searchText) : searchText,\n };\n return [...filteredOptions, createOption];\n }, [filteredOptions, showCreate, searchText, creatableLabel]);\n\n const prevOptionsLengthRef = React.useRef(visibleOptions.length);\n const prevSearchTextRef = React.useRef(searchText);\n\n React.useEffect(() => {\n if (\n searchText !== prevSearchTextRef.current ||\n visibleOptions.length !== prevOptionsLengthRef.current\n ) {\n setActiveIndex(-1);\n prevSearchTextRef.current = searchText;\n prevOptionsLengthRef.current = visibleOptions.length;\n }\n }, [searchText, visibleOptions.length]);\n\n // --- scroll active option into view ---\n React.useEffect(() => {\n if (activeIndex < 0 || !listRef.current) return;\n const el = listRef.current.querySelector(`[data-option-index=\"${activeIndex}\"]`);\n if (typeof el?.scrollIntoView === \"function\") {\n el.scrollIntoView({ block: \"nearest\" });\n }\n }, [activeIndex]);\n\n // --- helpers ---\n function clearInputText() {\n if (inputValueProp === undefined) setInternalInputValue(\"\");\n onInputChange?.(\"\");\n }\n\n function selectSingle(val: string | null) {\n if (!isMulti && props.value === undefined) setInternalValue(val);\n if (!isMulti) (props as { onChange?: (v: string | null) => void }).onChange?.(val);\n }\n\n function toggleMulti(val: string) {\n const next = selectedMultiValues.includes(val)\n ? selectedMultiValues.filter((v) => v !== val)\n : [...selectedMultiValues, val];\n if (isMulti && props.value === undefined) setInternalMultiValue(next);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.(next);\n }\n\n function handleSelect(option: AutocompleteOption) {\n if (option.value.startsWith(CREATE_PREFIX)) {\n const raw = option.value.slice(CREATE_PREFIX.length);\n onCreate?.(raw);\n if (isMulti) {\n toggleMulti(raw);\n } else {\n selectSingle(raw);\n setOpen(false);\n }\n clearInputText();\n return;\n }\n\n if (isMulti) {\n toggleMulti(option.value);\n clearInputText();\n } else {\n selectSingle(option.value);\n clearInputText();\n setOpen(false);\n }\n }\n\n function handleInputChange(e: React.ChangeEvent<HTMLInputElement>) {\n const val = e.target.value;\n if (inputValueProp === undefined) setInternalInputValue(val);\n onInputChange?.(val);\n if (!isOpen) setOpen(true);\n }\n\n function findNextEnabled(start: number, direction: 1 | -1): number | null {\n let idx = start;\n while (idx >= 0 && idx < visibleOptions.length && visibleOptions[idx]?.disabled) {\n idx += direction;\n }\n return idx >= 0 && idx < visibleOptions.length ? idx : null;\n }\n\n // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Flat switch is clearer than splitting into separate handler functions\n function handleKeyDown(e: React.KeyboardEvent) {\n if (disabled) return;\n\n switch (e.key) {\n case \"ArrowDown\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev + 1, 1) ?? prev);\n break;\n }\n case \"ArrowUp\": {\n e.preventDefault();\n if (!isOpen) {\n setOpen(true);\n return;\n }\n setActiveIndex((prev) => findNextEnabled(prev - 1, -1) ?? prev);\n break;\n }\n case \"Enter\": {\n if (isOpen && activeIndex >= 0 && activeIndex < visibleOptions.length) {\n e.preventDefault();\n const opt = visibleOptions[activeIndex];\n if (opt && !opt.disabled) handleSelect(opt);\n }\n break;\n }\n case \"Escape\": {\n e.preventDefault();\n if (isOpen) setOpen(false);\n break;\n }\n case \"Backspace\": {\n if (isMulti && searchText === \"\" && selectedMultiValues.length > 0) {\n const lastVal = selectedMultiValues[selectedMultiValues.length - 1];\n if (lastVal !== undefined) toggleMulti(lastVal);\n }\n break;\n }\n case \"Home\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(0, 1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n case \"End\": {\n if (isOpen) {\n e.preventDefault();\n const idx = findNextEnabled(visibleOptions.length - 1, -1);\n if (idx !== null) setActiveIndex(idx);\n }\n break;\n }\n }\n }\n\n function handleClear(e: React.MouseEvent) {\n e.stopPropagation();\n if (isMulti) {\n if (props.value === undefined) setInternalMultiValue([]);\n if (isMulti) (props as { onChange?: (v: string[]) => void }).onChange?.([]);\n } else {\n selectSingle(null);\n }\n clearInputText();\n inputRef.current?.focus();\n }\n\n function handleBlur(e: React.FocusEvent<HTMLInputElement>) {\n const container = e.currentTarget.closest(\"[data-autocomplete-root]\");\n if (container && e.relatedTarget instanceof Node && container.contains(e.relatedTarget)) {\n return;\n }\n if (\n e.relatedTarget instanceof Node &&\n (e.relatedTarget as Element).closest?.(\"[data-radix-popper-content-wrapper]\")\n ) {\n return;\n }\n if (isOpen) setOpen(false);\n }\n\n function handleFocus() {\n if (!disabled && !isOpen) setOpen(true);\n }\n\n function handleContainerClick() {\n if (!disabled) {\n inputRef.current?.focus();\n if (!isOpen) setOpen(true);\n }\n }\n\n function handleOpenChange(next: boolean) {\n setOpen(next);\n if (!next) {\n clearInputText();\n setActiveIndex(-1);\n }\n }\n\n const selectedOption = React.useMemo(\n () => options.find((o) => o.value === selectedValue),\n [options, selectedValue],\n );\n\n const selectedMultiOptions = React.useMemo(\n () =>\n selectedMultiValues\n .map((v) => options.find((o) => o.value === v))\n .filter(Boolean) as AutocompleteOption[],\n [options, selectedMultiValues],\n );\n\n const hasClearableValue = isMulti ? selectedMultiValues.length > 0 : selectedValue != null;\n\n const displayInputValue = React.useMemo(() => {\n if (searchText) return searchText;\n if (isMulti || isOpen) return \"\";\n return selectedOption ? getLabel(selectedOption) : \"\";\n }, [searchText, isMulti, isOpen, selectedOption]);\n\n const activeDescendantId = activeIndex >= 0 ? `${listboxId}-option-${activeIndex}` : undefined;\n\n return {\n inputId,\n helperTextId,\n listboxId,\n inputRef,\n listRef,\n isMulti,\n isOpen,\n selectedValue,\n selectedMultiValues,\n selectedMultiOptions,\n searchText,\n visibleOptions,\n activeIndex,\n activeDescendantId,\n hasClearableValue,\n displayInputValue,\n setActiveIndex,\n handleSelect,\n handleInputChange,\n handleKeyDown,\n handleClear,\n handleBlur,\n handleFocus,\n handleContainerClick,\n handleOpenChange,\n toggleMulti,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -2,27 +2,15 @@
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "../../utils/cn.mjs";
5
- const BottomNavigationContext = React.createContext({
6
- showLabelsOnlyWhenActive: false,
7
- hideLabels: false
8
- });
5
+ const BottomNavigationContext = React.createContext({});
9
6
  function useBottomNavigationContext() {
10
7
  return React.useContext(BottomNavigationContext);
11
8
  }
12
9
  const BottomNavigation = React.forwardRef(
13
- ({
14
- className,
15
- children,
16
- value,
17
- onValueChange,
18
- showLabelsOnlyWhenActive = false,
19
- hideLabels = false,
20
- hideOnDesktop = false,
21
- ...props
22
- }, ref) => {
10
+ ({ className, children, value, onValueChange, hideOnDesktop = false, ...props }, ref) => {
23
11
  const contextValue = React.useMemo(
24
- () => ({ value, onValueChange, showLabelsOnlyWhenActive, hideLabels }),
25
- [value, onValueChange, showLabelsOnlyWhenActive, hideLabels]
12
+ () => ({ value, onValueChange }),
13
+ [value, onValueChange]
26
14
  );
27
15
  return /* @__PURE__ */ jsx(BottomNavigationContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
28
16
  "nav",
@@ -31,7 +19,7 @@ const BottomNavigation = React.forwardRef(
31
19
  className: cn(
32
20
  "fixed inset-x-0 bottom-0",
33
21
  "flex h-[calc(env(safe-area-inset-bottom,0px)+68px)] items-center justify-around",
34
- "border-neutral-200 border-t bg-surface-page/[.82] backdrop-blur-[16px]",
22
+ "border-neutral-200 border-t bg-surface-page",
35
23
  "pb-[env(safe-area-inset-bottom,0px)]",
36
24
  hideOnDesktop && "md:hidden",
37
25
  className
@@ -1 +1 @@
1
- {"version":3,"file":"BottomNavigation.mjs","sources":["../../../src/components/BottomNavigation/BottomNavigation.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport interface BottomNavigationProps extends React.HTMLAttributes<HTMLElement> {\n /** The currently selected action value. */\n value?: string;\n /** Called when the selected action changes. */\n onValueChange?: (value: string) => void;\n /** When `true`, labels are only shown on the active action. @default false */\n showLabelsOnlyWhenActive?: boolean;\n /** When `true`, all labels are hidden. @default false */\n hideLabels?: boolean;\n /** When `true`, the navigation bar is hidden on viewports wider than `md` (768 px). @default false */\n hideOnDesktop?: boolean;\n}\n\ninterface BottomNavigationContextValue {\n value?: string;\n onValueChange?: (value: string) => void;\n showLabelsOnlyWhenActive: boolean;\n hideLabels: boolean;\n}\n\nconst BottomNavigationContext = React.createContext<BottomNavigationContextValue>({\n showLabelsOnlyWhenActive: false,\n hideLabels: false,\n});\n\nexport function useBottomNavigationContext(): BottomNavigationContextValue {\n return React.useContext(BottomNavigationContext);\n}\n\nexport const BottomNavigation = React.forwardRef<HTMLElement, BottomNavigationProps>(\n (\n {\n className,\n children,\n value,\n onValueChange,\n showLabelsOnlyWhenActive = false,\n hideLabels = false,\n hideOnDesktop = false,\n ...props\n },\n ref,\n ) => {\n const contextValue = React.useMemo<BottomNavigationContextValue>(\n () => ({ value, onValueChange, showLabelsOnlyWhenActive, hideLabels }),\n [value, onValueChange, showLabelsOnlyWhenActive, hideLabels],\n );\n\n return (\n <BottomNavigationContext.Provider value={contextValue}>\n <nav\n ref={ref}\n className={cn(\n \"fixed inset-x-0 bottom-0\",\n \"flex h-[calc(env(safe-area-inset-bottom,0px)+68px)] items-center justify-around\",\n \"border-neutral-200 border-t bg-surface-page/[.82] backdrop-blur-[16px]\",\n \"pb-[env(safe-area-inset-bottom,0px)]\",\n hideOnDesktop && \"md:hidden\",\n className,\n )}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...props.style }}\n {...props}\n >\n {children}\n </nav>\n </BottomNavigationContext.Provider>\n );\n },\n);\n\nBottomNavigation.displayName = \"BottomNavigation\";\n"],"names":[],"mappings":";;;;AAuBA,MAAM,0BAA0B,MAAM,cAA4C;AAAA,EAChF,0BAA0B;AAAA,EAC1B,YAAY;AACd,CAAC;AAEM,SAAS,6BAA2D;AACzE,SAAO,MAAM,WAAW,uBAAuB;AACjD;AAEO,MAAM,mBAAmB,MAAM;AAAA,EACpC,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,2BAA2B;AAAA,IAC3B,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,GAAG;AAAA,EAAA,GAEL,QACG;AACH,UAAM,eAAe,MAAM;AAAA,MACzB,OAAO,EAAE,OAAO,eAAe,0BAA0B,WAAA;AAAA,MACzD,CAAC,OAAO,eAAe,0BAA0B,UAAU;AAAA,IAAA;AAG7D,WACE,oBAAC,wBAAwB,UAAxB,EAAiC,OAAO,cACvC,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,iBAAiB;AAAA,UACjB;AAAA,QAAA;AAAA,QAEF,OAAO,EAAE,QAAQ,uCAAuC,GAAG,MAAM,MAAA;AAAA,QAChE,GAAG;AAAA,QAEH;AAAA,MAAA;AAAA,IAAA,GAEL;AAAA,EAEJ;AACF;AAEA,iBAAiB,cAAc;"}
1
+ {"version":3,"file":"BottomNavigation.mjs","sources":["../../../src/components/BottomNavigation/BottomNavigation.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\n\nexport interface BottomNavigationProps extends React.HTMLAttributes<HTMLElement> {\n /** The currently selected action value. */\n value?: string;\n /** Called when the selected action changes. */\n onValueChange?: (value: string) => void;\n /** When `true`, the navigation bar is hidden on viewports wider than `md` (768 px). @default false */\n hideOnDesktop?: boolean;\n}\n\ninterface BottomNavigationContextValue {\n value?: string;\n onValueChange?: (value: string) => void;\n}\n\nconst BottomNavigationContext = React.createContext<BottomNavigationContextValue>({});\n\nexport function useBottomNavigationContext(): BottomNavigationContextValue {\n return React.useContext(BottomNavigationContext);\n}\n\nexport const BottomNavigation = React.forwardRef<HTMLElement, BottomNavigationProps>(\n ({ className, children, value, onValueChange, hideOnDesktop = false, ...props }, ref) => {\n const contextValue = React.useMemo<BottomNavigationContextValue>(\n () => ({ value, onValueChange }),\n [value, onValueChange],\n );\n\n return (\n <BottomNavigationContext.Provider value={contextValue}>\n <nav\n ref={ref}\n className={cn(\n \"fixed inset-x-0 bottom-0\",\n \"flex h-[calc(env(safe-area-inset-bottom,0px)+68px)] items-center justify-around\",\n \"border-neutral-200 border-t bg-surface-page\",\n \"pb-[env(safe-area-inset-bottom,0px)]\",\n hideOnDesktop && \"md:hidden\",\n className,\n )}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...props.style }}\n {...props}\n >\n {children}\n </nav>\n </BottomNavigationContext.Provider>\n );\n },\n);\n\nBottomNavigation.displayName = \"BottomNavigation\";\n"],"names":[],"mappings":";;;;AAiBA,MAAM,0BAA0B,MAAM,cAA4C,EAAE;AAE7E,SAAS,6BAA2D;AACzE,SAAO,MAAM,WAAW,uBAAuB;AACjD;AAEO,MAAM,mBAAmB,MAAM;AAAA,EACpC,CAAC,EAAE,WAAW,UAAU,OAAO,eAAe,gBAAgB,OAAO,GAAG,MAAA,GAAS,QAAQ;AACvF,UAAM,eAAe,MAAM;AAAA,MACzB,OAAO,EAAE,OAAO;MAChB,CAAC,OAAO,aAAa;AAAA,IAAA;AAGvB,WACE,oBAAC,wBAAwB,UAAxB,EAAiC,OAAO,cACvC,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,iBAAiB;AAAA,UACjB;AAAA,QAAA;AAAA,QAEF,OAAO,EAAE,QAAQ,uCAAuC,GAAG,MAAM,MAAA;AAAA,QAChE,GAAG;AAAA,QAEH;AAAA,MAAA;AAAA,IAAA,GAEL;AAAA,EAEJ;AACF;AAEA,iBAAiB,cAAc;"}
@@ -5,14 +5,8 @@ import * as React from "react";
5
5
  import { cn } from "../../utils/cn.mjs";
6
6
  import { useBottomNavigationContext } from "./BottomNavigation.mjs";
7
7
  const BottomNavigationAction = React.forwardRef(({ className, value, icon, label, badge, onClick, asChild = false, children, ...props }, ref) => {
8
- const {
9
- value: selectedValue,
10
- onValueChange,
11
- showLabelsOnlyWhenActive,
12
- hideLabels
13
- } = useBottomNavigationContext();
8
+ const { value: selectedValue, onValueChange } = useBottomNavigationContext();
14
9
  const isActive = selectedValue === value;
15
- const showLabel = !hideLabels && (!showLabelsOnlyWhenActive || isActive);
16
10
  const handleClick = (e) => {
17
11
  onValueChange?.(value);
18
12
  onClick?.(e);
@@ -24,13 +18,13 @@ const BottomNavigationAction = React.forwardRef(({ className, value, icon, label
24
18
  ref,
25
19
  ...!asChild && { type: "button" },
26
20
  "aria-current": isActive ? "page" : void 0,
27
- "aria-label": !showLabel && label ? label : void 0,
21
+ "aria-label": label,
28
22
  "data-state": isActive ? "active" : "inactive",
29
23
  className: cn(
30
24
  "relative flex min-w-0 flex-1 cursor-pointer flex-col items-center justify-center gap-0.5 overflow-hidden px-2 py-2",
25
+ "text-foreground-default",
31
26
  "motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out",
32
27
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-page",
33
- isActive ? "text-brand-accent-default" : "text-foreground-tertiary hover:text-foreground-secondary",
34
28
  className
35
29
  ),
36
30
  onClick: handleClick,
@@ -38,19 +32,9 @@ const BottomNavigationAction = React.forwardRef(({ className, value, icon, label
38
32
  children: [
39
33
  asChild && /* @__PURE__ */ jsx(Slottable, { children }),
40
34
  /* @__PURE__ */ jsxs("span", { className: "relative inline-flex", children: [
41
- /* @__PURE__ */ jsx("span", { className: "flex size-7", "aria-hidden": "true", children: icon }),
42
- badge && /* @__PURE__ */ jsx("span", { className: "absolute -end-1 -top-0.5", children: badge })
43
- ] }),
44
- showLabel && label && /* @__PURE__ */ jsx(
45
- "span",
46
- {
47
- className: cn(
48
- "typography-semibold-body-xs min-w-0 max-w-full truncate",
49
- isActive ? "text-brand-accent-default" : "text-foreground-tertiary"
50
- ),
51
- children: label
52
- }
53
- )
35
+ /* @__PURE__ */ jsx("span", { className: "flex items-center justify-center [&>svg]:size-7", "aria-hidden": "true", children: icon }),
36
+ badge && /* @__PURE__ */ jsx("span", { className: "absolute -end-1 -top-2.5", children: badge })
37
+ ] })
54
38
  ]
55
39
  }
56
40
  );
@@ -1 +1 @@
1
- {"version":3,"file":"BottomNavigationAction.mjs","sources":["../../../src/components/BottomNavigation/BottomNavigationAction.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { useBottomNavigationContext } from \"./BottomNavigation\";\n\nexport interface BottomNavigationActionProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, \"value\"> {\n /** Unique value that identifies this action. */\n value: string;\n /** Icon element displayed above the label. */\n icon: React.ReactElement;\n /** Text label displayed below the icon. */\n label?: string;\n /** Optional badge element (e.g. {@link Count}) rendered at the top-end corner of the icon. */\n badge?: React.ReactNode;\n /** Merge props onto a child element instead of rendering a `<button>`. @default false */\n asChild?: boolean;\n}\n\nexport const BottomNavigationAction = React.forwardRef<\n HTMLButtonElement,\n BottomNavigationActionProps\n>(({ className, value, icon, label, badge, onClick, asChild = false, children, ...props }, ref) => {\n const {\n value: selectedValue,\n onValueChange,\n showLabelsOnlyWhenActive,\n hideLabels,\n } = useBottomNavigationContext();\n\n const isActive = selectedValue === value;\n const showLabel = !hideLabels && (!showLabelsOnlyWhenActive || isActive);\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onValueChange?.(value);\n onClick?.(e);\n };\n\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n {...(!asChild && { type: \"button\" as const })}\n aria-current={isActive ? \"page\" : undefined}\n aria-label={!showLabel && label ? label : undefined}\n data-state={isActive ? \"active\" : \"inactive\"}\n className={cn(\n \"relative flex min-w-0 flex-1 cursor-pointer flex-col items-center justify-center gap-0.5 overflow-hidden px-2 py-2\",\n \"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-page\",\n isActive\n ? \"text-brand-accent-default\"\n : \"text-foreground-tertiary hover:text-foreground-secondary\",\n className,\n )}\n onClick={handleClick}\n {...props}\n >\n {asChild && <Slottable>{children}</Slottable>}\n <span className=\"relative inline-flex\">\n <span className=\"flex size-7\" aria-hidden=\"true\">\n {icon}\n </span>\n {badge && <span className=\"absolute -end-1 -top-0.5\">{badge}</span>}\n </span>\n {showLabel && label && (\n <span\n className={cn(\n \"typography-semibold-body-xs min-w-0 max-w-full truncate\",\n isActive ? \"text-brand-accent-default\" : \"text-foreground-tertiary\",\n )}\n >\n {label}\n </span>\n )}\n </Comp>\n );\n});\n\nBottomNavigationAction.displayName = \"BottomNavigationAction\";\n"],"names":[],"mappings":";;;;;;AAmBO,MAAM,yBAAyB,MAAM,WAG1C,CAAC,EAAE,WAAW,OAAO,MAAM,OAAO,OAAO,SAAS,UAAU,OAAO,UAAU,GAAG,MAAA,GAAS,QAAQ;AACjG,QAAM;AAAA,IACJ,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACE,2BAAA;AAEJ,QAAM,WAAW,kBAAkB;AACnC,QAAM,YAAY,CAAC,eAAe,CAAC,4BAA4B;AAE/D,QAAM,cAAc,CAAC,MAA2C;AAC9D,oBAAgB,KAAK;AACrB,cAAU,CAAC;AAAA,EACb;AAEA,QAAM,OAAO,UAAU,OAAO;AAE9B,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACC,GAAI,CAAC,WAAW,EAAE,MAAM,SAAA;AAAA,MACzB,gBAAc,WAAW,SAAS;AAAA,MAClC,cAAY,CAAC,aAAa,QAAQ,QAAQ;AAAA,MAC1C,cAAY,WAAW,WAAW;AAAA,MAClC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA,WACI,8BACA;AAAA,QACJ;AAAA,MAAA;AAAA,MAEF,SAAS;AAAA,MACR,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,WAAW,oBAAC,aAAW,SAAA,CAAS;AAAA,QACjC,qBAAC,QAAA,EAAK,WAAU,wBACd,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,eAAc,eAAY,QACvC,UAAA,MACH;AAAA,UACC,SAAS,oBAAC,QAAA,EAAK,WAAU,4BAA4B,UAAA,MAAA,CAAM;AAAA,QAAA,GAC9D;AAAA,QACC,aAAa,SACZ;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,WAAW,8BAA8B;AAAA,YAAA;AAAA,YAG1C,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MACH;AAAA,IAAA;AAAA,EAAA;AAIR,CAAC;AAED,uBAAuB,cAAc;"}
1
+ {"version":3,"file":"BottomNavigationAction.mjs","sources":["../../../src/components/BottomNavigation/BottomNavigationAction.tsx"],"sourcesContent":["import { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { useBottomNavigationContext } from \"./BottomNavigation\";\n\nexport interface BottomNavigationActionProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, \"value\"> {\n /** Unique value that identifies this action. */\n value: string;\n /** Icon element displayed in the action. */\n icon: React.ReactElement;\n /** Accessible label applied as `aria-label`. */\n label?: string;\n /** Optional badge element (e.g. {@link Count}) rendered at the top-end corner of the icon. */\n badge?: React.ReactNode;\n /** Merge props onto a child element instead of rendering a `<button>`. @default false */\n asChild?: boolean;\n}\n\nexport const BottomNavigationAction = React.forwardRef<\n HTMLButtonElement,\n BottomNavigationActionProps\n>(({ className, value, icon, label, badge, onClick, asChild = false, children, ...props }, ref) => {\n const { value: selectedValue, onValueChange } = useBottomNavigationContext();\n\n const isActive = selectedValue === value;\n\n const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {\n onValueChange?.(value);\n onClick?.(e);\n };\n\n const Comp = asChild ? Slot : \"button\";\n\n return (\n <Comp\n ref={ref}\n {...(!asChild && { type: \"button\" as const })}\n aria-current={isActive ? \"page\" : undefined}\n aria-label={label}\n data-state={isActive ? \"active\" : \"inactive\"}\n className={cn(\n \"relative flex min-w-0 flex-1 cursor-pointer flex-col items-center justify-center gap-0.5 overflow-hidden px-2 py-2\",\n \"text-foreground-default\",\n \"motion-safe:transition-colors motion-safe:duration-150 motion-safe:ease-in-out\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-surface-page\",\n className,\n )}\n onClick={handleClick}\n {...props}\n >\n {asChild && <Slottable>{children}</Slottable>}\n <span className=\"relative inline-flex\">\n <span className=\"flex items-center justify-center [&>svg]:size-7\" aria-hidden=\"true\">\n {icon}\n </span>\n {badge && <span className=\"absolute -end-1 -top-2.5\">{badge}</span>}\n </span>\n </Comp>\n );\n});\n\nBottomNavigationAction.displayName = \"BottomNavigationAction\";\n"],"names":[],"mappings":";;;;;;AAmBO,MAAM,yBAAyB,MAAM,WAG1C,CAAC,EAAE,WAAW,OAAO,MAAM,OAAO,OAAO,SAAS,UAAU,OAAO,UAAU,GAAG,MAAA,GAAS,QAAQ;AACjG,QAAM,EAAE,OAAO,eAAe,cAAA,IAAkB,2BAAA;AAEhD,QAAM,WAAW,kBAAkB;AAEnC,QAAM,cAAc,CAAC,MAA2C;AAC9D,oBAAgB,KAAK;AACrB,cAAU,CAAC;AAAA,EACb;AAEA,QAAM,OAAO,UAAU,OAAO;AAE9B,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACC,GAAI,CAAC,WAAW,EAAE,MAAM,SAAA;AAAA,MACzB,gBAAc,WAAW,SAAS;AAAA,MAClC,cAAY;AAAA,MACZ,cAAY,WAAW,WAAW;AAAA,MAClC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAEF,SAAS;AAAA,MACR,GAAG;AAAA,MAEH,UAAA;AAAA,QAAA,WAAW,oBAAC,aAAW,SAAA,CAAS;AAAA,QACjC,qBAAC,QAAA,EAAK,WAAU,wBACd,UAAA;AAAA,UAAA,oBAAC,QAAA,EAAK,WAAU,mDAAkD,eAAY,QAC3E,UAAA,MACH;AAAA,UACC,SAAS,oBAAC,QAAA,EAAK,WAAU,4BAA4B,UAAA,MAAA,CAAM;AAAA,QAAA,EAAA,CAC9D;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGN,CAAC;AAED,uBAAuB,cAAc;"}
@@ -37,6 +37,10 @@ function getContainerClassName(size, error, disabled) {
37
37
  function getInputClassName(size) {
38
38
  return cn(
39
39
  "h-full min-w-0 flex-1 rounded-xl bg-transparent text-foreground-default no-underline placeholder:text-foreground-secondary placeholder:opacity-40 focus:outline-none disabled:cursor-not-allowed",
40
+ // Override browser autofill background so it fills the full container consistently
41
+ "[&:-webkit-autofill]:[box-shadow:inset_0_0_0_1000px_var(--color-neutral-100)] [&:-webkit-autofill]:[-webkit-text-fill-color:var(--color-foreground-default)]",
42
+ "[&:-webkit-autofill:hover]:[box-shadow:inset_0_0_0_1000px_var(--color-neutral-100)]",
43
+ "[&:-webkit-autofill:focus]:[box-shadow:inset_0_0_0_1000px_var(--color-neutral-100)]",
40
44
  INPUT_SIZE_CLASSES[size]
41
45
  );
42
46
  }
@@ -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\",\n \"40\": \"py-2 typography-regular-body-lg\",\n \"32\": \"py-2 typography-regular-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst ICON_SPACING: Record<TextFieldSize, string> = {\n \"48\": \"gap-3\",\n \"40\": \"gap-3\",\n \"32\": \"gap-2\",\n};\n\nfunction getContainerClassName(size: TextFieldSize, error: boolean, disabled?: boolean) {\n return cn(\n \"flex items-center rounded-xl border bg-neutral-100 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-default\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-400\",\n CONTAINER_HEIGHT[size],\n PADDING_HORIZONTAL[size],\n ICON_SPACING[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getInputClassName(size: TextFieldSize) {\n return cn(\n \"h-full min-w-0 flex-1 rounded-xl bg-transparent text-foreground-default no-underline placeholder:text-foreground-secondary placeholder:opacity-40 focus:outline-none disabled:cursor-not-allowed\",\n INPUT_SIZE_CLASSES[size],\n );\n}\n\nfunction TextFieldIcon({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"flex size-5 shrink-0 items-center justify-center text-foreground-secondary\">\n {children}\n </div>\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-1 pb-0.5\",\n error ? \"text-error-default\" : \"text-foreground-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-foreground-default\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n {leftIcon && <TextFieldIcon>{leftIcon}</TextFieldIcon>}\n\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),\n // Hide native clear button for input[type=\"search\"] in WebKit browsers (Safari/Chrome)\n \"[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n\n {rightIcon && <TextFieldIcon>{rightIcon}</TextFieldIcon>}\n {validated && (\n <TextFieldIcon>\n <CheckOutlineIcon className=\"text-success-default\" />\n </TextFieldIcon>\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\",\n \"40\": \"py-2 typography-regular-body-lg\",\n \"32\": \"py-2 typography-regular-body-md\",\n};\n\nconst PADDING_HORIZONTAL: Record<TextFieldSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst ICON_SPACING: Record<TextFieldSize, string> = {\n \"48\": \"gap-3\",\n \"40\": \"gap-3\",\n \"32\": \"gap-2\",\n};\n\nfunction getContainerClassName(size: TextFieldSize, error: boolean, disabled?: boolean) {\n return cn(\n \"flex items-center rounded-xl border bg-neutral-100 has-focus-visible:outline-none motion-safe:transition-colors\",\n error ? \"border-error-default\" : \"border-transparent\",\n !disabled && !error && \"hover:border-neutral-400\",\n CONTAINER_HEIGHT[size],\n PADDING_HORIZONTAL[size],\n ICON_SPACING[size],\n disabled && \"opacity-50\",\n );\n}\n\nfunction getInputClassName(size: TextFieldSize) {\n return cn(\n \"h-full min-w-0 flex-1 rounded-xl bg-transparent text-foreground-default no-underline placeholder:text-foreground-secondary placeholder:opacity-40 focus:outline-none disabled:cursor-not-allowed\",\n // Override browser autofill background so it fills the full container consistently\n \"[&:-webkit-autofill]:[box-shadow:inset_0_0_0_1000px_var(--color-neutral-100)] [&:-webkit-autofill]:[-webkit-text-fill-color:var(--color-foreground-default)]\",\n \"[&:-webkit-autofill:hover]:[box-shadow:inset_0_0_0_1000px_var(--color-neutral-100)]\",\n \"[&:-webkit-autofill:focus]:[box-shadow:inset_0_0_0_1000px_var(--color-neutral-100)]\",\n INPUT_SIZE_CLASSES[size],\n );\n}\n\nfunction TextFieldIcon({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"flex size-5 shrink-0 items-center justify-center text-foreground-secondary\">\n {children}\n </div>\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-1 pb-0.5\",\n error ? \"text-error-default\" : \"text-foreground-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-foreground-default\"\n >\n {label}\n </label>\n )}\n\n <div className={getContainerClassName(size, error, disabled)}>\n {leftIcon && <TextFieldIcon>{leftIcon}</TextFieldIcon>}\n\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),\n // Hide native clear button for input[type=\"search\"] in WebKit browsers (Safari/Chrome)\n \"[&[type='search']::-webkit-search-cancel-button]:hidden [&[type='search']::-webkit-search-cancel-button]:appearance-none\",\n )}\n {...props}\n />\n\n {rightIcon && <TextFieldIcon>{rightIcon}</TextFieldIcon>}\n {validated && (\n <TextFieldIcon>\n <CheckOutlineIcon className=\"text-success-default\" />\n </TextFieldIcon>\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/dist/index.d.ts CHANGED
@@ -388,9 +388,9 @@ export declare const BottomNavigationAction: React_2.ForwardRefExoticComponent<B
388
388
  export declare interface BottomNavigationActionProps extends Omit<React_2.ButtonHTMLAttributes<HTMLButtonElement>, "value"> {
389
389
  /** Unique value that identifies this action. */
390
390
  value: string;
391
- /** Icon element displayed above the label. */
391
+ /** Icon element displayed in the action. */
392
392
  icon: React_2.ReactElement;
393
- /** Text label displayed below the icon. */
393
+ /** Accessible label applied as `aria-label`. */
394
394
  label?: string;
395
395
  /** Optional badge element (e.g. {@link Count}) rendered at the top-end corner of the icon. */
396
396
  badge?: React_2.ReactNode;
@@ -403,10 +403,6 @@ export declare interface BottomNavigationProps extends React_2.HTMLAttributes<HT
403
403
  value?: string;
404
404
  /** Called when the selected action changes. */
405
405
  onValueChange?: (value: string) => void;
406
- /** When `true`, labels are only shown on the active action. @default false */
407
- showLabelsOnlyWhenActive?: boolean;
408
- /** When `true`, all labels are hidden. @default false */
409
- hideLabels?: boolean;
410
406
  /** When `true`, the navigation bar is hidden on viewports wider than `md` (768 px). @default false */
411
407
  hideOnDesktop?: boolean;
412
408
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fanvue/ui",
3
- "version": "1.18.1",
3
+ "version": "1.18.2",
4
4
  "description": "React component library built with Tailwind CSS for Fanvue ecosystem",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org",