@wordpress/media-utils
Version:
WordPress Media Upload Utils.
469 lines (467 loc) • 16.7 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// packages/media-utils/src/components/media-upload-modal/index.tsx
var media_upload_modal_exports = {};
__export(media_upload_modal_exports, {
MediaUploadModal: () => MediaUploadModal,
default: () => media_upload_modal_default
});
module.exports = __toCommonJS(media_upload_modal_exports);
var import_clsx = __toESM(require("clsx"));
var import_element = require("@wordpress/element");
var import_i18n = require("@wordpress/i18n");
var import_core_data = require("@wordpress/core-data");
var import_data = require("@wordpress/data");
var import_components = require("@wordpress/components");
var import_icons = require("@wordpress/icons");
var import_dataviews = require("@wordpress/dataviews");
var import_ui = require("@wordpress/ui");
var import_media_fields = require("@wordpress/media-fields");
var import_notices = require("@wordpress/notices");
var import_transform_attachment = require("../../utils/transform-attachment.cjs");
var import_upload_media = require("../../utils/upload-media.cjs");
var import_lock_unlock = require("../../lock-unlock.cjs");
var import_upload_status_popover = require("./upload-status-popover.cjs");
var import_use_invalidate_attachment_resolutions = require("./use-invalidate-attachment-resolutions.cjs");
var import_use_upload_status = require("./use-upload-status.cjs");
var import_jsx_runtime = require("react/jsx-runtime");
var { useEntityRecordsWithPermissions } = (0, import_lock_unlock.unlock)(import_core_data.privateApis);
var LAYOUT_PICKER_GRID = "pickerGrid";
var LAYOUT_PICKER_TABLE = "pickerTable";
var NOTICES_CONTEXT = "media-modal";
var NOTICE_ID_UPLOAD_PROGRESS = "media-modal-upload-progress";
function MediaUploadModal({
allowedTypes,
multiple = false,
value,
onSelect,
onClose,
onUpload,
title = (0, import_i18n.__)("Select Media"),
isOpen,
isDismissible = true,
modalClass,
search = true,
searchLabel = (0, import_i18n.__)("Search media")
}) {
const [selection, setSelection] = (0, import_element.useState)(() => {
if (!value) {
return [];
}
return Array.isArray(value) ? value.map(String) : [String(value)];
});
const { createSuccessNotice, removeAllNotices } = (0, import_data.useDispatch)(import_notices.store);
const invalidateAttachmentResolutions = (0, import_use_invalidate_attachment_resolutions.useInvalidateAttachmentResolutions)();
const [view, setView] = (0, import_element.useState)(() => ({
type: LAYOUT_PICKER_GRID,
fields: [],
showTitle: false,
titleField: "title",
mediaField: "media_thumbnail",
search: "",
page: 1,
perPage: 50,
filters: [],
layout: {
previewSize: 170,
density: "compact"
}
}));
const queryArgs = (0, import_element.useMemo)(() => {
const filters = {};
view.filters?.forEach((filter) => {
if (filter.field === "media_type") {
filters.media_type = filter.value;
}
if (filter.field === "author") {
if (filter.operator === "isAny") {
filters.author = filter.value;
} else if (filter.operator === "isNone") {
filters.author_exclude = filter.value;
}
}
if (filter.field === "date" || filter.field === "modified") {
if (filter.operator === "before") {
filters.before = filter.value;
} else if (filter.operator === "after") {
filters.after = filter.value;
}
}
if (filter.field === "mime_type") {
filters.mime_type = filter.value;
}
});
if (!filters.media_type) {
filters.media_type = allowedTypes?.includes("*") ? void 0 : allowedTypes;
}
return {
per_page: view.perPage || 20,
page: view.page || 1,
status: "inherit",
order: view.sort?.direction,
orderby: view.sort?.field,
search: view.search,
_embed: "author,wp:attached-to",
...filters
};
}, [view, allowedTypes]);
const handleBatchComplete = (0, import_element.useCallback)(
(attachments) => {
const uploadedIds = attachments.map((attachment) => String(attachment.id)).filter(Boolean);
if (multiple) {
setSelection((prev) => {
const existing = new Set(prev);
const newIds = uploadedIds.filter(
(id) => !existing.has(id)
);
return [...prev, ...newIds];
});
} else {
setSelection(uploadedIds.slice(0, 1));
}
invalidateAttachmentResolutions();
},
[multiple, invalidateAttachmentResolutions]
);
const {
uploadingFiles,
registerBatch,
dismissError,
clearCompleted,
allComplete
} = (0, import_use_upload_status.useUploadStatus)({ onBatchComplete: handleBatchComplete });
const isPopoverOpenRef = (0, import_element.useRef)(false);
const handlePopoverOpenChange = (0, import_element.useCallback)(
(open) => {
isPopoverOpenRef.current = open;
if (!open) {
clearCompleted();
}
},
[clearCompleted]
);
const {
records: mediaRecords,
isResolving: isLoading,
totalItems,
totalPages
} = useEntityRecordsWithPermissions("postType", "attachment", queryArgs);
const fields = (0, import_element.useMemo)(
() => [
// Media field definitions from @wordpress/media-fields
// Cast is safe because RestAttachment has the same properties as Attachment
{
...import_media_fields.mediaThumbnailField,
enableHiding: false
// Within the modal, the thumbnail should always be shown.
},
{
id: "title",
type: "text",
label: (0, import_i18n.__)("Title"),
getValue: ({ item }) => {
const titleValue = item.title.raw || item.title.rendered;
return titleValue || (0, import_i18n.__)("(no title)");
}
},
import_media_fields.altTextField,
import_media_fields.captionField,
import_media_fields.descriptionField,
import_media_fields.dateAddedField,
import_media_fields.dateModifiedField,
import_media_fields.authorField,
import_media_fields.filenameField,
import_media_fields.filesizeField,
import_media_fields.mediaDimensionsField,
import_media_fields.mimeTypeField,
import_media_fields.attachedToField
],
[]
);
const actions = (0, import_element.useMemo)(
() => [
{
id: "select",
label: (0, import_i18n.__)("Select"),
isPrimary: true,
supportsBulk: multiple,
async callback() {
if (selection.length === 0) {
return;
}
const selectedPostsQuery = {
include: selection,
per_page: -1
};
const selectedPosts = await (0, import_data.resolveSelect)(
import_core_data.store
).getEntityRecords(
"postType",
"attachment",
selectedPostsQuery
);
const transformedPosts = (selectedPosts ?? []).map(import_transform_attachment.transformAttachment).filter(Boolean);
const selectedItems = multiple ? transformedPosts : transformedPosts?.[0];
removeAllNotices("snackbar", NOTICES_CONTEXT);
onSelect(selectedItems);
}
}
],
[multiple, onSelect, selection, removeAllNotices]
);
const handleModalClose = (0, import_element.useCallback)(() => {
removeAllNotices("snackbar", NOTICES_CONTEXT);
onClose?.();
}, [removeAllNotices, onClose]);
const handleUpload = onUpload || import_upload_media.uploadMedia;
const prevAllCompleteRef = (0, import_element.useRef)(false);
(0, import_element.useEffect)(() => {
if (allComplete && !prevAllCompleteRef.current) {
const completeCount = uploadingFiles.filter(
(file) => file.status === "uploaded"
).length;
if (completeCount > 0) {
createSuccessNotice(
(0, import_i18n.sprintf)(
// translators: %s: number of files
(0, import_i18n._n)(
"Uploaded %s file",
"Uploaded %s files",
completeCount
),
completeCount.toLocaleString()
),
{
type: "snackbar",
context: NOTICES_CONTEXT,
id: NOTICE_ID_UPLOAD_PROGRESS
}
);
}
if (!isPopoverOpenRef.current) {
clearCompleted();
}
}
prevAllCompleteRef.current = allComplete;
}, [allComplete, uploadingFiles, createSuccessNotice, clearCompleted]);
const handleFileSelect = (0, import_element.useCallback)(
(event) => {
const files = event.target.files;
if (files && files.length > 0) {
const filesArray = Array.from(files);
const { onFileChange, onError } = registerBatch(filesArray);
handleUpload({
allowedTypes,
filesList: filesArray,
onFileChange,
onError
});
}
},
[allowedTypes, handleUpload, registerBatch]
);
const paginationInfo = (0, import_element.useMemo)(
() => ({
totalItems,
totalPages
}),
[totalItems, totalPages]
);
const defaultLayouts = (0, import_element.useMemo)(
() => ({
[LAYOUT_PICKER_GRID]: {
fields: [],
showTitle: false
},
[LAYOUT_PICKER_TABLE]: {
fields: [
"filename",
"filesize",
"media_dimensions",
"author",
"date"
],
showTitle: true
}
}),
[]
);
const acceptTypes = (0, import_element.useMemo)(() => {
if (allowedTypes?.includes("*")) {
return void 0;
}
return allowedTypes?.join(",");
}, [allowedTypes]);
if (!isOpen) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
import_components.Modal,
{
title,
onRequestClose: handleModalClose,
isDismissible,
className: modalClass,
overlayClassName: "media-upload-modal",
size: "fill",
headerActions: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.FormFileUpload,
{
accept: acceptTypes,
multiple: true,
onChange: handleFileSelect,
__next40pxDefaultSize: true,
render: ({ openFileDialog }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.Button,
{
onClick: openFileDialog,
icon: import_icons.upload,
__next40pxDefaultSize: true,
children: (0, import_i18n.__)("Upload media")
}
)
}
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.DropZone,
{
onFilesDrop: (files) => {
let filteredFiles = files;
if (allowedTypes && !allowedTypes.includes("*")) {
filteredFiles = files.filter(
(file) => allowedTypes.some((allowedType) => {
return file.type === allowedType || file.type.startsWith(
allowedType.replace("*", "")
);
})
);
}
if (filteredFiles.length > 0) {
const { onFileChange, onError } = registerBatch(filteredFiles);
handleUpload({
allowedTypes,
filesList: filteredFiles,
onFileChange,
onError
});
}
},
label: (0, import_i18n.__)("Drop files to upload")
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
import_dataviews.DataViewsPicker,
{
data: mediaRecords || [],
fields,
view,
onChangeView: setView,
actions,
selection,
onChangeSelection: setSelection,
isLoading,
paginationInfo,
defaultLayouts,
getItemId: (item) => String(item.id),
itemListLabel: (0, import_i18n.__)("Media items"),
children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
import_ui.Stack,
{
direction: "row",
align: "top",
justify: "space-between",
className: "dataviews__view-actions",
gap: "xs",
children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
import_ui.Stack,
{
direction: "row",
gap: "sm",
justify: "start",
className: "dataviews__search",
children: [
search && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.Search, { label: searchLabel }),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.FiltersToggle, {})
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_ui.Stack, { direction: "row", gap: "xs", style: { flexShrink: 0 }, children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.LayoutSwitcher, {}),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.ViewConfig, {})
] })
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.FiltersToggled, { className: "dataviews-filters__container" }),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.Layout, {}),
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
"div",
{
className: (0, import_clsx.default)("media-upload-modal__footer", {
"is-uploading": uploadingFiles.length > 0
}),
children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_upload_status_popover.UploadStatusPopover,
{
uploadingFiles,
onDismissError: dismissError,
onOpenChange: handlePopoverOpenChange
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dataviews.DataViewsPicker.BulkActionToolbar, {})
]
}
)
]
}
),
(0, import_element.createPortal)(
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_notices.SnackbarNotices,
{
className: "media-upload-modal__snackbar",
context: NOTICES_CONTEXT
}
),
document.body
)
]
}
);
}
var media_upload_modal_default = MediaUploadModal;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
MediaUploadModal
});
//# sourceMappingURL=index.cjs.map