@scality/data-browser-library 1.0.0-preview.11 → 1.0.0-preview.15

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 (212) hide show
  1. package/dist/components/DataBrowserUI.d.ts +27 -0
  2. package/dist/components/DataBrowserUI.js +88 -0
  3. package/dist/components/Editor.d.ts +1 -1
  4. package/dist/components/Editor.js +3 -3
  5. package/dist/components/__tests__/BucketCreate.test.js +102 -102
  6. package/dist/components/__tests__/BucketDetails.test.d.ts +1 -0
  7. package/dist/components/__tests__/BucketDetails.test.js +420 -0
  8. package/dist/components/__tests__/BucketLifecycleFormPage.test.js +177 -177
  9. package/dist/components/__tests__/BucketLifecycleList.test.js +85 -85
  10. package/dist/components/__tests__/BucketList.test.js +463 -239
  11. package/dist/components/__tests__/BucketNotificationCreatePage.test.js +84 -84
  12. package/dist/components/__tests__/BucketOverview.test.js +273 -261
  13. package/dist/components/__tests__/BucketPolicyPage.test.js +62 -62
  14. package/dist/components/__tests__/BucketReplicationFormPage.test.js +542 -542
  15. package/dist/components/__tests__/BucketReplicationList.test.js +106 -106
  16. package/dist/components/__tests__/CreateFolderButton.test.js +56 -56
  17. package/dist/components/__tests__/DeleteBucketButton.test.js +62 -62
  18. package/dist/components/__tests__/DeleteBucketConfigRuleButton.test.js +47 -47
  19. package/dist/components/__tests__/DeleteObjectButton.test.js +63 -63
  20. package/dist/components/__tests__/EmptyBucketButton.test.js +56 -56
  21. package/dist/components/__tests__/MetadataSearch.test.js +65 -65
  22. package/dist/components/__tests__/ObjectList.test.js +739 -238
  23. package/dist/components/__tests__/UploadButton.test.js +45 -45
  24. package/dist/components/buckets/BucketCreate.d.ts +2 -2
  25. package/dist/components/buckets/BucketCreate.js +41 -41
  26. package/dist/components/buckets/BucketDetails.d.ts +40 -0
  27. package/dist/components/buckets/BucketDetails.js +208 -88
  28. package/dist/components/buckets/BucketLifecycleFormPage.js +161 -160
  29. package/dist/components/buckets/BucketLifecycleList.d.ts +2 -2
  30. package/dist/components/buckets/BucketLifecycleList.js +46 -46
  31. package/dist/components/buckets/BucketList.d.ts +7 -8
  32. package/dist/components/buckets/BucketList.js +156 -100
  33. package/dist/components/buckets/BucketLocation.js +3 -3
  34. package/dist/components/buckets/BucketOverview.d.ts +7 -1
  35. package/dist/components/buckets/BucketOverview.js +366 -182
  36. package/dist/components/buckets/BucketPage.js +20 -16
  37. package/dist/components/buckets/BucketPolicyButton.js +2 -2
  38. package/dist/components/buckets/BucketPolicyPage.js +27 -25
  39. package/dist/components/buckets/BucketReplicationFormPage.js +133 -132
  40. package/dist/components/buckets/BucketReplicationList.d.ts +2 -2
  41. package/dist/components/buckets/BucketReplicationList.js +41 -41
  42. package/dist/components/buckets/BucketVersioning.js +13 -10
  43. package/dist/components/buckets/DeleteBucketButton.js +5 -5
  44. package/dist/components/buckets/DeleteBucketConfigRuleButton.d.ts +2 -2
  45. package/dist/components/buckets/DeleteBucketConfigRuleButton.js +1 -1
  46. package/dist/components/buckets/EmptyBucketButton.js +20 -20
  47. package/dist/components/buckets/EmptyBucketSummary.d.ts +1 -1
  48. package/dist/components/buckets/EmptyBucketSummary.js +1 -1
  49. package/dist/components/buckets/EmptyBucketSummaryList.js +22 -22
  50. package/dist/components/buckets/__tests__/BucketVersioning.test.js +45 -45
  51. package/dist/components/buckets/notifications/BucketNotificationCreatePage.js +34 -33
  52. package/dist/components/buckets/notifications/EventsSection.js +144 -28
  53. package/dist/components/buckets/notifications/__tests__/events.test.d.ts +1 -0
  54. package/dist/components/buckets/notifications/__tests__/events.test.js +56 -0
  55. package/dist/components/buckets/notifications/events.d.ts +71 -7
  56. package/dist/components/buckets/notifications/events.js +98 -16
  57. package/dist/components/index.d.ts +23 -21
  58. package/dist/components/index.js +4 -2
  59. package/dist/components/layouts/ArrowNavigation.d.ts +1 -2
  60. package/dist/components/layouts/ArrowNavigation.js +22 -10
  61. package/dist/components/layouts/BrowserPageLayout.d.ts +2 -3
  62. package/dist/components/layouts/BrowserPageLayout.js +1 -1
  63. package/dist/components/objects/CreateFolderButton.d.ts +2 -2
  64. package/dist/components/objects/CreateFolderButton.js +10 -10
  65. package/dist/components/objects/DeleteObjectButton.d.ts +1 -1
  66. package/dist/components/objects/DeleteObjectButton.js +20 -20
  67. package/dist/components/objects/ObjectDetails/ObjectMetadata.d.ts +1 -1
  68. package/dist/components/objects/ObjectDetails/ObjectMetadata.js +56 -56
  69. package/dist/components/objects/ObjectDetails/ObjectSummary.d.ts +1 -1
  70. package/dist/components/objects/ObjectDetails/ObjectSummary.js +294 -164
  71. package/dist/components/objects/ObjectDetails/ObjectTags.d.ts +1 -1
  72. package/dist/components/objects/ObjectDetails/ObjectTags.js +25 -25
  73. package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.d.ts +1 -0
  74. package/dist/components/objects/ObjectDetails/__tests__/ObjectDetails.test.js +516 -0
  75. package/dist/components/objects/ObjectDetails/__tests__/ObjectSummary.test.d.ts +1 -0
  76. package/dist/components/objects/ObjectDetails/__tests__/ObjectSummary.test.js +813 -0
  77. package/dist/components/objects/ObjectDetails/index.d.ts +17 -1
  78. package/dist/components/objects/ObjectDetails/index.js +136 -50
  79. package/dist/components/objects/ObjectList.d.ts +11 -9
  80. package/dist/components/objects/ObjectList.js +576 -295
  81. package/dist/components/objects/ObjectLock/EditRetentionButton.js +3 -3
  82. package/dist/components/objects/ObjectLock/ObjectLockRetentionSettings.js +14 -14
  83. package/dist/components/objects/ObjectLock/ObjectLockSettings.d.ts +1 -1
  84. package/dist/components/objects/ObjectLock/ObjectLockSettings.js +29 -28
  85. package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.d.ts +1 -1
  86. package/dist/components/objects/ObjectLock/ObjectLockSettingsUtils.js +6 -6
  87. package/dist/components/objects/ObjectLock/__tests__/EditRetentionButton.test.js +50 -50
  88. package/dist/components/objects/ObjectLock/__tests__/ObjectLockSettings.test.js +77 -77
  89. package/dist/components/objects/ObjectPage.js +5 -4
  90. package/dist/components/objects/UploadButton.d.ts +3 -3
  91. package/dist/components/objects/UploadButton.js +6 -6
  92. package/dist/components/providers/DataBrowserProvider.d.ts +15 -4
  93. package/dist/components/providers/DataBrowserProvider.js +33 -11
  94. package/dist/components/search/MetadataSearch.js +26 -25
  95. package/dist/components/search/SearchHints.js +1 -1
  96. package/dist/components/ui/ArrayFieldActions.js +4 -4
  97. package/dist/components/ui/ConfirmDeleteRuleModal.d.ts +1 -1
  98. package/dist/components/ui/ConfirmDeleteRuleModal.js +1 -1
  99. package/dist/components/ui/DeleteObjectModalContent.d.ts +1 -1
  100. package/dist/components/ui/DeleteObjectModalContent.js +12 -12
  101. package/dist/components/ui/FilterFormSection.d.ts +2 -2
  102. package/dist/components/ui/FilterFormSection.js +29 -29
  103. package/dist/components/ui/Search.elements.d.ts +1 -1
  104. package/dist/components/ui/Search.elements.js +7 -7
  105. package/dist/components/ui/Table.elements.js +5 -5
  106. package/dist/config/factory.d.ts +23 -10
  107. package/dist/config/factory.js +22 -7
  108. package/dist/config/types.d.ts +134 -0
  109. package/dist/contexts/DataBrowserUICustomizationContext.d.ts +27 -0
  110. package/dist/contexts/DataBrowserUICustomizationContext.js +13 -0
  111. package/dist/hooks/__tests__/useISVBucketDetection.test.js +41 -41
  112. package/dist/hooks/__tests__/useIsBucketEmpty.test.js +25 -25
  113. package/dist/hooks/bucketConfiguration.d.ts +1 -1
  114. package/dist/hooks/bucketConfiguration.js +48 -48
  115. package/dist/hooks/bucketOperations.d.ts +1 -1
  116. package/dist/hooks/bucketOperations.js +6 -6
  117. package/dist/hooks/factories/__tests__/useCreateS3FunctionMutationHook.test.js +78 -78
  118. package/dist/hooks/factories/__tests__/useCreateS3InfiniteQueryHook.test.js +78 -78
  119. package/dist/hooks/factories/__tests__/useCreateS3LoginHook.test.js +42 -42
  120. package/dist/hooks/factories/__tests__/useCreateS3MutationHook.test.js +61 -61
  121. package/dist/hooks/factories/__tests__/useCreateS3QueryHook.test.js +63 -63
  122. package/dist/hooks/factories/index.d.ts +4 -4
  123. package/dist/hooks/factories/useCreateS3InfiniteQueryHook.d.ts +2 -2
  124. package/dist/hooks/factories/useCreateS3InfiniteQueryHook.js +15 -12
  125. package/dist/hooks/factories/useCreateS3LoginHook.d.ts +2 -2
  126. package/dist/hooks/factories/useCreateS3MutationHook.d.ts +3 -3
  127. package/dist/hooks/factories/useCreateS3MutationHook.js +6 -1
  128. package/dist/hooks/factories/useCreateS3QueryHook.d.ts +2 -2
  129. package/dist/hooks/factories/useCreateS3QueryHook.js +8 -5
  130. package/dist/hooks/index.d.ts +14 -13
  131. package/dist/hooks/index.js +2 -1
  132. package/dist/hooks/loginOperations.d.ts +1 -1
  133. package/dist/hooks/loginOperations.js +1 -1
  134. package/dist/hooks/objectOperations.d.ts +2 -2
  135. package/dist/hooks/objectOperations.js +49 -49
  136. package/dist/hooks/presignedOperations.d.ts +2 -2
  137. package/dist/hooks/presignedOperations.js +3 -3
  138. package/dist/hooks/useBatchObjectLegalHold.js +7 -4
  139. package/dist/hooks/useDataBrowserNavigate.d.ts +14 -0
  140. package/dist/hooks/useDataBrowserNavigate.js +24 -0
  141. package/dist/hooks/useDeleteBucketConfigRule.d.ts +2 -2
  142. package/dist/hooks/useDeleteBucketConfigRule.js +4 -4
  143. package/dist/hooks/useEmptyBucket.js +10 -10
  144. package/dist/hooks/useISVBucketDetection.js +3 -3
  145. package/dist/hooks/useIsBucketEmpty.js +4 -4
  146. package/dist/hooks/useLoginMutation.d.ts +1 -1
  147. package/dist/hooks/useLoginMutation.js +1 -1
  148. package/dist/hooks/useS3Client.d.ts +5 -0
  149. package/dist/hooks/useS3Client.js +3 -2
  150. package/dist/hooks/useS3ConfigSwitch.d.ts +11 -0
  151. package/dist/hooks/useS3ConfigSwitch.js +37 -0
  152. package/dist/index.d.ts +6 -6
  153. package/dist/test/msw/handlers/deleteBucket.d.ts +1 -1
  154. package/dist/test/msw/handlers/deleteBucket.js +20 -10
  155. package/dist/test/msw/handlers/getBucketAcl.d.ts +1 -1
  156. package/dist/test/msw/handlers/getBucketAcl.js +29 -17
  157. package/dist/test/msw/handlers/getBucketLocation.d.ts +1 -1
  158. package/dist/test/msw/handlers/getBucketLocation.js +29 -15
  159. package/dist/test/msw/handlers/getBucketPolicy.d.ts +1 -1
  160. package/dist/test/msw/handlers/getBucketPolicy.js +52 -32
  161. package/dist/test/msw/handlers/headObject.d.ts +1 -1
  162. package/dist/test/msw/handlers/headObject.js +31 -13
  163. package/dist/test/msw/handlers/listBuckets.d.ts +1 -1
  164. package/dist/test/msw/handlers/listBuckets.js +5 -3
  165. package/dist/test/msw/handlers/listObjectVersions.d.ts +1 -1
  166. package/dist/test/msw/handlers/listObjectVersions.js +38 -26
  167. package/dist/test/msw/handlers/listObjects.d.ts +1 -1
  168. package/dist/test/msw/handlers/listObjects.js +35 -23
  169. package/dist/test/msw/handlers/objectLegalHold.d.ts +1 -1
  170. package/dist/test/msw/handlers/objectLegalHold.js +31 -16
  171. package/dist/test/msw/handlers/objectRetention.d.ts +1 -1
  172. package/dist/test/msw/handlers/objectRetention.js +31 -17
  173. package/dist/test/msw/handlers/putBucketAcl.d.ts +1 -1
  174. package/dist/test/msw/handlers/putBucketAcl.js +29 -14
  175. package/dist/test/msw/handlers/putObject.d.ts +1 -1
  176. package/dist/test/msw/handlers/putObject.js +27 -12
  177. package/dist/test/msw/handlers.d.ts +3 -3
  178. package/dist/test/msw/handlers.js +72 -49
  179. package/dist/test/msw/index.d.ts +2 -2
  180. package/dist/test/msw/server.d.ts +1 -1
  181. package/dist/test/msw/server.js +1 -1
  182. package/dist/test/msw/utils.js +2 -2
  183. package/dist/test/setup.d.ts +1 -1
  184. package/dist/test/setup.js +19 -19
  185. package/dist/test/testUtils.d.ts +70 -12
  186. package/dist/test/testUtils.js +160 -75
  187. package/dist/test/utils/errorHandling.test.js +119 -119
  188. package/dist/types/index.d.ts +8 -31
  189. package/dist/utils/__tests__/s3ConfigIdentifier.test.d.ts +1 -0
  190. package/dist/utils/__tests__/s3ConfigIdentifier.test.js +429 -0
  191. package/dist/utils/constants.d.ts +7 -0
  192. package/dist/utils/constants.js +16 -9
  193. package/dist/utils/deletion/index.d.ts +2 -2
  194. package/dist/utils/deletion/messages.d.ts +1 -1
  195. package/dist/utils/deletion/messages.js +4 -4
  196. package/dist/utils/errorHandling.d.ts +3 -3
  197. package/dist/utils/errorHandling.js +6 -6
  198. package/dist/utils/hooks.js +8 -8
  199. package/dist/utils/index.d.ts +5 -4
  200. package/dist/utils/index.js +2 -0
  201. package/dist/utils/proxyMiddleware.d.ts +1 -1
  202. package/dist/utils/proxyMiddleware.js +6 -11
  203. package/dist/utils/s3Client.d.ts +2 -2
  204. package/dist/utils/s3Client.js +1 -1
  205. package/dist/utils/s3ConfigIdentifier.d.ts +68 -0
  206. package/dist/utils/s3ConfigIdentifier.js +55 -0
  207. package/dist/utils/s3RuleUtils.d.ts +5 -5
  208. package/dist/utils/s3RuleUtils.js +17 -17
  209. package/dist/utils/useFeatures.js +1 -1
  210. package/dist/utils/useSupportedNotificationEvents.d.ts +6 -0
  211. package/dist/utils/useSupportedNotificationEvents.js +7 -0
  212. package/package.json +3 -3
@@ -1,8 +1,11 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { ConstrainedText, FormattedDateTime, Icon, Link, PrettyBytes, Text, Toggle, Wrap, spacing } from "@scality/core-ui";
2
+ import { ConstrainedText, FormattedDateTime, Icon, Link, PrettyBytes, Text, Toggle, Wrap, spacing, useToast } from "@scality/core-ui";
3
3
  import { Box, Table } from "@scality/core-ui/dist/next";
4
- import { useCallback, useEffect, useMemo, useState } from "react";
4
+ import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
5
+ import { useDataBrowserUICustomization } from "../../contexts/DataBrowserUICustomizationContext.js";
6
+ import { useInvalidateQueries } from "../providers/DataBrowserProvider.js";
5
7
  import { useSearchObjects, useSearchObjectsVersions } from "../../hooks/index.js";
8
+ import { useGetPresignedDownload } from "../../hooks/presignedOperations.js";
6
9
  import { useBatchObjectLegalHold } from "../../hooks/useBatchObjectLegalHold.js";
7
10
  import { useQueryParams } from "../../utils/hooks.js";
8
11
  import { useFeatures } from "../../utils/useFeatures.js";
@@ -11,22 +14,312 @@ import MetadataSearch from "../search/MetadataSearch.js";
11
14
  import UploadButton from "./UploadButton.js";
12
15
  import DeleteObjectButton from "./DeleteObjectButton.js";
13
16
  const DEFAULT_PAGE_SIZE = 20;
17
+ const SEARCH_QUERY_PARAM = 'search';
18
+ const NAME_COLUMN_FLEX = '2';
19
+ const VERSION_ID_COLUMN_FLEX = '1.5';
20
+ const LAST_MODIFIED_COLUMN_FLEX = '1';
21
+ const SIZE_COLUMN_FLEX = '1';
22
+ const STORAGE_CLASS_COLUMN_FLEX = '1';
23
+ const isObjectLike = (item)=>'folder' !== item.type;
14
24
  const getVersionTextColor = (row)=>{
15
25
  const isLatest = row.original.IsLatest;
16
- const isVersion = "version" === row.original.type;
17
- return !isLatest && isVersion ? "infoPrimary" : "textPrimary";
26
+ const isVersion = 'version' === row.original.type;
27
+ return !isLatest && isVersion ? 'infoPrimary' : 'textPrimary';
18
28
  };
29
+ const removePrefix = (path, prefix)=>{
30
+ if (!prefix) return path;
31
+ if (path.startsWith(prefix)) return path.slice(prefix.length);
32
+ return path;
33
+ };
34
+ const createLegalHoldKey = (key, versionId)=>`${key}:${versionId ?? 'null'}`;
35
+ const downloadFile = (url, filename, onCleanup)=>{
36
+ try {
37
+ new URL(url);
38
+ const link = document.createElement('a');
39
+ link.href = url;
40
+ link.download = filename;
41
+ link.style.display = 'none';
42
+ link.rel = 'noopener noreferrer';
43
+ document.body.appendChild(link);
44
+ link.click();
45
+ const timeoutId = setTimeout(()=>{
46
+ if (document.body.contains(link)) document.body.removeChild(link);
47
+ }, 100);
48
+ onCleanup?.(timeoutId);
49
+ } catch (error) {
50
+ console.error('Invalid download URL:', url, error);
51
+ throw new Error('Failed to initiate download: Invalid URL');
52
+ }
53
+ };
54
+ const createNameColumn = (onPrefixChange, onDownload)=>({
55
+ Header: 'Name',
56
+ accessor: 'displayName',
57
+ id: 'name',
58
+ sortType: (rowA, rowB)=>{
59
+ const aIsFolder = 'folder' === rowA.original.type;
60
+ const bIsFolder = 'folder' === rowB.original.type;
61
+ if (aIsFolder && !bIsFolder) return -1;
62
+ if (!aIsFolder && bIsFolder) return 1;
63
+ const aName = String(rowA.values.displayName || '');
64
+ const bName = String(rowB.values.displayName || '');
65
+ return aName.localeCompare(bName, 'en', {
66
+ sensitivity: 'base'
67
+ });
68
+ },
69
+ Cell: ({ value, row })=>{
70
+ const isFolder = 'folder' === row.original.type;
71
+ const isVersion = 'version' === row.original.type;
72
+ const isDeleteMarker = 'deleteMarker' === row.original.type;
73
+ const isLatest = row.original.IsLatest;
74
+ let iconName;
75
+ iconName = isFolder ? 'Folder' : isDeleteMarker ? 'Deletion-marker' : 'File';
76
+ const shouldIndent = isVersion && !isLatest;
77
+ const isLegalHoldEnabled = isObjectLike(row.original) && Boolean(row.original.isLegalHoldEnabled);
78
+ return /*#__PURE__*/ jsx(ConstrainedText, {
79
+ text: /*#__PURE__*/ jsxs(Box, {
80
+ display: "flex",
81
+ alignItems: "center",
82
+ gap: spacing.r8,
83
+ paddingLeft: shouldIndent ? spacing.r24 : 0,
84
+ children: [
85
+ /*#__PURE__*/ jsx(Icon, {
86
+ name: iconName,
87
+ size: "sm",
88
+ color: getVersionTextColor(row)
89
+ }),
90
+ isLegalHoldEnabled && /*#__PURE__*/ jsx(Icon, {
91
+ name: "Rebalance",
92
+ size: "sm",
93
+ color: getVersionTextColor(row)
94
+ }),
95
+ isDeleteMarker ? /*#__PURE__*/ jsx(Text, {
96
+ color: getVersionTextColor(row),
97
+ children: value
98
+ }) : /*#__PURE__*/ jsx(Text, {
99
+ color: getVersionTextColor(row),
100
+ children: /*#__PURE__*/ jsx(Link, {
101
+ onClick: (e)=>{
102
+ e.stopPropagation();
103
+ if (isFolder) onPrefixChange?.(row.original.Key ?? '');
104
+ else onDownload?.(row.original);
105
+ },
106
+ children: value
107
+ })
108
+ })
109
+ ]
110
+ }),
111
+ lineClamp: 2
112
+ });
113
+ },
114
+ cellStyle: {
115
+ flex: NAME_COLUMN_FLEX,
116
+ width: 'unset'
117
+ }
118
+ });
119
+ const createVersionIdColumn = ()=>({
120
+ Header: 'Version ID',
121
+ accessor: 'VersionId',
122
+ id: 'versionId',
123
+ Cell: ({ row })=>{
124
+ const isFolder = 'folder' === row.original.type;
125
+ const isDeleteMarker = 'deleteMarker' === row.original.type;
126
+ const isLatest = row.original.IsLatest;
127
+ if (isFolder || isDeleteMarker) return /*#__PURE__*/ jsx(Text, {
128
+ children: "-"
129
+ });
130
+ const versionId = 'version' === row.original.type ? row.original.VersionId ?? '-' : '-';
131
+ const textColor = isLatest ? void 0 : 'infoPrimary';
132
+ return /*#__PURE__*/ jsx(ConstrainedText, {
133
+ text: versionId,
134
+ lineClamp: 1,
135
+ color: textColor
136
+ });
137
+ },
138
+ cellStyle: {
139
+ flex: VERSION_ID_COLUMN_FLEX,
140
+ textAlign: 'left',
141
+ paddingRight: spacing.r16,
142
+ width: 'unset',
143
+ maxWidth: '8rem'
144
+ }
145
+ });
146
+ const createLastModifiedColumn = ()=>({
147
+ Header: 'Modified on',
148
+ accessor: 'LastModified',
149
+ id: 'lastModified',
150
+ Cell: ({ value, row })=>{
151
+ if ('folder' === row.original.type || null == value) return /*#__PURE__*/ jsx(Text, {
152
+ children: "-"
153
+ });
154
+ return /*#__PURE__*/ jsx(Text, {
155
+ color: getVersionTextColor(row),
156
+ children: /*#__PURE__*/ jsx(FormattedDateTime, {
157
+ format: "date-time-second",
158
+ value: new Date(value)
159
+ })
160
+ });
161
+ },
162
+ cellStyle: {
163
+ flex: LAST_MODIFIED_COLUMN_FLEX,
164
+ textAlign: 'right',
165
+ paddingRight: spacing.r16,
166
+ width: 'unset',
167
+ minWidth: '10rem'
168
+ }
169
+ });
170
+ const createSizeColumn = ()=>({
171
+ Header: 'Size',
172
+ accessor: 'Size',
173
+ id: 'size',
174
+ Cell: ({ value, row })=>{
175
+ if ('folder' === row.original.type || null == value) return /*#__PURE__*/ jsx(Text, {
176
+ children: "-"
177
+ });
178
+ return /*#__PURE__*/ jsx(Box, {
179
+ display: "flex",
180
+ justifyContent: "flex-end",
181
+ children: /*#__PURE__*/ jsx(Text, {
182
+ color: getVersionTextColor(row),
183
+ children: /*#__PURE__*/ jsx(PrettyBytes, {
184
+ bytes: value,
185
+ decimals: 2
186
+ })
187
+ })
188
+ });
189
+ },
190
+ cellStyle: {
191
+ flex: SIZE_COLUMN_FLEX,
192
+ textAlign: 'right',
193
+ paddingRight: spacing.r16,
194
+ width: 'unset'
195
+ }
196
+ });
197
+ const createStorageClassColumn = ()=>({
198
+ Header: 'Storage Location',
199
+ accessor: 'StorageClass',
200
+ id: 'storageClass',
201
+ Cell: ({ value, row })=>{
202
+ if (null == value) return /*#__PURE__*/ jsx(Text, {
203
+ children: "-"
204
+ });
205
+ return /*#__PURE__*/ jsx(Box, {
206
+ display: "flex",
207
+ justifyContent: "flex-end",
208
+ children: /*#__PURE__*/ jsx(Text, {
209
+ color: getVersionTextColor(row),
210
+ children: 'STANDARD' === value ? 'default' : value
211
+ })
212
+ });
213
+ },
214
+ cellStyle: {
215
+ flex: STORAGE_CLASS_COLUMN_FLEX,
216
+ textAlign: 'right',
217
+ paddingRight: spacing.r16,
218
+ width: 'unset'
219
+ }
220
+ });
221
+ const buildCustomColumn = (columnConfig)=>({
222
+ Header: columnConfig.header,
223
+ id: String(columnConfig.id),
224
+ Cell: ({ row })=>{
225
+ const RenderComponent = columnConfig.render;
226
+ return /*#__PURE__*/ jsx(RenderComponent, {
227
+ data: row.original
228
+ });
229
+ },
230
+ cellStyle: {
231
+ width: columnConfig.width ?? 'unset',
232
+ flex: columnConfig.width ? void 0 : '1',
233
+ ...columnConfig.cellStyle
234
+ }
235
+ });
236
+ function createOverrideMap(customItems) {
237
+ return new Map(customItems.filter((item)=>item.id).map((item)=>[
238
+ String(item.id),
239
+ item
240
+ ]));
241
+ }
19
242
  const ObjectList = ({ bucketName, prefix, onObjectSelect, onPrefixChange })=>{
243
+ const { extraObjectListColumns, extraObjectListActions } = useDataBrowserUICustomization();
244
+ const invalidateQueries = useInvalidateQueries();
20
245
  const [showVersions, setShowVersions] = useState(false);
21
- const isMetadataSearchEnabled = useFeatures("metadatasearch");
22
- const metadataSearchQuery = useQueryParams().get("metadatasearch");
246
+ const isMetadataSearchEnabled = useFeatures('metadatasearch');
247
+ const metadataSearchQuery = useQueryParams().get('metadatasearch');
23
248
  const [selectedObjects, setSelectedObjects] = useState([]);
249
+ const { mutateAsync: getPresignedDownload } = useGetPresignedDownload();
250
+ const downloadingRef = useRef(new Set());
251
+ const downloadTimeoutsRef = useRef(new Map());
252
+ const { showToast } = useToast();
253
+ const onObjectSelectRef = useRef(onObjectSelect);
254
+ const onPrefixChangeRef = useRef(onPrefixChange);
255
+ useEffect(()=>{
256
+ onObjectSelectRef.current = onObjectSelect;
257
+ }, [
258
+ onObjectSelect
259
+ ]);
260
+ useEffect(()=>{
261
+ onPrefixChangeRef.current = onPrefixChange;
262
+ }, [
263
+ onPrefixChange
264
+ ]);
265
+ useEffect(()=>()=>{
266
+ downloadTimeoutsRef.current.forEach((timeout)=>clearTimeout(timeout));
267
+ downloadTimeoutsRef.current.clear();
268
+ }, []);
269
+ const handleObjectSelect = useCallback((object)=>{
270
+ onObjectSelectRef.current(object);
271
+ }, []);
272
+ const handlePrefixChange = useCallback((newPrefix)=>{
273
+ onPrefixChangeRef.current(newPrefix);
274
+ }, []);
275
+ const handleDownload = useCallback(async (object)=>{
276
+ if (!object.Key) return void console.warn('Cannot download object: missing Key');
277
+ const versionId = isObjectLike(object) ? object.VersionId : void 0;
278
+ const downloadKey = versionId ? `${object.Key}:${versionId}` : object.Key;
279
+ if (downloadingRef.current.has(downloadKey)) return void console.debug(`Download already in progress for: ${object.Key}`);
280
+ downloadingRef.current.add(downloadKey);
281
+ try {
282
+ const rawFilename = object.displayName || object.Key.split('/').pop() || 'download';
283
+ const sanitized = rawFilename.replace(/["\r\n\t]/g, '');
284
+ const encoded = encodeURIComponent(sanitized);
285
+ const result = await getPresignedDownload({
286
+ Bucket: bucketName,
287
+ Key: object.Key,
288
+ ResponseContentDisposition: `attachment; filename="${sanitized}"; filename*=UTF-8''${encoded}`,
289
+ ...versionId ? {
290
+ VersionId: versionId
291
+ } : {}
292
+ });
293
+ if (!result?.Url) throw new Error('Failed to generate presigned URL: No URL returned');
294
+ downloadFile(result.Url, sanitized, (timeoutId)=>{
295
+ downloadTimeoutsRef.current.set(`${downloadKey}_link`, timeoutId);
296
+ });
297
+ } catch (error) {
298
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
299
+ showToast({
300
+ open: true,
301
+ message: errorMessage,
302
+ status: 'error'
303
+ });
304
+ } finally{
305
+ const timeoutId = setTimeout(()=>{
306
+ downloadingRef.current.delete(downloadKey);
307
+ downloadTimeoutsRef.current.delete(downloadKey);
308
+ downloadTimeoutsRef.current.delete(`${downloadKey}_link`);
309
+ }, 1000);
310
+ downloadTimeoutsRef.current.set(downloadKey, timeoutId);
311
+ }
312
+ }, [
313
+ bucketName,
314
+ getPresignedDownload,
315
+ showToast
316
+ ]);
24
317
  const searchParams = useMemo(()=>{
25
318
  const baseParams = {
26
319
  Bucket: bucketName,
27
320
  Prefix: prefix,
28
321
  MaxKeys: DEFAULT_PAGE_SIZE,
29
- Delimiter: "/"
322
+ Delimiter: '/'
30
323
  };
31
324
  if (isMetadataSearchEnabled && metadataSearchQuery) return {
32
325
  ...baseParams,
@@ -40,72 +333,121 @@ const ObjectList = ({ bucketName, prefix, onObjectSelect, onPrefixChange })=>{
40
333
  metadataSearchQuery
41
334
  ]);
42
335
  const listObjectsQuery = useSearchObjects(searchParams, {
43
- enabled: !showVersions
336
+ enabled: Boolean(bucketName)
44
337
  });
45
- const listVersionsQuery = useSearchObjectsVersions(searchParams, {
46
- enabled: showVersions
338
+ const versionSearchParams = useMemo(()=>({
339
+ Bucket: bucketName,
340
+ Prefix: prefix,
341
+ MaxKeys: DEFAULT_PAGE_SIZE
342
+ }), [
343
+ bucketName,
344
+ prefix
345
+ ]);
346
+ const listVersionsQuery = useSearchObjectsVersions(versionSearchParams, {
347
+ enabled: showVersions && Boolean(bucketName)
47
348
  });
48
- const activeQuery = showVersions ? listVersionsQuery : listObjectsQuery;
49
349
  useEffect(()=>{
50
350
  setSelectedObjects([]);
51
351
  }, [
52
352
  prefix,
53
353
  showVersions
54
354
  ]);
55
- const { data, error, hasNextPage, fetchNextPage, isFetchingNextPage, isLoading } = activeQuery;
355
+ const data = showVersions ? listVersionsQuery.data : listObjectsQuery.data;
356
+ const error = showVersions ? listVersionsQuery.error : listObjectsQuery.error;
357
+ const hasNextPage = showVersions ? listVersionsQuery.hasNextPage : listObjectsQuery.hasNextPage;
358
+ const fetchNextPage = showVersions ? listVersionsQuery.fetchNextPage : listObjectsQuery.fetchNextPage;
359
+ const isFetchingNextPage = showVersions ? listVersionsQuery.isFetchingNextPage : listObjectsQuery.isFetchingNextPage;
360
+ const isLoading = showVersions ? listVersionsQuery.isLoading : listObjectsQuery.isLoading;
56
361
  const objectsForLegalHold = useMemo(()=>{
57
362
  const objects = [];
58
- if (showVersions && listVersionsQuery.data?.pages) listVersionsQuery.data.pages.forEach((page)=>{
59
- if (page.Versions) page.Versions.forEach((version)=>{
60
- if (version.Key) objects.push({
61
- Key: version.Key,
62
- VersionId: version.VersionId
63
- });
64
- });
65
- });
66
- else if (!showVersions && data?.pages) data.pages.forEach((page)=>{
67
- if (page.Contents) page.Contents.forEach((obj)=>{
68
- if (obj.Key) objects.push({
69
- Key: obj.Key
70
- });
71
- });
72
- });
363
+ const currentData = showVersions ? listVersionsQuery.data : listObjectsQuery.data;
364
+ if (!currentData?.pages) return objects;
365
+ let count = 0;
366
+ if (showVersions) for (const page of currentData.pages){
367
+ if (count >= 100) break;
368
+ if (page.Versions) for (const version of page.Versions){
369
+ if (count >= 100) break;
370
+ if (version.Key) {
371
+ objects.push({
372
+ Key: version.Key,
373
+ VersionId: version.VersionId
374
+ });
375
+ count++;
376
+ }
377
+ }
378
+ }
379
+ else for (const page of currentData.pages){
380
+ if (count >= 100) break;
381
+ if (page.Contents) for (const obj of page.Contents){
382
+ if (count >= 100) break;
383
+ if (obj.Key) {
384
+ objects.push({
385
+ Key: obj.Key
386
+ });
387
+ count++;
388
+ }
389
+ }
390
+ }
73
391
  return objects;
74
392
  }, [
75
- data,
76
393
  showVersions,
77
- listVersionsQuery.data
394
+ listVersionsQuery.data,
395
+ listObjectsQuery.data
78
396
  ]);
79
- const { data: legalHoldData } = useBatchObjectLegalHold(bucketName, objectsForLegalHold, true);
80
- const tableData = useMemo(()=>{
397
+ const shouldFetchLegalHold = useMemo(()=>Boolean(bucketName) && objectsForLegalHold.length > 0, [
398
+ bucketName,
399
+ objectsForLegalHold.length
400
+ ]);
401
+ const { data: legalHoldData } = useBatchObjectLegalHold(bucketName, objectsForLegalHold, shouldFetchLegalHold);
402
+ const processFolders = useCallback((pages)=>{
81
403
  const folders = [];
404
+ pages.forEach((page)=>{
405
+ if (page.CommonPrefixes) page.CommonPrefixes.forEach((cp)=>{
406
+ if (cp.Prefix) {
407
+ const folderName = removePrefix(cp.Prefix, prefix).replace(/\/$/, '');
408
+ folders.push({
409
+ Key: cp.Prefix,
410
+ displayName: folderName + '/',
411
+ type: 'folder'
412
+ });
413
+ }
414
+ });
415
+ });
416
+ return folders;
417
+ }, [
418
+ prefix
419
+ ]);
420
+ const createObjectItem = useCallback((obj, legalHoldData)=>{
421
+ const legalHoldKey = createLegalHoldKey(obj.Key, null);
422
+ const isLegalHoldEnabled = legalHoldData?.[legalHoldKey]?.isLegalHoldEnabled ?? false;
423
+ return {
424
+ ...obj,
425
+ displayName: removePrefix(obj.Key, prefix),
426
+ type: 'object',
427
+ isLegalHoldEnabled
428
+ };
429
+ }, [
430
+ prefix
431
+ ]);
432
+ const tableData = useMemo(()=>{
433
+ let folders = [];
82
434
  const items = [];
83
435
  if (showVersions) {
84
- if (listObjectsQuery.data?.pages) listObjectsQuery.data.pages.forEach((page)=>{
85
- if (page.CommonPrefixes) page.CommonPrefixes.forEach((cp)=>{
86
- if (cp.Prefix) {
87
- const folderName = cp.Prefix.replace(prefix, "").replace(/\/$/, "");
88
- folders.push({
89
- Key: cp.Prefix,
90
- displayName: folderName + "/",
91
- type: "folder",
92
- LastModified: void 0,
93
- Size: void 0
94
- });
95
- }
96
- });
97
- });
436
+ folders = listObjectsQuery.data?.pages ? processFolders(listObjectsQuery.data.pages) : [];
437
+ const objectsWithVersions = new Set();
98
438
  if (listVersionsQuery.data?.pages) {
99
439
  const itemsByKey = {};
100
440
  listVersionsQuery.data.pages.forEach((page)=>{
101
441
  if (page.Versions) page.Versions.forEach((version)=>{
102
- if (version.Key) {
442
+ if (version.Key && !version.Key.endsWith('/')) {
443
+ objectsWithVersions.add(version.Key);
103
444
  if (!itemsByKey[version.Key]) itemsByKey[version.Key] = [];
104
445
  itemsByKey[version.Key].push(version);
105
446
  }
106
447
  });
107
448
  if (page.DeleteMarkers) page.DeleteMarkers.forEach((marker)=>{
108
- if (marker.Key) {
449
+ if (marker.Key && !marker.Key.endsWith('/')) {
450
+ objectsWithVersions.add(marker.Key);
109
451
  if (!itemsByKey[marker.Key]) itemsByKey[marker.Key] = [];
110
452
  itemsByKey[marker.Key].push({
111
453
  ...marker,
@@ -114,25 +456,29 @@ const ObjectList = ({ bucketName, prefix, onObjectSelect, onPrefixChange })=>{
114
456
  }
115
457
  });
116
458
  });
117
- Object.keys(itemsByKey).sort().forEach((key)=>{
459
+ Object.keys(itemsByKey).sort((a, b)=>a.localeCompare(b, 'en', {
460
+ sensitivity: 'base'
461
+ })).forEach((key)=>{
118
462
  const allItems = itemsByKey[key];
119
463
  allItems.sort((a, b)=>{
120
464
  if (a.IsLatest) return -1;
121
465
  if (b.IsLatest) return 1;
122
- const aDate = a.LastModified ? new Date(a.LastModified).getTime() : 0;
123
- const bDate = b.LastModified ? new Date(b.LastModified).getTime() : 0;
124
- return bDate - aDate;
466
+ const aTime = a.LastModified?.getTime() ?? 0;
467
+ const bTime = b.LastModified?.getTime() ?? 0;
468
+ return bTime - aTime;
125
469
  });
126
470
  allItems.forEach((item)=>{
127
- const baseName = item.Key.replace(prefix, "");
471
+ if (!item.Key) return;
472
+ if (item.Key.endsWith('/')) return;
473
+ const baseName = removePrefix(item.Key, prefix);
128
474
  const isLatest = item.IsLatest;
129
- const isDeleteMarker = "isDeleteMarker" in item && item.isDeleteMarker;
130
- const legalHoldKey = `${item.Key}:${item.VersionId || "null"}`;
131
- const isLegalHoldEnabled = legalHoldData?.[legalHoldKey]?.isLegalHoldEnabled || false;
475
+ const isDeleteMarker = 'isDeleteMarker' in item && item.isDeleteMarker;
476
+ const legalHoldKey = createLegalHoldKey(item.Key, item.VersionId);
477
+ const isLegalHoldEnabled = legalHoldData?.[legalHoldKey]?.isLegalHoldEnabled ?? false;
132
478
  if (isDeleteMarker) items.push({
133
479
  ...item,
134
480
  displayName: baseName,
135
- type: "deleteMarker",
481
+ type: 'deleteMarker',
136
482
  IsLatest: isLatest,
137
483
  isDeleteMarker: true,
138
484
  isLegalHoldEnabled
@@ -140,231 +486,171 @@ const ObjectList = ({ bucketName, prefix, onObjectSelect, onPrefixChange })=>{
140
486
  else items.push({
141
487
  ...item,
142
488
  displayName: baseName,
143
- type: "version",
489
+ type: 'version',
144
490
  IsLatest: isLatest,
145
491
  isLegalHoldEnabled
146
492
  });
147
493
  });
148
494
  });
149
495
  }
150
- } else if (data?.pages) data.pages.forEach((page)=>{
151
- if (page.CommonPrefixes) page.CommonPrefixes.forEach((cp)=>{
152
- if (cp.Prefix) {
153
- const folderName = cp.Prefix.replace(prefix, "").replace(/\/$/, "");
154
- folders.push({
155
- Key: cp.Prefix,
156
- displayName: folderName + "/",
157
- type: "folder",
158
- LastModified: void 0,
159
- Size: void 0
160
- });
161
- }
496
+ if (listObjectsQuery.data?.pages) listObjectsQuery.data.pages.forEach((page)=>{
497
+ if (page.Contents) page.Contents.forEach((obj)=>{
498
+ if (obj.Key && !obj.Key.endsWith('/') && !objectsWithVersions.has(obj.Key)) items.push(createObjectItem(obj, legalHoldData));
499
+ });
162
500
  });
163
- if (page.Contents) page.Contents.forEach((obj)=>{
164
- if (obj.Key && !obj.Key.endsWith("/")) {
165
- const legalHoldKey = `${obj.Key}:null`;
166
- const isLegalHoldEnabled = legalHoldData?.[legalHoldKey]?.isLegalHoldEnabled || false;
167
- items.push({
168
- ...obj,
169
- displayName: obj.Key.replace(prefix, ""),
170
- type: "object",
171
- isLegalHoldEnabled
172
- });
173
- }
501
+ } else {
502
+ folders = data?.pages ? processFolders(data.pages) : [];
503
+ if (data?.pages) data.pages.forEach((page)=>{
504
+ if (page.Contents) page.Contents.forEach((obj)=>{
505
+ if (obj.Key && !obj.Key.endsWith('/')) items.push(createObjectItem(obj, legalHoldData));
506
+ });
174
507
  });
175
- });
508
+ }
176
509
  const allItems = [
177
510
  ...folders,
178
511
  ...items
179
512
  ];
180
513
  return allItems;
181
514
  }, [
182
- data,
183
- prefix,
184
515
  showVersions,
185
516
  listObjectsQuery.data,
186
517
  listVersionsQuery.data,
187
- legalHoldData
518
+ legalHoldData,
519
+ processFolders,
520
+ createObjectItem
521
+ ]);
522
+ const versionIdColumn = useMemo(()=>createVersionIdColumn(), []);
523
+ const lastModifiedColumn = useMemo(()=>createLastModifiedColumn(), []);
524
+ const sizeColumn = useMemo(()=>createSizeColumn(), []);
525
+ const storageClassColumn = useMemo(()=>createStorageClassColumn(), []);
526
+ const nameColumn = useMemo(()=>createNameColumn(handlePrefixChange, handleDownload), [
527
+ handlePrefixChange,
528
+ handleDownload
188
529
  ]);
189
530
  const columns = useMemo(()=>{
190
- const cols = [
191
- {
192
- Header: "Name",
193
- accessor: "displayName",
194
- id: "name",
195
- sortType: (rowA, rowB)=>{
196
- const aIsFolder = "folder" === rowA.original.type;
197
- const bIsFolder = "folder" === rowB.original.type;
198
- if (aIsFolder && !bIsFolder) return -1;
199
- if (!aIsFolder && bIsFolder) return 1;
200
- const aName = String(rowA.values.displayName || "");
201
- const bName = String(rowB.values.displayName || "");
202
- return aName.localeCompare(bName);
203
- },
204
- Cell: ({ value, row })=>{
205
- const displayValue = value;
206
- const isFolder = "folder" === row.original.type;
207
- const isVersion = "version" === row.original.type;
208
- const isDeleteMarker = "deleteMarker" === row.original.type;
209
- const isLatest = row.original.IsLatest;
210
- let iconName;
211
- iconName = isFolder ? "Folder" : isDeleteMarker ? "Deletion-marker" : "File";
212
- const shouldIndent = isVersion && !isLatest;
213
- const isLegalHoldEnabled = Boolean(row.original.isLegalHoldEnabled);
214
- return /*#__PURE__*/ jsx(ConstrainedText, {
215
- text: /*#__PURE__*/ jsxs("div", {
216
- style: {
217
- display: "flex",
218
- alignItems: "center",
219
- gap: spacing.r8,
220
- paddingLeft: shouldIndent ? spacing.r24 : 0
221
- },
222
- children: [
223
- /*#__PURE__*/ jsx(Icon, {
224
- name: iconName,
225
- size: "sm",
226
- color: getVersionTextColor(row)
227
- }),
228
- isLegalHoldEnabled && /*#__PURE__*/ jsx(Icon, {
229
- name: "Rebalance",
230
- size: "sm",
231
- color: getVersionTextColor(row)
232
- }),
233
- isDeleteMarker ? /*#__PURE__*/ jsx(Text, {
234
- color: getVersionTextColor(row),
235
- children: displayValue
236
- }) : /*#__PURE__*/ jsx(Link, {
237
- onClick: (e)=>{
238
- e.stopPropagation();
239
- if (isFolder) onPrefixChange?.(row.original.Key || "");
240
- else onObjectSelect(row.original);
241
- },
242
- children: /*#__PURE__*/ jsx(Text, {
243
- color: getVersionTextColor(row),
244
- children: displayValue
245
- })
246
- })
247
- ]
248
- }),
249
- lineClamp: 2
250
- });
251
- },
252
- cellStyle: {
253
- flex: "2",
254
- width: "unset"
255
- }
256
- }
531
+ const defaultColumnsMap = {
532
+ name: nameColumn,
533
+ versionId: versionIdColumn,
534
+ lastModified: lastModifiedColumn,
535
+ size: sizeColumn,
536
+ storageClass: storageClassColumn
537
+ };
538
+ const customColumns = (extraObjectListColumns || []).map((config)=>buildCustomColumn(config));
539
+ const customColumnsMap = createOverrideMap(customColumns);
540
+ const getColumn = (id)=>customColumnsMap.get(id) || defaultColumnsMap[id];
541
+ const extraColumns = customColumns.filter((col)=>col.id && !(col.id in defaultColumnsMap));
542
+ const finalColumns = [
543
+ getColumn('name'),
544
+ ...showVersions ? [
545
+ getColumn('versionId')
546
+ ] : [],
547
+ ...extraColumns,
548
+ getColumn('lastModified'),
549
+ getColumn('size'),
550
+ getColumn('storageClass')
257
551
  ];
258
- if (showVersions) cols.push({
259
- Header: "Version ID",
260
- accessor: "VersionId",
261
- id: "versionId",
262
- Cell: ({ row })=>{
263
- const isFolder = "folder" === row.original.type;
264
- const isDeleteMarker = "deleteMarker" === row.original.type;
265
- const isLatest = row.original.IsLatest;
266
- if (isFolder || isDeleteMarker) return /*#__PURE__*/ jsx(Text, {
267
- children: "-"
268
- });
269
- const versionId = "version" === row.original.type ? row.original.VersionId || "-" : "-";
270
- const textColor = isLatest ? void 0 : "infoPrimary";
271
- return /*#__PURE__*/ jsx(ConstrainedText, {
272
- text: versionId,
273
- lineClamp: 1,
274
- color: textColor
275
- });
276
- },
277
- cellStyle: {
278
- flex: "1.5",
279
- textAlign: "left",
280
- paddingRight: spacing.r16,
281
- width: "unset",
282
- maxWidth: "8rem"
283
- }
552
+ return finalColumns;
553
+ }, [
554
+ showVersions,
555
+ extraObjectListColumns,
556
+ nameColumn,
557
+ versionIdColumn,
558
+ lastModifiedColumn,
559
+ sizeColumn,
560
+ storageClassColumn
561
+ ]);
562
+ const handleUploadSuccess = useCallback(()=>{
563
+ invalidateQueries({
564
+ queryKey: [
565
+ 'ListObjects'
566
+ ]
284
567
  });
285
- cols.push({
286
- Header: "Modified on",
287
- accessor: "LastModified",
288
- id: "lastModified",
289
- Cell: ({ value, row })=>{
290
- if ("folder" === row.original.type || !value) return /*#__PURE__*/ jsx(Text, {
291
- children: "-"
292
- });
293
- const dateValue = value;
294
- return /*#__PURE__*/ jsx(Text, {
295
- color: getVersionTextColor(row),
296
- children: /*#__PURE__*/ jsx(FormattedDateTime, {
297
- format: "date-time-second",
298
- value: new Date(dateValue)
299
- })
300
- });
301
- },
302
- cellStyle: {
303
- flex: "1",
304
- textAlign: "right",
305
- paddingRight: spacing.r16,
306
- width: "unset",
307
- minWidth: "10rem"
308
- }
309
- }, {
310
- Header: "Size",
311
- accessor: "Size",
312
- id: "size",
313
- Cell: ({ value, row })=>{
314
- if ("folder" === row.original.type || null == value) return /*#__PURE__*/ jsx(Text, {
315
- children: "-"
316
- });
317
- const sizeValue = value;
318
- return /*#__PURE__*/ jsx(Wrap, {
319
- style: {
320
- justifyContent: "flex-end"
321
- },
322
- children: /*#__PURE__*/ jsx(Text, {
323
- color: getVersionTextColor(row),
324
- children: /*#__PURE__*/ jsx(PrettyBytes, {
325
- bytes: sizeValue,
326
- decimals: 2
327
- })
328
- })
329
- });
568
+ invalidateQueries({
569
+ queryKey: [
570
+ 'ListObjectVersions'
571
+ ]
572
+ });
573
+ }, [
574
+ invalidateQueries
575
+ ]);
576
+ const handleFolderSuccess = useCallback(()=>{
577
+ invalidateQueries({
578
+ queryKey: [
579
+ 'ListObjects'
580
+ ]
581
+ });
582
+ invalidateQueries({
583
+ queryKey: [
584
+ 'ListObjectVersions'
585
+ ]
586
+ });
587
+ }, [
588
+ invalidateQueries
589
+ ]);
590
+ const handleUploadError = useCallback(()=>{}, []);
591
+ const handleFolderError = useCallback(()=>{}, []);
592
+ const renderUploadAction = useCallback(()=>/*#__PURE__*/ jsx(UploadButton, {
593
+ bucket: bucketName,
594
+ prefix: prefix,
595
+ onUploadSuccess: handleUploadSuccess,
596
+ onUploadError: handleUploadError
597
+ }), [
598
+ bucketName,
599
+ prefix,
600
+ handleUploadSuccess,
601
+ handleUploadError
602
+ ]);
603
+ const renderCreateFolderAction = useCallback(()=>/*#__PURE__*/ jsx(CreateFolderButton, {
604
+ bucket: bucketName,
605
+ prefix: prefix,
606
+ onFolderSuccess: handleFolderSuccess,
607
+ onFolderError: handleFolderError
608
+ }), [
609
+ bucketName,
610
+ prefix,
611
+ handleFolderSuccess,
612
+ handleFolderError
613
+ ]);
614
+ const renderDeleteAction = useCallback(()=>/*#__PURE__*/ jsx(DeleteObjectButton, {
615
+ objects: selectedObjects,
616
+ bucketName: bucketName
617
+ }), [
618
+ selectedObjects,
619
+ bucketName
620
+ ]);
621
+ const actions = useMemo(()=>{
622
+ const defaultActionsMap = {
623
+ upload: {
624
+ id: 'upload',
625
+ render: renderUploadAction
330
626
  },
331
- cellStyle: {
332
- flex: "1",
333
- textAlign: "right",
334
- paddingRight: spacing.r16,
335
- width: "unset"
336
- }
337
- }, {
338
- Header: "Storage Location",
339
- accessor: "StorageClass",
340
- id: "storageClass",
341
- Cell: ({ value, row })=>{
342
- if (!value) return /*#__PURE__*/ jsx(Text, {
343
- children: "-"
344
- });
345
- const storageClass = value;
346
- return /*#__PURE__*/ jsx(Wrap, {
347
- style: {
348
- justifyContent: "flex-end"
349
- },
350
- children: /*#__PURE__*/ jsx(Text, {
351
- color: getVersionTextColor(row),
352
- children: "STANDARD" === storageClass ? "default" : storageClass
353
- })
354
- });
627
+ createFolder: {
628
+ id: 'createFolder',
629
+ render: renderCreateFolderAction
355
630
  },
356
- cellStyle: {
357
- flex: "1",
358
- textAlign: "right",
359
- paddingRight: spacing.r16,
360
- width: "unset"
631
+ delete: {
632
+ id: 'delete',
633
+ render: renderDeleteAction
361
634
  }
362
- });
363
- return cols;
635
+ };
636
+ const customActions = (extraObjectListActions || []).map((config)=>({
637
+ id: config.id,
638
+ render: config.render
639
+ }));
640
+ const customActionsMap = createOverrideMap(customActions);
641
+ const getAction = (id)=>customActionsMap.get(id) || defaultActionsMap[id];
642
+ const extraActions = customActions.filter((action)=>!(action.id in defaultActionsMap));
643
+ return [
644
+ getAction('upload'),
645
+ getAction('createFolder'),
646
+ getAction('delete'),
647
+ ...extraActions
648
+ ];
364
649
  }, [
365
- onObjectSelect,
366
- onPrefixChange,
367
- showVersions
650
+ renderUploadAction,
651
+ renderCreateFolderAction,
652
+ renderDeleteAction,
653
+ extraObjectListActions
368
654
  ]);
369
655
  const handleReachBottom = useCallback(()=>{
370
656
  if (hasNextPage && !isFetchingNextPage) fetchNextPage();
@@ -373,18 +659,33 @@ const ObjectList = ({ bucketName, prefix, onObjectSelect, onPrefixChange })=>{
373
659
  isFetchingNextPage,
374
660
  fetchNextPage
375
661
  ]);
376
- const tableStatus = isLoading ? "loading" : error ? "error" : "success";
662
+ const handleMultiSelectionChanged = useCallback((rows)=>{
663
+ const objects = rows.map((row)=>row.original);
664
+ setSelectedObjects(objects);
665
+ }, []);
666
+ const handleSingleRowSelected = useCallback((row)=>{
667
+ handleObjectSelect(row.original);
668
+ }, [
669
+ handleObjectSelect
670
+ ]);
671
+ const handleToggleAll = useCallback((selected)=>{
672
+ selected ? setSelectedObjects(tableData) : setSelectedObjects([]);
673
+ }, [
674
+ tableData
675
+ ]);
676
+ const tableStatus = isLoading ? 'loading' : error ? 'error' : 'success';
677
+ const entityName = useMemo(()=>({
678
+ en: {
679
+ singular: 'object',
680
+ plural: 'objects'
681
+ }
682
+ }), []);
377
683
  return /*#__PURE__*/ jsxs(Table, {
378
684
  columns: columns,
379
685
  data: tableData,
380
686
  status: tableStatus,
381
687
  onBottom: handleReachBottom,
382
- entityName: {
383
- en: {
384
- singular: "object",
385
- plural: "objects"
386
- }
387
- },
688
+ entityName: entityName,
388
689
  children: [
389
690
  /*#__PURE__*/ jsxs(Wrap, {
390
691
  padding: spacing.r16,
@@ -392,7 +693,7 @@ const ObjectList = ({ bucketName, prefix, onObjectSelect, onPrefixChange })=>{
392
693
  isMetadataSearchEnabled ? /*#__PURE__*/ jsx(MetadataSearch, {
393
694
  isError: !!error
394
695
  }) : /*#__PURE__*/ jsx(Table.SearchWithQueryParams, {
395
- queryParams: "search"
696
+ queryParams: SEARCH_QUERY_PARAM
396
697
  }),
397
698
  /*#__PURE__*/ jsxs(Box, {
398
699
  display: "flex",
@@ -400,33 +701,20 @@ const ObjectList = ({ bucketName, prefix, onObjectSelect, onPrefixChange })=>{
400
701
  alignItems: "center",
401
702
  gap: spacing.r8,
402
703
  children: [
403
- /*#__PURE__*/ jsxs(Box, {
704
+ /*#__PURE__*/ jsx(Box, {
404
705
  display: "flex",
405
706
  alignItems: "center",
406
707
  gap: spacing.r8,
407
- children: [
408
- /*#__PURE__*/ jsx(UploadButton, {
409
- bucket: bucketName,
410
- prefix: prefix,
411
- onUploadSuccess: ()=>{},
412
- onUploadError: ()=>{}
413
- }),
414
- /*#__PURE__*/ jsx(CreateFolderButton, {
415
- bucket: bucketName,
416
- prefix: prefix,
417
- onFolderSuccess: ()=>{},
418
- onFolderError: ()=>{}
419
- }),
420
- /*#__PURE__*/ jsx(DeleteObjectButton, {
421
- objects: selectedObjects,
422
- bucketName: bucketName
423
- })
424
- ]
708
+ children: actions.map((action)=>/*#__PURE__*/ jsx(Fragment, {
709
+ children: action.render()
710
+ }, action.id))
425
711
  }),
426
712
  /*#__PURE__*/ jsx(Toggle, {
427
713
  toggle: showVersions,
428
714
  onChange: (e)=>setShowVersions(e.target.checked),
429
- label: "List Versions"
715
+ label: "List Versions",
716
+ "aria-label": showVersions ? 'Hide object versions' : 'Show object versions',
717
+ "aria-pressed": showVersions
430
718
  })
431
719
  ]
432
720
  })
@@ -434,20 +722,13 @@ const ObjectList = ({ bucketName, prefix, onObjectSelect, onPrefixChange })=>{
434
722
  }),
435
723
  /*#__PURE__*/ jsx(Table.MultiSelectableContent, {
436
724
  rowHeight: "h40",
437
- onMultiSelectionChanged: (rows)=>{
438
- const objects = rows.map((row)=>row.original);
439
- setSelectedObjects(objects);
440
- },
441
- onSingleRowSelected: (row)=>{
442
- onObjectSelect(row.original);
443
- },
444
- onToggleAll: (selected)=>{
445
- console.log("Toggle all", selected);
446
- },
725
+ onMultiSelectionChanged: handleMultiSelectionChanged,
726
+ onSingleRowSelected: handleSingleRowSelected,
727
+ onToggleAll: handleToggleAll,
447
728
  separationLineVariant: "backgroundLevel1",
448
729
  isLoadingMoreItems: isFetchingNextPage
449
730
  })
450
731
  ]
451
732
  });
452
733
  };
453
- export { ObjectList };
734
+ export { ObjectList, isObjectLike };