mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-19 21:46:43 -04:00
Use floating UI for AutoSuggestInput
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { autoUpdate, flip, size, useFloating } from '@floating-ui/react-dom';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, {
|
import React, {
|
||||||
FocusEvent,
|
FocusEvent,
|
||||||
@@ -19,8 +20,6 @@ import Autosuggest, {
|
|||||||
RenderInputComponentProps,
|
RenderInputComponentProps,
|
||||||
RenderSuggestionsContainerParams,
|
RenderSuggestionsContainerParams,
|
||||||
} from 'react-autosuggest';
|
} from 'react-autosuggest';
|
||||||
import { Manager, Popper, Reference } from 'react-popper';
|
|
||||||
import Portal from 'Components/Portal';
|
|
||||||
import usePrevious from 'Helpers/Hooks/usePrevious';
|
import usePrevious from 'Helpers/Hooks/usePrevious';
|
||||||
import { InputChanged } from 'typings/inputs';
|
import { InputChanged } from 'typings/inputs';
|
||||||
import styles from './AutoSuggestInput.css';
|
import styles from './AutoSuggestInput.css';
|
||||||
@@ -37,7 +36,6 @@ interface AutoSuggestInputProps<T>
|
|||||||
hasError?: boolean;
|
hasError?: boolean;
|
||||||
hasWarning?: boolean;
|
hasWarning?: boolean;
|
||||||
enforceMaxHeight?: boolean;
|
enforceMaxHeight?: boolean;
|
||||||
minHeight?: number;
|
|
||||||
maxHeight?: number;
|
maxHeight?: number;
|
||||||
renderInputComponent?: (
|
renderInputComponent?: (
|
||||||
inputProps: RenderInputComponentProps,
|
inputProps: RenderInputComponentProps,
|
||||||
@@ -70,7 +68,6 @@ function AutoSuggestInput<T = any>(props: AutoSuggestInputProps<T>) {
|
|||||||
enforceMaxHeight = true,
|
enforceMaxHeight = true,
|
||||||
hasError,
|
hasError,
|
||||||
hasWarning,
|
hasWarning,
|
||||||
minHeight = 50,
|
|
||||||
maxHeight = 200,
|
maxHeight = 200,
|
||||||
getSuggestionValue,
|
getSuggestionValue,
|
||||||
renderSuggestion,
|
renderSuggestion,
|
||||||
@@ -89,95 +86,59 @@ function AutoSuggestInput<T = any>(props: AutoSuggestInputProps<T>) {
|
|||||||
const updater = useRef<(() => void) | null>(null);
|
const updater = useRef<(() => void) | null>(null);
|
||||||
const previousSuggestions = usePrevious(suggestions);
|
const previousSuggestions = usePrevious(suggestions);
|
||||||
|
|
||||||
const handleComputeMaxHeight = useCallback(
|
const { refs, floatingStyles } = useFloating({
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
middleware: [
|
||||||
(data: any) => {
|
flip({
|
||||||
const { top, bottom, width } = data.offsets.reference;
|
crossAxis: false,
|
||||||
|
mainAxis: true,
|
||||||
if (enforceMaxHeight) {
|
}),
|
||||||
data.styles.maxHeight = maxHeight;
|
size({
|
||||||
} else {
|
apply({ rects, elements }) {
|
||||||
const windowHeight = window.innerHeight;
|
Object.assign(elements.floating.style, {
|
||||||
|
width: `${rects.reference.width}px`,
|
||||||
if (/^botton/.test(data.placement)) {
|
});
|
||||||
data.styles.maxHeight = windowHeight - bottom;
|
},
|
||||||
} else {
|
}),
|
||||||
data.styles.maxHeight = top;
|
],
|
||||||
}
|
placement: 'bottom-start',
|
||||||
}
|
whileElementsMounted: autoUpdate,
|
||||||
|
});
|
||||||
data.styles.width = width;
|
|
||||||
|
|
||||||
return data;
|
|
||||||
},
|
|
||||||
[enforceMaxHeight, maxHeight]
|
|
||||||
);
|
|
||||||
|
|
||||||
const createRenderInputComponent = useCallback(
|
const createRenderInputComponent = useCallback(
|
||||||
(inputProps: RenderInputComponentProps) => {
|
(inputProps: RenderInputComponentProps) => {
|
||||||
return (
|
if (renderInputComponent) {
|
||||||
<Reference>
|
return renderInputComponent(inputProps, refs.setReference);
|
||||||
{({ ref }) => {
|
}
|
||||||
if (renderInputComponent) {
|
|
||||||
return renderInputComponent(inputProps, ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={ref}>
|
<div ref={refs.setReference}>
|
||||||
<input {...inputProps} />
|
<input {...inputProps} />
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Reference>
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[renderInputComponent]
|
[refs.setReference, renderInputComponent]
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderSuggestionsContainer = useCallback(
|
const renderSuggestionsContainer = useCallback(
|
||||||
({ containerProps, children }: RenderSuggestionsContainerParams) => {
|
({ containerProps, children }: RenderSuggestionsContainerParams) => {
|
||||||
return (
|
return (
|
||||||
<Portal>
|
<div
|
||||||
<Popper
|
ref={refs.setFloating}
|
||||||
placement="bottom-start"
|
style={floatingStyles}
|
||||||
modifiers={{
|
className={children ? styles.suggestionsContainerOpen : undefined}
|
||||||
computeMaxHeight: {
|
>
|
||||||
order: 851,
|
<div
|
||||||
enabled: true,
|
{...containerProps}
|
||||||
fn: handleComputeMaxHeight,
|
style={{
|
||||||
},
|
maxHeight: enforceMaxHeight ? maxHeight : undefined,
|
||||||
flip: {
|
|
||||||
padding: minHeight,
|
|
||||||
},
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({ ref: popperRef, style, scheduleUpdate }) => {
|
{children}
|
||||||
updater.current = scheduleUpdate;
|
</div>
|
||||||
|
</div>
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={popperRef}
|
|
||||||
style={style}
|
|
||||||
className={
|
|
||||||
children ? styles.suggestionsContainerOpen : undefined
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
{...containerProps}
|
|
||||||
style={{
|
|
||||||
maxHeight: style.maxHeight,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Popper>
|
|
||||||
</Portal>
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[minHeight, handleComputeMaxHeight]
|
[enforceMaxHeight, floatingStyles, maxHeight, refs.setFloating]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleInputKeyDown = useCallback(
|
const handleInputKeyDown = useCallback(
|
||||||
@@ -236,23 +197,21 @@ function AutoSuggestInput<T = any>(props: AutoSuggestInputProps<T>) {
|
|||||||
}, [suggestions, previousSuggestions]);
|
}, [suggestions, previousSuggestions]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Manager>
|
<Autosuggest
|
||||||
<Autosuggest
|
{...otherProps}
|
||||||
{...otherProps}
|
ref={forwardedRef}
|
||||||
ref={forwardedRef}
|
id={name}
|
||||||
id={name}
|
inputProps={inputProps}
|
||||||
inputProps={inputProps}
|
theme={theme}
|
||||||
theme={theme}
|
suggestions={suggestions}
|
||||||
suggestions={suggestions}
|
getSuggestionValue={getSuggestionValue}
|
||||||
getSuggestionValue={getSuggestionValue}
|
renderInputComponent={createRenderInputComponent}
|
||||||
renderInputComponent={createRenderInputComponent}
|
renderSuggestionsContainer={renderSuggestionsContainer}
|
||||||
renderSuggestionsContainer={renderSuggestionsContainer}
|
renderSuggestion={renderSuggestion}
|
||||||
renderSuggestion={renderSuggestion}
|
onSuggestionSelected={onSuggestionSelected}
|
||||||
onSuggestionSelected={onSuggestionSelected}
|
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
|
||||||
onSuggestionsFetchRequested={onSuggestionsFetchRequested}
|
onSuggestionsClearRequested={onSuggestionsClearRequested}
|
||||||
onSuggestionsClearRequested={onSuggestionsClearRequested}
|
/>
|
||||||
/>
|
|
||||||
</Manager>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user