@mirror-physics/fractal-ui 0.2.0-next.78 → 0.2.0-next.80

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
@@ -163,9 +163,9 @@ var colorPalette = {
163
163
  }
164
164
  };
165
165
  function getButtonStyles(buttonType, isHovered, isActive, disabled, color, hasIconOnly) {
166
- const palette = colorPalette[color];
166
+ const palette = color ? colorPalette[color] : void 0;
167
167
  const base = {
168
- borderRadius: hasIconOnly ? "var(--radius-lg, 8px)" : "var(--radius-full, 9999px)",
168
+ borderRadius: "var(--radius-sm, 4px)",
169
169
  cursor: disabled ? "not-allowed" : "pointer",
170
170
  transition: "background-color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease"
171
171
  };
@@ -179,22 +179,17 @@ function getButtonStyles(buttonType, isHovered, isActive, disabled, color, hasIc
179
179
  return { ...base, backgroundColor: "transparent", border: "1px solid var(--fg-disabled)", color: "var(--fg-disabled)" };
180
180
  }
181
181
  switch (buttonType) {
182
- // Primary with text: dark pill. Icon-only: color palette (Synapse style).
182
+ // Text and icon-only primary actions inherit the same scoped theme.
183
183
  case "primary":
184
- if (hasIconOnly) {
185
- return {
186
- ...base,
187
- backgroundColor: isActive ? palette[700] : isHovered ? palette[500] : palette[600],
188
- border: "1px solid transparent",
189
- color: "var(--fg-inverse)"
190
- };
191
- }
192
184
  return {
193
185
  ...base,
194
- backgroundColor: "var(--fg-primary)",
195
- border: "1px solid var(--fg-primary)",
196
- color: "var(--fg-inverse)",
197
- ...isHovered || isActive ? { opacity: 0.85 } : {}
186
+ border: "1px solid transparent",
187
+ ...palette ? {
188
+ "--button-primary-bg": palette[600],
189
+ "--button-primary-hover": palette[500],
190
+ "--button-primary-active": palette[700],
191
+ "--button-primary-fg": "var(--grey-0)"
192
+ } : {}
198
193
  };
199
194
  // Secondary: neutral. Rest=canvas, hover=surface-1 (darken), pressed=surface-2. Subtle border, dark text.
200
195
  case "secondary":
@@ -225,7 +220,7 @@ function getButtonStyles(buttonType, isHovered, isActive, disabled, color, hasIc
225
220
  }
226
221
  }
227
222
  var Button = React.forwardRef(
228
- ({ className, buttonType = "primary", buttonSize = "md", controlSize, color = "blue", icon, hasIconOnly, iconPosition = "left", style, disabled, children, ...props }, ref) => {
223
+ ({ className, buttonType = "primary", buttonSize = "lg", controlSize, color, icon, hasIconOnly, iconPosition = "left", style, disabled, children, ...props }, ref) => {
229
224
  const [isHovered, setIsHovered] = React.useState(false);
230
225
  const [isActive, setIsActive] = React.useState(false);
231
226
  const typeStyles = getButtonStyles(buttonType, isHovered, isActive, !!disabled, color, hasIconOnly);
@@ -770,16 +765,37 @@ PromptGrid.displayName = "PromptGrid";
770
765
  var React4 = __toESM(require("react"), 1);
771
766
  var import_jsx_runtime6 = require("react/jsx-runtime");
772
767
  var Input = React4.forwardRef(
773
- ({ className, type, controlSize = "md", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
774
- "input",
775
- {
776
- type,
777
- ref,
778
- "data-control-size": controlSize,
779
- className: cn("fractal-control", "fractal-input", className),
780
- ...props
781
- }
782
- )
768
+ ({ className, type, controlSize = "lg", fieldLabel, id, placeholder, ...props }, ref) => {
769
+ const generatedId = React4.useId();
770
+ const fieldId = id ?? generatedId;
771
+ const floatingLabel = type === "search" ? void 0 : fieldLabel;
772
+ const input = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
773
+ "input",
774
+ {
775
+ type,
776
+ ref,
777
+ id: floatingLabel ? fieldId : id,
778
+ placeholder: floatingLabel ? placeholder || " " : placeholder,
779
+ "data-control-size": controlSize,
780
+ className: cn("fractal-control", "fractal-input", className),
781
+ ...props,
782
+ "aria-label": props["aria-label"] ?? (type === "search" && !props["aria-labelledby"] ? fieldLabel ?? "Search" : void 0)
783
+ }
784
+ );
785
+ if (!floatingLabel) return input;
786
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
787
+ "div",
788
+ {
789
+ className: "fractal-floating-field",
790
+ "data-control-size": controlSize,
791
+ "data-always-float": ["date", "datetime-local", "time", "month", "week", "color", "file", "range"].includes(type ?? "") || void 0,
792
+ children: [
793
+ input,
794
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("label", { className: "fractal-floating-field__label", htmlFor: fieldId, children: fieldLabel })
795
+ ]
796
+ }
797
+ );
798
+ }
783
799
  );
784
800
  Input.displayName = "Input";
785
801
 
@@ -790,7 +806,7 @@ var import_jsx_runtime7 = require("react/jsx-runtime");
790
806
  var TableSearch = React5.forwardRef(
791
807
  ({
792
808
  searchType = "text",
793
- controlSize = "md",
809
+ controlSize = "lg",
794
810
  className,
795
811
  inputClassName,
796
812
  placeholder,
@@ -832,7 +848,7 @@ var import_fractal_icons5 = require("@mirror-physics/fractal-icons");
832
848
  var import_jsx_runtime8 = require("react/jsx-runtime");
833
849
  function NumberInput({
834
850
  unit,
835
- controlSize = "md",
851
+ controlSize = "lg",
836
852
  className,
837
853
  value,
838
854
  defaultValue,
@@ -845,7 +861,7 @@ function NumberInput({
845
861
  const inputRef = React6.useRef(null);
846
862
  const numericValue = Number(value ?? defaultValue);
847
863
  const characterCount = Math.max(String(value ?? defaultValue ?? "").length, 1);
848
- const controlLabel = typeof props["aria-label"] === "string" ? props["aria-label"] : "value";
864
+ const controlLabel = typeof props["aria-label"] === "string" ? props["aria-label"] : props.fieldLabel ?? "value";
849
865
  const decrementDisabled = disabled || readOnly || Number.isFinite(numericValue) && min !== void 0 && numericValue <= Number(min);
850
866
  const incrementDisabled = disabled || readOnly || Number.isFinite(numericValue) && max !== void 0 && numericValue >= Number(max);
851
867
  const changeByStep = (direction) => {
@@ -1041,7 +1057,7 @@ var import_jsx_runtime10 = require("react/jsx-runtime");
1041
1057
  var PasswordInput = React7.forwardRef(
1042
1058
  ({
1043
1059
  className,
1044
- controlSize = "md",
1060
+ controlSize = "lg",
1045
1061
  disabled,
1046
1062
  showPasswordLabel = "Show password",
1047
1063
  hidePasswordLabel = "Hide password",
@@ -1091,7 +1107,7 @@ PasswordInput.displayName = "PasswordInput";
1091
1107
  var React8 = __toESM(require("react"), 1);
1092
1108
  var import_jsx_runtime11 = require("react/jsx-runtime");
1093
1109
  var Label = React8.forwardRef(
1094
- ({ className, controlSize = "md", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1110
+ ({ className, controlSize = "lg", ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1095
1111
  "label",
1096
1112
  {
1097
1113
  ref,
@@ -1704,9 +1720,16 @@ function positionDropdown(trigger, panel, bounds, direction, align) {
1704
1720
  var import_jsx_runtime20 = require("react/jsx-runtime");
1705
1721
  function Dropdown({
1706
1722
  items,
1707
- selectedValue,
1723
+ selectedValue = "",
1708
1724
  onSelect,
1709
- triggerLabel = "Options",
1725
+ multiple = false,
1726
+ selectedValues = [],
1727
+ onSelectionChange,
1728
+ searchable = false,
1729
+ searchPlaceholder = "Find an option",
1730
+ emptyMessage = "No matching options.",
1731
+ triggerLabel,
1732
+ fieldLabel,
1710
1733
  direction = "down",
1711
1734
  align = "left",
1712
1735
  maxHeight = 320,
@@ -1716,7 +1739,7 @@ function Dropdown({
1716
1739
  label,
1717
1740
  iconOnly = false,
1718
1741
  noChevron = false,
1719
- size: sizeAlias = "md",
1742
+ size: sizeAlias = "lg",
1720
1743
  controlSize,
1721
1744
  triggerId,
1722
1745
  triggerVariant = "outline",
@@ -1726,17 +1749,37 @@ function Dropdown({
1726
1749
  closeOnSelect = true,
1727
1750
  disabled = false
1728
1751
  }) {
1752
+ const [query, setQuery] = React12.useState("");
1753
+ const searchRef = React12.useRef(null);
1754
+ const accessibleLabel = triggerLabel ?? fieldLabel ?? "Options";
1729
1755
  const size = controlSize ?? sizeAlias;
1730
1756
  const [open, setOpen] = React12.useState(false);
1757
+ React12.useEffect(() => {
1758
+ if (!open) setQuery("");
1759
+ }, [open]);
1731
1760
  const rootRef = React12.useRef(null);
1732
1761
  const menuId = React12.useId();
1733
1762
  const focusOnOpen = React12.useRef(false);
1734
1763
  const triggerRef = React12.useRef(null);
1735
1764
  const panelRef = React12.useRef(null);
1736
1765
  const [panelStyle, setPanelStyle] = React12.useState({});
1737
- const visibleItems = items.filter(
1766
+ const renderableItems = items.filter(
1738
1767
  (item) => item.icon != null || item.description != null && (typeof item.description === "string" ? item.description !== "" : true)
1739
1768
  );
1769
+ const normalizedQuery = query.trim().toLocaleLowerCase();
1770
+ let groupTitle = "";
1771
+ const matching = new Set(renderableItems.filter((item) => {
1772
+ if (item.kind === "header") {
1773
+ groupTitle = String(item.description ?? "");
1774
+ return false;
1775
+ }
1776
+ return `${groupTitle} ${typeof item.description === "string" ? item.description : item.value}`.toLocaleLowerCase().includes(normalizedQuery);
1777
+ }).map((item) => item.value));
1778
+ const visibleItems = renderableItems.filter((item, index) => {
1779
+ if (item.kind !== "header") return matching.has(item.value);
1780
+ const nextHeader = renderableItems.findIndex((next, i) => i > index && next.kind === "header");
1781
+ return renderableItems.slice(index + 1, nextHeader < 0 ? void 0 : nextHeader).some((next) => matching.has(next.value));
1782
+ });
1740
1783
  const selectableItems = visibleItems.filter((i) => i.kind !== "header");
1741
1784
  const selected = items.find((i) => i.value === selectedValue && i.kind !== "header") ?? selectableItems[0] ?? visibleItems[0];
1742
1785
  const ChevronIcon = direction === "up" ? import_fractal_icons10.ChevronUp : direction === "left" ? import_fractal_icons10.ChevronLeft : direction === "right" ? import_fractal_icons10.ChevronRight : import_fractal_icons10.ChevronDown;
@@ -1796,6 +1839,7 @@ function Dropdown({
1796
1839
  setPanelStyle((previous) => Object.entries(nextStyle).every(([key, value]) => previous[key] === value) ? previous : nextStyle);
1797
1840
  };
1798
1841
  updatePosition();
1842
+ if (searchable && !panel.contains(document.activeElement)) searchRef.current?.focus({ preventScroll: true });
1799
1843
  if (focusOnOpen.current) {
1800
1844
  focusOnOpen.current = false;
1801
1845
  const option = panel.querySelector('[aria-selected="true"]') ?? panel.querySelector('[role="option"]');
@@ -1819,7 +1863,7 @@ function Dropdown({
1819
1863
  window.visualViewport?.removeEventListener("resize", updatePosition);
1820
1864
  window.visualViewport?.removeEventListener("scroll", updatePosition);
1821
1865
  };
1822
- }, [open, direction, align, maxHeight, collisionPadding, items, label]);
1866
+ }, [open, direction, align, maxHeight, collisionPadding, items, label, query, searchable]);
1823
1867
  React12.useEffect(() => {
1824
1868
  if (!open) return;
1825
1869
  const handleClickOutside = (e) => {
@@ -1830,19 +1874,19 @@ function Dropdown({
1830
1874
  document.addEventListener("mousedown", handleClickOutside);
1831
1875
  return () => document.removeEventListener("mousedown", handleClickOutside);
1832
1876
  }, [open]);
1833
- if (visibleItems.length === 0) return null;
1834
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { ref: rootRef, "data-control-size": size, className: cn("fractal-dropdown", className), children: [
1877
+ if (renderableItems.length === 0) return null;
1878
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { ref: rootRef, "data-control-size": size, "data-filled": multiple ? selectedValues.length > 0 : Boolean(selectedValue), "data-open": open, className: cn("fractal-dropdown", fieldLabel && "fractal-floating-field fractal-floating-field--dropdown", className), children: [
1835
1879
  /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1836
1880
  "button",
1837
1881
  {
1838
1882
  ref: triggerRef,
1839
- id: triggerId,
1883
+ id: triggerId ?? `${menuId}-trigger`,
1840
1884
  "data-control-size": size,
1841
1885
  type: "button",
1842
1886
  disabled,
1843
- "aria-haspopup": "listbox",
1887
+ "aria-haspopup": searchable ? "dialog" : "listbox",
1844
1888
  "aria-expanded": open,
1845
- "aria-controls": open ? menuId : void 0,
1889
+ "aria-controls": open ? searchable ? menuId : `${menuId}-options` : void 0,
1846
1890
  onKeyDown: (event) => {
1847
1891
  if (event.key === "ArrowDown" || event.key === "ArrowUp") {
1848
1892
  event.preventDefault();
@@ -1856,7 +1900,7 @@ function Dropdown({
1856
1900
  }
1857
1901
  }
1858
1902
  },
1859
- "aria-label": triggerLabel,
1903
+ "aria-label": accessibleLabel,
1860
1904
  onClick: () => setOpen((o) => !o),
1861
1905
  className: cn(
1862
1906
  "fractal-control",
@@ -1868,20 +1912,22 @@ function Dropdown({
1868
1912
  triggerClassName
1869
1913
  ),
1870
1914
  children: [
1871
- triggerContent != null ? triggerContent : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
1872
- selected.icon != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-icon fractal-dropdown-icon--trigger", children: selected.icon }),
1873
- selected.description != null && (typeof selected.description !== "string" || selected.description !== "") && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-selected-text", children: selected.description })
1874
- ] }),
1915
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: fieldLabel ? "fractal-dropdown-field-value" : void 0, style: fieldLabel ? void 0 : { display: "contents" }, children: triggerContent != null ? triggerContent : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
1916
+ selected?.icon != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-icon fractal-dropdown-icon--trigger", children: selected?.icon }),
1917
+ selected?.description != null && (typeof selected?.description !== "string" || selected?.description !== "") && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-selected-text", children: selected?.description })
1918
+ ] }) }),
1875
1919
  !noChevron && !iconOnly && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-chevron", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ChevronIcon, { size: size === "sm" ? 14 : 16 }) })
1876
1920
  ]
1877
1921
  }
1878
1922
  ),
1923
+ fieldLabel ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("label", { className: "fractal-floating-field__label", htmlFor: triggerId ?? `${menuId}-trigger`, children: fieldLabel }) : null,
1879
1924
  open && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1880
1925
  "div",
1881
1926
  {
1882
1927
  ref: panelRef,
1883
1928
  id: menuId,
1884
1929
  onKeyDown: (event) => {
1930
+ if (event.target === searchRef.current && event.key !== "ArrowDown") return;
1885
1931
  if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
1886
1932
  event.preventDefault();
1887
1933
  const options = Array.from(panelRef.current?.querySelectorAll('[role="option"]') ?? []);
@@ -1890,49 +1936,58 @@ function Dropdown({
1890
1936
  options[index]?.focus({ preventScroll: true });
1891
1937
  options[index]?.scrollIntoView({ block: "nearest" });
1892
1938
  },
1893
- role: "listbox",
1894
- "aria-label": triggerLabel,
1939
+ role: searchable ? "dialog" : void 0,
1940
+ "aria-label": accessibleLabel,
1895
1941
  className: cn("fractal-dropdown-panel", `fractal-dropdown-panel--${size}`, panelClassName),
1896
1942
  style: panelStyle,
1897
1943
  children: [
1898
- label != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "fractal-dropdown-label", children: label }),
1899
- visibleItems.map((item, index) => {
1900
- const topDivider = item.border === "top" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "fractal-dropdown-separator" }) : null;
1901
- const bottomDivider = item.border === "bottom" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "fractal-dropdown-separator" }) : null;
1902
- if (item.kind === "header") {
1944
+ searchable && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "fractal-dropdown-search", children: [
1945
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_fractal_icons10.MagnifyingGlass, { "aria-hidden": "true" }),
1946
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("input", { ref: searchRef, type: "search", "aria-label": searchPlaceholder, placeholder: searchPlaceholder, value: query, onChange: (event) => setQuery(event.target.value) })
1947
+ ] }),
1948
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { role: "listbox", id: `${menuId}-options`, "aria-label": accessibleLabel, "aria-multiselectable": multiple || void 0, children: [
1949
+ label != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "fractal-dropdown-label", children: label }),
1950
+ visibleItems.map((item, index) => {
1951
+ const topDivider = item.border === "top" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "fractal-dropdown-separator" }) : null;
1952
+ const bottomDivider = item.border === "bottom" ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "fractal-dropdown-separator" }) : null;
1953
+ if (item.kind === "header") {
1954
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(React12.Fragment, { children: [
1955
+ topDivider,
1956
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { role: "presentation", className: "fractal-dropdown-label", children: item.description }),
1957
+ bottomDivider
1958
+ ] }, `header-${index}`);
1959
+ }
1960
+ const isSelected = multiple ? selectedValues.includes(item.value) : item.value === selectedValue;
1903
1961
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(React12.Fragment, { children: [
1904
1962
  topDivider,
1905
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { role: "presentation", className: "fractal-dropdown-label", children: item.description }),
1963
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1964
+ "button",
1965
+ {
1966
+ type: "button",
1967
+ role: "option",
1968
+ "aria-selected": isSelected,
1969
+ onClick: () => {
1970
+ if (multiple) onSelectionChange?.(isSelected ? selectedValues.filter((value) => value !== item.value) : [...selectedValues, item.value]);
1971
+ else onSelect?.(item.value);
1972
+ if (!multiple && closeOnSelect) {
1973
+ setOpen(false);
1974
+ triggerRef.current?.focus();
1975
+ }
1976
+ },
1977
+ className: "fractal-dropdown-option",
1978
+ children: [
1979
+ multiple && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-checkbox", "data-checked": isSelected, "aria-hidden": "true", children: isSelected && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_fractal_icons10.Check, {}) }),
1980
+ item.icon != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-icon", children: item.icon }),
1981
+ item.description != null && (typeof item.description !== "string" || item.description !== "") && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-option-text", children: item.description }),
1982
+ item.trailing != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-trailing", children: item.trailing })
1983
+ ]
1984
+ }
1985
+ ),
1906
1986
  bottomDivider
1907
- ] }, `header-${index}`);
1908
- }
1909
- const isSelected = item.value === selectedValue;
1910
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(React12.Fragment, { children: [
1911
- topDivider,
1912
- /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
1913
- "button",
1914
- {
1915
- type: "button",
1916
- role: "option",
1917
- "aria-selected": isSelected,
1918
- onClick: () => {
1919
- onSelect(item.value);
1920
- if (closeOnSelect) {
1921
- setOpen(false);
1922
- triggerRef.current?.focus();
1923
- }
1924
- },
1925
- className: "fractal-dropdown-option",
1926
- children: [
1927
- item.icon != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-icon", children: item.icon }),
1928
- item.description != null && (typeof item.description !== "string" || item.description !== "") && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-option-text", children: item.description }),
1929
- item.trailing != null && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "fractal-dropdown-trailing", children: item.trailing })
1930
- ]
1931
- }
1932
- ),
1933
- bottomDivider
1934
- ] }, item.value);
1935
- })
1987
+ ] }, item.value);
1988
+ })
1989
+ ] }),
1990
+ !selectableItems.length && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { role: "status", className: "fractal-dropdown-label", children: emptyMessage })
1936
1991
  ]
1937
1992
  }
1938
1993
  )
@@ -2663,7 +2718,7 @@ function Ghost({ className, width, height, radius, style, ...props }) {
2663
2718
  function GhostLine({ className, size = "md", width = "100%", ...props }) {
2664
2719
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { className: cn("fractal-ghost-line", `fractal-ghost-line--${size}`, className), width, ...props });
2665
2720
  }
2666
- function ButtonGhost({ className, size = "md", controlSize, iconOnly = false, width, ...props }) {
2721
+ function ButtonGhost({ className, size = "lg", controlSize, iconOnly = false, width, ...props }) {
2667
2722
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { "data-control-size": controlSize ?? size, className: cn("fractal-ghost-button", `fractal-ghost-button--${size}`, iconOnly && "fractal-ghost-button--icon-only", className), width, ...props });
2668
2723
  }
2669
2724
  function CardGhost({ className, lines = 3, showHeader = true, ...props }) {
@@ -2697,7 +2752,7 @@ function TextareaGhost({ className, labelWidth = "26%", ...props }) {
2697
2752
  function LabelGhost({ className, width = "26%", ...props }) {
2698
2753
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { className: cn("fractal-ghost-label", className), size: "sm", width, ...props });
2699
2754
  }
2700
- function FormFieldGhost({ className, labelWidth = "26%", multiline = false, controlSize = "md", ...props }) {
2755
+ function FormFieldGhost({ className, labelWidth = "26%", multiline = false, controlSize = "lg", ...props }) {
2701
2756
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { "aria-hidden": "true", "data-control-size": controlSize, className: cn("fractal-ghost-field", className), ...props, children: [
2702
2757
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(LabelGhost, { width: labelWidth }),
2703
2758
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { className: cn("fractal-ghost-field__control", multiline && "fractal-ghost-field__control--multiline") })
@@ -2723,7 +2778,7 @@ function LinkGhost({ className, width = 96, ...props }) {
2723
2778
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { className: cn("fractal-ghost-link", className), size: "sm", width, ...props });
2724
2779
  }
2725
2780
  var TextButtonGhost = LinkGhost;
2726
- function DropdownGhost({ className, controlSize = "md", ...props }) {
2781
+ function DropdownGhost({ className, controlSize = "lg", ...props }) {
2727
2782
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { "aria-hidden": "true", "data-control-size": controlSize, className: cn("fractal-ghost-dropdown", className), ...props, children: [
2728
2783
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GhostLine, { width: "38%" }),
2729
2784
  /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Ghost, { className: "fractal-ghost-dropdown__chevron" })
@@ -2961,7 +3016,25 @@ function SidePanel({ open, onClose, title, description, children, footer, size =
2961
3016
  var React18 = __toESM(require("react"), 1);
2962
3017
  var import_jsx_runtime33 = require("react/jsx-runtime");
2963
3018
  var Textarea = React18.forwardRef(
2964
- ({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("textarea", { ref, className: cn("fractal-textarea", className), ...props })
3019
+ ({ className, fieldLabel, textStyle = "source", id, placeholder, ...props }, ref) => {
3020
+ const generatedId = React18.useId();
3021
+ const fieldId = id ?? generatedId;
3022
+ const input = /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
3023
+ "textarea",
3024
+ {
3025
+ ref,
3026
+ id: fieldLabel ? fieldId : id,
3027
+ placeholder: fieldLabel ? placeholder || " " : placeholder,
3028
+ className: cn("fractal-textarea", className),
3029
+ "data-text-style": textStyle,
3030
+ ...props
3031
+ }
3032
+ );
3033
+ return fieldLabel ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "fractal-floating-field fractal-floating-field--textarea", "data-control-size": "md", children: [
3034
+ input,
3035
+ /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("label", { className: "fractal-floating-field__label", htmlFor: fieldId, children: fieldLabel })
3036
+ ] }) : input;
3037
+ }
2965
3038
  );
2966
3039
  Textarea.displayName = "Textarea";
2967
3040