| 1 |
/** |
| 2 |
* Inspector Patches / Glue for Section: |
| 3 |
* --------------------------------------------------------------------------------- |
| 4 |
* 1) Prevent full re-render when changing "columns" for .wpbc_bfb__section |
| 5 |
* 2) Bind inspector to: |
| 6 |
* - Mount "layout_chips" slot |
| 7 |
* - Keep numeric "columns" input stable & debounced |
| 8 |
* - Observe live DOM column changes and sync inspector |
| 9 |
* - Mount/refresh "column_styles" slot exposed by UI.wpbc_bfb_column_styles |
| 10 |
* |
| 11 |
* == File: /includes/page-form-builder/field-packs/section/_out/bfb-inspector-patches.js |
| 12 |
* |
| 13 |
* @since 11.0.0 |
| 14 |
* @modified 2025-09-16 11:26 |
| 15 |
* @version 1.0.0 |
| 16 |
*/ |
| 17 |
(function ( w ) { |
| 18 |
'use strict'; |
| 19 |
|
| 20 |
var Core = ( w.WPBC_BFB_Core = w.WPBC_BFB_Core || {} ); |
| 21 |
var UI = ( Core.UI = Core.UI || {} ); |
| 22 |
|
| 23 |
var H = Core.WPBC_Form_Builder_Helper || {}; |
| 24 |
var DOM = ( Core.WPBC_BFB_DOM && Core.WPBC_BFB_DOM.SELECTORS ) || { |
| 25 |
row : '.wpbc_bfb__row', |
| 26 |
column : '.wpbc_bfb__column' |
| 27 |
}; |
| 28 |
|
| 29 |
/** |
| 30 |
* Read actual number of columns from DOM. |
| 31 |
* |
| 32 |
* @param {HTMLElement} el |
| 33 |
* @returns {number} |
| 34 |
*/ |
| 35 |
function dom_cols( el ) { |
| 36 |
try { |
| 37 |
var row = el ? el.querySelector( ':scope > ' + DOM.row ) : null; |
| 38 |
var cnt = row ? row.querySelectorAll( ':scope > ' + DOM.column ).length : 1; |
| 39 |
var clamp = Core.WPBC_BFB_Sanitize && Core.WPBC_BFB_Sanitize.clamp ? Core.WPBC_BFB_Sanitize.clamp : function ( n, a, b ) { return Math.max( a, Math.min( b, n ) ); }; |
| 40 |
return clamp( Number( cnt ) || 1, 1, 4 ); |
| 41 |
} catch ( _e ) { |
| 42 |
return 1; |
| 43 |
} |
| 44 |
} |
| 45 |
|
| 46 |
/** |
| 47 |
* Observe column DOM changes to keep inspector inputs in sync. |
| 48 |
* |
| 49 |
* @param {object} builder |
| 50 |
* @param {HTMLElement} section_el |
| 51 |
* @param {HTMLElement} inspector_root |
| 52 |
* @returns {Function} disconnect |
| 53 |
*/ |
| 54 |
function observe_cols( builder, section_el, inspector_root ) { |
| 55 |
var row = section_el && section_el.querySelector( ':scope > ' + DOM.row ); |
| 56 |
if ( ! row ) { return function () {}; } |
| 57 |
|
| 58 |
var input = inspector_root && inspector_root.querySelector( 'input[data-inspector-key="columns"]' ); |
| 59 |
var mo = new MutationObserver( function () { |
| 60 |
var cnt = dom_cols( section_el ); |
| 61 |
if ( input && input.value !== String( cnt ) ) { |
| 62 |
input.value = String( cnt ); |
| 63 |
// Fire an input event so len-control syncs the slider too. |
| 64 |
try { input.dispatchEvent( new Event('input', { bubbles: true }) ); } catch (_e) {} |
| 65 |
} |
| 66 |
inspector_root && inspector_root.setAttribute( 'data-bfb-section-cols', String( cnt ) ); |
| 67 |
section_el.dataset.columns = String( cnt ); |
| 68 |
|
| 69 |
// Also refresh column styles if present. |
| 70 |
if ( UI && UI.wpbc_bfb_column_styles && typeof UI.wpbc_bfb_column_styles.refresh_for_section === 'function' ) { |
| 71 |
UI.wpbc_bfb_column_styles.refresh_for_section( builder, section_el, inspector_root ); |
| 72 |
} |
| 73 |
} ); |
| 74 |
try { mo.observe( row, { childList : true } ); } catch ( _err ) {} |
| 75 |
return function unobserve() { try { mo.disconnect(); } catch ( _e ) {} }; |
| 76 |
} |
| 77 |
|
| 78 |
/** |
| 79 |
* Ensure host for layout chips stays stable. |
| 80 |
* |
| 81 |
* @param {HTMLElement} inspector_root |
| 82 |
* @returns {HTMLElement|null} |
| 83 |
*/ |
| 84 |
function ensure_chips_host( inspector_root ) { |
| 85 |
if ( ! inspector_root ) { return null; } |
| 86 |
var host = inspector_root.querySelector( '[data-bfb-slot="layout_chips"]' ) |
| 87 |
|| inspector_root.querySelector( '.inspector__row--layout-chips .wpbc_bfb__layout_chips' ) |
| 88 |
|| inspector_root.querySelector( '#wpbc_bfb__layout_chips_host' ); |
| 89 |
if ( host ) { |
| 90 |
host.classList.add( 'wpbc_bfb__layout_chips' ); |
| 91 |
host.setAttribute( 'data-bfb-slot', 'layout_chips' ); |
| 92 |
} |
| 93 |
return host; |
| 94 |
} |
| 95 |
|
| 96 |
/** |
| 97 |
* Render layout chips into inspector, if available. |
| 98 |
* |
| 99 |
* @param {object} builder |
| 100 |
* @param {HTMLElement} section_el |
| 101 |
* @param {HTMLElement} inspector_root |
| 102 |
*/ |
| 103 |
function render_chips_into_inspector( builder, section_el, inspector_root ) { |
| 104 |
if ( ! UI.WPBC_BFB_Layout_Chips || typeof UI.WPBC_BFB_Layout_Chips.render_for_section !== 'function' ) { return; } |
| 105 |
var host = ensure_chips_host( inspector_root ); |
| 106 |
if ( ! host ) { return; } |
| 107 |
host.innerHTML = ''; |
| 108 |
UI.WPBC_BFB_Layout_Chips.render_for_section( builder, section_el, host ); |
| 109 |
} |
| 110 |
|
| 111 |
// ------------------------------------------------------------------------------------------------ |
| 112 |
// 1) Patch: prevent full re-render for columns change (section only) |
| 113 |
// ------------------------------------------------------------------------------------------------ |
| 114 |
(function patch_no_rerender_for_columns() { |
| 115 |
var Inspector = w.WPBC_BFB_Inspector; |
| 116 |
if ( ! Inspector || Inspector.__wpbc_no_rerender_columns ) { return; } |
| 117 |
Inspector.__wpbc_no_rerender_columns = true; |
| 118 |
|
| 119 |
var orig = Inspector.prototype._needs_rerender; |
| 120 |
Inspector.prototype._needs_rerender = function ( el, key, e ) { |
| 121 |
if ( el && el.classList && el.classList.contains( 'wpbc_bfb__section' ) && key === 'columns' ) { |
| 122 |
return false; |
| 123 |
} |
| 124 |
return ( typeof orig === 'function' ) ? orig.call( this, el, key, e ) : false; |
| 125 |
}; |
| 126 |
})(); |
| 127 |
|
| 128 |
// ------------------------------------------------------------------------------------------------ |
| 129 |
// 2) Patch: extend Inspector.bind_to_field to wire chips + columns input + column styles slot |
| 130 |
// ------------------------------------------------------------------------------------------------ |
| 131 |
function patch_inspector_bind_once() { |
| 132 |
var Inspector = w.WPBC_BFB_Inspector; |
| 133 |
if ( ! Inspector || Inspector.__wpbc_section_bind_patched ) { return; } |
| 134 |
if ( ! Inspector.prototype || ! Inspector.prototype.bind_to_field ) { return; } |
| 135 |
Inspector.__wpbc_section_bind_patched = true; |
| 136 |
|
| 137 |
var orig_bind = Inspector.prototype.bind_to_field; |
| 138 |
|
| 139 |
Inspector.prototype.bind_to_field = function ( el ) { |
| 140 |
orig_bind.call( this, el ); |
| 141 |
|
| 142 |
try { |
| 143 |
var ins = this.panel || document.getElementById( 'wpbc_bfb__inspector' ) || document.querySelector( '.wpbc_bfb__inspector' ); |
| 144 |
var builder = this.builder || w.wpbc_bfb || null; |
| 145 |
if ( ! ins || ! builder || !( el && el.classList && el.classList.contains( 'wpbc_bfb__section' ) ) ) { return; } |
| 146 |
|
| 147 |
|
| 148 |
// Layout chips. |
| 149 |
render_chips_into_inspector( builder, el, ins ); |
| 150 |
|
| 151 |
// Stable numeric "columns" input behavior. |
| 152 |
var input = ins.querySelector( 'input[data-inspector-key="columns"]' ); |
| 153 |
if ( input && ! input.__wpbc_cols_hooked ) { |
| 154 |
input.__wpbc_cols_hooked = true; |
| 155 |
input.value = String( dom_cols( el ) ); |
| 156 |
|
| 157 |
var apply_change = function () { |
| 158 |
var desired = ( Core.WPBC_BFB_Sanitize && Core.WPBC_BFB_Sanitize.clamp ) |
| 159 |
? Core.WPBC_BFB_Sanitize.clamp( input.value | 0, 1, 4 ) |
| 160 |
: Math.max( 1, Math.min( 4, Number( input.value ) || 1 ) ); |
| 161 |
var current = dom_cols( el ); |
| 162 |
if ( desired === current ) { return; } |
| 163 |
|
| 164 |
if ( typeof builder.set_section_columns === 'function' ) { |
| 165 |
builder.set_section_columns( el, desired ); |
| 166 |
} |
| 167 |
el.dataset.columns = String( desired ); |
| 168 |
ins.setAttribute( 'data-bfb-section-cols', String( desired ) ); |
| 169 |
|
| 170 |
// Refresh column styles UI on column count change. |
| 171 |
if ( UI && UI.wpbc_bfb_column_styles && typeof UI.wpbc_bfb_column_styles.refresh_for_section === 'function' ) { |
| 172 |
UI.wpbc_bfb_column_styles.refresh_for_section( builder, el, ins ); |
| 173 |
} |
| 174 |
}; |
| 175 |
|
| 176 |
var debounced = ( H && H.debounce ) ? H.debounce( apply_change, 120 ) : apply_change; |
| 177 |
input.addEventListener( 'input', debounced ); |
| 178 |
input.addEventListener( 'change', apply_change ); |
| 179 |
} |
| 180 |
|
| 181 |
// Observe DOM changes in columns (sync inspector). |
| 182 |
if ( this.__wpbc_cols_unobserve ) { |
| 183 |
try { this.__wpbc_cols_unobserve(); } catch ( _e ) {} |
| 184 |
this.__wpbc_cols_unobserve = null; |
| 185 |
} |
| 186 |
this.__wpbc_cols_unobserve = observe_cols( builder, el, ins ); |
| 187 |
|
| 188 |
// Mount reusable "column_styles" slot into the inspector, if available. |
| 189 |
if ( UI && UI.wpbc_bfb_column_styles && typeof UI.wpbc_bfb_column_styles.render_for_section === 'function' ) { |
| 190 |
UI.wpbc_bfb_column_styles.render_for_section( builder, el, ins.querySelector( '[data-bfb-slot="column_styles"]' ) ); |
| 191 |
} |
| 192 |
|
| 193 |
// Keep both slots fresh on structure events. |
| 194 |
var EV = Core.WPBC_BFB_Events || {}; |
| 195 |
var refresh = () => { |
| 196 |
var cur = builder.get_selected_field && builder.get_selected_field(); |
| 197 |
if ( ! cur || ! cur.classList || ! cur.classList.contains( 'wpbc_bfb__section' ) ) { return; } |
| 198 |
render_chips_into_inspector( builder, cur, ins ); |
| 199 |
if ( UI && UI.wpbc_bfb_column_styles && typeof UI.wpbc_bfb_column_styles.refresh_for_section === 'function' ) { |
| 200 |
UI.wpbc_bfb_column_styles.refresh_for_section( builder, cur, ins ); |
| 201 |
} |
| 202 |
var c = dom_cols( cur ); |
| 203 |
ins.setAttribute( 'data-bfb-section-cols', String( c ) ); |
| 204 |
var inp = ins.querySelector( 'input[data-inspector-key="columns"]' ); |
| 205 |
if ( inp ) { inp.value = String( c ); } |
| 206 |
}; |
| 207 |
|
| 208 |
if ( ! this.__wpbc_cols_bus_hooked && builder.bus ) { |
| 209 |
this.__wpbc_cols_bus_hooked = true; |
| 210 |
builder.bus.on && builder.bus.on( EV.FIELD_ADD, refresh ); |
| 211 |
builder.bus.on && builder.bus.on( EV.FIELD_REMOVE, refresh ); |
| 212 |
builder.bus.on && builder.bus.on( EV.STRUCTURE_LOADED, refresh ); |
| 213 |
builder.bus.on && builder.bus.on( EV.STRUCTURE_CHANGE, function (ev) { |
| 214 |
// The bus gives a DOM CustomEvent → data is in ev.detail. But be defensive in case some callers pass a plain object. |
| 215 |
var d = ev && (ev.detail || ev); |
| 216 |
var src = d && d.source; |
| 217 |
// Skip re-render when only per-column styles changed. |
| 218 |
if ( src === 'column_styles' || src === 'column_styles_reset' ) { |
| 219 |
// Optional: keep the "columns" input value in sync without re-rendering. |
| 220 |
try { |
| 221 |
var cur = builder.get_selected_field && builder.get_selected_field(); |
| 222 |
if ( cur && cur.classList && cur.classList.contains( 'wpbc_bfb__section' ) ) { |
| 223 |
var c = dom_cols( cur ); |
| 224 |
ins.setAttribute( 'data-bfb-section-cols', String( c ) ); |
| 225 |
var inp = ins.querySelector( 'input[data-inspector-key="columns"]' ); |
| 226 |
if ( inp ) { |
| 227 |
inp.value = String( c ); |
| 228 |
} |
| 229 |
} |
| 230 |
} catch ( _e ) {} |
| 231 |
return; |
| 232 |
} |
| 233 |
refresh(); |
| 234 |
} ); |
| 235 |
} |
| 236 |
|
| 237 |
} catch ( e ) { |
| 238 |
w._wpbc && w._wpbc.dev && w._wpbc.dev.error && w._wpbc.dev.error( 'section_inspector.patch_bind', e ); |
| 239 |
} |
| 240 |
}; |
| 241 |
} |
| 242 |
|
| 243 |
patch_inspector_bind_once(); |
| 244 |
document.addEventListener( 'wpbc_bfb_inspector_ready', patch_inspector_bind_once ); |
| 245 |
|
| 246 |
})( window ); |
| 247 |
|