lecodes-sdk 0.19.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 +114 -0
- package/dist/global.d.ts +503 -0
- package/dist/host.d.ts +61 -0
- package/dist/inject.js +4730 -0
- package/dist/types/animate/animate.d.ts +20 -0
- package/dist/types/animate/bezier.d.ts +2 -0
- package/dist/types/animate/easings.d.ts +21 -0
- package/dist/types/canvas/Canvas.d.ts +168 -0
- package/dist/types/core/Aspect.d.ts +171 -0
- package/dist/types/core/InspectorUI.d.ts +88 -0
- package/dist/types/core/StateMachine.d.ts +81 -0
- package/dist/types/core/color.d.ts +13 -0
- package/dist/types/core/compWrite.d.ts +11 -0
- package/dist/types/core/events.d.ts +11 -0
- package/dist/types/core/fields.d.ts +58 -0
- package/dist/types/core/registry.d.ts +7 -0
- package/dist/types/core/signals.d.ts +29 -0
- package/dist/types/core/time.d.ts +31 -0
- package/dist/types/g2/Camera2D.d.ts +19 -0
- package/dist/types/g2/CharacterController2D.d.ts +134 -0
- package/dist/types/g2/Node2D.d.ts +91 -0
- package/dist/types/g2/OneWay2D.d.ts +24 -0
- package/dist/types/g2/Physics2D.d.ts +147 -0
- package/dist/types/g2/Scene2D.d.ts +72 -0
- package/dist/types/g2/Shape2D.d.ts +58 -0
- package/dist/types/g2/Sprite.d.ts +51 -0
- package/dist/types/g2/SpriteAnimation.d.ts +50 -0
- package/dist/types/g2/SpriteSheet.d.ts +69 -0
- package/dist/types/g2/Texture2D.d.ts +16 -0
- package/dist/types/g2/Tilemap.d.ts +20 -0
- package/dist/types/g2/Tileset.d.ts +32 -0
- package/dist/types/g2/Trigger2D.d.ts +20 -0
- package/dist/types/g2/autotile.d.ts +133 -0
- package/dist/types/g2/cells.d.ts +14 -0
- package/dist/types/g2/defineScene2d.d.ts +141 -0
- package/dist/types/g2/groups2d.d.ts +16 -0
- package/dist/types/g2/loop.d.ts +4 -0
- package/dist/types/g2/scenarios2d.d.ts +30 -0
- package/dist/types/g2/touch.d.ts +2 -0
- package/dist/types/gl/Camera.d.ts +79 -0
- package/dist/types/gl/CameraPlace.d.ts +26 -0
- package/dist/types/gl/CharacterController.d.ts +98 -0
- package/dist/types/gl/Gearbox.d.ts +86 -0
- package/dist/types/gl/Geometry.d.ts +39 -0
- package/dist/types/gl/IK.d.ts +53 -0
- package/dist/types/gl/InstancedMesh.d.ts +46 -0
- package/dist/types/gl/Light.d.ts +73 -0
- package/dist/types/gl/Lightmap.d.ts +85 -0
- package/dist/types/gl/Locomotion.d.ts +279 -0
- package/dist/types/gl/Material.d.ts +100 -0
- package/dist/types/gl/Mesh.d.ts +33 -0
- package/dist/types/gl/Model.d.ts +53 -0
- package/dist/types/gl/NavAgent.d.ts +99 -0
- package/dist/types/gl/NavMesh.d.ts +169 -0
- package/dist/types/gl/Node.d.ts +99 -0
- package/dist/types/gl/Noise.d.ts +20 -0
- package/dist/types/gl/Particles.d.ts +277 -0
- package/dist/types/gl/Physics.d.ts +91 -0
- package/dist/types/gl/Plane.d.ts +19 -0
- package/dist/types/gl/Ragdoll.d.ts +86 -0
- package/dist/types/gl/Ray.d.ts +8 -0
- package/dist/types/gl/Scene.d.ts +221 -0
- package/dist/types/gl/Shape.d.ts +122 -0
- package/dist/types/gl/Terrain.d.ts +280 -0
- package/dist/types/gl/Texture.d.ts +27 -0
- package/dist/types/gl/Trigger.d.ts +10 -0
- package/dist/types/gl/Vehicle.d.ts +191 -0
- package/dist/types/gl/Wheel.d.ts +95 -0
- package/dist/types/gl/animation/AnimationClip.d.ts +60 -0
- package/dist/types/gl/animation/Animator.d.ts +219 -0
- package/dist/types/gl/animation/Layer.d.ts +31 -0
- package/dist/types/gl/animation/Loop.d.ts +17 -0
- package/dist/types/gl/animation/Playback.d.ts +36 -0
- package/dist/types/gl/animation/core.d.ts +367 -0
- package/dist/types/gl/controls.d.ts +19 -0
- package/dist/types/gl/physicsEvents.d.ts +1 -0
- package/dist/types/gl/scenarios.d.ts +98 -0
- package/dist/types/gl/state.d.ts +5 -0
- package/dist/types/gl/terrainMesh.d.ts +46 -0
- package/dist/types/gl/touch.d.ts +2 -0
- package/dist/types/inject.d.ts +123 -0
- package/dist/types/math/Mathf.d.ts +39 -0
- package/dist/types/math/mat4.d.ts +66 -0
- package/dist/types/math/quat.d.ts +53 -0
- package/dist/types/math/vec.d.ts +129 -0
- package/dist/types/net/codec.d.ts +25 -0
- package/dist/types/net/core.d.ts +106 -0
- package/dist/types/net/index.d.ts +80 -0
- package/dist/types/net/replication.d.ts +101 -0
- package/dist/types/plugins/camera.d.ts +25 -0
- package/dist/types/plugins/geolocation.d.ts +39 -0
- package/dist/types/plugins/oauth.d.ts +25 -0
- package/dist/types/plugins/permission.d.ts +1 -0
- package/dist/types/plugins/push.d.ts +46 -0
- package/dist/types/plugins/qr.d.ts +21 -0
- package/dist/types/plugins/service.d.ts +33 -0
- package/dist/types/runtime/app.d.ts +67 -0
- package/dist/types/runtime/appEvents.d.ts +7 -0
- package/dist/types/runtime/channel.d.ts +1 -0
- package/dist/types/runtime/clipboard.d.ts +7 -0
- package/dist/types/runtime/datetime.d.ts +80 -0
- package/dist/types/runtime/device.d.ts +126 -0
- package/dist/types/runtime/fetch.d.ts +31 -0
- package/dist/types/runtime/files.d.ts +33 -0
- package/dist/types/runtime/input.d.ts +121 -0
- package/dist/types/runtime/media.d.ts +46 -0
- package/dist/types/runtime/misc.d.ts +11 -0
- package/dist/types/runtime/net.d.ts +15 -0
- package/dist/types/runtime/rpc.d.ts +44 -0
- package/dist/types/runtime/service.d.ts +1 -0
- package/dist/types/runtime/share.d.ts +2 -0
- package/dist/types/runtime/storage.d.ts +5 -0
- package/dist/types/runtime/touch.d.ts +56 -0
- package/dist/types/scene/defineScene.d.ts +296 -0
- package/dist/types/scene/editorPlugins.d.ts +82 -0
- package/dist/types/scene/gizmos.d.ts +56 -0
- package/dist/types/scene/grammar.d.ts +56 -0
- package/dist/types/scene/level.d.ts +98 -0
- package/dist/types/scene/material.d.ts +76 -0
- package/dist/types/server/auth/appConfig.d.ts +16 -0
- package/dist/types/server/auth/global.d.ts +56 -0
- package/dist/types/server/auth/models.d.ts +164 -0
- package/dist/types/server/auth/types.d.ts +56 -0
- package/dist/types/server/channel.d.ts +28 -0
- package/dist/types/server/context.d.ts +27 -0
- package/dist/types/server/db/defineDb.d.ts +36 -0
- package/dist/types/server/db/fields.d.ts +146 -0
- package/dist/types/server/db/index.d.ts +6 -0
- package/dist/types/server/db/marci/query.d.ts +326 -0
- package/dist/types/server/db/types.d.ts +163 -0
- package/dist/types/server/errors.d.ts +8 -0
- package/dist/types/server/inject.d.ts +5 -0
- package/dist/types/ui/NativeView.d.ts +38 -0
- package/dist/types/ui/UI.d.ts +27 -0
- package/dist/types/ui/UIBottomSheet.d.ts +71 -0
- package/dist/types/ui/UIButton.d.ts +30 -0
- package/dist/types/ui/UIContainer.d.ts +33 -0
- package/dist/types/ui/UIImage.d.ts +35 -0
- package/dist/types/ui/UIInput.d.ts +110 -0
- package/dist/types/ui/UIModal.d.ts +70 -0
- package/dist/types/ui/UINode.d.ts +363 -0
- package/dist/types/ui/UIPager.d.ts +123 -0
- package/dist/types/ui/UIPopover.d.ts +39 -0
- package/dist/types/ui/UIScreen.d.ts +57 -0
- package/dist/types/ui/UIScrollable.d.ts +40 -0
- package/dist/types/ui/UISpacer.d.ts +9 -0
- package/dist/types/ui/UITabs.d.ts +96 -0
- package/dist/types/ui/UIText.d.ts +19 -0
- package/dist/types/ui/UIVideo.d.ts +28 -0
- package/dist/types/ui/UIVirtualizedList.d.ts +97 -0
- package/dist/types/ui/UIWidget.d.ts +65 -0
- package/dist/types/ui/fonts.d.ts +9 -0
- package/dist/types/ui/presentable.d.ts +68 -0
- package/dist/types/ui/router.d.ts +40 -0
- package/dist/types/ui/theme.d.ts +26 -0
- package/dist/types.json +1 -0
- package/package.json +46 -0
- package/prompts/2d.md +395 -0
- package/prompts/3d-scene-files.md +113 -0
- package/prompts/3d-scene.md +119 -0
- package/prompts/3d.md +303 -0
- package/prompts/README.md +142 -0
- package/prompts/ar.md +109 -0
- package/prompts/canvas.md +27 -0
- package/prompts/compose.ts +46 -0
- package/prompts/concept.md +49 -0
- package/prompts/core-design.md +75 -0
- package/prompts/core.md +264 -0
- package/prompts/design.md +218 -0
- package/prompts/dist/2d-game.md +1530 -0
- package/prompts/dist/3d-app.md +1672 -0
- package/prompts/dist/ar-app.md +1548 -0
- package/prompts/dist/concept.md +49 -0
- package/prompts/dist/design.md +498 -0
- package/prompts/dist/ui-app.md +1134 -0
- package/prompts/index.md +37 -0
- package/prompts/intake-prompt.md +33 -0
- package/prompts/namer-prompt.md +12 -0
- package/prompts/router-prompt.md +50 -0
- package/prompts/select.ts +111 -0
- package/prompts/ui-design.md +203 -0
- package/prompts/ui.md +803 -0
- package/src/animate/animate.ts +238 -0
- package/src/animate/bezier.ts +138 -0
- package/src/animate/easings.ts +126 -0
- package/src/bridges.d.ts +1345 -0
- package/src/canvas/Canvas.ts +305 -0
- package/src/compile/__tests__/assetIconMacro.test.ts +219 -0
- package/src/compile/__tests__/assetMacro.test.ts +74 -0
- package/src/compile/__tests__/assetName.test.ts +55 -0
- package/src/compile/__tests__/compile.test.ts +287 -0
- package/src/compile/__tests__/detectEntry.test.ts +132 -0
- package/src/compile/__tests__/fontMacro.test.ts +199 -0
- package/src/compile/aspectMacro.ts +86 -0
- package/src/compile/assetIconMacro.ts +384 -0
- package/src/compile/assetMacro.ts +146 -0
- package/src/compile/assetName.ts +50 -0
- package/src/compile/bundler.ts +287 -0
- package/src/compile/compileProject.ts +127 -0
- package/src/compile/detectEntry.ts +128 -0
- package/src/compile/fontMacro.ts +459 -0
- package/src/compile/fontRegistry.ts +78 -0
- package/src/compile/header.ts +67 -0
- package/src/compile/index.ts +100 -0
- package/src/compile/libraryImports.ts +52 -0
- package/src/compile/liteMaterial.ts +247 -0
- package/src/compile/sceneEditor.ts +88 -0
- package/src/compile/serverSplit.ts +233 -0
- package/src/compile/serverTypes.ts +227 -0
- package/src/compile/sfnt.ts +98 -0
- package/src/compile/shaderSchema.ts +202 -0
- package/src/compile/shaderTargets.ts +77 -0
- package/src/compile/sourcemap.ts +25 -0
- package/src/core/Aspect.ts +568 -0
- package/src/core/InspectorUI.ts +212 -0
- package/src/core/StateMachine.ts +308 -0
- package/src/core/__tests__/stateMachine.test.ts +132 -0
- package/src/core/color.ts +66 -0
- package/src/core/compWrite.ts +42 -0
- package/src/core/events.ts +38 -0
- package/src/core/fields.ts +120 -0
- package/src/core/registry.ts +23 -0
- package/src/core/signals.ts +277 -0
- package/src/core/time.ts +84 -0
- package/src/g2/Camera2D.ts +40 -0
- package/src/g2/CharacterController2D.ts +276 -0
- package/src/g2/Node2D.ts +267 -0
- package/src/g2/OneWay2D.ts +66 -0
- package/src/g2/Physics2D.ts +346 -0
- package/src/g2/Scene2D.ts +209 -0
- package/src/g2/Shape2D.ts +259 -0
- package/src/g2/Sprite.ts +89 -0
- package/src/g2/SpriteAnimation.ts +171 -0
- package/src/g2/SpriteSheet.ts +166 -0
- package/src/g2/Texture2D.ts +47 -0
- package/src/g2/Tilemap.ts +41 -0
- package/src/g2/Tileset.ts +71 -0
- package/src/g2/Trigger2D.ts +77 -0
- package/src/g2/autotile.ts +433 -0
- package/src/g2/cells.ts +91 -0
- package/src/g2/defineScene2d.ts +381 -0
- package/src/g2/groups2d.ts +106 -0
- package/src/g2/loop.ts +50 -0
- package/src/g2/scenarios2d.ts +69 -0
- package/src/g2/touch.ts +83 -0
- package/src/gl/Camera.ts +160 -0
- package/src/gl/CameraPlace.ts +52 -0
- package/src/gl/CharacterController.ts +238 -0
- package/src/gl/Gearbox.ts +212 -0
- package/src/gl/Geometry.ts +279 -0
- package/src/gl/IK.ts +193 -0
- package/src/gl/InstancedMesh.ts +132 -0
- package/src/gl/Light.ts +135 -0
- package/src/gl/Lightmap.ts +402 -0
- package/src/gl/Locomotion.ts +498 -0
- package/src/gl/Material.ts +279 -0
- package/src/gl/Mesh.ts +83 -0
- package/src/gl/Model.ts +124 -0
- package/src/gl/NavAgent.ts +337 -0
- package/src/gl/NavMesh.ts +397 -0
- package/src/gl/Node.ts +350 -0
- package/src/gl/Noise.ts +30 -0
- package/src/gl/Particles.ts +676 -0
- package/src/gl/Physics.ts +222 -0
- package/src/gl/Plane.ts +53 -0
- package/src/gl/Ragdoll.ts +270 -0
- package/src/gl/Ray.ts +16 -0
- package/src/gl/Scene.ts +505 -0
- package/src/gl/Shape.ts +409 -0
- package/src/gl/Terrain.ts +1092 -0
- package/src/gl/Texture.ts +63 -0
- package/src/gl/Trigger.ts +45 -0
- package/src/gl/Vehicle.ts +473 -0
- package/src/gl/Wheel.ts +240 -0
- package/src/gl/animation/AnimationClip.ts +204 -0
- package/src/gl/animation/Animator.ts +329 -0
- package/src/gl/animation/Layer.ts +39 -0
- package/src/gl/animation/Loop.ts +31 -0
- package/src/gl/animation/Playback.ts +52 -0
- package/src/gl/animation/core.ts +670 -0
- package/src/gl/controls.ts +95 -0
- package/src/gl/physicsEvents.ts +20 -0
- package/src/gl/scenarios.ts +291 -0
- package/src/gl/state.ts +6 -0
- package/src/gl/terrainMesh.ts +219 -0
- package/src/gl/touch.ts +68 -0
- package/src/host.d.ts +61 -0
- package/src/inject.ts +203 -0
- package/src/math/Mathf.ts +118 -0
- package/src/math/mat4.ts +278 -0
- package/src/math/quat.ts +232 -0
- package/src/math/vec.ts +255 -0
- package/src/net/codec.ts +119 -0
- package/src/net/core.ts +384 -0
- package/src/net/index.ts +181 -0
- package/src/net/replication.ts +622 -0
- package/src/plugins/camera.ts +81 -0
- package/src/plugins/geolocation.ts +123 -0
- package/src/plugins/oauth.ts +61 -0
- package/src/plugins/permission.ts +7 -0
- package/src/plugins/push.ts +132 -0
- package/src/plugins/qr.ts +73 -0
- package/src/plugins/service.ts +47 -0
- package/src/runtime/app.ts +101 -0
- package/src/runtime/appEvents.ts +54 -0
- package/src/runtime/channel.ts +61 -0
- package/src/runtime/clipboard.ts +20 -0
- package/src/runtime/datetime.ts +329 -0
- package/src/runtime/device.ts +293 -0
- package/src/runtime/fetch.ts +77 -0
- package/src/runtime/files.ts +108 -0
- package/src/runtime/input.ts +175 -0
- package/src/runtime/media.ts +111 -0
- package/src/runtime/misc.ts +16 -0
- package/src/runtime/net.ts +36 -0
- package/src/runtime/rpc.ts +218 -0
- package/src/runtime/service.ts +83 -0
- package/src/runtime/share.ts +9 -0
- package/src/runtime/storage.ts +13 -0
- package/src/runtime/touch.ts +76 -0
- package/src/scene/defineScene.ts +1396 -0
- package/src/scene/editorPlugins.ts +110 -0
- package/src/scene/gizmos.ts +148 -0
- package/src/scene/grammar.ts +120 -0
- package/src/scene/level.ts +296 -0
- package/src/scene/material.ts +188 -0
- package/src/server/auth/appConfig.ts +12 -0
- package/src/server/auth/global.ts +80 -0
- package/src/server/auth/host.ts +318 -0
- package/src/server/auth/models.ts +83 -0
- package/src/server/auth/types.ts +50 -0
- package/src/server/channel.ts +56 -0
- package/src/server/context.ts +36 -0
- package/src/server/db/defineDb.ts +237 -0
- package/src/server/db/fields.ts +132 -0
- package/src/server/db/httpTransport.ts +93 -0
- package/src/server/db/index.ts +7 -0
- package/src/server/db/marci/query.ts +412 -0
- package/src/server/db/types.ts +202 -0
- package/src/server/errors.ts +12 -0
- package/src/server/host.ts +74 -0
- package/src/server/inject.ts +13 -0
- package/src/server/runtime.ts +133 -0
- package/src/server/validate.ts +87 -0
- package/src/ui/NativeView.ts +142 -0
- package/src/ui/UI.ts +39 -0
- package/src/ui/UIBottomSheet.ts +139 -0
- package/src/ui/UIButton.ts +101 -0
- package/src/ui/UIContainer.ts +60 -0
- package/src/ui/UIImage.ts +83 -0
- package/src/ui/UIInput.ts +185 -0
- package/src/ui/UIModal.ts +139 -0
- package/src/ui/UINode.ts +830 -0
- package/src/ui/UIPager.ts +362 -0
- package/src/ui/UIPopover.ts +100 -0
- package/src/ui/UIScreen.ts +123 -0
- package/src/ui/UIScrollable.ts +87 -0
- package/src/ui/UISpacer.ts +14 -0
- package/src/ui/UITabs.ts +236 -0
- package/src/ui/UIText.ts +51 -0
- package/src/ui/UIVideo.ts +88 -0
- package/src/ui/UIVirtualizedList.ts +241 -0
- package/src/ui/UIWidget.ts +127 -0
- package/src/ui/fonts.ts +13 -0
- package/src/ui/presentable.ts +117 -0
- package/src/ui/router.ts +132 -0
- package/src/ui/theme.ts +84 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
// A host-registered platform view (map, QR scanner, camera preview, …) — the plugin surface of
|
|
2
|
+
// the Presentable model (docs/navigation-presentable-plan.md). The host registers a *capability*
|
|
3
|
+
// (`engine.registerView("map") { params, channel -> MapView(...) }`); when and with what to show
|
|
4
|
+
// it is app code. Dual-natured like every Presentable: a fullscreen destination
|
|
5
|
+
// (`Router.push(map)` / `map.open()`) AND an embeddable layout node (wire type `"native"` — place
|
|
6
|
+
// it among a screen's children, sized by its styles). One instance lives in one place at a time;
|
|
7
|
+
// pushing a mounted instance fullscreen is promotion (the native view reparents, state intact).
|
|
8
|
+
//
|
|
9
|
+
// Plugins ship a typed TS wrapper over the raw `call`/`on` channel — see QRScanner for the
|
|
10
|
+
// wrapper convention.
|
|
11
|
+
|
|
12
|
+
import { _channelCall, _channelEmit, _channelOff, _channelOn, type ChannelListeners } from "../runtime/channel"
|
|
13
|
+
import { _bumpNavEpoch, _navSupported, _setCurrent, Presentable, type PresentOptions } from "./presentable"
|
|
14
|
+
import { Element, type BaseStyle, type DrawableStyle, type ElementStyle, type UIElementBase } from "./UINode"
|
|
15
|
+
|
|
16
|
+
export type UINativeViewStyle = ElementStyle & DrawableStyle
|
|
17
|
+
|
|
18
|
+
/** An instance of a host-registered platform view (map, QR scanner, camera preview, …) —
|
|
19
|
+
* embeddable among a screen's children AND fullscreen-presentable (`open()`); one instance lives
|
|
20
|
+
* in one place at a time. Talk to it via the `call`/`on` channel. */
|
|
21
|
+
export interface NativeView extends UIElementBase<UINativeViewStyle, DrawableStyle & BaseStyle> {
|
|
22
|
+
readonly type: "native",
|
|
23
|
+
/** The registered view kind this instance resolves to (the `registerView` name). */
|
|
24
|
+
readonly viewName: string,
|
|
25
|
+
/** Creation params, passed to the host factory (JSON-serializable). */
|
|
26
|
+
readonly params: any,
|
|
27
|
+
|
|
28
|
+
/** Show fullscreen as the current destination (see Presentable). */
|
|
29
|
+
open(options?: PresentOptions): void,
|
|
30
|
+
/** Dismiss if this is the visible destination. */
|
|
31
|
+
close(): void,
|
|
32
|
+
|
|
33
|
+
/** Became the visible destination. */
|
|
34
|
+
onOpen(callback: () => void): this
|
|
35
|
+
/** Stopped being the visible destination. */
|
|
36
|
+
onClose(callback: () => void): this
|
|
37
|
+
/** Hardware/system back while current. */
|
|
38
|
+
onBackPressed(callback: () => void): this
|
|
39
|
+
|
|
40
|
+
/** Invoke a method on the native view (`map.call("setCenter", [lng, lat])`). Args and the
|
|
41
|
+
* result are JSON-serialized. Rejects if the host has no such view/method. */
|
|
42
|
+
call(method: string, ...args: any[]): Promise<any>
|
|
43
|
+
/** Subscribe to an event the native view emits (`map.on("markerTap", cb)`). */
|
|
44
|
+
on(event: string, callback: (data?: any) => void): this
|
|
45
|
+
off(event: string, callback: (data?: any) => void): this
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let nextViewId = 1
|
|
49
|
+
|
|
50
|
+
/** @internal Base for typed plugin wrappers (QRScanner, CameraView — src/plugins/): subclass it,
|
|
51
|
+
* pin the viewName/params, and layer typed methods over the raw `call`/`on` channel. Not a
|
|
52
|
+
* public global — apps use the `NativeView(name)` factory below. */
|
|
53
|
+
export class NativeViewElement extends Element<"native"> implements Presentable {
|
|
54
|
+
readonly viewName: string
|
|
55
|
+
readonly params: any
|
|
56
|
+
/** @internal stable per-instance handle for the viewCall channel + promotion identity. */
|
|
57
|
+
readonly _viewId: number
|
|
58
|
+
/** @internal event listeners by event name — the host invokes these (vlist pcl/psl convention)
|
|
59
|
+
* or calls _emitViewEvent. */
|
|
60
|
+
readonly nvl: ChannelListeners = {}
|
|
61
|
+
readonly ol: (() => void)[] = []
|
|
62
|
+
readonly cl: (() => void)[] = []
|
|
63
|
+
_backButtonCallback?: () => void
|
|
64
|
+
private _vd?: object
|
|
65
|
+
|
|
66
|
+
constructor(viewName: string, params: any) {
|
|
67
|
+
super("native", {})
|
|
68
|
+
this.viewName = viewName
|
|
69
|
+
this.params = params
|
|
70
|
+
this._viewId = nextViewId++
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** @internal stable wire descriptor; hosts hand it back via router onChange (`_p` = this). */
|
|
74
|
+
_viewDesc(): object {
|
|
75
|
+
return this._vd ??= { type: "native", viewName: this.viewName, params: this.params, viewId: this._viewId, _p: this }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
open(options?: PresentOptions): void {
|
|
79
|
+
if (!_navSupported()) {
|
|
80
|
+
throw new Error(`NativeView("${this.viewName}") is not supported on this host (requires _creatorUI.openView)`)
|
|
81
|
+
}
|
|
82
|
+
_bumpNavEpoch()
|
|
83
|
+
_creatorUI.openView!(this._viewDesc(), options?.transition ?? "none")
|
|
84
|
+
_setCurrent(this)
|
|
85
|
+
}
|
|
86
|
+
close(): void {
|
|
87
|
+
_bumpNavEpoch()
|
|
88
|
+
if (_navSupported()) _creatorUI.closeView!()
|
|
89
|
+
if (Presentable.current === this) _setCurrent(null)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
onOpen(callback: () => void): this {
|
|
93
|
+
this.ol.push(callback)
|
|
94
|
+
return this
|
|
95
|
+
}
|
|
96
|
+
onClose(callback: () => void): this {
|
|
97
|
+
this.cl.push(callback)
|
|
98
|
+
return this
|
|
99
|
+
}
|
|
100
|
+
onBackPressed(callback: () => void): this {
|
|
101
|
+
this._backButtonCallback = callback
|
|
102
|
+
return this
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
call(method: string, ...args: any[]): Promise<any> {
|
|
106
|
+
return _channelCall(
|
|
107
|
+
typeof _creatorUI !== "undefined" ? _creatorUI : undefined, "viewCall",
|
|
108
|
+
`NativeView("${this.viewName}") is not supported on this host (requires _creatorUI.viewCall)`,
|
|
109
|
+
this._viewId, method, args)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
on(event: string, callback: (data?: any) => void): this {
|
|
113
|
+
_channelOn(this.nvl, event, callback)
|
|
114
|
+
return this
|
|
115
|
+
}
|
|
116
|
+
off(event: string, callback: (data?: any) => void): this {
|
|
117
|
+
_channelOff(this.nvl, event, callback)
|
|
118
|
+
return this
|
|
119
|
+
}
|
|
120
|
+
/** @internal host entry: deliver a native event (payload as a JSON string, or omitted). */
|
|
121
|
+
_emitViewEvent(event: string, dataJson?: string): void {
|
|
122
|
+
_channelEmit(this.nvl, event, dataJson)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Create an instance of a host-registered platform view. `NativeView.isSupported(name)` reports
|
|
128
|
+
* whether this host registered a factory for it. Style via the chained `.style()`.
|
|
129
|
+
*/
|
|
130
|
+
// PURE IIFE (not a plain function + expando assignment) so an app that never uses NativeView
|
|
131
|
+
// tree-shakes it away — top-level property assignments read as side effects to the bundler.
|
|
132
|
+
export const NativeView: {
|
|
133
|
+
(name: string, params?: object): NativeView
|
|
134
|
+
/** Whether this host registered a `registerView` factory under `name`. */
|
|
135
|
+
isSupported(name: string): boolean
|
|
136
|
+
} = /*#__PURE__*/ (() => {
|
|
137
|
+
const factory = (name: string, params: object = {}): NativeView =>
|
|
138
|
+
new NativeViewElement(name, params) as unknown as NativeView
|
|
139
|
+
factory.isSupported = (name: string): boolean =>
|
|
140
|
+
typeof _creatorUI !== "undefined" && typeof _creatorUI.isViewSupported === "function" && _creatorUI.isViewSupported(name)
|
|
141
|
+
return factory
|
|
142
|
+
})()
|
package/src/ui/UI.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Renderer-side contract + per-renderer status (DOM / canvas-ui / desktop) for every component
|
|
2
|
+
// below is tracked in packages/canvas-ui/COMPONENTS.md.
|
|
3
|
+
export { UIButton, type UIButtonStyle } from './UIButton'
|
|
4
|
+
export { UIScreen, type UIScreenStyle } from './UIScreen'
|
|
5
|
+
export { UISpacer, type UISpacerStyle } from './UISpacer'
|
|
6
|
+
export { UIText, type UITextStyle } from './UIText'
|
|
7
|
+
export { registerFont } from './fonts'
|
|
8
|
+
export { UIImage, type UIImageStyle } from './UIImage'
|
|
9
|
+
export { UIVideo, type UIVideoStyle } from './UIVideo'
|
|
10
|
+
export { UIInput, UITextArea, type UIInputStyle } from './UIInput'
|
|
11
|
+
|
|
12
|
+
export { UIWidget, type UIWidgetStyle } from './UIWidget'
|
|
13
|
+
export { UIModal, type UIModalStyle } from './UIModal'
|
|
14
|
+
export { UIBottomSheet, type UIBottomSheetStyle } from './UIBottomSheet'
|
|
15
|
+
export { UIPopover, type UIPopoverStyle, type UIPopoverAnchor } from './UIPopover'
|
|
16
|
+
|
|
17
|
+
export { UIRow, UIColumn, UIBox, type UIContainerStyle } from './UIContainer'
|
|
18
|
+
export { UIScrollable, type UIScrollableStyle } from './UIScrollable'
|
|
19
|
+
export { UIPager, type UIPagerStyle } from './UIPager'
|
|
20
|
+
export { UITabs, type UITabDef, defineTabs, type TabDef, type TabsHandle } from './UITabs'
|
|
21
|
+
|
|
22
|
+
export { UIVirtualizedList } from './UIVirtualizedList'
|
|
23
|
+
|
|
24
|
+
export { Router } from './router'
|
|
25
|
+
export { NativeView, type UINativeViewStyle } from './NativeView'
|
|
26
|
+
// Presentable is both the interface and its runtime companion (Presentable.current).
|
|
27
|
+
export { Presentable } from './presentable'
|
|
28
|
+
export type { PresentOptions, Transition, TransitionName, TransitionSpec, TransitionTransform } from './presentable'
|
|
29
|
+
|
|
30
|
+
// Internal: memoized map for reactive children — the chisel `reactiveUi` pass rewrites
|
|
31
|
+
// `list.map(fn)` inside children bindings to `__uiMap(list, fn, slot)` calls.
|
|
32
|
+
export { __uiMap } from './UINode'
|
|
33
|
+
// Compiler fast paths (chisel `flatten_ui` raw lowering): dispatch-free construction for calls
|
|
34
|
+
// whose arguments the compiler proved to be plain children. Never written by hand.
|
|
35
|
+
export { __UIColumn, __UIRow, __UIBox } from './UIContainer'
|
|
36
|
+
export { __UIButton } from './UIButton'
|
|
37
|
+
export { __UIScreen } from './UIScreen'
|
|
38
|
+
export { __UIScrollable } from './UIScrollable'
|
|
39
|
+
export { __UIWidget } from './UIWidget'
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { ModalElement, type UIModalStyle } from "./UIModal"
|
|
2
|
+
import type { UIWidget } from "./UIWidget"
|
|
3
|
+
import { buildUI, type ChildrenFn, type UIChildArg, type UINodeChild } from "./UINode"
|
|
4
|
+
|
|
5
|
+
export type UIBottomSheetStyle = UIModalStyle
|
|
6
|
+
|
|
7
|
+
const SHEET_ANIM_MS = 250
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A bottom sheet: a `UIWidget` pinned to the bottom edge. By default it is **content-sized** —
|
|
11
|
+
* as tall as what's inside (capped at the screen), one position, drag down to dismiss (the
|
|
12
|
+
* action-sheet shape). Call `detents()` for the multi-position map-app model instead: snap
|
|
13
|
+
* positions the user drags between on native hosts. Ships as a widget node on the wire (no new
|
|
14
|
+
* node type — docs/bottom-sheet-plan.md): hosts without sheet support show a static bottom panel.
|
|
15
|
+
*
|
|
16
|
+
* With detents, content lays out at the HIGHEST detent's size; lower detents show the top
|
|
17
|
+
* slice. The scrim, `onOpen`/`onClose`, back-button and scrim-tap dismissal all come from the
|
|
18
|
+
* modal machinery.
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* const actions = UIBottomSheet({ bgColor: "#1c1c1e", borderRadius: 16 }, [rows]) // content-sized
|
|
22
|
+
*
|
|
23
|
+
* const mapSheet = UIBottomSheet({ bgColor: "#1c1c1e", borderRadius: 16 }, [results])
|
|
24
|
+
* .detents([0.3, 0.6, 1])
|
|
25
|
+
* .onDetentChange(i => map.setPadding(...))
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export interface UIBottomSheet extends UIWidget {
|
|
29
|
+
/** Snap positions as fractions of screen height, ascending. Also sizes the sheet: its box is
|
|
30
|
+
* the last (highest) detent; set this before `show()`. Without a `detents()` call the sheet
|
|
31
|
+
* is CONTENT-SIZED instead — one position, as tall as its children (up to the screen). */
|
|
32
|
+
detents(list: number[]): this,
|
|
33
|
+
/** Snap to a detent (animated). Clamped to the detents range; fires `onDetentChange`. */
|
|
34
|
+
setDetent(index: number): void,
|
|
35
|
+
/** Current detent index. */
|
|
36
|
+
readonly detent: number,
|
|
37
|
+
/** Fires on every settle: a finger snap on native hosts, or a `setDetent()` call. */
|
|
38
|
+
onDetentChange(callback: (index: number) => void): this,
|
|
39
|
+
|
|
40
|
+
/** Mount and slide in to the current detent. No-op while already open. */
|
|
41
|
+
show(): void,
|
|
42
|
+
/** Slide out, fade the scrim, then unmount. No-op while closed. */
|
|
43
|
+
hide(): void,
|
|
44
|
+
/** True from `show()` until `hide()` starts. */
|
|
45
|
+
readonly isOpen: boolean,
|
|
46
|
+
|
|
47
|
+
/** Called when `show()` mounts the sheet. */
|
|
48
|
+
onOpen(callback: () => void): this,
|
|
49
|
+
/** Called when the sheet starts closing — scrim tap, back button, drag-dismiss, or `hide()`. */
|
|
50
|
+
onClose(callback: () => void): this,
|
|
51
|
+
/** `dismissible(false)`: scrim taps, the back button and dragging below the lowest detent no
|
|
52
|
+
* longer close the sheet (a drag collapses to the lowest detent instead). Default `true`. */
|
|
53
|
+
dismissible(enabled: boolean): this,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export class BottomSheetElement extends ModalElement {
|
|
57
|
+
_detents: number[] = [1]
|
|
58
|
+
_detent = 0
|
|
59
|
+
/** Host-facing (the `sl`/`otl` contract): hosts read `dtl` at mount and call it with the
|
|
60
|
+
* settled detent index for GESTURE settles only — programmatic `setDetent` fires JS-side.
|
|
61
|
+
* `-1` = dragged below the lowest detent while dismissible. */
|
|
62
|
+
readonly dtl: any[] = []
|
|
63
|
+
readonly _detentListeners: ((index: number) => void)[] = []
|
|
64
|
+
|
|
65
|
+
constructor(style: UIBottomSheetStyle, children: UINodeChild[] | ChildrenFn) {
|
|
66
|
+
// No default height: an absolute node with only bottom set is CONTENT-sized by yoga. A
|
|
67
|
+
// single detent makes the geometry height-independent on every host (shown = offset 0,
|
|
68
|
+
// hidden = own height), so content mode needs no host support beyond the detents path.
|
|
69
|
+
super({ left: 0, right: 0, bottom: 0, maxHeight: "100%", sheetDetents: "1", ...style } as UIModalStyle, children)
|
|
70
|
+
// The inherited show/hide pose animates ONLY the scrim: sheet geometry rides the
|
|
71
|
+
// sheetDetent style key, never the pose system.
|
|
72
|
+
this._transition = { duration: SHEET_ANIM_MS }
|
|
73
|
+
this.dtl.push((index: number) => {
|
|
74
|
+
if (index < 0) { this.hide(); return }
|
|
75
|
+
this._detent = index
|
|
76
|
+
for (const callback of this._detentListeners) callback(index)
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
detents(list: number[]): this {
|
|
81
|
+
const sorted = [...list].filter(d => d > 0).sort((a, b) => a - b)
|
|
82
|
+
if (sorted.length === 0) return this
|
|
83
|
+
this._detents = sorted
|
|
84
|
+
if (this._detent >= sorted.length) this._detent = sorted.length - 1
|
|
85
|
+
this.style({
|
|
86
|
+
height: `${sorted[sorted.length - 1] * 100}%`,
|
|
87
|
+
sheetDetents: sorted.join(","),
|
|
88
|
+
} as UIBottomSheetStyle)
|
|
89
|
+
return this
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
get detent(): number {
|
|
93
|
+
return this._detent
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
setDetent(index: number): void {
|
|
97
|
+
const clamped = Math.max(0, Math.min(Math.floor(index), this._detents.length - 1))
|
|
98
|
+
this._detent = clamped
|
|
99
|
+
if (!this._open) return // applied by the next show()
|
|
100
|
+
this.style({ sheetDetent: clamped } as UIBottomSheetStyle)
|
|
101
|
+
for (const callback of this._detentListeners) callback(clamped)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
onDetentChange(callback: (index: number) => void): this {
|
|
105
|
+
this._detentListeners.push(callback)
|
|
106
|
+
return this
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
show(): void {
|
|
110
|
+
if (this._open) return
|
|
111
|
+
// Before the mount, so the built node carries the target and hosts play the entrance to it.
|
|
112
|
+
this.style({ sheetDetent: this._detent } as UIBottomSheetStyle)
|
|
113
|
+
super.show()
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
hide(): void {
|
|
117
|
+
if (!this._open) return
|
|
118
|
+
this.style({ sheetDetent: -1 } as UIBottomSheetStyle) // hosts slide out; unmount follows
|
|
119
|
+
super.hide()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
dismissible(enabled: boolean): this {
|
|
123
|
+
super.dismissible(enabled)
|
|
124
|
+
this.style({ sheetDismissible: enabled ? 1 : 0 } as UIBottomSheetStyle)
|
|
125
|
+
return this
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const makeBottomSheet = (style: any, children: any) => new BottomSheetElement(style, children)
|
|
130
|
+
|
|
131
|
+
/** Create a bottom sheet (see {@link UIBottomSheet}) — content-sized by default; call
|
|
132
|
+
* `.detents([...])` for snap positions. Same argument forms as `UIColumn`. */
|
|
133
|
+
export function UIBottomSheet(...children: UIChildArg[]): UIBottomSheet;
|
|
134
|
+
export function UIBottomSheet(children: UINodeChild[] | ChildrenFn): UIBottomSheet;
|
|
135
|
+
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
|
|
136
|
+
export function UIBottomSheet(style: UIBottomSheetStyle, children?: UINodeChild[] | ChildrenFn): UIBottomSheet;
|
|
137
|
+
export function UIBottomSheet(...args: any[]): UIBottomSheet {
|
|
138
|
+
return buildUI(args, null, makeBottomSheet)
|
|
139
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
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 } from "./UINode"
|
|
4
|
+
|
|
5
|
+
export type UIButtonStyle = ElementStyle & DrawableStyle & ContainerStyle & { rippleColor?: Color | "default" }
|
|
6
|
+
|
|
7
|
+
/** A tappable container — row + centered children by default. Press feedback via the `onPressed`
|
|
8
|
+
* style block, the `$pressed` class, or `rippleColor`. */
|
|
9
|
+
export interface UIButton extends UIContainerBase<UIButtonStyle & { onPressed?: UIButtonStyle & { duration?: number } }, DrawableStyle & BaseStyle> {
|
|
10
|
+
readonly type: "button",
|
|
11
|
+
|
|
12
|
+
/** Fired on a completed tap/click. */
|
|
13
|
+
onClick(callback: (ev: ClickEventType<UIButton>) => void): this,
|
|
14
|
+
/** Fired when a touch begins; `ev.track(...)` takes over the rest of the gesture (dragging). */
|
|
15
|
+
onTouchStart(callback: (ev: TouchStartEventType<UIButton>) => void): this,
|
|
16
|
+
/** Fired when a finger is held on the button past the long-press threshold. The event can
|
|
17
|
+
* `ev.track(...)` the rest of the gesture (like `onTouchStart`), so a hold can flow straight into
|
|
18
|
+
* dragging the element. A handled long-press suppresses the click that would follow the release. */
|
|
19
|
+
onLongPress(callback: (ev: LongPressEventType<UIButton>) => void): this,
|
|
20
|
+
/** Is a finger currently down on the button? */
|
|
21
|
+
isPressed(): boolean,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ButtonElement extends ContainerElement<"button"> {
|
|
26
|
+
|
|
27
|
+
readonly clickListeners: any[] = []
|
|
28
|
+
readonly touchStartListeners: any[] = []
|
|
29
|
+
readonly longPressListeners: any[] = []
|
|
30
|
+
|
|
31
|
+
onClick (callback: any): this {
|
|
32
|
+
this.clickListeners.push(callback)
|
|
33
|
+
return this
|
|
34
|
+
}
|
|
35
|
+
_emitClick(pointerId: number, clientX: number, clientY: number) {
|
|
36
|
+
const touchEvent = new ClickEvent(clientX, clientY, pointerId)
|
|
37
|
+
touchEvent.target = this
|
|
38
|
+
for (let callback of this.clickListeners) {
|
|
39
|
+
callback(touchEvent)
|
|
40
|
+
}
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
onTouchStart (callback: any): this {
|
|
45
|
+
this.touchStartListeners.push(callback)
|
|
46
|
+
return this
|
|
47
|
+
}
|
|
48
|
+
_emitTouchStart(pointerId: number, clientX: number, clientY: number): any {
|
|
49
|
+
const touchEvent = new TouchStartEvent(clientX, clientY, pointerId)
|
|
50
|
+
touchEvent.target = this
|
|
51
|
+
for (let callback of this.touchStartListeners) {
|
|
52
|
+
callback(touchEvent)
|
|
53
|
+
if ((touchEvent as any)._trackHandler) {
|
|
54
|
+
return (touchEvent as any)._trackHandler
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
onLongPress (callback: any): this {
|
|
61
|
+
this.longPressListeners.push(callback)
|
|
62
|
+
return this
|
|
63
|
+
}
|
|
64
|
+
// Fired by the host when the finger is held past the long-press threshold. Returns null when
|
|
65
|
+
// nothing is listening — the host then lets the trailing click through as usual. Otherwise it
|
|
66
|
+
// returns an object whose presence tells the host to swallow that click, carrying the optional
|
|
67
|
+
// drag handler a listener registered via `ev.track(...)` for the host to drive the gesture.
|
|
68
|
+
_emitLongPress(pointerId: number, clientX: number, clientY: number): any {
|
|
69
|
+
if (this.longPressListeners.length === 0) return null
|
|
70
|
+
const longPressEvent = new LongPressEvent(clientX, clientY, pointerId)
|
|
71
|
+
longPressEvent.target = this
|
|
72
|
+
for (let callback of this.longPressListeners) {
|
|
73
|
+
callback(longPressEvent)
|
|
74
|
+
}
|
|
75
|
+
return { track: (longPressEvent as any)._trackHandler ?? null }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
isPressed(): boolean {
|
|
79
|
+
if (!this._id) return false
|
|
80
|
+
return _creatorUI.isButtonPressed(this._id)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Buttons are row + centered by default: the icon+label button is the norm, and yoga's column
|
|
85
|
+
// default was a recurring authoring/codegen trap.
|
|
86
|
+
const BUTTON_DEFAULTS = { flexDirection: "row", justifyContent: "center", alignItems: "center" } as const
|
|
87
|
+
|
|
88
|
+
const makeButton = (style: any, children: any) => new ButtonElement("button", style, children)
|
|
89
|
+
|
|
90
|
+
/** @internal Compiler fast path (chisel `flatten_ui` raw lowering) — see UIContainer. */
|
|
91
|
+
export const __UIButton = (children: UINodeChild[] | ChildrenFn): UIButton => makeButton({ ...BUTTON_DEFAULTS }, children)
|
|
92
|
+
|
|
93
|
+
/** Create a button (row + centered by default — the icon+label shape). Same argument forms as
|
|
94
|
+
* `UIColumn`; attach behavior with `.onClick(...)`. */
|
|
95
|
+
export function UIButton(...children: UIChildArg[]): UIButton;
|
|
96
|
+
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
|
+
export function UIButton(...args: any[]): UIButton {
|
|
100
|
+
return buildUI(args, BUTTON_DEFAULTS, makeButton)
|
|
101
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ContainerElement, buildUI, type ChildrenFn, type ContainerStyle, type DrawableStyle, type ElementStyle, type UIChildArg, type UIContainerBase, type UINodeChild } from "./UINode"
|
|
2
|
+
|
|
3
|
+
export type UIContainerStyle = ElementStyle & DrawableStyle & ContainerStyle
|
|
4
|
+
|
|
5
|
+
/** A vertical flex container — children stack top to bottom. */
|
|
6
|
+
export interface UIColumn extends UIContainerBase<UIContainerStyle> {
|
|
7
|
+
readonly type: "column",
|
|
8
|
+
}
|
|
9
|
+
const makeColumn = (style: any, children: any) => new ContainerElement("column", style, children)
|
|
10
|
+
|
|
11
|
+
/** @internal Compiler fast path (chisel `flatten_ui` raw lowering): construction with a proven
|
|
12
|
+
* plain-children array — or a reactive ChildrenFn (the Element constructor branches on
|
|
13
|
+
* `typeof === "function"` either way) — skipping the buildUI argument dispatch entirely. */
|
|
14
|
+
export const __UIColumn = (children: UINodeChild[] | ChildrenFn): UIColumn => makeColumn({}, children)
|
|
15
|
+
export const __UIRow = (children: UINodeChild[] | ChildrenFn): UIRow => makeRow({ flexDirection: "row" }, children)
|
|
16
|
+
export const __UIBox = (children: UINodeChild[] | ChildrenFn): UIBox => makeBox({ ...BOX_DEFAULTS }, children)
|
|
17
|
+
|
|
18
|
+
/** Create a vertical flex container. Children are variadic — arrays flatten one level, falsy
|
|
19
|
+
* entries are skipped; a lone function makes the children reactive. */
|
|
20
|
+
export function UIColumn(...children: UIChildArg[]): UIColumn
|
|
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
|
+
export function UIColumn(...args: any[]): UIColumn {
|
|
25
|
+
return buildUI(args, null, makeColumn)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** A horizontal flex container — children flow left to right. */
|
|
29
|
+
export interface UIRow extends UIContainerBase<UIContainerStyle> {
|
|
30
|
+
readonly type: "row",
|
|
31
|
+
}
|
|
32
|
+
const ROW_DEFAULTS = { flexDirection: "row" } as const
|
|
33
|
+
const makeRow = (style: any, children: any) => new ContainerElement("row", style, children)
|
|
34
|
+
|
|
35
|
+
/** Create a horizontal flex container (same argument forms as `UIColumn`). */
|
|
36
|
+
export function UIRow(...children: UIChildArg[]): UIRow
|
|
37
|
+
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
|
+
export function UIRow(...args: any[]): UIRow {
|
|
41
|
+
return buildUI(args, ROW_DEFAULTS, makeRow)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** A centering container — `justifyContent`/`alignItems` default to center. */
|
|
45
|
+
export interface UIBox extends UIContainerBase<UIContainerStyle> {
|
|
46
|
+
readonly type: "box",
|
|
47
|
+
}
|
|
48
|
+
const BOX_DEFAULTS = { justifyContent: "center", alignItems: "center" } as const
|
|
49
|
+
|
|
50
|
+
const makeBox = (style: any, children: any) => new ContainerElement("box", style, children)
|
|
51
|
+
|
|
52
|
+
/** Create a centering container — children are centered on both axes by default (icon badges,
|
|
53
|
+
* avatars, empty states). Same argument forms as `UIColumn`. */
|
|
54
|
+
export function UIBox(...children: UIChildArg[]): UIBox
|
|
55
|
+
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
|
+
export function UIBox(...args: any[]): UIBox {
|
|
59
|
+
return buildUI(args, BOX_DEFAULTS, makeBox)
|
|
60
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { FetchResponse } from "../runtime/fetch"
|
|
2
|
+
import type { SvgSourceValue as SvgSource } from "../runtime/misc"
|
|
3
|
+
import type { File } from "../runtime/fetch"
|
|
4
|
+
import { Canvas, isCanvas } from "../canvas/Canvas"
|
|
5
|
+
import { Element, type Color, type ElementStyle, type UIElementBase } from "./UINode"
|
|
6
|
+
|
|
7
|
+
export type UIImageStyle = ElementStyle & { borderRadius?: number, objectFit?: "cover" | "contain" | "fill", tintColor?: Color }
|
|
8
|
+
|
|
9
|
+
/** A baked Canvas surface as an image source. The UI backend resolves it to a UI texture — see
|
|
10
|
+
* docs/canvas-contract.md ("Pending"). */
|
|
11
|
+
export type CanvasImageSource = { canvasSurface: number }
|
|
12
|
+
|
|
13
|
+
/** Accepted image sources. A Canvas is baked to a surface marker on assign. */
|
|
14
|
+
export type ImageSource = string | FetchResponse | File | SvgSource | Canvas
|
|
15
|
+
|
|
16
|
+
const toSrc = (src: ImageSource): string | FetchResponse | File | SvgSource | CanvasImageSource =>
|
|
17
|
+
// _bakeSurfaceForUI (not _bakeSurface) also flags the canvas UI-bound, so canvas.update() later
|
|
18
|
+
// refreshes this image node in place — that's what makes an animated UIImage(canvas) update.
|
|
19
|
+
isCanvas(src) ? { canvasSurface: src._bakeSurfaceForUI() } : src
|
|
20
|
+
|
|
21
|
+
/** The stored/resolved image source (a Canvas is resolved to a surface marker). */
|
|
22
|
+
export type ResolvedImageSource = string | FetchResponse | File | SvgSource | CanvasImageSource
|
|
23
|
+
|
|
24
|
+
/** An image element. Sources: URL / `asset(...)`, fetch response / File, an SVG source
|
|
25
|
+
* (`assetIcon`), or a live Canvas (`canvas.update()` refreshes the node in place). */
|
|
26
|
+
export interface UIImage extends UIElementBase<UIImageStyle> {
|
|
27
|
+
readonly type: "image",
|
|
28
|
+
/** The image source — writable to swap the picture in place. */
|
|
29
|
+
get src(): ResolvedImageSource
|
|
30
|
+
set src(value: ImageSource)
|
|
31
|
+
|
|
32
|
+
/** Render only a sub-rectangle of the source texture (texture pixels) — the atlas/spritesheet
|
|
33
|
+
* primitive: crop once, then swap the rect per frame for sprite animation. The rect's w/h
|
|
34
|
+
* become the node's intrinsic size and override `objectFit` — the cropped frame fills the box. */
|
|
35
|
+
setSourceRect(x: number, y: number, w: number, h: number): this
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class ImageElement extends Element<"image"> {
|
|
39
|
+
private _src: ResolvedImageSource
|
|
40
|
+
// Current source sub-rect; undefined until the first setSourceRect. Kept on the node (not
|
|
41
|
+
// just sent as a command) so a crop set while detached (_id == 0) is reapplied at mount —
|
|
42
|
+
// the viewer reads it when it creates the element.
|
|
43
|
+
private _sourceRect?: [number, number, number, number]
|
|
44
|
+
constructor(style: any, src: ImageSource) {
|
|
45
|
+
super("image", style)
|
|
46
|
+
this._src = toSrc(src)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get src(): ResolvedImageSource {
|
|
50
|
+
return this._src
|
|
51
|
+
}
|
|
52
|
+
set src(value: ImageSource) {
|
|
53
|
+
this._src = toSrc(value)
|
|
54
|
+
if (this._id !== 0) {
|
|
55
|
+
_creatorUI.updateNode(this._id, "src", this._src)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
setSourceRect(x: number, y: number, w: number, h: number): this {
|
|
60
|
+
// Stash on the node so a crop set before mount is reapplied when the element is created.
|
|
61
|
+
this._sourceRect = [x, y, w, h]
|
|
62
|
+
// Live mutation: fast channel (not _style → no PX_VALUE density scaling: these are texture
|
|
63
|
+
// pixels, a different coordinate space). On a detached node there is nothing to mutate yet —
|
|
64
|
+
// the stash above carries it to mount. The viewer decides relayout vs draw-only by comparing
|
|
65
|
+
// w/h against the rect it has stashed on the element.
|
|
66
|
+
if (this._id !== 0) {
|
|
67
|
+
_creatorUI.setSourceRect(this._id, x, y, w, h)
|
|
68
|
+
}
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Create an image element from a source (see {@link ImageSource}). */
|
|
74
|
+
export function UIImage(src: ImageSource): UIImage
|
|
75
|
+
/** @deprecated Style as the first argument is legacy — use chained `.style({...})`. */
|
|
76
|
+
export function UIImage(style: UIImageStyle, src: ImageSource): UIImage
|
|
77
|
+
export function UIImage(...args: [ImageSource] | [UIImageStyle, ImageSource]): UIImage {
|
|
78
|
+
if (args.length === 1) {
|
|
79
|
+
return new ImageElement({}, args[0])
|
|
80
|
+
} else {
|
|
81
|
+
return new ImageElement(args[0], args[1])
|
|
82
|
+
}
|
|
83
|
+
}
|