zero-query 0.9.5 → 0.9.6
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/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +69 -8
- package/dist/zquery.min.js +2 -2
- package/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/component.js +66 -5
- package/tests/component.test.js +1185 -0
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
| Module | Highlights |
|
|
23
23
|
| --- | --- |
|
|
24
|
-
| **Components** | Reactive state, template literals, `@event` delegation (
|
|
24
|
+
| **Components** | Reactive state, template literals, `@event` delegation (22 modifiers — key filters, system keys, `.outside`, timing, and more), `z-model` two-way binding (with `z-trim`, `z-number`, `z-lazy`, `z-debounce`, `z-uppercase`, `z-lowercase`), computed properties, watch callbacks, slot-based content projection, directives (`z-if`/`z-else-if`/`z-else`, `z-for`, `z-show`, `z-bind`/`:attr`, `z-class`, `z-style`, `z-text`, `z-html`, `z-ref`, `z-cloak`, `z-pre`, `z-key`, `z-skip`), DOM morphing engine with LIS-based keyed reconciliation (no innerHTML rebuild), CSP-safe expression evaluation with AST caching, scoped styles, external templates (`templateUrl` / `styleUrl`), lifecycle hooks, auto-injected base styles |
|
|
25
25
|
| **Router** | History & hash mode, route params (`:id`), wildcards, guards (`beforeEach`/`afterEach`), lazy loading, `z-link` navigation, `z-to-top` scroll modifier (`instant`/`smooth`), sub-route history substates (`pushSubstate`/`onSubstate`) |
|
|
26
26
|
| **Directives** | `z-if`, `z-for`, `z-model`, `z-show`, `z-bind`, `z-class`, `z-style`, `z-text`, `z-html`, `z-ref`, `z-cloak`, `z-pre`, `z-key`, `z-skip` — 17 built-in template directives |
|
|
27
27
|
| **Reactive** | Deep proxy reactivity, Signals (`.value`, `.peek()`), computed values, effects (auto-tracked with dispose) |
|
package/dist/zquery.dist.zip
CHANGED
|
Binary file
|
package/dist/zquery.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* zQuery (zeroQuery) v0.9.
|
|
2
|
+
* zQuery (zeroQuery) v0.9.6
|
|
3
3
|
* Lightweight Frontend Library
|
|
4
4
|
* https://github.com/tonywied17/zero-query
|
|
5
5
|
* (c) 2026 Anthony Wiedman - MIT License
|
|
@@ -2887,7 +2887,7 @@ class Component {
|
|
|
2887
2887
|
const defaultSlotNodes = [];
|
|
2888
2888
|
[...el.childNodes].forEach(node => {
|
|
2889
2889
|
if (node.nodeType === 1 && node.hasAttribute('slot')) {
|
|
2890
|
-
const slotName = node.getAttribute('slot');
|
|
2890
|
+
const slotName = node.getAttribute('slot') || 'default';
|
|
2891
2891
|
if (!this._slotContent[slotName]) this._slotContent[slotName] = '';
|
|
2892
2892
|
this._slotContent[slotName] += node.outerHTML;
|
|
2893
2893
|
} else if (node.nodeType === 1 || (node.nodeType === 3 && node.textContent.trim())) {
|
|
@@ -3296,6 +3296,24 @@ class Component {
|
|
|
3296
3296
|
for (const [event, bindings] of eventMap) {
|
|
3297
3297
|
this._attachDelegatedEvent(event, bindings);
|
|
3298
3298
|
}
|
|
3299
|
+
|
|
3300
|
+
// .outside — attach a document-level listener for bindings that need
|
|
3301
|
+
// to detect clicks/events outside their element.
|
|
3302
|
+
this._outsideListeners = this._outsideListeners || [];
|
|
3303
|
+
for (const [event, bindings] of eventMap) {
|
|
3304
|
+
for (const binding of bindings) {
|
|
3305
|
+
if (!binding.modifiers.includes('outside')) continue;
|
|
3306
|
+
const outsideHandler = (e) => {
|
|
3307
|
+
if (binding.el.contains(e.target)) return;
|
|
3308
|
+
const match = binding.methodExpr.match(/^(\w+)(?:\(([^)]*)\))?$/);
|
|
3309
|
+
if (!match) return;
|
|
3310
|
+
const fn = this[match[1]];
|
|
3311
|
+
if (typeof fn === 'function') fn.call(this, e);
|
|
3312
|
+
};
|
|
3313
|
+
document.addEventListener(event, outsideHandler, true);
|
|
3314
|
+
this._outsideListeners.push({ event, handler: outsideHandler });
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3299
3317
|
}
|
|
3300
3318
|
|
|
3301
3319
|
// Attach a single delegated listener for an event type
|
|
@@ -3340,6 +3358,28 @@ class Component {
|
|
|
3340
3358
|
// .self — only fire if target is the element itself
|
|
3341
3359
|
if (modifiers.includes('self') && e.target !== el) continue;
|
|
3342
3360
|
|
|
3361
|
+
// .outside — only fire if event target is OUTSIDE the element
|
|
3362
|
+
if (modifiers.includes('outside')) {
|
|
3363
|
+
if (el.contains(e.target)) continue;
|
|
3364
|
+
}
|
|
3365
|
+
|
|
3366
|
+
// Key modifiers — filter keyboard events by key
|
|
3367
|
+
const _keyMap = { enter: 'Enter', escape: 'Escape', tab: 'Tab', space: ' ', delete: 'Delete|Backspace', up: 'ArrowUp', down: 'ArrowDown', left: 'ArrowLeft', right: 'ArrowRight' };
|
|
3368
|
+
let keyFiltered = false;
|
|
3369
|
+
for (const mod of modifiers) {
|
|
3370
|
+
if (_keyMap[mod]) {
|
|
3371
|
+
const keys = _keyMap[mod].split('|');
|
|
3372
|
+
if (!e.key || !keys.includes(e.key)) { keyFiltered = true; break; }
|
|
3373
|
+
}
|
|
3374
|
+
}
|
|
3375
|
+
if (keyFiltered) continue;
|
|
3376
|
+
|
|
3377
|
+
// System key modifiers — require modifier keys to be held
|
|
3378
|
+
if (modifiers.includes('ctrl') && !e.ctrlKey) continue;
|
|
3379
|
+
if (modifiers.includes('shift') && !e.shiftKey) continue;
|
|
3380
|
+
if (modifiers.includes('alt') && !e.altKey) continue;
|
|
3381
|
+
if (modifiers.includes('meta') && !e.metaKey) continue;
|
|
3382
|
+
|
|
3343
3383
|
// Handle modifiers
|
|
3344
3384
|
if (modifiers.includes('prevent')) e.preventDefault();
|
|
3345
3385
|
if (modifiers.includes('stop')) {
|
|
@@ -3425,9 +3465,12 @@ class Component {
|
|
|
3425
3465
|
// textarea, select (single & multiple), contenteditable
|
|
3426
3466
|
// Nested state keys: z-model="user.name" → this.state.user.name
|
|
3427
3467
|
// Modifiers (boolean attributes on the same element):
|
|
3428
|
-
// z-lazy
|
|
3429
|
-
// z-trim
|
|
3430
|
-
// z-number
|
|
3468
|
+
// z-lazy — listen on 'change' instead of 'input' (update on blur / commit)
|
|
3469
|
+
// z-trim — trim whitespace before writing to state
|
|
3470
|
+
// z-number — force Number() conversion regardless of input type
|
|
3471
|
+
// z-debounce — debounce state writes (default 250ms, or z-debounce="300")
|
|
3472
|
+
// z-uppercase — convert string to uppercase before writing to state
|
|
3473
|
+
// z-lowercase — convert string to lowercase before writing to state
|
|
3431
3474
|
//
|
|
3432
3475
|
// Writes to reactive state so the rest of the UI stays in sync.
|
|
3433
3476
|
// Focus and cursor position are preserved in _render() via focusInfo.
|
|
@@ -3443,6 +3486,10 @@ class Component {
|
|
|
3443
3486
|
const isLazy = el.hasAttribute('z-lazy');
|
|
3444
3487
|
const isTrim = el.hasAttribute('z-trim');
|
|
3445
3488
|
const isNum = el.hasAttribute('z-number');
|
|
3489
|
+
const isUpper = el.hasAttribute('z-uppercase');
|
|
3490
|
+
const isLower = el.hasAttribute('z-lowercase');
|
|
3491
|
+
const hasDebounce = el.hasAttribute('z-debounce');
|
|
3492
|
+
const debounceMs = hasDebounce ? (parseInt(el.getAttribute('z-debounce'), 10) || 250) : 0;
|
|
3446
3493
|
|
|
3447
3494
|
// Read current state value (supports dot-path keys)
|
|
3448
3495
|
const currentVal = _getPath(this.state, key);
|
|
@@ -3483,6 +3530,8 @@ class Component {
|
|
|
3483
3530
|
|
|
3484
3531
|
// Apply modifiers
|
|
3485
3532
|
if (isTrim && typeof val === 'string') val = val.trim();
|
|
3533
|
+
if (isUpper && typeof val === 'string') val = val.toUpperCase();
|
|
3534
|
+
if (isLower && typeof val === 'string') val = val.toLowerCase();
|
|
3486
3535
|
if (isNum || type === 'number' || type === 'range') val = Number(val);
|
|
3487
3536
|
|
|
3488
3537
|
// Write through the reactive proxy (triggers re-render).
|
|
@@ -3490,7 +3539,15 @@ class Component {
|
|
|
3490
3539
|
_setPath(this.state, key, val);
|
|
3491
3540
|
};
|
|
3492
3541
|
|
|
3493
|
-
|
|
3542
|
+
if (hasDebounce) {
|
|
3543
|
+
let timer = null;
|
|
3544
|
+
el.addEventListener(event, () => {
|
|
3545
|
+
clearTimeout(timer);
|
|
3546
|
+
timer = setTimeout(handler, debounceMs);
|
|
3547
|
+
});
|
|
3548
|
+
} else {
|
|
3549
|
+
el.addEventListener(event, handler);
|
|
3550
|
+
}
|
|
3494
3551
|
});
|
|
3495
3552
|
}
|
|
3496
3553
|
|
|
@@ -3776,6 +3833,10 @@ class Component {
|
|
|
3776
3833
|
}
|
|
3777
3834
|
this._listeners.forEach(({ event, handler }) => this._el.removeEventListener(event, handler));
|
|
3778
3835
|
this._listeners = [];
|
|
3836
|
+
if (this._outsideListeners) {
|
|
3837
|
+
this._outsideListeners.forEach(({ event, handler }) => document.removeEventListener(event, handler, true));
|
|
3838
|
+
this._outsideListeners = [];
|
|
3839
|
+
}
|
|
3779
3840
|
this._delegatedEvents = null;
|
|
3780
3841
|
this._eventBindings = null;
|
|
3781
3842
|
// Clear any pending debounce/throttle timers to prevent stale closures.
|
|
@@ -5737,8 +5798,8 @@ $.guardCallback = guardCallback;
|
|
|
5737
5798
|
$.validate = validate;
|
|
5738
5799
|
|
|
5739
5800
|
// --- Meta ------------------------------------------------------------------
|
|
5740
|
-
$.version = '0.9.
|
|
5741
|
-
$.libSize = '~
|
|
5801
|
+
$.version = '0.9.6';
|
|
5802
|
+
$.libSize = '~100 KB';
|
|
5742
5803
|
$.meta = {}; // populated at build time by CLI bundler
|
|
5743
5804
|
|
|
5744
5805
|
$.noConflict = () => {
|