@wordpress/block-library
Version:
Block library for the WordPress editor.
8 lines (7 loc) • 14.8 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../src/gallery/use-dynamic-gallery.js"],
"sourcesContent": ["import { useMemo } from '@wordpress/element';\nimport { useDispatch, useSelect, useRegistry } from '@wordpress/data';\nimport { store as blockEditorStore } from '@wordpress/block-editor';\nimport { store as coreStore } from '@wordpress/core-data';\nimport { createBlock } from '@wordpress/blocks';\nimport { pickRelevantMediaFiles } from './shared';\nimport { getHrefAndDestination } from './utils';\nimport { getUpdatedLinkTargetSettings } from '../image/utils';\nimport {\n\tgetSourceQuery,\n\tgetDynamicSource,\n\tATTACHED_MEDIA,\n\tDEFAULT_ORDERBY,\n\tDEFAULT_ORDER,\n\tMAX_IMAGES,\n} from './dynamic-source';\n\nconst EMPTY_ARRAY = [];\n\n/**\n * Builds the attributes for a `core/image` block from a media (attachment)\n * record, applying the gallery-wide settings that affect how the image renders.\n *\n * Used to construct the (non-persisted) image blocks previewed in dynamic mode,\n * and the real image blocks created when a dynamic gallery is converted\n * (\"pinned\") back to individual images. The frontend equivalent is\n * `block_core_gallery_render_dynamic_image()` in `index.php`.\n *\n * @param {Object} media A media object as returned by the REST API.\n * @param {Object} galleryAttributes The gallery block's attributes.\n * @return {Object} Attributes to pass to `createBlock( 'core/image', ... )`.\n */\nfunction buildImageBlockAttributes( media, galleryAttributes ) {\n\tconst { sizeSlug, linkTo, linkTarget, aspectRatio } = galleryAttributes;\n\tconst hasAspectRatio = !! aspectRatio && aspectRatio !== 'auto';\n\n\treturn {\n\t\tid: media.id,\n\t\t...pickRelevantMediaFiles( media, sizeSlug ),\n\t\t...getHrefAndDestination( media, linkTo ),\n\t\t...getUpdatedLinkTargetSettings( linkTarget, galleryAttributes ),\n\t\tsizeSlug,\n\t\t// Raw caption, mirroring the frontend (`index.php`). Gap: the REST API\n\t\t// exposes no caption run through `wp_get_attachment_caption`, so neither\n\t\t// side applies that filter.\n\t\tcaption: media.caption?.raw || '',\n\t\talt: media.alt_text || '',\n\t\taspectRatio: hasAspectRatio ? aspectRatio : undefined,\n\t\t// Pair `scale` with `aspectRatio` so the image crops rather than stretches,\n\t\t// matching the image block's UI and the frontend (`index.php`).\n\t\tscale: hasAspectRatio ? 'cover' : undefined,\n\t};\n}\n\n/**\n * Builds a set of `core/image` blocks from the resolved media, applying the\n * gallery-wide settings. Each call mints fresh client IDs, so it can produce\n * both the editor preview and the materialized inner blocks on convert.\n *\n * @param {Object[]} media Media records from the REST API.\n * @param {Object} galleryAttributes The image-relevant gallery attributes.\n * @return {Object[]} New `core/image` block instances.\n */\nfunction buildImageBlocks( media, galleryAttributes ) {\n\treturn media.map( ( mediaItem ) =>\n\t\tcreateBlock(\n\t\t\t'core/image',\n\t\t\tbuildImageBlockAttributes( mediaItem, galleryAttributes )\n\t\t)\n\t);\n}\n\n/**\n * Bundles the Gallery block's \"dynamic mode\" source resolution and actions.\n *\n * Dynamic mode resolves the gallery's images from a configured source\n * (`attributes.dynamicContent`) instead of from manually-added inner image\n * blocks. This hook centralizes the shared, single-instance pieces — the source\n * resolution (one `getEntityRecords`), the editor-preview blocks, and the\n * mode/ordering actions — out of the block's `edit` component. Transient UI\n * concerns (e.g. the convert-to-dynamic confirmation) live in the components\n * that own them.\n *\n * @param {Object} options\n * @param {Object} options.attributes The gallery block attributes.\n * @param {Function} options.setAttributes The block's `setAttributes`.\n * @param {string} options.clientId The block client ID.\n * @param {?number} options.postId The current post ID (from block context).\n * @param {?string} options.postType The current post type (from block context).\n * @return {Object} Dynamic-mode source data and actions.\n */\nexport default function useDynamicGallery( {\n\tattributes,\n\tsetAttributes,\n\tclientId,\n\tpostId,\n\tpostType,\n} ) {\n\tconst { dynamicContent } = attributes;\n\n\t// Whether dynamic mode makes sense in the current editing context. A\n\t// `postType` means the block will resolve against some post at render time —\n\t// either a concrete post (post/page editor, Query Loop item) or a post-bound\n\t// template (`single`, `page`) whose post is filled in by `get_the_ID()` on the\n\t// frontend (see `index.php`). Without it (template part, pattern, generic\n\t// template) there's no post to attach to, so the source can never resolve.\n\tconst canUseDynamicSource = !! postType;\n\n\t// The descriptor for the configured source (its `title`/`description`/\n\t// `emptyMessage`), resolved once here so consumers read the copy without\n\t// re-deriving it from `dynamicContent`. `undefined` for an unknown source.\n\tconst sourceDescriptor = getDynamicSource( dynamicContent?.source );\n\n\t// Current source ordering, falling back to the shared defaults when unset.\n\tconst sourceOrderby = dynamicContent?.args?.orderBy ?? DEFAULT_ORDERBY;\n\tconst sourceOrder = dynamicContent?.args?.order ?? DEFAULT_ORDER;\n\n\tconst registry = useRegistry();\n\tconst { replaceInnerBlocks, __unstableMarkNextChangeAsNotPersistent } =\n\t\tuseDispatch( blockEditorStore );\n\n\t// Resolve the configured source to a media query. `null` (static mode, or an\n\t// unresolvable source) short-circuits the select below so no request fires.\n\tconst query = useMemo(\n\t\t() =>\n\t\t\tdynamicContent\n\t\t\t\t? getSourceQuery( dynamicContent, { postId } )\n\t\t\t\t: null,\n\t\t[ dynamicContent, postId ]\n\t);\n\n\tconst { dynamicMedia, dynamicMediaTotal, isResolvingDynamic } = useSelect(\n\t\t( select ) => {\n\t\t\tif ( ! query ) {\n\t\t\t\treturn {\n\t\t\t\t\tdynamicMedia: EMPTY_ARRAY,\n\t\t\t\t\tdynamicMediaTotal: 0,\n\t\t\t\t\tisResolvingDynamic: false,\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst selectorArgs = [ 'postType', 'attachment', query ];\n\t\t\treturn {\n\t\t\t\tdynamicMedia:\n\t\t\t\t\tselect( coreStore ).getEntityRecords( ...selectorArgs ) ??\n\t\t\t\t\tEMPTY_ARRAY,\n\t\t\t\t// Total matching attachments (the `X-WP-Total` header), which the\n\t\t\t\t// query's `per_page` cap doesn't bound — so it reveals when the\n\t\t\t\t// post has more attached images than are shown.\n\t\t\t\tdynamicMediaTotal:\n\t\t\t\t\tselect( coreStore ).getEntityRecordsTotalItems(\n\t\t\t\t\t\t...selectorArgs\n\t\t\t\t\t) ?? 0,\n\t\t\t\tisResolvingDynamic: ! select( coreStore ).hasFinishedResolution(\n\t\t\t\t\t'getEntityRecords',\n\t\t\t\t\tselectorArgs\n\t\t\t\t),\n\t\t\t};\n\t\t},\n\t\t[ query ]\n\t);\n\n\t// The source caps results at `MAX_IMAGES` (matching the frontend), so flag\n\t// when the post has more attached images than the gallery can show.\n\tconst hasMoreImagesThanCap = dynamicMediaTotal > MAX_IMAGES;\n\n\t// The only gallery settings that affect how an image renders, and so the\n\t// only ones `buildImageBlockAttributes` reads. Depending on this narrowed\n\t// set (rather than the whole `attributes` object) keeps the preview from\n\t// rebuilding on unrelated edits, e.g. typing in the gallery caption.\n\tconst { sizeSlug, linkTo, linkTarget, aspectRatio } = attributes;\n\tconst imageAttributes = useMemo(\n\t\t() => ( { sizeSlug, linkTo, linkTarget, aspectRatio } ),\n\t\t[ sizeSlug, linkTo, linkTarget, aspectRatio ]\n\t);\n\n\t// The (non-persisted) `core/image` blocks used for the editor preview.\n\t// Rebuilt when the resolved media or an image-relevant setting changes.\n\tconst dynamicImageBlocks = useMemo(\n\t\t() => buildImageBlocks( dynamicMedia, imageAttributes ),\n\t\t[ dynamicMedia, imageAttributes ]\n\t);\n\n\t// Context the gallery provides to its (previewed) image blocks.\n\tconst galleryContext = useMemo(\n\t\t() => ( {\n\t\t\tallowResize: attributes.allowResize ?? false,\n\t\t\timageCrop: attributes.imageCrop,\n\t\t\tfixedHeight: attributes.fixedHeight,\n\t\t\tnavigationButtonType: attributes.navigationButtonType,\n\t\t} ),\n\t\t[\n\t\t\tattributes.allowResize,\n\t\t\tattributes.imageCrop,\n\t\t\tattributes.fixedHeight,\n\t\t\tattributes.navigationButtonType,\n\t\t]\n\t);\n\n\t// Switches the gallery into dynamic mode, displaying images attached to the\n\t// current post. Clearing the inner blocks removes the manually-added images:\n\t// they're the gallery's image data, so there's nothing else to reset. The\n\t// legacy `images`/`ids` attributes aren't touched — they're back-compat shims\n\t// for the pre-innerBlocks format (see `deprecated.js`/`transforms.js`), empty\n\t// on any gallery reachable here.\n\tfunction enableDynamicMode() {\n\t\t// Batch the attribute change and the inner-block reset into a single\n\t\t// undo level: they're two halves of one mode switch, so one undo should\n\t\t// revert both. Marking the second dispatch non-persistent stops it from\n\t\t// opening a second undo level, which would otherwise leave the gallery\n\t\t// in a half-switched state (dynamic source set, images still present).\n\t\tregistry.batch( () => {\n\t\t\tsetAttributes( { dynamicContent: { source: ATTACHED_MEDIA } } );\n\t\t\t__unstableMarkNextChangeAsNotPersistent();\n\t\t\treplaceInnerBlocks( clientId, [] );\n\t\t} );\n\t}\n\n\t// \"Pins\" a dynamic gallery: materializes the currently-resolved media as\n\t// real, editable image blocks and leaves dynamic mode.\n\tfunction convertToStatic() {\n\t\t// Batch the inner-block materialization and the attribute change into a\n\t\t// single undo level so one undo reverts the whole conversion (see\n\t\t// `enableDynamicMode`). Build fresh blocks rather than reusing the\n\t\t// preview's `dynamicImageBlocks` so the materialized inner blocks get\n\t\t// their own client IDs, distinct from the (disabled) preview instances.\n\t\tregistry.batch( () => {\n\t\t\treplaceInnerBlocks(\n\t\t\t\tclientId,\n\t\t\t\tbuildImageBlocks( dynamicMedia, imageAttributes )\n\t\t\t);\n\t\t\t__unstableMarkNextChangeAsNotPersistent();\n\t\t\tsetAttributes( { dynamicContent: undefined } );\n\t\t} );\n\t}\n\n\t// Updates the source ordering within `dynamicContent.args`. Passing\n\t// `undefined` (or the default order) strips the keys so they aren't\n\t// persisted redundantly and the ToolsPanel item reads as unset.\n\tfunction setSourceOrder( nextOrderby, nextOrder ) {\n\t\tconst nextArgs = { ...dynamicContent?.args };\n\t\tdelete nextArgs.orderBy;\n\t\tdelete nextArgs.order;\n\t\tif (\n\t\t\tnextOrderby !== undefined &&\n\t\t\t( nextOrderby !== DEFAULT_ORDERBY || nextOrder !== DEFAULT_ORDER )\n\t\t) {\n\t\t\tnextArgs.orderBy = nextOrderby;\n\t\t\tnextArgs.order = nextOrder;\n\t\t}\n\t\tconst nextSource = { ...dynamicContent };\n\t\tif ( Object.keys( nextArgs ).length ) {\n\t\t\tnextSource.args = nextArgs;\n\t\t} else {\n\t\t\tdelete nextSource.args;\n\t\t}\n\t\tsetAttributes( { dynamicContent: nextSource } );\n\t}\n\n\t// Resets the source to its bare form: keeps the source kind, drops its args.\n\tfunction resetSource() {\n\t\tsetAttributes( {\n\t\t\tdynamicContent: { source: dynamicContent.source },\n\t\t} );\n\t}\n\n\treturn {\n\t\tdynamicContent,\n\t\tcanUseDynamicSource,\n\t\tsourceDescriptor,\n\t\thasMoreImagesThanCap,\n\t\tdynamicMediaTotal,\n\t\tsourceOrderby,\n\t\tsourceOrder,\n\t\tdynamicMedia,\n\t\tdynamicImageBlocks,\n\t\tisResolvingDynamic,\n\t\tgalleryContext,\n\t\tenableDynamicMode,\n\t\tconvertToStatic,\n\t\tsetSourceOrder,\n\t\tresetSource,\n\t};\n}\n"],
"mappings": ";AAAA,SAAS,eAAe;AACxB,SAAS,aAAa,WAAW,mBAAmB;AACpD,SAAS,SAAS,wBAAwB;AAC1C,SAAS,SAAS,iBAAiB;AACnC,SAAS,mBAAmB;AAC5B,SAAS,8BAA8B;AACvC,SAAS,6BAA6B;AACtC,SAAS,oCAAoC;AAC7C;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAEP,IAAM,cAAc,CAAC;AAerB,SAAS,0BAA2B,OAAO,mBAAoB;AAC9D,QAAM,EAAE,UAAU,QAAQ,YAAY,YAAY,IAAI;AACtD,QAAM,iBAAiB,CAAC,CAAE,eAAe,gBAAgB;AAEzD,SAAO;AAAA,IACN,IAAI,MAAM;AAAA,IACV,GAAG,uBAAwB,OAAO,QAAS;AAAA,IAC3C,GAAG,sBAAuB,OAAO,MAAO;AAAA,IACxC,GAAG,6BAA8B,YAAY,iBAAkB;AAAA,IAC/D;AAAA;AAAA;AAAA;AAAA,IAIA,SAAS,MAAM,SAAS,OAAO;AAAA,IAC/B,KAAK,MAAM,YAAY;AAAA,IACvB,aAAa,iBAAiB,cAAc;AAAA;AAAA;AAAA,IAG5C,OAAO,iBAAiB,UAAU;AAAA,EACnC;AACD;AAWA,SAAS,iBAAkB,OAAO,mBAAoB;AACrD,SAAO,MAAM;AAAA,IAAK,CAAE,cACnB;AAAA,MACC;AAAA,MACA,0BAA2B,WAAW,iBAAkB;AAAA,IACzD;AAAA,EACD;AACD;AAqBe,SAAR,kBAAoC;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,GAAI;AACH,QAAM,EAAE,eAAe,IAAI;AAQ3B,QAAM,sBAAsB,CAAC,CAAE;AAK/B,QAAM,mBAAmB,iBAAkB,gBAAgB,MAAO;AAGlE,QAAM,gBAAgB,gBAAgB,MAAM,WAAW;AACvD,QAAM,cAAc,gBAAgB,MAAM,SAAS;AAEnD,QAAM,WAAW,YAAY;AAC7B,QAAM,EAAE,oBAAoB,wCAAwC,IACnE,YAAa,gBAAiB;AAI/B,QAAM,QAAQ;AAAA,IACb,MACC,iBACG,eAAgB,gBAAgB,EAAE,OAAO,CAAE,IAC3C;AAAA,IACJ,CAAE,gBAAgB,MAAO;AAAA,EAC1B;AAEA,QAAM,EAAE,cAAc,mBAAmB,mBAAmB,IAAI;AAAA,IAC/D,CAAE,WAAY;AACb,UAAK,CAAE,OAAQ;AACd,eAAO;AAAA,UACN,cAAc;AAAA,UACd,mBAAmB;AAAA,UACnB,oBAAoB;AAAA,QACrB;AAAA,MACD;AACA,YAAM,eAAe,CAAE,YAAY,cAAc,KAAM;AACvD,aAAO;AAAA,QACN,cACC,OAAQ,SAAU,EAAE,iBAAkB,GAAG,YAAa,KACtD;AAAA;AAAA;AAAA;AAAA,QAID,mBACC,OAAQ,SAAU,EAAE;AAAA,UACnB,GAAG;AAAA,QACJ,KAAK;AAAA,QACN,oBAAoB,CAAE,OAAQ,SAAU,EAAE;AAAA,UACzC;AAAA,UACA;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,IACA,CAAE,KAAM;AAAA,EACT;AAIA,QAAM,uBAAuB,oBAAoB;AAMjD,QAAM,EAAE,UAAU,QAAQ,YAAY,YAAY,IAAI;AACtD,QAAM,kBAAkB;AAAA,IACvB,OAAQ,EAAE,UAAU,QAAQ,YAAY,YAAY;AAAA,IACpD,CAAE,UAAU,QAAQ,YAAY,WAAY;AAAA,EAC7C;AAIA,QAAM,qBAAqB;AAAA,IAC1B,MAAM,iBAAkB,cAAc,eAAgB;AAAA,IACtD,CAAE,cAAc,eAAgB;AAAA,EACjC;AAGA,QAAM,iBAAiB;AAAA,IACtB,OAAQ;AAAA,MACP,aAAa,WAAW,eAAe;AAAA,MACvC,WAAW,WAAW;AAAA,MACtB,aAAa,WAAW;AAAA,MACxB,sBAAsB,WAAW;AAAA,IAClC;AAAA,IACA;AAAA,MACC,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,MACX,WAAW;AAAA,IACZ;AAAA,EACD;AAQA,WAAS,oBAAoB;AAM5B,aAAS,MAAO,MAAM;AACrB,oBAAe,EAAE,gBAAgB,EAAE,QAAQ,eAAe,EAAE,CAAE;AAC9D,8CAAwC;AACxC,yBAAoB,UAAU,CAAC,CAAE;AAAA,IAClC,CAAE;AAAA,EACH;AAIA,WAAS,kBAAkB;AAM1B,aAAS,MAAO,MAAM;AACrB;AAAA,QACC;AAAA,QACA,iBAAkB,cAAc,eAAgB;AAAA,MACjD;AACA,8CAAwC;AACxC,oBAAe,EAAE,gBAAgB,OAAU,CAAE;AAAA,IAC9C,CAAE;AAAA,EACH;AAKA,WAAS,eAAgB,aAAa,WAAY;AACjD,UAAM,WAAW,EAAE,GAAG,gBAAgB,KAAK;AAC3C,WAAO,SAAS;AAChB,WAAO,SAAS;AAChB,QACC,gBAAgB,WACd,gBAAgB,mBAAmB,cAAc,gBAClD;AACD,eAAS,UAAU;AACnB,eAAS,QAAQ;AAAA,IAClB;AACA,UAAM,aAAa,EAAE,GAAG,eAAe;AACvC,QAAK,OAAO,KAAM,QAAS,EAAE,QAAS;AACrC,iBAAW,OAAO;AAAA,IACnB,OAAO;AACN,aAAO,WAAW;AAAA,IACnB;AACA,kBAAe,EAAE,gBAAgB,WAAW,CAAE;AAAA,EAC/C;AAGA,WAAS,cAAc;AACtB,kBAAe;AAAA,MACd,gBAAgB,EAAE,QAAQ,eAAe,OAAO;AAAA,IACjD,CAAE;AAAA,EACH;AAEA,SAAO;AAAA,IACN;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;",
"names": []
}