@aws-amplify/ui-react-storage 3.7.0 → 3.7.2
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 +1 -1
- package/dist/{createAmplifyAuthAdapter-C4RcR9wH.js → createAmplifyAuthAdapter-C0qoSzPO.js} +1188 -1186
- package/dist/esm/components/StorageBrowser/StorageBrowserDefault.mjs +16 -0
- package/dist/esm/components/StorageBrowser/actions/handlers/constants.mjs +3 -0
- package/dist/esm/components/StorageBrowser/actions/handlers/createFolder.mjs +2 -0
- package/dist/esm/components/StorageBrowser/actions/handlers/upload.mjs +2 -2
- package/dist/esm/components/StorageBrowser/createStorageBrowser.mjs +3 -3
- package/dist/esm/components/StorageBrowser/views/useView.mjs +4 -4
- package/dist/esm/version.mjs +1 -1
- package/dist/index.js +1 -1
- package/dist/types/components/StorageBrowser/actions/handlers/constants.d.ts +1 -0
- package/dist/types/components/StorageBrowser/actions/handlers/upload.d.ts +0 -1
- package/dist/types/components/StorageBrowser/index.d.ts +2 -1
- package/dist/types/components/StorageBrowser/types.d.ts +1 -2
- package/dist/types/components/StorageBrowser/views/index.d.ts +2 -0
- package/dist/types/components/StorageBrowser/views/useView.d.ts +4 -4
- package/dist/types/version.d.ts +1 -1
- package/package.json +6 -8
- package/dist/storage-browser-styles.css +0 -482
- package/dist/storage-browser-styles.js +0 -2
- package/dist/types/styles/storage-browser-styles.d.ts +0 -1
|
@@ -32,7 +32,7 @@ function _interopNamespace(e) {
|
|
|
32
32
|
|
|
33
33
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
34
34
|
|
|
35
|
-
const VERSION = '3.7.
|
|
35
|
+
const VERSION = '3.7.2';
|
|
36
36
|
|
|
37
37
|
const constructBucket = ({ bucket: bucketName, region, }) => ({ bucketName, region });
|
|
38
38
|
const parseAccessGrantLocation = (location) => {
|
|
@@ -159,6 +159,8 @@ const copyHandler = (input) => {
|
|
|
159
159
|
};
|
|
160
160
|
};
|
|
161
161
|
|
|
162
|
+
const DEFAULT_CHECKSUM_ALGORITHM = 'crc-32';
|
|
163
|
+
|
|
162
164
|
const createFolderHandler = (input) => {
|
|
163
165
|
const { config, data, options } = input;
|
|
164
166
|
const { accountId, credentials, customEndpoint } = config;
|
|
@@ -178,6 +180,7 @@ const createFolderHandler = (input) => {
|
|
|
178
180
|
onProgress(data, getProgress(event));
|
|
179
181
|
},
|
|
180
182
|
preventOverwrite,
|
|
183
|
+
checksumAlgorithm: DEFAULT_CHECKSUM_ALGORITHM,
|
|
181
184
|
},
|
|
182
185
|
});
|
|
183
186
|
return {
|
|
@@ -348,7 +351,6 @@ const isFileTooBig = (file) => file.size > UPLOAD_FILE_SIZE_LIMIT;
|
|
|
348
351
|
// 5MB for multipart upload
|
|
349
352
|
// https://github.com/aws-amplify/amplify-js/blob/1a5366d113c9af4ce994168653df3aadb142c581/packages/storage/src/providers/s3/utils/constants.ts#L16
|
|
350
353
|
const MULTIPART_UPLOAD_THRESHOLD_BYTES = 5 * 1024 * 1024;
|
|
351
|
-
const DEFAULT_CHECKSUM_ALGORITHM = 'crc-32';
|
|
352
354
|
const UNDEFINED_CALLBACKS = {
|
|
353
355
|
cancel: undefined,
|
|
354
356
|
pause: undefined,
|
|
@@ -1182,1282 +1184,1282 @@ const componentsDefault = {
|
|
|
1182
1184
|
Title,
|
|
1183
1185
|
};
|
|
1184
1186
|
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
const CREDENTIALS_STORE_DEFAULT_SIZE = 10;
|
|
1207
|
-
const CREDENTIALS_REFRESH_WINDOW_MS = 30000;
|
|
1208
|
-
|
|
1209
|
-
const serializedPermissions = (permissions) => permissions.sort().join('_');
|
|
1210
|
-
const createCacheKey = (location) => `${location.scope}_${serializedPermissions(location.permissions)}`;
|
|
1211
|
-
const pastTTL = (credentials) => {
|
|
1212
|
-
const { expiration } = credentials;
|
|
1213
|
-
return expiration.getTime() - CREDENTIALS_REFRESH_WINDOW_MS <= Date.now();
|
|
1187
|
+
const DEFAULT_ACTION_VIEW_DISPLAY_TEXT = {
|
|
1188
|
+
actionCancelLabel: 'Cancel',
|
|
1189
|
+
actionExitLabel: 'Exit',
|
|
1190
|
+
actionDestinationLabel: 'Destination',
|
|
1191
|
+
statusDisplayCanceledLabel: 'Canceled',
|
|
1192
|
+
statusDisplayCompletedLabel: 'Completed',
|
|
1193
|
+
statusDisplayFailedLabel: 'Failed',
|
|
1194
|
+
statusDisplayInProgressLabel: 'In progress',
|
|
1195
|
+
statusDisplayTotalLabel: 'Total',
|
|
1196
|
+
statusDisplayQueuedLabel: 'Not started',
|
|
1197
|
+
// empty by default
|
|
1198
|
+
tableColumnCancelHeader: '',
|
|
1199
|
+
tableColumnStatusHeader: 'Status',
|
|
1200
|
+
tableColumnFolderHeader: 'Folder',
|
|
1201
|
+
tableColumnNameHeader: 'Name',
|
|
1202
|
+
tableColumnTypeHeader: 'Type',
|
|
1203
|
+
tableColumnSizeHeader: 'Size',
|
|
1204
|
+
tableColumnProgressHeader: 'Progress',
|
|
1214
1205
|
};
|
|
1215
|
-
const
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1206
|
+
const DEFAULT_LIST_VIEW_DISPLAY_TEXT = {
|
|
1207
|
+
loadingIndicatorLabel: 'Loading',
|
|
1208
|
+
searchSubmitLabel: 'Submit',
|
|
1209
|
+
searchClearLabel: 'Clear search',
|
|
1210
|
+
getDateDisplayValue: (date) => new Intl.DateTimeFormat('en-US', {
|
|
1211
|
+
month: 'short',
|
|
1212
|
+
day: 'numeric',
|
|
1213
|
+
hour: 'numeric',
|
|
1214
|
+
year: 'numeric',
|
|
1215
|
+
minute: 'numeric',
|
|
1216
|
+
hourCycle: 'h12',
|
|
1217
|
+
}).format(date),
|
|
1225
1218
|
};
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1219
|
+
|
|
1220
|
+
const DEFAULT_CREATE_FOLDER_VIEW_DISPLAY_TEXT = {
|
|
1221
|
+
...DEFAULT_ACTION_VIEW_DISPLAY_TEXT,
|
|
1222
|
+
title: 'Create folder',
|
|
1223
|
+
actionStartLabel: 'Create folder',
|
|
1224
|
+
folderNameLabel: 'Folder name',
|
|
1225
|
+
folderNamePlaceholder: 'Folder name cannot contain "/", nor end or start with "."',
|
|
1226
|
+
getValidationMessage: () => 'Folder name cannot contain "/", nor end or start with "."',
|
|
1227
|
+
getActionCompleteMessage: (data) => {
|
|
1228
|
+
const { counts } = data ?? {};
|
|
1229
|
+
const { FAILED, OVERWRITE_PREVENTED } = counts ?? {};
|
|
1230
|
+
if (OVERWRITE_PREVENTED) {
|
|
1231
|
+
return {
|
|
1232
|
+
content: 'A folder already exists with the provided name',
|
|
1233
|
+
type: 'warning',
|
|
1234
|
+
};
|
|
1242
1235
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1236
|
+
if (FAILED) {
|
|
1237
|
+
return {
|
|
1238
|
+
content: 'There was an issue creating the folder.',
|
|
1239
|
+
type: 'error',
|
|
1240
|
+
};
|
|
1245
1241
|
}
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
};
|
|
1249
|
-
/**
|
|
1250
|
-
* @internal
|
|
1251
|
-
*/
|
|
1252
|
-
const initStore = (refreshHandler, size = CREDENTIALS_STORE_DEFAULT_SIZE) => {
|
|
1253
|
-
internals.assertValidationError(size > 0, internals.StorageValidationErrorCode.InvalidLocationCredentialsCacheSize);
|
|
1254
|
-
return {
|
|
1255
|
-
capacity: size,
|
|
1256
|
-
refreshHandler,
|
|
1257
|
-
values: new Map(),
|
|
1258
|
-
};
|
|
1259
|
-
};
|
|
1260
|
-
const getCacheValue = (store, location) => {
|
|
1261
|
-
const cacheKey = createCacheKey(location);
|
|
1262
|
-
const cachedValue = store.values.get(cacheKey);
|
|
1263
|
-
const cachedCredentials = cachedValue?.credentials;
|
|
1264
|
-
if (!cachedCredentials) {
|
|
1265
|
-
return null;
|
|
1266
|
-
}
|
|
1267
|
-
// Delete and re-insert to key to map to indicate a latest reference in LRU.
|
|
1268
|
-
store.values.delete(cacheKey);
|
|
1269
|
-
if (!pastTTL(cachedCredentials)) {
|
|
1270
|
-
// TODO(@AllanZhengYP): If the credential is still valid but will expire
|
|
1271
|
-
// soon, we should return credentials AND dispatch a refresh.
|
|
1272
|
-
store.values.set(cacheKey, cachedValue);
|
|
1273
|
-
return cachedCredentials;
|
|
1274
|
-
}
|
|
1275
|
-
return null;
|
|
1276
|
-
};
|
|
1277
|
-
/**
|
|
1278
|
-
* Fetch new credentials value with refresh handler and cache the result in
|
|
1279
|
-
* LRU cache.
|
|
1280
|
-
* @internal
|
|
1281
|
-
*/
|
|
1282
|
-
const fetchNewValue = async (store, location) => {
|
|
1283
|
-
const storeValues = store.values;
|
|
1284
|
-
const key = createCacheKey(location);
|
|
1285
|
-
if (!storeValues.has(key)) {
|
|
1286
|
-
const newStoreValue = {
|
|
1287
|
-
scope: location.scope,
|
|
1288
|
-
permissions: location.permissions,
|
|
1289
|
-
};
|
|
1290
|
-
setCacheRecord(store, key, newStoreValue);
|
|
1291
|
-
}
|
|
1292
|
-
const storeValue = storeValues.get(key);
|
|
1293
|
-
return dispatchRefresh(store.refreshHandler, storeValue, () => {
|
|
1294
|
-
store.values.delete(key);
|
|
1295
|
-
});
|
|
1242
|
+
return { content: 'Folder created.', type: 'success' };
|
|
1243
|
+
},
|
|
1296
1244
|
};
|
|
1297
1245
|
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
};
|
|
1312
|
-
const getCredentialsStore = (storeSymbol) => {
|
|
1313
|
-
internals.assertValidationError(storeRegistry.has(storeSymbol), internals.StorageValidationErrorCode.LocationCredentialsStoreDestroyed);
|
|
1314
|
-
return storeRegistry.get(storeSymbol);
|
|
1315
|
-
};
|
|
1316
|
-
/**
|
|
1317
|
-
* @internal
|
|
1318
|
-
*/
|
|
1319
|
-
const getValue = async (input) => {
|
|
1320
|
-
const { storeSymbol: storeReference, location, forceRefresh } = input;
|
|
1321
|
-
const store = getCredentialsStore(storeReference);
|
|
1322
|
-
if (!forceRefresh) {
|
|
1323
|
-
const credentials = getCacheValue(store, location);
|
|
1324
|
-
if (credentials !== null) {
|
|
1325
|
-
return { credentials };
|
|
1246
|
+
const DEFAULT_COPY_VIEW_DISPLAY_TEXT = {
|
|
1247
|
+
...DEFAULT_ACTION_VIEW_DISPLAY_TEXT,
|
|
1248
|
+
title: 'Copy',
|
|
1249
|
+
actionStartLabel: 'Copy',
|
|
1250
|
+
actionDestinationLabel: 'Copy destination',
|
|
1251
|
+
getListFoldersResultsMessage: ({ folders, query, message, hasError, hasExhaustedSearch, }) => {
|
|
1252
|
+
if (!folders?.length) {
|
|
1253
|
+
return {
|
|
1254
|
+
content: query
|
|
1255
|
+
? `No folders found matching "${query}"`
|
|
1256
|
+
: 'No subfolders found within selected folder.',
|
|
1257
|
+
type: 'info',
|
|
1258
|
+
};
|
|
1326
1259
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
}
|
|
1330
|
-
|
|
1331
|
-
|
|
1260
|
+
if (message && !!query) {
|
|
1261
|
+
return { content: 'Error loading folders.', type: 'error' };
|
|
1262
|
+
}
|
|
1263
|
+
if (hasError) {
|
|
1264
|
+
return { content: 'Error loading folders.', type: 'error' };
|
|
1265
|
+
}
|
|
1266
|
+
if (hasExhaustedSearch) {
|
|
1267
|
+
return {
|
|
1268
|
+
content: 'Showing results for up to the first 10,000 items.',
|
|
1269
|
+
type: 'info',
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
},
|
|
1273
|
+
loadingIndicatorLabel: 'Loading',
|
|
1274
|
+
overwriteWarningMessage: 'Copied files will overwrite existing files at selected destination.',
|
|
1275
|
+
searchPlaceholder: 'Search for folders',
|
|
1276
|
+
getActionCompleteMessage: (data) => {
|
|
1277
|
+
const { counts } = data ?? {};
|
|
1278
|
+
const { COMPLETE, FAILED, TOTAL } = counts ?? {};
|
|
1279
|
+
if (COMPLETE === TOTAL) {
|
|
1280
|
+
return {
|
|
1281
|
+
content: 'All files copied.',
|
|
1282
|
+
type: 'success',
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
if (FAILED === TOTAL) {
|
|
1286
|
+
return { content: 'All files failed to copy.', type: 'error' };
|
|
1287
|
+
}
|
|
1288
|
+
return {
|
|
1289
|
+
content: `${COMPLETE} files copied, ${FAILED} files failed to copy.`,
|
|
1290
|
+
type: 'error',
|
|
1291
|
+
};
|
|
1292
|
+
},
|
|
1293
|
+
searchSubmitLabel: 'Submit',
|
|
1294
|
+
searchClearLabel: 'Clear search',
|
|
1332
1295
|
};
|
|
1333
1296
|
|
|
1334
|
-
const
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
validateS3Uri(providerLocation.scope);
|
|
1344
|
-
// TODO(@AllanZhengYP): validate the action bucket and paths matches provider scope.
|
|
1345
|
-
return getValue({
|
|
1346
|
-
storeSymbol,
|
|
1347
|
-
location: { ...providerLocation },
|
|
1348
|
-
forceRefresh,
|
|
1349
|
-
});
|
|
1350
|
-
};
|
|
1351
|
-
return locationCredentialsProvider;
|
|
1352
|
-
},
|
|
1353
|
-
destroy() {
|
|
1354
|
-
removeStore(storeSymbol);
|
|
1355
|
-
},
|
|
1356
|
-
};
|
|
1357
|
-
return store;
|
|
1358
|
-
};
|
|
1359
|
-
|
|
1360
|
-
const createCredentialsStore = ({ ...input }) => {
|
|
1361
|
-
const { destroy, getProvider } = createLocationCredentialsStore(input);
|
|
1362
|
-
return {
|
|
1363
|
-
destroy,
|
|
1364
|
-
getCredentials: ({ scope, permissions }) => getProvider({
|
|
1365
|
-
scope,
|
|
1366
|
-
permissions,
|
|
1367
|
-
}),
|
|
1368
|
-
};
|
|
1369
|
-
};
|
|
1370
|
-
const isCredentialsStore = (value) => ui.isFunction(value?.getCredentials);
|
|
1371
|
-
function useCredentialsStore({ getLocationCredentials: handler, initialValue, onDestroy, registerAuthListener, }) {
|
|
1372
|
-
const hasExistingStore = isCredentialsStore(initialValue);
|
|
1373
|
-
const [store, setStore] = React__namespace["default"].useState(() => hasExistingStore ? initialValue : createCredentialsStore({ handler }));
|
|
1374
|
-
const { destroy } = store;
|
|
1375
|
-
React__namespace["default"].useEffect(() => {
|
|
1376
|
-
if (hasExistingStore) {
|
|
1377
|
-
return;
|
|
1297
|
+
const DEFAULT_DELETE_VIEW_DISPLAY_TEXT = {
|
|
1298
|
+
...DEFAULT_ACTION_VIEW_DISPLAY_TEXT,
|
|
1299
|
+
title: 'Delete',
|
|
1300
|
+
actionStartLabel: 'Delete',
|
|
1301
|
+
getActionCompleteMessage: (data) => {
|
|
1302
|
+
const { counts } = data ?? {};
|
|
1303
|
+
const { COMPLETE, FAILED, TOTAL } = counts ?? {};
|
|
1304
|
+
if (COMPLETE === TOTAL) {
|
|
1305
|
+
return { content: 'All files deleted.', type: 'success' };
|
|
1378
1306
|
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
}
|
|
1384
|
-
|
|
1307
|
+
if (FAILED === TOTAL) {
|
|
1308
|
+
return { content: 'All files failed to delete.', type: 'error' };
|
|
1309
|
+
}
|
|
1310
|
+
return {
|
|
1311
|
+
content: `${COMPLETE} files deleted, ${FAILED} files failed to delete.`,
|
|
1312
|
+
type: 'error',
|
|
1385
1313
|
};
|
|
1386
|
-
|
|
1387
|
-
registerAuthListener(handleAuthStatusChange);
|
|
1388
|
-
}, [destroy, handler, hasExistingStore, onDestroy, registerAuthListener]);
|
|
1389
|
-
return store;
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
const ERROR_MESSAGE$3 = '`useCredentials` must be called from within a `CredentialsProvider`.';
|
|
1393
|
-
const { useCredentials, CredentialsContext } = uiReactCore.createContextUtilities({
|
|
1394
|
-
contextName: 'Credentials',
|
|
1395
|
-
errorMessage: ERROR_MESSAGE$3,
|
|
1396
|
-
});
|
|
1397
|
-
function CredentialsProvider({ children, ...props }) {
|
|
1398
|
-
const initialValue = React__namespace["default"].useContext(CredentialsContext);
|
|
1399
|
-
const value = useCredentialsStore({ ...props, initialValue });
|
|
1400
|
-
return (React__namespace["default"].createElement(CredentialsContext.Provider, { value: value }, children));
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
const ERROR_MESSAGE$2 = 'Invalid `location` value provided as initial value to `LocationProvider.';
|
|
1404
|
-
const DEFAULT_STATE$1 = {
|
|
1405
|
-
current: undefined,
|
|
1406
|
-
path: '',
|
|
1407
|
-
key: '',
|
|
1314
|
+
},
|
|
1408
1315
|
};
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1316
|
+
|
|
1317
|
+
const DEFAULT_ERROR_MESSAGE$1 = 'There was an error loading items.';
|
|
1318
|
+
const DEFAULT_LOCATION_DETAIL_VIEW_DISPLAY_TEXT = {
|
|
1319
|
+
...DEFAULT_LIST_VIEW_DISPLAY_TEXT,
|
|
1320
|
+
getListItemsResultMessage: (data) => {
|
|
1321
|
+
const { items, hasExhaustedSearch, hasError = false, message, isLoading, } = data ?? {};
|
|
1322
|
+
if (isLoading) {
|
|
1323
|
+
return undefined;
|
|
1324
|
+
}
|
|
1325
|
+
if (hasError) {
|
|
1416
1326
|
return {
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
key: `${location.prefix ?? ''}${path}`,
|
|
1327
|
+
type: 'error',
|
|
1328
|
+
content: message ?? DEFAULT_ERROR_MESSAGE$1,
|
|
1420
1329
|
};
|
|
1421
1330
|
}
|
|
1422
|
-
|
|
1423
|
-
return
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
}
|
|
1427
|
-
const defaultValue$7 = [DEFAULT_STATE$1, ui.noop];
|
|
1428
|
-
const { LocationContext, useLocation } = uiReactCore.createContextUtilities({
|
|
1429
|
-
contextName: 'Location',
|
|
1430
|
-
defaultValue: defaultValue$7,
|
|
1431
|
-
});
|
|
1432
|
-
function LocationProvider({ children, location, path = '', }) {
|
|
1433
|
-
if (location) {
|
|
1434
|
-
assertLocationData(location, ERROR_MESSAGE$2);
|
|
1435
|
-
}
|
|
1436
|
-
const value = React__namespace["default"].useReducer(handleAction$1, location
|
|
1437
|
-
? {
|
|
1438
|
-
current: location,
|
|
1439
|
-
path,
|
|
1440
|
-
key: `${location.prefix ?? ''}${path}`,
|
|
1331
|
+
if (!items?.length && hasExhaustedSearch) {
|
|
1332
|
+
return {
|
|
1333
|
+
type: 'info',
|
|
1334
|
+
content: `No results found in the first 10,000 items.`,
|
|
1335
|
+
};
|
|
1441
1336
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
const resolveFiles = (prevItems, files) => {
|
|
1448
|
-
if (!files?.length)
|
|
1449
|
-
return prevItems;
|
|
1450
|
-
// construct `nextItems` and filter out existing `file` entries
|
|
1451
|
-
const nextItems = files.reduce((items, file) => {
|
|
1452
|
-
const { name, webkitRelativePath } = file;
|
|
1453
|
-
return prevItems.some(({ file: existing }) => existing.name === name &&
|
|
1454
|
-
existing.webkitRelativePath === webkitRelativePath)
|
|
1455
|
-
? items
|
|
1456
|
-
: items.concat({
|
|
1457
|
-
key: ui.isEmpty(webkitRelativePath) ? name : webkitRelativePath,
|
|
1458
|
-
id: crypto.randomUUID(),
|
|
1459
|
-
file,
|
|
1460
|
-
});
|
|
1461
|
-
}, []);
|
|
1462
|
-
if (!nextItems.length)
|
|
1463
|
-
return prevItems;
|
|
1464
|
-
if (!prevItems.length) {
|
|
1465
|
-
return nextItems.sort(compareFileItems);
|
|
1466
|
-
}
|
|
1467
|
-
return prevItems.concat(nextItems).sort(compareFileItems);
|
|
1468
|
-
};
|
|
1469
|
-
const filesReducer = (prevItems, input) => {
|
|
1470
|
-
switch (input.type) {
|
|
1471
|
-
case 'ADD_FILE_ITEMS': {
|
|
1472
|
-
return resolveFiles(prevItems, input.files);
|
|
1337
|
+
if (!items?.length) {
|
|
1338
|
+
return {
|
|
1339
|
+
type: 'info',
|
|
1340
|
+
content: 'No files.',
|
|
1341
|
+
};
|
|
1473
1342
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1343
|
+
if (hasExhaustedSearch) {
|
|
1344
|
+
return {
|
|
1345
|
+
type: 'info',
|
|
1346
|
+
content: `Showing results for up to the first 10,000 items.`,
|
|
1347
|
+
};
|
|
1479
1348
|
}
|
|
1480
|
-
|
|
1481
|
-
|
|
1349
|
+
// TODO: add more cases as needed
|
|
1350
|
+
return undefined;
|
|
1351
|
+
},
|
|
1352
|
+
searchSubfoldersToggleLabel: 'Include subfolders',
|
|
1353
|
+
searchPlaceholder: 'Search current folder',
|
|
1354
|
+
tableColumnLastModifiedHeader: 'Last modified',
|
|
1355
|
+
tableColumnNameHeader: 'Name',
|
|
1356
|
+
tableColumnSizeHeader: 'Size',
|
|
1357
|
+
tableColumnTypeHeader: 'Type',
|
|
1358
|
+
selectFileLabel: 'Select file',
|
|
1359
|
+
selectAllFilesLabel: 'Select all files',
|
|
1360
|
+
getActionListItemLabel: (key = '') => {
|
|
1361
|
+
switch (key) {
|
|
1362
|
+
case 'Copy':
|
|
1363
|
+
return 'Copy';
|
|
1364
|
+
case 'Delete':
|
|
1365
|
+
return 'Delete';
|
|
1366
|
+
case 'Create folder':
|
|
1367
|
+
return 'Create folder';
|
|
1368
|
+
case 'Upload':
|
|
1369
|
+
return 'Upload';
|
|
1370
|
+
default:
|
|
1371
|
+
return key;
|
|
1482
1372
|
}
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
};
|
|
1486
|
-
const
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
if (ui.isString(value))
|
|
1490
|
-
return [value, undefined];
|
|
1491
|
-
const [selectType, ...rest] = value;
|
|
1492
|
-
return [selectType, !rest?.length ? undefined : { accept: rest.join() }];
|
|
1373
|
+
},
|
|
1374
|
+
getTitle: (location) => {
|
|
1375
|
+
const { current, key } = location;
|
|
1376
|
+
const { bucket = '' } = current ?? {};
|
|
1377
|
+
return key || bucket;
|
|
1378
|
+
},
|
|
1493
1379
|
};
|
|
1494
1380
|
|
|
1495
|
-
const
|
|
1496
|
-
const
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
});
|
|
1505
|
-
const handleFilesAction = React__namespace["default"].useCallback((action) => {
|
|
1506
|
-
if (action.type === 'SELECT_FILES') {
|
|
1507
|
-
handleFileSelect(...parseFileSelectParams(action.selectionType));
|
|
1381
|
+
const DEFAULT_ERROR_MESSAGE = 'There was an error loading locations.';
|
|
1382
|
+
const DEFAULT_LOCATIONS_VIEW_DISPLAY_TEXT = {
|
|
1383
|
+
...DEFAULT_LIST_VIEW_DISPLAY_TEXT,
|
|
1384
|
+
title: 'Home',
|
|
1385
|
+
searchPlaceholder: 'Filter folders and files',
|
|
1386
|
+
getListLocationsResultMessage: (data) => {
|
|
1387
|
+
const { isLoading, items, hasExhaustedSearch, hasError = false, message, } = data ?? {};
|
|
1388
|
+
if (isLoading) {
|
|
1389
|
+
return undefined;
|
|
1508
1390
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1391
|
+
if (hasError) {
|
|
1392
|
+
return {
|
|
1393
|
+
type: 'error',
|
|
1394
|
+
content: message ?? DEFAULT_ERROR_MESSAGE,
|
|
1395
|
+
};
|
|
1511
1396
|
}
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
}
|
|
1518
|
-
|
|
1519
|
-
const handleAction = (event) => {
|
|
1520
|
-
switch (event.type) {
|
|
1521
|
-
case 'SET_ACTION_TYPE': {
|
|
1522
|
-
return event.actionType;
|
|
1397
|
+
if (items?.length === 0 && !hasExhaustedSearch) {
|
|
1398
|
+
return {
|
|
1399
|
+
type: 'info',
|
|
1400
|
+
content: 'No folders or files.',
|
|
1401
|
+
};
|
|
1523
1402
|
}
|
|
1524
|
-
|
|
1525
|
-
return
|
|
1403
|
+
if (hasExhaustedSearch) {
|
|
1404
|
+
return {
|
|
1405
|
+
type: 'info',
|
|
1406
|
+
content: `Showing results for up to the first 10,000 items.`,
|
|
1407
|
+
};
|
|
1526
1408
|
}
|
|
1527
|
-
|
|
1409
|
+
// TODO: add more cases as needed
|
|
1410
|
+
return undefined;
|
|
1411
|
+
},
|
|
1412
|
+
getPermissionName: (permissions) => {
|
|
1413
|
+
let text = '';
|
|
1414
|
+
if (permissions.includes('get') || permissions.includes('list')) {
|
|
1415
|
+
text = 'Read';
|
|
1416
|
+
}
|
|
1417
|
+
if (permissions.includes('write') || permissions.includes('delete')) {
|
|
1418
|
+
text = text ? 'Read/Write' : 'Write';
|
|
1419
|
+
}
|
|
1420
|
+
if (!text) {
|
|
1421
|
+
text = permissions.join('/');
|
|
1422
|
+
}
|
|
1423
|
+
return text;
|
|
1424
|
+
},
|
|
1425
|
+
getDownloadLabel: (fileName) => `Download ${fileName}`,
|
|
1426
|
+
tableColumnBucketHeader: 'Bucket',
|
|
1427
|
+
tableColumnFolderHeader: 'Folder',
|
|
1428
|
+
tableColumnPermissionsHeader: 'Permissions',
|
|
1429
|
+
tableColumnActionsHeader: 'Actions',
|
|
1528
1430
|
};
|
|
1529
|
-
function useActionTypeState(initialState) {
|
|
1530
|
-
const [actionType, setActionType] = React__namespace["default"].useState(initialState);
|
|
1531
|
-
const handler = React__namespace["default"].useCallback((action) => setActionType(handleAction(action)), []);
|
|
1532
|
-
return [actionType, handler];
|
|
1533
|
-
}
|
|
1534
|
-
|
|
1535
|
-
const defaultValue$5 = [undefined, ui.noop];
|
|
1536
|
-
const { ActionTypeContext, useActionType } = uiReactCore.createContextUtilities({
|
|
1537
|
-
contextName: 'ActionType',
|
|
1538
|
-
defaultValue: defaultValue$5,
|
|
1539
|
-
});
|
|
1540
|
-
function ActionTypeProvider({ actionType, children, }) {
|
|
1541
|
-
const value = useActionTypeState(actionType);
|
|
1542
|
-
return (React__namespace["default"].createElement(ActionTypeContext.Provider, { value: value }, children));
|
|
1543
|
-
}
|
|
1544
1431
|
|
|
1545
|
-
const
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1432
|
+
const DEFAULT_UPLOAD_VIEW_DISPLAY_TEXT = {
|
|
1433
|
+
...DEFAULT_ACTION_VIEW_DISPLAY_TEXT,
|
|
1434
|
+
title: 'Upload',
|
|
1435
|
+
actionStartLabel: 'Upload',
|
|
1436
|
+
addFilesLabel: 'Add files',
|
|
1437
|
+
addFolderLabel: 'Add folder',
|
|
1438
|
+
getActionCompleteMessage: (data) => {
|
|
1439
|
+
const { counts } = data ?? {};
|
|
1440
|
+
const { COMPLETE, FAILED, OVERWRITE_PREVENTED, CANCELED, TOTAL } = counts ?? {};
|
|
1441
|
+
const hasPreventedOverwrite = !!OVERWRITE_PREVENTED;
|
|
1442
|
+
const hasFailure = !!FAILED;
|
|
1443
|
+
const hasSuccess = !!COMPLETE;
|
|
1444
|
+
const hasCanceled = !!CANCELED;
|
|
1445
|
+
const type = hasFailure
|
|
1446
|
+
? 'error'
|
|
1447
|
+
: hasPreventedOverwrite || hasCanceled
|
|
1448
|
+
? 'warning'
|
|
1449
|
+
: 'success';
|
|
1450
|
+
const preventedOverwriteMessage = hasPreventedOverwrite
|
|
1451
|
+
? [
|
|
1452
|
+
'Overwrite prevented for',
|
|
1453
|
+
OVERWRITE_PREVENTED === TOTAL ? 'all' : String(OVERWRITE_PREVENTED),
|
|
1454
|
+
OVERWRITE_PREVENTED > 1 || OVERWRITE_PREVENTED === TOTAL
|
|
1455
|
+
? `files`
|
|
1456
|
+
: 'file',
|
|
1457
|
+
].join(' ')
|
|
1458
|
+
: undefined;
|
|
1459
|
+
const canceledMessage = hasCanceled
|
|
1460
|
+
? [
|
|
1461
|
+
CANCELED === TOTAL ? 'All' : String(CANCELED),
|
|
1462
|
+
CANCELED > 1 || CANCELED === TOTAL ? `uploads` : 'upload',
|
|
1463
|
+
'canceled',
|
|
1464
|
+
].join(' ')
|
|
1465
|
+
: undefined;
|
|
1466
|
+
const failedMessage = hasFailure
|
|
1467
|
+
? [
|
|
1468
|
+
FAILED === TOTAL ? 'All' : String(FAILED),
|
|
1469
|
+
FAILED > 1 || FAILED === TOTAL ? `files` : 'file',
|
|
1470
|
+
'failed to upload',
|
|
1471
|
+
].join(' ')
|
|
1472
|
+
: undefined;
|
|
1473
|
+
const completedMessage = hasSuccess
|
|
1474
|
+
? [
|
|
1475
|
+
COMPLETE === TOTAL ? 'All' : String(COMPLETE),
|
|
1476
|
+
COMPLETE > 1 || COMPLETE === TOTAL ? `files` : 'file',
|
|
1477
|
+
'uploaded',
|
|
1478
|
+
].join(' ')
|
|
1479
|
+
: undefined;
|
|
1480
|
+
const messages = [
|
|
1481
|
+
preventedOverwriteMessage,
|
|
1482
|
+
failedMessage,
|
|
1483
|
+
canceledMessage,
|
|
1484
|
+
completedMessage,
|
|
1485
|
+
].filter(Boolean);
|
|
1486
|
+
if (messages.length > 0) {
|
|
1562
1487
|
return {
|
|
1563
|
-
|
|
1488
|
+
content: messages.join(', ') + '.',
|
|
1489
|
+
type,
|
|
1564
1490
|
};
|
|
1565
1491
|
}
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
if (fileDataItems.length === prevState.fileDataItems.length) {
|
|
1572
|
-
return prevState;
|
|
1573
|
-
}
|
|
1574
|
-
return { fileDataItems };
|
|
1492
|
+
return { content: 'All files uploaded.', type };
|
|
1493
|
+
},
|
|
1494
|
+
getFilesValidationMessage: (data) => {
|
|
1495
|
+
if (!data?.invalidFiles) {
|
|
1496
|
+
return undefined;
|
|
1575
1497
|
}
|
|
1576
|
-
|
|
1577
|
-
|
|
1498
|
+
const tooBigFileNames = data.invalidFiles
|
|
1499
|
+
.filter(({ file }) => isFileTooBig(file))
|
|
1500
|
+
.map(({ file }) => file.name)
|
|
1501
|
+
.join(', ');
|
|
1502
|
+
if (tooBigFileNames) {
|
|
1503
|
+
return {
|
|
1504
|
+
content: `Files larger than 160GB cannot be added to the upload queue: ${tooBigFileNames}`,
|
|
1505
|
+
type: 'warning',
|
|
1506
|
+
};
|
|
1578
1507
|
}
|
|
1579
|
-
|
|
1508
|
+
return undefined;
|
|
1509
|
+
},
|
|
1510
|
+
statusDisplayOverwritePreventedLabel: 'Overwrite prevented',
|
|
1511
|
+
overwriteToggleLabel: 'Overwrite existing files',
|
|
1580
1512
|
};
|
|
1581
|
-
const defaultValue$4 = [DEFAULT_STATE, ui.noop];
|
|
1582
|
-
const { LocationItemsContext, useLocationItems } = uiReactCore.createContextUtilities({ contextName: 'LocationItems', defaultValue: defaultValue$4 });
|
|
1583
|
-
function LocationItemsProvider({ children, }) {
|
|
1584
|
-
const value = React__namespace["default"].useReducer(locationItemsReducer, DEFAULT_STATE);
|
|
1585
|
-
return (React__namespace["default"].createElement(LocationItemsContext.Provider, { value: value }, children));
|
|
1586
|
-
}
|
|
1587
1513
|
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1514
|
+
const DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT = {
|
|
1515
|
+
CopyView: DEFAULT_COPY_VIEW_DISPLAY_TEXT,
|
|
1516
|
+
CreateFolderView: DEFAULT_CREATE_FOLDER_VIEW_DISPLAY_TEXT,
|
|
1517
|
+
DeleteView: DEFAULT_DELETE_VIEW_DISPLAY_TEXT,
|
|
1518
|
+
LocationDetailView: DEFAULT_LOCATION_DETAIL_VIEW_DISPLAY_TEXT,
|
|
1519
|
+
LocationsView: DEFAULT_LOCATIONS_VIEW_DISPLAY_TEXT,
|
|
1520
|
+
UploadView: DEFAULT_UPLOAD_VIEW_DISPLAY_TEXT,
|
|
1521
|
+
};
|
|
1595
1522
|
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
]);
|
|
1633
|
-
return [{ actionType, files, location, locationItems }, dispatchHandler];
|
|
1523
|
+
const { DisplayTextContext, useDisplayText } = uiReactCore.createContextUtilities({
|
|
1524
|
+
contextName: 'DisplayText',
|
|
1525
|
+
errorMessage: '`useDisplayText` must be called inside `DisplayTextProvider`',
|
|
1526
|
+
});
|
|
1527
|
+
function resolveDisplayText(displayText) {
|
|
1528
|
+
if (!displayText)
|
|
1529
|
+
return DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT;
|
|
1530
|
+
// override
|
|
1531
|
+
const { CopyView, CreateFolderView, DeleteView, LocationDetailView, LocationsView, UploadView, } = displayText;
|
|
1532
|
+
return {
|
|
1533
|
+
CopyView: { ...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.CopyView, ...CopyView },
|
|
1534
|
+
CreateFolderView: {
|
|
1535
|
+
...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.CreateFolderView,
|
|
1536
|
+
...CreateFolderView,
|
|
1537
|
+
},
|
|
1538
|
+
DeleteView: {
|
|
1539
|
+
...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.DeleteView,
|
|
1540
|
+
...DeleteView,
|
|
1541
|
+
},
|
|
1542
|
+
LocationDetailView: {
|
|
1543
|
+
...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.LocationDetailView,
|
|
1544
|
+
...LocationDetailView,
|
|
1545
|
+
},
|
|
1546
|
+
LocationsView: {
|
|
1547
|
+
...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.LocationsView,
|
|
1548
|
+
...LocationsView,
|
|
1549
|
+
},
|
|
1550
|
+
UploadView: {
|
|
1551
|
+
...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.UploadView,
|
|
1552
|
+
...UploadView,
|
|
1553
|
+
},
|
|
1554
|
+
};
|
|
1555
|
+
}
|
|
1556
|
+
function DisplayTextProvider({ children, displayText: _override, }) {
|
|
1557
|
+
// do deep merge here of default and override here
|
|
1558
|
+
const resolvedDisplayText = React__namespace["default"].useMemo(() => resolveDisplayText(_override), [_override]);
|
|
1559
|
+
return (React__namespace["default"].createElement(DisplayTextContext.Provider, { value: resolvedDisplayText }, children));
|
|
1634
1560
|
}
|
|
1635
1561
|
|
|
1636
|
-
const
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
//
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
return
|
|
1654
|
-
|
|
1655
|
-
bucket,
|
|
1656
|
-
credentials: getCredentials({
|
|
1657
|
-
permissions,
|
|
1658
|
-
scope,
|
|
1659
|
-
}),
|
|
1660
|
-
region,
|
|
1661
|
-
customEndpoint,
|
|
1662
|
-
};
|
|
1663
|
-
}, [accountId, current, customEndpoint, getCredentials, key, region]);
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
|
-
const ERROR_MESSAGE$1 = '`useGetActionInput` must be called from within a `ConfigurationProvider`.';
|
|
1667
|
-
const { useGetActionInput, GetActionInputContext } = uiReactCore.createContextUtilities({
|
|
1668
|
-
contextName: 'GetActionInput',
|
|
1669
|
-
errorMessage: ERROR_MESSAGE$1,
|
|
1670
|
-
});
|
|
1671
|
-
function GetActionInputProvider({ accountId, children, customEndpoint, region, }) {
|
|
1672
|
-
const value = useGetActionInputCallback({
|
|
1673
|
-
accountId,
|
|
1674
|
-
customEndpoint,
|
|
1675
|
-
region,
|
|
1676
|
-
});
|
|
1677
|
-
return (React__namespace["default"].createElement(GetActionInputContext.Provider, { value: value }, children));
|
|
1678
|
-
}
|
|
1679
|
-
|
|
1680
|
-
const Passthrough = ({ children }) => (React__namespace["default"].createElement(React__namespace["default"].Fragment, null, children));
|
|
1681
|
-
function createConfigurationProvider(input) {
|
|
1682
|
-
const { accountId, ChildComponent, displayName, region, customEndpoint, ...rest } = input;
|
|
1683
|
-
const Child = elements.isComponent(ChildComponent) ? ChildComponent : Passthrough;
|
|
1684
|
-
const Provider = (props) => (React__namespace["default"].createElement(CredentialsProvider, { ...rest },
|
|
1685
|
-
React__namespace["default"].createElement(GetActionInputProvider, { accountId: accountId, region: region, customEndpoint: customEndpoint },
|
|
1686
|
-
React__namespace["default"].createElement(Child, { ...props }))));
|
|
1687
|
-
Provider.displayName = displayName;
|
|
1688
|
-
return Provider;
|
|
1562
|
+
const Fallback = () => (React__namespace["default"].createElement("div", { className: STORAGE_BROWSER_BLOCK_TO_BE_UPDATED },
|
|
1563
|
+
React__namespace["default"].createElement("div", { className: `${STORAGE_BROWSER_BLOCK_TO_BE_UPDATED}__error-boundary` }, "Something went wrong.")));
|
|
1564
|
+
class ErrorBoundary extends React__namespace["default"].Component {
|
|
1565
|
+
constructor(props) {
|
|
1566
|
+
super(props);
|
|
1567
|
+
this.state = { hasError: false };
|
|
1568
|
+
}
|
|
1569
|
+
static getDerivedStateFromError(_error) {
|
|
1570
|
+
// Update state so the next render will show the fallback UI.
|
|
1571
|
+
return { hasError: true };
|
|
1572
|
+
}
|
|
1573
|
+
render() {
|
|
1574
|
+
const { hasError } = this.state;
|
|
1575
|
+
const { children } = this.props;
|
|
1576
|
+
if (hasError) {
|
|
1577
|
+
return React__namespace["default"].createElement(Fallback, null);
|
|
1578
|
+
}
|
|
1579
|
+
return children;
|
|
1580
|
+
}
|
|
1689
1581
|
}
|
|
1690
1582
|
|
|
1691
|
-
const
|
|
1692
|
-
const
|
|
1693
|
-
contextName: 'ControlsContext',
|
|
1694
|
-
defaultValue: defaultValue$3,
|
|
1695
|
-
});
|
|
1583
|
+
const CREDENTIALS_STORE_DEFAULT_SIZE = 10;
|
|
1584
|
+
const CREDENTIALS_REFRESH_WINDOW_MS = 30000;
|
|
1696
1585
|
|
|
1697
|
-
const
|
|
1698
|
-
|
|
1586
|
+
const serializedPermissions = (permissions) => permissions.sort().join('_');
|
|
1587
|
+
const createCacheKey = (location) => `${location.scope}_${serializedPermissions(location.permissions)}`;
|
|
1588
|
+
const pastTTL = (credentials) => {
|
|
1589
|
+
const { expiration } = credentials;
|
|
1590
|
+
return expiration.getTime() - CREDENTIALS_REFRESH_WINDOW_MS <= Date.now();
|
|
1591
|
+
};
|
|
1592
|
+
const setCacheRecord = (store, key, value) => {
|
|
1593
|
+
if (store.capacity === store.values.size) {
|
|
1594
|
+
// Pop least used entry. The Map's key are in insertion order.
|
|
1595
|
+
// So first key is the last recently inserted.
|
|
1596
|
+
const [oldestKey] = store.values.keys();
|
|
1597
|
+
store.values.delete(oldestKey);
|
|
1598
|
+
// TODO(@AllanZhengYP): Add log info when record is evicted.
|
|
1599
|
+
}
|
|
1600
|
+
// Add latest used value to the cache.
|
|
1601
|
+
store.values.set(key, value);
|
|
1602
|
+
};
|
|
1603
|
+
const dispatchRefresh = (refreshHandler, value, onRefreshFailure) => {
|
|
1604
|
+
if (value.inflightCredentials) {
|
|
1605
|
+
return value.inflightCredentials;
|
|
1606
|
+
}
|
|
1607
|
+
value.inflightCredentials = (async () => {
|
|
1608
|
+
try {
|
|
1609
|
+
const { credentials } = await refreshHandler({
|
|
1610
|
+
scope: value.scope,
|
|
1611
|
+
permissions: value.permissions,
|
|
1612
|
+
});
|
|
1613
|
+
value.credentials = credentials;
|
|
1614
|
+
return { credentials };
|
|
1615
|
+
}
|
|
1616
|
+
catch (e) {
|
|
1617
|
+
onRefreshFailure();
|
|
1618
|
+
throw e;
|
|
1619
|
+
}
|
|
1620
|
+
finally {
|
|
1621
|
+
value.inflightCredentials = undefined;
|
|
1622
|
+
}
|
|
1623
|
+
})();
|
|
1624
|
+
return value.inflightCredentials;
|
|
1625
|
+
};
|
|
1626
|
+
/**
|
|
1627
|
+
* @internal
|
|
1628
|
+
*/
|
|
1629
|
+
const initStore = (refreshHandler, size = CREDENTIALS_STORE_DEFAULT_SIZE) => {
|
|
1630
|
+
internals.assertValidationError(size > 0, internals.StorageValidationErrorCode.InvalidLocationCredentialsCacheSize);
|
|
1699
1631
|
return {
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1632
|
+
capacity: size,
|
|
1633
|
+
refreshHandler,
|
|
1634
|
+
values: new Map(),
|
|
1703
1635
|
};
|
|
1704
1636
|
};
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
const
|
|
1708
|
-
const
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
const Resolved = useResolvedComposable(ActionCancel, 'ActionCancel');
|
|
1722
|
-
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
1637
|
+
const getCacheValue = (store, location) => {
|
|
1638
|
+
const cacheKey = createCacheKey(location);
|
|
1639
|
+
const cachedValue = store.values.get(cacheKey);
|
|
1640
|
+
const cachedCredentials = cachedValue?.credentials;
|
|
1641
|
+
if (!cachedCredentials) {
|
|
1642
|
+
return null;
|
|
1643
|
+
}
|
|
1644
|
+
// Delete and re-insert to key to map to indicate a latest reference in LRU.
|
|
1645
|
+
store.values.delete(cacheKey);
|
|
1646
|
+
if (!pastTTL(cachedCredentials)) {
|
|
1647
|
+
// TODO(@AllanZhengYP): If the credential is still valid but will expire
|
|
1648
|
+
// soon, we should return credentials AND dispatch a refresh.
|
|
1649
|
+
store.values.set(cacheKey, cachedValue);
|
|
1650
|
+
return cachedCredentials;
|
|
1651
|
+
}
|
|
1652
|
+
return null;
|
|
1723
1653
|
};
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
const
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
type,
|
|
1737
|
-
permissions,
|
|
1738
|
-
bucket,
|
|
1739
|
-
prefix,
|
|
1740
|
-
};
|
|
1741
|
-
return {
|
|
1742
|
-
name: part,
|
|
1743
|
-
...(isCurrent && { isCurrent }),
|
|
1744
|
-
onNavigate: () => {
|
|
1745
|
-
onNavigate?.(destination, destinationPath);
|
|
1746
|
-
},
|
|
1654
|
+
/**
|
|
1655
|
+
* Fetch new credentials value with refresh handler and cache the result in
|
|
1656
|
+
* LRU cache.
|
|
1657
|
+
* @internal
|
|
1658
|
+
*/
|
|
1659
|
+
const fetchNewValue = async (store, location) => {
|
|
1660
|
+
const storeValues = store.values;
|
|
1661
|
+
const key = createCacheKey(location);
|
|
1662
|
+
if (!storeValues.has(key)) {
|
|
1663
|
+
const newStoreValue = {
|
|
1664
|
+
scope: location.scope,
|
|
1665
|
+
permissions: location.permissions,
|
|
1747
1666
|
};
|
|
1667
|
+
setCacheRecord(store, key, newStoreValue);
|
|
1668
|
+
}
|
|
1669
|
+
const storeValue = storeValues.get(key);
|
|
1670
|
+
return dispatchRefresh(store.refreshHandler, storeValue, () => {
|
|
1671
|
+
store.values.delete(key);
|
|
1748
1672
|
});
|
|
1749
1673
|
};
|
|
1750
1674
|
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1675
|
+
/**
|
|
1676
|
+
* Keep all cache records for all instances of credentials store in a singleton
|
|
1677
|
+
* so we can reliably de-reference from the memory when we destroy a store
|
|
1678
|
+
* instance.
|
|
1679
|
+
*/
|
|
1680
|
+
const storeRegistry = new WeakMap();
|
|
1681
|
+
/**
|
|
1682
|
+
* @internal
|
|
1683
|
+
*/
|
|
1684
|
+
const createStore = (refreshHandler, size) => {
|
|
1685
|
+
const storeSymbol = { value: Symbol('LocationCredentialsStore') };
|
|
1686
|
+
storeRegistry.set(storeSymbol, initStore(refreshHandler, size));
|
|
1687
|
+
return storeSymbol;
|
|
1688
|
+
};
|
|
1689
|
+
const getCredentialsStore = (storeSymbol) => {
|
|
1690
|
+
internals.assertValidationError(storeRegistry.has(storeSymbol), internals.StorageValidationErrorCode.LocationCredentialsStoreDestroyed);
|
|
1691
|
+
return storeRegistry.get(storeSymbol);
|
|
1692
|
+
};
|
|
1693
|
+
/**
|
|
1694
|
+
* @internal
|
|
1695
|
+
*/
|
|
1696
|
+
const getValue = async (input) => {
|
|
1697
|
+
const { storeSymbol: storeReference, location, forceRefresh } = input;
|
|
1698
|
+
const store = getCredentialsStore(storeReference);
|
|
1699
|
+
if (!forceRefresh) {
|
|
1700
|
+
const credentials = getCacheValue(store, location);
|
|
1701
|
+
if (credentials !== null) {
|
|
1702
|
+
return { credentials };
|
|
1765
1703
|
}
|
|
1766
1704
|
}
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
const pathParts = trimmedPath ? trimmedPath.split('/') : [];
|
|
1772
|
-
return prefixParts.concat(pathParts);
|
|
1705
|
+
return fetchNewValue(store, location);
|
|
1706
|
+
};
|
|
1707
|
+
const removeStore = (storeSymbol) => {
|
|
1708
|
+
storeRegistry.delete(storeSymbol);
|
|
1773
1709
|
};
|
|
1774
1710
|
|
|
1775
|
-
|
|
1776
|
-
const
|
|
1777
|
-
|
|
1778
|
-
const { actionDestinationLabel, isActionDestinationNavigable, destination } = data;
|
|
1779
|
-
return React__namespace["default"].useMemo(() => {
|
|
1780
|
-
if (!destination?.current) {
|
|
1781
|
-
return { items: [] };
|
|
1782
|
-
}
|
|
1783
|
-
const { current, path } = destination;
|
|
1784
|
-
const destinationParts = getNavigationParts({
|
|
1785
|
-
location: current,
|
|
1786
|
-
path,
|
|
1787
|
-
});
|
|
1788
|
-
return {
|
|
1789
|
-
label: actionDestinationLabel,
|
|
1790
|
-
items: getNavigationItems({
|
|
1791
|
-
location: current,
|
|
1792
|
-
destinationParts,
|
|
1793
|
-
onNavigate: onSelectDestination,
|
|
1794
|
-
}),
|
|
1795
|
-
isNavigable: isActionDestinationNavigable,
|
|
1796
|
-
};
|
|
1797
|
-
}, [
|
|
1798
|
-
actionDestinationLabel,
|
|
1799
|
-
isActionDestinationNavigable,
|
|
1800
|
-
destination,
|
|
1801
|
-
onSelectDestination,
|
|
1802
|
-
]);
|
|
1803
|
-
};
|
|
1804
|
-
|
|
1805
|
-
const ActionDestinationControl = () => {
|
|
1806
|
-
const props = useActionDestination();
|
|
1807
|
-
const Resolved = useResolvedComposable(ActionDestination$1, 'ActionDestination');
|
|
1808
|
-
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
1809
|
-
};
|
|
1810
|
-
|
|
1811
|
-
const useActionExit = () => {
|
|
1812
|
-
const { data: { actionExitLabel: label, isActionExitDisabled: isDisabled }, onActionExit: onExit, } = useControlsContext();
|
|
1813
|
-
return { label, isDisabled, onExit };
|
|
1814
|
-
};
|
|
1815
|
-
|
|
1816
|
-
const ActionExitControl = () => {
|
|
1817
|
-
const props = useActionExit();
|
|
1818
|
-
const Resolved = useResolvedComposable(ActionExit, 'ActionExit');
|
|
1819
|
-
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
1711
|
+
const validateS3Uri = (uri) => {
|
|
1712
|
+
const s3UrlSchemaRegex = /^s3:\/\/[^/]+/;
|
|
1713
|
+
internals.assertValidationError(s3UrlSchemaRegex.test(uri), internals.StorageValidationErrorCode.InvalidS3Uri);
|
|
1820
1714
|
};
|
|
1821
|
-
|
|
1822
|
-
const
|
|
1823
|
-
const
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1715
|
+
const createLocationCredentialsStore = (input) => {
|
|
1716
|
+
const storeSymbol = createStore(input.handler);
|
|
1717
|
+
const store = {
|
|
1718
|
+
getProvider(providerLocation) {
|
|
1719
|
+
const locationCredentialsProvider = async ({ forceRefresh = false, } = {}) => {
|
|
1720
|
+
validateS3Uri(providerLocation.scope);
|
|
1721
|
+
// TODO(@AllanZhengYP): validate the action bucket and paths matches provider scope.
|
|
1722
|
+
return getValue({
|
|
1723
|
+
storeSymbol,
|
|
1724
|
+
location: { ...providerLocation },
|
|
1725
|
+
forceRefresh,
|
|
1726
|
+
});
|
|
1727
|
+
};
|
|
1728
|
+
return locationCredentialsProvider;
|
|
1729
|
+
},
|
|
1730
|
+
destroy() {
|
|
1731
|
+
removeStore(storeSymbol);
|
|
1732
|
+
},
|
|
1828
1733
|
};
|
|
1734
|
+
return store;
|
|
1829
1735
|
};
|
|
1830
1736
|
|
|
1831
|
-
const
|
|
1832
|
-
const
|
|
1833
|
-
const Resolved = useResolvedComposable(ActionStart, 'ActionStart');
|
|
1834
|
-
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
1835
|
-
};
|
|
1836
|
-
|
|
1837
|
-
const useAddFiles = () => {
|
|
1838
|
-
const { data: { addFilesLabel, isAddFilesDisabled }, onAddFiles, } = useControlsContext();
|
|
1737
|
+
const createCredentialsStore = ({ ...input }) => {
|
|
1738
|
+
const { destroy, getProvider } = createLocationCredentialsStore(input);
|
|
1839
1739
|
return {
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1740
|
+
destroy,
|
|
1741
|
+
getCredentials: ({ scope, permissions }) => getProvider({
|
|
1742
|
+
scope,
|
|
1743
|
+
permissions,
|
|
1744
|
+
}),
|
|
1843
1745
|
};
|
|
1844
1746
|
};
|
|
1747
|
+
const isCredentialsStore = (value) => ui.isFunction(value?.getCredentials);
|
|
1748
|
+
function useCredentialsStore({ getLocationCredentials: handler, initialValue, onDestroy, registerAuthListener, }) {
|
|
1749
|
+
const hasExistingStore = isCredentialsStore(initialValue);
|
|
1750
|
+
const [store, setStore] = React__namespace["default"].useState(() => hasExistingStore ? initialValue : createCredentialsStore({ handler }));
|
|
1751
|
+
const { destroy } = store;
|
|
1752
|
+
React__namespace["default"].useEffect(() => {
|
|
1753
|
+
if (hasExistingStore) {
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
const handleAuthStatusChange = () => {
|
|
1757
|
+
destroy();
|
|
1758
|
+
if (ui.isFunction(onDestroy)) {
|
|
1759
|
+
onDestroy();
|
|
1760
|
+
}
|
|
1761
|
+
setStore(createCredentialsStore({ handler }));
|
|
1762
|
+
};
|
|
1763
|
+
// provide `handleAuthStatusChange` to consumer
|
|
1764
|
+
registerAuthListener(handleAuthStatusChange);
|
|
1765
|
+
}, [destroy, handler, hasExistingStore, onDestroy, registerAuthListener]);
|
|
1766
|
+
return store;
|
|
1767
|
+
}
|
|
1845
1768
|
|
|
1846
|
-
const
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
};
|
|
1851
|
-
|
|
1852
|
-
const
|
|
1853
|
-
const
|
|
1854
|
-
return {
|
|
1855
|
-
|
|
1856
|
-
label: addFolderLabel,
|
|
1857
|
-
onAddFolder,
|
|
1858
|
-
};
|
|
1859
|
-
};
|
|
1769
|
+
const ERROR_MESSAGE$3 = '`useCredentials` must be called from within a `CredentialsProvider`.';
|
|
1770
|
+
const { useCredentials, CredentialsContext } = uiReactCore.createContextUtilities({
|
|
1771
|
+
contextName: 'Credentials',
|
|
1772
|
+
errorMessage: ERROR_MESSAGE$3,
|
|
1773
|
+
});
|
|
1774
|
+
function CredentialsProvider({ children, ...props }) {
|
|
1775
|
+
const initialValue = React__namespace["default"].useContext(CredentialsContext);
|
|
1776
|
+
const value = useCredentialsStore({ ...props, initialValue });
|
|
1777
|
+
return (React__namespace["default"].createElement(CredentialsContext.Provider, { value: value }, children));
|
|
1778
|
+
}
|
|
1860
1779
|
|
|
1861
|
-
const
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1780
|
+
const ERROR_MESSAGE$2 = 'Invalid `location` value provided as initial value to `LocationProvider.';
|
|
1781
|
+
const DEFAULT_STATE$1 = {
|
|
1782
|
+
current: undefined,
|
|
1783
|
+
path: '',
|
|
1784
|
+
key: '',
|
|
1865
1785
|
};
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1786
|
+
function handleAction$1(state, action) {
|
|
1787
|
+
switch (action.type) {
|
|
1788
|
+
case 'NAVIGATE': {
|
|
1789
|
+
const { location, path = '' } = action;
|
|
1790
|
+
if (state.current?.id === location.id && state.path === path) {
|
|
1791
|
+
return state;
|
|
1792
|
+
}
|
|
1793
|
+
return {
|
|
1794
|
+
current: location,
|
|
1795
|
+
path,
|
|
1796
|
+
key: `${location.prefix ?? ''}${path}`,
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1799
|
+
case 'RESET_LOCATION': {
|
|
1800
|
+
return DEFAULT_STATE$1;
|
|
1801
|
+
}
|
|
1870
1802
|
}
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
const
|
|
1874
|
-
|
|
1875
|
-
:
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
if (
|
|
1879
|
-
|
|
1803
|
+
}
|
|
1804
|
+
const defaultValue$7 = [DEFAULT_STATE$1, ui.noop];
|
|
1805
|
+
const { LocationContext, useLocation } = uiReactCore.createContextUtilities({
|
|
1806
|
+
contextName: 'Location',
|
|
1807
|
+
defaultValue: defaultValue$7,
|
|
1808
|
+
});
|
|
1809
|
+
function LocationProvider({ children, location, path = '', }) {
|
|
1810
|
+
if (location) {
|
|
1811
|
+
assertLocationData(location, ERROR_MESSAGE$2);
|
|
1880
1812
|
}
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1813
|
+
const value = React__namespace["default"].useReducer(handleAction$1, location
|
|
1814
|
+
? {
|
|
1815
|
+
current: location,
|
|
1816
|
+
path,
|
|
1817
|
+
key: `${location.prefix ?? ''}${path}`,
|
|
1818
|
+
}
|
|
1819
|
+
: DEFAULT_STATE$1);
|
|
1820
|
+
return (React__namespace["default"].createElement(LocationContext.Provider, { value: value }, children));
|
|
1821
|
+
}
|
|
1886
1822
|
|
|
1887
|
-
const
|
|
1888
|
-
|
|
1889
|
-
|
|
1823
|
+
const compareFileItems = (prev, next) => prev.key.localeCompare(next.key);
|
|
1824
|
+
const resolveFiles = (prevItems, files) => {
|
|
1825
|
+
if (!files?.length)
|
|
1826
|
+
return prevItems;
|
|
1827
|
+
// construct `nextItems` and filter out existing `file` entries
|
|
1828
|
+
const nextItems = files.reduce((items, file) => {
|
|
1829
|
+
const { name, webkitRelativePath } = file;
|
|
1830
|
+
return prevItems.some(({ file: existing }) => existing.name === name &&
|
|
1831
|
+
existing.webkitRelativePath === webkitRelativePath)
|
|
1832
|
+
? items
|
|
1833
|
+
: items.concat({
|
|
1834
|
+
key: ui.isEmpty(webkitRelativePath) ? name : webkitRelativePath,
|
|
1835
|
+
id: crypto.randomUUID(),
|
|
1836
|
+
file,
|
|
1837
|
+
});
|
|
1838
|
+
}, []);
|
|
1839
|
+
if (!nextItems.length)
|
|
1840
|
+
return prevItems;
|
|
1841
|
+
if (!prevItems.length) {
|
|
1842
|
+
return nextItems.sort(compareFileItems);
|
|
1890
1843
|
}
|
|
1891
|
-
return
|
|
1844
|
+
return prevItems.concat(nextItems).sort(compareFileItems);
|
|
1892
1845
|
};
|
|
1893
|
-
const
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1846
|
+
const filesReducer = (prevItems, input) => {
|
|
1847
|
+
switch (input.type) {
|
|
1848
|
+
case 'ADD_FILE_ITEMS': {
|
|
1849
|
+
return resolveFiles(prevItems, input.files);
|
|
1850
|
+
}
|
|
1851
|
+
case 'REMOVE_FILE_ITEM': {
|
|
1852
|
+
const filteredItems = prevItems.filter(({ id }) => id !== input.id);
|
|
1853
|
+
return filteredItems.length === prevItems.length
|
|
1854
|
+
? prevItems
|
|
1855
|
+
: filteredItems;
|
|
1856
|
+
}
|
|
1857
|
+
case 'RESET_FILE_ITEMS': {
|
|
1858
|
+
return [];
|
|
1859
|
+
}
|
|
1860
|
+
// TODO: clear message
|
|
1900
1861
|
}
|
|
1901
|
-
return b === undefined ? -1 : a.localeCompare(b);
|
|
1902
1862
|
};
|
|
1903
|
-
const
|
|
1904
|
-
|
|
1905
|
-
|
|
1863
|
+
const parseFileSelectParams = (value) => {
|
|
1864
|
+
if (ui.isUndefined(value))
|
|
1865
|
+
return ['FILE', undefined];
|
|
1866
|
+
if (ui.isString(value))
|
|
1867
|
+
return [value, undefined];
|
|
1868
|
+
const [selectType, ...rest] = value;
|
|
1869
|
+
return [selectType, !rest?.length ? undefined : { accept: rest.join() }];
|
|
1870
|
+
};
|
|
1906
1871
|
|
|
1907
|
-
const
|
|
1908
|
-
|
|
1909
|
-
'
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
];
|
|
1914
|
-
const
|
|
1915
|
-
|
|
1916
|
-
const { data } = useControlsContext();
|
|
1917
|
-
const { tableData } = data;
|
|
1918
|
-
const defaultSortIndex = React__namespace["default"].useMemo(() => tableData?.headers?.findIndex(({ type }) => type === 'sort') ?? -1, [tableData]);
|
|
1919
|
-
const [sortState, setSortState] = React__namespace["default"].useState({
|
|
1920
|
-
index: defaultSortIndex,
|
|
1921
|
-
direction: 'ascending',
|
|
1872
|
+
const defaultValue$6 = [undefined, ui.noop];
|
|
1873
|
+
const { FilesContext, useFiles } = uiReactCore.createContextUtilities({
|
|
1874
|
+
contextName: 'Files',
|
|
1875
|
+
defaultValue: defaultValue$6,
|
|
1876
|
+
});
|
|
1877
|
+
function FilesProvider({ children, }) {
|
|
1878
|
+
const [items, dispatch] = React__namespace["default"].useReducer(filesReducer, []);
|
|
1879
|
+
const [fileInput, handleFileSelect] = internal.useFileSelect((nextFiles) => {
|
|
1880
|
+
dispatch({ type: 'ADD_FILE_ITEMS', files: nextFiles });
|
|
1922
1881
|
});
|
|
1923
|
-
const
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
case 'sort': {
|
|
1927
|
-
return {
|
|
1928
|
-
...header,
|
|
1929
|
-
content: {
|
|
1930
|
-
...header.content,
|
|
1931
|
-
onSort: () => {
|
|
1932
|
-
setSortState({
|
|
1933
|
-
index,
|
|
1934
|
-
direction: sortState.index === index
|
|
1935
|
-
? sortState.direction === 'ascending'
|
|
1936
|
-
? 'descending'
|
|
1937
|
-
: 'ascending'
|
|
1938
|
-
: 'ascending',
|
|
1939
|
-
});
|
|
1940
|
-
},
|
|
1941
|
-
sortDirection: sortState.index === index ? sortState.direction : undefined,
|
|
1942
|
-
},
|
|
1943
|
-
};
|
|
1944
|
-
}
|
|
1945
|
-
case 'checkbox':
|
|
1946
|
-
case 'text':
|
|
1947
|
-
default: {
|
|
1948
|
-
return header;
|
|
1949
|
-
}
|
|
1950
|
-
}
|
|
1951
|
-
}), [sortState, tableData]);
|
|
1952
|
-
const sortedRows = React__namespace["default"].useMemo(() => {
|
|
1953
|
-
// Early return if there is no table data
|
|
1954
|
-
if (!tableData) {
|
|
1955
|
-
return;
|
|
1882
|
+
const handleFilesAction = React__namespace["default"].useCallback((action) => {
|
|
1883
|
+
if (action.type === 'SELECT_FILES') {
|
|
1884
|
+
handleFileSelect(...parseFileSelectParams(action.selectionType));
|
|
1956
1885
|
}
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
return tableData.rows;
|
|
1886
|
+
else {
|
|
1887
|
+
dispatch(action);
|
|
1960
1888
|
}
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
text: [],
|
|
1968
|
-
};
|
|
1969
|
-
tableData.rows.forEach((row) => {
|
|
1970
|
-
const { type } = row.content[index];
|
|
1971
|
-
groupedRows[type].push(row);
|
|
1972
|
-
});
|
|
1973
|
-
const groupOrder = direction === 'ascending' ? GROUP_ORDER : [...GROUP_ORDER].reverse();
|
|
1974
|
-
return groupOrder
|
|
1975
|
-
.map((groupType) => {
|
|
1976
|
-
if (UNSORTABLE_GROUPS.includes(groupType)) {
|
|
1977
|
-
return groupedRows[groupType];
|
|
1978
|
-
}
|
|
1979
|
-
return groupedRows[groupType].sort((rowA, rowB) => {
|
|
1980
|
-
switch (groupType) {
|
|
1981
|
-
case 'button': {
|
|
1982
|
-
return compareButtonData(rowA.content[index], rowB.content[index], direction);
|
|
1983
|
-
}
|
|
1984
|
-
case 'date': {
|
|
1985
|
-
return compareDateData(rowA.content[index], rowB.content[index], direction);
|
|
1986
|
-
}
|
|
1987
|
-
case 'number': {
|
|
1988
|
-
return compareNumberData(rowA.content[index], rowB.content[index], direction);
|
|
1989
|
-
}
|
|
1990
|
-
case 'text':
|
|
1991
|
-
default: {
|
|
1992
|
-
return compareTextData(rowA.content[index], rowB.content[index], direction);
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
1995
|
-
});
|
|
1996
|
-
})
|
|
1997
|
-
.flat();
|
|
1998
|
-
}, [sortState, tableData]);
|
|
1999
|
-
return {
|
|
2000
|
-
headers: mappedHeaders ?? [],
|
|
2001
|
-
rows: sortedRows ?? [],
|
|
2002
|
-
};
|
|
2003
|
-
};
|
|
2004
|
-
|
|
2005
|
-
const DataTableControl = () => {
|
|
2006
|
-
const props = useDataTable();
|
|
2007
|
-
const Resolved = useResolvedComposable(DataTable, 'DataTable');
|
|
2008
|
-
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2009
|
-
};
|
|
2010
|
-
|
|
2011
|
-
/**
|
|
2012
|
-
* This hook, not to be confused with the useDropZone vended from @aws-amplify/ui-react-core, is only intended for use
|
|
2013
|
-
* with its corresponding DropZone control.
|
|
2014
|
-
*/
|
|
2015
|
-
const useDropZone = () => {
|
|
2016
|
-
const { onDropFiles } = useControlsContext();
|
|
2017
|
-
return { onDropFiles };
|
|
2018
|
-
};
|
|
2019
|
-
|
|
2020
|
-
const DropZoneControl = ({ children, }) => {
|
|
2021
|
-
const props = useDropZone();
|
|
2022
|
-
const Resolved = useResolvedComposable(DropZone, 'DropZone');
|
|
2023
|
-
return React__namespace["default"].createElement(Resolved, { ...props }, children);
|
|
2024
|
-
};
|
|
2025
|
-
|
|
2026
|
-
const useOverwriteToggle = () => {
|
|
2027
|
-
const { data: { isOverwritingEnabled, isOverwriteToggleDisabled, overwriteToggleLabel, }, onToggleOverwrite, } = useControlsContext();
|
|
2028
|
-
return {
|
|
2029
|
-
isDisabled: isOverwriteToggleDisabled,
|
|
2030
|
-
isOverwritingEnabled,
|
|
2031
|
-
label: overwriteToggleLabel,
|
|
2032
|
-
onToggle: onToggleOverwrite,
|
|
2033
|
-
};
|
|
2034
|
-
};
|
|
1889
|
+
}, [handleFileSelect]);
|
|
1890
|
+
const value = React__namespace["default"].useMemo(() => [items, handleFilesAction], [items, handleFilesAction]);
|
|
1891
|
+
return (React__namespace["default"].createElement(FilesContext.Provider, { value: value },
|
|
1892
|
+
fileInput,
|
|
1893
|
+
children));
|
|
1894
|
+
}
|
|
2035
1895
|
|
|
2036
|
-
const
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
1896
|
+
const handleAction = (event) => {
|
|
1897
|
+
switch (event.type) {
|
|
1898
|
+
case 'SET_ACTION_TYPE': {
|
|
1899
|
+
return event.actionType;
|
|
1900
|
+
}
|
|
1901
|
+
case 'RESET_ACTION_TYPE': {
|
|
1902
|
+
return undefined;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
2040
1905
|
};
|
|
1906
|
+
function useActionTypeState(initialState) {
|
|
1907
|
+
const [actionType, setActionType] = React__namespace["default"].useState(initialState);
|
|
1908
|
+
const handler = React__namespace["default"].useCallback((action) => setActionType(handleAction(action)), []);
|
|
1909
|
+
return [actionType, handler];
|
|
1910
|
+
}
|
|
2041
1911
|
|
|
2042
|
-
const
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
1912
|
+
const defaultValue$5 = [undefined, ui.noop];
|
|
1913
|
+
const { ActionTypeContext, useActionType } = uiReactCore.createContextUtilities({
|
|
1914
|
+
contextName: 'ActionType',
|
|
1915
|
+
defaultValue: defaultValue$5,
|
|
1916
|
+
});
|
|
1917
|
+
function ActionTypeProvider({ actionType, children, }) {
|
|
1918
|
+
const value = useActionTypeState(actionType);
|
|
1919
|
+
return (React__namespace["default"].createElement(ActionTypeContext.Provider, { value: value }, children));
|
|
1920
|
+
}
|
|
2046
1921
|
|
|
2047
|
-
const
|
|
2048
|
-
|
|
2049
|
-
const Resolved = useResolvedComposable(Message, 'Message');
|
|
2050
|
-
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
1922
|
+
const DEFAULT_STATE = {
|
|
1923
|
+
fileDataItems: undefined,
|
|
2051
1924
|
};
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
1925
|
+
const locationItemsReducer = (prevState, event) => {
|
|
1926
|
+
switch (event.type) {
|
|
1927
|
+
case 'SET_LOCATION_ITEMS': {
|
|
1928
|
+
const { items } = event;
|
|
1929
|
+
if (!items?.length)
|
|
1930
|
+
return prevState;
|
|
1931
|
+
if (!prevState.fileDataItems?.length) {
|
|
1932
|
+
return { fileDataItems: items.map(createFileDataItem) };
|
|
1933
|
+
}
|
|
1934
|
+
const nextFileDataItems = items?.reduce((fileDataItems, data) => prevState.fileDataItems?.some(({ id }) => id === data.id)
|
|
1935
|
+
? fileDataItems
|
|
1936
|
+
: fileDataItems.concat(createFileDataItem(data)), []);
|
|
1937
|
+
if (!nextFileDataItems?.length)
|
|
1938
|
+
return prevState;
|
|
1939
|
+
return {
|
|
1940
|
+
fileDataItems: prevState.fileDataItems.concat(nextFileDataItems),
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
case 'REMOVE_LOCATION_ITEM': {
|
|
1944
|
+
const { id } = event;
|
|
1945
|
+
if (!prevState.fileDataItems)
|
|
1946
|
+
return prevState;
|
|
1947
|
+
const fileDataItems = prevState.fileDataItems.filter((item) => item.id !== id);
|
|
1948
|
+
if (fileDataItems.length === prevState.fileDataItems.length) {
|
|
1949
|
+
return prevState;
|
|
1950
|
+
}
|
|
1951
|
+
return { fileDataItems };
|
|
1952
|
+
}
|
|
1953
|
+
case 'RESET_LOCATION_ITEMS': {
|
|
1954
|
+
return DEFAULT_STATE;
|
|
1955
|
+
}
|
|
2058
1956
|
}
|
|
2059
|
-
const statuses = [
|
|
2060
|
-
{ name: statusDisplayCompletedLabel ?? '', count: statusCounts.COMPLETE },
|
|
2061
|
-
{ name: statusDisplayFailedLabel ?? '', count: statusCounts.FAILED },
|
|
2062
|
-
{ name: statusDisplayCanceledLabel ?? '', count: statusCounts.CANCELED },
|
|
2063
|
-
{ name: statusDisplayQueuedLabel ?? '', count: statusCounts.QUEUED },
|
|
2064
|
-
];
|
|
2065
|
-
return { statuses, total: statusCounts.TOTAL };
|
|
2066
|
-
};
|
|
2067
|
-
|
|
2068
|
-
const StatusDisplayControl = () => {
|
|
2069
|
-
const props = useStatusDisplay();
|
|
2070
|
-
const Resolved = useResolvedComposable(StatusDisplay$1, 'StatusDisplay');
|
|
2071
|
-
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2072
|
-
};
|
|
2073
|
-
|
|
2074
|
-
const useTitle = () => {
|
|
2075
|
-
const { data } = useControlsContext();
|
|
2076
|
-
return {
|
|
2077
|
-
title: data?.title,
|
|
2078
|
-
};
|
|
2079
1957
|
};
|
|
1958
|
+
const defaultValue$4 = [DEFAULT_STATE, ui.noop];
|
|
1959
|
+
const { LocationItemsContext, useLocationItems } = uiReactCore.createContextUtilities({ contextName: 'LocationItems', defaultValue: defaultValue$4 });
|
|
1960
|
+
function LocationItemsProvider({ children, }) {
|
|
1961
|
+
const value = React__namespace["default"].useReducer(locationItemsReducer, DEFAULT_STATE);
|
|
1962
|
+
return (React__namespace["default"].createElement(LocationItemsContext.Provider, { value: value }, children));
|
|
1963
|
+
}
|
|
2080
1964
|
|
|
2081
|
-
|
|
2082
|
-
const
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
1965
|
+
function StoreProvider(props) {
|
|
1966
|
+
const { actionType, children, location, path } = props;
|
|
1967
|
+
return (React__namespace["default"].createElement(FilesProvider, null,
|
|
1968
|
+
React__namespace["default"].createElement(LocationProvider, { location: location, path: path },
|
|
1969
|
+
React__namespace["default"].createElement(LocationItemsProvider, null,
|
|
1970
|
+
React__namespace["default"].createElement(ActionTypeProvider, { actionType: actionType }, children)))));
|
|
1971
|
+
}
|
|
2086
1972
|
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
}
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
}).format(date),
|
|
2118
|
-
};
|
|
2119
|
-
|
|
2120
|
-
const DEFAULT_CREATE_FOLDER_VIEW_DISPLAY_TEXT = {
|
|
2121
|
-
...DEFAULT_ACTION_VIEW_DISPLAY_TEXT,
|
|
2122
|
-
title: 'Create folder',
|
|
2123
|
-
actionStartLabel: 'Create folder',
|
|
2124
|
-
folderNameLabel: 'Folder name',
|
|
2125
|
-
folderNamePlaceholder: 'Folder name cannot contain "/", nor end or start with "."',
|
|
2126
|
-
getValidationMessage: () => 'Folder name cannot contain "/", nor end or start with "."',
|
|
2127
|
-
getActionCompleteMessage: (data) => {
|
|
2128
|
-
const { counts } = data ?? {};
|
|
2129
|
-
const { FAILED, OVERWRITE_PREVENTED } = counts ?? {};
|
|
2130
|
-
if (OVERWRITE_PREVENTED) {
|
|
2131
|
-
return {
|
|
2132
|
-
content: 'A folder already exists with the provided name',
|
|
2133
|
-
type: 'warning',
|
|
2134
|
-
};
|
|
2135
|
-
}
|
|
2136
|
-
if (FAILED) {
|
|
2137
|
-
return {
|
|
2138
|
-
content: 'There was an issue creating the folder.',
|
|
2139
|
-
type: 'error',
|
|
2140
|
-
};
|
|
1973
|
+
function useStore() {
|
|
1974
|
+
const [actionType, dispatchActionType] = useActionType();
|
|
1975
|
+
const [files, dispatchFilesAction] = useFiles();
|
|
1976
|
+
const [location, dispatchLocationAction] = useLocation();
|
|
1977
|
+
const [locationItems, dispatchLocationItemsAction] = useLocationItems();
|
|
1978
|
+
const dispatchHandler = React__namespace["default"].useCallback((action) => {
|
|
1979
|
+
switch (action.type) {
|
|
1980
|
+
case 'ADD_FILE_ITEMS':
|
|
1981
|
+
case 'REMOVE_FILE_ITEM':
|
|
1982
|
+
case 'SELECT_FILES':
|
|
1983
|
+
case 'RESET_FILE_ITEMS': {
|
|
1984
|
+
dispatchFilesAction(action);
|
|
1985
|
+
break;
|
|
1986
|
+
}
|
|
1987
|
+
case 'NAVIGATE':
|
|
1988
|
+
case 'RESET_LOCATION': {
|
|
1989
|
+
dispatchLocationAction(action);
|
|
1990
|
+
break;
|
|
1991
|
+
}
|
|
1992
|
+
case 'SET_LOCATION_ITEMS':
|
|
1993
|
+
case 'REMOVE_LOCATION_ITEM':
|
|
1994
|
+
case 'RESET_LOCATION_ITEMS': {
|
|
1995
|
+
dispatchLocationItemsAction(action);
|
|
1996
|
+
break;
|
|
1997
|
+
}
|
|
1998
|
+
case 'SET_ACTION_TYPE':
|
|
1999
|
+
case 'RESET_ACTION_TYPE': {
|
|
2000
|
+
dispatchActionType(action);
|
|
2001
|
+
break;
|
|
2002
|
+
}
|
|
2141
2003
|
}
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2004
|
+
}, [
|
|
2005
|
+
dispatchActionType,
|
|
2006
|
+
dispatchFilesAction,
|
|
2007
|
+
dispatchLocationAction,
|
|
2008
|
+
dispatchLocationItemsAction,
|
|
2009
|
+
]);
|
|
2010
|
+
return [{ actionType, files, location, locationItems }, dispatchHandler];
|
|
2011
|
+
}
|
|
2145
2012
|
|
|
2146
|
-
const
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
}
|
|
2163
|
-
if (hasError) {
|
|
2164
|
-
return { content: 'Error loading folders.', type: 'error' };
|
|
2165
|
-
}
|
|
2166
|
-
if (hasExhaustedSearch) {
|
|
2167
|
-
return {
|
|
2168
|
-
content: 'Showing results for up to the first 10,000 items.',
|
|
2169
|
-
type: 'info',
|
|
2170
|
-
};
|
|
2171
|
-
}
|
|
2172
|
-
},
|
|
2173
|
-
loadingIndicatorLabel: 'Loading',
|
|
2174
|
-
overwriteWarningMessage: 'Copied files will overwrite existing files at selected destination.',
|
|
2175
|
-
searchPlaceholder: 'Search for folders',
|
|
2176
|
-
getActionCompleteMessage: (data) => {
|
|
2177
|
-
const { counts } = data ?? {};
|
|
2178
|
-
const { COMPLETE, FAILED, TOTAL } = counts ?? {};
|
|
2179
|
-
if (COMPLETE === TOTAL) {
|
|
2180
|
-
return {
|
|
2181
|
-
content: 'All files copied.',
|
|
2182
|
-
type: 'success',
|
|
2183
|
-
};
|
|
2184
|
-
}
|
|
2185
|
-
if (FAILED === TOTAL) {
|
|
2186
|
-
return { content: 'All files failed to copy.', type: 'error' };
|
|
2187
|
-
}
|
|
2013
|
+
const getErrorMessage = (propertyName) => `Unable to resolve credentials due to invalid value of '${propertyName}'`;
|
|
2014
|
+
function useGetActionInputCallback({ accountId, customEndpoint, region, }) {
|
|
2015
|
+
const { getCredentials } = useCredentials();
|
|
2016
|
+
const [{ location }] = useStore();
|
|
2017
|
+
const { current, key } = location;
|
|
2018
|
+
return React__namespace["default"].useCallback((_location) => {
|
|
2019
|
+
// prefer passed in location / prefix over current location in state
|
|
2020
|
+
const location = _location ?? current;
|
|
2021
|
+
// when `location` has been provided as a param, resolve `_prefix` to `location.prefix`.
|
|
2022
|
+
// in the default scenario where `current` is the target `location` use the fully qualified `key`
|
|
2023
|
+
// that includes the default `prefix` and any additional prefixes from navigation
|
|
2024
|
+
const prefix = _location ? _location.prefix : key;
|
|
2025
|
+
assertLocationData(location, getErrorMessage('locationData'));
|
|
2026
|
+
assertPrefix(prefix, getErrorMessage('prefix'));
|
|
2027
|
+
const { bucket, permissions, type } = location;
|
|
2028
|
+
// BUCKET/PREFIX grants end with `*`, but object grants do not.
|
|
2029
|
+
const scope = `s3://${bucket}/${prefix}${type === 'OBJECT' ? '' : '*'}`;
|
|
2188
2030
|
return {
|
|
2189
|
-
|
|
2190
|
-
|
|
2031
|
+
accountId,
|
|
2032
|
+
bucket,
|
|
2033
|
+
credentials: getCredentials({
|
|
2034
|
+
permissions,
|
|
2035
|
+
scope,
|
|
2036
|
+
}),
|
|
2037
|
+
region,
|
|
2038
|
+
customEndpoint,
|
|
2191
2039
|
};
|
|
2192
|
-
},
|
|
2193
|
-
|
|
2194
|
-
|
|
2040
|
+
}, [accountId, current, customEndpoint, getCredentials, key, region]);
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
const ERROR_MESSAGE$1 = '`useGetActionInput` must be called from within a `ConfigurationProvider`.';
|
|
2044
|
+
const { useGetActionInput, GetActionInputContext } = uiReactCore.createContextUtilities({
|
|
2045
|
+
contextName: 'GetActionInput',
|
|
2046
|
+
errorMessage: ERROR_MESSAGE$1,
|
|
2047
|
+
});
|
|
2048
|
+
function GetActionInputProvider({ accountId, children, customEndpoint, region, }) {
|
|
2049
|
+
const value = useGetActionInputCallback({
|
|
2050
|
+
accountId,
|
|
2051
|
+
customEndpoint,
|
|
2052
|
+
region,
|
|
2053
|
+
});
|
|
2054
|
+
return (React__namespace["default"].createElement(GetActionInputContext.Provider, { value: value }, children));
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
const Passthrough = ({ children }) => (React__namespace["default"].createElement(React__namespace["default"].Fragment, null, children));
|
|
2058
|
+
function createConfigurationProvider(input) {
|
|
2059
|
+
const { accountId, ChildComponent, displayName, region, customEndpoint, ...rest } = input;
|
|
2060
|
+
const Child = elements.isComponent(ChildComponent) ? ChildComponent : Passthrough;
|
|
2061
|
+
const Provider = (props) => (React__namespace["default"].createElement(CredentialsProvider, { ...rest },
|
|
2062
|
+
React__namespace["default"].createElement(GetActionInputProvider, { accountId: accountId, region: region, customEndpoint: customEndpoint },
|
|
2063
|
+
React__namespace["default"].createElement(Child, { ...props }))));
|
|
2064
|
+
Provider.displayName = displayName;
|
|
2065
|
+
return Provider;
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
const defaultValue$3 = { data: {} };
|
|
2069
|
+
const { useControlsContext, ControlsContextProvider } = uiReactCore.createContextUtilities({
|
|
2070
|
+
contextName: 'ControlsContext',
|
|
2071
|
+
defaultValue: defaultValue$3,
|
|
2072
|
+
});
|
|
2073
|
+
|
|
2074
|
+
const useActionCancel = () => {
|
|
2075
|
+
const { data: { actionCancelLabel, isActionCancelDisabled }, onActionCancel, } = useControlsContext();
|
|
2076
|
+
return {
|
|
2077
|
+
onCancel: onActionCancel,
|
|
2078
|
+
isDisabled: isActionCancelDisabled,
|
|
2079
|
+
label: actionCancelLabel,
|
|
2080
|
+
};
|
|
2195
2081
|
};
|
|
2196
2082
|
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2083
|
+
function useResolvedComposable(DefaultComposable, name) {
|
|
2084
|
+
const { composables } = useComposables();
|
|
2085
|
+
const Composable = React__namespace["default"].useMemo(() => {
|
|
2086
|
+
const ResolvedComposable = (props) => {
|
|
2087
|
+
const Resolved = composables?.[name] ?? DefaultComposable;
|
|
2088
|
+
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2089
|
+
};
|
|
2090
|
+
ResolvedComposable.displayName = name;
|
|
2091
|
+
return ResolvedComposable;
|
|
2092
|
+
}, [composables, DefaultComposable, name]);
|
|
2093
|
+
return Composable;
|
|
2094
|
+
}
|
|
2095
|
+
|
|
2096
|
+
const ActionCancelControl = () => {
|
|
2097
|
+
const props = useActionCancel();
|
|
2098
|
+
const Resolved = useResolvedComposable(ActionCancel, 'ActionCancel');
|
|
2099
|
+
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2100
|
+
};
|
|
2101
|
+
|
|
2102
|
+
const getNavigationItems = ({ destinationParts, location, onNavigate, }) => {
|
|
2103
|
+
const { bucket, permissions, prefix = '', type } = location;
|
|
2104
|
+
const destinationSubpaths = [];
|
|
2105
|
+
return destinationParts.map((part, index) => {
|
|
2106
|
+
const isCurrent = index === destinationParts.length - 1;
|
|
2107
|
+
if (index !== 0) {
|
|
2108
|
+
destinationSubpaths.push(part);
|
|
2209
2109
|
}
|
|
2110
|
+
const destinationPath = `${destinationSubpaths.concat('').join('/')}`;
|
|
2111
|
+
const destination = {
|
|
2112
|
+
id: crypto.randomUUID(),
|
|
2113
|
+
type,
|
|
2114
|
+
permissions,
|
|
2115
|
+
bucket,
|
|
2116
|
+
prefix,
|
|
2117
|
+
};
|
|
2210
2118
|
return {
|
|
2211
|
-
|
|
2212
|
-
|
|
2119
|
+
name: part,
|
|
2120
|
+
...(isCurrent && { isCurrent }),
|
|
2121
|
+
onNavigate: () => {
|
|
2122
|
+
onNavigate?.(destination, destinationPath);
|
|
2123
|
+
},
|
|
2213
2124
|
};
|
|
2214
|
-
}
|
|
2125
|
+
});
|
|
2215
2126
|
};
|
|
2216
2127
|
|
|
2217
|
-
const
|
|
2218
|
-
const
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2128
|
+
const getNavigationParts = ({ location, path, includeBucketInPrefix, }) => {
|
|
2129
|
+
const { bucket, prefix = '', type } = location;
|
|
2130
|
+
const trimmedPrefix = prefix.endsWith('/') ? prefix.slice(0, -1) : prefix;
|
|
2131
|
+
const trimmedPath = path.endsWith('/') ? path.slice(0, -1) : path;
|
|
2132
|
+
const firstPrefixPart = [];
|
|
2133
|
+
if (type !== 'BUCKET') {
|
|
2134
|
+
if (includeBucketInPrefix) {
|
|
2135
|
+
firstPrefixPart.push(bucket);
|
|
2224
2136
|
}
|
|
2225
|
-
if (
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2137
|
+
if (trimmedPrefix) {
|
|
2138
|
+
if (includeBucketInPrefix) {
|
|
2139
|
+
firstPrefixPart.push('/');
|
|
2140
|
+
}
|
|
2141
|
+
firstPrefixPart.push(trimmedPrefix);
|
|
2230
2142
|
}
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2143
|
+
}
|
|
2144
|
+
const prefixParts = type === 'BUCKET' ? [bucket] : [firstPrefixPart.join('')];
|
|
2145
|
+
if (type === 'BUCKET' && trimmedPrefix) {
|
|
2146
|
+
prefixParts.push(trimmedPrefix);
|
|
2147
|
+
}
|
|
2148
|
+
const pathParts = trimmedPath ? trimmedPath.split('/') : [];
|
|
2149
|
+
return prefixParts.concat(pathParts);
|
|
2150
|
+
};
|
|
2151
|
+
|
|
2152
|
+
// import { ActionDestinationProps } from '../../composables/ActionDestination';
|
|
2153
|
+
const useActionDestination = () => {
|
|
2154
|
+
const { data, onSelectDestination } = useControlsContext();
|
|
2155
|
+
const { actionDestinationLabel, isActionDestinationNavigable, destination } = data;
|
|
2156
|
+
return React__namespace["default"].useMemo(() => {
|
|
2157
|
+
if (!destination?.current) {
|
|
2158
|
+
return { items: [] };
|
|
2236
2159
|
}
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2160
|
+
const { current, path } = destination;
|
|
2161
|
+
const destinationParts = getNavigationParts({
|
|
2162
|
+
location: current,
|
|
2163
|
+
path,
|
|
2164
|
+
});
|
|
2165
|
+
return {
|
|
2166
|
+
label: actionDestinationLabel,
|
|
2167
|
+
items: getNavigationItems({
|
|
2168
|
+
location: current,
|
|
2169
|
+
destinationParts,
|
|
2170
|
+
onNavigate: onSelectDestination,
|
|
2171
|
+
}),
|
|
2172
|
+
isNavigable: isActionDestinationNavigable,
|
|
2173
|
+
};
|
|
2174
|
+
}, [
|
|
2175
|
+
actionDestinationLabel,
|
|
2176
|
+
isActionDestinationNavigable,
|
|
2177
|
+
destination,
|
|
2178
|
+
onSelectDestination,
|
|
2179
|
+
]);
|
|
2180
|
+
};
|
|
2181
|
+
|
|
2182
|
+
const ActionDestinationControl = () => {
|
|
2183
|
+
const props = useActionDestination();
|
|
2184
|
+
const Resolved = useResolvedComposable(ActionDestination$1, 'ActionDestination');
|
|
2185
|
+
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2186
|
+
};
|
|
2187
|
+
|
|
2188
|
+
const useActionExit = () => {
|
|
2189
|
+
const { data: { actionExitLabel: label, isActionExitDisabled: isDisabled }, onActionExit: onExit, } = useControlsContext();
|
|
2190
|
+
return { label, isDisabled, onExit };
|
|
2191
|
+
};
|
|
2192
|
+
|
|
2193
|
+
const ActionExitControl = () => {
|
|
2194
|
+
const props = useActionExit();
|
|
2195
|
+
const Resolved = useResolvedComposable(ActionExit, 'ActionExit');
|
|
2196
|
+
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2197
|
+
};
|
|
2198
|
+
|
|
2199
|
+
const useActionStart = () => {
|
|
2200
|
+
const { data: { actionStartLabel, isActionStartDisabled }, onActionStart, } = useControlsContext();
|
|
2201
|
+
return {
|
|
2202
|
+
label: actionStartLabel,
|
|
2203
|
+
isDisabled: isActionStartDisabled,
|
|
2204
|
+
onStart: onActionStart,
|
|
2205
|
+
};
|
|
2206
|
+
};
|
|
2207
|
+
|
|
2208
|
+
const ActionStartControl = () => {
|
|
2209
|
+
const props = useActionStart();
|
|
2210
|
+
const Resolved = useResolvedComposable(ActionStart, 'ActionStart');
|
|
2211
|
+
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2212
|
+
};
|
|
2213
|
+
|
|
2214
|
+
const useAddFiles = () => {
|
|
2215
|
+
const { data: { addFilesLabel, isAddFilesDisabled }, onAddFiles, } = useControlsContext();
|
|
2216
|
+
return {
|
|
2217
|
+
isDisabled: isAddFilesDisabled,
|
|
2218
|
+
label: addFilesLabel,
|
|
2219
|
+
onAddFiles,
|
|
2220
|
+
};
|
|
2221
|
+
};
|
|
2222
|
+
|
|
2223
|
+
const AddFilesControl = () => {
|
|
2224
|
+
const props = useAddFiles();
|
|
2225
|
+
const Resolved = useResolvedComposable(AddFiles, 'AddFiles');
|
|
2226
|
+
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2227
|
+
};
|
|
2228
|
+
|
|
2229
|
+
const useAddFolder = () => {
|
|
2230
|
+
const { data: { addFolderLabel, isAddFolderDisabled }, onAddFolder, } = useControlsContext();
|
|
2231
|
+
return {
|
|
2232
|
+
isDisabled: isAddFolderDisabled,
|
|
2233
|
+
label: addFolderLabel,
|
|
2234
|
+
onAddFolder,
|
|
2235
|
+
};
|
|
2236
|
+
};
|
|
2237
|
+
|
|
2238
|
+
const AddFolderControl = () => {
|
|
2239
|
+
const props = useAddFolder();
|
|
2240
|
+
const Resolved = useResolvedComposable(AddFolder, 'AddFolder');
|
|
2241
|
+
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2242
|
+
};
|
|
2243
|
+
|
|
2244
|
+
const compareContent$3 = ({ label: a }, { label: b }) => {
|
|
2245
|
+
if (a === undefined) {
|
|
2246
|
+
return b === undefined ? 0 : 1;
|
|
2247
|
+
}
|
|
2248
|
+
return b === undefined ? -1 : a.localeCompare(b);
|
|
2249
|
+
};
|
|
2250
|
+
const compareButtonData = (a, b, direction) => direction === 'ascending'
|
|
2251
|
+
? compareContent$3(a.content, b.content)
|
|
2252
|
+
: compareContent$3(b.content, a.content);
|
|
2253
|
+
|
|
2254
|
+
const compareContent$2 = ({ value: a }, { value: b }) => {
|
|
2255
|
+
if (a === undefined) {
|
|
2256
|
+
return b === undefined ? 0 : 1;
|
|
2257
|
+
}
|
|
2258
|
+
return b === undefined ? -1 : a.getTime() - b.getTime();
|
|
2259
|
+
};
|
|
2260
|
+
const compareDateData = (a, b, direction) => direction === 'ascending'
|
|
2261
|
+
? compareContent$2(a.content, b.content)
|
|
2262
|
+
: compareContent$2(b.content, a.content);
|
|
2263
|
+
|
|
2264
|
+
const compareContent$1 = ({ value: a }, { value: b }) => {
|
|
2265
|
+
if (a === undefined) {
|
|
2266
|
+
return b === undefined ? 0 : 1;
|
|
2267
|
+
}
|
|
2268
|
+
return b === undefined ? -1 : a - b;
|
|
2269
|
+
};
|
|
2270
|
+
const compareNumberData = (a, b, direction) => direction === 'ascending'
|
|
2271
|
+
? compareContent$1(a.content, b.content)
|
|
2272
|
+
: compareContent$1(b.content, a.content);
|
|
2273
|
+
|
|
2274
|
+
const compareContent = ({ text: a }, { text: b }) => {
|
|
2275
|
+
if (a === undefined) {
|
|
2276
|
+
return b === undefined ? 0 : 1;
|
|
2277
|
+
}
|
|
2278
|
+
return b === undefined ? -1 : a.localeCompare(b);
|
|
2279
|
+
};
|
|
2280
|
+
const compareTextData = (a, b, direction) => direction === 'ascending'
|
|
2281
|
+
? compareContent(a.content, b.content)
|
|
2282
|
+
: compareContent(b.content, a.content);
|
|
2283
|
+
|
|
2284
|
+
const GROUP_ORDER = [
|
|
2285
|
+
'checkbox',
|
|
2286
|
+
'button',
|
|
2287
|
+
'date',
|
|
2288
|
+
'number',
|
|
2289
|
+
'text',
|
|
2290
|
+
];
|
|
2291
|
+
const UNSORTABLE_GROUPS = ['checkbox'];
|
|
2292
|
+
const useDataTable = () => {
|
|
2293
|
+
const { data } = useControlsContext();
|
|
2294
|
+
const { tableData } = data;
|
|
2295
|
+
const defaultSortIndex = React__namespace["default"].useMemo(() => tableData?.headers?.findIndex(({ type }) => type === 'sort') ?? -1, [tableData]);
|
|
2296
|
+
const [sortState, setSortState] = React__namespace["default"].useState({
|
|
2297
|
+
index: defaultSortIndex,
|
|
2298
|
+
direction: 'ascending',
|
|
2299
|
+
});
|
|
2300
|
+
const mappedHeaders = React__namespace["default"].useMemo(() => tableData?.headers.map((header, index) => {
|
|
2301
|
+
const { type } = header;
|
|
2302
|
+
switch (type) {
|
|
2303
|
+
case 'sort': {
|
|
2304
|
+
return {
|
|
2305
|
+
...header,
|
|
2306
|
+
content: {
|
|
2307
|
+
...header.content,
|
|
2308
|
+
onSort: () => {
|
|
2309
|
+
setSortState({
|
|
2310
|
+
index,
|
|
2311
|
+
direction: sortState.index === index
|
|
2312
|
+
? sortState.direction === 'ascending'
|
|
2313
|
+
? 'descending'
|
|
2314
|
+
: 'ascending'
|
|
2315
|
+
: 'ascending',
|
|
2316
|
+
});
|
|
2317
|
+
},
|
|
2318
|
+
sortDirection: sortState.index === index ? sortState.direction : undefined,
|
|
2319
|
+
},
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
case 'checkbox':
|
|
2323
|
+
case 'text':
|
|
2324
|
+
default: {
|
|
2325
|
+
return header;
|
|
2326
|
+
}
|
|
2242
2327
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2328
|
+
}), [sortState, tableData]);
|
|
2329
|
+
const sortedRows = React__namespace["default"].useMemo(() => {
|
|
2330
|
+
// Early return if there is no table data
|
|
2331
|
+
if (!tableData) {
|
|
2332
|
+
return;
|
|
2248
2333
|
}
|
|
2249
|
-
//
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
searchSubfoldersToggleLabel: 'Include subfolders',
|
|
2253
|
-
searchPlaceholder: 'Search current folder',
|
|
2254
|
-
tableColumnLastModifiedHeader: 'Last modified',
|
|
2255
|
-
tableColumnNameHeader: 'Name',
|
|
2256
|
-
tableColumnSizeHeader: 'Size',
|
|
2257
|
-
tableColumnTypeHeader: 'Type',
|
|
2258
|
-
selectFileLabel: 'Select file',
|
|
2259
|
-
selectAllFilesLabel: 'Select all files',
|
|
2260
|
-
getActionListItemLabel: (key = '') => {
|
|
2261
|
-
switch (key) {
|
|
2262
|
-
case 'Copy':
|
|
2263
|
-
return 'Copy';
|
|
2264
|
-
case 'Delete':
|
|
2265
|
-
return 'Delete';
|
|
2266
|
-
case 'Create folder':
|
|
2267
|
-
return 'Create folder';
|
|
2268
|
-
case 'Upload':
|
|
2269
|
-
return 'Upload';
|
|
2270
|
-
default:
|
|
2271
|
-
return key;
|
|
2334
|
+
// Return rows as is if there are no sortable columns
|
|
2335
|
+
if (sortState.index < 0) {
|
|
2336
|
+
return tableData.rows;
|
|
2272
2337
|
}
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2338
|
+
const { index, direction } = sortState;
|
|
2339
|
+
const groupedRows = {
|
|
2340
|
+
button: [],
|
|
2341
|
+
checkbox: [],
|
|
2342
|
+
date: [],
|
|
2343
|
+
number: [],
|
|
2344
|
+
text: [],
|
|
2345
|
+
};
|
|
2346
|
+
tableData.rows.forEach((row) => {
|
|
2347
|
+
const { type } = row.content[index];
|
|
2348
|
+
groupedRows[type].push(row);
|
|
2349
|
+
});
|
|
2350
|
+
const groupOrder = direction === 'ascending' ? GROUP_ORDER : [...GROUP_ORDER].reverse();
|
|
2351
|
+
return groupOrder
|
|
2352
|
+
.map((groupType) => {
|
|
2353
|
+
if (UNSORTABLE_GROUPS.includes(groupType)) {
|
|
2354
|
+
return groupedRows[groupType];
|
|
2355
|
+
}
|
|
2356
|
+
return groupedRows[groupType].sort((rowA, rowB) => {
|
|
2357
|
+
switch (groupType) {
|
|
2358
|
+
case 'button': {
|
|
2359
|
+
return compareButtonData(rowA.content[index], rowB.content[index], direction);
|
|
2360
|
+
}
|
|
2361
|
+
case 'date': {
|
|
2362
|
+
return compareDateData(rowA.content[index], rowB.content[index], direction);
|
|
2363
|
+
}
|
|
2364
|
+
case 'number': {
|
|
2365
|
+
return compareNumberData(rowA.content[index], rowB.content[index], direction);
|
|
2366
|
+
}
|
|
2367
|
+
case 'text':
|
|
2368
|
+
default: {
|
|
2369
|
+
return compareTextData(rowA.content[index], rowB.content[index], direction);
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
});
|
|
2373
|
+
})
|
|
2374
|
+
.flat();
|
|
2375
|
+
}, [sortState, tableData]);
|
|
2376
|
+
return {
|
|
2377
|
+
headers: mappedHeaders ?? [],
|
|
2378
|
+
rows: sortedRows ?? [],
|
|
2379
|
+
};
|
|
2380
|
+
};
|
|
2381
|
+
|
|
2382
|
+
const DataTableControl = () => {
|
|
2383
|
+
const props = useDataTable();
|
|
2384
|
+
const Resolved = useResolvedComposable(DataTable, 'DataTable');
|
|
2385
|
+
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2386
|
+
};
|
|
2387
|
+
|
|
2388
|
+
/**
|
|
2389
|
+
* This hook, not to be confused with the useDropZone vended from @aws-amplify/ui-react-core, is only intended for use
|
|
2390
|
+
* with its corresponding DropZone control.
|
|
2391
|
+
*/
|
|
2392
|
+
const useDropZone = () => {
|
|
2393
|
+
const { onDropFiles } = useControlsContext();
|
|
2394
|
+
return { onDropFiles };
|
|
2395
|
+
};
|
|
2396
|
+
|
|
2397
|
+
const DropZoneControl = ({ children, }) => {
|
|
2398
|
+
const props = useDropZone();
|
|
2399
|
+
const Resolved = useResolvedComposable(DropZone, 'DropZone');
|
|
2400
|
+
return React__namespace["default"].createElement(Resolved, { ...props }, children);
|
|
2279
2401
|
};
|
|
2280
2402
|
|
|
2281
|
-
const
|
|
2282
|
-
const
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
return undefined;
|
|
2290
|
-
}
|
|
2291
|
-
if (hasError) {
|
|
2292
|
-
return {
|
|
2293
|
-
type: 'error',
|
|
2294
|
-
content: message ?? DEFAULT_ERROR_MESSAGE,
|
|
2295
|
-
};
|
|
2296
|
-
}
|
|
2297
|
-
if (items?.length === 0 && !hasExhaustedSearch) {
|
|
2298
|
-
return {
|
|
2299
|
-
type: 'info',
|
|
2300
|
-
content: 'No folders or files.',
|
|
2301
|
-
};
|
|
2302
|
-
}
|
|
2303
|
-
if (hasExhaustedSearch) {
|
|
2304
|
-
return {
|
|
2305
|
-
type: 'info',
|
|
2306
|
-
content: `Showing results for up to the first 10,000 items.`,
|
|
2307
|
-
};
|
|
2308
|
-
}
|
|
2309
|
-
// TODO: add more cases as needed
|
|
2310
|
-
return undefined;
|
|
2311
|
-
},
|
|
2312
|
-
getPermissionName: (permissions) => {
|
|
2313
|
-
let text = '';
|
|
2314
|
-
if (permissions.includes('get') || permissions.includes('list')) {
|
|
2315
|
-
text = 'Read';
|
|
2316
|
-
}
|
|
2317
|
-
if (permissions.includes('write') || permissions.includes('delete')) {
|
|
2318
|
-
text = text ? 'Read/Write' : 'Write';
|
|
2319
|
-
}
|
|
2320
|
-
if (!text) {
|
|
2321
|
-
text = permissions.join('/');
|
|
2322
|
-
}
|
|
2323
|
-
return text;
|
|
2324
|
-
},
|
|
2325
|
-
getDownloadLabel: (fileName) => `Download ${fileName}`,
|
|
2326
|
-
tableColumnBucketHeader: 'Bucket',
|
|
2327
|
-
tableColumnFolderHeader: 'Folder',
|
|
2328
|
-
tableColumnPermissionsHeader: 'Permissions',
|
|
2329
|
-
tableColumnActionsHeader: 'Actions',
|
|
2403
|
+
const useOverwriteToggle = () => {
|
|
2404
|
+
const { data: { isOverwritingEnabled, isOverwriteToggleDisabled, overwriteToggleLabel, }, onToggleOverwrite, } = useControlsContext();
|
|
2405
|
+
return {
|
|
2406
|
+
isDisabled: isOverwriteToggleDisabled,
|
|
2407
|
+
isOverwritingEnabled,
|
|
2408
|
+
label: overwriteToggleLabel,
|
|
2409
|
+
onToggle: onToggleOverwrite,
|
|
2410
|
+
};
|
|
2330
2411
|
};
|
|
2331
2412
|
|
|
2332
|
-
const
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
addFilesLabel: 'Add files',
|
|
2337
|
-
addFolderLabel: 'Add folder',
|
|
2338
|
-
getActionCompleteMessage: (data) => {
|
|
2339
|
-
const { counts } = data ?? {};
|
|
2340
|
-
const { COMPLETE, FAILED, OVERWRITE_PREVENTED, CANCELED, TOTAL } = counts ?? {};
|
|
2341
|
-
const hasPreventedOverwrite = !!OVERWRITE_PREVENTED;
|
|
2342
|
-
const hasFailure = !!FAILED;
|
|
2343
|
-
const hasSuccess = !!COMPLETE;
|
|
2344
|
-
const hasCanceled = !!CANCELED;
|
|
2345
|
-
const type = hasFailure
|
|
2346
|
-
? 'error'
|
|
2347
|
-
: hasPreventedOverwrite || hasCanceled
|
|
2348
|
-
? 'warning'
|
|
2349
|
-
: 'success';
|
|
2350
|
-
const preventedOverwriteMessage = hasPreventedOverwrite
|
|
2351
|
-
? [
|
|
2352
|
-
'Overwrite prevented for',
|
|
2353
|
-
OVERWRITE_PREVENTED === TOTAL ? 'all' : String(OVERWRITE_PREVENTED),
|
|
2354
|
-
OVERWRITE_PREVENTED > 1 || OVERWRITE_PREVENTED === TOTAL
|
|
2355
|
-
? `files`
|
|
2356
|
-
: 'file',
|
|
2357
|
-
].join(' ')
|
|
2358
|
-
: undefined;
|
|
2359
|
-
const canceledMessage = hasCanceled
|
|
2360
|
-
? [
|
|
2361
|
-
CANCELED === TOTAL ? 'All' : String(CANCELED),
|
|
2362
|
-
CANCELED > 1 || CANCELED === TOTAL ? `uploads` : 'upload',
|
|
2363
|
-
'canceled',
|
|
2364
|
-
].join(' ')
|
|
2365
|
-
: undefined;
|
|
2366
|
-
const failedMessage = hasFailure
|
|
2367
|
-
? [
|
|
2368
|
-
FAILED === TOTAL ? 'All' : String(FAILED),
|
|
2369
|
-
FAILED > 1 || FAILED === TOTAL ? `files` : 'file',
|
|
2370
|
-
'failed to upload',
|
|
2371
|
-
].join(' ')
|
|
2372
|
-
: undefined;
|
|
2373
|
-
const completedMessage = hasSuccess
|
|
2374
|
-
? [
|
|
2375
|
-
COMPLETE === TOTAL ? 'All' : String(COMPLETE),
|
|
2376
|
-
COMPLETE > 1 || COMPLETE === TOTAL ? `files` : 'file',
|
|
2377
|
-
'uploaded',
|
|
2378
|
-
].join(' ')
|
|
2379
|
-
: undefined;
|
|
2380
|
-
const messages = [
|
|
2381
|
-
preventedOverwriteMessage,
|
|
2382
|
-
failedMessage,
|
|
2383
|
-
canceledMessage,
|
|
2384
|
-
completedMessage,
|
|
2385
|
-
].filter(Boolean);
|
|
2386
|
-
if (messages.length > 0) {
|
|
2387
|
-
return {
|
|
2388
|
-
content: messages.join(', ') + '.',
|
|
2389
|
-
type,
|
|
2390
|
-
};
|
|
2391
|
-
}
|
|
2392
|
-
return { content: 'All files uploaded.', type };
|
|
2393
|
-
},
|
|
2394
|
-
getFilesValidationMessage: (data) => {
|
|
2395
|
-
if (!data?.invalidFiles) {
|
|
2396
|
-
return undefined;
|
|
2397
|
-
}
|
|
2398
|
-
const tooBigFileNames = data.invalidFiles
|
|
2399
|
-
.filter(({ file }) => isFileTooBig(file))
|
|
2400
|
-
.map(({ file }) => file.name)
|
|
2401
|
-
.join(', ');
|
|
2402
|
-
if (tooBigFileNames) {
|
|
2403
|
-
return {
|
|
2404
|
-
content: `Files larger than 160GB cannot be added to the upload queue: ${tooBigFileNames}`,
|
|
2405
|
-
type: 'warning',
|
|
2406
|
-
};
|
|
2407
|
-
}
|
|
2408
|
-
return undefined;
|
|
2409
|
-
},
|
|
2410
|
-
statusDisplayOverwritePreventedLabel: 'Overwrite prevented',
|
|
2411
|
-
overwriteToggleLabel: 'Overwrite existing files',
|
|
2413
|
+
const OverwriteToggleControl = () => {
|
|
2414
|
+
const props = useOverwriteToggle();
|
|
2415
|
+
const Resolved = useResolvedComposable(OverwriteToggle$1, 'OverwriteToggle');
|
|
2416
|
+
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2412
2417
|
};
|
|
2413
2418
|
|
|
2414
|
-
const
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
DeleteView: DEFAULT_DELETE_VIEW_DISPLAY_TEXT,
|
|
2418
|
-
LocationDetailView: DEFAULT_LOCATION_DETAIL_VIEW_DISPLAY_TEXT,
|
|
2419
|
-
LocationsView: DEFAULT_LOCATIONS_VIEW_DISPLAY_TEXT,
|
|
2420
|
-
UploadView: DEFAULT_UPLOAD_VIEW_DISPLAY_TEXT,
|
|
2419
|
+
const useMessage = () => {
|
|
2420
|
+
const { data: { message = {} }, } = useControlsContext();
|
|
2421
|
+
return message;
|
|
2421
2422
|
};
|
|
2422
2423
|
|
|
2423
|
-
const
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
});
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
const {
|
|
2424
|
+
const MessageControl = () => {
|
|
2425
|
+
const props = useMessage();
|
|
2426
|
+
const Resolved = useResolvedComposable(Message, 'Message');
|
|
2427
|
+
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2428
|
+
};
|
|
2429
|
+
|
|
2430
|
+
const useStatusDisplay = () => {
|
|
2431
|
+
const { data } = useControlsContext();
|
|
2432
|
+
const { statusCounts, statusDisplayCanceledLabel, statusDisplayCompletedLabel, statusDisplayFailedLabel, statusDisplayQueuedLabel, } = data;
|
|
2433
|
+
if (!statusCounts?.TOTAL) {
|
|
2434
|
+
return { statuses: [], total: 0 };
|
|
2435
|
+
}
|
|
2436
|
+
const statuses = [
|
|
2437
|
+
{ name: statusDisplayCompletedLabel ?? '', count: statusCounts.COMPLETE },
|
|
2438
|
+
{ name: statusDisplayFailedLabel ?? '', count: statusCounts.FAILED },
|
|
2439
|
+
{ name: statusDisplayCanceledLabel ?? '', count: statusCounts.CANCELED },
|
|
2440
|
+
{ name: statusDisplayQueuedLabel ?? '', count: statusCounts.QUEUED },
|
|
2441
|
+
];
|
|
2442
|
+
return { statuses, total: statusCounts.TOTAL };
|
|
2443
|
+
};
|
|
2444
|
+
|
|
2445
|
+
const StatusDisplayControl = () => {
|
|
2446
|
+
const props = useStatusDisplay();
|
|
2447
|
+
const Resolved = useResolvedComposable(StatusDisplay$1, 'StatusDisplay');
|
|
2448
|
+
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2449
|
+
};
|
|
2450
|
+
|
|
2451
|
+
const useTitle = () => {
|
|
2452
|
+
const { data } = useControlsContext();
|
|
2432
2453
|
return {
|
|
2433
|
-
|
|
2434
|
-
CreateFolderView: {
|
|
2435
|
-
...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.CreateFolderView,
|
|
2436
|
-
...CreateFolderView,
|
|
2437
|
-
},
|
|
2438
|
-
DeleteView: {
|
|
2439
|
-
...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.DeleteView,
|
|
2440
|
-
...DeleteView,
|
|
2441
|
-
},
|
|
2442
|
-
LocationDetailView: {
|
|
2443
|
-
...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.LocationDetailView,
|
|
2444
|
-
...LocationDetailView,
|
|
2445
|
-
},
|
|
2446
|
-
LocationsView: {
|
|
2447
|
-
...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.LocationsView,
|
|
2448
|
-
...LocationsView,
|
|
2449
|
-
},
|
|
2450
|
-
UploadView: {
|
|
2451
|
-
...DEFAULT_STORAGE_BROWSER_DISPLAY_TEXT.UploadView,
|
|
2452
|
-
...UploadView,
|
|
2453
|
-
},
|
|
2454
|
+
title: data?.title,
|
|
2454
2455
|
};
|
|
2455
|
-
}
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
const
|
|
2459
|
-
|
|
2460
|
-
}
|
|
2456
|
+
};
|
|
2457
|
+
|
|
2458
|
+
const TitleControl = () => {
|
|
2459
|
+
const props = useTitle();
|
|
2460
|
+
const Resolved = useResolvedComposable(Title$1, 'Title');
|
|
2461
|
+
return React__namespace["default"].createElement(Resolved, { ...props });
|
|
2462
|
+
};
|
|
2461
2463
|
|
|
2462
2464
|
const getActionIcon = (status) => {
|
|
2463
2465
|
switch (status) {
|
|
@@ -4763,6 +4765,23 @@ function useViews() {
|
|
|
4763
4765
|
};
|
|
4764
4766
|
}
|
|
4765
4767
|
|
|
4768
|
+
const DEFAULT_VIEW_HOOKS = {
|
|
4769
|
+
Copy: useCopyView,
|
|
4770
|
+
CreateFolder: useCreateFolderView,
|
|
4771
|
+
Delete: useDeleteView,
|
|
4772
|
+
LocationDetail: useLocationDetailView,
|
|
4773
|
+
Locations: useLocationsView,
|
|
4774
|
+
Upload: useUploadView,
|
|
4775
|
+
};
|
|
4776
|
+
const isUseViewType = (value) => !!DEFAULT_VIEW_HOOKS?.[value];
|
|
4777
|
+
// @ts-expect-error
|
|
4778
|
+
const useView = (type) => {
|
|
4779
|
+
if (!isUseViewType(type)) {
|
|
4780
|
+
throw new Error(`Value of \`${type}\` cannot be used to index \`useView\``);
|
|
4781
|
+
}
|
|
4782
|
+
return DEFAULT_VIEW_HOOKS[type]();
|
|
4783
|
+
};
|
|
4784
|
+
|
|
4766
4785
|
/**
|
|
4767
4786
|
* Handles default `StorageBrowser` behavior:
|
|
4768
4787
|
* - render `LocationsView` on init
|
|
@@ -4783,23 +4802,6 @@ function StorageBrowserDefault() {
|
|
|
4783
4802
|
return React__namespace["default"].createElement(LocationsView, null);
|
|
4784
4803
|
}
|
|
4785
4804
|
|
|
4786
|
-
const USE_VIEW_HOOKS = {
|
|
4787
|
-
Copy: useCopyView,
|
|
4788
|
-
CreateFolder: useCreateFolderView,
|
|
4789
|
-
Delete: useDeleteView,
|
|
4790
|
-
LocationDetail: useLocationDetailView,
|
|
4791
|
-
Locations: useLocationsView,
|
|
4792
|
-
Upload: useUploadView,
|
|
4793
|
-
};
|
|
4794
|
-
const isUseViewType = (value) => !!USE_VIEW_HOOKS?.[value];
|
|
4795
|
-
// @ts-expect-error
|
|
4796
|
-
const useView = (type) => {
|
|
4797
|
-
if (!isUseViewType(type)) {
|
|
4798
|
-
throw new Error(`Value of \`${type}\` cannot be used to index \`useView\``);
|
|
4799
|
-
}
|
|
4800
|
-
return USE_VIEW_HOOKS[type]();
|
|
4801
|
-
};
|
|
4802
|
-
|
|
4803
4805
|
function createStorageBrowser(input) {
|
|
4804
4806
|
assertRegisterAuthListener(input.config.registerAuthListener);
|
|
4805
4807
|
const { accountId, customEndpoint, registerAuthListener, getLocationCredentials, region, } = input.config;
|