Skip to content

Commit a709a84

Browse files
author
fabienSvstr
committed
[MS] File list reponsive
1 parent 335b30b commit a709a84

12 files changed

+248
-103
lines changed

client/src/components/files/FileDropZone.vue

+10-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
:image="DocumentImport"
2424
class="restore-password-header-img"
2525
/>
26-
<ion-label class="subtitles-normal">
26+
<ion-label class="subtitles-sm">
2727
{{ $msTranslate('FoldersPage.ImportFile.dropInstructions') }}
2828
</ion-label>
2929
</div>
@@ -125,6 +125,8 @@ function reset(): void {
125125
</script>
126126

127127
<style scoped lang="scss">
128+
@import '@/theme/responsive-mixin';
129+
128130
.drop-zone {
129131
width: 100%;
130132
height: 100%;
@@ -139,6 +141,11 @@ function reset(): void {
139141
bottom: 1.5rem;
140142
z-index: 1100;
141143

144+
@include breakpoint('sm') {
145+
top: 0.75rem;
146+
bottom: 0.75rem;
147+
}
148+
142149
&.drop-active {
143150
outline: 2px dashed var(--parsec-color-light-primary-400);
144151
border-radius: var(--parsec-radius-8);
@@ -152,7 +159,8 @@ function reset(): void {
152159
bottom: 2em;
153160
transform: translate(-50%, -50%);
154161
width: fit-content;
155-
background-color: var(--parsec-color-light-primary-50);
162+
background-color: var(--parsec-color-light-secondary-premiere);
163+
border: 1px solid var(--parsec-color-light-secondary-medium);
156164
border-radius: var(--parsec-radius-8);
157165
box-shadow: var(--parsec-shadow-strong);
158166
padding: 0.75rem;

client/src/components/files/FileListDisplay.vue

+76-76
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,82 @@
11
<!-- Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS -->
22

33
<template>
4-
<div>
5-
<file-drop-zone
6-
ref="fileDropZoneRef"
7-
:current-path="currentPath"
8-
@files-added="$emit('filesAdded', $event)"
9-
:show-drop-message="true"
10-
:is-reader="ownRole === WorkspaceRole.Reader"
11-
@drop-as-reader="$emit('dropAsReader')"
4+
<file-drop-zone
5+
ref="fileDropZoneRef"
6+
:current-path="currentPath"
7+
@files-added="$emit('filesAdded', $event)"
8+
:show-drop-message="true"
9+
:is-reader="ownRole === WorkspaceRole.Reader"
10+
@drop-as-reader="$emit('dropAsReader')"
11+
>
12+
<div
13+
class="scroll"
14+
@contextmenu="onContextMenu"
1215
>
13-
<div
14-
class="scroll"
15-
@contextmenu="onContextMenu"
16-
>
17-
<ion-list class="list">
18-
<ion-list-header
19-
class="folder-list-header"
20-
lines="full"
21-
>
22-
<ion-label class="folder-list-header__label ion-text-nowrap label-selected">
23-
<ms-checkbox
24-
@change="selectAll"
25-
:checked="allSelected"
26-
:indeterminate="someSelected && !allSelected"
27-
/>
28-
</ion-label>
29-
<ion-label class="folder-list-header__label cell-title ion-text-nowrap label-name">
30-
{{ $msTranslate('FoldersPage.listDisplayTitles.name') }}
31-
</ion-label>
32-
<ion-label
33-
class="folder-list-header__label cell-title ion-text-nowrap label-updatedBy"
34-
v-show="false"
35-
>
36-
{{ $msTranslate('FoldersPage.listDisplayTitles.updatedBy') }}
37-
</ion-label>
38-
<ion-label class="folder-list-header__label cell-title ion-text-nowrap label-lastUpdate">
39-
{{ $msTranslate('FoldersPage.listDisplayTitles.lastUpdate') }}
40-
</ion-label>
41-
<ion-label class="folder-list-header__label cell-title ion-text-nowrap label-size">
42-
{{ $msTranslate('FoldersPage.listDisplayTitles.size') }}
43-
</ion-label>
44-
<ion-label class="folder-list-header__label cell-title ion-text-nowrap label-space" />
45-
</ion-list-header>
46-
<div>
47-
<file-list-item
48-
v-for="folder in folders.getEntries()"
49-
:key="folder.id"
50-
:entry="folder"
51-
:show-checkbox="someSelected"
52-
@click="$emit('click', folder, $event)"
53-
@menu-click="(event, entry, onFinished) => $emit('menuClick', event, entry, onFinished)"
54-
@selected-change="onSelectedChange"
55-
@files-added="onFilesAdded"
56-
:is-workspace-reader="ownRole === WorkspaceRole.Reader"
57-
@drop-as-reader="$emit('dropAsReader')"
58-
/>
59-
<file-list-item
60-
v-for="file in files.getEntries()"
61-
:key="file.id"
62-
:entry="file"
63-
:show-checkbox="someSelected"
64-
@click="$emit('click', file, $event)"
65-
@menu-click="(event, entry, onFinished) => $emit('menuClick', event, entry, onFinished)"
66-
@selected-change="onSelectedChange"
67-
@files-added="onFilesAdded"
68-
@drop-as-reader="$emit('dropAsReader')"
16+
<ion-list class="list">
17+
<ion-list-header
18+
class="folder-list-header"
19+
lines="full"
20+
v-if="isLargeDisplay"
21+
>
22+
<ion-label class="folder-list-header__label ion-text-nowrap label-selected">
23+
<ms-checkbox
24+
@change="selectAll"
25+
:checked="allSelected"
26+
:indeterminate="someSelected && !allSelected"
6927
/>
70-
<file-list-item-processing
71-
v-for="op in operationsInProgress"
72-
:key="op.data.id"
73-
:data="op.data"
74-
:progress="op.progress"
75-
/>
76-
</div>
77-
</ion-list>
78-
</div>
79-
</file-drop-zone>
80-
</div>
28+
</ion-label>
29+
<ion-label class="folder-list-header__label cell-title ion-text-nowrap label-name">
30+
{{ $msTranslate('FoldersPage.listDisplayTitles.name') }}
31+
</ion-label>
32+
<ion-label
33+
class="folder-list-header__label cell-title ion-text-nowrap label-updatedBy"
34+
v-show="false"
35+
>
36+
{{ $msTranslate('FoldersPage.listDisplayTitles.updatedBy') }}
37+
</ion-label>
38+
<ion-label class="folder-list-header__label cell-title ion-text-nowrap label-lastUpdate">
39+
{{ $msTranslate('FoldersPage.listDisplayTitles.lastUpdate') }}
40+
</ion-label>
41+
<ion-label class="folder-list-header__label cell-title ion-text-nowrap label-size">
42+
{{ $msTranslate('FoldersPage.listDisplayTitles.size') }}
43+
</ion-label>
44+
<ion-label class="folder-list-header__label cell-title ion-text-nowrap label-space" />
45+
</ion-list-header>
46+
<div>
47+
<file-list-item
48+
v-for="folder in folders.getEntries()"
49+
:key="folder.id"
50+
:entry="folder"
51+
:show-checkbox="someSelected"
52+
@click="$emit('click', folder, $event)"
53+
@menu-click="(event, entry, onFinished) => $emit('menuClick', event, entry, onFinished)"
54+
@selected-change="onSelectedChange"
55+
@files-added="onFilesAdded"
56+
:is-workspace-reader="ownRole === WorkspaceRole.Reader"
57+
@drop-as-reader="$emit('dropAsReader')"
58+
/>
59+
<file-list-item
60+
v-for="file in files.getEntries()"
61+
:key="file.id"
62+
:entry="file"
63+
:show-checkbox="someSelected"
64+
@click="$emit('click', file, $event)"
65+
@menu-click="(event, entry, onFinished) => $emit('menuClick', event, entry, onFinished)"
66+
@selected-change="onSelectedChange"
67+
@files-added="onFilesAdded"
68+
@drop-as-reader="$emit('dropAsReader')"
69+
/>
70+
<file-list-item-processing
71+
v-for="op in operationsInProgress"
72+
:key="op.data.id"
73+
:data="op.data"
74+
:progress="op.progress"
75+
/>
76+
</div>
77+
</ion-list>
78+
</div>
79+
</file-drop-zone>
8180
</template>
8281

8382
<script setup lang="ts">
@@ -89,8 +88,9 @@ import { FileImportTuple } from '@/components/files/utils';
8988
import { FsPath, WorkspaceRole } from '@/parsec';
9089
import { IonLabel, IonList, IonListHeader } from '@ionic/vue';
9190
import { computed, ref } from 'vue';
92-
import { MsCheckbox } from 'megashark-lib';
91+
import { MsCheckbox, useWindowSize } from 'megashark-lib';
9392

93+
const { isLargeDisplay } = useWindowSize();
9494
const props = defineProps<{
9595
operationsInProgress: Array<FileOperationProgress>;
9696
files: EntryCollection<FileModel>;

client/src/components/files/FileListItem.vue

+71-10
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,23 @@
1111
>
1212
<ion-item
1313
button
14-
lines="full"
14+
:lines="isLargeDisplay ? 'full' : 'none'"
1515
:detail="false"
1616
class="file-list-item"
1717
:class="{
1818
selected: entry.isSelected,
1919
'file-hovered': !entry.isSelected && (menuOpened || isHovered),
20+
'file-list-item-mobile': isSmallDisplay,
2021
}"
2122
@dblclick="$emit('click', $event, entry)"
2223
@mouseenter="isHovered = true"
2324
@mouseleave="isHovered = false"
2425
@contextmenu="onOptionsClick"
2526
>
26-
<div class="file-selected">
27+
<div
28+
class="file-selected"
29+
v-if="isLargeDisplay || entry.isSelected || showCheckbox"
30+
>
2731
<!-- eslint-disable vue/no-mutating-props -->
2832
<ms-checkbox
2933
v-model="entry.isSelected"
@@ -35,14 +39,32 @@
3539
<!-- eslint-enable vue/no-mutating-props -->
3640
</div>
3741
<!-- file name -->
38-
<div class="file-name">
42+
<div
43+
class="file-name"
44+
:class="{ 'file-mobile-content': isSmallDisplay }"
45+
>
3946
<ms-image
4047
:image="entry.isFile() ? getFileIcon(entry.name) : Folder"
4148
class="file-icon"
4249
/>
43-
<ion-label class="file-name__label cell">
44-
{{ entry.name }}
45-
</ion-label>
50+
<div class="file-mobile-text">
51+
<ion-label class="file-name__label cell">
52+
{{ entry.name }}
53+
</ion-label>
54+
<ion-text
55+
v-if="isSmallDisplay"
56+
class="file-mobile-text__data body-sm"
57+
>
58+
<span class="data-date">{{ $msTranslate(formatTimeSince(entry.updated, '--', 'short')) }}</span>
59+
<span v-if="entry.isFile()"> &bull; </span>
60+
<span
61+
class="data-size"
62+
v-if="entry.isFile()"
63+
>
64+
{{ $msTranslate(formatFileSize((entry as FileModel).size)) }}
65+
</span>
66+
</ion-text>
67+
</div>
4668
<ion-icon
4769
class="cloud-overlay"
4870
:class="isFileSynced() ? 'cloud-overlay-ok' : 'cloud-overlay-ko'"
@@ -83,7 +105,7 @@
83105
<div class="file-options ion-item-child-clickable">
84106
<ion-button
85107
fill="clear"
86-
v-show="isHovered || menuOpened"
108+
v-show="isHovered || menuOpened || isSmallDisplay"
87109
class="options-button"
88110
@click.stop="onOptionsClick($event)"
89111
@dblclick.stop
@@ -101,18 +123,19 @@
101123

102124
<script setup lang="ts">
103125
import { formatFileSize, getFileIcon } from '@/common/file';
104-
import { Folder, formatTimeSince, MsImage, MsCheckbox } from 'megashark-lib';
126+
import { Folder, formatTimeSince, MsImage, MsCheckbox, useWindowSize } from 'megashark-lib';
105127
import FileDropZone from '@/components/files/FileDropZone.vue';
106128
import { EntryModel, FileModel } from '@/components/files/types';
107129
import { FileImportTuple } from '@/components/files/utils';
108130
import UserAvatarName from '@/components/users/UserAvatarName.vue';
109131
import { FsPath, Path } from '@/parsec';
110-
import { IonButton, IonIcon, IonItem, IonLabel } from '@ionic/vue';
132+
import { IonButton, IonIcon, IonItem, IonLabel, IonText } from '@ionic/vue';
111133
import { cloudDone, cloudOffline, ellipsisHorizontal } from 'ionicons/icons';
112134
import { Ref, onMounted, ref } from 'vue';
113135

114136
const isHovered = ref(false);
115137
const menuOpened = ref(false);
138+
const { isSmallDisplay, isLargeDisplay } = useWindowSize();
116139

117140
const props = defineProps<{
118141
entry: EntryModel;
@@ -160,19 +183,25 @@ async function onOptionsClick(event: PointerEvent): Promise<void> {
160183
</script>
161184

162185
<style lang="scss" scoped>
186+
@import '@/theme/responsive-mixin';
187+
163188
.drop-zone-item {
164189
height: fit-content;
165190
}
166191

167192
.file-name {
193+
position: relative;
194+
display: flex;
195+
gap: 1rem;
196+
168197
.file-icon {
169198
width: 2rem;
170199
height: 2rem;
200+
flex-shrink: 0;
171201
}
172202

173203
&__label {
174204
color: var(--parsec-color-light-secondary-text);
175-
margin-left: 1em;
176205
overflow: hidden;
177206
text-overflow: ellipsis;
178207
text-wrap: nowrap;
@@ -182,6 +211,12 @@ async function onOptionsClick(event: PointerEvent): Promise<void> {
182211
font-size: 1rem;
183212
flex-shrink: 0;
184213
margin-left: auto;
214+
position: absolute;
215+
right: 0;
216+
217+
@include breakpoint('sm') {
218+
display: none;
219+
}
185220

186221
&-ok {
187222
color: var(--parsec-color-light-primary-500);
@@ -212,4 +247,30 @@ async function onOptionsClick(event: PointerEvent): Promise<void> {
212247
}
213248
}
214249
}
250+
251+
.file-list-item-mobile {
252+
overflow: hidden;
253+
text-overflow: ellipsis;
254+
white-space: nowrap;
255+
256+
.file-mobile-content {
257+
gap: 1rem;
258+
}
259+
}
260+
261+
.file-mobile-text {
262+
flex-grow: 1;
263+
overflow: hidden;
264+
text-overflow: ellipsis;
265+
white-space: nowrap;
266+
color: var(--parsec-color-light-secondary-grey);
267+
display: flex;
268+
gap: 0.125rem;
269+
flex-direction: column;
270+
271+
&__data {
272+
display: flex;
273+
gap: 0.5rem;
274+
}
275+
}
215276
</style>

client/src/components/workspaces/WorkspaceCard.vue

+5-3
Original file line numberDiff line numberDiff line change
@@ -52,17 +52,19 @@
5252
<div
5353
v-show="clientProfile !== UserProfile.Outsider"
5454
class="icon-option-container"
55+
@click.stop="$emit('shareClick', workspace, $event)"
5556
>
5657
<ion-icon
5758
:icon="shareSocial"
58-
@click.stop="$emit('shareClick', workspace, $event)"
5959
class="icon-option"
6060
/>
6161
</div>
62-
<div class="icon-option-container">
62+
<div
63+
class="icon-option-container"
64+
@click.stop="onOptionsClick($event)"
65+
>
6366
<ion-icon
6467
:icon="ellipsisHorizontal"
65-
@click.stop="onOptionsClick($event)"
6668
class="icon-option"
6769
/>
6870
</div>

0 commit comments

Comments
 (0)