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

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