Moving show filter to where is used

This commit is contained in:
Jesús Espino 2020-10-24 09:12:09 +02:00
parent 3859ad30b1
commit 6e51954866
5 changed files with 98 additions and 107 deletions

View file

@ -25,11 +25,11 @@ import Button from './button'
import {CardDialog} from './cardDialog'
import {Editable} from './editable'
import RootPortal from './rootPortal'
import {FilterComponent} from './filterComponent'
type Props = {
boardTree?: BoardTree
showView: (id: string) => void
showFilter: (el: HTMLElement) => void
setSearchText: (text: string) => void
}
@ -39,6 +39,7 @@ type State = {
viewMenu: boolean
isHoverOnCover: boolean
selectedCards: Card[]
showFilter: boolean
}
class BoardComponent extends React.Component<Props, State> {
@ -75,6 +76,7 @@ class BoardComponent extends React.Component<Props, State> {
isSearching: Boolean(this.props.boardTree?.getSearchText()),
viewMenu: false,
selectedCards: [],
showFilter: false,
}
}
@ -256,10 +258,19 @@ class BoardComponent extends React.Component<Props, State> {
</MenuWrapper>
<div
className={hasFilter ? 'octo-button active' : 'octo-button'}
onClick={(e) => {
this.filterClicked(e)
}}
>Filter</div>
style={{position: 'relative', overflow: 'unset'}}
onClick={this.filterClicked}
>
<FormattedMessage
id='TableComponent.filter'
defaultMessage='Filter'
/>
{this.state.showFilter &&
<FilterComponent
boardTree={boardTree}
onClose={this.hideFilter}
/>}
</div>
<MenuWrapper>
<div className={hasSort ? 'octo-button active' : 'octo-button'}>
<FormattedMessage
@ -644,8 +655,12 @@ class BoardComponent extends React.Component<Props, State> {
await mutator.changePropertyOptionValue(boardTree, boardTree.groupByProperty, option, text)
}
private filterClicked(e: React.MouseEvent) {
this.props.showFilter(e.target as HTMLElement)
private filterClicked = () => {
this.setState({showFilter: true})
}
private hideFilter = () => {
this.setState({showFilter: false})
}
private async testAddCards(count: number) {

View file

@ -13,74 +13,80 @@ import {Utils} from '../utils'
type Props = {
boardTree: BoardTree
pageX: number
pageY: number
onClose: () => void
}
class FilterComponent extends React.Component<Props> {
shouldComponentUpdate(): boolean {
return true
private node: React.RefObject<HTMLDivElement>
public constructor(props: Props) {
super(props)
this.node = React.createRef()
}
public componentDidMount() {
document.addEventListener('click', this.closeOnBlur, true)
}
public componentWillUnmount() {
document.removeEventListener('click', this.closeOnBlur, true)
}
private closeOnBlur = (e: Event) => {
if (this.node && this.node.current && e.target && this.node.current.contains(e.target as Node)) {
return
}
this.props.onClose()
}
render(): JSX.Element {
const {boardTree} = this.props
const {board, activeView} = boardTree
const backgroundRef = React.createRef<HTMLDivElement>()
// TODO: Handle FilterGroups (compound filter statements)
const filters: FilterClause[] = activeView.filter?.filters.filter((o) => !FilterGroup.isAnInstanceOf(o)) as FilterClause[] || []
return (
<div
className='octo-modal-back'
ref={backgroundRef}
onClick={(e) => {
if (e.target === backgroundRef.current) {
this.props.onClose()
}
}}
className='octo-modal octo-filter-dialog'
style={{position: 'absolute', top: 25, left: -200}}
ref={this.node}
>
{filters.map((filter) => {
const template = board.cardProperties.find((o) => o.id === filter.propertyId)
const propertyName = template ? template.name : '(unknown)' // TODO: Handle error
const key = `${filter.propertyId}-${filter.condition}-${filter.values.join(',')}`
Utils.log(`FilterClause key: ${key}`)
return (<div
className='octo-filterclause'
key={key}
>
<div
className='octo-button'
onClick={(e) => this.propertyClicked(e, filter)}
>{propertyName}</div>
<div
className='octo-button'
onClick={(e) => this.conditionClicked(e, filter)}
>{FilterClause.filterConditionDisplayString(filter.condition)}</div>
{
this.filterValue(filter, template)
}
<div className='octo-spacer'/>
<div
className='octo-button'
onClick={() => this.deleteClicked(filter)}
>Delete</div>
</div>)
})}
<br/>
<div
className='octo-modal octo-filter-dialog'
style={{position: 'absolute', left: this.props.pageX, top: this.props.pageY}}
>
{filters.map((filter) => {
const template = board.cardProperties.find((o) => o.id === filter.propertyId)
const propertyName = template ? template.name : '(unknown)' // TODO: Handle error
const key = `${filter.propertyId}-${filter.condition}-${filter.values.join(',')}`
Utils.log(`FilterClause key: ${key}`)
return (<div
className='octo-filterclause'
key={key}
>
<div
className='octo-button'
onClick={(e) => this.propertyClicked(e, filter)}
>{propertyName}</div>
<div
className='octo-button'
onClick={(e) => this.conditionClicked(e, filter)}
>{FilterClause.filterConditionDisplayString(filter.condition)}</div>
{
this.filterValue(filter, template)
}
<div className='octo-spacer'/>
<div
className='octo-button'
onClick={() => this.deleteClicked(filter)}
>Delete</div>
</div>)
})}
<br/>
<div
className='octo-button'
onClick={() => this.addFilterClicked()}
>+ Add Filter</div>
</div>
className='octo-button'
onClick={() => this.addFilterClicked()}
>+ Add Filter</div>
</div>
)
}

View file

@ -23,11 +23,11 @@ import {CardDialog} from './cardDialog'
import {Editable} from './editable'
import RootPortal from './rootPortal'
import {TableRow} from './tableRow'
import {FilterComponent} from './filterComponent'
type Props = {
boardTree?: BoardTree
showView: (id: string) => void
showFilter: (el: HTMLElement) => void
setSearchText: (text: string) => void
}
@ -36,6 +36,7 @@ type State = {
isSearching: boolean
shownCard?: Card
viewMenu: boolean
showFilter: boolean
}
class TableComponent extends React.Component<Props, State> {
@ -46,7 +47,7 @@ class TableComponent extends React.Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = {isHoverOnCover: false, isSearching: Boolean(this.props.boardTree?.getSearchText()), viewMenu: false}
this.state = {isHoverOnCover: false, isSearching: Boolean(this.props.boardTree?.getSearchText()), viewMenu: false, showFilter: false}
}
shouldComponentUpdate(): boolean {
@ -219,14 +220,18 @@ class TableComponent extends React.Component<Props, State> {
</MenuWrapper>
<div
className={hasFilter ? 'octo-button active' : 'octo-button'}
onClick={(e) => {
this.filterClicked(e)
}}
style={{position: 'relative', overflow: 'unset'}}
onClick={this.filterClicked}
>
<FormattedMessage
id='TableComponent.filter'
defaultMessage='Filter'
/>
{this.state.showFilter &&
<FilterComponent
boardTree={boardTree}
onClose={this.hideFilter}
/>}
</div>
<MenuWrapper>
<div className={hasSort ? 'octo-button active' : 'octo-button'}>
@ -457,8 +462,12 @@ class TableComponent extends React.Component<Props, State> {
)
}
private filterClicked(e: React.MouseEvent) {
this.props.showFilter(e.target as HTMLElement)
private filterClicked = () => {
this.setState({showFilter: true})
}
private hideFilter = () => {
this.setState({showFilter: false})
}
private async headerClicked(e: React.MouseEvent<HTMLDivElement>, templateId: string) {

View file

@ -15,7 +15,6 @@ type Props = {
boardTree?: BoardTree
showBoard: (id: string) => void
showView: (id: string, boardId?: string) => void
showFilter: (el: HTMLElement) => void
setSearchText: (text: string) => void
setLanguage: (lang: string) => void
}
@ -41,7 +40,7 @@ class WorkspaceComponent extends React.Component<Props> {
}
private mainComponent() {
const {boardTree, showFilter, setSearchText, showView} = this.props
const {boardTree, setSearchText, showView} = this.props
const {activeView} = boardTree || {}
if (!activeView) {
@ -52,19 +51,17 @@ class WorkspaceComponent extends React.Component<Props> {
case 'board': {
return (<BoardComponent
boardTree={boardTree}
showFilter={showFilter}
setSearchText={setSearchText}
showView={showView}
/>)
/>)
}
case 'table': {
return (<TableComponent
boardTree={boardTree}
showFilter={showFilter}
setSearchText={setSearchText}
showView={showView}
/>)
/>)
}
default: {

View file

@ -22,7 +22,6 @@ type State = {
viewId: string
workspaceTree: MutableWorkspaceTree
boardTree?: MutableBoardTree
filterAnchorElement?: HTMLElement
}
export default class BoardPage extends React.Component<Props, State> {
@ -112,34 +111,6 @@ export default class BoardPage extends React.Component<Props, State> {
render(): JSX.Element {
const {workspaceTree} = this.state
if (this.state.filterAnchorElement) {
const element = this.state.filterAnchorElement
const bodyRect = document.body.getBoundingClientRect()
const rect = element.getBoundingClientRect()
// Show at bottom-left of element
const maxX = bodyRect.right - 420 - 100
const pageX = Math.min(maxX, rect.left - bodyRect.left)
const pageY = rect.bottom - bodyRect.top
ReactDOM.render(
<FilterComponent
boardTree={this.state.boardTree}
pageX={pageX}
pageY={pageY}
onClose={() => {
this.showFilter(undefined)
}}
/>,
Utils.getElementById('modal'),
)
} else {
const modal = document.getElementById('modal')
if (modal) {
ReactDOM.render(<div/>, modal)
}
}
Utils.log(`BoardPage.render ${this.state.boardTree?.board?.title}`)
return (
<div className='BoardPage'>
@ -152,9 +123,6 @@ export default class BoardPage extends React.Component<Props, State> {
showBoard={(id) => {
this.showBoard(id)
}}
showFilter={(el) => {
this.showFilter(el)
}}
setSearchText={(text) => {
this.setSearchText(text)
}}
@ -230,10 +198,6 @@ export default class BoardPage extends React.Component<Props, State> {
window.history.pushState({path: newUrl}, '', newUrl)
}
showFilter(anchorElement?: HTMLElement): void {
this.setState({...this.state, filterAnchorElement: anchorElement})
}
setSearchText(text?: string): void {
this.state.boardTree?.setSearchText(text)
this.setState({...this.state, boardTree: this.state.boardTree})