@lingxia/elements 0.15.0 → 0.16.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/dist/index.d.ts +3 -2
- package/dist/index.js +1 -0
- package/dist/inline-native/commit.d.ts +39 -0
- package/dist/inline-native/commit.js +94 -0
- package/dist/inline-native/elements.d.ts +104 -0
- package/dist/inline-native/elements.js +749 -0
- package/dist/inline-native/errors.d.ts +8 -0
- package/dist/inline-native/errors.js +23 -0
- package/dist/inline-native/events.d.ts +10 -0
- package/dist/inline-native/events.js +16 -0
- package/dist/inline-native/geometry.d.ts +78 -0
- package/dist/inline-native/geometry.js +40 -0
- package/dist/inline-native/identity.d.ts +28 -0
- package/dist/inline-native/identity.js +51 -0
- package/dist/inline-native/index.d.ts +14 -0
- package/dist/inline-native/index.js +19 -0
- package/dist/inline-native/layout.d.ts +2 -0
- package/dist/inline-native/layout.js +58 -0
- package/dist/inline-native/lease.d.ts +13 -0
- package/dist/inline-native/lease.js +31 -0
- package/dist/inline-native/runtime.d.ts +64 -0
- package/dist/inline-native/runtime.js +119 -0
- package/dist/inline-native/schema.d.ts +111 -0
- package/dist/inline-native/schema.js +210 -0
- package/dist/inline-native/structure.d.ts +22 -0
- package/dist/inline-native/structure.js +706 -0
- package/dist/inline-native/style.d.ts +7 -0
- package/dist/inline-native/style.js +119 -0
- package/dist/inline-native/types.d.ts +82 -0
- package/dist/inline-native/types.js +7 -0
- package/dist/inline-native/unwrap.d.ts +10 -0
- package/dist/inline-native/unwrap.js +32 -0
- package/dist/inline-native/video.d.ts +65 -0
- package/dist/inline-native/video.js +69 -0
- package/dist/native_component_wrapper_shared.d.ts +3 -2
- package/dist/native_component_wrapper_shared.js +6 -3
- package/dist/nativecomponent.d.ts +1 -1
- package/dist/nativecomponent.js +8 -11
- package/dist/navigator.d.ts +2 -2
- package/dist/navigator.js +5 -5
- package/dist/video-events.d.ts +2 -0
- package/dist/video-events.js +18 -0
- package/dist/video.d.ts +98 -79
- package/dist/video.js +182 -757
- package/package.json +2 -1
- package/src/index.ts +58 -1
- package/src/inline-native/commit.ts +141 -0
- package/src/inline-native/elements.ts +829 -0
- package/src/inline-native/errors.ts +35 -0
- package/src/inline-native/events.ts +31 -0
- package/src/inline-native/geometry.ts +86 -0
- package/src/inline-native/identity.ts +102 -0
- package/src/inline-native/index.ts +103 -0
- package/src/inline-native/layout.ts +58 -0
- package/src/inline-native/lease.ts +48 -0
- package/src/inline-native/runtime.ts +185 -0
- package/src/inline-native/schema.ts +247 -0
- package/src/inline-native/structure.ts +823 -0
- package/src/inline-native/style.ts +111 -0
- package/src/inline-native/types.ts +105 -0
- package/src/inline-native/unwrap.ts +34 -0
- package/src/inline-native/video.ts +125 -0
- package/src/native_component_wrapper_shared.ts +8 -6
- package/src/nativecomponent.ts +8 -13
- package/src/navigator.ts +9 -9
- package/src/video-events.ts +16 -0
- package/src/video.ts +245 -839
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
import { collectAuthorTreeFromElement, compileInlineNativeRoot, parseBooleanAttr, } from "./structure.js";
|
|
2
|
+
import { nativeError } from "./errors.js";
|
|
3
|
+
import { nextOpaqueKey } from "./identity.js";
|
|
4
|
+
import { applyHostLeaseMessage, createRootRuntimeState, publishCompiledRoot, rootFallbackAllowed, } from "./runtime.js";
|
|
5
|
+
import { isFallbackElement } from "./structure.js";
|
|
6
|
+
import { applyIslandHostEvent } from "./events.js";
|
|
7
|
+
import { effectiveOpacity } from "./style.js";
|
|
8
|
+
import { observeNativeLayout } from "./layout.js";
|
|
9
|
+
import { ensureComponentId } from "../component.js";
|
|
10
|
+
import { registerNativeComponentHandler, sendNativeComponentMessage, } from "../nativecomponent.js";
|
|
11
|
+
const ROOT_INVALID_EVENT = "error";
|
|
12
|
+
const STRUCTURE_COMPILED_EVENT = "lxnativecompiled";
|
|
13
|
+
function asRecord(el) {
|
|
14
|
+
return el;
|
|
15
|
+
}
|
|
16
|
+
function reflectBoolean(el, name, value) {
|
|
17
|
+
if (value === false || value === "false") {
|
|
18
|
+
el.setAttribute(name, "false");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (value === true || value === "" || value === "true") {
|
|
22
|
+
el.setAttribute(name, "");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (value == null) {
|
|
26
|
+
el.removeAttribute(name);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
el.setAttribute(name, String(value));
|
|
30
|
+
}
|
|
31
|
+
function reflectString(el, name, value) {
|
|
32
|
+
if (value == null || value === "") {
|
|
33
|
+
el.removeAttribute(name);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
el.setAttribute(name, String(value));
|
|
37
|
+
}
|
|
38
|
+
class LxNativeBaseElement extends HTMLElement {
|
|
39
|
+
static get observedAttributes() {
|
|
40
|
+
return [
|
|
41
|
+
"id",
|
|
42
|
+
"automation-id",
|
|
43
|
+
"hidden",
|
|
44
|
+
"pointer-events",
|
|
45
|
+
"aria-label",
|
|
46
|
+
"aria-description",
|
|
47
|
+
"aria-hidden",
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
/// Geometry is measured per element id, so every island node needs one —
|
|
51
|
+
/// without it the host would place this node at the root's rect.
|
|
52
|
+
connectedCallback() {
|
|
53
|
+
ensureComponentId(this, this.localName);
|
|
54
|
+
}
|
|
55
|
+
get automationId() {
|
|
56
|
+
return this.getAttribute("automation-id");
|
|
57
|
+
}
|
|
58
|
+
set automationId(value) {
|
|
59
|
+
reflectString(this, "automation-id", value);
|
|
60
|
+
}
|
|
61
|
+
get pointerEvents() {
|
|
62
|
+
return this.getAttribute("pointer-events");
|
|
63
|
+
}
|
|
64
|
+
set pointerEvents(value) {
|
|
65
|
+
reflectString(this, "pointer-events", value);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export class LxNativeRootElement extends LxNativeBaseElement {
|
|
69
|
+
constructor() {
|
|
70
|
+
super(...arguments);
|
|
71
|
+
this.pending = { frame: null, timer: null };
|
|
72
|
+
this.lastResult = null;
|
|
73
|
+
this.styleDiagnostics = new Set();
|
|
74
|
+
this.rootKey = nextOpaqueKey("root");
|
|
75
|
+
this.rootEpoch = 1;
|
|
76
|
+
this.runtime = createRootRuntimeState();
|
|
77
|
+
this.fallbackTimer = null;
|
|
78
|
+
this.onViewportGeometryChanged = () => this.scheduleCompile();
|
|
79
|
+
}
|
|
80
|
+
static get observedAttributes() {
|
|
81
|
+
return LxNativeBaseElement.observedAttributes;
|
|
82
|
+
}
|
|
83
|
+
lastCompileResult() {
|
|
84
|
+
return this.lastResult;
|
|
85
|
+
}
|
|
86
|
+
retry() {
|
|
87
|
+
this.compileNow();
|
|
88
|
+
return Promise.resolve();
|
|
89
|
+
}
|
|
90
|
+
connectedCallback() {
|
|
91
|
+
if (!this.shadowRoot) {
|
|
92
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
93
|
+
shadow.innerHTML = '<style>:host([data-lx-native-paint-hidden]:not([data-lx-native-measuring])) { opacity: 0 !important; }</style><slot></slot>';
|
|
94
|
+
}
|
|
95
|
+
this.style.display = this.style.display || "block";
|
|
96
|
+
this.style.position = this.style.position || "relative";
|
|
97
|
+
this.unregisterHost = registerNativeComponentHandler(this.rootKey, (message) => {
|
|
98
|
+
this.onHostMessage(message);
|
|
99
|
+
});
|
|
100
|
+
this.updateFallbackVisibility(true);
|
|
101
|
+
sendNativeComponentMessage({ id: this.rootKey, action: "component.ready" });
|
|
102
|
+
this.observer = new MutationObserver((records) => {
|
|
103
|
+
if (records.every(record => {
|
|
104
|
+
var _a;
|
|
105
|
+
return record.target === this &&
|
|
106
|
+
["data-lx-native-measuring", "data-lx-native-paint-hidden"].includes((_a = record.attributeName) !== null && _a !== void 0 ? _a : "");
|
|
107
|
+
}))
|
|
108
|
+
return;
|
|
109
|
+
this.syncResizeObservation();
|
|
110
|
+
this.scheduleCompile();
|
|
111
|
+
});
|
|
112
|
+
this.observer.observe(this, {
|
|
113
|
+
childList: true,
|
|
114
|
+
subtree: true,
|
|
115
|
+
attributes: true,
|
|
116
|
+
characterData: true,
|
|
117
|
+
});
|
|
118
|
+
this.documentObserver = new MutationObserver((records) => {
|
|
119
|
+
if (records.some((record) => Array.from(record.addedNodes).concat(Array.from(record.removedNodes)).some(containsNativeRoot))) {
|
|
120
|
+
this.scheduleCompile();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
this.documentObserver.observe(document.documentElement, { childList: true, subtree: true });
|
|
124
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
125
|
+
this.resizeObserver = new ResizeObserver(() => this.scheduleCompile());
|
|
126
|
+
this.syncResizeObservation();
|
|
127
|
+
}
|
|
128
|
+
document.addEventListener("scroll", this.onViewportGeometryChanged, true);
|
|
129
|
+
window.addEventListener("resize", this.onViewportGeometryChanged);
|
|
130
|
+
this.stopLayoutObservation = observeNativeLayout(this, () => this.scheduleCompile());
|
|
131
|
+
this.scheduleCompile();
|
|
132
|
+
}
|
|
133
|
+
disconnectedCallback() {
|
|
134
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
135
|
+
const root = (_b = (_a = this.runtime.identified) === null || _a === void 0 ? void 0 : _a.rootRef) !== null && _b !== void 0 ? _b : this.rootRef();
|
|
136
|
+
sendNativeComponentMessage({ id: this.rootKey, action: "root.destroy", root });
|
|
137
|
+
this.runtime = createRootRuntimeState();
|
|
138
|
+
this.rootEpoch += 1;
|
|
139
|
+
(_c = this.observer) === null || _c === void 0 ? void 0 : _c.disconnect();
|
|
140
|
+
this.observer = undefined;
|
|
141
|
+
(_d = this.documentObserver) === null || _d === void 0 ? void 0 : _d.disconnect();
|
|
142
|
+
this.documentObserver = undefined;
|
|
143
|
+
(_e = this.unregisterHost) === null || _e === void 0 ? void 0 : _e.call(this);
|
|
144
|
+
this.unregisterHost = undefined;
|
|
145
|
+
(_f = this.resizeObserver) === null || _f === void 0 ? void 0 : _f.disconnect();
|
|
146
|
+
this.resizeObserver = undefined;
|
|
147
|
+
(_g = this.stopLayoutObservation) === null || _g === void 0 ? void 0 : _g.call(this);
|
|
148
|
+
this.stopLayoutObservation = undefined;
|
|
149
|
+
document.removeEventListener("scroll", this.onViewportGeometryChanged, true);
|
|
150
|
+
window.removeEventListener("resize", this.onViewportGeometryChanged);
|
|
151
|
+
if (this.pending.frame != null && typeof cancelAnimationFrame === "function") {
|
|
152
|
+
cancelAnimationFrame(this.pending.frame);
|
|
153
|
+
this.pending.frame = null;
|
|
154
|
+
}
|
|
155
|
+
if (this.pending.timer != null) {
|
|
156
|
+
clearTimeout(this.pending.timer);
|
|
157
|
+
this.pending.timer = null;
|
|
158
|
+
}
|
|
159
|
+
if (this.fallbackTimer != null) {
|
|
160
|
+
clearTimeout(this.fallbackTimer);
|
|
161
|
+
this.fallbackTimer = null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
scheduleCompile() {
|
|
165
|
+
if (typeof requestAnimationFrame !== "function") {
|
|
166
|
+
this.compileNow();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
// A hidden document never services animation frames — an occluded or
|
|
170
|
+
// backgrounded window would leave the host without a tree until it is
|
|
171
|
+
// revealed. Commit on a timer instead; the host owns what it paints.
|
|
172
|
+
if (typeof document !== "undefined" && document.hidden) {
|
|
173
|
+
if (this.pending.timer != null)
|
|
174
|
+
return;
|
|
175
|
+
this.pending.timer = setTimeout(() => {
|
|
176
|
+
this.pending.timer = null;
|
|
177
|
+
this.compileNow();
|
|
178
|
+
}, 0);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (this.pending.frame != null || this.pending.timer != null)
|
|
182
|
+
return;
|
|
183
|
+
this.pending.frame = requestAnimationFrame(() => {
|
|
184
|
+
this.pending.frame = null;
|
|
185
|
+
if (this.pending.timer != null) {
|
|
186
|
+
clearTimeout(this.pending.timer);
|
|
187
|
+
this.pending.timer = null;
|
|
188
|
+
}
|
|
189
|
+
this.compileNow();
|
|
190
|
+
});
|
|
191
|
+
// Browsers may defer animation frames while a window is occluded or while
|
|
192
|
+
// automation owns the frame clock. Geometry must still reach the native
|
|
193
|
+
// island or its visual and hit target remain at the pre-scroll position.
|
|
194
|
+
this.pending.timer = setTimeout(() => {
|
|
195
|
+
this.pending.timer = null;
|
|
196
|
+
if (this.pending.frame != null) {
|
|
197
|
+
cancelAnimationFrame(this.pending.frame);
|
|
198
|
+
this.pending.frame = null;
|
|
199
|
+
}
|
|
200
|
+
this.compileNow();
|
|
201
|
+
}, 50);
|
|
202
|
+
}
|
|
203
|
+
compileNow() {
|
|
204
|
+
// Measure author CSS synchronously, then suppress its duplicate paint before the browser frame.
|
|
205
|
+
const measuring = this.hasAttribute("data-lx-native-measuring");
|
|
206
|
+
this.setAttribute("data-lx-native-measuring", "");
|
|
207
|
+
try {
|
|
208
|
+
return this.compileMeasured();
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
if (!measuring)
|
|
212
|
+
this.removeAttribute("data-lx-native-measuring");
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
compileMeasured() {
|
|
216
|
+
const author = collectAuthorTreeFromElement(this);
|
|
217
|
+
const result = compileInlineNativeRoot(author, { rootRef: this.rootRef() });
|
|
218
|
+
this.lastResult = result;
|
|
219
|
+
const nextDiagnostics = new Set(result.diagnostics.map((diagnostic) => diagnostic.message));
|
|
220
|
+
const previousDiagnostics = this.styleDiagnostics;
|
|
221
|
+
this.styleDiagnostics = nextDiagnostics;
|
|
222
|
+
for (const diagnostic of result.diagnostics) {
|
|
223
|
+
if (previousDiagnostics.has(diagnostic.message))
|
|
224
|
+
continue;
|
|
225
|
+
console.warn(`[LingXia] ${diagnostic.code}: ${diagnostic.message}`);
|
|
226
|
+
this.dispatchEvent(new CustomEvent(ROOT_INVALID_EVENT, { detail: diagnostic }));
|
|
227
|
+
}
|
|
228
|
+
if (!result.ok) {
|
|
229
|
+
this.destroyPublishedRoot();
|
|
230
|
+
this.updateFallbackVisibility(true);
|
|
231
|
+
this.dispatchEvent(new CustomEvent(ROOT_INVALID_EVENT, {
|
|
232
|
+
detail: result.error,
|
|
233
|
+
bubbles: false,
|
|
234
|
+
}));
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
const geometry = measureNativeNodeGeometry(this);
|
|
238
|
+
const published = publishCompiledRoot({
|
|
239
|
+
compiled: result.root,
|
|
240
|
+
rootRef: this.rootRef(),
|
|
241
|
+
state: this.runtime,
|
|
242
|
+
rootRect: elementContentRect(this),
|
|
243
|
+
rootVisible: elementIsVisible(this),
|
|
244
|
+
nodeRects: geometry.rects,
|
|
245
|
+
nodeVisibility: geometry.visibility,
|
|
246
|
+
nodeClipStacks: geometry.clipStacks,
|
|
247
|
+
rootOrder: nativeRootOrder(this),
|
|
248
|
+
viewportOffset: pageViewportOffset(),
|
|
249
|
+
});
|
|
250
|
+
// Adopt the published state only once the host has actually been told about
|
|
251
|
+
// it: a dropped first commit carries every mount operation, and the next
|
|
252
|
+
// compile would only produce a diff against a tree the host never saw.
|
|
253
|
+
if (published.messages.commit) {
|
|
254
|
+
if (!sendNativeComponentMessage({ id: this.rootKey, ...published.messages.commit })) {
|
|
255
|
+
this.scheduleCompile();
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
this.runtime = published.state;
|
|
260
|
+
sendNativeComponentMessage({ id: this.rootKey, ...published.messages.geometry });
|
|
261
|
+
this.dispatchEvent(new CustomEvent(STRUCTURE_COMPILED_EVENT, {
|
|
262
|
+
detail: result.root,
|
|
263
|
+
bubbles: false,
|
|
264
|
+
}));
|
|
265
|
+
}
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
onHostMessage(message) {
|
|
269
|
+
var _a, _b, _c;
|
|
270
|
+
const currentRoot = (_b = (_a = this.runtime.identified) === null || _a === void 0 ? void 0 : _a.rootRef) !== null && _b !== void 0 ? _b : this.rootRef();
|
|
271
|
+
if (message.root && !sameRootGeneration(message.root, currentRoot)) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (applyIslandHostEvent(this, message)) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (message.action === "root.resyncRequired") {
|
|
278
|
+
this.runtime = createRootRuntimeState();
|
|
279
|
+
this.updateFallbackVisibility(true);
|
|
280
|
+
this.scheduleCompile();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (message.action === "root.error") {
|
|
284
|
+
this.destroyPublishedRoot();
|
|
285
|
+
this.updateFallbackVisibility(true);
|
|
286
|
+
this.dispatchEvent(new CustomEvent(ROOT_INVALID_EVENT, {
|
|
287
|
+
detail: nativeError("NATIVE_ROOT_FAILED", (_c = message.message) !== null && _c !== void 0 ? _c : "native Root rejected"),
|
|
288
|
+
}));
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
const wasReady = this.runtime.lease.phase === "active";
|
|
292
|
+
const applied = applyHostLeaseMessage(this.runtime, message, Date.now());
|
|
293
|
+
this.runtime = applied.state;
|
|
294
|
+
if (applied.leaseAccept) {
|
|
295
|
+
sendNativeComponentMessage(applied.leaseAccept);
|
|
296
|
+
}
|
|
297
|
+
if (applied.ready) {
|
|
298
|
+
if (this.fallbackTimer != null) {
|
|
299
|
+
clearTimeout(this.fallbackTimer);
|
|
300
|
+
this.fallbackTimer = null;
|
|
301
|
+
}
|
|
302
|
+
this.updateFallbackVisibility(false);
|
|
303
|
+
if (!wasReady)
|
|
304
|
+
this.dispatchEvent(new CustomEvent("ready", { detail: {} }));
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
this.armFallbackDeadline();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
armFallbackDeadline() {
|
|
311
|
+
if (this.fallbackTimer != null)
|
|
312
|
+
clearTimeout(this.fallbackTimer);
|
|
313
|
+
const deadline = this.runtime.lease.deadlineTickMs;
|
|
314
|
+
if (deadline === undefined)
|
|
315
|
+
return;
|
|
316
|
+
this.fallbackTimer = setTimeout(() => {
|
|
317
|
+
this.fallbackTimer = null;
|
|
318
|
+
if (rootFallbackAllowed(this.runtime, Date.now())) {
|
|
319
|
+
this.updateFallbackVisibility(true);
|
|
320
|
+
}
|
|
321
|
+
}, Math.max(0, deadline - Date.now()));
|
|
322
|
+
}
|
|
323
|
+
updateFallbackVisibility(show) {
|
|
324
|
+
if (this.hasAttribute("data-lx-native-paint-hidden") === show) {
|
|
325
|
+
this.toggleAttribute("data-lx-native-paint-hidden", !show);
|
|
326
|
+
}
|
|
327
|
+
for (const fallback of Array.from(this.querySelectorAll("[data-lx-native-fallback]"))) {
|
|
328
|
+
if (fallback instanceof HTMLElement && fallback.closest("lx-native-root") === this) {
|
|
329
|
+
if (fallback.hidden === show)
|
|
330
|
+
fallback.hidden = !show;
|
|
331
|
+
if (show && fallback.hasAttribute("aria-hidden"))
|
|
332
|
+
fallback.removeAttribute("aria-hidden");
|
|
333
|
+
else if (!show && fallback.getAttribute("aria-hidden") !== "true")
|
|
334
|
+
fallback.setAttribute("aria-hidden", "true");
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
rootRef() {
|
|
339
|
+
return pageRootScope(this.rootKey, this.rootEpoch);
|
|
340
|
+
}
|
|
341
|
+
destroyPublishedRoot() {
|
|
342
|
+
var _a;
|
|
343
|
+
const root = (_a = this.runtime.identified) === null || _a === void 0 ? void 0 : _a.rootRef;
|
|
344
|
+
if (!root)
|
|
345
|
+
return;
|
|
346
|
+
sendNativeComponentMessage({ id: this.rootKey, action: "root.destroy", root });
|
|
347
|
+
this.runtime = createRootRuntimeState();
|
|
348
|
+
this.rootEpoch += 1;
|
|
349
|
+
}
|
|
350
|
+
syncResizeObservation() {
|
|
351
|
+
if (!this.resizeObserver)
|
|
352
|
+
return;
|
|
353
|
+
this.resizeObserver.disconnect();
|
|
354
|
+
this.resizeObserver.observe(this);
|
|
355
|
+
for (const element of nativeDescendants(this)) {
|
|
356
|
+
this.resizeObserver.observe(element);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
export class LxNativeViewElement extends LxNativeBaseElement {
|
|
361
|
+
static get observedAttributes() {
|
|
362
|
+
return [...LxNativeBaseElement.observedAttributes, "role"];
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
export class LxNativeCoverElement extends LxNativeBaseElement {
|
|
366
|
+
static get observedAttributes() {
|
|
367
|
+
return [...LxNativeBaseElement.observedAttributes, "scrim", "scrim-opacity", "role"];
|
|
368
|
+
}
|
|
369
|
+
connectedCallback() {
|
|
370
|
+
super.connectedCallback();
|
|
371
|
+
if (!this.style.position)
|
|
372
|
+
this.style.position = "absolute";
|
|
373
|
+
if (!this.style.inset && !this.style.top && !this.style.left) {
|
|
374
|
+
this.style.inset = "0";
|
|
375
|
+
}
|
|
376
|
+
if (!this.style.pointerEvents) {
|
|
377
|
+
this.style.pointerEvents = "none";
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
get scrim() {
|
|
381
|
+
var _a;
|
|
382
|
+
return (_a = this.getAttribute("scrim")) !== null && _a !== void 0 ? _a : "none";
|
|
383
|
+
}
|
|
384
|
+
set scrim(value) {
|
|
385
|
+
reflectString(this, "scrim", value);
|
|
386
|
+
}
|
|
387
|
+
get scrimOpacity() {
|
|
388
|
+
const raw = this.getAttribute("scrim-opacity");
|
|
389
|
+
const parsed = raw == null ? 0.6 : Number(raw);
|
|
390
|
+
return Number.isFinite(parsed) ? parsed : 0.6;
|
|
391
|
+
}
|
|
392
|
+
set scrimOpacity(value) {
|
|
393
|
+
if (value == null) {
|
|
394
|
+
this.removeAttribute("scrim-opacity");
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
this.setAttribute("scrim-opacity", String(value));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
export class LxNativeTextElement extends LxNativeBaseElement {
|
|
401
|
+
static get observedAttributes() {
|
|
402
|
+
return [
|
|
403
|
+
...LxNativeBaseElement.observedAttributes,
|
|
404
|
+
"max-lines",
|
|
405
|
+
"dir",
|
|
406
|
+
"font-size",
|
|
407
|
+
"font-weight",
|
|
408
|
+
"line-height",
|
|
409
|
+
"text-align",
|
|
410
|
+
"color",
|
|
411
|
+
];
|
|
412
|
+
}
|
|
413
|
+
connectedCallback() {
|
|
414
|
+
super.connectedCallback();
|
|
415
|
+
if (!this.shadowRoot) {
|
|
416
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
417
|
+
this.typography = document.createElement("style");
|
|
418
|
+
shadow.append(this.typography, document.createElement("slot"));
|
|
419
|
+
}
|
|
420
|
+
this.syncTypography();
|
|
421
|
+
if (!this.style.pointerEvents) {
|
|
422
|
+
this.style.pointerEvents = "none";
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
attributeChangedCallback() {
|
|
426
|
+
this.syncTypography();
|
|
427
|
+
}
|
|
428
|
+
syncTypography() {
|
|
429
|
+
if (!this.typography)
|
|
430
|
+
return;
|
|
431
|
+
const style = document.createElement("span").style;
|
|
432
|
+
for (const name of ["font-size", "font-weight", "line-height", "text-align", "color"]) {
|
|
433
|
+
const value = this.getAttribute(name);
|
|
434
|
+
if (!value)
|
|
435
|
+
continue;
|
|
436
|
+
const cssValue = (name === "font-size" || name === "line-height") && /^\d+(\.\d+)?$/.test(value)
|
|
437
|
+
? `${value}px` : value;
|
|
438
|
+
style.setProperty(name, cssValue);
|
|
439
|
+
}
|
|
440
|
+
// Keep DOM measurement and accessible text without painting a second native label.
|
|
441
|
+
this.typography.textContent = `:host { ${style.cssText} } slot { opacity: 0; }`;
|
|
442
|
+
}
|
|
443
|
+
get maxLines() {
|
|
444
|
+
const raw = this.getAttribute("max-lines");
|
|
445
|
+
if (raw == null)
|
|
446
|
+
return null;
|
|
447
|
+
const parsed = Number(raw);
|
|
448
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
449
|
+
}
|
|
450
|
+
set maxLines(value) {
|
|
451
|
+
reflectString(this, "max-lines", value == null ? null : String(value));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
export class LxNativeButtonElement extends LxNativeBaseElement {
|
|
455
|
+
constructor() {
|
|
456
|
+
super(...arguments);
|
|
457
|
+
this.onAccessibleClick = (event) => {
|
|
458
|
+
this.dispatchAccessiblePress(event.detail === 0 ? "keyboard" : "pointer");
|
|
459
|
+
};
|
|
460
|
+
this.onAccessibleKeyDown = (event) => {
|
|
461
|
+
if (event.key !== "Enter" && event.key !== " ")
|
|
462
|
+
return;
|
|
463
|
+
event.preventDefault();
|
|
464
|
+
this.dispatchAccessiblePress("keyboard");
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
dispatchAccessiblePress(source) {
|
|
468
|
+
if (this.disabled || this.loading)
|
|
469
|
+
return;
|
|
470
|
+
this.dispatchEvent(new CustomEvent("press", {
|
|
471
|
+
bubbles: true,
|
|
472
|
+
detail: { source },
|
|
473
|
+
}));
|
|
474
|
+
}
|
|
475
|
+
connectedCallback() {
|
|
476
|
+
super.connectedCallback();
|
|
477
|
+
if (!this.shadowRoot) {
|
|
478
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
479
|
+
const style = document.createElement("style");
|
|
480
|
+
style.textContent = `
|
|
481
|
+
:host { display: inline-flex; box-sizing: border-box; align-items: center;
|
|
482
|
+
justify-content: center; gap: .4em; min-height: 32px; padding: 6px 12px; border-radius: 10px; }
|
|
483
|
+
:host([hidden]:not([hidden="false"])) { display: none; }
|
|
484
|
+
:host([size="compact"]) { min-height: 26px; padding: 4px 8px; }
|
|
485
|
+
:host([icon-position="end"]) { flex-direction: row-reverse; }
|
|
486
|
+
.icon { width: 1em; height: 1em; flex: none; }
|
|
487
|
+
span, slot { opacity: 0; }
|
|
488
|
+
[hidden] { display: none; }
|
|
489
|
+
`;
|
|
490
|
+
this.measuredLabel = document.createElement("span");
|
|
491
|
+
this.measuredIcon = document.createElement("span");
|
|
492
|
+
this.measuredIcon.className = "icon";
|
|
493
|
+
this.measuredIcon.setAttribute("aria-hidden", "true");
|
|
494
|
+
this.contentSlot = document.createElement("slot");
|
|
495
|
+
shadow.append(style, this.measuredIcon, this.measuredLabel, this.contentSlot);
|
|
496
|
+
}
|
|
497
|
+
this.syncMeasurement();
|
|
498
|
+
if (!this.hasAttribute("role"))
|
|
499
|
+
this.setAttribute("role", "button");
|
|
500
|
+
if (!this.hasAttribute("tabindex"))
|
|
501
|
+
this.tabIndex = 0;
|
|
502
|
+
this.syncAccessibility();
|
|
503
|
+
this.addEventListener("click", this.onAccessibleClick);
|
|
504
|
+
this.addEventListener("keydown", this.onAccessibleKeyDown);
|
|
505
|
+
this.registerEventBridge();
|
|
506
|
+
}
|
|
507
|
+
registerEventBridge() {
|
|
508
|
+
var _a;
|
|
509
|
+
const id = ensureComponentId(this, "lx-native-button");
|
|
510
|
+
if (this.registeredId === id)
|
|
511
|
+
return;
|
|
512
|
+
(_a = this.unregisterHost) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
513
|
+
this.registeredId = id;
|
|
514
|
+
this.unregisterHost = registerNativeComponentHandler(id, (message) => {
|
|
515
|
+
applyIslandHostEvent(this, message);
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
disconnectedCallback() {
|
|
519
|
+
var _a;
|
|
520
|
+
this.removeEventListener("click", this.onAccessibleClick);
|
|
521
|
+
this.removeEventListener("keydown", this.onAccessibleKeyDown);
|
|
522
|
+
(_a = this.unregisterHost) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
523
|
+
this.unregisterHost = undefined;
|
|
524
|
+
this.registeredId = undefined;
|
|
525
|
+
}
|
|
526
|
+
attributeChangedCallback(name) {
|
|
527
|
+
if (name === "id" && this.isConnected)
|
|
528
|
+
this.registerEventBridge();
|
|
529
|
+
this.syncMeasurement();
|
|
530
|
+
this.syncAccessibility();
|
|
531
|
+
}
|
|
532
|
+
syncMeasurement() {
|
|
533
|
+
var _a;
|
|
534
|
+
if (!this.measuredLabel || !this.measuredIcon || !this.contentSlot)
|
|
535
|
+
return;
|
|
536
|
+
this.measuredLabel.textContent = (_a = this.label) !== null && _a !== void 0 ? _a : "";
|
|
537
|
+
this.measuredLabel.hidden = !this.label;
|
|
538
|
+
this.contentSlot.hidden = Boolean(this.label);
|
|
539
|
+
this.measuredIcon.hidden = !this.icon;
|
|
540
|
+
}
|
|
541
|
+
syncAccessibility() {
|
|
542
|
+
this.setAttribute("aria-disabled", String(this.disabled || this.loading));
|
|
543
|
+
this.setAttribute("aria-pressed", String(this.pressed));
|
|
544
|
+
this.setAttribute("aria-expanded", String(this.expanded));
|
|
545
|
+
}
|
|
546
|
+
static get observedAttributes() {
|
|
547
|
+
return [
|
|
548
|
+
...LxNativeBaseElement.observedAttributes,
|
|
549
|
+
"label",
|
|
550
|
+
"icon",
|
|
551
|
+
"icon-position",
|
|
552
|
+
"intent",
|
|
553
|
+
"emphasis",
|
|
554
|
+
"size",
|
|
555
|
+
"hit-slop",
|
|
556
|
+
"disabled",
|
|
557
|
+
"pressed",
|
|
558
|
+
"expanded",
|
|
559
|
+
"loading",
|
|
560
|
+
];
|
|
561
|
+
}
|
|
562
|
+
get label() {
|
|
563
|
+
return this.getAttribute("label");
|
|
564
|
+
}
|
|
565
|
+
set label(value) {
|
|
566
|
+
reflectString(this, "label", value);
|
|
567
|
+
}
|
|
568
|
+
get icon() {
|
|
569
|
+
return this.getAttribute("icon");
|
|
570
|
+
}
|
|
571
|
+
set icon(value) {
|
|
572
|
+
// Invalid JS values remain visible to the compiler instead of disappearing.
|
|
573
|
+
reflectString(this, "icon", value);
|
|
574
|
+
}
|
|
575
|
+
get disabled() {
|
|
576
|
+
return parseBooleanAttr(this.getAttribute("disabled"), false);
|
|
577
|
+
}
|
|
578
|
+
set disabled(value) {
|
|
579
|
+
reflectBoolean(this, "disabled", value);
|
|
580
|
+
}
|
|
581
|
+
get pressed() {
|
|
582
|
+
return parseBooleanAttr(this.getAttribute("pressed"), false);
|
|
583
|
+
}
|
|
584
|
+
set pressed(value) {
|
|
585
|
+
reflectBoolean(this, "pressed", value);
|
|
586
|
+
}
|
|
587
|
+
get expanded() {
|
|
588
|
+
return parseBooleanAttr(this.getAttribute("expanded"), false);
|
|
589
|
+
}
|
|
590
|
+
set expanded(value) {
|
|
591
|
+
reflectBoolean(this, "expanded", value);
|
|
592
|
+
}
|
|
593
|
+
get loading() {
|
|
594
|
+
return parseBooleanAttr(this.getAttribute("loading"), false);
|
|
595
|
+
}
|
|
596
|
+
set loading(value) {
|
|
597
|
+
reflectBoolean(this, "loading", value);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
function elementContentRect(el) {
|
|
601
|
+
if (typeof el.getBoundingClientRect !== "function") {
|
|
602
|
+
return { x: 0, y: 0, width: 0, height: 0 };
|
|
603
|
+
}
|
|
604
|
+
const box = el.getBoundingClientRect();
|
|
605
|
+
const scrollX = typeof window !== "undefined" ? window.scrollX || 0 : 0;
|
|
606
|
+
const scrollY = typeof window !== "undefined" ? window.scrollY || 0 : 0;
|
|
607
|
+
return {
|
|
608
|
+
x: box.left + scrollX,
|
|
609
|
+
y: box.top + scrollY,
|
|
610
|
+
width: box.width,
|
|
611
|
+
height: box.height,
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
function pageViewportOffset() {
|
|
615
|
+
if (typeof window === "undefined")
|
|
616
|
+
return { x: 0, y: 0 };
|
|
617
|
+
return { x: window.scrollX || 0, y: window.scrollY || 0 };
|
|
618
|
+
}
|
|
619
|
+
function measureNativeNodeGeometry(root) {
|
|
620
|
+
const rects = {};
|
|
621
|
+
const visibility = {};
|
|
622
|
+
const clipStacks = {};
|
|
623
|
+
for (const child of nativeDescendants(root)) {
|
|
624
|
+
const tag = child.tagName.toLowerCase();
|
|
625
|
+
const id = ensureComponentId(child, tag);
|
|
626
|
+
const rect = elementContentRect(child);
|
|
627
|
+
const clips = overflowClipStack(child);
|
|
628
|
+
rects[id] = rect;
|
|
629
|
+
visibility[id] = elementIsVisible(child) && clips.every((clip) => rectsIntersect(rect, clip));
|
|
630
|
+
clipStacks[id] = clips;
|
|
631
|
+
}
|
|
632
|
+
return { rects, visibility, clipStacks };
|
|
633
|
+
}
|
|
634
|
+
function nativeDescendants(root) {
|
|
635
|
+
return Array.from(root.querySelectorAll("lx-native-view,lx-native-cover,lx-native-text,lx-native-button,lx-video"))
|
|
636
|
+
.filter((element) => !isFallbackElement(element) && element.closest("lx-native-root") === root);
|
|
637
|
+
}
|
|
638
|
+
function elementIsVisible(element) {
|
|
639
|
+
const html = element;
|
|
640
|
+
if (typeof getComputedStyle !== "function")
|
|
641
|
+
return true;
|
|
642
|
+
const style = getComputedStyle(html);
|
|
643
|
+
if (style.display === "none" || style.visibility === "hidden" || effectiveOpacity(element) === 0) {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
const rect = html.getBoundingClientRect();
|
|
647
|
+
if (rect.width <= 0 || rect.height <= 0 || html.getClientRects().length === 0) {
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
if (typeof window === "undefined")
|
|
651
|
+
return true;
|
|
652
|
+
return rect.right > 0 && rect.bottom > 0 && rect.left < window.innerWidth && rect.top < window.innerHeight;
|
|
653
|
+
}
|
|
654
|
+
function overflowClipStack(element) {
|
|
655
|
+
if (typeof getComputedStyle !== "function")
|
|
656
|
+
return [];
|
|
657
|
+
const clips = [];
|
|
658
|
+
for (let ancestor = element.parentElement; ancestor; ancestor = ancestor.parentElement) {
|
|
659
|
+
const style = getComputedStyle(ancestor);
|
|
660
|
+
const clipsX = style.overflowX !== "visible";
|
|
661
|
+
const clipsY = style.overflowY !== "visible";
|
|
662
|
+
if (clipsX || clipsY) {
|
|
663
|
+
const box = elementContentRect(ancestor);
|
|
664
|
+
const own = elementContentRect(element);
|
|
665
|
+
const html = ancestor;
|
|
666
|
+
clips.push({
|
|
667
|
+
x: clipsX ? box.x + html.clientLeft : own.x,
|
|
668
|
+
y: clipsY ? box.y + html.clientTop : own.y,
|
|
669
|
+
width: clipsX ? html.clientWidth : own.width,
|
|
670
|
+
height: clipsY ? html.clientHeight : own.height,
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return clips;
|
|
675
|
+
}
|
|
676
|
+
function rectsIntersect(a, b) {
|
|
677
|
+
return a.x < b.x + b.width &&
|
|
678
|
+
a.x + a.width > b.x &&
|
|
679
|
+
a.y < b.y + b.height &&
|
|
680
|
+
a.y + a.height > b.y;
|
|
681
|
+
}
|
|
682
|
+
function nativeRootOrder(root) {
|
|
683
|
+
if (typeof document === "undefined")
|
|
684
|
+
return 0;
|
|
685
|
+
const roots = Array.from(document.querySelectorAll("lx-native-root"));
|
|
686
|
+
const index = roots.indexOf(root);
|
|
687
|
+
return index < 0 ? 0 : index;
|
|
688
|
+
}
|
|
689
|
+
function containsNativeRoot(node) {
|
|
690
|
+
return node instanceof Element &&
|
|
691
|
+
(node.localName === "lx-native-root" || node.querySelector("lx-native-root") !== null);
|
|
692
|
+
}
|
|
693
|
+
function sameRootGeneration(a, b) {
|
|
694
|
+
return a.surfaceInstanceId === b.surfaceInstanceId &&
|
|
695
|
+
a.pageInstanceId === b.pageInstanceId &&
|
|
696
|
+
a.documentInstanceId === b.documentInstanceId &&
|
|
697
|
+
a.rootKey === b.rootKey &&
|
|
698
|
+
a.rootEpoch === b.rootEpoch;
|
|
699
|
+
}
|
|
700
|
+
function pageRootScope(rootKey, rootEpoch) {
|
|
701
|
+
let surfaceInstanceId = "surface";
|
|
702
|
+
let pageInstanceId = "page";
|
|
703
|
+
let documentInstanceId = "doc";
|
|
704
|
+
if (typeof window !== "undefined") {
|
|
705
|
+
const cfg = window.__LX_BRIDGE_CFG;
|
|
706
|
+
if (cfg === null || cfg === void 0 ? void 0 : cfg.surfaceInstanceId)
|
|
707
|
+
surfaceInstanceId = cfg.surfaceInstanceId;
|
|
708
|
+
if (cfg === null || cfg === void 0 ? void 0 : cfg.pageInstanceId)
|
|
709
|
+
pageInstanceId = cfg.pageInstanceId;
|
|
710
|
+
const w = window;
|
|
711
|
+
if (!w.__LX_DOCUMENT_INSTANCE_ID) {
|
|
712
|
+
w.__LX_DOCUMENT_INSTANCE_ID = nextOpaqueKey("doc");
|
|
713
|
+
}
|
|
714
|
+
documentInstanceId = w.__LX_DOCUMENT_INSTANCE_ID;
|
|
715
|
+
}
|
|
716
|
+
return { surfaceInstanceId, pageInstanceId, documentInstanceId, rootKey, rootEpoch };
|
|
717
|
+
}
|
|
718
|
+
function defineOnce(tag, ctor) {
|
|
719
|
+
if (typeof customElements === "undefined")
|
|
720
|
+
return;
|
|
721
|
+
if (customElements.get(tag))
|
|
722
|
+
return;
|
|
723
|
+
customElements.define(tag, ctor);
|
|
724
|
+
}
|
|
725
|
+
export function registerNativeRootComponent() {
|
|
726
|
+
defineOnce("lx-native-root", LxNativeRootElement);
|
|
727
|
+
}
|
|
728
|
+
export function registerNativeViewComponent() {
|
|
729
|
+
defineOnce("lx-native-view", LxNativeViewElement);
|
|
730
|
+
}
|
|
731
|
+
export function registerNativeCoverComponent() {
|
|
732
|
+
defineOnce("lx-native-cover", LxNativeCoverElement);
|
|
733
|
+
}
|
|
734
|
+
export function registerNativeTextComponent() {
|
|
735
|
+
defineOnce("lx-native-text", LxNativeTextElement);
|
|
736
|
+
}
|
|
737
|
+
export function registerNativeButtonComponent() {
|
|
738
|
+
defineOnce("lx-native-button", LxNativeButtonElement);
|
|
739
|
+
}
|
|
740
|
+
export function registerInlineNativeAuthorComponents() {
|
|
741
|
+
registerNativeRootComponent();
|
|
742
|
+
registerNativeViewComponent();
|
|
743
|
+
registerNativeCoverComponent();
|
|
744
|
+
registerNativeTextComponent();
|
|
745
|
+
registerNativeButtonComponent();
|
|
746
|
+
}
|
|
747
|
+
export function structureErrorFromUnknown(message) {
|
|
748
|
+
return nativeError("NATIVE_ROOT_INVALID_STRUCTURE", message);
|
|
749
|
+
}
|