@maas/payload-plugin-media-cloud 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +8 -0
  2. package/dist/adapter/handleDelete.d.ts +20 -0
  3. package/dist/adapter/handleDelete.js +70 -0
  4. package/dist/adapter/handleDelete.js.map +1 -0
  5. package/dist/adapter/handleUpload.d.ts +12 -0
  6. package/dist/adapter/handleUpload.js +29 -0
  7. package/dist/adapter/handleUpload.js.map +1 -0
  8. package/dist/adapter/staticHandler.d.ts +17 -0
  9. package/dist/adapter/staticHandler.js +64 -0
  10. package/dist/adapter/staticHandler.js.map +1 -0
  11. package/dist/adapter/storageAdapter.d.ts +23 -0
  12. package/dist/adapter/storageAdapter.js +30 -0
  13. package/dist/adapter/storageAdapter.js.map +1 -0
  14. package/dist/collections/mediaCollection.d.ts +16 -0
  15. package/dist/collections/mediaCollection.js +139 -0
  16. package/dist/collections/mediaCollection.js.map +1 -0
  17. package/dist/components/index.d.ts +4 -0
  18. package/dist/components/index.js +5 -0
  19. package/dist/components/mux-preview/index.d.ts +2 -0
  20. package/dist/components/mux-preview/index.js +3 -0
  21. package/dist/components/mux-preview/mux-preview.d.ts +14 -0
  22. package/dist/components/mux-preview/mux-preview.js +38 -0
  23. package/dist/components/mux-preview/mux-preview.js.map +1 -0
  24. package/dist/components/upload-handler/index.d.ts +2 -0
  25. package/dist/components/upload-handler/index.js +3 -0
  26. package/dist/components/upload-handler/upload-handler.d.ts +22 -0
  27. package/dist/components/upload-handler/upload-handler.js +178 -0
  28. package/dist/components/upload-handler/upload-handler.js.map +1 -0
  29. package/dist/components/upload-manager/index.d.ts +2 -0
  30. package/dist/components/upload-manager/index.js +3 -0
  31. package/dist/components/upload-manager/upload-manager-DN4RrmYB.css +204 -0
  32. package/dist/components/upload-manager/upload-manager-DN4RrmYB.css.map +1 -0
  33. package/dist/components/upload-manager/upload-manager.css +201 -0
  34. package/dist/components/upload-manager/upload-manager.d.ts +42 -0
  35. package/dist/components/upload-manager/upload-manager.js +315 -0
  36. package/dist/components/upload-manager/upload-manager.js.map +1 -0
  37. package/dist/components/upload-manager/upload-manager2.js +0 -0
  38. package/dist/endpoints/muxAssetHandler.d.ts +11 -0
  39. package/dist/endpoints/muxAssetHandler.js +59 -0
  40. package/dist/endpoints/muxAssetHandler.js.map +1 -0
  41. package/dist/endpoints/muxCreateUploadHandler.d.ts +13 -0
  42. package/dist/endpoints/muxCreateUploadHandler.js +40 -0
  43. package/dist/endpoints/muxCreateUploadHandler.js.map +1 -0
  44. package/dist/endpoints/muxWebhookHandler.d.ts +11 -0
  45. package/dist/endpoints/muxWebhookHandler.js +49 -0
  46. package/dist/endpoints/muxWebhookHandler.js.map +1 -0
  47. package/dist/hooks/useEmitter.d.ts +48 -0
  48. package/dist/hooks/useEmitter.js +19 -0
  49. package/dist/hooks/useEmitter.js.map +1 -0
  50. package/dist/hooks/useErrorHandler.d.ts +11 -0
  51. package/dist/hooks/useErrorHandler.js +19 -0
  52. package/dist/hooks/useErrorHandler.js.map +1 -0
  53. package/dist/index.d.ts +3 -0
  54. package/dist/index.js +3 -0
  55. package/dist/plugin.d.ts +15 -0
  56. package/dist/plugin.js +242 -0
  57. package/dist/plugin.js.map +1 -0
  58. package/dist/tus/stores/s3/expiration-manager.d.ts +36 -0
  59. package/dist/tus/stores/s3/expiration-manager.js +76 -0
  60. package/dist/tus/stores/s3/expiration-manager.js.map +1 -0
  61. package/dist/tus/stores/s3/file-operations.d.ts +66 -0
  62. package/dist/tus/stores/s3/file-operations.js +90 -0
  63. package/dist/tus/stores/s3/file-operations.js.map +1 -0
  64. package/dist/tus/stores/s3/log.d.ts +5 -0
  65. package/dist/tus/stores/s3/log.js +8 -0
  66. package/dist/tus/stores/s3/log.js.map +1 -0
  67. package/dist/tus/stores/s3/metadata-manager.d.ts +85 -0
  68. package/dist/tus/stores/s3/metadata-manager.js +135 -0
  69. package/dist/tus/stores/s3/metadata-manager.js.map +1 -0
  70. package/dist/tus/stores/s3/parts-manager.d.ts +130 -0
  71. package/dist/tus/stores/s3/parts-manager.js +328 -0
  72. package/dist/tus/stores/s3/parts-manager.js.map +1 -0
  73. package/dist/tus/stores/s3/s3-store.d.ts +110 -0
  74. package/dist/tus/stores/s3/s3-store.js +342 -0
  75. package/dist/tus/stores/s3/s3-store.js.map +1 -0
  76. package/dist/tus/stores/s3/semaphore.d.ts +16 -0
  77. package/dist/tus/stores/s3/semaphore.js +32 -0
  78. package/dist/tus/stores/s3/semaphore.js.map +1 -0
  79. package/dist/types/errors.d.ts +26 -0
  80. package/dist/types/errors.js +28 -0
  81. package/dist/types/errors.js.map +1 -0
  82. package/dist/types/index.d.ts +73 -0
  83. package/dist/types/index.js +0 -0
  84. package/dist/utils/file.d.ts +30 -0
  85. package/dist/utils/file.js +84 -0
  86. package/dist/utils/file.js.map +1 -0
  87. package/package.json +92 -0
@@ -0,0 +1,178 @@
1
+ 'use client';
2
+
3
+
4
+ import { MediaCloudError } from "../../types/errors.js";
5
+ import { useErrorHandler } from "../../hooks/useErrorHandler.js";
6
+ import { getFileType, isVideo, sanitizeFilename } from "../../utils/file.js";
7
+ import { emitter } from "../../hooks/useEmitter.js";
8
+ import * as upchunk from "@mux/upchunk";
9
+ import * as tus from "tus-js-client";
10
+ import { toast } from "@payloadcms/ui";
11
+ import { createClientUploadHandler } from "@payloadcms/plugin-cloud-storage/client";
12
+
13
+ //#region src/components/upload-handler/upload-handler.tsx
14
+ const { logError, throwError } = useErrorHandler();
15
+ const MUX_CHUNK_SIZE = 30720;
16
+ const TUS_CHUNK_SIZE = 1024 * 1024;
17
+ const TUS_RETRY_DELAYS = [
18
+ 0,
19
+ 1e3,
20
+ 2e3,
21
+ 5e3
22
+ ];
23
+ /**
24
+ * Utility function to parse upload ID from URL
25
+ * @param uploadUrl - The upload URL to parse
26
+ * @returns The extracted upload ID or empty string if parsing fails
27
+ */
28
+ function parseUploadId(uploadUrl) {
29
+ if (!uploadUrl) {
30
+ logError(MediaCloudError.UPLOAD_NO_URL);
31
+ return "";
32
+ }
33
+ const url = new URL(uploadUrl);
34
+ return url.pathname.split("/").pop() || "";
35
+ }
36
+ /**
37
+ * Handles Mux video upload with progress tracking
38
+ * @param args - The upload arguments including file, server URL, and callbacks
39
+ * @returns Promise that resolves to upload result or null if upload fails
40
+ */
41
+ async function muxUpload(args) {
42
+ const { file, serverURL, apiRoute, mimeType, updateFilename } = args;
43
+ const filename = sanitizeFilename(file.name);
44
+ const getUploadUrlEndpoint = `${serverURL}${apiRoute}/mux/upload`;
45
+ const getAssetEndpoint = `${serverURL}${apiRoute}/mux/asset`;
46
+ updateFilename(filename);
47
+ try {
48
+ const response = await fetch(getUploadUrlEndpoint, {
49
+ body: JSON.stringify({
50
+ filename,
51
+ mimeType
52
+ }),
53
+ credentials: "include",
54
+ method: "POST",
55
+ headers: { "Content-Type": "application/json" }
56
+ });
57
+ const { url, uploadId } = await response.json();
58
+ const uploader = await upchunk.createUpload({
59
+ endpoint: url,
60
+ file,
61
+ chunkSize: MUX_CHUNK_SIZE
62
+ });
63
+ emitter.emit("add-upload", {
64
+ id: uploadId,
65
+ filename,
66
+ polling: false,
67
+ pollingUrl: getAssetEndpoint
68
+ });
69
+ uploader.on("error", () => {
70
+ logError(MediaCloudError.MUX_UPLOAD_ERROR);
71
+ toast.error("Video upload failed");
72
+ emitter.emit("remove-upload", { id: uploadId });
73
+ });
74
+ uploader.on("progress", (progress) => {
75
+ emitter.emit("update-upload", {
76
+ id: uploadId,
77
+ progress: progress.detail
78
+ });
79
+ });
80
+ uploader.on("success", () => {
81
+ emitter.emit("upload-completed", { id: uploadId });
82
+ });
83
+ return {
84
+ uploadId,
85
+ mimeType,
86
+ storage: "mux"
87
+ };
88
+ } catch (_error) {
89
+ logError(MediaCloudError.MUX_DIRECT_UPLOAD_ERROR);
90
+ toast.error("Video upload failed");
91
+ return null;
92
+ }
93
+ }
94
+ /**
95
+ * Handles TUS file upload with resumable capabilities
96
+ * @param args - The upload arguments including file, server URL, and callbacks
97
+ * @returns Promise that resolves to upload result or null if upload fails
98
+ */
99
+ async function tusUpload(args) {
100
+ const { apiRoute, serverURL, file, mimeType, updateFilename } = args;
101
+ const filename = file.name;
102
+ const filetype = file.type;
103
+ const filesize = file.size.toString();
104
+ return new Promise((resolve) => {
105
+ const upload = new tus.Upload(file, {
106
+ endpoint: `${serverURL}${apiRoute}/uploads`,
107
+ retryDelays: TUS_RETRY_DELAYS,
108
+ chunkSize: TUS_CHUNK_SIZE,
109
+ metadata: {
110
+ filename,
111
+ filetype,
112
+ filesize,
113
+ contentType: filetype,
114
+ contentDisposition: "inline",
115
+ contentLength: filesize
116
+ },
117
+ onError: () => {
118
+ logError(MediaCloudError.TUS_UPLOAD_ERROR);
119
+ toast.error("File upload failed");
120
+ resolve(null);
121
+ },
122
+ onProgress: (bytesUploaded, bytesTotal) => {
123
+ const percentage = Math.round(bytesUploaded / bytesTotal * 100);
124
+ const uploadId = parseUploadId(upload?.url);
125
+ emitter.emit("update-upload", {
126
+ id: uploadId,
127
+ progress: percentage
128
+ });
129
+ },
130
+ onSuccess: () => {
131
+ const uploadId = parseUploadId(upload?.url);
132
+ emitter.emit("upload-completed", { id: uploadId });
133
+ },
134
+ onUploadUrlAvailable: () => {
135
+ const uploadId = parseUploadId(upload?.url);
136
+ updateFilename(uploadId);
137
+ emitter.emit("add-upload", {
138
+ id: uploadId,
139
+ filename
140
+ });
141
+ resolve({
142
+ uploadId,
143
+ mimeType,
144
+ storage: "s3"
145
+ });
146
+ }
147
+ });
148
+ upload.start();
149
+ });
150
+ }
151
+ const UploadHandler = createClientUploadHandler({ handler: async (args) => {
152
+ const { serverURL, apiRoute, file, updateFilename } = args;
153
+ try {
154
+ const mimeType = await getFileType(file);
155
+ if (!mimeType) {
156
+ throwError(MediaCloudError.FILE_TYPE_UNKNOWN);
157
+ throw new Error();
158
+ }
159
+ const isVideoFile = await isVideo(file);
160
+ const uploadArgs = {
161
+ file,
162
+ serverURL,
163
+ apiRoute,
164
+ mimeType,
165
+ updateFilename
166
+ };
167
+ if (isVideoFile) return await muxUpload(uploadArgs);
168
+ else return await tusUpload(uploadArgs);
169
+ } catch (_error) {
170
+ logError(MediaCloudError.UPLOAD_HANDLER_ERROR);
171
+ toast.error("Upload failed");
172
+ return null;
173
+ }
174
+ } });
175
+
176
+ //#endregion
177
+ export { UploadHandler };
178
+ //# sourceMappingURL=upload-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-handler.js","names":["uploadUrl?: string | null","args: UploadArgs","uploadArgs: UploadArgs"],"sources":["../../../src/components/upload-handler/upload-handler.tsx"],"sourcesContent":["'use client'\n\nimport * as upchunk from '@mux/upchunk'\nimport * as tus from 'tus-js-client'\n\nimport { toast } from '@payloadcms/ui'\nimport { createClientUploadHandler } from '@payloadcms/plugin-cloud-storage/client'\n\nimport { MediaCloudError } from '../../types/errors'\nimport { emitter } from '../../hooks/useEmitter'\nimport { useErrorHandler } from '../../hooks/useErrorHandler'\nimport { isVideo, getFileType, sanitizeFilename } from '../../utils/file'\n\ninterface UploadArgs {\n serverURL: string\n apiRoute: string\n file: File\n mimeType: string\n updateFilename: (filename: string) => void\n}\n\ninterface UploadResult {\n uploadId: string\n mimeType: string\n storage: 'mux' | 's3'\n}\n\ninterface MuxCreateUploadResponse {\n url: string\n uploadId: string\n}\n\nconst { logError, throwError } = useErrorHandler()\n\nconst MUX_CHUNK_SIZE = 30720\nconst TUS_CHUNK_SIZE = 1024 * 1024\nconst TUS_RETRY_DELAYS = [0, 1000, 2000, 5000]\n\n/**\n * Utility function to parse upload ID from URL\n * @param uploadUrl - The upload URL to parse\n * @returns The extracted upload ID or empty string if parsing fails\n */\nfunction parseUploadId(uploadUrl?: string | null): string {\n if (!uploadUrl) {\n logError(MediaCloudError.UPLOAD_NO_URL)\n return ''\n }\n const url = new URL(uploadUrl)\n return url.pathname.split('/').pop() || ''\n}\n\n/**\n * Handles Mux video upload with progress tracking\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function muxUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { file, serverURL, apiRoute, mimeType, updateFilename } = args\n\n const filename = sanitizeFilename(file.name)\n const getUploadUrlEndpoint = `${serverURL}${apiRoute}/mux/upload`\n const getAssetEndpoint = `${serverURL}${apiRoute}/mux/asset`\n\n updateFilename(filename)\n\n try {\n // Request upload URL from Mux\n const response = await fetch(getUploadUrlEndpoint, {\n body: JSON.stringify({ filename, mimeType }),\n credentials: 'include',\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n })\n\n const { url, uploadId } = (await response.json()) as MuxCreateUploadResponse\n\n // Create upchunk uploader\n const uploader = await upchunk.createUpload({\n endpoint: url,\n file,\n chunkSize: MUX_CHUNK_SIZE,\n })\n\n // Add upload to tracker\n emitter.emit('add-upload', {\n id: uploadId,\n filename,\n polling: false,\n pollingUrl: getAssetEndpoint,\n })\n\n // Set up event handlers\n uploader.on('error', () => {\n logError(MediaCloudError.MUX_UPLOAD_ERROR)\n toast.error('Video upload failed')\n emitter.emit('remove-upload', { id: uploadId })\n })\n\n uploader.on('progress', (progress) => {\n emitter.emit('update-upload', {\n id: uploadId,\n progress: progress.detail,\n })\n })\n\n uploader.on('success', () => {\n emitter.emit('upload-completed', { id: uploadId })\n })\n\n return {\n uploadId,\n mimeType,\n storage: 'mux',\n }\n } catch (_error) {\n logError(MediaCloudError.MUX_DIRECT_UPLOAD_ERROR)\n toast.error('Video upload failed')\n return null\n }\n}\n\n/**\n * Handles TUS file upload with resumable capabilities\n * @param args - The upload arguments including file, server URL, and callbacks\n * @returns Promise that resolves to upload result or null if upload fails\n */\nasync function tusUpload(args: UploadArgs): Promise<UploadResult | null> {\n const { apiRoute, serverURL, file, mimeType, updateFilename } = args\n\n const filename = file.name\n const filetype = file.type\n const filesize = file.size.toString()\n\n return new Promise((resolve) => {\n const upload = new tus.Upload(file, {\n endpoint: `${serverURL}${apiRoute}/uploads`,\n retryDelays: TUS_RETRY_DELAYS,\n chunkSize: TUS_CHUNK_SIZE,\n metadata: {\n filename,\n filetype,\n filesize,\n contentType: filetype,\n contentDisposition: 'inline',\n contentLength: filesize,\n },\n onError: () => {\n logError(MediaCloudError.TUS_UPLOAD_ERROR)\n toast.error('File upload failed')\n resolve(null)\n },\n onProgress: (bytesUploaded, bytesTotal) => {\n const percentage = Math.round((bytesUploaded / bytesTotal) * 100)\n const uploadId = parseUploadId(upload?.url)\n emitter.emit('update-upload', {\n id: uploadId,\n progress: percentage,\n })\n },\n onSuccess: () => {\n const uploadId = parseUploadId(upload?.url)\n emitter.emit('upload-completed', { id: uploadId })\n },\n onUploadUrlAvailable: () => {\n const uploadId = parseUploadId(upload?.url)\n updateFilename(uploadId)\n emitter.emit('add-upload', { id: uploadId, filename })\n resolve({\n uploadId,\n mimeType,\n storage: 's3',\n })\n },\n })\n\n upload.start()\n })\n}\n\nexport const UploadHandler = createClientUploadHandler({\n handler: async (args) => {\n const { serverURL, apiRoute, file, updateFilename } = args\n\n try {\n const mimeType = await getFileType(file)\n\n if (!mimeType) {\n throwError(MediaCloudError.FILE_TYPE_UNKNOWN)\n throw new Error() // This will never execute but satisfies TypeScript\n }\n\n const isVideoFile = await isVideo(file)\n\n const uploadArgs: UploadArgs = {\n file,\n serverURL,\n apiRoute,\n mimeType,\n updateFilename,\n }\n\n if (isVideoFile) {\n return await muxUpload(uploadArgs)\n } else {\n return await tusUpload(uploadArgs)\n }\n } catch (_error) {\n logError(MediaCloudError.UPLOAD_HANDLER_ERROR)\n toast.error('Upload failed')\n return null\n }\n },\n})\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { UploadManagerProvider, useUploadManagerContext } from "./upload-manager.js";
2
+ export { UploadManagerProvider, useUploadManagerContext };
@@ -0,0 +1,3 @@
1
+ import { UploadManagerProvider, useUploadManagerContext } from "./upload-manager.js";
2
+
3
+ export { UploadManagerProvider, useUploadManagerContext };
@@ -0,0 +1,204 @@
1
+ @keyframes pulse {
2
+ 0% {
3
+ opacity: 0.6;
4
+ }
5
+ 50% {
6
+ opacity: 1;
7
+ }
8
+ 100% {
9
+ opacity: 0.6;
10
+ }
11
+ }
12
+
13
+ @keyframes shimmer {
14
+ 0% {
15
+ background-position: -200% 0;
16
+ }
17
+ 100% {
18
+ background-position: 200% 0;
19
+ }
20
+ }
21
+
22
+ .upload-manager {
23
+ position: fixed;
24
+ overflow: hidden;
25
+ bottom: 0;
26
+ left: 50%;
27
+ transform: translateX(-50%);
28
+ width: 400px;
29
+ max-height: 500px;
30
+ margin: 2.25rem;
31
+ background: var(--theme-bg);
32
+ border: 1px solid var(--theme-elevation-100);
33
+ border-radius: var(--style-radius-m);
34
+ box-shadow: var(--box-shadow-lg);
35
+ z-index: 1024;
36
+ display: flex;
37
+ flex-direction: column;
38
+ }
39
+
40
+ .upload-manager__header {
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: space-between;
44
+ padding: 0.75rem 1rem;
45
+ border-bottom: 1px solid var(--theme-elevation-100);
46
+ background: var(--theme-elevation-50);
47
+
48
+ & h4 {
49
+ margin: 0;
50
+ font-size: 0.875rem;
51
+ font-weight: 600;
52
+ color: var(--theme-text);
53
+ }
54
+ }
55
+
56
+ .upload-manager__tabs {
57
+ display: flex;
58
+ border-bottom: 1px solid var(--theme-elevation-100);
59
+ background: var(--theme-elevation-25);
60
+
61
+ & .upload-tab {
62
+ flex: 1;
63
+ padding: 0.5rem 0.75rem;
64
+ background: none;
65
+ border: none;
66
+ color: var(--theme-text-dim);
67
+ font-size: 0.75rem;
68
+ font-weight: 500;
69
+ cursor: pointer;
70
+ transition: all 0.15s ease;
71
+ border-bottom: 2px solid transparent;
72
+
73
+ &:hover {
74
+ background: var(--theme-elevation-50);
75
+ color: var(--theme-text);
76
+ }
77
+
78
+ &[data-active='true'] {
79
+ color: var(--theme-success-500);
80
+ border-bottom-color: var(--theme-success-500);
81
+ background: var(--theme-bg);
82
+ }
83
+ }
84
+ }
85
+
86
+ .upload-manager__content {
87
+ flex: 1;
88
+ overflow-y: auto;
89
+ max-height: 300px;
90
+ padding: 0;
91
+
92
+ & ul {
93
+ list-style: none;
94
+ margin: 0;
95
+ padding: 0;
96
+
97
+ & li {
98
+ padding: 0.75rem 1rem;
99
+ border-bottom: 1px solid var(--theme-elevation-50);
100
+
101
+ &:last-child {
102
+ border-bottom: none;
103
+ }
104
+
105
+ &[data-status='uploading'] {
106
+ background: var(--theme-bg);
107
+ }
108
+
109
+ &[data-status='processing'] {
110
+ background: var(--theme-warning-50);
111
+ }
112
+
113
+ &[data-status='completed'] {
114
+ background: var(--theme-success-50);
115
+ }
116
+ }
117
+ }
118
+
119
+ & .upload-empty-state {
120
+ padding: 2rem 1rem;
121
+ text-align: center;
122
+ color: var(--theme-text-dim);
123
+ font-size: 0.875rem;
124
+ margin: 0;
125
+ }
126
+ }
127
+
128
+ .upload-progress-bar {
129
+ height: 3px;
130
+ background: var(--theme-elevation-100);
131
+ border-radius: 2px;
132
+ overflow: hidden;
133
+ position: relative;
134
+
135
+ & .upload-progress {
136
+ height: 100%;
137
+ background: var(--theme-success-500);
138
+ width: calc(var(--progress) * 100%);
139
+ transition: width 0.3s ease;
140
+ border-radius: inherit;
141
+
142
+ &[data-active='true'] {
143
+ background: var(--theme-warning-500);
144
+ animation: pulse 1.5s ease-in-out infinite;
145
+ width: 100%;
146
+ }
147
+ }
148
+ }
149
+
150
+ .upload-info {
151
+ display: flex;
152
+ align-items: center;
153
+ justify-content: space-between;
154
+ margin-bottom: 0.5rem;
155
+
156
+ & .upload-filename {
157
+ font-size: 0.875rem;
158
+ font-weight: 500;
159
+ color: var(--theme-text);
160
+ flex: 1;
161
+ margin-right: 0.5rem;
162
+ overflow: hidden;
163
+ text-overflow: ellipsis;
164
+ white-space: nowrap;
165
+ }
166
+
167
+ & .upload-meta {
168
+ font-size: 0.75rem;
169
+ color: var(--theme-text-dim);
170
+ font-weight: 500;
171
+ flex-shrink: 0;
172
+ }
173
+ }
174
+
175
+ .upload-manager__file {
176
+ & .upload-manager__file--polling {
177
+ animation: pulse 2s ease-in-out infinite;
178
+ background: linear-gradient(90deg, #3b82f6, #8b5cf6, #3b82f6);
179
+ background-size: 200% 100%;
180
+ animation:
181
+ pulse 2s ease-in-out infinite,
182
+ shimmer 3s ease-in-out infinite;
183
+ }
184
+ }
185
+
186
+ .upload-manager__footer {
187
+ padding: 0.75rem 1rem;
188
+ border-top: 1px solid var(--theme-elevation-100);
189
+ background: var(--theme-elevation-50);
190
+ display: flex;
191
+ justify-content: center;
192
+ }
193
+
194
+ // Responsive adjustments
195
+ @media (max-width: 768px) {
196
+ .upload-manager {
197
+ width: calc(100vw - 2rem);
198
+ right: 1rem;
199
+ left: 1rem;
200
+ }
201
+ }
202
+
203
+
204
+ /*# sourceMappingURL=upload-manager-DN4RrmYB.css.map*/
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-manager-DN4RrmYB.css","names":[],"sources":["../../../src/components/upload-manager/upload-manager.css"],"sourcesContent":["@keyframes pulse {\n 0% {\n opacity: 0.6;\n }\n 50% {\n opacity: 1;\n }\n 100% {\n opacity: 0.6;\n }\n}\n\n@keyframes shimmer {\n 0% {\n background-position: -200% 0;\n }\n 100% {\n background-position: 200% 0;\n }\n}\n\n.upload-manager {\n position: fixed;\n overflow: hidden;\n bottom: 0;\n left: 50%;\n transform: translateX(-50%);\n width: 400px;\n max-height: 500px;\n margin: 2.25rem;\n background: var(--theme-bg);\n border: 1px solid var(--theme-elevation-100);\n border-radius: var(--style-radius-m);\n box-shadow: var(--box-shadow-lg);\n z-index: 1024;\n display: flex;\n flex-direction: column;\n}\n\n.upload-manager__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--theme-elevation-100);\n background: var(--theme-elevation-50);\n\n & h4 {\n margin: 0;\n font-size: 0.875rem;\n font-weight: 600;\n color: var(--theme-text);\n }\n}\n\n.upload-manager__tabs {\n display: flex;\n border-bottom: 1px solid var(--theme-elevation-100);\n background: var(--theme-elevation-25);\n\n & .upload-tab {\n flex: 1;\n padding: 0.5rem 0.75rem;\n background: none;\n border: none;\n color: var(--theme-text-dim);\n font-size: 0.75rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n border-bottom: 2px solid transparent;\n\n &:hover {\n background: var(--theme-elevation-50);\n color: var(--theme-text);\n }\n\n &[data-active='true'] {\n color: var(--theme-success-500);\n border-bottom-color: var(--theme-success-500);\n background: var(--theme-bg);\n }\n }\n}\n\n.upload-manager__content {\n flex: 1;\n overflow-y: auto;\n max-height: 300px;\n padding: 0;\n\n & ul {\n list-style: none;\n margin: 0;\n padding: 0;\n\n & li {\n padding: 0.75rem 1rem;\n border-bottom: 1px solid var(--theme-elevation-50);\n\n &:last-child {\n border-bottom: none;\n }\n\n &[data-status='uploading'] {\n background: var(--theme-bg);\n }\n\n &[data-status='processing'] {\n background: var(--theme-warning-50);\n }\n\n &[data-status='completed'] {\n background: var(--theme-success-50);\n }\n }\n }\n\n & .upload-empty-state {\n padding: 2rem 1rem;\n text-align: center;\n color: var(--theme-text-dim);\n font-size: 0.875rem;\n margin: 0;\n }\n}\n\n.upload-progress-bar {\n height: 3px;\n background: var(--theme-elevation-100);\n border-radius: 2px;\n overflow: hidden;\n position: relative;\n\n & .upload-progress {\n height: 100%;\n background: var(--theme-success-500);\n width: calc(var(--progress) * 100%);\n transition: width 0.3s ease;\n border-radius: inherit;\n\n &[data-active='true'] {\n background: var(--theme-warning-500);\n animation: pulse 1.5s ease-in-out infinite;\n width: 100%;\n }\n }\n}\n\n.upload-info {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 0.5rem;\n\n & .upload-filename {\n font-size: 0.875rem;\n font-weight: 500;\n color: var(--theme-text);\n flex: 1;\n margin-right: 0.5rem;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n & .upload-meta {\n font-size: 0.75rem;\n color: var(--theme-text-dim);\n font-weight: 500;\n flex-shrink: 0;\n }\n}\n\n.upload-manager__file {\n & .upload-manager__file--polling {\n animation: pulse 2s ease-in-out infinite;\n background: linear-gradient(90deg, #3b82f6, #8b5cf6, #3b82f6);\n background-size: 200% 100%;\n animation:\n pulse 2s ease-in-out infinite,\n shimmer 3s ease-in-out infinite;\n }\n}\n\n.upload-manager__footer {\n padding: 0.75rem 1rem;\n border-top: 1px solid var(--theme-elevation-100);\n background: var(--theme-elevation-50);\n display: flex;\n justify-content: center;\n}\n\n// Responsive adjustments\n@media (max-width: 768px) {\n .upload-manager {\n width: calc(100vw - 2rem);\n right: 1rem;\n left: 1rem;\n }\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -0,0 +1,201 @@
1
+ @keyframes pulse {
2
+ 0% {
3
+ opacity: 0.6;
4
+ }
5
+ 50% {
6
+ opacity: 1;
7
+ }
8
+ 100% {
9
+ opacity: 0.6;
10
+ }
11
+ }
12
+
13
+ @keyframes shimmer {
14
+ 0% {
15
+ background-position: -200% 0;
16
+ }
17
+ 100% {
18
+ background-position: 200% 0;
19
+ }
20
+ }
21
+
22
+ .upload-manager {
23
+ position: fixed;
24
+ overflow: hidden;
25
+ bottom: 0;
26
+ left: 50%;
27
+ transform: translateX(-50%);
28
+ width: 400px;
29
+ max-height: 500px;
30
+ margin: 2.25rem;
31
+ background: var(--theme-bg);
32
+ border: 1px solid var(--theme-elevation-100);
33
+ border-radius: var(--style-radius-m);
34
+ box-shadow: var(--box-shadow-lg);
35
+ z-index: 1024;
36
+ display: flex;
37
+ flex-direction: column;
38
+ }
39
+
40
+ .upload-manager__header {
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: space-between;
44
+ padding: 0.75rem 1rem;
45
+ border-bottom: 1px solid var(--theme-elevation-100);
46
+ background: var(--theme-elevation-50);
47
+
48
+ & h4 {
49
+ margin: 0;
50
+ font-size: 0.875rem;
51
+ font-weight: 600;
52
+ color: var(--theme-text);
53
+ }
54
+ }
55
+
56
+ .upload-manager__tabs {
57
+ display: flex;
58
+ border-bottom: 1px solid var(--theme-elevation-100);
59
+ background: var(--theme-elevation-25);
60
+
61
+ & .upload-tab {
62
+ flex: 1;
63
+ padding: 0.5rem 0.75rem;
64
+ background: none;
65
+ border: none;
66
+ color: var(--theme-text-dim);
67
+ font-size: 0.75rem;
68
+ font-weight: 500;
69
+ cursor: pointer;
70
+ transition: all 0.15s ease;
71
+ border-bottom: 2px solid transparent;
72
+
73
+ &:hover {
74
+ background: var(--theme-elevation-50);
75
+ color: var(--theme-text);
76
+ }
77
+
78
+ &[data-active='true'] {
79
+ color: var(--theme-success-500);
80
+ border-bottom-color: var(--theme-success-500);
81
+ background: var(--theme-bg);
82
+ }
83
+ }
84
+ }
85
+
86
+ .upload-manager__content {
87
+ flex: 1;
88
+ overflow-y: auto;
89
+ max-height: 300px;
90
+ padding: 0;
91
+
92
+ & ul {
93
+ list-style: none;
94
+ margin: 0;
95
+ padding: 0;
96
+
97
+ & li {
98
+ padding: 0.75rem 1rem;
99
+ border-bottom: 1px solid var(--theme-elevation-50);
100
+
101
+ &:last-child {
102
+ border-bottom: none;
103
+ }
104
+
105
+ &[data-status='uploading'] {
106
+ background: var(--theme-bg);
107
+ }
108
+
109
+ &[data-status='processing'] {
110
+ background: var(--theme-warning-50);
111
+ }
112
+
113
+ &[data-status='completed'] {
114
+ background: var(--theme-success-50);
115
+ }
116
+ }
117
+ }
118
+
119
+ & .upload-empty-state {
120
+ padding: 2rem 1rem;
121
+ text-align: center;
122
+ color: var(--theme-text-dim);
123
+ font-size: 0.875rem;
124
+ margin: 0;
125
+ }
126
+ }
127
+
128
+ .upload-progress-bar {
129
+ height: 3px;
130
+ background: var(--theme-elevation-100);
131
+ border-radius: 2px;
132
+ overflow: hidden;
133
+ position: relative;
134
+
135
+ & .upload-progress {
136
+ height: 100%;
137
+ background: var(--theme-success-500);
138
+ width: calc(var(--progress) * 100%);
139
+ transition: width 0.3s ease;
140
+ border-radius: inherit;
141
+
142
+ &[data-active='true'] {
143
+ background: var(--theme-warning-500);
144
+ animation: pulse 1.5s ease-in-out infinite;
145
+ width: 100%;
146
+ }
147
+ }
148
+ }
149
+
150
+ .upload-info {
151
+ display: flex;
152
+ align-items: center;
153
+ justify-content: space-between;
154
+ margin-bottom: 0.5rem;
155
+
156
+ & .upload-filename {
157
+ font-size: 0.875rem;
158
+ font-weight: 500;
159
+ color: var(--theme-text);
160
+ flex: 1;
161
+ margin-right: 0.5rem;
162
+ overflow: hidden;
163
+ text-overflow: ellipsis;
164
+ white-space: nowrap;
165
+ }
166
+
167
+ & .upload-meta {
168
+ font-size: 0.75rem;
169
+ color: var(--theme-text-dim);
170
+ font-weight: 500;
171
+ flex-shrink: 0;
172
+ }
173
+ }
174
+
175
+ .upload-manager__file {
176
+ & .upload-manager__file--polling {
177
+ animation: pulse 2s ease-in-out infinite;
178
+ background: linear-gradient(90deg, #3b82f6, #8b5cf6, #3b82f6);
179
+ background-size: 200% 100%;
180
+ animation:
181
+ pulse 2s ease-in-out infinite,
182
+ shimmer 3s ease-in-out infinite;
183
+ }
184
+ }
185
+
186
+ .upload-manager__footer {
187
+ padding: 0.75rem 1rem;
188
+ border-top: 1px solid var(--theme-elevation-100);
189
+ background: var(--theme-elevation-50);
190
+ display: flex;
191
+ justify-content: center;
192
+ }
193
+
194
+ // Responsive adjustments
195
+ @media (max-width: 768px) {
196
+ .upload-manager {
197
+ width: calc(100vw - 2rem);
198
+ right: 1rem;
199
+ left: 1rem;
200
+ }
201
+ }
@@ -0,0 +1,42 @@
1
+ import React from "react";
2
+
3
+ //#region src/components/upload-manager/upload-manager.d.ts
4
+ interface Upload {
5
+ id: string;
6
+ filename: string;
7
+ progress: number;
8
+ error?: string;
9
+ polling?: boolean;
10
+ pollingUrl?: string;
11
+ status?: 'uploading' | 'processing' | 'completed';
12
+ }
13
+ interface UploadManagerContextType {
14
+ showUploadManager?: boolean;
15
+ activeUploads: Upload[];
16
+ addUpload: (args: AddUploadArgs) => void;
17
+ updateUpload: (args: UpdateUploadArgs) => void;
18
+ }
19
+ interface UploadManagerProviderArgs {
20
+ children: React.ReactNode;
21
+ }
22
+ interface AddUploadArgs {
23
+ id: string;
24
+ filename: string;
25
+ polling?: boolean;
26
+ pollingUrl?: string;
27
+ }
28
+ interface UpdateUploadArgs {
29
+ id: string;
30
+ progress: number;
31
+ polling?: boolean;
32
+ }
33
+ /**
34
+ * Provider component for upload management context
35
+ * @param args - Arguments including children to wrap
36
+ * @returns JSX element providing upload management context
37
+ */
38
+ declare function UploadManagerProvider(args: UploadManagerProviderArgs): React.JSX.Element;
39
+ declare const useUploadManagerContext: () => UploadManagerContextType;
40
+ //#endregion
41
+ export { UploadManagerProvider, useUploadManagerContext };
42
+ //# sourceMappingURL=upload-manager.d.ts.map