Skip to content

Commit b97fdda

Browse files
committed
docs(37-04): retire MapLibre/Rete declarative-children defer notes (D37-07)
- both comparison pages: defer-note retired + comparison-table cells flipped to now-supported (alongside config arrays) - MapLibre <Source>/<Layer>; Rete <FlowNode>/<Handle>/<Connection> with the #body body-slot shape + the portal-teleport-vs-tree-scoped-$inject why (Rete vocabulary, not React Flow's) - still-deferred items (MiniMap, big-framework depth, 0.1.0) preserved
1 parent e889365 commit b97fdda

2 files changed

Lines changed: 48 additions & 5 deletions

File tree

docs/guide/maplibre-comparison.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Cell legend: **✅** = documented out-of-the-box · **❌** = not supported / no
3131
| **Popups** (framework component) |`<Popup>` |`MglPopup` |`mgl-popup` |`<Popup>` | ⚠️ ||`popup` reactive portal slot (all 6) |
3232
| Custom control (framework component) |`useControl` |`MglCustomControl` | ✅ control directives || ⚠️ ||`control` mount-once portal slot (all 6) |
3333
| Standard controls ||||| ⚠️ ||`:controls` prop (all 6) |
34-
| Sources / layers |`<Source>` / `<Layer>` |`MglGeoJSONSource` / `Mgl…Layer` | ✅ source / layer directives |`<Source>` / `<Layer>` | ⚠️ || ⚠️ **`:sources` / `:layers` config props** (see below) |
34+
| Sources / layers |`<Source>` / `<Layer>` |`MglGeoJSONSource` / `Mgl…Layer` | ✅ source / layer directives |`<Source>` / `<Layer>` | ⚠️ || `<Source>` / `<Layer>` children **and** `:sources` / `:layers` config props (see below) |
3535
| Interactive-layer hover (`features`) |`interactiveLayerIds` |||| ⚠️ ||`:interactiveLayerIds` + `@mouseenter`/`@mouseleave` |
3636
| Imperative handle (`getMap` etc.) |`useMap` / ref |`useMap` |`MapService` / ref || ⚠️ | hand-roll | ✅ uniform 8-verb `$expose` |
3737
| TypeScript ||||| ⚠️ |||
@@ -46,19 +46,36 @@ Cell legend: **✅** = documented out-of-the-box · **❌** = not supported / no
4646
- **A uniform 8-verb imperative handle** (`getMap` / `flyTo` / `easeTo` / `jumpTo` / `fitBounds` / `getCenter` / `getZoom` / `resize`) grabbed with each framework's native ref — versus "however this wrapper happens to expose the `Map`" (a hook, a service, a ref, a directive input).
4747
- **`getMap()` is always one hop from the raw engine**, so the full MapLibre API is reachable on any target when the curated surface doesn't cover something.
4848

49+
## Declarative `<Source>` / `<Layer>` children — now supported {#declarative-children}
50+
51+
Declarative `<Source>` / `<Layer>` children are **now supported on all six targets, alongside the `:sources` / `:layers` config-array props** — the authoring shape the big-framework wrappers (`react-map-gl`, `vue-maplibre-gl`, `svelte-maplibre-gl`, `ngx-maplibre-gl`) are known for.
52+
53+
```html
54+
<MapLibre :center="[0, 0]" :zoom="2">
55+
<Source id="pts" :spec="geojson">
56+
<Layer id="circles" type="circle" :paint="paint" />
57+
</Source>
58+
<Layer id="bg" type="background" :paint="bgPaint" />
59+
</MapLibre>
60+
```
61+
62+
- **Nested `<Source><Layer/></Source>` auto-binds** the layer to its parent source via injected context — no `source` attr needed.
63+
- **Flat `<Layer source="id" />`** directly under `<MapLibre>` also works, for background layers (no source) and cross-source references.
64+
- **Both shapes coexist with `:sources` / `:layers`.** When a consumer supplies both a config array and declarative children, both feed the **same id-keyed registry**; on an id collision the declarative child wins (last-writer-wins, matching the engine's own reconcile). It is the **same `addSource` / `addLayer` runtime** and the same style-load-gated reconcile — the children just register into it.
65+
66+
This was built by dogfooding Rozie's own cross-component context primitive (`$provide` / `$inject`): the map provides an id-keyed registry, and each `<Source>` / `<Layer>` child injects it, registers on mount, updates on prop change, and unregisters on unmount. Verified behaviorally across all six targets (including the Angular real-build).
67+
4968
## What Rozie defers {#what-rozie-defers}
5069

5170
This page concedes where the standalone wrappers are genuinely ahead — that's what keeps the comparison credible, and it doubles as Rozie's own roadmap.
5271

53-
- **Declarative `<Source>` / `<Layer>` *children*.** `react-map-gl`, `vue-maplibre-gl`, `svelte-maplibre-gl`, and `ngx-maplibre-gl` all let you compose data sources and styled layers as **child components / directives** inside the map — `<Source><Layer /></Source>` and friends. Rozie v1 takes a different authoring shape: the **`:sources` / `:layers` config props** (MapLibre's own source / layer specs, reconciled into the live style after load). It is the **same `addSource` / `addLayer` runtime** and reaches the same result, but the authoring model is a config array, not nested children. True declarative source / layer children need a **cross-component context primitive** (a parent map providing context to descendant `<Source>` / `<Layer>` elements) that Rozie deliberately defers — it's a meaningful compiler primitive, not a quick wrapper feature. Until then, `:sources` / `:layers` cover the same ground in config form.
54-
5572
- **Big-framework depth on the home framework.** `react-map-gl` (vis.gl, OpenJS-foundation-backed), `vue-maplibre-gl`, `svelte-maplibre-gl` (MIERUNE), and the official `ngx-maplibre-gl` are mature, multi-year libraries with deep component catalogs (terrain, globe / projection, geocoding integrations, draw plugins, and the full declarative children model). On their own framework, they expose more surface than Rozie's curated prop set. Rozie's value is **not** "more than react-map-gl on React" — it's the **same idiomatic component on all six frameworks from one source**, with the underserved **Solid and Lit** getting a first-class wrapper they otherwise lack. For anything outside the curated surface, `getMap()` hands you the raw engine on every target.
5673

5774
- **`@rozie-ui/maplibre` is `0.1.0`.** The surface (25 props / 20 events / 8-verb handle / `marker` + `popup` reactive slots + `control` mount-once slot) is stable and gate-verified, but it is younger than the multi-year incumbents.
5875

5976
## Try it
6077

61-
The [`@rozie-ui/maplibre` showcase + API reference](/guide/maplibre) documents the `@rozie-ui/maplibre-*` packages — one pre-compiled, per-framework install (`npm i @rozie-ui/maplibre-react maplibre-gl`, etc.), plus the `import 'maplibre-gl/dist/maplibre-gl.css'` the engine DOM needs. The showcase walks the four two-way camera bindings, the 20-event surface, the imperative handle, the `:sources` / `:layers` passthroughs, and the per-target recipe for the `marker` / `popup` / `control` portal slots.
78+
The [`@rozie-ui/maplibre` showcase + API reference](/guide/maplibre) documents the `@rozie-ui/maplibre-*` packages — one pre-compiled, per-framework install (`npm i @rozie-ui/maplibre-react maplibre-gl`, etc.), plus the `import 'maplibre-gl/dist/maplibre-gl.css'` the engine DOM needs. The showcase walks the four two-way camera bindings, the 20-event surface, the imperative handle, both the `<Source>` / `<Layer>` declarative children and the `:sources` / `:layers` config-array passthroughs, and the per-target recipe for the `marker` / `popup` / `control` portal slots.
6279

6380
## Cross-references
6481

docs/guide/rete-comparison.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Cell legend: **✅** = documented out-of-the-box · **❌** = not supported / no
3737
| Two-way zoom binding | ⚠️ controlled | ⚠️ | ⚠️ | ⚠️ | ⚠️ | hand-roll |`r-model:zoom` (echo-guarded) |
3838
| Graph events (moved / connected / picked) ||||| ⚠️ | hand-roll | ✅ 7 structured events |
3939
| Imperative handle |`useReactFlow` |`useVueFlow` || ✅ service | ⚠️ | hand-roll | ✅ uniform 12-verb `$expose` |
40+
| Declarative graph children (`<FlowNode>` / `<Handle>` / `<Connection>`) | ✅ JSX children |||| ⚠️ || ✅ children **and** `:nodes` / `:connections` config props |
4041
| Config-array graph (`:nodes` / `:connections`) ||||| ⚠️ || ✅ reconciled live, no remount |
4142
| MiniMap / Background / Controls |||| ⚠️ | ⚠️ || ⚠️ deferred (see below) |
4243
| TypeScript ||||| ⚠️ |||
@@ -50,11 +51,36 @@ Cell legend: **✅** = documented out-of-the-box · **❌** = not supported / no
5051
- **A uniform 12-verb imperative handle** (`getEditor` / `getArea` / `addNode` / `removeNode` / `addConnection` / `removeConnection` / `clear` / `zoomToFit` / `zoomTo` / `getNodes` / `getConnections` / `getTransform`) grabbed with each framework's native ref — versus "however this library happens to expose its instance" (a hook, a service, a ref).
5152
- **`getEditor()` / `getArea()` are always one hop from the raw engine**, so the full Rete API (custom plugins, `rete-engine` dataflow, `rete-auto-arrange-plugin`, …) is reachable on any target when the curated surface doesn't cover something.
5253

54+
## Declarative `<FlowNode>` / `<Handle>` / `<Connection>` children — now supported {#declarative-children}
55+
56+
Declarative graph children are **now supported on all six targets, alongside the `:nodes` / `:connections` config-array props**. The vocabulary follows Rete's own model, not React Flow's: `<FlowNode>` is a node, `<Handle>` is a **port / socket** (nested inside its node), and `<Connection>` is an **edge** (a flat child of the canvas, since an edge spans two nodes and can't nest inside one).
57+
58+
```html
59+
<FlowCanvas>
60+
<FlowNode id="a" :x="40" :y="60">
61+
<template #body><MyCard /></template>
62+
<Handle side="output" port="out" />
63+
</FlowNode>
64+
<FlowNode id="b" :x="320" :y="60">
65+
<template #body><MyCard /></template>
66+
<Handle side="input" port="in" />
67+
</FlowNode>
68+
<Connection source="a" target="b" sourceOutput="out" targetInput="in" />
69+
</FlowCanvas>
70+
```
71+
72+
- **`<Handle>` nests inside `<FlowNode>`** and auto-binds to its node via injected context (no `nodeId` to wire by hand).
73+
- **`<Connection>` is a flat child** of `<FlowCanvas>` referencing source / target node ids + handle keys.
74+
- **Both shapes coexist with `:nodes` / `:connections`.** A config array and declarative children feed the **same id-keyed registry**; on an id collision the declarative child wins (last-writer-wins). It is the **same `addNode` / `addConnection` runtime** — children just register into the engine's existing reconcile.
75+
76+
**Why the node body is a named `#body` slot, not bare children.** A FlowNode's visual body has to *teleport* into the node element the Rete engine creates — it doesn't render in the normal component tree. Rozie mounts that body through a portal (`$portals.body`), which gives it a fresh framework render-root inside the engine-owned host. But a portal render-root has no tree ancestor, so context-consuming children placed inside it would not resolve their `$inject` on five of six targets (context is tree-scoped on React/Vue/Svelte/Solid/Lit). Separating the teleported body (`<template #body>`) from the context-consuming config children (`<Handle>` / `<Connection>`, which stay in the normal child position) is therefore the robust cross-framework shape: the body teleports, the config children keep their tree scope and inject correctly. Verified behaviorally across all six targets (including the Angular real-build).
77+
78+
This was built by dogfooding Rozie's own cross-component context primitive (`$provide` / `$inject`): the canvas provides an id-keyed registry, and each child injects it. (A general `$portals.default` capability also exists for default-slot portals — but `<FlowNode>` deliberately uses the named `#body` slot for the tree-scope reason above; it does **not** rely on the bare default slot.)
79+
5380
## What Rozie defers {#what-rozie-defers}
5481

5582
This page concedes where the standalone libraries are genuinely ahead — that's what keeps the comparison credible, and it doubles as Rozie's roadmap.
5683

57-
- **Declarative `<Node>` / `<Edge>` *children*.** React Flow et al. let you compose a graph as JSX/children with per-type node components registered up front. Rozie v1 takes a different authoring shape: the **`:nodes` / `:connections` config-array props** (reconciled into the live editor), with per-node bodies supplied through the `node` slot. It is the **same `addNode` / `addConnection` runtime** and reaches the same result, but the authoring model is a config array, not nested children. True declarative graph children — deeply-nested `<Node>`/`<Handle>` elements reading shared graph state (selection, viewport, neighbors) without prop-drilling — need a **cross-component context primitive** that Rozie deliberately defers (the same primitive MapLibre's `:sources` / `:layers` deferral is waiting on). The wrap-a-vanilla-engine strategy sidesteps it entirely: the **engine** owns the store, and node bodies reach it through portal scope.
5884
- **MiniMap / Background variants / NodeToolbar / NodeResizer.** React Flow ships these as first-class components. `FlowCanvas` v1 covers the canvas + nodes + sockets + connections + a dotted background; the second-tier chrome is on the roadmap (config-prop first, the MapLibre stance).
5985
- **Big-framework depth on the home framework.** React Flow (Zustand store, deep node/edge-type catalogs, helper hooks, layouting integrations) is a mature, multi-year library; on React it exposes more surface than Rozie's curated set. Rozie's value is **not** "more than React Flow on React" — it's the **same idiomatic editor on all six frameworks from one source**, with the unserved **Solid and Lit** finally covered.
6086
- **`@rozie-ui/rete` is `0.1.0`.** The surface (13 props / 7 events / 12-verb handle / `node` reactive slot) is stable and gate-verified (behavioral parity across all six targets), but it is younger than the incumbents.

0 commit comments

Comments
 (0)