@terminal-blueprint/core 0.1.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.
- package/dist/index.cjs +540 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +2389 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +150 -0
- package/dist/index.d.ts +150 -0
- package/dist/index.iife.js +528 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.js +503 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/js/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
TBModal: () => TBModal,
|
|
24
|
+
TBSelect: () => TBSelect,
|
|
25
|
+
TBTabs: () => TBTabs,
|
|
26
|
+
VERSION: () => VERSION,
|
|
27
|
+
dismissAllToasts: () => dismissAllToasts,
|
|
28
|
+
dismissToast: () => dismissToast,
|
|
29
|
+
initModals: () => initModals,
|
|
30
|
+
initSelects: () => initSelects,
|
|
31
|
+
initSidenavs: () => initSidenavs,
|
|
32
|
+
initTabs: () => initTabs,
|
|
33
|
+
toast: () => toast
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/js/dropdown.ts
|
|
38
|
+
var TBSelect = class {
|
|
39
|
+
constructor(el, options) {
|
|
40
|
+
this.value = "";
|
|
41
|
+
this.focusedIndex = -1;
|
|
42
|
+
this.root = el;
|
|
43
|
+
this.onChange = options?.onChange;
|
|
44
|
+
const trigger = el.querySelector(".tb-select__trigger");
|
|
45
|
+
const dropdown = el.querySelector(".tb-select__dropdown");
|
|
46
|
+
if (!trigger || !dropdown) {
|
|
47
|
+
throw new Error("TBSelect: missing .tb-select__trigger or .tb-select__dropdown");
|
|
48
|
+
}
|
|
49
|
+
this.trigger = trigger;
|
|
50
|
+
this.dropdown = dropdown;
|
|
51
|
+
this.options = Array.from(el.querySelectorAll(".tb-select__option"));
|
|
52
|
+
if (!this.trigger.hasAttribute("tabindex") && this.trigger.tagName !== "BUTTON") {
|
|
53
|
+
this.trigger.setAttribute("tabindex", "0");
|
|
54
|
+
}
|
|
55
|
+
this.trigger.setAttribute("role", "combobox");
|
|
56
|
+
this.trigger.setAttribute("aria-expanded", "false");
|
|
57
|
+
this.trigger.setAttribute("aria-haspopup", "listbox");
|
|
58
|
+
this.dropdown.setAttribute("role", "listbox");
|
|
59
|
+
this.options.forEach((opt) => {
|
|
60
|
+
opt.setAttribute("role", "option");
|
|
61
|
+
opt.setAttribute("tabindex", "-1");
|
|
62
|
+
});
|
|
63
|
+
this.handleTriggerClick = () => this.toggle();
|
|
64
|
+
this.handleDocumentClick = (e) => {
|
|
65
|
+
if (!this.root.contains(e.target)) {
|
|
66
|
+
this.closeDropdown();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
this.handleKeyDown = (e) => {
|
|
70
|
+
if (!this.isOpen() && !this.root.contains(document.activeElement)) return;
|
|
71
|
+
switch (e.key) {
|
|
72
|
+
case "Escape":
|
|
73
|
+
this.closeDropdown();
|
|
74
|
+
this.trigger.focus();
|
|
75
|
+
e.preventDefault();
|
|
76
|
+
break;
|
|
77
|
+
case "ArrowDown":
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
if (!this.isOpen()) {
|
|
80
|
+
this.openDropdown();
|
|
81
|
+
}
|
|
82
|
+
this.moveFocus(1);
|
|
83
|
+
break;
|
|
84
|
+
case "ArrowUp":
|
|
85
|
+
e.preventDefault();
|
|
86
|
+
if (!this.isOpen()) {
|
|
87
|
+
this.openDropdown();
|
|
88
|
+
}
|
|
89
|
+
this.moveFocus(-1);
|
|
90
|
+
break;
|
|
91
|
+
case "Enter":
|
|
92
|
+
case " ":
|
|
93
|
+
if (this.isOpen() && this.focusedIndex >= 0) {
|
|
94
|
+
e.preventDefault();
|
|
95
|
+
this.selectOption(this.options[this.focusedIndex]);
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
this.handleOptionClick = (e) => {
|
|
101
|
+
const opt = e.target.closest(".tb-select__option");
|
|
102
|
+
if (opt) this.selectOption(opt);
|
|
103
|
+
};
|
|
104
|
+
this.trigger.addEventListener("click", this.handleTriggerClick);
|
|
105
|
+
document.addEventListener("click", this.handleDocumentClick);
|
|
106
|
+
document.addEventListener("keydown", this.handleKeyDown);
|
|
107
|
+
this.dropdown.addEventListener("click", this.handleOptionClick);
|
|
108
|
+
el._tbSelect = this;
|
|
109
|
+
}
|
|
110
|
+
// ── Public API ──
|
|
111
|
+
getValue() {
|
|
112
|
+
return this.value;
|
|
113
|
+
}
|
|
114
|
+
setValue(value) {
|
|
115
|
+
const opt = this.options.find((o) => o.dataset.value === value);
|
|
116
|
+
if (opt) this.selectOption(opt, false);
|
|
117
|
+
}
|
|
118
|
+
destroy() {
|
|
119
|
+
this.trigger.removeEventListener("click", this.handleTriggerClick);
|
|
120
|
+
document.removeEventListener("click", this.handleDocumentClick);
|
|
121
|
+
document.removeEventListener("keydown", this.handleKeyDown);
|
|
122
|
+
this.dropdown.removeEventListener("click", this.handleOptionClick);
|
|
123
|
+
delete this.root._tbSelect;
|
|
124
|
+
}
|
|
125
|
+
// ── Internal helpers ──
|
|
126
|
+
isOpen() {
|
|
127
|
+
return this.dropdown.classList.contains("tb-select__dropdown--open");
|
|
128
|
+
}
|
|
129
|
+
openDropdown() {
|
|
130
|
+
this.dropdown.classList.add("tb-select__dropdown--open");
|
|
131
|
+
this.trigger.setAttribute("aria-expanded", "true");
|
|
132
|
+
}
|
|
133
|
+
closeDropdown() {
|
|
134
|
+
this.dropdown.classList.remove("tb-select__dropdown--open");
|
|
135
|
+
this.trigger.setAttribute("aria-expanded", "false");
|
|
136
|
+
this.focusedIndex = -1;
|
|
137
|
+
this.options.forEach((o) => o.classList.remove("tb-select__option--focused"));
|
|
138
|
+
}
|
|
139
|
+
toggle() {
|
|
140
|
+
if (this.isOpen()) {
|
|
141
|
+
this.closeDropdown();
|
|
142
|
+
} else {
|
|
143
|
+
this.openDropdown();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
moveFocus(delta) {
|
|
147
|
+
this.options.forEach((o) => o.classList.remove("tb-select__option--focused"));
|
|
148
|
+
this.focusedIndex += delta;
|
|
149
|
+
if (this.focusedIndex < 0) this.focusedIndex = this.options.length - 1;
|
|
150
|
+
if (this.focusedIndex >= this.options.length) this.focusedIndex = 0;
|
|
151
|
+
const focused = this.options[this.focusedIndex];
|
|
152
|
+
focused.classList.add("tb-select__option--focused");
|
|
153
|
+
focused.focus();
|
|
154
|
+
}
|
|
155
|
+
selectOption(opt, notify = true) {
|
|
156
|
+
this.value = opt.dataset.value ?? opt.textContent?.trim() ?? "";
|
|
157
|
+
const label = this.trigger.querySelector("span");
|
|
158
|
+
if (label) {
|
|
159
|
+
label.textContent = opt.textContent?.trim() ?? "";
|
|
160
|
+
}
|
|
161
|
+
this.options.forEach((o) => o.classList.remove("tb-select__option--selected"));
|
|
162
|
+
opt.classList.add("tb-select__option--selected");
|
|
163
|
+
opt.setAttribute("aria-selected", "true");
|
|
164
|
+
this.closeDropdown();
|
|
165
|
+
this.trigger.focus();
|
|
166
|
+
if (notify && this.onChange) {
|
|
167
|
+
this.onChange(this.value);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
function initSelects() {
|
|
172
|
+
const instances = [];
|
|
173
|
+
document.querySelectorAll("[data-tb-select]").forEach((el) => {
|
|
174
|
+
if (el._tbSelect) return;
|
|
175
|
+
instances.push(new TBSelect(el));
|
|
176
|
+
});
|
|
177
|
+
return instances;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// src/js/modal.ts
|
|
181
|
+
var FOCUSABLE = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
182
|
+
var TBModal = class {
|
|
183
|
+
constructor(el, options) {
|
|
184
|
+
this.previousFocus = null;
|
|
185
|
+
this.overlay = el;
|
|
186
|
+
this.closeOnEscape = options?.closeOnEscape ?? true;
|
|
187
|
+
this.closeOnBackdrop = options?.closeOnBackdrop ?? true;
|
|
188
|
+
this.closeBtns = Array.from(el.querySelectorAll("[data-tb-modal-close]"));
|
|
189
|
+
const modal = el.querySelector(".tb-modal");
|
|
190
|
+
if (modal) {
|
|
191
|
+
modal.setAttribute("role", "dialog");
|
|
192
|
+
modal.setAttribute("aria-modal", "true");
|
|
193
|
+
}
|
|
194
|
+
this.handleKeyDown = (e) => {
|
|
195
|
+
if (!this.isOpen()) return;
|
|
196
|
+
if (e.key === "Escape" && this.closeOnEscape) {
|
|
197
|
+
e.preventDefault();
|
|
198
|
+
this.close();
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (e.key === "Tab") {
|
|
202
|
+
this.trapFocus(e);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
this.handleOverlayClick = (e) => {
|
|
206
|
+
if (this.closeOnBackdrop && e.target === this.overlay) {
|
|
207
|
+
this.close();
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
this.handleCloseClick = () => this.close();
|
|
211
|
+
document.addEventListener("keydown", this.handleKeyDown);
|
|
212
|
+
this.overlay.addEventListener("click", this.handleOverlayClick);
|
|
213
|
+
this.closeBtns.forEach((btn) => {
|
|
214
|
+
btn.addEventListener("click", this.handleCloseClick);
|
|
215
|
+
});
|
|
216
|
+
if (this.overlay.id) {
|
|
217
|
+
document.querySelectorAll(`[data-tb-modal-open="${this.overlay.id}"]`).forEach((trigger) => {
|
|
218
|
+
trigger.addEventListener("click", () => this.open());
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
;
|
|
222
|
+
el._tbModal = this;
|
|
223
|
+
}
|
|
224
|
+
// ── Public API ──
|
|
225
|
+
open() {
|
|
226
|
+
this.previousFocus = document.activeElement;
|
|
227
|
+
this.overlay.classList.add("tb-modal-overlay--open");
|
|
228
|
+
document.body.style.overflow = "hidden";
|
|
229
|
+
const firstFocusable = this.overlay.querySelector(FOCUSABLE);
|
|
230
|
+
if (firstFocusable) {
|
|
231
|
+
firstFocusable.focus();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
close() {
|
|
235
|
+
this.overlay.classList.remove("tb-modal-overlay--open");
|
|
236
|
+
document.body.style.overflow = "";
|
|
237
|
+
if (this.previousFocus) {
|
|
238
|
+
this.previousFocus.focus();
|
|
239
|
+
this.previousFocus = null;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
destroy() {
|
|
243
|
+
document.removeEventListener("keydown", this.handleKeyDown);
|
|
244
|
+
this.overlay.removeEventListener("click", this.handleOverlayClick);
|
|
245
|
+
this.closeBtns.forEach((btn) => {
|
|
246
|
+
btn.removeEventListener("click", this.handleCloseClick);
|
|
247
|
+
});
|
|
248
|
+
if (this.isOpen()) {
|
|
249
|
+
document.body.style.overflow = "";
|
|
250
|
+
}
|
|
251
|
+
delete this.overlay._tbModal;
|
|
252
|
+
}
|
|
253
|
+
// ── Internal helpers ──
|
|
254
|
+
isOpen() {
|
|
255
|
+
return this.overlay.classList.contains("tb-modal-overlay--open");
|
|
256
|
+
}
|
|
257
|
+
trapFocus(e) {
|
|
258
|
+
const focusable = Array.from(this.overlay.querySelectorAll(FOCUSABLE));
|
|
259
|
+
if (focusable.length === 0) return;
|
|
260
|
+
const first = focusable[0];
|
|
261
|
+
const last = focusable[focusable.length - 1];
|
|
262
|
+
if (e.shiftKey) {
|
|
263
|
+
if (document.activeElement === first) {
|
|
264
|
+
e.preventDefault();
|
|
265
|
+
last.focus();
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
if (document.activeElement === last) {
|
|
269
|
+
e.preventDefault();
|
|
270
|
+
first.focus();
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
function initModals() {
|
|
276
|
+
const instances = [];
|
|
277
|
+
document.querySelectorAll("[data-tb-modal]").forEach((el) => {
|
|
278
|
+
if (el._tbModal) return;
|
|
279
|
+
instances.push(new TBModal(el));
|
|
280
|
+
});
|
|
281
|
+
return instances;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// src/js/tabs.ts
|
|
285
|
+
var TBTabs = class {
|
|
286
|
+
constructor(el) {
|
|
287
|
+
this.root = el;
|
|
288
|
+
this.triggers = Array.from(el.querySelectorAll(".tb-tabs__trigger"));
|
|
289
|
+
this.contents = Array.from(el.querySelectorAll(".tb-tabs__content"));
|
|
290
|
+
this.triggers.forEach((t, i) => {
|
|
291
|
+
t.setAttribute("role", "tab");
|
|
292
|
+
t.setAttribute("aria-selected", t.classList.contains("tb-tabs__trigger--active") ? "true" : "false");
|
|
293
|
+
if (!t.hasAttribute("tabindex")) {
|
|
294
|
+
t.setAttribute("tabindex", t.classList.contains("tb-tabs__trigger--active") ? "0" : "-1");
|
|
295
|
+
}
|
|
296
|
+
const value = t.dataset.value ?? String(i);
|
|
297
|
+
const panelId = `tb-tabpanel-${value}`;
|
|
298
|
+
t.setAttribute("aria-controls", panelId);
|
|
299
|
+
});
|
|
300
|
+
this.contents.forEach((c, i) => {
|
|
301
|
+
c.setAttribute("role", "tabpanel");
|
|
302
|
+
const value = c.dataset.value ?? String(i);
|
|
303
|
+
c.id = `tb-tabpanel-${value}`;
|
|
304
|
+
});
|
|
305
|
+
const list = el.querySelector(".tb-tabs__list");
|
|
306
|
+
if (list) {
|
|
307
|
+
list.setAttribute("role", "tablist");
|
|
308
|
+
}
|
|
309
|
+
this.handleTriggerClick = (e) => {
|
|
310
|
+
const trigger = e.target.closest(".tb-tabs__trigger");
|
|
311
|
+
if (!trigger) return;
|
|
312
|
+
const value = trigger.dataset.value;
|
|
313
|
+
if (value != null) this.setTab(value);
|
|
314
|
+
};
|
|
315
|
+
this.handleKeyDown = (e) => {
|
|
316
|
+
const target = e.target;
|
|
317
|
+
if (!target.classList.contains("tb-tabs__trigger")) return;
|
|
318
|
+
let index = this.triggers.indexOf(target);
|
|
319
|
+
if (index === -1) return;
|
|
320
|
+
let newIndex = null;
|
|
321
|
+
switch (e.key) {
|
|
322
|
+
case "ArrowRight":
|
|
323
|
+
newIndex = (index + 1) % this.triggers.length;
|
|
324
|
+
break;
|
|
325
|
+
case "ArrowLeft":
|
|
326
|
+
newIndex = (index - 1 + this.triggers.length) % this.triggers.length;
|
|
327
|
+
break;
|
|
328
|
+
case "Home":
|
|
329
|
+
newIndex = 0;
|
|
330
|
+
break;
|
|
331
|
+
case "End":
|
|
332
|
+
newIndex = this.triggers.length - 1;
|
|
333
|
+
break;
|
|
334
|
+
default:
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
e.preventDefault();
|
|
338
|
+
const next = this.triggers[newIndex];
|
|
339
|
+
next.focus();
|
|
340
|
+
const value = next.dataset.value;
|
|
341
|
+
if (value != null) this.setTab(value);
|
|
342
|
+
};
|
|
343
|
+
this.root.addEventListener("click", this.handleTriggerClick);
|
|
344
|
+
this.root.addEventListener("keydown", this.handleKeyDown);
|
|
345
|
+
el._tbTabs = this;
|
|
346
|
+
}
|
|
347
|
+
// ── Public API ──
|
|
348
|
+
setTab(value) {
|
|
349
|
+
this.triggers.forEach((t) => {
|
|
350
|
+
t.classList.remove("tb-tabs__trigger--active");
|
|
351
|
+
t.setAttribute("aria-selected", "false");
|
|
352
|
+
t.setAttribute("tabindex", "-1");
|
|
353
|
+
});
|
|
354
|
+
this.contents.forEach((c) => {
|
|
355
|
+
c.classList.remove("tb-tabs__content--active");
|
|
356
|
+
});
|
|
357
|
+
const trigger = this.triggers.find((t) => t.dataset.value === value);
|
|
358
|
+
if (trigger) {
|
|
359
|
+
trigger.classList.add("tb-tabs__trigger--active");
|
|
360
|
+
trigger.setAttribute("aria-selected", "true");
|
|
361
|
+
trigger.setAttribute("tabindex", "0");
|
|
362
|
+
}
|
|
363
|
+
const content = this.contents.find((c) => c.dataset.value === value);
|
|
364
|
+
if (content) {
|
|
365
|
+
content.classList.add("tb-tabs__content--active");
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
destroy() {
|
|
369
|
+
this.root.removeEventListener("click", this.handleTriggerClick);
|
|
370
|
+
this.root.removeEventListener("keydown", this.handleKeyDown);
|
|
371
|
+
delete this.root._tbTabs;
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
function initTabs() {
|
|
375
|
+
const instances = [];
|
|
376
|
+
document.querySelectorAll("[data-tb-tabs]").forEach((el) => {
|
|
377
|
+
if (el._tbTabs) return;
|
|
378
|
+
instances.push(new TBTabs(el));
|
|
379
|
+
});
|
|
380
|
+
return instances;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// src/js/toast.ts
|
|
384
|
+
var nextId = 0;
|
|
385
|
+
var TBToastManager = class {
|
|
386
|
+
constructor(position = "bottom-right") {
|
|
387
|
+
this.toasts = /* @__PURE__ */ new Map();
|
|
388
|
+
let existing = document.querySelector(".tb-toast-container");
|
|
389
|
+
if (existing) {
|
|
390
|
+
this.container = existing;
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
this.container = document.createElement("div");
|
|
394
|
+
this.container.className = `tb-toast-container tb-toast-container--${position}`;
|
|
395
|
+
this.container.setAttribute("aria-live", "polite");
|
|
396
|
+
this.container.setAttribute("aria-atomic", "false");
|
|
397
|
+
document.body.appendChild(this.container);
|
|
398
|
+
}
|
|
399
|
+
show(options) {
|
|
400
|
+
const id = `tb-toast-${++nextId}`;
|
|
401
|
+
const duration = options.duration === void 0 ? 3500 : options.duration;
|
|
402
|
+
const type = options.type ?? "info";
|
|
403
|
+
const el = document.createElement("div");
|
|
404
|
+
el.className = `tb-toast tb-toast--${type}`;
|
|
405
|
+
el.id = id;
|
|
406
|
+
el.setAttribute("role", "status");
|
|
407
|
+
const icon = document.createElement("span");
|
|
408
|
+
icon.className = "tb-toast__icon";
|
|
409
|
+
icon.textContent = this.iconForType(type);
|
|
410
|
+
el.appendChild(icon);
|
|
411
|
+
const body = document.createElement("div");
|
|
412
|
+
body.className = "tb-toast__body";
|
|
413
|
+
const title = document.createElement("span");
|
|
414
|
+
title.className = "tb-toast__title";
|
|
415
|
+
title.textContent = options.title;
|
|
416
|
+
body.appendChild(title);
|
|
417
|
+
if (options.description) {
|
|
418
|
+
const desc = document.createElement("span");
|
|
419
|
+
desc.className = "tb-toast__description";
|
|
420
|
+
desc.textContent = options.description;
|
|
421
|
+
body.appendChild(desc);
|
|
422
|
+
}
|
|
423
|
+
el.appendChild(body);
|
|
424
|
+
if (options.action) {
|
|
425
|
+
const btn = document.createElement("button");
|
|
426
|
+
btn.className = "tb-toast__action";
|
|
427
|
+
btn.textContent = options.action.label;
|
|
428
|
+
const actionCb = options.action.onClick;
|
|
429
|
+
btn.addEventListener("click", () => {
|
|
430
|
+
actionCb();
|
|
431
|
+
this.dismiss(id);
|
|
432
|
+
});
|
|
433
|
+
el.appendChild(btn);
|
|
434
|
+
}
|
|
435
|
+
const close = document.createElement("button");
|
|
436
|
+
close.className = "tb-toast__close";
|
|
437
|
+
close.setAttribute("aria-label", "Dismiss");
|
|
438
|
+
close.textContent = "\xD7";
|
|
439
|
+
close.addEventListener("click", () => this.dismiss(id));
|
|
440
|
+
el.appendChild(close);
|
|
441
|
+
this.container.appendChild(el);
|
|
442
|
+
requestAnimationFrame(() => {
|
|
443
|
+
el.classList.add("tb-toast--visible");
|
|
444
|
+
});
|
|
445
|
+
let timer;
|
|
446
|
+
if (duration !== null) {
|
|
447
|
+
timer = setTimeout(() => this.dismiss(id), duration);
|
|
448
|
+
}
|
|
449
|
+
this.toasts.set(id, { el, timer });
|
|
450
|
+
return id;
|
|
451
|
+
}
|
|
452
|
+
dismiss(id) {
|
|
453
|
+
const entry = this.toasts.get(id);
|
|
454
|
+
if (!entry) return;
|
|
455
|
+
if (entry.timer) clearTimeout(entry.timer);
|
|
456
|
+
entry.el.classList.remove("tb-toast--visible");
|
|
457
|
+
entry.el.classList.add("tb-toast--exit");
|
|
458
|
+
const onEnd = () => {
|
|
459
|
+
entry.el.removeEventListener("transitionend", onEnd);
|
|
460
|
+
entry.el.remove();
|
|
461
|
+
this.toasts.delete(id);
|
|
462
|
+
};
|
|
463
|
+
entry.el.addEventListener("transitionend", onEnd);
|
|
464
|
+
setTimeout(onEnd, 400);
|
|
465
|
+
}
|
|
466
|
+
dismissAll() {
|
|
467
|
+
for (const id of Array.from(this.toasts.keys())) {
|
|
468
|
+
this.dismiss(id);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
iconForType(type) {
|
|
472
|
+
switch (type) {
|
|
473
|
+
case "success":
|
|
474
|
+
return "\u2713";
|
|
475
|
+
case "error":
|
|
476
|
+
return "\u2717";
|
|
477
|
+
case "warning":
|
|
478
|
+
return "\u26A0";
|
|
479
|
+
case "info":
|
|
480
|
+
default:
|
|
481
|
+
return "\u2139";
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
var manager = null;
|
|
486
|
+
function toast(options) {
|
|
487
|
+
if (!manager) manager = new TBToastManager();
|
|
488
|
+
return manager.show(options);
|
|
489
|
+
}
|
|
490
|
+
function dismissToast(id) {
|
|
491
|
+
manager?.dismiss(id);
|
|
492
|
+
}
|
|
493
|
+
function dismissAllToasts() {
|
|
494
|
+
manager?.dismissAll();
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// src/js/sidenav.ts
|
|
498
|
+
function initSidenavs() {
|
|
499
|
+
document.querySelectorAll(".tb-sidenav__group-trigger").forEach((trigger) => {
|
|
500
|
+
if (trigger._tbSidenav) return;
|
|
501
|
+
trigger._tbSidenav = true;
|
|
502
|
+
trigger.addEventListener("click", () => {
|
|
503
|
+
const group = trigger.closest(".tb-sidenav__group");
|
|
504
|
+
if (group) {
|
|
505
|
+
group.classList.toggle("tb-sidenav__group--open");
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// src/js/index.ts
|
|
512
|
+
var VERSION = "0.1.0";
|
|
513
|
+
function autoInit() {
|
|
514
|
+
initSelects();
|
|
515
|
+
initModals();
|
|
516
|
+
initTabs();
|
|
517
|
+
initSidenavs();
|
|
518
|
+
}
|
|
519
|
+
if (typeof document !== "undefined") {
|
|
520
|
+
if (document.readyState === "loading") {
|
|
521
|
+
document.addEventListener("DOMContentLoaded", autoInit);
|
|
522
|
+
} else {
|
|
523
|
+
autoInit();
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
527
|
+
0 && (module.exports = {
|
|
528
|
+
TBModal,
|
|
529
|
+
TBSelect,
|
|
530
|
+
TBTabs,
|
|
531
|
+
VERSION,
|
|
532
|
+
dismissAllToasts,
|
|
533
|
+
dismissToast,
|
|
534
|
+
initModals,
|
|
535
|
+
initSelects,
|
|
536
|
+
initSidenavs,
|
|
537
|
+
initTabs,
|
|
538
|
+
toast
|
|
539
|
+
});
|
|
540
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/js/index.ts","../src/js/dropdown.ts","../src/js/modal.ts","../src/js/tabs.ts","../src/js/toast.ts","../src/js/sidenav.ts"],"sourcesContent":["import '../css/index.css'\nexport const VERSION = '0.1.0'\n\nimport { TBSelect, initSelects } from './dropdown'\nimport { TBModal, initModals } from './modal'\nimport { TBTabs, initTabs } from './tabs'\nimport { toast, dismissToast, dismissAllToasts } from './toast'\nimport { initSidenavs } from './sidenav'\n\nexport { TBSelect, initSelects }\nexport type { TBSelectOptions } from './dropdown'\nexport { TBModal, initModals }\nexport type { TBModalOptions } from './modal'\nexport { TBTabs, initTabs }\nexport { toast, dismissToast, dismissAllToasts }\nexport type { TBToastOptions } from './toast'\nexport { initSidenavs }\n\n// ── Auto-initialize ──\n// When loaded via <script> or import, automatically discover and init components.\n// Safe to call multiple times — already-initialized elements are skipped.\n\nfunction autoInit(): void {\n initSelects()\n initModals()\n initTabs()\n initSidenavs()\n}\n\nif (typeof document !== 'undefined') {\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', autoInit)\n } else {\n autoInit()\n }\n}\n","/**\n * TBSelect — Custom select dropdown component\n *\n * Expected markup:\n * <div data-tb-select>\n * <button class=\"tb-select__trigger\"><span>Placeholder</span></button>\n * <div class=\"tb-select__dropdown\">\n * <div class=\"tb-select__option\" data-value=\"a\">Option A</div>\n * <div class=\"tb-select__option\" data-value=\"b\">Option B</div>\n * </div>\n * </div>\n */\n\nexport interface TBSelectOptions {\n onChange?: (value: string) => void\n}\n\nexport class TBSelect {\n private root: HTMLElement\n private trigger: HTMLElement\n private dropdown: HTMLElement\n private options: HTMLElement[]\n private value: string = ''\n private onChange?: (value: string) => void\n private focusedIndex: number = -1\n\n // Bound listeners kept for cleanup\n private handleTriggerClick: () => void\n private handleDocumentClick: (e: MouseEvent) => void\n private handleKeyDown: (e: KeyboardEvent) => void\n private handleOptionClick: (e: Event) => void\n\n constructor(el: HTMLElement, options?: TBSelectOptions) {\n this.root = el\n this.onChange = options?.onChange\n\n const trigger = el.querySelector<HTMLElement>('.tb-select__trigger')\n const dropdown = el.querySelector<HTMLElement>('.tb-select__dropdown')\n if (!trigger || !dropdown) {\n throw new Error('TBSelect: missing .tb-select__trigger or .tb-select__dropdown')\n }\n\n this.trigger = trigger\n this.dropdown = dropdown\n this.options = Array.from(el.querySelectorAll<HTMLElement>('.tb-select__option'))\n\n // Ensure trigger is focusable\n if (!this.trigger.hasAttribute('tabindex') && this.trigger.tagName !== 'BUTTON') {\n this.trigger.setAttribute('tabindex', '0')\n }\n\n // ARIA\n this.trigger.setAttribute('role', 'combobox')\n this.trigger.setAttribute('aria-expanded', 'false')\n this.trigger.setAttribute('aria-haspopup', 'listbox')\n this.dropdown.setAttribute('role', 'listbox')\n this.options.forEach((opt) => {\n opt.setAttribute('role', 'option')\n opt.setAttribute('tabindex', '-1')\n })\n\n // ── Bind listeners ──\n\n this.handleTriggerClick = () => this.toggle()\n\n this.handleDocumentClick = (e: MouseEvent) => {\n if (!this.root.contains(e.target as Node)) {\n this.closeDropdown()\n }\n }\n\n this.handleKeyDown = (e: KeyboardEvent) => {\n if (!this.isOpen() && !this.root.contains(document.activeElement)) return\n\n switch (e.key) {\n case 'Escape':\n this.closeDropdown()\n this.trigger.focus()\n e.preventDefault()\n break\n case 'ArrowDown':\n e.preventDefault()\n if (!this.isOpen()) {\n this.openDropdown()\n }\n this.moveFocus(1)\n break\n case 'ArrowUp':\n e.preventDefault()\n if (!this.isOpen()) {\n this.openDropdown()\n }\n this.moveFocus(-1)\n break\n case 'Enter':\n case ' ':\n if (this.isOpen() && this.focusedIndex >= 0) {\n e.preventDefault()\n this.selectOption(this.options[this.focusedIndex])\n }\n break\n }\n }\n\n this.handleOptionClick = (e: Event) => {\n const opt = (e.target as HTMLElement).closest<HTMLElement>('.tb-select__option')\n if (opt) this.selectOption(opt)\n }\n\n this.trigger.addEventListener('click', this.handleTriggerClick)\n document.addEventListener('click', this.handleDocumentClick)\n document.addEventListener('keydown', this.handleKeyDown)\n this.dropdown.addEventListener('click', this.handleOptionClick)\n\n // Store instance on element for programmatic access\n ;(el as any)._tbSelect = this\n }\n\n // ── Public API ──\n\n getValue(): string {\n return this.value\n }\n\n setValue(value: string): void {\n const opt = this.options.find((o) => o.dataset.value === value)\n if (opt) this.selectOption(opt, false)\n }\n\n destroy(): void {\n this.trigger.removeEventListener('click', this.handleTriggerClick)\n document.removeEventListener('click', this.handleDocumentClick)\n document.removeEventListener('keydown', this.handleKeyDown)\n this.dropdown.removeEventListener('click', this.handleOptionClick)\n delete (this.root as any)._tbSelect\n }\n\n // ── Internal helpers ──\n\n private isOpen(): boolean {\n return this.dropdown.classList.contains('tb-select__dropdown--open')\n }\n\n private openDropdown(): void {\n this.dropdown.classList.add('tb-select__dropdown--open')\n this.trigger.setAttribute('aria-expanded', 'true')\n }\n\n private closeDropdown(): void {\n this.dropdown.classList.remove('tb-select__dropdown--open')\n this.trigger.setAttribute('aria-expanded', 'false')\n this.focusedIndex = -1\n this.options.forEach((o) => o.classList.remove('tb-select__option--focused'))\n }\n\n private toggle(): void {\n if (this.isOpen()) {\n this.closeDropdown()\n } else {\n this.openDropdown()\n }\n }\n\n private moveFocus(delta: number): void {\n this.options.forEach((o) => o.classList.remove('tb-select__option--focused'))\n this.focusedIndex += delta\n if (this.focusedIndex < 0) this.focusedIndex = this.options.length - 1\n if (this.focusedIndex >= this.options.length) this.focusedIndex = 0\n const focused = this.options[this.focusedIndex]\n focused.classList.add('tb-select__option--focused')\n focused.focus()\n }\n\n private selectOption(opt: HTMLElement, notify = true): void {\n this.value = opt.dataset.value ?? opt.textContent?.trim() ?? ''\n\n // Update trigger label\n const label = this.trigger.querySelector('span')\n if (label) {\n label.textContent = opt.textContent?.trim() ?? ''\n }\n\n // Mark selected\n this.options.forEach((o) => o.classList.remove('tb-select__option--selected'))\n opt.classList.add('tb-select__option--selected')\n opt.setAttribute('aria-selected', 'true')\n\n this.closeDropdown()\n this.trigger.focus()\n\n if (notify && this.onChange) {\n this.onChange(this.value)\n }\n }\n}\n\n/** Auto-initialise all elements with `[data-tb-select]`. */\nexport function initSelects(): TBSelect[] {\n const instances: TBSelect[] = []\n document.querySelectorAll<HTMLElement>('[data-tb-select]').forEach((el) => {\n if ((el as any)._tbSelect) return // already initialized\n instances.push(new TBSelect(el))\n })\n return instances\n}\n","/**\n * TBModal — Modal dialog component\n *\n * Expected markup:\n * <div class=\"tb-modal-overlay\" data-tb-modal>\n * <div class=\"tb-modal\">\n * <button class=\"tb-modal__close\" data-tb-modal-close></button>\n * <!-- content -->\n * </div>\n * </div>\n *\n * Triggers (anywhere in the page):\n * <button data-tb-modal-open=\"modal-id\">Open</button>\n */\n\nexport interface TBModalOptions {\n closeOnEscape?: boolean\n closeOnBackdrop?: boolean\n}\n\nconst FOCUSABLE =\n 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex=\"-1\"])'\n\nexport class TBModal {\n private overlay: HTMLElement\n private closeBtns: HTMLElement[]\n private previousFocus: HTMLElement | null = null\n private closeOnEscape: boolean\n private closeOnBackdrop: boolean\n\n // Bound listeners for cleanup\n private handleKeyDown: (e: KeyboardEvent) => void\n private handleOverlayClick: (e: MouseEvent) => void\n private handleCloseClick: () => void\n\n constructor(el: HTMLElement, options?: TBModalOptions) {\n this.overlay = el\n this.closeOnEscape = options?.closeOnEscape ?? true\n this.closeOnBackdrop = options?.closeOnBackdrop ?? true\n this.closeBtns = Array.from(el.querySelectorAll('[data-tb-modal-close]'))\n\n // ARIA\n const modal = el.querySelector<HTMLElement>('.tb-modal')\n if (modal) {\n modal.setAttribute('role', 'dialog')\n modal.setAttribute('aria-modal', 'true')\n }\n\n // ── Listeners ──\n\n this.handleKeyDown = (e: KeyboardEvent) => {\n if (!this.isOpen()) return\n\n if (e.key === 'Escape' && this.closeOnEscape) {\n e.preventDefault()\n this.close()\n return\n }\n\n // Focus trap\n if (e.key === 'Tab') {\n this.trapFocus(e)\n }\n }\n\n this.handleOverlayClick = (e: MouseEvent) => {\n if (this.closeOnBackdrop && e.target === this.overlay) {\n this.close()\n }\n }\n\n this.handleCloseClick = () => this.close()\n\n document.addEventListener('keydown', this.handleKeyDown)\n this.overlay.addEventListener('click', this.handleOverlayClick)\n this.closeBtns.forEach((btn) => {\n btn.addEventListener('click', this.handleCloseClick)\n })\n\n // Wire up external open triggers that reference this modal by id\n if (this.overlay.id) {\n document.querySelectorAll<HTMLElement>(`[data-tb-modal-open=\"${this.overlay.id}\"]`).forEach((trigger) => {\n trigger.addEventListener('click', () => this.open())\n })\n }\n\n // Store instance on element for programmatic access\n ;(el as any)._tbModal = this\n }\n\n // ── Public API ──\n\n open(): void {\n this.previousFocus = document.activeElement as HTMLElement | null\n this.overlay.classList.add('tb-modal-overlay--open')\n document.body.style.overflow = 'hidden'\n\n // Move focus into modal\n const firstFocusable = this.overlay.querySelector<HTMLElement>(FOCUSABLE)\n if (firstFocusable) {\n firstFocusable.focus()\n }\n }\n\n close(): void {\n this.overlay.classList.remove('tb-modal-overlay--open')\n document.body.style.overflow = ''\n\n if (this.previousFocus) {\n this.previousFocus.focus()\n this.previousFocus = null\n }\n }\n\n destroy(): void {\n document.removeEventListener('keydown', this.handleKeyDown)\n this.overlay.removeEventListener('click', this.handleOverlayClick)\n this.closeBtns.forEach((btn) => {\n btn.removeEventListener('click', this.handleCloseClick)\n })\n // Ensure body scroll is restored\n if (this.isOpen()) {\n document.body.style.overflow = ''\n }\n delete (this.overlay as any)._tbModal\n }\n\n // ── Internal helpers ──\n\n private isOpen(): boolean {\n return this.overlay.classList.contains('tb-modal-overlay--open')\n }\n\n private trapFocus(e: KeyboardEvent): void {\n const focusable = Array.from(this.overlay.querySelectorAll<HTMLElement>(FOCUSABLE))\n if (focusable.length === 0) return\n\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n\n if (e.shiftKey) {\n if (document.activeElement === first) {\n e.preventDefault()\n last.focus()\n }\n } else {\n if (document.activeElement === last) {\n e.preventDefault()\n first.focus()\n }\n }\n }\n}\n\n/** Auto-initialise all elements with `[data-tb-modal]`. */\nexport function initModals(): TBModal[] {\n const instances: TBModal[] = []\n document.querySelectorAll<HTMLElement>('[data-tb-modal]').forEach((el) => {\n if ((el as any)._tbModal) return\n instances.push(new TBModal(el))\n })\n return instances\n}\n","/**\n * TBTabs — Tab switching component\n *\n * Expected markup:\n * <div class=\"tb-tabs\" data-tb-tabs>\n * <div class=\"tb-tabs__list\">\n * <button class=\"tb-tabs__trigger tb-tabs__trigger--active\" data-value=\"one\">One</button>\n * <button class=\"tb-tabs__trigger\" data-value=\"two\">Two</button>\n * </div>\n * <div class=\"tb-tabs__content tb-tabs__content--active\" data-value=\"one\">...</div>\n * <div class=\"tb-tabs__content\" data-value=\"two\">...</div>\n * </div>\n */\n\nexport class TBTabs {\n private root: HTMLElement\n private triggers: HTMLElement[]\n private contents: HTMLElement[]\n\n // Bound listeners for cleanup\n private handleTriggerClick: (e: Event) => void\n private handleKeyDown: (e: KeyboardEvent) => void\n\n constructor(el: HTMLElement) {\n this.root = el\n this.triggers = Array.from(el.querySelectorAll<HTMLElement>('.tb-tabs__trigger'))\n this.contents = Array.from(el.querySelectorAll<HTMLElement>('.tb-tabs__content'))\n\n // ARIA\n this.triggers.forEach((t, i) => {\n t.setAttribute('role', 'tab')\n t.setAttribute('aria-selected', t.classList.contains('tb-tabs__trigger--active') ? 'true' : 'false')\n if (!t.hasAttribute('tabindex')) {\n t.setAttribute('tabindex', t.classList.contains('tb-tabs__trigger--active') ? '0' : '-1')\n }\n // Generate an id if needed for aria-controls\n const value = t.dataset.value ?? String(i)\n const panelId = `tb-tabpanel-${value}`\n t.setAttribute('aria-controls', panelId)\n })\n\n this.contents.forEach((c, i) => {\n c.setAttribute('role', 'tabpanel')\n const value = c.dataset.value ?? String(i)\n c.id = `tb-tabpanel-${value}`\n })\n\n const list = el.querySelector('.tb-tabs__list')\n if (list) {\n list.setAttribute('role', 'tablist')\n }\n\n // ── Listeners ──\n\n this.handleTriggerClick = (e: Event) => {\n const trigger = (e.target as HTMLElement).closest<HTMLElement>('.tb-tabs__trigger')\n if (!trigger) return\n const value = trigger.dataset.value\n if (value != null) this.setTab(value)\n }\n\n this.handleKeyDown = (e: KeyboardEvent) => {\n const target = e.target as HTMLElement\n if (!target.classList.contains('tb-tabs__trigger')) return\n\n let index = this.triggers.indexOf(target)\n if (index === -1) return\n\n let newIndex: number | null = null\n\n switch (e.key) {\n case 'ArrowRight':\n newIndex = (index + 1) % this.triggers.length\n break\n case 'ArrowLeft':\n newIndex = (index - 1 + this.triggers.length) % this.triggers.length\n break\n case 'Home':\n newIndex = 0\n break\n case 'End':\n newIndex = this.triggers.length - 1\n break\n default:\n return\n }\n\n e.preventDefault()\n const next = this.triggers[newIndex]\n next.focus()\n const value = next.dataset.value\n if (value != null) this.setTab(value)\n }\n\n this.root.addEventListener('click', this.handleTriggerClick)\n this.root.addEventListener('keydown', this.handleKeyDown)\n\n // Store instance on element for programmatic access\n ;(el as any)._tbTabs = this\n }\n\n // ── Public API ──\n\n setTab(value: string): void {\n // Deactivate all\n this.triggers.forEach((t) => {\n t.classList.remove('tb-tabs__trigger--active')\n t.setAttribute('aria-selected', 'false')\n t.setAttribute('tabindex', '-1')\n })\n this.contents.forEach((c) => {\n c.classList.remove('tb-tabs__content--active')\n })\n\n // Activate matching trigger\n const trigger = this.triggers.find((t) => t.dataset.value === value)\n if (trigger) {\n trigger.classList.add('tb-tabs__trigger--active')\n trigger.setAttribute('aria-selected', 'true')\n trigger.setAttribute('tabindex', '0')\n }\n\n // Activate matching content\n const content = this.contents.find((c) => c.dataset.value === value)\n if (content) {\n content.classList.add('tb-tabs__content--active')\n }\n }\n\n destroy(): void {\n this.root.removeEventListener('click', this.handleTriggerClick)\n this.root.removeEventListener('keydown', this.handleKeyDown)\n delete (this.root as any)._tbTabs\n }\n}\n\n/** Auto-initialise all elements with `[data-tb-tabs]`. */\nexport function initTabs(): TBTabs[] {\n const instances: TBTabs[] = []\n document.querySelectorAll<HTMLElement>('[data-tb-tabs]').forEach((el) => {\n if ((el as any)._tbTabs) return\n instances.push(new TBTabs(el))\n })\n return instances\n}\n","/**\n * TBToast — Toast notification system\n *\n * Usage:\n * import { toast, dismissToast, dismissAllToasts } from '@terminal-blueprint/core'\n *\n * toast({ title: 'Saved', type: 'success' })\n * toast({ title: 'Oops', description: 'Something broke', type: 'error', duration: 5000 })\n * toast({ title: 'Confirm?', duration: null, action: { label: 'Undo', onClick: () => {} } })\n */\n\nexport interface TBToastOptions {\n title: string\n description?: string\n type?: 'success' | 'error' | 'warning' | 'info'\n duration?: number | null // ms, default 3500. null = persistent\n action?: { label: string; onClick: () => void }\n}\n\ntype ToastPosition = 'top-right' | 'top-center' | 'bottom-right' | 'bottom-center'\n\nlet nextId = 0\n\nclass TBToastManager {\n private container: HTMLElement\n private toasts: Map<string, { el: HTMLElement; timer?: ReturnType<typeof setTimeout> }> = new Map()\n\n constructor(position: ToastPosition = 'bottom-right') {\n // Reuse existing container if present\n let existing = document.querySelector<HTMLElement>('.tb-toast-container')\n if (existing) {\n this.container = existing\n return\n }\n\n this.container = document.createElement('div')\n this.container.className = `tb-toast-container tb-toast-container--${position}`\n this.container.setAttribute('aria-live', 'polite')\n this.container.setAttribute('aria-atomic', 'false')\n document.body.appendChild(this.container)\n }\n\n show(options: TBToastOptions): string {\n const id = `tb-toast-${++nextId}`\n const duration = options.duration === undefined ? 3500 : options.duration\n const type = options.type ?? 'info'\n\n // Build toast element\n const el = document.createElement('div')\n el.className = `tb-toast tb-toast--${type}`\n el.id = id\n el.setAttribute('role', 'status')\n\n // Icon\n const icon = document.createElement('span')\n icon.className = 'tb-toast__icon'\n icon.textContent = this.iconForType(type)\n el.appendChild(icon)\n\n // Body\n const body = document.createElement('div')\n body.className = 'tb-toast__body'\n\n const title = document.createElement('span')\n title.className = 'tb-toast__title'\n title.textContent = options.title\n body.appendChild(title)\n\n if (options.description) {\n const desc = document.createElement('span')\n desc.className = 'tb-toast__description'\n desc.textContent = options.description\n body.appendChild(desc)\n }\n\n el.appendChild(body)\n\n // Action button\n if (options.action) {\n const btn = document.createElement('button')\n btn.className = 'tb-toast__action'\n btn.textContent = options.action.label\n const actionCb = options.action.onClick\n btn.addEventListener('click', () => {\n actionCb()\n this.dismiss(id)\n })\n el.appendChild(btn)\n }\n\n // Close button\n const close = document.createElement('button')\n close.className = 'tb-toast__close'\n close.setAttribute('aria-label', 'Dismiss')\n close.textContent = '\\u00d7'\n close.addEventListener('click', () => this.dismiss(id))\n el.appendChild(close)\n\n this.container.appendChild(el)\n\n // Trigger entrance animation on next frame\n requestAnimationFrame(() => {\n el.classList.add('tb-toast--visible')\n })\n\n // Auto-dismiss\n let timer: ReturnType<typeof setTimeout> | undefined\n if (duration !== null) {\n timer = setTimeout(() => this.dismiss(id), duration)\n }\n\n this.toasts.set(id, { el, timer })\n return id\n }\n\n dismiss(id: string): void {\n const entry = this.toasts.get(id)\n if (!entry) return\n\n if (entry.timer) clearTimeout(entry.timer)\n entry.el.classList.remove('tb-toast--visible')\n entry.el.classList.add('tb-toast--exit')\n\n // Remove from DOM after exit animation\n const onEnd = () => {\n entry.el.removeEventListener('transitionend', onEnd)\n entry.el.remove()\n this.toasts.delete(id)\n }\n entry.el.addEventListener('transitionend', onEnd)\n\n // Fallback if no transition fires\n setTimeout(onEnd, 400)\n }\n\n dismissAll(): void {\n for (const id of Array.from(this.toasts.keys())) {\n this.dismiss(id)\n }\n }\n\n private iconForType(type: string): string {\n switch (type) {\n case 'success':\n return '\\u2713'\n case 'error':\n return '\\u2717'\n case 'warning':\n return '\\u26A0'\n case 'info':\n default:\n return '\\u2139'\n }\n }\n}\n\n// ── Singleton API ──\n\nlet manager: TBToastManager | null = null\n\nexport function toast(options: TBToastOptions): string {\n if (!manager) manager = new TBToastManager()\n return manager.show(options)\n}\n\nexport function dismissToast(id: string): void {\n manager?.dismiss(id)\n}\n\nexport function dismissAllToasts(): void {\n manager?.dismissAll()\n}\n","/**\n * TBSidenav — Collapsible sidenav groups\n *\n * Expected markup:\n * <nav class=\"tb-sidenav\">\n * <div class=\"tb-sidenav__group\">\n * <button class=\"tb-sidenav__group-trigger\">\n * Label\n * <span class=\"tb-sidenav__group-chevron material-symbols-outlined\">expand_more</span>\n * </button>\n * <div class=\"tb-sidenav__group-content\">\n * <a class=\"tb-sidenav__item\">Sub-item</a>\n * </div>\n * </div>\n * </nav>\n */\n\n/** Wire up all `.tb-sidenav__group-trigger` buttons to toggle their parent group. */\nexport function initSidenavs(): void {\n document.querySelectorAll<HTMLElement>('.tb-sidenav__group-trigger').forEach((trigger) => {\n if ((trigger as any)._tbSidenav) return\n ;(trigger as any)._tbSidenav = true\n trigger.addEventListener('click', () => {\n const group = trigger.closest('.tb-sidenav__group')\n if (group) {\n group.classList.toggle('tb-sidenav__group--open')\n }\n })\n })\n}\n"],"mappings":"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","names":[]}
|