Shift+Click to mutl-select cards, drag multiple

This commit is contained in:
Chen-I Lim 2020-10-21 15:58:41 -07:00
parent a8a274ff0f
commit 3dfeeeba6d
3 changed files with 87 additions and 19 deletions

View file

@ -14,6 +14,7 @@ import {Utils} from '../utils'
type BoardCardProps = {
card: Card
visiblePropertyTemplates: IPropertyTemplate[]
isSelected: boolean
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void
onDragStart?: (e: React.DragEvent<HTMLDivElement>) => void
onDragEnd?: (e: React.DragEvent<HTMLDivElement>) => void
@ -33,9 +34,11 @@ class BoardCard extends React.Component<BoardCardProps, BoardCardState> {
const {card} = this.props
const optionsButtonRef = React.createRef<HTMLDivElement>()
const visiblePropertyTemplates = this.props.visiblePropertyTemplates || []
const className = this.props.isSelected ? 'octo-board-card selected' : 'octo-board-card'
const element =
(<div
className='octo-board-card'
className={className}
draggable={true}
style={{opacity: this.state.isDragged ? 0.5 : 1}}
onClick={this.props.onClick}

View file

@ -36,16 +36,44 @@ type State = {
shownCard?: Card
viewMenu: boolean
isHoverOnCover: boolean
selectedCards: Card[]
}
class BoardComponent extends React.Component<Props, State> {
private draggedCard: Card
private draggedCards: Card[] = []
private draggedHeaderOption: IPropertyOption
private backgroundRef = React.createRef<HTMLDivElement>()
private searchFieldRef = React.createRef<Editable>()
private keydownHandler = (e: KeyboardEvent) => {
if (e.target !== document.body) {
return
}
if (e.keyCode === 27) {
if (this.state.selectedCards.length > 0) {
this.setState({selectedCards: []})
e.stopPropagation()
}
}
}
componentDidMount() {
document.addEventListener('keydown', this.keydownHandler)
}
componentWillUnmount() {
document.removeEventListener('keydown', this.keydownHandler)
}
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,
selectedCards: [],
}
}
componentDidUpdate(prevPros: Props, prevState: State) {
@ -73,7 +101,13 @@ class BoardComponent extends React.Component<Props, State> {
const hasSort = activeView.sortOptions.length > 0
return (
<div className='octo-app'>
<div
className='octo-app'
ref={this.backgroundRef}
onClick={(e) => {
this.backgroundClicked(e)
}}
>
{this.state.shownCard &&
<RootPortal>
<CardDialog
@ -332,14 +366,15 @@ class BoardComponent extends React.Component<Props, State> {
card={card}
visiblePropertyTemplates={visiblePropertyTemplates}
key={card.id}
onClick={() => {
this.setState({shownCard: card})
isSelected={this.state.selectedCards.includes(card)}
onClick={(e) => {
this.cardClicked(e, card)
}}
onDragStart={() => {
this.draggedCard = card
this.draggedCards = this.state.selectedCards.includes(card) ? this.state.selectedCards : [card]
}}
onDragEnd={() => {
this.draggedCard = undefined
this.draggedCards = []
}}
/>),
)}
@ -364,14 +399,15 @@ class BoardComponent extends React.Component<Props, State> {
card={card}
visiblePropertyTemplates={visiblePropertyTemplates}
key={card.id}
onClick={() => {
this.setState({shownCard: card})
isSelected={this.state.selectedCards.includes(card)}
onClick={(e) => {
this.cardClicked(e, card)
}}
onDragStart={() => {
this.draggedCard = card
this.draggedCards = this.state.selectedCards.includes(card) ? this.state.selectedCards : [card]
}}
onDragEnd={() => {
this.draggedCard = undefined
this.draggedCards = []
}}
/>),
)}
@ -389,6 +425,13 @@ class BoardComponent extends React.Component<Props, State> {
)
}
private backgroundClicked(e: React.MouseEvent) {
if (this.state.selectedCards.length > 0) {
this.setState({selectedCards: []})
e.stopPropagation()
}
}
async addCard(groupByValue?: string) {
const {boardTree} = this.props
const {activeView, board} = boardTree
@ -524,6 +567,22 @@ class BoardComponent extends React.Component<Props, State> {
OldMenu.shared.showAtElement(e.target as HTMLElement)
}
private cardClicked(e: React.MouseEvent, card: Card) {
if (e.shiftKey) {
let selectedCards = this.state.selectedCards.slice()
if (selectedCards.includes(card)) {
selectedCards = selectedCards.filter((o) => o != card)
} else {
selectedCards.push(card)
}
this.setState({selectedCards})
} else {
this.setState({selectedCards: [], shownCard: card})
}
e.stopPropagation()
}
async addGroupClicked() {
console.log('onAddGroupClicked')
@ -540,17 +599,19 @@ class BoardComponent extends React.Component<Props, State> {
async onDropToColumn(option: IPropertyOption) {
const {boardTree} = this.props
const {draggedCard, draggedHeaderOption} = this
const {draggedCards, draggedHeaderOption} = this
const propertyValue = option ? option.value : undefined
Utils.assertValue(mutator)
Utils.assertValue(boardTree)
if (draggedCard) {
Utils.log(`ondrop. Card: ${draggedCard.title}, column: ${propertyValue}`)
const oldValue = draggedCard.properties[boardTree.groupByProperty.id]
if (propertyValue !== oldValue) {
await mutator.changePropertyValue(draggedCard, boardTree.groupByProperty.id, propertyValue, 'drag card')
if (draggedCards.length > 0) {
for (const draggedCard of draggedCards) {
Utils.log(`ondrop. Card: ${draggedCard.title}, column: ${propertyValue}`)
const oldValue = draggedCard.properties[boardTree.groupByProperty.id]
if (propertyValue !== oldValue) {
await mutator.changePropertyValue(draggedCard, boardTree.groupByProperty.id, propertyValue, 'drag card')
}
}
} else if (draggedHeaderOption) {
Utils.log(`ondrop. Header option: ${draggedHeaderOption.value}, column: ${propertyValue}`)
@ -568,7 +629,7 @@ class BoardComponent extends React.Component<Props, State> {
onSearchKeyDown(e: React.KeyboardEvent) {
if (e.keyCode === 27) { // ESC: Clear search
this.searchFieldRef.current.text = ''
this.setState({...this.state, isSearching: false})
this.setState({isSearching: false})
this.props.setSearchText(undefined)
e.preventDefault()
}

View file

@ -237,6 +237,10 @@ hr {
transition: background 100ms ease-out 0s;
}
.octo-board-card.selected {
background-color: rgba(90, 200, 255, 0.2);
}
.octo-board-card > div {
margin-bottom: 3px;
}