| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 // packages/block-library/build-module/tabs/view.mjs 2 import { 3 store, 4 getContext, 5 getElement, 6 withSyncEvent 7 } from "@wordpress/interactivity"; 8 var { actions, state } = store( 9 "core/tabs", 10 { 11 state: { 12 /** 13 * Gets a contextually aware list of tabs for the current tabs block. 14 * 15 * @type {Array} 16 */ 17 get tabsList() { 18 const context = getContext(); 19 const tabsId = context?.tabsId; 20 const tabsList = state[tabsId]; 21 return tabsList; 22 }, 23 /** 24 * Gets the index of the active tab element whether it 25 * is a tab label or tab panel. 26 * 27 * @type {number|null} 28 */ 29 get tabIndex() { 30 const { attributes } = getElement(); 31 const tabId = attributes?.id?.replace("tab__", "") || null; 32 if (!tabId) { 33 return null; 34 } 35 const { tabsList } = state; 36 const tabIndex = tabsList.findIndex((t) => t === tabId); 37 return tabIndex; 38 }, 39 /** 40 * Whether the tab panel or tab label is the active tab. 41 * 42 * @type {boolean} 43 */ 44 get isActiveTab() { 45 const { activeTabIndex } = getContext(); 46 const { tabIndex } = state; 47 return activeTabIndex === tabIndex; 48 }, 49 /** 50 * The value of the hidden attribute for tab panels. 51 * 52 * Inactive panels use the `until-found` state rather than being 53 * hidden outright, so their content stays reachable by the 54 * browser's find-in-page and by fragment navigation. The browser 55 * fires `beforematch` on the panel before revealing it, which is 56 * where the matching tab gets activated. 57 * 58 * @type {string|null} 59 */ 60 get isHidden() { 61 return state.isActiveTab ? null : "until-found"; 62 }, 63 /** 64 * The value of the tabindex attribute for tab buttons and tab 65 * panels. Only the active tab should be in the tab sequence. 66 * 67 * An inactive panel is hidden with `until-found`, which leaves it 68 * in the layout, so it stays focusable unless it is taken out of 69 * the tab sequence here. 70 * 71 * @type {number} 72 */ 73 get tabIndexAttribute() { 74 return state.isActiveTab ? 0 : -1; 75 } 76 }, 77 actions: { 78 /** 79 * Handles the keydown events for the tab label and tabs controller. 80 * 81 * @param {KeyboardEvent} event The keydown event. 82 */ 83 handleTabKeyDown: withSyncEvent((event) => { 84 const { tabIndex, tabsList } = state; 85 if (tabIndex === null) { 86 return; 87 } 88 if (event.key === "ArrowRight") { 89 event.preventDefault(); 90 actions.moveFocus(tabIndex + 1); 91 } else if (event.key === "ArrowLeft") { 92 event.preventDefault(); 93 actions.moveFocus(tabIndex - 1); 94 } else if (event.key === "Home") { 95 event.preventDefault(); 96 actions.moveFocus(0); 97 } else if (event.key === "End") { 98 event.preventDefault(); 99 actions.moveFocus(tabsList.length - 1); 100 } 101 }), 102 /** 103 * Handles the click event for the tab label. 104 * 105 * @param {MouseEvent} event The click event. 106 */ 107 handleTabClick: withSyncEvent((event) => { 108 event.preventDefault(); 109 const { tabIndex } = state; 110 if (tabIndex !== null) { 111 actions.setActiveTab(tabIndex); 112 } 113 }), 114 /** 115 * Activates the tab whose panel the browser is about to reveal. 116 * 117 * Fired on a panel hidden with `until-found` when find-in-page or 118 * a fragment navigation matches content inside it. 119 */ 120 handleBeforeMatch: () => { 121 const { tabIndex } = state; 122 if (tabIndex !== null) { 123 actions.setActiveTab(tabIndex); 124 } 125 }, 126 /** 127 * Moves focus to a specific tab without activating it. 128 * 129 * @param {number} tabIndex The index to move focus to. 130 */ 131 moveFocus: (tabIndex) => { 132 const { tabsList } = state; 133 if (!tabsList || tabsList.length === 0) { 134 return; 135 } 136 let newIndex = tabIndex; 137 if (newIndex < 0) { 138 newIndex = tabsList.length - 1; 139 } else if (newIndex >= tabsList.length) { 140 newIndex = 0; 141 } 142 const tabId = tabsList[newIndex]; 143 const tabElement = document.getElementById("tab__" + tabId); 144 if (tabElement) { 145 tabElement.focus(); 146 } 147 }, 148 /** 149 * Sets the active tab index. 150 * 151 * @param {number} tabIndex The index of the active tab. 152 * @param {boolean} scrollToTab Whether to scroll to the tab element. 153 */ 154 setActiveTab: (tabIndex, scrollToTab = false) => { 155 const { tabsList } = state; 156 if (!tabsList || tabsList.length === 0) { 157 return; 158 } 159 let newIndex = tabIndex; 160 if (newIndex < 0) { 161 newIndex = 0; 162 } else if (newIndex >= tabsList.length) { 163 newIndex = tabsList.length - 1; 164 } 165 const context = getContext(); 166 context.activeTabIndex = newIndex; 167 if (scrollToTab) { 168 const tabId = tabsList[newIndex]; 169 const tabElement = document.getElementById(tabId); 170 if (tabElement) { 171 setTimeout(() => { 172 tabElement.scrollIntoView({ behavior: "smooth" }); 173 }, 100); 174 } 175 } 176 } 177 }, 178 callbacks: { 179 activateTabByHash: () => { 180 const { tabsList } = state; 181 if (!tabsList || tabsList.length === 0) { 182 return; 183 } 184 const targetElement = document.querySelector(":target"); 185 if (!targetElement) { 186 return; 187 } 188 const panelIndex = tabsList.findIndex( 189 (t) => t === targetElement.id 190 ); 191 if (panelIndex >= 0) { 192 actions.setActiveTab(panelIndex, true); 193 return; 194 } 195 let panel = targetElement.closest(".wp-block-tab-panel"); 196 let tabIndex = -1; 197 while (panel && tabIndex < 0) { 198 tabIndex = tabsList.findIndex((t) => t === panel.id); 199 panel = panel.parentElement?.closest(".wp-block-tab-panel") ?? null; 200 } 201 if (tabIndex < 0) { 202 return; 203 } 204 actions.setActiveTab(tabIndex); 205 window.setTimeout(() => { 206 targetElement.scrollIntoView(); 207 }, 0); 208 } 209 } 210 }, 211 { 212 lock: true 213 } 214 );
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Thu Sep 24 08:20:34 2026 | Cross-referenced by PHPXref |