mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
inplace editing
This commit is contained in:
parent
b893b344f5
commit
ce1e58dbdc
@ -22,18 +22,21 @@ export function createChangeSet(): ChangeSet {
|
||||
};
|
||||
}
|
||||
|
||||
export interface ChangeSetFieldDefinition {
|
||||
export interface ChangeSetRowDefinition {
|
||||
pureName: string;
|
||||
schemaName: string;
|
||||
uniqueName: string;
|
||||
columnName: string;
|
||||
insertedRowIndex?: number;
|
||||
condition?: { [column: string]: string };
|
||||
}
|
||||
|
||||
function findExistingChangeSetItem(
|
||||
export interface ChangeSetFieldDefinition extends ChangeSetRowDefinition {
|
||||
uniqueName: string;
|
||||
columnName: string;
|
||||
}
|
||||
|
||||
export function findExistingChangeSetItem(
|
||||
changeSet: ChangeSet,
|
||||
definition: ChangeSetFieldDefinition
|
||||
definition: ChangeSetRowDefinition
|
||||
): [keyof ChangeSet, ChangeSetItem] {
|
||||
if (definition.insertedRowIndex != null) {
|
||||
return [
|
||||
|
@ -4,7 +4,7 @@ import { ForeignKeyInfo, TableInfo, ColumnInfo, DbType } from '@dbgate/types';
|
||||
import { parseFilter, getFilterType } from '@dbgate/filterparser';
|
||||
import { filterName } from './filterName';
|
||||
import { Select, Expression } from '@dbgate/sqltree';
|
||||
import { ChangeSetFieldDefinition } from './ChangeSet';
|
||||
import { ChangeSetFieldDefinition, ChangeSetRowDefinition } from './ChangeSet';
|
||||
|
||||
export interface DisplayColumn {
|
||||
schemaName: string;
|
||||
@ -372,4 +372,13 @@ export abstract class GridDisplay {
|
||||
condition: this.getChangeSetCondition(row),
|
||||
};
|
||||
}
|
||||
|
||||
getChangeSetRow(row): ChangeSetRowDefinition {
|
||||
if (!this.baseTable) return null;
|
||||
return {
|
||||
pureName: this.baseTable.pureName,
|
||||
schemaName: this.baseTable.schemaName,
|
||||
condition: this.getChangeSetCondition(row),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -100,6 +100,7 @@ export default function DataGridCore(props) {
|
||||
const [shiftDragStartCell, setShiftDragStartCell] = React.useState(nullCell);
|
||||
|
||||
const [inplaceEditorCell, setInplaceEditorCell] = React.useState(nullCell);
|
||||
const [inplaceEditorInitText, setInplaceEditorInitText] = React.useState('');
|
||||
|
||||
const loadNextData = async () => {
|
||||
if (isLoading) return;
|
||||
@ -291,7 +292,7 @@ export default function DataGridCore(props) {
|
||||
if (isRegularCell(cell) && !_.isEqual(cell, inplaceEditorCell) && _.isEqual(cell, currentCell)) {
|
||||
setInplaceEditorCell(cell);
|
||||
} else if (!_.isEqual(cell, inplaceEditorCell)) {
|
||||
setInplaceEditorCell(null);
|
||||
handleCloseInplaceEditor();
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,14 +335,28 @@ export default function DataGridCore(props) {
|
||||
}
|
||||
|
||||
function handleGridKeyDown(event) {
|
||||
handleCursorMove(event);
|
||||
if (
|
||||
!event.ctrlKey &&
|
||||
!event.altKey &&
|
||||
((event.keyCode >= keycodes.a && event.keyCode <= keycodes.z) ||
|
||||
(event.keyCode >= keycodes.n0 && event.keyCode <= keycodes.n9) ||
|
||||
event.keyCode == keycodes.dash)
|
||||
) {
|
||||
setInplaceEditorInitText(event.nativeEvent.key);
|
||||
setInplaceEditorCell(currentCell);
|
||||
// console.log('event', event.nativeEvent);
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
if (!isRegularCell(shiftDragStartCell)) {
|
||||
setShiftDragStartCell(currentCell);
|
||||
const moved = handleCursorMove(event);
|
||||
|
||||
if (moved) {
|
||||
if (event.shiftKey) {
|
||||
if (!isRegularCell(shiftDragStartCell)) {
|
||||
setShiftDragStartCell(currentCell);
|
||||
}
|
||||
} else {
|
||||
setShiftDragStartCell(nullCell);
|
||||
}
|
||||
} else {
|
||||
setShiftDragStartCell(nullCell);
|
||||
}
|
||||
|
||||
const newCell = handleCursorMove(event);
|
||||
@ -480,6 +495,11 @@ export default function DataGridCore(props) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleCloseInplaceEditor() {
|
||||
setInplaceEditorCell(null);
|
||||
setInplaceEditorInitText(null);
|
||||
}
|
||||
|
||||
// console.log('visibleRowCountUpperBound', visibleRowCountUpperBound);
|
||||
// console.log('gridScrollAreaHeight', gridScrollAreaHeight);
|
||||
// console.log('containerHeight', containerHeight);
|
||||
@ -599,6 +619,8 @@ export default function DataGridCore(props) {
|
||||
rowHeight={rowHeight}
|
||||
visibleRealColumns={visibleRealColumns}
|
||||
inplaceEditorCell={inplaceEditorCell}
|
||||
inplaceEditorInitText={inplaceEditorInitText}
|
||||
onCloseInplaceEditor={handleCloseInplaceEditor}
|
||||
cellIsSelected={cellIsSelected}
|
||||
changeSet={changeSet}
|
||||
setChangeSet={setChangeSet}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import moment from 'moment';
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
@ -11,15 +12,7 @@ import axios from '../utility/axios';
|
||||
import ColumnLabel from './ColumnLabel';
|
||||
import DataFilterControl from './DataFilterControl';
|
||||
import { getFilterType } from '@dbgate/filterparser';
|
||||
import {
|
||||
convertCellAddress,
|
||||
cellFromEvent,
|
||||
getCellRange,
|
||||
topLeftCell,
|
||||
isRegularCell,
|
||||
nullCell,
|
||||
emptyCellArray,
|
||||
} from './selection';
|
||||
import { findExistingChangeSetItem } from '@dbgate/datalib';
|
||||
import keycodes from '../utility/keycodes';
|
||||
import InplaceEditor from './InplaceEditor';
|
||||
|
||||
@ -31,13 +24,23 @@ const TableBodyCell = styled.td`
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
${props =>
|
||||
// @ts-ignore
|
||||
props.isSelected &&
|
||||
`
|
||||
background: initial;
|
||||
background-color: deepskyblue;
|
||||
color: white;`}
|
||||
`;
|
||||
${props =>
|
||||
props.isModifiedRow &&
|
||||
!props.isSelected &&
|
||||
!props.isModifiedCell &&
|
||||
`
|
||||
background-color: #FFFFDB;`}
|
||||
${props =>
|
||||
!props.isSelected &&
|
||||
props.isModifiedCell &&
|
||||
`
|
||||
background-color: bisque;`}
|
||||
`;
|
||||
const HintSpan = styled.span`
|
||||
color: gray;
|
||||
margin-left: 5px;
|
||||
@ -77,12 +80,17 @@ export default function DataGridRow({
|
||||
rowIndex,
|
||||
visibleRealColumns,
|
||||
inplaceEditorCell,
|
||||
inplaceEditorInitText,
|
||||
onCloseInplaceEditor,
|
||||
cellIsSelected,
|
||||
row,
|
||||
display,
|
||||
changeSet,
|
||||
setChangeSet,
|
||||
}) {
|
||||
const rowDefinition = display.getChangeSetRow(row);
|
||||
const [_fld, matchedChangeSetItem] = findExistingChangeSetItem(changeSet, rowDefinition);
|
||||
const rowUpdated = matchedChangeSetItem ? { ...row, ...matchedChangeSetItem.fields } : row;
|
||||
return (
|
||||
<TableBodyRow style={{ height: `${rowHeight}px` }}>
|
||||
<TableHeaderCell data-row={rowIndex} data-col="header">
|
||||
@ -100,19 +108,25 @@ export default function DataGridRow({
|
||||
data-col={col.colIndex}
|
||||
// @ts-ignore
|
||||
isSelected={cellIsSelected(rowIndex, col.colIndex)}
|
||||
isModifiedRow={!!matchedChangeSetItem}
|
||||
isModifiedCell={matchedChangeSetItem && col.uniqueName in matchedChangeSetItem.fields}
|
||||
>
|
||||
{inplaceEditorCell && rowIndex == inplaceEditorCell[0] && col.colIndex == inplaceEditorCell[1] ? (
|
||||
<InplaceEditor
|
||||
widthPx={col.widthPx}
|
||||
value={row[col.uniqueName]}
|
||||
value={inplaceEditorInitText || rowUpdated[col.uniqueName]}
|
||||
selectAll={!inplaceEditorInitText}
|
||||
changeSet={changeSet}
|
||||
setChangeSet={setChangeSet}
|
||||
definition={display.getChangeSetField(row, col.uniqueName)}
|
||||
onClose={onCloseInplaceEditor}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<CellFormattedValue value={row[col.uniqueName]} />
|
||||
{col.hintColumnName && <HintSpan>{row[col.hintColumnName]}</HintSpan>}
|
||||
<CellFormattedValue value={rowUpdated[col.uniqueName]} />
|
||||
{(!matchedChangeSetItem || !(col.uniqueName in matchedChangeSetItem.fields)) && col.hintColumnName && (
|
||||
<HintSpan>{row[col.hintColumnName]}</HintSpan>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</TableBodyCell>
|
||||
|
@ -1,3 +1,5 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import _ from 'lodash';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
@ -12,23 +14,38 @@ const StyledInput = styled.input`
|
||||
padding: 0px;
|
||||
`;
|
||||
|
||||
export default function InplaceEditor({ widthPx, value, definition, changeSet, setChangeSet }) {
|
||||
export default function InplaceEditor({ widthPx, value, definition, changeSet, setChangeSet, onClose, selectAll }) {
|
||||
const editorRef = React.useRef();
|
||||
const isChangedRef = React.createRef();
|
||||
React.useEffect(() => {
|
||||
const editor = editorRef.current;
|
||||
editor.value = value;
|
||||
editor.focus();
|
||||
editor.select();
|
||||
if (selectAll) {
|
||||
editor.select();
|
||||
}
|
||||
}, []);
|
||||
function handleBlur() {
|
||||
const editor = editorRef.current;
|
||||
setChangeSet(setChangeSetValue(changeSet, definition, editor.value));
|
||||
if (isChangedRef.current) {
|
||||
const editor = editorRef.current;
|
||||
setChangeSet(setChangeSetValue(changeSet, definition, editor.value));
|
||||
}
|
||||
}
|
||||
function handleKeyDown(event) {
|
||||
switch (event.keyCode) {
|
||||
case keycodes.escape:
|
||||
isChangedRef.current = false;
|
||||
onClose();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<StyledInput
|
||||
onBlur={handleBlur}
|
||||
ref={editorRef}
|
||||
type="text"
|
||||
onChange={() => (isChangedRef.current = true)}
|
||||
onKeyDown={handleKeyDown}
|
||||
style={{
|
||||
width: widthPx,
|
||||
minWidth: widthPx,
|
||||
|
Loading…
Reference in New Issue
Block a user