@lingxia/elements 0.18.0 → 0.20.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,6 +1,6 @@
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";
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
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";
@@ -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,3 +1,4 @@
1
+ import type { LxPickerCascadingColumns, LxPickerColumns } from "./picker.js";
1
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";
@@ -27,18 +28,17 @@ export interface NavigatorNativeAttrOptions {
27
28
  hoverStayTime?: number;
28
29
  }
29
30
  export declare function buildNavigatorNativeAttrs(options: NavigatorNativeAttrOptions, extraAttrs?: Record<string, unknown>): Record<string, string>;
30
- export type PickerColumns = string[][] | [string[], Record<string, string[]>];
31
- export type PickerValue = string | string[] | undefined;
31
+ export type PickerValue = string | readonly string[] | undefined;
32
32
  export type PickerFields = "year" | "month" | "day" | "range";
33
33
  export interface PickerNativeAttrOptions {
34
34
  id?: string;
35
- columns?: PickerColumns;
35
+ columns?: LxPickerColumns;
36
36
  mode?: "date" | "time";
37
37
  start?: string;
38
38
  end?: string;
39
39
  fields?: PickerFields;
40
- modelValue?: string | string[];
41
- value?: string | string[];
40
+ modelValue?: string | readonly string[];
41
+ value?: string | readonly string[];
42
42
  cancelText?: string;
43
43
  cancelTextColor?: string;
44
44
  cancelButtonColor?: string;
@@ -47,10 +47,10 @@ export interface PickerNativeAttrOptions {
47
47
  confirmButtonColor?: string;
48
48
  }
49
49
  export declare function isPickerDateMode(mode?: "date" | "time"): boolean;
50
- export declare function isPickerCascading(columns?: PickerColumns): columns is [string[], Record<string, string[]>];
51
- export declare function isPickerSingle(columns?: PickerColumns): boolean;
52
- export declare function getPickerIndexFromValue(columns: PickerColumns | undefined, value: PickerValue): number | number[];
53
- 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[];
54
54
  export declare function getPickerDisplayText(value: PickerValue, fields?: PickerFields): string;
55
55
  export declare function buildPickerNativeAttrs(options: PickerNativeAttrOptions, extraAttrs?: Record<string, unknown>): Record<string, string>;
56
56
  export declare const VIDEO_DOM_EVENT_MAP: {
@@ -80,8 +80,8 @@ export interface VideoNativeAttrOptions {
80
80
  progressBar?: boolean;
81
81
  live?: boolean;
82
82
  volume?: string | number;
83
- qualities?: LxVideoQuality[];
84
- playbackRates?: number[];
83
+ qualities?: readonly LxVideoQuality[];
84
+ playbackRates?: readonly number[];
85
85
  }
86
86
  export declare function buildVideoNativeAttrs(options: VideoNativeAttrOptions, extraAttrs?: Record<string, unknown>): Record<string, string | number>;
87
87
  export declare const MEDIA_SWIPER_DOM_EVENT_MAP: {
@@ -94,7 +94,7 @@ export declare const MEDIA_SWIPER_DOM_EVENT_MAP: {
94
94
  };
95
95
  export interface MediaSwiperNativeAttrOptions {
96
96
  id?: string;
97
- items?: LxMediaSwiperItem[];
97
+ items?: readonly LxMediaSwiperItem[];
98
98
  index?: number;
99
99
  initialIndex?: number;
100
100
  loop?: boolean;
package/dist/picker.d.ts CHANGED
@@ -1,5 +1,10 @@
1
- export type LxPickerColumn = string[];
2
- export type LxPickerCascadingColumns = [string[], Record<string, string[]>];
1
+ export type LxPickerColumn = readonly string[];
2
+ export type LxPickerCascadingColumns = readonly [
3
+ readonly string[],
4
+ Readonly<Record<string, readonly string[]>>
5
+ ];
6
+ /** Selector or multi-column columns, or cascading keys and their options. */
7
+ export type LxPickerColumns = readonly LxPickerColumn[] | LxPickerCascadingColumns;
3
8
  type LxPickerViewEventHandler = (e: LxPickerEvent) => void;
4
9
  export interface LxPickerEventDetail {
5
10
  index?: number | number[];
@@ -13,7 +18,7 @@ export interface LxPickerEvent extends CustomEvent<LxPickerEventDetail> {
13
18
  export type LxPickerAttributes = {
14
19
  id?: string;
15
20
  mode?: 'selector' | 'multiSelector' | 'cascading' | 'date' | 'time';
16
- columns?: LxPickerColumn[] | LxPickerCascadingColumns;
21
+ columns?: LxPickerColumns;
17
22
  defaultIndex?: number | number[];
18
23
  value?: string;
19
24
  start?: string;
@@ -30,7 +35,6 @@ export type LxPickerAttributes = {
30
35
  ref?: any;
31
36
  onChange?: LxPickerViewEventHandler;
32
37
  onScroll?: LxPickerViewEventHandler;
33
- pageBindings?: Record<string, string>;
34
38
  };
35
39
  declare global {
36
40
  namespace JSX {
@@ -46,7 +50,6 @@ export declare class LxPickerElement extends HTMLElement {
46
50
  private unregister?;
47
51
  private _handlers;
48
52
  private rawHandlers;
49
- private _pageBindings;
50
53
  private harmonyEmbed?;
51
54
  private lastHarmonyProps?;
52
55
  private webCleanup?;
@@ -54,8 +57,6 @@ export declare class LxPickerElement extends HTMLElement {
54
57
  private removeLayoutInvalidationListener?;
55
58
  private readonly layoutInvalidationListener;
56
59
  private upgradeProperty;
57
- set pageBindings(bindings: Record<string, string>);
58
- get pageBindings(): Record<string, string>;
59
60
  connectedCallback(): void;
60
61
  disconnectedCallback(): void;
61
62
  attributeChangedCallback(name: string): void;
@@ -65,8 +66,6 @@ export declare class LxPickerElement extends HTMLElement {
65
66
  private shouldRefreshForAttribute;
66
67
  private startAttrObserver;
67
68
  private stopAttrObserver;
68
- private dataAttrToDatasetKey;
69
- private collectDataset;
70
69
  private mountPicker;
71
70
  set onchange(cb: EventListener | null);
72
71
  get onchange(): EventListener | null;
package/dist/picker.js CHANGED
@@ -19,7 +19,6 @@ export class LxPickerElement extends HTMLElement {
19
19
  this.mounted = false;
20
20
  this._handlers = {};
21
21
  this.rawHandlers = {};
22
- this._pageBindings = {};
23
22
  this.layoutInvalidationListener = {
24
23
  handleEvent: () => {
25
24
  if (!this.isConnected || isHarmony() || !hasNativePicker())
@@ -54,17 +53,7 @@ export class LxPickerElement extends HTMLElement {
54
53
  delete self[propName];
55
54
  self[propName] = value;
56
55
  }
57
- set pageBindings(bindings) {
58
- this._pageBindings = bindings !== null && bindings !== void 0 ? bindings : {};
59
- if (this.isConnected) {
60
- this.mountPicker();
61
- }
62
- }
63
- get pageBindings() {
64
- return this._pageBindings;
65
- }
66
56
  connectedCallback() {
67
- this.upgradeProperty("pageBindings");
68
57
  this.upgradeProperty("onchange");
69
58
  this.componentId = ensureComponentId(this, "lx-picker", this.componentId);
70
59
  if (!this.componentId)
@@ -203,46 +192,10 @@ export class LxPickerElement extends HTMLElement {
203
192
  this.attrObserver.disconnect();
204
193
  this.attrObserver = undefined;
205
194
  }
206
- dataAttrToDatasetKey(attr) {
207
- const raw = attr.slice(5).trim();
208
- if (!raw)
209
- return "";
210
- const parts = raw.split("-").filter(Boolean);
211
- if (parts.length === 0)
212
- return "";
213
- return parts
214
- .map((segment, index) => {
215
- if (index === 0)
216
- return segment.toLowerCase();
217
- return segment.charAt(0).toUpperCase() + segment.slice(1);
218
- })
219
- .join("");
220
- }
221
- collectDataset() {
222
- const dataset = {};
223
- const attrs = this.getAttributeNames();
224
- for (const attr of attrs) {
225
- if (!attr.startsWith("data-"))
226
- continue;
227
- const key = this.dataAttrToDatasetKey(attr);
228
- if (!key)
229
- continue;
230
- const value = this.getAttribute(attr);
231
- if (value == null)
232
- continue;
233
- dataset[key] = value;
234
- }
235
- return dataset;
236
- }
237
195
  async mountPicker() {
238
196
  if (!this.componentId)
239
197
  return;
240
198
  const props = this.collectProps();
241
- const dataset = this.collectDataset();
242
- props.dataset = dataset;
243
- props.datasetJson = JSON.stringify(dataset);
244
- props.pageFuncBindings = this._pageBindings;
245
- props.pageFuncBindingsJson = JSON.stringify(this._pageBindings);
246
199
  const { rect, cornerRadius } = measureElement(this);
247
200
  if (isHarmony()) {
248
201
  this.ensureHarmonyEmbed(rect, props, cornerRadius);
package/dist/video.d.ts CHANGED
@@ -69,8 +69,8 @@ export type LxVideoAttributes = {
69
69
  progressBar?: boolean;
70
70
  live?: boolean;
71
71
  volume?: string | number;
72
- qualities?: LxVideoQuality[];
73
- playbackRates?: number[];
72
+ qualities?: readonly LxVideoQuality[];
73
+ playbackRates?: readonly number[];
74
74
  className?: string;
75
75
  style?: unknown;
76
76
  ref?: unknown;
@@ -88,7 +88,6 @@ export type LxVideoAttributes = {
88
88
  onQualityChange?: (event: CustomEvent<LxVideoEventPayloads["onQualityChange"]>) => void;
89
89
  onRateChange?: (event: CustomEvent<LxVideoEventPayloads["onRateChange"]>) => void;
90
90
  onVolumeChange?: (event: CustomEvent<LxVideoEventPayloads["onVolumeChange"]>) => void;
91
- pageBindings?: Record<string, string>;
92
91
  };
93
92
  declare global {
94
93
  namespace JSX {
@@ -110,11 +109,6 @@ export declare class LxVideoElement extends HTMLElement {
110
109
  private unregister?;
111
110
  private handlers;
112
111
  private rawHandlers;
113
- private bindings;
114
- set pageBindings(bindings: Record<string, string>);
115
- get pageBindings(): Record<string, string>;
116
- get pageFuncBindings(): Record<string, string>;
117
- get pageFuncBindingsJson(): string;
118
112
  set src(value: string | null | undefined);
119
113
  get src(): string | null;
120
114
  set contentRotate(value: unknown);
package/dist/video.js CHANGED
@@ -15,7 +15,6 @@ export class LxVideoElement extends HTMLElement {
15
15
  this.islandPlaybackIdle = false;
16
16
  this.handlers = {};
17
17
  this.rawHandlers = {};
18
- this.bindings = {};
19
18
  }
20
19
  static get observedAttributes() {
21
20
  return [
@@ -35,20 +34,6 @@ export class LxVideoElement extends HTMLElement {
35
34
  "playback-rates",
36
35
  ];
37
36
  }
38
- set pageBindings(bindings) {
39
- this.bindings = bindings !== null && bindings !== void 0 ? bindings : {};
40
- if (this.isConnected)
41
- this.requestRootCompile();
42
- }
43
- get pageBindings() {
44
- return this.bindings;
45
- }
46
- get pageFuncBindings() {
47
- return this.bindings;
48
- }
49
- get pageFuncBindingsJson() {
50
- return JSON.stringify(this.bindings);
51
- }
52
37
  set src(value) {
53
38
  if (value == null) {
54
39
  this.removeAttribute("src");
@@ -74,7 +59,6 @@ export class LxVideoElement extends HTMLElement {
74
59
  }
75
60
  connectedCallback() {
76
61
  for (const property of [
77
- "pageBindings",
78
62
  "contentRotate",
79
63
  "src",
80
64
  "onplayrequest",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lingxia/elements",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "private": false,
5
5
  "description": "LingXia custom elements — native-backed web components for LxApp pages",
6
6
  "type": "module",
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ export {
4
4
  type LxVideoAttributes,
5
5
  type LxVideoEventPayloads,
6
6
  type LxVideoEventHandlers,
7
+ type LxVideoQuality,
7
8
  } from "./video.js";
8
9
 
9
10
  export {
@@ -11,6 +12,9 @@ export {
11
12
  LxMediaSwiperElement,
12
13
  type LxMediaSwiperAttributes,
13
14
  type LxMediaSwiperItem,
15
+ type LxMediaSwiperEventPayloads,
16
+ type LxMediaSwiperEventHandlers,
17
+ type LxMediaSwiperHandle,
14
18
  type LxMediaSwiperChangeEvent,
15
19
  type LxMediaSwiperChangeEventDetail,
16
20
  type LxMediaSwiperTransitionEndEvent,
@@ -31,6 +35,7 @@ export {
31
35
  type LxPickerAttributes,
32
36
  type LxPickerColumn,
33
37
  type LxPickerCascadingColumns,
38
+ type LxPickerColumns,
34
39
  type LxPickerEvent,
35
40
  type LxPickerEventDetail,
36
41
  } from "./picker.js";
@@ -687,7 +687,6 @@ export function isFallbackElement(element: Element): boolean {
687
687
 
688
688
  function readElementProps(element: Element): Record<string, unknown> {
689
689
  const props: Record<string, unknown> = {};
690
- const dataset: Record<string, string> = {};
691
690
  for (const attr of Array.from(element.attributes)) {
692
691
  if (attr.name === "data-lx-native-measuring" || attr.name === "data-lx-native-paint-hidden") continue;
693
692
  const name = attr.name;
@@ -695,10 +694,6 @@ function readElementProps(element: Element): Record<string, unknown> {
695
694
  continue;
696
695
  }
697
696
  props[camelize(name)] = attr.value;
698
- if (name.startsWith("data-") && name !== "data-lx-component-id") {
699
- const key = camelize(name.slice(5));
700
- if (key) dataset[key] = attr.value;
701
- }
702
697
  if (name.startsWith("aria-")) {
703
698
  props[name] = attr.value;
704
699
  }
@@ -730,8 +725,6 @@ function readElementProps(element: Element): Record<string, unknown> {
730
725
  "muted",
731
726
  "volume",
732
727
  "progressBar",
733
- "pageFuncBindings",
734
- "pageFuncBindingsJson",
735
728
  "intent",
736
729
  "emphasis",
737
730
  "size",
@@ -754,10 +747,6 @@ function readElementProps(element: Element): Record<string, unknown> {
754
747
  props[key] = anyEl[key];
755
748
  }
756
749
  }
757
- if (Object.keys(dataset).length > 0) {
758
- props.dataset = dataset;
759
- props.datasetJson = JSON.stringify(dataset);
760
- }
761
750
  const ariaLabel = element.getAttribute("aria-label");
762
751
  if (ariaLabel) {
763
752
  props["aria-label"] = ariaLabel;
@@ -7,8 +7,6 @@ import { ensureComponentId, NativeComponentUpdateState, iOSNativeComponentHelper
7
7
  import { measureElement } from "./dom.js";
8
8
  import { isAndroid, isHarmony, isIOS } from "./platform.js";
9
9
 
10
- type LxMediaSwiperEventHandler = (e: Event) => void;
11
-
12
10
  export type LxMediaSwiperItem =
13
11
  | {
14
12
  id?: string;
@@ -66,6 +64,28 @@ export type LxMediaSwiperItemEvent = CustomEvent<LxMediaSwiperEventDetail>;
66
64
  export type LxMediaSwiperEndReachedEvent = CustomEvent<LxMediaSwiperEndReachedEventDetail>;
67
65
  export type LxMediaSwiperErrorEvent = CustomEvent<LxMediaSwiperErrorEventDetail>;
68
66
 
67
+ /** What each framework callback receives: the event's `detail`. */
68
+ export interface LxMediaSwiperEventPayloads {
69
+ onChange: LxMediaSwiperChangeEventDetail;
70
+ onTransitionEnd: LxMediaSwiperTransitionEndEventDetail;
71
+ onEndReached: LxMediaSwiperEndReachedEventDetail;
72
+ onTap: LxMediaSwiperEventDetail;
73
+ onVideoEnded: LxMediaSwiperEventDetail;
74
+ onError: LxMediaSwiperErrorEventDetail;
75
+ }
76
+
77
+ /** The framework callbacks: each receives its event's payload. */
78
+ export type LxMediaSwiperEventHandlers = {
79
+ [K in keyof LxMediaSwiperEventPayloads]?: (payload: LxMediaSwiperEventPayloads[K]) => void;
80
+ };
81
+
82
+ /** Imperative control of a mounted swiper (React ref, Vue template ref). */
83
+ export interface LxMediaSwiperHandle {
84
+ next(): void;
85
+ previous(): void;
86
+ goToIndex(index: number): void;
87
+ }
88
+
69
89
  /**
70
90
  * Peek leaves part of the previous and next pages visible alongside the current
71
91
  * page so users get a hint that more content exists. A single `number` applies
@@ -81,7 +101,7 @@ export type LxMediaSwiperPeek = number | { previous?: number; next?: number };
81
101
 
82
102
  export type LxMediaSwiperAttributes = {
83
103
  id?: string;
84
- items?: LxMediaSwiperItem[];
104
+ items?: readonly LxMediaSwiperItem[];
85
105
  index?: number;
86
106
  initialIndex?: number;
87
107
  loop?: boolean;
@@ -100,13 +120,12 @@ export type LxMediaSwiperAttributes = {
100
120
  className?: string;
101
121
  style?: any;
102
122
  ref?: any;
103
- onChange?: LxMediaSwiperEventHandler;
104
- onTransitionEnd?: LxMediaSwiperEventHandler;
105
- onEndReached?: LxMediaSwiperEventHandler;
106
- onTap?: LxMediaSwiperEventHandler;
107
- onVideoEnded?: LxMediaSwiperEventHandler;
108
- onError?: LxMediaSwiperEventHandler;
109
- pageBindings?: Record<string, string>;
123
+ onChange?: (event: LxMediaSwiperChangeEvent) => void;
124
+ onTransitionEnd?: (event: LxMediaSwiperTransitionEndEvent) => void;
125
+ onEndReached?: (event: LxMediaSwiperEndReachedEvent) => void;
126
+ onTap?: (event: LxMediaSwiperItemEvent) => void;
127
+ onVideoEnded?: (event: LxMediaSwiperItemEvent) => void;
128
+ onError?: (event: LxMediaSwiperErrorEvent) => void;
110
129
  };
111
130
 
112
131
  type ObjectFit = "cover" | "contain" | "fill" | "fit";
@@ -129,7 +148,7 @@ declare global {
129
148
  }
130
149
  }
131
150
 
132
- export class LxMediaSwiperElement extends HTMLElement {
151
+ export class LxMediaSwiperElement extends HTMLElement implements LxMediaSwiperHandle {
133
152
  static get observedAttributes() {
134
153
  return [
135
154
  "id",
@@ -163,7 +182,6 @@ export class LxMediaSwiperElement extends HTMLElement {
163
182
  private iOSHelper?: iOSNativeComponentHelper;
164
183
  private rawHandlers: Record<string, EventListenerOrEventListenerObject> = {};
165
184
  private handlers: Record<string, EventListenerOrEventListenerObject> = {};
166
- private _pageBindings: Record<string, string> = {};
167
185
  // Monotonic command counter used as the Harmony bridge fallback. Harmony's
168
186
  // Web.onNativeEmbedDataInfo channel only delivers attribute-derived props,
169
187
  // so imperative API calls have to piggyback into a prop the Builder polls.
@@ -179,7 +197,7 @@ export class LxMediaSwiperElement extends HTMLElement {
179
197
  private harmonyEmbed?: HTMLEmbedElement;
180
198
  private lastHarmonyProps?: string;
181
199
 
182
- set items(value: LxMediaSwiperItem[] | string | null | undefined) {
200
+ set items(value: readonly LxMediaSwiperItem[] | string | null | undefined) {
183
201
  if (typeof value === "string") {
184
202
  const trimmed = value.trim();
185
203
  if (trimmed.length === 0) {
@@ -237,23 +255,6 @@ export class LxMediaSwiperElement extends HTMLElement {
237
255
  return this.parseRotate(this.getAttribute("content-rotate"));
238
256
  }
239
257
 
240
- set pageBindings(bindings: Record<string, string>) {
241
- this.setPageBindings(bindings);
242
- }
243
-
244
- get pageBindings(): Record<string, string> {
245
- return this._pageBindings;
246
- }
247
-
248
- set pageFuncBindings(bindings: Record<string, string>) {
249
- this.setPageBindings(bindings);
250
- }
251
-
252
- setPageBindings(bindings: Record<string, string>) {
253
- this._pageBindings = bindings ?? {};
254
- if (this.isConnected) this.mountOrUpdate(true);
255
- }
256
-
257
258
  next(): void {
258
259
  this.sendCommand("next");
259
260
  }
@@ -272,8 +273,6 @@ export class LxMediaSwiperElement extends HTMLElement {
272
273
  this.upgradeProperty("index");
273
274
  this.upgradeProperty("initialIndex");
274
275
  this.upgradeProperty("contentRotate");
275
- this.upgradeProperty("pageBindings");
276
- this.upgradeProperty("pageFuncBindings");
277
276
  for (const prop of Object.keys(EVENT_MAP)) this.upgradeProperty(prop);
278
277
 
279
278
  this.componentId = ensureComponentId(this, "lx-media-swiper", this.componentId);
@@ -377,7 +376,6 @@ export class LxMediaSwiperElement extends HTMLElement {
377
376
  private collectProps(): Record<string, unknown> {
378
377
  const objectFit = this.parseObjectFit(this.getAttribute("object-fit"));
379
378
  const dots = this.parseDots();
380
- const dataset = this.collectDataset();
381
379
  // Always emit a normalised peek object so removing the attribute (or setting it
382
380
  // to 0) propagates as an explicit reset. JSON.stringify would drop `undefined`,
383
381
  // which makes native think peek hasn't changed and keep the old padding.
@@ -399,10 +397,6 @@ export class LxMediaSwiperElement extends HTMLElement {
399
397
  dots,
400
398
  swipeEnabled: this.getAttribute("swipe-enabled") !== "false",
401
399
  peek,
402
- pageFuncBindings: this._pageBindings,
403
- pageFuncBindingsJson: JSON.stringify(this._pageBindings),
404
- dataset,
405
- datasetJson: JSON.stringify(dataset),
406
400
  };
407
401
  return props;
408
402
  }
@@ -809,16 +803,6 @@ export class LxMediaSwiperElement extends HTMLElement {
809
803
  }
810
804
  }
811
805
 
812
- private collectDataset(): Record<string, string> {
813
- const dataset: Record<string, string> = {};
814
- for (const attr of this.getAttributeNames()) {
815
- if (!attr.startsWith("data-")) continue;
816
- const key = attr.slice(5).replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());
817
- const value = this.getAttribute(attr);
818
- if (key && value != null) dataset[key] = value;
819
- }
820
- return dataset;
821
- }
822
806
  }
823
807
 
824
808
  export function registerMediaSwiperComponent() {
@@ -1,3 +1,4 @@
1
+ import type { LxPickerCascadingColumns, LxPickerColumns } from "./picker.js";
1
2
  import type {
2
3
  NavigatorChannel,
3
4
  NavigatorEdge,
@@ -96,19 +97,18 @@ export function buildNavigatorNativeAttrs(
96
97
  return result;
97
98
  }
98
99
 
99
- export type PickerColumns = string[][] | [string[], Record<string, string[]>];
100
- export type PickerValue = string | string[] | undefined;
100
+ export type PickerValue = string | readonly string[] | undefined;
101
101
  export type PickerFields = "year" | "month" | "day" | "range";
102
102
 
103
103
  export interface PickerNativeAttrOptions {
104
104
  id?: string;
105
- columns?: PickerColumns;
105
+ columns?: LxPickerColumns;
106
106
  mode?: "date" | "time";
107
107
  start?: string;
108
108
  end?: string;
109
109
  fields?: PickerFields;
110
- modelValue?: string | string[];
111
- value?: string | string[];
110
+ modelValue?: string | readonly string[];
111
+ value?: string | readonly string[];
112
112
  cancelText?: string;
113
113
  cancelTextColor?: string;
114
114
  cancelButtonColor?: string;
@@ -121,16 +121,16 @@ export function isPickerDateMode(mode?: "date" | "time"): boolean {
121
121
  return mode === "date" || mode === "time";
122
122
  }
123
123
 
124
- export function isPickerCascading(columns?: PickerColumns): columns is [string[], Record<string, string[]>] {
124
+ export function isPickerCascading(columns?: LxPickerColumns): columns is LxPickerCascadingColumns {
125
125
  return !!columns && columns.length === 2 && typeof columns[1] === "object" && !Array.isArray(columns[1]);
126
126
  }
127
127
 
128
- export function isPickerSingle(columns?: PickerColumns): boolean {
128
+ export function isPickerSingle(columns?: LxPickerColumns): boolean {
129
129
  return !!columns && columns.length === 1;
130
130
  }
131
131
 
132
132
  export function getPickerIndexFromValue(
133
- columns: PickerColumns | undefined,
133
+ columns: LxPickerColumns | undefined,
134
134
  value: PickerValue
135
135
  ): number | number[] {
136
136
  if (!columns) return 0;
@@ -154,7 +154,7 @@ export function getPickerIndexFromValue(
154
154
  }
155
155
 
156
156
  export function getPickerValueFromIndex(
157
- columns: PickerColumns | undefined,
157
+ columns: LxPickerColumns | undefined,
158
158
  index: number | number[]
159
159
  ): string | string[] {
160
160
  if (!columns) return "";
@@ -244,8 +244,8 @@ export interface VideoNativeAttrOptions {
244
244
  progressBar?: boolean;
245
245
  live?: boolean;
246
246
  volume?: string | number;
247
- qualities?: LxVideoQuality[];
248
- playbackRates?: number[];
247
+ qualities?: readonly LxVideoQuality[];
248
+ playbackRates?: readonly number[];
249
249
  }
250
250
 
251
251
  export function buildVideoNativeAttrs(
@@ -285,7 +285,7 @@ export const MEDIA_SWIPER_DOM_EVENT_MAP = {
285
285
 
286
286
  export interface MediaSwiperNativeAttrOptions {
287
287
  id?: string;
288
- items?: LxMediaSwiperItem[];
288
+ items?: readonly LxMediaSwiperItem[];
289
289
  index?: number;
290
290
  initialIndex?: number;
291
291
  loop?: boolean;
package/src/picker.ts CHANGED
@@ -19,8 +19,13 @@ function hasNativePicker(): boolean {
19
19
  const HARMONY_PROPS_PREFIX = "data:application/json,";
20
20
 
21
21
  // Type definitions
22
- export type LxPickerColumn = string[];
23
- export type LxPickerCascadingColumns = [string[], Record<string, string[]>];
22
+ export type LxPickerColumn = readonly string[];
23
+ export type LxPickerCascadingColumns = readonly [
24
+ readonly string[],
25
+ Readonly<Record<string, readonly string[]>>,
26
+ ];
27
+ /** Selector or multi-column columns, or cascading keys and their options. */
28
+ export type LxPickerColumns = readonly LxPickerColumn[] | LxPickerCascadingColumns;
24
29
  type LxPickerViewEventHandler = (e: LxPickerEvent) => void;
25
30
 
26
31
  export interface LxPickerEventDetail {
@@ -37,7 +42,7 @@ export interface LxPickerEvent extends CustomEvent<LxPickerEventDetail> {
37
42
  export type LxPickerAttributes = {
38
43
  id?: string;
39
44
  mode?: 'selector' | 'multiSelector' | 'cascading' | 'date' | 'time';
40
- columns?: LxPickerColumn[] | LxPickerCascadingColumns;
45
+ columns?: LxPickerColumns;
41
46
  defaultIndex?: number | number[];
42
47
  value?: string;
43
48
  start?: string;
@@ -54,7 +59,6 @@ export type LxPickerAttributes = {
54
59
  ref?: any;
55
60
  onChange?: LxPickerViewEventHandler;
56
61
  onScroll?: LxPickerViewEventHandler;
57
- pageBindings?: Record<string, string>;
58
62
  };
59
63
 
60
64
  declare global {
@@ -91,7 +95,6 @@ export class LxPickerElement extends HTMLElement {
91
95
  private unregister?: () => void;
92
96
  private _handlers: Record<string, EventListenerOrEventListenerObject> = {};
93
97
  private rawHandlers: Record<string, EventListenerOrEventListenerObject> = {};
94
- private _pageBindings: Record<string, string> = {};
95
98
  private harmonyEmbed?: HTMLEmbedElement;
96
99
  private lastHarmonyProps?: string;
97
100
  private webCleanup?: () => void;
@@ -112,19 +115,7 @@ export class LxPickerElement extends HTMLElement {
112
115
  self[propName] = value;
113
116
  }
114
117
 
115
- set pageBindings(bindings: Record<string, string>) {
116
- this._pageBindings = bindings ?? {};
117
- if (this.isConnected) {
118
- this.mountPicker();
119
- }
120
- }
121
-
122
- get pageBindings(): Record<string, string> {
123
- return this._pageBindings;
124
- }
125
-
126
118
  connectedCallback() {
127
- this.upgradeProperty("pageBindings");
128
119
  this.upgradeProperty("onchange");
129
120
 
130
121
  this.componentId = ensureComponentId(this, "lx-picker", this.componentId);
@@ -266,42 +257,10 @@ export class LxPickerElement extends HTMLElement {
266
257
  this.attrObserver = undefined;
267
258
  }
268
259
 
269
- private dataAttrToDatasetKey(attr: string): string {
270
- const raw = attr.slice(5).trim();
271
- if (!raw) return "";
272
- const parts = raw.split("-").filter(Boolean);
273
- if (parts.length === 0) return "";
274
- return parts
275
- .map((segment, index) => {
276
- if (index === 0) return segment.toLowerCase();
277
- return segment.charAt(0).toUpperCase() + segment.slice(1);
278
- })
279
- .join("");
280
- }
281
-
282
- private collectDataset(): Record<string, string> {
283
- const dataset: Record<string, string> = {};
284
- const attrs = this.getAttributeNames();
285
- for (const attr of attrs) {
286
- if (!attr.startsWith("data-")) continue;
287
- const key = this.dataAttrToDatasetKey(attr);
288
- if (!key) continue;
289
- const value = this.getAttribute(attr);
290
- if (value == null) continue;
291
- dataset[key] = value;
292
- }
293
- return dataset;
294
- }
295
-
296
260
  private async mountPicker() {
297
261
  if (!this.componentId) return;
298
262
 
299
263
  const props = this.collectProps();
300
- const dataset = this.collectDataset();
301
- props.dataset = dataset;
302
- props.datasetJson = JSON.stringify(dataset);
303
- props.pageFuncBindings = this._pageBindings;
304
- props.pageFuncBindingsJson = JSON.stringify(this._pageBindings);
305
264
  const { rect, cornerRadius } = measureElement(this);
306
265
 
307
266
  if (isHarmony()) {
package/src/video.ts CHANGED
@@ -51,8 +51,8 @@ export type LxVideoAttributes = {
51
51
  progressBar?: boolean;
52
52
  live?: boolean;
53
53
  volume?: string | number;
54
- qualities?: LxVideoQuality[];
55
- playbackRates?: number[];
54
+ qualities?: readonly LxVideoQuality[];
55
+ playbackRates?: readonly number[];
56
56
  className?: string;
57
57
  style?: unknown;
58
58
  ref?: unknown;
@@ -70,7 +70,6 @@ export type LxVideoAttributes = {
70
70
  onQualityChange?: (event: CustomEvent<LxVideoEventPayloads["onQualityChange"]>) => void;
71
71
  onRateChange?: (event: CustomEvent<LxVideoEventPayloads["onRateChange"]>) => void;
72
72
  onVolumeChange?: (event: CustomEvent<LxVideoEventPayloads["onVolumeChange"]>) => void;
73
- pageBindings?: Record<string, string>;
74
73
  };
75
74
 
76
75
  type LxObjectFit = "cover" | "contain" | "fill" | "fit";
@@ -114,24 +113,6 @@ export class LxVideoElement extends HTMLElement {
114
113
  private unregister?: () => void;
115
114
  private handlers: Record<string, EventListenerOrEventListenerObject> = {};
116
115
  private rawHandlers: Record<string, EventListenerOrEventListenerObject> = {};
117
- private bindings: Record<string, string> = {};
118
-
119
- set pageBindings(bindings: Record<string, string>) {
120
- this.bindings = bindings ?? {};
121
- if (this.isConnected) this.requestRootCompile();
122
- }
123
-
124
- get pageBindings(): Record<string, string> {
125
- return this.bindings;
126
- }
127
-
128
- get pageFuncBindings(): Record<string, string> {
129
- return this.bindings;
130
- }
131
-
132
- get pageFuncBindingsJson(): string {
133
- return JSON.stringify(this.bindings);
134
- }
135
116
 
136
117
  set src(value: string | null | undefined) {
137
118
  if (value == null) {
@@ -160,7 +141,6 @@ export class LxVideoElement extends HTMLElement {
160
141
 
161
142
  connectedCallback(): void {
162
143
  for (const property of [
163
- "pageBindings",
164
144
  "contentRotate",
165
145
  "src",
166
146
  "onplayrequest",