zero-query 0.9.8 → 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/README.md +55 -31
- package/cli/args.js +1 -1
- package/cli/commands/build.js +2 -2
- package/cli/commands/bundle.js +15 -15
- package/cli/commands/create.js +41 -7
- package/cli/commands/dev/devtools/index.js +1 -1
- package/cli/commands/dev/devtools/js/core.js +14 -14
- package/cli/commands/dev/devtools/js/elements.js +4 -4
- package/cli/commands/dev/devtools/js/stats.js +1 -1
- package/cli/commands/dev/devtools/styles.css +2 -2
- package/cli/commands/dev/index.js +2 -2
- package/cli/commands/dev/logger.js +1 -1
- package/cli/commands/dev/overlay.js +21 -14
- package/cli/commands/dev/server.js +5 -5
- package/cli/commands/dev/validator.js +7 -7
- package/cli/commands/dev/watcher.js +6 -6
- package/cli/help.js +4 -2
- package/cli/index.js +2 -2
- package/cli/scaffold/default/app/app.js +17 -18
- package/cli/scaffold/default/app/components/about.js +9 -9
- package/cli/scaffold/default/app/components/api-demo.js +6 -6
- package/cli/scaffold/default/app/components/contact-card.js +4 -4
- package/cli/scaffold/default/app/components/contacts/contacts.css +2 -2
- package/cli/scaffold/default/app/components/contacts/contacts.html +3 -3
- package/cli/scaffold/default/app/components/contacts/contacts.js +11 -11
- package/cli/scaffold/default/app/components/counter.js +8 -8
- package/cli/scaffold/default/app/components/home.js +13 -13
- package/cli/scaffold/default/app/components/not-found.js +1 -1
- package/cli/scaffold/default/app/components/playground/playground.css +1 -1
- package/cli/scaffold/default/app/components/playground/playground.html +11 -11
- package/cli/scaffold/default/app/components/playground/playground.js +11 -11
- package/cli/scaffold/default/app/components/todos.js +8 -8
- package/cli/scaffold/default/app/components/toolkit/toolkit.css +1 -1
- package/cli/scaffold/default/app/components/toolkit/toolkit.html +4 -4
- package/cli/scaffold/default/app/components/toolkit/toolkit.js +7 -7
- package/cli/scaffold/default/app/routes.js +1 -1
- package/cli/scaffold/default/app/store.js +1 -1
- package/cli/scaffold/default/global.css +2 -2
- package/cli/scaffold/default/index.html +2 -2
- package/cli/scaffold/minimal/app/app.js +6 -7
- package/cli/scaffold/minimal/app/components/about.js +5 -5
- package/cli/scaffold/minimal/app/components/counter.js +6 -6
- package/cli/scaffold/minimal/app/components/home.js +8 -8
- package/cli/scaffold/minimal/app/components/not-found.js +1 -1
- package/cli/scaffold/minimal/app/routes.js +1 -1
- package/cli/scaffold/minimal/app/store.js +1 -1
- package/cli/scaffold/minimal/global.css +2 -2
- package/cli/scaffold/minimal/index.html +1 -1
- package/cli/scaffold/ssr/app/app.js +29 -0
- package/cli/scaffold/ssr/app/components/about.js +28 -0
- package/cli/scaffold/ssr/app/components/home.js +37 -0
- package/cli/scaffold/ssr/app/components/not-found.js +15 -0
- package/cli/scaffold/ssr/app/routes.js +6 -0
- package/cli/scaffold/ssr/global.css +113 -0
- package/cli/scaffold/ssr/index.html +31 -0
- package/cli/scaffold/ssr/package.json +8 -0
- package/cli/scaffold/ssr/server/index.js +118 -0
- package/cli/utils.js +6 -6
- package/dist/zquery.dist.zip +0 -0
- package/dist/zquery.js +565 -228
- package/dist/zquery.min.js +2 -2
- package/index.d.ts +25 -12
- package/index.js +11 -7
- package/package.json +9 -3
- package/src/component.js +64 -63
- package/src/core.js +15 -15
- package/src/diff.js +38 -38
- package/src/errors.js +72 -18
- package/src/expression.js +15 -17
- package/src/http.js +4 -4
- package/src/package.json +1 -0
- package/src/reactive.js +75 -9
- package/src/router.js +104 -24
- package/src/ssr.js +133 -39
- package/src/store.js +103 -21
- package/src/utils.js +64 -12
- package/tests/audit.test.js +143 -15
- package/tests/cli.test.js +20 -20
- package/tests/component.test.js +121 -121
- package/tests/core.test.js +56 -56
- package/tests/diff.test.js +42 -42
- package/tests/errors.test.js +425 -147
- package/tests/expression.test.js +58 -53
- package/tests/http.test.js +20 -20
- package/tests/reactive.test.js +185 -24
- package/tests/router.test.js +501 -74
- package/tests/ssr.test.js +444 -10
- package/tests/store.test.js +264 -23
- package/tests/utils.test.js +163 -26
- package/types/collection.d.ts +2 -2
- package/types/component.d.ts +5 -5
- package/types/errors.d.ts +36 -4
- package/types/http.d.ts +3 -3
- package/types/misc.d.ts +9 -9
- package/types/reactive.d.ts +25 -3
- package/types/router.d.ts +10 -6
- package/types/ssr.d.ts +22 -2
- package/types/store.d.ts +40 -5
- package/types/utils.d.ts +1 -1
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* cli/commands/dev/overlay.js
|
|
2
|
+
* cli/commands/dev/overlay.js - Client-side error overlay + SSE live-reload
|
|
3
3
|
*
|
|
4
4
|
* Returns an HTML <script> snippet that is injected before </body> in
|
|
5
5
|
* every HTML response served by the dev server. Responsibilities:
|
|
6
6
|
*
|
|
7
|
-
* 1. Error Overlay
|
|
7
|
+
* 1. Error Overlay - full-screen dark overlay with code frames, stack
|
|
8
8
|
* traces, and ZQueryError metadata. Dismissable via Esc or ×.
|
|
9
|
-
* 2. Runtime error hooks
|
|
9
|
+
* 2. Runtime error hooks - window.onerror, unhandledrejection, AND
|
|
10
10
|
* the zQuery $.onError() hook so framework-level errors are
|
|
11
11
|
* surfaced in the overlay automatically.
|
|
12
|
-
* 3. SSE connection
|
|
12
|
+
* 3. SSE connection - listens for reload / css / error:syntax /
|
|
13
13
|
* error:clear events from the dev server watcher.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
// The snippet is a self-contained IIFE
|
|
18
|
+
// The snippet is a self-contained IIFE - no external dependencies.
|
|
19
19
|
// It must work in all browsers that support EventSource (IE11 excluded).
|
|
20
20
|
|
|
21
21
|
const OVERLAY_SCRIPT = `<script>
|
|
@@ -239,7 +239,7 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
239
239
|
// Hook into zQuery's $.onError() when the library is loaded
|
|
240
240
|
// =====================================================================
|
|
241
241
|
function hookZQueryErrors() {
|
|
242
|
-
// $.onError is set by the framework
|
|
242
|
+
// $.onError is set by the framework - wait for it
|
|
243
243
|
if (typeof $ !== 'undefined' && typeof $.onError === 'function') {
|
|
244
244
|
$.onError(function(zqErr) {
|
|
245
245
|
var data = {
|
|
@@ -298,7 +298,7 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
298
298
|
});
|
|
299
299
|
|
|
300
300
|
// 2) Try hot-swapping scoped <style data-zq-style-urls> elements
|
|
301
|
-
// These come from component styleUrl
|
|
301
|
+
// These come from component styleUrl - the CSS was fetched, scoped,
|
|
302
302
|
// and injected as an inline <style>. We re-fetch and re-scope it.
|
|
303
303
|
if (!matched) {
|
|
304
304
|
var scopedEls = document.querySelectorAll('style[data-zq-style-urls]');
|
|
@@ -315,9 +315,13 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
315
315
|
|
|
316
316
|
// Re-fetch all style URLs (cache-busted)
|
|
317
317
|
var urlList = urls.split(' ').filter(Boolean);
|
|
318
|
+
var prevCSS = el.textContent; // preserve current styles as rollback
|
|
318
319
|
Promise.all(urlList.map(function(u) {
|
|
319
320
|
return fetch(u + (u.indexOf('?') >= 0 ? '&' : '?') + '_zqr=' + Date.now())
|
|
320
|
-
.then(function(r) {
|
|
321
|
+
.then(function(r) {
|
|
322
|
+
if (!r.ok) throw new Error('CSS fetch failed: ' + r.status);
|
|
323
|
+
return r.text();
|
|
324
|
+
});
|
|
321
325
|
})).then(function(results) {
|
|
322
326
|
var raw = (inlineStyles ? inlineStyles + '\\n' : '') + results.join('\\n');
|
|
323
327
|
// Re-scope CSS with the same scope attribute
|
|
@@ -332,11 +336,14 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
332
336
|
});
|
|
333
337
|
}
|
|
334
338
|
el.textContent = raw;
|
|
339
|
+
}).catch(function() {
|
|
340
|
+
// Restore previous CSS on failure to prevent blank page
|
|
341
|
+
el.textContent = prevCSS;
|
|
335
342
|
});
|
|
336
343
|
});
|
|
337
344
|
}
|
|
338
345
|
|
|
339
|
-
// 3) Nothing matched
|
|
346
|
+
// 3) Nothing matched - fall back to full reload
|
|
340
347
|
if (!matched) { location.reload(); }
|
|
341
348
|
});
|
|
342
349
|
|
|
@@ -362,7 +369,7 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
362
369
|
connect();
|
|
363
370
|
|
|
364
371
|
// =====================================================================
|
|
365
|
-
// Fetch / $.http Interceptor
|
|
372
|
+
// Fetch / $.http Interceptor - pretty console logging
|
|
366
373
|
// =====================================================================
|
|
367
374
|
var __zqChannel;
|
|
368
375
|
try { __zqChannel = new BroadcastChannel('__zq_devtools'); } catch(e) {}
|
|
@@ -473,7 +480,7 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
473
480
|
};
|
|
474
481
|
|
|
475
482
|
// =====================================================================
|
|
476
|
-
// Morph instrumentation
|
|
483
|
+
// Morph instrumentation - hook via window.__zqMorphHook (set by diff.js)
|
|
477
484
|
// =====================================================================
|
|
478
485
|
window.__zqMorphHook = function(el, elapsed) {
|
|
479
486
|
__zqMorphCount++;
|
|
@@ -505,7 +512,7 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
505
512
|
};
|
|
506
513
|
|
|
507
514
|
// =====================================================================
|
|
508
|
-
// Render instrumentation
|
|
515
|
+
// Render instrumentation - hook for first-renders & route swaps
|
|
509
516
|
// =====================================================================
|
|
510
517
|
window.__zqRenderHook = function(el, elapsed, kind, name) {
|
|
511
518
|
__zqRenderCount++;
|
|
@@ -538,7 +545,7 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
538
545
|
};
|
|
539
546
|
|
|
540
547
|
// =====================================================================
|
|
541
|
-
// Router instrumentation
|
|
548
|
+
// Router instrumentation - history state tracking for devtools
|
|
542
549
|
// =====================================================================
|
|
543
550
|
var __zqRouterEvents = [];
|
|
544
551
|
|
|
@@ -613,7 +620,7 @@ const OVERLAY_SCRIPT = `<script>
|
|
|
613
620
|
});
|
|
614
621
|
|
|
615
622
|
// =====================================================================
|
|
616
|
-
// Dev Toolbar
|
|
623
|
+
// Dev Toolbar - expandable floating bar with stats
|
|
617
624
|
// =====================================================================
|
|
618
625
|
var devBar;
|
|
619
626
|
var __zqBarExpanded = false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* cli/commands/dev/server.js
|
|
2
|
+
* cli/commands/dev/server.js - HTTP server & SSE broadcasting
|
|
3
3
|
*
|
|
4
4
|
* Creates the zero-http app, serves static files, injects the
|
|
5
5
|
* error-overlay snippet into HTML responses, and manages the
|
|
@@ -70,10 +70,10 @@ function promptInstall() {
|
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* @param {object} opts
|
|
73
|
-
* @param {string} opts.root
|
|
74
|
-
* @param {string} opts.htmlEntry
|
|
73
|
+
* @param {string} opts.root - absolute path to project root
|
|
74
|
+
* @param {string} opts.htmlEntry - e.g. 'index.html'
|
|
75
75
|
* @param {number} opts.port
|
|
76
|
-
* @param {boolean} opts.noIntercept
|
|
76
|
+
* @param {boolean} opts.noIntercept - skip zquery.min.js auto-resolve
|
|
77
77
|
* @returns {Promise<{ app, pool: SSEPool, listen: Function }>}
|
|
78
78
|
*/
|
|
79
79
|
async function createServer({ root, htmlEntry, port, noIntercept }) {
|
|
@@ -136,7 +136,7 @@ async function createServer({ root, htmlEntry, port, noIntercept }) {
|
|
|
136
136
|
// ---- Static files ----
|
|
137
137
|
app.use(serveStatic(root, { index: false, dotfiles: 'ignore' }));
|
|
138
138
|
|
|
139
|
-
// ---- SPA fallback
|
|
139
|
+
// ---- SPA fallback - inject overlay/SSE snippet ----
|
|
140
140
|
app.get('*', (req, res) => {
|
|
141
141
|
if (path.extname(req.url) && path.extname(req.url) !== '.html') {
|
|
142
142
|
res.status(404).send('Not Found');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* cli/commands/dev/validator.js
|
|
2
|
+
* cli/commands/dev/validator.js - JS syntax validation
|
|
3
3
|
*
|
|
4
4
|
* Pre-validates JavaScript files on save using Node's VM module.
|
|
5
5
|
* Returns structured error descriptors with code frames compatible
|
|
@@ -19,9 +19,9 @@ const vm = require('vm');
|
|
|
19
19
|
* Build a code frame showing ~4 lines of context around the error
|
|
20
20
|
* with a caret pointer at the offending column.
|
|
21
21
|
*
|
|
22
|
-
* @param {string} source
|
|
23
|
-
* @param {number} line
|
|
24
|
-
* @param {number} column
|
|
22
|
+
* @param {string} source - full file contents
|
|
23
|
+
* @param {number} line - 1-based line number
|
|
24
|
+
* @param {number} column - 1-based column number
|
|
25
25
|
* @returns {string}
|
|
26
26
|
*/
|
|
27
27
|
function generateCodeFrame(source, line, column) {
|
|
@@ -52,9 +52,9 @@ function generateCodeFrame(source, line, column) {
|
|
|
52
52
|
* Strips ESM import/export statements (preserving line numbers) so the
|
|
53
53
|
* VM can parse module-style code, then compiles via vm.Script.
|
|
54
54
|
*
|
|
55
|
-
* @param {string} filePath
|
|
56
|
-
* @param {string} relPath
|
|
57
|
-
* @returns {object|null}
|
|
55
|
+
* @param {string} filePath - absolute path to the file
|
|
56
|
+
* @param {string} relPath - display-friendly relative path
|
|
57
|
+
* @returns {object|null} - error descriptor, or null if valid
|
|
58
58
|
*/
|
|
59
59
|
function validateJS(filePath, relPath) {
|
|
60
60
|
let source;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* cli/commands/dev/watcher.js
|
|
2
|
+
* cli/commands/dev/watcher.js - File system watcher
|
|
3
3
|
*
|
|
4
4
|
* Recursively watches the project root for file changes, validates
|
|
5
5
|
* JS files for syntax errors, and broadcasts reload / CSS hot-swap /
|
|
@@ -44,7 +44,7 @@ function collectWatchDirs(dir) {
|
|
|
44
44
|
if (!entry.isDirectory() || IGNORE_DIRS.has(entry.name)) continue;
|
|
45
45
|
dirs.push(...collectWatchDirs(path.join(dir, entry.name)));
|
|
46
46
|
}
|
|
47
|
-
} catch { /* unreadable dir
|
|
47
|
+
} catch { /* unreadable dir - skip */ }
|
|
48
48
|
return dirs;
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -56,8 +56,8 @@ function collectWatchDirs(dir) {
|
|
|
56
56
|
* Start watching `root` for file changes.
|
|
57
57
|
*
|
|
58
58
|
* @param {object} opts
|
|
59
|
-
* @param {string} opts.root
|
|
60
|
-
* @param {SSEPool} opts.pool
|
|
59
|
+
* @param {string} opts.root - absolute project root
|
|
60
|
+
* @param {SSEPool} opts.pool - SSE broadcast pool
|
|
61
61
|
* @returns {{ dirs: string[], destroy: Function }}
|
|
62
62
|
*/
|
|
63
63
|
function startWatcher({ root, pool, bundleMode, serveRoot }) {
|
|
@@ -121,7 +121,7 @@ function startWatcher({ root, pool, bundleMode, serveRoot }) {
|
|
|
121
121
|
pool.broadcast('error:syntax', JSON.stringify(err));
|
|
122
122
|
return;
|
|
123
123
|
}
|
|
124
|
-
// File was fixed
|
|
124
|
+
// File was fixed - clear previous overlay
|
|
125
125
|
if (currentError === rel) {
|
|
126
126
|
currentError = null;
|
|
127
127
|
pool.broadcast('error:clear', '');
|
|
@@ -158,7 +158,7 @@ function startWatcher({ root, pool, bundleMode, serveRoot }) {
|
|
|
158
158
|
}, 100);
|
|
159
159
|
});
|
|
160
160
|
watchers.push(watcher);
|
|
161
|
-
} catch { /* dir became inaccessible
|
|
161
|
+
} catch { /* dir became inaccessible - skip */ }
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
function destroy() {
|
package/cli/help.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// cli/help.js
|
|
1
|
+
// cli/help.js - show CLI usage information
|
|
2
2
|
|
|
3
3
|
function showHelp() {
|
|
4
4
|
console.log(`
|
|
@@ -11,6 +11,8 @@ function showHelp() {
|
|
|
11
11
|
(defaults to the current directory)
|
|
12
12
|
--minimal, -m Use the minimal template (home, counter, about)
|
|
13
13
|
instead of the full-featured default scaffold
|
|
14
|
+
--ssr, -s Use the SSR template - includes server/index.js
|
|
15
|
+
SSR HTTP server with shared component definitions
|
|
14
16
|
|
|
15
17
|
dev [root] Start a dev server with live-reload
|
|
16
18
|
--port, -p <number> Port number (default: 3100)
|
|
@@ -67,7 +69,7 @@ function showHelp() {
|
|
|
67
69
|
zquery dev --port 8080 custom port
|
|
68
70
|
|
|
69
71
|
The dev server includes a full-screen error overlay:
|
|
70
|
-
• JS files are syntax-checked on save
|
|
72
|
+
• JS files are syntax-checked on save - errors block reload
|
|
71
73
|
and show an overlay with exact file, line:column, and code frame
|
|
72
74
|
• Runtime errors and unhandled rejections are also captured
|
|
73
75
|
• The overlay auto-clears when the file is fixed and saved
|
package/cli/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
// app.js
|
|
1
|
+
// app.js - Application entry point
|
|
2
2
|
//
|
|
3
3
|
// Bootstraps the app: imports components, sets up routing,
|
|
4
4
|
// wires the responsive sidebar, and connects the store.
|
|
5
5
|
//
|
|
6
6
|
// Key APIs used:
|
|
7
|
-
// $.router
|
|
8
|
-
// $.ready
|
|
9
|
-
// $.bus
|
|
10
|
-
// $.on
|
|
11
|
-
// $.storage
|
|
12
|
-
// $.create
|
|
7
|
+
// $.router - SPA navigation (history mode)
|
|
8
|
+
// $.ready - run after DOM is loaded
|
|
9
|
+
// $.bus - event bus (toast notifications, contact card)
|
|
10
|
+
// $.on - global delegated event listeners
|
|
11
|
+
// $.storage - localStorage wrapper
|
|
12
|
+
// $.create - create DOM elements
|
|
13
13
|
|
|
14
14
|
import './store.js';
|
|
15
15
|
import './components/home.js';
|
|
@@ -25,16 +25,15 @@ import './components/not-found.js';
|
|
|
25
25
|
import { routes } from './routes.js';
|
|
26
26
|
|
|
27
27
|
// ---------------------------------------------------------------------------
|
|
28
|
-
// Router
|
|
28
|
+
// Router - SPA navigation with history mode
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
30
30
|
const router = $.router({
|
|
31
|
-
el: '#app', //@ Mount point (Set in index.html)
|
|
32
31
|
routes,
|
|
33
32
|
fallback: 'not-found',
|
|
34
33
|
mode: 'history'
|
|
35
34
|
});
|
|
36
35
|
|
|
37
|
-
// Post-navigation hook
|
|
36
|
+
// Post-navigation hook - track page views on every navigation
|
|
38
37
|
router.afterEach((to) => {
|
|
39
38
|
const store = $.getStore('main');
|
|
40
39
|
if (store) store.dispatch('incrementVisits');
|
|
@@ -65,17 +64,17 @@ function toggleMobileMenu(open) {
|
|
|
65
64
|
|
|
66
65
|
function closeMobileMenu() { toggleMobileMenu(false); }
|
|
67
66
|
|
|
68
|
-
// $.on
|
|
67
|
+
// $.on - global delegated event listeners
|
|
69
68
|
$.on('click', '#menu-toggle', () => toggleMobileMenu(!$sidebar.hasClass('open')));
|
|
70
69
|
$.on('click', '#overlay', closeMobileMenu);
|
|
71
70
|
|
|
72
|
-
// Close sidebar on Escape key
|
|
71
|
+
// Close sidebar on Escape key - using $.on direct (no selector needed)
|
|
73
72
|
$.on('keydown', (e) => {
|
|
74
73
|
if (e.key === 'Escape') closeMobileMenu();
|
|
75
74
|
});
|
|
76
75
|
|
|
77
76
|
// ---------------------------------------------------------------------------
|
|
78
|
-
// Sidebar stats panel
|
|
77
|
+
// Sidebar stats panel - collapsible, live-updating from $.store
|
|
79
78
|
// Starts expanded by default. Open/closed state saved via $.storage.
|
|
80
79
|
// ---------------------------------------------------------------------------
|
|
81
80
|
$.on('click', '#stats-toggle', () => {
|
|
@@ -100,7 +99,7 @@ function updateSidebarStats() {
|
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
// ---------------------------------------------------------------------------
|
|
103
|
-
// Sidebar contacts
|
|
102
|
+
// Sidebar contacts - live status indicators from $.store
|
|
104
103
|
// ---------------------------------------------------------------------------
|
|
105
104
|
function updateSidebarContacts() {
|
|
106
105
|
const store = $.getStore('main');
|
|
@@ -145,7 +144,7 @@ $.bus.on('toast', ({ message, type = 'info' }) => {
|
|
|
145
144
|
});
|
|
146
145
|
|
|
147
146
|
// ---------------------------------------------------------------------------
|
|
148
|
-
// On DOM ready
|
|
147
|
+
// On DOM ready - final setup
|
|
149
148
|
// ---------------------------------------------------------------------------
|
|
150
149
|
$.ready(() => {
|
|
151
150
|
// Display version in the sidebar footer
|
|
@@ -173,7 +172,7 @@ $.ready(() => {
|
|
|
173
172
|
$('#stats-arrow').removeClass('open');
|
|
174
173
|
}
|
|
175
174
|
|
|
176
|
-
// Sidebar contact click
|
|
175
|
+
// Sidebar contact click - open the global contact card overlay
|
|
177
176
|
$('#sc-list').on('click', '.sc-item', (e) => {
|
|
178
177
|
const item = e.target.closest('.sc-item');
|
|
179
178
|
if (!item) return;
|
|
@@ -193,11 +192,11 @@ $.ready(() => {
|
|
|
193
192
|
// Mount any components outside the router outlet (e.g. <contact-card>)
|
|
194
193
|
$.mountAll();
|
|
195
194
|
|
|
196
|
-
console.log('⚡ {{NAME}}
|
|
195
|
+
console.log('⚡ {{NAME}} - powered by zQuery v' + $.version);
|
|
197
196
|
});
|
|
198
197
|
|
|
199
198
|
// ---------------------------------------------------------------------------
|
|
200
|
-
// Theme helper
|
|
199
|
+
// Theme helper - resolves 'system' to actual dark/light and applies it
|
|
201
200
|
// ---------------------------------------------------------------------------
|
|
202
201
|
function applyTheme(preference) {
|
|
203
202
|
let resolved = preference;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// about.js
|
|
1
|
+
// about.js - About page with theme switcher
|
|
2
2
|
//
|
|
3
3
|
// Features used:
|
|
4
|
-
// $.storage
|
|
5
|
-
// $.version
|
|
6
|
-
// $.unitTests
|
|
7
|
-
// $.bus.emit
|
|
8
|
-
// data-theme attr
|
|
4
|
+
// $.storage - localStorage wrapper (get / set)
|
|
5
|
+
// $.version - library version string
|
|
6
|
+
// $.unitTests - build-time test results object
|
|
7
|
+
// $.bus.emit - toast notifications
|
|
8
|
+
// data-theme attr - dark / light theming
|
|
9
9
|
|
|
10
10
|
$.component('about-page', {
|
|
11
11
|
styles: `
|
|
@@ -100,7 +100,7 @@ $.component('about-page', {
|
|
|
100
100
|
'Directives': [
|
|
101
101
|
['z-if / z-for / z-show', 'Structural directives for conditional & list rendering'],
|
|
102
102
|
['z-bind / z-class / z-style', 'Dynamic attributes, classes, and inline styles'],
|
|
103
|
-
['z-debounce', 'Debounced model updates
|
|
103
|
+
['z-debounce', 'Debounced model updates - z-model z-debounce="300"'],
|
|
104
104
|
['z-lowercase / z-uppercase', 'Auto-transform text input on contacts email'],
|
|
105
105
|
['z-html', 'Trusted HTML injection in the playground'],
|
|
106
106
|
['templateUrl / styleUrl', 'External templates and CSS with auto-scoping'],
|
|
@@ -109,7 +109,7 @@ $.component('about-page', {
|
|
|
109
109
|
['@click.stop / .prevent', 'Event modifiers for fine-grained control'],
|
|
110
110
|
['@click.self / .once', 'Self-only and one-shot handlers in modals'],
|
|
111
111
|
['@click.outside', 'Outside-click detection for dropdowns'],
|
|
112
|
-
['@keydown.escape', 'Key modifiers
|
|
112
|
+
['@keydown.escape', 'Key modifiers - Escape to close forms'],
|
|
113
113
|
],
|
|
114
114
|
'Reactive Primitives': [
|
|
115
115
|
['$.signal() / $.computed()', 'Fine-grained reactive primitives for derived state'],
|
|
@@ -136,7 +136,7 @@ $.component('about-page', {
|
|
|
136
136
|
<div class="about-hero">
|
|
137
137
|
<span class="ver">v${$.version}</span>
|
|
138
138
|
<h1>zQuery</h1>
|
|
139
|
-
<p>A zero-dependency frontend micro-library
|
|
139
|
+
<p>A zero-dependency frontend micro-library - reactive components, routing, state management, and more in <strong>${$.libSize}</strong> minified.</p>
|
|
140
140
|
</div>
|
|
141
141
|
|
|
142
142
|
<div class="about-stats">
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// api-demo.js
|
|
1
|
+
// api-demo.js - HTTP client demo
|
|
2
2
|
//
|
|
3
3
|
// Features used:
|
|
4
|
-
// $.get()
|
|
5
|
-
// z-if / z-else / z-show
|
|
6
|
-
// z-for / z-text
|
|
7
|
-
// $.escapeHtml()
|
|
8
|
-
// async methods
|
|
4
|
+
// $.get() - fetch JSON from an API
|
|
5
|
+
// z-if / z-else / z-show - conditional rendering
|
|
6
|
+
// z-for / z-text - list rendering & text binding
|
|
7
|
+
// $.escapeHtml() - sanitize user-provided content
|
|
8
|
+
// async methods - loading & error state patterns
|
|
9
9
|
|
|
10
10
|
$.component('api-demo', {
|
|
11
11
|
styles: `
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
// contact-card.js
|
|
1
|
+
// contact-card.js - Global contact detail popup
|
|
2
2
|
//
|
|
3
3
|
// Always-mounted overlay that listens for $.bus 'openContact' events.
|
|
4
4
|
// Works from any page without navigation.
|
|
5
5
|
//
|
|
6
6
|
// Features used:
|
|
7
|
-
// $.bus.on / $.bus.emit
|
|
8
|
-
// $.getStore
|
|
9
|
-
// @click.self
|
|
7
|
+
// $.bus.on / $.bus.emit - event-driven communication
|
|
8
|
+
// $.getStore - read contact data from store
|
|
9
|
+
// @click.self - dismiss on backdrop click
|
|
10
10
|
|
|
11
11
|
$.component('contact-card', {
|
|
12
12
|
styles: `
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/* contacts.css
|
|
1
|
+
/* contacts.css - scoped styles for contacts-page component
|
|
2
2
|
*
|
|
3
|
-
* Loaded via styleUrl
|
|
3
|
+
* Loaded via styleUrl - these styles are automatically scoped
|
|
4
4
|
* to the contacts-page component by zQuery.
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
contacts.html
|
|
2
|
+
contacts.html - external template for contacts-page component
|
|
3
3
|
|
|
4
4
|
This template is fetched via templateUrl and uses {{expression}} syntax
|
|
5
5
|
for data binding. All zQuery directives work here: z-if, z-else,
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
<h1>Contacts</h1>
|
|
11
11
|
<p class="subtitle">
|
|
12
12
|
External template & styles via <code>templateUrl</code> / <code>styleUrl</code>.
|
|
13
|
-
Click a contact card to open the detail modal
|
|
13
|
+
Click a contact card to open the detail modal - dismiss with <code>Esc</code> or <code>@click.outside</code>.
|
|
14
14
|
</p>
|
|
15
15
|
</div>
|
|
16
16
|
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor" style="width:48px;height:48px;opacity:.3;"><path stroke-linecap="round" stroke-linejoin="round" d="M15 9h3.75M15 12h3.75M15 15h3.75M4.5 19.5h15a2.25 2.25 0 0 0 2.25-2.25V6.75A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25v10.5A2.25 2.25 0 0 0 4.5 19.5Zm6-10.125a1.875 1.875 0 1 1-3.75 0 1.875 1.875 0 0 1 3.75 0Zm1.294 6.336a6.721 6.721 0 0 1-3.17.789 6.721 6.721 0 0 1-3.168-.789 3.376 3.376 0 0 1 6.338 0Z"/></svg>
|
|
75
75
|
</div>
|
|
76
76
|
<p z-if="filterText || filterRole">No contacts match your filter.</p>
|
|
77
|
-
<p z-else>No contacts yet
|
|
77
|
+
<p z-else>No contacts yet - hit <strong>+ Add Contact</strong> to create one!</p>
|
|
78
78
|
</div>
|
|
79
79
|
|
|
80
80
|
<!-- ═══ Add Contact Modal ═══ -->
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
// contacts.js
|
|
1
|
+
// contacts.js - Contact book page
|
|
2
2
|
//
|
|
3
3
|
// Features used:
|
|
4
|
-
// templateUrl / styleUrl
|
|
5
|
-
// z-if / z-show / z-for
|
|
6
|
-
// z-model / z-ref
|
|
7
|
-
// @click / @submit.prevent
|
|
8
|
-
// @keydown.escape
|
|
9
|
-
// $.getStore / dispatch
|
|
10
|
-
// $.bus.emit('toast')
|
|
4
|
+
// templateUrl / styleUrl - external template & scoped styles
|
|
5
|
+
// z-if / z-show / z-for - conditional & list rendering
|
|
6
|
+
// z-model / z-ref - form bindings
|
|
7
|
+
// @click / @submit.prevent - event handling
|
|
8
|
+
// @keydown.escape - keyboard modifier
|
|
9
|
+
// $.getStore / dispatch - store integration
|
|
10
|
+
// $.bus.emit('toast') - notifications
|
|
11
11
|
|
|
12
12
|
$.component('contacts-page', {
|
|
13
13
|
templateUrl: 'contacts.html',
|
|
@@ -29,7 +29,7 @@ $.component('contacts-page', {
|
|
|
29
29
|
favoriteCount: 0,
|
|
30
30
|
filterText: '',
|
|
31
31
|
filterRole: '',
|
|
32
|
-
// Derived state (not computed
|
|
32
|
+
// Derived state (not computed - external templates resolve state only)
|
|
33
33
|
filteredContacts: [],
|
|
34
34
|
modalContact: null,
|
|
35
35
|
}),
|
|
@@ -45,7 +45,7 @@ $.component('contacts-page', {
|
|
|
45
45
|
this._syncFromStore(store);
|
|
46
46
|
this._unsub = store.subscribe(() => this._syncFromStore(store));
|
|
47
47
|
|
|
48
|
-
// Global Escape handler
|
|
48
|
+
// Global Escape handler - template @keydown.escape on overlays is
|
|
49
49
|
// unreliable when no child element has focus (detail modal has no inputs).
|
|
50
50
|
this._onEscape = (e) => {
|
|
51
51
|
if (e.key !== 'Escape') return;
|
|
@@ -62,7 +62,7 @@ $.component('contacts-page', {
|
|
|
62
62
|
|
|
63
63
|
_syncFromStore(store) {
|
|
64
64
|
// Shallow-clone each contact so the framework detects new references
|
|
65
|
-
// (store actions mutate objects in place
|
|
65
|
+
// (store actions mutate objects in place - same refs won't trigger re-render)
|
|
66
66
|
this.state.contacts = store.state.contacts.map(c => ({ ...c }));
|
|
67
67
|
this.state.totalAdded = store.state.contactsAdded;
|
|
68
68
|
this.state.favoriteCount = store.getters.favoriteCount;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// counter.js
|
|
1
|
+
// counter.js - Interactive counter
|
|
2
2
|
//
|
|
3
3
|
// Features used:
|
|
4
|
-
// state / computed / watch
|
|
5
|
-
// @click
|
|
6
|
-
// z-model + z-number
|
|
7
|
-
// z-class / z-if / z-for
|
|
8
|
-
// $.bus.emit('toast', …)
|
|
4
|
+
// state / computed / watch - reactive data flow
|
|
5
|
+
// @click - event binding
|
|
6
|
+
// z-model + z-number - two-way binding with type coercion
|
|
7
|
+
// z-class / z-if / z-for - conditional & list rendering
|
|
8
|
+
// $.bus.emit('toast', …) - notifications via event bus
|
|
9
9
|
|
|
10
10
|
$.component('counter-page', {
|
|
11
11
|
styles: `
|
|
@@ -72,7 +72,7 @@ $.component('counter-page', {
|
|
|
72
72
|
|
|
73
73
|
decrement() {
|
|
74
74
|
this.state.count -= this.state.step;
|
|
75
|
-
this._pushHistory('
|
|
75
|
+
this._pushHistory('-', this.state.step, this.state.count);
|
|
76
76
|
},
|
|
77
77
|
|
|
78
78
|
_pushHistory(action, value, result) {
|
|
@@ -101,7 +101,7 @@ $.component('counter-page', {
|
|
|
101
101
|
</div>
|
|
102
102
|
|
|
103
103
|
<div class="ctr-actions">
|
|
104
|
-
<button class="btn btn-outline" @click="decrement"
|
|
104
|
+
<button class="btn btn-outline" @click="decrement">- Subtract</button>
|
|
105
105
|
<button class="btn btn-primary" @click="increment">+ Add</button>
|
|
106
106
|
</div>
|
|
107
107
|
|