@ulu/frontend 0.2.0-beta.9 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.dev.md +36 -13
- package/README.md +3 -1
- package/dist/es/core/events.js +36 -25
- package/dist/es/core/settings.js +33 -22
- package/dist/es/index.js +47 -45
- package/dist/es/ui/dialog.js +57 -46
- package/dist/es/ui/index.d.ts +1 -0
- package/dist/es/ui/modal-builder.js +39 -28
- package/dist/es/ui/overflow-scroller.js +30 -24
- package/dist/es/ui/proxy-click.js +37 -26
- package/dist/es/ui/resizer.js +57 -49
- package/dist/es/ui/slider.d.ts.map +1 -1
- package/dist/es/ui/slider.js +90 -67
- package/dist/es/ui/tab-manager.d.ts +145 -0
- package/dist/es/ui/tab-manager.d.ts.map +1 -0
- package/dist/es/ui/tab-manager.js +155 -0
- package/dist/es/ui/tabs.d.ts +5 -5
- package/dist/es/ui/tabs.d.ts.map +1 -1
- package/dist/es/ui/tabs.js +34 -51
- package/dist/es/ui/theme-toggle.js +80 -69
- package/dist/es/ui/tooltip.js +53 -44
- package/dist/es/utils/floating-ui.js +35 -24
- package/dist/umd/frontend.css +1 -0
- package/dist/umd/ulu-frontend.umd.js +40 -47
- package/lib/js/exports.md +1 -0
- package/lib/js/ui/index.js +4 -0
- package/lib/js/ui/slider.js +3 -3
- package/lib/js/ui/tab-manager.js +324 -0
- package/lib/js/ui/tabs.js +33 -92
- package/lib/scss/_breakpoint.scss +3 -3
- package/lib/scss/_button.scss +3 -3
- package/lib/scss/_color.scss +3 -2
- package/lib/scss/_element.scss +10 -4
- package/lib/scss/_layout.scss +11 -4
- package/lib/scss/_selector.scss +2 -1
- package/lib/scss/_typography.scss +9 -10
- package/lib/scss/_utils.scss +52 -19
- package/lib/scss/base/_elements.scss +1 -1
- package/lib/scss/components/_basic-hero.scss +1 -1
- package/lib/scss/components/_button-verbose.scss +2 -2
- package/lib/scss/components/_callout.scss +3 -4
- package/lib/scss/components/_css-icon.scss +2 -2
- package/lib/scss/components/_data-grid.scss +5 -5
- package/lib/scss/components/_flipcard.scss +3 -2
- package/lib/scss/components/_index.scss +6 -0
- package/lib/scss/components/_panel.scss +1 -1
- package/lib/scss/components/_popover.scss +9 -6
- package/lib/scss/components/_tabs.scss +20 -5
- package/lib/scss/components/_tagged.scss +59 -0
- package/package.json +25 -35
- package/dist/umd/style.css +0 -1
- package/lib/js/ui/dialog.todo +0 -3
package/dist/es/ui/slider.js
CHANGED
|
@@ -1,46 +1,70 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
1
|
+
var $ = Object.defineProperty;
|
|
2
|
+
var w = Object.getOwnPropertySymbols;
|
|
3
|
+
var I = Object.prototype.hasOwnProperty, A = Object.prototype.propertyIsEnumerable;
|
|
4
|
+
var b = (o, t, s) => t in o ? $(o, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : o[t] = s, v = (o, t) => {
|
|
5
|
+
for (var s in t || (t = {}))
|
|
6
|
+
I.call(t, s) && b(o, s, t[s]);
|
|
7
|
+
if (w)
|
|
8
|
+
for (var s of w(t))
|
|
9
|
+
A.call(t, s) && b(o, s, t[s]);
|
|
10
|
+
return o;
|
|
11
|
+
};
|
|
12
|
+
var f = (o, t, s) => b(o, typeof t != "symbol" ? t + "" : t, s);
|
|
13
|
+
var y = (o, t, s) => new Promise((e, i) => {
|
|
14
|
+
var n = (c) => {
|
|
15
|
+
try {
|
|
16
|
+
r(s.next(c));
|
|
17
|
+
} catch (u) {
|
|
18
|
+
i(u);
|
|
19
|
+
}
|
|
20
|
+
}, a = (c) => {
|
|
21
|
+
try {
|
|
22
|
+
r(s.throw(c));
|
|
23
|
+
} catch (u) {
|
|
24
|
+
i(u);
|
|
25
|
+
}
|
|
26
|
+
}, r = (c) => c.done ? e(c.value) : Promise.resolve(c.value).then(n, a);
|
|
27
|
+
r((s = s.apply(o, t)).next());
|
|
28
|
+
});
|
|
29
|
+
import { ComponentInitializer as z } from "../core/component.js";
|
|
30
|
+
import { wrapSettingString as T } from "../core/settings.js";
|
|
31
|
+
import { hasRequiredProps as D } from "@ulu/utils/object.js";
|
|
32
|
+
import { trimWhitespace as L } from "@ulu/utils/string.js";
|
|
33
|
+
import { debounce as M } from "@ulu/utils/performance.js";
|
|
34
|
+
import { logError as x, log as B, logWarning as S } from "../utils/class-logger.js";
|
|
35
|
+
import N from "swipe-listener";
|
|
36
|
+
const C = new z({
|
|
13
37
|
type: "slider",
|
|
14
38
|
baseAttribute: "data-ulu-slider"
|
|
15
|
-
}),
|
|
39
|
+
}), q = C.attributeSelector("track"), P = C.attributeSelector("track-container"), V = C.attributeSelector("control-context"), F = C.attributeSelector("slide"), H = [], m = { once: !0 }, k = (o) => `${o}ms`, O = [
|
|
16
40
|
"container",
|
|
17
41
|
"trackContainer",
|
|
18
42
|
"track",
|
|
19
43
|
"slides"
|
|
20
44
|
];
|
|
21
|
-
function
|
|
22
|
-
|
|
45
|
+
function Y() {
|
|
46
|
+
C.init({
|
|
23
47
|
withData: !0,
|
|
24
48
|
coreEvents: ["pageModified"],
|
|
25
|
-
setup({ element:
|
|
26
|
-
|
|
49
|
+
setup({ element: o, data: t, initialize: s }) {
|
|
50
|
+
R(o, t), s();
|
|
27
51
|
}
|
|
28
52
|
});
|
|
29
53
|
}
|
|
30
|
-
function
|
|
54
|
+
function R(o, t) {
|
|
31
55
|
const s = Object.assign({}, t), e = {
|
|
32
|
-
container:
|
|
33
|
-
track:
|
|
34
|
-
trackContainer:
|
|
35
|
-
controlContext:
|
|
36
|
-
slides:
|
|
56
|
+
container: o,
|
|
57
|
+
track: o.querySelector(q),
|
|
58
|
+
trackContainer: o.querySelector(P),
|
|
59
|
+
controlContext: o.querySelector(V),
|
|
60
|
+
slides: o.querySelectorAll(F)
|
|
37
61
|
};
|
|
38
|
-
e.slides.length &&
|
|
62
|
+
e.slides.length && H.push(new E(e, s, !1));
|
|
39
63
|
}
|
|
40
64
|
const l = class l {
|
|
41
65
|
static _initializeGlobals() {
|
|
42
66
|
l.globalsInitialized || (addEventListener("load", () => {
|
|
43
|
-
addEventListener("resize",
|
|
67
|
+
addEventListener("resize", M(() => {
|
|
44
68
|
l.instances.forEach((t) => t.handleResize());
|
|
45
69
|
}, 250));
|
|
46
70
|
}), l.reduceMotion = matchMedia("(prefers-reduced-motion: reduce)").matches, l.globalsInitialized = !0);
|
|
@@ -48,15 +72,11 @@ const l = class l {
|
|
|
48
72
|
constructor(t, s) {
|
|
49
73
|
l._initializeGlobals();
|
|
50
74
|
const e = Object.assign({}, l.defaults, s);
|
|
51
|
-
this.options = e, this.slide = null, this.index = null, this.swipeInstance = null, this.swipeListener = null, this.swipeImageListener = null, this.transitioning = !1,
|
|
75
|
+
this.options = e, this.slide = null, this.index = null, this.swipeInstance = null, this.swipeListener = null, this.swipeImageListener = null, this.transitioning = !1, D(O) || x(this, "Missing a required Element"), t.slides.length || x(this, "Missing slides"), this.slides = [...t.slides].map((i, n) => ({
|
|
52
76
|
element: i,
|
|
53
77
|
index: n,
|
|
54
78
|
number: n + 1
|
|
55
|
-
})), this.elements = {
|
|
56
|
-
...t,
|
|
57
|
-
...this.createControls(t.controlContext || t.container),
|
|
58
|
-
...this.createNav(t.navContext || t.container)
|
|
59
|
-
}, this.transition = e.transition ? e.transitionFade || l.reduceMotion ? this.fadeTransition : this.slideTransition : this.noTransition, this.setup(), this.goto(0, null, "init"), A(this, "Slider Instance Created", this), l.instances.push(this);
|
|
79
|
+
})), this.elements = v(v(v({}, t), this.createControls(t.controlContext || t.container)), this.createNav(t.navContext || t.container)), this.transition = e.transition ? e.transitionFade || l.reduceMotion ? this.fadeTransition : this.slideTransition : this.noTransition, this.setup(), this.goto(0, null, "init"), B(this, "Slider Instance Created", this), l.instances.push(this);
|
|
60
80
|
}
|
|
61
81
|
/**
|
|
62
82
|
* Sliding mechanism needs translate updated on resize
|
|
@@ -69,8 +89,8 @@ const l = class l {
|
|
|
69
89
|
* Goto to the previous slide
|
|
70
90
|
*/
|
|
71
91
|
previous(t) {
|
|
72
|
-
const { index: s, slides: e } = this, i = e.length - 1, n = s - 1,
|
|
73
|
-
this.emit("previous", [t,
|
|
92
|
+
const { index: s, slides: e } = this, i = e.length - 1, n = s - 1, a = n < 0 ? i : n;
|
|
93
|
+
this.emit("previous", [t, a]), this.goto(a, t, "previous");
|
|
74
94
|
}
|
|
75
95
|
/**
|
|
76
96
|
* Goto to the next slide
|
|
@@ -88,12 +108,12 @@ const l = class l {
|
|
|
88
108
|
*/
|
|
89
109
|
ensureTransitionEnds(t, s, e) {
|
|
90
110
|
return new Promise((i) => {
|
|
91
|
-
const n = {},
|
|
111
|
+
const n = {}, a = () => {
|
|
92
112
|
clearTimeout(n.start), n.end = setTimeout(r, s + 500);
|
|
93
113
|
}, r = () => {
|
|
94
|
-
clearTimeout(n.start), clearTimeout(n.end), t.removeEventListener("transitionrun",
|
|
114
|
+
clearTimeout(n.start), clearTimeout(n.end), t.removeEventListener("transitionrun", a, m), t.removeEventListener("transitionend", r, m), t.removeEventListener("transitioncancel", r, m), i();
|
|
95
115
|
};
|
|
96
|
-
t.addEventListener("transitionrun",
|
|
116
|
+
t.addEventListener("transitionrun", a, m), t.addEventListener("transitionend", r, m), t.addEventListener("transitioncancel", r, m), n.start = setTimeout(r, s + 500), t.style.transitionDuration = k(s), e(), s || r();
|
|
97
117
|
});
|
|
98
118
|
}
|
|
99
119
|
/**
|
|
@@ -126,16 +146,20 @@ const l = class l {
|
|
|
126
146
|
/**
|
|
127
147
|
* Handler for the entire slide transtion
|
|
128
148
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
149
|
+
slideTransition(a) {
|
|
150
|
+
return y(this, arguments, function* ({ slide: t, index: s, old: e, oldIndex: i, triggerType: n }) {
|
|
151
|
+
const r = this.slides.length, c = n === "previous", u = r - 1, h = s === 0 && i === u, p = s === u && i === 0;
|
|
152
|
+
let d, g = this.options.transitionDuration;
|
|
153
|
+
i && !h && !p && (g = g * Math.abs(i - s)), r < 3 ? h && !c ? d = e : p && (d = c ? t : e) : h ? d = e : p && (d = t), this.setVisibility(null, !0), d && (d.element.style.order = "-1", yield this.translateTo(h ? 0 : e.element.offsetLeft, 0)), yield this.translateTo(t.element.offsetLeft, g), d && (d.element.style.order = "0", yield this.translateTo(t.element.offsetLeft, 0)), this.setVisibility(t, !1);
|
|
154
|
+
});
|
|
133
155
|
}
|
|
134
156
|
/**
|
|
135
157
|
* Handler for the entire fade transtion
|
|
136
158
|
*/
|
|
137
|
-
|
|
138
|
-
|
|
159
|
+
fadeTransition(e) {
|
|
160
|
+
return y(this, arguments, function* ({ slide: t, old: s }) {
|
|
161
|
+
this.setVisibility(null, !0), s && (yield this.fadeSlide(s, !1), s.element.style.order = "0"), t.element.style.order = "-1", yield this.fadeSlide(t, !0), this.setVisibility(t, !1);
|
|
162
|
+
});
|
|
139
163
|
}
|
|
140
164
|
/**
|
|
141
165
|
* Handler for the entire NO transtion
|
|
@@ -147,26 +171,25 @@ const l = class l {
|
|
|
147
171
|
const {
|
|
148
172
|
slide: i,
|
|
149
173
|
index: n,
|
|
150
|
-
slides:
|
|
174
|
+
slides: a,
|
|
151
175
|
elements: r
|
|
152
|
-
} = this,
|
|
176
|
+
} = this, c = e === "init", u = a[t], h = this.getClass("nav-button--active"), p = this.getClass("transition", !0), d = { slide: u, index: t, old: i, oldIndex: n, triggerType: e };
|
|
153
177
|
if (t === n) {
|
|
154
|
-
|
|
178
|
+
S(this, "Could not goto slide, still performing transition");
|
|
155
179
|
return;
|
|
156
180
|
}
|
|
157
181
|
if (this.transitioning) {
|
|
158
|
-
|
|
182
|
+
S(this, "Cancel goto(), same slide index as current slide");
|
|
159
183
|
return;
|
|
160
184
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
this.index = t, this.slide = u, this.transitioning = !1, r.container.classList.remove(c), w.disengage(), C || (u.element.focus(), this.emit("goto", [s, t, u]));
|
|
185
|
+
this.elements.track.inert = !0, this.transitioning = !0, i && i.navButton.classList.remove(h), u.navButton.classList.add(h), r.container.classList.add(p), this.transition(d).then(() => {
|
|
186
|
+
this.index = t, this.slide = u, this.transitioning = !1, this.elements.track.inert = !1, r.container.classList.remove(p), c || (u.element.focus(), this.emit("goto", [s, t, u]));
|
|
164
187
|
});
|
|
165
188
|
}
|
|
166
189
|
setup() {
|
|
167
|
-
const { container: t, track: s, trackContainer: e } = this.elements, i =
|
|
190
|
+
const { container: t, track: s, trackContainer: e } = this.elements, i = L(this.trackCss()), n = L(this.trackContainerStyles()), a = L(this.slideCss());
|
|
168
191
|
s.setAttribute("style", i), e.setAttribute("style", n), this.slides.forEach((r) => {
|
|
169
|
-
r.element.setAttribute("style",
|
|
192
|
+
r.element.setAttribute("style", a), r.element.setAttribute("tabindex", "-1");
|
|
170
193
|
}), t.classList.add(this.getClass()), this.options.swipeEnabled && this.setupSwipe();
|
|
171
194
|
}
|
|
172
195
|
setupSwipe() {
|
|
@@ -177,7 +200,7 @@ const l = class l {
|
|
|
177
200
|
s.preventDefault();
|
|
178
201
|
}, this.slides.forEach((s) => {
|
|
179
202
|
const { element: e } = s;
|
|
180
|
-
s.swipeInstance =
|
|
203
|
+
s.swipeInstance = N(e, this.options.swipeOptions), e.addEventListener("swipe", this.swipeListener);
|
|
181
204
|
}), t.forEach((s) => {
|
|
182
205
|
s.addEventListener("dragstart", this.swipeImageListener);
|
|
183
206
|
});
|
|
@@ -195,7 +218,7 @@ const l = class l {
|
|
|
195
218
|
const { transitionTimingFunction: s, transitionDuration: e } = this.options;
|
|
196
219
|
return `
|
|
197
220
|
transition-property: ${t};
|
|
198
|
-
transition-duration: ${
|
|
221
|
+
transition-duration: ${k(e)};
|
|
199
222
|
transition-timing-function: ${s};
|
|
200
223
|
`;
|
|
201
224
|
}
|
|
@@ -225,19 +248,19 @@ const l = class l {
|
|
|
225
248
|
return s.classList.add(this.getClass("control-button")), s.classList.add(this.getClass(`control-button--${t}`)), s.classList.add(...this.options.buttonClasses), s.setAttribute("data-slider-control", t), s.setAttribute("type", "button"), s.innerHTML = this.getControlContent(t), s;
|
|
226
249
|
}
|
|
227
250
|
createControls(t) {
|
|
228
|
-
const s = document.createElement("ul"), e = document.createElement("li"), i = document.createElement("li"), n = this.createControlButton("previous"),
|
|
229
|
-
return s.classList.add(this.getClass("controls")), e.appendChild(n), i.appendChild(
|
|
251
|
+
const s = document.createElement("ul"), e = document.createElement("li"), i = document.createElement("li"), n = this.createControlButton("previous"), a = this.createControlButton("next");
|
|
252
|
+
return s.classList.add(this.getClass("controls")), e.appendChild(n), i.appendChild(a), s.appendChild(e), s.appendChild(i), n.addEventListener("click", this.previous.bind(this)), a.addEventListener("click", this.next.bind(this)), t.appendChild(s), {
|
|
230
253
|
controls: s,
|
|
231
254
|
previousItem: e,
|
|
232
255
|
nextItem: i,
|
|
233
256
|
previous: n,
|
|
234
|
-
next:
|
|
257
|
+
next: a
|
|
235
258
|
};
|
|
236
259
|
}
|
|
237
260
|
createNav(t) {
|
|
238
261
|
const s = document.createElement("ul"), e = this.slides.map(this.createNavButton.bind(this)), i = e.map((n) => {
|
|
239
|
-
const
|
|
240
|
-
return
|
|
262
|
+
const a = document.createElement("li");
|
|
263
|
+
return a.appendChild(n), s.appendChild(a), a;
|
|
241
264
|
});
|
|
242
265
|
return s.classList.add(this.getClass("nav")), t.appendChild(s), {
|
|
243
266
|
nav: s,
|
|
@@ -263,10 +286,10 @@ const l = class l {
|
|
|
263
286
|
this.options.events[t] && this.options.events[t].apply(this, s);
|
|
264
287
|
}
|
|
265
288
|
};
|
|
266
|
-
|
|
289
|
+
f(l, "instances", []), f(l, "globalsInitialized", !1), f(l, "reduceMotion", !1), /**
|
|
267
290
|
* Default options for slider
|
|
268
291
|
*/
|
|
269
|
-
|
|
292
|
+
f(l, "defaults", {
|
|
270
293
|
classAccessiblyHidden: "hidden-visually",
|
|
271
294
|
namespace: "Slider",
|
|
272
295
|
events: {},
|
|
@@ -276,17 +299,17 @@ m(l, "defaults", {
|
|
|
276
299
|
transitionDurationExit: 400,
|
|
277
300
|
transitionTimingFunction: "ease-in-out",
|
|
278
301
|
buttonClasses: ["button", "button--icon"],
|
|
279
|
-
iconClassPrevious:
|
|
280
|
-
iconClassNext:
|
|
302
|
+
iconClassPrevious: T("iconClassPrevious"),
|
|
303
|
+
iconClassNext: T("iconClassNext"),
|
|
281
304
|
swipeEnabled: !0,
|
|
282
305
|
swipeOptions: {
|
|
283
306
|
preventScroll: !0
|
|
284
307
|
}
|
|
285
308
|
});
|
|
286
|
-
let
|
|
309
|
+
let E = l;
|
|
287
310
|
export {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
311
|
+
E as Slider,
|
|
312
|
+
Y as init,
|
|
313
|
+
C as initializer,
|
|
314
|
+
R as setupSlider
|
|
292
315
|
};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} TabManagerOptions
|
|
3
|
+
* @property {String|null} [orientation=null] - "horizontal"|"vertical", auto-detected if omitted.
|
|
4
|
+
* @property {Number} [initialIndex=0] - Index to activate on load.
|
|
5
|
+
* @property {Boolean} [allArrows=false] - Allow all arrow keys to navigate regardless of orientation.
|
|
6
|
+
* @property {Boolean} [openByUrlHash=false] - Activate tab based on URL hash on initialization.
|
|
7
|
+
* @property {Boolean} [setUrlHash=false] - Update URL hash when a new tab is activated.
|
|
8
|
+
* @property {Boolean} [equalHeights=false] - Automatically match the height of all panels.
|
|
9
|
+
* @property {Function|null} [onReady=null] - Callback fired after initialization: (instance) => {}
|
|
10
|
+
* @property {Function|null} [onChange=null] - Callback fired when tab changes: (active, previous) => {}
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Class for managing Aria tabs
|
|
14
|
+
* - Designed to be minimal and lightweight but cover all traditional needs
|
|
15
|
+
* - Designed for static / traditional webpages (not SPA)
|
|
16
|
+
* - Separated from tabs.js so it can be used by itself as needed (tree-shaking)
|
|
17
|
+
*/
|
|
18
|
+
export class TabManager {
|
|
19
|
+
/**
|
|
20
|
+
* Default options for TabManager.
|
|
21
|
+
* @type {TabManagerOptions}
|
|
22
|
+
*/
|
|
23
|
+
static defaults: TabManagerOptions;
|
|
24
|
+
/**
|
|
25
|
+
* @param {HTMLElement} tablistElement - The element with role="tablist"
|
|
26
|
+
* @param {Partial<TabManagerOptions>} [options] - Configuration options.
|
|
27
|
+
*/
|
|
28
|
+
constructor(tablistElement: HTMLElement, options?: Partial<TabManagerOptions>);
|
|
29
|
+
tablist: HTMLElement;
|
|
30
|
+
options: {
|
|
31
|
+
/**
|
|
32
|
+
* - "horizontal"|"vertical", auto-detected if omitted.
|
|
33
|
+
*/
|
|
34
|
+
orientation?: string | null | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* - Index to activate on load.
|
|
37
|
+
*/
|
|
38
|
+
initialIndex?: number | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* - Allow all arrow keys to navigate regardless of orientation.
|
|
41
|
+
*/
|
|
42
|
+
allArrows?: boolean | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* - Activate tab based on URL hash on initialization.
|
|
45
|
+
*/
|
|
46
|
+
openByUrlHash?: boolean | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* - Update URL hash when a new tab is activated.
|
|
49
|
+
*/
|
|
50
|
+
setUrlHash?: boolean | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* - Automatically match the height of all panels.
|
|
53
|
+
*/
|
|
54
|
+
equalHeights?: boolean | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* - Callback fired after initialization: (instance) => {}
|
|
57
|
+
*/
|
|
58
|
+
onReady?: Function | null | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* - Callback fired when tab changes: (active, previous) => {}
|
|
61
|
+
*/
|
|
62
|
+
onChange?: Function | null | undefined;
|
|
63
|
+
};
|
|
64
|
+
tabs: Element[];
|
|
65
|
+
panels: (HTMLElement | null)[];
|
|
66
|
+
currentIndex: number;
|
|
67
|
+
/**
|
|
68
|
+
* Handles keyboard navigation (arrows, Home, End) on the tab list.
|
|
69
|
+
* @param {KeyboardEvent} e - The keydown event.
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
private handleKeydown;
|
|
73
|
+
/**
|
|
74
|
+
* Handles click events on tabs, activating the corresponding panel.
|
|
75
|
+
* @param {MouseEvent} e - The click event.
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
78
|
+
private handleClick;
|
|
79
|
+
/**
|
|
80
|
+
* Calculates and applies equal heights to all panels.
|
|
81
|
+
* Waits for images within panels to load before calculating.
|
|
82
|
+
*/
|
|
83
|
+
updatePanelHeights(): void;
|
|
84
|
+
orientation: string | undefined;
|
|
85
|
+
/**
|
|
86
|
+
* Sets the necessary ARIA attributes and initial states for tabs and panels.
|
|
87
|
+
* @private
|
|
88
|
+
*/
|
|
89
|
+
private setupAttributes;
|
|
90
|
+
/**
|
|
91
|
+
* Attaches click and keydown event listeners to each tab.
|
|
92
|
+
* @private
|
|
93
|
+
*/
|
|
94
|
+
private attachListeners;
|
|
95
|
+
/**
|
|
96
|
+
* Activates a tab. Can be called with an index or a tab ID string.
|
|
97
|
+
* @param {Number|String} indexOrId - The index or ID of the tab to activate.
|
|
98
|
+
* @param {Boolean} [triggerActions=true] - If false, will not fire onChange or set URL hash.
|
|
99
|
+
*/
|
|
100
|
+
activate(indexOrId: number | string, triggerActions?: boolean): void;
|
|
101
|
+
/**
|
|
102
|
+
* Public method to activate a tab by its ID.
|
|
103
|
+
* @param {String} id - The ID of the tab element to activate.
|
|
104
|
+
*/
|
|
105
|
+
activateById(id: string): void;
|
|
106
|
+
/**
|
|
107
|
+
* Removes event listeners, cleans up ARIA attributes, and resets the DOM to its pre-initialized state.
|
|
108
|
+
*/
|
|
109
|
+
destroy(): void;
|
|
110
|
+
}
|
|
111
|
+
export type TabManagerOptions = {
|
|
112
|
+
/**
|
|
113
|
+
* - "horizontal"|"vertical", auto-detected if omitted.
|
|
114
|
+
*/
|
|
115
|
+
orientation?: string | null | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* - Index to activate on load.
|
|
118
|
+
*/
|
|
119
|
+
initialIndex?: number | undefined;
|
|
120
|
+
/**
|
|
121
|
+
* - Allow all arrow keys to navigate regardless of orientation.
|
|
122
|
+
*/
|
|
123
|
+
allArrows?: boolean | undefined;
|
|
124
|
+
/**
|
|
125
|
+
* - Activate tab based on URL hash on initialization.
|
|
126
|
+
*/
|
|
127
|
+
openByUrlHash?: boolean | undefined;
|
|
128
|
+
/**
|
|
129
|
+
* - Update URL hash when a new tab is activated.
|
|
130
|
+
*/
|
|
131
|
+
setUrlHash?: boolean | undefined;
|
|
132
|
+
/**
|
|
133
|
+
* - Automatically match the height of all panels.
|
|
134
|
+
*/
|
|
135
|
+
equalHeights?: boolean | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* - Callback fired after initialization: (instance) => {}
|
|
138
|
+
*/
|
|
139
|
+
onReady?: Function | null | undefined;
|
|
140
|
+
/**
|
|
141
|
+
* - Callback fired when tab changes: (active, previous) => {}
|
|
142
|
+
*/
|
|
143
|
+
onChange?: Function | null | undefined;
|
|
144
|
+
};
|
|
145
|
+
//# sourceMappingURL=tab-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tab-manager.d.ts","sourceRoot":"","sources":["../../../lib/js/ui/tab-manager.js"],"names":[],"mappings":"AAOA;;;;;;;;;;GAUG;AAEH;;;;;GAKG;AACH;IACE;;;OAGG;IACH,iBAFU,iBAAiB,CAWzB;IAEF;;;OAGG;IACH,4BAHW,WAAW,YACX,OAAO,CAAC,iBAAiB,CAAC,EAqDpC;IAlDC,qBAA6B;IAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAAqD;IACrD,gBAA6C;IAG7C,+BAGkB;IAElB,qBAAsB;IA8FxB;;;;OAIG;IACH,sBAgCC;IA/CD;;;;OAIG;IACH,oBAGC;IAoGD;;;OAGG;IACH,2BAoCC;IA1NC,gCAA4G;IA4B9G;;;OAGG;IACH,wBAyBC;IAED;;;OAGG;IACH,wBAKC;IAmDD;;;;OAIG;IACH,oBAHW,eAAa,kCA+CvB;IAED;;;OAGG;IACH,+BAEC;IA4CD;;OAEG;IACH,gBA6BC;CACF"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
var y = Object.defineProperty;
|
|
2
|
+
var b = Object.getOwnPropertySymbols;
|
|
3
|
+
var A = Object.prototype.hasOwnProperty, x = Object.prototype.propertyIsEnumerable;
|
|
4
|
+
var c = (h, t, i) => t in h ? y(h, t, { enumerable: !0, configurable: !0, writable: !0, value: i }) : h[t] = i, u = (h, t) => {
|
|
5
|
+
for (var i in t || (t = {}))
|
|
6
|
+
A.call(t, i) && c(h, i, t[i]);
|
|
7
|
+
if (b)
|
|
8
|
+
for (var i of b(t))
|
|
9
|
+
x.call(t, i) && c(h, i, t[i]);
|
|
10
|
+
return h;
|
|
11
|
+
};
|
|
12
|
+
var f = (h, t, i) => c(h, typeof t != "symbol" ? t + "" : t, i);
|
|
13
|
+
import { ensureId as p } from "../utils/id.js";
|
|
14
|
+
import { getCoreEventName as g } from "../core/events.js";
|
|
15
|
+
const o = class o {
|
|
16
|
+
/**
|
|
17
|
+
* @param {HTMLElement} tablistElement - The element with role="tablist"
|
|
18
|
+
* @param {Partial<TabManagerOptions>} [options] - Configuration options.
|
|
19
|
+
*/
|
|
20
|
+
constructor(t, i = {}) {
|
|
21
|
+
if (this.tablist = t, this.options = u(u({}, o.defaults), i), this.tabs = Array.from(this.tablist.children), this.panels = this.tabs.map((n) => {
|
|
22
|
+
const s = n.getAttribute("aria-controls");
|
|
23
|
+
return s ? document.getElementById(s) : null;
|
|
24
|
+
}).filter(Boolean), this.currentIndex = -1, this.handleKeydown = this.handleKeydown.bind(this), this.handleClick = this.handleClick.bind(this), this.updatePanelHeights = this.updatePanelHeights.bind(this), this.tabs.length === 0 || this.tabs.length !== this.panels.length) {
|
|
25
|
+
console.warn("TabManager: Tab/Panel count mismatch. Check aria-controls.", { tabs: this.tabs, panels: this.panels });
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
this.orientation = this.options.orientation || this.tablist.getAttribute("aria-orientation") || "horizontal", this.setupAttributes(), this.attachListeners();
|
|
29
|
+
let e = this.options.initialIndex;
|
|
30
|
+
if (this.options.openByUrlHash) {
|
|
31
|
+
const n = window.location.hash.substring(1), s = this.tabs.findIndex((r) => r.id === n);
|
|
32
|
+
s > -1 && (e = s);
|
|
33
|
+
}
|
|
34
|
+
this.activate(e, !1), this.options.equalHeights && (this.updatePanelHeights(), document.addEventListener(g("pageResized"), this.updatePanelHeights)), this.options.onReady && this.options.onReady(this);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Sets the necessary ARIA attributes and initial states for tabs and panels.
|
|
38
|
+
* @private
|
|
39
|
+
*/
|
|
40
|
+
setupAttributes() {
|
|
41
|
+
this.tablist.setAttribute("role", "tablist"), this.tabs.forEach((t, i) => {
|
|
42
|
+
const e = this.panels[i];
|
|
43
|
+
p(t), p(e), t.setAttribute("role", "tab"), t.hasAttribute("aria-controls") || t.setAttribute("aria-controls", e.id), e.setAttribute("role", "tabpanel"), e.setAttribute("aria-labelledby", t.id), e.hidden = !0, t.setAttribute("tabindex", "-1"), t.setAttribute("aria-selected", "false");
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Attaches click and keydown event listeners to each tab.
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
50
|
+
attachListeners() {
|
|
51
|
+
this.tabs.forEach((t) => {
|
|
52
|
+
t.addEventListener("click", this.handleClick), t.addEventListener("keydown", this.handleKeydown);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Handles click events on tabs, activating the corresponding panel.
|
|
57
|
+
* @param {MouseEvent} e - The click event.
|
|
58
|
+
* @private
|
|
59
|
+
*/
|
|
60
|
+
handleClick(t) {
|
|
61
|
+
const i = this.tabs.indexOf(t.currentTarget);
|
|
62
|
+
this.activate(i);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Handles keyboard navigation (arrows, Home, End) on the tab list.
|
|
66
|
+
* @param {KeyboardEvent} e - The keydown event.
|
|
67
|
+
* @private
|
|
68
|
+
*/
|
|
69
|
+
handleKeydown(t) {
|
|
70
|
+
const i = this.tabs.indexOf(t.currentTarget);
|
|
71
|
+
let e = null;
|
|
72
|
+
const n = this.orientation === "vertical", s = this.options.allArrows, r = (this.tablist.dir === "rtl" || document.dir === "rtl") && this.tablist.dir !== "ltr", a = r ? "ArrowLeft" : "ArrowRight", l = r ? "ArrowRight" : "ArrowLeft";
|
|
73
|
+
t.key === "ArrowDown" ? (n || s) && (e = (i + 1) % this.tabs.length) : t.key === "ArrowUp" ? (n || s) && (e = (i - 1 + this.tabs.length) % this.tabs.length) : t.key === a ? (!n || s) && (e = (i + 1) % this.tabs.length) : t.key === l ? (!n || s) && (e = (i - 1 + this.tabs.length) % this.tabs.length) : t.key === "Home" ? e = 0 : t.key === "End" && (e = this.tabs.length - 1), e !== null && (t.preventDefault(), this.activate(e), this.tabs[e].focus());
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Activates a tab. Can be called with an index or a tab ID string.
|
|
77
|
+
* @param {Number|String} indexOrId - The index or ID of the tab to activate.
|
|
78
|
+
* @param {Boolean} [triggerActions=true] - If false, will not fire onChange or set URL hash.
|
|
79
|
+
*/
|
|
80
|
+
activate(t, i = !0) {
|
|
81
|
+
let e = -1;
|
|
82
|
+
if (typeof t == "string" ? e = this.tabs.findIndex((d) => d.id === t) : e = t, e < 0 || e >= this.tabs.length || this.currentIndex === e) return;
|
|
83
|
+
const n = this.currentIndex, s = n > -1 ? this.tabs[n] : null, r = n > -1 ? this.panels[n] : null;
|
|
84
|
+
s && (s.setAttribute("aria-selected", "false"), s.setAttribute("tabindex", "-1"), r.hidden = !0);
|
|
85
|
+
const a = this.tabs[e], l = this.panels[e];
|
|
86
|
+
a.setAttribute("aria-selected", "true"), a.setAttribute("tabindex", "0"), l.hidden = !1, this.currentIndex = e, i && this.options.setUrlHash && window.history && window.history.replaceState(null, "", `#${a.id}`), i && this.options.onChange && this.options.onChange(
|
|
87
|
+
{ index: e, tab: a, panel: l },
|
|
88
|
+
{ index: n, tab: s, panel: r }
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Public method to activate a tab by its ID.
|
|
93
|
+
* @param {String} id - The ID of the tab element to activate.
|
|
94
|
+
*/
|
|
95
|
+
activateById(t) {
|
|
96
|
+
this.activate(t, !0);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Calculates and applies equal heights to all panels.
|
|
100
|
+
* Waits for images within panels to load before calculating.
|
|
101
|
+
*/
|
|
102
|
+
updatePanelHeights() {
|
|
103
|
+
if (!this.panels || this.panels.length === 0) return;
|
|
104
|
+
const t = this.panels[0].parentElement;
|
|
105
|
+
if (!t) return;
|
|
106
|
+
const i = [...t.querySelectorAll("img")], e = (s) => new Promise((r) => {
|
|
107
|
+
if (s.complete) return r(s);
|
|
108
|
+
s.onload = () => r(s), s.onerror = () => r(s);
|
|
109
|
+
}), n = i.map(e);
|
|
110
|
+
Promise.all(n).then(() => {
|
|
111
|
+
this.panels.forEach((a) => {
|
|
112
|
+
a.style.minHeight = "";
|
|
113
|
+
});
|
|
114
|
+
const s = this.panels.map((a) => {
|
|
115
|
+
const l = a.hidden;
|
|
116
|
+
a.hidden = !1;
|
|
117
|
+
const d = a.offsetHeight;
|
|
118
|
+
return a.hidden = l, d;
|
|
119
|
+
}), r = Math.max(...s);
|
|
120
|
+
r > 0 && this.panels.forEach((a) => {
|
|
121
|
+
a.style.minHeight = `${r}px`;
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Removes event listeners, cleans up ARIA attributes, and resets the DOM to its pre-initialized state.
|
|
127
|
+
*/
|
|
128
|
+
destroy() {
|
|
129
|
+
this.tabs.forEach((t) => {
|
|
130
|
+
t.removeEventListener("click", this.handleClick), t.removeEventListener("keydown", this.handleKeydown);
|
|
131
|
+
}), this.options.equalHeights && document.removeEventListener(g("pageResized"), this.updatePanelHeights), this.tablist.removeAttribute("role"), this.tabs.forEach((t) => {
|
|
132
|
+
t.removeAttribute("role"), t.removeAttribute("aria-selected"), t.removeAttribute("tabindex");
|
|
133
|
+
}), this.panels.forEach((t) => {
|
|
134
|
+
t.removeAttribute("role"), t.removeAttribute("aria-labelledby"), t.hidden = !1, t.style.minHeight = "";
|
|
135
|
+
}), this.tablist = null, this.tabs = [], this.panels = [], this.options = {}, this.currentIndex = -1;
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Default options for TabManager.
|
|
140
|
+
* @type {TabManagerOptions}
|
|
141
|
+
*/
|
|
142
|
+
f(o, "defaults", {
|
|
143
|
+
orientation: null,
|
|
144
|
+
initialIndex: 0,
|
|
145
|
+
allArrows: !1,
|
|
146
|
+
openByUrlHash: !1,
|
|
147
|
+
setUrlHash: !1,
|
|
148
|
+
equalHeights: !1,
|
|
149
|
+
onReady: null,
|
|
150
|
+
onChange: null
|
|
151
|
+
});
|
|
152
|
+
let m = o;
|
|
153
|
+
export {
|
|
154
|
+
m as TabManager
|
|
155
|
+
};
|
package/dist/es/ui/tabs.d.ts
CHANGED
|
@@ -4,12 +4,12 @@ import { ComponentInitializer } from '../core/component.js';
|
|
|
4
4
|
*/
|
|
5
5
|
export function init(): void;
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
* @param {
|
|
9
|
-
* @param {
|
|
10
|
-
* @return {
|
|
7
|
+
* Setup a new TabManager instance
|
|
8
|
+
* @param {HTMLElement} element Tablist Element
|
|
9
|
+
* @param {object} options Options to set as defaults
|
|
10
|
+
* @return {object} Instance object
|
|
11
11
|
*/
|
|
12
|
-
export function setup(element:
|
|
12
|
+
export function setup(element: HTMLElement, options?: object): object;
|
|
13
13
|
/**
|
|
14
14
|
* Array of current tab instances (exported if you need to interact with them)
|
|
15
15
|
* @type {Array}
|
package/dist/es/ui/tabs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../../lib/js/ui/tabs.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../../lib/js/ui/tabs.js"],"names":[],"mappings":"AAsBA;;GAEG;AACH,6BAiBC;AAED;;;;;GAKG;AACH,+BAJW,WAAW,YACX,MAAM,GACL,MAAM,CAuCjB;AA/ED;;;GAGG;AACH,8BAA4B;AAE5B;;GAEG;AACH,+CAGG;qCAfkC,sBAAsB"}
|