@aws-amplify/ui-react-storage 3.10.3 → 3.12.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 (88) hide show
  1. package/dist/browser.js +1 -1
  2. package/dist/{createStorageBrowser-B75dAYRb.js → createStorageBrowser-D5xaObbd.js} +439 -251
  3. package/dist/esm/components/StorageBrowser/actions/configs/defaults.mjs +12 -3
  4. package/dist/esm/components/StorageBrowser/actions/handlers/listLocationItems.mjs +1 -1
  5. package/dist/esm/components/StorageBrowser/actions/handlers/listLocations.mjs +1 -1
  6. package/dist/esm/components/StorageBrowser/createStorageBrowser/StorageBrowserDefault.mjs +2 -1
  7. package/dist/esm/components/StorageBrowser/createStorageBrowser/createProvider.mjs +6 -3
  8. package/dist/esm/components/StorageBrowser/createStorageBrowser/createStorageBrowser.mjs +3 -1
  9. package/dist/esm/components/StorageBrowser/displayText/context.mjs +5 -1
  10. package/dist/esm/components/StorageBrowser/displayText/libraries/en/default.mjs +2 -0
  11. package/dist/esm/components/StorageBrowser/displayText/libraries/en/downloadView.mjs +23 -0
  12. package/dist/esm/components/StorageBrowser/displayText/libraries/en/locationDetailView.mjs +2 -0
  13. package/dist/esm/components/StorageBrowser/displayText/libraries/en/uploadView.mjs +3 -6
  14. package/dist/esm/components/StorageBrowser/displayText/utils.mjs +3 -1
  15. package/dist/esm/components/StorageBrowser/fileItems/constants.mjs +11 -0
  16. package/dist/esm/components/StorageBrowser/fileItems/context.mjs +42 -0
  17. package/dist/esm/components/StorageBrowser/fileItems/fileItemsReducer.mjs +33 -0
  18. package/dist/esm/components/StorageBrowser/fileItems/utils.mjs +55 -0
  19. package/dist/esm/components/StorageBrowser/useAction/utils.mjs +2 -2
  20. package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/CopyViewProvider.mjs +3 -2
  21. package/dist/esm/components/StorageBrowser/views/LocationActionView/CreateFolderView/CreateFolderViewProvider.mjs +0 -1
  22. package/dist/esm/components/StorageBrowser/views/LocationActionView/DeleteView/DeleteViewProvider.mjs +4 -3
  23. package/dist/esm/components/StorageBrowser/views/LocationActionView/DownloadView/DownloadView.mjs +47 -0
  24. package/dist/esm/components/StorageBrowser/views/LocationActionView/DownloadView/DownloadViewProvider.mjs +38 -0
  25. package/dist/esm/components/StorageBrowser/views/LocationActionView/DownloadView/useDownloadView.mjs +66 -0
  26. package/dist/esm/components/StorageBrowser/views/LocationActionView/UploadView/UploadView.mjs +3 -0
  27. package/dist/esm/components/StorageBrowser/views/LocationActionView/UploadView/UploadViewProvider.mjs +7 -2
  28. package/dist/esm/components/StorageBrowser/views/LocationActionView/UploadView/useUploadView.mjs +23 -35
  29. package/dist/esm/components/StorageBrowser/views/LocationDetailView/LocationDetailViewProvider.mjs +0 -1
  30. package/dist/esm/components/StorageBrowser/views/LocationDetailView/useLocationDetailView.mjs +3 -3
  31. package/dist/esm/components/StorageBrowser/views/context/actionViews.mjs +3 -1
  32. package/dist/esm/components/StorageBrowser/views/context/primaryViews.mjs +2 -1
  33. package/dist/esm/components/StorageBrowser/views/hooks/usePaginate.mjs +1 -1
  34. package/dist/esm/components/StorageBrowser/views/useView.mjs +3 -0
  35. package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/constants.mjs +9 -1
  36. package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/copyResolvers.mjs +4 -42
  37. package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/deleteResolvers.mjs +4 -42
  38. package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/downloadResolvers.mjs +36 -0
  39. package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/utils.mjs +45 -5
  40. package/dist/esm/version.mjs +1 -1
  41. package/dist/index.js +1 -1
  42. package/dist/styles.css +17 -2
  43. package/dist/types/components/StorageBrowser/actions/configs/defaults.d.ts +4 -3
  44. package/dist/types/components/StorageBrowser/actions/configs/types.d.ts +3 -1
  45. package/dist/types/components/StorageBrowser/actions/handlers/download.d.ts +2 -2
  46. package/dist/types/components/StorageBrowser/actions/index.d.ts +1 -1
  47. package/dist/types/components/StorageBrowser/createStorageBrowser/createProvider.d.ts +1 -1
  48. package/dist/types/components/StorageBrowser/createStorageBrowser/types.d.ts +23 -4
  49. package/dist/types/components/StorageBrowser/displayText/index.d.ts +2 -2
  50. package/dist/types/components/StorageBrowser/displayText/libraries/en/downloadView.d.ts +2 -0
  51. package/dist/types/components/StorageBrowser/displayText/types.d.ts +8 -2
  52. package/dist/types/components/StorageBrowser/displayText/utils.d.ts +2 -1
  53. package/dist/types/components/StorageBrowser/fileItems/constants.d.ts +4 -0
  54. package/dist/types/components/StorageBrowser/fileItems/context.d.ts +6 -0
  55. package/dist/types/components/StorageBrowser/fileItems/fileItemsReducer.d.ts +6 -0
  56. package/dist/types/components/StorageBrowser/fileItems/index.d.ts +3 -0
  57. package/dist/types/components/StorageBrowser/fileItems/types.d.ts +44 -0
  58. package/dist/types/components/StorageBrowser/fileItems/utils.d.ts +5 -0
  59. package/dist/types/components/StorageBrowser/validators/index.d.ts +1 -2
  60. package/dist/types/components/StorageBrowser/views/LocationActionView/DownloadView/DownloadView.d.ts +2 -0
  61. package/dist/types/components/StorageBrowser/views/LocationActionView/DownloadView/DownloadViewProvider.d.ts +3 -0
  62. package/dist/types/components/StorageBrowser/views/LocationActionView/DownloadView/index.d.ts +3 -0
  63. package/dist/types/components/StorageBrowser/views/LocationActionView/DownloadView/types.d.ts +22 -0
  64. package/dist/types/components/StorageBrowser/views/LocationActionView/DownloadView/useDownloadView.d.ts +2 -0
  65. package/dist/types/components/StorageBrowser/views/LocationActionView/UploadView/types.d.ts +6 -1
  66. package/dist/types/components/StorageBrowser/views/LocationActionView/index.d.ts +2 -0
  67. package/dist/types/components/StorageBrowser/views/index.d.ts +2 -2
  68. package/dist/types/components/StorageBrowser/views/types.d.ts +5 -3
  69. package/dist/types/components/StorageBrowser/views/useView.d.ts +2 -1
  70. package/dist/types/components/StorageBrowser/views/utils/index.d.ts +1 -1
  71. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/__testUtils__/tasks.d.ts +6 -11
  72. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/constants.d.ts +1 -0
  73. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/copyResolvers.d.ts +2 -3
  74. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/deleteResolvers.d.ts +2 -3
  75. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/downloadResolvers.d.ts +2 -0
  76. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/index.d.ts +4 -2
  77. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/types.d.ts +15 -11
  78. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/utils.d.ts +15 -7
  79. package/dist/types/version.d.ts +1 -1
  80. package/package.json +3 -3
  81. package/dist/esm/components/StorageBrowser/files/context.mjs +0 -31
  82. package/dist/esm/components/StorageBrowser/files/utils.mjs +0 -52
  83. package/dist/esm/components/StorageBrowser/validators/isFileTooBig.mjs +0 -4
  84. package/dist/types/components/StorageBrowser/files/context.d.ts +0 -6
  85. package/dist/types/components/StorageBrowser/files/index.d.ts +0 -2
  86. package/dist/types/components/StorageBrowser/files/types.d.ts +0 -28
  87. package/dist/types/components/StorageBrowser/files/utils.d.ts +0 -7
  88. package/dist/types/components/StorageBrowser/validators/isFileTooBig.d.ts +0 -2
@@ -32,7 +32,7 @@ function _interopNamespace(e) {
32
32
 
33
33
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
34
34
 
35
- const VERSION = '3.10.3';
35
+ const VERSION = '3.12.0';
36
36
 
37
37
  const toAccessGrantPermission = (permission) => {
38
38
  let result = '';
@@ -389,7 +389,7 @@ const downloadHandler = ({ config, data: { key }, }) => {
389
389
  return { result };
390
390
  };
391
391
 
392
- const DEFAULT_PAGE_SIZE$4 = 1000;
392
+ const DEFAULT_PAGE_SIZE$5 = 1000;
393
393
  const parseItems = (items, excludedPath) => items
394
394
  // remove root `key` from results
395
395
  .filter(({ path }) => path !== excludedPath)
@@ -422,7 +422,7 @@ const parseResult = ({ excludedSubpaths, items }, prefix) => filterDotItems([...
422
422
  const listLocationItemsHandler = async (input) => {
423
423
  const { config, prefix, options } = input;
424
424
  const { bucket: _bucket, credentials, customEndpoint, region, accountId, } = config;
425
- const { exclude, delimiter, nextToken, pageSize: _pageSize = DEFAULT_PAGE_SIZE$4, ..._options } = options ?? {};
425
+ const { exclude, delimiter, nextToken, pageSize: _pageSize = DEFAULT_PAGE_SIZE$5, ..._options } = options ?? {};
426
426
  const bucket = { bucketName: _bucket, region };
427
427
  const subpathStrategy = {
428
428
  delimiter,
@@ -455,7 +455,7 @@ const listLocationItemsHandler = async (input) => {
455
455
  nextNextToken = output.nextToken;
456
456
  const items = parseResult(output, prefix);
457
457
  result.push(...(exclude ? items.filter((item) => item.type !== exclude) : items));
458
- } while (nextNextToken && result.length < pageSize);
458
+ } while (nextNextToken && result.length < _pageSize);
459
459
  return { items: result, nextToken: nextNextToken };
460
460
  };
461
461
 
@@ -541,9 +541,6 @@ function assertRegisterAuthListener(value) {
541
541
  }
542
542
  }
543
543
 
544
- const UPLOAD_FILE_SIZE_LIMIT = 160 * 1000 * 1000 * 1000;
545
- const isFileTooBig = (file) => file.size > UPLOAD_FILE_SIZE_LIMIT;
546
-
547
544
  const defaultValue$7 = {
548
545
  actionConfigs: undefined,
549
546
  };
@@ -587,12 +584,21 @@ const uploadActionConfig = {
587
584
  },
588
585
  handler: defaultHandlers.upload,
589
586
  };
587
+ const downloadActionConfig = {
588
+ viewName: 'DownloadView',
589
+ actionListItem: {
590
+ disable: (selected) => !selected || selected.length === 0,
591
+ hide: (permissions) => !permissions.includes('get'),
592
+ icon: 'download',
593
+ label: 'Download',
594
+ },
595
+ handler: defaultHandlers.download,
596
+ };
590
597
  // Action view configs only, does not include `listLocationItems`
591
598
  const defaultActionViewConfigs = {
592
599
  copy: copyActionConfig,
593
600
  createFolder: createFolderActionConfig,
594
- // provide `download` handler only; `download` does not have a dedicated view/config
595
- download: defaultHandlers.download,
601
+ download: downloadActionConfig,
596
602
  delete: deleteActionConfig,
597
603
  upload: uploadActionConfig,
598
604
  };
@@ -1664,7 +1670,7 @@ function CredentialsProvider({ children, ...props }) {
1664
1670
  return (React__namespace["default"].createElement(CredentialsContext.Provider, { value: value }, children));
1665
1671
  }
1666
1672
 
1667
- const DEFAULT_STATE$1 = {
1673
+ const DEFAULT_STATE$2 = {
1668
1674
  actionType: undefined,
1669
1675
  location: { current: undefined, path: '', key: '' },
1670
1676
  };
@@ -1683,7 +1689,7 @@ function storeReducer(state, action) {
1683
1689
  case 'RESET_LOCATION': {
1684
1690
  if (state.location.current === undefined)
1685
1691
  return state;
1686
- return { ...state, location: DEFAULT_STATE$1.location };
1692
+ return { ...state, location: DEFAULT_STATE$2.location };
1687
1693
  }
1688
1694
  case 'CHANGE_ACTION_TYPE': {
1689
1695
  const { actionType } = action;
@@ -1695,7 +1701,7 @@ function storeReducer(state, action) {
1695
1701
  case 'RESET_ACTION_TYPE': {
1696
1702
  if (state.actionType === undefined)
1697
1703
  return state;
1698
- return { ...state, actionType: DEFAULT_STATE$1.actionType };
1704
+ return { ...state, actionType: DEFAULT_STATE$2.actionType };
1699
1705
  }
1700
1706
  }
1701
1707
  }
@@ -1711,10 +1717,10 @@ function getLocationData(location) {
1711
1717
  }
1712
1718
  function getState(value) {
1713
1719
  if (value === undefined || value === null)
1714
- return DEFAULT_STATE$1;
1720
+ return DEFAULT_STATE$2;
1715
1721
  const current = getLocationData(value.location);
1716
1722
  if (!current)
1717
- return DEFAULT_STATE$1;
1723
+ return DEFAULT_STATE$2;
1718
1724
  const actionType = value?.actionType;
1719
1725
  const path = value?.location?.path ?? '';
1720
1726
  const key = `${current.prefix}${path}`;
@@ -1816,7 +1822,7 @@ function useStoreReducer(props) {
1816
1822
  });
1817
1823
  }
1818
1824
 
1819
- const DEFAULT_VALUE = [DEFAULT_STATE$1, ui.noop];
1825
+ const DEFAULT_VALUE = [DEFAULT_STATE$2, ui.noop];
1820
1826
  const { StoreContext, useStore } = uiReactCore.createContextUtilities({
1821
1827
  contextName: 'Store',
1822
1828
  defaultValue: DEFAULT_VALUE,
@@ -2181,12 +2187,12 @@ const useList = (type) => {
2181
2187
 
2182
2188
  const resolveHandler = (value) => (ui.isFunction(value) ? value : value.handler);
2183
2189
  const getActionHandlers = (configs) => {
2184
- const { copy: copyConfig, createFolder: createFolderConfig, delete: deleteConfig, download, upload: uploadConfig, listLocationItems, listLocations, } = configs.default;
2190
+ const { copy: copyConfig, createFolder: createFolderConfig, delete: deleteConfig, download: downloadConfig, upload: uploadConfig, listLocationItems, listLocations, } = configs.default;
2185
2191
  const defaultHandlers = {
2186
2192
  copy: copyConfig.handler,
2187
2193
  createFolder: createFolderConfig.handler,
2188
2194
  delete: deleteConfig.handler,
2189
- download,
2195
+ download: 'handler' in downloadConfig ? downloadConfig.handler : downloadConfig,
2190
2196
  listLocationItems,
2191
2197
  listLocations,
2192
2198
  upload: uploadConfig.handler,
@@ -2908,6 +2914,8 @@ const DEFAULT_LOCATION_DETAIL_VIEW_DISPLAY_TEXT = {
2908
2914
  return 'Create folder';
2909
2915
  case 'Upload':
2910
2916
  return 'Upload';
2917
+ case 'Download':
2918
+ return 'Download';
2911
2919
  default:
2912
2920
  return key;
2913
2921
  }
@@ -3035,13 +3043,12 @@ const DEFAULT_UPLOAD_VIEW_DISPLAY_TEXT = {
3035
3043
  if (!data?.invalidFiles) {
3036
3044
  return undefined;
3037
3045
  }
3038
- const tooBigFileNames = data.invalidFiles
3039
- .filter(({ file }) => isFileTooBig(file))
3046
+ const invalidFileNames = data.invalidFiles
3040
3047
  .map(({ file }) => file.name)
3041
3048
  .join(', ');
3042
- if (tooBigFileNames) {
3049
+ if (invalidFileNames) {
3043
3050
  return {
3044
- content: `Files larger than 160GB cannot be added to the upload queue: ${tooBigFileNames}`,
3051
+ content: `Files larger than 160GB cannot be added to the upload queue: ${invalidFileNames}`,
3045
3052
  type: 'warning',
3046
3053
  };
3047
3054
  }
@@ -3053,10 +3060,31 @@ const DEFAULT_UPLOAD_VIEW_DISPLAY_TEXT = {
3053
3060
  title: 'Upload',
3054
3061
  };
3055
3062
 
3063
+ const DEFAULT_DOWNLOAD_VIEW_DISPLAY_TEXT = {
3064
+ ...DEFAULT_ACTION_VIEW_DISPLAY_TEXT,
3065
+ title: 'Download',
3066
+ actionStartLabel: 'Download',
3067
+ getActionCompleteMessage: (data) => {
3068
+ const { counts } = data ?? {};
3069
+ const { COMPLETE, FAILED, TOTAL } = counts ?? {};
3070
+ if (COMPLETE === TOTAL) {
3071
+ return { content: 'All files downloaded.', type: 'success' };
3072
+ }
3073
+ if (FAILED === TOTAL) {
3074
+ return { content: 'All files failed to download.', type: 'error' };
3075
+ }
3076
+ return {
3077
+ content: `${COMPLETE} files downloaded, ${FAILED} files failed to download.`,
3078
+ type: 'error',
3079
+ };
3080
+ },
3081
+ };
3082
+
3056
3083
  const DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT = {
3057
3084
  CopyView: DEFAULT_COPY_VIEW_DISPLAY_TEXT,
3058
3085
  CreateFolderView: DEFAULT_CREATE_FOLDER_VIEW_DISPLAY_TEXT,
3059
3086
  DeleteView: DEFAULT_DELETE_VIEW_DISPLAY_TEXT,
3087
+ DownloadView: DEFAULT_DOWNLOAD_VIEW_DISPLAY_TEXT,
3060
3088
  LocationDetailView: DEFAULT_LOCATION_DETAIL_VIEW_DISPLAY_TEXT,
3061
3089
  LocationsView: DEFAULT_LOCATIONS_VIEW_DISPLAY_TEXT,
3062
3090
  UploadView: DEFAULT_UPLOAD_VIEW_DISPLAY_TEXT,
@@ -3070,7 +3098,7 @@ function resolveDisplayText(displayText) {
3070
3098
  if (!displayText)
3071
3099
  return DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT;
3072
3100
  // override
3073
- const { CopyView, CreateFolderView, DeleteView, LocationDetailView, LocationsView, UploadView, } = displayText;
3101
+ const { CopyView, CreateFolderView, DeleteView, DownloadView, LocationDetailView, LocationsView, UploadView, } = displayText;
3074
3102
  return {
3075
3103
  CopyView: { ...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.CopyView, ...CopyView },
3076
3104
  CreateFolderView: {
@@ -3081,6 +3109,10 @@ function resolveDisplayText(displayText) {
3081
3109
  ...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.DeleteView,
3082
3110
  ...DeleteView,
3083
3111
  },
3112
+ DownloadView: {
3113
+ ...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.DownloadView,
3114
+ ...DownloadView,
3115
+ },
3084
3116
  LocationDetailView: {
3085
3117
  ...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.LocationDetailView,
3086
3118
  ...LocationDetailView,
@@ -3103,6 +3135,7 @@ function DisplayTextProvider({ children, displayText: _override, }) {
3103
3135
 
3104
3136
  const isCopyViewDisplayTextKey = (value) => !!DEFAULT_COPY_VIEW_DISPLAY_TEXT[value];
3105
3137
  const isDeleteViewDisplayTextKey = (value) => !!DEFAULT_DELETE_VIEW_DISPLAY_TEXT[value];
3138
+ const isDownloadViewDisplayTextKey = (value) => !!DEFAULT_DOWNLOAD_VIEW_DISPLAY_TEXT[value];
3106
3139
 
3107
3140
  function useResolveTableData(keys, { getCell, getHeader, getRowKey }, { items, props }) {
3108
3141
  return React__namespace["default"].useMemo(() => {
@@ -3132,19 +3165,33 @@ const STATUS_ICONS = {
3132
3165
  CANCELED: 'action-canceled',
3133
3166
  QUEUED: 'action-queued',
3134
3167
  };
3168
+ const FILE_DATA_ITEM_TABLE_KEYS = [
3169
+ 'name',
3170
+ 'folder',
3171
+ 'type',
3172
+ 'size',
3173
+ 'status',
3174
+ 'cancel',
3175
+ ];
3135
3176
 
3136
3177
  const getFileType = (value, fallback = '') => value.lastIndexOf('.') !== -1
3137
3178
  ? value.slice(value.lastIndexOf('.') + 1)
3138
3179
  : fallback;
3139
3180
  const getCellName = (value) =>
3140
3181
  // `value.split` always returns an array with at least one entry
3141
- // ensruing `.pop()` will always return a string
3182
+ // ensuring `.pop()` will always return a string
3142
3183
  value.split('/').pop();
3143
3184
  const getUploadCellFolder = ({ data: { file: { webkitRelativePath }, }, }, fallback = '-') => webkitRelativePath
3144
3185
  ? webkitRelativePath.slice(0, webkitRelativePath.lastIndexOf('/') + 1)
3145
3186
  : fallback;
3146
- const getCopyCellFolder = ({ data: { fileKey, sourceKey }, }) => sourceKey.slice(0, -fileKey.length);
3147
- const getDeleteCellFolder = ({ data: { fileKey, key }, }) => key.slice(0, -fileKey.length);
3187
+ const isCopyActionTask = (task) => 'sourceKey' in task.data;
3188
+ const getFileDataCellFolder = (task) => {
3189
+ const targetKey = isCopyActionTask(task)
3190
+ ? task.data.sourceKey
3191
+ : task.data.key;
3192
+ const { fileKey } = task.data;
3193
+ return targetKey.slice(0, -fileKey.length);
3194
+ };
3148
3195
  const getUploadCellProgress = ({ progress, status, }) => {
3149
3196
  // prefer `progress` if available, 1 if status is complete, default 0
3150
3197
  const value = progress ?? (status === 'COMPLETE' ? 1 : 0);
@@ -3152,7 +3199,7 @@ const getUploadCellProgress = ({ progress, status, }) => {
3152
3199
  return { displayValue, value };
3153
3200
  };
3154
3201
  const getFileSize = (value, fallback = '-') => (!value ? fallback : ui.humanFileSize(value, true));
3155
- const getCopyOrDeleteCancelCellContent = (data) => {
3202
+ const getFileDataCancelCellContent = (data) => {
3156
3203
  const { item, props } = data;
3157
3204
  const { cancel, status } = item;
3158
3205
  const { isProcessing, onTaskRemove } = props;
@@ -3178,42 +3225,42 @@ const getCopyOrDeleteCancelCellContent = (data) => {
3178
3225
  };
3179
3226
  return { ariaLabel, isDisabled, onClick, icon: 'cancel' };
3180
3227
  };
3181
-
3182
- const COPY_TABLE_KEYS = [
3183
- 'name',
3184
- 'folder',
3185
- 'type',
3186
- 'size',
3187
- 'status',
3188
- 'cancel',
3189
- ];
3190
- const getCopyCellKey = ({ key, item, }) => `${key}-${item.data.id}`;
3191
- const name$2 = (data) => {
3192
- const key = getCopyCellKey(data);
3228
+ /**
3229
+ * Generates a unique key for a table cell based on the key and item id
3230
+ */
3231
+ const getFileDataCellKey = ({ key, item, }) => `${key}-${item.data.id}`;
3232
+ const name$1 = (data) => {
3233
+ const key = getFileDataCellKey(data);
3193
3234
  const { item } = data;
3194
3235
  const text = item.data.fileKey;
3195
3236
  const icon = STATUS_ICONS[item.status];
3196
3237
  return { key, type: 'text', content: { icon, text } };
3197
3238
  };
3198
- const folder$2 = (data) => {
3199
- const key = getCopyCellKey(data);
3200
- const text = getCopyCellFolder(data.item);
3239
+ const folder$1 = (data) => {
3240
+ const key = getFileDataCellKey(data);
3241
+ const text = getFileDataCellFolder(data.item);
3201
3242
  return { key, type: 'text', content: { text } };
3202
3243
  };
3203
- const type$2 = (data) => {
3204
- const key = getCopyCellKey(data);
3244
+ const type$1 = (data) => {
3245
+ const key = getFileDataCellKey(data);
3205
3246
  const { fileKey } = data.item.data;
3206
3247
  const text = getFileType(fileKey);
3207
3248
  return { key, type: 'text', content: { text } };
3208
3249
  };
3209
- const size$2 = (data) => {
3210
- const key = getCopyCellKey(data);
3250
+ const size$1 = (data) => {
3251
+ const key = getFileDataCellKey(data);
3211
3252
  const { size: value } = data.item.data;
3212
3253
  const displayValue = getFileSize(value);
3213
3254
  return { key, type: 'number', content: { value, displayValue } };
3214
3255
  };
3215
- const status$2 = (data) => {
3216
- const key = getCopyCellKey(data);
3256
+ const cancel$1 = (data) => {
3257
+ const key = getFileDataCellKey(data);
3258
+ const content = getFileDataCancelCellContent(data);
3259
+ return { key, type: 'button', content };
3260
+ };
3261
+
3262
+ const status$3 = (data) => {
3263
+ const key = getFileDataCellKey(data);
3217
3264
  const { item: { status }, props: { displayText }, } = data;
3218
3265
  const statusLabelKey = STATUS_LABELS[status];
3219
3266
  const text = isCopyViewDisplayTextKey(statusLabelKey)
@@ -3221,18 +3268,13 @@ const status$2 = (data) => {
3221
3268
  : '';
3222
3269
  return { key, type: 'text', content: { text } };
3223
3270
  };
3224
- const cancel$2 = (data) => {
3225
- const key = getCopyCellKey(data);
3226
- const content = getCopyOrDeleteCancelCellContent(data);
3227
- return { key, type: 'button', content };
3228
- };
3229
3271
  const COPY_CELL_RESOLVERS = {
3230
- name: name$2,
3231
- folder: folder$2,
3232
- type: type$2,
3233
- size: size$2,
3234
- status: status$2,
3235
- cancel: cancel$2,
3272
+ name: name$1,
3273
+ folder: folder$1,
3274
+ type: type$1,
3275
+ size: size$1,
3276
+ status: status$3,
3277
+ cancel: cancel$1,
3236
3278
  /**
3237
3279
  * @deprecated
3238
3280
  *
@@ -3254,6 +3296,72 @@ const COPY_TABLE_RESOLVERS = {
3254
3296
  getRowKey: ({ item }) => item.data.id,
3255
3297
  };
3256
3298
 
3299
+ const status$2 = (data) => {
3300
+ const key = getFileDataCellKey(data);
3301
+ const { item: { status }, props: { displayText }, } = data;
3302
+ const statusLabelKey = STATUS_LABELS[status];
3303
+ const text = isDeleteViewDisplayTextKey(statusLabelKey)
3304
+ ? displayText[statusLabelKey]
3305
+ : '';
3306
+ return { key, type: 'text', content: { text } };
3307
+ };
3308
+ const DELETE_CELL_RESOLVERS = {
3309
+ name: name$1,
3310
+ folder: folder$1,
3311
+ type: type$1,
3312
+ size: size$1,
3313
+ status: status$2,
3314
+ cancel: cancel$1,
3315
+ /**
3316
+ * @deprecated
3317
+ *
3318
+ * non-upload view tables do not include "progress" headers but include here to
3319
+ * keep TS happy as "progress" headers were included in display text interfaces
3320
+ * and cannot be removed from the tables without a breaking change
3321
+ */
3322
+ progress: ui.noop,
3323
+ };
3324
+ const DELETE_TABLE_RESOLVERS = {
3325
+ getCell: (data) => DELETE_CELL_RESOLVERS[data.key](data),
3326
+ getHeader: ({ key, props: { displayText } }) => {
3327
+ const text = displayText[`tableColumn${ui.capitalize(key)}Header`];
3328
+ if (key === 'cancel') {
3329
+ return { key, type: 'text', content: { text } };
3330
+ }
3331
+ return { key, type: 'sort', content: { label: text } };
3332
+ },
3333
+ getRowKey: ({ item }) => item.data.id,
3334
+ };
3335
+
3336
+ const status$1 = (data) => {
3337
+ const key = getFileDataCellKey(data);
3338
+ const { item: { status }, props: { displayText }, } = data;
3339
+ const statusLabelKey = STATUS_LABELS[status];
3340
+ const text = isDownloadViewDisplayTextKey(statusLabelKey)
3341
+ ? displayText[statusLabelKey]
3342
+ : '';
3343
+ return { key, type: 'text', content: { text } };
3344
+ };
3345
+ const DOWNLOAD_CELL_RESOLVERS = {
3346
+ name: name$1,
3347
+ folder: folder$1,
3348
+ type: type$1,
3349
+ size: size$1,
3350
+ status: status$1,
3351
+ cancel: cancel$1,
3352
+ };
3353
+ const DOWNLOAD_TABLE_RESOLVERS = {
3354
+ getCell: (data) => DOWNLOAD_CELL_RESOLVERS[data.key](data),
3355
+ getHeader: ({ key, props: { displayText } }) => {
3356
+ const text = displayText[`tableColumn${ui.capitalize(key)}Header`];
3357
+ if (key === 'cancel') {
3358
+ return { key, type: 'text', content: { text } };
3359
+ }
3360
+ return { key, type: 'sort', content: { label: text } };
3361
+ },
3362
+ getRowKey: ({ item }) => item.data.id,
3363
+ };
3364
+
3257
3365
  const UPLOAD_TABLE_KEYS = [
3258
3366
  'name',
3259
3367
  'folder',
@@ -3264,31 +3372,31 @@ const UPLOAD_TABLE_KEYS = [
3264
3372
  'cancel',
3265
3373
  ];
3266
3374
  const getUploadCellKey = ({ key, item, }) => `${key}-${item.data.id}`;
3267
- const name$1 = (data) => {
3375
+ const name = (data) => {
3268
3376
  const key = getUploadCellKey(data);
3269
3377
  const { item } = data;
3270
3378
  const icon = STATUS_ICONS[item.status];
3271
3379
  const text = getCellName(item.data.key);
3272
3380
  return { key, type: 'text', content: { icon, text } };
3273
3381
  };
3274
- const folder$1 = (data) => {
3382
+ const folder = (data) => {
3275
3383
  const key = getUploadCellKey(data);
3276
3384
  const text = getUploadCellFolder(data.item);
3277
3385
  return { key, type: 'text', content: { text } };
3278
3386
  };
3279
- const type$1 = (data) => {
3387
+ const type = (data) => {
3280
3388
  const key = getUploadCellKey(data);
3281
3389
  const { item } = data;
3282
3390
  const text = getFileType(getCellName(item.data.key));
3283
3391
  return { key, type: 'text', content: { text } };
3284
3392
  };
3285
- const size$1 = (data) => {
3393
+ const size = (data) => {
3286
3394
  const key = getUploadCellKey(data);
3287
3395
  const { size: value } = data.item.data.file;
3288
3396
  const displayValue = getFileSize(value);
3289
3397
  return { key, type: 'number', content: { value, displayValue } };
3290
3398
  };
3291
- const status$1 = (data) => {
3399
+ const status = (data) => {
3292
3400
  const key = getUploadCellKey(data);
3293
3401
  const { item: { status }, props: { displayText }, } = data;
3294
3402
  const text = displayText[STATUS_LABELS[status]];
@@ -3299,7 +3407,7 @@ const progress = (data) => {
3299
3407
  const content = getUploadCellProgress(data.item);
3300
3408
  return { key, type: 'number', content };
3301
3409
  };
3302
- const cancel$1 = (data) => {
3410
+ const cancel = (data) => {
3303
3411
  const key = getUploadCellKey(data);
3304
3412
  const { item, props } = data;
3305
3413
  const { cancel, data: taskData, status, progress } = item;
@@ -3341,91 +3449,16 @@ const cancel$1 = (data) => {
3341
3449
  return { key, type: 'button', content };
3342
3450
  };
3343
3451
  const UPLOAD_CELL_RESOLVERS = {
3344
- cancel: cancel$1,
3345
- folder: folder$1,
3346
- name: name$1,
3347
- progress,
3348
- size: size$1,
3349
- status: status$1,
3350
- type: type$1,
3351
- };
3352
- const UPLOAD_TABLE_RESOLVERS = {
3353
- getCell: (data) => UPLOAD_CELL_RESOLVERS[data.key](data),
3354
- getHeader: ({ key, props: { displayText } }) => {
3355
- const text = displayText[`tableColumn${ui.capitalize(key)}Header`];
3356
- if (key === 'cancel') {
3357
- return { key, type: 'text', content: { text } };
3358
- }
3359
- return { key, type: 'sort', content: { label: text } };
3360
- },
3361
- getRowKey: ({ item }) => item.data.id,
3362
- };
3363
-
3364
- const DELETE_TABLE_KEYS = [
3365
- 'name',
3366
- 'folder',
3367
- 'type',
3368
- 'size',
3369
- 'status',
3370
- 'cancel',
3371
- ];
3372
- const getDeleteCellKey = ({ key, item, }) => `${key}-${item.data.id}`;
3373
- const name = (data) => {
3374
- const key = getDeleteCellKey(data);
3375
- const { item } = data;
3376
- const text = item.data.fileKey;
3377
- const icon = STATUS_ICONS[item.status];
3378
- return { key, type: 'text', content: { icon, text } };
3379
- };
3380
- const folder = (data) => {
3381
- const key = getDeleteCellKey(data);
3382
- const text = getDeleteCellFolder(data.item);
3383
- return { key, type: 'text', content: { text } };
3384
- };
3385
- const type = (data) => {
3386
- const key = getDeleteCellKey(data);
3387
- const { fileKey } = data.item.data;
3388
- const text = getFileType(fileKey);
3389
- return { key, type: 'text', content: { text } };
3390
- };
3391
- const size = (data) => {
3392
- const key = getDeleteCellKey(data);
3393
- const { size: value } = data.item.data;
3394
- const displayValue = getFileSize(value);
3395
- return { key, type: 'number', content: { value, displayValue } };
3396
- };
3397
- const status = (data) => {
3398
- const key = getDeleteCellKey(data);
3399
- const { item: { status }, props: { displayText }, } = data;
3400
- const statusLabelKey = STATUS_LABELS[status];
3401
- const text = isDeleteViewDisplayTextKey(statusLabelKey)
3402
- ? displayText[statusLabelKey]
3403
- : '';
3404
- return { key, type: 'text', content: { text } };
3405
- };
3406
- const cancel = (data) => {
3407
- const key = getDeleteCellKey(data);
3408
- const content = getCopyOrDeleteCancelCellContent(data);
3409
- return { key, type: 'button', content };
3410
- };
3411
- const DELETE_CELL_RESOLVERS = {
3412
- name,
3452
+ cancel,
3413
3453
  folder,
3414
- type,
3454
+ name,
3455
+ progress,
3415
3456
  size,
3416
3457
  status,
3417
- cancel,
3418
- /**
3419
- * @deprecated
3420
- *
3421
- * non-upload view tables do not include "progress" headers but include here to
3422
- * keep TS happy as "progress" headers were included in display text interfaces
3423
- * and cannot be removed from the tables without a breaking change
3424
- */
3425
- progress: ui.noop,
3458
+ type,
3426
3459
  };
3427
- const DELETE_TABLE_RESOLVERS = {
3428
- getCell: (data) => DELETE_CELL_RESOLVERS[data.key](data),
3460
+ const UPLOAD_TABLE_RESOLVERS = {
3461
+ getCell: (data) => UPLOAD_CELL_RESOLVERS[data.key](data),
3429
3462
  getHeader: ({ key, props: { displayText } }) => {
3430
3463
  const text = displayText[`tableColumn${ui.capitalize(key)}Header`];
3431
3464
  if (key === 'cancel') {
@@ -3439,7 +3472,7 @@ const DELETE_TABLE_RESOLVERS = {
3439
3472
  function UploadViewProvider({ children, ...props }) {
3440
3473
  const { UploadView: displayText } = useDisplayText();
3441
3474
  const { actionCancelLabel, actionDestinationLabel, actionExitLabel, actionStartLabel, addFilesLabel, addFolderLabel, statusDisplayCanceledLabel, statusDisplayCompletedLabel, statusDisplayFailedLabel, statusDisplayQueuedLabel, overwriteToggleLabel, title, getActionCompleteMessage, getFilesValidationMessage, } = displayText;
3442
- const { isOverwritingEnabled, isProcessing, isProcessingComplete, location, tasks: items, statusCounts, invalidFiles, onActionStart, onActionCancel, onDropFiles, onActionExit, onTaskRemove, onSelectFiles, onToggleOverwrite, } = props;
3475
+ const { hasNextPage, highestPageVisited, page, isOverwritingEnabled, isProcessing, isProcessingComplete, location, tasks: items, statusCounts, invalidFiles, onActionStart, onActionCancel, onDropFiles, onPaginate, onActionExit, onTaskRemove, onSelectFiles, onToggleOverwrite, } = props;
3443
3476
  const isActionStartDisabled = isProcessing || isProcessingComplete || statusCounts.TOTAL === 0;
3444
3477
  const isActionCancelDisabled = !isProcessing || isProcessingComplete;
3445
3478
  const isAddFilesDisabled = isProcessing || isProcessingComplete;
@@ -3473,6 +3506,11 @@ function UploadViewProvider({ children, ...props }) {
3473
3506
  isOverwriteToggleDisabled: isProcessing || isProcessingComplete,
3474
3507
  isOverwritingEnabled,
3475
3508
  overwriteToggleLabel,
3509
+ paginationData: {
3510
+ page,
3511
+ hasNextPage,
3512
+ highestPageVisited,
3513
+ },
3476
3514
  destination: location,
3477
3515
  message: actionCompleteMessage ?? filesValidationMessage,
3478
3516
  statusCounts,
@@ -3482,21 +3520,50 @@ function UploadViewProvider({ children, ...props }) {
3482
3520
  statusDisplayQueuedLabel,
3483
3521
  tableData,
3484
3522
  title,
3485
- }, onActionCancel: onActionCancel, onActionExit: onActionExit, onActionStart: onActionStart, onAddFiles: () => {
3523
+ }, onActionCancel: onActionCancel, onActionExit: onActionExit, onActionStart: onActionStart, onPaginate: onPaginate, onAddFiles: () => {
3486
3524
  onSelectFiles('FILE');
3487
3525
  }, onAddFolder: () => {
3488
3526
  onSelectFiles('FOLDER');
3489
3527
  }, onDropFiles: onDropFiles, onToggleOverwrite: onToggleOverwrite }, children));
3490
3528
  }
3491
3529
 
3530
+ const DEFAULT_STATE$1 = {
3531
+ validItems: undefined,
3532
+ invalidItems: undefined,
3533
+ };
3534
+ const DEFAULT_RESOLVED_FILES = {
3535
+ validFiles: undefined,
3536
+ invalidFiles: undefined,
3537
+ };
3538
+ const UPLOAD_FILE_SIZE_LIMIT = 160 * 1000 * 1000 * 1000;
3539
+
3492
3540
  const compareFileItems = (prev, next) => prev.key.localeCompare(next.key);
3493
- const resolveFiles = (prevItems, files) => {
3541
+ const constructFiles = (files, file) => ui.isUndefined(files) ? [file] : files.concat(file);
3542
+ const defaultValidateFile = (file) => file.size <= UPLOAD_FILE_SIZE_LIMIT;
3543
+ const resolveFiles = (files, validateFile) => {
3544
+ if (!files?.length)
3545
+ return DEFAULT_RESOLVED_FILES;
3546
+ if (!validateFile)
3547
+ return { validFiles: files, invalidFiles: undefined };
3548
+ return files.reduce((acc, file) => {
3549
+ if (validateFile(file)) {
3550
+ acc.validFiles = constructFiles(acc.validFiles, file);
3551
+ }
3552
+ else {
3553
+ acc.invalidFiles = constructFiles(acc.invalidFiles, file);
3554
+ }
3555
+ return acc;
3556
+ },
3557
+ // create new copy of default to be modified
3558
+ { ...DEFAULT_RESOLVED_FILES });
3559
+ };
3560
+ const processFileItems = (prevItems, files) => {
3494
3561
  if (!files?.length)
3495
3562
  return prevItems;
3496
3563
  // construct `nextItems` and filter out existing `file` entries
3497
3564
  const nextItems = files.reduce((items, file) => {
3498
3565
  const { name, webkitRelativePath } = file;
3499
- return prevItems.some(({ file: existing }) => existing.name === name &&
3566
+ return prevItems?.some(({ file: existing }) => existing.name === name &&
3500
3567
  existing.webkitRelativePath === webkitRelativePath)
3501
3568
  ? items
3502
3569
  : items.concat({
@@ -3507,27 +3574,10 @@ const resolveFiles = (prevItems, files) => {
3507
3574
  }, []);
3508
3575
  if (!nextItems.length)
3509
3576
  return prevItems;
3510
- if (!prevItems.length) {
3577
+ if (!prevItems?.length) {
3511
3578
  return nextItems.sort(compareFileItems);
3512
3579
  }
3513
- return prevItems.concat(nextItems).sort(compareFileItems);
3514
- };
3515
- const filesReducer = (prevItems, input) => {
3516
- switch (input.type) {
3517
- case 'ADD_FILE_ITEMS': {
3518
- return resolveFiles(prevItems, input.files);
3519
- }
3520
- case 'REMOVE_FILE_ITEM': {
3521
- const filteredItems = prevItems.filter(({ id }) => id !== input.id);
3522
- return filteredItems.length === prevItems.length
3523
- ? prevItems
3524
- : filteredItems;
3525
- }
3526
- case 'RESET_FILE_ITEMS': {
3527
- return [];
3528
- }
3529
- // TODO: clear message
3530
- }
3580
+ return prevItems?.concat(nextItems).sort(compareFileItems);
3531
3581
  };
3532
3582
  const parseFileSelectParams = (value) => {
3533
3583
  if (ui.isUndefined(value))
@@ -3538,73 +3588,129 @@ const parseFileSelectParams = (value) => {
3538
3588
  return [selectType, !rest?.length ? undefined : { accept: rest.join() }];
3539
3589
  };
3540
3590
 
3541
- const defaultValue$4 = [undefined, ui.noop];
3542
- const { FilesContext, useFiles } = uiReactCore.createContextUtilities({
3543
- contextName: 'Files',
3591
+ const fileItemsReducer = (state, action) => {
3592
+ switch (action.type) {
3593
+ case 'ADD_FILE_ITEMS': {
3594
+ if (!action.validFiles?.length && !action.invalidFiles?.length) {
3595
+ return state;
3596
+ }
3597
+ const validItems = processFileItems(state.validItems, action.validFiles);
3598
+ // `invalidItems` should only track invalid items from latest action taken
3599
+ const invalidItems = processFileItems(undefined, action.invalidFiles);
3600
+ return { validItems, invalidItems };
3601
+ }
3602
+ case 'REMOVE_FILE_ITEM': {
3603
+ const { validItems: prevItems } = state;
3604
+ if (!prevItems?.length)
3605
+ return state;
3606
+ const nextItems = prevItems.filter(({ id }) => id !== action.id);
3607
+ if (nextItems.length === prevItems.length)
3608
+ return state;
3609
+ // `validItems` is strictly undefined if it has 0 file items
3610
+ // otherwise, `validItems` is guaranteed to have at least 1+ file items
3611
+ const validItems = nextItems.length ? nextItems : undefined;
3612
+ return { ...state, validItems };
3613
+ }
3614
+ case 'RESET_FILE_ITEMS': {
3615
+ return DEFAULT_STATE$1;
3616
+ }
3617
+ }
3618
+ };
3619
+
3620
+ const defaultValue$4 = [DEFAULT_STATE$1, ui.noop];
3621
+ const { FileItemsContext, useFileItems } = uiReactCore.createContextUtilities({
3622
+ contextName: 'FileItems',
3544
3623
  defaultValue: defaultValue$4,
3545
3624
  });
3546
- function FilesProvider({ children, }) {
3547
- const [items, dispatch] = React__namespace["default"].useReducer(filesReducer, []);
3625
+ function FileItemsProvider({ children, validateFile, }) {
3626
+ const [state, dispatch] = React__namespace["default"].useReducer(fileItemsReducer, DEFAULT_STATE$1);
3548
3627
  const [fileInput, handleFileSelect] = internal.useFileSelect((nextFiles) => {
3549
- dispatch({ type: 'ADD_FILE_ITEMS', files: nextFiles });
3628
+ dispatch({
3629
+ type: 'ADD_FILE_ITEMS',
3630
+ ...resolveFiles(nextFiles, validateFile),
3631
+ });
3550
3632
  });
3551
3633
  const handleFilesAction = React__namespace["default"].useCallback((action) => {
3552
3634
  if (action.type === 'SELECT_FILES') {
3553
3635
  handleFileSelect(...parseFileSelectParams(action.selectionType));
3554
3636
  }
3637
+ else if (action.type === 'ADD_FILES') {
3638
+ dispatch({
3639
+ type: 'ADD_FILE_ITEMS',
3640
+ ...resolveFiles(action.files, validateFile),
3641
+ });
3642
+ }
3555
3643
  else {
3556
3644
  dispatch(action);
3557
3645
  }
3558
- }, [handleFileSelect]);
3559
- const value = React__namespace["default"].useMemo(() => [items, handleFilesAction], [items, handleFilesAction]);
3560
- return (React__namespace["default"].createElement(FilesContext.Provider, { value: value },
3646
+ }, [handleFileSelect, validateFile]);
3647
+ const value = React__namespace["default"].useMemo(() => [state, handleFilesAction], [state, handleFilesAction]);
3648
+ return (React__namespace["default"].createElement(FileItemsContext.Provider, { value: value },
3561
3649
  fileInput,
3562
3650
  children));
3563
3651
  }
3564
3652
 
3565
3653
  const DEFAULT_OVERWRITE_ENABLED = false;
3566
3654
 
3655
+ const DEFAULT_PAGE_SIZE$4 = 100;
3656
+ const usePaginate = ({ items, onPaginate, page = 1, pageSize = DEFAULT_PAGE_SIZE$4, }) => {
3657
+ const [currentPage, setCurrentPage] = React__namespace["default"].useState(page);
3658
+ const visitedRef = React__namespace["default"].useRef(page);
3659
+ const handleReset = React__namespace["default"].useRef(() => {
3660
+ setCurrentPage(page);
3661
+ // set `visitedRef` to initially provided `page`
3662
+ visitedRef.current = page;
3663
+ }).current;
3664
+ return React__namespace["default"].useMemo(() => {
3665
+ const hasItems = Array.isArray(items);
3666
+ const highestPageVisited = visitedRef.current;
3667
+ const isFirstPage = currentPage === 1;
3668
+ const start = isFirstPage ? 0 : (currentPage - 1) * pageSize;
3669
+ const end = isFirstPage ? pageSize : currentPage * pageSize;
3670
+ const pageItems = hasItems ? items.slice(start, end) : [];
3671
+ return {
3672
+ currentPage,
3673
+ handlePaginate: (page) => {
3674
+ if (page < 1)
3675
+ return;
3676
+ if (ui.isFunction(onPaginate))
3677
+ onPaginate(page);
3678
+ if (page > currentPage && page > highestPageVisited)
3679
+ visitedRef.current = page;
3680
+ setCurrentPage(page);
3681
+ },
3682
+ handleReset,
3683
+ highestPageVisited,
3684
+ pageItems,
3685
+ };
3686
+ }, [currentPage, handleReset, items, onPaginate, pageSize]);
3687
+ };
3688
+
3689
+ const DEFAULT_PAGE_SIZE$3 = 100;
3567
3690
  const useUploadView = (options) => {
3568
3691
  const { onExit: _onExit } = options ?? {};
3569
3692
  const [{ location }, storeDispatch] = useStore();
3570
- const [files, filesDispatch] = useFiles();
3693
+ const [{ validItems, invalidItems: invalidFiles }, fileItemsDispatch] = useFileItems();
3571
3694
  const { current } = location;
3572
3695
  const [isOverwritingEnabled, setIsOverwritingEnabled] = React__namespace["default"].useState(DEFAULT_OVERWRITE_ENABLED);
3573
- const filesData = React__namespace["default"].useMemo(() => (files ?? [])?.reduce((curr, item) => {
3574
- if (isFileTooBig(item.file)) {
3575
- curr.invalidFiles = ui.isUndefined(curr.invalidFiles)
3576
- ? [item]
3577
- : curr.invalidFiles.concat(item);
3578
- }
3579
- else {
3580
- curr.validFiles = ui.isUndefined(curr.validFiles)
3581
- ? [item]
3582
- : curr.validFiles.concat(item);
3583
- const parsedFileItem = {
3584
- ...item,
3585
- key: `${location.key}${item.key}`,
3586
- };
3587
- curr.data = curr.data.concat({
3588
- ...parsedFileItem,
3589
- preventOverwrite: !isOverwritingEnabled,
3590
- });
3591
- }
3592
- return curr;
3593
- }, { invalidFiles: undefined, validFiles: undefined, data: [] }), [files, isOverwritingEnabled, location.key]);
3594
- const { data, invalidFiles } = filesData;
3595
- const [{ isProcessing, isProcessingComplete, statusCounts, tasks }, handleUploads,] = useAction('upload', { items: data });
3696
+ const items = React__namespace["default"].useMemo(() => (validItems ?? []).map((item) => ({
3697
+ ...item,
3698
+ key: `${location.key}${item.key}`,
3699
+ preventOverwrite: !isOverwritingEnabled,
3700
+ })), [validItems, isOverwritingEnabled, location.key]);
3701
+ const [{ isProcessing, isProcessingComplete, statusCounts, tasks }, handleUploads,] = useAction('upload', { items });
3702
+ const { currentPage, handlePaginate, pageItems: pageTasks, } = usePaginate({
3703
+ items: tasks,
3704
+ });
3596
3705
  const onDropFiles = (files) => {
3597
3706
  if (files) {
3598
- filesDispatch({ type: 'ADD_FILE_ITEMS', files });
3707
+ fileItemsDispatch({ type: 'ADD_FILES', files });
3599
3708
  }
3600
3709
  };
3601
3710
  const onSelectFiles = (type) => {
3602
- filesDispatch({ type: 'SELECT_FILES', selectionType: type });
3711
+ fileItemsDispatch({ type: 'SELECT_FILES', selectionType: type });
3603
3712
  };
3604
3713
  const onActionStart = () => {
3605
- invalidFiles?.forEach((file) => {
3606
- filesDispatch({ type: 'REMOVE_FILE_ITEM', id: file.id });
3607
- });
3608
3714
  handleUploads();
3609
3715
  };
3610
3716
  const onActionCancel = () => {
@@ -3612,7 +3718,7 @@ const useUploadView = (options) => {
3612
3718
  };
3613
3719
  const onActionExit = () => {
3614
3720
  // clear files state
3615
- filesDispatch({ type: 'RESET_FILE_ITEMS' });
3721
+ fileItemsDispatch({ type: 'RESET_FILE_ITEMS' });
3616
3722
  // clear selected action
3617
3723
  storeDispatch({ type: 'RESET_ACTION_TYPE' });
3618
3724
  _onExit?.(current);
@@ -3621,7 +3727,7 @@ const useUploadView = (options) => {
3621
3727
  setIsOverwritingEnabled((prev) => !prev);
3622
3728
  };
3623
3729
  const onTaskRemove = ({ data }) => {
3624
- filesDispatch({ type: 'REMOVE_FILE_ITEM', id: data.id });
3730
+ fileItemsDispatch({ type: 'REMOVE_FILE_ITEM', id: data.id });
3625
3731
  };
3626
3732
  return {
3627
3733
  isProcessing,
@@ -3630,11 +3736,15 @@ const useUploadView = (options) => {
3630
3736
  location,
3631
3737
  invalidFiles,
3632
3738
  statusCounts,
3633
- tasks,
3739
+ tasks: pageTasks,
3740
+ page: currentPage,
3741
+ hasNextPage: currentPage * DEFAULT_PAGE_SIZE$3 < items.length,
3742
+ highestPageVisited: Math.ceil(items.length / DEFAULT_PAGE_SIZE$3),
3634
3743
  onActionCancel,
3635
3744
  onActionExit,
3636
3745
  onActionStart,
3637
3746
  onDropFiles,
3747
+ onPaginate: handlePaginate,
3638
3748
  onTaskRemove,
3639
3749
  onSelectFiles,
3640
3750
  onToggleOverwrite,
@@ -3650,6 +3760,7 @@ const UploadView = ({ className, ...props }) => {
3650
3760
  React__namespace["default"].createElement(ViewElement, { className: `${STORAGE_BROWSER_BLOCK}__controls` },
3651
3761
  React__namespace["default"].createElement(OverwriteToggleControl, null),
3652
3762
  React__namespace["default"].createElement(ViewElement, { className: `${STORAGE_BROWSER_BLOCK}__buttons` },
3763
+ React__namespace["default"].createElement(PaginationControl, null),
3653
3764
  React__namespace["default"].createElement(AddFolderControl, null),
3654
3765
  React__namespace["default"].createElement(AddFilesControl, null))),
3655
3766
  React__namespace["default"].createElement(DropZoneControl, null,
@@ -3669,6 +3780,7 @@ UploadView.displayName = 'UploadView';
3669
3780
  UploadView.Provider = UploadViewProvider;
3670
3781
  UploadView.AddFiles = AddFilesControl;
3671
3782
  UploadView.AddFolder = AddFolderControl;
3783
+ UploadView.Pagination = PaginationControl;
3672
3784
  UploadView.Cancel = ActionCancelControl;
3673
3785
  UploadView.Destination = ActionDestinationControl;
3674
3786
  UploadView.DropZone = DropZoneControl;
@@ -3854,7 +3966,7 @@ function CopyViewProvider({ children, ...props }) {
3854
3966
  const { actionCancelLabel, actionDestinationLabel, actionExitLabel, actionStartLabel, getActionCompleteMessage, overwriteWarningMessage, searchPlaceholder, searchSubmitLabel, searchClearLabel, statusDisplayCanceledLabel, statusDisplayCompletedLabel, statusDisplayFailedLabel, statusDisplayQueuedLabel, title, } = displayText;
3855
3967
  const { destination, folders, isProcessing, isProcessingComplete, statusCounts, tasks: items, onActionCancel, onActionExit, onActionStart, onSelectDestination, onTaskRemove, } = props;
3856
3968
  const { hasNextPage, highestPageVisited, hasError: hasFoldersError, message: foldersErrorMessage, query, hasExhaustedSearch, isLoading, page, pageItems, onPaginate, onQuery, onSearchClear, onSearch, onSelectFolder, } = folders;
3857
- const tableData = useResolveTableData(COPY_TABLE_KEYS, COPY_TABLE_RESOLVERS, {
3969
+ const tableData = useResolveTableData(FILE_DATA_ITEM_TABLE_KEYS, COPY_TABLE_RESOLVERS, {
3858
3970
  items,
3859
3971
  props: { displayText, isProcessing, onTaskRemove },
3860
3972
  });
@@ -3938,40 +4050,6 @@ function LocationItemsProvider({ children, }) {
3938
4050
  return (React__namespace["default"].createElement(LocationItemsContext.Provider, { value: value }, children));
3939
4051
  }
3940
4052
 
3941
- const DEFAULT_PAGE_SIZE$3 = 100;
3942
- const usePaginate = ({ items, onPaginate, page = 1, pageSize = DEFAULT_PAGE_SIZE$3, }) => {
3943
- const [currentPage, setCurrentPage] = React__namespace["default"].useState(page);
3944
- const visitedRef = React__namespace["default"].useRef(page);
3945
- const handleReset = React__namespace["default"].useRef(() => {
3946
- setCurrentPage(page);
3947
- // set `visitedRef` to initially provided `page`
3948
- visitedRef.current = page;
3949
- }).current;
3950
- return React__namespace["default"].useMemo(() => {
3951
- const hasItems = Array.isArray(items);
3952
- const highestPageVisited = visitedRef.current;
3953
- const isFirstPage = currentPage === 1;
3954
- const start = isFirstPage ? 0 : (currentPage - 1) * pageSize;
3955
- const end = isFirstPage ? pageSize : currentPage * pageSize;
3956
- const pageItems = hasItems ? items.slice(start, end) : [];
3957
- return {
3958
- currentPage,
3959
- handlePaginate: (page) => {
3960
- if (page < 1)
3961
- return;
3962
- if (ui.isFunction(onPaginate))
3963
- onPaginate(page);
3964
- if (page > currentPage)
3965
- visitedRef.current = page;
3966
- setCurrentPage(page);
3967
- },
3968
- handleReset,
3969
- highestPageVisited,
3970
- pageItems,
3971
- };
3972
- }, [currentPage, handleReset, items, onPaginate, pageSize]);
3973
- };
3974
-
3975
4053
  function useSearch(props) {
3976
4054
  const { onSearch } = props;
3977
4055
  const [searchQuery, setQuery] = React__namespace["default"].useState('');
@@ -4198,7 +4276,7 @@ function DeleteViewProvider({ children, ...props }) {
4198
4276
  const message = isProcessingComplete
4199
4277
  ? getActionCompleteMessage({ counts: statusCounts })
4200
4278
  : undefined;
4201
- const tableData = useResolveTableData(DELETE_TABLE_KEYS, DELETE_TABLE_RESOLVERS, {
4279
+ const tableData = useResolveTableData(FILE_DATA_ITEM_TABLE_KEYS, DELETE_TABLE_RESOLVERS, {
4202
4280
  items,
4203
4281
  props: { displayText, isProcessing, onTaskRemove },
4204
4282
  });
@@ -4221,13 +4299,13 @@ function DeleteViewProvider({ children, ...props }) {
4221
4299
  }
4222
4300
 
4223
4301
  // assign to constant to ensure referential equality
4224
- const EMPTY_ITEMS = [];
4302
+ const EMPTY_ITEMS$1 = [];
4225
4303
  const useDeleteView = (options) => {
4226
4304
  const { onExit: _onExit } = options ?? {};
4227
4305
  const [{ location }, storeDispatch] = useStore();
4228
4306
  const [locationItems, locationItemsDispatch] = useLocationItems();
4229
4307
  const { current } = location;
4230
- const { fileDataItems: items = EMPTY_ITEMS } = locationItems;
4308
+ const { fileDataItems: items = EMPTY_ITEMS$1 } = locationItems;
4231
4309
  const [processState, handleProcess] = useAction('delete', { items });
4232
4310
  const { isProcessing, isProcessingComplete, statusCounts, tasks } = processState;
4233
4311
  const onActionStart = () => {
@@ -4293,10 +4371,117 @@ DeleteView.Statuses = StatusDisplayControl;
4293
4371
  DeleteView.TasksTable = DataTableControl;
4294
4372
  DeleteView.Title = TitleControl;
4295
4373
 
4374
+ function DownloadViewProvider({ children, ...props }) {
4375
+ const { DownloadView: displayText } = useDisplayText();
4376
+ const { actionCancelLabel, actionExitLabel, actionStartLabel, title, statusDisplayCanceledLabel, statusDisplayCompletedLabel, statusDisplayFailedLabel, statusDisplayQueuedLabel, getActionCompleteMessage, } = displayText;
4377
+ const { isProcessing, isProcessingComplete, statusCounts, tasks: items, onActionCancel, onActionStart, onActionExit, onTaskRemove, } = props;
4378
+ const message = isProcessingComplete
4379
+ ? getActionCompleteMessage({ counts: statusCounts })
4380
+ : undefined;
4381
+ const tableData = useResolveTableData(FILE_DATA_ITEM_TABLE_KEYS, DOWNLOAD_TABLE_RESOLVERS, {
4382
+ items,
4383
+ props: { displayText, isProcessing, onTaskRemove },
4384
+ });
4385
+ return (React__namespace["default"].createElement(ControlsContextProvider, { data: {
4386
+ actionCancelLabel,
4387
+ actionExitLabel,
4388
+ actionStartLabel,
4389
+ isActionCancelDisabled: !isProcessing || isProcessingComplete,
4390
+ isActionExitDisabled: isProcessing,
4391
+ isActionStartDisabled: isProcessing || isProcessingComplete,
4392
+ statusDisplayCanceledLabel,
4393
+ statusDisplayCompletedLabel,
4394
+ statusDisplayFailedLabel,
4395
+ statusDisplayQueuedLabel,
4396
+ statusCounts,
4397
+ tableData,
4398
+ title,
4399
+ message,
4400
+ }, onActionStart: onActionStart, onActionExit: onActionExit, onActionCancel: onActionCancel }, children));
4401
+ }
4402
+
4403
+ // assign to constant to ensure referential equality
4404
+ const EMPTY_ITEMS = [];
4405
+ const useDownloadView = (options) => {
4406
+ const { onExit: _onExit } = options ?? {};
4407
+ const [{ location }, storeDispatch] = useStore();
4408
+ const [locationItems, locationItemsDispatch] = useLocationItems();
4409
+ const { current } = location;
4410
+ const { fileDataItems: items = EMPTY_ITEMS } = locationItems;
4411
+ const [processState, handleProcess] = useAction('download', {
4412
+ items,
4413
+ });
4414
+ const { isProcessing, isProcessingComplete, statusCounts, tasks } = processState;
4415
+ const onActionStart = () => {
4416
+ if (!current)
4417
+ return;
4418
+ handleProcess();
4419
+ };
4420
+ const onActionCancel = () => {
4421
+ tasks.forEach((task) => {
4422
+ // Calling cancel on task works only on queued tasks.
4423
+ // In case of download, all download presigned url open at once
4424
+ // When certain threshold is reached for queuing inside StorageBrowser, cancel might be possible.
4425
+ if (ui.isFunction(task.cancel))
4426
+ task.cancel();
4427
+ });
4428
+ };
4429
+ const onActionExit = () => {
4430
+ // clear files state
4431
+ locationItemsDispatch({ type: 'RESET_LOCATION_ITEMS' });
4432
+ // clear selected action
4433
+ storeDispatch({ type: 'RESET_ACTION_TYPE' });
4434
+ if (ui.isFunction(_onExit))
4435
+ _onExit(current);
4436
+ };
4437
+ const onTaskRemove = React__namespace["default"].useCallback(({ data }) => {
4438
+ locationItemsDispatch({ type: 'REMOVE_LOCATION_ITEM', id: data.id });
4439
+ }, [locationItemsDispatch]);
4440
+ return {
4441
+ isProcessing,
4442
+ isProcessingComplete,
4443
+ location,
4444
+ statusCounts,
4445
+ tasks,
4446
+ onActionCancel,
4447
+ onActionExit,
4448
+ onActionStart,
4449
+ onTaskRemove,
4450
+ };
4451
+ };
4452
+
4453
+ const DownloadView = ({ className, ...props }) => {
4454
+ const state = useDownloadView(props);
4455
+ return (React__namespace["default"].createElement(ViewElement, { className: ui.classNames(STORAGE_BROWSER_BLOCK, className) },
4456
+ React__namespace["default"].createElement(DownloadViewProvider, { ...state },
4457
+ React__namespace["default"].createElement(ActionExitControl, null),
4458
+ React__namespace["default"].createElement(TitleControl, null),
4459
+ React__namespace["default"].createElement(ViewElement, { className: `${STORAGE_BROWSER_BLOCK}__data-table` },
4460
+ React__namespace["default"].createElement(DataTableControl, null)),
4461
+ React__namespace["default"].createElement(ViewElement, { className: `${STORAGE_BROWSER_BLOCK}__summary` },
4462
+ React__namespace["default"].createElement(StatusDisplayControl, null)),
4463
+ React__namespace["default"].createElement(ViewElement, { className: `${STORAGE_BROWSER_BLOCK}__footer` },
4464
+ React__namespace["default"].createElement(ViewElement, { className: `${STORAGE_BROWSER_BLOCK}__message` },
4465
+ React__namespace["default"].createElement(MessageControl, null)),
4466
+ React__namespace["default"].createElement(ViewElement, { className: `${STORAGE_BROWSER_BLOCK}__buttons` },
4467
+ React__namespace["default"].createElement(ActionCancelControl, null),
4468
+ React__namespace["default"].createElement(ActionStartControl, null))))));
4469
+ };
4470
+ DownloadView.displayName = 'DownloadView';
4471
+ DownloadView.Provider = DownloadViewProvider;
4472
+ DownloadView.Cancel = ActionCancelControl;
4473
+ DownloadView.Exit = ActionExitControl;
4474
+ DownloadView.Message = MessageControl;
4475
+ DownloadView.Start = ActionStartControl;
4476
+ DownloadView.Statuses = StatusDisplayControl;
4477
+ DownloadView.TasksTable = DataTableControl;
4478
+ DownloadView.Title = TitleControl;
4479
+
4296
4480
  const DEFAULT_ACTION_VIEWS = {
4297
4481
  createFolder: CreateFolderView,
4298
4482
  copy: CopyView,
4299
4483
  delete: DeleteView,
4484
+ download: DownloadView,
4300
4485
  upload: UploadView,
4301
4486
  };
4302
4487
  const ActionViewsContext = React__namespace["default"].createContext({
@@ -4637,7 +4822,7 @@ const useLocationDetailView = (options) => {
4637
4822
  const listOptions = listOptionsRef.current;
4638
4823
  const [{ location, actionType }, storeDispatch] = useStore();
4639
4824
  const [locationItems, locationItemsDispatch] = useLocationItems();
4640
- const filesDispatch = useFiles()[1];
4825
+ const fileItemsDispatch = useFileItems()[1];
4641
4826
  const { current, key } = location;
4642
4827
  const { permissions, prefix } = current ?? {};
4643
4828
  const { fileDataItems } = locationItems;
@@ -4753,7 +4938,7 @@ const useLocationDetailView = (options) => {
4753
4938
  locationItemsDispatch({ type: 'RESET_LOCATION_ITEMS' });
4754
4939
  },
4755
4940
  onDropFiles: (files) => {
4756
- filesDispatch({ type: 'ADD_FILE_ITEMS', files });
4941
+ fileItemsDispatch({ type: 'ADD_FILES', files });
4757
4942
  const actionType = 'upload';
4758
4943
  storeDispatch({ type: 'CHANGE_ACTION_TYPE', actionType });
4759
4944
  options?.onActionSelect?.(actionType);
@@ -5144,6 +5329,7 @@ function useViews() {
5144
5329
  const USE_VIEW_HOOKS = {
5145
5330
  Copy: useCopyView,
5146
5331
  CreateFolder: useCreateFolderView,
5332
+ Download: useDownloadView,
5147
5333
  Delete: useDeleteView,
5148
5334
  LocationDetail: useLocationDetailView,
5149
5335
  Locations: useLocationsView,
@@ -5157,7 +5343,7 @@ const useView = (type) => {
5157
5343
  return USE_VIEW_HOOKS[type]();
5158
5344
  };
5159
5345
 
5160
- function createProvider({ actions, components, config, }) {
5346
+ function createProvider({ actions, components, config, options, }) {
5161
5347
  const { accountId, customEndpoint, registerAuthListener, getLocationCredentials, region, listLocations, } = config;
5162
5348
  const resolvedActions = {
5163
5349
  default: {
@@ -5186,6 +5372,7 @@ function createProvider({ actions, components, config, }) {
5186
5372
  // override components
5187
5373
  ...components,
5188
5374
  };
5375
+ const { validateFile = defaultValidateFile } = options ?? {};
5189
5376
  /**
5190
5377
  * Provides state, configuration and action values that are shared between
5191
5378
  * the primary View components
@@ -5199,7 +5386,7 @@ function createProvider({ actions, components, config, }) {
5199
5386
  React__namespace["default"].createElement(ViewsProvider, { actions: resolvedActions, views: views },
5200
5387
  React__namespace["default"].createElement(ComponentsProvider, { composables: composables },
5201
5388
  React__namespace["default"].createElement(LocationItemsProvider, null,
5202
- React__namespace["default"].createElement(FilesProvider, null, children))))))))));
5389
+ React__namespace["default"].createElement(FileItemsProvider, { validateFile: validateFile }, children))))))))));
5203
5390
  }
5204
5391
  return Provider;
5205
5392
  }
@@ -5256,6 +5443,7 @@ function createStorageBrowser(input) {
5256
5443
  StorageBrowser.CopyView = CopyView;
5257
5444
  StorageBrowser.CreateFolderView = CreateFolderView;
5258
5445
  StorageBrowser.DeleteView = DeleteView;
5446
+ StorageBrowser.DownloadView = DownloadView;
5259
5447
  StorageBrowser.UploadView = UploadView;
5260
5448
  StorageBrowser.Provider = Provider;
5261
5449
  StorageBrowser.displayName = 'StorageBrowser';