@webiny/app-file-manager 6.4.0-beta.2 → 6.4.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app.js +2 -1
- package/app.js.map +1 -1
- package/exports/admin/ui/file-manager.d.ts +2 -0
- package/exports/admin/ui/file-manager.js +1 -0
- package/features/fileUploader/FileUploader.d.ts +3 -2
- package/features/fileUploader/FileUploader.js +10 -6
- package/features/fileUploader/FileUploader.js.map +1 -1
- package/features/fileUploader/FileUploader.test.js +2 -0
- package/features/fileUploader/FileUploader.test.js.map +1 -1
- package/features/fileUrlFormatter/FileUrlFormatter.d.ts +8 -0
- package/features/fileUrlFormatter/FileUrlFormatter.js +13 -0
- package/features/fileUrlFormatter/FileUrlFormatter.js.map +1 -0
- package/features/fileUrlFormatter/abstractions.d.ts +1 -0
- package/features/fileUrlFormatter/abstractions.js +1 -0
- package/features/fileUrlFormatter/feature.d.ts +1 -0
- package/features/fileUrlFormatter/feature.js +11 -0
- package/features/fileUrlFormatter/feature.js.map +1 -0
- package/features/getFile/GetFileGateway.d.ts +3 -1
- package/features/getFile/GetFileGateway.js +7 -4
- package/features/getFile/GetFileGateway.js.map +1 -1
- package/features/listFiles/ListFilesGateway.d.ts +3 -1
- package/features/listFiles/ListFilesGateway.js +7 -4
- package/features/listFiles/ListFilesGateway.js.map +1 -1
- package/features/shared/FILE_FIELDS.js +1 -2
- package/features/shared/FILE_FIELDS.js.map +1 -1
- package/features/shared/FileFieldsProvider.d.ts +8 -0
- package/features/shared/FileFieldsProvider.js +14 -0
- package/features/shared/FileFieldsProvider.js.map +1 -0
- package/features/shared/FileFieldsProviderWithWcp.d.ts +12 -0
- package/features/shared/FileFieldsProviderWithWcp.js +25 -0
- package/features/shared/FileFieldsProviderWithWcp.js.map +1 -0
- package/features/shared/abstractions.d.ts +7 -0
- package/features/shared/abstractions.js +2 -1
- package/features/shared/abstractions.js.map +1 -1
- package/features/shared/feature.d.ts +1 -0
- package/features/shared/feature.js +7 -2
- package/features/shared/feature.js.map +1 -1
- package/features/shared/index.d.ts +2 -2
- package/features/shared/index.js +1 -1
- package/features/updateFile/UpdateFile.test.js +2 -0
- package/features/updateFile/UpdateFile.test.js.map +1 -1
- package/features/updateFile/UpdateFileRepository.d.ts +3 -2
- package/features/updateFile/UpdateFileRepository.js +7 -5
- package/features/updateFile/UpdateFileRepository.js.map +1 -1
- package/features/updateFile/UpdateFileUseCase.d.ts +3 -1
- package/features/updateFile/UpdateFileUseCase.js +7 -4
- package/features/updateFile/UpdateFileUseCase.js.map +1 -1
- package/modules/FileUrlFormatter.d.ts +2 -0
- package/modules/FileUrlFormatter.js +9 -0
- package/modules/FileUrlFormatter.js.map +1 -0
- package/package.json +24 -22
- package/presentation/FileList/components/FileDropArea/FileDropArea.js +0 -1
- package/presentation/FileList/components/FileDropArea/FileDropArea.js.map +1 -1
- package/presentation/FileList/components/Grid/FileGrid.js +1 -7
- package/presentation/FileList/components/Grid/FileGrid.js.map +1 -1
- package/presentation/FileManager/FileManagerRenderer.js +2 -2
- package/presentation/FileManager/FileManagerRenderer.js.map +1 -1
- package/presentation/FileManager/FileManagerView.js +14 -6
- package/presentation/FileManager/FileManagerView.js.map +1 -1
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { WcpService } from "@webiny/app-admin/features/wcp/abstractions.js";
|
|
2
|
+
import { FileFieldsProvider as Abstraction } from "./abstractions.js";
|
|
3
|
+
declare class FileFieldsProviderWithWcpImpl implements Abstraction.Interface {
|
|
4
|
+
private wcp;
|
|
5
|
+
private decoratee;
|
|
6
|
+
constructor(wcp: WcpService.Interface, decoratee: Abstraction.Interface);
|
|
7
|
+
execute(): Promise<string[]>;
|
|
8
|
+
}
|
|
9
|
+
export declare const FileFieldsProviderWithWcp: typeof FileFieldsProviderWithWcpImpl & {
|
|
10
|
+
__abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").IFileFieldsProvider>;
|
|
11
|
+
};
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { WcpService } from "@webiny/app-admin/features/wcp/abstractions.js";
|
|
2
|
+
import { FileFieldsProvider } from "./abstractions.js";
|
|
3
|
+
class FileFieldsProviderWithWcpImpl {
|
|
4
|
+
constructor(wcp, decoratee){
|
|
5
|
+
this.wcp = wcp;
|
|
6
|
+
this.decoratee = decoratee;
|
|
7
|
+
}
|
|
8
|
+
async execute() {
|
|
9
|
+
const fields = await this.decoratee.execute();
|
|
10
|
+
if (this.wcp.getProject().canUsePrivateFiles()) return [
|
|
11
|
+
...fields,
|
|
12
|
+
"accessControl.type"
|
|
13
|
+
];
|
|
14
|
+
return fields;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const FileFieldsProviderWithWcp = FileFieldsProvider.createDecorator({
|
|
18
|
+
decorator: FileFieldsProviderWithWcpImpl,
|
|
19
|
+
dependencies: [
|
|
20
|
+
WcpService
|
|
21
|
+
]
|
|
22
|
+
});
|
|
23
|
+
export { FileFieldsProviderWithWcp };
|
|
24
|
+
|
|
25
|
+
//# sourceMappingURL=FileFieldsProviderWithWcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/shared/FileFieldsProviderWithWcp.js","sources":["../../../src/features/shared/FileFieldsProviderWithWcp.ts"],"sourcesContent":["import { WcpService } from \"@webiny/app-admin/features/wcp/abstractions.js\";\nimport { FileFieldsProvider as Abstraction } from \"./abstractions.js\";\n\nclass FileFieldsProviderWithWcpImpl implements Abstraction.Interface {\n constructor(\n private wcp: WcpService.Interface,\n private decoratee: Abstraction.Interface\n ) {}\n\n async execute(): Promise<string[]> {\n const fields = await this.decoratee.execute();\n\n if (this.wcp.getProject().canUsePrivateFiles()) {\n return [...fields, \"accessControl.type\"];\n }\n\n return fields;\n }\n}\n\nexport const FileFieldsProviderWithWcp = Abstraction.createDecorator({\n decorator: FileFieldsProviderWithWcpImpl,\n dependencies: [WcpService]\n});\n"],"names":["FileFieldsProviderWithWcpImpl","wcp","decoratee","fields","FileFieldsProviderWithWcp","Abstraction","WcpService"],"mappings":";;AAGA,MAAMA;IACF,YACYC,GAAyB,EACzBC,SAAgC,CAC1C;aAFUD,GAAG,GAAHA;aACAC,SAAS,GAATA;IACT;IAEH,MAAM,UAA6B;QAC/B,MAAMC,SAAS,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO;QAE3C,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,kBAAkB,IACxC,OAAO;eAAIA;YAAQ;SAAqB;QAG5C,OAAOA;IACX;AACJ;AAEO,MAAMC,4BAA4BC,mBAAAA,eAA2B,CAAC;IACjE,WAAWL;IACX,cAAc;QAACM;KAAW;AAC9B"}
|
|
@@ -4,3 +4,10 @@ export declare const FilesListCache: import("@webiny/di").Abstraction<IListCache
|
|
|
4
4
|
export declare namespace FilesListCache {
|
|
5
5
|
type Interface = IListCache<FmFile>;
|
|
6
6
|
}
|
|
7
|
+
export interface IFileFieldsProvider {
|
|
8
|
+
execute(): Promise<string[]>;
|
|
9
|
+
}
|
|
10
|
+
export declare const FileFieldsProvider: import("@webiny/di").Abstraction<IFileFieldsProvider>;
|
|
11
|
+
export declare namespace FileFieldsProvider {
|
|
12
|
+
type Interface = IFileFieldsProvider;
|
|
13
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createAbstraction } from "@webiny/feature/admin";
|
|
2
2
|
const FilesListCache = createAbstraction("FilesListCache");
|
|
3
|
-
|
|
3
|
+
const FileFieldsProvider = createAbstraction("FileFieldsProvider");
|
|
4
|
+
export { FileFieldsProvider, FilesListCache };
|
|
4
5
|
|
|
5
6
|
//# sourceMappingURL=abstractions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/shared/abstractions.js","sources":["../../../src/features/shared/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { IListCache } from \"@webiny/app-admin/features/listCache/index.js\";\nimport type { FmFile } from \"./types.js\";\n\n// Shared cache for file list data, used by all file CRUD repositories.\nexport const FilesListCache = createAbstraction<IListCache<FmFile>>(\"FilesListCache\");\n\nexport namespace FilesListCache {\n export type Interface = IListCache<FmFile>;\n}\n"],"names":["FilesListCache","createAbstraction"],"mappings":";AAKO,MAAMA,iBAAiBC,kBAAsC"}
|
|
1
|
+
{"version":3,"file":"features/shared/abstractions.js","sources":["../../../src/features/shared/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/admin\";\nimport type { IListCache } from \"@webiny/app-admin/features/listCache/index.js\";\nimport type { FmFile } from \"./types.js\";\n\n// Shared cache for file list data, used by all file CRUD repositories.\nexport const FilesListCache = createAbstraction<IListCache<FmFile>>(\"FilesListCache\");\n\nexport namespace FilesListCache {\n export type Interface = IListCache<FmFile>;\n}\n\nexport interface IFileFieldsProvider {\n execute(): Promise<string[]>;\n}\n\nexport const FileFieldsProvider = createAbstraction<IFileFieldsProvider>(\"FileFieldsProvider\");\n\nexport namespace FileFieldsProvider {\n export type Interface = IFileFieldsProvider;\n}\n"],"names":["FilesListCache","createAbstraction","FileFieldsProvider"],"mappings":";AAKO,MAAMA,iBAAiBC,kBAAsC;AAU7D,MAAMC,qBAAqBD,kBAAuC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FmFile } from "./types.js";
|
|
2
2
|
export declare const SharedCacheFeature: import("@webiny/feature/admin").FeatureDefinition<{
|
|
3
3
|
cache: import("@webiny/app-admin/features/listCache/ListCache.js").IListCache<FmFile>;
|
|
4
|
+
fileFieldsProvider: import("./abstractions.js").IFileFieldsProvider;
|
|
4
5
|
}, []>;
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { createFeature } from "@webiny/feature/admin";
|
|
2
|
-
import { FilesListCache } from "./abstractions.js";
|
|
2
|
+
import { FileFieldsProvider, FilesListCache } from "./abstractions.js";
|
|
3
3
|
import { ListCache } from "./FilesListCache.js";
|
|
4
|
+
import { FileFieldsProvider as external_FileFieldsProvider_js_FileFieldsProvider } from "./FileFieldsProvider.js";
|
|
5
|
+
import { FileFieldsProviderWithWcp } from "./FileFieldsProviderWithWcp.js";
|
|
4
6
|
const SharedCacheFeature = createFeature({
|
|
5
7
|
name: "FileManager/SharedCache",
|
|
6
8
|
register (container) {
|
|
7
9
|
container.registerInstance(FilesListCache, new ListCache());
|
|
10
|
+
container.register(external_FileFieldsProvider_js_FileFieldsProvider).inSingletonScope();
|
|
11
|
+
container.registerDecorator(FileFieldsProviderWithWcp);
|
|
8
12
|
},
|
|
9
13
|
resolve (container) {
|
|
10
14
|
return {
|
|
11
|
-
cache: container.resolve(FilesListCache)
|
|
15
|
+
cache: container.resolve(FilesListCache),
|
|
16
|
+
fileFieldsProvider: container.resolve(FileFieldsProvider)
|
|
12
17
|
};
|
|
13
18
|
}
|
|
14
19
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/shared/feature.js","sources":["../../../src/features/shared/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/admin\";\nimport {
|
|
1
|
+
{"version":3,"file":"features/shared/feature.js","sources":["../../../src/features/shared/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/admin\";\nimport {\n FilesListCache,\n FileFieldsProvider as FileFieldsProviderAbstraction\n} from \"./abstractions.js\";\nimport { ListCache } from \"./FilesListCache.js\";\nimport { FileFieldsProvider } from \"./FileFieldsProvider.js\";\nimport { FileFieldsProviderWithWcp } from \"./FileFieldsProviderWithWcp.js\";\nimport type { FmFile } from \"./types.js\";\n\nexport const SharedCacheFeature = createFeature({\n name: \"FileManager/SharedCache\",\n register(container) {\n container.registerInstance(FilesListCache, new ListCache<FmFile>());\n container.register(FileFieldsProvider).inSingletonScope();\n container.registerDecorator(FileFieldsProviderWithWcp);\n },\n resolve(container) {\n return {\n cache: container.resolve(FilesListCache),\n fileFieldsProvider: container.resolve(FileFieldsProviderAbstraction)\n };\n }\n});\n"],"names":["SharedCacheFeature","createFeature","container","FilesListCache","ListCache","FileFieldsProvider","FileFieldsProviderWithWcp","FileFieldsProviderAbstraction"],"mappings":";;;;;AAUO,MAAMA,qBAAqBC,cAAc;IAC5C,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,gBAAgB,CAACC,gBAAgB,IAAIC;QAC/CF,UAAU,QAAQ,CAACG,mDAAoB,gBAAgB;QACvDH,UAAU,iBAAiB,CAACI;IAChC;IACA,SAAQJ,SAAS;QACb,OAAO;YACH,OAAOA,UAAU,OAAO,CAACC;YACzB,oBAAoBD,UAAU,OAAO,CAACK;QAC1C;IACJ;AACJ"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { SharedCacheFeature } from "./feature.js";
|
|
2
|
-
export { FilesListCache } from "./abstractions.js";
|
|
3
|
-
export type { FilesListCache as FilesListCacheNs } from "./abstractions.js";
|
|
2
|
+
export { FilesListCache, FileFieldsProvider } from "./abstractions.js";
|
|
3
|
+
export type { FilesListCache as FilesListCacheNs, FileFieldsProvider as FileFieldsProviderNs } from "./abstractions.js";
|
|
4
4
|
export * from "./types.js";
|
package/features/shared/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
2
2
|
import { Container } from "@webiny/di";
|
|
3
3
|
import { ListCache } from "@webiny/app-admin/features/listCache/index.js";
|
|
4
4
|
import { FilesListCache } from "../shared/abstractions.js";
|
|
5
|
+
import { FileFieldsProvider } from "../shared/FileFieldsProvider.js";
|
|
5
6
|
import { UpdateFileGateway, UpdateFileUseCase } from "./abstractions.js";
|
|
6
7
|
import { UpdateFileUseCase as external_UpdateFileUseCase_js_UpdateFileUseCase } from "./UpdateFileUseCase.js";
|
|
7
8
|
import { UpdateFileRepository } from "./UpdateFileRepository.js";
|
|
@@ -47,6 +48,7 @@ function createContainer(mockGateway) {
|
|
|
47
48
|
const cache = new ListCache();
|
|
48
49
|
container.registerInstance(UpdateFileGateway, mockGateway);
|
|
49
50
|
container.registerInstance(FilesListCache, cache);
|
|
51
|
+
container.register(FileFieldsProvider).inSingletonScope();
|
|
50
52
|
container.register(UpdateFileRepository).inSingletonScope();
|
|
51
53
|
container.register(external_UpdateFileUseCase_js_UpdateFileUseCase);
|
|
52
54
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/updateFile/UpdateFile.test.js","sources":["../../../src/features/updateFile/UpdateFile.test.ts"],"sourcesContent":["import { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { ListCache } from \"@webiny/app-admin/features/listCache/index.js\";\nimport { FilesListCache } from \"../shared/abstractions.js\";\nimport {\n UpdateFileUseCase as UseCaseAbstraction,\n UpdateFileGateway as GatewayAbstraction,\n type IUpdateFileGateway,\n type UpdateFileGatewayParams\n} from \"./abstractions.js\";\nimport { UpdateFileUseCase } from \"./UpdateFileUseCase.js\";\nimport { UpdateFileRepository } from \"./UpdateFileRepository.js\";\nimport type { FmFile } from \"../shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction createFile(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: 204800,\n metadata: {},\n tags: [\"photo\"],\n createdOn: \"2025-01-01T00:00:00Z\",\n savedOn: \"2025-01-01T00:00:00Z\",\n createdBy: { id: \"user-1\", displayName: \"Test User\", type: \"admin\" },\n savedBy: { id: \"user-1\", displayName: \"Test User\", type: \"admin\" },\n location: { folderId: \"root\" },\n ...overrides\n };\n}\n\ntype MockGateway = IUpdateFileGateway & { execute: ReturnType<typeof vi.fn> };\n\nfunction createMockGateway(file: FmFile = createFile()): MockGateway {\n const execute = vi.fn<(params: UpdateFileGatewayParams) => Promise<FmFile>>();\n execute.mockResolvedValue(file);\n return { execute };\n}\n\nfunction createContainer(mockGateway: MockGateway) {\n const container = new Container();\n const cache = new ListCache<FmFile>();\n\n // Register the mock gateway instance.\n container.registerInstance(GatewayAbstraction, mockGateway);\n\n // Register the shared cache instance.\n container.registerInstance(FilesListCache, cache);\n\n // Register the real repository and use case.\n container.register(UpdateFileRepository).inSingletonScope();\n container.register(UpdateFileUseCase);\n\n return { container, cache };\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe(\"UpdateFile Feature\", () => {\n let mockGateway: MockGateway;\n let cache: ListCache<FmFile>;\n let container: Container;\n\n beforeEach(() => {\n mockGateway = createMockGateway();\n const result = createContainer(mockGateway);\n container = result.container;\n cache = result.cache as ListCache<FmFile>;\n });\n\n // -----------------------------------------------------------------------\n // Metadata update.\n // -----------------------------------------------------------------------\n\n it(\"should call gateway with file ID and changed fields\", async () => {\n const updated = createFile({ name: \"renamed.jpg\", tags: [\"photo\", \"landscape\"] });\n mockGateway.execute.mockResolvedValue(updated);\n\n const useCase = container.resolve(UseCaseAbstraction);\n await useCase.execute({\n id: \"file-1\",\n data: { name: \"renamed.jpg\", tags: [\"photo\", \"landscape\"] }\n });\n\n expect(mockGateway.execute).toHaveBeenCalledTimes(1);\n const callArgs = mockGateway.execute.mock.calls[0][0];\n expect(callArgs.id).toBe(\"file-1\");\n expect(callArgs.data.name).toBe(\"renamed.jpg\");\n expect(callArgs.data.tags).toEqual([\"photo\", \"landscape\"]);\n });\n\n it(\"should return success result with the updated file\", async () => {\n const updated = createFile({ name: \"renamed.jpg\" });\n mockGateway.execute.mockResolvedValue(updated);\n\n const useCase = container.resolve(UseCaseAbstraction);\n const result = await useCase.execute({\n id: \"file-1\",\n data: { name: \"renamed.jpg\" }\n });\n\n expect(result.success).toBe(true);\n if (result.success) {\n expect(result.file).toEqual(updated);\n }\n });\n\n it(\"should pass accessControl through to the gateway\", async () => {\n const updated = createFile({\n accessControl: { type: \"private-authenticated\" }\n });\n mockGateway.execute.mockResolvedValue(updated);\n\n const useCase = container.resolve(UseCaseAbstraction);\n await useCase.execute({\n id: \"file-1\",\n data: { accessControl: { type: \"private-authenticated\" } }\n });\n\n const callArgs = mockGateway.execute.mock.calls[0][0];\n expect(callArgs.data.accessControl).toEqual({ type: \"private-authenticated\" });\n });\n\n it(\"should pass metadata through to the gateway\", async () => {\n const updated = createFile({\n metadata: { image: { width: 800, height: 600 } }\n });\n mockGateway.execute.mockResolvedValue(updated);\n\n const useCase = container.resolve(UseCaseAbstraction);\n await useCase.execute({\n id: \"file-1\",\n data: { metadata: { image: { width: 800, height: 600 } } }\n });\n\n const callArgs = mockGateway.execute.mock.calls[0][0];\n expect(callArgs.data.metadata).toEqual({ image: { width: 800, height: 600 } });\n });\n\n // -----------------------------------------------------------------------\n // Move file (location.folderId change) updates cache correctly.\n // -----------------------------------------------------------------------\n\n it(\"should update the cached file when location.folderId changes\", async () => {\n // Seed the cache with the original file.\n const original = createFile({ id: \"file-move\", location: { folderId: \"folder-a\" } });\n cache.addItems([original]);\n\n const moved = createFile({ id: \"file-move\", location: { folderId: \"folder-b\" } });\n mockGateway.execute.mockResolvedValue(moved);\n\n const useCase = container.resolve(UseCaseAbstraction);\n await useCase.execute({\n id: \"file-move\",\n data: { location: { folderId: \"folder-b\" } }\n });\n\n const cached = cache.getItem(f => f.id === \"file-move\");\n expect(cached).toBeDefined();\n expect(cached!.location.folderId).toBe(\"folder-b\");\n });\n\n it(\"should only update the matching file in cache, leaving others untouched\", async () => {\n // Seed cache with two files.\n const fileA = createFile({ id: \"file-a\", name: \"a.jpg\" });\n const fileB = createFile({ id: \"file-b\", name: \"b.jpg\" });\n cache.addItems([fileA, fileB]);\n\n const updatedA = createFile({ id: \"file-a\", name: \"a-renamed.jpg\" });\n mockGateway.execute.mockResolvedValue(updatedA);\n\n const useCase = container.resolve(UseCaseAbstraction);\n await useCase.execute({\n id: \"file-a\",\n data: { name: \"a-renamed.jpg\" }\n });\n\n // file-a should be updated.\n const cachedA = cache.getItem(f => f.id === \"file-a\");\n expect(cachedA!.name).toBe(\"a-renamed.jpg\");\n\n // file-b should remain unchanged.\n const cachedB = cache.getItem(f => f.id === \"file-b\");\n expect(cachedB!.name).toBe(\"b.jpg\");\n });\n\n // -----------------------------------------------------------------------\n // Error handling.\n // -----------------------------------------------------------------------\n\n it(\"should return error result when gateway throws\", async () => {\n mockGateway.execute.mockRejectedValue(new Error(\"Update failed\"));\n\n const useCase = container.resolve(UseCaseAbstraction);\n const result = await useCase.execute({\n id: \"file-1\",\n data: { name: \"renamed.jpg\" }\n });\n\n expect(result.success).toBe(false);\n if (!result.success) {\n expect(result.error.code).toBe(\"UPDATE_FILE_ERROR\");\n expect(result.error.message).toBe(\"Update failed\");\n }\n });\n\n it(\"should not update cache when gateway throws\", async () => {\n // Seed cache with the original file.\n const original = createFile({ id: \"file-err\", name: \"original.jpg\" });\n cache.addItems([original]);\n\n mockGateway.execute.mockRejectedValue(new Error(\"Update failed\"));\n\n const useCase = container.resolve(UseCaseAbstraction);\n await useCase.execute({\n id: \"file-err\",\n data: { name: \"should-not-apply.jpg\" }\n });\n\n // Cache should still have the original file unchanged.\n const cached = cache.getItem(f => f.id === \"file-err\");\n expect(cached!.name).toBe(\"original.jpg\");\n });\n});\n"],"names":["createFile","overrides","createMockGateway","file","execute","vi","createContainer","mockGateway","container","Container","cache","ListCache","GatewayAbstraction","FilesListCache","UpdateFileRepository","UpdateFileUseCase","describe","beforeEach","result","it","updated","useCase","UseCaseAbstraction","expect","callArgs","original","moved","cached","f","fileA","fileB","updatedA","cachedA","cachedB","Error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"features/updateFile/UpdateFile.test.js","sources":["../../../src/features/updateFile/UpdateFile.test.ts"],"sourcesContent":["import { describe, it, expect, vi, beforeEach } from \"vitest\";\nimport { Container } from \"@webiny/di\";\nimport { ListCache } from \"@webiny/app-admin/features/listCache/index.js\";\nimport { FilesListCache } from \"../shared/abstractions.js\";\nimport { FileFieldsProvider } from \"../shared/FileFieldsProvider.js\";\nimport {\n UpdateFileUseCase as UseCaseAbstraction,\n UpdateFileGateway as GatewayAbstraction,\n type IUpdateFileGateway,\n type UpdateFileGatewayParams\n} from \"./abstractions.js\";\nimport { UpdateFileUseCase } from \"./UpdateFileUseCase.js\";\nimport { UpdateFileRepository } from \"./UpdateFileRepository.js\";\nimport type { FmFile } from \"../shared/types.js\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction createFile(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: 204800,\n metadata: {},\n tags: [\"photo\"],\n createdOn: \"2025-01-01T00:00:00Z\",\n savedOn: \"2025-01-01T00:00:00Z\",\n createdBy: { id: \"user-1\", displayName: \"Test User\", type: \"admin\" },\n savedBy: { id: \"user-1\", displayName: \"Test User\", type: \"admin\" },\n location: { folderId: \"root\" },\n ...overrides\n };\n}\n\ntype MockGateway = IUpdateFileGateway & { execute: ReturnType<typeof vi.fn> };\n\nfunction createMockGateway(file: FmFile = createFile()): MockGateway {\n const execute = vi.fn<(params: UpdateFileGatewayParams) => Promise<FmFile>>();\n execute.mockResolvedValue(file);\n return { execute };\n}\n\nfunction createContainer(mockGateway: MockGateway) {\n const container = new Container();\n const cache = new ListCache<FmFile>();\n\n // Register the mock gateway instance.\n container.registerInstance(GatewayAbstraction, mockGateway);\n\n // Register the shared cache instance.\n container.registerInstance(FilesListCache, cache);\n\n // Register the file fields provider.\n container.register(FileFieldsProvider).inSingletonScope();\n\n // Register the real repository and use case.\n container.register(UpdateFileRepository).inSingletonScope();\n container.register(UpdateFileUseCase);\n\n return { container, cache };\n}\n\n// ---------------------------------------------------------------------------\n// Tests\n// ---------------------------------------------------------------------------\n\ndescribe(\"UpdateFile Feature\", () => {\n let mockGateway: MockGateway;\n let cache: ListCache<FmFile>;\n let container: Container;\n\n beforeEach(() => {\n mockGateway = createMockGateway();\n const result = createContainer(mockGateway);\n container = result.container;\n cache = result.cache as ListCache<FmFile>;\n });\n\n // -----------------------------------------------------------------------\n // Metadata update.\n // -----------------------------------------------------------------------\n\n it(\"should call gateway with file ID and changed fields\", async () => {\n const updated = createFile({ name: \"renamed.jpg\", tags: [\"photo\", \"landscape\"] });\n mockGateway.execute.mockResolvedValue(updated);\n\n const useCase = container.resolve(UseCaseAbstraction);\n await useCase.execute({\n id: \"file-1\",\n data: { name: \"renamed.jpg\", tags: [\"photo\", \"landscape\"] }\n });\n\n expect(mockGateway.execute).toHaveBeenCalledTimes(1);\n const callArgs = mockGateway.execute.mock.calls[0][0];\n expect(callArgs.id).toBe(\"file-1\");\n expect(callArgs.data.name).toBe(\"renamed.jpg\");\n expect(callArgs.data.tags).toEqual([\"photo\", \"landscape\"]);\n });\n\n it(\"should return success result with the updated file\", async () => {\n const updated = createFile({ name: \"renamed.jpg\" });\n mockGateway.execute.mockResolvedValue(updated);\n\n const useCase = container.resolve(UseCaseAbstraction);\n const result = await useCase.execute({\n id: \"file-1\",\n data: { name: \"renamed.jpg\" }\n });\n\n expect(result.success).toBe(true);\n if (result.success) {\n expect(result.file).toEqual(updated);\n }\n });\n\n it(\"should pass accessControl through to the gateway\", async () => {\n const updated = createFile({\n accessControl: { type: \"private-authenticated\" }\n });\n mockGateway.execute.mockResolvedValue(updated);\n\n const useCase = container.resolve(UseCaseAbstraction);\n await useCase.execute({\n id: \"file-1\",\n data: { accessControl: { type: \"private-authenticated\" } }\n });\n\n const callArgs = mockGateway.execute.mock.calls[0][0];\n expect(callArgs.data.accessControl).toEqual({ type: \"private-authenticated\" });\n });\n\n it(\"should pass metadata through to the gateway\", async () => {\n const updated = createFile({\n metadata: { image: { width: 800, height: 600 } }\n });\n mockGateway.execute.mockResolvedValue(updated);\n\n const useCase = container.resolve(UseCaseAbstraction);\n await useCase.execute({\n id: \"file-1\",\n data: { metadata: { image: { width: 800, height: 600 } } }\n });\n\n const callArgs = mockGateway.execute.mock.calls[0][0];\n expect(callArgs.data.metadata).toEqual({ image: { width: 800, height: 600 } });\n });\n\n // -----------------------------------------------------------------------\n // Move file (location.folderId change) updates cache correctly.\n // -----------------------------------------------------------------------\n\n it(\"should update the cached file when location.folderId changes\", async () => {\n // Seed the cache with the original file.\n const original = createFile({ id: \"file-move\", location: { folderId: \"folder-a\" } });\n cache.addItems([original]);\n\n const moved = createFile({ id: \"file-move\", location: { folderId: \"folder-b\" } });\n mockGateway.execute.mockResolvedValue(moved);\n\n const useCase = container.resolve(UseCaseAbstraction);\n await useCase.execute({\n id: \"file-move\",\n data: { location: { folderId: \"folder-b\" } }\n });\n\n const cached = cache.getItem(f => f.id === \"file-move\");\n expect(cached).toBeDefined();\n expect(cached!.location.folderId).toBe(\"folder-b\");\n });\n\n it(\"should only update the matching file in cache, leaving others untouched\", async () => {\n // Seed cache with two files.\n const fileA = createFile({ id: \"file-a\", name: \"a.jpg\" });\n const fileB = createFile({ id: \"file-b\", name: \"b.jpg\" });\n cache.addItems([fileA, fileB]);\n\n const updatedA = createFile({ id: \"file-a\", name: \"a-renamed.jpg\" });\n mockGateway.execute.mockResolvedValue(updatedA);\n\n const useCase = container.resolve(UseCaseAbstraction);\n await useCase.execute({\n id: \"file-a\",\n data: { name: \"a-renamed.jpg\" }\n });\n\n // file-a should be updated.\n const cachedA = cache.getItem(f => f.id === \"file-a\");\n expect(cachedA!.name).toBe(\"a-renamed.jpg\");\n\n // file-b should remain unchanged.\n const cachedB = cache.getItem(f => f.id === \"file-b\");\n expect(cachedB!.name).toBe(\"b.jpg\");\n });\n\n // -----------------------------------------------------------------------\n // Error handling.\n // -----------------------------------------------------------------------\n\n it(\"should return error result when gateway throws\", async () => {\n mockGateway.execute.mockRejectedValue(new Error(\"Update failed\"));\n\n const useCase = container.resolve(UseCaseAbstraction);\n const result = await useCase.execute({\n id: \"file-1\",\n data: { name: \"renamed.jpg\" }\n });\n\n expect(result.success).toBe(false);\n if (!result.success) {\n expect(result.error.code).toBe(\"UPDATE_FILE_ERROR\");\n expect(result.error.message).toBe(\"Update failed\");\n }\n });\n\n it(\"should not update cache when gateway throws\", async () => {\n // Seed cache with the original file.\n const original = createFile({ id: \"file-err\", name: \"original.jpg\" });\n cache.addItems([original]);\n\n mockGateway.execute.mockRejectedValue(new Error(\"Update failed\"));\n\n const useCase = container.resolve(UseCaseAbstraction);\n await useCase.execute({\n id: \"file-err\",\n data: { name: \"should-not-apply.jpg\" }\n });\n\n // Cache should still have the original file unchanged.\n const cached = cache.getItem(f => f.id === \"file-err\");\n expect(cached!.name).toBe(\"original.jpg\");\n });\n});\n"],"names":["createFile","overrides","createMockGateway","file","execute","vi","createContainer","mockGateway","container","Container","cache","ListCache","GatewayAbstraction","FilesListCache","FileFieldsProvider","UpdateFileRepository","UpdateFileUseCase","describe","beforeEach","result","it","updated","useCase","UseCaseAbstraction","expect","callArgs","original","moved","cached","f","fileA","fileB","updatedA","cachedA","cachedB","Error"],"mappings":"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"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { FilesListCache } from "../shared/abstractions.js";
|
|
1
|
+
import { FilesListCache, FileFieldsProvider } from "../shared/abstractions.js";
|
|
2
2
|
import type { FmFile } from "../shared/types.js";
|
|
3
3
|
import { UpdateFileRepository as RepositoryAbstraction, UpdateFileGateway, type UpdateFileGatewayParams } from "./abstractions.js";
|
|
4
4
|
declare class UpdateFileRepositoryImpl implements RepositoryAbstraction.Interface {
|
|
5
5
|
private gateway;
|
|
6
6
|
private cache;
|
|
7
|
-
|
|
7
|
+
private fileFieldsProvider;
|
|
8
|
+
constructor(gateway: UpdateFileGateway.Interface, cache: FilesListCache.Interface, fileFieldsProvider: FileFieldsProvider.Interface);
|
|
8
9
|
execute(params: UpdateFileGatewayParams): Promise<FmFile>;
|
|
9
10
|
}
|
|
10
11
|
export declare const UpdateFileRepository: typeof UpdateFileRepositoryImpl & {
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { runInAction } from "mobx";
|
|
2
|
-
import { FilesListCache } from "../shared/abstractions.js";
|
|
2
|
+
import { FileFieldsProvider, FilesListCache } from "../shared/abstractions.js";
|
|
3
3
|
import { UpdateFileGateway, UpdateFileRepository } from "./abstractions.js";
|
|
4
|
-
import { FILE_FIELDS } from "../shared/FILE_FIELDS.js";
|
|
5
4
|
class UpdateFileRepositoryImpl {
|
|
6
|
-
constructor(gateway, cache){
|
|
5
|
+
constructor(gateway, cache, fileFieldsProvider){
|
|
7
6
|
this.gateway = gateway;
|
|
8
7
|
this.cache = cache;
|
|
8
|
+
this.fileFieldsProvider = fileFieldsProvider;
|
|
9
9
|
}
|
|
10
10
|
async execute(params) {
|
|
11
|
+
const fileFields = await this.fileFieldsProvider.execute();
|
|
11
12
|
const file = await this.gateway.execute({
|
|
12
13
|
...params,
|
|
13
|
-
fields: params.fields.length > 0 ? params.fields :
|
|
14
|
+
fields: params.fields.length > 0 ? params.fields : fileFields
|
|
14
15
|
});
|
|
15
16
|
runInAction(()=>{
|
|
16
17
|
this.cache.updateItems((item)=>item.id === file.id ? file : item);
|
|
@@ -22,7 +23,8 @@ const UpdateFileRepository_UpdateFileRepository = UpdateFileRepository.createImp
|
|
|
22
23
|
implementation: UpdateFileRepositoryImpl,
|
|
23
24
|
dependencies: [
|
|
24
25
|
UpdateFileGateway,
|
|
25
|
-
FilesListCache
|
|
26
|
+
FilesListCache,
|
|
27
|
+
FileFieldsProvider
|
|
26
28
|
]
|
|
27
29
|
});
|
|
28
30
|
export { UpdateFileRepository_UpdateFileRepository as UpdateFileRepository };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/updateFile/UpdateFileRepository.js","sources":["../../../src/features/updateFile/UpdateFileRepository.ts"],"sourcesContent":["import { runInAction } from \"mobx\";\nimport { FilesListCache } from \"../shared/abstractions.js\";\nimport type { FmFile } from \"../shared/types.js\";\nimport {\n UpdateFileRepository as RepositoryAbstraction,\n UpdateFileGateway,\n type UpdateFileGatewayParams\n} from \"./abstractions.js\";\
|
|
1
|
+
{"version":3,"file":"features/updateFile/UpdateFileRepository.js","sources":["../../../src/features/updateFile/UpdateFileRepository.ts"],"sourcesContent":["import { runInAction } from \"mobx\";\nimport { FilesListCache, FileFieldsProvider } from \"../shared/abstractions.js\";\nimport type { FmFile } from \"../shared/types.js\";\nimport {\n UpdateFileRepository as RepositoryAbstraction,\n UpdateFileGateway,\n type UpdateFileGatewayParams\n} from \"./abstractions.js\";\n\nclass UpdateFileRepositoryImpl implements RepositoryAbstraction.Interface {\n constructor(\n private gateway: UpdateFileGateway.Interface,\n private cache: FilesListCache.Interface,\n private fileFieldsProvider: FileFieldsProvider.Interface\n ) {}\n\n async execute(params: UpdateFileGatewayParams): Promise<FmFile> {\n const fileFields = await this.fileFieldsProvider.execute();\n const file = await this.gateway.execute({\n ...params,\n fields: params.fields.length > 0 ? params.fields : fileFields\n });\n\n runInAction(() => {\n this.cache.updateItems(item => (item.id === file.id ? file : item));\n });\n\n return file;\n }\n}\n\nexport const UpdateFileRepository = RepositoryAbstraction.createImplementation({\n implementation: UpdateFileRepositoryImpl,\n dependencies: [UpdateFileGateway, FilesListCache, FileFieldsProvider]\n});\n"],"names":["UpdateFileRepositoryImpl","gateway","cache","fileFieldsProvider","params","fileFields","file","runInAction","item","UpdateFileRepository","RepositoryAbstraction","UpdateFileGateway","FilesListCache","FileFieldsProvider"],"mappings":";;;AASA,MAAMA;IACF,YACYC,OAAoC,EACpCC,KAA+B,EAC/BC,kBAAgD,CAC1D;aAHUF,OAAO,GAAPA;aACAC,KAAK,GAALA;aACAC,kBAAkB,GAAlBA;IACT;IAEH,MAAM,QAAQC,MAA+B,EAAmB;QAC5D,MAAMC,aAAa,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO;QACxD,MAAMC,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;YACpC,GAAGF,MAAM;YACT,QAAQA,OAAO,MAAM,CAAC,MAAM,GAAG,IAAIA,OAAO,MAAM,GAAGC;QACvD;QAEAE,YAAY;YACR,IAAI,CAAC,KAAK,CAAC,WAAW,CAACC,CAAAA,OAASA,KAAK,EAAE,KAAKF,KAAK,EAAE,GAAGA,OAAOE;QACjE;QAEA,OAAOF;IACX;AACJ;AAEO,MAAMG,4CAAuBC,qBAAAA,oBAA0C,CAAC;IAC3E,gBAAgBV;IAChB,cAAc;QAACW;QAAmBC;QAAgBC;KAAmB;AACzE"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { FileFieldsProvider } from "../shared/abstractions.js";
|
|
1
2
|
import { UpdateFileUseCase as UseCaseAbstraction, UpdateFileRepository, type UpdateFileUseCaseParams, type UpdateFileUseCaseResult } from "./abstractions.js";
|
|
2
3
|
declare class UpdateFileUseCaseImpl implements UseCaseAbstraction.Interface {
|
|
3
4
|
private repository;
|
|
4
|
-
|
|
5
|
+
private fileFieldsProvider;
|
|
6
|
+
constructor(repository: UpdateFileRepository.Interface, fileFieldsProvider: FileFieldsProvider.Interface);
|
|
5
7
|
execute(params: UpdateFileUseCaseParams): Promise<UpdateFileUseCaseResult>;
|
|
6
8
|
}
|
|
7
9
|
export declare const UpdateFileUseCase: typeof UpdateFileUseCaseImpl & {
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
import { FileFieldsProvider } from "../shared/abstractions.js";
|
|
1
2
|
import { UpdateFileRepository, UpdateFileUseCase } from "./abstractions.js";
|
|
2
|
-
import { FILE_FIELDS } from "../shared/FILE_FIELDS.js";
|
|
3
3
|
class UpdateFileUseCaseImpl {
|
|
4
|
-
constructor(repository){
|
|
4
|
+
constructor(repository, fileFieldsProvider){
|
|
5
5
|
this.repository = repository;
|
|
6
|
+
this.fileFieldsProvider = fileFieldsProvider;
|
|
6
7
|
}
|
|
7
8
|
async execute(params) {
|
|
8
9
|
try {
|
|
10
|
+
const fileFields = await this.fileFieldsProvider.execute();
|
|
9
11
|
const file = await this.repository.execute({
|
|
10
12
|
id: params.id,
|
|
11
13
|
data: params.data,
|
|
12
|
-
fields:
|
|
14
|
+
fields: fileFields
|
|
13
15
|
});
|
|
14
16
|
return {
|
|
15
17
|
success: true,
|
|
@@ -30,7 +32,8 @@ class UpdateFileUseCaseImpl {
|
|
|
30
32
|
const UpdateFileUseCase_UpdateFileUseCase = UpdateFileUseCase.createImplementation({
|
|
31
33
|
implementation: UpdateFileUseCaseImpl,
|
|
32
34
|
dependencies: [
|
|
33
|
-
UpdateFileRepository
|
|
35
|
+
UpdateFileRepository,
|
|
36
|
+
FileFieldsProvider
|
|
34
37
|
]
|
|
35
38
|
});
|
|
36
39
|
export { UpdateFileUseCase_UpdateFileUseCase as UpdateFileUseCase };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/updateFile/UpdateFileUseCase.js","sources":["../../../src/features/updateFile/UpdateFileUseCase.ts"],"sourcesContent":["import {\n UpdateFileUseCase as UseCaseAbstraction,\n UpdateFileRepository,\n type UpdateFileUseCaseParams,\n type UpdateFileUseCaseResult\n} from \"./abstractions.js\";\
|
|
1
|
+
{"version":3,"file":"features/updateFile/UpdateFileUseCase.js","sources":["../../../src/features/updateFile/UpdateFileUseCase.ts"],"sourcesContent":["import { FileFieldsProvider } from \"../shared/abstractions.js\";\nimport {\n UpdateFileUseCase as UseCaseAbstraction,\n UpdateFileRepository,\n type UpdateFileUseCaseParams,\n type UpdateFileUseCaseResult\n} from \"./abstractions.js\";\n\nclass UpdateFileUseCaseImpl implements UseCaseAbstraction.Interface {\n constructor(\n private repository: UpdateFileRepository.Interface,\n private fileFieldsProvider: FileFieldsProvider.Interface\n ) {}\n\n async execute(params: UpdateFileUseCaseParams): Promise<UpdateFileUseCaseResult> {\n try {\n const fileFields = await this.fileFieldsProvider.execute();\n const file = await this.repository.execute({\n id: params.id,\n data: params.data,\n fields: fileFields\n });\n return { success: true, file };\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n return { success: false, error: { code: \"UPDATE_FILE_ERROR\", message } };\n }\n }\n}\n\nexport const UpdateFileUseCase = UseCaseAbstraction.createImplementation({\n implementation: UpdateFileUseCaseImpl,\n dependencies: [UpdateFileRepository, FileFieldsProvider]\n});\n"],"names":["UpdateFileUseCaseImpl","repository","fileFieldsProvider","params","fileFields","file","error","message","Error","UpdateFileUseCase","UseCaseAbstraction","UpdateFileRepository","FileFieldsProvider"],"mappings":";;AAQA,MAAMA;IACF,YACYC,UAA0C,EAC1CC,kBAAgD,CAC1D;aAFUD,UAAU,GAAVA;aACAC,kBAAkB,GAAlBA;IACT;IAEH,MAAM,QAAQC,MAA+B,EAAoC;QAC7E,IAAI;YACA,MAAMC,aAAa,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO;YACxD,MAAMC,OAAO,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;gBACvC,IAAIF,OAAO,EAAE;gBACb,MAAMA,OAAO,IAAI;gBACjB,QAAQC;YACZ;YACA,OAAO;gBAAE,SAAS;gBAAMC;YAAK;QACjC,EAAE,OAAOC,OAAO;YACZ,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAM,OAAO,GAAG;YACzD,OAAO;gBAAE,SAAS;gBAAO,OAAO;oBAAE,MAAM;oBAAqBC;gBAAQ;YAAE;QAC3E;IACJ;AACJ;AAEO,MAAME,sCAAoBC,kBAAAA,oBAAuC,CAAC;IACrE,gBAAgBV;IAChB,cAAc;QAACW;QAAsBC;KAAmB;AAC5D"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import react from "react";
|
|
2
|
+
import { RegisterFeature } from "@webiny/app-admin";
|
|
3
|
+
import { FileUrlFormatterFeature } from "../features/fileUrlFormatter/feature.js";
|
|
4
|
+
const FileUrlFormatterModule = ()=>/*#__PURE__*/ react.createElement(RegisterFeature, {
|
|
5
|
+
feature: FileUrlFormatterFeature
|
|
6
|
+
});
|
|
7
|
+
export { FileUrlFormatterModule };
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=FileUrlFormatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modules/FileUrlFormatter.js","sources":["../../src/modules/FileUrlFormatter.tsx"],"sourcesContent":["import React from \"react\";\nimport { RegisterFeature } from \"@webiny/app-admin\";\nimport { FileUrlFormatterFeature } from \"~/features/fileUrlFormatter/feature.js\";\n\nexport const FileUrlFormatterModule = () => {\n return <RegisterFeature feature={FileUrlFormatterFeature} />;\n};\n"],"names":["FileUrlFormatterModule","RegisterFeature","FileUrlFormatterFeature"],"mappings":";;;AAIO,MAAMA,yBAAyB,IAC3B,WAAP,GAAO,oBAACC,iBAAeA;QAAC,SAASC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/app-file-manager",
|
|
3
|
-
"version": "6.4.0-beta.
|
|
3
|
+
"version": "6.4.0-beta.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -15,23 +15,23 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@apollo/react-common": "3.1.4",
|
|
17
17
|
"@apollo/react-components": "3.1.5",
|
|
18
|
-
"@types/react": "18.3.
|
|
19
|
-
"@webiny/admin-ui": "6.4.0-beta.
|
|
20
|
-
"@webiny/app": "6.4.0-beta.
|
|
21
|
-
"@webiny/app-aco": "6.4.0-beta.
|
|
22
|
-
"@webiny/app-admin": "6.4.0-beta.
|
|
23
|
-
"@webiny/app-headless-cms": "6.4.0-beta.
|
|
24
|
-
"@webiny/app-headless-cms-common": "6.4.0-beta.
|
|
25
|
-
"@webiny/app-websockets": "6.4.0-beta.
|
|
18
|
+
"@types/react": "18.3.29",
|
|
19
|
+
"@webiny/admin-ui": "6.4.0-beta.4",
|
|
20
|
+
"@webiny/app": "6.4.0-beta.4",
|
|
21
|
+
"@webiny/app-aco": "6.4.0-beta.4",
|
|
22
|
+
"@webiny/app-admin": "6.4.0-beta.4",
|
|
23
|
+
"@webiny/app-headless-cms": "6.4.0-beta.4",
|
|
24
|
+
"@webiny/app-headless-cms-common": "6.4.0-beta.4",
|
|
25
|
+
"@webiny/app-websockets": "6.4.0-beta.4",
|
|
26
26
|
"@webiny/di": "1.0.1",
|
|
27
|
-
"@webiny/feature": "6.4.0-beta.
|
|
28
|
-
"@webiny/form": "6.4.0-beta.
|
|
29
|
-
"@webiny/icons": "6.4.0-beta.
|
|
30
|
-
"@webiny/plugins": "6.4.0-beta.
|
|
31
|
-
"@webiny/react-composition": "6.4.0-beta.
|
|
32
|
-
"@webiny/react-properties": "6.4.0-beta.
|
|
33
|
-
"@webiny/sdk": "6.4.0-beta.
|
|
34
|
-
"@webiny/validation": "6.4.0-beta.
|
|
27
|
+
"@webiny/feature": "6.4.0-beta.4",
|
|
28
|
+
"@webiny/form": "6.4.0-beta.4",
|
|
29
|
+
"@webiny/icons": "6.4.0-beta.4",
|
|
30
|
+
"@webiny/plugins": "6.4.0-beta.4",
|
|
31
|
+
"@webiny/react-composition": "6.4.0-beta.4",
|
|
32
|
+
"@webiny/react-properties": "6.4.0-beta.4",
|
|
33
|
+
"@webiny/sdk": "6.4.0-beta.4",
|
|
34
|
+
"@webiny/validation": "6.4.0-beta.4",
|
|
35
35
|
"apollo-cache": "1.3.5",
|
|
36
36
|
"apollo-client": "2.6.10",
|
|
37
37
|
"apollo-link": "1.2.14",
|
|
@@ -53,14 +53,16 @@
|
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@svgr/webpack": "8.1.0",
|
|
56
|
-
"@webiny/build-tools": "6.4.0-beta.
|
|
56
|
+
"@webiny/build-tools": "6.4.0-beta.4",
|
|
57
57
|
"rimraf": "6.1.3",
|
|
58
58
|
"typescript": "6.0.3",
|
|
59
|
-
"vitest": "4.1.
|
|
59
|
+
"vitest": "4.1.7"
|
|
60
60
|
},
|
|
61
61
|
"publishConfig": {
|
|
62
|
-
"access": "public"
|
|
63
|
-
"directory": "dist"
|
|
62
|
+
"access": "public"
|
|
64
63
|
},
|
|
65
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "8476da73b653c89cc1474d968baf55c1b0ae0e5f",
|
|
65
|
+
"webiny": {
|
|
66
|
+
"publishFrom": "dist"
|
|
67
|
+
}
|
|
66
68
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/FileList/components/FileDropArea/FileDropArea.js","sources":["../../../../../src/presentation/FileList/components/FileDropArea/FileDropArea.tsx"],"sourcesContent":["import React from \"react\";\nimport { Button, cn, Heading, Text } from \"@webiny/admin-ui\";\nimport { ReactComponent as UploadFileIcon } from \"@webiny/icons/file_upload.svg\";\nimport { ReactComponent as UploadIcon } from \"@webiny/icons/cloud_upload.svg\";\n\ninterface DropAreaContainerProps {\n empty?: boolean;\n children: React.ReactNode;\n}\n\nconst DropAreaContainer = ({ empty, children }: DropAreaContainerProps) => (\n <div\n className={cn([\n \"w-full h-full p-lg flex items-center justify-center\",\n empty ? \"bg-neutral-base\" : \"pt-xxl bg-neutral-xstrong/20\"\n ])}\n >\n {children}\n </div>\n);\n\ninterface DropAreaBoxProps {\n empty?: boolean;\n children: React.ReactNode;\n}\n\nconst DropAreaBox = ({ children, empty }: DropAreaBoxProps) => (\n <div\n style={{\n width: \"650px\",\n height: \"400px\",\n ...(empty\n ? {\n backgroundImage: `url(\"data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='24' ry='24' stroke='%23E7E7E7FF' stroke-width='4' stroke-dasharray='12' stroke-dashoffset='35' stroke-linecap='square'/%3e%3c/svg%3e\")`\n }\n : {})\n }}\n className={cn([\n \"
|
|
1
|
+
{"version":3,"file":"presentation/FileList/components/FileDropArea/FileDropArea.js","sources":["../../../../../src/presentation/FileList/components/FileDropArea/FileDropArea.tsx"],"sourcesContent":["import React from \"react\";\nimport { Button, cn, Heading, Text } from \"@webiny/admin-ui\";\nimport { ReactComponent as UploadFileIcon } from \"@webiny/icons/file_upload.svg\";\nimport { ReactComponent as UploadIcon } from \"@webiny/icons/cloud_upload.svg\";\n\ninterface DropAreaContainerProps {\n empty?: boolean;\n children: React.ReactNode;\n}\n\nconst DropAreaContainer = ({ empty, children }: DropAreaContainerProps) => (\n <div\n className={cn([\n \"w-full h-full p-lg flex items-center justify-center\",\n empty ? \"bg-neutral-base\" : \"pt-xxl bg-neutral-xstrong/20\"\n ])}\n >\n {children}\n </div>\n);\n\ninterface DropAreaBoxProps {\n empty?: boolean;\n children: React.ReactNode;\n}\n\nconst DropAreaBox = ({ children, empty }: DropAreaBoxProps) => (\n <div\n style={{\n width: \"650px\",\n height: \"400px\",\n ...(empty\n ? {\n backgroundImage: `url(\"data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' fill='none' rx='24' ry='24' stroke='%23E7E7E7FF' stroke-width='4' stroke-dasharray='12' stroke-dashoffset='35' stroke-linecap='square'/%3e%3c/svg%3e\")`\n }\n : {})\n }}\n className={cn([\n \"flex flex-col items-center justify-center gap-lg\",\n \"p-lg rounded-3xl\",\n \"bg-neutral-base\",\n !empty && \"shadow-md\"\n ])}\n >\n {children}\n </div>\n);\n\ninterface DropAreaContentProps {\n empty?: boolean;\n title?: string;\n description?: string;\n}\n\nconst DropAreaContent = ({ title, description }: DropAreaContentProps) => (\n <div className={\"flex flex-col items-center justify-center gap-sm\"}>\n <div className={\"fill-neutral-strong\"}>\n <UploadIcon width={75} height={75} />\n </div>\n <div className={\"text-center\"}>\n {title && (\n <Heading level={4} className={\"text-neutral-strong\"}>\n {title}\n </Heading>\n )}\n {description && (\n <Text as={\"div\"} style={{ width: \"300px\" }} className={\"text-neutral-strong\"}>\n {description}\n </Text>\n )}\n </div>\n </div>\n);\n\ninterface DropAreaButtonProps {\n onClick?: (event?: React.MouseEvent<HTMLElement>) => void;\n}\n\nconst DropAreaButton = ({ onClick }: DropAreaButtonProps) => (\n <div>\n <Button onClick={onClick} text={\"Upload files\"} icon={<UploadFileIcon />} />\n </div>\n);\n\nexport interface FileDropAreaProps {\n empty?: boolean;\n onClick?: (event?: React.MouseEvent<HTMLElement>) => void;\n title?: string;\n description?: string;\n icon?: React.ReactElement;\n}\n\nexport const FileDropArea = ({ title, description, empty, onClick }: FileDropAreaProps) => (\n <DropAreaContainer empty={empty}>\n <DropAreaBox empty={empty}>\n <DropAreaContent title={title} description={description} />\n {empty && <DropAreaButton onClick={onClick} />}\n </DropAreaBox>\n </DropAreaContainer>\n);\n"],"names":["DropAreaContainer","empty","children","cn","DropAreaBox","DropAreaContent","title","description","UploadIcon","Heading","Text","DropAreaButton","onClick","Button","UploadFileIcon","FileDropArea"],"mappings":";;;;AAUA,MAAMA,oBAAoB,CAAC,EAAEC,KAAK,EAAEC,QAAQ,EAA0B,iBAClE,oBAAC;QACG,WAAWC,GAAG;YACV;YACAF,QAAQ,oBAAoB;SAC/B;OAEAC;AAST,MAAME,cAAc,CAAC,EAAEF,QAAQ,EAAED,KAAK,EAAoB,iBACtD,oBAAC;QACG,OAAO;YACH,OAAO;YACP,QAAQ;YACR,GAAIA,QACE;gBACI,iBAAiB;YACrB,IACA,CAAC,CAAC;QACZ;QACA,WAAWE,GAAG;YACV;YACA;YACA;YACA,CAACF,SAAS;SACb;OAEAC;AAUT,MAAMG,kBAAkB,CAAC,EAAEC,KAAK,EAAEC,WAAW,EAAwB,iBACjE,oBAAC;QAAI,WAAW;qBACZ,oBAAC;QAAI,WAAW;qBACZ,oBAACC,iCAAUA;QAAC,OAAO;QAAI,QAAQ;uBAEnC,oBAAC;QAAI,WAAW;OACXF,SAAS,WAATA,GACG,oBAACG,SAAOA;QAAC,OAAO;QAAG,WAAW;OACzBH,QAGRC,eAAe,WAAfA,GACG,oBAACG,MAAIA;QAAC,IAAI;QAAO,OAAO;YAAE,OAAO;QAAQ;QAAG,WAAW;OAClDH;AAWrB,MAAMI,iBAAiB,CAAC,EAAEC,OAAO,EAAuB,iBACpD,oBAAC,2BACG,oBAACC,QAAMA;QAAC,SAASD;QAAS,MAAM;QAAgB,oBAAM,oBAACE,gBAAcA;;AAYtE,MAAMC,eAAe,CAAC,EAAET,KAAK,EAAEC,WAAW,EAAEN,KAAK,EAAEW,OAAO,EAAqB,iBAClF,oBAACZ,mBAAiBA;QAAC,OAAOC;qBACtB,oBAACG,aAAWA;QAAC,OAAOH;qBAChB,oBAACI,iBAAeA;QAAC,OAAOC;QAAO,aAAaC;QAC3CN,SAAS,WAATA,GAAS,oBAACU,gBAAcA;QAAC,SAASC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import react, { useCallback } from "react";
|
|
2
2
|
import { observer } from "mobx-react-lite";
|
|
3
3
|
import react_lazy_load from "react-lazy-load";
|
|
4
|
-
import { CheckboxPrimitive,
|
|
4
|
+
import { CheckboxPrimitive, Text, TimeAgo, cn } from "@webiny/admin-ui";
|
|
5
5
|
import { useOverlay } from "../../../FileManager/OverlayContext.js";
|
|
6
6
|
import { i18n } from "@webiny/app/i18n/index.js";
|
|
7
7
|
import { FolderIcon } from "@webiny/app-aco";
|
|
@@ -136,12 +136,6 @@ const FileGrid_FileGrid = observer(function() {
|
|
|
136
136
|
}, [
|
|
137
137
|
actions.selection
|
|
138
138
|
]);
|
|
139
|
-
if (vm.list.pagination.loading && 0 === vm.list.rows.length) return /*#__PURE__*/ react.createElement("div", {
|
|
140
|
-
className: "relative size-full"
|
|
141
|
-
}, /*#__PURE__*/ react.createElement(OverlayLoader, {
|
|
142
|
-
text: t`Loading files...`,
|
|
143
|
-
size: "lg"
|
|
144
|
-
}));
|
|
145
139
|
return /*#__PURE__*/ react.createElement("div", {
|
|
146
140
|
className: cn([
|
|
147
141
|
"p-lg",
|
|
@@ -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, OverlayLoader } 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 if (vm.list.pagination.loading && vm.list.rows.length === 0) {\n return (\n <div className={\"relative size-full\"}>\n <OverlayLoader text={t`Loading files...`} size={\"lg\"} />\n </div>\n );\n }\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","OverlayLoader"],"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.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,5 +1,5 @@
|
|
|
1
1
|
import react from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { FileManager } from "@webiny/app-admin";
|
|
3
3
|
import { FileManagerView } from "./FileManagerView.js";
|
|
4
4
|
const formatFileItem = (file)=>({
|
|
5
5
|
id: file.id,
|
|
@@ -12,7 +12,7 @@ const formatFileItem = (file)=>({
|
|
|
12
12
|
extensions: file.extensions,
|
|
13
13
|
metadata: file.metadata
|
|
14
14
|
});
|
|
15
|
-
const FileManagerRendererDecorator =
|
|
15
|
+
const FileManagerRendererDecorator = FileManager.Renderer.createDecorator(()=>function(props) {
|
|
16
16
|
const { onChange, onClose, multiple, accept, scope, overlay = true } = props;
|
|
17
17
|
const handleChange = (files)=>{
|
|
18
18
|
if (!onChange || !files.length) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentation/FileManager/FileManagerRenderer.js","sources":["../../../src/presentation/FileManager/FileManagerRenderer.tsx"],"sourcesContent":["import React from \"react\";\nimport type { FileManagerFileItem, FileManagerOnChange } from \"@webiny/app-admin\";\nimport {
|
|
1
|
+
{"version":3,"file":"presentation/FileManager/FileManagerRenderer.js","sources":["../../../src/presentation/FileManager/FileManagerRenderer.tsx"],"sourcesContent":["import React from \"react\";\nimport type { FileManagerFileItem, FileManagerOnChange } from \"@webiny/app-admin\";\nimport { FileManager } from \"@webiny/app-admin\";\nimport type { FmFile } from \"~/features/shared/types.js\";\nimport { FileManagerView } from \"./FileManagerView.js\";\n\nconst formatFileItem = (file: FmFile): FileManagerFileItem => {\n return {\n id: file.id,\n src: file.src,\n name: file.name,\n type: file.type,\n size: file.size,\n width: file.metadata?.image?.width,\n height: file.metadata?.image?.height,\n extensions: file.extensions,\n metadata: file.metadata\n };\n};\n\nexport const FileManagerRendererDecorator = FileManager.Renderer.createDecorator(() => {\n return function FileManagerRendererImpl(props) {\n const { onChange, onClose, multiple, accept, scope, overlay = true } = props;\n\n const handleChange = (files: FmFile[]) => {\n if (!onChange || !files.length) {\n return;\n }\n\n if (multiple) {\n (onChange as FileManagerOnChange<FileManagerFileItem[]>)(files.map(formatFileItem));\n } else {\n (onChange as FileManagerOnChange<FileManagerFileItem>)(formatFileItem(files[0]));\n }\n\n if (onClose) {\n onClose();\n }\n };\n\n return (\n <FileManagerView\n overlay={overlay}\n onChange={handleChange}\n onClose={onClose}\n multiple={multiple}\n accept={accept}\n scope={scope}\n />\n );\n };\n});\n"],"names":["formatFileItem","file","FileManagerRendererDecorator","FileManager","props","onChange","onClose","multiple","accept","scope","overlay","handleChange","files","FileManagerView"],"mappings":";;;AAMA,MAAMA,iBAAiB,CAACC,OACb;QACH,IAAIA,KAAK,EAAE;QACX,KAAKA,KAAK,GAAG;QACb,MAAMA,KAAK,IAAI;QACf,MAAMA,KAAK,IAAI;QACf,MAAMA,KAAK,IAAI;QACf,OAAOA,KAAK,QAAQ,EAAE,OAAO;QAC7B,QAAQA,KAAK,QAAQ,EAAE,OAAO;QAC9B,YAAYA,KAAK,UAAU;QAC3B,UAAUA,KAAK,QAAQ;IAC3B;AAGG,MAAMC,+BAA+BC,YAAY,QAAQ,CAAC,eAAe,CAAC,IACtE,SAAiCC,KAAK;QACzC,MAAM,EAAEC,QAAQ,EAAEC,OAAO,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,KAAK,EAAEC,UAAU,IAAI,EAAE,GAAGN;QAEvE,MAAMO,eAAe,CAACC;YAClB,IAAI,CAACP,YAAY,CAACO,MAAM,MAAM,EAC1B;YAGAL,WACCF,SAAwDO,MAAM,GAAG,CAACZ,mBAElEK,SAAsDL,eAAeY,KAAK,CAAC,EAAE;YAGlF,IAAIN,SACAA;QAER;QAEA,OAAO,WAAP,GACI,oBAACO,iBAAeA;YACZ,SAASH;YACT,UAAUC;YACV,SAASL;YACT,UAAUC;YACV,QAAQC;YACR,OAAOC;;IAGnB"}
|