@dodlhuat/basix 1.4.2 → 1.5.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/README.md +55 -9
- package/css/alert.scss +9 -9
- package/css/badge.scss +5 -25
- package/css/bottom-sheet.scss +2 -6
- package/css/breadcrumb.scss +18 -9
- package/css/calendar.scss +19 -29
- package/css/carousel.scss +3 -3
- package/css/chart.scss +1 -6
- package/css/checkbox.scss +1 -1
- package/css/chips.scss +13 -23
- package/css/code-viewer.scss +2 -2
- package/css/color-picker.scss +3 -5
- package/css/context-menu.scss +3 -8
- package/css/datepicker.scss +84 -71
- package/css/defaults.scss +25 -0
- package/css/docs.scss +6 -6
- package/css/dropdown.scss +3 -8
- package/css/editor.scss +15 -18
- package/css/file-uploader.scss +6 -17
- package/css/flyout-menu.scss +5 -1
- package/css/form.scss +43 -57
- package/css/gallery.scss +15 -0
- package/css/group-picker.scss +28 -17
- package/css/icons.scss +7 -327
- package/css/lightbox.scss +7 -7
- package/css/mixins.scss +77 -0
- package/css/modal.scss +4 -8
- package/css/parameters.scss +8 -2
- package/css/placeholder.scss +2 -17
- package/css/popover.scss +2 -7
- package/css/properties.scss +0 -5
- package/css/push-menu.scss +1 -1
- package/css/range-slider.scss +7 -6
- package/css/stepper.scss +4 -3
- package/css/style.css +279 -554
- package/css/style.css.map +1 -1
- package/css/style.min.css +1 -1
- package/css/style.min.css.map +1 -1
- package/css/tabs.scss +3 -7
- package/css/timepicker.scss +3 -3
- package/css/tooltip.scss +1 -1
- package/css/typography.scss +3 -3
- package/css/virtual-dropdown.scss +12 -27
- package/js/bottom-sheet.d.ts +5 -5
- package/js/bottom-sheet.js +41 -58
- package/js/calendar.d.ts +1 -2
- package/js/calendar.js +77 -84
- package/js/carousel.d.ts +2 -1
- package/js/carousel.js +25 -16
- package/js/chart.d.ts +1 -1
- package/js/chart.js +104 -65
- package/js/code-viewer.d.ts +1 -1
- package/js/code-viewer.js +7 -18
- package/js/color-picker.d.ts +2 -2
- package/js/color-picker.js +20 -22
- package/js/context-menu.d.ts +1 -1
- package/js/context-menu.js +10 -12
- package/js/datepicker.d.ts +14 -3
- package/js/datepicker.js +180 -99
- package/js/docs-nav.js +1 -1
- package/js/dropdown.d.ts +1 -1
- package/js/dropdown.js +4 -5
- package/js/editor.d.ts +1 -1
- package/js/editor.js +30 -34
- package/js/file-uploader.d.ts +2 -9
- package/js/file-uploader.js +57 -86
- package/js/flyout-menu.d.ts +3 -3
- package/js/flyout-menu.js +20 -22
- package/js/gallery.d.ts +6 -2
- package/js/gallery.js +49 -20
- package/js/group-picker.d.ts +2 -1
- package/js/group-picker.js +30 -34
- package/js/lightbox.d.ts +5 -3
- package/js/lightbox.js +36 -31
- package/js/listeners.d.ts +7 -0
- package/js/listeners.js +14 -0
- package/js/modal.d.ts +4 -1
- package/js/modal.js +17 -22
- package/js/popover.d.ts +2 -2
- package/js/popover.js +42 -37
- package/js/position.js +3 -5
- package/js/push-menu.d.ts +8 -2
- package/js/push-menu.js +34 -23
- package/js/range-slider.d.ts +2 -2
- package/js/range-slider.js +7 -6
- package/js/scroll.js +4 -7
- package/js/scrollbar.d.ts +3 -3
- package/js/scrollbar.js +39 -42
- package/js/select.d.ts +1 -1
- package/js/select.js +20 -24
- package/js/sidebar-nav.d.ts +2 -1
- package/js/sidebar-nav.js +15 -10
- package/js/stepper.d.ts +1 -1
- package/js/stepper.js +9 -11
- package/js/table.d.ts +1 -1
- package/js/table.js +24 -23
- package/js/tabs.d.ts +1 -1
- package/js/tabs.js +10 -17
- package/js/theme.d.ts +1 -0
- package/js/theme.js +6 -15
- package/js/timepicker.d.ts +8 -6
- package/js/timepicker.js +36 -36
- package/js/toast.d.ts +4 -1
- package/js/toast.js +13 -5
- package/js/tooltip.d.ts +1 -4
- package/js/tooltip.js +14 -23
- package/js/tree.d.ts +0 -1
- package/js/tree.js +6 -11
- package/js/utils.js +7 -8
- package/js/virtual-dropdown.d.ts +1 -1
- package/js/virtual-dropdown.js +26 -28
- package/package.json +2 -1
- package/svg-icons/chevron_right.svg +1 -0
- package/svg-icons/keyboard_arrow_down.svg +1 -0
- package/css/calendar.css +0 -928
- package/css/guitar-chords.css +0 -251
- package/fonts/MaterialSymbolsOutlined.woff2 +0 -0
- package/js/form-builder.js +0 -107
- package/js/guitar-chords.js +0 -268
- package/js/lazy-loader.js +0 -121
- package/js/request.js +0 -51
package/js/timepicker.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ListenerGroup } from './listeners.js';
|
|
1
2
|
class TimeSpanPicker {
|
|
2
3
|
container;
|
|
3
4
|
startTimeInput;
|
|
@@ -13,11 +14,13 @@ class TimeSpanPicker {
|
|
|
13
14
|
startHandleEl;
|
|
14
15
|
endHandleEl;
|
|
15
16
|
dragState = null;
|
|
17
|
+
listeners = new ListenerGroup();
|
|
18
|
+
dragListeners = null;
|
|
16
19
|
constructor(elementOrSelector, options) {
|
|
17
20
|
const element = typeof elementOrSelector === 'string'
|
|
18
|
-
?
|
|
21
|
+
? elementOrSelector.startsWith('#') || elementOrSelector.startsWith('.')
|
|
19
22
|
? document.querySelector(elementOrSelector)
|
|
20
|
-
: document.getElementById(elementOrSelector)
|
|
23
|
+
: document.getElementById(elementOrSelector)
|
|
21
24
|
: elementOrSelector;
|
|
22
25
|
if (!element) {
|
|
23
26
|
throw new Error(`TimeSpanPicker: Element not found for "${elementOrSelector}"`);
|
|
@@ -74,17 +77,17 @@ class TimeSpanPicker {
|
|
|
74
77
|
</div>
|
|
75
78
|
`;
|
|
76
79
|
}
|
|
77
|
-
handleChange
|
|
80
|
+
handleChange() {
|
|
78
81
|
this.updateUI();
|
|
79
82
|
const start = this.startTimeInput.value;
|
|
80
83
|
const end = this.endTimeInput.value;
|
|
81
|
-
if (
|
|
82
|
-
this.onChange(start, end);
|
|
83
|
-
|
|
84
|
-
};
|
|
84
|
+
if (start && end)
|
|
85
|
+
this.onChange?.(start, end);
|
|
86
|
+
}
|
|
85
87
|
attachEventListeners() {
|
|
86
|
-
this.
|
|
87
|
-
this.
|
|
88
|
+
const sig = { signal: this.listeners.signal };
|
|
89
|
+
this.startTimeInput.addEventListener('change', () => this.handleChange(), sig);
|
|
90
|
+
this.endTimeInput.addEventListener('change', () => this.handleChange(), sig);
|
|
88
91
|
}
|
|
89
92
|
attachBarListeners() {
|
|
90
93
|
this.pickerEl = this.queryEl('.timespan-picker');
|
|
@@ -93,9 +96,10 @@ class TimeSpanPicker {
|
|
|
93
96
|
this.barFillEl = this.queryEl('.timespan-bar-fill');
|
|
94
97
|
this.startHandleEl = this.queryEl('.timespan-handle-start');
|
|
95
98
|
this.endHandleEl = this.queryEl('.timespan-handle-end');
|
|
96
|
-
this.
|
|
97
|
-
this.
|
|
98
|
-
this.
|
|
99
|
+
const sig = { signal: this.listeners.signal };
|
|
100
|
+
this.startHandleEl.addEventListener('pointerdown', (e) => this.onStartHandleDown(e), sig);
|
|
101
|
+
this.endHandleEl.addEventListener('pointerdown', (e) => this.onEndHandleDown(e), sig);
|
|
102
|
+
this.barFillEl.addEventListener('pointerdown', (e) => this.onFillDown(e), sig);
|
|
99
103
|
}
|
|
100
104
|
beginDrag(type, clickOffsetMins = 0, rect) {
|
|
101
105
|
const start = this.startTimeInput.value;
|
|
@@ -112,20 +116,22 @@ class TimeSpanPicker {
|
|
|
112
116
|
clickOffsetMins,
|
|
113
117
|
};
|
|
114
118
|
this.barEl.classList.add('is-dragging');
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
this.dragListeners = new ListenerGroup();
|
|
120
|
+
const dragSig = { signal: this.dragListeners.signal };
|
|
121
|
+
document.addEventListener('pointermove', (e) => this.onPointerMove(e), dragSig);
|
|
122
|
+
document.addEventListener('pointerup', () => this.onPointerUp(), dragSig);
|
|
117
123
|
}
|
|
118
|
-
onStartHandleDown
|
|
124
|
+
onStartHandleDown(e) {
|
|
119
125
|
e.stopPropagation();
|
|
120
126
|
e.preventDefault();
|
|
121
127
|
this.beginDrag('start');
|
|
122
|
-
}
|
|
123
|
-
onEndHandleDown
|
|
128
|
+
}
|
|
129
|
+
onEndHandleDown(e) {
|
|
124
130
|
e.stopPropagation();
|
|
125
131
|
e.preventDefault();
|
|
126
132
|
this.beginDrag('end');
|
|
127
|
-
}
|
|
128
|
-
onFillDown
|
|
133
|
+
}
|
|
134
|
+
onFillDown(e) {
|
|
129
135
|
if (e.target.classList.contains('timespan-handle'))
|
|
130
136
|
return;
|
|
131
137
|
e.preventDefault();
|
|
@@ -135,8 +141,8 @@ class TimeSpanPicker {
|
|
|
135
141
|
const rect = this.barEl.getBoundingClientRect();
|
|
136
142
|
const clickMins = ((e.clientX - rect.left) / rect.width) * 1440;
|
|
137
143
|
this.beginDrag('move', clickMins - this.toMinutes(start), rect);
|
|
138
|
-
}
|
|
139
|
-
onPointerMove
|
|
144
|
+
}
|
|
145
|
+
onPointerMove(e) {
|
|
140
146
|
if (!this.dragState)
|
|
141
147
|
return;
|
|
142
148
|
e.preventDefault();
|
|
@@ -162,17 +168,16 @@ class TimeSpanPicker {
|
|
|
162
168
|
this.endTimeInput.value = end;
|
|
163
169
|
}
|
|
164
170
|
this.updateUI();
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
onPointerUp = () => {
|
|
171
|
+
this.onChange?.(start, end);
|
|
172
|
+
}
|
|
173
|
+
onPointerUp() {
|
|
169
174
|
if (!this.dragState)
|
|
170
175
|
return;
|
|
171
176
|
this.dragState = null;
|
|
172
177
|
this.barEl.classList.remove('is-dragging');
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
178
|
+
this.dragListeners?.destroy();
|
|
179
|
+
this.dragListeners = null;
|
|
180
|
+
}
|
|
176
181
|
toMinutes(time) {
|
|
177
182
|
const [h, m] = time.split(':').map(Number);
|
|
178
183
|
return h * 60 + m;
|
|
@@ -225,7 +230,7 @@ class TimeSpanPicker {
|
|
|
225
230
|
getValue() {
|
|
226
231
|
return {
|
|
227
232
|
start: this.startTimeInput.value,
|
|
228
|
-
end: this.endTimeInput.value
|
|
233
|
+
end: this.endTimeInput.value,
|
|
229
234
|
};
|
|
230
235
|
}
|
|
231
236
|
setValue(start, end) {
|
|
@@ -248,13 +253,8 @@ class TimeSpanPicker {
|
|
|
248
253
|
return !!(start && end && start < end);
|
|
249
254
|
}
|
|
250
255
|
destroy() {
|
|
251
|
-
this.
|
|
252
|
-
this.
|
|
253
|
-
this.startHandleEl.removeEventListener('pointerdown', this.onStartHandleDown);
|
|
254
|
-
this.endHandleEl.removeEventListener('pointerdown', this.onEndHandleDown);
|
|
255
|
-
this.barFillEl.removeEventListener('pointerdown', this.onFillDown);
|
|
256
|
-
document.removeEventListener('pointermove', this.onPointerMove);
|
|
257
|
-
document.removeEventListener('pointerup', this.onPointerUp);
|
|
256
|
+
this.listeners.destroy();
|
|
257
|
+
this.dragListeners?.destroy();
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
export { TimeSpanPicker };
|
package/js/toast.d.ts
CHANGED
|
@@ -4,20 +4,23 @@ interface ToastOptions {
|
|
|
4
4
|
header?: string;
|
|
5
5
|
type?: ToastType;
|
|
6
6
|
closeable?: boolean;
|
|
7
|
+
iconBasePath?: string;
|
|
7
8
|
}
|
|
8
9
|
declare class Toast {
|
|
9
10
|
private readonly content;
|
|
10
11
|
private readonly header;
|
|
11
12
|
private readonly type?;
|
|
12
13
|
private readonly closeable;
|
|
14
|
+
private readonly iconBasePath;
|
|
13
15
|
private readonly closureIcon;
|
|
14
16
|
private readonly template;
|
|
15
17
|
private toastElement;
|
|
16
18
|
private timerId;
|
|
19
|
+
private openListeners;
|
|
17
20
|
constructor(options: ToastOptions);
|
|
18
21
|
constructor(content: string, header?: string, type?: ToastType, closeable?: boolean);
|
|
19
22
|
show(ms?: number): void;
|
|
20
|
-
hide
|
|
23
|
+
hide(): void;
|
|
21
24
|
private startTimer;
|
|
22
25
|
destroy(): void;
|
|
23
26
|
private buildTemplate;
|
package/js/toast.js
CHANGED
|
@@ -1,26 +1,32 @@
|
|
|
1
1
|
import { escapeHtml } from './utils.js';
|
|
2
|
+
import { ListenerGroup } from './listeners.js';
|
|
2
3
|
class Toast {
|
|
3
4
|
content;
|
|
4
5
|
header;
|
|
5
6
|
type;
|
|
6
7
|
closeable;
|
|
7
|
-
|
|
8
|
+
iconBasePath;
|
|
9
|
+
closureIcon;
|
|
8
10
|
template;
|
|
9
11
|
toastElement = null;
|
|
10
12
|
timerId = null;
|
|
13
|
+
openListeners = null;
|
|
11
14
|
constructor(contentOrOptions, header = '', type, closeable = true) {
|
|
12
15
|
if (typeof contentOrOptions === 'object') {
|
|
13
16
|
this.content = contentOrOptions.content;
|
|
14
17
|
this.header = contentOrOptions.header ?? '';
|
|
15
18
|
this.type = contentOrOptions.type;
|
|
16
19
|
this.closeable = contentOrOptions.closeable ?? true;
|
|
20
|
+
this.iconBasePath = contentOrOptions.iconBasePath ?? 'svg-icons/';
|
|
17
21
|
}
|
|
18
22
|
else {
|
|
19
23
|
this.content = contentOrOptions;
|
|
20
24
|
this.header = header;
|
|
21
25
|
this.type = type;
|
|
22
26
|
this.closeable = closeable;
|
|
27
|
+
this.iconBasePath = 'svg-icons/';
|
|
23
28
|
}
|
|
29
|
+
this.closureIcon = `<svg class="icon-svg close" aria-hidden="true"><use href="${this.iconBasePath}icons.svg#close"/></svg>`;
|
|
24
30
|
this.template = this.buildTemplate();
|
|
25
31
|
}
|
|
26
32
|
show(ms) {
|
|
@@ -37,7 +43,8 @@ class Toast {
|
|
|
37
43
|
this.toastElement?.classList.add('show');
|
|
38
44
|
const closeButton = this.toastElement?.querySelector('.close');
|
|
39
45
|
if (closeButton) {
|
|
40
|
-
|
|
46
|
+
this.openListeners = new ListenerGroup();
|
|
47
|
+
closeButton.addEventListener('click', () => this.hide(), { signal: this.openListeners.signal });
|
|
41
48
|
}
|
|
42
49
|
if (ms !== undefined && ms > 0) {
|
|
43
50
|
this.startTimer(ms);
|
|
@@ -45,18 +52,19 @@ class Toast {
|
|
|
45
52
|
});
|
|
46
53
|
});
|
|
47
54
|
}
|
|
48
|
-
hide
|
|
55
|
+
hide() {
|
|
49
56
|
if (this.timerId !== null) {
|
|
50
57
|
clearTimeout(this.timerId);
|
|
51
58
|
this.timerId = null;
|
|
52
59
|
}
|
|
53
60
|
this.toastElement?.classList.remove('show');
|
|
61
|
+
this.openListeners?.destroy();
|
|
62
|
+
this.openListeners = null;
|
|
54
63
|
setTimeout(() => {
|
|
55
|
-
this.toastElement?.querySelector('.close')?.removeEventListener('click', this.hide);
|
|
56
64
|
this.toastElement?.remove();
|
|
57
65
|
this.toastElement = null;
|
|
58
66
|
}, 150);
|
|
59
|
-
}
|
|
67
|
+
}
|
|
60
68
|
startTimer(ms, elapsed = 0) {
|
|
61
69
|
const stepSize = 250;
|
|
62
70
|
if (elapsed >= ms) {
|
package/js/tooltip.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ declare class Tooltip {
|
|
|
14
14
|
private tooltipElement;
|
|
15
15
|
private showTimeout;
|
|
16
16
|
private isVisible;
|
|
17
|
+
private listeners;
|
|
17
18
|
constructor(trigger: HTMLElement, content: string, options?: TooltipOptions);
|
|
18
19
|
static initializeAll(): void;
|
|
19
20
|
show(): void;
|
|
@@ -22,10 +23,6 @@ declare class Tooltip {
|
|
|
22
23
|
private createTooltip;
|
|
23
24
|
private position;
|
|
24
25
|
private attachEvents;
|
|
25
|
-
private handleMouseEnter;
|
|
26
|
-
private handleMouseLeave;
|
|
27
|
-
private handleFocus;
|
|
28
|
-
private handleBlur;
|
|
29
26
|
destroy(): void;
|
|
30
27
|
}
|
|
31
28
|
export { Tooltip };
|
package/js/tooltip.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { computePosition } from './position.js';
|
|
2
|
+
import { ListenerGroup } from './listeners.js';
|
|
2
3
|
class Tooltip {
|
|
3
4
|
static activeTooltip = null;
|
|
4
5
|
static idCounter = 0;
|
|
@@ -8,6 +9,7 @@ class Tooltip {
|
|
|
8
9
|
tooltipElement = null;
|
|
9
10
|
showTimeout = null;
|
|
10
11
|
isVisible = false;
|
|
12
|
+
listeners = new ListenerGroup();
|
|
11
13
|
constructor(trigger, content, options = {}) {
|
|
12
14
|
this.trigger = trigger;
|
|
13
15
|
this.content = content;
|
|
@@ -22,7 +24,7 @@ class Tooltip {
|
|
|
22
24
|
}
|
|
23
25
|
static initializeAll() {
|
|
24
26
|
const triggers = document.querySelectorAll('[data-tooltip]');
|
|
25
|
-
triggers.forEach(trigger => {
|
|
27
|
+
triggers.forEach((trigger) => {
|
|
26
28
|
const content = trigger.getAttribute('data-tooltip');
|
|
27
29
|
const position = trigger.getAttribute('data-tooltip-position') ?? 'auto';
|
|
28
30
|
const className = trigger.getAttribute('data-tooltip-class') ?? '';
|
|
@@ -31,7 +33,7 @@ class Tooltip {
|
|
|
31
33
|
}
|
|
32
34
|
});
|
|
33
35
|
const advancedTriggers = document.querySelectorAll('[data-tooltip-id]');
|
|
34
|
-
advancedTriggers.forEach(trigger => {
|
|
36
|
+
advancedTriggers.forEach((trigger) => {
|
|
35
37
|
const contentId = trigger.getAttribute('data-tooltip-id');
|
|
36
38
|
const position = trigger.getAttribute('data-tooltip-position') ?? 'auto';
|
|
37
39
|
const className = trigger.getAttribute('data-tooltip-class') ?? '';
|
|
@@ -107,35 +109,24 @@ class Tooltip {
|
|
|
107
109
|
position() {
|
|
108
110
|
if (!this.tooltipElement)
|
|
109
111
|
return;
|
|
110
|
-
const { left, top, placement } = computePosition(this.trigger.getBoundingClientRect(), this.tooltipElement.getBoundingClientRect(), {
|
|
112
|
+
const { left, top, placement } = computePosition(this.trigger.getBoundingClientRect(), this.tooltipElement.getBoundingClientRect(), {
|
|
113
|
+
placement: this.options.position,
|
|
114
|
+
offset: this.options.offset,
|
|
115
|
+
});
|
|
111
116
|
this.tooltipElement.style.left = `${left}px`;
|
|
112
117
|
this.tooltipElement.style.top = `${top}px`;
|
|
113
118
|
this.tooltipElement.setAttribute('data-position', placement);
|
|
114
119
|
}
|
|
115
120
|
attachEvents() {
|
|
116
|
-
this.
|
|
117
|
-
this.trigger.addEventListener('
|
|
118
|
-
this.trigger.addEventListener('
|
|
119
|
-
this.trigger.addEventListener('
|
|
121
|
+
const sig = { signal: this.listeners.signal };
|
|
122
|
+
this.trigger.addEventListener('mouseenter', () => this.show(), sig);
|
|
123
|
+
this.trigger.addEventListener('mouseleave', () => this.hide(), sig);
|
|
124
|
+
this.trigger.addEventListener('focus', () => this.show(), sig);
|
|
125
|
+
this.trigger.addEventListener('blur', () => this.hide(), sig);
|
|
120
126
|
}
|
|
121
|
-
handleMouseEnter = () => {
|
|
122
|
-
this.show();
|
|
123
|
-
};
|
|
124
|
-
handleMouseLeave = () => {
|
|
125
|
-
this.hide();
|
|
126
|
-
};
|
|
127
|
-
handleFocus = () => {
|
|
128
|
-
this.show();
|
|
129
|
-
};
|
|
130
|
-
handleBlur = () => {
|
|
131
|
-
this.hide();
|
|
132
|
-
};
|
|
133
127
|
destroy() {
|
|
134
128
|
this.hide();
|
|
135
|
-
this.
|
|
136
|
-
this.trigger.removeEventListener('mouseleave', this.handleMouseLeave);
|
|
137
|
-
this.trigger.removeEventListener('focus', this.handleFocus);
|
|
138
|
-
this.trigger.removeEventListener('blur', this.handleBlur);
|
|
129
|
+
this.listeners.destroy();
|
|
139
130
|
const previousDescribedBy = this.trigger.getAttribute('data-previous-describedby');
|
|
140
131
|
if (previousDescribedBy) {
|
|
141
132
|
this.trigger.setAttribute('aria-describedby', previousDescribedBy);
|
package/js/tree.d.ts
CHANGED
|
@@ -18,7 +18,6 @@ declare class TreeComponent {
|
|
|
18
18
|
private selectedNode;
|
|
19
19
|
private readonly options;
|
|
20
20
|
constructor(elementOrSelector: string | HTMLElement, data: TreeNode[], options?: TreeOptions);
|
|
21
|
-
private init;
|
|
22
21
|
render(): void;
|
|
23
22
|
private renderNode;
|
|
24
23
|
private createIconElement;
|
package/js/tree.js
CHANGED
|
@@ -22,9 +22,7 @@ class TreeComponent {
|
|
|
22
22
|
selectedNode;
|
|
23
23
|
options;
|
|
24
24
|
constructor(elementOrSelector, data, options = {}) {
|
|
25
|
-
const container = typeof elementOrSelector === 'string'
|
|
26
|
-
? document.querySelector(elementOrSelector)
|
|
27
|
-
: elementOrSelector;
|
|
25
|
+
const container = typeof elementOrSelector === 'string' ? document.querySelector(elementOrSelector) : elementOrSelector;
|
|
28
26
|
if (!container) {
|
|
29
27
|
throw new Error(`TreeComponent: Element not found for selector "${elementOrSelector}"`);
|
|
30
28
|
}
|
|
@@ -32,14 +30,11 @@ class TreeComponent {
|
|
|
32
30
|
this.data = data;
|
|
33
31
|
this.selectedNode = null;
|
|
34
32
|
this.options = options;
|
|
35
|
-
this.init();
|
|
36
|
-
}
|
|
37
|
-
init() {
|
|
38
33
|
this.render();
|
|
39
34
|
}
|
|
40
35
|
render() {
|
|
41
36
|
this.container.innerHTML = '';
|
|
42
|
-
this.data.forEach(node => {
|
|
37
|
+
this.data.forEach((node) => {
|
|
43
38
|
this.renderNode(node, this.container);
|
|
44
39
|
});
|
|
45
40
|
}
|
|
@@ -107,7 +102,7 @@ class TreeComponent {
|
|
|
107
102
|
childrenUl.classList.add('expanded');
|
|
108
103
|
childrenUl.style.height = 'auto';
|
|
109
104
|
}
|
|
110
|
-
node.children.forEach(child => {
|
|
105
|
+
node.children.forEach((child) => {
|
|
111
106
|
this.renderNode(child, childrenUl);
|
|
112
107
|
});
|
|
113
108
|
return childrenUl;
|
|
@@ -126,13 +121,13 @@ class TreeComponent {
|
|
|
126
121
|
}
|
|
127
122
|
}
|
|
128
123
|
expandChildren(container) {
|
|
129
|
-
container.style.height = container.scrollHeight
|
|
124
|
+
container.style.height = `${container.scrollHeight}px`;
|
|
130
125
|
container.addEventListener('transitionend', () => {
|
|
131
126
|
container.style.height = 'auto';
|
|
132
127
|
}, { once: true });
|
|
133
128
|
}
|
|
134
129
|
collapseChildren(container) {
|
|
135
|
-
container.style.height = container.offsetHeight
|
|
130
|
+
container.style.height = `${container.offsetHeight}px`;
|
|
136
131
|
requestAnimationFrame(() => {
|
|
137
132
|
container.style.height = '0';
|
|
138
133
|
});
|
|
@@ -172,7 +167,7 @@ class TreeComponent {
|
|
|
172
167
|
});
|
|
173
168
|
}
|
|
174
169
|
traverseNodes(nodes, callback) {
|
|
175
|
-
nodes.forEach(node => {
|
|
170
|
+
nodes.forEach((node) => {
|
|
176
171
|
callback(node);
|
|
177
172
|
if (node.children.length > 0) {
|
|
178
173
|
this.traverseNodes(node.children, callback);
|
package/js/utils.js
CHANGED
|
@@ -9,10 +9,10 @@ const utils = {
|
|
|
9
9
|
},
|
|
10
10
|
value(element) {
|
|
11
11
|
if (element.hasAttribute('value')) {
|
|
12
|
-
return element.getAttribute('value')
|
|
12
|
+
return element.getAttribute('value');
|
|
13
13
|
}
|
|
14
14
|
if (element.hasAttribute('data-value')) {
|
|
15
|
-
return element.getAttribute('data-value')
|
|
15
|
+
return element.getAttribute('data-value');
|
|
16
16
|
}
|
|
17
17
|
return element.innerText;
|
|
18
18
|
},
|
|
@@ -26,11 +26,11 @@ const utils = {
|
|
|
26
26
|
return undefined;
|
|
27
27
|
},
|
|
28
28
|
isList(element) {
|
|
29
|
-
return NodeList
|
|
29
|
+
return element instanceof NodeList;
|
|
30
30
|
},
|
|
31
31
|
isHidden(element) {
|
|
32
32
|
return element.offsetParent === null;
|
|
33
|
-
}
|
|
33
|
+
},
|
|
34
34
|
};
|
|
35
35
|
function escapeHtml(text) {
|
|
36
36
|
const div = document.createElement('div');
|
|
@@ -40,11 +40,10 @@ function escapeHtml(text) {
|
|
|
40
40
|
function sanitizeHtml(html) {
|
|
41
41
|
const parser = new DOMParser();
|
|
42
42
|
const doc = parser.parseFromString(html, 'text/html');
|
|
43
|
-
doc.querySelectorAll('script, style, iframe, object, embed').forEach(el => el.remove());
|
|
44
|
-
doc.querySelectorAll('*').forEach(el => {
|
|
43
|
+
doc.querySelectorAll('script, style, iframe, object, embed').forEach((el) => el.remove());
|
|
44
|
+
doc.querySelectorAll('*').forEach((el) => {
|
|
45
45
|
for (const attr of Array.from(el.attributes)) {
|
|
46
|
-
if (attr.name.startsWith('on') ||
|
|
47
|
-
attr.value.trim().toLowerCase().startsWith('javascript:')) {
|
|
46
|
+
if (attr.name.startsWith('on') || attr.value.trim().toLowerCase().startsWith('javascript:')) {
|
|
48
47
|
el.removeAttribute(attr.name);
|
|
49
48
|
}
|
|
50
49
|
}
|
package/js/virtual-dropdown.d.ts
CHANGED
package/js/virtual-dropdown.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { escapeHtml } from './utils.js';
|
|
2
|
+
import { ListenerGroup } from './listeners.js';
|
|
2
3
|
class VirtualDropdown {
|
|
3
4
|
container;
|
|
4
5
|
options;
|
|
@@ -21,11 +22,9 @@ class VirtualDropdown {
|
|
|
21
22
|
filteredOptions;
|
|
22
23
|
isOpen;
|
|
23
24
|
scrollTop;
|
|
24
|
-
|
|
25
|
+
listeners = new ListenerGroup();
|
|
25
26
|
constructor(config) {
|
|
26
|
-
const containerElement = typeof config.container === 'string'
|
|
27
|
-
? document.querySelector(config.container)
|
|
28
|
-
: config.container;
|
|
27
|
+
const containerElement = typeof config.container === 'string' ? document.querySelector(config.container) : config.container;
|
|
29
28
|
if (!containerElement) {
|
|
30
29
|
throw new Error('Container element not found');
|
|
31
30
|
}
|
|
@@ -34,8 +33,8 @@ class VirtualDropdown {
|
|
|
34
33
|
this.multiSelect = config.multiSelect ?? false;
|
|
35
34
|
this.searchable = config.searchable ?? false;
|
|
36
35
|
this.placeholder = config.placeholder || 'Select...';
|
|
37
|
-
this.renderLimit = config.renderLimit
|
|
38
|
-
this.itemHeight = config.itemHeight
|
|
36
|
+
this.renderLimit = config.renderLimit ?? 20;
|
|
37
|
+
this.itemHeight = config.itemHeight ?? 40;
|
|
39
38
|
this.onSelect = config.onSelect ?? null;
|
|
40
39
|
this.anchorName = `--vd-${Math.random().toString(36).slice(2, 9)}`;
|
|
41
40
|
this.selectedValues = new Set();
|
|
@@ -87,7 +86,7 @@ class VirtualDropdown {
|
|
|
87
86
|
return element;
|
|
88
87
|
}
|
|
89
88
|
bindEvents() {
|
|
90
|
-
const sig = { signal: this.
|
|
89
|
+
const sig = { signal: this.listeners.signal };
|
|
91
90
|
this.trigger.addEventListener('click', () => this.toggle(), sig);
|
|
92
91
|
this.trigger.addEventListener('keydown', (e) => {
|
|
93
92
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
@@ -122,7 +121,12 @@ class VirtualDropdown {
|
|
|
122
121
|
}, sig);
|
|
123
122
|
}
|
|
124
123
|
toggle() {
|
|
125
|
-
this.isOpen
|
|
124
|
+
if (this.isOpen) {
|
|
125
|
+
this.close();
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
this.open();
|
|
129
|
+
}
|
|
126
130
|
}
|
|
127
131
|
open() {
|
|
128
132
|
this.isOpen = true;
|
|
@@ -146,7 +150,7 @@ class VirtualDropdown {
|
|
|
146
150
|
}
|
|
147
151
|
else {
|
|
148
152
|
const lowerQuery = query.toLowerCase();
|
|
149
|
-
this.filteredOptions = this.options.filter(opt => opt.label.toLowerCase().includes(lowerQuery));
|
|
153
|
+
this.filteredOptions = this.options.filter((opt) => opt.label.toLowerCase().includes(lowerQuery));
|
|
150
154
|
}
|
|
151
155
|
this.listWrapper.scrollTop = 0;
|
|
152
156
|
this.scrollTop = 0;
|
|
@@ -180,29 +184,25 @@ class VirtualDropdown {
|
|
|
180
184
|
`;
|
|
181
185
|
})
|
|
182
186
|
.join('');
|
|
183
|
-
this.content.querySelectorAll('.dropdown-item').forEach(item => {
|
|
184
|
-
|
|
187
|
+
this.content.querySelectorAll('.dropdown-item').forEach((item) => {
|
|
188
|
+
item.addEventListener('click', (e) => {
|
|
185
189
|
e.stopPropagation();
|
|
186
190
|
const value = item.dataset.value;
|
|
187
|
-
if (value)
|
|
191
|
+
if (value)
|
|
188
192
|
this.handleSelect(value);
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const handleItemKeydown = (e) => {
|
|
193
|
+
});
|
|
194
|
+
item.addEventListener('keydown', (e) => {
|
|
192
195
|
if (e.key === 'Enter' || e.key === ' ') {
|
|
193
196
|
e.preventDefault();
|
|
194
197
|
const value = item.dataset.value;
|
|
195
|
-
if (value)
|
|
198
|
+
if (value)
|
|
196
199
|
this.handleSelect(value);
|
|
197
|
-
}
|
|
198
200
|
}
|
|
199
|
-
};
|
|
200
|
-
item.addEventListener('click', handleItemClick);
|
|
201
|
-
item.addEventListener('keydown', handleItemKeydown);
|
|
201
|
+
});
|
|
202
202
|
});
|
|
203
203
|
}
|
|
204
204
|
handleSelect(valueString) {
|
|
205
|
-
const selectedOpt = this.filteredOptions.find(o => String(o.value) === valueString);
|
|
205
|
+
const selectedOpt = this.filteredOptions.find((o) => String(o.value) === valueString);
|
|
206
206
|
if (!selectedOpt)
|
|
207
207
|
return;
|
|
208
208
|
const val = selectedOpt.value;
|
|
@@ -221,9 +221,7 @@ class VirtualDropdown {
|
|
|
221
221
|
this.close();
|
|
222
222
|
}
|
|
223
223
|
this.updateTrigger();
|
|
224
|
-
|
|
225
|
-
this.onSelect(Array.from(this.selectedValues));
|
|
226
|
-
}
|
|
224
|
+
this.onSelect?.(Array.from(this.selectedValues));
|
|
227
225
|
}
|
|
228
226
|
updateTrigger() {
|
|
229
227
|
if (this.selectedValues.size === 0) {
|
|
@@ -238,7 +236,7 @@ class VirtualDropdown {
|
|
|
238
236
|
}
|
|
239
237
|
else {
|
|
240
238
|
const val = Array.from(this.selectedValues)[0];
|
|
241
|
-
const opt = this.options.find(o => o.value === val);
|
|
239
|
+
const opt = this.options.find((o) => o.value === val);
|
|
242
240
|
this.triggerText.textContent = opt ? opt.label : String(val);
|
|
243
241
|
}
|
|
244
242
|
}
|
|
@@ -248,8 +246,8 @@ class VirtualDropdown {
|
|
|
248
246
|
}
|
|
249
247
|
setValue(values) {
|
|
250
248
|
this.selectedValues.clear();
|
|
251
|
-
values.forEach(val => {
|
|
252
|
-
if (this.options.some(opt => opt.value === val)) {
|
|
249
|
+
values.forEach((val) => {
|
|
250
|
+
if (this.options.some((opt) => opt.value === val)) {
|
|
253
251
|
this.selectedValues.add(val);
|
|
254
252
|
}
|
|
255
253
|
});
|
|
@@ -268,7 +266,7 @@ class VirtualDropdown {
|
|
|
268
266
|
destroy() {
|
|
269
267
|
if (this.isOpen)
|
|
270
268
|
this.menu.hidePopover();
|
|
271
|
-
this.
|
|
269
|
+
this.listeners.destroy();
|
|
272
270
|
this.container.innerHTML = '';
|
|
273
271
|
this.container.classList.remove('custom-dropdown', 'open');
|
|
274
272
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dodlhuat/basix",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Basix is intended as a starter for the rapid development of a design. Each design element can be added individually to include only the data required. It is using plain javascript / typescript and therefore is not dependent on any plugin.",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./css/*": "./css/*",
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"eslint": "^10.5.0",
|
|
37
37
|
"eslint-config-prettier": "^10.1.8",
|
|
38
38
|
"prettier": "^3.8.4",
|
|
39
|
+
"sass": "^1.100.0",
|
|
39
40
|
"typescript": "^6.0.0",
|
|
40
41
|
"typescript-eslint": "^8.61.0"
|
|
41
42
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M530-481 332-679l43-43 241 241-241 241-43-43 198-198Z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M480-344 240-584l43-43 197 197 197-197 43 43-240 240Z"/></svg>
|