PluginProbe
Gutenberg / 24.1.0
Gutenberg v24.1.0
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
gutenberg / build / modules / block-library / tabs / view.js

view.js in Gutenberg 24.1.0, at build/modules/block-library/tabs/view.js

216 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 );
215 //# sourceMappingURL=view.js.map
216