@aws-amplify/ui-react-storage 3.15.0 → 3.16.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 (80) hide show
  1. package/dist/browser.js +1 -1
  2. package/dist/{createStorageBrowser-CG-6mXiT.js → createStorageBrowser-B-J76Lyp.js} +624 -105
  3. package/dist/esm/components/StorageBrowser/ErrorBoundary/ErrorBoundary.mjs +0 -4
  4. package/dist/esm/components/StorageBrowser/actions/configs/defaults.mjs +13 -3
  5. package/dist/esm/components/StorageBrowser/actions/handlers/delete.mjs +39 -8
  6. package/dist/esm/components/StorageBrowser/components/ComponentsProvider.mjs +0 -4
  7. package/dist/esm/components/StorageBrowser/components/base/preview/DownloadButton.mjs +0 -4
  8. package/dist/esm/components/StorageBrowser/components/composables/ActionConfirmationModal.mjs +34 -0
  9. package/dist/esm/components/StorageBrowser/components/composables/defaults.mjs +2 -0
  10. package/dist/esm/components/StorageBrowser/components/elements/definitions.mjs +2 -2
  11. package/dist/esm/components/StorageBrowser/controls/ActionConfirmationModalControl.mjs +12 -0
  12. package/dist/esm/components/StorageBrowser/controls/DataTableControl.mjs +0 -4
  13. package/dist/esm/components/StorageBrowser/controls/hooks/useActionConfirmationModal.mjs +17 -0
  14. package/dist/esm/components/StorageBrowser/createStorageBrowser/StorageBrowserDefault.mjs +8 -4
  15. package/dist/esm/components/StorageBrowser/createStorageBrowser/createStorageBrowser.mjs +9 -5
  16. package/dist/esm/components/StorageBrowser/displayText/libraries/en/deleteView.mjs +117 -5
  17. package/dist/esm/components/StorageBrowser/displayText/libraries/en/locationDetailView.mjs +1 -0
  18. package/dist/esm/components/StorageBrowser/displayText/libraries/en/shared.mjs +1 -0
  19. package/dist/esm/components/StorageBrowser/locationItems/context.mjs +17 -14
  20. package/dist/esm/components/StorageBrowser/locationItems/utils.mjs +38 -0
  21. package/dist/esm/components/StorageBrowser/store/validateStoreProps.mjs +1 -1
  22. package/dist/esm/components/StorageBrowser/tasks/useProcessTasks.mjs +7 -4
  23. package/dist/esm/components/StorageBrowser/useAction/utils.mjs +0 -5
  24. package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/CopyView.mjs +0 -4
  25. package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/CopyViewProvider.mjs +4 -4
  26. package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/FoldersMessageControl.mjs +0 -4
  27. package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/useCopyView.mjs +0 -4
  28. package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/useFolders.mjs +0 -4
  29. package/dist/esm/components/StorageBrowser/views/LocationActionView/CreateFolderView/CreateFolderView.mjs +0 -4
  30. package/dist/esm/components/StorageBrowser/views/LocationActionView/CreateFolderView/useCreateFolderView.mjs +0 -4
  31. package/dist/esm/components/StorageBrowser/views/LocationActionView/DeleteView/DeleteView.mjs +5 -6
  32. package/dist/esm/components/StorageBrowser/views/LocationActionView/DeleteView/DeleteViewProvider.mjs +7 -6
  33. package/dist/esm/components/StorageBrowser/views/LocationActionView/DeleteView/useDeleteView.mjs +69 -7
  34. package/dist/esm/components/StorageBrowser/views/LocationActionView/DeleteView/utils.mjs +87 -0
  35. package/dist/esm/components/StorageBrowser/views/LocationActionView/DownloadView/DownloadView.mjs +0 -4
  36. package/dist/esm/components/StorageBrowser/views/LocationActionView/DownloadView/DownloadViewProvider.mjs +9 -0
  37. package/dist/esm/components/StorageBrowser/views/LocationActionView/DownloadView/useDownloadView.mjs +0 -4
  38. package/dist/esm/components/StorageBrowser/views/LocationActionView/UploadView/UploadView.mjs +1 -5
  39. package/dist/esm/components/StorageBrowser/views/LocationActionView/UploadView/UploadViewProvider.mjs +3 -0
  40. package/dist/esm/components/StorageBrowser/views/LocationActionView/UploadView/useUploadView.mjs +0 -4
  41. package/dist/esm/components/StorageBrowser/views/LocationDetailView/LocationDetailView.mjs +0 -4
  42. package/dist/esm/components/StorageBrowser/views/LocationDetailView/LocationDetailViewProvider.mjs +2 -1
  43. package/dist/esm/components/StorageBrowser/views/LocationDetailView/getLocationDetailViewTableData/getFolderRowContent.mjs +38 -27
  44. package/dist/esm/components/StorageBrowser/views/LocationDetailView/getLocationDetailViewTableData/getLocationDetailViewTableData.mjs +8 -1
  45. package/dist/esm/components/StorageBrowser/views/LocationDetailView/useLocationDetailView.mjs +10 -9
  46. package/dist/esm/components/StorageBrowser/views/LocationsView/LocationsView.mjs +0 -4
  47. package/dist/esm/components/StorageBrowser/views/LocationsView/LocationsViewProvider.mjs +0 -4
  48. package/dist/esm/components/StorageBrowser/views/context/actionViews.mjs +7 -4
  49. package/dist/esm/components/StorageBrowser/views/context/primaryViews.mjs +8 -4
  50. package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/constants.mjs +10 -1
  51. package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/deleteResolvers.mjs +123 -13
  52. package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/utils.mjs +3 -3
  53. package/dist/esm/version.mjs +1 -1
  54. package/dist/index.js +1 -1
  55. package/dist/styles.css +83 -1
  56. package/dist/types/components/StorageBrowser/actions/handlers/delete.d.ts +5 -3
  57. package/dist/types/components/StorageBrowser/actions/handlers/types.d.ts +15 -1
  58. package/dist/types/components/StorageBrowser/components/composables/ActionConfirmationModal.d.ts +12 -0
  59. package/dist/types/components/StorageBrowser/components/composables/types.d.ts +2 -0
  60. package/dist/types/components/StorageBrowser/controls/ActionConfirmationModalControl.d.ts +2 -0
  61. package/dist/types/components/StorageBrowser/controls/hooks/useActionConfirmationModal.d.ts +2 -0
  62. package/dist/types/components/StorageBrowser/controls/index.d.ts +1 -0
  63. package/dist/types/components/StorageBrowser/controls/types.d.ts +4 -0
  64. package/dist/types/components/StorageBrowser/displayText/types.d.ts +7 -0
  65. package/dist/types/components/StorageBrowser/locationItems/context.d.ts +12 -3
  66. package/dist/types/components/StorageBrowser/locationItems/index.d.ts +1 -0
  67. package/dist/types/components/StorageBrowser/locationItems/utils.d.ts +27 -0
  68. package/dist/types/components/StorageBrowser/tasks/types.d.ts +7 -2
  69. package/dist/types/components/StorageBrowser/views/LocationActionView/DeleteView/types.d.ts +5 -0
  70. package/dist/types/components/StorageBrowser/views/LocationActionView/DeleteView/utils.d.ts +26 -0
  71. package/dist/types/components/StorageBrowser/views/LocationDetailView/getLocationDetailViewTableData/getFolderRowContent.d.ts +4 -1
  72. package/dist/types/components/StorageBrowser/views/LocationDetailView/getLocationDetailViewTableData/getLocationDetailViewTableData.d.ts +3 -2
  73. package/dist/types/components/StorageBrowser/views/LocationDetailView/types.d.ts +8 -1
  74. package/dist/types/components/StorageBrowser/views/utils/index.d.ts +1 -1
  75. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/constants.d.ts +1 -0
  76. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/deleteResolvers.d.ts +5 -2
  77. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/index.d.ts +1 -1
  78. package/dist/types/components/StorageBrowser/views/utils/tableResolvers/types.d.ts +4 -0
  79. package/dist/types/version.d.ts +1 -1
  80. package/package.json +7 -7
@@ -13,10 +13,6 @@ import '@aws-amplify/ui-react-core/elements';
13
13
  import '../../../credentials/context.mjs';
14
14
  import '@aws-amplify/storage/internals';
15
15
  import '../../../configuration/context.mjs';
16
- import 'aws-amplify';
17
- import 'jszip';
18
- import 'aws-amplify/storage';
19
- import '../../../actions/configs/context.mjs';
20
16
  import '../../../actions/configs/defaults.mjs';
21
17
  import { useDisplayText } from '../../../displayText/context.mjs';
22
18
  import '../../../filePreview/context.mjs';
@@ -9,10 +9,6 @@ import '@aws-amplify/ui-react-core/elements';
9
9
  import '../../../credentials/context.mjs';
10
10
  import '@aws-amplify/storage/internals';
11
11
  import '../../../configuration/context.mjs';
12
- import 'aws-amplify';
13
- import 'jszip';
14
- import 'aws-amplify/storage';
15
- import '../../../actions/configs/context.mjs';
16
12
  import '../../../actions/configs/defaults.mjs';
17
13
  import { useFolders } from './useFolders.mjs';
18
14
 
@@ -6,10 +6,6 @@ import '@aws-amplify/storage/internals';
6
6
  import '../../../configuration/context.mjs';
7
7
  import '@aws-amplify/ui';
8
8
  import { useList } from '../../../useAction/useList.mjs';
9
- import 'aws-amplify';
10
- import 'jszip';
11
- import 'aws-amplify/storage';
12
- import '../../../actions/configs/context.mjs';
13
9
  import '../../../actions/configs/defaults.mjs';
14
10
  import { usePaginate } from '../../hooks/usePaginate.mjs';
15
11
  import { useSearch } from '../../hooks/useSearch.mjs';
@@ -11,10 +11,6 @@ import '@aws-amplify/ui-react-core/elements';
11
11
  import '../../../credentials/context.mjs';
12
12
  import '@aws-amplify/storage/internals';
13
13
  import '../../../configuration/context.mjs';
14
- import 'aws-amplify';
15
- import 'jszip';
16
- import 'aws-amplify/storage';
17
- import '../../../actions/configs/context.mjs';
18
14
  import '../../../actions/configs/defaults.mjs';
19
15
  import '../../../displayText/context.mjs';
20
16
  import '../../../filePreview/context.mjs';
@@ -7,10 +7,6 @@ import '@aws-amplify/ui-react-core/elements';
7
7
  import '../../../credentials/context.mjs';
8
8
  import '@aws-amplify/storage/internals';
9
9
  import '../../../configuration/context.mjs';
10
- import 'aws-amplify';
11
- import 'jszip';
12
- import 'aws-amplify/storage';
13
- import '../../../actions/configs/context.mjs';
14
10
  import '../../../actions/configs/defaults.mjs';
15
11
  import { useStore } from '../../../store/context.mjs';
16
12
 
@@ -1,15 +1,16 @@
1
1
  import React__default from 'react';
2
+ import { classNames } from '@aws-amplify/ui';
2
3
  import '@aws-amplify/ui-react';
3
4
  import { ViewElement } from '../../../components/elements/definitions.mjs';
4
5
  import '../../../components/elements/IconElement.mjs';
5
6
  import { ActionCancelControl } from '../../../controls/ActionCancelControl.mjs';
7
+ import { ActionConfirmationModalControl } from '../../../controls/ActionConfirmationModalControl.mjs';
6
8
  import { ActionExitControl } from '../../../controls/ActionExitControl.mjs';
7
9
  import { ActionStartControl } from '../../../controls/ActionStartControl.mjs';
8
10
  import { DataTableControl } from '../../../controls/DataTableControl.mjs';
9
11
  import { MessageControl } from '../../../controls/MessageControl.mjs';
10
12
  import { StatusDisplayControl } from '../../../controls/StatusDisplayControl.mjs';
11
13
  import { TitleControl } from '../../../controls/TitleControl.mjs';
12
- import { classNames } from '@aws-amplify/ui';
13
14
  import { STORAGE_BROWSER_BLOCK } from '../../../components/base/constants.mjs';
14
15
  import '../../../components/composables/context.mjs';
15
16
  import '@aws-amplify/ui-react-core';
@@ -18,10 +19,6 @@ import '@aws-amplify/ui-react-core/elements';
18
19
  import '../../../credentials/context.mjs';
19
20
  import '@aws-amplify/storage/internals';
20
21
  import '../../../configuration/context.mjs';
21
- import 'aws-amplify';
22
- import 'jszip';
23
- import 'aws-amplify/storage';
24
- import '../../../actions/configs/context.mjs';
25
22
  import '../../../actions/configs/defaults.mjs';
26
23
  import '../../../displayText/context.mjs';
27
24
  import '../../../filePreview/context.mjs';
@@ -43,11 +40,13 @@ const DeleteView = ({ className, ...props }) => {
43
40
  React__default.createElement(MessageControl, null)),
44
41
  React__default.createElement(ViewElement, { className: `${STORAGE_BROWSER_BLOCK}__buttons` },
45
42
  React__default.createElement(ActionCancelControl, null),
46
- React__default.createElement(ActionStartControl, null))))));
43
+ React__default.createElement(ActionStartControl, null))),
44
+ React__default.createElement(ActionConfirmationModalControl, null))));
47
45
  };
48
46
  DeleteView.displayName = 'DeleteView';
49
47
  DeleteView.Provider = DeleteViewProvider;
50
48
  DeleteView.Cancel = ActionCancelControl;
49
+ DeleteView.ConfirmationModal = ActionConfirmationModalControl;
51
50
  DeleteView.Exit = ActionExitControl;
52
51
  DeleteView.Message = MessageControl;
53
52
  DeleteView.Start = ActionStartControl;
@@ -3,18 +3,18 @@ import { ControlsContextProvider } from '../../../controls/context.mjs';
3
3
  import { useDisplayText } from '../../../displayText/context.mjs';
4
4
  import useResolveTableData from '../../hooks/useResolveTableData/useResolveTableData.mjs';
5
5
  import '@aws-amplify/ui';
6
- import { FILE_DATA_ITEM_TABLE_KEYS } from '../../utils/tableResolvers/constants.mjs';
6
+ import { DELETE_TABLE_KEYS } from '../../utils/tableResolvers/constants.mjs';
7
7
  import { DELETE_TABLE_RESOLVERS } from '../../utils/tableResolvers/deleteResolvers.mjs';
8
8
 
9
9
  function DeleteViewProvider({ children, ...props }) {
10
10
  const { DeleteView: displayText } = useDisplayText();
11
11
  const { actionCancelLabel, actionExitLabel, actionStartLabel, title, statusDisplayCanceledLabel, statusDisplayCompletedLabel, statusDisplayFailedLabel, statusDisplayQueuedLabel, getActionCompleteMessage, } = displayText;
12
- const { isProcessing, isProcessingComplete, statusCounts, tasks: items, onActionCancel, onActionStart, onActionExit, onTaskRemove, } = props;
12
+ const { isProcessing, isProcessingComplete, statusCounts, tasks, confirmationModal, onActionCancel, onActionStart, onActionExit, onTaskRemove, onConfirmDelete, onCancelConfirmation, } = props;
13
13
  const message = isProcessingComplete
14
- ? getActionCompleteMessage({ counts: statusCounts })
14
+ ? getActionCompleteMessage({ counts: statusCounts, tasks })
15
15
  : undefined;
16
- const tableData = useResolveTableData(FILE_DATA_ITEM_TABLE_KEYS, DELETE_TABLE_RESOLVERS, {
17
- items,
16
+ const tableData = useResolveTableData(DELETE_TABLE_KEYS, DELETE_TABLE_RESOLVERS, {
17
+ items: tasks,
18
18
  props: { displayText, isProcessing, onTaskRemove },
19
19
  });
20
20
  return (React__default.createElement(ControlsContextProvider, { data: {
@@ -32,7 +32,8 @@ function DeleteViewProvider({ children, ...props }) {
32
32
  tableData,
33
33
  title,
34
34
  message,
35
- }, onActionStart: onActionStart, onActionExit: onActionExit, onActionCancel: onActionCancel }, children));
35
+ confirmationModal,
36
+ }, onActionStart: onActionStart, onActionExit: onActionExit, onActionCancel: onActionCancel, onConfirmationModalConfirm: onConfirmDelete, onConfirmationModalCancel: onCancelConfirmation }, children));
36
37
  }
37
38
 
38
39
  export { DeleteViewProvider };
@@ -1,6 +1,7 @@
1
1
  import React__default from 'react';
2
2
  import { isFunction } from '@aws-amplify/ui';
3
3
  import { useLocationItems } from '../../../locationItems/context.mjs';
4
+ import { getSelectionSummary } from '../../../locationItems/utils.mjs';
4
5
  import { useStore } from '../../../store/context.mjs';
5
6
  import '../../../useAction/context.mjs';
6
7
  import { useAction } from '../../../useAction/useAction.mjs';
@@ -8,28 +9,81 @@ import '@aws-amplify/ui-react-core';
8
9
  import '@aws-amplify/ui-react-core/elements';
9
10
  import '../../../credentials/context.mjs';
10
11
  import '@aws-amplify/storage/internals';
11
- import '../../../configuration/context.mjs';
12
- import 'aws-amplify';
13
- import 'jszip';
14
- import 'aws-amplify/storage';
15
- import '../../../actions/configs/context.mjs';
12
+ import { useGetActionInput } from '../../../configuration/context.mjs';
16
13
  import '../../../actions/configs/defaults.mjs';
14
+ import { useDisplayText } from '../../../displayText/context.mjs';
15
+ import { createDeleteConfirmationModalProps, countFilesInFolder } from './utils.mjs';
17
16
 
18
17
  // assign to constant to ensure referential equality
19
18
  const EMPTY_ITEMS = [];
20
19
  const useDeleteView = (options) => {
21
20
  const { onExit: _onExit } = options ?? {};
21
+ const { DeleteView: displayText } = useDisplayText();
22
22
  const [{ location }, storeDispatch] = useStore();
23
23
  const [locationItems, locationItemsDispatch] = useLocationItems();
24
24
  const { current } = location;
25
- const { fileDataItems: items = EMPTY_ITEMS } = locationItems;
26
- const [processState, handleProcess] = useAction('delete', { items });
25
+ const { dataItems = EMPTY_ITEMS } = locationItems;
26
+ const getConfig = useGetActionInput();
27
+ const [itemsWithCount, setItemsWithCount] = React__default.useState(dataItems);
28
+ const folderCountsRef = React__default.useRef(new Map());
29
+ // Sync itemsWithCount with dataItems when dataItems changes (e.g., item removal)
30
+ React__default.useEffect(() => {
31
+ const itemsWithAppliedCounts = dataItems.map((item) => ({
32
+ ...item,
33
+ totalCount: folderCountsRef.current.get(item.id),
34
+ }));
35
+ setItemsWithCount(itemsWithAppliedCounts);
36
+ }, [dataItems]);
37
+ const [processState, handleProcess] = useAction('delete', {
38
+ items: itemsWithCount,
39
+ });
40
+ const [showConfirmation, setShowConfirmation] = React__default.useState(false);
27
41
  const { isProcessing, isProcessingComplete, statusCounts, tasks } = processState;
42
+ const selectionSummary = getSelectionSummary(dataItems);
43
+ const { hasFolders } = selectionSummary;
44
+ React__default.useEffect(() => {
45
+ const initializeFolderCounts = async () => {
46
+ if (!selectionSummary.hasFolders || !current) {
47
+ return;
48
+ }
49
+ const config = getConfig(current);
50
+ const foldersToCount = dataItems.filter((item) => item.type === 'FOLDER' && !folderCountsRef.current.has(item.id));
51
+ if (foldersToCount.length === 0) {
52
+ return;
53
+ }
54
+ await Promise.all(foldersToCount.map(async (folder) => {
55
+ try {
56
+ const totalCount = await countFilesInFolder(folder.key, config);
57
+ folderCountsRef.current.set(folder.id, totalCount);
58
+ }
59
+ catch (error) {
60
+ folderCountsRef.current.set(folder.id, null);
61
+ }
62
+ }));
63
+ setItemsWithCount((currentItems) => currentItems.map((item) => ({
64
+ ...item,
65
+ totalCount: folderCountsRef.current.get(item.id),
66
+ })));
67
+ };
68
+ initializeFolderCounts();
69
+ }, [current, getConfig, selectionSummary.hasFolders, dataItems]);
28
70
  const onActionStart = () => {
29
71
  if (!current)
30
72
  return;
73
+ if (hasFolders) {
74
+ setShowConfirmation(true);
75
+ }
76
+ else {
77
+ handleProcess();
78
+ }
79
+ };
80
+ const onConfirmDelete = () => {
81
+ setShowConfirmation(false);
31
82
  handleProcess();
32
83
  };
84
+ const onCancelConfirmation = () => {
85
+ setShowConfirmation(false);
86
+ };
33
87
  const onActionCancel = () => {
34
88
  tasks.forEach((task) => {
35
89
  // @TODO Fixme, calling cancel on task doesn't currently work
@@ -48,6 +102,11 @@ const useDeleteView = (options) => {
48
102
  const onTaskRemove = React__default.useCallback(({ data }) => {
49
103
  locationItemsDispatch({ type: 'REMOVE_LOCATION_ITEM', id: data.id });
50
104
  }, [locationItemsDispatch]);
105
+ const confirmationModal = React__default.useMemo(() => createDeleteConfirmationModalProps({
106
+ items: dataItems,
107
+ showConfirmation,
108
+ displayText,
109
+ }), [dataItems, showConfirmation, displayText]);
51
110
  return {
52
111
  isProcessing,
53
112
  isProcessingComplete,
@@ -58,6 +117,9 @@ const useDeleteView = (options) => {
58
117
  onActionExit,
59
118
  onActionStart,
60
119
  onTaskRemove,
120
+ onConfirmDelete,
121
+ onCancelConfirmation,
122
+ confirmationModal,
61
123
  };
62
124
  };
63
125
 
@@ -0,0 +1,87 @@
1
+ import React__default from 'react';
2
+ import { getSelectedFolders } from '../../../locationItems/utils.mjs';
3
+ import '@aws-amplify/ui-react';
4
+ import { TextElement, UnorderedListElement, ListItemElement } from '../../../components/elements/definitions.mjs';
5
+ import '../../../components/elements/IconElement.mjs';
6
+ import { list } from '@aws-amplify/storage/internals';
7
+ import { constructBucket } from '../../../actions/handlers/utils.mjs';
8
+ import '@aws-amplify/ui';
9
+ import 'jszip';
10
+ import 'aws-amplify/storage';
11
+
12
+ const getFolderName = (folderKey) => {
13
+ return folderKey.replace(/\/$/, '').split('/').pop() ?? folderKey;
14
+ };
15
+ /**
16
+ * Creates JSX content showing a list of folders to be deleted
17
+ */
18
+ const createFolderListContent = (folders, folderListTitle) => {
19
+ return (React__default.createElement(React__default.Fragment, null,
20
+ React__default.createElement(TextElement, { className: "amplify-modal__list-title" },
21
+ React__default.createElement("strong", null, folderListTitle)),
22
+ React__default.createElement(UnorderedListElement, { className: "amplify-modal__list" }, folders.map((folder) => (React__default.createElement(ListItemElement, { key: folder.id, className: "amplify-modal__list-item" }, getFolderName(folder.key)))))));
23
+ };
24
+ const createDeleteConfirmationModalProps = ({ items, showConfirmation, displayText, }) => {
25
+ const folders = getSelectedFolders(items);
26
+ const folderCount = folders.length;
27
+ return {
28
+ isOpen: showConfirmation,
29
+ title: displayText.confirmationModalTitle,
30
+ message: displayText.confirmationModalMessage
31
+ .replace('{count}', folderCount.toString())
32
+ .replace('{plural}', folderCount !== 1 ? 's' : ''),
33
+ confirmLabel: displayText.confirmationModalConfirmLabel,
34
+ cancelLabel: displayText.confirmationModalCancelLabel,
35
+ content: createFolderListContent(folders, displayText.confirmationModalFolderListTitle),
36
+ };
37
+ };
38
+ /**
39
+ * Maximum number of files to count before showing "+" notation
40
+ * This prevents expensive operations on very large folders
41
+ */
42
+ const MAX_FILE_COUNT_LIMIT = 5000;
43
+ const LIST_PAGE_SIZE = 1000;
44
+ /**
45
+ * Count the total number of files in a folder with pagination and limits
46
+ * @param folderKey - The folder path to count files in
47
+ * @param config - Storage configuration
48
+ * @returns Promise<number | string> - File count or "5000+" if exceeds limit
49
+ */
50
+ const countFilesInFolder = async (folderKey, config) => {
51
+ try {
52
+ const { accountId, credentials, customEndpoint } = config;
53
+ const bucket = constructBucket(config);
54
+ let fileCount = 0;
55
+ let nextToken;
56
+ let hasMoreItems = true;
57
+ while (hasMoreItems && fileCount < MAX_FILE_COUNT_LIMIT) {
58
+ const { items, nextToken: listNextToken } = await list({
59
+ path: folderKey,
60
+ options: {
61
+ bucket,
62
+ locationCredentialsProvider: credentials,
63
+ expectedBucketOwner: accountId,
64
+ customEndpoint,
65
+ pageSize: LIST_PAGE_SIZE,
66
+ nextToken,
67
+ },
68
+ });
69
+ const batchFileCount = items.filter((item) => !item.path.endsWith('/')).length;
70
+ fileCount += batchFileCount;
71
+ nextToken = listNextToken;
72
+ hasMoreItems = !!nextToken;
73
+ if (fileCount >= MAX_FILE_COUNT_LIMIT) {
74
+ return `${MAX_FILE_COUNT_LIMIT}+`;
75
+ }
76
+ }
77
+ if (hasMoreItems) {
78
+ return `${fileCount}+`;
79
+ }
80
+ return fileCount;
81
+ }
82
+ catch (error) {
83
+ return 0;
84
+ }
85
+ };
86
+
87
+ export { countFilesInFolder, createDeleteConfirmationModalProps, createFolderListContent, getFolderName };
@@ -18,10 +18,6 @@ import '@aws-amplify/ui-react-core/elements';
18
18
  import '../../../credentials/context.mjs';
19
19
  import '@aws-amplify/storage/internals';
20
20
  import '../../../configuration/context.mjs';
21
- import 'aws-amplify';
22
- import 'jszip';
23
- import 'aws-amplify/storage';
24
- import '../../../actions/configs/context.mjs';
25
21
  import '../../../actions/configs/defaults.mjs';
26
22
  import '../../../displayText/context.mjs';
27
23
  import '../../../filePreview/context.mjs';
@@ -3,6 +3,15 @@ import { ControlsContextProvider } from '../../../controls/context.mjs';
3
3
  import { useDisplayText } from '../../../displayText/context.mjs';
4
4
  import useResolveTableData from '../../hooks/useResolveTableData/useResolveTableData.mjs';
5
5
  import '@aws-amplify/ui';
6
+ import '@aws-amplify/storage/internals';
7
+ import 'aws-amplify';
8
+ import 'jszip';
9
+ import 'aws-amplify/storage';
10
+ import '../../../actions/configs/context.mjs';
11
+ import '../../../actions/configs/defaults.mjs';
12
+ import '@aws-amplify/ui-react';
13
+ import '../../../components/elements/definitions.mjs';
14
+ import '../../../components/elements/IconElement.mjs';
6
15
  import { DOWNLOAD_TABLE_RESOLVERS, DOWNLOAD_TABLE_KEYS } from '../../utils/tableResolvers/downloadResolvers.mjs';
7
16
 
8
17
  function DownloadViewProvider({ children, ...props }) {
@@ -9,10 +9,6 @@ import '@aws-amplify/ui-react-core/elements';
9
9
  import '../../../credentials/context.mjs';
10
10
  import '@aws-amplify/storage/internals';
11
11
  import '../../../configuration/context.mjs';
12
- import 'aws-amplify';
13
- import 'jszip';
14
- import 'aws-amplify/storage';
15
- import '../../../actions/configs/context.mjs';
16
12
  import '../../../actions/configs/defaults.mjs';
17
13
 
18
14
  // assign to constant to ensure referential equality
@@ -11,17 +11,13 @@ import '@aws-amplify/ui-react-core/elements';
11
11
  import '../../../credentials/context.mjs';
12
12
  import '@aws-amplify/storage/internals';
13
13
  import '../../../configuration/context.mjs';
14
- import 'aws-amplify';
15
- import 'jszip';
16
- import 'aws-amplify/storage';
17
- import '../../../actions/configs/context.mjs';
18
14
  import '../../../actions/configs/defaults.mjs';
19
15
  import '../../../displayText/context.mjs';
20
16
  import '../../../filePreview/context.mjs';
21
17
  import { ActionCancelControl } from '../../../controls/ActionCancelControl.mjs';
18
+ import '../../../controls/context.mjs';
22
19
  import { ActionDestinationControl } from '../../../controls/ActionDestinationControl.mjs';
23
20
  import { ActionExitControl } from '../../../controls/ActionExitControl.mjs';
24
- import '../../../controls/context.mjs';
25
21
  import { ActionStartControl } from '../../../controls/ActionStartControl.mjs';
26
22
  import { AddFilesControl } from '../../../controls/AddFilesControl.mjs';
27
23
  import { AddFolderControl } from '../../../controls/AddFolderControl.mjs';
@@ -9,6 +9,9 @@ import '../../../actions/configs/defaults.mjs';
9
9
  import { ControlsContextProvider } from '../../../controls/context.mjs';
10
10
  import { useDisplayText } from '../../../displayText/context.mjs';
11
11
  import useResolveTableData from '../../hooks/useResolveTableData/useResolveTableData.mjs';
12
+ import '@aws-amplify/ui-react';
13
+ import '../../../components/elements/definitions.mjs';
14
+ import '../../../components/elements/IconElement.mjs';
12
15
  import { UPLOAD_TABLE_RESOLVERS, UPLOAD_TABLE_KEYS } from '../../utils/tableResolvers/uploadResolvers.mjs';
13
16
 
14
17
  function UploadViewProvider({ children, ...props }) {
@@ -9,10 +9,6 @@ import '@aws-amplify/ui-react-core/elements';
9
9
  import '../../../credentials/context.mjs';
10
10
  import '@aws-amplify/storage/internals';
11
11
  import '../../../configuration/context.mjs';
12
- import 'aws-amplify';
13
- import 'jszip';
14
- import 'aws-amplify/storage';
15
- import '../../../actions/configs/context.mjs';
16
12
  import '../../../actions/configs/defaults.mjs';
17
13
  import { DEFAULT_OVERWRITE_ENABLED } from './constants.mjs';
18
14
  import { usePaginate } from '../../hooks/usePaginate.mjs';
@@ -11,10 +11,6 @@ import '@aws-amplify/ui-react-core/elements';
11
11
  import '../../credentials/context.mjs';
12
12
  import '@aws-amplify/storage/internals';
13
13
  import '../../configuration/context.mjs';
14
- import 'aws-amplify';
15
- import 'jszip';
16
- import 'aws-amplify/storage';
17
- import '../../actions/configs/context.mjs';
18
14
  import '../../actions/configs/defaults.mjs';
19
15
  import '../../displayText/context.mjs';
20
16
  import '../../filePreview/context.mjs';
@@ -6,7 +6,7 @@ import { getLocationDetailViewTableData } from './getLocationDetailViewTableData
6
6
  function LocationDetailViewProvider({ children, ...props }) {
7
7
  const { LocationDetailView: displayText } = useDisplayText();
8
8
  const { LocationDetailView: { loadingIndicatorLabel, searchSubfoldersToggleLabel, selectFileLabel, selectAllFilesLabel, searchPlaceholder, searchSubmitLabel, searchClearLabel, getActionListItemLabel, getDateDisplayValue, getTitle, getListItemsResultMessage, }, } = useDisplayText();
9
- 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;
9
+ 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;
10
10
  const actionsWithDisplayText = actionItems.map((item) => ({
11
11
  ...item,
12
12
  label: getActionListItemLabel(item.label),
@@ -63,6 +63,7 @@ function LocationDetailViewProvider({ children, ...props }) {
63
63
  onNavigate,
64
64
  onSelect,
65
65
  onSelectAll: onToggleSelectAll,
66
+ dataItems,
66
67
  }),
67
68
  title: getTitle(location),
68
69
  message: messageControlContent,
@@ -1,32 +1,43 @@
1
1
  import { LOCATION_DETAIL_VIEW_HEADERS } from './constants.mjs';
2
2
 
3
- const getFolderRowContent = ({ itemSubPath, rowId, onNavigate, }) => LOCATION_DETAIL_VIEW_HEADERS.map((columnKey) => {
4
- const key = `${columnKey}-${rowId}`;
5
- switch (columnKey) {
6
- case 'checkbox': {
7
- return { key, type: 'text', content: { text: '' } };
3
+ const getFolderRowContent = ({ itemSubPath, rowId, isSelected, selectFolderLabel, onNavigate, onSelect, }) => {
4
+ return LOCATION_DETAIL_VIEW_HEADERS.map((columnKey) => {
5
+ const key = `${columnKey}-${rowId}`;
6
+ switch (columnKey) {
7
+ case 'checkbox': {
8
+ return {
9
+ key,
10
+ type: 'checkbox',
11
+ content: {
12
+ checked: isSelected,
13
+ id: key,
14
+ label: `${selectFolderLabel} ${itemSubPath}`,
15
+ onSelect,
16
+ },
17
+ };
18
+ }
19
+ case 'name': {
20
+ return {
21
+ key,
22
+ type: 'button',
23
+ content: {
24
+ icon: 'folder',
25
+ ariaLabel: itemSubPath,
26
+ label: itemSubPath,
27
+ onClick: onNavigate,
28
+ },
29
+ };
30
+ }
31
+ case 'type': {
32
+ return { key, type: 'text', content: { text: 'Folder' } };
33
+ }
34
+ case 'last-modified':
35
+ case 'size':
36
+ case 'download': {
37
+ return { key, type: 'text', content: { text: '' } };
38
+ }
8
39
  }
9
- case 'name': {
10
- return {
11
- key,
12
- type: 'button',
13
- content: {
14
- icon: 'folder',
15
- ariaLabel: itemSubPath,
16
- label: itemSubPath,
17
- onClick: onNavigate,
18
- },
19
- };
20
- }
21
- case 'type': {
22
- return { key, type: 'text', content: { text: 'Folder' } };
23
- }
24
- case 'last-modified':
25
- case 'size':
26
- case 'download': {
27
- return { key, type: 'text', content: { text: '' } };
28
- }
29
- }
30
- });
40
+ });
41
+ };
31
42
 
32
43
  export { getFolderRowContent };
@@ -9,7 +9,7 @@ import { getFileRowContent } from './getFileRowContent.mjs';
9
9
  import { getFolderRowContent } from './getFolderRowContent.mjs';
10
10
  import { getHeaders } from './getHeaders.mjs';
11
11
 
12
- const getLocationDetailViewTableData = ({ filePreviewEnabled, activeFile, onSelectActiveFile, areAllFilesSelected, displayText, location, fileDataItems, hasFiles, pageItems, selectFileLabel, selectAllFilesLabel, getDateDisplayValue, onDownload, onNavigate, onSelect, onSelectAll, }) => {
12
+ const getLocationDetailViewTableData = ({ filePreviewEnabled, activeFile, onSelectActiveFile, areAllFilesSelected, displayText, location, fileDataItems, dataItems, hasFiles, pageItems, selectFileLabel, selectAllFilesLabel, getDateDisplayValue, onDownload, onNavigate, onSelect, onSelectAll, }) => {
13
13
  const { tableColumnLastModifiedHeader, tableColumnNameHeader, tableColumnSizeHeader, tableColumnTypeHeader, } = displayText;
14
14
  const headers = getHeaders({
15
15
  areAllFilesSelected,
@@ -67,6 +67,10 @@ const getLocationDetailViewTableData = ({ filePreviewEnabled, activeFile, onSele
67
67
  }
68
68
  onNavigate({ ...current, id }, itemLocationPath);
69
69
  };
70
+ const isSelected = dataItems?.some((item) => item.id === id) ?? false;
71
+ const onFolderSelect = () => {
72
+ onSelect(isSelected, locationItem);
73
+ };
70
74
  return {
71
75
  key: id,
72
76
  active: false,
@@ -74,6 +78,9 @@ const getLocationDetailViewTableData = ({ filePreviewEnabled, activeFile, onSele
74
78
  itemSubPath,
75
79
  rowId: id,
76
80
  onNavigate: onFolderNavigate,
81
+ selectFolderLabel: selectFileLabel,
82
+ isSelected,
83
+ onSelect: onFolderSelect,
77
84
  }),
78
85
  };
79
86
  }
@@ -39,7 +39,7 @@ const useLocationDetailView = (options) => {
39
39
  const [activeFile, setActiveFile] = React__default.useState();
40
40
  const { current, key } = location;
41
41
  const { permissions, prefix } = current ?? {};
42
- const { fileDataItems } = locationItems;
42
+ const { dataItems, fileDataItems } = locationItems;
43
43
  const hasInvalidPrefix = isUndefined(prefix);
44
44
  const [{ task }, handleDownload] = useAction('download');
45
45
  const [{ value, isLoading, hasError, message }, handleList] = useList('locationItems');
@@ -166,13 +166,13 @@ const useLocationDetailView = (options) => {
166
166
  actionType: type,
167
167
  icon,
168
168
  isDisabled: isFunction(disable)
169
- ? disable(fileDataItems)
169
+ ? disable(dataItems)
170
170
  : disable ?? false,
171
171
  isHidden: isFunction(hide) ? hide(permissions) : hide,
172
172
  label,
173
173
  };
174
174
  });
175
- }, [actionConfigs, fileDataItems, permissions]);
175
+ }, [actionConfigs, dataItems, permissions]);
176
176
  return {
177
177
  actionItems,
178
178
  actionType,
@@ -183,6 +183,7 @@ const useLocationDetailView = (options) => {
183
183
  page: currentPage,
184
184
  pageItems,
185
185
  location,
186
+ dataItems,
186
187
  fileDataItems,
187
188
  hasError,
188
189
  hasDownloadError: task?.status === 'FAILED',
@@ -238,16 +239,16 @@ const useLocationDetailView = (options) => {
238
239
  storeDispatch({ type: 'RESET_ACTION_TYPE' });
239
240
  locationItemsDispatch({ type: 'RESET_LOCATION_ITEMS' });
240
241
  },
241
- onSelect: (isSelected, fileItem) => {
242
+ onSelect: (isSelected, item) => {
242
243
  locationItemsDispatch(isSelected
243
- ? { type: 'REMOVE_LOCATION_ITEM', id: fileItem.id }
244
- : { type: 'SET_LOCATION_ITEMS', items: [fileItem] });
244
+ ? { type: 'REMOVE_LOCATION_ITEM', id: item.id }
245
+ : { type: 'SET_LOCATION_ITEMS', items: [item] });
245
246
  },
246
247
  onToggleSelectAll: () => {
247
- const fileItems = pageItems.filter((item) => item.type === 'FILE');
248
- locationItemsDispatch(fileItems.length === fileDataItems?.length
248
+ const selectableItems = pageItems;
249
+ locationItemsDispatch(selectableItems.length === dataItems?.length
249
250
  ? { type: 'RESET_LOCATION_ITEMS' }
250
- : { type: 'SET_LOCATION_ITEMS', items: fileItems });
251
+ : { type: 'SET_LOCATION_ITEMS', items: selectableItems });
251
252
  },
252
253
  onSearch: () => {
253
254
  setActiveFile(undefined);
@@ -11,10 +11,6 @@ import '@aws-amplify/ui-react-core/elements';
11
11
  import '../../credentials/context.mjs';
12
12
  import '@aws-amplify/storage/internals';
13
13
  import '../../configuration/context.mjs';
14
- import 'aws-amplify';
15
- import 'jszip';
16
- import 'aws-amplify/storage';
17
- import '../../actions/configs/context.mjs';
18
14
  import '../../actions/configs/defaults.mjs';
19
15
  import '../../displayText/context.mjs';
20
16
  import '../../filePreview/context.mjs';
@@ -11,10 +11,6 @@ import '@aws-amplify/ui-react-core/elements';
11
11
  import '../../credentials/context.mjs';
12
12
  import '@aws-amplify/storage/internals';
13
13
  import '../../configuration/context.mjs';
14
- import 'aws-amplify';
15
- import 'jszip';
16
- import 'aws-amplify/storage';
17
- import '../../actions/configs/context.mjs';
18
14
  import '../../actions/configs/defaults.mjs';
19
15
  import { useDisplayText } from '../../displayText/context.mjs';
20
16
  import '../../filePreview/context.mjs';