Changeset View
Changeset View
Standalone View
Standalone View
web/chat/typeahead-tooltip.react.js
// @flow | // @flow | ||||
import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import type { RelativeMemberInfo } from 'lib/types/thread-types'; | import type { RelativeMemberInfo } from 'lib/types/thread-types'; | ||||
import { leastPositiveResidue } from 'lib/utils/math-utils'; | |||||
import type { InputState } from '../input/input-state'; | import type { InputState } from '../input/input-state'; | ||||
import { | import { | ||||
getTypeaheadTooltipActions, | getTypeaheadTooltipActions, | ||||
getTypeaheadTooltipButtons, | getTypeaheadTooltipButtons, | ||||
getTypeaheadTooltipPosition, | getTypeaheadTooltipPosition, | ||||
} from '../utils/typeahead-utils'; | } from '../utils/typeahead-utils'; | ||||
import type { TypeaheadMatchedStrings } from './chat-input-bar.react'; | import type { TypeaheadMatchedStrings } from './chat-input-bar.react'; | ||||
Show All 10 Lines | function TypeaheadTooltip(props: TypeaheadTooltipProps): React.Node { | ||||
const { inputState, textarea, matchedStrings, suggestedUsers } = props; | const { inputState, textarea, matchedStrings, suggestedUsers } = props; | ||||
const { textBeforeAtSymbol, usernamePrefix } = matchedStrings; | const { textBeforeAtSymbol, usernamePrefix } = matchedStrings; | ||||
const [isVisibleForAnimation, setIsVisibleForAnimation] = React.useState( | const [isVisibleForAnimation, setIsVisibleForAnimation] = React.useState( | ||||
false, | false, | ||||
); | ); | ||||
const [ | |||||
chosenPositionInOverlay, | |||||
setChosenPositionInOverlay, | |||||
] = React.useState<number>(0); | |||||
React.useEffect(() => { | |||||
setChosenPositionInOverlay(0); | |||||
}, [suggestedUsers]); | |||||
React.useEffect(() => { | React.useEffect(() => { | ||||
setIsVisibleForAnimation(true); | setIsVisibleForAnimation(true); | ||||
return () => setIsVisibleForAnimation(false); | return () => setIsVisibleForAnimation(false); | ||||
}, []); | }, []); | ||||
const actions = React.useMemo( | const actions = React.useMemo( | ||||
() => | () => | ||||
Show All 29 Lines | const tooltipPositionStyle = React.useMemo( | ||||
[tooltipPosition], | [tooltipPosition], | ||||
); | ); | ||||
const tooltipButtons = React.useMemo( | const tooltipButtons = React.useMemo( | ||||
() => getTypeaheadTooltipButtons(actions), | () => getTypeaheadTooltipButtons(actions), | ||||
[actions], | [actions], | ||||
); | ); | ||||
if (!actions || actions.length === 0) { | const close = React.useCallback(() => { | ||||
const setter = inputState.setTypeaheadState; | |||||
setter({ | |||||
canBeVisible: false, | |||||
moveChoiceUp: null, | |||||
moveChoiceDown: null, | |||||
close: null, | |||||
accept: null, | |||||
}); | |||||
}, [inputState.setTypeaheadState]); | |||||
const accept = React.useCallback(() => { | |||||
actions[chosenPositionInOverlay].execute(); | |||||
close(); | |||||
}, [actions, chosenPositionInOverlay, close]); | |||||
const moveChoiceUp = React.useCallback(() => { | |||||
if (actions.length === 0) { | |||||
return; | |||||
} | |||||
setChosenPositionInOverlay(previousPosition => | |||||
leastPositiveResidue(previousPosition - 1, actions.length), | |||||
); | |||||
}, [setChosenPositionInOverlay, actions.length]); | |||||
const moveChoiceDown = React.useCallback(() => { | |||||
if (actions.length === 0) { | |||||
return; | |||||
} | |||||
setChosenPositionInOverlay(previousPosition => | |||||
leastPositiveResidue(previousPosition + 1, actions.length), | |||||
); | |||||
}, [setChosenPositionInOverlay, actions.length]); | |||||
React.useEffect(() => { | |||||
const setter = inputState.setTypeaheadState; | |||||
setter({ | |||||
canBeVisible: true, | |||||
moveChoiceUp, | |||||
moveChoiceDown, | |||||
close, | |||||
accept, | |||||
}); | |||||
return close; | |||||
}, [ | |||||
close, | |||||
accept, | |||||
moveChoiceUp, | |||||
moveChoiceDown, | |||||
actions, | |||||
inputState.setTypeaheadState, | |||||
]); | |||||
if (suggestedUsers.length === 0) { | |||||
return null; | return null; | ||||
} | } | ||||
const overlayClasses = classNames(css.suggestionsContainer, { | const overlayClasses = classNames(css.suggestionsContainer, { | ||||
[css.notVisible]: !isVisibleForAnimation, | [css.notVisible]: !isVisibleForAnimation, | ||||
[css.visible]: isVisibleForAnimation, | [css.visible]: isVisibleForAnimation, | ||||
}); | }); | ||||
return ( | return ( | ||||
<div className={overlayClasses} style={tooltipPositionStyle}> | <div className={overlayClasses} style={tooltipPositionStyle}> | ||||
{tooltipButtons} | {tooltipButtons} | ||||
</div> | </div> | ||||
); | ); | ||||
} | } | ||||
export default TypeaheadTooltip; | export default TypeaheadTooltip; |