@lingxia/page-runtime 0.5.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.
Files changed (54) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +1 -0
  3. package/dist/react/LxInput.d.ts +47 -0
  4. package/dist/react/LxInput.js +128 -0
  5. package/dist/react/LxNavigator.d.ts +23 -0
  6. package/dist/react/LxNavigator.js +61 -0
  7. package/dist/react/LxPicker.d.ts +27 -0
  8. package/dist/react/LxPicker.js +113 -0
  9. package/dist/react/LxTextarea.d.ts +47 -0
  10. package/dist/react/LxTextarea.js +118 -0
  11. package/dist/react/LxVideo.d.ts +5 -0
  12. package/dist/react/LxVideo.js +113 -0
  13. package/dist/react/hook.d.ts +39 -0
  14. package/dist/react/hook.js +212 -0
  15. package/dist/react/index.d.ts +6 -0
  16. package/dist/react/index.js +6 -0
  17. package/dist/react/text_component_shared.d.ts +5 -0
  18. package/dist/react/text_component_shared.js +33 -0
  19. package/dist/shared/runtime.d.ts +21 -0
  20. package/dist/shared/runtime.js +142 -0
  21. package/dist/vue/LxInput.vue +92 -0
  22. package/dist/vue/LxNavigator.vue +79 -0
  23. package/dist/vue/LxPicker.vue +144 -0
  24. package/dist/vue/LxTextarea.vue +90 -0
  25. package/dist/vue/LxVideo.vue +111 -0
  26. package/dist/vue/hook.d.ts +42 -0
  27. package/dist/vue/hook.js +194 -0
  28. package/dist/vue/index.d.ts +7 -0
  29. package/dist/vue/index.js +6 -0
  30. package/dist/vue/text_component_shared.d.ts +3 -0
  31. package/dist/vue/text_component_shared.js +26 -0
  32. package/dist/vue/types.d.ts +101 -0
  33. package/dist/vue/types.js +1 -0
  34. package/package.json +67 -0
  35. package/src/index.ts +21 -0
  36. package/src/react/LxInput.tsx +224 -0
  37. package/src/react/LxNavigator.tsx +132 -0
  38. package/src/react/LxPicker.tsx +205 -0
  39. package/src/react/LxTextarea.tsx +216 -0
  40. package/src/react/LxVideo.tsx +166 -0
  41. package/src/react/hook.ts +317 -0
  42. package/src/react/index.ts +14 -0
  43. package/src/react/text_component_shared.ts +43 -0
  44. package/src/shared/runtime.ts +186 -0
  45. package/src/vue/LxInput.vue +92 -0
  46. package/src/vue/LxNavigator.vue +79 -0
  47. package/src/vue/LxPicker.vue +144 -0
  48. package/src/vue/LxTextarea.vue +90 -0
  49. package/src/vue/LxVideo.vue +111 -0
  50. package/src/vue/hook.ts +313 -0
  51. package/src/vue/index.ts +22 -0
  52. package/src/vue/shims-vue.d.ts +5 -0
  53. package/src/vue/text_component_shared.ts +33 -0
  54. package/src/vue/types.ts +107 -0
@@ -0,0 +1,113 @@
1
+ import React, { forwardRef, useCallback, useEffect, useId, useMemo, useRef } from 'react';
2
+ import { registerVideoComponent } from '@lingxia/elements';
3
+ import { buildVideoNativeAttrs, VIDEO_DOM_EVENT_MAP, } from '@lingxia/elements';
4
+ import { assignForwardedRef, bindElementEvents, unbindElementEvents, } from './text_component_shared.js';
5
+ if (typeof window !== "undefined") {
6
+ registerVideoComponent();
7
+ }
8
+ export const LxVideo = forwardRef(({ id, src, poster, objectFit, rotate, autoplay, loop, muted, controls, progressBar, live, volume, qualities, playbackRates, onPlayRequest, onPlay, onPlaying, onPause, onStop, onEnded, onTimeUpdate, onError, onLoadedMetadata, onFullscreenChange, onWaiting, onQualityChange, onRateChange, pageBindings, className, style, ...rest }, ref) => {
9
+ const elementRef = useRef(null);
10
+ const boundElementRef = useRef(null);
11
+ const reactId = useId();
12
+ const resolvedId = useMemo(() => {
13
+ if (id)
14
+ return id;
15
+ return `lx-video-${reactId.replace(/[:]/g, "")}`;
16
+ }, [id, reactId]);
17
+ const handlerRef = useRef({
18
+ onPlayRequest,
19
+ onPlay,
20
+ onPlaying,
21
+ onPause,
22
+ onStop,
23
+ onEnded,
24
+ onTimeUpdate,
25
+ onError,
26
+ onLoadedMetadata,
27
+ onFullscreenChange,
28
+ onWaiting,
29
+ onQualityChange,
30
+ onRateChange,
31
+ });
32
+ handlerRef.current = {
33
+ onPlayRequest,
34
+ onPlay,
35
+ onPlaying,
36
+ onPause,
37
+ onStop,
38
+ onEnded,
39
+ onTimeUpdate,
40
+ onError,
41
+ onLoadedMetadata,
42
+ onFullscreenChange,
43
+ onWaiting,
44
+ onQualityChange,
45
+ onRateChange,
46
+ };
47
+ const listenerMapRef = useRef(Object.fromEntries(Object.entries(VIDEO_DOM_EVENT_MAP).map(([propKey, eventName]) => [
48
+ eventName,
49
+ {
50
+ handleEvent: (event) => {
51
+ const handler = handlerRef.current[propKey];
52
+ if (typeof handler === "function") {
53
+ handler(event);
54
+ }
55
+ },
56
+ },
57
+ ])));
58
+ const elementRefCallback = useCallback((element) => {
59
+ boundElementRef.current = bindElementEvents(boundElementRef.current, element, listenerMapRef.current);
60
+ elementRef.current = element;
61
+ assignForwardedRef(ref, element);
62
+ }, [ref]);
63
+ useEffect(() => () => {
64
+ unbindElementEvents(boundElementRef.current, listenerMapRef.current);
65
+ boundElementRef.current = null;
66
+ elementRef.current = null;
67
+ }, []);
68
+ useEffect(() => {
69
+ const el = elementRef.current;
70
+ if (!el)
71
+ return;
72
+ if (rotate === undefined || rotate === null) {
73
+ el.removeAttribute("rotate");
74
+ }
75
+ else {
76
+ el.setAttribute("rotate", String(rotate).trim());
77
+ }
78
+ if (objectFit === undefined || objectFit === null) {
79
+ el.removeAttribute("object-fit");
80
+ }
81
+ else {
82
+ el.setAttribute("object-fit", String(objectFit).trim().toLowerCase());
83
+ }
84
+ }, [rotate, objectFit]);
85
+ // Set pageBindings property on custom element
86
+ useEffect(() => {
87
+ const el = elementRef.current;
88
+ if (el && pageBindings) {
89
+ el.pageBindings = pageBindings;
90
+ }
91
+ }, [pageBindings]);
92
+ const domProps = buildVideoNativeAttrs({
93
+ id: resolvedId,
94
+ src,
95
+ poster,
96
+ autoplay,
97
+ loop,
98
+ muted,
99
+ controls,
100
+ progressBar,
101
+ live,
102
+ volume,
103
+ qualities,
104
+ playbackRates,
105
+ }, rest);
106
+ return React.createElement('lx-video', {
107
+ ref: elementRefCallback,
108
+ className,
109
+ style,
110
+ ...domProps
111
+ });
112
+ });
113
+ LxVideo.displayName = 'LxVideo';
@@ -0,0 +1,39 @@
1
+ import type { LxChannel, LxBridgeError, LxStream } from "@lingxia/bridge";
2
+ import { type ActionMap, type ChannelIn, type ChannelOut, type MethodParams, type ParamsSource, type Snapshot, type StreamData, type StreamResult } from "../shared/runtime.js";
3
+ export declare function useLxPage<TData = Snapshot, TActions extends ActionMap = ActionMap>(): {
4
+ data: TData;
5
+ actions: TActions;
6
+ };
7
+ export interface LxStreamOptions<TData, TReduced> {
8
+ params?: unknown | (() => unknown);
9
+ manual?: boolean;
10
+ reduce?: (accumulated: TReduced, chunk: TData) => TReduced;
11
+ initial?: TReduced;
12
+ }
13
+ export interface LxStreamState<TData, TResult = unknown> {
14
+ data: TData | undefined;
15
+ result: TResult | undefined;
16
+ error: LxBridgeError | undefined;
17
+ streaming: boolean;
18
+ cancel: () => void;
19
+ start: () => void;
20
+ }
21
+ export declare function useLxStream<TMethod extends (...args: any[]) => LxStream<any, any>, TReduced = StreamData<TMethod>>(method: TMethod, options?: LxStreamOptions<StreamData<TMethod>, TReduced> & {
22
+ params?: ParamsSource<MethodParams<TMethod>>;
23
+ }): LxStreamState<TReduced extends StreamData<TMethod> ? StreamData<TMethod> : TReduced, StreamResult<TMethod>>;
24
+ export interface LxChannelOptions {
25
+ params?: unknown | (() => unknown);
26
+ manual?: boolean;
27
+ }
28
+ export interface LxChannelState<TData, TOut = TData> {
29
+ last: TData | undefined;
30
+ error: LxBridgeError | undefined;
31
+ connecting: boolean;
32
+ connected: boolean;
33
+ send: (payload: TOut) => void;
34
+ close: (code?: string, reason?: string) => void;
35
+ reopen: () => void;
36
+ }
37
+ export declare function useLxChannel<TMethod extends (...args: any[]) => Promise<LxChannel<any, any>>>(method: TMethod, options?: LxChannelOptions & {
38
+ params?: ParamsSource<MethodParams<TMethod>>;
39
+ }): LxChannelState<ChannelIn<TMethod>, ChannelOut<TMethod>>;
@@ -0,0 +1,212 @@
1
+ import * as React from "react";
2
+ import { ensurePageBridgeSubscription, getMethodKey, getPageActions, getPageSnapshot, invokeMethod, resolveParams, stableParamKey, subscribePageSnapshot, toBridgeError, } from "../shared/runtime.js";
3
+ export function useLxPage() {
4
+ ensurePageBridgeSubscription();
5
+ const [, setVersion] = React.useState(0);
6
+ React.useEffect(() => {
7
+ ensurePageBridgeSubscription();
8
+ const listener = () => setVersion((v) => v + 1);
9
+ const unsubscribe = subscribePageSnapshot(listener);
10
+ setVersion((v) => v + 1);
11
+ return unsubscribe;
12
+ }, []);
13
+ const actions = React.useMemo(() => getPageActions(), []);
14
+ return { data: getPageSnapshot(), actions };
15
+ }
16
+ export function useLxStream(method, options) {
17
+ var _a;
18
+ const [state, setState] = React.useState({
19
+ data: ((options === null || options === void 0 ? void 0 : options.reduce) ? options.initial : undefined),
20
+ result: undefined,
21
+ error: undefined,
22
+ streaming: false,
23
+ });
24
+ const handleRef = React.useRef(null);
25
+ const accRef = React.useRef(options === null || options === void 0 ? void 0 : options.initial);
26
+ const optionsRef = React.useRef(options);
27
+ const methodRef = React.useRef(method);
28
+ const paramsRef = React.useRef(resolveParams(options === null || options === void 0 ? void 0 : options.params));
29
+ const runIdRef = React.useRef(0);
30
+ const resolvedParams = resolveParams(options === null || options === void 0 ? void 0 : options.params);
31
+ const paramsKey = (options === null || options === void 0 ? void 0 : options.manual) ? "" : stableParamKey(resolvedParams);
32
+ const methodDep = (_a = getMethodKey(method)) !== null && _a !== void 0 ? _a : method;
33
+ optionsRef.current = options;
34
+ methodRef.current = method;
35
+ paramsRef.current = resolvedParams;
36
+ const cancel = React.useCallback(() => {
37
+ var _a;
38
+ runIdRef.current += 1;
39
+ (_a = handleRef.current) === null || _a === void 0 ? void 0 : _a.cancel();
40
+ handleRef.current = null;
41
+ setState((prev) => ({ ...prev, streaming: false }));
42
+ }, []);
43
+ const start = React.useCallback(() => {
44
+ var _a;
45
+ (_a = handleRef.current) === null || _a === void 0 ? void 0 : _a.cancel();
46
+ const runId = runIdRef.current + 1;
47
+ runIdRef.current = runId;
48
+ const opts = optionsRef.current;
49
+ accRef.current = opts === null || opts === void 0 ? void 0 : opts.initial;
50
+ setState({
51
+ data: ((opts === null || opts === void 0 ? void 0 : opts.reduce) ? opts.initial : undefined),
52
+ result: undefined,
53
+ error: undefined,
54
+ streaming: true,
55
+ });
56
+ let handle;
57
+ try {
58
+ handle = invokeMethod(methodRef.current, paramsRef.current);
59
+ }
60
+ catch (err) {
61
+ if (runIdRef.current !== runId)
62
+ return;
63
+ handleRef.current = null;
64
+ setState((prev) => ({
65
+ ...prev,
66
+ error: toBridgeError(err),
67
+ streaming: false,
68
+ }));
69
+ return;
70
+ }
71
+ handleRef.current = handle;
72
+ handle.on("data", (chunk) => {
73
+ if (runIdRef.current !== runId)
74
+ return;
75
+ const currentOpts = optionsRef.current;
76
+ if (currentOpts === null || currentOpts === void 0 ? void 0 : currentOpts.reduce) {
77
+ accRef.current = currentOpts.reduce(accRef.current, chunk);
78
+ setState((prev) => ({
79
+ ...prev,
80
+ data: accRef.current,
81
+ }));
82
+ }
83
+ else {
84
+ setState((prev) => ({
85
+ ...prev,
86
+ data: chunk,
87
+ }));
88
+ }
89
+ });
90
+ handle.on("end", (result) => {
91
+ if (runIdRef.current !== runId)
92
+ return;
93
+ handleRef.current = null;
94
+ setState((prev) => ({
95
+ ...prev,
96
+ result,
97
+ streaming: false,
98
+ }));
99
+ });
100
+ handle.on("error", (err) => {
101
+ if (runIdRef.current !== runId)
102
+ return;
103
+ handleRef.current = null;
104
+ setState((prev) => ({
105
+ ...prev,
106
+ error: err,
107
+ streaming: false,
108
+ }));
109
+ });
110
+ }, []);
111
+ React.useEffect(() => {
112
+ if (!(options === null || options === void 0 ? void 0 : options.manual)) {
113
+ start();
114
+ }
115
+ return () => {
116
+ var _a;
117
+ runIdRef.current += 1;
118
+ (_a = handleRef.current) === null || _a === void 0 ? void 0 : _a.cancel();
119
+ handleRef.current = null;
120
+ };
121
+ }, [methodDep, options === null || options === void 0 ? void 0 : options.manual, paramsKey, start]);
122
+ return { ...state, cancel, start };
123
+ }
124
+ export function useLxChannel(method, options) {
125
+ var _a;
126
+ const [state, setState] = React.useState({
127
+ last: undefined,
128
+ error: undefined,
129
+ connecting: false,
130
+ connected: false,
131
+ });
132
+ const chRef = React.useRef(null);
133
+ const methodRef = React.useRef(method);
134
+ const paramsRef = React.useRef(resolveParams(options === null || options === void 0 ? void 0 : options.params));
135
+ const runIdRef = React.useRef(0);
136
+ const resolvedParams = resolveParams(options === null || options === void 0 ? void 0 : options.params);
137
+ const paramsKey = (options === null || options === void 0 ? void 0 : options.manual) ? "" : stableParamKey(resolvedParams);
138
+ const methodDep = (_a = getMethodKey(method)) !== null && _a !== void 0 ? _a : method;
139
+ methodRef.current = method;
140
+ paramsRef.current = resolvedParams;
141
+ const send = React.useCallback((payload) => {
142
+ var _a;
143
+ (_a = chRef.current) === null || _a === void 0 ? void 0 : _a.send(payload);
144
+ }, []);
145
+ const close = React.useCallback((code, reason) => {
146
+ var _a;
147
+ runIdRef.current += 1;
148
+ (_a = chRef.current) === null || _a === void 0 ? void 0 : _a.close(code, reason);
149
+ chRef.current = null;
150
+ setState((prev) => ({ ...prev, connecting: false, connected: false }));
151
+ }, []);
152
+ const reopen = React.useCallback(() => {
153
+ var _a;
154
+ (_a = chRef.current) === null || _a === void 0 ? void 0 : _a.close();
155
+ chRef.current = null;
156
+ const runId = ++runIdRef.current;
157
+ setState({
158
+ last: undefined,
159
+ error: undefined,
160
+ connecting: true,
161
+ connected: false,
162
+ });
163
+ Promise.resolve(invokeMethod(methodRef.current, paramsRef.current))
164
+ .then((ch) => {
165
+ if (runIdRef.current !== runId) {
166
+ ch.close();
167
+ return;
168
+ }
169
+ chRef.current = ch;
170
+ setState((prev) => ({ ...prev, connecting: false, connected: true }));
171
+ ch.on("data", (payload) => {
172
+ if (runIdRef.current !== runId)
173
+ return;
174
+ setState((prev) => ({ ...prev, last: payload }));
175
+ });
176
+ ch.on("close", () => {
177
+ if (runIdRef.current !== runId)
178
+ return;
179
+ chRef.current = null;
180
+ setState((prev) => ({ ...prev, connecting: false, connected: false }));
181
+ });
182
+ ch.on("error", (err) => {
183
+ if (runIdRef.current !== runId)
184
+ return;
185
+ chRef.current = null;
186
+ setState((prev) => ({ ...prev, error: err, connecting: false, connected: false }));
187
+ });
188
+ })
189
+ .catch((err) => {
190
+ if (runIdRef.current !== runId)
191
+ return;
192
+ setState({
193
+ last: undefined,
194
+ error: toBridgeError(err),
195
+ connecting: false,
196
+ connected: false,
197
+ });
198
+ });
199
+ }, []);
200
+ React.useEffect(() => {
201
+ if (!(options === null || options === void 0 ? void 0 : options.manual)) {
202
+ reopen();
203
+ }
204
+ return () => {
205
+ var _a;
206
+ runIdRef.current += 1;
207
+ (_a = chRef.current) === null || _a === void 0 ? void 0 : _a.close();
208
+ chRef.current = null;
209
+ };
210
+ }, [methodDep, options === null || options === void 0 ? void 0 : options.manual, paramsKey, reopen]);
211
+ return { ...state, send, close, reopen };
212
+ }
@@ -0,0 +1,6 @@
1
+ export { useLxPage, useLxStream, useLxChannel, type LxStreamOptions, type LxStreamState, type LxChannelOptions, type LxChannelState, } from "./hook.js";
2
+ export { LxVideo, type LxVideoProps } from "./LxVideo.js";
3
+ export { LxPicker, type LxPickerProps } from "./LxPicker.js";
4
+ export { LxNavigator, type LxNavigatorProps } from "./LxNavigator.js";
5
+ export { LxInput, type LxInputProps } from "./LxInput.js";
6
+ export { LxTextarea, type LxTextareaProps } from "./LxTextarea.js";
@@ -0,0 +1,6 @@
1
+ export { useLxPage, useLxStream, useLxChannel, } from "./hook.js";
2
+ export { LxVideo } from "./LxVideo.js";
3
+ export { LxPicker } from "./LxPicker.js";
4
+ export { LxNavigator } from "./LxNavigator.js";
5
+ export { LxInput } from "./LxInput.js";
6
+ export { LxTextarea } from "./LxTextarea.js";
@@ -0,0 +1,5 @@
1
+ import type { ForwardedRef } from "react";
2
+ export declare function getCustomEventDetail<T>(event: Event): T;
3
+ export declare function assignForwardedRef<T>(ref: ForwardedRef<T>, value: T | null): void;
4
+ export declare function bindElementEvents(boundElement: HTMLElement | null, nextElement: HTMLElement | null, listeners: Record<string, EventListenerObject>): HTMLElement | null;
5
+ export declare function unbindElementEvents(boundElement: HTMLElement | null, listeners: Record<string, EventListenerObject>): void;
@@ -0,0 +1,33 @@
1
+ export function getCustomEventDetail(event) {
2
+ var _a;
3
+ return ((_a = event.detail) !== null && _a !== void 0 ? _a : {});
4
+ }
5
+ export function assignForwardedRef(ref, value) {
6
+ if (typeof ref === "function") {
7
+ ref(value);
8
+ return;
9
+ }
10
+ if (ref) {
11
+ ref.current = value;
12
+ }
13
+ }
14
+ export function bindElementEvents(boundElement, nextElement, listeners) {
15
+ if (boundElement && boundElement !== nextElement) {
16
+ unbindElementEvents(boundElement, listeners);
17
+ boundElement = null;
18
+ }
19
+ if (nextElement && boundElement !== nextElement) {
20
+ for (const [event, listener] of Object.entries(listeners)) {
21
+ nextElement.addEventListener(event, listener);
22
+ }
23
+ boundElement = nextElement;
24
+ }
25
+ return boundElement;
26
+ }
27
+ export function unbindElementEvents(boundElement, listeners) {
28
+ if (!boundElement)
29
+ return;
30
+ for (const [event, listener] of Object.entries(listeners)) {
31
+ boundElement.removeEventListener(event, listener);
32
+ }
33
+ }
@@ -0,0 +1,21 @@
1
+ import type { DataSubscriber, LxBridgeError, LxChannel, LxStream, StateInfo } from "@lingxia/bridge";
2
+ export type ActionMap = Record<string, (...args: unknown[]) => unknown>;
3
+ export type Snapshot = Record<string, unknown>;
4
+ export type Listener = () => void;
5
+ export type ParamsSource<T> = T | (() => T);
6
+ export type MethodParams<TMethod> = TMethod extends () => any ? undefined : TMethod extends (params: infer P) => any ? P : never;
7
+ export type StreamData<TMethod> = TMethod extends (...args: any[]) => LxStream<infer TData, any> ? TData : never;
8
+ export type StreamResult<TMethod> = TMethod extends (...args: any[]) => LxStream<any, infer TResult> ? TResult : never;
9
+ export type ChannelIn<TMethod> = TMethod extends (...args: any[]) => Promise<LxChannel<infer TIn, any>> ? TIn : never;
10
+ export type ChannelOut<TMethod> = TMethod extends (...args: any[]) => Promise<LxChannel<any, infer TOut>> ? TOut : never;
11
+ export declare function toBridgeError(err: unknown): LxBridgeError;
12
+ export declare function resolveParams<T>(source?: ParamsSource<T>): T | undefined;
13
+ export declare function stableParamKey(value: unknown): string;
14
+ export declare function invokeMethod<TMethod extends (...args: any[]) => any>(method: TMethod, params: unknown): ReturnType<TMethod>;
15
+ export declare function getMethodKey(method: unknown): string | undefined;
16
+ export declare function ensurePageBridgeSubscription(): void;
17
+ export declare function subscribePageSnapshot(listener: Listener): () => void;
18
+ export declare function subscribePageData(callback: DataSubscriber): () => void;
19
+ export declare function getPageSnapshot<TData = Snapshot>(): TData;
20
+ export declare function getPageActions<TActions extends ActionMap>(): TActions;
21
+ export declare function getPageStateInfo(): StateInfo;
@@ -0,0 +1,142 @@
1
+ let snapshot = {};
2
+ let stateInfo = { rev: -1, initial: true };
3
+ let subscribed = false;
4
+ let subscribeRetryTimer = null;
5
+ let initialSnapshotResolved = false;
6
+ let snapshotRequestInFlight = false;
7
+ const listeners = new Set();
8
+ export function toBridgeError(err) {
9
+ return err && typeof err === "object" && "code" in err
10
+ ? err
11
+ : {
12
+ code: "BRIDGE_INTERNAL_ERROR",
13
+ message: err instanceof Error ? err.message : String(err),
14
+ };
15
+ }
16
+ export function resolveParams(source) {
17
+ if (typeof source === "function") {
18
+ return source();
19
+ }
20
+ return source;
21
+ }
22
+ export function stableParamKey(value) {
23
+ var _a;
24
+ if (value === undefined)
25
+ return "undefined";
26
+ try {
27
+ return (_a = JSON.stringify(value)) !== null && _a !== void 0 ? _a : "undefined";
28
+ }
29
+ catch {
30
+ return String(value);
31
+ }
32
+ }
33
+ export function invokeMethod(method, params) {
34
+ if (params === undefined) {
35
+ return method();
36
+ }
37
+ return method(params);
38
+ }
39
+ export function getMethodKey(method) {
40
+ if (typeof method !== "function")
41
+ return undefined;
42
+ const candidate = method.__funcName;
43
+ return typeof candidate === "string" && candidate !== "" ? candidate : undefined;
44
+ }
45
+ function notifyListeners() {
46
+ listeners.forEach((listener) => {
47
+ try {
48
+ listener();
49
+ }
50
+ catch {
51
+ // Ignore listener errors to avoid breaking state fanout.
52
+ }
53
+ });
54
+ }
55
+ function updateSnapshot(next, info) {
56
+ snapshot = next && typeof next === "object" ? next : {};
57
+ stateInfo = info;
58
+ notifyListeners();
59
+ }
60
+ function scheduleSubscribeRetry() {
61
+ if (subscribeRetryTimer !== null || subscribed)
62
+ return;
63
+ subscribeRetryTimer = setTimeout(() => {
64
+ subscribeRetryTimer = null;
65
+ ensurePageBridgeSubscription();
66
+ }, 10);
67
+ }
68
+ function requestInitialSnapshot(bridge) {
69
+ if (initialSnapshotResolved || snapshotRequestInFlight)
70
+ return;
71
+ if (!(bridge === null || bridge === void 0 ? void 0 : bridge.call))
72
+ return;
73
+ snapshotRequestInFlight = true;
74
+ bridge
75
+ .call("state.getSnapshot", { scope: "page" })
76
+ .then(() => {
77
+ initialSnapshotResolved = true;
78
+ })
79
+ .catch(() => {
80
+ scheduleSubscribeRetry();
81
+ })
82
+ .finally(() => {
83
+ snapshotRequestInFlight = false;
84
+ });
85
+ }
86
+ export function ensurePageBridgeSubscription() {
87
+ var _a;
88
+ if (subscribed)
89
+ return;
90
+ const bridge = window.LingXiaBridge;
91
+ const subscribeState = (_a = bridge === null || bridge === void 0 ? void 0 : bridge.state) === null || _a === void 0 ? void 0 : _a.subscribe;
92
+ if (!subscribeState) {
93
+ scheduleSubscribeRetry();
94
+ return;
95
+ }
96
+ subscribeState((next, info) => {
97
+ updateSnapshot(next, info);
98
+ });
99
+ subscribed = true;
100
+ requestInitialSnapshot(bridge);
101
+ }
102
+ export function subscribePageSnapshot(listener) {
103
+ ensurePageBridgeSubscription();
104
+ listeners.add(listener);
105
+ return () => {
106
+ listeners.delete(listener);
107
+ };
108
+ }
109
+ export function subscribePageData(callback) {
110
+ if (typeof callback !== "function")
111
+ return () => { };
112
+ ensurePageBridgeSubscription();
113
+ if (stateInfo.rev >= 0) {
114
+ callback(snapshot, { rev: stateInfo.rev, initial: true });
115
+ }
116
+ return subscribePageSnapshot(() => {
117
+ callback(snapshot, stateInfo);
118
+ });
119
+ }
120
+ export function getPageSnapshot() {
121
+ ensurePageBridgeSubscription();
122
+ return snapshot;
123
+ }
124
+ export function getPageActions() {
125
+ const actions = {};
126
+ const bridge = window.__pageBridge;
127
+ if (!(bridge === null || bridge === void 0 ? void 0 : bridge.__names)) {
128
+ return actions;
129
+ }
130
+ for (const name of bridge.__names) {
131
+ if (typeof name !== "string")
132
+ continue;
133
+ const fn = bridge[name];
134
+ if (typeof fn === "function") {
135
+ actions[name] = fn;
136
+ }
137
+ }
138
+ return actions;
139
+ }
140
+ export function getPageStateInfo() {
141
+ return stateInfo;
142
+ }
@@ -0,0 +1,92 @@
1
+ <script setup lang="ts">
2
+ import { computed, h, onBeforeUnmount, ref, useAttrs, watch } from 'vue';
3
+ import { registerInputComponent } from '@lingxia/elements';
4
+ import { buildInputNativeAttrs } from '@lingxia/elements';
5
+ import type { LxInputProps } from './types.js';
6
+ import {
7
+ bindElementEvents,
8
+ getCustomEventDetail,
9
+ unbindElementEvents,
10
+ } from './text_component_shared.js';
11
+
12
+ const props = withDefaults(defineProps<LxInputProps>(), {
13
+ type: 'text',
14
+ disabled: false,
15
+ confirmType: 'done',
16
+ });
17
+ const attrs = useAttrs();
18
+
19
+ const emit = defineEmits<{
20
+ 'update:modelValue': [value: string];
21
+ input: [detail: Record<string, unknown>];
22
+ change: [detail: Record<string, unknown>];
23
+ focus: [detail: Record<string, unknown>];
24
+ blur: [detail: Record<string, unknown>];
25
+ confirm: [detail: Record<string, unknown>];
26
+ keyboardHeightChange: [detail: Record<string, unknown>];
27
+ nicknameReview: [detail: Record<string, unknown>];
28
+ }>();
29
+
30
+ if (typeof window !== 'undefined') {
31
+ registerInputComponent();
32
+ }
33
+
34
+ const elementRef = ref<HTMLElement | null>(null);
35
+ let boundElement: HTMLElement | null = null;
36
+
37
+ const inputEventListeners: Record<string, EventListenerObject> = {
38
+ input: {
39
+ handleEvent: (event: Event) => {
40
+ const detail = getCustomEventDetail<Record<string, unknown>>(event);
41
+ if (typeof detail.value === 'string') {
42
+ emit('update:modelValue', detail.value);
43
+ }
44
+ emit('input', detail);
45
+ },
46
+ },
47
+ change: {
48
+ handleEvent: (event: Event) => emit('change', getCustomEventDetail<Record<string, unknown>>(event)),
49
+ },
50
+ focus: {
51
+ handleEvent: (event: Event) => emit('focus', getCustomEventDetail<Record<string, unknown>>(event)),
52
+ },
53
+ blur: {
54
+ handleEvent: (event: Event) => emit('blur', getCustomEventDetail<Record<string, unknown>>(event)),
55
+ },
56
+ confirm: {
57
+ handleEvent: (event: Event) => emit('confirm', getCustomEventDetail<Record<string, unknown>>(event)),
58
+ },
59
+ keyboardheightchange: {
60
+ handleEvent: (event: Event) => emit('keyboardHeightChange', getCustomEventDetail<Record<string, unknown>>(event)),
61
+ },
62
+ nicknamereview: {
63
+ handleEvent: (event: Event) => emit('nicknameReview', getCustomEventDetail<Record<string, unknown>>(event)),
64
+ },
65
+ };
66
+
67
+ watch(elementRef, (element) => {
68
+ boundElement = bindElementEvents(boundElement, element, inputEventListeners);
69
+ });
70
+
71
+ onBeforeUnmount(() => {
72
+ unbindElementEvents(boundElement, inputEventListeners);
73
+ });
74
+
75
+ const inputProps = computed<Record<string, unknown>>(() => {
76
+ const result: Record<string, unknown> = buildInputNativeAttrs({
77
+ ...props,
78
+ id: props.id ?? (typeof attrs.id === 'string' ? attrs.id : undefined),
79
+ }, attrs as Record<string, unknown>, elementRef.value !== null);
80
+ result.class = props.class ?? attrs.class;
81
+ result.style = props.style ?? attrs.style;
82
+ return result;
83
+ });
84
+
85
+ defineExpose({ el: elementRef });
86
+
87
+ const renderInput = () => h('lx-input', { ref: elementRef, ...inputProps.value });
88
+ </script>
89
+
90
+ <template>
91
+ <renderInput />
92
+ </template>