@ecohouse/ui 0.1.53 → 0.1.54

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.js CHANGED
@@ -27555,7 +27555,7 @@ var require_react_dom_development = __commonJS({
27555
27555
  return root2;
27556
27556
  }
27557
27557
  var ReactVersion = "18.3.1";
27558
- function createPortal3(children, containerInfo, implementation) {
27558
+ function createPortal4(children, containerInfo, implementation) {
27559
27559
  var key = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : null;
27560
27560
  {
27561
27561
  checkKeyStringCoercion(key);
@@ -28411,7 +28411,7 @@ var require_react_dom_development = __commonJS({
28411
28411
  if (!isValidContainer(container)) {
28412
28412
  throw new Error("Target container is not a DOM element.");
28413
28413
  }
28414
- return createPortal3(children, container, null, key);
28414
+ return createPortal4(children, container, null, key);
28415
28415
  }
28416
28416
  function renderSubtreeIntoContainer(parentComponent, element, containerNode, callback) {
28417
28417
  return unstable_renderSubtreeIntoContainer(parentComponent, element, containerNode, callback);
@@ -36208,6 +36208,9 @@ var styles32 = StyleSheet.create({
36208
36208
  color: colors.red
36209
36209
  }
36210
36210
  });
36211
+
36212
+ // src/components/Select/Select.tsx
36213
+ var import_react_dom3 = __toESM(require_react_dom());
36211
36214
  var DEFAULT_WIDTH4 = 308;
36212
36215
  var TRIGGER_HEIGHT2 = 44;
36213
36216
  var TRIGGER_RADIUS2 = 60;
@@ -36217,6 +36220,9 @@ var ITEM_RADIUS2 = 12;
36217
36220
  var ICON_SIZE13 = 20;
36218
36221
  var CHIP_GAP = 6;
36219
36222
  var FALLBACK_ELLIPSIS_WIDTH = 20;
36223
+ var WEB_MENU_Z_INDEX = 600;
36224
+ var WEB_VIEWPORT_PADDING = 8;
36225
+ var WEB_MENU_GAP = 8;
36220
36226
  function itemBackground(state) {
36221
36227
  switch (state) {
36222
36228
  case "hover":
@@ -36363,6 +36369,7 @@ var Select = forwardRef(
36363
36369
  const valueProp = props.value;
36364
36370
  const rootRef = useRef(null);
36365
36371
  const triggerRef = useRef(null);
36372
+ const menuRef = useRef(null);
36366
36373
  const searchInputRef = useRef(null);
36367
36374
  const setRootRef = useMemo(() => mergeRefs(rootRef, forwardedRef), [forwardedRef]);
36368
36375
  const isOpenControlled = typeof openProp === "boolean";
@@ -36378,6 +36385,7 @@ var Select = forwardRef(
36378
36385
  const [hoveredValue, setHoveredValue] = useState(null);
36379
36386
  const [menuAlignEnd, setMenuAlignEnd] = useState(false);
36380
36387
  const [menuPlaceAbove, setMenuPlaceAbove] = useState(false);
36388
+ const [webMenuPosition, setWebMenuPosition] = useState(null);
36381
36389
  const selectedValues = useMemo(() => {
36382
36390
  const current = isValueControlled ? valueProp : multiple ? uncontrolledMultiple : uncontrolledSingle;
36383
36391
  return toArray(current);
@@ -36431,6 +36439,7 @@ var Select = forwardRef(
36431
36439
  setHoveredValue(null);
36432
36440
  setMenuAlignEnd(false);
36433
36441
  setMenuPlaceAbove(false);
36442
+ setWebMenuPosition(null);
36434
36443
  onSearchChange?.("");
36435
36444
  }
36436
36445
  },
@@ -36442,15 +36451,18 @@ var Select = forwardRef(
36442
36451
  const trigger = triggerRef.current;
36443
36452
  const rect = trigger?.getBoundingClientRect?.();
36444
36453
  if (!rect) return;
36445
- const viewportPadding = 8;
36446
- const gap = 8;
36447
36454
  const menuWidth = Math.max(rect.width, resolvedMenuWidth ?? 0);
36448
- const overflowsRight = rect.left + menuWidth > window.innerWidth - viewportPadding;
36455
+ const overflowsRight = rect.left + menuWidth > window.innerWidth - WEB_VIEWPORT_PADDING;
36449
36456
  setMenuAlignEnd(overflowsRight);
36450
- const spaceBelow = window.innerHeight - viewportPadding - rect.bottom;
36451
- const spaceAbove = rect.top - viewportPadding;
36452
- const needsAbove = estimatedMenuHeight + gap > spaceBelow && spaceAbove > spaceBelow;
36457
+ const spaceBelow = window.innerHeight - WEB_VIEWPORT_PADDING - rect.bottom;
36458
+ const spaceAbove = rect.top - WEB_VIEWPORT_PADDING;
36459
+ const needsAbove = estimatedMenuHeight + WEB_MENU_GAP > spaceBelow && spaceAbove > spaceBelow;
36453
36460
  setMenuPlaceAbove(needsAbove);
36461
+ const available = Math.max(0, (needsAbove ? spaceAbove : spaceBelow) - WEB_MENU_GAP);
36462
+ const maxHeight = Math.min(estimatedMenuHeight, Math.max(available, TRIGGER_HEIGHT2));
36463
+ const left = overflowsRight ? Math.max(WEB_VIEWPORT_PADDING, rect.right - menuWidth) : Math.min(rect.left, window.innerWidth - WEB_VIEWPORT_PADDING - menuWidth);
36464
+ const top = needsAbove ? Math.max(WEB_VIEWPORT_PADDING, rect.top - WEB_MENU_GAP - maxHeight) : rect.bottom + WEB_MENU_GAP;
36465
+ setWebMenuPosition({ top, left, width: menuWidth, maxHeight });
36454
36466
  };
36455
36467
  updateMenuPlacement();
36456
36468
  window.addEventListener("resize", updateMenuPlacement);
@@ -36467,11 +36479,12 @@ var Select = forwardRef(
36467
36479
  useEffect(() => {
36468
36480
  if (Platform.OS !== "web" || !isOpen) return;
36469
36481
  const handlePointerDown = (event) => {
36470
- const root = rootRef.current;
36471
36482
  const target = event.target;
36472
- if (root?.contains && target instanceof Node && !root.contains(target)) {
36473
- setOpen(false);
36474
- }
36483
+ if (!(target instanceof Node)) return;
36484
+ const root = rootRef.current;
36485
+ const menu = menuRef.current;
36486
+ if (root?.contains?.(target) || menu?.contains?.(target)) return;
36487
+ setOpen(false);
36475
36488
  };
36476
36489
  document.addEventListener("mousedown", handlePointerDown);
36477
36490
  document.addEventListener("touchstart", handlePointerDown);
@@ -36585,7 +36598,7 @@ var Select = forwardRef(
36585
36598
  ]);
36586
36599
  const leftIconNode = renderStatefulIcon(leftIcon, UserIcon, {
36587
36600
  size: ICON_SIZE13,
36588
- color: error ? colors.red : colors.primary
36601
+ color: error ? colors.red : isOpen ? colors.primary : colors.grey100
36589
36602
  });
36590
36603
  const hasLeftIcon = showLeftIcon || leftIcon != null;
36591
36604
  const triggerBorderColor = error ? colors.redMuted : isOpen ? colors.primaryMuted : colors.grey700;
@@ -36594,6 +36607,95 @@ var Select = forwardRef(
36594
36607
  const hintColor = error ? colors.red : colors.grey100;
36595
36608
  const singleLabel = selectedOptions[0]?.label ?? placeholder;
36596
36609
  const resolvedAccessibilityLabel = accessibilityLabel ?? (typeof label === "string" ? label : void 0);
36610
+ const optionListMaxHeight = Platform.OS === "web" && webMenuPosition != null ? Math.max(43, webMenuPosition.maxHeight - (24 + (showSearch ? 48 : 0))) : resolvedOptionListMaxHeight;
36611
+ const menuNode = isOpen ? /* @__PURE__ */ jsxs(
36612
+ View,
36613
+ {
36614
+ ref: menuRef,
36615
+ style: [
36616
+ styles33.menu,
36617
+ Platform.OS === "web" && webMenuPosition != null ? {
36618
+ position: "fixed",
36619
+ top: webMenuPosition.top,
36620
+ left: webMenuPosition.left,
36621
+ width: webMenuPosition.width,
36622
+ minWidth: webMenuPosition.width,
36623
+ maxHeight: webMenuPosition.maxHeight,
36624
+ zIndex: WEB_MENU_Z_INDEX,
36625
+ marginTop: 0,
36626
+ marginBottom: 0
36627
+ } : Platform.OS === "web" ? { position: "fixed", opacity: 0, pointerEvents: "none" } : [
36628
+ menuAlignEnd ? styles33.menuAlignEnd : styles33.menuAlignStart,
36629
+ menuPlaceAbove ? styles33.menuAbove : styles33.menuBelow
36630
+ ],
36631
+ menuStyle
36632
+ ],
36633
+ children: [
36634
+ showSearch ? /* @__PURE__ */ jsxs(View, { style: styles33.searchField, children: [
36635
+ /* @__PURE__ */ jsx(View, { style: styles33.iconSlot, children: /* @__PURE__ */ jsx(SearchIcon, { size: ICON_SIZE13, color: colors.grey100 }) }),
36636
+ /* @__PURE__ */ jsx(
36637
+ TextInput,
36638
+ {
36639
+ ref: searchInputRef,
36640
+ value: searchQuery,
36641
+ onChangeText: handleSearchChange,
36642
+ editable: !disabled,
36643
+ placeholder: searchPlaceholder,
36644
+ placeholderTextColor: colors.grey100,
36645
+ style: [
36646
+ styles33.searchInput,
36647
+ Platform.OS === "web" ? styles33.searchInputWeb : null,
36648
+ Platform.OS === "android" ? styles33.searchInputAndroid : null
36649
+ ],
36650
+ accessibilityLabel: searchPlaceholder
36651
+ }
36652
+ )
36653
+ ] }) : null,
36654
+ /* @__PURE__ */ jsxs(
36655
+ ScrollView,
36656
+ {
36657
+ style: [styles33.optionList, { maxHeight: optionListMaxHeight }],
36658
+ contentContainerStyle: styles33.optionListContent,
36659
+ keyboardShouldPersistTaps: "handled",
36660
+ showsVerticalScrollIndicator: false,
36661
+ children: [
36662
+ loading ? /* @__PURE__ */ jsx(View, { style: styles33.statusRow, children: /* @__PURE__ */ jsx(Text, { style: styles33.statusText, children: "Loading..." }) }) : filteredOptions.length === 0 ? /* @__PURE__ */ jsx(View, { style: styles33.statusRow, children: /* @__PURE__ */ jsx(Text, { style: styles33.statusText, children: emptyText }) }) : null,
36663
+ !loading && filteredOptions.map((option) => {
36664
+ const isSelected = selectedValues.includes(option.value);
36665
+ const isHovered = hoveredValue === option.value;
36666
+ const visualState = isSelected ? "selected" : isHovered ? "hover" : "default";
36667
+ return /* @__PURE__ */ jsxs(
36668
+ Pressable,
36669
+ {
36670
+ onPress: () => handleSelect(option.value),
36671
+ disabled,
36672
+ onHoverIn: () => setHoveredValue(option.value),
36673
+ onHoverOut: () => setHoveredValue((current) => current === option.value ? null : current),
36674
+ onPressIn: () => setHoveredValue(option.value),
36675
+ onPressOut: () => setHoveredValue((current) => current === option.value ? null : current),
36676
+ accessibilityRole: multiple ? "checkbox" : "button",
36677
+ accessibilityState: { selected: isSelected, checked: isSelected, disabled },
36678
+ style: [
36679
+ styles33.item,
36680
+ {
36681
+ backgroundColor: itemBackground(visualState)
36682
+ }
36683
+ ],
36684
+ children: [
36685
+ multiple ? /* @__PURE__ */ jsx(View, { style: [styles33.itemCheckbox, { pointerEvents: "none" }], children: /* @__PURE__ */ jsx(Checkbox, { value: isSelected, variant: "light" }) }) : null,
36686
+ /* @__PURE__ */ jsx(Text, { style: styles33.itemLabel, numberOfLines: 1, children: option.label })
36687
+ ]
36688
+ },
36689
+ option.value
36690
+ );
36691
+ })
36692
+ ]
36693
+ }
36694
+ )
36695
+ ]
36696
+ }
36697
+ ) : null;
36698
+ const webMenuPortal = Platform.OS === "web" && isOpen && typeof document !== "undefined" ? (0, import_react_dom3.createPortal)(menuNode, document.body) : null;
36597
36699
  return /* @__PURE__ */ jsxs(
36598
36700
  View,
36599
36701
  {
@@ -36634,80 +36736,7 @@ var Select = forwardRef(
36634
36736
  ]
36635
36737
  }
36636
36738
  ),
36637
- isOpen ? /* @__PURE__ */ jsxs(
36638
- View,
36639
- {
36640
- style: [
36641
- styles33.menu,
36642
- menuAlignEnd ? styles33.menuAlignEnd : styles33.menuAlignStart,
36643
- menuPlaceAbove ? styles33.menuAbove : styles33.menuBelow,
36644
- menuStyle
36645
- ],
36646
- children: [
36647
- showSearch ? /* @__PURE__ */ jsxs(View, { style: styles33.searchField, children: [
36648
- /* @__PURE__ */ jsx(View, { style: styles33.iconSlot, children: /* @__PURE__ */ jsx(SearchIcon, { size: ICON_SIZE13, color: colors.grey100 }) }),
36649
- /* @__PURE__ */ jsx(
36650
- TextInput,
36651
- {
36652
- ref: searchInputRef,
36653
- value: searchQuery,
36654
- onChangeText: handleSearchChange,
36655
- editable: !disabled,
36656
- placeholder: searchPlaceholder,
36657
- placeholderTextColor: colors.grey100,
36658
- style: [
36659
- styles33.searchInput,
36660
- Platform.OS === "web" ? styles33.searchInputWeb : null,
36661
- Platform.OS === "android" ? styles33.searchInputAndroid : null
36662
- ],
36663
- accessibilityLabel: searchPlaceholder
36664
- }
36665
- )
36666
- ] }) : null,
36667
- /* @__PURE__ */ jsxs(
36668
- ScrollView,
36669
- {
36670
- style: [styles33.optionList, { maxHeight: resolvedOptionListMaxHeight }],
36671
- contentContainerStyle: styles33.optionListContent,
36672
- keyboardShouldPersistTaps: "handled",
36673
- showsVerticalScrollIndicator: false,
36674
- children: [
36675
- loading ? /* @__PURE__ */ jsx(View, { style: styles33.statusRow, children: /* @__PURE__ */ jsx(Text, { style: styles33.statusText, children: "Loading..." }) }) : filteredOptions.length === 0 ? /* @__PURE__ */ jsx(View, { style: styles33.statusRow, children: /* @__PURE__ */ jsx(Text, { style: styles33.statusText, children: emptyText }) }) : null,
36676
- !loading && filteredOptions.map((option) => {
36677
- const isSelected = selectedValues.includes(option.value);
36678
- const isHovered = hoveredValue === option.value;
36679
- const visualState = isSelected ? "selected" : isHovered ? "hover" : "default";
36680
- return /* @__PURE__ */ jsxs(
36681
- Pressable,
36682
- {
36683
- onPress: () => handleSelect(option.value),
36684
- disabled,
36685
- onHoverIn: () => setHoveredValue(option.value),
36686
- onHoverOut: () => setHoveredValue((current) => current === option.value ? null : current),
36687
- onPressIn: () => setHoveredValue(option.value),
36688
- onPressOut: () => setHoveredValue((current) => current === option.value ? null : current),
36689
- accessibilityRole: multiple ? "checkbox" : "button",
36690
- accessibilityState: { selected: isSelected, checked: isSelected, disabled },
36691
- style: [
36692
- styles33.item,
36693
- {
36694
- backgroundColor: itemBackground(visualState)
36695
- }
36696
- ],
36697
- children: [
36698
- multiple ? /* @__PURE__ */ jsx(View, { style: [styles33.itemCheckbox, { pointerEvents: "none" }], children: /* @__PURE__ */ jsx(Checkbox, { value: isSelected, variant: "light" }) }) : null,
36699
- /* @__PURE__ */ jsx(Text, { style: styles33.itemLabel, numberOfLines: 1, children: option.label })
36700
- ]
36701
- },
36702
- option.value
36703
- );
36704
- })
36705
- ]
36706
- }
36707
- )
36708
- ]
36709
- }
36710
- ) : null
36739
+ Platform.OS === "web" ? webMenuPortal : menuNode
36711
36740
  ] }),
36712
36741
  showHint && hint ? /* @__PURE__ */ jsx(Text, { style: [styles33.hint, { color: hintColor }], children: hint }) : null
36713
36742
  ]