mirror of
https://github.com/nextcloud/server.git
synced 2026-10-29 16:15:46 +01:00
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
1 line
346 KiB
Plaintext
1 line
346 KiB
Plaintext
{"version":3,"mappings":"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","names":["element","width","ref","isWide","computed","isMedium","isNarrow","observer","contentBoxSize","updateObserver","el","useFileListWidth","onMounted","readonly","Directory","name","contents","directory","acc","file","entry","renameInvalidDroppedEntries","path","index","isFolder","node","getFilenameValidity","otherNames","other","newNodeName","t","join","logger","uniqueName","getUniqueName","traverseTree","resolve","reject","entries","readDirectory","dirReader","getEntries","results","error","createDirectoryIfNotExists","destination","davUrl","defaultRemoteURL","defaultRootPath","davRoot","davClient","getClient","stat","getDefaultPropfind","emit","resultToNode","resolveConflict","files","conflicts","uploads","conflictResults","openConflictPicker","showInfo","showWarning","dataTransferToFileTree","items","item","warned","fileTree","showError","onDropExternalFiles","root","uploader","getUploader","getConflicts","queue","hasDirectoryErrors","uploadDirectoryContents","relativePath","previousDestination","errors","result","showSuccess","onDropInternalFiles","nodes","isCopy","handleCopyMoveNodesTo","MoveCopyAction","HintException","useDragAndDropStore","defineStore","selection","useSelectionStore","lastSelectedIndex","useUploaderStore","args","_sfc_main$n","defineComponent","NcActionButton","NcBreadcrumbs","NcBreadcrumb","NcIconSvgWrapper","activeStore","useActiveStore","filesStore","useFilesStore","pathsStore","usePathsStore","draggingStore","selectionStore","uploaderStore","views","useViews","isMenuOpen","watch","isSharingEnabled","getCapabilities","isPublic","isPublicShare","canShare","Permission","openSharingSidebar","mdiAccountPlus","mdiChevronDown","mdiChevronUp","mdiReload","getSidebar","cumulativePath","value","dir","source","HomeSvg","basename","view","event","controller","folder","canDrop","section","_createBlock","_component_NcBreadcrumbs","_ctx","_normalizeClass","_withCtx","_renderSlot","_openBlock","_createElementBlock","_Fragment","_renderList","_component_NcBreadcrumb","_mergeProps","_cache","$event","_createSlots","_createVNode","_component_NcIconSvgWrapper","_component_NcActionButton","_createTextVNode","_toDisplayString","_sfc_main$m","TrayArrowDownIcon","debounce","mainContent","lastUpload","upload","UploadStatus","fileid","_hoisted_1","_hoisted_2","_createElementVNode","_component_TrayArrowDownIcon","_hoisted_3","isFileListFilterWithUi","useFiltersStore","chips","filters","activeChips","sortedFilters","a","b","filtersWithUI","addFilter","filter","onFilterUpdateChips","onFilterUpdate","removeFilter","filterId","id","initialize","registry","getFilesRegistry","initialFilters","getFileListFilters","subscribe","onViewChanged","menuTriggerId","filterStore","visualFilters","hasActiveFilters","selectedFilter","boundary","$style","_unref","NcPopover","NcButton","_resolveDynamicComponent","mdiFilterVariant","mdiArrowLeft","_sfc_main$k","ROW_HEIGHT","CELL_MARGIN","CHECKBOX_WIDTH","ACTIONS_MENU_WIDTH","INLINE_ACTION_WIDTH","MIN_NAME_WIDTH","COLUMN_WIDTHS","DROPPABLE_BUILTIN_COLUMNS","KEPT_BUILTIN_COLUMNS","FileListLayoutKey","inlineActionsWidth","reportInlineActionsWidth","resetInlineActionsWidth","useInlineActionsWidth","getFileListLayout","available","layout","cellWidth","columnWidth","builtinColumns","getUsedWidth","total","column","reductions","reduce","useFileListLayout","inject","actionsMixins","record","action","menuAction","_sfc_main$j","ArrowLeftIcon","CustomElementRender","NcActions","NcActionSeparator","NcLoadingIcon","enabledFileActions","NodeStatus","actions","DefaultType","self","topActionsIds","useHotKey","renderActionsWidth","title","executeAction","_component_CustomElementRender","_component_NcActions","_component_NcLoadingIcon","_component_NcActionSeparator","_component_ArrowLeftIcon","useKeyboardStore","keyboardStore","_sfc_main$i","NcCheckboxRadioSwitch","FileType","selected","newSelectedIndex","isAlreadySelected","start","end","lastSelection","filesToSelect","_component_NcCheckboxRadioSwitch","useRenamingStore","renamingNode","isRenaming","rename","oldName","newName","userConfig","useUserConfigStore","oldExtension","extname","newExtension","showFileExtensionDialog","showHiddenFileDialog","oldEncodedSource","axios","fetchNode","dirname","$reset","isAxiosError","spawnDialog","defineAsyncComponent","__vitePreload","filename","n","_sfc_main$h","NcTextField","vOnClickOutside","renamingStore","userConfigStore","activeFolder","activeView","defaultFileAction","accessibleName","renaming","input","validity","sourceName","length","_withDirectives","_component_NcTextField","_withKeys","_hoisted_4","_createCommentVNode","_sfc_main","$props","_sfc_main$f","StarSvg","_sfc_main$e","mdiPlus","_sfc_main$d","AccountGroupIcon","AccountPlusIcon","CollectivesIcon","FavoriteIcon","FileIcon","FolderIcon","FolderOpenIcon","KeyIcon","LinkIcon","NetworkIcon","TagIcon","RecentlyCreatedIcon","props","previewUrl","usePreviewImage","toRef","oneDayAgo","isLivePhoto","PlayCircleIcon","shareTypes","type","ShareType","previewImg","canvas","height","pixels","decode","ctx","imageData","_setBlockTracking","_component_FolderOpenIcon","_component_FolderIcon","_component_FileIcon","_hoisted_5","_component_FavoriteIcon","_hoisted_6","_component_RecentlyCreatedIcon","useActionsMenuStore","getSummaryFor","hidden","fileCount","folderCount","summary","fileSummary","folderSummary","hiddenSummary","_sfc_main$c","FileMultipleIcon","totalSize","size","formatFileSize","preview","_component_FileMultipleIcon","onLoaded","getDragAndDropPreview","mountingPoint","createApp","DragAndDropPreview","FileEntryMixin","Folder","NcFile","Node","hashCode","canDrag","opened","maxOpacityTime","timeDiff","opacityTime","newSource","oldSource","contentRect","isMoreThanOneSelected","metaKeyPressed","isDownloadable","url","generateUrl","sidebarAction","image","cachedContents","_sfc_main$b","FileEntryActions","FileEntryCheckbox","FileEntryName","FileEntryPreview","NcDateTime","actionsMenuStore","currentRouteFileId","useRouteParameters","activeNode","storeToRefs","useFileActions","baseType","ext","_toHandlers","_component_FileEntryCheckbox","_component_FileEntryPreview","_component_FileEntryName","_component_FileEntryActions","_normalizeStyle","_component_NcDateTime","_sfc_main$a","chip","NcChip","NcAvatar","searchStore","useSearchStore","pinia","isVisible","onClick","_sfc_main$7","PQueue","enabled","initialRender","currentFolder","currentView","$options","__props","columns","classForColumn","_sfc_main$5","boundariesElement","inlineActions","childrenActionsIds","menuActions","displayName","selectionSources","failedSources","e","translate","useFilesSorting","viewConfigStore","useViewConfigStore","sortingMode","isAscSorting","toggleSortBy","key","MenuUp","MenuDown","_sfc_main$3","FilesListTableHeaderActions","FilesListTableHeaderButton","label","mode","_component_FilesListTableHeaderActions","_component_FilesListTableHeaderButton","_hoisted_9","_hoisted_8","_sfc_main$2","fileListWidth","firstColumnIndex","oldItemsKeys","unusedKeys","rowsAbove","rowsBelow","columnCount","oldColumnCount","lastVisibleIndex","clampedIndex","scrollTop","scrollPos","topScroll","theadHeight","filterHeight","i","_hoisted_7","headers","sorted","useFileListHeaders","getFileListHeaders","_sfc_main$1","FileListFilterChips","FileListFilterToSearch","FilesListHeader","FilesListTableFooter","FilesListTableHeader","VirtualList","View","sidebar","fileId","openDetails","openFile","isMtimeAvailable","isSizeAvailable","provide","isMimeAvailable","FileEntry","FileEntryGrid","defaultCaption","viewCaption","cantUploadCaption","quotaExceededCaption","sortableCaption","virtualListNote","warn","defaultAction","useEnabledFileActions","tableElement","tableTop","tableBottom","nextIndex","nextNode","query","_component_VirtualList","_component_FileListFilterToSearch","_component_FileListFilterChips","header","_component_FilesListHeader","_component_FilesListTableHeader","_component_FilesListTableFooter","useFileListActions","getFileListActions","useEnabledFileListActions","toValue","useNewFileMenuActions","context","getNewFileMenuEntries","toAction","NewMenuEntryCategory","caption","category","isWebDAVClientError","humanizeWebDAVError","status","BreadCrumbs","DragAndDropNotice","FileListFilters","FilesListVirtual","ListViewIcon","NcAppContent","NcEmptyContent","NcUploadPicker","ViewGridIcon","IconAlertCircleOutline","IconReload","useSidebarStore","filtersStore","enableGridView","loadState","dirContents","enabledFileListActions","newFileMenuActions","stopWatching","nextTick","normalizedPath","normalize","customColumn","sortNodes","aa","relative","bb","useThrottleFn","show","newView","oldView","newDir","oldDir","filesListVirtual","unsubscribe","scope","defaultView","success","_component_NcAppContent","_Teleport","_component_NcUploadPicker","_component_BreadCrumbs","_component_FileListFilters","_component_NcButton","_component_ListViewIcon","_component_ViewGridIcon","_component_DragAndDropNotice","_component_FilesListVirtual","_component_NcEmptyContent","_component_IconReload","_component_IconAlertCircleOutline"],"ignoreList":[],"sources":["../build/frontend/apps/files/src/composables/useFileListWidth.ts","../build/frontend/apps/files/src/services/DropServiceUtils.ts","../build/frontend/apps/files/src/services/DropService.ts","../build/frontend/apps/files/src/store/dragging.ts","../build/frontend/apps/files/src/store/selection.ts","../build/frontend/apps/files/src/store/uploader.ts","../build/frontend/apps/files/src/components/BreadCrumbs.vue","../build/frontend/apps/files/src/components/DragAndDropNotice.vue","../build/frontend/apps/files/src/store/filters.ts","../build/frontend/apps/files/src/components/FileListFilter/FileListFilters.vue","../build/frontend/apps/files/src/components/CustomElementRender.vue","../build/frontend/apps/files/src/composables/useFileListLayout.ts","../build/frontend/apps/files/src/mixins/actionsMixin.ts","../build/frontend/apps/files/src/components/FileEntry/FileEntryActions.vue","../build/frontend/apps/files/src/store/keyboard.ts","../build/frontend/apps/files/src/components/FileEntry/FileEntryCheckbox.vue","../build/frontend/apps/files/src/store/renaming.ts","../build/frontend/apps/files/src/components/FileEntry/FileEntryName.vue","../build/frontend/apps/files/src/components/FileEntry/CollectivesIcon.vue","../build/frontend/apps/files/src/components/FileEntry/FavoriteIcon.vue","../build/frontend/apps/files/src/components/FileEntry/RecentlyCreatedIcon.vue","../build/frontend/apps/files/src/components/FileEntry/FileEntryPreview.vue","../build/frontend/apps/files/src/store/actionsmenu.ts","../build/frontend/apps/files/src/utils/fileUtils.ts","../build/frontend/apps/files/src/components/DragAndDropPreview.vue","../build/frontend/apps/files/src/utils/dragUtils.ts","../build/frontend/apps/files/src/components/FileEntryMixin.ts","../build/frontend/apps/files/src/components/FileEntry.vue","../build/frontend/apps/files/src/components/FileEntryGrid.vue","../build/frontend/apps/files/src/components/FileListFilter/FileListFilterChips.vue","../build/frontend/apps/files/src/components/FileListFilter/FileListFilterToSearch.vue","../build/frontend/apps/files/src/components/FilesListHeader.vue","../build/frontend/apps/files/src/components/FilesListTableFooter.vue","../build/frontend/apps/files/src/components/FilesListTableHeaderActions.vue","../build/frontend/apps/files/src/composables/filesSorting.ts","../build/frontend/apps/files/src/components/FilesListTableHeaderButton.vue","../build/frontend/apps/files/src/components/FilesListTableHeader.vue","../build/frontend/apps/files/src/components/VirtualList.vue","../build/frontend/apps/files/src/composables/useFileListHeaders.ts","../build/frontend/apps/files/src/components/FilesListVirtual.vue","../build/frontend/apps/files/src/composables/useFileListActions.ts","../build/frontend/apps/files/src/composables/useNewFileMenuActions.ts","../build/frontend/apps/files/src/utils/davUtils.ts","../build/frontend/apps/files/src/views/FilesList.vue"],"sourcesContent":["/*!\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport { computed, onMounted, readonly, ref } from 'vue'\n\n/** The element we observe */\nlet element: HTMLElement | undefined\n\n/** The current width of the element */\nconst width = ref(0)\n\nconst isWide = computed(() => width.value >= 1024)\nconst isMedium = computed(() => width.value >= 512 && width.value < 1024)\nconst isNarrow = computed(() => width.value < 512)\n\nconst observer = new ResizeObserver(([element]) => {\n\tif (!element) {\n\t\treturn\n\t}\n\n\tconst contentBoxSize = element.contentBoxSize?.[0]\n\tif (contentBoxSize) {\n\t\t// use the newer `contentBoxSize` property if available\n\t\twidth.value = contentBoxSize.inlineSize\n\t} else {\n\t\t// fall back to `contentRect`\n\t\twidth.value = element.contentRect.width\n\t}\n})\n\n/**\n * Update the observed element if needed and reconfigure the observer\n */\nfunction updateObserver() {\n\tconst el = document.querySelector<HTMLElement>('#app-content-vue') ?? document.body\n\tif (el !== element) {\n\t\t// if already observing: stop observing the old element\n\t\tif (element) {\n\t\t\tobserver.unobserve(element)\n\t\t}\n\t\t// observe the new element if needed\n\t\tobserver.observe(el)\n\t\telement = el\n\t}\n}\n\n/**\n * Get the reactive width of the file list\n */\nexport function useFileListWidth() {\n\t// Update the observer when the component is mounted (e.g. because this is the files app)\n\tonMounted(updateObserver)\n\t// Update the observer also in setup context, so we already have an initial value\n\tupdateObserver()\n\n\treturn {\n\t\twidth: readonly(width),\n\n\t\tisWide,\n\t\tisMedium,\n\t\tisNarrow,\n\t}\n}\n","/*!\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { IFolder, INode } from '@nextcloud/files'\nimport type { FileStat, ResponseDataDetailed } from 'webdav'\n\nimport { showInfo, showWarning } from '@nextcloud/dialogs'\nimport { openConflictPicker } from '@nextcloud/dialogs'\nimport { emit } from '@nextcloud/event-bus'\nimport { getUniqueName } from '@nextcloud/files'\nimport { defaultRemoteURL, defaultRootPath, getClient, getDefaultPropfind, resultToNode } from '@nextcloud/files/dav'\nimport { t } from '@nextcloud/l10n'\nimport { join } from '@nextcloud/paths'\nimport { getFilenameValidity } from '../utils/filenameValidity.ts'\nimport { logger } from '../utils/logger.ts'\nimport { newNodeName } from '../utils/newNodeDialog.ts'\n\n/**\n * This represents a Directory in the file tree\n * We extend the File class to better handling uploading\n * and stay as close as possible as the Filesystem API.\n * This also allow us to hijack the size or lastModified\n * properties to compute them dynamically.\n */\nexport class Directory extends File {\n\t_contents: (Directory | File)[]\n\n\tconstructor(name, contents: (Directory | File)[] = []) {\n\t\tsuper([], name, { type: 'httpd/unix-directory' })\n\t\tthis._contents = contents\n\t}\n\n\tset contents(contents: (Directory | File)[]) {\n\t\tthis._contents = contents\n\t}\n\n\tget contents(): (Directory | File)[] {\n\t\treturn this._contents\n\t}\n\n\tget size() {\n\t\treturn this._computeDirectorySize(this)\n\t}\n\n\tget lastModified() {\n\t\tif (this._contents.length === 0) {\n\t\t\treturn Date.now()\n\t\t}\n\t\treturn this._computeDirectoryMtime(this)\n\t}\n\n\t/**\n\t * Get the last modification time of a file tree\n\t * This is not perfect, but will get us a pretty good approximation\n\t *\n\t * @param directory the directory to traverse\n\t */\n\t_computeDirectoryMtime(directory: Directory): number {\n\t\treturn directory.contents.reduce((acc, file) => {\n\t\t\treturn file.lastModified > acc\n\t\t\t\t// If the file is a directory, the lastModified will\n\t\t\t\t// also return the results of its _computeDirectoryMtime method\n\t\t\t\t// Fancy recursion, huh?\n\t\t\t\t? file.lastModified\n\t\t\t\t: acc\n\t\t}, 0)\n\t}\n\n\t/**\n\t * Get the size of a file tree\n\t *\n\t * @param directory the directory to traverse\n\t */\n\t_computeDirectorySize(directory: Directory): number {\n\t\treturn directory.contents.reduce((acc: number, entry: Directory | File) => {\n\t\t\t// If the file is a directory, the size will\n\t\t\t// also return the results of its _computeDirectorySize method\n\t\t\t// Fancy recursion, huh?\n\t\t\treturn acc + entry.size\n\t\t}, 0)\n\t}\n}\n\nexport type RootDirectory = Directory & {\n\tname: 'root'\n}\n\n/**\n * Ask the user to rename every invalid file or folder of a dropped file tree.\n * Entries are replaced instead of renamed, as `File.name` is read only.\n *\n * @param directory Directory to validate\n * @param path Path of the directory relative to the upload destination\n * @return false if the user aborted the rename\n */\nexport async function renameInvalidDroppedEntries(directory: Directory, path = ''): Promise<boolean> {\n\tfor (const [index, entry] of directory.contents.entries()) {\n\t\tconst isFolder = entry instanceof Directory\n\t\tlet node = entry\n\n\t\tif (getFilenameValidity(node.name, false, isFolder) !== '') {\n\t\t\tconst otherNames = directory.contents.filter((other) => other !== node).map((other) => other.name)\n\t\t\tconst name = await newNodeName(node.name, otherNames, {\n\t\t\t\tname: t('files', 'Invalid name for \"{path}\"', { path: join(path, node.name) }, { escape: false }),\n\t\t\t\tlabel: isFolder ? t('files', 'Folder name') : t('files', 'Filename'),\n\t\t\t\tsubmitLabel: t('files', 'Rename'),\n\t\t\t\tisFolder,\n\t\t\t})\n\n\t\t\tif (name === null) {\n\t\t\t\tlogger.debug('Upload cancelled while renaming an invalid entry', { path, name: node.name })\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Keep the name unique within the dropped folder\n\t\t\tconst uniqueName = getUniqueName(name.trim(), otherNames)\n\t\t\tnode = isFolder\n\t\t\t\t? new Directory(uniqueName, (node as Directory).contents)\n\t\t\t\t: new File([node], uniqueName, { type: node.type, lastModified: node.lastModified })\n\t\t\tdirectory.contents.splice(index, 1, node)\n\t\t}\n\n\t\tif (node instanceof Directory && !await renameInvalidDroppedEntries(node, join(path, node.name))) {\n\t\t\treturn false\n\t\t}\n\t}\n\n\treturn true\n}\n\n/**\n * Traverse a file tree using the Filesystem API\n *\n * @param entry the entry to traverse\n */\nexport async function traverseTree(entry: FileSystemEntry): Promise<Directory | File> {\n\t// Handle file\n\tif (entry.isFile) {\n\t\treturn new Promise<File>((resolve, reject) => {\n\t\t\t(entry as FileSystemFileEntry).file(resolve, reject)\n\t\t})\n\t}\n\n\t// Handle directory\n\tlogger.debug('Handling recursive file tree', { entry: entry.name })\n\tconst directory = entry as FileSystemDirectoryEntry\n\tconst entries = await readDirectory(directory)\n\tconst contents = (await Promise.all(entries.map(traverseTree))).flat()\n\treturn new Directory(directory.name, contents)\n}\n\n/**\n * Read a directory using Filesystem API\n *\n * @param directory the directory to read\n */\nfunction readDirectory(directory: FileSystemDirectoryEntry): Promise<FileSystemEntry[]> {\n\tconst dirReader = directory.createReader()\n\n\treturn new Promise<FileSystemEntry[]>((resolve, reject) => {\n\t\tconst entries = [] as FileSystemEntry[]\n\t\tconst getEntries = () => {\n\t\t\tdirReader.readEntries((results) => {\n\t\t\t\tif (results.length) {\n\t\t\t\t\tentries.push(...results)\n\t\t\t\t\tgetEntries()\n\t\t\t\t} else {\n\t\t\t\t\tresolve(entries)\n\t\t\t\t}\n\t\t\t}, (error) => {\n\t\t\t\treject(error)\n\t\t\t})\n\t\t}\n\n\t\tgetEntries()\n\t})\n}\n\n/**\n * @param path - The path relative to the destination root\n * @param destination - The destination folder. When provided, directories are created relative\n * to its source URL instead of the default user root. This is needed for uploads into\n * non-default locations like team folders.\n */\nexport async function createDirectoryIfNotExists(path: string, destination?: IFolder) {\n\tconst davUrl = destination?.source ?? join(defaultRemoteURL, defaultRootPath)\n\tconst davRoot = destination?.root ?? defaultRootPath\n\tconst davClient = getClient(davUrl)\n\tconst dirExists = await davClient.exists(path)\n\tif (!dirExists) {\n\t\tlogger.debug('Directory does not exist, creating it', { path, davUrl })\n\t\tawait davClient.createDirectory(path, { recursive: true })\n\t\tconst stat = await davClient.stat(path, { details: true, data: getDefaultPropfind() }) as ResponseDataDetailed<FileStat>\n\t\temit('files:node:created', resultToNode(stat.data, davRoot, davUrl))\n\t}\n}\n\n/**\n * Resolve conflicts between existing files and incoming files\n *\n * @param files - incoming files\n * @param destination - destination folder\n * @param contents - existing contents of the destination folder\n */\nexport async function resolveConflict<T extends ((Directory | File) | INode)>(files: Array<T>, destination: IFolder, contents: INode[]): Promise<T[]> {\n\ttry {\n\t\t// List all conflicting files\n\t\tconst conflicts = files.filter((file: File | INode) => {\n\t\t\treturn contents.find((node: INode) => node.basename === (file instanceof File ? file.name : file.basename))\n\t\t}).filter(Boolean) as (File | INode)[]\n\n\t\t// List of incoming files that are NOT in conflict\n\t\tconst uploads = files.filter((file: File | INode) => {\n\t\t\treturn !conflicts.includes(file)\n\t\t})\n\n\t\t// Let the user choose what to do with the conflicting files\n\t\tconst conflictResults = await openConflictPicker(destination.path, conflicts, contents)\n\t\tlogger.debug('Conflict resolution', { uploads, conflictResults })\n\n\t\t// If the user selected nothing, we cancel the upload\n\t\tif (conflictResults === null || (conflictResults.selected.length === 0 && conflictResults.renamed.length === 0 && uploads.length === 0)) {\n\t\t\t// User skipped\n\t\t\tshowInfo(t('files', 'Conflicts resolution skipped'))\n\t\t\tlogger.info('User skipped the conflict resolution')\n\t\t\treturn []\n\t\t}\n\n\t\t// Update the list of files to upload\n\t\treturn [...uploads, ...conflictResults.selected, ...conflictResults.renamed] as (typeof files)\n\t} catch (error) {\n\t\t// User cancelled\n\t\tlogger.warn('User cancelled the upload', { error })\n\t\tshowWarning(t('files', 'Upload cancelled'))\n\t}\n\n\treturn []\n}\n","/*!\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { Folder, IFolder, INode } from '@nextcloud/files'\nimport type { IUpload } from '@nextcloud/files/upload'\nimport type { RootDirectory } from './DropServiceUtils.ts'\n\nimport { showError, showInfo, showSuccess, showWarning } from '@nextcloud/dialogs'\nimport { getUploader } from '@nextcloud/files/upload'\nimport { t } from '@nextcloud/l10n'\nimport { join } from '@nextcloud/paths'\nimport { handleCopyMoveNodesTo, HintException } from '../actions/moveOrCopyAction.ts'\nimport { MoveCopyAction } from '../actions/moveOrCopyActionUtils.ts'\nimport { getConflicts } from '../utils/conflicts.ts'\nimport { logger } from '../utils/logger.ts'\nimport { createDirectoryIfNotExists, Directory, renameInvalidDroppedEntries, resolveConflict, traverseTree } from './DropServiceUtils.ts'\n\n/**\n * This function converts a list of DataTransferItems to a file tree.\n * It uses the Filesystem API if available, otherwise it falls back to the File API.\n * The File API will NOT be available if the browser is not in a secure context (e.g. HTTP).\n * ⚠️ When using this method, you need to use it as fast as possible, as the DataTransferItems\n * will be cleared after the first access to the props of one of the entries.\n *\n * @param items the list of DataTransferItems\n */\nexport async function dataTransferToFileTree(items: DataTransferItem[]): Promise<RootDirectory> {\n\t// Check if the browser supports the Filesystem API\n\t// We need to cache the entries to prevent Blink engine bug that clears\n\t// the list (`data.items`) after first access props of one of the entries\n\tconst entries = items\n\t\t.filter((item) => {\n\t\t\tif (item.kind !== 'file') {\n\t\t\t\tlogger.debug('Skipping dropped item', { kind: item.kind, type: item.type })\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn true\n\t\t}).map((item) => {\n\t\t\t// MDN recommends to try both, as it might be renamed in the future\n\t\t\treturn (item as unknown as { getAsEntry?: () => FileSystemEntry | undefined })?.getAsEntry?.()\n\t\t\t\t?? item?.webkitGetAsEntry?.()\n\t\t\t\t?? item\n\t\t}) as (FileSystemEntry | DataTransferItem)[]\n\n\tlet warned = false\n\tconst fileTree = new Directory('root') as RootDirectory\n\n\t// Traverse the file tree\n\tfor (const entry of entries) {\n\t\t// Handle browser issues if Filesystem API is not available. Fallback to File API\n\t\tif (entry instanceof DataTransferItem) {\n\t\t\tlogger.warn('Could not get FilesystemEntry of item, falling back to file')\n\n\t\t\tconst file = entry.getAsFile()\n\t\t\tif (file === null) {\n\t\t\t\tlogger.warn('Could not process DataTransferItem', { type: entry.type, kind: entry.kind })\n\t\t\t\tshowError(t('files', 'One of the dropped files could not be processed'))\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Warn the user that the browser does not support the Filesystem API\n\t\t\t// we therefore cannot upload directories recursively.\n\t\t\t// Note: files with an unknown type are kept on purpose, the server\n\t\t\t// detects their mime type on upload (content sniffing + extension map).\n\t\t\tif (file.type === 'httpd/unix-directory' || (!file.type && file.size === 0)) {\n\t\t\t\tif (!warned) {\n\t\t\t\t\tlogger.warn('Browser does not support Filesystem API. Directories will not be uploaded')\n\t\t\t\t\tshowWarning(t('files', 'Your browser does not support the Filesystem API. Directories will not be uploaded'))\n\t\t\t\t\twarned = true\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfileTree.contents.push(file)\n\t\t\tcontinue\n\t\t}\n\n\t\t// Use Filesystem API\n\t\ttry {\n\t\t\tfileTree.contents.push(await traverseTree(entry))\n\t\t} catch (error) {\n\t\t\t// Do not throw, as we want to continue with the other files\n\t\t\tlogger.error('Error while traversing file tree', { error })\n\t\t}\n\t}\n\n\treturn fileTree\n}\n\n/**\n * Handle dropping external files\n *\n * @param root - The root directory which should be uploaded\n * @param destination - The destination folder\n * @param contents - The contents of the destination folder\n */\nexport async function onDropExternalFiles(root: RootDirectory, destination: IFolder, contents: INode[]): Promise<IUpload[]> {\n\t// Ask the user to fix any invalid name before uploading anything\n\tif (!await renameInvalidDroppedEntries(root)) {\n\t\tshowInfo(t('files', 'Upload cancelled, drop the files again to retry'))\n\t\treturn []\n\t}\n\n\tconst uploader = getUploader()\n\n\t// Check for conflicts on root elements\n\tif (getConflicts(root.contents, contents).length !== 0) {\n\t\troot.contents = await resolveConflict(root.contents, destination, contents)\n\t\tif (root.contents.length === 0) {\n\t\t\t// user cancelled the upload\n\t\t\treturn []\n\t\t}\n\t} else if (root.contents.length === 0) {\n\t\tlogger.info('No files to upload', { root })\n\t\tshowInfo(t('files', 'No files to upload'))\n\t\treturn []\n\t}\n\n\t// Let's process the files\n\tlogger.debug(`Uploading files to ${destination.path}`, { root, contents: root.contents })\n\tconst queue = [] as Promise<IUpload>[]\n\tlet hasDirectoryErrors = false\n\n\tconst uploadDirectoryContents = async (directory: Directory, path: string) => {\n\t\tfor (const file of directory.contents) {\n\t\t\t// This is the relative path to the resource\n\t\t\t// from the current uploader destination\n\t\t\tconst relativePath = join(path, file.name)\n\n\t\t\t// If the file is a directory, we need to create it first\n\t\t\t// then browse its tree and upload its contents.\n\t\t\tif (file instanceof Directory) {\n\t\t\t\ttry {\n\t\t\t\t\tlogger.debug('Processing directory', { relativePath, destination })\n\t\t\t\t\tawait createDirectoryIfNotExists(relativePath, destination)\n\t\t\t\t\tawait uploadDirectoryContents(file, relativePath)\n\t\t\t\t} catch (error) {\n\t\t\t\t\thasDirectoryErrors = true\n\t\t\t\t\tshowError(t('files', 'Unable to create the directory {directory}', { directory: file.name }))\n\t\t\t\t\tlogger.error('Unable to create the directory', { error, relativePath, directory: file })\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// If we've reached a file, we can upload it\n\t\t\tlogger.debug('Uploading file to ' + join(destination.path, relativePath), { file })\n\n\t\t\tqueue.push(uploader.upload(relativePath, file))\n\t\t}\n\t}\n\n\t// Pause the uploader to prevent it from starting\n\t// while we compute the queue\n\tuploader.pause()\n\n\t// The uploader resolves every upload path against its own destination, so it\n\t// has to be pointed at the drop target while the queue is built. Dropping on\n\t// a folder row uploads into that folder, not into the folder being browsed.\n\tconst previousDestination = uploader.destination\n\tuploader.destination = destination as Folder\n\ttry {\n\t\t// Upload the files. Using '/' as the starting point\n\t\t// as we already adjusted the uploader destination\n\t\tawait uploadDirectoryContents(root, '/')\n\t} finally {\n\t\tuploader.destination = previousDestination\n\t\tuploader.start()\n\t}\n\n\t// Wait for all promises to settle\n\tconst results = await Promise.allSettled(queue)\n\n\t// Check for errors\n\tconst errors = results.filter((result) => result.status === 'rejected')\n\tif (errors.length > 0) {\n\t\tlogger.error('Error while uploading files', { errors })\n\t\tshowError(t('files', 'Some files could not be uploaded'))\n\t}\n\tif (errors.length > 0 || hasDirectoryErrors) {\n\t\treturn []\n\t}\n\n\tlogger.debug('Files uploaded successfully')\n\tshowSuccess(t('files', 'Files uploaded successfully'))\n\n\treturn Promise.all(queue)\n}\n\n/**\n * Handle dropping internal files\n *\n * @param nodes - The nodes being dropped\n * @param destination - The destination folder\n * @param contents - The contents of the destination folder\n * @param isCopy - Whether the operation is a copy\n */\nexport async function onDropInternalFiles(nodes: INode[], destination: IFolder, contents: INode[], isCopy = false) {\n\t// Check for conflicts on root elements\n\tif (getConflicts(nodes, contents).length !== 0) {\n\t\tnodes = await resolveConflict(nodes, destination, contents)\n\t}\n\n\tif (nodes.length === 0) {\n\t\tlogger.info('No files to process', { nodes })\n\t\tshowInfo(t('files', 'No files to process'))\n\t\treturn\n\t}\n\n\ttry {\n\t\tconst promises = Array.fromAsync(handleCopyMoveNodesTo(nodes, destination, isCopy ? MoveCopyAction.COPY : MoveCopyAction.MOVE))\n\t\tawait promises\n\t\tlogger.debug('Files copy/move successful')\n\t\tshowSuccess(isCopy ? t('files', 'Files copied successfully') : t('files', 'Files moved successfully'))\n\t} catch (error) {\n\t\tlogger.error('Error while processing dropped files', { error })\n\t\tif (error instanceof HintException) {\n\t\t\tshowError(error.message)\n\t\t} else {\n\t\t\tshowError(isCopy ? t('files', 'Some files could not be copied') : t('files', 'Some files could not be moved'))\n\t\t}\n\t}\n}\n","/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { DragAndDropStore, FileSource } from '../types.ts'\n\nimport { defineStore } from 'pinia'\n\nexport const useDragAndDropStore = defineStore('dragging', {\n\tstate: () => ({\n\t\tdragging: [],\n\t} as DragAndDropStore),\n\n\tactions: {\n\t\t/**\n\t\t * Set the selection of files being dragged currently\n\t\t *\n\t\t * @param selection array of node sources\n\t\t */\n\t\tset(selection = [] as FileSource[]) {\n\t\t\tthis.dragging = selection\n\t\t},\n\n\t\t/**\n\t\t * Reset the selection\n\t\t */\n\t\treset() {\n\t\t\tthis.dragging = []\n\t\t},\n\t},\n})\n","/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { FileSource, SelectionStore } from '../types.ts'\n\nimport { defineStore } from 'pinia'\n\nexport const useSelectionStore = defineStore('selection', {\n\tstate: () => ({\n\t\tselected: [],\n\t\tlastSelection: [],\n\t\tlastSelectedIndex: null,\n\t} as SelectionStore),\n\n\tactions: {\n\t\t/**\n\t\t * Set the selection of fileIds\n\t\t *\n\t\t * @param selection\n\t\t */\n\t\tset(selection = [] as FileSource[]) {\n\t\t\tthis.selected = [...new Set(selection)]\n\t\t},\n\n\t\t/**\n\t\t * Set the last selected index\n\t\t *\n\t\t * @param lastSelectedIndex\n\t\t */\n\t\tsetLastIndex(lastSelectedIndex = null as number | null) {\n\t\t\t// Update the last selection if we provided a new selection starting point\n\t\t\tthis.lastSelection = lastSelectedIndex ? this.selected : []\n\t\t\tthis.lastSelectedIndex = lastSelectedIndex\n\t\t},\n\n\t\t/**\n\t\t * Reset the selection\n\t\t */\n\t\treset() {\n\t\t\tthis.selected = []\n\t\t\tthis.lastSelection = []\n\t\t\tthis.lastSelectedIndex = null\n\t\t},\n\t},\n})\n","/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { Uploader } from '@nextcloud/files/upload'\nimport type { UploaderStore } from '../types.ts'\n\nimport { getUploader } from '@nextcloud/files/upload'\nimport { defineStore } from 'pinia'\n\nlet uploader: Uploader\n\n/**\n *\n * @param args\n */\nexport function useUploaderStore(...args) {\n\t// Only init on runtime\n\tuploader = getUploader()\n\n\tconst store = defineStore('uploader', {\n\t\tstate: () => ({\n\t\t\tqueue: uploader.queue,\n\t\t} as UploaderStore),\n\t})\n\n\treturn store(...args)\n}\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n\n<template>\n\t<NcBreadcrumbs\n\t\tdata-cy-files-content-breadcrumbs\n\t\t:aria-label=\"t('files', 'Current directory path')\"\n\t\tclass=\"files-list__breadcrumbs\"\n\t\t:class=\"{ 'files-list__breadcrumbs--with-progress': wrapUploadProgressBar }\">\n\t\t<!-- Current path sections -->\n\t\t<NcBreadcrumb\n\t\t\tv-for=\"(section, index) in sections\"\n\t\t\t:key=\"section.dir\"\n\t\t\tv-bind=\"section\"\n\t\t\tv-model:open=\"isMenuOpen\"\n\t\t\tdir=\"auto\"\n\t\t\t:to=\"section.to\"\n\t\t\t:forceIconText=\"index === 0 && !isNarrow\"\n\t\t\tforceMenu\n\t\t\t:title=\"titleForSection(index, section)\"\n\t\t\t:aria-description=\"ariaForSection(section)\"\n\t\t\t@dragover=\"onDragOver($event, section.dir)\"\n\t\t\t@drop=\"onDrop($event, section.dir)\">\n\t\t\t<template v-if=\"index === 0\" #icon>\n\t\t\t\t<NcIconSvgWrapper\n\t\t\t\t\t:size=\"20\"\n\t\t\t\t\t:svg=\"viewIcon\" />\n\t\t\t</template>\n\t\t\t<template v-if=\"index === sections.length - 1\" #menu-icon>\n\t\t\t\t<NcIconSvgWrapper :path=\"isMenuOpen ? mdiChevronUp : mdiChevronDown\" />\n\t\t\t</template>\n\t\t\t<template v-if=\"index === sections.length - 1\" #default>\n\t\t\t\t<!-- Sharing button -->\n\t\t\t\t<NcActionButton v-if=\"canShare\" closeAfterClick @click=\"openSharingSidebar\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<NcIconSvgWrapper :path=\"mdiAccountPlus\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ t('files', 'Share') }}\n\t\t\t\t</NcActionButton>\n\n\t\t\t\t<!-- Reload button -->\n\t\t\t\t<NcActionButton closeAfterClick @click=\"$emit('reload')\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<NcIconSvgWrapper :path=\"mdiReload\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ t('files', 'Reload content') }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\t\t</NcBreadcrumb>\n\n\t\t<!-- Forward the actions slot -->\n\t\t<template #actions>\n\t\t\t<slot name=\"actions\" />\n\t\t</template>\n\t</NcBreadcrumbs>\n</template>\n\n<script lang=\"ts\">\nimport type { Node } from '@nextcloud/files'\nimport type { FileSource } from '../types.ts'\n\nimport { mdiAccountPlus, mdiChevronDown, mdiChevronUp, mdiReload } from '@mdi/js'\nimport HomeSvg from '@mdi/svg/svg/home.svg?raw'\nimport { getCapabilities } from '@nextcloud/capabilities'\nimport { showError } from '@nextcloud/dialogs'\nimport { getSidebar, Permission } from '@nextcloud/files'\nimport { t } from '@nextcloud/l10n'\nimport { isPublicShare } from '@nextcloud/sharing/public'\nimport { basename } from 'path'\nimport { computed, defineComponent, ref, watch } from 'vue'\nimport NcActionButton from '@nextcloud/vue/components/NcActionButton'\nimport NcBreadcrumb from '@nextcloud/vue/components/NcBreadcrumb'\nimport NcBreadcrumbs from '@nextcloud/vue/components/NcBreadcrumbs'\nimport NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'\nimport { useFileListWidth } from '../composables/useFileListWidth.ts'\nimport { useViews } from '../composables/useViews.ts'\nimport { dataTransferToFileTree, onDropExternalFiles, onDropInternalFiles } from '../services/DropService.ts'\nimport { useActiveStore } from '../store/active.ts'\nimport { useDragAndDropStore } from '../store/dragging.ts'\nimport { useFilesStore } from '../store/files.ts'\nimport { usePathsStore } from '../store/paths.ts'\nimport { useSelectionStore } from '../store/selection.ts'\nimport { useUploaderStore } from '../store/uploader.ts'\nimport { logger } from '../utils/logger.ts'\n\nexport default defineComponent({\n\tname: 'BreadCrumbs',\n\n\tcomponents: {\n\t\tNcActionButton,\n\t\tNcBreadcrumbs,\n\t\tNcBreadcrumb,\n\t\tNcIconSvgWrapper,\n\t},\n\n\tprops: {\n\t\tpath: {\n\t\t\ttype: String,\n\t\t\tdefault: '/',\n\t\t},\n\t},\n\n\temits: ['reload'],\n\n\tsetup() {\n\t\tconst activeStore = useActiveStore()\n\t\tconst filesStore = useFilesStore()\n\t\tconst pathsStore = usePathsStore()\n\t\tconst draggingStore = useDragAndDropStore()\n\t\tconst selectionStore = useSelectionStore()\n\t\tconst uploaderStore = useUploaderStore()\n\n\t\tconst { isNarrow } = useFileListWidth()\n\t\tconst views = useViews()\n\n\t\tconst isMenuOpen = ref(false)\n\t\twatch(() => activeStore.activeFolder, () => {\n\t\t\tisMenuOpen.value = false\n\t\t})\n\n\t\tconst isSharingEnabled = (getCapabilities() as { files_sharing?: boolean })?.files_sharing !== undefined\n\t\tconst isPublic = isPublicShare()\n\t\tconst canShare = computed(() => {\n\t\t\treturn isSharingEnabled\n\t\t\t\t&& !isPublic\n\t\t\t\t&& activeStore.activeFolder\n\t\t\t\t&& (activeStore.activeFolder.permissions & Permission.SHARE) !== 0\n\t\t})\n\n\t\treturn {\n\t\t\tactiveStore,\n\t\t\tdraggingStore,\n\t\t\tfilesStore,\n\t\t\tpathsStore,\n\t\t\tselectionStore,\n\t\t\tuploaderStore,\n\n\t\t\tcanShare,\n\t\t\tisMenuOpen,\n\t\t\tisNarrow,\n\t\t\tviews,\n\t\t\topenSharingSidebar,\n\n\t\t\tmdiAccountPlus,\n\t\t\tmdiChevronDown,\n\t\t\tmdiChevronUp,\n\t\t\tmdiReload,\n\t\t}\n\n\t\t/**\n\t\t * Open the sharing sidebar for the current folder\n\t\t */\n\t\tfunction openSharingSidebar() {\n\t\t\tgetSidebar().open(activeStore.activeFolder!, 'sharing')\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tdirs(): string[] {\n\t\t\tconst cumulativePath = (acc: string) => (value: string) => (acc += `${value}/`)\n\t\t\t// Generate a cumulative path for each path segment: ['/', '/foo', '/foo/bar', ...] etc\n\t\t\tconst paths: string[] = this.path.split('/').filter(Boolean).map(cumulativePath('/'))\n\t\t\t// Strip away trailing slash\n\t\t\treturn ['/', ...paths.map((path: string) => path.replace(/^(.+)\\/$/, '$1'))]\n\t\t},\n\n\t\tsections() {\n\t\t\treturn this.dirs.map((dir: string, index: number) => {\n\t\t\t\tconst source = this.getFileSourceFromPath(dir)\n\t\t\t\tconst node: Node | undefined = source ? this.getNodeFromSource(source) : undefined\n\t\t\t\treturn {\n\t\t\t\t\tdir,\n\t\t\t\t\texact: true,\n\t\t\t\t\tname: this.getDirDisplayName(dir),\n\t\t\t\t\tto: this.getTo(dir, node),\n\t\t\t\t\t// disable drop on current directory\n\t\t\t\t\tdisableDrop: index === this.dirs.length - 1,\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\tisUploadInProgress(): boolean {\n\t\t\treturn this.uploaderStore.queue.length !== 0\n\t\t},\n\n\t\t// Hide breadcrumbs if an upload is ongoing\n\t\twrapUploadProgressBar(): boolean {\n\t\t\t// if an upload is ongoing, and on small screens / mobile, then\n\t\t\t// show the progress bar for the upload below breadcrumbs\n\t\t\treturn this.isUploadInProgress && this.isNarrow\n\t\t},\n\n\t\t// used to show the views icon for the first breadcrumb\n\t\tviewIcon(): string {\n\t\t\treturn this.activeStore.activeView?.icon ?? HomeSvg\n\t\t},\n\n\t\tselectedFiles() {\n\t\t\treturn this.selectionStore.selected as FileSource[]\n\t\t},\n\n\t\tdraggingFiles() {\n\t\t\treturn this.draggingStore.dragging as FileSource[]\n\t\t},\n\t},\n\n\tmethods: {\n\t\tgetNodeFromSource(source: FileSource): Node | undefined {\n\t\t\treturn this.filesStore.getNode(source)\n\t\t},\n\n\t\tgetFileSourceFromPath(path: string): FileSource | null {\n\t\t\treturn (this.activeStore.activeView && this.pathsStore.getPath(this.activeStore.activeView.id, path)) ?? null\n\t\t},\n\n\t\tgetDirDisplayName(path: string): string {\n\t\t\tif (path === '/') {\n\t\t\t\treturn this.activeStore.activeView?.name || t('files', 'Home')\n\t\t\t}\n\n\t\t\tconst source = this.getFileSourceFromPath(path)\n\t\t\tconst node = source ? this.getNodeFromSource(source) : undefined\n\t\t\treturn node?.displayname || basename(path)\n\t\t},\n\n\t\tgetTo(dir: string, node?: Node): Record<string, unknown> {\n\t\t\tif (dir === '/') {\n\t\t\t\treturn {\n\t\t\t\t\t...this.$route,\n\t\t\t\t\tparams: { view: this.activeStore.activeView?.id },\n\t\t\t\t\tquery: {},\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (node === undefined) {\n\t\t\t\tconst view = this.views.find((view) => view.params?.dir === dir)\n\t\t\t\treturn {\n\t\t\t\t\t...this.$route,\n\t\t\t\t\tparams: { fileid: view?.params?.fileid ?? '' },\n\t\t\t\t\tquery: { dir },\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\t...this.$route,\n\t\t\t\tparams: { fileid: String(node.fileid) },\n\t\t\t\tquery: { dir: node.path },\n\t\t\t}\n\t\t},\n\n\t\tonDragOver(event: DragEvent, path: string) {\n\t\t\tif (!event.dataTransfer) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Cannot drop on the current directory\n\t\t\tif (path === this.dirs[this.dirs.length - 1]) {\n\t\t\t\tevent.dataTransfer.dropEffect = 'none'\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Handle copy/move drag and drop\n\t\t\tif (event.ctrlKey) {\n\t\t\t\tevent.dataTransfer.dropEffect = 'copy'\n\t\t\t} else {\n\t\t\t\tevent.dataTransfer.dropEffect = 'move'\n\t\t\t}\n\t\t},\n\n\t\tasync onDrop(event: DragEvent, path: string) {\n\t\t\t// skip if native drop like text drag and drop from files names\n\t\t\tif (!this.draggingFiles && !event.dataTransfer?.items?.length) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Do not stop propagation, so the main content\n\t\t\t// drop event can be triggered too and clear the\n\t\t\t// dragover state on the DragAndDropNotice component.\n\t\t\tevent.preventDefault()\n\n\t\t\t// Caching the selection\n\t\t\tconst selection = this.draggingFiles\n\t\t\tconst items = [...event.dataTransfer?.items || []] as DataTransferItem[]\n\n\t\t\t// We need to process the dataTransfer ASAP before the\n\t\t\t// browser clears it. This is why we cache the items too.\n\t\t\tconst fileTree = await dataTransferToFileTree(items)\n\n\t\t\t// We might not have the target directory fetched yet\n\t\t\tconst controller = new AbortController()\n\t\t\tconst contents = await this.activeStore.activeView?.getContents(path, { signal: controller.signal })\n\t\t\tconst folder = contents?.folder\n\t\t\tif (!folder) {\n\t\t\t\tshowError(this.t('files', 'Target folder does not exist any more'))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst canDrop = (folder.permissions & Permission.CREATE) !== 0\n\t\t\tconst isCopy = event.ctrlKey\n\n\t\t\t// If another button is pressed, cancel it. This\n\t\t\t// allows cancelling the drag with the right click.\n\t\t\tif (!canDrop || event.button !== 0) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlogger.debug('Dropped', { event, folder, selection, fileTree })\n\n\t\t\t// Check whether we're uploading files\n\t\t\tif (fileTree.contents.length > 0) {\n\t\t\t\tawait onDropExternalFiles(fileTree, folder, contents.contents)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Else we're moving/copying files\n\t\t\tconst nodes = selection.map((source) => this.filesStore.getNode(source)) as Node[]\n\t\t\tawait onDropInternalFiles(nodes, folder, contents.contents, isCopy)\n\n\t\t\t// Reset selection after we dropped the files\n\t\t\t// if the dropped files are within the selection\n\t\t\tif (selection.some((source) => this.selectedFiles.includes(source))) {\n\t\t\t\tlogger.debug('Dropped selection, resetting select store...')\n\t\t\t\tthis.selectionStore.reset()\n\t\t\t}\n\t\t},\n\n\t\ttitleForSection(index, section) {\n\t\t\tif (section?.to?.query?.dir === this.$route.query.dir) {\n\t\t\t\treturn t('files', 'Reload current directory')\n\t\t\t} else if (index === 0) {\n\t\t\t\treturn t('files', 'Go to the \"{dir}\" directory', section)\n\t\t\t}\n\t\t},\n\n\t\tariaForSection(section) {\n\t\t\tif (section?.to?.query?.dir === this.$route.query.dir) {\n\t\t\t\treturn t('files', 'Reload current directory')\n\t\t\t}\n\t\t},\n\n\t\tt,\n\t},\n})\n</script>\n\n<style lang=\"scss\" scoped>\n.files-list__breadcrumbs {\n\t// Take as much space as possible\n\tflex: 1 1 100% !important;\n\twidth: 100%;\n\theight: 100%;\n\tmargin: 0;\n\tmin-width: 0;\n\n\t:deep() {\n\t\ta {\n\t\t\tcursor: pointer !important;\n\t\t}\n\t}\n\n\t&--with-progress {\n\t\tflex-direction: column !important;\n\t\talign-items: flex-start !important;\n\t}\n}\n</style>\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<div\n\t\tv-show=\"dragover\"\n\t\tdata-cy-files-drag-drop-area\n\t\tclass=\"files-list__drag-drop-notice\"\n\t\t@drop=\"onDrop\">\n\t\t<div class=\"files-list__drag-drop-notice-wrapper\">\n\t\t\t<template v-if=\"canUpload && !isQuotaExceeded\">\n\t\t\t\t<TrayArrowDownIcon :size=\"48\" />\n\t\t\t\t<h3 class=\"files-list-drag-drop-notice__title\">\n\t\t\t\t\t{{ t('files', 'Drag and drop files here to upload') }}\n\t\t\t\t</h3>\n\t\t\t</template>\n\n\t\t\t<!-- Not permitted to drop files here -->\n\t\t\t<template v-else>\n\t\t\t\t<h3 class=\"files-list-drag-drop-notice__title\">\n\t\t\t\t\t{{ cantUploadLabel }}\n\t\t\t\t</h3>\n\t\t\t</template>\n\t\t</div>\n\t</div>\n</template>\n\n<script lang=\"ts\">\nimport type { Folder } from '@nextcloud/files'\nimport type { PropType } from 'vue'\n\nimport { showError } from '@nextcloud/dialogs'\nimport { Permission } from '@nextcloud/files'\nimport { UploadStatus } from '@nextcloud/files/upload'\nimport { t } from '@nextcloud/l10n'\nimport debounce from 'debounce'\nimport { defineComponent } from 'vue'\nimport TrayArrowDownIcon from 'vue-material-design-icons/TrayArrowDown.vue'\nimport { dataTransferToFileTree, onDropExternalFiles } from '../services/DropService.ts'\nimport { useActiveStore } from '../store/active.ts'\nimport { logger } from '../utils/logger.ts'\n\nexport default defineComponent({\n\tname: 'DragAndDropNotice',\n\n\tcomponents: {\n\t\tTrayArrowDownIcon,\n\t},\n\n\tprops: {\n\t\tcurrentFolder: {\n\t\t\ttype: Object as PropType<Folder>,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\tsetup() {\n\t\tconst activeStore = useActiveStore()\n\n\t\treturn {\n\t\t\tactiveStore,\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tdragover: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Check if the current folder has create permissions\n\t\t */\n\t\tcanUpload() {\n\t\t\treturn this.currentFolder && (this.currentFolder.permissions & Permission.CREATE) !== 0\n\t\t},\n\n\t\tisQuotaExceeded() {\n\t\t\treturn this.currentFolder?.attributes?.['quota-available-bytes'] === 0\n\t\t},\n\n\t\tcantUploadLabel() {\n\t\t\tif (this.isQuotaExceeded) {\n\t\t\t\treturn this.t('files', 'Your have used your space quota and cannot upload files anymore')\n\t\t\t} else if (!this.canUpload) {\n\t\t\t\treturn this.t('files', 'You do not have permission to upload or create files here.')\n\t\t\t}\n\t\t\treturn null\n\t\t},\n\n\t\t/**\n\t\t * Debounced function to reset the drag over state\n\t\t * Required as Firefox has a bug where no dragleave is emitted:\n\t\t * https://bugzilla.mozilla.org/show_bug.cgi?id=656164\n\t\t */\n\t\tresetDragOver() {\n\t\t\treturn debounce(() => {\n\t\t\t\tthis.dragover = false\n\t\t\t}, 3000)\n\t\t},\n\t},\n\n\tmounted() {\n\t\t// Add events on parent to cover both the table and DragAndDrop notice\n\t\tconst mainContent = window.document.getElementById('app-content-vue') as HTMLElement\n\t\tmainContent.addEventListener('dragover', this.onDragOver)\n\t\tmainContent.addEventListener('dragleave', this.onDragLeave)\n\t\tmainContent.addEventListener('drop', this.onContentDrop)\n\t},\n\n\tbeforeUnmount() {\n\t\tconst mainContent = window.document.getElementById('app-content-vue') as HTMLElement\n\t\tmainContent.removeEventListener('dragover', this.onDragOver)\n\t\tmainContent.removeEventListener('dragleave', this.onDragLeave)\n\t\tmainContent.removeEventListener('drop', this.onContentDrop)\n\t},\n\n\tmethods: {\n\t\tonDragOver(event: DragEvent) {\n\t\t\t// Needed to keep the drag/drop events chain working\n\t\t\tevent.preventDefault()\n\n\t\t\tconst isForeignFile = event.dataTransfer?.types.includes('Files')\n\t\t\tif (isForeignFile) {\n\t\t\t\t// Only handle uploading of outside files (not Nextcloud files)\n\t\t\t\tthis.dragover = true\n\t\t\t\tthis.resetDragOver()\n\t\t\t}\n\t\t},\n\n\t\tonDragLeave(event: DragEvent) {\n\t\t\t// Counter bubbling, make sure we're ending the drag\n\t\t\t// only when we're leaving the current element\n\t\t\t// Avoid flickering\n\t\t\tconst currentTarget = event.currentTarget as HTMLElement\n\t\t\tif (currentTarget?.contains((event.relatedTarget ?? event.target) as HTMLElement)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (this.dragover) {\n\t\t\t\tthis.dragover = false\n\t\t\t\tthis.resetDragOver.clear()\n\t\t\t}\n\t\t},\n\n\t\tonContentDrop(event: DragEvent) {\n\t\t\tlogger.debug('Drag and drop cancelled, dropped on empty space', { event })\n\t\t\tevent.preventDefault()\n\t\t\tif (this.dragover) {\n\t\t\t\tthis.dragover = false\n\t\t\t\tthis.resetDragOver.clear()\n\t\t\t}\n\t\t},\n\n\t\tasync onDrop(event: DragEvent) {\n\t\t\t// cantUploadLabel is null if we can upload\n\t\t\tif (this.cantUploadLabel) {\n\t\t\t\tshowError(this.cantUploadLabel)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (this.$el.querySelector('tbody')?.contains(event.target as Node)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tevent.preventDefault()\n\t\t\tevent.stopPropagation()\n\n\t\t\t// Caching the selection\n\t\t\tconst items: DataTransferItem[] = Array.from(event.dataTransfer?.items || [])\n\n\t\t\t// We need to process the dataTransfer ASAP before the\n\t\t\t// browser clears it. This is why we cache the items too.\n\t\t\tconst fileTree = await dataTransferToFileTree(items)\n\n\t\t\t// We might not have the target directory fetched yet\n\t\t\tconst controller = new AbortController()\n\t\t\tconst contents = await this.activeStore.activeView?.getContents(this.currentFolder.path, { signal: controller.signal })\n\t\t\tconst folder = contents?.folder\n\t\t\tif (!folder) {\n\t\t\t\tshowError(this.t('files', 'Target folder does not exist any more'))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If another button is pressed, cancel it. This\n\t\t\t// allows cancelling the drag with the right click.\n\t\t\tif (event.button) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlogger.debug('Dropped', { event, folder, fileTree })\n\n\t\t\t// Check whether we're uploading files\n\t\t\tconst uploads = await onDropExternalFiles(fileTree, folder, contents.contents)\n\n\t\t\t// Scroll to last successful upload in current directory if terminated\n\t\t\tconst lastUpload = uploads.findLast((upload) => upload.status !== UploadStatus.FAILED\n\t\t\t\t&& !upload.file.webkitRelativePath.includes('/')\n\t\t\t\t&& upload.response?.headers?.['oc-fileid']\n\t\t\t\t// Only use the last ID if it's in the current folder\n\t\t\t\t&& upload.source.replace(folder.source, '').split('/').length === 2)\n\n\t\t\tif (lastUpload !== undefined) {\n\t\t\t\tconst fileid = String(lastUpload.response!.headers['oc-fileid']).split(/(oc|nc)/, 2)[0]!\n\t\t\t\tlogger.debug('Scrolling to last upload in current folder', { lastUpload })\n\t\t\t\tthis.$router.push({\n\t\t\t\t\tname: this.$route.name!,\n\t\t\t\t\tparams: {\n\t\t\t\t\t\t// Keep params but change file id\n\t\t\t\t\t\t...this.$route.params,\n\t\t\t\t\t\tfileid,\n\t\t\t\t\t},\n\t\t\t\t\tquery: {\n\t\t\t\t\t\tdir: this.$route.query.dir,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tthis.dragover = false\n\t\t\tthis.resetDragOver.clear()\n\t\t},\n\n\t\tt,\n\t},\n})\n</script>\n\n<style lang=\"scss\" scoped>\n.files-list__drag-drop-notice {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 100%;\n\t// Breadcrumbs height + row thead height\n\tmin-height: calc(58px + 44px);\n\tmargin: 0;\n\tuser-select: none;\n\tcolor: var(--color-text-maxcontrast);\n\tbackground-color: var(--color-main-background);\n\tborder-color: black;\n\n\th3 {\n\t\tmargin-inline-start: 16px;\n\t\tcolor: inherit;\n\t}\n\n\t&-wrapper {\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\theight: 15vh;\n\t\tmax-height: 70%;\n\t\tpadding: 0 5vw;\n\t\tborder: 2px var(--color-border-dark) dashed;\n\t\tborder-radius: var(--border-radius-large);\n\t}\n}\n\n</style>\n","/*!\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { FilterUpdateChipsEvent, IFileListFilter, IFileListFilterChip, IFileListFilterWithUi } from '@nextcloud/files'\n\nimport { emit, subscribe } from '@nextcloud/event-bus'\nimport { getFileListFilters, getFilesRegistry } from '@nextcloud/files'\nimport { defineStore } from 'pinia'\nimport { computed, ref } from 'vue'\nimport { logger } from '../utils/logger.ts'\n\n/**\n * Check if the given value is an instance file list filter with mount function\n *\n * @param value The filter to check\n */\nfunction isFileListFilterWithUi(value: IFileListFilter): value is IFileListFilterWithUi {\n\treturn 'tagName' in value\n}\n\nexport const useFiltersStore = defineStore('filters', () => {\n\tconst chips = ref<Record<string, IFileListFilterChip[]>>({})\n\tconst filters = ref<IFileListFilter[]>([])\n\n\t/**\n\t * Currently active filter chips\n\t */\n\tconst activeChips = computed<IFileListFilterChip[]>(() => Object.values(chips.value).flat())\n\n\t/**\n\t * Filters sorted by order\n\t */\n\tconst sortedFilters = computed<IFileListFilter[]>(() => filters.value.sort((a, b) => a.order - b.order))\n\n\t/**\n\t * All filters that provide a UI for visual controlling the filter state\n\t */\n\tconst filtersWithUI = computed<IFileListFilterWithUi[]>(() => sortedFilters.value.filter(isFileListFilterWithUi))\n\n\t/**\n\t * Register a new filter on the store.\n\t * This will subscribe the store to the filters events.\n\t *\n\t * @param filter The filter to add\n\t */\n\tfunction addFilter(filter: IFileListFilter) {\n\t\tfilter.addEventListener('update:chips', onFilterUpdateChips)\n\t\tfilter.addEventListener('update:filter', onFilterUpdate)\n\n\t\tfilters.value.push(filter)\n\t\tlogger.debug('New file list filter registered', { id: filter.id })\n\t}\n\n\t/**\n\t * Unregister a filter from the store.\n\t * This will remove the filter from the store and unsubscribe the store from the filer events.\n\t *\n\t * @param filterId Id of the filter to remove\n\t */\n\tfunction removeFilter(filterId: string) {\n\t\tconst index = filters.value.findIndex(({ id }) => id === filterId)\n\t\tif (index > -1) {\n\t\t\tconst [filter] = filters.value.splice(index, 1)\n\t\t\tfilter!.removeEventListener('update:chips', onFilterUpdateChips)\n\t\t\tfilter!.removeEventListener('update:filter', onFilterUpdate)\n\t\t\tlogger.debug('Files list filter unregistered', { id: filterId })\n\t\t}\n\t}\n\n\t/**\n\t * Event handler for filter update events\n\t *\n\t */\n\tfunction onFilterUpdate() {\n\t\temit('files:filters:changed')\n\t}\n\n\t/**\n\t * Event handler for filter chips updates\n\t *\n\t * @param event The update event\n\t */\n\tfunction onFilterUpdateChips(event: FilterUpdateChipsEvent) {\n\t\tconst id = (event.target as IFileListFilter).id\n\t\tchips.value = {\n\t\t\t...chips.value,\n\t\t\t[id]: [...event.detail],\n\t\t}\n\n\t\tlogger.debug('File list filter chips updated', { filter: id, chips: event.detail })\n\t}\n\n\tinitialize()\n\n\treturn {\n\t\t// state\n\t\tchips,\n\t\tfilters,\n\t\tfiltersWithUI,\n\n\t\t// getters / computed\n\t\tactiveChips,\n\t\tsortedFilters,\n\t}\n\n\t/**\n\t * Initialize the store by registering event listeners and loading initial filters.\n\t *\n\t * @internal\n\t */\n\tfunction initialize() {\n\t\tconst registry = getFilesRegistry()\n\t\tconst initialFilters = getFileListFilters()\n\t\t// handle adding and removing filters after initialization\n\t\tregistry.addEventListener('register:listFilter', (event) => {\n\t\t\taddFilter(event.detail)\n\t\t})\n\t\tregistry.addEventListener('unregister:listFilter', (event) => {\n\t\t\tremoveFilter(event.detail)\n\t\t})\n\t\t// register the initial filters\n\t\tfor (const filter of initialFilters) {\n\t\t\taddFilter(filter)\n\t\t}\n\n\t\t// subscribe to file list view changes to reset the filters\n\t\tsubscribe('files:navigation:changed', onViewChanged)\n\t}\n\n\t/**\n\t * Event handler that resets all filters if the file list view was changed.\n\t *\n\t * @internal\n\t */\n\tfunction onViewChanged() {\n\t\tlogger.debug('Reset all file list filters - view changed')\n\n\t\tfor (const filter of filters.value) {\n\t\t\tif (filter.reset !== undefined) {\n\t\t\t\tfilter.reset()\n\t\t\t}\n\t\t}\n\t}\n})\n","<!--\n - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n\n<script setup lang=\"ts\">\nimport type { IFileListFilterWithUi } from '@nextcloud/files'\n\nimport { mdiArrowLeft, mdiFilterVariant } from '@mdi/js'\nimport { t } from '@nextcloud/l10n'\nimport { computed, ref } from 'vue'\nimport NcButton from '@nextcloud/vue/components/NcButton'\nimport NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'\nimport NcPopover from '@nextcloud/vue/components/NcPopover'\nimport { useFileListWidth } from '../../composables/useFileListWidth.ts'\nimport { useFiltersStore } from '../../store/filters.ts'\n\nconst filterStore = useFiltersStore()\nconst visualFilters = computed(() => filterStore.filtersWithUI)\nconst hasActiveFilters = computed(() => filterStore.activeChips.length > 0)\n\nconst selectedFilter = ref<IFileListFilterWithUi>()\n\nconst { isWide } = useFileListWidth()\nconst menuTriggerId = 'file-list-filters-menu-trigger'\n\nconst boundary = document.getElementById('app-content-vue')!\n</script>\n\n<template>\n\t<div :class=\"$style.fileListFilters\" data-test-id=\"files-list-filters\">\n\t\t<template v-if=\"isWide\">\n\t\t\t<NcPopover v-for=\"filter of visualFilters\" :key=\"filter.id\" :boundary=\"boundary\">\n\t\t\t\t<template #trigger>\n\t\t\t\t\t<NcButton variant=\"tertiary\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<NcIconSvgWrapper :svg=\"filter.iconSvgInline\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ filter.displayName }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t</template>\n\t\t\t\t<template #default>\n\t\t\t\t\t<div :class=\"$style.fileListFilters__popoverContainer\">\n\t\t\t\t\t\t<component :is=\"filter.tagName\" :filter.prop=\"filter\" />\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</NcPopover>\n\t\t</template>\n\n\t\t<NcPopover\n\t\t\tv-else\n\t\t\t:boundary=\"boundary\"\n\t\t\t:popupRole=\"selectedFilter ? 'dialog' : 'menu'\"\n\t\t\t@update:shown=\"selectedFilter = undefined\">\n\t\t\t<template #trigger>\n\t\t\t\t<NcButton\n\t\t\t\t\t:id=\"menuTriggerId\"\n\t\t\t\t\t:aria-label=\"t('files', 'Filters')\"\n\t\t\t\t\t:pressed=\"hasActiveFilters\"\n\t\t\t\t\tvariant=\"tertiary\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<NcIconSvgWrapper :path=\"mdiFilterVariant\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcButton>\n\t\t\t</template>\n\t\t\t<template #default>\n\t\t\t\t<div v-if=\"selectedFilter\" :class=\"$style.fileListFilters__popoverFilterView\">\n\t\t\t\t\t<NcButton wide variant=\"tertiary\" @click=\"selectedFilter = undefined\">\n\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t<NcIconSvgWrapper directional :path=\"mdiArrowLeft\" />\n\t\t\t\t\t\t</template>\n\t\t\t\t\t\t{{ t('files', 'Back to filters') }}\n\t\t\t\t\t</NcButton>\n\t\t\t\t\t<component :is=\"selectedFilter.tagName\" :filter.prop=\"selectedFilter\" />\n\t\t\t\t</div>\n\t\t\t\t<template v-else>\n\t\t\t\t\t<ul :class=\"$style.fileListFilters__popoverContainer\" :aria-labelledby=\"menuTriggerId\" role=\"menu\">\n\t\t\t\t\t\t<li v-for=\"filter of visualFilters\" :key=\"filter.id\" role=\"presentation\">\n\t\t\t\t\t\t\t<NcButton\n\t\t\t\t\t\t\t\trole=\"menuitem\"\n\t\t\t\t\t\t\t\talignment=\"start\"\n\t\t\t\t\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t\t\t\t\twide\n\t\t\t\t\t\t\t\t@click=\"selectedFilter = filter\">\n\t\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t\t<NcIconSvgWrapper :svg=\"filter.iconSvgInline\" />\n\t\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t\t{{ filter.displayName }}\n\t\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t\t\t</template>\n\t\t\t</template>\n\t\t</NcPopover>\n\t</div>\n</template>\n\n<style module>\n.fileListFilters {\n\tdisplay: flex;\n\tflex-direction: row;\n\tgap: var(--default-grid-baseline);\n\tmargin-inline-end: var(--default-grid-baseline);\n\theight: 100%;\n\twidth: 100%;\n}\n\n.fileListFilters__popoverFilterView {\n\tbox-sizing: border-box;\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: calc(2 * var(--default-grid-baseline));\n\tpadding: calc(var(--default-grid-baseline) / 2);\n\tmin-width: calc(7 * var(--default-clickable-area));\n}\n\n.fileListFilters__popoverContainer {\n\tbox-sizing: border-box;\n\tpadding: calc(var(--default-grid-baseline) / 2);\n\tmin-width: calc(7 * var(--default-clickable-area));\n}\n\n.fileListFilters__filter {\n\tdisplay: flex;\n\talign-items: start;\n\tjustify-content: start;\n\tgap: calc(var(--default-grid-baseline, 4px) * 2);\n\n\t> * {\n\t\tflex: 0 1 fit-content;\n\t}\n}\n\n.fileListFilters__active {\n\tdisplay: flex;\n\tflex-direction: row;\n\tgap: calc(var(--default-grid-baseline, 4px) * 2);\n}\n</style>\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<span />\n</template>\n\n<script lang=\"ts\">\nimport type { IFileAction, IFolder, INode, IView } from '@nextcloud/files'\nimport type { PropType } from 'vue'\n\ntype RenderFunction = IFileAction['renderInline']\n\n/**\n * This component is used to render custom\n * elements provided by an API. Vue doesn't allow\n * to directly render an HTMLElement, so we can do\n * this magic here.\n */\nexport default {\n\tname: 'CustomElementRender',\n\tprops: {\n\t\tsource: {\n\t\t\ttype: Object as PropType<INode>,\n\t\t\trequired: true,\n\t\t},\n\n\t\tactiveView: {\n\t\t\ttype: Object as PropType<IView>,\n\t\t\trequired: true,\n\t\t},\n\n\t\tactiveFolder: {\n\t\t\ttype: Object as PropType<IFolder>,\n\t\t\trequired: true,\n\t\t},\n\n\t\trender: {\n\t\t\ttype: Function as PropType<RenderFunction>,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\twatch: {\n\t\tsource() {\n\t\t\tthis.updateRootElement()\n\t\t},\n\n\t\tcurrentView() {\n\t\t\tthis.updateRootElement()\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.updateRootElement()\n\t},\n\n\tmethods: {\n\t\tasync updateRootElement() {\n\t\t\tconst element = await this.render!({\n\t\t\t\tnodes: [this.source],\n\t\t\t\tview: this.activeView,\n\t\t\t\tfolder: this.activeFolder,\n\t\t\t\tcontents: [],\n\t\t\t})\n\n\t\t\tif (element) {\n\t\t\t\tthis.$el.replaceChildren(element)\n\t\t\t} else {\n\t\t\t\tthis.$el.replaceChildren()\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n","/*!\n * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { IColumn } from '@nextcloud/files'\nimport type { InjectionKey, Ref } from 'vue'\n\nimport { inject, readonly, ref } from 'vue'\n\n/** Row height of the files list, must match `--row-height` */\nconst ROW_HEIGHT = 44\n/** Margin around cells after the actions cell, must match `--cell-margin` */\nconst CELL_MARGIN = 14\n\n/** Width of the selection checkbox cell */\nconst CHECKBOX_WIDTH = ROW_HEIGHT\n/** Width of the actions menu button */\nconst ACTIONS_MENU_WIDTH = ROW_HEIGHT\n/** Width of an inline action button including its margin */\nexport const INLINE_ACTION_WIDTH = ROW_HEIGHT + CELL_MARGIN\n/** Minimal width reserved for the name column */\nconst MIN_NAME_WIDTH = 200\n\n/**\n * Content widths of the columns, without the surrounding cell margin.\n * Must match the files list styles.\n */\nexport const COLUMN_WIDTHS = {\n\tmime: ROW_HEIGHT * 3.5,\n\tsize: ROW_HEIGHT * 2,\n\tmtime: ROW_HEIGHT * 2.5,\n\tcustom: ROW_HEIGHT * 2.5,\n} as const\n\n/** Built-in columns hidden, first to last, when the name column runs out of room. */\nexport const DROPPABLE_BUILTIN_COLUMNS = ['mime'] as const\n\n/** Built-in columns kept even when the name column is below its minimum width. */\nexport const KEPT_BUILTIN_COLUMNS = ['size', 'mtime'] as const\n\ntype BuiltinColumn = typeof DROPPABLE_BUILTIN_COLUMNS[number] | typeof KEPT_BUILTIN_COLUMNS[number]\n\nexport interface FileListLayout {\n\t/** Whether inline actions are shown, otherwise they are moved to the actions menu */\n\tinlineActions: boolean\n\t/** Whether the mime column is shown */\n\tmime: boolean\n\t/** Whether the size column is shown */\n\tsize: boolean\n\t/** Whether the mtime column is shown */\n\tmtime: boolean\n\t/** The custom view columns to show */\n\tcolumns: IColumn[]\n}\n\nexport const FileListLayoutKey: InjectionKey<Ref<FileListLayout>> = Symbol('fileListLayout')\n\n/** The largest width needed by the inline actions of a row */\nconst inlineActionsWidth = ref(0)\n\n/**\n * Report the width needed by the inline actions of a row\n *\n * @param width - The width needed by the inline actions\n */\nexport function reportInlineActionsWidth(width: number) {\n\tif (width > inlineActionsWidth.value) {\n\t\tinlineActionsWidth.value = width\n\t}\n}\n\n/**\n * Reset the reported inline actions width, e.g. when the shown content changes\n */\nexport function resetInlineActionsWidth() {\n\tinlineActionsWidth.value = 0\n}\n\n/**\n * Get the largest width needed by the inline actions of a row\n */\nexport function useInlineActionsWidth(): Readonly<Ref<number>> {\n\treturn readonly(inlineActionsWidth)\n}\n\n/**\n * Compute which parts of the files list fit into the available width.\n * If space is tight, parts are hidden in this order until the name column\n * has at least `MIN_NAME_WIDTH` space left: inline actions, then\n * `DROPPABLE_BUILTIN_COLUMNS`, then custom view columns (last first).\n * `KEPT_BUILTIN_COLUMNS` stay as available.\n *\n * @param width - The available width of the files list\n * @param available - The parts of the files list available in the current view\n * @param inlineActionsWidth - The width needed by the inline actions\n */\nexport function getFileListLayout(width: number, available: FileListLayout, inlineActionsWidth: number): FileListLayout {\n\tconst layout: FileListLayout = { ...available, columns: [...available.columns] }\n\n\tconst cellWidth = (columnWidth: number) => columnWidth + 2 * CELL_MARGIN\n\tconst builtinColumns: readonly BuiltinColumn[] = [...DROPPABLE_BUILTIN_COLUMNS, ...KEPT_BUILTIN_COLUMNS]\n\tconst getUsedWidth = () => CHECKBOX_WIDTH\n\t\t+ ACTIONS_MENU_WIDTH\n\t\t+ (layout.inlineActions ? inlineActionsWidth : 0)\n\t\t+ builtinColumns.reduce((total, column) => total + (layout[column] ? cellWidth(COLUMN_WIDTHS[column]) : 0), 0)\n\t\t+ layout.columns.length * cellWidth(COLUMN_WIDTHS.custom)\n\n\tconst reductions: (() => void)[] = [\n\t\t() => { layout.inlineActions = false },\n\t\t...DROPPABLE_BUILTIN_COLUMNS.map((column) => () => { layout[column] = false }),\n\t\t...available.columns.map(() => () => { layout.columns.pop() }),\n\t]\n\n\tfor (const reduce of reductions) {\n\t\tif (width - getUsedWidth() >= MIN_NAME_WIDTH) {\n\t\t\tbreak\n\t\t}\n\t\treduce()\n\t}\n\n\treturn layout\n}\n\n/**\n * Get the current layout of the files list as provided by the files list\n */\nexport function useFileListLayout(): Ref<FileListLayout> {\n\treturn inject(FileListLayoutKey, ref({\n\t\tinlineActions: true,\n\t\tmime: false,\n\t\tsize: false,\n\t\tmtime: false,\n\t\tcolumns: [],\n\t}))\n}\n","/**\n * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { IFileAction } from '@nextcloud/files'\n\nimport { defineComponent } from 'vue'\n\nexport default defineComponent({\n\n\tdata() {\n\t\treturn {\n\t\t\topenedSubmenu: null as IFileAction | null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tenabledSubmenuActions(): Record<string, IFileAction[]> {\n\t\t\treturn (this.enabledFileActions as IFileAction[])\n\t\t\t\t.reduce((record, action) => {\n\t\t\t\t\tif (action.parent !== undefined) {\n\t\t\t\t\t\tif (!record[action.parent]) {\n\t\t\t\t\t\t\trecord[action.parent] = []\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\trecord[action.parent]!.push(action)\n\t\t\t\t\t}\n\t\t\t\t\treturn record\n\t\t\t\t}, {} as Record<string, IFileAction[]>)\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Check if a menu is valid, meaning it is\n\t\t * defined and has at least one action\n\t\t *\n\t\t * @param action The action to check\n\t\t */\n\t\tisValidMenu(action: IFileAction): boolean {\n\t\t\treturn this.enabledSubmenuActions[action.id]?.length > 0\n\t\t},\n\n\t\tasync onBackToMenuClick(action: IFileAction | null) {\n\t\t\tif (!action) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.openedSubmenu = null\n\t\t\t// Wait for first render\n\t\t\tawait this.$nextTick()\n\n\t\t\t// Focus the previous menu action button\n\t\t\tthis.$nextTick(() => {\n\t\t\t\t// Focus the action button, test both batch and single action references\n\t\t\t\t// as this mixin is used in both single and batch actions.\n\t\t\t\tconst menuAction = this.$refs[`action-batch-${action.id}`]?.[0]\n\t\t\t\t\t|| this.$refs[`action-${action.id}`]?.[0]\n\t\t\t\tif (menuAction) {\n\t\t\t\t\tmenuAction.$el.querySelector('button')?.focus()\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t},\n})\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<td\n\t\tclass=\"files-list__row-actions\"\n\t\tdata-cy-files-list-row-actions>\n\t\t<!-- Render actions -->\n\t\t<CustomElementRender\n\t\t\tv-for=\"action in enabledRenderActions\"\n\t\t\t:key=\"action.id\"\n\t\t\t:class=\"'files-list__row-action-' + action.id\"\n\t\t\t:activeFolder=\"activeStore.activeFolder\"\n\t\t\t:activeView=\"activeStore.activeView\"\n\t\t\t:render=\"action.renderInline\"\n\t\t\t:source=\"source\"\n\t\t\tclass=\"files-list__row-action--inline\" />\n\n\t\t<!-- Menu actions -->\n\t\t<NcActions\n\t\t\tref=\"actionsMenu\"\n\t\t\t:boundariesElement=\"getBoundariesElement\"\n\t\t\t:container=\"getBoundariesElement\"\n\t\t\t:forceName=\"true\"\n\t\t\tvariant=\"tertiary\"\n\t\t\t:forceMenu=\"enabledInlineActions.length === 0 /* forceMenu only if no inline actions */\"\n\t\t\t:inline=\"enabledInlineActions.length\"\n\t\t\t:open=\"openedMenu\"\n\t\t\t@close=\"onMenuClose\"\n\t\t\t@closed=\"onMenuClosed\">\n\t\t\t<!-- Non-destructive actions list -->\n\t\t\t<!-- Please keep this block in sync with the destructive actions block below -->\n\t\t\t<NcActionButton\n\t\t\t\tv-for=\"action, index in renderedNonDestructiveActions\"\n\t\t\t\t:key=\"action.id\"\n\t\t\t\t:ref=\"`action-${action.id}`\"\n\t\t\t\tclass=\"files-list__row-action\"\n\t\t\t\t:class=\"{\n\t\t\t\t\t[`files-list__row-action-${action.id}`]: true,\n\t\t\t\t\t'files-list__row-action--inline': index < enabledInlineActions.length,\n\t\t\t\t\t'files-list__row-action--menu': isValidMenu(action),\n\t\t\t\t}\"\n\t\t\t\t:closeAfterClick=\"!isValidMenu(action)\"\n\t\t\t\t:data-cy-files-list-row-action=\"action.id\"\n\t\t\t\t:isMenu=\"isValidMenu(action)\"\n\t\t\t\t:aria-label=\"action.title?.(actionContext)\"\n\t\t\t\t:title=\"action.title?.(actionContext)\"\n\t\t\t\t@click=\"onActionClick(action)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NcLoadingIcon v-if=\"isLoadingAction(action)\" />\n\t\t\t\t\t<NcIconSvgWrapper\n\t\t\t\t\t\tv-else\n\t\t\t\t\t\tclass=\"files-list__row-action-icon\"\n\t\t\t\t\t\t:svg=\"action.iconSvgInline(actionContext)\" />\n\t\t\t\t</template>\n\t\t\t\t{{ actionDisplayName(action) }}\n\t\t\t</NcActionButton>\n\n\t\t\t<!-- Destructive actions list -->\n\t\t\t<template v-if=\"renderedDestructiveActions.length > 0\">\n\t\t\t\t<NcActionSeparator />\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-for=\"action, index in renderedDestructiveActions\"\n\t\t\t\t\t:key=\"action.id\"\n\t\t\t\t\t:ref=\"`action-${action.id}`\"\n\t\t\t\t\tclass=\"files-list__row-action files-list__row-action--destructive\"\n\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t[`files-list__row-action-${action.id}`]: true,\n\t\t\t\t\t\t'files-list__row-action--inline': index < enabledInlineActions.length,\n\t\t\t\t\t\t'files-list__row-action--menu': isValidMenu(action),\n\t\t\t\t\t}\"\n\t\t\t\t\t:closeAfterClick=\"!isValidMenu(action)\"\n\t\t\t\t\t:data-cy-files-list-row-action=\"action.id\"\n\t\t\t\t\t:isMenu=\"isValidMenu(action)\"\n\t\t\t\t\t:aria-label=\"action.title?.(actionContext)\"\n\t\t\t\t\t:title=\"action.title?.(actionContext)\"\n\t\t\t\t\t@click=\"onActionClick(action)\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<NcLoadingIcon v-if=\"isLoadingAction(action)\" />\n\t\t\t\t\t\t<NcIconSvgWrapper\n\t\t\t\t\t\t\tv-else\n\t\t\t\t\t\t\tclass=\"files-list__row-action-icon\"\n\t\t\t\t\t\t\t:svg=\"action.iconSvgInline(actionContext)\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ actionDisplayName(action) }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\n\t\t\t<!-- Submenu actions list-->\n\t\t\t<template v-if=\"openedSubmenu && enabledSubmenuActions[openedSubmenu?.id]\">\n\t\t\t\t<!-- Back to top-level button -->\n\t\t\t\t<NcActionButton class=\"files-list__row-action-back\" data-cy-files-list-row-action=\"menu-back\" @click=\"onBackToMenuClick(openedSubmenu)\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ArrowLeftIcon />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ t('files', 'Back') }}\n\t\t\t\t</NcActionButton>\n\t\t\t\t<NcActionSeparator />\n\n\t\t\t\t<!-- Submenu actions -->\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-for=\"action in enabledSubmenuActions[openedSubmenu?.id]\"\n\t\t\t\t\t:key=\"action.id\"\n\t\t\t\t\t:class=\"`files-list__row-action-${action.id}`\"\n\t\t\t\t\tclass=\"files-list__row-action--submenu\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t:data-cy-files-list-row-action=\"action.id\"\n\t\t\t\t\t:aria-label=\"action.title?.(actionContext)\"\n\t\t\t\t\t:title=\"action.title?.(actionContext)\"\n\t\t\t\t\t@click=\"onActionClick(action)\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<NcLoadingIcon v-if=\"isLoadingAction(action)\" />\n\t\t\t\t\t\t<NcIconSvgWrapper v-else :svg=\"action.iconSvgInline(actionContext)\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ actionDisplayName(action) }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\t\t</NcActions>\n\t</td>\n</template>\n\n<script lang=\"ts\">\nimport type { ActionContextSingle, IFileAction, Node } from '@nextcloud/files'\nimport type { PropType } from 'vue'\n\nimport { DefaultType, NodeStatus } from '@nextcloud/files'\nimport { t } from '@nextcloud/l10n'\nimport { useHotKey } from '@nextcloud/vue/composables/useHotKey'\nimport { defineComponent, inject } from 'vue'\nimport NcActionButton from '@nextcloud/vue/components/NcActionButton'\nimport NcActions from '@nextcloud/vue/components/NcActions'\nimport NcActionSeparator from '@nextcloud/vue/components/NcActionSeparator'\nimport NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'\nimport NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'\nimport ArrowLeftIcon from 'vue-material-design-icons/ArrowLeft.vue'\nimport CustomElementRender from '../CustomElementRender.vue'\nimport { INLINE_ACTION_WIDTH, reportInlineActionsWidth, useFileListLayout } from '../../composables/useFileListLayout.ts'\nimport actionsMixins from '../../mixins/actionsMixin.ts'\nimport { useActiveStore } from '../../store/active.ts'\nimport { executeAction } from '../../utils/actionUtils.ts'\nimport { logger } from '../../utils/logger.ts'\n\nexport default defineComponent({\n\tname: 'FileEntryActions',\n\n\tcomponents: {\n\t\tArrowLeftIcon,\n\t\tCustomElementRender,\n\t\tNcActionButton,\n\t\tNcActions,\n\t\tNcActionSeparator,\n\t\tNcIconSvgWrapper,\n\t\tNcLoadingIcon,\n\t},\n\n\tmixins: [actionsMixins],\n\n\tprops: {\n\t\topened: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\tsource: {\n\t\t\ttype: Object as PropType<Node>,\n\t\t\trequired: true,\n\t\t},\n\n\t\tgridMode: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\temits: ['update:opened'],\n\n\tsetup() {\n\t\t// The file list is guaranteed to be shown with active view - thus we can set the `loaded` flag\n\t\tconst activeStore = useActiveStore()\n\t\tconst layout = useFileListLayout()\n\t\tconst enabledFileActions = inject<IFileAction[]>('enabledFileActions', [])\n\t\treturn {\n\t\t\tactiveStore,\n\t\t\tenabledFileActions,\n\t\t\tlayout,\n\t\t\tt,\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tresizeObserver: null as ResizeObserver | null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tisActive() {\n\t\t\treturn this.activeStore.activeNode?.source === this.source.source\n\t\t},\n\n\t\tisLoading() {\n\t\t\treturn this.source.status === NodeStatus.LOADING\n\t\t},\n\n\t\tactionContext(): ActionContextSingle {\n\t\t\treturn {\n\t\t\t\tnodes: [this.source],\n\t\t\t\tview: this.activeStore.activeView!,\n\t\t\t\tfolder: this.activeStore.activeFolder!,\n\t\t\t\tcontents: [],\n\t\t\t}\n\t\t},\n\n\t\t// Enabled actions that can be displayed inline\n\t\tinlineCapableActions() {\n\t\t\tif (this.gridMode) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.enabledFileActions.filter((action) => {\n\t\t\t\ttry {\n\t\t\t\t\treturn action?.inline?.(this.actionContext) === true\n\t\t\t\t} catch (error) {\n\t\t\t\t\tlogger.error('Error while checking if action is inline', { action, error })\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\n\t\t// Enabled actions that are displayed inline\n\t\tenabledInlineActions() {\n\t\t\tif (!this.layout.inlineActions) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.inlineCapableActions\n\t\t},\n\n\t\t// Enabled action that are displayed inline with a custom render function\n\t\tenabledRenderActions() {\n\t\t\tif (!this.layout.inlineActions || this.gridMode) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.enabledFileActions.filter((action) => typeof action.renderInline === 'function')\n\t\t},\n\n\t\t// Actions shown in the menu\n\t\tenabledMenuActions() {\n\t\t\t// If we're in a submenu, only render the inline\n\t\t\t// actions before the filtered submenu\n\t\t\tif (this.openedSubmenu) {\n\t\t\t\treturn this.enabledInlineActions\n\t\t\t}\n\n\t\t\tconst actions = [\n\t\t\t\t// Showing inline first for the NcActions inline prop\n\t\t\t\t...this.enabledInlineActions,\n\t\t\t\t// Then the rest\n\t\t\t\t...this.enabledFileActions.filter((action) => action.default !== DefaultType.HIDDEN && typeof action.renderInline !== 'function'),\n\t\t\t].filter((value, index, self) => {\n\t\t\t\t// Then we filter duplicates to prevent inline actions to be shown twice\n\t\t\t\treturn index === self.findIndex((action) => action.id === value.id)\n\t\t\t})\n\n\t\t\t// Generate list of all top-level actions ids\n\t\t\tconst topActionsIds = actions.filter((action) => !action.parent).map((action) => action.id) as string[]\n\n\t\t\t// Filter actions that are not top-level AND have a valid parent\n\t\t\treturn actions.filter((action) => !(action.parent && topActionsIds.includes(action.parent)))\n\t\t},\n\n\t\trenderedNonDestructiveActions() {\n\t\t\treturn this.enabledMenuActions.filter((action) => !action.destructive)\n\t\t},\n\n\t\trenderedDestructiveActions() {\n\t\t\treturn this.enabledMenuActions.filter((action) => action.destructive)\n\t\t},\n\n\t\topenedMenu: {\n\t\t\tget() {\n\t\t\t\treturn this.opened\n\t\t\t},\n\n\t\t\tset(value) {\n\t\t\t\tthis.$emit('update:opened', value)\n\t\t\t},\n\t\t},\n\n\t\t/**\n\t\t * Making this a function in case the files-list\n\t\t * reference changes in the future. That way we're\n\t\t * sure there is one at the time we call it.\n\t\t */\n\t\tgetBoundariesElement() {\n\t\t\treturn document.querySelector('.app-content > .files-list')\n\t\t},\n\t},\n\n\twatch: {\n\t\t// Close any submenu when the menu state changes\n\t\topenedMenu() {\n\t\t\tthis.openedSubmenu = null\n\t\t},\n\n\t\tinlineCapableActions() {\n\t\t\tthis.reportInlineActionsWidth()\n\t\t},\n\t},\n\n\tcreated() {\n\t\tuseHotKey('Escape', this.onKeyDown, {\n\t\t\tstop: true,\n\t\t\tprevent: true,\n\t\t})\n\n\t\tuseHotKey('a', this.onKeyDown, {\n\t\t\tstop: true,\n\t\t\tprevent: true,\n\t\t})\n\t},\n\n\tmounted() {\n\t\tif (this.gridMode) {\n\t\t\treturn\n\t\t}\n\t\t// Inline rendered actions (e.g. tags) are rendered asynchronously and change the cell size\n\t\tthis.resizeObserver = new ResizeObserver(() => this.reportInlineActionsWidth())\n\t\tthis.resizeObserver.observe(this.$el)\n\t\tthis.reportInlineActionsWidth()\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.resizeObserver?.disconnect()\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Report the width needed by the inline actions of this row to the files list layout\n\t\t */\n\t\treportInlineActionsWidth() {\n\t\t\tif (this.gridMode || !this.$el) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst renderActionsWidth = [...this.$el.querySelectorAll<HTMLElement>(':scope > .files-list__row-action--inline')]\n\t\t\t\t.filter((element) => element.offsetWidth > 0)\n\t\t\t\t.reduce((total, element) => total + element.offsetWidth + parseFloat(getComputedStyle(element).marginInlineEnd || '0'), 0)\n\n\t\t\treportInlineActionsWidth(this.inlineCapableActions.length * INLINE_ACTION_WIDTH + renderActionsWidth)\n\t\t},\n\n\t\tactionDisplayName(action: IFileAction) {\n\t\t\ttry {\n\t\t\t\tif ((this.gridMode || (!this.layout.inlineActions && action.inline)) && typeof action.title === 'function') {\n\t\t\t\t\t// if an inline action is rendered in the menu for\n\t\t\t\t\t// lack of space we use the title first if defined\n\t\t\t\t\tconst title = action.title(this.actionContext)\n\t\t\t\t\tif (title) {\n\t\t\t\t\t\treturn title\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn action.displayName(this.actionContext)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error('Error while getting action display name', { action, error })\n\t\t\t\t// Not ideal, but better than nothing\n\t\t\t\treturn action.id\n\t\t\t}\n\t\t},\n\n\t\tisLoadingAction(action: IFileAction) {\n\t\t\tif (!this.isActive) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\treturn this.activeStore.activeAction?.id === action.id\n\t\t},\n\n\t\tasync onActionClick(action) {\n\t\t\t// If the action is a submenu, we open it\n\t\t\tif (this.enabledSubmenuActions[action.id]) {\n\t\t\t\tthis.openedSubmenu = action\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Make sure we set the node as active\n\t\t\tthis.activeStore.activeNode = this.source\n\n\t\t\t// Execute the action\n\t\t\tawait executeAction(action, 'menu')\n\t\t},\n\n\t\tonKeyDown(event: KeyboardEvent) {\n\t\t\t// Don't react to the event if the file row is not active\n\t\t\tif (!this.isActive) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// ESC close the action menu if opened\n\t\t\tif (event.key === 'Escape' && this.openedMenu) {\n\t\t\t\tthis.openedMenu = false\n\t\t\t}\n\n\t\t\t// a open the action menu\n\t\t\tif (event.key === 'a' && !this.openedMenu) {\n\t\t\t\tthis.openedMenu = true\n\t\t\t}\n\t\t},\n\n\t\tonMenuClose() {\n\t\t\t// We reset the submenu state when the menu is closing\n\t\t\tthis.openedSubmenu = null\n\t\t},\n\n\t\tonMenuClosed() {\n\t\t\t// We reset the actions menu state when the menu is finally closed\n\t\t\tthis.openedMenu = false\n\t\t},\n\t},\n})\n</script>\n\n<style lang=\"scss\">\n// Allow right click to define the position of the menu\n// only if defined\nmain.app-content[style*=\"mouse-pos-x\"] .v-popper__popper {\n\ttransform: translate3d(var(--mouse-pos-x), var(--mouse-pos-y), 0px) !important;\n\n\t// If the menu is too close to the bottom, we move it up\n\t&[data-popper-placement=\"top\"] {\n\t\t// 34px added to align with the top of the cursor\n\t\ttransform: translate3d(var(--mouse-pos-x), calc(var(--mouse-pos-y) - 50vh + 34px), 0px) !important;\n\t}\n\t// Hide arrow if floating\n\t.v-popper__arrow-container {\n\t\tdisplay: none;\n\t}\n}\n</style>\n\n<style scoped lang=\"scss\">\n.files-list__row-action {\n\t--max-icon-size: calc(var(--default-clickable-area) - 2 * var(--default-grid-baseline));\n\n\t// inline icons can have clickable area size so they still fit into the row\n\t&.files-list__row-action--inline {\n\t\t--max-icon-size: var(--default-clickable-area);\n\t}\n\n\t// Some icons exceed the default size so we need to enforce a max width and height\n\t.files-list__row-action-icon :deep(svg) {\n\t\tmax-height: var(--max-icon-size) !important;\n\t\tmax-width: var(--max-icon-size) !important;\n\t}\n\n\t&.files-list__row-action--destructive {\n\t\t::deep(button) {\n\t\t\tcolor: var(--color-text-error) !important;\n\t\t}\n\t}\n}\n\n</style>\n","/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\nimport { defineStore } from 'pinia'\n\n/**\n * Observe various events and save the current\n * special keys states. Useful for checking the\n * current status of a key when executing a method.\n *\n * @param args\n */\nexport function useKeyboardStore(...args) {\n\tconst store = defineStore('keyboard', {\n\t\tstate: () => ({\n\t\t\taltKey: false,\n\t\t\tctrlKey: false,\n\t\t\tmetaKey: false,\n\t\t\tshiftKey: false,\n\t\t}),\n\n\t\tactions: {\n\t\t\tonEvent(event: MouseEvent | KeyboardEvent) {\n\t\t\t\tif (!event) {\n\t\t\t\t\tevent = window.event as MouseEvent | KeyboardEvent\n\t\t\t\t}\n\t\t\t\tthis.altKey = !!event.altKey\n\t\t\t\tthis.ctrlKey = !!event.ctrlKey\n\t\t\t\tthis.metaKey = !!event.metaKey\n\t\t\t\tthis.shiftKey = !!event.shiftKey\n\t\t\t},\n\t\t},\n\t})\n\n\tconst keyboardStore = store(...args)\n\t// Make sure we only register the listeners once\n\tif (!keyboardStore._initialized) {\n\t\twindow.addEventListener('keydown', keyboardStore.onEvent)\n\t\twindow.addEventListener('keyup', keyboardStore.onEvent)\n\t\twindow.addEventListener('mousemove', keyboardStore.onEvent)\n\n\t\tkeyboardStore._initialized = true\n\t}\n\n\treturn keyboardStore\n}\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<td\n\t\tclass=\"files-list__row-checkbox\"\n\t\t@keyup.esc.exact=\"resetSelection\">\n\t\t<NcLoadingIcon v-if=\"isLoading\" :name=\"loadingLabel\" />\n\t\t<NcCheckboxRadioSwitch\n\t\t\tv-else\n\t\t\t:aria-label=\"ariaLabel\"\n\t\t\t:modelValue=\"isSelected\"\n\t\t\tdata-cy-files-list-row-checkbox\n\t\t\t@update:modelValue=\"onSelectionChange\" />\n\t</td>\n</template>\n\n<script lang=\"ts\">\nimport type { Node } from '@nextcloud/files'\nimport type { PropType } from 'vue'\nimport type { FileSource } from '../../types.ts'\n\nimport { FileType } from '@nextcloud/files'\nimport { translate as t } from '@nextcloud/l10n'\nimport { useHotKey } from '@nextcloud/vue/composables/useHotKey'\nimport { defineComponent } from 'vue'\nimport NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'\nimport NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'\nimport { useActiveStore } from '../../store/active.ts'\nimport { useKeyboardStore } from '../../store/keyboard.ts'\nimport { useSelectionStore } from '../../store/selection.ts'\nimport { logger } from '../../utils/logger.ts'\n\nexport default defineComponent({\n\tname: 'FileEntryCheckbox',\n\n\tcomponents: {\n\t\tNcCheckboxRadioSwitch,\n\t\tNcLoadingIcon,\n\t},\n\n\tprops: {\n\t\tfileid: {\n\t\t\ttype: Number,\n\t\t\trequired: true,\n\t\t},\n\n\t\tisLoading: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\tnodes: {\n\t\t\ttype: Array as PropType<Node[]>,\n\t\t\trequired: true,\n\t\t},\n\n\t\tsource: {\n\t\t\ttype: Object as PropType<Node>,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\tsetup() {\n\t\tconst selectionStore = useSelectionStore()\n\t\tconst keyboardStore = useKeyboardStore()\n\t\tconst activeStore = useActiveStore()\n\n\t\treturn {\n\t\t\tactiveStore,\n\t\t\tkeyboardStore,\n\t\t\tselectionStore,\n\t\t\tt,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tisActive() {\n\t\t\treturn this.activeStore.activeNode?.source === this.source.source\n\t\t},\n\n\t\tselectedFiles() {\n\t\t\treturn this.selectionStore.selected\n\t\t},\n\n\t\tisSelected() {\n\t\t\treturn this.selectedFiles.includes(this.source.source)\n\t\t},\n\n\t\tindex() {\n\t\t\treturn this.nodes.findIndex((node: Node) => node.source === this.source.source)\n\t\t},\n\n\t\tisFile() {\n\t\t\treturn this.source.type === FileType.File\n\t\t},\n\n\t\tariaLabel() {\n\t\t\treturn this.isFile\n\t\t\t\t? t('files', 'Toggle selection for file \"{displayName}\"', { displayName: this.source.basename })\n\t\t\t\t: t('files', 'Toggle selection for folder \"{displayName}\"', { displayName: this.source.basename })\n\t\t},\n\n\t\tloadingLabel() {\n\t\t\treturn this.isFile\n\t\t\t\t? t('files', 'File is loading')\n\t\t\t\t: t('files', 'Folder is loading')\n\t\t},\n\t},\n\n\tcreated() {\n\t\t// ctrl+space toggle selection\n\t\tuseHotKey(' ', this.onToggleSelect, {\n\t\t\tstop: true,\n\t\t\tprevent: true,\n\t\t\tctrl: true,\n\t\t})\n\n\t\t// ctrl+shift+space toggle range selection\n\t\tuseHotKey(' ', this.onToggleSelect, {\n\t\t\tstop: true,\n\t\t\tprevent: true,\n\t\t\tctrl: true,\n\t\t\tshift: true,\n\t\t})\n\t},\n\n\tmethods: {\n\t\tonSelectionChange(selected: boolean) {\n\t\t\tconst newSelectedIndex = this.index\n\t\t\tconst lastSelectedIndex = this.selectionStore.lastSelectedIndex\n\n\t\t\t// Get the last selected and select all files in between\n\t\t\tif (this.keyboardStore?.shiftKey && lastSelectedIndex !== null) {\n\t\t\t\tconst isAlreadySelected = this.selectedFiles.includes(this.source.source)\n\n\t\t\t\tconst start = Math.min(newSelectedIndex, lastSelectedIndex)\n\t\t\t\tconst end = Math.max(lastSelectedIndex, newSelectedIndex)\n\n\t\t\t\tconst lastSelection = this.selectionStore.lastSelection\n\t\t\t\tconst filesToSelect = this.nodes\n\t\t\t\t\t.map((file) => file.source)\n\t\t\t\t\t.slice(start, end + 1)\n\t\t\t\t\t.filter(Boolean) as FileSource[]\n\n\t\t\t\t// If already selected, update the new selection _without_ the current file\n\t\t\t\tconst selection = [...lastSelection, ...filesToSelect]\n\t\t\t\t\t.filter((source) => !isAlreadySelected || source !== this.source.source)\n\n\t\t\t\tlogger.debug('Shift key pressed, selecting all files in between', { start, end, filesToSelect, isAlreadySelected })\n\t\t\t\t// Keep previous lastSelectedIndex to be use for further shift selections\n\t\t\t\tthis.selectionStore.set(selection)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst selection = selected\n\t\t\t\t? [...this.selectedFiles, this.source.source]\n\t\t\t\t: this.selectedFiles.filter((source) => source !== this.source.source)\n\n\t\t\tlogger.debug('Updating selection', { selection })\n\t\t\tthis.selectionStore.set(selection)\n\t\t\tthis.selectionStore.setLastIndex(newSelectedIndex)\n\t\t},\n\n\t\tresetSelection() {\n\t\t\tthis.selectionStore.reset()\n\t\t},\n\n\t\tonToggleSelect() {\n\t\t\t// Don't react if the node is not active\n\t\t\tif (!this.isActive) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlogger.debug('Toggling selection for file', { source: this.source })\n\t\t\tthis.onSelectionChange(!this.isSelected)\n\t\t},\n\t},\n})\n</script>\n","/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { INode } from '@nextcloud/files'\n\nimport axios, { isAxiosError } from '@nextcloud/axios'\nimport { emit, subscribe } from '@nextcloud/event-bus'\nimport { FileType, NodeStatus } from '@nextcloud/files'\nimport { t } from '@nextcloud/l10n'\nimport { basename, dirname, extname } from '@nextcloud/paths'\nimport { spawnDialog } from '@nextcloud/vue/functions/dialog'\nimport { defineStore } from 'pinia'\nimport { defineAsyncComponent, ref } from 'vue'\nimport { fetchNode } from '../services/WebdavClient.ts'\nimport { logger } from '../utils/logger.ts'\nimport { useUserConfigStore } from './userconfig.ts'\n\nexport const useRenamingStore = defineStore('renaming', () => {\n\t/**\n\t * The currently renamed node\n\t */\n\tconst renamingNode = ref<INode>()\n\t/**\n\t * The new name of the currently renamed node\n\t */\n\tconst newNodeName = ref('')\n\n\t/**\n\t * Internal flag to only allow calling `rename` once.\n\t */\n\tconst isRenaming = ref(false)\n\n\t/**\n\t * Execute the renaming.\n\t * This will rename the node set as `renamingNode` to the configured new name `newName`.\n\t *\n\t * @return true if success, false if skipped (e.g. new and old name are the same)\n\t * @throws {Error} if renaming fails, details are set in the error message\n\t */\n\tasync function rename(): Promise<boolean> {\n\t\tif (renamingNode.value === undefined) {\n\t\t\tthrow new Error('No node is currently being renamed')\n\t\t}\n\n\t\tconst oldName = renamingNode.value.basename\n\t\tlet newName = newNodeName.value.trim()\n\t\tif (newName === oldName) {\n\t\t\treturn false\n\t\t}\n\n\t\t// Only rename once so we use this as some kind of mutex\n\t\tif (isRenaming.value) {\n\t\t\treturn false\n\t\t}\n\t\tisRenaming.value = true\n\n\t\tconst userConfig = useUserConfigStore()\n\t\tlet node = renamingNode.value\n\t\tnode.status = NodeStatus.LOADING\n\t\ttry {\n\t\t\tif (userConfig.userConfig.show_dialog_file_extension) {\n\t\t\t\tconst oldExtension = extname(oldName)\n\t\t\t\tconst newExtension = extname(newName)\n\t\t\t\t// Check for extension change for files\n\t\t\t\tif (node.type === FileType.File\n\t\t\t\t\t&& oldExtension !== newExtension\n\t\t\t\t\t&& !(await showFileExtensionDialog(oldExtension, newExtension))) {\n\t\t\t\t\t// user selected to use the old extension\n\t\t\t\t\tnewName = basename(newName, newExtension) + oldExtension\n\t\t\t\t\tif (oldName === newName) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (!userConfig.userConfig.show_hidden\n\t\t\t\t\t&& newName.startsWith('.')\n\t\t\t\t\t&& !oldName.startsWith('.')\n\t\t\t\t\t&& !(await showHiddenFileDialog(newName))) {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst oldEncodedSource = node.encodedSource\n\t\t\t// rename the node\n\t\t\tnode.rename(newName)\n\t\t\tlogger.debug('Moving file to', { destination: node.encodedSource, oldEncodedSource })\n\t\t\t// create MOVE request\n\t\t\tawait axios({\n\t\t\t\tmethod: 'MOVE',\n\t\t\t\turl: oldEncodedSource,\n\t\t\t\theaders: {\n\t\t\t\t\tDestination: node.encodedSource,\n\t\t\t\t\tOverwrite: 'F',\n\t\t\t\t},\n\t\t\t})\n\n\t\t\t// Update mime type if extension changed\n\t\t\t// as other related informations might have changed\n\t\t\t// on the backend but it is really hard to know on the front\n\t\t\tif (extname(oldName) !== extname(newName)) {\n\t\t\t\tnode = await fetchNode(node.path)\n\t\t\t}\n\n\t\t\t// Success 🎉\n\t\t\temit('files:node:updated', node)\n\t\t\temit('files:node:renamed', node)\n\t\t\temit('files:node:moved', {\n\t\t\t\tnode,\n\t\t\t\toldSource: `${dirname(node.source)}/${oldName}`,\n\t\t\t})\n\n\t\t\t// Reset the state not changed\n\t\t\tif (renamingNode.value === node) {\n\t\t\t\t$reset()\n\t\t\t}\n\n\t\t\treturn true\n\t\t} catch (error) {\n\t\t\tlogger.error('Error while renaming file', { error })\n\t\t\t// Rename back as it failed\n\t\t\tnode.rename(oldName)\n\t\t\tif (isAxiosError(error)) {\n\t\t\t\t// TODO: 409 means current folder does not exist, redirect ?\n\t\t\t\tif (error?.response?.status === 404) {\n\t\t\t\t\tthrow new Error(t('files', 'Could not rename \"{oldName}\", it does not exist any more', { oldName }), { cause: error })\n\t\t\t\t} else if (error?.response?.status === 412) {\n\t\t\t\t\tthrow new Error(t(\n\t\t\t\t\t\t'files',\n\t\t\t\t\t\t'The name \"{newName}\" is already used in the folder \"{dir}\". Please choose a different name.',\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tnewName,\n\t\t\t\t\t\t\tdir: basename(renamingNode.value!.dirname),\n\t\t\t\t\t\t},\n\t\t\t\t\t), { cause: error })\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Unknown error\n\t\t\tthrow new Error(t('files', 'Could not rename \"{oldName}\"', { oldName }), { cause: error })\n\t\t} finally {\n\t\t\tnode.status = undefined\n\t\t\tisRenaming.value = false\n\t\t}\n\t}\n\n\t/**\n\t * Reset the store state\n\t */\n\tfunction $reset(): void {\n\t\tnewNodeName.value = ''\n\t\trenamingNode.value = undefined\n\t}\n\n\t// Make sure we only register the listeners once\n\tsubscribe('files:node:rename', (node: INode) => {\n\t\trenamingNode.value = node\n\t\tnewNodeName.value = node.basename\n\t})\n\n\treturn {\n\t\t$reset,\n\n\t\tnewNodeName,\n\t\trename,\n\t\trenamingNode,\n\t}\n})\n\n/**\n * Show a dialog asking user for confirmation about changing the file extension.\n *\n * @param oldExtension the old file name extension\n * @param newExtension the new file name extension\n */\nasync function showFileExtensionDialog(oldExtension: string, newExtension: string): Promise<boolean> {\n\treturn await spawnDialog(\n\t\tdefineAsyncComponent(() => import('../views/DialogConfirmFileExtension.vue')),\n\t\t{ oldExtension, newExtension },\n\t)\n}\n\n/**\n * Show a dialog asking user for confirmation about renaming a file to a hidden file.\n *\n * @param filename - The new filename\n */\nasync function showHiddenFileDialog(filename: string): Promise<boolean> {\n\treturn await spawnDialog(\n\t\tdefineAsyncComponent(() => import('../views/DialogConfirmFileHidden.vue')),\n\t\t{ filename },\n\t)\n}\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<!-- Rename input -->\n\t<form\n\t\tv-if=\"isRenaming\"\n\t\tref=\"renameForm\"\n\t\tv-on-click-outside=\"onRename\"\n\t\t:aria-label=\"t('files', 'Rename file')\"\n\t\tclass=\"files-list__row-rename\"\n\t\t@submit.prevent.stop=\"onRename\">\n\t\t<NcTextField\n\t\t\tref=\"renameInput\"\n\t\t\tv-model=\"newName\"\n\t\t\t:label=\"renameLabel\"\n\t\t\tautofocus\n\t\t\trequired\n\t\t\t:minlength=\"1\"\n\t\t\tenterkeyhint=\"done\"\n\t\t\t@keyup.esc=\"stopRenaming\" />\n\t</form>\n\n\t<component\n\t\t:is=\"linkTo.is\"\n\t\tv-else\n\t\tref=\"basename\"\n\t\tclass=\"files-list__row-name-link\"\n\t\tdata-cy-files-list-row-name-link\n\t\tv-bind=\"linkTo.params\">\n\t\t<!-- Filename -->\n\t\t<span class=\"files-list__row-name-text\" dir=\"auto\">\n\t\t\t<!-- Keep the filename stuck to the extension to avoid whitespace rendering issues-->\n\t\t\t<span class=\"files-list__row-name-\" v-text=\"basename\" />\n\t\t\t<span v-if=\"userConfigStore.userConfig.show_files_extensions\" class=\"files-list__row-name-ext\" v-text=\"extension\" />\n\t\t</span>\n\t</component>\n</template>\n\n<script lang=\"ts\">\nimport type { IFileAction, Node, TFileType } from '@nextcloud/files'\nimport type { ComponentPublicInstance, PropType } from 'vue'\n\nimport { showError } from '@nextcloud/dialogs'\nimport { FileType, NodeStatus } from '@nextcloud/files'\nimport { translate as t } from '@nextcloud/l10n'\nimport { basename } from '@nextcloud/paths'\nimport { vOnClickOutside } from '@vueuse/components'\nimport { defineComponent, inject } from 'vue'\nimport NcTextField from '@nextcloud/vue/components/NcTextField'\nimport { useFileListWidth } from '../../composables/useFileListWidth.ts'\nimport { useActiveStore } from '../../store/active.ts'\nimport { useRenamingStore } from '../../store/renaming.ts'\nimport { useUserConfigStore } from '../../store/userconfig.ts'\nimport { getFilenameValidity } from '../../utils/filenameValidity.ts'\nimport { logger } from '../../utils/logger.ts'\n\nexport default defineComponent({\n\tname: 'FileEntryName',\n\n\tcomponents: {\n\t\tNcTextField,\n\t},\n\n\tdirectives: {\n\t\tonClickOutside: vOnClickOutside,\n\t},\n\n\tprops: {\n\t\t/**\n\t\t * The filename without extension\n\t\t */\n\t\tbasename: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\t/**\n\t\t * The extension of the filename\n\t\t */\n\t\textension: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\n\t\tsource: {\n\t\t\ttype: Object as PropType<Node>,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\tsetup() {\n\t\t// The file list is guaranteed to be only shown with active view - thus we can set the `loaded` flag\n\t\tconst { isNarrow } = useFileListWidth()\n\t\tconst renamingStore = useRenamingStore()\n\t\tconst userConfigStore = useUserConfigStore()\n\t\tconst { activeFolder, activeView } = useActiveStore()\n\n\t\tconst defaultFileAction = inject<IFileAction | undefined>('defaultFileAction')\n\n\t\treturn {\n\t\t\tactiveFolder,\n\t\t\tactiveView,\n\t\t\tdefaultFileAction,\n\t\t\tisNarrow,\n\t\t\trenamingStore,\n\t\t\tuserConfigStore,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tisRenaming() {\n\t\t\treturn this.renamingStore.renamingNode === this.source\n\t\t},\n\n\t\tisRenamingSmallScreen() {\n\t\t\treturn this.isRenaming && this.isNarrow\n\t\t},\n\n\t\tnewName: {\n\t\t\tget(): string {\n\t\t\t\treturn this.renamingStore.newNodeName\n\t\t\t},\n\n\t\t\tset(newName: string) {\n\t\t\t\tthis.renamingStore.newNodeName = newName\n\t\t\t},\n\t\t},\n\n\t\trenameLabel() {\n\t\t\tconst matchLabel: Record<TFileType, string> = {\n\t\t\t\t[FileType.File]: t('files', 'Filename'),\n\t\t\t\t[FileType.Folder]: t('files', 'Folder name'),\n\t\t\t}\n\t\t\treturn matchLabel[this.source.type]\n\t\t},\n\n\t\tlinkTo() {\n\t\t\tif (this.source.status === NodeStatus.FAILED) {\n\t\t\t\treturn {\n\t\t\t\t\tis: 'span',\n\t\t\t\t\tparams: {\n\t\t\t\t\t\ttitle: t('files', '{displayName} (unavailable)', { displayName: this.source.displayname }),\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.defaultFileAction) {\n\t\t\t\tconst displayName = this.defaultFileAction.displayName({\n\t\t\t\t\tnodes: [this.source],\n\t\t\t\t\tview: this.activeView!,\n\t\t\t\t\tfolder: this.activeFolder!,\n\t\t\t\t\tcontents: [],\n\t\t\t\t})\n\t\t\t\tconst accessibleName = `${displayName}: ${this.source.displayname}`\n\t\t\t\treturn {\n\t\t\t\t\tis: 'button',\n\t\t\t\t\tparams: {\n\t\t\t\t\t\t'aria-label': accessibleName,\n\t\t\t\t\t\ttitle: accessibleName,\n\t\t\t\t\t\ttabindex: '0',\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// nothing interactive here, there is no default action\n\t\t\t// so if not even the download action works we only can show the list entry\n\t\t\treturn {\n\t\t\t\tis: 'span',\n\t\t\t}\n\t\t},\n\t},\n\n\twatch: {\n\t\t/**\n\t\t * If renaming starts, select the filename\n\t\t * in the input, without the extension.\n\t\t *\n\t\t * @param renaming\n\t\t */\n\t\tisRenaming: {\n\t\t\timmediate: true,\n\t\t\thandler(renaming: boolean) {\n\t\t\t\tif (renaming) {\n\t\t\t\t\tthis.startRenaming()\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\n\t\tnewName() {\n\t\t\t// Check validity of the new name\n\t\t\tconst newName = this.newName.trim?.() || ''\n\t\t\tconst input = (this.$refs.renameInput as ComponentPublicInstance | undefined)?.$el.querySelector('input')\n\t\t\tif (!input) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlet validity = getFilenameValidity(newName, false, this.source.type === FileType.Folder)\n\t\t\t// Checking if already exists\n\t\t\tif (validity === '' && this.checkIfNodeExists(newName)) {\n\t\t\t\tvalidity = t('files', 'Another entry with the same name already exists.')\n\t\t\t}\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tif (this.isRenaming) {\n\t\t\t\t\tinput.setCustomValidity(validity)\n\t\t\t\t\tinput.reportValidity()\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t},\n\n\tmethods: {\n\t\tcheckIfNodeExists(name: string) {\n\t\t\tconst sources: string[] = (this.activeFolder as { _children?: string[] })?._children || []\n\t\t\treturn sources.some((sourceName) => basename(sourceName) === name)\n\t\t},\n\n\t\tstartRenaming() {\n\t\t\tthis.$nextTick(() => {\n\t\t\t\t// Using split to get the true string length\n\t\t\t\tconst input = (this.$refs.renameInput as ComponentPublicInstance | undefined)?.$el.querySelector('input')\n\t\t\t\tif (!input) {\n\t\t\t\t\tlogger.error('Could not find the rename input')\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tinput.focus()\n\t\t\t\tconst length = this.source.basename.length - (this.source.extension ?? '').length\n\t\t\t\tinput.setSelectionRange(0, length)\n\n\t\t\t\t// Trigger a keyup event to update the input validity\n\t\t\t\tinput.dispatchEvent(new Event('keyup'))\n\t\t\t})\n\t\t},\n\n\t\tstopRenaming() {\n\t\t\tif (!this.isRenaming) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Reset the renaming store\n\t\t\tthis.renamingStore.$reset()\n\t\t},\n\n\t\t// Rename and move the file\n\t\tasync onRename() {\n\t\t\tconst newName = this.newName.trim?.() || ''\n\t\t\tconst form = this.$refs.renameForm as HTMLFormElement\n\t\t\tif (!form.checkValidity()) {\n\t\t\t\tshowError(t('files', 'Invalid filename.') + ' ' + getFilenameValidity(newName))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst oldName = this.source.basename\n\t\t\tif (newName === oldName) {\n\t\t\t\tthis.stopRenaming()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst status = await this.renamingStore.rename()\n\t\t\t\tif (status) {\n\t\t\t\t\tthis.$nextTick(() => {\n\t\t\t\t\t\tconst nameContainer = this.$refs.basename as HTMLElement | undefined\n\t\t\t\t\t\tnameContainer?.focus()\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\t// Was cancelled - meaning the renaming state is just reset\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error(error as Error)\n\t\t\t\tshowError((error as Error).message)\n\t\t\t\t// And ensure we reset to the renaming state\n\t\t\t\tthis.startRenaming()\n\t\t\t}\n\t\t},\n\n\t\tt,\n\t},\n})\n</script>\n\n<style scoped lang=\"scss\">\nbutton.files-list__row-name-link {\n\tbackground-color: unset;\n\tborder: none;\n\tfont-weight: normal;\n\n\t&:active {\n\t\t// No active styles - handled by the row entry\n\t\tbackground-color: unset !important;\n\t}\n}\n</style>\n","<!--\n - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n -->\n<template>\n\t<span\n\t\t:aria-hidden=\"!title\"\n\t\t:aria-label=\"title\"\n\t\tclass=\"material-design-icon collectives-icon\"\n\t\trole=\"img\"\n\t\tv-bind=\"$attrs\"\n\t\t@click=\"$emit('click', $event)\">\n\t\t<svg\n\t\t\t:fill=\"fillColor\"\n\t\t\tclass=\"material-design-icon__svg\"\n\t\t\t:width=\"size\"\n\t\t\t:height=\"size\"\n\t\t\tviewBox=\"0 0 16 16\">\n\t\t\t<path d=\"M2.9,8.8c0-1.2,0.4-2.4,1.2-3.3L0.3,6c-0.2,0-0.3,0.3-0.1,0.4l2.7,2.6C2.9,9,2.9,8.9,2.9,8.8z\" />\n\t\t\t<path d=\"M8,3.7c0.7,0,1.3,0.1,1.9,0.4L8.2,0.6c-0.1-0.2-0.3-0.2-0.4,0L6.1,4C6.7,3.8,7.3,3.7,8,3.7z\" />\n\t\t\t<path d=\"M3.7,11.5L3,15.2c0,0.2,0.2,0.4,0.4,0.3l3.3-1.7C5.4,13.4,4.4,12.6,3.7,11.5z\" />\n\t\t\t<path d=\"M15.7,6l-3.7-0.5c0.7,0.9,1.2,2,1.2,3.3c0,0.1,0,0.2,0,0.3l2.7-2.6C15.9,6.3,15.9,6.1,15.7,6z\" />\n\t\t\t<path d=\"M12.3,11.5c-0.7,1.1-1.8,1.9-3,2.2l3.3,1.7c0.2,0.1,0.4-0.1,0.4-0.3L12.3,11.5z\" />\n\t\t\t<path d=\"M9.6,10.1c-0.4,0.5-1,0.8-1.6,0.8c-1.1,0-2-0.9-2.1-2C5.9,7.7,6.8,6.7,8,6.7c0.6,0,1.1,0.3,1.5,0.7 c0.1,0.1,0.1,0.1,0.2,0.1h1.4c0.2,0,0.4-0.2,0.3-0.5c-0.7-1.3-2.1-2.2-3.8-2.1C5.8,5,4.3,6.6,4.1,8.5C4,10.8,5.8,12.7,8,12.7 c1.6,0,2.9-0.9,3.5-2.3c0.1-0.2-0.1-0.4-0.3-0.4H9.9C9.8,10,9.7,10,9.6,10.1z\" />\n\t\t</svg>\n\t</span>\n</template>\n\n<script>\nexport default {\n\tname: 'CollectivesIcon',\n\tprops: {\n\t\ttitle: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\n\t\tfillColor: {\n\t\t\ttype: String,\n\t\t\tdefault: 'currentColor',\n\t\t},\n\n\t\tsize: {\n\t\t\ttype: Number,\n\t\t\tdefault: 24,\n\t\t},\n\t},\n}\n</script>\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<NcIconSvgWrapper class=\"favorite-marker-icon\" :name=\"t('files', 'Favorite')\" :svg=\"StarSvg\" />\n</template>\n\n<script lang=\"ts\">\nimport StarSvg from '@mdi/svg/svg/star.svg?raw'\nimport { translate as t } from '@nextcloud/l10n'\nimport { defineComponent } from 'vue'\nimport NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'\n\n/**\n * A favorite icon to be used for overlaying favorite entries like the file preview / icon\n * It has a stroke around the star icon to ensure enough contrast for accessibility.\n *\n * If the background has a hover state you might want to also apply it to the stroke like this:\n * ```scss\n * .parent:hover :deep(.favorite-marker-icon svg path) {\n * stroke: var(--color-background-hover);\n * }\n * ```\n */\nexport default defineComponent({\n\tname: 'FavoriteIcon',\n\tcomponents: {\n\t\tNcIconSvgWrapper,\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tStarSvg,\n\t\t}\n\t},\n\n\tasync mounted() {\n\t\tawait this.$nextTick()\n\t\t// MDI default viewBox is \"0 0 24 24\" but we add a stroke of 10px so we must adjust it\n\t\tconst el = this.$el.querySelector('svg')\n\t\tel?.setAttribute?.('viewBox', '-4 -4 30 30')\n\t},\n\n\tmethods: {\n\t\tt,\n\t},\n})\n</script>\n\n<style lang=\"scss\" scoped>\n.favorite-marker-icon {\n\tcolor: var(--color-favorite);\n\t// Override NcIconSvgWrapper defaults (clickable area)\n\tmin-width: unset !important;\n min-height: unset !important;\n\n\t:deep() {\n\t\tsvg {\n\t\t\t// We added a stroke for a11y so we must increase the size to include the stroke\n\t\t\twidth: 20px !important;\n\t\t\theight: 20px !important;\n\n\t\t\t// Override NcIconSvgWrapper defaults of 20px\n\t\t\tmax-width: unset !important;\n\t\t\tmax-height: unset !important;\n\n\t\t\t// Sow a border around the icon for better contrast\n\t\t\tpath {\n\t\t\t\tstroke: var(--color-main-background);\n\t\t\t\tstroke-width: 8px;\n\t\t\t\tstroke-linejoin: round;\n\t\t\t\tpaint-order: stroke;\n\t\t\t}\n\t\t}\n\t}\n}\n</style>\n","<!--\n - SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<NcIconSvgWrapper class=\"recently-created-marker-icon\" :name=\"t('files', 'Recently created')\" :path=\"mdiPlus\" />\n</template>\n\n<script lang=\"ts\">\nimport { mdiPlus } from '@mdi/js'\nimport { t } from '@nextcloud/l10n'\nimport { defineComponent } from 'vue'\nimport NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'\n\n/**\n * A recently created icon to be used for overlaying recently created entries like the file preview / icon\n * It has a stroke around the icon to ensure enough contrast for accessibility.\n *\n * If the background has a hover state you might want to also apply it to the stroke like this:\n * ```scss\n * .parent:hover :deep(.recently-created-marker-icon svg path) {\n * stroke: var(--color-background-hover);\n * }\n * ```\n */\nexport default defineComponent({\n\tname: 'RecentlyCreatedIcon',\n\tcomponents: {\n\t\tNcIconSvgWrapper,\n\t},\n\n\tsetup() {\n\t\treturn {\n\t\t\tmdiPlus,\n\t\t}\n\t},\n\n\tmethods: {\n\t\tt,\n\t},\n})\n</script>\n\n<style lang=\"scss\" scoped>\n.recently-created-marker-icon {\n\tcolor: var(--color-element-success);\n\t// Override NcIconSvgWrapper defaults (clickable area)\n\tmin-width: unset !important;\n\tmin-height: unset !important;\n\n\t:deep() {\n\t\tsvg {\n\t\t\t// We added a stroke for a11y so we must increase the size to include the stroke\n\t\t\twidth: 20px !important;\n\t\t\theight: 20px !important;\n\n\t\t\t// Override NcIconSvgWrapper defaults of 20px\n\t\t\tmax-width: unset !important;\n\t\t\tmax-height: unset !important;\n\n\t\t\t// Show a border around the icon for better contrast\n\t\t\tpath {\n\t\t\t\tstroke: var(--color-main-background);\n\t\t\t\tstroke-width: 8px;\n\t\t\t\tstroke-linejoin: round;\n\t\t\t\tpaint-order: stroke;\n\t\t\t}\n\t\t}\n\t}\n}\n</style>\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<span class=\"files-list__row-icon\">\n\t\t<template v-if=\"source.type === 'folder'\">\n\t\t\t<FolderOpenIcon v-if=\"dragover\" v-once />\n\t\t\t<template v-else>\n\t\t\t\t<FolderIcon v-once />\n\t\t\t\t<component\n\t\t\t\t\t:is=\"folderOverlay\"\n\t\t\t\t\tv-if=\"folderOverlay\"\n\t\t\t\t\tclass=\"files-list__row-icon-overlay\" />\n\t\t\t</template>\n\t\t</template>\n\n\t\t<!-- Decorative images, should not be aria documented -->\n\t\t<span v-else-if=\"previewUrl\" class=\"files-list__row-icon-preview-container\">\n\t\t\t<canvas\n\t\t\t\tv-if=\"hasBlurhash && (backgroundFailed === true || !backgroundLoaded)\"\n\t\t\t\tref=\"canvas\"\n\t\t\t\tclass=\"files-list__row-icon-blurhash\"\n\t\t\t\taria-hidden=\"true\" />\n\t\t\t<img\n\t\t\t\tv-if=\"backgroundFailed !== true\"\n\t\t\t\t:key=\"source.fileid\"\n\t\t\t\tref=\"previewImg\"\n\t\t\t\talt=\"\"\n\t\t\t\tclass=\"files-list__row-icon-preview\"\n\t\t\t\t:class=\"{ 'files-list__row-icon-preview--loaded': backgroundFailed === false }\"\n\t\t\t\tloading=\"lazy\"\n\t\t\t\t:src=\"previewUrl\"\n\t\t\t\t@error=\"onBackgroundError\"\n\t\t\t\t@load=\"onBackgroundLoad\">\n\t\t</span>\n\n\t\t<FileIcon v-else v-once />\n\n\t\t<!-- Favorite icon -->\n\t\t<span v-if=\"isFavorite\" class=\"files-list__row-icon-favorite\">\n\t\t\t<FavoriteIcon v-once />\n\t\t</span>\n\n\t\t<!-- Recently created icon -->\n\t\t<span v-else-if=\"isRecentView && isRecentlyCreated\" class=\"files-list__row-icon-recently-created\">\n\t\t\t<RecentlyCreatedIcon v-once />\n\t\t</span>\n\n\t\t<component\n\t\t\t:is=\"fileOverlay\"\n\t\t\tv-if=\"fileOverlay\"\n\t\t\tclass=\"files-list__row-icon-overlay files-list__row-icon-overlay--file\" />\n\t</span>\n</template>\n\n<script lang=\"ts\">\nimport type { Node } from '@nextcloud/files'\nimport type { PropType } from 'vue'\nimport type { UserConfig } from '../../types.ts'\n\nimport { FileType } from '@nextcloud/files'\nimport { translate as t } from '@nextcloud/l10n'\nimport { ShareType } from '@nextcloud/sharing'\nimport { decode } from 'blurhash'\nimport { computed, defineComponent, toRef } from 'vue'\nimport AccountGroupIcon from 'vue-material-design-icons/AccountGroup.vue'\nimport AccountPlusIcon from 'vue-material-design-icons/AccountPlus.vue'\nimport FileIcon from 'vue-material-design-icons/File.vue'\nimport FolderIcon from 'vue-material-design-icons/Folder.vue'\nimport FolderOpenIcon from 'vue-material-design-icons/FolderOpen.vue'\nimport KeyIcon from 'vue-material-design-icons/Key.vue'\nimport LinkIcon from 'vue-material-design-icons/Link.vue'\nimport NetworkIcon from 'vue-material-design-icons/NetworkOutline.vue'\nimport PlayCircleIcon from 'vue-material-design-icons/PlayCircle.vue'\nimport TagIcon from 'vue-material-design-icons/Tag.vue'\nimport CollectivesIcon from './CollectivesIcon.vue'\nimport FavoriteIcon from './FavoriteIcon.vue'\nimport RecentlyCreatedIcon from './RecentlyCreatedIcon.vue'\nimport { usePreviewImage } from '../../composables/usePreviewImage.ts'\nimport { isLivePhoto } from '../../services/LivePhotos.ts'\nimport { useUserConfigStore } from '../../store/userconfig.ts'\nimport { logger } from '../../utils/logger.ts'\n\nexport default defineComponent({\n\tname: 'FileEntryPreview',\n\n\tcomponents: {\n\t\tAccountGroupIcon,\n\t\tAccountPlusIcon,\n\t\tCollectivesIcon,\n\t\tFavoriteIcon,\n\t\tFileIcon,\n\t\tFolderIcon,\n\t\tFolderOpenIcon,\n\t\tKeyIcon,\n\t\tLinkIcon,\n\t\tNetworkIcon,\n\t\tTagIcon,\n\t\tRecentlyCreatedIcon,\n\t},\n\n\tprops: {\n\t\tsource: {\n\t\t\ttype: Object as PropType<Node>,\n\t\t\trequired: true,\n\t\t},\n\n\t\tdragover: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\tgridMode: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\tsetup(props) {\n\t\tconst userConfigStore = useUserConfigStore()\n\t\tconst previewUrl = usePreviewImage(\n\t\t\ttoRef(props, 'source'),\n\t\t\tcomputed(() => ({\n\t\t\t\tcrop: userConfigStore.userConfig.crop_image_previews === true,\n\t\t\t\tsize: props.gridMode ? 128 : 32,\n\t\t\t})),\n\t\t)\n\n\t\treturn {\n\t\t\tuserConfigStore,\n\t\t\tpreviewUrl,\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tbackgroundFailed: undefined as boolean | undefined,\n\t\t\tbackgroundLoaded: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tisFavorite(): boolean {\n\t\t\treturn this.source.attributes.favorite === 1\n\t\t},\n\n\t\tisRecentlyCreated(): boolean {\n\t\t\tif (!this.source.crtime) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tconst oneDayAgo = new Date()\n\t\t\toneDayAgo.setDate(oneDayAgo.getDate() - 1)\n\n\t\t\treturn this.source.crtime > oneDayAgo\n\t\t},\n\n\t\tisRecentView(): boolean {\n\t\t\treturn this.$route?.params?.view === 'recent'\n\t\t},\n\n\t\tuserConfig(): UserConfig {\n\t\t\treturn this.userConfigStore.userConfig\n\t\t},\n\n\t\tfileOverlay() {\n\t\t\tif (isLivePhoto(this.source)) {\n\t\t\t\treturn PlayCircleIcon\n\t\t\t}\n\n\t\t\treturn null\n\t\t},\n\n\t\tfolderOverlay() {\n\t\t\tif (this.source.type !== FileType.Folder) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\t// Encrypted folders\n\t\t\tif (this.source?.attributes?.['is-encrypted'] === 1) {\n\t\t\t\treturn KeyIcon\n\t\t\t}\n\n\t\t\t// System tags\n\t\t\tif (this.source?.attributes?.['is-tag']) {\n\t\t\t\treturn TagIcon\n\t\t\t}\n\n\t\t\t// Link and mail shared folders\n\t\t\tconst shareTypes = Object.values(this.source?.attributes?.['share-types'] || {}).flat() as number[]\n\t\t\tif (shareTypes.some((type) => type === ShareType.Link || type === ShareType.Email)) {\n\t\t\t\treturn LinkIcon\n\t\t\t}\n\n\t\t\t// Shared folders\n\t\t\tif (shareTypes.length > 0) {\n\t\t\t\treturn AccountPlusIcon\n\t\t\t}\n\n\t\t\tswitch (this.source?.attributes?.['mount-type']) {\n\t\t\t\tcase 'external':\n\t\t\t\tcase 'external-session':\n\t\t\t\t\treturn NetworkIcon\n\t\t\t\tcase 'group':\n\t\t\t\t\treturn AccountGroupIcon\n\t\t\t\tcase 'collective':\n\t\t\t\t\treturn CollectivesIcon\n\t\t\t\tcase 'shared':\n\t\t\t\t\treturn AccountPlusIcon\n\t\t\t}\n\n\t\t\treturn null\n\t\t},\n\n\t\thasBlurhash() {\n\t\t\treturn this.source.attributes['metadata-blurhash'] !== undefined\n\t\t},\n\t},\n\n\tmounted() {\n\t\tif (this.hasBlurhash && this.$refs.canvas) {\n\t\t\tthis.drawBlurhash()\n\t\t}\n\t},\n\n\tmethods: {\n\t\t// Called from FileEntry\n\t\treset() {\n\t\t\t// Reset background state to cancel any ongoing requests\n\t\t\tthis.backgroundFailed = undefined\n\t\t\tthis.backgroundLoaded = false\n\t\t\tconst previewImg = this.$refs.previewImg as HTMLImageElement | undefined\n\t\t\tif (previewImg) {\n\t\t\t\tpreviewImg.src = ''\n\t\t\t}\n\t\t},\n\n\t\tonBackgroundLoad() {\n\t\t\tthis.backgroundFailed = false\n\t\t\tthis.backgroundLoaded = true\n\t\t},\n\n\t\tonBackgroundError(event) {\n\t\t\t// Do not fail if we just reset the background\n\t\t\tif (event.target?.src === '') {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.backgroundFailed = true\n\t\t\tthis.backgroundLoaded = false\n\t\t},\n\n\t\tdrawBlurhash() {\n\t\t\tconst canvas = this.$refs.canvas as HTMLCanvasElement\n\n\t\t\tconst width = canvas.width\n\t\t\tconst height = canvas.height\n\n\t\t\tconst pixels = decode(this.source.attributes['metadata-blurhash'], width, height)\n\n\t\t\tconst ctx = canvas.getContext('2d')\n\t\t\tif (ctx === null) {\n\t\t\t\tlogger.error('Cannot create context for blurhash canvas')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tconst imageData = ctx.createImageData(width, height)\n\t\t\timageData.data.set(pixels)\n\t\t\tctx.putImageData(imageData, 0, 0)\n\t\t},\n\n\t\tt,\n\t},\n})\n</script>\n","/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { ActionsMenuStore } from '../types.ts'\n\nimport { defineStore } from 'pinia'\n\nexport const useActionsMenuStore = defineStore('actionsmenu', {\n\tstate: () => ({\n\t\topened: null,\n\t} as ActionsMenuStore),\n})\n","/**\n * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { Node } from '@nextcloud/files'\n\nimport { FileType } from '@nextcloud/files'\nimport { n } from '@nextcloud/l10n'\n\n/**\n * Extract dir and name from file path\n *\n * @param path - The full path\n * @return [dirPath, fileName]\n */\nexport function extractFilePaths(path: string): [string, string] {\n\tconst pathSections = path.split('/')\n\tconst fileName = pathSections[pathSections.length - 1]\n\tconst dirPath = pathSections.slice(0, pathSections.length - 1).join('/')\n\treturn [dirPath, fileName]\n}\n\n/**\n * Generate a translated summary of an array of nodes\n *\n * @param nodes - The nodes to summarize\n * @param hidden - The number of hidden nodes\n */\nexport function getSummaryFor(nodes: Node[], hidden = 0): string {\n\tconst fileCount = nodes.filter((node) => node.type === FileType.File).length\n\tconst folderCount = nodes.filter((node) => node.type === FileType.Folder).length\n\n\tconst summary: string[] = []\n\tif (fileCount > 0 || folderCount === 0) {\n\t\tconst fileSummary = n('files', '%n file', '%n files', fileCount)\n\t\tsummary.push(fileSummary)\n\t}\n\tif (folderCount > 0) {\n\t\tconst folderSummary = n('files', '%n folder', '%n folders', folderCount)\n\t\tsummary.push(folderSummary)\n\t}\n\tif (hidden > 0) {\n\t\t// TRANSLATORS: This is the number of hidden files or folders\n\t\tconst hiddenSummary = n('files', '%n hidden', '%n hidden', hidden)\n\t\tsummary.push(hiddenSummary)\n\t}\n\n\treturn summary.join(' · ')\n}\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<div class=\"files-list-drag-image\">\n\t\t<span class=\"files-list-drag-image__icon\">\n\t\t\t<span ref=\"previewImg\" />\n\t\t\t<FolderIcon v-if=\"isSingleFolder\" />\n\t\t\t<FileMultipleIcon v-else />\n\t\t</span>\n\t\t<span class=\"files-list-drag-image__name\">{{ name }}</span>\n\t</div>\n</template>\n\n<script lang=\"ts\">\nimport type { Node } from '@nextcloud/files'\n\nimport { FileType, formatFileSize } from '@nextcloud/files'\nimport { defineComponent } from 'vue'\nimport FileMultipleIcon from 'vue-material-design-icons/FileMultiple.vue'\nimport FolderIcon from 'vue-material-design-icons/Folder.vue'\nimport { getSummaryFor } from '../utils/fileUtils.ts'\n\nexport default defineComponent({\n\tname: 'DragAndDropPreview',\n\n\tcomponents: {\n\t\tFileMultipleIcon,\n\t\tFolderIcon,\n\t},\n\n\temits: ['loaded'],\n\n\tdata() {\n\t\treturn {\n\t\t\tnodes: [] as Node[],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tisSingleNode() {\n\t\t\treturn this.nodes.length === 1\n\t\t},\n\n\t\tisSingleFolder() {\n\t\t\treturn this.isSingleNode\n\t\t\t\t&& this.nodes[0].type === FileType.Folder\n\t\t},\n\n\t\tname() {\n\t\t\tif (!this.size) {\n\t\t\t\treturn this.summary\n\t\t\t}\n\t\t\treturn `${this.summary} – ${this.size}`\n\t\t},\n\n\t\tsize() {\n\t\t\tconst totalSize = this.nodes.reduce((total, node) => total + node.size || 0, 0)\n\t\t\tconst size = parseInt(totalSize, 10) || 0\n\t\t\tif (typeof size !== 'number' || size < 0) {\n\t\t\t\treturn null\n\t\t\t}\n\t\t\treturn formatFileSize(size, true)\n\t\t},\n\n\t\tsummary(): string {\n\t\t\tif (this.isSingleNode) {\n\t\t\t\tconst node = this.nodes[0]\n\t\t\t\treturn node.attributes?.displayname || node.basename\n\t\t\t}\n\n\t\t\treturn getSummaryFor(this.nodes)\n\t\t},\n\t},\n\n\tmethods: {\n\t\tupdate(nodes: Node[]) {\n\t\t\tthis.nodes = nodes\n\t\t\tthis.$refs.previewImg.replaceChildren()\n\n\t\t\t// Clone icon node from the list\n\t\t\tnodes.slice(0, 3).forEach((node) => {\n\t\t\t\tconst preview = document.querySelector(`[data-cy-files-list-row-fileid=\"${node.fileid}\"] .files-list__row-icon img`)\n\t\t\t\tif (preview) {\n\t\t\t\t\tconst previewElmt = this.$refs.previewImg as HTMLElement\n\t\t\t\t\tpreviewElmt.appendChild(preview.parentNode.cloneNode(true))\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tthis.$emit('loaded', this.$el)\n\t\t\t})\n\t\t},\n\t},\n})\n</script>\n\n<style lang=\"scss\">\n$size: 28px;\n$stack-shift: 6px;\n\n.files-list-drag-image {\n\tposition: absolute;\n\ttop: -9999px;\n\tinset-inline-start: -9999px;\n\tdisplay: flex;\n\toverflow: hidden;\n\talign-items: center;\n\theight: $size + $stack-shift;\n\tpadding: $stack-shift $stack-shift * 2;\n\tbackground: var(--color-main-background);\n\n\t&__icon,\n\t.files-list__row-icon-preview-container {\n\t\tdisplay: flex;\n\t\toverflow: hidden;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\twidth: $size - $stack-shift;\n\t\theight: $size - $stack-shift;;\n\t\tborder-radius: var(--border-radius);\n\t}\n\n\t&__icon {\n\t\toverflow: visible;\n\t\tmargin-inline-end: $stack-shift * 2;\n\n\t\timg {\n\t\t\tmax-width: 100%;\n\t\t\tmax-height: 100%;\n\t\t}\n\n\t\t.material-design-icon {\n\t\t\tcolor: var(--color-text-maxcontrast);\n\t\t\t&.folder-icon {\n\t\t\t\tcolor: var(--color-primary-element);\n\t\t\t}\n\t\t}\n\n\t\t// Previews container\n\t\t> span {\n\t\t\tdisplay: flex;\n\n\t\t\t// Stack effect if more than one element\n\t\t\t// Max 3 elements\n\t\t\t> .files-list__row-icon-preview-container + .files-list__row-icon-preview-container {\n\t\t\t\tmargin-top: $stack-shift;\n\t\t\t\tmargin-inline-start: $stack-shift * 2 - $size;\n\t\t\t\t& + .files-list__row-icon-preview-container {\n\t\t\t\t\tmargin-top: $stack-shift * 2;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// If we have manually clone the preview,\n\t\t\t// let's hide any fallback icons\n\t\t\t&:not(:empty) + * {\n\t\t\t\tdisplay: none;\n\t\t\t}\n\t\t}\n\t}\n\n\t&__name {\n\t\toverflow: hidden;\n\t\twhite-space: nowrap;\n\t\ttext-overflow: ellipsis;\n\t}\n}\n\n</style>\n","/**\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\nimport type { Node } from '@nextcloud/files'\nimport type { ComponentPublicInstance } from 'vue'\n\nimport { createApp } from 'vue'\nimport DragAndDropPreview from '../components/DragAndDropPreview.vue'\n\ntype PreviewInstance = ComponentPublicInstance & { update: (nodes: Node[]) => void }\n\nlet preview: PreviewInstance | undefined\nlet onLoaded: ((el: Element) => void) | undefined\n\n/**\n *\n * @param nodes\n */\nexport async function getDragAndDropPreview(nodes: Node[]): Promise<Element> {\n\treturn new Promise((resolve) => {\n\t\tif (!preview) {\n\t\t\tconst mountingPoint = document.createElement('div')\n\t\t\tdocument.body.appendChild(mountingPoint)\n\t\t\tconst app = createApp(DragAndDropPreview, {\n\t\t\t\tonLoaded: (el: Element) => onLoaded?.(el),\n\t\t\t})\n\t\t\tpreview = app.mount(mountingPoint) as PreviewInstance\n\t\t}\n\n\t\tonLoaded = resolve\n\t\tpreview.update(nodes)\n\t})\n}\n","/**\n * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { IFileAction } from '@nextcloud/files'\nimport type { PropType } from 'vue'\nimport type { FileSource } from '../types.ts'\n\nimport { FileType, Folder, File as NcFile, Node, NodeStatus, Permission } from '@nextcloud/files'\nimport { t } from '@nextcloud/l10n'\nimport { generateUrl } from '@nextcloud/router'\nimport { isPublicShare } from '@nextcloud/sharing/public'\nimport { extname } from 'path'\nimport { computed, defineComponent } from 'vue'\nimport { action as sidebarAction } from '../actions/sidebarAction.ts'\nimport { dataTransferToFileTree, onDropExternalFiles, onDropInternalFiles } from '../services/DropService.ts'\nimport { getDragAndDropPreview } from '../utils/dragUtils.ts'\nimport { hashCode } from '../utils/hashUtils.ts'\nimport { logger } from '../utils/logger.ts'\nimport { isDownloadable } from '../utils/permissions.ts'\n\nexport default defineComponent({\n\tprops: {\n\t\tsource: {\n\t\t\ttype: [Folder, NcFile, Node] as PropType<Node>,\n\t\t\trequired: true,\n\t\t},\n\t\tnodes: {\n\t\t\ttype: Array as PropType<Node[]>,\n\t\t\trequired: true,\n\t\t},\n\t\tisMtimeAvailable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t\tcompact: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\tprovide() {\n\t\treturn {\n\t\t\tdefaultFileAction: computed(() => this.defaultFileAction),\n\t\t\tenabledFileActions: computed(() => this.enabledFileActions),\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tdragover: false,\n\t\t\tgridMode: false,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tfileid() {\n\t\t\treturn this.source.fileid ?? 0\n\t\t},\n\n\t\tuniqueId() {\n\t\t\treturn hashCode(this.source.source)\n\t\t},\n\n\t\tisLoading() {\n\t\t\treturn this.source.status === NodeStatus.LOADING\n\t\t},\n\n\t\t/**\n\t\t * The display name of the current node\n\t\t * Either the nodes filename or a custom display name (e.g. for shares)\n\t\t */\n\t\tdisplayName() {\n\t\t\t// basename fallback needed for apps using old `@nextcloud/files` prior 3.6.0\n\t\t\treturn this.source.displayname || this.source.basename\n\t\t},\n\t\t/**\n\t\t * The display name without extension\n\t\t */\n\t\tbasename() {\n\t\t\tif (this.extension === '') {\n\t\t\t\treturn this.displayName\n\t\t\t}\n\t\t\treturn this.displayName.slice(0, 0 - this.extension.length)\n\t\t},\n\t\t/**\n\t\t * The extension of the file\n\t\t */\n\t\textension() {\n\t\t\tif (this.source.type === FileType.Folder) {\n\t\t\t\treturn ''\n\t\t\t}\n\n\t\t\treturn extname(this.displayName)\n\t\t},\n\n\t\tdraggingFiles() {\n\t\t\treturn this.draggingStore.dragging as FileSource[]\n\t\t},\n\t\tselectedFiles() {\n\t\t\treturn this.selectionStore.selected as FileSource[]\n\t\t},\n\t\tisSelected() {\n\t\t\treturn this.selectedFiles.includes(this.source.source)\n\t\t},\n\n\t\tisRenaming() {\n\t\t\treturn this.renamingStore.renamingNode === this.source\n\t\t},\n\t\tisRenamingSmallScreen() {\n\t\t\treturn this.isRenaming && this.isNarrow\n\t\t},\n\n\t\tisActive() {\n\t\t\t// Not using activeNode here because we want to\n\t\t\t// be reactive to the url change directly\n\t\t\treturn String(this.fileid) === String(this.currentRouteFileId)\n\t\t},\n\n\t\t/**\n\t\t * Check if the source is in a failed state after an API request\n\t\t */\n\t\tisFailedSource() {\n\t\t\treturn this.source.status === NodeStatus.FAILED\n\t\t},\n\n\t\tcanDrag(): boolean {\n\t\t\tif (this.isRenaming) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Ignore if the node is not available\n\t\t\tif (this.isFailedSource) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\tconst canDrag = (node: Node): boolean => {\n\t\t\t\treturn (node?.permissions & Permission.UPDATE) !== 0\n\t\t\t}\n\n\t\t\t// If we're dragging a selection, we need to check all files\n\t\t\tif (this.selectedFiles.length > 0) {\n\t\t\t\tconst nodes = this.selectedFiles.map((source) => this.filesStore.getNode(source)) as Node[]\n\t\t\t\treturn nodes.every(canDrag)\n\t\t\t}\n\t\t\treturn canDrag(this.source)\n\t\t},\n\n\t\tcanDrop(): boolean {\n\t\t\tif (this.source.type !== FileType.Folder) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// Ignore if the node is not available\n\t\t\tif (this.isFailedSource) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\t// If the current folder is also being dragged, we can't drop it on itself\n\t\t\tif (this.draggingFiles.includes(this.source.source)) {\n\t\t\t\treturn false\n\t\t\t}\n\n\t\t\treturn (this.source.permissions & Permission.CREATE) !== 0\n\t\t},\n\n\t\topenedMenu: {\n\t\t\tget() {\n\t\t\t\treturn this.actionsMenuStore.opened === this.uniqueId.toString()\n\t\t\t},\n\t\t\tset(opened) {\n\t\t\t\t// If the menu is opened on another file entry, we ignore closed events\n\t\t\t\tif (opened === false && this.actionsMenuStore.opened !== this.uniqueId.toString()) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// If opened, we specify the current file id\n\t\t\t\t// else we set it to null to close the menu\n\t\t\t\tthis.actionsMenuStore.opened = opened\n\t\t\t\t\t? this.uniqueId.toString()\n\t\t\t\t\t: null\n\t\t\t},\n\t\t},\n\n\t\tmtime() {\n\t\t\t// If the mtime is not a valid date, return it as is\n\t\t\tif (this.source.mtime && !isNaN(this.source.mtime.getDate())) {\n\t\t\t\treturn this.source.mtime\n\t\t\t}\n\n\t\t\tif (this.source.crtime && !isNaN(this.source.crtime.getDate())) {\n\t\t\t\treturn this.source.crtime\n\t\t\t}\n\n\t\t\treturn null\n\t\t},\n\n\t\tmtimeOpacity() {\n\t\t\tif (!this.mtime) {\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// The time when we start reducing the opacity\n\t\t\tconst maxOpacityTime = 31 * 24 * 60 * 60 * 1000 // 31 days\n\t\t\t// everything older than the maxOpacityTime will have the same value\n\t\t\tconst timeDiff = Date.now() - this.mtime.getTime()\n\t\t\tif (timeDiff < 0) {\n\t\t\t\t// this means we have an invalid mtime which is in the future!\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\t// inversed time difference from 0 to maxOpacityTime (which would mean today)\n\t\t\tconst opacityTime = Math.max(0, maxOpacityTime - timeDiff)\n\t\t\t// 100 = today, 0 = 31 days ago or older\n\t\t\tconst percentage = Math.round(opacityTime * 100 / maxOpacityTime)\n\t\t\treturn {\n\t\t\t\tcolor: `color-mix(in srgb, var(--color-main-text) ${percentage}%, var(--color-text-maxcontrast))`,\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Sorted actions that are enabled for this node\n\t\t */\n\t\tenabledFileActions() {\n\t\t\tif (this.source.status === NodeStatus.FAILED) {\n\t\t\t\treturn []\n\t\t\t}\n\n\t\t\treturn this.actions\n\t\t\t\t.filter((action: IFileAction) => {\n\t\t\t\t\tif (!action.enabled) {\n\t\t\t\t\t\treturn true\n\t\t\t\t\t}\n\n\t\t\t\t\t// In case something goes wrong, since we don't want to break\n\t\t\t\t\t// the entire list, we filter out actions that throw an error.\n\t\t\t\t\ttry {\n\t\t\t\t\t\treturn action.enabled({\n\t\t\t\t\t\t\tnodes: [this.source],\n\t\t\t\t\t\t\tview: this.activeView,\n\t\t\t\t\t\t\tfolder: this.activeFolder!,\n\t\t\t\t\t\t\tcontents: this.nodes,\n\t\t\t\t\t\t})\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tlogger.error('Error while checking action', { action, error })\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.sort((a, b) => (a.order || 0) - (b.order || 0))\n\t\t},\n\n\t\tdefaultFileAction() {\n\t\t\treturn this.enabledFileActions.find((action: IFileAction) => action.default !== undefined)\n\t\t},\n\t},\n\n\twatch: {\n\t\t/**\n\t\t * When the source changes, reset the preview\n\t\t * and fetch the new one.\n\t\t *\n\t\t * @param newSource The new value of the source prop\n\t\t * @param oldSource The previous value\n\t\t */\n\t\tsource(newSource: Node, oldSource: Node) {\n\t\t\tif (newSource.source !== oldSource.source) {\n\t\t\t\tthis.resetState()\n\t\t\t}\n\t\t},\n\n\t\topenedMenu() {\n\t\t\t// Checking if the menu is really closed and not\n\t\t\t// just a change in the open state to another file entry.\n\t\t\tif (this.actionsMenuStore.opened === null) {\n\t\t\t\t// Reset any right menu position potentially set\n\t\t\t\tlogger.debug('All actions menu closed, resetting right menu position...')\n\t\t\t\tconst root = this.$el?.closest('main.app-content') as HTMLElement\n\t\t\t\tif (root !== null) {\n\t\t\t\t\troot.style.removeProperty('--mouse-pos-x')\n\t\t\t\t\troot.style.removeProperty('--mouse-pos-y')\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t},\n\n\tbeforeUnmount() {\n\t\tthis.resetState()\n\t},\n\n\tmethods: {\n\t\tresetState() {\n\t\t\t// Reset the preview state\n\t\t\tthis.$refs?.preview?.reset?.()\n\n\t\t\t// Close menu\n\t\t\tthis.openedMenu = false\n\t\t},\n\n\t\t// Open the actions menu on right click\n\t\tonRightClick(event) {\n\t\t\t// If already opened, fallback to default browser\n\t\t\tif (this.openedMenu) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Ignore right click if the node is not available\n\t\t\tif (this.isFailedSource) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// The grid mode is compact enough to not care about\n\t\t\t// the actions menu mouse position\n\t\t\tif (!this.gridMode) {\n\t\t\t\t// Actions menu is contained within the app content\n\t\t\t\tconst root = this.$el?.closest('main.app-content') as HTMLElement\n\t\t\t\tconst contentRect = root.getBoundingClientRect()\n\t\t\t\t// Using Math.min/max to prevent the menu from going out of the AppContent\n\t\t\t\t// 200 = max width of the menu\n\t\t\t\tlogger.debug('Setting actions menu position...')\n\t\t\t\troot.style.setProperty('--mouse-pos-x', Math.max(0, event.clientX - contentRect.left - 200) + 'px')\n\t\t\t\troot.style.setProperty('--mouse-pos-y', Math.max(0, event.clientY - contentRect.top) + 'px')\n\t\t\t} else {\n\t\t\t\t// Reset any right menu position potentially set\n\t\t\t\tconst root = this.$el?.closest('main.app-content') as HTMLElement\n\t\t\t\troot.style.removeProperty('--mouse-pos-x')\n\t\t\t\troot.style.removeProperty('--mouse-pos-y')\n\t\t\t}\n\n\t\t\t// If the clicked row is in the selection, open global menu\n\t\t\tconst isMoreThanOneSelected = this.selectedFiles.length > 1\n\t\t\tthis.actionsMenuStore.opened = this.isSelected && isMoreThanOneSelected ? 'global' : this.uniqueId.toString()\n\n\t\t\t// Prevent any browser defaults\n\t\t\tevent.preventDefault()\n\t\t\tevent.stopPropagation()\n\t\t},\n\n\t\texecDefaultAction(event: MouseEvent) {\n\t\t\t// Ignore click if we are renaming\n\t\t\tif (this.isRenaming) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Ignore right click (button & 2) and any auxiliary button expect mouse-wheel (button & 4)\n\t\t\tif (Boolean(event.button & 2) || event.button > 4) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Ignore if the node is not available\n\t\t\tif (this.isFailedSource) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// if ctrl+click / cmd+click (MacOS uses the meta key) or middle mouse button (button & 4), open in new tab\n\t\t\t// also if there is no default action use this as a fallback\n\t\t\tconst metaKeyPressed = event.ctrlKey || event.metaKey || event.button === 1\n\t\t\tif (metaKeyPressed || !this.defaultFileAction) {\n\t\t\t\t// If no download permission, then we can not allow to download (direct link) the files\n\t\t\t\tif (!isDownloadable(this.source)) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst url = isPublicShare()\n\t\t\t\t\t? this.source.encodedSource\n\t\t\t\t\t: generateUrl('/f/{fileId}', { fileId: this.fileid })\n\t\t\t\tevent.preventDefault()\n\t\t\t\tevent.stopPropagation()\n\n\t\t\t\t// Open the file in a new tab if the meta key or the middle mouse button is clicked\n\t\t\t\twindow.open(url, metaKeyPressed ? '_blank' : '_self')\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// every special case handled so just execute the default action\n\t\t\tevent.preventDefault()\n\t\t\tevent.stopPropagation()\n\t\t\t// Execute the first default action if any\n\t\t\tthis.defaultFileAction.exec({\n\t\t\t\tnodes: [this.source],\n\t\t\t\tfolder: this.activeFolder!,\n\t\t\t\tcontents: this.nodes,\n\t\t\t\tview: this.activeView!,\n\t\t\t})\n\t\t},\n\n\t\topenDetailsIfAvailable(event) {\n\t\t\tevent.preventDefault()\n\t\t\tevent.stopPropagation()\n\t\t\tif (sidebarAction?.enabled?.({\n\t\t\t\tnodes: [this.source],\n\t\t\t\tfolder: this.activeFolder!,\n\t\t\t\tcontents: this.nodes,\n\t\t\t\tview: this.activeView!,\n\t\t\t})) {\n\t\t\t\tsidebarAction.exec({\n\t\t\t\t\tnodes: [this.source],\n\t\t\t\t\tfolder: this.activeFolder!,\n\t\t\t\t\tcontents: this.nodes,\n\t\t\t\t\tview: this.activeView!,\n\t\t\t\t})\n\t\t\t}\n\t\t},\n\n\t\tonDragOver(event: DragEvent) {\n\t\t\tthis.dragover = this.canDrop\n\t\t\tif (!this.canDrop) {\n\t\t\t\tevent.dataTransfer.dropEffect = 'none'\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Handle copy/move drag and drop\n\t\t\tif (event.ctrlKey) {\n\t\t\t\tevent.dataTransfer.dropEffect = 'copy'\n\t\t\t} else {\n\t\t\t\tevent.dataTransfer.dropEffect = 'move'\n\t\t\t}\n\t\t},\n\t\tonDragLeave(event: DragEvent) {\n\t\t\t// Counter bubbling, make sure we're ending the drag\n\t\t\t// only when we're leaving the current element\n\t\t\tconst currentTarget = event.currentTarget as HTMLElement\n\t\t\tif (currentTarget?.contains(event.relatedTarget as HTMLElement)) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tthis.dragover = false\n\t\t},\n\n\t\tasync onDragStart(event: DragEvent) {\n\t\t\tevent.stopPropagation()\n\t\t\tif (!this.canDrag || !this.fileid) {\n\t\t\t\tevent.preventDefault()\n\t\t\t\tevent.stopPropagation()\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlogger.debug('Drag started', { event })\n\n\t\t\t// Make sure that we're not dragging a file like the preview\n\t\t\tevent.dataTransfer?.clearData?.()\n\n\t\t\t// Reset any renaming\n\t\t\tthis.renamingStore.$reset()\n\n\t\t\t// Dragging set of files, if we're dragging a file\n\t\t\t// that is already selected, we use the entire selection\n\t\t\tif (this.selectedFiles.includes(this.source.source)) {\n\t\t\t\tthis.draggingStore.set(this.selectedFiles)\n\t\t\t} else {\n\t\t\t\tthis.draggingStore.set([this.source.source])\n\t\t\t}\n\n\t\t\tconst nodes = this.draggingStore.dragging\n\t\t\t\t.map((source) => this.filesStore.getNode(source)) as Node[]\n\n\t\t\tconst image = await getDragAndDropPreview(nodes)\n\t\t\tevent.dataTransfer?.setDragImage(image, -10, -10)\n\t\t},\n\t\tonDragEnd() {\n\t\t\tthis.draggingStore.reset()\n\t\t\tthis.dragover = false\n\t\t\tlogger.debug('Drag ended')\n\t\t},\n\n\t\tasync onDrop(event: DragEvent) {\n\t\t\t// skip if native drop like text drag and drop from files names\n\t\t\tif (!this.draggingFiles && !event.dataTransfer?.items?.length) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tevent.preventDefault()\n\t\t\tevent.stopPropagation()\n\n\t\t\t// If another button is pressed, cancel it. This\n\t\t\t// allows cancelling the drag with the right click.\n\t\t\tif (!this.canDrop || event.button) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Caching the selection\n\t\t\tconst selection = this.draggingFiles\n\t\t\tconst items = Array.from(event.dataTransfer?.items || [])\n\n\t\t\tif (selection.length === 0 && items.some((item) => item.kind === 'file')) {\n\t\t\t\t// Snapshot DataTransfer items immediately so Blink clears data.items\n\t\t\t\t// after the first async yield. Then convert FileSystemEntry to File\n\t\t\t\t// inside dataTransferToFileTree (duck-typed via entry.isFile) rather\n\t\t\t\t// than deferring to @nextcloud/upload's batchUpload, whose\n\t\t\t\t// instanceof-based conversion silently no-ops on some Chromium builds.\n\t\t\t\t// See https://github.com/nextcloud/server/issues/60139\n\t\t\t\tconst fileTree = await dataTransferToFileTree(items)\n\n\t\t\t\t// canDrop already gates this branch on FileType.Folder, but the\n\t\t\t\t// type system can't see that — narrow defensively so a future\n\t\t\t\t// loosening of canDrop can't silently lie via the cast below.\n\t\t\t\t// Use the `type` field rather than `instanceof Folder`: apps\n\t\t\t\t// bundle their own copy of @nextcloud/files, so a Folder from\n\t\t\t\t// an app would not be `instanceof` the server's Folder class.\n\t\t\t\tif (this.source.type !== FileType.Folder) {\n\t\t\t\t\tlogger.error('onDrop: external drop target is not a Folder', { source: this.source })\n\t\t\t\t\tthis.dragover = false\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Fetch destination contents for conflict resolution\n\t\t\t\tconst cachedContents = this.filesStore.getNodesByPath(this.activeView.id, this.source.path)\n\t\t\t\tconst contents = cachedContents.length === 0\n\t\t\t\t\t? (await this.activeView!.getContents(this.source.path)).contents\n\t\t\t\t\t: cachedContents\n\n\t\t\t\tlogger.debug('Start uploading dropped files', { target: this.source.path, fileTree })\n\t\t\t\tawait onDropExternalFiles(fileTree, this.source as Folder, contents)\n\t\t\t\tthis.dragover = false\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// We might not have the target directory fetched yet\n\t\t\tconst cachedContents = this.filesStore.getNodesByPath(this.activeView.id, this.source.path)\n\t\t\tconst contents = cachedContents.length === 0\n\t\t\t\t? (await this.activeView!.getContents(this.source.path)).contents\n\t\t\t\t: cachedContents\n\n\t\t\tconst isCopy = event.ctrlKey\n\t\t\tthis.dragover = false\n\n\t\t\tlogger.debug('Dropped', { event, folder: this.source, selection })\n\n\t\t\tconst nodes = selection.map((source) => this.filesStore.getNode(source)) as Node[]\n\t\t\tawait onDropInternalFiles(nodes, this.source, contents, isCopy)\n\n\t\t\t// Reset selection after we dropped the files\n\t\t\t// if the dropped files are within the selection\n\t\t\tif (selection.some((source) => this.selectedFiles.includes(source))) {\n\t\t\t\tlogger.debug('Dropped selection, resetting select store...')\n\t\t\t\tthis.selectionStore.reset()\n\t\t\t}\n\t\t},\n\n\t\tt,\n\t},\n})\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n\n<template>\n\t<tr\n\t\t:class=\"{\n\t\t\t'files-list__row--dragover': dragover,\n\t\t\t'files-list__row--loading': isLoading,\n\t\t\t'files-list__row--active': isActive,\n\t\t}\"\n\t\tdata-cy-files-list-row\n\t\t:data-cy-files-list-row-fileid=\"fileid\"\n\t\t:data-cy-files-list-row-name=\"source.basename\"\n\t\t:draggable=\"canDrag\"\n\t\tclass=\"files-list__row\"\n\t\tv-on=\"rowListeners\">\n\t\t<!-- Failed indicator -->\n\t\t<span v-if=\"isFailedSource\" class=\"files-list__row--failed\" />\n\n\t\t<!-- Checkbox -->\n\t\t<FileEntryCheckbox\n\t\t\t:fileid=\"fileid\"\n\t\t\t:isLoading=\"isLoading\"\n\t\t\t:nodes=\"nodes\"\n\t\t\t:source=\"source\" />\n\n\t\t<!-- Link to file -->\n\t\t<td class=\"files-list__row-name\" data-cy-files-list-row-name>\n\t\t\t<!-- Icon or preview -->\n\t\t\t<FileEntryPreview\n\t\t\t\tref=\"preview\"\n\t\t\t\t:source=\"source\"\n\t\t\t\t:dragover=\"dragover\"\n\t\t\t\t@auxclick=\"execDefaultAction\"\n\t\t\t\t@click=\"execDefaultAction\" />\n\n\t\t\t<FileEntryName\n\t\t\t\tref=\"name\"\n\t\t\t\t:basename=\"basename\"\n\t\t\t\t:extension=\"extension\"\n\t\t\t\t:source=\"source\"\n\t\t\t\t@auxclick=\"execDefaultAction\"\n\t\t\t\t@click=\"execDefaultAction\" />\n\t\t</td>\n\n\t\t<!-- Actions -->\n\t\t<FileEntryActions\n\t\t\tv-show=\"!isRenamingSmallScreen\"\n\t\t\tref=\"actions\"\n\t\t\tv-model:opened=\"openedMenu\"\n\t\t\t:class=\"`files-list__row-actions-${uniqueId}`\"\n\t\t\t:source=\"source\" />\n\n\t\t<!-- Mime -->\n\t\t<td\n\t\t\tv-if=\"isMimeAvailable\"\n\t\t\t:title=\"mime\"\n\t\t\tclass=\"files-list__row-mime\"\n\t\t\tdata-cy-files-list-row-mime\n\t\t\t@click=\"openDetailsIfAvailable\">\n\t\t\t<span>{{ mime }}</span>\n\t\t</td>\n\n\t\t<!-- Size -->\n\t\t<td\n\t\t\tv-if=\"!compact && isSizeAvailable\"\n\t\t\t:style=\"sizeOpacity\"\n\t\t\tclass=\"files-list__row-size\"\n\t\t\tdata-cy-files-list-row-size\n\t\t\t@click=\"openDetailsIfAvailable\">\n\t\t\t<span>{{ size }}</span>\n\t\t</td>\n\n\t\t<!-- Mtime -->\n\t\t<td\n\t\t\tv-if=\"!compact && isMtimeAvailable\"\n\t\t\t:style=\"mtimeOpacity\"\n\t\t\tclass=\"files-list__row-mtime\"\n\t\t\tdata-cy-files-list-row-mtime\n\t\t\t@click=\"openDetailsIfAvailable\">\n\t\t\t<NcDateTime\n\t\t\t\tv-if=\"mtime\"\n\t\t\t\tignoreSeconds\n\t\t\t\t:timestamp=\"mtime\" />\n\t\t\t<span v-else>{{ t('files', 'Unknown date') }}</span>\n\t\t</td>\n\n\t\t<!-- View columns -->\n\t\t<td\n\t\t\tv-for=\"column in columns\"\n\t\t\t:key=\"column.id\"\n\t\t\t:class=\"`files-list__row-${activeView.id}-${column.id}`\"\n\t\t\tclass=\"files-list__row-column-custom\"\n\t\t\t:data-cy-files-list-row-column-custom=\"column.id\"\n\t\t\t@click=\"openDetailsIfAvailable\">\n\t\t\t<CustomElementRender\n\t\t\t\t:activeFolder=\"activeFolder\"\n\t\t\t\t:activeView=\"activeView\"\n\t\t\t\t:render=\"adaptColumnRenderToCustomElementRender(column)\"\n\t\t\t\t:source=\"source\" />\n\t\t</td>\n\t</tr>\n</template>\n\n<script lang=\"ts\">\nimport { FileType, formatFileSize } from '@nextcloud/files'\nimport { t } from '@nextcloud/l10n'\nimport { useHotKey } from '@nextcloud/vue/composables/useHotKey'\nimport { storeToRefs } from 'pinia'\nimport { defineComponent } from 'vue'\nimport NcDateTime from '@nextcloud/vue/components/NcDateTime'\nimport CustomElementRender from './CustomElementRender.vue'\nimport FileEntryActions from './FileEntry/FileEntryActions.vue'\nimport FileEntryCheckbox from './FileEntry/FileEntryCheckbox.vue'\nimport FileEntryName from './FileEntry/FileEntryName.vue'\nimport FileEntryPreview from './FileEntry/FileEntryPreview.vue'\nimport { useFileActions } from '../composables/useFileActions.ts'\nimport { useFileListLayout } from '../composables/useFileListLayout.ts'\nimport { useFileListWidth } from '../composables/useFileListWidth.ts'\nimport { useRouteParameters } from '../composables/useRouteParameters.ts'\nimport { useActionsMenuStore } from '../store/actionsmenu.ts'\nimport { useActiveStore } from '../store/active.ts'\nimport { useDragAndDropStore } from '../store/dragging.ts'\nimport { useFilesStore } from '../store/files.ts'\nimport { useRenamingStore } from '../store/renaming.ts'\nimport { useSelectionStore } from '../store/selection.ts'\nimport FileEntryMixin from './FileEntryMixin.ts'\n\nexport default defineComponent({\n\tname: 'FileEntry',\n\n\tcomponents: {\n\t\tCustomElementRender,\n\t\tFileEntryActions,\n\t\tFileEntryCheckbox,\n\t\tFileEntryName,\n\t\tFileEntryPreview,\n\t\tNcDateTime,\n\t},\n\n\tmixins: [\n\t\tFileEntryMixin,\n\t],\n\n\tprops: {\n\t\tisMimeAvailable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\tisSizeAvailable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\tsetup() {\n\t\tconst actionsMenuStore = useActionsMenuStore()\n\t\tconst draggingStore = useDragAndDropStore()\n\t\tconst filesStore = useFilesStore()\n\t\tconst renamingStore = useRenamingStore()\n\t\tconst selectionStore = useSelectionStore()\n\t\tconst { isNarrow } = useFileListWidth()\n\t\tconst layout = useFileListLayout()\n\t\tconst {\n\t\t\tfileId: currentRouteFileId,\n\t\t} = useRouteParameters()\n\n\t\tconst activeStore = useActiveStore()\n\t\tconst {\n\t\t\tactiveFolder,\n\t\t\tactiveNode,\n\t\t\tactiveView,\n\t\t} = storeToRefs(activeStore)\n\n\t\tconst actions = useFileActions()\n\n\t\treturn {\n\t\t\tactions,\n\t\t\tactiveFolder,\n\t\t\tactiveNode,\n\t\t\tactiveView,\n\t\t\tcurrentRouteFileId,\n\t\t\tisNarrow,\n\t\t\tlayout,\n\n\t\t\tactiveStore,\n\t\t\tactionsMenuStore,\n\t\t\tdraggingStore,\n\t\t\tfilesStore,\n\t\t\trenamingStore,\n\t\t\tselectionStore,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Conditionally add drag and drop listeners\n\t\t * Do not add drag start and over listeners on renaming to allow to drag and drop text\n\t\t */\n\t\trowListeners() {\n\t\t\tconst conditionals = this.isRenaming\n\t\t\t\t? {}\n\t\t\t\t: {\n\t\t\t\t\t\tdragstart: this.onDragStart,\n\t\t\t\t\t\tdragover: this.onDragOver,\n\t\t\t\t\t}\n\n\t\t\treturn {\n\t\t\t\t...conditionals,\n\t\t\t\tcontextmenu: this.onRightClick,\n\t\t\t\tdragleave: this.onDragLeave,\n\t\t\t\tdragend: this.onDragEnd,\n\t\t\t\tdrop: this.onDrop,\n\t\t\t}\n\t\t},\n\n\t\tcolumns() {\n\t\t\tif (this.compact) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\treturn this.layout.columns\n\t\t},\n\n\t\tmime() {\n\t\t\tif (this.source.type === FileType.Folder) {\n\t\t\t\treturn this.t('files', 'Folder')\n\t\t\t}\n\n\t\t\tif (!this.source.mime || this.source.mime === 'application/octet-stream') {\n\t\t\t\treturn t('files', 'Unknown file type')\n\t\t\t}\n\n\t\t\tif (window.OC?.MimeTypeList?.names?.[this.source.mime]) {\n\t\t\t\treturn window.OC.MimeTypeList.names[this.source.mime]\n\t\t\t}\n\n\t\t\tconst baseType = this.source.mime.split('/')[0]\n\t\t\tconst ext = this.source?.extension?.toUpperCase().replace(/^\\./, '') || ''\n\t\t\tif (baseType === 'image') {\n\t\t\t\treturn t('files', '{ext} image', { ext })\n\t\t\t}\n\t\t\tif (baseType === 'video') {\n\t\t\t\treturn t('files', '{ext} video', { ext })\n\t\t\t}\n\t\t\tif (baseType === 'audio') {\n\t\t\t\treturn t('files', '{ext} audio', { ext })\n\t\t\t}\n\t\t\tif (baseType === 'text') {\n\t\t\t\treturn t('files', '{ext} text', { ext })\n\t\t\t}\n\n\t\t\treturn this.source.mime\n\t\t},\n\n\t\tsize() {\n\t\t\tconst size = this.source.size\n\t\t\tif (size === undefined || isNaN(size) || size < 0) {\n\t\t\t\treturn this.t('files', 'Pending')\n\t\t\t}\n\t\t\treturn formatFileSize(size, true)\n\t\t},\n\n\t\tsizeOpacity() {\n\t\t\tconst maxOpacitySize = 10 * 1024 * 1024\n\n\t\t\tconst size = this.source.size\n\t\t\tif (size === undefined || isNaN(size) || size < 0) {\n\t\t\t\treturn {}\n\t\t\t}\n\n\t\t\tconst ratio = Math.round(Math.min(100, 100 * Math.pow((size / maxOpacitySize), 2)))\n\t\t\treturn {\n\t\t\t\tcolor: `color-mix(in srgb, var(--color-main-text) ${ratio}%, var(--color-text-maxcontrast))`,\n\t\t\t}\n\t\t},\n\t},\n\n\tcreated() {\n\t\tuseHotKey('Enter', this.triggerDefaultAction)\n\t},\n\n\tmethods: {\n\t\tformatFileSize,\n\n\t\ttriggerDefaultAction(event?: KeyboardEvent) {\n\t\t\t// Don't react to the event if the file row is not active\n\t\t\tif (!this.isActive) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tevent?.preventDefault()\n\t\t\tevent?.stopPropagation()\n\n\t\t\tthis.defaultFileAction?.exec({\n\t\t\t\tnodes: [this.source],\n\t\t\t\tfolder: this.activeFolder!,\n\t\t\t\tcontents: this.nodes,\n\t\t\t\tview: this.activeView!,\n\t\t\t})\n\t\t},\n\n\t\tadaptColumnRenderToCustomElementRender(column) {\n\t\t\treturn ({ nodes, view }) => {\n\t\t\t\treturn column.render(nodes[0], view)\n\t\t\t}\n\t\t},\n\t},\n})\n</script>\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n\n<template>\n\t<tr\n\t\t:class=\"{ 'files-list__row--active': isActive, 'files-list__row--dragover': dragover, 'files-list__row--loading': isLoading }\"\n\t\tdata-cy-files-list-row\n\t\t:data-cy-files-list-row-fileid=\"fileid\"\n\t\t:data-cy-files-list-row-name=\"source.basename\"\n\t\t:draggable=\"canDrag\"\n\t\tclass=\"files-list__row\"\n\t\t@contextmenu=\"onRightClick\"\n\t\t@dragover=\"onDragOver\"\n\t\t@dragleave=\"onDragLeave\"\n\t\t@dragstart=\"onDragStart\"\n\t\t@dragend=\"onDragEnd\"\n\t\t@drop=\"onDrop\">\n\t\t<!-- Failed indicator -->\n\t\t<span v-if=\"isFailedSource\" class=\"files-list__row--failed\" />\n\n\t\t<!-- Checkbox -->\n\t\t<FileEntryCheckbox\n\t\t\t:fileid=\"fileid\"\n\t\t\t:isLoading=\"isLoading\"\n\t\t\t:nodes=\"nodes\"\n\t\t\t:source=\"source\" />\n\n\t\t<!-- Link to file -->\n\t\t<td class=\"files-list__row-name\" data-cy-files-list-row-name>\n\t\t\t<!-- Icon or preview -->\n\t\t\t<FileEntryPreview\n\t\t\t\tref=\"preview\"\n\t\t\t\t:dragover=\"dragover\"\n\t\t\t\t:gridMode=\"true\"\n\t\t\t\t:source=\"source\"\n\t\t\t\t@auxclick=\"execDefaultAction\"\n\t\t\t\t@click=\"execDefaultAction\" />\n\n\t\t\t<FileEntryName\n\t\t\t\tref=\"name\"\n\t\t\t\t:basename=\"basename\"\n\t\t\t\t:extension=\"extension\"\n\t\t\t\t:source=\"source\"\n\t\t\t\t@auxclick=\"execDefaultAction\"\n\t\t\t\t@click=\"execDefaultAction\" />\n\t\t</td>\n\n\t\t<!-- Mtime -->\n\t\t<td\n\t\t\tv-if=\"!compact && isMtimeAvailable\"\n\t\t\t:style=\"mtimeOpacity\"\n\t\t\tclass=\"files-list__row-mtime\"\n\t\t\tdata-cy-files-list-row-mtime\n\t\t\t@click=\"openDetailsIfAvailable\">\n\t\t\t<NcDateTime\n\t\t\t\tv-if=\"mtime\"\n\t\t\t\tignoreSeconds\n\t\t\t\t:timestamp=\"mtime\" />\n\t\t</td>\n\n\t\t<!-- Actions -->\n\t\t<FileEntryActions\n\t\t\tref=\"actions\"\n\t\t\tv-model:opened=\"openedMenu\"\n\t\t\t:class=\"`files-list__row-actions-${uniqueId}`\"\n\t\t\t:gridMode=\"true\"\n\t\t\t:source=\"source\" />\n\t</tr>\n</template>\n\n<script lang=\"ts\">\nimport { storeToRefs } from 'pinia'\nimport { defineComponent } from 'vue'\nimport NcDateTime from '@nextcloud/vue/components/NcDateTime'\nimport FileEntryActions from './FileEntry/FileEntryActions.vue'\nimport FileEntryCheckbox from './FileEntry/FileEntryCheckbox.vue'\nimport FileEntryName from './FileEntry/FileEntryName.vue'\nimport FileEntryPreview from './FileEntry/FileEntryPreview.vue'\nimport { useFileActions } from '../composables/useFileActions.ts'\nimport { useFileListWidth } from '../composables/useFileListWidth.ts'\nimport { useRouteParameters } from '../composables/useRouteParameters.ts'\nimport { useActionsMenuStore } from '../store/actionsmenu.ts'\nimport { useActiveStore } from '../store/active.ts'\nimport { useDragAndDropStore } from '../store/dragging.ts'\nimport { useFilesStore } from '../store/files.ts'\nimport { useRenamingStore } from '../store/renaming.ts'\nimport { useSelectionStore } from '../store/selection.ts'\nimport FileEntryMixin from './FileEntryMixin.ts'\n\nexport default defineComponent({\n\tname: 'FileEntryGrid',\n\n\tcomponents: {\n\t\tFileEntryActions,\n\t\tFileEntryCheckbox,\n\t\tFileEntryName,\n\t\tFileEntryPreview,\n\t\tNcDateTime,\n\t},\n\n\tmixins: [\n\t\tFileEntryMixin,\n\t],\n\n\tinheritAttrs: false,\n\n\t// keep in sync with FileEntry.vue\n\tsetup() {\n\t\tconst actionsMenuStore = useActionsMenuStore()\n\t\tconst draggingStore = useDragAndDropStore()\n\t\tconst filesStore = useFilesStore()\n\t\tconst renamingStore = useRenamingStore()\n\t\tconst selectionStore = useSelectionStore()\n\t\tconst { isNarrow } = useFileListWidth()\n\t\tconst {\n\t\t\tfileId: currentRouteFileId,\n\t\t} = useRouteParameters()\n\n\t\tconst activeStore = useActiveStore()\n\t\tconst {\n\t\t\tactiveFolder,\n\t\t\tactiveNode,\n\t\t\tactiveView,\n\t\t} = storeToRefs(activeStore)\n\n\t\tconst actions = useFileActions()\n\n\t\treturn {\n\t\t\tactions,\n\t\t\tactionsMenuStore,\n\t\t\tactiveFolder,\n\t\t\tactiveNode,\n\t\t\tactiveView,\n\t\t\tcurrentRouteFileId,\n\t\t\tdraggingStore,\n\t\t\tisNarrow,\n\t\t\tfilesStore,\n\t\t\trenamingStore,\n\t\t\tselectionStore,\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tgridMode: true,\n\t\t}\n\t},\n})\n</script>\n","<!--\n - SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n\n<script setup lang=\"ts\">\nimport { t } from '@nextcloud/l10n'\nimport { computed } from 'vue'\nimport NcAvatar from '@nextcloud/vue/components/NcAvatar'\nimport NcChip from '@nextcloud/vue/components/NcChip'\nimport { useFiltersStore } from '../../store/filters.ts'\n\nconst filterStore = useFiltersStore()\nconst activeChips = computed(() => filterStore.activeChips)\n</script>\n\n<template>\n\t<ul\n\t\tv-if=\"activeChips.length > 0\"\n\t\t:class=\"$style.fileListFilterChips\"\n\t\t:aria-label=\"t('files', 'Active filters')\">\n\t\t<li v-for=\"(chip, index) of activeChips\" :key=\"index\">\n\t\t\t<NcChip\n\t\t\t\t:aria-label-close=\"t('files', 'Remove filter')\"\n\t\t\t\t:iconSvg=\"chip.icon\"\n\t\t\t\t:text=\"chip.text\"\n\t\t\t\t@close=\"chip.onclick\">\n\t\t\t\t<template v-if=\"chip.user\" #icon>\n\t\t\t\t\t<NcAvatar\n\t\t\t\t\t\tdisableMenu\n\t\t\t\t\t\thideStatus\n\t\t\t\t\t\t:size=\"24\"\n\t\t\t\t\t\t:user=\"chip.user\" />\n\t\t\t\t</template>\n\t\t\t</NcChip>\n\t\t</li>\n\t</ul>\n</template>\n\n<style module>\n.fileListFilterChips {\n\tdisplay: flex;\n\tgap: var(--default-grid-baseline);\n}\n</style>\n","<!--\n - SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n\n<template>\n\t<NcButton v-if=\"isVisible\" size=\"small\" @click=\"onClick\">\n\t\t{{ t('files', 'Search everywhere') }}\n\t</NcButton>\n</template>\n\n<script setup lang=\"ts\">\nimport { t } from '@nextcloud/l10n'\nimport { computed } from 'vue'\nimport NcButton from '@nextcloud/vue/components/NcButton'\nimport { pinia } from '../../store/index.ts'\nimport { useSearchStore } from '../../store/search.ts'\n\nconst searchStore = useSearchStore(pinia)\n\nconst isVisible = computed(() => searchStore.query.length >= 3 && searchStore.scope === 'filter')\n\n/**\n * Button click handler to make the filtering a global search.\n */\nfunction onClick() {\n\tsearchStore.scope = 'globally'\n}\n</script>\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<div v-show=\"enabled\" :class=\"`files-list__header-${header.id}`\">\n\t\t<span ref=\"mount\" />\n\t</div>\n</template>\n\n<script lang=\"ts\">\nimport type { Folder, IFileListHeader, View } from '@nextcloud/files'\nimport type { PropType } from 'vue'\n\nimport PQueue from 'p-queue'\nimport { logger } from '../utils/logger.ts'\n\n/**\n * This component is used to render custom\n * elements provided by an API. Vue doesn't allow\n * to directly render an HTMLElement, so we can do\n * this magic here.\n */\nexport default {\n\tname: 'FilesListHeader',\n\tprops: {\n\t\theader: {\n\t\t\ttype: Object as PropType<IFileListHeader>,\n\t\t\trequired: true,\n\t\t},\n\n\t\tcurrentFolder: {\n\t\t\ttype: Object as PropType<Folder>,\n\t\t\trequired: true,\n\t\t},\n\n\t\tcurrentView: {\n\t\t\ttype: Object as PropType<View>,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\tsetup() {\n\t\t// Create a queue to ensure that the header is only rendered once at a time\n\t\tconst queue = new PQueue({ concurrency: 1 })\n\n\t\treturn {\n\t\t\tqueue,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tenabled() {\n\t\t\treturn this.header.enabled?.(this.currentFolder, this.currentView) ?? true\n\t\t},\n\t},\n\n\twatch: {\n\t\tenabled(enabled) {\n\t\t\tif (!enabled) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// If the header is enabled, we need to render it\n\t\t\tlogger.debug(`Enabled ${this.header.id} FilesListHeader`, { header: this.header })\n\t\t\tthis.queueUpdate(this.currentFolder, this.currentView)\n\t\t},\n\n\t\tcurrentFolder(folder: Folder) {\n\t\t\t// This method can be used to queue an update of the header\n\t\t\t// It will ensure that the header is only updated once at a time\n\t\t\tthis.queueUpdate(folder, this.currentView)\n\t\t},\n\n\t\tcurrentView(view: View) {\n\t\t\tthis.queueUpdate(this.currentFolder, view)\n\t\t},\n\t},\n\n\tmounted() {\n\t\tlogger.debug(`Mounted ${this.header.id} FilesListHeader`, { header: this.header })\n\t\tconst initialRender = () => this.header.render(this.$refs.mount as HTMLElement, this.currentFolder, this.currentView)\n\t\tthis.queue.add(initialRender).then(() => {\n\t\t\tlogger.debug(`Rendered ${this.header.id} FilesListHeader`, { header: this.header })\n\t\t}).catch((error) => {\n\t\t\tlogger.error(`Error rendering ${this.header.id} FilesListHeader`, { header: this.header, error })\n\t\t})\n\t},\n\n\tunmounted() {\n\t\tlogger.debug(`Destroyed ${this.header.id} FilesListHeader`, { header: this.header })\n\t},\n\n\tmethods: {\n\t\tqueueUpdate(currentFolder: Folder, currentView: View) {\n\t\t\t// This method can be used to queue an update of the header\n\t\t\t// It will ensure that the header is only updated once at a time\n\t\t\tthis.queue.add(() => this.header.updated(currentFolder, currentView))\n\t\t\t\t.then(() => {\n\t\t\t\t\tlogger.debug(`Updated ${this.header.id} FilesListHeader`, { header: this.header })\n\t\t\t\t})\n\t\t\t\t.catch((error) => {\n\t\t\t\t\tlogger.error(`Error updating ${this.header.id} FilesListHeader`, { header: this.header, error })\n\t\t\t\t})\n\t\t},\n\t},\n}\n</script>\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<tr>\n\t\t<th class=\"files-list__row-checkbox\">\n\t\t\t<!-- TRANSLATORS Label for a table footer which summarizes the columns of the table -->\n\t\t\t<span class=\"hidden-visually\">{{ t('files', 'Total rows summary') }}</span>\n\t\t</th>\n\n\t\t<!-- Link to file -->\n\t\t<td class=\"files-list__row-name\">\n\t\t\t<!-- Icon or preview -->\n\t\t\t<span class=\"files-list__row-icon\" />\n\n\t\t\t<!-- Summary -->\n\t\t\t<span>{{ summary }}</span>\n\t\t</td>\n\n\t\t<!-- Actions -->\n\t\t<td class=\"files-list__row-actions\" />\n\n\t\t<!-- Mime -->\n\t\t<td\n\t\t\tv-if=\"isMimeAvailable\"\n\t\t\tclass=\"files-list__column files-list__row-mime\" />\n\n\t\t<!-- Size -->\n\t\t<td\n\t\t\tv-if=\"isSizeAvailable\"\n\t\t\tclass=\"files-list__column files-list__row-size\">\n\t\t\t<span>{{ totalSize }}</span>\n\t\t</td>\n\n\t\t<!-- Mtime -->\n\t\t<td\n\t\t\tv-if=\"isMtimeAvailable\"\n\t\t\tclass=\"files-list__column files-list__row-mtime\" />\n\n\t\t<!-- Custom views columns -->\n\t\t<th\n\t\t\tv-for=\"column in columns\"\n\t\t\t:key=\"column.id\"\n\t\t\t:class=\"classForColumn(column)\">\n\t\t\t<span>{{ column.summary?.(nodes, currentView) }}</span>\n\t\t</th>\n\t</tr>\n</template>\n\n<script setup lang=\"ts\">\nimport type { IColumn, INode, IView } from '@nextcloud/files'\n\nimport { formatFileSize } from '@nextcloud/files'\nimport { t } from '@nextcloud/l10n'\nimport { computed } from 'vue'\nimport { useFileListLayout } from '../composables/useFileListLayout.ts'\nimport { useActiveStore } from '../store/active.ts'\n\nconst props = defineProps<{\n\t/** The current view */\n\tcurrentView: IView\n\n\t/** Whether the mime column is available */\n\tisMimeAvailable: boolean\n\n\t/** Whether the mtime column is available */\n\tisMtimeAvailable: boolean\n\n\t/** Whether the size column is available */\n\tisSizeAvailable: boolean\n\n\t/** The nodes to summarize */\n\tnodes: INode[]\n\n\t/** Summary text */\n\tsummary: string\n}>()\n\nconst activeStore = useActiveStore()\nconst layout = useFileListLayout()\n\nconst currentFolder = computed(() => activeStore.activeFolder)\n\nconst columns = computed(() => layout.value.columns)\n\nconst totalSize = computed(() => {\n\t// If we have the size already, let's use it\n\tif (currentFolder.value?.size) {\n\t\treturn formatFileSize(currentFolder.value.size, true)\n\t}\n\n\t// Otherwise let's compute it\n\treturn formatFileSize(props.nodes.reduce((total, node) => total + (node.size ?? 0), 0), true)\n})\n\n/**\n * Get the CSS classes for a custom column\n *\n * @param column - The column\n */\nfunction classForColumn(column: IColumn) {\n\treturn {\n\t\t'files-list__row-column-custom': true,\n\t\t[`files-list__row-${props.currentView.id}-${column.id}`]: true,\n\t}\n}\n</script>\n\n<style scoped lang=\"scss\">\n// Scoped row\ntr {\n\tmargin-bottom: var(--body-container-margin);\n\tborder-top: 1px solid var(--color-border);\n\t// Prevent hover effect on the whole row\n\tbackground-color: transparent !important;\n\tborder-bottom: none !important;\n\n\ttd {\n\t\tuser-select: none;\n\t\t// Make sure the cell colors don't apply to column headers\n\t\tcolor: var(--color-text-maxcontrast) !important;\n\t}\n}\n</style>\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<div class=\"files-list__column files-list__row-actions-batch\" data-cy-files-list-selection-actions>\n\t\t<NcActions\n\t\t\tref=\"actionsMenu\"\n\t\t\tv-model:open=\"openedMenu\"\n\t\t\tcontainer=\"#app-content-vue\"\n\t\t\t:boundariesElement=\"boundariesElement\"\n\t\t\t:disabled=\"!!loading || areSomeNodesLoading\"\n\t\t\t:forceName=\"true\"\n\t\t\t:inline=\"enabledInlineActions.length\"\n\t\t\t:menuName=\"enabledInlineActions.length <= 1 ? t('files', 'Actions') : undefined\"\n\t\t\t@close=\"openedSubmenu = null\">\n\t\t\t<!-- Default actions list-->\n\t\t\t<NcActionButton\n\t\t\t\tv-for=\"(action) in enabledMenuActions\"\n\t\t\t\t:key=\"action.id\"\n\t\t\t\t:ref=\"`action-batch-${action.id}`\"\n\t\t\t\t:class=\"{\n\t\t\t\t\t[`files-list__row-actions-batch-${action.id}`]: true,\n\t\t\t\t\t[`files-list__row-actions-batch--menu`]: isValidMenu(action),\n\t\t\t\t}\"\n\t\t\t\t:closeAfterClick=\"!isValidMenu(action)\"\n\t\t\t\t:data-cy-files-list-selection-action=\"action.id\"\n\t\t\t\t:isMenu=\"isValidMenu(action)\"\n\t\t\t\t:aria-label=\"action.displayName(actionContext) + ' ' + t('files', '(selected)') /** TRANSLATORS: Selected like 'selected files and folders' */\"\n\t\t\t\t:title=\"action.title?.(actionContext)\"\n\t\t\t\t@click=\"onActionClick(action)\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<NcLoadingIcon v-if=\"loading === action.id\" :size=\"18\" />\n\t\t\t\t\t<NcIconSvgWrapper v-else :svg=\"action.iconSvgInline(actionContext)\" />\n\t\t\t\t</template>\n\t\t\t\t{{ action.displayName(actionContext) }}\n\t\t\t</NcActionButton>\n\n\t\t\t<!-- Submenu actions list-->\n\t\t\t<template v-if=\"openedSubmenu && enabledSubmenuActions[openedSubmenu?.id]\">\n\t\t\t\t<!-- Back to top-level button -->\n\t\t\t\t<NcActionButton class=\"files-list__row-actions-batch-back\" data-cy-files-list-selection-action=\"menu-back\" @click=\"onBackToMenuClick(openedSubmenu)\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<ArrowLeftIcon />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ t('files', 'Back') }}\n\t\t\t\t</NcActionButton>\n\t\t\t\t<NcActionSeparator />\n\n\t\t\t\t<!-- Submenu actions -->\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-for=\"action in enabledSubmenuActions[openedSubmenu?.id]\"\n\t\t\t\t\t:key=\"action.id\"\n\t\t\t\t\t:class=\"`files-list__row-actions-batch-${action.id}`\"\n\t\t\t\t\tclass=\"files-list__row-actions-batch--submenu\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t:data-cy-files-list-selection-action=\"action.id\"\n\t\t\t\t\t:aria-label=\"action.displayName(actionContext) + ' ' + t('files', '(selected)') /** TRANSLATORS: Selected like 'selected files and folders' */\"\n\t\t\t\t\t:title=\"action.title?.(actionContext)\"\n\t\t\t\t\t@click=\"onActionClick(action)\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<NcLoadingIcon v-if=\"loading === action.id\" :size=\"18\" />\n\t\t\t\t\t\t<NcIconSvgWrapper v-else :svg=\"action.iconSvgInline(actionContext)\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ action.displayName(actionContext) }}\n\t\t\t\t</NcActionButton>\n\t\t\t</template>\n\t\t</NcActions>\n\t</div>\n</template>\n\n<script lang=\"ts\">\nimport type { ActionContext, IFileAction, Node, View } from '@nextcloud/files'\nimport type { PropType } from 'vue'\nimport type { FileSource } from '../types.ts'\n\nimport { showError, showSuccess } from '@nextcloud/dialogs'\nimport { DefaultType, NodeStatus } from '@nextcloud/files'\nimport { translate } from '@nextcloud/l10n'\nimport { computed, defineComponent } from 'vue'\nimport NcActionButton from '@nextcloud/vue/components/NcActionButton'\nimport NcActions from '@nextcloud/vue/components/NcActions'\nimport NcActionSeparator from '@nextcloud/vue/components/NcActionSeparator'\nimport NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'\nimport NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'\nimport ArrowLeftIcon from 'vue-material-design-icons/ArrowLeft.vue'\nimport { useFileActions } from '../composables/useFileActions.ts'\nimport { useFileListWidth } from '../composables/useFileListWidth.ts'\nimport actionsMixins from '../mixins/actionsMixin.ts'\nimport { useActionsMenuStore } from '../store/actionsmenu.ts'\nimport { useActiveStore } from '../store/active.ts'\nimport { useFilesStore } from '../store/files.ts'\nimport { useSelectionStore } from '../store/selection.ts'\nimport { logger } from '../utils/logger.ts'\n\nexport default defineComponent({\n\tname: 'FilesListTableHeaderActions',\n\n\tcomponents: {\n\t\tArrowLeftIcon,\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcActionSeparator,\n\t\tNcIconSvgWrapper,\n\t\tNcLoadingIcon,\n\t},\n\n\tmixins: [actionsMixins],\n\n\tprops: {\n\t\tcurrentView: {\n\t\t\ttype: Object as PropType<View>,\n\t\t\trequired: true,\n\t\t},\n\n\t\tselectedNodes: {\n\t\t\ttype: Array as PropType<FileSource[]>,\n\t\t\tdefault: () => ([]),\n\t\t},\n\t},\n\n\tsetup() {\n\t\tconst { activeFolder } = useActiveStore()\n\t\tconst actionsMenuStore = useActionsMenuStore()\n\t\tconst filesStore = useFilesStore()\n\t\tconst selectionStore = useSelectionStore()\n\t\tconst { isMedium, isNarrow } = useFileListWidth()\n\n\t\tconst boundariesElement = document.getElementById('app-content-vue') as HTMLElement\n\n\t\tconst inlineActions = computed(() => {\n\t\t\tif (isNarrow.value) {\n\t\t\t\treturn 0\n\t\t\t}\n\t\t\tif (isMedium.value) {\n\t\t\t\treturn 1\n\t\t\t}\n\t\t\treturn 3\n\t\t})\n\n\t\tconst actions = useFileActions()\n\n\t\treturn {\n\t\t\tactions,\n\t\t\tactionsMenuStore,\n\t\t\tactiveFolder,\n\t\t\tfilesStore,\n\t\t\tselectionStore,\n\n\t\t\tboundariesElement,\n\t\t\tinlineActions,\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tloading: null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tenabledFileActions(): IFileAction[] {\n\t\t\treturn this.actions\n\t\t\t\t// We don't handle renderInline actions in this component\n\t\t\t\t.filter((action) => !action.renderInline)\n\t\t\t\t// We don't handle actions that are not visible\n\t\t\t\t.filter((action) => action.default !== DefaultType.HIDDEN)\n\t\t\t\t// We allow top-level actions that have no execBatch method\n\t\t\t\t// but children actions always need to have it\n\t\t\t\t.filter((action) => action.execBatch || !action.parent)\n\t\t\t\t// We filter out actions that are not enabled for the current selection\n\t\t\t\t.filter((action) => !action.enabled || action.enabled(this.actionContext))\n\t\t\t\t.sort((a, b) => (a.order || 0) - (b.order || 0))\n\t\t},\n\n\t\t/**\n\t\t * Return the list of enabled actions that are\n\t\t * allowed to be rendered inlined.\n\t\t * This means that they are not within a menu, nor\n\t\t * being the parent of submenu actions.\n\t\t */\n\t\tenabledInlineActions(): IFileAction[] {\n\t\t\treturn this.enabledFileActions\n\t\t\t\t// Remove all actions that are not top-level actions\n\t\t\t\t.filter((action) => action.parent === undefined)\n\t\t\t\t// Remove all actions that are not batch actions\n\t\t\t\t.filter((action) => action.execBatch !== undefined)\n\t\t\t\t// Remove all top-menu entries\n\t\t\t\t.filter((action) => !this.isValidMenu(action))\n\t\t\t\t// Return a maximum actions to fit the screen\n\t\t\t\t.slice(0, this.inlineActions)\n\t\t},\n\n\t\t/**\n\t\t * Return the rest of enabled actions that are not\n\t\t * rendered inlined.\n\t\t */\n\t\tenabledMenuActions(): IFileAction[] {\n\t\t\t// If we're in a submenu, only render the inline\n\t\t\t// actions before the filtered submenu\n\t\t\tif (this.openedSubmenu) {\n\t\t\t\treturn this.enabledInlineActions\n\t\t\t}\n\n\t\t\t// We filter duplicates to prevent inline actions to be shown twice\n\t\t\tconst actions = this.enabledFileActions.filter((value, index, self) => {\n\t\t\t\treturn index === self.findIndex((action) => action.id === value.id)\n\t\t\t})\n\n\t\t\t// Generate list of all top-level actions ids\n\t\t\tconst childrenActionsIds = actions\n\t\t\t\t.filter((action) => action.parent)\n\t\t\t\t// Filter out all actions that are not batch actions\n\t\t\t\t.filter((action) => action.execBatch)\n\t\t\t\t.map((action) => action.parent) as string[]\n\n\t\t\tconst menuActions = actions\n\t\t\t\t.filter((action) => {\n\t\t\t\t\t// If the action is not a batch action, we need\n\t\t\t\t\t// to make sure it's a top-level parent entry\n\t\t\t\t\t// and that we have some children actions bound to it\n\t\t\t\t\tif (!action.execBatch) {\n\t\t\t\t\t\treturn childrenActionsIds.includes(action.id)\n\t\t\t\t\t}\n\n\t\t\t\t\t// Rendering second-level actions is done in the template\n\t\t\t\t\t// when openedSubmenu is set.\n\t\t\t\t\tif (action.parent) {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\n\t\t\t\t\treturn true\n\t\t\t\t})\n\t\t\t\t.filter((action) => !this.enabledInlineActions.includes(action))\n\n\t\t\t// Make sure we render the inline actions first\n\t\t\t// and then the rest of the actions.\n\t\t\t// We do NOT want nested actions to be rendered inlined\n\t\t\treturn [...this.enabledInlineActions, ...menuActions]\n\t\t},\n\n\t\tactionContext(): ActionContext {\n\t\t\treturn {\n\t\t\t\tnodes: this.nodes,\n\t\t\t\tview: this.currentView,\n\t\t\t\tfolder: this.activeFolder!,\n\t\t\t\tcontents: this.nodes,\n\t\t\t}\n\t\t},\n\n\t\tnodes() {\n\t\t\treturn this.selectedNodes\n\t\t\t\t.map((source) => this.getNode(source))\n\t\t\t\t.filter(Boolean) as Node[]\n\t\t},\n\n\t\tareSomeNodesLoading() {\n\t\t\treturn this.nodes.some((node) => node.status === NodeStatus.LOADING)\n\t\t},\n\n\t\topenedMenu: {\n\t\t\tget() {\n\t\t\t\treturn this.actionsMenuStore.opened === 'global'\n\t\t\t},\n\n\t\t\tset(opened) {\n\t\t\t\tthis.actionsMenuStore.opened = opened ? 'global' : null\n\t\t\t},\n\t\t},\n\t},\n\n\tmethods: {\n\t\t/**\n\t\t * Get a cached note from the store\n\t\t *\n\t\t * @param source The source of the node to get\n\t\t */\n\t\tgetNode(source: string): Node | undefined {\n\t\t\treturn this.filesStore.getNode(source)\n\t\t},\n\n\t\tasync onActionClick(action) {\n\t\t\t// If the action is a submenu, we open it\n\t\t\tif (this.enabledSubmenuActions[action.id]) {\n\t\t\t\tthis.openedSubmenu = action\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlet displayName = action.id\n\t\t\ttry {\n\t\t\t\tdisplayName = action.displayName(this.actionContext)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error('Error while getting action display name', { action, error })\n\t\t\t}\n\n\t\t\tconst selectionSources = this.selectedNodes\n\t\t\ttry {\n\t\t\t\t// Set loading markers\n\t\t\t\tthis.loading = action.id\n\t\t\t\tthis.nodes.forEach((node) => {\n\t\t\t\t\tnode.status = NodeStatus.LOADING\n\t\t\t\t})\n\n\t\t\t\t// Dispatch action execution\n\t\t\t\tconst results = await action.execBatch(this.actionContext)\n\n\t\t\t\t// Check if all actions returned null\n\t\t\t\tif (!results.some((result) => result !== null)) {\n\t\t\t\t\t// If the actions returned null, we stay silent\n\t\t\t\t\tthis.selectionStore.reset()\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Handle potential failures\n\t\t\t\tif (results.some((result) => result === false)) {\n\t\t\t\t\t// Remove the failed ids from the selection\n\t\t\t\t\tconst failedSources = selectionSources\n\t\t\t\t\t\t.filter((source, index) => results[index] === false)\n\t\t\t\t\tthis.selectionStore.set(failedSources)\n\n\t\t\t\t\tif (results.some((result) => result === null)) {\n\t\t\t\t\t\t// If some actions returned null, we assume that the dev\n\t\t\t\t\t\t// is handling the error messages and we stay silent\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\n\t\t\t\t\tshowError(this.t('files', '{displayName}: failed on some elements', { displayName }))\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Show success message and clear selection\n\t\t\t\tshowSuccess(this.t('files', '{displayName}: done', { displayName }))\n\t\t\t\tthis.selectionStore.reset()\n\t\t\t} catch (e) {\n\t\t\t\tlogger.error('Error while executing action', { action, e })\n\t\t\t\tshowError(this.t('files', '{displayName}: failed', { displayName }))\n\t\t\t} finally {\n\t\t\t\t// Remove loading markers\n\t\t\t\tthis.loading = null\n\t\t\t\tthis.nodes.forEach((node) => {\n\t\t\t\t\tnode.status = undefined\n\t\t\t\t})\n\t\t\t}\n\t\t},\n\n\t\tt: translate,\n\t},\n})\n</script>\n\n<style scoped lang=\"scss\">\n.files-list__row-actions-batch {\n\tflex: 1 1 100% !important;\n\tmax-width: 100%;\n}\n</style>\n","/*!\n * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport { computed } from 'vue'\nimport { useActiveStore } from '../store/active.ts'\nimport { useViewConfigStore } from '../store/viewConfig.ts'\n\n/**\n * Composable to get and set the sorting mode and direction for the current view.\n */\nexport function useFilesSorting() {\n\tconst activeStore = useActiveStore()\n\tconst viewConfigStore = useViewConfigStore()\n\n\t/**\n\t * Get the sorting mode for the current view\n\t */\n\tconst sortingMode = computed(() => {\n\t\tif (!activeStore.activeView) {\n\t\t\treturn 'basename'\n\t\t}\n\n\t\treturn viewConfigStore.getConfig(activeStore.activeView.id)?.sorting_mode as string\n\t\t\t|| activeStore.activeView?.defaultSortKey\n\t\t\t|| 'basename'\n\t})\n\n\t/**\n\t * Get the sorting direction for the current view\n\t */\n\tconst isAscSorting = computed(() => {\n\t\tif (!activeStore.activeView) {\n\t\t\treturn true\n\t\t}\n\n\t\tconst sortingDirection = viewConfigStore.getConfig(activeStore.activeView.id)?.sorting_direction\n\t\treturn sortingDirection !== 'desc'\n\t})\n\n\t/**\n\t * Toogle sorting by a given key.\n\t *\n\t * If we're already sorting by this key, toggle the direction.\n\t * Otherwise, sort ASC by this key.\n\t *\n\t * @param key - The key to sort by\n\t */\n\tfunction toggleSortBy(key: string) {\n\t\t// If we're already sorting by this key, flip the direction\n\t\tif (sortingMode.value === key) {\n\t\t\tviewConfigStore.toggleSortingDirection(activeStore.activeView?.id)\n\t\t\treturn\n\t\t}\n\t\t// else sort ASC by this new key\n\t\tviewConfigStore.setSortingBy(key, activeStore.activeView?.id)\n\t}\n\n\treturn {\n\t\ttoggleSortBy,\n\t\tsortingMode,\n\t\tisAscSorting,\n\t}\n}\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<NcButton\n\t\tclass=\"files-list__column-sort-button\"\n\t\t:class=\"[{\n\t\t\t'files-list__column-sort-button--active': sortingMode === mode,\n\t\t\t'files-list__column-sort-button--size': sortingMode === 'size',\n\t\t}]\"\n\t\t:alignment=\"mode === 'size' ? 'end' : 'start-reverse'\"\n\t\tvariant=\"tertiary\"\n\t\t:title=\"name\"\n\t\t@click=\"toggleSortBy(mode)\">\n\t\t<template #icon>\n\t\t\t<MenuUp v-if=\"sortingMode !== mode || isAscSorting\" class=\"files-list__column-sort-button-icon\" />\n\t\t\t<MenuDown v-else class=\"files-list__column-sort-button-icon\" />\n\t\t</template>\n\t\t<span class=\"files-list__column-sort-button-text\">{{ name }}</span>\n\t</NcButton>\n</template>\n\n<script setup lang=\"ts\">\nimport NcButton from '@nextcloud/vue/components/NcButton'\nimport MenuDown from 'vue-material-design-icons/MenuDown.vue'\nimport MenuUp from 'vue-material-design-icons/MenuUp.vue'\nimport { useFilesSorting } from '../composables/filesSorting.ts'\n\ndefineProps<{\n\t/**\n\t * The display name of the column this button represents.\n\t */\n\tname: string\n\t/**\n\t * The sorting mode this button represents.\n\t */\n\tmode: string\n}>()\n\nconst {\n\tsortingMode,\n\tisAscSorting,\n\ttoggleSortBy,\n} = useFilesSorting()\n</script>\n\n<style scoped lang=\"scss\">\n.files-list__column-sort-button {\n\t// Compensate for cells margin\n\tmargin: 0 calc(var(--button-padding, var(--cell-margin)) * -1);\n\tmin-width: calc(100% - 3 * var(--cell-margin))!important;\n\n\t&-text {\n\t\tcolor: var(--color-text-maxcontrast);\n\t\tfont-weight: normal;\n\t}\n\n\t&-icon {\n\t\tcolor: var(--color-text-maxcontrast);\n\t\topacity: 0;\n\t\ttransition: opacity var(--animation-quick);\n\t\tinset-inline-start: -10px;\n\t}\n\n\t&--size &-icon {\n\t\tinset-inline-start: 10px;\n\t}\n\n\t&--active &-icon,\n\t&:hover &-icon,\n\t&:focus &-icon,\n\t&:active &-icon {\n\t\topacity: 1;\n\t}\n}\n</style>\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<tr\n\t\tclass=\"files-list__row-head\"\n\t\t:class=\"{ 'files-list__row-head--selected': !isNoneSelected }\">\n\t\t<th\n\t\t\tclass=\"files-list__column files-list__row-checkbox\"\n\t\t\t@keyup.esc.exact=\"resetSelection\">\n\t\t\t<NcCheckboxRadioSwitch\n\t\t\t\tv-bind=\"selectAllBind\"\n\t\t\t\tdata-cy-files-list-selection-checkbox\n\t\t\t\t@update:modelValue=\"onToggleAll\" />\n\n\t\t\t<!-- Batch selection UI lives in the select-all cell so column headers\n\t\t\t stay valid and Tab order stays in document flow\n\t\t\t (select-all → actions → rows). -->\n\t\t\t<template v-if=\"!isNoneSelected\">\n\t\t\t\t<span class=\"files-list__selected\">\n\t\t\t\t\t{{ n('files', '{count} selected', '{count} selected', selectedNodes.length, { count: selectedNodes.length }) }}\n\t\t\t\t</span>\n\t\t\t\t<FilesListTableHeaderActions\n\t\t\t\t\t:currentView=\"currentView\"\n\t\t\t\t\t:selectedNodes=\"selectedNodes\" />\n\t\t\t</template>\n\t\t</th>\n\n\t\t<!-- Columns display (always present so headers match body cells;\n\t\t visually hidden while a selection is active) -->\n\n\t\t<!-- Link to file -->\n\t\t<th\n\t\t\tclass=\"files-list__column files-list__row-name files-list__column--sortable\"\n\t\t\t:class=\"{ 'hidden-visually': !isNoneSelected }\"\n\t\t\t:aria-sort=\"ariaSortForMode('basename')\"\n\t\t\t:inert=\"!isNoneSelected\">\n\t\t\t<!-- Icon or preview -->\n\t\t\t<span class=\"files-list__row-icon\" />\n\n\t\t\t<!-- Name -->\n\t\t\t<FilesListTableHeaderButton :name=\"t('files', 'Name')\" mode=\"basename\" />\n\t\t</th>\n\n\t\t<!-- Actions -->\n\t\t<th\n\t\t\tclass=\"files-list__row-actions\"\n\t\t\t:class=\"{ 'hidden-visually': !isNoneSelected }\"\n\t\t\t:inert=\"!isNoneSelected\">\n\t\t\t<span class=\"hidden-visually\">\n\t\t\t\t{{ t('files', 'Actions') }}\n\t\t\t</span>\n\t\t</th>\n\n\t\t<!-- Mime -->\n\t\t<th\n\t\t\tv-if=\"isMimeAvailable\"\n\t\t\tclass=\"files-list__column files-list__row-mime\"\n\t\t\t:class=\"{\n\t\t\t\t'files-list__column--sortable': isMimeAvailable,\n\t\t\t\t'hidden-visually': !isNoneSelected,\n\t\t\t}\"\n\t\t\t:aria-sort=\"ariaSortForMode('mime')\"\n\t\t\t:inert=\"!isNoneSelected\">\n\t\t\t<FilesListTableHeaderButton :name=\"t('files', 'File type')\" mode=\"mime\" />\n\t\t</th>\n\n\t\t<!-- Size -->\n\t\t<th\n\t\t\tv-if=\"isSizeAvailable\"\n\t\t\tclass=\"files-list__column files-list__row-size\"\n\t\t\t:class=\"{\n\t\t\t\t'files-list__column--sortable': isSizeAvailable,\n\t\t\t\t'hidden-visually': !isNoneSelected,\n\t\t\t}\"\n\t\t\t:aria-sort=\"ariaSortForMode('size')\"\n\t\t\t:inert=\"!isNoneSelected\">\n\t\t\t<FilesListTableHeaderButton :name=\"t('files', 'Size')\" mode=\"size\" />\n\t\t</th>\n\n\t\t<!-- Mtime -->\n\t\t<th\n\t\t\tv-if=\"isMtimeAvailable\"\n\t\t\tclass=\"files-list__column files-list__row-mtime\"\n\t\t\t:class=\"{\n\t\t\t\t'files-list__column--sortable': isMtimeAvailable,\n\t\t\t\t'hidden-visually': !isNoneSelected,\n\t\t\t}\"\n\t\t\t:aria-sort=\"ariaSortForMode('mtime')\"\n\t\t\t:inert=\"!isNoneSelected\">\n\t\t\t<FilesListTableHeaderButton :name=\"t('files', 'Modified')\" mode=\"mtime\" />\n\t\t</th>\n\n\t\t<!-- Custom views columns -->\n\t\t<th\n\t\t\tv-for=\"column in columns\"\n\t\t\t:key=\"column.id\"\n\t\t\t:class=\"classForColumn(column)\"\n\t\t\t:aria-sort=\"ariaSortForMode(column.id)\"\n\t\t\t:inert=\"!isNoneSelected\">\n\t\t\t<FilesListTableHeaderButton v-if=\"!!column.sort\" :name=\"column.title\" :mode=\"column.id\" />\n\t\t\t<span v-else>\n\t\t\t\t{{ column.title }}\n\t\t\t</span>\n\t\t</th>\n\t</tr>\n</template>\n\n<script lang=\"ts\">\nimport type { Node, View } from '@nextcloud/files'\nimport type { PropType } from 'vue'\nimport type { FileSource } from '../types.ts'\n\nimport { n, t } from '@nextcloud/l10n'\nimport { useHotKey } from '@nextcloud/vue/composables/useHotKey'\nimport { defineComponent } from 'vue'\nimport NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'\nimport FilesListTableHeaderActions from './FilesListTableHeaderActions.vue'\nimport FilesListTableHeaderButton from './FilesListTableHeaderButton.vue'\nimport { useFilesSorting } from '../composables/filesSorting.ts'\nimport { useFileListLayout } from '../composables/useFileListLayout.ts'\nimport { useRouteParameters } from '../composables/useRouteParameters.ts'\nimport { useActiveStore } from '../store/active.ts'\nimport { useFilesStore } from '../store/files.ts'\nimport { useSelectionStore } from '../store/selection.ts'\nimport { logger } from '../utils/logger.ts'\n\nexport default defineComponent({\n\tname: 'FilesListTableHeader',\n\n\tcomponents: {\n\t\tFilesListTableHeaderActions,\n\t\tFilesListTableHeaderButton,\n\t\tNcCheckboxRadioSwitch,\n\t},\n\n\tprops: {\n\t\tcurrentView: {\n\t\t\ttype: Object as PropType<View>,\n\t\t\trequired: true,\n\t\t},\n\n\t\tisMimeAvailable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\tisMtimeAvailable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\tisSizeAvailable: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\tnodes: {\n\t\t\ttype: Array as PropType<Node[]>,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\tsetup() {\n\t\tconst activeStore = useActiveStore()\n\t\tconst filesStore = useFilesStore()\n\t\tconst selectionStore = useSelectionStore()\n\t\tconst { directory } = useRouteParameters()\n\n\t\tconst layout = useFileListLayout()\n\n\t\treturn {\n\t\t\tactiveStore,\n\t\t\tfilesStore,\n\t\t\tselectionStore,\n\n\t\t\tdirectory,\n\t\t\tlayout,\n\n\t\t\t...useFilesSorting(),\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tcolumns() {\n\t\t\treturn this.layout.columns\n\t\t},\n\n\t\tdir() {\n\t\t\t// Remove any trailing slash but leave root slash\n\t\t\treturn this.directory.replace(/^(.+)\\/$/, '$1')\n\t\t},\n\n\t\tselectAllBind() {\n\t\t\tconst label = t('files', 'Toggle selection for all files and folders')\n\t\t\treturn {\n\t\t\t\t'aria-label': label,\n\t\t\t\tindeterminate: this.isSomeSelected,\n\t\t\t\tmodelValue: this.isAllSelected,\n\t\t\t\ttitle: label,\n\t\t\t}\n\t\t},\n\n\t\tselectedNodes() {\n\t\t\treturn this.selectionStore.selected\n\t\t},\n\n\t\tisAllSelected() {\n\t\t\treturn this.selectedNodes.length === this.nodes.length\n\t\t},\n\n\t\tisNoneSelected() {\n\t\t\treturn this.selectedNodes.length === 0\n\t\t},\n\n\t\tisSomeSelected() {\n\t\t\treturn !this.isAllSelected && !this.isNoneSelected\n\t\t},\n\t},\n\n\tcreated() {\n\t\t// ctrl+a selects all\n\t\tuseHotKey('a', this.onToggleAll, {\n\t\t\tctrl: true,\n\t\t\tstop: true,\n\t\t\tprevent: true,\n\t\t})\n\n\t\t// Escape key cancels selection\n\t\tuseHotKey('Escape', this.resetSelection, {\n\t\t\tstop: true,\n\t\t\tprevent: true,\n\t\t})\n\t},\n\n\tmethods: {\n\t\tariaSortForMode(mode: string): 'ascending' | 'descending' | undefined {\n\t\t\tif (this.sortingMode === mode) {\n\t\t\t\treturn this.isAscSorting ? 'ascending' : 'descending'\n\t\t\t}\n\t\t},\n\n\t\tclassForColumn(column) {\n\t\t\treturn {\n\t\t\t\t'files-list__column': true,\n\t\t\t\t'files-list__column--sortable': !!column.sort,\n\t\t\t\t'files-list__row-column-custom': true,\n\t\t\t\t[`files-list__row-${this.activeStore.activeView?.id}-${column.id}`]: true,\n\t\t\t\t'hidden-visually': !this.isNoneSelected,\n\t\t\t}\n\t\t},\n\n\t\tonToggleAll(selected = true) {\n\t\t\tif (selected) {\n\t\t\t\tconst selection = this.nodes.map((node) => node.source).filter(Boolean) as FileSource[]\n\t\t\t\tlogger.debug('Added all nodes to selection', { selection })\n\t\t\t\tthis.selectionStore.setLastIndex(null)\n\t\t\t\tthis.selectionStore.set(selection)\n\t\t\t} else {\n\t\t\t\tlogger.debug('Cleared selection')\n\t\t\t\tthis.selectionStore.reset()\n\t\t\t}\n\t\t},\n\n\t\tresetSelection() {\n\t\t\tif (this.isNoneSelected) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.selectionStore.reset()\n\t\t},\n\n\t\tn,\n\t\tt,\n\t},\n})\n</script>\n\n<style scoped lang=\"scss\">\n.files-list__column {\n\tuser-select: none;\n\t// Make sure the cell colors don't apply to column headers\n\tcolor: var(--color-text-maxcontrast) !important;\n\n\t&--sortable {\n\t\tcursor: pointer;\n\t}\n}\n\n.files-list__selected {\n\tpadding-inline-end: 12px;\n\twhite-space: nowrap;\n\tfont-variant-numeric: tabular-nums;\n\tflex-shrink: 0;\n\tcolor: var(--color-main-text);\n}\n</style>\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n -->\n<template>\n\t<div\n\t\tclass=\"files-list\"\n\t\t:class=\"{ 'files-list--grid': gridMode }\"\n\t\tdata-cy-files-list\n\t\t@scroll.passive=\"onScroll\">\n\t\t<!-- Header -->\n\t\t<div ref=\"before\" class=\"files-list__before\">\n\t\t\t<slot name=\"before\" />\n\t\t</div>\n\n\t\t<div ref=\"filters\" class=\"files-list__filters\">\n\t\t\t<slot name=\"filters\" />\n\t\t</div>\n\n\t\t<div\n\t\t\tv-if=\"dataSources.length === 0\"\n\t\t\tclass=\"files-list__empty\">\n\t\t\t<slot name=\"empty\" />\n\t\t</div>\n\n\t\t<table\n\t\t\t:aria-hidden=\"dataSources.length === 0\"\n\t\t\t:inert=\"dataSources.length === 0\"\n\t\t\tclass=\"files-list__table\"\n\t\t\t:class=\"{\n\t\t\t\t'files-list__table--hidden': dataSources.length === 0,\n\t\t\t}\">\n\t\t\t<!-- Accessibility table caption for screen readers -->\n\t\t\t<caption v-if=\"caption\" class=\"hidden-visually\">\n\t\t\t\t{{ caption }}\n\t\t\t</caption>\n\n\t\t\t<!-- Header -->\n\t\t\t<thead ref=\"thead\" class=\"files-list__thead\" data-cy-files-list-thead>\n\t\t\t\t<slot name=\"header\" />\n\t\t\t</thead>\n\n\t\t\t<!-- Body -->\n\t\t\t<tbody\n\t\t\t\t:style=\"tbodyStyle\"\n\t\t\t\tclass=\"files-list__tbody\"\n\t\t\t\tdata-cy-files-list-tbody>\n\t\t\t\t<component\n\t\t\t\t\t:is=\"dataComponent\"\n\t\t\t\t\tv-for=\"({ key, item }, i) in renderedItems\"\n\t\t\t\t\t:key=\"key\"\n\t\t\t\t\t:source=\"item\"\n\t\t\t\t\t:index=\"i\"\n\t\t\t\t\tv-bind=\"extraProps\" />\n\t\t\t</tbody>\n\n\t\t\t<!-- Footer -->\n\t\t\t<tfoot\n\t\t\t\tref=\"footer\"\n\t\t\t\tclass=\"files-list__tfoot\"\n\t\t\t\tdata-cy-files-list-tfoot>\n\t\t\t\t<slot name=\"footer\" />\n\t\t\t</tfoot>\n\t\t</table>\n\t</div>\n</template>\n\n<script lang=\"ts\">\nimport type { File, Folder, Node } from '@nextcloud/files'\nimport type { PropType } from 'vue'\n\nimport debounce from 'debounce'\nimport { defineComponent } from 'vue'\nimport { useFileListWidth } from '../composables/useFileListWidth.ts'\nimport { logger } from '../utils/logger.ts'\n\ninterface RecycledPoolItem {\n\tkey: string\n\titem: Node\n}\n\ntype DataSource = File | Folder\ntype DataSourceKey = keyof DataSource\n\nexport default defineComponent({\n\tname: 'VirtualList',\n\n\tprops: {\n\t\tdataComponent: {\n\t\t\ttype: [Object, Function],\n\t\t\trequired: true,\n\t\t},\n\n\t\tdataKey: {\n\t\t\ttype: String as PropType<DataSourceKey>,\n\t\t\trequired: true,\n\t\t},\n\n\t\tdataSources: {\n\t\t\ttype: Array as PropType<DataSource[]>,\n\t\t\trequired: true,\n\t\t},\n\n\t\textraProps: {\n\t\t\ttype: Object as PropType<Record<string, unknown>>,\n\t\t\tdefault: () => ({}),\n\t\t},\n\n\t\tscrollToIndex: {\n\t\t\ttype: Number,\n\t\t\tdefault: 0,\n\t\t},\n\n\t\tgridMode: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Visually hidden caption for the table accessibility\n\t\t */\n\t\tcaption: {\n\t\t\ttype: String,\n\t\t\tdefault: '',\n\t\t},\n\t},\n\n\tsetup() {\n\t\tconst { width: fileListWidth } = useFileListWidth()\n\n\t\treturn {\n\t\t\tfileListWidth,\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tindex: this.scrollToIndex,\n\t\t\tbeforeHeight: 0,\n\t\t\tfooterHeight: 0,\n\t\t\theaderHeight: 0,\n\t\t\ttableHeight: 0,\n\t\t\tresizeObserver: null as ResizeObserver | null,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t// Wait for measurements to be done before rendering\n\t\tisReady() {\n\t\t\treturn this.tableHeight > 0\n\t\t},\n\n\t\t// Items to render before and after the visible area\n\t\tbufferItems() {\n\t\t\tif (this.gridMode) {\n\t\t\t\t// 1 row before and after in grid mode\n\t\t\t\treturn this.columnCount\n\t\t\t}\n\t\t\t// 3 rows before and after\n\t\t\treturn 3\n\t\t},\n\n\t\titemHeight() {\n\t\t\t// Align with css in FilesListVirtual\n\t\t\t// 166px + 32px (name) + 16px (mtime) + 16px (padding top and bottom)\n\t\t\treturn this.gridMode ? (166 + 32 + 16 + 16 + 16) : 44\n\t\t},\n\n\t\t// Grid mode only\n\t\titemWidth() {\n\t\t\t// 166px + 16px x 2 (padding left and right)\n\t\t\treturn 166 + 16 + 16\n\t\t},\n\n\t\t/**\n\t\t * The number of rows currently (fully!) visible\n\t\t */\n\t\tvisibleRows(): number {\n\t\t\treturn Math.floor((this.tableHeight - this.headerHeight) / this.itemHeight)\n\t\t},\n\n\t\t/**\n\t\t * Number of rows that will be rendered.\n\t\t * This includes only visible + buffer rows.\n\t\t */\n\t\trowCount(): number {\n\t\t\treturn this.visibleRows + (this.bufferItems / this.columnCount) * 2 + 1\n\t\t},\n\n\t\t/**\n\t\t * Number of columns.\n\t\t * 1 for list view otherwise depending on the file list width.\n\t\t */\n\t\tcolumnCount(): number {\n\t\t\tif (!this.gridMode) {\n\t\t\t\treturn 1\n\t\t\t}\n\t\t\treturn Math.floor(this.fileListWidth / this.itemWidth)\n\t\t},\n\n\t\t/**\n\t\t * Index of the first item to be rendered\n\t\t * The index can be any file, not just the first one\n\t\t * But the start index is the first item to be rendered,\n\t\t * which needs to align with the column count\n\t\t */\n\t\tstartIndex() {\n\t\t\tconst firstColumnIndex = this.index - (this.index % this.columnCount)\n\t\t\treturn Math.max(0, firstColumnIndex - this.bufferItems)\n\t\t},\n\n\t\t/**\n\t\t * Number of items to be rendered at the same time\n\t\t * For list view this is the same as `rowCount`, for grid view this is `rowCount` * `columnCount`\n\t\t */\n\t\tshownItems() {\n\t\t\t// If in grid mode, we need to multiply the number of rows by the number of columns\n\t\t\tif (this.gridMode) {\n\t\t\t\treturn this.rowCount * this.columnCount\n\t\t\t}\n\n\t\t\treturn this.rowCount\n\t\t},\n\n\t\trenderedItems(): RecycledPoolItem[] {\n\t\t\tif (!this.isReady) {\n\t\t\t\treturn []\n\t\t\t}\n\n\t\t\tconst items = this.dataSources.slice(this.startIndex, this.startIndex + this.shownItems) as Node[]\n\n\t\t\tconst oldItems = items.filter((item) => Object.values(this.$_recycledPool).includes(item[this.dataKey]))\n\t\t\tconst oldItemsKeys = oldItems.map((item) => item[this.dataKey] as string)\n\t\t\tconst unusedKeys = Object.keys(this.$_recycledPool).filter((key) => !oldItemsKeys.includes(this.$_recycledPool[key]))\n\n\t\t\treturn items.map((item) => {\n\t\t\t\tconst index = Object.values(this.$_recycledPool).indexOf(item[this.dataKey])\n\t\t\t\t// If defined, let's keep the key\n\t\t\t\tif (index !== -1) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkey: Object.keys(this.$_recycledPool)[index],\n\t\t\t\t\t\titem,\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Get and consume reusable key or generate a new one\n\t\t\t\tconst key = unusedKeys.pop() || Math.random().toString(36).substr(2)\n\t\t\t\tthis.$_recycledPool[key] = item[this.dataKey]\n\t\t\t\treturn { key, item }\n\t\t\t})\n\t\t},\n\n\t\t/**\n\t\t * The total number of rows that are available\n\t\t */\n\t\ttotalRowCount() {\n\t\t\treturn Math.ceil(this.dataSources.length / this.columnCount)\n\t\t},\n\n\t\ttbodyStyle() {\n\t\t\t// The number of (virtual) rows above the currently rendered ones.\n\t\t\t// start index is aligned so this should always be an integer\n\t\t\tconst rowsAbove = Math.round(this.startIndex / this.columnCount)\n\t\t\t// The number of (virtual) rows below the currently rendered ones.\n\t\t\tconst rowsBelow = Math.max(0, this.totalRowCount - rowsAbove - this.rowCount)\n\n\t\t\treturn {\n\t\t\t\tpaddingBlock: `${rowsAbove * this.itemHeight}px ${rowsBelow * this.itemHeight}px`,\n\t\t\t\tminHeight: `${this.totalRowCount * this.itemHeight}px`,\n\t\t\t}\n\t\t},\n\t},\n\n\twatch: {\n\t\tscrollToIndex(index) {\n\t\t\tthis.scrollTo(index)\n\t\t},\n\n\t\ttotalRowCount() {\n\t\t\tif (this.scrollToIndex) {\n\t\t\t\tthis.scrollTo(this.scrollToIndex)\n\t\t\t}\n\t\t},\n\n\t\tcolumnCount(columnCount, oldColumnCount) {\n\t\t\tif (oldColumnCount === 0) {\n\t\t\t\t// We're initializing, the scroll position is handled on mounted\n\t\t\t\tlogger.debug('VirtualList: columnCount is 0, skipping scroll')\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// If the column count changes in grid view,\n\t\t\t// update the scroll position again\n\t\t\tthis.scrollTo(this.index)\n\t\t},\n\t},\n\n\tmounted() {\n\t\tthis.$_recycledPool = {} as Record<string, DataSource[DataSourceKey]>\n\n\t\tthis.resizeObserver = new ResizeObserver(debounce(() => {\n\t\t\tthis.updateHeightVariables()\n\t\t\tlogger.debug('VirtualList: resizeObserver updated')\n\t\t\tthis.onScroll()\n\t\t}, 100))\n\t\tthis.resizeObserver.observe(this.$el)\n\t\tthis.resizeObserver.observe(this.$refs.before as HTMLElement)\n\t\tthis.resizeObserver.observe(this.$refs.filters as HTMLElement)\n\t\tthis.resizeObserver.observe(this.$refs.footer as HTMLElement)\n\n\t\tthis.$nextTick(() => {\n\t\t\t// Make sure height values are initialized\n\t\t\tthis.updateHeightVariables()\n\t\t\t// If we need to scroll to an index we do so in the next tick.\n\t\t\t// This is needed to apply updates from the initialization of the height variables\n\t\t\t// which will update the tbody styles until next tick.\n\t\t\tif (this.scrollToIndex) {\n\t\t\t\tthis.scrollTo(this.scrollToIndex)\n\t\t\t}\n\t\t})\n\t},\n\n\tbeforeUnmount() {\n\t\tif (this.resizeObserver) {\n\t\t\tthis.resizeObserver.disconnect()\n\t\t}\n\t},\n\n\tmethods: {\n\t\tscrollTo(index: number) {\n\t\t\tif (!this.$el || this.index === index) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Skip scrolling if the target index is already visible\n\t\t\tconst lastVisibleIndex = this.index + (this.visibleRows * this.columnCount) - 1\n\t\t\tif (index >= this.index && index <= lastVisibleIndex) {\n\t\t\t\tlogger.debug('VirtualList: Skip scrolling, index already visible', { index })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Check if the content is smaller (not equal! keep the footer in mind) than the viewport\n\t\t\t// meaning there is no scrollbar\n\t\t\tif (this.totalRowCount < this.visibleRows) {\n\t\t\t\tlogger.debug('VirtualList: Skip scrolling, nothing to scroll', {\n\t\t\t\t\tindex,\n\t\t\t\t\ttotalRows: this.totalRowCount,\n\t\t\t\t\tvisibleRows: this.visibleRows,\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// We can not scroll further as the last page of rows\n\t\t\t// For the grid view we also need to account for all columns in that row (columnCount - 1)\n\t\t\tconst clampedIndex = (this.totalRowCount - this.visibleRows) * this.columnCount + (this.columnCount - 1)\n\t\t\t// The scroll position\n\t\t\tlet scrollTop = this.indexToScrollPos(Math.min(index, clampedIndex))\n\n\t\t\t// First we need to update the internal index for rendering.\n\t\t\t// This will cause the <tbody> element to be resized allowing us to set the correct scroll position.\n\t\t\tthis.index = index\n\n\t\t\t// If this is not the first row we can add a half row from above.\n\t\t\t// This is to help users understand the table is scrolled and not items did not just disappear.\n\t\t\t// But we also can only add a half row if we have enough rows below to scroll (visual rows / end of scrollable area)\n\t\t\tif (index >= this.columnCount && index <= clampedIndex) {\n\t\t\t\tscrollTop -= (this.itemHeight / 2)\n\t\t\t\t// As we render one half row more we also need to adjust the internal index\n\t\t\t\tthis.index = index - this.columnCount\n\t\t\t} else if (index > clampedIndex) {\n\t\t\t\t// If we are on the last page we cannot scroll any further\n\t\t\t\t// but we can at least scroll the footer into view\n\t\t\t\tif (index <= (clampedIndex + this.columnCount)) {\n\t\t\t\t\t// We only show have of the footer for the first of the last page\n\t\t\t\t\t// To still show the previous row partly. Same reasoning as above:\n\t\t\t\t\t// help the user understand that the table is scrolled not \"magically trimmed\"\n\t\t\t\t\tscrollTop += this.footerHeight / 2\n\t\t\t\t} else {\n\t\t\t\t\t// We reached the very end of the files list and we are focussing not the first visible row\n\t\t\t\t\t// so all we now can do is scroll to the end (footer)\n\t\t\t\t\tscrollTop += this.footerHeight\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Now we need to wait for the <tbody> element to get resized so we can correctly apply the scrollTop position\n\t\t\tthis.$nextTick(() => {\n\t\t\t\tthis.$el.scrollTop = scrollTop\n\t\t\t\tlogger.debug(`VirtualList: scrolling to index ${index}`, {\n\t\t\t\t\tclampedIndex,\n\t\t\t\t\tscrollTop,\n\t\t\t\t\tcolumnCount: this.columnCount,\n\t\t\t\t\ttotal: this.totalRowCount,\n\t\t\t\t\tvisibleRows: this.visibleRows,\n\t\t\t\t\tbeforeHeight: this.beforeHeight,\n\t\t\t\t})\n\t\t\t})\n\t\t},\n\n\t\tonScroll() {\n\t\t\tthis._onScrollHandle ??= requestAnimationFrame(() => {\n\t\t\t\tthis._onScrollHandle = null\n\n\t\t\t\tconst index = this.scrollPosToIndex(this.$el.scrollTop)\n\t\t\t\tif (index === this.index) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\t// Max 0 to prevent negative index\n\t\t\t\tthis.index = Math.max(0, Math.floor(index))\n\t\t\t\tthis.$emit('scroll')\n\t\t\t})\n\t\t},\n\n\t\t// Convert scroll position to index\n\t\t// It should be the opposite of `indexToScrollPos`\n\t\tscrollPosToIndex(scrollPos: number): number {\n\t\t\tconst topScroll = scrollPos - this.beforeHeight\n\t\t\t// Max 0 to prevent negative index\n\t\t\treturn Math.max(0, Math.floor(topScroll / this.itemHeight)) * this.columnCount\n\t\t},\n\n\t\t// Convert index to scroll position\n\t\t// It should be the opposite of `scrollPosToIndex`\n\t\tindexToScrollPos(index: number): number {\n\t\t\treturn Math.floor(index / this.columnCount) * this.itemHeight + this.beforeHeight\n\t\t},\n\n\t\t/**\n\t\t * Update the height variables.\n\t\t * To be called by resize observer and `onMount`\n\t\t */\n\t\tupdateHeightVariables(): void {\n\t\t\tthis.tableHeight = this.$el?.clientHeight ?? 0\n\t\t\tthis.beforeHeight = (this.$refs.before as HTMLElement)?.clientHeight ?? 0\n\t\t\tthis.footerHeight = (this.$refs.footer as HTMLElement)?.clientHeight ?? 0\n\n\t\t\t// Get the header height which consists of table header and filters\n\t\t\tconst theadHeight = (this.$refs.thead as HTMLElement)?.clientHeight ?? 0\n\t\t\tconst filterHeight = (this.$refs.filters as HTMLElement)?.clientHeight ?? 0\n\t\t\tthis.headerHeight = theadHeight + filterHeight\n\t\t},\n\t},\n})\n</script>\n","/*!\n * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { IFileListHeader } from '@nextcloud/files'\nimport type { ComputedRef } from 'vue'\n\nimport { getFileListHeaders, getFilesRegistry } from '@nextcloud/files'\nimport { computed, ref } from 'vue'\n\nconst headers = ref<IFileListHeader[]>()\nconst sorted = computed(() => [...(headers.value ?? [])].sort((a, b) => a.order - b.order) as IFileListHeader[])\n\n/**\n * Get the registered and sorted file list headers.\n */\nexport function useFileListHeaders(): ComputedRef<IFileListHeader[]> {\n\tif (!headers.value) {\n\t\t// if not initialized by other component yet, initialize and subscribe to registry changes\n\t\theaders.value = getFileListHeaders()\n\t\tgetFilesRegistry().addEventListener('register:listHeader', () => {\n\t\t\theaders.value = getFileListHeaders()\n\t\t})\n\t}\n\n\treturn sorted\n}\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<VirtualList\n\t\tref=\"table\"\n\t\t:data-component=\"userConfig.grid_view ? FileEntryGrid : FileEntry\"\n\t\tdata-key=\"source\"\n\t\t:data-sources=\"nodes\"\n\t\t:gridMode=\"userConfig.grid_view\"\n\t\t:extraProps=\"{\n\t\t\tisMimeAvailable,\n\t\t\tisMtimeAvailable,\n\t\t\tisSizeAvailable,\n\t\t\tnodes,\n\t\t}\"\n\t\t:scrollToIndex=\"scrollToIndex\"\n\t\t:caption=\"caption\">\n\t\t<!-- eslint-disable-next-line vue/singleline-html-element-content-newline -- no space allowed as otherwise `:empty` css selector does not trigger! -->\n\t\t<template #filters><FileListFilterToSearch /><FileListFilterChips /></template>\n\n\t\t<template #before>\n\t\t\t<!-- Headers -->\n\t\t\t<FilesListHeader\n\t\t\t\tv-for=\"header in headers\"\n\t\t\t\t:key=\"header.id\"\n\t\t\t\t:currentFolder=\"currentFolder\"\n\t\t\t\t:currentView=\"currentView\"\n\t\t\t\t:header=\"header\" />\n\t\t</template>\n\n\t\t<!-- Thead-->\n\t\t<template #header>\n\t\t\t<!-- Table header and sort buttons -->\n\t\t\t<FilesListTableHeader\n\t\t\t\tref=\"thead\"\n\t\t\t\t:currentView=\"currentView\"\n\t\t\t\t:isMimeAvailable=\"isMimeAvailable\"\n\t\t\t\t:isMtimeAvailable=\"isMtimeAvailable\"\n\t\t\t\t:isSizeAvailable=\"isSizeAvailable\"\n\t\t\t\t:nodes=\"nodes\" />\n\t\t</template>\n\n\t\t<!-- Body replacement if no files are available -->\n\t\t<template #empty>\n\t\t\t<slot name=\"empty\" />\n\t\t</template>\n\n\t\t<!-- Tfoot-->\n\t\t<template #footer>\n\t\t\t<FilesListTableFooter\n\t\t\t\t:currentView=\"currentView\"\n\t\t\t\t:isMimeAvailable=\"isMimeAvailable\"\n\t\t\t\t:isMtimeAvailable=\"isMtimeAvailable\"\n\t\t\t\t:isSizeAvailable=\"isSizeAvailable\"\n\t\t\t\t:nodes=\"nodes\"\n\t\t\t\t:summary=\"summary\" />\n\t\t</template>\n\t</VirtualList>\n</template>\n\n<script lang=\"ts\">\nimport type { INode } from '@nextcloud/files'\nimport type { ComponentPublicInstance, PropType } from 'vue'\nimport type { UserConfig } from '../types.ts'\n\nimport { showError } from '@nextcloud/dialogs'\nimport { FileType, Folder, getSidebar, Permission, View } from '@nextcloud/files'\nimport { t } from '@nextcloud/l10n'\nimport { useHotKey } from '@nextcloud/vue/composables/useHotKey'\nimport { computed, defineComponent, provide, watch } from 'vue'\nimport FileEntry from './FileEntry.vue'\nimport FileEntryGrid from './FileEntryGrid.vue'\nimport FileListFilterChips from './FileListFilter/FileListFilterChips.vue'\nimport FileListFilterToSearch from './FileListFilter/FileListFilterToSearch.vue'\nimport FilesListHeader from './FilesListHeader.vue'\nimport FilesListTableFooter from './FilesListTableFooter.vue'\nimport FilesListTableHeader from './FilesListTableHeader.vue'\nimport VirtualList from './VirtualList.vue'\nimport { useEnabledFileActions } from '../composables/useFileActions.ts'\nimport { useFileListHeaders } from '../composables/useFileListHeaders.ts'\nimport { FileListLayoutKey, getFileListLayout, resetInlineActionsWidth, useInlineActionsWidth } from '../composables/useFileListLayout.ts'\nimport { useFileListWidth } from '../composables/useFileListWidth.ts'\nimport { useRouteParameters } from '../composables/useRouteParameters.ts'\nimport { useActiveStore } from '../store/active.ts'\nimport { useUserConfigStore } from '../store/userconfig.ts'\nimport { logger } from '../utils/logger.ts'\n\nexport default defineComponent({\n\tname: 'FilesListVirtual',\n\n\tcomponents: {\n\t\tFileListFilterChips,\n\t\tFileListFilterToSearch,\n\t\tFilesListHeader,\n\t\tFilesListTableFooter,\n\t\tFilesListTableHeader,\n\t\tVirtualList,\n\t},\n\n\tprops: {\n\t\tcurrentView: {\n\t\t\ttype: View,\n\t\t\trequired: true,\n\t\t},\n\n\t\tcurrentFolder: {\n\t\t\ttype: Folder,\n\t\t\trequired: true,\n\t\t},\n\n\t\tnodes: {\n\t\t\ttype: Array as PropType<INode[]>,\n\t\t\trequired: true,\n\t\t},\n\n\t\tsummary: {\n\t\t\ttype: String,\n\t\t\trequired: true,\n\t\t},\n\t},\n\n\tsetup(props) {\n\t\tconst sidebar = getSidebar()\n\t\tconst activeStore = useActiveStore()\n\t\tconst userConfigStore = useUserConfigStore()\n\n\t\tconst { width, isNarrow, isWide } = useFileListWidth()\n\t\tconst { fileId, openDetails, openFile } = useRouteParameters()\n\n\t\tconst isMtimeAvailable = computed(() => {\n\t\t\t// Hide mtime column on narrow screens\n\t\t\tif (isNarrow.value) {\n\t\t\t\treturn false // hide on narrow screens\n\t\t\t}\n\t\t\treturn props.nodes.some((node: INode) => node.mtime !== undefined)\n\t\t})\n\n\t\tconst isSizeAvailable = computed(() => {\n\t\t\t// Hide size column on narrow screens\n\t\t\tif (isNarrow.value) {\n\t\t\t\treturn false // hide on narrow screens\n\t\t\t}\n\t\t\treturn props.nodes.some((node: INode) => node.size !== undefined)\n\t\t})\n\n\t\tconst inlineActionsWidth = useInlineActionsWidth()\n\t\twatch(() => [props.currentFolder, props.currentView], resetInlineActionsWidth, { immediate: true })\n\n\t\tconst layout = computed(() => getFileListLayout(width.value, {\n\t\t\tinlineActions: true,\n\t\t\tmime: userConfigStore.userConfig.show_mime_column\n\t\t\t\t&& isWide.value // only show on wide screens\n\t\t\t\t&& props.nodes.some((node: INode) => node.mime !== undefined || node.mime !== 'application/octet-stream'),\n\t\t\tsize: isSizeAvailable.value,\n\t\t\tmtime: isMtimeAvailable.value,\n\t\t\tcolumns: isNarrow.value ? [] : (props.currentView.columns ?? []),\n\t\t}, inlineActionsWidth.value))\n\t\tprovide(FileListLayoutKey, layout)\n\n\t\tconst isMimeAvailable = computed(() => layout.value.mime)\n\n\t\treturn {\n\t\t\tfileId,\n\t\t\theaders: useFileListHeaders(),\n\t\t\tisSizeAvailable,\n\t\t\tisMtimeAvailable,\n\t\t\tisMimeAvailable,\n\t\t\topenDetails,\n\t\t\topenFile,\n\n\t\t\tsidebar,\n\t\t\tactiveStore,\n\t\t\tuserConfigStore,\n\n\t\t\tt,\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tFileEntry,\n\t\t\tFileEntryGrid,\n\t\t\tscrollToIndex: 0,\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tuserConfig(): UserConfig {\n\t\t\treturn this.userConfigStore.userConfig\n\t\t},\n\n\t\tcantUpload() {\n\t\t\treturn this.currentFolder && (this.currentFolder.permissions & Permission.CREATE) === 0\n\t\t},\n\n\t\tisQuotaExceeded() {\n\t\t\treturn this.currentFolder?.attributes?.['quota-available-bytes'] === 0\n\t\t},\n\n\t\tcaption() {\n\t\t\tconst defaultCaption = t('files', 'List of files and folders.')\n\t\t\tconst viewCaption = this.currentView.caption || defaultCaption\n\t\t\tconst cantUploadCaption = this.cantUpload ? t('files', 'You do not have permission to upload or create files here.') : null\n\t\t\tconst quotaExceededCaption = this.isQuotaExceeded ? t('files', 'You have used your space quota and cannot upload files anymore.') : null\n\t\t\tconst sortableCaption = t('files', 'Column headers with buttons are sortable.')\n\t\t\tconst virtualListNote = t('files', 'This list is not fully rendered for performance reasons. The files will be rendered as you navigate through the list.')\n\t\t\treturn [\n\t\t\t\tviewCaption,\n\t\t\t\tcantUploadCaption,\n\t\t\t\tquotaExceededCaption,\n\t\t\t\tsortableCaption,\n\t\t\t\tvirtualListNote,\n\t\t\t].filter(Boolean).join('\\n')\n\t\t},\n\n\t\tisEmpty() {\n\t\t\treturn this.nodes.length === 0\n\t\t},\n\t},\n\n\twatch: {\n\t\t// If nodes gets populated and we have a fileId,\n\t\t// an openFile or openDetails, we fire the appropriate actions.\n\t\tisEmpty() {\n\t\t\tthis.handleOpenQueries()\n\t\t},\n\n\t\tfileId() {\n\t\t\tthis.handleOpenQueries()\n\t\t},\n\n\t\topenFile() {\n\t\t\tthis.handleOpenQueries()\n\t\t},\n\n\t\topenDetails() {\n\t\t\tthis.handleOpenQueries()\n\t\t},\n\t},\n\n\tcreated() {\n\t\tuseHotKey('Escape', this.unselectFile, {\n\t\t\tstop: true,\n\t\t\tprevent: true,\n\t\t})\n\n\t\tuseHotKey(['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'], this.onKeyDown, {\n\t\t\tstop: true,\n\t\t\tprevent: true,\n\t\t})\n\t},\n\n\tmounted() {\n\t\t// Add events on parent to cover both the table and DragAndDrop notice\n\t\tconst mainContent = window.document.querySelector('main.app-content') as HTMLElement\n\t\tmainContent.addEventListener('dragover', this.onDragOver)\n\t},\n\n\tbeforeUnmount() {\n\t\tconst mainContent = window.document.querySelector('main.app-content') as HTMLElement\n\t\tmainContent.removeEventListener('dragover', this.onDragOver)\n\t},\n\n\tmethods: {\n\t\thandleOpenQueries() {\n\t\t\t// If the list is empty, or we don't have a fileId,\n\t\t\t// there's nothing to be done.\n\t\t\tif (this.isEmpty || this.fileId === null) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlogger.debug('FilesListVirtual: checking for requested fileId, openFile or openDetails', {\n\t\t\t\tnodes: this.nodes,\n\t\t\t\tfileId: this.fileId,\n\t\t\t\topenFile: this.openFile,\n\t\t\t\topenDetails: this.openDetails,\n\t\t\t})\n\n\t\t\tif (this.openFile) {\n\t\t\t\tthis.handleOpenFile(this.fileId)\n\t\t\t}\n\n\t\t\tif (this.openDetails) {\n\t\t\t\tthis.openSidebarForFile(this.fileId)\n\t\t\t}\n\n\t\t\tif (this.fileId) {\n\t\t\t\tconst node = this.nodes.find((node) => node.fileid === this.fileId)\n\t\t\t\tif (node) {\n\t\t\t\t\tthis.activeStore.activeNode = node\n\t\t\t\t\tthis.scrollToFile(this.fileId, false)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\topenSidebarForFile(fileId) {\n\t\t\t// Open the sidebar for the given URL fileid\n\t\t\t// iif we just loaded the app.\n\t\t\tconst node = this.nodes.find((n) => n.fileid === fileId) as INode\n\t\t\tif (node && this.sidebar.available) {\n\t\t\t\tlogger.debug('Opening sidebar on file ' + node.path, { node })\n\t\t\t\tthis.sidebar.open(node)\n\t\t\t} else {\n\t\t\t\tlogger.warn(`Failed to open sidebar on file ${fileId}, file isn't cached yet !`, { fileId, node })\n\t\t\t}\n\t\t},\n\n\t\tscrollToFile(fileId: number | null, warn = true) {\n\t\t\tif (fileId) {\n\t\t\t\t// Do not uselessly scroll to the top of the list.\n\t\t\t\tif (fileId === this.currentFolder.fileid) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst index = this.nodes.findIndex((node) => node.fileid === fileId)\n\t\t\t\tif (warn && index === -1 && fileId !== this.currentFolder.fileid) {\n\t\t\t\t\tshowError(t('files', 'File not found'))\n\t\t\t\t}\n\n\t\t\t\tthis.scrollToIndex = Math.max(0, index)\n\t\t\t\tlogger.debug('Scrolling to file ' + fileId, { fileId, index })\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Unselect the current file and clear open parameters from the URL\n\t\t */\n\t\tunselectFile() {\n\t\t\tconst query = { ...this.$route.query }\n\t\t\tdelete query.openfile\n\t\t\tdelete query.opendetails\n\n\t\t\tthis.activeStore.activeNode = this.currentFolder\n\t\t},\n\n\t\t/**\n\t\t * Handle opening a file (e.g. by ?openfile=true)\n\t\t *\n\t\t * @param fileId File to open\n\t\t */\n\t\tasync handleOpenFile(fileId: number) {\n\t\t\tconst node = this.nodes.find((n) => n.fileid === fileId) as INode\n\t\t\tif (node === undefined) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (node.type === FileType.File) {\n\t\t\t\tconst actions = useEnabledFileActions({\n\t\t\t\t\tnodes: [node],\n\t\t\t\t\tview: this.currentView,\n\t\t\t\t\tfolder: this.currentFolder,\n\t\t\t\t\tcontents: this.nodes,\n\t\t\t\t})\n\t\t\t\tconst defaultAction = actions.value.find((action) => action.id !== 'download' && !!action.default)\n\n\t\t\t\t// Some file types do not have a default action (e.g. they can only be downloaded)\n\t\t\t\t// So if there is an enabled default action, so execute it\n\t\t\t\tif (defaultAction) {\n\t\t\t\t\tlogger.debug('Opening file ' + node.path, { node })\n\t\t\t\t\treturn await defaultAction.exec({\n\t\t\t\t\t\tnodes: [node],\n\t\t\t\t\t\tview: this.currentView,\n\t\t\t\t\t\tfolder: this.currentFolder,\n\t\t\t\t\t\tcontents: this.nodes,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t\t// The file is either a folder or has no default action other than downloading\n\t\t\t// in this case we need to open the details instead and remove the route from the history\n\t\t\tlogger.debug('Ignore `openfile` query and replacing with `opendetails` for ' + node.path, { node })\n\t\t\twindow.OCP.Files.Router.goToRoute(\n\t\t\t\tnull,\n\t\t\t\twindow.OCP.Files.Router.params,\n\t\t\t\t{ ...this.$route.query, openfile: undefined, opendetails: '' },\n\t\t\t\ttrue, // silent update of the URL\n\t\t\t)\n\t\t},\n\n\t\tonDragOver(event: DragEvent) {\n\t\t\t// Detect if we're only dragging existing files or not\n\t\t\tconst isForeignFile = event.dataTransfer?.types.includes('Files')\n\t\t\tif (isForeignFile) {\n\t\t\t\t// Only handle uploading of existing Nextcloud files\n\t\t\t\t// See DragAndDropNotice for handling of foreign files\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tevent.preventDefault()\n\t\t\tevent.stopPropagation()\n\n\t\t\tconst tableElement = (this.$refs.table as ComponentPublicInstance<typeof VirtualList>).$el\n\t\t\tconst tableTop = tableElement.getBoundingClientRect().top\n\t\t\tconst tableBottom = tableTop + tableElement.getBoundingClientRect().height\n\n\t\t\t// If reaching top, scroll up. Using 100 because of the floating header\n\t\t\tif (event.clientY < tableTop + 100) {\n\t\t\t\ttableElement.scrollTop = tableElement.scrollTop - 25\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// If reaching bottom, scroll down\n\t\t\tif (event.clientY > tableBottom - 50) {\n\t\t\t\ttableElement.scrollTop = tableElement.scrollTop + 25\n\t\t\t}\n\t\t},\n\n\t\tonKeyDown(event: KeyboardEvent) {\n\t\t\tif (this.isEmpty) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (event.key !== 'ArrowUp' && event.key !== 'ArrowDown'\n\t\t\t\t&& (!this.userConfig.grid_view || (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight'))) {\n\t\t\t\t// not an arrow key we handle\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (!this.fileId || this.fileId === this.currentFolder.fileid) {\n\t\t\t\t// no active node so use either first or last node\n\t\t\t\tconst index = event.key === 'ArrowUp' || event.key === 'ArrowLeft'\n\t\t\t\t\t? this.nodes.length - 1\n\t\t\t\t\t: 0\n\t\t\t\tthis.setActiveNode(this.nodes[index] as INode & { fileid: number })\n\t\t\t}\n\n\t\t\tconst index = this.nodes.findIndex((node) => node.fileid === this.fileId) ?? 0\n\t\t\t// Up and down arrow keys\n\t\t\tif (event.key === 'ArrowUp' || event.key === 'ArrowDown') {\n\t\t\t\tconst columnCount = this.$refs.table?.columnCount ?? 1\n\t\t\t\tconst nextIndex = event.key === 'ArrowUp' ? index - columnCount : index + columnCount\n\t\t\t\tif (nextIndex < 0 || nextIndex >= this.nodes.length) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst nextNode = this.nodes[nextIndex]\n\n\t\t\t\tif (nextNode && nextNode?.fileid) {\n\t\t\t\t\tthis.setActiveNode(nextNode)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// if grid mode, left and right arrow keys\n\t\t\tif (this.userConfig.grid_view && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {\n\t\t\t\tconst nextIndex = event.key === 'ArrowLeft' ? index - 1 : index + 1\n\t\t\t\tif (nextIndex < 0 || nextIndex >= this.nodes.length) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tconst nextNode = this.nodes[nextIndex]\n\n\t\t\t\tif (nextNode && nextNode?.fileid) {\n\t\t\t\t\tthis.setActiveNode(nextNode)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tasync setActiveNode(node: INode & { fileid: number }) {\n\t\t\tlogger.debug('Navigating to file ' + node.path, { node, fileid: node.fileid })\n\t\t\tthis.scrollToFile(node.fileid)\n\n\t\t\t// Remove openfile and opendetails from the URL\n\t\t\tconst query = { ...this.$route.query }\n\t\t\tdelete query.openfile\n\t\t\tdelete query.opendetails\n\t\t\tawait this.$router.replace({\n\t\t\t\t...this.$route,\n\t\t\t\tquery,\n\t\t\t})\n\n\t\t\t// set the new file as active\n\t\t\tthis.activeStore.activeNode = node\n\t\t},\n\t},\n})\n</script>\n\n<style scoped lang=\"scss\">\n.files-list {\n\t--row-height: 44px;\n\t--cell-margin: 14px;\n\n\t--checkbox-padding: calc((var(--row-height) - var(--checkbox-size)) / 2);\n\t--checkbox-size: 24px;\n\t--clickable-area: var(--default-clickable-area);\n\t--icon-preview-size: 24px;\n\n\t--fixed-block-start-position: calc(var(--clickable-area-small) + var(--default-grid-baseline, 4px));\n\tdisplay: flex;\n\tflex-direction: column;\n\toverflow: auto;\n\theight: 100%;\n\twill-change: scroll-position;\n\n\t&:has(&__filters:empty) {\n\t\t--fixed-block-start-position: 0px;\n\t}\n\n\t& :deep() {\n\t\t// Table head, body and footer\n\t\ttbody {\n\t\t\twill-change: padding;\n\t\t\tcontain: layout paint style;\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\twidth: 100%;\n\t\t\t// Necessary for virtual scrolling absolute\n\t\t\tposition: relative;\n\n\t\t\t/* Hover effect on tbody lines only */\n\t\t\ttr {\n\t\t\t\tcontain: strict;\n\t\t\t\t&:hover,\n\t\t\t\t&:focus {\n\t\t\t\t\tbackground-color: var(--color-background-dark);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Before table and thead\n\t\t.files-list__before {\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t}\n\n\t\t.files-list__table {\n\t\t\tdisplay: block;\n\n\t\t\t// Visually hide the table when there are no files\n\t\t\t&--hidden {\n\t\t\t\tvisibility: hidden;\n\t\t\t\tz-index: -1;\n\t\t\t\topacity: 0;\n\t\t\t}\n\t\t}\n\n\t\t.files-list__filters {\n\t\t\tdisplay: flex;\n\t\t\tgap: var(--default-grid-baseline);\n\t\t\tbox-sizing: border-box;\n\n\t\t\t// Pinned on top when scrolling above table header\n\t\t\tposition: sticky;\n\t\t\ttop: 0;\n\t\t\t// ensure there is a background to hide the file list on scroll\n\t\t\tbackground-color: var(--color-main-background);\n\t\t\tz-index: 10;\n\t\t\t// fixed the size\n\t\t\tpadding-inline: var(--row-height) var(--default-grid-baseline, 4px);\n\t\t\theight: var(--fixed-block-start-position);\n\t\t\twidth: 100%;\n\n\t\t\t&:not(:empty) {\n\t\t\t\tpadding-block: calc(var(--default-grid-baseline, 4px) / 2);\n\t\t\t}\n\t\t}\n\n\t\t.files-list__thead,\n\t\t.files-list__tfoot {\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\twidth: 100%;\n\t\t\tbackground-color: var(--color-main-background);\n\t\t}\n\n\t\t// Table header\n\t\t.files-list__thead {\n\t\t\t// Pinned on top when scrolling\n\t\t\tposition: sticky;\n\t\t\tz-index: 10;\n\t\t\ttop: var(--fixed-block-start-position);\n\t\t}\n\n\t\t// Empty content\n\t\t.files-list__empty {\n\t\t\tdisplay: flex;\n\t\t\tflex-direction: column;\n\t\t\talign-items: center;\n\t\t\tjustify-content: center;\n\t\t\twidth: 100%;\n\t\t\theight: 100%;\n\t\t}\n\n\t\ttr {\n\t\t\tposition: relative;\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\twidth: 100%;\n\t\t\tborder-block-end: 1px solid var(--color-border);\n\t\t\tbox-sizing: border-box;\n\t\t\tuser-select: none;\n\t\t\theight: var(--row-height);\n\t\t}\n\n\t\ttd, th {\n\t\t\tdisplay: flex;\n\t\t\talign-items: center;\n\t\t\tflex: 0 0 auto;\n\t\t\tjustify-content: start;\n\t\t\twidth: var(--row-height);\n\t\t\theight: var(--row-height);\n\t\t\tmargin: 0;\n\t\t\tpadding: 0;\n\t\t\tcolor: var(--color-text-maxcontrast);\n\t\t\tborder: none;\n\n\t\t\t// Columns should try to add any text\n\t\t\t// node wrapped in a span. That should help\n\t\t\t// with the ellipsis on overflow.\n\t\t\tspan {\n\t\t\t\toverflow: hidden;\n\t\t\t\twhite-space: nowrap;\n\t\t\t\ttext-overflow: ellipsis;\n\t\t\t}\n\t\t}\n\n\t\t.files-list__row--failed {\n\t\t\tposition: absolute;\n\t\t\tdisplay: block;\n\t\t\ttop: 0;\n\t\t\tinset-inline: 0;\n\t\t\tbottom: 0;\n\t\t\topacity: .1;\n\t\t\tz-index: -1;\n\t\t\tbackground: var(--color-error);\n\t\t}\n\n\t\t.files-list__row-checkbox {\n\t\t\tjustify-content: center;\n\n\t\t\t.checkbox-radio-switch {\n\t\t\t\tdisplay: flex;\n\t\t\t\tjustify-content: center;\n\n\t\t\t\t--icon-size: var(--checkbox-size);\n\n\t\t\t\tlabel.checkbox-radio-switch__label {\n\t\t\t\t\twidth: var(--clickable-area);\n\t\t\t\t\theight: var(--clickable-area);\n\t\t\t\t\tmargin: 0;\n\t\t\t\t\tpadding: calc((var(--clickable-area) - var(--checkbox-size)) / 2);\n\t\t\t\t}\n\n\t\t\t\t.checkbox-radio-switch__icon {\n\t\t\t\t\tmargin: 0 !important;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// When files are selected, batch actions live in the select-all cell\n\t\t// and other headers are visually hidden (kept in DOM for column association).\n\t\t.files-list__row-head--selected {\n\t\t\t.files-list__row-checkbox {\n\t\t\t\tflex: 1 1 auto;\n\t\t\t\twidth: auto;\n\t\t\t\tmin-width: var(--row-height);\n\t\t\t\tmax-width: 100%;\n\t\t\t\tjustify-content: flex-start;\n\t\t\t\tgap: 12px;\n\t\t\t\toverflow: hidden;\n\t\t\t}\n\t\t}\n\n\t\t.files-list__row {\n\t\t\t&:hover, &:focus, &:active, &--active, &--dragover {\n\t\t\t\t// WCAG AA compliant\n\t\t\t\tbackground-color: var(--color-background-hover);\n\t\t\t\t// text-maxcontrast have been designed to pass WCAG AA over\n\t\t\t\t// a white background, we need to adjust then.\n\t\t\t\t--color-text-maxcontrast: var(--color-main-text);\n\t\t\t\t> * {\n\t\t\t\t\t--color-border: var(--color-border-dark);\n\t\t\t\t}\n\n\t\t\t\t// Hover state of the row should also change the favorite markers background\n\t\t\t\t.favorite-marker-icon svg path {\n\t\t\t\t\tstroke: var(--color-background-hover);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t&--dragover * {\n\t\t\t\t// Prevent dropping on row children\n\t\t\t\tpointer-events: none;\n\t\t\t}\n\t\t}\n\n\t\t// Entry preview or mime icon\n\t\t.files-list__row-icon {\n\t\t\tposition: relative;\n\t\t\tdisplay: flex;\n\t\t\toverflow: visible;\n\t\t\talign-items: center;\n\t\t\t// No shrinking or growing allowed\n\t\t\tflex: 0 0 var(--icon-preview-size);\n\t\t\tjustify-content: center;\n\t\t\twidth: var(--icon-preview-size);\n\t\t\theight: 100%;\n\t\t\t// Show same padding as the checkbox right padding for visual balance\n\t\t\tmargin-inline-end: var(--checkbox-padding);\n\t\t\tcolor: var(--color-primary-element);\n\n\t\t\t// Icon is also clickable\n\t\t\t* {\n\t\t\t\tcursor: pointer;\n\t\t\t}\n\n\t\t\t& > span {\n\t\t\t\tjustify-content: flex-start;\n\n\t\t\t\t&:not(.files-list__row-icon-favorite):not(.files-list__row-icon-recently-created) svg {\n\t\t\t\t\twidth: var(--icon-preview-size);\n\t\t\t\t\theight: var(--icon-preview-size);\n\t\t\t\t}\n\n\t\t\t\t// Slightly increase the size of the folder icon\n\t\t\t\t&.folder-icon,\n\t\t\t\t&.folder-open-icon {\n\t\t\t\t\tmargin: -3px;\n\t\t\t\t\tsvg {\n\t\t\t\t\t\twidth: calc(var(--icon-preview-size) + 6px);\n\t\t\t\t\t\theight: calc(var(--icon-preview-size) + 6px);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t&-preview-container {\n\t\t\t\tposition: relative; // Needed for the blurshash to be positioned correctly\n\t\t\t\toverflow: hidden;\n\t\t\t\twidth: var(--icon-preview-size);\n\t\t\t\theight: var(--icon-preview-size);\n\t\t\t\tborder-radius: var(--border-radius);\n\t\t\t}\n\n\t\t\t&-blurhash {\n\t\t\t\tposition: absolute;\n\t\t\t\tinset-block-start: 0;\n\t\t\t\tinset-inline-start: 0;\n\t\t\t\theight: 100%;\n\t\t\t\twidth: 100%;\n\t\t\t\tobject-fit: cover;\n\t\t\t}\n\n\t\t\t&-preview {\n\t\t\t\t// Center and contain the preview\n\t\t\t\tobject-fit: contain;\n\t\t\t\tobject-position: center;\n\n\t\t\t\theight: 100%;\n\t\t\t\twidth: 100%;\n\n\t\t\t\t/* Preview not loaded animation effect */\n\t\t\t\t&:not(.files-list__row-icon-preview--loaded) {\n\t\t\t\t\tbackground: var(--color-loading-dark);\n\t\t\t\t\t// animation: preview-gradient-fade 1.2s ease-in-out infinite;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t&-favorite,\n\t\t\t&-recently-created {\n\t\t\t\tposition: absolute;\n\t\t\t\ttop: 0px;\n\t\t\t\tinset-inline-end: -10px;\n\t\t\t}\n\n\t\t\t// File and folder overlay\n\t\t\t&-overlay {\n\t\t\t\tposition: absolute;\n\t\t\t\tmax-height: calc(var(--icon-preview-size) * 0.6);\n\t\t\t\tmax-width: calc(var(--icon-preview-size) * 0.6);\n\t\t\t\tcolor: var(--color-primary-element-text);\n\t\t\t\t// better alignment with the folder icon\n\t\t\t\tmargin-block-start: 2px;\n\n\t\t\t\t// Improve icon contrast with a background for files\n\t\t\t\t&--file {\n\t\t\t\t\tcolor: var(--color-main-text);\n\t\t\t\t\tbackground: var(--color-main-background);\n\t\t\t\t\tborder-radius: 100%;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Entry link\n\t\t.files-list__row-name {\n\t\t\t// Prevent link from overflowing\n\t\t\toverflow: hidden;\n\t\t\t// Take as much space as possible\n\t\t\tflex: 1 1 auto;\n\n\t\t\tbutton.files-list__row-name-link {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\ttext-align: start;\n\t\t\t\t// Fill cell height and width\n\t\t\t\twidth: 100%;\n\t\t\t\theight: 100%;\n\t\t\t\t// Necessary for flex grow to work\n\t\t\t\tmin-width: 0;\n\t\t\t\tmargin: 0;\n\t\t\t\tpadding: 0;\n\n\t\t\t\t// Already added to the inner text, see rule below\n\t\t\t\t&:focus-visible {\n\t\t\t\t\toutline: none !important;\n\t\t\t\t}\n\n\t\t\t\t// Keyboard indicator a11y\n\t\t\t\t&:focus .files-list__row-name-text {\n\t\t\t\t\toutline: var(--border-width-input-focused) solid var(--color-main-text) !important;\n\t\t\t\t\tborder-radius: var(--border-radius-element);\n\t\t\t\t}\n\t\t\t\t&:focus:not(:focus-visible) .files-list__row-name-text {\n\t\t\t\t\toutline: none !important;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t.files-list__row-name-text {\n\t\t\t\tcolor: var(--color-main-text);\n\t\t\t\t// Make some space for the outline\n\t\t\t\tpadding: var(--default-grid-baseline) calc(2 * var(--default-grid-baseline));\n\t\t\t\tpadding-inline-start: -10px;\n\t\t\t\t// Align two name and ext\n\t\t\t\tdisplay: inline-flex;\n\t\t\t}\n\n\t\t\t.files-list__row-name-ext {\n\t\t\t\tcolor: var(--color-text-maxcontrast);\n\t\t\t\t// always show the extension\n\t\t\t\toverflow: visible;\n\t\t\t}\n\t\t}\n\n\t\t// Rename form\n\t\t.files-list__row-rename {\n\t\t\twidth: 100%;\n\t\t\tmax-width: 600px;\n\t\t\tinput {\n\t\t\t\twidth: 100%;\n\t\t\t\t// Align with text, 0 - padding - border\n\t\t\t\tmargin-inline-start: -8px;\n\t\t\t\tpadding: 2px 6px;\n\t\t\t\tborder-width: 2px;\n\n\t\t\t\t&:invalid {\n\t\t\t\t\t// Show red border on invalid input\n\t\t\t\t\tborder-color: var(--color-border-error);\n\t\t\t\t\tcolor: var(--color-text-error);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t.files-list__row-actions {\n\t\t\t// take as much space as necessary\n\t\t\twidth: auto;\n\n\t\t\t// Add margin to all cells after the actions\n\t\t\t& ~ td,\n\t\t\t& ~ th {\n\t\t\t\tmargin: 0 var(--cell-margin);\n\t\t\t}\n\n\t\t\tbutton {\n\t\t\t\t.button-vue__text {\n\t\t\t\t\t// Remove bold from default button styling\n\t\t\t\t\tfont-weight: normal;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t.files-list__row-action--inline {\n\t\t\tmargin-inline-end: 7px;\n\t\t}\n\n\t\t.files-list__row-mime,\n\t\t.files-list__row-mtime,\n\t\t.files-list__row-size {\n\t\t\tcolor: var(--color-text-maxcontrast);\n\t\t}\n\n\t\t.files-list__row-size {\n\t\t\twidth: calc(var(--row-height) * 2);\n\t\t\t// Right align content/text\n\t\t\tjustify-content: flex-end;\n\t\t}\n\n\t\t.files-list__row-mtime {\n\t\t\twidth: calc(var(--row-height) * 2.5);\n\t\t}\n\n\t\t.files-list__row-mime {\n\t\t\twidth: calc(var(--row-height) * 3.5);\n\t\t}\n\n\t\t.files-list__row-column-custom {\n\t\t\twidth: calc(var(--row-height) * 2.5);\n\t\t}\n\t}\n}\n\n@media screen and (max-width: 512px) {\n\t.files-list :deep(.files-list__filters) {\n\t\t// Reduce padding on mobile\n\t\tpadding-inline: var(--default-grid-baseline, 4px);\n\t}\n}\n\n</style>\n\n<style lang=\"scss\">\n// Grid mode\n.files-list--grid tbody.files-list__tbody {\n\t--item-padding: 16px;\n\t--icon-preview-size: 166px;\n\t--name-height: var(--default-clickable-area);\n\t--mtime-height: calc(var(--font-size-small) + var(--default-grid-baseline));\n\t--row-width: calc(var(--icon-preview-size) + var(--item-padding) * 2);\n\t--row-height: calc(var(--icon-preview-size) + var(--name-height) + var(--mtime-height) + var(--item-padding) * 2);\n\t--checkbox-padding: 0px;\n\tdisplay: grid;\n\tgrid-template-columns: repeat(auto-fill, var(--row-width));\n\n\talign-content: center;\n\talign-items: center;\n\tjustify-content: space-around;\n\tjustify-items: center;\n\n\ttr {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\twidth: var(--row-width);\n\t\theight: var(--row-height);\n\t\tborder: none;\n\t\tborder-radius: var(--border-radius-large);\n\t\tpadding: var(--item-padding);\n\t}\n\n\t// Checkbox in the top left\n\t.files-list__row-checkbox {\n\t\tposition: absolute;\n\t\tz-index: 9;\n\t\ttop: calc(var(--item-padding) / 2);\n\t\tinset-inline-start: calc(var(--item-padding) / 2);\n\t\toverflow: hidden;\n\t\t--checkbox-container-size: 44px;\n\t\twidth: var(--checkbox-container-size);\n\t\theight: var(--checkbox-container-size);\n\n\t\t// Add a background to the checkbox so we do not see the image through it.\n\t\t.checkbox-radio-switch__content::after {\n\t\t\tcontent: '';\n\t\t\twidth: 16px;\n\t\t\theight: 16px;\n\t\t\tposition: absolute;\n\t\t\tinset-inline-start: 50%;\n\t\t\tmargin-inline-start: -8px;\n\t\t\tz-index: -1;\n\t\t\tbackground: var(--color-main-background);\n\t\t}\n\t}\n\n\t// Icon in the top right\n\t.files-list__row-icon-favorite,\n\t.files-list__row-icon-recently-created {\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tinset-inline-end: 0;\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t\twidth: var(--clickable-area);\n\t\theight: var(--clickable-area);\n\t}\n\n\t.files-list__row-name {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\twidth: var(--icon-preview-size);\n\t\theight: calc(var(--icon-preview-size) + var(--name-height));\n\t\t// Ensure that the name outline is visible.\n\t\toverflow: visible;\n\n\t\tspan.files-list__row-icon {\n\t\t\twidth: var(--icon-preview-size);\n\t\t\theight: var(--icon-preview-size);\n\t\t}\n\n\t\t.files-list__row-name-text {\n\t\t\tmargin: 0;\n\t\t\t// Ensure that the outline is not too close to the text.\n\t\t\tmargin-inline-start: -4px;\n\t\t\tpadding: 0px 4px;\n\t\t}\n\t}\n\n\t.files-list__row-mtime {\n\t\twidth: var(--icon-preview-size);\n\t\theight: var(--mtime-height);\n\t\tfont-size: var(--font-size-small);\n\t}\n\n\t.files-list__row-actions {\n\t\tposition: absolute;\n\t\tinset-inline-end: calc(var(--clickable-area) / 4);\n\t\tinset-block-end: calc(var(--mtime-height) / 2);\n\t\twidth: var(--clickable-area);\n\t\theight: var(--clickable-area);\n\t}\n}\n\n@media screen and (max-width: 768px) {\n\t// there is no mtime\n\t.files-list--grid tbody.files-list__tbody {\n\t\t--mtime-height: 0px;\n\n\t\t// so we move the action to the name\n\t\t.files-list__row-actions {\n\t\t\tinset-block-end: var(--item-padding);\n\t\t}\n\n\t\t// and we need to keep space on the name for the actions\n\t\t.files-list__row-name-text {\n\t\t\tpadding-inline-end: var(--clickable-area) !important;\n\t\t}\n\t}\n}\n</style>\n","/*!\n * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { IFileListAction, IFolder, INode, IView } from '@nextcloud/files'\nimport type { ComputedRef, MaybeRefOrGetter } from 'vue'\n\nimport { getFileListActions, getFilesRegistry } from '@nextcloud/files'\nimport { computed, ref, toValue } from 'vue'\n\nconst actions = ref<IFileListAction[]>()\nconst sorted = computed(() => [...(actions.value ?? [])].sort((a, b) => a.order - b.order))\n\n/**\n * Get the registered and sorted file list actions.\n */\nexport function useFileListActions(): ComputedRef<IFileListAction[]> {\n\tif (!actions.value) {\n\t\t// if not initialized by other component yet, initialize and subscribe to registry changes\n\t\tactions.value = getFileListActions()\n\t\tgetFilesRegistry().addEventListener('register:listAction', () => {\n\t\t\tactions.value = getFileListActions()\n\t\t})\n\t}\n\n\treturn sorted\n}\n\n/**\n * Get the enabled file list actions for the given folder, contents and view.\n *\n * @param folder - The current folder\n * @param contents - The contents of the current folder\n * @param view - The current view\n */\nexport function useEnabledFileListActions(\n\tfolder: MaybeRefOrGetter<IFolder | undefined>,\n\tcontents: MaybeRefOrGetter<INode[]>,\n\tview: MaybeRefOrGetter<IView | undefined>,\n) {\n\tconst actions = useFileListActions()\n\treturn computed(() => {\n\t\tif (toValue(folder) === undefined || toValue(view) === undefined) {\n\t\t\treturn []\n\t\t}\n\n\t\treturn actions.value.filter((action) => action.enabled === undefined\n\t\t\t|| action.enabled({ folder: toValue(folder)!, contents: toValue(contents), view: toValue(view)! }))\n\t})\n}\n","/*!\n * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { IFolder, INode, NewMenuEntry } from '@nextcloud/files'\nimport type { FilePickerItem, FilePickerItemGroup } from '@nextcloud/vue/components/NcUploadPicker'\nimport type { ComputedRef, MaybeRefOrGetter } from 'vue'\n\nimport { getNewFileMenuEntries, NewMenuEntryCategory } from '@nextcloud/files'\nimport { t } from '@nextcloud/l10n'\nimport { computed, toValue } from 'vue'\n\n/**\n * Get the registered \"new\"-menu entries of a folder as actions for the upload picker,\n * grouped by the category of the entries.\n *\n * @param folder - The folder the new nodes are created in\n * @param contents - The current contents of that folder, passed to the entry handlers\n */\nexport function useNewFileMenuActions(\n\tfolder: MaybeRefOrGetter<IFolder | undefined>,\n\tcontents: MaybeRefOrGetter<INode[]>,\n): ComputedRef<FilePickerItemGroup[]> {\n\treturn computed(() => {\n\t\tconst context = toValue(folder)\n\t\tif (context === undefined) {\n\t\t\treturn []\n\t\t}\n\n\t\tconst entries = getNewFileMenuEntries(context)\n\n\t\t/**\n\t\t * Map a menu entry to a picker action.\n\t\t * The folder contents are resolved on click to also pass nodes added after the menu was built.\n\t\t *\n\t\t * @param entry - The menu entry to map\n\t\t */\n\t\tconst toAction = (entry: NewMenuEntry): FilePickerItem => ({\n\t\t\tlabel: entry.displayName,\n\t\t\ticonSvg: entry.iconSvgInline ?? '',\n\t\t\tonClick: () => entry.handler(context, toValue(contents)),\n\t\t})\n\n\t\treturn [\n\t\t\t{ caption: t('files', 'Upload from device'), category: NewMenuEntryCategory.UploadFromDevice },\n\t\t\t{ caption: t('files', 'Create new'), category: NewMenuEntryCategory.CreateNew },\n\t\t\t{ caption: t('files', 'Other'), category: NewMenuEntryCategory.Other },\n\t\t].map(({ caption, category }) => ({\n\t\t\tcaption,\n\t\t\tactions: entries.filter((entry) => entry.category === category).map(toAction),\n\t\t})).filter(({ actions }) => actions.length > 0)\n\t})\n}\n","/**\n * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors\n * SPDX-License-Identifier: AGPL-3.0-or-later\n */\n\nimport type { WebDAVClientError } from 'webdav'\n\nimport { t } from '@nextcloud/l10n'\n\n/**\n * Whether error is a WebDAVClientError\n *\n * @param error - Any exception\n * @return - Whether error is a WebDAVClientError\n */\nfunction isWebDAVClientError(error: unknown): error is WebDAVClientError {\n\treturn error instanceof Error && 'status' in error && 'response' in error\n}\n\n/**\n * Get a localized error message from webdav request\n *\n * @param error - An exception from webdav request\n * @return Localized error message for end user\n */\nexport function humanizeWebDAVError(error: unknown) {\n\tif (error instanceof Error) {\n\t\tif (isWebDAVClientError(error)) {\n\t\t\tconst status = error.status || error.response?.status || 0\n\t\t\tif ([400, 404, 405].includes(status)) {\n\t\t\t\treturn t('files', 'Folder not found')\n\t\t\t} else if (status === 403) {\n\t\t\t\treturn t('files', 'This operation is forbidden')\n\t\t\t} else if (status === 500) {\n\t\t\t\treturn t('files', 'This folder is unavailable, please try again later or contact the administration')\n\t\t\t} else if (status === 503) {\n\t\t\t\treturn t('files', 'Storage is temporarily not available')\n\t\t\t}\n\t\t}\n\t\t// We don't need to show abortion error to the user as those are expected.\n\t\tif (error.name === 'AbortError') {\n\t\t\treturn null\n\t\t}\n\t\treturn t('files', 'Unexpected error: {error}', { error: error.message })\n\t}\n\n\treturn t('files', 'Unknown error')\n}\n","<!--\n - SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n<template>\n\t<NcAppContent :pageHeading=\"pageHeading\" data-cy-files-content>\n\t\t<div class=\"files-list__header\" :class=\"{ 'files-list__header--public': isPublic }\">\n\t\t\t<!-- Uploader -->\n\t\t\t<Teleport :disabled=\"!isNarrow\" to=\"body\">\n\t\t\t\t<NcUploadPicker\n\t\t\t\t\tv-if=\"canUpload && !isQuotaExceeded && currentFolder\"\n\t\t\t\t\t:actions=\"newFileMenuActions\"\n\t\t\t\t\tdirectory\n\t\t\t\t\t:iconOnly=\"isNarrow\"\n\t\t\t\t\tclass=\"files-list__header-upload-button\"\n\t\t\t\t\t:class=\"{ 'files-list__header-upload-button--narrow': isNarrow }\"\n\t\t\t\t\t:content=\"getContent\"\n\t\t\t\t\t:destination=\"currentFolder\"\n\t\t\t\t\tmultiple\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\tdata-cy-upload-picker\n\t\t\t\t\t@upload:finished=\"onUploadFinished\" />\n\t\t\t</Teleport>\n\n\t\t\t<!-- Current folder breadcrumbs -->\n\t\t\t<BreadCrumbs :path=\"directory\" @reload=\"fetchContent\" />\n\n\t\t\t<!-- Loading indicator -->\n\t\t\t<NcLoadingIcon\n\t\t\t\tv-if=\"isRefreshing\"\n\t\t\t\tclass=\"files-list__refresh-icon\"\n\t\t\t\t:name=\"t('files', 'File list is reloading')\" />\n\n\t\t\t<!-- File list actions (global actions like restore all files from trashbin) -->\n\t\t\t<NcActions\n\t\t\t\tclass=\"files-list__header-actions\"\n\t\t\t\t:inline=\"1\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\tforceName>\n\t\t\t\t<NcActionButton\n\t\t\t\t\tv-for=\"action in enabledFileListActions\"\n\t\t\t\t\t:key=\"action.id\"\n\t\t\t\t\t:disabled=\"!!loadingAction\"\n\t\t\t\t\t:data-cy-files-list-action=\"action.id\"\n\t\t\t\t\tcloseAfterClick\n\t\t\t\t\t@click=\"execFileListAction(action)\">\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<NcLoadingIcon v-if=\"loadingAction === action.id\" :size=\"18\" />\n\t\t\t\t\t\t<NcIconSvgWrapper\n\t\t\t\t\t\t\tv-else-if=\"action.iconSvgInline !== undefined && currentView\"\n\t\t\t\t\t\t\t:svg=\"action.iconSvgInline(currentView /* TODO: FIXME */)\" />\n\t\t\t\t\t</template>\n\t\t\t\t\t{{ actionDisplayName(action) }}\n\t\t\t\t</NcActionButton>\n\t\t\t</NcActions>\n\n\t\t\t<!-- Filters thats can be applied to the file list -->\n\t\t\t<FileListFilters />\n\n\t\t\t<!-- Grid view toggle -->\n\t\t\t<NcButton\n\t\t\t\tv-if=\"enableGridView\"\n\t\t\t\t:aria-label=\"gridViewButtonLabel\"\n\t\t\t\t:title=\"gridViewButtonLabel\"\n\t\t\t\tclass=\"files-list__header-grid-button\"\n\t\t\t\tvariant=\"tertiary\"\n\t\t\t\t@click=\"toggleGridView\">\n\t\t\t\t<template #icon>\n\t\t\t\t\t<ListViewIcon v-if=\"userConfig.grid_view\" />\n\t\t\t\t\t<ViewGridIcon v-else />\n\t\t\t\t</template>\n\t\t\t</NcButton>\n\t\t</div>\n\n\t\t<!-- Drag and drop notice -->\n\t\t<DragAndDropNotice v-if=\"!loading && canUpload && currentFolder\" :currentFolder />\n\n\t\t<!--\n\t\t\tInitial current view loading0. This should never happen,\n\t\t\tviews are supposed to be registered far earlier in the lifecycle.\n\t\t\tIn case the URL is bad or a view is missing, we show a loading icon.\n\t\t-->\n\t\t<NcLoadingIcon\n\t\t\tv-if=\"!currentView\"\n\t\t\tclass=\"files-list__loading-icon\"\n\t\t\t:size=\"38\"\n\t\t\t:name=\"t('files', 'Loading current folder')\" />\n\n\t\t<!-- File list - always mounted -->\n\t\t<FilesListVirtual\n\t\t\tv-else\n\t\t\tref=\"filesListVirtual\"\n\t\t\t:currentFolder\n\t\t\t:currentView\n\t\t\t:nodes=\"dirContentsSorted\"\n\t\t\t:summary=\"summary\">\n\t\t\t<template #empty>\n\t\t\t\t<!-- Initial loading -->\n\t\t\t\t<NcLoadingIcon\n\t\t\t\t\tv-if=\"loading && !isRefreshing\"\n\t\t\t\t\tclass=\"files-list__loading-icon\"\n\t\t\t\t\t:size=\"38\"\n\t\t\t\t\t:name=\"t('files', 'Loading current folder')\" />\n\n\t\t\t\t<!-- Empty due to error -->\n\t\t\t\t<NcEmptyContent v-else-if=\"error\" :name=\"error\" data-cy-files-content-error>\n\t\t\t\t\t<template #action>\n\t\t\t\t\t\t<NcButton variant=\"secondary\" @click=\"fetchContent\">\n\t\t\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t\t\t<IconReload :size=\"20\" />\n\t\t\t\t\t\t\t</template>\n\t\t\t\t\t\t\t{{ t('files', 'Retry') }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<IconAlertCircleOutline />\n\t\t\t\t\t</template>\n\t\t\t\t</NcEmptyContent>\n\n\t\t\t\t<!-- Custom empty view -->\n\t\t\t\t<div v-else-if=\"currentView?.emptyView\" class=\"files-list__empty-view-wrapper\">\n\t\t\t\t\t<div ref=\"customEmptyView\" />\n\t\t\t\t</div>\n\n\t\t\t\t<!-- Default empty directory view -->\n\t\t\t\t<NcEmptyContent\n\t\t\t\t\tv-else\n\t\t\t\t\t:name=\"currentView?.emptyTitle || t('files', 'No files in here')\"\n\t\t\t\t\t:description=\"currentView?.emptyCaption || t('files', 'Upload some content or sync with your devices!')\"\n\t\t\t\t\tdata-cy-files-content-empty>\n\t\t\t\t\t<template v-if=\"directory !== '/'\" #action>\n\t\t\t\t\t\t<!-- Uploader -->\n\t\t\t\t\t\t<NcUploadPicker\n\t\t\t\t\t\t\tv-if=\"canUpload && !isQuotaExceeded\"\n\t\t\t\t\t\t\t:actions=\"newFileMenuActions\"\n\t\t\t\t\t\t\tdirectory\n\t\t\t\t\t\t\tclass=\"files-list__header-upload-button\"\n\t\t\t\t\t\t\t:content=\"getContent\"\n\t\t\t\t\t\t\t:destination=\"currentFolder\"\n\t\t\t\t\t\t\tmultiple\n\t\t\t\t\t\t\tdata-cy-upload-picker\n\t\t\t\t\t\t\t@upload:finished=\"onUploadFinished\" />\n\t\t\t\t\t\t<NcButton v-else :to=\"toPreviousDir\" variant=\"primary\">\n\t\t\t\t\t\t\t{{ t('files', 'Go back') }}\n\t\t\t\t\t\t</NcButton>\n\t\t\t\t\t</template>\n\t\t\t\t\t<template #icon>\n\t\t\t\t\t\t<NcIconSvgWrapper :svg=\"currentView?.icon\" />\n\t\t\t\t\t</template>\n\t\t\t\t</NcEmptyContent>\n\t\t\t</template>\n\t\t</FilesListVirtual>\n\t</NcAppContent>\n</template>\n\n<script lang=\"ts\">\nimport type { ContentsWithRoot, IFileListAction, INode, Node } from '@nextcloud/files'\nimport type { IUpload } from '@nextcloud/files/upload'\nimport type { ComponentPublicInstance } from 'vue'\nimport type { RouteLocationRaw } from 'vue-router'\nimport type { UserConfig } from '../types.ts'\n\nimport { showError, showSuccess } from '@nextcloud/dialogs'\nimport { emit, subscribe, unsubscribe } from '@nextcloud/event-bus'\nimport { Permission, sortNodes } from '@nextcloud/files'\nimport { UploadStatus } from '@nextcloud/files/upload'\nimport { loadState } from '@nextcloud/initial-state'\nimport { translate as t } from '@nextcloud/l10n'\nimport { basename, dirname, join } from '@nextcloud/paths'\nimport { ShareType } from '@nextcloud/sharing'\nimport { useThrottleFn } from '@vueuse/core'\nimport { normalize, relative } from 'path'\nimport { computed, defineComponent, nextTick, watch } from 'vue'\nimport NcActionButton from '@nextcloud/vue/components/NcActionButton'\nimport NcActions from '@nextcloud/vue/components/NcActions'\nimport NcAppContent from '@nextcloud/vue/components/NcAppContent'\nimport NcButton from '@nextcloud/vue/components/NcButton'\nimport NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent'\nimport NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'\nimport NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'\nimport NcUploadPicker from '@nextcloud/vue/components/NcUploadPicker'\nimport IconAlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue'\nimport ListViewIcon from 'vue-material-design-icons/FormatListBulletedSquare.vue'\nimport IconReload from 'vue-material-design-icons/Reload.vue'\nimport ViewGridIcon from 'vue-material-design-icons/ViewGridOutline.vue'\nimport BreadCrumbs from '../components/BreadCrumbs.vue'\nimport DragAndDropNotice from '../components/DragAndDropNotice.vue'\nimport FileListFilters from '../components/FileListFilter/FileListFilters.vue'\nimport FilesListVirtual from '../components/FilesListVirtual.vue'\nimport { useFilesSorting } from '../composables/filesSorting.ts'\nimport { useEnabledFileListActions } from '../composables/useFileListActions.ts'\nimport { useFileListWidth } from '../composables/useFileListWidth.ts'\nimport { useNewFileMenuActions } from '../composables/useNewFileMenuActions.ts'\nimport { useRouteParameters } from '../composables/useRouteParameters.ts'\nimport { useActiveStore } from '../store/active.ts'\nimport { useFilesStore } from '../store/files.ts'\nimport { useFiltersStore } from '../store/filters.ts'\nimport { usePathsStore } from '../store/paths.ts'\nimport { useSelectionStore } from '../store/selection.ts'\nimport { useSidebarStore } from '../store/sidebar.ts'\nimport { useUploaderStore } from '../store/uploader.ts'\nimport { useUserConfigStore } from '../store/userconfig.ts'\nimport { useViewConfigStore } from '../store/viewConfig.ts'\nimport { humanizeWebDAVError } from '../utils/davUtils.ts'\nimport { defaultView } from '../utils/filesViews.ts'\nimport { getSummaryFor } from '../utils/fileUtils.ts'\nimport { logger } from '../utils/logger.ts'\n\nexport default defineComponent({\n\tname: 'FilesList',\n\n\tcomponents: {\n\t\tBreadCrumbs,\n\t\tDragAndDropNotice,\n\t\tFileListFilters,\n\t\tFilesListVirtual,\n\t\tListViewIcon,\n\t\tNcAppContent,\n\t\tNcActions,\n\t\tNcActionButton,\n\t\tNcButton,\n\t\tNcEmptyContent,\n\t\tNcIconSvgWrapper,\n\t\tNcLoadingIcon,\n\t\tNcUploadPicker,\n\t\tViewGridIcon,\n\t\tIconAlertCircleOutline,\n\t\tIconReload,\n\t},\n\n\tprops: {\n\t\tisPublic: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\t},\n\n\tsetup() {\n\t\tconst sidebar = useSidebarStore()\n\t\tconst activeStore = useActiveStore()\n\t\tconst filesStore = useFilesStore()\n\t\tconst filtersStore = useFiltersStore()\n\t\tconst pathsStore = usePathsStore()\n\t\tconst selectionStore = useSelectionStore()\n\t\tconst uploaderStore = useUploaderStore()\n\t\tconst userConfigStore = useUserConfigStore()\n\t\tconst viewConfigStore = useViewConfigStore()\n\n\t\tconst { isNarrow } = useFileListWidth()\n\t\tconst { directory, fileId } = useRouteParameters()\n\n\t\tconst enableGridView = (loadState('core', 'config', [])['enable_non-accessible_features'] ?? true)\n\n\t\tconst currentView = computed(() => activeStore.activeView)\n\t\tconst currentFolder = computed(() => activeStore.activeFolder)\n\t\tconst dirContents = computed<INode[]>(() => {\n\t\t\tconst sources = (currentFolder.value as { _children?: string[] })?._children ?? []\n\t\t\treturn sources.map(filesStore.getNode)\n\t\t\t\t.filter(Boolean) as INode[]\n\t\t})\n\n\t\tconst enabledFileListActions = useEnabledFileListActions(\n\t\t\tcurrentFolder,\n\t\t\tdirContents,\n\t\t\tcurrentView,\n\t\t)\n\n\t\tconst newFileMenuActions = useNewFileMenuActions(currentFolder, dirContents)\n\n\t\t// wait until the current folder is set up to notifiy the list is initialized\n\t\tconst stopWatching = watch(currentFolder, () => {\n\t\t\tif (currentFolder.value.fileid !== undefined && currentFolder.value.fileid! > 0) {\n\t\t\t\tnextTick(async () => emit('files:list:initialized'))\n\t\t\t\tstopWatching()\n\t\t\t}\n\t\t}, { immediate: true })\n\n\t\treturn {\n\t\t\tcurrentFolder,\n\t\t\tcurrentView,\n\t\t\tdirContents,\n\t\t\tdirectory,\n\t\t\tenabledFileListActions,\n\t\t\tfileId,\n\t\t\tisNarrow,\n\t\t\tnewFileMenuActions,\n\n\t\t\tsidebar,\n\t\t\tactiveStore,\n\t\t\tfilesStore,\n\t\t\tfiltersStore,\n\t\t\tpathsStore,\n\t\t\tselectionStore,\n\t\t\tuploaderStore,\n\t\t\tuserConfigStore,\n\t\t\tviewConfigStore,\n\n\t\t\t...useFilesSorting(),\n\n\t\t\t// non reactive data\n\t\t\tenableGridView,\n\t\t\tShareType,\n\t\t\tt,\n\t\t}\n\t},\n\n\tdata() {\n\t\treturn {\n\t\t\tinitialized: false,\n\n\t\t\tloading: true,\n\t\t\tloadingAction: null as string | null,\n\t\t\terror: null as string | null,\n\t\t\tcontroller: new AbortController(),\n\t\t\tpromise: null as Promise<ContentsWithRoot> | null,\n\n\t\t\tdirContentsFiltered: [] as INode[],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\t/**\n\t\t * Get a callback function for the uploader to fetch directory contents for conflict resolution\n\t\t */\n\t\tgetContent() {\n\t\t\tconst view = this.currentView!\n\t\t\treturn async (path?: string) => {\n\t\t\t\t// as the path is allowed to be undefined we need to normalize the path ('//' to '/')\n\t\t\t\tconst normalizedPath = normalize(`${this.currentFolder?.path ?? ''}/${path ?? ''}`)\n\t\t\t\t// Try cache first\n\t\t\t\tconst nodes = this.filesStore.getNodesByPath(view.id, normalizedPath)\n\t\t\t\tif (nodes.length > 0) {\n\t\t\t\t\treturn nodes\n\t\t\t\t}\n\t\t\t\t// If not found in the files store (cache)\n\t\t\t\t// use the current view to fetch the content for the requested path\n\t\t\t\tconst controller = new AbortController()\n\t\t\t\treturn (await view.getContents(normalizedPath, { signal: controller.signal })).contents\n\t\t\t}\n\t\t},\n\n\t\tuserConfig(): UserConfig {\n\t\t\treturn this.userConfigStore.userConfig\n\t\t},\n\n\t\tpageHeading(): string {\n\t\t\tconst title = this.currentView?.name ?? t('files', 'Files')\n\n\t\t\tif (this.currentFolder === undefined || this.directory === '/') {\n\t\t\t\treturn title\n\t\t\t}\n\t\t\treturn `${this.currentFolder.displayname} - ${title}`\n\t\t},\n\n\t\t/**\n\t\t * The current directory contents.\n\t\t */\n\t\tdirContentsSorted(): INode[] {\n\t\t\tif (!this.currentView) {\n\t\t\t\treturn []\n\t\t\t}\n\n\t\t\tconst customColumn = (this.currentView?.columns || [])\n\t\t\t\t.find((column) => column.id === this.sortingMode)\n\n\t\t\t// Custom column must provide their own sorting methods\n\t\t\tif (customColumn?.sort && typeof customColumn.sort === 'function') {\n\t\t\t\tconst results = [...this.dirContentsFiltered].sort(customColumn.sort)\n\t\t\t\treturn this.isAscSorting ? results : results.reverse()\n\t\t\t}\n\n\t\t\tconst nodes = sortNodes(this.dirContentsFiltered, {\n\t\t\t\tsortFavoritesFirst: this.userConfig.sort_favorites_first,\n\t\t\t\tsortFoldersFirst: this.userConfig.sort_folders_first,\n\t\t\t\tsortingMode: this.sortingMode,\n\t\t\t\tsortingOrder: this.isAscSorting ? 'asc' : 'desc',\n\t\t\t})\n\n\t\t\t// TODO upstream this\n\t\t\tif (this.currentView.id === 'files') {\n\t\t\t\tnodes.sort((a, b) => {\n\t\t\t\t\tconst aa = relative(a.source, this.currentFolder!.source) === '..'\n\t\t\t\t\tconst bb = relative(b.source, this.currentFolder!.source) === '..'\n\t\t\t\t\tif (aa && bb) {\n\t\t\t\t\t\treturn 0\n\t\t\t\t\t} else if (aa) {\n\t\t\t\t\t\treturn -1\n\t\t\t\t\t}\n\t\t\t\t\treturn 1\n\t\t\t\t})\n\t\t\t}\n\n\t\t\treturn nodes\n\t\t},\n\n\t\t/**\n\t\t * The current directory is empty.\n\t\t */\n\t\tisEmptyDir(): boolean {\n\t\t\treturn this.dirContents.length === 0\n\t\t},\n\n\t\t/**\n\t\t * We are refreshing the current directory.\n\t\t * But we already have a cached version of it\n\t\t * that is not empty.\n\t\t */\n\t\tisRefreshing(): boolean {\n\t\t\treturn this.currentFolder !== undefined\n\t\t\t\t&& !this.isEmptyDir\n\t\t\t\t&& this.loading\n\t\t},\n\n\t\t/**\n\t\t * Route to the previous directory.\n\t\t */\n\t\ttoPreviousDir(): RouteLocationRaw {\n\t\t\tconst dir = this.directory.split('/').slice(0, -1).join('/') || '/'\n\t\t\treturn { ...this.$route, query: { dir } }\n\t\t},\n\n\t\tgridViewButtonLabel() {\n\t\t\treturn this.userConfig.grid_view\n\t\t\t\t? t('files', 'Switch to list view')\n\t\t\t\t: t('files', 'Switch to grid view')\n\t\t},\n\n\t\t/**\n\t\t * Check if the current folder has create permissions\n\t\t */\n\t\tcanUpload() {\n\t\t\treturn this.currentFolder && (this.currentFolder.permissions & Permission.CREATE) !== 0\n\t\t},\n\n\t\tisQuotaExceeded() {\n\t\t\treturn this.currentFolder?.attributes?.['quota-available-bytes'] === 0\n\t\t},\n\n\t\tshowCustomEmptyView() {\n\t\t\treturn !this.loading && this.isEmptyDir && this.currentView?.emptyView !== undefined\n\t\t},\n\n\t\t/**\n\t\t * Using the filtered content if filters are active\n\t\t */\n\t\tsummary() {\n\t\t\tconst hidden = this.dirContents.length - this.dirContentsFiltered.length\n\t\t\treturn getSummaryFor(this.dirContentsFiltered, hidden)\n\t\t},\n\n\t\tdebouncedFetchContent() {\n\t\t\treturn useThrottleFn(this.fetchContent, 800, true)\n\t\t},\n\t},\n\n\twatch: {\n\t\t/**\n\t\t * Handle rendering the custom empty view\n\t\t *\n\t\t * @param show The current state if the custom empty view should be rendered\n\t\t */\n\t\tshowCustomEmptyView(show: boolean) {\n\t\t\tif (show) {\n\t\t\t\tthis.$nextTick(() => {\n\t\t\t\t\tconst el = this.$refs.customEmptyView as HTMLDivElement\n\t\t\t\t\t// We can cast here because \"showCustomEmptyView\" assets that current view is set\n\t\t\t\t\tthis.currentView!.emptyView!(el)\n\t\t\t\t})\n\t\t\t}\n\t\t},\n\n\t\tcurrentView(newView, oldView) {\n\t\t\tif (newView?.id === oldView?.id) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlogger.debug('View changed', { newView, oldView })\n\t\t\tthis.selectionStore.reset()\n\t\t\tthis.fetchContent()\n\t\t},\n\n\t\tdirectory(newDir, oldDir) {\n\t\t\tlogger.debug('Directory changed', { newDir, oldDir })\n\t\t\t// TODO: preserve selection on browsing?\n\t\t\tthis.selectionStore.reset()\n\t\t\tthis.sidebar.close()\n\t\t\tthis.fetchContent()\n\n\t\t\t// Scroll to top, force virtual scroller to re-render\n\t\t\tconst filesListVirtual = this.$refs?.filesListVirtual as ComponentPublicInstance<typeof FilesListVirtual> | undefined\n\t\t\tif (filesListVirtual?.$el) {\n\t\t\t\tfilesListVirtual.$el.scrollTop = 0\n\t\t\t}\n\t\t},\n\n\t\tdirContents(contents) {\n\t\t\tlogger.debug('Directory contents changed', { view: this.currentView, folder: this.currentFolder, contents })\n\t\t\temit('files:list:updated', { view: this.currentView, folder: this.currentFolder, contents })\n\t\t\t// Also refresh the filtered content\n\t\t\tthis.filterDirContent()\n\t\t},\n\t},\n\n\tasync mounted() {\n\t\tsubscribe('files:node:updated', this.onUpdatedNode)\n\n\t\t// reload on settings change\n\t\tsubscribe('files:config:updated', this.fetchContent)\n\n\t\t// filter content if filter were changed\n\t\tsubscribe('files:filters:changed', this.filterDirContent)\n\n\t\tsubscribe('files:search:updated', this.onUpdateSearch)\n\n\t\t// Finally, fetch the current directory contents\n\t\tawait this.fetchContent()\n\t\tif (this.fileId) {\n\t\t\t// If we have a fileId, let's check if the file exists\n\t\t\tconst node = this.dirContents.find((node) => node.fileid?.toString() === this.fileId?.toString())\n\t\t\t// If the file isn't in the current directory nor if\n\t\t\t// the current directory is the file, we show an error\n\t\t\tif (!node && this.currentFolder?.fileid?.toString() !== this.fileId.toString()) {\n\t\t\t\tshowError(t('files', 'The file could not be found'))\n\t\t\t}\n\t\t}\n\t},\n\n\tunmounted() {\n\t\tunsubscribe('files:node:updated', this.onUpdatedNode)\n\t\tunsubscribe('files:config:updated', this.fetchContent)\n\t\tunsubscribe('files:filters:changed', this.filterDirContent)\n\t\tunsubscribe('files:search:updated', this.onUpdateSearch)\n\t},\n\n\tmethods: {\n\t\tonUpdateSearch({ query, scope }) {\n\t\t\tif (query && scope !== 'filter') {\n\t\t\t\tthis.debouncedFetchContent()\n\t\t\t}\n\t\t},\n\n\t\tasync fetchContent() {\n\t\t\tthis.loading = true\n\t\t\tthis.error = null\n\t\t\tconst dir = this.directory\n\t\t\tconst currentView = this.currentView\n\n\t\t\tif (!currentView) {\n\t\t\t\tlogger.debug('The current view does not exists or is not ready.', { currentView })\n\n\t\t\t\t// If we still haven't a valid view, let's wait for the page to load\n\t\t\t\t// then try again. Else redirect to the default view\n\t\t\t\twindow.addEventListener('DOMContentLoaded', () => {\n\t\t\t\t\tif (!this.currentView) {\n\t\t\t\t\t\tlogger.warn('No current view after DOMContentLoaded, redirecting to the default view')\n\t\t\t\t\t\twindow.OCP.Files.Router.goToRoute(null, { view: defaultView()! })\n\t\t\t\t\t}\n\t\t\t\t}, { once: true })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlogger.debug('Fetching contents for directory', { dir, currentView })\n\n\t\t\t// If we have a cancellable promise ongoing, cancel it\n\t\t\tif (this.promise) {\n\t\t\t\tthis.controller.abort()\n\t\t\t\tlogger.debug('Cancelled previous ongoing fetch')\n\t\t\t}\n\n\t\t\t// Fetch the current dir contents\n\t\t\tthis.controller = new AbortController()\n\t\t\tthis.promise = currentView.getContents(dir, { signal: this.controller.signal })\n\t\t\ttry {\n\t\t\t\tconst { folder, contents } = await this.promise\n\t\t\t\tlogger.debug('Fetched contents', { dir, folder, contents })\n\n\t\t\t\t// Update store\n\t\t\t\tthis.filesStore.updateNodes(contents)\n\n\t\t\t\t// Define current directory children\n\t\t\t\t// TODO: make it more official\n\t\t\t\tfolder._children = contents.map((node) => node.source)\n\n\t\t\t\t// If we're in the root dir, define the root\n\t\t\t\tif (dir === '/') {\n\t\t\t\t\tthis.filesStore.setRoot({ service: currentView.id, root: folder })\n\t\t\t\t} else {\n\t\t\t\t\t// Otherwise, add the folder to the store\n\t\t\t\t\tif (folder.fileid) {\n\t\t\t\t\t\tthis.filesStore.updateNodes([folder])\n\t\t\t\t\t\tthis.pathsStore.addPath({ service: currentView.id, source: folder.source, path: dir })\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If we're here, the view API messed up\n\t\t\t\t\t\tlogger.fatal('Invalid root folder returned', { dir, folder, currentView })\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Update paths store\n\t\t\t\tconst folders = contents.filter((node) => node.type === 'folder')\n\t\t\t\tfolders.forEach((node) => {\n\t\t\t\t\tthis.pathsStore.addPath({ service: currentView.id, source: node.source, path: join(dir, node.basename) })\n\t\t\t\t})\n\n\t\t\t\tthis.activeStore.activeFolder = folder\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error('Error while fetching content', { error })\n\t\t\t\tthis.error = humanizeWebDAVError(error)\n\t\t\t} finally {\n\t\t\t\tthis.loading = false\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * An upload of the upload manager has finished - successfully, failed or cancelled.\n\t\t *\n\t\t * @param upload - The upload that finished\n\t\t */\n\t\tonUploadFinished(upload: IUpload) {\n\t\t\tif (upload.status === UploadStatus.FAILED) {\n\t\t\t\tlogger.error('Upload failed', { upload })\n\t\t\t\tshowError(t('files', 'Could not upload \"{name}\"', { name: basename(upload.source) }))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Cancelled uploads are user initiated and do not add any content\n\t\t\tif (upload.status !== UploadStatus.FINISHED) {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// Let's only refresh the current Folder\n\t\t\t// Navigating to a different folder will refresh it anyway\n\t\t\t// TODO: fetch uploaded files data only\n\t\t\tif (dirname(upload.source) === this.currentFolder!.source) {\n\t\t\t\t// fetchContent will cancel the previous ongoing promise\n\t\t\t\tthis.fetchContent()\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Refreshes the current folder on update.\n\t\t *\n\t\t * @param node is the file/folder being updated.\n\t\t */\n\t\tonUpdatedNode(node?: Node) {\n\t\t\tif (node?.fileid === this.currentFolder?.fileid) {\n\t\t\t\tthis.fetchContent()\n\t\t\t}\n\t\t},\n\n\t\ttoggleGridView() {\n\t\t\tthis.userConfigStore.update('grid_view', !this.userConfig.grid_view)\n\t\t},\n\n\t\tfilterDirContent() {\n\t\t\tlet nodes: INode[] = this.dirContents\n\t\t\tfor (const filter of this.filtersStore.sortedFilters) {\n\t\t\t\tnodes = filter.filter(nodes)\n\t\t\t}\n\t\t\tthis.dirContentsFiltered = nodes\n\t\t},\n\n\t\tactionDisplayName(action: IFileListAction): string {\n\t\t\tlet displayName = action.id\n\t\t\ttry {\n\t\t\t\tdisplayName = action.displayName(this.currentView!)\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error('Error while getting action display name', { action, error })\n\t\t\t}\n\t\t\treturn displayName\n\t\t},\n\n\t\tasync execFileListAction(action: IFileListAction) {\n\t\t\tthis.loadingAction = action.id\n\n\t\t\tconst displayName = this.actionDisplayName(action)\n\t\t\ttry {\n\t\t\t\tconst success = await action.exec({\n\t\t\t\t\tview: this.currentView,\n\t\t\t\t\tfolder: this.currentFolder,\n\t\t\t\t\tcontents: this.dirContents,\n\t\t\t\t})\n\t\t\t\t// If the action returns null, we stay silent\n\t\t\t\tif (success === null || success === undefined) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif (success) {\n\t\t\t\t\tshowSuccess(t('files', '{displayName}: done', { displayName }))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tshowError(t('files', '{displayName}: failed', { displayName }))\n\t\t\t} catch (error) {\n\t\t\t\tlogger.error('Error while executing action', { action, error })\n\t\t\t\tshowError(t('files', '{displayName}: failed', { displayName }))\n\t\t\t} finally {\n\t\t\t\tthis.loadingAction = null\n\t\t\t}\n\t\t},\n\t},\n})\n</script>\n\n<style scoped lang=\"scss\">\n:global(.toast-loading-icon) {\n\t// Reduce start margin (it was made for text but this is an icon)\n\tmargin-inline-start: -4px;\n\t// 16px icon + 5px on both sides\n\tmin-width: 26px;\n}\n\n.app-content {\n\t// Virtual list needs to be full height and is scrollable\n\tdisplay: flex;\n\toverflow: hidden;\n\tflex-direction: column;\n\tmax-height: 100%;\n\tposition: relative !important;\n}\n\n.files-list {\n\t&__header {\n\t\tdisplay: flex;\n\t\tgap: var(--default-grid-baseline);\n\t\talign-items: center;\n\t\t// Do not grow or shrink (vertically)\n\t\tflex: 0 0;\n\t\tmax-width: 100%;\n\t\t// Align with the navigation toggle icon\n\t\tmargin-block: var(--app-navigation-padding, 4px);\n\t\tmargin-inline: calc(var(--default-clickable-area) + 2 * var(--app-navigation-padding, 4px)) var(--app-navigation-padding, 4px);\n\n\t\t&--public {\n\t\t\t// There is no navigation toggle on public shares\n\t\t\tmargin-inline: var(--app-navigation-padding, 4px);\n\t\t}\n\n\t\t>* {\n\t\t\t// Do not grow or shrink (horizontally)\n\t\t\t// Only the breadcrumbs shrinks\n\t\t\tflex: 0 0;\n\t\t}\n\n\t\t&-actions {\n\t\t\tmin-width: fit-content !important;\n\t\t}\n\t}\n\n\t&__header-upload-button--narrow {\n\t\t// this is teleported to body on narrow screens\n\t\tposition: fixed;\n\t\tinset-block-end: calc(1.5 * var(--default-grid-baseline));\n\t\tinset-inline-end: calc(1.5 * var(--default-grid-baseline));\n\t}\n\n\t&__before {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\tgap: calc(var(--default-grid-baseline) * 2);\n\t\tmargin-inline: calc(var(--default-clickable-area) + 2 * var(--app-navigation-padding));\n\t}\n\n\t&__empty-view-wrapper {\n\t\tdisplay: flex;\n\t\theight: 100%;\n\t}\n\n\t&__refresh-icon {\n\t\tflex: 0 0 var(--default-clickable-area);\n\t\twidth: var(--default-clickable-area);\n\t\theight: var(--default-clickable-area);\n\t}\n\n\t&__loading-icon {\n\t\tmargin: auto;\n\t}\n}\n</style>\n"],"file":"FilesList-cV7mwles.chunk.mjs"} |