web/satellite: FileBrowser component migrated to use composition api

Change-Id: Id0bb007b790e0d2365eb78f2bc78f8c3da05bc70
This commit is contained in:
NickolaiYurchenko 2022-10-26 20:07:39 +03:00 committed by Nikolay Yurchenko
parent e4fab975ad
commit 8ebea4cf8d
14 changed files with 756 additions and 917 deletions

View File

@ -32,13 +32,7 @@ module.exports = {
'indent': ['warn', 4],
'vue/html-indent': ['warn', 4],
'@typescript-eslint/no-unused-vars': [
'warn', {
'vars': 'all',
'args': 'all',
'argsIgnorePattern': '^_',
}],
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-var-requires': 'off',
@ -65,6 +59,8 @@ module.exports = {
'newlines-between': 'always',
}],
'no-duplicate-imports': 'error',
'import/default': 'off',
'vue/script-setup-uses-vars': 'error',
'object-curly-spacing': ['error', 'always'],
'quotes': ['error', 'single', { 'allowTemplateLiterals': true }],
'semi': ['error', 'always'],

View File

@ -146,7 +146,6 @@
items-label="access grants"
:limit="accessGrantLimit"
:total-page-count="totalPageCount"
:items="accessGrantsList"
:total-items-count="accessGrantsTotalCount"
:on-page-click-callback="onPageClick"
>
@ -186,7 +185,6 @@
:selectable="true"
:limit="accessGrantLimit"
:total-page-count="totalPageCount"
:items="accessGrantsList"
:total-items-count="accessGrantsTotalCount"
:on-page-click-callback="onPageClick"
>

View File

@ -155,7 +155,6 @@
<v-table
class="payments-area__transactions-area__table"
items-label="transactions"
:items="displayedHistory"
:limit="pageSize"
:total-page-count="pageCount"
:total-items-count="transactionCount"

View File

@ -4,7 +4,7 @@
<template>
<div class="mb-3">
<div class="d-inline">
<a class="d-inline path-buckets" @click="() => $emit('bucketClick')">Buckets</a>
<a class="d-inline path-buckets" @click="bucketClick">Buckets</a>
<svg
class="mx-3"
width="6"
@ -24,9 +24,9 @@
</div>
<div v-for="(path, idx) in crumbs" :key="idx" class="d-inline">
<router-link :to="link(idx)">
<span @click="redirectToCrumb(idx)">
<a class="path" href="javascript:null">{{ path }}</a>
</router-link>
</span>
<svg
v-if="displayDivider(idx)"
@ -49,48 +49,65 @@
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
<script setup lang="ts">
import { computed } from 'vue';
// @vue/component
@Component
export default class BreadCrumbs extends Vue {
/**
* Retrieves the current bucket name from the store.
*/
private get bucketName(): string {
return this.$store.state.files.bucket;
}
import { useRouter, useStore } from '@/utils/hooks';
/**
* Retrieves the current path from the store and creates an array of folders for the bread crumbs that the user can click on.
*/
public get crumbs(): string[] {
let path: string[] = this.$store.state.files.path.split('/');
path =
path.length > 1
? [this.bucketName, ...path.slice(0, path.length - 1)]
: [this.bucketName];
return path;
}
const store = useStore();
const router = useRouter();
/**
* Returns a link to the folder at the current breadcrumb index.
*/
public link(idx: number): string {
const crumbs = this.crumbs;
let path = '';
if (idx > 0) path = crumbs.slice(1, idx + 1).join('/') + '/';
return this.$store.state.files.browserRoot + path;
}
const emit = defineEmits(['onUpdate']);
/**
* Returns a boolean denoting if a divider needs to be displayed at current breadcrumb index.
*/
public displayDivider(idx: number): boolean {
const length = this.crumbs.length;
return (idx !== 0 || length > 1) && idx !== length - 1;
}
/**
* Retrieves the current bucket name from the store.
*/
const bucketName = computed((): string => {
return store.state.files.bucket;
});
/**
* Retrieves the current path from the store and creates an array of folders for the bread crumbs that the user can click on.
*/
const crumbs = computed((): string[] => {
let path: string[] = store.state.files.path.split('/');
path =
path.length > 1
? [bucketName.value, ...path.slice(0, path.length - 1)]
: [bucketName.value];
return path;
});
function bucketClick() {
emit('bucketClick');
}
/**
* Redirects to partial upload to bucket buckets path.
*/
async function redirectToCrumb(idx: number): Promise<void> {
await router.push(link(idx)).catch(err => {});
emit('onUpdate');
}
/**
* Returns a link to the folder at the current breadcrumb index.
*/
function link(idx: number): string {
let path = '';
if (idx > 0) path = crumbs.value.slice(1, idx + 1).join('/') + '/';
return store.state.files.browserRoot + path;
}
/**
* Returns a boolean denoting if a divider needs to be displayed at current breadcrumb index.
*/
function displayDivider(idx: number): boolean {
const length = crumbs.value.length;
return (idx !== 0 || length > 1) && idx !== length - 1;
}
</script>

View File

@ -11,7 +11,7 @@
@drop.prevent="upload"
@dragover.prevent
>
<bread-crumbs @bucketClick="goToBuckets" />
<bread-crumbs @onUpdate="onRouteChange" @bucketClick="goToBuckets" />
<div class="tile-action-bar">
<h2 class="tile-action-bar__title">{{ bucketName }}</h2>
@ -150,7 +150,7 @@
<td />
</tr>
<tr v-if="filesUploadingLength">
<tr v-if="filesUploading.length">
<div class="files-uploading-count my-3">
<div
class="px-2"
@ -164,14 +164,13 @@
<tr v-if="path.length > 0">
<td class="px-3">
<router-link to="../">
<span @click="onBack">
<a
id="navigate-back"
href="javascript:null"
class="px-2 font-weight-bold"
@click="back"
>..</a>
</router-link>
</span>
</td>
</tr>
@ -180,6 +179,7 @@
:key="file.Key"
:path="path"
:file="file"
@onUpdate="onRouteChange"
/>
<file-entry
@ -191,7 +191,7 @@
</template>
</v-table>
<div
v-if="displayUpload"
v-if="!fetchingFilesSpinner"
class="upload-help"
@click="buttonFileUpload"
>
@ -201,7 +201,7 @@
</p>
</div>
<div
v-if="fetchingFilesSpinner"
v-else
class="d-flex justify-content-center"
>
<div class="spinner-border" />
@ -212,8 +212,8 @@
</div>
</template>
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator';
<script setup lang="ts">
import { computed, onBeforeMount, ref } from 'vue';
import FileBrowserHeader from './FileBrowserHeader.vue';
import FileEntry from './FileEntry.vue';
@ -224,6 +224,7 @@ import { BrowserFile } from '@/types/browser';
import { AnalyticsEvent } from '@/utils/constants/analyticsEventNames';
import { RouteConfig } from '@/router';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useRouter, useStore } from '@/utils/hooks';
import eventBus from '@/utils/eventBus';
import BucketSettingsNav from '@/components/objects/BucketSettingsNav.vue';
@ -233,290 +234,245 @@ import BlackArrowHide from '@/../static/images/common/BlackArrowHide.svg';
import BlackArrowExpand from '@/../static/images/common/BlackArrowExpand.svg';
import UploadIcon from '@/../static/images/browser/upload.svg';
// @vue/component
@Component({
components: {
BucketSettingsNav,
FileEntry,
BreadCrumbs,
VTable,
FileBrowserHeader,
BlackArrowExpand,
BlackArrowHide,
UploadIcon,
},
})
export default class FileBrowser extends Vue {
public $refs!: {
folderInput: HTMLInputElement;
fileInput: HTMLInputElement;
const store = useStore();
const router = useRouter();
const folderInput = ref<HTMLInputElement>(null);
const fileInput = ref<HTMLInputElement>(null);
const fetchingFilesSpinner = ref<boolean>(false);
const isUploadDropDownShown = ref<boolean>(false);
const analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Check if the s3 client has been initialized in the store.
*/
const isInitialized = computed((): boolean => {
return store.getters['files/isInitialized'];
});
/**
* Retrieve the current path from the store.
*/
const path = computed((): string => {
return store.state.files.path;
});
/**
* Return files that are currently being uploaded from the store.
*/
const filesUploading = computed((): string => {
return store.state.files.uploading;
});
/**
* Return up to five files currently being uploaded for display purposes.
*/
const formattedFilesUploading = computed((): BrowserFile[] => {
if (filesUploading.value.length > 5) {
return filesUploading.value.slice(0, 5);
}
return filesUploading.value;
});
/**
* Return the text of how many files in total are being uploaded to be displayed to give users more context.
*/
const formattedFilesWaitingToBeUploaded = computed((): BrowserFile[] => {
let file = 'file';
if (filesUploading.value.length > 1) {
file = 'files';
}
return `${filesUploading.value.length} ${file}`;
});
const bucketName = computed((): string => {
return store.state.files.bucket;
});
const files = computed((): BrowserFile[] => {
return store.getters['files/sortedFiles'];
});
/**
* Return an array of BrowserFile type that are files and not folders.
*/
const singleFiles = computed((): BrowserFile[] => {
return files.value.filter((f) => f.type === 'file');
});
/**
* Return an array of BrowserFile type that are folders and not files.
*/
const folders = computed((): BrowserFile[] => {
return files.value.filter((f) => f.type === 'folder');
});
/**
* Retrieve the pathMatch from the current route.
*/
const routePath = ref(calculateRoutePath());
/**
* Returns bucket name from store.
*/
const bucket = computed((): string => {
return store.state.objectsModule.fileComponentBucketName;
});
function calculateRoutePath(): string {
let pathMatch = router.history.current.params.pathMatch;
pathMatch = Array.isArray(pathMatch)
? pathMatch.join('/') + '/'
: pathMatch;
return pathMatch;
}
async function onBack(): Promise<void> {
await router.push('../');
await onRouteChange();
}
async function onRouteChange(): Promise<void> {
routePath.value = calculateRoutePath();
await store.dispatch('files/closeDropdown');
await list(routePath.value);
}
/**
* Set spinner state. If routePath is not present navigate away. If there's some error re-render the page with a call to list. All of this is done on the created lifecycle method.
*/
onBeforeMount(async () => {
if (!bucket.value) {
const path = RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path;
await analytics.pageVisit(path);
await router.push(path);
return;
}
// display the spinner while files are being fetched
fetchingFilesSpinner.value = true;
if (!routePath.value) {
try {
await analytics.pageVisit(`${store.state.files.browserRoot}${path.value}`);
await router.push({
path: `${store.state.files.browserRoot}${path.value}`,
});
} catch (err) {
await list('');
}
}
// remove the spinner after files have been fetched
fetchingFilesSpinner.value = false;
});
/**
* Close modal, file share modal, dropdown, and remove all selected files from the store.
*/
function closeModalDropdown(): void {
if (store.state.files.openedDropdown) {
store.dispatch('files/closeDropdown');
}
if (store.state.files.selectedFile) {
store.dispatch('files/clearAllSelectedFiles');
}
}
/**
* Toggle the folder creation modal in the store.
*/
function toggleFolderCreationModal(): void {
store.commit(APP_STATE_MUTATIONS.TOGGLE_NEW_FOLDER_MODAL_SHOWN);
}
/**
* Return the file name of the passed in file argument formatted.
*/
function filename(file: BrowserFile): string {
return file.Key.length > 25
? file.Key.slice(0, 25) + '...'
: file.Key;
}
/**
* Upload the current selected or dragged-and-dropped file.
*/
async function upload(e: Event): Promise<void> {
const callback = () => {
eventBus.$emit('upload_progress');
};
public fetchingFilesSpinner = false;
public isUploadDropDownShown = false;
await store.dispatch('files/upload', { e, callback });
await analytics.eventTriggered(AnalyticsEvent.OBJECT_UPLOADED);
const target = e.target as HTMLInputElement;
target.value = '';
}
private readonly analytics: AnalyticsHttpApi = new AnalyticsHttpApi();
/**
* Cancel the upload of the current file that's passed in as an argument.
*/
function cancelUpload(fileName: string): void {
store.dispatch('files/cancelUpload', fileName);
}
/**
* Check if the s3 client has been initialized in the store.
*/
public get isInitialized(): boolean {
return this.$store.getters['files/isInitialized'];
}
/**
* Call the list method from the store, which will trigger a re-render and fetch all files under the current path passed in as an argument.
*/
async function list(path: string): Promise<void> {
await store.dispatch('files/list', path, {
root: true,
});
}
/**
* Retrieve the current path from the store.
*/
private get path(): string {
return this.$store.state.files.path;
}
/**
* Open the operating system's file system for file upload.
*/
async function buttonFileUpload(): Promise<void> {
await analytics.eventTriggered(AnalyticsEvent.UPLOAD_FILE_CLICKED);
const fileInputElement = fileInput.value as HTMLInputElement;
fileInputElement.click();
closeUploadDropdown();
}
/**
* Return files that are currently being uploaded from the store.
*/
private get filesUploading(): BrowserFile[] {
return this.$store.state.files.uploading;
}
/**
* Open the operating system's file system for folder upload.
*/
async function buttonFolderUpload(): Promise<void> {
await analytics.eventTriggered(AnalyticsEvent.UPLOAD_FOLDER_CLICKED);
const folderInputElement = folderInput.value as HTMLInputElement;
folderInputElement.click();
closeUploadDropdown();
}
/**
* Return the length of the array of files currently being uploaded.
*/
public get filesUploadingLength(): number {
return this.filesUploading.length;
}
/**
* Toggles upload options dropdown.
*/
function toggleUploadDropdown(): void {
isUploadDropDownShown.value = !isUploadDropDownShown.value;
}
/**
* Return up to five files currently being uploaded for display purposes.
*/
public get formattedFilesUploading(): BrowserFile[] {
if (this.filesUploadingLength > 5) {
return this.filesUploading.slice(0, 5);
}
return this.filesUploading;
}
/**
* Return the text of how many files in total are being uploaded to be displayed to give users more context.
*/
public get formattedFilesWaitingToBeUploaded(): string {
let file = 'file';
if (this.filesUploadingLength > 1) {
file = 'files';
}
return `${this.filesUploadingLength} ${file}`;
}
/**
* Retrieve the current bucket from the store.
*/
private get bucketName(): string {
return this.$store.state.files.bucket;
}
/**
* Retrieve all of the files sorted from the store.
*/
private get files(): BrowserFile[] {
return this.$store.getters['files/sortedFiles'];
}
/**
* Return an array of BrowserFile type that are files and not folders.
*/
public get singleFiles(): BrowserFile[] {
return this.files.filter((f) => f.type === 'file');
}
/**
* Return an array of BrowserFile type that are folders and not files.
*/
public get folders(): BrowserFile[] {
return this.files.filter((f) => f.type === 'folder');
}
/**
* Retrieve the pathMatch from the current route.
*/
private get routePath(): string {
let pathMatch = this.$route.params.pathMatch;
pathMatch = Array.isArray(pathMatch)
? pathMatch.join('/') + '/'
: pathMatch;
return pathMatch;
}
/**
* Returns bucket name from store.
*/
private get bucket(): string {
return this.$store.state.objectsModule.fileComponentBucketName;
}
/**
* Return a boolean signifying whether the upload display is allowed to be shown.
*/
public get displayUpload(): boolean {
return !this.fetchingFilesSpinner;
}
/**
* Watch for changes in the path and call goToRoutePath, navigating away from the current page.
*/
@Watch('routePath')
private async handleRoutePathChange() {
await this.goToRoutePath();
}
/**
* Set spinner state. If routePath is not present navigate away. If there's some error re-render the page with a call to list. All of this is done on the created lifecycle method.
*/
public async created(): Promise<void> {
if (!this.bucket) {
const path = RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path;
this.analytics.pageVisit(path);
await this.$router.push(path);
return;
}
// display the spinner while files are being fetched
this.fetchingFilesSpinner = true;
if (!this.routePath) {
try {
this.analytics.pageVisit(`${this.$store.state.files.browserRoot}${this.path}`);
await this.$router.push({
path: `${this.$store.state.files.browserRoot}${this.path}`,
});
} catch (err) {
await this.list('');
}
}
// remove the spinner after files have been fetched
this.fetchingFilesSpinner = false;
}
/**
* Close modal, file share modal, dropdown, and remove all selected files from the store.
*/
public closeModalDropdown(): void {
if (this.$store.state.files.openedDropdown) {
this.$store.dispatch('files/closeDropdown');
}
if (this.$store.state.files.selectedFile) {
this.$store.dispatch('files/clearAllSelectedFiles');
}
}
/**
* Toggle the folder creation modal in the store.
*/
public toggleFolderCreationModal(): void {
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_NEW_FOLDER_MODAL_SHOWN);
}
/**
* Return the file name of the passed in file argument formatted.
*/
public filename(file: BrowserFile): string {
return file.Key.length > 25
? file.Key.slice(0, 25) + '...'
: file.Key;
}
/**
* Upload the current selected or dragged-and-dropped file.
*/
public async upload(e: Event): Promise<void> {
const callback = () => {
eventBus.$emit('upload_progress');
};
await this.$store.dispatch('files/upload', { e, callback });
this.analytics.eventTriggered(AnalyticsEvent.OBJECT_UPLOADED);
const target = e.target as HTMLInputElement;
target.value = '';
}
/**
* Cancel the upload of the current file that's passed in as an argument.
*/
public cancelUpload(fileName: string): void {
this.$store.dispatch('files/cancelUpload', fileName);
}
/**
* Call the list method from the store, which will trigger a re-render and fetch all files under the current path passed in as an argument.
*/
private async list(path: string): Promise<void> {
await this.$store.dispatch('files/list', path, {
root: true,
});
}
/**
* Remove the folder creation input and close any opened dropdowns when a user chooses to navigate back to the previous folder.
*/
public async back(): Promise<void> {
await this.$store.dispatch('files/closeDropdown');
}
/**
* Navigate to the path under routePath.
*/
private async goToRoutePath(): Promise<void> {
if (typeof this.routePath === 'string') {
await this.$store.dispatch('files/closeDropdown');
await this.list(this.routePath);
}
}
/**
* Open the operating system's file system for file upload.
*/
public async buttonFileUpload(): Promise<void> {
this.analytics.eventTriggered(AnalyticsEvent.UPLOAD_FILE_CLICKED);
const fileInputElement = this.$refs.fileInput as HTMLInputElement;
fileInputElement.click();
this.closeUploadDropdown();
}
/**
* Open the operating system's file system for folder upload.
*/
public async buttonFolderUpload(): Promise<void> {
this.analytics.eventTriggered(AnalyticsEvent.UPLOAD_FOLDER_CLICKED);
const folderInputElement = this.$refs.folderInput as HTMLInputElement;
folderInputElement.click();
this.closeUploadDropdown();
}
/**
* Toggles upload options dropdown.
*/
public toggleUploadDropdown(): void {
this.isUploadDropDownShown = !this.isUploadDropDownShown;
}
/**
* Closes upload options dropdown.
*/
public closeUploadDropdown(): void {
this.isUploadDropDownShown = false;
}
/**
* Redirects to buckets list view.
*/
public goToBuckets(): void {
this.analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path);
this.$router.push(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path);
}
/**
* Closes upload options dropdown.
*/
function closeUploadDropdown(): void {
isUploadDropDownShown.value = false;
}
/**
* Redirects to buckets list view.
*/
async function goToBuckets(): Promise<void> {
await analytics.pageVisit(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path);
await router.push(RouteConfig.Buckets.with(RouteConfig.BucketsManagement).path).catch(err => {});
await onRouteChange();
}
</script>

View File

@ -6,13 +6,13 @@
<th
class="w-50"
scope="col"
@mouseover="mouseOverName"
@mouseover="mouseOver('name')"
@mouseleave="mouseLeave"
@click="sortByName"
@click="sortBy('name')"
>
Name
<span v-if="showNameArrow">
<a v-if="nameDesc" class="arrow">
<span v-if="showArrow('name')">
<a v-if="isDesc('name')" class="arrow">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
@ -47,13 +47,13 @@
<th
class="file-browser-heading w-25"
scope="col"
@mouseover="mouseOverSize"
@mouseover="mouseOver('size')"
@mouseleave="mouseLeave"
@click="sortBySize"
@click="sortBy('size')"
>
Size
<span v-if="showSizeArrow">
<a v-if="sizeDesc" class="arrow">
<span v-if="showArrow('size')">
<a v-if="isDesc('size')" class="arrow">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
@ -88,13 +88,13 @@
<th
class="file-browser-heading"
scope="col"
@mouseover="mouseOverDate"
@mouseover="mouseOver('date')"
@mouseleave="mouseLeave"
@click="sortByDate"
@click="sortBy('date')"
>
Upload Date
<span v-if="showDateArrow">
<a v-if="dateDesc" class="arrow">
<span v-if="showArrow('date')">
<a v-if="isDesc('date')" class="arrow">
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
@ -132,7 +132,7 @@
v-if="filesToDelete"
id="header-delete"
class="d-flex justify-content-end"
@click="deleteSelectedDropdown"
@click.stop="deleteSelectedDropdown"
>
<svg
xmlns="http://www.w3.org/2000/svg"
@ -152,7 +152,7 @@
</svg>
</a>
<div
v-if="displayDropdown"
v-if="isDropdownDisplayed"
class="dropdown-menu shadow show"
>
<div>
@ -211,202 +211,93 @@
</fragment>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
<script setup lang="ts">
import { Fragment } from 'vue-fragment';
import { computed, ref } from 'vue';
// @vue/component
@Component({
components: {
Fragment,
},
})
export default class FileBrowserHeader extends Vue {
private hover = '';
import { useStore } from '@/utils/hooks';
/**
* Check if a heading is sorted in descending order.
*/
private isDesc(heading: string): boolean {
return this.headingSorted === heading && this.orderBy === 'desc';
}
const store = useStore();
/**
* Check if sorting arrow should be displayed.
*/
private showArrow(heading: string): boolean {
return this.headingSorted === heading || this.hover === heading;
}
const hover = ref('');
/**
* Retreive a string property from the store.
*/
private fromFilesStore(prop: string): string {
return this.$store.state.files[prop];
}
function fromFilesStore (prop: string): string {
return store.state.files[prop];
}
/**
* Set the heading of the current heading being hovered over.
*/
private mouseOver(heading: string): void {
this.hover = heading;
}
/**
* Check if the trashcan to delete selected files/folder should be displayed.
*/
const filesToDelete = computed( (): string => {
return (!!store.state.files.selectedAnchorFile || (
store.state.files.unselectedAnchorFile &&
(store.state.files.selectedFiles.length > 0 ||
store.state.files.shiftSelectedFiles.length > 0)
));
});
/**
* Set the heading for files/folders to be sorted by in the store.
*/
private sortBy(heading: string): void {
this.$store.commit('files/sort', heading);
}
/**
* Check if the files/folders deletion dropdown should be displayed.
*/
const isDropdownDisplayed = computed( (): boolean => {
return store.state.files.openedDropdown === 'FileBrowser';
});
/**
* Get the current heading being sorted from the store.
*/
private get headingSorted(): string {
return this.fromFilesStore('headingSorted');
}
/**
* Check if a heading is sorted in descending order.
*/
function isDesc(heading: string): boolean {
return fromFilesStore('headingSorted') === heading && fromFilesStore('orderBy') === 'desc';
}
/**
* Get the current order being sorted from the store.
*/
private get orderBy(): string {
return this.fromFilesStore('orderBy');
}
/**
* Check if sorting arrow should be displayed.
*/
function showArrow(heading: string): boolean {
return fromFilesStore('headingSorted') === heading || hover.value === heading;
}
/**
* Check if the name heading is being sorted in descending order.
*/
public get nameDesc(): boolean {
return this.isDesc('name');
}
/**
* Set the heading of the current heading being hovered over.
*/
function mouseOver(heading: string): void {
hover.value = heading;
}
/**
* Check if the size heading is being sorted in descending order.
*/
public get sizeDesc(): boolean {
return this.isDesc('size');
}
/**
* Set the heading for files/folders to be sorted by in the store.
*/
function sortBy(heading: string): void {
store.commit('files/sort', heading);
}
/**
* Check if the date heading is being sorted in descending order.
*/
public get dateDesc(): boolean {
return this.isDesc('date');
}
/**
* Change the hover property to nothing on mouse leave.
*/
function mouseLeave(): void {
hover.value = '';
}
/**
* Check if the name heading's arrow should be displayed.
*/
public get showNameArrow(): boolean {
return this.showArrow('name');
}
/**
* Open the deletion of files/folders dropdown.
*/
function deleteSelectedDropdown(): void {
store.dispatch('files/openFileBrowserDropdown');
}
/**
* Check if the size heading's arrow should be displayed.
*/
public get showSizeArrow(): boolean {
return this.showArrow('size');
}
/**
* Delete the selected files/folders.
*/
function confirmDeleteSelection(): void {
store.dispatch('files/deleteSelected');
store.dispatch('files/closeDropdown');
}
/**
* Check if the date heading's arrow should be displayed.
*/
public get showDateArrow(): boolean {
return this.showArrow('date');
}
/**
* Check if the trashcan to delete selected files/folder should be displayed.
*/
public get filesToDelete(): boolean {
return (
!!this.$store.state.files.selectedAnchorFile ||
!!(
this.$store.state.files.unselectedAnchorFile &&
(this.$store.state.files.selectedFiles.length > 0 ||
this.$store.state.files.shiftSelectedFiles.length > 0)
)
);
}
/**
* Check if the files/folders deletion dropdown should be displayed.
*/
public get displayDropdown(): boolean {
return this.$store.state.files.openedDropdown === 'FileBrowser';
}
/**
* Sort files/folder based on their name.
*/
public sortByName(): void {
this.sortBy('name');
}
/**
* Sort files/folder based on their size.
*/
public sortBySize(): void {
this.sortBy('size');
}
/**
* Sort files/folder based on their date.
*/
public sortByDate(): void {
this.sortBy('date');
}
/**
* Change the hover property to the name heading on hover.
*/
public mouseOverName(): void {
this.mouseOver('name');
}
/**
* Change the hover property to the size heading on hover.
*/
public mouseOverSize(): void {
this.mouseOver('size');
}
/**
* Change the hover property to the date heading on hover.
*/
public mouseOverDate(): void {
this.mouseOver('date');
}
/**
* Change the hover property to nothing on mouse leave.
*/
public mouseLeave(): void {
this.hover = '';
}
/**
* Open the deletion of files/folders dropdown.
*/
public deleteSelectedDropdown(event: Event): void {
event.stopPropagation();
this.$store.dispatch('files/openFileBrowserDropdown');
}
/**
* Delete the selected files/folders.
*/
public confirmDeleteSelection(): void {
this.$store.dispatch('files/deleteSelected');
this.$store.dispatch('files/closeDropdown');
}
/**
* Abort files/folder selected for deletion.
*/
public cancelDeleteSelection(): void {
this.$store.dispatch('files/closeDropdown');
}
/**
* Abort files/folder selected for deletion.
*/
function cancelDeleteSelection(): void {
store.dispatch('files/closeDropdown');
}
</script>

View File

@ -7,7 +7,7 @@
:class="{ 'selected-row': isFileSelected }"
@click.stop="selectFile"
>
<td data-ls-disabled>
<td data-ls-disabled class="px-3">
<span v-if="fileTypeIsFolder" class="folder-name">
<svg
class="ml-2 mr-1"
@ -23,16 +23,14 @@
/>
</svg>
<span>
<router-link :to="link">
<a
href="javascript:null"
class="file-name"
aria-roledescription="folder"
>
{{ filename }}
</a>
</router-link>
<span @click="openBucket">
<a
href="javascript:null"
class="file-name"
aria-roledescription="folder"
>
{{ file.Key }}
</a>
</span>
</span>
@ -55,13 +53,11 @@
/>
<path d="M9.5 3V0L14 4.5h-3A1.5 1.5 0 0 1 9.5 3z" />
</svg>
<middle-truncate :text="filename" />
<middle-truncate :text="file.Key" />
</span>
</td>
<td>
<span v-if="fileTypeIsFile" aria-roledescription="file-size">{{
size
}}</span>
<span v-if="fileTypeIsFile" aria-roledescription="file-size">{{ size }}</span>
</td>
<td>
<span
@ -365,337 +361,325 @@
</tr>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
<script setup lang="ts">
import { computed, ref } from 'vue';
import prettyBytes from 'pretty-bytes';
import MiddleTruncate from './MiddleTruncate.vue';
import type { BrowserFile } from '@/types/browser';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useNotify, useRouter, useStore } from '@/utils/hooks';
// @vue/component
@Component({
components: {
MiddleTruncate,
},
})
export default class FileEntry extends Vue {
public deleteConfirmation = false;
const store = useStore();
const notify = useNotify();
const router = useRouter();
@Prop({ default: '' })
private readonly path: string;
@Prop()
private readonly file: BrowserFile;
const props = defineProps({
path: { type: String, default: '' },
file: { type: Object as BrowserFile, default: undefined },
});
/**
* Return the name of file/folder formatted.
*/
public get filename(): string {
return this.file.Key;
const emit = defineEmits(['onUpdate']);
const deleteConfirmation = ref(false);
/**
* Return the size of the file formatted.
*/
const size: string = computed((): string => {
return prettyBytes(props.file.Size);
});
/**
* Return the upload date of the file formatted.
*/
const uploadDate: string = computed((): string => {
return props.file.LastModified.toLocaleString().split(',')[0];
});
/**
* Check with the store to see if the dropdown is open for the current file/folder.
*/
const dropdownOpen: string = computed((): string => {
return store.state.files.openedDropdown === props.file.Key;
});
/**
* Return a link to the current folder for navigation.
*/
const link: string = computed((): string => {
const browserRoot = store.state.files.browserRoot;
const pathAndKey = store.state.files.path + props.file.Key;
return pathAndKey.length > 0
? browserRoot + pathAndKey + '/'
: browserRoot;
});
/**
* Return a flag signifying whether the current file/folder is selected.
*/
const isFileSelected: boolean = computed((): string => {
return Boolean(
store.state.files.selectedAnchorFile === props.file ||
store.state.files.selectedFiles.find(
(file) => file === props.file,
) ||
store.state.files.shiftSelectedFiles.find(
(file) => file === props.file,
),
);
});
/**
* Return a boolean signifying whether the current file/folder is a folder.
*/
const fileTypeIsFolder: boolean = computed((): string => {
return props.file.type === 'folder';
});
/**
* Return a boolean signifying whether the current file/folder is a file.
*/
const fileTypeIsFile: boolean = computed((): string => {
return props.file.type === 'file';
});
/**
* Open the modal for the current file.
*/
function openModal(): void {
store.commit('files/setObjectPathForModal', props.path + props.file.Key);
store.commit(APP_STATE_MUTATIONS.TOGGLE_OBJECT_DETAILS_MODAL_SHOWN);
store.dispatch('files/closeDropdown');
}
/**
* Return a boolean signifying whether the current file/folder is in the process of being deleted, therefore a spinner shoud be shown.
*/
function loadingSpinner(): boolean {
return Boolean(store.state.files.filesToBeDeleted.find(
(file) => file === props.file,
));
}
/**
* Select the current file/folder whether it be a click, click + shiftKey, click + metaKey or ctrlKey, or unselect the rest.
*/
function selectFile(event: KeyboardEvent): void {
if (store.state.files.openedDropdown) {
store.dispatch('files/closeDropdown');
}
/**
* Return the size of the file formatted.
*/
public get size(): string {
return prettyBytes(this.file.Size);
if (event.shiftKey) {
setShiftSelectedFiles();
return;
}
/**
* Return the upload date of the file formatted.
*/
public get uploadDate(): string {
return this.file.LastModified.toLocaleString().split(',')[0];
}
const isSelectedFile = Boolean(event.metaKey || event.ctrlKey);
/**
* Check with the store to see if the dropdown is open for the current file/folder.
*/
public get dropdownOpen(): boolean {
return this.$store.state.files.openedDropdown === this.file.Key;
}
setSelectedFile(isSelectedFile);
}
/**
* Return a link to the current folder for navigation.
*/
public get link(): string {
const browserRoot = this.$store.state.files.browserRoot;
const pathAndKey = this.$store.state.files.path + this.file.Key;
const url =
pathAndKey.length > 0
? browserRoot + pathAndKey + '/'
: browserRoot;
return url;
}
async function openBucket(): Promise<void> {
await router.push(link.value);
emit('onUpdate');
}
/**
* Return a boolean signifying whether the current file/folder is selected.
*/
public get isFileSelected(): boolean {
return !!(
this.$store.state.files.selectedAnchorFile === this.file ||
this.$store.state.files.selectedFiles.find(
(file) => file === this.file,
) ||
this.$store.state.files.shiftSelectedFiles.find(
(file) => file === this.file,
)
/**
* Set the selected file/folder in the store.
*/
function setSelectedFile(command: boolean): void {
/* this function is responsible for selecting and unselecting a file on file click or [CMD + click] AKA command. */
const shiftSelectedFiles =
store.state.files.shiftSelectedFiles;
const selectedFiles = store.state.files.selectedFiles;
const files = [
...selectedFiles,
...shiftSelectedFiles,
];
const selectedAnchorFile =
store.state.files.selectedAnchorFile;
if (command && props.file === selectedAnchorFile) {
/* if it's [CMD + click] and the file selected is the actual selectedAnchorFile, then unselect the file but store it under unselectedAnchorFile in case the user decides to do a [shift + click] right after this action. */
store.commit('files/setUnselectedAnchorFile', props.file);
store.commit('files/setSelectedAnchorFile', null);
} else if (command && files.includes(props.file)) {
/* if it's [CMD + click] and the file selected is a file that has already been selected in selectedFiles and shiftSelectedFiles, then unselect it by filtering it out. */
store.dispatch(
'files/updateSelectedFiles',
selectedFiles.filter(
(fileSelected) => fileSelected !== props.file,
),
);
}
/**
* Return a boolean signifying whether the current file/folder is a folder.
*/
public get fileTypeIsFolder(): boolean {
return this.file.type === 'folder';
}
/**
* Return a boolean signifying whether the current file/folder is a folder.
*/
public get fileTypeIsFile(): boolean {
return this.file.type === 'file';
}
/**
* Open the modal for the current file.
*/
public openModal(): void {
this.$store.commit('files/setObjectPathForModal', this.path + this.file.Key);
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_OBJECT_DETAILS_MODAL_SHOWN);
this.$store.dispatch('files/closeDropdown');
}
/**
* Return a boolean signifying whether the current file/folder is in the process of being deleted, therefore a spinner shoud be shown.
*/
public loadingSpinner(): boolean {
return !!this.$store.state.files.filesToBeDeleted.find(
(file) => file === this.file,
);
}
/**
* Select the current file/folder whether it be a click, click + shiftKey, click + metaKey or ctrlKey, or unselect the rest.
*/
public selectFile(event: KeyboardEvent): void {
if (this.$store.state.files.openedDropdown) {
this.$store.dispatch('files/closeDropdown');
}
if (event.shiftKey) {
this.setShiftSelectedFiles();
} else if (event.metaKey || event.ctrlKey) {
this.setSelectedFile(true);
} else {
this.setSelectedFile(false);
}
}
/**
* Set the selected file/folder in the store.
*/
private setSelectedFile(command: boolean): void {
/* this function is responsible for selecting and unselecting a file on file click or [CMD + click] AKA command. */
const files = [
...this.$store.state.files.selectedFiles,
...this.$store.state.files.shiftSelectedFiles,
];
const selectedAnchorFile =
this.$store.state.files.selectedAnchorFile;
const shiftSelectedFiles =
this.$store.state.files.shiftSelectedFiles;
const selectedFiles = this.$store.state.files.selectedFiles;
if (command && this.file === selectedAnchorFile) {
/* if it's [CMD + click] and the file selected is the actual selectedAnchorFile, then unselect the file but store it under unselectedAnchorFile in case the user decides to do a [shift + click] right after this action. */
this.$store.commit('files/setUnselectedAnchorFile', this.file);
this.$store.commit('files/setSelectedAnchorFile', null);
} else if (command && files.includes(this.file)) {
/* if it's [CMD + click] and the file selected is a file that has already been selected in selectedFiles and shiftSelectedFiles, then unselect it by filtering it out. */
this.$store.dispatch(
'files/updateSelectedFiles',
selectedFiles.filter(
(fileSelected) => fileSelected !== this.file,
),
);
this.$store.dispatch(
'files/updateShiftSelectedFiles',
shiftSelectedFiles.filter(
(fileSelected) => fileSelected !== this.file,
),
);
} else if (command && selectedAnchorFile) {
/* if it's [CMD + click] and there is already a selectedAnchorFile, then add the selectedAnchorFile and shiftSelectedFiles into the array of selectedFiles, set selectedAnchorFile to the file that was clicked, set unselectedAnchorFile to null, and set shiftSelectedFiles to an empty array. */
const filesSelected = [...selectedFiles];
if (!filesSelected.includes(selectedAnchorFile)) {
filesSelected.push(selectedAnchorFile);
}
this.$store.dispatch('files/updateSelectedFiles', [
...filesSelected,
...shiftSelectedFiles.filter(
(file) => !filesSelected.includes(file),
),
]);
this.$store.commit('files/setSelectedAnchorFile', this.file);
this.$store.commit('files/setUnselectedAnchorFile', null);
this.$store.dispatch('files/updateShiftSelectedFiles', []);
} else if (command) {
/* if it's [CMD + click] and it has not met any of the above conditions, then set selectedAnchorFile to file and set unselectedAnchorfile to null, update the selectedFiles, and update the shiftSelectedFiles */
this.$store.commit('files/setSelectedAnchorFile', this.file);
this.$store.commit('files/setUnselectedAnchorFile', null);
this.$store.dispatch('files/updateSelectedFiles', [
...selectedFiles,
...shiftSelectedFiles,
]);
this.$store.dispatch('files/updateShiftSelectedFiles', []);
} else {
/* if it's just a file click without any modifier, then set selectedAnchorFile to the file that was clicked, set shiftSelectedFiles and selectedFiles to an empty array. */
this.$store.commit('files/setSelectedAnchorFile', this.file);
this.$store.dispatch('files/updateShiftSelectedFiles', []);
this.$store.dispatch('files/updateSelectedFiles', []);
}
}
/**
* Set files/folders selected using shift key in the store.
*/
private setShiftSelectedFiles(): void {
/* this function is responsible for selecting all files from selectedAnchorFile to the file that was selected with [shift + click] */
const files = this.$store.getters['files/sortedFiles'];
const unselectedAnchorFile =
this.$store.state.files.unselectedAnchorFile;
if (unselectedAnchorFile) {
/* if there is an unselectedAnchorFile, meaning that in the previous action the user unselected the anchor file but is now chosing to do a [shift + click] on another file, then reset the selectedAnchorFile, the achor file, to unselectedAnchorFile. */
this.$store.commit(
'files/setSelectedAnchorFile',
unselectedAnchorFile,
);
this.$store.commit('files/setUnselectedAnchorFile', null);
}
const selectedAnchorFile =
this.$store.state.files.selectedAnchorFile;
if (!selectedAnchorFile) {
this.$store.commit('files/setSelectedAnchorFile', this.file);
return;
}
const anchorIdx = files.findIndex(
(file) => file === selectedAnchorFile,
);
const shiftIdx = files.findIndex((file) => file === this.file);
const start = Math.min(anchorIdx, shiftIdx);
const end = Math.max(anchorIdx, shiftIdx) + 1;
this.$store.dispatch(
store.dispatch(
'files/updateShiftSelectedFiles',
files
.slice(start, end)
.filter(
(file) =>
!this.$store.state.files.selectedFiles.includes(
file,
) && file !== selectedAnchorFile,
),
shiftSelectedFiles.filter(
(fileSelected) => fileSelected !== props.file,
),
);
}
} else if (command && selectedAnchorFile) {
/* if it's [CMD + click] and there is already a selectedAnchorFile, then add the selectedAnchorFile and shiftSelectedFiles into the array of selectedFiles, set selectedAnchorFile to the file that was clicked, set unselectedAnchorFile to null, and set shiftSelectedFiles to an empty array. */
/**
* Open the share modal for the current file.
*/
public share(): void {
this.$store.dispatch('files/closeDropdown');
this.$store.commit('files/setObjectPathForModal', this.path + this.file.Key);
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_SHARE_OBJECT_MODAL_SHOWN);
}
const filesSelected = [...selectedFiles];
/**
* Toggle the dropdown for the current file/folder.
*/
public toggleDropdown(): void {
if (this.$store.state.files.openedDropdown === this.file.Key) {
this.$store.dispatch('files/closeDropdown');
} else {
this.$store.dispatch('files/openDropdown', this.file.Key);
if (!filesSelected.includes(selectedAnchorFile)) {
filesSelected.push(selectedAnchorFile);
}
// remove the dropdown delete confirmation
this.deleteConfirmation = false;
store.dispatch('files/updateSelectedFiles', [
...filesSelected,
...shiftSelectedFiles.filter(
(file) => !filesSelected.includes(file),
),
]);
store.commit('files/setSelectedAnchorFile', props.file);
store.commit('files/setUnselectedAnchorFile', null);
store.dispatch('files/updateShiftSelectedFiles', []);
} else if (command) {
/* if it's [CMD + click] and it has not met any of the above conditions, then set selectedAnchorFile to file and set unselectedAnchorfile to null, update the selectedFiles, and update the shiftSelectedFiles */
store.commit('files/setSelectedAnchorFile', props.file);
store.commit('files/setUnselectedAnchorFile', null);
store.dispatch('files/updateSelectedFiles', [
...selectedFiles,
...shiftSelectedFiles,
]);
store.dispatch('files/updateShiftSelectedFiles', []);
} else {
/* if it's just a file click without any modifier, then set selectedAnchorFile to the file that was clicked, set shiftSelectedFiles and selectedFiles to an empty array. */
store.commit('files/setSelectedAnchorFile', props.file);
store.dispatch('files/updateShiftSelectedFiles', []);
store.dispatch('files/updateSelectedFiles', []);
}
}
/**
* Set files/folders selected using shift key in the store.
*/
function setShiftSelectedFiles(): void {
/* this function is responsible for selecting all files from selectedAnchorFile to the file that was selected with [shift + click] */
const files = store.getters['files/sortedFiles'];
const unselectedAnchorFile =
store.state.files.unselectedAnchorFile;
if (unselectedAnchorFile) {
/* if there is an unselectedAnchorFile, meaning that in the previous action the user unselected the anchor file but is now chosing to do a [shift + click] on another file, then reset the selectedAnchorFile, the achor file, to unselectedAnchorFile. */
store.commit(
'files/setSelectedAnchorFile',
unselectedAnchorFile,
);
store.commit('files/setUnselectedAnchorFile', null);
}
/**
* Download the current file.
*/
public download(): void {
try {
this.$store.dispatch('files/download', this.file);
this.$notify.warning('Do not share download link with other people. If you want to share this data better use "Share" option.');
} catch (error) {
this.$notify.error('Can not download your file');
}
const selectedAnchorFile = store.state.files.selectedAnchorFile;
this.$store.dispatch('files/closeDropdown');
this.deleteConfirmation = false;
if (!selectedAnchorFile) {
store.commit('files/setSelectedAnchorFile', props.file);
return;
}
/**
* Set the data property deleteConfirmation to true, signifying that this user does in fact want the current selected file/folder.
*/
public confirmDeletion(): void {
this.deleteConfirmation = true;
const anchorIdx = files.findIndex(
(file) => file === selectedAnchorFile,
);
const shiftIdx = files.findIndex((file) => file === props.file);
const start = Math.min(anchorIdx, shiftIdx);
const end = Math.max(anchorIdx, shiftIdx) + 1;
store.dispatch(
'files/updateShiftSelectedFiles',
files
.slice(start, end)
.filter(
(file) =>
!store.state.files.selectedFiles.includes(
file,
) && file !== selectedAnchorFile,
),
);
}
/**
* Open the share modal for the current file.
*/
function share(): void {
store.dispatch('files/closeDropdown');
store.commit('files/setObjectPathForModal', this.path + this.file.Key);
store.commit(APP_STATE_MUTATIONS.TOGGLE_SHARE_OBJECT_MODAL_SHOWN);
}
/**
* Toggle the dropdown for the current file/folder.
*/
function toggleDropdown(): void {
(store.state.files.openedDropdown === props.file.Key) ?
store.dispatch('files/closeDropdown')
: store.dispatch('files/openDropdown', props.file.Key);
// remove the dropdown delete confirmation
deleteConfirmation.value = false;
}
/**
* Download the current file.
*/
function download(): void {
try {
store.dispatch('files/download', props.file);
notify.warning('Do not share download link with other people. If you want to share this data better use "Share" option.');
} catch (error) {
notify.error('Can not download your file');
}
/**
* Delete the selected file/folder.
*/
public async finalDelete(): Promise<void> {
this.$store.dispatch('files/closeDropdown');
this.$store.dispatch('files/addFileToBeDeleted', this.file);
store.dispatch('files/closeDropdown');
deleteConfirmation.value = false;
}
const params = {
path: this.path,
file: this.file,
};
/**
* Set the data property deleteConfirmation to true, signifying that this user does in fact want the current selected file/folder.
*/
function confirmDeletion(): void {
deleteConfirmation.value = true;
}
if (this.file.type === 'file') {
await this.$store.dispatch('files/delete', params);
} else {
this.$store.dispatch('files/deleteFolder', params);
}
/**
* Delete the selected file/folder.
*/
async function finalDelete(): Promise<void> {
store.dispatch('files/closeDropdown');
store.dispatch('files/addFileToBeDeleted', props.file);
// refresh the files displayed
await this.$store.dispatch('files/list');
this.$store.dispatch('files/removeFileFromToBeDeleted', this.file);
this.deleteConfirmation = false;
}
const params = { ...props };
/**
* Abort the deletion of the current file/folder.
*/
public cancelDeletion(): void {
this.$store.dispatch('files/closeDropdown');
this.deleteConfirmation = false;
}
(props.file.type === 'file') ? await store.dispatch('files/delete', params) : store.dispatch('files/deleteFolder', params);
// refresh the files displayed
await store.dispatch('files/list');
store.dispatch('files/removeFileFromToBeDeleted', props.file);
deleteConfirmation.value = false;
}
/**
* Abort the deletion of the current file/folder.
*/
function cancelDeletion(): void {
store.dispatch('files/closeDropdown');
deleteConfirmation.value = false;
}
</script>

View File

@ -26,35 +26,19 @@
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
<script setup lang="ts">
import { OnPageClickCallback } from '@/types/pagination';
import TablePagination from '@/components/common/TablePagination.vue';
// @vue/component
@Component({
components: {
TablePagination,
},
})
export default class VTable extends Vue {
@Prop({ default: false })
public readonly selectable: boolean;
@Prop({ default: 0 })
private readonly totalPageCount: number;
@Prop({ default: 'items' })
private readonly itemsLabel: string;
@Prop({ default: 0 })
private readonly limit: number;
@Prop({ default: () => [] })
private readonly items: object[];
@Prop({ default: 0 })
private readonly totalItemsCount: number;
@Prop({ default: () => () => new Promise(() => false) })
private readonly onPageClickCallback: OnPageClickCallback;
}
const props = defineProps({
selectable: { type: Boolean, default: false },
totalPageCount: { type: Number, default: 0 },
itemsLabel: { type: String, default: 'items' },
limit: { type: Number, default: 0 },
totalItemsCount: { type: Number, default: 0 },
onPageClickCallback: { type: Function as OnPageClickCallback, default: () => () => new Promise(() => false) },
});
</script>
<style lang="scss">

View File

@ -21,66 +21,62 @@
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
<script setup lang="ts">
import { computed, ref } from 'vue';
import { RouteConfig } from '@/router';
import { APP_STATE_MUTATIONS } from '@/store/mutationConstants';
import { useRoute, useRouter, useStore } from '@/utils/hooks';
import ArrowDownIcon from '@/../static/images/objects/arrowDown.svg';
import DetailsIcon from '@/../static/images/objects/details.svg';
import ShareIcon from '@/../static/images/objects/share.svg';
import GearIcon from '@/../static/images/common/gearIcon.svg';
// @vue/component
@Component({
components: {
ArrowDownIcon,
GearIcon,
DetailsIcon,
ShareIcon,
},
})
export default class BucketSettingsNav extends Vue {
@Prop({ default: '' })
public readonly bucketName: string;
const router = useRouter();
const route = useRoute();
const store = useStore();
public isDropdownOpen = false;
const props = defineProps({
bucketName: { type: String, default: '' },
});
public closeDropdown(): void {
if (!this.isDropdownOpen) return;
const isDropdownOpen = ref(false);
this.isDropdownOpen = false;
}
/**
* Returns files amount from store.
*/
const filesCount: number = computed((): number => {
return store.getters['files/sortedFiles'].length;
});
/**
* Redirects to bucket details page.
*/
public onDetailsClick(): void {
this.$router.push({
name: RouteConfig.BucketsDetails.name,
params: {
bucketName: this.bucketName,
backRoute: this.$route.name ? this.$route.name : '',
},
});
this.isDropdownOpen = false;
}
function closeDropdown(): void {
if (!isDropdownOpen) return;
/**
* Toggles share bucket modal.
*/
public onShareBucketClick(): void {
this.$store.commit(APP_STATE_MUTATIONS.TOGGLE_SHARE_BUCKET_MODAL_SHOWN);
this.isDropdownOpen = false;
}
isDropdownOpen.value = false;
}
/**
* Returns files amount from store.
*/
public get filesCount(): number {
return this.$store.getters['files/sortedFiles'].length;
}
/**
* Redirects to bucket details page.
*/
function onDetailsClick(): void {
router.push({
name: RouteConfig.BucketsDetails.name,
params: {
bucketName: props.bucketName,
backRoute: route.name || '',
},
});
isDropdownOpen.value = false;
}
/**
* Toggles share bucket modal.
*/
function onShareBucketClick(): void {
store.commit(APP_STATE_MUTATIONS.TOGGLE_SHARE_BUCKET_MODAL_SHOWN);
isDropdownOpen.value = false;
}
</script>

View File

@ -35,7 +35,6 @@
class="buckets-view__list"
:limit="bucketsPage.limit"
:total-page-count="bucketsPage.pageCount"
:items="bucketsPage.buckets"
items-label="buckets"
:on-page-click-callback="fetchBuckets"
:total-items-count="bucketsPage.totalCount"

View File

@ -24,7 +24,6 @@
class="projects-list-items"
:limit="projectsPage.limit"
:total-page-count="projectsPage.pageCount"
:items="projectsPage.projects"
items-label="projects"
:on-page-click-callback="onPageClick"
:total-items-count="projectsPage.totalCount"

View File

@ -24,7 +24,6 @@
:selectable="true"
:limit="projectMemberLimit"
:total-page-count="totalPageCount"
:items="projectMembers"
:total-items-count="projectMembersTotalCount"
:on-page-click-callback="onPageClick"
>

View File

@ -0,0 +1,21 @@
// Copyright (C) 2022 Storj Labs, Inc.
// See LICENSE for copying information.
import { getCurrentInstance } from 'vue';
// TODO: remove after updating router and store deps.
export function useRoute() {
return getCurrentInstance()?.proxy.$route;
}
export function useRouter() {
return getCurrentInstance()?.proxy.$router;
}
export function useStore() {
return getCurrentInstance()?.proxy.$store;
}
export function useNotify() {
return getCurrentInstance()?.proxy.$notify;
}

View File

@ -21,7 +21,7 @@ exports[`ProjectMembersArea.vue renders correctly 1`] = `
</div>
<!---->
<!---->
<v-table-stub selectable="true" totalpagecount="1" itemslabel="project members" limit="6" items="[object Object]" totalitemscount="1" onpageclickcallback="[Function]" class="team-area__table"></v-table-stub>
<v-table-stub items-label="project members" selectable="true" limit="6" total-page-count="1" total-items-count="1" on-page-click-callback="function () { [native code] }" class="team-area__table"></v-table-stub>
</div>
`;
@ -32,6 +32,6 @@ exports[`ProjectMembersArea.vue team area renders correctly 1`] = `
</div>
<!---->
<!---->
<v-table-stub selectable="true" totalpagecount="1" itemslabel="project members" limit="6" items="[object Object]" totalitemscount="1" onpageclickcallback="[Function]" class="team-area__table"></v-table-stub>
<v-table-stub items-label="project members" selectable="true" limit="6" total-page-count="1" total-items-count="1" on-page-click-callback="function () { [native code] }" class="team-area__table"></v-table-stub>
</div>
`;