PluginProbe
Gutenberg / trunk
Gutenberg vtrunk
24.1.0 24.0.0 23.9.1 23.9.0 23.8.0 23.7.2 23.7.1 23.7.0 23.6.1 23.6.2 23.6.0 23.5.3 23.5.2 23.5.1 23.5.0 23.4.0 23.3.2 23.3.1 23.3.0 23.2.0 23.2.1 23.2.2 23.1.1 23.1.0 23.0.1 All 404 releases
← All changes | build/modules/block-library/tabs/view.js +64 -14 23.5.2 → trunk View file →
@@ -46,11 +46,29 @@
46 46 const { tabIndex } = state;
47 47 return activeTabIndex === tabIndex;
48 48 },
49 49 /**
50 - * The value of the tabindex attribute for tab buttons.
51 - * Only the active tab should be in the tab sequence.
50 + * The value of the hidden attribute for tab panels.
52 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 + *
53 71 * @type {number}
54 72 */
55 73 get tabIndexAttribute() {
56 74 return state.isActiveTab ? 0 : -1;
@@ -62,9 +80,9 @@
62 80 *
63 81 * @param {KeyboardEvent} event The keydown event.
64 82 */
65 83 handleTabKeyDown: withSyncEvent((event) => {
66 - const { tabIndex } = state;
84 + const { tabIndex, tabsList } = state;
67 85 if (tabIndex === null) {
68 86 return;
69 87 }
70 88 if (event.key === "ArrowRight") {
@@ -72,8 +90,14 @@
72 90 actions.moveFocus(tabIndex + 1);
73 91 } else if (event.key === "ArrowLeft") {
74 92 event.preventDefault();
75 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);
76 100 }
77 101 }),
78 102 /**
79 103 * Handles the click event for the tab label.
@@ -87,8 +111,20 @@
87 111 actions.setActiveTab(tabIndex);
88 112 }
89 113 }),
90 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 + /**
91 127 * Moves focus to a specific tab without activating it.
92 128 *
93 129 * @param {number} tabIndex The index to move focus to.
94 130 */
@@ -139,23 +175,37 @@
139 175 }
140 176 }
141 177 },
142 178 callbacks: {
143 - /**
144 - * When the tabs are initialized, we need to check if there is a hash in the url and if so if it exists in the current tabsList, set the active tab to that index.
145 - *
146 - */
147 - onTabsInit: () => {
179 + activateTabByHash: () => {
148 180 const { tabsList } = state;
149 - if (tabsList.length === 0) {
181 + if (!tabsList || tabsList.length === 0) {
150 182 return;
151 183 }
152 - const { hash } = window.location;
153 - const tabId = hash.replace("#", "");
154 - const tabIndex = tabsList.findIndex((t) => t === tabId);
155 - if (tabIndex >= 0) {
156 - actions.setActiveTab(tabIndex, true);
184 + const targetElement = document.querySelector(":target");
185 + if (!targetElement) {
186 + return;
157 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);
158 208 }
159 209 }
160 210 },
161 211 {