Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3177475
D5289.id17301.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
6 KB
Referenced Files
None
Subscribers
None
D5289.id17301.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D5289: [web] refactored ThreadPicker from class based to functional
Attached
Detach File
Event Timeline
Log In to Comment