@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.cjs CHANGED
@@ -581,12 +581,8 @@ var Accordion = React7.forwardRef(
581
581
  bordered = false,
582
582
  ...rootProps
583
583
  } = props;
584
- const {
585
- value,
586
- defaultValue,
587
- onValueChange,
588
- ...restRootProps
589
- } = rootProps;
584
+ const { value, defaultValue, onValueChange, ...restRootProps } = rootProps;
585
+ const [isDoneAnimating, setIsDoneAnimating] = React7.useState(defaultOpen);
590
586
  const resolvedDefaultValue = value === void 0 && defaultValue === void 0 && defaultOpen ? "item" : defaultValue;
591
587
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
592
588
  AccordionPrimitive.Root,
@@ -620,7 +616,18 @@ var Accordion = React7.forwardRef(
620
616
  ]
621
617
  }
622
618
  ) }),
623
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AccordionPrimitive.Content, { className: "data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pb-3", children }) })
619
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
620
+ AccordionPrimitive.Content,
621
+ {
622
+ onAnimationStart: () => setIsDoneAnimating(false),
623
+ onAnimationEnd: () => setIsDoneAnimating(true),
624
+ className: cn(
625
+ "data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down",
626
+ !isDoneAnimating && "overflow-hidden"
627
+ ),
628
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "pb-3", children })
629
+ }
630
+ )
624
631
  ]
625
632
  }
626
633
  )
@@ -1730,6 +1737,7 @@ Checkbox.displayName = "Checkbox";
1730
1737
 
1731
1738
  // src/components/Inputs/Autocomplete.tsx
1732
1739
  var React24 = __toESM(require("react"), 1);
1740
+ var ReactDOM = __toESM(require("react-dom"), 1);
1733
1741
  var import_class_variance_authority15 = require("class-variance-authority");
1734
1742
 
1735
1743
  // src/components/Inputs/InputShell.tsx
@@ -1816,9 +1824,7 @@ var inputTextVariants = (0, import_class_variance_authority15.cva)("truncate", {
1816
1824
  xl: "h6-title"
1817
1825
  }
1818
1826
  },
1819
- defaultVariants: {
1820
- size: "lg"
1821
- }
1827
+ defaultVariants: { size: "lg" }
1822
1828
  });
1823
1829
  var optionVariants = (0, import_class_variance_authority15.cva)(
1824
1830
  "w-full text-left hover:bg-(--background-secondary)",
@@ -1830,14 +1836,9 @@ var optionVariants = (0, import_class_variance_authority15.cva)(
1830
1836
  lg: "paragraph-lg py-(--space-8) ",
1831
1837
  xl: "subtitle py-(--space-10) "
1832
1838
  },
1833
- active: {
1834
- true: "bg-(--background-secondary)"
1835
- }
1839
+ active: { true: "bg-(--background-secondary)" }
1836
1840
  },
1837
- defaultVariants: {
1838
- size: "lg",
1839
- active: false
1840
- }
1841
+ defaultVariants: { size: "lg", active: false }
1841
1842
  }
1842
1843
  );
1843
1844
  var iconWrapperVariants = (0, import_class_variance_authority15.cva)(
@@ -1850,13 +1851,9 @@ var iconWrapperVariants = (0, import_class_variance_authority15.cva)(
1850
1851
  lg: "size-5 [&>svg]:size-5",
1851
1852
  xl: "size-6 [&>svg]:size-6"
1852
1853
  },
1853
- disabled: {
1854
- true: "text-(--icon-primary-disabled)"
1855
- }
1854
+ disabled: { true: "text-(--icon-primary-disabled)" }
1856
1855
  },
1857
- defaultVariants: {
1858
- size: "lg"
1859
- }
1856
+ defaultVariants: { size: "lg" }
1860
1857
  }
1861
1858
  );
1862
1859
  var Autocomplete = React24.forwardRef((props, forwardedRef) => {
@@ -1891,84 +1888,68 @@ var Autocomplete = React24.forwardRef((props, forwardedRef) => {
1891
1888
  ...inputProps
1892
1889
  } = props;
1893
1890
  const isValueControlled = value !== void 0;
1894
- const [internalValue, setInternalValue] = React24.useState(
1895
- defaultValue ?? ""
1896
- );
1891
+ const [internalValue, setInternalValue] = React24.useState(defaultValue ?? "");
1897
1892
  const isInputControlled = inputValue !== void 0;
1898
- const [internalInputValue, setInternalInputValue] = React24.useState(
1899
- defaultInputValue ?? ""
1900
- );
1893
+ const [internalInputValue, setInternalInputValue] = React24.useState(defaultInputValue ?? "");
1901
1894
  const [isFocused, setIsFocused] = React24.useState(false);
1902
1895
  const [activeIndex, setActiveIndex] = React24.useState(-1);
1896
+ const [dropdownStyle, setDropdownStyle] = React24.useState({});
1903
1897
  const inputRef = React24.useRef(null);
1904
- const setInputRef = React24.useCallback(
1905
- (node) => {
1906
- inputRef.current = node;
1907
- if (!forwardedRef) return;
1908
- if (typeof forwardedRef === "function") {
1909
- forwardedRef(node);
1910
- } else {
1911
- forwardedRef.current = node;
1912
- }
1913
- },
1914
- [forwardedRef]
1915
- );
1898
+ const anchorRef = React24.useRef(null);
1899
+ const rafIdRef = React24.useRef(null);
1916
1900
  const baseId = React24.useId();
1917
1901
  const inputId = id ?? baseId;
1918
1902
  const listboxId = `${inputId}-listbox`;
1919
1903
  const currentValue = (isValueControlled ? value : internalValue) ?? "";
1920
1904
  const currentInput = (isInputControlled ? inputValue : internalInputValue) ?? "";
1921
- React24.useEffect(() => {
1922
- if (isFocused) return;
1923
- if (isInputControlled) return;
1924
- if (!isValueControlled) return;
1925
- setInternalInputValue(currentValue);
1926
- }, [currentValue, isFocused, isInputControlled, isValueControlled]);
1927
- const showDropdown = isFocused && (loading || options.length > 0 || currentInput.trim().length > 0);
1905
+ const trimmedInput = currentInput.trim();
1906
+ const showDropdown = isFocused && (loading || options.length > 0 || trimmedInput.length > 0);
1928
1907
  const setInputText = (next) => {
1929
- if (!isInputControlled) {
1930
- setInternalInputValue(next);
1931
- }
1908
+ if (!isInputControlled) setInternalInputValue(next);
1932
1909
  onInputChange?.(next);
1933
1910
  };
1934
1911
  const commitTypedValue = (next) => {
1935
- if (!isValueControlled) {
1936
- setInternalValue(next);
1937
- }
1912
+ if (!isValueControlled) setInternalValue(next);
1938
1913
  onChange?.(next);
1939
1914
  };
1940
1915
  const commitValue = (next) => {
1941
- if (!isValueControlled) {
1942
- setInternalValue(next);
1943
- }
1916
+ if (!isValueControlled) setInternalValue(next);
1944
1917
  onChange?.(next);
1945
1918
  setInputText(next);
1946
1919
  setActiveIndex(-1);
1947
1920
  };
1948
- const handleContainerClick = () => {
1949
- if (disabled) return;
1950
- inputRef.current?.focus();
1951
- };
1921
+ const setInputRef = React24.useCallback((node) => {
1922
+ inputRef.current = node;
1923
+ if (!forwardedRef) return;
1924
+ if (typeof forwardedRef === "function") forwardedRef(node);
1925
+ else forwardedRef.current = node;
1926
+ }, [forwardedRef]);
1927
+ const updateDropdownPosition = React24.useCallback(() => {
1928
+ if (rafIdRef.current !== null) return;
1929
+ rafIdRef.current = requestAnimationFrame(() => {
1930
+ rafIdRef.current = null;
1931
+ if (!anchorRef.current) return;
1932
+ const rect = anchorRef.current.getBoundingClientRect();
1933
+ setDropdownStyle({
1934
+ position: "fixed",
1935
+ top: rect.bottom,
1936
+ left: rect.left,
1937
+ width: rect.width,
1938
+ zIndex: 9999
1939
+ });
1940
+ });
1941
+ }, []);
1952
1942
  const handleInputChange = (event) => {
1953
1943
  const next = event.target.value;
1954
1944
  setInputText(next);
1955
1945
  setActiveIndex(-1);
1956
- if (freeSolo) {
1957
- commitTypedValue(next);
1958
- }
1959
- };
1960
- const handleFocus = (event) => {
1961
- setIsFocused(true);
1962
- onFocus?.(event);
1946
+ if (freeSolo) commitTypedValue(next);
1963
1947
  };
1964
1948
  const handleBlur = (event) => {
1965
1949
  setIsFocused(false);
1966
1950
  setActiveIndex(-1);
1967
- if (freeSolo) {
1968
- const trimmed = currentInput.trim();
1969
- if (trimmed.length > 0 && currentInput !== currentValue) {
1970
- commitTypedValue(currentInput);
1971
- }
1951
+ if (freeSolo && trimmedInput.length > 0 && currentInput !== currentValue) {
1952
+ commitTypedValue(currentInput);
1972
1953
  }
1973
1954
  onBlur?.(event);
1974
1955
  };
@@ -1980,64 +1961,64 @@ var Autocomplete = React24.forwardRef((props, forwardedRef) => {
1980
1961
  return;
1981
1962
  }
1982
1963
  switch (event.key) {
1983
- case "ArrowDown": {
1964
+ case "ArrowDown":
1984
1965
  event.preventDefault();
1985
- setActiveIndex((prev) => {
1986
- if (options.length === 0) return -1;
1987
- const next = prev < 0 ? 0 : Math.min(prev + 1, options.length - 1);
1988
- return next;
1989
- });
1966
+ setActiveIndex((prev) => options.length === 0 ? -1 : prev < 0 ? 0 : Math.min(prev + 1, options.length - 1));
1990
1967
  break;
1991
- }
1992
- case "ArrowUp": {
1968
+ case "ArrowUp":
1993
1969
  event.preventDefault();
1994
- setActiveIndex((prev) => {
1995
- if (options.length === 0) return -1;
1996
- const next = prev <= 0 ? 0 : prev - 1;
1997
- return next;
1998
- });
1970
+ setActiveIndex((prev) => options.length === 0 ? -1 : prev <= 0 ? 0 : prev - 1);
1999
1971
  break;
2000
- }
2001
- case "Enter": {
1972
+ case "Enter":
2002
1973
  if (activeIndex >= 0 && activeIndex < options.length) {
2003
1974
  event.preventDefault();
2004
1975
  commitValue(options[activeIndex]);
2005
1976
  setIsFocused(false);
2006
- break;
2007
- }
2008
- if (freeSolo) {
2009
- const trimmed = currentInput.trim();
2010
- if (trimmed.length > 0) {
2011
- event.preventDefault();
2012
- if (currentInput !== currentValue) {
2013
- commitTypedValue(currentInput);
2014
- }
2015
- setIsFocused(false);
2016
- }
1977
+ } else if (freeSolo && trimmedInput.length > 0) {
1978
+ event.preventDefault();
1979
+ if (currentInput !== currentValue) commitTypedValue(currentInput);
1980
+ setIsFocused(false);
2017
1981
  }
2018
1982
  break;
2019
- }
2020
- case "Escape": {
1983
+ case "Escape":
2021
1984
  event.preventDefault();
2022
1985
  setIsFocused(false);
2023
1986
  setActiveIndex(-1);
2024
1987
  break;
2025
- }
2026
- default:
2027
- break;
2028
1988
  }
2029
1989
  };
2030
- const handleOptionMouseDown = (event) => {
2031
- event.preventDefault();
1990
+ const handleOptionMouseEnter = (event) => {
1991
+ const index = Number(event.currentTarget.dataset.index);
1992
+ if (!isNaN(index)) setActiveIndex(index);
2032
1993
  };
2033
- const handleOptionClick = (option) => {
2034
- commitValue(option);
2035
- setIsFocused(false);
1994
+ const handleOptionClick = (event) => {
1995
+ const index = Number(event.currentTarget.dataset.index);
1996
+ if (index >= 0 && index < options.length) {
1997
+ commitValue(options[index]);
1998
+ setIsFocused(false);
1999
+ }
2036
2000
  };
2037
- const activeDescendantId = activeIndex >= 0 ? `${inputId}-option-${activeIndex}` : void 0;
2038
- const showLeadingIcon = !!leadingIcon;
2039
- const showTrailingIcon = !!trailingIcon;
2040
- return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Field, { label, hint, hideHint, status, disabled, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "relative w-full", children: [
2001
+ const handleContainerClick = () => {
2002
+ if (disabled) return;
2003
+ inputRef.current?.focus();
2004
+ };
2005
+ React24.useLayoutEffect(() => {
2006
+ if (!showDropdown) return;
2007
+ updateDropdownPosition();
2008
+ window.addEventListener("scroll", updateDropdownPosition, { capture: true, passive: true });
2009
+ window.addEventListener("resize", updateDropdownPosition);
2010
+ return () => {
2011
+ window.removeEventListener("scroll", updateDropdownPosition, { capture: true });
2012
+ window.removeEventListener("resize", updateDropdownPosition);
2013
+ if (rafIdRef.current !== null) cancelAnimationFrame(rafIdRef.current);
2014
+ };
2015
+ }, [showDropdown, updateDropdownPosition]);
2016
+ React24.useEffect(() => {
2017
+ if (!isFocused && !isInputControlled && isValueControlled) {
2018
+ setInternalInputValue(currentValue);
2019
+ }
2020
+ }, [currentValue, isFocused, isInputControlled, isValueControlled]);
2021
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Field, { label, hint, hideHint, status, disabled, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { ref: anchorRef, className: "relative w-full", children: [
2041
2022
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
2042
2023
  InputShell,
2043
2024
  {
@@ -2047,7 +2028,7 @@ var Autocomplete = React24.forwardRef((props, forwardedRef) => {
2047
2028
  className,
2048
2029
  onClick: handleContainerClick,
2049
2030
  children: [
2050
- showLeadingIcon && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: cn(iconWrapperVariants({ size, disabled: !!disabled })), children: leadingIcon }),
2031
+ leadingIcon && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: cn(iconWrapperVariants({ size, disabled: !!disabled })), children: leadingIcon }),
2051
2032
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2052
2033
  Input,
2053
2034
  {
@@ -2058,72 +2039,53 @@ var Autocomplete = React24.forwardRef((props, forwardedRef) => {
2058
2039
  placeholder,
2059
2040
  value: currentInput,
2060
2041
  onChange: handleInputChange,
2061
- onFocus: handleFocus,
2042
+ onFocus: (e) => {
2043
+ setIsFocused(true);
2044
+ onFocus?.(e);
2045
+ },
2062
2046
  onBlur: handleBlur,
2063
2047
  onKeyDown: handleKeyDown,
2064
2048
  role: "combobox",
2065
2049
  "aria-autocomplete": "list",
2066
2050
  "aria-controls": listboxId,
2067
2051
  "aria-expanded": showDropdown,
2068
- "aria-activedescendant": activeDescendantId,
2052
+ "aria-activedescendant": activeIndex >= 0 ? `${inputId}-option-${activeIndex}` : void 0,
2069
2053
  variant: "bare",
2070
2054
  className: cn(inputTextVariants({ size }), "bg-transparent outline-none w-full"),
2071
2055
  ...inputProps
2072
2056
  }
2073
2057
  ),
2074
- showTrailingIcon && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: cn(iconWrapperVariants({ size, disabled: !!disabled })), children: trailingIcon })
2058
+ trailingIcon && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: cn(iconWrapperVariants({ size, disabled: !!disabled })), children: trailingIcon })
2075
2059
  ]
2076
2060
  }
2077
2061
  ),
2078
- showDropdown && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2079
- "div",
2080
- {
2081
- className: cn(
2082
- "absolute left-0 right-0 mt-1",
2083
- dropdownSurfaceClass,
2084
- dropdownScrollClass,
2085
- dropdownClassName
2086
- ),
2087
- children: loading ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2088
- "div",
2089
- {
2090
- className: cn(optionVariants({ size }), "px-(--space-8) pr-(--space-16) text-secondary"),
2091
- "aria-live": "polite",
2092
- children: loadingText
2093
- }
2094
- ) : options.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2095
- "div",
2096
- {
2097
- className: cn(optionVariants({ size }), "px-(--space-8) pr-(--space-16) text-secondary"),
2098
- "aria-live": "polite",
2099
- children: noOptionsText
2100
- }
2101
- ) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2102
- "ul",
2103
- {
2104
- id: listboxId,
2105
- role: "listbox",
2106
- className: cn("flex flex-col", listboxClassName),
2107
- children: options.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2108
- "li",
2109
- {
2110
- id: `${inputId}-option-${index}`,
2111
- role: "option",
2112
- "aria-selected": index === activeIndex,
2113
- className: cn(
2114
- optionVariants({ size, active: index === activeIndex }),
2115
- "px-(--space-8) pr-(--space-16) text-primary cursor-pointer"
2116
- ),
2117
- onMouseDown: handleOptionMouseDown,
2118
- onMouseEnter: () => setActiveIndex(index),
2119
- onClick: () => handleOptionClick(option),
2120
- children: option
2121
- },
2122
- `${option}-${index}`
2123
- ))
2124
- }
2125
- )
2126
- }
2062
+ showDropdown && ReactDOM.createPortal(
2063
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2064
+ "div",
2065
+ {
2066
+ style: dropdownStyle,
2067
+ className: cn(dropdownSurfaceClass, dropdownScrollClass, dropdownClassName),
2068
+ children: loading ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: cn(optionVariants({ size }), "px-(--space-8) pr-(--space-16) text-secondary"), "aria-live": "polite", children: loadingText }) : options.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: cn(optionVariants({ size }), "px-(--space-8) pr-(--space-16) text-secondary"), "aria-live": "polite", children: noOptionsText }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("ul", { id: listboxId, role: "listbox", className: cn("flex flex-col", listboxClassName), children: options.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
2069
+ "li",
2070
+ {
2071
+ id: `${inputId}-option-${index}`,
2072
+ role: "option",
2073
+ "aria-selected": index === activeIndex,
2074
+ "data-index": index,
2075
+ className: cn(
2076
+ optionVariants({ size, active: index === activeIndex }),
2077
+ "px-(--space-8) pr-(--space-16) text-primary cursor-pointer"
2078
+ ),
2079
+ onMouseDown: (e) => e.preventDefault(),
2080
+ onMouseEnter: handleOptionMouseEnter,
2081
+ onClick: handleOptionClick,
2082
+ children: option
2083
+ },
2084
+ `${option}-${index}`
2085
+ )) })
2086
+ }
2087
+ ),
2088
+ document.body
2127
2089
  )
2128
2090
  ] }) });
2129
2091
  });