@octane-xplat/ui 0.3.0 → 0.4.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.
Files changed (123) hide show
  1. package/README.md +46 -0
  2. package/dist/native/octane-client-build.json +1 -1
  3. package/dist/native/ui.js +52 -40
  4. package/dist/native/ui10.js +20 -11
  5. package/dist/native/ui11.js +42 -109
  6. package/dist/native/ui12.js +17 -84
  7. package/dist/native/ui13.js +11 -41
  8. package/dist/native/ui14.js +118 -76
  9. package/dist/native/ui15.js +43 -92
  10. package/dist/native/ui16.js +106 -45
  11. package/dist/native/ui17.js +29 -42
  12. package/dist/native/ui18.js +34 -53
  13. package/dist/native/ui19.js +246 -32
  14. package/dist/native/ui20.js +27 -16
  15. package/dist/native/ui21.js +376 -86
  16. package/dist/native/ui22.js +26 -114
  17. package/dist/native/ui23.js +49 -97
  18. package/dist/native/ui24.js +78 -53
  19. package/dist/native/ui25.js +138 -16
  20. package/dist/native/ui26.js +8 -23
  21. package/dist/native/ui27.js +60 -34
  22. package/dist/native/ui28.js +36 -142
  23. package/dist/native/ui29.js +101 -21
  24. package/dist/native/ui30.js +14 -31
  25. package/dist/native/ui31.js +55 -36
  26. package/dist/native/ui32.js +49 -61
  27. package/dist/native/ui33.js +36 -28
  28. package/dist/native/ui34.js +51 -8
  29. package/dist/native/ui35.js +98 -20
  30. package/dist/native/ui36.js +9 -33
  31. package/dist/native/ui37.js +42 -15
  32. package/dist/native/ui38.js +186 -57
  33. package/dist/native/ui39.js +96 -24
  34. package/dist/native/ui4.js +22 -20
  35. package/dist/native/ui40.js +57 -13
  36. package/dist/native/ui41.js +15 -100
  37. package/dist/native/ui42.js +6 -146
  38. package/dist/native/ui43.js +37 -21
  39. package/dist/native/ui44.js +265 -6
  40. package/dist/native/ui45.js +28 -0
  41. package/dist/native/ui46.js +35 -0
  42. package/dist/native/ui47.js +41 -0
  43. package/dist/native/ui48.js +72 -0
  44. package/dist/native/ui49.js +66 -0
  45. package/dist/native/ui5.js +37 -77
  46. package/dist/native/ui50.js +27 -0
  47. package/dist/native/ui51.js +13 -0
  48. package/dist/native/ui52.js +92 -0
  49. package/dist/native/ui53.js +34 -0
  50. package/dist/native/ui54.js +17 -0
  51. package/dist/native/ui55.js +156 -0
  52. package/dist/native/ui56.js +33 -0
  53. package/dist/native/ui57.js +14 -0
  54. package/dist/native/ui58.js +22 -0
  55. package/dist/native/ui59.js +8 -0
  56. package/dist/native/ui6.js +52 -9
  57. package/dist/native/ui60.js +108 -0
  58. package/dist/native/ui61.js +76 -0
  59. package/dist/native/ui7.js +42 -26
  60. package/dist/native/ui8.js +22 -12
  61. package/dist/native/ui9.js +18 -9
  62. package/dist/web/octane-client-build.json +1 -1
  63. package/dist/web/ui.js +51 -40
  64. package/dist/web/ui10.js +33 -186
  65. package/dist/web/ui11.js +28 -409
  66. package/dist/web/ui12.js +21 -68
  67. package/dist/web/ui13.js +201 -97
  68. package/dist/web/ui14.js +85 -95
  69. package/dist/web/ui15.js +368 -25
  70. package/dist/web/ui16.js +13 -101
  71. package/dist/web/ui17.js +246 -42
  72. package/dist/web/ui18.js +237 -28
  73. package/dist/web/ui19.js +36 -10
  74. package/dist/web/ui20.js +73 -102
  75. package/dist/web/ui21.js +102 -73
  76. package/dist/web/ui22.js +95 -55
  77. package/dist/web/ui23.js +94 -55
  78. package/dist/web/ui24.js +31 -15
  79. package/dist/web/ui25.js +105 -19
  80. package/dist/web/ui26.js +29 -23
  81. package/dist/web/ui27.js +12 -123
  82. package/dist/web/ui28.js +9 -110
  83. package/dist/web/ui29.js +52 -102
  84. package/dist/web/ui3.js +29 -18
  85. package/dist/web/ui30.js +32 -41
  86. package/dist/web/ui31.js +120 -39
  87. package/dist/web/ui32.js +41 -74
  88. package/dist/web/ui33.js +148 -32
  89. package/dist/web/ui34.js +54 -51
  90. package/dist/web/ui35.js +55 -7
  91. package/dist/web/ui36.js +18 -41
  92. package/dist/web/ui37.js +6 -46
  93. package/dist/web/ui38.js +37 -16
  94. package/dist/web/ui39.js +135 -81
  95. package/dist/web/ui4.js +46 -152
  96. package/dist/web/ui40.js +39 -37
  97. package/dist/web/ui41.js +41 -16
  98. package/dist/web/ui42.js +74 -7
  99. package/dist/web/ui43.js +40 -20
  100. package/dist/web/ui44.js +60 -6
  101. package/dist/web/ui45.js +9 -0
  102. package/dist/web/ui46.js +45 -0
  103. package/dist/web/ui47.js +13 -0
  104. package/dist/web/ui48.js +63 -0
  105. package/dist/web/ui49.js +47 -0
  106. package/dist/web/ui5.js +131 -27
  107. package/dist/web/ui50.js +17 -0
  108. package/dist/web/ui51.js +149 -0
  109. package/dist/web/ui52.js +42 -0
  110. package/dist/web/ui53.js +17 -0
  111. package/dist/web/ui54.js +9 -0
  112. package/dist/web/ui55.js +22 -0
  113. package/dist/web/ui56.js +8 -0
  114. package/dist/web/ui57.js +44 -0
  115. package/dist/web/ui58.js +46 -0
  116. package/dist/web/ui6.js +81 -45
  117. package/dist/web/ui7.js +43 -21
  118. package/dist/web/ui8.js +27 -21
  119. package/dist/web/ui9.js +29 -18
  120. package/package.json +13 -4
  121. package/src/theme/tokens.css +369 -138
  122. package/types/index.d.ts +102 -58
  123. package/types/props.d.ts +233 -13
@@ -1,93 +1,383 @@
1
- import { cx } from "./ui2.js";
2
- import { getColorScheme } from "./ui20.js";
3
- import { createNativeScriptRoot, defineUniversalComponent, hookSlots, universalPlan, universalValue, useEffect, useRef } from "@nativescript-community/octane";
4
- import { GridLayout, getRootLayout, isAndroid, isIOS } from "@nativescript/core";
5
- //#region src/Overlay.native.tsrx
6
- var _hs$ = /* @__PURE__ */ hookSlots(3);
7
- var _h$0 = /* @__PURE__ */ Symbol(_hs$);
8
- var _h$1 = /* @__PURE__ */ Symbol(_hs$ + 1);
9
- var _h$2 = /* @__PURE__ */ Symbol(_hs$ + 2);
10
- var __octaneUniversalPlan0 = universalPlan("nativescript", {
11
- "kind": "range",
12
- "children": [{
13
- "kind": "slot",
14
- "slot": 0
15
- }]
16
- });
17
- var __octaneUniversalPlan1 = universalPlan("nativescript", {
18
- "kind": "range",
19
- "children": []
1
+ import { getStack, onStackRegistered, stackEntries } from "./ui18.js";
2
+ import { RouteRedirect, buildRoutePath, layoutChain, linkPath, matchUrl } from "./ui19.js";
3
+ import { RouteHost } from "./ui20.js";
4
+ import { createNativeScriptRoot } from "@nativescript-community/octane";
5
+ import { Application, GridLayout, Page } from "@nativescript/core";
6
+ import { hookSlots as hookSlots$1, useSyncExternalStore as useSyncExternalStore$1 } from "octane";
7
+ //#region src/route.native.ts
8
+ /** Native twin of route.web — same Route contract over Frame/Page stacks.
9
+ * `pushRoute` resolves a stack ('root' = the app's root Frame — an
10
+ * explicit registerStack('root') or, by default, the Frame returned by
11
+ * Application.getRootView(); named stacks come from TabSpec.stack panes
12
+ * or registerStack) and pushes a Page hosting the registered screen —
13
+ * each pushed Page is its own Octane root (decision #9: pages never
14
+ * share context with their presenter). Params land as props; named-stack
15
+ * pushes get `_stack` injected so the screen can popRoute its own stack.
16
+ *
17
+ * Registration contract (the footgun this module exists to defuse):
18
+ * - `registerScreens(table)` once — pushRoute resolves `name` through it.
19
+ * - 'root' needs nothing when the app boots a Frame as its root view
20
+ * (the standard shape); non-Frame roots can't host pushes at all.
21
+ * - named stacks register via `TabSpec.stack` or `registerStack`.
22
+ * Every drop path warns loudly — silent no-ops are how apps ship a
23
+ * default route on native while working fine on web. */
24
+ var screens = {};
25
+ var routes = [];
26
+ var routeLayouts = {};
27
+ var routeLoaders = {};
28
+ /** Register the app's name → screen table (call once, from the shared
29
+ * routes module). Native `pushRoute` resolves `route.name` through it;
30
+ * on web the table feeds `screenFor` for outlets without a
31
+ * `resolveScreen` prop. `manifest` (from deriveRouteManifest) is stored
32
+ * for parity — the web leaf matches URLs through it; native navigation
33
+ * is name+params and only needs the table. */
34
+ function registerScreens(table, manifest) {
35
+ screens = table;
36
+ routes = manifest ?? [];
37
+ }
38
+ /** One-call registration for route-dir apps — screens + URL patterns +
39
+ * layouts all come from deriveRouteManifest. */
40
+ function registerRoutes(manifest) {
41
+ registerScreens(manifest.screens, manifest.routes);
42
+ routeLayouts = manifest.layouts ?? {};
43
+ routeLoaders = manifest.loaders ?? {};
44
+ }
45
+ function layoutsForRoute(name) {
46
+ return layoutChain(routeLayouts, name);
47
+ }
48
+ function screenFor(name) {
49
+ return screens[name];
50
+ }
51
+ /** Wrap a screen in its directory `_layout` chain (outermost →
52
+ * innermost) as a single root component — pushed Pages and modal roots
53
+ * render this, matching web's outlet wrapping. */
54
+ var tracked = /* @__PURE__ */ new WeakSet();
55
+ var listeners = /* @__PURE__ */ new Set();
56
+ var pageRoutes = /* @__PURE__ */ new WeakMap();
57
+ var warned = /* @__PURE__ */ new Set();
58
+ function emit() {
59
+ listeners.forEach((l) => l());
60
+ }
61
+ function warnOnce(key, msg) {
62
+ if (warned.has(key)) return;
63
+ warned.add(key);
64
+ console.warn("[octane-xplat] " + msg);
65
+ }
66
+ /** Attach the route-store emission hook once per frame. `navigatedTo`
67
+ * fires on push AND on pop (the revealed page re-fires it), so one
68
+ * listener keeps every useRoute snapshot honest. */
69
+ function trackFrame(frame) {
70
+ if (tracked.has(frame)) return;
71
+ tracked.add(frame);
72
+ frame.on("navigatedTo", emit);
73
+ }
74
+ function resolveStack(stack) {
75
+ const frame = getStack(stack);
76
+ if (frame) trackFrame(frame);
77
+ return frame;
78
+ }
79
+ onStackRegistered((_name, frame) => {
80
+ trackFrame(frame);
81
+ emit();
20
82
  });
21
- var OverlayBody = defineUniversalComponent("nativescript", function OverlayBody(props) {
22
- return universalValue(__octaneUniversalPlan0, [props.children]);
23
- }, { "module": "@nativescript-community/octane" });
24
- var Overlay = defineUniversalComponent("nativescript", function Overlay(props) {
25
- const shown = useRef(null, _h$0);
26
- const dismiss = useRef(props.onDismiss, _h$1);
27
- dismiss.current = props.onDismiss;
28
- useEffect(() => {
29
- const current = shown.current;
30
- if (!props.open) {
31
- if (!current) return;
32
- current.dismissOnClose = false;
33
- shown.current = null;
34
- const rootLayout = getRootLayout();
35
- if (rootLayout?.hasChild?.(current.host)) rootLayout.close(current.host).then(() => current.root.unmount?.(), (error) => {
36
- console.error("[Overlay] close failed", error);
37
- current.root.unmount?.();
38
- });
39
- else current.root.unmount?.();
40
- return;
41
- }
42
- if (current) {
43
- current.root.render(OverlayBody, { children: props.children });
44
- current.host.className = cx("vx-overlay-host", getColorScheme() === "dark" && "ns-dark", props.className);
45
- if (props.style) Object.assign(current.host.style, props.style);
46
- Object.assign(current.host, isIOS ? props.ios : isAndroid ? props.android : void 0);
83
+ var modalHosts = [];
84
+ var swapTabRoutes = /* @__PURE__ */ new Map();
85
+ var swapTabOrder = [];
86
+ function presentationFor(name) {
87
+ return routes.find((m) => m.name === name)?.presentation;
88
+ }
89
+ function metaFor(name) {
90
+ return routes.find((meta) => meta.name === name);
91
+ }
92
+ function headFor(r) {
93
+ const head = metaFor(r.name)?.head;
94
+ return typeof head === "function" ? head(r.params) : head;
95
+ }
96
+ function contextFor(r) {
97
+ return r.context ?? {};
98
+ }
99
+ async function prepareRoute(r, redirects = 0) {
100
+ const beforeLoad = metaFor(r.name)?.beforeLoad;
101
+ if (!beforeLoad) {
102
+ commitRoute(r);
103
+ return;
104
+ }
105
+ if (redirects > 16) {
106
+ console.warn(`[octane-xplat] beforeLoad redirect loop for '${r.name}'`);
107
+ return;
108
+ }
109
+ try {
110
+ const returned = await beforeLoad({
111
+ params: r.params,
112
+ context: contextFor(r)
113
+ });
114
+ const context = returned ? {
115
+ ...contextFor(r),
116
+ ...returned
117
+ } : contextFor(r);
118
+ commitRoute({
119
+ ...r,
120
+ context
121
+ });
122
+ } catch (e) {
123
+ if (e instanceof RouteRedirect) {
124
+ await prepareRoute(e.route, redirects + 1);
47
125
  return;
48
126
  }
49
- const rootLayout = getRootLayout();
50
- if (!rootLayout) return;
51
- const host = new GridLayout();
52
- host.className = cx("vx-overlay-host", getColorScheme() === "dark" && "ns-dark", props.className);
53
- host.horizontalAlignment = "stretch";
54
- host.verticalAlignment = "stretch";
55
- if (props.style) Object.assign(host.style, props.style);
56
- Object.assign(host, isIOS ? props.ios : isAndroid ? props.android : void 0);
57
- const root = createNativeScriptRoot(host);
58
- const opened = {
59
- host,
60
- root,
61
- dismissOnClose: true
127
+ console.warn(`[octane-xplat] beforeLoad('${r.name}') rejected: ${e?.message ?? e}`);
128
+ }
129
+ }
130
+ function redirect(r) {
131
+ throw new RouteRedirect(r);
132
+ }
133
+ function pushRoute(r) {
134
+ if (metaFor(r.name)?.beforeLoad) {
135
+ prepareRoute(r);
136
+ return;
137
+ }
138
+ commitRoute(r);
139
+ }
140
+ function commitRoute(r) {
141
+ const C = screenFor(r.name);
142
+ if (!C) {
143
+ warnOnce("screen:" + r.name, `pushRoute('${r.name}') dropped — '${r.name}' isn't in the screen table. Call registerScreens({ ${r.name}: ... }) at boot.`);
144
+ return;
145
+ }
146
+ const presentation = r.presentation ?? presentationFor(r.name);
147
+ const loader = routeLoaders[r.name];
148
+ if (loader && !Object.prototype.hasOwnProperty.call(r, "loaderData") && !Object.prototype.hasOwnProperty.call(r, "loaderError")) {
149
+ Promise.resolve().then(() => loader(r.params)).then((loaderData) => commitRoute({
150
+ ...r,
151
+ loaderData
152
+ }), (loaderError) => commitRoute({
153
+ ...r,
154
+ loaderError
155
+ }));
156
+ return;
157
+ }
158
+ if (presentation === "modal") {
159
+ const presenter = resolveStack(r.stack) ?? resolveStack("root");
160
+ if (presenter) pushModal(presenter, {
161
+ ...r,
162
+ presentation
163
+ }, C);
164
+ else warnOnce("modal:" + r.name, `modal route '${r.name}' dropped — no loaded presenter Frame.`);
165
+ return;
166
+ }
167
+ if (Application.android && r.stack !== "root") {
168
+ const entries = swapTabRoutes.get(r.stack) ?? [];
169
+ entries.push({
170
+ ...r,
171
+ presentation
172
+ });
173
+ swapTabRoutes.set(r.stack, entries);
174
+ const at = swapTabOrder.indexOf(r.stack);
175
+ if (at !== -1) swapTabOrder.splice(at, 1);
176
+ swapTabOrder.push(r.stack);
177
+ const title = headFor(r)?.title;
178
+ const page = resolveStack(r.stack)?.currentPage;
179
+ if (title !== void 0 && page) page.actionBar.title = title;
180
+ emit();
181
+ return;
182
+ }
183
+ const frame = resolveStack(r.stack);
184
+ if (!frame) {
185
+ warnOnce("stack:" + r.stack, r.stack === "root" ? `pushRoute('${r.name}') dropped — no root Frame. Boot with a Frame as the app root (Application.run create() returning new Frame) or call registerStack('root', frame).` : `pushRoute('${r.name}') dropped — no stack '${r.stack}' is registered. Named stacks come from TabSpec.stack on <Tabs> or registerStack('${r.stack}', frame).`);
186
+ return;
187
+ }
188
+ if (!frame.isLoaded) frame.callLoaded?.();
189
+ const props = {
190
+ ...r.params,
191
+ ...contextFor(r),
192
+ ...r.stack === "root" ? {} : { _stack: r.stack },
193
+ _pushed: true
194
+ };
195
+ if (Object.prototype.hasOwnProperty.call(r, "loaderData")) props.data = r.loaderData;
196
+ if (Object.prototype.hasOwnProperty.call(r, "loaderError")) props.error = r.loaderError;
197
+ try {
198
+ frame.navigate({
199
+ create: () => {
200
+ const page = new Page();
201
+ page.id = r.name + "-page";
202
+ page.actionBarHidden = true;
203
+ const head = headFor(r);
204
+ if (head?.title !== void 0) page.actionBar.title = head.title;
205
+ pageRoutes.set(page, r);
206
+ const host = new GridLayout();
207
+ page.content = host;
208
+ createNativeScriptRoot(host).render(RouteHost, {
209
+ screen: C,
210
+ layouts: layoutsForRoute(r.name),
211
+ params: props
212
+ });
213
+ return page;
214
+ },
215
+ transition: presentation === "fade" ? { name: "fade" } : void 0
216
+ });
217
+ } catch (e) {
218
+ console.warn("[octane-xplat] pushRoute(" + r.name + ") threw: " + e?.message);
219
+ }
220
+ }
221
+ /** A modal route is its own Octane root on a `showModal` host — the same
222
+ * shape as openModal in modal-service, but tracked as a Route so
223
+ * popRoute/currentModalRoute stay honest. The screen receives `close`
224
+ * alongside its params and calls it (or popRoute) to dismiss. */
225
+ function pushModal(frame, r, C) {
226
+ const host = new GridLayout();
227
+ const root = createNativeScriptRoot(host);
228
+ const entry = {
229
+ host,
230
+ route: r,
231
+ dismiss: () => {}
232
+ };
233
+ entry.dismiss = () => {
234
+ const i = modalHosts.indexOf(entry);
235
+ if (i === -1) return;
236
+ modalHosts.splice(i, 1);
237
+ emit();
238
+ };
239
+ const presenter = frame.currentPage ?? frame;
240
+ try {
241
+ modalHosts.push(entry);
242
+ const params = {
243
+ ...r.params,
244
+ ...contextFor(r),
245
+ _pushed: true,
246
+ close: () => host.closeModal?.()
62
247
  };
63
- shown.current = opened;
64
- root.render(OverlayBody, { children: props.children });
65
- host.on("closed", () => {
66
- if (shown.current === opened) shown.current = null;
67
- root.unmount?.();
68
- if (opened.dismissOnClose) dismiss.current?.();
248
+ if (Object.prototype.hasOwnProperty.call(r, "loaderData")) params.data = r.loaderData;
249
+ if (Object.prototype.hasOwnProperty.call(r, "loaderError")) params.error = r.loaderError;
250
+ root.render(RouteHost, {
251
+ screen: C,
252
+ layouts: layoutsForRoute(r.name),
253
+ params
69
254
  });
70
- const open = () => rootLayout.open(host, props.shadeCover ? { shadeCover: {
71
- opacity: .45,
72
- color: "#000000",
73
- tapToClose: true
74
- } } : {}).catch((error) => {
75
- if (shown.current === opened) shown.current = null;
76
- console.error("[Overlay] open failed", error);
77
- root.unmount?.();
255
+ presenter.showModal(host, {
256
+ context: {},
257
+ closeCallback: entry.dismiss,
258
+ fullscreen: true,
259
+ animated: true
78
260
  });
79
- if (rootLayout.hasChild?.(host)) rootLayout.close(host).then(open, (error) => console.error("[Overlay] stale host close failed", error));
80
- else open();
81
- }, [
82
- props.open,
83
- props.children,
84
- props.shadeCover,
85
- props.className,
86
- props.style,
87
- props.ios,
88
- props.android
89
- ], _h$2);
90
- return universalValue(__octaneUniversalPlan1, []);
91
- }, { "module": "@nativescript-community/octane" });
261
+ emit();
262
+ } catch (e) {
263
+ entry.dismiss();
264
+ root.unmount?.();
265
+ console.warn("[octane-xplat] modal pushRoute(" + r.name + ") threw: " + e?.message);
266
+ }
267
+ }
268
+ /** Pop the top page of a stack ('root' default) — or dismiss the top
269
+ * modal if one is open. Quiet no-op at the base page — matching web,
270
+ * where back at the app root is a no-op; loud only when the stack
271
+ * itself doesn't exist. */
272
+ function popRoute(stack = "root") {
273
+ const modal = modalHosts[modalHosts.length - 1];
274
+ if (modal) {
275
+ modal.dismiss();
276
+ modal.host.closeModal?.();
277
+ return;
278
+ }
279
+ if (Application.android && stack !== "root") {
280
+ const entries = swapTabRoutes.get(stack);
281
+ if (entries?.length) {
282
+ entries.pop();
283
+ if (!entries.length) {
284
+ swapTabRoutes.delete(stack);
285
+ const at = swapTabOrder.indexOf(stack);
286
+ if (at !== -1) swapTabOrder.splice(at, 1);
287
+ }
288
+ emit();
289
+ }
290
+ return;
291
+ }
292
+ const frame = resolveStack(stack);
293
+ if (!frame) {
294
+ warnOnce("pop:" + stack, `popRoute('${stack}') dropped — no such stack is registered.`);
295
+ return;
296
+ }
297
+ frame.goBack();
298
+ }
299
+ /** Route stamped on a stack's current page, or null at its base page. */
300
+ function routeFor(stack) {
301
+ if (Application.android && stack !== "root") {
302
+ const entries = swapTabRoutes.get(stack);
303
+ if (entries?.length) return entries[entries.length - 1];
304
+ }
305
+ const page = resolveStack(stack)?.currentPage;
306
+ return (page && pageRoutes.get(page)) ?? null;
307
+ }
308
+ /** The top route across stacks — root's current route wins (a root push
309
+ * covers the shell); otherwise the most recently registered named stack
310
+ * showing a pushed page. Native has no boot URL, so this is an
311
+ * approximation of web's "current URL route", not a deep link. */
312
+ function currentRoute() {
313
+ const root = routeFor("root");
314
+ if (root) return root;
315
+ for (const name of [...swapTabOrder].reverse()) {
316
+ const route = routeFor(name);
317
+ if (route) return route;
318
+ }
319
+ const named = [...stackEntries()].reverse();
320
+ for (const [name] of named) {
321
+ const r = routeFor(name);
322
+ if (r) return r;
323
+ }
324
+ return null;
325
+ }
326
+ /** Named stacks containing routes, including Android's swap-pane stacks. */
327
+ function routeStacks() {
328
+ return Application.android ? [.../* @__PURE__ */ new Set([...swapTabOrder, ...[...stackEntries()].map(([name]) => name)])] : [...stackEntries()].map(([name]) => name);
329
+ }
330
+ /** The modal route currently open, if any — parity with the web leaf. */
331
+ function currentModalRoute() {
332
+ return modalHosts[modalHosts.length - 1]?.route ?? null;
333
+ }
334
+ /** Deep-link entry: normalize a URL (http(s) or app-scheme) to a path,
335
+ * match it against the manifest, push the route. Wire it at boot:
336
+ * `onDeepLink(pushDeepLink)` plus one `consumeInitialUrl()` call. */
337
+ function pushDeepLink(url) {
338
+ const r = matchUrl(routes, linkPath(url));
339
+ if (!r) {
340
+ warnOnce("link:" + url, `pushDeepLink('${url}') dropped — no route matches.`);
341
+ return false;
342
+ }
343
+ pushRoute(r);
344
+ return true;
345
+ }
346
+ /** Path-string parity for the web leaf's hrefFor — a canonical
347
+ * /<stack>/<path> rendering of the route (deep-linking consumes it
348
+ * there). Native navigation itself is name+params, not URLs. */
349
+ function hrefFor(r) {
350
+ return buildRoutePath(routes, r);
351
+ }
352
+ function useRoute(stack) {
353
+ return useSyncExternalStore$1((cb) => {
354
+ listeners.add(cb);
355
+ return () => listeners.delete(cb);
356
+ }, () => routeFor(stack), _h$0);
357
+ }
358
+ /** Whether the selected native stack has a page (or route-owned Android swap
359
+ * entry) that can be popped. The modal root is also a back affordance. */
360
+ function canGoBack(stack = "root") {
361
+ if (modalHosts[modalHosts.length - 1]?.route.stack === stack) return true;
362
+ if (Application.android && stack !== "root") return !!swapTabRoutes.get(stack)?.length;
363
+ return !!resolveStack(stack)?.canGoBack?.();
364
+ }
365
+ function useCanGoBack(stack = "root") {
366
+ return useSyncExternalStore$1((cb) => {
367
+ listeners.add(cb);
368
+ return () => listeners.delete(cb);
369
+ }, () => canGoBack(stack), _h$1);
370
+ }
371
+ /** The modal route overlaying the shell — parity with the web leaf. */
372
+ function useModalRoute() {
373
+ return useSyncExternalStore$1((cb) => {
374
+ listeners.add(cb);
375
+ return () => listeners.delete(cb);
376
+ }, () => currentModalRoute(), _h$2);
377
+ }
378
+ var _hs$ = /* @__PURE__ */ hookSlots$1(3);
379
+ var _h$0 = /* @__PURE__ */ Symbol(_hs$);
380
+ var _h$1 = /* @__PURE__ */ Symbol(_hs$ + 1);
381
+ var _h$2 = /* @__PURE__ */ Symbol(_hs$ + 2);
92
382
  //#endregion
93
- export { Overlay };
383
+ export { canGoBack, currentModalRoute, currentRoute, hrefFor, layoutsForRoute, popRoute, pushDeepLink, pushRoute, redirect, registerRoutes, registerScreens, routeFor, routeStacks, screenFor, useCanGoBack, useModalRoute, useRoute };
@@ -1,124 +1,36 @@
1
1
  import { cx } from "./ui2.js";
2
- import { getColorScheme } from "./ui20.js";
3
- import { createNativeScriptRoot, defineUniversalComponent, hookSlots, universalPlan, universalValue, useEffect, useRef } from "@nativescript-community/octane";
4
- import { GridLayout, getRootLayout, isAndroid, isIOS } from "@nativescript/core";
5
- //#region src/Popover.native.tsrx
6
- var _hs$ = /* @__PURE__ */ hookSlots(2);
7
- var _h$0 = /* @__PURE__ */ Symbol(_hs$);
8
- var _h$1 = /* @__PURE__ */ Symbol(_hs$ + 1);
2
+ import { Pressable } from "./ui16.js";
3
+ import { pushRoute, useRoute } from "./ui21.js";
4
+ import { defineUniversalComponent, hookSlots, universalChildren, universalComponent, universalPlan, universalProps, universalValue, withSlot } from "@nativescript-community/octane";
5
+ //#region src/NavLink.native.tsrx
6
+ var _h$0 = /* @__PURE__ */ Symbol(/* @__PURE__ */ hookSlots(1));
9
7
  var __octaneUniversalPlan0 = universalPlan("nativescript", {
10
8
  "kind": "host",
11
- "type": "absolutelayout",
9
+ "type": "flexboxlayout",
10
+ "props": { "flexDirection": "row" },
12
11
  "children": [{
13
- "kind": "host",
14
- "type": "gridlayout",
15
- "bindings": [
16
- ["left", 0],
17
- ["top", 1],
18
- ["className", 2],
19
- ["style", 3]
20
- ],
21
- "children": [{
22
- "kind": "slot",
23
- "slot": 4
24
- }]
12
+ "kind": "slot",
13
+ "slot": 0
25
14
  }]
26
15
  });
27
16
  var __octaneUniversalPlan1 = universalPlan("nativescript", {
28
- "kind": "range",
29
- "children": []
17
+ "kind": "slot",
18
+ "slot": 0
30
19
  });
31
- var PopoverBody = defineUniversalComponent("nativescript", function PopoverBody(props) {
32
- return universalValue(__octaneUniversalPlan0, [
33
- props.left,
34
- props.top,
35
- cx("vx-popover", props.className),
36
- props.style,
37
- props.children
38
- ]);
39
- }, { "module": "@nativescript-community/octane" });
40
- var Popover = defineUniversalComponent("nativescript", function Popover(props) {
41
- const shown = useRef(null, _h$0);
42
- const anchorRef = props.anchor?.current ?? props.anchor;
43
- useEffect(() => {
44
- const current = shown.current;
45
- if (!props.open) {
46
- if (!current) return;
47
- shown.current = null;
48
- const rootLayout = getRootLayout();
49
- if (rootLayout?.hasChild?.(current.host)) rootLayout.close(current.host).then(() => current.root.unmount?.(), (error) => {
50
- console.error("[Popover] close failed", error);
51
- current.root.unmount?.();
52
- });
53
- else current.root.unmount?.();
54
- return;
55
- }
56
- const rootLayout = getRootLayout();
57
- if (!rootLayout || !anchorRef?.getLocationRelativeTo || !anchorRef?.getActualSize) return;
58
- const anchorPoint = anchorRef.getLocationRelativeTo(rootLayout);
59
- const size = anchorRef.getActualSize();
60
- const gap = 8;
61
- let left = anchorPoint.x;
62
- let top = anchorPoint.y + size.height + gap;
63
- if (props.placement === "top") top = anchorPoint.y - 56;
64
- if (props.placement === "left") {
65
- left = anchorPoint.x - 168;
66
- top = anchorPoint.y;
67
- }
68
- if (props.placement === "right") {
69
- left = anchorPoint.x + size.width + gap;
70
- top = anchorPoint.y;
71
- }
72
- if (current) {
73
- current.left = left;
74
- current.top = top;
75
- current.root.render(PopoverBody, {
76
- children: props.children,
77
- left,
78
- top,
79
- className: props.className,
80
- style: props.style
81
- });
82
- return;
83
- }
84
- const host = new GridLayout();
85
- host.className = cx("vx-popover-host", getColorScheme() === "dark" && "ns-dark");
86
- host.horizontalAlignment = "stretch";
87
- host.verticalAlignment = "stretch";
88
- Object.assign(host, isIOS ? props.ios : isAndroid ? props.android : void 0);
89
- const root = createNativeScriptRoot(host);
90
- const opened = {
91
- host,
92
- root,
93
- left,
94
- top
95
- };
96
- shown.current = opened;
97
- root.render(PopoverBody, {
98
- children: props.children,
99
- left,
100
- top,
101
- className: props.className,
102
- style: props.style
103
- });
104
- const open = () => rootLayout.open(host).catch((error) => {
105
- if (shown.current === opened) shown.current = null;
106
- console.error("[Popover] open failed", error);
107
- root.unmount?.();
108
- });
109
- if (rootLayout.hasChild?.(host)) rootLayout.close(host).then(open, (error) => console.error("[Popover] stale host close failed", error));
110
- else open();
111
- }, [
112
- props.open,
113
- props.anchor,
114
- props.placement,
115
- props.children,
116
- props.className,
117
- props.style,
118
- props.ios,
119
- props.android
120
- ], _h$1);
121
- return universalValue(__octaneUniversalPlan1, []);
20
+ var NavLink = defineUniversalComponent("nativescript", function NavLink(props) {
21
+ const current = withSlot(_h$0, useRoute, props.route.stack);
22
+ const active = !!current && current.name === props.route.name && Object.keys(current.params).length === Object.keys(props.route.params).length && Object.entries(props.route.params).every(([key, value]) => String(current.params[key]) === String(value));
23
+ return universalValue(__octaneUniversalPlan1, [universalComponent("nativescript", Pressable, universalProps([[
24
+ "set",
25
+ "className",
26
+ cx(props.className, active ? props.activeClassName : void 0)
27
+ ], [
28
+ "set",
29
+ "onPress",
30
+ () => pushRoute(props.route)
31
+ ]], universalChildren("nativescript", () => {
32
+ return universalValue(__octaneUniversalPlan0, [props.children]);
33
+ })))]);
122
34
  }, { "module": "@nativescript-community/octane" });
123
35
  //#endregion
124
- export { Popover };
36
+ export { NavLink };