feat: Tag search will now only show the direct tag results instead of also showing the parent and siblings (#2906)

This commit is contained in:
Aman Harwara
2025-06-24 12:32:27 +05:30
committed by GitHub
parent c050a2cd33
commit caa8eff216
5 changed files with 63 additions and 14 deletions

View File

@@ -38,8 +38,11 @@ export function itemMatchesQuery(
searchQuery: SearchQuery, searchQuery: SearchQuery,
collection: ReferenceLookupCollection, collection: ReferenceLookupCollection,
): boolean { ): boolean {
const shouldCheckForSomeTagMatches = searchQuery.shouldCheckForSomeTagMatches ?? true
const itemTags = collection.elementsReferencingElement(itemToMatch, ContentType.TYPES.Tag) as SNTag[] const itemTags = collection.elementsReferencingElement(itemToMatch, ContentType.TYPES.Tag) as SNTag[]
const someTagsMatches = itemTags.some((tag) => matchResultForStringQuery(tag, searchQuery.query) !== MatchResult.None) const someTagsMatches =
shouldCheckForSomeTagMatches &&
itemTags.some((tag) => matchResultForStringQuery(tag, searchQuery.query) !== MatchResult.None)
if (itemToMatch.protected && !searchQuery.includeProtectedNoteText) { if (itemToMatch.protected && !searchQuery.includeProtectedNoteText) {
const match = matchResultForStringQuery(itemToMatch, searchQuery.query) const match = matchResultForStringQuery(itemToMatch, searchQuery.query)

View File

@@ -5,6 +5,7 @@ import { SearchableItem } from './SearchableItem'
export type SearchQuery = { export type SearchQuery = {
query: string query: string
includeProtectedNoteText: boolean includeProtectedNoteText: boolean
shouldCheckForSomeTagMatches?: boolean
} }
export interface ReferenceLookupCollection { export interface ReferenceLookupCollection {

View File

@@ -6,16 +6,26 @@ import { TagListSectionType } from './TagListSection'
import { TagsListItem } from './TagsListItem' import { TagsListItem } from './TagsListItem'
import { useApplication } from '../ApplicationProvider' import { useApplication } from '../ApplicationProvider'
import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation' import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation'
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
type Props = { type Props = {
type: TagListSectionType type: TagListSectionType
} }
function getAllTagsForType(controller: NavigationController, type: TagListSectionType) {
if (type === 'all') {
if (controller.isSearching) {
return controller.tags
}
return controller.allLocalRootTags
}
return controller.starredTags
}
const TagsList: FunctionComponent<Props> = ({ type }: Props) => { const TagsList: FunctionComponent<Props> = ({ type }: Props) => {
const application = useApplication() const application = useApplication()
const allTags = const allTags = getAllTagsForType(application.navigationController, type)
type === 'all' ? application.navigationController.allLocalRootTags : application.navigationController.starredTags
const openTagContextMenu = useCallback( const openTagContextMenu = useCallback(
(x: number, y: number) => { (x: number, y: number) => {

View File

@@ -26,6 +26,8 @@ import { log, LoggingDomain } from '@/Logging'
import { NoteDragDataFormat, TagDragDataFormat } from './DragNDrop' import { NoteDragDataFormat, TagDragDataFormat } from './DragNDrop'
import { usePremiumModal } from '@/Hooks/usePremiumModal' import { usePremiumModal } from '@/Hooks/usePremiumModal'
import { useApplication } from '../ApplicationProvider' import { useApplication } from '../ApplicationProvider'
import { mergeRefs } from '../../Hooks/mergeRefs'
import { getTitleForLinkedTag } from '../../Utils/Items/Display/getTitleForLinkedTag'
type Props = { type Props = {
tag: SNTag tag: SNTag
@@ -46,6 +48,8 @@ export const TagsListItem: FunctionComponent<Props> = observer(
const [title, setTitle] = useState(tag.title || '') const [title, setTitle] = useState(tag.title || '')
const [subtagTitle, setSubtagTitle] = useState('') const [subtagTitle, setSubtagTitle] = useState('')
const tagRef = useRef<HTMLDivElement>(null)
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
const subtagInputRef = useRef<HTMLInputElement>(null) const subtagInputRef = useRef<HTMLInputElement>(null)
const menuButtonRef = useRef<HTMLAnchorElement>(null) const menuButtonRef = useRef<HTMLAnchorElement>(null)
@@ -75,6 +79,8 @@ export const TagsListItem: FunctionComponent<Props> = observer(
const [isBeingDraggedOver, setIsBeingDraggedOver] = useState(false) const [isBeingDraggedOver, setIsBeingDraggedOver] = useState(false)
const tagHierarchyPrefix = navigationController.isSearching && getTitleForLinkedTag(tag, application)?.titlePrefix
useEffect(() => { useEffect(() => {
if (!hadChildren && hasChildren) { if (!hadChildren && hasChildren) {
setShowChildren(true) setShowChildren(true)
@@ -117,6 +123,18 @@ export const TagsListItem: FunctionComponent<Props> = observer(
const selectCurrentTag = useCallback(async () => { const selectCurrentTag = useCallback(async () => {
await navigationController.setSelectedTag(tag, type, { await navigationController.setSelectedTag(tag, type, {
userTriggered: true, userTriggered: true,
scrollIntoView: false,
})
}, [navigationController, tag, type])
const clearSearchAndSelectCurrentTag = useCallback(async () => {
if (!navigationController.isSearching) {
return
}
navigationController.setSearchQuery('')
await navigationController.setSelectedTag(tag, type, {
userTriggered: true,
scrollIntoView: true,
}) })
}, [navigationController, tag, type]) }, [navigationController, tag, type])
@@ -196,8 +214,6 @@ export const TagsListItem: FunctionComponent<Props> = observer(
[navigationController, onContextMenu, tag, type], [navigationController, onContextMenu, tag, type],
) )
const tagRef = useRef<HTMLDivElement>(null)
const { addDragTarget, removeDragTarget } = useFileDragNDrop() const { addDragTarget, removeDragTarget } = useFileDragNDrop()
useEffect(() => { useEffect(() => {
@@ -292,6 +308,7 @@ export const TagsListItem: FunctionComponent<Props> = observer(
isBeingDraggedOver && 'is-drag-over', isBeingDraggedOver && 'is-drag-over',
)} )}
onClick={selectCurrentTag} onClick={selectCurrentTag}
onDoubleClick={clearSearchAndSelectCurrentTag}
onKeyDown={(event) => { onKeyDown={(event) => {
if (event.key === KeyboardKey.Enter || event.key === KeyboardKey.Space) { if (event.key === KeyboardKey.Enter || event.key === KeyboardKey.Space) {
selectCurrentTag().catch(console.error) selectCurrentTag().catch(console.error)
@@ -301,7 +318,18 @@ export const TagsListItem: FunctionComponent<Props> = observer(
setTagExpanded(true) setTagExpanded(true)
} }
}} }}
ref={tagRef} ref={mergeRefs([
tagRef,
function scrollIntoViewIfNeeded(el) {
if (!el || navigationController.tagToScrollIntoView !== tag) {
return
}
el.scrollIntoView({
block: 'center',
})
navigationController.tagToScrollIntoView = undefined
},
])}
style={{ style={{
paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`, paddingLeft: `${level * PADDING_PER_LEVEL_PX + PADDING_BASE_PX}px`,
}} }}
@@ -330,9 +358,7 @@ export const TagsListItem: FunctionComponent<Props> = observer(
{isEditing && ( {isEditing && (
<input <input
className={ className="title editing min-w-0 overflow-hidden text-mobile-navigation-list-item focus:shadow-none focus:outline-none lg:text-navigation-list-item"
'title editing overflow-hidden text-mobile-navigation-list-item focus:shadow-none focus:outline-none lg:text-navigation-list-item'
}
id={`react-tag-${tag.uuid}-${type}`} id={`react-tag-${tag.uuid}-${type}`}
onBlur={onBlur} onBlur={onBlur}
onInput={onInput} onInput={onInput}
@@ -346,11 +372,10 @@ export const TagsListItem: FunctionComponent<Props> = observer(
{!isEditing && ( {!isEditing && (
<> <>
<div <div
className={ className="title overflow-hidden text-left text-mobile-navigation-list-item focus:shadow-none focus:outline-none lg:text-navigation-list-item"
'title overflow-hidden text-left text-mobile-navigation-list-item focus:shadow-none focus:outline-none lg:text-navigation-list-item'
}
id={`react-tag-${tag.uuid}-${type}`} id={`react-tag-${tag.uuid}-${type}`}
> >
{tagHierarchyPrefix && <span className="opacity-50">{tagHierarchyPrefix}</span>}
{title} {title}
</div> </div>
</> </>

View File

@@ -59,6 +59,7 @@ export class NavigationController
previouslySelected_: AnyTag | undefined = undefined previouslySelected_: AnyTag | undefined = undefined
editing_: SNTag | SmartView | undefined = undefined editing_: SNTag | SmartView | undefined = undefined
addingSubtagTo: SNTag | undefined = undefined addingSubtagTo: SNTag | undefined = undefined
tagToScrollIntoView: AnyTag | undefined = undefined
contextMenuOpen = false contextMenuOpen = false
contextMenuClickLocation: { x: number; y: number } = { x: 0, y: 0 } contextMenuClickLocation: { x: number; y: number } = { x: 0, y: 0 }
@@ -390,10 +391,14 @@ export class NavigationController
return [] return []
} }
if (this.isSearching) {
return []
}
const children = this.items.getTagChildren(tag) const children = this.items.getTagChildren(tag)
const childrenUuids = children.map((childTag) => childTag.uuid) const childrenUuids = children.map((childTag) => childTag.uuid)
const childrenTags = this.isSearching ? children : this.tags.filter((tag) => childrenUuids.includes(tag.uuid)) const childrenTags = this.tags.filter((tag) => childrenUuids.includes(tag.uuid))
return childrenTags return childrenTags
} }
@@ -479,8 +484,9 @@ export class NavigationController
public async setSelectedTag( public async setSelectedTag(
tag: AnyTag | undefined, tag: AnyTag | undefined,
location: TagListSectionType, location: TagListSectionType,
{ userTriggered } = { userTriggered: false }, options?: { userTriggered: boolean; scrollIntoView?: boolean },
) { ) {
const { userTriggered = false, scrollIntoView = false } = options || {}
if (tag && tag.conflictOf) { if (tag && tag.conflictOf) {
this._changeAndSaveItem this._changeAndSaveItem
.execute(tag, (mutator) => { .execute(tag, (mutator) => {
@@ -512,6 +518,9 @@ export class NavigationController
}, },
InternalEventPublishStrategy.SEQUENCE, InternalEventPublishStrategy.SEQUENCE,
) )
if (userTriggered && scrollIntoView) {
this.tagToScrollIntoView = tag
}
}) })
} }
@@ -678,6 +687,7 @@ export class NavigationController
searchQuery: { searchQuery: {
query: this.searchQuery, query: this.searchQuery,
includeProtectedNoteText: false, includeProtectedNoteText: false,
shouldCheckForSomeTagMatches: false,
}, },
}) })
this.reloadTags() this.reloadTags()