Changeset View
Changeset View
Standalone View
Standalone View
web/utils/typeahead-utils.js
// @flow | // @flow | ||||
import classNames from 'classnames'; | |||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { oldValidUsernameRegexString } from 'lib/shared/account-utils'; | import { oldValidUsernameRegexString } from 'lib/shared/account-utils'; | ||||
import { stringForUserExplicit } from 'lib/shared/user-utils'; | import { stringForUserExplicit } from 'lib/shared/user-utils'; | ||||
import type { SetState } from 'lib/types/hook-types'; | |||||
import type { RelativeMemberInfo } from 'lib/types/thread-types'; | import type { RelativeMemberInfo } from 'lib/types/thread-types'; | ||||
import { typeaheadStyle } from '../chat/chat-constants'; | import { typeaheadStyle } from '../chat/chat-constants'; | ||||
import css from '../chat/typeahead-tooltip.css'; | import css from '../chat/typeahead-tooltip.css'; | ||||
import Button from '../components/button.react'; | import Button from '../components/button.react'; | ||||
const webTypeaheadRegex: RegExp = new RegExp( | const webTypeaheadRegex: RegExp = new RegExp( | ||||
`(?<textPrefix>(?:^(?:.|\n)*\\s+)|^)@(?<username>${oldValidUsernameRegexString})?$`, | `(?<textPrefix>(?:^(?:.|\n)*\\s+)|^)@(?<username>${oldValidUsernameRegexString})?$`, | ||||
▲ Show 20 Lines • Show All 103 Lines • ▼ Show 20 Lines | .map(suggestedUser => ({ | ||||
newText.length - newSuffixText.length + 1, | newText.length - newSuffixText.length + 1, | ||||
); | ); | ||||
}, | }, | ||||
actionButtonContent: stringForUserExplicit(suggestedUser), | actionButtonContent: stringForUserExplicit(suggestedUser), | ||||
})); | })); | ||||
} | } | ||||
function getTypeaheadTooltipButtons( | function getTypeaheadTooltipButtons( | ||||
setChosenPositionInOverlay: SetState<number>, | |||||
chosenPositionInOverlay: number, | |||||
actions: $ReadOnlyArray<TypeaheadTooltipAction>, | actions: $ReadOnlyArray<TypeaheadTooltipAction>, | ||||
): $ReadOnlyArray<React.Node> { | ): $ReadOnlyArray<React.Node> { | ||||
return actions.map(({ key, execute, actionButtonContent }) => ( | return actions.map((action, idx) => { | ||||
<Button key={key} onClick={execute} className={css.suggestion}> | const { key, execute, actionButtonContent } = action; | ||||
const buttonClasses = classNames(css.suggestion, { | |||||
[css.suggestionHover]: idx === chosenPositionInOverlay, | |||||
}); | |||||
const onMouseMove: ( | |||||
event: SyntheticEvent<HTMLButtonElement>, | |||||
) => mixed = () => { | |||||
setChosenPositionInOverlay(idx); | |||||
}; | |||||
return ( | |||||
<Button | |||||
key={key} | |||||
onClick={execute} | |||||
onMouseMove={onMouseMove} | |||||
className={buttonClasses} | |||||
> | |||||
<span>@{actionButtonContent}</span> | <span>@{actionButtonContent}</span> | ||||
</Button> | </Button> | ||||
)); | ); | ||||
}); | |||||
} | } | ||||
function getTypeaheadTooltipPosition( | function getTypeaheadTooltipPosition( | ||||
textarea: HTMLTextAreaElement, | textarea: HTMLTextAreaElement, | ||||
actionsLength: number, | actionsLength: number, | ||||
textBeforeAtSymbol: string, | textBeforeAtSymbol: string, | ||||
): TooltipPosition { | ): TooltipPosition { | ||||
const { caretTopOffset, caretLeftOffset } = getCaretOffsets( | const { caretTopOffset, caretLeftOffset } = getCaretOffsets( | ||||
textarea, | textarea, | ||||
textBeforeAtSymbol, | textBeforeAtSymbol, | ||||
Show All 29 Lines |