diff --git a/web/calendar/day.react.js b/web/calendar/day.react.js --- a/web/calendar/day.react.js +++ b/web/calendar/day.react.js @@ -24,13 +24,13 @@ import LogInFirstModal from '../modals/account/log-in-first-modal.react'; import HistoryModal from '../modals/history/history-modal.react'; import { useModalContext } from '../modals/modal-provider.react'; +import ThreadPickerModal from '../modals/threads/thread-picker-modal.react'; import { useSelector } from '../redux/redux-utils'; import { htmlTargetFromEvent } from '../vector-utils'; import { AddVector, HistoryVector } from '../vectors.react'; import css from './calendar.css'; import type { InnerEntry } from './entry.react'; import Entry from './entry.react'; -import ThreadPicker from './thread-picker.react'; type BaseProps = { +dayString: string, @@ -46,14 +46,13 @@ +timeZone: ?string, +dispatch: Dispatch, +pushModal: (modal: React.Node) => void, + +popModal: () => void, }; type State = { - +pickerOpen: boolean, +hovered: boolean, }; class Day extends React.PureComponent { state: State = { - pickerOpen: false, hovered: false, }; entryContainer: ?HTMLDivElement; @@ -61,13 +60,6 @@ actionLinks: ?HTMLDivElement; entries: Map = new Map(); - static getDerivedStateFromProps(props: Props) { - if (props.onScreenThreadInfos.length === 0) { - return { pickerOpen: false }; - } - return null; - } - componentDidUpdate(prevProps: Props) { if (this.props.entryInfos.length > prevProps.entryInfos.length) { invariant(this.entryContainer, 'entryContainer ref not set'); @@ -115,20 +107,6 @@ ); }); - let threadPicker = null; - if (this.state.pickerOpen) { - invariant( - this.props.onScreenThreadInfos.length > 0, - 'onScreenThreadInfos should exist if pickerOpen', - ); - threadPicker = ( - - ); - } - const entryContainerClasses = classNames(css.entryContainer, { [css.focusedEntryContainer]: hovered, }); @@ -149,7 +127,6 @@ /> {actionLinks} - {threadPicker} ); } @@ -170,10 +147,6 @@ this.entries.set(key, entry); }; - closePicker = () => { - this.setState({ pickerOpen: false }); - }; - onMouseEnter = () => { this.setState({ hovered: true }); }; @@ -210,7 +183,13 @@ if (this.props.onScreenThreadInfos.length === 1) { this.createNewEntry(this.props.onScreenThreadInfos[0].id); } else if (this.props.onScreenThreadInfos.length > 1) { - this.setState({ pickerOpen: true }); + this.props.pushModal( + , + ); } }; @@ -262,7 +241,7 @@ const nextLocalID = useSelector(state => state.nextLocalID); const timeZone = useSelector(state => state.timeZone); const dispatch = useDispatch(); - const modalContext = useModalContext(); + const { pushModal, popModal } = useModalContext(); return ( ); }, diff --git a/web/calendar/thread-picker.css b/web/calendar/thread-picker.css deleted file mode 100644 --- a/web/calendar/thread-picker.css +++ /dev/null @@ -1,45 +0,0 @@ -div.container { - position: absolute; - left: 4px; - bottom: 20px; - box-shadow: 0 8px 16px 0 rgba(0, 0, 0, 0.3); - z-index: 1; - font-weight: 600; - background-color: white; - border-radius: 3px; - white-space: nowrap; - line-height: normal; - font-size: 12px; - cursor: pointer; - outline: none; -} -span.thread { - color: black; - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - width: 110px; -} -div.container > div.option { - border-bottom: 1px solid #d3d3d3; - padding: 1px 10px 1px 25px; - position: relative; -} -div.container > div.option:hover { - background-color: #f2f2f2; -} -div.container > div:last-child { - border-bottom: none; -} -div.colorPreview { - width: 12px; - height: 12px; - left: 6px; - position: absolute; - top: 4px; - border: 1px solid lightgray; - border-radius: 2px; -} -div.threadName { - padding-top: 1px; -} diff --git a/web/calendar/thread-picker.react.js b/web/calendar/thread-picker.react.js deleted file mode 100644 --- a/web/calendar/thread-picker.react.js +++ /dev/null @@ -1,144 +0,0 @@ -// @flow - -import invariant from 'invariant'; -import * as React from 'react'; -import { createSelector } from 'reselect'; - -import { threadSearchIndex } from 'lib/selectors/nav-selectors'; -import { onScreenEntryEditableThreadInfos } from 'lib/selectors/thread-selectors'; -import type { ThreadInfo } from 'lib/types/thread-types'; - -import { useSelector } from '../redux/redux-utils'; -import { htmlTargetFromEvent } from '../vector-utils'; -import css from './thread-picker.css'; - -type OptionProps = { - +threadInfo: ThreadInfo, - +createNewEntry: (threadID: string) => void, -}; -function ThreadPickerOption(props: OptionProps) { - const { threadInfo, createNewEntry } = props; - const onClick = React.useCallback(() => createNewEntry(threadInfo.id), [ - threadInfo.id, - createNewEntry, - ]); - const colorStyle = { backgroundColor: `#${props.threadInfo.color}` }; - - return ( -
- -
- {props.threadInfo.uiName} - -
- ); -} - -type Props = { - +createNewEntry: (threadID: string) => void, - +closePicker: () => void, -}; - -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 can't be open when onScreenThreadInfos is empty", - ); - - const pickerDivRef = React.useRef(null); - - React.useLayoutEffect(() => { - invariant(pickerDivRef, 'pickerDivRef must be set'); - const { current } = pickerDivRef; - current?.focus(); - }, []); - - const [searchText, setSearchText] = React.useState(''); - const [searchResults, setSearchResults] = React.useState>( - new Set(), - ); - - const onPickerKeyDown = React.useCallback( - (event: SyntheticKeyboardEvent) => { - if (event.keyCode === 27) { - // esc - closePicker(); - } - }, - [closePicker], - ); - - const onMouseDown = React.useCallback( - (event: SyntheticEvent) => { - const target = htmlTargetFromEvent(event); - invariant(pickerDivRef, 'pickerDivRef must be set'); - if (pickerDivRef.current?.contains(target)) { - // This prevents onBlur from firing - event.preventDefault(); - } - }, - [], - ); - - // 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 listDataSelector = createSelector( - state => state.onScreenThreadInfos, - state => state.searchText, - state => state.searchResults, - ( - threadInfos: $ReadOnlyArray, - text: string, - results: Set, - ) => - text - ? threadInfos.filter(threadInfo => results.has(threadInfo.id)) - : [...threadInfos], - ); - - const threads = useSelector(() => - listDataSelector({ - onScreenThreadInfos, - searchText, - searchResults, - }), - ); - - const options = React.useMemo(() => { - return threads.map(threadInfo => ( - - )); - }, [threads, createNewEntry]); - - return ( -
- {options} -
- ); -} - -export default ThreadPicker;