@dodlhuat/basix 1.0.0 → 1.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/README.md +1 -1
- package/css/accordion.scss +31 -22
- package/css/alert.scss +79 -27
- package/css/button.scss +151 -102
- package/css/card.scss +11 -12
- package/css/carousel.scss +123 -87
- package/css/chart.scss +9 -11
- package/css/chat-bubbles.scss +2 -2
- package/css/checkbox.scss +72 -55
- package/css/chips.scss +52 -52
- package/css/code-viewer.scss +73 -98
- package/css/datepicker.scss +20 -0
- package/css/dropdown.scss +151 -137
- package/css/editor.scss +9 -6
- package/css/file-uploader.scss +187 -195
- package/css/flyout-menu.scss +20 -13
- package/css/form.scss +168 -115
- package/css/gallery.scss +62 -63
- package/css/grid.scss +0 -1
- package/css/modal.scss +117 -72
- package/css/placeholder.scss +17 -12
- package/css/properties.scss +6 -0
- package/css/push-menu.scss +70 -23
- package/css/radiobutton.scss +86 -64
- package/css/range-slider.scss +136 -0
- package/css/scrollbar.scss +69 -69
- package/css/spinner.scss +41 -66
- package/css/style.css +4351 -3735
- package/css/style.css.map +1 -1
- package/css/style.scss +2 -1
- package/css/switch.scss +43 -42
- package/css/table.scss +61 -40
- package/css/tabs.scss +12 -7
- package/css/timeline.scss +72 -69
- package/css/timepicker.scss +151 -72
- package/css/toast.scss +49 -48
- package/css/tooltip.scss +112 -122
- package/css/tree.scss +135 -192
- package/css/typography.scss +70 -9
- package/css/virtual-dropdown.scss +201 -142
- package/js/carousel.js +45 -18
- package/js/carousel.ts +217 -173
- package/js/datepicker.js +505 -497
- package/js/datepicker.ts +9 -0
- package/js/editor.js +398 -415
- package/js/file-uploader.js +142 -128
- package/js/file-uploader.ts +364 -350
- package/js/gallery.js +22 -15
- package/js/gallery.ts +17 -12
- package/js/index.js +718 -720
- package/js/index.ts +7 -8
- package/js/push-menu.js +113 -101
- package/js/push-menu.ts +17 -2
- package/js/range-slider.js +26 -0
- package/js/range-slider.ts +33 -0
- package/js/timepicker.js +144 -98
- package/js/timepicker.ts +194 -131
- package/js/tree.js +56 -28
- package/js/tree.ts +239 -218
- package/package.json +1 -1
- package/css/accordion.css +0 -109
- package/css/accordion.css.map +0 -1
- package/css/alert.css +0 -57
- package/css/alert.css.map +0 -1
- package/css/button.css +0 -69
- package/css/button.css.map +0 -1
- package/css/card.css +0 -144
- package/css/card.css.map +0 -1
- package/css/carousel.css +0 -118
- package/css/carousel.css.map +0 -1
- package/css/chart.css +0 -159
- package/css/chart.css.map +0 -1
- package/css/chat-bubbles.css +0 -97
- package/css/chat-bubbles.css.map +0 -1
- package/css/checkbox.css +0 -77
- package/css/checkbox.css.map +0 -1
- package/css/chips.css +0 -72
- package/css/chips.css.map +0 -1
- package/css/code-viewer.css +0 -97
- package/css/code-viewer.css.map +0 -1
- package/css/colors.css +0 -63
- package/css/colors.css.map +0 -1
- package/css/datepicker.css +0 -264
- package/css/datepicker.css.map +0 -1
- package/css/defaults.css +0 -118
- package/css/defaults.css.map +0 -1
- package/css/dropdown.css +0 -146
- package/css/dropdown.css.map +0 -1
- package/css/editor.css +0 -413
- package/css/file-uploader.css +0 -194
- package/css/file-uploader.css.map +0 -1
- package/css/flyout-menu.css +0 -345
- package/css/flyout-menu.css.map +0 -1
- package/css/form-builder.css +0 -9
- package/css/form-builder.css.map +0 -1
- package/css/form-builder.scss +0 -11
- package/css/form.css +0 -130
- package/css/form.css.map +0 -1
- package/css/gallery.css +0 -91
- package/css/gallery.css.map +0 -1
- package/css/grid.css +0 -44
- package/css/grid.css.map +0 -1
- package/css/icons.css +0 -327
- package/css/icons.css.map +0 -1
- package/css/modal.css +0 -97
- package/css/modal.css.map +0 -1
- package/css/parameters.css +0 -1
- package/css/parameters.css.map +0 -1
- package/css/placeholder.css +0 -50
- package/css/placeholder.css.map +0 -1
- package/css/progress.css +0 -51
- package/css/progress.css.map +0 -1
- package/css/properties.css +0 -31
- package/css/properties.css.map +0 -1
- package/css/push-menu.css +0 -145
- package/css/push-menu.css.map +0 -1
- package/css/radiobutton.css +0 -91
- package/css/radiobutton.css.map +0 -1
- package/css/reset.css +0 -46
- package/css/reset.css.map +0 -1
- package/css/scrollbar.css +0 -91
- package/css/scrollbar.css.map +0 -1
- package/css/spinner.css +0 -118
- package/css/spinner.css.map +0 -1
- package/css/switch.css +0 -66
- package/css/switch.css.map +0 -1
- package/css/table.css +0 -201
- package/css/table.css.map +0 -1
- package/css/tabs.css +0 -135
- package/css/tabs.css.map +0 -1
- package/css/timeline.css +0 -69
- package/css/timeline.css.map +0 -1
- package/css/toast.css +0 -98
- package/css/toast.css.map +0 -1
- package/css/tooltip.css +0 -151
- package/css/tooltip.css.map +0 -1
- package/css/tree.css +0 -199
- package/css/tree.css.map +0 -1
- package/css/typography.css +0 -137
- package/css/typography.css.map +0 -1
- package/css/virtual-dropdown.css +0 -149
- package/css/virtual-dropdown.css.map +0 -1
package/js/timepicker.ts
CHANGED
|
@@ -1,131 +1,194 @@
|
|
|
1
|
-
interface TimeSpan {
|
|
2
|
-
start: string;
|
|
3
|
-
end: string;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
interface TimeSpanPickerOptions {
|
|
7
|
-
onChange?: (start: string, end: string) => void;
|
|
8
|
-
defaultStart?: string;
|
|
9
|
-
defaultEnd?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
class TimeSpanPicker {
|
|
13
|
-
private container: HTMLElement;
|
|
14
|
-
private startTimeInput: HTMLInputElement;
|
|
15
|
-
private endTimeInput: HTMLInputElement;
|
|
16
|
-
private onChange?: (start: string, end: string) => void;
|
|
17
|
-
|
|
18
|
-
constructor(containerId: string, options?: TimeSpanPickerOptions) {
|
|
19
|
-
const element = document.getElementById(containerId);
|
|
20
|
-
if (!element) {
|
|
21
|
-
throw new Error(`Container with id "${containerId}" not found`);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
this.container = element;
|
|
25
|
-
this.onChange = options?.onChange;
|
|
26
|
-
|
|
27
|
-
this.render();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
this.
|
|
109
|
-
|
|
110
|
-
this.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
1
|
+
interface TimeSpan {
|
|
2
|
+
start: string;
|
|
3
|
+
end: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface TimeSpanPickerOptions {
|
|
7
|
+
onChange?: (start: string, end: string) => void;
|
|
8
|
+
defaultStart?: string;
|
|
9
|
+
defaultEnd?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
class TimeSpanPicker {
|
|
13
|
+
private container: HTMLElement;
|
|
14
|
+
private startTimeInput: HTMLInputElement;
|
|
15
|
+
private endTimeInput: HTMLInputElement;
|
|
16
|
+
private onChange?: (start: string, end: string) => void;
|
|
17
|
+
|
|
18
|
+
constructor(containerId: string, options?: TimeSpanPickerOptions) {
|
|
19
|
+
const element = document.getElementById(containerId);
|
|
20
|
+
if (!element) {
|
|
21
|
+
throw new Error(`Container with id "${containerId}" not found`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.container = element;
|
|
25
|
+
this.onChange = options?.onChange;
|
|
26
|
+
|
|
27
|
+
this.render();
|
|
28
|
+
|
|
29
|
+
this.startTimeInput = this.queryInput('.timespan-start');
|
|
30
|
+
this.endTimeInput = this.queryInput('.timespan-end');
|
|
31
|
+
|
|
32
|
+
if (options?.defaultStart) {
|
|
33
|
+
this.startTimeInput.value = options.defaultStart;
|
|
34
|
+
}
|
|
35
|
+
if (options?.defaultEnd) {
|
|
36
|
+
this.endTimeInput.value = options.defaultEnd;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.attachEventListeners();
|
|
40
|
+
|
|
41
|
+
// Render initial state if defaults provided
|
|
42
|
+
if (options?.defaultStart || options?.defaultEnd) {
|
|
43
|
+
this.updateUI();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private queryInput(selector: string): HTMLInputElement {
|
|
48
|
+
const input = this.container.querySelector<HTMLInputElement>(selector);
|
|
49
|
+
if (!input) {
|
|
50
|
+
throw new Error(`Input with selector "${selector}" not found`);
|
|
51
|
+
}
|
|
52
|
+
return input;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private render(): void {
|
|
56
|
+
this.container.innerHTML = `
|
|
57
|
+
<div class="timespan-picker">
|
|
58
|
+
<div class="timespan-field timespan-field-start">
|
|
59
|
+
<label for="timespan-start">From</label>
|
|
60
|
+
<input
|
|
61
|
+
type="time"
|
|
62
|
+
class="timespan-start"
|
|
63
|
+
id="timespan-start"
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div class="timespan-center">
|
|
68
|
+
<span class="timespan-arrow">→</span>
|
|
69
|
+
<span class="timespan-duration"></span>
|
|
70
|
+
</div>
|
|
71
|
+
|
|
72
|
+
<div class="timespan-field timespan-field-end">
|
|
73
|
+
<label for="timespan-end">To</label>
|
|
74
|
+
<input
|
|
75
|
+
type="time"
|
|
76
|
+
class="timespan-end"
|
|
77
|
+
id="timespan-end"
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="timespan-bar" aria-hidden="true">
|
|
82
|
+
<div class="timespan-bar-fill"></div>
|
|
83
|
+
</div>
|
|
84
|
+
`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private attachEventListeners(): void {
|
|
88
|
+
this.startTimeInput.addEventListener('change', () => this.handleChange());
|
|
89
|
+
this.endTimeInput.addEventListener('change', () => this.handleChange());
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private toMinutes(time: string): number {
|
|
93
|
+
const [h, m] = time.split(':').map(Number);
|
|
94
|
+
return h * 60 + m;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private formatDuration(minutes: number): string {
|
|
98
|
+
const h = Math.floor(minutes / 60);
|
|
99
|
+
const m = minutes % 60;
|
|
100
|
+
if (h && m) return `${h}h ${m}m`;
|
|
101
|
+
if (h) return `${h}h`;
|
|
102
|
+
return `${m}m`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private updateUI(): void {
|
|
106
|
+
const picker = this.container.querySelector<HTMLElement>('.timespan-picker');
|
|
107
|
+
const durationEl = this.container.querySelector<HTMLElement>('.timespan-duration');
|
|
108
|
+
const barFill = this.container.querySelector<HTMLElement>('.timespan-bar-fill');
|
|
109
|
+
|
|
110
|
+
const start = this.startTimeInput.value;
|
|
111
|
+
const end = this.endTimeInput.value;
|
|
112
|
+
const isError = !!(start && end && start >= end);
|
|
113
|
+
|
|
114
|
+
picker?.classList.toggle('is-error', isError);
|
|
115
|
+
|
|
116
|
+
if (isError) {
|
|
117
|
+
this.endTimeInput.setCustomValidity('End time must be after start time');
|
|
118
|
+
if (durationEl) durationEl.textContent = '!';
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
this.endTimeInput.setCustomValidity('');
|
|
123
|
+
|
|
124
|
+
if (start && end && durationEl && barFill) {
|
|
125
|
+
const startMins = this.toMinutes(start);
|
|
126
|
+
const endMins = this.toMinutes(end);
|
|
127
|
+
const duration = endMins - startMins;
|
|
128
|
+
|
|
129
|
+
durationEl.textContent = this.formatDuration(duration);
|
|
130
|
+
|
|
131
|
+
const startPct = ((startMins / 1440) * 100).toFixed(2);
|
|
132
|
+
const widthPct = ((duration / 1440) * 100).toFixed(2);
|
|
133
|
+
barFill.style.left = `${startPct}%`;
|
|
134
|
+
barFill.style.width = `${widthPct}%`;
|
|
135
|
+
} else {
|
|
136
|
+
if (durationEl) durationEl.textContent = '';
|
|
137
|
+
if (barFill) {
|
|
138
|
+
barFill.style.left = '0';
|
|
139
|
+
barFill.style.width = '0';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private handleChange(): void {
|
|
145
|
+
this.updateUI();
|
|
146
|
+
|
|
147
|
+
const { start, end } = this.getValue();
|
|
148
|
+
if (this.onChange && start && end) {
|
|
149
|
+
this.onChange(start, end);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
public getValue(): TimeSpan {
|
|
154
|
+
return {
|
|
155
|
+
start: this.startTimeInput.value,
|
|
156
|
+
end: this.endTimeInput.value
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public setValue(start: string, end: string): void {
|
|
161
|
+
this.startTimeInput.value = start;
|
|
162
|
+
this.endTimeInput.value = end;
|
|
163
|
+
this.handleChange();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
public reset(): void {
|
|
167
|
+
this.startTimeInput.value = '';
|
|
168
|
+
this.endTimeInput.value = '';
|
|
169
|
+
this.endTimeInput.setCustomValidity('');
|
|
170
|
+
|
|
171
|
+
const picker = this.container.querySelector('.timespan-picker');
|
|
172
|
+
const durationEl = this.container.querySelector<HTMLElement>('.timespan-duration');
|
|
173
|
+
const barFill = this.container.querySelector<HTMLElement>('.timespan-bar-fill');
|
|
174
|
+
|
|
175
|
+
picker?.classList.remove('is-error');
|
|
176
|
+
if (durationEl) durationEl.textContent = '';
|
|
177
|
+
if (barFill) { barFill.style.left = '0'; barFill.style.width = '0'; }
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
public isValid(): boolean {
|
|
181
|
+
const { start, end } = this.getValue();
|
|
182
|
+
return !!(start && end && start < end);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public destroy(): void {
|
|
186
|
+
const startHandler = () => this.handleChange();
|
|
187
|
+
const endHandler = () => this.handleChange();
|
|
188
|
+
this.startTimeInput.removeEventListener('change', startHandler);
|
|
189
|
+
this.endTimeInput.removeEventListener('change', endHandler);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export { TimeSpanPicker };
|
|
194
|
+
export type { TimeSpan, TimeSpanPickerOptions };
|
package/js/tree.js
CHANGED
|
@@ -10,7 +10,7 @@ class TreeNode {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
class TreeComponent {
|
|
13
|
-
constructor(elementOrSelector, data) {
|
|
13
|
+
constructor(elementOrSelector, data, options = {}) {
|
|
14
14
|
const container = typeof elementOrSelector === 'string'
|
|
15
15
|
? document.querySelector(elementOrSelector)
|
|
16
16
|
: elementOrSelector;
|
|
@@ -20,6 +20,7 @@ class TreeComponent {
|
|
|
20
20
|
this.container = container;
|
|
21
21
|
this.data = data;
|
|
22
22
|
this.selectedNode = null;
|
|
23
|
+
this.options = options;
|
|
23
24
|
this.init();
|
|
24
25
|
}
|
|
25
26
|
init() {
|
|
@@ -36,12 +37,10 @@ class TreeComponent {
|
|
|
36
37
|
li.className = 'tree-node';
|
|
37
38
|
const itemDiv = document.createElement('div');
|
|
38
39
|
itemDiv.className = `tree-item ${node.type}`;
|
|
39
|
-
if (node.selected)
|
|
40
|
+
if (node.selected)
|
|
40
41
|
itemDiv.classList.add('selected');
|
|
41
|
-
|
|
42
|
-
if (node.expanded) {
|
|
42
|
+
if (node.expanded)
|
|
43
43
|
itemDiv.classList.add('expanded');
|
|
44
|
-
}
|
|
45
44
|
const iconDiv = this.createIconElement(node.type);
|
|
46
45
|
const labelSpan = this.createLabelElement(node.label);
|
|
47
46
|
itemDiv.append(iconDiv, labelSpan);
|
|
@@ -68,18 +67,18 @@ class TreeComponent {
|
|
|
68
67
|
const iconDiv = document.createElement('div');
|
|
69
68
|
iconDiv.className = 'tree-icon';
|
|
70
69
|
if (type === 'folder') {
|
|
71
|
-
iconDiv.innerHTML = `
|
|
72
|
-
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
73
|
-
<path d="M9 18l6-6-6-6" stroke-linecap="round" stroke-linejoin="round"/>
|
|
74
|
-
</svg>
|
|
70
|
+
iconDiv.innerHTML = `
|
|
71
|
+
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
72
|
+
<path d="M9 18l6-6-6-6" stroke-linecap="round" stroke-linejoin="round"/>
|
|
73
|
+
</svg>
|
|
75
74
|
`;
|
|
76
75
|
}
|
|
77
76
|
else {
|
|
78
|
-
iconDiv.innerHTML = `
|
|
79
|
-
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
80
|
-
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z" stroke-linecap="round" stroke-linejoin="round"/>
|
|
81
|
-
<path d="M13 2v7h7" stroke-linecap="round" stroke-linejoin="round"/>
|
|
82
|
-
</svg>
|
|
77
|
+
iconDiv.innerHTML = `
|
|
78
|
+
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
79
|
+
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z" stroke-linecap="round" stroke-linejoin="round"/>
|
|
80
|
+
<path d="M13 2v7h7" stroke-linecap="round" stroke-linejoin="round"/>
|
|
81
|
+
</svg>
|
|
83
82
|
`;
|
|
84
83
|
}
|
|
85
84
|
return iconDiv;
|
|
@@ -95,6 +94,7 @@ class TreeComponent {
|
|
|
95
94
|
childrenUl.className = 'tree-children';
|
|
96
95
|
if (node.expanded) {
|
|
97
96
|
childrenUl.classList.add('expanded');
|
|
97
|
+
childrenUl.style.height = 'auto';
|
|
98
98
|
}
|
|
99
99
|
node.children.forEach(child => {
|
|
100
100
|
this.renderNode(child, childrenUl);
|
|
@@ -103,13 +103,29 @@ class TreeComponent {
|
|
|
103
103
|
}
|
|
104
104
|
toggleNode(node) {
|
|
105
105
|
node.expanded = !node.expanded;
|
|
106
|
-
|
|
107
|
-
node.element.classList.toggle('expanded', node.expanded);
|
|
108
|
-
}
|
|
106
|
+
node.element?.classList.toggle('expanded', node.expanded);
|
|
109
107
|
if (node.childrenContainer) {
|
|
108
|
+
if (node.expanded) {
|
|
109
|
+
this.expandChildren(node.childrenContainer);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.collapseChildren(node.childrenContainer);
|
|
113
|
+
}
|
|
110
114
|
node.childrenContainer.classList.toggle('expanded', node.expanded);
|
|
111
115
|
}
|
|
112
116
|
}
|
|
117
|
+
expandChildren(container) {
|
|
118
|
+
container.style.height = container.scrollHeight + 'px';
|
|
119
|
+
container.addEventListener('transitionend', () => {
|
|
120
|
+
container.style.height = 'auto';
|
|
121
|
+
}, { once: true });
|
|
122
|
+
}
|
|
123
|
+
collapseChildren(container) {
|
|
124
|
+
container.style.height = container.offsetHeight + 'px';
|
|
125
|
+
requestAnimationFrame(() => {
|
|
126
|
+
container.style.height = '0';
|
|
127
|
+
});
|
|
128
|
+
}
|
|
113
129
|
selectNode(node) {
|
|
114
130
|
if (this.selectedNode?.element) {
|
|
115
131
|
this.selectedNode.element.classList.remove('selected');
|
|
@@ -118,23 +134,29 @@ class TreeComponent {
|
|
|
118
134
|
node.selected = true;
|
|
119
135
|
node.element?.classList.add('selected');
|
|
120
136
|
this.selectedNode = node;
|
|
121
|
-
|
|
137
|
+
this.options.onSelect?.(node);
|
|
138
|
+
this.container.dispatchEvent(new CustomEvent('tree-select', {
|
|
139
|
+
detail: { node },
|
|
140
|
+
bubbles: true,
|
|
141
|
+
}));
|
|
122
142
|
}
|
|
123
143
|
expandAll() {
|
|
124
144
|
this.traverseNodes(this.data, (node) => {
|
|
125
|
-
if (node.type === 'folder') {
|
|
145
|
+
if (node.type === 'folder' && node.childrenContainer) {
|
|
126
146
|
node.expanded = true;
|
|
127
147
|
node.element?.classList.add('expanded');
|
|
128
|
-
node.childrenContainer
|
|
148
|
+
node.childrenContainer.classList.add('expanded');
|
|
149
|
+
node.childrenContainer.style.height = 'auto';
|
|
129
150
|
}
|
|
130
151
|
});
|
|
131
152
|
}
|
|
132
153
|
collapseAll() {
|
|
133
154
|
this.traverseNodes(this.data, (node) => {
|
|
134
|
-
if (node.type === 'folder') {
|
|
155
|
+
if (node.type === 'folder' && node.childrenContainer) {
|
|
135
156
|
node.expanded = false;
|
|
136
157
|
node.element?.classList.remove('expanded');
|
|
137
|
-
node.childrenContainer
|
|
158
|
+
node.childrenContainer.classList.remove('expanded');
|
|
159
|
+
node.childrenContainer.style.height = '0';
|
|
138
160
|
}
|
|
139
161
|
});
|
|
140
162
|
}
|
|
@@ -150,13 +172,19 @@ class TreeComponent {
|
|
|
150
172
|
return this.selectedNode;
|
|
151
173
|
}
|
|
152
174
|
findNodeByLabel(label) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
175
|
+
return this.findNode(this.data, label);
|
|
176
|
+
}
|
|
177
|
+
findNode(nodes, label) {
|
|
178
|
+
for (const node of nodes) {
|
|
179
|
+
if (node.label === label)
|
|
180
|
+
return node;
|
|
181
|
+
if (node.children.length > 0) {
|
|
182
|
+
const found = this.findNode(node.children, label);
|
|
183
|
+
if (found)
|
|
184
|
+
return found;
|
|
157
185
|
}
|
|
158
|
-
}
|
|
159
|
-
return
|
|
186
|
+
}
|
|
187
|
+
return null;
|
|
160
188
|
}
|
|
161
189
|
}
|
|
162
190
|
export { TreeComponent, TreeNode };
|