@dodlhuat/basix 1.2.1 → 1.2.3
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 +252 -5
- package/css/lightbox.scss +272 -0
- package/css/style.css +256 -0
- package/css/style.css.map +1 -1
- package/css/style.scss +1 -0
- package/js/bottom-sheet.js +4 -3
- package/js/bottom-sheet.ts +5 -3
- package/js/calendar.js +9 -5
- package/js/calendar.ts +7 -2
- package/js/carousel.js +15 -11
- package/js/carousel.ts +16 -11
- package/js/chart.js +4 -4
- package/js/chart.ts +5 -3
- package/js/datepicker.js +11 -3
- package/js/datepicker.ts +13 -3
- package/js/docs-nav.js +1 -0
- package/js/editor.js +28 -20
- package/js/editor.ts +28 -20
- package/js/file-uploader.js +6 -10
- package/js/file-uploader.ts +7 -11
- package/js/flyout-menu.js +8 -2
- package/js/flyout-menu.ts +7 -2
- package/js/gallery.js +6 -13
- package/js/gallery.ts +8 -16
- package/js/group-picker.js +10 -7
- package/js/group-picker.ts +11 -7
- package/js/lightbox.js +277 -0
- package/js/lightbox.ts +331 -0
- package/js/modal.js +5 -4
- package/js/modal.ts +6 -4
- package/js/popover.js +4 -2
- package/js/popover.ts +4 -2
- package/js/push-menu.js +3 -2
- package/js/push-menu.ts +4 -2
- package/js/scrollbar.js +31 -23
- package/js/scrollbar.ts +36 -26
- package/js/select.js +23 -9
- package/js/select.ts +29 -11
- package/js/stepper.js +5 -1
- package/js/stepper.ts +6 -1
- package/js/table.js +8 -3
- package/js/table.ts +9 -3
- package/js/timepicker.js +20 -21
- package/js/timepicker.ts +23 -21
- package/js/toast.js +3 -7
- package/js/toast.ts +4 -8
- package/js/tooltip.js +13 -4
- package/js/tooltip.ts +16 -4
- package/js/tree.js +4 -0
- package/js/tree.ts +5 -0
- package/js/utils.js +29 -1
- package/js/utils.ts +36 -1
- package/js/virtual-dropdown.js +4 -8
- package/js/virtual-dropdown.ts +5 -9
- package/package.json +1 -3
package/js/scrollbar.ts
CHANGED
|
@@ -9,6 +9,8 @@ class Scrollbar {
|
|
|
9
9
|
private static readonly instances = new WeakMap<HTMLElement, Scrollbar>();
|
|
10
10
|
private static activeInstance: Scrollbar | null = null;
|
|
11
11
|
private static globalListenersInstalled = false;
|
|
12
|
+
private static instanceCount = 0;
|
|
13
|
+
private static globalListenerAbortController: AbortController | null = null;
|
|
12
14
|
|
|
13
15
|
private readonly container!: HTMLElement;
|
|
14
16
|
private readonly viewport!: HTMLElement;
|
|
@@ -32,21 +34,7 @@ class Scrollbar {
|
|
|
32
34
|
private readonly boundUpdateThumb!: () => void;
|
|
33
35
|
private readonly boundContainerWheel!: (e: WheelEvent) => void;
|
|
34
36
|
|
|
35
|
-
constructor(
|
|
36
|
-
const container = typeof elementOrSelector === 'string'
|
|
37
|
-
? document.querySelector<HTMLElement>(elementOrSelector)
|
|
38
|
-
: elementOrSelector;
|
|
39
|
-
|
|
40
|
-
if (!container) {
|
|
41
|
-
throw new Error(`Scrollbar: Element not found for selector "${elementOrSelector}"`);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Return existing instance if already initialized
|
|
45
|
-
const existingInstance = Scrollbar.instances.get(container);
|
|
46
|
-
if (existingInstance) {
|
|
47
|
-
return existingInstance as any;
|
|
48
|
-
}
|
|
49
|
-
|
|
37
|
+
private constructor(container: HTMLElement) {
|
|
50
38
|
this.container = container;
|
|
51
39
|
|
|
52
40
|
// Query and validate required elements
|
|
@@ -75,7 +63,8 @@ class Scrollbar {
|
|
|
75
63
|
this.attachEventListeners();
|
|
76
64
|
Scrollbar.instances.set(container, this);
|
|
77
65
|
|
|
78
|
-
//
|
|
66
|
+
// Track instances and install global listeners once for all
|
|
67
|
+
Scrollbar.instanceCount++;
|
|
79
68
|
if (!Scrollbar.globalListenersInstalled) {
|
|
80
69
|
Scrollbar.installGlobalListeners();
|
|
81
70
|
}
|
|
@@ -112,18 +101,20 @@ class Scrollbar {
|
|
|
112
101
|
}
|
|
113
102
|
|
|
114
103
|
private static installGlobalListeners(): void {
|
|
115
|
-
|
|
104
|
+
const ac = new AbortController();
|
|
105
|
+
Scrollbar.globalListenerAbortController = ac;
|
|
106
|
+
|
|
116
107
|
document.addEventListener('pointermove', (e: PointerEvent) => {
|
|
117
108
|
Scrollbar.activeInstance?.boundPointerMove(e);
|
|
118
|
-
}, { passive: false });
|
|
109
|
+
}, { passive: false, signal: ac.signal });
|
|
119
110
|
|
|
120
111
|
document.addEventListener('pointerup', (e: PointerEvent) => {
|
|
121
112
|
Scrollbar.activeInstance?.boundPointerUp(e);
|
|
122
|
-
});
|
|
113
|
+
}, { signal: ac.signal });
|
|
123
114
|
|
|
124
115
|
document.addEventListener('pointercancel', (e: PointerEvent) => {
|
|
125
116
|
Scrollbar.activeInstance?.boundPointerUp(e);
|
|
126
|
-
});
|
|
117
|
+
}, { signal: ac.signal });
|
|
127
118
|
|
|
128
119
|
Scrollbar.globalListenersInstalled = true;
|
|
129
120
|
}
|
|
@@ -300,26 +291,45 @@ class Scrollbar {
|
|
|
300
291
|
if (Scrollbar.activeInstance === this) {
|
|
301
292
|
Scrollbar.activeInstance = null;
|
|
302
293
|
}
|
|
294
|
+
|
|
295
|
+
// Uninstall global listeners when last instance is destroyed
|
|
296
|
+
Scrollbar.instanceCount--;
|
|
297
|
+
if (Scrollbar.instanceCount === 0) {
|
|
298
|
+
Scrollbar.globalListenerAbortController?.abort();
|
|
299
|
+
Scrollbar.globalListenerAbortController = null;
|
|
300
|
+
Scrollbar.globalListenersInstalled = false;
|
|
301
|
+
}
|
|
303
302
|
}
|
|
304
303
|
|
|
305
304
|
// Static factory methods
|
|
305
|
+
public static create(elementOrSelector: string | HTMLElement): Scrollbar {
|
|
306
|
+
const container = typeof elementOrSelector === 'string'
|
|
307
|
+
? document.querySelector<HTMLElement>(elementOrSelector)
|
|
308
|
+
: elementOrSelector;
|
|
309
|
+
|
|
310
|
+
if (!container) {
|
|
311
|
+
throw new Error(`Scrollbar: Element not found for selector "${elementOrSelector}"`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const existing = Scrollbar.instances.get(container);
|
|
315
|
+
if (existing) return existing;
|
|
316
|
+
|
|
317
|
+
return new Scrollbar(container);
|
|
318
|
+
}
|
|
319
|
+
|
|
306
320
|
public static initAll(selector: string): Scrollbar[] {
|
|
307
321
|
const containers = document.querySelectorAll<HTMLElement>(selector);
|
|
308
|
-
return Array.from(containers).map(container =>
|
|
322
|
+
return Array.from(containers).map(container => Scrollbar.create(container));
|
|
309
323
|
}
|
|
310
324
|
|
|
311
325
|
public static initOne(elementOrSelector: string | HTMLElement): Scrollbar {
|
|
312
|
-
return
|
|
326
|
+
return Scrollbar.create(elementOrSelector);
|
|
313
327
|
}
|
|
314
328
|
|
|
315
329
|
public static getInstance(container: HTMLElement): Scrollbar | undefined {
|
|
316
330
|
return Scrollbar.instances.get(container);
|
|
317
331
|
}
|
|
318
332
|
|
|
319
|
-
public static destroyAll(): void {
|
|
320
|
-
// Note: WeakMap doesn't support iteration, so this is a no-op
|
|
321
|
-
// Individual instances should be destroyed by calling destroy()
|
|
322
|
-
}
|
|
323
333
|
}
|
|
324
334
|
|
|
325
335
|
export { Scrollbar, ScrollbarElements };
|
package/js/select.js
CHANGED
|
@@ -11,7 +11,18 @@ class Select {
|
|
|
11
11
|
if (result === null) {
|
|
12
12
|
throw new Error(`Select: Failed to initialize select for "${elementOrSelector}"`);
|
|
13
13
|
}
|
|
14
|
-
this.isMultiselect = result;
|
|
14
|
+
this.isMultiselect = result.isMulti;
|
|
15
|
+
this.dropdown = result.dropdown;
|
|
16
|
+
this.documentClickHandler = (e) => {
|
|
17
|
+
if (this.dropdown && !this.dropdown.contains(e.target)) {
|
|
18
|
+
this.dropdown.classList.remove('open');
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
document.addEventListener('click', this.documentClickHandler);
|
|
22
|
+
}
|
|
23
|
+
destroy() {
|
|
24
|
+
document.removeEventListener('click', this.documentClickHandler);
|
|
25
|
+
this.dropdown?.classList.remove('open');
|
|
15
26
|
}
|
|
16
27
|
value() {
|
|
17
28
|
if (!this.element) {
|
|
@@ -29,7 +40,16 @@ class Select {
|
|
|
29
40
|
if (!element) {
|
|
30
41
|
return null;
|
|
31
42
|
}
|
|
32
|
-
|
|
43
|
+
const result = Select.initElement(element);
|
|
44
|
+
if (!result)
|
|
45
|
+
return null;
|
|
46
|
+
// Static init path: add document listener without lifecycle management
|
|
47
|
+
document.addEventListener('click', (e) => {
|
|
48
|
+
if (!result.dropdown.contains(e.target)) {
|
|
49
|
+
result.dropdown.classList.remove('open');
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
return result.isMulti;
|
|
33
53
|
}
|
|
34
54
|
static initElement(element) {
|
|
35
55
|
if (!Select.transformSelect(element)) {
|
|
@@ -74,13 +94,7 @@ class Select {
|
|
|
74
94
|
dropdown.classList.remove('open');
|
|
75
95
|
});
|
|
76
96
|
}
|
|
77
|
-
|
|
78
|
-
document.addEventListener('click', (e) => {
|
|
79
|
-
if (!dropdown.contains(e.target)) {
|
|
80
|
-
dropdown.classList.remove('open');
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
return isMulti;
|
|
97
|
+
return { isMulti, dropdown };
|
|
84
98
|
}
|
|
85
99
|
static closeAllDropdowns(exceptDropdown) {
|
|
86
100
|
document.querySelectorAll('.dropdown').forEach(dropdown => {
|
package/js/select.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
class Select {
|
|
2
2
|
private readonly element: HTMLSelectElement;
|
|
3
3
|
private readonly isMultiselect: boolean;
|
|
4
|
+
private readonly dropdown: HTMLElement | null;
|
|
5
|
+
private readonly documentClickHandler: (e: Event) => void;
|
|
4
6
|
|
|
5
7
|
constructor(elementOrSelector: string | HTMLSelectElement) {
|
|
6
8
|
const element = typeof elementOrSelector === 'string'
|
|
@@ -18,7 +20,20 @@ class Select {
|
|
|
18
20
|
throw new Error(`Select: Failed to initialize select for "${elementOrSelector}"`);
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
this.isMultiselect = result;
|
|
23
|
+
this.isMultiselect = result.isMulti;
|
|
24
|
+
this.dropdown = result.dropdown;
|
|
25
|
+
|
|
26
|
+
this.documentClickHandler = (e: Event) => {
|
|
27
|
+
if (this.dropdown && !this.dropdown.contains(e.target as Node)) {
|
|
28
|
+
this.dropdown.classList.remove('open');
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
document.addEventListener('click', this.documentClickHandler);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public destroy(): void {
|
|
35
|
+
document.removeEventListener('click', this.documentClickHandler);
|
|
36
|
+
this.dropdown?.classList.remove('open');
|
|
22
37
|
}
|
|
23
38
|
|
|
24
39
|
public value(): string | string[] | undefined {
|
|
@@ -42,10 +57,20 @@ class Select {
|
|
|
42
57
|
return null;
|
|
43
58
|
}
|
|
44
59
|
|
|
45
|
-
|
|
60
|
+
const result = Select.initElement(element);
|
|
61
|
+
if (!result) return null;
|
|
62
|
+
|
|
63
|
+
// Static init path: add document listener without lifecycle management
|
|
64
|
+
document.addEventListener('click', (e: Event) => {
|
|
65
|
+
if (!result.dropdown.contains(e.target as Node)) {
|
|
66
|
+
result.dropdown.classList.remove('open');
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return result.isMulti;
|
|
46
71
|
}
|
|
47
72
|
|
|
48
|
-
private static initElement(element: HTMLSelectElement): boolean | null {
|
|
73
|
+
private static initElement(element: HTMLSelectElement): { isMulti: boolean; dropdown: HTMLElement } | null {
|
|
49
74
|
if (!Select.transformSelect(element)) {
|
|
50
75
|
return null;
|
|
51
76
|
}
|
|
@@ -98,14 +123,7 @@ class Select {
|
|
|
98
123
|
});
|
|
99
124
|
}
|
|
100
125
|
|
|
101
|
-
|
|
102
|
-
document.addEventListener('click', (e: Event) => {
|
|
103
|
-
if (!dropdown.contains(e.target as Node)) {
|
|
104
|
-
dropdown.classList.remove('open');
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
return isMulti;
|
|
126
|
+
return { isMulti, dropdown };
|
|
109
127
|
}
|
|
110
128
|
|
|
111
129
|
private static closeAllDropdowns(exceptDropdown?: HTMLElement): void {
|
package/js/stepper.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
class Stepper {
|
|
2
2
|
constructor(elementOrSelector, options = {}) {
|
|
3
|
+
this.abortController = new AbortController();
|
|
3
4
|
const element = typeof elementOrSelector === 'string'
|
|
4
5
|
? document.querySelector(elementOrSelector)
|
|
5
6
|
: elementOrSelector;
|
|
@@ -17,7 +18,7 @@ class Stepper {
|
|
|
17
18
|
if (options.clickable) {
|
|
18
19
|
this.container.classList.add('stepper-clickable');
|
|
19
20
|
this.steps.forEach((step, i) => {
|
|
20
|
-
step.addEventListener('click', () => this.goTo(i));
|
|
21
|
+
step.addEventListener('click', () => this.goTo(i), { signal: this.abortController.signal });
|
|
21
22
|
});
|
|
22
23
|
}
|
|
23
24
|
this.render();
|
|
@@ -76,5 +77,8 @@ class Stepper {
|
|
|
76
77
|
isLast() {
|
|
77
78
|
return this.current === this.steps.length - 1;
|
|
78
79
|
}
|
|
80
|
+
destroy() {
|
|
81
|
+
this.abortController.abort();
|
|
82
|
+
}
|
|
79
83
|
}
|
|
80
84
|
export { Stepper };
|
package/js/stepper.ts
CHANGED
|
@@ -10,6 +10,7 @@ class Stepper {
|
|
|
10
10
|
private connectors: HTMLElement[];
|
|
11
11
|
private current: number;
|
|
12
12
|
private readonly onChange?: (current: number, previous: number) => void;
|
|
13
|
+
private abortController = new AbortController();
|
|
13
14
|
|
|
14
15
|
constructor(elementOrSelector: string | HTMLElement, options: StepperOptions = {}) {
|
|
15
16
|
const element = typeof elementOrSelector === 'string'
|
|
@@ -32,7 +33,7 @@ class Stepper {
|
|
|
32
33
|
if (options.clickable) {
|
|
33
34
|
this.container.classList.add('stepper-clickable');
|
|
34
35
|
this.steps.forEach((step, i) => {
|
|
35
|
-
step.addEventListener('click', () => this.goTo(i));
|
|
36
|
+
step.addEventListener('click', () => this.goTo(i), { signal: this.abortController.signal });
|
|
36
37
|
});
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -99,6 +100,10 @@ class Stepper {
|
|
|
99
100
|
public isLast(): boolean {
|
|
100
101
|
return this.current === this.steps.length - 1;
|
|
101
102
|
}
|
|
103
|
+
|
|
104
|
+
public destroy(): void {
|
|
105
|
+
this.abortController.abort();
|
|
106
|
+
}
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
export { Stepper, type StepperOptions };
|
package/js/table.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Select } from "./select.js";
|
|
2
2
|
class Table {
|
|
3
3
|
constructor(elementOrSelector, options = {}) {
|
|
4
|
+
this.abortController = new AbortController();
|
|
4
5
|
const element = typeof elementOrSelector === 'string'
|
|
5
6
|
? document.querySelector(elementOrSelector)
|
|
6
7
|
: elementOrSelector;
|
|
@@ -74,7 +75,7 @@ class Table {
|
|
|
74
75
|
searchInput.className = 'search-input';
|
|
75
76
|
searchInput.addEventListener('input', (e) => {
|
|
76
77
|
this.handleSearch(e.target.value);
|
|
77
|
-
});
|
|
78
|
+
}, { signal: this.abortController.signal });
|
|
78
79
|
controlsDiv.appendChild(searchInput);
|
|
79
80
|
// Page size selector
|
|
80
81
|
const selectGroup = document.createElement('div');
|
|
@@ -93,7 +94,7 @@ class Table {
|
|
|
93
94
|
});
|
|
94
95
|
pageSizeSelect.addEventListener('change', (e) => {
|
|
95
96
|
this.handlePageSizeChange(parseInt(e.target.value, 10));
|
|
96
|
-
});
|
|
97
|
+
}, { signal: this.abortController.signal });
|
|
97
98
|
this.assignUniqueId(pageSizeSelect, 'page-size-select-0');
|
|
98
99
|
selectGroup.appendChild(pageSizeSelect);
|
|
99
100
|
controlsDiv.appendChild(selectGroup);
|
|
@@ -117,7 +118,7 @@ class Table {
|
|
|
117
118
|
th.dataset.key = col.key;
|
|
118
119
|
if (col.sortable !== false) {
|
|
119
120
|
th.classList.add('sortable');
|
|
120
|
-
th.addEventListener('click', () => this.handleSort(col.key));
|
|
121
|
+
th.addEventListener('click', () => this.handleSort(col.key), { signal: this.abortController.signal });
|
|
121
122
|
}
|
|
122
123
|
tr.appendChild(th);
|
|
123
124
|
});
|
|
@@ -355,5 +356,9 @@ class Table {
|
|
|
355
356
|
getData() {
|
|
356
357
|
return this.getFilteredAndSortedData();
|
|
357
358
|
}
|
|
359
|
+
destroy() {
|
|
360
|
+
this.abortController.abort();
|
|
361
|
+
this.container.innerHTML = '';
|
|
362
|
+
}
|
|
358
363
|
}
|
|
359
364
|
export { Table };
|
package/js/table.ts
CHANGED
|
@@ -30,6 +30,7 @@ class Table {
|
|
|
30
30
|
private tableBody!: HTMLTableSectionElement;
|
|
31
31
|
private tableHeader!: HTMLTableSectionElement;
|
|
32
32
|
private paginationContainer!: HTMLDivElement;
|
|
33
|
+
private abortController = new AbortController();
|
|
33
34
|
|
|
34
35
|
constructor(elementOrSelector: string | HTMLElement, options: TableOptions = {}) {
|
|
35
36
|
const element = typeof elementOrSelector === 'string'
|
|
@@ -118,7 +119,7 @@ class Table {
|
|
|
118
119
|
searchInput.className = 'search-input';
|
|
119
120
|
searchInput.addEventListener('input', (e) => {
|
|
120
121
|
this.handleSearch((e.target as HTMLInputElement).value);
|
|
121
|
-
});
|
|
122
|
+
}, { signal: this.abortController.signal });
|
|
122
123
|
controlsDiv.appendChild(searchInput);
|
|
123
124
|
|
|
124
125
|
// Page size selector
|
|
@@ -142,7 +143,7 @@ class Table {
|
|
|
142
143
|
|
|
143
144
|
pageSizeSelect.addEventListener('change', (e) => {
|
|
144
145
|
this.handlePageSizeChange(parseInt((e.target as HTMLSelectElement).value, 10));
|
|
145
|
-
});
|
|
146
|
+
}, { signal: this.abortController.signal });
|
|
146
147
|
|
|
147
148
|
this.assignUniqueId(pageSizeSelect, 'page-size-select-0');
|
|
148
149
|
selectGroup.appendChild(pageSizeSelect);
|
|
@@ -172,7 +173,7 @@ class Table {
|
|
|
172
173
|
|
|
173
174
|
if (col.sortable !== false) {
|
|
174
175
|
th.classList.add('sortable');
|
|
175
|
-
th.addEventListener('click', () => this.handleSort(col.key));
|
|
176
|
+
th.addEventListener('click', () => this.handleSort(col.key), { signal: this.abortController.signal });
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
tr.appendChild(th);
|
|
@@ -448,6 +449,11 @@ class Table {
|
|
|
448
449
|
public getData(): TableRow[] {
|
|
449
450
|
return this.getFilteredAndSortedData();
|
|
450
451
|
}
|
|
452
|
+
|
|
453
|
+
public destroy(): void {
|
|
454
|
+
this.abortController.abort();
|
|
455
|
+
this.container.innerHTML = '';
|
|
456
|
+
}
|
|
451
457
|
}
|
|
452
458
|
|
|
453
459
|
export { Table, TableRow, TableColumn, TableOptions };
|
package/js/timepicker.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
class TimeSpanPicker {
|
|
2
|
-
constructor(
|
|
3
|
-
|
|
2
|
+
constructor(elementOrSelector, options) {
|
|
3
|
+
this.handleStartChange = () => { this.handleChange(); };
|
|
4
|
+
this.handleEndChange = () => { this.handleChange(); };
|
|
5
|
+
const element = typeof elementOrSelector === 'string'
|
|
6
|
+
? (elementOrSelector.startsWith('#') || elementOrSelector.startsWith('.')
|
|
7
|
+
? document.querySelector(elementOrSelector)
|
|
8
|
+
: document.getElementById(elementOrSelector))
|
|
9
|
+
: elementOrSelector;
|
|
4
10
|
if (!element) {
|
|
5
|
-
throw new Error(`
|
|
11
|
+
throw new Error(`TimeSpanPicker: Element not found for "${elementOrSelector}"`);
|
|
6
12
|
}
|
|
7
13
|
this.container = element;
|
|
8
14
|
this.onChange = options?.onChange;
|
|
15
|
+
this.uid = `tsp-${Math.random().toString(36).slice(2, 9)}`;
|
|
9
16
|
this.render();
|
|
10
17
|
this.startTimeInput = this.queryInput('.timespan-start');
|
|
11
18
|
this.endTimeInput = this.queryInput('.timespan-end');
|
|
@@ -29,15 +36,13 @@ class TimeSpanPicker {
|
|
|
29
36
|
return input;
|
|
30
37
|
}
|
|
31
38
|
render() {
|
|
39
|
+
const startId = `${this.uid}-start`;
|
|
40
|
+
const endId = `${this.uid}-end`;
|
|
32
41
|
this.container.innerHTML = `
|
|
33
42
|
<div class="timespan-picker">
|
|
34
43
|
<div class="timespan-field timespan-field-start">
|
|
35
|
-
<label for="
|
|
36
|
-
<input
|
|
37
|
-
type="time"
|
|
38
|
-
class="timespan-start"
|
|
39
|
-
id="timespan-start"
|
|
40
|
-
/>
|
|
44
|
+
<label for="${startId}">From</label>
|
|
45
|
+
<input type="time" class="timespan-start" id="${startId}"/>
|
|
41
46
|
</div>
|
|
42
47
|
|
|
43
48
|
<div class="timespan-center">
|
|
@@ -46,12 +51,8 @@ class TimeSpanPicker {
|
|
|
46
51
|
</div>
|
|
47
52
|
|
|
48
53
|
<div class="timespan-field timespan-field-end">
|
|
49
|
-
<label for="
|
|
50
|
-
<input
|
|
51
|
-
type="time"
|
|
52
|
-
class="timespan-end"
|
|
53
|
-
id="timespan-end"
|
|
54
|
-
/>
|
|
54
|
+
<label for="${endId}">To</label>
|
|
55
|
+
<input type="time" class="timespan-end" id="${endId}"/>
|
|
55
56
|
</div>
|
|
56
57
|
</div>
|
|
57
58
|
<div class="timespan-bar" aria-hidden="true">
|
|
@@ -60,8 +61,8 @@ class TimeSpanPicker {
|
|
|
60
61
|
`;
|
|
61
62
|
}
|
|
62
63
|
attachEventListeners() {
|
|
63
|
-
this.startTimeInput.addEventListener('change',
|
|
64
|
-
this.endTimeInput.addEventListener('change',
|
|
64
|
+
this.startTimeInput.addEventListener('change', this.handleStartChange);
|
|
65
|
+
this.endTimeInput.addEventListener('change', this.handleEndChange);
|
|
65
66
|
}
|
|
66
67
|
toMinutes(time) {
|
|
67
68
|
const [h, m] = time.split(':').map(Number);
|
|
@@ -148,10 +149,8 @@ class TimeSpanPicker {
|
|
|
148
149
|
return !!(start && end && start < end);
|
|
149
150
|
}
|
|
150
151
|
destroy() {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
this.startTimeInput.removeEventListener('change', startHandler);
|
|
154
|
-
this.endTimeInput.removeEventListener('change', endHandler);
|
|
152
|
+
this.startTimeInput.removeEventListener('change', this.handleStartChange);
|
|
153
|
+
this.endTimeInput.removeEventListener('change', this.handleEndChange);
|
|
155
154
|
}
|
|
156
155
|
}
|
|
157
156
|
export { TimeSpanPicker };
|
package/js/timepicker.ts
CHANGED
|
@@ -14,15 +14,22 @@ class TimeSpanPicker {
|
|
|
14
14
|
private startTimeInput: HTMLInputElement;
|
|
15
15
|
private endTimeInput: HTMLInputElement;
|
|
16
16
|
private onChange?: (start: string, end: string) => void;
|
|
17
|
+
private readonly uid: string;
|
|
18
|
+
|
|
19
|
+
constructor(elementOrSelector: string | HTMLElement, options?: TimeSpanPickerOptions) {
|
|
20
|
+
const element = typeof elementOrSelector === 'string'
|
|
21
|
+
? (elementOrSelector.startsWith('#') || elementOrSelector.startsWith('.')
|
|
22
|
+
? document.querySelector<HTMLElement>(elementOrSelector)
|
|
23
|
+
: document.getElementById(elementOrSelector))
|
|
24
|
+
: elementOrSelector;
|
|
17
25
|
|
|
18
|
-
constructor(containerId: string, options?: TimeSpanPickerOptions) {
|
|
19
|
-
const element = document.getElementById(containerId);
|
|
20
26
|
if (!element) {
|
|
21
|
-
throw new Error(`
|
|
27
|
+
throw new Error(`TimeSpanPicker: Element not found for "${elementOrSelector}"`);
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
this.container = element;
|
|
25
31
|
this.onChange = options?.onChange;
|
|
32
|
+
this.uid = `tsp-${Math.random().toString(36).slice(2, 9)}`;
|
|
26
33
|
|
|
27
34
|
this.render();
|
|
28
35
|
|
|
@@ -53,15 +60,13 @@ class TimeSpanPicker {
|
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
private render(): void {
|
|
63
|
+
const startId = `${this.uid}-start`;
|
|
64
|
+
const endId = `${this.uid}-end`;
|
|
56
65
|
this.container.innerHTML = `
|
|
57
66
|
<div class="timespan-picker">
|
|
58
67
|
<div class="timespan-field timespan-field-start">
|
|
59
|
-
<label for="
|
|
60
|
-
<input
|
|
61
|
-
type="time"
|
|
62
|
-
class="timespan-start"
|
|
63
|
-
id="timespan-start"
|
|
64
|
-
/>
|
|
68
|
+
<label for="${startId}">From</label>
|
|
69
|
+
<input type="time" class="timespan-start" id="${startId}"/>
|
|
65
70
|
</div>
|
|
66
71
|
|
|
67
72
|
<div class="timespan-center">
|
|
@@ -70,12 +75,8 @@ class TimeSpanPicker {
|
|
|
70
75
|
</div>
|
|
71
76
|
|
|
72
77
|
<div class="timespan-field timespan-field-end">
|
|
73
|
-
<label for="
|
|
74
|
-
<input
|
|
75
|
-
type="time"
|
|
76
|
-
class="timespan-end"
|
|
77
|
-
id="timespan-end"
|
|
78
|
-
/>
|
|
78
|
+
<label for="${endId}">To</label>
|
|
79
|
+
<input type="time" class="timespan-end" id="${endId}"/>
|
|
79
80
|
</div>
|
|
80
81
|
</div>
|
|
81
82
|
<div class="timespan-bar" aria-hidden="true">
|
|
@@ -84,9 +85,12 @@ class TimeSpanPicker {
|
|
|
84
85
|
`;
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
private readonly handleStartChange = (): void => { this.handleChange(); };
|
|
89
|
+
private readonly handleEndChange = (): void => { this.handleChange(); };
|
|
90
|
+
|
|
87
91
|
private attachEventListeners(): void {
|
|
88
|
-
this.startTimeInput.addEventListener('change',
|
|
89
|
-
this.endTimeInput.addEventListener('change',
|
|
92
|
+
this.startTimeInput.addEventListener('change', this.handleStartChange);
|
|
93
|
+
this.endTimeInput.addEventListener('change', this.handleEndChange);
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
private toMinutes(time: string): number {
|
|
@@ -183,10 +187,8 @@ class TimeSpanPicker {
|
|
|
183
187
|
}
|
|
184
188
|
|
|
185
189
|
public destroy(): void {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
this.startTimeInput.removeEventListener('change', startHandler);
|
|
189
|
-
this.endTimeInput.removeEventListener('change', endHandler);
|
|
190
|
+
this.startTimeInput.removeEventListener('change', this.handleStartChange);
|
|
191
|
+
this.endTimeInput.removeEventListener('change', this.handleEndChange);
|
|
190
192
|
}
|
|
191
193
|
}
|
|
192
194
|
|
package/js/toast.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { escapeHtml } from './utils.js';
|
|
1
2
|
class Toast {
|
|
2
3
|
constructor(contentOrOptions, header = '', type, closeable = true) {
|
|
3
4
|
this.closureIcon = '<div class="icon icon-close close"></div>';
|
|
@@ -79,15 +80,10 @@ class Toast {
|
|
|
79
80
|
parts.push(this.closureIcon);
|
|
80
81
|
}
|
|
81
82
|
if (this.header) {
|
|
82
|
-
parts.push(`<div class="header">${
|
|
83
|
+
parts.push(`<div class="header">${escapeHtml(this.header)}</div>`);
|
|
83
84
|
}
|
|
84
|
-
parts.push(`<div class="content">${
|
|
85
|
+
parts.push(`<div class="content">${escapeHtml(this.content)}</div>`);
|
|
85
86
|
return parts.join('');
|
|
86
87
|
}
|
|
87
|
-
escapeHtml(text) {
|
|
88
|
-
const div = document.createElement('div');
|
|
89
|
-
div.textContent = text;
|
|
90
|
-
return div.innerHTML;
|
|
91
|
-
}
|
|
92
88
|
}
|
|
93
89
|
export { Toast };
|
package/js/toast.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { escapeHtml } from './utils.js';
|
|
2
|
+
|
|
1
3
|
type ToastType = 'success' | 'error' | 'warning' | 'info';
|
|
2
4
|
|
|
3
5
|
interface ToastOptions {
|
|
@@ -119,19 +121,13 @@ class Toast {
|
|
|
119
121
|
}
|
|
120
122
|
|
|
121
123
|
if (this.header) {
|
|
122
|
-
parts.push(`<div class="header">${
|
|
124
|
+
parts.push(`<div class="header">${escapeHtml(this.header)}</div>`);
|
|
123
125
|
}
|
|
124
126
|
|
|
125
|
-
parts.push(`<div class="content">${
|
|
127
|
+
parts.push(`<div class="content">${escapeHtml(this.content)}</div>`);
|
|
126
128
|
|
|
127
129
|
return parts.join('');
|
|
128
130
|
}
|
|
129
|
-
|
|
130
|
-
private escapeHtml(text: string): string {
|
|
131
|
-
const div = document.createElement('div');
|
|
132
|
-
div.textContent = text;
|
|
133
|
-
return div.innerHTML;
|
|
134
|
-
}
|
|
135
131
|
}
|
|
136
132
|
|
|
137
133
|
export { Toast };
|
package/js/tooltip.js
CHANGED
|
@@ -23,7 +23,8 @@ class Tooltip {
|
|
|
23
23
|
position: options.position ?? 'auto',
|
|
24
24
|
offset: options.offset ?? 8,
|
|
25
25
|
delay: options.delay ?? 0,
|
|
26
|
-
className: options.className ?? ''
|
|
26
|
+
className: options.className ?? '',
|
|
27
|
+
isHtml: options.isHtml ?? false,
|
|
27
28
|
};
|
|
28
29
|
this.attachEvents();
|
|
29
30
|
}
|
|
@@ -34,7 +35,7 @@ class Tooltip {
|
|
|
34
35
|
const position = trigger.getAttribute('data-tooltip-position') ?? 'auto';
|
|
35
36
|
const className = trigger.getAttribute('data-tooltip-class') ?? '';
|
|
36
37
|
if (content) {
|
|
37
|
-
new Tooltip(trigger, content, { position, className });
|
|
38
|
+
new Tooltip(trigger, content, { position, className, isHtml: false });
|
|
38
39
|
}
|
|
39
40
|
});
|
|
40
41
|
// Also support content from separate elements
|
|
@@ -47,7 +48,7 @@ class Tooltip {
|
|
|
47
48
|
const contentElement = document.getElementById(contentId);
|
|
48
49
|
if (contentElement) {
|
|
49
50
|
const content = contentElement.innerHTML;
|
|
50
|
-
new Tooltip(trigger, content, { position, className });
|
|
51
|
+
new Tooltip(trigger, content, { position, className, isHtml: true });
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
});
|
|
@@ -94,7 +95,15 @@ class Tooltip {
|
|
|
94
95
|
tooltip.className = 'tooltip';
|
|
95
96
|
tooltip.id = `tooltip-${++Tooltip.idCounter}`;
|
|
96
97
|
tooltip.setAttribute('role', 'tooltip');
|
|
97
|
-
|
|
98
|
+
const tooltipContent = document.createElement('div');
|
|
99
|
+
tooltipContent.className = 'tooltip-content';
|
|
100
|
+
if (this.options.isHtml) {
|
|
101
|
+
tooltipContent.innerHTML = this.content;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
tooltipContent.textContent = this.content;
|
|
105
|
+
}
|
|
106
|
+
tooltip.appendChild(tooltipContent);
|
|
98
107
|
if (this.options.className) {
|
|
99
108
|
tooltip.classList.add(this.options.className);
|
|
100
109
|
}
|