Skip to content

Commit

Permalink
refactor(comments): inline text filtering for mentions menu
Browse files Browse the repository at this point in the history
  • Loading branch information
skogsmaskin committed Sep 26, 2023
1 parent d3bb098 commit 0873340
Show file tree
Hide file tree
Showing 5 changed files with 213 additions and 167 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {Box, Flex, Spinner, Stack, Text, TextInput} from '@sanity/ui'
import {Box, Flex, Spinner, Stack, Text} from '@sanity/ui'
import styled from 'styled-components'
import React, {useCallback, useEffect, useMemo, useState} from 'react'
import {SearchIcon} from '@sanity/icons'
import React, {useCallback, useImperativeHandle, useMemo, useRef, useState} from 'react'
import {MentionOptionUser} from '../../types'
import {CommandList} from '../../../components'
import {CommandList, CommandListHandle} from '../../../components'
import {FIXME} from '../../../FIXME'
import {MentionsMenuItem} from './MentionsMenuItem'

const EMPTY_ARRAY: MentionOptionUser[] = []
Expand All @@ -12,11 +12,6 @@ const Root = styled(Stack)({
maxWidth: '220px', // todo: improve
})

const HeaderBox = styled(Box)({
borderBottom: '1px solid var(--card-border-color)',
minHeight: 'max-content',
})

const ITEM_HEIGHT = 41
const LIST_PADDING = 4
const MAX_ITEMS = 7
Expand All @@ -25,23 +20,39 @@ const FlexWrap = styled(Flex)({
maxHeight: ITEM_HEIGHT * MAX_ITEMS + LIST_PADDING * 2 + ITEM_HEIGHT / 2,
})

export interface MentionsMenuHandle {
selectCurrent: () => void
setSearchTerm: (term: string) => void
}
interface MentionsMenuProps {
loading: boolean
inputElement?: HTMLDivElement | null
onSelect: (userId: string) => void
options: MentionOptionUser[] | null
}

export const MentionsMenu = React.forwardRef(function MentionsMenu(
props: MentionsMenuProps,
ref: React.Ref<HTMLDivElement>,
ref: React.Ref<FIXME>,
) {
const {loading, onSelect, options = []} = props
const [inputElement, setInputElement] = useState<HTMLInputElement | null>(null)
const {loading, onSelect, options = [], inputElement} = props
const [searchTerm, setSearchTerm] = useState<string>('')

const handleInputChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(event.target.value)
}, [])
const commandListRef = useRef<CommandListHandle>(null)

useImperativeHandle(
ref,
() => {
return {
selectCurrent() {
commandListRef.current?.selectCurrent()
},
setSearchTerm(term: string) {
setSearchTerm(term)
},
}
},
[commandListRef],
)

const renderItem = useCallback(
(itemProps: MentionOptionUser) => {
Expand All @@ -67,14 +78,6 @@ export const MentionsMenu = React.forwardRef(function MentionsMenu(
return filtered || EMPTY_ARRAY
}, [options, searchTerm])

useEffect(() => {
const timeout = setTimeout(() => inputElement?.focus(), 0)

return () => {
clearTimeout(timeout)
}
}, [inputElement])

if (loading) {
return (
<Root>
Expand All @@ -85,19 +88,12 @@ export const MentionsMenu = React.forwardRef(function MentionsMenu(
)
}

// In this case the input element is the actual content editable div from the PTE.
// Typecast it as a HTMLInputElement to the CommandList
const _inputElement = inputElement ? (inputElement as HTMLInputElement) : undefined

return (
<Flex direction="column" height="fill" ref={ref}>
<HeaderBox padding={1}>
<TextInput
fontSize={1}
icon={SearchIcon}
onChange={handleInputChange}
placeholder="Search for a user"
radius={2}
ref={setInputElement}
/>
</HeaderBox>

{filteredOptions.length === 0 && (
<Box padding={5}>
<Text align="center" size={1} muted>
Expand All @@ -111,13 +107,13 @@ export const MentionsMenu = React.forwardRef(function MentionsMenu(
<CommandList
activeItemDataAttr="data-hovered"
ariaLabel="List of users to mention"
autoFocus="input"
fixedHeight
getItemDisabled={getItemDisabled}
inputElement={inputElement}
inputElement={_inputElement}
itemHeight={41}
items={filteredOptions}
padding={1}
ref={commandListRef}
renderItem={renderItem}
/>
</FlexWrap>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ export function CommentInputInner(props: CommentInputInnerProps) {
const [discardButtonElement, setDiscardButtonElement] = useState<HTMLButtonElement | null>(null)

const [user] = useUser(currentUser.id)
const {openMentions, focused, expandOnFocus, canSubmit, hasChanges} = useCommentInput()
const {openMentions, focused, expandOnFocus, canSubmit, hasChanges, insertAtChar} =
useCommentInput()

const avatar = <CommentsAvatar user={user} />

Expand All @@ -89,6 +90,11 @@ export function CommentInputInner(props: CommentInputInnerProps) {
discardButtonElement?.blur()
}, [discardButtonElement, onEditDiscard])

const handleMentionButtonClicked = useCallback(() => {
insertAtChar()
openMentions()
}, [insertAtChar, openMentions])

return (
<Flex align="flex-start" gap={2}>
{withAvatar ? avatar : null}
Expand All @@ -114,7 +120,7 @@ export function CommentInputInner(props: CommentInputInnerProps) {
fontSize={1}
icon={MentionIcon}
mode="bleed"
onClick={openMentions}
onClick={handleMentionButtonClicked}
padding={2}
/>

Expand Down
Loading

0 comments on commit 0873340

Please sign in to comment.