mirror of
https://github.com/mattermost/focalboard.git
synced 2024-12-21 13:38:56 +02:00
making the grouped table list flat to prepare for virtualization
This commit is contained in:
parent
31b7734f85
commit
19007c486a
@ -188,27 +188,27 @@ const Table = (props: Props): JSX.Element => {
|
|||||||
{/* Table rows */}
|
{/* Table rows */}
|
||||||
<div className='table-row-container'>
|
<div className='table-row-container'>
|
||||||
{activeView.fields.groupById &&
|
{activeView.fields.groupById &&
|
||||||
visibleGroups.map((group) => {
|
visibleGroups.map((group) => {
|
||||||
return (
|
return (
|
||||||
<TableGroup
|
<TableGroup
|
||||||
key={group.option.id}
|
key={group.option.id}
|
||||||
board={board}
|
board={board}
|
||||||
activeView={activeView}
|
activeView={activeView}
|
||||||
groupByProperty={groupByProperty}
|
groupByProperty={groupByProperty}
|
||||||
group={group}
|
group={group}
|
||||||
readonly={props.readonly || !canEditCards}
|
readonly={props.readonly || !canEditCards}
|
||||||
selectedCardIds={props.selectedCardIds}
|
selectedCardIds={props.selectedCardIds}
|
||||||
cardIdToFocusOnRender={props.cardIdToFocusOnRender}
|
cardIdToFocusOnRender={props.cardIdToFocusOnRender}
|
||||||
hideGroup={hideGroup}
|
hideGroup={hideGroup}
|
||||||
addCard={props.addCard}
|
addCard={props.addCard}
|
||||||
showCard={props.showCard}
|
showCard={props.showCard}
|
||||||
propertyNameChanged={propertyNameChanged}
|
propertyNameChanged={propertyNameChanged}
|
||||||
onCardClicked={props.onCardClicked}
|
onCardClicked={props.onCardClicked}
|
||||||
onDropToGroupHeader={onDropToGroupHeader}
|
onDropToGroupHeader={onDropToGroupHeader}
|
||||||
onDropToCard={onDropToCard}
|
onDropToCard={onDropToCard}
|
||||||
onDropToGroup={onDropToGroup}
|
onDropToGroup={onDropToGroup}
|
||||||
/>)
|
/>)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
{/* No Grouping, Rows, one per card */}
|
{/* No Grouping, Rows, one per card */}
|
||||||
@ -224,6 +224,7 @@ const Table = (props: Props): JSX.Element => {
|
|||||||
addCard={props.addCard}
|
addCard={props.addCard}
|
||||||
onCardClicked={props.onCardClicked}
|
onCardClicked={props.onCardClicked}
|
||||||
onDrop={onDropToCard}
|
onDrop={onDropToCard}
|
||||||
|
useVirtualizedList={true}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -32,31 +32,9 @@ type Props = {
|
|||||||
|
|
||||||
const TableGroup = (props: Props): JSX.Element => {
|
const TableGroup = (props: Props): JSX.Element => {
|
||||||
const {board, activeView, group, onDropToGroup, groupByProperty} = props
|
const {board, activeView, group, onDropToGroup, groupByProperty} = props
|
||||||
const groupId = group.option.id
|
|
||||||
|
|
||||||
const [{isOver}, drop] = useDrop(() => ({
|
|
||||||
accept: 'card',
|
|
||||||
collect: (monitor) => ({
|
|
||||||
isOver: monitor.isOver(),
|
|
||||||
}),
|
|
||||||
drop: (item: Card, monitor) => {
|
|
||||||
if (monitor.isOver({shallow: true})) {
|
|
||||||
onDropToGroup(item, groupId, '')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}), [onDropToGroup, groupId])
|
|
||||||
|
|
||||||
let className = 'octo-table-group'
|
|
||||||
if (isOver) {
|
|
||||||
className += ' dragover'
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<>
|
||||||
ref={drop}
|
|
||||||
className={className}
|
|
||||||
key={group.option.id}
|
|
||||||
>
|
|
||||||
<TableGroupHeaderRow
|
<TableGroupHeaderRow
|
||||||
group={group}
|
group={group}
|
||||||
board={board}
|
board={board}
|
||||||
@ -67,6 +45,8 @@ const TableGroup = (props: Props): JSX.Element => {
|
|||||||
readonly={props.readonly}
|
readonly={props.readonly}
|
||||||
propertyNameChanged={props.propertyNameChanged}
|
propertyNameChanged={props.propertyNameChanged}
|
||||||
onDrop={props.onDropToGroupHeader}
|
onDrop={props.onDropToGroupHeader}
|
||||||
|
key={group.option.id}
|
||||||
|
onDropToGroup={onDropToGroup}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{(group.cards.length > 0) &&
|
{(group.cards.length > 0) &&
|
||||||
@ -81,8 +61,10 @@ const TableGroup = (props: Props): JSX.Element => {
|
|||||||
addCard={props.addCard}
|
addCard={props.addCard}
|
||||||
onCardClicked={props.onCardClicked}
|
onCardClicked={props.onCardClicked}
|
||||||
onDrop={props.onDropToCard}
|
onDrop={props.onDropToCard}
|
||||||
|
useVirtualizedList={false}
|
||||||
/>}
|
/>}
|
||||||
</div>
|
</>
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@ test('should match snapshot, no groups', async () => {
|
|||||||
type: 'text',
|
type: 'text',
|
||||||
options: [{id: 'property1', value: 'Property 1', color: ''}],
|
options: [{id: 'property1', value: 'Property 1', color: ''}],
|
||||||
}}
|
}}
|
||||||
|
onDropToGroup={jest.fn()}
|
||||||
/>
|
/>
|
||||||
</Wrapper>,
|
</Wrapper>,
|
||||||
)
|
)
|
||||||
@ -89,6 +90,7 @@ test('should match snapshot with Group', async () => {
|
|||||||
addCard={jest.fn()}
|
addCard={jest.fn()}
|
||||||
propertyNameChanged={jest.fn()}
|
propertyNameChanged={jest.fn()}
|
||||||
onDrop={jest.fn()}
|
onDrop={jest.fn()}
|
||||||
|
onDropToGroup={jest.fn()}
|
||||||
/>
|
/>
|
||||||
</Wrapper>,
|
</Wrapper>,
|
||||||
)
|
)
|
||||||
@ -107,6 +109,7 @@ test('should match snapshot on read only', async () => {
|
|||||||
addCard={jest.fn()}
|
addCard={jest.fn()}
|
||||||
propertyNameChanged={jest.fn()}
|
propertyNameChanged={jest.fn()}
|
||||||
onDrop={jest.fn()}
|
onDrop={jest.fn()}
|
||||||
|
onDropToGroup={jest.fn()}
|
||||||
/>
|
/>
|
||||||
</Wrapper>,
|
</Wrapper>,
|
||||||
)
|
)
|
||||||
@ -130,6 +133,7 @@ test('should match snapshot, hide group', async () => {
|
|||||||
addCard={jest.fn()}
|
addCard={jest.fn()}
|
||||||
propertyNameChanged={jest.fn()}
|
propertyNameChanged={jest.fn()}
|
||||||
onDrop={jest.fn()}
|
onDrop={jest.fn()}
|
||||||
|
onDropToGroup={jest.fn()}
|
||||||
/>
|
/>
|
||||||
</Wrapper>,
|
</Wrapper>,
|
||||||
)
|
)
|
||||||
@ -158,6 +162,7 @@ test('should match snapshot, add new', async () => {
|
|||||||
addCard={addNew}
|
addCard={addNew}
|
||||||
propertyNameChanged={jest.fn()}
|
propertyNameChanged={jest.fn()}
|
||||||
onDrop={jest.fn()}
|
onDrop={jest.fn()}
|
||||||
|
onDropToGroup={jest.fn()}
|
||||||
/>
|
/>
|
||||||
</Wrapper>,
|
</Wrapper>,
|
||||||
)
|
)
|
||||||
@ -184,6 +189,7 @@ test('should match snapshot, edit title', async () => {
|
|||||||
addCard={jest.fn()}
|
addCard={jest.fn()}
|
||||||
propertyNameChanged={jest.fn()}
|
propertyNameChanged={jest.fn()}
|
||||||
onDrop={jest.fn()}
|
onDrop={jest.fn()}
|
||||||
|
onDropToGroup={jest.fn()}
|
||||||
/>
|
/>
|
||||||
</Wrapper>,
|
</Wrapper>,
|
||||||
)
|
)
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
/* eslint-disable max-lines */
|
/* eslint-disable max-lines */
|
||||||
import React, {useState, useEffect} from 'react'
|
import React, {useState, useEffect, useRef} from 'react'
|
||||||
import {FormattedMessage, useIntl} from 'react-intl'
|
import {FormattedMessage, useIntl} from 'react-intl'
|
||||||
|
import {useDrag, useDrop} from 'react-dnd'
|
||||||
|
|
||||||
import {Constants} from '../../constants'
|
import {Constants} from '../../constants'
|
||||||
import {IPropertyOption, Board, IPropertyTemplate, BoardGroup} from '../../blocks/board'
|
import {IPropertyOption, Board, IPropertyTemplate, BoardGroup} from '../../blocks/board'
|
||||||
import {BoardView} from '../../blocks/boardView'
|
import {BoardView} from '../../blocks/boardView'
|
||||||
import {useSortable} from '../../hooks/sortable'
|
import {Card} from '../../blocks/card'
|
||||||
import mutator from '../../mutator'
|
import mutator from '../../mutator'
|
||||||
import Button from '../../widgets/buttons/button'
|
import Button from '../../widgets/buttons/button'
|
||||||
import IconButton from '../../widgets/buttons/iconButton'
|
import IconButton from '../../widgets/buttons/iconButton'
|
||||||
@ -33,13 +34,42 @@ type Props = {
|
|||||||
addCard: (groupByOptionId?: string) => Promise<void>
|
addCard: (groupByOptionId?: string) => Promise<void>
|
||||||
propertyNameChanged: (option: IPropertyOption, text: string) => Promise<void>
|
propertyNameChanged: (option: IPropertyOption, text: string) => Promise<void>
|
||||||
onDrop: (srcOption: IPropertyOption, dstOption?: IPropertyOption) => void
|
onDrop: (srcOption: IPropertyOption, dstOption?: IPropertyOption) => void
|
||||||
|
onDropToGroup: (srcCard: Card, groupID: string, dstCardID: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const TableGroupHeaderRow = (props: Props): JSX.Element => {
|
const TableGroupHeaderRow = (props: Props): JSX.Element => {
|
||||||
const {board, activeView, group, groupByProperty} = props
|
const {board, activeView, group, groupByProperty} = props
|
||||||
const [groupTitle, setGroupTitle] = useState(group.option.value)
|
const [groupTitle, setGroupTitle] = useState(group.option.value)
|
||||||
|
|
||||||
const [isDragging, isOver, groupHeaderRef] = useSortable('groupHeader', group.option, !props.readonly, props.onDrop)
|
const ref = useRef<HTMLDivElement>(null)
|
||||||
|
const [{isDragging}, drag] = useDrag(() => ({
|
||||||
|
type: 'groupHeader',
|
||||||
|
item: group.option,
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isDragging: monitor.isDragging(),
|
||||||
|
}),
|
||||||
|
canDrag: () => !props.readonly,
|
||||||
|
}), ['groupHeader', group.option, props.readonly])
|
||||||
|
|
||||||
|
const [{isOver}, drop] = useDrop(() => ({
|
||||||
|
accept: ['groupHeader', 'card'],
|
||||||
|
collect: (monitor) => ({
|
||||||
|
isOver: monitor.isOver(),
|
||||||
|
}),
|
||||||
|
drop: (dragItem: IPropertyOption | Card, monitor) => {
|
||||||
|
// @ts-ignore
|
||||||
|
if (dragItem?.type === 'card' && monitor.isOver({shallow: true})) {
|
||||||
|
// @ts-ignore
|
||||||
|
props.onDropToGroup(dragItem, group.option.id, '')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
props.onDrop(dragItem, group.option)
|
||||||
|
},
|
||||||
|
canDrop: () => !props.readonly,
|
||||||
|
}), [group.option, props.onDrop, props.readonly])
|
||||||
|
drop(drag(ref))
|
||||||
|
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const columnResize = useColumnResize()
|
const columnResize = useColumnResize()
|
||||||
|
|
||||||
@ -59,7 +89,7 @@ const TableGroupHeaderRow = (props: Props): JSX.Element => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={group.option.id + 'header'}
|
key={group.option.id + 'header'}
|
||||||
ref={groupHeaderRef}
|
ref={ref}
|
||||||
style={{opacity: isDragging ? 0.5 : 1}}
|
style={{opacity: isDragging ? 0.5 : 1}}
|
||||||
className={className}
|
className={className}
|
||||||
>
|
>
|
||||||
|
@ -74,6 +74,7 @@ describe('components/table/TableRows', () => {
|
|||||||
addCard={addCard}
|
addCard={addCard}
|
||||||
onCardClicked={jest.fn()}
|
onCardClicked={jest.fn()}
|
||||||
onDrop={jest.fn()}
|
onDrop={jest.fn()}
|
||||||
|
useVirtualizedList={false}
|
||||||
/>
|
/>
|
||||||
</ColumnResizeProvider>
|
</ColumnResizeProvider>
|
||||||
</ReduxProvider>,
|
</ReduxProvider>,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
import React, {useCallback} from 'react'
|
import React, {useCallback} from 'react'
|
||||||
import {FixedSizeList, ListChildComponentProps} from 'react-window';
|
import {FixedSizeList, ListChildComponentProps} from 'react-window'
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
import AutoSizer from 'react-virtualized-auto-sizer'
|
||||||
|
|
||||||
import {Card} from '../../blocks/card'
|
import {Card} from '../../blocks/card'
|
||||||
import {Board} from '../../blocks/board'
|
import {Board} from '../../blocks/board'
|
||||||
@ -23,21 +23,50 @@ type Props = {
|
|||||||
addCard: (groupByOptionId?: string) => Promise<void>
|
addCard: (groupByOptionId?: string) => Promise<void>
|
||||||
onCardClicked: (e: React.MouseEvent, card: Card) => void
|
onCardClicked: (e: React.MouseEvent, card: Card) => void
|
||||||
onDrop: (srcCard: Card, dstCard: Card) => void
|
onDrop: (srcCard: Card, dstCard: Card) => void
|
||||||
|
useVirtualizedList: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const TableRows = (props: Props): JSX.Element => {
|
const TableRows = (props: Props): JSX.Element => {
|
||||||
const {board, cards, activeView} = props
|
const {board, cards, activeView, useVirtualizedList} = props
|
||||||
|
|
||||||
const onClickRow = useCallback((e: React.MouseEvent<HTMLDivElement>, card: Card) => {
|
const onClickRow = useCallback((e: React.MouseEvent<HTMLDivElement>, card: Card) => {
|
||||||
props.onCardClicked(e, card)
|
props.onCardClicked(e, card)
|
||||||
}, [props.onCardClicked])
|
}, [props.onCardClicked])
|
||||||
|
|
||||||
|
if (!useVirtualizedList) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{cards.map((card, idx) => {
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
key={card.id + card.updateAt}
|
||||||
|
board={board}
|
||||||
|
columnWidths={activeView.fields.columnWidths}
|
||||||
|
isManualSort={activeView.fields.sortOptions.length === 0}
|
||||||
|
groupById={activeView.fields.groupById}
|
||||||
|
visiblePropertyIds={activeView.fields.visiblePropertyIds}
|
||||||
|
collapsedOptionIds={activeView.fields.collapsedOptionIds}
|
||||||
|
card={card}
|
||||||
|
addCard={props.addCard}
|
||||||
|
isSelected={props.selectedCardIds.includes(card.id)}
|
||||||
|
focusOnMount={props.cardIdToFocusOnRender === card.id}
|
||||||
|
isLastCard={idx === (cards.length - 1)}
|
||||||
|
onClick={onClickRow}
|
||||||
|
showCard={props.showCard}
|
||||||
|
readonly={props.readonly}
|
||||||
|
onDrop={props.onDrop}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const isItemLoaded = (index: number) => {
|
const isItemLoaded = (index: number) => {
|
||||||
return index < cards.length;
|
return index < cards.length;
|
||||||
};
|
}
|
||||||
|
|
||||||
const Item = ({index, style}: ListChildComponentProps) => {
|
const Item = ({index, style}: ListChildComponentProps) => {
|
||||||
|
|
||||||
const card = cards[index]
|
const card = cards[index]
|
||||||
if (isItemLoaded(index)) {
|
if (isItemLoaded(index)) {
|
||||||
return (
|
return (
|
||||||
@ -72,13 +101,13 @@ const TableRows = (props: Props): JSX.Element => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AutoSizer>
|
<AutoSizer disableWidth>
|
||||||
{({height, width}) => (
|
{({height}) => (
|
||||||
<FixedSizeList
|
<FixedSizeList
|
||||||
height={height}
|
height={height}
|
||||||
itemCount={1828}
|
itemCount={1828}
|
||||||
itemSize={44}
|
itemSize={44}
|
||||||
width={width}
|
width={'100%'}
|
||||||
>
|
>
|
||||||
{Item}
|
{Item}
|
||||||
</FixedSizeList>
|
</FixedSizeList>
|
||||||
|
Loading…
Reference in New Issue
Block a user