lecodes-cli 0.19.0 → 0.19.2
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/index.js +730 -235
- package/package.json +14 -13
- package/runtime/scene-harness.json +1 -1
- package/runtime/sdk-types.json +1 -1
- package/src/api.ts +302 -0
- package/src/browserAuth.ts +87 -0
- package/src/cmgenTool.ts +104 -0
- package/src/commands/app.ts +891 -0
- package/src/commands/appAndroid.ts +603 -0
- package/src/commands/appDesktop.ts +326 -0
- package/src/commands/appDesktopMac.ts +470 -0
- package/src/commands/appIcon.ts +187 -0
- package/src/commands/appShared.ts +448 -0
- package/src/commands/appTemplates.ts +883 -0
- package/src/commands/appTemplatesAndroid.ts +599 -0
- package/src/commands/appTemplatesGradlew.ts +9 -0
- package/src/commands/assets.ts +28 -0
- package/src/commands/clone.ts +60 -0
- package/src/commands/compile.ts +253 -0
- package/src/commands/create.ts +59 -0
- package/src/commands/design.ts +863 -0
- package/src/commands/designTemplates.ts +11 -0
- package/src/commands/desktop.ts +122 -0
- package/src/commands/dev.ts +214 -0
- package/src/commands/diff.ts +59 -0
- package/src/commands/init.ts +191 -0
- package/src/commands/install.ts +198 -0
- package/src/commands/lightmap.ts +290 -0
- package/src/commands/link.ts +147 -0
- package/src/commands/login.ts +24 -0
- package/src/commands/navmesh.ts +225 -0
- package/src/commands/pn.ts +305 -0
- package/src/commands/projectTemplates.ts +129 -0
- package/src/commands/pull.ts +109 -0
- package/src/commands/push.ts +173 -0
- package/src/commands/render.ts +414 -0
- package/src/commands/scene.ts +148 -0
- package/src/commands/shaders.ts +157 -0
- package/src/commands/status.ts +29 -0
- package/src/commands/test.ts +370 -0
- package/src/commands/thumbs.ts +178 -0
- package/src/commands/types.ts +87 -0
- package/src/commands/update.ts +190 -0
- package/src/compile/assetIcons.ts +214 -0
- package/src/compile/collect.ts +46 -0
- package/src/compile/collectLocal.ts +37 -0
- package/src/compile/designCompile.ts +104 -0
- package/src/compile/fonts.ts +156 -0
- package/src/compile/headlessBundle.ts +129 -0
- package/src/compile/nativeStack.ts +21 -0
- package/src/compile/projectCompile.ts +109 -0
- package/src/compile/sceneCompile.ts +113 -0
- package/src/compile/screenEntry.ts +127 -0
- package/src/compile/shaders.ts +245 -0
- package/src/config.ts +42 -0
- package/src/designMeta.ts +35 -0
- package/src/desktopRenderer.ts +532 -0
- package/src/desktopScript.ts +276 -0
- package/src/dev/clientTemplates.ts +160 -0
- package/src/dev/devServer.ts +292 -0
- package/src/dev/wsServer.ts +144 -0
- package/src/distRoot.ts +20 -0
- package/src/ignore.ts +163 -0
- package/src/index.ts +491 -0
- package/src/lecodes-3d-editor.d.ts +41 -0
- package/src/lecodes-assets.d.ts +7 -0
- package/src/lecodes-design.d.ts +191 -0
- package/src/lecodes-renderer.d.ts +131 -0
- package/src/localFiles.ts +144 -0
- package/src/manifest.ts +46 -0
- package/src/matcTool.ts +137 -0
- package/src/peers.ts +40 -0
- package/src/project.ts +61 -0
- package/src/projectEnv.ts +94 -0
- package/src/qrcode-terminal.d.ts +9 -0
- package/src/releases.ts +125 -0
- package/src/serverDiff.ts +78 -0
- package/src/textDiff.ts +103 -0
- package/src/types.ts +0 -0
- package/src/util.ts +146 -0
- package/runtime/sdk/animate/animate.ts +0 -238
- package/runtime/sdk/animate/bezier.ts +0 -138
- package/runtime/sdk/animate/easings.ts +0 -126
- package/runtime/sdk/canvas/Canvas.ts +0 -305
- package/runtime/sdk/compile/aspectMacro.ts +0 -86
- package/runtime/sdk/compile/assetIconMacro.ts +0 -384
- package/runtime/sdk/compile/assetMacro.ts +0 -146
- package/runtime/sdk/compile/assetName.ts +0 -50
- package/runtime/sdk/compile/bundler.ts +0 -287
- package/runtime/sdk/compile/compileProject.ts +0 -129
- package/runtime/sdk/compile/detectEntry.ts +0 -128
- package/runtime/sdk/compile/fontMacro.ts +0 -459
- package/runtime/sdk/compile/fontRegistry.ts +0 -78
- package/runtime/sdk/compile/header.ts +0 -67
- package/runtime/sdk/compile/index.ts +0 -100
- package/runtime/sdk/compile/libraryImports.ts +0 -52
- package/runtime/sdk/compile/liteMaterial.ts +0 -247
- package/runtime/sdk/compile/sceneEditor.ts +0 -88
- package/runtime/sdk/compile/serverSplit.ts +0 -233
- package/runtime/sdk/compile/serverTypes.ts +0 -227
- package/runtime/sdk/compile/sfnt.ts +0 -98
- package/runtime/sdk/compile/shaderSchema.ts +0 -202
- package/runtime/sdk/compile/shaderTargets.ts +0 -81
- package/runtime/sdk/compile/sourcemap.ts +0 -25
- package/runtime/sdk/core/Aspect.ts +0 -568
- package/runtime/sdk/core/InspectorUI.ts +0 -212
- package/runtime/sdk/core/StateMachine.ts +0 -308
- package/runtime/sdk/core/color.ts +0 -66
- package/runtime/sdk/core/compWrite.ts +0 -42
- package/runtime/sdk/core/events.ts +0 -38
- package/runtime/sdk/core/fields.ts +0 -120
- package/runtime/sdk/core/registry.ts +0 -23
- package/runtime/sdk/core/signals.ts +0 -277
- package/runtime/sdk/core/time.ts +0 -84
- package/runtime/sdk/g2/Camera2D.ts +0 -40
- package/runtime/sdk/g2/CharacterController2D.ts +0 -276
- package/runtime/sdk/g2/Node2D.ts +0 -267
- package/runtime/sdk/g2/OneWay2D.ts +0 -66
- package/runtime/sdk/g2/Physics2D.ts +0 -346
- package/runtime/sdk/g2/Scene2D.ts +0 -209
- package/runtime/sdk/g2/Shape2D.ts +0 -259
- package/runtime/sdk/g2/Sprite.ts +0 -89
- package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
- package/runtime/sdk/g2/SpriteSheet.ts +0 -166
- package/runtime/sdk/g2/Texture2D.ts +0 -47
- package/runtime/sdk/g2/Tilemap.ts +0 -41
- package/runtime/sdk/g2/Tileset.ts +0 -71
- package/runtime/sdk/g2/Trigger2D.ts +0 -77
- package/runtime/sdk/g2/autotile.ts +0 -433
- package/runtime/sdk/g2/cells.ts +0 -91
- package/runtime/sdk/g2/defineScene2d.ts +0 -381
- package/runtime/sdk/g2/groups2d.ts +0 -106
- package/runtime/sdk/g2/loop.ts +0 -50
- package/runtime/sdk/g2/scenarios2d.ts +0 -69
- package/runtime/sdk/g2/touch.ts +0 -83
- package/runtime/sdk/gl/Camera.ts +0 -160
- package/runtime/sdk/gl/CameraPlace.ts +0 -52
- package/runtime/sdk/gl/CharacterController.ts +0 -238
- package/runtime/sdk/gl/Gearbox.ts +0 -212
- package/runtime/sdk/gl/Geometry.ts +0 -279
- package/runtime/sdk/gl/IK.ts +0 -193
- package/runtime/sdk/gl/InstancedMesh.ts +0 -132
- package/runtime/sdk/gl/Light.ts +0 -107
- package/runtime/sdk/gl/Lightmap.ts +0 -256
- package/runtime/sdk/gl/Material.ts +0 -260
- package/runtime/sdk/gl/Mesh.ts +0 -83
- package/runtime/sdk/gl/Model.ts +0 -124
- package/runtime/sdk/gl/NavAgent.ts +0 -337
- package/runtime/sdk/gl/NavMesh.ts +0 -397
- package/runtime/sdk/gl/Node.ts +0 -350
- package/runtime/sdk/gl/Noise.ts +0 -30
- package/runtime/sdk/gl/Particles.ts +0 -676
- package/runtime/sdk/gl/Physics.ts +0 -222
- package/runtime/sdk/gl/Plane.ts +0 -53
- package/runtime/sdk/gl/Ragdoll.ts +0 -270
- package/runtime/sdk/gl/Ray.ts +0 -16
- package/runtime/sdk/gl/Scene.ts +0 -486
- package/runtime/sdk/gl/Shape.ts +0 -409
- package/runtime/sdk/gl/Terrain.ts +0 -1092
- package/runtime/sdk/gl/Texture.ts +0 -63
- package/runtime/sdk/gl/Trigger.ts +0 -45
- package/runtime/sdk/gl/Vehicle.ts +0 -473
- package/runtime/sdk/gl/Wheel.ts +0 -240
- package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
- package/runtime/sdk/gl/animation/Animator.ts +0 -120
- package/runtime/sdk/gl/animation/Layer.ts +0 -31
- package/runtime/sdk/gl/animation/Loop.ts +0 -30
- package/runtime/sdk/gl/animation/Playback.ts +0 -44
- package/runtime/sdk/gl/animation/core.ts +0 -336
- package/runtime/sdk/gl/controls.ts +0 -95
- package/runtime/sdk/gl/physicsEvents.ts +0 -20
- package/runtime/sdk/gl/scenarios.ts +0 -291
- package/runtime/sdk/gl/state.ts +0 -6
- package/runtime/sdk/gl/terrainMesh.ts +0 -219
- package/runtime/sdk/gl/touch.ts +0 -68
- package/runtime/sdk/inject.ts +0 -197
- package/runtime/sdk/math/Mathf.ts +0 -118
- package/runtime/sdk/math/mat4.ts +0 -278
- package/runtime/sdk/math/quat.ts +0 -232
- package/runtime/sdk/math/vec.ts +0 -255
- package/runtime/sdk/net/codec.ts +0 -119
- package/runtime/sdk/net/core.ts +0 -384
- package/runtime/sdk/net/index.ts +0 -181
- package/runtime/sdk/net/replication.ts +0 -622
- package/runtime/sdk/plugins/camera.ts +0 -81
- package/runtime/sdk/plugins/geolocation.ts +0 -123
- package/runtime/sdk/plugins/oauth.ts +0 -61
- package/runtime/sdk/plugins/permission.ts +0 -7
- package/runtime/sdk/plugins/push.ts +0 -132
- package/runtime/sdk/plugins/qr.ts +0 -73
- package/runtime/sdk/plugins/service.ts +0 -47
- package/runtime/sdk/runtime/app.ts +0 -101
- package/runtime/sdk/runtime/appEvents.ts +0 -54
- package/runtime/sdk/runtime/channel.ts +0 -61
- package/runtime/sdk/runtime/clipboard.ts +0 -20
- package/runtime/sdk/runtime/datetime.ts +0 -329
- package/runtime/sdk/runtime/device.ts +0 -293
- package/runtime/sdk/runtime/fetch.ts +0 -77
- package/runtime/sdk/runtime/files.ts +0 -23
- package/runtime/sdk/runtime/input.ts +0 -175
- package/runtime/sdk/runtime/media.ts +0 -111
- package/runtime/sdk/runtime/misc.ts +0 -16
- package/runtime/sdk/runtime/net.ts +0 -36
- package/runtime/sdk/runtime/rpc.ts +0 -218
- package/runtime/sdk/runtime/service.ts +0 -83
- package/runtime/sdk/runtime/share.ts +0 -9
- package/runtime/sdk/runtime/storage.ts +0 -13
- package/runtime/sdk/runtime/touch.ts +0 -76
- package/runtime/sdk/scene/defineScene.ts +0 -1353
- package/runtime/sdk/scene/editorPlugins.ts +0 -110
- package/runtime/sdk/scene/gizmos.ts +0 -148
- package/runtime/sdk/scene/grammar.ts +0 -120
- package/runtime/sdk/scene/material.ts +0 -188
- package/runtime/sdk/server/auth/appConfig.ts +0 -12
- package/runtime/sdk/server/auth/global.ts +0 -80
- package/runtime/sdk/server/auth/host.ts +0 -318
- package/runtime/sdk/server/auth/models.ts +0 -83
- package/runtime/sdk/server/auth/types.ts +0 -50
- package/runtime/sdk/server/channel.ts +0 -56
- package/runtime/sdk/server/context.ts +0 -36
- package/runtime/sdk/server/db/defineDb.ts +0 -237
- package/runtime/sdk/server/db/fields.ts +0 -132
- package/runtime/sdk/server/db/httpTransport.ts +0 -93
- package/runtime/sdk/server/db/index.ts +0 -7
- package/runtime/sdk/server/db/marci/query.ts +0 -412
- package/runtime/sdk/server/db/types.ts +0 -202
- package/runtime/sdk/server/errors.ts +0 -12
- package/runtime/sdk/server/host.ts +0 -74
- package/runtime/sdk/server/inject.ts +0 -13
- package/runtime/sdk/server/runtime.ts +0 -133
- package/runtime/sdk/server/validate.ts +0 -87
- package/runtime/sdk/ui/NativeView.ts +0 -142
- package/runtime/sdk/ui/UI.ts +0 -39
- package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
- package/runtime/sdk/ui/UIButton.ts +0 -101
- package/runtime/sdk/ui/UIContainer.ts +0 -60
- package/runtime/sdk/ui/UIImage.ts +0 -83
- package/runtime/sdk/ui/UIInput.ts +0 -185
- package/runtime/sdk/ui/UIModal.ts +0 -139
- package/runtime/sdk/ui/UINode.ts +0 -830
- package/runtime/sdk/ui/UIPager.ts +0 -362
- package/runtime/sdk/ui/UIPopover.ts +0 -100
- package/runtime/sdk/ui/UIScreen.ts +0 -123
- package/runtime/sdk/ui/UIScrollable.ts +0 -87
- package/runtime/sdk/ui/UISpacer.ts +0 -14
- package/runtime/sdk/ui/UITabs.ts +0 -236
- package/runtime/sdk/ui/UIText.ts +0 -51
- package/runtime/sdk/ui/UIVideo.ts +0 -88
- package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
- package/runtime/sdk/ui/UIWidget.ts +0 -127
- package/runtime/sdk/ui/fonts.ts +0 -13
- package/runtime/sdk/ui/presentable.ts +0 -117
- package/runtime/sdk/ui/router.ts +0 -132
- package/runtime/sdk/ui/theme.ts +0 -84
|
@@ -1,362 +0,0 @@
|
|
|
1
|
-
import { ContainerElement, type BaseStyle, type ChildrenFn, type DrawableStyle, type ElementStyle, type UIElementBase, type UINodeChild } from "./UINode"
|
|
2
|
-
import { disposeBinding } from "../core/signals"
|
|
3
|
-
import type { UIScreen } from "./UIScreen"
|
|
4
|
-
|
|
5
|
-
// UIPager — the system's ONE screen-navigation element (docs/pager-component-plan.md): sibling
|
|
6
|
-
// tabs that swipe natively side to side, with a native navigation STACK per tab. `push` opens a
|
|
7
|
-
// screen on top of the currently open tab (within the pager); `pop` / the interactive edge
|
|
8
|
-
// back-swipe unwinds it; `replace` swaps the top. A pager with one tab is a plain stack.
|
|
9
|
-
//
|
|
10
|
-
// Reachable from any scope as a global: `UIPager.push(screen)` pushes onto the pager that owns
|
|
11
|
-
// the current screen (UIPager.current) — no reference threading into child screens. Router stays
|
|
12
|
-
// distinct: `Router.push` opens ON TOP of the pager (bars and all), the pager navigates within.
|
|
13
|
-
//
|
|
14
|
-
// Wire node type "pager": children are separate layout roots (full UIScreens), NOT yoga children.
|
|
15
|
-
// The wire shape is `children` flat in TAB-MAJOR order (tab 0's stack root→top, then tab 1's, …)
|
|
16
|
-
// partitioned by `_lens` (per-tab stack lengths) with `_index` = selected tab. State is mutated
|
|
17
|
-
// BEFORE each command, so the host reconciles against the node; commands only name the native
|
|
18
|
-
// transition to play. Host-driven changes come back via `_hostDidSelect` / `_hostDidPop` — both
|
|
19
|
-
// idempotent no-ops when the logical state already matches.
|
|
20
|
-
|
|
21
|
-
/** Styles a pager accepts — layout box + drawable (background, borders, …). Pages style
|
|
22
|
-
* themselves; the pager only owns its own slot. */
|
|
23
|
-
export type UIPagerStyle = ElementStyle & DrawableStyle
|
|
24
|
-
|
|
25
|
-
/** The pagers whose screen is currently visible, outermost → innermost. `UIPager.current` is the
|
|
26
|
-
* last (innermost). Maintained by each owned screen's onOpen/onClose via _activate/_deactivate. */
|
|
27
|
-
const _activeStack: PagerElement[] = []
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The screen-navigation element: sibling **tabs** that swipe natively side to side, each with its
|
|
31
|
-
* own navigation **stack**. `push` opens a screen on top of the current tab (within the pager);
|
|
32
|
-
* `pop` / the native edge back-swipe unwinds it. A one-tab pager is a plain navigation stack.
|
|
33
|
-
*
|
|
34
|
-
* ```ts
|
|
35
|
-
* const pager = UIPager([ HomeScreen(), ProfileScreen() ]) // two tabs, side by side
|
|
36
|
-
* Router.init(UIScreen([ pager.style({ flexGrow: 1 }), tabBar ]))
|
|
37
|
-
*
|
|
38
|
-
* // from anywhere inside a hosted screen — no reference threading:
|
|
39
|
-
* UIPager.push(PostScreen(id))
|
|
40
|
-
* ```
|
|
41
|
-
*
|
|
42
|
-
* To open something over *everything* (above bars, above the pager), use `Router.push` instead.
|
|
43
|
-
*/
|
|
44
|
-
export interface UIPager extends UIElementBase<UIPagerStyle, DrawableStyle & BaseStyle> {
|
|
45
|
-
readonly type: "pager",
|
|
46
|
-
|
|
47
|
-
/** Replace the pager's tabs — one screen per tab, in order. Every tab becomes a fresh
|
|
48
|
-
* one-screen stack (pushed screens are dropped) and the selection is clamped into range;
|
|
49
|
-
* screens that were already tabs keep their state. Pass a function for reactive tabs. */
|
|
50
|
-
setContent(tabs: UIScreen[] | (() => UIScreen[])): this,
|
|
51
|
-
|
|
52
|
-
/** Switch to tab `index`. **Instant by default** — the tab-bar convention (tabs are parallel
|
|
53
|
-
* modes, not neighbors); pass `animated: true` for the direction-aware slide of top-tab /
|
|
54
|
-
* pager UIs. Out-of-range and same-index calls are no-ops. Tabs keep their stacks: selecting
|
|
55
|
-
* away and back restores the tab exactly where it was, pushed screens included. */
|
|
56
|
-
select(index: number, animated?: boolean): this,
|
|
57
|
-
/** The selected tab index (0-based). */
|
|
58
|
-
readonly index: number,
|
|
59
|
-
/** Fires when the selected tab changes and has settled — a user swipe or `select()`. */
|
|
60
|
-
onSelect(callback: (index: number) => void): this,
|
|
61
|
-
|
|
62
|
-
/** Push `screen` on top of the **current tab** — slides in natively; the edge back-swipe (or
|
|
63
|
-
* `pop()`) removes it. Other tabs are unaffected. */
|
|
64
|
-
push(screen: UIScreen): this,
|
|
65
|
-
/** Pop the current tab's top screen (no-op at the tab root — the root never pops). The
|
|
66
|
-
* programmatic twin of the native edge back-swipe. */
|
|
67
|
-
pop(): this,
|
|
68
|
-
/** Unwind the current tab back to its root screen in one step. */
|
|
69
|
-
popToRoot(): this,
|
|
70
|
-
/** Swap the current tab's top screen for `screen` without growing the stack (at depth 1 this
|
|
71
|
-
* swaps the tab's root). */
|
|
72
|
-
replace(screen: UIScreen): this,
|
|
73
|
-
/** The current tab's screens, root → top (read-only snapshot). */
|
|
74
|
-
readonly stack: UIScreen[],
|
|
75
|
-
/** The current tab's stack depth — 1 means only the root. While `depth > 1`, swiping between
|
|
76
|
-
* tabs is disabled (the horizontal gesture belongs to the back-swipe). */
|
|
77
|
-
readonly depth: number,
|
|
78
|
-
/** Fires when the current tab's depth changes — push, pop, replace, or a back-swipe. */
|
|
79
|
-
onChange(callback: (depth: number) => void): this,
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export class PagerElement extends ContainerElement<"pager"> {
|
|
83
|
-
/** Per-tab stack lengths — partitions the flat tab-major `children`. Named `_*` so chisel
|
|
84
|
-
* keeps it (the host reads it live). */
|
|
85
|
-
_lens: number[]
|
|
86
|
-
/** Selected tab. */
|
|
87
|
-
_index = 0
|
|
88
|
-
// >0 while a screen owned by this pager is the visible one (its onOpen fired, onClose hasn't).
|
|
89
|
-
// Only one owned screen is ever open, so this is effectively 0/1 — the count only makes the
|
|
90
|
-
// transient during a transition's old-close/new-open pair order-independent.
|
|
91
|
-
_openCount = 0
|
|
92
|
-
private scl: ((index: number) => void)[] = [] // onSelect listeners
|
|
93
|
-
private ncl: ((depth: number) => void)[] = [] // onChange listeners
|
|
94
|
-
|
|
95
|
-
constructor(style: UIPagerStyle | null, tabs: UIScreen[]) {
|
|
96
|
-
// flexShrink: 1 default (SDK-owned, like UIScrollable's): pages are separate layout roots
|
|
97
|
-
// sized to the pager's box, so yoga sees no shrinkable content — without this a pager in a
|
|
98
|
-
// flex column overflows the screen instead of leaving room for the tab bar.
|
|
99
|
-
super("pager", { flexShrink: 1, ...style }, tabs as any)
|
|
100
|
-
this._lens = tabs.map(() => 1)
|
|
101
|
-
for (const s of this.children) this._track(s)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** Start offset of tab `i`'s segment in the flat `children`. */
|
|
105
|
-
private _offset(i: number): number {
|
|
106
|
-
let off = 0
|
|
107
|
-
for (let t = 0; t < i; t++) off += this._lens[t]
|
|
108
|
-
return off
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
get index(): number {
|
|
112
|
-
return this._index
|
|
113
|
-
}
|
|
114
|
-
get stack(): UIScreen[] {
|
|
115
|
-
const off = this._offset(this._index)
|
|
116
|
-
return this.children.slice(off, off + (this._lens[this._index] ?? 0))
|
|
117
|
-
}
|
|
118
|
-
get depth(): number {
|
|
119
|
-
return this._lens[this._index] ?? 0
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
onSelect(callback: (index: number) => void): this {
|
|
123
|
-
this.scl.push(callback)
|
|
124
|
-
return this
|
|
125
|
-
}
|
|
126
|
-
onChange(callback: (depth: number) => void): this {
|
|
127
|
-
this.ncl.push(callback)
|
|
128
|
-
return this
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
select(index: number, animated = false): this {
|
|
132
|
-
if (index < 0 || index >= this._lens.length || index === this._index) return this
|
|
133
|
-
this._index = index
|
|
134
|
-
if (this._id !== 0) _creatorUI.command(this, "select", animated)
|
|
135
|
-
this._emitSelect()
|
|
136
|
-
return this
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
push(screen: UIScreen): this {
|
|
140
|
-
if (this._lens.length === 0) {
|
|
141
|
-
console.warn("UIPager.push: pager has no tabs")
|
|
142
|
-
return this
|
|
143
|
-
}
|
|
144
|
-
this.children.splice(this._offset(this._index) + this._lens[this._index], 0, screen)
|
|
145
|
-
this._lens[this._index]++
|
|
146
|
-
this._track(screen)
|
|
147
|
-
if (this._id !== 0) _creatorUI.command(this, "push")
|
|
148
|
-
this._emitChange()
|
|
149
|
-
return this
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
pop(): this {
|
|
153
|
-
if ((this._lens[this._index] ?? 0) <= 1) return this
|
|
154
|
-
this.children.splice(this._offset(this._index) + this._lens[this._index] - 1, 1)
|
|
155
|
-
this._lens[this._index]--
|
|
156
|
-
if (this._id !== 0) _creatorUI.command(this, "pop")
|
|
157
|
-
this._emitChange()
|
|
158
|
-
return this
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
popToRoot(): this {
|
|
162
|
-
const len = this._lens[this._index] ?? 0
|
|
163
|
-
if (len <= 1) return this
|
|
164
|
-
this.children.splice(this._offset(this._index) + 1, len - 1)
|
|
165
|
-
this._lens[this._index] = 1
|
|
166
|
-
if (this._id !== 0) _creatorUI.command(this, "popToRoot")
|
|
167
|
-
this._emitChange()
|
|
168
|
-
return this
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
replace(screen: UIScreen): this {
|
|
172
|
-
if (this._lens.length === 0) {
|
|
173
|
-
console.warn("UIPager.replace: pager has no tabs")
|
|
174
|
-
return this
|
|
175
|
-
}
|
|
176
|
-
this.children[this._offset(this._index) + this._lens[this._index] - 1] = screen
|
|
177
|
-
this._track(screen)
|
|
178
|
-
if (this._id !== 0) _creatorUI.command(this, "replace")
|
|
179
|
-
this._emitChange()
|
|
180
|
-
return this
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// ── Children ARE the tabs ──────────────────────────────────────────────────────────────────
|
|
184
|
-
// Every inherited container mutation would send a generic setContent/insertNode/removeNode wire
|
|
185
|
-
// op, and a host must never run those on a pager: its children are page ROOTS the host owns
|
|
186
|
-
// (each laid out independently at the pager's box), not yoga children — the generic path frees
|
|
187
|
-
// the live pages and rebuilds them as ordinary children stacked on the first tab. So each one is
|
|
188
|
-
// overridden to mutate the element first (children, `_lens`, `_index`) and then notify over the
|
|
189
|
-
// same command channel every other pager op uses, where the host reconciles against the node.
|
|
190
|
-
//
|
|
191
|
-
// Content set this way is always one screen per tab: a per-tab STACK only ever grows through
|
|
192
|
-
// push/replace, so `_lens` resets to all-ones here.
|
|
193
|
-
private _rebuild(children: any[]): this {
|
|
194
|
-
const tabs = children.filter(Boolean)
|
|
195
|
-
const prevIndex = this._index
|
|
196
|
-
this.children.length = 0
|
|
197
|
-
this.children.push(...tabs)
|
|
198
|
-
this._lens = tabs.map(() => 1)
|
|
199
|
-
if (this._index >= tabs.length) this._index = Math.max(0, tabs.length - 1)
|
|
200
|
-
for (const s of tabs) this._track(s)
|
|
201
|
-
if (this._id !== 0) _creatorUI.command(this, "content")
|
|
202
|
-
if (this._index !== prevIndex) this._emitSelect()
|
|
203
|
-
this._emitChange()
|
|
204
|
-
return this
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
setContent(children: UINodeChild[] | ChildrenFn): this {
|
|
208
|
-
if (typeof children === "function") {
|
|
209
|
-
this._bindChildren(children) // reactive children land in _reconcile below
|
|
210
|
-
return this
|
|
211
|
-
}
|
|
212
|
-
disposeBinding(this, "#children") // static content overrides a reactive binding
|
|
213
|
-
return this._rebuild(children as any[])
|
|
214
|
-
}
|
|
215
|
-
append(...nodes: UINodeChild[]): this {
|
|
216
|
-
return this._rebuild([...this.children, ...nodes])
|
|
217
|
-
}
|
|
218
|
-
insert(index: number, ...nodes: UINodeChild[]): this {
|
|
219
|
-
const next = this.children.slice()
|
|
220
|
-
next.splice(index, 0, ...(nodes as any[]))
|
|
221
|
-
return this._rebuild(next)
|
|
222
|
-
}
|
|
223
|
-
remove(...nodesToDelete: UIScreen[]): this {
|
|
224
|
-
const drop = new Set<any>(nodesToDelete)
|
|
225
|
-
return this._rebuild(this.children.filter((c: any) => !drop.has(c)))
|
|
226
|
-
}
|
|
227
|
-
_reconcile(next: UINodeChild[]): void {
|
|
228
|
-
this._rebuild(next as any[])
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Host-driven tab change (committed swipe): the native pager already moved — sync the logical
|
|
232
|
-
// index + fire onSelect. Named `_*` so chisel keeps it (hosts call it).
|
|
233
|
-
_hostDidSelect(index: number): void {
|
|
234
|
-
if (index < 0 || index >= this._lens.length || index === this._index) return
|
|
235
|
-
this._index = index
|
|
236
|
-
this._emitSelect()
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Host-driven pop on the current tab (interactive edge back-swipe): the native stack already
|
|
240
|
-
// popped — sync the logical stack + fire onChange. Idempotent, safe after every transition.
|
|
241
|
-
_hostDidPop(depth: number): void {
|
|
242
|
-
if (depth < 1) depth = 1
|
|
243
|
-
const len = this._lens[this._index] ?? 0
|
|
244
|
-
if (depth >= len) return
|
|
245
|
-
this.children.splice(this._offset(this._index) + depth, len - depth)
|
|
246
|
-
this._lens[this._index] = depth
|
|
247
|
-
this._emitChange()
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
private _emitSelect(): void {
|
|
251
|
-
const i = this._index
|
|
252
|
-
for (const cb of this.scl) cb(i)
|
|
253
|
-
}
|
|
254
|
-
private _emitChange(): void {
|
|
255
|
-
const d = this._lens[this._index] ?? 0
|
|
256
|
-
for (const cb of this.ncl) cb(d)
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Register a screen for `current` tracking: while a screen owned by this pager is the visible
|
|
260
|
-
// top, this pager is active. The hook reads screen._pagerOwner LIVE (not a captured `this`), so
|
|
261
|
-
// re-homing a screen into another pager activates the right one.
|
|
262
|
-
private _track(screen: any): void {
|
|
263
|
-
screen._pagerOwner = this
|
|
264
|
-
if (!screen._pagerHooked) {
|
|
265
|
-
screen._pagerHooked = true
|
|
266
|
-
screen.onOpen(() => screen._pagerOwner?._activate())
|
|
267
|
-
screen.onClose(() => screen._pagerOwner?._deactivate())
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
_activate(): void {
|
|
271
|
-
if (++this._openCount === 1 && _activeStack.indexOf(this) < 0) {
|
|
272
|
-
_activeStack.push(this)
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
_deactivate(): void {
|
|
276
|
-
if (--this._openCount <= 0) {
|
|
277
|
-
this._openCount = 0
|
|
278
|
-
const i = _activeStack.indexOf(this)
|
|
279
|
-
if (i >= 0) _activeStack.splice(i, 1)
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const isScreenArg = (v: any): boolean => v != null && typeof v === "object" && v.type === "screen"
|
|
285
|
-
|
|
286
|
-
/** Callable factory + ambient statics. The name `UIPager` is a value (this factory) and a type
|
|
287
|
-
* (the instance interface above) — a standard value/type merge. */
|
|
288
|
-
interface UIPagerConstructor {
|
|
289
|
-
/** Tabs — each argument is a screen or an array of screens (arrays flatten one level, falsy
|
|
290
|
-
* entries are skipped). One screen → a plain navigation stack rooted at it (push/pop, no tab
|
|
291
|
-
* swiping); several → swipeable sibling tabs, fixed at construction, each starting as its own
|
|
292
|
-
* one-screen stack. */
|
|
293
|
-
(...tabs: (UIScreen | UIScreen[] | false | null | undefined)[]): UIPager
|
|
294
|
-
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
|
|
295
|
-
(style: UIPagerStyle, content?: UIScreen | UIScreen[]): UIPager
|
|
296
|
-
/** The pager that owns the currently visible screen, or `null` when the visible screen isn't
|
|
297
|
-
* hosted by one. With pagers nested inside pages, resolves to the **innermost** — so ambient
|
|
298
|
-
* calls act on the pager the user is actually looking at. */
|
|
299
|
-
readonly current: UIPager | null
|
|
300
|
-
/** `UIPager.current.push(screen)` — push onto the pager owning the visible screen, no
|
|
301
|
-
* reference threading. No-op with a console warning when there is no current pager. */
|
|
302
|
-
push(screen: UIScreen): void
|
|
303
|
-
/** `UIPager.current.pop()` — pop the visible pager's current tab (no-op at the tab root, or
|
|
304
|
-
* with a warning when there is no current pager). */
|
|
305
|
-
pop(): void
|
|
306
|
-
/** `UIPager.current.popToRoot()` — unwind the visible pager's current tab to its root. */
|
|
307
|
-
popToRoot(): void
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function pagerFactory(...args: any[]): UIPager {
|
|
311
|
-
let style: any = null
|
|
312
|
-
let start = 0
|
|
313
|
-
const a0 = args.length > 0 ? args[0] : undefined
|
|
314
|
-
if (a0 != null && typeof a0 === "object" && !Array.isArray(a0) && !isScreenArg(a0)) {
|
|
315
|
-
style = a0
|
|
316
|
-
start = 1
|
|
317
|
-
}
|
|
318
|
-
const tabs: any[] = []
|
|
319
|
-
for (let i = start; i < args.length; i++) {
|
|
320
|
-
const a = args[i]
|
|
321
|
-
if (Array.isArray(a)) {
|
|
322
|
-
for (let j = 0; j < a.length; j++) if (a[j]) tabs.push(a[j])
|
|
323
|
-
} else if (a) {
|
|
324
|
-
tabs.push(a)
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
return new PagerElement(style, tabs) as unknown as UIPager
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Create a pager — THE screen-navigation element: an array of screens becomes swipeable sibling
|
|
332
|
-
* tabs, a single screen becomes a plain stack. Navigate within it via `pager.push/pop/replace`
|
|
333
|
-
* (per-tab) and `pager.select(i)` (between tabs), or from any child screen via the ambient
|
|
334
|
-
* `UIPager.push` / `UIPager.pop` / `UIPager.current`. Pages are full `UIScreen`s (own
|
|
335
|
-
* `onOpen`/`onClose`); give the pager `flexGrow: 1` or a size so it doesn't
|
|
336
|
-
* collapse. `Router` stays the way to open a destination ON TOP of the pager.
|
|
337
|
-
*/
|
|
338
|
-
export const UIPager = pagerFactory as unknown as UIPagerConstructor
|
|
339
|
-
|
|
340
|
-
const _resolveCurrent = (): UIPager | null => {
|
|
341
|
-
const cur = _activeStack[_activeStack.length - 1]
|
|
342
|
-
if (!cur) {
|
|
343
|
-
console.warn("UIPager.push/pop: no active pager on the current screen")
|
|
344
|
-
return null
|
|
345
|
-
}
|
|
346
|
-
return cur as unknown as UIPager
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// The ambient statics. These are top-level statements in a `sideEffects: false` module, so they only
|
|
350
|
-
// survive bundling because chisel treats a statement that *mutates a binding this module declares*
|
|
351
|
-
// as part of that binding's declaration (`ItemKind::AttachedTo`, chisel >= 0.4.2) — kept iff
|
|
352
|
-
// `UIPager` is reached, shaken away with it otherwise. Under chisel <= 0.4.1 they were dropped
|
|
353
|
-
// outright and `UIPager.push` arrived as `undefined` at runtime, in bundled builds only; don't lower
|
|
354
|
-
// the pinned `@letary/chisel` back past 0.4.2. Covered by sdk/tests/compile.test.ts.
|
|
355
|
-
// `Object.defineProperty` for `current`, not `Object.assign({ get current() {} })` — Object.assign
|
|
356
|
-
// *invokes* the getter and copies the resulting value, freezing it at null forever.
|
|
357
|
-
Object.defineProperty(UIPager, "current", {
|
|
358
|
-
get: () => (_activeStack[_activeStack.length - 1] ?? null) as unknown as UIPager | null,
|
|
359
|
-
})
|
|
360
|
-
;(UIPager as any).push = (screen: UIScreen) => { _resolveCurrent()?.push(screen) }
|
|
361
|
-
;(UIPager as any).pop = () => { _resolveCurrent()?.pop() }
|
|
362
|
-
;(UIPager as any).popToRoot = () => { _resolveCurrent()?.popToRoot() }
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import { ModalElement, type UIModal, type UIModalStyle } from "./UIModal"
|
|
2
|
-
import { Presentable } from "./presentable"
|
|
3
|
-
import { device } from "../runtime/device"
|
|
4
|
-
import { buildUI, type BoundingClientRect, type ChildrenFn, type UIChildArg, type UINodeChild } from "./UINode"
|
|
5
|
-
|
|
6
|
-
export type UIPopoverStyle = UIModalStyle
|
|
7
|
-
|
|
8
|
-
/** What a popover can anchor to: any element (it reads `getBoundingClientRect()`), or a raw
|
|
9
|
-
* `{x, y}` point — a long-press context menu anchors to the touch position. */
|
|
10
|
-
export type UIPopoverAnchor = { getBoundingClientRect(): BoundingClientRect | null } | { x: number, y: number }
|
|
11
|
-
|
|
12
|
-
const POPOVER_ANIM_MS = 120
|
|
13
|
-
const ANCHOR_GAP = 4 // between the anchor's edge and the popover
|
|
14
|
-
const VIEWPORT_MARGIN = 8 // minimum distance kept to every screen edge
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* An anchored menu: a `UIModal` whose scrim is transparent (invisible but still intercepting —
|
|
18
|
-
* an outside tap dismisses, and nothing underneath can scroll while it's open) and whose
|
|
19
|
-
* position comes from the anchor passed to `show(anchor)`: below the anchor's left edge,
|
|
20
|
-
* flipped above when there's no room, clamped into the viewport. Position is computed once per
|
|
21
|
-
* show — anchored popovers don't follow their anchor. Backs dropdowns, context menus, and
|
|
22
|
-
* tooltips; create it **once** at module scope and reuse it.
|
|
23
|
-
*/
|
|
24
|
-
export interface UIPopover extends UIModal {
|
|
25
|
-
/** Position next to `anchor` (an element or an `{x, y}` point), attach to
|
|
26
|
-
* `Presentable.current` (unless an owner was set via `attachTo`), and play the entrance
|
|
27
|
-
* transition. Without an anchor the popover shows wherever its own style puts it. */
|
|
28
|
-
show(anchor?: UIPopoverAnchor): void,
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export class PopoverElement extends ModalElement {
|
|
32
|
-
private _anchorRect?: { left: number, top: number, right: number, bottom: number }
|
|
33
|
-
private _placedLeft?: number
|
|
34
|
-
private _placedTop?: number
|
|
35
|
-
|
|
36
|
-
constructor(style: UIPopoverStyle, children: UINodeChild[] | ChildrenFn) {
|
|
37
|
-
super({ overlayColor: "transparent", ...style }, children)
|
|
38
|
-
this.transition({ opacity: 0, duration: POPOVER_ANIM_MS })
|
|
39
|
-
// The flip/clamp decision needs the popover's measured size, which exists only after the
|
|
40
|
-
// mount lays it out — so placement is corrected on layout, inside the entrance fade.
|
|
41
|
-
this.onLayout(({ width, height }) => this._place(width, height))
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
show(anchor?: UIPopoverAnchor): void {
|
|
45
|
-
if (anchor) {
|
|
46
|
-
const r = "getBoundingClientRect" in anchor
|
|
47
|
-
? anchor.getBoundingClientRect()
|
|
48
|
-
: { left: anchor.x, top: anchor.y, right: anchor.x, bottom: anchor.y }
|
|
49
|
-
if (r) {
|
|
50
|
-
this._anchorRect = { left: r.left, top: r.top, right: r.right, bottom: r.bottom }
|
|
51
|
-
this._placedLeft = undefined
|
|
52
|
-
this._placedTop = undefined
|
|
53
|
-
// Preliminary spot (below, left-aligned) so the first paint is already close; _place
|
|
54
|
-
// refines it once the size is known.
|
|
55
|
-
this.style({ left: r.left, top: r.bottom + ANCHOR_GAP })
|
|
56
|
-
}
|
|
57
|
-
} else {
|
|
58
|
-
this._anchorRect = undefined // unanchored show: the user's own style owns the position
|
|
59
|
-
}
|
|
60
|
-
// A menu belongs to the page it opened on: it hides with it and rides its transition. An
|
|
61
|
-
// explicit attachTo() owner (e.g. a scene HUD's popover) is respected.
|
|
62
|
-
if (!this._owner) this.attachTo(Presentable.current)
|
|
63
|
-
super.show()
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
private _place(width: number, height: number): void {
|
|
67
|
-
const anchor = this._anchorRect
|
|
68
|
-
if (!anchor || !this._open) return
|
|
69
|
-
const displayWidth = device.width, displayHeight = device.height // 0 on hosts without a size
|
|
70
|
-
let left = anchor.left
|
|
71
|
-
let top = anchor.bottom + ANCHOR_GAP
|
|
72
|
-
if (displayWidth > 0) {
|
|
73
|
-
left = Math.min(left, displayWidth - width - VIEWPORT_MARGIN)
|
|
74
|
-
left = Math.max(left, VIEWPORT_MARGIN)
|
|
75
|
-
}
|
|
76
|
-
if (displayHeight > 0 && top + height > displayHeight - VIEWPORT_MARGIN) {
|
|
77
|
-
const above = anchor.top - ANCHOR_GAP - height
|
|
78
|
-
top = above >= VIEWPORT_MARGIN ? above : Math.max(VIEWPORT_MARGIN, displayHeight - VIEWPORT_MARGIN - height)
|
|
79
|
-
}
|
|
80
|
-
// Idempotence guard: hosts re-fire onLayout on recalc, and writing the same position would
|
|
81
|
-
// trigger another recalc — only write when the target actually moved.
|
|
82
|
-
if (this._placedLeft !== undefined && Math.abs(left - this._placedLeft) < 0.5
|
|
83
|
-
&& this._placedTop !== undefined && Math.abs(top - this._placedTop) < 0.5) return
|
|
84
|
-
this._placedLeft = left
|
|
85
|
-
this._placedTop = top
|
|
86
|
-
this.style({ left, top })
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const makePopover = (style: any, children: any) => new PopoverElement(style, children)
|
|
91
|
-
|
|
92
|
-
/** Create an anchored popover (see {@link UIPopover}) — create once at module scope, then
|
|
93
|
-
* `show(anchor)` next to an element or `{x, y}` point. Same argument forms as `UIColumn`. */
|
|
94
|
-
export function UIPopover(...children: UIChildArg[]): UIPopover;
|
|
95
|
-
export function UIPopover(children: UINodeChild[] | ChildrenFn): UIPopover;
|
|
96
|
-
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
|
|
97
|
-
export function UIPopover(style: UIPopoverStyle, children?: UINodeChild[] | ChildrenFn): UIPopover;
|
|
98
|
-
export function UIPopover(...args: any[]): UIPopover {
|
|
99
|
-
return buildUI(args, null, makePopover)
|
|
100
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import { TouchStartEvent } from "../runtime/touch"
|
|
2
|
-
import { _bumpNavEpoch, _navSupported, _setCurrent, Presentable, type PresentOptions } from "./presentable"
|
|
3
|
-
import { ContainerElement, buildUI, type BaseStyle, type ContainerStyle, type DrawableStyle, type PaddingStyle, type UIChildArg, type UIContainerBase, type UINodeChild, type ChildrenFn, } from "./UINode"
|
|
4
|
-
|
|
5
|
-
export type UIScreenStyle = ContainerStyle & DrawableStyle & PaddingStyle & BaseStyle
|
|
6
|
-
|
|
7
|
-
/** A screen — the root the router/host mounts, sized to its slot. A screen NEVER scrolls: it is
|
|
8
|
-
* fixed chrome (header, tab bar) plus, when the content overflows, one `UIScrollable` body child
|
|
9
|
-
* (`flexGrow: 1`). Scroll events and pull-to-refresh live on the scroll containers
|
|
10
|
-
* (`UIScrollable`, `UIVirtualizedList`). */
|
|
11
|
-
export interface UIScreen extends UIContainerBase<UIScreenStyle, DrawableStyle & BaseStyle> {
|
|
12
|
-
readonly type: "screen",
|
|
13
|
-
|
|
14
|
-
/** Show this screen as the current destination (replaces whatever is visible — a screen, a
|
|
15
|
-
* scene, a native view — suspending an active Router until `Router.restore()`). */
|
|
16
|
-
open(options?: PresentOptions): void,
|
|
17
|
-
/** Dismiss if this screen is the visible destination. */
|
|
18
|
-
close(): void
|
|
19
|
-
|
|
20
|
-
/** Hardware/system back while this screen is current. */
|
|
21
|
-
onBackPressed(callback: () => void): this
|
|
22
|
-
|
|
23
|
-
/** The screen became the visible destination (first open, or revealed by a pop). */
|
|
24
|
-
onOpen(callback: () => void): this
|
|
25
|
-
/** The screen stopped being visible — closed, replaced, or covered by a push. */
|
|
26
|
-
onClose(callback: () => void): this
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Keep this screen's built native tree alive when a host detaches it: instead of being destroyed,
|
|
30
|
-
* its views + layout + live state (scroll position, input text) are retained, so re-hosting the
|
|
31
|
-
* SAME screen object restores it in place rather than rebuilding. `onClose`/`onOpen` still fire on
|
|
32
|
-
* detach/re-attach. You own the lifetime — a kept tree is freed only by `dispose()` or when the
|
|
33
|
-
* project unloads. (Native-only today; web rebuilds.)
|
|
34
|
-
*/
|
|
35
|
-
keepAlive(enabled?: boolean): this
|
|
36
|
-
/** Free a `keepAlive()` screen's retained tree now. No-op unless it's currently detached-and-kept. */
|
|
37
|
-
dispose(): this
|
|
38
|
-
|
|
39
|
-
/** Touch began on the screen; `ev.track(...)` takes over the rest of the gesture. */
|
|
40
|
-
onTouchStart(callback: (ev: TouchStartEvent<UIScreen>) => void): this,
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export class ScreenElement extends ContainerElement<"screen"> {
|
|
44
|
-
open(options?: PresentOptions): void {
|
|
45
|
-
_bumpNavEpoch()
|
|
46
|
-
if (_navSupported()) {
|
|
47
|
-
_creatorUI.openView!(this, options?.transition ?? "none")
|
|
48
|
-
} else {
|
|
49
|
-
// Legacy host: no destination navigation — transition is dropped.
|
|
50
|
-
_creatorUI.openScreen(this)
|
|
51
|
-
}
|
|
52
|
-
_setCurrent(this as any)
|
|
53
|
-
}
|
|
54
|
-
close(): void {
|
|
55
|
-
_bumpNavEpoch()
|
|
56
|
-
if (_navSupported()) {
|
|
57
|
-
_creatorUI.closeView!()
|
|
58
|
-
} else {
|
|
59
|
-
_creatorUI.closeScreen()
|
|
60
|
-
}
|
|
61
|
-
if (Presentable.current === (this as any)) _setCurrent(null)
|
|
62
|
-
}
|
|
63
|
-
protected ol: (() => void)[] = []
|
|
64
|
-
onOpen(callback: () => void): this {
|
|
65
|
-
this.ol.push(callback)
|
|
66
|
-
return this
|
|
67
|
-
}
|
|
68
|
-
protected cl: (() => void)[] = []
|
|
69
|
-
onClose(callback: () => void): this {
|
|
70
|
-
this.cl.push(callback)
|
|
71
|
-
return this
|
|
72
|
-
}
|
|
73
|
-
// Read by the host at detach time (a plain flag on the node, like _interactive).
|
|
74
|
-
_keepAlive = false
|
|
75
|
-
keepAlive(enabled = true): this {
|
|
76
|
-
this._keepAlive = enabled
|
|
77
|
-
return this
|
|
78
|
-
}
|
|
79
|
-
dispose(): this {
|
|
80
|
-
// Rides the generic command channel (no dedicated bridge). No-op while _id is 0 (never mounted)
|
|
81
|
-
// or when the tree isn't currently detached-and-kept — the host ignores it then.
|
|
82
|
-
if (this._id !== 0) _creatorUI.command(this, "disposeKeepAlive")
|
|
83
|
-
return this
|
|
84
|
-
}
|
|
85
|
-
readonly touchStartListeners: any[] = []
|
|
86
|
-
_interactive = false
|
|
87
|
-
onTouchStart (callback: any): this {
|
|
88
|
-
this._interactive = true
|
|
89
|
-
this.touchStartListeners.push(callback)
|
|
90
|
-
return this
|
|
91
|
-
}
|
|
92
|
-
_emitTouchStart(pointerId: number, clientX: number, clientY: number): any {
|
|
93
|
-
const touchEvent = new TouchStartEvent(clientX, clientY, pointerId)
|
|
94
|
-
touchEvent.target = this
|
|
95
|
-
for (let callback of this.touchStartListeners) {
|
|
96
|
-
callback(touchEvent)
|
|
97
|
-
if ((touchEvent as any)._trackHandler) {
|
|
98
|
-
return (touchEvent as any)._trackHandler
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return null
|
|
102
|
-
}
|
|
103
|
-
_backButtonCallback?: () => void
|
|
104
|
-
onBackPressed(callback: any): this {
|
|
105
|
-
this._backButtonCallback = callback
|
|
106
|
-
return this
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const makeScreen = (style: any, children: any) => new ScreenElement("screen", style, children)
|
|
111
|
-
|
|
112
|
-
/** @internal Compiler fast path (chisel `flatten_ui` raw lowering) — see UIContainer. */
|
|
113
|
-
export const __UIScreen = (children: UINodeChild[] | ChildrenFn): UIScreen => makeScreen({}, children)
|
|
114
|
-
|
|
115
|
-
/** Create a screen. Present it with `.open()`, or via `Router` / `UIPager`. Same argument forms
|
|
116
|
-
* as `UIColumn`. */
|
|
117
|
-
export function UIScreen(...children: UIChildArg[]): UIScreen;
|
|
118
|
-
export function UIScreen(children: UINodeChild[] | ChildrenFn): UIScreen;
|
|
119
|
-
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
|
|
120
|
-
export function UIScreen(style: UIScreenStyle, children?: UINodeChild[] | ChildrenFn): UIScreen;
|
|
121
|
-
export function UIScreen(...args: any[]): UIScreen {
|
|
122
|
-
return buildUI(args, null, makeScreen)
|
|
123
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import type { UIContainerStyle } from "./UIContainer"
|
|
2
|
-
import { type UIChildArg, type UIContainerBase, type UINodeChild, type ChildrenFn, type Color, ContainerElement, buildUI } from "./UINode"
|
|
3
|
-
|
|
4
|
-
export type ScrollStyle = {
|
|
5
|
-
showScrollbar?: boolean,
|
|
6
|
-
overscrollMode?: "none" | "absorb" | "default",
|
|
7
|
-
/** Tint of the native pull-to-refresh spinner (pairs with `onRefresh`). */
|
|
8
|
-
refreshControlColor?: Color,
|
|
9
|
-
/** How a scroll gesture in THIS scrollable dismisses the keyboard: `"interactive"` (default) —
|
|
10
|
-
* dragging over the keyboard slides it away (chat feel); `"scroll"` — any drag dismisses
|
|
11
|
-
* immediately; `"none"` — never (tap-outside policy still applies). */
|
|
12
|
-
keyboardDismissMode?: "interactive" | "scroll" | "none",
|
|
13
|
-
/** Paging: when the drag ends, rest on a direct child's boundary along the scroll axis — the
|
|
14
|
-
* value picks where the child sits in the viewport; `"none"` (default) is free scrolling. Snap
|
|
15
|
-
* targets are the children themselves, so item widths can differ (card-carousel-with-peek); a
|
|
16
|
-
* carousel is a horizontal scrollable of full-width children + `snap: "start"`. */
|
|
17
|
-
snap?: "none" | "start" | "center" | "end"
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export type UIScrollableStyle = UIContainerStyle & ScrollStyle & { scrollDirection?: "horizontal" | "vertical" }
|
|
21
|
-
|
|
22
|
-
/** A scroll container — vertical by default, `scrollDirection: "horizontal"` for carousels/chip
|
|
23
|
-
* rows. The scrolling body of a screen (screens themselves never scroll). */
|
|
24
|
-
export interface UIScrollable extends UIContainerBase<UIScrollableStyle> {
|
|
25
|
-
readonly type: "scrollable",
|
|
26
|
-
|
|
27
|
-
/** Scroll position changed (px from the start edge). */
|
|
28
|
-
onScroll(callback: (scrollPosition: number) => void): UIScrollable,
|
|
29
|
-
/** The scroll gesture ended (finger lifted). */
|
|
30
|
-
onScrollRelease(callback: () => void): UIScrollable,
|
|
31
|
-
/** Pulled past the content edge by `delta` px. */
|
|
32
|
-
onOverscroll(callback: (delta: number) => void): UIScrollable
|
|
33
|
-
/** Pull-to-refresh: the spinner stays until the returned promise settles. Attach BEFORE the
|
|
34
|
-
* node mounts (hosts read it at creation). Vertical only; native hosts — no-op on web. */
|
|
35
|
-
onRefresh(callback: () => void | Promise<void>): UIScrollable
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
class ScrollableElement extends ContainerElement<"scrollable"> {
|
|
39
|
-
|
|
40
|
-
readonly osl: any[] = []
|
|
41
|
-
readonly sl: any[] = []
|
|
42
|
-
readonly srl: any[] = []
|
|
43
|
-
|
|
44
|
-
get scrollListeners() {
|
|
45
|
-
return this.sl
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
onScroll (callback: any): this {
|
|
49
|
-
this.sl.push(callback)
|
|
50
|
-
return this
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
onScrollRelease (callback: any): this {
|
|
54
|
-
this.srl.push(callback)
|
|
55
|
-
return this
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
onOverscroll (callback: any): this {
|
|
59
|
-
this.osl.push(callback)
|
|
60
|
-
return this
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Read by the host at mount (same contract the scrollable screen had before 2026-07-22).
|
|
64
|
-
declare _refreshCallback: any
|
|
65
|
-
onRefresh (callback: any): this {
|
|
66
|
-
this._refreshCallback = callback
|
|
67
|
-
return this
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// flexShrink: 1 default (SDK-owned, like UIRow's flexDirection): without it a scrollable in a
|
|
72
|
-
// flex parent grows to its content height and overflows instead of scrolling.
|
|
73
|
-
const SCROLLABLE_DEFAULTS = { flexShrink: 1 } as const
|
|
74
|
-
const makeScrollable = (style: any, children: any) => new ScrollableElement("scrollable", style, children)
|
|
75
|
-
|
|
76
|
-
/** @internal Compiler fast path (chisel `flatten_ui` raw lowering) — see UIContainer. */
|
|
77
|
-
export const __UIScrollable = (children: UINodeChild[] | ChildrenFn): UIScrollable => makeScrollable({ ...SCROLLABLE_DEFAULTS }, children)
|
|
78
|
-
|
|
79
|
-
/** Create a scroll container (same argument forms as `UIColumn`). As a screen body, pair with
|
|
80
|
-
* `flexGrow: 1`. */
|
|
81
|
-
export function UIScrollable(...children: UIChildArg[]): UIScrollable
|
|
82
|
-
export function UIScrollable(children: UINodeChild[] | ChildrenFn): UIScrollable
|
|
83
|
-
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
|
|
84
|
-
export function UIScrollable(style: UIScrollableStyle, children?: UINodeChild[] | ChildrenFn): UIScrollable
|
|
85
|
-
export function UIScrollable(...args: any[]): UIScrollable {
|
|
86
|
-
return buildUI(args, SCROLLABLE_DEFAULTS, makeScrollable)
|
|
87
|
-
}
|