@devansharora18/tardisjs 0.0.2
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/README.md +0 -0
- package/dist/bin/bin/create-tardis-app.d.ts +2 -0
- package/dist/bin/bin/tardis.d.ts +15 -0
- package/dist/bin/create-tardis-app.d.ts +2 -0
- package/dist/bin/create-tardis-app.js +2102 -0
- package/dist/bin/create-tardis-app.js.map +1 -0
- package/dist/bin/src/compiler/compiler.d.ts +3 -0
- package/dist/bin/src/lexer/lexer.d.ts +2 -0
- package/dist/bin/src/lexer/tokens.d.ts +8 -0
- package/dist/bin/src/parser/errors.d.ts +6 -0
- package/dist/bin/src/parser/parser.d.ts +3 -0
- package/dist/bin/src/parser/types.d.ts +67 -0
- package/dist/bin/src/runtime/events.d.ts +9 -0
- package/dist/bin/src/runtime/fetch.d.ts +11 -0
- package/dist/bin/src/runtime/index.d.ts +52 -0
- package/dist/bin/src/runtime/registry.d.ts +17 -0
- package/dist/bin/src/runtime/render.d.ts +11 -0
- package/dist/bin/src/runtime/router.d.ts +15 -0
- package/dist/bin/src/runtime/selector.d.ts +67 -0
- package/dist/bin/src/runtime/state.d.ts +9 -0
- package/dist/bin/src/runtime/styles.d.ts +9 -0
- package/dist/bin/tardis.d.ts +15 -0
- package/dist/bin/tardis.js +2086 -0
- package/dist/bin/tardis.js.map +1 -0
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/dist/runtime/bin/create-tardis-app.d.ts +2 -0
- package/dist/runtime/bin/tardis.d.ts +15 -0
- package/dist/runtime/index.cjs +829 -0
- package/dist/runtime/index.cjs.map +1 -0
- package/dist/runtime/index.mjs +791 -0
- package/dist/runtime/index.mjs.map +1 -0
- package/dist/runtime/src/compiler/compiler.d.ts +3 -0
- package/dist/runtime/src/lexer/lexer.d.ts +2 -0
- package/dist/runtime/src/lexer/tokens.d.ts +8 -0
- package/dist/runtime/src/parser/errors.d.ts +6 -0
- package/dist/runtime/src/parser/parser.d.ts +3 -0
- package/dist/runtime/src/parser/types.d.ts +67 -0
- package/dist/runtime/src/runtime/events.d.ts +9 -0
- package/dist/runtime/src/runtime/fetch.d.ts +11 -0
- package/dist/runtime/src/runtime/index.d.ts +52 -0
- package/dist/runtime/src/runtime/registry.d.ts +17 -0
- package/dist/runtime/src/runtime/render.d.ts +11 -0
- package/dist/runtime/src/runtime/router.d.ts +15 -0
- package/dist/runtime/src/runtime/selector.d.ts +67 -0
- package/dist/runtime/src/runtime/state.d.ts +9 -0
- package/dist/runtime/src/runtime/styles.d.ts +9 -0
- package/dist/src/compiler/compiler.d.ts +3 -0
- package/dist/src/lexer/lexer.d.ts +2 -0
- package/dist/src/lexer/tokens.d.ts +8 -0
- package/dist/src/parser/errors.d.ts +6 -0
- package/dist/src/parser/parser.d.ts +3 -0
- package/dist/src/parser/types.d.ts +67 -0
- package/dist/src/runtime/events.d.ts +9 -0
- package/dist/src/runtime/fetch.d.ts +11 -0
- package/dist/src/runtime/index.d.ts +52 -0
- package/dist/src/runtime/registry.d.ts +17 -0
- package/dist/src/runtime/render.d.ts +11 -0
- package/dist/src/runtime/router.d.ts +15 -0
- package/dist/src/runtime/selector.d.ts +67 -0
- package/dist/src/runtime/state.d.ts +9 -0
- package/dist/src/runtime/styles.d.ts +9 -0
- package/package.json +67 -0
|
@@ -0,0 +1,791 @@
|
|
|
1
|
+
// src/runtime/state.ts
|
|
2
|
+
// internal dep map — keyed by state object identity
|
|
3
|
+
const depMap = new WeakMap();
|
|
4
|
+
let activeEffect = null;
|
|
5
|
+
let batchQueue = null;
|
|
6
|
+
function $batch(fn) {
|
|
7
|
+
batchQueue = new Set();
|
|
8
|
+
fn();
|
|
9
|
+
const queue = batchQueue;
|
|
10
|
+
batchQueue = null;
|
|
11
|
+
requestAnimationFrame(() => {
|
|
12
|
+
queue.forEach(updater => updater());
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function createState(initial) {
|
|
16
|
+
const raw = { ...initial };
|
|
17
|
+
const deps = new Map();
|
|
18
|
+
for (const key of Object.keys(raw)) {
|
|
19
|
+
deps.set(key, new Set());
|
|
20
|
+
}
|
|
21
|
+
const proxy = new Proxy(raw, {
|
|
22
|
+
get(target, key) {
|
|
23
|
+
if (activeEffect) {
|
|
24
|
+
if (!deps.has(key))
|
|
25
|
+
deps.set(key, new Set());
|
|
26
|
+
deps.get(key).add(activeEffect);
|
|
27
|
+
}
|
|
28
|
+
return target[key];
|
|
29
|
+
},
|
|
30
|
+
set(target, key, value) {
|
|
31
|
+
target[key] = value;
|
|
32
|
+
const updaters = deps.get(key);
|
|
33
|
+
if (updaters) {
|
|
34
|
+
if (batchQueue) {
|
|
35
|
+
updaters.forEach(fn => batchQueue.add(fn));
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
updaters.forEach(fn => fn());
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
depMap.set(proxy, deps);
|
|
45
|
+
return proxy;
|
|
46
|
+
}
|
|
47
|
+
function registerDep(state, key, updater) {
|
|
48
|
+
const deps = depMap.get(state);
|
|
49
|
+
if (deps) {
|
|
50
|
+
if (!deps.has(key))
|
|
51
|
+
deps.set(key, new Set());
|
|
52
|
+
deps.get(key).add(updater);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function withEffect(effect, fn) {
|
|
56
|
+
const prev = activeEffect;
|
|
57
|
+
activeEffect = effect;
|
|
58
|
+
try {
|
|
59
|
+
return fn();
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
activeEffect = prev;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function $update(state, key, value) {
|
|
66
|
+
state[key] = value;
|
|
67
|
+
}
|
|
68
|
+
function $toggle(state, key) {
|
|
69
|
+
state[key] = !state[key];
|
|
70
|
+
}
|
|
71
|
+
function $reset(state, initial) {
|
|
72
|
+
for (const [key, val] of Object.entries(initial)) {
|
|
73
|
+
state[key] = val;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/runtime/fetch.ts
|
|
78
|
+
function $fetch(url) {
|
|
79
|
+
let _method = 'GET';
|
|
80
|
+
let _body = undefined;
|
|
81
|
+
let _headers = {};
|
|
82
|
+
let _done = null;
|
|
83
|
+
let _fail = null;
|
|
84
|
+
let _always = null;
|
|
85
|
+
let _executed = false;
|
|
86
|
+
function execute() {
|
|
87
|
+
if (_executed)
|
|
88
|
+
return;
|
|
89
|
+
_executed = true;
|
|
90
|
+
const options = {
|
|
91
|
+
method: _method,
|
|
92
|
+
headers: {
|
|
93
|
+
'Content-Type': 'application/json',
|
|
94
|
+
..._headers,
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
if (_body !== undefined && _method !== 'GET') {
|
|
98
|
+
options.body = JSON.stringify(_body);
|
|
99
|
+
}
|
|
100
|
+
fetch(url, options)
|
|
101
|
+
.then(res => {
|
|
102
|
+
if (!res.ok)
|
|
103
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`);
|
|
104
|
+
return res.json();
|
|
105
|
+
})
|
|
106
|
+
.then(data => {
|
|
107
|
+
_done?.(data);
|
|
108
|
+
})
|
|
109
|
+
.catch(err => {
|
|
110
|
+
_fail?.(err instanceof Error ? err : new Error(String(err)));
|
|
111
|
+
})
|
|
112
|
+
.finally(() => {
|
|
113
|
+
_always?.();
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const chain = {
|
|
117
|
+
method(m) {
|
|
118
|
+
_method = m;
|
|
119
|
+
return chain;
|
|
120
|
+
},
|
|
121
|
+
body(b) {
|
|
122
|
+
_body = b;
|
|
123
|
+
return chain;
|
|
124
|
+
},
|
|
125
|
+
headers(h) {
|
|
126
|
+
_headers = { ..._headers, ...h };
|
|
127
|
+
return chain;
|
|
128
|
+
},
|
|
129
|
+
done(fn) {
|
|
130
|
+
_done = fn;
|
|
131
|
+
execute();
|
|
132
|
+
return chain;
|
|
133
|
+
},
|
|
134
|
+
fail(fn) {
|
|
135
|
+
_fail = fn;
|
|
136
|
+
execute();
|
|
137
|
+
return chain;
|
|
138
|
+
},
|
|
139
|
+
always(fn) {
|
|
140
|
+
_always = fn;
|
|
141
|
+
execute();
|
|
142
|
+
return chain;
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
return chain;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/runtime/render.ts
|
|
149
|
+
// ── $if ────────────────────────────────────────────────────────────────────
|
|
150
|
+
function $if(conditionFn, builderFn) {
|
|
151
|
+
const anchor = document.createComment('$if');
|
|
152
|
+
let currentEl = null;
|
|
153
|
+
let isMounted = conditionFn();
|
|
154
|
+
// initial render
|
|
155
|
+
if (isMounted) {
|
|
156
|
+
currentEl = builderFn();
|
|
157
|
+
}
|
|
158
|
+
return currentEl ?? anchor;
|
|
159
|
+
}
|
|
160
|
+
// ── $each ──────────────────────────────────────────────────────────────────
|
|
161
|
+
function $each(arrayFn, itemBuilderFn) {
|
|
162
|
+
const container = document.createElement('div');
|
|
163
|
+
container.setAttribute('data-each', '');
|
|
164
|
+
function render() {
|
|
165
|
+
const items = arrayFn();
|
|
166
|
+
// clear existing children
|
|
167
|
+
while (container.firstChild) {
|
|
168
|
+
container.removeChild(container.firstChild);
|
|
169
|
+
}
|
|
170
|
+
// render new children
|
|
171
|
+
items.forEach((item, index) => {
|
|
172
|
+
container.appendChild(itemBuilderFn(item, index));
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
render();
|
|
176
|
+
return container;
|
|
177
|
+
}
|
|
178
|
+
// ── $show ──────────────────────────────────────────────────────────────────
|
|
179
|
+
function $show(conditionFn, builderFn) {
|
|
180
|
+
const el = builderFn();
|
|
181
|
+
function update() {
|
|
182
|
+
el.style.display = conditionFn() ? '' : 'none';
|
|
183
|
+
}
|
|
184
|
+
update();
|
|
185
|
+
return el;
|
|
186
|
+
}
|
|
187
|
+
// ── $portal ────────────────────────────────────────────────────────────────
|
|
188
|
+
function $portal(selector, builderFn) {
|
|
189
|
+
const target = document.querySelector(selector);
|
|
190
|
+
const el = builderFn();
|
|
191
|
+
if (target) {
|
|
192
|
+
target.appendChild(el);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
console.warn(`$portal: target "${selector}" not found`);
|
|
196
|
+
}
|
|
197
|
+
return el;
|
|
198
|
+
}
|
|
199
|
+
// ── bind ───────────────────────────────────────────────────────────────────
|
|
200
|
+
// binds a DOM property to a reactive expression
|
|
201
|
+
// re-runs the expression and updates the DOM whenever state changes
|
|
202
|
+
function bind(el, prop, valueFn) {
|
|
203
|
+
function update() {
|
|
204
|
+
const val = withEffect(update, valueFn);
|
|
205
|
+
if (prop === 'textContent') {
|
|
206
|
+
el.textContent = String(val ?? '');
|
|
207
|
+
}
|
|
208
|
+
else if (prop in el) {
|
|
209
|
+
el[prop] = val;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// initial render
|
|
213
|
+
update();
|
|
214
|
+
el.__update = update;
|
|
215
|
+
}
|
|
216
|
+
// ── bindClass ──────────────────────────────────────────────────────────────
|
|
217
|
+
function bindClass(el, classFn) {
|
|
218
|
+
function update() {
|
|
219
|
+
el.className = withEffect(update, classFn);
|
|
220
|
+
}
|
|
221
|
+
update();
|
|
222
|
+
el.__updateClass = update;
|
|
223
|
+
}
|
|
224
|
+
// ── bindAttr ───────────────────────────────────────────────────────────────
|
|
225
|
+
function bindAttr(el, attr, valueFn) {
|
|
226
|
+
function update() {
|
|
227
|
+
const val = withEffect(update, valueFn);
|
|
228
|
+
if (val === null || val === undefined || val === false) {
|
|
229
|
+
el.removeAttribute(attr);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
el.setAttribute(attr, String(val));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
update();
|
|
236
|
+
el[`__updateAttr_${attr}`] = update;
|
|
237
|
+
}
|
|
238
|
+
// ── resolveStyles ──────────────────────────────────────────────────────────
|
|
239
|
+
function resolveStyles(styles, key) {
|
|
240
|
+
const maybeRules = styles.rules;
|
|
241
|
+
const styleMap = maybeRules && typeof maybeRules === 'object' && !Array.isArray(maybeRules)
|
|
242
|
+
? maybeRules
|
|
243
|
+
: styles;
|
|
244
|
+
return styleMap[key] ?? key;
|
|
245
|
+
}
|
|
246
|
+
// ── text ───────────────────────────────────────────────────────────────────
|
|
247
|
+
function text(value) {
|
|
248
|
+
const node = document.createTextNode('');
|
|
249
|
+
if (typeof value === 'function') {
|
|
250
|
+
const valueFn = value;
|
|
251
|
+
function update() { node.textContent = withEffect(update, valueFn); }
|
|
252
|
+
update();
|
|
253
|
+
node.__update = update;
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
node.textContent = value;
|
|
257
|
+
}
|
|
258
|
+
return node;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// src/runtime/styles.ts
|
|
262
|
+
function createStyles(mode, rules, props, state) {
|
|
263
|
+
function resolve() {
|
|
264
|
+
const classes = [];
|
|
265
|
+
for (const [key, value] of Object.entries(rules)) {
|
|
266
|
+
if (key === 'base') {
|
|
267
|
+
classes.push(value);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
// dotted key — variant.primary, disabled.true, size.sm
|
|
271
|
+
const dotIdx = key.indexOf('.');
|
|
272
|
+
if (dotIdx >= 0) {
|
|
273
|
+
const propOrState = key.slice(0, dotIdx);
|
|
274
|
+
const expectedVal = key.slice(dotIdx + 1);
|
|
275
|
+
// check props first then state
|
|
276
|
+
const actualVal = props[propOrState] !== undefined
|
|
277
|
+
? props[propOrState]
|
|
278
|
+
: state[propOrState];
|
|
279
|
+
if (actualVal === undefined)
|
|
280
|
+
continue;
|
|
281
|
+
// match boolean shorthand — disabled.true
|
|
282
|
+
if (expectedVal === 'true' && actualVal === true) {
|
|
283
|
+
classes.push(value);
|
|
284
|
+
}
|
|
285
|
+
else if (expectedVal === 'false' && actualVal === false) {
|
|
286
|
+
classes.push(value);
|
|
287
|
+
}
|
|
288
|
+
else if (String(actualVal) === expectedVal) {
|
|
289
|
+
// match string value — variant.primary
|
|
290
|
+
classes.push(value);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (mode === 'tailwind') {
|
|
295
|
+
return classes.join(' ');
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
// css mode — join as inline style string
|
|
299
|
+
return classes.join('; ');
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return {
|
|
303
|
+
mode,
|
|
304
|
+
rules,
|
|
305
|
+
resolve,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/runtime/registry.ts
|
|
310
|
+
// global registry of all mounted component instances
|
|
311
|
+
const registry = new Map();
|
|
312
|
+
function register(name, instance) {
|
|
313
|
+
const id = `${name}_${Math.random().toString(36).slice(2, 9)}`;
|
|
314
|
+
const full = {
|
|
315
|
+
name,
|
|
316
|
+
id,
|
|
317
|
+
el: null,
|
|
318
|
+
state: instance.state ?? {},
|
|
319
|
+
methods: instance.methods ?? {},
|
|
320
|
+
props: instance.props ?? {},
|
|
321
|
+
};
|
|
322
|
+
if (!registry.has(name))
|
|
323
|
+
registry.set(name, []);
|
|
324
|
+
registry.get(name).push(full);
|
|
325
|
+
return id;
|
|
326
|
+
}
|
|
327
|
+
function unregister(id) {
|
|
328
|
+
for (const [name, instances] of registry.entries()) {
|
|
329
|
+
const idx = instances.findIndex(i => i.id === id);
|
|
330
|
+
if (idx >= 0) {
|
|
331
|
+
instances.splice(idx, 1);
|
|
332
|
+
if (instances.length === 0)
|
|
333
|
+
registry.delete(name);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
function getAll(name) {
|
|
339
|
+
return registry.get(name) ?? [];
|
|
340
|
+
}
|
|
341
|
+
function getById(id) {
|
|
342
|
+
for (const instances of registry.values()) {
|
|
343
|
+
const found = instances.find(i => i.id === id);
|
|
344
|
+
if (found)
|
|
345
|
+
return found;
|
|
346
|
+
}
|
|
347
|
+
return null;
|
|
348
|
+
}
|
|
349
|
+
function getByClass(name, cls) {
|
|
350
|
+
return getAll(name).filter(i => i.el?.classList.contains(cls));
|
|
351
|
+
}
|
|
352
|
+
function getByPropValue(name, prop, value) {
|
|
353
|
+
return getAll(name).filter(i => i.props[prop] === value);
|
|
354
|
+
}
|
|
355
|
+
function getByStateValue(name, key, value) {
|
|
356
|
+
return getAll(name).filter(i => i.state[key] === value);
|
|
357
|
+
}
|
|
358
|
+
function clearRegistry() {
|
|
359
|
+
registry.clear();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// src/runtime/selector.ts
|
|
363
|
+
// jQuery-tribute: unified selector for DOM elements and Tardis component instances
|
|
364
|
+
function parseAttrFilter(raw) {
|
|
365
|
+
const match = raw.match(/^([\w.]+)\s*(>=|<=|>|<|=)\s*(.+)$/);
|
|
366
|
+
if (!match)
|
|
367
|
+
return null;
|
|
368
|
+
const [, key, op, rawVal] = match;
|
|
369
|
+
let value;
|
|
370
|
+
if (rawVal === 'true')
|
|
371
|
+
value = true;
|
|
372
|
+
else if (rawVal === 'false')
|
|
373
|
+
value = false;
|
|
374
|
+
else if (/^-?\d+(\.\d+)?$/.test(rawVal))
|
|
375
|
+
value = parseFloat(rawVal);
|
|
376
|
+
else
|
|
377
|
+
value = rawVal.replace(/^["']|["']$/g, ''); // strip surrounding quotes
|
|
378
|
+
return { key, op, value };
|
|
379
|
+
}
|
|
380
|
+
function instanceMatchesAttr(inst, filter) {
|
|
381
|
+
const { key, op, value } = filter;
|
|
382
|
+
let actual;
|
|
383
|
+
if (key.startsWith('state.')) {
|
|
384
|
+
actual = inst.state[key.slice(6)];
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
actual = inst.props[key] !== undefined ? inst.props[key] : inst.state[key];
|
|
388
|
+
}
|
|
389
|
+
switch (op) {
|
|
390
|
+
case '=': return actual === value;
|
|
391
|
+
case '>': return actual > value;
|
|
392
|
+
case '<': return actual < value;
|
|
393
|
+
case '>=': return actual >= value;
|
|
394
|
+
case '<=': return actual <= value;
|
|
395
|
+
default: return false;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// ── DOMSelection ─────────────────────────────────────────────────────────────
|
|
399
|
+
class DOMSelection {
|
|
400
|
+
constructor(elements) {
|
|
401
|
+
this.elements = elements;
|
|
402
|
+
}
|
|
403
|
+
each(fn) {
|
|
404
|
+
this.elements.forEach(fn);
|
|
405
|
+
return this;
|
|
406
|
+
}
|
|
407
|
+
focus() { return this.each(el => el.focus()); }
|
|
408
|
+
blur() { return this.each(el => el.blur()); }
|
|
409
|
+
hide() { return this.each(el => { el.style.display = 'none'; }); }
|
|
410
|
+
show() { return this.each(el => { el.style.display = ''; }); }
|
|
411
|
+
remove() { return this.each(el => el.parentNode?.removeChild(el)); }
|
|
412
|
+
toggle() {
|
|
413
|
+
return this.each(el => {
|
|
414
|
+
el.style.display = el.style.display === 'none' ? '' : 'none';
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
disable() { return this.each(el => el.setAttribute('disabled', '')); }
|
|
418
|
+
enable() { return this.each(el => el.removeAttribute('disabled')); }
|
|
419
|
+
addClass(cls) { return this.each(el => el.classList.add(cls)); }
|
|
420
|
+
removeClass(cls) { return this.each(el => el.classList.remove(cls)); }
|
|
421
|
+
toggleClass(cls) { return this.each(el => el.classList.toggle(cls)); }
|
|
422
|
+
attr(key, val) {
|
|
423
|
+
if (val === undefined)
|
|
424
|
+
return this.elements[0]?.getAttribute(key) ?? null;
|
|
425
|
+
return this.each(el => el.setAttribute(key, val));
|
|
426
|
+
}
|
|
427
|
+
val(value) {
|
|
428
|
+
if (value === undefined)
|
|
429
|
+
return this.elements[0]?.value ?? '';
|
|
430
|
+
return this.each(el => { el.value = value; });
|
|
431
|
+
}
|
|
432
|
+
rect() { return this.elements[0]?.getBoundingClientRect() ?? null; }
|
|
433
|
+
width() { return this.elements[0]?.offsetWidth ?? 0; }
|
|
434
|
+
height() { return this.elements[0]?.offsetHeight ?? 0; }
|
|
435
|
+
scrollIntoView() { return this.each(el => el.scrollIntoView?.()); }
|
|
436
|
+
scrollTo(opts) { return this.each(el => el.scrollTo?.(opts)); }
|
|
437
|
+
fadeIn() { return this.each(el => { el.style.display = ''; el.style.opacity = '1'; }); }
|
|
438
|
+
fadeOut() { return this.each(el => { el.style.opacity = '0'; }); }
|
|
439
|
+
slideDown() {
|
|
440
|
+
return this.each(el => { el.style.overflow = 'hidden'; el.style.maxHeight = ''; });
|
|
441
|
+
}
|
|
442
|
+
slideUp() {
|
|
443
|
+
return this.each(el => { el.style.overflow = 'hidden'; el.style.maxHeight = '0px'; });
|
|
444
|
+
}
|
|
445
|
+
// no-ops for component-only API — keeps chaining safe on DOM selections
|
|
446
|
+
$update(_key, _value) { return this; }
|
|
447
|
+
$reset(_initial) { return this; }
|
|
448
|
+
get methods() {
|
|
449
|
+
const self = this;
|
|
450
|
+
return new Proxy({}, {
|
|
451
|
+
get: () => () => self,
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
// ── ComponentSelection ────────────────────────────────────────────────────────
|
|
456
|
+
class ComponentSelection {
|
|
457
|
+
constructor(instances) {
|
|
458
|
+
this.instances = instances;
|
|
459
|
+
}
|
|
460
|
+
eachEl(fn) {
|
|
461
|
+
this.instances.forEach(inst => { if (inst.el)
|
|
462
|
+
fn(inst.el); });
|
|
463
|
+
return this;
|
|
464
|
+
}
|
|
465
|
+
focus() { return this.eachEl(el => el.focus()); }
|
|
466
|
+
blur() { return this.eachEl(el => el.blur()); }
|
|
467
|
+
hide() { return this.eachEl(el => { el.style.display = 'none'; }); }
|
|
468
|
+
show() { return this.eachEl(el => { el.style.display = ''; }); }
|
|
469
|
+
remove() { return this.eachEl(el => el.parentNode?.removeChild(el)); }
|
|
470
|
+
toggle() {
|
|
471
|
+
return this.eachEl(el => {
|
|
472
|
+
el.style.display = el.style.display === 'none' ? '' : 'none';
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
disable() { return this.eachEl(el => el.setAttribute('disabled', '')); }
|
|
476
|
+
enable() { return this.eachEl(el => el.removeAttribute('disabled')); }
|
|
477
|
+
addClass(cls) { return this.eachEl(el => el.classList.add(cls)); }
|
|
478
|
+
removeClass(cls) { return this.eachEl(el => el.classList.remove(cls)); }
|
|
479
|
+
toggleClass(cls) { return this.eachEl(el => el.classList.toggle(cls)); }
|
|
480
|
+
attr(key, val) {
|
|
481
|
+
if (val === undefined)
|
|
482
|
+
return this.instances[0]?.el?.getAttribute(key) ?? null;
|
|
483
|
+
return this.eachEl(el => el.setAttribute(key, val));
|
|
484
|
+
}
|
|
485
|
+
val(value) {
|
|
486
|
+
if (value === undefined)
|
|
487
|
+
return this.instances[0]?.el?.value ?? '';
|
|
488
|
+
return this.eachEl(el => { el.value = value; });
|
|
489
|
+
}
|
|
490
|
+
rect() { return this.instances[0]?.el?.getBoundingClientRect() ?? null; }
|
|
491
|
+
width() { return this.instances[0]?.el?.offsetWidth ?? 0; }
|
|
492
|
+
height() { return this.instances[0]?.el?.offsetHeight ?? 0; }
|
|
493
|
+
scrollIntoView() { return this.eachEl(el => el.scrollIntoView?.()); }
|
|
494
|
+
scrollTo(opts) { return this.eachEl(el => el.scrollTo?.(opts)); }
|
|
495
|
+
fadeIn() { return this.eachEl(el => { el.style.display = ''; el.style.opacity = '1'; }); }
|
|
496
|
+
fadeOut() { return this.eachEl(el => { el.style.opacity = '0'; }); }
|
|
497
|
+
slideDown() {
|
|
498
|
+
return this.eachEl(el => { el.style.overflow = 'hidden'; el.style.maxHeight = ''; });
|
|
499
|
+
}
|
|
500
|
+
slideUp() {
|
|
501
|
+
return this.eachEl(el => { el.style.overflow = 'hidden'; el.style.maxHeight = '0px'; });
|
|
502
|
+
}
|
|
503
|
+
// component-specific
|
|
504
|
+
$update(key, value) {
|
|
505
|
+
this.instances.forEach(inst => $update(inst.state, key, value));
|
|
506
|
+
return this;
|
|
507
|
+
}
|
|
508
|
+
$reset(initial) {
|
|
509
|
+
this.instances.forEach(inst => $reset(inst.state, initial));
|
|
510
|
+
return this;
|
|
511
|
+
}
|
|
512
|
+
get methods() {
|
|
513
|
+
const self = this;
|
|
514
|
+
return new Proxy({}, {
|
|
515
|
+
get(_, methodName) {
|
|
516
|
+
return (...args) => {
|
|
517
|
+
self.instances.forEach(inst => inst.methods[methodName]?.(...args));
|
|
518
|
+
return self;
|
|
519
|
+
};
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
// ── $ ────────────────────────────────────────────────────────────────────────
|
|
525
|
+
function $(selector) {
|
|
526
|
+
const s = selector.trim();
|
|
527
|
+
// Component selectors start with an uppercase letter
|
|
528
|
+
if (/^[A-Z]/.test(s)) {
|
|
529
|
+
const m = s.match(/^([A-Za-z]\w*)(?:#([\w-]+))?(?:\.([\w-]+))?(?:\[([^\]]+)\])?$/);
|
|
530
|
+
if (m) {
|
|
531
|
+
const [, name, idFilter, classFilter, attrFilter] = m;
|
|
532
|
+
let instances = getAll(name);
|
|
533
|
+
if (idFilter)
|
|
534
|
+
instances = instances.filter(inst => inst.el?.id === idFilter);
|
|
535
|
+
if (classFilter)
|
|
536
|
+
instances = instances.filter(inst => inst.el?.classList.contains(classFilter));
|
|
537
|
+
if (attrFilter) {
|
|
538
|
+
const parsed = parseAttrFilter(attrFilter);
|
|
539
|
+
if (parsed)
|
|
540
|
+
instances = instances.filter(inst => instanceMatchesAttr(inst, parsed));
|
|
541
|
+
}
|
|
542
|
+
return new ComponentSelection(instances);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
// DOM selector — delegate to querySelectorAll
|
|
546
|
+
const nodes = document.querySelectorAll(s);
|
|
547
|
+
return new DOMSelection(Array.from(nodes));
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
// src/runtime/events.ts
|
|
551
|
+
// stores pending mount callbacks to be called after DOM insertion
|
|
552
|
+
const mountCallbacks = [];
|
|
553
|
+
const destroyCallbacks = [];
|
|
554
|
+
function registerEvents(handlers) {
|
|
555
|
+
if (handlers.onMount) {
|
|
556
|
+
mountCallbacks.push(handlers.onMount);
|
|
557
|
+
}
|
|
558
|
+
if (handlers.onDestroy) {
|
|
559
|
+
destroyCallbacks.push(handlers.onDestroy);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function flushMountCallbacks() {
|
|
563
|
+
while (mountCallbacks.length > 0) {
|
|
564
|
+
const cb = mountCallbacks.shift();
|
|
565
|
+
cb();
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
function flushDestroyCallbacks() {
|
|
569
|
+
while (destroyCallbacks.length > 0) {
|
|
570
|
+
const cb = destroyCallbacks.shift();
|
|
571
|
+
cb();
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const $params = {};
|
|
576
|
+
let activeRouter = null;
|
|
577
|
+
function normalizePath(path) {
|
|
578
|
+
if (!path)
|
|
579
|
+
return '/';
|
|
580
|
+
const [pathname] = path.split('?');
|
|
581
|
+
const normalized = pathname.startsWith('/') ? pathname : `/${pathname}`;
|
|
582
|
+
if (normalized !== '/' && normalized.endsWith('/'))
|
|
583
|
+
return normalized.slice(0, -1);
|
|
584
|
+
return normalized;
|
|
585
|
+
}
|
|
586
|
+
function splitPath(path) {
|
|
587
|
+
const normalized = normalizePath(path);
|
|
588
|
+
if (normalized === '/')
|
|
589
|
+
return [];
|
|
590
|
+
return normalized.slice(1).split('/').filter(Boolean);
|
|
591
|
+
}
|
|
592
|
+
function setParams(next) {
|
|
593
|
+
for (const key of Object.keys($params)) {
|
|
594
|
+
delete $params[key];
|
|
595
|
+
}
|
|
596
|
+
Object.assign($params, next);
|
|
597
|
+
}
|
|
598
|
+
function matchRoute(routePath, currentPath) {
|
|
599
|
+
const routeParts = splitPath(routePath);
|
|
600
|
+
const currentParts = splitPath(currentPath);
|
|
601
|
+
if (routeParts.length !== currentParts.length) {
|
|
602
|
+
return { matched: false, params: {} };
|
|
603
|
+
}
|
|
604
|
+
const params = {};
|
|
605
|
+
for (let i = 0; i < routeParts.length; i++) {
|
|
606
|
+
const routePart = routeParts[i];
|
|
607
|
+
const currentPart = currentParts[i];
|
|
608
|
+
if (routePart.startsWith(':')) {
|
|
609
|
+
params[routePart.slice(1)] = decodeURIComponent(currentPart);
|
|
610
|
+
continue;
|
|
611
|
+
}
|
|
612
|
+
if (routePart !== currentPart) {
|
|
613
|
+
return { matched: false, params: {} };
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
return { matched: true, params };
|
|
617
|
+
}
|
|
618
|
+
function resolveRoute(routes, path) {
|
|
619
|
+
const normalized = normalizePath(path);
|
|
620
|
+
for (const route of routes) {
|
|
621
|
+
const result = matchRoute(route.path, normalized);
|
|
622
|
+
if (result.matched)
|
|
623
|
+
return { route, params: result.params };
|
|
624
|
+
}
|
|
625
|
+
return { route: null, params: {} };
|
|
626
|
+
}
|
|
627
|
+
function createRouter(routes, mountTarget) {
|
|
628
|
+
const target = mountTarget ?? document.getElementById('app') ?? document.body;
|
|
629
|
+
let currentNode = null;
|
|
630
|
+
function render(path) {
|
|
631
|
+
const { route, params } = resolveRoute(routes, path);
|
|
632
|
+
if (!route) {
|
|
633
|
+
target.innerHTML = '<h1>404</h1>';
|
|
634
|
+
setParams({});
|
|
635
|
+
currentNode = null;
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
flushDestroyCallbacks();
|
|
639
|
+
if (currentNode && currentNode.parentNode === target) {
|
|
640
|
+
target.removeChild(currentNode);
|
|
641
|
+
}
|
|
642
|
+
const nextNode = route.component();
|
|
643
|
+
currentNode = nextNode;
|
|
644
|
+
setParams(params);
|
|
645
|
+
target.innerHTML = '';
|
|
646
|
+
target.appendChild(nextNode);
|
|
647
|
+
}
|
|
648
|
+
function navigate(path, replace = false) {
|
|
649
|
+
const nextPath = normalizePath(path);
|
|
650
|
+
if (replace) {
|
|
651
|
+
window.history.replaceState({}, '', nextPath);
|
|
652
|
+
}
|
|
653
|
+
else {
|
|
654
|
+
window.history.pushState({}, '', nextPath);
|
|
655
|
+
}
|
|
656
|
+
render(nextPath);
|
|
657
|
+
}
|
|
658
|
+
function back() {
|
|
659
|
+
window.history.back();
|
|
660
|
+
}
|
|
661
|
+
function forward() {
|
|
662
|
+
window.history.forward();
|
|
663
|
+
}
|
|
664
|
+
function handleLinkClick(event) {
|
|
665
|
+
if (event.defaultPrevented)
|
|
666
|
+
return;
|
|
667
|
+
if (event.button !== 0)
|
|
668
|
+
return;
|
|
669
|
+
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)
|
|
670
|
+
return;
|
|
671
|
+
const targetEl = event.target;
|
|
672
|
+
const anchor = targetEl?.closest('a[href]');
|
|
673
|
+
if (!anchor)
|
|
674
|
+
return;
|
|
675
|
+
const href = anchor.getAttribute('href');
|
|
676
|
+
if (!href || href.startsWith('http') || href.startsWith('mailto:') || href.startsWith('tel:') || href.startsWith('#'))
|
|
677
|
+
return;
|
|
678
|
+
if (anchor.target && anchor.target !== '_self')
|
|
679
|
+
return;
|
|
680
|
+
const nextPath = normalizePath(href);
|
|
681
|
+
event.preventDefault();
|
|
682
|
+
navigate(nextPath);
|
|
683
|
+
}
|
|
684
|
+
const router = {
|
|
685
|
+
start() {
|
|
686
|
+
render(window.location.pathname);
|
|
687
|
+
document.addEventListener('click', handleLinkClick);
|
|
688
|
+
window.addEventListener('popstate', () => {
|
|
689
|
+
render(window.location.pathname);
|
|
690
|
+
});
|
|
691
|
+
activeRouter = router;
|
|
692
|
+
},
|
|
693
|
+
navigate,
|
|
694
|
+
back,
|
|
695
|
+
forward,
|
|
696
|
+
};
|
|
697
|
+
return router;
|
|
698
|
+
}
|
|
699
|
+
function $navigate(path) {
|
|
700
|
+
if (activeRouter) {
|
|
701
|
+
activeRouter.navigate(path);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
const nextPath = normalizePath(path);
|
|
705
|
+
window.history.pushState({}, '', nextPath);
|
|
706
|
+
}
|
|
707
|
+
function $back() {
|
|
708
|
+
if (activeRouter) {
|
|
709
|
+
activeRouter.back();
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
window.history.back();
|
|
713
|
+
}
|
|
714
|
+
function $forward() {
|
|
715
|
+
if (activeRouter) {
|
|
716
|
+
activeRouter.forward();
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
window.history.forward();
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// src/runtime/index.ts
|
|
723
|
+
// the ~5KB runtime that ships with every tardisjs app
|
|
724
|
+
const $runtime = {
|
|
725
|
+
// selector
|
|
726
|
+
$,
|
|
727
|
+
params: $params,
|
|
728
|
+
navigate: $navigate,
|
|
729
|
+
back: $back,
|
|
730
|
+
forward: $forward,
|
|
731
|
+
// state
|
|
732
|
+
state: createState,
|
|
733
|
+
update: $update,
|
|
734
|
+
toggle: $toggle,
|
|
735
|
+
reset: $reset,
|
|
736
|
+
batch: $batch,
|
|
737
|
+
registerDep,
|
|
738
|
+
// fetch
|
|
739
|
+
fetch: $fetch,
|
|
740
|
+
// render
|
|
741
|
+
if: $if,
|
|
742
|
+
each: $each,
|
|
743
|
+
show: $show,
|
|
744
|
+
portal: $portal,
|
|
745
|
+
bind,
|
|
746
|
+
bindClass,
|
|
747
|
+
bindAttr,
|
|
748
|
+
resolveStyles,
|
|
749
|
+
text,
|
|
750
|
+
// styles
|
|
751
|
+
styles: createStyles,
|
|
752
|
+
// registry
|
|
753
|
+
register,
|
|
754
|
+
unregister,
|
|
755
|
+
getAll,
|
|
756
|
+
getById,
|
|
757
|
+
// events
|
|
758
|
+
events: registerEvents,
|
|
759
|
+
flush: flushMountCallbacks,
|
|
760
|
+
// placeholder — used during compilation before ui is wired up
|
|
761
|
+
placeholder(name) {
|
|
762
|
+
const el = document.createElement('div');
|
|
763
|
+
el.setAttribute('data-component', name);
|
|
764
|
+
return el;
|
|
765
|
+
},
|
|
766
|
+
// component instantiation
|
|
767
|
+
component(name, props) {
|
|
768
|
+
// looks up the component factory from a global registry
|
|
769
|
+
// populated when components are imported
|
|
770
|
+
const globalRegistry = window;
|
|
771
|
+
const factory = globalRegistry[`__tardis_${name}`];
|
|
772
|
+
if (factory)
|
|
773
|
+
return factory(props);
|
|
774
|
+
const el = document.createElement('div');
|
|
775
|
+
el.setAttribute('data-component', name);
|
|
776
|
+
el.setAttribute('data-props', JSON.stringify(props));
|
|
777
|
+
return el;
|
|
778
|
+
},
|
|
779
|
+
// chain — attaches event to a chained element
|
|
780
|
+
chain(el, event, handler) {
|
|
781
|
+
if (event === '__mount') {
|
|
782
|
+
flushMountCallbacks();
|
|
783
|
+
}
|
|
784
|
+
else {
|
|
785
|
+
el.addEventListener(event, handler);
|
|
786
|
+
}
|
|
787
|
+
},
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
export { $, $back, $batch, $each, $fetch, $forward, $if, $navigate, $params, $portal, $reset, $runtime, $show, $toggle, $update, ComponentSelection, DOMSelection, bind, bindAttr, bindClass, clearRegistry, createRouter, createState, createStyles, flushDestroyCallbacks, flushMountCallbacks, getAll, getByClass, getById, getByPropValue, getByStateValue, register, registerDep, registerEvents, resolveStyles, text, unregister };
|
|
791
|
+
//# sourceMappingURL=index.mjs.map
|