@aws-amplify/ui-react-storage 3.15.0 → 3.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/dist/browser.js +1 -1
  2. package/dist/{createStorageBrowser-CG-6mXiT.js → createStorageBrowser-De4hcP-u.js} +710 -158
  3. package/dist/esm/browser.mjs +1 -0
  4. package/dist/esm/components/StorageBrowser/ErrorBoundary/ErrorBoundary.mjs +1 -4
  5. package/dist/esm/components/StorageBrowser/actions/configs/defaults.mjs +13 -3
  6. package/dist/esm/components/StorageBrowser/actions/handlers/delete.mjs +39 -8
  7. package/dist/esm/components/StorageBrowser/components/ComponentsProvider.mjs +1 -4
  8. package/dist/esm/components/StorageBrowser/components/base/preview/DownloadButton.mjs +1 -4
  9. package/dist/esm/components/StorageBrowser/components/composables/ActionConfirmationModal.mjs +34 -0
  10. package/dist/esm/components/StorageBrowser/components/composables/defaults.mjs +2 -0
  11. package/dist/esm/components/StorageBrowser/components/elements/definitions.mjs +2 -2
  12. package/dist/esm/components/StorageBrowser/configuration/paginationContext.mjs +15 -0
  13. package/dist/esm/components/StorageBrowser/controls/ActionConfirmationModalControl.mjs +12 -0
  14. package/dist/esm/components/StorageBrowser/controls/DataTableControl.mjs +1 -4
  15. package/dist/esm/components/StorageBrowser/controls/hooks/useActionConfirmationModal.mjs +17 -0
  16. package/dist/esm/components/StorageBrowser/createStorageBrowser/StorageBrowserDefault.mjs +9 -4
  17. package/dist/esm/components/StorageBrowser/createStorageBrowser/createProvider.mjs +11 -9
  18. package/dist/esm/components/StorageBrowser/createStorageBrowser/createStorageBrowser.mjs +10 -5
  19. package/dist/esm/components/StorageBrowser/displayText/libraries/en/deleteView.mjs +117 -5
  20. package/dist/esm/components/StorageBrowser/displayText/libraries/en/locationDetailView.mjs +1 -0
  21. package/dist/esm/components/StorageBrowser/displayText/libraries/en/shared.mjs +1 -0
  22. package/dist/esm/components/StorageBrowser/locationItems/context.mjs +17 -14
  23. package/dist/esm/components/StorageBrowser/locationItems/utils.mjs +38 -0
  24. package/dist/esm/components/StorageBrowser/store/validateStoreProps.mjs +1 -1
  25. package/dist/esm/components/StorageBrowser/tasks/useProcessTasks.mjs +7 -4
  26. package/dist/esm/components/StorageBrowser/useAction/useHandler.mjs +1 -0
  27. package/dist/esm/components/StorageBrowser/useAction/useListFolderItems.mjs +1 -0
  28. package/dist/esm/components/StorageBrowser/useAction/useListLocationItems.mjs +1 -0
  29. package/dist/esm/components/StorageBrowser/useAction/utils.mjs +0 -5
  30. package/dist/esm/components/StorageBrowser/utils/validatePageSize.mjs +12 -0
  31. package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/CopyView.mjs +1 -4
  32. package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/CopyViewProvider.mjs +5 -4
  33. package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/FoldersMessageControl.mjs +1 -4
  34. package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/useCopyView.mjs +1 -4
  35. package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/useFolders.mjs +1 -4
  36. package/dist/esm/components/StorageBrowser/views/LocationActionView/CreateFolderView/CreateFolderView.mjs +1 -4
  37. package/dist/esm/components/StorageBrowser/views/LocationActionView/CreateFolderView/useCreateFolderView.mjs +1 -4
  38. package/dist/esm/components/StorageBrowser/views/LocationActionView/DeleteView/DeleteView.mjs +6 -6
  39. package/dist/esm/components/StorageBrowser/views/LocationActionView/DeleteView/DeleteViewProvider.mjs +7 -6
  40. package/dist/esm/components/StorageBrowser/views/LocationActionView/DeleteView/useDeleteView.mjs +70 -7
  41. package/dist/esm/components/StorageBrowser/views/LocationActionView/DeleteView/utils.mjs +87 -0
  42. package/dist/esm/components/StorageBrowser/views/LocationActionView/DownloadView/DownloadView.mjs +1 -4
  43. package/dist/esm/components/StorageBrowser/views/LocationActionView/DownloadView/DownloadViewProvider.mjs +9 -0
  44. package/dist/esm/components/StorageBrowser/views/LocationActionView/DownloadView/useDownloadView.mjs +1 -4
  45. package/dist/esm/components/StorageBrowser/views/LocationActionView/UploadView/UploadView.mjs +3 -6
  46. package/dist/esm/components/StorageBrowser/views/LocationActionView/UploadView/UploadViewProvider.mjs +3 -0
  47. package/dist/esm/components/StorageBrowser/views/LocationActionView/UploadView/useUploadView.mjs +11 -12
  48. package/dist/esm/components/StorageBrowser/views/LocationDetailView/LocationDetailView.mjs +1 -4
  49. package/dist/esm/components/StorageBrowser/views/LocationDetailView/LocationDetailViewProvider.mjs +2 -1
  50. package/dist/esm/components/StorageBrowser/views/LocationDetailView/getLocationDetailViewTableData/getFolderRowContent.mjs +38 -27
  51. package/dist/esm/components/StorageBrowser/views/LocationDetailView/getLocationDetailViewTableData/getLocationDetailViewTableData.mjs +8 -1
  52. package/dist/esm/components/StorageBrowser/views/LocationDetailView/useLocationDetailView.mjs +24 -20
  53. package/dist/esm/components/StorageBrowser/views/LocationsView/LocationsView.mjs +1 -4
  54. package/dist/esm/components/StorageBrowser/views/LocationsView/LocationsViewProvider.mjs +1 -4
  55. package/dist/esm/components/StorageBrowser/views/LocationsView/useLocationsView.mjs +13 -10
  56. package/dist/esm/components/StorageBrowser/views/context/actionViews.mjs +12 -8
  57. package/dist/esm/components/StorageBrowser/views/context/primaryViews.mjs +9 -4
  58. package/dist/esm/components/StorageBrowser/views/hooks/usePaginate.mjs +18 -7
  59. package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/constants.mjs +10 -1
  60. package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/deleteResolvers.mjs +123 -13
  61. package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/utils.mjs +3 -3
  62. package/dist/esm/version.mjs +1 -1
  63. package/dist/index.js +1 -1
  64. package/dist/styles.css +83 -1
  65. package/dist/types/components/StorageBrowser/StorageBrowserAmplify.d.ts +3 -1
  66. package/dist/types/components/StorageBrowser/actions/handlers/delete.d.ts +5 -3
  67. package/dist/types/components/StorageBrowser/actions/handlers/types.d.ts +15 -1
  68. package/dist/types/components/StorageBrowser/components/composables/ActionConfirmationModal.d.ts +12 -0
  69. package/dist/types/components/StorageBrowser/components/composables/types.d.ts +2 -0
  70. package/dist/types/components/StorageBrowser/configuration/index.d.ts +2 -0
  71. package/dist/types/components/StorageBrowser/configuration/paginationContext.d.ts +12 -0
  72. package/dist/types/components/StorageBrowser/controls/ActionConfirmationModalControl.d.ts +2 -0
  73. package/dist/types/components/StorageBrowser/controls/hooks/useActionConfirmationModal.d.ts +2 -0
  74. package/dist/types/components/StorageBrowser/controls/index.d.ts +1 -0
  75. package/dist/types/components/StorageBrowser/controls/types.d.ts +4 -0
  76. package/dist/types/components/StorageBrowser/createStorageBrowser/types.d.ts +7 -1
  77. package/dist/types/components/StorageBrowser/displayText/types.d.ts +7 -0
  78. package/dist/types/components/StorageBrowser/locationItems/context.d.ts +12 -3
  79. package/dist/types/components/StorageBrowser/locationItems/index.d.ts +1 -0
  80. package/dist/types/components/StorageBrowser/locationItems/utils.d.ts +27 -0
  81. package/dist/types/components/StorageBrowser/store/types.d.ts +7 -0
  82. package/dist/types/components/StorageBrowser/tasks/types.d.ts +7 -2
  83. package/dist/types/components/StorageBrowser/utils/index.d.ts +1 -0
  84. package/dist/types/components/StorageBrowser/utils/validatePageSize.d.ts +1 -0
  85. package/dist/types/components/StorageBrowser/views/LocationActionView/DeleteView/types.d.ts +5 -0
  86. package/dist/types/components/StorageBrowser/views/LocationActionView/DeleteView/utils.d.ts +26 -0
  87. package/dist/types/components/StorageBrowser/views/LocationActionView/UploadView/types.d.ts +7 -0
  88. package/dist/types/components/StorageBrowser/views/LocationDetailView/getLocationDetailViewTableData/getFolderRowContent.d.ts +4 -1
  89. package/dist/types/components/StorageBrowser/views/LocationDetailView/getLocationDetailViewTableData/getLocationDetailViewTableData.d.ts +3 -2
  90. package/dist/types/components/StorageBrowser/views/LocationDetailView/types.d.ts +10 -3
  91. package/dist/types/components/StorageBrowser/views/LocationsView/types.d.ts +2 -2
  92. package/dist/types/components/StorageBrowser/views/hooks/usePaginate.d.ts +1 -1
  93. package/dist/types/components/StorageBrowser/views/types.d.ts +6 -0
  94. package/dist/types/components/StorageBrowser/views/utils/index.d.ts +1 -1
  95. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/constants.d.ts +1 -0
  96. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/deleteResolvers.d.ts +5 -2
  97. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/index.d.ts +1 -1
  98. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/types.d.ts +4 -0
  99. package/dist/types/index.d.ts +1 -1
  100. package/dist/types/version.d.ts +1 -1
  101. package/package.json +7 -7
@@ -36,7 +36,7 @@ function _interopNamespace(e) {
36
36
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
37
37
  var JSZip__default = /*#__PURE__*/_interopDefault(JSZip);
38
38
 
39
- const VERSION = '3.15.0';
39
+ const VERSION = '3.17.0';
40
40
 
41
41
  const DEFAULT_CHECKSUM_ALGORITHM = 'crc-32';
42
42
  // 5MiB for multipart upload
@@ -422,27 +422,58 @@ const createFolderHandler = (input) => {
422
422
  };
423
423
  };
424
424
 
425
- const deleteHandler = ({ config, data, }) => {
425
+ const deleteHandler = ({ config, data, options, }) => {
426
426
  const { key } = data;
427
427
  const { accountId, credentials, customEndpoint } = config;
428
- const result = internals.remove({
428
+ const { onProgress } = options ?? {};
429
+ let cumulativeSuccessCount = 0;
430
+ let cumulativeFailureCount = 0;
431
+ let operationCancel = () => {
432
+ // noop
433
+ };
434
+ const cancel = () => {
435
+ operationCancel?.();
436
+ };
437
+ const operation = internals.remove({
429
438
  path: key,
430
439
  options: {
431
440
  bucket: constructBucket$1(config),
432
441
  locationCredentialsProvider: credentials,
433
442
  expectedBucketOwner: accountId,
434
443
  customEndpoint,
444
+ onProgress: (progress) => {
445
+ if (!progress) {
446
+ return;
447
+ }
448
+ const batchSuccessCount = progress?.deleted?.length ?? 0;
449
+ const batchFailureCount = progress?.failed?.length ?? 0;
450
+ cumulativeSuccessCount += batchSuccessCount;
451
+ cumulativeFailureCount += batchFailureCount;
452
+ onProgress?.(data, {
453
+ successCount: cumulativeSuccessCount,
454
+ failureCount: cumulativeFailureCount,
455
+ });
456
+ },
435
457
  },
458
+ });
459
+ operationCancel = operation?.cancel ? operation?.cancel : () => { };
460
+ const operationPromise = operation?.result ?? operation;
461
+ const result = operationPromise
462
+ ?.then?.(({ path }) => {
463
+ return {
464
+ status: 'COMPLETE',
465
+ value: {
466
+ key: path,
467
+ successCount: cumulativeSuccessCount,
468
+ failureCount: cumulativeFailureCount,
469
+ },
470
+ };
436
471
  })
437
- .then(({ path }) => ({
438
- status: 'COMPLETE',
439
- value: { key: path },
440
- }))
441
- .catch((error) => {
472
+ ?.catch?.((error) => {
442
473
  const { message } = error;
443
474
  return { error, message, status: 'FAILED' };
444
475
  });
445
- return { result };
476
+ return { result, cancel };
446
477
  };
447
478
 
448
479
  const zipProgressManager = ({ dataMap, onZipProgress, }) => {
@@ -634,7 +665,7 @@ const downloadHandler = (() => {
634
665
  return handler;
635
666
  })();
636
667
 
637
- const DEFAULT_PAGE_SIZE$5 = 1000;
668
+ const DEFAULT_PAGE_SIZE$2 = 1000;
638
669
  const parseItems = (items, excludedPath) => items
639
670
  // remove root `key` from results
640
671
  .filter(({ path }) => path !== excludedPath)
@@ -667,7 +698,7 @@ const parseResult = ({ excludedSubpaths, items }, prefix) => filterDotItems([...
667
698
  const listLocationItemsHandler = async (input) => {
668
699
  const { config, prefix, options } = input;
669
700
  const { bucket: _bucket, credentials, customEndpoint, region: globalRegion, accountId, } = config;
670
- const { exclude, delimiter, nextToken, pageSize: _pageSize = DEFAULT_PAGE_SIZE$5, ..._options } = options ?? {};
701
+ const { exclude, delimiter, nextToken, pageSize: _pageSize = DEFAULT_PAGE_SIZE$2, ..._options } = options ?? {};
671
702
  const bucketRegion = getBucketRegion(_bucket, globalRegion);
672
703
  const bucket = { bucketName: _bucket, region: bucketRegion };
673
704
  const subpathStrategy = {
@@ -792,10 +823,50 @@ const defaultValue$7 = {
792
823
  };
793
824
  const { useActionConfigs, ActionConfigsProvider } = uiReactCore.createContextUtilities({ contextName: 'ActionConfigs', defaultValue: defaultValue$7 });
794
825
 
826
+ /**
827
+ * Selection utility functions for LocationItems
828
+ */
829
+ /**
830
+ * Get selected files from dataItems
831
+ */
832
+ const getSelectedFiles = (dataItems) => {
833
+ return dataItems?.filter((item) => item.type === 'FILE') ?? [];
834
+ };
835
+ /**
836
+ * Get selected folders from dataItems
837
+ */
838
+ const getSelectedFolders = (dataItems) => {
839
+ return dataItems?.filter((item) => item.type === 'FOLDER') ?? [];
840
+ };
841
+ /**
842
+ * Check if selection contains folders
843
+ */
844
+ const hasSelectedFolders = (dataItems) => {
845
+ return dataItems?.some((item) => item.type === 'FOLDER') ?? false;
846
+ };
847
+ /**
848
+ * Get selection summary
849
+ */
850
+ const getSelectionSummary = (dataItems) => {
851
+ const files = getSelectedFiles(dataItems);
852
+ const folders = getSelectedFolders(dataItems);
853
+ return {
854
+ total: dataItems?.length ?? 0,
855
+ files: files.length,
856
+ folders: folders.length,
857
+ hasFiles: files.length > 0,
858
+ hasFolders: folders.length > 0,
859
+ isMixed: files.length > 0 && folders.length > 0,
860
+ };
861
+ };
862
+
795
863
  const copyActionConfig = {
796
864
  viewName: 'CopyView',
797
865
  actionListItem: {
798
- disable: (selected) => !selected || selected.length === 0,
866
+ disable: (selected) => {
867
+ const hasNoSelection = !selected || selected.length === 0;
868
+ return hasNoSelection || hasSelectedFolders(selected);
869
+ },
799
870
  hide: (permissions) => !permissions.includes('write'),
800
871
  icon: 'copy-file',
801
872
  label: 'Copy',
@@ -805,7 +876,10 @@ const copyActionConfig = {
805
876
  const deleteActionConfig = {
806
877
  viewName: 'DeleteView',
807
878
  actionListItem: {
808
- disable: (selected) => !selected || selected.length === 0,
879
+ disable: (selected) => {
880
+ const hasNoSelection = !selected || selected.length === 0;
881
+ return hasNoSelection;
882
+ },
809
883
  hide: (permissions) => !permissions.includes('delete'),
810
884
  icon: 'delete-file',
811
885
  label: 'Delete',
@@ -833,7 +907,10 @@ const uploadActionConfig = {
833
907
  const downloadActionConfig = {
834
908
  viewName: 'DownloadView',
835
909
  actionListItem: {
836
- disable: (selected) => !selected || selected.length === 0,
910
+ disable: (selected) => {
911
+ const hasNoSelection = !selected || selected.length === 0;
912
+ return hasNoSelection || hasSelectedFolders(selected);
913
+ },
837
914
  hide: (permissions) => !permissions.includes('get'),
838
915
  icon: 'download',
839
916
  label: 'Download',
@@ -1038,7 +1115,7 @@ const OrderedListElement = elements.defineBaseElement({
1038
1115
  type: 'ol',
1039
1116
  displayName: 'OrderedList',
1040
1117
  });
1041
- elements.defineBaseElement({
1118
+ const UnorderedListElement = elements.defineBaseElement({
1042
1119
  type: 'ul',
1043
1120
  displayName: 'UnorderedList',
1044
1121
  });
@@ -1178,6 +1255,34 @@ const { useComposables, ComposablesProvider } = uiReactCore.createContextUtiliti
1178
1255
 
1179
1256
  const ActionCancel = ({ onCancel, isDisabled, label, }) => (React__namespace["default"].createElement(ButtonElement, { variant: "cancel", className: `${STORAGE_BROWSER_BLOCK}__cancel`, onClick: onCancel, disabled: isDisabled }, label));
1180
1257
 
1258
+ const ActionConfirmationModal = ({ isOpen = false, title, message, content, onConfirm, onCancel, confirmLabel, cancelLabel, }) => {
1259
+ const modalRef = React__namespace["default"].useRef(null);
1260
+ React__namespace["default"].useEffect(() => {
1261
+ if (isOpen && modalRef.current) {
1262
+ modalRef.current.focus();
1263
+ }
1264
+ }, [isOpen]);
1265
+ const handleKeyDown = (event) => {
1266
+ if (event.key === 'Escape') {
1267
+ onCancel?.();
1268
+ }
1269
+ };
1270
+ if (!isOpen)
1271
+ return null;
1272
+ return (React__namespace["default"].createElement("div", { ref: modalRef, className: "amplify-modal__overlay", role: "dialog", "aria-modal": "true", tabIndex: -1, onKeyDown: handleKeyDown, onClick: (e) => {
1273
+ if (e.target === e.currentTarget) {
1274
+ onCancel?.();
1275
+ }
1276
+ } },
1277
+ React__namespace["default"].createElement(ViewElement, { className: "amplify-modal__content", role: "document" },
1278
+ React__namespace["default"].createElement(HeadingElement, { className: "amplify-modal__title" }, title),
1279
+ message && (React__namespace["default"].createElement(TextElement, { className: "amplify-modal__body" }, message)),
1280
+ content && (React__namespace["default"].createElement(ViewElement, { className: "amplify-modal__body" }, content)),
1281
+ React__namespace["default"].createElement(ViewElement, { className: "amplify-modal__footer" },
1282
+ React__namespace["default"].createElement(ButtonElement, { className: "amplify-modal__cancel", onClick: onCancel, variant: "cancel" }, cancelLabel),
1283
+ React__namespace["default"].createElement(ButtonElement, { className: "amplify-modal__confirm", onClick: onConfirm, variant: "primary" }, confirmLabel)))));
1284
+ };
1285
+
1181
1286
  const ActionDestination$1 = ({ isNavigable, items, label, }) => {
1182
1287
  if (!items.length) {
1183
1288
  return null;
@@ -1709,10 +1814,10 @@ function useCredentialsStore({ getLocationCredentials: handler, initialValue, on
1709
1814
  return store;
1710
1815
  }
1711
1816
 
1712
- const ERROR_MESSAGE$2 = '`useCredentials` must be called from within a `CredentialsProvider`.';
1817
+ const ERROR_MESSAGE$3 = '`useCredentials` must be called from within a `CredentialsProvider`.';
1713
1818
  const { useCredentials, CredentialsContext } = uiReactCore.createContextUtilities({
1714
1819
  contextName: 'Credentials',
1715
- errorMessage: ERROR_MESSAGE$2,
1820
+ errorMessage: ERROR_MESSAGE$3,
1716
1821
  });
1717
1822
  function CredentialsProvider({ children, ...props }) {
1718
1823
  const initialValue = React__namespace["default"].useContext(CredentialsContext);
@@ -1804,7 +1909,7 @@ const DEPRECATED_PROP_KEYS = ['actionType', 'location', 'path'];
1804
1909
  const template = (key, index, values) => `\`${key}\`${index < values.length - 1 ? ', ' : ''}`;
1805
1910
  const getMissingLocationKeys = (location) => location === null
1806
1911
  ? []
1807
- : REQUIRED_LOCATION_KEYS.filter((key) => !location[key]);
1912
+ : REQUIRED_LOCATION_KEYS.filter((key) => location[key] === undefined || location[key] === null);
1808
1913
  let didWarnConflictingBehavior = false;
1809
1914
  let didWarnDeprecatedAndConflictingProps = false;
1810
1915
  let didWarnDeprecatedProps = false;
@@ -1912,10 +2017,10 @@ function useGetActionInputCallback({ accountId, customEndpoint, region, }) {
1912
2017
  }, [accountId, current, customEndpoint, getCredentials, key, region]);
1913
2018
  }
1914
2019
 
1915
- const ERROR_MESSAGE$1 = '`useGetActionInput` must be called from within a `ConfigurationProvider`.';
2020
+ const ERROR_MESSAGE$2 = '`useGetActionInput` must be called from within a `ConfigurationProvider`.';
1916
2021
  const { useGetActionInput, GetActionInputContext } = uiReactCore.createContextUtilities({
1917
2022
  contextName: 'GetActionInput',
1918
- errorMessage: ERROR_MESSAGE$1,
2023
+ errorMessage: ERROR_MESSAGE$2,
1919
2024
  });
1920
2025
  function GetActionInputProvider({ accountId, children, customEndpoint, region, }) {
1921
2026
  const value = useGetActionInputCallback({
@@ -1937,6 +2042,27 @@ function createConfigurationProvider(input) {
1937
2042
  return Provider;
1938
2043
  }
1939
2044
 
2045
+ const DEFAULT_PAGE_SIZE$1 = 100;
2046
+ const validatePageSize = (pageSize) => {
2047
+ if (pageSize === undefined ||
2048
+ pageSize === null ||
2049
+ !Number.isInteger(pageSize) ||
2050
+ pageSize < 1) {
2051
+ return DEFAULT_PAGE_SIZE$1;
2052
+ }
2053
+ return pageSize;
2054
+ };
2055
+
2056
+ const ERROR_MESSAGE$1 = '`usePaginationConfig` must be called from within a `PaginationConfigProvider`.';
2057
+ const { usePaginationConfig, PaginationConfigContext } = uiReactCore.createContextUtilities({
2058
+ contextName: 'PaginationConfig',
2059
+ errorMessage: ERROR_MESSAGE$1,
2060
+ });
2061
+ function PaginationConfigProvider({ children, pageSize, }) {
2062
+ const value = React__namespace["default"].useMemo(() => ({ pageSize: validatePageSize(pageSize) }), [pageSize]);
2063
+ return (React__namespace["default"].createElement(PaginationConfigContext.Provider, { value: value }, children));
2064
+ }
2065
+
1940
2066
  const DEFAULT_ACTION_CONCURRENCY = 4;
1941
2067
  const USE_LIST_ERROR_MESSAGE = '`useList` must be called from within `StorageBrowser.Provider`';
1942
2068
 
@@ -2065,16 +2191,19 @@ function useProcessTasks(handler, options) {
2065
2191
  const getTask = () => tasksRef.current.get(data.id);
2066
2192
  const { options } = _input;
2067
2193
  const { onProgress: _onProgress } = options ?? {};
2068
- const onProgress = ({ id }, progress, status = 'PENDING') => {
2194
+ const onProgress = ({ id }, progressDetails, status = 'PENDING') => {
2195
+ const isNumber = typeof progressDetails === 'number';
2069
2196
  const task = updateTask(id, {
2070
- progress,
2197
+ progress: isNumber ? progressDetails : progressDetails.progress,
2198
+ failureCount: isNumber ? undefined : progressDetails.failureCount,
2199
+ successCount: isNumber ? undefined : progressDetails.successCount,
2071
2200
  status,
2072
2201
  });
2073
2202
  if (task && ui.isFunction(onTaskProgress)) {
2074
- onTaskProgress(task, progress);
2203
+ onTaskProgress(task, progressDetails);
2075
2204
  }
2076
2205
  if (task && ui.isFunction(_onProgress)) {
2077
- _onProgress(data, progress, status);
2206
+ _onProgress(data, progressDetails, status);
2078
2207
  }
2079
2208
  };
2080
2209
  const input = { ..._input, data, all, options: { ...options, onProgress } };
@@ -2284,6 +2413,7 @@ const DEFAULT_ACTION_VIEW_DISPLAY_TEXT = {
2284
2413
  tableColumnNameHeader: 'Name',
2285
2414
  tableColumnTypeHeader: 'Type',
2286
2415
  tableColumnSizeHeader: 'Size',
2416
+ tableColumnProgressHeader: 'Progress',
2287
2417
  };
2288
2418
  const DEFAULT_LIST_VIEW_DISPLAY_TEXT = {
2289
2419
  loadingIndicatorLabel: 'Loading',
@@ -2376,21 +2506,133 @@ const DEFAULT_COPY_VIEW_DISPLAY_TEXT = {
2376
2506
  searchClearLabel: 'Clear search',
2377
2507
  };
2378
2508
 
2509
+ const pluralize = (count, word) => count === 1 ? word : `${word}s`;
2510
+ const formatCount = (count, word) => `${count === 1 ? '' : 'All '}${count} ${pluralize(count, word)}`;
2379
2511
  const DEFAULT_DELETE_VIEW_DISPLAY_TEXT = {
2380
2512
  ...DEFAULT_ACTION_VIEW_DISPLAY_TEXT,
2381
2513
  title: 'Delete',
2382
2514
  actionStartLabel: 'Delete',
2515
+ confirmationModalTitle: 'Confirm Deletion',
2516
+ confirmationModalConfirmLabel: 'Delete',
2517
+ confirmationModalCancelLabel: 'Cancel',
2518
+ confirmationModalMessage: 'The items that will be deleted contain {count} folder{plural}',
2519
+ confirmationModalFolderListTitle: 'Folder list:',
2383
2520
  getActionCompleteMessage: (data) => {
2384
- const { counts } = data ?? {};
2385
- const { COMPLETE, FAILED, TOTAL } = counts ?? {};
2521
+ const { counts, tasks } = data ?? {};
2522
+ const { COMPLETE = 0, FAILED = 0, TOTAL = 0 } = counts ?? {};
2523
+ if (!TOTAL || TOTAL === 0) {
2524
+ return { content: 'No items to delete.', type: 'info' };
2525
+ }
2526
+ if (tasks && tasks.length > 0) {
2527
+ const folderTasks = tasks.filter((task) => task.data.type === 'FOLDER');
2528
+ const fileTasks = tasks.filter((task) => task.data.type === 'FILE');
2529
+ const completeFolders = folderTasks.filter((task) => task.status === 'COMPLETE').length;
2530
+ const failedFolders = folderTasks.filter((task) => task.status === 'FAILED').length;
2531
+ const completeFiles = fileTasks.filter((task) => task.status === 'COMPLETE').length;
2532
+ const failedFiles = fileTasks.filter((task) => task.status === 'FAILED').length;
2533
+ const hasFolders = folderTasks.length > 0;
2534
+ const hasFiles = fileTasks.length > 0;
2535
+ const isMixed = hasFolders && hasFiles;
2536
+ // All successful
2537
+ if (COMPLETE === TOTAL) {
2538
+ if (isMixed) {
2539
+ return {
2540
+ content: `${formatCount(completeFolders, 'folder')} and ${completeFiles} ${pluralize(completeFiles, 'file')} deleted successfully.`,
2541
+ type: 'success',
2542
+ };
2543
+ }
2544
+ else if (hasFolders) {
2545
+ return {
2546
+ content: `${formatCount(completeFolders, 'folder')} deleted successfully.`,
2547
+ type: 'success',
2548
+ };
2549
+ }
2550
+ else {
2551
+ return {
2552
+ content: `${formatCount(completeFiles, 'file')} deleted successfully.`,
2553
+ type: 'success',
2554
+ };
2555
+ }
2556
+ }
2557
+ // Complete failure
2558
+ if (FAILED === TOTAL) {
2559
+ if (isMixed) {
2560
+ return {
2561
+ content: `Failed to delete ${failedFolders} ${pluralize(failedFolders, 'folder')} and ${failedFiles} ${pluralize(failedFiles, 'file')}. Some contents may have been deleted.`,
2562
+ type: 'error',
2563
+ };
2564
+ }
2565
+ else if (hasFolders) {
2566
+ return {
2567
+ content: `Failed to delete ${failedFolders} ${pluralize(failedFolders, 'folder')}. Some items may have been deleted.`,
2568
+ type: 'error',
2569
+ };
2570
+ }
2571
+ else {
2572
+ return {
2573
+ content: `Failed to delete ${failedFiles} ${pluralize(failedFiles, 'file')}.`,
2574
+ type: 'error',
2575
+ };
2576
+ }
2577
+ }
2578
+ // Partial failure
2579
+ if (isMixed) {
2580
+ const messages = [];
2581
+ if (completeFiles > 0) {
2582
+ messages.push(`${completeFiles} ${pluralize(completeFiles, 'file')} deleted`);
2583
+ }
2584
+ if (completeFolders > 0) {
2585
+ messages.push(`${completeFolders} ${pluralize(completeFolders, 'folder')} deleted`);
2586
+ }
2587
+ if (failedFiles > 0) {
2588
+ messages.push(`${failedFiles} ${pluralize(failedFiles, 'file')} failed`);
2589
+ }
2590
+ if (failedFolders > 0) {
2591
+ messages.push(`${failedFolders} ${pluralize(failedFolders, 'folder')} failed`);
2592
+ }
2593
+ return {
2594
+ content: messages.join(', ') + '. Some items may have been deleted.',
2595
+ type: 'error',
2596
+ };
2597
+ }
2598
+ else if (hasFolders) {
2599
+ // Folders only partial failure
2600
+ if (completeFolders > 0) {
2601
+ return {
2602
+ content: `${completeFolders} ${pluralize(completeFolders, 'folder')} deleted, ${failedFolders} ${pluralize(failedFolders, 'folder')} failed. Some items may have been deleted.`,
2603
+ type: 'error',
2604
+ };
2605
+ }
2606
+ else {
2607
+ return {
2608
+ content: `Failed to delete ${failedFolders} ${pluralize(failedFolders, 'folder')}. Some items may have been deleted.`,
2609
+ type: 'error',
2610
+ };
2611
+ }
2612
+ }
2613
+ else {
2614
+ // Files only partial failure
2615
+ return {
2616
+ content: `${completeFiles} ${pluralize(completeFiles, 'file')} deleted, ${failedFiles} ${pluralize(failedFiles, 'file')} failed.`,
2617
+ type: 'error',
2618
+ };
2619
+ }
2620
+ }
2621
+ // Fallback to generic messaging if tasks not available
2386
2622
  if (COMPLETE === TOTAL) {
2387
- return { content: 'All files deleted.', type: 'success' };
2623
+ return {
2624
+ content: `${formatCount(TOTAL, 'item')} deleted successfully.`,
2625
+ type: 'success',
2626
+ };
2388
2627
  }
2389
2628
  if (FAILED === TOTAL) {
2390
- return { content: 'All files failed to delete.', type: 'error' };
2629
+ return {
2630
+ content: `Failed to delete ${formatCount(TOTAL, 'item')}.`,
2631
+ type: 'error',
2632
+ };
2391
2633
  }
2392
2634
  return {
2393
- content: `${COMPLETE} files deleted, ${FAILED} files failed to delete.`,
2635
+ content: `${COMPLETE} ${pluralize(COMPLETE, 'item')} deleted, ${FAILED} ${pluralize(FAILED, 'item')} failed to delete.`,
2394
2636
  type: 'error',
2395
2637
  };
2396
2638
  },
@@ -2465,6 +2707,7 @@ const DEFAULT_LOCATION_DETAIL_VIEW_DISPLAY_TEXT = {
2465
2707
  tableColumnSizeHeader: 'Size',
2466
2708
  tableColumnTypeHeader: 'Type',
2467
2709
  selectFileLabel: 'Select file',
2710
+ selectFolderLabel: 'Select folder',
2468
2711
  selectAllFilesLabel: 'Select all files',
2469
2712
  getActionListItemLabel: (key = '') => {
2470
2713
  switch (key) {
@@ -3287,6 +3530,7 @@ const Title$1 = ({ title }) => (React__namespace["default"].createElement(Headin
3287
3530
 
3288
3531
  const DEFAULT_COMPOSABLES = {
3289
3532
  ActionCancel,
3533
+ ActionConfirmationModal,
3290
3534
  ActionDestination: ActionDestination$1,
3291
3535
  ActionExit,
3292
3536
  ActionStart,
@@ -3477,6 +3721,26 @@ const ActionCancelControl = () => {
3477
3721
  return React__namespace["default"].createElement(Resolved, { ...props });
3478
3722
  };
3479
3723
 
3724
+ const useActionConfirmationModal = () => {
3725
+ const { data: { confirmationModal = {} }, onConfirmationModalConfirm = () => { }, onConfirmationModalCancel = () => { }, } = useControlsContext();
3726
+ return {
3727
+ isOpen: false,
3728
+ title: 'Confirm Action',
3729
+ message: '',
3730
+ confirmLabel: 'Confirm',
3731
+ cancelLabel: 'Cancel',
3732
+ ...confirmationModal,
3733
+ onConfirm: onConfirmationModalConfirm,
3734
+ onCancel: onConfirmationModalCancel,
3735
+ };
3736
+ };
3737
+
3738
+ const ActionConfirmationModalControl = () => {
3739
+ const props = useActionConfirmationModal();
3740
+ const Resolved = useResolvedComposable(ActionConfirmationModal, 'ActionConfirmationModal');
3741
+ return React__namespace["default"].createElement(Resolved, { ...props });
3742
+ };
3743
+
3480
3744
  const getNavigationItems = ({ destinationParts, location, onNavigate, }) => {
3481
3745
  const { bucket, permissions, prefix = '', type } = location;
3482
3746
  const destinationSubpaths = [];
@@ -4011,9 +4275,18 @@ const FILE_DATA_ITEM_TABLE_KEYS = [
4011
4275
  'status',
4012
4276
  'cancel',
4013
4277
  ];
4278
+ const DELETE_TABLE_KEYS = [
4279
+ 'name',
4280
+ 'folder',
4281
+ 'type',
4282
+ 'size',
4283
+ 'status',
4284
+ 'progress',
4285
+ 'cancel',
4286
+ ];
4014
4287
 
4015
- const getFileType = (value, fallback = '') => value.lastIndexOf('.') !== -1
4016
- ? value.slice(value.lastIndexOf('.') + 1)
4288
+ const getFileType = (value, fallback = '') => value?.lastIndexOf?.('.') !== -1
4289
+ ? value?.slice(value?.lastIndexOf?.('.') + 1)
4017
4290
  : fallback;
4018
4291
  const getCellName = (value) =>
4019
4292
  // `value.split` always returns an array with at least one entry
@@ -4028,7 +4301,7 @@ const getFileDataCellFolder = (task) => {
4028
4301
  ? task.data.sourceKey
4029
4302
  : task.data.key;
4030
4303
  const { fileKey } = task.data;
4031
- return targetKey.slice(0, -fileKey.length);
4304
+ return targetKey.slice(0, -fileKey?.length);
4032
4305
  };
4033
4306
  const getUploadCellProgress = ({ progress, status, }) => {
4034
4307
  // prefer `progress` if available, 1 if status is complete, default 0
@@ -4073,31 +4346,31 @@ const getFileDataCancelCellContent = (data) => {
4073
4346
  * Generates a unique key for a table cell based on the key and item id
4074
4347
  */
4075
4348
  const getFileDataCellKey = ({ key, item, }) => `${key}-${item.data.id}`;
4076
- const name$2 = (data) => {
4349
+ const name$3 = (data) => {
4077
4350
  const key = getFileDataCellKey(data);
4078
4351
  const { item } = data;
4079
4352
  const text = item.data.fileKey;
4080
4353
  const icon = STATUS_ICONS[item.status];
4081
4354
  return { key, type: 'text', content: { icon, text } };
4082
4355
  };
4083
- const folder$2 = (data) => {
4356
+ const folder$3 = (data) => {
4084
4357
  const key = getFileDataCellKey(data);
4085
4358
  const text = getFileDataCellFolder(data.item);
4086
4359
  return { key, type: 'text', content: { text } };
4087
4360
  };
4088
- const type$2 = (data) => {
4361
+ const type$3 = (data) => {
4089
4362
  const key = getFileDataCellKey(data);
4090
4363
  const { fileKey } = data.item.data;
4091
4364
  const text = getFileType(fileKey);
4092
4365
  return { key, type: 'text', content: { text } };
4093
4366
  };
4094
- const size$2 = (data) => {
4367
+ const size$3 = (data) => {
4095
4368
  const key = getFileDataCellKey(data);
4096
4369
  const { size: value } = data.item.data;
4097
4370
  const displayValue = getFileSize(value);
4098
4371
  return { key, type: 'number', content: { value, displayValue } };
4099
4372
  };
4100
- const cancel$2 = (data) => {
4373
+ const cancel$3 = (data) => {
4101
4374
  const key = getFileDataCellKey(data);
4102
4375
  const content = getFileDataCancelCellContent(data);
4103
4376
  return { key, type: 'button', content };
@@ -4113,12 +4386,12 @@ const status$3 = (data) => {
4113
4386
  return { key, type: 'text', content: { text } };
4114
4387
  };
4115
4388
  const COPY_CELL_RESOLVERS = {
4116
- name: name$2,
4117
- folder: folder$2,
4118
- type: type$2,
4119
- size: size$2,
4389
+ name: name$3,
4390
+ folder: folder$3,
4391
+ type: type$3,
4392
+ size: size$3,
4120
4393
  status: status$3,
4121
- cancel: cancel$2,
4394
+ cancel: cancel$3,
4122
4395
  /**
4123
4396
  * @deprecated
4124
4397
  *
@@ -4140,8 +4413,160 @@ const COPY_TABLE_RESOLVERS = {
4140
4413
  getRowKey: ({ item }) => item.data.id,
4141
4414
  };
4142
4415
 
4416
+ const getFolderName = (folderKey) => {
4417
+ return folderKey.replace(/\/$/, '').split('/').pop() ?? folderKey;
4418
+ };
4419
+ /**
4420
+ * Creates JSX content showing a list of folders to be deleted
4421
+ */
4422
+ const createFolderListContent = (folders, folderListTitle) => {
4423
+ return (React__namespace["default"].createElement(React__namespace["default"].Fragment, null,
4424
+ React__namespace["default"].createElement(TextElement, { className: "amplify-modal__list-title" },
4425
+ React__namespace["default"].createElement("strong", null, folderListTitle)),
4426
+ React__namespace["default"].createElement(UnorderedListElement, { className: "amplify-modal__list" }, folders.map((folder) => (React__namespace["default"].createElement(ListItemElement, { key: folder.id, className: "amplify-modal__list-item" }, getFolderName(folder.key)))))));
4427
+ };
4428
+ const createDeleteConfirmationModalProps = ({ items, showConfirmation, displayText, }) => {
4429
+ const folders = getSelectedFolders(items);
4430
+ const folderCount = folders.length;
4431
+ return {
4432
+ isOpen: showConfirmation,
4433
+ title: displayText.confirmationModalTitle,
4434
+ message: displayText.confirmationModalMessage
4435
+ .replace('{count}', folderCount.toString())
4436
+ .replace('{plural}', folderCount !== 1 ? 's' : ''),
4437
+ confirmLabel: displayText.confirmationModalConfirmLabel,
4438
+ cancelLabel: displayText.confirmationModalCancelLabel,
4439
+ content: createFolderListContent(folders, displayText.confirmationModalFolderListTitle),
4440
+ };
4441
+ };
4442
+ /**
4443
+ * Maximum number of files to count before showing "+" notation
4444
+ * This prevents expensive operations on very large folders
4445
+ */
4446
+ const MAX_FILE_COUNT_LIMIT = 5000;
4447
+ const LIST_PAGE_SIZE = 1000;
4448
+ /**
4449
+ * Count the total number of files in a folder with pagination and limits
4450
+ * @param folderKey - The folder path to count files in
4451
+ * @param config - Storage configuration
4452
+ * @returns Promise<number | string> - File count or "5000+" if exceeds limit
4453
+ */
4454
+ const countFilesInFolder = async (folderKey, config) => {
4455
+ try {
4456
+ const { accountId, credentials, customEndpoint } = config;
4457
+ const bucket = constructBucket$1(config);
4458
+ let fileCount = 0;
4459
+ let nextToken;
4460
+ let hasMoreItems = true;
4461
+ while (hasMoreItems && fileCount < MAX_FILE_COUNT_LIMIT) {
4462
+ const { items, nextToken: listNextToken } = await internals.list({
4463
+ path: folderKey,
4464
+ options: {
4465
+ bucket,
4466
+ locationCredentialsProvider: credentials,
4467
+ expectedBucketOwner: accountId,
4468
+ customEndpoint,
4469
+ pageSize: LIST_PAGE_SIZE,
4470
+ nextToken,
4471
+ },
4472
+ });
4473
+ const batchFileCount = items.filter((item) => !item.path.endsWith('/')).length;
4474
+ fileCount += batchFileCount;
4475
+ nextToken = listNextToken;
4476
+ hasMoreItems = !!nextToken;
4477
+ if (fileCount >= MAX_FILE_COUNT_LIMIT) {
4478
+ return `${MAX_FILE_COUNT_LIMIT}+`;
4479
+ }
4480
+ }
4481
+ if (hasMoreItems) {
4482
+ return `${fileCount}+`;
4483
+ }
4484
+ return fileCount;
4485
+ }
4486
+ catch (error) {
4487
+ return 0;
4488
+ }
4489
+ };
4490
+
4491
+ const getDeleteCellKey = (data) => `${data.key}-${data.item.data.id}`;
4492
+ const getCellDataFolder = (data) => {
4493
+ const { type, key } = data;
4494
+ const fileKey = getFileKey(key);
4495
+ const targetKey = data.key;
4496
+ if (type === 'FOLDER') {
4497
+ const pathWithoutTrailingSlash = targetKey.replace(/\/$/, '');
4498
+ const lastSlashIndex = pathWithoutTrailingSlash.lastIndexOf('/');
4499
+ return lastSlashIndex >= 0
4500
+ ? pathWithoutTrailingSlash.slice(0, lastSlashIndex + 1)
4501
+ : '';
4502
+ }
4503
+ return targetKey.slice(0, -fileKey?.length);
4504
+ };
4505
+ const name$2 = (data) => {
4506
+ const key = getDeleteCellKey(data);
4507
+ const { item } = data;
4508
+ let text;
4509
+ if (item.data.type === 'FOLDER') {
4510
+ text = `${getFolderName(item.data.key)}/`;
4511
+ }
4512
+ else {
4513
+ text = item.data.fileKey ?? getCellName(item.data.key);
4514
+ }
4515
+ const icon = STATUS_ICONS[item.status];
4516
+ return { key, type: 'text', content: { icon, text } };
4517
+ };
4518
+ const folder$2 = (data) => {
4519
+ const key = getDeleteCellKey(data);
4520
+ const text = getCellDataFolder(data.item.data);
4521
+ return { key, type: 'text', content: { text } };
4522
+ };
4523
+ const type$2 = (data) => {
4524
+ const key = getDeleteCellKey(data);
4525
+ if (data.item.data.type === 'FOLDER') {
4526
+ return { key, type: 'text', content: { text: 'Folder' } };
4527
+ }
4528
+ const text = getFileType(data.item.data.key);
4529
+ return { key, type: 'text', content: { text } };
4530
+ };
4531
+ const size$2 = (data) => {
4532
+ const key = getDeleteCellKey(data);
4533
+ const itemData = data.item.data;
4534
+ if (data.item.data.type === 'FOLDER') {
4535
+ return { key, type: 'text', content: { text: '-' } };
4536
+ }
4537
+ const value = 'size' in itemData ? itemData.size : 0;
4538
+ const displayValue = getFileSize(value);
4539
+ return { key, type: 'number', content: { value, displayValue } };
4540
+ };
4541
+ const getCancelCellContent = (data) => {
4542
+ const { item, props } = data;
4543
+ const { cancel, status } = item;
4544
+ const { isProcessing, onTaskRemove } = props;
4545
+ const isQueued = status === 'QUEUED';
4546
+ const isRemovable = isQueued && !isProcessing;
4547
+ const isCancelable = isProcessing && !!cancel;
4548
+ const itemAriaValue = getCellName(item.data.fileKey ?? item.data.key);
4549
+ const ariaLabel = `${isRemovable ? 'Remove' : 'Cancel'} item: ${itemAriaValue}`;
4550
+ const isDisabled = !isRemovable && !isCancelable;
4551
+ const onClick = !isCancelable && !isRemovable
4552
+ ? undefined
4553
+ : () => {
4554
+ if (isRemovable) {
4555
+ onTaskRemove?.(item);
4556
+ return;
4557
+ }
4558
+ if (isCancelable)
4559
+ cancel();
4560
+ };
4561
+ return { ariaLabel, isDisabled, onClick, icon: 'cancel' };
4562
+ };
4563
+ const cancel$2 = (data) => {
4564
+ const key = getDeleteCellKey(data);
4565
+ const content = getCancelCellContent(data);
4566
+ return { key, type: 'button', content };
4567
+ };
4143
4568
  const status$2 = (data) => {
4144
- const key = getFileDataCellKey(data);
4569
+ const key = getDeleteCellKey(data);
4145
4570
  const { item: { status }, props: { displayText }, } = data;
4146
4571
  const statusLabelKey = STATUS_LABELS[status];
4147
4572
  const text = isDeleteViewDisplayTextKey(statusLabelKey)
@@ -4149,21 +4574,47 @@ const status$2 = (data) => {
4149
4574
  : '';
4150
4575
  return { key, type: 'text', content: { text } };
4151
4576
  };
4577
+ const progress$2 = (data) => {
4578
+ const key = getDeleteCellKey(data);
4579
+ const { item } = data;
4580
+ const itemIsFile = item.data.type === 'FILE';
4581
+ if (itemIsFile) {
4582
+ const text = item.status === 'COMPLETE' ? 'Deleted' : '-';
4583
+ return { key, type: 'text', content: { text } };
4584
+ }
4585
+ if ((item.status === 'PENDING' ||
4586
+ item.status === 'FAILED' ||
4587
+ item.status === 'CANCELED') &&
4588
+ item.data.totalCount !== undefined) {
4589
+ const countDisplay = item.data.totalCount ?? '?';
4590
+ const text = `${item.successCount ?? 0}/${countDisplay} files`;
4591
+ return { key, type: 'text', content: { text } };
4592
+ }
4593
+ else if (item.status === 'COMPLETE') {
4594
+ if (item.data.totalCount === null) {
4595
+ const text = `${item.successCount ?? 0} files deleted`;
4596
+ return { key, type: 'text', content: { text } };
4597
+ }
4598
+ const text = `${item.data.totalCount} files deleted`;
4599
+ return { key, type: 'text', content: { text } };
4600
+ }
4601
+ else {
4602
+ const text = item.data.totalCount === null
4603
+ ? 'Count failed'
4604
+ : item.data.totalCount !== undefined
4605
+ ? `${item.data.totalCount} files`
4606
+ : 'Calculating...';
4607
+ return { key, type: 'text', content: { text } };
4608
+ }
4609
+ };
4152
4610
  const DELETE_CELL_RESOLVERS = {
4153
4611
  name: name$2,
4154
4612
  folder: folder$2,
4155
4613
  type: type$2,
4156
4614
  size: size$2,
4157
4615
  status: status$2,
4616
+ progress: progress$2,
4158
4617
  cancel: cancel$2,
4159
- /**
4160
- * @deprecated
4161
- *
4162
- * non-upload view tables do not include "progress" headers but include here to
4163
- * keep TS happy as "progress" headers were included in display text interfaces
4164
- * and cannot be removed from the tables without a breaking change
4165
- */
4166
- progress: ui.noop,
4167
4618
  };
4168
4619
  const DELETE_TABLE_RESOLVERS = {
4169
4620
  getCell: (data) => DELETE_CELL_RESOLVERS[data.key](data),
@@ -4564,8 +5015,7 @@ function FileItemsProvider({ children, validateFile, }) {
4564
5015
 
4565
5016
  const DEFAULT_OVERWRITE_ENABLED = false;
4566
5017
 
4567
- const DEFAULT_PAGE_SIZE$4 = 100;
4568
- const usePaginate = ({ items, onPaginate, page = 1, pageSize = DEFAULT_PAGE_SIZE$4, }) => {
5018
+ const usePaginate = ({ items, onPaginate, page = 1, pageSize, }) => {
4569
5019
  const [currentPage, setCurrentPage] = React__namespace["default"].useState(page);
4570
5020
  const visitedRef = React__namespace["default"].useRef(page);
4571
5021
  const handleReset = React__namespace["default"].useRef(() => {
@@ -4575,19 +5025,31 @@ const usePaginate = ({ items, onPaginate, page = 1, pageSize = DEFAULT_PAGE_SIZE
4575
5025
  }).current;
4576
5026
  return React__namespace["default"].useMemo(() => {
4577
5027
  const hasItems = Array.isArray(items);
5028
+ const totalItems = hasItems ? items.length : 0;
5029
+ const totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
5030
+ // Auto-adjust current page if it's beyond available pages
5031
+ // For server-side pagination, don't auto-adjust if onPaginate callback is provided
5032
+ let adjustedCurrentPage = currentPage;
5033
+ if (currentPage > totalPages && !ui.isFunction(onPaginate)) {
5034
+ adjustedCurrentPage = totalPages;
5035
+ setCurrentPage(adjustedCurrentPage);
5036
+ }
4578
5037
  const highestPageVisited = visitedRef.current;
4579
- const isFirstPage = currentPage === 1;
4580
- const start = isFirstPage ? 0 : (currentPage - 1) * pageSize;
4581
- const end = isFirstPage ? pageSize : currentPage * pageSize;
5038
+ const isFirstPage = adjustedCurrentPage === 1;
5039
+ const start = isFirstPage ? 0 : (adjustedCurrentPage - 1) * pageSize;
5040
+ const end = isFirstPage ? pageSize : adjustedCurrentPage * pageSize;
4582
5041
  const pageItems = hasItems ? items.slice(start, end) : [];
4583
5042
  return {
4584
- currentPage,
5043
+ currentPage: adjustedCurrentPage,
4585
5044
  handlePaginate: (page) => {
4586
5045
  if (page < 1)
4587
5046
  return;
5047
+ // For server-side pagination, allow going beyond totalPages if onPaginate is provided
5048
+ if (page > totalPages && !ui.isFunction(onPaginate))
5049
+ return;
4588
5050
  if (ui.isFunction(onPaginate))
4589
5051
  onPaginate(page);
4590
- if (page > currentPage && page > highestPageVisited)
5052
+ if (page > adjustedCurrentPage && page > highestPageVisited)
4591
5053
  visitedRef.current = page;
4592
5054
  setCurrentPage(page);
4593
5055
  },
@@ -4598,9 +5060,10 @@ const usePaginate = ({ items, onPaginate, page = 1, pageSize = DEFAULT_PAGE_SIZE
4598
5060
  }, [currentPage, handleReset, items, onPaginate, pageSize]);
4599
5061
  };
4600
5062
 
4601
- const DEFAULT_PAGE_SIZE$3 = 100;
4602
5063
  const useUploadView = (options) => {
4603
- const { onExit: _onExit } = options ?? {};
5064
+ const { pageSize: configPageSize } = usePaginationConfig();
5065
+ const { onExit: _onExit, pageSize: propPageSize } = options ?? {};
5066
+ const pageSize = propPageSize ?? configPageSize;
4604
5067
  const [{ location }, storeDispatch] = useStore();
4605
5068
  const [{ validItems, invalidItems: invalidFiles }, fileItemsDispatch] = useFileItems();
4606
5069
  const { current } = location;
@@ -4613,6 +5076,7 @@ const useUploadView = (options) => {
4613
5076
  const [{ isProcessing, isProcessingComplete, statusCounts, tasks }, handleUploads,] = useAction('upload', { items });
4614
5077
  const { currentPage, handlePaginate, pageItems: pageTasks, } = usePaginate({
4615
5078
  items: tasks,
5079
+ pageSize,
4616
5080
  });
4617
5081
  const onDropFiles = (files) => {
4618
5082
  if (files) {
@@ -4650,8 +5114,8 @@ const useUploadView = (options) => {
4650
5114
  statusCounts,
4651
5115
  tasks: pageTasks,
4652
5116
  page: currentPage,
4653
- hasNextPage: currentPage * DEFAULT_PAGE_SIZE$3 < items.length,
4654
- highestPageVisited: Math.ceil(items.length / DEFAULT_PAGE_SIZE$3),
5117
+ hasNextPage: currentPage * pageSize < items.length,
5118
+ highestPageVisited: Math.ceil(items.length / pageSize),
4655
5119
  onActionCancel,
4656
5120
  onActionExit,
4657
5121
  onActionStart,
@@ -4665,7 +5129,7 @@ const useUploadView = (options) => {
4665
5129
 
4666
5130
  const UploadView = ({ className, ...props }) => {
4667
5131
  const state = useUploadView(props);
4668
- return (React__namespace["default"].createElement(ViewElement, { className: ui.classNames(STORAGE_BROWSER_BLOCK, className) },
5132
+ return (React__namespace["default"].createElement(ViewElement, { className: ui.classNames(STORAGE_BROWSER_BLOCK, className), "data-testid": "UPLOAD_VIEW" },
4669
5133
  React__namespace["default"].createElement(UploadViewProvider, { ...state },
4670
5134
  React__namespace["default"].createElement(ActionExitControl, null),
4671
5135
  React__namespace["default"].createElement(TitleControl, null),
@@ -4920,6 +5384,7 @@ function CopyViewProvider({ children, ...props }) {
4920
5384
  }
4921
5385
 
4922
5386
  const DEFAULT_STATE = {
5387
+ dataItems: undefined,
4923
5388
  fileDataItems: undefined,
4924
5389
  };
4925
5390
  const locationItemsReducer = (prevState, event) => {
@@ -4928,27 +5393,29 @@ const locationItemsReducer = (prevState, event) => {
4928
5393
  const { items } = event;
4929
5394
  if (!items?.length)
4930
5395
  return prevState;
4931
- if (!prevState.fileDataItems?.length) {
4932
- return { fileDataItems: items.map(createFileDataItem) };
4933
- }
4934
- const nextFileDataItems = items?.reduce((fileDataItems, data) => prevState.fileDataItems?.some(({ id }) => id === data.id)
4935
- ? fileDataItems
4936
- : fileDataItems.concat(createFileDataItem(data)), []);
4937
- if (!nextFileDataItems?.length)
4938
- return prevState;
5396
+ const nextDataItems = !prevState.dataItems?.length
5397
+ ? items
5398
+ : prevState.dataItems.concat(items.filter((data) => !prevState.dataItems?.some(({ id }) => id === data.id)));
5399
+ const fileItems = items.filter((item) => item.type === 'FILE');
5400
+ const nextFileDataItems = !prevState.fileDataItems?.length
5401
+ ? fileItems.map(createFileDataItem)
5402
+ : prevState.fileDataItems.concat(fileItems
5403
+ .filter((data) => !prevState.fileDataItems?.some(({ id }) => id === data.id))
5404
+ .map(createFileDataItem));
4939
5405
  return {
4940
- fileDataItems: prevState.fileDataItems.concat(nextFileDataItems),
5406
+ dataItems: nextDataItems,
5407
+ fileDataItems: nextFileDataItems,
4941
5408
  };
4942
5409
  }
4943
5410
  case 'REMOVE_LOCATION_ITEM': {
4944
5411
  const { id } = event;
4945
- if (!prevState.fileDataItems)
4946
- return prevState;
4947
- const fileDataItems = prevState.fileDataItems.filter((item) => item.id !== id);
4948
- if (fileDataItems.length === prevState.fileDataItems.length) {
5412
+ const dataItems = prevState.dataItems?.filter((item) => item.id !== id);
5413
+ const fileDataItems = prevState.fileDataItems?.filter((item) => item.id !== id);
5414
+ if (dataItems?.length === prevState.dataItems?.length &&
5415
+ fileDataItems?.length === prevState.fileDataItems?.length) {
4949
5416
  return prevState;
4950
5417
  }
4951
- return { fileDataItems };
5418
+ return { dataItems, fileDataItems };
4952
5419
  }
4953
5420
  case 'RESET_LOCATION_ITEMS': {
4954
5421
  return DEFAULT_STATE;
@@ -4986,13 +5453,13 @@ function useSearch(props) {
4986
5453
  };
4987
5454
  }
4988
5455
 
4989
- const DEFAULT_PAGE_SIZE$2 = 100;
4990
- const DEFAULT_LIST_OPTIONS$2 = {
4991
- pageSize: DEFAULT_PAGE_SIZE$2,
5456
+ const DEFAULT_PAGE_SIZE = 100;
5457
+ const DEFAULT_LIST_OPTIONS = {
5458
+ pageSize: DEFAULT_PAGE_SIZE,
4992
5459
  delimiter: '/',
4993
5460
  exclude: 'FILE',
4994
5461
  };
4995
- const DEFAULT_REFRESH_OPTIONS = { ...DEFAULT_LIST_OPTIONS$2, refresh: true };
5462
+ const DEFAULT_REFRESH_OPTIONS = { ...DEFAULT_LIST_OPTIONS, refresh: true };
4996
5463
  const useFolders = ({ destination, setDestination, }) => {
4997
5464
  const { current, key } = destination;
4998
5465
  const [{ value, hasError, isLoading, message }, handleList] = useList('folderItems');
@@ -5009,19 +5476,19 @@ const useFolders = ({ destination, setDestination, }) => {
5009
5476
  return;
5010
5477
  handleList({
5011
5478
  prefix: key,
5012
- options: { ...DEFAULT_LIST_OPTIONS$2, nextToken },
5479
+ options: { ...DEFAULT_LIST_OPTIONS, nextToken },
5013
5480
  });
5014
5481
  };
5015
5482
  const { currentPage: page, handlePaginate, highestPageVisited, pageItems, handleReset, } = usePaginate({
5016
5483
  items,
5017
5484
  onPaginate,
5018
- pageSize: DEFAULT_PAGE_SIZE$2,
5485
+ pageSize: DEFAULT_PAGE_SIZE,
5019
5486
  });
5020
5487
  const onSearch = (query) => {
5021
5488
  handleReset();
5022
5489
  handleList({
5023
5490
  prefix: key,
5024
- options: { ...DEFAULT_LIST_OPTIONS$2, search: { query, filterBy: 'key' } },
5491
+ options: { ...DEFAULT_LIST_OPTIONS, search: { query, filterBy: 'key' } },
5025
5492
  });
5026
5493
  };
5027
5494
  const { onSearchSubmit, searchQuery: query, resetSearch, onSearchQueryChange: onQuery, } = useSearch({ onSearch });
@@ -5184,12 +5651,12 @@ CopyView.Title = TitleControl;
5184
5651
  function DeleteViewProvider({ children, ...props }) {
5185
5652
  const { DeleteView: displayText } = useDisplayText();
5186
5653
  const { actionCancelLabel, actionExitLabel, actionStartLabel, title, statusDisplayCanceledLabel, statusDisplayCompletedLabel, statusDisplayFailedLabel, statusDisplayQueuedLabel, getActionCompleteMessage, } = displayText;
5187
- const { isProcessing, isProcessingComplete, statusCounts, tasks: items, onActionCancel, onActionStart, onActionExit, onTaskRemove, } = props;
5654
+ const { isProcessing, isProcessingComplete, statusCounts, tasks, confirmationModal, onActionCancel, onActionStart, onActionExit, onTaskRemove, onConfirmDelete, onCancelConfirmation, } = props;
5188
5655
  const message = isProcessingComplete
5189
- ? getActionCompleteMessage({ counts: statusCounts })
5656
+ ? getActionCompleteMessage({ counts: statusCounts, tasks })
5190
5657
  : undefined;
5191
- const tableData = useResolveTableData(FILE_DATA_ITEM_TABLE_KEYS, DELETE_TABLE_RESOLVERS, {
5192
- items,
5658
+ const tableData = useResolveTableData(DELETE_TABLE_KEYS, DELETE_TABLE_RESOLVERS, {
5659
+ items: tasks,
5193
5660
  props: { displayText, isProcessing, onTaskRemove },
5194
5661
  });
5195
5662
  return (React__namespace["default"].createElement(ControlsContextProvider, { data: {
@@ -5207,24 +5674,80 @@ function DeleteViewProvider({ children, ...props }) {
5207
5674
  tableData,
5208
5675
  title,
5209
5676
  message,
5210
- }, onActionStart: onActionStart, onActionExit: onActionExit, onActionCancel: onActionCancel }, children));
5677
+ confirmationModal,
5678
+ }, onActionStart: onActionStart, onActionExit: onActionExit, onActionCancel: onActionCancel, onConfirmationModalConfirm: onConfirmDelete, onConfirmationModalCancel: onCancelConfirmation }, children));
5211
5679
  }
5212
5680
 
5213
5681
  // assign to constant to ensure referential equality
5214
5682
  const EMPTY_ITEMS$1 = [];
5215
5683
  const useDeleteView = (options) => {
5216
5684
  const { onExit: _onExit } = options ?? {};
5685
+ const { DeleteView: displayText } = useDisplayText();
5217
5686
  const [{ location }, storeDispatch] = useStore();
5218
5687
  const [locationItems, locationItemsDispatch] = useLocationItems();
5219
5688
  const { current } = location;
5220
- const { fileDataItems: items = EMPTY_ITEMS$1 } = locationItems;
5221
- const [processState, handleProcess] = useAction('delete', { items });
5689
+ const { dataItems = EMPTY_ITEMS$1 } = locationItems;
5690
+ const getConfig = useGetActionInput();
5691
+ const [itemsWithCount, setItemsWithCount] = React__namespace["default"].useState(dataItems);
5692
+ const folderCountsRef = React__namespace["default"].useRef(new Map());
5693
+ // Sync itemsWithCount with dataItems when dataItems changes (e.g., item removal)
5694
+ React__namespace["default"].useEffect(() => {
5695
+ const itemsWithAppliedCounts = dataItems.map((item) => ({
5696
+ ...item,
5697
+ totalCount: folderCountsRef.current.get(item.id),
5698
+ }));
5699
+ setItemsWithCount(itemsWithAppliedCounts);
5700
+ }, [dataItems]);
5701
+ const [processState, handleProcess] = useAction('delete', {
5702
+ items: itemsWithCount,
5703
+ });
5704
+ const [showConfirmation, setShowConfirmation] = React__namespace["default"].useState(false);
5222
5705
  const { isProcessing, isProcessingComplete, statusCounts, tasks } = processState;
5706
+ const selectionSummary = getSelectionSummary(dataItems);
5707
+ const { hasFolders } = selectionSummary;
5708
+ React__namespace["default"].useEffect(() => {
5709
+ const initializeFolderCounts = async () => {
5710
+ if (!selectionSummary.hasFolders || !current) {
5711
+ return;
5712
+ }
5713
+ const config = getConfig(current);
5714
+ const foldersToCount = dataItems.filter((item) => item.type === 'FOLDER' && !folderCountsRef.current.has(item.id));
5715
+ if (foldersToCount.length === 0) {
5716
+ return;
5717
+ }
5718
+ await Promise.all(foldersToCount.map(async (folder) => {
5719
+ try {
5720
+ const totalCount = await countFilesInFolder(folder.key, config);
5721
+ folderCountsRef.current.set(folder.id, totalCount);
5722
+ }
5723
+ catch (error) {
5724
+ folderCountsRef.current.set(folder.id, null);
5725
+ }
5726
+ }));
5727
+ setItemsWithCount((currentItems) => currentItems.map((item) => ({
5728
+ ...item,
5729
+ totalCount: folderCountsRef.current.get(item.id),
5730
+ })));
5731
+ };
5732
+ initializeFolderCounts();
5733
+ }, [current, getConfig, selectionSummary.hasFolders, dataItems]);
5223
5734
  const onActionStart = () => {
5224
5735
  if (!current)
5225
5736
  return;
5737
+ if (hasFolders) {
5738
+ setShowConfirmation(true);
5739
+ }
5740
+ else {
5741
+ handleProcess();
5742
+ }
5743
+ };
5744
+ const onConfirmDelete = () => {
5745
+ setShowConfirmation(false);
5226
5746
  handleProcess();
5227
5747
  };
5748
+ const onCancelConfirmation = () => {
5749
+ setShowConfirmation(false);
5750
+ };
5228
5751
  const onActionCancel = () => {
5229
5752
  tasks.forEach((task) => {
5230
5753
  // @TODO Fixme, calling cancel on task doesn't currently work
@@ -5243,6 +5766,11 @@ const useDeleteView = (options) => {
5243
5766
  const onTaskRemove = React__namespace["default"].useCallback(({ data }) => {
5244
5767
  locationItemsDispatch({ type: 'REMOVE_LOCATION_ITEM', id: data.id });
5245
5768
  }, [locationItemsDispatch]);
5769
+ const confirmationModal = React__namespace["default"].useMemo(() => createDeleteConfirmationModalProps({
5770
+ items: dataItems,
5771
+ showConfirmation,
5772
+ displayText,
5773
+ }), [dataItems, showConfirmation, displayText]);
5246
5774
  return {
5247
5775
  isProcessing,
5248
5776
  isProcessingComplete,
@@ -5253,6 +5781,9 @@ const useDeleteView = (options) => {
5253
5781
  onActionExit,
5254
5782
  onActionStart,
5255
5783
  onTaskRemove,
5784
+ onConfirmDelete,
5785
+ onCancelConfirmation,
5786
+ confirmationModal,
5256
5787
  };
5257
5788
  };
5258
5789
 
@@ -5271,11 +5802,13 @@ const DeleteView = ({ className, ...props }) => {
5271
5802
  React__namespace["default"].createElement(MessageControl, null)),
5272
5803
  React__namespace["default"].createElement(ViewElement, { className: `${STORAGE_BROWSER_BLOCK}__buttons` },
5273
5804
  React__namespace["default"].createElement(ActionCancelControl, null),
5274
- React__namespace["default"].createElement(ActionStartControl, null))))));
5805
+ React__namespace["default"].createElement(ActionStartControl, null))),
5806
+ React__namespace["default"].createElement(ActionConfirmationModalControl, null))));
5275
5807
  };
5276
5808
  DeleteView.displayName = 'DeleteView';
5277
5809
  DeleteView.Provider = DeleteViewProvider;
5278
5810
  DeleteView.Cancel = ActionCancelControl;
5811
+ DeleteView.ConfirmationModal = ActionConfirmationModalControl;
5279
5812
  DeleteView.Exit = ActionExitControl;
5280
5813
  DeleteView.Message = MessageControl;
5281
5814
  DeleteView.Start = ActionStartControl;
@@ -5489,34 +6022,45 @@ const getFileRowContent = ({ filePreviewEnabled, permissions, isSelected, itemLo
5489
6022
  }
5490
6023
  });
5491
6024
 
5492
- const getFolderRowContent = ({ itemSubPath, rowId, onNavigate, }) => LOCATION_DETAIL_VIEW_HEADERS.map((columnKey) => {
5493
- const key = `${columnKey}-${rowId}`;
5494
- switch (columnKey) {
5495
- case 'checkbox': {
5496
- return { key, type: 'text', content: { text: '' } };
5497
- }
5498
- case 'name': {
5499
- return {
5500
- key,
5501
- type: 'button',
5502
- content: {
5503
- icon: 'folder',
5504
- ariaLabel: itemSubPath,
5505
- label: itemSubPath,
5506
- onClick: onNavigate,
5507
- },
5508
- };
5509
- }
5510
- case 'type': {
5511
- return { key, type: 'text', content: { text: 'Folder' } };
5512
- }
5513
- case 'last-modified':
5514
- case 'size':
5515
- case 'download': {
5516
- return { key, type: 'text', content: { text: '' } };
6025
+ const getFolderRowContent = ({ itemSubPath, rowId, isSelected, selectFolderLabel, onNavigate, onSelect, }) => {
6026
+ return LOCATION_DETAIL_VIEW_HEADERS.map((columnKey) => {
6027
+ const key = `${columnKey}-${rowId}`;
6028
+ switch (columnKey) {
6029
+ case 'checkbox': {
6030
+ return {
6031
+ key,
6032
+ type: 'checkbox',
6033
+ content: {
6034
+ checked: isSelected,
6035
+ id: key,
6036
+ label: `${selectFolderLabel} ${itemSubPath}`,
6037
+ onSelect,
6038
+ },
6039
+ };
6040
+ }
6041
+ case 'name': {
6042
+ return {
6043
+ key,
6044
+ type: 'button',
6045
+ content: {
6046
+ icon: 'folder',
6047
+ ariaLabel: itemSubPath,
6048
+ label: itemSubPath,
6049
+ onClick: onNavigate,
6050
+ },
6051
+ };
6052
+ }
6053
+ case 'type': {
6054
+ return { key, type: 'text', content: { text: 'Folder' } };
6055
+ }
6056
+ case 'last-modified':
6057
+ case 'size':
6058
+ case 'download': {
6059
+ return { key, type: 'text', content: { text: '' } };
6060
+ }
5517
6061
  }
5518
- }
5519
- });
6062
+ });
6063
+ };
5520
6064
 
5521
6065
  const getHeaders$1 = ({ tableColumnLastModifiedHeader, tableColumnNameHeader, tableColumnSizeHeader, tableColumnTypeHeader, areAllFilesSelected, selectAllFilesLabel, onSelectAll, hasFiles, }) => LOCATION_DETAIL_VIEW_HEADERS.map((key) => {
5522
6066
  switch (key) {
@@ -5583,7 +6127,7 @@ const getHeaders$1 = ({ tableColumnLastModifiedHeader, tableColumnNameHeader, ta
5583
6127
  }
5584
6128
  });
5585
6129
 
5586
- const getLocationDetailViewTableData = ({ filePreviewEnabled, activeFile, onSelectActiveFile, areAllFilesSelected, displayText, location, fileDataItems, hasFiles, pageItems, selectFileLabel, selectAllFilesLabel, getDateDisplayValue, onDownload, onNavigate, onSelect, onSelectAll, }) => {
6130
+ const getLocationDetailViewTableData = ({ filePreviewEnabled, activeFile, onSelectActiveFile, areAllFilesSelected, displayText, location, fileDataItems, dataItems, hasFiles, pageItems, selectFileLabel, selectAllFilesLabel, getDateDisplayValue, onDownload, onNavigate, onSelect, onSelectAll, }) => {
5587
6131
  const { tableColumnLastModifiedHeader, tableColumnNameHeader, tableColumnSizeHeader, tableColumnTypeHeader, } = displayText;
5588
6132
  const headers = getHeaders$1({
5589
6133
  areAllFilesSelected,
@@ -5641,6 +6185,10 @@ const getLocationDetailViewTableData = ({ filePreviewEnabled, activeFile, onSele
5641
6185
  }
5642
6186
  onNavigate({ ...current, id }, itemLocationPath);
5643
6187
  };
6188
+ const isSelected = dataItems?.some((item) => item.id === id) ?? false;
6189
+ const onFolderSelect = () => {
6190
+ onSelect(isSelected, locationItem);
6191
+ };
5644
6192
  return {
5645
6193
  key: id,
5646
6194
  active: false,
@@ -5648,6 +6196,9 @@ const getLocationDetailViewTableData = ({ filePreviewEnabled, activeFile, onSele
5648
6196
  itemSubPath,
5649
6197
  rowId: id,
5650
6198
  onNavigate: onFolderNavigate,
6199
+ selectFolderLabel: selectFileLabel,
6200
+ isSelected,
6201
+ onSelect: onFolderSelect,
5651
6202
  }),
5652
6203
  };
5653
6204
  }
@@ -5659,7 +6210,7 @@ const getLocationDetailViewTableData = ({ filePreviewEnabled, activeFile, onSele
5659
6210
  function LocationDetailViewProvider({ children, ...props }) {
5660
6211
  const { LocationDetailView: displayText } = useDisplayText();
5661
6212
  const { LocationDetailView: { loadingIndicatorLabel, searchSubfoldersToggleLabel, selectFileLabel, selectAllFilesLabel, searchPlaceholder, searchSubmitLabel, searchClearLabel, getActionListItemLabel, getDateDisplayValue, getTitle, getListItemsResultMessage, }, } = useDisplayText();
5662
- const { actionItems, activeFile, activeFileHasNext, activeFileHasPrev, page, pageItems, hasNextPage, highestPageVisited, isLoading, isSearchSubfoldersEnabled, location, fileDataItems, hasError, hasDownloadError, message, downloadErrorMessage, searchQuery, hasExhaustedSearch, onActionSelect, onDropFiles, onRefresh, onPaginate, onDownload, onNavigate, onNavigateHome, onSelect, onSelectActiveFile, onToggleSelectAll, onSearch, onSearchQueryChange, onSearchClear, onToggleSearchSubfolders, filePreviewState, filePreviewEnabled, onRetryFilePreview, } = props;
6213
+ const { actionItems, activeFile, activeFileHasNext, activeFileHasPrev, page, pageItems, hasNextPage, highestPageVisited, isLoading, isSearchSubfoldersEnabled, location, fileDataItems, hasError, hasDownloadError, message, downloadErrorMessage, searchQuery, hasExhaustedSearch, onActionSelect, onDropFiles, onRefresh, onPaginate, onDownload, onNavigate, onNavigateHome, onSelect, onSelectActiveFile, onToggleSelectAll, onSearch, onSearchQueryChange, onSearchClear, onToggleSearchSubfolders, filePreviewState, filePreviewEnabled, onRetryFilePreview, dataItems, } = props;
5663
6214
  const actionsWithDisplayText = actionItems.map((item) => ({
5664
6215
  ...item,
5665
6216
  label: getActionListItemLabel(item.label),
@@ -5716,6 +6267,7 @@ function LocationDetailViewProvider({ children, ...props }) {
5716
6267
  onNavigate,
5717
6268
  onSelect,
5718
6269
  onSelectAll: onToggleSelectAll,
6270
+ dataItems,
5719
6271
  }),
5720
6272
  title: getTitle(location),
5721
6273
  message: messageControlContent,
@@ -5865,30 +6417,29 @@ function useFilePreview({ activeFile, }) {
5865
6417
  }, [getFilePreview, filePreviewContext, filePreviewContent, enabled]);
5866
6418
  }
5867
6419
 
5868
- const DEFAULT_PAGE_SIZE$1 = 100;
5869
- const DEFAULT_LIST_OPTIONS$1 = {
5870
- delimiter: '/',
5871
- pageSize: DEFAULT_PAGE_SIZE$1,
5872
- };
5873
6420
  const getDownloadErrorMessageFromFailedDownloadTask = (task) => {
5874
6421
  if (!task)
5875
6422
  return;
5876
6423
  return `Failed to download ${task.data.fileKey ?? task.data.key} due to error: ${task.message}.`;
5877
6424
  };
5878
6425
  const useLocationDetailView = (options) => {
5879
- const { initialValues, onExit, onNavigate } = options ?? {};
5880
- const listOptionsRef = React__namespace["default"].useRef({
5881
- ...DEFAULT_LIST_OPTIONS$1,
6426
+ const { pageSize: configPageSize } = usePaginationConfig();
6427
+ const { initialValues = {}, onExit, onNavigate, pageSize: propPageSize, } = options ?? {};
6428
+ const pageSize = propPageSize ?? configPageSize;
6429
+ const listOptions = React__namespace["default"].useMemo(() => ({
5882
6430
  ...initialValues,
5883
- });
5884
- const listOptions = listOptionsRef.current;
6431
+ delimiter: '/',
6432
+ pageSize,
6433
+ }),
6434
+ // eslint-disable-next-line react-hooks/exhaustive-deps
6435
+ [pageSize, initialValues.delimiter, initialValues.pageSize]);
5885
6436
  const [{ location, actionType }, storeDispatch] = useStore();
5886
6437
  const [locationItems, locationItemsDispatch] = useLocationItems();
5887
6438
  const fileItemsDispatch = useFileItems()[1];
5888
6439
  const [activeFile, setActiveFile] = React__namespace["default"].useState();
5889
6440
  const { current, key } = location;
5890
6441
  const { permissions, prefix } = current ?? {};
5891
- const { fileDataItems } = locationItems;
6442
+ const { dataItems, fileDataItems } = locationItems;
5892
6443
  const hasInvalidPrefix = ui.isUndefined(prefix);
5893
6444
  const [{ task }, handleDownload] = useAction('download');
5894
6445
  const [{ value, isLoading, hasError, message }, handleList] = useList('locationItems');
@@ -6015,13 +6566,13 @@ const useLocationDetailView = (options) => {
6015
6566
  actionType: type,
6016
6567
  icon,
6017
6568
  isDisabled: ui.isFunction(disable)
6018
- ? disable(fileDataItems)
6569
+ ? disable(dataItems)
6019
6570
  : disable ?? false,
6020
6571
  isHidden: ui.isFunction(hide) ? hide(permissions) : hide,
6021
6572
  label,
6022
6573
  };
6023
6574
  });
6024
- }, [actionConfigs, fileDataItems, permissions]);
6575
+ }, [actionConfigs, dataItems, permissions]);
6025
6576
  return {
6026
6577
  actionItems,
6027
6578
  actionType,
@@ -6032,6 +6583,7 @@ const useLocationDetailView = (options) => {
6032
6583
  page: currentPage,
6033
6584
  pageItems,
6034
6585
  location,
6586
+ dataItems,
6035
6587
  fileDataItems,
6036
6588
  hasError,
6037
6589
  hasDownloadError: task?.status === 'FAILED',
@@ -6087,16 +6639,16 @@ const useLocationDetailView = (options) => {
6087
6639
  storeDispatch({ type: 'RESET_ACTION_TYPE' });
6088
6640
  locationItemsDispatch({ type: 'RESET_LOCATION_ITEMS' });
6089
6641
  },
6090
- onSelect: (isSelected, fileItem) => {
6642
+ onSelect: (isSelected, item) => {
6091
6643
  locationItemsDispatch(isSelected
6092
- ? { type: 'REMOVE_LOCATION_ITEM', id: fileItem.id }
6093
- : { type: 'SET_LOCATION_ITEMS', items: [fileItem] });
6644
+ ? { type: 'REMOVE_LOCATION_ITEM', id: item.id }
6645
+ : { type: 'SET_LOCATION_ITEMS', items: [item] });
6094
6646
  },
6095
6647
  onToggleSelectAll: () => {
6096
- const fileItems = pageItems.filter((item) => item.type === 'FILE');
6097
- locationItemsDispatch(fileItems.length === fileDataItems?.length
6648
+ const selectableItems = pageItems;
6649
+ locationItemsDispatch(selectableItems.length === dataItems?.length
6098
6650
  ? { type: 'RESET_LOCATION_ITEMS' }
6099
- : { type: 'SET_LOCATION_ITEMS', items: fileItems });
6651
+ : { type: 'SET_LOCATION_ITEMS', items: selectableItems });
6100
6652
  },
6101
6653
  onSearch: () => {
6102
6654
  setActiveFile(undefined);
@@ -6310,12 +6862,8 @@ function LocationsViewProvider({ children, ...props }) {
6310
6862
  const DEFAULT_EXCLUDE = {
6311
6863
  exactPermissions: ['delete', 'write'],
6312
6864
  };
6313
- const DEFAULT_PAGE_SIZE = 100;
6314
- const DEFAULT_LIST_OPTIONS = {
6315
- exclude: DEFAULT_EXCLUDE,
6316
- pageSize: DEFAULT_PAGE_SIZE,
6317
- };
6318
6865
  const useLocationsView = (options) => {
6866
+ const { pageSize: configPageSize } = usePaginationConfig();
6319
6867
  const handleDownload = useAction('download')[1];
6320
6868
  const [state, handleList] = useList('locations');
6321
6869
  const dispatchStoreAction = useStore()[1];
@@ -6323,12 +6871,15 @@ const useLocationsView = (options) => {
6323
6871
  const { items, nextToken, hasExhaustedSearch = false } = value;
6324
6872
  const hasNextToken = !!nextToken;
6325
6873
  const onNavigate = options?.onNavigate;
6874
+ const pageSize = options?.pageSize ?? configPageSize;
6326
6875
  const initialValues = options?.initialValues ?? {};
6327
- const listOptionsRef = React__namespace["default"].useRef({
6328
- ...DEFAULT_LIST_OPTIONS,
6876
+ const listOptions = React__namespace["default"].useMemo(() => ({
6329
6877
  ...initialValues,
6330
- });
6331
- const listOptions = listOptionsRef.current;
6878
+ exclude: DEFAULT_EXCLUDE,
6879
+ pageSize,
6880
+ }),
6881
+ // eslint-disable-next-line react-hooks/exhaustive-deps
6882
+ [pageSize, initialValues.pageSize]);
6332
6883
  // initial load
6333
6884
  React__namespace["default"].useEffect(() => {
6334
6885
  handleList({ options: { ...listOptions, refresh: true } });
@@ -6532,17 +7083,18 @@ function createProvider({ actions, components, config, options, filePreview = {}
6532
7083
  * Provides state, configuration and action values that are shared between
6533
7084
  * the primary View components
6534
7085
  */
6535
- function Provider({ children, displayText, views, ...props }) {
7086
+ function Provider({ children, displayText, views, pageSize, ...props }) {
6536
7087
  return (React__namespace["default"].createElement(StoreProvider, { ...props },
6537
7088
  React__namespace["default"].createElement(ConfigurationProvider, null,
6538
- React__namespace["default"].createElement(ActionConfigsProvider, { actionConfigs: actionConfigs },
6539
- React__namespace["default"].createElement(ActionHandlersProvider, { handlers: handlers },
6540
- React__namespace["default"].createElement(DisplayTextProvider, { displayText: displayText },
6541
- React__namespace["default"].createElement(ViewsProvider, { actions: resolvedActions, views: views },
6542
- React__namespace["default"].createElement(ComponentsProvider, { composables: composables },
6543
- React__namespace["default"].createElement(LocationItemsProvider, null,
6544
- React__namespace["default"].createElement(FileItemsProvider, { validateFile: validateFile },
6545
- React__namespace["default"].createElement(FilePreviewProvider, { filePreview: filePreview }, children)))))))))));
7089
+ React__namespace["default"].createElement(PaginationConfigProvider, { pageSize: pageSize },
7090
+ React__namespace["default"].createElement(ActionConfigsProvider, { actionConfigs: actionConfigs },
7091
+ React__namespace["default"].createElement(ActionHandlersProvider, { handlers: handlers },
7092
+ React__namespace["default"].createElement(DisplayTextProvider, { displayText: displayText },
7093
+ React__namespace["default"].createElement(ViewsProvider, { actions: resolvedActions, views: views },
7094
+ React__namespace["default"].createElement(ComponentsProvider, { composables: composables },
7095
+ React__namespace["default"].createElement(LocationItemsProvider, null,
7096
+ React__namespace["default"].createElement(FileItemsProvider, { validateFile: validateFile },
7097
+ React__namespace["default"].createElement(FilePreviewProvider, { filePreview: filePreview }, children))))))))))));
6546
7098
  }
6547
7099
  return Provider;
6548
7100
  }