@sigx/lynx-runtime-main 0.2.7 → 0.4.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/animated-bridge-mt.js +228 -0
- package/dist/animated-style-mappers.js +154 -0
- package/dist/element-registry.js +16 -0
- package/dist/entry-main.js +229 -1
- package/dist/event-slots.js +111 -0
- package/dist/hybrid-worklet.js +73 -0
- package/dist/index.js +22 -63
- package/dist/install-hybrid-worklet.js +19 -6
- package/dist/mt-element.js +203 -0
- package/dist/ops-apply.js +449 -0
- package/dist/run-on-background-mt.js +91 -0
- package/dist/upstream/observers.js +39 -0
- package/dist/upstream/processGesture.js +147 -0
- package/dist/worklet-events.js +47 -0
- package/dist/worklet-refs.js +43 -0
- package/package.json +8 -8
- package/dist/entry-main-CBM2DHsU.js +0 -615
- package/dist/entry-main-CBM2DHsU.js.map +0 -1
- package/dist/hybrid-worklet-nTcCh-mA.js +0 -58
- package/dist/hybrid-worklet-nTcCh-mA.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/install-hybrid-worklet.js.map +0 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MT-side per-slot event registration state machine.
|
|
3
|
+
*
|
|
4
|
+
* Lynx native's `__AddEvent(el, eventType, eventName, value)` only stores ONE
|
|
5
|
+
* value per `(el, eventType, eventName)` slot — the second call wins. When
|
|
6
|
+
* sigx user code declares both a `main-thread-bind*` worklet AND a regular
|
|
7
|
+
* `bind*` BG handler on the same element, two ops arrive in the same patch
|
|
8
|
+
* batch (SET_WORKLET_EVENT + SET_EVENT). Calling `__AddEvent` eagerly per op
|
|
9
|
+
* means the second one silently overwrites the first.
|
|
10
|
+
*
|
|
11
|
+
* This module defers the `__AddEvent` call. Each op updates per-slot state
|
|
12
|
+
* (`worklet?`, `bgSign?`) and marks the slot dirty. After the entire op
|
|
13
|
+
* batch is processed (`flushDirtySlots` is called at the tail of `applyOps`),
|
|
14
|
+
* we issue ONE `__AddEvent` per dirty slot using whichever shape combines
|
|
15
|
+
* the present handlers — string sign, worklet ctx, or hybrid ctx.
|
|
16
|
+
*
|
|
17
|
+
* State persists across batches because re-renders may update only one of
|
|
18
|
+
* the two handlers (the BG event-registry already deduplicates SET_EVENT
|
|
19
|
+
* after the first registration, and `sentWorklets` deduplicates
|
|
20
|
+
* SET_WORKLET_EVENT by `_wkltId`).
|
|
21
|
+
*/
|
|
22
|
+
import { elements } from './element-registry.js';
|
|
23
|
+
import { hybridCtx } from './hybrid-worklet.js';
|
|
24
|
+
/** elementId → typeName ('bindEvent:tap' etc.) → slot state */
|
|
25
|
+
const slotStates = new Map();
|
|
26
|
+
/** Set of `${elementId}|${typeName}` keys that need __AddEvent re-issuing. */
|
|
27
|
+
const dirtySlots = new Set();
|
|
28
|
+
function getOrCreateSlot(elId, type, name) {
|
|
29
|
+
let perEl = slotStates.get(elId);
|
|
30
|
+
if (!perEl) {
|
|
31
|
+
perEl = new Map();
|
|
32
|
+
slotStates.set(elId, perEl);
|
|
33
|
+
}
|
|
34
|
+
const typeName = `${type}:${name}`;
|
|
35
|
+
let slot = perEl.get(typeName);
|
|
36
|
+
if (!slot) {
|
|
37
|
+
slot = {};
|
|
38
|
+
perEl.set(typeName, slot);
|
|
39
|
+
}
|
|
40
|
+
return slot;
|
|
41
|
+
}
|
|
42
|
+
function markDirty(elId, type, name) {
|
|
43
|
+
dirtySlots.add(`${elId}|${type}:${name}`);
|
|
44
|
+
}
|
|
45
|
+
export function setSlotWorklet(elId, type, name, ctx) {
|
|
46
|
+
const slot = getOrCreateSlot(elId, type, name);
|
|
47
|
+
slot.worklet = ctx;
|
|
48
|
+
markDirty(elId, type, name);
|
|
49
|
+
}
|
|
50
|
+
export function setSlotBgSign(elId, type, name, sign) {
|
|
51
|
+
const slot = getOrCreateSlot(elId, type, name);
|
|
52
|
+
slot.bgSign = sign;
|
|
53
|
+
markDirty(elId, type, name);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Pick the right __AddEvent value given which handlers are present.
|
|
57
|
+
* Returns `undefined` to mean "unregister this slot".
|
|
58
|
+
*/
|
|
59
|
+
function computeAddEventValue(slot) {
|
|
60
|
+
const { worklet, bgSign } = slot;
|
|
61
|
+
if (!worklet && !bgSign)
|
|
62
|
+
return undefined;
|
|
63
|
+
if (worklet && !bgSign) {
|
|
64
|
+
return { type: 'worklet', value: worklet };
|
|
65
|
+
}
|
|
66
|
+
if (!worklet && bgSign) {
|
|
67
|
+
return bgSign;
|
|
68
|
+
}
|
|
69
|
+
return { type: 'worklet', value: hybridCtx(worklet, bgSign) };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Commit __AddEvent for every slot that changed since the last flush.
|
|
73
|
+
* Called from `applyOps` after the op loop, before `__FlushElementTree()`.
|
|
74
|
+
*/
|
|
75
|
+
export function flushDirtySlots() {
|
|
76
|
+
for (const key of dirtySlots) {
|
|
77
|
+
const sep = key.indexOf('|');
|
|
78
|
+
const elId = Number(key.slice(0, sep));
|
|
79
|
+
const typeName = key.slice(sep + 1);
|
|
80
|
+
const colon = typeName.indexOf(':');
|
|
81
|
+
const type = typeName.slice(0, colon);
|
|
82
|
+
const name = typeName.slice(colon + 1);
|
|
83
|
+
const el = elements.get(elId);
|
|
84
|
+
if (!el)
|
|
85
|
+
continue;
|
|
86
|
+
const slot = slotStates.get(elId)?.get(typeName);
|
|
87
|
+
if (!slot)
|
|
88
|
+
continue;
|
|
89
|
+
const value = computeAddEventValue(slot);
|
|
90
|
+
if (sameRef(value, slot.installed))
|
|
91
|
+
continue;
|
|
92
|
+
// Lynx PAPI: undefined as the 4th arg unregisters.
|
|
93
|
+
__AddEvent(el, type, name, value);
|
|
94
|
+
slot.installed = value;
|
|
95
|
+
}
|
|
96
|
+
dirtySlots.clear();
|
|
97
|
+
}
|
|
98
|
+
function sameRef(a, b) {
|
|
99
|
+
// Reference equality is enough for our usage:
|
|
100
|
+
// - undefined === undefined
|
|
101
|
+
// - bgSign string deduplicated by event-registry, so identity stable
|
|
102
|
+
// - worklet ctx is a fresh object per SET_WORKLET_EVENT op (never compared
|
|
103
|
+
// to itself across batches because the prev value would always differ),
|
|
104
|
+
// so this is mostly a defensive no-op for the first three branches.
|
|
105
|
+
return a === b;
|
|
106
|
+
}
|
|
107
|
+
/** Hot-reload / test reset hook — clears all slot state. */
|
|
108
|
+
export function resetSlotStates() {
|
|
109
|
+
slotStates.clear();
|
|
110
|
+
dirtySlots.clear();
|
|
111
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid worklet — combines a user worklet handler and a BG-side handler
|
|
3
|
+
* into a single registration that fits Lynx's one-handler-per-slot rule.
|
|
4
|
+
*
|
|
5
|
+
* Registered ONCE at MT init under the stable id `__sigx_hybrid_dispatch__`
|
|
6
|
+
* in upstream's `lynxWorkletImpl._workletMap`. When the slot machine sees a
|
|
7
|
+
* slot with both a worklet and a BG sign, it asks `hybridCtx(worklet, sign)`
|
|
8
|
+
* for the ctx to hand to `__AddEvent({ type: 'worklet', value })`.
|
|
9
|
+
*
|
|
10
|
+
* Lynx native dispatches via `runWorklet` → upstream's `transformWorklet`
|
|
11
|
+
* walks the ctx and `_c`. It detects nested `_wkltId` (our `realCtx`) and
|
|
12
|
+
* replaces it with the bound user-worklet callable. Our hybrid body then
|
|
13
|
+
* just invokes it, then bridges to BG via the `Lynx.Sigx.PublishEvent`
|
|
14
|
+
* channel that `bg-bridge.ts` listens on.
|
|
15
|
+
*/
|
|
16
|
+
export const HYBRID_WORKLET_ID = '__sigx_hybrid_dispatch__';
|
|
17
|
+
function hybridDispatch(event) {
|
|
18
|
+
if (this._c.realCtx) {
|
|
19
|
+
try {
|
|
20
|
+
this._c.realCtx(event);
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
console.log('[sigx-mt] hybrid worklet body threw:', String(e));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (this._c.bgSign)
|
|
27
|
+
bridgeToBg(this._c.bgSign, event);
|
|
28
|
+
}
|
|
29
|
+
function bridgeToBg(sign, event) {
|
|
30
|
+
const lynxObj = globalThis.lynx;
|
|
31
|
+
if (!lynxObj) {
|
|
32
|
+
console.log('[sigx-mt] bridgeToBg: globalThis.lynx is undefined');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const ctx = lynxObj.getJSContext?.();
|
|
36
|
+
if (!ctx) {
|
|
37
|
+
console.log('[sigx-mt] bridgeToBg: lynx.getJSContext() returned', typeof ctx);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (!ctx.dispatchEvent) {
|
|
41
|
+
console.log('[sigx-mt] bridgeToBg: jsContext has no dispatchEvent', Object.keys(ctx).join(','));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
let data;
|
|
45
|
+
try {
|
|
46
|
+
data = JSON.stringify({ sign, event });
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
console.log('[sigx-mt] bridgeToBg: JSON.stringify failed', String(e));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
console.log('[sigx-mt] bridgeToBg: dispatching to BG, sign=', sign);
|
|
53
|
+
ctx.dispatchEvent({ type: 'Lynx.Sigx.PublishEvent', data });
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Install the hybrid worklet into upstream's worklet map. Must be called
|
|
57
|
+
* AFTER the @lynx-js/react/worklet-runtime IIFE has populated
|
|
58
|
+
* globalThis.lynxWorkletImpl. Idempotent — safe to call across hot reloads.
|
|
59
|
+
*/
|
|
60
|
+
export function installHybridWorklet() {
|
|
61
|
+
const impl = globalThis
|
|
62
|
+
.lynxWorkletImpl;
|
|
63
|
+
if (impl)
|
|
64
|
+
impl._workletMap[HYBRID_WORKLET_ID] = hybridDispatch;
|
|
65
|
+
}
|
|
66
|
+
/** Build the ctx for a hybrid registration. */
|
|
67
|
+
export function hybridCtx(realCtx, bgSign) {
|
|
68
|
+
return {
|
|
69
|
+
_wkltId: HYBRID_WORKLET_ID,
|
|
70
|
+
_workletType: 'main-thread',
|
|
71
|
+
_c: { realCtx, bgSign },
|
|
72
|
+
};
|
|
73
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,64 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
getAttributeNames() {
|
|
24
|
-
return typeof __GetAttributeNames == "function" ? __GetAttributeNames(this._el) : [];
|
|
25
|
-
}
|
|
26
|
-
querySelector(t) {
|
|
27
|
-
if (typeof __QuerySelector != "function") return null;
|
|
28
|
-
let n = __QuerySelector(this._el, t, {});
|
|
29
|
-
return n ? new e(n) : null;
|
|
30
|
-
}
|
|
31
|
-
querySelectorAll(t) {
|
|
32
|
-
return typeof __QuerySelectorAll == "function" ? __QuerySelectorAll(this._el, t, {}).map((t) => new e(t)) : [];
|
|
33
|
-
}
|
|
34
|
-
invoke(e, t) {
|
|
35
|
-
return new Promise((n, r) => {
|
|
36
|
-
if (typeof __InvokeUIMethod != "function") {
|
|
37
|
-
r(/* @__PURE__ */ Error("UI method invoke: __InvokeUIMethod not available"));
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
__InvokeUIMethod(this._el, e, t ?? {}, (e) => {
|
|
41
|
-
e.code === 0 ? n(e.data) : r(/* @__PURE__ */ Error("UI method invoke: " + JSON.stringify(e)));
|
|
42
|
-
}), this.flushElementTree();
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
animate(e, t = {}) {
|
|
46
|
-
let n = this._el;
|
|
47
|
-
return typeof n.animate == "function" ? n.animate(e, t) : null;
|
|
48
|
-
}
|
|
49
|
-
setAttribute(e, t) {
|
|
50
|
-
__SetAttribute(this._el, e, t), this.flushElementTree();
|
|
51
|
-
}
|
|
52
|
-
setStyleProperty(e, t) {
|
|
53
|
-
__AddInlineStyle(this._el, e, t), this.flushElementTree();
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
//#endregion
|
|
57
|
-
//#region src/index.ts
|
|
58
|
-
function h(e) {
|
|
59
|
-
return !0;
|
|
1
|
+
/// <reference path="./shims.d.ts" />
|
|
2
|
+
// Side-effect import — entry-main.ts registers globalThis.renderPage /
|
|
3
|
+
// processData / sigxPatchUpdate / sigxRunOnMT and side-effect imports
|
|
4
|
+
// @lynx-js/react/worklet-runtime which installs lynxWorkletImpl /
|
|
5
|
+
// registerWorkletInternal / runWorklet. Must run at module load time so
|
|
6
|
+
// the main-thread entry of the Lynx template has these globals.
|
|
7
|
+
import './entry-main.js';
|
|
8
|
+
export { elements, pageUniqueId, setPageUniqueId } from './element-registry.js';
|
|
9
|
+
export { applyOps, resetMainThreadState } from './ops-apply.js';
|
|
10
|
+
export { MTElementWrapper } from './mt-element.js';
|
|
11
|
+
export { invokeWorklet } from './worklet-events.js';
|
|
12
|
+
export { setSlotWorklet, setSlotBgSign, flushDirtySlots, resetSlotStates, } from './event-slots.js';
|
|
13
|
+
export { HYBRID_WORKLET_ID, hybridCtx, installHybridWorklet, } from './hybrid-worklet.js';
|
|
14
|
+
/**
|
|
15
|
+
* Compatibility shim — upstream's worklet-runtime provides the canonical
|
|
16
|
+
* `loadWorkletRuntime`, but @lynx-js/react/transform's LEPUS output imports
|
|
17
|
+
* it from `runtimePkg`. Our MT loader strips the import + gating before
|
|
18
|
+
* shipping registrations, so this re-export is only for the rare case where
|
|
19
|
+
* upstream's raw output is used unstripped (tests, future Phase 1c).
|
|
20
|
+
*/
|
|
21
|
+
export function loadWorkletRuntime(_globDynamicComponentEntry) {
|
|
22
|
+
return true;
|
|
60
23
|
}
|
|
61
|
-
//#endregion
|
|
62
|
-
export { f as HYBRID_WORKLET_ID, m as MTElementWrapper, c as applyOps, t as elements, o as flushDirtySlots, u as hybridCtx, d as installHybridWorklet, s as invokeWorklet, h as loadWorkletRuntime, r as pageUniqueId, i as resetMainThreadState, n as resetSlotStates, l as setPageUniqueId, e as setSlotBgSign, a as setSlotWorklet };
|
|
63
|
-
|
|
64
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Side-effect module that installs the hybrid worklet into upstream's
|
|
3
|
+
* `lynxWorkletImpl._workletMap`.
|
|
4
|
+
*
|
|
5
|
+
* Must be loaded AFTER `@lynx-js/react/worklet-runtime` (which populates
|
|
6
|
+
* `lynxWorkletImpl`) but BEFORE user code runs. The MT bootstrap preamble
|
|
7
|
+
* in `lynx-plugin/src/loaders/worklet-loader-mt.ts` lists it third in the
|
|
8
|
+
* import order:
|
|
9
|
+
*
|
|
10
|
+
* 1. @sigx/lynx-runtime-main/entry-main (sets SystemInfo + globals)
|
|
11
|
+
* 2. @lynx-js/react/worklet-runtime (installs lynxWorkletImpl)
|
|
12
|
+
* 3. @sigx/lynx-runtime-main/install-hybrid (this file)
|
|
13
|
+
* 4. user code (calls registerWorkletInternal)
|
|
14
|
+
*
|
|
15
|
+
* Splitting this out is necessary because vite would otherwise hoist any
|
|
16
|
+
* bare `import` statement above whatever sets up its prerequisites.
|
|
17
|
+
*/
|
|
18
|
+
import { installHybridWorklet } from './hybrid-worklet.js';
|
|
19
|
+
installHybridWorklet();
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MainThread element wrapper — provides a high-level API over raw PAPI
|
|
3
|
+
* element handles for use in main-thread event handlers.
|
|
4
|
+
*
|
|
5
|
+
* When a MainThreadRef's `.current` is set, it receives one of these
|
|
6
|
+
* wrappers so user code can call:
|
|
7
|
+
* - setStyleProperties({ transform: '...' })
|
|
8
|
+
* - getComputedStyleProperty('width')
|
|
9
|
+
* - animate(keyframes, options)
|
|
10
|
+
*
|
|
11
|
+
* These methods call Lynx PAPI directly on the main thread — no cross-thread
|
|
12
|
+
* round-trip, enabling zero-latency UI updates.
|
|
13
|
+
*/
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// MTElementWrapper
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
/**
|
|
18
|
+
* Module-level latch that microtask-debounces `__FlushElementTree()` across
|
|
19
|
+
* every MTElementWrapper write within a single microtask. Mirrors the
|
|
20
|
+
* pattern in upstream `@lynx-js/react`'s `Element` class
|
|
21
|
+
* (`runtime/lib/worklet-runtime/api/element.js`):
|
|
22
|
+
*
|
|
23
|
+
* - First write in a microtask: schedule one Promise-microtask to flush.
|
|
24
|
+
* - Subsequent writes (same wrapper or any other) before the microtask
|
|
25
|
+
* fires: no-op (`willFlush` is already `true`).
|
|
26
|
+
* - Microtask runs: clear the latch, call the (animated-bridge-wrapped)
|
|
27
|
+
* `__FlushElementTree()` exactly once.
|
|
28
|
+
*
|
|
29
|
+
* Why the latch is module-level, not per-instance: a single tick often
|
|
30
|
+
* touches multiple element wrappers (e.g. `<Draggable>` writes its own
|
|
31
|
+
* transform AND issues `scrollBy` on the parent `<scroll-view>`). Two
|
|
32
|
+
* wrappers, two writes, but the native flush is a tree-wide operation —
|
|
33
|
+
* coalescing them into one is the whole point.
|
|
34
|
+
*
|
|
35
|
+
* Without this debounce, each of `setStyleProperties` / `setStyleProperty`
|
|
36
|
+
* / `setAttribute` / `invoke` was firing a synchronous
|
|
37
|
+
* `__FlushElementTree()`, paying the full layout pass per-call. The
|
|
38
|
+
* stutter showed up most obviously in the `<Draggable edgeScroll>` rAF
|
|
39
|
+
* tick (one transform write + one scrollBy invoke = 2 flushes/frame).
|
|
40
|
+
*/
|
|
41
|
+
let willFlush = false;
|
|
42
|
+
export class MTElementWrapper {
|
|
43
|
+
/** The raw PAPI element handle. */
|
|
44
|
+
_el;
|
|
45
|
+
constructor(el) {
|
|
46
|
+
this._el = el;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Coalesce multiple element writes into a single `__FlushElementTree()`
|
|
50
|
+
* call at the end of the current microtask. See `willFlush` doc for the
|
|
51
|
+
* full rationale; mirrors `Element.flushElementTree` in upstream
|
|
52
|
+
* `@lynx-js/react`.
|
|
53
|
+
*
|
|
54
|
+
* `__FlushElementTree` itself is wrapped by `animated-bridge-mt` to
|
|
55
|
+
* apply pending `useAnimatedStyle` bindings before the native flush, so
|
|
56
|
+
* the debounce automatically coalesces SV-binding application too.
|
|
57
|
+
*/
|
|
58
|
+
flushElementTree() {
|
|
59
|
+
if (willFlush)
|
|
60
|
+
return;
|
|
61
|
+
willFlush = true;
|
|
62
|
+
void Promise.resolve().then(() => {
|
|
63
|
+
willFlush = false;
|
|
64
|
+
__FlushElementTree();
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Synchronously update inline styles on this element.
|
|
69
|
+
* This bypasses the BG→MT op queue — styles are applied immediately
|
|
70
|
+
* on the main thread, making it ideal for scroll-driven animations.
|
|
71
|
+
*
|
|
72
|
+
* The native `__FlushElementTree` call is microtask-debounced (see
|
|
73
|
+
* `flushElementTree`), so chaining multiple writes within one tick
|
|
74
|
+
* pays for a single flush instead of one per call.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* ref.current?.setStyleProperties({
|
|
79
|
+
* transform: `translateX(${offset}px)`,
|
|
80
|
+
* opacity: `${1 - ratio}`,
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
setStyleProperties(styles) {
|
|
85
|
+
__SetInlineStyles(this._el, styles);
|
|
86
|
+
this.flushElementTree();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get a computed style property value from this element.
|
|
90
|
+
*
|
|
91
|
+
* @param name - CSS property name in kebab-case (e.g. 'background-color')
|
|
92
|
+
* @returns The computed value as a string
|
|
93
|
+
*/
|
|
94
|
+
getComputedStyleProperty(name) {
|
|
95
|
+
if (typeof __GetComputedStyleByKey === 'function') {
|
|
96
|
+
return __GetComputedStyleByKey(this._el, name);
|
|
97
|
+
}
|
|
98
|
+
return '';
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get a single attribute value by name. Returns the value as set via
|
|
102
|
+
* `setAttribute` (or by a parent component); does not resolve aliases.
|
|
103
|
+
*/
|
|
104
|
+
getAttribute(name) {
|
|
105
|
+
if (typeof __GetAttributeByName === 'function') {
|
|
106
|
+
return __GetAttributeByName(this._el, name);
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Get the list of attribute names currently set on this element.
|
|
112
|
+
*/
|
|
113
|
+
getAttributeNames() {
|
|
114
|
+
if (typeof __GetAttributeNames === 'function') {
|
|
115
|
+
return __GetAttributeNames(this._el);
|
|
116
|
+
}
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Find the first descendant element matching the CSS selector.
|
|
121
|
+
* Mirrors `Element.prototype.querySelector` from the DOM.
|
|
122
|
+
*
|
|
123
|
+
* Returns `null` when no match is found OR when the host runtime does not
|
|
124
|
+
* provide `__QuerySelector` (older Lynx SDKs).
|
|
125
|
+
*/
|
|
126
|
+
querySelector(selector) {
|
|
127
|
+
if (typeof __QuerySelector !== 'function')
|
|
128
|
+
return null;
|
|
129
|
+
const ref = __QuerySelector(this._el, selector, {});
|
|
130
|
+
return ref ? new MTElementWrapper(ref) : null;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Find every descendant element matching the CSS selector. Returns an empty
|
|
134
|
+
* array when nothing matches OR when the host runtime does not provide
|
|
135
|
+
* `__QuerySelectorAll`.
|
|
136
|
+
*/
|
|
137
|
+
querySelectorAll(selector) {
|
|
138
|
+
if (typeof __QuerySelectorAll !== 'function')
|
|
139
|
+
return [];
|
|
140
|
+
return __QuerySelectorAll(this._el, selector, {})
|
|
141
|
+
.map((el) => new MTElementWrapper(el));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Invoke a UI method exposed by the underlying native element (e.g.
|
|
145
|
+
* `scrollIntoView` on `<scroll-view>`, `scrollToIndex` on `<list>`).
|
|
146
|
+
*
|
|
147
|
+
* Resolves with the method's `data` payload on success (`code === 0`);
|
|
148
|
+
* rejects with an Error containing the JSON-stringified response otherwise.
|
|
149
|
+
*/
|
|
150
|
+
invoke(methodName, params) {
|
|
151
|
+
return new Promise((resolve, reject) => {
|
|
152
|
+
if (typeof __InvokeUIMethod !== 'function') {
|
|
153
|
+
reject(new Error('UI method invoke: __InvokeUIMethod not available'));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
__InvokeUIMethod(this._el, methodName, params ?? {}, (res) => {
|
|
157
|
+
if (res.code === 0)
|
|
158
|
+
resolve(res.data);
|
|
159
|
+
else
|
|
160
|
+
reject(new Error('UI method invoke: ' + JSON.stringify(res)));
|
|
161
|
+
});
|
|
162
|
+
this.flushElementTree();
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Start a keyframe animation on this element using the Lynx animation API.
|
|
167
|
+
*
|
|
168
|
+
* @param keyframes - Array of keyframe objects
|
|
169
|
+
* @param options - Animation configuration
|
|
170
|
+
* @returns Animation controller with play/pause/cancel methods
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* ref.current?.animate(
|
|
175
|
+
* [{ opacity: 0 }, { opacity: 1 }],
|
|
176
|
+
* { duration: 300, easing: 'ease-in-out' }
|
|
177
|
+
* );
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
animate(keyframes, options = {}) {
|
|
181
|
+
const el = this._el;
|
|
182
|
+
if (typeof el.animate === 'function') {
|
|
183
|
+
return el.animate(keyframes, options);
|
|
184
|
+
}
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Set a single attribute on this element. Microtask-debounced flush —
|
|
189
|
+
* see `flushElementTree`.
|
|
190
|
+
*/
|
|
191
|
+
setAttribute(key, value) {
|
|
192
|
+
__SetAttribute(this._el, key, value);
|
|
193
|
+
this.flushElementTree();
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Set a single inline style property. Microtask-debounced flush — see
|
|
197
|
+
* `flushElementTree`.
|
|
198
|
+
*/
|
|
199
|
+
setStyleProperty(name, value) {
|
|
200
|
+
__AddInlineStyle(this._el, name, value);
|
|
201
|
+
this.flushElementTree();
|
|
202
|
+
}
|
|
203
|
+
}
|