mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
save dialog
This commit is contained in:
parent
b64b6be68a
commit
423644e9d9
@ -98,9 +98,6 @@
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
td.isSelected {
|
||||
background: var(--theme-bg-selected);
|
||||
}
|
||||
td.isFrameSelected {
|
||||
outline: 3px solid var(--theme-bg-selected);
|
||||
outline-offset: -3px;
|
||||
@ -127,6 +124,9 @@
|
||||
background-repeat: repeat-x;
|
||||
background-position: 50% 50%;
|
||||
}
|
||||
td.isSelected {
|
||||
background: var(--theme-bg-selected);
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: var(--theme-font-3);
|
||||
|
@ -12,6 +12,17 @@
|
||||
onClick: () => get(currentDataGrid).refresh(),
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'dataGrid.save',
|
||||
category: 'Data grid',
|
||||
name: 'Save',
|
||||
keyText: 'Ctrl+S',
|
||||
toolbar: true,
|
||||
icon: 'icon save',
|
||||
enabledStore: derived([currentDataGrid], ([grid]) => grid?.getGrider()?.allowSave),
|
||||
onClick: () => get(currentDataGrid).save(),
|
||||
});
|
||||
|
||||
function getRowCountInfo(selectedCells, grider, realColumnUniqueNames, selectedRowData, allRowCount) {
|
||||
if (selectedCells.length > 1 && selectedCells.every(x => _.isNumber(x[0]) && _.isNumber(x[1]))) {
|
||||
let sum = _.sumBy(selectedCells, cell => {
|
||||
@ -62,6 +73,9 @@
|
||||
import InlineButton from '../elements/InlineButton.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
import DataFilterControl from './DataFilterControl.svelte';
|
||||
import createReducer from '../utility/createReducer';
|
||||
import keycodes from '../utility/keycodes';
|
||||
import { nullStore } from '../stores';
|
||||
|
||||
export let loadNextData = undefined;
|
||||
export let grider = undefined;
|
||||
@ -73,6 +87,7 @@
|
||||
export let allRowCount = undefined;
|
||||
export let onReferenceSourceChanged = undefined;
|
||||
export let onReferenceClick = undefined;
|
||||
export let onSave;
|
||||
|
||||
export let isLoadedAll;
|
||||
export let loadedTime;
|
||||
@ -97,6 +112,18 @@
|
||||
let autofillDragStartCell = nullCell;
|
||||
let autofillSelectedCells = emptyCellArray;
|
||||
|
||||
export function refresh() {
|
||||
display.reload();
|
||||
}
|
||||
|
||||
export function save() {
|
||||
if (onSave) onSave();
|
||||
}
|
||||
|
||||
export function getGrider() {
|
||||
return grider;
|
||||
}
|
||||
|
||||
$: autofillMarkerCell =
|
||||
selectedCells && selectedCells.length > 0 && _.uniq(selectedCells.map(x => x[0])).length == 1
|
||||
? [_.max(selectedCells.map(x => x[0])), _.max(selectedCells.map(x => x[1]))]
|
||||
@ -228,6 +255,7 @@
|
||||
if (autofill) {
|
||||
autofillDragStartCell = cell;
|
||||
} else {
|
||||
const oldCurrentCell = currentCell;
|
||||
currentCell = cell;
|
||||
|
||||
if (event.ctrlKey) {
|
||||
@ -242,13 +270,11 @@
|
||||
selectedCells = getCellRange(cell, cell);
|
||||
dragStartCell = 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, oldCurrentCell)) {
|
||||
dispatchInsplaceEditor({ type: 'show', cell, selectAll: true });
|
||||
} else if (!_.isEqual(cell, inplaceEditorState.cell)) {
|
||||
dispatchInsplaceEditor({ type: 'close' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,10 +340,6 @@
|
||||
domVerticalScroll.scroll(newFirstVisibleRowScrollIndex);
|
||||
}
|
||||
|
||||
export function refresh() {
|
||||
display.reload();
|
||||
}
|
||||
|
||||
function getSelectedRowIndexes() {
|
||||
if (selectedCells.find(x => x[0] == 'header')) return _.range(0, grider.rowCount);
|
||||
return _.uniq((selectedCells || []).map(x => x[0])).filter(x => _.isNumber(x));
|
||||
@ -339,6 +361,206 @@
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
function handleGridKeyDown(event) {
|
||||
if (event.keyCode == keycodes.f5) {
|
||||
event.preventDefault();
|
||||
display.reload();
|
||||
}
|
||||
|
||||
// if (event.keyCode == keycodes.f4) {
|
||||
// event.preventDefault();
|
||||
// handleSwitchToFormView();
|
||||
// }
|
||||
|
||||
// if (event.keyCode == keycodes.s && event.ctrlKey) {
|
||||
// event.preventDefault();
|
||||
// handleSave();
|
||||
// // this.saveAndFocus();
|
||||
// }
|
||||
|
||||
if (event.keyCode == keycodes.n0 && event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
setNull();
|
||||
}
|
||||
|
||||
// if (event.keyCode == keycodes.r && event.ctrlKey) {
|
||||
// event.preventDefault();
|
||||
// revertRowChanges();
|
||||
// }
|
||||
|
||||
// if (event.keyCode == keycodes.f && event.ctrlKey) {
|
||||
// event.preventDefault();
|
||||
// filterSelectedValue();
|
||||
// }
|
||||
|
||||
// if (event.keyCode == keycodes.z && event.ctrlKey) {
|
||||
// event.preventDefault();
|
||||
// undo();
|
||||
// }
|
||||
|
||||
// if (event.keyCode == keycodes.y && event.ctrlKey) {
|
||||
// event.preventDefault();
|
||||
// redo();
|
||||
// }
|
||||
|
||||
// if (event.keyCode == keycodes.c && event.ctrlKey) {
|
||||
// event.preventDefault();
|
||||
// copyToClipboard();
|
||||
// }
|
||||
|
||||
// if (event.keyCode == keycodes.delete && event.ctrlKey) {
|
||||
// event.preventDefault();
|
||||
// deleteSelectedRows();
|
||||
// // this.saveAndFocus();
|
||||
// }
|
||||
|
||||
// if (event.keyCode == keycodes.insert && !event.ctrlKey) {
|
||||
// event.preventDefault();
|
||||
// insertNewRow();
|
||||
// // this.saveAndFocus();
|
||||
// }
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
if (!isRegularCell(shiftDragStartCell)) {
|
||||
shiftDragStartCell = currentCell;
|
||||
}
|
||||
} else {
|
||||
shiftDragStartCell = nullCell;
|
||||
}
|
||||
|
||||
handleCursorMove(event);
|
||||
|
||||
if (event.shiftKey) {
|
||||
selectedCells = getCellRange(shiftDragStartCell || currentCell, currentCell);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCursorMove(event) {
|
||||
if (!isRegularCell(currentCell)) return null;
|
||||
let rowCount = grider.rowCount;
|
||||
if (event.ctrlKey) {
|
||||
switch (event.keyCode) {
|
||||
case keycodes.upArrow:
|
||||
case keycodes.pageUp:
|
||||
return moveCurrentCell(0, currentCell[1], event);
|
||||
case keycodes.downArrow:
|
||||
case keycodes.pageDown:
|
||||
return moveCurrentCell(rowCount - 1, currentCell[1], event);
|
||||
case keycodes.leftArrow:
|
||||
return moveCurrentCell(currentCell[0], 0, event);
|
||||
case keycodes.rightArrow:
|
||||
return moveCurrentCell(currentCell[0], columnSizes.realCount - 1, event);
|
||||
case keycodes.home:
|
||||
return moveCurrentCell(0, 0, event);
|
||||
case keycodes.end:
|
||||
return moveCurrentCell(rowCount - 1, columnSizes.realCount - 1, event);
|
||||
case keycodes.a:
|
||||
selectedCells = [['header', 'header']];
|
||||
event.preventDefault();
|
||||
return ['header', 'header'];
|
||||
}
|
||||
} else {
|
||||
switch (event.keyCode) {
|
||||
case keycodes.upArrow:
|
||||
// if (currentCell[0] == 0) return focusFilterEditor(currentCell[1]);
|
||||
return moveCurrentCell(currentCell[0] - 1, currentCell[1], event);
|
||||
case keycodes.downArrow:
|
||||
case keycodes.enter:
|
||||
return moveCurrentCell(currentCell[0] + 1, currentCell[1], event);
|
||||
case keycodes.leftArrow:
|
||||
return moveCurrentCell(currentCell[0], currentCell[1] - 1, event);
|
||||
case keycodes.rightArrow:
|
||||
return moveCurrentCell(currentCell[0], currentCell[1] + 1, event);
|
||||
case keycodes.home:
|
||||
return moveCurrentCell(currentCell[0], 0, event);
|
||||
case keycodes.end:
|
||||
return moveCurrentCell(currentCell[0], columnSizes.realCount - 1, event);
|
||||
case keycodes.pageUp:
|
||||
return moveCurrentCell(currentCell[0] - visibleRowCountLowerBound, currentCell[1], event);
|
||||
case keycodes.pageDown:
|
||||
return moveCurrentCell(currentCell[0] + visibleRowCountLowerBound, currentCell[1], event);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function setNull() {
|
||||
grider.beginUpdate();
|
||||
selectedCells.filter(isRegularCell).forEach(cell => {
|
||||
setCellValue(cell, null);
|
||||
});
|
||||
grider.endUpdate();
|
||||
}
|
||||
|
||||
function setCellValue(cell, value) {
|
||||
grider.setCellValue(cell[0], realColumnUniqueNames[cell[1]], value);
|
||||
}
|
||||
|
||||
function moveCurrentCell(row, col, event = null) {
|
||||
const rowCount = grider.rowCount;
|
||||
|
||||
if (row < 0) row = 0;
|
||||
if (row >= rowCount) row = rowCount - 1;
|
||||
if (col < 0) col = 0;
|
||||
if (col >= columnSizes.realCount) col = columnSizes.realCount - 1;
|
||||
currentCell = [row, col];
|
||||
// setSelectedCells([...(event.ctrlKey ? selectedCells : []), [row, col]]);
|
||||
selectedCells = [[row, col]];
|
||||
scrollIntoView([row, col]);
|
||||
// this.selectedCells.push(this.currentCell);
|
||||
// this.scrollIntoView(this.currentCell);
|
||||
|
||||
if (event) event.preventDefault();
|
||||
return [row, col];
|
||||
}
|
||||
|
||||
const [inplaceEditorState, dispatchInsplaceEditor] = createReducer((state, action) => {
|
||||
switch (action.type) {
|
||||
case 'show':
|
||||
if (!grider.editable) return {};
|
||||
return {
|
||||
cell: action.cell,
|
||||
text: action.text,
|
||||
selectAll: action.selectAll,
|
||||
};
|
||||
case 'close': {
|
||||
const [row, col] = currentCell || [];
|
||||
if (domFocusField) domFocusField.focus();
|
||||
// @ts-ignore
|
||||
if (action.mode == 'enter' && row) setTimeout(() => moveCurrentCell(row + 1, col), 0);
|
||||
// if (action.mode == 'save') setTimeout(handleSave, 0);
|
||||
return {};
|
||||
}
|
||||
case 'shouldSave': {
|
||||
return {
|
||||
...state,
|
||||
shouldSave: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}, {});
|
||||
</script>
|
||||
|
||||
<div class="container" bind:clientWidth={containerWidth} bind:clientHeight={containerHeight}>
|
||||
@ -346,6 +568,7 @@
|
||||
type="text"
|
||||
class="focus-field"
|
||||
bind:this={domFocusField}
|
||||
on:keydown={handleGridKeyDown}
|
||||
on:focus={() => {
|
||||
currentDataGrid.set(instance);
|
||||
}}
|
||||
@ -436,6 +659,8 @@
|
||||
selectedCells={filterCellsForRow(selectedCells, rowIndex)}
|
||||
autofillMarkerCell={filterCellForRow(autofillMarkerCell, rowIndex)}
|
||||
focusedColumn={display.focusedColumn}
|
||||
inplaceEditorState={$inplaceEditorState}
|
||||
{dispatchInsplaceEditor}
|
||||
{frameSelection}
|
||||
/>
|
||||
{/each}
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import DataGridCell from './DataGridCell.svelte';
|
||||
import { cellIsSelected } from './gridutil';
|
||||
import InplaceEditor from './InplaceEditor.svelte';
|
||||
|
||||
import RowHeaderCell from './RowHeaderCell.svelte';
|
||||
|
||||
@ -13,6 +14,8 @@
|
||||
export let autofillSelectedCells = undefined;
|
||||
export let autofillMarkerCell = undefined;
|
||||
export let focusedColumn = undefined;
|
||||
export let inplaceEditorState;
|
||||
export let dispatchInsplaceEditor;
|
||||
|
||||
$: rowData = grider.getRowData(rowIndex);
|
||||
$: rowStatus = grider.getRowStatus(rowIndex);
|
||||
@ -29,16 +32,32 @@
|
||||
<tr style={`height: ${rowHeight}px`}>
|
||||
<RowHeaderCell {rowIndex} />
|
||||
{#each visibleRealColumns as col (col.uniqueName)}
|
||||
<DataGridCell
|
||||
{rowIndex}
|
||||
{rowData}
|
||||
{col}
|
||||
{hintFieldsAllowed}
|
||||
isSelected={frameSelection ? false : cellIsSelected(rowIndex, col.colIndex, selectedCells)}
|
||||
isFrameSelected={frameSelection ? cellIsSelected(rowIndex, col.colIndex, selectedCells) : false}
|
||||
isAutofillSelected={cellIsSelected(rowIndex, col.colIndex, autofillSelectedCells)}
|
||||
isFocusedColumn={col.uniqueName == focusedColumn}
|
||||
/>
|
||||
{#if inplaceEditorState.cell && rowIndex == inplaceEditorState.cell[0] && col.colIndex == inplaceEditorState.cell[1]}
|
||||
<InplaceEditor
|
||||
width={col.width}
|
||||
{inplaceEditorState}
|
||||
{dispatchInsplaceEditor}
|
||||
cellValue={rowData[col.uniqueName]}
|
||||
onSetValue={value => grider.setCellValue(rowIndex, col.uniqueName, value)}
|
||||
/>
|
||||
{:else}
|
||||
<DataGridCell
|
||||
{rowIndex}
|
||||
{rowData}
|
||||
{col}
|
||||
{hintFieldsAllowed}
|
||||
isSelected={frameSelection ? false : cellIsSelected(rowIndex, col.colIndex, selectedCells)}
|
||||
isFrameSelected={frameSelection ? cellIsSelected(rowIndex, col.colIndex, selectedCells) : false}
|
||||
isAutofillSelected={cellIsSelected(rowIndex, col.colIndex, autofillSelectedCells)}
|
||||
isFocusedColumn={col.uniqueName == focusedColumn}
|
||||
isModifiedCell={rowStatus.modifiedFields && rowStatus.modifiedFields.has(col.uniqueName)}
|
||||
isModifiedRow={rowStatus.status == 'updated'}
|
||||
isInserted={rowStatus.status == 'inserted' ||
|
||||
(rowStatus.insertedFields && rowStatus.insertedFields.has(col.uniqueName))}
|
||||
isDeleted={rowStatus.status == 'deleted' ||
|
||||
(rowStatus.deletedFields && rowStatus.deletedFields.has(col.uniqueName))}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</tr>
|
||||
|
||||
|
80
packages/web/src/datagrid/InplaceEditor.svelte
Normal file
80
packages/web/src/datagrid/InplaceEditor.svelte
Normal file
@ -0,0 +1,80 @@
|
||||
<script lang="ts">
|
||||
import keycodes from '../utility/keycodes';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
export let inplaceEditorState;
|
||||
export let dispatchInsplaceEditor;
|
||||
export let onSetValue;
|
||||
export let width;
|
||||
export let cellValue;
|
||||
|
||||
let domEditor;
|
||||
|
||||
const widthCopy = width;
|
||||
|
||||
const isChangedRef = { current: !!inplaceEditorState.text };
|
||||
|
||||
function handleKeyDown(event) {
|
||||
switch (event.keyCode) {
|
||||
case keycodes.escape:
|
||||
isChangedRef.current = false;
|
||||
dispatchInsplaceEditor({ type: 'close' });
|
||||
break;
|
||||
case keycodes.enter:
|
||||
if (isChangedRef.current) {
|
||||
// grider.setCellValue(rowIndex, uniqueName, editor.value);
|
||||
onSetValue(domEditor.value);
|
||||
isChangedRef.current = false;
|
||||
}
|
||||
domEditor.blur();
|
||||
dispatchInsplaceEditor({ type: 'close', mode: 'enter' });
|
||||
break;
|
||||
case keycodes.s:
|
||||
if (event.ctrlKey) {
|
||||
if (isChangedRef.current) {
|
||||
onSetValue(domEditor.value);
|
||||
// grider.setCellValue(rowIndex, uniqueName, editor.value);
|
||||
isChangedRef.current = false;
|
||||
}
|
||||
event.preventDefault();
|
||||
dispatchInsplaceEditor({ type: 'close', mode: 'save' });
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function handleBlur() {
|
||||
if (isChangedRef.current) {
|
||||
onSetValue(domEditor.value);
|
||||
// grider.setCellValue(rowIndex, uniqueName, editor.value);
|
||||
isChangedRef.current = false;
|
||||
}
|
||||
dispatchInsplaceEditor({ type: 'close' });
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
domEditor.value = inplaceEditorState.text || cellValue;
|
||||
domEditor.focus();
|
||||
if (inplaceEditorState.selectAll) {
|
||||
domEditor.select();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<input
|
||||
type="text"
|
||||
on:change={() => (isChangedRef.current = true)}
|
||||
on:keydown={handleKeyDown}
|
||||
on:blur={handleBlur}
|
||||
bind:this={domEditor}
|
||||
style={`width:${widthCopy}px;min-width:${widthCopy}px;max-width:${widthCopy}px`}
|
||||
/>
|
||||
|
||||
<style>
|
||||
input {
|
||||
border: 0px solid;
|
||||
outline: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
</style>
|
@ -44,6 +44,12 @@
|
||||
</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 axios from '../utility/axios';
|
||||
import ChangeSetGrider from './ChangeSetGrider';
|
||||
|
||||
@ -55,11 +61,47 @@
|
||||
export let schemaName;
|
||||
export let pureName;
|
||||
export let config;
|
||||
export let changeSetState;
|
||||
export let dispatchChangeSet;
|
||||
|
||||
let loadedRows = [];
|
||||
|
||||
// $: console.log('loadedRows BIND', loadedRows);
|
||||
$: grider = new ChangeSetGrider(loadedRows, null, null, display);
|
||||
$: grider = new ChangeSetGrider(loadedRows, changeSetState, dispatchChangeSet, display);
|
||||
// $: console.log('GRIDER', grider);
|
||||
|
||||
async function handleConfirmSql(sql) {
|
||||
const resp = await axios.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() });
|
||||
display.reload();
|
||||
}
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
const script = changeSetToSql(changeSetState && changeSetState.value, display.dbinfo);
|
||||
const sql = scriptToSql(display.driver, script);
|
||||
showModal(ConfirmSqlModal, { sql, onConfirm: () => handleConfirmSql(sql) });
|
||||
}
|
||||
</script>
|
||||
|
||||
<LoadingDataGridCore {...$$props} {loadDataPage} {dataPageAvailable} {loadRowCount} bind:loadedRows {grider} />
|
||||
<LoadingDataGridCore
|
||||
{...$$props}
|
||||
{loadDataPage}
|
||||
{dataPageAvailable}
|
||||
{loadRowCount}
|
||||
bind:loadedRows
|
||||
{grider}
|
||||
onSave={handleSave}
|
||||
/>
|
||||
|
38
packages/web/src/modals/ConfirmSqlModal.svelte
Normal file
38
packages/web/src/modals/ConfirmSqlModal.svelte
Normal file
@ -0,0 +1,38 @@
|
||||
<script>
|
||||
import FormStyledButton from '../elements/FormStyledButton.svelte';
|
||||
import FormProvider from '../forms/FormProvider.svelte';
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
|
||||
export let sql;
|
||||
export let onConfirm;
|
||||
</script>
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<div slot="header">Save changes</div>
|
||||
|
||||
<textarea class="editor" value={sql} />
|
||||
|
||||
<div slot="footer">
|
||||
<FormSubmit
|
||||
value="OK"
|
||||
on:click={() => {
|
||||
closeCurrentModal();
|
||||
onConfirm();
|
||||
}}
|
||||
/>
|
||||
<FormStyledButton type="button" value="Close" onClick={closeCurrentModal} />
|
||||
</div>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
<style>
|
||||
.editor {
|
||||
position: relative;
|
||||
height: 30vh;
|
||||
width: 40vw;
|
||||
}
|
||||
</style>
|
41
packages/web/src/modals/ErrorMessageModal.svelte
Normal file
41
packages/web/src/modals/ErrorMessageModal.svelte
Normal file
@ -0,0 +1,41 @@
|
||||
<script>
|
||||
import FormStyledButton from '../elements/FormStyledButton.svelte';
|
||||
import FormProvider from '../forms/FormProvider.svelte';
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import FontIcon from '../icons/FontIcon.svelte';
|
||||
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
|
||||
export let title = 'Error';
|
||||
export let message;
|
||||
</script>
|
||||
|
||||
<FormProvider>
|
||||
<ModalBase {...$$restProps}>
|
||||
<div slot="header">{title}</div>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="icon">
|
||||
<FontIcon icon="img error" />
|
||||
</div>
|
||||
{message}
|
||||
</div>
|
||||
|
||||
<div slot="footer">
|
||||
<FormStyledButton type="button" value="Close" onClick={closeCurrentModal} />
|
||||
</div>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
|
||||
<style>
|
||||
.wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
margin-right: 10px;
|
||||
font-size: 20pt;
|
||||
}
|
||||
</style>
|
@ -1,4 +1,4 @@
|
||||
import { writable, derived } from 'svelte/store';
|
||||
import { writable, derived, readable } from 'svelte/store';
|
||||
import { ExtensionsDirectory } from 'dbgate-types';
|
||||
|
||||
interface TabDefinition {
|
||||
@ -37,6 +37,7 @@ export const visibleToolbar = writableWithStorage(1, 'visibleToolbar');
|
||||
export const leftPanelWidth = writable(300);
|
||||
export const currentDropDownMenu = writable(null);
|
||||
export const openedModals = writable([]);
|
||||
export const nullStore = readable(null, () => {});
|
||||
|
||||
subscribeCssVariable(selectedWidget, x => (x ? 1 : 0), '--dim-visible-left-panel');
|
||||
subscribeCssVariable(visibleToolbar, x => (x ? 1 : 0), '--dim-visible-toolbar');
|
||||
|
@ -2,9 +2,16 @@
|
||||
import App from '../App.svelte';
|
||||
import TableDataGrid from '../datagrid/TableDataGrid.svelte';
|
||||
import useGridConfig from '../utility/useGridConfig';
|
||||
import { createGridCache, createGridConfig, TableFormViewDisplay, TableGridDisplay } from 'dbgate-datalib';
|
||||
import {
|
||||
createChangeSet,
|
||||
createGridCache,
|
||||
createGridConfig,
|
||||
TableFormViewDisplay,
|
||||
TableGridDisplay,
|
||||
} from 'dbgate-datalib';
|
||||
import { findEngineDriver } from 'dbgate-tools';
|
||||
import { writable } from 'svelte/store';
|
||||
import createUndoReducer from '../utility/createUndoReducer';
|
||||
|
||||
export let tabid;
|
||||
export let conid;
|
||||
@ -14,6 +21,17 @@
|
||||
|
||||
const config = useGridConfig(tabid);
|
||||
const cache = writable(createGridCache());
|
||||
|
||||
const [changeSetStore, dispatchChangeSet] = createUndoReducer(createChangeSet());
|
||||
</script>
|
||||
|
||||
<TableDataGrid {...$$props} config={$config} setConfig={config.update} cache={$cache} setCache={cache.update} />
|
||||
<TableDataGrid
|
||||
{...$$props}
|
||||
config={$config}
|
||||
setConfig={config.update}
|
||||
cache={$cache}
|
||||
setCache={cache.update}
|
||||
changeSetState={$changeSetStore}
|
||||
{changeSetStore}
|
||||
{dispatchChangeSet}
|
||||
/>
|
||||
|
11
packages/web/src/utility/createReducer.ts
Normal file
11
packages/web/src/utility/createReducer.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export default function createReducer(reducer, initialState): any {
|
||||
const state = writable(initialState);
|
||||
|
||||
function dispatch(action) {
|
||||
state.update(x => reducer(x, action));
|
||||
}
|
||||
|
||||
return [state, dispatch];
|
||||
}
|
69
packages/web/src/utility/createUndoReducer.ts
Normal file
69
packages/web/src/utility/createUndoReducer.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import _ from 'lodash';
|
||||
import createReducer from './createReducer';
|
||||
|
||||
const reducer = options => (state, action) => {
|
||||
const { mergeNearActions } = options || {};
|
||||
|
||||
const useMerge =
|
||||
action.useMerge || (mergeNearActions && state.lastActionTm && new Date().getTime() - state.lastActionTm < 100);
|
||||
|
||||
switch (action.type) {
|
||||
case 'set':
|
||||
return {
|
||||
history: [...state.history.slice(0, useMerge ? state.current : state.current + 1), action.value],
|
||||
current: useMerge ? state.current : state.current + 1,
|
||||
value: action.value,
|
||||
canUndo: true,
|
||||
canRedo: false,
|
||||
lastActionTm: new Date().getTime(),
|
||||
};
|
||||
case 'compute': {
|
||||
const newValue = action.compute(state.history[state.current]);
|
||||
return {
|
||||
history: [...state.history.slice(0, useMerge ? state.current : state.current + 1), newValue],
|
||||
current: useMerge ? state.current : state.current + 1,
|
||||
value: newValue,
|
||||
canUndo: true,
|
||||
canRedo: false,
|
||||
lastActionTm: new Date().getTime(),
|
||||
};
|
||||
}
|
||||
case 'undo':
|
||||
if (state.current > 0)
|
||||
return {
|
||||
history: state.history,
|
||||
current: state.current - 1,
|
||||
value: state.history[state.current - 1],
|
||||
canUndo: state.current > 1,
|
||||
canRedo: true,
|
||||
lastActionTm: null,
|
||||
};
|
||||
return state;
|
||||
case 'redo':
|
||||
if (state.current < state.history.length - 1)
|
||||
return {
|
||||
history: state.history,
|
||||
current: state.current + 1,
|
||||
value: state.history[state.current + 1],
|
||||
canUndo: true,
|
||||
canRedo: state.current < state.history.length - 2,
|
||||
lastActionTm: null,
|
||||
};
|
||||
return state;
|
||||
case 'reset':
|
||||
return {
|
||||
history: [action.value],
|
||||
current: 0,
|
||||
value: action.value,
|
||||
lastActionTm: null,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export default function createUndoReducer(initialValue, options = null) {
|
||||
return createReducer(reducer(options), {
|
||||
history: [initialValue],
|
||||
current: 0,
|
||||
value: initialValue,
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user