mirror of
https://github.com/mattermost/focalboard.git
synced 2024-12-24 13:43:12 +02:00
Shift+Click to mutl-select cards, drag multiple
This commit is contained in:
parent
a8a274ff0f
commit
3dfeeeba6d
@ -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}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user