lecodes-sdk 1.1.0 → 2.0.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.
- package/README.md +5 -2
- package/dist/editor.d.ts +12 -0
- package/dist/global.d.ts +4 -8
- package/dist/host.d.ts +2 -3
- package/dist/types/animate/tween/Animation.d.ts +0 -3
- package/dist/types/animate/tween/animateValue.d.ts +4 -2
- package/dist/types/animate/tween/easing.d.ts +8 -0
- package/dist/types/animate/tween/spec.d.ts +15 -7
- package/dist/types/audio/audio.d.ts +2 -1
- package/dist/types/canvas/Canvas.d.ts +40 -108
- package/dist/types/canvas/gen/cssColor.d.ts +17 -0
- package/dist/types/canvas/gen/recorder.d.ts +118 -0
- package/dist/types/canvas/gen/spec.d.ts +144 -0
- package/dist/types/core/color.d.ts +3 -1
- package/dist/types/core/pins.d.ts +18 -0
- package/dist/types/g2/Node2D.d.ts +5 -8
- package/dist/types/g2/Scene2D.d.ts +6 -2
- package/dist/types/gl/Foliage.d.ts +30 -7
- package/dist/types/gl/Light.d.ts +11 -0
- package/dist/types/gl/Lightmap.d.ts +13 -2
- package/dist/types/gl/Material.d.ts +17 -3
- package/dist/types/gl/Model.d.ts +6 -2
- package/dist/types/gl/Node.d.ts +3 -6
- package/dist/types/gl/Particles.d.ts +15 -0
- package/dist/types/gl/Scene.d.ts +41 -13
- package/dist/types/gl/Texture.d.ts +1 -1
- package/dist/types/gl/animation/Locomotion.d.ts +59 -3
- package/dist/types/gl/terrain/Terrain.d.ts +8 -0
- package/dist/types/inject.d.ts +11 -11
- package/dist/types/inject.editor.d.ts +1 -0
- package/dist/types/net/core.d.ts +7 -0
- package/dist/types/plugin.d.ts +76 -0
- package/dist/types/plugins/gen/camera/sdk/camera.d.ts +24 -0
- package/dist/types/plugins/gen/camera/sdk/camera.gen.d.ts +25 -0
- package/dist/types/plugins/{geolocation.d.ts → gen/geolocation/sdk/geolocation.d.ts} +2 -20
- package/dist/types/plugins/gen/geolocation/sdk/geolocation.gen.d.ts +31 -0
- package/dist/types/plugins/{map.d.ts → gen/map/sdk/map.d.ts} +9 -50
- package/dist/types/plugins/gen/map/sdk/map.gen.d.ts +53 -0
- package/dist/types/plugins/gen/push/sdk/push.d.ts +23 -0
- package/dist/types/plugins/gen/push/sdk/push.gen.d.ts +35 -0
- package/dist/types/plugins/{qr.d.ts → gen/qr-scanner/sdk/qr-scanner.d.ts} +2 -3
- package/dist/types/plugins/gen/qr-scanner/sdk/qr-scanner.gen.d.ts +15 -0
- package/dist/types/runtime/app.d.ts +9 -2
- package/dist/types/runtime/fetch.d.ts +2 -0
- package/dist/types/runtime/input.d.ts +8 -1
- package/dist/types/runtime/media.d.ts +6 -10
- package/dist/types/runtime/misc.d.ts +4 -1
- package/dist/types/runtime/net.d.ts +3 -2
- package/dist/types/runtime/touch.d.ts +32 -0
- package/dist/types/scene/defineScene.d.ts +43 -2
- package/dist/types/scene/editor.d.ts +52 -0
- package/dist/types/scene/gizmos.d.ts +7 -4
- package/dist/types/ui/NativeView.d.ts +6 -4
- package/dist/types/ui/UI.d.ts +1 -1
- package/dist/types/ui/UIBottomSheet.d.ts +6 -12
- package/dist/types/ui/UIButton.d.ts +12 -14
- package/dist/types/ui/UIContainer.d.ts +0 -6
- package/dist/types/ui/UIImage.d.ts +1 -4
- package/dist/types/ui/UIInput.d.ts +8 -24
- package/dist/types/ui/UIModal.d.ts +0 -2
- package/dist/types/ui/UINode.d.ts +70 -56
- package/dist/types/ui/UIPager.d.ts +28 -27
- package/dist/types/ui/UIPopover.d.ts +0 -2
- package/dist/types/ui/UIScreen.d.ts +12 -17
- package/dist/types/ui/UIScrollable.d.ts +1 -4
- package/dist/types/ui/UIText.d.ts +0 -2
- package/dist/types/ui/UIVideo.d.ts +3 -5
- package/dist/types/ui/UIVirtualizedList.d.ts +14 -16
- package/dist/types/ui/UIWidget.d.ts +10 -9
- package/dist/types/ui/colorKeys.gen.d.ts +9 -0
- package/dist/types/ui/presentable.d.ts +46 -32
- package/dist/types/ui/router.d.ts +18 -7
- package/dist/types/ui/styleColor.d.ts +1 -0
- package/dist/types/ui/transitions.d.ts +18 -0
- package/dist/types/ui/tree.d.ts +75 -0
- package/dist/types/version.d.ts +10 -0
- package/dist/types.json +1 -1
- package/package.json +12 -3
- package/prompts/2d.md +2 -6
- package/prompts/3d.md +1 -5
- package/prompts/README.md +142 -142
- package/prompts/canvas.md +9 -8
- package/prompts/compose.ts +1 -1
- package/prompts/core.md +3 -3
- package/prompts/design.md +1 -1
- package/prompts/dist/2d-game.md +77 -54
- package/prompts/dist/3d-app.md +75 -52
- package/prompts/dist/ar-app.md +74 -51
- package/prompts/dist/design.md +30 -8
- package/prompts/dist/ui-app.md +73 -46
- package/prompts/ui-design.md +2 -3
- package/prompts/ui.md +45 -37
- package/src/animate/tween/Animation.ts +262 -378
- package/src/animate/tween/animateValue.ts +103 -100
- package/src/animate/tween/easing.ts +10 -3
- package/src/animate/tween/spec.ts +505 -479
- package/src/audio/Sound.ts +3 -3
- package/src/audio/audio.ts +162 -161
- package/src/bridges/2d.d.ts +317 -0
- package/src/bridges/app.d.ts +91 -0
- package/src/bridges/audio.d.ts +97 -0
- package/src/bridges/canvas.d.ts +79 -0
- package/src/bridges/device.d.ts +72 -0
- package/src/bridges/fetch.d.ts +80 -0
- package/src/bridges/files.d.ts +70 -0
- package/src/bridges/gl.d.ts +1133 -0
- package/src/bridges/input.d.ts +72 -0
- package/src/bridges/media.d.ts +55 -0
- package/src/bridges/nav.d.ts +71 -0
- package/src/bridges/net.d.ts +52 -0
- package/src/bridges/service.d.ts +52 -0
- package/src/bridges/socket.d.ts +31 -0
- package/src/bridges/storage.d.ts +33 -0
- package/src/bridges/tree.d.ts +301 -0
- package/src/bridges/types.d.ts +49 -0
- package/src/canvas/Canvas.ts +114 -159
- package/src/canvas/gen/cssColor.ts +224 -0
- package/src/canvas/gen/recorder.ts +212 -0
- package/src/canvas/gen/spec.ts +201 -0
- package/src/chisel.ts +193 -0
- package/src/compile/assetMacro.ts +1 -1
- package/src/compile/bundler.ts +11 -2
- package/src/compile/compileProject.ts +43 -4
- package/src/compile/fontMacro.ts +3 -4
- package/src/compile/header.ts +26 -5
- package/src/compile/index.ts +3 -1
- package/src/compile/liteMaterial.ts +1 -1
- package/src/compile/sceneEditor.ts +11 -26
- package/src/core/color.ts +73 -30
- package/src/core/pins.ts +51 -0
- package/src/core/signals.ts +8 -1
- package/src/g2/CharacterController2D.ts +3 -3
- package/src/g2/Node2D.ts +57 -39
- package/src/g2/Physics2D.ts +2 -2
- package/src/g2/Scene2D.ts +35 -23
- package/src/g2/Texture2D.ts +1 -1
- package/src/g2/loop.ts +4 -4
- package/src/gl/Foliage.ts +72 -17
- package/src/gl/Geometry.ts +1 -2
- package/src/gl/Light.ts +13 -0
- package/src/gl/Lightmap.ts +45 -29
- package/src/gl/Material.ts +95 -51
- package/src/gl/Model.ts +172 -167
- package/src/gl/Node.ts +91 -24
- package/src/gl/Particles.ts +20 -1
- package/src/gl/Scene.ts +100 -44
- package/src/gl/Texture.ts +8 -7
- package/src/gl/animation/AnimationClip.ts +1 -1
- package/src/gl/animation/Locomotion.ts +77 -8
- package/src/gl/nav/NavMesh.ts +3 -4
- package/src/gl/physics/Physics.ts +2 -2
- package/src/gl/physics/physicsEvents.ts +3 -3
- package/src/gl/terrain/Terrain.ts +33 -5
- package/src/gl/touch.ts +14 -15
- package/src/host.d.ts +2 -3
- package/src/inject.editor.ts +7 -0
- package/src/inject.ts +233 -236
- package/src/net/core.ts +6 -5
- package/src/net/index.ts +1 -1
- package/src/net/replication.ts +1 -1
- package/src/plugin.ts +191 -0
- package/src/plugins/gen/camera/contract.d.ts +27 -0
- package/src/plugins/gen/camera/sdk/camera.gen.ts +46 -0
- package/src/plugins/gen/camera/sdk/camera.ts +57 -0
- package/src/plugins/gen/geolocation/contract.d.ts +50 -0
- package/src/plugins/gen/geolocation/sdk/geolocation.gen.ts +54 -0
- package/src/plugins/{geolocation.ts → gen/geolocation/sdk/geolocation.ts} +22 -43
- package/src/plugins/gen/map/contract.d.ts +144 -0
- package/src/plugins/gen/map/sdk/map.gen.ts +88 -0
- package/src/plugins/{map.ts → gen/map/sdk/map.ts} +68 -102
- package/src/plugins/gen/push/contract.d.ts +61 -0
- package/src/plugins/gen/push/sdk/push.gen.ts +60 -0
- package/src/plugins/gen/push/sdk/push.ts +105 -0
- package/src/plugins/gen/qr-scanner/contract.d.ts +16 -0
- package/src/plugins/gen/qr-scanner/sdk/qr-scanner.gen.ts +29 -0
- package/src/plugins/gen/qr-scanner/sdk/qr-scanner.ts +52 -0
- package/src/plugins/permission.ts +5 -4
- package/src/runtime/app.ts +20 -9
- package/src/runtime/appEvents.ts +5 -4
- package/src/runtime/channel.ts +18 -15
- package/src/runtime/clipboard.ts +4 -3
- package/src/runtime/datetime.ts +2 -1
- package/src/runtime/device.ts +17 -15
- package/src/runtime/fetch.ts +30 -20
- package/src/runtime/files.ts +16 -15
- package/src/runtime/input.ts +21 -8
- package/src/runtime/media.ts +50 -46
- package/src/runtime/misc.ts +7 -3
- package/src/runtime/net.ts +8 -7
- package/src/runtime/rpc.ts +1 -3
- package/src/runtime/service.ts +19 -14
- package/src/runtime/share.ts +4 -3
- package/src/runtime/storage.ts +6 -4
- package/src/runtime/touch.ts +32 -0
- package/src/scene/defineScene.ts +61 -365
- package/src/scene/editor.ts +408 -0
- package/src/scene/editorPlugins.ts +3 -3
- package/src/scene/gizmos.ts +154 -148
- package/src/server/db/marci/query.ts +1 -1
- package/src/server/host.ts +1 -1
- package/src/server/runtime.ts +1 -1
- package/src/ui/NativeView.ts +72 -25
- package/src/ui/UI.ts +3 -3
- package/src/ui/UIBottomSheet.ts +16 -17
- package/src/ui/UIButton.ts +54 -16
- package/src/ui/UIContainer.ts +0 -6
- package/src/ui/UIImage.ts +34 -30
- package/src/ui/UIInput.ts +29 -37
- package/src/ui/UIModal.ts +1 -3
- package/src/ui/UINode.ts +347 -297
- package/src/ui/UIPager.ts +93 -78
- package/src/ui/UIPopover.ts +0 -2
- package/src/ui/UIScreen.ts +41 -36
- package/src/ui/UIScrollable.ts +19 -13
- package/src/ui/UISpacer.ts +1 -1
- package/src/ui/UITabs.ts +8 -6
- package/src/ui/UIText.ts +7 -17
- package/src/ui/UIVideo.ts +30 -27
- package/src/ui/UIVirtualizedList.ts +58 -59
- package/src/ui/UIWidget.ts +38 -18
- package/src/ui/colorKeys.gen.ts +37 -0
- package/src/ui/fonts.ts +2 -2
- package/src/ui/presentable.ts +59 -42
- package/src/ui/router.ts +52 -35
- package/src/ui/styleColor.ts +56 -0
- package/src/ui/theme.ts +7 -6
- package/src/ui/transitions.ts +249 -0
- package/src/ui/tree.ts +346 -0
- package/src/version.ts +24 -0
- package/tests/helpers/engineWorld.ts +11 -0
- package/tests/helpers/fakeTree.ts +353 -0
- package/tests/helpers/hostStubs.ts +31 -0
- package/tests/helpers/index.ts +14 -0
- package/tests/helpers/memoryMarci.ts +124 -0
- package/tests/helpers/phases.ts +23 -0
- package/tests/helpers/preload.ts +18 -0
- package/tests/helpers/stubApp.ts +2 -0
- package/tests/helpers/stubDevice.ts +2 -0
- package/tests/helpers/stubFetch.ts +2 -0
- package/tests/helpers/stubInput.ts +2 -0
- package/dist/inject.js +0 -4730
- package/dist/types/core/registry.d.ts +0 -7
- package/dist/types/plugins/camera.d.ts +0 -25
- package/dist/types/plugins/push.d.ts +0 -46
- package/src/bridges.d.ts +0 -1760
- package/src/compile/__tests__/assetIconMacro.test.ts +0 -219
- package/src/compile/__tests__/assetMacro.test.ts +0 -100
- package/src/compile/__tests__/assetName.test.ts +0 -55
- package/src/compile/__tests__/compile.test.ts +0 -310
- package/src/compile/__tests__/detectEntry.test.ts +0 -151
- package/src/compile/__tests__/fontMacro.test.ts +0 -199
- package/src/compile/__tests__/serverSplit.test.ts +0 -27
- package/src/core/__tests__/stateMachine.test.ts +0 -132
- package/src/core/registry.ts +0 -23
- package/src/plugins/camera.ts +0 -81
- package/src/plugins/push.ts +0 -132
- package/src/plugins/qr.ts +0 -73
package/src/ui/UIButton.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { ClickEvent, TouchStartEvent, LongPressEvent } from "../runtime/touch"
|
|
2
|
-
import type { ClickEvent as ClickEventType, TouchStartEvent as TouchStartEventType, LongPressEvent as LongPressEventType } from "../runtime/touch"
|
|
3
|
-
import { type ElementStyle, type ContainerStyle, ContainerElement, buildUI, type DrawableStyle, type UIChildArg, type UIContainerBase, type UINodeChild, type ChildrenFn, type BaseStyle, type Color
|
|
1
|
+
import { ClickEvent, TouchStartEvent, LongPressEvent, MouseEnterEvent } from "../runtime/touch"
|
|
2
|
+
import type { ClickEvent as ClickEventType, TouchStartEvent as TouchStartEventType, LongPressEvent as LongPressEventType, MouseEnterEvent as MouseEnterEventType } from "../runtime/touch"
|
|
3
|
+
import { type ElementStyle, type ContainerStyle, ContainerElement, buildUI, type DrawableStyle, type UIChildArg, type UIContainerBase, type UINodeChild, type ChildrenFn, type BaseStyle, type Color } from "./UINode"
|
|
4
|
+
import { TREE_FLAG_CLICK, TREE_FLAG_HOVER, TREE_FLAG_INTERACTIVE, TREE_FLAG_LONG_PRESS, tree } from "./tree"
|
|
4
5
|
|
|
5
6
|
export type UIButtonStyle = ElementStyle & DrawableStyle & ContainerStyle & { rippleColor?: Color | "default" }
|
|
6
7
|
|
|
7
|
-
/** A tappable container — row + centered children by default. Press feedback via the `
|
|
8
|
-
*
|
|
9
|
-
export interface UIButton extends UIContainerBase<UIButtonStyle
|
|
8
|
+
/** A tappable container — row + centered children by default. Press feedback via the `$pressed`
|
|
9
|
+
* class block or `rippleColor`. */
|
|
10
|
+
export interface UIButton extends UIContainerBase<UIButtonStyle, DrawableStyle & BaseStyle> {
|
|
10
11
|
readonly type: "button",
|
|
11
12
|
|
|
12
13
|
/** Fired on a completed tap/click. */
|
|
@@ -17,8 +18,14 @@ export interface UIButton extends UIContainerBase<UIButtonStyle & { onPressed?:
|
|
|
17
18
|
* `ev.track(...)` the rest of the gesture (like `onTouchStart`), so a hold can flow straight into
|
|
18
19
|
* dragging the element. A handled long-press suppresses the click that would follow the release. */
|
|
19
20
|
onLongPress(callback: (ev: LongPressEventType<UIButton>) => void): this,
|
|
21
|
+
/** Fired when a mouse cursor (no button) enters the button — desktop / web; touch never fires it.
|
|
22
|
+
* There is no leave event: `ev.track({ onMove, onEnd, onCancel })` follows the hover to its
|
|
23
|
+
* end. The visual state is the reserved `$hovered` class (cascades like `$pressed`). */
|
|
24
|
+
onMouseEnter(callback: (ev: MouseEnterEventType<UIButton>) => void): this,
|
|
20
25
|
/** Is a finger currently down on the button? */
|
|
21
|
-
isPressed
|
|
26
|
+
readonly isPressed: boolean,
|
|
27
|
+
/** Is a mouse cursor currently over the button (the topmost interactive node under it)? */
|
|
28
|
+
readonly isHovered: boolean,
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
|
|
@@ -27,11 +34,19 @@ class ButtonElement extends ContainerElement<"button"> {
|
|
|
27
34
|
readonly clickListeners: any[] = []
|
|
28
35
|
readonly touchStartListeners: any[] = []
|
|
29
36
|
readonly longPressListeners: any[] = []
|
|
37
|
+
readonly mouseEnterListeners: any[] = []
|
|
38
|
+
|
|
39
|
+
constructor(type: "button", style: any, children: any) {
|
|
40
|
+
super(type, style, children)
|
|
41
|
+
this._addFlags(TREE_FLAG_INTERACTIVE) // a button is a touch target from the start (press feedback)
|
|
42
|
+
}
|
|
30
43
|
|
|
31
44
|
onClick (callback: any): this {
|
|
32
45
|
this.clickListeners.push(callback)
|
|
46
|
+
this._addFlags(TREE_FLAG_CLICK)
|
|
33
47
|
return this
|
|
34
48
|
}
|
|
49
|
+
/** @internal */
|
|
35
50
|
_emitClick(pointerId: number, clientX: number, clientY: number) {
|
|
36
51
|
const touchEvent = new ClickEvent(clientX, clientY, pointerId)
|
|
37
52
|
touchEvent.target = this
|
|
@@ -45,6 +60,7 @@ class ButtonElement extends ContainerElement<"button"> {
|
|
|
45
60
|
this.touchStartListeners.push(callback)
|
|
46
61
|
return this
|
|
47
62
|
}
|
|
63
|
+
/** @internal */
|
|
48
64
|
_emitTouchStart(pointerId: number, clientX: number, clientY: number): any {
|
|
49
65
|
const touchEvent = new TouchStartEvent(clientX, clientY, pointerId)
|
|
50
66
|
touchEvent.target = this
|
|
@@ -59,12 +75,13 @@ class ButtonElement extends ContainerElement<"button"> {
|
|
|
59
75
|
|
|
60
76
|
onLongPress (callback: any): this {
|
|
61
77
|
this.longPressListeners.push(callback)
|
|
78
|
+
this._addFlags(TREE_FLAG_LONG_PRESS)
|
|
62
79
|
return this
|
|
63
80
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
81
|
+
/** @internal Fired by the host when the finger is held past the long-press threshold. Returns
|
|
82
|
+
* null when nothing is listening — the trailing click then goes through as usual. Otherwise the
|
|
83
|
+
* wrapper whose presence swallows that click, carrying the optional drag handler a listener
|
|
84
|
+
* registered via `ev.track(...)`. */
|
|
68
85
|
_emitLongPress(pointerId: number, clientX: number, clientY: number): any {
|
|
69
86
|
if (this.longPressListeners.length === 0) return null
|
|
70
87
|
const longPressEvent = new LongPressEvent(clientX, clientY, pointerId)
|
|
@@ -75,9 +92,32 @@ class ButtonElement extends ContainerElement<"button"> {
|
|
|
75
92
|
return { track: (longPressEvent as any)._trackHandler ?? null }
|
|
76
93
|
}
|
|
77
94
|
|
|
78
|
-
isPressed(): boolean {
|
|
79
|
-
if (!this.
|
|
80
|
-
return
|
|
95
|
+
get isPressed(): boolean {
|
|
96
|
+
if (!this._h.id) return false
|
|
97
|
+
return tree().isButtonPressed(this._h.id)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
onMouseEnter (callback: any): this {
|
|
101
|
+
this.mouseEnterListeners.push(callback)
|
|
102
|
+
this._addFlags(TREE_FLAG_HOVER) // the runtime crosses into JS for hover only on flagged nodes
|
|
103
|
+
return this
|
|
104
|
+
}
|
|
105
|
+
/** @internal Fired by the runtime when the cursor enters (TREE_EVENT_HOVER_ENTER). Returns the
|
|
106
|
+
* hover track handler a listener registered via `ev.track(...)`, or null — the emitter keeps it
|
|
107
|
+
* in its one hover slot and answers whether moves are wanted. */
|
|
108
|
+
_emitMouseEnter(clientX: number, clientY: number): any {
|
|
109
|
+
const ev = new MouseEnterEvent(clientX, clientY)
|
|
110
|
+
ev.target = this
|
|
111
|
+
for (let callback of this.mouseEnterListeners) {
|
|
112
|
+
callback(ev)
|
|
113
|
+
if ((ev as any)._trackHandler) return (ev as any)._trackHandler
|
|
114
|
+
}
|
|
115
|
+
return null
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
get isHovered(): boolean {
|
|
119
|
+
if (!this._h.id) return false
|
|
120
|
+
return tree().isHovered(this._h.id)
|
|
81
121
|
}
|
|
82
122
|
}
|
|
83
123
|
|
|
@@ -94,8 +134,6 @@ export const __UIButton = (children: UINodeChild[] | ChildrenFn): UIButton => ma
|
|
|
94
134
|
* `UIColumn`; attach behavior with `.onClick(...)`. */
|
|
95
135
|
export function UIButton(...children: UIChildArg[]): UIButton;
|
|
96
136
|
export function UIButton(children: UINodeChild[] | ChildrenFn): UIButton;
|
|
97
|
-
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
|
|
98
|
-
export function UIButton(style: UIButtonStyle, children?: UINodeChild[] | ChildrenFn): UIButton;
|
|
99
137
|
export function UIButton(...args: any[]): UIButton {
|
|
100
138
|
return buildUI(args, BUTTON_DEFAULTS, makeButton)
|
|
101
139
|
}
|
package/src/ui/UIContainer.ts
CHANGED
|
@@ -19,8 +19,6 @@ export const __UIBox = (children: UINodeChild[] | ChildrenFn): UIBox => makeBox(
|
|
|
19
19
|
* entries are skipped; a lone function makes the children reactive. */
|
|
20
20
|
export function UIColumn(...children: UIChildArg[]): UIColumn
|
|
21
21
|
export function UIColumn(children: UINodeChild[] | ChildrenFn): UIColumn
|
|
22
|
-
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
|
|
23
|
-
export function UIColumn(style: UIContainerStyle, children?: UINodeChild[] | ChildrenFn): UIColumn
|
|
24
22
|
export function UIColumn(...args: any[]): UIColumn {
|
|
25
23
|
return buildUI(args, null, makeColumn)
|
|
26
24
|
}
|
|
@@ -35,8 +33,6 @@ const makeRow = (style: any, children: any) => new ContainerElement("row", style
|
|
|
35
33
|
/** Create a horizontal flex container (same argument forms as `UIColumn`). */
|
|
36
34
|
export function UIRow(...children: UIChildArg[]): UIRow
|
|
37
35
|
export function UIRow(children: UINodeChild[] | ChildrenFn): UIRow
|
|
38
|
-
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
|
|
39
|
-
export function UIRow(style: UIContainerStyle, children?: UINodeChild[] | ChildrenFn): UIRow
|
|
40
36
|
export function UIRow(...args: any[]): UIRow {
|
|
41
37
|
return buildUI(args, ROW_DEFAULTS, makeRow)
|
|
42
38
|
}
|
|
@@ -53,8 +49,6 @@ const makeBox = (style: any, children: any) => new ContainerElement("box", style
|
|
|
53
49
|
* avatars, empty states). Same argument forms as `UIColumn`. */
|
|
54
50
|
export function UIBox(...children: UIChildArg[]): UIBox
|
|
55
51
|
export function UIBox(children: UINodeChild[] | ChildrenFn): UIBox
|
|
56
|
-
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
|
|
57
|
-
export function UIBox(style: UIContainerStyle, children?: UINodeChild[] | ChildrenFn): UIBox
|
|
58
52
|
export function UIBox(...args: any[]): UIBox {
|
|
59
53
|
return buildUI(args, BOX_DEFAULTS, makeBox)
|
|
60
54
|
}
|
package/src/ui/UIImage.ts
CHANGED
|
@@ -4,11 +4,12 @@ import type { File } from "../runtime/fetch"
|
|
|
4
4
|
import { Canvas, isCanvas } from "../canvas/Canvas"
|
|
5
5
|
import type { Scene2D } from "../g2/Scene2D"
|
|
6
6
|
import { Element, type Color, type ElementStyle, type UIElementBase } from "./UINode"
|
|
7
|
+
import { wireColor } from "./styleColor"
|
|
8
|
+
import { tree } from "./tree"
|
|
7
9
|
|
|
8
10
|
export type UIImageStyle = ElementStyle & { borderRadius?: number, objectFit?: "cover" | "contain" | "fill", tintColor?: Color }
|
|
9
11
|
|
|
10
|
-
/** A baked Canvas surface as an image source. The UI backend resolves it to a UI texture
|
|
11
|
-
* docs/canvas-contract.md ("Pending"). */
|
|
12
|
+
/** A baked Canvas surface as an image source. The UI backend resolves it to a UI texture. */
|
|
12
13
|
export type CanvasImageSource = { canvasSurface: number }
|
|
13
14
|
|
|
14
15
|
/** A live 2D scene as an image source: the host draws the scene into the node's box every frame
|
|
@@ -50,48 +51,51 @@ export interface UIImage extends UIElementBase<UIImageStyle> {
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
class ImageElement extends Element<"image"> {
|
|
53
|
-
private _src
|
|
54
|
-
// Current source sub-rect; undefined until the first setSourceRect. Kept on the node (not
|
|
55
|
-
// just sent as a command) so a crop set while detached (_id == 0) is reapplied at mount —
|
|
56
|
-
// the viewer reads it when it creates the element.
|
|
57
|
-
private _sourceRect?: [number, number, number, number]
|
|
54
|
+
private _src!: ResolvedImageSource
|
|
58
55
|
constructor(style: any, src: ImageSource) {
|
|
59
56
|
super("image", style)
|
|
60
|
-
this.
|
|
57
|
+
this.src = src
|
|
61
58
|
}
|
|
62
59
|
|
|
63
60
|
get src(): ResolvedImageSource {
|
|
64
61
|
return this._src
|
|
65
62
|
}
|
|
66
63
|
set src(value: ImageSource) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
const next = toSrc(value)
|
|
65
|
+
// A source tint that is not a colour throws BEFORE anything is applied, like a style colour.
|
|
66
|
+
const n: any = next
|
|
67
|
+
const tint = n && typeof n === "object" && "svg" in n && n.tintColor != null ? wireColor("tintColor", n.tintColor) as string : null
|
|
68
|
+
this._src = next
|
|
69
|
+
if (this._h.id === 0) return
|
|
70
|
+
// The wire spellings of a source (the host resolves each prefix): a url as is, "id:<bufferId>"
|
|
71
|
+
// for a fetch / asset buffer, "svg:<source>" (+ "srcTintColor", '#rrggbbaa' or a var(), AFTER
|
|
72
|
+
// the src: a src write resets the source tint), "canvas:<surfaceId>", "scene2d:<sceneId>".
|
|
73
|
+
const s: any = this._src
|
|
74
|
+
if (typeof s === "string") {
|
|
75
|
+
tree().setString(this._h.id, "src", s)
|
|
76
|
+
} else if (s && typeof s === "object") {
|
|
77
|
+
if ("svg" in s) {
|
|
78
|
+
tree().setString(this._h.id, "src", "svg:" + s.svg)
|
|
79
|
+
if (tint != null) tree().setString(this._h.id, "srcTintColor", tint)
|
|
80
|
+
} else if ("canvasSurface" in s) {
|
|
81
|
+
tree().setString(this._h.id, "src", "canvas:" + s.canvasSurface)
|
|
82
|
+
} else if ("scene2d" in s) {
|
|
83
|
+
tree().setString(this._h.id, "src", "scene2d:" + s.scene2d)
|
|
84
|
+
} else if ("_h" in s) {
|
|
85
|
+
tree().setString(this._h.id, "src", "id:" + s._h.id)
|
|
86
|
+
}
|
|
70
87
|
}
|
|
71
88
|
}
|
|
72
89
|
|
|
73
90
|
setSourceRect(x: number, y: number, w: number, h: number): this {
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
// pixels, a different coordinate space). On a detached node there is nothing to mutate yet —
|
|
78
|
-
// the stash above carries it to mount. The viewer decides relayout vs draw-only by comparing
|
|
79
|
-
// w/h against the rect it has stashed on the element.
|
|
80
|
-
if (this._id !== 0) {
|
|
81
|
-
_creatorUI.setSourceRect(this._id, x, y, w, h)
|
|
82
|
-
}
|
|
91
|
+
// Texture pixels (not a style → no density scaling); the host decides relayout vs draw-only by
|
|
92
|
+
// comparing w/h against the rect it has.
|
|
93
|
+
if (this._h.id !== 0) tree().setString(this._h.id, "sourceRect", `${x} ${y} ${w} ${h}`)
|
|
83
94
|
return this
|
|
84
95
|
}
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
/** Create an image element from a source (see {@link ImageSource}). */
|
|
88
|
-
export function UIImage(src: ImageSource): UIImage
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
export function UIImage(...args: [ImageSource] | [UIImageStyle, ImageSource]): UIImage {
|
|
92
|
-
if (args.length === 1) {
|
|
93
|
-
return new ImageElement({}, args[0])
|
|
94
|
-
} else {
|
|
95
|
-
return new ImageElement(args[0], args[1])
|
|
96
|
-
}
|
|
97
|
-
}
|
|
99
|
+
export function UIImage(src: ImageSource): UIImage {
|
|
100
|
+
return new ImageElement({}, src)
|
|
101
|
+
}
|
package/src/ui/UIInput.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type ElementStyle, type DrawableStyle, Element, type TextStyle, type Color, type BaseStyle, type UIElementBase
|
|
1
|
+
import { type ElementStyle, type DrawableStyle, Element, type TextStyle, type Color, type BaseStyle, type UIElementBase } from "./UINode";
|
|
2
|
+
import { TREE_FLAG_BLUR, TREE_FLAG_CHANGE, TREE_FLAG_FOCUS, TREE_FLAG_SUBMIT, tree } from "./tree"
|
|
2
3
|
|
|
3
4
|
type InputStyle = {
|
|
4
5
|
placeholder?: string,
|
|
@@ -51,15 +52,11 @@ type SubmitStyle = {
|
|
|
51
52
|
|
|
52
53
|
/** A single-line text input. Keyboard/picker kind via the `type` style, return-key behavior via
|
|
53
54
|
* `enterKey` + `onSubmit`; keyboard policy on `keyboardShrink`/`keyboardDismiss`. */
|
|
54
|
-
export interface UIInput extends UIElementBase<UIInputStyle & SubmitStyle
|
|
55
|
+
export interface UIInput extends UIElementBase<UIInputStyle & SubmitStyle, DrawableStyle & BaseStyle> {
|
|
55
56
|
readonly type: "input",
|
|
56
57
|
|
|
57
|
-
/**
|
|
58
|
-
*
|
|
59
|
-
* (settings, edit-profile); the value is stashed on the node and applied when the host builds
|
|
60
|
-
* it. Reads return the live text while the screen is open, and the last value you set while it
|
|
61
|
-
* isn't. The user's typing is not preserved across close/open (use `keepAlive()` for that).
|
|
62
|
-
*/
|
|
58
|
+
/** The field's text. Writable at any time; reads return the live text (the host owns the field
|
|
59
|
+
* while the user types — IME composition, paste, the maxLength clamp, date/time canonicalization). */
|
|
63
60
|
value: string
|
|
64
61
|
|
|
65
62
|
/** The text changed — fires on every user edit. */
|
|
@@ -71,17 +68,17 @@ export interface UIInput extends UIElementBase<UIInputStyle & SubmitStyle & { on
|
|
|
71
68
|
/** The keyboard's return key was pressed. `value` is the current text. See `enterKey`. */
|
|
72
69
|
onSubmit(callback: (value: string) => void): UIInput
|
|
73
70
|
|
|
74
|
-
/** Focus the input (opens the keyboard).
|
|
71
|
+
/** Focus the input (opens the keyboard). */
|
|
75
72
|
focus(): void
|
|
76
73
|
/** Release focus (dismisses the keyboard when this input held it). */
|
|
77
74
|
blur(): void
|
|
78
75
|
}
|
|
79
76
|
|
|
80
77
|
/** A multi-line text field — see the {@link UITextArea} factory for the auto-grow contract. */
|
|
81
|
-
export interface UITextArea extends UIElementBase<UIInputStyle
|
|
78
|
+
export interface UITextArea extends UIElementBase<UIInputStyle, DrawableStyle & BaseStyle> {
|
|
82
79
|
readonly type: "textarea",
|
|
83
80
|
|
|
84
|
-
/** The field's text — same contract as {@link UIInput.value}
|
|
81
|
+
/** The field's text — same contract as {@link UIInput.value}. */
|
|
85
82
|
value: string
|
|
86
83
|
|
|
87
84
|
/** The text changed — fires on every user edit. */
|
|
@@ -91,7 +88,7 @@ export interface UITextArea extends UIElementBase<UIInputStyle & { onFocused?: U
|
|
|
91
88
|
/** Lost focus. */
|
|
92
89
|
onBlur(callback: () => void): UITextArea
|
|
93
90
|
|
|
94
|
-
/** Focus the text area (opens the keyboard).
|
|
91
|
+
/** Focus the text area (opens the keyboard). */
|
|
95
92
|
focus(): void
|
|
96
93
|
/** Release focus (dismisses the keyboard when this text area held it). */
|
|
97
94
|
blur(): void
|
|
@@ -104,25 +101,12 @@ class InputElement<T extends "input" | "textarea"> extends Element<T> {
|
|
|
104
101
|
readonly bl: Function[] = []
|
|
105
102
|
readonly sl: Function[] = []
|
|
106
103
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
* Same pre-mount stash as UIImage's `_sourceRect`. Typing does NOT sync back here: while
|
|
111
|
-
* mounted the HOST owns the text (IME composition, paste, the maxLength clamp, date/time
|
|
112
|
-
* canonicalization) and the getter reads it over the bridge. So on a remount the field comes
|
|
113
|
-
* back with the last code-set value, not what the user typed — screens that must keep live
|
|
114
|
-
* editing state across close/open use `keepAlive()`, the mechanism built for it. */
|
|
115
|
-
_value = ""
|
|
116
|
-
|
|
117
|
-
get value() {
|
|
118
|
-
// Mounted: the host is authoritative. Unmounted: what code last set (0 => never mounted).
|
|
119
|
-
return this._id !== 0 ? _creatorUI.getTextValue(this._id) : this._value
|
|
104
|
+
get value(): string {
|
|
105
|
+
// The host is authoritative for the field's text (typing never round-trips through JS).
|
|
106
|
+
return this._h.id !== 0 ? tree().getTextValue(this._h.id) : ""
|
|
120
107
|
}
|
|
121
108
|
set value(value: string) {
|
|
122
|
-
this.
|
|
123
|
-
if (this._id !== 0) {
|
|
124
|
-
_creatorUI.updateNode(this._id, "value", value)
|
|
125
|
-
}
|
|
109
|
+
if (this._h.id !== 0) tree().setString(this._h.id, "value", value)
|
|
126
110
|
}
|
|
127
111
|
|
|
128
112
|
// `phone` is SDK-surface only — hosts (and the web's verbatim `el.type`) keep HTML's `tel` as
|
|
@@ -133,31 +117,39 @@ class InputElement<T extends "input" | "textarea"> extends Element<T> {
|
|
|
133
117
|
|
|
134
118
|
onChange (callback: (value: string) => void): this {
|
|
135
119
|
this.cl.push(callback)
|
|
120
|
+
this._addFlags(TREE_FLAG_CHANGE)
|
|
136
121
|
return this
|
|
137
122
|
}
|
|
138
123
|
onFocus(callback: () => void): this {
|
|
139
124
|
this.fl.push(callback)
|
|
125
|
+
this._addFlags(TREE_FLAG_FOCUS)
|
|
140
126
|
return this
|
|
141
127
|
}
|
|
142
128
|
onBlur(callback: () => void): this {
|
|
143
129
|
this.bl.push(callback)
|
|
130
|
+
this._addFlags(TREE_FLAG_BLUR)
|
|
144
131
|
return this
|
|
145
132
|
}
|
|
146
133
|
onSubmit(callback: (value: string) => void): this {
|
|
147
134
|
this.sl.push(callback)
|
|
135
|
+
this._addFlags(TREE_FLAG_SUBMIT)
|
|
148
136
|
return this
|
|
149
137
|
}
|
|
150
|
-
|
|
151
|
-
|
|
138
|
+
/** @internal */
|
|
139
|
+
_emitChange(value: string): void { for (const cb of this.cl.slice()) cb(value) }
|
|
140
|
+
/** @internal */
|
|
141
|
+
_emitFocus(): void { for (const cb of this.fl.slice()) cb() }
|
|
142
|
+
/** @internal */
|
|
143
|
+
_emitBlur(): void { for (const cb of this.bl.slice()) cb() }
|
|
144
|
+
/** @internal */
|
|
145
|
+
_emitSubmit(value: string): void { for (const cb of this.sl.slice()) cb(value) }
|
|
146
|
+
|
|
147
|
+
// Focus rides the content channel ("focus" = "1" / "0") — no dedicated bridge method.
|
|
152
148
|
focus(): void {
|
|
153
|
-
if (this.
|
|
154
|
-
_creatorUI.updateNode(this._id, "focus", true)
|
|
155
|
-
}
|
|
149
|
+
if (this._h.id !== 0) tree().setString(this._h.id, "focus", "1")
|
|
156
150
|
}
|
|
157
151
|
blur(): void {
|
|
158
|
-
if (this.
|
|
159
|
-
_creatorUI.updateNode(this._id, "focus", false)
|
|
160
|
-
}
|
|
152
|
+
if (this._h.id !== 0) tree().setString(this._h.id, "focus", "0")
|
|
161
153
|
}
|
|
162
154
|
}
|
|
163
155
|
|
package/src/ui/UIModal.ts
CHANGED
|
@@ -63,7 +63,7 @@ export class ModalElement extends WidgetElement {
|
|
|
63
63
|
constructor(style: UIModalStyle, children: UINodeChild[] | ChildrenFn) {
|
|
64
64
|
super("widget", { overlayColor: "rgba(0, 0, 0, 0.5)", ...style }, children)
|
|
65
65
|
this.onOverlayTap(() => { if (this._dismissible) this.hide() })
|
|
66
|
-
this.
|
|
66
|
+
this.onBack(() => { if (this._dismissible) this.hide() })
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// The full animate argument: hidden pose + duration, plus the scrim fade — only when the
|
|
@@ -132,8 +132,6 @@ const makeModal = (style: any, children: any) => new ModalElement(style, childre
|
|
|
132
132
|
* `show()`/`hide()`. Same argument forms as `UIColumn`. */
|
|
133
133
|
export function UIModal(...children: UIChildArg[]): UIModal;
|
|
134
134
|
export function UIModal(children: UINodeChild[] | ChildrenFn): UIModal;
|
|
135
|
-
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
|
|
136
|
-
export function UIModal(style: UIModalStyle, children?: UINodeChild[] | ChildrenFn): UIModal;
|
|
137
135
|
export function UIModal(...args: any[]): UIModal {
|
|
138
136
|
return buildUI(args, null, makeModal)
|
|
139
137
|
}
|