zero-query 0.5.2 → 0.7.5

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.
Files changed (58) hide show
  1. package/README.md +12 -10
  2. package/cli/commands/build.js +7 -5
  3. package/cli/commands/bundle.js +286 -8
  4. package/cli/commands/dev/index.js +82 -0
  5. package/cli/commands/dev/logger.js +70 -0
  6. package/cli/commands/dev/overlay.js +366 -0
  7. package/cli/commands/dev/server.js +158 -0
  8. package/cli/commands/dev/validator.js +94 -0
  9. package/cli/commands/dev/watcher.js +147 -0
  10. package/cli/scaffold/favicon.ico +0 -0
  11. package/cli/scaffold/index.html +1 -0
  12. package/cli/scaffold/scripts/app.js +15 -22
  13. package/cli/scaffold/scripts/components/about.js +14 -2
  14. package/cli/scaffold/scripts/components/contacts/contacts.css +0 -7
  15. package/cli/scaffold/scripts/components/contacts/contacts.html +8 -7
  16. package/cli/scaffold/scripts/components/contacts/contacts.js +17 -1
  17. package/cli/scaffold/scripts/components/counter.js +30 -10
  18. package/cli/scaffold/scripts/components/home.js +3 -3
  19. package/cli/scaffold/scripts/components/todos.js +6 -5
  20. package/cli/scaffold/styles/styles.css +1 -0
  21. package/cli/utils.js +111 -6
  22. package/dist/zquery.dist.zip +0 -0
  23. package/dist/zquery.js +2005 -216
  24. package/dist/zquery.min.js +3 -13
  25. package/index.d.ts +149 -1080
  26. package/index.js +18 -7
  27. package/package.json +9 -3
  28. package/src/component.js +186 -45
  29. package/src/core.js +327 -35
  30. package/src/diff.js +280 -0
  31. package/src/errors.js +155 -0
  32. package/src/expression.js +806 -0
  33. package/src/http.js +18 -10
  34. package/src/reactive.js +29 -4
  35. package/src/router.js +59 -6
  36. package/src/ssr.js +224 -0
  37. package/src/store.js +24 -8
  38. package/tests/component.test.js +304 -0
  39. package/tests/core.test.js +726 -0
  40. package/tests/diff.test.js +194 -0
  41. package/tests/errors.test.js +162 -0
  42. package/tests/expression.test.js +334 -0
  43. package/tests/http.test.js +181 -0
  44. package/tests/reactive.test.js +191 -0
  45. package/tests/router.test.js +332 -0
  46. package/tests/store.test.js +253 -0
  47. package/tests/utils.test.js +353 -0
  48. package/types/collection.d.ts +368 -0
  49. package/types/component.d.ts +210 -0
  50. package/types/errors.d.ts +103 -0
  51. package/types/http.d.ts +81 -0
  52. package/types/misc.d.ts +166 -0
  53. package/types/reactive.d.ts +76 -0
  54. package/types/router.d.ts +132 -0
  55. package/types/ssr.d.ts +49 -0
  56. package/types/store.d.ts +107 -0
  57. package/types/utils.d.ts +142 -0
  58. /package/cli/commands/{dev.js → dev.old.js} +0 -0
package/src/core.js CHANGED
@@ -26,6 +26,11 @@ export class ZQueryCollection {
26
26
  return this.elements.map((el, i) => fn.call(el, i, el));
27
27
  }
28
28
 
29
+ forEach(fn) {
30
+ this.elements.forEach((el, i) => fn(el, i, this.elements));
31
+ return this;
32
+ }
33
+
29
34
  first() { return this.elements[0] || null; }
30
35
  last() { return this.elements[this.length - 1] || null; }
31
36
  eq(i) { return new ZQueryCollection(this.elements[i] ? [this.elements[i]] : []); }
@@ -70,8 +75,96 @@ export class ZQueryCollection {
70
75
  return new ZQueryCollection(sibs);
71
76
  }
72
77
 
73
- next() { return new ZQueryCollection(this.elements.map(el => el.nextElementSibling).filter(Boolean)); }
74
- prev() { return new ZQueryCollection(this.elements.map(el => el.previousElementSibling).filter(Boolean)); }
78
+ next(selector) {
79
+ const els = this.elements.map(el => el.nextElementSibling).filter(Boolean);
80
+ return new ZQueryCollection(selector ? els.filter(el => el.matches(selector)) : els);
81
+ }
82
+
83
+ prev(selector) {
84
+ const els = this.elements.map(el => el.previousElementSibling).filter(Boolean);
85
+ return new ZQueryCollection(selector ? els.filter(el => el.matches(selector)) : els);
86
+ }
87
+
88
+ nextAll(selector) {
89
+ const result = [];
90
+ this.elements.forEach(el => {
91
+ let sib = el.nextElementSibling;
92
+ while (sib) {
93
+ if (!selector || sib.matches(selector)) result.push(sib);
94
+ sib = sib.nextElementSibling;
95
+ }
96
+ });
97
+ return new ZQueryCollection(result);
98
+ }
99
+
100
+ nextUntil(selector, filter) {
101
+ const result = [];
102
+ this.elements.forEach(el => {
103
+ let sib = el.nextElementSibling;
104
+ while (sib) {
105
+ if (selector && sib.matches(selector)) break;
106
+ if (!filter || sib.matches(filter)) result.push(sib);
107
+ sib = sib.nextElementSibling;
108
+ }
109
+ });
110
+ return new ZQueryCollection(result);
111
+ }
112
+
113
+ prevAll(selector) {
114
+ const result = [];
115
+ this.elements.forEach(el => {
116
+ let sib = el.previousElementSibling;
117
+ while (sib) {
118
+ if (!selector || sib.matches(selector)) result.push(sib);
119
+ sib = sib.previousElementSibling;
120
+ }
121
+ });
122
+ return new ZQueryCollection(result);
123
+ }
124
+
125
+ prevUntil(selector, filter) {
126
+ const result = [];
127
+ this.elements.forEach(el => {
128
+ let sib = el.previousElementSibling;
129
+ while (sib) {
130
+ if (selector && sib.matches(selector)) break;
131
+ if (!filter || sib.matches(filter)) result.push(sib);
132
+ sib = sib.previousElementSibling;
133
+ }
134
+ });
135
+ return new ZQueryCollection(result);
136
+ }
137
+
138
+ parents(selector) {
139
+ const result = [];
140
+ this.elements.forEach(el => {
141
+ let parent = el.parentElement;
142
+ while (parent) {
143
+ if (!selector || parent.matches(selector)) result.push(parent);
144
+ parent = parent.parentElement;
145
+ }
146
+ });
147
+ return new ZQueryCollection([...new Set(result)]);
148
+ }
149
+
150
+ parentsUntil(selector, filter) {
151
+ const result = [];
152
+ this.elements.forEach(el => {
153
+ let parent = el.parentElement;
154
+ while (parent) {
155
+ if (selector && parent.matches(selector)) break;
156
+ if (!filter || parent.matches(filter)) result.push(parent);
157
+ parent = parent.parentElement;
158
+ }
159
+ });
160
+ return new ZQueryCollection([...new Set(result)]);
161
+ }
162
+
163
+ contents() {
164
+ const result = [];
165
+ this.elements.forEach(el => result.push(...el.childNodes));
166
+ return new ZQueryCollection(result);
167
+ }
75
168
 
76
169
  filter(selector) {
77
170
  if (typeof selector === 'function') {
@@ -91,6 +184,42 @@ export class ZQueryCollection {
91
184
  return new ZQueryCollection(this.elements.filter(el => el.querySelector(selector)));
92
185
  }
93
186
 
187
+ is(selector) {
188
+ if (typeof selector === 'function') {
189
+ return this.elements.some((el, i) => selector.call(el, i, el));
190
+ }
191
+ return this.elements.some(el => el.matches(selector));
192
+ }
193
+
194
+ slice(start, end) {
195
+ return new ZQueryCollection(this.elements.slice(start, end));
196
+ }
197
+
198
+ add(selector, context) {
199
+ const toAdd = (selector instanceof ZQueryCollection)
200
+ ? selector.elements
201
+ : (selector instanceof Node)
202
+ ? [selector]
203
+ : Array.from((context || document).querySelectorAll(selector));
204
+ return new ZQueryCollection([...this.elements, ...toAdd]);
205
+ }
206
+
207
+ get(index) {
208
+ if (index === undefined) return [...this.elements];
209
+ return index < 0 ? this.elements[this.length + index] : this.elements[index];
210
+ }
211
+
212
+ index(selector) {
213
+ if (selector === undefined) {
214
+ const el = this.first();
215
+ return el ? Array.from(el.parentElement.children).indexOf(el) : -1;
216
+ }
217
+ const target = (typeof selector === 'string')
218
+ ? document.querySelector(selector)
219
+ : selector;
220
+ return this.elements.indexOf(target);
221
+ }
222
+
94
223
  // --- Classes -------------------------------------------------------------
95
224
 
96
225
  addClass(...names) {
@@ -103,8 +232,12 @@ export class ZQueryCollection {
103
232
  return this.each((_, el) => el.classList.remove(...classes));
104
233
  }
105
234
 
106
- toggleClass(name, force) {
107
- return this.each((_, el) => el.classList.toggle(name, force));
235
+ toggleClass(...args) {
236
+ const force = typeof args[args.length - 1] === 'boolean' ? args.pop() : undefined;
237
+ const classes = args.flatMap(n => n.split(/\s+/));
238
+ return this.each((_, el) => {
239
+ classes.forEach(c => force !== undefined ? el.classList.toggle(c, force) : el.classList.toggle(c));
240
+ });
108
241
  }
109
242
 
110
243
  hasClass(name) {
@@ -158,6 +291,60 @@ export class ZQueryCollection {
158
291
  return el ? { top: el.offsetTop, left: el.offsetLeft } : null;
159
292
  }
160
293
 
294
+ scrollTop(value) {
295
+ if (value === undefined) {
296
+ const el = this.first();
297
+ return el === window ? window.scrollY : el?.scrollTop;
298
+ }
299
+ return this.each((_, el) => {
300
+ if (el === window) window.scrollTo(window.scrollX, value);
301
+ else el.scrollTop = value;
302
+ });
303
+ }
304
+
305
+ scrollLeft(value) {
306
+ if (value === undefined) {
307
+ const el = this.first();
308
+ return el === window ? window.scrollX : el?.scrollLeft;
309
+ }
310
+ return this.each((_, el) => {
311
+ if (el === window) window.scrollTo(value, window.scrollY);
312
+ else el.scrollLeft = value;
313
+ });
314
+ }
315
+
316
+ innerWidth() {
317
+ const el = this.first();
318
+ return el?.clientWidth;
319
+ }
320
+
321
+ innerHeight() {
322
+ const el = this.first();
323
+ return el?.clientHeight;
324
+ }
325
+
326
+ outerWidth(includeMargin = false) {
327
+ const el = this.first();
328
+ if (!el) return undefined;
329
+ let w = el.offsetWidth;
330
+ if (includeMargin) {
331
+ const style = getComputedStyle(el);
332
+ w += parseFloat(style.marginLeft) + parseFloat(style.marginRight);
333
+ }
334
+ return w;
335
+ }
336
+
337
+ outerHeight(includeMargin = false) {
338
+ const el = this.first();
339
+ if (!el) return undefined;
340
+ let h = el.offsetHeight;
341
+ if (includeMargin) {
342
+ const style = getComputedStyle(el);
343
+ h += parseFloat(style.marginTop) + parseFloat(style.marginBottom);
344
+ }
345
+ return h;
346
+ }
347
+
161
348
  // --- Content -------------------------------------------------------------
162
349
 
163
350
  html(content) {
@@ -237,6 +424,73 @@ export class ZQueryCollection {
237
424
  });
238
425
  }
239
426
 
427
+ appendTo(target) {
428
+ const dest = typeof target === 'string' ? document.querySelector(target) : target instanceof ZQueryCollection ? target.first() : target;
429
+ if (dest) this.each((_, el) => dest.appendChild(el));
430
+ return this;
431
+ }
432
+
433
+ prependTo(target) {
434
+ const dest = typeof target === 'string' ? document.querySelector(target) : target instanceof ZQueryCollection ? target.first() : target;
435
+ if (dest) this.each((_, el) => dest.insertBefore(el, dest.firstChild));
436
+ return this;
437
+ }
438
+
439
+ insertAfter(target) {
440
+ const ref = typeof target === 'string' ? document.querySelector(target) : target instanceof ZQueryCollection ? target.first() : target;
441
+ if (ref && ref.parentNode) this.each((_, el) => ref.parentNode.insertBefore(el, ref.nextSibling));
442
+ return this;
443
+ }
444
+
445
+ insertBefore(target) {
446
+ const ref = typeof target === 'string' ? document.querySelector(target) : target instanceof ZQueryCollection ? target.first() : target;
447
+ if (ref && ref.parentNode) this.each((_, el) => ref.parentNode.insertBefore(el, ref));
448
+ return this;
449
+ }
450
+
451
+ replaceAll(target) {
452
+ const targets = typeof target === 'string'
453
+ ? Array.from(document.querySelectorAll(target))
454
+ : target instanceof ZQueryCollection ? target.elements : [target];
455
+ targets.forEach((t, i) => {
456
+ const nodes = i === 0 ? this.elements : this.elements.map(el => el.cloneNode(true));
457
+ nodes.forEach(el => t.parentNode.insertBefore(el, t));
458
+ t.remove();
459
+ });
460
+ return this;
461
+ }
462
+
463
+ unwrap(selector) {
464
+ this.elements.forEach(el => {
465
+ const parent = el.parentElement;
466
+ if (!parent || parent === document.body) return;
467
+ if (selector && !parent.matches(selector)) return;
468
+ parent.replaceWith(...parent.childNodes);
469
+ });
470
+ return this;
471
+ }
472
+
473
+ wrapAll(wrapper) {
474
+ const w = typeof wrapper === 'string' ? createFragment(wrapper).firstElementChild : wrapper.cloneNode(true);
475
+ const first = this.first();
476
+ if (!first) return this;
477
+ first.parentNode.insertBefore(w, first);
478
+ this.each((_, el) => w.appendChild(el));
479
+ return this;
480
+ }
481
+
482
+ wrapInner(wrapper) {
483
+ return this.each((_, el) => {
484
+ const w = typeof wrapper === 'string' ? createFragment(wrapper).firstElementChild : wrapper.cloneNode(true);
485
+ while (el.firstChild) w.appendChild(el.firstChild);
486
+ el.appendChild(w);
487
+ });
488
+ }
489
+
490
+ detach() {
491
+ return this.each((_, el) => el.remove());
492
+ }
493
+
240
494
  // --- Visibility ----------------------------------------------------------
241
495
 
242
496
  show(display = '') {
@@ -262,9 +516,10 @@ export class ZQueryCollection {
262
516
  events.forEach(evt => {
263
517
  if (typeof selectorOrHandler === 'function') {
264
518
  el.addEventListener(evt, selectorOrHandler);
265
- } else {
266
- // Delegated event
519
+ } else if (typeof selectorOrHandler === 'string') {
520
+ // Delegated event — only works on elements that support closest()
267
521
  el.addEventListener(evt, (e) => {
522
+ if (!e.target || typeof e.target.closest !== 'function') return;
268
523
  const target = e.target.closest(selectorOrHandler);
269
524
  if (target && el.contains(target)) handler.call(target, e);
270
525
  });
@@ -297,6 +552,10 @@ export class ZQueryCollection {
297
552
  submit(fn) { return fn ? this.on('submit', fn) : this.trigger('submit'); }
298
553
  focus() { this.first()?.focus(); return this; }
299
554
  blur() { this.first()?.blur(); return this; }
555
+ hover(enterFn, leaveFn) {
556
+ this.on('mouseenter', enterFn);
557
+ return this.on('mouseleave', leaveFn || enterFn);
558
+ }
300
559
 
301
560
  // --- Animation -----------------------------------------------------------
302
561
 
@@ -328,6 +587,40 @@ export class ZQueryCollection {
328
587
  return this.animate({ opacity: '0' }, duration).then(col => col.hide());
329
588
  }
330
589
 
590
+ fadeToggle(duration = 300) {
591
+ return Promise.all(this.elements.map(el => {
592
+ const visible = getComputedStyle(el).opacity !== '0' && getComputedStyle(el).display !== 'none';
593
+ const col = new ZQueryCollection([el]);
594
+ return visible ? col.fadeOut(duration) : col.fadeIn(duration);
595
+ })).then(() => this);
596
+ }
597
+
598
+ fadeTo(duration, opacity) {
599
+ return this.animate({ opacity: String(opacity) }, duration);
600
+ }
601
+
602
+ slideDown(duration = 300) {
603
+ return this.each((_, el) => {
604
+ el.style.display = '';
605
+ el.style.overflow = 'hidden';
606
+ const h = el.scrollHeight + 'px';
607
+ el.style.maxHeight = '0';
608
+ el.style.transition = `max-height ${duration}ms ease`;
609
+ requestAnimationFrame(() => { el.style.maxHeight = h; });
610
+ setTimeout(() => { el.style.maxHeight = ''; el.style.overflow = ''; el.style.transition = ''; }, duration);
611
+ });
612
+ }
613
+
614
+ slideUp(duration = 300) {
615
+ return this.each((_, el) => {
616
+ el.style.overflow = 'hidden';
617
+ el.style.maxHeight = el.scrollHeight + 'px';
618
+ el.style.transition = `max-height ${duration}ms ease`;
619
+ requestAnimationFrame(() => { el.style.maxHeight = '0'; });
620
+ setTimeout(() => { el.style.display = 'none'; el.style.maxHeight = ''; el.style.overflow = ''; el.style.transition = ''; }, duration);
621
+ });
622
+ }
623
+
331
624
  slideToggle(duration = 300) {
332
625
  return this.each((_, el) => {
333
626
  if (el.style.display === 'none' || getComputedStyle(el).display === 'none') {
@@ -384,42 +677,40 @@ function createFragment(html) {
384
677
 
385
678
 
386
679
  // ---------------------------------------------------------------------------
387
- // $() — main selector / creator function (returns single element for CSS selectors)
680
+ // $() — main selector / creator (returns ZQueryCollection, like jQuery)
388
681
  // ---------------------------------------------------------------------------
389
682
  export function query(selector, context) {
390
683
  // null / undefined
391
- if (!selector) return null;
684
+ if (!selector) return new ZQueryCollection([]);
392
685
 
393
- // Already a collection — return first element
394
- if (selector instanceof ZQueryCollection) return selector.first();
686
+ // Already a collection — return as-is
687
+ if (selector instanceof ZQueryCollection) return selector;
395
688
 
396
- // DOM element or Window — return as-is
689
+ // DOM element or Window — wrap in collection
397
690
  if (selector instanceof Node || selector === window) {
398
- return selector;
691
+ return new ZQueryCollection([selector]);
399
692
  }
400
693
 
401
- // NodeList / HTMLCollection / Array — return first element
694
+ // NodeList / HTMLCollection / Array — wrap in collection
402
695
  if (selector instanceof NodeList || selector instanceof HTMLCollection || Array.isArray(selector)) {
403
- const arr = Array.from(selector);
404
- return arr[0] || null;
696
+ return new ZQueryCollection(Array.from(selector));
405
697
  }
406
698
 
407
- // HTML string → create elements, return first
699
+ // HTML string → create elements, wrap in collection
408
700
  if (typeof selector === 'string' && selector.trim().startsWith('<')) {
409
701
  const fragment = createFragment(selector);
410
- const els = [...fragment.childNodes].filter(n => n.nodeType === 1);
411
- return els[0] || null;
702
+ return new ZQueryCollection([...fragment.childNodes].filter(n => n.nodeType === 1));
412
703
  }
413
704
 
414
- // CSS selector string → querySelector (single element)
705
+ // CSS selector string → querySelectorAll (collection)
415
706
  if (typeof selector === 'string') {
416
707
  const root = context
417
708
  ? (typeof context === 'string' ? document.querySelector(context) : context)
418
709
  : document;
419
- return root.querySelector(selector);
710
+ return new ZQueryCollection([...root.querySelectorAll(selector)]);
420
711
  }
421
712
 
422
- return null;
713
+ return new ZQueryCollection([]);
423
714
  }
424
715
 
425
716
 
@@ -466,24 +757,18 @@ export function queryAll(selector, context) {
466
757
  // ---------------------------------------------------------------------------
467
758
  query.id = (id) => document.getElementById(id);
468
759
  query.class = (name) => document.querySelector(`.${name}`);
469
- query.classes = (name) => Array.from(document.getElementsByClassName(name));
470
- query.tag = (name) => Array.from(document.getElementsByTagName(name));
760
+ query.classes = (name) => new ZQueryCollection(Array.from(document.getElementsByClassName(name)));
761
+ query.tag = (name) => new ZQueryCollection(Array.from(document.getElementsByTagName(name)));
471
762
  Object.defineProperty(query, 'name', {
472
- value: (name) => Array.from(document.getElementsByName(name)),
763
+ value: (name) => new ZQueryCollection(Array.from(document.getElementsByName(name))),
473
764
  writable: true, configurable: true
474
765
  });
475
- query.attr = (attr, value) => Array.from(
476
- document.querySelectorAll(value !== undefined ? `[${attr}="${value}"]` : `[${attr}]`)
477
- );
478
- query.data = (key, value) => Array.from(
479
- document.querySelectorAll(value !== undefined ? `[data-${key}="${value}"]` : `[data-${key}]`)
480
- );
481
766
  query.children = (parentId) => {
482
767
  const p = document.getElementById(parentId);
483
- return p ? Array.from(p.children) : [];
768
+ return new ZQueryCollection(p ? Array.from(p.children) : []);
484
769
  };
485
770
 
486
- // Create element shorthand
771
+ // Create element shorthand — returns ZQueryCollection for chaining
487
772
  query.create = (tag, attrs = {}, ...children) => {
488
773
  const el = document.createElement(tag);
489
774
  for (const [k, v] of Object.entries(attrs)) {
@@ -497,7 +782,7 @@ query.create = (tag, attrs = {}, ...children) => {
497
782
  if (typeof child === 'string') el.appendChild(document.createTextNode(child));
498
783
  else if (child instanceof Node) el.appendChild(child);
499
784
  });
500
- return el;
785
+ return new ZQueryCollection(el);
501
786
  };
502
787
 
503
788
  // DOM ready
@@ -506,17 +791,24 @@ query.ready = (fn) => {
506
791
  else document.addEventListener('DOMContentLoaded', fn);
507
792
  };
508
793
 
509
- // Global event listeners — supports direct and delegated forms
794
+ // Global event listeners — supports direct, delegated, and target-bound forms
510
795
  // $.on('keydown', handler) → direct listener on document
511
796
  // $.on('click', '.btn', handler) → delegated via closest()
797
+ // $.on('scroll', window, handler) → direct listener on target
512
798
  query.on = (event, selectorOrHandler, handler) => {
513
799
  if (typeof selectorOrHandler === 'function') {
514
800
  // 2-arg: direct document listener (keydown, resize, etc.)
515
801
  document.addEventListener(event, selectorOrHandler);
516
802
  return;
517
803
  }
518
- // 3-arg: delegated
804
+ // EventTarget (window, element, etc.) — direct listener on target
805
+ if (typeof selectorOrHandler === 'object' && typeof selectorOrHandler.addEventListener === 'function') {
806
+ selectorOrHandler.addEventListener(event, handler);
807
+ return;
808
+ }
809
+ // 3-arg string: delegated
519
810
  document.addEventListener(event, (e) => {
811
+ if (!e.target || typeof e.target.closest !== 'function') return;
520
812
  const target = e.target.closest(selectorOrHandler);
521
813
  if (target) handler.call(target, e);
522
814
  });