@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,7 @@
|
|
|
1
|
+
import type { AuthorNode } from "./types.js";
|
|
2
|
+
export declare const OBSERVED_STYLE_FIELDS: string[];
|
|
3
|
+
/** Native nodes are flat siblings, so each receives its cumulative alpha. */
|
|
4
|
+
export declare function effectiveOpacity(element: Element): number;
|
|
5
|
+
/** Canvas resolves browser-supported CSS colors into the hosts' sRGB wire format. */
|
|
6
|
+
export declare function normalizeNativeColor(document: Document, value: string): string | undefined;
|
|
7
|
+
export declare function collectNativeStyleIssues(element: Element): NonNullable<AuthorNode["styleIssues"]>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// Computed values catch unsupported styles from classes as well as inline CSS.
|
|
2
|
+
const unsupportedDefaults = {
|
|
3
|
+
transform: ["none"], translate: ["none"], rotate: ["none"], scale: ["none"],
|
|
4
|
+
perspective: ["none"], clipPath: ["none"], maskImage: ["none"],
|
|
5
|
+
boxShadow: ["none"], filter: ["none"], backdropFilter: ["none"],
|
|
6
|
+
mixBlendMode: ["normal"], backgroundImage: ["none"],
|
|
7
|
+
animationName: ["none"], transitionDuration: ["0s"],
|
|
8
|
+
textDecorationLine: ["none"], fontStyle: ["normal"],
|
|
9
|
+
letterSpacing: ["normal", "0px"], textShadow: ["none"],
|
|
10
|
+
};
|
|
11
|
+
const layoutProperties = new Set([
|
|
12
|
+
"transform", "translate", "rotate", "scale", "perspective", "clipPath", "maskImage",
|
|
13
|
+
]);
|
|
14
|
+
export const OBSERVED_STYLE_FIELDS = [
|
|
15
|
+
...Object.keys(unsupportedDefaults), "display", "visibility", "opacity", "overflowX", "overflowY",
|
|
16
|
+
"color", "backgroundColor", "accentColor", "fontSize", "fontWeight", "lineHeight", "textAlign", "direction",
|
|
17
|
+
...["Top", "Right", "Bottom", "Left"].flatMap(side => ["Width", "Style", "Color"].map(suffix => `border${side}${suffix}`)),
|
|
18
|
+
"borderTopLeftRadius", "borderTopRightRadius", "borderBottomLeftRadius", "borderBottomRightRadius",
|
|
19
|
+
];
|
|
20
|
+
/** Native nodes are flat siblings, so each receives its cumulative alpha. */
|
|
21
|
+
export function effectiveOpacity(element) {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
let opacity = 1;
|
|
24
|
+
for (let node = element; node; node = node.parentElement) {
|
|
25
|
+
opacity *= Number((_b = (_a = node.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.getComputedStyle(node).opacity) !== null && _b !== void 0 ? _b : 1);
|
|
26
|
+
}
|
|
27
|
+
return opacity;
|
|
28
|
+
}
|
|
29
|
+
const colorContexts = new WeakMap();
|
|
30
|
+
const colors = new Map();
|
|
31
|
+
/** Canvas resolves browser-supported CSS colors into the hosts' sRGB wire format. */
|
|
32
|
+
export function normalizeNativeColor(document, value) {
|
|
33
|
+
var _a;
|
|
34
|
+
if (/^rgba?\([\d.,\s]+\)$/.test(value) || /^#[\da-f]{6}([\da-f]{2})?$/i.test(value))
|
|
35
|
+
return value;
|
|
36
|
+
const cached = colors.get(value);
|
|
37
|
+
if (cached)
|
|
38
|
+
return cached;
|
|
39
|
+
let context = colorContexts.get(document);
|
|
40
|
+
if (!context) {
|
|
41
|
+
const canvas = document.createElement("canvas");
|
|
42
|
+
canvas.width = canvas.height = 1;
|
|
43
|
+
context = (_a = canvas.getContext("2d", { willReadFrequently: true, colorSpace: "srgb" })) !== null && _a !== void 0 ? _a : undefined;
|
|
44
|
+
if (!context)
|
|
45
|
+
return undefined;
|
|
46
|
+
colorContexts.set(document, context);
|
|
47
|
+
}
|
|
48
|
+
context.fillStyle = "#010203";
|
|
49
|
+
context.fillStyle = value;
|
|
50
|
+
const first = context.fillStyle;
|
|
51
|
+
context.fillStyle = "#040506";
|
|
52
|
+
context.fillStyle = value;
|
|
53
|
+
if (context.fillStyle !== first)
|
|
54
|
+
return undefined;
|
|
55
|
+
context.clearRect(0, 0, 1, 1);
|
|
56
|
+
context.fillRect(0, 0, 1, 1);
|
|
57
|
+
const [r, g, b, a] = context.getImageData(0, 0, 1, 1).data;
|
|
58
|
+
const result = `rgba(${r}, ${g}, ${b}, ${a / 255})`;
|
|
59
|
+
if (colors.size >= 256)
|
|
60
|
+
colors.clear();
|
|
61
|
+
colors.set(value, result);
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
export function collectNativeStyleIssues(element) {
|
|
65
|
+
var _a, _b;
|
|
66
|
+
const view = (_a = element.ownerDocument) === null || _a === void 0 ? void 0 : _a.defaultView;
|
|
67
|
+
if (!(view === null || view === void 0 ? void 0 : view.getComputedStyle))
|
|
68
|
+
return [];
|
|
69
|
+
const style = view.getComputedStyle(element);
|
|
70
|
+
const issues = [];
|
|
71
|
+
for (const property of ["color", "backgroundColor", "borderTopColor", "accentColor"]) {
|
|
72
|
+
const value = style[property];
|
|
73
|
+
if (value && value !== "auto" && !normalizeNativeColor(element.ownerDocument, value)) {
|
|
74
|
+
issues.push({ property, value, layout: false });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
for (const [property, defaults] of Object.entries(unsupportedDefaults)) {
|
|
78
|
+
const value = String((_b = style[property]) !== null && _b !== void 0 ? _b : "").trim();
|
|
79
|
+
if (!value || defaults.includes(value))
|
|
80
|
+
continue;
|
|
81
|
+
if (property === "backgroundImage" && isVideoPoster(element, value))
|
|
82
|
+
continue;
|
|
83
|
+
if (property === "transitionDuration" && value.split(",").every((duration) => parseFloat(duration) === 0))
|
|
84
|
+
continue;
|
|
85
|
+
issues.push({ property, value, layout: layoutProperties.has(property) });
|
|
86
|
+
}
|
|
87
|
+
const corners = [style.borderTopLeftRadius, style.borderTopRightRadius,
|
|
88
|
+
style.borderBottomRightRadius, style.borderBottomLeftRadius];
|
|
89
|
+
if (corners.some((value) => value && (value !== corners[0] || !/^\d+(\.\d+)?px$/.test(value)))) {
|
|
90
|
+
issues.push({ property: "borderRadius", value: corners.join(" / "), layout: false });
|
|
91
|
+
}
|
|
92
|
+
for (const suffix of ["Width", "Color", "Style"]) {
|
|
93
|
+
const values = [style[`borderTop${suffix}`], style[`borderRight${suffix}`],
|
|
94
|
+
style[`borderBottom${suffix}`], style[`borderLeft${suffix}`]];
|
|
95
|
+
const hasBorder = [style.borderTopWidth, style.borderRightWidth,
|
|
96
|
+
style.borderBottomWidth, style.borderLeftWidth].some((width) => parseFloat(width) > 0);
|
|
97
|
+
if (hasBorder && (values.some((value) => value !== values[0]) ||
|
|
98
|
+
(suffix === "Style" && values.some((value) => value !== "solid" && value !== "none")))) {
|
|
99
|
+
issues.push({ property: `border${suffix}`, value: values.join(" / "), layout: false });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return issues;
|
|
103
|
+
}
|
|
104
|
+
function isVideoPoster(element, background) {
|
|
105
|
+
var _a;
|
|
106
|
+
if (element.tagName.toLowerCase() !== "lx-video")
|
|
107
|
+
return false;
|
|
108
|
+
const poster = element.getAttribute("poster");
|
|
109
|
+
const url = (_a = /^url\("(.*)"\)$/.exec(background)) === null || _a === void 0 ? void 0 : _a[1];
|
|
110
|
+
if (!poster || !url)
|
|
111
|
+
return false;
|
|
112
|
+
// LxVideo owns this DOM placeholder; the native backend paints its poster prop.
|
|
113
|
+
try {
|
|
114
|
+
return new URL(url.replace(/\\(["\\])/g, "$1"), element.baseURI).href === new URL(poster, element.baseURI).href;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { CoreKind, HostFactoryKind, NativeErrorCode, PublicComponentName } from "./schema.js";
|
|
2
|
+
export interface RootRef {
|
|
3
|
+
surfaceInstanceId: string;
|
|
4
|
+
pageInstanceId: string;
|
|
5
|
+
documentInstanceId: string;
|
|
6
|
+
rootKey: string;
|
|
7
|
+
rootEpoch: number;
|
|
8
|
+
}
|
|
9
|
+
export interface NodeRef extends RootRef {
|
|
10
|
+
nodeKey: string;
|
|
11
|
+
nodeEpoch: number;
|
|
12
|
+
}
|
|
13
|
+
export interface NativeError {
|
|
14
|
+
code: NativeErrorCode;
|
|
15
|
+
scope: "node" | "root";
|
|
16
|
+
recoverable: boolean;
|
|
17
|
+
root: RootRef;
|
|
18
|
+
node?: NodeRef;
|
|
19
|
+
message: string;
|
|
20
|
+
}
|
|
21
|
+
export interface PressPayload {
|
|
22
|
+
source: "pointer" | "keyboard" | "accessibility";
|
|
23
|
+
}
|
|
24
|
+
export interface ValuePayload {
|
|
25
|
+
value: number;
|
|
26
|
+
}
|
|
27
|
+
export interface FocusPayload {
|
|
28
|
+
source: "pointer" | "keyboard" | "accessibility";
|
|
29
|
+
}
|
|
30
|
+
export interface PointerPayload {
|
|
31
|
+
source: "pointer";
|
|
32
|
+
}
|
|
33
|
+
export type NativeHandler<TPayload> = (payload: TPayload) => void;
|
|
34
|
+
export type AuthorChild = AuthorNode | string | number | false | null | undefined | readonly AuthorChild[];
|
|
35
|
+
export interface AuthorNode {
|
|
36
|
+
type: string;
|
|
37
|
+
authorId?: string;
|
|
38
|
+
automationId?: string;
|
|
39
|
+
props?: Record<string, unknown>;
|
|
40
|
+
children?: AuthorChild;
|
|
41
|
+
textContent?: string | number;
|
|
42
|
+
styleIssues?: Array<{
|
|
43
|
+
property: string;
|
|
44
|
+
value: string;
|
|
45
|
+
layout: boolean;
|
|
46
|
+
}>;
|
|
47
|
+
}
|
|
48
|
+
export interface CoreNode {
|
|
49
|
+
kind: HostFactoryKind;
|
|
50
|
+
authorType: PublicComponentName;
|
|
51
|
+
authorId?: string;
|
|
52
|
+
automationId?: string;
|
|
53
|
+
props: Record<string, unknown>;
|
|
54
|
+
children: CoreNode[];
|
|
55
|
+
text?: string;
|
|
56
|
+
}
|
|
57
|
+
export interface CompiledNativeRoot {
|
|
58
|
+
authorType: "LxNativeRoot";
|
|
59
|
+
authorId?: string;
|
|
60
|
+
automationId?: string;
|
|
61
|
+
props: Record<string, unknown>;
|
|
62
|
+
children: CoreNode[];
|
|
63
|
+
kind: Extract<CoreKind, "root">;
|
|
64
|
+
}
|
|
65
|
+
export type CompileInlineNativeResult = {
|
|
66
|
+
ok: true;
|
|
67
|
+
root: CompiledNativeRoot;
|
|
68
|
+
diagnostics: NativeError[];
|
|
69
|
+
} | {
|
|
70
|
+
ok: false;
|
|
71
|
+
error: NativeError;
|
|
72
|
+
diagnostics: NativeError[];
|
|
73
|
+
};
|
|
74
|
+
export interface CompileInlineNativeOptions {
|
|
75
|
+
rootRef?: RootRef;
|
|
76
|
+
/**
|
|
77
|
+
* When true, `controls` defaults to `true` (author contract). Tests and
|
|
78
|
+
* wrappers should leave this on; only a host fixture may override it.
|
|
79
|
+
*/
|
|
80
|
+
defaultVideoControls?: boolean;
|
|
81
|
+
}
|
|
82
|
+
export declare const EMPTY_ROOT_REF: RootRef;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payload-first unwrap used by React/Vue wrappers. HTML keeps CustomEvent.
|
|
3
|
+
*
|
|
4
|
+
* A real DOM Event (or an event-like `{ type, detail }` used by unit tests)
|
|
5
|
+
* yields `detail`. A bare payload object is returned as-is so wrappers can
|
|
6
|
+
* also be called with already-unwrapped values.
|
|
7
|
+
*/
|
|
8
|
+
export declare function unwrapNativeEventPayload<TPayload>(event: unknown): TPayload;
|
|
9
|
+
export declare function isDomEvent(value: unknown): value is Event;
|
|
10
|
+
export declare function bindPayloadHandler<TPayload>(handler: ((payload: TPayload) => void) | undefined): EventListener;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Payload-first unwrap used by React/Vue wrappers. HTML keeps CustomEvent.
|
|
3
|
+
*
|
|
4
|
+
* A real DOM Event (or an event-like `{ type, detail }` used by unit tests)
|
|
5
|
+
* yields `detail`. A bare payload object is returned as-is so wrappers can
|
|
6
|
+
* also be called with already-unwrapped values.
|
|
7
|
+
*/
|
|
8
|
+
export function unwrapNativeEventPayload(event) {
|
|
9
|
+
if (isDomEvent(event) || isEventLike(event)) {
|
|
10
|
+
const detail = event.detail;
|
|
11
|
+
return (detail === undefined ? {} : detail);
|
|
12
|
+
}
|
|
13
|
+
return event;
|
|
14
|
+
}
|
|
15
|
+
export function isDomEvent(value) {
|
|
16
|
+
return typeof Event !== "undefined" && value instanceof Event;
|
|
17
|
+
}
|
|
18
|
+
function isEventLike(value) {
|
|
19
|
+
if (!value || typeof value !== "object")
|
|
20
|
+
return false;
|
|
21
|
+
const record = value;
|
|
22
|
+
if (!("detail" in record))
|
|
23
|
+
return false;
|
|
24
|
+
return "type" in record || "target" in record || "currentTarget" in record;
|
|
25
|
+
}
|
|
26
|
+
export function bindPayloadHandler(handler) {
|
|
27
|
+
return (event) => {
|
|
28
|
+
if (typeof handler !== "function")
|
|
29
|
+
return;
|
|
30
|
+
handler(unwrapNativeEventPayload(event));
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { NodeRef } from "./types.js";
|
|
2
|
+
export declare const VIDEO_COMMANDS: readonly ["play", "pause", "stop", "seek", "enterFullscreen", "exitFullscreen", "setStreamSource"];
|
|
3
|
+
export type VideoCommandName = (typeof VIDEO_COMMANDS)[number];
|
|
4
|
+
export type VideoCommand = {
|
|
5
|
+
name: "play";
|
|
6
|
+
} | {
|
|
7
|
+
name: "pause";
|
|
8
|
+
} | {
|
|
9
|
+
name: "stop";
|
|
10
|
+
} | {
|
|
11
|
+
name: "seek";
|
|
12
|
+
seconds: number;
|
|
13
|
+
} | {
|
|
14
|
+
name: "enterFullscreen";
|
|
15
|
+
} | {
|
|
16
|
+
name: "exitFullscreen";
|
|
17
|
+
} | {
|
|
18
|
+
name: "setStreamSource";
|
|
19
|
+
options: Record<string, unknown>;
|
|
20
|
+
};
|
|
21
|
+
export interface VideoCommandRequest {
|
|
22
|
+
action: "video.command";
|
|
23
|
+
owner: NodeRef;
|
|
24
|
+
requestId: string;
|
|
25
|
+
command: VideoCommand;
|
|
26
|
+
}
|
|
27
|
+
export interface VideoControlDescriptorBase {
|
|
28
|
+
controlId: string;
|
|
29
|
+
label: string;
|
|
30
|
+
frame: {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
width: number;
|
|
34
|
+
height: number;
|
|
35
|
+
};
|
|
36
|
+
visible: boolean;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export type VideoControlDescriptor = (VideoControlDescriptorBase & {
|
|
40
|
+
role: "button";
|
|
41
|
+
pressed?: boolean;
|
|
42
|
+
expanded?: boolean;
|
|
43
|
+
actions: ReadonlyArray<"focus" | "blur" | "activate">;
|
|
44
|
+
}) | (VideoControlDescriptorBase & {
|
|
45
|
+
role: "slider";
|
|
46
|
+
value: number;
|
|
47
|
+
min: number;
|
|
48
|
+
max: number;
|
|
49
|
+
actions: ReadonlyArray<"focus" | "blur" | "increment" | "decrement" | "setValue">;
|
|
50
|
+
});
|
|
51
|
+
export interface VideoControlsSemanticSnapshot {
|
|
52
|
+
action: "video.controlsSemanticSnapshot";
|
|
53
|
+
owner: NodeRef;
|
|
54
|
+
revision: number;
|
|
55
|
+
controls: readonly VideoControlDescriptor[];
|
|
56
|
+
}
|
|
57
|
+
export declare function buildVideoCommandRequest(owner: NodeRef, command: VideoCommand, requestId: string): VideoCommandRequest;
|
|
58
|
+
export declare function videoCommandUrls(command: VideoCommand): string[];
|
|
59
|
+
export declare function collectVideoResourceUrls(props: Record<string, unknown>): string[];
|
|
60
|
+
export declare function validateControlsSnapshot(snapshot: VideoControlsSemanticSnapshot, lastRevision: number): {
|
|
61
|
+
ok: true;
|
|
62
|
+
} | {
|
|
63
|
+
ok: false;
|
|
64
|
+
message: string;
|
|
65
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export const VIDEO_COMMANDS = [
|
|
2
|
+
"play",
|
|
3
|
+
"pause",
|
|
4
|
+
"stop",
|
|
5
|
+
"seek",
|
|
6
|
+
"enterFullscreen",
|
|
7
|
+
"exitFullscreen",
|
|
8
|
+
"setStreamSource",
|
|
9
|
+
];
|
|
10
|
+
export function buildVideoCommandRequest(owner, command, requestId) {
|
|
11
|
+
return { action: "video.command", owner, requestId, command };
|
|
12
|
+
}
|
|
13
|
+
export function videoCommandUrls(command) {
|
|
14
|
+
var _a;
|
|
15
|
+
if (command.name !== "setStreamSource")
|
|
16
|
+
return [];
|
|
17
|
+
const options = (_a = command.options) !== null && _a !== void 0 ? _a : {};
|
|
18
|
+
const urls = [];
|
|
19
|
+
for (const key of ["url", "src", "uri"]) {
|
|
20
|
+
const value = options[key];
|
|
21
|
+
if (typeof value === "string" && value)
|
|
22
|
+
urls.push(value);
|
|
23
|
+
}
|
|
24
|
+
return urls;
|
|
25
|
+
}
|
|
26
|
+
export function collectVideoResourceUrls(props) {
|
|
27
|
+
const urls = [];
|
|
28
|
+
for (const key of ["src", "poster"]) {
|
|
29
|
+
const value = props[key];
|
|
30
|
+
if (typeof value === "string" && value)
|
|
31
|
+
urls.push(value);
|
|
32
|
+
}
|
|
33
|
+
const watermark = props.watermark;
|
|
34
|
+
if ((watermark === null || watermark === void 0 ? void 0 : watermark.resource) && typeof watermark.resource === "object") {
|
|
35
|
+
const url = watermark.resource.url;
|
|
36
|
+
if (typeof url === "string" && url)
|
|
37
|
+
urls.push(url);
|
|
38
|
+
}
|
|
39
|
+
const qualities = props.qualities;
|
|
40
|
+
if (Array.isArray(qualities)) {
|
|
41
|
+
for (const item of qualities) {
|
|
42
|
+
if (item && typeof item === "object" && typeof item.url === "string") {
|
|
43
|
+
urls.push(item.url);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return urls;
|
|
48
|
+
}
|
|
49
|
+
export function validateControlsSnapshot(snapshot, lastRevision) {
|
|
50
|
+
if (snapshot.revision <= lastRevision) {
|
|
51
|
+
return { ok: false, message: "controls snapshot revision must increase" };
|
|
52
|
+
}
|
|
53
|
+
const seen = new Set();
|
|
54
|
+
for (const control of snapshot.controls) {
|
|
55
|
+
if (!control.controlId || seen.has(control.controlId)) {
|
|
56
|
+
return { ok: false, message: "controlId must be unique and non-empty" };
|
|
57
|
+
}
|
|
58
|
+
seen.add(control.controlId);
|
|
59
|
+
if (control.role === "slider") {
|
|
60
|
+
if (![control.min, control.max, control.value].every(Number.isFinite)) {
|
|
61
|
+
return { ok: false, message: "slider descriptor values must be finite" };
|
|
62
|
+
}
|
|
63
|
+
if (control.min > control.value || control.value > control.max) {
|
|
64
|
+
return { ok: false, message: "slider descriptor value is out of range" };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { ok: true };
|
|
69
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { NavigatorChannel, NavigatorOpenType, NavigatorTarget } from "./navigator.js";
|
|
2
2
|
import type { NavigatorQuery } from "./navigator.js";
|
|
3
3
|
import type { LxMediaSwiperItem } from "./media_swiper.js";
|
|
4
4
|
import type { LxVideoQuality } from "./video.js";
|
|
@@ -12,7 +12,7 @@ export interface NavigatorNativeAttrOptions {
|
|
|
12
12
|
delta?: number;
|
|
13
13
|
appId?: string;
|
|
14
14
|
query?: NavigatorQuery;
|
|
15
|
-
|
|
15
|
+
channel?: NavigatorChannel;
|
|
16
16
|
targetVersion?: string;
|
|
17
17
|
phoneNumber?: string;
|
|
18
18
|
hoverClass?: string;
|
|
@@ -61,6 +61,7 @@ export declare const VIDEO_DOM_EVENT_MAP: {
|
|
|
61
61
|
readonly onWaiting: "waiting";
|
|
62
62
|
readonly onQualityChange: "qualitychange";
|
|
63
63
|
readonly onRateChange: "ratechange";
|
|
64
|
+
readonly onVolumeChange: "volumechange";
|
|
64
65
|
};
|
|
65
66
|
export interface VideoNativeAttrOptions {
|
|
66
67
|
id?: string;
|
|
@@ -43,8 +43,8 @@ export function buildNavigatorNativeAttrs(options, extraAttrs = {}) {
|
|
|
43
43
|
result["app-id"] = options.appId;
|
|
44
44
|
if (options.query)
|
|
45
45
|
result.query = JSON.stringify(options.query);
|
|
46
|
-
if (options.
|
|
47
|
-
result
|
|
46
|
+
if (options.channel)
|
|
47
|
+
result.channel = options.channel;
|
|
48
48
|
if (options.targetVersion)
|
|
49
49
|
result["target-version"] = options.targetVersion;
|
|
50
50
|
if (options.phoneNumber)
|
|
@@ -164,6 +164,7 @@ export const VIDEO_DOM_EVENT_MAP = {
|
|
|
164
164
|
onWaiting: "waiting",
|
|
165
165
|
onQualityChange: "qualitychange",
|
|
166
166
|
onRateChange: "ratechange",
|
|
167
|
+
onVolumeChange: "volumechange",
|
|
167
168
|
};
|
|
168
169
|
export function buildVideoNativeAttrs(options, extraAttrs = {}) {
|
|
169
170
|
var _a, _b;
|
|
@@ -181,7 +182,9 @@ export function buildVideoNativeAttrs(options, extraAttrs = {}) {
|
|
|
181
182
|
result.loop = "";
|
|
182
183
|
if (options.muted)
|
|
183
184
|
result.muted = "";
|
|
184
|
-
if (options.controls)
|
|
185
|
+
if (options.controls === false)
|
|
186
|
+
result.controls = "false";
|
|
187
|
+
else if (options.controls)
|
|
185
188
|
result.controls = "";
|
|
186
189
|
if (options.progressBar === false)
|
|
187
190
|
result["progress-bar"] = "false";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { NativeComponentMessage } from './types.js';
|
|
2
2
|
export type { NativeComponentMessage } from './types.js';
|
|
3
|
-
export declare function sendNativeComponentMessage(message: NativeComponentMessage):
|
|
3
|
+
export declare function sendNativeComponentMessage(message: NativeComponentMessage): boolean;
|
|
4
4
|
export declare function registerNativeComponentHandler(id: string, handler: (msg: NativeComponentMessage) => void): () => void;
|
|
5
5
|
export declare function invalidateNativeComponentLayout(delays?: number[]): void;
|
|
6
6
|
export declare function addNativeComponentLayoutInvalidationListener(listener: EventListenerOrEventListenerObject): () => void;
|
package/dist/nativecomponent.js
CHANGED
|
@@ -2,6 +2,8 @@ let warnedNoHandler = false;
|
|
|
2
2
|
const NATIVE_COMPONENT_LAYOUT_INVALIDATED_EVENT = "lingxia:native-component-layout-invalidated";
|
|
3
3
|
let pendingLayoutInvalidationFrame = null;
|
|
4
4
|
const pendingLayoutInvalidationTimers = new Map();
|
|
5
|
+
/// Returns false when the native channel is not up yet and the message was
|
|
6
|
+
/// dropped, so callers that carry state (an island's first commit) can retry.
|
|
5
7
|
export function sendNativeComponentMessage(message) {
|
|
6
8
|
var _a, _b;
|
|
7
9
|
const sender = typeof window !== "undefined"
|
|
@@ -12,29 +14,24 @@ export function sendNativeComponentMessage(message) {
|
|
|
12
14
|
warnedNoHandler = true;
|
|
13
15
|
console.warn("[LingXia NativeComponent] message handler not available");
|
|
14
16
|
}
|
|
15
|
-
return;
|
|
17
|
+
return false;
|
|
16
18
|
}
|
|
17
19
|
sender(message);
|
|
20
|
+
return true;
|
|
18
21
|
}
|
|
19
22
|
export function registerNativeComponentHandler(id, handler) {
|
|
20
|
-
var _a
|
|
23
|
+
var _a;
|
|
21
24
|
const nativeComponents = typeof window !== "undefined"
|
|
22
25
|
? (_a = window.LingXiaBridge) === null || _a === void 0 ? void 0 : _a.nativeComponents
|
|
23
26
|
: undefined;
|
|
24
27
|
const registerFn = nativeComponents === null || nativeComponents === void 0 ? void 0 : nativeComponents.register;
|
|
25
28
|
if (typeof registerFn === "function") {
|
|
26
29
|
const unregister = registerFn(id, handler);
|
|
27
|
-
const platform = (_b = window.LingXiaBridge) === null || _b === void 0 ? void 0 : _b.platform;
|
|
28
|
-
const requiresReadyHandshake = !!(((_c = platform === null || platform === void 0 ? void 0 : platform.isIOS) === null || _c === void 0 ? void 0 : _c.call(platform)) ||
|
|
29
|
-
((_d = platform === null || platform === void 0 ? void 0 : platform.isAndroid) === null || _d === void 0 ? void 0 : _d.call(platform)) ||
|
|
30
|
-
((_e = platform === null || platform === void 0 ? void 0 : platform.isMacOS) === null || _e === void 0 ? void 0 : _e.call(platform)));
|
|
31
|
-
const hasHandler = nativeComponents === null || nativeComponents === void 0 ? void 0 : nativeComponents.hasHandler;
|
|
32
30
|
const send = nativeComponents === null || nativeComponents === void 0 ? void 0 : nativeComponents.send;
|
|
31
|
+
// Always announce ready after the leaf handler is registered, including
|
|
32
|
+
// Windows: island video queues play/playing until this id's handshake.
|
|
33
33
|
if (typeof send === "function") {
|
|
34
|
-
|
|
35
|
-
if (requiresReadyHandshake || nativeReady) {
|
|
36
|
-
send({ action: "component.ready", id });
|
|
37
|
-
}
|
|
34
|
+
send({ action: "component.ready", id });
|
|
38
35
|
}
|
|
39
36
|
return unregister;
|
|
40
37
|
}
|
package/dist/navigator.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export type NavigatorOpenType = 'navigate' | 'redirect' | 'navigateBack' | 'reLa
|
|
|
2
2
|
export type NavigatorTarget = 'self' | 'lxapp' | 'browser';
|
|
3
3
|
export type NavigatorQueryValue = string | number | boolean | null | undefined;
|
|
4
4
|
export type NavigatorQuery = Record<string, NavigatorQueryValue>;
|
|
5
|
-
export type
|
|
5
|
+
export type NavigatorChannel = 'release' | 'preview' | 'draft';
|
|
6
6
|
export interface LxNavigatorEventDetail {
|
|
7
7
|
success?: boolean;
|
|
8
8
|
errMsg?: string;
|
|
@@ -18,7 +18,7 @@ export type LxNavigatorAttributes = {
|
|
|
18
18
|
target?: NavigatorTarget;
|
|
19
19
|
delta?: number;
|
|
20
20
|
'app-id'?: string;
|
|
21
|
-
|
|
21
|
+
channel?: NavigatorChannel;
|
|
22
22
|
'target-version'?: string;
|
|
23
23
|
'phone-number'?: string;
|
|
24
24
|
'hover-class'?: string;
|
package/dist/navigator.js
CHANGED
|
@@ -44,7 +44,7 @@ export class LxNavigatorElement extends HTMLElement {
|
|
|
44
44
|
"target",
|
|
45
45
|
"delta",
|
|
46
46
|
"app-id",
|
|
47
|
-
"
|
|
47
|
+
"channel",
|
|
48
48
|
"target-version",
|
|
49
49
|
"phone-number",
|
|
50
50
|
"hover-class",
|
|
@@ -169,7 +169,7 @@ export class LxNavigatorElement extends HTMLElement {
|
|
|
169
169
|
const explicitTarget = this.getAttribute('target');
|
|
170
170
|
const delta = parseInt(this.getAttribute('delta') || '1', 10);
|
|
171
171
|
const appId = this.getAttribute('app-id');
|
|
172
|
-
const
|
|
172
|
+
const channel = this.getAttribute('channel');
|
|
173
173
|
const targetVersion = this.getAttribute('target-version');
|
|
174
174
|
const phoneNumber = this.getAttribute('phone-number');
|
|
175
175
|
// Auto-infer target if not explicitly specified
|
|
@@ -183,7 +183,7 @@ export class LxNavigatorElement extends HTMLElement {
|
|
|
183
183
|
target,
|
|
184
184
|
delta,
|
|
185
185
|
appId,
|
|
186
|
-
|
|
186
|
+
channel,
|
|
187
187
|
targetVersion,
|
|
188
188
|
phoneNumber
|
|
189
189
|
});
|
|
@@ -381,8 +381,8 @@ export class LxNavigatorElement extends HTMLElement {
|
|
|
381
381
|
const pageTarget = this.buildPageTarget(options);
|
|
382
382
|
if (pageTarget)
|
|
383
383
|
Object.assign(target, pageTarget);
|
|
384
|
-
if (options.
|
|
385
|
-
target.
|
|
384
|
+
if (options.channel)
|
|
385
|
+
target.channel = options.channel;
|
|
386
386
|
if (options.targetVersion)
|
|
387
387
|
target.targetVersion = options.targetVersion;
|
|
388
388
|
return target;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Keep platform spelling differences out of the public DOM/payload contract. */
|
|
2
|
+
export function normalizeVideoEventDetail(event, payload) {
|
|
3
|
+
var _a, _b, _c, _d, _e;
|
|
4
|
+
const detail = payload && typeof payload === "object" ? payload : {};
|
|
5
|
+
if (["playrequest", "play", "playing", "pause", "stop", "ended", "waiting"].includes(event))
|
|
6
|
+
return {};
|
|
7
|
+
if (event === "fullscreenchange") {
|
|
8
|
+
return { ...detail, fullscreen: (_b = (_a = detail.fullscreen) !== null && _a !== void 0 ? _a : detail.fullScreen) !== null && _b !== void 0 ? _b : false };
|
|
9
|
+
}
|
|
10
|
+
if (event === "error") {
|
|
11
|
+
return {
|
|
12
|
+
...detail,
|
|
13
|
+
code: String((_c = detail.code) !== null && _c !== void 0 ? _c : "NATIVE_COMPONENT_COMMAND_FAILED"),
|
|
14
|
+
message: String((_e = (_d = detail.message) !== null && _d !== void 0 ? _d : detail.errMsg) !== null && _e !== void 0 ? _e : "Native video failed"),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return detail;
|
|
18
|
+
}
|