@brftech/filex-core 0.40.0 → 0.41.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -25
- package/dist/ArchiveViewer-DxxjhYZD.js +124 -0
- package/dist/ArchiveViewer-DxxjhYZD.js.map +1 -0
- package/dist/CsvViewer-CyuwENI_.js +141 -0
- package/dist/CsvViewer-CyuwENI_.js.map +1 -0
- package/dist/{DrawioViewer-BNALOB04.js → DrawioViewer-BIYsTX97.js} +48 -42
- package/dist/DrawioViewer-BIYsTX97.js.map +1 -0
- package/dist/EpubViewer-K9PjCMmS.js +163 -0
- package/dist/EpubViewer-K9PjCMmS.js.map +1 -0
- package/dist/IpynbViewer-DYU6Zgz2.js +184 -0
- package/dist/IpynbViewer-DYU6Zgz2.js.map +1 -0
- package/dist/MermaidViewer-Bp73aId8.js +137 -0
- package/dist/MermaidViewer-Bp73aId8.js.map +1 -0
- package/dist/PsdViewer-C2eLivw1.js +122 -0
- package/dist/PsdViewer-C2eLivw1.js.map +1 -0
- package/dist/TiffViewer-DsHg5gcz.js +142 -0
- package/dist/TiffViewer-DsHg5gcz.js.map +1 -0
- package/dist/Viewer3D-C_dc2HN9.js +75 -0
- package/dist/Viewer3D-C_dc2HN9.js.map +1 -0
- package/dist/filex-core.js +218 -20361
- package/dist/filex-core.js.map +1 -1
- package/dist/filex-core.umd.cjs +61 -62
- package/dist/filex-core.umd.cjs.map +1 -1
- package/dist/index-vxMZpYb-.js +26700 -0
- package/dist/index-vxMZpYb-.js.map +1 -0
- package/dist/index.d.ts +4212 -862
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/FileExplorer.vue +2154 -506
- package/src/components/AdvancedSearch.vue +870 -0
- package/src/components/Breadcrumb.vue +191 -19
- package/src/components/CommandPalette.vue +66 -22
- package/src/components/ConnectionsPanel.vue +6 -2
- package/src/components/ContextMenu.vue +39 -2
- package/src/components/EncryptedFolderModal.vue +31 -25
- package/src/components/FilePane.vue +951 -0
- package/src/components/FilterBar.vue +568 -72
- package/src/components/GalleryView.vue +172 -56
- package/src/components/GridView.vue +331 -82
- package/src/components/HomeView.vue +331 -0
- package/src/components/InspectorPanel.vue +296 -53
- package/src/components/ListView.vue +1173 -194
- package/src/components/NFSExportsPanel.vue +7 -4
- package/src/components/OnboardingTour.vue +156 -33
- package/src/components/QuickLook.vue +26 -1
- package/src/components/RecentlyOpened.vue +17 -2
- package/src/components/S3KeysPanel.vue +7 -4
- package/src/components/SSHKeysPanel.vue +7 -4
- package/src/components/ShortcutSettings.vue +6 -2
- package/src/components/ShortcutsHelp.vue +4 -1
- package/src/components/SideNav.vue +206 -140
- package/src/components/StorageFields.vue +19 -1
- package/src/components/TabBar.vue +87 -1
- package/src/components/TagPicker.vue +77 -6
- package/src/components/ThemeGallery.vue +13 -198
- package/src/components/ThemePalette.vue +244 -0
- package/src/components/TimeZoneDialog.vue +135 -0
- package/src/components/TimeZonePicker.vue +696 -0
- package/src/components/TokensPanel.vue +35 -14
- package/src/components/Toolbar.vue +953 -465
- package/src/components/ViewSwitcher.vue +9 -4
- package/src/composables/useConnections.ts +35 -4
- package/src/composables/useExplorerTimeZone.ts +89 -0
- package/src/composables/useFileApi.ts +53 -4
- package/src/composables/useLocale.ts +382 -26
- package/src/composables/useRowTouch.ts +95 -0
- package/src/index.ts +177 -3
- package/src/lib/actionIcons.ts +330 -0
- package/src/lib/advSearch.ts +162 -0
- package/src/lib/dateGroups.ts +197 -0
- package/src/lib/destinationTree.ts +235 -0
- package/src/lib/downloadSelection.ts +154 -0
- package/src/lib/fileFilters.ts +319 -12
- package/src/lib/fileIcons.ts +299 -5
- package/src/lib/filePreview.ts +605 -0
- package/src/lib/listing.ts +72 -0
- package/src/lib/shareTtl.ts +31 -2
- package/src/lib/sortOrder.ts +459 -0
- package/src/lib/themes.ts +151 -116
- package/src/lib/timezone.ts +501 -0
- package/src/lib/uiProfile.ts +82 -0
- package/src/lib/viewPrefs.ts +1036 -0
- package/src/locales/en.ts +416 -26
- package/src/locales/tr.ts +409 -26
- package/src/modals/ConvertModal.vue +23 -3
- package/src/modals/DestinationPickerModal.vue +341 -0
- package/src/modals/Modal.vue +18 -4
- package/src/modals/NewDocumentModal.vue +525 -0
- package/src/modals/NewFolderModal.vue +15 -9
- package/src/modals/PermissionsModal.vue +557 -476
- package/src/modals/PreviewModal.vue +516 -156
- package/src/styles/base.css +5409 -8
- package/src/styles/variables.css +139 -65
- package/src/types/Connections.ts +9 -0
- package/src/types/ExplorerConfig.ts +144 -39
- package/src/types/FileNode.ts +28 -0
- package/src/viewers/ArchiveViewer.vue +46 -8
- package/src/viewers/CsvViewer.vue +19 -2
- package/src/viewers/DrawioViewer.vue +12 -1
- package/src/viewers/EpubViewer.vue +50 -6
- package/src/viewers/IpynbViewer.vue +19 -2
- package/src/viewers/MermaidViewer.vue +20 -3
- package/src/viewers/PdfViewer.vue +24 -4
- package/src/viewers/PsdViewer.vue +28 -4
- package/src/viewers/TiffViewer.vue +19 -2
- package/src/viewers/Viewer3D.vue +36 -2
- package/dist/ArchiveViewer-Cdfv9LFC.js +0 -99
- package/dist/ArchiveViewer-Cdfv9LFC.js.map +0 -1
- package/dist/CsvViewer-CqWeV8VO.js +0 -131
- package/dist/CsvViewer-CqWeV8VO.js.map +0 -1
- package/dist/DrawioViewer-BNALOB04.js.map +0 -1
- package/dist/EpubViewer-BPmlImG7.js +0 -145
- package/dist/EpubViewer-BPmlImG7.js.map +0 -1
- package/dist/IpynbViewer-CDlQmuCV.js +0 -175
- package/dist/IpynbViewer-CDlQmuCV.js.map +0 -1
- package/dist/MermaidViewer-sfZWdR-d.js +0 -127
- package/dist/MermaidViewer-sfZWdR-d.js.map +0 -1
- package/dist/PsdViewer-DwuySZsb.js +0 -112
- package/dist/PsdViewer-DwuySZsb.js.map +0 -1
- package/dist/TiffViewer-BDQ3d3CQ.js +0 -132
- package/dist/TiffViewer-BDQ3d3CQ.js.map +0 -1
- package/dist/Viewer3D-CJJJgkuv.js +0 -60
- package/dist/Viewer3D-CJJJgkuv.js.map +0 -1
- package/src/components/SecondaryPane.vue +0 -447
- package/src/modals/ShareModal.vue +0 -139
|
@@ -8,14 +8,27 @@
|
|
|
8
8
|
* square tiles: thumbnail (or SVG file-type icon fallback) with the name
|
|
9
9
|
* below and size+date revealed on hover/focus. GridView itself is untouched.
|
|
10
10
|
*/
|
|
11
|
-
import { ref } from 'vue';
|
|
11
|
+
import { computed, onBeforeUnmount, ref } from 'vue';
|
|
12
12
|
import { hasInternalDrag } from '../lib/dragOut';
|
|
13
13
|
import type { FileNode } from '../types/FileNode';
|
|
14
14
|
import type { LocaleCode } from '../types/ExplorerConfig';
|
|
15
15
|
import { useLocale } from '../composables/useLocale';
|
|
16
|
-
import {
|
|
16
|
+
import { useRowTouch } from '../composables/useRowTouch';
|
|
17
|
+
import { encryptedFolderTile, fileIconTile, isEncryptedFolder } from '../lib/fileIcons';
|
|
18
|
+
import {
|
|
19
|
+
createFilePreviews,
|
|
20
|
+
drawsAsPage,
|
|
21
|
+
drawsAsVideo,
|
|
22
|
+
type FilePreview,
|
|
23
|
+
} from '../lib/filePreview'; /* gorunum:v1-preview */
|
|
17
24
|
import StarButton from './StarButton.vue';
|
|
18
25
|
import { applyDragGhost } from '../lib/dragGhost';
|
|
26
|
+
import { parentDirOf } from '../lib/listing'; /* the folder a row sits in — one split rule for every view */
|
|
27
|
+
import {
|
|
28
|
+
groupByDate,
|
|
29
|
+
groupingActive,
|
|
30
|
+
} from '../lib/dateGroups'; /* gruplama */
|
|
31
|
+
import { useSortStore, type ListingOrder } from '../lib/sortOrder'; /* gruplama */
|
|
19
32
|
|
|
20
33
|
const props = defineProps<{
|
|
21
34
|
files: FileNode[];
|
|
@@ -50,10 +63,21 @@ const props = defineProps<{
|
|
|
50
63
|
apiBase?: string;
|
|
51
64
|
authHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
|
|
52
65
|
authCredentials?: RequestCredentials;
|
|
66
|
+
/** gorunum:v1-preview — inside an E2E-encrypted folder every body is
|
|
67
|
+
* ciphertext, so the text preview stays off. Same prop, same meaning and
|
|
68
|
+
* same default as GridView's. */
|
|
69
|
+
e2eActive?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* gruplama — where these rows got their order, the same prop the list and
|
|
72
|
+
* the grid take (`lib/sortOrder.ListingOrder`). Read for one thing only: a
|
|
73
|
+
* date heading over the server's RANKED answer would be a claim about an
|
|
74
|
+
* order the tiles are not in. Omitted = an ordinary folder listing.
|
|
75
|
+
*/
|
|
76
|
+
order?: ListingOrder;
|
|
53
77
|
}>();
|
|
54
78
|
|
|
55
79
|
const emit = defineEmits<{
|
|
56
|
-
(e: 'click-card', node: FileNode, mod: { ctrl: boolean; shift: boolean }): void;
|
|
80
|
+
(e: 'click-card', node: FileNode, mod: { ctrl: boolean; shift: boolean; touch?: boolean }): void;
|
|
57
81
|
(e: 'dbl-card', node: FileNode): void;
|
|
58
82
|
(e: 'context-card', node: FileNode, ev: MouseEvent): void;
|
|
59
83
|
(e: 'item-drag-start', node: FileNode, ev: DragEvent): void;
|
|
@@ -61,12 +85,68 @@ const emit = defineEmits<{
|
|
|
61
85
|
(e: 'star-change', node: FileNode, value: boolean): void;
|
|
62
86
|
}>();
|
|
63
87
|
|
|
64
|
-
const { t, formatSize, nodeDisplayName
|
|
88
|
+
const { t, formatSize, nodeDisplayName, formatDate, formatMonthYear, zonedYearMonth, toDate } =
|
|
89
|
+
useLocale(() => props.locale);
|
|
90
|
+
|
|
91
|
+
/* gruplama — this pane's sort, by injection, so the gallery cannot disagree
|
|
92
|
+
* with the list beside it about whether a date heading is honest right now. */
|
|
93
|
+
const sort = useSortStore();
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* gruplama — THE DATE HEADINGS, from the one module that owns the ladder
|
|
97
|
+
* (`lib/dateGroups`), read identically by ListView and GridView.
|
|
98
|
+
*
|
|
99
|
+
* ⚠ The gallery has no Folders / Files sections to collide with, so nothing
|
|
100
|
+
* here replaces anything: folders keep a run of their own at the top — with no
|
|
101
|
+
* heading, because the gallery has never named one — and each day of files
|
|
102
|
+
* gets its label. Any sort key but `modified` draws nothing at all, exactly as
|
|
103
|
+
* before this existed.
|
|
104
|
+
*
|
|
105
|
+
* ⚠ The tiles are handed to us ALREADY sorted (`FilePane.displayFiles` runs
|
|
106
|
+
* the pane's comparator, folders first, then the key), which is why this
|
|
107
|
+
* groups `props.files` in place rather than re-sorting: a second sort here is
|
|
108
|
+
* a second opinion about the order, and the grid learned that lesson once.
|
|
109
|
+
*/
|
|
110
|
+
const grouped = computed(() =>
|
|
111
|
+
groupByDate(props.files, {
|
|
112
|
+
active: groupingActive(sort.key.value, props.order),
|
|
113
|
+
dateOf: (n) => toDate(n.last_modified),
|
|
114
|
+
aside: (n) => (n.type === 'dir' ? { id: 'dirs' } : null),
|
|
115
|
+
labels: { t, formatMonthYear, zonedYearMonth },
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
65
118
|
|
|
66
119
|
function thumbOf(n: FileNode): string | null {
|
|
67
120
|
return props.thumbSrc ? props.thumbSrc(n) : (n.thumb_url ?? null);
|
|
68
121
|
}
|
|
69
122
|
|
|
123
|
+
/* gorunum:v1-preview — the SAME preview the grid card draws, from the same
|
|
124
|
+
* module and with the same bounds. Not a gallery variant: a text file in a
|
|
125
|
+
* gallery is the identical hole (the backend's generic extension-card), and
|
|
126
|
+
* "open it on the desktop, leave the embeds on the old behaviour" is how one
|
|
127
|
+
* product becomes two. Only the type scale differs, and that is CSS. */
|
|
128
|
+
const previews = createFilePreviews({
|
|
129
|
+
apiBase: props.apiBase,
|
|
130
|
+
authHeaders: props.authHeaders ? () => props.authHeaders!() : undefined,
|
|
131
|
+
credentials: props.authCredentials,
|
|
132
|
+
disabled: () => props.e2eActive === true,
|
|
133
|
+
});
|
|
134
|
+
onBeforeUnmount(() => previews.dispose());
|
|
135
|
+
|
|
136
|
+
function previewKind(n: FileNode) {
|
|
137
|
+
return previews.kindFor(n);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** At most one element — a typed local for the template, no `!` per loop. */
|
|
141
|
+
function previewFor(n: FileNode): FilePreview[] {
|
|
142
|
+
const p = previews.get(n);
|
|
143
|
+
return p ? [p] : [];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function bindPreview(el: unknown, n: FileNode) {
|
|
147
|
+
previews.bind(el instanceof Element ? el : null, n);
|
|
148
|
+
}
|
|
149
|
+
|
|
70
150
|
/** A card carries a star when the host wired the API and the node is a file
|
|
71
151
|
* with a server id — the same rule the list row uses. */
|
|
72
152
|
function canStar(n: FileNode): boolean {
|
|
@@ -79,7 +159,7 @@ function isSelected(n: FileNode): boolean {
|
|
|
79
159
|
}
|
|
80
160
|
|
|
81
161
|
function onClick(n: FileNode, ev: MouseEvent) {
|
|
82
|
-
emit('click-card', n, { ctrl: ev.ctrlKey || ev.metaKey, shift: ev.shiftKey });
|
|
162
|
+
emit('click-card', n, { ctrl: ev.ctrlKey || ev.metaKey, shift: ev.shiftKey, touch: touch.isTap(ev) });
|
|
83
163
|
}
|
|
84
164
|
|
|
85
165
|
function onDbl(n: FileNode) {
|
|
@@ -126,47 +206,20 @@ function onItemDrop(n: FileNode, ev: DragEvent) {
|
|
|
126
206
|
emit('item-drop-into', n, ev);
|
|
127
207
|
}
|
|
128
208
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
pressTarget = n;
|
|
134
|
-
if (pressTimer) clearTimeout(pressTimer);
|
|
135
|
-
pressTimer = setTimeout(() => {
|
|
136
|
-
if (pressTarget) {
|
|
137
|
-
const t0 = ev.touches[0];
|
|
138
|
-
emit('context-card', pressTarget, {
|
|
139
|
-
clientX: t0.clientX,
|
|
140
|
-
clientY: t0.clientY,
|
|
141
|
-
preventDefault: () => {},
|
|
142
|
-
} as unknown as MouseEvent);
|
|
143
|
-
}
|
|
144
|
-
}, 500);
|
|
145
|
-
}
|
|
146
|
-
function cancelPress() {
|
|
147
|
-
if (pressTimer) clearTimeout(pressTimer);
|
|
148
|
-
pressTarget = null;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function parentDir(path: string): string {
|
|
152
|
-
const stripped = path.replace(/^[a-z][a-z0-9+.-]*:\/\//i, '');
|
|
153
|
-
const idx = stripped.lastIndexOf('/');
|
|
154
|
-
if (idx === -1) return '';
|
|
155
|
-
return stripped.slice(0, idx);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Special rows keep their emoji (trash/storage are not file-TYPE icons).
|
|
159
|
-
function specialEmojiFor(n: FileNode): string | null {
|
|
160
|
-
if (n.basename === '.trash') return '🗑';
|
|
161
|
-
if (n.mime_type === 'inode/storage') return '💾';
|
|
162
|
-
if (n.type === 'dir' && n.e2e === true) return '🔒'; /* wiring:e2 — encrypted-folder badge */
|
|
163
|
-
return null;
|
|
164
|
-
}
|
|
209
|
+
/* Long press → the card's menu; a tap is reported as a tap (issue #26). */
|
|
210
|
+
const touch = useRowTouch<FileNode>((n, at) =>
|
|
211
|
+
emit('context-card', n, { ...at, preventDefault: () => {}, stopPropagation: () => {} } as unknown as MouseEvent),
|
|
212
|
+
);
|
|
165
213
|
|
|
214
|
+
/**
|
|
215
|
+
* zaman:z1 — routed through useLocale.formatDate, the package's one date
|
|
216
|
+
* formatter. It was a bare `toLocaleString()`, i.e. the BROWSER's locale and
|
|
217
|
+
* the BROWSER's zone: the same file's timestamp came out in a different
|
|
218
|
+
* language here than in the list two clicks away, and in a different clock
|
|
219
|
+
* than the viewer had chosen. Neither of those is a gallery decision.
|
|
220
|
+
*/
|
|
166
221
|
function displayDate(ms: number | undefined): string {
|
|
167
|
-
|
|
168
|
-
const d = new Date(ms * (ms < 1e12 ? 1000 : 1));
|
|
169
|
-
return d.toLocaleString();
|
|
222
|
+
return formatDate(ms, { time: true });
|
|
170
223
|
}
|
|
171
224
|
|
|
172
225
|
// Hover meta line: size for files, entry count (when known) for dirs.
|
|
@@ -186,9 +239,13 @@ function metaFor(n: FileNode): string {
|
|
|
186
239
|
:aria-label="t('gallery.aria')"
|
|
187
240
|
:aria-busy="loading ? 'true' : undefined"
|
|
188
241
|
>
|
|
242
|
+
<template v-for="n in files" :key="n.path">
|
|
243
|
+
<!-- gruplama — the date label: a grid item spanning every column, so the
|
|
244
|
+
tiles keep flowing under it. The cards keep `role="option"`; the
|
|
245
|
+
heading is aria-hidden and the day it names is already in each
|
|
246
|
+
card's own hover text. -->
|
|
247
|
+
<p v-if="grouped.headingBefore(n)" class="fe-gal__heading" aria-hidden="true">{{ grouped.headingBefore(n) }}</p>
|
|
189
248
|
<div
|
|
190
|
-
v-for="n in files"
|
|
191
|
-
:key="n.path"
|
|
192
249
|
class="fe-gal__card"
|
|
193
250
|
:class="{
|
|
194
251
|
'is-selected': isSelected(n),
|
|
@@ -210,27 +267,85 @@ function metaFor(n: FileNode): string {
|
|
|
210
267
|
@dragover="onItemDragOver(n, $event)"
|
|
211
268
|
@dragleave="onItemDragLeave(n)"
|
|
212
269
|
@drop="onItemDrop(n, $event)"
|
|
213
|
-
@touchstart.passive="onTouchStart(n, $event)"
|
|
214
|
-
@touchend="
|
|
215
|
-
@touchmove="
|
|
270
|
+
@touchstart.passive="touch.onTouchStart(n, $event)"
|
|
271
|
+
@touchend.passive="touch.onTouchEnd"
|
|
272
|
+
@touchmove.passive="touch.onTouchMove"
|
|
216
273
|
>
|
|
217
|
-
<div class="fe-gal__thumb">
|
|
274
|
+
<div class="fe-gal__thumb" :ref="(el) => bindPreview(el, n)">
|
|
275
|
+
<!-- gorunum:v1-preview — a text-readable file shows its own first
|
|
276
|
+
lines; it never reaches the <img> branch, so no generic
|
|
277
|
+
placeholder thumbnail is fetched for it either. -->
|
|
278
|
+
<template v-if="previewKind(n)">
|
|
279
|
+
<!-- ⚠ Interpolated, never v-html: this is somebody's file content. -->
|
|
280
|
+
<div
|
|
281
|
+
v-for="p in previewFor(n)"
|
|
282
|
+
:key="'fprev'"
|
|
283
|
+
class="fe-fprev"
|
|
284
|
+
:class="'fe-fprev--' + p.kind"
|
|
285
|
+
:style="p.kind === 'table' ? { '--fprev-cols': String(p.cols) } : undefined"
|
|
286
|
+
aria-hidden="true"
|
|
287
|
+
>
|
|
288
|
+
<!-- One grid, cells emitted flat: <template> makes no DOM, so a
|
|
289
|
+
short row's padding cells keep every column aligned. -->
|
|
290
|
+
<template v-if="p.kind === 'table'">
|
|
291
|
+
<template v-for="(row, ri) in p.rows" :key="ri">
|
|
292
|
+
<span
|
|
293
|
+
v-for="(cell, ci) in row"
|
|
294
|
+
:key="ci"
|
|
295
|
+
class="fe-fprev__cell"
|
|
296
|
+
:class="{ 'is-head': ri === 0 }"
|
|
297
|
+
>{{ cell }}</span>
|
|
298
|
+
</template>
|
|
299
|
+
</template>
|
|
300
|
+
<template v-else>
|
|
301
|
+
<div v-for="(ln, li) in p.lines" :key="li" class="fe-fprev__line"><span
|
|
302
|
+
v-if="ln.tint"
|
|
303
|
+
class="fe-fprev__tint"
|
|
304
|
+
>{{ ln.indent }}{{ ln.tint }}</span>{{ ln.text }}</div>
|
|
305
|
+
</template>
|
|
306
|
+
</div>
|
|
307
|
+
<!-- eslint-disable-next-line vue/no-v-html — static markup from lib/fileIcons -->
|
|
308
|
+
<span
|
|
309
|
+
v-if="previewFor(n).length === 0"
|
|
310
|
+
class="fe-gal__icon fe-gal__icon--svg"
|
|
311
|
+
v-html="fileIconTile(n)"
|
|
312
|
+
></span>
|
|
313
|
+
</template>
|
|
218
314
|
<!-- draggable="false" for the same reason as GridView: HTML5 image
|
|
219
315
|
drag adds a 'Files' MIME to dataTransfer, which would trip the
|
|
220
316
|
parent's upload handler on internal drags. -->
|
|
221
317
|
<img
|
|
222
|
-
v-if="thumbOf(n)"
|
|
318
|
+
v-else-if="thumbOf(n)"
|
|
223
319
|
:src="thumbOf(n)!"
|
|
224
320
|
:alt="n.basename"
|
|
321
|
+
:class="{ 'fe-thumb--page': drawsAsPage(n) /* gorunum:v1-preview — crop a page from its TOP */ }"
|
|
225
322
|
loading="lazy"
|
|
226
323
|
draggable="false"
|
|
227
324
|
/>
|
|
228
|
-
|
|
325
|
+
<!-- ikon:emoji — see GridView: the folder shape survives, the padlock
|
|
326
|
+
is cut out of it, and the name the 🔒 never had is on the span. -->
|
|
327
|
+
<!-- eslint-disable-next-line vue/no-v-html — static markup from lib/fileIcons -->
|
|
328
|
+
<span
|
|
329
|
+
v-else-if="isEncryptedFolder(n)"
|
|
330
|
+
class="fe-gal__icon fe-gal__icon--svg"
|
|
331
|
+
role="img"
|
|
332
|
+
:aria-label="t('e2e.badge')"
|
|
333
|
+
v-html="encryptedFolderTile()"
|
|
334
|
+
></span>
|
|
229
335
|
<!-- eslint-disable-next-line vue/no-v-html — static markup from lib/fileIcons -->
|
|
230
|
-
<span v-else class="fe-gal__icon fe-gal__icon--svg" v-html="
|
|
336
|
+
<span v-else class="fe-gal__icon fe-gal__icon--svg" v-html="fileIconTile(n)"></span>
|
|
231
337
|
<!-- Same star chip as the grid, same component, same rule: painted
|
|
232
338
|
when starred, on hover/focus otherwise. It sits above .fe-gal__meta
|
|
233
339
|
(which is aria-hidden and covers the tile's foot on hover). -->
|
|
340
|
+
<!-- gorunum:v1-preview — a frame lifted out of a video is, on a card,
|
|
341
|
+
indistinguishable from a photograph. The badge is the difference,
|
|
342
|
+
and it is drawn only over a real frame: a video that fell back to
|
|
343
|
+
its type tile already says what it is. -->
|
|
344
|
+
<span
|
|
345
|
+
v-if="!previewKind(n) && thumbOf(n) && drawsAsVideo(n)"
|
|
346
|
+
class="fe-thumb__play"
|
|
347
|
+
aria-hidden="true"
|
|
348
|
+
></span>
|
|
234
349
|
<div v-if="canStar(n)" class="fe-gal__star" @click.stop @dblclick.stop>
|
|
235
350
|
<StarButton
|
|
236
351
|
:starred="!!starredIds?.has(n.id!)"
|
|
@@ -247,16 +362,17 @@ function metaFor(n: FileNode): string {
|
|
|
247
362
|
<div class="fe-gal__meta" aria-hidden="true">
|
|
248
363
|
<span v-if="metaFor(n)" class="fe-gal__meta-line">{{ metaFor(n) }}</span>
|
|
249
364
|
<span
|
|
250
|
-
v-if="showParentPath &&
|
|
365
|
+
v-if="showParentPath && parentDirOf(n.path)"
|
|
251
366
|
class="fe-gal__meta-line fe-gal__meta-line--path"
|
|
252
|
-
:title="
|
|
253
|
-
>{{
|
|
367
|
+
:title="parentDirOf(n.path)"
|
|
368
|
+
>{{ parentDirOf(n.path) }}</span>
|
|
254
369
|
</div>
|
|
255
370
|
</div>
|
|
256
371
|
<div class="fe-gal__label" :title="n.basename">
|
|
257
372
|
{{ nodeDisplayName(n) }}
|
|
258
373
|
</div>
|
|
259
374
|
</div>
|
|
375
|
+
</template>
|
|
260
376
|
<div v-if="!loading && files.length === 0" class="fe-gal__empty">
|
|
261
377
|
{{ t('empty.folder') }}
|
|
262
378
|
</div>
|