@wordpress/media-utils 5.41.1-next.v.202603102151.0 → 5.42.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/CHANGELOG.md +2 -0
- package/build/components/media-upload-modal/index.cjs +136 -89
- package/build/components/media-upload-modal/index.cjs.map +3 -3
- package/build/components/media-upload-modal/upload-status-popover.cjs +156 -0
- package/build/components/media-upload-modal/upload-status-popover.cjs.map +7 -0
- package/build/components/media-upload-modal/use-invalidate-attachment-resolutions.cjs +45 -0
- package/build/components/media-upload-modal/use-invalidate-attachment-resolutions.cjs.map +7 -0
- package/build/components/media-upload-modal/use-upload-status.cjs +127 -0
- package/build/components/media-upload-modal/use-upload-status.cjs.map +7 -0
- package/build-module/components/media-upload-modal/index.mjs +129 -90
- package/build-module/components/media-upload-modal/index.mjs.map +2 -2
- package/build-module/components/media-upload-modal/upload-status-popover.mjs +131 -0
- package/build-module/components/media-upload-modal/upload-status-popover.mjs.map +7 -0
- package/build-module/components/media-upload-modal/use-invalidate-attachment-resolutions.mjs +20 -0
- package/build-module/components/media-upload-modal/use-invalidate-attachment-resolutions.mjs.map +7 -0
- package/build-module/components/media-upload-modal/use-upload-status.mjs +102 -0
- package/build-module/components/media-upload-modal/use-upload-status.mjs.map +7 -0
- package/build-style/style-rtl.css +73 -3
- package/build-style/style.css +73 -3
- package/build-types/components/media-upload-modal/index.d.ts.map +1 -1
- package/build-types/components/media-upload-modal/upload-status-popover.d.ts +15 -0
- package/build-types/components/media-upload-modal/upload-status-popover.d.ts.map +1 -0
- package/build-types/components/media-upload-modal/use-invalidate-attachment-resolutions.d.ts +22 -0
- package/build-types/components/media-upload-modal/use-invalidate-attachment-resolutions.d.ts.map +1 -0
- package/build-types/components/media-upload-modal/use-upload-status.d.ts +41 -0
- package/build-types/components/media-upload-modal/use-upload-status.d.ts.map +1 -0
- package/package.json +17 -15
- package/src/components/media-upload-modal/index.tsx +131 -108
- package/src/components/media-upload-modal/style.scss +88 -3
- package/src/components/media-upload-modal/test/use-upload-status.test.ts +501 -0
- package/src/components/media-upload-modal/upload-status-popover.tsx +155 -0
- package/src/components/media-upload-modal/use-invalidate-attachment-resolutions.ts +50 -0
- package/src/components/media-upload-modal/use-upload-status.ts +203 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// packages/media-utils/src/components/media-upload-modal/index.tsx
|
|
@@ -24,6 +34,7 @@ __export(media_upload_modal_exports, {
|
|
|
24
34
|
default: () => media_upload_modal_default
|
|
25
35
|
});
|
|
26
36
|
module.exports = __toCommonJS(media_upload_modal_exports);
|
|
37
|
+
var import_clsx = __toESM(require("clsx"));
|
|
27
38
|
var import_element = require("@wordpress/element");
|
|
28
39
|
var import_i18n = require("@wordpress/i18n");
|
|
29
40
|
var import_core_data = require("@wordpress/core-data");
|
|
@@ -31,12 +42,15 @@ var import_data = require("@wordpress/data");
|
|
|
31
42
|
var import_components = require("@wordpress/components");
|
|
32
43
|
var import_icons = require("@wordpress/icons");
|
|
33
44
|
var import_dataviews = require("@wordpress/dataviews");
|
|
45
|
+
var import_ui = require("@wordpress/ui");
|
|
34
46
|
var import_media_fields = require("@wordpress/media-fields");
|
|
35
47
|
var import_notices = require("@wordpress/notices");
|
|
36
|
-
var import_blob = require("@wordpress/blob");
|
|
37
48
|
var import_transform_attachment = require("../../utils/transform-attachment.cjs");
|
|
38
49
|
var import_upload_media = require("../../utils/upload-media.cjs");
|
|
39
50
|
var import_lock_unlock = require("../../lock-unlock.cjs");
|
|
51
|
+
var import_upload_status_popover = require("./upload-status-popover.cjs");
|
|
52
|
+
var import_use_invalidate_attachment_resolutions = require("./use-invalidate-attachment-resolutions.cjs");
|
|
53
|
+
var import_use_upload_status = require("./use-upload-status.cjs");
|
|
40
54
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
41
55
|
var { useEntityRecordsWithPermissions } = (0, import_lock_unlock.unlock)(import_core_data.privateApis);
|
|
42
56
|
var LAYOUT_PICKER_GRID = "pickerGrid";
|
|
@@ -63,8 +77,8 @@ function MediaUploadModal({
|
|
|
63
77
|
}
|
|
64
78
|
return Array.isArray(value) ? value.map(String) : [String(value)];
|
|
65
79
|
});
|
|
66
|
-
const { createSuccessNotice,
|
|
67
|
-
const
|
|
80
|
+
const { createSuccessNotice, removeAllNotices } = (0, import_data.useDispatch)(import_notices.store);
|
|
81
|
+
const invalidateAttachmentResolutions = (0, import_use_invalidate_attachment_resolutions.useInvalidateAttachmentResolutions)();
|
|
68
82
|
const [view, setView] = (0, import_element.useState)(() => ({
|
|
69
83
|
type: LAYOUT_PICKER_GRID,
|
|
70
84
|
fields: [],
|
|
@@ -73,10 +87,11 @@ function MediaUploadModal({
|
|
|
73
87
|
mediaField: "media_thumbnail",
|
|
74
88
|
search: "",
|
|
75
89
|
page: 1,
|
|
76
|
-
perPage:
|
|
90
|
+
perPage: 50,
|
|
77
91
|
filters: [],
|
|
78
92
|
layout: {
|
|
79
|
-
previewSize: 170
|
|
93
|
+
previewSize: 170,
|
|
94
|
+
density: "compact"
|
|
80
95
|
}
|
|
81
96
|
}));
|
|
82
97
|
const queryArgs = (0, import_element.useMemo)(() => {
|
|
@@ -117,6 +132,41 @@ function MediaUploadModal({
|
|
|
117
132
|
...filters
|
|
118
133
|
};
|
|
119
134
|
}, [view, allowedTypes]);
|
|
135
|
+
const handleBatchComplete = (0, import_element.useCallback)(
|
|
136
|
+
(attachments) => {
|
|
137
|
+
const uploadedIds = attachments.map((attachment) => String(attachment.id)).filter(Boolean);
|
|
138
|
+
if (multiple) {
|
|
139
|
+
setSelection((prev) => {
|
|
140
|
+
const existing = new Set(prev);
|
|
141
|
+
const newIds = uploadedIds.filter(
|
|
142
|
+
(id) => !existing.has(id)
|
|
143
|
+
);
|
|
144
|
+
return [...prev, ...newIds];
|
|
145
|
+
});
|
|
146
|
+
} else {
|
|
147
|
+
setSelection(uploadedIds.slice(0, 1));
|
|
148
|
+
}
|
|
149
|
+
invalidateAttachmentResolutions();
|
|
150
|
+
},
|
|
151
|
+
[multiple, invalidateAttachmentResolutions]
|
|
152
|
+
);
|
|
153
|
+
const {
|
|
154
|
+
uploadingFiles,
|
|
155
|
+
registerBatch,
|
|
156
|
+
dismissError,
|
|
157
|
+
clearCompleted,
|
|
158
|
+
allComplete
|
|
159
|
+
} = (0, import_use_upload_status.useUploadStatus)({ onBatchComplete: handleBatchComplete });
|
|
160
|
+
const isPopoverOpenRef = (0, import_element.useRef)(false);
|
|
161
|
+
const handlePopoverOpenChange = (0, import_element.useCallback)(
|
|
162
|
+
(open) => {
|
|
163
|
+
isPopoverOpenRef.current = open;
|
|
164
|
+
if (!open) {
|
|
165
|
+
clearCompleted();
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
[clearCompleted]
|
|
169
|
+
);
|
|
120
170
|
const {
|
|
121
171
|
records: mediaRecords,
|
|
122
172
|
isResolving: isLoading,
|
|
@@ -159,7 +209,7 @@ function MediaUploadModal({
|
|
|
159
209
|
() => [
|
|
160
210
|
{
|
|
161
211
|
id: "select",
|
|
162
|
-
label:
|
|
212
|
+
label: (0, import_i18n.__)("Select"),
|
|
163
213
|
isPrimary: true,
|
|
164
214
|
supportsBulk: multiple,
|
|
165
215
|
async callback() {
|
|
@@ -179,31 +229,34 @@ function MediaUploadModal({
|
|
|
179
229
|
);
|
|
180
230
|
const transformedPosts = (selectedPosts ?? []).map(import_transform_attachment.transformAttachment).filter(Boolean);
|
|
181
231
|
const selectedItems = multiple ? transformedPosts : transformedPosts?.[0];
|
|
232
|
+
removeAllNotices("snackbar", NOTICES_CONTEXT);
|
|
182
233
|
onSelect(selectedItems);
|
|
183
234
|
}
|
|
184
235
|
}
|
|
185
236
|
],
|
|
186
|
-
[multiple, onSelect, selection]
|
|
237
|
+
[multiple, onSelect, selection, removeAllNotices]
|
|
187
238
|
);
|
|
188
239
|
const handleModalClose = (0, import_element.useCallback)(() => {
|
|
240
|
+
removeAllNotices("snackbar", NOTICES_CONTEXT);
|
|
189
241
|
onClose?.();
|
|
190
|
-
}, [onClose]);
|
|
242
|
+
}, [removeAllNotices, onClose]);
|
|
191
243
|
const handleUpload = onUpload || import_upload_media.uploadMedia;
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
244
|
+
const prevAllCompleteRef = (0, import_element.useRef)(false);
|
|
245
|
+
(0, import_element.useEffect)(() => {
|
|
246
|
+
if (allComplete && !prevAllCompleteRef.current) {
|
|
247
|
+
const completeCount = uploadingFiles.filter(
|
|
248
|
+
(file) => file.status === "uploaded"
|
|
249
|
+
).length;
|
|
250
|
+
if (completeCount > 0) {
|
|
198
251
|
createSuccessNotice(
|
|
199
252
|
(0, import_i18n.sprintf)(
|
|
200
253
|
// translators: %s: number of files
|
|
201
254
|
(0, import_i18n._n)(
|
|
202
255
|
"Uploaded %s file",
|
|
203
256
|
"Uploaded %s files",
|
|
204
|
-
|
|
257
|
+
completeCount
|
|
205
258
|
),
|
|
206
|
-
|
|
259
|
+
completeCount.toLocaleString()
|
|
207
260
|
),
|
|
208
261
|
{
|
|
209
262
|
type: "snackbar",
|
|
@@ -211,68 +264,28 @@ function MediaUploadModal({
|
|
|
211
264
|
id: NOTICE_ID_UPLOAD_PROGRESS
|
|
212
265
|
}
|
|
213
266
|
);
|
|
214
|
-
const uploadedIds = attachments.map((attachment) => String(attachment.id)).filter(Boolean);
|
|
215
|
-
if (multiple) {
|
|
216
|
-
setSelection((prev) => [...prev, ...uploadedIds]);
|
|
217
|
-
} else {
|
|
218
|
-
setSelection(uploadedIds.slice(0, 1));
|
|
219
|
-
}
|
|
220
|
-
invalidateResolution("getEntityRecords", [
|
|
221
|
-
"postType",
|
|
222
|
-
"attachment",
|
|
223
|
-
queryArgs
|
|
224
|
-
]);
|
|
225
267
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
type: "snackbar",
|
|
233
|
-
context: NOTICES_CONTEXT,
|
|
234
|
-
id: NOTICE_ID_UPLOAD_PROGRESS
|
|
235
|
-
});
|
|
236
|
-
},
|
|
237
|
-
[createErrorNotice]
|
|
238
|
-
);
|
|
268
|
+
if (!isPopoverOpenRef.current) {
|
|
269
|
+
clearCompleted();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
prevAllCompleteRef.current = allComplete;
|
|
273
|
+
}, [allComplete, uploadingFiles, createSuccessNotice, clearCompleted]);
|
|
239
274
|
const handleFileSelect = (0, import_element.useCallback)(
|
|
240
275
|
(event) => {
|
|
241
276
|
const files = event.target.files;
|
|
242
277
|
if (files && files.length > 0) {
|
|
243
278
|
const filesArray = Array.from(files);
|
|
244
|
-
|
|
245
|
-
(0, import_i18n.sprintf)(
|
|
246
|
-
// translators: %s: number of files
|
|
247
|
-
(0, import_i18n._n)(
|
|
248
|
-
"Uploading %s file",
|
|
249
|
-
"Uploading %s files",
|
|
250
|
-
filesArray.length
|
|
251
|
-
),
|
|
252
|
-
filesArray.length.toLocaleString()
|
|
253
|
-
),
|
|
254
|
-
{
|
|
255
|
-
type: "snackbar",
|
|
256
|
-
context: NOTICES_CONTEXT,
|
|
257
|
-
id: NOTICE_ID_UPLOAD_PROGRESS,
|
|
258
|
-
explicitDismiss: true
|
|
259
|
-
}
|
|
260
|
-
);
|
|
279
|
+
const { onFileChange, onError } = registerBatch(filesArray);
|
|
261
280
|
handleUpload({
|
|
262
281
|
allowedTypes,
|
|
263
282
|
filesList: filesArray,
|
|
264
|
-
onFileChange
|
|
265
|
-
onError
|
|
283
|
+
onFileChange,
|
|
284
|
+
onError
|
|
266
285
|
});
|
|
267
286
|
}
|
|
268
287
|
},
|
|
269
|
-
[
|
|
270
|
-
allowedTypes,
|
|
271
|
-
handleUpload,
|
|
272
|
-
createInfoNotice,
|
|
273
|
-
handleUploadComplete,
|
|
274
|
-
handleUploadError
|
|
275
|
-
]
|
|
288
|
+
[allowedTypes, handleUpload, registerBatch]
|
|
276
289
|
);
|
|
277
290
|
const paginationInfo = (0, import_element.useMemo)(
|
|
278
291
|
() => ({
|
|
@@ -352,35 +365,19 @@ function MediaUploadModal({
|
|
|
352
365
|
);
|
|
353
366
|
}
|
|
354
367
|
if (filteredFiles.length > 0) {
|
|
355
|
-
|
|
356
|
-
(0, import_i18n.sprintf)(
|
|
357
|
-
// translators: %s: number of files
|
|
358
|
-
(0, import_i18n._n)(
|
|
359
|
-
"Uploading %s file",
|
|
360
|
-
"Uploading %s files",
|
|
361
|
-
filteredFiles.length
|
|
362
|
-
),
|
|
363
|
-
filteredFiles.length.toLocaleString()
|
|
364
|
-
),
|
|
365
|
-
{
|
|
366
|
-
type: "snackbar",
|
|
367
|
-
context: NOTICES_CONTEXT,
|
|
368
|
-
id: NOTICE_ID_UPLOAD_PROGRESS,
|
|
369
|
-
explicitDismiss: true
|
|
370
|
-
}
|
|
371
|
-
);
|
|
368
|
+
const { onFileChange, onError } = registerBatch(filteredFiles);
|
|
372
369
|
handleUpload({
|
|
373
370
|
allowedTypes,
|
|
374
371
|
filesList: filteredFiles,
|
|
375
|
-
onFileChange
|
|
376
|
-
onError
|
|
372
|
+
onFileChange,
|
|
373
|
+
onError
|
|
377
374
|
});
|
|
378
375
|
}
|
|
379
376
|
},
|
|
380
377
|
label: (0, import_i18n.__)("Drop files to upload")
|
|
381
378
|
}
|
|
382
379
|
),
|
|
383
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
380
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
384
381
|
import_dataviews.DataViewsPicker,
|
|
385
382
|
{
|
|
386
383
|
data: mediaRecords || [],
|
|
@@ -394,9 +391,59 @@ function MediaUploadModal({
|
|
|
394
391
|
paginationInfo,
|
|
395
392
|
defaultLayouts,
|
|
396
393
|
getItemId: (item) => String(item.id),
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
394
|
+
itemListLabel: (0, import_i18n.__)("Media items"),
|
|
395
|
+
children: [
|
|
396
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
397
|
+
import_ui.Stack,
|
|
398
|
+
{
|
|
399
|
+
direction: "row",
|
|
400
|
+
align: "top",
|
|
401
|
+
justify: "space-between",
|
|
402
|
+
className: "dataviews__view-actions",
|
|
403
|
+
gap: "xs",
|
|
404
|
+
children: [
|
|
405
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
406
|
+
import_ui.Stack,
|
|
407
|
+
{
|
|
408
|
+
direction: "row",
|
|
409
|
+
gap: "sm",
|
|
410
|
+
justify: "start",
|
|
411
|
+
className: "dataviews__search",
|
|
412
|
+
children: [
|
|
413
|
+
search && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.Search, { label: searchLabel }),
|
|
414
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.FiltersToggle, {})
|
|
415
|
+
]
|
|
416
|
+
}
|
|
417
|
+
),
|
|
418
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ui.Stack, { direction: "row", gap: "xs", style: { flexShrink: 0 }, children: [
|
|
419
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.LayoutSwitcher, {}),
|
|
420
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.ViewConfig, {})
|
|
421
|
+
] })
|
|
422
|
+
]
|
|
423
|
+
}
|
|
424
|
+
),
|
|
425
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.FiltersToggled, { className: "dataviews-filters__container" }),
|
|
426
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.Layout, {}),
|
|
427
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
428
|
+
"div",
|
|
429
|
+
{
|
|
430
|
+
className: (0, import_clsx.default)("media-upload-modal__footer", {
|
|
431
|
+
"is-uploading": uploadingFiles.length > 0
|
|
432
|
+
}),
|
|
433
|
+
children: [
|
|
434
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
435
|
+
import_upload_status_popover.UploadStatusPopover,
|
|
436
|
+
{
|
|
437
|
+
uploadingFiles,
|
|
438
|
+
onDismissError: dismissError,
|
|
439
|
+
onOpenChange: handlePopoverOpenChange
|
|
440
|
+
}
|
|
441
|
+
),
|
|
442
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.BulkActionToolbar, {})
|
|
443
|
+
]
|
|
444
|
+
}
|
|
445
|
+
)
|
|
446
|
+
]
|
|
400
447
|
}
|
|
401
448
|
),
|
|
402
449
|
(0, import_element.createPortal)(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/media-upload-modal/index.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * WordPress dependencies\n */\nimport {\n\tcreatePortal,\n\tuseState,\n\tuseCallback,\n\tuseMemo,\n} from '@wordpress/element';\nimport { __, sprintf, _n } from '@wordpress/i18n';\nimport {\n\tprivateApis as coreDataPrivateApis,\n\tstore as coreStore,\n} from '@wordpress/core-data';\nimport { resolveSelect, useDispatch } from '@wordpress/data';\nimport { Modal, DropZone, FormFileUpload, Button } from '@wordpress/components';\nimport { upload as uploadIcon } from '@wordpress/icons';\nimport { DataViewsPicker } from '@wordpress/dataviews';\nimport type { View, Field, ActionButton } from '@wordpress/dataviews';\nimport {\n\taltTextField,\n\tattachedToField,\n\tauthorField,\n\tcaptionField,\n\tdateAddedField,\n\tdateModifiedField,\n\tdescriptionField,\n\tfilenameField,\n\tfilesizeField,\n\tmediaDimensionsField,\n\tmediaThumbnailField,\n\tmimeTypeField,\n} from '@wordpress/media-fields';\nimport { store as noticesStore, SnackbarNotices } from '@wordpress/notices';\nimport { isBlobURL } from '@wordpress/blob';\n\n/**\n * Internal dependencies\n */\nimport type { Attachment, RestAttachment } from '../../utils/types';\nimport { transformAttachment } from '../../utils/transform-attachment';\nimport { uploadMedia } from '../../utils/upload-media';\nimport { unlock } from '../../lock-unlock';\n\nconst { useEntityRecordsWithPermissions } = unlock( coreDataPrivateApis );\n\n// Layout constants - matching the picker layout types\nconst LAYOUT_PICKER_GRID = 'pickerGrid';\nconst LAYOUT_PICKER_TABLE = 'pickerTable';\n\n// Custom notices context for the media modal\nconst NOTICES_CONTEXT = 'media-modal';\n\n// Notice ID - reused for all upload-related notices to prevent flooding\nconst NOTICE_ID_UPLOAD_PROGRESS = 'media-modal-upload-progress';\n\ninterface MediaUploadModalProps {\n\t/**\n\t * Array of allowed media types.\n\t */\n\tallowedTypes?: string[];\n\n\t/**\n\t * Whether multiple files can be selected.\n\t * @default false\n\t */\n\tmultiple?: boolean;\n\n\t/**\n\t * The currently selected media item(s).\n\t * Can be a single ID number or array of IDs for multiple selection.\n\t */\n\tvalue?: number | number[];\n\n\t/**\n\t * Function called when media is selected.\n\t * Receives single attachment object or array of attachments.\n\t */\n\tonSelect: ( media: Attachment | Attachment[] ) => void;\n\n\t/**\n\t * Function called when the modal is closed without selection.\n\t */\n\tonClose?: () => void;\n\n\t/**\n\t * Function to handle media uploads.\n\t * If not provided, drag and drop will be disabled.\n\t */\n\tonUpload?: ( args: {\n\t\tallowedTypes?: string[];\n\t\tfilesList: File[];\n\t\tonFileChange?: ( attachments: Partial< Attachment >[] ) => void;\n\t\tonError?: ( error: Error ) => void;\n\t\tmultiple?: boolean;\n\t} ) => void;\n\n\t/**\n\t * Title for the modal.\n\t * @default 'Select Media'\n\t */\n\ttitle?: string;\n\n\t/**\n\t * Whether the modal is open.\n\t */\n\tisOpen: boolean;\n\n\t/**\n\t * Whether the modal can be closed by clicking outside or pressing escape.\n\t * @default true\n\t */\n\tisDismissible?: boolean;\n\n\t/**\n\t * Additional CSS class for the modal.\n\t */\n\tmodalClass?: string;\n\n\t/**\n\t * Whether to show a search input.\n\t * @default true\n\t */\n\tsearch?: boolean;\n\n\t/**\n\t * Label for the search input.\n\t */\n\tsearchLabel?: string;\n}\n\n/**\n * MediaUploadModal component that uses Modal and DataViewsPicker for media selection.\n *\n * This is a modern functional component alternative to the legacy MediaUpload class component.\n * It provides a cleaner API and better integration with the WordPress block editor.\n *\n * @param props Component props\n * @param props.allowedTypes Array of allowed media types\n * @param props.multiple Whether multiple files can be selected\n * @param props.value Currently selected media item(s)\n * @param props.onSelect Function called when media is selected\n * @param props.onClose Function called when modal is closed\n * @param props.onUpload Function to handle media uploads\n * @param props.title Title for the modal\n * @param props.isOpen Whether the modal is open\n * @param props.isDismissible Whether modal can be dismissed\n * @param props.modalClass Additional CSS class for modal\n * @param props.search Whether to show search input\n * @param props.searchLabel Label for search input\n * @return JSX element or null\n */\nexport function MediaUploadModal( {\n\tallowedTypes,\n\tmultiple = false,\n\tvalue,\n\tonSelect,\n\tonClose,\n\tonUpload,\n\ttitle = __( 'Select Media' ),\n\tisOpen,\n\tisDismissible = true,\n\tmodalClass,\n\tsearch = true,\n\tsearchLabel = __( 'Search media' ),\n}: MediaUploadModalProps ) {\n\tconst [ selection, setSelection ] = useState< string[] >( () => {\n\t\tif ( ! value ) {\n\t\t\treturn [];\n\t\t}\n\t\treturn Array.isArray( value )\n\t\t\t? value.map( String )\n\t\t\t: [ String( value ) ];\n\t} );\n\n\tconst { createSuccessNotice, createErrorNotice, createInfoNotice } =\n\t\tuseDispatch( noticesStore );\n\tconst { invalidateResolution } = useDispatch( coreStore );\n\n\t// DataViews configuration - allow view updates\n\tconst [ view, setView ] = useState< View >( () => ( {\n\t\ttype: LAYOUT_PICKER_GRID,\n\t\tfields: [],\n\t\tshowTitle: false,\n\t\ttitleField: 'title',\n\t\tmediaField: 'media_thumbnail',\n\t\tsearch: '',\n\t\tpage: 1,\n\t\tperPage: 20,\n\t\tfilters: [],\n\t\tlayout: {\n\t\t\tpreviewSize: 170,\n\t\t},\n\t} ) );\n\n\t// Build query args based on view properties, similar to PostList\n\tconst queryArgs = useMemo( () => {\n\t\tconst filters: Record< string, any > = {};\n\n\t\tview.filters?.forEach( ( filter ) => {\n\t\t\t// Handle media type filters\n\t\t\tif ( filter.field === 'media_type' ) {\n\t\t\t\tfilters.media_type = filter.value;\n\t\t\t}\n\t\t\t// Handle author filters\n\t\t\tif ( filter.field === 'author' ) {\n\t\t\t\tif ( filter.operator === 'isAny' ) {\n\t\t\t\t\tfilters.author = filter.value;\n\t\t\t\t} else if ( filter.operator === 'isNone' ) {\n\t\t\t\t\tfilters.author_exclude = filter.value;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Handle date filters\n\t\t\tif ( filter.field === 'date' || filter.field === 'modified' ) {\n\t\t\t\tif ( filter.operator === 'before' ) {\n\t\t\t\t\tfilters.before = filter.value;\n\t\t\t\t} else if ( filter.operator === 'after' ) {\n\t\t\t\t\tfilters.after = filter.value;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Handle mime type filters\n\t\t\tif ( filter.field === 'mime_type' ) {\n\t\t\t\tfilters.mime_type = filter.value;\n\t\t\t}\n\t\t} );\n\n\t\t// Base media type on allowedTypes if no filter is set\n\t\tif ( ! filters.media_type ) {\n\t\t\tfilters.media_type = allowedTypes?.includes( '*' )\n\t\t\t\t? undefined\n\t\t\t\t: allowedTypes;\n\t\t}\n\n\t\treturn {\n\t\t\tper_page: view.perPage || 20,\n\t\t\tpage: view.page || 1,\n\t\t\tstatus: 'inherit',\n\t\t\torder: view.sort?.direction,\n\t\t\torderby: view.sort?.field,\n\t\t\tsearch: view.search,\n\t\t\t_embed: 'author,wp:attached-to',\n\t\t\t...filters,\n\t\t};\n\t}, [ view, allowedTypes ] );\n\n\t// Fetch all media attachments using WordPress core data with permissions\n\tconst {\n\t\trecords: mediaRecords,\n\t\tisResolving: isLoading,\n\t\ttotalItems,\n\t\ttotalPages,\n\t} = useEntityRecordsWithPermissions( 'postType', 'attachment', queryArgs );\n\n\tconst fields: Field< RestAttachment >[] = useMemo(\n\t\t() => [\n\t\t\t// Media field definitions from @wordpress/media-fields\n\t\t\t// Cast is safe because RestAttachment has the same properties as Attachment\n\t\t\t{\n\t\t\t\t...( mediaThumbnailField as Field< RestAttachment > ),\n\t\t\t\tenableHiding: false, // Within the modal, the thumbnail should always be shown.\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'title',\n\t\t\t\ttype: 'text' as const,\n\t\t\t\tlabel: __( 'Title' ),\n\t\t\t\tgetValue: ( { item }: { item: RestAttachment } ) => {\n\t\t\t\t\tconst titleValue = item.title.raw || item.title.rendered;\n\t\t\t\t\treturn titleValue || __( '(no title)' );\n\t\t\t\t},\n\t\t\t},\n\t\t\taltTextField as Field< RestAttachment >,\n\t\t\tcaptionField as Field< RestAttachment >,\n\t\t\tdescriptionField as Field< RestAttachment >,\n\t\t\tdateAddedField as Field< RestAttachment >,\n\t\t\tdateModifiedField as Field< RestAttachment >,\n\t\t\tauthorField as Field< RestAttachment >,\n\t\t\tfilenameField as Field< RestAttachment >,\n\t\t\tfilesizeField as Field< RestAttachment >,\n\t\t\tmediaDimensionsField as Field< RestAttachment >,\n\t\t\tmimeTypeField as Field< RestAttachment >,\n\t\t\tattachedToField as Field< RestAttachment >,\n\t\t],\n\t\t[]\n\t);\n\n\tconst actions: ActionButton< RestAttachment >[] = useMemo(\n\t\t() => [\n\t\t\t{\n\t\t\t\tid: 'select',\n\t\t\t\tlabel: multiple ? __( 'Select' ) : __( 'Select' ),\n\t\t\t\tisPrimary: true,\n\t\t\t\tsupportsBulk: multiple,\n\t\t\t\tasync callback() {\n\t\t\t\t\tif ( selection.length === 0 ) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst selectedPostsQuery = {\n\t\t\t\t\t\tinclude: selection,\n\t\t\t\t\t\tper_page: -1,\n\t\t\t\t\t};\n\n\t\t\t\t\tconst selectedPosts = await resolveSelect(\n\t\t\t\t\t\tcoreStore\n\t\t\t\t\t).getEntityRecords< RestAttachment >(\n\t\t\t\t\t\t'postType',\n\t\t\t\t\t\t'attachment',\n\t\t\t\t\t\tselectedPostsQuery\n\t\t\t\t\t);\n\n\t\t\t\t\t// Transform the selected posts to the expected Attachment format\n\t\t\t\t\tconst transformedPosts = ( selectedPosts ?? [] )\n\t\t\t\t\t\t.map( transformAttachment )\n\t\t\t\t\t\t.filter( Boolean );\n\n\t\t\t\t\tconst selectedItems = multiple\n\t\t\t\t\t\t? transformedPosts\n\t\t\t\t\t\t: transformedPosts?.[ 0 ];\n\n\t\t\t\t\tonSelect( selectedItems );\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[ multiple, onSelect, selection ]\n\t);\n\n\tconst handleModalClose = useCallback( () => {\n\t\tonClose?.();\n\t}, [ onClose ] );\n\n\t// Use onUpload if provided, otherwise fall back to uploadMedia\n\tconst handleUpload = onUpload || uploadMedia;\n\n\t// Shared upload success handler\n\tconst handleUploadComplete = useCallback(\n\t\t( attachments: Partial< Attachment >[] ) => {\n\t\t\t// Check if all uploads are complete (no blob URLs)\n\t\t\tconst allComplete = attachments.every(\n\t\t\t\t( attachment ) =>\n\t\t\t\t\tattachment.id &&\n\t\t\t\t\tattachment.url &&\n\t\t\t\t\t! isBlobURL( attachment.url )\n\t\t\t);\n\n\t\t\tif ( allComplete && attachments.length > 0 ) {\n\t\t\t\t// Show success notice (replaces progress notice via ID)\n\t\t\t\tcreateSuccessNotice(\n\t\t\t\t\tsprintf(\n\t\t\t\t\t\t// translators: %s: number of files\n\t\t\t\t\t\t_n(\n\t\t\t\t\t\t\t'Uploaded %s file',\n\t\t\t\t\t\t\t'Uploaded %s files',\n\t\t\t\t\t\t\tattachments.length\n\t\t\t\t\t\t),\n\t\t\t\t\t\tattachments.length.toLocaleString()\n\t\t\t\t\t),\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'snackbar',\n\t\t\t\t\t\tcontext: NOTICES_CONTEXT,\n\t\t\t\t\t\tid: NOTICE_ID_UPLOAD_PROGRESS,\n\t\t\t\t\t}\n\t\t\t\t);\n\n\t\t\t\t// Auto-select the newly uploaded items\n\t\t\t\tconst uploadedIds = attachments\n\t\t\t\t\t.map( ( attachment ) => String( attachment.id ) )\n\t\t\t\t\t.filter( Boolean );\n\n\t\t\t\tif ( multiple ) {\n\t\t\t\t\t// In multiple mode, add to existing selection\n\t\t\t\t\tsetSelection( ( prev ) => [ ...prev, ...uploadedIds ] );\n\t\t\t\t} else {\n\t\t\t\t\t// In single mode, replace selection with the first uploaded item\n\t\t\t\t\tsetSelection( uploadedIds.slice( 0, 1 ) );\n\t\t\t\t}\n\n\t\t\t\t// Invalidate the entity records resolution to refresh the view\n\t\t\t\tinvalidateResolution( 'getEntityRecords', [\n\t\t\t\t\t'postType',\n\t\t\t\t\t'attachment',\n\t\t\t\t\tqueryArgs,\n\t\t\t\t] );\n\t\t\t}\n\t\t},\n\t\t[ createSuccessNotice, invalidateResolution, queryArgs, multiple ]\n\t);\n\n\t// Shared upload error handler\n\tconst handleUploadError = useCallback(\n\t\t( error: Error ) => {\n\t\t\t// Show error notice (replaces progress notice via ID)\n\t\t\tcreateErrorNotice( error.message, {\n\t\t\t\ttype: 'snackbar',\n\t\t\t\tcontext: NOTICES_CONTEXT,\n\t\t\t\tid: NOTICE_ID_UPLOAD_PROGRESS,\n\t\t\t} );\n\t\t},\n\t\t[ createErrorNotice ]\n\t);\n\n\tconst handleFileSelect = useCallback(\n\t\t( event: React.ChangeEvent< HTMLInputElement > ) => {\n\t\t\tconst files = event.target.files;\n\t\t\tif ( files && files.length > 0 ) {\n\t\t\t\tconst filesArray = Array.from( files );\n\n\t\t\t\t// Show upload start notice\n\t\t\t\tcreateInfoNotice(\n\t\t\t\t\tsprintf(\n\t\t\t\t\t\t// translators: %s: number of files\n\t\t\t\t\t\t_n(\n\t\t\t\t\t\t\t'Uploading %s file',\n\t\t\t\t\t\t\t'Uploading %s files',\n\t\t\t\t\t\t\tfilesArray.length\n\t\t\t\t\t\t),\n\t\t\t\t\t\tfilesArray.length.toLocaleString()\n\t\t\t\t\t),\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'snackbar',\n\t\t\t\t\t\tcontext: NOTICES_CONTEXT,\n\t\t\t\t\t\tid: NOTICE_ID_UPLOAD_PROGRESS,\n\t\t\t\t\t\texplicitDismiss: true,\n\t\t\t\t\t}\n\t\t\t\t);\n\n\t\t\t\thandleUpload( {\n\t\t\t\t\tallowedTypes,\n\t\t\t\t\tfilesList: filesArray,\n\t\t\t\t\tonFileChange: handleUploadComplete,\n\t\t\t\t\tonError: handleUploadError,\n\t\t\t\t} );\n\t\t\t}\n\t\t},\n\t\t[\n\t\t\tallowedTypes,\n\t\t\thandleUpload,\n\t\t\tcreateInfoNotice,\n\t\t\thandleUploadComplete,\n\t\t\thandleUploadError,\n\t\t]\n\t);\n\n\tconst paginationInfo = useMemo(\n\t\t() => ( {\n\t\t\ttotalItems,\n\t\t\ttotalPages,\n\t\t} ),\n\t\t[ totalItems, totalPages ]\n\t);\n\n\tconst defaultLayouts = useMemo(\n\t\t() => ( {\n\t\t\t[ LAYOUT_PICKER_GRID ]: {\n\t\t\t\tfields: [],\n\t\t\t\tshowTitle: false,\n\t\t\t},\n\t\t\t[ LAYOUT_PICKER_TABLE ]: {\n\t\t\t\tfields: [\n\t\t\t\t\t'filename',\n\t\t\t\t\t'filesize',\n\t\t\t\t\t'media_dimensions',\n\t\t\t\t\t'author',\n\t\t\t\t\t'date',\n\t\t\t\t],\n\t\t\t\tshowTitle: true,\n\t\t\t},\n\t\t} ),\n\t\t[]\n\t);\n\n\t// Build accept attribute from allowedTypes\n\tconst acceptTypes = useMemo( () => {\n\t\tif ( allowedTypes?.includes( '*' ) ) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn allowedTypes?.join( ',' );\n\t}, [ allowedTypes ] );\n\n\tif ( ! isOpen ) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<Modal\n\t\t\ttitle={ title }\n\t\t\tonRequestClose={ handleModalClose }\n\t\t\tisDismissible={ isDismissible }\n\t\t\tclassName={ modalClass }\n\t\t\toverlayClassName=\"media-upload-modal\"\n\t\t\tsize=\"fill\"\n\t\t\theaderActions={\n\t\t\t\t<FormFileUpload\n\t\t\t\t\taccept={ acceptTypes }\n\t\t\t\t\tmultiple\n\t\t\t\t\tonChange={ handleFileSelect }\n\t\t\t\t\t__next40pxDefaultSize\n\t\t\t\t\trender={ ( { openFileDialog } ) => (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tonClick={ openFileDialog }\n\t\t\t\t\t\t\ticon={ uploadIcon }\n\t\t\t\t\t\t\t__next40pxDefaultSize\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{ __( 'Upload media' ) }\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t) }\n\t\t\t\t/>\n\t\t\t}\n\t\t>\n\t\t\t<DropZone\n\t\t\t\tonFilesDrop={ ( files ) => {\n\t\t\t\t\tlet filteredFiles = files;\n\t\t\t\t\t// Filter files by allowed types if specified\n\t\t\t\t\tif ( allowedTypes && ! allowedTypes.includes( '*' ) ) {\n\t\t\t\t\t\tfilteredFiles = files.filter( ( file ) =>\n\t\t\t\t\t\t\tallowedTypes.some( ( allowedType ) => {\n\t\t\t\t\t\t\t\t// Check if the file type matches the allowed MIME type\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\tfile.type === allowedType ||\n\t\t\t\t\t\t\t\t\tfile.type.startsWith(\n\t\t\t\t\t\t\t\t\t\tallowedType.replace( '*', '' )\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif ( filteredFiles.length > 0 ) {\n\t\t\t\t\t\t// Show upload start notice\n\t\t\t\t\t\tcreateInfoNotice(\n\t\t\t\t\t\t\tsprintf(\n\t\t\t\t\t\t\t\t// translators: %s: number of files\n\t\t\t\t\t\t\t\t_n(\n\t\t\t\t\t\t\t\t\t'Uploading %s file',\n\t\t\t\t\t\t\t\t\t'Uploading %s files',\n\t\t\t\t\t\t\t\t\tfilteredFiles.length\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\tfilteredFiles.length.toLocaleString()\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: 'snackbar',\n\t\t\t\t\t\t\t\tcontext: NOTICES_CONTEXT,\n\t\t\t\t\t\t\t\tid: NOTICE_ID_UPLOAD_PROGRESS,\n\t\t\t\t\t\t\t\texplicitDismiss: true,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\thandleUpload( {\n\t\t\t\t\t\t\tallowedTypes,\n\t\t\t\t\t\t\tfilesList: filteredFiles,\n\t\t\t\t\t\t\tonFileChange: handleUploadComplete,\n\t\t\t\t\t\t\tonError: handleUploadError,\n\t\t\t\t\t\t} );\n\t\t\t\t\t}\n\t\t\t\t} }\n\t\t\t\tlabel={ __( 'Drop files to upload' ) }\n\t\t\t/>\n\t\t\t<DataViewsPicker\n\t\t\t\tdata={ mediaRecords || [] }\n\t\t\t\tfields={ fields }\n\t\t\t\tview={ view }\n\t\t\t\tonChangeView={ setView }\n\t\t\t\tactions={ actions }\n\t\t\t\tselection={ selection }\n\t\t\t\tonChangeSelection={ setSelection }\n\t\t\t\tisLoading={ isLoading }\n\t\t\t\tpaginationInfo={ paginationInfo }\n\t\t\t\tdefaultLayouts={ defaultLayouts }\n\t\t\t\tgetItemId={ ( item: RestAttachment ) => String( item.id ) }\n\t\t\t\tsearch={ search }\n\t\t\t\tsearchLabel={ searchLabel }\n\t\t\t\titemListLabel={ __( 'Media items' ) }\n\t\t\t/>\n\t\t\t{ createPortal(\n\t\t\t\t<SnackbarNotices\n\t\t\t\t\tclassName=\"media-upload-modal__snackbar\"\n\t\t\t\t\tcontext={ NOTICES_CONTEXT }\n\t\t\t\t/>,\n\t\t\t\tdocument.body\n\t\t\t) }\n\t\t</Modal>\n\t);\n}\n\nexport default MediaUploadModal;\n"],
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["coreDataPrivateApis", "noticesStore", "coreStore", "uploadIcon"]
|
|
4
|
+
"sourcesContent": ["/**\n * External dependencies\n */\nimport clsx from 'clsx';\n\n/**\n * WordPress dependencies\n */\nimport {\n\tcreatePortal,\n\tuseState,\n\tuseCallback,\n\tuseMemo,\n\tuseRef,\n\tuseEffect,\n} from '@wordpress/element';\nimport { __, sprintf, _n } from '@wordpress/i18n';\nimport {\n\tprivateApis as coreDataPrivateApis,\n\tstore as coreStore,\n} from '@wordpress/core-data';\nimport { resolveSelect, useDispatch } from '@wordpress/data';\nimport { Modal, DropZone, FormFileUpload, Button } from '@wordpress/components';\nimport { upload as uploadIcon } from '@wordpress/icons';\nimport { DataViewsPicker } from '@wordpress/dataviews';\nimport type { View, Field, ActionButton } from '@wordpress/dataviews';\nimport { Stack } from '@wordpress/ui';\nimport {\n\taltTextField,\n\tattachedToField,\n\tauthorField,\n\tcaptionField,\n\tdateAddedField,\n\tdateModifiedField,\n\tdescriptionField,\n\tfilenameField,\n\tfilesizeField,\n\tmediaDimensionsField,\n\tmediaThumbnailField,\n\tmimeTypeField,\n} from '@wordpress/media-fields';\nimport { store as noticesStore, SnackbarNotices } from '@wordpress/notices';\n\n/**\n * Internal dependencies\n */\nimport type { Attachment, RestAttachment } from '../../utils/types';\nimport { transformAttachment } from '../../utils/transform-attachment';\nimport { uploadMedia } from '../../utils/upload-media';\nimport { unlock } from '../../lock-unlock';\nimport { UploadStatusPopover } from './upload-status-popover';\nimport { useInvalidateAttachmentResolutions } from './use-invalidate-attachment-resolutions';\nimport { useUploadStatus } from './use-upload-status';\n\nconst { useEntityRecordsWithPermissions } = unlock( coreDataPrivateApis );\n\n// Layout constants - matching the picker layout types\nconst LAYOUT_PICKER_GRID = 'pickerGrid';\nconst LAYOUT_PICKER_TABLE = 'pickerTable';\n\n// Custom notices context for the media modal\nconst NOTICES_CONTEXT = 'media-modal';\n\n// Notice ID - reused for all upload-related notices to prevent flooding\nconst NOTICE_ID_UPLOAD_PROGRESS = 'media-modal-upload-progress';\n\ninterface MediaUploadModalProps {\n\t/**\n\t * Array of allowed media types.\n\t */\n\tallowedTypes?: string[];\n\n\t/**\n\t * Whether multiple files can be selected.\n\t * @default false\n\t */\n\tmultiple?: boolean;\n\n\t/**\n\t * The currently selected media item(s).\n\t * Can be a single ID number or array of IDs for multiple selection.\n\t */\n\tvalue?: number | number[];\n\n\t/**\n\t * Function called when media is selected.\n\t * Receives single attachment object or array of attachments.\n\t */\n\tonSelect: ( media: Attachment | Attachment[] ) => void;\n\n\t/**\n\t * Function called when the modal is closed without selection.\n\t */\n\tonClose?: () => void;\n\n\t/**\n\t * Function to handle media uploads.\n\t * If not provided, drag and drop will be disabled.\n\t */\n\tonUpload?: ( args: {\n\t\tallowedTypes?: string[];\n\t\tfilesList: File[];\n\t\tonFileChange?: ( attachments: Partial< Attachment >[] ) => void;\n\t\tonError?: ( error: Error ) => void;\n\t\tmultiple?: boolean;\n\t} ) => void;\n\n\t/**\n\t * Title for the modal.\n\t * @default 'Select Media'\n\t */\n\ttitle?: string;\n\n\t/**\n\t * Whether the modal is open.\n\t */\n\tisOpen: boolean;\n\n\t/**\n\t * Whether the modal can be closed by clicking outside or pressing escape.\n\t * @default true\n\t */\n\tisDismissible?: boolean;\n\n\t/**\n\t * Additional CSS class for the modal.\n\t */\n\tmodalClass?: string;\n\n\t/**\n\t * Whether to show a search input.\n\t * @default true\n\t */\n\tsearch?: boolean;\n\n\t/**\n\t * Label for the search input.\n\t */\n\tsearchLabel?: string;\n}\n\n/**\n * MediaUploadModal component that uses Modal and DataViewsPicker for media selection.\n *\n * This is a modern functional component alternative to the legacy MediaUpload class component.\n * It provides a cleaner API and better integration with the WordPress block editor.\n *\n * @param props Component props\n * @param props.allowedTypes Array of allowed media types\n * @param props.multiple Whether multiple files can be selected\n * @param props.value Currently selected media item(s)\n * @param props.onSelect Function called when media is selected\n * @param props.onClose Function called when modal is closed\n * @param props.onUpload Function to handle media uploads\n * @param props.title Title for the modal\n * @param props.isOpen Whether the modal is open\n * @param props.isDismissible Whether modal can be dismissed\n * @param props.modalClass Additional CSS class for modal\n * @param props.search Whether to show search input\n * @param props.searchLabel Label for search input\n * @return JSX element or null\n */\nexport function MediaUploadModal( {\n\tallowedTypes,\n\tmultiple = false,\n\tvalue,\n\tonSelect,\n\tonClose,\n\tonUpload,\n\ttitle = __( 'Select Media' ),\n\tisOpen,\n\tisDismissible = true,\n\tmodalClass,\n\tsearch = true,\n\tsearchLabel = __( 'Search media' ),\n}: MediaUploadModalProps ) {\n\tconst [ selection, setSelection ] = useState< string[] >( () => {\n\t\tif ( ! value ) {\n\t\t\treturn [];\n\t\t}\n\t\treturn Array.isArray( value )\n\t\t\t? value.map( String )\n\t\t\t: [ String( value ) ];\n\t} );\n\n\tconst { createSuccessNotice, removeAllNotices } =\n\t\tuseDispatch( noticesStore );\n\tconst invalidateAttachmentResolutions =\n\t\tuseInvalidateAttachmentResolutions();\n\n\t// DataViews configuration - allow view updates\n\tconst [ view, setView ] = useState< View >( () => ( {\n\t\ttype: LAYOUT_PICKER_GRID,\n\t\tfields: [],\n\t\tshowTitle: false,\n\t\ttitleField: 'title',\n\t\tmediaField: 'media_thumbnail',\n\t\tsearch: '',\n\t\tpage: 1,\n\t\tperPage: 50,\n\t\tfilters: [],\n\t\tlayout: {\n\t\t\tpreviewSize: 170,\n\t\t\tdensity: 'compact',\n\t\t},\n\t} ) );\n\n\t// Build query args based on view properties, similar to PostList\n\tconst queryArgs = useMemo( () => {\n\t\tconst filters: Record< string, any > = {};\n\n\t\tview.filters?.forEach( ( filter ) => {\n\t\t\t// Handle media type filters\n\t\t\tif ( filter.field === 'media_type' ) {\n\t\t\t\tfilters.media_type = filter.value;\n\t\t\t}\n\t\t\t// Handle author filters\n\t\t\tif ( filter.field === 'author' ) {\n\t\t\t\tif ( filter.operator === 'isAny' ) {\n\t\t\t\t\tfilters.author = filter.value;\n\t\t\t\t} else if ( filter.operator === 'isNone' ) {\n\t\t\t\t\tfilters.author_exclude = filter.value;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Handle date filters\n\t\t\tif ( filter.field === 'date' || filter.field === 'modified' ) {\n\t\t\t\tif ( filter.operator === 'before' ) {\n\t\t\t\t\tfilters.before = filter.value;\n\t\t\t\t} else if ( filter.operator === 'after' ) {\n\t\t\t\t\tfilters.after = filter.value;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Handle mime type filters\n\t\t\tif ( filter.field === 'mime_type' ) {\n\t\t\t\tfilters.mime_type = filter.value;\n\t\t\t}\n\t\t} );\n\n\t\t// Base media type on allowedTypes if no filter is set\n\t\tif ( ! filters.media_type ) {\n\t\t\tfilters.media_type = allowedTypes?.includes( '*' )\n\t\t\t\t? undefined\n\t\t\t\t: allowedTypes;\n\t\t}\n\n\t\treturn {\n\t\t\tper_page: view.perPage || 20,\n\t\t\tpage: view.page || 1,\n\t\t\tstatus: 'inherit',\n\t\t\torder: view.sort?.direction,\n\t\t\torderby: view.sort?.field,\n\t\t\tsearch: view.search,\n\t\t\t_embed: 'author,wp:attached-to',\n\t\t\t...filters,\n\t\t};\n\t}, [ view, allowedTypes ] );\n\n\t// Per-batch completion handler: auto-select uploaded items and refresh the grid.\n\tconst handleBatchComplete = useCallback(\n\t\t( attachments: Partial< Attachment >[] ) => {\n\t\t\tconst uploadedIds = attachments\n\t\t\t\t.map( ( attachment ) => String( attachment.id ) )\n\t\t\t\t.filter( Boolean );\n\n\t\t\tif ( multiple ) {\n\t\t\t\tsetSelection( ( prev ) => {\n\t\t\t\t\tconst existing = new Set( prev );\n\t\t\t\t\tconst newIds = uploadedIds.filter(\n\t\t\t\t\t\t( id ) => ! existing.has( id )\n\t\t\t\t\t);\n\t\t\t\t\treturn [ ...prev, ...newIds ];\n\t\t\t\t} );\n\t\t\t} else {\n\t\t\t\tsetSelection( uploadedIds.slice( 0, 1 ) );\n\t\t\t}\n\n\t\t\t// Invalidate all cached attachment queries so every page of\n\t\t\t// results refreshes \u2014 not just the page the user is viewing.\n\t\t\tinvalidateAttachmentResolutions();\n\t\t},\n\t\t[ multiple, invalidateAttachmentResolutions ]\n\t);\n\n\tconst {\n\t\tuploadingFiles,\n\t\tregisterBatch,\n\t\tdismissError,\n\t\tclearCompleted,\n\t\tallComplete,\n\t} = useUploadStatus( { onBatchComplete: handleBatchComplete } );\n\n\tconst isPopoverOpenRef = useRef( false );\n\tconst handlePopoverOpenChange = useCallback(\n\t\t( open: boolean ) => {\n\t\t\tisPopoverOpenRef.current = open;\n\t\t\tif ( ! open ) {\n\t\t\t\tclearCompleted();\n\t\t\t}\n\t\t},\n\t\t[ clearCompleted ]\n\t);\n\n\t// Fetch all media attachments using WordPress core data with permissions\n\tconst {\n\t\trecords: mediaRecords,\n\t\tisResolving: isLoading,\n\t\ttotalItems,\n\t\ttotalPages,\n\t} = useEntityRecordsWithPermissions( 'postType', 'attachment', queryArgs );\n\n\tconst fields: Field< RestAttachment >[] = useMemo(\n\t\t() => [\n\t\t\t// Media field definitions from @wordpress/media-fields\n\t\t\t// Cast is safe because RestAttachment has the same properties as Attachment\n\t\t\t{\n\t\t\t\t...( mediaThumbnailField as Field< RestAttachment > ),\n\t\t\t\tenableHiding: false, // Within the modal, the thumbnail should always be shown.\n\t\t\t},\n\t\t\t{\n\t\t\t\tid: 'title',\n\t\t\t\ttype: 'text' as const,\n\t\t\t\tlabel: __( 'Title' ),\n\t\t\t\tgetValue: ( { item }: { item: RestAttachment } ) => {\n\t\t\t\t\tconst titleValue = item.title.raw || item.title.rendered;\n\t\t\t\t\treturn titleValue || __( '(no title)' );\n\t\t\t\t},\n\t\t\t},\n\t\t\taltTextField as Field< RestAttachment >,\n\t\t\tcaptionField as Field< RestAttachment >,\n\t\t\tdescriptionField as Field< RestAttachment >,\n\t\t\tdateAddedField as Field< RestAttachment >,\n\t\t\tdateModifiedField as Field< RestAttachment >,\n\t\t\tauthorField as Field< RestAttachment >,\n\t\t\tfilenameField as Field< RestAttachment >,\n\t\t\tfilesizeField as Field< RestAttachment >,\n\t\t\tmediaDimensionsField as Field< RestAttachment >,\n\t\t\tmimeTypeField as Field< RestAttachment >,\n\t\t\tattachedToField as Field< RestAttachment >,\n\t\t],\n\t\t[]\n\t);\n\n\tconst actions: ActionButton< RestAttachment >[] = useMemo(\n\t\t() => [\n\t\t\t{\n\t\t\t\tid: 'select',\n\t\t\t\tlabel: __( 'Select' ),\n\t\t\t\tisPrimary: true,\n\t\t\t\tsupportsBulk: multiple,\n\t\t\t\tasync callback() {\n\t\t\t\t\tif ( selection.length === 0 ) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst selectedPostsQuery = {\n\t\t\t\t\t\tinclude: selection,\n\t\t\t\t\t\tper_page: -1,\n\t\t\t\t\t};\n\n\t\t\t\t\tconst selectedPosts = await resolveSelect(\n\t\t\t\t\t\tcoreStore\n\t\t\t\t\t).getEntityRecords< RestAttachment >(\n\t\t\t\t\t\t'postType',\n\t\t\t\t\t\t'attachment',\n\t\t\t\t\t\tselectedPostsQuery\n\t\t\t\t\t);\n\n\t\t\t\t\t// Transform the selected posts to the expected Attachment format\n\t\t\t\t\tconst transformedPosts = ( selectedPosts ?? [] )\n\t\t\t\t\t\t.map( transformAttachment )\n\t\t\t\t\t\t.filter( Boolean );\n\n\t\t\t\t\tconst selectedItems = multiple\n\t\t\t\t\t\t? transformedPosts\n\t\t\t\t\t\t: transformedPosts?.[ 0 ];\n\n\t\t\t\t\tremoveAllNotices( 'snackbar', NOTICES_CONTEXT );\n\t\t\t\t\tonSelect( selectedItems );\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\t[ multiple, onSelect, selection, removeAllNotices ]\n\t);\n\n\tconst handleModalClose = useCallback( () => {\n\t\tremoveAllNotices( 'snackbar', NOTICES_CONTEXT );\n\t\tonClose?.();\n\t}, [ removeAllNotices, onClose ] );\n\n\t// Use onUpload if provided, otherwise fall back to uploadMedia\n\tconst handleUpload = onUpload || uploadMedia;\n\n\t// Show success notice and auto-clear completed entries when all batches finish.\n\tconst prevAllCompleteRef = useRef( false );\n\tuseEffect( () => {\n\t\tif ( allComplete && ! prevAllCompleteRef.current ) {\n\t\t\tconst completeCount = uploadingFiles.filter(\n\t\t\t\t( file ) => file.status === 'uploaded'\n\t\t\t).length;\n\t\t\tif ( completeCount > 0 ) {\n\t\t\t\tcreateSuccessNotice(\n\t\t\t\t\tsprintf(\n\t\t\t\t\t\t// translators: %s: number of files\n\t\t\t\t\t\t_n(\n\t\t\t\t\t\t\t'Uploaded %s file',\n\t\t\t\t\t\t\t'Uploaded %s files',\n\t\t\t\t\t\t\tcompleteCount\n\t\t\t\t\t\t),\n\t\t\t\t\t\tcompleteCount.toLocaleString()\n\t\t\t\t\t),\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'snackbar',\n\t\t\t\t\t\tcontext: NOTICES_CONTEXT,\n\t\t\t\t\t\tid: NOTICE_ID_UPLOAD_PROGRESS,\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Auto-clear completed entries, unless the popover is\n\t\t\t// open \u2014 in that case, they'll be cleared on close.\n\t\t\tif ( ! isPopoverOpenRef.current ) {\n\t\t\t\tclearCompleted();\n\t\t\t}\n\t\t}\n\t\tprevAllCompleteRef.current = allComplete;\n\t}, [ allComplete, uploadingFiles, createSuccessNotice, clearCompleted ] );\n\n\tconst handleFileSelect = useCallback(\n\t\t( event: React.ChangeEvent< HTMLInputElement > ) => {\n\t\t\tconst files = event.target.files;\n\t\t\tif ( files && files.length > 0 ) {\n\t\t\t\tconst filesArray = Array.from( files );\n\t\t\t\tconst { onFileChange, onError } = registerBatch( filesArray );\n\n\t\t\t\thandleUpload( {\n\t\t\t\t\tallowedTypes,\n\t\t\t\t\tfilesList: filesArray,\n\t\t\t\t\tonFileChange,\n\t\t\t\t\tonError,\n\t\t\t\t} );\n\t\t\t}\n\t\t},\n\t\t[ allowedTypes, handleUpload, registerBatch ]\n\t);\n\n\tconst paginationInfo = useMemo(\n\t\t() => ( {\n\t\t\ttotalItems,\n\t\t\ttotalPages,\n\t\t} ),\n\t\t[ totalItems, totalPages ]\n\t);\n\n\tconst defaultLayouts = useMemo(\n\t\t() => ( {\n\t\t\t[ LAYOUT_PICKER_GRID ]: {\n\t\t\t\tfields: [],\n\t\t\t\tshowTitle: false,\n\t\t\t},\n\t\t\t[ LAYOUT_PICKER_TABLE ]: {\n\t\t\t\tfields: [\n\t\t\t\t\t'filename',\n\t\t\t\t\t'filesize',\n\t\t\t\t\t'media_dimensions',\n\t\t\t\t\t'author',\n\t\t\t\t\t'date',\n\t\t\t\t],\n\t\t\t\tshowTitle: true,\n\t\t\t},\n\t\t} ),\n\t\t[]\n\t);\n\n\t// Build accept attribute from allowedTypes\n\tconst acceptTypes = useMemo( () => {\n\t\tif ( allowedTypes?.includes( '*' ) ) {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn allowedTypes?.join( ',' );\n\t}, [ allowedTypes ] );\n\n\tif ( ! isOpen ) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<Modal\n\t\t\ttitle={ title }\n\t\t\tonRequestClose={ handleModalClose }\n\t\t\tisDismissible={ isDismissible }\n\t\t\tclassName={ modalClass }\n\t\t\toverlayClassName=\"media-upload-modal\"\n\t\t\tsize=\"fill\"\n\t\t\theaderActions={\n\t\t\t\t<FormFileUpload\n\t\t\t\t\taccept={ acceptTypes }\n\t\t\t\t\tmultiple\n\t\t\t\t\tonChange={ handleFileSelect }\n\t\t\t\t\t__next40pxDefaultSize\n\t\t\t\t\trender={ ( { openFileDialog } ) => (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tonClick={ openFileDialog }\n\t\t\t\t\t\t\ticon={ uploadIcon }\n\t\t\t\t\t\t\t__next40pxDefaultSize\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{ __( 'Upload media' ) }\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t) }\n\t\t\t\t/>\n\t\t\t}\n\t\t>\n\t\t\t<DropZone\n\t\t\t\tonFilesDrop={ ( files ) => {\n\t\t\t\t\tlet filteredFiles = files;\n\t\t\t\t\t// Filter files by allowed types if specified\n\t\t\t\t\tif ( allowedTypes && ! allowedTypes.includes( '*' ) ) {\n\t\t\t\t\t\tfilteredFiles = files.filter( ( file ) =>\n\t\t\t\t\t\t\tallowedTypes.some( ( allowedType ) => {\n\t\t\t\t\t\t\t\t// Check if the file type matches the allowed MIME type\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\tfile.type === allowedType ||\n\t\t\t\t\t\t\t\t\tfile.type.startsWith(\n\t\t\t\t\t\t\t\t\t\tallowedType.replace( '*', '' )\n\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} )\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif ( filteredFiles.length > 0 ) {\n\t\t\t\t\t\tconst { onFileChange, onError } =\n\t\t\t\t\t\t\tregisterBatch( filteredFiles );\n\n\t\t\t\t\t\thandleUpload( {\n\t\t\t\t\t\t\tallowedTypes,\n\t\t\t\t\t\t\tfilesList: filteredFiles,\n\t\t\t\t\t\t\tonFileChange,\n\t\t\t\t\t\t\tonError,\n\t\t\t\t\t\t} );\n\t\t\t\t\t}\n\t\t\t\t} }\n\t\t\t\tlabel={ __( 'Drop files to upload' ) }\n\t\t\t/>\n\t\t\t<DataViewsPicker\n\t\t\t\tdata={ mediaRecords || [] }\n\t\t\t\tfields={ fields }\n\t\t\t\tview={ view }\n\t\t\t\tonChangeView={ setView }\n\t\t\t\tactions={ actions }\n\t\t\t\tselection={ selection }\n\t\t\t\tonChangeSelection={ setSelection }\n\t\t\t\tisLoading={ isLoading }\n\t\t\t\tpaginationInfo={ paginationInfo }\n\t\t\t\tdefaultLayouts={ defaultLayouts }\n\t\t\t\tgetItemId={ ( item: RestAttachment ) => String( item.id ) }\n\t\t\t\titemListLabel={ __( 'Media items' ) }\n\t\t\t>\n\t\t\t\t<Stack\n\t\t\t\t\tdirection=\"row\"\n\t\t\t\t\talign=\"top\"\n\t\t\t\t\tjustify=\"space-between\"\n\t\t\t\t\tclassName=\"dataviews__view-actions\"\n\t\t\t\t\tgap=\"xs\"\n\t\t\t\t>\n\t\t\t\t\t<Stack\n\t\t\t\t\t\tdirection=\"row\"\n\t\t\t\t\t\tgap=\"sm\"\n\t\t\t\t\t\tjustify=\"start\"\n\t\t\t\t\t\tclassName=\"dataviews__search\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{ search && (\n\t\t\t\t\t\t\t<DataViewsPicker.Search label={ searchLabel } />\n\t\t\t\t\t\t) }\n\t\t\t\t\t\t<DataViewsPicker.FiltersToggle />\n\t\t\t\t\t</Stack>\n\t\t\t\t\t<Stack direction=\"row\" gap=\"xs\" style={ { flexShrink: 0 } }>\n\t\t\t\t\t\t<DataViewsPicker.LayoutSwitcher />\n\t\t\t\t\t\t<DataViewsPicker.ViewConfig />\n\t\t\t\t\t</Stack>\n\t\t\t\t</Stack>\n\t\t\t\t<DataViewsPicker.FiltersToggled className=\"dataviews-filters__container\" />\n\t\t\t\t<DataViewsPicker.Layout />\n\t\t\t\t<div\n\t\t\t\t\tclassName={ clsx( 'media-upload-modal__footer', {\n\t\t\t\t\t\t'is-uploading': uploadingFiles.length > 0,\n\t\t\t\t\t} ) }\n\t\t\t\t>\n\t\t\t\t\t<UploadStatusPopover\n\t\t\t\t\t\tuploadingFiles={ uploadingFiles }\n\t\t\t\t\t\tonDismissError={ dismissError }\n\t\t\t\t\t\tonOpenChange={ handlePopoverOpenChange }\n\t\t\t\t\t/>\n\t\t\t\t\t<DataViewsPicker.BulkActionToolbar />\n\t\t\t\t</div>\n\t\t\t</DataViewsPicker>\n\t\t\t{ createPortal(\n\t\t\t\t<SnackbarNotices\n\t\t\t\t\tclassName=\"media-upload-modal__snackbar\"\n\t\t\t\t\tcontext={ NOTICES_CONTEXT }\n\t\t\t\t/>,\n\t\t\t\tdocument.body\n\t\t\t) }\n\t\t</Modal>\n\t);\n}\n\nexport default MediaUploadModal;\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["coreDataPrivateApis", "noticesStore", "coreStore", "uploadIcon", "clsx"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// packages/media-utils/src/components/media-upload-modal/upload-status-popover.tsx
|
|
21
|
+
var upload_status_popover_exports = {};
|
|
22
|
+
__export(upload_status_popover_exports, {
|
|
23
|
+
UploadStatusPopover: () => UploadStatusPopover
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(upload_status_popover_exports);
|
|
26
|
+
var import_element = require("@wordpress/element");
|
|
27
|
+
var import_i18n = require("@wordpress/i18n");
|
|
28
|
+
var import_components = require("@wordpress/components");
|
|
29
|
+
var import_icons = require("@wordpress/icons");
|
|
30
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
31
|
+
function UploadStatusPopover({
|
|
32
|
+
uploadingFiles,
|
|
33
|
+
onDismissError,
|
|
34
|
+
onOpenChange
|
|
35
|
+
}) {
|
|
36
|
+
const [isOpen, setIsOpen] = (0, import_element.useState)(false);
|
|
37
|
+
const [prevHadErrors, setPrevHadErrors] = (0, import_element.useState)(false);
|
|
38
|
+
const triggerRef = (0, import_element.useRef)(null);
|
|
39
|
+
const updateIsOpen = (0, import_element.useCallback)(
|
|
40
|
+
(open) => {
|
|
41
|
+
setIsOpen(open);
|
|
42
|
+
onOpenChange?.(open);
|
|
43
|
+
},
|
|
44
|
+
[onOpenChange]
|
|
45
|
+
);
|
|
46
|
+
const activeFiles = uploadingFiles.filter(
|
|
47
|
+
(file) => file.status === "uploading"
|
|
48
|
+
);
|
|
49
|
+
const errorFiles = uploadingFiles.filter(
|
|
50
|
+
(file) => file.status === "error"
|
|
51
|
+
);
|
|
52
|
+
const hasErrors = errorFiles.length > 0;
|
|
53
|
+
const isUploading = activeFiles.length > 0;
|
|
54
|
+
(0, import_element.useEffect)(() => {
|
|
55
|
+
if (hasErrors && !prevHadErrors) {
|
|
56
|
+
updateIsOpen(true);
|
|
57
|
+
}
|
|
58
|
+
setPrevHadErrors(hasErrors);
|
|
59
|
+
}, [hasErrors, prevHadErrors, updateIsOpen]);
|
|
60
|
+
if (uploadingFiles.length === 0) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
let buttonLabel, popoverHeading;
|
|
64
|
+
if (isUploading) {
|
|
65
|
+
buttonLabel = (0, import_i18n.sprintf)(
|
|
66
|
+
// translators: %s: number of files being uploaded
|
|
67
|
+
(0, import_i18n._n)("Uploading %s file", "Uploading %s files", activeFiles.length),
|
|
68
|
+
activeFiles.length.toLocaleString()
|
|
69
|
+
);
|
|
70
|
+
popoverHeading = (0, import_i18n.__)("Uploading");
|
|
71
|
+
} else if (hasErrors) {
|
|
72
|
+
buttonLabel = (0, import_i18n.sprintf)(
|
|
73
|
+
// translators: %s: number of upload errors
|
|
74
|
+
(0, import_i18n._n)("%s upload error", "%s upload errors", errorFiles.length),
|
|
75
|
+
errorFiles.length.toLocaleString()
|
|
76
|
+
);
|
|
77
|
+
popoverHeading = (0, import_i18n.__)("Upload errors");
|
|
78
|
+
} else {
|
|
79
|
+
buttonLabel = (0, import_i18n.__)("Upload complete");
|
|
80
|
+
popoverHeading = (0, import_i18n.__)("Upload complete");
|
|
81
|
+
}
|
|
82
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "media-upload-modal__upload-status", children: [
|
|
83
|
+
isUploading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Spinner, {}),
|
|
84
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
85
|
+
import_components.Button,
|
|
86
|
+
{
|
|
87
|
+
className: "media-upload-modal__upload-status__trigger",
|
|
88
|
+
size: "compact",
|
|
89
|
+
icon: import_icons.chevronDown,
|
|
90
|
+
iconPosition: "right",
|
|
91
|
+
onClick: () => updateIsOpen(!isOpen),
|
|
92
|
+
"aria-expanded": isOpen,
|
|
93
|
+
ref: triggerRef,
|
|
94
|
+
children: buttonLabel
|
|
95
|
+
}
|
|
96
|
+
),
|
|
97
|
+
isOpen && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
98
|
+
import_components.Popover,
|
|
99
|
+
{
|
|
100
|
+
className: "media-upload-modal__upload-status__popover",
|
|
101
|
+
placement: "top-start",
|
|
102
|
+
offset: 8,
|
|
103
|
+
anchor: triggerRef.current,
|
|
104
|
+
focusOnMount: true,
|
|
105
|
+
onClose: () => {
|
|
106
|
+
if (triggerRef.current?.contains(
|
|
107
|
+
triggerRef.current.ownerDocument.activeElement
|
|
108
|
+
)) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
updateIsOpen(false);
|
|
112
|
+
},
|
|
113
|
+
children: [
|
|
114
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "media-upload-modal__upload-status__header", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h3", { children: popoverHeading }) }),
|
|
115
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { className: "media-upload-modal__upload-status__list", children: uploadingFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
116
|
+
"li",
|
|
117
|
+
{
|
|
118
|
+
className: "media-upload-modal__upload-status__item",
|
|
119
|
+
children: [
|
|
120
|
+
file.status === "uploading" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Spinner, {}),
|
|
121
|
+
file.status === "uploaded" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Icon, { icon: import_icons.check, size: 16 }),
|
|
122
|
+
(file.status === "uploading" || file.status === "uploaded") && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
123
|
+
"span",
|
|
124
|
+
{
|
|
125
|
+
className: "media-upload-modal__upload-status__filename",
|
|
126
|
+
title: file.name,
|
|
127
|
+
children: file.name
|
|
128
|
+
}
|
|
129
|
+
),
|
|
130
|
+
file.status === "error" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
131
|
+
import_components.Notice,
|
|
132
|
+
{
|
|
133
|
+
status: "error",
|
|
134
|
+
isDismissible: !!onDismissError,
|
|
135
|
+
onRemove: () => onDismissError?.(file.id),
|
|
136
|
+
children: [
|
|
137
|
+
file.name,
|
|
138
|
+
": ",
|
|
139
|
+
file.error
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
)
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
file.id
|
|
146
|
+
)) })
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
] });
|
|
151
|
+
}
|
|
152
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
153
|
+
0 && (module.exports = {
|
|
154
|
+
UploadStatusPopover
|
|
155
|
+
});
|
|
156
|
+
//# sourceMappingURL=upload-status-popover.cjs.map
|