@maas/payload-plugin-media-cloud 0.0.32 → 0.0.33

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 (58) hide show
  1. package/dist/adapter/handleDelete.mjs.map +1 -1
  2. package/dist/adapter/handleUpload.mjs.map +1 -1
  3. package/dist/adapter/staticHandler.mjs.map +1 -1
  4. package/dist/adapter/storageAdapter.mjs.map +1 -1
  5. package/dist/collectionHooks/afterChange.mjs +1 -1
  6. package/dist/collectionHooks/afterChange.mjs.map +1 -1
  7. package/dist/collectionHooks/beforeChange.mjs.map +1 -1
  8. package/dist/collections/mediaCollection.mjs.map +1 -1
  9. package/dist/components/folderFileCard/folderFileCard.mjs +61 -16
  10. package/dist/components/folderFileCard/folderFileCard.mjs.map +1 -1
  11. package/dist/components/gridContext/gridContext.mjs +30 -30
  12. package/dist/components/gridContext/gridContext.mjs.map +1 -1
  13. package/dist/components/gridView/gridView.mjs +250 -91
  14. package/dist/components/gridView/gridView.mjs.map +1 -1
  15. package/dist/components/itemCardGrid/itemCardGrid.mjs +30 -10
  16. package/dist/components/itemCardGrid/itemCardGrid.mjs.map +1 -1
  17. package/dist/components/muxPreview/muxPreview.mjs +46 -10
  18. package/dist/components/muxPreview/muxPreview.mjs.map +1 -1
  19. package/dist/components/uploadHandler/uploadHandler.mjs.map +1 -1
  20. package/dist/components/uploadManager/uploadManager.mjs +320 -230
  21. package/dist/components/uploadManager/uploadManager.mjs.map +1 -1
  22. package/dist/endpoints/fileExistsHandler.mjs.map +1 -1
  23. package/dist/endpoints/muxAssetHandler.mjs.map +1 -1
  24. package/dist/endpoints/muxCreateUploadHandler.mjs.map +1 -1
  25. package/dist/endpoints/muxWebhookHandler.mjs.map +1 -1
  26. package/dist/endpoints/tusCleanupHandler.mjs.map +1 -1
  27. package/dist/endpoints/tusFolderHandler.mjs.map +1 -1
  28. package/dist/endpoints/tusPostProcessorHandler.mjs.map +1 -1
  29. package/dist/fields/alt.mjs.map +1 -1
  30. package/dist/fields/filename.mjs.map +1 -1
  31. package/dist/fields/height.mjs.map +1 -1
  32. package/dist/fields/mux.mjs.map +1 -1
  33. package/dist/fields/path.mjs.map +1 -1
  34. package/dist/fields/storage.mjs.map +1 -1
  35. package/dist/fields/thumbnail.mjs.map +1 -1
  36. package/dist/fields/width.mjs.map +1 -1
  37. package/dist/hooks/useErrorHandler.mjs +25 -8
  38. package/dist/hooks/useErrorHandler.mjs.map +1 -1
  39. package/dist/hooks/useMediaCloudEmitter.mjs.map +1 -1
  40. package/dist/plugin.mjs.map +1 -1
  41. package/dist/tus/stores/s3/expirationManager.mjs.map +1 -1
  42. package/dist/tus/stores/s3/fileOperations.mjs.map +1 -1
  43. package/dist/tus/stores/s3/index.mjs.map +1 -1
  44. package/dist/tus/stores/s3/metadataManager.mjs.map +1 -1
  45. package/dist/tus/stores/s3/partsManager.mjs.map +1 -1
  46. package/dist/tus/stores/s3/s3Store.mjs +2 -2
  47. package/dist/tus/stores/s3/s3Store.mjs.map +1 -1
  48. package/dist/tus/stores/s3/semaphore.mjs.map +1 -1
  49. package/dist/types/errors.mjs.map +1 -1
  50. package/dist/utils/buildS3Path.mjs.map +1 -1
  51. package/dist/utils/buildThumbnailURL.mjs.map +1 -1
  52. package/dist/utils/defaultOptions.mjs.map +1 -1
  53. package/dist/utils/file.mjs.map +1 -1
  54. package/dist/utils/mux.d.mts +3 -3
  55. package/dist/utils/mux.mjs.map +1 -1
  56. package/dist/utils/tus.d.mts +3 -3
  57. package/dist/utils/tus.mjs.map +1 -1
  58. package/package.json +3 -1
@@ -1 +1 @@
1
- {"version":3,"file":"handleDelete.mjs","names":[],"sources":["../../src/adapter/handleDelete.ts"],"sourcesContent":["import { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nimport type { HandleDelete } from '@payloadcms/plugin-cloud-storage/types'\nimport type { Document } from 'payload'\nimport type { Mux } from '@mux/mux-node'\nimport type { S3Store } from '../tus/stores/s3/s3Store'\n\ninterface HandleDeleteArgs {\n getS3Store: () => S3Store\n getMuxClient: () => Mux\n}\n\ninterface DeleteMuxAssetArgs {\n getMuxClient: () => Mux\n uploadId: string\n}\n\ninterface DeleteS3FileArgs {\n getS3Store: () => S3Store\n filename: string\n}\n\nconst { logError } = useErrorHandler()\n\n/**\n * Deletes a Mux asset by upload ID\n * @param args - The arguments for deleting the Mux asset\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @param args.uploadId - The upload ID of the Mux asset to delete\n * @returns Promise that resolves when the asset is deleted or rejects on error\n */\nasync function deleteMuxAsset(args: DeleteMuxAssetArgs): Promise<void> {\n const { getMuxClient, uploadId } = args\n\n try {\n const mux = getMuxClient()\n const assets = await mux.video.assets.list({ upload_id: uploadId })\n if (assets.data.length > 0) {\n const asset = assets.data[0]\n await mux.video.assets.delete(asset.id)\n }\n } catch (_error) {\n logError(MediaCloudErrors.MUX_ASSET_DELETE_ERROR.message)\n }\n}\n\n/**\n * Deletes a file from S3 storage including its metadata info file\n * @param args - The arguments for deleting the S3 file\n * @param args.getS3Store - Function that returns an S3 client instance\n * @param args.filename - The filename of the file to delete from S3\n * @returns Promise that resolves when the file is deleted or rejects on error\n */\nasync function deleteS3File(args: DeleteS3FileArgs): Promise<void> {\n const { getS3Store, filename } = args\n\n try {\n const { client, bucket } = getS3Store()\n await client.deleteObjects({\n Bucket: bucket,\n Delete: {\n Objects: [{ Key: filename }, { Key: `${filename}.info` }],\n },\n })\n } catch (_error) {\n logError(MediaCloudErrors.S3_DELETE_ERROR.message)\n }\n}\n\n/**\n * Creates a handle delete function for processing file deletions from both Mux and S3 storage\n * @param args - The arguments for creating the delete handler\n * @param args.getS3Store - Function that returns an S3 client instance\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @returns A HandleDelete function that processes deletion requests based on storage type\n */\nexport function getHandleDelete(args: HandleDeleteArgs): HandleDelete {\n const { getS3Store, getMuxClient } = args\n return async ({ doc, req }) => {\n if (req?.method !== 'DELETE' || !doc) {\n return\n }\n\n const media = doc as Document\n\n switch (media?.storage) {\n case 'mux':\n if (media.mux?.uploadId) {\n await deleteMuxAsset({ getMuxClient, uploadId: media.mux.uploadId })\n }\n break\n case 's3':\n if (media.filename) {\n await deleteS3File({ getS3Store, filename: media.filename })\n }\n break\n default:\n logError(MediaCloudErrors.UNKNOWN_STORAGE_TYPE.message)\n }\n }\n}\n"],"mappings":";;;;AAuBA,MAAM,EAAE,aAAa,iBAAiB;;;;;;;;AAStC,eAAe,eAAe,MAAyC;CACrE,MAAM,EAAE,cAAc,aAAa;AAEnC,KAAI;EACF,MAAM,MAAM,cAAc;EAC1B,MAAM,SAAS,MAAM,IAAI,MAAM,OAAO,KAAK,EAAE,WAAW,UAAU,CAAC;AACnE,MAAI,OAAO,KAAK,SAAS,GAAG;GAC1B,MAAM,QAAQ,OAAO,KAAK;AAC1B,SAAM,IAAI,MAAM,OAAO,OAAO,MAAM,GAAG;;UAElC,QAAQ;AACf,WAAS,iBAAiB,uBAAuB,QAAQ;;;;;;;;;;AAW7D,eAAe,aAAa,MAAuC;CACjE,MAAM,EAAE,YAAY,aAAa;AAEjC,KAAI;EACF,MAAM,EAAE,QAAQ,WAAW,YAAY;AACvC,QAAM,OAAO,cAAc;GACzB,QAAQ;GACR,QAAQ,EACN,SAAS,CAAC,EAAE,KAAK,UAAU,EAAE,EAAE,KAAK,GAAG,SAAS,QAAQ,CAAC,EAC1D;GACF,CAAC;UACK,QAAQ;AACf,WAAS,iBAAiB,gBAAgB,QAAQ;;;;;;;;;;AAWtD,SAAgB,gBAAgB,MAAsC;CACpE,MAAM,EAAE,YAAY,iBAAiB;AACrC,QAAO,OAAO,EAAE,KAAK,UAAU;AAC7B,MAAI,KAAK,WAAW,YAAY,CAAC,IAC/B;EAGF,MAAM,QAAQ;AAEd,UAAQ,OAAO,SAAf;GACE,KAAK;AACH,QAAI,MAAM,KAAK,SACb,OAAM,eAAe;KAAE;KAAc,UAAU,MAAM,IAAI;KAAU,CAAC;AAEtE;GACF,KAAK;AACH,QAAI,MAAM,SACR,OAAM,aAAa;KAAE;KAAY,UAAU,MAAM;KAAU,CAAC;AAE9D;GACF,QACE,UAAS,iBAAiB,qBAAqB,QAAQ"}
1
+ {"version":3,"file":"handleDelete.mjs","names":["useErrorHandler","MediaCloudErrors","HandleDelete","Document","Mux","S3Store","HandleDeleteArgs","getS3Store","getMuxClient","DeleteMuxAssetArgs","uploadId","DeleteS3FileArgs","filename","logError","deleteMuxAsset","args","Promise","mux","assets","video","list","upload_id","data","length","asset","delete","id","_error","MUX_ASSET_DELETE_ERROR","message","deleteS3File","client","bucket","deleteObjects","Bucket","Delete","Objects","Key","S3_DELETE_ERROR","getHandleDelete","doc","req","method","media","storage","UNKNOWN_STORAGE_TYPE"],"sources":["../../src/adapter/handleDelete.ts"],"sourcesContent":["import { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\n\nimport type { HandleDelete } from '@payloadcms/plugin-cloud-storage/types'\nimport type { Document } from 'payload'\nimport type { Mux } from '@mux/mux-node'\nimport type { S3Store } from '../tus/stores/s3/s3Store'\n\ninterface HandleDeleteArgs {\n getS3Store: () => S3Store\n getMuxClient: () => Mux\n}\n\ninterface DeleteMuxAssetArgs {\n getMuxClient: () => Mux\n uploadId: string\n}\n\ninterface DeleteS3FileArgs {\n getS3Store: () => S3Store\n filename: string\n}\n\nconst { logError } = useErrorHandler()\n\n/**\n * Deletes a Mux asset by upload ID\n * @param args - The arguments for deleting the Mux asset\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @param args.uploadId - The upload ID of the Mux asset to delete\n * @returns Promise that resolves when the asset is deleted or rejects on error\n */\nasync function deleteMuxAsset(args: DeleteMuxAssetArgs): Promise<void> {\n const { getMuxClient, uploadId } = args\n\n try {\n const mux = getMuxClient()\n const assets = await mux.video.assets.list({ upload_id: uploadId })\n if (assets.data.length > 0) {\n const asset = assets.data[0]\n await mux.video.assets.delete(asset.id)\n }\n } catch (_error) {\n logError(MediaCloudErrors.MUX_ASSET_DELETE_ERROR.message)\n }\n}\n\n/**\n * Deletes a file from S3 storage including its metadata info file\n * @param args - The arguments for deleting the S3 file\n * @param args.getS3Store - Function that returns an S3 client instance\n * @param args.filename - The filename of the file to delete from S3\n * @returns Promise that resolves when the file is deleted or rejects on error\n */\nasync function deleteS3File(args: DeleteS3FileArgs): Promise<void> {\n const { getS3Store, filename } = args\n\n try {\n const { client, bucket } = getS3Store()\n await client.deleteObjects({\n Bucket: bucket,\n Delete: {\n Objects: [{ Key: filename }, { Key: `${filename}.info` }],\n },\n })\n } catch (_error) {\n logError(MediaCloudErrors.S3_DELETE_ERROR.message)\n }\n}\n\n/**\n * Creates a handle delete function for processing file deletions from both Mux and S3 storage\n * @param args - The arguments for creating the delete handler\n * @param args.getS3Store - Function that returns an S3 client instance\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @returns A HandleDelete function that processes deletion requests based on storage type\n */\nexport function getHandleDelete(args: HandleDeleteArgs): HandleDelete {\n const { getS3Store, getMuxClient } = args\n return async ({ doc, req }) => {\n if (req?.method !== 'DELETE' || !doc) {\n return\n }\n\n const media = doc as Document\n\n switch (media?.storage) {\n case 'mux':\n if (media.mux?.uploadId) {\n await deleteMuxAsset({ getMuxClient, uploadId: media.mux.uploadId })\n }\n break\n case 's3':\n if (media.filename) {\n await deleteS3File({ getS3Store, filename: media.filename })\n }\n break\n default:\n logError(MediaCloudErrors.UNKNOWN_STORAGE_TYPE.message)\n }\n }\n}\n"],"mappings":";;;;AAuBA,MAAM,EAAEa,aAAab,iBAAiB;;;;;;;;AAStC,eAAec,eAAeC,MAAyC;CACrE,MAAM,EAAEP,cAAcE,aAAaK;AAEnC,KAAI;EACF,MAAME,MAAMT,cAAc;EAC1B,MAAMU,SAAS,MAAMD,IAAIE,MAAMD,OAAOE,KAAK,EAAEC,WAAWX,UAAU,CAAC;AACnE,MAAIQ,OAAOI,KAAKC,SAAS,GAAG;GAC1B,MAAMC,QAAQN,OAAOI,KAAK;AAC1B,SAAML,IAAIE,MAAMD,OAAOO,OAAOD,MAAME,GAAG;;UAElCC,QAAQ;AACfd,WAASZ,iBAAiB2B,uBAAuBC,QAAQ;;;;;;;;;;AAW7D,eAAeC,aAAaf,MAAuC;CACjE,MAAM,EAAER,YAAYK,aAAaG;AAEjC,KAAI;EACF,MAAM,EAAEgB,QAAQC,WAAWzB,YAAY;AACvC,QAAMwB,OAAOE,cAAc;GACzBC,QAAQF;GACRG,QAAQ,EACNC,SAAS,CAAC,EAAEC,KAAKzB,UAAU,EAAE,EAAEyB,KAAK,GAAGzB,SAAQ,QAAS,CAAA,EAC1D;GACD,CAAC;UACKe,QAAQ;AACfd,WAASZ,iBAAiBqC,gBAAgBT,QAAQ;;;;;;;;;;AAWtD,SAAgBU,gBAAgBxB,MAAsC;CACpE,MAAM,EAAER,YAAYC,iBAAiBO;AACrC,QAAO,OAAO,EAAEyB,KAAKC,UAAU;AAC7B,MAAIA,KAAKC,WAAW,YAAY,CAACF,IAC/B;EAGF,MAAMG,QAAQH;AAEd,UAAQG,OAAOC,SAAf;GACE,KAAK;AACH,QAAID,MAAM1B,KAAKP,SACb,OAAMI,eAAe;KAAEN;KAAcE,UAAUiC,MAAM1B,IAAIP;KAAU,CAAC;AAEtE;GACF,KAAK;AACH,QAAIiC,MAAM/B,SACR,OAAMkB,aAAa;KAAEvB;KAAYK,UAAU+B,MAAM/B;KAAU,CAAC;AAE9D;GACF,QACEC,UAASZ,iBAAiB4C,qBAAqBhB,QAAQ"}
@@ -1 +1 @@
1
- {"version":3,"file":"handleUpload.mjs","names":[],"sources":["../../src/adapter/handleUpload.ts"],"sourcesContent":["import type { HandleUpload } from '@payloadcms/plugin-cloud-storage/types'\n\ninterface ClientUploadContext {\n storage: 'mux' | 's3'\n uploadId?: string\n mimeType?: string\n}\n\n/**\n * Creates a handle upload function for processing file uploads to both Mux and S3 storage\n * @returns A HandleUpload function that processes client upload context and updates document data\n */\nexport function getHandleUpload(): HandleUpload {\n return async function ({ clientUploadContext, data }) {\n if (!clientUploadContext) {\n return data\n }\n\n const ctx = clientUploadContext as ClientUploadContext\n data.mimeType = ctx.mimeType\n\n switch (ctx.storage) {\n case 'mux':\n data.storage = 'mux'\n data.mux = {\n uploadId: ctx.uploadId ?? '',\n status: 'preparing',\n }\n break\n default:\n data.storage = 's3'\n break\n }\n\n return data\n }\n}\n"],"mappings":";;;;;AAYA,SAAgB,kBAAgC;AAC9C,QAAO,eAAgB,EAAE,qBAAqB,QAAQ;AACpD,MAAI,CAAC,oBACH,QAAO;EAGT,MAAM,MAAM;AACZ,OAAK,WAAW,IAAI;AAEpB,UAAQ,IAAI,SAAZ;GACE,KAAK;AACH,SAAK,UAAU;AACf,SAAK,MAAM;KACT,UAAU,IAAI,YAAY;KAC1B,QAAQ;KACT;AACD;GACF;AACE,SAAK,UAAU;AACf;;AAGJ,SAAO"}
1
+ {"version":3,"file":"handleUpload.mjs","names":["HandleUpload","ClientUploadContext","storage","uploadId","mimeType","getHandleUpload","clientUploadContext","data","ctx","mux","status"],"sources":["../../src/adapter/handleUpload.ts"],"sourcesContent":["import type { HandleUpload } from '@payloadcms/plugin-cloud-storage/types'\n\ninterface ClientUploadContext {\n storage: 'mux' | 's3'\n uploadId?: string\n mimeType?: string\n}\n\n/**\n * Creates a handle upload function for processing file uploads to both Mux and S3 storage\n * @returns A HandleUpload function that processes client upload context and updates document data\n */\nexport function getHandleUpload(): HandleUpload {\n return async function ({ clientUploadContext, data }) {\n if (!clientUploadContext) {\n return data\n }\n\n const ctx = clientUploadContext as ClientUploadContext\n data.mimeType = ctx.mimeType\n\n switch (ctx.storage) {\n case 'mux':\n data.storage = 'mux'\n data.mux = {\n uploadId: ctx.uploadId ?? '',\n status: 'preparing',\n }\n break\n default:\n data.storage = 's3'\n break\n }\n\n return data\n }\n}\n"],"mappings":";;;;;AAYA,SAAgBK,kBAAgC;AAC9C,QAAO,eAAgB,EAAEC,qBAAqBC,QAAQ;AACpD,MAAI,CAACD,oBACH,QAAOC;EAGT,MAAMC,MAAMF;AACZC,OAAKH,WAAWI,IAAIJ;AAEpB,UAAQI,IAAIN,SAAZ;GACE,KAAK;AACHK,SAAKL,UAAU;AACfK,SAAKE,MAAM;KACTN,UAAUK,IAAIL,YAAY;KAC1BO,QAAQ;KACT;AACD;GACF;AACEH,SAAKL,UAAU;AACf;;AAGJ,SAAOK"}
@@ -1 +1 @@
1
- {"version":3,"file":"staticHandler.mjs","names":["stream"],"sources":["../../src/adapter/staticHandler.ts"],"sourcesContent":["import { Readable } from 'node:stream'\nimport type { StaticHandler } from '@payloadcms/plugin-cloud-storage/types'\nimport type { Document } from 'payload'\nimport type { S3Store } from '../tus/stores/s3/s3Store'\n\ninterface StaticHandlerArgs {\n getS3Store: () => S3Store\n collection: string\n}\n\ninterface ServeS3FileArgs {\n getS3Store: () => S3Store\n doc: Document\n}\n\ninterface CreateEmptyResponseArgs {\n mimeType?: string\n}\n\n/**\n * Creates a static handler that serves files from S3 or returns empty responses for Mux assets\n * @param args - The arguments for creating the static handler\n * @param args.getS3Store - Function that returns an S3 client instance\n * @param args.collection - The name of the media collection in Payload\n * @returns A StaticHandler function that serves files or empty responses based on storage type\n */\nexport function getStaticHandler(args: StaticHandlerArgs): StaticHandler {\n const { getS3Store, collection } = args\n return async (req, { params }) => {\n const { payload } = req\n const filename = params.filename\n\n const { docs } = await payload.find({\n collection,\n where: { filename: { equals: filename } },\n })\n\n const doc = docs[0] as Document\n\n if (!doc) {\n return createEmptyResponse()\n }\n\n if (doc.storage === 'mux') {\n return createEmptyResponse({ mimeType: doc.mimeType })\n }\n\n return await serveS3File({ getS3Store, doc })\n }\n}\n\n/**\n * Creates an empty response with appropriate headers\n * @param args - Optional arguments for the empty response\n * @param args.mimeType - The MIME type to set in the Content-Type header\n * @returns A Response object with empty content and appropriate headers\n */\nfunction createEmptyResponse(args?: CreateEmptyResponseArgs): Response {\n const { mimeType = 'application/octet-stream' } = args ?? {}\n return new Response(new Uint8Array(0), {\n status: 200,\n headers: {\n 'Content-Type': mimeType,\n 'Content-Length': '0',\n },\n })\n}\n\n/**\n * Serves a file from S3 storage\n * @param args - The arguments for serving the S3 file\n * @param args.getS3Store - Function that returns an S3 client instance\n * @param args.doc - The document containing file metadata\n * @returns A Promise that resolves to a Response object containing the file stream or empty response on error\n */\nasync function serveS3File({\n getS3Store,\n doc,\n}: ServeS3FileArgs): Promise<Response> {\n try {\n const s3Store = getS3Store()\n const stream = await s3Store.read(doc.filename)\n\n if (stream) {\n const webStream = Readable.toWeb(stream) as ReadableStream<Uint8Array>\n return new Response(webStream)\n }\n } catch (_error) {\n // File not found or other error, fall back to empty response\n }\n\n return createEmptyResponse({})\n}\n"],"mappings":";;;;;;;;;;AA0BA,SAAgB,iBAAiB,MAAwC;CACvE,MAAM,EAAE,YAAY,eAAe;AACnC,QAAO,OAAO,KAAK,EAAE,aAAa;EAChC,MAAM,EAAE,YAAY;EACpB,MAAM,WAAW,OAAO;EAExB,MAAM,EAAE,SAAS,MAAM,QAAQ,KAAK;GAClC;GACA,OAAO,EAAE,UAAU,EAAE,QAAQ,UAAU,EAAE;GAC1C,CAAC;EAEF,MAAM,MAAM,KAAK;AAEjB,MAAI,CAAC,IACH,QAAO,qBAAqB;AAG9B,MAAI,IAAI,YAAY,MAClB,QAAO,oBAAoB,EAAE,UAAU,IAAI,UAAU,CAAC;AAGxD,SAAO,MAAM,YAAY;GAAE;GAAY;GAAK,CAAC;;;;;;;;;AAUjD,SAAS,oBAAoB,MAA0C;CACrE,MAAM,EAAE,WAAW,+BAA+B,QAAQ,EAAE;AAC5D,QAAO,IAAI,SAAS,IAAI,WAAW,EAAE,EAAE;EACrC,QAAQ;EACR,SAAS;GACP,gBAAgB;GAChB,kBAAkB;GACnB;EACF,CAAC;;;;;;;;;AAUJ,eAAe,YAAY,EACzB,YACA,OACqC;AACrC,KAAI;EAEF,MAAMA,WAAS,MADC,YAAY,CACC,KAAK,IAAI,SAAS;AAE/C,MAAIA,UAAQ;GACV,MAAM,YAAY,SAAS,MAAMA,SAAO;AACxC,UAAO,IAAI,SAAS,UAAU;;UAEzB,QAAQ;AAIjB,QAAO,oBAAoB,EAAE,CAAC"}
1
+ {"version":3,"file":"staticHandler.mjs","names":["Readable","StaticHandler","Document","S3Store","StaticHandlerArgs","getS3Store","collection","ServeS3FileArgs","doc","CreateEmptyResponseArgs","mimeType","getStaticHandler","args","req","params","payload","filename","docs","find","where","equals","createEmptyResponse","storage","serveS3File","Response","Uint8Array","status","headers","Promise","s3Store","stream","read","webStream","toWeb","ReadableStream","_error"],"sources":["../../src/adapter/staticHandler.ts"],"sourcesContent":["import { Readable } from 'node:stream'\nimport type { StaticHandler } from '@payloadcms/plugin-cloud-storage/types'\nimport type { Document } from 'payload'\nimport type { S3Store } from '../tus/stores/s3/s3Store'\n\ninterface StaticHandlerArgs {\n getS3Store: () => S3Store\n collection: string\n}\n\ninterface ServeS3FileArgs {\n getS3Store: () => S3Store\n doc: Document\n}\n\ninterface CreateEmptyResponseArgs {\n mimeType?: string\n}\n\n/**\n * Creates a static handler that serves files from S3 or returns empty responses for Mux assets\n * @param args - The arguments for creating the static handler\n * @param args.getS3Store - Function that returns an S3 client instance\n * @param args.collection - The name of the media collection in Payload\n * @returns A StaticHandler function that serves files or empty responses based on storage type\n */\nexport function getStaticHandler(args: StaticHandlerArgs): StaticHandler {\n const { getS3Store, collection } = args\n return async (req, { params }) => {\n const { payload } = req\n const filename = params.filename\n\n const { docs } = await payload.find({\n collection,\n where: { filename: { equals: filename } },\n })\n\n const doc = docs[0] as Document\n\n if (!doc) {\n return createEmptyResponse()\n }\n\n if (doc.storage === 'mux') {\n return createEmptyResponse({ mimeType: doc.mimeType })\n }\n\n return await serveS3File({ getS3Store, doc })\n }\n}\n\n/**\n * Creates an empty response with appropriate headers\n * @param args - Optional arguments for the empty response\n * @param args.mimeType - The MIME type to set in the Content-Type header\n * @returns A Response object with empty content and appropriate headers\n */\nfunction createEmptyResponse(args?: CreateEmptyResponseArgs): Response {\n const { mimeType = 'application/octet-stream' } = args ?? {}\n return new Response(new Uint8Array(0), {\n status: 200,\n headers: {\n 'Content-Type': mimeType,\n 'Content-Length': '0',\n },\n })\n}\n\n/**\n * Serves a file from S3 storage\n * @param args - The arguments for serving the S3 file\n * @param args.getS3Store - Function that returns an S3 client instance\n * @param args.doc - The document containing file metadata\n * @returns A Promise that resolves to a Response object containing the file stream or empty response on error\n */\nasync function serveS3File({\n getS3Store,\n doc,\n}: ServeS3FileArgs): Promise<Response> {\n try {\n const s3Store = getS3Store()\n const stream = await s3Store.read(doc.filename)\n\n if (stream) {\n const webStream = Readable.toWeb(stream) as ReadableStream<Uint8Array>\n return new Response(webStream)\n }\n } catch (_error) {\n // File not found or other error, fall back to empty response\n }\n\n return createEmptyResponse({})\n}\n"],"mappings":";;;;;;;;;;AA0BA,SAAgBW,iBAAiBC,MAAwC;CACvE,MAAM,EAAEP,YAAYC,eAAeM;AACnC,QAAO,OAAOC,KAAK,EAAEC,aAAa;EAChC,MAAM,EAAEC,YAAYF;EACpB,MAAMG,WAAWF,OAAOE;EAExB,MAAM,EAAEC,SAAS,MAAMF,QAAQG,KAAK;GAClCZ;GACAa,OAAO,EAAEH,UAAU,EAAEI,QAAQJ,UAAS,EAAE;GACzC,CAAC;EAEF,MAAMR,MAAMS,KAAK;AAEjB,MAAI,CAACT,IACH,QAAOa,qBAAqB;AAG9B,MAAIb,IAAIc,YAAY,MAClB,QAAOD,oBAAoB,EAAEX,UAAUF,IAAIE,UAAU,CAAC;AAGxD,SAAO,MAAMa,YAAY;GAAElB;GAAYG;GAAK,CAAC;;;;;;;;;AAUjD,SAASa,oBAAoBT,MAA0C;CACrE,MAAM,EAAEF,WAAW,+BAA+BE,QAAQ,EAAE;AAC5D,QAAO,IAAIY,SAAS,IAAIC,WAAW,EAAE,EAAE;EACrCC,QAAQ;EACRC,SAAS;GACP,gBAAgBjB;GAChB,kBAAkB;GACpB;EACD,CAAC;;;;;;;;;AAUJ,eAAea,YAAY,EACzBlB,YACAG,OACqC;AACrC,KAAI;EAEF,MAAMsB,WAAS,MADCzB,YAAY,CACC0B,KAAKvB,IAAIQ,SAAS;AAE/C,MAAIc,UAAQ;GACV,MAAME,YAAYhC,SAASiC,MAAMH,SAAO;AACxC,UAAO,IAAIN,SAASQ,UAAU;;UAEzBG,QAAQ;AAIjB,QAAOd,oBAAoB,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"storageAdapter.mjs","names":[],"sources":["../../src/adapter/storageAdapter.ts"],"sourcesContent":["import { getHandleUpload } from './handleUpload'\nimport { getHandleDelete } from './handleDelete'\nimport { getStaticHandler } from './staticHandler'\n\nimport type {\n Adapter,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\nimport type { Mux } from '@mux/mux-node'\nimport type { MediaCloudPluginOptions } from '../types'\nimport type { S3Store } from '../tus/stores/s3/s3Store'\n\ninterface StorageAdapterArgs {\n pluginOptions: MediaCloudPluginOptions\n getS3Store: () => S3Store\n getMuxClient: () => Mux\n}\n\n/**\n * Creates the storage adapter for media cloud plugin\n * @param args - The arguments for creating the storage adapter\n * @param args.pluginOptions - The media cloud plugin options\n * @param args.getS3Store - Function that returns an S3 client instance\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @returns An Adapter function that returns a GeneratedAdapter configuration\n */\nexport function getStorageAdapter(args: StorageAdapterArgs): Adapter {\n const { getS3Store, getMuxClient } = args\n\n return (): GeneratedAdapter => ({\n name: 'media-cloud',\n clientUploads: true,\n handleUpload: getHandleUpload(),\n handleDelete: getHandleDelete({ getS3Store, getMuxClient }),\n staticHandler: getStaticHandler({ getS3Store }),\n })\n}\n"],"mappings":";;;;;;;;;;;;;AA0BA,SAAgB,kBAAkB,MAAmC;CACnE,MAAM,EAAE,YAAY,iBAAiB;AAErC,eAAgC;EAC9B,MAAM;EACN,eAAe;EACf,cAAc,iBAAiB;EAC/B,cAAc,gBAAgB;GAAE;GAAY;GAAc,CAAC;EAC3D,eAAe,iBAAiB,EAAE,YAAY,CAAC;EAChD"}
1
+ {"version":3,"file":"storageAdapter.mjs","names":["getHandleUpload","getHandleDelete","getStaticHandler","Adapter","GeneratedAdapter","Mux","MediaCloudPluginOptions","S3Store","StorageAdapterArgs","pluginOptions","getS3Store","getMuxClient","getStorageAdapter","args","name","clientUploads","handleUpload","handleDelete","staticHandler"],"sources":["../../src/adapter/storageAdapter.ts"],"sourcesContent":["import { getHandleUpload } from './handleUpload'\nimport { getHandleDelete } from './handleDelete'\nimport { getStaticHandler } from './staticHandler'\n\nimport type {\n Adapter,\n GeneratedAdapter,\n} from '@payloadcms/plugin-cloud-storage/types'\nimport type { Mux } from '@mux/mux-node'\nimport type { MediaCloudPluginOptions } from '../types'\nimport type { S3Store } from '../tus/stores/s3/s3Store'\n\ninterface StorageAdapterArgs {\n pluginOptions: MediaCloudPluginOptions\n getS3Store: () => S3Store\n getMuxClient: () => Mux\n}\n\n/**\n * Creates the storage adapter for media cloud plugin\n * @param args - The arguments for creating the storage adapter\n * @param args.pluginOptions - The media cloud plugin options\n * @param args.getS3Store - Function that returns an S3 client instance\n * @param args.getMuxClient - Function that returns a Mux client instance\n * @returns An Adapter function that returns a GeneratedAdapter configuration\n */\nexport function getStorageAdapter(args: StorageAdapterArgs): Adapter {\n const { getS3Store, getMuxClient } = args\n\n return (): GeneratedAdapter => ({\n name: 'media-cloud',\n clientUploads: true,\n handleUpload: getHandleUpload(),\n handleDelete: getHandleDelete({ getS3Store, getMuxClient }),\n staticHandler: getStaticHandler({ getS3Store }),\n })\n}\n"],"mappings":";;;;;;;;;;;;;AA0BA,SAAgBY,kBAAkBC,MAAmC;CACnE,MAAM,EAAEH,YAAYC,iBAAiBE;AAErC,eAAgC;EAC9BC,MAAM;EACNC,eAAe;EACfC,cAAchB,iBAAiB;EAC/BiB,cAAchB,gBAAgB;GAAES;GAAYC;GAAc,CAAC;EAC3DO,eAAehB,iBAAiB,EAAEQ,YAAY,CAAA;EAC/C"}
@@ -22,7 +22,7 @@ const afterChangeHook = async ({ collection, doc, previousDoc, req }) => {
22
22
  const thumbnail = buildThumbnailURL({
23
23
  storage: doc.storage,
24
24
  playbackId: doc.storage === "mux" ? doc.id : void 0,
25
- s3Key: doc.storage === "s3" ? doc.path : void 0,
25
+ s3Key: doc.storage === "s3" ? doc.path ?? doc.filename : void 0,
26
26
  s3Store
27
27
  });
28
28
  req.payload.update({
@@ -1 +1 @@
1
- {"version":3,"file":"afterChange.mjs","names":["afterChangeHook: CollectionAfterChangeHook"],"sources":["../../src/collectionHooks/afterChange.ts"],"sourcesContent":["import { buildThumbnailURL } from '../utils/buildThumbnailURL'\nimport { s3Store } from '../plugin'\nimport { MediaCloudErrors } from '../types/errors'\nimport { useErrorHandler } from '../hooks/useErrorHandler'\n\nimport type { CollectionAfterChangeHook } from 'payload'\n\nexport const afterChangeHook: CollectionAfterChangeHook = async ({\n collection,\n doc,\n previousDoc,\n req,\n}) => {\n const { throwError } = useErrorHandler()\n\n // Move asset in S3 if path has changed\n if (doc.path !== previousDoc?.path) {\n if (doc.storage === 's3' && s3Store) {\n try {\n const oldKey = previousDoc?.path ?? previousDoc?.filename\n const newKey = doc.path\n\n await s3Store.copy(oldKey, newKey)\n } catch (error) {\n throwError({ ...MediaCloudErrors.S3_MOVE_ERROR, cause: error })\n }\n }\n }\n\n // Handle thumbnail\n if (!doc.thumbnail || previousDoc?.path !== doc.path) {\n const thumbnail = buildThumbnailURL({\n storage: doc.storage,\n playbackId: doc.storage === 'mux' ? doc.id : undefined,\n s3Key: doc.storage === 's3' ? doc.path : undefined,\n s3Store,\n })\n\n req.payload.update({\n collection: collection.slug,\n id: doc.id,\n data: { thumbnail },\n })\n }\n\n return doc\n}\n"],"mappings":";;;;;;AAOA,MAAaA,kBAA6C,OAAO,EAC/D,YACA,KACA,aACA,UACI;CACJ,MAAM,EAAE,eAAe,iBAAiB;AAGxC,KAAI,IAAI,SAAS,aAAa,MAC5B;MAAI,IAAI,YAAY,QAAQ,QAC1B,KAAI;GACF,MAAM,SAAS,aAAa,QAAQ,aAAa;GACjD,MAAM,SAAS,IAAI;AAEnB,SAAM,QAAQ,KAAK,QAAQ,OAAO;WAC3B,OAAO;AACd,cAAW;IAAE,GAAG,iBAAiB;IAAe,OAAO;IAAO,CAAC;;;AAMrE,KAAI,CAAC,IAAI,aAAa,aAAa,SAAS,IAAI,MAAM;EACpD,MAAM,YAAY,kBAAkB;GAClC,SAAS,IAAI;GACb,YAAY,IAAI,YAAY,QAAQ,IAAI,KAAK;GAC7C,OAAO,IAAI,YAAY,OAAO,IAAI,OAAO;GACzC;GACD,CAAC;AAEF,MAAI,QAAQ,OAAO;GACjB,YAAY,WAAW;GACvB,IAAI,IAAI;GACR,MAAM,EAAE,WAAW;GACpB,CAAC;;AAGJ,QAAO"}
1
+ {"version":3,"file":"afterChange.mjs","names":["buildThumbnailURL","s3Store","MediaCloudErrors","useErrorHandler","CollectionAfterChangeHook","afterChangeHook","collection","doc","previousDoc","req","throwError","path","storage","oldKey","filename","newKey","copy","error","S3_MOVE_ERROR","cause","thumbnail","playbackId","id","undefined","s3Key","payload","update","slug","data"],"sources":["../../src/collectionHooks/afterChange.ts"],"sourcesContent":["import { buildThumbnailURL } from '../utils/buildThumbnailURL'\nimport { s3Store } from '../plugin'\nimport { MediaCloudErrors } from '../types/errors'\nimport { useErrorHandler } from '../hooks/useErrorHandler'\n\nimport type { CollectionAfterChangeHook } from 'payload'\n\nexport const afterChangeHook: CollectionAfterChangeHook = async ({\n collection,\n doc,\n previousDoc,\n req,\n}) => {\n const { throwError } = useErrorHandler()\n\n // Move asset in S3 if path has changed\n if (doc.path !== previousDoc?.path) {\n if (doc.storage === 's3' && s3Store) {\n try {\n const oldKey = previousDoc?.path ?? previousDoc?.filename\n const newKey = doc.path\n\n await s3Store.copy(oldKey, newKey)\n } catch (error) {\n throwError({ ...MediaCloudErrors.S3_MOVE_ERROR, cause: error })\n }\n }\n }\n\n // Handle thumbnail\n if (!doc.thumbnail || previousDoc?.path !== doc.path) {\n const thumbnail = buildThumbnailURL({\n storage: doc.storage,\n playbackId: doc.storage === 'mux' ? doc.id : undefined,\n s3Key: doc.storage === 's3' ? (doc.path ?? doc.filename) : undefined,\n s3Store,\n })\n\n req.payload.update({\n collection: collection.slug,\n id: doc.id,\n data: { thumbnail },\n })\n }\n\n return doc\n}\n"],"mappings":";;;;;;AAOA,MAAaK,kBAA6C,OAAO,EAC/DC,YACAC,KACAC,aACAC,UACI;CACJ,MAAM,EAAEC,eAAeP,iBAAiB;AAGxC,KAAII,IAAII,SAASH,aAAaG,MAC5B;MAAIJ,IAAIK,YAAY,QAAQX,QAC1B,KAAI;GACF,MAAMY,SAASL,aAAaG,QAAQH,aAAaM;GACjD,MAAMC,SAASR,IAAII;AAEnB,SAAMV,QAAQe,KAAKH,QAAQE,OAAO;WAC3BE,OAAO;AACdP,cAAW;IAAE,GAAGR,iBAAiBgB;IAAeC,OAAOF;IAAO,CAAC;;;AAMrE,KAAI,CAACV,IAAIa,aAAaZ,aAAaG,SAASJ,IAAII,MAAM;EACpD,MAAMS,YAAYpB,kBAAkB;GAClCY,SAASL,IAAIK;GACbS,YAAYd,IAAIK,YAAY,QAAQL,IAAIe,KAAKC;GAC7CC,OAAOjB,IAAIK,YAAY,OAAQL,IAAII,QAAQJ,IAAIO,WAAYS;GAC3DtB;GACD,CAAC;AAEFQ,MAAIgB,QAAQC,OAAO;GACjBpB,YAAYA,WAAWqB;GACvBL,IAAIf,IAAIe;GACRM,MAAM,EAAER,WAAU;GACnB,CAAC;;AAGJ,QAAOb"}
@@ -1 +1 @@
1
- {"version":3,"file":"beforeChange.mjs","names":["beforeChangeHook: CollectionBeforeChangeHook"],"sources":["../../src/collectionHooks/beforeChange.ts"],"sourcesContent":["import { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\nimport { buildS3Path } from '../utils/buildS3Path'\nimport type { CollectionBeforeChangeHook } from 'payload'\n\nexport const beforeChangeHook: CollectionBeforeChangeHook = async ({\n data,\n originalDoc,\n req,\n}) => {\n const { throwError } = useErrorHandler()\n // Update path\n if (\n (data.filename && data.filename !== originalDoc?.filename) ||\n (data.folder && data.folder !== originalDoc?.folder) ||\n !data.folder\n ) {\n let prefix = ''\n\n if (data.folder) {\n try {\n const folder = await req.payload.findByID({\n id: data.folder,\n collection: 'payload-folders',\n select: { name: true, folder: true },\n })\n\n if (folder) {\n prefix = buildS3Path(folder)\n }\n } catch (error) {\n throwError({ ...MediaCloudErrors.FOLDER_FETCH_ERROR, cause: error })\n }\n }\n\n // Save path\n data.path = prefix ? `${prefix}/${data.filename}` : data.filename\n }\n\n return data\n}\n"],"mappings":";;;;;AAKA,MAAaA,mBAA+C,OAAO,EACjE,MACA,aACA,UACI;CACJ,MAAM,EAAE,eAAe,iBAAiB;AAExC,KACG,KAAK,YAAY,KAAK,aAAa,aAAa,YAChD,KAAK,UAAU,KAAK,WAAW,aAAa,UAC7C,CAAC,KAAK,QACN;EACA,IAAI,SAAS;AAEb,MAAI,KAAK,OACP,KAAI;GACF,MAAM,SAAS,MAAM,IAAI,QAAQ,SAAS;IACxC,IAAI,KAAK;IACT,YAAY;IACZ,QAAQ;KAAE,MAAM;KAAM,QAAQ;KAAM;IACrC,CAAC;AAEF,OAAI,OACF,UAAS,YAAY,OAAO;WAEvB,OAAO;AACd,cAAW;IAAE,GAAG,iBAAiB;IAAoB,OAAO;IAAO,CAAC;;AAKxE,OAAK,OAAO,SAAS,GAAG,OAAO,GAAG,KAAK,aAAa,KAAK;;AAG3D,QAAO"}
1
+ {"version":3,"file":"beforeChange.mjs","names":["useErrorHandler","MediaCloudErrors","buildS3Path","CollectionBeforeChangeHook","beforeChangeHook","data","originalDoc","req","throwError","filename","folder","prefix","payload","findByID","id","collection","select","name","error","FOLDER_FETCH_ERROR","cause","path"],"sources":["../../src/collectionHooks/beforeChange.ts"],"sourcesContent":["import { useErrorHandler } from '../hooks/useErrorHandler'\nimport { MediaCloudErrors } from '../types/errors'\nimport { buildS3Path } from '../utils/buildS3Path'\nimport type { CollectionBeforeChangeHook } from 'payload'\n\nexport const beforeChangeHook: CollectionBeforeChangeHook = async ({\n data,\n originalDoc,\n req,\n}) => {\n const { throwError } = useErrorHandler()\n // Update path\n if (\n (data.filename && data.filename !== originalDoc?.filename) ||\n (data.folder && data.folder !== originalDoc?.folder) ||\n !data.folder\n ) {\n let prefix = ''\n\n if (data.folder) {\n try {\n const folder = await req.payload.findByID({\n id: data.folder,\n collection: 'payload-folders',\n select: { name: true, folder: true },\n })\n\n if (folder) {\n prefix = buildS3Path(folder)\n }\n } catch (error) {\n throwError({ ...MediaCloudErrors.FOLDER_FETCH_ERROR, cause: error })\n }\n }\n\n // Save path\n data.path = prefix ? `${prefix}/${data.filename}` : data.filename\n }\n\n return data\n}\n"],"mappings":";;;;;AAKA,MAAaI,mBAA+C,OAAO,EACjEC,MACAC,aACAC,UACI;CACJ,MAAM,EAAEC,eAAeR,iBAAiB;AAExC,KACGK,KAAKI,YAAYJ,KAAKI,aAAaH,aAAaG,YAChDJ,KAAKK,UAAUL,KAAKK,WAAWJ,aAAaI,UAC7C,CAACL,KAAKK,QACN;EACA,IAAIC,SAAS;AAEb,MAAIN,KAAKK,OACP,KAAI;GACF,MAAMA,SAAS,MAAMH,IAAIK,QAAQC,SAAS;IACxCC,IAAIT,KAAKK;IACTK,YAAY;IACZC,QAAQ;KAAEC,MAAM;KAAMP,QAAQ;KAAK;IACpC,CAAC;AAEF,OAAIA,OACFC,UAAST,YAAYQ,OAAO;WAEvBQ,OAAO;AACdV,cAAW;IAAE,GAAGP,iBAAiBkB;IAAoBC,OAAOF;IAAO,CAAC;;AAKxEb,OAAKgB,OAAOV,SAAS,GAAGA,OAAM,GAAIN,KAAKI,aAAaJ,KAAKI;;AAG3D,QAAOJ"}
@@ -1 +1 @@
1
- {"version":3,"file":"mediaCollection.mjs","names":["config: CollectionConfig"],"sources":["../../src/collections/mediaCollection.ts"],"sourcesContent":["import { thumbnailField } from '../fields/thumbnail'\nimport { pathField } from '../fields/path'\nimport { filenameField } from '../fields/filename'\nimport { altField } from '../fields/alt'\nimport { widthField } from '../fields/width'\nimport { heightField } from '../fields/height'\nimport { storageField } from '../fields/storage'\nimport { muxField } from '../fields/mux'\n\nimport { beforeChangeHook } from '../collectionHooks/beforeChange'\nimport { afterChangeHook } from '../collectionHooks/afterChange'\n\nimport type { CollectionConfig } from 'payload'\nimport type { Document } from 'payload'\nimport type { MediaCloudPluginOptions } from '../types/index'\n\ninterface GetMediaCollectionArgs {\n view: MediaCloudPluginOptions['view']\n folders: MediaCloudPluginOptions['folders']\n baseCollection?: CollectionConfig\n}\n\n/**\n * Creates a media collection configuration for Payload CMS\n * @param args - Arguments including the S3Store instance\n * @returns A configured Payload collection for media files\n */\nexport function getMediaCollection(\n args: GetMediaCollectionArgs\n): CollectionConfig {\n const { baseCollection, view, folders } = args\n\n // Override list view to use grid view if specified\n const components =\n view === 'grid'\n ? {\n views: {\n list: {\n Component: '@maas/payload-plugin-media-cloud/components#GridView',\n },\n },\n }\n : undefined\n\n // Add hook, if folders are enabled\n // to handle thumbnails and S3 prefixing\n const afterChange = folders ? [afterChangeHook] : []\n const beforeChange = folders ? [beforeChangeHook] : []\n\n const config: CollectionConfig = {\n slug: 'media',\n custom: {\n storage: 'test',\n },\n access: {\n read: () => true,\n delete: () => true,\n },\n admin: {\n components,\n group: 'Media Cloud',\n pagination: {\n defaultLimit: 50,\n },\n },\n upload: {\n crop: false,\n displayPreview: true,\n hideRemoveFile: true,\n adminThumbnail({ doc }: { doc: Document }) {\n return doc.thumbnail ?? null\n },\n },\n fields: [\n thumbnailField,\n pathField,\n altField,\n {\n type: 'row',\n fields: [widthField, heightField],\n },\n storageField,\n muxField,\n ],\n hooks: {\n beforeChange,\n afterChange,\n },\n }\n\n if (!baseCollection) {\n return config\n }\n\n return {\n ...baseCollection,\n slug: baseCollection.slug ?? config.slug,\n admin: {\n ...(config.admin ?? {}),\n ...(baseCollection.admin ?? {}),\n },\n access: {\n ...(config.access ?? {}),\n ...(baseCollection.access ?? {}),\n },\n upload: config.upload,\n fields: [...(baseCollection.fields ?? []), ...config.fields, filenameField],\n hooks: {\n ...baseCollection.hooks,\n beforeChange: [\n ...(baseCollection.hooks?.beforeChange ?? []),\n ...(config.hooks?.beforeChange ?? []),\n ],\n afterChange: [\n ...(baseCollection.hooks?.afterChange ?? []),\n ...(config.hooks?.afterChange ?? []),\n ],\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA2BA,SAAgB,mBACd,MACkB;CAClB,MAAM,EAAE,gBAAgB,MAAM,YAAY;CAmB1C,MAAMA,SAA2B;EAC/B,MAAM;EACN,QAAQ,EACN,SAAS,QACV;EACD,QAAQ;GACN,YAAY;GACZ,cAAc;GACf;EACD,OAAO;GACL,YAzBF,SAAS,SACL,EACE,OAAO,EACL,MAAM,EACJ,WAAW,wDACZ,EACF,EACF,GACD;GAkBF,OAAO;GACP,YAAY,EACV,cAAc,IACf;GACF;EACD,QAAQ;GACN,MAAM;GACN,gBAAgB;GAChB,gBAAgB;GAChB,eAAe,EAAE,OAA0B;AACzC,WAAO,IAAI,aAAa;;GAE3B;EACD,QAAQ;GACN;GACA;GACA;GACA;IACE,MAAM;IACN,QAAQ,CAAC,YAAY,YAAY;IAClC;GACD;GACA;GACD;EACD,OAAO;GACL,cAtCiB,UAAU,CAAC,iBAAiB,GAAG,EAAE;GAuClD,aAxCgB,UAAU,CAAC,gBAAgB,GAAG,EAAE;GAyCjD;EACF;AAED,KAAI,CAAC,eACH,QAAO;AAGT,QAAO;EACL,GAAG;EACH,MAAM,eAAe,QAAQ,OAAO;EACpC,OAAO;GACL,GAAI,OAAO,SAAS,EAAE;GACtB,GAAI,eAAe,SAAS,EAAE;GAC/B;EACD,QAAQ;GACN,GAAI,OAAO,UAAU,EAAE;GACvB,GAAI,eAAe,UAAU,EAAE;GAChC;EACD,QAAQ,OAAO;EACf,QAAQ;GAAC,GAAI,eAAe,UAAU,EAAE;GAAG,GAAG,OAAO;GAAQ;GAAc;EAC3E,OAAO;GACL,GAAG,eAAe;GAClB,cAAc,CACZ,GAAI,eAAe,OAAO,gBAAgB,EAAE,EAC5C,GAAI,OAAO,OAAO,gBAAgB,EAAE,CACrC;GACD,aAAa,CACX,GAAI,eAAe,OAAO,eAAe,EAAE,EAC3C,GAAI,OAAO,OAAO,eAAe,EAAE,CACpC;GACF;EACF"}
1
+ {"version":3,"file":"mediaCollection.mjs","names":["thumbnailField","pathField","filenameField","altField","widthField","heightField","storageField","muxField","beforeChangeHook","afterChangeHook","CollectionConfig","Document","MediaCloudPluginOptions","GetMediaCollectionArgs","view","folders","baseCollection","getMediaCollection","args","components","views","list","Component","undefined","afterChange","beforeChange","config","slug","custom","storage","access","read","delete","admin","group","pagination","defaultLimit","upload","crop","displayPreview","hideRemoveFile","adminThumbnail","doc","thumbnail","fields","type","hooks"],"sources":["../../src/collections/mediaCollection.ts"],"sourcesContent":["import { thumbnailField } from '../fields/thumbnail'\nimport { pathField } from '../fields/path'\nimport { filenameField } from '../fields/filename'\nimport { altField } from '../fields/alt'\nimport { widthField } from '../fields/width'\nimport { heightField } from '../fields/height'\nimport { storageField } from '../fields/storage'\nimport { muxField } from '../fields/mux'\n\nimport { beforeChangeHook } from '../collectionHooks/beforeChange'\nimport { afterChangeHook } from '../collectionHooks/afterChange'\n\nimport type { CollectionConfig } from 'payload'\nimport type { Document } from 'payload'\nimport type { MediaCloudPluginOptions } from '../types/index'\n\ninterface GetMediaCollectionArgs {\n view: MediaCloudPluginOptions['view']\n folders: MediaCloudPluginOptions['folders']\n baseCollection?: CollectionConfig\n}\n\n/**\n * Creates a media collection configuration for Payload CMS\n * @param args - Arguments including the S3Store instance\n * @returns A configured Payload collection for media files\n */\nexport function getMediaCollection(\n args: GetMediaCollectionArgs\n): CollectionConfig {\n const { baseCollection, view, folders } = args\n\n // Override list view to use grid view if specified\n const components =\n view === 'grid'\n ? {\n views: {\n list: {\n Component: '@maas/payload-plugin-media-cloud/components#GridView',\n },\n },\n }\n : undefined\n\n // Add hook, if folders are enabled\n // to handle thumbnails and S3 prefixing\n const afterChange = folders ? [afterChangeHook] : []\n const beforeChange = folders ? [beforeChangeHook] : []\n\n const config: CollectionConfig = {\n slug: 'media',\n custom: {\n storage: 'test',\n },\n access: {\n read: () => true,\n delete: () => true,\n },\n admin: {\n components,\n group: 'Media Cloud',\n pagination: {\n defaultLimit: 50,\n },\n },\n upload: {\n crop: false,\n displayPreview: true,\n hideRemoveFile: true,\n adminThumbnail({ doc }: { doc: Document }) {\n return doc.thumbnail ?? null\n },\n },\n fields: [\n thumbnailField,\n pathField,\n altField,\n {\n type: 'row',\n fields: [widthField, heightField],\n },\n storageField,\n muxField,\n ],\n hooks: {\n beforeChange,\n afterChange,\n },\n }\n\n if (!baseCollection) {\n return config\n }\n\n return {\n ...baseCollection,\n slug: baseCollection.slug ?? config.slug,\n admin: {\n ...(config.admin ?? {}),\n ...(baseCollection.admin ?? {}),\n },\n access: {\n ...(config.access ?? {}),\n ...(baseCollection.access ?? {}),\n },\n upload: config.upload,\n fields: [...(baseCollection.fields ?? []), ...config.fields, filenameField],\n hooks: {\n ...baseCollection.hooks,\n beforeChange: [\n ...(baseCollection.hooks?.beforeChange ?? []),\n ...(config.hooks?.beforeChange ?? []),\n ],\n afterChange: [\n ...(baseCollection.hooks?.afterChange ?? []),\n ...(config.hooks?.afterChange ?? []),\n ],\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA2BA,SAAgBiB,mBACdC,MACkB;CAClB,MAAM,EAAEF,gBAAgBF,MAAMC,YAAYG;CAmB1C,MAAMQ,SAA2B;EAC/BC,MAAM;EACNC,QAAQ,EACNC,SAAS,QACV;EACDC,QAAQ;GACNC,YAAY;GACZC,cAAc;GACf;EACDC,OAAO;GACLd,YAzBFL,SAAS,SACL,EACEM,OAAO,EACLC,MAAM,EACJC,WAAW,wDACb,EACF,EACD,GACDC;GAkBFW,OAAO;GACPC,YAAY,EACVC,cAAc,IAChB;GACD;EACDC,QAAQ;GACNC,MAAM;GACNC,gBAAgB;GAChBC,gBAAgB;GAChBC,eAAe,EAAEC,OAA0B;AACzC,WAAOA,IAAIC,aAAa;;GAE3B;EACDC,QAAQ;GACN5C;GACAC;GACAE;GACA;IACE0C,MAAM;IACND,QAAQ,CAACxC,YAAYC,YAAW;IACjC;GACDC;GACAC;GACD;EACDuC,OAAO;GACLrB,cAtCiBV,UAAU,CAACP,iBAAiB,GAAG,EAAE;GAuClDgB,aAxCgBT,UAAU,CAACN,gBAAgB,GAAG,EAAE;GAyClD;EACD;AAED,KAAI,CAACO,eACH,QAAOU;AAGT,QAAO;EACL,GAAGV;EACHW,MAAMX,eAAeW,QAAQD,OAAOC;EACpCM,OAAO;GACL,GAAIP,OAAOO,SAAS,EAAE;GACtB,GAAIjB,eAAeiB,SAAS,EAAE;GAC/B;EACDH,QAAQ;GACN,GAAIJ,OAAOI,UAAU,EAAE;GACvB,GAAId,eAAec,UAAU,EAAE;GAChC;EACDO,QAAQX,OAAOW;EACfO,QAAQ;GAAC,GAAI5B,eAAe4B,UAAU,EAAE;GAAG,GAAGlB,OAAOkB;GAAQ1C;GAAc;EAC3E4C,OAAO;GACL,GAAG9B,eAAe8B;GAClBrB,cAAc,CACZ,GAAIT,eAAe8B,OAAOrB,gBAAgB,EAAE,EAC5C,GAAIC,OAAOoB,OAAOrB,gBAAgB,EAAE,CACrC;GACDD,aAAa,CACX,GAAIR,eAAe8B,OAAOtB,eAAe,EAAE,EAC3C,GAAIE,OAAOoB,OAAOtB,eAAe,EAAE,CAAC;GAExC;EACD"}
@@ -1,28 +1,73 @@
1
1
  import { useGrid } from "../gridContext/gridContext.mjs";
2
- import { useMemo } from "react";
2
+ import { c } from "react/compiler-runtime";
3
3
  import { useSelection } from "@payloadcms/ui";
4
4
  import { FolderFileCard } from "@payloadcms/ui/elements/FolderView/FolderFileCard";
5
5
 
6
6
  //#region src/components/folderFileCard/folderFileCard.tsx
7
7
  function ContextFolderFileCard(props) {
8
+ const $ = c(23);
8
9
  const { className, index, item } = props;
9
10
  const { focusedItemIndex, onItemClick, onItemKeyPress } = useGrid();
10
11
  const { selectedIDs } = useSelection();
11
- const isSelected = useMemo(() => selectedIDs.includes(item.id), [selectedIDs, item.id]);
12
- const isFocused = useMemo(() => focusedItemIndex === index, [focusedItemIndex, index]);
13
- return <FolderFileCard className={className} id={item.id} isFocused={isFocused} isSelected={isSelected} itemKey={item.id.toString()} previewUrl={item.thumbnailURL} title={item.title} type={"file"} onClick={(event) => {
14
- onItemClick({
15
- event,
16
- index,
17
- item
18
- });
19
- }} onKeyDown={(event) => {
20
- onItemKeyPress({
21
- event,
22
- index,
23
- item
24
- });
25
- }} />;
12
+ let t0;
13
+ if ($[0] !== item.id || $[1] !== selectedIDs) {
14
+ t0 = selectedIDs.includes(item.id);
15
+ $[0] = item.id;
16
+ $[1] = selectedIDs;
17
+ $[2] = t0;
18
+ } else t0 = $[2];
19
+ const isSelected = t0;
20
+ const isFocused = focusedItemIndex === index;
21
+ const t1 = item.id;
22
+ let t2;
23
+ if ($[3] !== item.id) {
24
+ t2 = item.id.toString();
25
+ $[3] = item.id;
26
+ $[4] = t2;
27
+ } else t2 = $[4];
28
+ let t3;
29
+ if ($[5] !== index || $[6] !== item || $[7] !== onItemClick) {
30
+ t3 = (event) => {
31
+ onItemClick({
32
+ event,
33
+ index,
34
+ item
35
+ });
36
+ };
37
+ $[5] = index;
38
+ $[6] = item;
39
+ $[7] = onItemClick;
40
+ $[8] = t3;
41
+ } else t3 = $[8];
42
+ let t4;
43
+ if ($[9] !== index || $[10] !== item || $[11] !== onItemKeyPress) {
44
+ t4 = (event_0) => {
45
+ onItemKeyPress({
46
+ event: event_0,
47
+ index,
48
+ item
49
+ });
50
+ };
51
+ $[9] = index;
52
+ $[10] = item;
53
+ $[11] = onItemKeyPress;
54
+ $[12] = t4;
55
+ } else t4 = $[12];
56
+ let t5;
57
+ if ($[13] !== className || $[14] !== isFocused || $[15] !== isSelected || $[16] !== item.id || $[17] !== item.thumbnailURL || $[18] !== item.title || $[19] !== t2 || $[20] !== t3 || $[21] !== t4) {
58
+ t5 = <FolderFileCard className={className} id={t1} isFocused={isFocused} isSelected={isSelected} itemKey={t2} previewUrl={item.thumbnailURL} title={item.title} type="file" onClick={t3} onKeyDown={t4} />;
59
+ $[13] = className;
60
+ $[14] = isFocused;
61
+ $[15] = isSelected;
62
+ $[16] = item.id;
63
+ $[17] = item.thumbnailURL;
64
+ $[18] = item.title;
65
+ $[19] = t2;
66
+ $[20] = t3;
67
+ $[21] = t4;
68
+ $[22] = t5;
69
+ } else t5 = $[22];
70
+ return t5;
26
71
  }
27
72
 
28
73
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"folderFileCard.mjs","names":[],"sources":["../../../src/components/folderFileCard/folderFileCard.tsx"],"sourcesContent":["import { useMemo } from 'react'\nimport { FolderFileCard } from '@payloadcms/ui/elements/FolderView/FolderFileCard'\nimport { useSelection } from '@payloadcms/ui'\nimport { useGrid } from '../gridContext/gridContext'\n\nimport type { MappedDocument } from '../gridView/gridView'\n\ntype ContextCardProps = {\n readonly className?: string\n readonly index: number\n readonly item: MappedDocument\n}\n\nexport function ContextFolderFileCard(props: ContextCardProps) {\n const { className, index, item } = props\n\n const { focusedItemIndex, onItemClick, onItemKeyPress } = useGrid()\n const { selectedIDs } = useSelection()\n\n const isSelected = useMemo(\n () => selectedIDs.includes(item.id),\n [selectedIDs, item.id]\n )\n\n const isFocused = useMemo(\n () => focusedItemIndex === index,\n [focusedItemIndex, index]\n )\n\n return (\n <FolderFileCard\n className={className}\n id={item.id}\n isFocused={isFocused}\n isSelected={isSelected}\n itemKey={item.id.toString()}\n previewUrl={item.thumbnailURL}\n title={item.title}\n type={'file'}\n onClick={(event) => {\n void onItemClick({ event, index, item })\n }}\n onKeyDown={(event) => {\n void onItemKeyPress({ event, index, item })\n }}\n />\n )\n}\n"],"mappings":";;;;;;AAaA,SAAgB,sBAAsB,OAAyB;CAC7D,MAAM,EAAE,WAAW,OAAO,SAAS;CAEnC,MAAM,EAAE,kBAAkB,aAAa,mBAAmB,SAAS;CACnE,MAAM,EAAE,gBAAgB,cAAc;CAEtC,MAAM,aAAa,cACX,YAAY,SAAS,KAAK,GAAG,EACnC,CAAC,aAAa,KAAK,GAAG,CACvB;CAED,MAAM,YAAY,cACV,qBAAqB,OAC3B,CAAC,kBAAkB,MAAM,CAC1B;AAED,QACE,CAAC,eACC,WAAW,WACX,IAAI,KAAK,IACT,WAAW,WACX,YAAY,YACZ,SAAS,KAAK,GAAG,UAAU,EAC3B,YAAY,KAAK,cACjB,OAAO,KAAK,OACZ,MAAM,QACN,UAAU,UAAU;AAClB,EAAK,YAAY;GAAE;GAAO;GAAO;GAAM,CAAC;IAE1C,YAAY,UAAU;AACpB,EAAK,eAAe;GAAE;GAAO;GAAO;GAAM,CAAC"}
1
+ {"version":3,"file":"folderFileCard.mjs","names":["useMemo","FolderFileCard","useSelection","useGrid","MappedDocument","ContextCardProps","className","index","item","ContextFolderFileCard","props","$","_c","focusedItemIndex","onItemClick","onItemKeyPress","selectedIDs","t0","id","includes","isSelected","isFocused","t1","t2","toString","t3","event","t4","event_0","t5","thumbnailURL","title"],"sources":["../../../src/components/folderFileCard/folderFileCard.tsx"],"sourcesContent":["import { useMemo } from 'react'\nimport { FolderFileCard } from '@payloadcms/ui/elements/FolderView/FolderFileCard'\nimport { useSelection } from '@payloadcms/ui'\nimport { useGrid } from '../gridContext/gridContext'\n\nimport type { MappedDocument } from '../gridView/gridView'\n\ntype ContextCardProps = {\n readonly className?: string\n readonly index: number\n readonly item: MappedDocument\n}\n\nexport function ContextFolderFileCard(props: ContextCardProps) {\n const { className, index, item } = props\n\n const { focusedItemIndex, onItemClick, onItemKeyPress } = useGrid()\n const { selectedIDs } = useSelection()\n\n const isSelected = useMemo(\n () => selectedIDs.includes(item.id),\n [selectedIDs, item.id]\n )\n\n const isFocused = useMemo(\n () => focusedItemIndex === index,\n [focusedItemIndex, index]\n )\n\n return (\n <FolderFileCard\n className={className}\n id={item.id}\n isFocused={isFocused}\n isSelected={isSelected}\n itemKey={item.id.toString()}\n previewUrl={item.thumbnailURL}\n title={item.title}\n type={'file'}\n onClick={(event) => {\n void onItemClick({ event, index, item })\n }}\n onKeyDown={(event) => {\n void onItemKeyPress({ event, index, item })\n }}\n />\n )\n}\n"],"mappings":";;;;;;AAaA,SAAOS,sBAAAC,OAAA;CAAA,MAAAC,IAAAC,EAAA,GAAA;CACL,MAAA,EAAAN,WAAAC,OAAAC,SAAmCE;CAEnC,MAAA,EAAAG,kBAAAC,aAAAC,mBAA0DZ,SAAS;CACnE,MAAA,EAAAa,gBAAwBd,cAAc;CAAA,IAAAe;AAAA,KAAAN,EAAA,OAAAH,KAAAU,MAAAP,EAAA,OAAAK,aAAA;AAG9BC,OAAAD,YAAWG,SAAUX,KAAIU,GAAI;AAAAP,IAAA,KAAAH,KAAAU;AAAAP,IAAA,KAAAK;AAAAL,IAAA,KAAAM;OAAAA,MAAAN,EAAA;CADrC,MAAAS,aACQH;CAIR,MAAAI,YACQR,qBAAqBN;CAOrB,MAAAe,KAAAd,KAAIU;CAAG,IAAAK;AAAA,KAAAZ,EAAA,OAAAH,KAAAU,IAAA;AAGFK,OAAAf,KAAIU,GAAGM,UAAW;AAAAb,IAAA,KAAAH,KAAAU;AAAAP,IAAA,KAAAY;OAAAA,MAAAZ,EAAA;CAAA,IAAAc;AAAA,KAAAd,EAAA,OAAAJ,SAAAI,EAAA,OAAAH,QAAAG,EAAA,OAAAG,aAAA;AAIlBW,QAAAC,UAAA;AACFZ,eAAY;IAAAY;IAAAnB;IAAAC;IAAsB,CAAC;;AACzCG,IAAA,KAAAJ;AAAAI,IAAA,KAAAH;AAAAG,IAAA,KAAAG;AAAAH,IAAA,KAAAc;OAAAA,MAAAd,EAAA;CAAA,IAAAgB;AAAA,KAAAhB,EAAA,OAAAJ,SAAAI,EAAA,QAAAH,QAAAG,EAAA,QAAAI,gBAAA;AACUY,QAAAC,YAAA;AACJb,kBAAe;IAAAW,OAAEA;IAAKnB;IAAAC;IAAe,CAAC;;AAC5CG,IAAA,KAAAJ;AAAAI,IAAA,MAAAH;AAAAG,IAAA,MAAAI;AAAAJ,IAAA,MAAAgB;OAAAA,MAAAhB,EAAA;CAAA,IAAAkB;AAAA,KAAAlB,EAAA,QAAAL,aAAAK,EAAA,QAAAU,aAAAV,EAAA,QAAAS,cAAAT,EAAA,QAAAH,KAAAU,MAAAP,EAAA,QAAAH,KAAAsB,gBAAAnB,EAAA,QAAAH,KAAAuB,SAAApB,EAAA,QAAAY,MAAAZ,EAAA,QAAAc,MAAAd,EAAA,QAAAgB,IAAA;AAdHE,OAAA,CAAC,eACYvB,WAAAA,WACP,IAAAgB,IACOD,WAAAA,WACCD,YAAAA,YACH,SAAAG,IACG,YAAAf,KAAIsB,cACT,OAAAtB,KAAIuB,OACL,YACG,SAAAN,IAGE,WAAAE;AAGXhB,IAAA,MAAAL;AAAAK,IAAA,MAAAU;AAAAV,IAAA,MAAAS;AAAAT,IAAA,MAAAH,KAAAU;AAAAP,IAAA,MAAAH,KAAAsB;AAAAnB,IAAA,MAAAH,KAAAuB;AAAApB,IAAA,MAAAY;AAAAZ,IAAA,MAAAc;AAAAd,IAAA,MAAAgB;AAAAhB,IAAA,MAAAkB;OAAAA,MAAAlB,EAAA;AAAA,QAfFkB"}
@@ -74,16 +74,16 @@ function GridProvider(props) {
74
74
  }
75
75
  }, [docs, selectedIDs]);
76
76
  const updateSelections = React.useCallback(({ indexes }) => {
77
- const { newSelectedIDs } = docs.reduce((acc, item, index) => {
78
- if (indexes.includes(index)) acc.newSelectedIDs.add(item.id);
79
- return acc;
77
+ const { newSelectedIDs } = docs.reduce((acc_0, item_0, index) => {
78
+ if (indexes.includes(index)) acc_0.newSelectedIDs.add(item_0.id);
79
+ return acc_0;
80
80
  }, { newSelectedIDs: /* @__PURE__ */ new Set() });
81
- selectedIDs.forEach((id) => {
82
- setSelection(id);
81
+ selectedIDs.forEach((id_0) => {
82
+ setSelection(id_0);
83
83
  });
84
- newSelectedIDs.forEach((id) => {
85
- const item = getItem(id);
86
- if (item) setSelection(item.id);
84
+ newSelectedIDs.forEach((id_1) => {
85
+ const item_1 = getItem(id_1);
86
+ if (item_1) setSelection(item_1.id);
87
87
  });
88
88
  }, [
89
89
  docs,
@@ -95,7 +95,7 @@ function GridProvider(props) {
95
95
  const isShiftPressed = shiftKey;
96
96
  const isCtrlPressed = ctrlKey || metaKey;
97
97
  const isCurrentlySelected = selectedIDs.includes(currentItem.id);
98
- const currentItemIndex = docs.findIndex((item) => item.id === currentItem.id);
98
+ const currentItemIndex = docs.findIndex((item_2) => item_2.id === currentItem.id);
99
99
  switch (code) {
100
100
  case "ArrowDown":
101
101
  case "ArrowLeft":
@@ -130,17 +130,17 @@ function GridProvider(props) {
130
130
  if (allowMultiSelection && isCtrlPressed) {
131
131
  event.preventDefault();
132
132
  setFocusedItemIndex(totalCount - 1);
133
- updateSelections({ indexes: Array.from({ length: totalCount }, (_, i) => i) });
133
+ updateSelections({ indexes: Array.from({ length: totalCount }, (__0, i_0) => i_0) });
134
134
  }
135
135
  break;
136
136
  case "Space":
137
137
  if (allowMultiSelection && isShiftPressed) {
138
138
  event.preventDefault();
139
- updateSelections({ indexes: docs.reduce((acc, item, idx) => {
140
- if (item.id === currentItem.id) if (isCurrentlySelected) return acc;
141
- else acc.push(idx);
142
- else if (selectedIDs.includes(item.id)) acc.push(idx);
143
- return acc;
139
+ updateSelections({ indexes: docs.reduce((acc_1, item_3, idx_0) => {
140
+ if (item_3.id === currentItem.id) if (isCurrentlySelected) return acc_1;
141
+ else acc_1.push(idx_0);
142
+ else if (selectedIDs.includes(item_3.id)) acc_1.push(idx_0);
143
+ return acc_1;
144
144
  }, []) });
145
145
  } else {
146
146
  event.preventDefault();
@@ -165,30 +165,30 @@ function GridProvider(props) {
165
165
  totalCount
166
166
  ]);
167
167
  const [lastClickedItem, setLastClickedItem] = React.useState();
168
- const onItemClick = React.useCallback(({ event, item: clickedItem }) => {
168
+ const onItemClick = React.useCallback(({ event: event_0, item: clickedItem }) => {
169
169
  let doubleClicked = false;
170
- const isCtrlPressed = event.ctrlKey || event.metaKey;
171
- const isShiftPressed = event.shiftKey;
172
- const isCurrentlySelected = selectedIDs.includes(clickedItem.id);
173
- const currentItemIndex = docs.findIndex((item) => item.id === clickedItem.id);
170
+ const isCtrlPressed_0 = event_0.ctrlKey || event_0.metaKey;
171
+ const isShiftPressed_0 = event_0.shiftKey;
172
+ const isCurrentlySelected_0 = selectedIDs.includes(clickedItem.id);
173
+ const currentItemIndex_0 = docs.findIndex((item_4) => item_4.id === clickedItem.id);
174
174
  switch (true) {
175
- case allowMultiSelection && isCtrlPressed:
176
- event.preventDefault();
177
- updateSelections({ indexes: docs.reduce((acc, item, idx) => {
178
- if (item.id === clickedItem.id) {
179
- if (!isCurrentlySelected) acc.push(idx);
180
- } else if (selectedIDs.includes(item.id)) acc.push(idx);
181
- return acc;
175
+ case allowMultiSelection && isCtrlPressed_0:
176
+ event_0.preventDefault();
177
+ updateSelections({ indexes: docs.reduce((acc_2, item_5, idx_1) => {
178
+ if (item_5.id === clickedItem.id) {
179
+ if (!isCurrentlySelected_0) acc_2.push(idx_1);
180
+ } else if (selectedIDs.includes(item_5.id)) acc_2.push(idx_1);
181
+ return acc_2;
182
182
  }, []) });
183
183
  break;
184
- case allowMultiSelection && isShiftPressed:
185
- if (currentItemIndex !== -1) updateSelections({ indexes: handleShiftSelection(currentItemIndex) });
184
+ case allowMultiSelection && isShiftPressed_0:
185
+ if (currentItemIndex_0 !== -1) updateSelections({ indexes: handleShiftSelection(currentItemIndex_0) });
186
186
  break;
187
187
  default: {
188
188
  const now = Date.now();
189
189
  doubleClicked = now - (lastClickTime.current ?? 0) < 400 && lastClickedItem?.id === clickedItem.id;
190
190
  lastClickTime.current = now;
191
- if (!doubleClicked) updateSelections({ indexes: isCurrentlySelected && selectedIDs.length === 1 ? [] : [currentItemIndex] });
191
+ if (!doubleClicked) updateSelections({ indexes: isCurrentlySelected_0 && selectedIDs.length === 1 ? [] : [currentItemIndex_0] });
192
192
  break;
193
193
  }
194
194
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gridContext.mjs","names":["onItemKeyPress: GridContextValue['onItemKeyPress']","onItemClick: GridContextValue['onItemClick']","doubleClicked: boolean"],"sources":["../../../src/components/gridContext/gridContext.tsx"],"sourcesContent":["'use client'\n\nimport React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { formatAdminURL } from 'payload/shared'\n\nimport { useDrawerDepth } from '@payloadcms/ui/elements/Drawer'\nimport { useConfig, useRouteTransition, useSelection } from '@payloadcms/ui'\n\nimport type { FolderSortKeys } from 'payload'\nimport type { MappedDocument } from '../gridView/gridView'\ninterface OnItemClickArgs {\n event: React.MouseEvent\n index: number\n item: MappedDocument\n}\n\ninterface OnItemKeyPressArgs {\n event: React.KeyboardEvent\n index: number\n item: MappedDocument\n}\n\nexport type GridContextValue = {\n docs?: MappedDocument[]\n clearSelections: () => void\n onItemClick: (args: OnItemClickArgs) => void\n onItemKeyPress: (args: OnItemKeyPressArgs) => void\n setFocusedItemIndex: React.Dispatch<React.SetStateAction<number>>\n focusedItemIndex: number\n}\n\nconst Context = React.createContext<GridContextValue>({\n docs: [],\n clearSelections: () => {},\n onItemClick: () => undefined,\n onItemKeyPress: () => undefined,\n setFocusedItemIndex: () => -1,\n focusedItemIndex: -1,\n})\n\nexport type GridProviderProps = {\n readonly allowMultiSelection?: boolean\n /**\n * Children to render inside the provider\n */\n readonly children: React.ReactNode\n /**\n * All documents in the current folder\n */\n readonly docs: MappedDocument[]\n /**\n * The intial search query\n */\n readonly search?: string\n /**\n * The sort order of the documents\n *\n * @example\n * `name` for descending\n * `-name` for ascending\n */\n readonly sort?: FolderSortKeys\n}\nexport function GridProvider(props: GridProviderProps) {\n const { allowMultiSelection = true, children, docs } = props\n\n const { config } = useConfig()\n const drawerDepth = useDrawerDepth()\n const router = useRouter()\n const { startRouteTransition } = useRouteTransition()\n\n const { setSelection, selectedIDs } = useSelection()\n const currentlySelectedIndexes = React.useRef(new Set<number>())\n\n const [focusedItemIndex, setFocusedItemIndex] = React.useState(-1)\n const lastClickTime = React.useRef<null | number>(null)\n const totalCount = docs.length\n\n const clearSelections = React.useCallback(() => {\n setFocusedItemIndex(-1)\n\n currentlySelectedIndexes.current = new Set()\n }, [])\n\n const getItem = React.useCallback(\n (id: string | number) => {\n return docs.find((doc) => doc.id === id)\n },\n [docs]\n )\n\n const navigateAfterSelection = React.useCallback(\n ({\n collectionSlug,\n docID,\n }: {\n collectionSlug: string\n docID?: number | string\n }) => {\n if (drawerDepth === 1) {\n // not in a drawer (default is 1)\n if (collectionSlug) {\n // clicked on document, take the user to the documet view\n startRouteTransition(() => {\n router.push(\n formatAdminURL({\n adminRoute: config.routes.admin,\n path: `/collections/${collectionSlug}/${docID}`,\n })\n )\n clearSelections()\n })\n }\n } else {\n clearSelections()\n }\n },\n [\n clearSelections,\n config.routes.admin,\n drawerDepth,\n getItem,\n router,\n startRouteTransition,\n ]\n )\n\n const handleShiftSelection = React.useCallback(\n (targetIndex: number) => {\n // Find existing selection boundaries\n const existingIndexes = docs.reduce((acc, item, idx) => {\n if (selectedIDs.includes(item.id)) {\n acc.push(idx)\n }\n return acc\n }, [] as number[])\n\n if (existingIndexes.length === 0) {\n // No existing selection, just select target\n return [targetIndex]\n }\n\n const firstSelectedIndex = Math.min(...existingIndexes)\n const lastSelectedIndex = Math.max(...existingIndexes)\n const isWithinBounds =\n targetIndex >= firstSelectedIndex && targetIndex <= lastSelectedIndex\n\n // Choose anchor based on whether we're contracting or extending\n let anchorIndex = targetIndex\n if (isWithinBounds) {\n // Contracting: if target is at a boundary, use target as anchor\n // Otherwise, use furthest boundary to maintain opposite edge\n if (\n targetIndex === firstSelectedIndex ||\n targetIndex === lastSelectedIndex\n ) {\n anchorIndex = targetIndex\n } else {\n const distanceToFirst = Math.abs(targetIndex - firstSelectedIndex)\n const distanceToLast = Math.abs(targetIndex - lastSelectedIndex)\n anchorIndex =\n distanceToFirst >= distanceToLast\n ? firstSelectedIndex\n : lastSelectedIndex\n }\n } else {\n // Extending: use closest boundary\n const distanceToFirst = Math.abs(targetIndex - firstSelectedIndex)\n const distanceToLast = Math.abs(targetIndex - lastSelectedIndex)\n anchorIndex =\n distanceToFirst <= distanceToLast\n ? firstSelectedIndex\n : lastSelectedIndex\n }\n\n // Create range from anchor to target\n const startIndex = Math.min(anchorIndex, targetIndex)\n const endIndex = Math.max(anchorIndex, targetIndex)\n const newRangeIndexes = Array.from(\n { length: endIndex - startIndex + 1 },\n (_, i) => startIndex + i\n )\n\n if (isWithinBounds) {\n // Contracting: replace with new range\n return newRangeIndexes\n } else {\n // Extending: union with existing\n const mappedSet = new Set([...existingIndexes, ...newRangeIndexes])\n return Array.from(mappedSet)\n }\n },\n [docs, selectedIDs]\n )\n\n const updateSelections = React.useCallback(\n ({ indexes }: { indexes: number[] }) => {\n const { newSelectedIDs } = docs.reduce(\n (acc, item, index) => {\n if (indexes.includes(index)) {\n acc.newSelectedIDs.add(item.id)\n }\n return acc\n },\n {\n newSelectedIDs: new Set<string | number>(),\n }\n )\n\n // Clear previous selection in global selection context\n selectedIDs.forEach((id) => {\n setSelection(id)\n })\n\n // Update selection in global selection context\n newSelectedIDs.forEach((id) => {\n const item = getItem(id)\n if (item) {\n setSelection(item.id)\n }\n })\n },\n [docs, getItem, setSelection]\n )\n\n const onItemKeyPress: GridContextValue['onItemKeyPress'] = React.useCallback(\n ({ event, item: currentItem }) => {\n const { code, ctrlKey, metaKey, shiftKey } = event\n\n const isShiftPressed = shiftKey\n const isCtrlPressed = ctrlKey || metaKey\n const isCurrentlySelected = selectedIDs.includes(currentItem.id)\n const currentItemIndex = docs.findIndex(\n (item) => item.id === currentItem.id\n )\n\n switch (code) {\n case 'ArrowDown':\n case 'ArrowLeft':\n case 'ArrowRight':\n case 'ArrowUp': {\n event.preventDefault()\n\n if (currentItemIndex === -1) {\n break\n }\n\n const isBackward = code === 'ArrowLeft' || code === 'ArrowUp'\n const newItemIndex = isBackward\n ? currentItemIndex - 1\n : currentItemIndex + 1\n\n if (newItemIndex < 0 || newItemIndex > totalCount - 1) {\n // out of bounds, keep current selection\n return\n }\n\n setFocusedItemIndex(newItemIndex)\n\n if (isCtrlPressed) {\n break\n }\n\n if (isShiftPressed && allowMultiSelection) {\n const selectedIndexes = handleShiftSelection(newItemIndex)\n updateSelections({ indexes: selectedIndexes })\n return\n }\n\n // Single selection without shift\n if (!isShiftPressed) {\n // const newItem = allItems[newItemIndex]\n updateSelections({ indexes: [newItemIndex] })\n }\n\n break\n }\n case 'Enter': {\n if (selectedIDs.length === 1) {\n // setFocusedItemIndex(undefined)\n navigateAfterSelection({\n collectionSlug: currentItem.relationTo,\n docID: currentItem.id,\n })\n return\n }\n break\n }\n case 'Escape': {\n clearSelections()\n break\n }\n case 'KeyA': {\n if (allowMultiSelection && isCtrlPressed) {\n event.preventDefault()\n setFocusedItemIndex(totalCount - 1)\n updateSelections({\n indexes: Array.from({ length: totalCount }, (_, i) => i),\n })\n }\n break\n }\n case 'Space': {\n if (allowMultiSelection && isShiftPressed) {\n event.preventDefault()\n updateSelections({\n indexes: docs.reduce((acc, item, idx) => {\n if (item.id === currentItem.id) {\n if (isCurrentlySelected) {\n return acc\n } else {\n acc.push(idx)\n }\n } else if (selectedIDs.includes(item.id)) {\n acc.push(idx)\n }\n return acc\n }, [] as number[]),\n })\n } else {\n event.preventDefault()\n updateSelections({\n indexes: isCurrentlySelected ? [] : [currentItemIndex],\n })\n }\n break\n }\n case 'Tab': {\n if (allowMultiSelection && isShiftPressed) {\n const prevIndex = currentItemIndex - 1\n if (prevIndex < 0 && selectedIDs.length > 0) {\n setFocusedItemIndex(prevIndex)\n }\n } else {\n const nextIndex = currentItemIndex + 1\n if (nextIndex === totalCount && selectedIDs.length > 0) {\n setFocusedItemIndex(totalCount - 1)\n }\n }\n break\n }\n }\n },\n [\n selectedIDs,\n docs,\n allowMultiSelection,\n handleShiftSelection,\n updateSelections,\n navigateAfterSelection,\n clearSelections,\n totalCount,\n ]\n )\n\n // Track last clicked item for double-click detection\n const [lastClickedItem, setLastClickedItem] = React.useState<\n MappedDocument | undefined\n >()\n\n const onItemClick: GridContextValue['onItemClick'] = React.useCallback(\n ({ event, item: clickedItem }) => {\n let doubleClicked: boolean = false\n const isCtrlPressed = event.ctrlKey || event.metaKey\n const isShiftPressed = event.shiftKey\n const isCurrentlySelected = selectedIDs.includes(clickedItem.id)\n const currentItemIndex = docs.findIndex(\n (item) => item.id === clickedItem.id\n )\n\n switch (true) {\n case allowMultiSelection && isCtrlPressed: {\n event.preventDefault()\n const indexes = docs.reduce((acc, item, idx) => {\n if (item.id === clickedItem.id) {\n if (!isCurrentlySelected) {\n acc.push(idx)\n }\n } else if (selectedIDs.includes(item.id)) {\n acc.push(idx)\n }\n return acc\n }, [] as number[])\n\n updateSelections({ indexes })\n break\n }\n case allowMultiSelection && isShiftPressed: {\n if (currentItemIndex !== -1) {\n const selectedIndexes = handleShiftSelection(currentItemIndex)\n updateSelections({ indexes: selectedIndexes })\n }\n break\n }\n default: {\n const now = Date.now()\n doubleClicked =\n now - (lastClickTime.current ?? 0) < 400 &&\n lastClickedItem?.id === clickedItem.id\n lastClickTime.current = now\n\n if (!doubleClicked) {\n updateSelections({\n indexes:\n isCurrentlySelected && selectedIDs.length === 1\n ? []\n : [currentItemIndex],\n })\n }\n break\n }\n }\n\n // Update last clicked item to determine double clicks\n setLastClickedItem(clickedItem)\n\n if (doubleClicked) {\n navigateAfterSelection({\n collectionSlug: clickedItem.relationTo,\n docID: clickedItem.id,\n })\n }\n },\n [\n docs,\n allowMultiSelection,\n getItem,\n updateSelections,\n navigateAfterSelection,\n handleShiftSelection,\n ]\n )\n\n return (\n <Context\n value={{\n clearSelections,\n docs,\n focusedItemIndex,\n onItemClick,\n onItemKeyPress,\n setFocusedItemIndex,\n }}\n >\n {children}\n </Context>\n )\n}\n\nexport function useGrid(): GridContextValue {\n const context = React.use(Context)\n\n if (context === undefined) {\n throw new Error('useGrid must be used within a GridProvider')\n }\n\n return context\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"gridContext.mjs","names":["React","useRouter","formatAdminURL","useDrawerDepth","useConfig","useRouteTransition","useSelection","FolderSortKeys","MappedDocument","OnItemClickArgs","event","MouseEvent","index","item","OnItemKeyPressArgs","KeyboardEvent","GridContextValue","docs","clearSelections","onItemClick","args","onItemKeyPress","setFocusedItemIndex","Dispatch","SetStateAction","focusedItemIndex","Context","createContext","undefined","GridProviderProps","allowMultiSelection","children","ReactNode","search","sort","GridProvider","props","config","drawerDepth","router","startRouteTransition","setSelection","selectedIDs","currentlySelectedIndexes","useRef","Set","useState","lastClickTime","totalCount","length","useCallback","current","getItem","id","find","doc","navigateAfterSelection","collectionSlug","docID","push","adminRoute","routes","admin","path","handleShiftSelection","targetIndex","existingIndexes","reduce","acc","idx","includes","firstSelectedIndex","Math","min","lastSelectedIndex","max","isWithinBounds","anchorIndex","distanceToFirst","abs","distanceToLast","startIndex","endIndex","newRangeIndexes","Array","from","_","i","mappedSet","updateSelections","indexes","newSelectedIDs","add","forEach","currentItem","code","ctrlKey","metaKey","shiftKey","isShiftPressed","isCtrlPressed","isCurrentlySelected","currentItemIndex","findIndex","preventDefault","isBackward","newItemIndex","selectedIndexes","relationTo","prevIndex","nextIndex","lastClickedItem","setLastClickedItem","clickedItem","doubleClicked","now","Date","useGrid","context","use","Error"],"sources":["../../../src/components/gridContext/gridContext.tsx"],"sourcesContent":["'use client'\n\nimport React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { formatAdminURL } from 'payload/shared'\n\nimport { useDrawerDepth } from '@payloadcms/ui/elements/Drawer'\nimport { useConfig, useRouteTransition, useSelection } from '@payloadcms/ui'\n\nimport type { FolderSortKeys } from 'payload'\nimport type { MappedDocument } from '../gridView/gridView'\ninterface OnItemClickArgs {\n event: React.MouseEvent\n index: number\n item: MappedDocument\n}\n\ninterface OnItemKeyPressArgs {\n event: React.KeyboardEvent\n index: number\n item: MappedDocument\n}\n\nexport type GridContextValue = {\n docs?: MappedDocument[]\n clearSelections: () => void\n onItemClick: (args: OnItemClickArgs) => void\n onItemKeyPress: (args: OnItemKeyPressArgs) => void\n setFocusedItemIndex: React.Dispatch<React.SetStateAction<number>>\n focusedItemIndex: number\n}\n\nconst Context = React.createContext<GridContextValue>({\n docs: [],\n clearSelections: () => {},\n onItemClick: () => undefined,\n onItemKeyPress: () => undefined,\n setFocusedItemIndex: () => -1,\n focusedItemIndex: -1,\n})\n\nexport type GridProviderProps = {\n readonly allowMultiSelection?: boolean\n /**\n * Children to render inside the provider\n */\n readonly children: React.ReactNode\n /**\n * All documents in the current folder\n */\n readonly docs: MappedDocument[]\n /**\n * The intial search query\n */\n readonly search?: string\n /**\n * The sort order of the documents\n *\n * @example\n * `name` for descending\n * `-name` for ascending\n */\n readonly sort?: FolderSortKeys\n}\nexport function GridProvider(props: GridProviderProps) {\n const { allowMultiSelection = true, children, docs } = props\n\n const { config } = useConfig()\n const drawerDepth = useDrawerDepth()\n const router = useRouter()\n const { startRouteTransition } = useRouteTransition()\n\n const { setSelection, selectedIDs } = useSelection()\n const currentlySelectedIndexes = React.useRef(new Set<number>())\n\n const [focusedItemIndex, setFocusedItemIndex] = React.useState(-1)\n const lastClickTime = React.useRef<null | number>(null)\n const totalCount = docs.length\n\n const clearSelections = React.useCallback(() => {\n setFocusedItemIndex(-1)\n\n currentlySelectedIndexes.current = new Set()\n }, [])\n\n const getItem = React.useCallback(\n (id: string | number) => {\n return docs.find((doc) => doc.id === id)\n },\n [docs]\n )\n\n const navigateAfterSelection = React.useCallback(\n ({\n collectionSlug,\n docID,\n }: {\n collectionSlug: string\n docID?: number | string\n }) => {\n if (drawerDepth === 1) {\n // not in a drawer (default is 1)\n if (collectionSlug) {\n // clicked on document, take the user to the documet view\n startRouteTransition(() => {\n router.push(\n formatAdminURL({\n adminRoute: config.routes.admin,\n path: `/collections/${collectionSlug}/${docID}`,\n })\n )\n clearSelections()\n })\n }\n } else {\n clearSelections()\n }\n },\n [\n clearSelections,\n config.routes.admin,\n drawerDepth,\n getItem,\n router,\n startRouteTransition,\n ]\n )\n\n const handleShiftSelection = React.useCallback(\n (targetIndex: number) => {\n // Find existing selection boundaries\n const existingIndexes = docs.reduce((acc, item, idx) => {\n if (selectedIDs.includes(item.id)) {\n acc.push(idx)\n }\n return acc\n }, [] as number[])\n\n if (existingIndexes.length === 0) {\n // No existing selection, just select target\n return [targetIndex]\n }\n\n const firstSelectedIndex = Math.min(...existingIndexes)\n const lastSelectedIndex = Math.max(...existingIndexes)\n const isWithinBounds =\n targetIndex >= firstSelectedIndex && targetIndex <= lastSelectedIndex\n\n // Choose anchor based on whether we're contracting or extending\n let anchorIndex = targetIndex\n if (isWithinBounds) {\n // Contracting: if target is at a boundary, use target as anchor\n // Otherwise, use furthest boundary to maintain opposite edge\n if (\n targetIndex === firstSelectedIndex ||\n targetIndex === lastSelectedIndex\n ) {\n anchorIndex = targetIndex\n } else {\n const distanceToFirst = Math.abs(targetIndex - firstSelectedIndex)\n const distanceToLast = Math.abs(targetIndex - lastSelectedIndex)\n anchorIndex =\n distanceToFirst >= distanceToLast\n ? firstSelectedIndex\n : lastSelectedIndex\n }\n } else {\n // Extending: use closest boundary\n const distanceToFirst = Math.abs(targetIndex - firstSelectedIndex)\n const distanceToLast = Math.abs(targetIndex - lastSelectedIndex)\n anchorIndex =\n distanceToFirst <= distanceToLast\n ? firstSelectedIndex\n : lastSelectedIndex\n }\n\n // Create range from anchor to target\n const startIndex = Math.min(anchorIndex, targetIndex)\n const endIndex = Math.max(anchorIndex, targetIndex)\n const newRangeIndexes = Array.from(\n { length: endIndex - startIndex + 1 },\n (_, i) => startIndex + i\n )\n\n if (isWithinBounds) {\n // Contracting: replace with new range\n return newRangeIndexes\n } else {\n // Extending: union with existing\n const mappedSet = new Set([...existingIndexes, ...newRangeIndexes])\n return Array.from(mappedSet)\n }\n },\n [docs, selectedIDs]\n )\n\n const updateSelections = React.useCallback(\n ({ indexes }: { indexes: number[] }) => {\n const { newSelectedIDs } = docs.reduce(\n (acc, item, index) => {\n if (indexes.includes(index)) {\n acc.newSelectedIDs.add(item.id)\n }\n return acc\n },\n {\n newSelectedIDs: new Set<string | number>(),\n }\n )\n\n // Clear previous selection in global selection context\n selectedIDs.forEach((id) => {\n setSelection(id)\n })\n\n // Update selection in global selection context\n newSelectedIDs.forEach((id) => {\n const item = getItem(id)\n if (item) {\n setSelection(item.id)\n }\n })\n },\n [docs, getItem, setSelection]\n )\n\n const onItemKeyPress: GridContextValue['onItemKeyPress'] = React.useCallback(\n ({ event, item: currentItem }) => {\n const { code, ctrlKey, metaKey, shiftKey } = event\n\n const isShiftPressed = shiftKey\n const isCtrlPressed = ctrlKey || metaKey\n const isCurrentlySelected = selectedIDs.includes(currentItem.id)\n const currentItemIndex = docs.findIndex(\n (item) => item.id === currentItem.id\n )\n\n switch (code) {\n case 'ArrowDown':\n case 'ArrowLeft':\n case 'ArrowRight':\n case 'ArrowUp': {\n event.preventDefault()\n\n if (currentItemIndex === -1) {\n break\n }\n\n const isBackward = code === 'ArrowLeft' || code === 'ArrowUp'\n const newItemIndex = isBackward\n ? currentItemIndex - 1\n : currentItemIndex + 1\n\n if (newItemIndex < 0 || newItemIndex > totalCount - 1) {\n // out of bounds, keep current selection\n return\n }\n\n setFocusedItemIndex(newItemIndex)\n\n if (isCtrlPressed) {\n break\n }\n\n if (isShiftPressed && allowMultiSelection) {\n const selectedIndexes = handleShiftSelection(newItemIndex)\n updateSelections({ indexes: selectedIndexes })\n return\n }\n\n // Single selection without shift\n if (!isShiftPressed) {\n // const newItem = allItems[newItemIndex]\n updateSelections({ indexes: [newItemIndex] })\n }\n\n break\n }\n case 'Enter': {\n if (selectedIDs.length === 1) {\n // setFocusedItemIndex(undefined)\n navigateAfterSelection({\n collectionSlug: currentItem.relationTo,\n docID: currentItem.id,\n })\n return\n }\n break\n }\n case 'Escape': {\n clearSelections()\n break\n }\n case 'KeyA': {\n if (allowMultiSelection && isCtrlPressed) {\n event.preventDefault()\n setFocusedItemIndex(totalCount - 1)\n updateSelections({\n indexes: Array.from({ length: totalCount }, (_, i) => i),\n })\n }\n break\n }\n case 'Space': {\n if (allowMultiSelection && isShiftPressed) {\n event.preventDefault()\n updateSelections({\n indexes: docs.reduce((acc, item, idx) => {\n if (item.id === currentItem.id) {\n if (isCurrentlySelected) {\n return acc\n } else {\n acc.push(idx)\n }\n } else if (selectedIDs.includes(item.id)) {\n acc.push(idx)\n }\n return acc\n }, [] as number[]),\n })\n } else {\n event.preventDefault()\n updateSelections({\n indexes: isCurrentlySelected ? [] : [currentItemIndex],\n })\n }\n break\n }\n case 'Tab': {\n if (allowMultiSelection && isShiftPressed) {\n const prevIndex = currentItemIndex - 1\n if (prevIndex < 0 && selectedIDs.length > 0) {\n setFocusedItemIndex(prevIndex)\n }\n } else {\n const nextIndex = currentItemIndex + 1\n if (nextIndex === totalCount && selectedIDs.length > 0) {\n setFocusedItemIndex(totalCount - 1)\n }\n }\n break\n }\n }\n },\n [\n selectedIDs,\n docs,\n allowMultiSelection,\n handleShiftSelection,\n updateSelections,\n navigateAfterSelection,\n clearSelections,\n totalCount,\n ]\n )\n\n // Track last clicked item for double-click detection\n const [lastClickedItem, setLastClickedItem] = React.useState<\n MappedDocument | undefined\n >()\n\n const onItemClick: GridContextValue['onItemClick'] = React.useCallback(\n ({ event, item: clickedItem }) => {\n let doubleClicked: boolean = false\n const isCtrlPressed = event.ctrlKey || event.metaKey\n const isShiftPressed = event.shiftKey\n const isCurrentlySelected = selectedIDs.includes(clickedItem.id)\n const currentItemIndex = docs.findIndex(\n (item) => item.id === clickedItem.id\n )\n\n switch (true) {\n case allowMultiSelection && isCtrlPressed: {\n event.preventDefault()\n const indexes = docs.reduce((acc, item, idx) => {\n if (item.id === clickedItem.id) {\n if (!isCurrentlySelected) {\n acc.push(idx)\n }\n } else if (selectedIDs.includes(item.id)) {\n acc.push(idx)\n }\n return acc\n }, [] as number[])\n\n updateSelections({ indexes })\n break\n }\n case allowMultiSelection && isShiftPressed: {\n if (currentItemIndex !== -1) {\n const selectedIndexes = handleShiftSelection(currentItemIndex)\n updateSelections({ indexes: selectedIndexes })\n }\n break\n }\n default: {\n const now = Date.now()\n doubleClicked =\n now - (lastClickTime.current ?? 0) < 400 &&\n lastClickedItem?.id === clickedItem.id\n lastClickTime.current = now\n\n if (!doubleClicked) {\n updateSelections({\n indexes:\n isCurrentlySelected && selectedIDs.length === 1\n ? []\n : [currentItemIndex],\n })\n }\n break\n }\n }\n\n // Update last clicked item to determine double clicks\n setLastClickedItem(clickedItem)\n\n if (doubleClicked) {\n navigateAfterSelection({\n collectionSlug: clickedItem.relationTo,\n docID: clickedItem.id,\n })\n }\n },\n [\n docs,\n allowMultiSelection,\n getItem,\n updateSelections,\n navigateAfterSelection,\n handleShiftSelection,\n ]\n )\n\n return (\n <Context\n value={{\n clearSelections,\n docs,\n focusedItemIndex,\n onItemClick,\n onItemKeyPress,\n setFocusedItemIndex,\n }}\n >\n {children}\n </Context>\n )\n}\n\nexport function useGrid(): GridContextValue {\n const context = React.use(Context)\n\n if (context === undefined) {\n throw new Error('useGrid must be used within a GridProvider')\n }\n\n return context\n}\n"],"mappings":"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"}