PluginProbe
Gutenberg / 23.4.0
Gutenberg v23.4.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 23.4.0, at build/scripts/patterns/index.js

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