@twobirds/microcomponents 0.4.1 → 0.6.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.
@@ -0,0 +1,92 @@
1
+ All functionality in Micro Components is registered in the browser's custom element registry, regardless of its purpose. This includes:
2
+
3
+ - Real web components that render visible DOM elements
4
+ - Logic-only components that execute code in constructor() and oneInit(), then self-remove
5
+ - CSS-only components that attach stylesheets to the document and remove themselves
6
+
7
+ When a component should not render anything visually, it declares `static tag = '';`. This tells defineCE to register the element but never produce visible markup. The component still runs its lifecycle callbacks (constructor → connectedCallback → oneInit), allowing it to execute setup logic or inject global styles before optionally removing itself from the DOM.
8
+
9
+ This unified registration model means every piece of functionality — visual, logical, or stylistic — follows the same pattern: extend DC, define behavior, register with defineCE().
10
+
11
+ ---
12
+
13
+ ## Tag Name to File Path Mapping
14
+
15
+ Because `autoload()` resolves an undefined tag to a script URL by converting kebab-case to path segments (`<shop-header>` → `./shop/header.js`), the tag name _is_ the file path. The kebab-case segments become the directory path and the last segment becomes the filename. The directory structure in `dist/` must mirror this segmentation for autoloading to work.
16
+
17
+ | Tag | Source File | Companion Files |
18
+ | --------------------------- | -------------------------------- | --------------------------------------------------------------------------- |
19
+ | `<shop-header>` | `src/shop/header.ts` | `shop/header.target.html`, `shop/header.target.css` |
20
+ | `<shop-sidebar>` | `src/shop/sidebar.ts` | `shop/sidebar.target.html`, `shop/sidebar.target.css` |
21
+ | `<cart-container-articles>` | `src/cart/container/articles.ts` | `cart/container/articles.shadow.html`, `cart/container/articles.shadow.css` |
22
+
23
+ This is the same contract that makes one-class-per-file mandatory (see [microcomponent-architecture](microcomponent-architecture.md)): one tag → one script → one class.
24
+
25
+ ---
26
+
27
+ ## Frontend Nesting in Folder Structure
28
+
29
+ When a component is visually and structurally nested inside another (a composed parent projecting child web components — see [mc-types](mc-types.md) §1c), the folder hierarchy reflects that nesting. A child's tag name includes its parent's prefix, and its file resides in a subdirectory of the parent. This applies recursively to grandchildren.
30
+
31
+ | Parent Tag | Child Tag | Source File |
32
+ | ------------------ | --------------------------- | -------------------------------- |
33
+ | `<app-header>` | `<app-header-left>` | `src/app/header/left.ts` |
34
+ | `<app-header>` | `<app-header-right>` | `src/app/header/right.ts` |
35
+ | `<cart-container>` | `<cart-container-articles>` | `src/cart/container/articles.ts` |
36
+
37
+ ```
38
+ src/
39
+ app/
40
+ header.ts ← <app-header>
41
+ header.css
42
+ left.ts ← <app-header-left> (child of header)
43
+ left.css
44
+ right.ts ← <app-header-right> (child of header)
45
+ right.css
46
+ ```
47
+
48
+ The DOM hierarchy is therefore immediately visible in the file system.
49
+
50
+ ---
51
+
52
+ ## General-Purpose Components in Side Folders
53
+
54
+ Components serving cross-cutting concerns — used across unrelated parts of the frontend rather than belonging to one visual hierarchy — reside in dedicated side folders organized by purpose. These are not nested under any UI component but exist at a parallel level. They follow the same tag-to-file mapping and are imported directly by any component that needs them, regardless of where that component lives in the UI hierarchy.
55
+
56
+ | Purpose | Folder | Example Tags |
57
+ | -------------------- | ------------- | --------------------------------------------- |
58
+ | Server communication | `transport/` | `<transport-request>`, `<transport-response>` |
59
+ | Authentication | `auth/` | `<auth-guard>`, `<auth-token>` |
60
+ | State management | `store/` | `<store-cart>`, `<store-user>` |
61
+ | Validation | `validation/` | `<validation-email>`, `<validation-required>` |
62
+ | Utilities | `helpers/` | `<helper-formatter>`, `<helper-locale>` |
63
+
64
+ ### Diversification Within General-Purpose Folders
65
+
66
+ General-purpose folders can contain subdirectories for more specific variations within a category, flowing from broad purpose to narrow specialization. Subfolders represent functional specializations, not visual nesting.
67
+
68
+ | Purpose | Folder Structure | Example Tags |
69
+ | ------------------------ | ------------------ | ------------------------------------------------------- |
70
+ | Transport → HTTP methods | `transport/http/` | `<transport-http-get>`, `<transport-http-post>` |
71
+ | Transport → WebSocket | `transport/ws/` | `<transport-ws-client>`, `<transport-ws-handler>` |
72
+ | Validation → Form fields | `validation/form/` | `<validation-form-email>`, `<validation-form-password>` |
73
+ | Store → Persistence | `store/persist/` | `<store-persist-local>`, `<store-persist-session>` |
74
+
75
+ ```
76
+ src/
77
+ transport/
78
+ request.ts ← <transport-request> (general)
79
+ response.ts ← <transport-response> (general)
80
+ http/ ← HTTP-specific diversification
81
+ get.ts ← <transport-http-get>
82
+ post.ts ← <transport-http-post>
83
+ ws/ ← WebSocket-specific diversification
84
+ client.ts ← <transport-ws-client>
85
+ validation/
86
+ email.ts ← <validation-email> (general)
87
+ form/ ← Form-specific diversification
88
+ email.ts ← <validation-form-email>
89
+ password.ts ← <validation-form-password>
90
+ ```
91
+
92
+ The same tag-to-file mapping applies: `<transport-http-get>` maps to `src/transport/http/get.ts`.
@@ -0,0 +1,45 @@
1
+ Events in Micro Components use CustomEvent with directional bubbling:
2
+
3
+ Handler conventions (auto-attached):
4
+
5
+ - on<EventName>() - persistent listener (fires every time)
6
+ - one<EventName>() - one-time listener (fires once, then detaches)
7
+
8
+ Lifecycle events when registered via defineCE:
9
+
10
+ - oneInit() - constructor finished; all synchronous setup complete
11
+ - onConnected() - element inserted into DOM
12
+ - onDisconnected() - element removed from DOM
13
+ - onAdopted() - element moved to new document
14
+ - onAttributeChanged() - observed attribute changed
15
+
16
+ Bubble directions in trigger():
17
+
18
+ - 'l' = local dispatch only (default)
19
+ - 'u' = bubble up towards ancestors
20
+ - 'd' = bubble down to descendants
21
+ - Can combine: 'lu', 'ld', 'lud'
22
+
23
+ Only use bubbling when there is a specific reason for it. Usually directly addressing the target is better, because of the speed advantage using querySelectorAll().
24
+
25
+ ## Payload & propagation
26
+
27
+ MC-triggered custom events and lifecycle events are `McEvent<D>` — a `CustomEvent` whose `detail` is `{ data, control }`. The payload is always read from `ev.detail.data`, never `ev.data`.
28
+
29
+ ```ts
30
+ cart.on('CartUpdate', (ev: McEvent<{ count: number }>) => {
31
+ const count = ev.detail.data.count; // payload lives under detail.data
32
+ });
33
+ ```
34
+
35
+ To stop a bubbling event, call `stop()` on the bubble controller carried in the same detail object:
36
+
37
+ ```ts
38
+ cart.on('CartUpdate', (ev: McEvent<{ count: number }>) => {
39
+ ev.detail.control.stop(); // halt 'u'/'d' propagation
40
+ });
41
+ ```
42
+
43
+ When the event name is part of the component's event map (`EM` on `DC`), the payload type is inferred and the `McEvent<…>` annotation can be omitted; annotate it explicitly only for ad-hoc events.
44
+
45
+ Native DOM events (`click`, `input`, `submit`, …) are **not** `McEvent` — they carry their natural payload types (`MouseEvent`, `InputEvent`, …) and have no `detail.data` / `detail.control`. Only events dispatched through `trigger()` (and the lifecycle events emitted by `defineCE`) follow the `McEvent` shape above.
@@ -0,0 +1,10 @@
1
+ When implementing functionality, follow this strict priority chain. Each tier must be exhausted before descending to the next:
2
+
3
+ **Tier 1 — MC Framework Automatic Functionality (mandatory when inside the framework)**
4
+ Always use built-in DC/MC conventions first. This includes auto-attaching event handlers (`onClick()`, `oneInit()`, etc.), lifecycle callbacks, static properties (`tag`, `assets`, `attributes`), and plugin methods (`.subscribe()` `.bind()` `.formValues()`). These mechanisms are the framework's intended way of wiring behavior — never bypass them with manual alternatives when working inside a DC or MC class.
5
+
6
+ **Tier 2 — Exported Library Functions (when outside the framework)**
7
+ When using Micro Components alongside another framework, in plain JavaScript, or in non-DC/MC contexts, use the library's exported utility functions: `on()`, `one()`, `trigger()`, `observe()`, `formValues()`, `autoload()`, etc. These are the official public API for accessing MC functionality without extending DC or MC.
8
+
9
+ **Tier 3 — Pure TypeScript / Vanilla JavaScript (last resort only)**
10
+ Only fall back to native browser APIs (`addEventListener`, `querySelector`, manual DOM manipulation, raw `CustomEvent`, etc.) when neither Tier 1 nor Tier 2 provides a solution. This is the least preferred approach and should be avoided whenever any MC mechanism can accomplish the task.
@@ -0,0 +1,349 @@
1
+ # Micro Component Types
2
+
3
+ Every piece of functionality in Micro Components is registered in the custom element
4
+ registry — visual, logical, or stylistic. What differs is the **base class**, the
5
+ **registration pattern**, and **what (if anything) is rendered**.
6
+
7
+ This document defines each type, its purpose, and when to reach for it.
8
+
9
+ ---
10
+
11
+ ## Class Hierarchy
12
+
13
+ ```
14
+ McBase shared root — WeakRef target, auto-listener attachment, Init lifecycle
15
+ ├── MC general-purpose, no DOM requirement
16
+ └── DC DOM-centric, operates on an HTMLElement
17
+ ```
18
+
19
+ `McBase` is never extended directly. Choose `MC` for DOM-free logic and `DC` for
20
+ anything that touches an element.
21
+
22
+ ---
23
+
24
+ ## Overview
25
+
26
+ | Type | Base | Registry | Instantiation | Renders | Typical Use |
27
+ | -------------------- | ----------------- | ---------- | --------------------------------------------- | -------- | ------------------------ |
28
+ | Visual Component | `DC` | `defineCE` | `<tag-name>` in markup, or `new Ctor(data)` | Yes | UI elements |
29
+ | Logic-Only Component | `DC` (`tag = ''`) | `defineCE` | `getCE()` → `DC.add(host, key, new Ctor())` | No | Router, auth, stores |
30
+ | Style-Only Component | `DC` (`tag = ''`) | `defineCE` | `<tag-name>` in markup, or side-effect import | CSS only | Global stylesheets |
31
+ | Pure Logic Component | `MC` | — | `new Mc(data)` — direct import | No | Data processing, helpers |
32
+ | Plugin | function | — | `plugin(DC)` — called, never instantiated | No | Prototype extensions |
33
+ | Utility Function | function | — | called directly | No | Standalone API |
34
+
35
+ Registered `DC` types have three instantiation paths:
36
+
37
+ - **Declarative** — the tag appears in markup and `autoload` loads and constructs it. No
38
+ import statement is needed.
39
+ - **Programmatic** — `getCE('tag-name')` returns the constructor and `new Ctor(data)`
40
+ creates the element. Only the `data` payload is passed; the host element is created by
41
+ the custom element constructor and arrives as the first argument of the `DC`
42
+ constructor.
43
+ - **Attached** — a logic-only component is constructed with `new Ctor()` and bound to an
44
+ existing host via `DC.add(host, key, instance)`. It never appears in markup itself.
45
+
46
+ ---
47
+
48
+ ## 1. Visual Components
49
+
50
+ **Base:** `DC` · **Registry:** `defineCE` · **Renders:** visible DOM
51
+
52
+ The default type. Any element the user can see and interact with is a `DC`. It owns a
53
+ host element, receives lifecycle events, and wires its own event handlers by naming
54
+ convention.
55
+
56
+ ```ts
57
+ class ShopHeader extends DC {
58
+ static tag = 'header';
59
+ static attributes = { role: 'banner' };
60
+ static assets = ['targetHtml', 'targetCss'];
61
+ }
62
+
63
+ defineCE('shop-header', ShopHeader);
64
+ ```
65
+
66
+ Visual components split into three structural sub-patterns.
67
+
68
+ ### 1a. Target-Based (flat content)
69
+
70
+ Markup is injected into `this.target.innerHTML`. Styles are scoped by tag or class and
71
+ adopted into the **document** via `injectCSS(document, ...)`.
72
+
73
+ Use when the internal structure is simple and flat — a card, a cart line, a label.
74
+
75
+ ```
76
+ src/shop/product/card.ts
77
+ src/shop/product/card.target.html
78
+ src/shop/product/card.target.css
79
+ ```
80
+
81
+ ### 1b. Shadow-DOM Based (complex structure)
82
+
83
+ Markup is injected into `shadowRoot.innerHTML`. Internal styles are adopted into the
84
+ **shadow root** via `injectCSS(shadowRoot, ...)`.
85
+
86
+ Use when the component has several distinct regions or needs style isolation.
87
+
88
+ - _Simple shadow_ — one unnamed slot; light-DOM content slots in automatically.
89
+ - _Complex shadow_ — multiple named slots; light-DOM content uses `slot="…"`.
90
+
91
+ ```
92
+ src/cart/container/articles.ts
93
+ src/cart/container/articles.shadow.html
94
+ src/cart/container/articles.shadow.css
95
+ ```
96
+
97
+ ### 1c. Composed Components (parents)
98
+
99
+ A shadow-DOM component that projects **child web components** from the light DOM through
100
+ named `<slot name="…">` elements. Child elements are never embedded in the shadow
101
+ template — projection keeps them in the light DOM so `autoload` can discover and
102
+ register them.
103
+
104
+ External design of the composed unit is driven by namespaced CSS custom properties
105
+ (`--mcui-btn-*`, `--mcui-card-*`), with defaults declared in the companion `.css` file.
106
+
107
+ ```html
108
+ <cart-container>
109
+ <cart-container-articles slot="articles"></cart-container-articles>
110
+ <cart-container-checkout slot="checkout"></cart-container-checkout>
111
+ </cart-container>
112
+ ```
113
+
114
+ ### 1d. Leaf Components
115
+
116
+ Self-contained visual units that project no child custom elements — a button, a badge,
117
+ a single cart line. They are the smallest building block and are usually styled through
118
+ the namespaced variables of their composed parent.
119
+
120
+ ---
121
+
122
+ ## Companion Files
123
+
124
+ Each custom element (`.ts`/`.js`) ships with optional companion files sharing the same
125
+ base name. The suffix encodes the structural sub-pattern (§1a–1b) and the styling target.
126
+
127
+ | Suffix | Purpose | Styling Target |
128
+ | -------------- | --------------------------------------------------------------------- | ---------------------------------------------------- |
129
+ | `.css` | CSS custom properties (variable defaults) only — no structural styles | N/A (token definitions) |
130
+ | `.target.css` | Structural style rules for target DOM (`innerHTML`) | Global document scope via `injectCSS(document, ...)` |
131
+ | `.shadow.css` | Structural style rules for shadow DOM | Shadow root via `injectCSS(shadowRoot, ...)` |
132
+ | `.target.html` | HTML template injected into `this.target.innerHTML` | N/A (markup) |
133
+ | `.shadow.html` | HTML template injected into `shadowRoot.innerHTML` | N/A (markup) |
134
+ | `.json` | Static data files imported by the component | N/A (data) |
135
+
136
+ Companion files follow the same directory structure as their source file. A component at
137
+ `src/shop/sidebar.ts` has companions at `src/shop/sidebar.target.html`,
138
+ `src/shop/sidebar.target.css`, etc. Templates, styles, and data are loaded via
139
+ `loadAssets()` / `processAssets()`; keep the `.ts` source focused on logic only —
140
+ constructor, lifecycle, event handlers, state.
141
+
142
+ ### Inline Templates — Escape Hatch
143
+
144
+ A simple `const template` string constant is allowed **only** when the component has no
145
+ advanced handling: no reactive state (`observe`/`bind`/`subscribe`), no dynamic DOM
146
+ mutations after render, and no separate stylesheet to ship. This is useful for one-time
147
+ placeholder replacement during programmatic creation:
148
+
149
+ ```ts
150
+ const template = '<h1>{title}</h1><button click="this|onGo">Go</button>';
151
+ // ...
152
+ processAssets(this, { targetHtml: parse(template, data) });
153
+ ```
154
+
155
+ `processAssets` still runs `bindTemplateEvents`, so declarative `click='this|onHandler'`
156
+ bindings work for inline strings too. As soon as the component needs reactive data, a
157
+ stylesheet, or autoload-discoverable assets, move the markup into a `.target.html` /
158
+ `.shadow.html` companion file.
159
+
160
+ ---
161
+
162
+ ## Theming via CSS Custom Properties
163
+
164
+ Components **never hard-code colors**; they read tokens. Four themes — `custom-dark` /
165
+ `custom-light` / `dark` / `light` — are pure token sets on `:root` (switched by a `theme`
166
+ attribute), implemented as style-only components (§3). The `custom-*` themes merge into
167
+ the standard settings, preserving `dark` and `light` defaults.
168
+
169
+ External design of a composed unit is driven by namespaced CSS custom properties
170
+ (`--mcui-btn-*`, `--mcui-card-*`), with defaults declared in the companion `.css` file.
171
+ Variable names always reflect the element namespace to avoid collisions — a
172
+ `shop-header` component defines `--shop-header-bg`, `--shop-header-color`, etc. in
173
+ `shop-header.css` and consumes them in `shop-header.target.css` / `shop-header.shadow.css`.
174
+
175
+ ---
176
+
177
+ ## 2. Logic-Only Components
178
+
179
+ **Base:** `DC` with `static tag = ''` · **Registry:** `defineCE` · **Renders:** nothing
180
+
181
+ Functionality that must be discoverable through the registry and attachable to other
182
+ elements, but produces no markup. `static tag = ''` tells `defineCE` to register the
183
+ element while the constructor removes the host from the DOM. The full lifecycle still
184
+ runs — `constructor` → `connectedCallback` → `oneInit` — so setup logic executes
185
+ normally.
186
+
187
+ ```ts
188
+ class Router extends DC {
189
+ static tag = '';
190
+
191
+ constructor(target: HTMLElement) {
192
+ super(target);
193
+ // pure setup, no markup
194
+ }
195
+ }
196
+
197
+ defineCE('app-router', Router);
198
+ ```
199
+
200
+ Retrieved from the registry and attached to a host with `DC.add()`:
201
+
202
+ ```ts
203
+ const Router = getCE('app-router')!;
204
+ DC.add(target, 'router', new Router());
205
+ ```
206
+
207
+ Use for: routing, history management, auth guards, state stores, validation engines,
208
+ translation services — anything that should live in the central repository and be
209
+ composable onto existing elements rather than inherited through a class chain.
210
+
211
+ These components are pulled in through **barrel imports** so they register by
212
+ side-effect without appearing in the DOM.
213
+
214
+ ---
215
+
216
+ ## 3. Style-Only Components
217
+
218
+ **Base:** `DC` with `static tag = ''` · **Registry:** `defineCE` · **Renders:** CSS only
219
+
220
+ Structurally identical to a logic-only component, but its side effect is a stylesheet.
221
+ It injects global rules and then self-removes; the styles persist regardless of the
222
+ element's removal.
223
+
224
+ ```ts
225
+ class PageCockpit extends DC {
226
+ static tag = '';
227
+ static assets = ['css'];
228
+ }
229
+
230
+ defineCE('page-cockpit', PageCockpit);
231
+ ```
232
+
233
+ Consumption options: place the tag in a template, side-effect import the `.css.ts`
234
+ module, `@import` the raw `.css` into a bundle, or import it with `?raw` and apply it
235
+ manually.
236
+
237
+ Use for: global `body`/`:root` token sets, theme definitions, reset stylesheets.
238
+
239
+ ---
240
+
241
+ ## 4. Pure Logic Components
242
+
243
+ **Base:** `MC` · **Registry:** none · **Renders:** nothing
244
+
245
+ `MC` is the DOM-free branch of the hierarchy. It skips listener auto-attachment and the
246
+ `Init` lifecycle entirely, which keeps it lightweight for server-side or non-DOM use.
247
+
248
+ It is **not** registered with `defineCE`. It is imported directly and attached as a
249
+ property on the component that needs it:
250
+
251
+ ```ts
252
+ import { PriceCalculator } from '../shop/helpers.js';
253
+
254
+ class CartSummary extends DC {
255
+ constructor(target: HTMLElement) {
256
+ super(target);
257
+ this.calculator = new PriceCalculator();
258
+ }
259
+ }
260
+ ```
261
+
262
+ Use for: reusable process-flow helpers, calculations, parsers, formatters — logic that
263
+ is genuinely independent of the DOM. If the logic needs registry discoverability or
264
+ lifecycle events, use a logic-only `DC` instead.
265
+
266
+ ---
267
+
268
+ ## 5. Plugins
269
+
270
+ **Base:** none (functions) · **Registry:** none · **Renders:** nothing
271
+
272
+ Plugins are functions that curry additional instance methods onto `McBase.prototype`,
273
+ making them available on every `DC` and `MC` instance. They are called once at module
274
+ level and are idempotent.
275
+
276
+ ```ts
277
+ observePlugin(DC); // adds notify(), subscribe(), bind()
278
+ formValuePlugin(DC); // adds formValues() — also pulls in observePlugin
279
+ ```
280
+
281
+ Use for: cross-cutting behavior that every component should be able to chain — state
282
+ observation, two-way form binding. A plugin is the right shape when the capability is
283
+ orthogonal to what the component _is_ rather than part of its identity.
284
+
285
+ ---
286
+
287
+ ## 6. Utility Functions
288
+
289
+ **Base:** none (plain exports) · **Registry:** none · **Renders:** nothing
290
+
291
+ The public API for using Micro Components **outside** a `DC`/`MC` class — in plain
292
+ JavaScript, alongside another framework, or in a non-component context.
293
+
294
+ | Group | Functions |
295
+ | --------------- | ------------------------------------------------------------------------------------------------------------------- |
296
+ | Events | `on`, `one`, `off`, `trigger` |
297
+ | State | `observe`, `formValues` |
298
+ | Assets & styles | `loadAssets`, `processAssets`, `injectCSS`, `htmlToElements` |
299
+ | Templates | `parse`, `bindTemplateEvents` |
300
+ | Registry | `defineCE`, `getCE`, `listCE` |
301
+ | Loading | `autoload` |
302
+ | Helpers | `debounce`, `deepEqual`, `flattenObject`, `sortObject`, `nameSpace`, `kebabToPascal`, `isNativeObject`, `isBrowser` |
303
+
304
+ Inside a `DC`, prefer the instance equivalents (`this.on()`, `this.trigger()`,
305
+ `this.subscribe()`) — see [implementation-priority](implementation-priority.md).
306
+
307
+ ---
308
+
309
+ ## Not an MC: Plain Custom Elements
310
+
311
+ `defineCE` also accepts a class that does **not** extend `McBase`. In that case the class
312
+ itself becomes the element and no `_mc` registry is attached. This is an escape hatch for
313
+ interop with existing custom elements — it is not a Micro Component and gains none of the
314
+ framework's conventions.
315
+
316
+ ---
317
+
318
+ ## Decision Flow
319
+
320
+ ```
321
+ Does it need to be discoverable via getCE() / attachable via DC.add()?
322
+ │
323
+ ├── No → Is it DOM-free logic?
324
+ │ ├── Yes → MC (pure logic)
325
+ │ └── No → Is it a cross-cutting capability for all components?
326
+ │ ├── Yes → Plugin
327
+ │ └── No → Utility function
328
+ │
329
+ └── Yes → Does it render visible DOM?
330
+ ├── Yes → Visual DC
331
+ │ ├── Flat content → target-based
332
+ │ ├── Regions / isolation → shadow-based
333
+ │ ├── Projects children → composed (named slots)
334
+ │ └── No children → leaf
335
+ │
336
+ └── No → static tag = ''
337
+ ├── Injects styles → style-only DC
338
+ └── Runs logic → logic-only DC
339
+ ```
340
+
341
+ ---
342
+
343
+ ## Related
344
+
345
+ - [microcomponent-architecture](microcomponent-architecture.md) — composition model, `DC.add`
346
+ - [custom-element-registry](custom-element-registry.md) — unified registration
347
+ - [event-system](event-system.md) — handler conventions and lifecycle events
348
+ - [state-management](state-management.md) — observables and form binding
349
+ - [implementation-priority](implementation-priority.md) — which API tier to use
@@ -0,0 +1,31 @@
1
+ Micro Components are function blocks attached to DOM nodes. They use composition over inheritance and work like building blocks:
2
+
3
+ Key principles:
4
+
5
+ - Every visible element is a micro component extending DC
6
+ - Pure logic (not DOM) extends MC
7
+ - Register custom elements with defineCE()
8
+ - Attach additional DOM logic with DC.add()
9
+ - Minimal DOM interference: only _mc property on host elements
10
+ - Works alongside any frontend framework
11
+ - One class per file — see below
12
+
13
+ ## One Class Per File
14
+
15
+ Every `.ts` or `.js` file contains exactly **one class**, regardless of which type it is:
16
+ visual, logic-only, style-only, or pure logic. This applies to `DC` subclasses, `MC`
17
+ subclasses, and any other class defined in `src/`.
18
+
19
+ Why:
20
+
21
+ - **Autoload resolution** — `autoload()` maps a tag name to a single script path
22
+ (`<shop-header>` → `./shop/header.js`). One class per file keeps that mapping
23
+ unambiguous.
24
+ - **Companion files** — templates, styles, and data share the class's basename
25
+ (`header.target.html`, `header.css`). A second class in the same file would have no
26
+ place to put its own assets.
27
+ - **Registry clarity** — `defineCE()` is called once per file, so file, class, and
28
+ registered tag stay in a 1:1 relationship.
29
+
30
+ Non-class entities that belong to the class — interfaces, types, constants, plain
31
+ functions — may live alongside it. A class that is reusable on its own gets its own file.
@@ -0,0 +1,18 @@
1
+ State management uses observable objects with reactive binding. Two usage patterns exist:
2
+
3
+ **Standalone functions** (outside MC ecosystem):
4
+
5
+ - observe(obj) — wraps properties in Observable wrappers; you manage subscribe/bind/notify manually
6
+ - formValues(obj, prop, dom) — binds form controls to an observable property on the object
7
+
8
+ **Plugins** (inside DC custom elements):
9
+
10
+ - observePlugin(DC) — adds .notify(), .subscribe(), .bind() instance methods that implicitly create observables on demand
11
+ - formValuePlugin(DC) — adds .formValues() instance method; also pulls in all observePlugin methods
12
+
13
+ Key behaviors:
14
+
15
+ - subscribe(callback) registers change listeners
16
+ - bind(target) connects objects to DOM placeholders {propertyName} — only objects can be bound (primitives are ignored)
17
+ - Deep change detection on nested objects with debounced notifications via microtask queue
18
+ - The plugin methods take the property _name_ rather than the value, and are typed as `keyof this & string`: an undeclared or typo'd property is a compile error, and the `subscribe` callback parameter is inferred from the declared class field (`this.subscribe('count', (v) => v.toFixed(1))` — `v` is `number` without an annotation)
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":""}