/** * Contact Forms - Tab component for WordPress admin. * * Implements the W3C ARIA Tabs pattern with keyboard navigation. * Supports nesting (each instance scopes to its own direct tabs/panels). * * HTML structure: *
*
* * ... *
*
* ...content... *
* ... *
* * Usage: * $(selector).cfTabs(); * $(selector).cfTabs({ onSelect: function(tabId, $panel) { ... } }); */ (function($) { 'use strict'; $.fn.cfTabs = function(options) { var defaults = { onSelect: null }; var settings = $.extend({}, defaults, options); return this.each(function() { var $container = $(this); // Prevent double-init if ($container.hasClass('is-initialized')) { return; } // Scope: only direct tablist and panels (not nested ones) // Tablist may be a direct child or inside a header wrapper var $tablist = $container.children('[role="tablist"]'); if (!$tablist.length) { $tablist = $container.children('.accua-tabs__header').children('[role="tablist"]'); } var $tabs = $tablist.find('> [role="tab"]'); var $panels = $container.children('[role="tabpanel"]'); if (!$tabs.length || !$panels.length) { return; } // Wire up ARIA ids $tabs.each(function() { var $tab = $(this); var tabId = $tab.attr('data-tab'); var tabElId = $container.attr('id') + '-tab-' + tabId; var panelElId = $container.attr('id') + '-panel-' + tabId; $tab.attr('id', tabElId); $tab.attr('aria-controls', panelElId); $panels.filter('[data-tab="' + tabId + '"]') .attr('id', panelElId) .attr('aria-labelledby', tabElId); }); // Determine default tab var defaultTab = $container.attr('data-default-tab') || $tabs.first().attr('data-tab'); function selectTab(tabId, focus) { // Deselect all $tabs.attr('aria-selected', 'false').attr('tabindex', '-1'); $panels.attr('hidden', ''); // Select target var $activeTab = $tabs.filter('[data-tab="' + tabId + '"]'); var $activePanel = $panels.filter('[data-tab="' + tabId + '"]'); $activeTab.attr('aria-selected', 'true').attr('tabindex', '0'); $activePanel.removeAttr('hidden'); if (focus) { $activeTab.trigger('focus'); } if (typeof settings.onSelect === 'function') { settings.onSelect(tabId, $activePanel); } } // Click handler $tabs.on('click', function(e) { e.preventDefault(); selectTab($(this).attr('data-tab'), false); }); // Keyboard navigation (roving tabindex) $tabs.on('keydown', function(e) { var index = $tabs.index(this); var newIndex = null; switch (e.key) { case 'ArrowLeft': case 'ArrowUp': newIndex = index > 0 ? index - 1 : $tabs.length - 1; break; case 'ArrowRight': case 'ArrowDown': newIndex = index < $tabs.length - 1 ? index + 1 : 0; break; case 'Home': newIndex = 0; break; case 'End': newIndex = $tabs.length - 1; break; } if (newIndex !== null) { e.preventDefault(); selectTab($tabs.eq(newIndex).attr('data-tab'), true); } }); // Initialize: select default tab, mark container selectTab(defaultTab, false); $container.addClass('is-initialized'); }); }; // Form editor initialization $(document).ready(function() { var $tabs = $('#accua_tabs'); if (!$tabs.length) { return; } var $preview = $('#accua_form_preview_pane'); var $saveBtn = $('#accua_form_save_settings_top'); // Tabs that show the preview pane alongside var previewTabs = { fields: true, customise: true }; $tabs.find('.hidden').removeClass('hidden'); $tabs.cfTabs({ onSelect: function(tabId) { // Show/hide preview (no DOM movement - avoids iframe reload) if (previewTabs[tabId]) { $preview.removeAttr('hidden'); } else { $preview.attr('hidden', ''); } // Hide save button on tokens tab (read-only reference) if (tabId === 'tokens') { $saveBtn.hide(); } else { $saveBtn.show(); } } }); }); })(jQuery);