-
Notifications
You must be signed in to change notification settings - Fork 6.5k
Expand file tree
/
Copy pathindex.tsx
More file actions
128 lines (113 loc) · 3.4 KB
/
index.tsx
File metadata and controls
128 lines (113 loc) · 3.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { ChevronDownIcon } from '@heroicons/react/24/solid';
import classNames from 'classnames';
import { useId, useMemo } from 'react';
import type { SelectGroup, SelectProps } from '#ui/Common/Select';
import type { LinkLike } from '#ui/types';
import { isStringArray, isValuesArray } from '#ui/util/array';
import styles from '../index.module.css';
type StatelessSelectConfig = {
as?: LinkLike | 'div';
};
export type StatelessSelectProps<T extends string> = SelectProps<T> &
StatelessSelectConfig;
const StatelessSelect = <T extends string>({
values = [],
defaultValue,
placeholder,
label,
inline,
className,
ariaLabel,
disabled = false,
as: Component = 'div',
}: StatelessSelectProps<T>) => {
const id = useId();
const mappedValues = useMemo(() => {
let mappedValues = values;
if (isStringArray(mappedValues)) {
mappedValues = mappedValues.map(value => ({
label: value,
value: value,
}));
}
if (isValuesArray(mappedValues)) {
return [{ items: mappedValues }];
}
return mappedValues as Array<SelectGroup<T>>;
}, [values]) as Array<SelectGroup<T>>;
// Find the current/default item to display in summary
const currentItem = useMemo(
() =>
mappedValues
.flatMap(({ items }) => items)
.find(item => item.value === defaultValue),
[mappedValues, defaultValue]
);
return (
<div
className={classNames(
styles.select,
styles.noscript,
{ [styles.inline]: inline },
className
)}
>
{label && (
<label className={styles.label} htmlFor={id}>
{label}
</label>
)}
<details className={styles.trigger} id={id}>
<summary
className={styles.summary}
aria-label={ariaLabel}
aria-disabled={disabled}
>
{currentItem && (
<span className={styles.selectedValue}>
{currentItem.iconImage}
<span>{currentItem.label}</span>
</span>
)}
{!currentItem && (
<span className={styles.placeholder}>{placeholder}</span>
)}
<ChevronDownIcon className={styles.icon} />
</summary>
<div
className={classNames(styles.dropdown, { [styles.inline]: inline })}
>
{mappedValues.map(({ label: groupLabel, items }, groupKey) => (
<div
key={groupLabel?.toString() ?? groupKey}
className={styles.group}
>
{groupLabel && (
<div className={classNames(styles.item, styles.label)}>
{groupLabel}
</div>
)}
{items.map(
({ value, label, iconImage, disabled: itemDisabled }) => (
<Component
key={value}
href={value}
className={classNames(styles.item, styles.text, {
[styles.disabled]: itemDisabled || disabled,
[styles.selected]: value === defaultValue,
})}
aria-disabled={itemDisabled || disabled}
>
{iconImage}
<span>{label}</span>
</Component>
)
)}
</div>
))}
</div>
</details>
</div>
);
};
export default StatelessSelect;