kopular 0.17.1 → 0.18.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/LLM.md +26 -0
- package/README.md +59 -0
- package/package.json +1 -1
package/LLM.md
CHANGED
|
@@ -98,6 +98,12 @@ extern class VElement {
|
|
|
98
98
|
(Event) => void OnChange { get; set; }
|
|
99
99
|
void AppendChild(VElement child);
|
|
100
100
|
void SetAttr(string name, string value);
|
|
101
|
+
// Embeds a live child Component as this slot's content — see "Nested
|
|
102
|
+
// component composition" below. Declared taking a concrete Component
|
|
103
|
+
// rather than Kopular's own Mountable interface: extern declarations
|
|
104
|
+
// don't model inheritance/interface conformance between separate extern
|
|
105
|
+
// classes, and every real caller passes a Component subclass anyway.
|
|
106
|
+
static VElement Mount(Component component);
|
|
101
107
|
} from "kopular/velement";
|
|
102
108
|
|
|
103
109
|
extern class Component {
|
|
@@ -106,6 +112,9 @@ extern class Component {
|
|
|
106
112
|
virtual void AfterRender(Element root); // see "Component" below
|
|
107
113
|
void Mount(Element parent);
|
|
108
114
|
void Update();
|
|
115
|
+
// Called once when this component is removed from its parent's tree via
|
|
116
|
+
// VElement.Mount above — see "Nested component composition" below.
|
|
117
|
+
virtual void OnUnmount();
|
|
109
118
|
} from "kopular/component";
|
|
110
119
|
|
|
111
120
|
extern class Router {
|
|
@@ -275,6 +284,23 @@ w.Bump(); // re-render + diff + patch
|
|
|
275
284
|
components, a modal that comes and goes.
|
|
276
285
|
- Hand-written `Render()` only — there's no template (`.html`) syntax yet for embedding a
|
|
277
286
|
child component the way `*if`/`*for` embed structural logic.
|
|
287
|
+
- **Content projection (React's `children`/Angular's `<ng-content>`) needs no separate
|
|
288
|
+
mechanism** — pass a `() => VElement` into a child's constructor and call it from inside
|
|
289
|
+
its own `Render()`; it's invoked fresh every render and closes over the parent's `this`,
|
|
290
|
+
so projected content reflects the parent's current state on every patch:
|
|
291
|
+
```ks
|
|
292
|
+
class Card : Component {
|
|
293
|
+
private () => VElement ContentBuilder;
|
|
294
|
+
constructor(() => VElement contentBuilder) : base() { this.ContentBuilder = contentBuilder; }
|
|
295
|
+
public override VElement Render() {
|
|
296
|
+
VElement div = VElement.Create("div");
|
|
297
|
+
div.AppendChild(this.ContentBuilder());
|
|
298
|
+
return div;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
More than one slot: one named `() => VElement` callback per slot (same fixed-named-slot
|
|
303
|
+
convention as `VElement`'s own `OnClick`/etc.), not a generic bag.
|
|
278
304
|
- **`Update()` before `Mount()` is a safe no-op**, not an error. This matters for two
|
|
279
305
|
sibling `Component`s (e.g. two `Router` pages) that share one injected service's
|
|
280
306
|
`state<T>` and both `Subscribe()` it — every route's page is constructed eagerly (see
|
package/README.md
CHANGED
|
@@ -468,6 +468,65 @@ removal to get right, so there's nothing this mechanism would add there. Reach f
|
|
|
468
468
|
`VElement.Mount` for anything with real add/remove/reorder: a list of components, a modal
|
|
469
469
|
that comes and goes, anything genuinely dynamic.
|
|
470
470
|
|
|
471
|
+
## Content projection — a parent giving a child what to render
|
|
472
|
+
|
|
473
|
+
React's `children` prop and Angular's `<ng-content>` both let a parent hand a child
|
|
474
|
+
arbitrary markup to render at a spot the child itself decides. Kopular needs no separate
|
|
475
|
+
mechanism for this at all: pass a `() => VElement` into the child's constructor, the same
|
|
476
|
+
as any other constructor argument, and have the child call it from inside its own
|
|
477
|
+
`Render()`:
|
|
478
|
+
|
|
479
|
+
```ks
|
|
480
|
+
class Card : Component {
|
|
481
|
+
private () => VElement ContentBuilder;
|
|
482
|
+
constructor(() => VElement contentBuilder) : base() {
|
|
483
|
+
this.ContentBuilder = contentBuilder;
|
|
484
|
+
}
|
|
485
|
+
public override VElement Render() {
|
|
486
|
+
VElement div = VElement.Create("div");
|
|
487
|
+
div.ClassName = "card";
|
|
488
|
+
div.AppendChild(this.ContentBuilder());
|
|
489
|
+
return div;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Usage — the parent decides what's inside the card, Card decides the chrome around it:
|
|
494
|
+
Card card = new Card(() => this.BuildCardBody());
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
Because the callback is invoked fresh on every one of `Card`'s own renders (not a value
|
|
498
|
+
captured once and frozen), and it's a closure over the *parent's* own `this`, the projected
|
|
499
|
+
content reflects the parent's current state every time — bump a field the parent's own
|
|
500
|
+
`BuildCardBody()` reads, call the parent's `Update()`, and `Card`'s own next patch shows the
|
|
501
|
+
new value, patched in place like any other content. No new API: this is just
|
|
502
|
+
`VElement.Mount` plus an ordinary function-typed constructor argument, the same "no hidden
|
|
503
|
+
magic, explicit constructor args" pattern dependency injection above already uses.
|
|
504
|
+
|
|
505
|
+
More than one projection point works the same way — one named callback per slot, the same
|
|
506
|
+
fixed-named-slot convention `VElement`'s own `OnClick`/`OnInput`/`OnBlur`/`OnChange` already
|
|
507
|
+
use rather than a generic, unordered bag:
|
|
508
|
+
|
|
509
|
+
```ks
|
|
510
|
+
class Panel : Component {
|
|
511
|
+
private () => VElement HeaderBuilder;
|
|
512
|
+
private () => VElement BodyBuilder;
|
|
513
|
+
constructor(() => VElement headerBuilder, () => VElement bodyBuilder) : base() {
|
|
514
|
+
this.HeaderBuilder = headerBuilder;
|
|
515
|
+
this.BodyBuilder = bodyBuilder;
|
|
516
|
+
}
|
|
517
|
+
public override VElement Render() {
|
|
518
|
+
VElement div = VElement.Create("div");
|
|
519
|
+
VElement header = VElement.Create("header");
|
|
520
|
+
header.AppendChild(this.HeaderBuilder());
|
|
521
|
+
div.AppendChild(header);
|
|
522
|
+
VElement body = VElement.Create("section");
|
|
523
|
+
body.AppendChild(this.BodyBuilder());
|
|
524
|
+
div.AppendChild(body);
|
|
525
|
+
return div;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
```
|
|
529
|
+
|
|
471
530
|
## HTTP
|
|
472
531
|
|
|
473
532
|
```ks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kopular",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Kopular: a small component framework for KopScript — components, reactive state, constructor-injected services, routing, real compiled templates, and HTTP, with no DI container",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|