@aws-amplify/ui-react-storage 3.13.1 → 3.15.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.
- package/dist/browser.js +9 -3
- package/dist/{createStorageBrowser-DaVWyJzC.js → createStorageBrowser-CG-6mXiT.js} +487 -146
- package/dist/esm/browser.mjs +2 -0
- package/dist/esm/components/StorageBrowser/ErrorBoundary/ErrorBoundary.mjs +2 -0
- package/dist/esm/components/StorageBrowser/StorageBrowserAmplify.mjs +2 -0
- package/dist/esm/components/StorageBrowser/actions/configs/defaults.mjs +2 -0
- package/dist/esm/components/StorageBrowser/actions/handlers/defaults.mjs +1 -1
- package/dist/esm/components/StorageBrowser/actions/handlers/listLocationItems.mjs +4 -2
- package/dist/esm/components/StorageBrowser/actions/handlers/listLocations.mjs +7 -2
- package/dist/esm/components/StorageBrowser/actions/handlers/utils.mjs +87 -2
- package/dist/esm/components/StorageBrowser/actions/handlers/zipdownload.mjs +195 -0
- package/dist/esm/components/StorageBrowser/adapters/createAmplifyAuthAdapter/createAmplifyListLocationsHandler.mjs +3 -1
- package/dist/esm/components/StorageBrowser/adapters/createManagedAuthAdapter/createManagedAuthAdapter.mjs +2 -0
- package/dist/esm/components/StorageBrowser/components/ComponentsProvider.mjs +2 -0
- package/dist/esm/components/StorageBrowser/components/base/preview/DownloadButton.mjs +2 -0
- package/dist/esm/components/StorageBrowser/controls/DataTableControl.mjs +2 -0
- package/dist/esm/components/StorageBrowser/createStorageBrowser/StorageBrowserDefault.mjs +2 -0
- package/dist/esm/components/StorageBrowser/createStorageBrowser/createProvider.mjs +2 -0
- package/dist/esm/components/StorageBrowser/createStorageBrowser/createStorageBrowser.mjs +2 -0
- package/dist/esm/components/StorageBrowser/displayText/libraries/en/downloadView.mjs +3 -0
- package/dist/esm/components/StorageBrowser/displayText/libraries/en/shared.mjs +2 -0
- package/dist/esm/components/StorageBrowser/locationItems/context.mjs +1 -0
- package/dist/esm/components/StorageBrowser/tasks/constants.mjs +2 -0
- package/dist/esm/components/StorageBrowser/tasks/useProcessTasks.mjs +10 -4
- package/dist/esm/components/StorageBrowser/tasks/utils.mjs +4 -1
- package/dist/esm/components/StorageBrowser/useAction/useHandler.mjs +1 -1
- package/dist/esm/components/StorageBrowser/useAction/utils.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/CopyView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/CopyViewProvider.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/FoldersMessageControl.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/useCopyView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/CopyView/useFolders.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/CreateFolderView/CreateFolderView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/CreateFolderView/useCreateFolderView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/DeleteView/DeleteView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/DeleteView/useDeleteView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/DownloadView/DownloadView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/DownloadView/DownloadViewProvider.mjs +2 -3
- package/dist/esm/components/StorageBrowser/views/LocationActionView/DownloadView/useDownloadView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/UploadView/UploadView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/UploadView/UploadViewProvider.mjs +1 -0
- package/dist/esm/components/StorageBrowser/views/LocationActionView/UploadView/useUploadView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationDetailView/LocationDetailView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationDetailView/getLocationDetailViewTableData/getLocationDetailViewTableData.mjs +1 -0
- package/dist/esm/components/StorageBrowser/views/LocationDetailView/useLocationDetailView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationsView/LocationsView.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationsView/LocationsViewProvider.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/LocationsView/useLocationsView.mjs +1 -0
- package/dist/esm/components/StorageBrowser/views/context/actionViews.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/context/primaryViews.mjs +2 -0
- package/dist/esm/components/StorageBrowser/views/hooks/useFilePreview/useFilePreview.mjs +1 -0
- package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/constants.mjs +4 -0
- package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/downloadResolvers.mjs +72 -4
- package/dist/esm/components/StorageBrowser/views/utils/tableResolvers/utils.mjs +7 -1
- package/dist/esm/version.mjs +1 -1
- package/dist/index.js +2 -1
- package/dist/styles.css +17 -0
- package/dist/types/components/StorageBrowser/actions/handlers/index.d.ts +1 -0
- package/dist/types/components/StorageBrowser/actions/handlers/types.d.ts +4 -2
- package/dist/types/components/StorageBrowser/actions/handlers/utils.d.ts +13 -1
- package/dist/types/components/StorageBrowser/actions/handlers/zipdownload.d.ts +3 -0
- package/dist/types/components/StorageBrowser/displayText/types.d.ts +4 -1
- package/dist/types/components/StorageBrowser/tasks/types.d.ts +3 -3
- package/dist/types/components/StorageBrowser/tasks/useProcessTasks.d.ts +1 -1
- package/dist/types/components/StorageBrowser/useAction/useHandler.d.ts +1 -1
- package/dist/types/components/StorageBrowser/views/utils/index.d.ts +1 -1
- package/dist/types/components/StorageBrowser/views/utils/tableResolvers/constants.d.ts +4 -0
- package/dist/types/components/StorageBrowser/views/utils/tableResolvers/downloadResolvers.d.ts +3 -2
- package/dist/types/components/StorageBrowser/views/utils/tableResolvers/index.d.ts +1 -1
- package/dist/types/components/StorageBrowser/views/utils/tableResolvers/types.d.ts +4 -0
- package/dist/types/components/StorageBrowser/views/utils/tableResolvers/utils.d.ts +2 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +11 -8
- package/dist/esm/components/StorageBrowser/actions/handlers/download.mjs +0 -37
|
@@ -10,8 +10,11 @@ var uiReact = require('@aws-amplify/ui-react');
|
|
|
10
10
|
var elements = require('@aws-amplify/ui-react-core/elements');
|
|
11
11
|
var internal = require('@aws-amplify/ui-react/internal');
|
|
12
12
|
var uiReactCore = require('@aws-amplify/ui-react-core');
|
|
13
|
+
var JSZip = require('jszip');
|
|
13
14
|
var storage = require('aws-amplify/storage');
|
|
14
15
|
|
|
16
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
|
+
|
|
15
18
|
function _interopNamespace(e) {
|
|
16
19
|
if (e && e.__esModule) return e;
|
|
17
20
|
var n = Object.create(null);
|
|
@@ -31,8 +34,185 @@ function _interopNamespace(e) {
|
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
37
|
+
var JSZip__default = /*#__PURE__*/_interopDefault(JSZip);
|
|
38
|
+
|
|
39
|
+
const VERSION = '3.15.0';
|
|
34
40
|
|
|
35
|
-
const
|
|
41
|
+
const DEFAULT_CHECKSUM_ALGORITHM = 'crc-32';
|
|
42
|
+
// 5MiB for multipart upload
|
|
43
|
+
// https://github.com/aws-amplify/amplify-js/blob/1a5366d113c9af4ce994168653df3aadb142c581/packages/storage/src/providers/s3/utils/constants.ts#L16
|
|
44
|
+
const MULTIPART_UPLOAD_THRESHOLD_BYTES = 5 * 1024 * 1024;
|
|
45
|
+
|
|
46
|
+
const getBucketRegion = (bucketName, fallbackRegion) => {
|
|
47
|
+
try {
|
|
48
|
+
const config = awsAmplify.Amplify.getConfig()?.Storage?.S3;
|
|
49
|
+
if (!config?.buckets || typeof config.buckets !== 'object') {
|
|
50
|
+
return fallbackRegion;
|
|
51
|
+
}
|
|
52
|
+
for (const bucketConfig of Object.values(config.buckets)) {
|
|
53
|
+
if (bucketConfig.bucketName === bucketName && bucketConfig.region) {
|
|
54
|
+
return bucketConfig.region;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return fallbackRegion;
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
return fallbackRegion;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const constructBucket$1 = ({ bucket: bucketName, region: globalRegion, }) => {
|
|
64
|
+
const bucketRegion = getBucketRegion(bucketName, globalRegion);
|
|
65
|
+
return { bucketName, region: bucketRegion };
|
|
66
|
+
};
|
|
67
|
+
const parseAccessGrantLocation = (location) => {
|
|
68
|
+
const { permission, scope, type } = location;
|
|
69
|
+
if (!scope.startsWith('s3://')) {
|
|
70
|
+
throw new Error(`Invalid scope: ${scope}`);
|
|
71
|
+
}
|
|
72
|
+
const id = crypto.randomUUID();
|
|
73
|
+
// remove default path
|
|
74
|
+
const slicedScope = scope.slice(5);
|
|
75
|
+
let bucket, prefix;
|
|
76
|
+
switch (type) {
|
|
77
|
+
case 'BUCKET': {
|
|
78
|
+
// { scope: 's3://bucket/*', type: 'BUCKET', },
|
|
79
|
+
bucket = slicedScope.slice(0, -2);
|
|
80
|
+
prefix = '';
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case 'PREFIX': {
|
|
84
|
+
// { scope: 's3://bucket/path/*', type: 'PREFIX', },
|
|
85
|
+
bucket = slicedScope.slice(0, slicedScope.indexOf('/'));
|
|
86
|
+
prefix = `${slicedScope.slice(bucket.length + 1, -1)}`;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
case 'OBJECT': {
|
|
90
|
+
// { scope: 's3://bucket/path/to/object', type: 'OBJECT', },
|
|
91
|
+
bucket = slicedScope.slice(0, slicedScope.indexOf('/'));
|
|
92
|
+
prefix = slicedScope.slice(bucket.length + 1);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
default: {
|
|
96
|
+
throw new Error(`Invalid location type: ${type}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
let permissions;
|
|
100
|
+
switch (permission) {
|
|
101
|
+
case 'READ':
|
|
102
|
+
permissions = ['get', 'list'];
|
|
103
|
+
break;
|
|
104
|
+
case 'READWRITE':
|
|
105
|
+
permissions = ['delete', 'get', 'list', 'write'];
|
|
106
|
+
break;
|
|
107
|
+
case 'WRITE':
|
|
108
|
+
permissions = ['delete', 'write'];
|
|
109
|
+
break;
|
|
110
|
+
default:
|
|
111
|
+
throw new Error(`Invalid location permission: ${permission}`);
|
|
112
|
+
}
|
|
113
|
+
return { bucket, id, permissions: permissions, prefix, type };
|
|
114
|
+
};
|
|
115
|
+
const isSamePermissions = (permissionsToExclude, locationPermissions) => {
|
|
116
|
+
if (permissionsToExclude.length !== locationPermissions.length) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
const sortedLocationPermissions = locationPermissions.sort();
|
|
120
|
+
return permissionsToExclude
|
|
121
|
+
.sort()
|
|
122
|
+
.every((permission, index) => permission === sortedLocationPermissions[index]);
|
|
123
|
+
};
|
|
124
|
+
const isSameType = (typeToExclude, locationType) => typeof typeToExclude === 'string'
|
|
125
|
+
? typeToExclude === locationType
|
|
126
|
+
: typeToExclude.includes(locationType);
|
|
127
|
+
const shouldExcludeLocation = ({ permissions, type }, exclude) => {
|
|
128
|
+
const excludedByPermssions = !!(exclude?.exactPermissions &&
|
|
129
|
+
isSamePermissions(exclude.exactPermissions, permissions));
|
|
130
|
+
const excludedByType = !!(exclude?.type && isSameType(exclude.type, type));
|
|
131
|
+
return excludedByPermssions || excludedByType;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Determines if permissions1 is a strict superset (broader) of permissions2.
|
|
135
|
+
* Returns true only if permissions1 contains all permissions from permissions2
|
|
136
|
+
* AND has more permissions.
|
|
137
|
+
*/
|
|
138
|
+
const hasBroaderPermissions = (permissions1, permissions2) => {
|
|
139
|
+
// permissions1 must have more permissions (strict superset, not equal)
|
|
140
|
+
if (permissions1.length <= permissions2.length) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
// permissions1 must contain all permissions from permissions2
|
|
144
|
+
return permissions2.every((perm) => permissions1.includes(perm));
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Deduplicates locations with the same bucket and prefix.
|
|
148
|
+
* Only deduplicates when one location's permissions are a superset of another's.
|
|
149
|
+
* This prevents deduplication of incompatible grants like READ + WRITE.
|
|
150
|
+
*
|
|
151
|
+
* Examples:
|
|
152
|
+
* - READ + READWRITE → Keep READWRITE (superset)
|
|
153
|
+
* - READ + READ → Keep first (identical)
|
|
154
|
+
* - READ + WRITE → Keep both (not superset, need separate locations)
|
|
155
|
+
*/
|
|
156
|
+
const deduplicateLocations = (locations) => {
|
|
157
|
+
// Group locations by bucket:prefix
|
|
158
|
+
const locationGroups = new Map();
|
|
159
|
+
for (const location of locations) {
|
|
160
|
+
const key = `${location.bucket}:${location.prefix}`;
|
|
161
|
+
const group = locationGroups.get(key) ?? [];
|
|
162
|
+
group.push(location);
|
|
163
|
+
locationGroups.set(key, group);
|
|
164
|
+
}
|
|
165
|
+
// For each group, keep only non-redundant locations
|
|
166
|
+
const result = [];
|
|
167
|
+
for (const group of locationGroups.values()) {
|
|
168
|
+
if (group.length === 1) {
|
|
169
|
+
result.push(group[0]);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
// Find locations that are not subsets of any other location in the group
|
|
173
|
+
const nonRedundant = [];
|
|
174
|
+
for (const location of group) {
|
|
175
|
+
// Check if this location is a subset of any other location
|
|
176
|
+
const isSubsetOfAnother = group.some((other) => {
|
|
177
|
+
if (other === location)
|
|
178
|
+
return false;
|
|
179
|
+
return hasBroaderPermissions(other.permissions, location.permissions);
|
|
180
|
+
});
|
|
181
|
+
if (!isSubsetOfAnother) {
|
|
182
|
+
// Check if we already have an identical permission set
|
|
183
|
+
const isDuplicate = nonRedundant.some((existing) => {
|
|
184
|
+
const sortedNew = [...location.permissions].sort().join(',');
|
|
185
|
+
const sortedExisting = [...existing.permissions].sort().join(',');
|
|
186
|
+
return sortedNew === sortedExisting;
|
|
187
|
+
});
|
|
188
|
+
if (!isDuplicate) {
|
|
189
|
+
nonRedundant.push(location);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
result.push(...nonRedundant);
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
};
|
|
197
|
+
const getFilteredLocations = (locations, exclude) => locations.reduce((filteredLocations, location) => {
|
|
198
|
+
const parsedLocation = parseAccessGrantLocation(location);
|
|
199
|
+
const isNonFolderLikePrefix = !parsedLocation.prefix.endsWith('/') &&
|
|
200
|
+
parsedLocation.type === 'PREFIX';
|
|
201
|
+
if (isNonFolderLikePrefix) {
|
|
202
|
+
return filteredLocations;
|
|
203
|
+
}
|
|
204
|
+
if (!shouldExcludeLocation(parsedLocation, exclude)) {
|
|
205
|
+
filteredLocations.push(parsedLocation);
|
|
206
|
+
}
|
|
207
|
+
return filteredLocations;
|
|
208
|
+
}, []);
|
|
209
|
+
const getFileKey = (key) => key.slice(key.lastIndexOf('/') + 1, key.length);
|
|
210
|
+
const createFileDataItem = (data) => ({
|
|
211
|
+
...data,
|
|
212
|
+
fileKey: getFileKey(data.key),
|
|
213
|
+
});
|
|
214
|
+
const getProgress = ({ totalBytes, transferredBytes, }) => totalBytes ? transferredBytes / totalBytes : undefined;
|
|
215
|
+
const isMultipartUpload = (file) => file.size > MULTIPART_UPLOAD_THRESHOLD_BYTES;
|
|
36
216
|
|
|
37
217
|
const toAccessGrantPermission = (permission) => {
|
|
38
218
|
let result = '';
|
|
@@ -118,7 +298,8 @@ const createAmplifyListLocationsHandler = () => {
|
|
|
118
298
|
id: crypto.randomUUID(),
|
|
119
299
|
};
|
|
120
300
|
});
|
|
121
|
-
|
|
301
|
+
// Deduplicate locations with the same bucket and prefix, keeping broader permissions
|
|
302
|
+
cachedItems = deduplicateLocations(sanitizedItems);
|
|
122
303
|
return getPaginatedLocations({
|
|
123
304
|
items: cachedItems,
|
|
124
305
|
pageSize,
|
|
@@ -163,103 +344,11 @@ const createAmplifyAuthAdapter = () => {
|
|
|
163
344
|
};
|
|
164
345
|
};
|
|
165
346
|
|
|
166
|
-
const DEFAULT_CHECKSUM_ALGORITHM = 'crc-32';
|
|
167
|
-
// 5MiB for multipart upload
|
|
168
|
-
// https://github.com/aws-amplify/amplify-js/blob/1a5366d113c9af4ce994168653df3aadb142c581/packages/storage/src/providers/s3/utils/constants.ts#L16
|
|
169
|
-
const MULTIPART_UPLOAD_THRESHOLD_BYTES = 5 * 1024 * 1024;
|
|
170
|
-
|
|
171
|
-
const constructBucket = ({ bucket: bucketName, region, }) => ({ bucketName, region });
|
|
172
|
-
const parseAccessGrantLocation = (location) => {
|
|
173
|
-
const { permission, scope, type } = location;
|
|
174
|
-
if (!scope.startsWith('s3://')) {
|
|
175
|
-
throw new Error(`Invalid scope: ${scope}`);
|
|
176
|
-
}
|
|
177
|
-
const id = crypto.randomUUID();
|
|
178
|
-
// remove default path
|
|
179
|
-
const slicedScope = scope.slice(5);
|
|
180
|
-
let bucket, prefix;
|
|
181
|
-
switch (type) {
|
|
182
|
-
case 'BUCKET': {
|
|
183
|
-
// { scope: 's3://bucket/*', type: 'BUCKET', },
|
|
184
|
-
bucket = slicedScope.slice(0, -2);
|
|
185
|
-
prefix = '';
|
|
186
|
-
break;
|
|
187
|
-
}
|
|
188
|
-
case 'PREFIX': {
|
|
189
|
-
// { scope: 's3://bucket/path/*', type: 'PREFIX', },
|
|
190
|
-
bucket = slicedScope.slice(0, slicedScope.indexOf('/'));
|
|
191
|
-
prefix = `${slicedScope.slice(bucket.length + 1, -1)}`;
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
case 'OBJECT': {
|
|
195
|
-
// { scope: 's3://bucket/path/to/object', type: 'OBJECT', },
|
|
196
|
-
bucket = slicedScope.slice(0, slicedScope.indexOf('/'));
|
|
197
|
-
prefix = slicedScope.slice(bucket.length + 1);
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
200
|
-
default: {
|
|
201
|
-
throw new Error(`Invalid location type: ${type}`);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
let permissions;
|
|
205
|
-
switch (permission) {
|
|
206
|
-
case 'READ':
|
|
207
|
-
permissions = ['get', 'list'];
|
|
208
|
-
break;
|
|
209
|
-
case 'READWRITE':
|
|
210
|
-
permissions = ['delete', 'get', 'list', 'write'];
|
|
211
|
-
break;
|
|
212
|
-
case 'WRITE':
|
|
213
|
-
permissions = ['delete', 'write'];
|
|
214
|
-
break;
|
|
215
|
-
default:
|
|
216
|
-
throw new Error(`Invalid location permission: ${permission}`);
|
|
217
|
-
}
|
|
218
|
-
return { bucket, id, permissions: permissions, prefix, type };
|
|
219
|
-
};
|
|
220
|
-
const isSamePermissions = (permissionsToExclude, locationPermissions) => {
|
|
221
|
-
if (permissionsToExclude.length !== locationPermissions.length) {
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
const sortedLocationPermissions = locationPermissions.sort();
|
|
225
|
-
return permissionsToExclude
|
|
226
|
-
.sort()
|
|
227
|
-
.every((permission, index) => permission === sortedLocationPermissions[index]);
|
|
228
|
-
};
|
|
229
|
-
const isSameType = (typeToExclude, locationType) => typeof typeToExclude === 'string'
|
|
230
|
-
? typeToExclude === locationType
|
|
231
|
-
: typeToExclude.includes(locationType);
|
|
232
|
-
const shouldExcludeLocation = ({ permissions, type }, exclude) => {
|
|
233
|
-
const excludedByPermssions = !!(exclude?.exactPermissions &&
|
|
234
|
-
isSamePermissions(exclude.exactPermissions, permissions));
|
|
235
|
-
const excludedByType = !!(exclude?.type && isSameType(exclude.type, type));
|
|
236
|
-
return excludedByPermssions || excludedByType;
|
|
237
|
-
};
|
|
238
|
-
const getFilteredLocations = (locations, exclude) => locations.reduce((filteredLocations, location) => {
|
|
239
|
-
const parsedLocation = parseAccessGrantLocation(location);
|
|
240
|
-
const isNonFolderLikePrefix = !parsedLocation.prefix.endsWith('/') &&
|
|
241
|
-
parsedLocation.type === 'PREFIX';
|
|
242
|
-
if (isNonFolderLikePrefix) {
|
|
243
|
-
return filteredLocations;
|
|
244
|
-
}
|
|
245
|
-
if (!shouldExcludeLocation(parsedLocation, exclude)) {
|
|
246
|
-
filteredLocations.push(parsedLocation);
|
|
247
|
-
}
|
|
248
|
-
return filteredLocations;
|
|
249
|
-
}, []);
|
|
250
|
-
const getFileKey = (key) => key.slice(key.lastIndexOf('/') + 1, key.length);
|
|
251
|
-
const createFileDataItem = (data) => ({
|
|
252
|
-
...data,
|
|
253
|
-
fileKey: getFileKey(data.key),
|
|
254
|
-
});
|
|
255
|
-
const getProgress = ({ totalBytes, transferredBytes, }) => totalBytes ? transferredBytes / totalBytes : undefined;
|
|
256
|
-
const isMultipartUpload = (file) => file.size > MULTIPART_UPLOAD_THRESHOLD_BYTES;
|
|
257
|
-
|
|
258
347
|
const copyHandler = (input) => {
|
|
259
348
|
const { config, data } = input;
|
|
260
349
|
const { accountId: expectedBucketOwner, credentials, customEndpoint, } = config;
|
|
261
350
|
const { key, sourceKey, lastModified, eTag } = data;
|
|
262
|
-
const bucket = constructBucket(config);
|
|
351
|
+
const bucket = constructBucket$1(config);
|
|
263
352
|
const source = {
|
|
264
353
|
bucket,
|
|
265
354
|
expectedBucketOwner,
|
|
@@ -300,7 +389,7 @@ const createFolderHandler = (input) => {
|
|
|
300
389
|
const { accountId, credentials, customEndpoint } = config;
|
|
301
390
|
const { onProgress } = options ?? {};
|
|
302
391
|
const { key, preventOverwrite } = data;
|
|
303
|
-
const bucket = constructBucket(config);
|
|
392
|
+
const bucket = constructBucket$1(config);
|
|
304
393
|
const { result } = internals.uploadData({
|
|
305
394
|
path: key,
|
|
306
395
|
data: '',
|
|
@@ -339,7 +428,7 @@ const deleteHandler = ({ config, data, }) => {
|
|
|
339
428
|
const result = internals.remove({
|
|
340
429
|
path: key,
|
|
341
430
|
options: {
|
|
342
|
-
bucket: constructBucket(config),
|
|
431
|
+
bucket: constructBucket$1(config),
|
|
343
432
|
locationCredentialsProvider: credentials,
|
|
344
433
|
expectedBucketOwner: accountId,
|
|
345
434
|
customEndpoint,
|
|
@@ -356,18 +445,115 @@ const deleteHandler = ({ config, data, }) => {
|
|
|
356
445
|
return { result };
|
|
357
446
|
};
|
|
358
447
|
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
448
|
+
const zipProgressManager = ({ dataMap, onZipProgress, }) => {
|
|
449
|
+
const iter = (() => {
|
|
450
|
+
let f;
|
|
451
|
+
let i = 0;
|
|
452
|
+
return (str) => {
|
|
453
|
+
if (!f) {
|
|
454
|
+
f = str;
|
|
455
|
+
}
|
|
456
|
+
else if (str !== f) {
|
|
457
|
+
++i;
|
|
458
|
+
f = str;
|
|
459
|
+
}
|
|
460
|
+
return i;
|
|
461
|
+
};
|
|
462
|
+
})();
|
|
463
|
+
const progressMap = new Map(Array.from(dataMap.keys()).map((k) => [k, 0]));
|
|
464
|
+
const total = dataMap.size;
|
|
465
|
+
dataMap.forEach((data, key) => {
|
|
466
|
+
onZipProgress(key, 0, data);
|
|
467
|
+
});
|
|
468
|
+
return ({ percent, currentFile, }) => {
|
|
469
|
+
if (currentFile) {
|
|
470
|
+
const item = iter(currentFile);
|
|
471
|
+
const sliceSize = 100 / total; // when 3 this is 33.3%
|
|
472
|
+
const start = sliceSize * item; // this is 66.6% for last item of 3;
|
|
473
|
+
// take percent and calculate the percent of the slice
|
|
474
|
+
const progress = percent - start;
|
|
475
|
+
const actualPercent = (progress / sliceSize) * 100;
|
|
476
|
+
progressMap.set(currentFile, Math.max(actualPercent, progressMap.get(currentFile) ?? 0));
|
|
477
|
+
onZipProgress(currentFile, progressMap.get(currentFile), dataMap.get(currentFile));
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
};
|
|
481
|
+
const zipper = (() => {
|
|
482
|
+
let zip = null;
|
|
483
|
+
const dataMap = new Map();
|
|
484
|
+
return {
|
|
485
|
+
addFile: (file, name, data) => {
|
|
486
|
+
if (!zip) {
|
|
487
|
+
zip = new JSZip__default["default"]();
|
|
488
|
+
}
|
|
489
|
+
dataMap.set(name, data);
|
|
490
|
+
return new Promise((ok, no) => {
|
|
491
|
+
try {
|
|
492
|
+
zip?.file(name, file);
|
|
493
|
+
ok();
|
|
494
|
+
}
|
|
495
|
+
catch (e) {
|
|
496
|
+
no();
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
},
|
|
500
|
+
getBlobUrl: async (onProgress) => {
|
|
501
|
+
if (!zip) {
|
|
502
|
+
throw new Error('no zip');
|
|
503
|
+
}
|
|
504
|
+
const blob = await zip.generateAsync({
|
|
505
|
+
type: 'blob',
|
|
506
|
+
streamFiles: true,
|
|
507
|
+
compression: 'DEFLATE',
|
|
508
|
+
compressionOptions: {
|
|
509
|
+
level: 5,
|
|
510
|
+
},
|
|
511
|
+
}, zipProgressManager({
|
|
512
|
+
dataMap,
|
|
513
|
+
onZipProgress: (file, progress, data) => {
|
|
514
|
+
if (ui.isFunction(onProgress)) {
|
|
515
|
+
onProgress(progress, file, data);
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
}));
|
|
519
|
+
zip = null;
|
|
520
|
+
return URL.createObjectURL(blob);
|
|
521
|
+
},
|
|
522
|
+
destroy: () => {
|
|
523
|
+
dataMap.clear();
|
|
524
|
+
zip = null;
|
|
525
|
+
},
|
|
526
|
+
};
|
|
527
|
+
})();
|
|
528
|
+
const constructBucket = ({ bucket: bucketName, region, }) => ({ bucketName, region });
|
|
529
|
+
const readBody = async (response, { data, options }) => {
|
|
530
|
+
let loading = true;
|
|
531
|
+
const chunks = [];
|
|
532
|
+
const reader = response.body.getReader();
|
|
533
|
+
const size = +(response.headers.get('content-length') ?? 0);
|
|
534
|
+
let received = 0;
|
|
535
|
+
while (loading) {
|
|
536
|
+
const { value, done } = await reader.read();
|
|
537
|
+
if (done) {
|
|
538
|
+
loading = false;
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
chunks.push(value);
|
|
542
|
+
received += value.length;
|
|
543
|
+
if (ui.isFunction(options?.onProgress)) {
|
|
544
|
+
options?.onProgress(data, getProgress({
|
|
545
|
+
totalBytes: size,
|
|
546
|
+
transferredBytes: received,
|
|
547
|
+
}), 'PENDING');
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return new Blob(chunks);
|
|
552
|
+
};
|
|
553
|
+
const download = async ({ config, data, all, options }, abortController) => {
|
|
554
|
+
const { customEndpoint, credentials, accountId } = config;
|
|
555
|
+
const { key } = data;
|
|
556
|
+
const { url } = await internals.getUrl({
|
|
371
557
|
path: key,
|
|
372
558
|
options: {
|
|
373
559
|
bucket: constructBucket(config),
|
|
@@ -377,17 +563,76 @@ const downloadHandler = ({ config, data: { key }, }) => {
|
|
|
377
563
|
contentDisposition: 'attachment',
|
|
378
564
|
expectedBucketOwner: accountId,
|
|
379
565
|
},
|
|
380
|
-
})
|
|
381
|
-
.then(({ url }) => {
|
|
382
|
-
downloadFromUrl(key, url.toString());
|
|
383
|
-
return { status: 'COMPLETE', value: { url } };
|
|
384
|
-
})
|
|
385
|
-
.catch((error) => {
|
|
386
|
-
const { message } = error;
|
|
387
|
-
return { error, message, status: 'FAILED' };
|
|
388
566
|
});
|
|
389
|
-
|
|
390
|
-
|
|
567
|
+
const response = await fetch(url, {
|
|
568
|
+
mode: 'cors',
|
|
569
|
+
signal: abortController.signal,
|
|
570
|
+
});
|
|
571
|
+
const blob = await readBody(response, { data, options });
|
|
572
|
+
const [filename] = key.split('/').reverse();
|
|
573
|
+
await zipper.addFile(blob, filename, data);
|
|
574
|
+
return filename;
|
|
575
|
+
};
|
|
576
|
+
const downloadHandler = (() => {
|
|
577
|
+
const fileDownloadQueue = new Map();
|
|
578
|
+
const handler = ({ config, data, all, options }) => {
|
|
579
|
+
const { key } = data;
|
|
580
|
+
const [, folder] = key.split('/').reverse();
|
|
581
|
+
fileDownloadQueue.set(key, false);
|
|
582
|
+
const abortController = new AbortController();
|
|
583
|
+
return {
|
|
584
|
+
cancel: () => {
|
|
585
|
+
abortController.abort();
|
|
586
|
+
fileDownloadQueue.set(key, true);
|
|
587
|
+
},
|
|
588
|
+
result: download({ config, data, all, options }, abortController)
|
|
589
|
+
.then(() => {
|
|
590
|
+
fileDownloadQueue.set(key, true);
|
|
591
|
+
return {
|
|
592
|
+
status: 'LOADED',
|
|
593
|
+
};
|
|
594
|
+
})
|
|
595
|
+
.catch((e) => {
|
|
596
|
+
const error = e;
|
|
597
|
+
fileDownloadQueue.set(key, true);
|
|
598
|
+
return {
|
|
599
|
+
status: 'FAILED',
|
|
600
|
+
message: error.message,
|
|
601
|
+
error,
|
|
602
|
+
};
|
|
603
|
+
})
|
|
604
|
+
.finally(() => {
|
|
605
|
+
const done = all.every(({ key }) => {
|
|
606
|
+
return fileDownloadQueue.get(key);
|
|
607
|
+
});
|
|
608
|
+
if (done) {
|
|
609
|
+
zipper
|
|
610
|
+
.getBlobUrl((percent, name, _data) => {
|
|
611
|
+
if (ui.isFunction(options?.onProgress)) {
|
|
612
|
+
const progress = percent / 100;
|
|
613
|
+
options?.onProgress(_data, progress, progress === 1 ? 'COMPLETE' : 'FINISHING');
|
|
614
|
+
}
|
|
615
|
+
})
|
|
616
|
+
.then((blobURL) => {
|
|
617
|
+
if (blobURL) {
|
|
618
|
+
zipper.destroy();
|
|
619
|
+
const anchor = document.createElement('a');
|
|
620
|
+
const clickEvent = new MouseEvent('click');
|
|
621
|
+
anchor.href = blobURL;
|
|
622
|
+
anchor.download = `${folder || 'archive'}.zip`;
|
|
623
|
+
anchor.dispatchEvent(clickEvent);
|
|
624
|
+
}
|
|
625
|
+
})
|
|
626
|
+
.catch(() => {
|
|
627
|
+
// this catch happens, when no zip was created.
|
|
628
|
+
// it is handled by the UI showing "FAILED" for all files
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
}),
|
|
632
|
+
};
|
|
633
|
+
};
|
|
634
|
+
return handler;
|
|
635
|
+
})();
|
|
391
636
|
|
|
392
637
|
const DEFAULT_PAGE_SIZE$5 = 1000;
|
|
393
638
|
const parseItems = (items, excludedPath) => items
|
|
@@ -421,9 +666,10 @@ const filterDotItems = (items, prefix) => items.filter((item) => {
|
|
|
421
666
|
const parseResult = ({ excludedSubpaths, items }, prefix) => filterDotItems([...parseExcludedPaths(excludedSubpaths), ...parseItems(items, prefix)], prefix);
|
|
422
667
|
const listLocationItemsHandler = async (input) => {
|
|
423
668
|
const { config, prefix, options } = input;
|
|
424
|
-
const { bucket: _bucket, credentials, customEndpoint, region, accountId, } = config;
|
|
669
|
+
const { bucket: _bucket, credentials, customEndpoint, region: globalRegion, accountId, } = config;
|
|
425
670
|
const { exclude, delimiter, nextToken, pageSize: _pageSize = DEFAULT_PAGE_SIZE$5, ..._options } = options ?? {};
|
|
426
|
-
const
|
|
671
|
+
const bucketRegion = getBucketRegion(_bucket, globalRegion);
|
|
672
|
+
const bucket = { bucketName: _bucket, region: bucketRegion };
|
|
427
673
|
const subpathStrategy = {
|
|
428
674
|
delimiter,
|
|
429
675
|
strategy: delimiter ? 'exclude' : 'include',
|
|
@@ -472,7 +718,7 @@ const uploadHandler = ({ config, data, options }) => {
|
|
|
472
718
|
path: key,
|
|
473
719
|
data: file,
|
|
474
720
|
options: {
|
|
475
|
-
bucket: constructBucket(config),
|
|
721
|
+
bucket: constructBucket$1(config),
|
|
476
722
|
expectedBucketOwner: accountId,
|
|
477
723
|
locationCredentialsProvider: credentials,
|
|
478
724
|
onProgress: (event) => {
|
|
@@ -1697,8 +1943,10 @@ const USE_LIST_ERROR_MESSAGE = '`useList` must be called from within `StorageBro
|
|
|
1697
1943
|
const INITIAL_STATUS_COUNTS = {
|
|
1698
1944
|
CANCELED: 0,
|
|
1699
1945
|
COMPLETE: 0,
|
|
1946
|
+
LOADED: 0,
|
|
1700
1947
|
FAILED: 0,
|
|
1701
1948
|
PENDING: 0,
|
|
1949
|
+
FINISHING: 0,
|
|
1702
1950
|
OVERWRITE_PREVENTED: 0,
|
|
1703
1951
|
QUEUED: 0,
|
|
1704
1952
|
TOTAL: 0,
|
|
@@ -1709,7 +1957,10 @@ const isProcessingTasks = (statusCounts) => {
|
|
|
1709
1957
|
if (statusCounts.TOTAL === 0 || statusCounts.TOTAL === statusCounts.QUEUED) {
|
|
1710
1958
|
return false;
|
|
1711
1959
|
}
|
|
1712
|
-
return !(statusCounts.QUEUED === 0 &&
|
|
1960
|
+
return !(statusCounts.QUEUED === 0 &&
|
|
1961
|
+
statusCounts.PENDING === 0 &&
|
|
1962
|
+
statusCounts.LOADED === 0 &&
|
|
1963
|
+
statusCounts.FINISHING === 0);
|
|
1713
1964
|
};
|
|
1714
1965
|
const hasCompletedProcessingTasks = (statusCounts) => {
|
|
1715
1966
|
if (statusCounts.TOTAL === 0 || isProcessingTasks(statusCounts))
|
|
@@ -1807,20 +2058,26 @@ function useProcessTasks(handler, options) {
|
|
|
1807
2058
|
: [...tasksRef.current.values()].find(({ status }) => status === 'QUEUED') ?? {};
|
|
1808
2059
|
if (!data)
|
|
1809
2060
|
return;
|
|
2061
|
+
const all = isSingleTask
|
|
2062
|
+
? [_input.data]
|
|
2063
|
+
: [...tasksRef.current.values()].map(({ data }) => data);
|
|
1810
2064
|
const { onTaskCancel, onTaskComplete, onTaskError, onTaskProgress, onTaskSuccess, } = callbacksRef.current;
|
|
1811
2065
|
const getTask = () => tasksRef.current.get(data.id);
|
|
1812
2066
|
const { options } = _input;
|
|
1813
2067
|
const { onProgress: _onProgress } = options ?? {};
|
|
1814
|
-
const onProgress = ({ id }, progress) => {
|
|
1815
|
-
const task = updateTask(id, {
|
|
2068
|
+
const onProgress = ({ id }, progress, status = 'PENDING') => {
|
|
2069
|
+
const task = updateTask(id, {
|
|
2070
|
+
progress,
|
|
2071
|
+
status,
|
|
2072
|
+
});
|
|
1816
2073
|
if (task && ui.isFunction(onTaskProgress)) {
|
|
1817
2074
|
onTaskProgress(task, progress);
|
|
1818
2075
|
}
|
|
1819
2076
|
if (task && ui.isFunction(_onProgress)) {
|
|
1820
|
-
_onProgress(data, progress);
|
|
2077
|
+
_onProgress(data, progress, status);
|
|
1821
2078
|
}
|
|
1822
2079
|
};
|
|
1823
|
-
const input = { ..._input, data, options: { ...options, onProgress } };
|
|
2080
|
+
const input = { ..._input, data, all, options: { ...options, onProgress } };
|
|
1824
2081
|
const { cancel: _cancel, result } = handler(input);
|
|
1825
2082
|
const cancel = !_cancel
|
|
1826
2083
|
? undefined
|
|
@@ -1905,7 +2162,7 @@ function useHandler(handler, options) {
|
|
|
1905
2162
|
handleProcessing({
|
|
1906
2163
|
config,
|
|
1907
2164
|
...(hasData
|
|
1908
|
-
? { data: input.data }
|
|
2165
|
+
? { data: input.data, all: [input.data] }
|
|
1909
2166
|
: // if no `data` provided, provide `concurrency` to `options`
|
|
1910
2167
|
{ options: { concurrency: DEFAULT_ACTION_CONCURRENCY } }),
|
|
1911
2168
|
});
|
|
@@ -2014,8 +2271,10 @@ const DEFAULT_ACTION_VIEW_DISPLAY_TEXT = {
|
|
|
2014
2271
|
actionDestinationLabel: 'Destination',
|
|
2015
2272
|
statusDisplayCanceledLabel: 'Canceled',
|
|
2016
2273
|
statusDisplayCompletedLabel: 'Completed',
|
|
2274
|
+
statusDisplayLoadedLabel: 'Completed',
|
|
2017
2275
|
statusDisplayFailedLabel: 'Failed',
|
|
2018
2276
|
statusDisplayInProgressLabel: 'In progress',
|
|
2277
|
+
statusDisplayFinishingLabel: 'In progress',
|
|
2019
2278
|
statusDisplayTotalLabel: 'Total',
|
|
2020
2279
|
statusDisplayQueuedLabel: 'Not started',
|
|
2021
2280
|
// empty by default
|
|
@@ -2368,6 +2627,8 @@ const DEFAULT_DOWNLOAD_VIEW_DISPLAY_TEXT = {
|
|
|
2368
2627
|
...DEFAULT_ACTION_VIEW_DISPLAY_TEXT,
|
|
2369
2628
|
title: 'Download',
|
|
2370
2629
|
actionStartLabel: 'Download',
|
|
2630
|
+
statusDisplayFinishingLabel: 'Zipping',
|
|
2631
|
+
statusDisplayLoadedLabel: 'Loaded',
|
|
2371
2632
|
getActionCompleteMessage: (data) => {
|
|
2372
2633
|
const { counts } = data ?? {};
|
|
2373
2634
|
const { COMPLETE, FAILED, TOTAL } = counts ?? {};
|
|
@@ -2382,6 +2643,7 @@ const DEFAULT_DOWNLOAD_VIEW_DISPLAY_TEXT = {
|
|
|
2382
2643
|
type: 'error',
|
|
2383
2644
|
};
|
|
2384
2645
|
},
|
|
2646
|
+
tableColumnProgressHeader: 'Progress',
|
|
2385
2647
|
};
|
|
2386
2648
|
|
|
2387
2649
|
const DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT = {
|
|
@@ -3723,15 +3985,19 @@ function useResolveTableData(keys, { getCell, getHeader, getRowKey }, { items, p
|
|
|
3723
3985
|
|
|
3724
3986
|
const STATUS_LABELS = {
|
|
3725
3987
|
PENDING: 'statusDisplayInProgressLabel',
|
|
3988
|
+
FINISHING: 'statusDisplayFinishingLabel',
|
|
3726
3989
|
CANCELED: 'statusDisplayCanceledLabel',
|
|
3727
3990
|
COMPLETE: 'statusDisplayCompletedLabel',
|
|
3991
|
+
LOADED: 'statusDisplayLoadedLabel',
|
|
3728
3992
|
FAILED: 'statusDisplayFailedLabel',
|
|
3729
3993
|
QUEUED: 'statusDisplayQueuedLabel',
|
|
3730
3994
|
OVERWRITE_PREVENTED: 'statusDisplayOverwritePreventedLabel',
|
|
3731
3995
|
};
|
|
3732
3996
|
const STATUS_ICONS = {
|
|
3733
3997
|
PENDING: 'action-progress',
|
|
3998
|
+
FINISHING: 'action-progress',
|
|
3734
3999
|
COMPLETE: 'action-success',
|
|
4000
|
+
LOADED: 'action-success',
|
|
3735
4001
|
FAILED: 'action-error',
|
|
3736
4002
|
OVERWRITE_PREVENTED: 'action-info',
|
|
3737
4003
|
CANCELED: 'action-canceled',
|
|
@@ -3770,6 +4036,12 @@ const getUploadCellProgress = ({ progress, status, }) => {
|
|
|
3770
4036
|
const displayValue = `${Math.round(value * 100)}%`;
|
|
3771
4037
|
return { displayValue, value };
|
|
3772
4038
|
};
|
|
4039
|
+
const getDownloadCellProgress = ({ progress, status, }) => {
|
|
4040
|
+
// prefer `progress` if available, 1 if status is complete, default 0
|
|
4041
|
+
const value = progress ?? (status === 'LOADED' || status === 'COMPLETE' ? 1 : 0);
|
|
4042
|
+
const displayValue = `${Math.round(value * 100)}%`;
|
|
4043
|
+
return { displayValue, value };
|
|
4044
|
+
};
|
|
3773
4045
|
const getFileSize = (value, fallback = '-') => (!value ? fallback : ui.humanFileSize(value, true));
|
|
3774
4046
|
const getFileDataCancelCellContent = (data) => {
|
|
3775
4047
|
const { item, props } = data;
|
|
@@ -3801,31 +4073,31 @@ const getFileDataCancelCellContent = (data) => {
|
|
|
3801
4073
|
* Generates a unique key for a table cell based on the key and item id
|
|
3802
4074
|
*/
|
|
3803
4075
|
const getFileDataCellKey = ({ key, item, }) => `${key}-${item.data.id}`;
|
|
3804
|
-
const name$
|
|
4076
|
+
const name$2 = (data) => {
|
|
3805
4077
|
const key = getFileDataCellKey(data);
|
|
3806
4078
|
const { item } = data;
|
|
3807
4079
|
const text = item.data.fileKey;
|
|
3808
4080
|
const icon = STATUS_ICONS[item.status];
|
|
3809
4081
|
return { key, type: 'text', content: { icon, text } };
|
|
3810
4082
|
};
|
|
3811
|
-
const folder$
|
|
4083
|
+
const folder$2 = (data) => {
|
|
3812
4084
|
const key = getFileDataCellKey(data);
|
|
3813
4085
|
const text = getFileDataCellFolder(data.item);
|
|
3814
4086
|
return { key, type: 'text', content: { text } };
|
|
3815
4087
|
};
|
|
3816
|
-
const type$
|
|
4088
|
+
const type$2 = (data) => {
|
|
3817
4089
|
const key = getFileDataCellKey(data);
|
|
3818
4090
|
const { fileKey } = data.item.data;
|
|
3819
4091
|
const text = getFileType(fileKey);
|
|
3820
4092
|
return { key, type: 'text', content: { text } };
|
|
3821
4093
|
};
|
|
3822
|
-
const size$
|
|
4094
|
+
const size$2 = (data) => {
|
|
3823
4095
|
const key = getFileDataCellKey(data);
|
|
3824
4096
|
const { size: value } = data.item.data;
|
|
3825
4097
|
const displayValue = getFileSize(value);
|
|
3826
4098
|
return { key, type: 'number', content: { value, displayValue } };
|
|
3827
4099
|
};
|
|
3828
|
-
const cancel$
|
|
4100
|
+
const cancel$2 = (data) => {
|
|
3829
4101
|
const key = getFileDataCellKey(data);
|
|
3830
4102
|
const content = getFileDataCancelCellContent(data);
|
|
3831
4103
|
return { key, type: 'button', content };
|
|
@@ -3841,12 +4113,12 @@ const status$3 = (data) => {
|
|
|
3841
4113
|
return { key, type: 'text', content: { text } };
|
|
3842
4114
|
};
|
|
3843
4115
|
const COPY_CELL_RESOLVERS = {
|
|
3844
|
-
name: name$
|
|
3845
|
-
folder: folder$
|
|
3846
|
-
type: type$
|
|
3847
|
-
size: size$
|
|
4116
|
+
name: name$2,
|
|
4117
|
+
folder: folder$2,
|
|
4118
|
+
type: type$2,
|
|
4119
|
+
size: size$2,
|
|
3848
4120
|
status: status$3,
|
|
3849
|
-
cancel: cancel$
|
|
4121
|
+
cancel: cancel$2,
|
|
3850
4122
|
/**
|
|
3851
4123
|
* @deprecated
|
|
3852
4124
|
*
|
|
@@ -3878,12 +4150,12 @@ const status$2 = (data) => {
|
|
|
3878
4150
|
return { key, type: 'text', content: { text } };
|
|
3879
4151
|
};
|
|
3880
4152
|
const DELETE_CELL_RESOLVERS = {
|
|
3881
|
-
name: name$
|
|
3882
|
-
folder: folder$
|
|
3883
|
-
type: type$
|
|
3884
|
-
size: size$
|
|
4153
|
+
name: name$2,
|
|
4154
|
+
folder: folder$2,
|
|
4155
|
+
type: type$2,
|
|
4156
|
+
size: size$2,
|
|
3885
4157
|
status: status$2,
|
|
3886
|
-
cancel: cancel$
|
|
4158
|
+
cancel: cancel$2,
|
|
3887
4159
|
/**
|
|
3888
4160
|
* @deprecated
|
|
3889
4161
|
*
|
|
@@ -3905,8 +4177,42 @@ const DELETE_TABLE_RESOLVERS = {
|
|
|
3905
4177
|
getRowKey: ({ item }) => item.data.id,
|
|
3906
4178
|
};
|
|
3907
4179
|
|
|
4180
|
+
const DOWNLOAD_TABLE_KEYS = [
|
|
4181
|
+
'name',
|
|
4182
|
+
'folder',
|
|
4183
|
+
'type',
|
|
4184
|
+
'size',
|
|
4185
|
+
'status',
|
|
4186
|
+
'progress',
|
|
4187
|
+
'cancel',
|
|
4188
|
+
];
|
|
4189
|
+
const getDownloadCellKey = ({ key, item, }) => `${key}-${item.data.id}`;
|
|
4190
|
+
const name$1 = (data) => {
|
|
4191
|
+
const key = getDownloadCellKey(data);
|
|
4192
|
+
const { item } = data;
|
|
4193
|
+
const text = item.data.fileKey;
|
|
4194
|
+
const icon = STATUS_ICONS[item.status];
|
|
4195
|
+
return { key, type: 'text', content: { icon, text } };
|
|
4196
|
+
};
|
|
4197
|
+
const folder$1 = (data) => {
|
|
4198
|
+
const key = getDownloadCellKey(data);
|
|
4199
|
+
const text = getFileDataCellFolder(data.item);
|
|
4200
|
+
return { key, type: 'text', content: { text } };
|
|
4201
|
+
};
|
|
4202
|
+
const type$1 = (data) => {
|
|
4203
|
+
const key = getDownloadCellKey(data);
|
|
4204
|
+
const { item } = data;
|
|
4205
|
+
const text = getFileType(getCellName(item.data.key));
|
|
4206
|
+
return { key, type: 'text', content: { text } };
|
|
4207
|
+
};
|
|
4208
|
+
const size$1 = (data) => {
|
|
4209
|
+
const key = getDownloadCellKey(data);
|
|
4210
|
+
const { size: value } = data.item.data;
|
|
4211
|
+
const displayValue = getFileSize(value);
|
|
4212
|
+
return { key, type: 'number', content: { value, displayValue } };
|
|
4213
|
+
};
|
|
3908
4214
|
const status$1 = (data) => {
|
|
3909
|
-
const key =
|
|
4215
|
+
const key = getDownloadCellKey(data);
|
|
3910
4216
|
const { item: { status }, props: { displayText }, } = data;
|
|
3911
4217
|
const statusLabelKey = STATUS_LABELS[status];
|
|
3912
4218
|
const text = isDownloadViewDisplayTextKey(statusLabelKey)
|
|
@@ -3914,12 +4220,46 @@ const status$1 = (data) => {
|
|
|
3914
4220
|
: '';
|
|
3915
4221
|
return { key, type: 'text', content: { text } };
|
|
3916
4222
|
};
|
|
4223
|
+
const progress$1 = (data) => {
|
|
4224
|
+
const key = getDownloadCellKey(data);
|
|
4225
|
+
const content = getDownloadCellProgress(data.item);
|
|
4226
|
+
return { key, type: 'number', content };
|
|
4227
|
+
};
|
|
4228
|
+
const cancel$1 = (data) => {
|
|
4229
|
+
const key = getDownloadCellKey(data);
|
|
4230
|
+
const { item, props } = data;
|
|
4231
|
+
const { cancel, status } = item;
|
|
4232
|
+
const { isProcessing, onTaskRemove } = props;
|
|
4233
|
+
const isQueued = status === 'QUEUED';
|
|
4234
|
+
// a task is removable prior to processing start. Including `isQueued` ensures
|
|
4235
|
+
// that `isRemovable` is `false` on all tasks processing complete
|
|
4236
|
+
const isRemovable = isQueued && !isProcessing;
|
|
4237
|
+
// a task is cancelable while processing is true, and the task has a cancel handler
|
|
4238
|
+
const isCancelable = isProcessing && !!cancel;
|
|
4239
|
+
const ariaLabel = `${isRemovable ? 'Remove' : 'Cancel'} item: ${getCellName(item.data.fileKey)}`;
|
|
4240
|
+
const isDisabled = !isRemovable && !isCancelable;
|
|
4241
|
+
// resolve to `undefined` if not cancelable or removable
|
|
4242
|
+
const onClick = !isCancelable && !isRemovable
|
|
4243
|
+
? undefined
|
|
4244
|
+
: () => {
|
|
4245
|
+
if (isRemovable) {
|
|
4246
|
+
onTaskRemove?.(item);
|
|
4247
|
+
// do not run cancel handler on remove
|
|
4248
|
+
return;
|
|
4249
|
+
}
|
|
4250
|
+
if (isCancelable)
|
|
4251
|
+
cancel();
|
|
4252
|
+
};
|
|
4253
|
+
const content = { ariaLabel, isDisabled, onClick, icon: 'cancel' };
|
|
4254
|
+
return { key, type: 'button', content };
|
|
4255
|
+
};
|
|
3917
4256
|
const DOWNLOAD_CELL_RESOLVERS = {
|
|
3918
4257
|
name: name$1,
|
|
3919
4258
|
folder: folder$1,
|
|
3920
4259
|
type: type$1,
|
|
3921
4260
|
size: size$1,
|
|
3922
4261
|
status: status$1,
|
|
4262
|
+
progress: progress$1,
|
|
3923
4263
|
cancel: cancel$1,
|
|
3924
4264
|
};
|
|
3925
4265
|
const DOWNLOAD_TABLE_RESOLVERS = {
|
|
@@ -4950,7 +5290,7 @@ function DownloadViewProvider({ children, ...props }) {
|
|
|
4950
5290
|
const message = isProcessingComplete
|
|
4951
5291
|
? getActionCompleteMessage({ counts: statusCounts })
|
|
4952
5292
|
: undefined;
|
|
4953
|
-
const tableData = useResolveTableData(
|
|
5293
|
+
const tableData = useResolveTableData(DOWNLOAD_TABLE_KEYS, DOWNLOAD_TABLE_RESOLVERS, {
|
|
4954
5294
|
items,
|
|
4955
5295
|
props: { displayText, isProcessing, onTaskRemove },
|
|
4956
5296
|
});
|
|
@@ -5439,7 +5779,7 @@ function useFilePreview({ activeFile, }) {
|
|
|
5439
5779
|
const config = getConfig(location.current);
|
|
5440
5780
|
const { accountId, customEndpoint, credentials } = config;
|
|
5441
5781
|
const sharedOptions = {
|
|
5442
|
-
bucket: constructBucket(config),
|
|
5782
|
+
bucket: constructBucket$1(config),
|
|
5443
5783
|
expectedBucketOwner: accountId,
|
|
5444
5784
|
};
|
|
5445
5785
|
try {
|
|
@@ -6271,6 +6611,7 @@ exports.VERSION = VERSION;
|
|
|
6271
6611
|
exports.componentsDefault = componentsDefault;
|
|
6272
6612
|
exports.createAmplifyAuthAdapter = createAmplifyAuthAdapter;
|
|
6273
6613
|
exports.createStorageBrowser = createStorageBrowser;
|
|
6614
|
+
exports.deduplicateLocations = deduplicateLocations;
|
|
6274
6615
|
exports.defaultActionConfigs = defaultActionConfigs;
|
|
6275
6616
|
exports.defaultHandlers = defaultHandlers;
|
|
6276
6617
|
exports.getFilteredLocations = getFilteredLocations;
|