Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 8d1486c

Browse files
committed
changes selection to be completely controlled by parent component
1 parent e3d2113 commit 8d1486c

File tree

3 files changed

+138
-138
lines changed

3 files changed

+138
-138
lines changed

src/components/structures/AutocompleteInput.tsx

Lines changed: 54 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import React, { useState, ReactNode, ChangeEvent, KeyboardEvent, useEffect, useCallback, useRef } from 'react';
17+
import React, { useState, ReactNode, ChangeEvent, KeyboardEvent, useRef } from 'react';
1818
import classNames from 'classnames';
1919

2020
import Autocompleter from "../../autocomplete/AutocompleteProvider";
@@ -27,10 +27,10 @@ import { Icon as CheckmarkIcon } from '../../../res/img/element-icons/roomlist/c
2727
interface AutocompleteInputProps {
2828
provider: Autocompleter;
2929
placeholder: string;
30+
selection: ICompletion[];
31+
onSelectionChange: (selection: ICompletion[]) => void;
3032
maxSuggestions?: number;
3133
renderSuggestion?: (s: ICompletion) => ReactNode;
32-
selection?: ICompletion[];
33-
onSelectionChange?: (selection: ICompletion[]) => void;
3434
renderSelection?: (m: ICompletion) => ReactNode;
3535
additionalFilter?: (suggestion: ICompletion) => boolean;
3636
}
@@ -45,99 +45,74 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
4545
selection,
4646
additionalFilter,
4747
}) => {
48-
const [_selection, setSelection] = useState<ICompletion[]>([]);
49-
const [suggestions, setSuggestions] = useState<ICompletion[]>([]);
5048
const [query, setQuery] = useState<string>('');
51-
const [focusedElement, setFocusedElement] = useState<HTMLElement>(null);
52-
49+
const [suggestions, setSuggestions] = useState<ICompletion[]>([]);
50+
const [isFocused, setFocused] = useState<boolean>(false);
5351
const editorContainerRef = useRef<HTMLDivElement>();
5452
const editorRef = useRef<HTMLInputElement>();
5553

56-
const getSuggestions = useCallback(async () => {
57-
if (query) {
58-
let matches = await provider.getCompletions(
59-
query,
60-
{ start: query.length, end: query.length },
61-
true,
62-
maxSuggestions,
63-
);
64-
if (additionalFilter) {
65-
matches = matches.filter(additionalFilter);
66-
}
67-
setSuggestions(matches);
68-
}
69-
}, [query, maxSuggestions, provider, additionalFilter]);
70-
71-
useEffect(() => {
72-
getSuggestions();
73-
74-
if (selection) {
75-
setSelection(selection);
76-
}
77-
}, [getSuggestions, selection]);
78-
7954
const focusEditor = () => {
8055
editorRef?.current?.focus();
8156
};
8257

83-
const removeSelection = (t: ICompletion) => {
84-
const newSelection = _selection.map(s => s);
85-
const idx = _selection.findIndex(s => s.completionId === t.completionId);
58+
const onQueryChange = async (e: ChangeEvent<HTMLInputElement>) => {
59+
const value = e.target.value.trim();
8660

87-
if (idx >= 0) {
88-
newSelection.splice(idx, 1);
89-
if (onSelectionChange) {
90-
onSelectionChange(newSelection);
91-
}
92-
setSelection(newSelection);
93-
}
61+
setQuery(value);
9462

95-
setQuery('');
96-
};
63+
let matches = await provider.getCompletions(
64+
query,
65+
{ start: query.length, end: query.length },
66+
true,
67+
maxSuggestions,
68+
);
9769

98-
const onFilterChange = async (e: ChangeEvent<HTMLInputElement>) => {
99-
e.stopPropagation();
100-
e.preventDefault();
70+
if (additionalFilter) {
71+
matches = matches.filter(additionalFilter);
72+
}
10173

102-
setQuery(e.target.value.trim());
74+
setSuggestions(matches);
10375
};
10476

105-
const onClickInputArea = (e) => {
106-
e.stopPropagation();
107-
e.preventDefault();
108-
77+
const onClickInputArea = () => {
10978
focusEditor();
11079
};
11180

11281
const onKeyDown = (e: KeyboardEvent) => {
11382
const hasModifiers = e.ctrlKey || e.shiftKey || e.metaKey;
83+
11484
// when the field is empty and the user hits backspace remove the right-most target
115-
if (!query && _selection.length > 0 && e.key === Key.BACKSPACE && !hasModifiers) {
116-
e.preventDefault();
117-
removeSelection(_selection[_selection.length - 1]);
85+
if (!query && selection.length > 0 && e.key === Key.BACKSPACE && !hasModifiers) {
86+
removeSelection(selection[selection.length - 1]);
11887
}
11988
};
12089

121-
const toggleSelection = (e: ICompletion) => {
122-
const newSelection = _selection.map(t => t);
123-
const idx = _selection.findIndex(s => s.completionId === e.completionId);
124-
if (idx >= 0) {
125-
newSelection.splice(idx, 1);
90+
const toggleSelection = (completion: ICompletion) => {
91+
const newSelection = [...selection];
92+
const index = selection.findIndex(selection => selection.completionId === completion.completionId);
93+
94+
if (index >= 0) {
95+
newSelection.splice(index, 1);
12696
} else {
127-
newSelection.push(e);
97+
newSelection.push(completion);
12898
}
12999

130-
if (onSelectionChange) {
100+
onSelectionChange(newSelection);
101+
focusEditor();
102+
};
103+
104+
const removeSelection = (completion: ICompletion) => {
105+
const newSelection = [...selection];
106+
const index = selection.findIndex(selection => selection.completionId === completion.completionId);
107+
108+
if (index >= 0) {
109+
newSelection.splice(index, 1);
131110
onSelectionChange(newSelection);
132111
}
133-
setSelection(newSelection);
134-
setQuery('');
135-
136-
focusEditor();
137112
};
138113

139-
const _renderSuggestion = (s: ICompletion): ReactNode => {
140-
const isSelected = _selection.findIndex(selection => selection.completionId === s.completionId) >= 0;
114+
const _renderSuggestion = (completion: ICompletion): ReactNode => {
115+
const isSelected = selection.findIndex(selection => selection.completionId === completion.completionId) >= 0;
141116
const classes = classNames({
142117
'mx_AutocompleteInput_suggestion': true,
143118
'mx_AutocompleteInput_suggestion--selected': isSelected,
@@ -149,10 +124,10 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
149124
e.preventDefault();
150125
e.stopPropagation();
151126

152-
toggleSelection(s);
127+
toggleSelection(completion);
153128
}}
154-
key={s.completionId}
155-
data-testid={`autocomplete-suggestion-item-${s.completionId}`}
129+
key={completion.completionId}
130+
data-testid={`autocomplete-suggestion-item-${completion.completionId}`}
156131
>
157132
<div>
158133
{ children }
@@ -162,13 +137,13 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
162137
);
163138

164139
if (renderSuggestion) {
165-
return withContainer(renderSuggestion(s));
140+
return withContainer(renderSuggestion(completion));
166141
}
167142

168143
return withContainer(
169144
<>
170-
<span className='mx_AutocompleteInput_suggestion_title'>{ s.completion }</span>
171-
<span className='mx_AutocompleteInput_suggestion_description'>{ s.completionId }</span>
145+
<span className='mx_AutocompleteInput_suggestion_title'>{ completion.completion }</span>
146+
<span className='mx_AutocompleteInput_suggestion_description'>{ completion.completionId }</span>
172147
</>,
173148
);
174149
};
@@ -202,7 +177,7 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
202177
);
203178
};
204179

205-
const hasPlaceholder = (): boolean => _selection.length === 0 && query.length === 0;
180+
const hasPlaceholder = (): boolean => selection.length === 0 && query.length === 0;
206181

207182
return (
208183
<div className="mx_AutocompleteInput">
@@ -212,22 +187,22 @@ export const AutocompleteInput: React.FC<AutocompleteInputProps> = ({
212187
onClick={onClickInputArea}
213188
data-testid="autocomplete-editor"
214189
>
215-
{ _selection.map(s => _renderSelection(s)) }
190+
{ selection.map(s => _renderSelection(s)) }
216191
<input
192+
ref={editorRef}
217193
type="text"
218194
onKeyDown={onKeyDown}
219-
onChange={onFilterChange}
195+
onChange={onQueryChange}
220196
value={query}
221197
autoComplete="off"
222198
placeholder={hasPlaceholder() ? placeholder : null}
223-
ref={editorRef}
224-
onFocus={(e) => setFocusedElement(e.target)}
225-
onBlur={() => setFocusedElement(null)}
199+
onFocus={() => setFocused(true)}
200+
onBlur={() => setFocused(false)}
226201
data-testid="autocomplete-input"
227202
/>
228203
</div>
229204
{
230-
(focusedElement === editorRef.current && suggestions.length) ? (
205+
(isFocused && suggestions.length) ? (
231206
<div
232207
className="mx_AutocompleteInput_matches"
233208
style={{ top: editorContainerRef.current?.clientHeight }}

src/components/views/settings/AddPrivilegedUsers.tsx

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import React, { useCallback, useContext, useRef, useState } from 'react';
1818
import { Room } from 'matrix-js-sdk/src/models/room';
1919
import { EventType } from "matrix-js-sdk/src/@types/event";
2020

21-
import SettingsFieldSet from '../../../components/views/settings/SettingsFieldset';
2221
import { _t } from "../../../languageHandler";
2322
import { ICompletion } from '../../../autocomplete/Autocompleter';
2423
import UserProvider from "../../../autocomplete/UserProvider";
@@ -67,27 +66,29 @@ export const AddPrivilegedUsers: React.FC<AddPrivilegedUsersProps> = ({ room, de
6766
};
6867

6968
return (
70-
<form className="mx_SettingsFieldset">
71-
<legend className='mx_SettingsFieldset_legend'>{ _t('Add privileged users') }</legend>
72-
<div className='mx_SettingsFieldset_description'>
73-
{ _t('Give one or multiple users in this room more privileges') }
74-
</div>
75-
<AutocompleteInput
76-
provider={userProvider.current}
77-
placeholder={_t("Search users in this room …")}
78-
onSelectionChange={setSelectedUsers}
79-
selection={selectedUsers}
80-
additionalFilter={filterSuggestions}
81-
/>
82-
<PowerSelector value={powerLevel} onChange={setPowerLevel} />
83-
<AccessibleButton
84-
type='submit'
85-
kind='primary'
86-
disabled={!selectedUsers.length || isLoading}
87-
onClick={onSubmit}
88-
>
89-
{ _t('Apply') }
90-
</AccessibleButton>
69+
<form className="mx_SettingsFieldset" onSubmit={onSubmit}>
70+
<fieldset style={{ width: '100%' }}>
71+
<legend className='mx_SettingsFieldset_legend'>{ _t('Add privileged users') }</legend>
72+
<div className='mx_SettingsFieldset_description'>
73+
{ _t('Give one or multiple users in this room more privileges') }
74+
</div>
75+
<AutocompleteInput
76+
provider={userProvider.current}
77+
placeholder={_t("Search users in this room …")}
78+
onSelectionChange={setSelectedUsers}
79+
selection={selectedUsers}
80+
additionalFilter={filterSuggestions}
81+
/>
82+
<PowerSelector value={powerLevel} onChange={setPowerLevel} />
83+
<AccessibleButton
84+
type='submit'
85+
kind='primary'
86+
disabled={!selectedUsers.length || isLoading}
87+
onClick={onSubmit}
88+
>
89+
{ _t('Apply') }
90+
</AccessibleButton>
91+
</fieldset>
9192
</form>
9293
);
9394
};

0 commit comments

Comments
 (0)