@bubo-squared/ui-framework 0.2.35 → 0.2.37

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.
package/dist/index.d.cts CHANGED
@@ -365,34 +365,24 @@ declare const TextInput: React$1.ForwardRefExoticComponent<TextInputProps & Reac
365
365
  interface AutocompleteProps extends Omit<React$1.InputHTMLAttributes<HTMLInputElement>, "size" | "disabled" | "value" | "defaultValue" | "onChange"> {
366
366
  label?: React$1.ReactNode;
367
367
  hint?: React$1.ReactNode;
368
- /** If true, the hint will not be rendered even if provided. */
369
368
  hideHint?: boolean;
370
369
  status?: TextInputStatus;
371
370
  size?: TextInputSize;
372
371
  disabled?: boolean;
373
372
  leadingIcon?: React$1.ReactNode | null;
374
373
  trailingIcon?: React$1.ReactNode | null;
375
- /** Options to render (assumed already server-filtered). */
376
374
  options: string[];
377
375
  loading?: boolean;
378
376
  loadingText?: React$1.ReactNode;
379
377
  noOptionsText?: React$1.ReactNode;
380
- /** Committed value (e.g. selected option). */
381
378
  value?: string;
382
379
  defaultValue?: string;
383
380
  onChange?: (value: string) => void;
384
- /** Current input text (used as query). */
385
381
  inputValue?: string;
386
382
  defaultInputValue?: string;
387
383
  onInputChange?: (value: string) => void;
388
- /**
389
- * If true, free-typed input is also treated as the committed value.
390
- * Selecting an option from the dropdown still overrides the value.
391
- */
392
384
  freeSolo?: boolean;
393
- /** Optional className for the dropdown (options container). */
394
385
  dropdownClassName?: string;
395
- /** Optional className for the listbox (<ul>). */
396
386
  listboxClassName?: string;
397
387
  }
398
388
  declare const Autocomplete: React$1.ForwardRefExoticComponent<AutocompleteProps & React$1.RefAttributes<HTMLInputElement>>;
package/dist/index.d.ts CHANGED
@@ -365,34 +365,24 @@ declare const TextInput: React$1.ForwardRefExoticComponent<TextInputProps & Reac
365
365
  interface AutocompleteProps extends Omit<React$1.InputHTMLAttributes<HTMLInputElement>, "size" | "disabled" | "value" | "defaultValue" | "onChange"> {
366
366
  label?: React$1.ReactNode;
367
367
  hint?: React$1.ReactNode;
368
- /** If true, the hint will not be rendered even if provided. */
369
368
  hideHint?: boolean;
370
369
  status?: TextInputStatus;
371
370
  size?: TextInputSize;
372
371
  disabled?: boolean;
373
372
  leadingIcon?: React$1.ReactNode | null;
374
373
  trailingIcon?: React$1.ReactNode | null;
375
- /** Options to render (assumed already server-filtered). */
376
374
  options: string[];
377
375
  loading?: boolean;
378
376
  loadingText?: React$1.ReactNode;
379
377
  noOptionsText?: React$1.ReactNode;
380
- /** Committed value (e.g. selected option). */
381
378
  value?: string;
382
379
  defaultValue?: string;
383
380
  onChange?: (value: string) => void;
384
- /** Current input text (used as query). */
385
381
  inputValue?: string;
386
382
  defaultInputValue?: string;
387
383
  onInputChange?: (value: string) => void;
388
- /**
389
- * If true, free-typed input is also treated as the committed value.
390
- * Selecting an option from the dropdown still overrides the value.
391
- */
392
384
  freeSolo?: boolean;
393
- /** Optional className for the dropdown (options container). */
394
385
  dropdownClassName?: string;
395
- /** Optional className for the listbox (<ul>). */
396
386
  listboxClassName?: string;
397
387
  }
398
388
  declare const Autocomplete: React$1.ForwardRefExoticComponent<AutocompleteProps & React$1.RefAttributes<HTMLInputElement>>;
package/dist/index.js CHANGED
@@ -498,12 +498,8 @@ var Accordion = React7.forwardRef(
498
498
  bordered = false,
499
499
  ...rootProps
500
500
  } = props;
501
- const {
502
- value,
503
- defaultValue,
504
- onValueChange,
505
- ...restRootProps
506
- } = rootProps;
501
+ const { value, defaultValue, onValueChange, ...restRootProps } = rootProps;
502
+ const [isDoneAnimating, setIsDoneAnimating] = React7.useState(defaultOpen);
507
503
  const resolvedDefaultValue = value === void 0 && defaultValue === void 0 && defaultOpen ? "item" : defaultValue;
508
504
  return /* @__PURE__ */ jsx9(
509
505
  AccordionPrimitive.Root,
@@ -537,7 +533,18 @@ var Accordion = React7.forwardRef(
537
533
  ]
538
534
  }
539
535
  ) }),
540
- /* @__PURE__ */ jsx9(AccordionPrimitive.Content, { className: "data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden", children: /* @__PURE__ */ jsx9("div", { className: "pb-3", children }) })
536
+ /* @__PURE__ */ jsx9(
537
+ AccordionPrimitive.Content,
538
+ {
539
+ onAnimationStart: () => setIsDoneAnimating(false),
540
+ onAnimationEnd: () => setIsDoneAnimating(true),
541
+ className: cn(
542
+ "data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
543
+ !isDoneAnimating && "overflow-hidden"
544
+ ),
545
+ children: /* @__PURE__ */ jsx9("div", { className: "pb-3", children })
546
+ }
547
+ )
541
548
  ]
542
549
  }
543
550
  )
@@ -1653,6 +1660,7 @@ Checkbox.displayName = "Checkbox";
1653
1660
 
1654
1661
  // src/components/Inputs/Autocomplete.tsx
1655
1662
  import * as React24 from "react";
1663
+ import * as ReactDOM from "react-dom";
1656
1664
  import { cva as cva15 } from "class-variance-authority";
1657
1665
 
1658
1666
  // src/components/Inputs/InputShell.tsx
@@ -1739,9 +1747,7 @@ var inputTextVariants = cva15("truncate", {
1739
1747
  xl: "h6-title"
1740
1748
  }
1741
1749
  },
1742
- defaultVariants: {
1743
- size: "lg"
1744
- }
1750
+ defaultVariants: { size: "lg" }
1745
1751
  });
1746
1752
  var optionVariants = cva15(
1747
1753
  "w-full text-left hover:bg-(--background-secondary)",
@@ -1753,14 +1759,9 @@ var optionVariants = cva15(
1753
1759
  lg: "paragraph-lg py-(--space-8) ",
1754
1760
  xl: "subtitle py-(--space-10) "
1755
1761
  },
1756
- active: {
1757
- true: "bg-(--background-secondary)"
1758
- }
1762
+ active: { true: "bg-(--background-secondary)" }
1759
1763
  },
1760
- defaultVariants: {
1761
- size: "lg",
1762
- active: false
1763
- }
1764
+ defaultVariants: { size: "lg", active: false }
1764
1765
  }
1765
1766
  );
1766
1767
  var iconWrapperVariants = cva15(
@@ -1773,13 +1774,9 @@ var iconWrapperVariants = cva15(
1773
1774
  lg: "size-5 [&>svg]:size-5",
1774
1775
  xl: "size-6 [&>svg]:size-6"
1775
1776
  },
1776
- disabled: {
1777
- true: "text-(--icon-primary-disabled)"
1778
- }
1777
+ disabled: { true: "text-(--icon-primary-disabled)" }
1779
1778
  },
1780
- defaultVariants: {
1781
- size: "lg"
1782
- }
1779
+ defaultVariants: { size: "lg" }
1783
1780
  }
1784
1781
  );
1785
1782
  var Autocomplete = React24.forwardRef((props, forwardedRef) => {
@@ -1814,84 +1811,68 @@ var Autocomplete = React24.forwardRef((props, forwardedRef) => {
1814
1811
  ...inputProps
1815
1812
  } = props;
1816
1813
  const isValueControlled = value !== void 0;
1817
- const [internalValue, setInternalValue] = React24.useState(
1818
- defaultValue ?? ""
1819
- );
1814
+ const [internalValue, setInternalValue] = React24.useState(defaultValue ?? "");
1820
1815
  const isInputControlled = inputValue !== void 0;
1821
- const [internalInputValue, setInternalInputValue] = React24.useState(
1822
- defaultInputValue ?? ""
1823
- );
1816
+ const [internalInputValue, setInternalInputValue] = React24.useState(defaultInputValue ?? "");
1824
1817
  const [isFocused, setIsFocused] = React24.useState(false);
1825
1818
  const [activeIndex, setActiveIndex] = React24.useState(-1);
1819
+ const [dropdownStyle, setDropdownStyle] = React24.useState({});
1826
1820
  const inputRef = React24.useRef(null);
1827
- const setInputRef = React24.useCallback(
1828
- (node) => {
1829
- inputRef.current = node;
1830
- if (!forwardedRef) return;
1831
- if (typeof forwardedRef === "function") {
1832
- forwardedRef(node);
1833
- } else {
1834
- forwardedRef.current = node;
1835
- }
1836
- },
1837
- [forwardedRef]
1838
- );
1821
+ const anchorRef = React24.useRef(null);
1822
+ const rafIdRef = React24.useRef(null);
1839
1823
  const baseId = React24.useId();
1840
1824
  const inputId = id ?? baseId;
1841
1825
  const listboxId = `${inputId}-listbox`;
1842
1826
  const currentValue = (isValueControlled ? value : internalValue) ?? "";
1843
1827
  const currentInput = (isInputControlled ? inputValue : internalInputValue) ?? "";
1844
- React24.useEffect(() => {
1845
- if (isFocused) return;
1846
- if (isInputControlled) return;
1847
- if (!isValueControlled) return;
1848
- setInternalInputValue(currentValue);
1849
- }, [currentValue, isFocused, isInputControlled, isValueControlled]);
1850
- const showDropdown = isFocused && (loading || options.length > 0 || currentInput.trim().length > 0);
1828
+ const trimmedInput = currentInput.trim();
1829
+ const showDropdown = isFocused && (loading || options.length > 0 || trimmedInput.length > 0);
1851
1830
  const setInputText = (next) => {
1852
- if (!isInputControlled) {
1853
- setInternalInputValue(next);
1854
- }
1831
+ if (!isInputControlled) setInternalInputValue(next);
1855
1832
  onInputChange?.(next);
1856
1833
  };
1857
1834
  const commitTypedValue = (next) => {
1858
- if (!isValueControlled) {
1859
- setInternalValue(next);
1860
- }
1835
+ if (!isValueControlled) setInternalValue(next);
1861
1836
  onChange?.(next);
1862
1837
  };
1863
1838
  const commitValue = (next) => {
1864
- if (!isValueControlled) {
1865
- setInternalValue(next);
1866
- }
1839
+ if (!isValueControlled) setInternalValue(next);
1867
1840
  onChange?.(next);
1868
1841
  setInputText(next);
1869
1842
  setActiveIndex(-1);
1870
1843
  };
1871
- const handleContainerClick = () => {
1872
- if (disabled) return;
1873
- inputRef.current?.focus();
1874
- };
1844
+ const setInputRef = React24.useCallback((node) => {
1845
+ inputRef.current = node;
1846
+ if (!forwardedRef) return;
1847
+ if (typeof forwardedRef === "function") forwardedRef(node);
1848
+ else forwardedRef.current = node;
1849
+ }, [forwardedRef]);
1850
+ const updateDropdownPosition = React24.useCallback(() => {
1851
+ if (rafIdRef.current !== null) return;
1852
+ rafIdRef.current = requestAnimationFrame(() => {
1853
+ rafIdRef.current = null;
1854
+ if (!anchorRef.current) return;
1855
+ const rect = anchorRef.current.getBoundingClientRect();
1856
+ setDropdownStyle({
1857
+ position: "fixed",
1858
+ top: rect.bottom,
1859
+ left: rect.left,
1860
+ width: rect.width,
1861
+ zIndex: 9999
1862
+ });
1863
+ });
1864
+ }, []);
1875
1865
  const handleInputChange = (event) => {
1876
1866
  const next = event.target.value;
1877
1867
  setInputText(next);
1878
1868
  setActiveIndex(-1);
1879
- if (freeSolo) {
1880
- commitTypedValue(next);
1881
- }
1882
- };
1883
- const handleFocus = (event) => {
1884
- setIsFocused(true);
1885
- onFocus?.(event);
1869
+ if (freeSolo) commitTypedValue(next);
1886
1870
  };
1887
1871
  const handleBlur = (event) => {
1888
1872
  setIsFocused(false);
1889
1873
  setActiveIndex(-1);
1890
- if (freeSolo) {
1891
- const trimmed = currentInput.trim();
1892
- if (trimmed.length > 0 && currentInput !== currentValue) {
1893
- commitTypedValue(currentInput);
1894
- }
1874
+ if (freeSolo && trimmedInput.length > 0 && currentInput !== currentValue) {
1875
+ commitTypedValue(currentInput);
1895
1876
  }
1896
1877
  onBlur?.(event);
1897
1878
  };
@@ -1903,64 +1884,64 @@ var Autocomplete = React24.forwardRef((props, forwardedRef) => {
1903
1884
  return;
1904
1885
  }
1905
1886
  switch (event.key) {
1906
- case "ArrowDown": {
1887
+ case "ArrowDown":
1907
1888
  event.preventDefault();
1908
- setActiveIndex((prev) => {
1909
- if (options.length === 0) return -1;
1910
- const next = prev < 0 ? 0 : Math.min(prev + 1, options.length - 1);
1911
- return next;
1912
- });
1889
+ setActiveIndex((prev) => options.length === 0 ? -1 : prev < 0 ? 0 : Math.min(prev + 1, options.length - 1));
1913
1890
  break;
1914
- }
1915
- case "ArrowUp": {
1891
+ case "ArrowUp":
1916
1892
  event.preventDefault();
1917
- setActiveIndex((prev) => {
1918
- if (options.length === 0) return -1;
1919
- const next = prev <= 0 ? 0 : prev - 1;
1920
- return next;
1921
- });
1893
+ setActiveIndex((prev) => options.length === 0 ? -1 : prev <= 0 ? 0 : prev - 1);
1922
1894
  break;
1923
- }
1924
- case "Enter": {
1895
+ case "Enter":
1925
1896
  if (activeIndex >= 0 && activeIndex < options.length) {
1926
1897
  event.preventDefault();
1927
1898
  commitValue(options[activeIndex]);
1928
1899
  setIsFocused(false);
1929
- break;
1930
- }
1931
- if (freeSolo) {
1932
- const trimmed = currentInput.trim();
1933
- if (trimmed.length > 0) {
1934
- event.preventDefault();
1935
- if (currentInput !== currentValue) {
1936
- commitTypedValue(currentInput);
1937
- }
1938
- setIsFocused(false);
1939
- }
1900
+ } else if (freeSolo && trimmedInput.length > 0) {
1901
+ event.preventDefault();
1902
+ if (currentInput !== currentValue) commitTypedValue(currentInput);
1903
+ setIsFocused(false);
1940
1904
  }
1941
1905
  break;
1942
- }
1943
- case "Escape": {
1906
+ case "Escape":
1944
1907
  event.preventDefault();
1945
1908
  setIsFocused(false);
1946
1909
  setActiveIndex(-1);
1947
1910
  break;
1948
- }
1949
- default:
1950
- break;
1951
1911
  }
1952
1912
  };
1953
- const handleOptionMouseDown = (event) => {
1954
- event.preventDefault();
1913
+ const handleOptionMouseEnter = (event) => {
1914
+ const index = Number(event.currentTarget.dataset.index);
1915
+ if (!isNaN(index)) setActiveIndex(index);
1955
1916
  };
1956
- const handleOptionClick = (option) => {
1957
- commitValue(option);
1958
- setIsFocused(false);
1917
+ const handleOptionClick = (event) => {
1918
+ const index = Number(event.currentTarget.dataset.index);
1919
+ if (index >= 0 && index < options.length) {
1920
+ commitValue(options[index]);
1921
+ setIsFocused(false);
1922
+ }
1959
1923
  };
1960
- const activeDescendantId = activeIndex >= 0 ? `${inputId}-option-${activeIndex}` : void 0;
1961
- const showLeadingIcon = !!leadingIcon;
1962
- const showTrailingIcon = !!trailingIcon;
1963
- return /* @__PURE__ */ jsx26(Field, { label, hint, hideHint, status, disabled, children: /* @__PURE__ */ jsxs14("div", { className: "relative w-full", children: [
1924
+ const handleContainerClick = () => {
1925
+ if (disabled) return;
1926
+ inputRef.current?.focus();
1927
+ };
1928
+ React24.useLayoutEffect(() => {
1929
+ if (!showDropdown) return;
1930
+ updateDropdownPosition();
1931
+ window.addEventListener("scroll", updateDropdownPosition, { capture: true, passive: true });
1932
+ window.addEventListener("resize", updateDropdownPosition);
1933
+ return () => {
1934
+ window.removeEventListener("scroll", updateDropdownPosition, { capture: true });
1935
+ window.removeEventListener("resize", updateDropdownPosition);
1936
+ if (rafIdRef.current !== null) cancelAnimationFrame(rafIdRef.current);
1937
+ };
1938
+ }, [showDropdown, updateDropdownPosition]);
1939
+ React24.useEffect(() => {
1940
+ if (!isFocused && !isInputControlled && isValueControlled) {
1941
+ setInternalInputValue(currentValue);
1942
+ }
1943
+ }, [currentValue, isFocused, isInputControlled, isValueControlled]);
1944
+ return /* @__PURE__ */ jsx26(Field, { label, hint, hideHint, status, disabled, children: /* @__PURE__ */ jsxs14("div", { ref: anchorRef, className: "relative w-full", children: [
1964
1945
  /* @__PURE__ */ jsxs14(
1965
1946
  InputShell,
1966
1947
  {
@@ -1970,7 +1951,7 @@ var Autocomplete = React24.forwardRef((props, forwardedRef) => {
1970
1951
  className,
1971
1952
  onClick: handleContainerClick,
1972
1953
  children: [
1973
- showLeadingIcon && /* @__PURE__ */ jsx26("span", { className: cn(iconWrapperVariants({ size, disabled: !!disabled })), children: leadingIcon }),
1954
+ leadingIcon && /* @__PURE__ */ jsx26("span", { className: cn(iconWrapperVariants({ size, disabled: !!disabled })), children: leadingIcon }),
1974
1955
  /* @__PURE__ */ jsx26(
1975
1956
  Input,
1976
1957
  {
@@ -1981,72 +1962,53 @@ var Autocomplete = React24.forwardRef((props, forwardedRef) => {
1981
1962
  placeholder,
1982
1963
  value: currentInput,
1983
1964
  onChange: handleInputChange,
1984
- onFocus: handleFocus,
1965
+ onFocus: (e) => {
1966
+ setIsFocused(true);
1967
+ onFocus?.(e);
1968
+ },
1985
1969
  onBlur: handleBlur,
1986
1970
  onKeyDown: handleKeyDown,
1987
1971
  role: "combobox",
1988
1972
  "aria-autocomplete": "list",
1989
1973
  "aria-controls": listboxId,
1990
1974
  "aria-expanded": showDropdown,
1991
- "aria-activedescendant": activeDescendantId,
1975
+ "aria-activedescendant": activeIndex >= 0 ? `${inputId}-option-${activeIndex}` : void 0,
1992
1976
  variant: "bare",
1993
1977
  className: cn(inputTextVariants({ size }), "bg-transparent outline-none w-full"),
1994
1978
  ...inputProps
1995
1979
  }
1996
1980
  ),
1997
- showTrailingIcon && /* @__PURE__ */ jsx26("span", { className: cn(iconWrapperVariants({ size, disabled: !!disabled })), children: trailingIcon })
1981
+ trailingIcon && /* @__PURE__ */ jsx26("span", { className: cn(iconWrapperVariants({ size, disabled: !!disabled })), children: trailingIcon })
1998
1982
  ]
1999
1983
  }
2000
1984
  ),
2001
- showDropdown && /* @__PURE__ */ jsx26(
2002
- "div",
2003
- {
2004
- className: cn(
2005
- "absolute left-0 right-0 mt-1",
2006
- dropdownSurfaceClass,
2007
- dropdownScrollClass,
2008
- dropdownClassName
2009
- ),
2010
- children: loading ? /* @__PURE__ */ jsx26(
2011
- "div",
2012
- {
2013
- className: cn(optionVariants({ size }), "px-(--space-8) pr-(--space-16) text-secondary"),
2014
- "aria-live": "polite",
2015
- children: loadingText
2016
- }
2017
- ) : options.length === 0 ? /* @__PURE__ */ jsx26(
2018
- "div",
2019
- {
2020
- className: cn(optionVariants({ size }), "px-(--space-8) pr-(--space-16) text-secondary"),
2021
- "aria-live": "polite",
2022
- children: noOptionsText
2023
- }
2024
- ) : /* @__PURE__ */ jsx26(
2025
- "ul",
2026
- {
2027
- id: listboxId,
2028
- role: "listbox",
2029
- className: cn("flex flex-col", listboxClassName),
2030
- children: options.map((option, index) => /* @__PURE__ */ jsx26(
2031
- "li",
2032
- {
2033
- id: `${inputId}-option-${index}`,
2034
- role: "option",
2035
- "aria-selected": index === activeIndex,
2036
- className: cn(
2037
- optionVariants({ size, active: index === activeIndex }),
2038
- "px-(--space-8) pr-(--space-16) text-primary cursor-pointer"
2039
- ),
2040
- onMouseDown: handleOptionMouseDown,
2041
- onMouseEnter: () => setActiveIndex(index),
2042
- onClick: () => handleOptionClick(option),
2043
- children: option
2044
- },
2045
- `${option}-${index}`
2046
- ))
2047
- }
2048
- )
2049
- }
1985
+ showDropdown && ReactDOM.createPortal(
1986
+ /* @__PURE__ */ jsx26(
1987
+ "div",
1988
+ {
1989
+ style: dropdownStyle,
1990
+ className: cn(dropdownSurfaceClass, dropdownScrollClass, dropdownClassName),
1991
+ children: loading ? /* @__PURE__ */ jsx26("div", { className: cn(optionVariants({ size }), "px-(--space-8) pr-(--space-16) text-secondary"), "aria-live": "polite", children: loadingText }) : options.length === 0 ? /* @__PURE__ */ jsx26("div", { className: cn(optionVariants({ size }), "px-(--space-8) pr-(--space-16) text-secondary"), "aria-live": "polite", children: noOptionsText }) : /* @__PURE__ */ jsx26("ul", { id: listboxId, role: "listbox", className: cn("flex flex-col", listboxClassName), children: options.map((option, index) => /* @__PURE__ */ jsx26(
1992
+ "li",
1993
+ {
1994
+ id: `${inputId}-option-${index}`,
1995
+ role: "option",
1996
+ "aria-selected": index === activeIndex,
1997
+ "data-index": index,
1998
+ className: cn(
1999
+ optionVariants({ size, active: index === activeIndex }),
2000
+ "px-(--space-8) pr-(--space-16) text-primary cursor-pointer"
2001
+ ),
2002
+ onMouseDown: (e) => e.preventDefault(),
2003
+ onMouseEnter: handleOptionMouseEnter,
2004
+ onClick: handleOptionClick,
2005
+ children: option
2006
+ },
2007
+ `${option}-${index}`
2008
+ )) })
2009
+ }
2010
+ ),
2011
+ document.body
2050
2012
  )
2051
2013
  ] }) });
2052
2014
  });