@webiny/app-file-manager 6.4.5-beta.0 → 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.
Files changed (135) hide show
  1. package/app.js +13 -2
  2. package/app.js.map +1 -1
  3. package/domain/tagsHelpers.d.ts +2 -2
  4. package/domain/types.d.ts +0 -8
  5. package/features/permissions/abstractions.d.ts +2 -2
  6. package/features/permissions/feature.d.ts +3 -3
  7. package/modules/AiEnrichment/AiImageEnrichmentEventHandler.d.ts +18 -0
  8. package/modules/AiEnrichment/AiImageEnrichmentEventHandler.js +33 -0
  9. package/modules/AiEnrichment/AiImageEnrichmentEventHandler.js.map +1 -0
  10. package/modules/AiEnrichment/feature.d.ts +1 -0
  11. package/modules/AiEnrichment/feature.js +11 -0
  12. package/modules/AiEnrichment/feature.js.map +1 -0
  13. package/modules/AiEnrichment/index.d.ts +2 -0
  14. package/modules/AiEnrichment/index.js +9 -0
  15. package/modules/AiEnrichment/index.js.map +1 -0
  16. package/modules/HeadlessCms/fieldType/FileFieldSettingsModifier.d.ts +16 -0
  17. package/modules/HeadlessCms/fieldType/FileFieldSettingsModifier.js +33 -0
  18. package/modules/HeadlessCms/fieldType/FileFieldSettingsModifier.js.map +1 -0
  19. package/modules/HeadlessCms/fieldType/FileFieldType.d.ts +24 -0
  20. package/modules/HeadlessCms/fieldType/FileFieldType.js +37 -0
  21. package/modules/HeadlessCms/fieldType/FileFieldType.js.map +1 -0
  22. package/modules/HeadlessCms/fieldType/FileInputRenderer.d.ts +15 -0
  23. package/modules/HeadlessCms/fieldType/FileInputRenderer.js +19 -0
  24. package/modules/HeadlessCms/fieldType/FileInputRenderer.js.map +1 -0
  25. package/modules/HeadlessCms/fieldType/FileInputsRenderer.d.ts +15 -0
  26. package/modules/HeadlessCms/fieldType/FileInputsRenderer.js +19 -0
  27. package/modules/HeadlessCms/fieldType/FileInputsRenderer.js.map +1 -0
  28. package/modules/HeadlessCms/fieldType/feature.d.ts +1 -0
  29. package/modules/HeadlessCms/fieldType/feature.js +17 -0
  30. package/modules/HeadlessCms/fieldType/feature.js.map +1 -0
  31. package/package.json +19 -29
  32. package/presentation/FileDetails/FileDetailsPresenter.test.js +1 -0
  33. package/presentation/FileDetails/FileDetailsPresenter.test.js.map +1 -1
  34. package/presentation/FileDetails/components/Content.d.ts +5 -4
  35. package/presentation/FileList/FileListDataSource.d.ts +8 -12
  36. package/presentation/FileList/FileListDataSource.js +15 -88
  37. package/presentation/FileList/FileListDataSource.js.map +1 -1
  38. package/presentation/FileList/FileListPresenter.test.js +7 -2
  39. package/presentation/FileList/FileListPresenter.test.js.map +1 -1
  40. package/presentation/FileList/FileManagerPresenter.d.ts +1 -0
  41. package/presentation/FileList/FileManagerPresenter.js +9 -1
  42. package/presentation/FileList/FileManagerPresenter.js.map +1 -1
  43. package/presentation/FileList/abstractions.d.ts +2 -0
  44. package/presentation/FileList/abstractions.js.map +1 -1
  45. package/presentation/FileList/components/BulkActions/index.d.ts +0 -1
  46. package/presentation/FileList/components/BulkActions/index.js +0 -1
  47. package/presentation/FileList/components/BulkActions/useBulkActionWorker.d.ts +9 -8
  48. package/presentation/FileList/components/BulkActions/useBulkActionWorker.js +3 -9
  49. package/presentation/FileList/components/BulkActions/useBulkActionWorker.js.map +1 -1
  50. package/presentation/FileList/components/Grid/FileGrid.js +1 -1
  51. package/presentation/FileList/components/Grid/FileGrid.js.map +1 -1
  52. package/presentation/FileList/components/Grid/FileGrid.test.js +18 -7
  53. package/presentation/FileList/components/Grid/FileGrid.test.js.map +1 -1
  54. package/presentation/FileList/components/Table/FileTable.js +1 -1
  55. package/presentation/FileList/components/Table/FileTable.js.map +1 -1
  56. package/presentation/FileList/components/Table/FileTable.test.js +9 -4
  57. package/presentation/FileList/components/Table/FileTable.test.js.map +1 -1
  58. package/presentation/FileManager/FileManagerView.js +8 -6
  59. package/presentation/FileManager/FileManagerView.js.map +1 -1
  60. package/presentation/FileManager/RouteParamsSync.d.ts +2 -0
  61. package/presentation/FileManager/RouteParamsSync.js +30 -0
  62. package/presentation/FileManager/RouteParamsSync.js.map +1 -0
  63. package/presentation/config/DefaultFileManagerConfig.js +1 -4
  64. package/presentation/config/DefaultFileManagerConfig.js.map +1 -1
  65. package/presentation/config/FileManagerViewConfig.d.ts +19 -25
  66. package/presentation/config/configComponents/Browser/BulkAction.d.ts +10 -8
  67. package/presentation/config/configComponents/Browser/BulkAction.js +3 -9
  68. package/presentation/config/configComponents/Browser/BulkAction.js.map +1 -1
  69. package/presentation/config/configComponents/Browser/FileAction.d.ts +2 -1
  70. package/presentation/config/configComponents/Browser/FolderAction.d.ts +2 -1
  71. package/presentation/config/configComponents/Browser/Table/Column.d.ts +9 -8
  72. package/presentation/config/configComponents/Browser/Table/index.d.ts +9 -9
  73. package/presentation/config/configComponents/Browser/index.d.ts +20 -20
  74. package/presentation/config/configComponents/Browser/index.js +0 -8
  75. package/presentation/config/configComponents/Browser/index.js.map +1 -1
  76. package/presentation/config/configComponents/FileDetails/index.d.ts +4 -2
  77. package/presentation/fieldRenderers/CmsFilePickerRenderer.d.ts +13 -0
  78. package/presentation/fieldRenderers/CmsFilePickerRenderer.js +29 -0
  79. package/presentation/fieldRenderers/CmsFilePickerRenderer.js.map +1 -0
  80. package/presentation/fieldRenderers/CmsMultiFilePickerRenderer.d.ts +13 -0
  81. package/presentation/fieldRenderers/CmsMultiFilePickerRenderer.js +51 -0
  82. package/presentation/fieldRenderers/CmsMultiFilePickerRenderer.js.map +1 -0
  83. package/routes.d.ts +1 -0
  84. package/routes.js +2 -1
  85. package/routes.js.map +1 -1
  86. package/modules/HeadlessCms/fileField.d.ts +0 -2
  87. package/modules/HeadlessCms/fileField.js +0 -44
  88. package/modules/HeadlessCms/fileField.js.map +0 -1
  89. package/modules/HeadlessCms/fileRenderer/fileField.d.ts +0 -2
  90. package/modules/HeadlessCms/fileRenderer/fileField.js +0 -53
  91. package/modules/HeadlessCms/fileRenderer/fileField.js.map +0 -1
  92. package/modules/HeadlessCms/fileRenderer/fileFields.d.ts +0 -2
  93. package/modules/HeadlessCms/fileRenderer/fileFields.js +0 -73
  94. package/modules/HeadlessCms/fileRenderer/fileFields.js.map +0 -1
  95. package/modules/HeadlessCms/fileRenderer/utils.d.ts +0 -2
  96. package/modules/HeadlessCms/fileRenderer/utils.js +0 -15
  97. package/modules/HeadlessCms/fileRenderer/utils.js.map +0 -1
  98. package/modules/HeadlessCms/index.d.ts +0 -1
  99. package/modules/HeadlessCms/index.js +0 -14
  100. package/modules/HeadlessCms/index.js.map +0 -1
  101. package/modules/Settings/graphql.d.ts +0 -12
  102. package/modules/Settings/graphql.js +0 -36
  103. package/modules/Settings/graphql.js.map +0 -1
  104. package/modules/Settings/views/FileManagerSettings.d.ts +0 -2
  105. package/modules/Settings/views/FileManagerSettings.js +0 -134
  106. package/modules/Settings/views/FileManagerSettings.js.map +0 -1
  107. package/presentation/FileList/components/BulkActions/BatchEditorDialog/AddOperation.d.ts +0 -7
  108. package/presentation/FileList/components/BulkActions/BatchEditorDialog/AddOperation.js +0 -15
  109. package/presentation/FileList/components/BulkActions/BatchEditorDialog/AddOperation.js.map +0 -1
  110. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditor.d.ts +0 -15
  111. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditor.js +0 -44
  112. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditor.js.map +0 -1
  113. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialog.d.ts +0 -15
  114. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialog.js +0 -52
  115. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialog.js.map +0 -1
  116. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.d.ts +0 -61
  117. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.js +0 -111
  118. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.js.map +0 -1
  119. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.test.d.ts +0 -1
  120. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.test.js +0 -372
  121. package/presentation/FileList/components/BulkActions/BatchEditorDialog/BatchEditorDialogPresenter.test.js.map +0 -1
  122. package/presentation/FileList/components/BulkActions/BatchEditorDialog/FieldRenderer.d.ts +0 -8
  123. package/presentation/FileList/components/BulkActions/BatchEditorDialog/FieldRenderer.js +0 -31
  124. package/presentation/FileList/components/BulkActions/BatchEditorDialog/FieldRenderer.js.map +0 -1
  125. package/presentation/FileList/components/BulkActions/BatchEditorDialog/Operation.d.ts +0 -11
  126. package/presentation/FileList/components/BulkActions/BatchEditorDialog/Operation.js +0 -33
  127. package/presentation/FileList/components/BulkActions/BatchEditorDialog/Operation.js.map +0 -1
  128. package/presentation/FileList/components/BulkActions/BatchEditorDialog/index.d.ts +0 -1
  129. package/presentation/FileList/components/BulkActions/BatchEditorDialog/index.js +0 -1
  130. package/presentation/FileList/components/BulkActions/BulkActionEdit.d.ts +0 -4
  131. package/presentation/FileList/components/BulkActions/BulkActionEdit.js +0 -105
  132. package/presentation/FileList/components/BulkActions/BulkActionEdit.js.map +0 -1
  133. package/presentation/config/configComponents/Browser/FolderFieldDecorator.d.ts +0 -7
  134. package/presentation/config/configComponents/Browser/FolderFieldDecorator.js +0 -11
  135. 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":";AA8FO,MAAMA,uBACTC,kBAAyC"}
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, useEffect, useMemo, useRef } from "react";
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, useEffect, 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 // Derive selected file objects from the presenter's selection IDs and rows.\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 useEffect(() => {\n worker.items = selectedFiles;\n }, [selectedFiles.length]);\n\n // Reset selected items in both the presenter and the Worker.\n const resetItems = useCallback(() => {\n worker.items = [];\n actions.selection.deselectAll();\n }, []);\n\n // Reset results in Worker.\n const resetResults = useCallback(async () => {\n worker.resetResults();\n }, []);\n\n return {\n items: selectedFiles,\n process: (callback: (items: FmFile[]) => void) => worker.process(callback),\n processInSeries: async (\n callback: ({ item, allItems, report }: CallbackParams<FmFile>) => Promise<void>,\n chunkSize?: number\n ) => worker.processInSeries(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","useEffect","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;IAGvC,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;IAEhDU,UAAU;QACNP,OAAO,KAAK,GAAGG;IACnB,GAAG;QAACA,cAAc,MAAM;KAAC;IAGzB,MAAMK,aAAaC,YAAY;QAC3BT,OAAO,KAAK,GAAG,EAAE;QACjBF,QAAQ,SAAS,CAAC,WAAW;IACjC,GAAG,EAAE;IAGL,MAAMY,eAAeD,YAAY;QAC7BT,OAAO,YAAY;IACvB,GAAG,EAAE;IAEL,OAAO;QACH,OAAOG;QACP,SAAS,CAACQ,WAAwCX,OAAO,OAAO,CAACW;QACjE,iBAAiB,OACbA,UACAC,YACCZ,OAAO,eAAe,CAACW,UAAUC;QACtCJ;QACA,SAASR,OAAO,OAAO;QACvBU;IACJ;AACJ;AAGO,MAAMG,aAAa;IACtBC,YAAUA;IACVlB,WAAAA;IACA,WAAWmB;AACf"}
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.folders.childFolders;
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":"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"}
@@ -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.folders.childFolders),
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.folders.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,OAAO,CAAC,YAAY;WAAMM;KAAS,GAC5DA;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"}
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(),