PluginProbe
Gutenberg / 22.4.4
Gutenberg v22.4.4
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.4.4, at build/scripts/patterns/index.js

1,500 lines 57.2 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 = window?.__experimentalContentOnlyPatternInsertion && 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 } = (0, import_data9.useSelect)(
955 (select) => {
956 const { canRemoveBlock, getBlock } = select(import_block_editor4.store);
957 const { canUser } = select(import_core_data7.store);
958 const block = getBlock(clientId);
959 const _isUnsyncedPattern = window?.__experimentalContentOnlyPatternInsertion && !!block?.attributes?.metadata?.patternName;
960 const _isSyncedPattern = !!block && (0, import_blocks3.isReusableBlock)(block) && !!canUser("update", {
961 kind: "postType",
962 name: "wp_block",
963 id: block.attributes.ref
964 });
965 return {
966 attributes: block.attributes,
967 // For unsynced patterns, detaching is simply removing the `patternName` attribute.
968 // For synced patterns, the `core:block` block is replaced with its inner blocks,
969 // so checking whether `canRemoveBlock` is possible is required.
970 canDetach: _isUnsyncedPattern || _isSyncedPattern && canRemoveBlock(clientId),
971 isUnsyncedPattern: _isUnsyncedPattern,
972 isSyncedPattern: _isSyncedPattern,
973 isVisible: _isUnsyncedPattern || _isSyncedPattern,
974 // The site editor and templates both check whether the user
975 // has edit_theme_options capabilities. We can leverage that here
976 // and omit the manage patterns link if the user can't access it.
977 managePatternsUrl: canUser("create", {
978 kind: "postType",
979 name: "wp_template"
980 }) ? (0, import_url.addQueryArgs)("site-editor.php", {
981 p: "/pattern"
982 }) : (0, import_url.addQueryArgs)("edit.php", {
983 post_type: "wp_block"
984 })
985 };
986 },
987 [clientId]
988 );
989 const { updateBlockAttributes } = (0, import_data9.useDispatch)(import_block_editor4.store);
990 const { convertSyncedPatternToStatic: convertSyncedPatternToStatic2 } = unlock(
991 (0, import_data9.useDispatch)(store)
992 );
993 if (!isVisible) {
994 return null;
995 }
996 return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
997 canDetach && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
998 import_components6.MenuItem,
999 {
1000 onClick: () => {
1001 if (isSyncedPattern) {
1002 convertSyncedPatternToStatic2(clientId);
1003 }
1004 if (isUnsyncedPattern) {
1005 const {
1006 patternName,
1007 ...attributesWithoutPatternName
1008 } = attributes?.metadata ?? {};
1009 updateBlockAttributes(clientId, {
1010 metadata: attributesWithoutPatternName
1011 });
1012 }
1013 },
1014 children: (0, import_i18n7.__)("Disconnect pattern")
1015 }
1016 ),
1017 /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_components6.MenuItem, { href: managePatternsUrl, children: (0, import_i18n7.__)("Manage patterns") })
1018 ] });
1019 }
1020 var patterns_manage_button_default = PatternsManageButton;
1021
1022 // packages/patterns/build-module/components/index.mjs
1023 var import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
1024 function PatternsMenuItems({ rootClientId }) {
1025 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: [
1026 /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1027 PatternConvertButton,
1028 {
1029 clientIds: selectedClientIds,
1030 rootClientId,
1031 closeBlockSettingsMenu: onClose
1032 }
1033 ),
1034 selectedClientIds.length === 1 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
1035 patterns_manage_button_default,
1036 {
1037 clientId: selectedClientIds[0]
1038 }
1039 )
1040 ] }) });
1041 }
1042
1043 // packages/patterns/build-module/components/rename-pattern-category-modal.mjs
1044 var import_components7 = __toESM(require_components(), 1);
1045 var import_core_data8 = __toESM(require_core_data(), 1);
1046 var import_data10 = __toESM(require_data(), 1);
1047 var import_element7 = __toESM(require_element(), 1);
1048 var import_html_entities3 = __toESM(require_html_entities(), 1);
1049 var import_i18n8 = __toESM(require_i18n(), 1);
1050 var import_notices5 = __toESM(require_notices(), 1);
1051 var import_a11y = __toESM(require_a11y(), 1);
1052 var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
1053 function RenamePatternCategoryModal({
1054 category,
1055 existingCategories,
1056 onClose,
1057 onError,
1058 onSuccess,
1059 ...props
1060 }) {
1061 const id = (0, import_element7.useId)();
1062 const textControlRef = (0, import_element7.useRef)();
1063 const [name, setName] = (0, import_element7.useState)((0, import_html_entities3.decodeEntities)(category.name));
1064 const [isSaving, setIsSaving] = (0, import_element7.useState)(false);
1065 const [validationMessage, setValidationMessage] = (0, import_element7.useState)(false);
1066 const validationMessageId = validationMessage ? `patterns-rename-pattern-category-modal__validation-message-${id}` : void 0;
1067 const { saveEntityRecord, invalidateResolution } = (0, import_data10.useDispatch)(import_core_data8.store);
1068 const { createErrorNotice, createSuccessNotice } = (0, import_data10.useDispatch)(import_notices5.store);
1069 const onChange = (newName) => {
1070 if (validationMessage) {
1071 setValidationMessage(void 0);
1072 }
1073 setName(newName);
1074 };
1075 const onSave = async (event) => {
1076 event.preventDefault();
1077 if (isSaving) {
1078 return;
1079 }
1080 if (!name || name === category.name) {
1081 const message = (0, import_i18n8.__)("Please enter a new name for this category.");
1082 (0, import_a11y.speak)(message, "assertive");
1083 setValidationMessage(message);
1084 textControlRef.current?.focus();
1085 return;
1086 }
1087 if (existingCategories.patternCategories.find((existingCategory) => {
1088 return existingCategory.id !== category.id && existingCategory.label.toLowerCase() === name.toLowerCase();
1089 })) {
1090 const message = (0, import_i18n8.__)(
1091 "This category already exists. Please use a different name."
1092 );
1093 (0, import_a11y.speak)(message, "assertive");
1094 setValidationMessage(message);
1095 textControlRef.current?.focus();
1096 return;
1097 }
1098 try {
1099 setIsSaving(true);
1100 const savedRecord = await saveEntityRecord(
1101 "taxonomy",
1102 CATEGORY_SLUG,
1103 {
1104 id: category.id,
1105 slug: category.slug,
1106 name
1107 }
1108 );
1109 invalidateResolution("getUserPatternCategories");
1110 onSuccess?.(savedRecord);
1111 onClose();
1112 createSuccessNotice((0, import_i18n8.__)("Pattern category renamed."), {
1113 type: "snackbar",
1114 id: "pattern-category-update"
1115 });
1116 } catch (error) {
1117 onError?.();
1118 createErrorNotice(error.message, {
1119 type: "snackbar",
1120 id: "pattern-category-update"
1121 });
1122 } finally {
1123 setIsSaving(false);
1124 setName("");
1125 }
1126 };
1127 const onRequestClose = () => {
1128 onClose();
1129 setName("");
1130 };
1131 return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1132 import_components7.Modal,
1133 {
1134 title: (0, import_i18n8.__)("Rename"),
1135 onRequestClose,
1136 ...props,
1137 children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("form", { onSubmit: onSave, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_components7.__experimentalVStack, { spacing: "5", children: [
1138 /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_components7.__experimentalVStack, { spacing: "2", children: [
1139 /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1140 import_components7.TextControl,
1141 {
1142 ref: textControlRef,
1143 __next40pxDefaultSize: true,
1144 label: (0, import_i18n8.__)("Name"),
1145 value: name,
1146 onChange,
1147 "aria-describedby": validationMessageId,
1148 required: true
1149 }
1150 ),
1151 validationMessage && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1152 "span",
1153 {
1154 className: "patterns-rename-pattern-category-modal__validation-message",
1155 id: validationMessageId,
1156 children: validationMessage
1157 }
1158 )
1159 ] }),
1160 /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_components7.__experimentalHStack, { justify: "right", children: [
1161 /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1162 import_components7.Button,
1163 {
1164 __next40pxDefaultSize: true,
1165 variant: "tertiary",
1166 onClick: onRequestClose,
1167 children: (0, import_i18n8.__)("Cancel")
1168 }
1169 ),
1170 /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1171 import_components7.Button,
1172 {
1173 __next40pxDefaultSize: true,
1174 variant: "primary",
1175 type: "submit",
1176 "aria-disabled": !name || name === category.name || isSaving,
1177 isBusy: isSaving,
1178 children: (0, import_i18n8.__)("Save")
1179 }
1180 )
1181 ] })
1182 ] }) })
1183 }
1184 );
1185 }
1186
1187 // packages/patterns/build-module/components/pattern-overrides-controls.mjs
1188 var import_element9 = __toESM(require_element(), 1);
1189 var import_block_editor6 = __toESM(require_block_editor(), 1);
1190 var import_components9 = __toESM(require_components(), 1);
1191 var import_i18n10 = __toESM(require_i18n(), 1);
1192
1193 // packages/patterns/build-module/components/allow-overrides-modal.mjs
1194 var import_components8 = __toESM(require_components(), 1);
1195 var import_i18n9 = __toESM(require_i18n(), 1);
1196 var import_element8 = __toESM(require_element(), 1);
1197 var import_a11y2 = __toESM(require_a11y(), 1);
1198 var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
1199 function AllowOverridesModal({
1200 placeholder,
1201 initialName = "",
1202 onClose,
1203 onSave
1204 }) {
1205 const [editedBlockName, setEditedBlockName] = (0, import_element8.useState)(initialName);
1206 const descriptionId = (0, import_element8.useId)();
1207 const isNameValid = !!editedBlockName.trim();
1208 const handleSubmit = () => {
1209 if (editedBlockName !== initialName) {
1210 const message = (0, import_i18n9.sprintf)(
1211 /* translators: %s: new name/label for the block */
1212 (0, import_i18n9.__)('Block name changed to: "%s".'),
1213 editedBlockName
1214 );
1215 (0, import_a11y2.speak)(message, "assertive");
1216 }
1217 onSave(editedBlockName);
1218 onClose();
1219 };
1220 return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1221 import_components8.Modal,
1222 {
1223 title: (0, import_i18n9.__)("Enable overrides"),
1224 onRequestClose: onClose,
1225 focusOnMount: "firstContentElement",
1226 aria: { describedby: descriptionId },
1227 size: "small",
1228 children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1229 "form",
1230 {
1231 onSubmit: (event) => {
1232 event.preventDefault();
1233 if (!isNameValid) {
1234 return;
1235 }
1236 handleSubmit();
1237 },
1238 children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_components8.__experimentalVStack, { spacing: "6", children: [
1239 /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_components8.__experimentalText, { id: descriptionId, children: (0, import_i18n9.__)(
1240 "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."
1241 ) }),
1242 /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1243 import_components8.TextControl,
1244 {
1245 __next40pxDefaultSize: true,
1246 value: editedBlockName,
1247 label: (0, import_i18n9.__)("Name"),
1248 help: (0, import_i18n9.__)(
1249 'For example, if you are creating a recipe pattern, you use "Recipe Title", "Recipe Description", etc.'
1250 ),
1251 placeholder,
1252 onChange: setEditedBlockName
1253 }
1254 ),
1255 /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_components8.__experimentalHStack, { justify: "right", children: [
1256 /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1257 import_components8.Button,
1258 {
1259 __next40pxDefaultSize: true,
1260 variant: "tertiary",
1261 onClick: onClose,
1262 children: (0, import_i18n9.__)("Cancel")
1263 }
1264 ),
1265 /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1266 import_components8.Button,
1267 {
1268 __next40pxDefaultSize: true,
1269 "aria-disabled": !isNameValid,
1270 variant: "primary",
1271 type: "submit",
1272 children: (0, import_i18n9.__)("Enable")
1273 }
1274 )
1275 ] })
1276 ] })
1277 }
1278 )
1279 }
1280 );
1281 }
1282 function DisallowOverridesModal({ onClose, onSave }) {
1283 const descriptionId = (0, import_element8.useId)();
1284 return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1285 import_components8.Modal,
1286 {
1287 title: (0, import_i18n9.__)("Disable overrides"),
1288 onRequestClose: onClose,
1289 aria: { describedby: descriptionId },
1290 size: "small",
1291 children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1292 "form",
1293 {
1294 onSubmit: (event) => {
1295 event.preventDefault();
1296 onSave();
1297 onClose();
1298 },
1299 children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_components8.__experimentalVStack, { spacing: "6", children: [
1300 /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_components8.__experimentalText, { id: descriptionId, children: (0, import_i18n9.__)(
1301 "Are you sure you want to disable overrides? Disabling overrides will revert all applied overrides for this block throughout instances of this pattern."
1302 ) }),
1303 /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_components8.__experimentalHStack, { justify: "right", children: [
1304 /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1305 import_components8.Button,
1306 {
1307 __next40pxDefaultSize: true,
1308 variant: "tertiary",
1309 onClick: onClose,
1310 children: (0, import_i18n9.__)("Cancel")
1311 }
1312 ),
1313 /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1314 import_components8.Button,
1315 {
1316 __next40pxDefaultSize: true,
1317 variant: "primary",
1318 type: "submit",
1319 children: (0, import_i18n9.__)("Disable")
1320 }
1321 )
1322 ] })
1323 ] })
1324 }
1325 )
1326 }
1327 );
1328 }
1329
1330 // packages/patterns/build-module/components/pattern-overrides-controls.mjs
1331 var import_jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
1332 function PatternOverridesControls({
1333 attributes,
1334 setAttributes,
1335 name: blockName
1336 }) {
1337 const controlId = (0, import_element9.useId)();
1338 const [showAllowOverridesModal, setShowAllowOverridesModal] = (0, import_element9.useState)(false);
1339 const [showDisallowOverridesModal, setShowDisallowOverridesModal] = (0, import_element9.useState)(false);
1340 const hasName = !!attributes.metadata?.name;
1341 const defaultBindings = attributes.metadata?.bindings?.__default;
1342 const hasOverrides = hasName && defaultBindings?.source === PATTERN_OVERRIDES_BINDING_SOURCE;
1343 const isConnectedToOtherSources = defaultBindings?.source && defaultBindings.source !== PATTERN_OVERRIDES_BINDING_SOURCE;
1344 const { updateBlockBindings } = (0, import_block_editor6.useBlockBindingsUtils)();
1345 function updateBindings(isChecked, customName) {
1346 if (customName) {
1347 setAttributes({
1348 metadata: {
1349 ...attributes.metadata,
1350 name: customName
1351 }
1352 });
1353 }
1354 updateBlockBindings({
1355 __default: isChecked ? { source: PATTERN_OVERRIDES_BINDING_SOURCE } : void 0
1356 });
1357 }
1358 if (isConnectedToOtherSources) {
1359 return null;
1360 }
1361 const hasUnsupportedImageAttributes = blockName === "core/image" && !!attributes.href?.length;
1362 const helpText = !hasOverrides && hasUnsupportedImageAttributes ? (0, import_i18n10.__)(
1363 `Overrides currently don't support image links. Remove the link first before enabling overrides.`
1364 ) : (0, import_i18n10.__)(
1365 "Allow changes to this block throughout instances of this pattern."
1366 );
1367 return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
1368 /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_block_editor6.InspectorControls, { group: "advanced", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1369 import_components9.BaseControl,
1370 {
1371 id: controlId,
1372 label: (0, import_i18n10.__)("Overrides"),
1373 help: helpText,
1374 children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1375 import_components9.Button,
1376 {
1377 __next40pxDefaultSize: true,
1378 className: "pattern-overrides-control__allow-overrides-button",
1379 variant: "secondary",
1380 "aria-haspopup": "dialog",
1381 onClick: () => {
1382 if (hasOverrides) {
1383 setShowDisallowOverridesModal(true);
1384 } else {
1385 setShowAllowOverridesModal(true);
1386 }
1387 },
1388 disabled: !hasOverrides && hasUnsupportedImageAttributes,
1389 accessibleWhenDisabled: true,
1390 children: hasOverrides ? (0, import_i18n10.__)("Disable overrides") : (0, import_i18n10.__)("Enable overrides")
1391 }
1392 )
1393 }
1394 ) }),
1395 showAllowOverridesModal && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1396 AllowOverridesModal,
1397 {
1398 initialName: attributes.metadata?.name,
1399 onClose: () => setShowAllowOverridesModal(false),
1400 onSave: (newName) => {
1401 updateBindings(true, newName);
1402 }
1403 }
1404 ),
1405 showDisallowOverridesModal && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
1406 DisallowOverridesModal,
1407 {
1408 onClose: () => setShowDisallowOverridesModal(false),
1409 onSave: () => updateBindings(false)
1410 }
1411 )
1412 ] });
1413 }
1414 var pattern_overrides_controls_default = PatternOverridesControls;
1415
1416 // packages/patterns/build-module/components/reset-overrides-control.mjs
1417 var import_block_editor7 = __toESM(require_block_editor(), 1);
1418 var import_components10 = __toESM(require_components(), 1);
1419 var import_data11 = __toESM(require_data(), 1);
1420 var import_i18n11 = __toESM(require_i18n(), 1);
1421 var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
1422 var CONTENT = "content";
1423 function ResetOverridesControl(props) {
1424 const name = props.attributes.metadata?.name;
1425 const registry = (0, import_data11.useRegistry)();
1426 const isOverridden = (0, import_data11.useSelect)(
1427 (select) => {
1428 if (!name) {
1429 return;
1430 }
1431 const { getBlockAttributes, getBlockParentsByBlockName } = select(import_block_editor7.store);
1432 const [patternClientId] = getBlockParentsByBlockName(
1433 props.clientId,
1434 "core/block",
1435 true
1436 );
1437 if (!patternClientId) {
1438 return;
1439 }
1440 const overrides = getBlockAttributes(patternClientId)[CONTENT];
1441 if (!overrides) {
1442 return;
1443 }
1444 return overrides.hasOwnProperty(name);
1445 },
1446 [props.clientId, name]
1447 );
1448 function onClick() {
1449 const { getBlockAttributes, getBlockParentsByBlockName } = registry.select(import_block_editor7.store);
1450 const [patternClientId] = getBlockParentsByBlockName(
1451 props.clientId,
1452 "core/block",
1453 true
1454 );
1455 if (!patternClientId) {
1456 return;
1457 }
1458 const overrides = getBlockAttributes(patternClientId)[CONTENT];
1459 if (!overrides.hasOwnProperty(name)) {
1460 return;
1461 }
1462 const { updateBlockAttributes, __unstableMarkLastChangeAsPersistent } = registry.dispatch(import_block_editor7.store);
1463 __unstableMarkLastChangeAsPersistent();
1464 let newOverrides = { ...overrides };
1465 delete newOverrides[name];
1466 if (!Object.keys(newOverrides).length) {
1467 newOverrides = void 0;
1468 }
1469 updateBlockAttributes(patternClientId, {
1470 [CONTENT]: newOverrides
1471 });
1472 }
1473 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") }) }) });
1474 }
1475
1476 // packages/patterns/build-module/private-apis.mjs
1477 var privateApis = {};
1478 lock(privateApis, {
1479 OverridesPanel,
1480 CreatePatternModal,
1481 CreatePatternModalContents,
1482 DuplicatePatternModal,
1483 isOverridableBlock,
1484 useDuplicatePatternProps,
1485 RenamePatternModal,
1486 PatternsMenuItems,
1487 RenamePatternCategoryModal,
1488 PatternOverridesControls: pattern_overrides_controls_default,
1489 ResetOverridesControl,
1490 useAddPatternCategory,
1491 PATTERN_TYPES,
1492 PATTERN_DEFAULT_CATEGORY,
1493 PATTERN_USER_CATEGORY,
1494 EXCLUDED_PATTERN_SOURCES,
1495 PATTERN_SYNC_TYPES
1496 });
1497 return __toCommonJS(index_exports);
1498 })();
1499 //# sourceMappingURL=index.js.map
1500