PluginProbe
Gutenberg / 22.3.0
Gutenberg v22.3.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 7.4.0 All 402 releases
gutenberg / build / scripts / patterns / index.js

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

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