selection

This commit is contained in:
Jan Prochazka
2020-03-21 10:28:43 +01:00
parent 16e46300be
commit 20933cec8b
2 changed files with 122 additions and 5 deletions

View File

@@ -11,6 +11,7 @@ import axios from '../utility/axios';
import ColumnLabel from './ColumnLabel';
import DataFilterControl from './DataFilterControl';
import { getFilterType } from '@dbgate/filterparser';
import { convertCellAddress, cellFromEvent, getCellRange } from './selection';
const GridContainer = styled.div`
position: absolute;
@@ -18,6 +19,7 @@ const GridContainer = styled.div`
top: 0;
right: 0;
bottom: 0;
user-select: none;
`;
const Table = styled.table`
@@ -73,6 +75,13 @@ const TableBodyCell = styled.td`
padding: 2px;
white-space: nowrap;
overflow: hidden;
${props =>
// @ts-ignore
props.isSelected &&
`
background: initial;
background-color: deepskyblue;
color: white;`}
`;
const HintSpan = styled.span`
color: gray;
@@ -105,6 +114,10 @@ export default function DataGridCore(props) {
const loadedTimeRef = React.useRef(0);
const [currentCell, setCurrentCell] = React.useState([undefined, undefined]);
const [selectedCells, setSelectedCells] = React.useState([]);
const [dragStartCell, setDragStartCell] = React.useState(null);
const loadNextData = async () => {
if (isLoading) return;
setLoadProps({
@@ -273,6 +286,42 @@ export default function DataGridCore(props) {
return columnSizes;
}
function handleGridMouseDown(event) {
const cell = cellFromEvent(event);
setCurrentCell(cell);
setSelectedCells(getCellRange(cell, cell));
setDragStartCell(cell);
console.log('START',cell);
}
function handleGridMouseMove(event) {
if (dragStartCell) {
const cell = cellFromEvent(event);
setCurrentCell(cell);
setSelectedCells(getCellRange(dragStartCell, cell));
}
}
function handleGridMouseUp(event) {
if (dragStartCell) {
const cell = cellFromEvent(event);
setCurrentCell(cell);
setSelectedCells(getCellRange(dragStartCell, cell));
setDragStartCell(null);
}
}
function cellIsSelected(row, col) {
const [currentRow, currentCol] = currentCell;
if (row == currentRow && col == currentCol) return true;
for (const [selectedRow, selectedCol] of selectedCells) {
if (row == selectedRow && col == selectedCol) return true;
if (selectedRow == 'header' && col == selectedCol) return true;
if (row == selectedRow && selectedCol == 'header') return true;
}
return false;
}
// console.log('visibleRowCountUpperBound', visibleRowCountUpperBound);
// console.log('gridScrollAreaHeight', gridScrollAreaHeight);
// console.log('containerHeight', containerHeight);
@@ -282,7 +331,7 @@ export default function DataGridCore(props) {
const visibleRealColumnIndexes = [];
const modelIndexes = {};
/** @type {(import('@dbgate/datalib').DisplayColumn & {widthPx: string})[]} */
/** @type {(import('@dbgate/datalib').DisplayColumn & {widthPx: string; colIndex: number})[]} */
const realColumns = [];
// frozen columns
@@ -308,6 +357,7 @@ export default function DataGridCore(props) {
const widthNumber = columnSizes.getSizeByRealIndex(colIndex);
realColumns.push({
...col,
colIndex,
widthPx: `${widthNumber}px`,
});
}
@@ -322,12 +372,14 @@ export default function DataGridCore(props) {
// console.log('visibleRealColumnIndexes', visibleRealColumnIndexes);
return (
<GridContainer ref={containerRef}>
<Table>
<Table onMouseDown={handleGridMouseDown} onMouseMove={handleGridMouseMove} onMouseUp={handleGridMouseUp}>
<TableHead>
<TableHeaderRow ref={headerRowRef}>
<TableHeaderCell />
<TableHeaderCell data-row="header" data-col="header" />
{realColumns.map(col => (
<TableHeaderCell
data-row="header"
data-col={col.colIndex}
key={col.uniqueName}
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
>
@@ -336,7 +388,11 @@ export default function DataGridCore(props) {
))}
</TableHeaderRow>
<TableHeaderRow>
<TableHeaderCell style={{ width: hederColwidthPx, minWidth: hederColwidthPx, maxWidth: hederColwidthPx }}>
<TableHeaderCell
style={{ width: hederColwidthPx, minWidth: hederColwidthPx, maxWidth: hederColwidthPx }}
data-row="filter"
data-col="header"
>
{filterCount > 0 && (
<button onClick={handleClearFilters}>
<i className="fas fa-times" />
@@ -347,6 +403,8 @@ export default function DataGridCore(props) {
<TableFilterCell
key={col.uniqueName}
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
data-row="filter"
data-col={col.colIndex}
>
<DataFilterControl
filterType={getFilterType(col.commonType ? col.commonType.typeCode : null)}
@@ -362,11 +420,17 @@ export default function DataGridCore(props) {
.slice(firstVisibleRowScrollIndex, firstVisibleRowScrollIndex + visibleRowCountUpperBound)
.map((row, index) => (
<TableBodyRow key={firstVisibleRowScrollIndex + index}>
<TableHeaderCell>{firstVisibleRowScrollIndex + index + 1}</TableHeaderCell>
<TableHeaderCell data-row={firstVisibleRowScrollIndex + index} data-col="header">
{firstVisibleRowScrollIndex + index + 1}
</TableHeaderCell>
{realColumns.map(col => (
<TableBodyCell
key={col.uniqueName}
style={{ width: col.widthPx, minWidth: col.widthPx, maxWidth: col.widthPx }}
data-row={firstVisibleRowScrollIndex + index}
data-col={col.colIndex}
// @ts-ignore
isSelected={cellIsSelected(firstVisibleRowScrollIndex + index, col.colIndex)}
>
<CellFormattedValue value={row[col.uniqueName]} />
{col.hintColumnName && <HintSpan>{row[col.hintColumnName]}</HintSpan>}

View File

@@ -0,0 +1,53 @@
import _ from 'lodash';
export type CellAddress = [number | 'header' | 'filter' | undefined, number | 'header' | undefined];
export function getCellRange(a: CellAddress, b: CellAddress): CellAddress[] {
const [rowA, colA] = a;
const [rowB, colB] = b;
if (_.isNumber(rowA) && _.isNumber(colA) && _.isNumber(rowB) && _.isNumber(colB)) {
const rowMin = Math.min(rowA, rowB);
const rowMax = Math.max(rowA, rowB);
const colMin = Math.min(colA, colB);
const colMax = Math.max(colA, colB);
const res = [];
for (let row = rowMin; row <= rowMax; row++) {
for (let col = colMin; col <= colMax; col++) {
res.push([row, col]);
}
}
return res;
}
if (rowA == 'header' && rowB == 'header' && _.isNumber(colA) && _.isNumber(colB)) {
const colMin = Math.min(colA, colB);
const colMax = Math.max(colA, colB);
const res = [];
for (let col = colMin; col <= colMax; col++) {
res.push(['header', col]);
}
return res;
}
if (colA == 'header' && colB == 'header' && _.isNumber(rowA) && _.isNumber(rowB)) {
const rowMin = Math.min(rowA, rowB);
const rowMax = Math.max(rowA, rowB);
const res = [];
for (let row = rowMin; row <= rowMax; row++) {
res.push([row, 'header']);
}
return res;
}
return [];
}
export function convertCellAddress(row, col): CellAddress {
const rowNumber = parseInt(row);
const colNumber = parseInt(col);
return [_.isNaN(rowNumber) ? row : rowNumber, _.isNaN(colNumber) ? col : colNumber];
}
export function cellFromEvent(event): CellAddress {
const cell = event.target.closest('td');
const col = cell.getAttribute('data-col');
const row = cell.getAttribute('data-row');
return convertCellAddress(row, col);
}