@elaraai/east-ui 1.0.35 → 1.0.37
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/dist/src/component.d.ts +22 -0
- package/dist/src/component.d.ts.map +1 -1
- package/dist/src/component.js +22 -0
- package/dist/src/component.js.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/internal.d.ts +3 -2
- package/dist/src/internal.d.ts.map +1 -1
- package/dist/src/internal.js +2 -1
- package/dist/src/internal.js.map +1 -1
- package/dist/src/navigation/index.d.ts +1 -1
- package/dist/src/navigation/index.d.ts.map +1 -1
- package/dist/src/navigation/index.js +1 -1
- package/dist/src/navigation/index.js.map +1 -1
- package/dist/src/navigation/pages/index.d.ts +44 -8
- package/dist/src/navigation/pages/index.d.ts.map +1 -1
- package/dist/src/navigation/pages/index.js +44 -7
- package/dist/src/navigation/pages/index.js.map +1 -1
- package/dist/src/reactive/match.d.ts +85 -0
- package/dist/src/reactive/match.d.ts.map +1 -0
- package/dist/src/reactive/match.js +65 -0
- package/dist/src/reactive/match.js.map +1 -0
- package/dist/src/runtime/navigation/index.d.ts +1 -0
- package/dist/src/runtime/navigation/index.d.ts.map +1 -1
- package/dist/src/runtime/navigation/index.js +1 -0
- package/dist/src/runtime/navigation/index.js.map +1 -1
- package/dist/src/runtime/navigation/route.d.ts +46 -0
- package/dist/src/runtime/navigation/route.d.ts.map +1 -0
- package/dist/src/runtime/navigation/route.js +47 -0
- package/dist/src/runtime/navigation/route.js.map +1 -0
- package/dist/src/runtime/reactive/index.d.ts +1 -0
- package/dist/src/runtime/reactive/index.d.ts.map +1 -1
- package/dist/src/runtime/reactive/index.js +1 -0
- package/dist/src/runtime/reactive/index.js.map +1 -1
- package/dist/src/runtime/reactive/match.d.ts +51 -0
- package/dist/src/runtime/reactive/match.d.ts.map +1 -0
- package/dist/src/runtime/reactive/match.js +49 -0
- package/dist/src/runtime/reactive/match.js.map +1 -0
- package/dist/test/navigation/index.d.ts +1 -0
- package/dist/test/navigation/index.d.ts.map +1 -1
- package/dist/test/navigation/index.js +1 -0
- package/dist/test/navigation/index.js.map +1 -1
- package/dist/test/navigation/route.examples.d.ts +13 -0
- package/dist/test/navigation/route.examples.d.ts.map +1 -0
- package/dist/test/navigation/route.examples.js +69 -0
- package/dist/test/navigation/route.examples.js.map +1 -0
- package/dist/test/reactive/index.d.ts +7 -0
- package/dist/test/reactive/index.d.ts.map +1 -0
- package/dist/test/reactive/index.js +7 -0
- package/dist/test/reactive/index.js.map +1 -0
- package/dist/test/reactive/match.examples.d.ts +16 -0
- package/dist/test/reactive/match.examples.d.ts.map +1 -0
- package/dist/test/reactive/match.examples.js +77 -0
- package/dist/test/reactive/match.examples.js.map +1 -0
- package/package.json +3 -3
|
@@ -67,8 +67,8 @@ export const Navigation = {
|
|
|
67
67
|
bind: bindNavigation,
|
|
68
68
|
};
|
|
69
69
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
70
|
+
* Shared switcher builder for `Pages` / `Route` — the two emit the same
|
|
71
|
+
* payload (`render` + `navKey`) under their own variant case.
|
|
72
72
|
*
|
|
73
73
|
* The nullary `render` reads `current()` on the **passed** handle (no internal
|
|
74
74
|
* re-bind) and `match`es the active route to its page body. The bodies live in
|
|
@@ -77,11 +77,8 @@ export const Navigation = {
|
|
|
77
77
|
* the active page (leaf-only, visible-only). The render closes over `nav`, so
|
|
78
78
|
* its `current()` read subscribes to the same store the app's handle is bound
|
|
79
79
|
* to (#114).
|
|
80
|
-
*
|
|
81
|
-
* @param input - The {@link PagesInput} (`nav` handle + `pages` registry).
|
|
82
|
-
* @returns A `Pages` `UIComponentType` value.
|
|
83
80
|
*/
|
|
84
|
-
function
|
|
81
|
+
function createNavSwitcher(caseName, nav, pages) {
|
|
85
82
|
const handlers = pages;
|
|
86
83
|
// Build the match arms in host TS (outside the East block): one arm per
|
|
87
84
|
// route, forwarding the typed payload + the shared handle to the page body.
|
|
@@ -97,7 +94,17 @@ function createPages({ nav, pages }) {
|
|
|
97
94
|
const cur = $.const(nav.current());
|
|
98
95
|
return cur.match(armsFor(nav));
|
|
99
96
|
});
|
|
100
|
-
return East.value(variant(
|
|
97
|
+
return East.value(variant(caseName, { render, navKey: nav.key }), UIComponentType);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Build a `<Pages>` content-switcher from a bound nav handle + a typed page
|
|
101
|
+
* registry.
|
|
102
|
+
*
|
|
103
|
+
* @param input - The {@link PagesInput} (`nav` handle + `pages` registry).
|
|
104
|
+
* @returns A `Pages` `UIComponentType` value.
|
|
105
|
+
*/
|
|
106
|
+
function createPages({ nav, pages }) {
|
|
107
|
+
return createNavSwitcher("Pages", nav, pages);
|
|
101
108
|
}
|
|
102
109
|
/**
|
|
103
110
|
* The `Pages` namespace — the first-class navigation content-switcher.
|
|
@@ -112,4 +119,34 @@ export const Pages = {
|
|
|
112
119
|
/** Build a `<Pages>` content-switcher from `{ nav, pages }`. See {@link createPages}. */
|
|
113
120
|
Root: createPages,
|
|
114
121
|
};
|
|
122
|
+
/**
|
|
123
|
+
* Build a `<Route>` hosting slot from a bound nav handle + a typed route
|
|
124
|
+
* registry — `<Pages>` generalized to any slot.
|
|
125
|
+
*
|
|
126
|
+
* Renders only the active route's body and remounts it on navigation, but is
|
|
127
|
+
* placeable anywhere (a header widget, a sidebar, a drawer body). The body
|
|
128
|
+
* `<Pages>` and any number of `<Route>` slots bind the **same** `nav` handle,
|
|
129
|
+
* so they stay in lockstep and each remounts *its own* slot on navigation.
|
|
130
|
+
*
|
|
131
|
+
* @param input - The {@link RouteInput} (`nav` handle + `routes` registry).
|
|
132
|
+
* @returns A `Route` `UIComponentType` value.
|
|
133
|
+
*/
|
|
134
|
+
function createRoute({ nav, routes }) {
|
|
135
|
+
return createNavSwitcher("Route", nav, routes);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The `Route` namespace — the nav-typed hosting slot.
|
|
139
|
+
*
|
|
140
|
+
* @remarks
|
|
141
|
+
* Authored as the `<Route nav={…} routes={{…}} />` tag; `Route.Root({ nav,
|
|
142
|
+
* routes })` is the underlying factory. Use it to swap a **stateful**
|
|
143
|
+
* component (its own `<Reactive>` / binds) in any slot by route: each case is
|
|
144
|
+
* a self-contained component built with the existing binds, torn down and
|
|
145
|
+
* rebuilt fresh when the route changes. For a non-route key (a `State`-bound
|
|
146
|
+
* mode, a tagged selection), use `<Match on cases>` instead.
|
|
147
|
+
*/
|
|
148
|
+
export const Route = {
|
|
149
|
+
/** Build a `<Route>` hosting slot from `{ nav, routes }`. See {@link createRoute}. */
|
|
150
|
+
Root: createRoute,
|
|
151
|
+
};
|
|
115
152
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/navigation/pages/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;GAUG;AAEH,OAAO,EACH,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,UAAU,EACV,OAAO,GAIV,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EACH,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,GAInB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACH,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,GAMnB,MAAM,YAAY,CAAC;AAkBpB,SAAS,oBAAoB,CAAsB,MAAS;IACxD,MAAM,IAAI,GAAmD,EAAE,CAAC;IAChE,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAiB,EAAE,EAAE,CAC/B,OAAO,CAAC,IAAI,EAAE,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAA2C,CAAC;IACxG,CAAC;IACD,OAAO,IAA2B,CAAC;AACvC,CAAC;AAsBD,SAAS,eAAe,CAAsB,MAAS;IACnD,OAAO;QACH,MAAM;QACN,KAAK,EAAE,gBAAgB,CAAC,MAAM,CAAC;QAC/B,IAAI,EAAE,oBAAoB,CAAC,MAAM,CAAC;KACrC,CAAC;AACN,CAAC;AAED,+EAA+E;AAC/E,kFAAkF;AAClF,+EAA+E;AAE/E;;;;;;;;;;;;GAYG;AACH,SAAS,cAAc,CACnB,MAAoB,EACpB,GAAW,EACX,OAAyD;IAEzD,MAAM,UAAU,GAAG,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAC7C,OAAO,iBAAiB,CACpB,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,EAC1B,QAAQ,EACR,OAAgB,CACJ,CAAC;AACrB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACtB,oEAAoE;IACpE,MAAM,EAAE,eAAe;IACvB,qDAAqD;IACrD,IAAI,EAAE,cAAc;CACd,CAAC;AAuCX
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/navigation/pages/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* `Match` — the hosting slot over a variant (#333): the component-level twin
|
|
7
|
+
* of `variant.match`.
|
|
8
|
+
*
|
|
9
|
+
* A plain `variant.match(…)` selecting between mounted components reconciles
|
|
10
|
+
* same-shape nodes, so swapping component A → B at one slot keeps A mounted —
|
|
11
|
+
* its nested `Reactive` / bind / scroll / open state persists and the content
|
|
12
|
+
* goes stale. `Match` host-mounts only the active case and **remounts on tag
|
|
13
|
+
* change**: each case is a normal, self-contained component built with the
|
|
14
|
+
* existing binds, torn down and rebuilt fresh per key. Same-tag payload/data
|
|
15
|
+
* churn re-renders without losing mounted state.
|
|
16
|
+
*
|
|
17
|
+
* @packageDocumentation
|
|
18
|
+
*/
|
|
19
|
+
import { type BlockBuilder, type EastType, type ExprType, type SubtypeExprOrValue, type VariantType } from "@elaraai/east";
|
|
20
|
+
import { UIComponentType } from "../component.js";
|
|
21
|
+
/**
|
|
22
|
+
* The case registry: one body per variant case, receiving `($, payload)` —
|
|
23
|
+
* the inner block builder and that case's typed payload. Typed from the `on`
|
|
24
|
+
* variant so a wrong payload arg is a compile error; every case must be
|
|
25
|
+
* handled (exhaustive), exactly like `variant.match`.
|
|
26
|
+
*/
|
|
27
|
+
export type MatchCases<C extends {
|
|
28
|
+
[K in string]: EastType;
|
|
29
|
+
}> = {
|
|
30
|
+
[K in keyof C]: ($: BlockBuilder<typeof UIComponentType>, payload: ExprType<C[K]>) => SubtypeExprOrValue<typeof UIComponentType>;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The input to the `<Match>` hosting slot: the variant expression selecting
|
|
34
|
+
* the active case, plus the typed case registry.
|
|
35
|
+
*
|
|
36
|
+
* @remarks
|
|
37
|
+
* Taking a **variant** (not a bare string/number key) is what lets the case
|
|
38
|
+
* map be typed + exhaustive — model the choice as a variant (a `State`-bound
|
|
39
|
+
* mode, a selection tagged as a variant) to get it. `C` is fixed by `on`, so
|
|
40
|
+
* every `cases` handler is contextually typed.
|
|
41
|
+
*/
|
|
42
|
+
export interface MatchInput<C extends {
|
|
43
|
+
[K in string]: EastType;
|
|
44
|
+
}> {
|
|
45
|
+
/** The variant expression selecting the active case — its reads are the reactive subscription. */
|
|
46
|
+
on: ExprType<VariantType<C>>;
|
|
47
|
+
/** One body per case — see {@link MatchCases}. `C` is fixed by `on`, so the handlers are contextually typed. */
|
|
48
|
+
cases: MatchCases<NoInfer<C>>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build a `<Match>` hosting slot from a variant expression + a typed case
|
|
52
|
+
* registry.
|
|
53
|
+
*
|
|
54
|
+
* The nullary `render` re-reads `on` and `match`es the active case to its
|
|
55
|
+
* body. The bodies live in the match arms (in the IR), so the manifest walk
|
|
56
|
+
* collects every case's binds (the union) while only the matched arm executes
|
|
57
|
+
* — the renderer mounts just the active case. The `tag` closure re-reads `on`
|
|
58
|
+
* and yields the active case name; the renderer keys the mounted subtree by
|
|
59
|
+
* it, so the active case remounts exactly on tag change.
|
|
60
|
+
*
|
|
61
|
+
* @param input - The {@link MatchInput} (`on` variant + `cases` registry).
|
|
62
|
+
* @returns A `Match` `UIComponentType` value.
|
|
63
|
+
*/
|
|
64
|
+
declare function createMatch<C extends {
|
|
65
|
+
[K in string]: EastType;
|
|
66
|
+
}>({ on, cases }: MatchInput<C>): ExprType<typeof UIComponentType>;
|
|
67
|
+
/**
|
|
68
|
+
* The `Match` namespace — the general hosting slot over a variant.
|
|
69
|
+
*
|
|
70
|
+
* @remarks
|
|
71
|
+
* Authored as the `<Match on={…} cases={{…}} />` tag; `Match.Root({ on,
|
|
72
|
+
* cases })` is the underlying factory. Use it to swap a **stateful**
|
|
73
|
+
* component (its own `<Reactive>` / binds) at one slot by a reactive key:
|
|
74
|
+
* the active case remounts on tag change. Where the key is a nav route, use
|
|
75
|
+
* `<Route nav routes>` instead — it pins the case map to the
|
|
76
|
+
* `Navigation.config` and hands each body the route payload + nav. For
|
|
77
|
+
* selecting a plain *value* by key (a label, a count), use `variant.match`
|
|
78
|
+
* directly — only mounted subtrees need the hosting slot.
|
|
79
|
+
*/
|
|
80
|
+
export declare const Match: {
|
|
81
|
+
/** Build a `<Match>` hosting slot from `{ on, cases }`. See {@link createMatch}. */
|
|
82
|
+
readonly Root: typeof createMatch;
|
|
83
|
+
};
|
|
84
|
+
export {};
|
|
85
|
+
//# sourceMappingURL=match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../../../src/reactive/match.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAIH,KAAK,YAAY,EACjB,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,WAAW,EACnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS;KAAG,CAAC,IAAI,MAAM,GAAG,QAAQ;CAAE,IAAI;KAC3D,CAAC,IAAI,MAAM,CAAC,GAAG,CACZ,CAAC,EAAE,YAAY,CAAC,OAAO,eAAe,CAAC,EACvC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KACtB,kBAAkB,CAAC,OAAO,eAAe,CAAC;CAClD,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC,SAAS;KAAG,CAAC,IAAI,MAAM,GAAG,QAAQ;CAAE;IAC7D,kGAAkG;IAClG,EAAE,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,gHAAgH;IAChH,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;CACjC;AAED;;;;;;;;;;;;;GAaG;AACH,iBAAS,WAAW,CAAC,CAAC,SAAS;KAAG,CAAC,IAAI,MAAM,GAAG,QAAQ;CAAE,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,OAAO,eAAe,CAAC,CAY1H;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,KAAK;IACd,oFAAoF;;CAE9E,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* `Match` — the hosting slot over a variant (#333): the component-level twin
|
|
7
|
+
* of `variant.match`.
|
|
8
|
+
*
|
|
9
|
+
* A plain `variant.match(…)` selecting between mounted components reconciles
|
|
10
|
+
* same-shape nodes, so swapping component A → B at one slot keeps A mounted —
|
|
11
|
+
* its nested `Reactive` / bind / scroll / open state persists and the content
|
|
12
|
+
* goes stale. `Match` host-mounts only the active case and **remounts on tag
|
|
13
|
+
* change**: each case is a normal, self-contained component built with the
|
|
14
|
+
* existing binds, torn down and rebuilt fresh per key. Same-tag payload/data
|
|
15
|
+
* churn re-renders without losing mounted state.
|
|
16
|
+
*
|
|
17
|
+
* @packageDocumentation
|
|
18
|
+
*/
|
|
19
|
+
import { East, StringType, variant, } from "@elaraai/east";
|
|
20
|
+
import { UIComponentType } from "../component.js";
|
|
21
|
+
/**
|
|
22
|
+
* Build a `<Match>` hosting slot from a variant expression + a typed case
|
|
23
|
+
* registry.
|
|
24
|
+
*
|
|
25
|
+
* The nullary `render` re-reads `on` and `match`es the active case to its
|
|
26
|
+
* body. The bodies live in the match arms (in the IR), so the manifest walk
|
|
27
|
+
* collects every case's binds (the union) while only the matched arm executes
|
|
28
|
+
* — the renderer mounts just the active case. The `tag` closure re-reads `on`
|
|
29
|
+
* and yields the active case name; the renderer keys the mounted subtree by
|
|
30
|
+
* it, so the active case remounts exactly on tag change.
|
|
31
|
+
*
|
|
32
|
+
* @param input - The {@link MatchInput} (`on` variant + `cases` registry).
|
|
33
|
+
* @returns A `Match` `UIComponentType` value.
|
|
34
|
+
*/
|
|
35
|
+
function createMatch({ on, cases }) {
|
|
36
|
+
// The handlers receive ($, payload) — the exact `variant.match` arm shape —
|
|
37
|
+
// so the registry passes straight through as the match arms.
|
|
38
|
+
const render = East.function([], UIComponentType, ($) => {
|
|
39
|
+
const cur = $.const(on);
|
|
40
|
+
return cur.match(cases);
|
|
41
|
+
});
|
|
42
|
+
const tag = East.function([], StringType, ($) => {
|
|
43
|
+
const cur = $.const(on);
|
|
44
|
+
return cur.getTag();
|
|
45
|
+
});
|
|
46
|
+
return East.value(variant("Match", { render, tag }), UIComponentType);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The `Match` namespace — the general hosting slot over a variant.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* Authored as the `<Match on={…} cases={{…}} />` tag; `Match.Root({ on,
|
|
53
|
+
* cases })` is the underlying factory. Use it to swap a **stateful**
|
|
54
|
+
* component (its own `<Reactive>` / binds) at one slot by a reactive key:
|
|
55
|
+
* the active case remounts on tag change. Where the key is a nav route, use
|
|
56
|
+
* `<Route nav routes>` instead — it pins the case map to the
|
|
57
|
+
* `Navigation.config` and hands each body the route payload + nav. For
|
|
58
|
+
* selecting a plain *value* by key (a label, a count), use `variant.match`
|
|
59
|
+
* directly — only mounted subtrees need the hosting slot.
|
|
60
|
+
*/
|
|
61
|
+
export const Match = {
|
|
62
|
+
/** Build a `<Match>` hosting slot from `{ on, cases }`. See {@link createMatch}. */
|
|
63
|
+
Root: createMatch,
|
|
64
|
+
};
|
|
65
|
+
//# sourceMappingURL=match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match.js","sourceRoot":"","sources":["../../../src/reactive/match.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;;;;;GAaG;AAEH,OAAO,EACH,IAAI,EACJ,UAAU,EACV,OAAO,GAMV,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAgClD;;;;;;;;;;;;;GAaG;AACH,SAAS,WAAW,CAAwC,EAAE,EAAE,EAAE,KAAK,EAAiB;IACpF,4EAA4E;IAC5E,6DAA6D;IAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE;QACpD,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxB,OAAQ,GAA0E,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACpG,CAAC,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE;QAC5C,MAAM,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxB,OAAQ,GAAqD,CAAC,MAAM,EAAE,CAAC;IAC3E,CAAC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,eAAe,CAAC,CAAC;AAC1E,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACjB,oFAAoF;IACpF,IAAI,EAAE,WAAW;CACX,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/runtime/navigation/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,sDAAsD;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/runtime/navigation/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,sDAAsD;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/runtime/navigation/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,sDAAsD;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/runtime/navigation/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,sDAAsD;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/** Navigation `<Route>` tag — the nav-typed hosting slot (#333). */
|
|
6
|
+
import { type RouteInput } from "../../navigation/pages/index.js";
|
|
7
|
+
import type { NavRoutes } from "../../navigation/pages/types.js";
|
|
8
|
+
import type { UIElement } from "../runtime.js";
|
|
9
|
+
/**
|
|
10
|
+
* Nav-typed hosting slot — `<Pages>` generalized to any slot: renders only the
|
|
11
|
+
* active route's body and **remounts it on navigation**, but is placeable
|
|
12
|
+
* anywhere (a header widget, a sidebar, a drawer body). Bind the nav handle
|
|
13
|
+
* once in the enclosing `<Reactive>` and share it between the body `<Pages>`,
|
|
14
|
+
* the chrome, and any number of `<Route>` slots — they stay in lockstep and
|
|
15
|
+
* each remounts *its own* slot on navigation. Route keys are typed +
|
|
16
|
+
* exhaustive against the `Navigation.config`; each body receives the route's
|
|
17
|
+
* payload + the nav handle. For a non-route key (a `State`-bound mode, a
|
|
18
|
+
* tagged selection), use `<Match on cases>` instead.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* // .tsx file with the `@jsxImportSource @elaraai/east-ui` pragma
|
|
23
|
+
* import { East, NullType, UIComponentType } from "@elaraai/east";
|
|
24
|
+
* import { Navigation, Route, Reactive, Text } from "@elaraai/east-ui";
|
|
25
|
+
*
|
|
26
|
+
* const widget = East.function([], UIComponentType, _$ => {
|
|
27
|
+
* const routes = Navigation.config({ overview: { value: NullType, label: "Overview" } });
|
|
28
|
+
* return (
|
|
29
|
+
* <Reactive>{$ => {
|
|
30
|
+
* const nav = $.let(Navigation.bind(routes, "app.nav", [routes.Page.overview()]));
|
|
31
|
+
* return <Route nav={nav} routes={{ overview: () => <Text>Overview widget</Text> }} />;
|
|
32
|
+
* }}</Reactive>
|
|
33
|
+
* );
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @remarks
|
|
38
|
+
* Desugars to `Route.Root({ nav, routes })`. A generic function declaration
|
|
39
|
+
* (merged with the `Route` namespace) so it is a valid generic JSX component.
|
|
40
|
+
*/
|
|
41
|
+
export declare function Route<R extends NavRoutes>(props: RouteInput<R>): UIElement;
|
|
42
|
+
export declare namespace Route {
|
|
43
|
+
/** The underlying factory: `Route.Root({ nav, routes })`. */
|
|
44
|
+
const Root: <R extends NavRoutes>({ nav, routes }: RouteInput<R>) => import("@elaraai/east").ExprType<typeof import("../../component.js").UIComponentType>;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../../../src/runtime/navigation/route.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,oEAAoE;AAEpE,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,iCAAiC,CAAC;AACzF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAE1E;AAGD,yBAAiB,KAAK,CAAC;IACnB,6DAA6D;IACtD,MAAM,IAAI,gJAAoB,CAAC;CACzC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/** Navigation `<Route>` tag — the nav-typed hosting slot (#333). */
|
|
6
|
+
import { Route as RouteFactory } from "../../navigation/pages/index.js";
|
|
7
|
+
/**
|
|
8
|
+
* Nav-typed hosting slot — `<Pages>` generalized to any slot: renders only the
|
|
9
|
+
* active route's body and **remounts it on navigation**, but is placeable
|
|
10
|
+
* anywhere (a header widget, a sidebar, a drawer body). Bind the nav handle
|
|
11
|
+
* once in the enclosing `<Reactive>` and share it between the body `<Pages>`,
|
|
12
|
+
* the chrome, and any number of `<Route>` slots — they stay in lockstep and
|
|
13
|
+
* each remounts *its own* slot on navigation. Route keys are typed +
|
|
14
|
+
* exhaustive against the `Navigation.config`; each body receives the route's
|
|
15
|
+
* payload + the nav handle. For a non-route key (a `State`-bound mode, a
|
|
16
|
+
* tagged selection), use `<Match on cases>` instead.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* // .tsx file with the `@jsxImportSource @elaraai/east-ui` pragma
|
|
21
|
+
* import { East, NullType, UIComponentType } from "@elaraai/east";
|
|
22
|
+
* import { Navigation, Route, Reactive, Text } from "@elaraai/east-ui";
|
|
23
|
+
*
|
|
24
|
+
* const widget = East.function([], UIComponentType, _$ => {
|
|
25
|
+
* const routes = Navigation.config({ overview: { value: NullType, label: "Overview" } });
|
|
26
|
+
* return (
|
|
27
|
+
* <Reactive>{$ => {
|
|
28
|
+
* const nav = $.let(Navigation.bind(routes, "app.nav", [routes.Page.overview()]));
|
|
29
|
+
* return <Route nav={nav} routes={{ overview: () => <Text>Overview widget</Text> }} />;
|
|
30
|
+
* }}</Reactive>
|
|
31
|
+
* );
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* Desugars to `Route.Root({ nav, routes })`. A generic function declaration
|
|
37
|
+
* (merged with the `Route` namespace) so it is a valid generic JSX component.
|
|
38
|
+
*/
|
|
39
|
+
export function Route(props) {
|
|
40
|
+
return RouteFactory.Root(props);
|
|
41
|
+
}
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
43
|
+
(function (Route) {
|
|
44
|
+
/** The underlying factory: `Route.Root({ nav, routes })`. */
|
|
45
|
+
Route.Root = RouteFactory.Root;
|
|
46
|
+
})(Route || (Route = {}));
|
|
47
|
+
//# sourceMappingURL=route.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../../../src/runtime/navigation/route.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,oEAAoE;AAEpE,OAAO,EAAE,KAAK,IAAI,YAAY,EAAmB,MAAM,iCAAiC,CAAC;AAIzF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,KAAK,CAAsB,KAAoB;IAC3D,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAc,CAAC;AACjD,CAAC;AAED,2DAA2D;AAC3D,WAAiB,KAAK;IAClB,6DAA6D;IAChD,UAAI,GAAG,YAAY,CAAC,IAAI,CAAC;AAC1C,CAAC,EAHgB,KAAK,KAAL,KAAK,QAGrB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/runtime/reactive/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,iDAAiD;AAEjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/runtime/reactive/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,iDAAiD;AAEjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/runtime/reactive/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,iDAAiD;AAEjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/runtime/reactive/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,iDAAiD;AAEjD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
/** The `<Match>` hosting-slot tag (#333). */
|
|
6
|
+
import type { EastType } from "@elaraai/east";
|
|
7
|
+
import { type MatchInput } from "../../reactive/match.js";
|
|
8
|
+
import type { UIElement } from "../runtime.js";
|
|
9
|
+
/**
|
|
10
|
+
* Hosting slot over a variant — the component-level twin of `variant.match`:
|
|
11
|
+
* host-mounts only the active case and **remounts it on tag change**, so a
|
|
12
|
+
* stateful component (its own `<Reactive>` / binds) swapped at one slot is
|
|
13
|
+
* torn down and rebuilt fresh per key instead of keeping the previous case's
|
|
14
|
+
* mounted state. Same-tag payload/data churn re-renders without remounting.
|
|
15
|
+
* `cases` are keyed by the variant's case names, exhaustive, and each handler
|
|
16
|
+
* receives that case's typed payload — exactly like `variant.match`. Where
|
|
17
|
+
* the key is a nav route, use `<Route nav routes>` instead; for selecting a
|
|
18
|
+
* plain *value* by key, use `variant.match` directly.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* // .tsx file with the `@jsxImportSource @elaraai/east-ui` pragma
|
|
23
|
+
* import { East, NullType, StringType, UIComponentType, VariantType } from "@elaraai/east";
|
|
24
|
+
* import { Match, Reactive, State, Text } from "@elaraai/east-ui";
|
|
25
|
+
*
|
|
26
|
+
* const ModeType = VariantType({ list: NullType, detail: StringType });
|
|
27
|
+
*
|
|
28
|
+
* const slot = East.function([], UIComponentType, _$ => (
|
|
29
|
+
* <Reactive>{$ => {
|
|
30
|
+
* const modeBind = $.let(State.bind([ModeType], "app.mode", East.value(variant("list", null), ModeType)));
|
|
31
|
+
* const mode = $.let(modeBind.read());
|
|
32
|
+
* return <Match on={mode} cases={{
|
|
33
|
+
* list: (_$) => <Text>All items</Text>,
|
|
34
|
+
* detail: (_$, id) => <Text>{East.str`Item ${id}`}</Text>,
|
|
35
|
+
* }} />;
|
|
36
|
+
* }}</Reactive>
|
|
37
|
+
* ));
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @remarks
|
|
41
|
+
* Desugars to `Match.Root({ on, cases })`. A generic function declaration
|
|
42
|
+
* (merged with the `Match` namespace) so it is a valid generic JSX component.
|
|
43
|
+
*/
|
|
44
|
+
export declare function Match<C extends {
|
|
45
|
+
[K in string]: EastType;
|
|
46
|
+
}>(props: MatchInput<C>): UIElement;
|
|
47
|
+
export declare namespace Match {
|
|
48
|
+
/** The underlying factory: `Match.Root({ on, cases })`. */
|
|
49
|
+
const Root: <C extends { [K in string]: EastType; }>({ on, cases }: MatchInput<C>) => import("@elaraai/east").ExprType<typeof import("../../component.js").UIComponentType>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../../../../src/runtime/reactive/match.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,6CAA6C;AAE7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS;KAAG,CAAC,IAAI,MAAM,GAAG,QAAQ;CAAE,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAE5F;AAGD,yBAAiB,KAAK,CAAC;IACnB,2DAA2D;IACpD,MAAM,IAAI,iKAAoB,CAAC;CACzC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
3
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
4
|
+
*/
|
|
5
|
+
import { Match as MatchFactory } from "../../reactive/match.js";
|
|
6
|
+
/**
|
|
7
|
+
* Hosting slot over a variant — the component-level twin of `variant.match`:
|
|
8
|
+
* host-mounts only the active case and **remounts it on tag change**, so a
|
|
9
|
+
* stateful component (its own `<Reactive>` / binds) swapped at one slot is
|
|
10
|
+
* torn down and rebuilt fresh per key instead of keeping the previous case's
|
|
11
|
+
* mounted state. Same-tag payload/data churn re-renders without remounting.
|
|
12
|
+
* `cases` are keyed by the variant's case names, exhaustive, and each handler
|
|
13
|
+
* receives that case's typed payload — exactly like `variant.match`. Where
|
|
14
|
+
* the key is a nav route, use `<Route nav routes>` instead; for selecting a
|
|
15
|
+
* plain *value* by key, use `variant.match` directly.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* // .tsx file with the `@jsxImportSource @elaraai/east-ui` pragma
|
|
20
|
+
* import { East, NullType, StringType, UIComponentType, VariantType } from "@elaraai/east";
|
|
21
|
+
* import { Match, Reactive, State, Text } from "@elaraai/east-ui";
|
|
22
|
+
*
|
|
23
|
+
* const ModeType = VariantType({ list: NullType, detail: StringType });
|
|
24
|
+
*
|
|
25
|
+
* const slot = East.function([], UIComponentType, _$ => (
|
|
26
|
+
* <Reactive>{$ => {
|
|
27
|
+
* const modeBind = $.let(State.bind([ModeType], "app.mode", East.value(variant("list", null), ModeType)));
|
|
28
|
+
* const mode = $.let(modeBind.read());
|
|
29
|
+
* return <Match on={mode} cases={{
|
|
30
|
+
* list: (_$) => <Text>All items</Text>,
|
|
31
|
+
* detail: (_$, id) => <Text>{East.str`Item ${id}`}</Text>,
|
|
32
|
+
* }} />;
|
|
33
|
+
* }}</Reactive>
|
|
34
|
+
* ));
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @remarks
|
|
38
|
+
* Desugars to `Match.Root({ on, cases })`. A generic function declaration
|
|
39
|
+
* (merged with the `Match` namespace) so it is a valid generic JSX component.
|
|
40
|
+
*/
|
|
41
|
+
export function Match(props) {
|
|
42
|
+
return MatchFactory.Root(props);
|
|
43
|
+
}
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
45
|
+
(function (Match) {
|
|
46
|
+
/** The underlying factory: `Match.Root({ on, cases })`. */
|
|
47
|
+
Match.Root = MatchFactory.Root;
|
|
48
|
+
})(Match || (Match = {}));
|
|
49
|
+
//# sourceMappingURL=match.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match.js","sourceRoot":"","sources":["../../../../src/runtime/reactive/match.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,KAAK,IAAI,YAAY,EAAmB,MAAM,yBAAyB,CAAC;AAGjF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,KAAK,CAAwC,KAAoB;IAC7E,OAAO,YAAY,CAAC,IAAI,CAAC,KAAK,CAAc,CAAC;AACjD,CAAC;AAED,2DAA2D;AAC3D,WAAiB,KAAK;IAClB,2DAA2D;IAC9C,UAAI,GAAG,YAAY,CAAC,IAAI,CAAC;AAC1C,CAAC,EAHgB,KAAK,KAAL,KAAK,QAGrB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../test/navigation/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,+EAA+E;AAE/E,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../test/navigation/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,+EAA+E;AAE/E,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../test/navigation/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,+EAA+E;AAE/E,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../test/navigation/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,+EAA+E;AAE/E,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<Route>` is `<Pages>` generalized to any slot: renders only the active
|
|
3
|
+
* route's body and remounts it on navigation, but placeable anywhere. Here a
|
|
4
|
+
* single Route slot hosts a per-route widget driven by the bound nav handle.
|
|
5
|
+
*/
|
|
6
|
+
export declare const routeBasic: import("@elaraai/east").ExampleDef<[], any>;
|
|
7
|
+
/**
|
|
8
|
+
* The body `<Pages>` and a sidebar `<Route>` bind the **same** nav handle, so
|
|
9
|
+
* they stay in lockstep and each remounts *its own* slot on navigation — the
|
|
10
|
+
* behaviour `<Pages>` had for the page body, now available for any slot.
|
|
11
|
+
*/
|
|
12
|
+
export declare const routeSidebarSlot: import("@elaraai/east").ExampleDef<[], any>;
|
|
13
|
+
//# sourceMappingURL=route.examples.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.examples.d.ts","sourceRoot":"","sources":["../../../test/navigation/route.examples.tsx"],"names":[],"mappings":"AASA;;;;GAIG;AACH,eAAO,MAAM,UAAU,6CAgCrB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,6CA4C3B,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@elaraai/east-ui/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) 2025 Elara AI Pty Ltd
|
|
4
|
+
* Dual-licensed under AGPL-3.0 and commercial license. See LICENSE for details.
|
|
5
|
+
*/
|
|
6
|
+
/** @jsxImportSource @elaraai/east-ui */
|
|
7
|
+
import { East, NullType, StringType, IntegerType, StructType, example } from "@elaraai/east";
|
|
8
|
+
import { UIComponentType } from "@elaraai/east-ui";
|
|
9
|
+
import { Navigation, Route, Pages, Reactive, VStack, HStack, Box, Text, Button } from "@elaraai/east-ui";
|
|
10
|
+
/**
|
|
11
|
+
* `<Route>` is `<Pages>` generalized to any slot: renders only the active
|
|
12
|
+
* route's body and remounts it on navigation, but placeable anywhere. Here a
|
|
13
|
+
* single Route slot hosts a per-route widget driven by the bound nav handle.
|
|
14
|
+
*/
|
|
15
|
+
export const routeBasic = example({
|
|
16
|
+
keywords: ["Route", "Navigation", "nav", "routes", "hosting slot", "remount", "navigate", "widget"],
|
|
17
|
+
description: "A Route slot hosts a per-route widget — typed payload per route, remounts on navigation",
|
|
18
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
19
|
+
const ItemRow = StructType({ id: StringType, value: IntegerType });
|
|
20
|
+
const routes = Navigation.config({
|
|
21
|
+
overview: { value: NullType, label: "Overview" },
|
|
22
|
+
detail: { value: ItemRow, label: "Item" },
|
|
23
|
+
});
|
|
24
|
+
return (_jsx(Reactive, { children: $ => {
|
|
25
|
+
const nav = $.let(Navigation.bind(routes, "route.basic", [routes.Page.overview()]));
|
|
26
|
+
const open = $.const(East.function([], NullType, $ => {
|
|
27
|
+
$(nav.go.detail(East.value({ id: "item-1", value: 42n }, ItemRow)));
|
|
28
|
+
}));
|
|
29
|
+
const back = $.const(East.function([], NullType, $ => { $(nav.pop()); }));
|
|
30
|
+
return (_jsxs(VStack, { gap: "3", align: "stretch", children: [_jsxs(HStack, { gap: "2", children: [_jsx(Button, { onClick: open, children: "Open item-1" }), _jsx(Button, { onClick: back, children: "\u2190 Back" })] }), _jsx(Route, { nav: nav, routes: {
|
|
31
|
+
overview: () => _jsx(Text, { children: "Overview widget" }),
|
|
32
|
+
detail: (_$, row) => _jsx(Text, { children: East.str `Item ${row.id} — value ${East.print(row.value)}` }),
|
|
33
|
+
} })] }));
|
|
34
|
+
} }));
|
|
35
|
+
}),
|
|
36
|
+
inputs: [],
|
|
37
|
+
});
|
|
38
|
+
/**
|
|
39
|
+
* The body `<Pages>` and a sidebar `<Route>` bind the **same** nav handle, so
|
|
40
|
+
* they stay in lockstep and each remounts *its own* slot on navigation — the
|
|
41
|
+
* behaviour `<Pages>` had for the page body, now available for any slot.
|
|
42
|
+
*/
|
|
43
|
+
export const routeSidebarSlot = example({
|
|
44
|
+
keywords: ["Route", "Pages", "Navigation", "sidebar", "slot", "lockstep", "app shell", "remount"],
|
|
45
|
+
description: "A sidebar Route slot beside the body Pages — one nav handle drives both, each remounts its own slot on navigation",
|
|
46
|
+
fn: East.function([], UIComponentType, (_$) => {
|
|
47
|
+
const DetailRow = StructType({ id: StringType, group: StringType });
|
|
48
|
+
const routes = Navigation.config({
|
|
49
|
+
overview: { value: NullType, label: "Overview" },
|
|
50
|
+
detail: { value: DetailRow, label: "Item" },
|
|
51
|
+
});
|
|
52
|
+
return (_jsx(Reactive, { children: $ => {
|
|
53
|
+
const nav = $.let(Navigation.bind(routes, "route.shell", [routes.Page.overview()]));
|
|
54
|
+
const open = $.const(East.function([], NullType, $ => {
|
|
55
|
+
$(nav.go.detail(East.value({ id: "item-7", group: "B" }, DetailRow)));
|
|
56
|
+
}));
|
|
57
|
+
const back = $.const(East.function([], NullType, $ => { $(nav.pop()); }));
|
|
58
|
+
return (_jsxs(HStack, { gap: "4", align: "stretch", children: [_jsx(Box, { padding: "3", children: _jsx(Route, { nav: nav, routes: {
|
|
59
|
+
overview: () => _jsx(Text, { children: "No selection" }),
|
|
60
|
+
detail: (_$, row) => _jsx(Text, { children: East.str `Selected: ${row.id} · ${row.group}` }),
|
|
61
|
+
} }) }), _jsx(Pages, { nav: nav, pages: {
|
|
62
|
+
overview: () => (_jsxs(VStack, { gap: "2", align: "stretch", children: [_jsx(Text, { children: "Overview \u2014 item list" }), _jsx(Button, { onClick: open, children: "Open item-7" })] })),
|
|
63
|
+
detail: (_$, row) => (_jsxs(VStack, { gap: "2", align: "stretch", children: [_jsx(Text, { children: East.str `Item ${row.id} · group ${row.group}` }), _jsx(Button, { onClick: back, children: "\u2190 Back" })] })),
|
|
64
|
+
} })] }));
|
|
65
|
+
} }));
|
|
66
|
+
}),
|
|
67
|
+
inputs: [],
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=route.examples.js.map
|