Fix issues with Floating UI

This commit is contained in:
Mark McDowall 2025-04-07 16:09:25 -07:00
parent b103005aa2
commit 50286cc9ce
No known key found for this signature in database
5 changed files with 74 additions and 60 deletions

View file

@ -93,9 +93,10 @@ function AutoSuggestInput<T = any>(props: AutoSuggestInputProps<T>) {
mainAxis: true,
}),
size({
apply({ rects, elements }) {
apply({ availableHeight, elements, rects }) {
Object.assign(elements.floating.style, {
width: `${rects.reference.width}px`,
minWidth: `${rects.reference.width}px`,
maxHeight: `${Math.max(0, availableHeight)}px`,
});
},
}),

View file

@ -42,15 +42,10 @@
color: var(--disabledInputColor);
}
.optionsContainer {
z-index: $popperZIndex;
max-height: vh(50);
width: auto;
}
.options {
composes: scroller from '~Components/Scroller/Scroller.css';
z-index: $popperZIndex;
border: 1px solid var(--inputBorderColor);
border-radius: 4px;
background-color: var(--inputBackgroundColor);

View file

@ -13,7 +13,6 @@ interface CssExports {
'mobileCloseButton': string;
'mobileCloseButtonContainer': string;
'options': string;
'optionsContainer': string;
'optionsInnerModalBody': string;
'optionsModal': string;
'optionsModalBody': string;

View file

@ -14,6 +14,7 @@ import React, {
KeyboardEvent,
ReactNode,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
@ -180,15 +181,21 @@ function EnhancedSelectInput<T extends EnhancedSelectInputValue<V>, V>(
mainAxis: true,
}),
size({
apply({ rects, elements }) {
apply({ availableHeight, elements, rects }) {
Object.assign(elements.floating.style, {
'min-width': `${rects.reference.width}px`,
minWidth: `${rects.reference.width}px`,
maxHeight: `${Math.max(
0,
Math.min(window.innerHeight / 2, availableHeight)
)}px`,
});
},
}),
],
open: isOpen,
placement: 'bottom-start',
whileElementsMounted: autoUpdate,
onOpenChange: setIsOpen,
});
const click = useClick(context);
@ -214,12 +221,8 @@ function EnhancedSelectInput<T extends EnhancedSelectInputValue<V>, V>(
}, [value, values, isMultiSelect]);
const handlePress = useCallback(() => {
if (!isOpen && onOpen) {
onOpen();
}
setIsOpen(!isOpen);
}, [isOpen, setIsOpen, onOpen]);
setIsOpen((prevIsOpen) => !prevIsOpen);
}, []);
const handleSelect = useCallback(
(newValue: ArrayElement<V>) => {
@ -372,6 +375,12 @@ function EnhancedSelectInput<T extends EnhancedSelectInputValue<V>, V>(
[onChange]
);
useEffect(() => {
if (isOpen) {
onOpen?.();
}
}, [isOpen, onOpen]);
return (
<>
<div ref={refs.setReference} {...getReferenceProps()}>
@ -443,46 +452,43 @@ function EnhancedSelectInput<T extends EnhancedSelectInputValue<V>, V>(
</Link>
)}
</div>
{isOpen ? (
{!isMobile && isOpen ? (
<FloatingPortal id="portal-root">
<div
<Scroller
ref={refs.setFloating}
className={styles.optionsContainer}
className={styles.options}
style={floatingStyles}
{...getFloatingProps()}
>
{isOpen && !isMobile ? (
<Scroller className={styles.options}>
{values.map((v, index) => {
const hasParent = v.parentKey !== undefined;
const depth = hasParent ? 1 : 0;
const parentSelected =
v.parentKey !== undefined &&
Array.isArray(value) &&
value.includes(v.parentKey);
{values.map((v, index) => {
const hasParent = v.parentKey !== undefined;
const depth = hasParent ? 1 : 0;
const parentSelected =
v.parentKey !== undefined &&
Array.isArray(value) &&
value.includes(v.parentKey);
const { key, ...other } = v;
const { key, ...other } = v;
return (
<OptionComponent
key={v.key}
id={v.key}
depth={depth}
isSelected={isSelectedItem(index, value, values)}
isDisabled={parentSelected}
isMultiSelect={isMultiSelect}
{...valueOptions}
{...other}
isMobile={false}
onSelect={handleSelect}
>
{v.value}
</OptionComponent>
);
})}
</Scroller>
) : null}
</div>
return (
<OptionComponent
key={v.key}
id={v.key}
depth={depth}
isSelected={isSelectedItem(index, value, values)}
isDisabled={parentSelected}
isMultiSelect={isMultiSelect}
{...valueOptions}
{...other}
isMobile={false}
onSelect={handleSelect}
>
{v.value}
</OptionComponent>
);
})}
</Scroller>
</FloatingPortal>
) : null}

View file

@ -58,16 +58,6 @@ function Menu({
onPress: handleMenuButtonPress,
});
const handleFloaterPress = useCallback((_event: MouseEvent) => {
// TODO: Menu items should handle closing when they are clicked.
// This is handled before the menu item click event is handled, so wait 100ms before closing.
setTimeout(() => {
setIsMenuOpen(false);
}, 100);
return true;
}, []);
const handleWindowResize = useCallback(() => {
updateMaxHeight();
}, [updateMaxHeight]);
@ -118,8 +108,31 @@ function Menu({
onOpenChange: setIsMenuOpen,
});
const handleFloaterPress = useCallback(
(event: MouseEvent) => {
if (
refs.reference &&
(refs.reference.current as HTMLElement).contains(
event.target as HTMLElement
)
) {
return false;
}
// TODO: Menu items should handle closing when they are clicked.
// This is handled before the menu item click event is handled, so wait 100ms before closing.
setTimeout(() => {
setIsMenuOpen(false);
}, 100);
return true;
},
[refs.reference]
);
const click = useClick(context);
const dismiss = useDismiss(context, {
outsidePressEvent: 'click',
outsidePress: handleFloaterPress,
});