mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-30 23:13:57 +00:00
D & D CSV files
This commit is contained in:
@@ -37,6 +37,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mdi/font": "^5.9.55",
|
"@mdi/font": "^5.9.55",
|
||||||
|
"file-selector": "^0.2.4",
|
||||||
"sirv-cli": "^1.0.0",
|
"sirv-cli": "^1.0.0",
|
||||||
"svelte-select": "^3.17.0"
|
"svelte-select": "^3.17.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
<script>
|
<script>
|
||||||
import WidgetContainer from './widgets/WidgetContainer.svelte';
|
import WidgetContainer from './widgets/WidgetContainer.svelte';
|
||||||
import WidgetIconPanel from './widgets/WidgetIconPanel.svelte';
|
import WidgetIconPanel from './widgets/WidgetIconPanel.svelte';
|
||||||
import { currentTheme, leftPanelWidth, selectedWidget, visibleCommandPalette, visibleToolbar } from './stores';
|
import {
|
||||||
|
currentTheme,
|
||||||
|
isFileDragActive,
|
||||||
|
leftPanelWidth,
|
||||||
|
selectedWidget,
|
||||||
|
visibleCommandPalette,
|
||||||
|
visibleToolbar,
|
||||||
|
} from './stores';
|
||||||
import TabsPanel from './widgets/TabsPanel.svelte';
|
import TabsPanel from './widgets/TabsPanel.svelte';
|
||||||
import TabRegister from './TabRegister.svelte';
|
import TabRegister from './TabRegister.svelte';
|
||||||
import CommandPalette from './commands/CommandPalette.svelte';
|
import CommandPalette from './commands/CommandPalette.svelte';
|
||||||
@@ -11,9 +18,10 @@
|
|||||||
import StatusBar from './widgets/StatusBar.svelte';
|
import StatusBar from './widgets/StatusBar.svelte';
|
||||||
import ModalLayer from './modals/ModalLayer.svelte';
|
import ModalLayer from './modals/ModalLayer.svelte';
|
||||||
import DragAndDropFileTarget from './DragAndDropFileTarget.svelte';
|
import DragAndDropFileTarget from './DragAndDropFileTarget.svelte';
|
||||||
|
import dragDropFileTarget from './utility/dragDropFileTarget';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={`${$currentTheme} root`}>
|
<div class={`${$currentTheme} root`} use:dragDropFileTarget>
|
||||||
<div class="iconbar">
|
<div class="iconbar">
|
||||||
<WidgetIconPanel />
|
<WidgetIconPanel />
|
||||||
</div>
|
</div>
|
||||||
@@ -50,7 +58,9 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<CurrentDropDownMenu />
|
<CurrentDropDownMenu />
|
||||||
<ModalLayer />
|
<ModalLayer />
|
||||||
<!-- <DragAndDropFileTarget /> -->
|
{#if $isFileDragActive}
|
||||||
|
<DragAndDropFileTarget />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -1,11 +1,48 @@
|
|||||||
<script lang="ts">
|
<script lang="ts" context="module">
|
||||||
import { writable } from 'svelte/store';
|
async function addFileToSourceListDefault({ fileName, shortName, isDownload }, newSources, newValues) {
|
||||||
|
const sourceName = shortName;
|
||||||
|
newSources.push(sourceName);
|
||||||
|
newValues[`sourceFile_${sourceName}`] = {
|
||||||
|
fileName,
|
||||||
|
isDownload,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addFilesToSourceList(extensions, files, values, valuesStore, preferedStorageType, setPreviewSource) {
|
||||||
|
const newSources = [];
|
||||||
|
const newValues = {};
|
||||||
|
const storage = preferedStorageType || values.sourceStorageType;
|
||||||
|
for (const file of getAsArray(files)) {
|
||||||
|
const format = findFileFormat(extensions, storage);
|
||||||
|
if (format) {
|
||||||
|
await (format.addFileToSourceList || addFileToSourceListDefault)(file, newSources, newValues);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newValues['sourceList'] = [...(values.sourceList || []).filter(x => !newSources.includes(x)), ...newSources];
|
||||||
|
if (preferedStorageType && preferedStorageType != values.sourceStorageType) {
|
||||||
|
newValues['sourceStorageType'] = preferedStorageType;
|
||||||
|
}
|
||||||
|
valuesStore.set({
|
||||||
|
...values,
|
||||||
|
...newValues,
|
||||||
|
});
|
||||||
|
if (setPreviewSource && newSources.length == 1) {
|
||||||
|
setPreviewSource(newSources[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
import TableControl from '../elements/TableControl.svelte';
|
import TableControl from '../elements/TableControl.svelte';
|
||||||
import { getFormContext } from '../forms/FormProviderCore.svelte';
|
import { getFormContext } from '../forms/FormProviderCore.svelte';
|
||||||
|
|
||||||
import FontIcon from '../icons/FontIcon.svelte';
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
|
import { findFileFormat } from '../plugins/fileformats';
|
||||||
|
import { extensions } from '../stores';
|
||||||
|
import getAsArray from '../utility/getAsArray';
|
||||||
import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
|
import { useConnectionInfo, useDatabaseInfo } from '../utility/metadataLoaders';
|
||||||
|
import { setUploadListener } from '../utility/uploadFiles';
|
||||||
import PreviewCheckBox from './PreviewCheckBox.svelte';
|
import PreviewCheckBox from './PreviewCheckBox.svelte';
|
||||||
import SourceAction from './SourceAction.svelte';
|
import SourceAction from './SourceAction.svelte';
|
||||||
import SourceName from './SourceName.svelte';
|
import SourceName from './SourceName.svelte';
|
||||||
@@ -13,13 +50,60 @@
|
|||||||
import SourceTargetConfig from './SourceTargetConfig.svelte';
|
import SourceTargetConfig from './SourceTargetConfig.svelte';
|
||||||
import TargetName from './TargetName.svelte';
|
import TargetName from './TargetName.svelte';
|
||||||
|
|
||||||
|
export let uploadedFile = undefined;
|
||||||
|
export let openedFile = undefined;
|
||||||
|
|
||||||
const { values } = getFormContext();
|
const { values } = getFormContext();
|
||||||
|
|
||||||
$: targetDbinfo = useDatabaseInfo({ conid: $values.targetConnectionId, database: $values.targetDatabaseName });
|
$: targetDbinfo = useDatabaseInfo({ conid: $values.targetConnectionId, database: $values.targetDatabaseName });
|
||||||
$: sourceConnectionInfo = useConnectionInfo({ conid: $values.sourceConnectionId });
|
$: sourceConnectionInfo = useConnectionInfo({ conid: $values.sourceConnectionId });
|
||||||
|
$: sourceList = $values.sourceList;
|
||||||
|
|
||||||
const previewSource = writable(null);
|
const previewSource = writable(null);
|
||||||
|
|
||||||
|
const handleUpload = file => {
|
||||||
|
addFilesToSourceList(
|
||||||
|
$extensions,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
fileName: file.filePath,
|
||||||
|
shortName: file.shortName,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
$values,
|
||||||
|
values,
|
||||||
|
!sourceList || sourceList.length == 0 ? file.storageType : null,
|
||||||
|
previewSource.set
|
||||||
|
);
|
||||||
|
// setFieldValue('sourceList', [...(sourceList || []), file.originalName]);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
setUploadListener(handleUpload);
|
||||||
|
if (uploadedFile) {
|
||||||
|
handleUpload(uploadedFile);
|
||||||
|
}
|
||||||
|
if (openedFile) {
|
||||||
|
handleUpload(openedFile);
|
||||||
|
// addFilesToSourceList(
|
||||||
|
// $extensions,
|
||||||
|
// [
|
||||||
|
// {
|
||||||
|
// fileName: openedFile.filePath,
|
||||||
|
// shortName: openedFile.shortName,
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// $values,
|
||||||
|
// values,
|
||||||
|
// !sourceList || sourceList.length == 0 ? openedFile.storageType : null,
|
||||||
|
// previewSource.set
|
||||||
|
// );
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
setUploadListener(null);
|
||||||
|
};
|
||||||
|
});
|
||||||
// engine={sourceEngine}
|
// engine={sourceEngine}
|
||||||
// {setPreviewSource}
|
// {setPreviewSource}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -120,7 +120,7 @@
|
|||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
<HorizontalSplitter initialValue="70%">
|
<HorizontalSplitter initialValue="70%">
|
||||||
<div class="content" slot="1">
|
<div class="content" slot="1">
|
||||||
<ImportExportConfigurator />
|
<ImportExportConfigurator {uploadedFile} {openedFile} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<svelte:fragment slot="2">
|
<svelte:fragment slot="2">
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export const currentDropDownMenu = writable(null);
|
|||||||
export const openedModals = writable([]);
|
export const openedModals = writable([]);
|
||||||
export const nullStore = readable(null, () => {});
|
export const nullStore = readable(null, () => {});
|
||||||
export const currentArchive = writable('default');
|
export const currentArchive = writable('default');
|
||||||
|
export const isFileDragActive = writable(false);
|
||||||
|
|
||||||
subscribeCssVariable(selectedWidget, x => (x ? 1 : 0), '--dim-visible-left-panel');
|
subscribeCssVariable(selectedWidget, x => (x ? 1 : 0), '--dim-visible-left-panel');
|
||||||
subscribeCssVariable(visibleToolbar, x => (x ? 1 : 0), '--dim-visible-toolbar');
|
subscribeCssVariable(visibleToolbar, x => (x ? 1 : 0), '--dim-visible-toolbar');
|
||||||
|
|||||||
86
packages/web/src/utility/dragDropFileTarget.ts
Normal file
86
packages/web/src/utility/dragDropFileTarget.ts
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import _ from 'lodash';
|
||||||
|
import { isFileDragActive } from '../stores';
|
||||||
|
import { fromEvent } from 'file-selector';
|
||||||
|
import uploadFiles from './uploadFiles';
|
||||||
|
|
||||||
|
function isEvtWithFiles(event) {
|
||||||
|
if (!event.dataTransfer) {
|
||||||
|
return !!event.target && !!event.target.files;
|
||||||
|
}
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/types
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file
|
||||||
|
return Array.prototype.some.call(
|
||||||
|
event.dataTransfer.types,
|
||||||
|
type => type === 'Files' || type === 'application/x-moz-file'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default function dragDropFileTarget(node, items) {
|
||||||
|
let dragTargetsRef = [];
|
||||||
|
|
||||||
|
function handleDragEnter(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
dragTargetsRef = [...dragTargetsRef, event.target];
|
||||||
|
|
||||||
|
if (isEvtWithFiles(event)) {
|
||||||
|
isFileDragActive.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function handleDragOver(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
if (event.dataTransfer) {
|
||||||
|
try {
|
||||||
|
event.dataTransfer.dropEffect = 'copy';
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleDragLeave(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
// Only deactivate once the dropzone and all children have been left
|
||||||
|
const targets = dragTargetsRef.filter(target => node && node.contains(target));
|
||||||
|
// Make sure to remove a target present multiple times only once
|
||||||
|
// (Firefox may fire dragenter/dragleave multiple times on the same element)
|
||||||
|
const targetIdx = targets.indexOf(event.target);
|
||||||
|
if (targetIdx !== -1) {
|
||||||
|
targets.splice(targetIdx, 1);
|
||||||
|
}
|
||||||
|
dragTargetsRef = targets;
|
||||||
|
if (targets.length > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFileDragActive.set(false);
|
||||||
|
}
|
||||||
|
async function handleDrop(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
isFileDragActive.set(false);
|
||||||
|
|
||||||
|
if (isEvtWithFiles(event)) {
|
||||||
|
const files = await fromEvent(event);
|
||||||
|
uploadFiles(files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.addEventListener('dragenter', handleDragEnter);
|
||||||
|
node.addEventListener('dragover', handleDragOver);
|
||||||
|
node.addEventListener('dragleave', handleDragLeave);
|
||||||
|
node.addEventListener('drop', handleDrop);
|
||||||
|
|
||||||
|
return {
|
||||||
|
destroy() {
|
||||||
|
node.removeEventListener('dragenter', handleDragEnter);
|
||||||
|
node.removeEventListener('dragover', handleDragOver);
|
||||||
|
node.removeEventListener('dragleave', handleDragLeave);
|
||||||
|
node.removeEventListener('drop', handleDrop);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
47
packages/web/src/utility/openElectronFile.ts
Normal file
47
packages/web/src/utility/openElectronFile.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { showModal } from '../modals/modalTools';
|
||||||
|
import newQuery from '../query/newQuery';
|
||||||
|
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
||||||
|
|
||||||
|
export function canOpenByElectron(file, extensions) {
|
||||||
|
if (!file) return false;
|
||||||
|
const nameLower = file.toLowerCase();
|
||||||
|
if (nameLower.endsWith('.sql')) return true;
|
||||||
|
for (const format of extensions.fileFormats) {
|
||||||
|
if (nameLower.endsWith(`.${format.extension}`)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openElectronFileCore(filePath, extensions) {
|
||||||
|
const nameLower = filePath.toLowerCase();
|
||||||
|
const path = window.require('path');
|
||||||
|
const fs = window.require('fs');
|
||||||
|
const parsed = path.parse(filePath);
|
||||||
|
|
||||||
|
if (nameLower.endsWith('.sql')) {
|
||||||
|
const data = fs.readFileSync(filePath, { encoding: 'utf-8' });
|
||||||
|
|
||||||
|
newQuery({
|
||||||
|
title: parsed.name,
|
||||||
|
initialData: data,
|
||||||
|
// @ts-ignore
|
||||||
|
savedFilePath: filePath,
|
||||||
|
savedFormat: 'text',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (const format of extensions.fileFormats) {
|
||||||
|
if (nameLower.endsWith(`.${format.extension}`)) {
|
||||||
|
showModal(ImportExportModal, {
|
||||||
|
openedFile: {
|
||||||
|
filePath,
|
||||||
|
storageType: format.storageType,
|
||||||
|
shortName: parsed.name,
|
||||||
|
},
|
||||||
|
importToArchive: true,
|
||||||
|
initialValues: {
|
||||||
|
sourceStorageType: format.storageType,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
packages/web/src/utility/uploadFiles.ts
Normal file
78
packages/web/src/utility/uploadFiles.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { extensions } from '../stores';
|
||||||
|
import { get } from 'svelte/store';
|
||||||
|
import { canOpenByElectron, openElectronFileCore } from './openElectronFile';
|
||||||
|
import getElectron from './getElectron';
|
||||||
|
import resolveApi from './resolveApi';
|
||||||
|
import { findFileFormat } from '../plugins/fileformats';
|
||||||
|
import { showModal } from '../modals/modalTools';
|
||||||
|
import ImportExportModal from '../modals/ImportExportModal.svelte';
|
||||||
|
|
||||||
|
let uploadListener;
|
||||||
|
|
||||||
|
export function setUploadListener(value) {
|
||||||
|
uploadListener = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function uploadFiles(files) {
|
||||||
|
const ext = get(extensions);
|
||||||
|
const electron = getElectron();
|
||||||
|
files.forEach(async file => {
|
||||||
|
if (parseInt(file.size, 10) >= 4 * 1024 * 1024) {
|
||||||
|
// to big file
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('FILE', file);
|
||||||
|
|
||||||
|
if (electron && canOpenByElectron(file.path, ext)) {
|
||||||
|
openElectronFileCore(file.path, ext);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('data', file);
|
||||||
|
|
||||||
|
const fetchOptions = {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
};
|
||||||
|
|
||||||
|
const apiBase = resolveApi();
|
||||||
|
const resp = await fetch(`${apiBase}/uploads/upload`, fetchOptions);
|
||||||
|
const fileData = await resp.json();
|
||||||
|
|
||||||
|
fileData.shortName = file.name;
|
||||||
|
|
||||||
|
for (const format of ext.fileFormats) {
|
||||||
|
if (file.name.endsWith('.' + format.extension)) {
|
||||||
|
fileData.shortName = file.name.slice(0, -format.extension.length - 1);
|
||||||
|
fileData.storageType = format.storageType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uploadListener) {
|
||||||
|
uploadListener(fileData);
|
||||||
|
} else {
|
||||||
|
if (findFileFormat(ext, fileData.storageType)) {
|
||||||
|
showModal(ImportExportModal, {
|
||||||
|
uploadedFile: fileData,
|
||||||
|
importToArchive: true,
|
||||||
|
initialValues: {
|
||||||
|
sourceStorageType: fileData.storageType,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// const reader = new FileReader();
|
||||||
|
|
||||||
|
// reader.onabort = () => console.log('file reading was aborted');
|
||||||
|
// reader.onerror = () => console.log('file reading has failed');
|
||||||
|
// reader.onload = () => {
|
||||||
|
// // Do whatever you want with the file contents
|
||||||
|
// const binaryStr = reader.result;
|
||||||
|
// console.log(binaryStr);
|
||||||
|
// };
|
||||||
|
// reader.readAsArrayBuffer(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -2789,6 +2789,13 @@ file-entry-cache@^5.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
flat-cache "^2.0.1"
|
flat-cache "^2.0.1"
|
||||||
|
|
||||||
|
file-selector@^0.2.4:
|
||||||
|
version "0.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.2.4.tgz#7b98286f9dbb9925f420130ea5ed0a69238d4d80"
|
||||||
|
integrity sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA==
|
||||||
|
dependencies:
|
||||||
|
tslib "^2.0.3"
|
||||||
|
|
||||||
file-uri-to-path@1.0.0:
|
file-uri-to-path@1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
||||||
@@ -7224,7 +7231,7 @@ tslib@^1.9.0:
|
|||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
|
||||||
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
|
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
|
||||||
|
|
||||||
tslib@^2.0.0:
|
tslib@^2.0.0, tslib@^2.0.3:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
|
||||||
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
|
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
|
||||||
|
|||||||
Reference in New Issue
Block a user