@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
package/dist/web/ui23.js CHANGED
@@ -1,57 +1,96 @@
1
- import { hookSlots, useRef } from "octane";
2
- //#region src/anim.web.tsrx
3
- var _h$0 = /* @__PURE__ */ Symbol(/* @__PURE__ */ hookSlots(1));
4
- function createAnimated(initial, prop) {
5
- let el = null;
6
- let current = initial;
7
- let raf = 0;
8
- const apply = (v) => {
9
- current = v;
10
- if (el) el.style.transform = `translateX(${v}px)`;
11
- };
12
- const run = (step) => {
13
- cancelAnimationFrame(raf);
14
- const start = Date.now();
15
- const tick = () => {
16
- const v = step(Date.now() - start);
17
- if (v === null) return;
18
- apply(v);
19
- raf = requestAnimationFrame(tick);
20
- };
21
- raf = requestAnimationFrame(tick);
22
- };
23
- return {
24
- get value() {
25
- return current;
26
- },
27
- bind(e) {
28
- el = e;
29
- if (el) el.style.transform = `translateX(${current}px)`;
30
- },
31
- to(target, opts) {
32
- const dur = opts?.duration ?? 300;
33
- const from = current;
34
- run((t) => t >= dur ? (apply(target), null) : from + (target - from) * (t / dur));
35
- },
36
- spring(target, opts) {
37
- const damping = opts?.damping ?? 14, stiffness = opts?.stiffness ?? 120;
38
- let v = 0, x = current;
39
- run(() => {
40
- const dt = 1 / 60;
41
- v += (-stiffness * (x - target) - damping * v) * dt;
42
- x += v * dt;
43
- return Math.abs(v) < .5 && Math.abs(x - target) < .5 ? (apply(target), null) : x;
44
- });
45
- },
46
- stop() {
47
- cancelAnimationFrame(raf);
1
+ import { applyEscapeProps } from "./ui2.js";
2
+ import { glassClassOf, glassStyleOf } from "./ui3.js";
3
+ import { layoutChildProps } from "./ui4.js";
4
+ import { __s, bag8, clone, snapshotSpread, template } from "octane";
5
+ import { bindSignalChild, bindSignalHostPropSources, enableSignalBindings } from "octane/internal/client";
6
+ //#region src/ScrollView.web.tsrx
7
+ enableSignalBindings(1, true);
8
+ var _t$0 = /* @__PURE__ */ template("<div></div>");
9
+ var _rm$1 = [
10
+ "s",
11
+ "f",
12
+ "e"
13
+ ];
14
+ var ScrollView = /* @__PURE__ */ __s(function ScrollView(props, __s, __extra) {
15
+ let _b = __s.slots[0];
16
+ if (_b === void 0) {
17
+ let _m0, _m1, _m2, _m3, _m4, _m5;
18
+ const _root = clone(_t$0);
19
+ _m0 = (e) => applyEscapeProps(e, props);
20
+ _m1 = props.id;
21
+ _m2 = [
22
+ "vx-scroll",
23
+ glassClassOf(props.glass),
24
+ props.className
25
+ ];
26
+ _m3 = snapshotSpread(layoutChildProps(props, {
27
+ ...glassStyleOf(props.glass),
28
+ ...props.horizontal ? { overflowX: "auto" } : { overflowY: "auto" },
29
+ ...props.style
30
+ }));
31
+ _m4 = _root;
32
+ _m5 = bindSignalHostPropSources(__s, void 0, _root, [
33
+ [
34
+ false,
35
+ "ref",
36
+ _m0
37
+ ],
38
+ [
39
+ false,
40
+ "id",
41
+ _m1
42
+ ],
43
+ [
44
+ false,
45
+ "className",
46
+ _m2
47
+ ],
48
+ [true, _m3]
49
+ ], "b:bf7e328f", true);
50
+ _b = bag8(__s, _root, _m0, _m1, _m2, _m3, _m4, _m5, null, void 0);
51
+ __s.refFields = _rm$1;
52
+ } else {
53
+ {
54
+ const _v = (e) => applyEscapeProps(e, props);
55
+ if (_b.a !== _v) _b.a = _v;
48
56
  }
49
- };
50
- }
51
- function useAnimation(initial = 0, prop = "translateX") {
52
- const c = useRef(null, _h$0);
53
- if (!c.current) c.current = createAnimated(initial, prop);
54
- return c.current;
55
- }
57
+ {
58
+ const _v = props.id;
59
+ if (_b.b !== _v) _b.b = _v;
60
+ }
61
+ {
62
+ const _v = [
63
+ "vx-scroll",
64
+ glassClassOf(props.glass),
65
+ props.className
66
+ ];
67
+ if (_b.c !== _v) _b.c = _v;
68
+ }
69
+ _b.d = snapshotSpread(layoutChildProps(props, {
70
+ ...glassStyleOf(props.glass),
71
+ ...props.horizontal ? { overflowX: "auto" } : { overflowY: "auto" },
72
+ ...props.style
73
+ }));
74
+ _b.f = bindSignalHostPropSources(__s, _b.f, _b.e, [
75
+ [
76
+ false,
77
+ "ref",
78
+ _b.a
79
+ ],
80
+ [
81
+ false,
82
+ "id",
83
+ _b.b
84
+ ],
85
+ [
86
+ false,
87
+ "className",
88
+ _b.c
89
+ ],
90
+ [true, _b.d]
91
+ ], "b:bf7e328f", true);
92
+ }
93
+ _b.i = bindSignalChild(__s, _b.i, 1, _b.e, props.children, "b:b1e7e75b", null, void 0, void 0, void 0, true);
94
+ });
56
95
  //#endregion
57
- export { useAnimation };
96
+ export { ScrollView };
package/dist/web/ui24.js CHANGED
@@ -1,16 +1,32 @@
1
- import { hookSlots, useSyncExternalStore } from "octane";
2
- //#region src/theme/colorScheme.web.tsrx
3
- var _h$0 = /* @__PURE__ */ Symbol(/* @__PURE__ */ hookSlots(1));
4
- function getColorScheme() {
5
- return typeof matchMedia === "function" && matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
6
- }
7
- function subscribe(cb) {
8
- const mq = matchMedia("(prefers-color-scheme: dark)");
9
- mq.addEventListener("change", cb);
10
- return () => mq.removeEventListener("change", cb);
11
- }
12
- function useColorScheme() {
13
- return useSyncExternalStore(subscribe, getColorScheme, _h$0);
14
- }
1
+ import { ScrollView } from "./ui23.js";
2
+ import { bag4, child, clone, componentSlotVoid, drainFrag, markChildrenBlock, template } from "octane";
3
+ import { textHoleUpdate } from "octane/internal/client";
4
+ //#region src/ScrollBox.web.tsrx
5
+ var _t$0 = /* @__PURE__ */ template("<!>", 0, 1);
6
+ var __childrenBody = {};
7
+ var ScrollBox = function ScrollBox(props, __s, __extra) {
8
+ const __block = __s.block;
9
+ function __children$1(__props, __s, __extra) {
10
+ const __block = __s.block;
11
+ let _b = __s.slots[0];
12
+ if (_b === void 0) {
13
+ let _m0, _m1;
14
+ const _root = clone(_t$0);
15
+ _m0 = __block.parentNode;
16
+ _m1 = child(_root);
17
+ drainFrag(_root, __block.parentNode, __block.endMarker);
18
+ _b = bag4(__s, null, _m0, _m1, null, void 0);
19
+ }
20
+ {
21
+ const _v = props.children;
22
+ _b.c = textHoleUpdate(__s, 1, _b.a, _v, _b.c, _b.d, _b.b, true, true);
23
+ _b.d = _v;
24
+ }
25
+ }
26
+ componentSlotVoid(__s, 0, __block.parentNode, ScrollView, {
27
+ ...props,
28
+ "children": markChildrenBlock(__children$1, __childrenBody)
29
+ }, __block.endMarker, void 0, void 0, true, void 0, "c:09467f8a");
30
+ };
15
31
  //#endregion
16
- export { getColorScheme, useColorScheme };
32
+ export { ScrollBox };
package/dist/web/ui25.js CHANGED
@@ -1,22 +1,108 @@
1
- import { componentSlot } from "octane";
2
- //#region src/styled.web.tsrx
3
- function styled(Base, def) {
4
- function Styled(props, __s, __extra) {
5
- const __block = __s.block;
6
- const cls = [def.base];
7
- const rest = { ...props };
8
- for (const k of Object.keys(def.variants ?? {})) {
9
- if (props[k]) cls.push(def.variants[k]);
10
- delete rest[k];
1
+ import { applyEscapeProps } from "./ui2.js";
2
+ import { layoutChildProps } from "./ui4.js";
3
+ import { __s, bag8, clone, snapshotSpread, template } from "octane";
4
+ import { bindSignalHostPropSources, enableSignalBindings } from "octane/internal/client";
5
+ //#region src/Image.web.tsrx
6
+ enableSignalBindings(1, true);
7
+ var _t$0 = /* @__PURE__ */ template("<img/>");
8
+ var _rm$0 = [
9
+ "s",
10
+ "h",
11
+ "g"
12
+ ];
13
+ var Image = /* @__PURE__ */ __s(function Image(props, __s, __extra) {
14
+ let _b = __s.slots[0];
15
+ if (_b === void 0) {
16
+ let _m0, _m1, _m2, _m3, _m4, _m5, _m6, _m7;
17
+ const _root = clone(_t$0);
18
+ _m0 = (e) => applyEscapeProps(e, props);
19
+ _m1 = props.id;
20
+ _m2 = props.className;
21
+ _m3 = snapshotSpread(layoutChildProps(props));
22
+ _m4 = props.src;
23
+ _m5 = props.alt;
24
+ _m6 = _root;
25
+ _m7 = bindSignalHostPropSources(__s, void 0, _root, [
26
+ [
27
+ false,
28
+ "ref",
29
+ _m0
30
+ ],
31
+ [
32
+ false,
33
+ "id",
34
+ _m1
35
+ ],
36
+ [
37
+ false,
38
+ "className",
39
+ _m2
40
+ ],
41
+ [true, _m3],
42
+ [
43
+ false,
44
+ "src",
45
+ _m4
46
+ ],
47
+ [
48
+ false,
49
+ "alt",
50
+ _m5
51
+ ]
52
+ ], "b:ed5f7791", false);
53
+ _b = bag8(__s, _root, _m0, _m1, _m2, _m3, _m4, _m5, _m6, _m7);
54
+ __s.refFields = _rm$0;
55
+ } else {
56
+ {
57
+ const _v = (e) => applyEscapeProps(e, props);
58
+ if (_b.a !== _v) _b.a = _v;
11
59
  }
12
- if (props.className) cls.push(props.className);
13
- if (__s.block.pending && !__s.block.crossRenderUpdate) return;
14
- componentSlot(__s, 0, __block.parentNode, Base, {
15
- ...rest,
16
- "className": cls
17
- }, __block.endMarker, void 0, void 0, true, void 0, "c:5d3d2b58");
60
+ {
61
+ const _v = props.id;
62
+ if (_b.b !== _v) _b.b = _v;
63
+ }
64
+ {
65
+ const _v = props.className;
66
+ if (_b.c !== _v) _b.c = _v;
67
+ }
68
+ _b.d = snapshotSpread(layoutChildProps(props));
69
+ {
70
+ const _v = props.src;
71
+ if (_b.e !== _v) _b.e = _v;
72
+ }
73
+ {
74
+ const _v = props.alt;
75
+ if (_b.f !== _v) _b.f = _v;
76
+ }
77
+ _b.h = bindSignalHostPropSources(__s, _b.h, _b.g, [
78
+ [
79
+ false,
80
+ "ref",
81
+ _b.a
82
+ ],
83
+ [
84
+ false,
85
+ "id",
86
+ _b.b
87
+ ],
88
+ [
89
+ false,
90
+ "className",
91
+ _b.c
92
+ ],
93
+ [true, _b.d],
94
+ [
95
+ false,
96
+ "src",
97
+ _b.e
98
+ ],
99
+ [
100
+ false,
101
+ "alt",
102
+ _b.f
103
+ ]
104
+ ], "b:ed5f7791", false);
18
105
  }
19
- return Styled;
20
- }
106
+ });
21
107
  //#endregion
22
- export { styled };
108
+ export { Image };
package/dist/web/ui26.js CHANGED
@@ -1,39 +1,45 @@
1
- import { applyEscapeProps } from "./ui2.js";
2
- import { __s, bag6, clone, queueRefAttach, queueRefDetach, setStyle, template } from "octane";
3
- import { childTextHoleUpdate, replaceRef, updateFreshClassName } from "octane/internal/client";
4
- //#region src/Screen.web.tsrx
5
- var _t$0 = /* @__PURE__ */ template("<div></div>");
1
+ import { __s, bag5, clone, createElement, delegateEvents, hookSlots, queueRefAttach, queueRefDetach, setEventHandler, template, useEffect, useMemo, useRef } from "octane";
2
+ import { bindSignalChild, enableSignalBindings, replaceRef, setClassNameIfChanged } from "octane/internal/client";
3
+ //#region src/Modal.web.tsrx
4
+ enableSignalBindings(1, true);
5
+ delegateEvents(["close"]);
6
+ var _t$0 = /* @__PURE__ */ template("<dialog></dialog>");
7
+ var _hs$ = /* @__PURE__ */ hookSlots(3);
6
8
  var _rm$1 = [
7
9
  "r",
8
10
  "b",
9
11
  "a"
10
12
  ];
11
- var Screen = /* @__PURE__ */ __s(function Screen(props, __s, __extra) {
13
+ var Modal = /* @__PURE__ */ __s(function Modal(props, __s, __extra) {
14
+ const el = useRef(null, _hs$ + 0);
15
+ const content = useMemo(() => props.component ? createElement(props.component, props.params ?? {}) : props.children, [props], _hs$ + 1);
16
+ const presentation = props.presentation;
17
+ const className = presentation === "sheet" ? "vx-modal vx-modal--sheet" : presentation === "dialog" ? "vx-modal vx-modal--dialog" : presentation === "fullscreen" || !presentation && props.fullscreen ? "vx-modal vx-modal--fullscreen" : "vx-modal";
18
+ useEffect(() => {
19
+ const d = el.current;
20
+ if (!d) return;
21
+ if (props.open && !d.open) d.showModal();
22
+ else if (!props.open && d.open) d.close();
23
+ }, [props.open], _hs$ + 2);
24
+ if (__s.block.pending && !__s.block.crossRenderUpdate) return;
12
25
  let _b = __s.slots[0];
13
26
  if (_b === void 0) {
14
27
  let _m0, _m1;
15
28
  const _root = clone(_t$0);
16
- queueRefAttach(__s, _m1 = (e) => applyEscapeProps(e, props), _m0 = _root);
29
+ queueRefAttach(__s, _m1 = el, _m0 = _root);
17
30
  (__s.cleanups ??= []).push(() => _b && queueRefDetach(_b.b, _b.a));
18
- _b = bag6(__s, _root, _m0, _m1, void 0, void 0, null, void 0);
31
+ setEventHandler(_root, "$$close", () => props.onClose?.());
32
+ _b = bag5(__s, _root, _m0, _m1, void 0, null, void 0);
19
33
  __s.refFields = _rm$1;
20
34
  } else {
21
- const _r = (e) => applyEscapeProps(e, props);
22
- if (_r !== _b.b) _b.b = replaceRef(__s, _b.b, _r, _b.a);
23
- }
24
- _b.c = updateFreshClassName(["vx-screen", props.className], _b.c, _b.a);
25
- {
26
- const _v = props.style;
27
- if (_b.d !== _v) {
28
- setStyle(_b.a, _v, _b.d);
29
- _b.d = _v;
35
+ {
36
+ const _r = el;
37
+ if (_r !== _b.b) _b.b = replaceRef(__s, _b.b, _r, _b.a);
30
38
  }
39
+ setEventHandler(_b.a, "$$close", () => props.onClose?.());
31
40
  }
32
- {
33
- const _v = props.children;
34
- _b.e = childTextHoleUpdate(__s, 1, _b.a, _v, _b.e, _b.f);
35
- _b.f = _v;
36
- }
41
+ _b.c = setClassNameIfChanged(className, _b.c, _b.a);
42
+ _b.f = bindSignalChild(__s, _b.f, 1, _b.a, content, "b:44cec141", null, void 0, void 0, void 0, true);
37
43
  });
38
44
  //#endregion
39
- export { Screen };
45
+ export { Modal };
package/dist/web/ui27.js CHANGED
@@ -1,127 +1,16 @@
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
- /** Component pick rule for route/layout modules: default export, then a
6
- * `screen` named export, then a single function export. Route files use
7
- * named exports (decision #13) — a lone component is unambiguous. */
8
- function pick(mod, file) {
9
- if (typeof mod?.default === "function") return mod.default;
10
- if (typeof mod?.screen === "function") return mod.screen;
11
- const fns = Object.keys(mod ?? {}).filter((k) => typeof mod[k] === "function");
12
- if (fns.length === 1) return mod[fns[0]];
13
- 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");
1
+ import { hookSlots, useSyncExternalStore } from "octane";
2
+ //#region src/theme/colorScheme.web.ts
3
+ function getColorScheme() {
4
+ return typeof matchMedia === "function" && matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
14
5
  }
15
- function deriveRouteManifest(files, prefer, dir = "app") {
16
- const prefix = dir.replace(/^\.?\/?/, "").replace(/\/+$/, "") + "/";
17
- const seen = /* @__PURE__ */ new Map();
18
- const layoutRank = /* @__PURE__ */ new Map();
19
- const layouts = {};
20
- const warned = /* @__PURE__ */ new Set();
21
- for (const key of Object.keys(files).sort()) {
22
- let rel = key.replace(/^\.?\//, "");
23
- if (rel.startsWith(prefix)) rel = rel.slice(prefix.length);
24
- rel = rel.replace(EXT, "");
25
- const parts = rel.split("/");
26
- let base = parts[parts.length - 1];
27
- const sm = SUFFIX.exec(base);
28
- const suffix = sm?.[1];
29
- if (sm) base = base.slice(0, base.length - sm[0].length);
30
- if (suffix && !prefer.includes(suffix)) continue;
31
- const rank = suffix ? prefer.indexOf(suffix) : prefer.length;
32
- if (base === "_layout") {
33
- const d = parts.slice(0, -1).join("/");
34
- const prev = layoutRank.get(d);
35
- if (prev !== void 0 && prev <= rank) continue;
36
- const component = pick(files[key], key);
37
- if (component) {
38
- layouts[d] = component;
39
- layoutRank.set(d, rank);
40
- }
41
- continue;
42
- }
43
- const segs = parts.slice(0, -1).concat(base);
44
- if (segs[segs.length - 1] === "index") segs.pop();
45
- const segments = segs.map((s) => {
46
- const p = PARAM.exec(s);
47
- return p ? ":" + p[1] : s;
48
- });
49
- const name = segments.join("/") || "index";
50
- const meta = {
51
- name,
52
- segments,
53
- params: segments.filter((s) => s.startsWith(":")).map((s) => s.slice(1)),
54
- file: key
55
- };
56
- const prev = seen.get(name);
57
- if (prev && prev.rank <= rank) {
58
- if (prev.rank === rank && !warned.has(name)) {
59
- warned.add(name);
60
- console.warn(`[octane-xplat] route '${name}' is defined by both ${prev.meta.file} and ${key} — keeping ${prev.meta.file}`);
61
- }
62
- continue;
63
- }
64
- const component = pick(files[key], key);
65
- if (component) seen.set(name, {
66
- rank,
67
- meta,
68
- component
69
- });
70
- }
71
- const screens = {};
72
- const routes = [];
73
- for (const { meta, component } of seen.values()) {
74
- screens[meta.name] = component;
75
- routes.push(meta);
76
- }
77
- 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));
78
- return {
79
- screens,
80
- routes,
81
- layouts
82
- };
6
+ function subscribeSystemScheme(cb) {
7
+ const mq = matchMedia("(prefers-color-scheme: dark)");
8
+ mq.addEventListener("change", cb);
9
+ return () => mq.removeEventListener("change", cb);
83
10
  }
84
- /** Match URL path segments against a manifest — returns the winning meta
85
- * plus extracted params, or null. First hit wins (routes are pre-sorted
86
- * by specificity). */
87
- function matchRoute(routes, segs) {
88
- for (const meta of routes) {
89
- if (meta.segments.length !== segs.length) continue;
90
- const params = {};
91
- let ok = true;
92
- for (let i = 0; i < segs.length; i++) {
93
- const p = meta.segments[i];
94
- if (p.startsWith(":")) params[p.slice(1)] = decodeURIComponent(segs[i]);
95
- else if (p !== segs[i]) {
96
- ok = false;
97
- break;
98
- }
99
- }
100
- if (ok) return {
101
- meta,
102
- params
103
- };
104
- }
105
- return null;
106
- }
107
- /** Serialize a Route to a URL path (no origin): manifest routes substitute
108
- * `:param` segments from params (leftover params → query string);
109
- * non-manifest names keep the legacy `/<stack>/<name>?params` shape. */
110
- function buildRoutePath(routes, r) {
111
- const meta = routes.find((m) => m.name === r.name);
112
- let segs;
113
- let rest = r.params;
114
- if (meta) {
115
- segs = meta.segments.map((s) => {
116
- if (!s.startsWith(":")) return s;
117
- const v = r.params[s.slice(1)];
118
- if (v === void 0) console.warn(`[octane-xplat] route '${r.name}' pushed without path param ${s}`);
119
- return encodeURIComponent(String(v ?? ""));
120
- });
121
- rest = Object.fromEntries(Object.entries(r.params).filter(([k]) => !meta.params.includes(k)));
122
- } else segs = [r.name];
123
- const q = Object.entries(rest).map(([k, v]) => encodeURIComponent(k) + "=" + encodeURIComponent(String(v))).join("&");
124
- return (((r.stack === "root" ? "" : "/" + r.stack) + "/" + segs.join("/")).replace(/\/+$/, "") || "/") + (q ? "?" + q : "");
11
+ function useColorScheme() {
12
+ return useSyncExternalStore(subscribeSystemScheme, getColorScheme, _h$0);
125
13
  }
14
+ var _h$0 = /* @__PURE__ */ Symbol(/* @__PURE__ */ hookSlots(1));
126
15
  //#endregion
127
- export { buildRoutePath, deriveRouteManifest, matchRoute };
16
+ export { getColorScheme, subscribeSystemScheme, useColorScheme };
package/dist/web/ui28.js CHANGED
@@ -1,112 +1,11 @@
1
- import { buildRoutePath, matchRoute } from "./ui27.js";
2
- import { hookSlots, useSyncExternalStore } from "octane";
3
- //#region src/route.web.ts
4
- var screens = {};
5
- var routes = [];
6
- /** Register the app's name → screen table. On web the table feeds
7
- * `screenFor` — the fallback outlet resolution in Tabs when no
8
- * `resolveScreen` prop is given; on native `pushRoute` resolves
9
- * `route.name` through it. `manifest` (from deriveRouteManifest) enables
10
- * path-param matching — call once from the shared routes module. */
11
- function registerScreens(table, manifest) {
12
- screens = table;
13
- routes = manifest ?? [];
14
- if (current !== void 0) {
15
- current = parse();
16
- emit();
17
- }
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(" ");
18
9
  }
19
- /** One-call registration for route-dir apps — screens + URL patterns +
20
- * layouts all come from deriveRouteManifest. */
21
- function registerRoutes(manifest) {
22
- registerScreens(manifest.screens, manifest.routes);
23
- }
24
- function screenFor(name) {
25
- return screens[name];
26
- }
27
- var listeners = /* @__PURE__ */ new Set();
28
- var current;
29
- function queryParams() {
30
- const params = {};
31
- new URLSearchParams(location.search).forEach((v, k) => params[k] = v);
32
- return params;
33
- }
34
- function parse() {
35
- const segs = location.pathname.split("/").filter(Boolean);
36
- if (!segs.length) return null;
37
- const query = queryParams();
38
- const root = matchRoute(routes, segs);
39
- if (root) return {
40
- stack: "root",
41
- name: root.meta.name,
42
- params: {
43
- ...query,
44
- ...root.params
45
- }
46
- };
47
- if (segs.length > 1) {
48
- const named = matchRoute(routes, segs.slice(1));
49
- if (named) return {
50
- stack: segs[0],
51
- name: named.meta.name,
52
- params: {
53
- ...query,
54
- ...named.params
55
- }
56
- };
57
- return {
58
- stack: segs[0],
59
- name: segs[1],
60
- params: query
61
- };
62
- }
63
- return {
64
- stack: "root",
65
- name: segs[0],
66
- params: query
67
- };
68
- }
69
- function read() {
70
- if (current === void 0) current = parse();
71
- return current;
72
- }
73
- function emit() {
74
- listeners.forEach((l) => l());
75
- }
76
- function pushRoute(r) {
77
- history.pushState(null, "", buildRoutePath(routes, r));
78
- current = r;
79
- emit();
80
- }
81
- /** Web history is one linear stack — back pops whatever route is current;
82
- * per-stack pops aren't expressible, so `stack` is accepted for parity
83
- * and ignored. */
84
- function popRoute(_stack = "root") {
85
- history.back();
86
- }
87
- window.addEventListener("popstate", () => {
88
- current = parse();
89
- emit();
90
- });
91
- /** Current route if it targets `stack`, else null. */
92
- function routeFor(stack) {
93
- const c = read();
94
- return c && c.stack === stack ? c : null;
95
- }
96
- /** The route active at boot — for deep-link tab selection. */
97
- function currentRoute() {
98
- return read();
99
- }
100
- /** URL for a Route — Link's href and shareable-path helper. */
101
- function hrefFor(r) {
102
- return buildRoutePath(routes, r);
103
- }
104
- function useRoute(stack) {
105
- return useSyncExternalStore((cb) => {
106
- listeners.add(cb);
107
- return () => listeners.delete(cb);
108
- }, () => routeFor(stack), _h$0);
109
- }
110
- var _h$0 = /* @__PURE__ */ Symbol(/* @__PURE__ */ hookSlots(1));
111
10
  //#endregion
112
- export { currentRoute, hrefFor, popRoute, pushRoute, registerRoutes, registerScreens, routeFor, screenFor, useRoute };
11
+ export { cx };