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
|
import {
DialogButton,
DialogCheckbox,
DialogCheckboxProps,
Export,
Marquee,
Menu,
MenuItem,
findModuleExport,
showContextMenu,
} from '@decky/ui';
import { FC, useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { FaChevronDown } from 'react-icons/fa';
// TODO add to dfl
const dropDownControlButtonClass = findModuleExport((e: Export) =>
e?.toString?.()?.includes('gamepaddropdown_DropDownControlButton'),
);
const DropdownMultiselectItem: FC<
{
value: any;
onSelect: (checked: boolean, value: any) => void;
checked: boolean;
} & DialogCheckboxProps
> = ({ value, onSelect, checked: defaultChecked, ...rest }) => {
const [checked, setChecked] = useState(defaultChecked);
useEffect(() => {
onSelect?.(checked, value);
}, [checked, onSelect, value]);
return (
<MenuItem bInteractableItem onClick={() => setChecked((x) => !x)}>
<DialogCheckbox
style={{ marginBottom: 0, padding: 0 }}
className="decky_DropdownMultiselectItem_DialogCheckbox"
bottomSeparator="none"
{...rest}
onClick={() => setChecked((x) => !x)}
onChange={(checked) => setChecked(checked)}
controlled
checked={checked}
/>
</MenuItem>
);
};
const DropdownMultiselect: FC<{
items: {
label: string;
value: string;
}[];
selected: string[];
onSelect: (selected: any[]) => void;
label: string;
}> = ({ label, items, selected, onSelect }) => {
const [itemsSelected, setItemsSelected] = useState<any>(selected);
const { t } = useTranslation();
const handleItemSelect = useCallback((checked: boolean, value: any) => {
setItemsSelected((x: any) =>
checked ? [...x.filter((y: any) => y !== value), value] : x.filter((y: any) => y !== value),
);
}, []);
useEffect(() => {
onSelect(itemsSelected);
}, [itemsSelected, onSelect]);
return (
<DialogButton
style={{
display: 'flex',
alignItems: 'center',
maxWidth: '100%',
}}
className={dropDownControlButtonClass}
onClick={(evt) => {
evt.preventDefault();
showContextMenu(
<Menu label={label} cancelText={t('DropdownMultiselect.button.back') as string}>
<style>
{`
/* Inherit color from ".basiccontextmenu" */
.decky_DropdownMultiselectItem_DialogCheckbox > .DialogToggle_Label {
color: inherit;
}
`}
</style>
<div style={{ marginTop: '10px' }}>{/*FIXME: Hack for missing padding under label menu*/}</div>
{items.map((x) => (
<DropdownMultiselectItem
key={x.value}
label={x.label}
value={x.value}
checked={itemsSelected.includes(x.value)}
onSelect={handleItemSelect}
/>
))}
</Menu>,
evt.currentTarget ?? window,
);
}}
>
<Marquee>
{selected.length > 0
? selected.map((x: any) => items[items.findIndex((v) => v.value === x)].label).join(', ')
: '…'}
</Marquee>
<div style={{ flexGrow: 1, minWidth: '1ch' }} />
<FaChevronDown style={{ height: '1em', flex: '0 0 1em' }} />
</DialogButton>
);
};
export default DropdownMultiselect;
|