@lingxia/elements 0.9.0 → 0.11.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.d.ts +0 -3
- package/dist/index.js +0 -3
- package/dist/platform.d.ts +1 -0
- package/dist/platform.js +4 -0
- package/dist/video.d.ts +5 -0
- package/dist/video.js +105 -1
- package/package.json +1 -1
- package/src/index.ts +0 -20
- package/src/platform.ts +4 -0
- package/src/video.ts +102 -1
- package/dist/input.d.ts +0 -117
- package/dist/input.js +0 -699
- package/dist/text_component_native_attrs.d.ts +0 -53
- package/dist/text_component_native_attrs.js +0 -111
- package/dist/text_component_shared.d.ts +0 -7
- package/dist/text_component_shared.js +0 -148
- package/dist/textarea.d.ts +0 -129
- package/dist/textarea.js +0 -841
- package/src/input.ts +0 -799
- package/src/text_component_native_attrs.ts +0 -137
- package/src/text_component_shared.ts +0 -147
- package/src/textarea.ts +0 -959
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
export { registerVideoComponent, LxVideoElement, type LxVideoAttributes, } from "./video.js";
|
|
2
2
|
export { registerMediaSwiperComponent, LxMediaSwiperElement, type LxMediaSwiperAttributes, type LxMediaSwiperItem, type LxMediaSwiperChangeEvent, type LxMediaSwiperChangeEventDetail, type LxMediaSwiperTransitionEndEvent, type LxMediaSwiperTransitionEndEventDetail, type LxMediaSwiperItemEvent, type LxMediaSwiperEventDetail, type LxMediaSwiperEndReachedEvent, type LxMediaSwiperEndReachedEventDetail, type LxMediaSwiperErrorEvent, type LxMediaSwiperErrorEventDetail, type LxMediaSwiperChangeSource, type LxMediaSwiperErrorCode, } from "./media_swiper.js";
|
|
3
3
|
export { registerPickerComponent, LxPickerElement, type LxPickerAttributes, type LxPickerColumn, type LxPickerCascadingColumns, type LxPickerEvent, type LxPickerEventDetail, } from "./picker.js";
|
|
4
|
-
export { registerInputComponent, LxInputElement, type LxInputAttributes, type LxInputEvent, type LxInputEventDetail, } from "./input.js";
|
|
5
|
-
export { registerTextareaComponent, LxTextareaElement, type LxTextareaAttributes, type LxTextareaEvent, type LxTextareaEventDetail, } from "./textarea.js";
|
|
6
4
|
export { LxNavigatorElement, type LxNavigatorAttributes, type LxNavigatorEvent, type NavigatorEnvVersion, type NavigatorOpenType, type NavigatorQuery, type NavigatorQueryValue, type NavigatorTarget, } from "./navigator.js";
|
|
7
5
|
export { buildVideoNativeAttrs, buildMediaSwiperNativeAttrs, buildNavigatorNativeAttrs, buildPickerNativeAttrs, getPickerDisplayText, getPickerValueFromIndex, VIDEO_DOM_EVENT_MAP, MEDIA_SWIPER_DOM_EVENT_MAP, } from "./native_component_wrapper_shared.js";
|
|
8
|
-
export { buildInputNativeAttrs, buildTextareaNativeAttrs, } from "./text_component_native_attrs.js";
|
|
9
6
|
export { isHarmony, isIOS, isAndroid, isMacOS, isDesktop, getOS, } from "./platform.js";
|
|
10
7
|
export { ensureComponentId, rectEquals, NativeComponentUpdateState, type Rect, } from "./component.js";
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
export { registerVideoComponent, LxVideoElement, } from "./video.js";
|
|
2
2
|
export { registerMediaSwiperComponent, LxMediaSwiperElement, } from "./media_swiper.js";
|
|
3
3
|
export { registerPickerComponent, LxPickerElement, } from "./picker.js";
|
|
4
|
-
export { registerInputComponent, LxInputElement, } from "./input.js";
|
|
5
|
-
export { registerTextareaComponent, LxTextareaElement, } from "./textarea.js";
|
|
6
4
|
export { LxNavigatorElement, } from "./navigator.js";
|
|
7
5
|
export { buildVideoNativeAttrs, buildMediaSwiperNativeAttrs, buildNavigatorNativeAttrs, buildPickerNativeAttrs, getPickerDisplayText, getPickerValueFromIndex, VIDEO_DOM_EVENT_MAP, MEDIA_SWIPER_DOM_EVENT_MAP, } from "./native_component_wrapper_shared.js";
|
|
8
|
-
export { buildInputNativeAttrs, buildTextareaNativeAttrs, } from "./text_component_native_attrs.js";
|
|
9
6
|
export { isHarmony, isIOS, isAndroid, isMacOS, isDesktop, getOS, } from "./platform.js";
|
|
10
7
|
export { ensureComponentId, rectEquals, NativeComponentUpdateState, } from "./component.js";
|
package/dist/platform.d.ts
CHANGED
|
@@ -2,5 +2,6 @@ export declare function isHarmony(): boolean;
|
|
|
2
2
|
export declare function isIOS(): boolean;
|
|
3
3
|
export declare function isAndroid(): boolean;
|
|
4
4
|
export declare function isMacOS(): boolean;
|
|
5
|
+
export declare function isWindows(): boolean;
|
|
5
6
|
export declare function isDesktop(): boolean;
|
|
6
7
|
export declare function getOS(): string;
|
package/dist/platform.js
CHANGED
|
@@ -14,6 +14,10 @@ export function isMacOS() {
|
|
|
14
14
|
var _a, _b, _c;
|
|
15
15
|
return ((_c = (_b = (_a = window.LingXiaBridge) === null || _a === void 0 ? void 0 : _a.platform) === null || _b === void 0 ? void 0 : _b.isMacOS) === null || _c === void 0 ? void 0 : _c.call(_b)) === true;
|
|
16
16
|
}
|
|
17
|
+
export function isWindows() {
|
|
18
|
+
var _a, _b, _c;
|
|
19
|
+
return ((_c = (_b = (_a = window.LingXiaBridge) === null || _a === void 0 ? void 0 : _a.platform) === null || _b === void 0 ? void 0 : _b.isWindows) === null || _c === void 0 ? void 0 : _c.call(_b)) === true;
|
|
20
|
+
}
|
|
17
21
|
export function isDesktop() {
|
|
18
22
|
var _a, _b, _c;
|
|
19
23
|
return ((_c = (_b = (_a = window.LingXiaBridge) === null || _a === void 0 ? void 0 : _a.platform) === null || _b === void 0 ? void 0 : _b.isDesktop) === null || _c === void 0 ? void 0 : _c.call(_b)) === true;
|
package/dist/video.d.ts
CHANGED
|
@@ -50,6 +50,7 @@ export declare class LxVideoElement extends HTMLElement {
|
|
|
50
50
|
private updateState;
|
|
51
51
|
private unregister?;
|
|
52
52
|
private resizeObserver?;
|
|
53
|
+
private winSettleFrame;
|
|
53
54
|
private pendingLayoutFrame;
|
|
54
55
|
private boundUpdatePosition;
|
|
55
56
|
private _handlers;
|
|
@@ -66,6 +67,8 @@ export declare class LxVideoElement extends HTMLElement {
|
|
|
66
67
|
private attrObserver?;
|
|
67
68
|
private resizeEventListener;
|
|
68
69
|
private iOSScrollEventListener;
|
|
70
|
+
private scrollSettleFrame;
|
|
71
|
+
private containerScrollListener;
|
|
69
72
|
private rawHandlers;
|
|
70
73
|
private _pageBindings;
|
|
71
74
|
private parseRotateValue;
|
|
@@ -110,6 +113,7 @@ export declare class LxVideoElement extends HTMLElement {
|
|
|
110
113
|
set onratechange(cb: EventListener);
|
|
111
114
|
get onratechange(): EventListener;
|
|
112
115
|
private ensurePlaceholderStyle;
|
|
116
|
+
private syncPosterPlaceholder;
|
|
113
117
|
private collectProps;
|
|
114
118
|
private shouldRefreshForAttribute;
|
|
115
119
|
private startAttrObserver;
|
|
@@ -125,6 +129,7 @@ export declare class LxVideoElement extends HTMLElement {
|
|
|
125
129
|
private startSizeObserver;
|
|
126
130
|
private stopSizeObserver;
|
|
127
131
|
private scheduleMountRetry;
|
|
132
|
+
private scheduleWindowsMountSettle;
|
|
128
133
|
private scheduleIOSBootstrapFrames;
|
|
129
134
|
private mountOrUpdateHarmony;
|
|
130
135
|
private ensureHarmonyEmbed;
|
package/dist/video.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { sendNativeComponentMessage, registerNativeComponentHandler, addNativeComponentLayoutInvalidationListener } from "./nativecomponent.js";
|
|
2
2
|
import { measureElement } from "./dom.js";
|
|
3
3
|
import { ensureComponentId, NativeComponentUpdateState, iOSNativeComponentHelper } from "./component.js";
|
|
4
|
-
import { isAndroid, isHarmony, isIOS } from "./platform.js";
|
|
4
|
+
import { isAndroid, isHarmony, isIOS, isWindows } from "./platform.js";
|
|
5
5
|
const HARMONY_PROPS_PREFIX = "data:application/json,";
|
|
6
6
|
// Component implementation
|
|
7
7
|
export class LxVideoElement extends HTMLElement {
|
|
@@ -10,6 +10,7 @@ export class LxVideoElement extends HTMLElement {
|
|
|
10
10
|
this.componentId = null;
|
|
11
11
|
this.mounted = false;
|
|
12
12
|
this.updateState = new NativeComponentUpdateState();
|
|
13
|
+
this.winSettleFrame = null;
|
|
13
14
|
this.pendingLayoutFrame = null;
|
|
14
15
|
this.boundUpdatePosition = this.updatePosition.bind(this);
|
|
15
16
|
this._handlers = {};
|
|
@@ -24,6 +25,21 @@ export class LxVideoElement extends HTMLElement {
|
|
|
24
25
|
this.iOSScrollEventListener = {
|
|
25
26
|
handleEvent: () => this.boundUpdatePosition(),
|
|
26
27
|
};
|
|
28
|
+
this.scrollSettleFrame = null;
|
|
29
|
+
// Scroll events do not bubble, so a plain window listener misses inner
|
|
30
|
+
// scroll containers; the capture phase sees them all. Any ancestor
|
|
31
|
+
// scrolling moves the element's document rect, so re-measure (coalesced
|
|
32
|
+
// to one update per frame; decide() dedups unchanged rects).
|
|
33
|
+
this.containerScrollListener = {
|
|
34
|
+
handleEvent: () => {
|
|
35
|
+
if (this.scrollSettleFrame !== null)
|
|
36
|
+
return;
|
|
37
|
+
this.scrollSettleFrame = requestAnimationFrame(() => {
|
|
38
|
+
this.scrollSettleFrame = null;
|
|
39
|
+
this.updatePosition();
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
};
|
|
27
43
|
this.rawHandlers = {};
|
|
28
44
|
this._pageBindings = {};
|
|
29
45
|
}
|
|
@@ -198,6 +214,17 @@ export class LxVideoElement extends HTMLElement {
|
|
|
198
214
|
else {
|
|
199
215
|
this.mountOrUpdate();
|
|
200
216
|
this.startTracking();
|
|
217
|
+
// Windows only: the WebView2 surface is created at an intermediate size
|
|
218
|
+
// and resized to its final content bounds shortly after this element
|
|
219
|
+
// mounts, which reflows the page and moves the element. Unlike the other
|
|
220
|
+
// platforms' webviews, WebView2 doesn't reliably fire the resize /
|
|
221
|
+
// ResizeObserver that would make us re-report, and an early update can be
|
|
222
|
+
// lost in the bridge transport during page load — so the native overlay
|
|
223
|
+
// can stay pinned at the mount-time rect. Re-measure + re-report across the
|
|
224
|
+
// settle window to land the final position. No-op on every other platform.
|
|
225
|
+
if (isWindows()) {
|
|
226
|
+
this.scheduleWindowsMountSettle();
|
|
227
|
+
}
|
|
201
228
|
}
|
|
202
229
|
}
|
|
203
230
|
disconnectedCallback() {
|
|
@@ -241,6 +268,9 @@ export class LxVideoElement extends HTMLElement {
|
|
|
241
268
|
}
|
|
242
269
|
if (!this.isConnected)
|
|
243
270
|
return;
|
|
271
|
+
if (name === "poster" || name === "object-fit" || name === "objectfit") {
|
|
272
|
+
this.syncPosterPlaceholder();
|
|
273
|
+
}
|
|
244
274
|
// Keep content-rotate/object-fit updates immediate across all native platforms.
|
|
245
275
|
const forcePropsUpdate = name === "content-rotate" || name === "object-fit" || name === "objectfit";
|
|
246
276
|
if (isHarmony() && forcePropsUpdate) {
|
|
@@ -314,6 +344,24 @@ export class LxVideoElement extends HTMLElement {
|
|
|
314
344
|
this.style.backgroundColor = "black";
|
|
315
345
|
if (!this.style.aspectRatio)
|
|
316
346
|
this.style.aspectRatio = "16 / 9";
|
|
347
|
+
this.syncPosterPlaceholder();
|
|
348
|
+
}
|
|
349
|
+
/// The poster doubles as the element's CSS background: on platforms
|
|
350
|
+
/// where the native layer hides while no media is presented (Windows:
|
|
351
|
+
/// initial state, after stop, on error), the placeholder shows it.
|
|
352
|
+
syncPosterPlaceholder() {
|
|
353
|
+
var _a;
|
|
354
|
+
const poster = this.getAttribute("poster");
|
|
355
|
+
if (!poster) {
|
|
356
|
+
this.style.backgroundImage = "";
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const objectFit = this.parseObjectFitValue((_a = this.getAttribute("object-fit")) !== null && _a !== void 0 ? _a : this.getAttribute("objectfit"));
|
|
360
|
+
const size = objectFit === "cover" ? "cover" : objectFit === "fill" ? "100% 100%" : "contain";
|
|
361
|
+
this.style.backgroundImage = `url("${poster.replace(/"/g, '\\"')}")`;
|
|
362
|
+
this.style.backgroundSize = size;
|
|
363
|
+
this.style.backgroundPosition = "center";
|
|
364
|
+
this.style.backgroundRepeat = "no-repeat";
|
|
317
365
|
}
|
|
318
366
|
collectProps() {
|
|
319
367
|
var _a;
|
|
@@ -505,6 +553,12 @@ export class LxVideoElement extends HTMLElement {
|
|
|
505
553
|
if (isIOS()) {
|
|
506
554
|
window.addEventListener("scroll", this.iOSScrollEventListener, { passive: true });
|
|
507
555
|
}
|
|
556
|
+
else if (isWindows()) {
|
|
557
|
+
window.addEventListener("scroll", this.containerScrollListener, {
|
|
558
|
+
capture: true,
|
|
559
|
+
passive: true,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
508
562
|
if (isAndroid()) {
|
|
509
563
|
this.removeLayoutInvalidationListener = addNativeComponentLayoutInvalidationListener(this.resizeEventListener);
|
|
510
564
|
}
|
|
@@ -517,9 +571,20 @@ export class LxVideoElement extends HTMLElement {
|
|
|
517
571
|
if (isIOS()) {
|
|
518
572
|
window.removeEventListener("scroll", this.iOSScrollEventListener);
|
|
519
573
|
}
|
|
574
|
+
else if (isWindows()) {
|
|
575
|
+
window.removeEventListener("scroll", this.containerScrollListener, { capture: true });
|
|
576
|
+
}
|
|
520
577
|
(_a = this.removeLayoutInvalidationListener) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
521
578
|
this.removeLayoutInvalidationListener = undefined;
|
|
522
579
|
this.stopSizeObserver();
|
|
580
|
+
if (this.scrollSettleFrame !== null) {
|
|
581
|
+
cancelAnimationFrame(this.scrollSettleFrame);
|
|
582
|
+
this.scrollSettleFrame = null;
|
|
583
|
+
}
|
|
584
|
+
if (this.winSettleFrame !== null) {
|
|
585
|
+
cancelAnimationFrame(this.winSettleFrame);
|
|
586
|
+
this.winSettleFrame = null;
|
|
587
|
+
}
|
|
523
588
|
if (this.pendingLayoutFrame !== null) {
|
|
524
589
|
cancelAnimationFrame(this.pendingLayoutFrame);
|
|
525
590
|
this.pendingLayoutFrame = null;
|
|
@@ -575,6 +640,45 @@ export class LxVideoElement extends HTMLElement {
|
|
|
575
640
|
this.mountOrUpdate();
|
|
576
641
|
});
|
|
577
642
|
}
|
|
643
|
+
scheduleWindowsMountSettle() {
|
|
644
|
+
if (typeof requestAnimationFrame === "undefined")
|
|
645
|
+
return;
|
|
646
|
+
if (this.winSettleFrame !== null)
|
|
647
|
+
return;
|
|
648
|
+
// Re-measure across the page-load / WebView2-resize settle window. The
|
|
649
|
+
// reflow that moves this element can land well after mount, so keep checking
|
|
650
|
+
// until the rect has been stable for a while; decide() dedups, so updates
|
|
651
|
+
// only fire when the rect changes.
|
|
652
|
+
let lastKey = "";
|
|
653
|
+
let stable = 0;
|
|
654
|
+
let elapsed = 0;
|
|
655
|
+
const tick = () => {
|
|
656
|
+
this.winSettleFrame = null;
|
|
657
|
+
if (!this.isConnected)
|
|
658
|
+
return;
|
|
659
|
+
// Force the re-send (don't dedup): an early update can be lost in the
|
|
660
|
+
// bridge transport during page load, and once the element has settled a
|
|
661
|
+
// dedup'd re-measure sees "no change" and never re-delivers it. Forcing
|
|
662
|
+
// each frame re-delivers the current rect until it lands.
|
|
663
|
+
this.mountOrUpdate(true);
|
|
664
|
+
const r = this.getBoundingClientRect();
|
|
665
|
+
const key = `${Math.round(r.top)}:${Math.round(r.width)}`;
|
|
666
|
+
if (key === lastKey) {
|
|
667
|
+
stable += 1;
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
stable = 0;
|
|
671
|
+
lastKey = key;
|
|
672
|
+
}
|
|
673
|
+
elapsed += 1;
|
|
674
|
+
// Keep re-sending until the layout has held steady for a while (so a late
|
|
675
|
+
// settle is still caught), capped so it never runs unbounded.
|
|
676
|
+
if (stable < 12 && elapsed < 180) {
|
|
677
|
+
this.winSettleFrame = requestAnimationFrame(tick);
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
this.winSettleFrame = requestAnimationFrame(tick);
|
|
681
|
+
}
|
|
578
682
|
scheduleIOSBootstrapFrames() {
|
|
579
683
|
if (!isIOS())
|
|
580
684
|
return;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -33,21 +33,6 @@ export {
|
|
|
33
33
|
type LxPickerEventDetail,
|
|
34
34
|
} from "./picker.js";
|
|
35
35
|
|
|
36
|
-
export {
|
|
37
|
-
registerInputComponent,
|
|
38
|
-
LxInputElement,
|
|
39
|
-
type LxInputAttributes,
|
|
40
|
-
type LxInputEvent,
|
|
41
|
-
type LxInputEventDetail,
|
|
42
|
-
} from "./input.js";
|
|
43
|
-
|
|
44
|
-
export {
|
|
45
|
-
registerTextareaComponent,
|
|
46
|
-
LxTextareaElement,
|
|
47
|
-
type LxTextareaAttributes,
|
|
48
|
-
type LxTextareaEvent,
|
|
49
|
-
type LxTextareaEventDetail,
|
|
50
|
-
} from "./textarea.js";
|
|
51
36
|
|
|
52
37
|
export {
|
|
53
38
|
LxNavigatorElement,
|
|
@@ -71,11 +56,6 @@ export {
|
|
|
71
56
|
MEDIA_SWIPER_DOM_EVENT_MAP,
|
|
72
57
|
} from "./native_component_wrapper_shared.js";
|
|
73
58
|
|
|
74
|
-
export {
|
|
75
|
-
buildInputNativeAttrs,
|
|
76
|
-
buildTextareaNativeAttrs,
|
|
77
|
-
} from "./text_component_native_attrs.js";
|
|
78
|
-
|
|
79
59
|
export {
|
|
80
60
|
isHarmony,
|
|
81
61
|
isIOS,
|
package/src/platform.ts
CHANGED
|
@@ -16,6 +16,10 @@ export function isMacOS(): boolean {
|
|
|
16
16
|
return window.LingXiaBridge?.platform?.isMacOS?.() === true;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
export function isWindows(): boolean {
|
|
20
|
+
return window.LingXiaBridge?.platform?.isWindows?.() === true;
|
|
21
|
+
}
|
|
22
|
+
|
|
19
23
|
export function isDesktop(): boolean {
|
|
20
24
|
return window.LingXiaBridge?.platform?.isDesktop?.() === true;
|
|
21
25
|
}
|
package/src/video.ts
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./nativecomponent.js";
|
|
6
6
|
import { measureElement } from "./dom.js";
|
|
7
7
|
import { ensureComponentId, NativeComponentUpdateState, iOSNativeComponentHelper } from "./component.js";
|
|
8
|
-
import { isAndroid, isHarmony, isIOS } from "./platform.js";
|
|
8
|
+
import { isAndroid, isHarmony, isIOS, isWindows } from "./platform.js";
|
|
9
9
|
|
|
10
10
|
const HARMONY_PROPS_PREFIX = "data:application/json,";
|
|
11
11
|
|
|
@@ -85,6 +85,7 @@ export class LxVideoElement extends HTMLElement {
|
|
|
85
85
|
private updateState = new NativeComponentUpdateState();
|
|
86
86
|
private unregister?: () => void;
|
|
87
87
|
private resizeObserver?: ResizeObserver;
|
|
88
|
+
private winSettleFrame: number | null = null;
|
|
88
89
|
private pendingLayoutFrame: number | null = null;
|
|
89
90
|
private boundUpdatePosition = this.updatePosition.bind(this);
|
|
90
91
|
private _handlers: Record<string, EventListenerOrEventListenerObject> = {};
|
|
@@ -105,6 +106,20 @@ export class LxVideoElement extends HTMLElement {
|
|
|
105
106
|
private iOSScrollEventListener: EventListenerObject = {
|
|
106
107
|
handleEvent: () => this.boundUpdatePosition(),
|
|
107
108
|
};
|
|
109
|
+
private scrollSettleFrame: number | null = null;
|
|
110
|
+
// Scroll events do not bubble, so a plain window listener misses inner
|
|
111
|
+
// scroll containers; the capture phase sees them all. Any ancestor
|
|
112
|
+
// scrolling moves the element's document rect, so re-measure (coalesced
|
|
113
|
+
// to one update per frame; decide() dedups unchanged rects).
|
|
114
|
+
private containerScrollListener: EventListenerObject = {
|
|
115
|
+
handleEvent: () => {
|
|
116
|
+
if (this.scrollSettleFrame !== null) return;
|
|
117
|
+
this.scrollSettleFrame = requestAnimationFrame(() => {
|
|
118
|
+
this.scrollSettleFrame = null;
|
|
119
|
+
this.updatePosition();
|
|
120
|
+
});
|
|
121
|
+
},
|
|
122
|
+
};
|
|
108
123
|
private rawHandlers: Record<string, EventListenerOrEventListenerObject> = {};
|
|
109
124
|
private _pageBindings: Record<string, string> = {};
|
|
110
125
|
|
|
@@ -268,6 +283,17 @@ export class LxVideoElement extends HTMLElement {
|
|
|
268
283
|
} else {
|
|
269
284
|
this.mountOrUpdate();
|
|
270
285
|
this.startTracking();
|
|
286
|
+
// Windows only: the WebView2 surface is created at an intermediate size
|
|
287
|
+
// and resized to its final content bounds shortly after this element
|
|
288
|
+
// mounts, which reflows the page and moves the element. Unlike the other
|
|
289
|
+
// platforms' webviews, WebView2 doesn't reliably fire the resize /
|
|
290
|
+
// ResizeObserver that would make us re-report, and an early update can be
|
|
291
|
+
// lost in the bridge transport during page load — so the native overlay
|
|
292
|
+
// can stay pinned at the mount-time rect. Re-measure + re-report across the
|
|
293
|
+
// settle window to land the final position. No-op on every other platform.
|
|
294
|
+
if (isWindows()) {
|
|
295
|
+
this.scheduleWindowsMountSettle();
|
|
296
|
+
}
|
|
271
297
|
}
|
|
272
298
|
}
|
|
273
299
|
|
|
@@ -314,6 +340,9 @@ export class LxVideoElement extends HTMLElement {
|
|
|
314
340
|
}
|
|
315
341
|
}
|
|
316
342
|
if (!this.isConnected) return;
|
|
343
|
+
if (name === "poster" || name === "object-fit" || name === "objectfit") {
|
|
344
|
+
this.syncPosterPlaceholder();
|
|
345
|
+
}
|
|
317
346
|
// Keep content-rotate/object-fit updates immediate across all native platforms.
|
|
318
347
|
const forcePropsUpdate = name === "content-rotate" || name === "object-fit" || name === "objectfit";
|
|
319
348
|
if (isHarmony() && forcePropsUpdate) {
|
|
@@ -399,6 +428,27 @@ export class LxVideoElement extends HTMLElement {
|
|
|
399
428
|
if (!this.style.position) this.style.position = "relative"; // Needed for iOS scroll container
|
|
400
429
|
if (!this.style.backgroundColor) this.style.backgroundColor = "black";
|
|
401
430
|
if (!this.style.aspectRatio) this.style.aspectRatio = "16 / 9";
|
|
431
|
+
this.syncPosterPlaceholder();
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/// The poster doubles as the element's CSS background: on platforms
|
|
435
|
+
/// where the native layer hides while no media is presented (Windows:
|
|
436
|
+
/// initial state, after stop, on error), the placeholder shows it.
|
|
437
|
+
private syncPosterPlaceholder() {
|
|
438
|
+
const poster = this.getAttribute("poster");
|
|
439
|
+
if (!poster) {
|
|
440
|
+
this.style.backgroundImage = "";
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const objectFit = this.parseObjectFitValue(
|
|
444
|
+
this.getAttribute("object-fit") ?? this.getAttribute("objectfit")
|
|
445
|
+
);
|
|
446
|
+
const size =
|
|
447
|
+
objectFit === "cover" ? "cover" : objectFit === "fill" ? "100% 100%" : "contain";
|
|
448
|
+
this.style.backgroundImage = `url("${poster.replace(/"/g, '\\"')}")`;
|
|
449
|
+
this.style.backgroundSize = size;
|
|
450
|
+
this.style.backgroundPosition = "center";
|
|
451
|
+
this.style.backgroundRepeat = "no-repeat";
|
|
402
452
|
}
|
|
403
453
|
|
|
404
454
|
private collectProps() {
|
|
@@ -605,6 +655,11 @@ export class LxVideoElement extends HTMLElement {
|
|
|
605
655
|
window.addEventListener("resize", this.resizeEventListener);
|
|
606
656
|
if (isIOS()) {
|
|
607
657
|
window.addEventListener("scroll", this.iOSScrollEventListener, { passive: true });
|
|
658
|
+
} else if (isWindows()) {
|
|
659
|
+
window.addEventListener("scroll", this.containerScrollListener, {
|
|
660
|
+
capture: true,
|
|
661
|
+
passive: true,
|
|
662
|
+
});
|
|
608
663
|
}
|
|
609
664
|
if (isAndroid()) {
|
|
610
665
|
this.removeLayoutInvalidationListener = addNativeComponentLayoutInvalidationListener(this.resizeEventListener);
|
|
@@ -617,10 +672,20 @@ export class LxVideoElement extends HTMLElement {
|
|
|
617
672
|
window.removeEventListener("resize", this.resizeEventListener);
|
|
618
673
|
if (isIOS()) {
|
|
619
674
|
window.removeEventListener("scroll", this.iOSScrollEventListener);
|
|
675
|
+
} else if (isWindows()) {
|
|
676
|
+
window.removeEventListener("scroll", this.containerScrollListener, { capture: true });
|
|
620
677
|
}
|
|
621
678
|
this.removeLayoutInvalidationListener?.();
|
|
622
679
|
this.removeLayoutInvalidationListener = undefined;
|
|
623
680
|
this.stopSizeObserver();
|
|
681
|
+
if (this.scrollSettleFrame !== null) {
|
|
682
|
+
cancelAnimationFrame(this.scrollSettleFrame);
|
|
683
|
+
this.scrollSettleFrame = null;
|
|
684
|
+
}
|
|
685
|
+
if (this.winSettleFrame !== null) {
|
|
686
|
+
cancelAnimationFrame(this.winSettleFrame);
|
|
687
|
+
this.winSettleFrame = null;
|
|
688
|
+
}
|
|
624
689
|
if (this.pendingLayoutFrame !== null) {
|
|
625
690
|
cancelAnimationFrame(this.pendingLayoutFrame);
|
|
626
691
|
this.pendingLayoutFrame = null;
|
|
@@ -675,6 +740,42 @@ export class LxVideoElement extends HTMLElement {
|
|
|
675
740
|
});
|
|
676
741
|
}
|
|
677
742
|
|
|
743
|
+
private scheduleWindowsMountSettle(): void {
|
|
744
|
+
if (typeof requestAnimationFrame === "undefined") return;
|
|
745
|
+
if (this.winSettleFrame !== null) return;
|
|
746
|
+
// Re-measure across the page-load / WebView2-resize settle window. The
|
|
747
|
+
// reflow that moves this element can land well after mount, so keep checking
|
|
748
|
+
// until the rect has been stable for a while; decide() dedups, so updates
|
|
749
|
+
// only fire when the rect changes.
|
|
750
|
+
let lastKey = "";
|
|
751
|
+
let stable = 0;
|
|
752
|
+
let elapsed = 0;
|
|
753
|
+
const tick = () => {
|
|
754
|
+
this.winSettleFrame = null;
|
|
755
|
+
if (!this.isConnected) return;
|
|
756
|
+
// Force the re-send (don't dedup): an early update can be lost in the
|
|
757
|
+
// bridge transport during page load, and once the element has settled a
|
|
758
|
+
// dedup'd re-measure sees "no change" and never re-delivers it. Forcing
|
|
759
|
+
// each frame re-delivers the current rect until it lands.
|
|
760
|
+
this.mountOrUpdate(true);
|
|
761
|
+
const r = this.getBoundingClientRect();
|
|
762
|
+
const key = `${Math.round(r.top)}:${Math.round(r.width)}`;
|
|
763
|
+
if (key === lastKey) {
|
|
764
|
+
stable += 1;
|
|
765
|
+
} else {
|
|
766
|
+
stable = 0;
|
|
767
|
+
lastKey = key;
|
|
768
|
+
}
|
|
769
|
+
elapsed += 1;
|
|
770
|
+
// Keep re-sending until the layout has held steady for a while (so a late
|
|
771
|
+
// settle is still caught), capped so it never runs unbounded.
|
|
772
|
+
if (stable < 12 && elapsed < 180) {
|
|
773
|
+
this.winSettleFrame = requestAnimationFrame(tick);
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
this.winSettleFrame = requestAnimationFrame(tick);
|
|
777
|
+
}
|
|
778
|
+
|
|
678
779
|
private scheduleIOSBootstrapFrames(): void {
|
|
679
780
|
if (!isIOS()) return;
|
|
680
781
|
// Keep a short frame-based bootstrap window so late-created WKChildScrollView
|
package/dist/input.d.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
export interface LxInputEventDetail {
|
|
2
|
-
value?: string;
|
|
3
|
-
cursor?: number;
|
|
4
|
-
keyCode?: number;
|
|
5
|
-
height?: number;
|
|
6
|
-
duration?: number;
|
|
7
|
-
encryptedValue?: string;
|
|
8
|
-
encryptError?: string;
|
|
9
|
-
pass?: boolean;
|
|
10
|
-
timeout?: boolean;
|
|
11
|
-
}
|
|
12
|
-
export interface LxInputEvent extends CustomEvent<LxInputEventDetail> {
|
|
13
|
-
detail: LxInputEventDetail;
|
|
14
|
-
}
|
|
15
|
-
export type LxInputAttributes = {
|
|
16
|
-
id?: string;
|
|
17
|
-
value?: string;
|
|
18
|
-
type?: 'text' | 'number' | 'password' | 'digit';
|
|
19
|
-
password?: boolean | string;
|
|
20
|
-
placeholder?: string;
|
|
21
|
-
'placeholder-style'?: string;
|
|
22
|
-
'placeholder-class'?: string;
|
|
23
|
-
maxlength?: number | string;
|
|
24
|
-
'cursor-spacing'?: number | string;
|
|
25
|
-
'auto-focus'?: boolean | string;
|
|
26
|
-
disabled?: boolean | string;
|
|
27
|
-
focus?: boolean | string;
|
|
28
|
-
'confirm-type'?: 'send' | 'search' | 'next' | 'go' | 'done';
|
|
29
|
-
'always-embed'?: boolean | string;
|
|
30
|
-
'confirm-hold'?: boolean | string;
|
|
31
|
-
cursor?: number | string;
|
|
32
|
-
'cursor-color'?: string;
|
|
33
|
-
'selection-start'?: number | string;
|
|
34
|
-
'selection-end'?: number | string;
|
|
35
|
-
'adjust-position'?: boolean | string;
|
|
36
|
-
'hold-keyboard'?: boolean | string;
|
|
37
|
-
className?: string;
|
|
38
|
-
style?: any;
|
|
39
|
-
ref?: any;
|
|
40
|
-
onInput?: (e: LxInputEvent) => void;
|
|
41
|
-
onChange?: (e: LxInputEvent) => void;
|
|
42
|
-
onFocus?: (e: LxInputEvent) => void;
|
|
43
|
-
onBlur?: (e: LxInputEvent) => void;
|
|
44
|
-
onConfirm?: (e: LxInputEvent) => void;
|
|
45
|
-
onKeyboardheightchange?: (e: LxInputEvent) => void;
|
|
46
|
-
onNicknamereview?: (e: LxInputEvent) => void;
|
|
47
|
-
pageBindings?: Record<string, string>;
|
|
48
|
-
};
|
|
49
|
-
declare global {
|
|
50
|
-
namespace JSX {
|
|
51
|
-
interface IntrinsicElements {
|
|
52
|
-
"lx-input": LxInputAttributes;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
export declare class LxInputElement extends HTMLElement {
|
|
57
|
-
static get observedAttributes(): string[];
|
|
58
|
-
private componentId;
|
|
59
|
-
private mounted;
|
|
60
|
-
private unregister?;
|
|
61
|
-
private _handlers;
|
|
62
|
-
private rawHandlers;
|
|
63
|
-
private harmonyEmbed?;
|
|
64
|
-
private lastHarmonyProps?;
|
|
65
|
-
private pendingHarmonyProps?;
|
|
66
|
-
private pendingHarmonyRetryTimer;
|
|
67
|
-
private pendingHarmonyRetryCount;
|
|
68
|
-
private attrObserver?;
|
|
69
|
-
private shadow?;
|
|
70
|
-
private innerInput?;
|
|
71
|
-
private readonly updateState;
|
|
72
|
-
private resizeObserver?;
|
|
73
|
-
private pendingLayoutFrame;
|
|
74
|
-
private focusLayoutSyncTimer;
|
|
75
|
-
private readonly layoutEventListener;
|
|
76
|
-
private _pageBindings;
|
|
77
|
-
set pageBindings(bindings: Record<string, string>);
|
|
78
|
-
get pageBindings(): Record<string, string>;
|
|
79
|
-
private upgradeProperty;
|
|
80
|
-
private registerNativeHandler;
|
|
81
|
-
private unmountNativeComponentById;
|
|
82
|
-
private teardownHarmonyEmbed;
|
|
83
|
-
connectedCallback(): void;
|
|
84
|
-
disconnectedCallback(): void;
|
|
85
|
-
attributeChangedCallback(name: string): void;
|
|
86
|
-
private getAttr;
|
|
87
|
-
private getBoolAttr;
|
|
88
|
-
private getNumAttr;
|
|
89
|
-
private getPropOrAttr;
|
|
90
|
-
private shouldTrackNativeLayout;
|
|
91
|
-
private startTracking;
|
|
92
|
-
private stopTracking;
|
|
93
|
-
private startSizeObserver;
|
|
94
|
-
private stopSizeObserver;
|
|
95
|
-
private schedulePositionSync;
|
|
96
|
-
private collectProps;
|
|
97
|
-
private getHostTextColor;
|
|
98
|
-
private resyncHarmonyLayoutAfterFocus;
|
|
99
|
-
private startFocusLayoutSync;
|
|
100
|
-
private stopFocusLayoutSync;
|
|
101
|
-
private shouldRefreshForAttribute;
|
|
102
|
-
private startAttrObserver;
|
|
103
|
-
private stopAttrObserver;
|
|
104
|
-
private collectDataset;
|
|
105
|
-
private getInputType;
|
|
106
|
-
private mountDesktopInput;
|
|
107
|
-
private mountInput;
|
|
108
|
-
syncNativeProps(): void;
|
|
109
|
-
private ensureHostLayout;
|
|
110
|
-
set oninput(cb: EventListener | null);
|
|
111
|
-
get oninput(): EventListener | null;
|
|
112
|
-
private ensureHarmonyEmbed;
|
|
113
|
-
private encodeHarmonyProps;
|
|
114
|
-
private pushHarmonyPropsUpdate;
|
|
115
|
-
private scheduleHarmonyPropsRetry;
|
|
116
|
-
}
|
|
117
|
-
export declare function registerInputComponent(): void;
|