UNPKG

@wordpress/media-utils

Version:
469 lines (467 loc) 16.7 kB
"use strict"; 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