elementdrawing 1.0.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/LICENSE +21 -0
- package/dist/elementdrawing.min.js +3 -0
- package/dist/elementdrawing.min.js.LICENSE.txt +8 -0
- package/dist/elementdrawing.min.js.map +1 -0
- package/dist/index.html +1 -0
- package/package.json +127 -0
- package/src/core/bridge.h +855 -0
- package/src/core/diff.c +900 -0
- package/src/core/element.c +1078 -0
- package/src/core/event.c +813 -0
- package/src/core/fiber.c +1027 -0
- package/src/core/hooks.c +919 -0
- package/src/core/renderer.c +963 -0
- package/src/core/scheduler.c +702 -0
- package/src/core/state.c +803 -0
- package/src/css/animations.css +779 -0
- package/src/css/base.css +615 -0
- package/src/css/components.css +1311 -0
- package/src/css/tailwind.css +370 -0
- package/src/css/themes.css +517 -0
- package/src/css/utilities.css +475 -0
- package/src/index.js +746 -0
- package/src/js/animation.js +655 -0
- package/src/js/dom.js +665 -0
- package/src/js/events.js +585 -0
- package/src/js/http.js +446 -0
- package/src/js/index.js +26 -0
- package/src/js/router.js +483 -0
- package/src/js/store.js +539 -0
- package/src/js/utils.js +593 -0
- package/src/js/validator.js +529 -0
- package/src/jsx/components/Accordion.jsx +210 -0
- package/src/jsx/components/Alert.jsx +169 -0
- package/src/jsx/components/Avatar.jsx +214 -0
- package/src/jsx/components/Badge.jsx +136 -0
- package/src/jsx/components/Breadcrumb.jsx +200 -0
- package/src/jsx/components/Button.jsx +188 -0
- package/src/jsx/components/Card.jsx +192 -0
- package/src/jsx/components/Carousel.jsx +278 -0
- package/src/jsx/components/Checkbox.jsx +215 -0
- package/src/jsx/components/Dialog.jsx +242 -0
- package/src/jsx/components/Drawer.jsx +190 -0
- package/src/jsx/components/Dropdown.jsx +268 -0
- package/src/jsx/components/Form.jsx +274 -0
- package/src/jsx/components/Input.jsx +285 -0
- package/src/jsx/components/Menu.jsx +276 -0
- package/src/jsx/components/Modal.jsx +274 -0
- package/src/jsx/components/Navbar.jsx +292 -0
- package/src/jsx/components/Pagination.jsx +268 -0
- package/src/jsx/components/Progress.jsx +252 -0
- package/src/jsx/components/Radio.jsx +208 -0
- package/src/jsx/components/Select.jsx +397 -0
- package/src/jsx/components/Sidebar.jsx +250 -0
- package/src/jsx/components/Slider.jsx +310 -0
- package/src/jsx/components/Spinner.jsx +198 -0
- package/src/jsx/components/Switch.jsx +201 -0
- package/src/jsx/components/Table.jsx +332 -0
- package/src/jsx/components/Tabs.jsx +227 -0
- package/src/jsx/components/Textarea.jsx +212 -0
- package/src/jsx/components/Toast.jsx +270 -0
- package/src/jsx/components/Tooltip.jsx +178 -0
- package/src/jsx/components/Typography.jsx +299 -0
- package/src/jsx/components/index.jsx +70 -0
- package/src/jsx/core/element.js +3 -0
- package/src/jsx/hooks/index.js +356 -0
- package/src/jsx/hooks/useCallback.js +472 -0
- package/src/jsx/hooks/useContext.js +586 -0
- package/src/jsx/hooks/useEffect.js +704 -0
- package/src/jsx/hooks/useLayoutEffect.js +508 -0
- package/src/jsx/hooks/useMemo.js +689 -0
- package/src/jsx/hooks/useReducer.js +729 -0
- package/src/jsx/hooks/useRef.js +542 -0
- package/src/jsx/hooks/useState.js +854 -0
- package/src/jsx/runtime/commit.js +903 -0
- package/src/jsx/runtime/createElement.js +860 -0
- package/src/jsx/runtime/index.js +356 -0
- package/src/jsx/runtime/reconcile.js +687 -0
- package/src/jsx/runtime/render.js +914 -0
package/src/js/dom.js
ADDED
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM Manipulation Utilities
|
|
3
|
+
* ElementDrawing Framework - Comprehensive DOM querying, creation,
|
|
4
|
+
* manipulation, CSS, attributes, dimensions, visibility, and animation helpers.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
// ─── Query Selectors ──────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Select a single element by ID.
|
|
13
|
+
* @param {string} id
|
|
14
|
+
* @param {Document|HTMLElement} [context=document]
|
|
15
|
+
* @returns {HTMLElement|null}
|
|
16
|
+
*/
|
|
17
|
+
function byId(id, context) {
|
|
18
|
+
return (context || document).getElementById(id);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Select elements by class name.
|
|
23
|
+
* @param {string} className
|
|
24
|
+
* @param {Document|HTMLElement} [context=document]
|
|
25
|
+
* @returns {HTMLCollection}
|
|
26
|
+
*/
|
|
27
|
+
function byClass(className, context) {
|
|
28
|
+
return (context || document).getElementsByClassName(className);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Select elements by tag name.
|
|
33
|
+
* @param {string} tagName
|
|
34
|
+
* @param {Document|HTMLElement} [context=document]
|
|
35
|
+
* @returns {HTMLCollection}
|
|
36
|
+
*/
|
|
37
|
+
function byTag(tagName, context) {
|
|
38
|
+
return (context || document).getElementsByTagName(tagName);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Select the first element matching a CSS selector.
|
|
43
|
+
* @param {string} selector
|
|
44
|
+
* @param {Document|HTMLElement} [context=document]
|
|
45
|
+
* @returns {HTMLElement|null}
|
|
46
|
+
*/
|
|
47
|
+
function query(selector, context) {
|
|
48
|
+
return (context || document).querySelector(selector);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Select all elements matching a CSS selector.
|
|
53
|
+
* @param {string} selector
|
|
54
|
+
* @param {Document|HTMLElement} [context=document]
|
|
55
|
+
* @returns {NodeList}
|
|
56
|
+
*/
|
|
57
|
+
function queryAll(selector, context) {
|
|
58
|
+
return (context || document).querySelectorAll(selector);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ─── DOM Creation ─────────────────────────────────────────────────────────────
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create an HTML element with optional attributes and children.
|
|
65
|
+
* @param {string} tagName
|
|
66
|
+
* @param {Object} [attrs]
|
|
67
|
+
* @param {...(string|HTMLElement)} children
|
|
68
|
+
* @returns {HTMLElement}
|
|
69
|
+
*/
|
|
70
|
+
function createElement(tagName, attrs) {
|
|
71
|
+
const el = document.createElement(tagName);
|
|
72
|
+
|
|
73
|
+
if (attrs) {
|
|
74
|
+
Object.keys(attrs).forEach((key) => {
|
|
75
|
+
const value = attrs[key];
|
|
76
|
+
if (key === 'className' || key === 'class') {
|
|
77
|
+
el.className = value;
|
|
78
|
+
} else if (key === 'style' && typeof value === 'object') {
|
|
79
|
+
Object.assign(el.style, value);
|
|
80
|
+
} else if (key === 'style' && typeof value === 'string') {
|
|
81
|
+
el.style.cssText = value;
|
|
82
|
+
} else if (key.startsWith('data-')) {
|
|
83
|
+
el.setAttribute(key, value);
|
|
84
|
+
} else if (key.startsWith('on') && typeof value === 'function') {
|
|
85
|
+
const eventType = key.slice(2).toLowerCase();
|
|
86
|
+
el.addEventListener(eventType, value);
|
|
87
|
+
} else if (typeof value === 'boolean') {
|
|
88
|
+
if (value) el.setAttribute(key, '');
|
|
89
|
+
else el.removeAttribute(key);
|
|
90
|
+
} else {
|
|
91
|
+
el.setAttribute(key, value);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const childArgs = Array.prototype.slice.call(arguments, 2);
|
|
97
|
+
childArgs.forEach((child) => {
|
|
98
|
+
if (typeof child === 'string' || typeof child === 'number') {
|
|
99
|
+
el.appendChild(document.createTextNode(String(child)));
|
|
100
|
+
} else if (child instanceof HTMLElement || child instanceof DocumentFragment) {
|
|
101
|
+
el.appendChild(child);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return el;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Create a document fragment.
|
|
110
|
+
* @returns {DocumentFragment}
|
|
111
|
+
*/
|
|
112
|
+
function createFragment() {
|
|
113
|
+
return document.createDocumentFragment();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Create a text node.
|
|
118
|
+
* @param {string} text
|
|
119
|
+
* @returns {Text}
|
|
120
|
+
*/
|
|
121
|
+
function createTextNode(text) {
|
|
122
|
+
return document.createTextNode(text);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ─── DOM Manipulation ─────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Append a child to a parent element.
|
|
129
|
+
* @param {HTMLElement} parent
|
|
130
|
+
* @param {HTMLElement|DocumentFragment|string} child
|
|
131
|
+
* @returns {HTMLElement}
|
|
132
|
+
*/
|
|
133
|
+
function append(parent, child) {
|
|
134
|
+
if (typeof child === 'string') {
|
|
135
|
+
parent.insertAdjacentHTML('beforeend', child);
|
|
136
|
+
} else {
|
|
137
|
+
parent.appendChild(child);
|
|
138
|
+
}
|
|
139
|
+
return parent;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Prepend a child to a parent element.
|
|
144
|
+
* @param {HTMLElement} parent
|
|
145
|
+
* @param {HTMLElement|DocumentFragment|string} child
|
|
146
|
+
* @returns {HTMLElement}
|
|
147
|
+
*/
|
|
148
|
+
function prepend(parent, child) {
|
|
149
|
+
if (typeof child === 'string') {
|
|
150
|
+
parent.insertAdjacentHTML('afterbegin', child);
|
|
151
|
+
} else {
|
|
152
|
+
parent.insertBefore(child, parent.firstChild);
|
|
153
|
+
}
|
|
154
|
+
return parent;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Insert a node before a reference node.
|
|
159
|
+
* @param {HTMLElement} newNode
|
|
160
|
+
* @param {HTMLElement} referenceNode
|
|
161
|
+
* @returns {HTMLElement}
|
|
162
|
+
*/
|
|
163
|
+
function insertBefore(newNode, referenceNode) {
|
|
164
|
+
if (referenceNode.parentNode) {
|
|
165
|
+
referenceNode.parentNode.insertBefore(newNode, referenceNode);
|
|
166
|
+
}
|
|
167
|
+
return newNode;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Remove an element from the DOM.
|
|
172
|
+
* @param {HTMLElement} el
|
|
173
|
+
* @returns {HTMLElement}
|
|
174
|
+
*/
|
|
175
|
+
function remove(el) {
|
|
176
|
+
if (el && el.parentNode) {
|
|
177
|
+
el.parentNode.removeChild(el);
|
|
178
|
+
}
|
|
179
|
+
return el;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Replace an old element with a new one.
|
|
184
|
+
* @param {HTMLElement} oldEl
|
|
185
|
+
* @param {HTMLElement} newEl
|
|
186
|
+
* @returns {HTMLElement}
|
|
187
|
+
*/
|
|
188
|
+
function replace(oldEl, newEl) {
|
|
189
|
+
if (oldEl.parentNode) {
|
|
190
|
+
oldEl.parentNode.replaceChild(newEl, oldEl);
|
|
191
|
+
}
|
|
192
|
+
return newEl;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Clone an element.
|
|
197
|
+
* @param {HTMLElement} el
|
|
198
|
+
* @param {boolean} [deep=true]
|
|
199
|
+
* @returns {HTMLElement}
|
|
200
|
+
*/
|
|
201
|
+
function clone(el, deep) {
|
|
202
|
+
return el.cloneNode(deep !== false);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Remove all children from an element.
|
|
207
|
+
* @param {HTMLElement} el
|
|
208
|
+
* @returns {HTMLElement}
|
|
209
|
+
*/
|
|
210
|
+
function empty(el) {
|
|
211
|
+
while (el.firstChild) {
|
|
212
|
+
el.removeChild(el.firstChild);
|
|
213
|
+
}
|
|
214
|
+
return el;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Wrap an element with a wrapper element.
|
|
219
|
+
* @param {HTMLElement} el
|
|
220
|
+
* @param {HTMLElement} wrapper
|
|
221
|
+
* @returns {HTMLElement}
|
|
222
|
+
*/
|
|
223
|
+
function wrap(el, wrapper) {
|
|
224
|
+
if (el.parentNode) {
|
|
225
|
+
el.parentNode.insertBefore(wrapper, el);
|
|
226
|
+
}
|
|
227
|
+
wrapper.appendChild(el);
|
|
228
|
+
return wrapper;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Unwrap an element (remove its parent, keeping the element).
|
|
233
|
+
* @param {HTMLElement} el
|
|
234
|
+
* @returns {HTMLElement}
|
|
235
|
+
*/
|
|
236
|
+
function unwrap(el) {
|
|
237
|
+
const parent = el.parentNode;
|
|
238
|
+
if (!parent) return el;
|
|
239
|
+
|
|
240
|
+
while (el.firstChild) {
|
|
241
|
+
parent.insertBefore(el.firstChild, el);
|
|
242
|
+
}
|
|
243
|
+
parent.removeChild(el);
|
|
244
|
+
return parent;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ─── CSS Manipulation ─────────────────────────────────────────────────────────
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Add one or more CSS classes to an element.
|
|
251
|
+
* @param {HTMLElement} el
|
|
252
|
+
* @param {...string} classes
|
|
253
|
+
* @returns {HTMLElement}
|
|
254
|
+
*/
|
|
255
|
+
function addClass(el) {
|
|
256
|
+
const classes = Array.prototype.slice.call(arguments, 1);
|
|
257
|
+
el.classList.add.apply(el.classList, classes);
|
|
258
|
+
return el;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Remove one or more CSS classes from an element.
|
|
263
|
+
* @param {HTMLElement} el
|
|
264
|
+
* @param {...string} classes
|
|
265
|
+
* @returns {HTMLElement}
|
|
266
|
+
*/
|
|
267
|
+
function removeClass(el) {
|
|
268
|
+
const classes = Array.prototype.slice.call(arguments, 1);
|
|
269
|
+
el.classList.remove.apply(el.classList, classes);
|
|
270
|
+
return el;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Toggle a CSS class on an element.
|
|
275
|
+
* @param {HTMLElement} el
|
|
276
|
+
* @param {string} className
|
|
277
|
+
* @param {boolean} [force]
|
|
278
|
+
* @returns {HTMLElement}
|
|
279
|
+
*/
|
|
280
|
+
function toggleClass(el, className, force) {
|
|
281
|
+
el.classList.toggle(className, force);
|
|
282
|
+
return el;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Check if an element has a CSS class.
|
|
287
|
+
* @param {HTMLElement} el
|
|
288
|
+
* @param {string} className
|
|
289
|
+
* @returns {boolean}
|
|
290
|
+
*/
|
|
291
|
+
function hasClass(el, className) {
|
|
292
|
+
return el.classList.contains(className);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Set an inline style property on an element.
|
|
297
|
+
* @param {HTMLElement} el
|
|
298
|
+
* @param {string|Object} prop - CSS property name or style object
|
|
299
|
+
* @param {string} [value] - CSS value (if prop is a string)
|
|
300
|
+
* @returns {HTMLElement}
|
|
301
|
+
*/
|
|
302
|
+
function setStyle(el, prop, value) {
|
|
303
|
+
if (typeof prop === 'object') {
|
|
304
|
+
Object.keys(prop).forEach((key) => {
|
|
305
|
+
el.style[key] = prop[key];
|
|
306
|
+
});
|
|
307
|
+
} else {
|
|
308
|
+
el.style[prop] = value;
|
|
309
|
+
}
|
|
310
|
+
return el;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Get a computed style value for an element.
|
|
315
|
+
* @param {HTMLElement} el
|
|
316
|
+
* @param {string} prop
|
|
317
|
+
* @returns {string}
|
|
318
|
+
*/
|
|
319
|
+
function getStyle(el, prop) {
|
|
320
|
+
return window.getComputedStyle(el).getPropertyValue(prop);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Get all computed styles for an element.
|
|
325
|
+
* @param {HTMLElement} el
|
|
326
|
+
* @returns {CSSStyleDeclaration}
|
|
327
|
+
*/
|
|
328
|
+
function getComputedStyle(el) {
|
|
329
|
+
return window.getComputedStyle(el);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ─── Attribute Manipulation ───────────────────────────────────────────────────
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Set an attribute on an element.
|
|
336
|
+
* @param {HTMLElement} el
|
|
337
|
+
* @param {string} name
|
|
338
|
+
* @param {string} value
|
|
339
|
+
* @returns {HTMLElement}
|
|
340
|
+
*/
|
|
341
|
+
function setAttribute(el, name, value) {
|
|
342
|
+
el.setAttribute(name, value);
|
|
343
|
+
return el;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Get an attribute value from an element.
|
|
348
|
+
* @param {HTMLElement} el
|
|
349
|
+
* @param {string} name
|
|
350
|
+
* @returns {string|null}
|
|
351
|
+
*/
|
|
352
|
+
function getAttribute(el, name) {
|
|
353
|
+
return el.getAttribute(name);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Remove an attribute from an element.
|
|
358
|
+
* @param {HTMLElement} el
|
|
359
|
+
* @param {string} name
|
|
360
|
+
* @returns {HTMLElement}
|
|
361
|
+
*/
|
|
362
|
+
function removeAttribute(el, name) {
|
|
363
|
+
el.removeAttribute(name);
|
|
364
|
+
return el;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Set a data attribute on an element.
|
|
369
|
+
* @param {HTMLElement} el
|
|
370
|
+
* @param {string} key
|
|
371
|
+
* @param {*} value
|
|
372
|
+
* @returns {HTMLElement}
|
|
373
|
+
*/
|
|
374
|
+
function setData(el, key, value) {
|
|
375
|
+
el.dataset[key] = value;
|
|
376
|
+
return el;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Get a data attribute from an element.
|
|
381
|
+
* @param {HTMLElement} el
|
|
382
|
+
* @param {string} key
|
|
383
|
+
* @returns {string|undefined}
|
|
384
|
+
*/
|
|
385
|
+
function getData(el, key) {
|
|
386
|
+
return el.dataset[key];
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// ─── Dimension Utilities ──────────────────────────────────────────────────────
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Get the width of an element (content + padding + border).
|
|
393
|
+
* @param {HTMLElement} el
|
|
394
|
+
* @returns {number}
|
|
395
|
+
*/
|
|
396
|
+
function getWidth(el) {
|
|
397
|
+
return el.offsetWidth;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Get the height of an element (content + padding + border).
|
|
402
|
+
* @param {HTMLElement} el
|
|
403
|
+
* @returns {number}
|
|
404
|
+
*/
|
|
405
|
+
function getHeight(el) {
|
|
406
|
+
return el.offsetHeight;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Get the offset position of an element relative to the document.
|
|
411
|
+
* @param {HTMLElement} el
|
|
412
|
+
* @returns {{ top: number, left: number }}
|
|
413
|
+
*/
|
|
414
|
+
function getOffset(el) {
|
|
415
|
+
const rect = el.getBoundingClientRect();
|
|
416
|
+
return {
|
|
417
|
+
top: rect.top + window.pageYOffset,
|
|
418
|
+
left: rect.left + window.pageXOffset,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Get the position of an element relative to its offset parent.
|
|
424
|
+
* @param {HTMLElement} el
|
|
425
|
+
* @returns {{ top: number, left: number }}
|
|
426
|
+
*/
|
|
427
|
+
function getPosition(el) {
|
|
428
|
+
return {
|
|
429
|
+
top: el.offsetTop,
|
|
430
|
+
left: el.offsetLeft,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Get scroll information for an element.
|
|
436
|
+
* @param {HTMLElement} [el=window]
|
|
437
|
+
* @returns {{ top: number, left: number, width: number, height: number }}
|
|
438
|
+
*/
|
|
439
|
+
function getScroll(el) {
|
|
440
|
+
if (!el || el === window) {
|
|
441
|
+
return {
|
|
442
|
+
top: window.pageYOffset || document.documentElement.scrollTop,
|
|
443
|
+
left: window.pageXOffset || document.documentElement.scrollLeft,
|
|
444
|
+
width: document.documentElement.scrollWidth,
|
|
445
|
+
height: document.documentElement.scrollHeight,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
return {
|
|
449
|
+
top: el.scrollTop,
|
|
450
|
+
left: el.scrollLeft,
|
|
451
|
+
width: el.scrollWidth,
|
|
452
|
+
height: el.scrollHeight,
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Get viewport dimensions.
|
|
458
|
+
* @returns {{ width: number, height: number }}
|
|
459
|
+
*/
|
|
460
|
+
function getViewport() {
|
|
461
|
+
return {
|
|
462
|
+
width: window.innerWidth || document.documentElement.clientWidth,
|
|
463
|
+
height: window.innerHeight || document.documentElement.clientHeight,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Get the bounding client rect of an element.
|
|
469
|
+
* @param {HTMLElement} el
|
|
470
|
+
* @returns {DOMRect}
|
|
471
|
+
*/
|
|
472
|
+
function getRect(el) {
|
|
473
|
+
return el.getBoundingClientRect();
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// ─── Visibility ───────────────────────────────────────────────────────────────
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Show a hidden element.
|
|
480
|
+
* @param {HTMLElement} el
|
|
481
|
+
* @param {string} [display='']
|
|
482
|
+
* @returns {HTMLElement}
|
|
483
|
+
*/
|
|
484
|
+
function show(el, display) {
|
|
485
|
+
el.style.display = display || '';
|
|
486
|
+
return el;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Hide an element.
|
|
491
|
+
* @param {HTMLElement} el
|
|
492
|
+
* @returns {HTMLElement}
|
|
493
|
+
*/
|
|
494
|
+
function hide(el) {
|
|
495
|
+
el.style.display = 'none';
|
|
496
|
+
return el;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Toggle element visibility.
|
|
501
|
+
* @param {HTMLElement} el
|
|
502
|
+
* @returns {HTMLElement}
|
|
503
|
+
*/
|
|
504
|
+
function toggle(el) {
|
|
505
|
+
if (el.style.display === 'none') {
|
|
506
|
+
show(el);
|
|
507
|
+
} else {
|
|
508
|
+
hide(el);
|
|
509
|
+
}
|
|
510
|
+
return el;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Check if an element is visible (not display:none or visibility:hidden).
|
|
515
|
+
* @param {HTMLElement} el
|
|
516
|
+
* @returns {boolean}
|
|
517
|
+
*/
|
|
518
|
+
function isVisible(el) {
|
|
519
|
+
if (el.offsetWidth === 0 && el.offsetHeight === 0) return false;
|
|
520
|
+
const style = window.getComputedStyle(el);
|
|
521
|
+
return style.display !== 'none' && style.visibility !== 'hidden' && style.opacity !== '0';
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Check if an element is within the viewport.
|
|
526
|
+
* @param {HTMLElement} el
|
|
527
|
+
* @param {number} [threshold=0]
|
|
528
|
+
* @returns {boolean}
|
|
529
|
+
*/
|
|
530
|
+
function isInViewport(el, threshold) {
|
|
531
|
+
threshold = threshold || 0;
|
|
532
|
+
const rect = el.getBoundingClientRect();
|
|
533
|
+
const vp = getViewport();
|
|
534
|
+
|
|
535
|
+
return (
|
|
536
|
+
rect.top >= -threshold &&
|
|
537
|
+
rect.left >= -threshold &&
|
|
538
|
+
rect.bottom <= vp.height + threshold &&
|
|
539
|
+
rect.right <= vp.width + threshold
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// ─── Animation Helpers ────────────────────────────────────────────────────────
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Animate an element using the Web Animations API or CSS transitions.
|
|
547
|
+
* @param {HTMLElement} el
|
|
548
|
+
* @param {Object} keyframes
|
|
549
|
+
* @param {Object} [options]
|
|
550
|
+
* @returns {Promise}
|
|
551
|
+
*/
|
|
552
|
+
function animate(el, keyframes, options) {
|
|
553
|
+
if (el.animate && typeof el.animate === 'function') {
|
|
554
|
+
return el.animate(keyframes, options).finished;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// Fallback: apply final state immediately
|
|
558
|
+
return new Promise((resolve) => {
|
|
559
|
+
Object.keys(keyframes).forEach((prop) => {
|
|
560
|
+
const value = Array.isArray(keyframes[prop])
|
|
561
|
+
? keyframes[prop][keyframes[prop].length - 1]
|
|
562
|
+
: keyframes[prop];
|
|
563
|
+
el.style[prop] = value;
|
|
564
|
+
});
|
|
565
|
+
requestAnimationFrame(() => resolve(el));
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Fade in an element.
|
|
571
|
+
* @param {HTMLElement} el
|
|
572
|
+
* @param {number} [duration=300]
|
|
573
|
+
* @returns {Promise}
|
|
574
|
+
*/
|
|
575
|
+
function fadeIn(el, duration) {
|
|
576
|
+
duration = duration || 300;
|
|
577
|
+
el.style.opacity = '0';
|
|
578
|
+
el.style.display = '';
|
|
579
|
+
return animate(el, { opacity: [0, 1] }, { duration, fill: 'forwards' });
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* Fade out an element.
|
|
584
|
+
* @param {HTMLElement} el
|
|
585
|
+
* @param {number} [duration=300]
|
|
586
|
+
* @returns {Promise}
|
|
587
|
+
*/
|
|
588
|
+
function fadeOut(el, duration) {
|
|
589
|
+
duration = duration || 300;
|
|
590
|
+
return animate(el, { opacity: [1, 0] }, { duration, fill: 'forwards' }).then(() => {
|
|
591
|
+
el.style.display = 'none';
|
|
592
|
+
return el;
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Slide up (collapse) an element.
|
|
598
|
+
* @param {HTMLElement} el
|
|
599
|
+
* @param {number} [duration=300]
|
|
600
|
+
* @returns {Promise}
|
|
601
|
+
*/
|
|
602
|
+
function slideUp(el, duration) {
|
|
603
|
+
duration = duration || 300;
|
|
604
|
+
const height = el.offsetHeight;
|
|
605
|
+
return animate(el, {
|
|
606
|
+
height: [height + 'px', '0px'],
|
|
607
|
+
overflow: ['hidden', 'hidden'],
|
|
608
|
+
}, { duration, fill: 'forwards' }).then(() => {
|
|
609
|
+
el.style.display = 'none';
|
|
610
|
+
return el;
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Slide down (expand) an element.
|
|
616
|
+
* @param {HTMLElement} el
|
|
617
|
+
* @param {number} [duration=300]
|
|
618
|
+
* @returns {Promise}
|
|
619
|
+
*/
|
|
620
|
+
function slideDown(el, duration) {
|
|
621
|
+
duration = duration || 300;
|
|
622
|
+
el.style.display = '';
|
|
623
|
+
el.style.overflow = 'hidden';
|
|
624
|
+
const height = el.scrollHeight;
|
|
625
|
+
el.style.height = '0px';
|
|
626
|
+
return animate(el, { height: ['0px', height + 'px'] }, { duration, fill: 'forwards' }).then(() => {
|
|
627
|
+
el.style.height = '';
|
|
628
|
+
el.style.overflow = '';
|
|
629
|
+
return el;
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Toggle slide up/down.
|
|
635
|
+
* @param {HTMLElement} el
|
|
636
|
+
* @param {number} [duration=300]
|
|
637
|
+
* @returns {Promise}
|
|
638
|
+
*/
|
|
639
|
+
function slideToggle(el, duration) {
|
|
640
|
+
if (el.style.display === 'none' || el.offsetHeight === 0) {
|
|
641
|
+
return slideDown(el, duration);
|
|
642
|
+
}
|
|
643
|
+
return slideUp(el, duration);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// ─── Exports ──────────────────────────────────────────────────────────────────
|
|
647
|
+
|
|
648
|
+
module.exports = {
|
|
649
|
+
// Query selectors
|
|
650
|
+
byId, byClass, byTag, query, queryAll,
|
|
651
|
+
// DOM creation
|
|
652
|
+
createElement, createFragment, createTextNode,
|
|
653
|
+
// DOM manipulation
|
|
654
|
+
append, prepend, insertBefore, remove, replace, clone, empty, wrap, unwrap,
|
|
655
|
+
// CSS manipulation
|
|
656
|
+
addClass, removeClass, toggleClass, hasClass, setStyle, getStyle, getComputedStyle,
|
|
657
|
+
// Attribute manipulation
|
|
658
|
+
setAttribute, getAttribute, removeAttribute, setData, getData,
|
|
659
|
+
// Dimension utilities
|
|
660
|
+
getWidth, getHeight, getOffset, getPosition, getScroll, getViewport, getRect,
|
|
661
|
+
// Visibility
|
|
662
|
+
show, hide, toggle, isVisible, isInViewport,
|
|
663
|
+
// Animation helpers
|
|
664
|
+
animate, fadeIn, fadeOut, slideUp, slideDown, slideToggle,
|
|
665
|
+
};
|