diff --git a/src/client/boardPage.tsx b/src/client/boardPage.tsx index c7334e3d5..8bb1334e3 100644 --- a/src/client/boardPage.tsx +++ b/src/client/boardPage.tsx @@ -227,6 +227,11 @@ class BoardPage implements IPageController { this.filterAnchorElement = ahchorElement this.render() } + + setSearchText(text?: string) { + this.boardTree.setSearchText(text) + this.render() + } } export { BoardPage } diff --git a/src/client/boardTree.ts b/src/client/boardTree.ts index 74c89dfbb..10459a5bf 100644 --- a/src/client/boardTree.ts +++ b/src/client/boardTree.ts @@ -17,6 +17,7 @@ class BoardTree { activeView?: BoardView groupByProperty?: IPropertyTemplate + private searchText?: string private allCards: IBlock[] = [] get allBlocks(): IBlock[] { return [this.board, ...this.views, ...this.allCards] @@ -93,8 +94,18 @@ class BoardTree { this.applyFilterSortAndGroup() } + getSearchText(): string | undefined { + return this.searchText + } + + setSearchText(text?: string) { + this.searchText = text + this.applyFilterSortAndGroup() + } + applyFilterSortAndGroup() { this.cards = this.filterCards(this.allCards) + this.cards = this.searchFilterCards(this.cards) this.cards = this.sortCards(this.cards) if (this.activeView.groupById) { @@ -104,6 +115,15 @@ class BoardTree { } } + private searchFilterCards(cards: IBlock[]) { + const { searchText } = this + if (!searchText) { return cards.slice() } + + return cards.filter(card => { + if (card.title?.toLowerCase().indexOf(searchText) !== -1) { return true } + }) + } + private setGroupByProperty(propertyId: string) { const { board } = this diff --git a/src/client/components/boardComponent.tsx b/src/client/components/boardComponent.tsx index 1b75c6ef3..ef1fca990 100644 --- a/src/client/components/boardComponent.tsx +++ b/src/client/components/boardComponent.tsx @@ -25,15 +25,26 @@ type Props = { type State = { isHoverOnCover: boolean + isSearching: boolean } class BoardComponent extends React.Component { private draggedCard: IBlock private draggedHeaderOption: IPropertyOption + private searchFieldRef = React.createRef() constructor(props: Props) { super(props) - this.state = { isHoverOnCover: false } + this.state = { + isHoverOnCover: false, + isSearching: false + } + } + + componentDidUpdate(prevPros: Props, prevState: State) { + if (this.state.isSearching && !prevState.isSearching) { + this.searchFieldRef.current.focus() + } } render() { @@ -87,9 +98,12 @@ class BoardComponent extends React.Component {
{ this.groupByClicked(e) }}> Group by {boardTree.groupByProperty?.name}
-
{ this.filterClicked(e) }}>Filter
-
{ this.sortClicked(e) }}>Sort
-
Search
+
{ this.filterClicked(e) }}>Filter
+
{ this.sortClicked(e) }}>Sort
+ {this.state.isSearching + ? { this.searchChanged(text) }} onKeyDown={(e) => { this.onSearchKeyDown(e) }}> + :
{ this.setState({ ...this.state, isSearching: true }) }}>Search
+ }
{ this.optionsClicked(e) }}>
{ this.addCard(undefined) }}>New
@@ -380,6 +394,19 @@ class BoardComponent extends React.Component { await mutator.changePropertyOptionOrder(board, boardTree.groupByProperty, draggedHeaderOption, destIndex) } } + + onSearchKeyDown(e: React.KeyboardEvent) { + if (e.keyCode === 27) { // ESC: Clear search + this.searchFieldRef.current.text = "" + this.setState({...this.state, isSearching: false}) + this.props.pageController.setSearchText(undefined) + e.preventDefault() + } + } + + searchChanged(text?: string) { + this.props.pageController.setSearchText(text) + } } export { BoardComponent } diff --git a/src/client/octoTypes.ts b/src/client/octoTypes.ts index 1a3f994c2..026046329 100644 --- a/src/client/octoTypes.ts +++ b/src/client/octoTypes.ts @@ -26,6 +26,7 @@ interface IPageController { showCard(card: IBlock): Promise showView(viewId: string): void showFilter(anchorElement?: HTMLElement): void + setSearchText(text?: string): void } export { IProperty, IBlock, IPageController }