Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

182849961 table advance selection #1527

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion v3/src/components/case-table/collection-table-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getNumericCssVariable } from "../../utilities/css-utils"
import { symParent } from "../../models/data/data-set-types"

const kDefaultRowHeaderHeight = 30
const kDefaultRowHeight = 18
export const kDefaultRowHeight = 18
const kDefaultRowCount = 12
const kDefaultGridHeight = kDefaultRowHeaderHeight + (kDefaultRowCount * kDefaultRowHeight)

Expand Down
135 changes: 130 additions & 5 deletions v3/src/components/case-table/collection-table.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { comparer } from "mobx"
import { observer } from "mobx-react-lite"
import React, { useCallback, useEffect, useMemo, useRef } from "react"
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"
import DataGrid, { CellKeyboardEvent, DataGridHandle } from "react-data-grid"
import { kCollectionTableBodyDropZoneBaseId } from "./case-table-drag-drop"
import {
Expand Down Expand Up @@ -33,6 +33,9 @@
import { useCaseTableModel } from "./use-case-table-model"
import { useCollectionTableModel } from "./use-collection-table-model"
import { useWhiteSpaceClick } from "./use-white-space-click"
import { collectionCaseIdFromIndex, collectionCaseIndexFromId, selectCases, setSelectedCases }
from "../../models/data/data-set-utils"
import { kDefaultRowHeight } from "./collection-table-model"

import "react-data-grid/lib/styles.css"
import styles from "./case-table-shared.scss"
Expand Down Expand Up @@ -60,9 +63,14 @@
.getPropertyValue("--rdg-row-selected-background-color") || undefined
const visibleAttributes = useVisibleAttributes(collectionId)
const { selectedRows, setSelectedRows, handleCellClick } = useSelectedRows({ gridRef, onScrollClosestRowIntoView })
const { handleWhiteSpaceClick } = useWhiteSpaceClick({ gridRef })
const { clearCurrentSelection } = useWhiteSpaceClick({ gridRef })
const forceUpdate = useForceUpdate()
const { isTileSelected } = useTileModelContext()
const [isSelectDragging, setIsSelectDragging] = useState(false)
const [isDragging, setIsDragging] = useState(false) //This prevents the grid click handler from firing on mouse up
const [initialMousePosition, setInitialMousePosition] = useState({ x: 0, y: 0 })
const [lastMousePosition, setLastMousePosition] = useState({ x: 0, y: 0 }) // Track last mouse position
const [initialDirection, setInitialDirection] = useState<'up' | 'down' | null>(null)

useEffect(function setGridElement() {
const element = gridRef.current?.element
Expand Down Expand Up @@ -192,22 +200,139 @@
event.preventGridDefault()
navigateToNextRow(event.shiftKey)
}
if ((event.key === "ArrowDown" || event.key === "ArrowUp")) {
const caseId = args.row.__id__

Check warning on line 204 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L204

Added line #L204 was not covered by tests
const isCaseSelected = data?.isCaseSelected(caseId)
const isExtending = event.shiftKey || event.altKey || event.metaKey
const currentSelectionIdx = collectionCaseIndexFromId(caseId, data, collectionId)

Check warning on line 207 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L207

Added line #L207 was not covered by tests

if (currentSelectionIdx !== undefined && currentSelectionIdx !== null) {
eireland marked this conversation as resolved.
Show resolved Hide resolved
const nextIndex = event.key === "ArrowDown" ? currentSelectionIdx + 1 : currentSelectionIdx - 1
const nextCaseId = collectionCaseIdFromIndex(nextIndex, data, collectionId)

Check warning on line 211 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L211

Added line #L211 was not covered by tests
if (nextCaseId) {
const isNextCaseSelected = data?.isCaseSelected(nextCaseId)
if (isExtending) {
if (isNextCaseSelected) {
selectCases([caseId], data, !isCaseSelected)
} else {
selectCases([nextCaseId], data)

Check warning on line 218 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L216-L218

Added lines #L216 - L218 were not covered by tests
}
} else {
setSelectedCases([nextCaseId], data)

Check warning on line 221 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L220-L221

Added lines #L220 - L221 were not covered by tests
}
}
}
}
}

const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {
if (isDragging) {
setIsDragging(false)
return
}
// the grid element is the target when clicking outside the cells (otherwise, the cell is the target)
if (isTileSelected() && event.target === gridRef.current?.element) {
handleWhiteSpaceClick()
clearCurrentSelection()

Check warning on line 235 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L235

Added line #L235 was not covered by tests
}
}

const handleMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {
const isExtending = event.shiftKey || event.altKey || event.metaKey
setIsSelectDragging(true)
setInitialMousePosition({ x: event.clientX, y: event.clientY })
setLastMousePosition({ x: event.clientX, y: event.clientY }) // Initialize last mouse position
setInitialDirection(null) // Reset the initial direction

if (!isExtending) {
clearCurrentSelection() // clear current selection
}
const target = event.target as HTMLDivElement
const closestDataCell = target.closest('.codap-data-cell')
const className = closestDataCell ? closestDataCell.className : ""
const caseId = className.split(" ").find(c => c.startsWith("rowId-"))?.split("-")[1]
if (caseId) {
if (selectedRows.size > 0) {
selectCases([caseId], data)
} else {

Check warning on line 256 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L256

Added line #L256 was not covered by tests
setSelectedCases([caseId], data)
}
}
}

const handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
const mouseMoveThreshold = Math.max(kDefaultRowHeight / 3, 5)
if (isSelectDragging) {
setIsDragging(true)
// Check if the mouse is inside the grid's boundaries
const gridElement = gridRef.current?.element
if (gridElement) {
const gridBounds = gridElement.getBoundingClientRect()
const { clientX, clientY } = event
if (
clientX >= gridBounds.left &&
clientX <= gridBounds.right &&
clientY >= gridBounds.top &&
clientY <= gridBounds.bottom
) {
const yDiffFromInitial = clientY - initialMousePosition.y
const yDiffFromLast = clientY - lastMousePosition.y // Difference from the last mouse position
const currentDirection = yDiffFromLast > mouseMoveThreshold
? 'down'

Check warning on line 280 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L280

Added line #L280 was not covered by tests
: yDiffFromLast < -mouseMoveThreshold ? 'up'
: null
if (!initialDirection && Math.abs(yDiffFromInitial) > mouseMoveThreshold) {
setInitialDirection(currentDirection)

Check warning on line 284 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L284

Added line #L284 was not covered by tests
}

if (currentDirection) {
const target = event.target as HTMLDivElement
const closestDataCell = target.closest('.codap-data-cell')

Check warning on line 289 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L288-L289

Added lines #L288 - L289 were not covered by tests
const className = closestDataCell ? closestDataCell.className : ""
const caseId = className.split(" ").find(c => c.startsWith("rowId-"))?.split("-")[1]

if (caseId) {
const caseIndex = collectionCaseIndexFromId(caseId, data, collectionId)

Check warning on line 294 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L294

Added line #L294 was not covered by tests
// Reset the initial direction to allow for reverse selection in the case of a user first
// moving the mouse up and then down or vice versa past the initially selected case
if (
(initialDirection === 'down' && currentDirection === 'up' && clientY < initialMousePosition.y) ||
(initialDirection === 'up' && currentDirection === 'down' && clientY > initialMousePosition.y)

Check warning on line 299 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L299

Added line #L299 was not covered by tests
) {
setInitialDirection(currentDirection)

Check warning on line 301 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L301

Added line #L301 was not covered by tests
}

if (currentDirection === initialDirection) {
selectCases([caseId], data)
} else {

Check warning on line 306 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L305-L306

Added lines #L305 - L306 were not covered by tests
// Deselect if moving in the opposite direction
if (initialDirection === 'down') {
const nextCaseId = caseIndex && collectionCaseIdFromIndex(caseIndex + 1, data, collectionId)
nextCaseId && selectCases([nextCaseId], data, false)
} else if (initialDirection === 'up') {
const prevCaseId = caseIndex && collectionCaseIdFromIndex(caseIndex - 1, data, collectionId)
prevCaseId && selectCases([prevCaseId], data, false)
}
}
}
setLastMousePosition({ x: clientX, y: clientY })

Check warning on line 317 in v3/src/components/case-table/collection-table.tsx

View check run for this annotation

Codecov / codecov/patch

v3/src/components/case-table/collection-table.tsx#L317

Added line #L317 was not covered by tests
}
}
}
}
}

const handleMouseUp = (event: React.MouseEvent<HTMLDivElement>) => {
setIsSelectDragging(false)
}

if (!data || !rows || !visibleAttributes.length) return null

return (
<div className={`collection-table collection-${collectionId}`}>
<CollectionTableSpacer selectedFillColor={selectedFillColor}
onWhiteSpaceClick={handleWhiteSpaceClick} onDrop={handleNewCollectionDrop} />
<div className="collection-table-and-title" ref={setNodeRef} onClick={handleClick}>
onWhiteSpaceClick={clearCurrentSelection} onDrop={handleNewCollectionDrop} />
<div className="collection-table-and-title" ref={setNodeRef} onClick={handleClick} onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove} onMouseUp={handleMouseUp}>
<CollectionTitle onAddNewAttribute={handleAddNewAttribute} showCount={true} />
<DataGrid ref={gridRef} className="rdg-light" data-testid="collection-table-grid" renderers={renderers}
columns={columns} rows={rows} headerRowHeight={+styles.headerRowHeight} rowKeyGetter={rowKey}
Expand Down
2 changes: 1 addition & 1 deletion v3/src/components/case-table/use-columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const useColumns = ({ data, indexColumn }: IUseColumnsProps) => {
resizable: true,
headerCellClass: `codap-column-header`,
renderHeaderCell: ColumnHeader,
cellClass: "codap-data-cell",
cellClass: row => `codap-data-cell rowId-${row.__id__}`,
renderCell: AttributeValueCell,
editable: row => isCaseEditable(data, row.__id__),
renderEditCell: isEditable
Expand Down
4 changes: 2 additions & 2 deletions v3/src/components/case-table/use-white-space-click.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function useWhiteSpaceClick({ gridRef }: IProps) {
})
}, [componentRef, data])

const handleWhiteSpaceClick = useCallback(() => {
const clearCurrentSelection = useCallback(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I named this function handleWhiteSpaceClick originally is because a name like clearCurrentSelection implies rather strongly that calling it will clear the selection, but it doesn't actually change the selection under some very important circumstances. If you would like to have access to a function which actually clears the selection, then the clearCaseSelection function can be exported from this hook in addition to handleWhiteSpaceClick.

if (!wasFocusedTileRef.current && isFocusedTileRef.current) {
// Focused the table, do nothing with the selection
wasFocusedTileRef.current = true
Expand All @@ -45,5 +45,5 @@ export function useWhiteSpaceClick({ gridRef }: IProps) {
clearCaseSelection()
}, [clearCaseSelection])

return { handleWhiteSpaceClick }
return { clearCurrentSelection }
}
2 changes: 1 addition & 1 deletion v3/src/components/case-tile-common/index-menu-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const IndexMenuList = ({caseId, index}: IProps) => {
itemKey: `DG.CaseTable.indexMenu.delete${deletableSelectedItems.length === 1 ? "Case" : "Cases" }`,
isEnabled: () => deletableSelectedItems.length >= 1,
handleClick: () => {
if (data?.selection.size) {
if (deletableSelectedItems && data) {
removeCasesWithCustomUndoRedo(data, deletableSelectedItems)
}
}
Expand Down
Loading