@lingxia/elements 0.17.0 → 0.19.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 CHANGED
@@ -1,7 +1,7 @@
1
- export { registerVideoComponent, LxVideoElement, type LxVideoAttributes, type LxVideoEventPayloads, type LxVideoEventHandlers, } from "./video.js";
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
- export { registerPickerComponent, LxPickerElement, type LxPickerAttributes, type LxPickerColumn, type LxPickerCascadingColumns, type LxPickerEvent, type LxPickerEventDetail, } from "./picker.js";
4
- export { LxNavigatorElement, type LxNavigatorAttributes, type LxNavigatorEvent, type NavigatorChannel, type NavigatorOpenType, type NavigatorQuery, type NavigatorQueryValue, type NavigatorTarget, } from "./navigator.js";
1
+ export { registerVideoComponent, LxVideoElement, type LxVideoAttributes, type LxVideoEventPayloads, type LxVideoEventHandlers, type LxVideoQuality, } from "./video.js";
2
+ export { registerMediaSwiperComponent, LxMediaSwiperElement, type LxMediaSwiperAttributes, type LxMediaSwiperItem, type LxMediaSwiperEventPayloads, type LxMediaSwiperEventHandlers, type LxMediaSwiperHandle, 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
+ export { registerPickerComponent, LxPickerElement, type LxPickerAttributes, type LxPickerColumn, type LxPickerCascadingColumns, type LxPickerColumns, type LxPickerEvent, type LxPickerEventDetail, } from "./picker.js";
4
+ export { LxNavigatorElement, type LxNavigatorAttributes, type LxNavigatorEvent, type NavigatorChannel, type NavigatorEdge, type NavigatorFloatPosition, type NavigatorInteraction, type NavigatorPagePlacement, type NavigatorPlacement, type NavigatorSize, type NavigatorSizeValue, type NavigatorUrlPlacement, type NavigatorWindowChrome, type NavigatorOpenType, type NavigatorQuery, type NavigatorQueryValue, type NavigatorTarget, } from "./navigator.js";
5
5
  export { buildVideoNativeAttrs, buildMediaSwiperNativeAttrs, buildNavigatorNativeAttrs, buildPickerNativeAttrs, getPickerDisplayText, getPickerValueFromIndex, VIDEO_DOM_EVENT_MAP, MEDIA_SWIPER_DOM_EVENT_MAP, } from "./native_component_wrapper_shared.js";
6
6
  export { isHarmony, isIOS, isAndroid, isMacOS, isDesktop, getOS, } from "./platform.js";
7
7
  export { ensureComponentId, rectEquals, NativeComponentUpdateState, type Rect, } from "./component.js";
@@ -569,7 +569,6 @@ export function isFallbackElement(element) {
569
569
  }
570
570
  function readElementProps(element) {
571
571
  const props = {};
572
- const dataset = {};
573
572
  for (const attr of Array.from(element.attributes)) {
574
573
  if (attr.name === "data-lx-native-measuring" || attr.name === "data-lx-native-paint-hidden")
575
574
  continue;
@@ -578,11 +577,6 @@ function readElementProps(element) {
578
577
  continue;
579
578
  }
580
579
  props[camelize(name)] = attr.value;
581
- if (name.startsWith("data-") && name !== "data-lx-component-id") {
582
- const key = camelize(name.slice(5));
583
- if (key)
584
- dataset[key] = attr.value;
585
- }
586
580
  if (name.startsWith("aria-")) {
587
581
  props[name] = attr.value;
588
582
  }
@@ -614,8 +608,6 @@ function readElementProps(element) {
614
608
  "muted",
615
609
  "volume",
616
610
  "progressBar",
617
- "pageFuncBindings",
618
- "pageFuncBindingsJson",
619
611
  "intent",
620
612
  "emphasis",
621
613
  "size",
@@ -638,10 +630,6 @@ function readElementProps(element) {
638
630
  props[key] = anyEl[key];
639
631
  }
640
632
  }
641
- if (Object.keys(dataset).length > 0) {
642
- props.dataset = dataset;
643
- props.datasetJson = JSON.stringify(dataset);
644
- }
645
633
  const ariaLabel = element.getAttribute("aria-label");
646
634
  if (ariaLabel) {
647
635
  props["aria-label"] = ariaLabel;
@@ -1,4 +1,3 @@
1
- type LxMediaSwiperEventHandler = (e: Event) => void;
2
1
  export type LxMediaSwiperItem = {
3
2
  id?: string;
4
3
  type: "image";
@@ -40,6 +39,25 @@ export type LxMediaSwiperTransitionEndEvent = CustomEvent<LxMediaSwiperTransitio
40
39
  export type LxMediaSwiperItemEvent = CustomEvent<LxMediaSwiperEventDetail>;
41
40
  export type LxMediaSwiperEndReachedEvent = CustomEvent<LxMediaSwiperEndReachedEventDetail>;
42
41
  export type LxMediaSwiperErrorEvent = CustomEvent<LxMediaSwiperErrorEventDetail>;
42
+ /** What each framework callback receives: the event's `detail`. */
43
+ export interface LxMediaSwiperEventPayloads {
44
+ onChange: LxMediaSwiperChangeEventDetail;
45
+ onTransitionEnd: LxMediaSwiperTransitionEndEventDetail;
46
+ onEndReached: LxMediaSwiperEndReachedEventDetail;
47
+ onTap: LxMediaSwiperEventDetail;
48
+ onVideoEnded: LxMediaSwiperEventDetail;
49
+ onError: LxMediaSwiperErrorEventDetail;
50
+ }
51
+ /** The framework callbacks: each receives its event's payload. */
52
+ export type LxMediaSwiperEventHandlers = {
53
+ [K in keyof LxMediaSwiperEventPayloads]?: (payload: LxMediaSwiperEventPayloads[K]) => void;
54
+ };
55
+ /** Imperative control of a mounted swiper (React ref, Vue template ref). */
56
+ export interface LxMediaSwiperHandle {
57
+ next(): void;
58
+ previous(): void;
59
+ goToIndex(index: number): void;
60
+ }
43
61
  /**
44
62
  * Peek leaves part of the previous and next pages visible alongside the current
45
63
  * page so users get a hint that more content exists. A single `number` applies
@@ -57,7 +75,7 @@ export type LxMediaSwiperPeek = number | {
57
75
  };
58
76
  export type LxMediaSwiperAttributes = {
59
77
  id?: string;
60
- items?: LxMediaSwiperItem[];
78
+ items?: readonly LxMediaSwiperItem[];
61
79
  index?: number;
62
80
  initialIndex?: number;
63
81
  loop?: boolean;
@@ -79,13 +97,12 @@ export type LxMediaSwiperAttributes = {
79
97
  className?: string;
80
98
  style?: any;
81
99
  ref?: any;
82
- onChange?: LxMediaSwiperEventHandler;
83
- onTransitionEnd?: LxMediaSwiperEventHandler;
84
- onEndReached?: LxMediaSwiperEventHandler;
85
- onTap?: LxMediaSwiperEventHandler;
86
- onVideoEnded?: LxMediaSwiperEventHandler;
87
- onError?: LxMediaSwiperEventHandler;
88
- pageBindings?: Record<string, string>;
100
+ onChange?: (event: LxMediaSwiperChangeEvent) => void;
101
+ onTransitionEnd?: (event: LxMediaSwiperTransitionEndEvent) => void;
102
+ onEndReached?: (event: LxMediaSwiperEndReachedEvent) => void;
103
+ onTap?: (event: LxMediaSwiperItemEvent) => void;
104
+ onVideoEnded?: (event: LxMediaSwiperItemEvent) => void;
105
+ onError?: (event: LxMediaSwiperErrorEvent) => void;
89
106
  };
90
107
  declare global {
91
108
  namespace JSX {
@@ -94,7 +111,7 @@ declare global {
94
111
  }
95
112
  }
96
113
  }
97
- export declare class LxMediaSwiperElement extends HTMLElement {
114
+ export declare class LxMediaSwiperElement extends HTMLElement implements LxMediaSwiperHandle {
98
115
  static get observedAttributes(): string[];
99
116
  private componentId;
100
117
  private mounted;
@@ -107,7 +124,6 @@ export declare class LxMediaSwiperElement extends HTMLElement {
107
124
  private iOSHelper?;
108
125
  private rawHandlers;
109
126
  private handlers;
110
- private _pageBindings;
111
127
  private harmonyCommandSeq;
112
128
  private harmonyPendingCommand;
113
129
  private pendingHarmonyProps?;
@@ -115,7 +131,7 @@ export declare class LxMediaSwiperElement extends HTMLElement {
115
131
  private pendingHarmonyRetryCount;
116
132
  private harmonyEmbed?;
117
133
  private lastHarmonyProps?;
118
- set items(value: LxMediaSwiperItem[] | string | null | undefined);
134
+ set items(value: readonly LxMediaSwiperItem[] | string | null | undefined);
119
135
  get items(): LxMediaSwiperItem[];
120
136
  set index(value: number | null | undefined);
121
137
  get index(): number | undefined;
@@ -123,10 +139,6 @@ export declare class LxMediaSwiperElement extends HTMLElement {
123
139
  get initialIndex(): number | undefined;
124
140
  set contentRotate(value: unknown);
125
141
  get contentRotate(): 0 | 90 | 180 | 270 | undefined;
126
- set pageBindings(bindings: Record<string, string>);
127
- get pageBindings(): Record<string, string>;
128
- set pageFuncBindings(bindings: Record<string, string>);
129
- setPageBindings(bindings: Record<string, string>): void;
130
142
  next(): void;
131
143
  previous(): void;
132
144
  goToIndex(index: number): void;
@@ -176,7 +188,5 @@ export declare class LxMediaSwiperElement extends HTMLElement {
176
188
  * to the zero-peek (full-width page) layout.
177
189
  */
178
190
  private parsePeek;
179
- private collectDataset;
180
191
  }
181
192
  export declare function registerMediaSwiperComponent(): void;
182
- export {};
@@ -19,7 +19,6 @@ export class LxMediaSwiperElement extends HTMLElement {
19
19
  this.boundUpdatePosition = this.updatePosition.bind(this);
20
20
  this.rawHandlers = {};
21
21
  this.handlers = {};
22
- this._pageBindings = {};
23
22
  // Monotonic command counter used as the Harmony bridge fallback. Harmony's
24
23
  // Web.onNativeEmbedDataInfo channel only delivers attribute-derived props,
25
24
  // so imperative API calls have to piggyback into a prop the Builder polls.
@@ -99,20 +98,6 @@ export class LxMediaSwiperElement extends HTMLElement {
99
98
  get contentRotate() {
100
99
  return this.parseRotate(this.getAttribute("content-rotate"));
101
100
  }
102
- set pageBindings(bindings) {
103
- this.setPageBindings(bindings);
104
- }
105
- get pageBindings() {
106
- return this._pageBindings;
107
- }
108
- set pageFuncBindings(bindings) {
109
- this.setPageBindings(bindings);
110
- }
111
- setPageBindings(bindings) {
112
- this._pageBindings = bindings !== null && bindings !== void 0 ? bindings : {};
113
- if (this.isConnected)
114
- this.mountOrUpdate(true);
115
- }
116
101
  next() {
117
102
  this.sendCommand("next");
118
103
  }
@@ -129,8 +114,6 @@ export class LxMediaSwiperElement extends HTMLElement {
129
114
  this.upgradeProperty("index");
130
115
  this.upgradeProperty("initialIndex");
131
116
  this.upgradeProperty("contentRotate");
132
- this.upgradeProperty("pageBindings");
133
- this.upgradeProperty("pageFuncBindings");
134
117
  for (const prop of Object.keys(EVENT_MAP))
135
118
  this.upgradeProperty(prop);
136
119
  this.componentId = ensureComponentId(this, "lx-media-swiper", this.componentId);
@@ -228,7 +211,6 @@ export class LxMediaSwiperElement extends HTMLElement {
228
211
  var _a, _b, _c, _d, _e, _f;
229
212
  const objectFit = this.parseObjectFit(this.getAttribute("object-fit"));
230
213
  const dots = this.parseDots();
231
- const dataset = this.collectDataset();
232
214
  // Always emit a normalised peek object so removing the attribute (or setting it
233
215
  // to 0) propagates as an explicit reset. JSON.stringify would drop `undefined`,
234
216
  // which makes native think peek hasn't changed and keep the old padding.
@@ -250,10 +232,6 @@ export class LxMediaSwiperElement extends HTMLElement {
250
232
  dots,
251
233
  swipeEnabled: this.getAttribute("swipe-enabled") !== "false",
252
234
  peek,
253
- pageFuncBindings: this._pageBindings,
254
- pageFuncBindingsJson: JSON.stringify(this._pageBindings),
255
- dataset,
256
- datasetJson: JSON.stringify(dataset),
257
235
  };
258
236
  return props;
259
237
  }
@@ -675,18 +653,6 @@ export class LxMediaSwiperElement extends HTMLElement {
675
653
  return undefined;
676
654
  }
677
655
  }
678
- collectDataset() {
679
- const dataset = {};
680
- for (const attr of this.getAttributeNames()) {
681
- if (!attr.startsWith("data-"))
682
- continue;
683
- const key = attr.slice(5).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
684
- const value = this.getAttribute(attr);
685
- if (key && value != null)
686
- dataset[key] = value;
687
- }
688
- return dataset;
689
- }
690
656
  }
691
657
  export function registerMediaSwiperComponent() {
692
658
  if (!customElements.get("lx-media-swiper")) {
@@ -1,4 +1,5 @@
1
- import type { NavigatorChannel, NavigatorOpenType, NavigatorTarget } from "./navigator.js";
1
+ import type { LxPickerCascadingColumns, LxPickerColumns } from "./picker.js";
2
+ import type { NavigatorChannel, NavigatorEdge, NavigatorFloatPosition, NavigatorInteraction, NavigatorOpenType, NavigatorPlacement, NavigatorSize, NavigatorTarget, NavigatorWindowChrome } from "./navigator.js";
2
3
  import type { NavigatorQuery } from "./navigator.js";
3
4
  import type { LxMediaSwiperItem } from "./media_swiper.js";
4
5
  import type { LxVideoQuality } from "./video.js";
@@ -10,6 +11,12 @@ export interface NavigatorNativeAttrOptions {
10
11
  openType?: NavigatorOpenType;
11
12
  target?: NavigatorTarget;
12
13
  delta?: number;
14
+ as?: NavigatorPlacement;
15
+ edge?: NavigatorEdge;
16
+ position?: NavigatorFloatPosition;
17
+ chrome?: NavigatorWindowChrome;
18
+ size?: NavigatorSize;
19
+ interaction?: NavigatorInteraction;
13
20
  appId?: string;
14
21
  query?: NavigatorQuery;
15
22
  channel?: NavigatorChannel;
@@ -21,18 +28,17 @@ export interface NavigatorNativeAttrOptions {
21
28
  hoverStayTime?: number;
22
29
  }
23
30
  export declare function buildNavigatorNativeAttrs(options: NavigatorNativeAttrOptions, extraAttrs?: Record<string, unknown>): Record<string, string>;
24
- export type PickerColumns = string[][] | [string[], Record<string, string[]>];
25
- export type PickerValue = string | string[] | undefined;
31
+ export type PickerValue = string | readonly string[] | undefined;
26
32
  export type PickerFields = "year" | "month" | "day" | "range";
27
33
  export interface PickerNativeAttrOptions {
28
34
  id?: string;
29
- columns?: PickerColumns;
35
+ columns?: LxPickerColumns;
30
36
  mode?: "date" | "time";
31
37
  start?: string;
32
38
  end?: string;
33
39
  fields?: PickerFields;
34
- modelValue?: string | string[];
35
- value?: string | string[];
40
+ modelValue?: string | readonly string[];
41
+ value?: string | readonly string[];
36
42
  cancelText?: string;
37
43
  cancelTextColor?: string;
38
44
  cancelButtonColor?: string;
@@ -41,10 +47,10 @@ export interface PickerNativeAttrOptions {
41
47
  confirmButtonColor?: string;
42
48
  }
43
49
  export declare function isPickerDateMode(mode?: "date" | "time"): boolean;
44
- export declare function isPickerCascading(columns?: PickerColumns): columns is [string[], Record<string, string[]>];
45
- export declare function isPickerSingle(columns?: PickerColumns): boolean;
46
- export declare function getPickerIndexFromValue(columns: PickerColumns | undefined, value: PickerValue): number | number[];
47
- export declare function getPickerValueFromIndex(columns: PickerColumns | undefined, index: number | number[]): string | string[];
50
+ export declare function isPickerCascading(columns?: LxPickerColumns): columns is LxPickerCascadingColumns;
51
+ export declare function isPickerSingle(columns?: LxPickerColumns): boolean;
52
+ export declare function getPickerIndexFromValue(columns: LxPickerColumns | undefined, value: PickerValue): number | number[];
53
+ export declare function getPickerValueFromIndex(columns: LxPickerColumns | undefined, index: number | number[]): string | string[];
48
54
  export declare function getPickerDisplayText(value: PickerValue, fields?: PickerFields): string;
49
55
  export declare function buildPickerNativeAttrs(options: PickerNativeAttrOptions, extraAttrs?: Record<string, unknown>): Record<string, string>;
50
56
  export declare const VIDEO_DOM_EVENT_MAP: {
@@ -74,8 +80,8 @@ export interface VideoNativeAttrOptions {
74
80
  progressBar?: boolean;
75
81
  live?: boolean;
76
82
  volume?: string | number;
77
- qualities?: LxVideoQuality[];
78
- playbackRates?: number[];
83
+ qualities?: readonly LxVideoQuality[];
84
+ playbackRates?: readonly number[];
79
85
  }
80
86
  export declare function buildVideoNativeAttrs(options: VideoNativeAttrOptions, extraAttrs?: Record<string, unknown>): Record<string, string | number>;
81
87
  export declare const MEDIA_SWIPER_DOM_EVENT_MAP: {
@@ -88,7 +94,7 @@ export declare const MEDIA_SWIPER_DOM_EVENT_MAP: {
88
94
  };
89
95
  export interface MediaSwiperNativeAttrOptions {
90
96
  id?: string;
91
- items?: LxMediaSwiperItem[];
97
+ items?: readonly LxMediaSwiperItem[];
92
98
  index?: number;
93
99
  initialIndex?: number;
94
100
  loop?: boolean;
@@ -39,6 +39,18 @@ export function buildNavigatorNativeAttrs(options, extraAttrs = {}) {
39
39
  result.page = options.page;
40
40
  if (options.target)
41
41
  result.target = options.target;
42
+ if (options.as)
43
+ result.as = options.as;
44
+ if (options.edge)
45
+ result.edge = options.edge;
46
+ if (options.position)
47
+ result.position = options.position;
48
+ if (options.chrome)
49
+ result.chrome = options.chrome;
50
+ if (options.size)
51
+ result.size = JSON.stringify(options.size);
52
+ if (options.interaction)
53
+ result.interaction = JSON.stringify(options.interaction);
42
54
  if (options.appId)
43
55
  result["app-id"] = options.appId;
44
56
  if (options.query)
@@ -1,5 +1,21 @@
1
- export type NavigatorOpenType = 'navigate' | 'redirect' | 'navigateBack' | 'reLaunch' | 'switchTab' | 'exit' | 'openUrl' | 'tel';
2
- export type NavigatorTarget = 'self' | 'lxapp' | 'browser';
1
+ export type NavigatorOpenType = 'navigate' | 'redirect' | 'navigateBack' | 'reLaunch' | 'switchTab' | 'exit' | 'tel';
2
+ export type NavigatorTarget = 'page' | 'lxapp' | 'url';
3
+ export type NavigatorUrlPlacement = 'external' | 'tab' | 'aside';
4
+ export type NavigatorPagePlacement = 'float' | 'window';
5
+ export type NavigatorPlacement = NavigatorUrlPlacement | NavigatorPagePlacement;
6
+ export type NavigatorEdge = 'left' | 'right' | 'top' | 'bottom';
7
+ export type NavigatorFloatPosition = 'center' | 'top' | 'bottom' | 'left' | 'right';
8
+ export type NavigatorWindowChrome = 'system' | 'full';
9
+ export type NavigatorSizeValue = number | `${number}%`;
10
+ export type NavigatorSize = {
11
+ width?: NavigatorSizeValue;
12
+ height?: NavigatorSizeValue;
13
+ };
14
+ export type NavigatorInteraction = {
15
+ closeButton?: boolean;
16
+ dismiss?: 'tapOutside' | 'manual';
17
+ modal?: boolean;
18
+ };
3
19
  export type NavigatorQueryValue = string | number | boolean | null | undefined;
4
20
  export type NavigatorQuery = Record<string, NavigatorQueryValue>;
5
21
  export type NavigatorChannel = 'release' | 'draft';
@@ -17,6 +33,12 @@ export type LxNavigatorAttributes = {
17
33
  'open-type'?: NavigatorOpenType;
18
34
  target?: NavigatorTarget;
19
35
  delta?: number;
36
+ as?: NavigatorPlacement;
37
+ edge?: NavigatorEdge;
38
+ position?: NavigatorFloatPosition;
39
+ chrome?: NavigatorWindowChrome;
40
+ size?: string;
41
+ interaction?: string;
20
42
  'app-id'?: string;
21
43
  channel?: NavigatorChannel;
22
44
  'target-version'?: string;
@@ -75,17 +97,15 @@ export declare class LxNavigatorElement extends HTMLElement {
75
97
  private removeHoverClass;
76
98
  private clearHoverTimer;
77
99
  private handleClick;
78
- /**
79
- * Auto-infer navigation target based on context
80
- * Priority: explicit target > appId > HTTPS URL > default (self)
81
- */
82
- private inferTarget;
83
100
  private navigate;
84
101
  private dispatchSuccess;
85
102
  private resolveErrorMessage;
86
103
  private dispatchFail;
104
+ private bridge;
87
105
  private callHost;
106
+ private callSurface;
88
107
  private performNavigation;
108
+ private readJsonObject;
89
109
  private readQuery;
90
110
  private buildPageTarget;
91
111
  private buildLxAppTarget;
package/dist/navigator.js CHANGED
@@ -43,6 +43,12 @@ export class LxNavigatorElement extends HTMLElement {
43
43
  "open-type",
44
44
  "target",
45
45
  "delta",
46
+ "as",
47
+ "edge",
48
+ "position",
49
+ "chrome",
50
+ "size",
51
+ "interaction",
46
52
  "app-id",
47
53
  "channel",
48
54
  "target-version",
@@ -158,55 +164,33 @@ export class LxNavigatorElement extends HTMLElement {
158
164
  }
159
165
  }
160
166
  handleClick(e) {
167
+ var _a;
161
168
  e.preventDefault();
162
- const url = this.getAttribute('url') || '';
163
- const page = this.getAttribute('page');
164
- // Read the removed attribute only to provide a forward-only runtime error
165
- // for untyped HTML callers instead of silently ignoring it.
166
- const path = this.getAttribute('path');
167
- const query = this.getAttribute('query');
168
- const openType = (this.getAttribute('open-type') || 'navigate');
169
- const explicitTarget = this.getAttribute('target');
170
- const delta = parseInt(this.getAttribute('delta') || '1', 10);
171
- const appId = this.getAttribute('app-id');
172
- const channel = this.getAttribute('channel');
173
- const targetVersion = this.getAttribute('target-version');
174
- const phoneNumber = this.getAttribute('phone-number');
175
- // Auto-infer target if not explicitly specified
176
- const target = this.inferTarget(explicitTarget, url, appId);
177
- void this.navigate({
169
+ const attr = (name) => this.getAttribute(name);
170
+ const url = attr('url') || '';
171
+ const appId = attr('app-id');
172
+ const options = {
178
173
  url,
179
- page,
180
- path,
181
- query,
182
- openType,
183
- target,
184
- delta,
174
+ page: attr('page'),
175
+ // Read the removed attribute only to provide a forward-only runtime error
176
+ // for untyped HTML callers instead of silently ignoring it.
177
+ path: attr('path'),
178
+ query: attr('query'),
179
+ openType: (attr('open-type') || 'navigate'),
180
+ target: (_a = attr('target')) !== null && _a !== void 0 ? _a : (appId ? 'lxapp' : url ? 'url' : 'page'),
181
+ delta: parseInt(attr('delta') || '1', 10),
182
+ placement: attr('as'),
183
+ edge: attr('edge'),
184
+ position: attr('position'),
185
+ chrome: attr('chrome'),
186
+ size: attr('size'),
187
+ interaction: attr('interaction'),
185
188
  appId,
186
- channel,
187
- targetVersion,
188
- phoneNumber
189
- });
190
- }
191
- /**
192
- * Auto-infer navigation target based on context
193
- * Priority: explicit target > appId > HTTPS URL > default (self)
194
- */
195
- inferTarget(explicitTarget, url, appId) {
196
- // 1. Explicit target has highest priority
197
- if (explicitTarget) {
198
- return explicitTarget;
199
- }
200
- // 2. If appId is specified, target is another lxapp
201
- if (appId) {
202
- return 'lxapp';
203
- }
204
- // 3. If URL starts with http:// or https://, open in browser
205
- if (/^https?:\/\//i.test(url)) {
206
- return 'browser';
207
- }
208
- // 4. Default: navigate within current lxapp
209
- return 'self';
189
+ channel: attr('channel'),
190
+ targetVersion: attr('target-version'),
191
+ phoneNumber: attr('phone-number'),
192
+ };
193
+ void this.navigate(options);
210
194
  }
211
195
  async navigate(options) {
212
196
  try {
@@ -273,17 +257,24 @@ export class LxNavigatorElement extends HTMLElement {
273
257
  });
274
258
  this.dispatchEvent(completeEvent);
275
259
  }
276
- callHost(route, params) {
260
+ bridge() {
277
261
  const bridge = window.LingXiaBridge;
278
262
  if (!bridge || typeof bridge.invoke !== 'function') {
279
- return Promise.reject(new Error('LingXiaBridge is not available'));
263
+ throw new Error('LingXiaBridge is not available');
280
264
  }
281
- return bridge.invoke(route, params).then(() => undefined);
265
+ return bridge;
266
+ }
267
+ async callHost(route, params) {
268
+ await this.bridge().invoke(route, params);
269
+ }
270
+ // Placements run through Logic's lx.surface, which owns surface handles.
271
+ async callSurface(method, params) {
272
+ await this.bridge().raw.call(`surface.${method}`, params);
282
273
  }
283
274
  async performNavigation(options) {
284
- const url = options.url || '';
275
+ const { url, placement } = options;
285
276
  const delta = Number.isFinite(options.delta) && options.delta > 0 ? options.delta : 1;
286
- if (options.path !== null && options.path !== undefined) {
277
+ if (options.path !== null) {
287
278
  throw new Error('path is not supported; pass the configured page name in page');
288
279
  }
289
280
  if (options.openType === 'tel') {
@@ -300,39 +291,71 @@ export class LxNavigatorElement extends HTMLElement {
300
291
  if (options.openType === 'navigateBack') {
301
292
  if (options.target === 'lxapp') {
302
293
  await this.callHost('navigator.navigateBackApp');
303
- return;
304
294
  }
305
- if (options.target === 'browser') {
306
- throw new Error('navigateBack is not supported for browser target');
295
+ else {
296
+ await this.callHost('navigation.navigateBack', { delta });
307
297
  }
308
- await this.callHost('navigation.navigateBack', { delta });
309
298
  return;
310
299
  }
311
- if (options.target === 'browser') {
312
- if (!url) {
313
- throw new Error('openUrl requires url');
314
- }
315
- await this.callHost('device.openUrl', { url, target: 'external' });
316
- return;
317
- }
318
- if (options.target === 'lxapp') {
319
- if (!options.appId) {
320
- throw new Error('navigateToApp requires app-id');
321
- }
322
- await this.callHost('navigator.navigateToApp', this.buildLxAppTarget(options));
323
- return;
324
- }
325
- if (options.target === 'self' && /^https?:\/\//i.test(url)) {
326
- if (!url) {
327
- throw new Error('openUrl requires url');
300
+ switch (options.target) {
301
+ case 'url': {
302
+ if (!url) {
303
+ throw new Error('target url requires url');
304
+ }
305
+ if (!placement || placement === 'external') {
306
+ await this.callHost('device.openUrl', { url, target: 'external' });
307
+ return;
308
+ }
309
+ if (placement !== 'tab' && placement !== 'aside') {
310
+ throw new Error(`a url opens as external, tab, or aside; got ${placement}`);
311
+ }
312
+ const surfaceOptions = { as: placement };
313
+ if (options.edge)
314
+ surfaceOptions.edge = options.edge;
315
+ const size = this.readJsonObject('size', options.size);
316
+ if (size)
317
+ surfaceOptions.size = size;
318
+ await this.callSurface('openUrl', { url, options: surfaceOptions });
319
+ return;
328
320
  }
329
- await this.callHost('device.openUrl', { url, target: 'self' });
330
- return;
321
+ case 'lxapp':
322
+ if (!options.appId) {
323
+ throw new Error('target lxapp requires app-id');
324
+ }
325
+ if (placement) {
326
+ throw new Error('as is not supported for target lxapp');
327
+ }
328
+ await this.callHost('navigator.navigateToApp', this.buildLxAppTarget(options));
329
+ return;
330
+ case 'page':
331
+ break;
332
+ default:
333
+ throw new Error(`Unsupported target: ${options.target}`);
331
334
  }
332
335
  const target = this.buildPageTarget(options);
333
336
  if (!target) {
334
337
  throw new Error(`${options.openType} requires page`);
335
338
  }
339
+ if (placement) {
340
+ if (placement !== 'float' && placement !== 'window') {
341
+ throw new Error(`a page opens as float or window; got ${placement}`);
342
+ }
343
+ const surfaceOptions = { as: placement };
344
+ if (target.query)
345
+ surfaceOptions.query = target.query;
346
+ if (options.position)
347
+ surfaceOptions.position = options.position;
348
+ if (options.chrome)
349
+ surfaceOptions.chrome = options.chrome;
350
+ const size = this.readJsonObject('size', options.size);
351
+ if (size)
352
+ surfaceOptions.size = size;
353
+ const interaction = this.readJsonObject('interaction', options.interaction);
354
+ if (interaction)
355
+ surfaceOptions.interaction = interaction;
356
+ await this.callSurface('openPage', { page: target.page, options: surfaceOptions });
357
+ return;
358
+ }
336
359
  switch (options.openType) {
337
360
  case 'navigate':
338
361
  await this.callHost('navigation.navigateTo', target);
@@ -346,25 +369,22 @@ export class LxNavigatorElement extends HTMLElement {
346
369
  case 'reLaunch':
347
370
  await this.callHost('navigation.reLaunch', target);
348
371
  break;
349
- case 'openUrl':
350
- await this.callHost('device.openUrl', {
351
- url,
352
- target: options.target === 'self' ? 'self' : 'external'
353
- });
354
- break;
355
372
  default:
356
373
  throw new Error(`Unsupported openType: ${options.openType}`);
357
374
  }
358
375
  }
359
- readQuery(raw) {
376
+ readJsonObject(name, raw) {
360
377
  if (!raw)
361
378
  return undefined;
362
379
  const parsed = JSON.parse(raw);
363
380
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
364
- throw new Error('query must be an object');
381
+ throw new Error(`${name} must be an object`);
365
382
  }
366
383
  return parsed;
367
384
  }
385
+ readQuery(raw) {
386
+ return this.readJsonObject('query', raw);
387
+ }
368
388
  buildPageTarget(options) {
369
389
  var _a;
370
390
  const page = (_a = options.page) === null || _a === void 0 ? void 0 : _a.trim();