@forcecalendar/interface 1.0.26 → 1.0.28
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 +9 -0
- package/dist/force-calendar-interface.esm.js +217 -112
- package/dist/force-calendar-interface.esm.js.map +1 -1
- package/dist/force-calendar-interface.umd.js +11 -11
- package/dist/force-calendar-interface.umd.js.map +1 -1
- package/package.json +3 -1
- package/src/components/EventForm.js +180 -176
- package/src/components/ForceCalendar.js +414 -392
- package/src/core/BaseComponent.js +146 -144
- package/src/core/EventBus.js +197 -197
- package/src/core/StateManager.js +405 -399
- package/src/index.js +3 -3
- package/src/renderers/BaseViewRenderer.js +195 -192
- package/src/renderers/DayViewRenderer.js +133 -118
- package/src/renderers/MonthViewRenderer.js +74 -72
- package/src/renderers/WeekViewRenderer.js +118 -96
- package/src/utils/DOMUtils.js +277 -277
- package/src/utils/DateUtils.js +164 -164
- package/src/utils/StyleUtils.js +286 -249
package/src/utils/DOMUtils.js
CHANGED
|
@@ -3,291 +3,291 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
export class DOMUtils {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Create element with attributes and children
|
|
8
|
+
*/
|
|
9
|
+
static createElement(tag, attributes = {}, children = []) {
|
|
10
|
+
const element = document.createElement(tag);
|
|
11
|
+
|
|
12
|
+
// Set attributes
|
|
13
|
+
Object.entries(attributes).forEach(([key, value]) => {
|
|
14
|
+
if (key === 'className') {
|
|
15
|
+
element.className = value;
|
|
16
|
+
} else if (key === 'style' && typeof value === 'object') {
|
|
17
|
+
Object.assign(element.style, value);
|
|
18
|
+
} else if (key.startsWith('data-')) {
|
|
19
|
+
element.setAttribute(key, value);
|
|
20
|
+
} else if (key.startsWith('on') && typeof value === 'function') {
|
|
21
|
+
const eventName = key.slice(2).toLowerCase();
|
|
22
|
+
element.addEventListener(eventName, value);
|
|
23
|
+
} else {
|
|
24
|
+
element[key] = value;
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Add children
|
|
29
|
+
children.forEach(child => {
|
|
30
|
+
if (typeof child === 'string') {
|
|
31
|
+
element.appendChild(document.createTextNode(child));
|
|
32
|
+
} else if (child instanceof Node) {
|
|
33
|
+
element.appendChild(child);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return element;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Add multiple event listeners
|
|
42
|
+
*/
|
|
43
|
+
static addEventListeners(element, events) {
|
|
44
|
+
Object.entries(events).forEach(([event, handler]) => {
|
|
45
|
+
element.addEventListener(event, handler);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Return cleanup function
|
|
49
|
+
return () => {
|
|
50
|
+
Object.entries(events).forEach(([event, handler]) => {
|
|
51
|
+
element.removeEventListener(event, handler);
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Delegate event handling
|
|
58
|
+
*/
|
|
59
|
+
static delegate(element, selector, event, handler) {
|
|
60
|
+
const delegatedHandler = e => {
|
|
61
|
+
const target = e.target.closest(selector);
|
|
62
|
+
if (target && element.contains(target)) {
|
|
63
|
+
handler.call(target, e);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
element.addEventListener(event, delegatedHandler);
|
|
68
|
+
return () => element.removeEventListener(event, delegatedHandler);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get element position relative to viewport
|
|
73
|
+
*/
|
|
74
|
+
static getPosition(element) {
|
|
75
|
+
const rect = element.getBoundingClientRect();
|
|
76
|
+
return {
|
|
77
|
+
top: rect.top + window.scrollY,
|
|
78
|
+
left: rect.left + window.scrollX,
|
|
79
|
+
bottom: rect.bottom + window.scrollY,
|
|
80
|
+
right: rect.right + window.scrollX,
|
|
81
|
+
width: rect.width,
|
|
82
|
+
height: rect.height
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check if element is in viewport
|
|
88
|
+
*/
|
|
89
|
+
static isInViewport(element, threshold = 0) {
|
|
90
|
+
const rect = element.getBoundingClientRect();
|
|
91
|
+
return (
|
|
92
|
+
rect.top >= -threshold &&
|
|
93
|
+
rect.left >= -threshold &&
|
|
94
|
+
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) + threshold &&
|
|
95
|
+
rect.right <= (window.innerWidth || document.documentElement.clientWidth) + threshold
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Smooth scroll to element
|
|
101
|
+
*/
|
|
102
|
+
static scrollToElement(element, options = {}) {
|
|
103
|
+
const { behavior = 'smooth', block = 'start', inline = 'nearest' } = options;
|
|
104
|
+
element.scrollIntoView({ behavior, block, inline });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get computed style value
|
|
109
|
+
*/
|
|
110
|
+
static getStyle(element, property) {
|
|
111
|
+
return window.getComputedStyle(element).getPropertyValue(property);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Set multiple styles
|
|
116
|
+
*/
|
|
117
|
+
static setStyles(element, styles) {
|
|
118
|
+
Object.assign(element.style, styles);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Add/remove classes with animation support
|
|
123
|
+
*/
|
|
124
|
+
static async animateClass(element, className, duration = 300) {
|
|
125
|
+
element.classList.add(className);
|
|
126
|
+
await this.wait(duration);
|
|
127
|
+
element.classList.remove(className);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Wait for animation/transition to complete
|
|
132
|
+
*/
|
|
133
|
+
static waitForAnimation(element, eventType = 'animationend') {
|
|
134
|
+
return new Promise(resolve => {
|
|
135
|
+
const handler = () => {
|
|
136
|
+
element.removeEventListener(eventType, handler);
|
|
137
|
+
resolve();
|
|
138
|
+
};
|
|
139
|
+
element.addEventListener(eventType, handler);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Utility wait function
|
|
145
|
+
*/
|
|
146
|
+
static wait(ms) {
|
|
147
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Parse HTML string safely
|
|
152
|
+
*/
|
|
153
|
+
static parseHTML(htmlString) {
|
|
154
|
+
const template = document.createElement('template');
|
|
155
|
+
template.innerHTML = htmlString.trim();
|
|
156
|
+
return template.content.firstChild;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Escape HTML to prevent XSS
|
|
161
|
+
*/
|
|
162
|
+
static escapeHTML(str) {
|
|
163
|
+
const div = document.createElement('div');
|
|
164
|
+
div.textContent = str;
|
|
165
|
+
return div.innerHTML;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Debounce function calls
|
|
170
|
+
*/
|
|
171
|
+
static debounce(func, wait = 250) {
|
|
172
|
+
let timeout;
|
|
173
|
+
return function executedFunction(...args) {
|
|
174
|
+
const later = () => {
|
|
175
|
+
clearTimeout(timeout);
|
|
176
|
+
func(...args);
|
|
177
|
+
};
|
|
178
|
+
clearTimeout(timeout);
|
|
179
|
+
timeout = setTimeout(later, wait);
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Throttle function calls
|
|
185
|
+
*/
|
|
186
|
+
static throttle(func, limit = 250) {
|
|
187
|
+
let inThrottle;
|
|
188
|
+
return function (...args) {
|
|
189
|
+
if (!inThrottle) {
|
|
190
|
+
func.apply(this, args);
|
|
191
|
+
inThrottle = true;
|
|
192
|
+
setTimeout(() => (inThrottle = false), limit);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get closest parent matching selector
|
|
199
|
+
*/
|
|
200
|
+
static closest(element, selector) {
|
|
201
|
+
return element.closest(selector);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Get all parents matching selector
|
|
206
|
+
*/
|
|
207
|
+
static parents(element, selector) {
|
|
208
|
+
const parents = [];
|
|
209
|
+
let parent = element.parentElement;
|
|
210
|
+
|
|
211
|
+
while (parent) {
|
|
212
|
+
if (parent.matches(selector)) {
|
|
213
|
+
parents.push(parent);
|
|
214
|
+
}
|
|
215
|
+
parent = parent.parentElement;
|
|
38
216
|
}
|
|
39
217
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
218
|
+
return parents;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Measure element dimensions including margins
|
|
223
|
+
*/
|
|
224
|
+
static getOuterDimensions(element) {
|
|
225
|
+
const styles = window.getComputedStyle(element);
|
|
226
|
+
const margin = {
|
|
227
|
+
top: parseInt(styles.marginTop),
|
|
228
|
+
right: parseInt(styles.marginRight),
|
|
229
|
+
bottom: parseInt(styles.marginBottom),
|
|
230
|
+
left: parseInt(styles.marginLeft)
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
width: element.offsetWidth + margin.left + margin.right,
|
|
235
|
+
height: element.offsetHeight + margin.top + margin.bottom,
|
|
236
|
+
margin
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Clone element with event listeners
|
|
242
|
+
*/
|
|
243
|
+
static cloneWithEvents(element, deep = true) {
|
|
244
|
+
const clone = element.cloneNode(deep);
|
|
245
|
+
|
|
246
|
+
// Copy event listeners (Note: This is a simplified version)
|
|
247
|
+
// In production, you'd need a more robust event copying mechanism
|
|
248
|
+
return clone;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Focus trap for modals/dialogs
|
|
253
|
+
*/
|
|
254
|
+
static trapFocus(container) {
|
|
255
|
+
const focusableElements = container.querySelectorAll(
|
|
256
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
257
|
+
);
|
|
258
|
+
|
|
259
|
+
// Handle case where there are no focusable elements
|
|
260
|
+
if (focusableElements.length === 0) {
|
|
261
|
+
// Make container focusable as fallback
|
|
262
|
+
container.setAttribute('tabindex', '-1');
|
|
263
|
+
container.focus();
|
|
264
|
+
return () => container.removeAttribute('tabindex');
|
|
54
265
|
}
|
|
55
266
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
*/
|
|
59
|
-
static delegate(element, selector, event, handler) {
|
|
60
|
-
const delegatedHandler = (e) => {
|
|
61
|
-
const target = e.target.closest(selector);
|
|
62
|
-
if (target && element.contains(target)) {
|
|
63
|
-
handler.call(target, e);
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
element.addEventListener(event, delegatedHandler);
|
|
68
|
-
return () => element.removeEventListener(event, delegatedHandler);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Get element position relative to viewport
|
|
73
|
-
*/
|
|
74
|
-
static getPosition(element) {
|
|
75
|
-
const rect = element.getBoundingClientRect();
|
|
76
|
-
return {
|
|
77
|
-
top: rect.top + window.scrollY,
|
|
78
|
-
left: rect.left + window.scrollX,
|
|
79
|
-
bottom: rect.bottom + window.scrollY,
|
|
80
|
-
right: rect.right + window.scrollX,
|
|
81
|
-
width: rect.width,
|
|
82
|
-
height: rect.height
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Check if element is in viewport
|
|
88
|
-
*/
|
|
89
|
-
static isInViewport(element, threshold = 0) {
|
|
90
|
-
const rect = element.getBoundingClientRect();
|
|
91
|
-
return (
|
|
92
|
-
rect.top >= -threshold &&
|
|
93
|
-
rect.left >= -threshold &&
|
|
94
|
-
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) + threshold &&
|
|
95
|
-
rect.right <= (window.innerWidth || document.documentElement.clientWidth) + threshold
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Smooth scroll to element
|
|
101
|
-
*/
|
|
102
|
-
static scrollToElement(element, options = {}) {
|
|
103
|
-
const { behavior = 'smooth', block = 'start', inline = 'nearest' } = options;
|
|
104
|
-
element.scrollIntoView({ behavior, block, inline });
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Get computed style value
|
|
109
|
-
*/
|
|
110
|
-
static getStyle(element, property) {
|
|
111
|
-
return window.getComputedStyle(element).getPropertyValue(property);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Set multiple styles
|
|
116
|
-
*/
|
|
117
|
-
static setStyles(element, styles) {
|
|
118
|
-
Object.assign(element.style, styles);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Add/remove classes with animation support
|
|
123
|
-
*/
|
|
124
|
-
static async animateClass(element, className, duration = 300) {
|
|
125
|
-
element.classList.add(className);
|
|
126
|
-
await this.wait(duration);
|
|
127
|
-
element.classList.remove(className);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Wait for animation/transition to complete
|
|
132
|
-
*/
|
|
133
|
-
static waitForAnimation(element, eventType = 'animationend') {
|
|
134
|
-
return new Promise(resolve => {
|
|
135
|
-
const handler = () => {
|
|
136
|
-
element.removeEventListener(eventType, handler);
|
|
137
|
-
resolve();
|
|
138
|
-
};
|
|
139
|
-
element.addEventListener(eventType, handler);
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Utility wait function
|
|
145
|
-
*/
|
|
146
|
-
static wait(ms) {
|
|
147
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Parse HTML string safely
|
|
152
|
-
*/
|
|
153
|
-
static parseHTML(htmlString) {
|
|
154
|
-
const template = document.createElement('template');
|
|
155
|
-
template.innerHTML = htmlString.trim();
|
|
156
|
-
return template.content.firstChild;
|
|
157
|
-
}
|
|
267
|
+
const firstFocusable = focusableElements[0];
|
|
268
|
+
const lastFocusable = focusableElements[focusableElements.length - 1];
|
|
158
269
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
*/
|
|
162
|
-
static escapeHTML(str) {
|
|
163
|
-
const div = document.createElement('div');
|
|
164
|
-
div.textContent = str;
|
|
165
|
-
return div.innerHTML;
|
|
166
|
-
}
|
|
270
|
+
const handleKeyDown = e => {
|
|
271
|
+
if (e.key !== 'Tab') return;
|
|
167
272
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
let timeout;
|
|
173
|
-
return function executedFunction(...args) {
|
|
174
|
-
const later = () => {
|
|
175
|
-
clearTimeout(timeout);
|
|
176
|
-
func(...args);
|
|
177
|
-
};
|
|
178
|
-
clearTimeout(timeout);
|
|
179
|
-
timeout = setTimeout(later, wait);
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Throttle function calls
|
|
185
|
-
*/
|
|
186
|
-
static throttle(func, limit = 250) {
|
|
187
|
-
let inThrottle;
|
|
188
|
-
return function(...args) {
|
|
189
|
-
if (!inThrottle) {
|
|
190
|
-
func.apply(this, args);
|
|
191
|
-
inThrottle = true;
|
|
192
|
-
setTimeout(() => inThrottle = false, limit);
|
|
193
|
-
}
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Get closest parent matching selector
|
|
199
|
-
*/
|
|
200
|
-
static closest(element, selector) {
|
|
201
|
-
return element.closest(selector);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Get all parents matching selector
|
|
206
|
-
*/
|
|
207
|
-
static parents(element, selector) {
|
|
208
|
-
const parents = [];
|
|
209
|
-
let parent = element.parentElement;
|
|
210
|
-
|
|
211
|
-
while (parent) {
|
|
212
|
-
if (parent.matches(selector)) {
|
|
213
|
-
parents.push(parent);
|
|
214
|
-
}
|
|
215
|
-
parent = parent.parentElement;
|
|
273
|
+
if (e.shiftKey) {
|
|
274
|
+
if (document.activeElement === firstFocusable) {
|
|
275
|
+
lastFocusable?.focus();
|
|
276
|
+
e.preventDefault();
|
|
216
277
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Measure element dimensions including margins
|
|
223
|
-
*/
|
|
224
|
-
static getOuterDimensions(element) {
|
|
225
|
-
const styles = window.getComputedStyle(element);
|
|
226
|
-
const margin = {
|
|
227
|
-
top: parseInt(styles.marginTop),
|
|
228
|
-
right: parseInt(styles.marginRight),
|
|
229
|
-
bottom: parseInt(styles.marginBottom),
|
|
230
|
-
left: parseInt(styles.marginLeft)
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
return {
|
|
234
|
-
width: element.offsetWidth + margin.left + margin.right,
|
|
235
|
-
height: element.offsetHeight + margin.top + margin.bottom,
|
|
236
|
-
margin
|
|
237
|
-
};
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Clone element with event listeners
|
|
242
|
-
*/
|
|
243
|
-
static cloneWithEvents(element, deep = true) {
|
|
244
|
-
const clone = element.cloneNode(deep);
|
|
245
|
-
|
|
246
|
-
// Copy event listeners (Note: This is a simplified version)
|
|
247
|
-
// In production, you'd need a more robust event copying mechanism
|
|
248
|
-
return clone;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Focus trap for modals/dialogs
|
|
253
|
-
*/
|
|
254
|
-
static trapFocus(container) {
|
|
255
|
-
const focusableElements = container.querySelectorAll(
|
|
256
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
257
|
-
);
|
|
258
|
-
|
|
259
|
-
// Handle case where there are no focusable elements
|
|
260
|
-
if (focusableElements.length === 0) {
|
|
261
|
-
// Make container focusable as fallback
|
|
262
|
-
container.setAttribute('tabindex', '-1');
|
|
263
|
-
container.focus();
|
|
264
|
-
return () => container.removeAttribute('tabindex');
|
|
278
|
+
} else {
|
|
279
|
+
if (document.activeElement === lastFocusable) {
|
|
280
|
+
firstFocusable?.focus();
|
|
281
|
+
e.preventDefault();
|
|
265
282
|
}
|
|
283
|
+
}
|
|
284
|
+
};
|
|
266
285
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
if (e.shiftKey) {
|
|
274
|
-
if (document.activeElement === firstFocusable) {
|
|
275
|
-
lastFocusable?.focus();
|
|
276
|
-
e.preventDefault();
|
|
277
|
-
}
|
|
278
|
-
} else {
|
|
279
|
-
if (document.activeElement === lastFocusable) {
|
|
280
|
-
firstFocusable?.focus();
|
|
281
|
-
e.preventDefault();
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
container.addEventListener('keydown', handleKeyDown);
|
|
287
|
-
firstFocusable?.focus();
|
|
288
|
-
|
|
289
|
-
return () => container.removeEventListener('keydown', handleKeyDown);
|
|
290
|
-
}
|
|
286
|
+
container.addEventListener('keydown', handleKeyDown);
|
|
287
|
+
firstFocusable?.focus();
|
|
288
|
+
|
|
289
|
+
return () => container.removeEventListener('keydown', handleKeyDown);
|
|
290
|
+
}
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
export default DOMUtils;
|
|
293
|
+
export default DOMUtils;
|