@webiny/app-file-manager 6.4.5 → 6.6.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.js +13 -2
- package/app.js.map +1 -1
- package/domain/tagsHelpers.d.ts +2 -2
- package/domain/types.d.ts +0 -8
- package/features/permissions/abstractions.d.ts +2 -2
- package/features/permissions/feature.d.ts +3 -3
- package/modules/AiEnrichment/AiImageEnrichmentEventHandler.d.ts +18 -0
- package/modules/AiEnrichment/AiImageEnrichmentEventHandler.js +33 -0
- package/modules/AiEnrichment/AiImageEnrichmentEventHandler.js.map +1 -0
- package/modules/AiEnrichment/feature.d.ts +1 -0
- package/modules/AiEnrichment/feature.js +11 -0
- package/modules/AiEnrichment/feature.js.map +1 -0
- package/modules/AiEnrichment/index.d.ts +2 -0
- package/modules/AiEnrichment/index.js +9 -0
- package/modules/AiEnrichment/index.js.map +1 -0
- package/modules/HeadlessCms/fieldType/FileFieldSettingsModifier.d.ts +16 -0
- package/modules/HeadlessCms/fieldType/FileFieldSettingsModifier.js +33 -0
- package/modules/HeadlessCms/fieldType/FileFieldSettingsModifier.js.map +1 -0
- package/modules/HeadlessCms/fieldType/FileFieldType.d.ts +24 -0
- package/modules/HeadlessCms/fieldType/FileFieldType.js +37 -0
- package/modules/HeadlessCms/fieldType/FileFieldType.js.map +1 -0
- package/modules/HeadlessCms/fieldType/FileInputRenderer.d.ts +15 -0
- package/modules/HeadlessCms/fieldType/FileInputRenderer.js +19 -0
- package/modules/HeadlessCms/fieldType/FileInputRenderer.js.map +1 -0
- package/modules/HeadlessCms/fieldType/FileInputsRenderer.d.ts +15 -0
- package/modules/HeadlessCms/fieldType/FileInputsRenderer.js +19 -0
- package/modules/HeadlessCms/fieldType/FileInputsRenderer.js.map +1 -0
- package/modules/HeadlessCms/fieldType/feature.d.ts +1 -0
- package/modules/HeadlessCms/fieldType/feature.js +17 -0
- package/modules/HeadlessCms/fieldType/feature.js.map +1 -0
- package/package.json +19 -29
- package/presentation/FileDetails/FileDetailsPresenter.test.js +1 -0
- package/presentation/FileDetails/FileDetailsPresenter.test.js.map +1 -1
- package/presentation/FileDetails/components/Content.d.ts +5 -4
- package/presentation/FileList/FileListDataSource.d.ts +8 -12
- package/presentation/FileList/FileListDataSource.js +15 -88
- package/presentation/FileList/FileListDataSource.js.map +1 -1
- package/presentation/FileList/FileListPresenter.test.js +7 -2
- package/presentation/FileList/FileListPresenter.test.js.map +1 -1
- package/presentation/FileList/FileManagerPresenter.d.ts +1 -0
- package/presentation/FileList/FileManagerPresenter.js +9 -1
- package/presentation/FileList/FileManagerPresenter.js.map +1 -1
- package/presentation/FileList/abstractions.d.ts +2 -0
- package/presentation/FileList/abstractions.js.map +1 -1
- package/presentation/FileList/components/BulkActions/index.d.ts +0 -1
- package/presentation/FileList/components/BulkActions/index.js +0 -1
- package/presentation/FileList/components/BulkActions/useBulkActionWorker.d.ts +9 -8
- package/presentation/FileList/components/BulkActions/useBulkActionWorker.js +3 -9
- package/presentation/FileList/components/BulkActions/useBulkActionWorker.js.map +1 -1
- package/presentation/FileList/components/Grid/FileGrid.js +1 -1
- package/presentation/FileList/components/Grid/FileGrid.js.map +1 -1
- package/presentation/FileList/components/Grid/FileGrid.test.js +18 -7
- package/presentation/FileList/components/Grid/FileGrid.test.js.map +1 -1
- package/presentation/FileList/components/Table/FileTable.js +1 -1
- package/presentation/FileList/components/Table/FileTable.js.map +1 -1
- package/presentation/FileList/components/Table/FileTable.test.js +9 -4
- package/presentation/FileList/components/Table/FileTable.test.js.map +1 -1
- package/presentation/FileManager/FileManagerView.js +8 -6
- package/presentation/FileManager/FileManagerView.js.map +1 -1
- package/presentation/FileManager/RouteParamsSync.d.ts +2 -0
- package/presentation/FileManager/RouteParamsSync.js +30 -0
- package/presentation/FileManager/RouteParamsSync.js.map +1 -0
- package/presentation/config/DefaultFileManagerConfig.js +1 -4
- package/presentation/config/DefaultFileManagerConfig.js.map +1 -1
- package/presentation/config/FileManagerViewConfig.d.ts +19 -25
- package/presentation/config/configComponents/Browser/BulkAction.d.ts +10 -8
- package/presentation/config/configComponents/Browser/BulkAction.js +3 -9
- package/presentation/config/configComponents/Browser/BulkAction.js.map +1 -1
- package/presentation/config/configComponents/Browser/FileAction.d.ts +2 -1
- package/presentation/config/configComponents/Browser/FolderAction.d.ts +2 -1
- package/presentation/config/configComponents/Browser/Table/Column.d.ts +9 -8
- package/presentation/config/configComponents/Browser/Table/index.d.ts +9 -9
- package/presentation/config/configComponents/Browser/index.d.ts +20 -20
- package/presentation/config/configComponents/Browser/index.js +0 -8
- package/presentation/config/configComponents/Browser/index.js.map +1 -1
- package/presentation/config/configComponents/FileDetails/index.d.ts +4 -2
- package/presentation/fieldRenderers/CmsFilePickerRenderer.d.ts +13 -0
- package/presentation/fieldRenderers/CmsFilePickerRenderer.js +29 -0
- package/presentation/fieldRenderers/CmsFilePickerRenderer.js.map +1 -0
- package/presentation/fieldRenderers/CmsMultiFilePickerRenderer.d.ts +13 -0
- package/presentation/fieldRenderers/CmsMultiFilePickerRenderer.js +51 -0
- package/presentation/fieldRenderers/CmsMultiFilePickerRenderer.js.map +1 -0
- package/routes.d.ts +1 -0
- package/routes.js +2 -1
- package/routes.js.map +1 -1
- package/modules/HeadlessCms/fileField.d.ts +0 -2
- package/modules/HeadlessCms/fileField.js +0 -44
- package/modules/HeadlessCms/fileField.js.map +0 -1
- package/modules/HeadlessCms/fileRenderer/fileField.d.ts +0 -2
- package/modules/HeadlessCms/fileRenderer/fileField.js +0 -53
- package/modules/HeadlessCms/fileRenderer/fileField.js.map +0 -1
- package/modules/HeadlessCms/fileRenderer/fileFields.d.ts +0 -2
- package/modules/HeadlessCms/fileRenderer/fileFields.js +0 -73
- package/modules/HeadlessCms/fileRenderer/fileFields.js.map +0 -1
- package/modules/HeadlessCms/fileRenderer/utils.d.ts +0 -2
- package/modules/HeadlessCms/fileRenderer/utils.js +0 -15
- package/modules/HeadlessCms/fileRenderer/utils.js.map +0 -1
- package/modules/HeadlessCms/index.d.ts +0 -1
- package/modules/HeadlessCms/index.js +0 -14
- package/modules/HeadlessCms/index.js.map +0 -1
- package/modules/Settings/graphql.d.ts +0 -12
- package/modules/Settings/graphql.js +0 -36
- package/modules/Settings/graphql.js.map +0 -1
- package/modules/Settings/views/FileManagerSettings.d.ts +0 -2
- package/modules/Settings/views/FileManagerSettings.js +0 -134
- package/modules/Settings/views/FileManagerSettings.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/AddOperation.d.ts +0 -7
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/AddOperation.js +0 -15
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/AddOperation.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditor.d.ts +0 -15
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditor.js +0 -44
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditor.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialog.d.ts +0 -15
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialog.js +0 -52
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialog.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.d.ts +0 -61
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.js +0 -111
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.test.d.ts +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.test.js +0 -372
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.test.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/FieldRenderer.d.ts +0 -8
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/FieldRenderer.js +0 -31
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/FieldRenderer.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/Operation.d.ts +0 -11
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/Operation.js +0 -33
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/Operation.js.map +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/index.d.ts +0 -1
- package/presentation/FileList/components/BulkActions/BatchEditorDialog/index.js +0 -1
- package/presentation/FileList/components/BulkActions/BulkActionEdit.d.ts +0 -4
- package/presentation/FileList/components/BulkActions/BulkActionEdit.js +0 -105
- package/presentation/FileList/components/BulkActions/BulkActionEdit.js.map +0 -1
- package/presentation/config/configComponents/Browser/FolderFieldDecorator.d.ts +0 -7
- package/presentation/config/configComponents/Browser/FolderFieldDecorator.js +0 -11
- package/presentation/config/configComponents/Browser/FolderFieldDecorator.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/FileList/FileManagerPresenter.js","sources":["../../../src/presentation/FileList/FileManagerPresenter.ts"],"sourcesContent":["import { makeAutoObservable, reaction, runInAction, computed } from \"mobx\";\nimport type { FmFile } from \"@webiny/sdk\";\nimport type { CmsModel } from \"@webiny/app-headless-cms-common/types/index.js\";\nimport { ListPresenter } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport { FolderTreePresenter } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport { LocalStorage } from \"@webiny/app/features/localStorage\";\nimport { GetDescendantFoldersUseCase } from \"@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js\";\nimport {\n FileManagerPresenter as Abstraction,\n type IFileManagerPresenter,\n type IFileManagerViewModel,\n type IFileManagerActions,\n type IFileManagerInitConfig\n} from \"./abstractions.js\";\nimport { FileListDataSource } from \"./FileListDataSource.js\";\nimport { FileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport { FileManagerPermissions } from \"~/features/permissions/abstractions.js\";\nimport { ListTagsRepository } from \"~/features/tags/index.js\";\nimport { FileUploader } from \"~/features/fileUploader/index.js\";\nimport { ListFilesUseCase } from \"~/features/listFiles/index.js\";\nimport { FilesListCache } from \"~/features/shared/index.js\";\nimport { FileModelProvider } from \"~/features/fileModel/index.js\";\nimport type { IFileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport type { SelectedFile } from \"@webiny/app-admin/presentation/browserFilePicker/index.js\";\n\nconst VIEW_MODE_KEY = \"fm:viewMode\";\nconst LAST_FOLDER_KEY = \"fm:lastFolder\";\n\nclass FileManagerPresenterImpl implements IFileManagerPresenter {\n private _viewMode: \"table\" | \"grid\" = \"grid\";\n private _dragging = false;\n private _disposeReaction: (() => void) | null = null;\n private _fileDetails: IFileDetailsPresenter | null = null;\n private _showingFilters = false;\n private _fileModel: CmsModel | null = null;\n private _persistFolder = true;\n\n constructor(\n private listPresenter: ListPresenter.Interface<FmFile>,\n private folderTreePresenter: FolderTreePresenter.Interface,\n private fileDetailsPresenter: FileDetailsPresenter.Interface,\n private permissions: FileManagerPermissions.Interface,\n private tagsRepository: ListTagsRepository.Interface,\n private fileUploader: FileUploader.Interface,\n private localStorage: LocalStorage.Interface,\n private listFilesUseCase: ListFilesUseCase.Interface,\n private filesListCache: FilesListCache.Interface,\n private getDescendantFoldersUseCase: GetDescendantFoldersUseCase.Interface,\n private fileModelProvider: FileModelProvider.Interface\n ) {\n makeAutoObservable<\n FileManagerPresenterImpl,\n \"_disposeReaction\" | \"fileDetailsPresenter\" | \"_persistFolder\"\n >(this, {\n _disposeReaction: false,\n fileDetailsPresenter: false,\n _persistFolder: false,\n vm: computed\n });\n\n const stored = this.localStorage.get<\"table\" | \"grid\">(VIEW_MODE_KEY);\n if (stored === \"table\" || stored === \"grid\") {\n this._viewMode = stored;\n }\n }\n\n get vm(): IFileManagerViewModel {\n return {\n fileModel: this._fileModel,\n list: this.listPresenter.vm,\n folders: this.folderTreePresenter.vm,\n fileDetails: this._fileDetails,\n // Flat booleans for UI visibility (e.g., show/hide buttons).\n // Per-file methods for ownership-aware checks (e.g., \"own\" scope).\n permissions: {\n canRead: this.permissions.canRead(\"file\"),\n canCreate: this.permissions.canCreate(\"file\"),\n canEdit: this.permissions.canEdit(\"file\"),\n canDelete: this.permissions.canDelete(\"file\"),\n canEditFile: (file: FmFile) => this.permissions.canEdit(\"file\", file),\n canDeleteFile: (file: FmFile) => this.permissions.canDelete(\"file\", file)\n },\n upload: {\n jobs: this.fileUploader.vm.jobs,\n overallProgress: {\n percentage: this.fileUploader.vm.overallProgress.percentage,\n bytesSent: this.fileUploader.vm.overallProgress.sent,\n totalBytes: this.fileUploader.vm.overallProgress.total\n },\n isUploading: this.fileUploader.vm.isUploading\n },\n tags: this.tagsRepository.tags,\n loading: this.isLoading(),\n empty: this.isEmpty(),\n showFolders: this.shouldShowFolders(),\n viewMode: this._viewMode,\n dragging: this._dragging,\n showingFilters: this._showingFilters\n };\n }\n\n actions: IFileManagerActions = {\n search: {\n set: (query: string) => this.listPresenter.actions.search.set(query),\n clear: () => this.listPresenter.actions.search.clear()\n },\n sort: {\n set: (field: string, direction: \"ASC\" | \"DESC\") =>\n this.listPresenter.actions.sort.set(field, direction),\n toggle: (field: string) => this.listPresenter.actions.sort.toggle(field)\n },\n filter: {\n set: (key: string, value: unknown) => this.listPresenter.actions.filter.set(key, value),\n clear: (key: string) => this.listPresenter.actions.filter.clear(key),\n clearAll: () => this.listPresenter.actions.filter.clearAll()\n },\n selection: {\n toggle: (id: string) => this.listPresenter.actions.selection.toggle(id),\n selectRangeTo: (id: string) => this.listPresenter.actions.selection.selectRangeTo(id),\n selectAll: () => this.listPresenter.actions.selection.selectAll(),\n deselectAll: () => this.listPresenter.actions.selection.deselectAll(),\n selectRows: (ids: string[]) => this.listPresenter.actions.selection.selectRows(ids),\n isSelected: (id: string) => this.listPresenter.actions.selection.isSelected(id)\n },\n loadMore: () => this.listPresenter.actions.loadMore(),\n refresh: () => this.listPresenter.actions.refresh(),\n upload: async (files: SelectedFile[]) => {\n const folderId = this.folderTreePresenter.vm.currentFolderId ?? \"root\";\n await this.fileUploader.uploadMany(\n files.map(file => ({\n file: file.src.file,\n data: {\n name: file.name,\n type: file.type,\n location: { folderId }\n }\n }))\n );\n this.fileUploader.clear();\n },\n setViewMode: (mode: \"table\" | \"grid\") => {\n this._viewMode = mode;\n this.localStorage.set(VIEW_MODE_KEY, mode);\n },\n setDragging: (dragging: boolean) => {\n this._dragging = dragging;\n },\n showFilters: () => {\n this._showingFilters = true;\n },\n hideFilters: () => {\n this._showingFilters = false;\n },\n showFileDetails: (id: string) => {\n this._fileDetails = this.fileDetailsPresenter;\n void this.fileDetailsPresenter.loadFile(id);\n },\n hideFileDetails: () => {\n this._fileDetails = null;\n },\n folders: {\n // Clear search when navigating to a folder — gives the user a clean slate.\n selectFolder: (folderId: string | null) => {\n this.listPresenter.actions.search.clear();\n this.folderTreePresenter.selectFolder(folderId);\n },\n createFolder: (parentFolderId?: string) =>\n this.folderTreePresenter.createFolder(parentFolderId),\n editFolder: (folderId: string) => this.folderTreePresenter.editFolder(folderId),\n deleteFolder: (folderId: string) => this.folderTreePresenter.deleteFolder(folderId),\n moveFolder: (folderId: string, targetParentId: string | null) =>\n this.folderTreePresenter.moveFolder(folderId, targetParentId),\n loadChildFolders: (parentIds: string[]) =>\n this.folderTreePresenter.loadChildFolders(parentIds),\n canManageStructure: (folderId: string) =>\n this.folderTreePresenter.canManageStructure(folderId),\n getAncestorIds: (folderId: string) => this.folderTreePresenter.getAncestorIds(folderId),\n submitOperation: () => this.folderTreePresenter.submitOperation(),\n cancelOperation: () => {\n this.folderTreePresenter.cancelOperation();\n }\n }\n };\n\n init(config?: IFileManagerInitConfig): void {\n const initialFolderId =\n config?.initialFolderId ?? this.localStorage.get<string>(LAST_FOLDER_KEY) ?? \"root\";\n\n // Don't persist folder navigation when an explicit initial folder is provided (e.g., overlay).\n this._persistFolder = !config?.initialFolderId;\n\n const dataSource = new FileListDataSource(\n this.listFilesUseCase,\n this.filesListCache,\n this.getDescendantFoldersUseCase,\n config?.scope\n );\n\n this.listPresenter.init({\n dataSource,\n initialSort: { field: \"createdOn\", direction: \"DESC\" },\n initialFilters: { folderId: initialFolderId },\n limit: 50\n });\n\n if (initialFolderId !== \"root\") {\n this.folderTreePresenter.selectFolder(initialFolderId);\n }\n\n // Sync folder navigation → file list: when the user selects a folder in the tree,\n // update the folderId filter so the file list re-queries for that folder's contents.\n this._disposeReaction = reaction(\n () => this.folderTreePresenter.vm.currentFolderId,\n folderId => {\n const effectiveFolderId = folderId ?? \"root\";\n this.listPresenter.actions.filter.set(\"folderId\", effectiveFolderId);\n\n if (this._persistFolder) {\n this.localStorage.set(LAST_FOLDER_KEY, effectiveFolderId);\n }\n }\n );\n\n void this.tagsRepository.execute({});\n\n // Load the CMS model in the background. The FileModelModule already kicks off\n // loading at app startup, so this usually resolves from cache instantly.\n // The view shows a loading overlay until `_fileModel` is set.\n void this.fileModelProvider.getModel().then(model => {\n runInAction(() => {\n this._fileModel = model;\n });\n });\n }\n\n dispose(): void {\n if (this._disposeReaction) {\n this._disposeReaction();\n this._disposeReaction = null;\n }\n }\n\n private isLoading(): boolean {\n return this.listPresenter.vm.pagination.loading && this.listPresenter.vm.rows.length === 0;\n }\n\n private isEmpty(): boolean {\n if (this.listPresenter.vm.pagination.loading) {\n return false;\n }\n\n if (this.listPresenter.vm.rows.length > 0) {\n return false;\n }\n\n if (this.shouldShowFolders() && this.folderTreePresenter.vm.childFolders.length > 0) {\n return false;\n }\n\n return true;\n }\n\n // Hide folders when the user is actively filtering or searching.\n // Uses `appliedQuery` (not `vm.search`) so folders stay visible while typing\n // and only disappear once the debounced query has actually executed.\n private shouldShowFolders(): boolean {\n const { appliedQuery } = this.listPresenter.vm;\n if (!appliedQuery) {\n return true;\n }\n\n if (appliedQuery.search) {\n return false;\n }\n\n // `folderId` is always present as a default filter; ignore it.\n const filterKeys = Object.keys(appliedQuery.filters ?? {}).filter(k => k !== \"folderId\");\n if (filterKeys.length > 0) {\n return false;\n }\n\n return true;\n }\n}\n\nexport const FileManagerPresenter = Abstraction.createImplementation({\n implementation: FileManagerPresenterImpl,\n dependencies: [\n ListPresenter,\n FolderTreePresenter,\n FileDetailsPresenter,\n FileManagerPermissions,\n ListTagsRepository,\n FileUploader,\n LocalStorage,\n ListFilesUseCase,\n FilesListCache,\n GetDescendantFoldersUseCase,\n FileModelProvider\n ]\n});\n"],"names":["VIEW_MODE_KEY","LAST_FOLDER_KEY","FileManagerPresenterImpl","listPresenter","folderTreePresenter","fileDetailsPresenter","permissions","tagsRepository","fileUploader","localStorage","listFilesUseCase","filesListCache","getDescendantFoldersUseCase","fileModelProvider","query","field","direction","key","value","id","ids","files","folderId","file","mode","dragging","parentFolderId","targetParentId","parentIds","makeAutoObservable","computed","stored","config","initialFolderId","dataSource","FileListDataSource","reaction","effectiveFolderId","model","runInAction","appliedQuery","filterKeys","Object","k","FileManagerPresenter","Abstraction","ListPresenter","FolderTreePresenter","FileDetailsPresenter","FileManagerPermissions","ListTagsRepository","FileUploader","LocalStorage","ListFilesUseCase","FilesListCache","GetDescendantFoldersUseCase","FileModelProvider"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"presentation/FileList/FileManagerPresenter.js","sources":["../../../src/presentation/FileList/FileManagerPresenter.ts"],"sourcesContent":["import { makeAutoObservable, reaction, runInAction, computed } from \"mobx\";\nimport type { FmFile } from \"@webiny/sdk\";\nimport type { CmsModel } from \"@webiny/app-headless-cms-common/types/index.js\";\nimport { ListPresenter } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport { FolderTreePresenter } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport { sortFolders } from \"@webiny/app-aco\";\nimport { LocalStorage } from \"@webiny/app/features/localStorage\";\nimport { GetDescendantFoldersUseCase } from \"@webiny/app-aco/features/folders/getDescendantFolders/abstractions.js\";\nimport {\n FileManagerPresenter as Abstraction,\n type IFileManagerPresenter,\n type IFileManagerViewModel,\n type IFileManagerActions,\n type IFileManagerInitConfig\n} from \"./abstractions.js\";\nimport { FileListDataSource } from \"./FileListDataSource.js\";\nimport { FileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport { FileManagerPermissions } from \"~/features/permissions/abstractions.js\";\nimport { ListTagsRepository } from \"~/features/tags/index.js\";\nimport { FileUploader } from \"~/features/fileUploader/index.js\";\nimport { ListFilesUseCase } from \"~/features/listFiles/index.js\";\nimport { FilesListCache } from \"~/features/shared/index.js\";\nimport { FileModelProvider } from \"~/features/fileModel/index.js\";\nimport type { IFileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport type { SelectedFile } from \"@webiny/app-admin/presentation/browserFilePicker/index.js\";\n\nconst VIEW_MODE_KEY = \"fm:viewMode\";\nconst LAST_FOLDER_KEY = \"fm:lastFolder\";\n\nclass FileManagerPresenterImpl implements IFileManagerPresenter {\n private _viewMode: \"table\" | \"grid\" = \"grid\";\n private _dragging = false;\n private _disposeReaction: (() => void) | null = null;\n private _fileDetails: IFileDetailsPresenter | null = null;\n private _showingFilters = false;\n private _fileModel: CmsModel | null = null;\n private _persistFolder = true;\n\n constructor(\n private listPresenter: ListPresenter.Interface<FmFile>,\n private folderTreePresenter: FolderTreePresenter.Interface,\n private fileDetailsPresenter: FileDetailsPresenter.Interface,\n private permissions: FileManagerPermissions.Interface,\n private tagsRepository: ListTagsRepository.Interface,\n private fileUploader: FileUploader.Interface,\n private localStorage: LocalStorage.Interface,\n private listFilesUseCase: ListFilesUseCase.Interface,\n private filesListCache: FilesListCache.Interface,\n private getDescendantFoldersUseCase: GetDescendantFoldersUseCase.Interface,\n private fileModelProvider: FileModelProvider.Interface\n ) {\n makeAutoObservable<\n FileManagerPresenterImpl,\n \"_disposeReaction\" | \"fileDetailsPresenter\" | \"_persistFolder\"\n >(this, {\n _disposeReaction: false,\n fileDetailsPresenter: false,\n _persistFolder: false,\n vm: computed\n });\n\n const stored = this.localStorage.get<\"table\" | \"grid\">(VIEW_MODE_KEY);\n if (stored === \"table\" || stored === \"grid\") {\n this._viewMode = stored;\n }\n }\n\n get vm(): IFileManagerViewModel {\n return {\n fileModel: this._fileModel,\n list: this.listPresenter.vm,\n folders: this.folderTreePresenter.vm,\n fileDetails: this._fileDetails,\n // Flat booleans for UI visibility (e.g., show/hide buttons).\n // Per-file methods for ownership-aware checks (e.g., \"own\" scope).\n permissions: {\n canRead: this.permissions.canRead(\"file\"),\n canCreate: this.permissions.canCreate(\"file\"),\n canEdit: this.permissions.canEdit(\"file\"),\n canDelete: this.permissions.canDelete(\"file\"),\n canEditFile: (file: FmFile) => this.permissions.canEdit(\"file\", file),\n canDeleteFile: (file: FmFile) => this.permissions.canDelete(\"file\", file)\n },\n upload: {\n jobs: this.fileUploader.vm.jobs,\n overallProgress: {\n percentage: this.fileUploader.vm.overallProgress.percentage,\n bytesSent: this.fileUploader.vm.overallProgress.sent,\n totalBytes: this.fileUploader.vm.overallProgress.total\n },\n isUploading: this.fileUploader.vm.isUploading\n },\n tags: this.tagsRepository.tags,\n loading: this.isLoading(),\n empty: this.isEmpty(),\n showFolders: this.shouldShowFolders(),\n childFolders: this.getSortedChildFolders(),\n viewMode: this._viewMode,\n dragging: this._dragging,\n showingFilters: this._showingFilters\n };\n }\n\n actions: IFileManagerActions = {\n search: {\n set: (query: string) => this.listPresenter.actions.search.set(query),\n clear: () => this.listPresenter.actions.search.clear()\n },\n sort: {\n set: (field: string, direction: \"ASC\" | \"DESC\") =>\n this.listPresenter.actions.sort.set(field, direction),\n toggle: (field: string) => this.listPresenter.actions.sort.toggle(field)\n },\n filter: {\n set: (key: string, value: unknown) => this.listPresenter.actions.filter.set(key, value),\n clear: (key: string) => this.listPresenter.actions.filter.clear(key),\n clearAll: () => this.listPresenter.actions.filter.clearAll(),\n show: () => this.listPresenter.actions.filter.show(),\n hide: () => this.listPresenter.actions.filter.hide()\n },\n selection: {\n toggle: (id: string) => this.listPresenter.actions.selection.toggle(id),\n selectRangeTo: (id: string) => this.listPresenter.actions.selection.selectRangeTo(id),\n selectAll: () => this.listPresenter.actions.selection.selectAll(),\n deselectAll: () => this.listPresenter.actions.selection.deselectAll(),\n selectRows: (ids: string[]) => this.listPresenter.actions.selection.selectRows(ids),\n isSelected: (id: string) => this.listPresenter.actions.selection.isSelected(id)\n },\n loadMore: () => this.listPresenter.actions.loadMore(),\n refresh: () => this.listPresenter.actions.refresh(),\n upload: async (files: SelectedFile[]) => {\n const folderId = this.folderTreePresenter.vm.currentFolderId ?? \"root\";\n await this.fileUploader.uploadMany(\n files.map(file => ({\n file: file.src.file,\n data: {\n name: file.name,\n type: file.type,\n location: { folderId }\n }\n }))\n );\n this.fileUploader.clear();\n },\n setViewMode: (mode: \"table\" | \"grid\") => {\n this._viewMode = mode;\n this.localStorage.set(VIEW_MODE_KEY, mode);\n },\n setDragging: (dragging: boolean) => {\n this._dragging = dragging;\n },\n showFilters: () => {\n this._showingFilters = true;\n },\n hideFilters: () => {\n this._showingFilters = false;\n },\n showFileDetails: (id: string) => {\n this._fileDetails = this.fileDetailsPresenter;\n void this.fileDetailsPresenter.loadFile(id);\n },\n hideFileDetails: () => {\n this._fileDetails = null;\n },\n folders: {\n // Clear search when navigating to a folder — gives the user a clean slate.\n selectFolder: (folderId: string | null) => {\n this.listPresenter.actions.search.clear();\n this.folderTreePresenter.selectFolder(folderId);\n },\n createFolder: (parentFolderId?: string) =>\n this.folderTreePresenter.createFolder(parentFolderId),\n editFolder: (folderId: string) => this.folderTreePresenter.editFolder(folderId),\n deleteFolder: (folderId: string) => this.folderTreePresenter.deleteFolder(folderId),\n moveFolder: (folderId: string, targetParentId: string | null) =>\n this.folderTreePresenter.moveFolder(folderId, targetParentId),\n loadChildFolders: (parentIds: string[]) =>\n this.folderTreePresenter.loadChildFolders(parentIds),\n canManageStructure: (folderId: string) =>\n this.folderTreePresenter.canManageStructure(folderId),\n getAncestorIds: (folderId: string) => this.folderTreePresenter.getAncestorIds(folderId),\n submitOperation: () => this.folderTreePresenter.submitOperation(),\n cancelOperation: () => {\n this.folderTreePresenter.cancelOperation();\n }\n }\n };\n\n init(config?: IFileManagerInitConfig): void {\n const initialFolderId =\n config?.initialFolderId ?? this.localStorage.get<string>(LAST_FOLDER_KEY) ?? \"root\";\n\n // Don't persist folder navigation when an explicit initial folder is provided (e.g., overlay).\n this._persistFolder = !config?.initialFolderId;\n\n const dataSource = new FileListDataSource(\n this.listFilesUseCase,\n this.filesListCache,\n this.getDescendantFoldersUseCase,\n config?.scope\n );\n\n this.listPresenter.init({\n dataSource,\n initialSort: { field: \"createdOn\", direction: \"DESC\" },\n initialFilters: { folderId: initialFolderId },\n limit: 50\n });\n\n if (initialFolderId !== \"root\") {\n this.folderTreePresenter.selectFolder(initialFolderId);\n }\n\n // Sync folder navigation → file list: when the user selects a folder in the tree,\n // update the folderId filter so the file list re-queries for that folder's contents.\n this._disposeReaction = reaction(\n () => this.folderTreePresenter.vm.currentFolderId,\n folderId => {\n const effectiveFolderId = folderId ?? \"root\";\n this.listPresenter.actions.filter.set(\"folderId\", effectiveFolderId);\n\n if (this._persistFolder) {\n this.localStorage.set(LAST_FOLDER_KEY, effectiveFolderId);\n }\n }\n );\n\n void this.tagsRepository.execute({});\n\n // Load the CMS model in the background. The FileModelModule already kicks off\n // loading at app startup, so this usually resolves from cache instantly.\n // The view shows a loading overlay until `_fileModel` is set.\n void this.fileModelProvider.getModel().then(model => {\n runInAction(() => {\n this._fileModel = model;\n });\n });\n }\n\n dispose(): void {\n if (this._disposeReaction) {\n this._disposeReaction();\n this._disposeReaction = null;\n }\n }\n\n private isLoading(): boolean {\n return this.listPresenter.vm.pagination.loading && this.listPresenter.vm.rows.length === 0;\n }\n\n private isEmpty(): boolean {\n if (this.listPresenter.vm.pagination.loading) {\n return false;\n }\n\n if (this.listPresenter.vm.rows.length > 0) {\n return false;\n }\n\n if (this.shouldShowFolders() && this.folderTreePresenter.vm.childFolders.length > 0) {\n return false;\n }\n\n return true;\n }\n\n private getSortedChildFolders() {\n if (!this.shouldShowFolders()) {\n return [];\n }\n return sortFolders(\n this.folderTreePresenter.vm.childFolders ?? [],\n this.listPresenter.vm.appliedQuery?.sort\n );\n }\n\n // Hide folders when the user is actively filtering or searching.\n // Uses `appliedQuery` (not `vm.search`) so folders stay visible while typing\n // and only disappear once the debounced query has actually executed.\n private shouldShowFolders(): boolean {\n const { appliedQuery } = this.listPresenter.vm;\n if (!appliedQuery) {\n return true;\n }\n\n if (appliedQuery.search) {\n return false;\n }\n\n // `folderId` is always present as a default filter; ignore it.\n const filterKeys = Object.keys(appliedQuery.filters ?? {}).filter(k => k !== \"folderId\");\n if (filterKeys.length > 0) {\n return false;\n }\n\n return true;\n }\n}\n\nexport const FileManagerPresenter = Abstraction.createImplementation({\n implementation: FileManagerPresenterImpl,\n dependencies: [\n ListPresenter,\n FolderTreePresenter,\n FileDetailsPresenter,\n FileManagerPermissions,\n ListTagsRepository,\n FileUploader,\n LocalStorage,\n ListFilesUseCase,\n FilesListCache,\n GetDescendantFoldersUseCase,\n FileModelProvider\n ]\n});\n"],"names":["VIEW_MODE_KEY","LAST_FOLDER_KEY","FileManagerPresenterImpl","listPresenter","folderTreePresenter","fileDetailsPresenter","permissions","tagsRepository","fileUploader","localStorage","listFilesUseCase","filesListCache","getDescendantFoldersUseCase","fileModelProvider","query","field","direction","key","value","id","ids","files","folderId","file","mode","dragging","parentFolderId","targetParentId","parentIds","makeAutoObservable","computed","stored","config","initialFolderId","dataSource","FileListDataSource","reaction","effectiveFolderId","model","runInAction","sortFolders","appliedQuery","filterKeys","Object","k","FileManagerPresenter","Abstraction","ListPresenter","FolderTreePresenter","FileDetailsPresenter","FileManagerPermissions","ListTagsRepository","FileUploader","LocalStorage","ListFilesUseCase","FilesListCache","GetDescendantFoldersUseCase","FileModelProvider"],"mappings":"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"}
|
|
@@ -3,6 +3,7 @@ import type { CmsModel } from "@webiny/app-headless-cms-common/types/index.js";
|
|
|
3
3
|
import type { IListViewModel } from "@webiny/app-admin/presentation/listPresenter/abstractions.js";
|
|
4
4
|
import type { IListActions } from "@webiny/app-admin/presentation/listPresenter/abstractions.js";
|
|
5
5
|
import type { IFolderTreeViewModel } from "@webiny/app-aco/presentation/folderTree/abstractions.js";
|
|
6
|
+
import type { FolderDto } from "@webiny/app-aco";
|
|
6
7
|
import type { IFileDetailsPresenter } from "../FileDetails/abstractions.js";
|
|
7
8
|
import type { FmFile } from "../../features/shared/types.js";
|
|
8
9
|
import type { FmTag } from "../../features/shared/types.js";
|
|
@@ -29,6 +30,7 @@ export interface IFileManagerViewModel {
|
|
|
29
30
|
loading: boolean;
|
|
30
31
|
empty: boolean;
|
|
31
32
|
showFolders: boolean;
|
|
33
|
+
childFolders: FolderDto[];
|
|
32
34
|
viewMode: "table" | "grid";
|
|
33
35
|
dragging: boolean;
|
|
34
36
|
showingFilters: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/FileList/abstractions.js","sources":["../../../src/presentation/FileList/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { SelectedFile } from \"@webiny/app-admin/presentation/browserFilePicker/index.js\";\nimport type { CmsModel } from \"@webiny/app-headless-cms-common/types/index.js\";\nimport type { IListViewModel } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport type { IListActions } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport type { IFolderTreeViewModel } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport type { IFileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\nimport type { FmTag } from \"../../features/shared/types.js\";\nimport type { UploadJob } from \"../../features/fileUploader/abstractions.js\";\n\n// ---------------------------------------------------------------------------\n// Init config passed to presenter.init().\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerInitConfig {\n initialFolderId?: string;\n scope?: string;\n}\n\n// ---------------------------------------------------------------------------\n// FileManagerViewModel\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerViewModel {\n fileModel: CmsModel | null;\n list: IListViewModel<FmFile>;\n folders: IFolderTreeViewModel;\n fileDetails: IFileDetailsPresenter | null;\n tags: FmTag[];\n upload: {\n jobs: UploadJob[];\n overallProgress: { percentage: number; bytesSent: number; totalBytes: number };\n isUploading: boolean;\n };\n loading: boolean;\n empty: boolean;\n showFolders: boolean;\n viewMode: \"table\" | \"grid\";\n dragging: boolean;\n showingFilters: boolean;\n permissions: {\n canRead: boolean;\n canCreate: boolean;\n canEdit: boolean;\n canDelete: boolean;\n canEditFile: (file: FmFile) => boolean;\n canDeleteFile: (file: FmFile) => boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Folder actions exposed by the FileManagerPresenter.\n// ---------------------------------------------------------------------------\n\nexport interface IFolderActions {\n selectFolder(folderId: string | null): void;\n createFolder(parentFolderId?: string): void;\n editFolder(folderId: string): void;\n deleteFolder(folderId: string): Promise<void>;\n moveFolder(folderId: string, targetParentId: string | null): Promise<void>;\n loadChildFolders(parentIds: string[]): Promise<void>;\n canManageStructure(folderId: string): boolean;\n getAncestorIds(folderId: string): string[];\n submitOperation(): Promise<boolean>;\n cancelOperation(): void;\n}\n\n// ---------------------------------------------------------------------------\n// FileManagerPresenter actions (extends ListActions with domain-specific actions).\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerActions extends IListActions {\n showFileDetails(id: string): void;\n hideFileDetails(): void;\n upload(files: SelectedFile[]): Promise<void>;\n setViewMode(mode: \"table\" | \"grid\"): void;\n setDragging(dragging: boolean): void;\n showFilters(): void;\n hideFilters(): void;\n folders: IFolderActions;\n}\n\n// ---------------------------------------------------------------------------\n// IFileManagerPresenter\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerPresenter {\n vm: IFileManagerViewModel;\n actions: IFileManagerActions;\n init(config?: IFileManagerInitConfig): void;\n dispose(): void;\n}\n\nexport const FileManagerPresenter =\n createAbstraction<IFileManagerPresenter>(\"FileManagerPresenter\");\n\nexport namespace FileManagerPresenter {\n export type Interface = IFileManagerPresenter;\n export type ViewModel = IFileManagerViewModel;\n export type Actions = IFileManagerActions;\n export type InitConfig = IFileManagerInitConfig;\n}\n"],"names":["FileManagerPresenter","createAbstraction"],"mappings":";
|
|
1
|
+
{"version":3,"file":"presentation/FileList/abstractions.js","sources":["../../../src/presentation/FileList/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { SelectedFile } from \"@webiny/app-admin/presentation/browserFilePicker/index.js\";\nimport type { CmsModel } from \"@webiny/app-headless-cms-common/types/index.js\";\nimport type { IListViewModel } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport type { IListActions } from \"@webiny/app-admin/presentation/listPresenter/abstractions.js\";\nimport type { IFolderTreeViewModel } from \"@webiny/app-aco/presentation/folderTree/abstractions.js\";\nimport type { FolderDto } from \"@webiny/app-aco\";\nimport type { IFileDetailsPresenter } from \"../FileDetails/abstractions.js\";\nimport type { FmFile } from \"../../features/shared/types.js\";\nimport type { FmTag } from \"../../features/shared/types.js\";\nimport type { UploadJob } from \"../../features/fileUploader/abstractions.js\";\n\n// ---------------------------------------------------------------------------\n// Init config passed to presenter.init().\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerInitConfig {\n initialFolderId?: string;\n scope?: string;\n}\n\n// ---------------------------------------------------------------------------\n// FileManagerViewModel\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerViewModel {\n fileModel: CmsModel | null;\n list: IListViewModel<FmFile>;\n folders: IFolderTreeViewModel;\n fileDetails: IFileDetailsPresenter | null;\n tags: FmTag[];\n upload: {\n jobs: UploadJob[];\n overallProgress: { percentage: number; bytesSent: number; totalBytes: number };\n isUploading: boolean;\n };\n loading: boolean;\n empty: boolean;\n showFolders: boolean;\n childFolders: FolderDto[];\n viewMode: \"table\" | \"grid\";\n dragging: boolean;\n showingFilters: boolean;\n permissions: {\n canRead: boolean;\n canCreate: boolean;\n canEdit: boolean;\n canDelete: boolean;\n canEditFile: (file: FmFile) => boolean;\n canDeleteFile: (file: FmFile) => boolean;\n };\n}\n\n// ---------------------------------------------------------------------------\n// Folder actions exposed by the FileManagerPresenter.\n// ---------------------------------------------------------------------------\n\nexport interface IFolderActions {\n selectFolder(folderId: string | null): void;\n createFolder(parentFolderId?: string): void;\n editFolder(folderId: string): void;\n deleteFolder(folderId: string): Promise<void>;\n moveFolder(folderId: string, targetParentId: string | null): Promise<void>;\n loadChildFolders(parentIds: string[]): Promise<void>;\n canManageStructure(folderId: string): boolean;\n getAncestorIds(folderId: string): string[];\n submitOperation(): Promise<boolean>;\n cancelOperation(): void;\n}\n\n// ---------------------------------------------------------------------------\n// FileManagerPresenter actions (extends ListActions with domain-specific actions).\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerActions extends IListActions {\n showFileDetails(id: string): void;\n hideFileDetails(): void;\n upload(files: SelectedFile[]): Promise<void>;\n setViewMode(mode: \"table\" | \"grid\"): void;\n setDragging(dragging: boolean): void;\n showFilters(): void;\n hideFilters(): void;\n folders: IFolderActions;\n}\n\n// ---------------------------------------------------------------------------\n// IFileManagerPresenter\n// ---------------------------------------------------------------------------\n\nexport interface IFileManagerPresenter {\n vm: IFileManagerViewModel;\n actions: IFileManagerActions;\n init(config?: IFileManagerInitConfig): void;\n dispose(): void;\n}\n\nexport const FileManagerPresenter =\n createAbstraction<IFileManagerPresenter>(\"FileManagerPresenter\");\n\nexport namespace FileManagerPresenter {\n export type Interface = IFileManagerPresenter;\n export type ViewModel = IFileManagerViewModel;\n export type Actions = IFileManagerActions;\n export type InitConfig = IFileManagerInitConfig;\n}\n"],"names":["FileManagerPresenter","createAbstraction"],"mappings":";AAgGO,MAAMA,uBACTC,kBAAyC"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { BulkActionBar, getFilesLabel } from "./BulkActionBar.js";
|
|
2
2
|
export { BulkActionDelete } from "./BulkActionDelete.js";
|
|
3
3
|
export { BulkActionMove } from "./BulkActionMove.js";
|
|
4
|
-
export { BulkActionEdit } from "./BulkActionEdit.js";
|
|
5
4
|
export { BulkAction } from "./useBulkActionWorker.js";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
export { BulkActionBar, getFilesLabel } from "./BulkActionBar.js";
|
|
2
2
|
export { BulkActionDelete } from "./BulkActionDelete.js";
|
|
3
3
|
export { BulkActionMove } from "./BulkActionMove.js";
|
|
4
|
-
export { BulkActionEdit } from "./BulkActionEdit.js";
|
|
5
4
|
export { BulkAction } from "./useBulkActionWorker.js";
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import type { CallbackParams } from "@webiny/app-admin";
|
|
2
2
|
import type { FmFile } from "../../../../features/shared/types.js";
|
|
3
3
|
import { getFilesLabel } from "./BulkActionBar.js";
|
|
4
|
+
declare const useWorker: () => {
|
|
5
|
+
items: FmFile[];
|
|
6
|
+
process: (callback: (items: FmFile[]) => void) => void;
|
|
7
|
+
processInSeries: (callback: ({ item, allItems, report }: CallbackParams<FmFile>) => Promise<void>, chunkSize?: number) => Promise<void>;
|
|
8
|
+
resetItems: () => void;
|
|
9
|
+
results: import("@webiny/app-admin").Result[];
|
|
10
|
+
resetResults: () => Promise<void>;
|
|
11
|
+
};
|
|
4
12
|
export declare const BulkAction: {
|
|
5
13
|
useButtons: () => import("@webiny/app-admin/components/Buttons/useButtons.js").ButtonsProviderContext;
|
|
6
|
-
useWorker:
|
|
7
|
-
items: FmFile[];
|
|
8
|
-
process: (callback: (items: FmFile[]) => void) => void;
|
|
9
|
-
processInSeries: (callback: ({ item, allItems, report }: CallbackParams<FmFile>) => Promise<void>, chunkSize?: number) => Promise<void>;
|
|
10
|
-
resetItems: () => void;
|
|
11
|
-
results: import("@webiny/app-admin").Result[];
|
|
12
|
-
resetResults: () => Promise<void>;
|
|
13
|
-
};
|
|
14
|
+
useWorker: typeof useWorker;
|
|
14
15
|
useDialog: () => import("@webiny/app-admin").UseDialogWithReportResponse;
|
|
15
16
|
};
|
|
16
17
|
export { getFilesLabel };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback,
|
|
1
|
+
import { useCallback, useMemo, useRef } from "react";
|
|
2
2
|
import { Worker, useButtons, useDialogWithReport } from "@webiny/app-admin";
|
|
3
3
|
import { useFileManagerPresenter } from "../../FileManagerPresenterProvider.js";
|
|
4
4
|
const useWorker = ()=>{
|
|
@@ -11,13 +11,7 @@ const useWorker = ()=>{
|
|
|
11
11
|
vm.list.selection.selectedIds,
|
|
12
12
|
vm.list.rows
|
|
13
13
|
]);
|
|
14
|
-
useEffect(()=>{
|
|
15
|
-
worker.items = selectedFiles;
|
|
16
|
-
}, [
|
|
17
|
-
selectedFiles.length
|
|
18
|
-
]);
|
|
19
14
|
const resetItems = useCallback(()=>{
|
|
20
|
-
worker.items = [];
|
|
21
15
|
actions.selection.deselectAll();
|
|
22
16
|
}, []);
|
|
23
17
|
const resetResults = useCallback(async ()=>{
|
|
@@ -25,8 +19,8 @@ const useWorker = ()=>{
|
|
|
25
19
|
}, []);
|
|
26
20
|
return {
|
|
27
21
|
items: selectedFiles,
|
|
28
|
-
process: (callback)=>worker.process(callback),
|
|
29
|
-
processInSeries: async (callback, chunkSize)=>worker.processInSeries(callback, chunkSize),
|
|
22
|
+
process: (callback)=>worker.process(selectedFiles, callback),
|
|
23
|
+
processInSeries: async (callback, chunkSize)=>worker.processInSeries(selectedFiles, callback, chunkSize),
|
|
30
24
|
resetItems,
|
|
31
25
|
results: worker.results,
|
|
32
26
|
resetResults
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/FileList/components/BulkActions/useBulkActionWorker.js","sources":["../../../../../src/presentation/FileList/components/BulkActions/useBulkActionWorker.ts"],"sourcesContent":["import { useCallback,
|
|
1
|
+
{"version":3,"file":"presentation/FileList/components/BulkActions/useBulkActionWorker.js","sources":["../../../../../src/presentation/FileList/components/BulkActions/useBulkActionWorker.ts"],"sourcesContent":["import { useCallback, useMemo, useRef } from \"react\";\nimport type { CallbackParams } from \"@webiny/app-admin\";\nimport { useButtons, useDialogWithReport, Worker } from \"@webiny/app-admin\";\nimport { useFileManagerPresenter } from \"../../FileManagerPresenterProvider.js\";\nimport type { FmFile } from \"~/features/shared/types.js\";\nimport { getFilesLabel } from \"./BulkActionBar.js\";\n\n// Hook that bridges the new presenter-based selection with the Worker pattern.\nconst useWorker = () => {\n const { vm, actions } = useFileManagerPresenter();\n const { current: worker } = useRef(new Worker<FmFile>());\n\n const selectedFiles = useMemo(() => {\n const { selectedIds } = vm.list.selection;\n return vm.list.rows.filter(row => selectedIds.has(row.id));\n }, [vm.list.selection.selectedIds, vm.list.rows]);\n\n const resetItems = useCallback(() => {\n actions.selection.deselectAll();\n }, []);\n\n const resetResults = useCallback(async () => {\n worker.resetResults();\n }, []);\n\n return {\n items: selectedFiles,\n process: (callback: (items: FmFile[]) => void) => worker.process(selectedFiles, callback),\n processInSeries: async (\n callback: ({ item, allItems, report }: CallbackParams<FmFile>) => Promise<void>,\n chunkSize?: number\n ) => worker.processInSeries(selectedFiles, callback, chunkSize),\n resetItems,\n results: worker.results,\n resetResults\n };\n};\n\n// Compose the BulkAction utilities for the new v2 architecture.\nexport const BulkAction = {\n useButtons,\n useWorker,\n useDialog: useDialogWithReport\n};\n\nexport { getFilesLabel };\n"],"names":["useWorker","vm","actions","useFileManagerPresenter","worker","useRef","Worker","selectedFiles","useMemo","selectedIds","row","resetItems","useCallback","resetResults","callback","chunkSize","BulkAction","useButtons","useDialogWithReport"],"mappings":";;;AAQA,MAAMA,YAAY;IACd,MAAM,EAAEC,EAAE,EAAEC,OAAO,EAAE,GAAGC;IACxB,MAAM,EAAE,SAASC,MAAM,EAAE,GAAGC,OAAO,IAAIC;IAEvC,MAAMC,gBAAgBC,QAAQ;QAC1B,MAAM,EAAEC,WAAW,EAAE,GAAGR,GAAG,IAAI,CAAC,SAAS;QACzC,OAAOA,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAACS,CAAAA,MAAOD,YAAY,GAAG,CAACC,IAAI,EAAE;IAC5D,GAAG;QAACT,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW;QAAEA,GAAG,IAAI,CAAC,IAAI;KAAC;IAEhD,MAAMU,aAAaC,YAAY;QAC3BV,QAAQ,SAAS,CAAC,WAAW;IACjC,GAAG,EAAE;IAEL,MAAMW,eAAeD,YAAY;QAC7BR,OAAO,YAAY;IACvB,GAAG,EAAE;IAEL,OAAO;QACH,OAAOG;QACP,SAAS,CAACO,WAAwCV,OAAO,OAAO,CAACG,eAAeO;QAChF,iBAAiB,OACbA,UACAC,YACCX,OAAO,eAAe,CAACG,eAAeO,UAAUC;QACrDJ;QACA,SAASP,OAAO,OAAO;QACvBS;IACJ;AACJ;AAGO,MAAMG,aAAa;IACtBC,YAAUA;IACVjB,WAAAA;IACA,WAAWkB;AACf"}
|
|
@@ -116,7 +116,7 @@ const FileGrid_FileGrid = observer(function() {
|
|
|
116
116
|
const presenter = useFileManagerPresenter();
|
|
117
117
|
const { vm, actions } = presenter;
|
|
118
118
|
const overlay = useOverlay();
|
|
119
|
-
const childFolders = vm.
|
|
119
|
+
const childFolders = vm.childFolders;
|
|
120
120
|
const handleFolderNavigate = useCallback((folderId)=>{
|
|
121
121
|
actions.folders.selectFolder(folderId);
|
|
122
122
|
}, [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/FileList/components/Grid/FileGrid.js","sources":["../../../../../src/presentation/FileList/components/Grid/FileGrid.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport LazyLoad from \"react-lazy-load\";\nimport { cn, CheckboxPrimitive, Text, TimeAgo } from \"@webiny/admin-ui\";\nimport { useOverlay } from \"~/presentation/FileManager/OverlayContext.js\";\nimport { i18n } from \"@webiny/app/i18n/index.js\";\nimport { FolderIcon } from \"@webiny/app-aco\";\nimport { useFileManagerPresenter } from \"../../FileManagerPresenterProvider.js\";\nimport { useFileManagerConfig } from \"~/presentation/config/FileManagerViewConfig.js\";\nimport { FileProvider } from \"~/presentation/contexts/FileProvider.js\";\nimport type { FolderDto } from \"@webiny/app-aco\";\nimport type { FileItem } from \"~/domain/types.js\";\n\nconst t = i18n.ns(\"app-file-manager/presentation/file-grid\");\n\n// ---------------------------------------------------------------------------\n// FolderCard — renders a single folder in the grid.\n// ---------------------------------------------------------------------------\n\ninterface FolderCardProps {\n folder: FolderDto;\n onNavigate: (folderId: string) => void;\n}\n\nconst FolderCard = ({ folder, onNavigate }: FolderCardProps) => {\n return (\n <div\n className={cn([\n \"group\",\n \"bg-neutral-base rounded-lg\",\n \"shadow-sm hover:shadow-lg\",\n \"border-sm border-solid border-neutral-base hover:border-neutral-dimmed-darker\",\n \"transition-shadow duration-250 ease-in-out\",\n \"overflow-hidden\",\n \"cursor-pointer\"\n ])}\n onClick={() => onNavigate(folder.id)}\n data-testid={\"fm-grid-folder-card\"}\n data-folder-id={folder.id}\n >\n <div style={{ height: 150 }} className={\"flex items-center justify-center\"}>\n <FolderIcon width={90} height={90} />\n </div>\n <div className={\"px-md py-sm-extra\"}>\n <Text size={\"sm\"} as={\"div\"} className={\"truncate text-neutral-primary\"}>\n {folder.title}\n </Text>\n <Text size={\"sm\"} as={\"div\"} className={\"truncate text-neutral-dimmed\"}>\n {t`Folder`}\n </Text>\n </div>\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// FileCard — renders a single file in the grid.\n// ---------------------------------------------------------------------------\n\ninterface FileCardProps {\n file: FileItem;\n selected: boolean;\n onToggle: (id: string, shiftKey: boolean) => void;\n}\n\nconst FileCard = observer(function FileCard({ file, selected, onToggle }: FileCardProps) {\n const { browser, getThumbnailRenderer } = useFileManagerConfig();\n const { itemActions } = browser.grid;\n\n const renderer = getThumbnailRenderer(browser.grid.itemThumbnails, file);\n\n const handleClick = useCallback(\n (e: React.MouseEvent) => {\n e.stopPropagation();\n onToggle(file.id, e.shiftKey);\n },\n [file.id, onToggle]\n );\n\n return (\n <FileProvider file={file}>\n <div\n onClick={handleClick}\n className={cn([\n \"group\",\n \"bg-neutral-base rounded-lg\",\n \"shadow-sm hover:shadow-lg\",\n \"border-sm border-solid border-neutral-base hover:border-neutral-dimmed-darker\",\n selected && \"ring-md ring-primary-strong\",\n \"transition-shadow duration-250 ease-in-out\",\n \"overflow-hidden\",\n \"cursor-pointer\"\n ])}\n data-testid={\"fm-grid-file-card\"}\n data-file-id={file.id}\n >\n <div className={\"relative\"}>\n {/* Selection checkbox. */}\n <div\n className={cn([\n \"p-xs rounded-md\",\n \"bg-neutral-base/30\",\n \"absolute top-sm left-sm z-[1]\",\n selected ? \"visible\" : \"invisible group-hover:visible\"\n ])}\n >\n <CheckboxPrimitive\n onClick={handleClick}\n checked={selected}\n onChange={() => void 0}\n />\n </div>\n {/* Item actions (hover). */}\n <div\n className={cn([\n \"invisible group-hover:visible\",\n \"flex items-center gap-xxs\",\n \"p-xs\",\n \"absolute top-xs-plus right-xs-plus z-[1]\"\n ])}\n >\n {itemActions.map(action => (\n <React.Fragment key={action.name}>{action.element}</React.Fragment>\n ))}\n </div>\n {/* Thumbnail. */}\n <LazyLoad\n height={150}\n offset={\"300px\"}\n className={cn([\n \"bg-neutral-muted\",\n \"flex items-center justify-center\",\n \"text-neutral-strong text-sm\"\n ])}\n >\n {renderer?.element || null}\n </LazyLoad>\n </div>\n {/* File label. */}\n <div className={\"px-md py-sm-extra\"}>\n <Text size={\"sm\"} as={\"div\"} className={\"truncate text-neutral-primary\"}>\n {file.name}\n </Text>\n <Text size={\"sm\"} as={\"div\"} className={\"truncate text-neutral-dimmed\"}>\n {file.type} {\" // \"} <TimeAgo datetime={file.createdOn} />\n </Text>\n </div>\n </div>\n </FileProvider>\n );\n});\n\n// ---------------------------------------------------------------------------\n// FileGrid — the main grid view component.\n// ---------------------------------------------------------------------------\n\n/**\n * Grid/Gallery View component driven by FileListPresenter vm.\n * Renders file thumbnail cards and folder cards in a responsive grid layout.\n * Wires click-to-select through presenter.actions.selection.\n */\nexport const FileGrid = observer(function FileGrid() {\n const presenter = useFileManagerPresenter();\n const { vm, actions } = presenter;\n\n const overlay = useOverlay();\n const childFolders = vm.folders.childFolders;\n\n // Handle folder navigation.\n const handleFolderNavigate = useCallback(\n (folderId: string) => {\n actions.folders.selectFolder(folderId);\n },\n [actions.filter]\n );\n\n const handleToggle = (id: string, shiftKey: boolean) => {\n // Shift-click always does range selection, regardless of overlay mode.\n if (shiftKey) {\n actions.selection.selectRangeTo(id);\n return;\n }\n\n if (overlay) {\n const file = vm.list.rows.find(f => f.id === id);\n if (file) {\n overlay.onFileClick(file);\n }\n return;\n }\n\n actions.selection.toggle(id);\n };\n\n // Deselect all when clicking the grid background.\n const handleBackgroundClick = useCallback(() => {\n actions.selection.deselectAll();\n }, [actions.selection]);\n\n return (\n <div\n className={cn([\"p-lg\", \"grid gap-md\"])}\n style={{ gridTemplateColumns: \"repeat(auto-fill, minmax(200px, 1fr))\" }}\n onClick={handleBackgroundClick}\n data-testid={\"fm-file-grid\"}\n >\n {vm.showFolders &&\n childFolders.map(folder => (\n <FolderCard key={folder.id} folder={folder} onNavigate={handleFolderNavigate} />\n ))}\n {vm.list.rows.map(file => (\n <FileCard\n key={file.id}\n file={file}\n selected={vm.list.selection.selectedIds.has(file.id)}\n onToggle={handleToggle}\n />\n ))}\n </div>\n );\n});\n"],"names":["t","i18n","FolderCard","folder","onNavigate","cn","FolderIcon","Text","FileCard","observer","file","selected","onToggle","browser","getThumbnailRenderer","useFileManagerConfig","itemActions","renderer","handleClick","useCallback","e","FileProvider","CheckboxPrimitive","action","React","LazyLoad","TimeAgo","FileGrid","presenter","useFileManagerPresenter","vm","actions","overlay","useOverlay","childFolders","handleFolderNavigate","folderId","handleToggle","id","shiftKey","f","handleBackgroundClick"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"presentation/FileList/components/Grid/FileGrid.js","sources":["../../../../../src/presentation/FileList/components/Grid/FileGrid.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport LazyLoad from \"react-lazy-load\";\nimport { cn, CheckboxPrimitive, Text, TimeAgo } from \"@webiny/admin-ui\";\nimport { useOverlay } from \"~/presentation/FileManager/OverlayContext.js\";\nimport { i18n } from \"@webiny/app/i18n/index.js\";\nimport { FolderIcon } from \"@webiny/app-aco\";\nimport { useFileManagerPresenter } from \"../../FileManagerPresenterProvider.js\";\nimport { useFileManagerConfig } from \"~/presentation/config/FileManagerViewConfig.js\";\nimport { FileProvider } from \"~/presentation/contexts/FileProvider.js\";\nimport type { FolderDto } from \"@webiny/app-aco\";\nimport type { FileItem } from \"~/domain/types.js\";\n\nconst t = i18n.ns(\"app-file-manager/presentation/file-grid\");\n\n// ---------------------------------------------------------------------------\n// FolderCard — renders a single folder in the grid.\n// ---------------------------------------------------------------------------\n\ninterface FolderCardProps {\n folder: FolderDto;\n onNavigate: (folderId: string) => void;\n}\n\nconst FolderCard = ({ folder, onNavigate }: FolderCardProps) => {\n return (\n <div\n className={cn([\n \"group\",\n \"bg-neutral-base rounded-lg\",\n \"shadow-sm hover:shadow-lg\",\n \"border-sm border-solid border-neutral-base hover:border-neutral-dimmed-darker\",\n \"transition-shadow duration-250 ease-in-out\",\n \"overflow-hidden\",\n \"cursor-pointer\"\n ])}\n onClick={() => onNavigate(folder.id)}\n data-testid={\"fm-grid-folder-card\"}\n data-folder-id={folder.id}\n >\n <div style={{ height: 150 }} className={\"flex items-center justify-center\"}>\n <FolderIcon width={90} height={90} />\n </div>\n <div className={\"px-md py-sm-extra\"}>\n <Text size={\"sm\"} as={\"div\"} className={\"truncate text-neutral-primary\"}>\n {folder.title}\n </Text>\n <Text size={\"sm\"} as={\"div\"} className={\"truncate text-neutral-dimmed\"}>\n {t`Folder`}\n </Text>\n </div>\n </div>\n );\n};\n\n// ---------------------------------------------------------------------------\n// FileCard — renders a single file in the grid.\n// ---------------------------------------------------------------------------\n\ninterface FileCardProps {\n file: FileItem;\n selected: boolean;\n onToggle: (id: string, shiftKey: boolean) => void;\n}\n\nconst FileCard = observer(function FileCard({ file, selected, onToggle }: FileCardProps) {\n const { browser, getThumbnailRenderer } = useFileManagerConfig();\n const { itemActions } = browser.grid;\n\n const renderer = getThumbnailRenderer(browser.grid.itemThumbnails, file);\n\n const handleClick = useCallback(\n (e: React.MouseEvent) => {\n e.stopPropagation();\n onToggle(file.id, e.shiftKey);\n },\n [file.id, onToggle]\n );\n\n return (\n <FileProvider file={file}>\n <div\n onClick={handleClick}\n className={cn([\n \"group\",\n \"bg-neutral-base rounded-lg\",\n \"shadow-sm hover:shadow-lg\",\n \"border-sm border-solid border-neutral-base hover:border-neutral-dimmed-darker\",\n selected && \"ring-md ring-primary-strong\",\n \"transition-shadow duration-250 ease-in-out\",\n \"overflow-hidden\",\n \"cursor-pointer\"\n ])}\n data-testid={\"fm-grid-file-card\"}\n data-file-id={file.id}\n >\n <div className={\"relative\"}>\n {/* Selection checkbox. */}\n <div\n className={cn([\n \"p-xs rounded-md\",\n \"bg-neutral-base/30\",\n \"absolute top-sm left-sm z-[1]\",\n selected ? \"visible\" : \"invisible group-hover:visible\"\n ])}\n >\n <CheckboxPrimitive\n onClick={handleClick}\n checked={selected}\n onChange={() => void 0}\n />\n </div>\n {/* Item actions (hover). */}\n <div\n className={cn([\n \"invisible group-hover:visible\",\n \"flex items-center gap-xxs\",\n \"p-xs\",\n \"absolute top-xs-plus right-xs-plus z-[1]\"\n ])}\n >\n {itemActions.map(action => (\n <React.Fragment key={action.name}>{action.element}</React.Fragment>\n ))}\n </div>\n {/* Thumbnail. */}\n <LazyLoad\n height={150}\n offset={\"300px\"}\n className={cn([\n \"bg-neutral-muted\",\n \"flex items-center justify-center\",\n \"text-neutral-strong text-sm\"\n ])}\n >\n {renderer?.element || null}\n </LazyLoad>\n </div>\n {/* File label. */}\n <div className={\"px-md py-sm-extra\"}>\n <Text size={\"sm\"} as={\"div\"} className={\"truncate text-neutral-primary\"}>\n {file.name}\n </Text>\n <Text size={\"sm\"} as={\"div\"} className={\"truncate text-neutral-dimmed\"}>\n {file.type} {\" // \"} <TimeAgo datetime={file.createdOn} />\n </Text>\n </div>\n </div>\n </FileProvider>\n );\n});\n\n// ---------------------------------------------------------------------------\n// FileGrid — the main grid view component.\n// ---------------------------------------------------------------------------\n\n/**\n * Grid/Gallery View component driven by FileListPresenter vm.\n * Renders file thumbnail cards and folder cards in a responsive grid layout.\n * Wires click-to-select through presenter.actions.selection.\n */\nexport const FileGrid = observer(function FileGrid() {\n const presenter = useFileManagerPresenter();\n const { vm, actions } = presenter;\n\n const overlay = useOverlay();\n const childFolders = vm.childFolders;\n\n // Handle folder navigation.\n const handleFolderNavigate = useCallback(\n (folderId: string) => {\n actions.folders.selectFolder(folderId);\n },\n [actions.filter]\n );\n\n const handleToggle = (id: string, shiftKey: boolean) => {\n // Shift-click always does range selection, regardless of overlay mode.\n if (shiftKey) {\n actions.selection.selectRangeTo(id);\n return;\n }\n\n if (overlay) {\n const file = vm.list.rows.find(f => f.id === id);\n if (file) {\n overlay.onFileClick(file);\n }\n return;\n }\n\n actions.selection.toggle(id);\n };\n\n // Deselect all when clicking the grid background.\n const handleBackgroundClick = useCallback(() => {\n actions.selection.deselectAll();\n }, [actions.selection]);\n\n return (\n <div\n className={cn([\"p-lg\", \"grid gap-md\"])}\n style={{ gridTemplateColumns: \"repeat(auto-fill, minmax(200px, 1fr))\" }}\n onClick={handleBackgroundClick}\n data-testid={\"fm-file-grid\"}\n >\n {vm.showFolders &&\n childFolders.map(folder => (\n <FolderCard key={folder.id} folder={folder} onNavigate={handleFolderNavigate} />\n ))}\n {vm.list.rows.map(file => (\n <FileCard\n key={file.id}\n file={file}\n selected={vm.list.selection.selectedIds.has(file.id)}\n onToggle={handleToggle}\n />\n ))}\n </div>\n );\n});\n"],"names":["t","i18n","FolderCard","folder","onNavigate","cn","FolderIcon","Text","FileCard","observer","file","selected","onToggle","browser","getThumbnailRenderer","useFileManagerConfig","itemActions","renderer","handleClick","useCallback","e","FileProvider","CheckboxPrimitive","action","React","LazyLoad","TimeAgo","FileGrid","presenter","useFileManagerPresenter","vm","actions","overlay","useOverlay","childFolders","handleFolderNavigate","folderId","handleToggle","id","shiftKey","f","handleBackgroundClick"],"mappings":";;;;;;;;;;AAaA,MAAMA,IAAIC,KAAK,EAAE,CAAC;AAWlB,MAAMC,aAAa,CAAC,EAAEC,MAAM,EAAEC,UAAU,EAAmB,GAChD,WAAP,GACI,oBAAC;QACG,WAAWC,GAAG;YACV;YACA;YACA;YACA;YACA;YACA;YACA;SACH;QACD,SAAS,IAAMD,WAAWD,OAAO,EAAE;QACnC,eAAa;QACb,kBAAgBA,OAAO,EAAE;qBAEzB,oBAAC;QAAI,OAAO;YAAE,QAAQ;QAAI;QAAG,WAAW;qBACpC,oBAACG,YAAUA;QAAC,OAAO;QAAI,QAAQ;uBAEnC,oBAAC;QAAI,WAAW;qBACZ,oBAACC,MAAIA;QAAC,MAAM;QAAM,IAAI;QAAO,WAAW;OACnCJ,OAAO,KAAK,iBAEjB,oBAACI,MAAIA;QAAC,MAAM;QAAM,IAAI;QAAO,WAAW;OACnCP,CAAC,CAAC,MAAM,CAAC;AAiB9B,MAAMQ,oBAAWC,SAAS,SAAkB,EAAEC,IAAI,EAAEC,QAAQ,EAAEC,QAAQ,EAAiB;IACnF,MAAM,EAAEC,OAAO,EAAEC,oBAAoB,EAAE,GAAGC;IAC1C,MAAM,EAAEC,WAAW,EAAE,GAAGH,QAAQ,IAAI;IAEpC,MAAMI,WAAWH,qBAAqBD,QAAQ,IAAI,CAAC,cAAc,EAAEH;IAEnE,MAAMQ,cAAcC,YAChB,CAACC;QACGA,EAAE,eAAe;QACjBR,SAASF,KAAK,EAAE,EAAEU,EAAE,QAAQ;IAChC,GACA;QAACV,KAAK,EAAE;QAAEE;KAAS;IAGvB,OAAO,WAAP,GACI,oBAACS,cAAYA;QAAC,MAAMX;qBAChB,oBAAC;QACG,SAASQ;QACT,WAAWb,GAAG;YACV;YACA;YACA;YACA;YACAM,YAAY;YACZ;YACA;YACA;SACH;QACD,eAAa;QACb,gBAAcD,KAAK,EAAE;qBAErB,oBAAC;QAAI,WAAW;qBAEZ,oBAAC;QACG,WAAWL,GAAG;YACV;YACA;YACA;YACAM,WAAW,YAAY;SAC1B;qBAED,oBAACW,mBAAiBA;QACd,SAASJ;QACT,SAASP;QACT,UAAU,IAAM,KAAK;uBAI7B,oBAAC;QACG,WAAWN,GAAG;YACV;YACA;YACA;YACA;SACH;OAEAW,YAAY,GAAG,CAACO,CAAAA,SAAAA,WAAAA,GACb,oBAACC,MAAAA,QAAc;YAAC,KAAKD,OAAO,IAAI;WAAGA,OAAO,OAAO,mBAIzD,oBAACE,iBAAQA;QACL,QAAQ;QACR,QAAQ;QACR,WAAWpB,GAAG;YACV;YACA;YACA;SACH;OAEAY,UAAU,WAAW,sBAI9B,oBAAC;QAAI,WAAW;qBACZ,oBAACV,MAAIA;QAAC,MAAM;QAAM,IAAI;QAAO,WAAW;OACnCG,KAAK,IAAI,iBAEd,oBAACH,MAAIA;QAAC,MAAM;QAAM,IAAI;QAAO,WAAW;OACnCG,KAAK,IAAI,EAAC,KAAE,QAAO,mBAAC,oBAACgB,SAAOA;QAAC,UAAUhB,KAAK,SAAS;;AAM9E;AAWO,MAAMiB,oBAAWlB,SAAS;IAC7B,MAAMmB,YAAYC;IAClB,MAAM,EAAEC,EAAE,EAAEC,OAAO,EAAE,GAAGH;IAExB,MAAMI,UAAUC;IAChB,MAAMC,eAAeJ,GAAG,YAAY;IAGpC,MAAMK,uBAAuBhB,YACzB,CAACiB;QACGL,QAAQ,OAAO,CAAC,YAAY,CAACK;IACjC,GACA;QAACL,QAAQ,MAAM;KAAC;IAGpB,MAAMM,eAAe,CAACC,IAAYC;QAE9B,IAAIA,UAAU,YACVR,QAAQ,SAAS,CAAC,aAAa,CAACO;QAIpC,IAAIN,SAAS;YACT,MAAMtB,OAAOoB,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAACU,CAAAA,IAAKA,EAAE,EAAE,KAAKF;YAC7C,IAAI5B,MACAsB,QAAQ,WAAW,CAACtB;YAExB;QACJ;QAEAqB,QAAQ,SAAS,CAAC,MAAM,CAACO;IAC7B;IAGA,MAAMG,wBAAwBtB,YAAY;QACtCY,QAAQ,SAAS,CAAC,WAAW;IACjC,GAAG;QAACA,QAAQ,SAAS;KAAC;IAEtB,OAAO,WAAP,GACI,oBAAC;QACG,WAAW1B,GAAG;YAAC;YAAQ;SAAc;QACrC,OAAO;YAAE,qBAAqB;QAAwC;QACtE,SAASoC;QACT,eAAa;OAEZX,GAAG,WAAW,IACXI,aAAa,GAAG,CAAC/B,CAAAA,SAAAA,WAAAA,GACb,oBAACD,YAAUA;YAAC,KAAKC,OAAO,EAAE;YAAE,QAAQA;YAAQ,YAAYgC;aAE/DL,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAACpB,CAAAA,OAAAA,WAAAA,GACd,oBAACF,mBAAQA;YACL,KAAKE,KAAK,EAAE;YACZ,MAAMA;YACN,UAAUoB,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAACpB,KAAK,EAAE;YACnD,UAAU2B;;AAK9B"}
|
|
@@ -59,6 +59,8 @@ function createMockFolderDto(overrides = {}) {
|
|
|
59
59
|
function createMockPresenter(vmOverrides = {}) {
|
|
60
60
|
const defaultVm = observable({
|
|
61
61
|
fileModel: null,
|
|
62
|
+
loading: false,
|
|
63
|
+
empty: true,
|
|
62
64
|
list: {
|
|
63
65
|
rows: [],
|
|
64
66
|
sort: null,
|
|
@@ -75,8 +77,10 @@ function createMockPresenter(vmOverrides = {}) {
|
|
|
75
77
|
selection: {
|
|
76
78
|
selectedIds: new Set(),
|
|
77
79
|
selectedCount: 0,
|
|
78
|
-
allSelected: false
|
|
80
|
+
allSelected: false,
|
|
81
|
+
label: ""
|
|
79
82
|
},
|
|
83
|
+
showingFilters: false,
|
|
80
84
|
empty: true,
|
|
81
85
|
emptyWithFilters: false,
|
|
82
86
|
error: null
|
|
@@ -114,9 +118,8 @@ function createMockPresenter(vmOverrides = {}) {
|
|
|
114
118
|
isUploading: false
|
|
115
119
|
},
|
|
116
120
|
tags: [],
|
|
117
|
-
loading: false,
|
|
118
|
-
empty: false,
|
|
119
121
|
showFolders: true,
|
|
122
|
+
childFolders: [],
|
|
120
123
|
viewMode: "grid",
|
|
121
124
|
dragging: false,
|
|
122
125
|
showingFilters: false,
|
|
@@ -141,7 +144,9 @@ function createMockPresenter(vmOverrides = {}) {
|
|
|
141
144
|
filter: {
|
|
142
145
|
set: vi.fn(),
|
|
143
146
|
clear: vi.fn(),
|
|
144
|
-
clearAll: vi.fn()
|
|
147
|
+
clearAll: vi.fn(),
|
|
148
|
+
show: vi.fn(),
|
|
149
|
+
hide: vi.fn()
|
|
145
150
|
},
|
|
146
151
|
selection: {
|
|
147
152
|
toggle: vi.fn(),
|
|
@@ -305,8 +310,10 @@ describe("FileGrid — selection action wiring", ()=>{
|
|
|
305
310
|
"file-1"
|
|
306
311
|
]),
|
|
307
312
|
selectedCount: 1,
|
|
308
|
-
allSelected: false
|
|
313
|
+
allSelected: false,
|
|
314
|
+
label: "1 item"
|
|
309
315
|
},
|
|
316
|
+
showingFilters: false,
|
|
310
317
|
empty: false,
|
|
311
318
|
emptyWithFilters: false,
|
|
312
319
|
appliedQuery: null,
|
|
@@ -343,8 +350,10 @@ describe("FileGrid — loading state", ()=>{
|
|
|
343
350
|
selection: {
|
|
344
351
|
selectedIds: new Set(),
|
|
345
352
|
selectedCount: 0,
|
|
346
|
-
allSelected: false
|
|
353
|
+
allSelected: false,
|
|
354
|
+
label: ""
|
|
347
355
|
},
|
|
356
|
+
showingFilters: false,
|
|
348
357
|
empty: true,
|
|
349
358
|
emptyWithFilters: false,
|
|
350
359
|
appliedQuery: null,
|
|
@@ -373,8 +382,10 @@ describe("FileGrid — loading state", ()=>{
|
|
|
373
382
|
selection: {
|
|
374
383
|
selectedIds: new Set(),
|
|
375
384
|
selectedCount: 0,
|
|
376
|
-
allSelected: false
|
|
385
|
+
allSelected: false,
|
|
386
|
+
label: ""
|
|
377
387
|
},
|
|
388
|
+
showingFilters: false,
|
|
378
389
|
empty: false,
|
|
379
390
|
emptyWithFilters: false,
|
|
380
391
|
appliedQuery: null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/FileList/components/Grid/FileGrid.test.js","sources":["../../../../../src/presentation/FileList/components/Grid/FileGrid.test.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect, vi } from \"vitest\";\nimport { observable } from \"mobx\";\nimport type { IFileManagerPresenter, IFileManagerViewModel } from \"../../abstractions.js\";\nimport type { FolderDto } from \"@webiny/app-aco\";\nimport type { FmFile } from \"~/features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Mock factories.\n// ---------------------------------------------------------------------------\n\nfunction createMockFile(overrides: Partial<FmFile> = {}): FmFile {\n return {\n id: \"file-1\",\n name: \"photo.jpg\",\n key: \"files/photo.jpg\",\n src: \"https://cdn.example.com/files/photo.jpg\",\n type: \"image/jpeg\",\n size: 1024,\n metadata: {},\n tags: [],\n createdOn: \"2025-01-01T00:00:00Z\",\n savedOn: \"2025-01-02T00:00:00Z\",\n createdBy: { id: \"user-1\", displayName: \"Alice\", type: \"admin\" },\n savedBy: { id: \"user-1\", displayName: \"Alice\", type: \"admin\" },\n location: { folderId: \"root\" },\n ...overrides\n };\n}\n\nconst emptyIdentity = { id: \"\", displayName: \"\", type: \"\" };\n\nfunction createMockFolderDto(overrides: Partial<FolderDto> = {}): FolderDto {\n return {\n id: \"folder-1\",\n title: \"Documents\",\n slug: \"documents\",\n type: \"\",\n parentId: null,\n path: \"\",\n permissions: [],\n hasNonInheritedPermissions: false,\n canManagePermissions: false,\n canManageStructure: true,\n canManageContent: false,\n createdBy: emptyIdentity,\n createdOn: \"\",\n savedBy: emptyIdentity,\n savedOn: \"\",\n modifiedBy: null,\n modifiedOn: null,\n extensions: {},\n ...overrides\n };\n}\n\nfunction createMockPresenter(\n vmOverrides: Partial<IFileManagerViewModel> = {}\n): IFileManagerPresenter {\n const defaultVm: IFileManagerViewModel = observable({\n fileModel: null,\n list: {\n rows: [],\n sort: null,\n filters: {},\n search: \"\",\n appliedQuery: null,\n pagination: {\n hasMore: false,\n loading: false,\n loadingMore: false,\n totalCount: 0,\n currentCount: 0\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: true,\n emptyWithFilters: false,\n error: null\n },\n folders: {\n folders: [],\n tree: [],\n currentFolderId: null,\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: true,\n currentFolderTitle: \"All Files\",\n childFolders: [],\n loadingNodeIds: []\n },\n permissions: {\n canRead: true,\n canCreate: true,\n canEdit: true,\n canDelete: true,\n canEditFile: () => true,\n canDeleteFile: () => true\n },\n upload: {\n jobs: [],\n overallProgress: { percentage: 0, bytesSent: 0, totalBytes: 0 },\n isUploading: false\n },\n tags: [],\n loading: false,\n empty: false,\n showFolders: true,\n viewMode: \"grid\" as const,\n dragging: false,\n showingFilters: false,\n isOverlay: false,\n accept: [],\n multiple: false,\n scope: undefined,\n fileDetails: null,\n ...vmOverrides\n });\n\n return {\n vm: defaultVm,\n actions: {\n search: { set: vi.fn(), clear: vi.fn() },\n sort: { set: vi.fn(), toggle: vi.fn() },\n filter: { set: vi.fn(), clear: vi.fn(), clearAll: vi.fn() },\n selection: {\n toggle: vi.fn(),\n selectRangeTo: vi.fn(),\n selectAll: vi.fn(),\n deselectAll: vi.fn(),\n selectRows: vi.fn(),\n isSelected: vi.fn().mockReturnValue(false)\n },\n loadMore: vi.fn().mockResolvedValue(undefined),\n refresh: vi.fn().mockResolvedValue(undefined),\n upload: vi.fn().mockResolvedValue(undefined),\n setViewMode: vi.fn(),\n showFileDetails: vi.fn(),\n hideFileDetails: vi.fn(),\n setDragging: vi.fn(),\n showFilters: vi.fn(),\n hideFilters: vi.fn(),\n folders: {\n selectFolder: vi.fn(),\n createFolder: vi.fn(),\n editFolder: vi.fn(),\n deleteFolder: vi.fn().mockResolvedValue(undefined),\n moveFolder: vi.fn().mockResolvedValue(undefined),\n loadChildFolders: vi.fn().mockResolvedValue(undefined),\n canManageStructure: vi.fn().mockReturnValue(true),\n getAncestorIds: vi.fn().mockReturnValue([]),\n submitOperation: vi.fn().mockResolvedValue(true),\n cancelOperation: vi.fn()\n }\n },\n init: vi.fn(),\n dispose: vi.fn()\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tests — Grid data composition logic.\n// ---------------------------------------------------------------------------\n\ndescribe(\"FileGrid — child folder resolution\", () => {\n it(\"should use vm.folders.childFolders when no current folder is selected\", () => {\n const childFolders = [\n createMockFolderDto({ id: \"f1\", title: \"Docs\" }),\n createMockFolderDto({ id: \"f2\", title: \"Photos\" })\n ];\n const presenter = createMockPresenter({\n folders: observable({\n folders: childFolders,\n tree: [],\n currentFolderId: null,\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: true,\n currentFolderTitle: \"All Files\",\n childFolders,\n loadingNodeIds: []\n })\n });\n\n expect(presenter.vm.folders.childFolders).toHaveLength(2);\n expect(presenter.vm.folders.childFolders[0].id).toBe(\"f1\");\n expect(presenter.vm.folders.childFolders[1].id).toBe(\"f2\");\n });\n\n it(\"should expose child folders via vm.folders.childFolders when a folder is selected\", () => {\n const childA = createMockFolderDto({ id: \"child-a\", title: \"Sub A\" });\n const childB = createMockFolderDto({ id: \"child-b\", title: \"Sub B\" });\n\n const presenter = createMockPresenter({\n folders: observable({\n folders: [],\n tree: [],\n currentFolderId: \"parent\",\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: false,\n currentFolderTitle: \"Parent\",\n childFolders: [childA, childB],\n loadingNodeIds: []\n })\n });\n\n expect(presenter.vm.folders.childFolders).toHaveLength(2);\n expect(presenter.vm.folders.childFolders[0].id).toBe(\"child-a\");\n expect(presenter.vm.folders.childFolders[1].id).toBe(\"child-b\");\n });\n\n it(\"should return empty childFolders when current folder has no children\", () => {\n const presenter = createMockPresenter({\n folders: observable({\n folders: [],\n tree: [],\n currentFolderId: \"leaf\",\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: false,\n currentFolderTitle: \"Leaf\",\n childFolders: [],\n loadingNodeIds: []\n })\n });\n\n expect(presenter.vm.folders.childFolders).toHaveLength(0);\n });\n});\n\ndescribe(\"FileGrid — selection action wiring\", () => {\n it(\"should call actions.selection.toggle with file id on file click\", () => {\n const presenter = createMockPresenter();\n\n // Simulate what the component does when a file card is clicked.\n const fileId = \"file-1\";\n presenter.actions.selection.toggle(fileId);\n\n expect(presenter.actions.selection.toggle).toHaveBeenCalledWith(\"file-1\");\n });\n\n it(\"should call actions.selection.deselectAll on background click\", () => {\n const presenter = createMockPresenter();\n\n // Simulate background click handler.\n presenter.actions.selection.deselectAll();\n\n expect(presenter.actions.selection.deselectAll).toHaveBeenCalled();\n });\n\n it(\"should correctly determine selected state from vm.list.selection.selectedIds\", () => {\n const file1 = createMockFile({ id: \"file-1\" });\n const file2 = createMockFile({ id: \"file-2\", name: \"doc.pdf\" });\n\n const presenter = createMockPresenter({\n list: observable({\n rows: [file1, file2],\n sort: null,\n filters: {},\n search: \"\",\n pagination: {\n hasMore: false,\n loading: false,\n loadingMore: false,\n totalCount: 2,\n currentCount: 2\n },\n selection: {\n selectedIds: new Set<string>([\"file-1\"]),\n selectedCount: 1,\n allSelected: false\n },\n empty: false,\n emptyWithFilters: false,\n appliedQuery: null,\n error: null\n })\n });\n\n expect(presenter.vm.list.selection.selectedIds.has(\"file-1\")).toBe(true);\n expect(presenter.vm.list.selection.selectedIds.has(\"file-2\")).toBe(false);\n });\n});\n\ndescribe(\"FileGrid — folder navigation wiring\", () => {\n it(\"should call actions.filter.set with folderId on folder card click\", () => {\n const presenter = createMockPresenter();\n\n // Simulate what the component does when a folder card is clicked.\n const folderId = \"folder-1\";\n presenter.actions.filter.set(\"folderId\", folderId);\n\n expect(presenter.actions.filter.set).toHaveBeenCalledWith(\"folderId\", \"folder-1\");\n });\n});\n\ndescribe(\"FileGrid — loading state\", () => {\n it(\"should show loading state when loading with no rows\", () => {\n const presenter = createMockPresenter({\n list: observable({\n rows: [],\n sort: null,\n filters: {},\n search: \"\",\n pagination: {\n hasMore: false,\n loading: true,\n loadingMore: false,\n totalCount: 0,\n currentCount: 0\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: true,\n emptyWithFilters: false,\n appliedQuery: null,\n error: null\n })\n });\n\n // Replicate the component's loading check.\n const showLoader =\n presenter.vm.list.pagination.loading && presenter.vm.list.rows.length === 0;\n\n expect(showLoader).toBe(true);\n });\n\n it(\"should not show loading state when loading with existing rows\", () => {\n const presenter = createMockPresenter({\n list: observable({\n rows: [createMockFile()],\n sort: null,\n filters: {},\n search: \"\",\n pagination: {\n hasMore: false,\n loading: true,\n loadingMore: false,\n totalCount: 1,\n currentCount: 1\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false\n },\n empty: false,\n emptyWithFilters: false,\n appliedQuery: null,\n error: null\n })\n });\n\n const showLoader =\n presenter.vm.list.pagination.loading && presenter.vm.list.rows.length === 0;\n\n expect(showLoader).toBe(false);\n });\n});\n"],"names":["createMockFile","overrides","emptyIdentity","createMockFolderDto","createMockPresenter","vmOverrides","defaultVm","observable","Set","undefined","vi","describe","it","childFolders","presenter","expect","childA","childB","fileId","file1","file2","folderId","showLoader"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"presentation/FileList/components/Grid/FileGrid.test.js","sources":["../../../../../src/presentation/FileList/components/Grid/FileGrid.test.ts"],"sourcesContent":["// @vitest-environment jsdom\nimport { describe, it, expect, vi } from \"vitest\";\nimport { observable } from \"mobx\";\nimport type { IFileManagerPresenter, IFileManagerViewModel } from \"../../abstractions.js\";\nimport type { FolderDto } from \"@webiny/app-aco\";\nimport type { FmFile } from \"~/features/shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Mock factories.\n// ---------------------------------------------------------------------------\n\nfunction createMockFile(overrides: Partial<FmFile> = {}): FmFile {\n return {\n id: \"file-1\",\n name: \"photo.jpg\",\n key: \"files/photo.jpg\",\n src: \"https://cdn.example.com/files/photo.jpg\",\n type: \"image/jpeg\",\n size: 1024,\n metadata: {},\n tags: [],\n createdOn: \"2025-01-01T00:00:00Z\",\n savedOn: \"2025-01-02T00:00:00Z\",\n createdBy: { id: \"user-1\", displayName: \"Alice\", type: \"admin\" },\n savedBy: { id: \"user-1\", displayName: \"Alice\", type: \"admin\" },\n location: { folderId: \"root\" },\n ...overrides\n };\n}\n\nconst emptyIdentity = { id: \"\", displayName: \"\", type: \"\" };\n\nfunction createMockFolderDto(overrides: Partial<FolderDto> = {}): FolderDto {\n return {\n id: \"folder-1\",\n title: \"Documents\",\n slug: \"documents\",\n type: \"\",\n parentId: null,\n path: \"\",\n permissions: [],\n hasNonInheritedPermissions: false,\n canManagePermissions: false,\n canManageStructure: true,\n canManageContent: false,\n createdBy: emptyIdentity,\n createdOn: \"\",\n savedBy: emptyIdentity,\n savedOn: \"\",\n modifiedBy: null,\n modifiedOn: null,\n extensions: {},\n ...overrides\n };\n}\n\nfunction createMockPresenter(\n vmOverrides: Partial<IFileManagerViewModel> = {}\n): IFileManagerPresenter {\n const defaultVm: IFileManagerViewModel = observable({\n fileModel: null,\n loading: false,\n empty: true,\n list: {\n rows: [],\n sort: null,\n filters: {},\n search: \"\",\n appliedQuery: null,\n pagination: {\n hasMore: false,\n loading: false,\n loadingMore: false,\n totalCount: 0,\n currentCount: 0\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false,\n label: \"\"\n },\n showingFilters: false,\n empty: true,\n emptyWithFilters: false,\n error: null\n },\n folders: {\n folders: [],\n tree: [],\n currentFolderId: null,\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: true,\n currentFolderTitle: \"All Files\",\n childFolders: [],\n loadingNodeIds: []\n },\n permissions: {\n canRead: true,\n canCreate: true,\n canEdit: true,\n canDelete: true,\n canEditFile: () => true,\n canDeleteFile: () => true\n },\n upload: {\n jobs: [],\n overallProgress: { percentage: 0, bytesSent: 0, totalBytes: 0 },\n isUploading: false\n },\n tags: [],\n showFolders: true,\n childFolders: [],\n viewMode: \"grid\" as const,\n dragging: false,\n showingFilters: false,\n isOverlay: false,\n accept: [],\n multiple: false,\n scope: undefined,\n fileDetails: null,\n ...vmOverrides\n });\n\n return {\n vm: defaultVm,\n actions: {\n search: { set: vi.fn(), clear: vi.fn() },\n sort: { set: vi.fn(), toggle: vi.fn() },\n filter: {\n set: vi.fn(),\n clear: vi.fn(),\n clearAll: vi.fn(),\n show: vi.fn(),\n hide: vi.fn()\n },\n selection: {\n toggle: vi.fn(),\n selectRangeTo: vi.fn(),\n selectAll: vi.fn(),\n deselectAll: vi.fn(),\n selectRows: vi.fn(),\n isSelected: vi.fn().mockReturnValue(false)\n },\n loadMore: vi.fn().mockResolvedValue(undefined),\n refresh: vi.fn().mockResolvedValue(undefined),\n upload: vi.fn().mockResolvedValue(undefined),\n setViewMode: vi.fn(),\n showFileDetails: vi.fn(),\n hideFileDetails: vi.fn(),\n setDragging: vi.fn(),\n showFilters: vi.fn(),\n hideFilters: vi.fn(),\n folders: {\n selectFolder: vi.fn(),\n createFolder: vi.fn(),\n editFolder: vi.fn(),\n deleteFolder: vi.fn().mockResolvedValue(undefined),\n moveFolder: vi.fn().mockResolvedValue(undefined),\n loadChildFolders: vi.fn().mockResolvedValue(undefined),\n canManageStructure: vi.fn().mockReturnValue(true),\n getAncestorIds: vi.fn().mockReturnValue([]),\n submitOperation: vi.fn().mockResolvedValue(true),\n cancelOperation: vi.fn()\n }\n },\n init: vi.fn(),\n dispose: vi.fn()\n };\n}\n\n// ---------------------------------------------------------------------------\n// Tests — Grid data composition logic.\n// ---------------------------------------------------------------------------\n\ndescribe(\"FileGrid — child folder resolution\", () => {\n it(\"should use vm.folders.childFolders when no current folder is selected\", () => {\n const childFolders = [\n createMockFolderDto({ id: \"f1\", title: \"Docs\" }),\n createMockFolderDto({ id: \"f2\", title: \"Photos\" })\n ];\n const presenter = createMockPresenter({\n folders: observable({\n folders: childFolders,\n tree: [],\n currentFolderId: null,\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: true,\n currentFolderTitle: \"All Files\",\n childFolders,\n loadingNodeIds: []\n })\n });\n\n expect(presenter.vm.folders.childFolders).toHaveLength(2);\n expect(presenter.vm.folders.childFolders[0].id).toBe(\"f1\");\n expect(presenter.vm.folders.childFolders[1].id).toBe(\"f2\");\n });\n\n it(\"should expose child folders via vm.folders.childFolders when a folder is selected\", () => {\n const childA = createMockFolderDto({ id: \"child-a\", title: \"Sub A\" });\n const childB = createMockFolderDto({ id: \"child-b\", title: \"Sub B\" });\n\n const presenter = createMockPresenter({\n folders: observable({\n folders: [],\n tree: [],\n currentFolderId: \"parent\",\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: false,\n currentFolderTitle: \"Parent\",\n childFolders: [childA, childB],\n loadingNodeIds: []\n })\n });\n\n expect(presenter.vm.folders.childFolders).toHaveLength(2);\n expect(presenter.vm.folders.childFolders[0].id).toBe(\"child-a\");\n expect(presenter.vm.folders.childFolders[1].id).toBe(\"child-b\");\n });\n\n it(\"should return empty childFolders when current folder has no children\", () => {\n const presenter = createMockPresenter({\n folders: observable({\n folders: [],\n tree: [],\n currentFolderId: \"leaf\",\n currentFolder: null,\n loading: false,\n operation: { active: false, mode: null },\n isRootFolder: false,\n currentFolderTitle: \"Leaf\",\n childFolders: [],\n loadingNodeIds: []\n })\n });\n\n expect(presenter.vm.folders.childFolders).toHaveLength(0);\n });\n});\n\ndescribe(\"FileGrid — selection action wiring\", () => {\n it(\"should call actions.selection.toggle with file id on file click\", () => {\n const presenter = createMockPresenter();\n\n // Simulate what the component does when a file card is clicked.\n const fileId = \"file-1\";\n presenter.actions.selection.toggle(fileId);\n\n expect(presenter.actions.selection.toggle).toHaveBeenCalledWith(\"file-1\");\n });\n\n it(\"should call actions.selection.deselectAll on background click\", () => {\n const presenter = createMockPresenter();\n\n // Simulate background click handler.\n presenter.actions.selection.deselectAll();\n\n expect(presenter.actions.selection.deselectAll).toHaveBeenCalled();\n });\n\n it(\"should correctly determine selected state from vm.list.selection.selectedIds\", () => {\n const file1 = createMockFile({ id: \"file-1\" });\n const file2 = createMockFile({ id: \"file-2\", name: \"doc.pdf\" });\n\n const presenter = createMockPresenter({\n list: observable({\n rows: [file1, file2],\n sort: null,\n filters: {},\n search: \"\",\n pagination: {\n hasMore: false,\n loading: false,\n loadingMore: false,\n totalCount: 2,\n currentCount: 2\n },\n selection: {\n selectedIds: new Set<string>([\"file-1\"]),\n selectedCount: 1,\n allSelected: false,\n label: \"1 item\"\n },\n showingFilters: false,\n empty: false,\n emptyWithFilters: false,\n appliedQuery: null,\n error: null\n })\n });\n\n expect(presenter.vm.list.selection.selectedIds.has(\"file-1\")).toBe(true);\n expect(presenter.vm.list.selection.selectedIds.has(\"file-2\")).toBe(false);\n });\n});\n\ndescribe(\"FileGrid — folder navigation wiring\", () => {\n it(\"should call actions.filter.set with folderId on folder card click\", () => {\n const presenter = createMockPresenter();\n\n // Simulate what the component does when a folder card is clicked.\n const folderId = \"folder-1\";\n presenter.actions.filter.set(\"folderId\", folderId);\n\n expect(presenter.actions.filter.set).toHaveBeenCalledWith(\"folderId\", \"folder-1\");\n });\n});\n\ndescribe(\"FileGrid — loading state\", () => {\n it(\"should show loading state when loading with no rows\", () => {\n const presenter = createMockPresenter({\n list: observable({\n rows: [],\n sort: null,\n filters: {},\n search: \"\",\n pagination: {\n hasMore: false,\n loading: true,\n loadingMore: false,\n totalCount: 0,\n currentCount: 0\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false,\n label: \"\"\n },\n showingFilters: false,\n empty: true,\n emptyWithFilters: false,\n appliedQuery: null,\n error: null\n })\n });\n\n // Replicate the component's loading check.\n const showLoader =\n presenter.vm.list.pagination.loading && presenter.vm.list.rows.length === 0;\n\n expect(showLoader).toBe(true);\n });\n\n it(\"should not show loading state when loading with existing rows\", () => {\n const presenter = createMockPresenter({\n list: observable({\n rows: [createMockFile()],\n sort: null,\n filters: {},\n search: \"\",\n pagination: {\n hasMore: false,\n loading: true,\n loadingMore: false,\n totalCount: 1,\n currentCount: 1\n },\n selection: {\n selectedIds: new Set<string>(),\n selectedCount: 0,\n allSelected: false,\n label: \"\"\n },\n showingFilters: false,\n empty: false,\n emptyWithFilters: false,\n appliedQuery: null,\n error: null\n })\n });\n\n const showLoader =\n presenter.vm.list.pagination.loading && presenter.vm.list.rows.length === 0;\n\n expect(showLoader).toBe(false);\n });\n});\n"],"names":["createMockFile","overrides","emptyIdentity","createMockFolderDto","createMockPresenter","vmOverrides","defaultVm","observable","Set","undefined","vi","describe","it","childFolders","presenter","expect","childA","childB","fileId","file1","file2","folderId","showLoader"],"mappings":"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"}
|
|
@@ -28,7 +28,7 @@ const FileTable_FileTable = observer(function() {
|
|
|
28
28
|
const overlay = useOverlay();
|
|
29
29
|
const fileRows = createRecordsData(vm.list.rows);
|
|
30
30
|
const data = vm.showFolders ? [
|
|
31
|
-
...toFolderTableRows(vm.
|
|
31
|
+
...toFolderTableRows(vm.childFolders),
|
|
32
32
|
...fileRows
|
|
33
33
|
] : fileRows;
|
|
34
34
|
const selected = createRecordsData(vm.list.rows.filter((file)=>vm.list.selection.selectedIds.has(file.id)));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/FileList/components/Table/FileTable.js","sources":["../../../../../src/presentation/FileList/components/Table/FileTable.tsx"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport type { DataTableSorting, OnDataTableSortingChange } from \"@webiny/admin-ui\";\nimport { useShiftKey } from \"@webiny/app-admin\";\nimport { useOverlay } from \"~/presentation/FileManager/OverlayContext.js\";\nimport { createRecordsData, Table as AcoTable } from \"@webiny/app-aco\";\nimport type { FolderTableRow, RecordTableRow } from \"@webiny/app-aco\";\nimport { useFileManagerPresenter } from \"../../FileManagerPresenterProvider.js\";\nimport { useFileManagerConfig } from \"~/presentation/config/FileManagerViewConfig.js\";\nimport type { FmFile } from \"~/features/shared/types.js\";\nimport type { FolderDto } from \"@webiny/app-aco\";\n\nexport type FileTableItem = FolderTableRow | RecordTableRow<FmFile>;\n\nconst toFolderTableRows = (folders: FolderDto[]): FolderTableRow[] => {\n return folders.map(folder => ({\n id: folder.id,\n $type: \"FOLDER\" as const,\n $selectable: false,\n data: folder\n }));\n};\n\n/**\n * Convert presenter sort state to DataTable sorting format.\n */\nconst toDataTableSorting = (\n sort: { field: string; direction: \"ASC\" | \"DESC\" } | null\n): DataTableSorting => {\n if (!sort) {\n return [];\n }\n return [{ id: sort.field, desc: sort.direction === \"DESC\" }];\n};\n\n/**\n * Table View component driven by FileListPresenter vm.\n * Bridges the presenter's state to the ACO Table component,\n * wiring sort and selection actions back to the presenter.\n */\nexport const FileTable = observer(function FileTable() {\n const presenter = useFileManagerPresenter();\n const { browser } = useFileManagerConfig();\n const { vm, actions } = presenter;\n\n const { isPressed: isShiftPressed } = useShiftKey();\n const overlay = useOverlay();\n\n // No useMemo — MobX observer needs to track these observable accesses during render.\n const fileRows = createRecordsData(vm.list.rows);\n const data: FileTableItem[] = vm.showFolders\n ? [...toFolderTableRows(vm.
|
|
1
|
+
{"version":3,"file":"presentation/FileList/components/Table/FileTable.js","sources":["../../../../../src/presentation/FileList/components/Table/FileTable.tsx"],"sourcesContent":["import React, { useCallback, useMemo } from \"react\";\nimport { observer } from \"mobx-react-lite\";\nimport type { DataTableSorting, OnDataTableSortingChange } from \"@webiny/admin-ui\";\nimport { useShiftKey } from \"@webiny/app-admin\";\nimport { useOverlay } from \"~/presentation/FileManager/OverlayContext.js\";\nimport { createRecordsData, Table as AcoTable } from \"@webiny/app-aco\";\nimport type { FolderTableRow, RecordTableRow } from \"@webiny/app-aco\";\nimport { useFileManagerPresenter } from \"../../FileManagerPresenterProvider.js\";\nimport { useFileManagerConfig } from \"~/presentation/config/FileManagerViewConfig.js\";\nimport type { FmFile } from \"~/features/shared/types.js\";\nimport type { FolderDto } from \"@webiny/app-aco\";\n\nexport type FileTableItem = FolderTableRow | RecordTableRow<FmFile>;\n\nconst toFolderTableRows = (folders: FolderDto[]): FolderTableRow[] => {\n return folders.map(folder => ({\n id: folder.id,\n $type: \"FOLDER\" as const,\n $selectable: false,\n data: folder\n }));\n};\n\n/**\n * Convert presenter sort state to DataTable sorting format.\n */\nconst toDataTableSorting = (\n sort: { field: string; direction: \"ASC\" | \"DESC\" } | null\n): DataTableSorting => {\n if (!sort) {\n return [];\n }\n return [{ id: sort.field, desc: sort.direction === \"DESC\" }];\n};\n\n/**\n * Table View component driven by FileListPresenter vm.\n * Bridges the presenter's state to the ACO Table component,\n * wiring sort and selection actions back to the presenter.\n */\nexport const FileTable = observer(function FileTable() {\n const presenter = useFileManagerPresenter();\n const { browser } = useFileManagerConfig();\n const { vm, actions } = presenter;\n\n const { isPressed: isShiftPressed } = useShiftKey();\n const overlay = useOverlay();\n\n // No useMemo — MobX observer needs to track these observable accesses during render.\n const fileRows = createRecordsData(vm.list.rows);\n const data: FileTableItem[] = vm.showFolders\n ? [...toFolderTableRows(vm.childFolders), ...fileRows]\n : fileRows;\n\n const selected: FileTableItem[] = createRecordsData(\n vm.list.rows.filter(file => vm.list.selection.selectedIds.has(file.id))\n );\n\n // Convert presenter sort state to DataTable format.\n const sorting = useMemo(() => toDataTableSorting(vm.list.sort), [vm.list.sort]);\n\n // Handle sorting changes from the DataTable.\n const onSortingChange: OnDataTableSortingChange = useCallback(\n updater => {\n const newSorting = typeof updater === \"function\" ? updater(sorting) : updater;\n if (newSorting.length > 0) {\n const { id, desc } = newSorting[0];\n actions.sort.set(id, desc ? \"DESC\" : \"ASC\");\n }\n },\n [sorting, actions.sort]\n );\n\n const onToggleRow = (row: FileTableItem) => {\n if (row.$type !== \"RECORD\") {\n return;\n }\n\n if (isShiftPressed()) {\n actions.selection.selectRangeTo(row.id);\n return;\n }\n\n if (overlay) {\n const file = vm.list.rows.find(f => f.id === row.id);\n if (file) {\n overlay.onFileClick(file);\n }\n return;\n }\n\n actions.selection.toggle(row.id);\n };\n\n // Handle select all / bulk selection.\n const onSelectRow = useCallback(\n (rows: FileTableItem[] | []) => {\n if (rows.length === 0) {\n actions.selection.deselectAll();\n return;\n }\n const ids = rows\n .filter((row): row is RecordTableRow<FmFile> => row.$type === \"RECORD\")\n .map(row => row.id);\n actions.selection.selectRows(ids);\n },\n [actions.selection]\n );\n\n return (\n <AcoTable<FileTableItem>\n columns={browser.table.columns}\n data={data}\n loading={vm.list.pagination.loading}\n onSelectRow={onSelectRow}\n onToggleRow={onToggleRow}\n sorting={sorting}\n onSortingChange={onSortingChange}\n selected={selected}\n namespace={\"fm/file/list\"}\n />\n );\n});\n"],"names":["toFolderTableRows","folders","folder","toDataTableSorting","sort","FileTable","observer","presenter","useFileManagerPresenter","browser","useFileManagerConfig","vm","actions","isShiftPressed","useShiftKey","overlay","useOverlay","fileRows","createRecordsData","data","selected","file","sorting","useMemo","onSortingChange","useCallback","updater","newSorting","id","desc","onToggleRow","row","f","onSelectRow","rows","ids","AcoTable"],"mappings":";;;;;;;AAcA,MAAMA,oBAAoB,CAACC,UAChBA,QAAQ,GAAG,CAACC,CAAAA,SAAW;YAC1B,IAAIA,OAAO,EAAE;YACb,OAAO;YACP,aAAa;YACb,MAAMA;QACV;AAMJ,MAAMC,qBAAqB,CACvBC;IAEA,IAAI,CAACA,MACD,OAAO,EAAE;IAEb,OAAO;QAAC;YAAE,IAAIA,KAAK,KAAK;YAAE,MAAMA,AAAmB,WAAnBA,KAAK,SAAS;QAAY;KAAE;AAChE;AAOO,MAAMC,sBAAYC,SAAS;IAC9B,MAAMC,YAAYC;IAClB,MAAM,EAAEC,OAAO,EAAE,GAAGC;IACpB,MAAM,EAAEC,EAAE,EAAEC,OAAO,EAAE,GAAGL;IAExB,MAAM,EAAE,WAAWM,cAAc,EAAE,GAAGC;IACtC,MAAMC,UAAUC;IAGhB,MAAMC,WAAWC,kBAAkBP,GAAG,IAAI,CAAC,IAAI;IAC/C,MAAMQ,OAAwBR,GAAG,WAAW,GACtC;WAAIX,kBAAkBW,GAAG,YAAY;WAAMM;KAAS,GACpDA;IAEN,MAAMG,WAA4BF,kBAC9BP,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAACU,CAAAA,OAAQV,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,GAAG,CAACU,KAAK,EAAE;IAIzE,MAAMC,UAAUC,QAAQ,IAAMpB,mBAAmBQ,GAAG,IAAI,CAAC,IAAI,GAAG;QAACA,GAAG,IAAI,CAAC,IAAI;KAAC;IAG9E,MAAMa,kBAA4CC,YAC9CC,CAAAA;QACI,MAAMC,aAAa,AAAmB,cAAnB,OAAOD,UAAyBA,QAAQJ,WAAWI;QACtE,IAAIC,WAAW,MAAM,GAAG,GAAG;YACvB,MAAM,EAAEC,EAAE,EAAEC,IAAI,EAAE,GAAGF,UAAU,CAAC,EAAE;YAClCf,QAAQ,IAAI,CAAC,GAAG,CAACgB,IAAIC,OAAO,SAAS;QACzC;IACJ,GACA;QAACP;QAASV,QAAQ,IAAI;KAAC;IAG3B,MAAMkB,cAAc,CAACC;QACjB,IAAIA,AAAc,aAAdA,IAAI,KAAK,EACT;QAGJ,IAAIlB,kBAAkB,YAClBD,QAAQ,SAAS,CAAC,aAAa,CAACmB,IAAI,EAAE;QAI1C,IAAIhB,SAAS;YACT,MAAMM,OAAOV,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAACqB,CAAAA,IAAKA,EAAE,EAAE,KAAKD,IAAI,EAAE;YACnD,IAAIV,MACAN,QAAQ,WAAW,CAACM;YAExB;QACJ;QAEAT,QAAQ,SAAS,CAAC,MAAM,CAACmB,IAAI,EAAE;IACnC;IAGA,MAAME,cAAcR,YAChB,CAACS;QACG,IAAIA,AAAgB,MAAhBA,KAAK,MAAM,EAAQ,YACnBtB,QAAQ,SAAS,CAAC,WAAW;QAGjC,MAAMuB,MAAMD,KACP,MAAM,CAAC,CAACH,MAAuCA,AAAc,aAAdA,IAAI,KAAK,EACxD,GAAG,CAACA,CAAAA,MAAOA,IAAI,EAAE;QACtBnB,QAAQ,SAAS,CAAC,UAAU,CAACuB;IACjC,GACA;QAACvB,QAAQ,SAAS;KAAC;IAGvB,OAAO,WAAP,GACI,oBAACwB,OAAQA;QACL,SAAS3B,QAAQ,KAAK,CAAC,OAAO;QAC9B,MAAMU;QACN,SAASR,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO;QACnC,aAAasB;QACb,aAAaH;QACb,SAASR;QACT,iBAAiBE;QACjB,UAAUJ;QACV,WAAW;;AAGvB"}
|
|
@@ -86,6 +86,8 @@ function createMockFolderNode(overrides = {}) {
|
|
|
86
86
|
function createMockPresenter(vmOverrides = {}) {
|
|
87
87
|
const defaultVm = observable({
|
|
88
88
|
fileModel: null,
|
|
89
|
+
loading: false,
|
|
90
|
+
empty: true,
|
|
89
91
|
list: {
|
|
90
92
|
rows: [],
|
|
91
93
|
sort: null,
|
|
@@ -102,8 +104,10 @@ function createMockPresenter(vmOverrides = {}) {
|
|
|
102
104
|
selection: {
|
|
103
105
|
selectedIds: new Set(),
|
|
104
106
|
selectedCount: 0,
|
|
105
|
-
allSelected: false
|
|
107
|
+
allSelected: false,
|
|
108
|
+
label: ""
|
|
106
109
|
},
|
|
110
|
+
showingFilters: false,
|
|
107
111
|
empty: true,
|
|
108
112
|
emptyWithFilters: false,
|
|
109
113
|
error: null
|
|
@@ -141,9 +145,8 @@ function createMockPresenter(vmOverrides = {}) {
|
|
|
141
145
|
isUploading: false
|
|
142
146
|
},
|
|
143
147
|
tags: [],
|
|
144
|
-
loading: false,
|
|
145
|
-
empty: false,
|
|
146
148
|
showFolders: true,
|
|
149
|
+
childFolders: [],
|
|
147
150
|
viewMode: "table",
|
|
148
151
|
dragging: false,
|
|
149
152
|
showingFilters: false,
|
|
@@ -168,7 +171,9 @@ function createMockPresenter(vmOverrides = {}) {
|
|
|
168
171
|
filter: {
|
|
169
172
|
set: vi.fn(),
|
|
170
173
|
clear: vi.fn(),
|
|
171
|
-
clearAll: vi.fn()
|
|
174
|
+
clearAll: vi.fn(),
|
|
175
|
+
show: vi.fn(),
|
|
176
|
+
hide: vi.fn()
|
|
172
177
|
},
|
|
173
178
|
selection: {
|
|
174
179
|
toggle: vi.fn(),
|