@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
@@ -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';
@@ -1,3 +1,4 @@
1
+ import { hasSelectedFolders } from '../../locationItems/utils.mjs';
1
2
  import '@aws-amplify/storage/internals';
2
3
  import 'aws-amplify';
3
4
  import '@aws-amplify/ui';
@@ -8,7 +9,10 @@ import 'aws-amplify/storage';
8
9
  const copyActionConfig = {
9
10
  viewName: 'CopyView',
10
11
  actionListItem: {
11
- disable: (selected) => !selected || selected.length === 0,
12
+ disable: (selected) => {
13
+ const hasNoSelection = !selected || selected.length === 0;
14
+ return hasNoSelection || hasSelectedFolders(selected);
15
+ },
12
16
  hide: (permissions) => !permissions.includes('write'),
13
17
  icon: 'copy-file',
14
18
  label: 'Copy',
@@ -18,7 +22,10 @@ const copyActionConfig = {
18
22
  const deleteActionConfig = {
19
23
  viewName: 'DeleteView',
20
24
  actionListItem: {
21
- disable: (selected) => !selected || selected.length === 0,
25
+ disable: (selected) => {
26
+ const hasNoSelection = !selected || selected.length === 0;
27
+ return hasNoSelection;
28
+ },
22
29
  hide: (permissions) => !permissions.includes('delete'),
23
30
  icon: 'delete-file',
24
31
  label: 'Delete',
@@ -46,7 +53,10 @@ const uploadActionConfig = {
46
53
  const downloadActionConfig = {
47
54
  viewName: 'DownloadView',
48
55
  actionListItem: {
49
- disable: (selected) => !selected || selected.length === 0,
56
+ disable: (selected) => {
57
+ const hasNoSelection = !selected || selected.length === 0;
58
+ return hasNoSelection || hasSelectedFolders(selected);
59
+ },
50
60
  hide: (permissions) => !permissions.includes('get'),
51
61
  icon: 'download',
52
62
  label: 'Download',
@@ -1,27 +1,58 @@
1
1
  import { remove } from '@aws-amplify/storage/internals';
2
2
  import { constructBucket } from './utils.mjs';
3
3
 
4
- const deleteHandler = ({ config, data, }) => {
4
+ const deleteHandler = ({ config, data, options, }) => {
5
5
  const { key } = data;
6
6
  const { accountId, credentials, customEndpoint } = config;
7
- const result = remove({
7
+ const { onProgress } = options ?? {};
8
+ let cumulativeSuccessCount = 0;
9
+ let cumulativeFailureCount = 0;
10
+ let operationCancel = () => {
11
+ // noop
12
+ };
13
+ const cancel = () => {
14
+ operationCancel?.();
15
+ };
16
+ const operation = remove({
8
17
  path: key,
9
18
  options: {
10
19
  bucket: constructBucket(config),
11
20
  locationCredentialsProvider: credentials,
12
21
  expectedBucketOwner: accountId,
13
22
  customEndpoint,
23
+ onProgress: (progress) => {
24
+ if (!progress) {
25
+ return;
26
+ }
27
+ const batchSuccessCount = progress?.deleted?.length ?? 0;
28
+ const batchFailureCount = progress?.failed?.length ?? 0;
29
+ cumulativeSuccessCount += batchSuccessCount;
30
+ cumulativeFailureCount += batchFailureCount;
31
+ onProgress?.(data, {
32
+ successCount: cumulativeSuccessCount,
33
+ failureCount: cumulativeFailureCount,
34
+ });
35
+ },
14
36
  },
37
+ });
38
+ operationCancel = operation?.cancel ? operation?.cancel : () => { };
39
+ const operationPromise = operation?.result ?? operation;
40
+ const result = operationPromise
41
+ ?.then?.(({ path }) => {
42
+ return {
43
+ status: 'COMPLETE',
44
+ value: {
45
+ key: path,
46
+ successCount: cumulativeSuccessCount,
47
+ failureCount: cumulativeFailureCount,
48
+ },
49
+ };
15
50
  })
16
- .then(({ path }) => ({
17
- status: 'COMPLETE',
18
- value: { key: path },
19
- }))
20
- .catch((error) => {
51
+ ?.catch?.((error) => {
21
52
  const { message } = error;
22
53
  return { error, message, status: 'FAILED' };
23
54
  });
24
- return { result };
55
+ return { result, cancel };
25
56
  };
26
57
 
27
58
  export { deleteHandler };
@@ -11,10 +11,6 @@ import '../useAction/context.mjs';
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';
@@ -12,10 +12,6 @@ import '../../../credentials/context.mjs';
12
12
  import '@aws-amplify/storage/internals';
13
13
  import '../../../configuration/context.mjs';
14
14
  import '@aws-amplify/ui';
15
- import 'aws-amplify';
16
- import 'jszip';
17
- import 'aws-amplify/storage';
18
- import '../../../actions/configs/context.mjs';
19
15
  import '../../../actions/configs/defaults.mjs';
20
16
  import { useDisplayText } from '../../../displayText/context.mjs';
21
17
 
@@ -0,0 +1,34 @@
1
+ import React__default from 'react';
2
+ import '@aws-amplify/ui-react';
3
+ import { ViewElement, HeadingElement, TextElement, ButtonElement } from '../elements/definitions.mjs';
4
+ import '../elements/IconElement.mjs';
5
+
6
+ const ActionConfirmationModal = ({ isOpen = false, title, message, content, onConfirm, onCancel, confirmLabel, cancelLabel, }) => {
7
+ const modalRef = React__default.useRef(null);
8
+ React__default.useEffect(() => {
9
+ if (isOpen && modalRef.current) {
10
+ modalRef.current.focus();
11
+ }
12
+ }, [isOpen]);
13
+ const handleKeyDown = (event) => {
14
+ if (event.key === 'Escape') {
15
+ onCancel?.();
16
+ }
17
+ };
18
+ if (!isOpen)
19
+ return null;
20
+ return (React__default.createElement("div", { ref: modalRef, className: "amplify-modal__overlay", role: "dialog", "aria-modal": "true", tabIndex: -1, onKeyDown: handleKeyDown, onClick: (e) => {
21
+ if (e.target === e.currentTarget) {
22
+ onCancel?.();
23
+ }
24
+ } },
25
+ React__default.createElement(ViewElement, { className: "amplify-modal__content", role: "document" },
26
+ React__default.createElement(HeadingElement, { className: "amplify-modal__title" }, title),
27
+ message && (React__default.createElement(TextElement, { className: "amplify-modal__body" }, message)),
28
+ content && (React__default.createElement(ViewElement, { className: "amplify-modal__body" }, content)),
29
+ React__default.createElement(ViewElement, { className: "amplify-modal__footer" },
30
+ React__default.createElement(ButtonElement, { className: "amplify-modal__cancel", onClick: onCancel, variant: "cancel" }, cancelLabel),
31
+ React__default.createElement(ButtonElement, { className: "amplify-modal__confirm", onClick: onConfirm, variant: "primary" }, confirmLabel)))));
32
+ };
33
+
34
+ export { ActionConfirmationModal };
@@ -1,4 +1,5 @@
1
1
  import { ActionCancel } from './ActionCancel.mjs';
2
+ import { ActionConfirmationModal } from './ActionConfirmationModal.mjs';
2
3
  import { ActionDestination } from './ActionDestination.mjs';
3
4
  import { ActionExit } from './ActionExit.mjs';
4
5
  import { ActionStart } from './ActionStart.mjs';
@@ -22,6 +23,7 @@ import { Title } from './Title.mjs';
22
23
 
23
24
  const DEFAULT_COMPOSABLES = {
24
25
  ActionCancel,
26
+ ActionConfirmationModal,
25
27
  ActionDestination,
26
28
  ActionExit,
27
29
  ActionStart,
@@ -29,7 +29,7 @@ const OrderedListElement = defineBaseElement({
29
29
  type: 'ol',
30
30
  displayName: 'OrderedList',
31
31
  });
32
- defineBaseElement({
32
+ const UnorderedListElement = defineBaseElement({
33
33
  type: 'ul',
34
34
  displayName: 'UnorderedList',
35
35
  });
@@ -83,4 +83,4 @@ const SpanElement = defineBaseElement({
83
83
  displayName: 'Span',
84
84
  });
85
85
 
86
- export { ButtonElement, DescriptionDetailsElement, DescriptionListElement, DescriptionTermElement, HeadingElement, InputElement, LabelElement, ListItemElement, NavElement, OrderedListElement, SpanElement, TableBodyElement, TableDataCellElement, TableElement, TableHeadElement, TableHeaderElement, TableRowElement, TextElement, ViewElement };
86
+ export { ButtonElement, DescriptionDetailsElement, DescriptionListElement, DescriptionTermElement, HeadingElement, InputElement, LabelElement, ListItemElement, NavElement, OrderedListElement, SpanElement, TableBodyElement, TableDataCellElement, TableElement, TableHeadElement, TableHeaderElement, TableRowElement, TextElement, UnorderedListElement, ViewElement };
@@ -0,0 +1,12 @@
1
+ import React__default from 'react';
2
+ import { ActionConfirmationModal } from '../components/composables/ActionConfirmationModal.mjs';
3
+ import { useResolvedComposable } from './hooks/useResolvedComposable.mjs';
4
+ import { useActionConfirmationModal } from './hooks/useActionConfirmationModal.mjs';
5
+
6
+ const ActionConfirmationModalControl = () => {
7
+ const props = useActionConfirmationModal();
8
+ const Resolved = useResolvedComposable(ActionConfirmationModal, 'ActionConfirmationModal');
9
+ return React__default.createElement(Resolved, { ...props });
10
+ };
11
+
12
+ export { ActionConfirmationModalControl };
@@ -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';
@@ -0,0 +1,17 @@
1
+ import { useControlsContext } from '../context.mjs';
2
+
3
+ const useActionConfirmationModal = () => {
4
+ const { data: { confirmationModal = {} }, onConfirmationModalConfirm = () => { }, onConfirmationModalCancel = () => { }, } = useControlsContext();
5
+ return {
6
+ isOpen: false,
7
+ title: 'Confirm Action',
8
+ message: '',
9
+ confirmLabel: 'Confirm',
10
+ cancelLabel: 'Cancel',
11
+ ...confirmationModal,
12
+ onConfirm: onConfirmationModalConfirm,
13
+ onCancel: onConfirmationModalCancel,
14
+ };
15
+ };
16
+
17
+ export { useActionConfirmationModal };
@@ -10,18 +10,22 @@ import '../credentials/context.mjs';
10
10
  import '@aws-amplify/storage/internals';
11
11
  import '../configuration/context.mjs';
12
12
  import '@aws-amplify/ui-react-core';
13
- import 'aws-amplify';
14
- import 'jszip';
15
- import 'aws-amplify/storage';
16
- import '../actions/configs/context.mjs';
17
13
  import '../actions/configs/defaults.mjs';
18
14
  import '../views/LocationActionView/CreateFolderView/CreateFolderView.mjs';
19
15
  import '../views/LocationActionView/DeleteView/DeleteView.mjs';
16
+ import '../displayText/context.mjs';
17
+ import '@aws-amplify/ui-react';
18
+ import '../components/elements/definitions.mjs';
19
+ import '../components/elements/IconElement.mjs';
20
+ import 'aws-amplify';
21
+ import 'jszip';
22
+ import 'aws-amplify/storage';
20
23
  import '../views/LocationActionView/DownloadView/DownloadView.mjs';
21
24
  import '../views/context/actionViews.mjs';
22
25
  import '../views/LocationActionView/UploadView/UploadView.mjs';
23
26
  import '../fileItems/context.mjs';
24
27
  import '../views/LocationDetailView/LocationDetailView.mjs';
28
+ import '../actions/configs/context.mjs';
25
29
  import '../filePreview/context.mjs';
26
30
  import '../views/LocationsView/LocationsView.mjs';
27
31
 
@@ -9,12 +9,8 @@ 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 { assertRegisterAuthListener } from '../validators/assertRegisterAuthListener.mjs';
16
- import '../actions/configs/context.mjs';
17
12
  import '../actions/configs/defaults.mjs';
13
+ import { assertRegisterAuthListener } from '../validators/assertRegisterAuthListener.mjs';
18
14
  import '../views/context/actionViews.mjs';
19
15
  import '../views/context/primaryViews.mjs';
20
16
  import { CopyView } from '../views/LocationActionView/CopyView/CopyView.mjs';
@@ -22,11 +18,19 @@ import '../locationItems/context.mjs';
22
18
  import '../store/context.mjs';
23
19
  import { CreateFolderView } from '../views/LocationActionView/CreateFolderView/CreateFolderView.mjs';
24
20
  import { DeleteView } from '../views/LocationActionView/DeleteView/DeleteView.mjs';
21
+ import '../displayText/context.mjs';
22
+ import '@aws-amplify/ui-react';
23
+ import '../components/elements/definitions.mjs';
24
+ import '../components/elements/IconElement.mjs';
25
+ import 'aws-amplify';
26
+ import 'jszip';
27
+ import 'aws-amplify/storage';
25
28
  import { DownloadView } from '../views/LocationActionView/DownloadView/DownloadView.mjs';
26
29
  import { LocationActionView } from '../views/LocationActionView/LocationActionView.mjs';
27
30
  import { UploadView } from '../views/LocationActionView/UploadView/UploadView.mjs';
28
31
  import '../fileItems/context.mjs';
29
32
  import { LocationDetailView } from '../views/LocationDetailView/LocationDetailView.mjs';
33
+ import '../actions/configs/context.mjs';
30
34
  import '../filePreview/context.mjs';
31
35
  import { LocationsView } from '../views/LocationsView/LocationsView.mjs';
32
36
  import { useView } from '../views/useView.mjs';
@@ -1,20 +1,132 @@
1
1
  import { DEFAULT_ACTION_VIEW_DISPLAY_TEXT } from './shared.mjs';
2
2
 
3
+ const pluralize = (count, word) => count === 1 ? word : `${word}s`;
4
+ const formatCount = (count, word) => `${count === 1 ? '' : 'All '}${count} ${pluralize(count, word)}`;
3
5
  const DEFAULT_DELETE_VIEW_DISPLAY_TEXT = {
4
6
  ...DEFAULT_ACTION_VIEW_DISPLAY_TEXT,
5
7
  title: 'Delete',
6
8
  actionStartLabel: 'Delete',
9
+ confirmationModalTitle: 'Confirm Deletion',
10
+ confirmationModalConfirmLabel: 'Delete',
11
+ confirmationModalCancelLabel: 'Cancel',
12
+ confirmationModalMessage: 'The items that will be deleted contain {count} folder{plural}',
13
+ confirmationModalFolderListTitle: 'Folder list:',
7
14
  getActionCompleteMessage: (data) => {
8
- const { counts } = data ?? {};
9
- const { COMPLETE, FAILED, TOTAL } = counts ?? {};
15
+ const { counts, tasks } = data ?? {};
16
+ const { COMPLETE = 0, FAILED = 0, TOTAL = 0 } = counts ?? {};
17
+ if (!TOTAL || TOTAL === 0) {
18
+ return { content: 'No items to delete.', type: 'info' };
19
+ }
20
+ if (tasks && tasks.length > 0) {
21
+ const folderTasks = tasks.filter((task) => task.data.type === 'FOLDER');
22
+ const fileTasks = tasks.filter((task) => task.data.type === 'FILE');
23
+ const completeFolders = folderTasks.filter((task) => task.status === 'COMPLETE').length;
24
+ const failedFolders = folderTasks.filter((task) => task.status === 'FAILED').length;
25
+ const completeFiles = fileTasks.filter((task) => task.status === 'COMPLETE').length;
26
+ const failedFiles = fileTasks.filter((task) => task.status === 'FAILED').length;
27
+ const hasFolders = folderTasks.length > 0;
28
+ const hasFiles = fileTasks.length > 0;
29
+ const isMixed = hasFolders && hasFiles;
30
+ // All successful
31
+ if (COMPLETE === TOTAL) {
32
+ if (isMixed) {
33
+ return {
34
+ content: `${formatCount(completeFolders, 'folder')} and ${completeFiles} ${pluralize(completeFiles, 'file')} deleted successfully.`,
35
+ type: 'success',
36
+ };
37
+ }
38
+ else if (hasFolders) {
39
+ return {
40
+ content: `${formatCount(completeFolders, 'folder')} deleted successfully.`,
41
+ type: 'success',
42
+ };
43
+ }
44
+ else {
45
+ return {
46
+ content: `${formatCount(completeFiles, 'file')} deleted successfully.`,
47
+ type: 'success',
48
+ };
49
+ }
50
+ }
51
+ // Complete failure
52
+ if (FAILED === TOTAL) {
53
+ if (isMixed) {
54
+ return {
55
+ content: `Failed to delete ${failedFolders} ${pluralize(failedFolders, 'folder')} and ${failedFiles} ${pluralize(failedFiles, 'file')}. Some contents may have been deleted.`,
56
+ type: 'error',
57
+ };
58
+ }
59
+ else if (hasFolders) {
60
+ return {
61
+ content: `Failed to delete ${failedFolders} ${pluralize(failedFolders, 'folder')}. Some items may have been deleted.`,
62
+ type: 'error',
63
+ };
64
+ }
65
+ else {
66
+ return {
67
+ content: `Failed to delete ${failedFiles} ${pluralize(failedFiles, 'file')}.`,
68
+ type: 'error',
69
+ };
70
+ }
71
+ }
72
+ // Partial failure
73
+ if (isMixed) {
74
+ const messages = [];
75
+ if (completeFiles > 0) {
76
+ messages.push(`${completeFiles} ${pluralize(completeFiles, 'file')} deleted`);
77
+ }
78
+ if (completeFolders > 0) {
79
+ messages.push(`${completeFolders} ${pluralize(completeFolders, 'folder')} deleted`);
80
+ }
81
+ if (failedFiles > 0) {
82
+ messages.push(`${failedFiles} ${pluralize(failedFiles, 'file')} failed`);
83
+ }
84
+ if (failedFolders > 0) {
85
+ messages.push(`${failedFolders} ${pluralize(failedFolders, 'folder')} failed`);
86
+ }
87
+ return {
88
+ content: messages.join(', ') + '. Some items may have been deleted.',
89
+ type: 'error',
90
+ };
91
+ }
92
+ else if (hasFolders) {
93
+ // Folders only partial failure
94
+ if (completeFolders > 0) {
95
+ return {
96
+ content: `${completeFolders} ${pluralize(completeFolders, 'folder')} deleted, ${failedFolders} ${pluralize(failedFolders, 'folder')} failed. Some items may have been deleted.`,
97
+ type: 'error',
98
+ };
99
+ }
100
+ else {
101
+ return {
102
+ content: `Failed to delete ${failedFolders} ${pluralize(failedFolders, 'folder')}. Some items may have been deleted.`,
103
+ type: 'error',
104
+ };
105
+ }
106
+ }
107
+ else {
108
+ // Files only partial failure
109
+ return {
110
+ content: `${completeFiles} ${pluralize(completeFiles, 'file')} deleted, ${failedFiles} ${pluralize(failedFiles, 'file')} failed.`,
111
+ type: 'error',
112
+ };
113
+ }
114
+ }
115
+ // Fallback to generic messaging if tasks not available
10
116
  if (COMPLETE === TOTAL) {
11
- return { content: 'All files deleted.', type: 'success' };
117
+ return {
118
+ content: `${formatCount(TOTAL, 'item')} deleted successfully.`,
119
+ type: 'success',
120
+ };
12
121
  }
13
122
  if (FAILED === TOTAL) {
14
- return { content: 'All files failed to delete.', type: 'error' };
123
+ return {
124
+ content: `Failed to delete ${formatCount(TOTAL, 'item')}.`,
125
+ type: 'error',
126
+ };
15
127
  }
16
128
  return {
17
- content: `${COMPLETE} files deleted, ${FAILED} files failed to delete.`,
129
+ content: `${COMPLETE} ${pluralize(COMPLETE, 'item')} deleted, ${FAILED} ${pluralize(FAILED, 'item')} failed to delete.`,
18
130
  type: 'error',
19
131
  };
20
132
  },
@@ -69,6 +69,7 @@ const DEFAULT_LOCATION_DETAIL_VIEW_DISPLAY_TEXT = {
69
69
  tableColumnSizeHeader: 'Size',
70
70
  tableColumnTypeHeader: 'Type',
71
71
  selectFileLabel: 'Select file',
72
+ selectFolderLabel: 'Select folder',
72
73
  selectAllFilesLabel: 'Select all files',
73
74
  getActionListItemLabel: (key = '') => {
74
75
  switch (key) {
@@ -17,6 +17,7 @@ const DEFAULT_ACTION_VIEW_DISPLAY_TEXT = {
17
17
  tableColumnNameHeader: 'Name',
18
18
  tableColumnTypeHeader: 'Type',
19
19
  tableColumnSizeHeader: 'Size',
20
+ tableColumnProgressHeader: 'Progress',
20
21
  };
21
22
  const DEFAULT_LIST_VIEW_DISPLAY_TEXT = {
22
23
  loadingIndicatorLabel: 'Loading',
@@ -9,6 +9,7 @@ import '../actions/configs/context.mjs';
9
9
  import '../actions/configs/defaults.mjs';
10
10
 
11
11
  const DEFAULT_STATE = {
12
+ dataItems: undefined,
12
13
  fileDataItems: undefined,
13
14
  };
14
15
  const locationItemsReducer = (prevState, event) => {
@@ -17,27 +18,29 @@ const locationItemsReducer = (prevState, event) => {
17
18
  const { items } = event;
18
19
  if (!items?.length)
19
20
  return prevState;
20
- if (!prevState.fileDataItems?.length) {
21
- return { fileDataItems: items.map(createFileDataItem) };
22
- }
23
- const nextFileDataItems = items?.reduce((fileDataItems, data) => prevState.fileDataItems?.some(({ id }) => id === data.id)
24
- ? fileDataItems
25
- : fileDataItems.concat(createFileDataItem(data)), []);
26
- if (!nextFileDataItems?.length)
27
- return prevState;
21
+ const nextDataItems = !prevState.dataItems?.length
22
+ ? items
23
+ : prevState.dataItems.concat(items.filter((data) => !prevState.dataItems?.some(({ id }) => id === data.id)));
24
+ const fileItems = items.filter((item) => item.type === 'FILE');
25
+ const nextFileDataItems = !prevState.fileDataItems?.length
26
+ ? fileItems.map(createFileDataItem)
27
+ : prevState.fileDataItems.concat(fileItems
28
+ .filter((data) => !prevState.fileDataItems?.some(({ id }) => id === data.id))
29
+ .map(createFileDataItem));
28
30
  return {
29
- fileDataItems: prevState.fileDataItems.concat(nextFileDataItems),
31
+ dataItems: nextDataItems,
32
+ fileDataItems: nextFileDataItems,
30
33
  };
31
34
  }
32
35
  case 'REMOVE_LOCATION_ITEM': {
33
36
  const { id } = event;
34
- if (!prevState.fileDataItems)
35
- return prevState;
36
- const fileDataItems = prevState.fileDataItems.filter((item) => item.id !== id);
37
- if (fileDataItems.length === prevState.fileDataItems.length) {
37
+ const dataItems = prevState.dataItems?.filter((item) => item.id !== id);
38
+ const fileDataItems = prevState.fileDataItems?.filter((item) => item.id !== id);
39
+ if (dataItems?.length === prevState.dataItems?.length &&
40
+ fileDataItems?.length === prevState.fileDataItems?.length) {
38
41
  return prevState;
39
42
  }
40
- return { fileDataItems };
43
+ return { dataItems, fileDataItems };
41
44
  }
42
45
  case 'RESET_LOCATION_ITEMS': {
43
46
  return DEFAULT_STATE;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Selection utility functions for LocationItems
3
+ */
4
+ /**
5
+ * Get selected files from dataItems
6
+ */
7
+ const getSelectedFiles = (dataItems) => {
8
+ return dataItems?.filter((item) => item.type === 'FILE') ?? [];
9
+ };
10
+ /**
11
+ * Get selected folders from dataItems
12
+ */
13
+ const getSelectedFolders = (dataItems) => {
14
+ return dataItems?.filter((item) => item.type === 'FOLDER') ?? [];
15
+ };
16
+ /**
17
+ * Check if selection contains folders
18
+ */
19
+ const hasSelectedFolders = (dataItems) => {
20
+ return dataItems?.some((item) => item.type === 'FOLDER') ?? false;
21
+ };
22
+ /**
23
+ * Get selection summary
24
+ */
25
+ const getSelectionSummary = (dataItems) => {
26
+ const files = getSelectedFiles(dataItems);
27
+ const folders = getSelectedFolders(dataItems);
28
+ return {
29
+ total: dataItems?.length ?? 0,
30
+ files: files.length,
31
+ folders: folders.length,
32
+ hasFiles: files.length > 0,
33
+ hasFolders: folders.length > 0,
34
+ isMixed: files.length > 0 && folders.length > 0,
35
+ };
36
+ };
37
+
38
+ export { getSelectedFiles, getSelectedFolders, getSelectionSummary, hasSelectedFolders };
@@ -12,7 +12,7 @@ const DEPRECATED_PROP_KEYS = ['actionType', 'location', 'path'];
12
12
  const template = (key, index, values) => `\`${key}\`${index < values.length - 1 ? ', ' : ''}`;
13
13
  const getMissingLocationKeys = (location) => location === null
14
14
  ? []
15
- : REQUIRED_LOCATION_KEYS.filter((key) => !location[key]);
15
+ : REQUIRED_LOCATION_KEYS.filter((key) => location[key] === undefined || location[key] === null);
16
16
  let didWarnConflictingBehavior = false;
17
17
  let didWarnDeprecatedAndConflictingProps = false;
18
18
  let didWarnDeprecatedProps = false;
@@ -99,16 +99,19 @@ function useProcessTasks(handler, options) {
99
99
  const getTask = () => tasksRef.current.get(data.id);
100
100
  const { options } = _input;
101
101
  const { onProgress: _onProgress } = options ?? {};
102
- const onProgress = ({ id }, progress, status = 'PENDING') => {
102
+ const onProgress = ({ id }, progressDetails, status = 'PENDING') => {
103
+ const isNumber = typeof progressDetails === 'number';
103
104
  const task = updateTask(id, {
104
- progress,
105
+ progress: isNumber ? progressDetails : progressDetails.progress,
106
+ failureCount: isNumber ? undefined : progressDetails.failureCount,
107
+ successCount: isNumber ? undefined : progressDetails.successCount,
105
108
  status,
106
109
  });
107
110
  if (task && isFunction(onTaskProgress)) {
108
- onTaskProgress(task, progress);
111
+ onTaskProgress(task, progressDetails);
109
112
  }
110
113
  if (task && isFunction(_onProgress)) {
111
- _onProgress(data, progress, status);
114
+ _onProgress(data, progressDetails, status);
112
115
  }
113
116
  };
114
117
  const input = { ..._input, data, all, options: { ...options, onProgress } };
@@ -1,9 +1,4 @@
1
1
  import { isFunction } from '@aws-amplify/ui';
2
- import '@aws-amplify/storage/internals';
3
- import 'aws-amplify';
4
- import 'jszip';
5
- import 'aws-amplify/storage';
6
- import '../actions/configs/context.mjs';
7
2
  import { isDefaultActionViewType } from '../actions/configs/defaults.mjs';
8
3
 
9
4
  const resolveHandler = (value) => (isFunction(value) ? value : value.handler);
@@ -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,16 +11,16 @@ 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';
21
17
  import useResolveTableData from '../../hooks/useResolveTableData/useResolveTableData.mjs';
22
18
  import { COPY_TABLE_RESOLVERS } from '../../utils/tableResolvers/copyResolvers.mjs';
23
19
  import { FILE_DATA_ITEM_TABLE_KEYS } from '../../utils/tableResolvers/constants.mjs';
20
+ import 'aws-amplify';
21
+ import 'jszip';
22
+ import 'aws-amplify/storage';
23
+ import '../../../actions/configs/context.mjs';
24
24
  import { FoldersMessageProvider } from './FoldersMessageControl.mjs';
25
25
  import { FoldersPaginationProvider } from './FoldersPaginationControl.mjs';
26
26
  import { FoldersTableProvider } from './FoldersTableControl.mjs';