lecodes-cli 0.6.4 → 0.7.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 +1 -0
- package/dist/index.js +920 -636
- package/package.json +9 -4
- package/runtime/scene-harness.json +1 -0
- package/runtime/sdk/compile/assetMacro.ts +4 -3
- package/runtime/sdk/compile/bundler.ts +35 -3
- package/runtime/sdk/compile/compileProject.ts +14 -2
- package/runtime/sdk/compile/libraryImports.ts +47 -0
- package/runtime/sdk/compile/sceneEditor.ts +16 -4
- package/runtime/sdk/core/Aspect.ts +37 -0
- package/runtime/sdk/core/InspectorUI.ts +212 -0
- package/runtime/sdk/core/fields.ts +15 -3
- package/runtime/sdk/gl/Node.ts +4 -1
- package/runtime/sdk/gl/scenarios.ts +349 -0
- package/runtime/sdk/inject.ts +14 -2
- package/runtime/sdk/kit/UITabs.ts +105 -0
- package/runtime/sdk/scene/defineScene.ts +651 -17
- package/runtime/sdk/scene/editorPlugins.ts +86 -0
- package/runtime/sdk/ui/UI.ts +1 -0
- package/runtime/sdk/ui/UIScreen.ts +23 -0
- package/runtime/sdk/ui/UIScreenHost.ts +212 -0
- package/runtime/sdk-types.json +1 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// UITabs — a self-contained tab component: a swipe-disabled UIScreenHost of tab screens + a bar of
|
|
2
|
+
// tap-to-switch buttons. Pure TS over UIScreenHost, no native work. It's just an element — you place
|
|
3
|
+
// it inside your own UIScreen and own the routing; UITabs only switches the visible tab.
|
|
4
|
+
//
|
|
5
|
+
// Router.push(UIScreen([
|
|
6
|
+
// UITabs([
|
|
7
|
+
// { icon: homeSvg, label: "Home", content: HomeScreen() },
|
|
8
|
+
// { icon: userSvg, label: "Profile", content: ProfileScreen() },
|
|
9
|
+
// ]),
|
|
10
|
+
// ]))
|
|
11
|
+
//
|
|
12
|
+
// A tab's content is a full UIScreen (screens are the only page kind UIScreenHost accepts): it gets
|
|
13
|
+
// onOpen/onClose from the host's settle lifecycle, plus makeScrollable/onRefresh. All tabs stay
|
|
14
|
+
// mounted, so switching is instant and per-tab state survives.
|
|
15
|
+
|
|
16
|
+
import { UIScreenHost, type HostedScreen } from "../ui/UIScreenHost"
|
|
17
|
+
import { UIRow, UIColumn, type UIColumn as UIColumnType } from "../ui/UIContainer"
|
|
18
|
+
import { UIButton } from "../ui/UIButton"
|
|
19
|
+
import { UIText } from "../ui/UIText"
|
|
20
|
+
import { UIImage, type ImageSource } from "../ui/UIImage"
|
|
21
|
+
import type { UINodeChild } from "../ui/UINode"
|
|
22
|
+
|
|
23
|
+
export interface UITabItem {
|
|
24
|
+
/** Icon image source (SvgSource / url / File / Canvas). Optional. */
|
|
25
|
+
icon?: ImageSource
|
|
26
|
+
/** Label under the icon. Optional. */
|
|
27
|
+
label?: string
|
|
28
|
+
/** Tab content — a full UIScreen (wrap a plain element: `UIScreen([el])`). */
|
|
29
|
+
content: HostedScreen
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** The tabs element (a UIColumn) plus a small control surface. Embed it like any element. */
|
|
33
|
+
export interface UITabs extends UIColumnType {
|
|
34
|
+
/** Switch the visible tab. */
|
|
35
|
+
select(index: number): this
|
|
36
|
+
onChange(callback: (index: number) => void): this
|
|
37
|
+
/** Active tab index. */
|
|
38
|
+
readonly index: number
|
|
39
|
+
/** The bar row — style it or its item buttons directly. */
|
|
40
|
+
readonly bar: UIRow
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const clamp = (v: number, min: number, max: number) => Math.max(min, Math.min(max, v))
|
|
44
|
+
|
|
45
|
+
function tabButton(item: UITabItem, i: number, onSelect: (i: number) => void): ReturnType<typeof UIButton> {
|
|
46
|
+
const parts: UINodeChild[] = []
|
|
47
|
+
if (item.icon) parts.push(UIImage({ width: 24, height: 24 }, item.icon))
|
|
48
|
+
if (item.label) parts.push(UIText({ fontSize: 11, lineHeight: 14, textAlign: "center" }, item.label))
|
|
49
|
+
|
|
50
|
+
// Chained .style() (not the legacy first-arg object) — required for the `$active` class block.
|
|
51
|
+
const btn = UIButton(parts).style({
|
|
52
|
+
flexGrow: 1,
|
|
53
|
+
flexBase: 0,
|
|
54
|
+
flexDirection: "column",
|
|
55
|
+
alignItems: "center",
|
|
56
|
+
justifyContent: "center",
|
|
57
|
+
gap: 3,
|
|
58
|
+
paddingVertical: 4,
|
|
59
|
+
opacity: 0.55,
|
|
60
|
+
$active: { opacity: 1, duration: 150 },
|
|
61
|
+
})
|
|
62
|
+
btn.onClick(() => onSelect(i))
|
|
63
|
+
return btn
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function UITabs(items: UITabItem[]): UITabs {
|
|
67
|
+
let index = 0
|
|
68
|
+
const listeners: ((index: number) => void)[] = []
|
|
69
|
+
|
|
70
|
+
const host = UIScreenHost(items.map(it => it.content))
|
|
71
|
+
.style({ swipeEnabled: false, flexGrow: 1, flexShrink: 1 })
|
|
72
|
+
|
|
73
|
+
const barItems = items.map((it, i) => tabButton(it, i, select))
|
|
74
|
+
const bar = UIRow({
|
|
75
|
+
flexShrink: 0,
|
|
76
|
+
paddingTop: 8,
|
|
77
|
+
paddingBottom: "safe-bottom",
|
|
78
|
+
borderTopWidth: 1,
|
|
79
|
+
borderTopColor: "#00000022",
|
|
80
|
+
}, barItems)
|
|
81
|
+
|
|
82
|
+
const applyActive = (i: number) => {
|
|
83
|
+
for (let k = 0; k < barItems.length; k++) barItems[k].setClass("active", k === i)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function select(i: number): UITabs {
|
|
87
|
+
i = clamp(i, 0, Math.max(0, items.length - 1))
|
|
88
|
+
if (i !== index) {
|
|
89
|
+
index = i
|
|
90
|
+
host.setPage(i, false)
|
|
91
|
+
applyActive(i)
|
|
92
|
+
for (const cb of listeners) cb(i)
|
|
93
|
+
}
|
|
94
|
+
return view
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
applyActive(0)
|
|
98
|
+
|
|
99
|
+
const view = UIColumn([ host, bar ]) as unknown as UITabs
|
|
100
|
+
;(view as any).select = select
|
|
101
|
+
;(view as any).onChange = (cb: (index: number) => void) => { listeners.push(cb); return view }
|
|
102
|
+
;(view as any).bar = bar
|
|
103
|
+
Object.defineProperty(view, "index", { get: () => index })
|
|
104
|
+
return view
|
|
105
|
+
}
|