Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Select Input UI should not close before selecting input #550

Merged
3 changes: 3 additions & 0 deletions src/components/RadioInput/RadioInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface OptionProps<T> {
}
export interface RadioInputProps<T> {
inputType?: 'radio' | 'checkbox';
disabled?: boolean;
OptionComponent?: React.ElementType<OptionProps<T>>;
OptionContainerComponent?: React.ElementType;
onChange?: (value: T, index: number) => void;
Expand Down Expand Up @@ -37,6 +38,7 @@ export default function RadioInput<T>({
list,
maxColumnCount,
value,
disabled,
name,
className,
containerClassName,
Expand Down Expand Up @@ -74,6 +76,7 @@ export default function RadioInput<T>({
type={inputType}
name={groupName}
id={id}
disabled={disabled}
checked={index === selectedIndex}
onChange={() => onChange?.(option, index)}
onClick={onClick}
Expand Down
39 changes: 36 additions & 3 deletions src/components/inputs/SelectInput/SelectInput.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { faAngleDown } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ReactNode, useCallback, useState } from 'react';
import { ReactNode, useCallback, useEffect, useRef, useState } from 'react';
import Drawer from '../../Drawer';
import RadioInput from '../../RadioInput';
import { OptionProps, RadioInputProps } from '../../RadioInput/RadioInput';
Expand Down Expand Up @@ -87,13 +87,45 @@ export default function SelectInput<T>({
() => setExpanded((expanded) => !expanded),
[]
);
const close = useCallback(() => setTimeout(() => setExpanded(false), 1), []);

const containerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
function handleClickOutside(event: Event) {
// only collapse if new focus doesn't exist or is outside of the container
const target = event.target as Element | null;
if (!target?.nodeType || !containerRef.current?.contains(target)) {
setExpanded(false);
}
}
// Bind the event listener to document
if (expanded) {
// use focusout instead of blur because blur don't not always bubble
// use focusin instead of focusout because tabbing to a child triggers
// "focusout" with the document.body as the activeElement
// use click and keydown and touchstart to catch start of all input types
// - https://www.quirksmode.org/dom/events/blurfocus.html
// - https://developer.mozilla.org/en-US/docs/Web/API/Element/focusin_event
// - https://developer.mozilla.org/en-US/docs/Web/API/Element/focusout_event
document.addEventListener('focusin', handleClickOutside);
document.addEventListener('click', handleClickOutside);
document.addEventListener('keydown', handleClickOutside);
document.addEventListener('touchstart', handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener('focusin', handleClickOutside);
document.removeEventListener('click', handleClickOutside);
document.removeEventListener('keydown', handleClickOutside);
document.removeEventListener('touchstart', handleClickOutside);
};
}
}, [expanded]);

return (
<div
ref={containerRef}
className={['select-input', className, expanded && 'expanded']
.filter(Boolean)
.join(' ')}
onBlur={close}
>
<button
className="select-input-selection row flex flex-centered"
Expand All @@ -116,6 +148,7 @@ export default function SelectInput<T>({
floating={floating}
>
<RadioInput<T>
disabled={!expanded}
inputType="checkbox"
className={[
'select-input-group',
Expand Down
Loading