micra.js 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 +166 -0
- package/dist/core/bus.d.ts +32 -0
- package/dist/core/mount.d.ts +27 -0
- package/dist/core/reactive.d.ts +31 -0
- package/dist/core/registry.d.ts +56 -0
- package/dist/core/start.d.ts +26 -0
- package/dist/dom/directives.d.ts +39 -0
- package/dist/dom/each.d.ts +25 -0
- package/dist/dom/events.d.ts +41 -0
- package/dist/dom/query.d.ts +23 -0
- package/dist/dom/refs.d.ts +23 -0
- package/dist/index.d.ts +29 -0
- package/dist/micra.cjs.js +576 -0
- package/dist/micra.cjs.js.map +7 -0
- package/dist/micra.esm.js +554 -0
- package/dist/micra.esm.js.map +7 -0
- package/dist/micra.js +578 -0
- package/dist/micra.js.map +7 -0
- package/dist/micra.min.js +2 -0
- package/dist/types.d.ts +138 -0
- package/dist/utils/expr.d.ts +27 -0
- package/dist/utils/fetch.d.ts +45 -0
- package/package.json +59 -0
- package/src/core/bus.ts +49 -0
- package/src/core/mount.ts +144 -0
- package/src/core/reactive.ts +50 -0
- package/src/core/registry.ts +100 -0
- package/src/core/start.ts +42 -0
- package/src/dom/directives.ts +207 -0
- package/src/dom/each.ts +167 -0
- package/src/dom/events.ts +145 -0
- package/src/dom/query.ts +36 -0
- package/src/dom/refs.ts +35 -0
- package/src/index.ts +42 -0
- package/src/types.ts +157 -0
- package/src/utils/expr.ts +72 -0
- package/src/utils/fetch.ts +100 -0
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Micra.js
|
|
2
|
+
|
|
3
|
+
Micra.js is a lightweight reactive TypeScript framework for small sites and SaaS apps. It gives you reactive state, DOM directives, keyed list rendering, an event bus, SSR-friendly props, and auto-mounting in about 3.7 KB gzip.
|
|
4
|
+
|
|
5
|
+
## What is Micra.js?
|
|
6
|
+
|
|
7
|
+
Micra.js is designed for server-rendered apps and small frontends that need a little reactivity without a large framework.
|
|
8
|
+
|
|
9
|
+
Use it when you want:
|
|
10
|
+
|
|
11
|
+
- JS expressions in directives like `data-if="count > 0"`
|
|
12
|
+
- keyed list diffing with `data-each` + `data-key`
|
|
13
|
+
- auto-mounting with `data-component`, `Micra.define()`, and `Micra.start()`
|
|
14
|
+
- SSR props from `data-*` attributes via `this.prop()`
|
|
15
|
+
- a built-in `this.fetch()` helper
|
|
16
|
+
- a small global event bus with `Micra.on()` and `Micra.emit()`
|
|
17
|
+
- DOM refs via `data-ref`
|
|
18
|
+
- additive class toggling with `data-class`
|
|
19
|
+
- simple lifecycle hooks: `onCreate`, `onDestroy`
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```html
|
|
24
|
+
<div data-component="counter">
|
|
25
|
+
<button @click="decrement">-</button>
|
|
26
|
+
<strong data-text="count"></strong>
|
|
27
|
+
<button @click="increment">+</button>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<script src="https://unpkg.com/micra.js/dist/micra.min.js"></script>
|
|
31
|
+
<script>
|
|
32
|
+
Micra.define('counter', {
|
|
33
|
+
state: { count: 0 },
|
|
34
|
+
increment() { this.state.count++ },
|
|
35
|
+
decrement() { this.state.count-- },
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
Micra.start()
|
|
39
|
+
</script>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
### CDN
|
|
45
|
+
|
|
46
|
+
```html
|
|
47
|
+
<script src="https://unpkg.com/micra.js/dist/micra.min.js"></script>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### npm
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install micra.js
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import * as Micra from 'micra'
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Basic usage
|
|
61
|
+
|
|
62
|
+
A counter mounted automatically from `data-component`:
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<div data-component="counter">
|
|
66
|
+
<p>Count: <span data-text="count"></span></p>
|
|
67
|
+
<button @click="decrement">-</button>
|
|
68
|
+
<button @click="increment">+</button>
|
|
69
|
+
<button @click="reset">Reset</button>
|
|
70
|
+
</div>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import * as Micra from 'micra'
|
|
75
|
+
|
|
76
|
+
Micra.define('counter', {
|
|
77
|
+
state: { count: 0 },
|
|
78
|
+
|
|
79
|
+
increment() {
|
|
80
|
+
this.state.count++
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
decrement() {
|
|
84
|
+
this.state.count--
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
reset() {
|
|
88
|
+
this.state.count = 0
|
|
89
|
+
},
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
Micra.start()
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Directives
|
|
96
|
+
|
|
97
|
+
| Directive | Example | Description |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| `data-text` | `data-text="name"` | Set `textContent` |
|
|
100
|
+
| `data-html` | `data-html="content"` | Set `innerHTML` |
|
|
101
|
+
| `data-if` | `data-if="count > 0"` | Toggle display |
|
|
102
|
+
| `data-show` | `data-show="loaded"` | Alias of `data-if` |
|
|
103
|
+
| `data-bind` | `data-bind="href:url, disabled:loading"` | Bind attributes |
|
|
104
|
+
| `data-model` | `data-model="search"` | Two-way input binding |
|
|
105
|
+
| `data-each` | `data-each="items" data-key="id"` | List rendering |
|
|
106
|
+
| `data-ref` | `data-ref="chart"` | DOM ref in `this.refs` |
|
|
107
|
+
| `data-class` | `data-class="active:isActive"` | Toggle classes additively |
|
|
108
|
+
| `data-on` | `data-on="click:save"` | Bind DOM events |
|
|
109
|
+
| `@event` | `@click="increment"` | Shorthand event binding |
|
|
110
|
+
|
|
111
|
+
## API reference summary
|
|
112
|
+
|
|
113
|
+
### Register
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
Micra.define(name: string, definition: ComponentDefinition): void
|
|
117
|
+
Micra.defineComponent(definition): ComponentDefinition
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Mount
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
Micra.mount(selector: string | HTMLElement, definition): ComponentInstance | null
|
|
124
|
+
Micra.start(root?: Document | HTMLElement): void
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Event bus
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
Micra.on(event, handler): UnsubFn
|
|
131
|
+
Micra.off(event, handler): void
|
|
132
|
+
Micra.emit(event, payload?): void
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### DevTools
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
Micra.instances(): ReadonlyMap<HTMLElement, ComponentInstance>
|
|
139
|
+
Micra.registry(): ReadonlyMap<string, ComponentDefinition>
|
|
140
|
+
Micra.debug() // prints all live components, their state and $el to the console
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Component instance (`this` inside methods)
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
this.$el
|
|
147
|
+
this.state
|
|
148
|
+
this.refs
|
|
149
|
+
this.render()
|
|
150
|
+
this.destroy()
|
|
151
|
+
this.prop(name, default?)
|
|
152
|
+
this.fetch(url, options?)
|
|
153
|
+
this.emit(event, payload?)
|
|
154
|
+
this.on(event, handler)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Documentation
|
|
158
|
+
|
|
159
|
+
- [Getting started](./docs/getting-started.md)
|
|
160
|
+
- [Core concepts](./docs/concepts.md)
|
|
161
|
+
- [Directives](./docs/directives.md)
|
|
162
|
+
- [State](./docs/state.md)
|
|
163
|
+
- [Lifecycle](./docs/lifecycle.md)
|
|
164
|
+
- [SSR](./docs/ssr.md)
|
|
165
|
+
- [Examples](./docs/examples.md)
|
|
166
|
+
- [API reference](./docs/api-reference.md)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/core/bus.ts — Global event bus.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Publish events (emit)
|
|
6
|
+
* - Subscribe and unsubscribe (on / off)
|
|
7
|
+
* - Provide unsubscribe tokens for component cleanup
|
|
8
|
+
*
|
|
9
|
+
* LLM NOTE: The bus is a module-level singleton.
|
|
10
|
+
* Component instances subscribe via `instance.on()` which auto-registers
|
|
11
|
+
* the unsub token in `instance.__micraSubs` for cleanup on destroy().
|
|
12
|
+
*/
|
|
13
|
+
import type { EventHandler, UnsubFn } from '../types';
|
|
14
|
+
/**
|
|
15
|
+
* Subscribe to a named event. Returns an unsubscribe function.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* const unsub = on('user:login', (user) => console.log(user))
|
|
19
|
+
* unsub() // stop listening
|
|
20
|
+
*/
|
|
21
|
+
export declare function on<T = unknown>(event: string, handler: EventHandler<T>): UnsubFn;
|
|
22
|
+
/**
|
|
23
|
+
* Unsubscribe a specific handler from an event.
|
|
24
|
+
*/
|
|
25
|
+
export declare function off(event: string, handler: EventHandler): void;
|
|
26
|
+
/**
|
|
27
|
+
* Publish an event to all subscribers. Errors are caught per-handler.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* emit('user:updated', { id: 1, name: 'Alice' })
|
|
31
|
+
*/
|
|
32
|
+
export declare function emit(event: string, payload?: unknown): void;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/core/mount.ts — Mount a component definition onto a DOM element.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Create and initialize an InternalInstance
|
|
6
|
+
* - Set up reactive state + batch scheduler
|
|
7
|
+
* - Wire render(), destroy(), prop(), fetch(), on(), emit()
|
|
8
|
+
* - Run initial render + call onCreate() in a microtask
|
|
9
|
+
*
|
|
10
|
+
* LLM NOTE: This is the core of the Micra runtime.
|
|
11
|
+
* mount() is called by both the public Micra.mount() API and by start()
|
|
12
|
+
* (which scans the DOM for [data-component] elements).
|
|
13
|
+
*/
|
|
14
|
+
import type { ComponentDefinition, ComponentInstance, StateRecord } from '../types';
|
|
15
|
+
/**
|
|
16
|
+
* Mount a component definition onto a DOM element.
|
|
17
|
+
* Returns the component instance, or null if the root element is not found.
|
|
18
|
+
*
|
|
19
|
+
* Already-mounted elements return the existing instance.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* const instance = Micra.mount('#counter', {
|
|
23
|
+
* state: { count: 0 },
|
|
24
|
+
* inc() { this.state.count++ },
|
|
25
|
+
* })
|
|
26
|
+
*/
|
|
27
|
+
export declare function mount<S extends StateRecord>(selector: string | HTMLElement, definition: ComponentDefinition<S>): ComponentInstance<S> | null;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/core/reactive.ts — Reactive state proxy and batch scheduler.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Wrap a plain state object in a Proxy that notifies on writes
|
|
6
|
+
* - Batch multiple synchronous mutations into a single microtask render
|
|
7
|
+
*
|
|
8
|
+
* LLM NOTE: Both functions are PURE constructors — they have no side effects
|
|
9
|
+
* beyond setting up a Proxy / Promise chain. No DOM access here.
|
|
10
|
+
*/
|
|
11
|
+
import type { StateRecord } from '../types';
|
|
12
|
+
/**
|
|
13
|
+
* Wrap `obj` in a shallow Proxy. Any property write calls `schedule()`.
|
|
14
|
+
* Arrays: replace, don't mutate — `state.items = [...state.items, x]`.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* const raw = { count: 0 }
|
|
18
|
+
* const state = createReactiveState(raw, render)
|
|
19
|
+
* state.count = 5 // triggers render() in next microtask
|
|
20
|
+
*/
|
|
21
|
+
export declare function createReactiveState<S extends StateRecord>(obj: S, schedule: () => void): S;
|
|
22
|
+
/**
|
|
23
|
+
* Return a debounce function that defers `render` to the next microtask.
|
|
24
|
+
* Multiple calls within the same tick collapse to a single render.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* const schedule = createScheduler(render)
|
|
28
|
+
* schedule() // defers render
|
|
29
|
+
* schedule() // no-op — already pending
|
|
30
|
+
*/
|
|
31
|
+
export declare function createScheduler(render: () => void): () => void;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/core/registry.ts — Component definition registry and instance store.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Store named component definitions (define / registry)
|
|
6
|
+
* - Store live component instances keyed by root HTMLElement (instances)
|
|
7
|
+
*
|
|
8
|
+
* LLM NOTE: Both maps are module-level singletons (one per page load).
|
|
9
|
+
* They are intentionally mutable from mount.ts and start.ts.
|
|
10
|
+
*/
|
|
11
|
+
import type { ComponentDefinition, ComponentInstance, InternalInstance, StateRecord } from '../types';
|
|
12
|
+
export declare const _registry: Map<string, ComponentDefinition>;
|
|
13
|
+
export declare const _instances: Map<HTMLElement, InternalInstance<StateRecord>>;
|
|
14
|
+
/**
|
|
15
|
+
* Register a component definition under `name`.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* define('counter', { state: { count: 0 }, inc() { this.state.count++ } })
|
|
19
|
+
*/
|
|
20
|
+
export declare function define<S extends StateRecord>(name: string, definition: ComponentDefinition<S>): void;
|
|
21
|
+
/**
|
|
22
|
+
* Type-helper — returns `definition` unchanged but lets TypeScript infer `S`
|
|
23
|
+
* from the `state` literal so all methods are typed with the correct `this`.
|
|
24
|
+
*
|
|
25
|
+
* Use this when defining a component outside a `define()` call.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* const counter = defineComponent({
|
|
29
|
+
* state: { count: 0 },
|
|
30
|
+
* increment() { this.state.count++ }, // this.state: { count: number } ✓
|
|
31
|
+
* })
|
|
32
|
+
* Micra.define('counter', counter)
|
|
33
|
+
*/
|
|
34
|
+
export declare function defineComponent<S extends StateRecord>(definition: ComponentDefinition<S>): ComponentDefinition<S>;
|
|
35
|
+
/**
|
|
36
|
+
* Returns a read-only view of all live instances (keyed by root element).
|
|
37
|
+
* Useful for DevTools / debugging.
|
|
38
|
+
*/
|
|
39
|
+
export declare function instances(): ReadonlyMap<HTMLElement, ComponentInstance>;
|
|
40
|
+
/**
|
|
41
|
+
* Returns a read-only view of all registered component definitions.
|
|
42
|
+
* Useful for DevTools / debugging.
|
|
43
|
+
*/
|
|
44
|
+
export declare function registry(): ReadonlyMap<string, ComponentDefinition>;
|
|
45
|
+
/**
|
|
46
|
+
* Print all live component instances to the browser console.
|
|
47
|
+
* Shows component name, root element, and current state for each instance.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* // In browser DevTools console:
|
|
51
|
+
* Micra.debug()
|
|
52
|
+
* // [Micra] 3 live component(s)
|
|
53
|
+
* // counter $el: <div> state: { count: 5 }
|
|
54
|
+
* // user-list $el: <div> state: { users: [...], loading: false }
|
|
55
|
+
*/
|
|
56
|
+
export declare function debug(): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/core/start.ts — Auto-mount via [data-component].
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Scan the DOM (or a subtree) for [data-component] elements
|
|
6
|
+
* - Mount each using the registered definition
|
|
7
|
+
* - Skip already-mounted elements (safe to call multiple times)
|
|
8
|
+
* - Warn clearly when a component name is not registered
|
|
9
|
+
*
|
|
10
|
+
* LLM NOTE: start() is SSR-friendly — calling it multiple times is safe
|
|
11
|
+
* because mount() checks _instances before re-mounting.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Scan for `[data-component]` elements and auto-mount registered definitions.
|
|
15
|
+
*
|
|
16
|
+
* Pass a subtree root to limit the scan (e.g., after a partial SSR update):
|
|
17
|
+
* `Micra.start(document.getElementById('panel'))`
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // Mount everything on the page (called once after DOM ready)
|
|
21
|
+
* Micra.start()
|
|
22
|
+
*
|
|
23
|
+
* // Re-mount after injecting new HTML
|
|
24
|
+
* Micra.start(document.querySelector('#dynamic-section'))
|
|
25
|
+
*/
|
|
26
|
+
export declare function start(root?: Document | HTMLElement): void;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/dom/directives.ts — Apply DOM directives to a component subtree.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - data-text, data-html, data-if, data-show, data-bind, data-model
|
|
6
|
+
* - data-class (additive class toggling)
|
|
7
|
+
* - Directive result cache (built once per element, reused on re-renders)
|
|
8
|
+
*
|
|
9
|
+
* LLM NOTE: applyDirectives() is called on every render. The directive cache
|
|
10
|
+
* (DirectiveCache on el.__micraCache) avoids repeated querySelectorAll on
|
|
11
|
+
* re-renders — cache is built lazily on the first call for each root element.
|
|
12
|
+
*
|
|
13
|
+
* Important: this module does NOT handle data-each — see dom/each.ts.
|
|
14
|
+
*/
|
|
15
|
+
import type { InternalInstance, StateRecord } from '../types';
|
|
16
|
+
import { warn } from '../utils/expr';
|
|
17
|
+
/**
|
|
18
|
+
* Apply all non-each directives to a component subtree.
|
|
19
|
+
*
|
|
20
|
+
* For regular Elements: directive bindings are cached in `el.__micraCache`
|
|
21
|
+
* after the first call — subsequent re-renders skip querySelectorAll entirely.
|
|
22
|
+
*
|
|
23
|
+
* For DocumentFragments (no-key each clones): always re-scan because these
|
|
24
|
+
* fragments are new clones on every render.
|
|
25
|
+
*
|
|
26
|
+
* @param root - Component root Element or DocumentFragment (no-key each clone)
|
|
27
|
+
* @param state - Expression state (may include item/index for each rows)
|
|
28
|
+
* @param rawState - Raw (non-proxy) state for model sync
|
|
29
|
+
* @param instance - Component instance (unused here, kept for future hooks)
|
|
30
|
+
*/
|
|
31
|
+
export declare function applyDirectives<S extends StateRecord>(root: Element | DocumentFragment, state: StateRecord, rawState: StateRecord, _instance: InternalInstance<S>): void;
|
|
32
|
+
/**
|
|
33
|
+
* Validate directive usage and emit dev warnings.
|
|
34
|
+
* Called once after the initial render of a component.
|
|
35
|
+
*
|
|
36
|
+
* @internal
|
|
37
|
+
*/
|
|
38
|
+
export declare function validateDirectives(root: Element): void;
|
|
39
|
+
export { warn };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/dom/each.ts — Keyed and non-keyed list rendering (data-each).
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Process `<template data-each="items" data-key="id">` elements
|
|
6
|
+
* - Keyed diff: reuse/reorder DOM nodes by key — O(n) with a Map
|
|
7
|
+
* - Non-keyed fallback: full replace (no key → warn in dev, full re-render)
|
|
8
|
+
* - Apply directives to each row with a scoped itemState
|
|
9
|
+
*
|
|
10
|
+
* LLM NOTE: renderList() is called on every render cycle AFTER applyDirectives().
|
|
11
|
+
* Only <template> elements with data-each are processed.
|
|
12
|
+
* Keyed mode (data-key present) mutates the DOM in-place — nodes are
|
|
13
|
+
* created once and reused. Non-keyed mode removes all nodes and re-clones.
|
|
14
|
+
*/
|
|
15
|
+
import type { InternalInstance, StateRecord } from '../types';
|
|
16
|
+
/**
|
|
17
|
+
* Process all `<template data-each>` elements owned by `root`.
|
|
18
|
+
* Scoped itemState makes `item`, `index`, `$index` available in row expressions.
|
|
19
|
+
*
|
|
20
|
+
* @param root - Component root Element
|
|
21
|
+
* @param state - Expression state (proxy merging rawState + instance)
|
|
22
|
+
* @param rawState - Raw (non-proxy) state — used for model binding
|
|
23
|
+
* @param instance - Component instance (for event binding)
|
|
24
|
+
*/
|
|
25
|
+
export declare function renderList<S extends StateRecord>(root: Element, state: StateRecord, rawState: StateRecord, instance: InternalInstance<S>): void;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/dom/events.ts — DOM event binding.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - Bind `data-on="event:method"` listeners (once per element)
|
|
6
|
+
* - Bind `@event="method"` shorthand (scanned once per component root)
|
|
7
|
+
*
|
|
8
|
+
* LLM NOTE: Listeners are attached exactly once. The `__micraEvents` and
|
|
9
|
+
* `__micraAtScanned` flags prevent duplicate bindings on re-renders.
|
|
10
|
+
*/
|
|
11
|
+
import type { InternalInstance, StateRecord } from '../types';
|
|
12
|
+
/**
|
|
13
|
+
* Bind `data-on="event:method[,event2:method2]"` listeners.
|
|
14
|
+
* Listeners are bound once — re-render calls are no-ops for already-bound elements.
|
|
15
|
+
*
|
|
16
|
+
* Supports modifiers: `click.prevent`, `click.stop`, `click.self`.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* <button data-on="click:save">Save</button>
|
|
20
|
+
* <form data-on="submit.prevent:handleSubmit">
|
|
21
|
+
*/
|
|
22
|
+
export declare function bindDataOn<S extends StateRecord>(root: Element, instance: InternalInstance<S>): void;
|
|
23
|
+
/**
|
|
24
|
+
* Bind `@event="method"` shorthand attributes (Stimulus-style).
|
|
25
|
+
* Scanned once per component root (guarded by `__micraAtScanned`).
|
|
26
|
+
* Supports the same modifiers as data-on: `@click.prevent="submit"`.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* <button @click="increment">+</button>
|
|
30
|
+
* <form @submit.prevent="handleSubmit">
|
|
31
|
+
*/
|
|
32
|
+
export declare function bindAtEvents<S extends StateRecord>(root: Element, instance: InternalInstance<S>): void;
|
|
33
|
+
/**
|
|
34
|
+
* Two-way binding: `data-model="key"` wires <input>/<select>/<textarea>
|
|
35
|
+
* to `state[key]`. Binding is attached once per element.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* <input data-model="search"> // updates state.search on every keystroke
|
|
39
|
+
* <select data-model="sortBy"> // updates state.sortBy on change
|
|
40
|
+
*/
|
|
41
|
+
export declare function bindModels<S extends StateRecord>(root: Element, instance: InternalInstance<S>): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/dom/query.ts — DOM query helpers.
|
|
3
|
+
*
|
|
4
|
+
* LLM NOTE: These are utility functions with no side effects.
|
|
5
|
+
* queryOwn is the critical function that prevents a parent component from
|
|
6
|
+
* accidentally processing directives belonging to a nested child component.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* querySelectorAll wrapper — returns a typed array.
|
|
10
|
+
*/
|
|
11
|
+
export declare function queryAll(root: ParentNode, sel: string): Element[];
|
|
12
|
+
/**
|
|
13
|
+
* Like querySelectorAll, but EXCLUDES elements that live inside a nested
|
|
14
|
+
* `[data-component]` subtree.
|
|
15
|
+
*
|
|
16
|
+
* This is what prevents a parent component's render() from clobbering
|
|
17
|
+
* the DOM managed by a child component.
|
|
18
|
+
*
|
|
19
|
+
* LLM NOTE: The walk goes up parentElement until it hits `root` or null.
|
|
20
|
+
* If any ancestor (between el and root) has data-component, the element is
|
|
21
|
+
* owned by that nested component, not by root's component — so we skip it.
|
|
22
|
+
*/
|
|
23
|
+
export declare function queryOwn(root: Element, attr: string): Element[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/dom/refs.ts — data-ref collection.
|
|
3
|
+
*
|
|
4
|
+
* Responsibilities:
|
|
5
|
+
* - After each render, scan for `[data-ref]` elements (owned by this component)
|
|
6
|
+
* - Populate `instance.refs` so methods can do `this.refs.chart` etc.
|
|
7
|
+
*
|
|
8
|
+
* LLM NOTE: This module is PURE relative to state — it only reads DOM attributes
|
|
9
|
+
* and writes to instance.refs. It does NOT trigger renders.
|
|
10
|
+
*/
|
|
11
|
+
import type { InternalInstance, StateRecord } from '../types';
|
|
12
|
+
/**
|
|
13
|
+
* Collect all `[data-ref="name"]` elements owned by this component root into
|
|
14
|
+
* `instance.refs`.
|
|
15
|
+
*
|
|
16
|
+
* Called once after the initial render and again on every re-render (refs may
|
|
17
|
+
* point to newly created elements after an each-list update).
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* // HTML: <canvas data-ref="chart">
|
|
21
|
+
* // JS: this.refs.chart → HTMLCanvasElement
|
|
22
|
+
*/
|
|
23
|
+
export declare function collectRefs<S extends StateRecord>(root: Element, instance: InternalInstance<S>): void;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Micra.js — Lightweight reactive framework for small sites and simple SaaS.
|
|
3
|
+
*
|
|
4
|
+
* Public surface — re-exports only.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - JS expressions in directives (data-if="count > 0")
|
|
8
|
+
* - Keyed list diffing (data-each="items" data-key="id")
|
|
9
|
+
* - Auto-mount via data-component (Micra.define + Micra.start)
|
|
10
|
+
* - Props from SSR data-attributes (this.prop('page'))
|
|
11
|
+
* - Built-in fetch helper (this.fetch('/api/...'))
|
|
12
|
+
* - Global event bus (Micra.on / Micra.emit)
|
|
13
|
+
* - DOM refs (data-ref="chart" → this.refs.chart)
|
|
14
|
+
* - Additive class toggling (data-class="active:isActive")
|
|
15
|
+
* - @event shorthand (@click="increment")
|
|
16
|
+
* - Lifecycle: onCreate, onDestroy
|
|
17
|
+
* - SSR-friendly: Micra.start() is safe to call multiple times
|
|
18
|
+
* - Directive cache: O(1) re-renders after first mount
|
|
19
|
+
*
|
|
20
|
+
* Size target: < 5 KB minified+gzipped
|
|
21
|
+
*
|
|
22
|
+
* @module Micra
|
|
23
|
+
*/
|
|
24
|
+
export type { StateRecord, UnsubFn, EventHandler, FetchOptions, ComponentInstance, ComponentDefinition, } from './types';
|
|
25
|
+
export { FetchError } from './utils/fetch';
|
|
26
|
+
export { define, defineComponent, instances, registry, debug } from './core/registry';
|
|
27
|
+
export { mount } from './core/mount';
|
|
28
|
+
export { start } from './core/start';
|
|
29
|
+
export { on, off, emit } from './core/bus';
|