diff --git a/packages/datalib/src/ChangeSet.ts b/packages/datalib/src/ChangeSet.ts
index 652c7c2c7..033d97e0d 100644
--- a/packages/datalib/src/ChangeSet.ts
+++ b/packages/datalib/src/ChangeSet.ts
@@ -115,6 +115,54 @@ export function setChangeSetValue(
};
}
+// export function batchUpdateChangeSet(
+// changeSet: ChangeSet,
+// rowDefinitions: ChangeSetRowDefinition[],
+// dataRows: []
+// ): ChangeSet {
+// const res = {
+// updates: [...changeSet.updates],
+// deletes: [...changeSet.deletes],
+// inserts: [...changeSet.inserts],
+// };
+// const rowItems: ChangeSetItem[] = rowDefinitions.map(definition => {
+// let [field, item] = findExistingChangeSetItem(res, definition);
+// let createUpdate = false;
+// if (field == 'deletes') {
+// res.deletes = res.deletes.filter(x => x != item);
+// createUpdate = true;
+// }
+// if (field == 'updates' && item == null) {
+// item = {
+// ...definition,
+// fields: {},
+// };
+// res.updates.push(item);
+// }
+// return item;
+// });
+// for (const tuple in _.zip(rowItems, dataRows)) {
+// const [definition, dataRow] = tuple;
+// for
+// }
+// return res;
+// }
+
+export function batchUpdateChangeSet(
+ changeSet: ChangeSet,
+ rowDefinitions: ChangeSetRowDefinition[],
+ dataRows: []
+): ChangeSet {
+ // console.log('batchUpdateChangeSet', changeSet, rowDefinitions, dataRows);
+ for (const tuple of _.zip(rowDefinitions, dataRows)) {
+ const [definition, dataRow] = tuple;
+ for (const key of _.keys(dataRow)) {
+ changeSet = setChangeSetValue(changeSet, { ...definition, columnName: key, uniqueName: key }, dataRow[key]);
+ }
+ }
+ return changeSet;
+}
+
function extractFields(item: ChangeSetItem): UpdateField[] {
return _.keys(item.fields).map(targetColumn => ({
targetColumn,
@@ -194,9 +242,9 @@ export function changeSetToSql(changeSet: ChangeSet): Command[] {
}
export function revertChangeSetRowChanges(changeSet: ChangeSet, definition: ChangeSetRowDefinition): ChangeSet {
- console.log('definition', definition)
+ console.log('definition', definition);
const [field, item] = findExistingChangeSetItem(changeSet, definition);
- console.log('field, item', field, item)
+ console.log('field, item', field, item);
if (item)
return {
...changeSet,
diff --git a/packages/web/src/datagrid/DataGridCore.js b/packages/web/src/datagrid/DataGridCore.js
index b542f273d..c37a97d30 100644
--- a/packages/web/src/datagrid/DataGridCore.js
+++ b/packages/web/src/datagrid/DataGridCore.js
@@ -33,6 +33,7 @@ import {
getChangeSetInsertedRows,
changeSetInsertNewRow,
deleteChangeSetRows,
+ batchUpdateChangeSet,
} from '@dbgate/datalib';
import { scriptToSql } from '@dbgate/sqltree';
import { sleep } from '../utility/common';
@@ -111,6 +112,8 @@ export default function DataGridCore(props) {
const [selectedCells, setSelectedCells] = React.useState(emptyCellArray);
const [dragStartCell, setDragStartCell] = React.useState(nullCell);
const [shiftDragStartCell, setShiftDragStartCell] = React.useState(nullCell);
+ const [autofillDragStartCell, setAutofillDragStartCell] = React.useState(nullCell);
+ const [autofillSelectedCells, setAutofillSelectedCells] = React.useState(emptyCellArray);
// const [inplaceEditorCell, setInplaceEditorCell] = React.useState(nullCell);
// const [inplaceEditorInitText, setInplaceEditorInitText] = React.useState('');
@@ -121,6 +124,14 @@ export default function DataGridCore(props) {
changeSetRef.current = changeSet;
+ const autofillMarkerCell = React.useMemo(
+ () =>
+ selectedCells && selectedCells.length > 0 && _.uniq(selectedCells.map(x => x[0])).length == 1
+ ? [_.max(selectedCells.map(x => x[0])), _.max(selectedCells.map(x => x[1]))]
+ : null,
+ [selectedCells]
+ );
+
const loadNextData = async () => {
if (isLoading) return;
setLoadProps({
@@ -280,21 +291,6 @@ export default function DataGridCore(props) {
[columnSizes, firstVisibleColumnScrollIndex, gridScrollAreaWidth, columns]
);
- const cellIsSelected = React.useCallback(
- (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;
- if (selectedRow == 'header' && selectedCol == 'header') return true;
- }
- return false;
- },
- [currentCell, selectedCells]
- );
-
if (!loadedRows || !columns) return null;
const insertedRows = getChangeSetInsertedRows(changeSet, display.baseTable);
const rowCountNewIncluded = loadedRows.length + insertedRows.length;
@@ -310,21 +306,33 @@ export default function DataGridCore(props) {
function handleGridMouseDown(event) {
event.target.closest('table').focus();
const cell = cellFromEvent(event);
- setCurrentCell(cell);
- setSelectedCells(getCellRange(cell, cell));
- setDragStartCell(cell);
+ const autofill = event.target.closest('div.autofillHandleMarker');
+ if (autofill) {
+ setAutofillDragStartCell(cell);
+ } else {
+ setCurrentCell(cell);
+ setSelectedCells(getCellRange(cell, cell));
+ setDragStartCell(cell);
- if (isRegularCell(cell) && !_.isEqual(cell, inplaceEditorState.cell) && _.isEqual(cell, currentCell)) {
- // @ts-ignore
- dispatchInsplaceEditor({ type: 'show', cell, selectAll: true });
- } else if (!_.isEqual(cell, inplaceEditorState.cell)) {
- // @ts-ignore
- dispatchInsplaceEditor({ type: 'close' });
+ if (isRegularCell(cell) && !_.isEqual(cell, inplaceEditorState.cell) && _.isEqual(cell, currentCell)) {
+ // @ts-ignore
+ dispatchInsplaceEditor({ type: 'show', cell, selectAll: true });
+ } else if (!_.isEqual(cell, inplaceEditorState.cell)) {
+ // @ts-ignore
+ dispatchInsplaceEditor({ type: 'close' });
+ }
}
}
function handleGridMouseMove(event) {
- if (dragStartCell) {
+ if (autofillDragStartCell) {
+ const cell = cellFromEvent(event);
+ if (isRegularCell(cell) && (cell[0] == autofillDragStartCell[0] || cell[1] == autofillDragStartCell[1])) {
+ const autoFillStart = [selectedCells[0][0], _.min(selectedCells.map(x => x[1]))];
+ // @ts-ignore
+ setAutofillSelectedCells(getCellRange(autoFillStart, cell));
+ }
+ } else if (dragStartCell) {
const cell = cellFromEvent(event);
setCurrentCell(cell);
setSelectedCells(getCellRange(dragStartCell, cell));
@@ -338,24 +346,44 @@ export default function DataGridCore(props) {
setSelectedCells(getCellRange(dragStartCell, cell));
setDragStartCell(null);
}
+ if (autofillDragStartCell) {
+ const currentRowNumber = currentCell[0];
+ if (_.isNumber(currentRowNumber)) {
+ const rowIndexes = _.uniq((autofillSelectedCells || []).map(x => x[0])).filter(x => x != currentRowNumber);
+ // @ts-ignore
+ const colNames = selectedCells.map(cell => columns[columnSizes.realToModel(cell[1])].uniqueName);
+ const changeObject = _.pick(loadedAndInsertedRows[currentRowNumber], colNames);
+ setChangeSet(
+ batchUpdateChangeSet(
+ changeSet,
+ getRowDefinitions(rowIndexes),
+ // @ts-ignore
+ rowIndexes.map(() => changeObject)
+ )
+ );
+ }
+
+ setAutofillDragStartCell(null);
+ setAutofillSelectedCells([]);
+ }
}
- function getSelectedRowDefinitions() {
+ function getRowDefinitions(rowIndexes) {
const res = [];
if (!loadedAndInsertedRows) return res;
- const rowIndexes = _.uniq((selectedCells || []).map(x => x[0]));
for (const index of rowIndexes) {
if (loadedAndInsertedRows[index] && _.isNumber(index)) {
- const insertedRowIndex =
- firstVisibleRowScrollIndex + index >= loadedRows.length
- ? firstVisibleRowScrollIndex + index - loadedRows.length
- : null;
+ const insertedRowIndex = index >= loadedRows.length ? index - loadedRows.length : null;
res.push(display.getChangeSetRow(loadedAndInsertedRows[index], insertedRowIndex));
}
}
return res;
}
+ function getSelectedRowDefinitions() {
+ return getRowDefinitions(_.uniq((selectedCells || []).map(x => x[0])));
+ }
+
function revertRowChanges() {
const updatedChangeSet = getSelectedRowDefinitions().reduce(
(chs, row) => revertChangeSetRowChanges(chs, row),
@@ -365,10 +393,7 @@ export default function DataGridCore(props) {
}
function deleteCurrentRow() {
- const updatedChangeSet = getSelectedRowDefinitions().reduce(
- (chs, row) => deleteChangeSetRows(chs, row),
- changeSet
- );
+ const updatedChangeSet = getSelectedRowDefinitions().reduce((chs, row) => deleteChangeSetRows(chs, row), changeSet);
setChangeSet(updatedChangeSet);
}
@@ -699,12 +724,14 @@ export default function DataGridCore(props) {
visibleRealColumns={visibleRealColumns}
inplaceEditorState={inplaceEditorState}
dispatchInsplaceEditor={dispatchInsplaceEditor}
- cellIsSelected={cellIsSelected}
+ autofillSelectedCells={autofillSelectedCells}
+ selectedCells={selectedCells}
insertedRowIndex={
firstVisibleRowScrollIndex + index >= loadedRows.length
? firstVisibleRowScrollIndex + index - loadedRows.length
: null
}
+ autofillMarkerCell={autofillMarkerCell}
changeSet={changeSet}
setChangeSet={setChangeSet}
display={display}
diff --git a/packages/web/src/datagrid/DataGridRow.js b/packages/web/src/datagrid/DataGridRow.js
index 51177dfcd..4a6247fae 100644
--- a/packages/web/src/datagrid/DataGridRow.js
+++ b/packages/web/src/datagrid/DataGridRow.js
@@ -22,22 +22,34 @@ const TableBodyCell = styled.td`
// border-collapse: collapse;
padding: 2px;
white-space: nowrap;
+ position: relative;
overflow: hidden;
${props =>
props.isSelected &&
+ !props.isAutofillSelected &&
`
background: initial;
background-color: deepskyblue;
color: white;`}
+
${props =>
+ props.isAutofillSelected &&
+ `
+ background: initial;
+ background-color: magenta;
+ color: white;`}
+
+ ${props =>
props.isModifiedRow &&
!props.isInsertedRow &&
!props.isSelected &&
+ !props.isAutofillSelected &&
!props.isModifiedCell &&
`
background-color: #FFFFDB;`}
${props =>
!props.isSelected &&
+ !props.isAutofillSelected &&
!props.isInsertedRow &&
props.isModifiedCell &&
`
@@ -45,20 +57,27 @@ const TableBodyCell = styled.td`
${props =>
!props.isSelected &&
+ !props.isAutofillSelected &&
props.isInsertedRow &&
`
background-color: #DBFFDB;`}
${props =>
!props.isSelected &&
+ !props.isAutofillSelected &&
props.isDeletedRow &&
`
background-color: #FFDBFF;
+ `}
+
+ ${props =>
+ props.isDeletedRow &&
+ `
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAEElEQVQImWNgIAX8x4KJBAD+agT8INXz9wAAAABJRU5ErkJggg==');
// from http://www.patternify.com/
background-repeat: repeat-x;
background-position: 50% 50%;`}
-
+
`;
const HintSpan = styled.span`
@@ -89,24 +108,47 @@ const TableHeaderCell = styled.td`
overflow: hidden;
`;
+const AutoFillPoint = styled.div`
+ width: 8px;
+ height: 8px;
+ background-color: #1a73e8;
+ position: absolute;
+ right: 0px;
+ bottom: 0px;
+ overflow: visible;
+ cursor: crosshair;
+`;
+
function CellFormattedValue({ value }) {
if (value == null) return (NULL);
if (_.isDate(value)) return moment(value).format('YYYY-MM-DD HH:mm:ss');
return value;
}
+function cellIsSelected(row, col, selectedCells) {
+ 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;
+ if (selectedRow == 'header' && selectedCol == 'header') return true;
+ }
+ return false;
+}
+
export default function DataGridRow({
rowHeight,
rowIndex,
visibleRealColumns,
inplaceEditorState,
dispatchInsplaceEditor,
- cellIsSelected,
row,
display,
changeSet,
setChangeSet,
insertedRowIndex,
+ autofillMarkerCell,
+ selectedCells,
+ autofillSelectedCells,
}) {
// console.log('RENDER ROW', rowIndex);
const rowDefinition = display.getChangeSetRow(row, insertedRowIndex);
@@ -120,6 +162,7 @@ export default function DataGridRow({
return true;
})
.map(col => col.uniqueName);
+
return (
@@ -135,8 +178,8 @@ export default function DataGridRow({
}}
data-row={rowIndex}
data-col={col.colIndex}
- // @ts-ignore
- isSelected={cellIsSelected(rowIndex, col.colIndex)}
+ isSelected={cellIsSelected(rowIndex, col.colIndex, selectedCells)}
+ isAutofillSelected={cellIsSelected(rowIndex, col.colIndex, autofillSelectedCells)}
isModifiedRow={!!matchedChangeSetItem}
isModifiedCell={
matchedChangeSetItem && matchedField == 'updates' && col.uniqueName in matchedChangeSetItem.fields
@@ -163,6 +206,9 @@ export default function DataGridRow({
{hintFieldsAllowed.includes(col.uniqueName) && {row[col.hintColumnName]}}
>
)}
+ {autofillMarkerCell && autofillMarkerCell[1] == col.colIndex && autofillMarkerCell[0] == rowIndex && (
+
+ )}
))}