PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
11.9 11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 All 205 releases
← All changes | includes/page-form-builder/_src/bfb-rightbar-tabs.js +258 -9 11.1 → 11.9 View file →
@@ -1,4 +1,4 @@
1 1 /**
2 2 * Booking Calendar — Rightbar Tabs Controller (JS)
3 3 *
4 4 * Purpose: Handles the main right sidebar tabs (Library / Inspector / Settings) in the Booking Form Builder.
@@ -303,17 +303,266 @@
303 303 if ( panel_id ) {
304 304 this.show_panel( panel_id, tab_el || undefined );
305 305 }
306 306 }
307 - }
308 -
309 - // Boot once DOM is ready.
310 - const instance = new WPBC_BFB_Rightbar_Tabs();
311 - if ( d.readyState === 'loading' ) {
312 - d.addEventListener( 'DOMContentLoaded', () => instance.init() );
313 - } else {
314 - instance.init();
315 - }
307 + }
308 +
309 + function esc_attr_selector_value(value) {
310 + if ( Sanit && typeof Sanit.esc_attr_value_for_selector === 'function' ) {
311 + return Sanit.esc_attr_value_for_selector( value );
312 + }
313 + return String( value == null ? '' : value )
314 + .replace( /\\/g, '\\\\' )
315 + .replace( /"/g, '\\"' )
316 + .replace( /\n/g, '\\A ' )
317 + .replace( /\]/g, '\\]' );
318 + }
319 +
320 + function get_url_params() {
321 + try {
322 + return new URLSearchParams( w.location.search || '' );
323 + } catch ( _e ) {
324 + return null;
325 + }
326 + }
327 +
328 + function open_settings_group(group_key) {
329 + const panel = d.getElementById( 'wpbc_bfb__inspector_form_settings' ) || d;
330 + const group = panel.querySelector( '.wpbc_bfb__inspector__group[data-group="' + esc_attr_selector_value( group_key ) + '"]' );
331 + if ( ! group ) {
332 + return false;
333 + }
334 +
335 + const header = group.querySelector( '.group__header' );
336 + const fields = group.querySelector( '.group__fields' );
337 +
338 + group.classList.add( 'is-open' );
339 + if ( header ) {
340 + header.setAttribute( 'aria-expanded', 'true' );
341 + }
342 + if ( fields ) {
343 + fields.removeAttribute( 'hidden' );
344 + fields.setAttribute( 'aria-hidden', 'false' );
345 + }
346 +
347 + return true;
348 + }
349 +
350 + function focus_settings_row(row_key) {
351 + const panel = d.getElementById( 'wpbc_bfb__inspector_form_settings' ) || d;
352 + const row = panel.querySelector( '.wpbc-setting[data-key="' + esc_attr_selector_value( row_key ) + '"]' );
353 + if ( ! row ) {
354 + return false;
355 + }
356 +
357 + try {
358 + row.scrollIntoView( { behavior: 'smooth', block: 'center', inline: 'nearest' } );
359 + } catch ( _e ) {
360 + row.scrollIntoView( true );
361 + }
362 +
363 + row.classList.remove( 'wpbc_bfb__scroll-pulse', 'wpbc_bfb__highlight-pulse' );
364 + void row.offsetWidth;
365 + row.classList.add( 'wpbc_bfb__scroll-pulse', 'wpbc_bfb__highlight-pulse' );
366 +
367 + setTimeout( () => {
368 + row.classList.remove( 'wpbc_bfb__scroll-pulse', 'wpbc_bfb__highlight-pulse' );
369 + }, 2200 );
370 +
371 + const control = row.querySelector( '[data-wpbc-bfb-fs-key="' + esc_attr_selector_value( row_key ) + '"]' )
372 + || row.querySelector( 'select,input,textarea,button' );
373 +
374 + if ( control && typeof control.focus === 'function' ) {
375 + setTimeout( () => {
376 + try {
377 + control.focus( { preventScroll: true } );
378 + } catch ( _e ) {
379 + control.focus();
380 + }
381 + }, 250 );
382 + }
383 +
384 + return true;
385 + }
386 +
387 + /**
388 + * Open one collapsible Add Fields palette group.
389 + *
390 + * The setup-wizard deep link uses this helper to reveal a field pack without
391 + * inserting or otherwise changing the current booking form.
392 + *
393 + * @param {string} group_key Palette group key from its data-group attribute.
394 + * @returns {boolean} Whether the requested palette group was found and opened.
395 + */
396 + function open_palette_group(group_key) {
397 + const panel = d.getElementById( 'wpbc_bfb__palette_add_new' ) || d;
398 + const group = panel.querySelector( '.wpbc_bfb__inspector__group[data-group="' + esc_attr_selector_value( group_key ) + '"]' );
399 + if ( ! group ) {
400 + return false;
401 + }
402 +
403 + const header = group.querySelector( '.group__header' );
404 + const fields = group.querySelector( '.group__fields' );
405 +
406 + group.classList.add( 'is-open' );
407 + if ( header ) {
408 + header.setAttribute( 'aria-expanded', 'true' );
409 + }
410 + if ( fields ) {
411 + fields.removeAttribute( 'hidden' );
412 + fields.setAttribute( 'aria-hidden', 'false' );
413 + }
414 +
415 + return true;
416 + }
417 +
418 + /**
419 + * Focus and visually highlight one Add Fields palette item.
420 + *
421 + * @param {string} field_type Registered Form Builder field type.
422 + * @returns {boolean} Whether the requested field pack palette item was found.
423 + */
424 + function focus_palette_field(field_type) {
425 + const panel = d.getElementById( 'wpbc_bfb__palette_add_new' ) || d;
426 + const field = panel.querySelector( '.wpbc_bfb__field[data-type="' + esc_attr_selector_value( field_type ) + '"]' );
427 + if ( ! field ) {
428 + return false;
429 + }
430 +
431 + try {
432 + field.scrollIntoView( { behavior: 'smooth', block: 'center', inline: 'nearest' } );
433 + } catch ( _e ) {
434 + field.scrollIntoView( true );
435 + }
436 +
437 + field.classList.remove( 'wpbc_bfb__scroll-pulse', 'wpbc_bfb__highlight-pulse' );
438 + void field.offsetWidth;
439 + field.classList.add( 'wpbc_bfb__scroll-pulse', 'wpbc_bfb__highlight-pulse' );
440 + field.setAttribute( 'tabindex', '-1' );
441 +
442 + setTimeout( () => {
443 + field.classList.remove( 'wpbc_bfb__scroll-pulse', 'wpbc_bfb__highlight-pulse' );
444 + }, 2200 );
445 + setTimeout( () => {
446 + try {
447 + field.focus( { preventScroll: true } );
448 + } catch ( _e ) {
449 + field.focus();
450 + }
451 + }, 250 );
452 +
453 + return true;
454 + }
455 +
456 + let deep_link_done = false;
457 + let deep_link_ajax_listener_bound = false;
458 +
459 + function has_initial_deep_link() {
460 + const params = get_url_params();
461 + return !! (
462 + params
463 + && -1 !== [ 'form_settings', 'add_fields' ].indexOf( params.get( 'wpbc_bfb_panel' ) )
464 + );
465 + }
466 +
467 + function handle_initial_deep_link(tabs, attempt = 0) {
468 + if ( deep_link_done ) {
469 + return;
470 + }
471 +
472 + const params = get_url_params();
473 + if ( ! params || -1 === [ 'form_settings', 'add_fields' ].indexOf( params.get( 'wpbc_bfb_panel' ) ) ) {
474 + return;
475 + }
476 +
477 + const panel_mode = params.get( 'wpbc_bfb_panel' );
478 + const panel_id = 'add_fields' === panel_mode ? 'wpbc_bfb__palette_add_new' : 'wpbc_bfb__inspector_form_settings';
479 + const tab = d.getElementById( 'add_fields' === panel_mode ? 'wpbc_tab_library' : 'wpbc_tab_form' );
480 + const panel = d.getElementById( panel_id );
481 + if ( ! tab || ! panel ) {
482 + if ( attempt < 25 ) {
483 + setTimeout( () => handle_initial_deep_link( tabs, attempt + 1 ), 80 );
484 + }
485 + return;
486 + }
487 +
488 + tabs.show_panel( panel_id, tab );
489 +
490 + const group_key = params.get( 'wpbc_bfb_group' );
491 + const row_key = params.get( 'wpbc_bfb_focus' );
492 + const group_ok = group_key
493 + ? ( 'add_fields' === panel_mode ? open_palette_group( group_key ) : open_settings_group( group_key ) )
494 + : true;
495 + const row_ok = row_key
496 + ? ( 'add_fields' === panel_mode ? focus_palette_field( row_key ) : focus_settings_row( row_key ) )
497 + : true;
498 +
499 + if ( ( ! group_ok || ! row_ok ) && attempt < 25 ) {
500 + setTimeout( () => handle_initial_deep_link( tabs, attempt + 1 ), 80 );
501 + return;
502 + }
503 +
504 + deep_link_done = group_ok && row_ok;
505 + }
506 +
507 + function schedule_initial_deep_link(tabs, delay = 0) {
508 + if ( deep_link_done || ! has_initial_deep_link() ) {
509 + return;
510 + }
511 +
512 + setTimeout( () => handle_initial_deep_link( tabs ), delay );
513 + }
514 +
515 + function bind_initial_deep_link_after_form_load(tabs, attempt = 0) {
516 + if ( ! has_initial_deep_link() ) {
517 + return;
518 + }
519 +
520 + if ( ! deep_link_ajax_listener_bound ) {
521 + deep_link_ajax_listener_bound = true;
522 + d.addEventListener( 'wpbc:bfb:form:ajax_loaded', () => {
523 + // Legacy/blank forms do not always emit STRUCTURE_LOADED; wait until add_page() and UI defaults settle.
524 + schedule_initial_deep_link( tabs, 450 );
525 + }, { once: true } );
526 + }
527 +
528 + if ( ! w.wpbc_bfb_api || ! w.wpbc_bfb_api.ready || typeof w.wpbc_bfb_api.ready.then !== 'function' ) {
529 + if ( attempt < 25 ) {
530 + setTimeout( () => bind_initial_deep_link_after_form_load( tabs, attempt + 1 ), 80 );
531 + }
532 + return;
533 + }
534 +
535 + w.wpbc_bfb_api.ready.then( (builder) => {
536 + const events = ( w.WPBC_BFB_Core && w.WPBC_BFB_Core.WPBC_BFB_Events ) || {};
537 + const event_name = events.STRUCTURE_LOADED || 'wpbc:bfb:structure:loaded';
538 + if ( ! builder || ! builder.bus || typeof builder.bus.on !== 'function' ) {
539 + return;
540 + }
541 +
542 + const on_structure_loaded = () => {
543 + if ( builder.bus && typeof builder.bus.off === 'function' ) {
544 + builder.bus.off( event_name, on_structure_loaded );
545 + }
546 + // Run after selection clearing/inspector defaults attached to the same load event.
547 + schedule_initial_deep_link( tabs, 0 );
548 + };
549 +
550 + builder.bus.on( event_name, on_structure_loaded );
551 + } );
552 + }
553 +
554 + // Boot once DOM is ready.
555 + const instance = new WPBC_BFB_Rightbar_Tabs();
556 + const boot = () => {
557 + instance.init();
558 + bind_initial_deep_link_after_form_load( instance );
559 + };
560 + if ( d.readyState === 'loading' ) {
561 + d.addEventListener( 'DOMContentLoaded', boot );
562 + } else {
563 + boot();
564 + }
316 565
317 566 // (Optional) expose for debugging:
318 567 // w.WPBC_BFB_Rightbar_Tabs = instance;
319 568