formview save

This commit is contained in:
Jan Prochazka 2021-03-21 10:20:57 +01:00
parent 90c4b44fdb
commit ec90a8b952
3 changed files with 221 additions and 75 deletions

View File

@ -2,6 +2,9 @@
let lastFocusedDataGrid = null;
const getCurrentDataGrid = () =>
lastFocusedDataGrid?.getTabId && lastFocusedDataGrid?.getTabId() == getActiveTabId() ? lastFocusedDataGrid : null;
export function clearLastFocusedDataGrid() {
lastFocusedDataGrid = null;
}
registerCommand({
id: 'dataGrid.refresh',
@ -205,6 +208,7 @@
import { copyTextToClipboard } from '../utility/clipboard';
import invalidateCommands from '../commands/invalidateCommands';
import createRef from '../utility/createRef';
import { clearLastFocusedFormView } from '../formview/FormView.svelte';
export let onLoadNextData = undefined;
export let grider = undefined;
@ -666,7 +670,6 @@
}
function handleGridKeyDown(event) {
if ($inplaceEditorState.cell) return;
if (
@ -903,6 +906,7 @@
on:keydown={handleGridKeyDown}
on:focus={() => {
lastFocusedDataGrid = instance;
clearLastFocusedFormView();
invalidateCommands();
}}
on:paste={handlePaste}

View File

@ -2,6 +2,9 @@
let lastFocusedFormView = null;
const getCurrentDataForm = () =>
lastFocusedFormView?.getTabId && lastFocusedFormView?.getTabId() == getActiveTabId() ? lastFocusedFormView : null;
export function clearLastFocusedFormView() {
lastFocusedFormView = null;
}
registerCommand({
id: 'dataForm.switchToTable',
@ -12,6 +15,17 @@
onClick: () => getCurrentDataForm().switchToTable(),
});
registerCommand({
id: 'dataForm.save',
group: 'save',
category: 'Data form',
name: 'Save',
toolbar: true,
icon: 'icon save',
testEnabled: () => getCurrentDataForm()?.getFormer()?.allowSave,
onClick: () => getCurrentDataForm().save(),
});
function isDataCell(cell) {
return cell[1] % 2 == 1;
}
@ -27,15 +41,18 @@
import registerCommand from '../commands/registerCommand';
import DataGridCell from '../datagrid/DataGridCell.svelte';
import { clearLastFocusedDataGrid } from '../datagrid/DataGridCore.svelte';
import InplaceEditor from '../datagrid/InplaceEditor.svelte';
import { cellFromEvent } from '../datagrid/selection';
import ColumnLabel from '../elements/ColumnLabel.svelte';
import LoadingInfo from '../elements/LoadingInfo.svelte';
import { plusExpandIcon } from '../icons/expandIcons';
import FontIcon from '../icons/FontIcon.svelte';
import { getActiveTabId } from '../stores';
import contextMenu from '../utility/contextMenu';
import createReducer from '../utility/createReducer';
import keycodes from '../utility/keycodes';
import resizeObserver from '../utility/resizeObserver';
export let config;
@ -46,6 +63,7 @@
export let isLoading;
export let former;
export let formDisplay;
export let onSave;
let wrapperHeight = 1;
let rowHeight = 1;
@ -73,6 +91,10 @@
return tabid;
}
export function getFormer() {
return former;
}
export function switchToTable() {
setConfig(cfg => ({
...cfg,
@ -81,6 +103,15 @@
}));
}
export function save() {
if ($inplaceEditorState.cell) {
// @ts-ignore
dispatchInsplaceEditor({ type: 'shouldSave' });
return;
}
if (onSave) onSave();
}
const handleTableMouseDown = event => {
if (event.target.closest('.buttonLike')) return;
if (event.target.closest('.resizeHandleControl')) return;
@ -93,12 +124,12 @@
event.preventDefault();
const cell = cellFromEvent(event);
if (isDataCell(cell) && !_.isEqual(cell, inplaceEditorState.cell) && _.isEqual(cell, currentCell)) {
if (isDataCell(cell) && !_.isEqual(cell, $inplaceEditorState.cell) && _.isEqual(cell, currentCell)) {
// @ts-ignore
if (rowData) {
dispatchInsplaceEditor({ type: 'show', cell, selectAll: true });
}
} else if (!_.isEqual(cell, inplaceEditorState.cell)) {
} else if (!_.isEqual(cell, $inplaceEditorState.cell)) {
// @ts-ignore
dispatchInsplaceEditor({ type: 'close' });
}
@ -161,83 +192,155 @@
return [{ command: 'dataForm.switchToTable' }];
}
$: console.log('rowHeight', rowHeight);
function handleKeyDown(event) {
if ($inplaceEditorState.cell) return;
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)
) {
// @ts-ignore
event.preventDefault();
dispatchInsplaceEditor({ type: 'show', text: event.key, cell: currentCell });
// console.log('event', event.nativeEvent);
}
if (event.keyCode == keycodes.f2) {
// @ts-ignore
dispatchInsplaceEditor({ type: 'show', cell: currentCell, selectAll: true });
}
handleCursorMove(event);
}
const scrollIntoView = cell => {
const element = domCells[`${cell[0]},${cell[1]}`];
if (element) element.scrollIntoView();
};
const moveCurrentCell = (row, col) => {
if (row < 0) row = 0;
if (col < 0) col = 0;
if (col >= columnChunks.length * 2) col = columnChunks.length * 2 - 1;
const chunk = columnChunks[Math.floor(col / 2)];
if (chunk && row >= chunk.length) row = chunk.length - 1;
currentCell = [row, col];
scrollIntoView(currentCell);
};
const handleCursorMove = event => {
if (event.ctrlKey) {
switch (event.keyCode) {
case keycodes.leftArrow:
return moveCurrentCell(currentCell[0], 0);
case keycodes.rightArrow:
return moveCurrentCell(currentCell[0], columnChunks.length * 2 - 1);
}
}
switch (event.keyCode) {
case keycodes.leftArrow:
return moveCurrentCell(currentCell[0], currentCell[1] - 1);
case keycodes.rightArrow:
return moveCurrentCell(currentCell[0], currentCell[1] + 1);
case keycodes.upArrow:
return moveCurrentCell(currentCell[0] - 1, currentCell[1]);
case keycodes.downArrow:
return moveCurrentCell(currentCell[0] + 1, currentCell[1]);
case keycodes.pageUp:
return moveCurrentCell(0, currentCell[1]);
case keycodes.pageDown:
return moveCurrentCell(rowCount - 1, currentCell[1]);
case keycodes.home:
return moveCurrentCell(0, 0);
case keycodes.end:
return moveCurrentCell(rowCount - 1, columnChunks.length * 2 - 1);
}
};
</script>
<div class="outer">
<div class="wrapper" use:contextMenu={createMenu} bind:clientHeight={wrapperHeight}>
{#each columnChunks as chunk, chunkIndex}
<table on:mousedown={handleTableMouseDown}>
{#each chunk as col, rowIndex}
<tr>
<td
class="header-cell"
data-row={rowIndex}
data-col={chunkIndex * 2}
style={`height: ${rowHeight}px`}
class:isSelected={currentCell[0] == rowIndex && currentCell[1] == chunkIndex * 2}
bind:this={domCells[`${rowIndex},${chunkIndex * 2}`]}
use:resizeObserver={chunkIndex == 0 && rowIndex == 0}
on:resize={e => {
// @ts-ignore
if (rowHeight == 1) rowHeight = e.detail.height;
}}
>
<div class="header-cell-inner">
{#if col.foreignKey}
<FontIcon
icon={plusExpandIcon(formDisplay.isExpandedColumn(col.uniqueName))}
on:click={e => {
e.stopPropagation();
formDisplay.toggleExpandedColumn(col.uniqueName);
{#if isLoading}
<LoadingInfo wrapper message="Loading data" />
{:else}
<div class="outer">
<div class="wrapper" use:contextMenu={createMenu} bind:clientHeight={wrapperHeight}>
{#each columnChunks as chunk, chunkIndex}
<table on:mousedown={handleTableMouseDown}>
{#each chunk as col, rowIndex}
<tr>
<td
class="header-cell"
data-row={rowIndex}
data-col={chunkIndex * 2}
style={`height: ${rowHeight}px`}
class:isSelected={currentCell[0] == rowIndex && currentCell[1] == chunkIndex * 2}
bind:this={domCells[`${rowIndex},${chunkIndex * 2}`]}
use:resizeObserver={chunkIndex == 0 && rowIndex == 0}
on:resize={e => {
// @ts-ignore
if (rowHeight == 1) rowHeight = e.detail.height;
}}
>
<div class="header-cell-inner">
{#if col.foreignKey}
<FontIcon
icon={plusExpandIcon(formDisplay.isExpandedColumn(col.uniqueName))}
on:click={e => {
e.stopPropagation();
formDisplay.toggleExpandedColumn(col.uniqueName);
}}
/>
{:else}
<FontIcon icon="icon invisible-box" />
{/if}
<span style={`margin-left: ${(col.uniquePath.length - 1) * 20}px`} />
<ColumnLabel {...col} headerText={col.columnName} />
</div>
</td>
<DataGridCell
{rowIndex}
{col}
{rowData}
colIndex={chunkIndex * 2 + 1}
isSelected={currentCell[0] == rowIndex && currentCell[1] == chunkIndex * 2 + 1}
isModifiedCell={rowStatus.modifiedFields && rowStatus.modifiedFields.has(col.uniqueName)}
bind:domCell={domCells[`${rowIndex},${chunkIndex * 2 + 1}`]}
hideContent={$inplaceEditorState.cell &&
rowIndex == $inplaceEditorState.cell[0] &&
chunkIndex * 2 + 1 == $inplaceEditorState.cell[1]}
>
{#if $inplaceEditorState.cell && rowIndex == $inplaceEditorState.cell[0] && chunkIndex * 2 + 1 == $inplaceEditorState.cell[1]}
<InplaceEditor
width={getCellWidth(rowIndex, chunkIndex * 2 + 1)}
inplaceEditorState={$inplaceEditorState}
{dispatchInsplaceEditor}
cellValue={rowData[col.uniqueName]}
onSetValue={value => {
former.setCellValue(col.uniqueName, value);
}}
/>
{:else}
<FontIcon icon="icon invisible-box" />
{/if}
<span style={`margin-left: ${(col.uniquePath.length - 1) * 20}px`} />
<ColumnLabel {...col} headerText={col.columnName} />
</div>
</td>
<DataGridCell
{rowIndex}
{col}
{rowData}
colIndex={chunkIndex * 2 + 1}
isSelected={currentCell[0] == rowIndex && currentCell[1] == chunkIndex * 2 + 1}
isModifiedCell={rowStatus.modifiedFields && rowStatus.modifiedFields.has(col.uniqueName)}
bind:domCell={domCells[`${rowIndex},${chunkIndex * 2 + 1}`]}
hideContent={$inplaceEditorState.cell &&
rowIndex == $inplaceEditorState.cell[0] &&
chunkIndex * 2 + 1 == $inplaceEditorState.cell[1]}
>
{#if $inplaceEditorState.cell && rowIndex == $inplaceEditorState.cell[0] && chunkIndex * 2 + 1 == $inplaceEditorState.cell[1]}
<InplaceEditor
width={getCellWidth(rowIndex, chunkIndex * 2 + 1)}
inplaceEditorState={$inplaceEditorState}
{dispatchInsplaceEditor}
cellValue={rowData[col.uniqueName]}
onSetValue={value => {
former.setCellValue(col.uniqueName, value);
}}
/>
{/if}
</DataGridCell>
</tr>
{/each}
</table>
{/each}
<input
type="text"
class="focus-field"
bind:this={domFocusField}
on:focus={() => {
lastFocusedFormView = instance;
invalidateCommands();
}}
/>
</DataGridCell>
</tr>
{/each}
</table>
{/each}
<input
type="text"
class="focus-field"
bind:this={domFocusField}
on:focus={() => {
lastFocusedFormView = instance;
clearLastFocusedDataGrid();
invalidateCommands();
}}
on:keydown={handleKeyDown}
/>
</div>
</div>
</div>
{/if}
<style>
table {

View File

@ -20,6 +20,14 @@
</script>
<script lang="ts">
import { changeSetToSql, createChangeSet } from 'dbgate-datalib';
import { scriptToSql } from 'dbgate-sqltree';
import ConfirmSqlModal from '../modals/ConfirmSqlModal.svelte';
import ErrorMessageModal from '../modals/ErrorMessageModal.svelte';
import { showModal } from '../modals/modalTools';
import axiosInstance from '../utility/axiosInstance';
import ChangeSetFormer from './ChangeSetFormer';
import FormView from './FormView.svelte';
@ -28,6 +36,8 @@
export let changeSetState;
export let dispatchChangeSet;
export let masterLoadedTime;
export let conid;
export let database;
let isLoadingData = false;
let isLoadedData = false;
@ -117,6 +127,35 @@
}
$: former = new ChangeSetFormer(rowData, changeSetState, dispatchChangeSet, formDisplay);
async function handleConfirmSql(sql) {
const resp = await axiosInstance.request({
url: 'database-connections/query-data',
method: 'post',
params: {
conid,
database,
},
data: { sql },
});
const { errorMessage } = resp.data || {};
if (errorMessage) {
showModal(ErrorMessageModal, { title: 'Error when saving', message: errorMessage });
} else {
dispatchChangeSet({ type: 'reset', value: createChangeSet() });
formDisplay.reload();
}
}
function handleSave() {
const script = changeSetToSql(changeSetState && changeSetState.value, formDisplay.dbinfo);
const sql = scriptToSql(formDisplay.driver, script);
showModal(ConfirmSqlModal, {
sql,
onConfirm: () => handleConfirmSql(sql),
engine: formDisplay.engine,
});
}
</script>
<FormView {...$$props} {former} isLoading={isLoadingData} {allRowCount} {rowCountBefore} />
<FormView {...$$props} {former} isLoading={isLoadingData} {allRowCount} {rowCountBefore} onSave={handleSave} />