@letsprogram/ng-oat 0.1.2 → 0.1.4
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.md +5 -7
- package/assets/oat/css/utilities.css +4 -0
- package/assets/oat/oat.min.css +16 -0
- package/fesm2022/letsprogram-ng-oat.mjs +728 -81
- package/package.json +1 -1
- package/types/letsprogram-ng-oat.d.ts +207 -34
- package/assets/oat/js/base.js +0 -107
- package/assets/oat/js/dropdown.js +0 -74
- package/assets/oat/js/index.js +0 -12
- package/assets/oat/js/sidebar.js +0 -22
- package/assets/oat/js/tabs.js +0 -94
- package/assets/oat/js/toast.js +0 -144
- package/assets/oat/js/tooltip.js +0 -36
- package/assets/oat/oat.js +0 -342
- package/assets/oat/oat.min.js +0 -267
package/assets/oat/js/tabs.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* oat - Tabs Component
|
|
3
|
-
* Provides keyboard navigation and ARIA state management.
|
|
4
|
-
*
|
|
5
|
-
* Usage:
|
|
6
|
-
* <ot-tabs>
|
|
7
|
-
* <div role="tablist">
|
|
8
|
-
* <button role="tab">Tab 1</button>
|
|
9
|
-
* <button role="tab">Tab 2</button>
|
|
10
|
-
* </div>
|
|
11
|
-
* <div role="tabpanel">Content 1</div>
|
|
12
|
-
* <div role="tabpanel">Content 2</div>
|
|
13
|
-
* </ot-tabs>
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { OtBase } from './base.js';
|
|
17
|
-
|
|
18
|
-
class OtTabs extends OtBase {
|
|
19
|
-
#tabs = [];
|
|
20
|
-
#panels = [];
|
|
21
|
-
|
|
22
|
-
init() {
|
|
23
|
-
const tablist = this.$(':scope > [role="tablist"]');
|
|
24
|
-
this.#tabs = tablist ? [...tablist.querySelectorAll('[role="tab"]')] : [];
|
|
25
|
-
this.#panels = this.$$(':scope > [role="tabpanel"]');
|
|
26
|
-
|
|
27
|
-
if (this.#tabs.length === 0 || this.#panels.length === 0) {
|
|
28
|
-
console.warn('ot-tabs: Missing tab or tabpanel elements');
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Generate IDs and set up ARIA.
|
|
33
|
-
this.#tabs.forEach((tab, i) => {
|
|
34
|
-
const panel = this.#panels[i];
|
|
35
|
-
if (!panel) return;
|
|
36
|
-
|
|
37
|
-
const tabId = tab.id || `ot-tab-${this.uid()}`;
|
|
38
|
-
const panelId = panel.id || `ot-panel-${this.uid()}`;
|
|
39
|
-
|
|
40
|
-
tab.id = tabId;
|
|
41
|
-
panel.id = panelId;
|
|
42
|
-
tab.setAttribute('aria-controls', panelId);
|
|
43
|
-
panel.setAttribute('aria-labelledby', tabId);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
tablist.addEventListener('click', this);
|
|
47
|
-
tablist.addEventListener('keydown', this);
|
|
48
|
-
|
|
49
|
-
// Find initially active tab or default to first.
|
|
50
|
-
const activeTab = this.#tabs.findIndex(t => t.ariaSelected === 'true');
|
|
51
|
-
this.#activate(activeTab >= 0 ? activeTab : 0);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
onclick(e) {
|
|
55
|
-
const index = this.#tabs.indexOf(e.target.closest('[role="tab"]'));
|
|
56
|
-
if (index >= 0) this.#activate(index);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
onkeydown(e) {
|
|
60
|
-
if (!e.target.closest('[role="tab"]')) return;
|
|
61
|
-
|
|
62
|
-
const next = this.keyNav(e, this.activeIndex, this.#tabs.length, 'ArrowLeft', 'ArrowRight');
|
|
63
|
-
if (next >= 0) {
|
|
64
|
-
this.#activate(next);
|
|
65
|
-
this.#tabs[next].focus();
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
#activate(idx) {
|
|
70
|
-
this.#tabs.forEach((tab, i) => {
|
|
71
|
-
const isActive = i === idx;
|
|
72
|
-
tab.ariaSelected = String(isActive);
|
|
73
|
-
tab.tabIndex = isActive ? 0 : -1;
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
this.#panels.forEach((panel, i) => {
|
|
77
|
-
panel.hidden = i !== idx;
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
this.emit('ot-tab-change', { index: idx, tab: this.#tabs[idx] });
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
get activeIndex() {
|
|
84
|
-
return this.#tabs.findIndex(t => t.ariaSelected === 'true');
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
set activeIndex(value) {
|
|
88
|
-
if (value >= 0 && value < this.#tabs.length) {
|
|
89
|
-
this.#activate(value);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
customElements.define('ot-tabs', OtTabs);
|
package/assets/oat/js/toast.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* oat - Toast Notifications
|
|
3
|
-
*
|
|
4
|
-
* Usage:
|
|
5
|
-
* ot.toast('Saved!')
|
|
6
|
-
* ot.toast('Action completed successfully', 'All good')
|
|
7
|
-
* ot.toast('Operation completed.', 'Success', { variant: 'success' })
|
|
8
|
-
* ot.toast('Something went wrong.', 'Error', { variant: 'danger', placement: 'bottom-center' })
|
|
9
|
-
*
|
|
10
|
-
* // Custom markup
|
|
11
|
-
* ot.toast.el(element)
|
|
12
|
-
* ot.toast.el(element, { duration: 4000, placement: 'bottom-center' })
|
|
13
|
-
* ot.toast.el(document.querySelector('#my-template'))
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
const toasts = {};
|
|
17
|
-
|
|
18
|
-
function _get(placement) {
|
|
19
|
-
if (!toasts[placement]) {
|
|
20
|
-
const el = document.createElement('div');
|
|
21
|
-
el.className = 'toast-container';
|
|
22
|
-
el.setAttribute('popover', 'manual');
|
|
23
|
-
el.setAttribute('data-placement', placement);
|
|
24
|
-
document.body.appendChild(el);
|
|
25
|
-
toasts[placement] = el;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return toasts[placement];
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function _show(el, options = {}) {
|
|
32
|
-
const { placement = 'top-right', duration = 4000 } = options;
|
|
33
|
-
const p = _get(placement);
|
|
34
|
-
|
|
35
|
-
el.classList.add('toast');
|
|
36
|
-
|
|
37
|
-
let timeout;
|
|
38
|
-
|
|
39
|
-
// Pause on hover.
|
|
40
|
-
el.onmouseenter = () => clearTimeout(timeout);
|
|
41
|
-
el.onmouseleave = () => {
|
|
42
|
-
if (duration > 0) {
|
|
43
|
-
timeout = setTimeout(() => _remove(el, p), duration);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
// Show with animation.
|
|
48
|
-
el.setAttribute('data-entering', '');
|
|
49
|
-
p.appendChild(el);
|
|
50
|
-
p.showPopover();
|
|
51
|
-
|
|
52
|
-
// Double RAF to compute styles before transition starts.
|
|
53
|
-
requestAnimationFrame(() => {
|
|
54
|
-
requestAnimationFrame(() => {
|
|
55
|
-
el.removeAttribute('data-entering');
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
if (duration > 0) {
|
|
60
|
-
timeout = setTimeout(() => _remove(el, p), duration);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return el;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function _remove(el, container) {
|
|
67
|
-
// Ignore if already in the process of exiting.
|
|
68
|
-
if (el.hasAttribute('data-exiting')) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
el.setAttribute('data-exiting', '');
|
|
72
|
-
|
|
73
|
-
const cleanup = () => {
|
|
74
|
-
el.remove();
|
|
75
|
-
if (!container.children.length) {
|
|
76
|
-
container.hidePopover();
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
el.addEventListener('transitionend', cleanup, { once: true });
|
|
81
|
-
|
|
82
|
-
// Couldn't confirm what unit this actually returns across browsers, so
|
|
83
|
-
// assume that it could be ms or s. Also, setTimeou() is required because
|
|
84
|
-
// there's no guarantee that the `transitionend` event will always fire,
|
|
85
|
-
// eg: clients that disable animations.
|
|
86
|
-
const t = getComputedStyle(el).getPropertyValue('--transition').trim();
|
|
87
|
-
const val = parseFloat(t);
|
|
88
|
-
const ms = t.endsWith('ms') ? val : val * 1000;
|
|
89
|
-
setTimeout(cleanup, ms);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Show a text toast.
|
|
93
|
-
export function toast(message, title, options = {}) {
|
|
94
|
-
const { variant = 'info', ...rest } = options;
|
|
95
|
-
|
|
96
|
-
const el = document.createElement('output');
|
|
97
|
-
el.setAttribute('data-variant', variant);
|
|
98
|
-
|
|
99
|
-
if (title) {
|
|
100
|
-
const titleEl = document.createElement('h6');
|
|
101
|
-
titleEl.className = 'toast-title';
|
|
102
|
-
titleEl.textContent = title;
|
|
103
|
-
el.appendChild(titleEl);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const msgEl = document.createElement('div');
|
|
107
|
-
msgEl.className = 'toast-message';
|
|
108
|
-
msgEl.textContent = message;
|
|
109
|
-
el.appendChild(msgEl);
|
|
110
|
-
|
|
111
|
-
return _show(el, rest);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Element-based toast.
|
|
115
|
-
export function toastEl(el, options = {}) {
|
|
116
|
-
let t;
|
|
117
|
-
|
|
118
|
-
if (el instanceof HTMLTemplateElement) {
|
|
119
|
-
t = el.content.firstElementChild?.cloneNode(true);
|
|
120
|
-
} else if (el) {
|
|
121
|
-
t = el.cloneNode(true);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (!t) {
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
t.removeAttribute('id');
|
|
129
|
-
|
|
130
|
-
return _show(t, options);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Clear all toasts.
|
|
134
|
-
export function toastClear(placement) {
|
|
135
|
-
if (placement && toasts[placement]) {
|
|
136
|
-
toasts[placement].innerHTML = '';
|
|
137
|
-
toasts[placement].hidePopover();
|
|
138
|
-
} else {
|
|
139
|
-
Object.values(toasts).forEach(c => {
|
|
140
|
-
c.innerHTML = '';
|
|
141
|
-
c.hidePopover();
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
}
|
package/assets/oat/js/tooltip.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* oat - Tooltip Enhancement
|
|
3
|
-
* Converts title attributes to data-tooltip for custom styling.
|
|
4
|
-
* Progressive enhancement: native title works without JS.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
8
|
-
const _attrib = 'title', _sel = '[title]';
|
|
9
|
-
const apply = el => {
|
|
10
|
-
const t = el.getAttribute(_attrib);
|
|
11
|
-
if (!t) return;
|
|
12
|
-
el.setAttribute('data-tooltip', t);
|
|
13
|
-
el.hasAttribute('aria-label') || el.setAttribute('aria-label', t);
|
|
14
|
-
|
|
15
|
-
// Kill the original 'title'.
|
|
16
|
-
el.removeAttribute(_attrib);
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// Apply to all elements on load.
|
|
20
|
-
document.querySelectorAll(_sel).forEach(apply);
|
|
21
|
-
|
|
22
|
-
// Apply to new elements.
|
|
23
|
-
new MutationObserver(muts => {
|
|
24
|
-
for (const m of muts) {
|
|
25
|
-
apply(m.target);
|
|
26
|
-
|
|
27
|
-
for (const n of m.addedNodes)
|
|
28
|
-
if (n.nodeType === 1) {
|
|
29
|
-
apply(n);
|
|
30
|
-
n.querySelectorAll(_sel).forEach(apply);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}).observe(document.body, {
|
|
34
|
-
childList: true, subtree: true, attributes: true, attributeFilter: [_attrib]
|
|
35
|
-
});
|
|
36
|
-
});
|
package/assets/oat/oat.js
DELETED
|
@@ -1,342 +0,0 @@
|
|
|
1
|
-
(() => {
|
|
2
|
-
// src/js/base.js
|
|
3
|
-
var OtBase = class extends HTMLElement {
|
|
4
|
-
#initialized = false;
|
|
5
|
-
// Called when element is added to DOM.
|
|
6
|
-
connectedCallback() {
|
|
7
|
-
if (this.#initialized) return;
|
|
8
|
-
if (document.readyState === "loading") {
|
|
9
|
-
document.addEventListener("DOMContentLoaded", () => this.#setup(), { once: true });
|
|
10
|
-
} else {
|
|
11
|
-
this.#setup();
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
// Private setup to ensure that init() is only called once.
|
|
15
|
-
#setup() {
|
|
16
|
-
if (this.#initialized) return;
|
|
17
|
-
this.#initialized = true;
|
|
18
|
-
this.init();
|
|
19
|
-
}
|
|
20
|
-
// Called when element is removed from DOM.
|
|
21
|
-
disconnectedCallback() {
|
|
22
|
-
this.cleanup();
|
|
23
|
-
}
|
|
24
|
-
// Override in subclass for cleanup logic.
|
|
25
|
-
cleanup() {
|
|
26
|
-
}
|
|
27
|
-
// Central event handler - enables automatic cleanup.
|
|
28
|
-
// Usage: element.addEventListener('click', this)
|
|
29
|
-
handleEvent(event) {
|
|
30
|
-
const handler = this[`on${event.type}`];
|
|
31
|
-
if (handler) handler.call(this, event);
|
|
32
|
-
}
|
|
33
|
-
// Given a keyboard event (left, right, home, end), the current selection idx
|
|
34
|
-
// total items in a list, return 0-n index of the next/previous item
|
|
35
|
-
// for doing a roving keyboard nav.
|
|
36
|
-
keyNav(event, idx, len, prevKey, nextKey, homeEnd = false) {
|
|
37
|
-
const { key } = event;
|
|
38
|
-
let next = -1;
|
|
39
|
-
if (key === nextKey) {
|
|
40
|
-
next = (idx + 1) % len;
|
|
41
|
-
} else if (key === prevKey) {
|
|
42
|
-
next = (idx - 1 + len) % len;
|
|
43
|
-
} else if (homeEnd) {
|
|
44
|
-
if (key === "Home") {
|
|
45
|
-
next = 0;
|
|
46
|
-
} else if (key === "End") {
|
|
47
|
-
next = len - 1;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (next >= 0) event.preventDefault();
|
|
51
|
-
return next;
|
|
52
|
-
}
|
|
53
|
-
// Emit a custom event.
|
|
54
|
-
emit(name, detail = null) {
|
|
55
|
-
return this.dispatchEvent(new CustomEvent(name, {
|
|
56
|
-
bubbles: true,
|
|
57
|
-
composed: true,
|
|
58
|
-
cancelable: true,
|
|
59
|
-
detail
|
|
60
|
-
}));
|
|
61
|
-
}
|
|
62
|
-
// Query selector within this element.
|
|
63
|
-
$(selector) {
|
|
64
|
-
return this.querySelector(selector);
|
|
65
|
-
}
|
|
66
|
-
// Query selector all within this element.
|
|
67
|
-
$$(selector) {
|
|
68
|
-
return Array.from(this.querySelectorAll(selector));
|
|
69
|
-
}
|
|
70
|
-
// Generate a unique ID string.
|
|
71
|
-
uid() {
|
|
72
|
-
return Math.random().toString(36).slice(2, 10);
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
if (!("commandForElement" in HTMLButtonElement.prototype)) {
|
|
76
|
-
document.addEventListener("click", (e) => {
|
|
77
|
-
const btn = e.target.closest("button[commandfor]");
|
|
78
|
-
if (!btn) return;
|
|
79
|
-
const target = document.getElementById(btn.getAttribute("commandfor"));
|
|
80
|
-
if (!target) return;
|
|
81
|
-
const command = btn.getAttribute("command") || "toggle";
|
|
82
|
-
if (target instanceof HTMLDialogElement) {
|
|
83
|
-
if (command === "show-modal") target.showModal();
|
|
84
|
-
else if (command === "close") target.close();
|
|
85
|
-
else target.open ? target.close() : target.showModal();
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// src/js/tabs.js
|
|
91
|
-
var OtTabs = class extends OtBase {
|
|
92
|
-
#tabs = [];
|
|
93
|
-
#panels = [];
|
|
94
|
-
init() {
|
|
95
|
-
const tablist = this.$(':scope > [role="tablist"]');
|
|
96
|
-
this.#tabs = tablist ? [...tablist.querySelectorAll('[role="tab"]')] : [];
|
|
97
|
-
this.#panels = this.$$(':scope > [role="tabpanel"]');
|
|
98
|
-
if (this.#tabs.length === 0 || this.#panels.length === 0) {
|
|
99
|
-
console.warn("ot-tabs: Missing tab or tabpanel elements");
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
this.#tabs.forEach((tab, i) => {
|
|
103
|
-
const panel = this.#panels[i];
|
|
104
|
-
if (!panel) return;
|
|
105
|
-
const tabId = tab.id || `ot-tab-${this.uid()}`;
|
|
106
|
-
const panelId = panel.id || `ot-panel-${this.uid()}`;
|
|
107
|
-
tab.id = tabId;
|
|
108
|
-
panel.id = panelId;
|
|
109
|
-
tab.setAttribute("aria-controls", panelId);
|
|
110
|
-
panel.setAttribute("aria-labelledby", tabId);
|
|
111
|
-
});
|
|
112
|
-
tablist.addEventListener("click", this);
|
|
113
|
-
tablist.addEventListener("keydown", this);
|
|
114
|
-
const activeTab = this.#tabs.findIndex((t) => t.ariaSelected === "true");
|
|
115
|
-
this.#activate(activeTab >= 0 ? activeTab : 0);
|
|
116
|
-
}
|
|
117
|
-
onclick(e) {
|
|
118
|
-
const index = this.#tabs.indexOf(e.target.closest('[role="tab"]'));
|
|
119
|
-
if (index >= 0) this.#activate(index);
|
|
120
|
-
}
|
|
121
|
-
onkeydown(e) {
|
|
122
|
-
if (!e.target.closest('[role="tab"]')) return;
|
|
123
|
-
const next = this.keyNav(e, this.activeIndex, this.#tabs.length, "ArrowLeft", "ArrowRight");
|
|
124
|
-
if (next >= 0) {
|
|
125
|
-
this.#activate(next);
|
|
126
|
-
this.#tabs[next].focus();
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
#activate(idx) {
|
|
130
|
-
this.#tabs.forEach((tab, i) => {
|
|
131
|
-
const isActive = i === idx;
|
|
132
|
-
tab.ariaSelected = String(isActive);
|
|
133
|
-
tab.tabIndex = isActive ? 0 : -1;
|
|
134
|
-
});
|
|
135
|
-
this.#panels.forEach((panel, i) => {
|
|
136
|
-
panel.hidden = i !== idx;
|
|
137
|
-
});
|
|
138
|
-
this.emit("ot-tab-change", { index: idx, tab: this.#tabs[idx] });
|
|
139
|
-
}
|
|
140
|
-
get activeIndex() {
|
|
141
|
-
return this.#tabs.findIndex((t) => t.ariaSelected === "true");
|
|
142
|
-
}
|
|
143
|
-
set activeIndex(value) {
|
|
144
|
-
if (value >= 0 && value < this.#tabs.length) {
|
|
145
|
-
this.#activate(value);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
customElements.define("ot-tabs", OtTabs);
|
|
150
|
-
|
|
151
|
-
// src/js/dropdown.js
|
|
152
|
-
var OtDropdown = class extends OtBase {
|
|
153
|
-
#menu;
|
|
154
|
-
#trigger;
|
|
155
|
-
#position;
|
|
156
|
-
#items;
|
|
157
|
-
init() {
|
|
158
|
-
this.#menu = this.$("[popover]");
|
|
159
|
-
this.#trigger = this.$("[popovertarget]");
|
|
160
|
-
if (!this.#menu || !this.#trigger) return;
|
|
161
|
-
this.#menu.addEventListener("toggle", this);
|
|
162
|
-
this.#menu.addEventListener("keydown", this);
|
|
163
|
-
this.#position = () => {
|
|
164
|
-
const r = this.#trigger.getBoundingClientRect();
|
|
165
|
-
const m = this.#menu.getBoundingClientRect();
|
|
166
|
-
this.#menu.style.top = `${r.bottom + m.height > window.innerHeight ? r.top - m.height : r.bottom}px`;
|
|
167
|
-
this.#menu.style.left = `${r.left + m.width > window.innerWidth ? r.right - m.width : r.left}px`;
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
ontoggle(e) {
|
|
171
|
-
if (e.newState === "open") {
|
|
172
|
-
this.#position();
|
|
173
|
-
window.addEventListener("scroll", this.#position, true);
|
|
174
|
-
window.addEventListener("resize", this.#position);
|
|
175
|
-
this.#items = this.$$('[role="menuitem"]');
|
|
176
|
-
this.#items[0]?.focus();
|
|
177
|
-
this.#trigger.ariaExpanded = "true";
|
|
178
|
-
} else {
|
|
179
|
-
this.cleanup();
|
|
180
|
-
this.#items = null;
|
|
181
|
-
this.#trigger.ariaExpanded = "false";
|
|
182
|
-
this.#trigger.focus();
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
onkeydown(e) {
|
|
186
|
-
if (!e.target.matches('[role="menuitem"]')) return;
|
|
187
|
-
const idx = this.#items.indexOf(e.target);
|
|
188
|
-
const next = this.keyNav(e, idx, this.#items.length, "ArrowUp", "ArrowDown", true);
|
|
189
|
-
if (next >= 0) this.#items[next].focus();
|
|
190
|
-
}
|
|
191
|
-
cleanup() {
|
|
192
|
-
window.removeEventListener("scroll", this.#position, true);
|
|
193
|
-
window.removeEventListener("resize", this.#position);
|
|
194
|
-
}
|
|
195
|
-
};
|
|
196
|
-
customElements.define("ot-dropdown", OtDropdown);
|
|
197
|
-
|
|
198
|
-
// src/js/tooltip.js
|
|
199
|
-
document.addEventListener("DOMContentLoaded", () => {
|
|
200
|
-
const _attrib = "title", _sel = "[title]";
|
|
201
|
-
const apply = (el) => {
|
|
202
|
-
const t = el.getAttribute(_attrib);
|
|
203
|
-
if (!t) return;
|
|
204
|
-
el.setAttribute("data-tooltip", t);
|
|
205
|
-
el.hasAttribute("aria-label") || el.setAttribute("aria-label", t);
|
|
206
|
-
el.removeAttribute(_attrib);
|
|
207
|
-
};
|
|
208
|
-
document.querySelectorAll(_sel).forEach(apply);
|
|
209
|
-
new MutationObserver((muts) => {
|
|
210
|
-
for (const m of muts) {
|
|
211
|
-
apply(m.target);
|
|
212
|
-
for (const n of m.addedNodes)
|
|
213
|
-
if (n.nodeType === 1) {
|
|
214
|
-
apply(n);
|
|
215
|
-
n.querySelectorAll(_sel).forEach(apply);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}).observe(document.body, {
|
|
219
|
-
childList: true,
|
|
220
|
-
subtree: true,
|
|
221
|
-
attributes: true,
|
|
222
|
-
attributeFilter: [_attrib]
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// src/js/sidebar.js
|
|
227
|
-
document.addEventListener("click", (e) => {
|
|
228
|
-
const toggle = e.target.closest("[data-sidebar-toggle]");
|
|
229
|
-
if (toggle) {
|
|
230
|
-
const layout = toggle.closest("[data-sidebar-layout]");
|
|
231
|
-
layout?.toggleAttribute("data-sidebar-open");
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
if (!e.target.closest("[data-sidebar]")) {
|
|
235
|
-
const layout = document.querySelector("[data-sidebar-layout][data-sidebar-open]");
|
|
236
|
-
if (layout && window.matchMedia("(max-width: 768px)").matches) {
|
|
237
|
-
layout.removeAttribute("data-sidebar-open");
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
// src/js/toast.js
|
|
243
|
-
var toasts = {};
|
|
244
|
-
function _get(placement) {
|
|
245
|
-
if (!toasts[placement]) {
|
|
246
|
-
const el = document.createElement("div");
|
|
247
|
-
el.className = "toast-container";
|
|
248
|
-
el.setAttribute("popover", "manual");
|
|
249
|
-
el.setAttribute("data-placement", placement);
|
|
250
|
-
document.body.appendChild(el);
|
|
251
|
-
toasts[placement] = el;
|
|
252
|
-
}
|
|
253
|
-
return toasts[placement];
|
|
254
|
-
}
|
|
255
|
-
function _show(el, options = {}) {
|
|
256
|
-
const { placement = "top-right", duration = 4e3 } = options;
|
|
257
|
-
const p = _get(placement);
|
|
258
|
-
el.classList.add("toast");
|
|
259
|
-
let timeout;
|
|
260
|
-
el.onmouseenter = () => clearTimeout(timeout);
|
|
261
|
-
el.onmouseleave = () => {
|
|
262
|
-
if (duration > 0) {
|
|
263
|
-
timeout = setTimeout(() => _remove(el, p), duration);
|
|
264
|
-
}
|
|
265
|
-
};
|
|
266
|
-
el.setAttribute("data-entering", "");
|
|
267
|
-
p.appendChild(el);
|
|
268
|
-
p.showPopover();
|
|
269
|
-
requestAnimationFrame(() => {
|
|
270
|
-
requestAnimationFrame(() => {
|
|
271
|
-
el.removeAttribute("data-entering");
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
if (duration > 0) {
|
|
275
|
-
timeout = setTimeout(() => _remove(el, p), duration);
|
|
276
|
-
}
|
|
277
|
-
return el;
|
|
278
|
-
}
|
|
279
|
-
function _remove(el, container) {
|
|
280
|
-
if (el.hasAttribute("data-exiting")) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
el.setAttribute("data-exiting", "");
|
|
284
|
-
const cleanup = () => {
|
|
285
|
-
el.remove();
|
|
286
|
-
if (!container.children.length) {
|
|
287
|
-
container.hidePopover();
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
el.addEventListener("transitionend", cleanup, { once: true });
|
|
291
|
-
const t = getComputedStyle(el).getPropertyValue("--transition").trim();
|
|
292
|
-
const val = parseFloat(t);
|
|
293
|
-
const ms = t.endsWith("ms") ? val : val * 1e3;
|
|
294
|
-
setTimeout(cleanup, ms);
|
|
295
|
-
}
|
|
296
|
-
function toast(message, title, options = {}) {
|
|
297
|
-
const { variant = "info", ...rest } = options;
|
|
298
|
-
const el = document.createElement("output");
|
|
299
|
-
el.setAttribute("data-variant", variant);
|
|
300
|
-
if (title) {
|
|
301
|
-
const titleEl = document.createElement("h6");
|
|
302
|
-
titleEl.className = "toast-title";
|
|
303
|
-
titleEl.textContent = title;
|
|
304
|
-
el.appendChild(titleEl);
|
|
305
|
-
}
|
|
306
|
-
const msgEl = document.createElement("div");
|
|
307
|
-
msgEl.className = "toast-message";
|
|
308
|
-
msgEl.textContent = message;
|
|
309
|
-
el.appendChild(msgEl);
|
|
310
|
-
return _show(el, rest);
|
|
311
|
-
}
|
|
312
|
-
function toastEl(el, options = {}) {
|
|
313
|
-
let t;
|
|
314
|
-
if (el instanceof HTMLTemplateElement) {
|
|
315
|
-
t = el.content.firstElementChild?.cloneNode(true);
|
|
316
|
-
} else if (el) {
|
|
317
|
-
t = el.cloneNode(true);
|
|
318
|
-
}
|
|
319
|
-
if (!t) {
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
t.removeAttribute("id");
|
|
323
|
-
return _show(t, options);
|
|
324
|
-
}
|
|
325
|
-
function toastClear(placement) {
|
|
326
|
-
if (placement && toasts[placement]) {
|
|
327
|
-
toasts[placement].innerHTML = "";
|
|
328
|
-
toasts[placement].hidePopover();
|
|
329
|
-
} else {
|
|
330
|
-
Object.values(toasts).forEach((c) => {
|
|
331
|
-
c.innerHTML = "";
|
|
332
|
-
c.hidePopover();
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// src/js/index.js
|
|
338
|
-
var ot = window.ot || (window.ot = {});
|
|
339
|
-
ot.toast = toast;
|
|
340
|
-
ot.toast.el = toastEl;
|
|
341
|
-
ot.toast.clear = toastClear;
|
|
342
|
-
})();
|