// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import React from 'react' import {injectIntl, IntlShape, FormattedMessage} from 'react-intl' import {Archiver} from '../archiver' import {ISortOption, MutableBoardView} from '../blocks/boardView' import {BlockIcons} from '../blockIcons' import {MutableCard} from '../blocks/card' import {IPropertyTemplate} from '../blocks/board' import {BoardTree} from '../viewModel/boardTree' import ViewMenu from '../components/viewMenu' import {CsvExporter} from '../csvExporter' import {CardFilter} from '../cardFilter' import mutator from '../mutator' import {Utils} from '../utils' import Menu from '../widgets/menu' import MenuWrapper from '../widgets/menuWrapper' import CheckIcon from '../widgets/icons/check' import DropdownIcon from '../widgets/icons/dropdown' import OptionsIcon from '../widgets/icons/options' import SortUpIcon from '../widgets/icons/sortUp' import SortDownIcon from '../widgets/icons/sortDown' import {Editable} from './editable' import FilterComponent from './filterComponent' import './viewHeader.scss' type Props = { boardTree?: BoardTree showView: (id: string) => void setSearchText: (text: string) => void addCard: (show: boolean) => void withGroupBy?: boolean intl: IntlShape } type State = { isSearching: boolean showFilter: boolean } class ViewHeader extends React.Component { private searchFieldRef = React.createRef() shouldComponentUpdate(): boolean { return true } constructor(props: Props) { super(props) this.state = {isSearching: Boolean(this.props.boardTree?.getSearchText()), showFilter: false} } componentDidUpdate(prevPros: Props, prevState: State): void { if (this.state.isSearching && !prevState.isSearching) { this.searchFieldRef.current.focus() } } private filterClicked = () => { this.setState({showFilter: true}) } private hideFilter = () => { this.setState({showFilter: false}) } private onSearchKeyDown = (e: React.KeyboardEvent) => { if (e.keyCode === 27) { // ESC: Clear search this.searchFieldRef.current.text = '' this.setState({isSearching: false}) this.props.setSearchText(undefined) e.preventDefault() } } private searchChanged(text?: string) { this.props.setSearchText(text) } private async testAddCards(count: number) { const {boardTree} = this.props const {board, activeView} = boardTree const startCount = boardTree?.cards?.length let optionIndex = 0 for (let i = 0; i < count; i++) { const card = new MutableCard() card.parentId = boardTree.board.id card.properties = CardFilter.propertiesThatMeetFilterGroup(activeView.filter, board.cardProperties) if (boardTree.groupByProperty && boardTree.groupByProperty.options.length > 0) { // Cycle through options const option = boardTree.groupByProperty.options[optionIndex] optionIndex = (optionIndex + 1) % boardTree.groupByProperty.options.length card.properties[boardTree.groupByProperty.id] = option.id card.title = `Test Card ${startCount + i + 1}` card.icon = BlockIcons.shared.randomIcon() } await mutator.insertBlock(card, 'test add card') } } private async testRandomizeIcons() { const {boardTree} = this.props for (const card of boardTree.cards) { mutator.changeIcon(card, BlockIcons.shared.randomIcon(), 'randomize icon') } } render(): JSX.Element { const {boardTree, showView, withGroupBy, intl} = this.props const {board, activeView} = boardTree const hasFilter = activeView.filter && activeView.filter.filters?.length > 0 const hasSort = activeView.sortOptions.length > 0 return (
{ mutator.changeTitle(activeView, text) }} />
{boardTree.board.cardProperties.map((option: IPropertyTemplate) => ( { const property = boardTree.board.cardProperties.find((o: IPropertyTemplate) => o.id === propertyId) Utils.assertValue(property) Utils.log(`Toggle property ${property.name}`) let newVisiblePropertyIds = [] if (activeView.visiblePropertyIds.includes(propertyId)) { newVisiblePropertyIds = activeView.visiblePropertyIds.filter((o: string) => o !== propertyId) } else { newVisiblePropertyIds = [...activeView.visiblePropertyIds, propertyId] } mutator.changeViewVisibleProperties(activeView, newVisiblePropertyIds) }} /> ))}
{withGroupBy &&
{boardTree.groupByProperty?.name} ), }} />
{boardTree.board.cardProperties.filter((o: IPropertyTemplate) => o.type === 'select').map((option: IPropertyTemplate) => ( : undefined} onClick={(id) => { if (boardTree.activeView.groupById === id) { return } mutator.changeViewGroupById(boardTree.activeView, id) }} /> ))}
}
{this.state.showFilter && }
{(activeView.sortOptions.length > 0) && <> { // This sets the manual card order to the currently displayed order // Note: Perform this as a single update to change both properties correctly const newView = new MutableBoardView(activeView) newView.cardOrder = boardTree.currentCardOrder() newView.sortOptions = [] mutator.updateBlock(newView, activeView, 'reorder') }} /> { mutator.changeViewSortOptions(activeView, []) }} /> } {boardTree.board.cardProperties.map((option: IPropertyTemplate) => ( : : undefined} onClick={(propertyId: string) => { let newSortOptions: ISortOption[] = [] if (activeView.sortOptions[0] && activeView.sortOptions[0].propertyId === propertyId) { // Already sorting by name, so reverse it newSortOptions = [ {propertyId, reversed: !activeView.sortOptions[0].reversed}, ] } else { newSortOptions = [ {propertyId, reversed: false}, ] } mutator.changeViewSortOptions(activeView, newSortOptions) }} /> ))}
{this.state.isSearching && { this.searchChanged(text) }} onKeyDown={(e) => { this.onSearchKeyDown(e) }} />} {!this.state.isSearching &&
{ this.setState({isSearching: true}) }} >
}
CsvExporter.exportTableCsv(boardTree)} /> Archiver.exportBoardTree(boardTree)} /> this.testAddCards(100)} /> this.testAddCards(1000)} /> this.testRandomizeIcons()} />
{ this.props.addCard(true) }} >
) } } export default injectIntl(ViewHeader)