@zjlab-fe/data-hub-ui 0.21.6 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/types/components/tip-tap/extensions/index.d.ts +1 -1
- package/es/assets/cardImg.png.js +3 -0
- package/es/assets/closeBtn.png.js +3 -0
- package/es/assets/icon-tag-header.png.js +3 -0
- package/es/assets/source.png.js +3 -0
- package/es/components/FileUploader/FileUploadModal.js +24 -0
- package/es/components/FileUploader/FileUploader.js +20 -0
- package/es/components/FileUploader/FileUploaderImpl/FileUploader.js +34 -0
- package/es/components/FileUploader/FileUploaderImpl/FileUploaderContextProvider.js +46 -0
- package/es/components/FileUploader/FileUploaderImpl/FileUploaderFileReceiver.css.js +36 -0
- package/es/components/FileUploader/FileUploaderImpl/FileUploaderFileReceiver.js +57 -0
- package/es/components/FileUploader/FileUploaderImpl/FileUploaderFileReceiverContainer.js +24 -0
- package/es/components/FileUploader/FileUploaderImpl/FileUploaderFileReceiverDesc.js +9 -0
- package/es/components/FileUploader/FileUploaderImpl/FileUploaderList.js +46 -0
- package/es/components/FileUploader/FileUploaderImpl/FileUploaderListItem.js +46 -0
- package/es/components/FileUploader/FileUploaderImpl/FileUploaderListItemFail.js +21 -0
- package/es/components/FileUploader/FileUploaderImpl/FileUploaderListItemPaused.js +20 -0
- package/es/components/FileUploader/FileUploaderImpl/FileUploaderListItemUploading.js +43 -0
- package/es/components/FileUploader/FileUploaderImpl/FileUploaderlListItemFinished.js +32 -0
- package/es/components/FileUploader/Locale/translations.js +52 -0
- package/es/components/FileUploader/Locale/useTranslate.js +16 -0
- package/es/components/FileUploader/UploadStoreProvider/UploadStoreProvider.js +31 -0
- package/es/components/FileUploader/UploadStoreProvider/uploadStoreReducer.js +62 -0
- package/es/components/FileUploader/UploadStoreProvider/useOnFileAllUploaded.js +31 -0
- package/es/components/FileUploader/UploadStoreProvider/useSaveUnfinishedUploads.js +42 -0
- package/es/components/FileUploader/UploadStoreProvider/useSetupMergingEvent.js +45 -0
- package/es/components/FileUploader/hooks/useCancelUpload.js +40 -0
- package/es/components/FileUploader/hooks/useCurrentFile.js +61 -0
- package/es/components/FileUploader/hooks/useCurrentFileOnUploading.js +52 -0
- package/es/components/FileUploader/hooks/useCurrentFileSetupAndBackUp.js +39 -0
- package/es/components/FileUploader/hooks/useFormatFiles.js +64 -0
- package/es/components/FileUploader/hooks/useFormatFilesFindDuplicates.js +75 -0
- package/es/components/FileUploader/hooks/useFormatFilesRemoveDuplicates.js +21 -0
- package/es/components/FileUploader/hooks/useHandleChunkCompletion.js +31 -0
- package/es/components/FileUploader/hooks/useOnMounted.js +24 -0
- package/es/components/FileUploader/hooks/useOnMountedResumeUnfinished.js +58 -0
- package/es/components/FileUploader/hooks/useRetryUpload.js +33 -0
- package/es/components/FileUploader/hooks/useUploadChunk.js +46 -0
- package/es/components/FileUploader/icons/CloseFilled.js +7 -0
- package/es/components/FileUploader/icons/UnkownFileIcon.js +7 -0
- package/es/components/FileUploader/icons/UploadIcon.js +7 -0
- package/es/components/FileUploader/index.css.js +532 -0
- package/es/components/FileUploader/index.js +23 -0
- package/es/components/FileUploader/types.js +13 -0
- package/es/components/FileUploader/utils/calcDisplaySize.js +20 -0
- package/es/components/FileUploader/utils/checkExpiryTime.js +15 -0
- package/es/components/FileUploader/utils/createFileToUpload.js +60 -0
- package/es/components/FileUploader/utils/fileDB/addFilesToIndexdb.js +14 -0
- package/es/components/FileUploader/utils/fileDB/deleteFileInStore.js +18 -0
- package/es/components/FileUploader/utils/fileDB/deleteObjectStore.js +25 -0
- package/es/components/FileUploader/utils/fileDB/index.js +77 -0
- package/es/components/FileUploader/utils/fileDB/openDBAndRetrieveData.js +90 -0
- package/es/components/FileUploader/utils/fileDB/retrieveExistingData.js +49 -0
- package/es/components/FileUploader/utils/modalConfig.css.js +20 -0
- package/es/components/FileUploader/utils/modalConfig.js +32 -0
- package/es/components/FileUploader/utils/retrieveAllFiles.js +70 -0
- package/es/components/FileUploader/utils/splitFileIntoChunk.js +15 -0
- package/es/components/SDK-modal/document-link.js +8 -0
- package/es/components/SDK-modal/index.js +25 -0
- package/es/components/SDK-modal/index.module.scss.js +49 -0
- package/es/components/SDK-modal/inner-modal.js +59 -0
- package/es/components/SDK-modal/locale/translations.js +17 -0
- package/es/components/apply-perm-modal/index.js +53 -0
- package/es/components/apply-perm-modal/index.module.scss.js +96 -0
- package/es/components/auth-tag/index.js +23 -0
- package/es/components/bench-card/index.js +20 -0
- package/es/components/bench-card/index.module.scss.js +48 -0
- package/es/components/confirm-again/index.js +15 -0
- package/es/components/confirm-again/index.scss.js +37 -0
- package/es/components/copy/index.js +41 -0
- package/es/components/copy/index.module.scss.js +15 -0
- package/es/components/corpus-card/index.js +88 -0
- package/es/components/corpus-card/index.module.scss.js +171 -0
- package/es/components/feature-card/classNames.js +72 -0
- package/es/components/feature-card/components.js +48 -0
- package/es/components/feature-card/context.js +13 -0
- package/es/components/feature-card/index.css.js +655 -0
- package/es/components/feature-card/index.js +48 -0
- package/es/components/file-preview/data-table/index.js +94 -0
- package/es/components/file-preview/data-table/index.module.scss.js +54 -0
- package/es/components/file-preview/excel-preview/index.js +132 -0
- package/es/components/file-preview/excel-preview/index.module.scss.js +11 -0
- package/es/components/file-preview/excel-preview/utils.js +46 -0
- package/es/components/file-preview/img-preview/index.js +18 -0
- package/es/components/file-preview/img-preview/index.module.scss.js +17 -0
- package/es/components/file-preview/index.js +124 -0
- package/es/components/file-preview/index.module.scss.js +32 -0
- package/es/components/file-preview/json-preview/index.js +166 -0
- package/es/components/file-preview/markdown-preview/index.js +53 -0
- package/es/components/file-preview/markdown-preview/index.module.scss.js +1055 -0
- package/es/components/file-preview/parquet-preview/index.js +54 -0
- package/es/components/file-preview/pdf-preview/index.js +215 -0
- package/es/components/file-preview/pdf-preview/index.module.scss.js +16 -0
- package/es/components/file-preview/table-preview/index.js +43 -0
- package/es/components/file-preview/table-preview/index.module.scss.js +11 -0
- package/es/components/file-preview/txt-preview/index.js +24 -0
- package/es/components/file-preview/util.js +94 -0
- package/es/components/file-uploader/components/circle-progress.css.js +39 -0
- package/es/components/file-uploader/components/circle-progress.js +12 -0
- package/es/components/file-uploader/components/hooks/use-drop-zone.js +74 -0
- package/es/components/file-uploader/components/hooks/use-file-validation.js +52 -0
- package/es/components/file-uploader/components/icons.js +19 -0
- package/es/components/file-uploader/components/theme.css.js +64 -0
- package/es/components/file-uploader/components/uploader-drop-zone.css.js +66 -0
- package/es/components/file-uploader/components/uploader-drop-zone.js +74 -0
- package/es/components/file-uploader/components/uploader-file-item.css.js +186 -0
- package/es/components/file-uploader/components/uploader-file-item.js +63 -0
- package/es/components/file-uploader/components/uploader-file-list.css.js +43 -0
- package/es/components/file-uploader/components/uploader-file-list.js +27 -0
- package/es/components/file-uploader/components/uploader.css.js +20 -0
- package/es/components/file-uploader/components/uploader.js +51 -0
- package/es/components/file-uploader/components/uploading-status.css.js +52 -0
- package/es/components/file-uploader/components/uploading-status.js +54 -0
- package/es/components/file-uploader/components/utils/directory-reader.js +65 -0
- package/es/components/file-uploader/constants.js +31 -0
- package/es/components/file-uploader/context/action-registry.js +69 -0
- package/es/components/file-uploader/context/file-state-store.js +83 -0
- package/es/components/file-uploader/context/handler-registry.js +119 -0
- package/es/components/file-uploader/context/upload-context.js +36 -0
- package/es/components/file-uploader/context/upload-provider.js +77 -0
- package/es/components/file-uploader/context/upload-reducer.js +21 -0
- package/es/components/file-uploader/context/use-upload-engine.js +440 -0
- package/es/components/file-uploader/engine/queue-callbacks.js +257 -0
- package/es/components/file-uploader/engine/queue-manager.js +195 -0
- package/es/components/file-uploader/engine/queue-uploader.js +25 -0
- package/es/components/file-uploader/hooks/use-upload.js +327 -0
- package/es/components/file-uploader/utils/chunk-handlers.js +66 -0
- package/es/components/file-uploader/utils/ensure-handlers.js +53 -0
- package/es/components/file-uploader/utils/error-handler.js +84 -0
- package/es/components/file-uploader/utils/upload-controllers.js +43 -0
- package/es/components/file-uploader/utils/upload-handler.js +68 -0
- package/es/components/file-uploader/utils/validate-handlers.js +16 -0
- package/es/components/floating-label-input/index.js +46 -0
- package/es/components/floating-label-input/index.module.scss.js +30 -0
- package/es/components/floating-layer/index.js +15 -0
- package/es/components/floating-layer/index.scss.js +31 -0
- package/es/components/header/index.js +10 -0
- package/es/components/header/index.module.scss.js +16 -0
- package/es/components/input-tag/index.js +65 -0
- package/es/components/menu/index.js +96 -0
- package/es/components/menu/index.module.scss.js +188 -0
- package/es/components/model-card/index.js +20 -0
- package/es/components/model-card/index.module.scss.js +116 -0
- package/es/components/notion-editor/code-block/index.js +18 -0
- package/es/components/notion-editor/code-block/languageDetector.js +134 -0
- package/es/components/notion-editor/code-block/languages.js +42 -0
- package/es/components/notion-editor/index.js +258 -0
- package/es/components/notion-editor/index.scss.js +421 -0
- package/es/components/notion-editor/parseMarkdown.js +184 -0
- package/es/components/operator-chain/components/AddButton.js +20 -0
- package/es/components/operator-chain/components/IOCard.js +8 -0
- package/es/components/operator-chain/components/SingleCard.js +12 -0
- package/es/components/operator-chain/components/VirtualDropdown.js +56 -0
- package/es/components/operator-chain/index.js +83 -0
- package/es/components/operator-chain/index.module.scss.js +232 -0
- package/es/components/permission-editor/index.js +9 -0
- package/es/components/permission-editor/permissionEditModal/index.js +37 -0
- package/es/components/permission-editor/permissionEditor/index.js +253 -0
- package/es/components/permission-editor/permissionEditor/index.module.scss.js +22 -0
- package/es/components/permission-editor/permissionViewPopover/index.js +9 -0
- package/es/components/popover-select/index.js +42 -0
- package/es/components/popover-select/index.module.scss.js +22 -0
- package/es/components/radio-card/index.js +24 -0
- package/es/components/radio-card/index.scss.js +58 -0
- package/es/components/section-heading/classNames.js +22 -0
- package/es/components/section-heading/components.js +27 -0
- package/es/components/section-heading/context.js +13 -0
- package/es/components/section-heading/index.css.js +648 -0
- package/es/components/section-heading/index.js +37 -0
- package/es/components/status-tag/index.js +31 -0
- package/es/components/tag-group-filter/dataSizeFilter.js +100 -0
- package/es/components/tag-group-filter/index.js +53 -0
- package/es/components/tag-group-filter/index.module.scss.js +134 -0
- package/es/components/tag-group-filter/tagFilter.js +19 -0
- package/es/components/tag-view/index.js +138 -0
- package/es/components/tag-view/index.module.scss.js +56 -0
- package/es/components/tag-view/useSize.js +41 -0
- package/es/components/tip-tap/bubble-menu.js +47 -0
- package/es/components/tip-tap/editor.js +34 -0
- package/es/components/tip-tap/extensions/index.js +133 -0
- package/es/components/tip-tap/extensions/markdown-paste.js +148 -0
- package/es/components/tip-tap/icons/index.js +29 -0
- package/es/components/tip-tap/reader.js +15 -0
- package/es/components/tip-tap/styles/editor.css.js +863 -0
- package/es/components/tip-tap/toolbar/components/color-picker.js +76 -0
- package/es/components/tip-tap/toolbar/components/heading-dropdown.js +44 -0
- package/es/components/tip-tap/toolbar/components/image-button.js +26 -0
- package/es/components/tip-tap/toolbar/components/link-button.js +107 -0
- package/es/components/tip-tap/toolbar/components/table-button.js +37 -0
- package/es/components/tip-tap/toolbar/components/toolbar-button.js +7 -0
- package/es/components/tip-tap/toolbar/index.js +39 -0
- package/es/components/uploadDrawer/UploadStoreProvider.js +61 -0
- package/es/components/uploadDrawer/fileUploadDrawer.js +66 -0
- package/es/components/uploadDrawer/fileUploadDrawerFileDragger.js +71 -0
- package/es/components/uploadDrawer/fileUploadDrawerList.js +54 -0
- package/es/components/uploadDrawer/fileUploadDrawerListProgressButton.js +54 -0
- package/es/components/uploadDrawer/hooks/useCancelUpload.js +37 -0
- package/es/components/uploadDrawer/hooks/useFilterFiles.js +107 -0
- package/es/components/uploadDrawer/hooks/useFinishUpload.js +33 -0
- package/es/components/uploadDrawer/hooks/useFormatFiles.js +54 -0
- package/es/components/uploadDrawer/hooks/useProgressButtonRef.js +22 -0
- package/es/components/uploadDrawer/hooks/useRemoveFilesInUploadQueue.js +21 -0
- package/es/components/uploadDrawer/hooks/useResumeUnfinishedUploads.js +52 -0
- package/es/components/uploadDrawer/hooks/useRetryUpload.js +35 -0
- package/es/components/uploadDrawer/hooks/useSaveUnfinishedUploads.js +42 -0
- package/es/components/uploadDrawer/hooks/useSetupUpload.js +80 -0
- package/es/components/uploadDrawer/hooks/useUploadFileToOSS.js +23 -0
- package/es/components/uploadDrawer/index.css.js +511 -0
- package/es/components/uploadDrawer/index.js +43 -0
- package/es/components/uploadDrawer/utils/calcDisplaySize.js +19 -0
- package/es/components/uploadDrawer/utils/constant.js +16 -0
- package/es/components/uploadDrawer/utils/createFileToUpload.js +51 -0
- package/es/components/uploadDrawer/utils/fileDB/deleteFileInStore.js +28 -0
- package/es/components/uploadDrawer/utils/fileDB/deleteObjectStore.js +28 -0
- package/es/components/uploadDrawer/utils/fileDB/handleAddFilesToDB.js +37 -0
- package/es/components/uploadDrawer/utils/fileDB/index.js +9 -0
- package/es/components/uploadDrawer/utils/fileDB/openDB.js +111 -0
- package/es/components/uploadDrawer/utils/retrieveAllFiles.js +110 -0
- package/es/components/uploadDrawer/utils/shouldCreateNewFile.js +13 -0
- package/es/components/uploadDrawer/utils/splitFileIntoChunk.js +15 -0
- package/es/index.js +42 -1
- package/es/lib/utils.js +8 -0
- package/lib/index.js +1 -1
- package/package.json +164 -154
- package/rollup.es.config.mjs +177 -0
- package/tsconfig.rollup.json +7 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
import './uploading-status.css.js';
|
|
4
|
+
|
|
5
|
+
const UPLOADING_MESSAGES = [
|
|
6
|
+
'上传中',
|
|
7
|
+
'传输数据中',
|
|
8
|
+
'正在处理',
|
|
9
|
+
'上传进行中',
|
|
10
|
+
'传输中,请稍候',
|
|
11
|
+
'文件上传中',
|
|
12
|
+
'数据传输中',
|
|
13
|
+
'上传处理中',
|
|
14
|
+
];
|
|
15
|
+
function UploadingStatus({ interval = 10000, className = '' }) {
|
|
16
|
+
const [messageIndex, setMessageIndex] = useState(0);
|
|
17
|
+
const [animationState, setAnimationState] = useState('slide-in');
|
|
18
|
+
const [dots, setDots] = useState('.');
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const scheduleNextChange = () => {
|
|
21
|
+
// Random interval: ±30% of base interval (e.g., 3500-6500ms for 5000ms base)
|
|
22
|
+
const randomInterval = interval + (Math.random() - 0.5) * interval * 0.6;
|
|
23
|
+
const timer = setTimeout(() => {
|
|
24
|
+
// Start slide-up animation
|
|
25
|
+
setAnimationState('slide-up');
|
|
26
|
+
// Change message after slide-up completes
|
|
27
|
+
setTimeout(() => {
|
|
28
|
+
setMessageIndex((prev) => (prev + 1) % UPLOADING_MESSAGES.length);
|
|
29
|
+
setAnimationState('slide-in');
|
|
30
|
+
scheduleNextChange(); // Schedule next change
|
|
31
|
+
}, 400); // Match animation duration
|
|
32
|
+
}, randomInterval);
|
|
33
|
+
return timer;
|
|
34
|
+
};
|
|
35
|
+
const timer = scheduleNextChange();
|
|
36
|
+
return () => clearTimeout(timer);
|
|
37
|
+
}, [interval]);
|
|
38
|
+
// Animate dots: . -> .. -> ...
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
const dotsTimer = setInterval(() => {
|
|
41
|
+
setDots((prev) => {
|
|
42
|
+
if (prev === '.')
|
|
43
|
+
return '..';
|
|
44
|
+
if (prev === '..')
|
|
45
|
+
return '...';
|
|
46
|
+
return '.';
|
|
47
|
+
});
|
|
48
|
+
}, 500); // Change dots every 500ms
|
|
49
|
+
return () => clearInterval(dotsTimer);
|
|
50
|
+
}, []);
|
|
51
|
+
return (jsxs("span", { className: `uploading-status ${className}`, children: [jsx("span", { className: `uploading-status-text ${animationState}`, children: UPLOADING_MESSAGES[messageIndex] }), jsx("span", { className: "uploading-dots", children: dots })] }));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { UploadingStatus };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { __awaiter } from 'tslib';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Recursively reads files from a FileSystemEntry (file or directory)
|
|
5
|
+
*/
|
|
6
|
+
function readEntry(entry_1, acceptFilter_1) {
|
|
7
|
+
return __awaiter(this, arguments, void 0, function* (entry, acceptFilter, directory = true, basePath = '') {
|
|
8
|
+
const acceptSet = new Set(acceptFilter ? acceptFilter.split(',').map((s) => s.trim()) : []);
|
|
9
|
+
if (entry.isFile) {
|
|
10
|
+
return new Promise((resolve) => {
|
|
11
|
+
entry.file((file) => {
|
|
12
|
+
// Apply accept filter if specified
|
|
13
|
+
if (acceptSet.size > 0) {
|
|
14
|
+
const matchesFilter = Array.from(acceptSet).some((filter) => {
|
|
15
|
+
if (filter.startsWith('.')) {
|
|
16
|
+
return file.name.endsWith(filter);
|
|
17
|
+
}
|
|
18
|
+
if (filter.includes('/*')) {
|
|
19
|
+
const mimePrefix = filter.split('/')[0];
|
|
20
|
+
return file.type.startsWith(mimePrefix);
|
|
21
|
+
}
|
|
22
|
+
return file.type === filter;
|
|
23
|
+
});
|
|
24
|
+
if (!matchesFilter) {
|
|
25
|
+
resolve([]);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const relativePath = basePath || '';
|
|
30
|
+
resolve([{ file, relativePath }]);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (entry.isDirectory) {
|
|
35
|
+
// Skip directories if directory support is disabled
|
|
36
|
+
if (!directory) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
const dirReader = entry.createReader();
|
|
40
|
+
const entries = [];
|
|
41
|
+
const newBasePath = basePath ? `${basePath}/${entry.name}` : entry.name;
|
|
42
|
+
// Read all entries in directory
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
const readEntries = () => {
|
|
45
|
+
dirReader.readEntries((results) => __awaiter(this, void 0, void 0, function* () {
|
|
46
|
+
if (results.length === 0) {
|
|
47
|
+
// Done reading directory
|
|
48
|
+
const filePromises = entries.map((e) => readEntry(e, acceptFilter, directory, newBasePath));
|
|
49
|
+
const fileArrays = yield Promise.all(filePromises);
|
|
50
|
+
resolve(fileArrays.flat());
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
entries.push(...results);
|
|
54
|
+
readEntries(); // Continue reading
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
57
|
+
};
|
|
58
|
+
readEntries();
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return [];
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { readEntry };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Size constants
|
|
2
|
+
const KB = 1024;
|
|
3
|
+
const MB = KB * 1024;
|
|
4
|
+
const GB = MB * 1024;
|
|
5
|
+
// Default configuration
|
|
6
|
+
const DEFAULT_CHUNK_SIZE = 10 * MB;
|
|
7
|
+
const DEFAULT_MAX_CHUNK_SIZE = 100 * MB;
|
|
8
|
+
const DEFAULT_MIN_CHUNK_SIZE = 5 * MB;
|
|
9
|
+
const DEFAULT_CONCURRENT_CHUNKS = 2;
|
|
10
|
+
const DEFAULT_CONCURRENT_FILES = 1;
|
|
11
|
+
const DEFAULT_MAX_FILE_SIZE = 10 * GB;
|
|
12
|
+
// Upload status enum
|
|
13
|
+
const UploadStatus = {
|
|
14
|
+
PENDING: 'pending',
|
|
15
|
+
PREPARING: 'preparing',
|
|
16
|
+
UPLOADING: 'uploading',
|
|
17
|
+
PAUSED: 'paused',
|
|
18
|
+
MERGING: 'merging',
|
|
19
|
+
SUCCESS: 'success',
|
|
20
|
+
FAILED: 'failed',
|
|
21
|
+
CANCELLED: 'cancelled',
|
|
22
|
+
};
|
|
23
|
+
// Chunk status enum
|
|
24
|
+
const ChunkStatus = {
|
|
25
|
+
PENDING: 'pending',
|
|
26
|
+
UPLOADING: 'uploading',
|
|
27
|
+
COMPLETED: 'completed',
|
|
28
|
+
FAILED: 'failed',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export { ChunkStatus, DEFAULT_CHUNK_SIZE, DEFAULT_CONCURRENT_CHUNKS, DEFAULT_CONCURRENT_FILES, DEFAULT_MAX_CHUNK_SIZE, DEFAULT_MAX_FILE_SIZE, DEFAULT_MIN_CHUNK_SIZE, GB, KB, MB, UploadStatus };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global action registry for providing fresh context to handlers.
|
|
3
|
+
*
|
|
4
|
+
* This registry stores references to the latest upload actions (removeFile, start, etc.)
|
|
5
|
+
* and state (files). It's updated on every render by useUpload hook, ensuring that
|
|
6
|
+
* when handlers are invoked, they receive always-current references.
|
|
7
|
+
*
|
|
8
|
+
* This solves the stale closure problem where handlers registered at file-add time
|
|
9
|
+
* would capture outdated versions of functions and state.
|
|
10
|
+
*
|
|
11
|
+
* Flow:
|
|
12
|
+
* 1. useUpload hook updates actionRegistry on every render with fresh refs
|
|
13
|
+
* 2. QueueManager callbacks (queue-callbacks.ts) invoke handlers
|
|
14
|
+
* 3. Before invoking, they call actionRegistry.getContext() to get fresh utilities
|
|
15
|
+
* 4. Handler receives fresh context, avoiding stale closure issues
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* // In useUpload hook (automatic):
|
|
20
|
+
* useEffect(() => {
|
|
21
|
+
* actionRegistry.update({ files, removeFile, start, ... });
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // In queue-callbacks.ts:
|
|
25
|
+
* const ctx = actionRegistry.getContext();
|
|
26
|
+
* handlers.onUploadSuccess?.(file, ctx);
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
class ActionRegistry {
|
|
30
|
+
constructor() {
|
|
31
|
+
this.context = null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Update the stored context with fresh references.
|
|
35
|
+
* Called by useUpload hook on every render.
|
|
36
|
+
*/
|
|
37
|
+
update(context) {
|
|
38
|
+
this.context = context;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get the current context with fresh references.
|
|
42
|
+
* Returns null if context hasn't been initialized yet.
|
|
43
|
+
*/
|
|
44
|
+
getContext() {
|
|
45
|
+
return this.context;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Check if context is available.
|
|
49
|
+
*/
|
|
50
|
+
hasContext() {
|
|
51
|
+
return this.context !== null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Clear the context.
|
|
55
|
+
* Mainly for testing or complete reset.
|
|
56
|
+
*/
|
|
57
|
+
clear() {
|
|
58
|
+
this.context = null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Singleton instance of the action registry.
|
|
63
|
+
* This is intentionally a module-level singleton, similar to handlerRegistry,
|
|
64
|
+
* because it needs to be accessible from queue-callbacks.ts which operates
|
|
65
|
+
* outside the React component tree.
|
|
66
|
+
*/
|
|
67
|
+
const actionRegistry = new ActionRegistry();
|
|
68
|
+
|
|
69
|
+
export { actionRegistry };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global file state store for background uploads.
|
|
3
|
+
*
|
|
4
|
+
* This store maintains a copy of file state that can be accessed
|
|
5
|
+
* even when the originating React component is unmounted.
|
|
6
|
+
*
|
|
7
|
+
* The store is updated whenever React state changes, ensuring
|
|
8
|
+
* QueueManager callbacks always have access to fresh file data.
|
|
9
|
+
*
|
|
10
|
+
* Flow:
|
|
11
|
+
* 1. UploadProvider updates state via reducer
|
|
12
|
+
* 2. Provider syncs state to this store via useEffect
|
|
13
|
+
* 3. QueueManager callbacks access files via getFile()
|
|
14
|
+
* 4. Even if user navigates away, store still has current data
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```tsx
|
|
18
|
+
* // In UploadProvider - sync state to store
|
|
19
|
+
* useEffect(() => {
|
|
20
|
+
* fileStateStore.syncFiles(state.files);
|
|
21
|
+
* }, [state.files]);
|
|
22
|
+
*
|
|
23
|
+
* // In queue callbacks - get fresh file data
|
|
24
|
+
* const file = fileStateStore.getFile(fileId);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
class FileStateStore {
|
|
28
|
+
constructor() {
|
|
29
|
+
/** Current files indexed by ID for O(1) lookup */
|
|
30
|
+
this.filesMap = new Map();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Sync files from React state to the global store.
|
|
34
|
+
* Called by UploadProvider whenever state.files changes.
|
|
35
|
+
*/
|
|
36
|
+
syncFiles(files) {
|
|
37
|
+
// Clear and rebuild map for simplicity
|
|
38
|
+
// Could be optimized with incremental updates if needed
|
|
39
|
+
this.filesMap.clear();
|
|
40
|
+
for (const file of files) {
|
|
41
|
+
this.filesMap.set(file.id, file);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get a file by ID.
|
|
46
|
+
* Returns undefined if file not found.
|
|
47
|
+
*/
|
|
48
|
+
getFile(fileId) {
|
|
49
|
+
return this.filesMap.get(fileId);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Check if a file exists in the store.
|
|
53
|
+
*/
|
|
54
|
+
hasFile(fileId) {
|
|
55
|
+
return this.filesMap.has(fileId);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get all files (for debugging).
|
|
59
|
+
*/
|
|
60
|
+
getAllFiles() {
|
|
61
|
+
return Array.from(this.filesMap.values());
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Clear all files from the store.
|
|
65
|
+
* Called when UploadProvider unmounts.
|
|
66
|
+
*/
|
|
67
|
+
clear() {
|
|
68
|
+
this.filesMap.clear();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get the number of files in the store.
|
|
72
|
+
*/
|
|
73
|
+
get size() {
|
|
74
|
+
return this.filesMap.size;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Singleton instance of the file state store.
|
|
79
|
+
* Shared across all upload components and QueueManager.
|
|
80
|
+
*/
|
|
81
|
+
const fileStateStore = new FileStateStore();
|
|
82
|
+
|
|
83
|
+
export { fileStateStore };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global handler registry for background uploads.
|
|
3
|
+
*
|
|
4
|
+
* This registry stores handlers per-file, allowing uploads to continue
|
|
5
|
+
* even when the user navigates away from the page that initiated the upload.
|
|
6
|
+
*
|
|
7
|
+
* Flow:
|
|
8
|
+
* 1. Component calls addFiles() -> handlers are registered per file ID
|
|
9
|
+
* 2. User navigates away -> component unmounts but handlers persist
|
|
10
|
+
* 3. QueueManager continues -> looks up handlers by file ID
|
|
11
|
+
* 4. Upload completes -> handlers are cleaned up
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* // In page component
|
|
16
|
+
* const { addFiles } = useUpload();
|
|
17
|
+
* addFiles(files, {
|
|
18
|
+
* getHandlers: (file) => ({
|
|
19
|
+
* onGetUploadUrls: (name, parts) => api.getUrls(datasetId, name, parts, folderFromUI()),
|
|
20
|
+
* // ... other handlers
|
|
21
|
+
* })
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // When user adds files, handlers are registered:
|
|
25
|
+
* // handlerRegistry.register(file.id, handlers)
|
|
26
|
+
*
|
|
27
|
+
* // User navigates away - no problem!
|
|
28
|
+
* // QueueManager uses: handlerRegistry.get(fileId)
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
class HandlerRegistry {
|
|
32
|
+
constructor() {
|
|
33
|
+
this.registry = new Map();
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Register handlers for a file.
|
|
37
|
+
* Called when a file is added for upload.
|
|
38
|
+
*/
|
|
39
|
+
register(fileId, handlers, context) {
|
|
40
|
+
this.registry.set(fileId, {
|
|
41
|
+
handlers,
|
|
42
|
+
registeredAt: Date.now(),
|
|
43
|
+
context,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get handlers for a file.
|
|
48
|
+
* Returns undefined if file has no registered handlers.
|
|
49
|
+
*/
|
|
50
|
+
get(fileId) {
|
|
51
|
+
var _a;
|
|
52
|
+
return (_a = this.registry.get(fileId)) === null || _a === void 0 ? void 0 : _a.handlers;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if handlers exist for a file.
|
|
56
|
+
*/
|
|
57
|
+
has(fileId) {
|
|
58
|
+
return this.registry.has(fileId);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Remove handlers for a file.
|
|
62
|
+
* Called when upload completes, fails permanently, or is cancelled.
|
|
63
|
+
*/
|
|
64
|
+
unregister(fileId) {
|
|
65
|
+
this.registry.delete(fileId);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get all registered file IDs.
|
|
69
|
+
* Useful for debugging and cleanup.
|
|
70
|
+
*/
|
|
71
|
+
getRegisteredFileIds() {
|
|
72
|
+
return Array.from(this.registry.keys());
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get entry with metadata for debugging.
|
|
76
|
+
*/
|
|
77
|
+
getEntry(fileId) {
|
|
78
|
+
return this.registry.get(fileId);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Clear all registrations.
|
|
82
|
+
* Use with caution - mainly for testing or complete reset.
|
|
83
|
+
*/
|
|
84
|
+
clear() {
|
|
85
|
+
this.registry.clear();
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get count of registered handlers.
|
|
89
|
+
*/
|
|
90
|
+
get size() {
|
|
91
|
+
return this.registry.size;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Singleton instance of the handler registry.
|
|
96
|
+
* This is intentionally a module-level singleton, not React context,
|
|
97
|
+
* because it needs to persist completely independently of React lifecycle.
|
|
98
|
+
*/
|
|
99
|
+
const handlerRegistry = new HandlerRegistry();
|
|
100
|
+
/**
|
|
101
|
+
* Debug helper to log current registry state.
|
|
102
|
+
*/
|
|
103
|
+
// export function debugHandlerRegistry(): void {
|
|
104
|
+
// console.group('🔧 Handler Registry Debug');
|
|
105
|
+
// console.log('Registered files:', handlerRegistry.getRegisteredFileIds());
|
|
106
|
+
// handlerRegistry.getRegisteredFileIds().forEach(id => {
|
|
107
|
+
// const entry = handlerRegistry.getEntry(id);
|
|
108
|
+
// console.log(` ${id}:`, {
|
|
109
|
+
// context: entry?.context,
|
|
110
|
+
// registeredAt: entry?.registeredAt ? new Date(entry.registeredAt).toISOString() : 'N/A',
|
|
111
|
+
// hasOnGetUploadUrls: !!entry?.handlers.onGetUploadUrls,
|
|
112
|
+
// hasOnPartComplete: !!entry?.handlers.onPartComplete,
|
|
113
|
+
// hasOnFileComplete: !!entry?.handlers.onFileComplete,
|
|
114
|
+
// });
|
|
115
|
+
// });
|
|
116
|
+
// console.groupEnd();
|
|
117
|
+
// }
|
|
118
|
+
|
|
119
|
+
export { handlerRegistry };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
import { DEFAULT_CONCURRENT_FILES, DEFAULT_CONCURRENT_CHUNKS, DEFAULT_MIN_CHUNK_SIZE, DEFAULT_MAX_CHUNK_SIZE } from '../constants.js';
|
|
3
|
+
|
|
4
|
+
// Initial empty upload state
|
|
5
|
+
const initialUploadState = {
|
|
6
|
+
files: [],
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Default upload configuration (Provider level).
|
|
10
|
+
* Controls chunk sizes and concurrency - typically same across app.
|
|
11
|
+
*/
|
|
12
|
+
const defaultUploadConfig = {
|
|
13
|
+
maxChunkSize: DEFAULT_MAX_CHUNK_SIZE,
|
|
14
|
+
minChunkSize: DEFAULT_MIN_CHUNK_SIZE,
|
|
15
|
+
concurrentChunks: DEFAULT_CONCURRENT_CHUNKS,
|
|
16
|
+
concurrentFiles: DEFAULT_CONCURRENT_FILES,
|
|
17
|
+
};
|
|
18
|
+
// Upload context - null before provider mounts
|
|
19
|
+
const UploadContext = createContext(null);
|
|
20
|
+
// Display name for React DevTools
|
|
21
|
+
UploadContext.displayName = 'UploadContext';
|
|
22
|
+
/**
|
|
23
|
+
* Hook to consume upload context
|
|
24
|
+
* Must be used within UploadProvider
|
|
25
|
+
* @throws Error if used outside of UploadProvider
|
|
26
|
+
*/
|
|
27
|
+
function useUploadContext() {
|
|
28
|
+
const context = useContext(UploadContext);
|
|
29
|
+
if (!context) {
|
|
30
|
+
throw new Error('useUploadContext must be used within UploadProvider. ' +
|
|
31
|
+
'Make sure your component is wrapped with <UploadProvider>.');
|
|
32
|
+
}
|
|
33
|
+
return context;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { UploadContext, defaultUploadConfig, initialUploadState, useUploadContext };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useReducer, useMemo, useRef, useEffect, useCallback } from 'react';
|
|
3
|
+
import { initialUploadState, defaultUploadConfig, UploadContext } from './upload-context.js';
|
|
4
|
+
import { uploadReducer } from './upload-reducer.js';
|
|
5
|
+
import { fileStateStore } from './file-state-store.js';
|
|
6
|
+
import { QueueManager } from '../engine/queue-manager.js';
|
|
7
|
+
import { createQueueCallbacks } from '../engine/queue-callbacks.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Upload Provider Component
|
|
11
|
+
*
|
|
12
|
+
* Provides shared upload state and global configuration across the application.
|
|
13
|
+
* Manages:
|
|
14
|
+
* - Files state (list of files, their status, progress)
|
|
15
|
+
* - QueueManager singleton (shared upload queue)
|
|
16
|
+
* - Global config (chunk sizes, concurrency)
|
|
17
|
+
*
|
|
18
|
+
* For persistence support, wrap your app with PersistenceProvider inside UploadProvider:
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* // App level - state management + global config
|
|
23
|
+
* <UploadProvider config={{ concurrentFiles: 3 }}>
|
|
24
|
+
* <PersistenceProvider config={{ mode: 'indexeddb' }}>
|
|
25
|
+
* <App />
|
|
26
|
+
* </PersistenceProvider>
|
|
27
|
+
* </UploadProvider>
|
|
28
|
+
*
|
|
29
|
+
* // In App - use persistence hook
|
|
30
|
+
* const { pendingFiles, restoreFiles } = usePersistence();
|
|
31
|
+
*
|
|
32
|
+
* // Component level - provide handlers & per-usage options
|
|
33
|
+
* const { addFiles } = useUpload();
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
function UploadProvider({ config, children }) {
|
|
37
|
+
// State management with reducer
|
|
38
|
+
const [state, dispatch] = useReducer(uploadReducer, initialUploadState);
|
|
39
|
+
// Merge user config with defaults
|
|
40
|
+
const mergedConfig = useMemo(() => (Object.assign(Object.assign({}, defaultUploadConfig), config)), [config]);
|
|
41
|
+
// Shared QueueManager ref - singleton across all useUploadEngine consumers
|
|
42
|
+
const queueManagerRef = useRef(null);
|
|
43
|
+
// Sync file state to global store for background upload support
|
|
44
|
+
// This ensures QueueManager callbacks always have fresh file data
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
fileStateStore.syncFiles(state.files);
|
|
47
|
+
}, [state.files]);
|
|
48
|
+
/**
|
|
49
|
+
* Get or create QueueManager instance with lazy initialization.
|
|
50
|
+
* This is stable across renders and shared by all consumers.
|
|
51
|
+
*/
|
|
52
|
+
const getQueueManager = useCallback(() => {
|
|
53
|
+
if (queueManagerRef.current) {
|
|
54
|
+
return queueManagerRef.current;
|
|
55
|
+
}
|
|
56
|
+
const queueConfig = {
|
|
57
|
+
concurrentFiles: mergedConfig.concurrentFiles,
|
|
58
|
+
concurrentChunks: mergedConfig.concurrentChunks,
|
|
59
|
+
};
|
|
60
|
+
const callbacks = createQueueCallbacks({ dispatch });
|
|
61
|
+
queueManagerRef.current = new QueueManager(queueConfig, callbacks);
|
|
62
|
+
return queueManagerRef.current;
|
|
63
|
+
}, [mergedConfig.concurrentFiles, mergedConfig.concurrentChunks, dispatch]);
|
|
64
|
+
// Memoize context value to prevent unnecessary re-renders
|
|
65
|
+
const contextValue = useMemo(() => ({
|
|
66
|
+
state,
|
|
67
|
+
dispatch,
|
|
68
|
+
config: mergedConfig,
|
|
69
|
+
queueManagerRef,
|
|
70
|
+
getQueueManager,
|
|
71
|
+
}), [state, mergedConfig, getQueueManager]);
|
|
72
|
+
return jsx(UploadContext.Provider, { value: contextValue, children: children });
|
|
73
|
+
}
|
|
74
|
+
// Display name for React DevTools
|
|
75
|
+
UploadProvider.displayName = 'UploadProvider';
|
|
76
|
+
|
|
77
|
+
export { UploadProvider };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reducer for upload state management
|
|
3
|
+
* Handles all file upload state transitions
|
|
4
|
+
*/
|
|
5
|
+
function uploadReducer(state, action) {
|
|
6
|
+
switch (action.type) {
|
|
7
|
+
case 'ADD_FILES':
|
|
8
|
+
return Object.assign(Object.assign({}, state), { files: [...state.files, ...action.files] });
|
|
9
|
+
case 'UPDATE_FILE':
|
|
10
|
+
return Object.assign(Object.assign({}, state), { files: state.files.map((file) => (file.id === action.id ? Object.assign(Object.assign({}, file), action.updates) : file)) });
|
|
11
|
+
case 'UPDATE_CHUNK':
|
|
12
|
+
return Object.assign(Object.assign({}, state), { files: state.files.map((file) => file.id === action.fileId
|
|
13
|
+
? Object.assign(Object.assign({}, file), { chunks: file.chunks.map((chunk) => chunk.partNum === action.partNum ? Object.assign(Object.assign({}, chunk), action.updates) : chunk) }) : file) });
|
|
14
|
+
case 'REMOVE_FILE':
|
|
15
|
+
return Object.assign(Object.assign({}, state), { files: state.files.filter((file) => file.id !== action.id) });
|
|
16
|
+
default:
|
|
17
|
+
return state;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { uploadReducer };
|