PluginProbe ʕ •ᴥ•ʔ
Secure Custom Fields / 6.9.3
Secure Custom Fields v6.9.3
6.9.5 6.9.4 6.9.3 6.9.2 6.9.1 6.9.0 6.8.9 6.8.7 6.8.8 6.8.6 6.8.4 6.8.5 trunk 6.4.0-beta1 6.4.0-beta2 6.4.1 6.4.1-beta3 6.4.1-beta4 6.4.1-beta5 6.4.1-beta6 6.4.1-beta7 6.4.2 6.5.0 6.5.1 6.5.2 6.5.3 6.5.4 6.5.5 6.5.6 6.5.7 6.6.0 6.7.0 6.7.1 6.8.0 6.8.1 6.8.2 6.8.3
secure-custom-fields / assets / build / js / pro / acf-pro-blocks.js
secure-custom-fields / assets / build / js / pro Last commit date
acf-datastore.asset.php 3 months ago acf-datastore.js 3 months ago acf-datastore.js.map 3 months ago acf-datastore.min.asset.php 3 months ago acf-datastore.min.js 3 months ago acf-field-bindings.asset.php 3 months ago acf-field-bindings.js 3 months ago acf-field-bindings.js.map 3 months ago acf-field-bindings.min.asset.php 3 months ago acf-field-bindings.min.js 3 months ago acf-pro-blocks.asset.php 4 weeks ago acf-pro-blocks.js 4 weeks ago acf-pro-blocks.js.map 4 weeks ago acf-pro-blocks.min.asset.php 4 weeks ago acf-pro-blocks.min.js 4 weeks ago acf-pro-field-group.asset.php 11 months ago acf-pro-field-group.js 11 months ago acf-pro-field-group.js.map 9 months ago acf-pro-field-group.min.asset.php 9 months ago acf-pro-field-group.min.js 9 months ago acf-pro-input.asset.php 1 month ago acf-pro-input.js 1 month ago acf-pro-input.js.map 1 month ago acf-pro-input.min.asset.php 1 month ago acf-pro-input.min.js 1 month ago acf-pro-ui-options-page.asset.php 9 months ago acf-pro-ui-options-page.js 9 months ago acf-pro-ui-options-page.js.map 9 months ago acf-pro-ui-options-page.min.asset.php 9 months ago acf-pro-ui-options-page.min.js 9 months ago index.php 1 year ago
acf-pro-blocks.js
5887 lines
1 /******/ (() => { // webpackBootstrap
2 /******/ var __webpack_modules__ = ({
3
4 /***/ "./assets/src/js/pro/_acf-blocks-v3.js":
5 /*!*********************************************!*\
6 !*** ./assets/src/js/pro/_acf-blocks-v3.js ***!
7 \*********************************************/
8 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
9
10 "use strict";
11 __webpack_require__.r(__webpack_exports__);
12 /* harmony import */ var _blocks_v3_utils_post_locking__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./blocks-v3/utils/post-locking */ "./assets/src/js/pro/blocks-v3/utils/post-locking.js");
13 /* harmony import */ var _blocks_v3_components_jsx_parser__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./blocks-v3/components/jsx-parser */ "./assets/src/js/pro/blocks-v3/components/jsx-parser.js");
14 /* harmony import */ var _blocks_v3_register_block_type_v3__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./blocks-v3/register-block-type-v3 */ "./assets/src/js/pro/blocks-v3/register-block-type-v3.js");
15 /**
16 * ACF Blocks Version 3 - Entry Point
17 * Imports all modules and initializes the block system
18 */
19
20 // Import utilities.
21
22
23 // Import JSX parser and expose on acf global.
24
25
26 // Import block registration (initializes automatically via acf.addAction).
27
28
29 /***/ }),
30
31 /***/ "./assets/src/js/pro/_acf-blocks.js":
32 /*!******************************************!*\
33 !*** ./assets/src/js/pro/_acf-blocks.js ***!
34 \******************************************/
35 /***/ ((__unused_webpack_module, __unused_webpack_exports, __webpack_require__) => {
36
37 function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
38 const md5 = __webpack_require__(/*! md5 */ "./node_modules/md5/md5.js");
39 (($, undefined) => {
40 // Dependencies.
41 const {
42 BlockControls,
43 InspectorControls,
44 InnerBlocks,
45 useBlockProps,
46 AlignmentToolbar,
47 BlockVerticalAlignmentToolbar
48 } = wp.blockEditor;
49 const {
50 ToolbarGroup,
51 ToolbarButton,
52 Placeholder,
53 Spinner
54 } = wp.components;
55 const {
56 Fragment
57 } = wp.element;
58 const {
59 Component
60 } = React;
61 const {
62 useSelect
63 } = wp.data;
64 const {
65 createHigherOrderComponent
66 } = wp.compose;
67
68 // Potentially experimental dependencies.
69 const BlockAlignmentMatrixToolbar = wp.blockEditor.__experimentalBlockAlignmentMatrixToolbar || wp.blockEditor.BlockAlignmentMatrixToolbar;
70 // Gutenberg v10.x begins transition from Toolbar components to Control components.
71 const BlockAlignmentMatrixControl = wp.blockEditor.__experimentalBlockAlignmentMatrixControl || wp.blockEditor.BlockAlignmentMatrixControl;
72 const BlockFullHeightAlignmentControl = wp.blockEditor.__experimentalBlockFullHeightAligmentControl || wp.blockEditor.__experimentalBlockFullHeightAlignmentControl || wp.blockEditor.BlockFullHeightAlignmentControl;
73 const useInnerBlocksProps = wp.blockEditor.__experimentalUseInnerBlocksProps || wp.blockEditor.useInnerBlocksProps;
74
75 /**
76 * Storage for registered block types.
77 *
78 * @since ACF 5.8.0
79 * @var object
80 */
81 const blockTypes = {};
82
83 /**
84 * Data storage for Block Instances and their DynamicHTML components.
85 * This is temporarily stored on the ACF object, but this will be replaced later.
86 * Developers should not rely on reading or using any aspect of acf.blockInstances.
87 *
88 * @since ACF 6.3
89 */
90 acf.blockInstances = {};
91
92 /**
93 * Returns a block type for the given name.
94 *
95 * @date 20/2/19
96 * @since ACF 5.8.0
97 *
98 * @param string name The block name.
99 * @return (object|false)
100 */
101 function getBlockType(name) {
102 return blockTypes[name] || false;
103 }
104
105 /**
106 * Returns a block version for a given block name
107 *
108 * @date 8/6/22
109 * @since ACF 6.0
110 *
111 * @param string name The block name
112 * @return int
113 */
114 function getBlockVersion(name) {
115 const blockType = getBlockType(name);
116 return blockType.acf_block_version || 1;
117 }
118
119 /**
120 * Returns a block's validate property. Default true.
121 *
122 * @since ACF 6.3
123 *
124 * @param string name The block name
125 * @return boolean
126 */
127 function blockSupportsValidation(name) {
128 const blockType = getBlockType(name);
129 return blockType.validate;
130 }
131
132 /**
133 * Returns true if a block (identified by client ID) is nested in a query loop block.
134 *
135 * @date 17/1/22
136 * @since ACF 5.12
137 *
138 * @param {string} clientId A block client ID
139 * @return boolean
140 */
141 function isBlockInQueryLoop(clientId) {
142 const parents = wp.data.select('core/block-editor').getBlockParents(clientId);
143 const parentsData = wp.data.select('core/block-editor').getBlocksByClientId(parents);
144 return parentsData.filter(block => block.name === 'core/query').length;
145 }
146
147 /**
148 * Returns true if we're currently inside the WP 5.9+ site editor.
149 *
150 * @date 08/02/22
151 * @since ACF 5.12
152 *
153 * @return boolean
154 */
155 function isSiteEditor() {
156 return document.querySelectorAll('iframe[name="editor-canvas"]').length > 0;
157 }
158
159 /**
160 * Returns true if the block editor is currently showing the desktop device type preview.
161 *
162 * This function will always return true in the site editor as it uses the
163 * edit-post store rather than the edit-site store.
164 *
165 * @date 15/02/22
166 * @since ACF 5.12
167 *
168 * @return boolean
169 */
170 function isDesktopPreviewDeviceType() {
171 const editPostStore = select('core/edit-post');
172
173 // Return true if the edit post store isn't available (such as in the widget editor)
174 if (!editPostStore) return true;
175
176 // Check if function exists (experimental or not) and return true if it's Desktop, or doesn't exist.
177 if (editPostStore.__experimentalGetPreviewDeviceType) {
178 return 'Desktop' === editPostStore.__experimentalGetPreviewDeviceType();
179 } else if (editPostStore.getPreviewDeviceType) {
180 return 'Desktop' === editPostStore.getPreviewDeviceType();
181 } else {
182 return true;
183 }
184 }
185
186 /**
187 * Returns true if the block editor is currently in template edit mode.
188 *
189 * @date 16/02/22
190 * @since ACF 5.12
191 *
192 * @return boolean
193 */
194 function isEditingTemplate() {
195 const editPostStore = select('core/edit-post');
196
197 // Return false if the edit post store isn't available (such as in the widget editor)
198 if (!editPostStore) return false;
199
200 // Return false if the function doesn't exist
201 if (!editPostStore.isEditingTemplate) return false;
202 return editPostStore.isEditingTemplate();
203 }
204
205 /**
206 * Returns true if we're currently inside an iFramed non-desktop device preview type (WP5.9+)
207 *
208 * @date 15/02/22
209 * @since ACF 5.12
210 *
211 * @return boolean
212 */
213 function isiFramedMobileDevicePreview() {
214 return $('iframe[name=editor-canvas]').length && !isDesktopPreviewDeviceType();
215 }
216
217 /**
218 * Registers a block type.
219 *
220 * @date 19/2/19
221 * @since ACF 5.8.0
222 *
223 * @param object blockType The block type settings localized from PHP.
224 * @return object The result from wp.blocks.registerBlockType().
225 */
226 function registerBlockType(blockType) {
227 // Bail early if is excluded post_type.
228 const allowedTypes = blockType.post_types || [];
229 if (allowedTypes.length) {
230 // Always allow block to appear on "Edit reusable Block" screen.
231 allowedTypes.push('wp_block');
232
233 // Check post type.
234 const postType = acf.get('postType');
235 if (!allowedTypes.includes(postType)) {
236 return false;
237 }
238 }
239
240 // Handle svg HTML.
241 if (typeof blockType.icon === 'string' && blockType.icon.substr(0, 4) === '<svg') {
242 const iconHTML = blockType.icon;
243 blockType.icon = /*#__PURE__*/React.createElement(Div, null, iconHTML);
244 }
245
246 // Remove icon if empty to allow for default "block".
247 // Avoids JS error preventing block from being registered.
248 if (!blockType.icon) {
249 delete blockType.icon;
250 }
251
252 // Check category exists and fallback to "common".
253 const category = wp.blocks.getCategories().filter(({
254 slug
255 }) => slug === blockType.category).pop();
256 if (!category) {
257 //console.warn( `The block "${blockType.name}" is registered with an unknown category "${blockType.category}".` );
258 blockType.category = 'common';
259 }
260
261 // Merge in block settings before local additions.
262 blockType = acf.parseArgs(blockType, {
263 title: '',
264 name: '',
265 category: '',
266 api_version: 2,
267 acf_block_version: 1
268 });
269
270 // Remove all empty attribute defaults from PHP values to allow serialisation.
271 // https://github.com/WordPress/gutenberg/issues/7342
272 for (const key in blockType.attributes) {
273 if ('default' in blockType.attributes[key] && blockType.attributes[key].default.length === 0) {
274 delete blockType.attributes[key].default;
275 }
276 }
277
278 // Apply anchor supports to avoid block editor default writing to ID.
279 if (blockType.supports.anchor) {
280 blockType.attributes.anchor = {
281 type: 'string'
282 };
283 }
284
285 // Append edit and save functions.
286 let ThisBlockEdit = BlockEdit;
287 let ThisBlockSave = BlockSave;
288
289 // Apply alignText functionality.
290 if (blockType.supports.alignText || blockType.supports.align_text) {
291 blockType.attributes = addBackCompatAttribute(blockType.attributes, 'align_text', 'string');
292 ThisBlockEdit = withAlignTextComponent(ThisBlockEdit, blockType);
293 }
294
295 // Apply alignContent functionality.
296 if (blockType.supports.alignContent || blockType.supports.align_content) {
297 blockType.attributes = addBackCompatAttribute(blockType.attributes, 'align_content', 'string');
298 ThisBlockEdit = withAlignContentComponent(ThisBlockEdit, blockType);
299 }
300
301 // Apply fullHeight functionality.
302 if (blockType.supports.fullHeight || blockType.supports.full_height) {
303 blockType.attributes = addBackCompatAttribute(blockType.attributes, 'full_height', 'boolean');
304 ThisBlockEdit = withFullHeightComponent(ThisBlockEdit, blockType.blockType);
305 }
306
307 // Set edit and save functions.
308 blockType.edit = props => {
309 // Ensure we remove our save lock if a block is removed.
310 wp.element.useEffect(() => {
311 return () => {
312 if (!wp.data.dispatch('core/editor')) return;
313 wp.data.dispatch('core/editor').unlockPostSaving('acf/block/' + props.clientId);
314 };
315 }, []);
316 return /*#__PURE__*/React.createElement(ThisBlockEdit, props);
317 };
318 blockType.save = () => /*#__PURE__*/React.createElement(ThisBlockSave, null);
319
320 // Add to storage.
321 blockTypes[blockType.name] = blockType;
322
323 // Register with WP.
324 const result = wp.blocks.registerBlockType(blockType.name, blockType);
325
326 // Fix bug in 'core/anchor/attribute' filter overwriting attribute.
327 // Required for < WP5.9
328 // See https://github.com/WordPress/gutenberg/issues/15240
329 if (result.attributes.anchor) {
330 result.attributes.anchor = {
331 type: 'string'
332 };
333 }
334
335 // Return result.
336 return result;
337 }
338
339 /**
340 * Returns the wp.data.select() response with backwards compatibility.
341 *
342 * @date 17/06/2020
343 * @since ACF 5.9.0
344 *
345 * @param string selector The selector name.
346 * @return mixed
347 */
348 function select(selector) {
349 if (selector === 'core/block-editor') {
350 return wp.data.select('core/block-editor') || wp.data.select('core/editor');
351 }
352 return wp.data.select(selector);
353 }
354
355 /**
356 * Returns the wp.data.dispatch() response with backwards compatibility.
357 *
358 * @date 17/06/2020
359 * @since ACF 5.9.0
360 *
361 * @param string selector The selector name.
362 * @return mixed
363 */
364 function dispatch(selector) {
365 return wp.data.dispatch(selector);
366 }
367
368 /**
369 * Returns an array of all blocks for the given args.
370 *
371 * @date 27/2/19
372 * @since ACF 5.7.13
373 *
374 * @param {object} args An object of key=>value pairs used to filter results.
375 * @return array.
376 */
377 function getBlocks(args) {
378 let blocks = [];
379
380 // Local function to recurse through all child blocks and add to the blocks array.
381 const recurseBlocks = block => {
382 blocks.push(block);
383 select('core/block-editor').getBlocks(block.clientId).forEach(recurseBlocks);
384 };
385
386 // Trigger initial recursion for parent level blocks.
387 select('core/block-editor').getBlocks().forEach(recurseBlocks);
388
389 // Loop over args and filter.
390 for (const k in args) {
391 blocks = blocks.filter(({
392 attributes
393 }) => attributes[k] === args[k]);
394 }
395
396 // Return results.
397 return blocks;
398 }
399
400 /**
401 * Storage for the AJAX queue.
402 *
403 * @const {array}
404 */
405 const ajaxQueue = {};
406
407 /**
408 * Storage for cached AJAX requests for block content.
409 *
410 * @since ACF 5.12
411 * @const {array}
412 */
413 const fetchCache = {};
414
415 /**
416 * Fetches a JSON result from the AJAX API.
417 *
418 * @date 28/2/19
419 * @since ACF 5.7.13
420 *
421 * @param object block The block props.
422 * @query object The query args used in AJAX callback.
423 * @return object The AJAX promise.
424 */
425 function fetchBlock(args) {
426 const {
427 attributes = {},
428 context = {},
429 query = {},
430 clientId = null,
431 delay = 0
432 } = args;
433
434 // Build a unique queue ID from block data, including the clientId for edit forms.
435 const queueId = md5(JSON.stringify({
436 ...attributes,
437 ...context,
438 ...query
439 }));
440 const data = ajaxQueue[queueId] || {
441 query: {},
442 timeout: false,
443 promise: $.Deferred(),
444 started: false
445 };
446
447 // Append query args to storage.
448 data.query = {
449 ...data.query,
450 ...query
451 };
452 if (data.started) return data.promise;
453
454 // Set fresh timeout.
455 clearTimeout(data.timeout);
456 data.timeout = setTimeout(() => {
457 data.started = true;
458 if (fetchCache[queueId]) {
459 ajaxQueue[queueId] = null;
460 data.promise.resolve.apply(fetchCache[queueId][0], fetchCache[queueId][1]);
461 } else {
462 $.ajax({
463 url: acf.get('ajaxurl'),
464 dataType: 'json',
465 type: 'post',
466 cache: false,
467 data: acf.prepareForAjax({
468 action: 'acf/ajax/fetch-block',
469 block: JSON.stringify(attributes),
470 clientId: clientId,
471 context: JSON.stringify(context),
472 query: data.query
473 })
474 }).always(() => {
475 // Clean up queue after AJAX request is complete.
476 ajaxQueue[queueId] = null;
477 }).done(function () {
478 fetchCache[queueId] = [this, arguments];
479 data.promise.resolve.apply(this, arguments);
480 }).fail(function () {
481 data.promise.reject.apply(this, arguments);
482 });
483 }
484 }, delay);
485
486 // Update storage.
487 ajaxQueue[queueId] = data;
488
489 // Return promise.
490 return data.promise;
491 }
492
493 /**
494 * Returns true if both object are the same.
495 *
496 * @date 19/05/2020
497 * @since ACF 5.9.0
498 *
499 * @param object obj1
500 * @param object obj2
501 * @return bool
502 */
503 function compareObjects(obj1, obj2) {
504 return JSON.stringify(obj1) === JSON.stringify(obj2);
505 }
506
507 /**
508 * Converts HTML into a React element.
509 *
510 * @date 19/05/2020
511 * @since ACF 5.9.0
512 *
513 * @param string html The HTML to convert.
514 * @param int acfBlockVersion The ACF block version number.
515 * @return object Result of React.createElement().
516 */
517 acf.parseJSX = (html, acfBlockVersion) => {
518 // Apply a temporary wrapper for the jQuery parse to prevent text nodes triggering errors.
519 html = '<div>' + html + '</div>';
520 // Correctly balance InnerBlocks tags for jQuery's initial parse.
521 html = html.replace(/<InnerBlocks([^>]+)?\/>/, '<InnerBlocks$1></InnerBlocks>');
522 return parseNode($(html)[0], acfBlockVersion, 0).props.children;
523 };
524
525 /**
526 * Converts a DOM node into a React element.
527 *
528 * @date 19/05/2020
529 * @since ACF 5.9.0
530 *
531 * @param DOM node The DOM node.
532 * @param int acfBlockVersion The ACF block version number.
533 * @param int level The recursion level.
534 * @return object Result of React.createElement().
535 */
536 function parseNode(node, acfBlockVersion, level = 0) {
537 // Get node name.
538 const nodeName = parseNodeName(node.nodeName.toLowerCase(), acfBlockVersion);
539 if (!nodeName) {
540 return null;
541 }
542
543 // Get node attributes in React friendly format.
544 const nodeAttrs = {};
545 if (level === 1 && nodeName !== 'ACFInnerBlocks') {
546 // Top level (after stripping away the container div), create a ref for passing through to ACF's JS API.
547 nodeAttrs.ref = React.createRef();
548 }
549 acf.arrayArgs(node.attributes).map(parseNodeAttr).forEach(({
550 name,
551 value
552 }) => {
553 nodeAttrs[name] = value;
554 });
555 if ('ACFInnerBlocks' === nodeName) {
556 return /*#__PURE__*/React.createElement(ACFInnerBlocks, nodeAttrs);
557 }
558
559 // Define args for React.createElement().
560 const args = [nodeName, nodeAttrs];
561 acf.arrayArgs(node.childNodes).forEach(child => {
562 if (child instanceof Text) {
563 const text = child.textContent;
564 if (text) {
565 args.push(text);
566 }
567 } else {
568 args.push(parseNode(child, acfBlockVersion, level + 1));
569 }
570 });
571
572 // Return element.
573 return React.createElement.apply(this, args);
574 }
575
576 /**
577 * Converts a node or attribute name into it's JSX compliant name
578 *
579 * @date 05/07/2021
580 * @since ACF 5.9.8
581 *
582 * @param string name The node or attribute name.
583 * @return string
584 */
585 function getJSXName(name) {
586 const replacement = acf.isget(acf, 'jsxNameReplacements', name);
587 if (replacement) return replacement;
588 return name;
589 }
590
591 /**
592 * A react Component for inline scripts.
593 *
594 * This Component uses a combination of React references and jQuery to append the
595 * inline <script> HTML each time the component is rendered.
596 *
597 * @date 29/05/2020
598 * @since ACF 5.9.0
599 *
600 * @param type Var Description.
601 * @return type Description.
602 */
603 class Script extends Component {
604 render() {
605 return /*#__PURE__*/React.createElement("div", {
606 ref: el => this.el = el
607 });
608 }
609 setHTML(html) {
610 $(this.el).html(`<script>${html}</script>`);
611 }
612 componentDidUpdate() {
613 this.setHTML(this.props.children);
614 }
615 componentDidMount() {
616 this.setHTML(this.props.children);
617 }
618 }
619
620 /**
621 * Converts the given name into a React friendly name or component.
622 *
623 * @date 19/05/2020
624 * @since ACF 5.9.0
625 *
626 * @param string name The node name in lowercase.
627 * @param int acfBlockVersion The ACF block version number.
628 * @return mixed
629 */
630 function parseNodeName(name, acfBlockVersion) {
631 switch (name) {
632 case 'innerblocks':
633 if (acfBlockVersion < 2) {
634 return InnerBlocks;
635 }
636 return 'ACFInnerBlocks';
637 case 'script':
638 return Script;
639 case '#comment':
640 return null;
641 default:
642 // Replace names for JSX counterparts.
643 name = getJSXName(name);
644 }
645 return name;
646 }
647
648 /**
649 * Functional component for ACFInnerBlocks.
650 *
651 * @since ACF 6.0.0
652 *
653 * @param obj props element properties.
654 * @return DOM element
655 */
656 function ACFInnerBlocks(props) {
657 const {
658 className = 'acf-innerblocks-container'
659 } = props;
660 const innerBlockProps = useInnerBlocksProps({
661 className: className
662 }, props);
663 return /*#__PURE__*/React.createElement("div", innerBlockProps, innerBlockProps.children);
664 }
665
666 /**
667 * Converts the given attribute into a React friendly name and value object.
668 *
669 * @date 19/05/2020
670 * @since ACF 5.9.0
671 *
672 * @param obj nodeAttr The node attribute.
673 * @return obj
674 */
675 function parseNodeAttr(nodeAttr) {
676 let name = nodeAttr.name;
677 let value = nodeAttr.value;
678
679 // Allow overrides for third party libraries who might use specific attributes.
680 let shortcut = acf.applyFilters('acf_blocks_parse_node_attr', false, nodeAttr);
681 if (shortcut) return shortcut;
682 switch (name) {
683 // Class.
684 case 'class':
685 name = 'className';
686 break;
687
688 // Style.
689 case 'style':
690 const css = {};
691 value.split(';').forEach(s => {
692 const pos = s.indexOf(':');
693 if (pos > 0) {
694 let ruleName = s.substr(0, pos).trim();
695 const ruleValue = s.substr(pos + 1).trim();
696
697 // Rename core properties, but not CSS variables.
698 if (ruleName.charAt(0) !== '-') {
699 ruleName = acf.strCamelCase(ruleName);
700 }
701 css[ruleName] = ruleValue;
702 }
703 });
704 value = css;
705 break;
706
707 // Default.
708 default:
709 // No formatting needed for "data-x" attributes.
710 if (name.indexOf('data-') === 0) {
711 break;
712 }
713
714 // Replace names for JSX counterparts.
715 name = getJSXName(name);
716
717 // Convert JSON values. Ignore values that merely start with a
718 // JSON-like character but aren't valid JSON (e.g. an oEmbed
719 // title beginning with "[" or "{").
720 const c1 = value.charAt(0);
721 if (c1 === '[' || c1 === '{') {
722 try {
723 value = JSON.parse(value);
724 } catch (err) {}
725 }
726
727 // Convert bool values.
728 if (value === 'true' || value === 'false') {
729 value = value === 'true';
730 }
731 break;
732 }
733 return {
734 name,
735 value
736 };
737 }
738
739 /**
740 * Higher Order Component used to set default block attribute values.
741 *
742 * By modifying block attributes directly, instead of defining defaults in registerBlockType(),
743 * WordPress will include them always within the saved block serialized JSON.
744 *
745 * @date 31/07/2020
746 * @since ACF 5.9.0
747 *
748 * @param Component BlockListBlock The BlockListBlock Component.
749 * @return Component
750 */
751 const withDefaultAttributes = createHigherOrderComponent(BlockListBlock => class WrappedBlockEdit extends Component {
752 constructor(props) {
753 super(props);
754
755 // Extract vars.
756 const {
757 name,
758 attributes
759 } = this.props;
760
761 // Only run on ACF Blocks.
762 const blockType = getBlockType(name);
763 if (!blockType) {
764 return;
765 }
766
767 // Check and remove any empty string attributes to match PHP behaviour.
768 Object.keys(attributes).forEach(key => {
769 if (attributes[key] === '') {
770 delete attributes[key];
771 }
772 });
773
774 // Backward compatibility attribute replacement.
775 const upgrades = {
776 full_height: 'fullHeight',
777 align_content: 'alignContent',
778 align_text: 'alignText'
779 };
780 Object.keys(upgrades).forEach(key => {
781 if (attributes[key] !== undefined) {
782 attributes[upgrades[key]] = attributes[key];
783 } else if (attributes[upgrades[key]] === undefined) {
784 //Check for a default
785 if (blockType[key] !== undefined) {
786 attributes[upgrades[key]] = blockType[key];
787 }
788 }
789 delete blockType[key];
790 delete attributes[key];
791 });
792
793 // Set default attributes for those undefined.
794 for (let attribute in blockType.attributes) {
795 if (attributes[attribute] === undefined && blockType[attribute] !== undefined) {
796 attributes[attribute] = blockType[attribute];
797 }
798 }
799 }
800 render() {
801 return /*#__PURE__*/React.createElement(BlockListBlock, this.props);
802 }
803 }, 'withDefaultAttributes');
804 wp.hooks.addFilter('editor.BlockListBlock', 'acf/with-default-attributes', withDefaultAttributes);
805
806 /**
807 * The BlockSave functional component.
808 *
809 * @date 08/07/2020
810 * @since ACF 5.9.0
811 */
812 function BlockSave() {
813 return /*#__PURE__*/React.createElement(InnerBlocks.Content, null);
814 }
815
816 /**
817 * The BlockEdit component.
818 *
819 * @date 19/2/19
820 * @since ACF 5.7.12
821 */
822 class BlockEdit extends Component {
823 constructor(props) {
824 super(props);
825 this.setup();
826 }
827 setup() {
828 const {
829 name,
830 attributes,
831 clientId
832 } = this.props;
833 const blockType = getBlockType(name);
834
835 // Restrict current mode.
836 function restrictMode(modes) {
837 if (!modes.includes(attributes.mode)) {
838 attributes.mode = modes[0];
839 }
840 }
841 if (isBlockInQueryLoop(clientId) || isSiteEditor()) {
842 restrictMode(['preview']);
843 } else {
844 switch (blockType.mode) {
845 case 'edit':
846 restrictMode(['edit', 'preview']);
847 break;
848 case 'preview':
849 restrictMode(['preview', 'edit']);
850 break;
851 default:
852 restrictMode(['auto']);
853 break;
854 }
855 }
856 }
857 render() {
858 const {
859 name,
860 attributes,
861 setAttributes,
862 clientId
863 } = this.props;
864 const blockType = getBlockType(name);
865 const forcePreview = isBlockInQueryLoop(clientId) || isSiteEditor();
866 let {
867 mode
868 } = attributes;
869 if (forcePreview) {
870 mode = 'preview';
871 }
872
873 // Show toggle only for edit/preview modes and for blocks not in a query loop/FSE.
874 let showToggle = blockType.supports.mode;
875 if (mode === 'auto' || forcePreview) {
876 showToggle = false;
877 }
878
879 // Configure toggle variables.
880 const toggleText = mode === 'preview' ? acf.__('Switch to Edit') : acf.__('Switch to Preview');
881 const toggleIcon = mode === 'preview' ? 'edit' : 'welcome-view-site';
882 function toggleMode() {
883 setAttributes({
884 mode: mode === 'preview' ? 'edit' : 'preview'
885 });
886 }
887
888 // Return template.
889 return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(BlockControls, null, showToggle && /*#__PURE__*/React.createElement(ToolbarGroup, null, /*#__PURE__*/React.createElement(ToolbarButton, {
890 className: "components-icon-button components-toolbar__control",
891 label: toggleText,
892 icon: toggleIcon,
893 onClick: toggleMode
894 }))), /*#__PURE__*/React.createElement(InspectorControls, null, mode === 'preview' && /*#__PURE__*/React.createElement("div", {
895 className: "acf-block-component acf-block-panel"
896 }, /*#__PURE__*/React.createElement(BlockForm, this.props))), /*#__PURE__*/React.createElement(BlockBody, this.props));
897 }
898 }
899
900 /**
901 * The BlockBody functional component.
902 *
903 * @since ACF 5.7.12
904 */
905 function BlockBody(props) {
906 const {
907 attributes,
908 isSelected,
909 name,
910 clientId
911 } = props;
912 const {
913 mode
914 } = attributes;
915 const index = useSelect(select => {
916 const rootClientId = select('core/block-editor').getBlockRootClientId(clientId);
917 return select('core/block-editor').getBlockIndex(clientId, rootClientId);
918 });
919 let showForm = true;
920 let additionalClasses = 'acf-block-component acf-block-body';
921 if (mode === 'auto' && !isSelected || mode === 'preview') {
922 additionalClasses += ' acf-block-preview';
923 showForm = false;
924 }
925
926 // Setup block cache if required, and update mode.
927 if (!(clientId in acf.blockInstances)) {
928 acf.blockInstances[clientId] = {
929 validation_errors: false,
930 mode: mode
931 };
932 }
933 acf.blockInstances[clientId].mode = mode;
934 if (!isSelected) {
935 if (blockSupportsValidation(name) && acf.blockInstances[clientId].validation_errors) {
936 additionalClasses += ' acf-block-has-validation-error';
937 }
938 acf.blockInstances[clientId].has_been_deselected = true;
939 }
940 if (getBlockVersion(name) > 1) {
941 return /*#__PURE__*/React.createElement("div", useBlockProps({
942 className: additionalClasses
943 }), showForm ? /*#__PURE__*/React.createElement(BlockForm, _extends({}, props, {
944 index: index
945 })) : /*#__PURE__*/React.createElement(BlockPreview, _extends({}, props, {
946 index: index
947 })));
948 } else {
949 return /*#__PURE__*/React.createElement("div", useBlockProps(), /*#__PURE__*/React.createElement("div", {
950 className: "acf-block-component acf-block-body"
951 }, showForm ? /*#__PURE__*/React.createElement(BlockForm, _extends({}, props, {
952 index: index
953 })) : /*#__PURE__*/React.createElement(BlockPreview, _extends({}, props, {
954 index: index
955 }))));
956 }
957 }
958
959 /**
960 * A react component to append HTMl.
961 *
962 * @date 19/2/19
963 * @since ACF 5.7.12
964 *
965 * @param string children The html to insert.
966 * @return void
967 */
968 class Div extends Component {
969 render() {
970 return /*#__PURE__*/React.createElement("div", {
971 dangerouslySetInnerHTML: {
972 __html: this.props.children
973 }
974 });
975 }
976 }
977
978 /**
979 * DynamicHTML Class.
980 *
981 * A react componenet to load and insert dynamic HTML.
982 *
983 * @date 19/2/19
984 * @since ACF 5.7.12
985 *
986 * @param void
987 * @return void
988 */
989 class DynamicHTML extends Component {
990 constructor(props) {
991 super(props);
992
993 // Bind callbacks.
994 this.setRef = this.setRef.bind(this);
995
996 // Define default props and call setup().
997 this.id = '';
998 this.el = false;
999 this.subscribed = true;
1000 this.renderMethod = 'jQuery';
1001 this.passedValidation = false;
1002 this.setup(props);
1003
1004 // Load state.
1005 this.loadState();
1006 }
1007 setup(props) {
1008 const constructor = this.constructor.name;
1009 const clientId = props.clientId;
1010 if (!(clientId in acf.blockInstances)) {
1011 acf.blockInstances[clientId] = {
1012 validation_errors: false,
1013 mode: props.mode
1014 };
1015 }
1016 if (!(constructor in acf.blockInstances[clientId])) {
1017 acf.blockInstances[clientId][constructor] = {};
1018 }
1019 }
1020 fetch() {
1021 // Do nothing.
1022 }
1023 maybePreload(blockId, clientId, form) {
1024 acf.debug('Preload check', blockId, clientId, form);
1025
1026 // Early return if block is in query loop.
1027 if (isBlockInQueryLoop(this.props.clientId)) {
1028 acf.debug('Preload failed: Block is in query loop.');
1029 return false;
1030 }
1031 const preloadedBlocks = acf.get('preloadedBlocks');
1032
1033 // Early return if no preloaded blocks exist.
1034 if (!preloadedBlocks || !preloadedBlocks[blockId]) {
1035 acf.debug('Preload failed: Block not preloaded.');
1036 return false;
1037 }
1038
1039 // Create a copy to avoid mutating the original preloaded data.
1040 const preloadedBlock = {
1041 ...preloadedBlocks[blockId]
1042 };
1043
1044 // Ensure we only preload the correct block state (form or preview).
1045 if (form && !preloadedBlock.form || !form && preloadedBlock.form) {
1046 acf.debug('Preload failed: Correct state not preloaded.', form ? 'form' : 'preview');
1047 return false;
1048 }
1049
1050 // Replace blockId with clientId in HTML.
1051 preloadedBlock.html = preloadedBlock.html.replaceAll(blockId, clientId);
1052
1053 // Replace blockId in validation errors.
1054 if (preloadedBlock.validation && preloadedBlock.validation.errors) {
1055 preloadedBlock.validation.errors = preloadedBlock.validation.errors.map(error => {
1056 error.input = error.input.replaceAll(blockId, clientId);
1057 return error;
1058 });
1059 }
1060
1061 // Return preloaded object.
1062 acf.debug('Preload successful', preloadedBlock);
1063 return preloadedBlock;
1064 }
1065 loadState() {
1066 const client = acf.blockInstances[this.props.clientId] || {};
1067 this.state = client[this.constructor.name] || {};
1068 }
1069 setState(state) {
1070 acf.blockInstances[this.props.clientId][this.constructor.name] = {
1071 ...this.state,
1072 ...state
1073 };
1074
1075 // Update component state if subscribed.
1076 // - Allows AJAX callback to update store without modifying state of an unmounted component.
1077 if (this.subscribed || acf.get('StrictMode')) {
1078 super.setState(state);
1079 }
1080 acf.debug('SetState', Object.assign({}, this), this.props.clientId, this.constructor.name, Object.assign({}, acf.blockInstances[this.props.clientId][this.constructor.name]));
1081 }
1082 setHtml(html) {
1083 html = html ? html.trim() : '';
1084
1085 // Bail early if html has not changed.
1086 if (html === this.state.html) {
1087 return;
1088 }
1089
1090 // Update state.
1091 const state = {
1092 html
1093 };
1094 if (this.renderMethod === 'jsx') {
1095 state.jsx = acf.parseJSX(html, getBlockVersion(this.props.name));
1096
1097 // Handle templates which don't contain any valid JSX parsable elements.
1098 if (!state.jsx) {
1099 console.warn('Your ACF block template contains no valid HTML elements. Appending a empty div to prevent React JS errors.');
1100 state.html += '<div></div>';
1101 state.jsx = acf.parseJSX(state.html, getBlockVersion(this.props.name));
1102 }
1103
1104 // If we've got an object (as an array) find the first valid React ref.
1105 if (Array.isArray(state.jsx)) {
1106 let refElement = state.jsx.find(element => React.isValidElement(element));
1107 state.ref = refElement.ref;
1108 } else {
1109 state.ref = state.jsx.ref;
1110 }
1111 state.$el = $(this.el);
1112 } else {
1113 state.$el = $(html);
1114 }
1115 this.setState(state);
1116 }
1117 setRef(el) {
1118 this.el = el;
1119 }
1120 render() {
1121 // Render JSX.
1122 if (this.state.jsx) {
1123 // If we're a v2+ block, use the jsx element itself as our ref.
1124 if (getBlockVersion(this.props.name) > 1) {
1125 this.setRef(this.state.jsx);
1126 return this.state.jsx;
1127 } else {
1128 return /*#__PURE__*/React.createElement("div", {
1129 ref: this.setRef
1130 }, this.state.jsx);
1131 }
1132 }
1133
1134 // Return HTML.
1135 return /*#__PURE__*/React.createElement("div", {
1136 ref: this.setRef
1137 }, /*#__PURE__*/React.createElement(Placeholder, null, /*#__PURE__*/React.createElement(Spinner, null)));
1138 }
1139 shouldComponentUpdate({
1140 index
1141 }, {
1142 html
1143 }) {
1144 if (index !== this.props.index) {
1145 this.componentWillMove();
1146 }
1147 return html !== this.state.html;
1148 }
1149 display(context) {
1150 // This method is called after setting new HTML and the Component render.
1151 // The jQuery render method simply needs to move $el into place.
1152 if (this.renderMethod === 'jQuery') {
1153 const $el = this.state.$el;
1154 const $prevParent = $el.parent();
1155 const $thisParent = $(this.el);
1156
1157 // Move $el into place.
1158 // Skip this in StrictMode unless context is 'append' or component is subscribed.
1159 if (!(acf.get('StrictMode') && context !== 'append' && !this.subscribed)) {
1160 $thisParent.html($el);
1161 }
1162
1163 // Special case for reusable blocks.
1164 // Multiple instances of the same reusable block share the same block id.
1165 // This causes all instances to share the same state (cool), which unfortunately
1166 // pulls $el back and forth between the last rendered reusable block.
1167 // This simple fix leaves a "clone" behind :)
1168 if ($prevParent.length && $prevParent[0] !== $thisParent[0]) {
1169 $prevParent.html($el.clone());
1170 }
1171 }
1172
1173 // Lock block if required.
1174 if (this.getValidationErrors() && this.isNotNewlyAdded()) {
1175 this.lockBlockForSaving();
1176 } else {
1177 this.unlockBlockForSaving();
1178 }
1179
1180 // Call context specific method.
1181 switch (context) {
1182 case 'append':
1183 this.componentDidAppend();
1184 break;
1185 case 'remount':
1186 this.componentDidRemount();
1187 break;
1188 }
1189 }
1190 validate() {
1191 // Do nothing.
1192 }
1193 componentDidMount() {
1194 // Fetch on first load.
1195 if (this.state.html === undefined) {
1196 this.fetch();
1197
1198 // Or remount existing HTML.
1199 } else {
1200 this.display('remount');
1201 }
1202 }
1203 componentDidUpdate(prevProps, prevState) {
1204 // HTML has changed.
1205 this.display('append');
1206 }
1207 componentDidAppend() {
1208 acf.doAction('append', this.state.$el);
1209 }
1210 componentWillUnmount() {
1211 // Only skip unmount action if in StrictMode AND component is not subscribed
1212 if (!acf.get('StrictMode') || this.subscribed) {
1213 acf.doAction('unmount', this.state.$el);
1214 }
1215
1216 // Unsubscribe this component from state
1217 this.subscribed = false;
1218 }
1219 componentDidRemount() {
1220 this.subscribed = true;
1221
1222 // Use setTimeout to avoid incorrect timing of events.
1223 // React will unmount and mount components in DOM order.
1224 // This means a new component can be mounted before an old one is unmounted.
1225 // ACF shares $el across new/old components which is un-React-like.
1226 // This timout ensures that unmounting occurs before remounting.
1227 setTimeout(() => {
1228 acf.doAction('remount', this.state.$el);
1229 });
1230 }
1231 componentWillMove() {
1232 acf.doAction('unmount', this.state.$el);
1233 setTimeout(() => {
1234 acf.doAction('remount', this.state.$el);
1235 });
1236 }
1237 isNotNewlyAdded() {
1238 return acf.blockInstances[this.props.clientId].has_been_deselected || false;
1239 }
1240 hasShownValidation() {
1241 return acf.blockInstances[this.props.clientId].shown_validation || false;
1242 }
1243 setShownValidation() {
1244 acf.blockInstances[this.props.clientId].shown_validation = true;
1245 }
1246 setValidationErrors(errors) {
1247 acf.blockInstances[this.props.clientId].validation_errors = errors;
1248 }
1249 getValidationErrors() {
1250 return acf.blockInstances[this.props.clientId].validation_errors;
1251 }
1252 getMode() {
1253 return acf.blockInstances[this.props.clientId].mode;
1254 }
1255 lockBlockForSaving() {
1256 if (!wp.data.dispatch('core/editor')) return;
1257 wp.data.dispatch('core/editor').lockPostSaving('acf/block/' + this.props.clientId);
1258 }
1259 unlockBlockForSaving() {
1260 if (!wp.data.dispatch('core/editor')) return;
1261 wp.data.dispatch('core/editor').unlockPostSaving('acf/block/' + this.props.clientId);
1262 }
1263 displayValidation($formEl) {
1264 if (!blockSupportsValidation(this.props.name)) {
1265 acf.debug('Block does not support validation');
1266 return;
1267 }
1268 if (!$formEl || $formEl.hasClass('acf-empty-block-fields')) {
1269 acf.debug('There is no edit form available to validate.');
1270 return;
1271 }
1272 const errors = this.getValidationErrors();
1273 acf.debug('Starting handle validation', Object.assign({}, this), Object.assign({}, $formEl), errors);
1274 this.setShownValidation();
1275 let validator = acf.getBlockFormValidator($formEl);
1276 validator.clearErrors();
1277 acf.doAction('blocks/validation/pre_apply', errors);
1278 if (errors) {
1279 validator.addErrors(errors);
1280 validator.showErrors('after');
1281 this.lockBlockForSaving();
1282 } else {
1283 // remove previous error message
1284 if (validator.has('notice')) {
1285 validator.get('notice').update({
1286 type: 'success',
1287 text: acf.__('Validation successful'),
1288 timeout: 1000
1289 });
1290 validator.set('notice', null);
1291 }
1292 this.unlockBlockForSaving();
1293 }
1294 acf.doAction('blocks/validation/post_apply', errors);
1295 }
1296 }
1297
1298 /**
1299 * BlockForm Class.
1300 *
1301 * A react componenet to handle the block form.
1302 *
1303 * @date 19/2/19
1304 * @since ACF 5.7.12
1305 *
1306 * @param string id the block id.
1307 * @return void
1308 */
1309 class BlockForm extends DynamicHTML {
1310 setup(props) {
1311 this.id = `BlockForm-${props.clientId}`;
1312 super.setup(props);
1313 }
1314 fetch(validate_only = false, data = false) {
1315 // Extract props.
1316 const {
1317 context,
1318 clientId,
1319 name
1320 } = this.props;
1321 let {
1322 attributes
1323 } = this.props;
1324 let query = {
1325 form: true
1326 };
1327 if (validate_only) {
1328 query = {
1329 validate: true
1330 };
1331 attributes.data = data;
1332 }
1333 const hash = createBlockAttributesHash(attributes, context);
1334 acf.debug('BlockForm fetch', attributes, query);
1335
1336 // Try preloaded data first.
1337 const preloaded = this.maybePreload(hash, clientId, true);
1338 if (preloaded) {
1339 this.setHtml(acf.applyFilters('blocks/form/render', preloaded.html, true));
1340 if (preloaded.validation) this.setValidationErrors(preloaded.validation.errors);
1341 return;
1342 }
1343 if (!blockSupportsValidation(name)) {
1344 query.validate = false;
1345 }
1346
1347 // Request AJAX and update HTML on complete.
1348 fetchBlock({
1349 attributes,
1350 context,
1351 clientId,
1352 query
1353 }).done(({
1354 data
1355 }) => {
1356 acf.debug('fetch block form promise');
1357 if (!data) {
1358 this.setHtml(`<div class="acf-block-fields acf-fields acf-empty-block-fields">${acf.__('Error loading block form')}</div>`);
1359 return;
1360 }
1361 if (data.form) {
1362 this.setHtml(acf.applyFilters('blocks/form/render', data.form.replaceAll(data.clientId, clientId), false));
1363 }
1364 if (data.validation) this.setValidationErrors(data.validation.errors);
1365 if (this.isNotNewlyAdded()) {
1366 acf.debug("Block has already shown it's invalid. The form needs to show validation errors");
1367 this.validate();
1368 }
1369 });
1370 }
1371 validate(loadState = true) {
1372 if (loadState) {
1373 this.loadState();
1374 }
1375 acf.debug('BlockForm calling validate with state', Object.assign({}, this));
1376 super.displayValidation(this.state.$el);
1377 }
1378 shouldComponentUpdate(nextProps, nextState) {
1379 if (blockSupportsValidation(this.props.name) && this.state.$el && this.isNotNewlyAdded() && !this.hasShownValidation()) {
1380 this.validate(false); // Shouldn't update state in shouldComponentUpdate.
1381 }
1382 return super.shouldComponentUpdate(nextProps, nextState);
1383 }
1384 componentWillUnmount() {
1385 super.componentWillUnmount();
1386
1387 //TODO: either delete this, or clear validations here (if that's a sensible idea)
1388
1389 acf.debug('BlockForm Component did unmount');
1390 }
1391 componentDidRemount() {
1392 super.componentDidRemount();
1393 acf.debug('BlockForm component did remount');
1394 const {
1395 $el
1396 } = this.state;
1397 if (blockSupportsValidation(this.props.name) && this.isNotNewlyAdded()) {
1398 acf.debug("Block has already shown it's invalid. The form needs to show validation errors");
1399 this.validate();
1400 }
1401
1402 // Make sure our on append events are registered.
1403 if ($el.data('acf-events-added') !== true) {
1404 this.componentDidAppend();
1405 }
1406 }
1407 componentDidAppend() {
1408 super.componentDidAppend();
1409 acf.debug('BlockForm component did append');
1410
1411 // Extract props.
1412 const {
1413 attributes,
1414 setAttributes,
1415 clientId,
1416 name
1417 } = this.props;
1418 const thisBlockForm = this;
1419 const {
1420 $el
1421 } = this.state;
1422
1423 // Callback for updating block data and validation status if we're in an edit only mode.
1424 function serializeData(silent = false) {
1425 const data = acf.serialize($el, `acf-block_${clientId}`);
1426 if (silent) {
1427 attributes.data = data;
1428 } else {
1429 setAttributes({
1430 data
1431 });
1432 }
1433 if (blockSupportsValidation(name) && !silent && thisBlockForm.getMode() === 'edit') {
1434 acf.debug('No block preview currently available. Need to trigger a validation only fetch.');
1435 thisBlockForm.fetch(true, data);
1436 }
1437 }
1438
1439 // Add events.
1440 let timeout = false;
1441 $el.on('change keyup', () => {
1442 clearTimeout(timeout);
1443 timeout = setTimeout(serializeData, 300);
1444 });
1445
1446 // Log initialization for remount check on the persistent element.
1447 $el.data('acf-events-added', true);
1448
1449 // Ensure newly added block is saved with data.
1450 // Do it silently to avoid triggering a preview render.
1451 if (!attributes.data) {
1452 serializeData(true);
1453 }
1454 }
1455 }
1456
1457 /**
1458 * BlockPreview Class.
1459 *
1460 * A react componenet to handle the block preview.
1461 *
1462 * @date 19/2/19
1463 * @since ACF 5.7.12
1464 *
1465 * @param string id the block id.
1466 * @return void
1467 */
1468 class BlockPreview extends DynamicHTML {
1469 setup(props) {
1470 const blockType = getBlockType(props.name);
1471 const contextPostId = acf.isget(this.props, 'context', 'postId');
1472 this.id = `BlockPreview-${props.clientId}`;
1473 super.setup(props);
1474
1475 // Apply the contextPostId to the ID if set to stop query loop ID duplication.
1476 if (contextPostId) {
1477 this.id = `BlockPreview-${props.clientId}-${contextPostId}`;
1478 }
1479 if (blockType.supports.jsx) {
1480 this.renderMethod = 'jsx';
1481 }
1482 }
1483 fetch(args = {}) {
1484 const {
1485 attributes = this.props.attributes,
1486 clientId = this.props.clientId,
1487 context = this.props.context,
1488 delay = 0
1489 } = args;
1490 const {
1491 name
1492 } = this.props;
1493
1494 // Remember attributes used to fetch HTML.
1495 this.setState({
1496 prevAttributes: attributes,
1497 prevContext: context
1498 });
1499 const hash = createBlockAttributesHash(attributes, context);
1500
1501 // Try preloaded data first.
1502 let preloaded = this.maybePreload(hash, clientId, false);
1503 if (preloaded) {
1504 if (getBlockVersion(name) == 1) {
1505 preloaded.html = '<div class="acf-block-preview">' + preloaded.html + '</div>';
1506 }
1507 this.setHtml(acf.applyFilters('blocks/preview/render', preloaded.html, true));
1508 if (preloaded.validation) this.setValidationErrors(preloaded.validation.errors);
1509 return;
1510 }
1511 let query = {
1512 preview: true
1513 };
1514 if (!blockSupportsValidation(name)) {
1515 query.validate = false;
1516 }
1517
1518 // Request AJAX and update HTML on complete.
1519 fetchBlock({
1520 attributes,
1521 context,
1522 clientId,
1523 query,
1524 delay
1525 }).done(({
1526 data
1527 }) => {
1528 if (!data) {
1529 this.setHtml(`<div class="acf-block-fields acf-fields acf-empty-block-fields">${acf.__('Error previewing block')}</div>`);
1530 return;
1531 }
1532 let replaceHtml = data.preview.replaceAll(data.clientId, clientId);
1533 if (getBlockVersion(name) == 1) {
1534 replaceHtml = '<div class="acf-block-preview">' + replaceHtml + '</div>';
1535 }
1536 acf.debug('fetch block render promise');
1537 this.setHtml(acf.applyFilters('blocks/preview/render', replaceHtml, false));
1538 if (data.validation) {
1539 this.setValidationErrors(data.validation.errors);
1540 }
1541 if (this.isNotNewlyAdded()) {
1542 this.validate();
1543 }
1544 });
1545 }
1546 validate() {
1547 // Check we've got a block form for this instance.
1548 const client = acf.blockInstances[this.props.clientId] || {};
1549 const blockFormState = client.BlockForm || false;
1550 if (blockFormState) {
1551 super.displayValidation(blockFormState.$el);
1552 }
1553 }
1554 componentDidAppend() {
1555 super.componentDidAppend();
1556 this.renderBlockPreviewEvent();
1557 }
1558 shouldComponentUpdate(nextProps, nextState) {
1559 const nextAttributes = nextProps.attributes;
1560 const thisAttributes = this.props.attributes;
1561
1562 // Update preview if block data has changed.
1563 if (!compareObjects(nextAttributes, thisAttributes) || !compareObjects(nextProps.context, this.props.context)) {
1564 let delay = 0;
1565
1566 // Delay fetch when editing className or anchor to simulate consistent logic to custom fields.
1567 if (nextAttributes.className !== thisAttributes.className) {
1568 delay = 300;
1569 }
1570 if (nextAttributes.anchor !== thisAttributes.anchor) {
1571 delay = 300;
1572 }
1573 acf.debug('Triggering fetch from block preview shouldComponentUpdate');
1574 this.fetch({
1575 attributes: nextAttributes,
1576 context: nextProps.context,
1577 delay
1578 });
1579 }
1580 return super.shouldComponentUpdate(nextProps, nextState);
1581 }
1582 renderBlockPreviewEvent() {
1583 // Extract props.
1584 const {
1585 attributes,
1586 name
1587 } = this.props;
1588 const {
1589 $el,
1590 ref
1591 } = this.state;
1592 var blockElement;
1593
1594 // Generate action friendly type.
1595 const type = attributes.name.replace('acf/', '');
1596 if (ref && ref.current) {
1597 // We've got a react ref from a JSX container. Use the parent as the blockElement
1598 blockElement = $(ref.current).parent();
1599 } else if (getBlockVersion(name) == 1) {
1600 blockElement = $el;
1601 } else {
1602 blockElement = $el.parents('.acf-block-preview');
1603 }
1604
1605 // Do action.
1606 acf.doAction('render_block_preview', blockElement, attributes);
1607 acf.doAction(`render_block_preview/type=${type}`, blockElement, attributes);
1608 }
1609 componentDidRemount() {
1610 super.componentDidRemount();
1611 acf.debug('Checking if fetch is required in BlockPreview componentDidRemount', Object.assign({}, this.state.prevAttributes), Object.assign({}, this.props.attributes), Object.assign({}, this.state.prevContext), Object.assign({}, this.props.context));
1612
1613 // Update preview if data has changed since last render (changing from "edit" to "preview").
1614 if (!compareObjects(this.state.prevAttributes, this.props.attributes) || !compareObjects(this.state.prevContext, this.props.context)) {
1615 acf.debug('Triggering block preview fetch from componentDidRemount');
1616 this.fetch();
1617 }
1618
1619 // Fire the block preview event so blocks can reinit JS elements.
1620 // React reusing DOM elements covers any potential race condition from the above fetch.
1621 this.renderBlockPreviewEvent();
1622 }
1623 }
1624
1625 /**
1626 * Initializes ACF Blocks logic and registration.
1627 *
1628 * @since ACF 5.9.0
1629 */
1630 function initialize() {
1631 // Add support for WordPress versions before 5.2.
1632 if (!wp.blockEditor) {
1633 wp.blockEditor = wp.editor;
1634 }
1635
1636 // Register block types.
1637 const blockTypes = acf.get('blockTypes');
1638 if (blockTypes) {
1639 // Only register blocks with version <= 2 (v3+ blocks are registered separately).
1640 blockTypes.forEach(blockType => {
1641 parseInt(blockType.acf_block_version) <= 2 && registerBlockType(blockType);
1642 });
1643 }
1644 }
1645
1646 // Run the initialize callback during the "prepare" action.
1647 // This ensures that all localized data is available and that blocks are registered before the WP editor has been instantiated.
1648 acf.addAction('prepare', initialize);
1649
1650 /**
1651 * Returns a valid vertical alignment.
1652 *
1653 * @date 07/08/2020
1654 * @since ACF 5.9.0
1655 *
1656 * @param string align A vertical alignment.
1657 * @return string
1658 */
1659 function validateVerticalAlignment(align) {
1660 const ALIGNMENTS = ['top', 'center', 'bottom'];
1661 const DEFAULT = 'top';
1662 return ALIGNMENTS.includes(align) ? align : DEFAULT;
1663 }
1664
1665 /**
1666 * Returns a valid horizontal alignment.
1667 *
1668 * @date 07/08/2020
1669 * @since ACF 5.9.0
1670 *
1671 * @param string align A horizontal alignment.
1672 * @return string
1673 */
1674 function validateHorizontalAlignment(align) {
1675 const ALIGNMENTS = ['left', 'center', 'right'];
1676 const DEFAULT = acf.get('rtl') ? 'right' : 'left';
1677 return ALIGNMENTS.includes(align) ? align : DEFAULT;
1678 }
1679
1680 /**
1681 * Returns a valid matrix alignment.
1682 *
1683 * Written for "upgrade-path" compatibility from vertical alignment to matrix alignment.
1684 *
1685 * @date 07/08/2020
1686 * @since ACF 5.9.0
1687 *
1688 * @param string align A matrix alignment.
1689 * @return string
1690 */
1691 function validateMatrixAlignment(align) {
1692 const DEFAULT = 'center center';
1693 if (align) {
1694 const [y, x] = align.split(' ');
1695 return `${validateVerticalAlignment(y)} ${validateHorizontalAlignment(x)}`;
1696 }
1697 return DEFAULT;
1698 }
1699
1700 /**
1701 * A higher order component adding alignContent editing functionality.
1702 *
1703 * @date 08/07/2020
1704 * @since ACF 5.9.0
1705 *
1706 * @param component OriginalBlockEdit The original BlockEdit component.
1707 * @param object blockType The block type settings.
1708 * @return component
1709 */
1710 function withAlignContentComponent(OriginalBlockEdit, blockType) {
1711 // Determine alignment vars
1712 let type = blockType.supports.align_content || blockType.supports.alignContent;
1713 let AlignmentComponent;
1714 let validateAlignment;
1715 switch (type) {
1716 case 'matrix':
1717 AlignmentComponent = BlockAlignmentMatrixControl || BlockAlignmentMatrixToolbar;
1718 validateAlignment = validateMatrixAlignment;
1719 break;
1720 default:
1721 AlignmentComponent = BlockVerticalAlignmentToolbar;
1722 validateAlignment = validateVerticalAlignment;
1723 break;
1724 }
1725
1726 // Ensure alignment component exists.
1727 if (AlignmentComponent === undefined) {
1728 console.warn(`The "${type}" alignment component was not found.`);
1729 return OriginalBlockEdit;
1730 }
1731
1732 // Ensure correct block attribute data is sent in intial preview AJAX request.
1733 blockType.alignContent = validateAlignment(blockType.alignContent);
1734
1735 // Return wrapped component.
1736 return class WrappedBlockEdit extends Component {
1737 render() {
1738 const {
1739 attributes,
1740 setAttributes
1741 } = this.props;
1742 const {
1743 alignContent
1744 } = attributes;
1745 function onChangeAlignContent(alignContent) {
1746 setAttributes({
1747 alignContent: validateAlignment(alignContent)
1748 });
1749 }
1750 return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(BlockControls, {
1751 group: "block"
1752 }, /*#__PURE__*/React.createElement(AlignmentComponent, {
1753 label: acf.__('Change content alignment'),
1754 value: validateAlignment(alignContent),
1755 onChange: onChangeAlignContent
1756 })), /*#__PURE__*/React.createElement(OriginalBlockEdit, this.props));
1757 }
1758 };
1759 }
1760
1761 /**
1762 * A higher order component adding alignText editing functionality.
1763 *
1764 * @date 08/07/2020
1765 * @since ACF 5.9.0
1766 *
1767 * @param component OriginalBlockEdit The original BlockEdit component.
1768 * @param object blockType The block type settings.
1769 * @return component
1770 */
1771 function withAlignTextComponent(OriginalBlockEdit, blockType) {
1772 const validateAlignment = validateHorizontalAlignment;
1773
1774 // Ensure correct block attribute data is sent in intial preview AJAX request.
1775 blockType.alignText = validateAlignment(blockType.alignText);
1776
1777 // Return wrapped component.
1778 return class WrappedBlockEdit extends Component {
1779 render() {
1780 const {
1781 attributes,
1782 setAttributes
1783 } = this.props;
1784 const {
1785 alignText
1786 } = attributes;
1787 function onChangeAlignText(alignText) {
1788 setAttributes({
1789 alignText: validateAlignment(alignText)
1790 });
1791 }
1792 return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(BlockControls, {
1793 group: "block"
1794 }, /*#__PURE__*/React.createElement(AlignmentToolbar, {
1795 value: validateAlignment(alignText),
1796 onChange: onChangeAlignText
1797 })), /*#__PURE__*/React.createElement(OriginalBlockEdit, this.props));
1798 }
1799 };
1800 }
1801
1802 /**
1803 * A higher order component adding full height support.
1804 *
1805 * @date 19/07/2021
1806 * @since ACF 5.10.0
1807 *
1808 * @param component OriginalBlockEdit The original BlockEdit component.
1809 * @param object blockType The block type settings.
1810 * @return component
1811 */
1812 function withFullHeightComponent(OriginalBlockEdit, blockType) {
1813 if (!BlockFullHeightAlignmentControl) return OriginalBlockEdit;
1814
1815 // Return wrapped component.
1816 return class WrappedBlockEdit extends Component {
1817 render() {
1818 const {
1819 attributes,
1820 setAttributes
1821 } = this.props;
1822 const {
1823 fullHeight
1824 } = attributes;
1825 function onToggleFullHeight(fullHeight) {
1826 setAttributes({
1827 fullHeight
1828 });
1829 }
1830 return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(BlockControls, {
1831 group: "block"
1832 }, /*#__PURE__*/React.createElement(BlockFullHeightAlignmentControl, {
1833 isActive: fullHeight,
1834 onToggle: onToggleFullHeight
1835 })), /*#__PURE__*/React.createElement(OriginalBlockEdit, this.props));
1836 }
1837 };
1838 }
1839
1840 /**
1841 * Appends a backwards compatibility attribute for conversion.
1842 *
1843 * @since ACF 6.0
1844 *
1845 * @param object attributes The block type attributes.
1846 * @return object
1847 */
1848 function addBackCompatAttribute(attributes, new_attribute, type) {
1849 attributes[new_attribute] = {
1850 type: type
1851 };
1852 return attributes;
1853 }
1854
1855 /**
1856 * Create a block hash from attributes
1857 *
1858 * @since ACF 6.0
1859 *
1860 * @param object attributes The block type attributes.
1861 * @param object context The current block context object.
1862 * @return string
1863 */
1864 function createBlockAttributesHash(attributes, context) {
1865 attributes['_acf_context'] = sortObjectByKey(context);
1866 return md5(JSON.stringify(sortObjectByKey(attributes)));
1867 }
1868
1869 /**
1870 * Key sort an object
1871 *
1872 * @since ACF 6.3.1
1873 *
1874 * @param object toSort The object to be sorted
1875 * @return object
1876 */
1877 function sortObjectByKey(toSort) {
1878 return Object.keys(toSort).sort().reduce((acc, currValue) => {
1879 acc[currValue] = toSort[currValue];
1880 return acc;
1881 }, {});
1882 }
1883 })(jQuery);
1884
1885 /***/ }),
1886
1887 /***/ "./assets/src/js/pro/_acf-jsx-names.js":
1888 /*!*********************************************!*\
1889 !*** ./assets/src/js/pro/_acf-jsx-names.js ***!
1890 \*********************************************/
1891 /***/ (() => {
1892
1893 (function ($, undefined) {
1894 acf.jsxNameReplacements = {
1895 'accent-height': 'accentHeight',
1896 accentheight: 'accentHeight',
1897 'accept-charset': 'acceptCharset',
1898 acceptcharset: 'acceptCharset',
1899 accesskey: 'accessKey',
1900 'alignment-baseline': 'alignmentBaseline',
1901 alignmentbaseline: 'alignmentBaseline',
1902 allowedblocks: 'allowedBlocks',
1903 allowfullscreen: 'allowFullScreen',
1904 allowreorder: 'allowReorder',
1905 'arabic-form': 'arabicForm',
1906 arabicform: 'arabicForm',
1907 attributename: 'attributeName',
1908 attributetype: 'attributeType',
1909 autocapitalize: 'autoCapitalize',
1910 autocomplete: 'autoComplete',
1911 autocorrect: 'autoCorrect',
1912 autofocus: 'autoFocus',
1913 autoplay: 'autoPlay',
1914 autoreverse: 'autoReverse',
1915 autosave: 'autoSave',
1916 basefrequency: 'baseFrequency',
1917 'baseline-shift': 'baselineShift',
1918 baselineshift: 'baselineShift',
1919 baseprofile: 'baseProfile',
1920 calcmode: 'calcMode',
1921 'cap-height': 'capHeight',
1922 capheight: 'capHeight',
1923 cellpadding: 'cellPadding',
1924 cellspacing: 'cellSpacing',
1925 charset: 'charSet',
1926 class: 'className',
1927 classid: 'classID',
1928 classname: 'className',
1929 'clip-path': 'clipPath',
1930 'clip-rule': 'clipRule',
1931 clippath: 'clipPath',
1932 clippathunits: 'clipPathUnits',
1933 cliprule: 'clipRule',
1934 'color-interpolation': 'colorInterpolation',
1935 'color-interpolation-filters': 'colorInterpolationFilters',
1936 'color-profile': 'colorProfile',
1937 'color-rendering': 'colorRendering',
1938 colorinterpolation: 'colorInterpolation',
1939 colorinterpolationfilters: 'colorInterpolationFilters',
1940 colorprofile: 'colorProfile',
1941 colorrendering: 'colorRendering',
1942 colspan: 'colSpan',
1943 contenteditable: 'contentEditable',
1944 contentscripttype: 'contentScriptType',
1945 contentstyletype: 'contentStyleType',
1946 contextmenu: 'contextMenu',
1947 controlslist: 'controlsList',
1948 crossorigin: 'crossOrigin',
1949 dangerouslysetinnerhtml: 'dangerouslySetInnerHTML',
1950 datetime: 'dateTime',
1951 defaultchecked: 'defaultChecked',
1952 defaultvalue: 'defaultValue',
1953 diffuseconstant: 'diffuseConstant',
1954 disablepictureinpicture: 'disablePictureInPicture',
1955 disableremoteplayback: 'disableRemotePlayback',
1956 'dominant-baseline': 'dominantBaseline',
1957 dominantbaseline: 'dominantBaseline',
1958 edgemode: 'edgeMode',
1959 'enable-background': 'enableBackground',
1960 enablebackground: 'enableBackground',
1961 enctype: 'encType',
1962 enterkeyhint: 'enterKeyHint',
1963 externalresourcesrequired: 'externalResourcesRequired',
1964 'fill-opacity': 'fillOpacity',
1965 'fill-rule': 'fillRule',
1966 fillopacity: 'fillOpacity',
1967 fillrule: 'fillRule',
1968 filterres: 'filterRes',
1969 filterunits: 'filterUnits',
1970 'flood-color': 'floodColor',
1971 'flood-opacity': 'floodOpacity',
1972 floodcolor: 'floodColor',
1973 floodopacity: 'floodOpacity',
1974 'font-family': 'fontFamily',
1975 'font-size': 'fontSize',
1976 'font-size-adjust': 'fontSizeAdjust',
1977 'font-stretch': 'fontStretch',
1978 'font-style': 'fontStyle',
1979 'font-variant': 'fontVariant',
1980 'font-weight': 'fontWeight',
1981 fontfamily: 'fontFamily',
1982 fontsize: 'fontSize',
1983 fontsizeadjust: 'fontSizeAdjust',
1984 fontstretch: 'fontStretch',
1985 fontstyle: 'fontStyle',
1986 fontvariant: 'fontVariant',
1987 fontweight: 'fontWeight',
1988 for: 'htmlFor',
1989 foreignobject: 'foreignObject',
1990 formaction: 'formAction',
1991 formenctype: 'formEncType',
1992 formmethod: 'formMethod',
1993 formnovalidate: 'formNoValidate',
1994 formtarget: 'formTarget',
1995 frameborder: 'frameBorder',
1996 'glyph-name': 'glyphName',
1997 'glyph-orientation-horizontal': 'glyphOrientationHorizontal',
1998 'glyph-orientation-vertical': 'glyphOrientationVertical',
1999 glyphname: 'glyphName',
2000 glyphorientationhorizontal: 'glyphOrientationHorizontal',
2001 glyphorientationvertical: 'glyphOrientationVertical',
2002 glyphref: 'glyphRef',
2003 gradienttransform: 'gradientTransform',
2004 gradientunits: 'gradientUnits',
2005 'horiz-adv-x': 'horizAdvX',
2006 'horiz-origin-x': 'horizOriginX',
2007 horizadvx: 'horizAdvX',
2008 horizoriginx: 'horizOriginX',
2009 hreflang: 'hrefLang',
2010 htmlfor: 'htmlFor',
2011 'http-equiv': 'httpEquiv',
2012 httpequiv: 'httpEquiv',
2013 'image-rendering': 'imageRendering',
2014 imagerendering: 'imageRendering',
2015 innerhtml: 'innerHTML',
2016 inputmode: 'inputMode',
2017 itemid: 'itemID',
2018 itemprop: 'itemProp',
2019 itemref: 'itemRef',
2020 itemscope: 'itemScope',
2021 itemtype: 'itemType',
2022 kernelmatrix: 'kernelMatrix',
2023 kernelunitlength: 'kernelUnitLength',
2024 keyparams: 'keyParams',
2025 keypoints: 'keyPoints',
2026 keysplines: 'keySplines',
2027 keytimes: 'keyTimes',
2028 keytype: 'keyType',
2029 lengthadjust: 'lengthAdjust',
2030 'letter-spacing': 'letterSpacing',
2031 letterspacing: 'letterSpacing',
2032 'lighting-color': 'lightingColor',
2033 lightingcolor: 'lightingColor',
2034 limitingconeangle: 'limitingConeAngle',
2035 marginheight: 'marginHeight',
2036 marginwidth: 'marginWidth',
2037 'marker-end': 'markerEnd',
2038 'marker-mid': 'markerMid',
2039 'marker-start': 'markerStart',
2040 markerend: 'markerEnd',
2041 markerheight: 'markerHeight',
2042 markermid: 'markerMid',
2043 markerstart: 'markerStart',
2044 markerunits: 'markerUnits',
2045 markerwidth: 'markerWidth',
2046 maskcontentunits: 'maskContentUnits',
2047 maskunits: 'maskUnits',
2048 maxlength: 'maxLength',
2049 mediagroup: 'mediaGroup',
2050 minlength: 'minLength',
2051 nomodule: 'noModule',
2052 novalidate: 'noValidate',
2053 numoctaves: 'numOctaves',
2054 'overline-position': 'overlinePosition',
2055 'overline-thickness': 'overlineThickness',
2056 overlineposition: 'overlinePosition',
2057 overlinethickness: 'overlineThickness',
2058 'paint-order': 'paintOrder',
2059 paintorder: 'paintOrder',
2060 'panose-1': 'panose1',
2061 pathlength: 'pathLength',
2062 patterncontentunits: 'patternContentUnits',
2063 patterntransform: 'patternTransform',
2064 patternunits: 'patternUnits',
2065 playsinline: 'playsInline',
2066 'pointer-events': 'pointerEvents',
2067 pointerevents: 'pointerEvents',
2068 pointsatx: 'pointsAtX',
2069 pointsaty: 'pointsAtY',
2070 pointsatz: 'pointsAtZ',
2071 preservealpha: 'preserveAlpha',
2072 preserveaspectratio: 'preserveAspectRatio',
2073 primitiveunits: 'primitiveUnits',
2074 radiogroup: 'radioGroup',
2075 readonly: 'readOnly',
2076 referrerpolicy: 'referrerPolicy',
2077 refx: 'refX',
2078 refy: 'refY',
2079 'rendering-intent': 'renderingIntent',
2080 renderingintent: 'renderingIntent',
2081 repeatcount: 'repeatCount',
2082 repeatdur: 'repeatDur',
2083 requiredextensions: 'requiredExtensions',
2084 requiredfeatures: 'requiredFeatures',
2085 rowspan: 'rowSpan',
2086 'shape-rendering': 'shapeRendering',
2087 shaperendering: 'shapeRendering',
2088 specularconstant: 'specularConstant',
2089 specularexponent: 'specularExponent',
2090 spellcheck: 'spellCheck',
2091 spreadmethod: 'spreadMethod',
2092 srcdoc: 'srcDoc',
2093 srclang: 'srcLang',
2094 srcset: 'srcSet',
2095 startoffset: 'startOffset',
2096 stddeviation: 'stdDeviation',
2097 stitchtiles: 'stitchTiles',
2098 'stop-color': 'stopColor',
2099 'stop-opacity': 'stopOpacity',
2100 stopcolor: 'stopColor',
2101 stopopacity: 'stopOpacity',
2102 'strikethrough-position': 'strikethroughPosition',
2103 'strikethrough-thickness': 'strikethroughThickness',
2104 strikethroughposition: 'strikethroughPosition',
2105 strikethroughthickness: 'strikethroughThickness',
2106 'stroke-dasharray': 'strokeDasharray',
2107 'stroke-dashoffset': 'strokeDashoffset',
2108 'stroke-linecap': 'strokeLinecap',
2109 'stroke-linejoin': 'strokeLinejoin',
2110 'stroke-miterlimit': 'strokeMiterlimit',
2111 'stroke-opacity': 'strokeOpacity',
2112 'stroke-width': 'strokeWidth',
2113 strokedasharray: 'strokeDasharray',
2114 strokedashoffset: 'strokeDashoffset',
2115 strokelinecap: 'strokeLinecap',
2116 strokelinejoin: 'strokeLinejoin',
2117 strokemiterlimit: 'strokeMiterlimit',
2118 strokeopacity: 'strokeOpacity',
2119 strokewidth: 'strokeWidth',
2120 suppresscontenteditablewarning: 'suppressContentEditableWarning',
2121 suppresshydrationwarning: 'suppressHydrationWarning',
2122 surfacescale: 'surfaceScale',
2123 systemlanguage: 'systemLanguage',
2124 tabindex: 'tabIndex',
2125 tablevalues: 'tableValues',
2126 targetx: 'targetX',
2127 targety: 'targetY',
2128 templatelock: 'templateLock',
2129 'text-anchor': 'textAnchor',
2130 'text-decoration': 'textDecoration',
2131 'text-rendering': 'textRendering',
2132 textanchor: 'textAnchor',
2133 textdecoration: 'textDecoration',
2134 textlength: 'textLength',
2135 textrendering: 'textRendering',
2136 'underline-position': 'underlinePosition',
2137 'underline-thickness': 'underlineThickness',
2138 underlineposition: 'underlinePosition',
2139 underlinethickness: 'underlineThickness',
2140 'unicode-bidi': 'unicodeBidi',
2141 'unicode-range': 'unicodeRange',
2142 unicodebidi: 'unicodeBidi',
2143 unicoderange: 'unicodeRange',
2144 'units-per-em': 'unitsPerEm',
2145 unitsperem: 'unitsPerEm',
2146 usemap: 'useMap',
2147 'v-alphabetic': 'vAlphabetic',
2148 'v-hanging': 'vHanging',
2149 'v-ideographic': 'vIdeographic',
2150 'v-mathematical': 'vMathematical',
2151 valphabetic: 'vAlphabetic',
2152 'vector-effect': 'vectorEffect',
2153 vectoreffect: 'vectorEffect',
2154 'vert-adv-y': 'vertAdvY',
2155 'vert-origin-x': 'vertOriginX',
2156 'vert-origin-y': 'vertOriginY',
2157 vertadvy: 'vertAdvY',
2158 vertoriginx: 'vertOriginX',
2159 vertoriginy: 'vertOriginY',
2160 vhanging: 'vHanging',
2161 videographic: 'vIdeographic',
2162 viewbox: 'viewBox',
2163 viewtarget: 'viewTarget',
2164 vmathematical: 'vMathematical',
2165 'word-spacing': 'wordSpacing',
2166 wordspacing: 'wordSpacing',
2167 'writing-mode': 'writingMode',
2168 writingmode: 'writingMode',
2169 'x-height': 'xHeight',
2170 xchannelselector: 'xChannelSelector',
2171 xheight: 'xHeight',
2172 'xlink:actuate': 'xlinkActuate',
2173 'xlink:arcrole': 'xlinkArcrole',
2174 'xlink:href': 'xlinkHref',
2175 'xlink:role': 'xlinkRole',
2176 'xlink:show': 'xlinkShow',
2177 'xlink:title': 'xlinkTitle',
2178 'xlink:type': 'xlinkType',
2179 xlinkactuate: 'xlinkActuate',
2180 xlinkarcrole: 'xlinkArcrole',
2181 xlinkhref: 'xlinkHref',
2182 xlinkrole: 'xlinkRole',
2183 xlinkshow: 'xlinkShow',
2184 xlinktitle: 'xlinkTitle',
2185 xlinktype: 'xlinkType',
2186 'xml:base': 'xmlBase',
2187 'xml:lang': 'xmlLang',
2188 'xml:space': 'xmlSpace',
2189 xmlbase: 'xmlBase',
2190 xmllang: 'xmlLang',
2191 'xmlns:xlink': 'xmlnsXlink',
2192 xmlnsxlink: 'xmlnsXlink',
2193 xmlspace: 'xmlSpace',
2194 ychannelselector: 'yChannelSelector',
2195 zoomandpan: 'zoomAndPan'
2196 };
2197 })(jQuery);
2198
2199 /***/ }),
2200
2201 /***/ "./assets/src/js/pro/blocks-v3/components/block-edit.js":
2202 /*!**************************************************************!*\
2203 !*** ./assets/src/js/pro/blocks-v3/components/block-edit.js ***!
2204 \**************************************************************/
2205 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
2206
2207 "use strict";
2208 __webpack_require__.r(__webpack_exports__);
2209 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
2210 /* harmony export */ BlockEdit: () => (/* binding */ BlockEdit)
2211 /* harmony export */ });
2212 /* harmony import */ var md5__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! md5 */ "./node_modules/md5/md5.js");
2213 /* harmony import */ var md5__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(md5__WEBPACK_IMPORTED_MODULE_0__);
2214 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
2215 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__);
2216 /* harmony import */ var _wordpress_block_editor__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/block-editor */ "@wordpress/block-editor");
2217 /* harmony import */ var _wordpress_block_editor__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_block_editor__WEBPACK_IMPORTED_MODULE_2__);
2218 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components");
2219 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_3__);
2220 /* harmony import */ var _block_placeholder__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./block-placeholder */ "./assets/src/js/pro/blocks-v3/components/block-placeholder.js");
2221 /* harmony import */ var _block_form__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./block-form */ "./assets/src/js/pro/blocks-v3/components/block-form.js");
2222 /* harmony import */ var _block_preview__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./block-preview */ "./assets/src/js/pro/blocks-v3/components/block-preview.js");
2223 /* harmony import */ var _error_boundary__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./error-boundary */ "./assets/src/js/pro/blocks-v3/components/error-boundary.js");
2224 /* harmony import */ var _block_toolbar_fields__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./block-toolbar-fields */ "./assets/src/js/pro/blocks-v3/components/block-toolbar-fields.js");
2225 /* harmony import */ var _inline_editing_toolbar__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./inline-editing-toolbar */ "./assets/src/js/pro/blocks-v3/components/inline-editing-toolbar.js");
2226 /* harmony import */ var _popover_wrapper__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./popover-wrapper */ "./assets/src/js/pro/blocks-v3/components/popover-wrapper.js");
2227 /* harmony import */ var _utils_post_locking__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../utils/post-locking */ "./assets/src/js/pro/blocks-v3/utils/post-locking.js");
2228 function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
2229 /**
2230 * BlockEdit Component
2231 * Main component for editing ACF blocks in the Gutenberg editor
2232 * Handles form fetching, validation, preview rendering, and user interactions
2233 */
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247 /**
2248 * InspectorBlockFormContainer
2249 * Small helper component that manages the inspector panel container ref
2250 * Sets the current form container when the inspector panel is available
2251 *
2252 * @param {Object} props
2253 * @param {React.RefObject} props.inspectorBlockFormRef - Ref to inspector container
2254 * @param {Function} props.setCurrentBlockFormContainer - Setter for current container
2255 */
2256 const InspectorBlockFormContainer = ({
2257 inspectorBlockFormRef,
2258 setCurrentBlockFormContainer
2259 }) => {
2260 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2261 setCurrentBlockFormContainer(inspectorBlockFormRef.current);
2262 }, []);
2263 return /*#__PURE__*/React.createElement("div", {
2264 ref: inspectorBlockFormRef
2265 });
2266 };
2267 const isBlockEditorInspectorSidebarOpen = () => {
2268 const interfaceStore = wp.data.select('core/interface');
2269 return typeof interfaceStore?.getActiveComplementaryArea === 'function' && interfaceStore.getActiveComplementaryArea('core') === 'edit-post/block';
2270 };
2271 const useBlockEditorInspectorSidebarOpen = () => {
2272 const [isOpen, setIsOpen] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(() => isBlockEditorInspectorSidebarOpen());
2273 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2274 if (typeof wp.data.subscribe !== 'function') {
2275 return;
2276 }
2277 const unsubscribe = wp.data.subscribe(() => {
2278 setIsOpen(isBlockEditorInspectorSidebarOpen());
2279 });
2280 return () => {
2281 unsubscribe();
2282 };
2283 }, []);
2284 return isOpen;
2285 };
2286
2287 /**
2288 * Converts serialized Google Map field values from JSON strings back into
2289 * objects so they aren't double-encoded when the block's data is stored as
2290 * a JSON block attribute. Google Map fields keep their value as a JSON
2291 * string in a hidden input, which acf.serialize() would otherwise pass
2292 * through as a string.
2293 *
2294 * @param {Object} $form The block form jQuery element that was serialized.
2295 * @param {Object} serializedData The data returned by acf.serialize(); modified in place.
2296 * @param {string} clientId The block client ID.
2297 * @return {void}
2298 */
2299 function deserializeGoogleMapValues($form, serializedData, clientId) {
2300 const inputPrefix = `acf-block_${clientId}`;
2301 $form.find('.acf-field-google-map input[type="hidden"][name]').each(function () {
2302 if (this.name.indexOf(inputPrefix) !== 0) {
2303 return;
2304 }
2305
2306 // Extract the bracketed key path, e.g. "[field_abc][field_def]" -> [ 'field_abc', 'field_def' ].
2307 const keyPath = this.name.slice(inputPrefix.length).match(/([^\[\]])+/g);
2308 if (!keyPath) {
2309 return;
2310 }
2311
2312 // Walk the serialized data down to the parent of the leaf key.
2313 let parent = serializedData;
2314 for (let i = 0; i < keyPath.length - 1; i++) {
2315 if (parent === null || typeof parent !== 'object') {
2316 return;
2317 }
2318 parent = parent[keyPath[i]];
2319 }
2320 if (parent === null || typeof parent !== 'object') {
2321 return;
2322 }
2323 const leafKey = keyPath[keyPath.length - 1];
2324 if (typeof parent[leafKey] === 'string') {
2325 try {
2326 const parsedValue = JSON.parse(parent[leafKey]);
2327 if (typeof parsedValue === 'object' && parsedValue !== null) {
2328 parent[leafKey] = parsedValue;
2329 }
2330 } catch (err) {}
2331 }
2332 });
2333 }
2334
2335 /**
2336 * Main BlockEdit component wrapper
2337 * Manages block data fetching and initial setup
2338 *
2339 * @param {Object} props - Component props
2340 * @param {Object} props.attributes - Block attributes
2341 * @param {Function} props.setAttributes - Function to update block attributes
2342 * @param {Object} props.context - Block context
2343 * @param {boolean} props.isSelected - Whether block is currently selected
2344 * @param {jQuery} props.$ - jQuery instance
2345 * @param {Object} props.blockType - ACF block type configuration
2346 * @returns {JSX.Element} - Rendered block editor
2347 */
2348 const BlockEdit = props => {
2349 const {
2350 attributes,
2351 setAttributes,
2352 context,
2353 isSelected,
2354 $,
2355 blockType
2356 } = props;
2357 const shouldValidate = blockType.validate;
2358 const {
2359 clientId
2360 } = (0,_wordpress_block_editor__WEBPACK_IMPORTED_MODULE_2__.useBlockEditContext)();
2361 const preloadedData = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useMemo)(() => {
2362 return checkPreloadedData(generateAttributesHash(attributes, context), clientId, isSelected);
2363 }, []);
2364 const [validationErrors, setValidationErrors] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(() => {
2365 return preloadedData?.validation?.errors ?? null;
2366 });
2367 const [showValidationErrors, setShowValidationErrors] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(null);
2368 const [theSerializedAcfData, setTheSerializedAcfData] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(null);
2369 const [blockFormHtml, setBlockFormHtml] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(() => preloadedData?.form ?? '');
2370 const [blockPreviewHtml, setBlockPreviewHtml] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(() => {
2371 if (preloadedData?.html) {
2372 return acf.applyFilters('blocks/preview/render', preloadedData.html, true);
2373 }
2374 return 'acf-block-preview-loading';
2375 });
2376 const [userHasInteractedWithForm, setUserHasInteractedWithForm] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
2377 const [hasFetchedOnce, setHasFetchedOnce] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
2378 const [ajaxRequest, setAjaxRequest] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)();
2379 const [isFetchingBlock, setIsFetchingBlock] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
2380
2381 // New state for inline editing features
2382 const [blockToolbarFields, setBlockToolbarFields] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
2383 const [blockFieldInfo, setBlockFieldInfo] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(() => preloadedData?.fields ?? null);
2384 const [gutenbergIframeOrDocument, setGutenbergIframeOrDocument] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(() => {
2385 const iframe = document.querySelector('[name="editor-canvas"]');
2386 return iframe ? iframe.contentDocument || iframe.contentWindow.document : document;
2387 });
2388 const [currentInlineEditingElement, setCurrentInlineEditingElement] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(null);
2389 const [currentInlineEditingElementUid, setCurrentInlineEditingElementUid] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(null);
2390 const [currentContentEditableElement, setCurrentContentEditableElement] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(null);
2391 const [inlineEditingToolbarHasFocus, setInlineEditingToolbarHasFocus] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
2392 const [contentEditableChangeInProgress, setContentEditableChangeInProgress] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
2393 const [acfDynamicStylesElement, setAcfDynamicStylesElement] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(null);
2394 const [freezeInlineToolbarDuringReRender, setFreezeInlineToolbarDuringReRender] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)('');
2395 const blockEditorInspectorSidebarOpen = useBlockEditorInspectorSidebarOpen();
2396 const acfFormRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
2397 const previewRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
2398 const debounceRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
2399
2400 // Initialize acf.blockEdit namespace for jsx-parser to use
2401 if (!acf.blockEdit) {
2402 acf.blockEdit = {};
2403 }
2404 acf.blockEdit.setCurrentInlineEditingElementUid = setCurrentInlineEditingElementUid;
2405 acf.blockEdit.setCurrentInlineEditingElement = setCurrentInlineEditingElement;
2406 acf.blockEdit.setCurrentContentEditableElement = setCurrentContentEditableElement;
2407 acf.blockEdit.getBlockFieldInfo = () => blockFieldInfo;
2408 const attributesWithoutError = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useMemo)(() => {
2409 const {
2410 hasAcfError,
2411 ...rest
2412 } = attributes;
2413 return rest;
2414 }, [attributes]);
2415
2416 /**
2417 * Fetches block data from server (form HTML, preview HTML, validation)
2418 *
2419 * @param {Object} params - Fetch parameters
2420 * @param {Object} params.theAttributes - Block attributes to fetch for
2421 * @param {string} params.theClientId - Block client ID
2422 * @param {Object} params.theContext - Block context
2423 * @param {boolean} params.isSelected - Whether block is selected
2424 */
2425 function fetchBlockData({
2426 theAttributes,
2427 theClientId,
2428 theContext,
2429 isSelected
2430 }) {
2431 if (!theAttributes) return;
2432
2433 // NEW: Abort any pending request
2434 if (ajaxRequest) {
2435 ajaxRequest.abort();
2436 }
2437
2438 // Generate hash of attributes for preload cache lookup
2439 const attributesHash = generateAttributesHash(theAttributes, context);
2440
2441 // Check for preloaded block data
2442 const preloadedData = checkPreloadedData(attributesHash, theClientId, isSelected);
2443 if (preloadedData) {
2444 handlePreloadedData(preloadedData);
2445 (0,_utils_post_locking__WEBPACK_IMPORTED_MODULE_11__.unlockPostSavingByName)('acf-fetching-block');
2446 setIsFetchingBlock(false);
2447 return;
2448 }
2449
2450 // Prepare query options
2451 const queryOptions = {
2452 preview: true,
2453 form: true,
2454 validate: true
2455 };
2456 if (!blockFormHtml) {
2457 queryOptions.validate = false;
2458 }
2459 if (!shouldValidate) {
2460 queryOptions.validate = false;
2461 }
2462 const blockData = {
2463 ...theAttributes
2464 };
2465 (0,_utils_post_locking__WEBPACK_IMPORTED_MODULE_11__.lockPostSavingByName)('acf-fetching-block');
2466 setIsFetchingBlock(true);
2467
2468 // Fetch block data via AJAX
2469 const request = $.ajax({
2470 url: acf.get('ajaxurl'),
2471 dataType: 'json',
2472 type: 'post',
2473 cache: false,
2474 data: acf.prepareForAjax({
2475 action: 'acf/ajax/fetch-block',
2476 block: JSON.stringify(blockData),
2477 clientId: theClientId,
2478 context: JSON.stringify(theContext),
2479 query: queryOptions
2480 })
2481 }).done(response => {
2482 (0,_utils_post_locking__WEBPACK_IMPORTED_MODULE_11__.unlockPostSavingByName)('acf-fetching-block');
2483 setIsFetchingBlock(false);
2484 setBlockFormHtml(response.data.form);
2485
2486 // Handle new field metadata for inline editing
2487 if (response.data.fields) {
2488 setBlockFieldInfo(response.data.fields);
2489 }
2490
2491 // Handle block toolbar fields configuration
2492 if (response.data.blockToolbarFields) {
2493 setBlockToolbarFields(response.data.blockToolbarFields);
2494 }
2495 if (response.data.preview) {
2496 setBlockPreviewHtml(acf.applyFilters('blocks/preview/render', response.data.preview, false));
2497 } else {
2498 setBlockPreviewHtml(acf.applyFilters('blocks/preview/render', 'acf-block-preview-no-html', false));
2499 }
2500 if (response.data?.validation && !response.data.validation.valid && response.data.validation.errors) {
2501 setValidationErrors(response.data.validation.errors);
2502 } else {
2503 setValidationErrors(null);
2504 }
2505 setHasFetchedOnce(true);
2506 }).fail(function () {
2507 setHasFetchedOnce(true);
2508 (0,_utils_post_locking__WEBPACK_IMPORTED_MODULE_11__.unlockPostSavingByName)('acf-fetching-block');
2509 setIsFetchingBlock(false);
2510 });
2511 setAjaxRequest(request);
2512 }
2513
2514 /**
2515 * Generates a hash of block attributes for caching
2516 *
2517 * @param {Object} attrs - Block attributes
2518 * @param {Object} ctx - Block context
2519 * @returns {string} - MD5 hash of serialized attributes
2520 */
2521 function generateAttributesHash(attrs, ctx) {
2522 delete attrs.hasAcfError;
2523 attrs._acf_context = (0,_utils_post_locking__WEBPACK_IMPORTED_MODULE_11__.sortObjectKeys)(ctx);
2524 return md5__WEBPACK_IMPORTED_MODULE_0___default()(JSON.stringify((0,_utils_post_locking__WEBPACK_IMPORTED_MODULE_11__.sortObjectKeys)(attrs)));
2525 }
2526
2527 /**
2528 * Checks if block data was preloaded and returns it
2529 *
2530 * @param {string} hash - Attributes hash
2531 * @param {string} clientId - Block client ID
2532 * @param {boolean} selected - Whether block is selected
2533 * @returns {Object|boolean} - Preloaded data or false
2534 */
2535 function checkPreloadedData(hash, clientId, selected) {
2536 if (selected) return false;
2537 acf.debug('Preload check', hash, clientId);
2538
2539 // Don't preload blocks inside Query Loop blocks
2540 if (isInQueryLoop(clientId)) {
2541 return false;
2542 }
2543 const data = getPreloadedBlockData(hash, clientId, acf.get('preloadedBlocks'));
2544 if (!data) {
2545 acf.debug('Preload failed: not preloaded.');
2546 return false;
2547 }
2548 acf.debug('Preload successful', data);
2549 return data;
2550 }
2551
2552 /**
2553 * Returns a copy of a preloaded block entry with the placeholder hash
2554 * replaced by the actual client ID.
2555 *
2556 * Works on a deep clone so the shared preloaded entry is never mutated —
2557 * duplicating a block with identical attributes reuses the same hash, and
2558 * an in-place replacement would corrupt the entry for the duplicate.
2559 *
2560 * @param {string} hash - Attributes hash
2561 * @param {string} blockClientId - Block client ID
2562 * @param {Object} preloadedBlocks - The preloaded blocks store
2563 * @return {Object|boolean} - Preloaded data or false
2564 */
2565 function getPreloadedBlockData(hash, blockClientId, preloadedBlocks) {
2566 if (!preloadedBlocks || !preloadedBlocks[hash]) {
2567 return false;
2568 }
2569 const data = JSON.parse(JSON.stringify(preloadedBlocks[hash]));
2570
2571 // Replace placeholder client ID with actual client ID
2572 data.html = data.html.replaceAll(hash, blockClientId);
2573 data.form = data.form.replaceAll(hash, blockClientId);
2574 if (data?.validation?.errors) {
2575 data.validation.errors = data.validation.errors.map(error => {
2576 error.input = error.input.replaceAll(hash, blockClientId);
2577 return error;
2578 });
2579 }
2580 return data;
2581 }
2582
2583 /**
2584 * Checks if block is inside a Query Loop block
2585 *
2586 * @param {string} clientId - Block client ID
2587 * @returns {boolean} - True if inside Query Loop
2588 */
2589 function isInQueryLoop(clientId) {
2590 const parentIds = wp.data.select('core/block-editor').getBlockParents(clientId);
2591 return wp.data.select('core/block-editor').getBlocksByClientId(parentIds).filter(block => block.name === 'core/query').length > 0;
2592 }
2593
2594 /**
2595 * Handles preloaded block data
2596 *
2597 * @param {Object} data - Preloaded data
2598 */
2599 function handlePreloadedData(data) {
2600 if (data.form) {
2601 setBlockFormHtml(data.form);
2602 }
2603 if (data.html) {
2604 setBlockPreviewHtml(acf.applyFilters('blocks/preview/render', data.html, true));
2605 } else {
2606 setBlockPreviewHtml(acf.applyFilters('blocks/preview/render', 'acf-block-preview-no-html', true));
2607 }
2608
2609 // Handle block toolbar fields from preloaded data
2610 if (data?.blockToolbarFields) {
2611 setBlockToolbarFields(data.blockToolbarFields);
2612 }
2613
2614 // Handle field info from preloaded data
2615 if (data?.fields) {
2616 setBlockFieldInfo(data.fields);
2617 }
2618 if (data?.validation && !data.validation.valid && data.validation.errors) {
2619 setValidationErrors(data.validation.errors);
2620 } else {
2621 setValidationErrors(null);
2622 }
2623 }
2624
2625 // Initial fetch on mount and when selection changes
2626 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2627 function trackUserInteraction() {
2628 setUserHasInteractedWithForm(true);
2629 window.removeEventListener('click', trackUserInteraction);
2630 window.removeEventListener('keydown', trackUserInteraction);
2631 }
2632 window.addEventListener('click', trackUserInteraction);
2633 window.addEventListener('keydown', trackUserInteraction);
2634 return () => {
2635 window.removeEventListener('click', trackUserInteraction);
2636 window.removeEventListener('keydown', trackUserInteraction);
2637 };
2638 }, []);
2639 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2640 if (isSelected) {
2641 return;
2642 }
2643 setCurrentInlineEditingElementUid(null);
2644 setCurrentInlineEditingElement(null);
2645 setCurrentContentEditableElement(null);
2646 }, [isSelected]);
2647
2648 // Update hasAcfError attribute based on validation errors
2649 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2650 setAttributes(validationErrors ? {
2651 hasAcfError: true
2652 } : {
2653 hasAcfError: false
2654 });
2655 }, [validationErrors, setAttributes]);
2656
2657 // Listen for validation error events from other blocks
2658 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2659 const handleErrorEvent = event => {
2660 // Only handle if this event is for this specific block
2661 if (clientId === event.detail.acfBlocksWithValidationErrors) {
2662 (0,_utils_post_locking__WEBPACK_IMPORTED_MODULE_11__.lockPostSaving)(clientId);
2663 setShowValidationErrors(true);
2664 setCurrentInlineEditingElementUid(null);
2665 }
2666 };
2667 document.addEventListener('acf/block/has-error', handleErrorEvent);
2668 return () => {
2669 document.removeEventListener('acf/block/has-error', handleErrorEvent);
2670 };
2671 }, []);
2672
2673 // Cleanup: unlock post saving on unmount
2674 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => () => {
2675 (0,_utils_post_locking__WEBPACK_IMPORTED_MODULE_11__.unlockPostSaving)(props.clientId);
2676 }, []);
2677
2678 // Handle form data changes with debouncing
2679 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2680 clearTimeout(debounceRef.current);
2681 (0,_utils_post_locking__WEBPACK_IMPORTED_MODULE_11__.lockPostSavingByName)('acf-fetching-block');
2682 setIsFetchingBlock(true);
2683 debounceRef.current = setTimeout(() => {
2684 const parsedData = JSON.parse(theSerializedAcfData);
2685 if (!parsedData) {
2686 return void fetchBlockData({
2687 theAttributes: attributesWithoutError,
2688 theClientId: clientId,
2689 theContext: context,
2690 isSelected: isSelected
2691 });
2692 }
2693 if (theSerializedAcfData === JSON.stringify(attributesWithoutError.data)) {
2694 return void fetchBlockData({
2695 theAttributes: attributesWithoutError,
2696 theClientId: clientId,
2697 theContext: context,
2698 isSelected: isSelected
2699 });
2700 }
2701
2702 // Use original attributes (with hasAcfError) when updating
2703 const updatedAttributes = {
2704 ...attributes,
2705 data: {
2706 ...parsedData
2707 }
2708 };
2709 setAttributes(updatedAttributes);
2710 }, 200);
2711
2712 // Cleanup function to unlock post saving
2713 return () => {
2714 clearTimeout(debounceRef.current);
2715 (0,_utils_post_locking__WEBPACK_IMPORTED_MODULE_11__.unlockPostSavingByName)('acf-fetching-block');
2716 setIsFetchingBlock(false);
2717 };
2718 }, [theSerializedAcfData, attributesWithoutError]);
2719
2720 // Trigger ACF actions when preview is rendered
2721 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2722 if (previewRef.current && blockPreviewHtml) {
2723 const blockName = attributes.name.replace('acf/', '');
2724 const $preview = $(previewRef.current);
2725 acf.doAction('render_block_preview', $preview, attributes);
2726 acf.doAction(`render_block_preview/type=${blockName}`, $preview, attributes);
2727
2728 // If there's an active inline editing element, re-initialize it after preview renders
2729 if (currentInlineEditingElementUid) {
2730 const inlineElement = previewRef?.current.querySelector(`[data-acf-inline-fields-uid="${currentInlineEditingElementUid}"]`);
2731 setCurrentInlineEditingElement(inlineElement);
2732 }
2733 }
2734 }, [blockPreviewHtml]);
2735 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2736 if (currentContentEditableElement && !inlineEditingToolbarHasFocus || !currentInlineEditingElement) {
2737 return;
2738 }
2739 const toolbar = document.querySelector('.acf-inline-editing-toolbar');
2740 if (toolbar?.style?.cssText) {
2741 setFreezeInlineToolbarDuringReRender(toolbar.style.cssText.replaceAll(';', '!important;'));
2742 }
2743 setTimeout(() => {
2744 setCurrentInlineEditingElement(currentInlineEditingElement);
2745 setTimeout(() => {
2746 setFreezeInlineToolbarDuringReRender(null);
2747 }, 0);
2748 }, 0);
2749 }, [currentInlineEditingElement]);
2750 return /*#__PURE__*/React.createElement(BlockEditInner, _extends({}, props, {
2751 validationErrors: validationErrors,
2752 showValidationErrors: showValidationErrors,
2753 theSerializedAcfData: theSerializedAcfData,
2754 setTheSerializedAcfData: setTheSerializedAcfData,
2755 acfFormRef: acfFormRef,
2756 blockFormHtml: blockFormHtml,
2757 blockPreviewHtml: blockPreviewHtml,
2758 blockFetcher: fetchBlockData,
2759 userHasInteractedWithForm: userHasInteractedWithForm,
2760 setUserHasInteractedWithForm: setUserHasInteractedWithForm,
2761 previewRef: previewRef,
2762 hasFetchedOnce: hasFetchedOnce,
2763 blockToolbarFields: blockToolbarFields,
2764 blockFieldInfo: blockFieldInfo,
2765 gutenbergIframeOrDocument: gutenbergIframeOrDocument,
2766 setGutenbergIframeOrDocument: setGutenbergIframeOrDocument,
2767 currentInlineEditingElement: currentInlineEditingElement,
2768 setCurrentInlineEditingElement: setCurrentInlineEditingElement,
2769 currentInlineEditingElementUid: currentInlineEditingElementUid,
2770 setCurrentInlineEditingElementUid: setCurrentInlineEditingElementUid,
2771 currentContentEditableElement: currentContentEditableElement,
2772 setCurrentContentEditableElement: setCurrentContentEditableElement,
2773 inlineEditingToolbarHasFocus: inlineEditingToolbarHasFocus,
2774 setInlineEditingToolbarHasFocus: setInlineEditingToolbarHasFocus,
2775 contentEditableChangeInProgress: contentEditableChangeInProgress,
2776 setContentEditableChangeInProgress: setContentEditableChangeInProgress,
2777 acfDynamicStylesElement: acfDynamicStylesElement,
2778 setAcfDynamicStylesElement: setAcfDynamicStylesElement,
2779 blockEditorInspectorSidebarOpen: blockEditorInspectorSidebarOpen,
2780 freezeInlineToolbarDuringReRender: freezeInlineToolbarDuringReRender,
2781 isFetchingBlock: isFetchingBlock
2782 }));
2783 };
2784
2785 /**
2786 * Inner component that handles rendering and portals
2787 * Separated to manage refs and portal targets properly
2788 */
2789 function BlockEditInner(props) {
2790 const {
2791 blockType,
2792 $,
2793 isSelected,
2794 attributes,
2795 context,
2796 validationErrors,
2797 showValidationErrors,
2798 theSerializedAcfData,
2799 setTheSerializedAcfData,
2800 acfFormRef,
2801 blockFormHtml,
2802 blockPreviewHtml,
2803 blockFetcher,
2804 userHasInteractedWithForm,
2805 setUserHasInteractedWithForm,
2806 previewRef,
2807 hasFetchedOnce,
2808 blockToolbarFields,
2809 blockFieldInfo,
2810 gutenbergIframeOrDocument,
2811 setGutenbergIframeOrDocument,
2812 currentInlineEditingElement,
2813 setCurrentInlineEditingElement,
2814 currentInlineEditingElementUid,
2815 setCurrentInlineEditingElementUid,
2816 currentContentEditableElement,
2817 setCurrentContentEditableElement,
2818 inlineEditingToolbarHasFocus,
2819 setInlineEditingToolbarHasFocus,
2820 contentEditableChangeInProgress,
2821 setContentEditableChangeInProgress,
2822 acfDynamicStylesElement,
2823 setAcfDynamicStylesElement,
2824 blockEditorInspectorSidebarOpen,
2825 freezeInlineToolbarDuringReRender,
2826 isFetchingBlock
2827 } = props;
2828 const {
2829 clientId
2830 } = (0,_wordpress_block_editor__WEBPACK_IMPORTED_MODULE_2__.useBlockEditContext)();
2831 const inspectorControlsRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)();
2832 const [blockFormModalOpen, setBlockFormModalOpen] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
2833 const modalFormContainerRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)();
2834 const [currentFormContainer, setCurrentFormContainer] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)();
2835 const [canRenderForm, setCanRenderForm] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
2836 const [shouldShowModalDoneFallback, setShouldShowModalDoneFallback] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
2837 const [invisibleBlockFormContainer, setInvisibleBlockFormContainer] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useState)();
2838 const closeBlockFormModal = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useCallback)(() => {
2839 setCurrentFormContainer(null);
2840 setBlockFormModalOpen(false);
2841 }, []);
2842 const setModalFormContainer = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useCallback)(container => {
2843 modalFormContainerRef.current = container;
2844 if (container && blockFormModalOpen) {
2845 setCurrentFormContainer(container);
2846 }
2847 }, [blockFormModalOpen]);
2848
2849 // Render counter for debugging
2850 const renderCount = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useRef)(0);
2851 renderCount.current++;
2852
2853 // Detect Gutenberg iframe or document
2854 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2855 const gutenbergIframe = document.querySelector('iframe[name="editor-canvas"]');
2856 if (gutenbergIframe?.contentDocument) {
2857 setGutenbergIframeOrDocument(gutenbergIframe.contentDocument);
2858 } else {
2859 setGutenbergIframeOrDocument(document);
2860 }
2861 }, []);
2862
2863 // Create/get dynamic styles element for inline field highlighting
2864 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2865 if (!gutenbergIframeOrDocument) return;
2866 let styleElement = gutenbergIframeOrDocument.getElementById('acf-dynamic-styles');
2867 if (!styleElement) {
2868 styleElement = document.createElement('style');
2869 styleElement.id = 'acf-dynamic-styles';
2870 gutenbergIframeOrDocument.head.appendChild(styleElement);
2871 }
2872 setAcfDynamicStylesElement(styleElement);
2873 }, [gutenbergIframeOrDocument]);
2874 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2875 let invisibleContainer = document.getElementById('invisible-acf-form-element');
2876 if (!invisibleContainer) {
2877 invisibleContainer = document.createElement('div');
2878 invisibleContainer.id = 'invisible-acf-form-element';
2879 invisibleContainer.style.display = 'none';
2880 document.body.appendChild(invisibleContainer);
2881 }
2882 setInvisibleBlockFormContainer(invisibleContainer);
2883 }, [blockEditorInspectorSidebarOpen]);
2884 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2885 if (blockFormModalOpen) {
2886 return;
2887 }
2888 setCurrentFormContainer(blockEditorInspectorSidebarOpen ? inspectorControlsRef.current : invisibleBlockFormContainer);
2889 }, [blockEditorInspectorSidebarOpen, invisibleBlockFormContainer, blockFormModalOpen]);
2890 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2891 if (!blockFormModalOpen) {
2892 return;
2893 }
2894 const setModalContainer = () => {
2895 if (modalFormContainerRef?.current) {
2896 setCurrentFormContainer(modalFormContainerRef.current);
2897 }
2898 };
2899 setModalContainer();
2900 const timeout = setTimeout(setModalContainer, 0);
2901 return () => {
2902 clearTimeout(timeout);
2903 };
2904 }, [blockFormModalOpen]);
2905 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2906 if (!blockFormModalOpen) {
2907 setShouldShowModalDoneFallback(false);
2908 return;
2909 }
2910 const updateFallbackVisibility = () => {
2911 const modal = modalFormContainerRef?.current?.closest('.acf-block-form-modal');
2912 const hasHeaderActions = modal?.querySelector('.components-modal__header .components-button');
2913 setShouldShowModalDoneFallback(!hasHeaderActions);
2914 };
2915 updateFallbackVisibility();
2916 const timeout = setTimeout(updateFallbackVisibility, 0);
2917 return () => {
2918 clearTimeout(timeout);
2919 };
2920 }, [blockFormModalOpen]);
2921 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2922 if (blockFormModalOpen) {
2923 return;
2924 }
2925 if (blockEditorInspectorSidebarOpen) {
2926 setCurrentFormContainer(inspectorControlsRef.current);
2927 return;
2928 }
2929 setCurrentFormContainer(invisibleBlockFormContainer);
2930 }, [blockEditorInspectorSidebarOpen, invisibleBlockFormContainer, blockFormModalOpen]);
2931 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2932 if (isSelected && inspectorControlsRef?.current) {
2933 setCanRenderForm(true);
2934 } else if (isSelected && !inspectorControlsRef?.current) {
2935 setTimeout(() => {
2936 setCanRenderForm(true);
2937 }, 1);
2938 }
2939 }, [isSelected, inspectorControlsRef, inspectorControlsRef.current]);
2940 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2941 if (isSelected && validationErrors && showValidationErrors && blockType?.hide_fields_in_sidebar) {
2942 setBlockFormModalOpen(true);
2943 }
2944 }, [isSelected, showValidationErrors, validationErrors, blockType]);
2945
2946 // Build block CSS classes
2947 let blockClasses = 'acf-block-component acf-block-body';
2948 blockClasses += ' acf-block-preview';
2949 if (validationErrors && showValidationErrors) {
2950 blockClasses += ' acf-block-has-validation-error';
2951 }
2952 const blockProps = {
2953 ...(0,_wordpress_block_editor__WEBPACK_IMPORTED_MODULE_2__.useBlockProps)({
2954 className: blockClasses,
2955 ref: previewRef
2956 })
2957 };
2958
2959 // Update field value from contentEditable changes (matches 6.7.0.2)
2960 const updateFieldValueFromContentEditable = (content, fieldSlug) => {
2961 if (!acfFormRef?.current || !fieldSlug) return;
2962 const fieldWrapper = acfFormRef.current.querySelector(`[data-name=${fieldSlug}]`);
2963 if (!fieldWrapper) return;
2964 const fieldKey = fieldWrapper.attributes.getNamedItem('data-key')?.value;
2965 if (!fieldKey) return;
2966 const fieldInput = acfFormRef.current.querySelector(`[name="acf-block_${clientId}[${fieldKey}]"`);
2967 if (!fieldInput) return;
2968
2969 // Update field value and trigger serialization (debouncing happens in useEffect)
2970 if (content) {
2971 setUserHasInteractedWithForm(true);
2972 }
2973 setContentEditableChangeInProgress(false);
2974 fieldInput.value = content;
2975 const $form = $(acfFormRef?.current);
2976 const serializedData = acf.serialize($form, `acf-block_${clientId}`);
2977 if (serializedData) {
2978 deserializeGoogleMapValues($form, serializedData, clientId);
2979 setTheSerializedAcfData(JSON.stringify(serializedData));
2980 } else {
2981 setUserHasInteractedWithForm(false);
2982 }
2983 };
2984
2985 // Watch for changes in contentEditable fields using MutationObserver
2986 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.useEffect)(() => {
2987 if (!gutenbergIframeOrDocument || !blockPreviewHtml) return;
2988 const observer = new MutationObserver(mutations => {
2989 for (const mutation of mutations) {
2990 // Handle text content changes
2991 if (mutation.type === 'characterData') {
2992 let element = mutation.target.parentNode;
2993 const blockElement = element?.closest('[data-block]');
2994 const blockId = blockElement?.getAttribute('data-block');
2995 if (!element || !blockElement || blockId !== clientId) return;
2996
2997 // Find the contentEditable element
2998 if (element && !element.hasAttribute('data-acf-inline-contenteditable')) {
2999 element = element.closest('[data-acf-inline-contenteditable]');
3000 }
3001 if (element && element.hasAttribute('data-acf-inline-contenteditable')) {
3002 const fieldSlug = element.attributes.getNamedItem('data-acf-inline-contenteditable-field-slug').value;
3003 let content = element.innerHTML.trim();
3004 if (!content) content = '';
3005 updateFieldValueFromContentEditable(content, fieldSlug);
3006 }
3007 }
3008 // Handle attribute or child list changes
3009 else {
3010 const element = mutation.target.closest('[data-acf-inline-contenteditable]');
3011 const blockElement = element?.closest('[data-block]');
3012 if (!element || !blockElement || blockElement.getAttribute('data-block') !== clientId) return;
3013 if (element) {
3014 const fieldSlug = element.attributes.getNamedItem('data-acf-inline-contenteditable-field-slug').value;
3015 let content = element.innerHTML.trim();
3016
3017 // Handle empty content - remove empty BR tags
3018 if (!content || element.textContent.trim().length === 0 && element.children.length === 1 && element.firstElementChild && element.firstElementChild.nodeName === 'BR') {
3019 element.innerHTML = '';
3020 content = '';
3021 }
3022 updateFieldValueFromContentEditable(content, fieldSlug);
3023 }
3024 }
3025 }
3026 });
3027
3028 // Observe the gutenberg iframe/document for changes
3029 observer.observe(gutenbergIframeOrDocument, {
3030 attributes: true,
3031 childList: true,
3032 subtree: true,
3033 characterData: true,
3034 attributeFilter: ['data-acf-inline-contenteditable']
3035 });
3036
3037 // Cleanup
3038 return () => {
3039 observer.disconnect();
3040 };
3041 }, [blockPreviewHtml, gutenbergIframeOrDocument]);
3042
3043 // Callback when a new inline editing element is selected
3044 const handleNewInlineEditingElementSelected = uid => {
3045 setTimeout(() => {
3046 setCurrentInlineEditingElementUid(uid);
3047 const element = previewRef?.current.querySelector(`[data-acf-inline-fields-uid="${uid}"]`);
3048 setCurrentInlineEditingElement(element);
3049 if (element) {
3050 element.scrollIntoView({
3051 behavior: 'smooth',
3052 block: 'nearest'
3053 });
3054 }
3055 }, 1);
3056 };
3057
3058 // Callback when a new contentEditable element is selected
3059 const handleNewContentEditableElementSelected = fieldSlug => {
3060 if (fieldSlug) {
3061 const element = previewRef?.current.querySelector(`[data-acf-inline-contenteditable-field-slug="${fieldSlug}"]`);
3062 setCurrentContentEditableElement(element);
3063 } else {
3064 setCurrentContentEditableElement(null);
3065 }
3066 };
3067 let portalTarget = blockEditorInspectorSidebarOpen && inspectorControlsRef?.current ? inspectorControlsRef.current : invisibleBlockFormContainer;
3068 if (currentFormContainer) {
3069 portalTarget = currentFormContainer;
3070 }
3071
3072 // Determine inline editing toolbar anchor (matches 6.7.0.2 logic)
3073 let inlineEditingToolbarAnchor = null;
3074 if (currentInlineEditingElement && currentContentEditableElement) {
3075 inlineEditingToolbarAnchor = currentInlineEditingElement;
3076 } else if (currentInlineEditingElement && !currentContentEditableElement) {
3077 inlineEditingToolbarAnchor = currentInlineEditingElement;
3078 } else if (!currentInlineEditingElement && currentContentEditableElement) {
3079 inlineEditingToolbarAnchor = currentContentEditableElement;
3080 }
3081 // Ensure anchor is connected to DOM
3082 if (inlineEditingToolbarAnchor && !inlineEditingToolbarAnchor.isConnected) {
3083 inlineEditingToolbarAnchor = null;
3084 }
3085 return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_block_toolbar_fields__WEBPACK_IMPORTED_MODULE_8__.BlockToolbarFields, {
3086 blockToolbarFields: blockToolbarFields,
3087 blockFieldInfo: blockFieldInfo,
3088 setCurrentBlockFormContainer: setCurrentFormContainer,
3089 gutenbergIframeOrDocument: gutenbergIframeOrDocument,
3090 setBlockFormModalOpen: setBlockFormModalOpen,
3091 blockFormModalOpen: blockFormModalOpen,
3092 invisibleBlockFormContainer: invisibleBlockFormContainer,
3093 currentInlineEditingElement: currentInlineEditingElement,
3094 setCurrentInlineEditingElement: setCurrentInlineEditingElement,
3095 currentInlineEditingElementUid: currentInlineEditingElementUid,
3096 hideExpandedEditorBtnInToolbar: blockType?.expanded_editor_buttons === false || Array.isArray(blockType?.expanded_editor_buttons) && !blockType?.expanded_editor_buttons.includes('toolbar') || !blockFieldInfo || blockFieldInfo?.length === 0,
3097 onNewInlineEditingElementSelected: handleNewInlineEditingElementSelected
3098 }), /*#__PURE__*/React.createElement(_wordpress_block_editor__WEBPACK_IMPORTED_MODULE_2__.InspectorControls, null, blockFieldInfo?.length > 0 && (blockType?.expanded_editor_buttons === true || Array.isArray(blockType?.expanded_editor_buttons) && blockType?.expanded_editor_buttons.includes('sidebar')) && /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_3__.PanelBody, null, /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_3__.Button, {
3099 className: "acf-blocks-open-expanded-editor-btn",
3100 variant: "secondary",
3101 onClick: () => {
3102 setBlockFormModalOpen(true);
3103 },
3104 icon: "edit"
3105 }, blockType?.expanded_editor_button_text || acf.__('Open Expanded Editor'))), /*#__PURE__*/React.createElement(InspectorBlockFormContainer, {
3106 inspectorBlockFormRef: inspectorControlsRef,
3107 setCurrentBlockFormContainer: setCurrentFormContainer
3108 })), portalTarget && canRenderForm && (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createPortal)(/*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_block_form__WEBPACK_IMPORTED_MODULE_5__.BlockForm, {
3109 $: $,
3110 clientId: clientId,
3111 blockFormHtml: blockFormHtml,
3112 onChange: function ($form) {
3113 const serializedData = acf.serialize($form, `acf-block_${clientId}`);
3114 if (serializedData) {
3115 deserializeGoogleMapValues($form, serializedData, clientId);
3116 setTheSerializedAcfData(JSON.stringify(serializedData));
3117 }
3118 },
3119 validationErrors: validationErrors,
3120 showValidationErrors: showValidationErrors,
3121 acfFormRef: acfFormRef,
3122 userHasInteractedWithForm: userHasInteractedWithForm,
3123 attributes: attributes,
3124 hideFieldsInSidebar: blockType?.auto_inline_editing && blockType?.hide_fields_in_sidebar === undefined && currentFormContainer === inspectorControlsRef.current || blockType?.hide_fields_in_sidebar && currentFormContainer === inspectorControlsRef.current
3125 }), freezeInlineToolbarDuringReRender && /*#__PURE__*/React.createElement("style", null, `.acf-inline-editing-toolbar{${freezeInlineToolbarDuringReRender}}`)), portalTarget), /*#__PURE__*/React.createElement(React.Fragment, null, blockFormModalOpen && /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_3__.Modal, {
3126 className: "acf-block-form-modal",
3127 overlayClassName: acf.applyFilters('blocks/expanded_editor_overlay_class', 'acf-expanded-editor-panel-overlay'),
3128 isFullScreen: true,
3129 title: blockType.title,
3130 onRequestClose: () => {
3131 if (!isFetchingBlock || validationErrors) {
3132 setBlockFormModalOpen(false);
3133 }
3134 },
3135 shouldCloseOnEsc: !isFetchingBlock || validationErrors,
3136 isDismissible: false,
3137 headerActions: [/*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_3__.Button, {
3138 key: "done",
3139 variant: "primary",
3140 disabled: isFetchingBlock && !validationErrors,
3141 isBusy: isFetchingBlock,
3142 onClick: closeBlockFormModal
3143 }, acf.__('Done'))]
3144 }, shouldShowModalDoneFallback && /*#__PURE__*/React.createElement("div", {
3145 className: "acf-block-form-modal__actions"
3146 }, /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_3__.Button, {
3147 className: "acf-block-form-modal__done-button",
3148 variant: "primary",
3149 disabled: isFetchingBlock && !validationErrors,
3150 isBusy: isFetchingBlock,
3151 onClick: closeBlockFormModal
3152 }, acf.__('Done'))), /*#__PURE__*/React.createElement("div", {
3153 className: "acf-modal-block-form-container",
3154 ref: setModalFormContainer
3155 }))), inlineEditingToolbarAnchor && /*#__PURE__*/React.createElement(_popover_wrapper__WEBPACK_IMPORTED_MODULE_10__.PopoverWrapper, {
3156 key: currentContentEditableElement,
3157 focusOnMount: (() => {
3158 const activeElement = document.activeElement;
3159 return activeElement && activeElement.isContentEditable, false;
3160 })(),
3161 variant: "unstyled",
3162 anchor: inlineEditingToolbarAnchor,
3163 className: "acf-inline-editing-toolbar block-editor-block-list__block-popover",
3164 placement: "top-start",
3165 onClose: event => {
3166 // Don't close if clicking toolbar button
3167 if (event.key !== 'Escape' && event.target.closest('.acf-toolbar-button')) {
3168 return false;
3169 }
3170
3171 // Handle Escape key
3172 if (event.key === 'Escape') {
3173 return !document.querySelector('.acf-inline-fields-popover-inner') && !currentContentEditableElement && (currentInlineEditingElement && currentInlineEditingElement.focus(), setCurrentInlineEditingElementUid(null), setCurrentContentEditableElement(null), true);
3174 }
3175
3176 // Don't close if clicking on contenteditable element
3177 if (event.target.getAttribute('data-acf-inline-contenteditable')) {
3178 return false;
3179 }
3180
3181 // Don't close if clicking inside popover or modal
3182 const inlineFieldsPopover = event.target.closest('.acf-inline-fields-popover-inner');
3183 const modal = event.target.closest('.components-modal__content');
3184 return inlineFieldsPopover || modal || (setCurrentInlineEditingElementUid(null), setCurrentContentEditableElement(null)), true;
3185 },
3186 gutenbergIframeOrDocument: gutenbergIframeOrDocument,
3187 hidePrimaryBlockToolbar: true
3188 }, /*#__PURE__*/React.createElement(_inline_editing_toolbar__WEBPACK_IMPORTED_MODULE_9__.InlineEditingToolbar, {
3189 key: currentInlineEditingElementUid,
3190 blockIcon: blockType.icon,
3191 blockFieldInfo: blockFieldInfo,
3192 acfFormRef: acfFormRef,
3193 setInlineEditingToolbarHasFocus: setInlineEditingToolbarHasFocus,
3194 currentContentEditableElement: currentContentEditableElement,
3195 currentInlineEditingElement: currentInlineEditingElement,
3196 currentInlineEditingElementUid: currentInlineEditingElementUid,
3197 gutenbergIframeOrDocument: gutenbergIframeOrDocument,
3198 setCurrentBlockFormContainer: setCurrentFormContainer,
3199 contentEditableChangeInProgress: contentEditableChangeInProgress
3200 })), currentInlineEditingElementUid && acfDynamicStylesElement && (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createPortal)(/*#__PURE__*/React.createElement("style", null, `
3201 [data-acf-inline-fields-uid="${currentInlineEditingElementUid}"]{
3202 outline: 2px solid var( --wp-admin-theme-color );
3203 outline-offset: 2px;
3204 }
3205 `), acfDynamicStylesElement), /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(_block_preview__WEBPACK_IMPORTED_MODULE_6__.BlockPreview, {
3206 blockPreviewHtml: blockPreviewHtml,
3207 blockProps: blockProps
3208 }, /*#__PURE__*/React.createElement(_error_boundary__WEBPACK_IMPORTED_MODULE_7__.ErrorBoundary, {
3209 fallbackRender: ({
3210 error
3211 }) => /*#__PURE__*/React.createElement(_error_boundary__WEBPACK_IMPORTED_MODULE_7__.BlockPreviewErrorFallback, {
3212 blockLabel: blockType?.title || acf.__('ACF Block'),
3213 setBlockFormModalOpen: setBlockFormModalOpen,
3214 error: error
3215 })
3216 }, blockPreviewHtml === 'acf-block-preview-no-html' ? /*#__PURE__*/React.createElement(_block_placeholder__WEBPACK_IMPORTED_MODULE_4__.BlockPlaceholder, {
3217 setBlockFormModalOpen: setBlockFormModalOpen,
3218 blockLabel: blockType.title
3219 }) : null, blockPreviewHtml === 'acf-block-preview-loading' && /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_3__.Placeholder, null, /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_3__.Spinner, null)), blockPreviewHtml !== 'acf-block-preview-loading' && blockPreviewHtml !== 'acf-block-preview-no-html' && blockPreviewHtml && acf.parseJSX(blockPreviewHtml, handleNewInlineEditingElementSelected, updateFieldValueFromContentEditable, handleNewContentEditableElementSelected, blockFieldInfo, $)))));
3220 }
3221
3222 /***/ }),
3223
3224 /***/ "./assets/src/js/pro/blocks-v3/components/block-form.js":
3225 /*!**************************************************************!*\
3226 !*** ./assets/src/js/pro/blocks-v3/components/block-form.js ***!
3227 \**************************************************************/
3228 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
3229
3230 "use strict";
3231 __webpack_require__.r(__webpack_exports__);
3232 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3233 /* harmony export */ BlockForm: () => (/* binding */ BlockForm)
3234 /* harmony export */ });
3235 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
3236 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
3237 /* harmony import */ var _utils_post_locking__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils/post-locking */ "./assets/src/js/pro/blocks-v3/utils/post-locking.js");
3238 /**
3239 * BlockForm Component
3240 * Renders the ACF fields form inside a block and handles form changes, validation, and remounting
3241 */
3242
3243
3244
3245
3246 /**
3247 * BlockForm component
3248 * Manages ACF field forms within Gutenberg blocks, including validation and change detection
3249 *
3250 * @param {Object} props - Component props
3251 * @param {jQuery} props.$ - jQuery instance
3252 * @param {string} props.clientId - Block client ID
3253 * @param {string} props.blockFormHtml - HTML markup for the ACF form
3254 * @param {Function} props.onMount - Callback when the form mounts
3255 * @param {Function} props.onChange - Callback when form data changes
3256 * @param {Array} props.validationErrors - Array of validation error objects
3257 * @param {boolean} props.showValidationErrors - Whether to display validation errors
3258 * @param {React.RefObject} props.acfFormRef - Ref to the form container element
3259 * @param {boolean} props.userHasInteractedWithForm - Whether user has interacted with the form
3260 * @param {Object} props.attributes - Block attributes
3261 * @returns {JSX.Element} - Rendered form component
3262 */
3263 const BlockForm = ({
3264 $,
3265 clientId,
3266 blockFormHtml,
3267 onMount,
3268 onChange,
3269 validationErrors,
3270 showValidationErrors,
3271 acfFormRef,
3272 userHasInteractedWithForm,
3273 attributes,
3274 hideFieldsInSidebar
3275 }) => {
3276 const [formHtml, setFormHtml] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useState)(blockFormHtml);
3277 const [pendingChange, setPendingChange] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
3278 const debounceTimer = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useRef)(null);
3279 const [userInteracted, setUserInteracted] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
3280 const [hasRemounted, setHasRemounted] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useState)(false);
3281
3282 // Trigger onChange when there's a pending change
3283 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
3284 if (pendingChange && (userHasInteractedWithForm || userInteracted)) {
3285 onChange(pendingChange);
3286 setPendingChange(false);
3287 }
3288 }, [pendingChange, userHasInteractedWithForm, userInteracted, setPendingChange, onChange]);
3289
3290 // Update form HTML when blockFormHtml prop changes
3291 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
3292 if (!formHtml && blockFormHtml) {
3293 setFormHtml(blockFormHtml);
3294 }
3295 }, [blockFormHtml]);
3296
3297 // Handle validation errors
3298 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
3299 if (!acfFormRef?.current) return;
3300 const validator = acf.getBlockFormValidator($(acfFormRef.current).find('.acf-block-fields'));
3301 validator.clearErrors();
3302 validator.set('notice', null);
3303 acf.doAction('blocks/validation/pre_apply', validationErrors);
3304 if (validationErrors) {
3305 if (showValidationErrors) {
3306 (0,_utils_post_locking__WEBPACK_IMPORTED_MODULE_1__.lockPostSaving)(clientId);
3307 validator.$el.find('.acf-notice').remove();
3308 validator.addErrors(validationErrors);
3309 validator.showErrors('after');
3310 }
3311 } else {
3312 // Handle successful validation
3313 if (validator.$el.find('.acf-notice').length > 0 && showValidationErrors) {
3314 validator.$el.find('.acf-notice').remove();
3315 validator.addErrors([{
3316 message: acf.__('Validation successful')
3317 }]);
3318 validator.showErrors('after');
3319 validator.get('notice').update({
3320 type: 'success',
3321 text: acf.__('Validation successful'),
3322 timeout: 1000
3323 });
3324 validator.set('notice', null);
3325 setTimeout(() => {
3326 validator.$el.find('.acf-notice').remove();
3327 }, 1001);
3328 const noticeDispatch = wp.data.dispatch('core/notices');
3329
3330 /**
3331 * Recursively checks for ACF errors in blocks
3332 * @param {Array} blocks - Array of block objects
3333 * @returns {Promise<boolean>} - True if error found
3334 */
3335 function checkForErrors(blocks) {
3336 return new Promise(function (resolve) {
3337 blocks.forEach(block => {
3338 if (block.innerBlocks.length > 0) {
3339 checkForErrors(block.innerBlocks).then(hasError => {
3340 if (hasError) return resolve(true);
3341 });
3342 }
3343 if (block.attributes.hasAcfError && block.clientId !== clientId) {
3344 return resolve(true);
3345 }
3346 });
3347 return resolve(false);
3348 });
3349 }
3350 checkForErrors(wp.data.select('core/block-editor').getBlocks()).then(hasError => {
3351 if (hasError) {
3352 noticeDispatch.createErrorNotice(acf.__('An ACF Block on this page requires attention before you can save.'), {
3353 id: 'acf-blocks-validation',
3354 isDismissible: true
3355 });
3356 } else {
3357 noticeDispatch.removeNotice('acf-blocks-validation');
3358 }
3359 });
3360 }
3361 (0,_utils_post_locking__WEBPACK_IMPORTED_MODULE_1__.unlockPostSaving)(clientId);
3362 }
3363 acf.doAction('blocks/validation/post_apply', validationErrors);
3364 }, [validationErrors, clientId, showValidationErrors]);
3365
3366 // Handle form remounting and change detection
3367 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
3368 if (!acfFormRef?.current || !formHtml) return;
3369 acf.debug('Remounting ACF Form');
3370 const formElement = acfFormRef.current;
3371 const $form = $(formElement);
3372 let isActive = true;
3373 if (!hasRemounted) {
3374 acf.debug('Remounting ACF Form');
3375 acf.doAction('remount', $form);
3376 onMount?.($form);
3377 setHasRemounted(true);
3378 }
3379 const handleChange = () => {
3380 onChange($form);
3381 };
3382 const scheduleChange = () => {
3383 if (!isActive) return;
3384 const inputs = formElement.querySelectorAll('input, textarea');
3385 const selects = formElement.querySelectorAll('select');
3386 inputs.forEach(input => {
3387 input.removeEventListener('input', handleChange);
3388 input.addEventListener('input', handleChange);
3389 input.removeEventListener('change', handleChange);
3390 input.addEventListener('change', handleChange);
3391 });
3392 selects.forEach(select => {
3393 select.removeEventListener('change', handleChange);
3394 select.addEventListener('change', handleChange);
3395 });
3396 clearTimeout(debounceTimer.current);
3397 debounceTimer.current = setTimeout(() => {
3398 if (isActive) {
3399 setPendingChange($form);
3400 }
3401 }, 300);
3402 };
3403
3404 // Observe DOM changes to detect field additions/removals
3405 const domObserver = new MutationObserver(scheduleChange);
3406
3407 // Observe iframe content changes (for WYSIWYG editors)
3408 const iframeObserver = new MutationObserver(() => {
3409 if (isActive) {
3410 setUserInteracted(true);
3411 scheduleChange();
3412 }
3413 });
3414 const observerConfig = {
3415 attributes: true,
3416 childList: true,
3417 subtree: true,
3418 characterData: true
3419 };
3420 domObserver.observe(formElement, observerConfig);
3421
3422 // Watch for changes in iframes (WYSIWYG fields)
3423 [...formElement.querySelectorAll('iframe')].forEach(iframe => {
3424 if (iframe && iframe.contentDocument) {
3425 const iframeBody = iframe.contentDocument.body;
3426 if (iframeBody) {
3427 iframeObserver.observe(iframeBody, observerConfig);
3428 }
3429 }
3430 });
3431
3432 // Attach event listeners to form inputs
3433 formElement.querySelectorAll('input, textarea').forEach(input => {
3434 input.addEventListener('input', handleChange);
3435 input.addEventListener('change', handleChange);
3436 });
3437 formElement.querySelectorAll('select').forEach(select => {
3438 select.addEventListener('change', handleChange);
3439 });
3440 if (userHasInteractedWithForm) {
3441 clearTimeout(debounceTimer.current);
3442 debounceTimer.current = setTimeout(() => {
3443 if (isActive) {
3444 setPendingChange($form);
3445 }
3446 }, 300);
3447 }
3448
3449 // Cleanup function
3450 return () => {
3451 isActive = false;
3452 domObserver.disconnect();
3453 iframeObserver.disconnect();
3454 clearTimeout(debounceTimer.current);
3455 if (formElement) {
3456 formElement.querySelectorAll('input, textarea').forEach(input => {
3457 input.removeEventListener('input', handleChange);
3458 input.removeEventListener('change', handleChange);
3459 });
3460 formElement.querySelectorAll('select').forEach(select => {
3461 select.removeEventListener('change', handleChange);
3462 });
3463 }
3464 };
3465 }, [acfFormRef, attributes, formHtml, onMount, userHasInteractedWithForm]);
3466 return /*#__PURE__*/React.createElement("div", {
3467 ref: acfFormRef,
3468 className: "acf-block-component acf-block-panel",
3469 style: {
3470 display: hideFieldsInSidebar ? 'none' : null
3471 },
3472 dangerouslySetInnerHTML: {
3473 __html: acf.applyFilters('blocks/form/render', formHtml, true)
3474 }
3475 });
3476 };
3477
3478 /***/ }),
3479
3480 /***/ "./assets/src/js/pro/blocks-v3/components/block-placeholder.js":
3481 /*!*********************************************************************!*\
3482 !*** ./assets/src/js/pro/blocks-v3/components/block-placeholder.js ***!
3483 \*********************************************************************/
3484 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
3485
3486 "use strict";
3487 __webpack_require__.r(__webpack_exports__);
3488 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3489 /* harmony export */ BlockPlaceholder: () => (/* binding */ BlockPlaceholder)
3490 /* harmony export */ });
3491 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components");
3492 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_0__);
3493 /**
3494 * BlockPlaceholder Component
3495 * Displays a placeholder UI when block has no preview HTML
3496 */
3497
3498
3499 /**
3500 * SVG icon for the block placeholder
3501 * Represents a generic block/form icon
3502 */
3503 const blockIcon = /*#__PURE__*/React.createElement("svg", {
3504 xmlns: "http://www.w3.org/2000/svg",
3505 viewBox: "0 0 24 24",
3506 width: "24",
3507 height: "24",
3508 "aria-hidden": "true",
3509 focusable: "false"
3510 }, /*#__PURE__*/React.createElement("path", {
3511 d: "M19 8h-1V6h-5v2h-2V6H6v2H5c-1.1 0-2 .9-2 2v8c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2v-8c0-1.1-.9-2-2-2zm.5 10c0 .3-.2.5-.5.5H5c-.3 0-.5-.2-.5-.5v-8c0-.3.2-.5.5-.5h14c.3 0 .5.2.5.5v8z"
3512 }));
3513
3514 /**
3515 * BlockPlaceholder component
3516 * Shows when a block has no preview HTML available
3517 * Prompts user to open the block form to edit fields
3518 *
3519 * @param {Object} props - Component props
3520 * @param {Function} props.setBlockFormModalOpen - Function to open the block form modal
3521 * @param {string} props.blockLabel - The block's title/label
3522 * @returns {JSX.Element} - Rendered placeholder
3523 */
3524 const BlockPlaceholder = ({
3525 setBlockFormModalOpen,
3526 blockLabel,
3527 instructions
3528 }) => {
3529 return /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_0__.Placeholder, {
3530 icon: /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_0__.Icon, {
3531 icon: blockIcon
3532 }),
3533 label: blockLabel,
3534 instructions: instructions
3535 }, /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_0__.Button, {
3536 variant: "primary",
3537 onClick: () => {
3538 setBlockFormModalOpen(true);
3539 }
3540 }, acf.__('Edit Block')));
3541 };
3542
3543 /***/ }),
3544
3545 /***/ "./assets/src/js/pro/blocks-v3/components/block-preview.js":
3546 /*!*****************************************************************!*\
3547 !*** ./assets/src/js/pro/blocks-v3/components/block-preview.js ***!
3548 \*****************************************************************/
3549 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
3550
3551 "use strict";
3552 __webpack_require__.r(__webpack_exports__);
3553 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3554 /* harmony export */ BlockPreview: () => (/* binding */ BlockPreview)
3555 /* harmony export */ });
3556 /**
3557 * BlockPreview Component
3558 * Simple wrapper component that renders block preview HTML with block props
3559 */
3560
3561 /**
3562 * BlockPreview component
3563 * Wraps block preview content with the appropriate block props from useBlockProps
3564 *
3565 * @param {Object} props - Component props
3566 * @param {React.ReactNode} props.children - Child elements to render
3567 * @param {Object} props.blockProps - Block props from useBlockProps hook
3568 * @returns {JSX.Element} - Rendered preview wrapper
3569 */
3570 const BlockPreview = ({
3571 children,
3572 blockProps
3573 }) => /*#__PURE__*/React.createElement("div", blockProps, children);
3574
3575 /***/ }),
3576
3577 /***/ "./assets/src/js/pro/blocks-v3/components/block-toolbar-fields.js":
3578 /*!************************************************************************!*\
3579 !*** ./assets/src/js/pro/blocks-v3/components/block-toolbar-fields.js ***!
3580 \************************************************************************/
3581 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
3582
3583 "use strict";
3584 __webpack_require__.r(__webpack_exports__);
3585 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3586 /* harmony export */ BlockToolbarFields: () => (/* binding */ BlockToolbarFields)
3587 /* harmony export */ });
3588 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
3589 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
3590 /* harmony import */ var _wordpress_blockEditor__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/blockEditor */ "@wordpress/block-editor");
3591 /* harmony import */ var _wordpress_blockEditor__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_blockEditor__WEBPACK_IMPORTED_MODULE_1__);
3592 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components");
3593 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_2__);
3594 /* harmony import */ var _popover_wrapper__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./popover-wrapper */ "./assets/src/js/pro/blocks-v3/components/popover-wrapper.js");
3595 /**
3596 * BlockToolbarFields Component
3597 * Renders field buttons in the WordPress block toolbar (top toolbar)
3598 * Allows quick access to edit specific fields from the block toolbar
3599 */
3600
3601 /* global acf */
3602
3603
3604
3605
3606
3607
3608 /**
3609 * BlockToolbarFields component
3610 * Displays field editing buttons in the WordPress block toolbar
3611 *
3612 * @param {Object} props - Component props
3613 * @param {Array} props.blockToolbarFields - Array of field names or field config objects to show in toolbar
3614 * @param {Array} props.blockFieldInfo - Array of all field information
3615 * @param {Function} props.setCurrentBlockFormContainer - Setter for form container ref
3616 * @param {Document|HTMLIFrameElement} props.gutenbergIframeOrDocument - Document or iframe reference
3617 * @param {Function} props.setBlockFormModalOpen - Setter for block form modal state
3618 * @param {boolean} props.blockFormModalOpen - Whether block form modal is open
3619 * @param {boolean} props.hideExpandedEditorBtnInToolbar - Whether to hide the expanded editor button in the toolbar
3620 * @return {JSX.Element} - Rendered toolbar controls
3621 */
3622 const BlockToolbarFields = ({
3623 blockToolbarFields,
3624 blockFieldInfo,
3625 setCurrentBlockFormContainer,
3626 gutenbergIframeOrDocument,
3627 setBlockFormModalOpen,
3628 blockFormModalOpen,
3629 hideExpandedEditorBtnInToolbar
3630 }) => {
3631 const [selectedFieldKey, setSelectedFieldKey] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
3632 const [selectedFieldButtonRef, setSelectedFieldButtonRef] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useState)();
3633 const [usePopover, setUsePopover] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useState)(true);
3634 const fieldPopoverContainerRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useRef)();
3635
3636 /**
3637 * Get field type class name from field name
3638 *
3639 * @param {string} fieldName Field name.
3640 * @return {string} Field type class name.
3641 */
3642 const getFieldTypeClassName = fieldName => {
3643 if (!fieldName || !blockFieldInfo) {
3644 return '';
3645 }
3646 const field = blockFieldInfo.find(f => f.name === fieldName);
3647 return field?.type ? field.type.replace(/_/g, '-') : '';
3648 };
3649
3650 /**
3651 * Get field info object from field name
3652 *
3653 * @param {string} fieldName Field name.
3654 * @return {Object|null} Field info object.
3655 */
3656 const getFieldInfo = fieldName => {
3657 return fieldName && blockFieldInfo ? blockFieldInfo.find(f => f.name === fieldName) : null;
3658 };
3659
3660 /**
3661 * Get field label from field name
3662 *
3663 * @param {string} fieldName Field name.
3664 * @return {string} Field label.
3665 */
3666 const getFieldLabel = fieldName => {
3667 if (!fieldName || !blockFieldInfo) {
3668 return fieldName || '';
3669 }
3670 const field = blockFieldInfo.find(f => f.name === fieldName);
3671 // Fallback to fieldName when label not found to ensure toolbar shows a title
3672 return field?.label || fieldName || '';
3673 };
3674 return /*#__PURE__*/React.createElement(_wordpress_blockEditor__WEBPACK_IMPORTED_MODULE_1__.BlockControls, null, !hideExpandedEditorBtnInToolbar && /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_2__.ToolbarGroup, null, /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_2__.ToolbarButton, {
3675 className: "components-icon-button components-toolbar__control",
3676 label: acf.__('Edit Block'),
3677 icon: "edit",
3678 onClick: () => {
3679 setBlockFormModalOpen(true);
3680 },
3681 isPressed: blockFormModalOpen
3682 })), blockToolbarFields.length > 0 && /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_2__.ToolbarGroup, null, (() => {
3683 const styleContent = `[data-name="${selectedFieldKey}"]{ display: block!important; }`;
3684 return /*#__PURE__*/React.createElement("style", null, styleContent);
3685 })(), blockToolbarFields && blockToolbarFields.map(field => {
3686 let fieldName = '';
3687 let fieldIconSvg = null;
3688 let fieldLabel = null;
3689
3690 // Handle field config object or simple string
3691 if (typeof field === 'object') {
3692 fieldName = field.fieldName ? field.fieldName : field.index;
3693 fieldIconSvg = field.fieldIcon ? window.atob(field.fieldIcon) : null;
3694 fieldLabel = field.fieldLabel ? field.fieldLabel : fieldName;
3695 } else {
3696 fieldName = field;
3697 }
3698 return /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_2__.ToolbarButton, {
3699 key: fieldName,
3700 icon: fieldIconSvg ? /*#__PURE__*/React.createElement("i", {
3701 dangerouslySetInnerHTML: {
3702 __html: fieldIconSvg
3703 }
3704 }) : /*#__PURE__*/React.createElement("i", {
3705 className: `field-type-icon field-type-icon-${getFieldTypeClassName(fieldName)}`
3706 }),
3707 label: fieldLabel || getFieldLabel(fieldName),
3708 isPressed: fieldName === selectedFieldKey,
3709 ref: fieldName === selectedFieldKey ? setSelectedFieldButtonRef : null,
3710 onMouseDown: () => {
3711 if (selectedFieldKey === fieldName) {
3712 setSelectedFieldKey(null);
3713 } else {
3714 setSelectedFieldKey(null);
3715 setTimeout(() => {
3716 const fieldInfo = getFieldInfo(fieldName);
3717 // Use modal for complex field types
3718 if (fieldInfo?.type === 'flexible_content' || fieldInfo?.type === 'repeater') {
3719 setUsePopover(false);
3720 } else {
3721 setUsePopover(true);
3722 }
3723 setSelectedFieldKey(fieldName);
3724 });
3725 }
3726 }
3727 });
3728 }), selectedFieldKey && selectedFieldButtonRef && usePopover && /*#__PURE__*/React.createElement(_popover_wrapper__WEBPACK_IMPORTED_MODULE_3__.PopoverWrapper, {
3729 focusOnMount: false,
3730 className: "acf-inline-fields-popover",
3731 anchor: selectedFieldButtonRef,
3732 animate: true,
3733 onClose: event => {
3734 // Don't close on certain events
3735 if (event.key !== 'Escape' && (event?.target.closest('.media-modal') || event?.target.closest('.acf-tooltip') || event?.target && fieldPopoverContainerRef?.current && fieldPopoverContainerRef?.current.contains(event.target) || selectedFieldButtonRef?.current && selectedFieldButtonRef?.current.contains(event?.target))) {
3736 return false;
3737 }
3738 setSelectedFieldKey(null);
3739 return true;
3740 },
3741 variant: "unstyled",
3742 gutenbergIframeOrDocument: gutenbergIframeOrDocument
3743 }, /*#__PURE__*/React.createElement("div", {
3744 ref: fieldPopoverContainerRef
3745 }, /*#__PURE__*/React.createElement("div", {
3746 className: "acf-inline-fields-popover-inner",
3747 style: {
3748 minWidth: '300px'
3749 },
3750 ref: setCurrentBlockFormContainer
3751 }))), selectedFieldKey && selectedFieldButtonRef && !usePopover && /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_2__.Modal, {
3752 className: "acf-block-form-modal",
3753 isFullScreen: true,
3754 title: getFieldInfo(selectedFieldKey)?.label,
3755 onRequestClose: () => {
3756 setSelectedFieldKey(null);
3757 }
3758 }, /*#__PURE__*/React.createElement("div", {
3759 ref: fieldPopoverContainerRef
3760 }, /*#__PURE__*/React.createElement("div", {
3761 className: "acf-inline-fields-popover-inner",
3762 style: {
3763 minWidth: '300px'
3764 },
3765 ref: setCurrentBlockFormContainer
3766 })))));
3767 };
3768
3769 /***/ }),
3770
3771 /***/ "./assets/src/js/pro/blocks-v3/components/error-boundary.js":
3772 /*!******************************************************************!*\
3773 !*** ./assets/src/js/pro/blocks-v3/components/error-boundary.js ***!
3774 \******************************************************************/
3775 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
3776
3777 "use strict";
3778 __webpack_require__.r(__webpack_exports__);
3779 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3780 /* harmony export */ BlockPreviewErrorFallback: () => (/* binding */ BlockPreviewErrorFallback),
3781 /* harmony export */ ErrorBoundary: () => (/* binding */ ErrorBoundary),
3782 /* harmony export */ ErrorBoundaryContext: () => (/* binding */ ErrorBoundaryContext)
3783 /* harmony export */ });
3784 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
3785 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
3786 /* harmony import */ var _block_placeholder__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./block-placeholder */ "./assets/src/js/pro/blocks-v3/components/block-placeholder.js");
3787
3788
3789
3790 // Create context outside the class
3791 const ErrorBoundaryContext = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.createContext)(null);
3792
3793 // Initial state constant
3794 const initialState = {
3795 didCatch: false,
3796 error: null
3797 };
3798
3799 // Error Boundary Component
3800 class ErrorBoundary extends _wordpress_element__WEBPACK_IMPORTED_MODULE_0__.Component {
3801 constructor(props) {
3802 super(props);
3803 this.resetErrorBoundary = this.resetErrorBoundary.bind(this);
3804 this.state = initialState;
3805 }
3806 static getDerivedStateFromError(error) {
3807 return {
3808 didCatch: true,
3809 error: error
3810 };
3811 }
3812 resetErrorBoundary() {
3813 const {
3814 error
3815 } = this.state;
3816 if (error !== null) {
3817 // Collect all arguments passed to reset
3818 const args = Array.from(arguments);
3819
3820 // Call optional onReset callback with context
3821 if (this.props.onReset) {
3822 this.props.onReset({
3823 args: args,
3824 reason: 'imperative-api'
3825 });
3826 }
3827 this.setState(initialState);
3828 }
3829 }
3830 componentDidCatch(error, errorInfo) {
3831 if (this.props.onError) {
3832 this.props.onError(error, errorInfo);
3833 }
3834 }
3835 componentDidUpdate(prevProps, prevState) {
3836 const {
3837 didCatch
3838 } = this.state;
3839 const {
3840 resetKeys
3841 } = this.props;
3842
3843 // Auto-reset if resetKeys prop changed
3844 if (didCatch && prevState.error !== null && hasResetKeysChanged(prevProps.resetKeys, resetKeys)) {
3845 if (this.props.onReset) {
3846 this.props.onReset({
3847 next: resetKeys,
3848 prev: prevProps.resetKeys,
3849 reason: 'keys'
3850 });
3851 }
3852 this.setState(initialState);
3853 }
3854 }
3855 render() {
3856 const {
3857 children,
3858 fallbackRender,
3859 FallbackComponent,
3860 fallback
3861 } = this.props;
3862 const {
3863 didCatch,
3864 error
3865 } = this.state;
3866 let content = children;
3867 if (didCatch) {
3868 const errorProps = {
3869 error: error,
3870 resetErrorBoundary: this.resetErrorBoundary
3871 };
3872 if (typeof fallbackRender === 'function') {
3873 content = fallbackRender(errorProps);
3874 } else if (FallbackComponent) {
3875 content = /*#__PURE__*/React.createElement(FallbackComponent, errorProps);
3876 } else if (fallback !== undefined) {
3877 content = fallback;
3878 } else {
3879 throw error;
3880 }
3881 }
3882 return /*#__PURE__*/React.createElement(ErrorBoundaryContext.Provider, {
3883 value: {
3884 didCatch,
3885 error,
3886 resetErrorBoundary: this.resetErrorBoundary
3887 }
3888 }, content);
3889 }
3890 }
3891
3892 // Helper function to check if reset keys changed
3893 function hasResetKeysChanged(prevKeys = [], nextKeys = []) {
3894 return prevKeys.length !== nextKeys.length || prevKeys.some((key, index) => !Object.is(key, nextKeys[index]));
3895 }
3896 const BlockPreviewErrorFallback = ({
3897 setBlockFormModalOpen,
3898 blockLabel,
3899 error
3900 }) => {
3901 let errorMessage = null;
3902 if (error) {
3903 acf.debug('Block preview error:', error);
3904 errorMessage = acf.__('Error previewing block v3');
3905 }
3906 return /*#__PURE__*/React.createElement(_block_placeholder__WEBPACK_IMPORTED_MODULE_1__.BlockPlaceholder, {
3907 setBlockFormModalOpen: setBlockFormModalOpen,
3908 blockLabel: blockLabel,
3909 instructions: errorMessage
3910 });
3911 };
3912
3913 /***/ }),
3914
3915 /***/ "./assets/src/js/pro/blocks-v3/components/inline-editing-toolbar.js":
3916 /*!**************************************************************************!*\
3917 !*** ./assets/src/js/pro/blocks-v3/components/inline-editing-toolbar.js ***!
3918 \**************************************************************************/
3919 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
3920
3921 "use strict";
3922 __webpack_require__.r(__webpack_exports__);
3923 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
3924 /* harmony export */ InlineEditingToolbar: () => (/* binding */ InlineEditingToolbar)
3925 /* harmony export */ });
3926 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
3927 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
3928 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components");
3929 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__);
3930 /* harmony import */ var _popover_wrapper__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./popover-wrapper */ "./assets/src/js/pro/blocks-v3/components/popover-wrapper.js");
3931 /**
3932 * InlineEditingToolbar Component
3933 * Main inline editing toolbar for ACF blocks
3934 * Handles field selection and editing for inline editable elements
3935 */
3936
3937
3938
3939
3940
3941 /**
3942 * InlineEditingToolbar component
3943 * Displays a toolbar with field buttons for inline editing
3944 *
3945 * @param {Object} props - Component props
3946 * @param {Object} props.blockIcon - Block icon configuration
3947 * @param {Array} props.blockFieldInfo - Array of field information
3948 * @param {Function} props.setInlineEditingToolbarHasFocus - Setter for toolbar focus state
3949 * @param {Element|null} props.currentContentEditableElement - Current content editable element
3950 * @param {Element|null} props.currentInlineEditingElement - Current inline editing element
3951 * @param {string|null} props.currentInlineEditingElementUid - Current element UID
3952 * @param {Document|HTMLIFrameElement} props.gutenbergIframeOrDocument - Document or iframe reference
3953 * @param {Function} props.setCurrentBlockFormContainer - Setter for form container
3954 * @param {boolean} props.contentEditableChangeInProgress - Whether content change is in progress
3955 * @returns {JSX.Element|null} - Rendered toolbar or null
3956 */
3957 const InlineEditingToolbar = ({
3958 blockIcon,
3959 blockFieldInfo,
3960 setInlineEditingToolbarHasFocus,
3961 currentContentEditableElement,
3962 currentInlineEditingElement,
3963 currentInlineEditingElementUid,
3964 gutenbergIframeOrDocument,
3965 setCurrentBlockFormContainer,
3966 contentEditableChangeInProgress
3967 }) => {
3968 const [selectedFieldKey, setSelectedFieldKey] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useState)(null);
3969 const [selectedFieldConfig, setSelectedFieldConfig] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useState)();
3970 const [selectedFieldButtonRef, setSelectedFieldButtonRef] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useState)();
3971 const [usePopover, setUsePopover] = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useState)(true);
3972 const fieldPopoverContainerRef = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useRef)();
3973
3974 // Get inline fields from data attribute
3975 const inlineFieldsAttr = currentInlineEditingElement ? currentInlineEditingElement.getAttribute('data-acf-inline-fields') : null;
3976 let inlineFields = [];
3977 try {
3978 inlineFields = JSON.parse(inlineFieldsAttr);
3979 } catch (e) {
3980 acf.debug('Inline fields were not a properly formatted JSON array', inlineFieldsAttr);
3981 }
3982
3983 /**
3984 * Get field type class name from field name
3985 */
3986 function getFieldTypeClassName(fieldName) {
3987 if (!fieldName || !blockFieldInfo) return '';
3988 const field = blockFieldInfo.find(f => f.name === fieldName);
3989 return field?.type ? field.type.replace(/_/g, '-') : '';
3990 }
3991
3992 /**
3993 * Get field label from field name
3994 */
3995 function getFieldLabel(fieldName) {
3996 if (!fieldName || !blockFieldInfo) return '';
3997 const field = blockFieldInfo.find(f => f.name === fieldName);
3998 return field ? field.label : '';
3999 }
4000
4001 // Clear selected field when content editable changes
4002 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
4003 setSelectedFieldKey(null);
4004 }, [contentEditableChangeInProgress]);
4005
4006 // Generate toolbar icon
4007 const toolbarIcon = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => {
4008 let icon = currentContentEditableElement && !currentInlineEditingElement ? currentContentEditableElement.getAttribute('data-acf-toolbar-icon') : null;
4009 if (!icon && currentInlineEditingElement) {
4010 icon = currentInlineEditingElement ? currentInlineEditingElement.getAttribute('data-acf-toolbar-icon') : null;
4011 }
4012 if (icon) {
4013 icon = /*#__PURE__*/React.createElement("i", {
4014 dangerouslySetInnerHTML: {
4015 __html: icon
4016 }
4017 });
4018 }
4019 if (!icon && currentContentEditableElement && !currentInlineEditingElement) {
4020 const fieldSlug = currentContentEditableElement.getAttribute('data-acf-inline-contenteditable-field-slug');
4021 icon = /*#__PURE__*/React.createElement("i", {
4022 className: `field-type-icon field-type-icon-${getFieldTypeClassName(fieldSlug)}`
4023 });
4024 }
4025 if (!icon) {
4026 icon = React.isValidElement(blockIcon) ? blockIcon : /*#__PURE__*/React.createElement("span", {
4027 className: `dashicon dashicons dashicons-${blockIcon}`
4028 });
4029 }
4030 return icon;
4031 }, [blockFieldInfo, currentContentEditableElement, currentInlineEditingElement]);
4032
4033 // Generate toolbar title
4034 const toolbarTitle = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useMemo)(() => {
4035 let fieldName;
4036 if (currentContentEditableElement && !currentInlineEditingElement) {
4037 const title = currentContentEditableElement.getAttribute('data-acf-toolbar-title');
4038 if (title) return title;
4039 fieldName = currentContentEditableElement.getAttribute('data-acf-inline-contenteditable-field-slug');
4040 } else if (currentInlineEditingElement) {
4041 const title = currentInlineEditingElement.getAttribute('data-acf-toolbar-title');
4042 if (title) return title;
4043 if (inlineFields.length > 1) {
4044 const elementTypeLabels = {
4045 A: 'Link',
4046 DIV: 'Division',
4047 P: 'Paragraph',
4048 SPAN: 'Span',
4049 INPUT: 'Input',
4050 BUTTON: 'Button',
4051 IMG: 'Image',
4052 UL: 'Unordered List',
4053 OL: 'Ordered List',
4054 LI: 'List Item',
4055 H1: 'Heading 1',
4056 H2: 'Heading 2',
4057 H3: 'Heading 3',
4058 H4: 'Heading 4',
4059 H5: 'Heading 5',
4060 H6: 'Heading 6',
4061 TABLE: 'Table',
4062 TR: 'Table Row',
4063 TD: 'Table Cell',
4064 TH: 'Table Header',
4065 FORM: 'Form',
4066 TEXTAREA: 'Text Area',
4067 SELECT: 'Select',
4068 OPTION: 'Option'
4069 };
4070 return elementTypeLabels[currentInlineEditingElement.tagName];
4071 }
4072 fieldName = typeof inlineFields[0] === 'object' ? inlineFields[0].fieldName : inlineFields[0];
4073 }
4074 return getFieldLabel(fieldName);
4075 }, [currentInlineEditingElement, currentContentEditableElement, blockFieldInfo]);
4076 return /*#__PURE__*/React.createElement(React.Fragment, null, selectedFieldKey && selectedFieldButtonRef && usePopover && currentInlineEditingElementUid && /*#__PURE__*/React.createElement(_popover_wrapper__WEBPACK_IMPORTED_MODULE_2__.PopoverWrapper, {
4077 focusOnMount: false,
4078 className: "acf-inline-fields-popover",
4079 anchor: selectedFieldButtonRef,
4080 onClose: event => {
4081 if (event.key === 'Escape') {
4082 setSelectedFieldKey(null);
4083 return true;
4084 }
4085 // Don't close if clicking inside the popover or anchor
4086 if (event?.target && fieldPopoverContainerRef?.current && fieldPopoverContainerRef?.current.contains(event.target) || selectedFieldButtonRef?.current && selectedFieldButtonRef?.current.contains(event?.target)) {
4087 return false;
4088 }
4089 return undefined;
4090 },
4091 variant: usePopover ? 'toolbar' : 'unstyled',
4092 gutenbergIframeOrDocument: gutenbergIframeOrDocument,
4093 hidePrimaryBlockToolbar: true,
4094 animate: true
4095 }, /*#__PURE__*/React.createElement("div", {
4096 ref: fieldPopoverContainerRef,
4097 onClick: () => {
4098 setInlineEditingToolbarHasFocus(true);
4099 }
4100 }, /*#__PURE__*/React.createElement("div", {
4101 className: "acf-inline-fields-popover-inner",
4102 style: {
4103 minWidth: selectedFieldConfig?.popoverMinWidth ? selectedFieldConfig?.popoverMinWidth : '300px'
4104 },
4105 ref: setCurrentBlockFormContainer
4106 }))), selectedFieldKey && selectedFieldButtonRef && !usePopover && currentInlineEditingElementUid && /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.Modal, {
4107 className: "acf-block-form-modal",
4108 isFullScreen: true,
4109 title: blockFieldInfo && selectedFieldKey ? blockFieldInfo.find(f => f.name === selectedFieldKey)?.label : '',
4110 onRequestClose: () => {
4111 setSelectedFieldKey(null);
4112 }
4113 }, /*#__PURE__*/React.createElement("div", {
4114 ref: fieldPopoverContainerRef,
4115 onClick: () => {
4116 setInlineEditingToolbarHasFocus(true);
4117 }
4118 }, /*#__PURE__*/React.createElement("div", {
4119 className: "acf-inline-fields-popover-inner",
4120 style: {
4121 minWidth: selectedFieldConfig?.popoverMinWidth ? selectedFieldConfig?.popoverMinWidth : '300px'
4122 },
4123 ref: setCurrentBlockFormContainer
4124 }))), /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.Toolbar, {
4125 orientation: "horizontal",
4126 className: "components-accessible-toolbar block-editor-block-contextual-toolbar",
4127 style: {
4128 width: 'max-content'
4129 }
4130 }, /*#__PURE__*/React.createElement("div", {
4131 className: "block-editor-block-toolbar"
4132 }, /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.ToolbarGroup, {
4133 style: {
4134 alignItems: 'center'
4135 }
4136 }, /*#__PURE__*/React.createElement("div", {
4137 className: "acf-blocks-toolbar-icon components-toolbar-group block-editor-block-toolbar__block-controls",
4138 label: toolbarTitle
4139 }, toolbarIcon, /*#__PURE__*/React.createElement("span", null, toolbarTitle))), /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.ToolbarGroup, null, (() => {
4140 if (!inlineFields || inlineFields.length === 0) {
4141 return null;
4142 }
4143 const buttons = inlineFields.map((field, index) => {
4144 let fieldName = '';
4145 let fieldIconSvg = null;
4146 let fieldLabel = null;
4147 if (typeof field === 'object') {
4148 fieldName = field.fieldName ? field.fieldName : index;
4149 fieldIconSvg = field.fieldIcon ? window.atob(field.fieldIcon) : null;
4150 fieldLabel = field.fieldLabel ? field.fieldLabel : fieldName;
4151 } else {
4152 fieldName = field;
4153 }
4154
4155 // Use 'edit' icon if field has useExpandedEditor flag
4156 if (!fieldIconSvg && field?.useExpandedEditor) {
4157 fieldIconSvg = 'edit';
4158 }
4159 return /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.ToolbarButton, {
4160 key: fieldName,
4161 disabled: contentEditableChangeInProgress,
4162 className: "acf-toolbar-button",
4163 icon: fieldIconSvg ? typeof fieldIconSvg === 'string' && fieldIconSvg === 'edit' ? 'edit' : /*#__PURE__*/React.createElement("i", {
4164 dangerouslySetInnerHTML: {
4165 __html: fieldIconSvg
4166 }
4167 }) : /*#__PURE__*/React.createElement("i", {
4168 className: `field-type-icon field-type-icon-${getFieldTypeClassName(fieldName)}`
4169 }),
4170 label: fieldLabel || getFieldLabel(fieldName),
4171 isPressed: fieldName === selectedFieldKey,
4172 ref: fieldName === selectedFieldKey ? setSelectedFieldButtonRef : null,
4173 onClick: () => {
4174 setInlineEditingToolbarHasFocus(true);
4175 if (selectedFieldKey === fieldName) {
4176 setSelectedFieldKey(null);
4177 } else {
4178 // Determine if we should use modal or popover
4179 setUsePopover(!field?.useExpandedEditor);
4180 setSelectedFieldKey(fieldName);
4181 setSelectedFieldConfig(field);
4182 }
4183 }
4184 });
4185 });
4186 return buttons;
4187 })()))), (() => {
4188 const styleContent = `[data-name="${selectedFieldKey}"]{ display: block!important; }`;
4189 return /*#__PURE__*/React.createElement("style", null, styleContent);
4190 })());
4191 };
4192
4193 /***/ }),
4194
4195 /***/ "./assets/src/js/pro/blocks-v3/components/jsx-parser.js":
4196 /*!**************************************************************!*\
4197 !*** ./assets/src/js/pro/blocks-v3/components/jsx-parser.js ***!
4198 \**************************************************************/
4199 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
4200
4201 "use strict";
4202 __webpack_require__.r(__webpack_exports__);
4203 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
4204 /* harmony export */ parseJSX: () => (/* binding */ parseJSX)
4205 /* harmony export */ });
4206 /* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! jquery */ "jquery");
4207 /* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_0__);
4208 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
4209 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_1__);
4210 /**
4211 * JSX Parser for ACF Blocks - Based on 6.7.0.2
4212 * Converts HTML strings to React/JSX elements for rendering in the block editor
4213 */
4214
4215 /* global acf, React, Text */
4216
4217 // eslint-disable-next-line import/no-unresolved -- WordPress provides jquery as an external script dependency.
4218
4219
4220 const useInnerBlocksProps = wp.blockEditor.__experimentalUseInnerBlocksProps || wp.blockEditor.useInnerBlocksProps;
4221
4222 /**
4223 * Gets the JSX-compatible name for an HTML attribute
4224 *
4225 * @param {string} attrName Attribute name.
4226 * @return {string} JSX-compatible attribute name.
4227 */
4228 function getJSXNameReplacement(attrName) {
4229 return acf.isget(acf, 'jsxNameReplacements', attrName) || attrName;
4230 }
4231
4232 /**
4233 * ACF InnerBlocks wrapper component
4234 *
4235 * @param {Object} props Component props.
4236 * @return {JSX.Element} InnerBlocks wrapper element.
4237 */
4238 function ACFInnerBlocksComponent(props) {
4239 const {
4240 className = 'acf-innerblocks-container'
4241 } = props;
4242 const innerBlocksProps = useInnerBlocksProps({
4243 className
4244 }, props);
4245 return (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createElement)('div', {
4246 ...innerBlocksProps,
4247 children: innerBlocksProps.children
4248 });
4249 }
4250
4251 /**
4252 * Script component for handling <script> tags
4253 */
4254 class ScriptComponent extends React.Component {
4255 render() {
4256 return (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createElement)('div', {
4257 ref: element => this.el = element
4258 });
4259 }
4260 setHTML(scriptContent) {
4261 jquery__WEBPACK_IMPORTED_MODULE_0___default()(this.el).html(`<script>${scriptContent}</script>`);
4262 }
4263 componentDidUpdate() {
4264 this.setHTML(this.props.children);
4265 }
4266 componentDidMount() {
4267 this.setHTML(this.props.children);
4268 }
4269 }
4270
4271 /**
4272 * Helper function to parse style attribute string into object
4273 *
4274 * @param {string} styleString Style attribute value.
4275 * @return {Object} Parsed style object.
4276 */
4277 function parseStyleAttribute(styleString) {
4278 const styleObj = {};
4279 if (!styleString) {
4280 return styleObj;
4281 }
4282 styleString.split(';').forEach(rule => {
4283 const colonIndex = rule.indexOf(':');
4284 if (colonIndex > 0) {
4285 let property = rule.substr(0, colonIndex).trim();
4286 const value = rule.substr(colonIndex + 1).trim();
4287
4288 // Convert to camelCase
4289 if (property.charAt(0) !== '-') {
4290 property = acf.strCamelCase(property);
4291 }
4292 styleObj[property] = value;
4293 }
4294 });
4295 return styleObj;
4296 }
4297
4298 /**
4299 * Parses and transforms a DOM attribute to React props format
4300 * Based on 6.7.0.2's implementation
4301 *
4302 * @param {Attr} attribute DOM attribute.
4303 * @return {Object} React prop name and value.
4304 */
4305 function parseAttribute(attribute) {
4306 let attrName = attribute.name;
4307 let attrValue = attribute.value;
4308
4309 // Allow custom filtering
4310 const customParsed = acf.applyFilters('acf_blocks_parse_node_attr', false, attribute);
4311 if (customParsed) {
4312 return customParsed;
4313 }
4314 switch (attrName) {
4315 case 'class':
4316 attrName = 'className';
4317 break;
4318 case 'style':
4319 const styleObj = {};
4320 attrValue.split(';').forEach(rule => {
4321 const colonIndex = rule.indexOf(':');
4322 if (colonIndex > 0) {
4323 let property = rule.substr(0, colonIndex).trim();
4324 const value = rule.substr(colonIndex + 1).trim();
4325 if (property.charAt(0) !== '-') {
4326 property = acf.strCamelCase(property);
4327 }
4328 styleObj[property] = value;
4329 }
4330 });
4331 attrValue = styleObj;
4332 break;
4333 default:
4334 // Skip data- attributes processing, keep them as-is
4335 if (attrName.indexOf('data-') === 0) {
4336 break;
4337 }
4338 attrName = getJSXNameReplacement(attrName);
4339 // Ignore values that merely start with a JSON-like character but
4340 // aren't valid JSON (e.g. an oEmbed title beginning with "[" or "{").
4341 const firstChar = attrValue.charAt(0);
4342 if (firstChar === '[' || firstChar === '{') {
4343 try {
4344 attrValue = JSON.parse(attrValue);
4345 } catch (err) {}
4346 }
4347 if (attrValue === 'true' || attrValue === 'false') {
4348 attrValue = attrValue === 'true';
4349 }
4350 }
4351 return {
4352 name: attrName,
4353 value: attrValue
4354 };
4355 }
4356
4357 /**
4358 * Main parseNodeToJSX function - Based on 6.7.0.2's function `O`
4359 * Recursively converts DOM nodes to React/JSX elements
4360 *
4361 * @param {Node} node DOM node.
4362 * @param {number} depth Recursion depth.
4363 * @return {JSX.Element|null} Parsed JSX element.
4364 */
4365 function parseNodeToJSX(node, depth = 0, callbacks = {}) {
4366 const {
4367 onNewInlineEditingElementSelected,
4368 onNewContentEditableElementSelected,
4369 blockFieldInfo
4370 } = callbacks;
4371 const selectInlineEditingElement = (uid, element = null) => {
4372 if (onNewInlineEditingElementSelected) {
4373 onNewInlineEditingElementSelected(uid);
4374 return;
4375 }
4376 if (acf.blockEdit?.setCurrentInlineEditingElementUid) {
4377 acf.blockEdit.setCurrentInlineEditingElementUid(uid);
4378 }
4379 if (acf.blockEdit?.setCurrentInlineEditingElement) {
4380 acf.blockEdit.setCurrentInlineEditingElement(element);
4381 }
4382 };
4383 const selectContentEditableElement = (fieldSlug, element = null) => {
4384 if (onNewContentEditableElementSelected) {
4385 onNewContentEditableElementSelected(fieldSlug);
4386 return;
4387 }
4388 if (acf.blockEdit?.setCurrentContentEditableElement) {
4389 acf.blockEdit.setCurrentContentEditableElement(element);
4390 }
4391 };
4392
4393 // Determine component type
4394 const nodeName = node.nodeName.toLowerCase();
4395 let componentType;
4396 switch (nodeName) {
4397 case 'innerblocks':
4398 componentType = 'ACFInnerBlocks';
4399 break;
4400 case 'script':
4401 componentType = ScriptComponent;
4402 break;
4403 case '#comment':
4404 return null;
4405 default:
4406 componentType = getJSXNameReplacement(nodeName);
4407 }
4408 if (!componentType) {
4409 return null;
4410 }
4411 const props = {};
4412
4413 // Add ref to first-level elements (except ACFInnerBlocks)
4414 if (depth === 1 && componentType !== 'ACFInnerBlocks') {
4415 props.ref = (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createRef)();
4416 }
4417
4418 // Parse attributes
4419 acf.arrayArgs(node.attributes).map(parseAttribute).forEach(({
4420 name,
4421 value
4422 }) => {
4423 props[name] = value;
4424 });
4425
4426 // Handle data-acf-inline-fields attributes
4427 if (node.hasAttribute('data-acf-inline-fields')) {
4428 props.style = {
4429 ...parseStyleAttribute(node.getAttribute('style') || ''),
4430 pointerEvents: 'all'
4431 };
4432 props.role = 'button';
4433 props.tabIndex = 0;
4434 props.onFocus = event => {
4435 event.stopPropagation();
4436 const uid = node.attributes.getNamedItem('data-acf-inline-fields-uid').value;
4437 selectInlineEditingElement(uid, event.currentTarget);
4438 };
4439 props.onMouseDown = event => event.stopPropagation();
4440 props.onClick = event => {
4441 event.stopPropagation();
4442 const link = event.target.closest('a');
4443 if (link && link.tagName === 'A') {
4444 event.preventDefault();
4445 acf.debug(`Navigation prevented for ${link.href}`);
4446 }
4447 if (!event.target.hasAttribute('data-acf-inline-contenteditable')) {
4448 const uid = node.attributes.getNamedItem('data-acf-inline-fields-uid').value;
4449 selectInlineEditingElement(uid, event.currentTarget);
4450 }
4451 };
4452 props.onKeyDown = event => {
4453 if (event.key === 'Tab' && event.shiftKey) {
4454 event.preventDefault();
4455 const toolbar = document.querySelector('.acf-inline-editing-toolbar');
4456 const button = toolbar?.querySelector('button');
4457 if (button) {
4458 button.focus();
4459 const uid = node.attributes.getNamedItem('data-acf-inline-fields-uid').value;
4460 selectInlineEditingElement(uid, event.currentTarget);
4461 }
4462 }
4463 if (event.key === 'Enter') {
4464 event.stopPropagation();
4465 const link = event.target.closest('a');
4466 if (link && link.tagName === 'A') {
4467 event.preventDefault();
4468 acf.debug(`Navigation prevented for ${link.href}`);
4469 }
4470 const uid = node.attributes.getNamedItem('data-acf-inline-fields-uid').value;
4471 selectInlineEditingElement(uid, event.currentTarget);
4472 }
4473 };
4474 }
4475
4476 // Handle data-acf-inline-contenteditable attributes
4477 if (node.hasAttribute('data-acf-inline-contenteditable')) {
4478 const fieldSlug = node.attributes.getNamedItem('data-acf-inline-contenteditable-field-slug').value;
4479 const fieldInfo = blockFieldInfo || acf.blockEdit?.getBlockFieldInfo?.();
4480 const editableFields = fieldInfo ? fieldInfo.filter(field => field.name === fieldSlug && (field.type === 'text' || field.type === 'textarea')) : [];
4481 if (editableFields.length > 0) {
4482 props.contentEditable = true;
4483 props.suppressContentEditableWarning = true;
4484 props.role = 'input';
4485 props.tabIndex = 0;
4486 props.onFocus = event => {
4487 const link = event.target.closest('a');
4488 if (link && link.tagName === 'A') {
4489 event.preventDefault();
4490 acf.debug(`Navigation prevented for ${link.href}`);
4491 }
4492 event.stopPropagation();
4493 selectContentEditableElement(fieldSlug, event.currentTarget);
4494 if (node.hasAttribute('data-acf-inline-fields')) {
4495 const uid = node.attributes.getNamedItem('data-acf-inline-fields-uid').value;
4496 selectInlineEditingElement(uid, event.currentTarget);
4497 } else {
4498 selectInlineEditingElement(null);
4499 }
4500 };
4501 props.onPaste = event => {
4502 event.preventDefault();
4503 const text = event.clipboardData.getData('text/plain');
4504 event.currentTarget.textContent = event.currentTarget.textContent + text;
4505 };
4506 } else {
4507 // Remove invalid contentEditable attributes
4508 delete props['data-acf-inline-contenteditable-field-slug'];
4509 delete props['data-acf-inline-contenteditable'];
4510 }
4511 }
4512
4513 // Add click handler to clear selection if clicking outside inline fields
4514 if (!node.hasAttribute('data-acf-inline-fields') && !node.hasAttribute('data-acf-inline-contenteditable')) {
4515 props.onClick = event => {
4516 if (event.target === event.currentTarget) {
4517 selectInlineEditingElement(null);
4518 selectContentEditableElement(null);
4519 }
4520 };
4521 }
4522
4523 // Handle ACFInnerBlocks component
4524 if (componentType === 'ACFInnerBlocks') {
4525 return (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createElement)(ACFInnerBlocksComponent, {
4526 ...props
4527 });
4528 }
4529
4530 // Build element with children
4531 const elementArgs = [componentType, props];
4532 acf.arrayArgs(node.childNodes).forEach(childNode => {
4533 if (childNode instanceof Text) {
4534 const textContent = childNode.textContent;
4535 if (textContent) {
4536 elementArgs.push(textContent);
4537 }
4538 } else {
4539 elementArgs.push(parseNodeToJSX(childNode, depth + 1, callbacks));
4540 }
4541 });
4542 const element = _wordpress_element__WEBPACK_IMPORTED_MODULE_1__.createElement.apply(this, elementArgs);
4543 return element;
4544 }
4545
4546 // Preserve the legacy parser for v1/v2 blocks, which pass the block version
4547 // as the second argument.
4548 const legacyParseJSX = acf.parseJSX;
4549
4550 /**
4551 * Main parseJSX function exposed on the acf global object
4552 * Matches 6.7.0.2's implementation exactly
4553 *
4554 * @param {string} htmlString HTML string.
4555 * @param {Function} onNewInlineEditingElementSelected Inline element selection callback or jQuery parser.
4556 * @param {Function} onContentEditableChange ContentEditable change callback.
4557 * @param {Function} onNewContentEditableElementSelected ContentEditable selection callback.
4558 * @param {Array} blockFieldInfo Field metadata.
4559 * @param {Function} $ jQuery-compatible parser.
4560 * @return {JSX.Element|Array} Parsed JSX children.
4561 */
4562 function parseJSX(htmlString, onNewInlineEditingElementSelected = (jquery__WEBPACK_IMPORTED_MODULE_0___default()), onContentEditableChange = null, onNewContentEditableElementSelected = null, blockFieldInfo = null, $ = null) {
4563 if (typeof onNewInlineEditingElementSelected === 'number' && typeof legacyParseJSX === 'function') {
4564 return legacyParseJSX(htmlString, onNewInlineEditingElementSelected);
4565 }
4566 const isJQueryParser = typeof onNewInlineEditingElementSelected === 'function' && (arguments.length === 2 || onNewInlineEditingElementSelected.fn && onNewInlineEditingElementSelected.fn.jquery);
4567 const parser = isJQueryParser ? onNewInlineEditingElementSelected : $ || (jquery__WEBPACK_IMPORTED_MODULE_0___default());
4568 const callbacks = isJQueryParser ? {} : {
4569 onNewInlineEditingElementSelected,
4570 onContentEditableChange,
4571 onNewContentEditableElementSelected,
4572 blockFieldInfo
4573 };
4574
4575 // Wrap in div to ensure valid HTML structure
4576 htmlString = '<div>' + htmlString + '</div>';
4577
4578 // Handle self-closing InnerBlocks tags
4579 htmlString = htmlString.replace(/<InnerBlocks([^>]+)?\/>/, '<InnerBlocks$1></InnerBlocks>');
4580
4581 // Parse with jQuery, convert to React, and extract children from wrapper div
4582 const parsedElement = parseNodeToJSX(parser(htmlString)[0], 0, callbacks);
4583 return parsedElement.props.children;
4584 }
4585 acf.parseJSXV3 = parseJSX;
4586 acf.parseJSX = parseJSX;
4587
4588 /***/ }),
4589
4590 /***/ "./assets/src/js/pro/blocks-v3/components/popover-wrapper.js":
4591 /*!*******************************************************************!*\
4592 !*** ./assets/src/js/pro/blocks-v3/components/popover-wrapper.js ***!
4593 \*******************************************************************/
4594 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
4595
4596 "use strict";
4597 __webpack_require__.r(__webpack_exports__);
4598 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
4599 /* harmony export */ PopoverWrapper: () => (/* binding */ PopoverWrapper)
4600 /* harmony export */ });
4601 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @wordpress/element */ "@wordpress/element");
4602 /* harmony import */ var _wordpress_element__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_wordpress_element__WEBPACK_IMPORTED_MODULE_0__);
4603 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @wordpress/components */ "@wordpress/components");
4604 /* harmony import */ var _wordpress_components__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__);
4605 /**
4606 * PopoverWrapper Component
4607 * Custom Popover wrapper that handles inline editing toolbar behavior
4608 * - Escape key to close
4609 * - Click outside to close
4610 * - Focus management
4611 * - Hiding primary block toolbar when active
4612 */
4613
4614
4615
4616
4617 /**
4618 * PopoverWrapper component
4619 * Wraps the WordPress Popover component with custom event handlers
4620 *
4621 * @param {Object} props - Component props
4622 * @param {React.ReactNode} props.children - Child elements to render inside popover
4623 * @param {string} props.className - CSS class for the popover
4624 * @param {Element|null} props.anchor - Anchor element for positioning
4625 * @param {string} props.placement - Placement relative to anchor (e.g., 'top-start')
4626 * @param {Function} props.onClose - Callback when popover should close
4627 * @param {boolean|string} props.focusOnMount - Whether to focus on mount
4628 * @param {string} props.variant - Popover variant (e.g., 'unstyled')
4629 * @param {boolean} props.animate - Whether to animate popover
4630 * @param {Document|HTMLIFrameElement} props.gutenbergIframeOrDocument - Document or iframe reference
4631 * @param {boolean} props.hidePrimaryBlockToolbar - Whether to hide the primary block toolbar
4632 * @returns {JSX.Element} - Wrapped Popover component
4633 */
4634 const PopoverWrapper = ({
4635 children,
4636 className,
4637 anchor,
4638 placement,
4639 onClose,
4640 focusOnMount,
4641 variant,
4642 animate = false,
4643 gutenbergIframeOrDocument,
4644 hidePrimaryBlockToolbar = false
4645 }) => {
4646 (0,_wordpress_element__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => {
4647 const doc = gutenbergIframeOrDocument?.contentDocument ? gutenbergIframeOrDocument.contentDocument : gutenbergIframeOrDocument || document;
4648 const handleEscapeKey = event => {
4649 if (event.key === 'Escape') {
4650 onClose?.(event);
4651 }
4652 };
4653 const handleClickOutside = event => {
4654 if (event?.key) {
4655 return;
4656 }
4657 const popoverSelector = className ? `.${className.split(' ').filter(Boolean).join('.')}` : null;
4658 const popoverElement = popoverSelector ? event.target.closest(popoverSelector) : null;
4659 if (!popoverElement) {
4660 onClose?.(event);
4661 }
4662 };
4663 doc.addEventListener('keydown', handleEscapeKey, true);
4664 doc.addEventListener('mousedown', handleClickOutside, true);
4665 if (hidePrimaryBlockToolbar) {
4666 const toolbar = doc.querySelector('.block-editor-block-list__block.is-selected > .block-editor-block-contextual-toolbar');
4667 if (toolbar) {
4668 toolbar.style.display = 'none';
4669 }
4670 }
4671 return () => {
4672 doc.removeEventListener('keydown', handleEscapeKey, true);
4673 doc.removeEventListener('mousedown', handleClickOutside, true);
4674 if (hidePrimaryBlockToolbar) {
4675 const toolbar = doc.querySelector('.block-editor-block-list__block.is-selected > .block-editor-block-contextual-toolbar');
4676 if (toolbar) {
4677 toolbar.style.display = '';
4678 }
4679 }
4680 };
4681 }, [gutenbergIframeOrDocument, onClose, className, hidePrimaryBlockToolbar]);
4682 return /*#__PURE__*/React.createElement(_wordpress_components__WEBPACK_IMPORTED_MODULE_1__.Popover, {
4683 focusOnMount: focusOnMount,
4684 variant: variant,
4685 anchor: anchor,
4686 className: className,
4687 placement: placement,
4688 animate: animate
4689 }, hidePrimaryBlockToolbar && /*#__PURE__*/React.createElement("style", null, `
4690 .components-popover.block-editor-block-popover.block-editor-block-list__block-popover{
4691 display: none!important;
4692 }
4693 `), children);
4694 };
4695
4696 /***/ }),
4697
4698 /***/ "./assets/src/js/pro/blocks-v3/high-order-components/with-align-content.js":
4699 /*!*********************************************************************************!*\
4700 !*** ./assets/src/js/pro/blocks-v3/high-order-components/with-align-content.js ***!
4701 \*********************************************************************************/
4702 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
4703
4704 "use strict";
4705 __webpack_require__.r(__webpack_exports__);
4706 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
4707 /* harmony export */ withAlignContent: () => (/* binding */ withAlignContent)
4708 /* harmony export */ });
4709 /**
4710 * withAlignContent Higher-Order Component
4711 * Adds content alignment toolbar controls to ACF blocks
4712 * Supports both vertical alignment and matrix alignment (horizontal + vertical)
4713 */
4714
4715 const {
4716 Fragment,
4717 Component
4718 } = wp.element;
4719 const {
4720 BlockControls,
4721 BlockVerticalAlignmentToolbar
4722 } = wp.blockEditor;
4723
4724 // Matrix alignment control (experimental)
4725 const BlockAlignmentMatrixControl = wp.blockEditor.__experimentalBlockAlignmentMatrixControl || wp.blockEditor.BlockAlignmentMatrixControl;
4726 const BlockAlignmentMatrixToolbar = wp.blockEditor.__experimentalBlockAlignmentMatrixToolbar || wp.blockEditor.BlockAlignmentMatrixToolbar;
4727
4728 /**
4729 * Normalizes vertical alignment value
4730 *
4731 * @param {string} alignment - Alignment value
4732 * @returns {string} - Normalized alignment (top, center, or bottom)
4733 */
4734 const normalizeVerticalAlignment = alignment => {
4735 return ['top', 'center', 'bottom'].includes(alignment) ? alignment : 'top';
4736 };
4737
4738 /**
4739 * Gets the default horizontal alignment based on RTL setting
4740 *
4741 * @param {string} alignment - Current alignment value
4742 * @returns {string} - Normalized alignment value (left, center, or right)
4743 */
4744 const getDefaultHorizontalAlignment = alignment => {
4745 const defaultAlign = acf.get('rtl') ? 'right' : 'left';
4746 return ['left', 'center', 'right'].includes(alignment) ? alignment : defaultAlign;
4747 };
4748
4749 /**
4750 * Normalizes matrix alignment value (vertical + horizontal)
4751 * Format: "top left", "center center", etc.
4752 *
4753 * @param {string} alignment - Alignment value
4754 * @returns {string} - Normalized matrix alignment
4755 */
4756 const normalizeMatrixAlignment = alignment => {
4757 if (alignment) {
4758 const [vertical, horizontal] = alignment.split(' ');
4759 return `${normalizeVerticalAlignment(vertical)} ${getDefaultHorizontalAlignment(horizontal)}`;
4760 }
4761 return 'center center';
4762 };
4763
4764 /**
4765 * Higher-order component that adds content alignment controls
4766 * Supports either vertical-only or matrix (2D) alignment based on block config
4767 *
4768 * @param {React.Component} BlockComponent - The component to wrap
4769 * @param {Object} blockConfig - ACF block configuration
4770 * @returns {React.Component} - Enhanced component with content alignment controls
4771 */
4772 const withAlignContent = (BlockComponent, blockConfig) => {
4773 let AlignmentControl;
4774 let normalizeAlignment;
4775
4776 // Determine which alignment control to use based on block supports
4777 if (blockConfig.supports.align_content === 'matrix' || blockConfig.supports.alignContent === 'matrix') {
4778 // Use matrix control (horizontal + vertical)
4779 AlignmentControl = BlockAlignmentMatrixControl || BlockAlignmentMatrixToolbar;
4780 normalizeAlignment = normalizeMatrixAlignment;
4781 } else {
4782 // Use vertical-only control
4783 AlignmentControl = BlockVerticalAlignmentToolbar;
4784 normalizeAlignment = normalizeVerticalAlignment;
4785 }
4786
4787 // If alignment control is not available, return original component
4788 if (AlignmentControl === undefined) {
4789 return BlockComponent;
4790 }
4791
4792 // Set default alignment on block config
4793 blockConfig.alignContent = normalizeAlignment(blockConfig.alignContent);
4794 return class extends Component {
4795 render() {
4796 const {
4797 attributes,
4798 setAttributes
4799 } = this.props;
4800 const {
4801 alignContent
4802 } = attributes;
4803 return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(BlockControls, {
4804 group: "block"
4805 }, /*#__PURE__*/React.createElement(AlignmentControl, {
4806 label: acf.__('Change content alignment'),
4807 value: normalizeAlignment(alignContent),
4808 onChange: function (newAlignment) {
4809 setAttributes({
4810 alignContent: normalizeAlignment(newAlignment)
4811 });
4812 }
4813 })), /*#__PURE__*/React.createElement(BlockComponent, this.props));
4814 }
4815 };
4816 };
4817
4818 /***/ }),
4819
4820 /***/ "./assets/src/js/pro/blocks-v3/high-order-components/with-align-text.js":
4821 /*!******************************************************************************!*\
4822 !*** ./assets/src/js/pro/blocks-v3/high-order-components/with-align-text.js ***!
4823 \******************************************************************************/
4824 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
4825
4826 "use strict";
4827 __webpack_require__.r(__webpack_exports__);
4828 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
4829 /* harmony export */ withAlignText: () => (/* binding */ withAlignText)
4830 /* harmony export */ });
4831 /**
4832 * withAlignText Higher-Order Component
4833 * Adds text alignment toolbar controls to ACF blocks
4834 */
4835
4836 const {
4837 Fragment,
4838 Component
4839 } = wp.element;
4840 const {
4841 BlockControls,
4842 AlignmentToolbar
4843 } = wp.blockEditor;
4844
4845 /**
4846 * Gets the default text alignment based on RTL setting
4847 *
4848 * @param {string} alignment - Current alignment value
4849 * @returns {string} - Normalized alignment value (left, center, or right)
4850 */
4851 const getDefaultAlignment = alignment => {
4852 const defaultAlign = acf.get('rtl') ? 'right' : 'left';
4853 return ['left', 'center', 'right'].includes(alignment) ? alignment : defaultAlign;
4854 };
4855
4856 /**
4857 * Higher-order component that adds text alignment controls
4858 * Wraps a block component and adds AlignmentToolbar to BlockControls
4859 *
4860 * @param {React.Component} BlockComponent - The component to wrap
4861 * @param {Object} blockConfig - ACF block configuration
4862 * @returns {React.Component} - Enhanced component with text alignment controls
4863 */
4864 const withAlignText = (BlockComponent, blockConfig) => {
4865 const normalizeAlignment = getDefaultAlignment;
4866
4867 // Set default alignment on block config
4868 blockConfig.alignText = normalizeAlignment(blockConfig.alignText);
4869 return class extends Component {
4870 render() {
4871 const {
4872 attributes,
4873 setAttributes
4874 } = this.props;
4875 const {
4876 alignText
4877 } = attributes;
4878 return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(BlockControls, {
4879 group: "block"
4880 }, /*#__PURE__*/React.createElement(AlignmentToolbar, {
4881 value: normalizeAlignment(alignText),
4882 onChange: function (newAlignment) {
4883 setAttributes({
4884 alignText: normalizeAlignment(newAlignment)
4885 });
4886 }
4887 })), /*#__PURE__*/React.createElement(BlockComponent, this.props));
4888 }
4889 };
4890 };
4891
4892 /***/ }),
4893
4894 /***/ "./assets/src/js/pro/blocks-v3/high-order-components/with-full-height.js":
4895 /*!*******************************************************************************!*\
4896 !*** ./assets/src/js/pro/blocks-v3/high-order-components/with-full-height.js ***!
4897 \*******************************************************************************/
4898 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
4899
4900 "use strict";
4901 __webpack_require__.r(__webpack_exports__);
4902 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
4903 /* harmony export */ withFullHeight: () => (/* binding */ withFullHeight)
4904 /* harmony export */ });
4905 /**
4906 * withFullHeight Higher-Order Component
4907 * Adds full height toggle control to ACF blocks
4908 */
4909
4910 const {
4911 Fragment,
4912 Component
4913 } = wp.element;
4914 const {
4915 BlockControls
4916 } = wp.blockEditor;
4917
4918 // Full height control (experimental)
4919 const BlockFullHeightAlignmentControl = wp.blockEditor.__experimentalBlockFullHeightAligmentControl || wp.blockEditor.__experimentalBlockFullHeightAlignmentControl || wp.blockEditor.BlockFullHeightAlignmentControl;
4920
4921 /**
4922 * Higher-order component that adds full height toggle controls
4923 * Allows blocks to expand to full available height
4924 *
4925 * @param {React.Component} BlockComponent - The component to wrap
4926 * @returns {React.Component} - Enhanced component with full height controls
4927 */
4928 const withFullHeight = BlockComponent => {
4929 // If control is not available, return original component
4930 if (!BlockFullHeightAlignmentControl) {
4931 return BlockComponent;
4932 }
4933 return class extends Component {
4934 render() {
4935 const {
4936 attributes,
4937 setAttributes
4938 } = this.props;
4939 const {
4940 fullHeight
4941 } = attributes;
4942 return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement(BlockControls, {
4943 group: "block"
4944 }, /*#__PURE__*/React.createElement(BlockFullHeightAlignmentControl, {
4945 isActive: fullHeight,
4946 onToggle: function (newValue) {
4947 setAttributes({
4948 fullHeight: newValue
4949 });
4950 }
4951 })), /*#__PURE__*/React.createElement(BlockComponent, this.props));
4952 }
4953 };
4954 };
4955
4956 /***/ }),
4957
4958 /***/ "./assets/src/js/pro/blocks-v3/register-block-type-v3.js":
4959 /*!***************************************************************!*\
4960 !*** ./assets/src/js/pro/blocks-v3/register-block-type-v3.js ***!
4961 \***************************************************************/
4962 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
4963
4964 "use strict";
4965 __webpack_require__.r(__webpack_exports__);
4966 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
4967 /* harmony export */ getRegisteredBlock: () => (/* binding */ getRegisteredBlock),
4968 /* harmony export */ registerACFBlockType: () => (/* binding */ registerACFBlockType)
4969 /* harmony export */ });
4970 /* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! jquery */ "jquery");
4971 /* harmony import */ var jquery__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(jquery__WEBPACK_IMPORTED_MODULE_0__);
4972 /* harmony import */ var _components_block_edit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./components/block-edit */ "./assets/src/js/pro/blocks-v3/components/block-edit.js");
4973 /* harmony import */ var _high_order_components_with_align_text__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./high-order-components/with-align-text */ "./assets/src/js/pro/blocks-v3/high-order-components/with-align-text.js");
4974 /* harmony import */ var _high_order_components_with_align_content__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./high-order-components/with-align-content */ "./assets/src/js/pro/blocks-v3/high-order-components/with-align-content.js");
4975 /* harmony import */ var _high_order_components_with_full_height__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./high-order-components/with-full-height */ "./assets/src/js/pro/blocks-v3/high-order-components/with-full-height.js");
4976 function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
4977 /**
4978 * ACF Block Type Registration - Version 3
4979 * Handles registration of ACF blocks (version 3) with WordPress Gutenberg
4980 * Includes attribute setup, higher-order component composition, and block filtering
4981 */
4982
4983
4984
4985
4986
4987
4988 const {
4989 InnerBlocks
4990 } = wp.blockEditor;
4991 const {
4992 Component
4993 } = wp.element;
4994 const {
4995 createHigherOrderComponent
4996 } = wp.compose;
4997
4998 // Registry to store registered block configurations
4999 const registeredBlocks = {};
5000
5001 /**
5002 * Adds an attribute to the block configuration
5003 *
5004 * @param {Object} attributes - Existing attributes object
5005 * @param {string} attributeName - Name of the attribute to add
5006 * @param {string} attributeType - Type of the attribute (string, boolean, etc.)
5007 * @returns {Object} - Updated attributes object
5008 */
5009 const addAttribute = (attributes, attributeName, attributeType) => {
5010 attributes[attributeName] = {
5011 type: attributeType
5012 };
5013 return attributes;
5014 };
5015
5016 /**
5017 * Checks if block should be registered for current post type
5018 *
5019 * @param {Object} blockConfig - Block configuration
5020 * @returns {boolean} - True if block should be registered
5021 */
5022 function shouldRegisterBlock(blockConfig) {
5023 const allowedPostTypes = blockConfig.post_types || [];
5024 if (allowedPostTypes.length) {
5025 // Always allow in reusable blocks
5026 allowedPostTypes.push('wp_block');
5027 const currentPostType = acf.get('postType');
5028 if (!allowedPostTypes.includes(currentPostType)) {
5029 return false;
5030 }
5031 }
5032 return true;
5033 }
5034
5035 /**
5036 * Processes and normalizes block icon
5037 *
5038 * @param {Object} blockConfig - Block configuration
5039 */
5040 function processBlockIcon(blockConfig) {
5041 // Convert SVG string to JSX element
5042 if (typeof blockConfig.icon === 'string' && blockConfig.icon.substr(0, 4) === '<svg') {
5043 const iconSvg = blockConfig.icon;
5044 blockConfig.icon = /*#__PURE__*/React.createElement("div", {
5045 dangerouslySetInnerHTML: {
5046 __html: iconSvg
5047 }
5048 });
5049 }
5050
5051 // Remove icon if empty/invalid
5052 if (!blockConfig.icon) {
5053 delete blockConfig.icon;
5054 }
5055 }
5056
5057 /**
5058 * Validates and normalizes block category
5059 * Falls back to 'common' if category doesn't exist
5060 *
5061 * @param {Object} blockConfig - Block configuration
5062 */
5063 function validateBlockCategory(blockConfig) {
5064 const categoryExists = wp.blocks.getCategories().filter(({
5065 slug
5066 }) => slug === blockConfig.category).pop();
5067 if (!categoryExists) {
5068 blockConfig.category = 'common';
5069 }
5070 }
5071
5072 /**
5073 * Sets default values for block configuration
5074 *
5075 * @param {Object} blockConfig - Block configuration
5076 * @returns {Object} - Block configuration with defaults applied
5077 */
5078 function applyBlockDefaults(blockConfig) {
5079 return acf.parseArgs(blockConfig, {
5080 title: '',
5081 name: '',
5082 category: '',
5083 api_version: 2,
5084 acf_block_version: 3,
5085 attributes: {},
5086 supports: {}
5087 });
5088 }
5089
5090 /**
5091 * Cleans up block attributes
5092 * Removes empty default values
5093 *
5094 * @param {Object} blockConfig - Block configuration
5095 */
5096 function cleanBlockAttributes(blockConfig) {
5097 for (const attributeName in blockConfig.attributes) {
5098 if ('default' in blockConfig.attributes[attributeName] && blockConfig.attributes[attributeName].default.length === 0) {
5099 delete blockConfig.attributes[attributeName].default;
5100 }
5101 }
5102 }
5103
5104 /**
5105 * Configures anchor support if enabled
5106 *
5107 * @param {Object} blockConfig - Block configuration
5108 */
5109 function configureAnchorSupport(blockConfig) {
5110 if (blockConfig.supports && blockConfig.supports.anchor) {
5111 blockConfig.attributes.anchor = {
5112 type: 'string'
5113 };
5114 }
5115 }
5116
5117 /**
5118 * Applies higher-order components based on block supports
5119 *
5120 * @param {React.Component} EditComponent - Base edit component
5121 * @param {Object} blockConfig - Block configuration
5122 * @returns {React.Component} - Enhanced edit component
5123 */
5124 function applyHigherOrderComponents(EditComponent, blockConfig) {
5125 let enhancedComponent = EditComponent;
5126
5127 // Add text alignment support
5128 if (blockConfig.supports.alignText || blockConfig.supports.align_text) {
5129 blockConfig.attributes = addAttribute(blockConfig.attributes, 'align_text', 'string');
5130 enhancedComponent = (0,_high_order_components_with_align_text__WEBPACK_IMPORTED_MODULE_2__.withAlignText)(enhancedComponent, blockConfig);
5131 }
5132
5133 // Add content alignment support
5134 if (blockConfig.supports.alignContent || blockConfig.supports.align_content) {
5135 blockConfig.attributes = addAttribute(blockConfig.attributes, 'align_content', 'string');
5136 enhancedComponent = (0,_high_order_components_with_align_content__WEBPACK_IMPORTED_MODULE_3__.withAlignContent)(enhancedComponent, blockConfig);
5137 }
5138
5139 // Add full height support
5140 if (blockConfig.supports.fullHeight || blockConfig.supports.full_height) {
5141 blockConfig.attributes = addAttribute(blockConfig.attributes, 'full_height', 'boolean');
5142 enhancedComponent = (0,_high_order_components_with_full_height__WEBPACK_IMPORTED_MODULE_4__.withFullHeight)(enhancedComponent);
5143 }
5144 return enhancedComponent;
5145 }
5146
5147 /**
5148 * Registers an ACF block type (version 3) with WordPress
5149 *
5150 * @param {Object} blockConfig - ACF block configuration object
5151 * @returns {Object|boolean} - Registered block type or false if not registered
5152 */
5153 function registerACFBlockType(blockConfig) {
5154 // Check if block should be registered for current post type
5155 if (!shouldRegisterBlock(blockConfig)) {
5156 return false;
5157 }
5158
5159 // Process icon
5160 processBlockIcon(blockConfig);
5161
5162 // Validate category
5163 validateBlockCategory(blockConfig);
5164
5165 // Apply default values
5166 blockConfig = applyBlockDefaults(blockConfig);
5167
5168 // Clean up attributes
5169 cleanBlockAttributes(blockConfig);
5170
5171 // Configure anchor support
5172 configureAnchorSupport(blockConfig);
5173
5174 // Start with base BlockEdit component
5175 let EditComponent = _components_block_edit__WEBPACK_IMPORTED_MODULE_1__.BlockEdit;
5176
5177 // Apply higher-order components based on supports
5178 EditComponent = applyHigherOrderComponents(EditComponent, blockConfig);
5179
5180 // Create edit function that passes blockConfig and jQuery
5181 blockConfig.edit = function (props) {
5182 return /*#__PURE__*/React.createElement(EditComponent, _extends({}, props, {
5183 blockType: blockConfig,
5184 $: (jquery__WEBPACK_IMPORTED_MODULE_0___default())
5185 }));
5186 };
5187
5188 // Create save function (ACF blocks save to post content as HTML comments)
5189 blockConfig.save = () => /*#__PURE__*/React.createElement(InnerBlocks.Content, null);
5190
5191 // Store in registry
5192 registeredBlocks[blockConfig.name] = blockConfig;
5193
5194 // Register with WordPress
5195 const registeredBlockType = wp.blocks.registerBlockType(blockConfig.name, blockConfig);
5196
5197 // Ensure anchor attribute is properly configured
5198 if (registeredBlockType && registeredBlockType.attributes && registeredBlockType.attributes.anchor) {
5199 registeredBlockType.attributes.anchor = {
5200 type: 'string'
5201 };
5202 }
5203 return registeredBlockType;
5204 }
5205
5206 /**
5207 * Retrieves a registered block configuration by name
5208 *
5209 * @param {string} blockName - Name of the block
5210 * @returns {Object|boolean} - Block configuration or false
5211 */
5212 function getRegisteredBlock(blockName) {
5213 return registeredBlocks[blockName] || false;
5214 }
5215
5216 /**
5217 * Higher-order component to migrate legacy attribute names to new format
5218 * Handles backward compatibility for align_text -> alignText, etc.
5219 */
5220 const withDefaultAttributes = createHigherOrderComponent(BlockListBlock => class extends Component {
5221 constructor(props) {
5222 super(props);
5223 const {
5224 name,
5225 attributes
5226 } = this.props;
5227 const blockConfig = getRegisteredBlock(name);
5228 if (!blockConfig) return;
5229
5230 // Remove empty string attributes
5231 Object.keys(attributes).forEach(key => {
5232 if (attributes[key] === '') {
5233 delete attributes[key];
5234 }
5235 });
5236
5237 // Map old attribute names to new camelCase names
5238 const attributeMap = {
5239 full_height: 'fullHeight',
5240 align_content: 'alignContent',
5241 align_text: 'alignText'
5242 };
5243 Object.keys(attributeMap).forEach(oldKey => {
5244 const newKey = attributeMap[oldKey];
5245 if (attributes[oldKey] !== undefined) {
5246 // Migrate old key to new key
5247 attributes[newKey] = attributes[oldKey];
5248 } else if (attributes[newKey] === undefined && blockConfig[oldKey] !== undefined) {
5249 // Set default from block config if not present
5250 attributes[newKey] = blockConfig[oldKey];
5251 }
5252
5253 // Clean up old attribute names
5254 delete blockConfig[oldKey];
5255 delete attributes[oldKey];
5256 });
5257
5258 // Apply default values from block config for missing attributes
5259 for (let key in blockConfig.attributes) {
5260 if (attributes[key] === undefined && blockConfig[key] !== undefined) {
5261 attributes[key] = blockConfig[key];
5262 }
5263 }
5264 }
5265 render() {
5266 return /*#__PURE__*/React.createElement(BlockListBlock, this.props);
5267 }
5268 }, 'withDefaultAttributes');
5269
5270 /**
5271 * Initialize ACF blocks on the 'prepare' action
5272 * Registers all ACF blocks with version 3 or higher
5273 */
5274 acf.addAction('prepare', function () {
5275 // Ensure wp.blockEditor exists (backward compatibility)
5276 if (!wp.blockEditor) {
5277 wp.blockEditor = wp.editor;
5278 }
5279 const blockTypes = acf.get('blockTypes');
5280 if (blockTypes) {
5281 blockTypes.forEach(blockType => {
5282 // Only register blocks with version 3 or higher
5283 if (parseInt(blockType.acf_block_version) >= 3) {
5284 registerACFBlockType(blockType);
5285 }
5286 });
5287 }
5288 });
5289
5290 /**
5291 * Register WordPress filter for attribute migration
5292 * Ensures backward compatibility with legacy attribute names
5293 */
5294 wp.hooks.addFilter('editor.BlockListBlock', 'acf/with-default-attributes', withDefaultAttributes);
5295
5296 // Export for testing/external use
5297
5298
5299 /***/ }),
5300
5301 /***/ "./assets/src/js/pro/blocks-v3/utils/post-locking.js":
5302 /*!***********************************************************!*\
5303 !*** ./assets/src/js/pro/blocks-v3/utils/post-locking.js ***!
5304 \***********************************************************/
5305 /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
5306
5307 "use strict";
5308 __webpack_require__.r(__webpack_exports__);
5309 /* harmony export */ __webpack_require__.d(__webpack_exports__, {
5310 /* harmony export */ isPostSavingLocked: () => (/* binding */ isPostSavingLocked),
5311 /* harmony export */ lockPostSaving: () => (/* binding */ lockPostSaving),
5312 /* harmony export */ lockPostSavingByName: () => (/* binding */ lockPostSavingByName),
5313 /* harmony export */ sortObjectKeys: () => (/* binding */ sortObjectKeys),
5314 /* harmony export */ unlockPostSaving: () => (/* binding */ unlockPostSaving),
5315 /* harmony export */ unlockPostSavingByName: () => (/* binding */ unlockPostSavingByName)
5316 /* harmony export */ });
5317 /**
5318 * WordPress post locking utilities for ACF blocks
5319 * Handles locking/unlocking post saving during block operations
5320 */
5321
5322 /**
5323 * Locks post saving in the WordPress editor for a specific block
5324 * Used when block operations are in progress for a specific block instance
5325 *
5326 * @param {string} clientId - The block's client ID
5327 */
5328 const lockPostSaving = clientId => {
5329 const dispatch = wp.data.dispatch('core/editor');
5330 if (dispatch) {
5331 dispatch.lockPostSaving('acf/block/' + clientId);
5332 }
5333 };
5334
5335 /**
5336 * Unlocks post saving in the WordPress editor for a specific block
5337 * Called when block operations are complete for a specific block instance
5338 *
5339 * @param {string} clientId - The block's client ID
5340 */
5341 const unlockPostSaving = clientId => {
5342 const dispatch = wp.data.dispatch('core/editor');
5343 if (dispatch) {
5344 dispatch.unlockPostSaving('acf/block/' + clientId);
5345 }
5346 };
5347
5348 /**
5349 * Checks if post saving is currently locked for a specific block
5350 *
5351 * @param {string} clientId - The block's client ID
5352 * @returns {boolean} - True if post saving is locked for this block
5353 */
5354 const isPostSavingLocked = clientId => {
5355 const dispatch = wp.data.dispatch('core/editor');
5356 if (!dispatch) {
5357 return false;
5358 }
5359 return wp.data.select('core/editor').isPostSavingLocked(`acf/block/${clientId}`);
5360 };
5361
5362 /**
5363 * Locks post saving with a custom lock name
5364 * Used for global operations that aren't tied to a specific block
5365 *
5366 * @param {string} lockName - The name of the lock
5367 */
5368 const lockPostSavingByName = lockName => {
5369 const dispatch = wp.data.dispatch('core/editor');
5370 if (dispatch) {
5371 dispatch.lockPostSaving('acf/block/' + lockName);
5372 }
5373 };
5374
5375 /**
5376 * Unlocks post saving with a custom lock name
5377 * Used for global operations that aren't tied to a specific block
5378 *
5379 * @param {string} lockName - The name of the lock
5380 */
5381 const unlockPostSavingByName = lockName => {
5382 const dispatch = wp.data.dispatch('core/editor');
5383 if (dispatch) {
5384 dispatch.unlockPostSaving('acf/block/' + lockName);
5385 }
5386 };
5387
5388 /**
5389 * Sorts an object's keys alphabetically and returns a new object
5390 * Used for consistent object serialization and comparison
5391 * Ensures that objects with same properties in different order produce same hash
5392 *
5393 * @param {Object} obj - Object to sort
5394 * @returns {Object} - New object with sorted keys in alphabetical order
5395 */
5396 const sortObjectKeys = obj => {
5397 return Object.keys(obj).sort().reduce((sortedObj, key) => {
5398 sortedObj[key] = obj[key];
5399 return sortedObj;
5400 }, {});
5401 };
5402
5403 /***/ }),
5404
5405 /***/ "./node_modules/charenc/charenc.js":
5406 /*!*****************************************!*\
5407 !*** ./node_modules/charenc/charenc.js ***!
5408 \*****************************************/
5409 /***/ ((module) => {
5410
5411 var charenc = {
5412 // UTF-8 encoding
5413 utf8: {
5414 // Convert a string to a byte array
5415 stringToBytes: function(str) {
5416 return charenc.bin.stringToBytes(unescape(encodeURIComponent(str)));
5417 },
5418
5419 // Convert a byte array to a string
5420 bytesToString: function(bytes) {
5421 return decodeURIComponent(escape(charenc.bin.bytesToString(bytes)));
5422 }
5423 },
5424
5425 // Binary encoding
5426 bin: {
5427 // Convert a string to a byte array
5428 stringToBytes: function(str) {
5429 for (var bytes = [], i = 0; i < str.length; i++)
5430 bytes.push(str.charCodeAt(i) & 0xFF);
5431 return bytes;
5432 },
5433
5434 // Convert a byte array to a string
5435 bytesToString: function(bytes) {
5436 for (var str = [], i = 0; i < bytes.length; i++)
5437 str.push(String.fromCharCode(bytes[i]));
5438 return str.join('');
5439 }
5440 }
5441 };
5442
5443 module.exports = charenc;
5444
5445
5446 /***/ }),
5447
5448 /***/ "./node_modules/crypt/crypt.js":
5449 /*!*************************************!*\
5450 !*** ./node_modules/crypt/crypt.js ***!
5451 \*************************************/
5452 /***/ ((module) => {
5453
5454 (function() {
5455 var base64map
5456 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
5457
5458 crypt = {
5459 // Bit-wise rotation left
5460 rotl: function(n, b) {
5461 return (n << b) | (n >>> (32 - b));
5462 },
5463
5464 // Bit-wise rotation right
5465 rotr: function(n, b) {
5466 return (n << (32 - b)) | (n >>> b);
5467 },
5468
5469 // Swap big-endian to little-endian and vice versa
5470 endian: function(n) {
5471 // If number given, swap endian
5472 if (n.constructor == Number) {
5473 return crypt.rotl(n, 8) & 0x00FF00FF | crypt.rotl(n, 24) & 0xFF00FF00;
5474 }
5475
5476 // Else, assume array and swap all items
5477 for (var i = 0; i < n.length; i++)
5478 n[i] = crypt.endian(n[i]);
5479 return n;
5480 },
5481
5482 // Generate an array of any length of random bytes
5483 randomBytes: function(n) {
5484 for (var bytes = []; n > 0; n--)
5485 bytes.push(Math.floor(Math.random() * 256));
5486 return bytes;
5487 },
5488
5489 // Convert a byte array to big-endian 32-bit words
5490 bytesToWords: function(bytes) {
5491 for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
5492 words[b >>> 5] |= bytes[i] << (24 - b % 32);
5493 return words;
5494 },
5495
5496 // Convert big-endian 32-bit words to a byte array
5497 wordsToBytes: function(words) {
5498 for (var bytes = [], b = 0; b < words.length * 32; b += 8)
5499 bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
5500 return bytes;
5501 },
5502
5503 // Convert a byte array to a hex string
5504 bytesToHex: function(bytes) {
5505 for (var hex = [], i = 0; i < bytes.length; i++) {
5506 hex.push((bytes[i] >>> 4).toString(16));
5507 hex.push((bytes[i] & 0xF).toString(16));
5508 }
5509 return hex.join('');
5510 },
5511
5512 // Convert a hex string to a byte array
5513 hexToBytes: function(hex) {
5514 for (var bytes = [], c = 0; c < hex.length; c += 2)
5515 bytes.push(parseInt(hex.substr(c, 2), 16));
5516 return bytes;
5517 },
5518
5519 // Convert a byte array to a base-64 string
5520 bytesToBase64: function(bytes) {
5521 for (var base64 = [], i = 0; i < bytes.length; i += 3) {
5522 var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
5523 for (var j = 0; j < 4; j++)
5524 if (i * 8 + j * 6 <= bytes.length * 8)
5525 base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));
5526 else
5527 base64.push('=');
5528 }
5529 return base64.join('');
5530 },
5531
5532 // Convert a base-64 string to a byte array
5533 base64ToBytes: function(base64) {
5534 // Remove non-base-64 characters
5535 base64 = base64.replace(/[^A-Z0-9+\/]/ig, '');
5536
5537 for (var bytes = [], i = 0, imod4 = 0; i < base64.length;
5538 imod4 = ++i % 4) {
5539 if (imod4 == 0) continue;
5540 bytes.push(((base64map.indexOf(base64.charAt(i - 1))
5541 & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2))
5542 | (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));
5543 }
5544 return bytes;
5545 }
5546 };
5547
5548 module.exports = crypt;
5549 })();
5550
5551
5552 /***/ }),
5553
5554 /***/ "./node_modules/is-buffer/index.js":
5555 /*!*****************************************!*\
5556 !*** ./node_modules/is-buffer/index.js ***!
5557 \*****************************************/
5558 /***/ ((module) => {
5559
5560 /*!
5561 * Determine if an object is a Buffer
5562 *
5563 * @author Feross Aboukhadijeh <https://feross.org>
5564 * @license MIT
5565 */
5566
5567 // The _isBuffer check is for Safari 5-7 support, because it's missing
5568 // Object.prototype.constructor. Remove this eventually
5569 module.exports = function (obj) {
5570 return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)
5571 }
5572
5573 function isBuffer (obj) {
5574 return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
5575 }
5576
5577 // For Node v0.10 support. Remove this eventually.
5578 function isSlowBuffer (obj) {
5579 return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))
5580 }
5581
5582
5583 /***/ }),
5584
5585 /***/ "./node_modules/md5/md5.js":
5586 /*!*********************************!*\
5587 !*** ./node_modules/md5/md5.js ***!
5588 \*********************************/
5589 /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
5590
5591 (function(){
5592 var crypt = __webpack_require__(/*! crypt */ "./node_modules/crypt/crypt.js"),
5593 utf8 = (__webpack_require__(/*! charenc */ "./node_modules/charenc/charenc.js").utf8),
5594 isBuffer = __webpack_require__(/*! is-buffer */ "./node_modules/is-buffer/index.js"),
5595 bin = (__webpack_require__(/*! charenc */ "./node_modules/charenc/charenc.js").bin),
5596
5597 // The core
5598 md5 = function (message, options) {
5599 // Convert to byte array
5600 if (message.constructor == String)
5601 if (options && options.encoding === 'binary')
5602 message = bin.stringToBytes(message);
5603 else
5604 message = utf8.stringToBytes(message);
5605 else if (isBuffer(message))
5606 message = Array.prototype.slice.call(message, 0);
5607 else if (!Array.isArray(message) && message.constructor !== Uint8Array)
5608 message = message.toString();
5609 // else, assume byte array already
5610
5611 var m = crypt.bytesToWords(message),
5612 l = message.length * 8,
5613 a = 1732584193,
5614 b = -271733879,
5615 c = -1732584194,
5616 d = 271733878;
5617
5618 // Swap endian
5619 for (var i = 0; i < m.length; i++) {
5620 m[i] = ((m[i] << 8) | (m[i] >>> 24)) & 0x00FF00FF |
5621 ((m[i] << 24) | (m[i] >>> 8)) & 0xFF00FF00;
5622 }
5623
5624 // Padding
5625 m[l >>> 5] |= 0x80 << (l % 32);
5626 m[(((l + 64) >>> 9) << 4) + 14] = l;
5627
5628 // Method shortcuts
5629 var FF = md5._ff,
5630 GG = md5._gg,
5631 HH = md5._hh,
5632 II = md5._ii;
5633
5634 for (var i = 0; i < m.length; i += 16) {
5635
5636 var aa = a,
5637 bb = b,
5638 cc = c,
5639 dd = d;
5640
5641 a = FF(a, b, c, d, m[i+ 0], 7, -680876936);
5642 d = FF(d, a, b, c, m[i+ 1], 12, -389564586);
5643 c = FF(c, d, a, b, m[i+ 2], 17, 606105819);
5644 b = FF(b, c, d, a, m[i+ 3], 22, -1044525330);
5645 a = FF(a, b, c, d, m[i+ 4], 7, -176418897);
5646 d = FF(d, a, b, c, m[i+ 5], 12, 1200080426);
5647 c = FF(c, d, a, b, m[i+ 6], 17, -1473231341);
5648 b = FF(b, c, d, a, m[i+ 7], 22, -45705983);
5649 a = FF(a, b, c, d, m[i+ 8], 7, 1770035416);
5650 d = FF(d, a, b, c, m[i+ 9], 12, -1958414417);
5651 c = FF(c, d, a, b, m[i+10], 17, -42063);
5652 b = FF(b, c, d, a, m[i+11], 22, -1990404162);
5653 a = FF(a, b, c, d, m[i+12], 7, 1804603682);
5654 d = FF(d, a, b, c, m[i+13], 12, -40341101);
5655 c = FF(c, d, a, b, m[i+14], 17, -1502002290);
5656 b = FF(b, c, d, a, m[i+15], 22, 1236535329);
5657
5658 a = GG(a, b, c, d, m[i+ 1], 5, -165796510);
5659 d = GG(d, a, b, c, m[i+ 6], 9, -1069501632);
5660 c = GG(c, d, a, b, m[i+11], 14, 643717713);
5661 b = GG(b, c, d, a, m[i+ 0], 20, -373897302);
5662 a = GG(a, b, c, d, m[i+ 5], 5, -701558691);
5663 d = GG(d, a, b, c, m[i+10], 9, 38016083);
5664 c = GG(c, d, a, b, m[i+15], 14, -660478335);
5665 b = GG(b, c, d, a, m[i+ 4], 20, -405537848);
5666 a = GG(a, b, c, d, m[i+ 9], 5, 568446438);
5667 d = GG(d, a, b, c, m[i+14], 9, -1019803690);
5668 c = GG(c, d, a, b, m[i+ 3], 14, -187363961);
5669 b = GG(b, c, d, a, m[i+ 8], 20, 1163531501);
5670 a = GG(a, b, c, d, m[i+13], 5, -1444681467);
5671 d = GG(d, a, b, c, m[i+ 2], 9, -51403784);
5672 c = GG(c, d, a, b, m[i+ 7], 14, 1735328473);
5673 b = GG(b, c, d, a, m[i+12], 20, -1926607734);
5674
5675 a = HH(a, b, c, d, m[i+ 5], 4, -378558);
5676 d = HH(d, a, b, c, m[i+ 8], 11, -2022574463);
5677 c = HH(c, d, a, b, m[i+11], 16, 1839030562);
5678 b = HH(b, c, d, a, m[i+14], 23, -35309556);
5679 a = HH(a, b, c, d, m[i+ 1], 4, -1530992060);
5680 d = HH(d, a, b, c, m[i+ 4], 11, 1272893353);
5681 c = HH(c, d, a, b, m[i+ 7], 16, -155497632);
5682 b = HH(b, c, d, a, m[i+10], 23, -1094730640);
5683 a = HH(a, b, c, d, m[i+13], 4, 681279174);
5684 d = HH(d, a, b, c, m[i+ 0], 11, -358537222);
5685 c = HH(c, d, a, b, m[i+ 3], 16, -722521979);
5686 b = HH(b, c, d, a, m[i+ 6], 23, 76029189);
5687 a = HH(a, b, c, d, m[i+ 9], 4, -640364487);
5688 d = HH(d, a, b, c, m[i+12], 11, -421815835);
5689 c = HH(c, d, a, b, m[i+15], 16, 530742520);
5690 b = HH(b, c, d, a, m[i+ 2], 23, -995338651);
5691
5692 a = II(a, b, c, d, m[i+ 0], 6, -198630844);
5693 d = II(d, a, b, c, m[i+ 7], 10, 1126891415);
5694 c = II(c, d, a, b, m[i+14], 15, -1416354905);
5695 b = II(b, c, d, a, m[i+ 5], 21, -57434055);
5696 a = II(a, b, c, d, m[i+12], 6, 1700485571);
5697 d = II(d, a, b, c, m[i+ 3], 10, -1894986606);
5698 c = II(c, d, a, b, m[i+10], 15, -1051523);
5699 b = II(b, c, d, a, m[i+ 1], 21, -2054922799);
5700 a = II(a, b, c, d, m[i+ 8], 6, 1873313359);
5701 d = II(d, a, b, c, m[i+15], 10, -30611744);
5702 c = II(c, d, a, b, m[i+ 6], 15, -1560198380);
5703 b = II(b, c, d, a, m[i+13], 21, 1309151649);
5704 a = II(a, b, c, d, m[i+ 4], 6, -145523070);
5705 d = II(d, a, b, c, m[i+11], 10, -1120210379);
5706 c = II(c, d, a, b, m[i+ 2], 15, 718787259);
5707 b = II(b, c, d, a, m[i+ 9], 21, -343485551);
5708
5709 a = (a + aa) >>> 0;
5710 b = (b + bb) >>> 0;
5711 c = (c + cc) >>> 0;
5712 d = (d + dd) >>> 0;
5713 }
5714
5715 return crypt.endian([a, b, c, d]);
5716 };
5717
5718 // Auxiliary functions
5719 md5._ff = function (a, b, c, d, x, s, t) {
5720 var n = a + (b & c | ~b & d) + (x >>> 0) + t;
5721 return ((n << s) | (n >>> (32 - s))) + b;
5722 };
5723 md5._gg = function (a, b, c, d, x, s, t) {
5724 var n = a + (b & d | c & ~d) + (x >>> 0) + t;
5725 return ((n << s) | (n >>> (32 - s))) + b;
5726 };
5727 md5._hh = function (a, b, c, d, x, s, t) {
5728 var n = a + (b ^ c ^ d) + (x >>> 0) + t;
5729 return ((n << s) | (n >>> (32 - s))) + b;
5730 };
5731 md5._ii = function (a, b, c, d, x, s, t) {
5732 var n = a + (c ^ (b | ~d)) + (x >>> 0) + t;
5733 return ((n << s) | (n >>> (32 - s))) + b;
5734 };
5735
5736 // Package private blocksize
5737 md5._blocksize = 16;
5738 md5._digestsize = 16;
5739
5740 module.exports = function (message, options) {
5741 if (message === undefined || message === null)
5742 throw new Error('Illegal argument ' + message);
5743
5744 var digestbytes = crypt.wordsToBytes(md5(message, options));
5745 return options && options.asBytes ? digestbytes :
5746 options && options.asString ? bin.bytesToString(digestbytes) :
5747 crypt.bytesToHex(digestbytes);
5748 };
5749
5750 })();
5751
5752
5753 /***/ }),
5754
5755 /***/ "@wordpress/block-editor":
5756 /*!*************************************!*\
5757 !*** external ["wp","blockEditor"] ***!
5758 \*************************************/
5759 /***/ ((module) => {
5760
5761 "use strict";
5762 module.exports = window["wp"]["blockEditor"];
5763
5764 /***/ }),
5765
5766 /***/ "@wordpress/components":
5767 /*!************************************!*\
5768 !*** external ["wp","components"] ***!
5769 \************************************/
5770 /***/ ((module) => {
5771
5772 "use strict";
5773 module.exports = window["wp"]["components"];
5774
5775 /***/ }),
5776
5777 /***/ "@wordpress/element":
5778 /*!*********************************!*\
5779 !*** external ["wp","element"] ***!
5780 \*********************************/
5781 /***/ ((module) => {
5782
5783 "use strict";
5784 module.exports = window["wp"]["element"];
5785
5786 /***/ }),
5787
5788 /***/ "jquery":
5789 /*!*************************!*\
5790 !*** external "jQuery" ***!
5791 \*************************/
5792 /***/ ((module) => {
5793
5794 "use strict";
5795 module.exports = window["jQuery"];
5796
5797 /***/ })
5798
5799 /******/ });
5800 /************************************************************************/
5801 /******/ // The module cache
5802 /******/ var __webpack_module_cache__ = {};
5803 /******/
5804 /******/ // The require function
5805 /******/ function __webpack_require__(moduleId) {
5806 /******/ // Check if module is in cache
5807 /******/ var cachedModule = __webpack_module_cache__[moduleId];
5808 /******/ if (cachedModule !== undefined) {
5809 /******/ return cachedModule.exports;
5810 /******/ }
5811 /******/ // Create a new module (and put it into the cache)
5812 /******/ var module = __webpack_module_cache__[moduleId] = {
5813 /******/ // no module.id needed
5814 /******/ // no module.loaded needed
5815 /******/ exports: {}
5816 /******/ };
5817 /******/
5818 /******/ // Execute the module function
5819 /******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
5820 /******/
5821 /******/ // Return the exports of the module
5822 /******/ return module.exports;
5823 /******/ }
5824 /******/
5825 /************************************************************************/
5826 /******/ /* webpack/runtime/compat get default export */
5827 /******/ (() => {
5828 /******/ // getDefaultExport function for compatibility with non-harmony modules
5829 /******/ __webpack_require__.n = (module) => {
5830 /******/ var getter = module && module.__esModule ?
5831 /******/ () => (module['default']) :
5832 /******/ () => (module);
5833 /******/ __webpack_require__.d(getter, { a: getter });
5834 /******/ return getter;
5835 /******/ };
5836 /******/ })();
5837 /******/
5838 /******/ /* webpack/runtime/define property getters */
5839 /******/ (() => {
5840 /******/ // define getter functions for harmony exports
5841 /******/ __webpack_require__.d = (exports, definition) => {
5842 /******/ for(var key in definition) {
5843 /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
5844 /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
5845 /******/ }
5846 /******/ }
5847 /******/ };
5848 /******/ })();
5849 /******/
5850 /******/ /* webpack/runtime/hasOwnProperty shorthand */
5851 /******/ (() => {
5852 /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
5853 /******/ })();
5854 /******/
5855 /******/ /* webpack/runtime/make namespace object */
5856 /******/ (() => {
5857 /******/ // define __esModule on exports
5858 /******/ __webpack_require__.r = (exports) => {
5859 /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
5860 /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
5861 /******/ }
5862 /******/ Object.defineProperty(exports, '__esModule', { value: true });
5863 /******/ };
5864 /******/ })();
5865 /******/
5866 /************************************************************************/
5867 var __webpack_exports__ = {};
5868 // This entry needs to be wrapped in an IIFE because it needs to be in strict mode.
5869 (() => {
5870 "use strict";
5871 /*!*********************************************!*\
5872 !*** ./assets/src/js/pro/acf-pro-blocks.js ***!
5873 \*********************************************/
5874 __webpack_require__.r(__webpack_exports__);
5875 /* harmony import */ var _acf_jsx_names_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./_acf-jsx-names.js */ "./assets/src/js/pro/_acf-jsx-names.js");
5876 /* harmony import */ var _acf_jsx_names_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_acf_jsx_names_js__WEBPACK_IMPORTED_MODULE_0__);
5877 /* harmony import */ var _acf_blocks_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./_acf-blocks.js */ "./assets/src/js/pro/_acf-blocks.js");
5878 /* harmony import */ var _acf_blocks_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_acf_blocks_js__WEBPACK_IMPORTED_MODULE_1__);
5879 /* harmony import */ var _acf_blocks_v3_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./_acf-blocks-v3.js */ "./assets/src/js/pro/_acf-blocks-v3.js");
5880
5881
5882
5883 })();
5884
5885 /******/ })()
5886 ;
5887 //# sourceMappingURL=acf-pro-blocks.js.map