@octane-xplat/ui 0.2.1 → 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 (126) 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 -19
  4. package/dist/native/ui10.js +25 -45
  5. package/dist/native/ui11.js +52 -57
  6. package/dist/native/ui12.js +47 -16
  7. package/dist/native/ui13.js +24 -18
  8. package/dist/native/ui14.js +117 -12
  9. package/dist/native/ui15.js +78 -17
  10. package/dist/native/ui16.js +88 -115
  11. package/dist/native/ui17.js +29 -19
  12. package/dist/native/ui18.js +34 -12
  13. package/dist/native/ui19.js +244 -8
  14. package/dist/native/ui2.js +10 -48
  15. package/dist/native/ui20.js +28 -0
  16. package/dist/native/ui21.js +383 -0
  17. package/dist/native/ui22.js +36 -0
  18. package/dist/native/ui23.js +55 -0
  19. package/dist/native/ui24.js +83 -0
  20. package/dist/native/ui25.js +141 -0
  21. package/dist/native/ui26.js +12 -0
  22. package/dist/native/ui27.js +61 -0
  23. package/dist/native/ui28.js +40 -0
  24. package/dist/native/ui29.js +108 -0
  25. package/dist/native/ui3.js +17 -20
  26. package/dist/native/ui30.js +18 -0
  27. package/dist/native/ui31.js +58 -0
  28. package/dist/native/ui32.js +58 -0
  29. package/dist/native/ui33.js +38 -0
  30. package/dist/native/ui34.js +53 -0
  31. package/dist/native/ui35.js +105 -0
  32. package/dist/native/ui36.js +10 -0
  33. package/dist/native/ui37.js +44 -0
  34. package/dist/native/ui38.js +197 -0
  35. package/dist/native/ui39.js +105 -0
  36. package/dist/native/ui4.js +23 -48
  37. package/dist/native/ui40.js +58 -0
  38. package/dist/native/ui41.js +19 -0
  39. package/dist/native/ui42.js +8 -0
  40. package/dist/native/ui43.js +38 -0
  41. package/dist/native/ui44.js +267 -0
  42. package/dist/native/ui45.js +28 -0
  43. package/dist/native/ui46.js +35 -0
  44. package/dist/native/ui47.js +41 -0
  45. package/dist/native/ui48.js +72 -0
  46. package/dist/native/ui49.js +66 -0
  47. package/dist/native/ui5.js +37 -34
  48. package/dist/native/ui50.js +27 -0
  49. package/dist/native/ui51.js +13 -0
  50. package/dist/native/ui52.js +92 -0
  51. package/dist/native/ui53.js +34 -0
  52. package/dist/native/ui54.js +17 -0
  53. package/dist/native/ui55.js +156 -0
  54. package/dist/native/ui56.js +33 -0
  55. package/dist/native/ui57.js +14 -0
  56. package/dist/native/ui58.js +22 -0
  57. package/dist/native/ui59.js +8 -0
  58. package/dist/native/ui6.js +79 -20
  59. package/dist/native/ui60.js +108 -0
  60. package/dist/native/ui61.js +76 -0
  61. package/dist/native/ui7.js +44 -65
  62. package/dist/native/ui8.js +25 -17
  63. package/dist/native/ui9.js +24 -12
  64. package/dist/web/octane-client-build.json +1 -1
  65. package/dist/web/ui.js +51 -19
  66. package/dist/web/ui10.js +33 -30
  67. package/dist/web/ui11.js +40 -55
  68. package/dist/web/ui12.js +36 -15
  69. package/dist/web/ui13.js +212 -20
  70. package/dist/web/ui14.js +84 -14
  71. package/dist/web/ui15.js +425 -46
  72. package/dist/web/ui16.js +12 -93
  73. package/dist/web/ui17.js +246 -28
  74. package/dist/web/ui18.js +238 -15
  75. package/dist/web/ui19.js +36 -8
  76. package/dist/web/ui2.js +15 -103
  77. package/dist/web/ui20.js +85 -0
  78. package/dist/web/ui21.js +111 -0
  79. package/dist/web/ui22.js +108 -0
  80. package/dist/web/ui23.js +96 -0
  81. package/dist/web/ui24.js +32 -0
  82. package/dist/web/ui25.js +108 -0
  83. package/dist/web/ui26.js +45 -0
  84. package/dist/web/ui27.js +16 -0
  85. package/dist/web/ui28.js +11 -0
  86. package/dist/web/ui29.js +58 -0
  87. package/dist/web/ui3.js +30 -27
  88. package/dist/web/ui30.js +34 -0
  89. package/dist/web/ui31.js +123 -0
  90. package/dist/web/ui32.js +44 -0
  91. package/dist/web/ui33.js +159 -0
  92. package/dist/web/ui34.js +70 -0
  93. package/dist/web/ui35.js +57 -0
  94. package/dist/web/ui36.js +22 -0
  95. package/dist/web/ui37.js +7 -0
  96. package/dist/web/ui38.js +38 -0
  97. package/dist/web/ui39.js +154 -0
  98. package/dist/web/ui4.js +46 -24
  99. package/dist/web/ui40.js +43 -0
  100. package/dist/web/ui41.js +42 -0
  101. package/dist/web/ui42.js +76 -0
  102. package/dist/web/ui43.js +42 -0
  103. package/dist/web/ui44.js +62 -0
  104. package/dist/web/ui45.js +9 -0
  105. package/dist/web/ui46.js +45 -0
  106. package/dist/web/ui47.js +13 -0
  107. package/dist/web/ui48.js +63 -0
  108. package/dist/web/ui49.js +47 -0
  109. package/dist/web/ui5.js +158 -52
  110. package/dist/web/ui50.js +17 -0
  111. package/dist/web/ui51.js +149 -0
  112. package/dist/web/ui52.js +42 -0
  113. package/dist/web/ui53.js +17 -0
  114. package/dist/web/ui54.js +9 -0
  115. package/dist/web/ui55.js +22 -0
  116. package/dist/web/ui56.js +8 -0
  117. package/dist/web/ui57.js +44 -0
  118. package/dist/web/ui58.js +46 -0
  119. package/dist/web/ui6.js +85 -22
  120. package/dist/web/ui7.js +46 -41
  121. package/dist/web/ui8.js +28 -11
  122. package/dist/web/ui9.js +32 -13
  123. package/package.json +18 -6
  124. package/src/theme/tokens.css +444 -47
  125. package/types/index.d.ts +180 -29
  126. package/types/props.d.ts +553 -8
@@ -1,13 +1,249 @@
1
- //#region src/route.native.ts
2
- function routeFor(_stack) {
3
- return null;
1
+ //#region src/route-table.ts
2
+ var EXT = /\.(tsrx|tsx|ts|mts|cts|js|mjs|cjs|jsx)$/;
3
+ var SUFFIX = /\.(web|native|ios|android)$/;
4
+ var PARAM = /^\[(.+)\]$/;
5
+ var PRESENT = /\+(modal|fade|push)$/;
6
+ /** Internal error carrier used by the public `redirect()` helper. Keeping
7
+ * this in the shared route table gives both platform leaves the same guard
8
+ * behavior without importing a platform runtime. */
9
+ var RouteRedirect = class extends Error {
10
+ route;
11
+ constructor(route) {
12
+ super("route redirect");
13
+ this.route = route;
14
+ this.name = "RouteRedirect";
15
+ }
16
+ };
17
+ /** Component pick rule for route/layout modules: default export, then a
18
+ * `screen` named export, then a single function export. Route files use
19
+ * named exports (decision #13) — a lone component is unambiguous.
20
+ * route-config exports are reserved metadata, never the screen. */
21
+ function pick(mod, file) {
22
+ if (typeof mod?.default === "function") return mod.default;
23
+ if (typeof mod?.screen === "function") return mod.screen;
24
+ const fns = Object.keys(mod ?? {}).filter((k) => typeof mod[k] === "function" && ![
25
+ "loader",
26
+ "beforeLoad",
27
+ "head"
28
+ ].includes(k));
29
+ if (fns.length === 1) return mod[fns[0]];
30
+ console.warn("[octane-xplat] route file " + file + (fns.length ? ` has ${fns.length} component-shaped exports (${fns.join(", ")}) — add a default or 'screen' export` : " has no component export") + " — skipped");
4
31
  }
5
- function currentRoute() {
6
- return null;
32
+ function deriveRouteManifest(files, prefer, dir = "app") {
33
+ const prefix = dir.replace(/^\.?\/?/, "").replace(/\/+$/, "") + "/";
34
+ const seen = /* @__PURE__ */ new Map();
35
+ const layoutRank = /* @__PURE__ */ new Map();
36
+ const layouts = {};
37
+ const loaders = {};
38
+ const warned = /* @__PURE__ */ new Set();
39
+ for (const key of Object.keys(files).sort()) {
40
+ let rel = key.replace(/^\.?\//, "");
41
+ if (rel.startsWith(prefix)) rel = rel.slice(prefix.length);
42
+ rel = rel.replace(EXT, "");
43
+ const parts = rel.split("/");
44
+ let base = parts[parts.length - 1];
45
+ const sm = SUFFIX.exec(base);
46
+ const suffix = sm?.[1];
47
+ if (sm) base = base.slice(0, base.length - sm[0].length);
48
+ if (suffix && !prefer.includes(suffix)) continue;
49
+ const rank = suffix ? prefer.indexOf(suffix) : prefer.length;
50
+ const pm = PRESENT.exec(base);
51
+ const presentation = pm?.[1];
52
+ if (pm) base = base.slice(0, base.length - pm[0].length);
53
+ if (base === "_layout") {
54
+ const d = parts.slice(0, -1).join("/");
55
+ const prev = layoutRank.get(d);
56
+ if (prev !== void 0 && prev <= rank) continue;
57
+ const component = pick(files[key], key);
58
+ if (component) {
59
+ layouts[d] = component;
60
+ layoutRank.set(d, rank);
61
+ }
62
+ continue;
63
+ }
64
+ const segs = parts.slice(0, -1).concat(base);
65
+ if (segs[segs.length - 1] === "index") segs.pop();
66
+ const segments = segs.map((s) => {
67
+ const p = PARAM.exec(s);
68
+ return p ? ":" + p[1] : s;
69
+ });
70
+ const name = segments.join("/") || "index";
71
+ const meta = {
72
+ name,
73
+ segments,
74
+ params: segments.filter((s) => s.startsWith(":")).map((s) => s.slice(1)),
75
+ file: key,
76
+ presentation
77
+ };
78
+ const loader = files[key]?.loader;
79
+ if (typeof loader === "function") meta.loader = loader;
80
+ const beforeLoad = files[key]?.beforeLoad;
81
+ if (typeof beforeLoad === "function") meta.beforeLoad = beforeLoad;
82
+ if (files[key]?.head && (typeof files[key].head === "object" || typeof files[key].head === "function")) meta.head = files[key].head;
83
+ const prev = seen.get(name);
84
+ if (prev && prev.rank <= rank) {
85
+ if (prev.rank === rank && !warned.has(name)) {
86
+ warned.add(name);
87
+ console.warn(`[octane-xplat] route '${name}' is defined by both ${prev.meta.file} and ${key} — keeping ${prev.meta.file}`);
88
+ }
89
+ continue;
90
+ }
91
+ const component = pick(files[key], key);
92
+ if (component) seen.set(name, {
93
+ rank,
94
+ meta,
95
+ component
96
+ });
97
+ }
98
+ const screens = {};
99
+ const routes = [];
100
+ for (const { meta, component } of seen.values()) {
101
+ screens[meta.name] = component;
102
+ routes.push(meta);
103
+ if (meta.loader) loaders[meta.name] = meta.loader;
104
+ }
105
+ routes.sort((a, b) => b.segments.reduce((n, s) => n + (s.startsWith(":") ? 1 : 2), 0) - a.segments.reduce((n, s) => n + (s.startsWith(":") ? 1 : 2), 0) || a.name.localeCompare(b.name));
106
+ return {
107
+ screens,
108
+ routes,
109
+ layouts,
110
+ loaders
111
+ };
7
112
  }
8
- function useRoute(_stack) {
113
+ /** Match URL path segments against a manifest — returns the winning meta
114
+ * plus extracted params, or null. First hit wins (routes are pre-sorted
115
+ * by specificity). */
116
+ function matchRoute(routes, segs) {
117
+ for (const meta of routes) {
118
+ if (meta.segments.length !== segs.length) continue;
119
+ const params = {};
120
+ let ok = true;
121
+ for (let i = 0; i < segs.length; i++) {
122
+ const p = meta.segments[i];
123
+ if (p.startsWith(":")) params[p.slice(1)] = decodeRouteParam(decodeURIComponent(segs[i]));
124
+ else if (p !== segs[i]) {
125
+ ok = false;
126
+ break;
127
+ }
128
+ }
129
+ if (ok) return {
130
+ meta,
131
+ params
132
+ };
133
+ }
9
134
  return null;
10
135
  }
11
- function pushRoute(_r) {}
136
+ /** Serialize a Route to a URL path (no origin): manifest routes substitute
137
+ * `:param` segments from params (leftover params → query string);
138
+ * non-manifest names keep the legacy `/<stack>/<name>?params` shape. */
139
+ function buildRoutePath(routes, r) {
140
+ const meta = routes.find((m) => m.name === r.name);
141
+ let segs;
142
+ let rest = r.params;
143
+ if (meta) {
144
+ segs = meta.segments.map((s) => {
145
+ if (!s.startsWith(":")) return s;
146
+ const v = r.params[s.slice(1)];
147
+ if (v === void 0) console.warn(`[octane-xplat] route '${r.name}' pushed without path param ${s}`);
148
+ return encodeURIComponent(encodeRouteParam(v, r.name, s.slice(1)));
149
+ });
150
+ rest = Object.fromEntries(Object.entries(r.params).filter(([k]) => !meta.params.includes(k)));
151
+ } else segs = [r.name];
152
+ const q = Object.entries(rest).map(([k, v]) => encodeURIComponent(k) + "=" + encodeURIComponent(encodeRouteParam(v, r.name, k))).join("&");
153
+ return (((r.stack === "root" ? "" : "/" + r.stack) + "/" + segs.join("/")).replace(/\/+$/, "") || "/") + (q ? "?" + q : "");
154
+ }
155
+ var JSON_PARAM_PREFIX = "json:";
156
+ /** Route codegen exposes string params, but the low-level Route shape remains
157
+ * open for compatibility. Preserve accidental object params on web instead
158
+ * of silently turning them into `[object Object]`; generated APIs still keep
159
+ * callers on scalar strings. */
160
+ function encodeRouteParam(value, routeName, key) {
161
+ if (value === void 0 || value === null || [
162
+ "string",
163
+ "number",
164
+ "boolean"
165
+ ].includes(typeof value)) return String(value ?? "");
166
+ console.warn(`[octane-xplat] route '${routeName}' param '${key}' is non-scalar; JSON-encoding it for the URL`);
167
+ try {
168
+ return JSON_PARAM_PREFIX + JSON.stringify(value);
169
+ } catch {
170
+ console.warn(`[octane-xplat] route '${routeName}' param '${key}' could not be JSON-encoded; using an empty value`);
171
+ return "";
172
+ }
173
+ }
174
+ function decodeRouteParam(value) {
175
+ if (!value.startsWith(JSON_PARAM_PREFIX)) return value;
176
+ try {
177
+ return JSON.parse(value.slice(5));
178
+ } catch {
179
+ return value;
180
+ }
181
+ }
182
+ /** `?a=1&b=2` → params — shared code carries no URLSearchParams
183
+ * (invariant 4: no DOM globals on the native path). */
184
+ function parseQueryString(qs) {
185
+ const params = {};
186
+ if (!qs) return params;
187
+ for (const pair of qs.split("&")) {
188
+ if (!pair) continue;
189
+ const eq = pair.indexOf("=");
190
+ const k = eq === -1 ? pair : pair.slice(0, eq);
191
+ const v = eq === -1 ? "" : pair.slice(eq + 1);
192
+ params[decodeURIComponent(k)] = decodeRouteParam(decodeURIComponent(v));
193
+ }
194
+ return params;
195
+ }
196
+ /** Full URL or path → canonical path for matching. `https://x/a/b?y` →
197
+ * `/a/b?y`; custom-scheme links keep their host as the first segment —
198
+ * `textcoral://post/5` → `/post/5` (the common app-scheme convention). */
199
+ function linkPath(url) {
200
+ const m = /^[a-z][a-z0-9+.-]*:(\/\/)?/i.exec(url);
201
+ let rest = m ? url.slice(m[0].length) : url;
202
+ if (m?.[1] && /^https?:\/\//i.test(m[0])) rest = rest.replace(/^[^/?#]*/, "");
203
+ return "/" + rest.replace(/^\/+/, "");
204
+ }
205
+ /** Match a URL path+query against the manifest into a Route — the shared
206
+ * half of web's URL parse and native's deep-link handling. Whole-path
207
+ * match wins ('/demo/x' → root 'demo/:id'); a non-matching first segment
208
+ * is the stack prefix ('/demos/demo/x' → stack 'demos'). Unmatched names
209
+ * fall through as literal routes for pre-manifest callers. */
210
+ function matchUrl(routes, url) {
211
+ const [p, qs] = url.split("?");
212
+ const segs = p.split("/").filter(Boolean);
213
+ const query = parseQueryString(qs);
214
+ if (!segs.length) return null;
215
+ const finish = (stack, m, fallbackName) => ({
216
+ stack,
217
+ name: m ? m.meta.name : fallbackName,
218
+ params: {
219
+ ...query,
220
+ ...m?.params
221
+ },
222
+ presentation: m?.meta.presentation
223
+ });
224
+ const root = matchRoute(routes, segs);
225
+ if (root) return finish("root", root, segs.join("/"));
226
+ if (segs.length > 1) {
227
+ const named = matchRoute(routes, segs.slice(1));
228
+ if (named) return finish(segs[0], named, segs[1]);
229
+ return finish(segs[0], null, segs[1]);
230
+ }
231
+ return finish("root", null, segs[0]);
232
+ }
233
+ /** Layout components wrapping a route name, outermost → innermost —
234
+ * 'chat/room' picks up `app/chat/_layout.tsrx`, 'a/b/c' chains 'a' then
235
+ * 'a/b'. The '' root layout is the app's own shell (entry renders it)
236
+ * and never joins a chain. Leaves apply the chain at render: native
237
+ * wraps each pushed Page's component, web wraps the outlet's resolved
238
+ * element. */
239
+ function layoutChain(layouts, name) {
240
+ const segs = name.split("/");
241
+ const chain = [];
242
+ for (let i = 1; i < segs.length; i++) {
243
+ const L = layouts[segs.slice(0, i).join("/")];
244
+ if (L) chain.push(L);
245
+ }
246
+ return chain;
247
+ }
12
248
  //#endregion
13
- export { currentRoute, pushRoute, routeFor, useRoute };
249
+ export { RouteRedirect, buildRoutePath, deriveRouteManifest, layoutChain, linkPath, matchRoute, matchUrl, parseQueryString };
@@ -1,49 +1,11 @@
1
- import { defineUniversalComponent, hookSlots, universalPlan, universalValue, useCallback } from "@nativescript-community/octane";
2
- //#region src/View.native.tsrx
3
- var _h$0 = /* @__PURE__ */ Symbol(/* @__PURE__ */ hookSlots(1));
4
- var __octaneUniversalPlan0 = universalPlan("nativescript", {
5
- "kind": "host",
6
- "type": "flexboxlayout",
7
- "props": { "flexDirection": "column" },
8
- "bindings": [
9
- ["id", 0],
10
- ["ref", 1],
11
- ["className", 2],
12
- ["style", 3],
13
- ["onPan", 4],
14
- ["onSwipe", 5]
15
- ],
16
- "children": [{
17
- "kind": "slot",
18
- "slot": 6
19
- }]
20
- });
21
- var PAN_STATE = [
22
- "cancelled",
23
- "began",
24
- "moved",
25
- "ended"
26
- ];
27
- var View = defineUniversalComponent("nativescript", function View(props) {
28
- const onPan = useCallback((e) => props.onPan?.({
29
- x: e.deltaX,
30
- y: e.deltaY,
31
- dx: e.deltaX,
32
- dy: e.deltaY,
33
- vx: 0,
34
- vy: 0,
35
- state: PAN_STATE[e.state] ?? "moved",
36
- target: e.view ?? e.object
37
- }), [props.onPan], _h$0);
38
- return universalValue(__octaneUniversalPlan0, [
39
- props.id,
40
- props.bind,
41
- props.className,
42
- props.style,
43
- props.onPan ? onPan : void 0,
44
- props.onSwipe,
45
- props.children
46
- ]);
47
- }, { "module": "@nativescript-community/octane" });
1
+ //#region src/cx.ts
2
+ /** NS `className` splits on ' ' only, and the universal driver applies the
3
+ * prop via `String(value)` — an array would arrive comma-joined and match
4
+ * no class at all. Native leaves normalize through cx() before handing
5
+ * className to an intrinsic. (The DOM renderer already flattens arrays,
6
+ * so web leaves don't need this.) */
7
+ function cx(...parts) {
8
+ return parts.flat(Infinity).filter(Boolean).join(" ");
9
+ }
48
10
  //#endregion
49
- export { View };
11
+ export { cx };
@@ -0,0 +1,28 @@
1
+ import { defineUniversalComponent, universalComponent, universalPlan, universalProps, universalValue } from "@nativescript-community/octane";
2
+ //#region src/RouteHost.native.tsrx
3
+ var __octaneUniversalPlan0 = universalPlan("nativescript", {
4
+ "kind": "slot",
5
+ "slot": 0
6
+ });
7
+ var __octaneUniversalPlan1 = universalPlan("nativescript", {
8
+ "kind": "slot",
9
+ "slot": 0
10
+ });
11
+ var __octaneUniversalPlan2 = universalPlan("nativescript", {
12
+ "kind": "range",
13
+ "children": [{
14
+ "kind": "slot",
15
+ "slot": 0
16
+ }]
17
+ });
18
+ var RouteHost = defineUniversalComponent("nativescript", function RouteHost(props) {
19
+ const Screen = props.screen;
20
+ let child = universalValue(__octaneUniversalPlan0, [universalComponent("nativescript", Screen, universalProps([["spread", props.params]]))]);
21
+ for (let i = props.layouts.length - 1; i >= 0; i--) {
22
+ const Layout = props.layouts[i];
23
+ child = universalValue(__octaneUniversalPlan1, [universalComponent("nativescript", Layout, universalProps([], child))]);
24
+ }
25
+ return universalValue(__octaneUniversalPlan2, [child]);
26
+ }, { "module": "@nativescript-community/octane" });
27
+ //#endregion
28
+ export { RouteHost };