PluginProbe
Gutenberg / 22.7.0
Gutenberg v22.7.0
24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 12.6.0 All 403 releases
gutenberg / build / scripts / patterns / index.js

index.js in Gutenberg 22.7.0, at build/scripts/patterns/index.js

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