viewHeaderSearch.tsx
TLDR
This file contains the ViewHeaderSearch
component, which is a search field with a magnifying glass icon. It is used in the header of a view to search for cards. The component includes an editable input field that allows the user to enter and save search queries. The search field also supports keyboard shortcuts to focus on the input field.
Methods
No methods are defined in this file.
Classes
ViewHeaderSearch
The ViewHeaderSearch
class is a React component that renders a search field in the header of a view. It includes the following features:
- Uses Redux hooks (
useAppSelector
anduseAppDispatch
) to access the search text state from the store and dispatch actions to update it. - Uses React Router hook (
useRouteMatch
) to get the current view ID from the URL. - Uses React Intl hook (
useIntl
) to format the placeholder text for the search input. - Uses React Hotkeys Hook (
useHotkeys
) to bind keyboard shortcuts (ctrl+shift+f
andcmd+shift+f
) to focus on the search input field. - Includes a
searchFieldRef
to reference the input field and focus on it when a hotkey is triggered. - Uses the
Editable
component to provide an editable input field with placeholder text, event handlers to update the search value, and debounced dispatch of the search text to the store.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useState, useRef, useEffect, useMemo} from 'react'
import {useRouteMatch} from 'react-router-dom'
import {useIntl} from 'react-intl'
import {useHotkeys} from 'react-hotkeys-hook'
import {debounce} from 'lodash'
import CompassIcon from '../../widgets/icons/compassIcon'
import Editable from '../../widgets/editable'
import {useAppSelector, useAppDispatch} from '../../store/hooks'
import {getSearchText, setSearchText} from '../../store/searchText'
const ViewHeaderSearch = (): JSX.Element => {
const searchText = useAppSelector<string>(getSearchText)
const dispatch = useAppDispatch()
const intl = useIntl()
const match = useRouteMatch<{viewId?: string}>()
const searchFieldRef = useRef<{focus(selectAll?: boolean): void}>(null)
const [searchValue, setSearchValue] = useState(searchText)
const [currentView, setCurrentView] = useState(match.params?.viewId)
const dispatchSearchText = (value: string) => {
dispatch(setSearchText(value))
}
const debouncedDispatchSearchText = useMemo(
() => debounce(dispatchSearchText, 200), [])
useEffect(() => {
const viewId = match.params?.viewId
if (viewId !== currentView) {
setCurrentView(viewId)
setSearchValue('')
// Previously debounced calls to change the search text should be cancelled
// to avoid resetting the search text.
debouncedDispatchSearchText.cancel()
dispatchSearchText('')
}
}, [match.url])
useEffect(() => {
return () => {
debouncedDispatchSearchText.cancel()
}
}, [])
useHotkeys('ctrl+shift+f,cmd+shift+f', () => {
searchFieldRef.current?.focus(true)
})
return (
<div className='board-search-field'>
<CompassIcon
icon='magnify'
className='board-search-icon'
/>
<Editable
ref={searchFieldRef}
value={searchValue}
placeholderText={intl.formatMessage({id: 'ViewHeader.search-text', defaultMessage: 'Search cards'})}
onChange={(value) => {
setSearchValue(value)
debouncedDispatchSearchText(value)
}}
onCancel={() => {
setSearchValue('')
debouncedDispatchSearchText('')
}}
onSave={() => {
debouncedDispatchSearchText(searchValue)
}}
/>
</div>
)
}
export default ViewHeaderSearch