mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
CB-6063 feat: add ability to import data without PK (#3188)
* CB-6063 feat: add ability to import data without PK * CB-6063 feat: add ability to insert rows to tables w/o PK using actions removes isImportAllowed flag, adds hasRowIdentifier, uses it in place of forbidden actions * CB-6063 refactor: use only false value for hasRowIdentifier flag * CB-6063 fix: revert logic in hasRowIdentifier * CB-6063 refactor: rename property * CB-6063 fix: return lock icon back for tables w/o identifier * CB-6063 fix: hasRowIdentifier in getResult --------- Co-authored-by: Daria Marutkina <125263541+dariamarutkina@users.noreply.github.com> Co-authored-by: Ainur <59531286+yagudin10@users.noreply.github.com>
This commit is contained in:
co-authored by
Daria Marutkina
Ainur
parent
7a83252490
commit
3b0a4e7d33
+4
-1
@@ -103,7 +103,10 @@ export const CellRenderer = observer<CellRendererProps<IResultSetRowKey, unknown
|
||||
);
|
||||
|
||||
function isEditable(column: CalculatedColumn<IResultSetRowKey>): boolean {
|
||||
if (!cellContext.cell) {
|
||||
if (
|
||||
!cellContext.cell ||
|
||||
(!dataGridContext.model.hasElementIdentifier(tableDataContext.view.resultIndex) && cellContext.editionState !== DatabaseEditChangeType.add)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+6
-4
@@ -86,9 +86,11 @@ export class DataGridContextMenuCellEditingService {
|
||||
const isComplex = format.isBinary(key) || format.isGeometry(key);
|
||||
const isTruncated = content.isTextTruncated(key);
|
||||
const selectedElements = select?.getSelectedElements() || [];
|
||||
// we can't edit table cells if table doesn't have row identifier, but we can edit new created rows before insert (CB-6063)
|
||||
const canEdit = model.hasElementIdentifier(resultIndex) || editor.getElementState(key) === DatabaseEditChangeType.add;
|
||||
|
||||
if (action === ACTION_EDIT) {
|
||||
if (!column || cellValue === undefined || format.isReadOnly(key) || isComplex || isTruncated) {
|
||||
if (!column || cellValue === undefined || format.isReadOnly(key) || isComplex || isTruncated || !canEdit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -96,7 +98,7 @@ export class DataGridContextMenuCellEditingService {
|
||||
}
|
||||
|
||||
if (action === ACTION_DATA_GRID_EDITING_SET_TO_NULL) {
|
||||
return cellValue !== undefined && !format.isReadOnly(key) && !view.getColumn(key.column)?.required && !format.isNull(key);
|
||||
return cellValue !== undefined && !format.isReadOnly(key) && !view.getColumn(key.column)?.required && !format.isNull(key) && canEdit;
|
||||
}
|
||||
|
||||
if (action === ACTION_DATA_GRID_EDITING_ADD_ROW || action === ACTION_DATA_GRID_EDITING_DUPLICATE_ROW) {
|
||||
@@ -104,11 +106,11 @@ export class DataGridContextMenuCellEditingService {
|
||||
}
|
||||
|
||||
if (action === ACTION_DATA_GRID_EDITING_DELETE_ROW) {
|
||||
return !format.isReadOnly(key) && editor.getElementState(key) !== DatabaseEditChangeType.delete;
|
||||
return !format.isReadOnly(key) && canEdit && editor.getElementState(key) !== DatabaseEditChangeType.delete;
|
||||
}
|
||||
|
||||
if (action === ACTION_DATA_GRID_EDITING_DELETE_SELECTED_ROW) {
|
||||
if (model.isReadonly(resultIndex) || !editor.hasFeature('delete')) {
|
||||
if (model.isReadonly(resultIndex) || !canEdit || !editor.hasFeature('delete')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -222,12 +222,14 @@ export const DataGridTable = observer<IDataPresentationProps>(function DataGridT
|
||||
|
||||
function handleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {
|
||||
gridSelectedCellCopy.onKeydownHandler(event);
|
||||
const cell = selectionAction.getFocusedElement();
|
||||
// we can't edit table cells if table doesn't have row identifier, but we can edit new created rows before insert (CB-6063)
|
||||
const canEdit = model.hasElementIdentifier(resultIndex) || !!(cell && tableData.editor.getElementState(cell) === DatabaseEditChangeType.add);
|
||||
|
||||
if (EventContext.has(event, EventStopPropagationFlag) || tableData.isReadOnly() || model.isReadonly(resultIndex)) {
|
||||
if (EventContext.has(event, EventStopPropagationFlag) || !canEdit || tableData.isReadOnly() || model.isReadonly(resultIndex)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cell = selectionAction.getFocusedElement();
|
||||
const activeElements = selectionAction.getActiveElements();
|
||||
const activeRows = selectionAction.getActiveRows();
|
||||
|
||||
|
||||
+6
-1
@@ -27,7 +27,12 @@ export const TableIndexColumnHeader = observer<RenderHeaderCellProps<any>>(funct
|
||||
throw new Error('Contexts required');
|
||||
}
|
||||
|
||||
const readonly = getComputed(() => tableDataContext.isReadOnly() || dataGridContext.model.isReadonly(dataGridContext.resultIndex));
|
||||
const readonly = getComputed(
|
||||
() =>
|
||||
tableDataContext.isReadOnly() ||
|
||||
dataGridContext.model.isReadonly(dataGridContext.resultIndex) ||
|
||||
!dataGridContext.model.hasElementIdentifier(dataGridContext.resultIndex),
|
||||
);
|
||||
|
||||
function handleClick(event: React.MouseEvent<HTMLDivElement>) {
|
||||
selectionContext.selectTable();
|
||||
|
||||
@@ -59,6 +59,10 @@ export class DatabaseDataModel<TSource extends IDatabaseDataSource<any, any> = I
|
||||
return this.source.isReadonly(resultIndex);
|
||||
}
|
||||
|
||||
hasElementIdentifier(resultIndex: number): boolean {
|
||||
return this.source.hasElementIdentifier(resultIndex);
|
||||
}
|
||||
|
||||
isDataAvailable(offset: number, count: number): boolean {
|
||||
return this.source.isDataAvailable(offset, count);
|
||||
}
|
||||
|
||||
@@ -206,6 +206,10 @@ export abstract class DatabaseDataSource<TOptions, TResult extends IDatabaseData
|
||||
return !this.isLoading() && !this.disabled;
|
||||
}
|
||||
|
||||
hasElementIdentifier(resultIndex: number): boolean {
|
||||
return this.getResult(resultIndex)?.data?.hasRowIdentifier === true;
|
||||
}
|
||||
|
||||
isReadonly(resultIndex: number): boolean {
|
||||
return this.access === DatabaseDataAccessMode.Readonly || this.results.length > 1 || this.disabled;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ export interface IDatabaseDataModel<TSource extends IDatabaseDataSource<any, any
|
||||
|
||||
setName: (name: string | null) => this;
|
||||
isReadonly: (resultIndex: number) => boolean;
|
||||
hasElementIdentifier: (resultIndex: number) => boolean;
|
||||
isDisabled: (resultIndex?: number) => boolean;
|
||||
isLoading: () => boolean;
|
||||
isDataAvailable: (offset: number, count: number) => boolean;
|
||||
|
||||
@@ -71,6 +71,7 @@ export interface IDatabaseDataSource<TOptions = unknown, TResult extends IDataba
|
||||
isOutdated: () => boolean;
|
||||
isLoadable: () => boolean;
|
||||
isReadonly: (resultIndex: number) => boolean;
|
||||
hasElementIdentifier: (resultIndex: number) => boolean;
|
||||
isDataAvailable: (offset: number, count: number) => boolean;
|
||||
isLoading: () => boolean;
|
||||
isDisabled: (resultIndex?: number) => boolean;
|
||||
|
||||
@@ -39,7 +39,7 @@ export abstract class ResultSetDataSource<TOptions = IDatabaseDataOptions> exten
|
||||
}
|
||||
|
||||
override isReadonly(resultIndex: number): boolean {
|
||||
return super.isReadonly(resultIndex) || !this.executionContext?.context || this.getResult(resultIndex)?.data?.hasRowIdentifier === false;
|
||||
return super.isReadonly(resultIndex) || !this.executionContext?.context;
|
||||
}
|
||||
|
||||
override async cancel(): Promise<void> {
|
||||
|
||||
+6
-3
@@ -101,13 +101,16 @@ export class TableFooterMenuService {
|
||||
}
|
||||
case ACTION_DELETE: {
|
||||
const editor = model.source.getActionImplementation(resultIndex, DatabaseEditAction);
|
||||
const selectedElements = getActiveElements(model, resultIndex);
|
||||
|
||||
if (!editor) {
|
||||
// we can't edit table cells if table doesn't have row identifier, but we can edit new created rows before insert (CB-6063)
|
||||
const canEdit =
|
||||
model.hasElementIdentifier(resultIndex) || selectedElements.every(key => editor?.getElementState(key) === DatabaseEditChangeType.add);
|
||||
|
||||
if (!editor || !canEdit) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const selectedElements = getActiveElements(model, resultIndex);
|
||||
|
||||
return selectedElements.length === 0 || !selectedElements.some(key => editor.getElementState(key) !== DatabaseEditChangeType.delete);
|
||||
}
|
||||
case ACTION_REVERT: {
|
||||
|
||||
+5
-1
@@ -51,7 +51,11 @@ export const BooleanValuePresentation: TabContainerPanelComponent<IDataValuePane
|
||||
|
||||
const column = viewAction.getColumn(firstSelectedCell.column);
|
||||
const nullable = column?.required === false;
|
||||
const readonly = model.isReadonly(resultIndex) || model.isDisabled(resultIndex) || formatAction.isReadOnly(firstSelectedCell);
|
||||
const readonly =
|
||||
model.isReadonly(resultIndex) ||
|
||||
!model.hasElementIdentifier(resultIndex) ||
|
||||
model.isDisabled(resultIndex) ||
|
||||
formatAction.isReadOnly(firstSelectedCell);
|
||||
|
||||
return (
|
||||
<div className={classes['container']}>
|
||||
|
||||
+2
-1
@@ -30,6 +30,7 @@ export function isTextValueReadonly({ contentAction, formatAction, model, result
|
||||
formatAction.isGeometry(cell) ||
|
||||
contentAction.isTextTruncated(cell) ||
|
||||
model.isReadonly(resultIndex) ||
|
||||
model.isDisabled(resultIndex)
|
||||
model.isDisabled(resultIndex) ||
|
||||
!model.hasElementIdentifier(resultIndex)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ export class GeneratorMenuBootstrap extends Bootstrap {
|
||||
const presentation = context.get(DATA_CONTEXT_DV_PRESENTATION);
|
||||
return (
|
||||
!model.isReadonly(resultIndex) &&
|
||||
model.hasElementIdentifier(resultIndex) &&
|
||||
model.source.getResult(resultIndex)?.dataFormat === ResultDataFormat.Resultset &&
|
||||
!presentation?.readonly &&
|
||||
(!presentation || presentation.type === DataViewerPresentationType.Data)
|
||||
|
||||
Reference in New Issue
Block a user