PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.2.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.2.0
2.4.0 2.3.0 2.2.0 2.1.0 2.0.0 1.11.2 1.11.1 1.11.0 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.6 1.9.5 trunk 1.3.0 1.3.1 1.3.2 1.4.0 All 65 releases
subscription / assets / js / admin-components / tabs.js

tabs.js in Subscriptions for WooCommerce with Stripe Recurring Payments 2.2.0, at assets/js/admin-components/tabs.js

144 lines 4.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * WPSubsTabs — accessible tabbed panels.
3 *
4 * Markup:
5 * <div class="wpsubs-tabs">
6 * <div class="wpsubs-tabs__list" role="tablist">
7 * <button class="wpsubs-tabs__tab" role="tab" id="t1" aria-controls="p1" aria-selected="true">One</button>
8 * <button class="wpsubs-tabs__tab" role="tab" id="t2" aria-controls="p2">Two</button>
9 * </div>
10 * <div class="wpsubs-tab-panel" role="tabpanel" id="p1" aria-labelledby="t1">…</div>
11 * <div class="wpsubs-tab-panel" role="tabpanel" id="p2" aria-labelledby="t2" hidden>…</div>
12 * </div>
13 *
14 * The tab with aria-selected="true" (or the first tab) is active on load.
15 *
16 * Persistence (opt-in): add data-tabs-query="<param>" to the root and the
17 * active tab's key is written to that URL query param (via replaceState, no
18 * reload). On load the tab matching the param is restored, else the first tab.
19 * Each tab's key comes from data-tab-key, falling back to its id.
20 *
21 * Events fired on the root element (bubbles):
22 * wpsubs:tab:change — { id } the newly selected tab's id
23 */
24 (function () {
25 "use strict";
26
27 /**
28 * @param {HTMLElement} el Root .wpsubs-tabs element.
29 */
30 function WPSubsTabs(el) {
31 this.el = el;
32 this.tabs = Array.prototype.slice.call(el.querySelectorAll(".wpsubs-tabs__tab"));
33 this.queryParam = el.getAttribute("data-tabs-query") || "";
34 this._bind();
35
36 var self = this;
37
38 // Restore from the URL query param (opt-in), else the pre-selected tab,
39 // else the first tab.
40 var fromQuery = null;
41 if (this.queryParam) {
42 var wanted = new URLSearchParams(window.location.search).get(this.queryParam);
43 if (wanted) {
44 fromQuery = this.tabs.filter(function (t) {
45 return self._key(t) === wanted;
46 })[0];
47 }
48 }
49 var selected = this.tabs.filter(function (t) {
50 return t.getAttribute("aria-selected") === "true";
51 })[0];
52 this.activate(fromQuery || selected || this.tabs[0], false);
53 }
54
55 /** Stable key for a tab: data-tab-key, else its id. */
56 WPSubsTabs.prototype._key = function (tab) {
57 return tab.getAttribute("data-tab-key") || tab.id || "";
58 };
59
60 /** Panel controlled by a tab (via aria-controls). */
61 WPSubsTabs.prototype._panel = function (tab) {
62 if (!tab) return null;
63 var id = tab.getAttribute("aria-controls");
64 return id ? this.el.querySelector("#" + id) : null;
65 };
66
67 /**
68 * Show one tab's panel, hide the rest.
69 *
70 * @param {HTMLElement} tab Tab to activate.
71 * @param {boolean} [focus] Move focus to the tab (keyboard nav).
72 * @param {boolean} [updateUrl] Write the tab key to the URL query param.
73 */
74 WPSubsTabs.prototype.activate = function (tab, focus, updateUrl) {
75 if (!tab) return;
76 var self = this;
77
78 this.tabs.forEach(function (t) {
79 var active = t === tab;
80 t.setAttribute("aria-selected", active ? "true" : "false");
81 t.setAttribute("tabindex", active ? "0" : "-1");
82 var panel = self._panel(t);
83 if (panel) panel.hidden = !active;
84 });
85
86 if (focus) tab.focus();
87
88 if (updateUrl && this.queryParam) {
89 var url = new URL(window.location.href);
90 url.searchParams.set(this.queryParam, this._key(tab));
91 window.history.replaceState(null, "", url);
92 }
93
94 this.el.dispatchEvent(
95 new CustomEvent("wpsubs:tab:change", {
96 bubbles: true,
97 detail: { id: tab.id || "" },
98 }),
99 );
100 };
101
102 WPSubsTabs.prototype._bind = function () {
103 var self = this;
104
105 this.tabs.forEach(function (tab, index) {
106 tab.addEventListener("click", function (e) {
107 e.preventDefault();
108 self.activate(tab, false, true);
109 });
110
111 tab.addEventListener("keydown", function (e) {
112 var dir = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0;
113 if (!dir) return;
114 e.preventDefault();
115 var next = (index + dir + self.tabs.length) % self.tabs.length;
116 self.activate(self.tabs[next], true, true);
117 });
118 });
119 };
120
121 /**
122 * Initialise all un-initialised .wpsubs-tabs elements under root.
123 *
124 * @param {Document|HTMLElement} [root]
125 */
126 function init(root) {
127 (root || document).querySelectorAll(".wpsubs-tabs:not([data-tabs-init])").forEach(function (el) {
128 el.setAttribute("data-tabs-init", "1");
129 new WPSubsTabs(el);
130 });
131 }
132
133 if (document.readyState === "loading") {
134 document.addEventListener("DOMContentLoaded", function () {
135 init();
136 });
137 } else {
138 init();
139 }
140
141 // Public API
142 window.WPSubsTabs = { init: init };
143 })();
144