fix: Fixed issue where entering a space or punctuation in Super autocomplete would dismiss menu
This commit is contained in:
@@ -10,6 +10,7 @@ import { LinkableItem } from '@/Utils/Items/Search/LinkableItem'
|
||||
import { getIconForItem } from '@/Utils/Items/Icons/getIconForItem'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import { getTitleForLinkedTag } from '@/Utils/Items/Display/getTitleForLinkedTag'
|
||||
import { getItemTitleInContextOfLinkBubble } from '@/Utils/Items/Search/doesItemMatchSearchQuery'
|
||||
|
||||
type Props = {
|
||||
link: ItemLink
|
||||
@@ -117,7 +118,7 @@ const LinkedItemBubble = ({
|
||||
{link.type === 'linked-by' && link.item.content_type !== ContentType.Tag && (
|
||||
<span className={!isBidirectional ? 'hidden group-focus:block' : ''}>Linked By:</span>
|
||||
)}
|
||||
{link.item.title}
|
||||
{getItemTitleInContextOfLinkBubble(link.item)}
|
||||
</span>
|
||||
</span>
|
||||
{showUnlinkButton && (
|
||||
|
||||
@@ -6,6 +6,7 @@ import { LinkableItem } from '@/Utils/Items/Search/LinkableItem'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import { useApplication } from '../ApplicationProvider'
|
||||
import Icon from '../Icon/Icon'
|
||||
import { getItemTitleInContextOfLinkBubble } from '@/Utils/Items/Search/doesItemMatchSearchQuery'
|
||||
|
||||
type Props = {
|
||||
item: LinkableItem
|
||||
@@ -16,7 +17,7 @@ const LinkedItemMeta = ({ item, searchQuery }: Props) => {
|
||||
const application = useApplication()
|
||||
const [icon, className] = getIconForItem(item, application)
|
||||
const tagTitle = getTitleForLinkedTag(item, application)
|
||||
const title = item.title ?? ''
|
||||
const title = getItemTitleInContextOfLinkBubble(item)
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -905,9 +905,7 @@ class NoteView extends AbstractComponent<NoteViewProps, State> {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{editorMode !== 'super' && (
|
||||
<LinkedItemBubblesContainer linkingController={this.viewControllerManager.linkingController} />
|
||||
)}
|
||||
<LinkedItemBubblesContainer linkingController={this.viewControllerManager.linkingController} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import { LexicalTypeaheadMenuPlugin, useBasicTypeaheadTriggerMatch } from '@lexical/react/LexicalTypeaheadMenuPlugin'
|
||||
import { LexicalTypeaheadMenuPlugin } from '@lexical/react/LexicalTypeaheadMenuPlugin'
|
||||
import { TextNode } from 'lexical'
|
||||
import { FunctionComponent, useCallback, useMemo, useState } from 'react'
|
||||
import { ItemSelectionItemComponent } from './ItemSelectionItemComponent'
|
||||
@@ -12,6 +12,7 @@ import { INSERT_BUBBLE_COMMAND, INSERT_FILE_COMMAND } from '../Commands'
|
||||
import { useLinkingController } from '../../../../../Controllers/LinkingControllerProvider'
|
||||
import { PopoverClassNames } from '../ClassNames'
|
||||
import { isMobileScreen } from '@/Utils'
|
||||
import { useTypeaheadAllowingSpacesAndPunctuation } from './useTypeaheadAllowingSpacesAndPunctuation'
|
||||
|
||||
type Props = {
|
||||
currentNote: SNNote
|
||||
@@ -26,7 +27,7 @@ export const ItemSelectionPlugin: FunctionComponent<Props> = ({ currentNote }) =
|
||||
|
||||
const [queryString, setQueryString] = useState<string | null>('')
|
||||
|
||||
const checkForTriggerMatch = useBasicTypeaheadTriggerMatch('@', {
|
||||
const checkForTriggerMatch = useTypeaheadAllowingSpacesAndPunctuation('@', {
|
||||
minLength: 0,
|
||||
})
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { LexicalEditor } from 'lexical'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
export type QueryMatch = {
|
||||
leadOffset: number
|
||||
matchingString: string
|
||||
replaceableString: string
|
||||
}
|
||||
type TriggerFn = (text: string, editor: LexicalEditor) => QueryMatch | null
|
||||
|
||||
/**
|
||||
* Derived from
|
||||
* https://github.com/facebook/lexical/blob/main/packages/lexical-react/src/LexicalTypeaheadMenuPlugin.tsx#L545
|
||||
*/
|
||||
export function useTypeaheadAllowingSpacesAndPunctuation(
|
||||
trigger: string,
|
||||
{ minLength = 1, maxLength = 75 }: { minLength?: number; maxLength?: number },
|
||||
): TriggerFn {
|
||||
return useCallback(
|
||||
(text: string) => {
|
||||
const validChars = '[^' + trigger + ']'
|
||||
const TypeaheadTriggerRegex = new RegExp(
|
||||
'(^|\\s|\\()(' + '[' + trigger + ']' + '((?:' + validChars + '){0,' + maxLength + '})' + ')$',
|
||||
)
|
||||
const match = TypeaheadTriggerRegex.exec(text)
|
||||
if (match !== null) {
|
||||
const maybeLeadingWhitespace = match[1]
|
||||
const matchingString = match[3]
|
||||
if (matchingString.length >= minLength) {
|
||||
return {
|
||||
leadOffset: match.index + maybeLeadingWhitespace.length,
|
||||
matchingString,
|
||||
replaceableString: match[2],
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
[maxLength, minLength, trigger],
|
||||
)
|
||||
}
|
||||
@@ -1,12 +1,26 @@
|
||||
import { SNTag, WebApplicationInterface, DecryptedItemInterface, ItemContent } from '@standardnotes/snjs'
|
||||
import { WebApplicationInterface, DecryptedItemInterface, ItemContent, isNote, isTag } from '@standardnotes/snjs'
|
||||
|
||||
export function getItemTitleInContextOfLinkBubble(item: DecryptedItemInterface<ItemContent>) {
|
||||
return item.title && item.title.length > 0 ? item.title : isNote(item) ? item.preview_plain : ''
|
||||
}
|
||||
|
||||
function getItemSearchableString(item: DecryptedItemInterface<ItemContent>, application: WebApplicationInterface) {
|
||||
if (isNote(item)) {
|
||||
return item.title.length > 0 ? item.title : item.preview_plain
|
||||
} else if (isTag(item)) {
|
||||
return application.items.getTagLongTitle(item)
|
||||
}
|
||||
|
||||
return item.title ?? ''
|
||||
}
|
||||
|
||||
export function doesItemMatchSearchQuery(
|
||||
item: DecryptedItemInterface<ItemContent>,
|
||||
searchQuery: string,
|
||||
application: WebApplicationInterface,
|
||||
) {
|
||||
const title = item instanceof SNTag ? application.items.getTagLongTitle(item) : item.title ?? ''
|
||||
const matchesQuery = title.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
const title = getItemSearchableString(item, application).toLowerCase()
|
||||
const matchesQuery = title.includes(searchQuery.toLowerCase())
|
||||
const isArchivedOrTrashed = item.archived || item.trashed
|
||||
const isValidSearchResult = matchesQuery && !isArchivedOrTrashed
|
||||
|
||||
|
||||
Reference in New Issue
Block a user