lecodes-cli 0.1.5 → 0.1.7
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 +300 -149
- package/package.json +5 -3
- package/runtime/sdk/animate/animate.ts +238 -0
- package/runtime/sdk/animate/bezier.ts +138 -0
- package/runtime/sdk/animate/easings.ts +126 -0
- package/runtime/sdk/core/color.ts +66 -0
- package/runtime/sdk/core/events.ts +38 -0
- package/runtime/sdk/core/registry.ts +23 -0
- package/runtime/sdk/g2/Camera2D.ts +73 -0
- package/runtime/sdk/g2/Node2D.ts +104 -0
- package/runtime/sdk/g2/Scene2D.ts +73 -0
- package/runtime/sdk/g2/Sprite.ts +164 -0
- package/runtime/sdk/g2/Texture2D.ts +37 -0
- package/runtime/sdk/g2/Tilemap.ts +41 -0
- package/runtime/sdk/g2/loop.ts +22 -0
- package/runtime/sdk/gl/Body.ts +101 -0
- package/runtime/sdk/gl/Camera.ts +40 -0
- package/runtime/sdk/gl/Geometry.ts +218 -0
- package/runtime/sdk/gl/Light.ts +37 -0
- package/runtime/sdk/gl/Material.ts +124 -0
- package/runtime/sdk/gl/Mesh.ts +123 -0
- package/runtime/sdk/gl/Node.ts +307 -0
- package/runtime/sdk/gl/Noise.ts +30 -0
- package/runtime/sdk/gl/Particles.ts +185 -0
- package/runtime/sdk/gl/Plane.ts +53 -0
- package/runtime/sdk/gl/Ray.ts +16 -0
- package/runtime/sdk/gl/Scene.ts +179 -0
- package/runtime/sdk/gl/Texture.ts +35 -0
- package/runtime/sdk/gl/controls.ts +95 -0
- package/runtime/sdk/gl/state.ts +6 -0
- package/runtime/sdk/gl/touch.ts +56 -0
- package/runtime/sdk/inject.ts +72 -0
- package/runtime/sdk/math/Mathf.ts +118 -0
- package/runtime/sdk/math/mat4.ts +278 -0
- package/runtime/sdk/math/quat.ts +232 -0
- package/runtime/sdk/math/vec.ts +255 -0
- package/runtime/sdk/plugins/qr.ts +36 -0
- package/runtime/sdk/runtime/device.ts +33 -0
- package/runtime/sdk/runtime/fetch.ts +71 -0
- package/runtime/sdk/runtime/files.ts +23 -0
- package/runtime/sdk/runtime/input.ts +12 -0
- package/runtime/sdk/runtime/media.ts +111 -0
- package/runtime/sdk/runtime/misc.ts +12 -0
- package/runtime/sdk/runtime/net.ts +36 -0
- package/runtime/sdk/runtime/storage.ts +13 -0
- package/runtime/sdk/runtime/touch.ts +44 -0
- package/runtime/sdk/ui/UI.ts +19 -0
- package/runtime/sdk/ui/UIButton.ts +84 -0
- package/runtime/sdk/ui/UIContainer.ts +100 -0
- package/runtime/sdk/ui/UIImage.ts +68 -0
- package/runtime/sdk/ui/UIInput.ts +78 -0
- package/runtime/sdk/ui/UINode.ts +342 -0
- package/runtime/sdk/ui/UIScreen.ts +122 -0
- package/runtime/sdk/ui/UIScrollable.ts +70 -0
- package/runtime/sdk/ui/UISpacer.ts +14 -0
- package/runtime/sdk/ui/UIText.ts +40 -0
- package/runtime/sdk/ui/UIVideo.ts +38 -0
- package/runtime/sdk/ui/UIVirtualizedList.ts +222 -0
- package/runtime/sdk/ui/UIWidget.ts +97 -0
- package/runtime/sdk/ui/fonts.ts +10 -0
- package/runtime/sdk/ui/router.ts +73 -0
- package/runtime/gl-matrix/common.js +0 -54
- package/runtime/gl-matrix/header.js +0 -12
- package/runtime/gl-matrix/index.js +0 -11
- package/runtime/gl-matrix/mat2.js +0 -425
- package/runtime/gl-matrix/mat2d.js +0 -476
- package/runtime/gl-matrix/mat3.js +0 -765
- package/runtime/gl-matrix/mat4.js +0 -1947
- package/runtime/gl-matrix/quat.js +0 -737
- package/runtime/gl-matrix/quat2.js +0 -822
- package/runtime/gl-matrix/vec2.js +0 -620
- package/runtime/gl-matrix/vec3.js +0 -791
- package/runtime/gl-matrix/vec4.js +0 -652
- package/runtime/worker/animate.js +0 -304
- package/runtime/worker/chunk-4ZN32GYS.js +0 -34
- package/runtime/worker/chunk-G5ZA3LKB.js +0 -52
- package/runtime/worker/chunk-MI3UXE5H.js +0 -1395
- package/runtime/worker/creator.js +0 -930
- package/runtime/worker/globals.js +0 -208
- package/runtime/worker/ui.js +0 -763
- package/runtime/worker/utils.js +0 -16
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ClickEvent, TouchStartEvent } from "../runtime/touch"
|
|
2
|
+
import type { ClickEvent as ClickEventType, TouchStartEvent as TouchStartEventType } from "../runtime/touch"
|
|
3
|
+
import { type ElementStyle, type ContainerStyle, type Style, ContainerElement, type DrawableStyle, type UINodeChild, type OnLayoutCallback, type BaseStyle, type AnimateStyle, type Color } from "./UINode"
|
|
4
|
+
|
|
5
|
+
export type UIButtonStyle = ElementStyle & DrawableStyle & ContainerStyle & { rippleColor?: Color | "default" }
|
|
6
|
+
|
|
7
|
+
export interface UIButton {
|
|
8
|
+
readonly type: "button",
|
|
9
|
+
|
|
10
|
+
style: Style<this, UIButtonStyle & { onPressed?: DrawableStyle & BaseStyle & { duration?: number } }>,
|
|
11
|
+
animateTo: AnimateStyle<this, DrawableStyle & BaseStyle>,
|
|
12
|
+
animateFrom: AnimateStyle<this, DrawableStyle & BaseStyle>
|
|
13
|
+
|
|
14
|
+
onClick(callback: (ev: ClickEventType<UIButton>) => void): this,
|
|
15
|
+
onTouchStart(callback: (ev: TouchStartEventType<UIButton>) => void): this,
|
|
16
|
+
isPressed(): boolean,
|
|
17
|
+
|
|
18
|
+
append(...nodes: UINodeChild[]): this
|
|
19
|
+
insert(index: number, ...nodes: UINodeChild[]): this
|
|
20
|
+
remove(...nodes: UINodeChild[]): this
|
|
21
|
+
setContent(nodes: UINodeChild[]): this
|
|
22
|
+
|
|
23
|
+
onLayout(onLayout: OnLayoutCallback): this
|
|
24
|
+
readonly children: UINodeChild[],
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ButtonElement extends ContainerElement<"button"> {
|
|
29
|
+
|
|
30
|
+
readonly clickListeners: any[] = []
|
|
31
|
+
readonly touchStartListeners: any[] = []
|
|
32
|
+
|
|
33
|
+
onClick (callback: any): this {
|
|
34
|
+
this.clickListeners.push(callback)
|
|
35
|
+
return this
|
|
36
|
+
}
|
|
37
|
+
_emitClick(pointerId: number, clientX: number, clientY: number) {
|
|
38
|
+
const touchEvent = new ClickEvent(clientX, clientY, pointerId)
|
|
39
|
+
touchEvent.target = this
|
|
40
|
+
for (let callback of this.clickListeners) {
|
|
41
|
+
callback(touchEvent)
|
|
42
|
+
}
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
onTouchStart (callback: any): this {
|
|
47
|
+
this.touchStartListeners.push(callback)
|
|
48
|
+
return this
|
|
49
|
+
}
|
|
50
|
+
_emitTouchStart(pointerId: number, clientX: number, clientY: number): any {
|
|
51
|
+
const touchEvent = new TouchStartEvent(clientX, clientY, pointerId)
|
|
52
|
+
touchEvent.target = this
|
|
53
|
+
for (let callback of this.touchStartListeners) {
|
|
54
|
+
callback(touchEvent)
|
|
55
|
+
if ((touchEvent as any)._trackHandler) {
|
|
56
|
+
return (touchEvent as any)._trackHandler
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
isPressed(): boolean {
|
|
63
|
+
if (!this._id) return false
|
|
64
|
+
return _creatorUI.isButtonPressed(this._id)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function UIButton(): UIButton;
|
|
69
|
+
export function UIButton(children: UINodeChild[]): UIButton;
|
|
70
|
+
export function UIButton(style: UIButtonStyle): UIButton;
|
|
71
|
+
export function UIButton(style: UIButtonStyle, children: UINodeChild[]): UIButton;
|
|
72
|
+
export function UIButton(...args: [] | [UINodeChild[] | UIButtonStyle] | [UIButtonStyle, children: UINodeChild[]]): UIButton {
|
|
73
|
+
if (args.length === 0) {
|
|
74
|
+
return new ButtonElement("button", {}, [])
|
|
75
|
+
} else if (args.length === 1) {
|
|
76
|
+
if (Array.isArray(args[0])) {
|
|
77
|
+
return new ButtonElement("button", {}, args[0])
|
|
78
|
+
} else {
|
|
79
|
+
return new ButtonElement("button", args[0], [])
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
return new ButtonElement("button", args[0], args[1])
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { ContainerElement, type AnimateStyle, type AppearStyle, type ContainerStyle, type DisappearStyle, type DrawableStyle, type ElementStyle, type OnLayoutCallback, type Style, type StyleFn, type UINode, type UINodeChild } from "./UINode"
|
|
2
|
+
|
|
3
|
+
export type UIContainerStyle = ElementStyle & DrawableStyle & ContainerStyle
|
|
4
|
+
|
|
5
|
+
export interface UIColumn {
|
|
6
|
+
readonly type: "column",
|
|
7
|
+
style: Style<this, UIContainerStyle>,
|
|
8
|
+
animateTo: AnimateStyle<this, UIContainerStyle>
|
|
9
|
+
animateFrom: AnimateStyle<this, UIContainerStyle>
|
|
10
|
+
|
|
11
|
+
append(...nodes: UINodeChild[]): UIColumn
|
|
12
|
+
insert(index: number, ...nodes: UINodeChild[]): UIColumn
|
|
13
|
+
remove(...nodes: UINodeChild[]): UIColumn
|
|
14
|
+
setContent(nodes: UINodeChild[]): this
|
|
15
|
+
|
|
16
|
+
onLayout(onLayout: OnLayoutCallback): this
|
|
17
|
+
|
|
18
|
+
readonly children: UINodeChild[]
|
|
19
|
+
}
|
|
20
|
+
export function UIColumn(): UIColumn
|
|
21
|
+
export function UIColumn(children: UINodeChild[]): UIColumn
|
|
22
|
+
export function UIColumn(style: UIContainerStyle): UIColumn
|
|
23
|
+
export function UIColumn(style: UIContainerStyle, children: UINodeChild[]): UIColumn
|
|
24
|
+
export function UIColumn(...args: [] | [UINodeChild[] | UIContainerStyle] | [UIContainerStyle, children: UINodeChild[]]): UIColumn {
|
|
25
|
+
if (args.length === 0) {
|
|
26
|
+
return new ContainerElement("column", {}, [])
|
|
27
|
+
} else if (args.length === 1) {
|
|
28
|
+
if (Array.isArray(args[0])) {
|
|
29
|
+
return new ContainerElement("column", {}, args[0])
|
|
30
|
+
} else {
|
|
31
|
+
return new ContainerElement("column", args[0], [])
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
return new ContainerElement("column", args[0], args[1])
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface UIRow {
|
|
39
|
+
readonly type: "row",
|
|
40
|
+
style: Style<this, UIContainerStyle>,
|
|
41
|
+
animateTo: AnimateStyle<this, UIContainerStyle>,
|
|
42
|
+
animateFrom: AnimateStyle<this, UIContainerStyle>,
|
|
43
|
+
|
|
44
|
+
append(...nodes: UINodeChild[]): UIRow
|
|
45
|
+
insert(index: number, ...nodes: UINodeChild[]): UIRow
|
|
46
|
+
remove(...nodes: UINodeChild[]): UIRow
|
|
47
|
+
setContent(nodes: UINodeChild[]): this
|
|
48
|
+
|
|
49
|
+
onLayout(onLayout: OnLayoutCallback): this
|
|
50
|
+
|
|
51
|
+
readonly children: UINodeChild[],
|
|
52
|
+
}
|
|
53
|
+
export function UIRow(children: UINodeChild[]): UIRow
|
|
54
|
+
export function UIRow(style: UIContainerStyle): UIRow
|
|
55
|
+
export function UIRow(style: UIContainerStyle, children: UINodeChild[]): UIRow
|
|
56
|
+
export function UIRow(...args: [] | [UINodeChild[] | UIContainerStyle] | [UIContainerStyle, children: UINodeChild[]]): UIRow {
|
|
57
|
+
if (args.length === 0) {
|
|
58
|
+
return new ContainerElement("row", {}, [])
|
|
59
|
+
} else if (args.length === 1) {
|
|
60
|
+
if (Array.isArray(args[0])) {
|
|
61
|
+
return new ContainerElement("row", {}, args[0])
|
|
62
|
+
} else {
|
|
63
|
+
return new ContainerElement("row", args[0], [])
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
return new ContainerElement("row", args[0], args[1])
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface UIBox {
|
|
71
|
+
readonly type: "box",
|
|
72
|
+
style: Style<this, UIContainerStyle>,
|
|
73
|
+
animateTo: AnimateStyle<this, UIContainerStyle>,
|
|
74
|
+
animateFrom: AnimateStyle<this, UIContainerStyle>,
|
|
75
|
+
|
|
76
|
+
append(...nodes: UINodeChild[]): this
|
|
77
|
+
insert(index: number, ...nodes: UINodeChild[]): this
|
|
78
|
+
remove(...nodes: UINodeChild[]): this
|
|
79
|
+
setContent(nodes: UINodeChild[]): this
|
|
80
|
+
|
|
81
|
+
onLayout(onLayout: OnLayoutCallback): this
|
|
82
|
+
|
|
83
|
+
readonly children: UINodeChild[],
|
|
84
|
+
}
|
|
85
|
+
export function UIBox(children: UINodeChild[]): UIBox
|
|
86
|
+
export function UIBox(style: UIContainerStyle): UIBox
|
|
87
|
+
export function UIBox(style: UIContainerStyle, children: UINodeChild[]): UIBox
|
|
88
|
+
export function UIBox(...args: [] | [UINodeChild[] | UIContainerStyle] | [UIContainerStyle, children: UINodeChild[]]): UIBox {
|
|
89
|
+
if (args.length === 0) {
|
|
90
|
+
return new ContainerElement("box", {}, [])
|
|
91
|
+
} else if (args.length === 1) {
|
|
92
|
+
if (Array.isArray(args[0])) {
|
|
93
|
+
return new ContainerElement("box", {}, args[0])
|
|
94
|
+
} else {
|
|
95
|
+
return new ContainerElement("box", args[0], [])
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
return new ContainerElement("box", args[0], args[1])
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
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 { Element, type AnimateStyle, type Color, type ElementStyle, type OnLayoutCallback, type Style } from "./UINode"
|
|
5
|
+
|
|
6
|
+
export type UIImageStyle = ElementStyle & { borderRadius?: number, objectFit?: "cover" | "contain" | "fill", tintColor?: Color }
|
|
7
|
+
|
|
8
|
+
export interface UIImage {
|
|
9
|
+
readonly type: "image",
|
|
10
|
+
src: string | FetchResponse | File | SvgSource,
|
|
11
|
+
style: Style<this, UIImageStyle>
|
|
12
|
+
animateTo: AnimateStyle<this, UIImageStyle>
|
|
13
|
+
animateFrom: AnimateStyle<this, UIImageStyle>
|
|
14
|
+
|
|
15
|
+
// Render only a sub-rectangle of the source texture (coords/size in texture pixels).
|
|
16
|
+
// This is the atlas / spritesheet primitive: the initial crop is the first call, sprite
|
|
17
|
+
// animation is swapping the rect per frame. The rect's w/h become the node's intrinsic
|
|
18
|
+
// size (one frame, not the whole atlas) and it overrides objectFit — the cropped frame
|
|
19
|
+
// always fills the box.
|
|
20
|
+
setSourceRect(x: number, y: number, w: number, h: number): this
|
|
21
|
+
|
|
22
|
+
onLayout(onLayout: OnLayoutCallback): this
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class ImageElement extends Element<"image"> {
|
|
26
|
+
private _src: string | FetchResponse | File | SvgSource
|
|
27
|
+
// Current source sub-rect; undefined until the first setSourceRect. Kept on the node (not
|
|
28
|
+
// just sent as a command) so a crop set while detached (_id == 0) is reapplied at mount —
|
|
29
|
+
// the viewer reads it when it creates the element.
|
|
30
|
+
private _sourceRect?: [number, number, number, number]
|
|
31
|
+
constructor(style: any, src: string | FetchResponse | File | SvgSource) {
|
|
32
|
+
super("image", style)
|
|
33
|
+
this._src = src
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get src() {
|
|
37
|
+
return this._src
|
|
38
|
+
}
|
|
39
|
+
set src(value) {
|
|
40
|
+
this._src = value
|
|
41
|
+
if (this._id !== 0) {
|
|
42
|
+
_creatorUI.updateNode(this._id, "src", value)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
setSourceRect(x: number, y: number, w: number, h: number): this {
|
|
47
|
+
// Stash on the node so a crop set before mount is reapplied when the element is created.
|
|
48
|
+
this._sourceRect = [x, y, w, h]
|
|
49
|
+
// Live mutation: fast channel (not _style → no PX_VALUE density scaling: these are texture
|
|
50
|
+
// pixels, a different coordinate space). On a detached node there is nothing to mutate yet —
|
|
51
|
+
// the stash above carries it to mount. The viewer decides relayout vs draw-only by comparing
|
|
52
|
+
// w/h against the rect it has stashed on the element.
|
|
53
|
+
if (this._id !== 0) {
|
|
54
|
+
_creatorUI.setSourceRect(this._id, x, y, w, h)
|
|
55
|
+
}
|
|
56
|
+
return this
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function UIImage(src: string | FetchResponse | File | SvgSource): UIImage
|
|
61
|
+
export function UIImage(style: UIImageStyle, src: string | FetchResponse | File | SvgSource): UIImage
|
|
62
|
+
export function UIImage(...args: [string | FetchResponse | File | SvgSource] | [UIImageStyle, string | FetchResponse | File | SvgSource]): UIImage {
|
|
63
|
+
if (args.length === 1) {
|
|
64
|
+
return new ImageElement({}, args[0])
|
|
65
|
+
} else {
|
|
66
|
+
return new ImageElement(args[0], args[1])
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { type ElementStyle, type DrawableStyle, type Style, type StyleFn, type AppearStyle, type DisappearStyle, Element, type TextStyle, type Color, type OnLayoutCallback, type BaseStyle, type AnimateStyle } from "./UINode";
|
|
2
|
+
|
|
3
|
+
type InputStyle = { placeholder?: string, placeholderColor?: Color, type?: "text" | "password" | "search" }
|
|
4
|
+
export type UIInputStyle = ElementStyle & DrawableStyle & TextStyle & InputStyle
|
|
5
|
+
|
|
6
|
+
export interface UIInput {
|
|
7
|
+
readonly type: "input",
|
|
8
|
+
|
|
9
|
+
value: string
|
|
10
|
+
|
|
11
|
+
style: Style<this, UIInputStyle & { onFocused?: DrawableStyle & BaseStyle & { duration?: number } }>,
|
|
12
|
+
animateTo: AnimateStyle<this, DrawableStyle & BaseStyle>
|
|
13
|
+
animateFrom: AnimateStyle<this, DrawableStyle & BaseStyle>
|
|
14
|
+
|
|
15
|
+
onChange(callback: (value: string) => void): UIInput
|
|
16
|
+
onFocus(callback: () => void): UIInput
|
|
17
|
+
onBlur(callback: () => void): UIInput
|
|
18
|
+
onLayout(onLayout: OnLayoutCallback): this
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface UITextArea {
|
|
22
|
+
readonly type: "textarea",
|
|
23
|
+
|
|
24
|
+
value: string
|
|
25
|
+
|
|
26
|
+
style: Style<this, UIInputStyle & { onFocused?: DrawableStyle & BaseStyle & { duration?: number } }>,
|
|
27
|
+
// styleV: StyleFn<this, UIInputStyle>,
|
|
28
|
+
// styleH: StyleFn<this, UIInputStyle>,
|
|
29
|
+
|
|
30
|
+
// appear: AppearStyle<this, UIInputStyle>
|
|
31
|
+
// disappear: DisappearStyle<this, UIInputStyle>
|
|
32
|
+
|
|
33
|
+
onChange(callback: (value: string) => void): UITextArea
|
|
34
|
+
onFocus(callback: () => void): UITextArea
|
|
35
|
+
onBlur(callback: () => void): UITextArea
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class InputElement<T extends "input" | "textarea"> extends Element<T> {
|
|
39
|
+
|
|
40
|
+
readonly cl: Function[] = []
|
|
41
|
+
readonly fl: Function[] = []
|
|
42
|
+
readonly bl: Function[] = []
|
|
43
|
+
|
|
44
|
+
get value() {
|
|
45
|
+
return _creatorUI.getTextValue(this._id)
|
|
46
|
+
}
|
|
47
|
+
set value(value: string) {
|
|
48
|
+
if (this._id !== 0) {
|
|
49
|
+
_creatorUI.updateNode(this._id, "value", value)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
onChange (callback: (value: string) => void): this {
|
|
54
|
+
this.cl.push(callback)
|
|
55
|
+
return this
|
|
56
|
+
}
|
|
57
|
+
onFocus(callback: () => void): this {
|
|
58
|
+
this.fl.push(callback)
|
|
59
|
+
return this
|
|
60
|
+
}
|
|
61
|
+
onBlur(callback: () => void): this {
|
|
62
|
+
this.bl.push(callback)
|
|
63
|
+
return this
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function UIInput(): UIInput;
|
|
68
|
+
export function UIInput(style: UIInputStyle): UIInput;
|
|
69
|
+
export function UIInput(style?: UIInputStyle): UIInput {
|
|
70
|
+
return new InputElement("input", style ?? {})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function UITextArea(): UITextArea;
|
|
74
|
+
export function UITextArea(style: UIInputStyle): UITextArea;
|
|
75
|
+
export function UITextArea(style?: UIInputStyle): UITextArea {
|
|
76
|
+
return new InputElement("textarea", style ?? {})
|
|
77
|
+
}
|
|
78
|
+
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import type { FetchResponse } from "../runtime/fetch"
|
|
2
|
+
|
|
3
|
+
export interface UINode {
|
|
4
|
+
readonly type: string
|
|
5
|
+
/** Author-given semantic name (a stable selector for tests + AI review feedback). */
|
|
6
|
+
readonly name?: string
|
|
7
|
+
style: Style<this, any>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type UINodeChild = UINode | null | undefined | false
|
|
11
|
+
|
|
12
|
+
export type Color = number | string | null
|
|
13
|
+
|
|
14
|
+
export type BaseStyle = {
|
|
15
|
+
opacity?: number | `${number}`,
|
|
16
|
+
transform?: string | null,
|
|
17
|
+
backgroundColor?: Color | null,
|
|
18
|
+
bgColor?: Color | null,
|
|
19
|
+
overflow?: "visible" | "hidden",
|
|
20
|
+
boxSizing?: "border-box" | "content-box",
|
|
21
|
+
/**
|
|
22
|
+
* Author-given semantic name — a stable selector for tests and for marking elements in the AI
|
|
23
|
+
* code-review feedback loop. NOT a style: it's extracted at construction (never sent to the layout
|
|
24
|
+
* engine) and surfaced on the node + in the renderer's serialized output. Available on every element.
|
|
25
|
+
*/
|
|
26
|
+
name?: string,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type DrawableStyle = {
|
|
30
|
+
backgroundImage?: FetchResponse | File | string | null,
|
|
31
|
+
bgGradient?: string,
|
|
32
|
+
backgroundGradient?: string,
|
|
33
|
+
bgImage?: FetchResponse | File | string | null,
|
|
34
|
+
backgroundSize?: "cover" | "contain" | "tile",
|
|
35
|
+
bgSize?: "cover" | "contain" | "tile",
|
|
36
|
+
border?: string | number | null,
|
|
37
|
+
borderWidth?: number,
|
|
38
|
+
borderColor?: Color,
|
|
39
|
+
|
|
40
|
+
borderLeft?: string | number | null,
|
|
41
|
+
borderLeftWidth?: number,
|
|
42
|
+
borderLeftColor?: Color,
|
|
43
|
+
borderTop?: string | number | null,
|
|
44
|
+
borderTopWidth?: number,
|
|
45
|
+
borderTopColor?: Color,
|
|
46
|
+
borderRight?: string | number | null,
|
|
47
|
+
borderRightWidth?: number,
|
|
48
|
+
borderRightColor?: Color,
|
|
49
|
+
borderBottom?: string | number | null,
|
|
50
|
+
borderBottomWidth?: number,
|
|
51
|
+
borderBottomColor?: Color,
|
|
52
|
+
|
|
53
|
+
borderRadius?: UIValue | string,
|
|
54
|
+
borderTopLeftRadius?: UIValue,
|
|
55
|
+
borderTopRightRadius?: UIValue,
|
|
56
|
+
borderBottomLeftRadius?: UIValue,
|
|
57
|
+
borderBottomRightRadius?: UIValue,
|
|
58
|
+
|
|
59
|
+
pointerEvents?: "all" | "none",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type ContainerStyle = {
|
|
63
|
+
flexDirection?: "row" | "column",
|
|
64
|
+
justifyContent?: "flex-start" | "center" | "flex-end" | "space-between" | "space-evenly",
|
|
65
|
+
alignItems?: "flex-start" | "center" | "flex-end" | "stretch",
|
|
66
|
+
gap?: UIValue,
|
|
67
|
+
flexWrap?: "nowrap" | "wrap" | "wrap-reverse"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type ScrollStyle = {
|
|
71
|
+
scrollX?: "auto" | "all" | "none",
|
|
72
|
+
scrollY?: "auto" | "all" | "none"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
type UIValue = number |
|
|
76
|
+
`${number}px` |
|
|
77
|
+
`${number}vw` |
|
|
78
|
+
`${number}vh` |
|
|
79
|
+
`${number}vmin` |
|
|
80
|
+
`${number}vmax` |
|
|
81
|
+
`${number}em` |
|
|
82
|
+
`calc(${string})` |
|
|
83
|
+
`min(${string})` |
|
|
84
|
+
`max(${string})` |
|
|
85
|
+
`${string}%` |
|
|
86
|
+
`clamp(${string})`
|
|
87
|
+
|
|
88
|
+
export type PaddingStyle = {
|
|
89
|
+
paddingLeft?: UIValue | "safe-left" | "safe-left-comfort",
|
|
90
|
+
paddingTop?: UIValue | "safe-top" | "safe-top-comfort",
|
|
91
|
+
paddingRight?: UIValue | "safe-right" | "safe-right-comfort",
|
|
92
|
+
paddingBottom?: UIValue | "safe-bottom" | "safe-bottom-comfort",
|
|
93
|
+
padding?: UIValue | "safe-all" | "safe-all-comfort",
|
|
94
|
+
paddingHorizontal?: UIValue,
|
|
95
|
+
paddingVertical?: UIValue,
|
|
96
|
+
|
|
97
|
+
pl?: UIValue | "safe-left" | "safe-left-comfort",
|
|
98
|
+
pt?: UIValue | "safe-top" | "safe-top-comfort",
|
|
99
|
+
pr?: UIValue | "safe-right" | "safe-right-comfort",
|
|
100
|
+
pb?: UIValue | "safe-bottom" | "safe-bottom-comfort",
|
|
101
|
+
p?: UIValue | "safe-all" | "safe-all-comfort",
|
|
102
|
+
px?: UIValue,
|
|
103
|
+
py?: UIValue,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
export type MarginStyle = {
|
|
109
|
+
marginLeft?: UIValue | "auto" | "safe-left" | "safe-left-comfort",
|
|
110
|
+
marginTop?: UIValue | "auto" | "safe-top" | "safe-top-comfort",
|
|
111
|
+
marginRight?: UIValue | "auto" | "safe-right" | "safe-right-comfort",
|
|
112
|
+
marginBottom?: UIValue | "auto" | "safe-bottom" | "safe-bottom-comfort",
|
|
113
|
+
margin?: UIValue | "auto" | "safe-all" | "safe-all-comfort",
|
|
114
|
+
marginHorizontal?: UIValue | "auto",
|
|
115
|
+
marginVertical?: UIValue | "auto",
|
|
116
|
+
|
|
117
|
+
ml?: UIValue | "auto" | "safe-left" | "safe-left-comfort",
|
|
118
|
+
mt?: UIValue | "auto" | "safe-top" | "safe-top-comfort",
|
|
119
|
+
mr?: UIValue | "auto" | "safe-right" | "safe-right-comfort",
|
|
120
|
+
mb?: UIValue | "auto" | "safe-bottom" | "safe-bottom-comfort",
|
|
121
|
+
m?: UIValue | "auto" | "safe-all" | "safe-all-comfort",
|
|
122
|
+
mx?: UIValue | "auto",
|
|
123
|
+
my?: UIValue | "auto",
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export type PositionStyle = {
|
|
127
|
+
top?: UIValue | `${number}%` | 'safe-top' | 'safe-top-comfort',
|
|
128
|
+
left?: UIValue | `${number}%` | 'safe-left' | 'safe-left-comfort',
|
|
129
|
+
bottom?: UIValue | `${number}%` | 'safe-bottom' | 'safe-bottom-comfort',
|
|
130
|
+
right?: UIValue | `${number}%` | 'safe-right' | 'safe-right-comfort',
|
|
131
|
+
width?: UIValue | `${number}%` | "auto",
|
|
132
|
+
height?: UIValue | `${number}%` | "auto",
|
|
133
|
+
maxWidth?: UIValue | `${number}%` | "auto",
|
|
134
|
+
maxHeight?: UIValue | `${number}%` | "auto",
|
|
135
|
+
minWidth?: UIValue | `${number}%` | "auto",
|
|
136
|
+
minHeight?: UIValue | `${number}%` | "auto",
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export type ElementStyle = PaddingStyle & MarginStyle & BaseStyle & PositionStyle & {
|
|
140
|
+
position?: "absolute" | "static" | "relative",
|
|
141
|
+
aspectRatio?: number,
|
|
142
|
+
flex?: number | string,
|
|
143
|
+
flexGrow?: number,
|
|
144
|
+
flexShrink?: number,
|
|
145
|
+
flexBase?: UIValue | `${number}%` | "auto",
|
|
146
|
+
alignSelf?: "flex-start" | "center" | "flex-end" | "stretch",
|
|
147
|
+
display?: "none" | "flex"
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export type TextStyle = {
|
|
151
|
+
textAlign?: "start" | "center" | "end" | "left" | "right"
|
|
152
|
+
fontFamily?: string,
|
|
153
|
+
fontSize?: UIValue,
|
|
154
|
+
lineHeight?: UIValue | "normal",
|
|
155
|
+
fontWeight?: number | `${number}` | "normal" | "bold",
|
|
156
|
+
fontStyle?: "normal" | "italic",
|
|
157
|
+
color?: Color,
|
|
158
|
+
textDecoration?: "underline" | "line-through" | "none",
|
|
159
|
+
letterSpacing?: UIValue
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Не надо переписывать эту функцию 10 раз, обрати внимание, что у тебя тут возвращается this
|
|
163
|
+
function styleFunction(this: any, newStyle: any) {
|
|
164
|
+
Object.assign(this._style, newStyle)
|
|
165
|
+
if (this._id !== 0) {
|
|
166
|
+
_creatorUI.mergeStyle(this._id, newStyle)
|
|
167
|
+
}
|
|
168
|
+
return this
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const handler: ProxyHandler<any> = {
|
|
172
|
+
get(target, key, receiver) {
|
|
173
|
+
if (key === "_raw") {
|
|
174
|
+
return target._el._style
|
|
175
|
+
}
|
|
176
|
+
return target._el._style[key]
|
|
177
|
+
},
|
|
178
|
+
set(target, key: string, value: string | number) {
|
|
179
|
+
if (target._el._id !== 0) {
|
|
180
|
+
_creatorUI.updateStyle(target._el._id, key, value)
|
|
181
|
+
}
|
|
182
|
+
target._el._style[key] = value
|
|
183
|
+
return true
|
|
184
|
+
},
|
|
185
|
+
getOwnPropertyDescriptor(target, p) {
|
|
186
|
+
return Object.getOwnPropertyDescriptor(target._el._style, p)
|
|
187
|
+
},
|
|
188
|
+
ownKeys(target) {
|
|
189
|
+
return Object.keys(target._el._style)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function Style() {
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export type StyleFn <R, T extends object> = ((this: R, style: T & { onLandscape?: T, onPortait?: T }) => R)
|
|
198
|
+
export type Style <R, T extends object> = StyleFn<R,T> & T & { onLandscape?: T, onPortait?: T }
|
|
199
|
+
|
|
200
|
+
export type AnimateStyle<R, T extends object> = ((this: R, style: T & { duration?: number, delay?: number, layer?: number, commit?: boolean }) => R)
|
|
201
|
+
|
|
202
|
+
export type AppearStyle <R, T> = (arg: { from: T, duration?: number, delay?: number }) => R
|
|
203
|
+
export type DisappearStyle <R, T> = (arg: { to: T, duration?: number, delay?: number }) => R
|
|
204
|
+
|
|
205
|
+
export type OnLayoutCallback = (layout: { left: number, top: number, width: number, height: number }) => void
|
|
206
|
+
|
|
207
|
+
export class Element<T extends string> {
|
|
208
|
+
readonly type: T
|
|
209
|
+
/** Author-given semantic name (LeCodes `name`) — see BaseStyle.name. Extracted from the options
|
|
210
|
+
* object so it never reaches the layout engine; the renderer reads it off the node. */
|
|
211
|
+
readonly name?: string
|
|
212
|
+
protected _style: any
|
|
213
|
+
|
|
214
|
+
protected _id: number = 0
|
|
215
|
+
|
|
216
|
+
protected _styleProxy: any
|
|
217
|
+
protected _appear: any
|
|
218
|
+
protected _disappear: any
|
|
219
|
+
|
|
220
|
+
constructor(type: T, style: any) {
|
|
221
|
+
this.type = type
|
|
222
|
+
if (style && typeof style.name === "string" && style.name.length > 0) {
|
|
223
|
+
this.name = style.name
|
|
224
|
+
delete style.name
|
|
225
|
+
}
|
|
226
|
+
this._style = style
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
get style(): any {
|
|
230
|
+
if (this._styleProxy) return this._styleProxy
|
|
231
|
+
const obj = styleFunction.bind(this)
|
|
232
|
+
// @ts-ignore
|
|
233
|
+
obj._el = this
|
|
234
|
+
|
|
235
|
+
const proxy = new Proxy(obj, handler)
|
|
236
|
+
this._styleProxy = proxy
|
|
237
|
+
return proxy
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
animateTo(style: any): this{
|
|
241
|
+
_creatorUI.animateTo(this._id, style)
|
|
242
|
+
if (style.commit !== false) {
|
|
243
|
+
Object.assign(this._style, style)
|
|
244
|
+
}
|
|
245
|
+
return this
|
|
246
|
+
}
|
|
247
|
+
animateFrom(style: any): this{
|
|
248
|
+
_creatorUI.animateFrom(this._id, style)
|
|
249
|
+
return this
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
protected ll?: OnLayoutCallback[]
|
|
253
|
+
onLayout(onLayout: OnLayoutCallback): this {
|
|
254
|
+
if (this.ll) {
|
|
255
|
+
this.ll.push(onLayout)
|
|
256
|
+
} else {
|
|
257
|
+
this.ll = [ onLayout ]
|
|
258
|
+
}
|
|
259
|
+
return this
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export class ContainerElement<T extends string> extends Element<T> {
|
|
264
|
+
children: any[] = []
|
|
265
|
+
|
|
266
|
+
constructor(type: T, style: any, children: any[]) {
|
|
267
|
+
super(type, style)
|
|
268
|
+
this.children = children
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
append(...nodes: UINodeChild[]): this {
|
|
272
|
+
if (this._id === 0) {
|
|
273
|
+
this.children.push(...nodes)
|
|
274
|
+
return this
|
|
275
|
+
}
|
|
276
|
+
let index = 0
|
|
277
|
+
for (let child of this.children) {
|
|
278
|
+
if (child) {
|
|
279
|
+
index++
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
for (let node of nodes) {
|
|
283
|
+
if (!node) continue
|
|
284
|
+
_creatorUI.insertNode(index, this._id, node)
|
|
285
|
+
index++
|
|
286
|
+
}
|
|
287
|
+
this.children.push(...nodes)
|
|
288
|
+
return this
|
|
289
|
+
}
|
|
290
|
+
insert(index: number, ...nodes: UINodeChild[]): this {
|
|
291
|
+
if (this._id === 0) {
|
|
292
|
+
this.children.splice(index, 0, ...nodes)
|
|
293
|
+
return this
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
let _index = 0
|
|
297
|
+
for (let i = 0; i < index; i++) {
|
|
298
|
+
if (this.children[i]) {
|
|
299
|
+
_index++
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
for (let node of nodes) {
|
|
303
|
+
if (!node) continue
|
|
304
|
+
_creatorUI.insertNode(_index, this._id, node)
|
|
305
|
+
_index++
|
|
306
|
+
}
|
|
307
|
+
this.children.splice(index, 0, ...nodes)
|
|
308
|
+
return this
|
|
309
|
+
}
|
|
310
|
+
remove(...nodesToDelete: UINode[]): this {
|
|
311
|
+
const set = new Set(nodesToDelete)
|
|
312
|
+
|
|
313
|
+
for (let i = 0; i < this.children.length; i++) {
|
|
314
|
+
if (set.has(this.children[i])) {
|
|
315
|
+
set.delete(this.children[i])
|
|
316
|
+
this.children.splice(i, 1)
|
|
317
|
+
i--
|
|
318
|
+
|
|
319
|
+
if (set.size === 0) {
|
|
320
|
+
break
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (this._id === 0) {
|
|
326
|
+
return this
|
|
327
|
+
}
|
|
328
|
+
for (let node of nodesToDelete) {
|
|
329
|
+
_creatorUI.removeNode(this._id, node)
|
|
330
|
+
}
|
|
331
|
+
return this
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
setContent(children: UINodeChild[]): this {
|
|
335
|
+
if (this._id !== 0) {
|
|
336
|
+
_creatorUI.setContent(this._id, this.children, children)
|
|
337
|
+
}
|
|
338
|
+
this.children.length = 0
|
|
339
|
+
this.children.push(...children)
|
|
340
|
+
return this
|
|
341
|
+
}
|
|
342
|
+
}
|