Skip to content

Commit

Permalink
feat: Added search bar to navigation panel for searching tags and sma…
Browse files Browse the repository at this point in the history
…rt views (#2815)
  • Loading branch information
amanharwara authored Feb 2, 2024
1 parent 50c1977 commit b07abaa
Show file tree
Hide file tree
Showing 19 changed files with 396 additions and 191 deletions.
9 changes: 6 additions & 3 deletions packages/models/src/Domain/Runtime/Display/DisplayOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ export interface NotesAndFilesDisplayOptions extends GenericDisplayOptions {
customFilter?: DisplayControllerCustomFilter
}

export type TagsDisplayOptions = GenericDisplayOptions
export interface TagsAndViewsDisplayOptions extends GenericDisplayOptions {
searchQuery?: SearchQuery
customFilter?: DisplayControllerCustomFilter
}

export interface DisplayControllerDisplayOptions extends GenericDisplayOptions {
sortBy: CollectionSortProperty
sortDirection: CollectionSortDirection
}

export type NotesAndFilesDisplayControllerOptions = NotesAndFilesDisplayOptions & DisplayControllerDisplayOptions
export type TagsDisplayControllerOptions = TagsDisplayOptions & DisplayControllerDisplayOptions
export type AnyDisplayOptions = NotesAndFilesDisplayOptions | TagsDisplayOptions | GenericDisplayOptions
export type TagsDisplayControllerOptions = TagsAndViewsDisplayOptions & DisplayControllerDisplayOptions
export type AnyDisplayOptions = NotesAndFilesDisplayOptions | TagsAndViewsDisplayOptions | GenericDisplayOptions
2 changes: 2 additions & 0 deletions packages/services/src/Domain/Item/ItemManagerInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
NotesAndFilesDisplayControllerOptions,
ComponentInterface,
ItemStream,
TagsAndViewsDisplayOptions,
} from '@standardnotes/models'
import { AbstractService } from '../Service/AbstractService'

Expand Down Expand Up @@ -130,6 +131,7 @@ export interface ItemManagerInterface extends AbstractService {
getDisplayableNotes(): SNNote[]
getDisplayableNotesAndFiles(): (SNNote | FileItem)[]
setPrimaryItemDisplayOptions(options: NotesAndFilesDisplayControllerOptions): void
setTagsAndViewsDisplayOptions(options: TagsAndViewsDisplayOptions): void
getTagPrefixTitle(tag: SNTag): string | undefined
getItemLinkedFiles(item: DecryptedItemInterface): FileItem[]
getItemLinkedNotes(item: DecryptedItemInterface): SNNote[]
Expand Down
31 changes: 24 additions & 7 deletions packages/snjs/lib/Services/Items/ItemManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ export class ItemManager extends Services.AbstractService implements Services.It
Models.SNNote | Models.FileItem,
Models.NotesAndFilesDisplayOptions
>
private tagDisplayController!: Models.ItemDisplayController<Models.SNTag, Models.TagsDisplayOptions>
private tagDisplayController!: Models.ItemDisplayController<Models.SNTag, Models.TagsAndViewsDisplayOptions>
private itemsKeyDisplayController!: Models.ItemDisplayController<SNItemsKey>
private componentDisplayController!: Models.ItemDisplayController<Models.ComponentInterface>
private themeDisplayController!: Models.ItemDisplayController<Models.ComponentInterface>
private fileDisplayController!: Models.ItemDisplayController<Models.FileItem>
private smartViewDisplayController!: Models.ItemDisplayController<Models.SmartView>
private smartViewDisplayController!: Models.ItemDisplayController<Models.SmartView, Models.TagsAndViewsDisplayOptions>

constructor(
private payloadManager: PayloadManager,
Expand Down Expand Up @@ -73,10 +73,14 @@ export class ItemManager extends Services.AbstractService implements Services.It
hiddenContentTypes: [],
},
)
this.tagDisplayController = new Models.ItemDisplayController(this.collection, [ContentType.TYPES.Tag], {
sortBy: 'title',
sortDirection: 'asc',
})
this.tagDisplayController = new Models.ItemDisplayController<Models.SNTag, Models.TagsAndViewsDisplayOptions>(
this.collection,
[ContentType.TYPES.Tag],
{
sortBy: 'title',
sortDirection: 'asc',
},
)
this.itemsKeyDisplayController = new Models.ItemDisplayController(this.collection, [ContentType.TYPES.ItemsKey], {
sortBy: 'created_at',
sortDirection: 'asc',
Expand All @@ -89,7 +93,10 @@ export class ItemManager extends Services.AbstractService implements Services.It
sortBy: 'title',
sortDirection: 'asc',
})
this.smartViewDisplayController = new Models.ItemDisplayController(this.collection, [ContentType.TYPES.SmartView], {
this.smartViewDisplayController = new Models.ItemDisplayController<
Models.SmartView,
Models.TagsAndViewsDisplayOptions
>(this.collection, [ContentType.TYPES.SmartView], {
sortBy: 'title',
sortDirection: 'asc',
})
Expand Down Expand Up @@ -194,6 +201,16 @@ export class ItemManager extends Services.AbstractService implements Services.It
this.itemCounter.setDisplayOptions(updatedOptions)
}

public setTagsAndViewsDisplayOptions(options: Models.TagsAndViewsDisplayOptions): void {
const updatedOptions: Models.TagsAndViewsDisplayOptions = {
customFilter: Models.computeUnifiedFilterForDisplayOptions(options, this.collection),
...options,
}

this.tagDisplayController.setDisplayOptions(updatedOptions)
this.smartViewDisplayController.setDisplayOptions(updatedOptions)
}

public setVaultDisplayOptions(options: Models.VaultDisplayOptions): void {
this.navigationDisplayController.setVaultDisplayOptions(options)
this.tagDisplayController.setVaultDisplayOptions(options)
Expand Down
13 changes: 6 additions & 7 deletions packages/web/src/javascripts/Components/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
KeyboardEventHandler,
useCallback,
useImperativeHandle,
useRef,
useState,
} from 'react'
import { KeyboardKey } from '@standardnotes/ui-services'
import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation'
Expand Down Expand Up @@ -33,7 +33,7 @@ const Menu = forwardRef(
}: MenuProps,
forwardedRef,
) => {
const menuElementRef = useRef<HTMLMenuElement>(null)
const [menuElement, setMenuElement] = useState<HTMLMenuElement | null>(null)

const handleKeyDown: KeyboardEventHandler<HTMLMenuElement> = useCallback(
(event) => {
Expand All @@ -49,11 +49,10 @@ const Menu = forwardRef(

const isMobileScreen = useMediaQuery(MutuallyExclusiveMediaQueryBreakpoints.sm)

const { setInitialFocus } = useListKeyboardNavigation(
menuElementRef,
const { setInitialFocus } = useListKeyboardNavigation(menuElement, {
initialFocus,
isMobileScreen ? false : shouldAutoFocus,
)
shouldAutoFocus: isMobileScreen ? false : shouldAutoFocus,
})

useImperativeHandle(forwardedRef, () => ({
focus: () => {
Expand All @@ -65,7 +64,7 @@ const Menu = forwardRef(
<menu
className={`m-0 list-none px-4 focus:shadow-none md:px-0 ${className}`}
onKeyDown={handleKeyDown}
ref={mergeRefs([menuElementRef, forwardedRef])}
ref={mergeRefs([setMenuElement, forwardedRef])}
style={style}
aria-label={a11yLabel}
{...props}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NoteType, SNNote, classNames } from '@standardnotes/snjs'
import Modal, { ModalAction } from '../../Modal/Modal'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { MutuallyExclusiveMediaQueryBreakpoints, useMediaQuery } from '@/Hooks/useMediaQuery'
import { useApplication } from '../../ApplicationProvider'
import { confirmDialog } from '@standardnotes/ui-services'
Expand Down Expand Up @@ -134,8 +134,8 @@ const NoteConflictResolutionModal = ({
[close],
)

const listRef = useRef<HTMLDivElement>(null)
useListKeyboardNavigation(listRef)
const [listElement, setListElement] = useState<HTMLDivElement | null>(null)
useListKeyboardNavigation(listElement)

const [selectedMobileTab, setSelectedMobileTab] = useState<'list' | 'preview'>('list')

Expand Down Expand Up @@ -279,7 +279,7 @@ const NoteConflictResolutionModal = ({
'w-full overflow-y-auto border-r border-border py-1.5 md:flex md:w-auto md:min-w-60 md:flex-col',
selectedMobileTab !== 'list' && 'hidden md:flex',
)}
ref={listRef}
ref={setListElement}
>
{allVersions.map((note, index) => (
<ConflictListItem
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Action } from '@standardnotes/snjs'
import { FunctionComponent, useRef } from 'react'
import { FunctionComponent, useState } from 'react'
import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation'
import HistoryListItem from './HistoryListItem'
import { NoteHistoryController } from '@/Controllers/NoteHistory/NoteHistoryController'
Expand All @@ -13,16 +13,16 @@ type Props = {
const LegacyHistoryList: FunctionComponent<Props> = ({ legacyHistory, noteHistoryController, onSelectRevision }) => {
const { selectLegacyRevision, selectedEntry } = noteHistoryController

const legacyHistoryListRef = useRef<HTMLDivElement>(null)
const [listElement, setListElement] = useState<HTMLDivElement | null>(null)

useListKeyboardNavigation(legacyHistoryListRef)
useListKeyboardNavigation(listElement)

return (
<div
className={`flex h-full w-full flex-col focus:shadow-none ${
!legacyHistory?.length ? 'items-center justify-center' : ''
}`}
ref={legacyHistoryListRef}
ref={setListElement}
>
{legacyHistory?.map((entry) => {
const selectedEntryUrl = (selectedEntry as Action)?.subactions?.[0].url
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { observer } from 'mobx-react-lite'
import { Fragment, FunctionComponent, useMemo, useRef } from 'react'
import { Fragment, FunctionComponent, useMemo, useState } from 'react'
import Icon from '@/Components/Icon/Icon'
import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation'
import HistoryListItem from './HistoryListItem'
Expand All @@ -22,9 +22,9 @@ const RemoteHistoryList: FunctionComponent<RemoteHistoryListProps> = ({
}) => {
const { remoteHistory, isFetchingRemoteHistory, selectRemoteRevision, selectedEntry } = noteHistoryController

const remoteHistoryListRef = useRef<HTMLDivElement>(null)
const [listElement, setListElement] = useState<HTMLDivElement | null>(null)

useListKeyboardNavigation(remoteHistoryListRef)
useListKeyboardNavigation(listElement)

const remoteHistoryLength = useMemo(() => remoteHistory?.map((group) => group.entries).flat().length, [remoteHistory])

Expand All @@ -33,7 +33,7 @@ const RemoteHistoryList: FunctionComponent<RemoteHistoryListProps> = ({
className={`flex h-full w-full flex-col focus:shadow-none ${
isFetchingRemoteHistory || !remoteHistoryLength ? 'items-center justify-center' : ''
}`}
ref={remoteHistoryListRef}
ref={setListElement}
>
{isFetchingRemoteHistory && <Spinner className="h-5 w-5" />}
{remoteHistory?.map((group) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Fragment, FunctionComponent, useMemo, useRef } from 'react'
import { Fragment, FunctionComponent, useMemo, useState } from 'react'
import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation'
import HistoryListItem from './HistoryListItem'
import { observer } from 'mobx-react-lite'
Expand All @@ -12,9 +12,9 @@ type Props = {
const SessionHistoryList: FunctionComponent<Props> = ({ noteHistoryController, onSelectRevision }) => {
const { sessionHistory, selectedRevision, selectSessionRevision } = noteHistoryController

const sessionHistoryListRef = useRef<HTMLDivElement>(null)
const [listElement, setListElement] = useState<HTMLDivElement | null>(null)

useListKeyboardNavigation(sessionHistoryListRef)
useListKeyboardNavigation(listElement)

const sessionHistoryLength = useMemo(
() => sessionHistory?.map((group) => group.entries).flat().length,
Expand All @@ -26,7 +26,7 @@ const SessionHistoryList: FunctionComponent<Props> = ({ noteHistoryController, o
className={`flex h-full w-full flex-col focus:shadow-none ${
!sessionHistoryLength ? 'items-center justify-center' : ''
}`}
ref={sessionHistoryListRef}
ref={setListElement}
>
{sessionHistory?.map((group) => {
if (group.entries && group.entries.length) {
Expand Down
2 changes: 2 additions & 0 deletions packages/web/src/javascripts/Components/Tags/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useAvailableSafeAreaPadding } from '@/Hooks/useSafeAreaPadding'
import QuickSettingsButton from '../Footer/QuickSettingsButton'
import VaultSelectionButton from '../Footer/VaultSelectionButton'
import PreferencesButton from '../Footer/PreferencesButton'
import TagSearchBar from './TagSearchBar'

type Props = {
application: WebApplication
Expand Down Expand Up @@ -78,6 +79,7 @@ const Navigation = forwardRef<HTMLDivElement, Props>(({ application, className,
'md:hover:[overflow-y:_overlay] pointer-coarse:md:overflow-y-auto',
)}
>
<TagSearchBar navigationController={application.navigationController} />
<SmartViewsSection
application={application}
featuresController={application.featuresController}
Expand Down
22 changes: 19 additions & 3 deletions packages/web/src/javascripts/Components/Tags/SmartViewsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import { FeaturesController } from '@/Controllers/FeaturesController'
import { NavigationController } from '@/Controllers/Navigation/NavigationController'
import { SmartView } from '@standardnotes/snjs'
import { observer } from 'mobx-react-lite'
import { FunctionComponent } from 'react'
import { FunctionComponent, useState } from 'react'
import SmartViewsListItem from './SmartViewsListItem'
import { useListKeyboardNavigation } from '@/Hooks/useListKeyboardNavigation'

type Props = {
navigationController: NavigationController
Expand All @@ -18,8 +19,23 @@ const SmartViewsList: FunctionComponent<Props> = ({
}: Props) => {
const allViews = navigationController.smartViews

const [container, setContainer] = useState<HTMLDivElement | null>(null)

useListKeyboardNavigation(container, {
initialFocus: 0,
shouldAutoFocus: false,
shouldWrapAround: false,
resetLastFocusedOnBlur: true,
})

if (allViews.length === 0 && navigationController.isSearching) {
return (
<div className="px-4 py-1 text-base opacity-60 lg:text-sm">No smart views found. Try a different search.</div>
)
}

return (
<>
<div ref={setContainer}>
{allViews.map((view) => {
return (
<SmartViewsListItem
Expand All @@ -31,7 +47,7 @@ const SmartViewsList: FunctionComponent<Props> = ({
/>
)
})}
</>
</div>
)
}

Expand Down
Loading

0 comments on commit b07abaa

Please sign in to comment.