web/satellite{/vuetify-poc}: implement CSV file previewing
This change allows .csv files to be previewed in the file browser. Resolves #6426 Change-Id: Ib93ebb417b8f69231ed2b36b8258ad91c6a1ba4b
This commit is contained in:
parent
42e1b088c2
commit
405491e8d0
@ -18,6 +18,7 @@
|
||||
"@stripe/stripe-js": "2.1.0",
|
||||
"bip39-english": "2.5.0",
|
||||
"chart.js": "4.2.1",
|
||||
"papaparse": "5.4.1",
|
||||
"pinia": "2.0.23",
|
||||
"pretty-bytes": "5.6.0",
|
||||
"qrcode": "1.5.3",
|
||||
@ -31,6 +32,7 @@
|
||||
"devDependencies": {
|
||||
"@types/filesystem": "0.0.32",
|
||||
"@types/node": "18.17.1",
|
||||
"@types/papaparse": "5.3.10",
|
||||
"@types/qrcode": "1.5.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.59.5",
|
||||
"@typescript-eslint/parser": "5.59.5",
|
||||
@ -2571,6 +2573,15 @@
|
||||
"integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/papaparse": {
|
||||
"version": "5.3.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/papaparse/-/papaparse-5.3.10.tgz",
|
||||
"integrity": "sha512-mS1Fta/xJ9EDYmAvpeWzcV9Gr0cOl1ClpW7di9+wSUNDIDO55tBtyXg97O7K+Syrd9rDEmuejM2iqmJIJ1SO5g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qrcode": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.0.tgz",
|
||||
@ -7259,6 +7270,11 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/papaparse": {
|
||||
"version": "5.4.1",
|
||||
"resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz",
|
||||
"integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw=="
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
|
@ -26,6 +26,7 @@
|
||||
"@stripe/stripe-js": "2.1.0",
|
||||
"bip39-english": "2.5.0",
|
||||
"chart.js": "4.2.1",
|
||||
"papaparse": "5.4.1",
|
||||
"pinia": "2.0.23",
|
||||
"pretty-bytes": "5.6.0",
|
||||
"qrcode": "1.5.3",
|
||||
@ -39,6 +40,7 @@
|
||||
"devDependencies": {
|
||||
"@types/filesystem": "0.0.32",
|
||||
"@types/node": "18.17.1",
|
||||
"@types/papaparse": "5.3.10",
|
||||
"@types/qrcode": "1.5.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.59.5",
|
||||
"@typescript-eslint/parser": "5.59.5",
|
||||
|
@ -160,6 +160,7 @@ onBeforeUnmount((): void => {
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
@ -171,4 +172,8 @@ onBeforeUnmount((): void => {
|
||||
border-radius: 6px;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
</style>
|
||||
|
@ -0,0 +1,84 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<VLoader v-if="isLoading" class="csv-file-preview__loader" width="100px" height="100px" is-white />
|
||||
<div v-else-if="!isError" class="csv-file-preview__container">
|
||||
<table>
|
||||
<tr v-for="(row, rowIdx) in items" :key="rowIdx">
|
||||
<td v-for="(col, colIdx) in row" :key="colIdx">
|
||||
{{ col }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<slot v-else />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import Papa, { ParseResult } from 'papaparse';
|
||||
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
|
||||
import VLoader from '@/components/common/VLoader.vue';
|
||||
|
||||
const notify = useNotify();
|
||||
|
||||
const props = defineProps<{
|
||||
src: string;
|
||||
}>();
|
||||
|
||||
const items = ref<string[][]>([]);
|
||||
const isLoading = ref<boolean>(true);
|
||||
const isError = ref<boolean>(false);
|
||||
|
||||
onMounted(() => {
|
||||
Papa.parse(props.src, {
|
||||
download: true,
|
||||
worker: true,
|
||||
header: false,
|
||||
skipEmptyLines: true,
|
||||
complete: (results: ParseResult<string[]>) => {
|
||||
if (results) items.value = results.data;
|
||||
isLoading.value = false;
|
||||
},
|
||||
error: (error: Error) => {
|
||||
if (isError.value) return;
|
||||
notify.error(`Error parsing object. ${error.message}`, AnalyticsErrorEventSource.GALLERY_VIEW);
|
||||
isError.value = true;
|
||||
},
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.csv-file-preview {
|
||||
|
||||
&__container {
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
align-self: flex-start;
|
||||
|
||||
table {
|
||||
min-width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
td {
|
||||
background: var(--c-white);
|
||||
border: 1px solid var(--c-grey-3);
|
||||
padding: 6px 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__loader {
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -60,6 +60,9 @@
|
||||
<text-file-preview v-if="previewType === PreviewType.Text" :src="objectPreviewUrl">
|
||||
<file-preview-placeholder :file="file" @download="download" />
|
||||
</text-file-preview>
|
||||
<c-s-v-file-preview v-else-if="previewType === PreviewType.CSV" :src="objectPreviewUrl">
|
||||
<file-preview-placeholder :file="file" @download="download" />
|
||||
</c-s-v-file-preview>
|
||||
<img
|
||||
v-else-if="previewType === PreviewType.Image"
|
||||
:src="objectPreviewUrl"
|
||||
@ -123,6 +126,7 @@ import DetailsModal from '@/components/browser/galleryView/modals/Details.vue';
|
||||
import DistributionModal from '@/components/browser/galleryView/modals/Distribution.vue';
|
||||
import VLoader from '@/components/common/VLoader.vue';
|
||||
import TextFilePreview from '@/components/browser/galleryView/TextFilePreview.vue';
|
||||
import CSVFilePreview from '@/components/browser/galleryView/CSVFilePreview.vue';
|
||||
import FilePreviewPlaceholder from '@/components/browser/galleryView/FilePreviewPlaceholder.vue';
|
||||
|
||||
import LogoIcon from '@/../static/images/logo.svg';
|
||||
|
@ -81,6 +81,7 @@ export enum ShareType {
|
||||
export enum PreviewType {
|
||||
None,
|
||||
Text,
|
||||
CSV,
|
||||
Image,
|
||||
Video,
|
||||
Audio,
|
||||
@ -89,6 +90,7 @@ export enum PreviewType {
|
||||
|
||||
export const EXTENSION_PREVIEW_TYPES = new Map<string[], PreviewType>([
|
||||
[['txt'], PreviewType.Text],
|
||||
[['csv'], PreviewType.CSV],
|
||||
[['bmp', 'svg', 'jpg', 'jpeg', 'png', 'ico', 'gif'], PreviewType.Image],
|
||||
[['m4v', 'mp4', 'webm', 'mov', 'mkv'], PreviewType.Video],
|
||||
[['m4a', 'mp3', 'wav', 'ogg'], PreviewType.Audio],
|
||||
|
@ -0,0 +1,74 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
<template>
|
||||
<div v-if="isLoading" class="w-100 h-100 d-flex align-center justify-center mt-n16">
|
||||
<v-progress-circular indeterminate />
|
||||
</div>
|
||||
<v-container v-else-if="!isError" class="w-100 max-h-100 overflow-auto">
|
||||
<table :class="`v-theme--${theme.global.name.value}`">
|
||||
<tr v-for="(row, rowIdx) in items" :key="rowIdx">
|
||||
<td v-for="(col, colIdx) in row" :key="colIdx">
|
||||
{{ col }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</v-container>
|
||||
<slot v-else />
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useTheme } from 'vuetify';
|
||||
import { VProgressCircular, VContainer } from 'vuetify/components';
|
||||
import Papa, { ParseResult } from 'papaparse';
|
||||
|
||||
import { useNotify } from '@/utils/hooks';
|
||||
import { AnalyticsErrorEventSource } from '@/utils/constants/analyticsEventNames';
|
||||
|
||||
const theme = useTheme();
|
||||
const notify = useNotify();
|
||||
|
||||
const props = defineProps<{
|
||||
src: string;
|
||||
}>();
|
||||
|
||||
const items = ref<string[][]>([]);
|
||||
const isLoading = ref<boolean>(true);
|
||||
const isError = ref<boolean>(false);
|
||||
|
||||
onMounted(() => {
|
||||
Papa.parse(props.src, {
|
||||
download: true,
|
||||
worker: true,
|
||||
header: false,
|
||||
skipEmptyLines: true,
|
||||
complete: (results: ParseResult<string[]>) => {
|
||||
if (results) items.value = results.data;
|
||||
isLoading.value = false;
|
||||
},
|
||||
error: (error: Error) => {
|
||||
if (isError.value) return;
|
||||
notify.error(`Error parsing object. ${error.message}`, AnalyticsErrorEventSource.GALLERY_VIEW);
|
||||
isError.value = true;
|
||||
},
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
table {
|
||||
min-width: 100%;
|
||||
color: rgb(var(--v-theme-on-background));
|
||||
border-collapse: collapse;
|
||||
|
||||
td {
|
||||
background: rgb(var(--v-theme-surface));
|
||||
padding: 6px 10px;
|
||||
white-space: nowrap;
|
||||
|
||||
/* stylelint-disable-next-line color-function-notation */
|
||||
border: 1px solid rgba(var(--v-border-color),var(--v-border-opacity));
|
||||
}
|
||||
}
|
||||
</style>
|
@ -8,6 +8,9 @@
|
||||
<text-file-preview v-else-if="previewType === PreviewType.Text" :src="objectPreviewUrl">
|
||||
<file-preview-placeholder :file="file" @download="emit('download')" />
|
||||
</text-file-preview>
|
||||
<c-s-v-file-preview v-else-if="previewType === PreviewType.CSV" :src="objectPreviewUrl">
|
||||
<file-preview-placeholder :file="file" @download="emit('download')" />
|
||||
</c-s-v-file-preview>
|
||||
<v-container v-else-if="previewType === PreviewType.Video" class="fill-height flex-column justify-center align-center">
|
||||
<video
|
||||
controls
|
||||
@ -58,6 +61,7 @@ import { EXTENSION_PREVIEW_TYPES, PreviewType } from '@/types/browser';
|
||||
|
||||
import FilePreviewPlaceholder from '@poc/components/dialogs/filePreviewComponents/FilePreviewPlaceholder.vue';
|
||||
import TextFilePreview from '@poc/components/dialogs/filePreviewComponents/TextFilePreview.vue';
|
||||
import CSVFilePreview from '@poc/components/dialogs/filePreviewComponents/CSVFilePreview.vue';
|
||||
|
||||
const obStore = useObjectBrowserStore();
|
||||
const bucketsStore = useBucketsStore();
|
||||
|
@ -243,6 +243,7 @@ table {
|
||||
// Scrollbar
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: rgb(var(--v-theme-background));
|
||||
@ -252,6 +253,9 @@ table {
|
||||
border-radius: 2px;
|
||||
min-height: 5px;
|
||||
}
|
||||
::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
// Alerts
|
||||
.v-alert-title {
|
||||
@ -278,6 +282,11 @@ table {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
// Sizing
|
||||
.max-h-100 {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
// text styles
|
||||
.text-cursor-pointer {
|
||||
cursor: pointer;
|
||||
|
Loading…
Reference in New Issue
Block a user