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..146a0ed2d 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.searchText?.toLocaleLowerCase() + if (!searchText) { return cards.slice() } + + return cards.filter(card => { + if (card.title?.toLocaleLowerCase().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..ba5343b1c 100644 --- a/src/client/components/boardComponent.tsx +++ b/src/client/components/boardComponent.tsx @@ -25,15 +25,23 @@ 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: !!this.props.boardTree?.getSearchText() } + } + + componentDidUpdate(prevPros: Props, prevState: State) { + if (this.state.isSearching && !prevState.isSearching) { + this.searchFieldRef.current.focus() + } } render() { @@ -87,9 +95,18 @@ 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 +397,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/components/editable.tsx b/src/client/components/editable.tsx index 5b7b1e028..0c388ff55 100644 --- a/src/client/components/editable.tsx +++ b/src/client/components/editable.tsx @@ -102,7 +102,6 @@ class Editable extends React.Component { this.text = newText this.elementRef.current.classList.remove("active") - if (onBlur) { onBlur() } }} diff --git a/src/client/components/tableComponent.tsx b/src/client/components/tableComponent.tsx index a7328af42..bd05ea44c 100644 --- a/src/client/components/tableComponent.tsx +++ b/src/client/components/tableComponent.tsx @@ -23,16 +23,24 @@ type Props = { type State = { isHoverOnCover: boolean + isSearching: boolean } class TableComponent extends React.Component { private draggedHeaderTemplate: IPropertyTemplate private cardIdToRowMap = new Map>() private cardIdToFocusOnRender: string + private searchFieldRef = React.createRef() constructor(props: Props) { super(props) - this.state = { isHoverOnCover: false } + this.state = { isHoverOnCover: false, isSearching: !!this.props.boardTree?.getSearchText() } + } + + componentDidUpdate(prevPros: Props, prevState: State) { + if (this.state.isSearching && !prevState.isSearching) { + this.searchFieldRef.current.focus() + } } render() { @@ -83,7 +91,16 @@ class TableComponent extends React.Component {
{ this.propertiesClicked(e) }}>Properties
{ this.filterClicked(e) }}>Filter
{ this.sortClicked(e) }}>Sort
-
Search
+ {this.state.isSearching + ? { this.searchChanged(text) }} + onKeyDown={(e) => { this.onSearchKeyDown(e) }}> + :
{ this.setState({ ...this.state, isSearching: true }) }}>Search
+ }
this.optionsClicked(e)}>
{ this.addCard(true) }}>New
@@ -401,6 +418,19 @@ class TableComponent extends React.Component { const destIndex = template ? board.cardProperties.indexOf(template) : 0 await mutator.changePropertyTemplateOrder(board, draggedHeaderTemplate, 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 { TableComponent } 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 }