Files
nextcloud-server-mirror/dist/FilesList-cV7mwles.chunk.mjs.map
2026-10-27 09:33:38 +00:00

1 line
346 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{"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"}