PluginProbe
Block Animations, Motion & Scroll Effects – Ghost Kit / 3.3.3
Block Animations, Motion & Scroll Effects – Ghost Kit v3.3.3
3.7.2 3.7.1 3.7.0 3.6.1 trunk 1.6.3 2.25.0 3.3.0 3.3.1 3.3.2 3.3.3 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.5.0 3.5.1 3.6.0
ghostkit / gutenberg / blocks / tabs / frontend.js

frontend.js in Block Animations, Motion & Scroll Effects – Ghost Kit 3.3.3, at gutenberg/blocks/tabs/frontend.js

275 lines 6.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import transitionCallback from '../../../assets/js/utils/transition-callback';
2 import { maybeDecode } from '../../utils/encode-decode';
3 import getSiblings from '../../utils/get-siblings';
4
5 /**
6 * Block Tabs
7 */
8 const {
9 location,
10 GHOSTKIT: { events },
11 } = window;
12
13 let pageHash = location.hash;
14
15 const ARROW_LEFT_KEY = 'ArrowLeft';
16 const ARROW_RIGHT_KEY = 'ArrowRight';
17 const ARROW_UP_KEY = 'ArrowUp';
18 const ARROW_DOWN_KEY = 'ArrowDown';
19 const HOME_KEY = 'Home';
20 const END_KEY = 'End';
21
22 function getTabByName($tabs, tabName) {
23 const tabNameEncoded = maybeDecode(tabName);
24
25 let $button = $tabs.querySelector(
26 `:scope > .ghostkit-tabs-buttons > [data-tab="${tabNameEncoded}"]`
27 );
28
29 // Legacy tab name.
30 if (!$button) {
31 $button = $tabs.querySelector(
32 `:scope > .ghostkit-tabs-buttons > [href="#${tabNameEncoded}"]`
33 );
34 }
35 if (!$button) {
36 $button = $tabs.querySelector(
37 `:scope > .ghostkit-tabs-buttons > [href="${tabNameEncoded}"]`
38 );
39 }
40 if (!$button) {
41 $button = $tabs.querySelector(
42 `:scope > .ghostkit-tabs-buttons > [href="#tab-${tabNameEncoded}"]`
43 );
44 }
45
46 const $tab = $button
47 ? $tabs.querySelector(
48 `:scope > .ghostkit-tabs-content > [data-tab="${tabNameEncoded.replace(
49 /^#/,
50 ''
51 )}"]`
52 )
53 : null;
54
55 return [$button, $tab];
56 }
57
58 /**
59 * Activate tab
60 *
61 * @param {Element} $tabs - tabs block element
62 * @param {string} tabName - tab name
63 *
64 * @return {boolean} is tab activated.
65 */
66 function activateTab($tabs, tabName) {
67 const [$activeBtn, $activeTab] = getTabByName($tabs, tabName);
68
69 if (!$activeBtn || !$activeTab) {
70 return false;
71 }
72
73 // Show tab.
74 events.trigger($tabs, 'show.tab.gkt', { relatedTarget: $activeTab });
75
76 const isModern = $activeBtn.nodeName === 'BUTTON';
77
78 $activeBtn.classList.add('ghostkit-tabs-buttons-item-active');
79
80 if (isModern) {
81 $activeBtn.removeAttribute('tabindex');
82 $activeBtn.setAttribute('aria-selected', true);
83 }
84
85 $activeTab.classList.add('ghostkit-tab-active');
86
87 transitionCallback(() => {
88 events.trigger($tabs, 'shown.tab.gkt', { relatedTarget: $activeTab });
89 }, $activeTab);
90
91 // Hide all siblings.
92 getSiblings($activeBtn).forEach(($this) => {
93 if ($this.classList.contains('ghostkit-tabs-buttons-item-active')) {
94 $this.classList.remove('ghostkit-tabs-buttons-item-active');
95
96 if (isModern) {
97 $this.setAttribute('aria-selected', false);
98 $this.setAttribute('tabindex', -1);
99 }
100 }
101 });
102 getSiblings($activeTab).forEach(($this) => {
103 if ($this.classList.contains('ghostkit-tab-active')) {
104 events.trigger($tabs, 'hide.tab.gkt', { relatedTarget: $this });
105
106 $this.classList.remove('ghostkit-tab-active');
107
108 transitionCallback(() => {
109 events.trigger($tabs, 'hidden.tab.gkt', {
110 relatedTarget: $this,
111 });
112 }, $activeTab);
113 }
114 });
115
116 return true;
117 }
118
119 /**
120 * Prepare Tabs.
121 */
122 events.on(document, 'init.blocks.gkt', () => {
123 document
124 .querySelectorAll('.ghostkit-tabs:not(.ghostkit-tabs-ready)')
125 .forEach(($tabs) => {
126 events.trigger($tabs, 'prepare.tabs.gkt');
127
128 $tabs.classList.add('ghostkit-tabs-ready');
129
130 const $buttons = $tabs.querySelector(
131 `:scope > .ghostkit-tabs-buttons[role="tablist"]`
132 );
133
134 // Add tabindex -1 to inactive tabs.
135 if ($buttons) {
136 $buttons
137 .querySelectorAll(':scope > .ghostkit-tabs-buttons-item')
138 .forEach(($btn) => {
139 if (
140 !$btn.classList.contains(
141 'ghostkit-tabs-buttons-item-active'
142 )
143 ) {
144 $btn.setAttribute('tabindex', '-1');
145 }
146 });
147 }
148
149 // activate by page hash
150 let tabActivated = false;
151 if (pageHash) {
152 tabActivated = activateTab($tabs, pageHash.replace('#', ''));
153 }
154
155 // legacy
156 const tabsActive = $tabs.getAttribute('data-tab-active');
157 if (tabsActive) {
158 if (!tabActivated) {
159 tabActivated = activateTab($tabs, `#${tabsActive}`);
160 }
161
162 if (!tabActivated) {
163 tabActivated = activateTab($tabs, tabsActive);
164 }
165 }
166
167 events.trigger($tabs, 'prepared.tabs.gkt');
168 });
169 });
170
171 /**
172 * Click Tabs.
173 */
174 events.on(document, 'click', '.ghostkit-tabs-buttons-item', (e) => {
175 e.preventDefault();
176
177 const $btn = e.delegateTarget;
178 const $tab = $btn.closest('.ghostkit-tabs');
179 const tabName = $btn.getAttribute('data-tab') || $btn.hash;
180
181 activateTab($tab, tabName);
182 });
183
184 /**
185 * Hover Tabs.
186 */
187 events.on(
188 document,
189 'mouseenter',
190 '.ghostkit-tabs-buttons-trigger-hover > .ghostkit-tabs-buttons .ghostkit-tabs-buttons-item',
191 (e) => {
192 const $btn = e.delegateTarget;
193 const $tab = $btn.closest('.ghostkit-tabs');
194 const tabName = $btn.getAttribute('data-tab') || $btn.hash;
195
196 activateTab($tab, tabName);
197 }
198 );
199
200 /*
201 * Activate tab on keydown.
202 */
203 events.on(
204 document,
205 'keydown',
206 '.ghostkit-tabs-buttons[role="tablist"]',
207 (e) => {
208 if (
209 ![
210 ARROW_LEFT_KEY,
211 ARROW_RIGHT_KEY,
212 ARROW_UP_KEY,
213 ARROW_DOWN_KEY,
214 HOME_KEY,
215 END_KEY,
216 ].includes(e.key)
217 ) {
218 return;
219 }
220
221 // stopPropagation/preventDefault both added to support up/down keys without scrolling the page.
222 e.stopPropagation();
223 e.preventDefault();
224
225 const $buttons = e.delegateTarget.querySelectorAll(
226 ':scope > .ghostkit-tabs-buttons-item'
227 );
228
229 let $nextActiveTab;
230
231 if ([HOME_KEY, END_KEY].includes(e.key)) {
232 $nextActiveTab =
233 $buttons[e.key === HOME_KEY ? 0 : $buttons.length - 1];
234 } else {
235 const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(e.key);
236 const currentIndex = [].indexOf.call($buttons, e.target);
237
238 if (isNext) {
239 $nextActiveTab = $buttons[currentIndex + 1] ?? $buttons[0];
240 } else {
241 $nextActiveTab =
242 $buttons[currentIndex - 1] ?? $buttons[$buttons.length - 1];
243 }
244 }
245
246 if ($nextActiveTab) {
247 $nextActiveTab.focus({ preventScroll: true });
248 activateTab(
249 $nextActiveTab.closest('.ghostkit-tabs'),
250 $nextActiveTab.getAttribute('data-tab')
251 );
252 }
253 }
254 );
255
256 /*
257 * Activate tab on hash change.
258 */
259 events.on(window, 'hashchange', function () {
260 if (window.location.hash === pageHash) {
261 return;
262 }
263
264 pageHash = window.location.hash;
265
266 if (!pageHash) {
267 return;
268 }
269
270 // Activate tab.
271 document.querySelectorAll('.ghostkit-tabs-ready').forEach(($this) => {
272 activateTab($this, pageHash.replace('#', ''));
273 });
274 });
275