-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathJoinRulesSwitcher.tsx
More file actions
135 lines (127 loc) · 4.18 KB
/
JoinRulesSwitcher.tsx
File metadata and controls
135 lines (127 loc) · 4.18 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
129
130
131
132
133
134
135
import { ComponentType, MouseEventHandler, useCallback, useMemo, useState } from 'react';
import { config, Box, MenuItem, Text, RectCords, PopOut, Menu, Button, Spinner } from 'folds';
import type { IconProps } from '@phosphor-icons/react';
import { CaretDownIcon } from '@phosphor-icons/react/dist/csr/CaretDown';
import { PhosphorIcon } from '$components/PhosphorIcon';
import { JoinRule } from '$types/matrix-sdk';
import FocusTrap from 'focus-trap-react';
import { stopPropagation } from '$utils/keyboard';
import { getRoomIcon } from '$utils/room';
export type ExtraJoinRules = 'knock_restricted';
export type ExtendedJoinRules = JoinRule | ExtraJoinRules;
type JoinRuleIcons = Record<ExtendedJoinRules, ComponentType<IconProps>>;
export const useJoinRuleIcons = (roomType?: string): JoinRuleIcons =>
useMemo(
() => ({
[JoinRule.Invite]: getRoomIcon(roomType, JoinRule.Invite),
[JoinRule.Knock]: getRoomIcon(roomType, JoinRule.Knock),
knock_restricted: getRoomIcon(roomType, JoinRule.Restricted),
[JoinRule.Restricted]: getRoomIcon(roomType, JoinRule.Restricted),
[JoinRule.Public]: getRoomIcon(roomType, JoinRule.Public),
[JoinRule.Private]: getRoomIcon(roomType, JoinRule.Private),
}),
[roomType]
);
type JoinRuleLabels = Record<ExtendedJoinRules, string>;
export const useRoomJoinRuleLabel = (): JoinRuleLabels =>
useMemo(
() => ({
[JoinRule.Invite]: 'Invite Only',
[JoinRule.Knock]: 'Knock & Invite',
knock_restricted: 'Space Members or Knock',
[JoinRule.Restricted]: 'Space Members',
[JoinRule.Public]: 'Public',
[JoinRule.Private]: 'Invite Only',
}),
[]
);
type JoinRulesSwitcherProps<T extends ExtendedJoinRules[]> = {
icons: JoinRuleIcons;
labels: JoinRuleLabels;
rules: T;
value: T[number];
onChange: (value: T[number]) => void;
disabled?: boolean;
changing?: boolean;
};
export function JoinRulesSwitcher<T extends ExtendedJoinRules[]>({
icons,
labels,
rules,
value,
onChange,
disabled,
changing,
}: JoinRulesSwitcherProps<T>) {
const [cords, setCords] = useState<RectCords>();
const handleOpenMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
setCords(evt.currentTarget.getBoundingClientRect());
};
const handleChange = useCallback(
(selectedRule: ExtendedJoinRules) => {
setCords(undefined);
onChange(selectedRule);
},
[onChange]
);
return (
<PopOut
anchor={cords}
position="Bottom"
align="End"
content={
<FocusTrap
focusTrapOptions={{
initialFocus: false,
onDeactivate: () => setCords(undefined),
clickOutsideDeactivates: true,
isKeyForward: (evt: KeyboardEvent) => evt.key === 'ArrowDown',
isKeyBackward: (evt: KeyboardEvent) => evt.key === 'ArrowUp',
escapeDeactivates: stopPropagation,
}}
>
<Menu>
<Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
{rules.map((rule) => (
<MenuItem
key={rule}
size="300"
variant="Surface"
radii="300"
aria-pressed={value === rule}
onClick={() => handleChange(rule)}
before={<PhosphorIcon size="100" as={icons[rule]} />}
disabled={disabled}
>
<Box grow="Yes">
<Text size="T300">{labels[rule]}</Text>
</Box>
</MenuItem>
))}
</Box>
</Menu>
</FocusTrap>
}
>
<Button
size="300"
variant="Secondary"
fill="Soft"
radii="300"
outlined
before={<PhosphorIcon size="100" as={icons[value] ?? icons[JoinRule.Restricted]} />}
after={
changing ? (
<Spinner size="100" variant="Secondary" fill="Soft" />
) : (
<PhosphorIcon size="100" as={CaretDownIcon} />
)
}
onClick={handleOpenMenu}
disabled={disabled}
>
<Text size="B300">{labels[value] ?? 'Unsupported'}</Text>
</Button>
</PopOut>
);
}