lecodes-cli 0.8.2 → 0.9.1
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 +745 -164
- package/package.json +36 -36
- package/runtime/sdk/inject.ts +2 -1
- package/runtime/sdk/ui/UI.ts +2 -1
- package/runtime/sdk/ui/UIBottomSheet.ts +149 -0
- package/runtime/sdk/ui/UIInput.ts +24 -1
- package/runtime/sdk/ui/UINode.ts +7 -6
- package/runtime/sdk/ui/UIPager.ts +71 -3
- package/runtime/sdk/ui/UIScreen.ts +7 -57
- package/runtime/sdk/ui/UIScrollable.ts +27 -6
- package/runtime/sdk/ui/UIVirtualizedList.ts +16 -3
- package/runtime/sdk/ui/UIWidget.ts +8 -2
- package/runtime/sdk/ui/presentable.ts +2 -1
- package/runtime/sdk/ui/router.ts +4 -2
- package/runtime/sdk/ui/theme.ts +34 -7
- package/runtime/sdk-types.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Element, type DrawableStyle, type ElementStyle, type OnLayoutCallback, type BoundingClientRect, type PaddingStyle, type Style, type UINodeChild } from "./UINode"
|
|
1
|
+
import { Element, type Color, type DrawableStyle, type ElementStyle, type OnLayoutCallback, type BoundingClientRect, type PaddingStyle, type Style, type UINodeChild } from "./UINode"
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// refreshControlColor tints the native pull-to-refresh spinner (pairs with onRefresh).
|
|
4
|
+
export type UIVirtualizedListStyle = DrawableStyle & ElementStyle & { refreshControlColor?: Color }
|
|
4
5
|
|
|
5
6
|
export interface VListConfig<T> {
|
|
6
7
|
/** Стабильный уникальный ключ айтема */
|
|
@@ -40,6 +41,9 @@ export interface UIVirtualizedList<T = unknown> {
|
|
|
40
41
|
scrollToEnd(animated?: boolean): void
|
|
41
42
|
|
|
42
43
|
onScroll(callback: (scrollPosition: number) => void): this
|
|
44
|
+
/** Pull-to-refresh: спиннер держится, пока не разрешится промис. Навесить ДО маунта
|
|
45
|
+
* (нативка читает при создании ноды). Только native-хосты; на вебе no-op. */
|
|
46
|
+
onRefresh(callback: () => void | Promise<void>): this
|
|
43
47
|
/** Порог в px от нижнего края контента. Срабатывает один раз до ухода от края (защёлка в нативке) */
|
|
44
48
|
onEndReached(thresholdPx: number, callback: () => void): this
|
|
45
49
|
/** Порог от верхнего края — подгрузка истории в чате */
|
|
@@ -76,7 +80,10 @@ export class VListElement<T> extends Element<"vlist"> implements UIVirtualizedLi
|
|
|
76
80
|
private estimate: (item: T) => number
|
|
77
81
|
|
|
78
82
|
constructor(config: VListConfig<T>) {
|
|
79
|
-
|
|
83
|
+
// flexShrink: 1 default (SDK-owned, same as UIScrollable): an explicit height/flexBasis
|
|
84
|
+
// larger than the available space shrinks to fit instead of overflowing. The vlist's own
|
|
85
|
+
// yoga basis is 0 (items live in detached roots), so this is inert until a size is set.
|
|
86
|
+
super("vlist", { flexShrink: 1 })
|
|
80
87
|
this.keyOf = config.keyOf
|
|
81
88
|
this.renderItem = config.render
|
|
82
89
|
this.estimate = typeof config.estimatedHeight === "function"
|
|
@@ -191,6 +198,12 @@ export class VListElement<T> extends Element<"vlist"> implements UIVirtualizedLi
|
|
|
191
198
|
this.sl.push(callback)
|
|
192
199
|
return this
|
|
193
200
|
}
|
|
201
|
+
// Читается нативкой при маунте (тот же контракт, что у UIScrollable).
|
|
202
|
+
declare _refreshCallback: any
|
|
203
|
+
onRefresh(callback: () => void | Promise<void>): this {
|
|
204
|
+
this._refreshCallback = callback
|
|
205
|
+
return this
|
|
206
|
+
}
|
|
194
207
|
onEndReached(thresholdPx: number, callback: () => void): this {
|
|
195
208
|
this._endThreshold = thresholdPx
|
|
196
209
|
this.erl.push(callback)
|
|
@@ -20,7 +20,10 @@ export interface UIWidget {
|
|
|
20
20
|
* Attach this widget to a destination (a `Scene`, a `UIScreen`, …): the widget lives on that
|
|
21
21
|
* page's layer — it shows/hides with the page and rides its transition, and a screen pushed
|
|
22
22
|
* on top covers it (a scene HUD behaves like part of the scene). Sticky until `attachTo(null)`
|
|
23
|
-
* (= back to a global overlay). Set it while the widget is hidden.
|
|
23
|
+
* (= back to a global overlay). Set it while the widget is hidden. The widget is always its own
|
|
24
|
+
* overlay root mounted into the owner's page view — it never becomes a child of the owner, so it
|
|
25
|
+
* stays topmost and its updates never trigger the owner's relayout. Works with a `UIScreen` that
|
|
26
|
+
* is a page inside a `UIPager`: the widget mounts into that tab and rides its swipe.
|
|
24
27
|
*/
|
|
25
28
|
attachTo(owner: Presentable | null): this,
|
|
26
29
|
|
|
@@ -66,7 +69,10 @@ export class WidgetElement extends ContainerElement<"widget"> {
|
|
|
66
69
|
return this
|
|
67
70
|
}
|
|
68
71
|
show(): void {
|
|
69
|
-
//
|
|
72
|
+
// A widget is always its OWN overlay root (a separate Yoga tree mounted above the owner's
|
|
73
|
+
// content) — never a child of the owner, so it stays topmost, its updates never relayout the
|
|
74
|
+
// owner, and it doesn't pollute the owner's children. `attachTo(owner)` just tells the host
|
|
75
|
+
// which page view to mount that root into; ownerless widgets are global overlays.
|
|
70
76
|
if (this._owner && _navSupported()) {
|
|
71
77
|
_creatorUI.showWidget(this, _descOf(this._owner))
|
|
72
78
|
} else {
|
|
@@ -49,7 +49,8 @@ export type Transition = TransitionName | TransitionSpec
|
|
|
49
49
|
|
|
50
50
|
export type PresentOptions = {
|
|
51
51
|
/** Transition to play while this destination replaces the current one (default "none" for a
|
|
52
|
-
* direct open(); the Router applies its own defaults — push/
|
|
52
|
+
* direct open(); the Router applies its own defaults — push/pop, and the theme's
|
|
53
|
+
* `replaceTransition` for replace). */
|
|
53
54
|
transition?: Transition
|
|
54
55
|
}
|
|
55
56
|
|
package/runtime/sdk/ui/router.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { _bumpNavEpoch, _descOf, _navSupported, _setCurrent, Presentable, type Transition } from "./presentable"
|
|
2
|
+
import { _navDefaults } from "./theme"
|
|
2
3
|
|
|
3
4
|
type RouterOptions = {
|
|
4
5
|
showDefaultBackButton?: boolean
|
|
5
6
|
}
|
|
6
7
|
|
|
7
8
|
type NavigateOptions = {
|
|
8
|
-
/** Transition to play. Defaults: `"push"` for push, `"
|
|
9
|
+
/** Transition to play. Defaults: `"push"` for push, `"pop"` for pop; replace follows the
|
|
10
|
+
* theme's `replaceTransition` (`"none"` out of the box). */
|
|
9
11
|
transition?: Transition
|
|
10
12
|
}
|
|
11
13
|
|
|
@@ -74,7 +76,7 @@ export const Router: Router = /*#__PURE__*/ Object.freeze({
|
|
|
74
76
|
const desc = wireDesc(page)
|
|
75
77
|
const go = () => {
|
|
76
78
|
_bumpNavEpoch()
|
|
77
|
-
_creatorUI.routerReplace(desc, opts?.transition ??
|
|
79
|
+
_creatorUI.routerReplace(desc, opts?.transition ?? _navDefaults.replace)
|
|
78
80
|
_setCurrent(page) // optimistic — host onChange confirms (async on native hosts)
|
|
79
81
|
}
|
|
80
82
|
const prep = page._prepare?.()
|
package/runtime/sdk/ui/theme.ts
CHANGED
|
@@ -20,30 +20,57 @@
|
|
|
20
20
|
// numbers are LENGTHS (px). `null` removes a key (comfort knobs reset to their defaults:
|
|
21
21
|
// 12 vertical, 16 horizontal — the page gutter). Env names (safe-*, vw/vh/vmin/vmax) are
|
|
22
22
|
// host-owned facts, never theme keys — they're skipped with a warning.
|
|
23
|
+
//
|
|
24
|
+
// One key is not a style var: `replaceTransition` — the app-wide default for
|
|
25
|
+
// `Router.replace` (any Transition, name or custom spec; `null` resets to "none"). The
|
|
26
|
+
// router resolves it in the SDK, so it's consumed here and never sent to the host table.
|
|
27
|
+
|
|
28
|
+
import type { Transition } from "./presentable"
|
|
23
29
|
|
|
24
30
|
export type ThemeValues = Record<string, string | number | null>
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
/** @internal Navigation defaults the theme owns; the router reads them at call time.
|
|
33
|
+
* (SDK-side state, deliberately NOT in the host var table — `var(--replaceTransition)`
|
|
34
|
+
* is not a style value.) */
|
|
35
|
+
export const _navDefaults: { replace: Transition } = { replace: "none" }
|
|
36
|
+
|
|
37
|
+
// (Unconstrained: it also maps inputs whose `replaceTransition` key was carved out.)
|
|
38
|
+
export type ThemeAccessors<T> = {
|
|
27
39
|
readonly [K in keyof T]: K extends string ? `var(--${K})` : never
|
|
28
40
|
}
|
|
29
41
|
|
|
30
42
|
const RESERVED = ["safe-top", "safe-bottom", "safe-left", "safe-right", "vw", "vh", "vmin", "vmax"]
|
|
31
43
|
|
|
32
|
-
|
|
44
|
+
/** Merge app theme variables and get typed `var(--key)` accessors back. Strings/numbers are
|
|
45
|
+
* style values (numbers = px); `null` removes a key. One special key: `replaceTransition` —
|
|
46
|
+
* the app-wide default transition for `Router.replace` (any transition name or custom spec;
|
|
47
|
+
* out of the box `"none"`). */
|
|
48
|
+
const setTheme = <T extends Record<string, string | number | Transition | null>>(
|
|
49
|
+
// Only `replaceTransition` may carry a Transition — every other key is a style value.
|
|
50
|
+
values: T & { [K in Exclude<keyof T, "replaceTransition">]: string | number | null }
|
|
51
|
+
): ThemeAccessors<Omit<T, "replaceTransition">> => {
|
|
33
52
|
const accessors: Record<string, string> = {}
|
|
34
|
-
let send
|
|
53
|
+
let send = values as ThemeValues
|
|
54
|
+
const strip = (key: string) => {
|
|
55
|
+
if (send === values) send = { ...values } as ThemeValues
|
|
56
|
+
delete send[key]
|
|
57
|
+
}
|
|
35
58
|
for (const key of Object.keys(values)) {
|
|
59
|
+
if (key === "replaceTransition") {
|
|
60
|
+
_navDefaults.replace = (values[key] ?? "none") as Transition
|
|
61
|
+
strip(key) // SDK-consumed, no accessor: it isn't a style var
|
|
62
|
+
continue
|
|
63
|
+
}
|
|
36
64
|
if (RESERVED.includes(key)) {
|
|
37
65
|
console.warn(`theme(): "${key}" is an environment value (host-owned) — ignored`)
|
|
38
|
-
|
|
39
|
-
delete send[key]
|
|
66
|
+
strip(key)
|
|
40
67
|
continue
|
|
41
68
|
}
|
|
42
69
|
accessors[key] = `var(--${key})`
|
|
43
70
|
}
|
|
44
71
|
// Optional during rollout: hosts without the method simply don't theme (values still type).
|
|
45
|
-
_creatorUI.setTheme?.(send)
|
|
46
|
-
return accessors as ThemeAccessors<T
|
|
72
|
+
if (Object.keys(send).length > 0) _creatorUI.setTheme?.(send)
|
|
73
|
+
return accessors as ThemeAccessors<Omit<T, "replaceTransition">>
|
|
47
74
|
}
|
|
48
75
|
|
|
49
76
|
export const theme = Object.assign(setTheme, {
|