mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
D & D CSV files
This commit is contained in:
parent
834eeabd3f
commit
500c1c76ba
@ -37,6 +37,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/font": "^5.9.55",
|
||||
"file-selector": "^0.2.4",
|
||||
"sirv-cli": "^1.0.0",
|
||||
"svelte-select": "^3.17.0"
|
||||
}
|
||||
|
@ -1,7 +1,14 @@
|
||||
<script>
|
||||
import WidgetContainer from './widgets/WidgetContainer.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 TabRegister from './TabRegister.svelte';
|
||||
import CommandPalette from './commands/CommandPalette.svelte';
|
||||
@ -11,9 +18,10 @@
|
||||
import StatusBar from './widgets/StatusBar.svelte';
|
||||
import ModalLayer from './modals/ModalLayer.svelte';
|
||||
import DragAndDropFileTarget from './DragAndDropFileTarget.svelte';
|
||||
import dragDropFileTarget from './utility/dragDropFileTarget';
|
||||
</script>
|
||||
|
||||
<div class={`${$currentTheme} root`}>
|
||||
<div class={`${$currentTheme} root`} use:dragDropFileTarget>
|
||||
<div class="iconbar">
|
||||
<WidgetIconPanel />
|
||||
</div>
|
||||
@ -50,7 +58,9 @@
|
||||
{/if}
|
||||
<CurrentDropDownMenu />
|
||||
<ModalLayer />
|
||||
<!-- <DragAndDropFileTarget /> -->
|
||||
{#if $isFileDragActive}
|
||||
<DragAndDropFileTarget />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
@ -1,11 +1,48 @@
|
||||
<script lang="ts">
|
||||
import { writable } from 'svelte/store';
|
||||
<script lang="ts" context="module">
|
||||
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 { getFormContext } from '../forms/FormProviderCore.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 { setUploadListener } from '../utility/uploadFiles';
|
||||
import PreviewCheckBox from './PreviewCheckBox.svelte';
|
||||
import SourceAction from './SourceAction.svelte';
|
||||
import SourceName from './SourceName.svelte';
|
||||
@ -13,13 +50,60 @@
|
||||
import SourceTargetConfig from './SourceTargetConfig.svelte';
|
||||
import TargetName from './TargetName.svelte';
|
||||
|
||||
export let uploadedFile = undefined;
|
||||
export let openedFile = undefined;
|
||||
|
||||
const { values } = getFormContext();
|
||||
|
||||
$: targetDbinfo = useDatabaseInfo({ conid: $values.targetConnectionId, database: $values.targetDatabaseName });
|
||||
$: sourceConnectionInfo = useConnectionInfo({ conid: $values.sourceConnectionId });
|
||||
$: sourceList = $values.sourceList;
|
||||
|
||||
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}
|
||||
// {setPreviewSource}
|
||||
</script>
|
||||
|
@ -120,7 +120,7 @@
|
||||
<div class="wrapper">
|
||||
<HorizontalSplitter initialValue="70%">
|
||||
<div class="content" slot="1">
|
||||
<ImportExportConfigurator />
|
||||
<ImportExportConfigurator {uploadedFile} {openedFile} />
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="2">
|
||||
|
@ -40,6 +40,7 @@ export const currentDropDownMenu = writable(null);
|
||||
export const openedModals = writable([]);
|
||||
export const nullStore = readable(null, () => {});
|
||||
export const currentArchive = writable('default');
|
||||
export const isFileDragActive = writable(false);
|
||||
|
||||
subscribeCssVariable(selectedWidget, x => (x ? 1 : 0), '--dim-visible-left-panel');
|
||||
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:
|
||||
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:
|
||||
version "1.0.0"
|
||||
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"
|
||||
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
|
||||
|
||||
tslib@^2.0.0:
|
||||
tslib@^2.0.0, tslib@^2.0.3:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
|
||||
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
|
||||
|
Loading…
Reference in New Issue
Block a user