Improve dropdown component
This commit is contained in:
parent
3c314b455d
commit
b325e249a3
|
@ -1,4 +1,10 @@
|
||||||
import { ChangeEvent, ComponentType, useState } from "react";
|
import {
|
||||||
|
ChangeEvent,
|
||||||
|
ComponentType,
|
||||||
|
useState,
|
||||||
|
KeyboardEvent,
|
||||||
|
useRef,
|
||||||
|
} from "react";
|
||||||
import "./dropdown.css";
|
import "./dropdown.css";
|
||||||
import { attributes } from "../utils/attributes";
|
import { attributes } from "../utils/attributes";
|
||||||
import { Backdrop } from "../Backdrop/Backdrop";
|
import { Backdrop } from "../Backdrop/Backdrop";
|
||||||
|
@ -67,12 +73,20 @@ type Props = {
|
||||||
* --codex-dropdown-option-background-hover
|
* --codex-dropdown-option-background-hover
|
||||||
*/
|
*/
|
||||||
style?: CustomStyleCSS;
|
style?: CustomStyleCSS;
|
||||||
|
|
||||||
|
label: string;
|
||||||
|
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
Component?: ComponentType<DropdownOption>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function Dropdown({
|
export function Dropdown({
|
||||||
placeholder,
|
placeholder,
|
||||||
style,
|
style,
|
||||||
options,
|
options,
|
||||||
|
label,
|
||||||
|
id,
|
||||||
onMouseEnter,
|
onMouseEnter,
|
||||||
onMouseLeave,
|
onMouseLeave,
|
||||||
onFocus,
|
onFocus,
|
||||||
|
@ -82,6 +96,7 @@ export function Dropdown({
|
||||||
value = "",
|
value = "",
|
||||||
className = "",
|
className = "",
|
||||||
}: Props) {
|
}: Props) {
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const lower = value.toLocaleLowerCase();
|
const lower = value.toLocaleLowerCase();
|
||||||
const filtered = options.filter(
|
const filtered = options.filter(
|
||||||
(o) =>
|
(o) =>
|
||||||
|
@ -105,48 +120,63 @@ export function Dropdown({
|
||||||
setFocused(false);
|
setFocused(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
|
||||||
|
if (e.key === "Escape") {
|
||||||
|
onClose();
|
||||||
|
inputRef.current?.blur();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const onClose = () => setFocused(false);
|
const onClose = () => setFocused(false);
|
||||||
|
|
||||||
const attr = attributes({ "aria-expanded": focused });
|
const attr = attributes({ "aria-expanded": focused });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`dropdown ${className}`} style={style}>
|
<>
|
||||||
<Backdrop onClose={onClose} open={focused} />
|
<label className="dropdown-label" htmlFor={id}>
|
||||||
|
{label}
|
||||||
|
</label>
|
||||||
|
|
||||||
<Input
|
<div className={`dropdown ${className}`} style={style}>
|
||||||
className="dropdown-input"
|
<Backdrop onClose={onClose} open={focused} />
|
||||||
onChange={onChange}
|
|
||||||
onFocus={onInternalFocus}
|
|
||||||
onBlur={onInternalBlur}
|
|
||||||
placeholder={placeholder}
|
|
||||||
value={value}
|
|
||||||
label={""}
|
|
||||||
id={""}
|
|
||||||
onMouseEnter={onMouseEnter}
|
|
||||||
onMouseLeave={onMouseLeave}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="dropdown-panel" {...attr}>
|
<Input
|
||||||
{filtered.length ? (
|
ref={inputRef}
|
||||||
filtered.map((o) => (
|
className="dropdown-input"
|
||||||
<div
|
onChange={onChange}
|
||||||
className="dropdown-option"
|
onFocus={onInternalFocus}
|
||||||
onClick={() => onSelect(o)}
|
onBlur={onInternalBlur}
|
||||||
key={o.title}
|
onKeyUp={onKeyUp}
|
||||||
>
|
onMouseEnter={onMouseEnter}
|
||||||
{o.Icon && <o.Icon />}
|
onMouseLeave={onMouseLeave}
|
||||||
<div>
|
placeholder={placeholder}
|
||||||
<span className="dropdown-title">{o.title}</span>
|
value={value}
|
||||||
{o.subtitle && (
|
label={""}
|
||||||
<span className="dropdown-subtitle">{o.subtitle}</span>
|
id={id}
|
||||||
)}
|
/>
|
||||||
|
|
||||||
|
<div className="dropdown-panel" {...attr}>
|
||||||
|
{filtered.length ? (
|
||||||
|
filtered.map((o) => (
|
||||||
|
<div
|
||||||
|
className="dropdown-option"
|
||||||
|
onClick={() => onSelect(o)}
|
||||||
|
key={o.title}
|
||||||
|
>
|
||||||
|
{o.Icon && <o.Icon />}
|
||||||
|
<div>
|
||||||
|
<span className="dropdown-title">{o.title}</span>
|
||||||
|
{o.subtitle && (
|
||||||
|
<span className="dropdown-subtitle">{o.subtitle}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
))
|
||||||
))
|
) : (
|
||||||
) : (
|
<p className="dropdown-noResults">No results found</p>
|
||||||
<p className="dropdown-noResults">No results found</p>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,17 @@
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dropdown-label {
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
display: block;
|
||||||
|
color: var(--codex-color);
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-panel[aria-expanded] {
|
.dropdown-panel[aria-expanded] {
|
||||||
z-index: 2;
|
|
||||||
transform: translateY(0.5rem);
|
transform: translateY(0.5rem);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
z-index: 2;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-input {
|
.dropdown-input {
|
||||||
|
|
Loading…
Reference in New Issue