@ulu/frontend 0.2.0-beta.9 → 0.3.0

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.
Files changed (78) hide show
  1. package/README.dev.md +52 -13
  2. package/README.md +3 -1
  3. package/dist/es/core/events.js +36 -25
  4. package/dist/es/core/settings.js +33 -22
  5. package/dist/es/index.js +47 -45
  6. package/dist/es/ui/dialog.d.ts +3 -1
  7. package/dist/es/ui/dialog.d.ts.map +1 -1
  8. package/dist/es/ui/dialog.js +70 -53
  9. package/dist/es/ui/index.d.ts +1 -0
  10. package/dist/es/ui/modal-builder.d.ts +6 -0
  11. package/dist/es/ui/modal-builder.d.ts.map +1 -1
  12. package/dist/es/ui/modal-builder.js +66 -47
  13. package/dist/es/ui/overflow-scroller.js +30 -24
  14. package/dist/es/ui/proxy-click.js +37 -26
  15. package/dist/es/ui/resizer.js +57 -49
  16. package/dist/es/ui/slider.d.ts.map +1 -1
  17. package/dist/es/ui/slider.js +90 -67
  18. package/dist/es/ui/tab-manager.d.ts +145 -0
  19. package/dist/es/ui/tab-manager.d.ts.map +1 -0
  20. package/dist/es/ui/tab-manager.js +155 -0
  21. package/dist/es/ui/tabs.d.ts +5 -5
  22. package/dist/es/ui/tabs.d.ts.map +1 -1
  23. package/dist/es/ui/tabs.js +34 -51
  24. package/dist/es/ui/theme-toggle.js +80 -69
  25. package/dist/es/ui/tooltip.js +53 -44
  26. package/dist/es/utils/dialog.d.ts +14 -0
  27. package/dist/es/utils/dialog.d.ts.map +1 -0
  28. package/dist/es/utils/dialog.js +16 -0
  29. package/dist/es/utils/floating-ui.js +35 -24
  30. package/dist/es/utils/iframe.d.ts +15 -0
  31. package/dist/es/utils/iframe.d.ts.map +1 -0
  32. package/dist/es/utils/iframe.js +33 -0
  33. package/dist/umd/frontend.css +1 -0
  34. package/dist/umd/ulu-frontend.umd.js +40 -47
  35. package/lib/js/exports.md +1 -0
  36. package/lib/js/ui/dialog.js +23 -3
  37. package/lib/js/ui/index.js +4 -0
  38. package/lib/js/ui/modal-builder.js +21 -0
  39. package/lib/js/ui/slider.js +3 -3
  40. package/lib/js/ui/tab-manager.js +324 -0
  41. package/lib/js/ui/tabs.js +33 -92
  42. package/lib/js/utils/dialog.js +29 -0
  43. package/lib/js/utils/iframe.js +59 -0
  44. package/lib/scss/_breakpoint.scss +3 -3
  45. package/lib/scss/_button.scss +3 -3
  46. package/lib/scss/_color.scss +4 -3
  47. package/lib/scss/_element.scss +25 -4
  48. package/lib/scss/_layout.scss +11 -4
  49. package/lib/scss/_selector.scss +2 -1
  50. package/lib/scss/_typography.scss +9 -10
  51. package/lib/scss/_utils.scss +74 -19
  52. package/lib/scss/base/_elements.scss +4 -1
  53. package/lib/scss/components/_accordion.scss +7 -2
  54. package/lib/scss/components/_badge.scss +1 -1
  55. package/lib/scss/components/_basic-hero.scss +1 -1
  56. package/lib/scss/components/_button-group.scss +8 -3
  57. package/lib/scss/components/_button-verbose.scss +2 -2
  58. package/lib/scss/components/_callout.scss +3 -4
  59. package/lib/scss/components/_card-grid.scss +8 -14
  60. package/lib/scss/components/_card.scss +15 -13
  61. package/lib/scss/components/_css-icon.scss +2 -2
  62. package/lib/scss/components/_data-grid.scss +5 -5
  63. package/lib/scss/components/_data-list.scss +270 -0
  64. package/lib/scss/components/_data-table.scss +3 -1
  65. package/lib/scss/components/_flipcard.scss +3 -2
  66. package/lib/scss/components/_index.scss +18 -0
  67. package/lib/scss/components/_menu-stack.scss +1 -1
  68. package/lib/scss/components/_modal.scss +97 -19
  69. package/lib/scss/components/_panel.scss +1 -1
  70. package/lib/scss/components/_popover.scss +9 -6
  71. package/lib/scss/components/_ratio-box.scss +11 -10
  72. package/lib/scss/components/_table-scroller.scss +63 -0
  73. package/lib/scss/components/_tabs.scss +20 -5
  74. package/lib/scss/components/_tagged.scss +59 -0
  75. package/lib/scss/helpers/_utilities.scss +23 -1
  76. package/package.json +28 -35
  77. package/dist/umd/style.css +0 -1
  78. package/lib/js/ui/dialog.todo +0 -3
@@ -1,46 +1,70 @@
1
- var T = Object.defineProperty;
2
- var x = (a, t, s) => t in a ? T(a, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : a[t] = s;
3
- var m = (a, t, s) => x(a, typeof t != "symbol" ? t + "" : t, s);
4
- import { ComponentInitializer as S } from "../core/component.js";
5
- import { wrapSettingString as b } from "../core/settings.js";
6
- import k from "ally.js/maintain/_maintain";
7
- import { hasRequiredProps as $ } from "@ulu/utils/object.js";
8
- import { trimWhitespace as v } from "@ulu/utils/string.js";
9
- import { debounce as I } from "@ulu/utils/performance.js";
10
- import { logError as y, log as A, logWarning as L } from "../utils/class-logger.js";
11
- import z from "swipe-listener";
12
- const f = new S({
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
- }), D = f.attributeSelector("track"), M = f.attributeSelector("track-container"), B = f.attributeSelector("control-context"), N = f.attributeSelector("slide"), q = [], h = { once: !0 }, E = (a) => `${a}ms`, P = [
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 K() {
22
- f.init({
45
+ function Y() {
46
+ C.init({
23
47
  withData: !0,
24
48
  coreEvents: ["pageModified"],
25
- setup({ element: a, data: t, initialize: s }) {
26
- V(a, t), s();
49
+ setup({ element: o, data: t, initialize: s }) {
50
+ R(o, t), s();
27
51
  }
28
52
  });
29
53
  }
30
- function V(a, t) {
54
+ function R(o, t) {
31
55
  const s = Object.assign({}, t), e = {
32
- container: a,
33
- track: a.querySelector(D),
34
- trackContainer: a.querySelector(M),
35
- controlContext: a.querySelector(B),
36
- slides: a.querySelectorAll(N)
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 && q.push(new g(e, s, !1));
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", I(() => {
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, $(P) || y(this, "Missing a required Element"), t.slides.length || y(this, "Missing slides"), this.slides = [...t.slides].map((i, n) => ({
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, o = n < 0 ? i : n;
73
- this.emit("previous", [t, o]), this.goto(o, t, "previous");
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 = {}, o = () => {
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", o, h), t.removeEventListener("transitionend", r, h), t.removeEventListener("transitioncancel", r, h), i();
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", o, h), t.addEventListener("transitionend", r, h), t.addEventListener("transitioncancel", r, h), n.start = setTimeout(r, s + 500), t.style.transitionDuration = E(s), e(), s || r();
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
- async slideTransition({ slide: t, index: s, old: e, oldIndex: i, triggerType: n }) {
130
- const o = this.slides.length, r = n === "previous", C = o - 1, u = s === 0 && i === C, d = s === C && i === 0;
131
- let c, p = this.options.transitionDuration;
132
- i && !u && !d && (p = p * Math.abs(i - s)), o < 3 ? u && !r ? c = e : d && (c = r ? t : e) : u ? c = e : d && (c = t), this.setVisibility(null, !0), c && (c.element.style.order = "-1", await this.translateTo(u ? 0 : e.element.offsetLeft, 0)), await this.translateTo(t.element.offsetLeft, p), c && (c.element.style.order = "0", await this.translateTo(t.element.offsetLeft, 0)), this.setVisibility(t, !1);
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
- async fadeTransition({ slide: t, old: s }) {
138
- this.setVisibility(null, !0), s && (await this.fadeSlide(s, !1), s.element.style.order = "0"), t.element.style.order = "-1", await this.fadeSlide(t, !0), this.setVisibility(t, !1);
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: o,
174
+ slides: a,
151
175
  elements: r
152
- } = this, C = e === "init", u = o[t], d = this.getClass("nav-button--active"), c = this.getClass("transition", !0), p = { slide: u, index: t, old: i, oldIndex: n, triggerType: e };
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
- L(this, "Could not goto slide, still performing transition");
178
+ S(this, "Could not goto slide, still performing transition");
155
179
  return;
156
180
  }
157
181
  if (this.transitioning) {
158
- L(this, "Cancel goto(), same slide index as current slide");
182
+ S(this, "Cancel goto(), same slide index as current slide");
159
183
  return;
160
184
  }
161
- const w = k.disabled({ context: this.elements.track });
162
- this.transitioning = !0, i && i.navButton.classList.remove(d), u.navButton.classList.add(d), r.container.classList.add(c), this.transition(p).then(() => {
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 = v(this.trackCss()), n = v(this.trackContainerStyles()), o = v(this.slideCss());
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", o), r.element.setAttribute("tabindex", "-1");
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 = z(e, this.options.swipeOptions), e.addEventListener("swipe", this.swipeListener);
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: ${E(e)};
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"), o = this.createControlButton("next");
229
- return s.classList.add(this.getClass("controls")), e.appendChild(n), i.appendChild(o), s.appendChild(e), s.appendChild(i), n.addEventListener("click", this.previous.bind(this)), o.addEventListener("click", this.next.bind(this)), t.appendChild(s), {
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: o
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 o = document.createElement("li");
240
- return o.appendChild(n), s.appendChild(o), o;
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
- m(l, "instances", []), m(l, "globalsInitialized", !1), m(l, "reduceMotion", !1), /**
289
+ f(l, "instances", []), f(l, "globalsInitialized", !1), f(l, "reduceMotion", !1), /**
267
290
  * Default options for slider
268
291
  */
269
- m(l, "defaults", {
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: b("iconClassPrevious"),
280
- iconClassNext: b("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 g = l;
309
+ let E = l;
287
310
  export {
288
- g as Slider,
289
- K as init,
290
- f as initializer,
291
- V as setupSlider
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
+ };
@@ -4,12 +4,12 @@ import { ComponentInitializer } from '../core/component.js';
4
4
  */
5
5
  export function init(): void;
6
6
  /**
7
- *
8
- * @param {Node} element Tablist Element
9
- * @param {Node} options Options to set as defaults (can be overridden by element dataset options)
10
- * @return {Object} Instance object
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: Node, options?: Node): Object;
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}
@@ -1 +1 @@
1
- {"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../../lib/js/ui/tabs.js"],"names":[],"mappings":"AA2BA;;GAEG;AACH,6BAoBC;AAED;;;;;GAKG;AACH,+BAJW,IAAI,YACJ,IAAI,GACH,MAAM,CA0BjB;AArED;;;GAGG;AACH,8BAA4B;AAE5B;;GAEG;AACH,+CAGG;qCAdkC,sBAAsB"}
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"}