Plugin Directory

source: gutenberg/trunk/build/scripts/patterns/index.js

Last change on this file was 3399036, checked in by gutenbergplugin, 5 days ago

Releasing version 22.1.0

File size: 57.5 KB
Line 
1var wp;
2(wp ||= {}).patterns = (() => {
3  var __create = Object.create;
4  var __defProp = Object.defineProperty;
5  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6  var __getOwnPropNames = Object.getOwnPropertyNames;
7  var __getProtoOf = Object.getPrototypeOf;
8  var __hasOwnProp = Object.prototype.hasOwnProperty;
9  var __commonJS = (cb, mod) => function __require() {
10    return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11  };
12  var __export = (target, all) => {
13    for (var name in all)
14      __defProp(target, name, { get: all[name], enumerable: true });
15  };
16  var __copyProps = (to, from, except, desc) => {
17    if (from && typeof from === "object" || typeof from === "function") {
18      for (let key of __getOwnPropNames(from))
19        if (!__hasOwnProp.call(to, key) && key !== except)
20          __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21    }
22    return to;
23  };
24  var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25    // If the importer is in node compatibility mode or this is not an ESM
26    // file that has been converted to a CommonJS file using a Babel-
27    // compatible transform (i.e. "__esModule" has not been set), then set
28    // "default" to the CommonJS "module.exports" for node compatibility.
29    isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30    mod
31  ));
32  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
33
34  // package-external:@wordpress/data
35  var require_data = __commonJS({
36    "package-external:@wordpress/data"(exports, module) {
37      module.exports = window.wp.data;
38    }
39  });
40
41  // package-external:@wordpress/blocks
42  var require_blocks = __commonJS({
43    "package-external:@wordpress/blocks"(exports, module) {
44      module.exports = window.wp.blocks;
45    }
46  });
47
48  // package-external:@wordpress/core-data
49  var require_core_data = __commonJS({
50    "package-external:@wordpress/core-data"(exports, module) {
51      module.exports = window.wp.coreData;
52    }
53  });
54
55  // package-external:@wordpress/block-editor
56  var require_block_editor = __commonJS({
57    "package-external:@wordpress/block-editor"(exports, module) {
58      module.exports = window.wp.blockEditor;
59    }
60  });
61
62  // package-external:@wordpress/private-apis
63  var require_private_apis = __commonJS({
64    "package-external:@wordpress/private-apis"(exports, module) {
65      module.exports = window.wp.privateApis;
66    }
67  });
68
69  // package-external:@wordpress/components
70  var require_components = __commonJS({
71    "package-external:@wordpress/components"(exports, module) {
72      module.exports = window.wp.components;
73    }
74  });
75
76  // package-external:@wordpress/element
77  var require_element = __commonJS({
78    "package-external:@wordpress/element"(exports, module) {
79      module.exports = window.wp.element;
80    }
81  });
82
83  // package-external:@wordpress/i18n
84  var require_i18n = __commonJS({
85    "package-external:@wordpress/i18n"(exports, module) {
86      module.exports = window.wp.i18n;
87    }
88  });
89
90  // vendor-external:react/jsx-runtime
91  var require_jsx_runtime = __commonJS({
92    "vendor-external:react/jsx-runtime"(exports, module) {
93      module.exports = window.ReactJSXRuntime;
94    }
95  });
96
97  // package-external:@wordpress/notices
98  var require_notices = __commonJS({
99    "package-external:@wordpress/notices"(exports, module) {
100      module.exports = window.wp.notices;
101    }
102  });
103
104  // package-external:@wordpress/compose
105  var require_compose = __commonJS({
106    "package-external:@wordpress/compose"(exports, module) {
107      module.exports = window.wp.compose;
108    }
109  });
110
111  // package-external:@wordpress/html-entities
112  var require_html_entities = __commonJS({
113    "package-external:@wordpress/html-entities"(exports, module) {
114      module.exports = window.wp.htmlEntities;
115    }
116  });
117
118  // package-external:@wordpress/primitives
119  var require_primitives = __commonJS({
120    "package-external:@wordpress/primitives"(exports, module) {
121      module.exports = window.wp.primitives;
122    }
123  });
124
125  // package-external:@wordpress/url
126  var require_url = __commonJS({
127    "package-external:@wordpress/url"(exports, module) {
128      module.exports = window.wp.url;
129    }
130  });
131
132  // package-external:@wordpress/a11y
133  var require_a11y = __commonJS({
134    "package-external:@wordpress/a11y"(exports, module) {
135      module.exports = window.wp.a11y;
136    }
137  });
138
139  // packages/patterns/build-module/index.js
140  var index_exports = {};
141  __export(index_exports, {
142    privateApis: () => privateApis,
143    store: () => store
144  });
145
146  // packages/patterns/build-module/store/index.js
147  var import_data2 = __toESM(require_data());
148
149  // packages/patterns/build-module/store/reducer.js
150  var import_data = __toESM(require_data());
151  function isEditingPattern(state = {}, action) {
152    if (action?.type === "SET_EDITING_PATTERN") {
153      return {
154        ...state,
155        [action.clientId]: action.isEditing
156      };
157    }
158    return state;
159  }
160  var reducer_default = (0, import_data.combineReducers)({
161    isEditingPattern
162  });
163
164  // packages/patterns/build-module/store/actions.js
165  var actions_exports = {};
166  __export(actions_exports, {
167    convertSyncedPatternToStatic: () => convertSyncedPatternToStatic,
168    createPattern: () => createPattern,
169    createPatternFromFile: () => createPatternFromFile,
170    setEditingPattern: () => setEditingPattern
171  });
172  var import_blocks = __toESM(require_blocks());
173  var import_core_data = __toESM(require_core_data());
174  var import_block_editor = __toESM(require_block_editor());
175
176  // packages/patterns/build-module/constants.js
177  var PATTERN_TYPES = {
178    theme: "pattern",
179    user: "wp_block"
180  };
181  var PATTERN_DEFAULT_CATEGORY = "all-patterns";
182  var PATTERN_USER_CATEGORY = "my-patterns";
183  var EXCLUDED_PATTERN_SOURCES = [
184    "core",
185    "pattern-directory/core",
186    "pattern-directory/featured"
187  ];
188  var PATTERN_SYNC_TYPES = {
189    full: "fully",
190    unsynced: "unsynced"
191  };
192  var PARTIAL_SYNCING_SUPPORTED_BLOCKS = {
193    "core/paragraph": ["content"],
194    "core/heading": ["content"],
195    "core/button": ["text", "url", "linkTarget", "rel"],
196    "core/image": ["id", "url", "title", "alt", "caption"]
197  };
198  var PATTERN_OVERRIDES_BINDING_SOURCE = "core/pattern-overrides";
199
200  // packages/patterns/build-module/store/actions.js
201  var createPattern = (title, syncType, content, categories) => async ({ registry }) => {
202    const meta = syncType === PATTERN_SYNC_TYPES.unsynced ? {
203      wp_pattern_sync_status: syncType
204    } : void 0;
205    const reusableBlock = {
206      title,
207      content,
208      status: "publish",
209      meta,
210      wp_pattern_category: categories
211    };
212    const updatedRecord = await registry.dispatch(import_core_data.store).saveEntityRecord("postType", "wp_block", reusableBlock);
213    return updatedRecord;
214  };
215  var createPatternFromFile = (file, categories) => async ({ dispatch }) => {
216    const fileContent = await file.text();
217    let parsedContent;
218    try {
219      parsedContent = JSON.parse(fileContent);
220    } catch (e) {
221      throw new Error("Invalid JSON file");
222    }
223    if (parsedContent.__file !== "wp_block" || !parsedContent.title || !parsedContent.content || typeof parsedContent.title !== "string" || typeof parsedContent.content !== "string" || parsedContent.syncStatus && typeof parsedContent.syncStatus !== "string") {
224      throw new Error("Invalid pattern JSON file");
225    }
226    const pattern = await dispatch.createPattern(
227      parsedContent.title,
228      parsedContent.syncStatus,
229      parsedContent.content,
230      categories
231    );
232    return pattern;
233  };
234  var convertSyncedPatternToStatic = (clientId) => ({ registry }) => {
235    const patternBlock = registry.select(import_block_editor.store).getBlock(clientId);
236    const existingOverrides = patternBlock.attributes?.content;
237    function cloneBlocksAndRemoveBindings(blocks) {
238      return blocks.map((block) => {
239        let metadata = block.attributes.metadata;
240        if (metadata) {
241          metadata = { ...metadata };
242          delete metadata.id;
243          delete metadata.bindings;
244          if (existingOverrides?.[metadata.name]) {
245            for (const [attributeName, value] of Object.entries(
246              existingOverrides[metadata.name]
247            )) {
248              if (!(0, import_blocks.getBlockType)(block.name)?.attributes[attributeName]) {
249                continue;
250              }
251              block.attributes[attributeName] = value;
252            }
253          }
254        }
255        return (0, import_blocks.cloneBlock)(
256          block,
257          {
258            metadata: metadata && Object.keys(metadata).length > 0 ? metadata : void 0
259          },
260          cloneBlocksAndRemoveBindings(block.innerBlocks)
261        );
262      });
263    }
264    const patternInnerBlocks = registry.select(import_block_editor.store).getBlocks(patternBlock.clientId);
265    registry.dispatch(import_block_editor.store).replaceBlocks(
266      patternBlock.clientId,
267      cloneBlocksAndRemoveBindings(patternInnerBlocks)
268    );
269  };
270  function setEditingPattern(clientId, isEditing) {
271    return {
272      type: "SET_EDITING_PATTERN",
273      clientId,
274      isEditing
275    };
276  }
277
278  // packages/patterns/build-module/store/constants.js
279  var STORE_NAME = "core/patterns";
280
281  // packages/patterns/build-module/store/selectors.js
282  var selectors_exports = {};
283  __export(selectors_exports, {
284    isEditingPattern: () => isEditingPattern2
285  });
286  function isEditingPattern2(state, clientId) {
287    return state.isEditingPattern[clientId];
288  }
289
290  // packages/patterns/build-module/lock-unlock.js
291  var import_private_apis = __toESM(require_private_apis());
292  var { lock, unlock } = (0, import_private_apis.__dangerousOptInToUnstableAPIsOnlyForCoreModules)(
293    "I acknowledge private features are not for use in themes or plugins and doing so will break in the next version of WordPress.",
294    "@wordpress/patterns"
295  );
296
297  // packages/patterns/build-module/store/index.js
298  var storeConfig = {
299    reducer: reducer_default
300  };
301  var store = (0, import_data2.createReduxStore)(STORE_NAME, {
302    ...storeConfig
303  });
304  (0, import_data2.register)(store);
305  unlock(store).registerPrivateActions(actions_exports);
306  unlock(store).registerPrivateSelectors(selectors_exports);
307
308  // packages/patterns/build-module/components/overrides-panel.js
309  var import_block_editor2 = __toESM(require_block_editor());
310  var import_components = __toESM(require_components());
311  var import_data3 = __toESM(require_data());
312  var import_element = __toESM(require_element());
313  var import_i18n = __toESM(require_i18n());
314
315  // packages/patterns/build-module/api/index.js
316  function isOverridableBlock(block) {
317    return Object.keys(PARTIAL_SYNCING_SUPPORTED_BLOCKS).includes(
318      block.name
319    ) && !!block.attributes.metadata?.name && !!block.attributes.metadata?.bindings && Object.values(block.attributes.metadata.bindings).some(
320      (binding) => binding.source === "core/pattern-overrides"
321    );
322  }
323  function hasOverridableBlocks(blocks) {
324    return blocks.some((block) => {
325      if (isOverridableBlock(block)) {
326        return true;
327      }
328      return hasOverridableBlocks(block.innerBlocks);
329    });
330  }
331
332  // packages/patterns/build-module/components/overrides-panel.js
333  var import_jsx_runtime = __toESM(require_jsx_runtime());
334  var { BlockQuickNavigation } = unlock(import_block_editor2.privateApis);
335  function OverridesPanel() {
336    const allClientIds = (0, import_data3.useSelect)(
337      (select) => select(import_block_editor2.store).getClientIdsWithDescendants(),
338      []
339    );
340    const { getBlock } = (0, import_data3.useSelect)(import_block_editor2.store);
341    const clientIdsWithOverrides = (0, import_element.useMemo)(
342      () => allClientIds.filter((clientId) => {
343        const block = getBlock(clientId);
344        return isOverridableBlock(block);
345      }),
346      [allClientIds, getBlock]
347    );
348    if (!clientIdsWithOverrides?.length) {
349      return null;
350    }
351    return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.PanelBody, { title: (0, import_i18n.__)("Overrides"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BlockQuickNavigation, { clientIds: clientIdsWithOverrides }) });
352  }
353
354  // packages/patterns/build-module/components/create-pattern-modal.js
355  var import_components3 = __toESM(require_components());
356  var import_i18n3 = __toESM(require_i18n());
357  var import_element4 = __toESM(require_element());
358  var import_data5 = __toESM(require_data());
359  var import_notices = __toESM(require_notices());
360  var import_core_data3 = __toESM(require_core_data());
361
362  // packages/patterns/build-module/components/category-selector.js
363  var import_i18n2 = __toESM(require_i18n());
364  var import_element2 = __toESM(require_element());
365  var import_components2 = __toESM(require_components());
366  var import_compose = __toESM(require_compose());
367  var import_html_entities = __toESM(require_html_entities());
368  var import_jsx_runtime2 = __toESM(require_jsx_runtime());
369  var unescapeString = (arg) => {
370    return (0, import_html_entities.decodeEntities)(arg);
371  };
372  var CATEGORY_SLUG = "wp_pattern_category";
373  function CategorySelector({
374    categoryTerms,
375    onChange,
376    categoryMap
377  }) {
378    const [search, setSearch] = (0, import_element2.useState)("");
379    const debouncedSearch = (0, import_compose.useDebounce)(setSearch, 500);
380    const suggestions = (0, import_element2.useMemo)(() => {
381      return Array.from(categoryMap.values()).map((category) => unescapeString(category.label)).filter((category) => {
382        if (search !== "") {
383          return category.toLowerCase().includes(search.toLowerCase());
384        }
385        return true;
386      }).sort((a, b) => a.localeCompare(b));
387    }, [search, categoryMap]);
388    function handleChange(termNames) {
389      const uniqueTerms = termNames.reduce((terms, newTerm) => {
390        if (!terms.some(
391          (term) => term.toLowerCase() === newTerm.toLowerCase()
392        )) {
393          terms.push(newTerm);
394        }
395        return terms;
396      }, []);
397      onChange(uniqueTerms);
398    }
399    return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
400      import_components2.FormTokenField,
401      {
402        className: "patterns-menu-items__convert-modal-categories",
403        value: categoryTerms,
404        suggestions,
405        onChange: handleChange,
406        onInputChange: debouncedSearch,
407        label: (0, import_i18n2.__)("Categories"),
408        tokenizeOnBlur: true,
409        __experimentalExpandOnFocus: true,
410        __next40pxDefaultSize: true,
411        __nextHasNoMarginBottom: true
412      }
413    );
414  }
415
416  // packages/patterns/build-module/private-hooks.js
417  var import_data4 = __toESM(require_data());
418  var import_core_data2 = __toESM(require_core_data());
419  var import_element3 = __toESM(require_element());
420  function useAddPatternCategory() {
421    const { saveEntityRecord, invalidateResolution } = (0, import_data4.useDispatch)(import_core_data2.store);
422    const { corePatternCategories, userPatternCategories } = (0, import_data4.useSelect)(
423      (select) => {
424        const { getUserPatternCategories, getBlockPatternCategories } = select(import_core_data2.store);
425        return {
426          corePatternCategories: getBlockPatternCategories(),
427          userPatternCategories: getUserPatternCategories()
428        };
429      },
430      []
431    );
432    const categoryMap = (0, import_element3.useMemo)(() => {
433      const uniqueCategories = /* @__PURE__ */ new Map();
434      userPatternCategories.forEach((category) => {
435        uniqueCategories.set(category.label.toLowerCase(), {
436          label: category.label,
437          name: category.name,
438          id: category.id
439        });
440      });
441      corePatternCategories.forEach((category) => {
442        if (!uniqueCategories.has(category.label.toLowerCase()) && // There are two core categories with `Post` label so explicitly remove the one with
443        // the `query` slug to avoid any confusion.
444        category.name !== "query") {
445          uniqueCategories.set(category.label.toLowerCase(), {
446            label: category.label,
447            name: category.name
448          });
449        }
450      });
451      return uniqueCategories;
452    }, [userPatternCategories, corePatternCategories]);
453    async function findOrCreateTerm(term) {
454      try {
455        const existingTerm = categoryMap.get(term.toLowerCase());
456        if (existingTerm?.id) {
457          return existingTerm.id;
458        }
459        const termData = existingTerm ? { name: existingTerm.label, slug: existingTerm.name } : { name: term };
460        const newTerm = await saveEntityRecord(
461          "taxonomy",
462          CATEGORY_SLUG,
463          termData,
464          { throwOnError: true }
465        );
466        invalidateResolution("getUserPatternCategories");
467        return newTerm.id;
468      } catch (error) {
469        if (error.code !== "term_exists") {
470          throw error;
471        }
472        return error.data.term_id;
473      }
474    }
475    return { categoryMap, findOrCreateTerm };
476  }
477
478  // packages/patterns/build-module/components/create-pattern-modal.js
479  var import_jsx_runtime3 = __toESM(require_jsx_runtime());
480  function CreatePatternModal({
481    className = "patterns-menu-items__convert-modal",
482    modalTitle,
483    ...restProps
484  }) {
485    const defaultModalTitle = (0, import_data5.useSelect)(
486      (select) => select(import_core_data3.store).getPostType(PATTERN_TYPES.user)?.labels?.add_new_item,
487      []
488    );
489    return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
490      import_components3.Modal,
491      {
492        title: modalTitle || defaultModalTitle,
493        onRequestClose: restProps.onClose,
494        overlayClassName: className,
495        focusOnMount: "firstContentElement",
496        size: "small",
497        children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CreatePatternModalContents, { ...restProps })
498      }
499    );
500  }
501  function CreatePatternModalContents({
502    confirmLabel = (0, import_i18n3.__)("Add"),
503    defaultCategories = [],
504    content,
505    onClose,
506    onError,
507    onSuccess,
508    defaultSyncType = PATTERN_SYNC_TYPES.full,
509    defaultTitle = ""
510  }) {
511    const [syncType, setSyncType] = (0, import_element4.useState)(defaultSyncType);
512    const [categoryTerms, setCategoryTerms] = (0, import_element4.useState)(defaultCategories);
513    const [title, setTitle] = (0, import_element4.useState)(defaultTitle);
514    const [isSaving, setIsSaving] = (0, import_element4.useState)(false);
515    const { createPattern: createPattern2 } = unlock((0, import_data5.useDispatch)(store));
516    const { createErrorNotice } = (0, import_data5.useDispatch)(import_notices.store);
517    const { categoryMap, findOrCreateTerm } = useAddPatternCategory();
518    async function onCreate(patternTitle, sync) {
519      if (!title || isSaving) {
520        return;
521      }
522      try {
523        setIsSaving(true);
524        const categories = await Promise.all(
525          categoryTerms.map(
526            (termName) => findOrCreateTerm(termName)
527          )
528        );
529        const newPattern = await createPattern2(
530          patternTitle,
531          sync,
532          typeof content === "function" ? content() : content,
533          categories
534        );
535        onSuccess({
536          pattern: newPattern,
537          categoryId: PATTERN_DEFAULT_CATEGORY
538        });
539      } catch (error) {
540        createErrorNotice(error.message, {
541          type: "snackbar",
542          id: "pattern-create"
543        });
544        onError?.();
545      } finally {
546        setIsSaving(false);
547        setCategoryTerms([]);
548        setTitle("");
549      }
550    }
551    return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
552      "form",
553      {
554        onSubmit: (event) => {
555          event.preventDefault();
556          onCreate(title, syncType);
557        },
558        children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_components3.__experimentalVStack, { spacing: "5", children: [
559          /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
560            import_components3.TextControl,
561            {
562              label: (0, import_i18n3.__)("Name"),
563              value: title,
564              onChange: setTitle,
565              placeholder: (0, import_i18n3.__)("My pattern"),
566              className: "patterns-create-modal__name-input",
567              __nextHasNoMarginBottom: true,
568              __next40pxDefaultSize: true
569            }
570          ),
571          /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
572            CategorySelector,
573            {
574              categoryTerms,
575              onChange: setCategoryTerms,
576              categoryMap
577            }
578          ),
579          /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
580            import_components3.ToggleControl,
581            {
582              __nextHasNoMarginBottom: true,
583              label: (0, import_i18n3._x)("Synced", "pattern (singular)"),
584              help: (0, import_i18n3.__)(
585                "Sync this pattern across multiple locations."
586              ),
587              checked: syncType === PATTERN_SYNC_TYPES.full,
588              onChange: () => {
589                setSyncType(
590                  syncType === PATTERN_SYNC_TYPES.full ? PATTERN_SYNC_TYPES.unsynced : PATTERN_SYNC_TYPES.full
591                );
592              }
593            }
594          ),
595          /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_components3.__experimentalHStack, { justify: "right", children: [
596            /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
597              import_components3.Button,
598              {
599                __next40pxDefaultSize: true,
600                variant: "tertiary",
601                onClick: () => {
602                  onClose();
603                  setTitle("");
604                },
605                children: (0, import_i18n3.__)("Cancel")
606              }
607            ),
608            /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
609              import_components3.Button,
610              {
611                __next40pxDefaultSize: true,
612                variant: "primary",
613                type: "submit",
614                "aria-disabled": !title || isSaving,
615                isBusy: isSaving,
616                children: confirmLabel
617              }
618            )
619          ] })
620        ] })
621      }
622    );
623  }
624
625  // packages/patterns/build-module/components/duplicate-pattern-modal.js
626  var import_core_data4 = __toESM(require_core_data());
627  var import_data6 = __toESM(require_data());
628  var import_i18n4 = __toESM(require_i18n());
629  var import_notices2 = __toESM(require_notices());
630  var import_jsx_runtime4 = __toESM(require_jsx_runtime());
631  function getTermLabels(pattern, categories) {
632    if (pattern.type !== PATTERN_TYPES.user) {
633      return categories.core?.filter(
634        (category) => pattern.categories?.includes(category.name)
635      ).map((category) => category.label);
636    }
637    return categories.user?.filter(
638      (category) => pattern.wp_pattern_category?.includes(category.id)
639    ).map((category) => category.label);
640  }
641  function useDuplicatePatternProps({ pattern, onSuccess }) {
642    const { createSuccessNotice } = (0, import_data6.useDispatch)(import_notices2.store);
643    const categories = (0, import_data6.useSelect)((select) => {
644      const { getUserPatternCategories, getBlockPatternCategories } = select(import_core_data4.store);
645      return {
646        core: getBlockPatternCategories(),
647        user: getUserPatternCategories()
648      };
649    });
650    if (!pattern) {
651      return null;
652    }
653    return {
654      content: pattern.content,
655      defaultCategories: getTermLabels(pattern, categories),
656      defaultSyncType: pattern.type !== PATTERN_TYPES.user ? PATTERN_SYNC_TYPES.unsynced : pattern.wp_pattern_sync_status || PATTERN_SYNC_TYPES.full,
657      defaultTitle: (0, import_i18n4.sprintf)(
658        /* translators: %s: Existing pattern title */
659        (0, import_i18n4._x)("%s (Copy)", "pattern"),
660        typeof pattern.title === "string" ? pattern.title : pattern.title.raw
661      ),
662      onSuccess: ({ pattern: newPattern }) => {
663        createSuccessNotice(
664          (0, import_i18n4.sprintf)(
665            // translators: %s: The new pattern's title e.g. 'Call to action (copy)'.
666            (0, import_i18n4._x)('"%s" duplicated.', "pattern"),
667            newPattern.title.raw
668          ),
669          {
670            type: "snackbar",
671            id: "patterns-create"
672          }
673        );
674        onSuccess?.({ pattern: newPattern });
675      }
676    };
677  }
678  function DuplicatePatternModal({
679    pattern,
680    onClose,
681    onSuccess
682  }) {
683    const duplicatedProps = useDuplicatePatternProps({ pattern, onSuccess });
684    if (!pattern) {
685      return null;
686    }
687    return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
688      CreatePatternModal,
689      {
690        modalTitle: (0, import_i18n4.__)("Duplicate pattern"),
691        confirmLabel: (0, import_i18n4.__)("Duplicate"),
692        onClose,
693        onError: onClose,
694        ...duplicatedProps
695      }
696    );
697  }
698
699  // packages/patterns/build-module/components/rename-pattern-modal.js
700  var import_components4 = __toESM(require_components());
701  var import_core_data5 = __toESM(require_core_data());
702  var import_data7 = __toESM(require_data());
703  var import_element5 = __toESM(require_element());
704  var import_html_entities2 = __toESM(require_html_entities());
705  var import_i18n5 = __toESM(require_i18n());
706  var import_notices3 = __toESM(require_notices());
707  var import_jsx_runtime5 = __toESM(require_jsx_runtime());
708  function RenamePatternModal({
709    onClose,
710    onError,
711    onSuccess,
712    pattern,
713    ...props
714  }) {
715    const originalName = (0, import_html_entities2.decodeEntities)(pattern.title);
716    const [name, setName] = (0, import_element5.useState)(originalName);
717    const [isSaving, setIsSaving] = (0, import_element5.useState)(false);
718    const {
719      editEntityRecord,
720      __experimentalSaveSpecifiedEntityEdits: saveSpecifiedEntityEdits
721    } = (0, import_data7.useDispatch)(import_core_data5.store);
722    const { createSuccessNotice, createErrorNotice } = (0, import_data7.useDispatch)(import_notices3.store);
723    const onRename = async (event) => {
724      event.preventDefault();
725      if (!name || name === pattern.title || isSaving) {
726        return;
727      }
728      try {
729        await editEntityRecord("postType", pattern.type, pattern.id, {
730          title: name
731        });
732        setIsSaving(true);
733        setName("");
734        onClose?.();
735        const savedRecord = await saveSpecifiedEntityEdits(
736          "postType",
737          pattern.type,
738          pattern.id,
739          ["title"],
740          { throwOnError: true }
741        );
742        onSuccess?.(savedRecord);
743        createSuccessNotice((0, import_i18n5.__)("Pattern renamed"), {
744          type: "snackbar",
745          id: "pattern-update"
746        });
747      } catch (error) {
748        onError?.();
749        const errorMessage = error.message && error.code !== "unknown_error" ? error.message : (0, import_i18n5.__)("An error occurred while renaming the pattern.");
750        createErrorNotice(errorMessage, {
751          type: "snackbar",
752          id: "pattern-update"
753        });
754      } finally {
755        setIsSaving(false);
756        setName("");
757      }
758    };
759    const onRequestClose = () => {
760      onClose?.();
761      setName("");
762    };
763    return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
764      import_components4.Modal,
765      {
766        title: (0, import_i18n5.__)("Rename"),
767        ...props,
768        onRequestClose: onClose,
769        focusOnMount: "firstContentElement",
770        size: "small",
771        children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("form", { onSubmit: onRename, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_components4.__experimentalVStack, { spacing: "5", children: [
772          /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
773            import_components4.TextControl,
774            {
775              __nextHasNoMarginBottom: true,
776              __next40pxDefaultSize: true,
777              label: (0, import_i18n5.__)("Name"),
778              value: name,
779              onChange: setName,
780              required: true
781            }
782          ),
783          /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_components4.__experimentalHStack, { justify: "right", children: [
784            /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
785              import_components4.Button,
786              {
787                __next40pxDefaultSize: true,
788                variant: "tertiary",
789                onClick: onRequestClose,
790                children: (0, import_i18n5.__)("Cancel")
791              }
792            ),
793            /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
794              import_components4.Button,
795              {
796                __next40pxDefaultSize: true,
797                variant: "primary",
798                type: "submit",
799                children: (0, import_i18n5.__)("Save")
800              }
801            )
802          ] })
803        ] }) })
804      }
805    );
806  }
807
808  // packages/patterns/build-module/components/index.js
809  var import_block_editor5 = __toESM(require_block_editor());
810
811  // packages/patterns/build-module/components/pattern-convert-button.js
812  var import_blocks2 = __toESM(require_blocks());
813  var import_block_editor3 = __toESM(require_block_editor());
814  var import_element6 = __toESM(require_element());
815  var import_components5 = __toESM(require_components());
816
817  // packages/icons/build-module/library/symbol.js
818  var import_primitives = __toESM(require_primitives());
819  var import_jsx_runtime6 = __toESM(require_jsx_runtime());
820  var symbol_default = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_primitives.SVG, { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_primitives.Path, { d: "M21.3 10.8l-5.6-5.6c-.7-.7-1.8-.7-2.5 0l-5.6 5.6c-.7.7-.7 1.8 0 2.5l5.6 5.6c.3.3.8.5 1.2.5s.9-.2 1.2-.5l5.6-5.6c.8-.7.8-1.9.1-2.5zm-1 1.4l-5.6 5.6c-.1.1-.3.1-.4 0l-5.6-5.6c-.1-.1-.1-.3 0-.4l5.6-5.6s.1-.1.2-.1.1 0 .2.1l5.6 5.6c.1.1.1.3 0 .4zm-16.6-.4L10 5.5l-1-1-6.3 6.3c-.7.7-.7 1.8 0 2.5L9 19.5l1.1-1.1-6.3-6.3c-.2 0-.2-.2-.1-.3z" }) });
821
822  // packages/patterns/build-module/components/pattern-convert-button.js
823  var import_data8 = __toESM(require_data());
824  var import_core_data6 = __toESM(require_core_data());
825  var import_i18n6 = __toESM(require_i18n());
826  var import_notices4 = __toESM(require_notices());
827  var import_jsx_runtime7 = __toESM(require_jsx_runtime());
828  function PatternConvertButton({
829    clientIds,
830    rootClientId,
831    closeBlockSettingsMenu
832  }) {
833    const { createSuccessNotice } = (0, import_data8.useDispatch)(import_notices4.store);
834    const { replaceBlocks, updateBlockAttributes } = (0, import_data8.useDispatch)(import_block_editor3.store);
835    const { setEditingPattern: setEditingPattern2 } = unlock((0, import_data8.useDispatch)(store));
836    const [isModalOpen, setIsModalOpen] = (0, import_element6.useState)(false);
837    const { getBlockAttributes } = (0, import_data8.useSelect)(import_block_editor3.store);
838    const canConvert = (0, import_data8.useSelect)(
839      (select) => {
840        const { canUser } = select(import_core_data6.store);
841        const {
842          getBlocksByClientId: getBlocksByClientId2,
843          canInsertBlockType,
844          getBlockRootClientId
845        } = select(import_block_editor3.store);
846        const rootId = rootClientId || (clientIds.length > 0 ? getBlockRootClientId(clientIds[0]) : void 0);
847        const blocks = getBlocksByClientId2(clientIds) ?? [];
848        const hasReusableBlockSupport = (blockName) => {
849          const blockType = (0, import_blocks2.getBlockType)(blockName);
850          const hasParent = blockType && "parent" in blockType;
851          return (0, import_blocks2.hasBlockSupport)(blockName, "reusable", !hasParent);
852        };
853        const isSyncedPattern = blocks.length === 1 && blocks[0] && (0, import_blocks2.isReusableBlock)(blocks[0]) && !!select(import_core_data6.store).getEntityRecord(
854          "postType",
855          "wp_block",
856          blocks[0].attributes.ref
857        );
858        const isUnsyncedPattern = window?.__experimentalContentOnlyPatternInsertion && blocks.length === 1 && blocks?.[0]?.attributes?.metadata?.patternName;
859        const _canConvert = (
860          // Hide when this is already a pattern.
861          !isUnsyncedPattern && !isSyncedPattern && // Hide when patterns are disabled.
862          canInsertBlockType("core/block", rootId) && blocks.every(
863            (block) => (
864              // Guard against the case where a regular block has *just* been converted.
865              !!block && // Hide on invalid blocks.
866              block.isValid && // Hide when block doesn't support being made into a pattern.
867              hasReusableBlockSupport(block.name)
868            )
869          ) && // Hide when current doesn't have permission to do that.
870          // Blocks refers to the wp_block post type, this checks the ability to create a post of that type.
871          !!canUser("create", {
872            kind: "postType",
873            name: "wp_block"
874          })
875        );
876        return _canConvert;
877      },
878      [clientIds, rootClientId]
879    );
880    const { getBlocksByClientId } = (0, import_data8.useSelect)(import_block_editor3.store);
881    const getContent = (0, import_element6.useCallback)(
882      () => (0, import_blocks2.serialize)(getBlocksByClientId(clientIds)),
883      [getBlocksByClientId, clientIds]
884    );
885    if (!canConvert) {
886      return null;
887    }
888    const handleSuccess = ({ pattern }) => {
889      if (pattern.wp_pattern_sync_status === PATTERN_SYNC_TYPES.unsynced) {
890        if (clientIds?.length === 1) {
891          const existingAttributes = getBlockAttributes(clientIds[0]);
892          updateBlockAttributes(clientIds[0], {
893            metadata: {
894              ...existingAttributes?.metadata ? existingAttributes.metadata : {},
895              patternName: `core/block/${pattern.id}`,
896              name: pattern.title.raw
897            }
898          });
899        }
900      } else {
901        const newBlock = (0, import_blocks2.createBlock)("core/block", {
902          ref: pattern.id
903        });
904        replaceBlocks(clientIds, newBlock);
905        setEditingPattern2(newBlock.clientId, true);
906        closeBlockSettingsMenu();
907      }
908      createSuccessNotice(
909        pattern.wp_pattern_sync_status === PATTERN_SYNC_TYPES.unsynced ? (0, import_i18n6.sprintf)(
910          // translators: %s: the name the user has given to the pattern.
911          (0, import_i18n6.__)("Unsynced pattern created: %s"),
912          pattern.title.raw
913        ) : (0, import_i18n6.sprintf)(
914          // translators: %s: the name the user has given to the pattern.
915          (0, import_i18n6.__)("Synced pattern created: %s"),
916          pattern.title.raw
917        ),
918        {
919          type: "snackbar",
920          id: "convert-to-pattern-success"
921        }
922      );
923      setIsModalOpen(false);
924    };
925    return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
926      /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
927        import_components5.MenuItem,
928        {
929          icon: symbol_default,
930          onClick: () => setIsModalOpen(true),
931          "aria-expanded": isModalOpen,
932          "aria-haspopup": "dialog",
933          children: (0, import_i18n6.__)("Create pattern")
934        }
935      ),
936      isModalOpen && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
937        CreatePatternModal,
938        {
939          content: getContent,
940          onSuccess: (pattern) => {
941            handleSuccess(pattern);
942          },
943          onError: () => {
944            setIsModalOpen(false);
945          },
946          onClose: () => {
947            setIsModalOpen(false);
948          }
949        }
950      )
951    ] });
952  }
953
954  // packages/patterns/build-module/components/patterns-manage-button.js
955  var import_components6 = __toESM(require_components());
956  var import_i18n7 = __toESM(require_i18n());
957  var import_blocks3 = __toESM(require_blocks());
958  var import_data9 = __toESM(require_data());
959  var import_block_editor4 = __toESM(require_block_editor());
960  var import_url = __toESM(require_url());
961  var import_core_data7 = __toESM(require_core_data());
962  var import_jsx_runtime8 = __toESM(require_jsx_runtime());
963  function PatternsManageButton({ clientId }) {
964    const {
965      attributes,
966      canDetach,
967      isVisible,
968      managePatternsUrl,
969      isSyncedPattern,
970      isUnsyncedPattern
971    } = (0, import_data9.useSelect)(
972      (select) => {
973        const { canRemoveBlock, getBlock } = select(import_block_editor4.store);
974        const { canUser } = select(import_core_data7.store);
975        const block = getBlock(clientId);
976        const _isUnsyncedPattern = window?.__experimentalContentOnlyPatternInsertion && !!block?.attributes?.metadata?.patternName;
977        const _isSyncedPattern = !!block && (0, import_blocks3.isReusableBlock)(block) && !!canUser("update", {
978          kind: "postType",
979          name: "wp_block",
980          id: block.attributes.ref
981        });
982        return {
983          attributes: block.attributes,
984          // For unsynced patterns, detaching is simply removing the `patternName` attribute.
985          // For synced patterns, the `core:block` block is replaced with its inner blocks,
986          // so checking whether `canRemoveBlock` is possible is required.
987          canDetach: _isUnsyncedPattern || _isSyncedPattern && canRemoveBlock(clientId),
988          isUnsyncedPattern: _isUnsyncedPattern,
989          isSyncedPattern: _isSyncedPattern,
990          isVisible: _isUnsyncedPattern || _isSyncedPattern,
991          // The site editor and templates both check whether the user
992          // has edit_theme_options capabilities. We can leverage that here
993          // and omit the manage patterns link if the user can't access it.
994          managePatternsUrl: canUser("create", {
995            kind: "postType",
996            name: "wp_template"
997          }) ? (0, import_url.addQueryArgs)("site-editor.php", {
998            p: "/pattern"
999          }) : (0, import_url.addQueryArgs)("edit.php", {
1000            post_type: "wp_block"
1001          })
1002        };
1003      },
1004      [clientId]
1005    );
1006    const { updateBlockAttributes } = (0, import_data9.useDispatch)(import_block_editor4.store);
1007    const { convertSyncedPatternToStatic: convertSyncedPatternToStatic2 } = unlock(
1008      (0, import_data9.useDispatch)(store)
1009    );
1010    if (!isVisible) {
1011      return null;
1012    }
1013    return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1014      canDetach && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1015        import_components6.MenuItem,
1016        {
1017          onClick: () => {
1018            if (isSyncedPattern) {
1019              convertSyncedPatternToStatic2(clientId);
1020            }
1021            if (isUnsyncedPattern) {
1022              const {
1023                patternName,
1024                ...attributesWithoutPatternName
1025              } = attributes?.metadata ?? {};
1026              updateBlockAttributes(clientId, {
1027                metadata: attributesWithoutPatternName
1028              });
1029            }
1030          },
1031          children: (0, import_i18n7.__)("Disconnect pattern")
1032        }
1033      ),
1034      /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.MenuItem, { href: managePatternsUrl, children: (0, import_i18n7.__)("Manage patterns") })
1035    ] });
1036  }
1037  var patterns_manage_button_default = PatternsManageButton;
1038
1039  // packages/patterns/build-module/components/index.js
1040  var import_jsx_runtime9 = __toESM(require_jsx_runtime());
1041  function PatternsMenuItems({ rootClientId }) {
1042    return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_block_editor5.BlockSettingsMenuControls, { children: ({ selectedClientIds, onClose }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_jsx_runtime9.Fragment, { children: [
1043      /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1044        PatternConvertButton,
1045        {
1046          clientIds: selectedClientIds,
1047          rootClientId,
1048          closeBlockSettingsMenu: onClose
1049        }
1050      ),
1051      selectedClientIds.length === 1 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1052        patterns_manage_button_default,
1053        {
1054          clientId: selectedClientIds[0]
1055        }
1056      )
1057    ] }) });
1058  }
1059
1060  // packages/patterns/build-module/components/rename-pattern-category-modal.js
1061  var import_components7 = __toESM(require_components());
1062  var import_core_data8 = __toESM(require_core_data());
1063  var import_data10 = __toESM(require_data());
1064  var import_element7 = __toESM(require_element());
1065  var import_html_entities3 = __toESM(require_html_entities());
1066  var import_i18n8 = __toESM(require_i18n());
1067  var import_notices5 = __toESM(require_notices());
1068  var import_a11y = __toESM(require_a11y());
1069  var import_jsx_runtime10 = __toESM(require_jsx_runtime());
1070  function RenamePatternCategoryModal({
1071    category,
1072    existingCategories,
1073    onClose,
1074    onError,
1075    onSuccess,
1076    ...props
1077  }) {
1078    const id = (0, import_element7.useId)();
1079    const textControlRef = (0, import_element7.useRef)();
1080    const [name, setName] = (0, import_element7.useState)((0, import_html_entities3.decodeEntities)(category.name));
1081    const [isSaving, setIsSaving] = (0, import_element7.useState)(false);
1082    const [validationMessage, setValidationMessage] = (0, import_element7.useState)(false);
1083    const validationMessageId = validationMessage ? `patterns-rename-pattern-category-modal__validation-message-${id}` : void 0;
1084    const { saveEntityRecord, invalidateResolution } = (0, import_data10.useDispatch)(import_core_data8.store);
1085    const { createErrorNotice, createSuccessNotice } = (0, import_data10.useDispatch)(import_notices5.store);
1086    const onChange = (newName) => {
1087      if (validationMessage) {
1088        setValidationMessage(void 0);
1089      }
1090      setName(newName);
1091    };
1092    const onSave = async (event) => {
1093      event.preventDefault();
1094      if (isSaving) {
1095        return;
1096      }
1097      if (!name || name === category.name) {
1098        const message = (0, import_i18n8.__)("Please enter a new name for this category.");
1099        (0, import_a11y.speak)(message, "assertive");
1100        setValidationMessage(message);
1101        textControlRef.current?.focus();
1102        return;
1103      }
1104      if (existingCategories.patternCategories.find((existingCategory) => {
1105        return existingCategory.id !== category.id && existingCategory.label.toLowerCase() === name.toLowerCase();
1106      })) {
1107        const message = (0, import_i18n8.__)(
1108          "This category already exists. Please use a different name."
1109        );
1110        (0, import_a11y.speak)(message, "assertive");
1111        setValidationMessage(message);
1112        textControlRef.current?.focus();
1113        return;
1114      }
1115      try {
1116        setIsSaving(true);
1117        const savedRecord = await saveEntityRecord(
1118          "taxonomy",
1119          CATEGORY_SLUG,
1120          {
1121            id: category.id,
1122            slug: category.slug,
1123            name
1124          }
1125        );
1126        invalidateResolution("getUserPatternCategories");
1127        onSuccess?.(savedRecord);
1128        onClose();
1129        createSuccessNotice((0, import_i18n8.__)("Pattern category renamed."), {
1130          type: "snackbar",
1131          id: "pattern-category-update"
1132        });
1133      } catch (error) {
1134        onError?.();
1135        createErrorNotice(error.message, {
1136          type: "snackbar",
1137          id: "pattern-category-update"
1138        });
1139      } finally {
1140        setIsSaving(false);
1141        setName("");
1142      }
1143    };
1144    const onRequestClose = () => {
1145      onClose();
1146      setName("");
1147    };
1148    return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1149      import_components7.Modal,
1150      {
1151        title: (0, import_i18n8.__)("Rename"),
1152        onRequestClose,
1153        ...props,
1154        children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("form", { onSubmit: onSave, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_components7.__experimentalVStack, { spacing: "5", children: [
1155          /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_components7.__experimentalVStack, { spacing: "2", children: [
1156            /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1157              import_components7.TextControl,
1158              {
1159                ref: textControlRef,
1160                __nextHasNoMarginBottom: true,
1161                __next40pxDefaultSize: true,
1162                label: (0, import_i18n8.__)("Name"),
1163                value: name,
1164                onChange,
1165                "aria-describedby": validationMessageId,
1166                required: true
1167              }
1168            ),
1169            validationMessage && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1170              "span",
1171              {
1172                className: "patterns-rename-pattern-category-modal__validation-message",
1173                id: validationMessageId,
1174                children: validationMessage
1175              }
1176            )
1177          ] }),
1178          /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_components7.__experimentalHStack, { justify: "right", children: [
1179            /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1180              import_components7.Button,
1181              {
1182                __next40pxDefaultSize: true,
1183                variant: "tertiary",
1184                onClick: onRequestClose,
1185                children: (0, import_i18n8.__)("Cancel")
1186              }
1187            ),
1188            /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1189              import_components7.Button,
1190              {
1191                __next40pxDefaultSize: true,
1192                variant: "primary",
1193                type: "submit",
1194                "aria-disabled": !name || name === category.name || isSaving,
1195                isBusy: isSaving,
1196                children: (0, import_i18n8.__)("Save")
1197              }
1198            )
1199          ] })
1200        ] }) })
1201      }
1202    );
1203  }
1204
1205  // packages/patterns/build-module/components/pattern-overrides-controls.js
1206  var import_element9 = __toESM(require_element());
1207  var import_block_editor6 = __toESM(require_block_editor());
1208  var import_components9 = __toESM(require_components());
1209  var import_i18n10 = __toESM(require_i18n());
1210
1211  // packages/patterns/build-module/components/allow-overrides-modal.js
1212  var import_components8 = __toESM(require_components());
1213  var import_i18n9 = __toESM(require_i18n());
1214  var import_element8 = __toESM(require_element());
1215  var import_a11y2 = __toESM(require_a11y());
1216  var import_jsx_runtime11 = __toESM(require_jsx_runtime());
1217  function AllowOverridesModal({
1218    placeholder,
1219    initialName = "",
1220    onClose,
1221    onSave
1222  }) {
1223    const [editedBlockName, setEditedBlockName] = (0, import_element8.useState)(initialName);
1224    const descriptionId = (0, import_element8.useId)();
1225    const isNameValid = !!editedBlockName.trim();
1226    const handleSubmit = () => {
1227      if (editedBlockName !== initialName) {
1228        const message = (0, import_i18n9.sprintf)(
1229          /* translators: %s: new name/label for the block */
1230          (0, import_i18n9.__)('Block name changed to: "%s".'),
1231          editedBlockName
1232        );
1233        (0, import_a11y2.speak)(message, "assertive");
1234      }
1235      onSave(editedBlockName);
1236      onClose();
1237    };
1238    return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1239      import_components8.Modal,
1240      {
1241        title: (0, import_i18n9.__)("Enable overrides"),
1242        onRequestClose: onClose,
1243        focusOnMount: "firstContentElement",
1244        aria: { describedby: descriptionId },
1245        size: "small",
1246        children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1247          "form",
1248          {
1249            onSubmit: (event) => {
1250              event.preventDefault();
1251              if (!isNameValid) {
1252                return;
1253              }
1254              handleSubmit();
1255            },
1256            children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_components8.__experimentalVStack, { spacing: "6", children: [
1257              /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_components8.__experimentalText, { id: descriptionId, children: (0, import_i18n9.__)(
1258                "Overrides are changes you make to a block within a synced pattern instance. Use overrides to customize a synced pattern instance to suit its new context. Name this block to specify an override."
1259              ) }),
1260              /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1261                import_components8.TextControl,
1262                {
1263                  __nextHasNoMarginBottom: true,
1264                  __next40pxDefaultSize: true,
1265                  value: editedBlockName,
1266                  label: (0, import_i18n9.__)("Name"),
1267                  help: (0, import_i18n9.__)(
1268                    'For example, if you are creating a recipe pattern, you use "Recipe Title", "Recipe Description", etc.'
1269                  ),
1270                  placeholder,
1271                  onChange: setEditedBlockName
1272                }
1273              ),
1274              /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_components8.__experimentalHStack, { justify: "right", children: [
1275                /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1276                  import_components8.Button,
1277                  {
1278                    __next40pxDefaultSize: true,
1279                    variant: "tertiary",
1280                    onClick: onClose,
1281                    children: (0, import_i18n9.__)("Cancel")
1282                  }
1283                ),
1284                /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1285                  import_components8.Button,
1286                  {
1287                    __next40pxDefaultSize: true,
1288                    "aria-disabled": !isNameValid,
1289                    variant: "primary",
1290                    type: "submit",
1291                    children: (0, import_i18n9.__)("Enable")
1292                  }
1293                )
1294              ] })
1295            ] })
1296          }
1297        )
1298      }
1299    );
1300  }
1301  function DisallowOverridesModal({ onClose, onSave }) {
1302    const descriptionId = (0, import_element8.useId)();
1303    return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1304      import_components8.Modal,
1305      {
1306        title: (0, import_i18n9.__)("Disable overrides"),
1307        onRequestClose: onClose,
1308        aria: { describedby: descriptionId },
1309        size: "small",
1310        children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1311          "form",
1312          {
1313            onSubmit: (event) => {
1314              event.preventDefault();
1315              onSave();
1316              onClose();
1317            },
1318            children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_components8.__experimentalVStack, { spacing: "6", children: [
1319              /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_components8.__experimentalText, { id: descriptionId, children: (0, import_i18n9.__)(
1320                "Are you sure you want to disable overrides? Disabling overrides will revert all applied overrides for this block throughout instances of this pattern."
1321              ) }),
1322              /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_components8.__experimentalHStack, { justify: "right", children: [
1323                /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1324                  import_components8.Button,
1325                  {
1326                    __next40pxDefaultSize: true,
1327                    variant: "tertiary",
1328                    onClick: onClose,
1329                    children: (0, import_i18n9.__)("Cancel")
1330                  }
1331                ),
1332                /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1333                  import_components8.Button,
1334                  {
1335                    __next40pxDefaultSize: true,
1336                    variant: "primary",
1337                    type: "submit",
1338                    children: (0, import_i18n9.__)("Disable")
1339                  }
1340                )
1341              ] })
1342            ] })
1343          }
1344        )
1345      }
1346    );
1347  }
1348
1349  // packages/patterns/build-module/components/pattern-overrides-controls.js
1350  var import_jsx_runtime12 = __toESM(require_jsx_runtime());
1351  function PatternOverridesControls({
1352    attributes,
1353    setAttributes,
1354    name: blockName
1355  }) {
1356    const controlId = (0, import_element9.useId)();
1357    const [showAllowOverridesModal, setShowAllowOverridesModal] = (0, import_element9.useState)(false);
1358    const [showDisallowOverridesModal, setShowDisallowOverridesModal] = (0, import_element9.useState)(false);
1359    const hasName = !!attributes.metadata?.name;
1360    const defaultBindings = attributes.metadata?.bindings?.__default;
1361    const hasOverrides = hasName && defaultBindings?.source === PATTERN_OVERRIDES_BINDING_SOURCE;
1362    const isConnectedToOtherSources = defaultBindings?.source && defaultBindings.source !== PATTERN_OVERRIDES_BINDING_SOURCE;
1363    const { updateBlockBindings } = (0, import_block_editor6.useBlockBindingsUtils)();
1364    function updateBindings(isChecked, customName) {
1365      if (customName) {
1366        setAttributes({
1367          metadata: {
1368            ...attributes.metadata,
1369            name: customName
1370          }
1371        });
1372      }
1373      updateBlockBindings({
1374        __default: isChecked ? { source: PATTERN_OVERRIDES_BINDING_SOURCE } : void 0
1375      });
1376    }
1377    if (isConnectedToOtherSources) {
1378      return null;
1379    }
1380    const hasUnsupportedImageAttributes = blockName === "core/image" && !!attributes.href?.length;
1381    const helpText = !hasOverrides && hasUnsupportedImageAttributes ? (0, import_i18n10.__)(
1382      `Overrides currently don't support image links. Remove the link first before enabling overrides.`
1383    ) : (0, import_i18n10.__)(
1384      "Allow changes to this block throughout instances of this pattern."
1385    );
1386    return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1387      /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_block_editor6.InspectorControls, { group: "advanced", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1388        import_components9.BaseControl,
1389        {
1390          __nextHasNoMarginBottom: true,
1391          id: controlId,
1392          label: (0, import_i18n10.__)("Overrides"),
1393          help: helpText,
1394          children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1395            import_components9.Button,
1396            {
1397              __next40pxDefaultSize: true,
1398              className: "pattern-overrides-control__allow-overrides-button",
1399              variant: "secondary",
1400              "aria-haspopup": "dialog",
1401              onClick: () => {
1402                if (hasOverrides) {
1403                  setShowDisallowOverridesModal(true);
1404                } else {
1405                  setShowAllowOverridesModal(true);
1406                }
1407              },
1408              disabled: !hasOverrides && hasUnsupportedImageAttributes,
1409              accessibleWhenDisabled: true,
1410              children: hasOverrides ? (0, import_i18n10.__)("Disable overrides") : (0, import_i18n10.__)("Enable overrides")
1411            }
1412          )
1413        }
1414      ) }),
1415      showAllowOverridesModal && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1416        AllowOverridesModal,
1417        {
1418          initialName: attributes.metadata?.name,
1419          onClose: () => setShowAllowOverridesModal(false),
1420          onSave: (newName) => {
1421            updateBindings(true, newName);
1422          }
1423        }
1424      ),
1425      showDisallowOverridesModal && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1426        DisallowOverridesModal,
1427        {
1428          onClose: () => setShowDisallowOverridesModal(false),
1429          onSave: () => updateBindings(false)
1430        }
1431      )
1432    ] });
1433  }
1434  var pattern_overrides_controls_default = PatternOverridesControls;
1435
1436  // packages/patterns/build-module/components/reset-overrides-control.js
1437  var import_block_editor7 = __toESM(require_block_editor());
1438  var import_components10 = __toESM(require_components());
1439  var import_data11 = __toESM(require_data());
1440  var import_i18n11 = __toESM(require_i18n());
1441  var import_jsx_runtime13 = __toESM(require_jsx_runtime());
1442  var CONTENT = "content";
1443  function ResetOverridesControl(props) {
1444    const name = props.attributes.metadata?.name;
1445    const registry = (0, import_data11.useRegistry)();
1446    const isOverridden = (0, import_data11.useSelect)(
1447      (select) => {
1448        if (!name) {
1449          return;
1450        }
1451        const { getBlockAttributes, getBlockParentsByBlockName } = select(import_block_editor7.store);
1452        const [patternClientId] = getBlockParentsByBlockName(
1453          props.clientId,
1454          "core/block",
1455          true
1456        );
1457        if (!patternClientId) {
1458          return;
1459        }
1460        const overrides = getBlockAttributes(patternClientId)[CONTENT];
1461        if (!overrides) {
1462          return;
1463        }
1464        return overrides.hasOwnProperty(name);
1465      },
1466      [props.clientId, name]
1467    );
1468    function onClick() {
1469      const { getBlockAttributes, getBlockParentsByBlockName } = registry.select(import_block_editor7.store);
1470      const [patternClientId] = getBlockParentsByBlockName(
1471        props.clientId,
1472        "core/block",
1473        true
1474      );
1475      if (!patternClientId) {
1476        return;
1477      }
1478      const overrides = getBlockAttributes(patternClientId)[CONTENT];
1479      if (!overrides.hasOwnProperty(name)) {
1480        return;
1481      }
1482      const { updateBlockAttributes, __unstableMarkLastChangeAsPersistent } = registry.dispatch(import_block_editor7.store);
1483      __unstableMarkLastChangeAsPersistent();
1484      let newOverrides = { ...overrides };
1485      delete newOverrides[name];
1486      if (!Object.keys(newOverrides).length) {
1487        newOverrides = void 0;
1488      }
1489      updateBlockAttributes(patternClientId, {
1490        [CONTENT]: newOverrides
1491      });
1492    }
1493    return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_block_editor7.__unstableBlockToolbarLastItem, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components10.ToolbarGroup, { children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_components10.ToolbarButton, { onClick, disabled: !isOverridden, children: (0, import_i18n11.__)("Reset") }) }) });
1494  }
1495
1496  // packages/patterns/build-module/private-apis.js
1497  var privateApis = {};
1498  lock(privateApis, {
1499    OverridesPanel,
1500    CreatePatternModal,
1501    CreatePatternModalContents,
1502    DuplicatePatternModal,
1503    isOverridableBlock,
1504    hasOverridableBlocks,
1505    useDuplicatePatternProps,
1506    RenamePatternModal,
1507    PatternsMenuItems,
1508    RenamePatternCategoryModal,
1509    PatternOverridesControls: pattern_overrides_controls_default,
1510    ResetOverridesControl,
1511    useAddPatternCategory,
1512    PATTERN_TYPES,
1513    PATTERN_DEFAULT_CATEGORY,
1514    PATTERN_USER_CATEGORY,
1515    EXCLUDED_PATTERN_SOURCES,
1516    PATTERN_SYNC_TYPES,
1517    PARTIAL_SYNCING_SUPPORTED_BLOCKS
1518  });
1519  return __toCommonJS(index_exports);
1520})();
1521//# sourceMappingURL=index.js.map
Note: See TracBrowser for help on using the repository browser.