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