Extracting ViewHeaderSearch component
This commit is contained in:
parent
dc5896a64a
commit
5ad7ed5fc0
2 changed files with 76 additions and 74 deletions
|
@ -1,14 +1,9 @@
|
||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {FormattedMessage, injectIntl, IntlShape} from 'react-intl'
|
import {FormattedMessage} from 'react-intl'
|
||||||
|
|
||||||
import {BlockIcons} from '../../blockIcons'
|
|
||||||
import {MutableCard} from '../../blocks/card'
|
|
||||||
import {CardFilter} from '../../cardFilter'
|
|
||||||
import ViewMenu from '../../components/viewMenu'
|
import ViewMenu from '../../components/viewMenu'
|
||||||
import {Constants} from '../../constants'
|
|
||||||
import {CsvExporter} from '../../csvExporter'
|
|
||||||
import mutator from '../../mutator'
|
import mutator from '../../mutator'
|
||||||
import {BoardTree} from '../../viewModel/boardTree'
|
import {BoardTree} from '../../viewModel/boardTree'
|
||||||
import Button from '../../widgets/buttons/button'
|
import Button from '../../widgets/buttons/button'
|
||||||
|
@ -19,13 +14,13 @@ import MenuWrapper from '../../widgets/menuWrapper'
|
||||||
import Editable from '../editable'
|
import Editable from '../editable'
|
||||||
import FilterComponent from '../filterComponent'
|
import FilterComponent from '../filterComponent'
|
||||||
import ModalWrapper from '../modalWrapper'
|
import ModalWrapper from '../modalWrapper'
|
||||||
import {sendFlashMessage} from '../flashMessages'
|
|
||||||
|
|
||||||
import NewCardButton from './newCardButton'
|
import NewCardButton from './newCardButton'
|
||||||
import ViewHeaderPropertiesMenu from './viewHeaderPropertiesMenu'
|
import ViewHeaderPropertiesMenu from './viewHeaderPropertiesMenu'
|
||||||
import ViewHeaderGroupByMenu from './viewHeaderGroupByMenu'
|
import ViewHeaderGroupByMenu from './viewHeaderGroupByMenu'
|
||||||
import ViewHeaderSortMenu from './viewHeaderSortMenu'
|
import ViewHeaderSortMenu from './viewHeaderSortMenu'
|
||||||
import ViewHeaderActionsMenu from './viewHeaderActionsMenu'
|
import ViewHeaderActionsMenu from './viewHeaderActionsMenu'
|
||||||
|
import ViewHeaderSearch from './viewHeaderSearch'
|
||||||
|
|
||||||
import './viewHeader.scss'
|
import './viewHeader.scss'
|
||||||
|
|
||||||
|
@ -38,52 +33,25 @@ type Props = {
|
||||||
addCardTemplate: () => void
|
addCardTemplate: () => void
|
||||||
editCardTemplate: (cardTemplateId: string) => void
|
editCardTemplate: (cardTemplateId: string) => void
|
||||||
withGroupBy?: boolean
|
withGroupBy?: boolean
|
||||||
intl: IntlShape
|
|
||||||
readonly: boolean
|
readonly: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
isSearching: boolean
|
|
||||||
showFilter: boolean
|
showFilter: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewHeader extends React.Component<Props, State> {
|
class ViewHeader extends React.Component<Props, State> {
|
||||||
private searchFieldRef = React.createRef<Editable>()
|
|
||||||
|
|
||||||
shouldComponentUpdate(): boolean {
|
shouldComponentUpdate(): boolean {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {isSearching: Boolean(this.props.boardTree.getSearchText()), showFilter: false}
|
this.state = {showFilter: false}
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(prevPros: Props, prevState: State): void {
|
|
||||||
if (this.state.isSearching && !prevState.isSearching) {
|
|
||||||
this.searchFieldRef.current?.focus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onExportCsvTrigger(boardTree: BoardTree) {
|
|
||||||
try {
|
|
||||||
CsvExporter.exportTableCsv(boardTree)
|
|
||||||
const exportCompleteMessage = this.props.intl.formatMessage({
|
|
||||||
id: 'ViewHeader.export-complete',
|
|
||||||
defaultMessage: 'Export complete!',
|
|
||||||
})
|
|
||||||
sendFlashMessage({content: exportCompleteMessage, severity: 'normal'})
|
|
||||||
} catch (e) {
|
|
||||||
const exportFailedMessage = this.props.intl.formatMessage({
|
|
||||||
id: 'ViewHeader.export-failed',
|
|
||||||
defaultMessage: 'Export failed!',
|
|
||||||
})
|
|
||||||
sendFlashMessage({content: exportFailedMessage, severity: 'high'})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
const {boardTree, showView, withGroupBy, intl} = this.props
|
const {boardTree, showView, withGroupBy} = this.props
|
||||||
const {board, activeView} = boardTree
|
const {board, activeView} = boardTree
|
||||||
|
|
||||||
const hasFilter = activeView.filter && activeView.filter.filters?.length > 0
|
const hasFilter = activeView.filter && activeView.filter.filters?.length > 0
|
||||||
|
@ -160,28 +128,10 @@ class ViewHeader extends React.Component<Props, State> {
|
||||||
|
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
|
|
||||||
{this.state.isSearching &&
|
<ViewHeaderSearch
|
||||||
<Editable
|
boardTree={this.props.boardTree}
|
||||||
ref={this.searchFieldRef}
|
setSearchText={this.props.setSearchText}
|
||||||
text={boardTree.getSearchText()}
|
/>
|
||||||
placeholderText={intl.formatMessage({id: 'ViewHeader.search-text', defaultMessage: 'Search text'})}
|
|
||||||
style={{color: 'rgb(var(--main-fg))'}}
|
|
||||||
onChanged={(text) => {
|
|
||||||
this.searchChanged(text)
|
|
||||||
}}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
this.onSearchKeyDown(e)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
|
|
||||||
{!this.state.isSearching &&
|
|
||||||
<Button onClick={() => this.setState({isSearching: true})}>
|
|
||||||
<FormattedMessage
|
|
||||||
id='ViewHeader.search'
|
|
||||||
defaultMessage='Search'
|
|
||||||
/>
|
|
||||||
</Button>}
|
|
||||||
|
|
||||||
{/* Options menu */}
|
{/* Options menu */}
|
||||||
|
|
||||||
|
@ -213,21 +163,6 @@ class ViewHeader extends React.Component<Props, State> {
|
||||||
private hideFilterDialog = () => {
|
private hideFilterDialog = () => {
|
||||||
this.setState({showFilter: false})
|
this.setState({showFilter: false})
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSearchKeyDown = (e: React.KeyboardEvent) => {
|
|
||||||
if (e.keyCode === 27) { // ESC: Clear search
|
|
||||||
if (this.searchFieldRef.current) {
|
|
||||||
this.searchFieldRef.current.text = ''
|
|
||||||
}
|
|
||||||
this.setState({isSearching: false})
|
|
||||||
this.props.setSearchText(undefined)
|
|
||||||
e.preventDefault()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private searchChanged(text?: string) {
|
|
||||||
this.props.setSearchText(text)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default injectIntl(ViewHeader)
|
export default ViewHeader
|
||||||
|
|
67
webapp/src/components/viewHeader/viewHeaderSearch.tsx
Normal file
67
webapp/src/components/viewHeader/viewHeaderSearch.tsx
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
import React, {useState, useRef, useEffect} from 'react'
|
||||||
|
import {FormattedMessage, injectIntl, IntlShape} from 'react-intl'
|
||||||
|
|
||||||
|
import {BoardTree} from '../../viewModel/boardTree'
|
||||||
|
import Button from '../../widgets/buttons/button'
|
||||||
|
|
||||||
|
import Editable from '../editable'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
boardTree: BoardTree
|
||||||
|
setSearchText: (text?: string) => void
|
||||||
|
intl: IntlShape
|
||||||
|
}
|
||||||
|
|
||||||
|
const ViewHeaderSearch = React.memo((props: Props) => {
|
||||||
|
const searchFieldRef = useRef<Editable>(null)
|
||||||
|
const [isSearching, setIsSearching] = useState(Boolean(props.boardTree.getSearchText()))
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
searchFieldRef.current?.focus()
|
||||||
|
}, [isSearching])
|
||||||
|
|
||||||
|
const onSearchKeyDown = (e: React.KeyboardEvent) => {
|
||||||
|
if (e.keyCode === 27) { // ESC: Clear search
|
||||||
|
if (searchFieldRef.current) {
|
||||||
|
searchFieldRef.current.text = ''
|
||||||
|
}
|
||||||
|
setIsSearching(false)
|
||||||
|
props.setSearchText(undefined)
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
if (e.keyCode === 13 && searchFieldRef.current?.text.trim() === '') { // ENTER: with empty string clear search
|
||||||
|
setIsSearching(false)
|
||||||
|
props.setSearchText(undefined)
|
||||||
|
e.preventDefault()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const {boardTree, intl} = props
|
||||||
|
|
||||||
|
if (isSearching) {
|
||||||
|
return (
|
||||||
|
<Editable
|
||||||
|
ref={searchFieldRef}
|
||||||
|
text={boardTree.getSearchText()}
|
||||||
|
placeholderText={intl.formatMessage({id: 'ViewHeader.search-text', defaultMessage: 'Search text'})}
|
||||||
|
style={{color: 'rgb(var(--main-fg))'}}
|
||||||
|
onChanged={props.setSearchText}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
onSearchKeyDown(e)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Button onClick={() => setIsSearching(true)}>
|
||||||
|
<FormattedMessage
|
||||||
|
id='ViewHeader.search'
|
||||||
|
defaultMessage='Search'
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default injectIntl(ViewHeaderSearch)
|
Loading…
Reference in a new issue