Page MenuHomePhabricator

D5289.id17301.diff
No OneTemporary

D5289.id17301.diff

diff --git a/web/calendar/thread-picker.react.js b/web/calendar/thread-picker.react.js
--- a/web/calendar/thread-picker.react.js
+++ b/web/calendar/thread-picker.react.js
@@ -6,7 +6,6 @@
import { threadSearchIndex } from 'lib/selectors/nav-selectors';
import { onScreenEntryEditableThreadInfos } from 'lib/selectors/thread-selectors';
-import SearchIndex from 'lib/shared/search-index';
import type { ThreadInfo } from 'lib/types/thread-types';
import { useSelector } from '../redux/redux-utils';
@@ -35,126 +34,107 @@
);
}
-type BaseProps = {
+type Props = {
+createNewEntry: (threadID: string) => void,
+closePicker: () => void,
};
-type Props = {
- ...BaseProps,
- +onScreenThreadInfos: $ReadOnlyArray<ThreadInfo>,
- +searchIndex: SearchIndex,
-};
-type State = {
- +searchText: string,
- +searchResults: Set<string>,
-};
-type PropsAndState = { ...Props, ...State };
-
-class ThreadPicker extends React.PureComponent<Props, State> {
- pickerDiv: ?HTMLDivElement;
-
- constructor(props: Props) {
- super(props);
- this.state = {
- searchText: '',
- searchResults: new Set(),
- };
- invariant(
- props.onScreenThreadInfos.length > 0,
- "ThreadPicker can't be open when onScreenThreadInfos is empty",
- );
- }
-
- componentDidMount() {
- invariant(this.pickerDiv, 'pickerDiv ref unset');
- this.pickerDiv.focus();
- }
-
- listDataSelector = createSelector(
- (propsAndState: PropsAndState) => propsAndState.onScreenThreadInfos,
- (propsAndState: PropsAndState) => propsAndState.searchText,
- (propsAndState: PropsAndState) => propsAndState.searchResults,
- (
- threadInfos: $ReadOnlyArray<ThreadInfo>,
- text: string,
- searchResults: Set<string>,
- ) =>
- text
- ? threadInfos.filter(threadInfo => searchResults.has(threadInfo.id))
- : [...threadInfos],
+
+function ThreadPicker(props: Props): React.Node {
+ const { closePicker, createNewEntry } = props;
+
+ const onScreenThreadInfos = useSelector(onScreenEntryEditableThreadInfos);
+ const searchIndex = useSelector(state => threadSearchIndex(state));
+
+ invariant(
+ onScreenThreadInfos.length > 0,
+ 'ThreadPicker cannot be open when OnScreenThreadInfos is empty',
+ );
+
+ const pickerDivRef = React.useRef<?HTMLDivElement>(null);
+
+ React.useLayoutEffect(() => {
+ invariant(pickerDivRef, 'pickerDivRef unset');
+ const { current } = pickerDivRef;
+ current?.focus();
+ }, []);
+
+ const [searchText, setSearchText] = React.useState<string>('');
+ const [searchResults, setSearchResults] = React.useState<Set<string>>(
+ new Set(),
);
- get getListData() {
- return this.listDataSelector({ ...this.props, ...this.state });
- }
+ const onPickerKeyDown = React.useCallback(
+ (event: SyntheticKeyboardEvent<HTMLDivElement>) => {
+ if (event.keyCode === 27) {
+ // esc
+ closePicker();
+ }
+ },
+ [closePicker],
+ );
+
+ const onMouseDown = React.useCallback(
+ (event: SyntheticEvent<HTMLDivElement>) => {
+ const target = htmlTargetFromEvent(event);
+ invariant(pickerDivRef, 'pickerDivRef not set');
+ if (pickerDivRef.current?.contains(target)) {
+ // This prevents onBlur from firing
+ event.preventDefault();
+ }
+ },
+ [],
+ );
- render() {
- const length = this.props.onScreenThreadInfos.length;
- invariant(
- length > 0,
- "ThreadPicker can't be open when onScreenThreadInfos is empty",
+ // eslint-disable-next-line no-unused-vars
+ const onChangeSearchText = React.useCallback(
+ (text: string) => {
+ const results = searchIndex.getSearchResults(text);
+ setSearchText(text);
+ setSearchResults(new Set(results));
+ },
+ [searchIndex],
+ );
+
+ const options = React.useMemo(() => {
+ const listDataSelector = createSelector(
+ state => state.onScreenThreadInfos,
+ state => state.searchText,
+ state => state.searchResults,
+ (
+ threadInfos: $ReadOnlyArray<ThreadInfo>,
+ text: string,
+ results: Set<string>,
+ ) =>
+ text
+ ? threadInfos.filter(threadInfo => results.has(threadInfo.id))
+ : [...threadInfos],
);
- const options = this.getListData.map(threadInfo => (
+ return listDataSelector({
+ onScreenThreadInfos,
+ searchText,
+ searchResults,
+ }).map(threadInfo => (
<ThreadPickerOption
threadInfo={threadInfo}
- createNewEntry={this.props.createNewEntry}
+ createNewEntry={createNewEntry}
key={threadInfo.id}
/>
));
+ }, [onScreenThreadInfos, searchResults, searchText, createNewEntry]);
- return (
- <div
- className={css.container}
- tabIndex="0"
- onBlur={this.props.closePicker}
- onKeyDown={this.onPickerKeyDown}
- onMouseDown={this.onMouseDown}
- ref={this.pickerDivRef}
- >
- {options}
- </div>
- );
- }
-
- pickerDivRef = (pickerDiv: ?HTMLDivElement) => {
- this.pickerDiv = pickerDiv;
- };
-
- onPickerKeyDown = (event: SyntheticKeyboardEvent<HTMLDivElement>) => {
- if (event.keyCode === 27) {
- // Esc
- this.props.closePicker();
- }
- };
-
- onMouseDown = (event: SyntheticEvent<HTMLDivElement>) => {
- const target = htmlTargetFromEvent(event);
- invariant(this.pickerDiv, 'pickerDiv ref not set');
- if (this.pickerDiv.contains(target)) {
- // This prevents onBlur from firing
- event.preventDefault();
- }
- };
-
- onChangeSearchText = (searchText: string) => {
- const results = this.props.searchIndex.getSearchResults(searchText);
- this.setState({ searchText, searchResults: new Set(results) });
- };
+ return (
+ <div
+ className={css.container}
+ tabIndex="0"
+ ref={pickerDivRef}
+ onBlur={closePicker}
+ onKeyDown={onPickerKeyDown}
+ onMouseDown={onMouseDown}
+ >
+ {options}
+ </div>
+ );
}
-const ConnectedThreadPicker: React.ComponentType<BaseProps> = React.memo<BaseProps>(
- function ConnectedThreadPicker(props) {
- const onScreenThreadInfos = useSelector(onScreenEntryEditableThreadInfos);
- const index = useSelector(state => threadSearchIndex(state));
- return (
- <ThreadPicker
- {...props}
- onScreenThreadInfos={onScreenThreadInfos}
- searchIndex={index}
- />
- );
- },
-);
-
-export default ConnectedThreadPicker;
+export default ThreadPicker;

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 8, 11:30 PM (19 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2446790
Default Alt Text
D5289.id17301.diff (6 KB)

Event Timeline