@lynx-js/web-elements 0.7.7 → 0.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @lynx-js/web-elements
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - refactor: move exposure system to web-core ([#1254](https://github.com/lynx-family/lynx-stack/pull/1254))
8
+
9
+ **THIS IS A BREAKING CHANGE**
10
+
11
+ **You'll need to upgrade your @lynx-js/web-elements to >= 0.8.0**
12
+
13
+ For SSR and better performance, we moved the lynx's exposure system from web-element to web-core.
14
+
15
+ Before this commit, we create Intersection observers by creating HTMLElements.
16
+
17
+ After this commit, we will create such Intersection observers after dom stabled.
18
+
19
+ Also, the setInterval for exposure has been removed, now we use an on time lazy timer for such features.
20
+
21
+ ### Patch Changes
22
+
23
+ - feat: add support for scrollTo method in x-swiper, remove scrollToNext && scrollToPrevious method ([#1197](https://github.com/lynx-family/lynx-stack/pull/1197))
24
+
25
+ - refactor: improve `linear-weight-sum` performance ([#1216](https://github.com/lynx-family/lynx-stack/pull/1216))
26
+
27
+ - Updated dependencies []:
28
+ - @lynx-js/web-elements-template@0.8.0
29
+
3
30
  ## 0.7.7
4
31
 
5
32
  ### Patch Changes
@@ -8,7 +8,7 @@ export declare const audioPlaybackStateMap: Record<string, {
8
8
  code: number;
9
9
  type: string;
10
10
  }>;
11
- export declare const getAudioState: (audioElement: HTMLAudioElement) => 0 | 2 | 1 | -1 | 3;
11
+ export declare const getAudioState: (audioElement: HTMLAudioElement) => 0 | 1 | 2 | 3 | -1;
12
12
  export declare const enum XAudioErrorCode {
13
13
  SrcError = -1,
14
14
  SrcJsonError = -2,
@@ -7,8 +7,19 @@ export declare class XSwiper extends HTMLElement {
7
7
  get snapDistance(): number;
8
8
  get isVertical(): boolean;
9
9
  get circularPlay(): boolean;
10
- scrollToNext(): void;
11
- scrollToPrevious(): void;
10
+ scrollTo(options: {
11
+ /**
12
+ * @description target index
13
+ */
14
+ index: number;
15
+ /**
16
+ * @description Whether there is switching animation
17
+ * @defaultValue true
18
+ */
19
+ smooth?: boolean;
20
+ }): void;
21
+ scrollTo(options?: ScrollToOptions | undefined): void;
22
+ scrollTo(x: number, y: number): void;
12
23
  connectedCallback(): void;
13
24
  get [scrollContainerDom](): this;
14
25
  }
@@ -140,31 +140,18 @@ let XSwiper = (() => {
140
140
  get circularPlay() {
141
141
  return this.getAttribute('circular') !== null;
142
142
  }
143
- scrollToNext() {
144
- const current = this.currentIndex;
145
- const count = this.childElementCount;
146
- if (current === count - 1) {
147
- const circularPlay = this.circularPlay;
148
- if (circularPlay) {
149
- this.currentIndex = 0;
143
+ scrollTo(...args) {
144
+ // Check if the first argument has an index property (custom usage)
145
+ if (args.length > 0 && typeof args[0] === 'object' && args[0] !== null
146
+ && 'index' in args[0]) {
147
+ const { index, smooth = true } = args[0];
148
+ if (typeof index === 'number') {
149
+ this.#scrollToIndex(index, smooth ? 'smooth' : 'instant');
150
+ return;
150
151
  }
151
152
  }
152
- else {
153
- this.currentIndex += 1;
154
- }
155
- }
156
- scrollToPrevious() {
157
- const current = this.currentIndex;
158
- const count = this.childElementCount;
159
- if (current === 0) {
160
- const circularPlay = this.circularPlay;
161
- if (circularPlay) {
162
- this.currentIndex = count - 1;
163
- }
164
- }
165
- else {
166
- this.currentIndex = count - 1;
167
- }
153
+ // Fall back to standard HTML scrollTo behavior
154
+ super.scrollTo(...args);
168
155
  }
169
156
  connectedCallback() {
170
157
  const current = this.getAttribute('current');
@@ -39,10 +39,23 @@ let XSwiperAutoScroll = (() => {
39
39
  constructor(dom) {
40
40
  this.#dom = dom;
41
41
  }
42
+ #scrollToNext() {
43
+ const current = this.#dom.currentIndex;
44
+ const count = this.#dom.childElementCount;
45
+ if (current === count - 1) {
46
+ const circularPlay = this.#dom.circularPlay;
47
+ if (circularPlay) {
48
+ this.#dom.currentIndex = 0;
49
+ }
50
+ }
51
+ else {
52
+ this.#dom.currentIndex += 1;
53
+ }
54
+ }
42
55
  get #handleCurrentChange() { return _private_handleCurrentChange_descriptor.value; }
43
56
  #autoPlayTimer;
44
57
  #autoPlayTick = (() => {
45
- this.#dom.scrollToNext();
58
+ this.#scrollToNext();
46
59
  }).bind(this);
47
60
  #startAutoplay(interval) {
48
61
  this.#stopAutoplay();
@@ -1,35 +1,9 @@
1
1
  export declare const layoutChangeTarget: unique symbol;
2
- export interface ExposureParameters {
3
- exposureID: string | null;
4
- exposureArea: string | null;
5
- exposureScene: string | null;
6
- exposureScreenMarginTop: string | null;
7
- exposureScreenMarginRight: string | null;
8
- exposureScreenMarginBottom: string | null;
9
- exposureScreenMarginLeft: string | null;
10
- exposureUIMarginTop: string | null;
11
- exposureUIMarginRight: string | null;
12
- exposureUIMarginBottom: string | null;
13
- exposureUIMarginLeft: string | null;
14
- }
15
- export interface ExposureEvent {
16
- 'exposure-id': string;
17
- 'exposure-scene': string;
18
- exposureID: string;
19
- exposureScene: string;
20
- }
21
2
  export declare class CommonEventsAndMethods {
22
3
  #private;
23
- static readonly observedAttributes: string[];
4
+ static readonly observedAttributes: never[];
24
5
  constructor(currentElement: HTMLElement & {
25
6
  [layoutChangeTarget]?: HTMLElement;
26
7
  });
27
- onExposureParamsChanged: () => void;
28
- onExposureIdChanged(_: string | null, oldValue: string | null): void;
29
- attributeChangedHandler: this;
30
- eventStatusChangedHandler: {
31
- uiappear: (status: boolean) => void;
32
- uidisappear: (status: boolean) => void;
33
- };
34
8
  __handleScrollUpperThresholdEventEnabled: (enabled: boolean) => void;
35
9
  }
@@ -2,9 +2,7 @@
2
2
  // Licensed under the Apache License Version 2.0 that can be found in the
3
3
  // LICENSE file in the root directory of this source tree.
4
4
  import { __esDecorate, __runInitializers } from "tslib";
5
- import { convertLengthToPx } from './convertLengthToPx.js';
6
5
  import { commonComponentEventSetting } from './commonEventInitConfiguration.js';
7
- import { scrollContainerDom } from './constants.js';
8
6
  import { registerEventEnableStatusChangeHandler } from '@lynx-js/web-elements-reactive';
9
7
  export const layoutChangeTarget = Symbol('layoutChangeTarget');
10
8
  let CommonEventsAndMethods = (() => {
@@ -18,194 +16,12 @@ let CommonEventsAndMethods = (() => {
18
16
  __esDecorate(null, null, ___handleScrollUpperThresholdEventEnabled_decorators, { kind: "field", name: "__handleScrollUpperThresholdEventEnabled", static: false, private: false, access: { has: obj => "__handleScrollUpperThresholdEventEnabled" in obj, get: obj => obj.__handleScrollUpperThresholdEventEnabled, set: (obj, value) => { obj.__handleScrollUpperThresholdEventEnabled = value; } }, metadata: _metadata }, ___handleScrollUpperThresholdEventEnabled_initializers, ___handleScrollUpperThresholdEventEnabled_extraInitializers);
19
17
  if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
20
18
  }
21
- static observedAttributes = [
22
- 'exposure-id',
23
- 'exposure-area',
24
- 'exposure-screen-margin-top',
25
- 'exposure-screen-margin-right',
26
- 'exposure-screen-margin-bottom',
27
- 'exposure-screen-margin-left',
28
- 'exposure-ui-margin-top',
29
- 'exposure-ui-margin-right',
30
- 'exposure-ui-margin-bottom',
31
- 'exposure-ui-margin-left',
32
- ];
33
- #uiAppearEnabled = false;
34
- #uiDisappearEnabled = false;
19
+ static observedAttributes = [];
35
20
  #dom;
36
- /**
37
- * Stores a promise. We will handler the exposure attribute change after all related life-cycle events has been fired by browser.
38
- */
39
- #afterAttributeChanged;
40
- /**
41
- * If this dom is already exposured
42
- */
43
- #exposureTriggered = false;
44
- /**
45
- * keeps the observer of current dom
46
- */
47
- #exposureObserver;
48
- get #exposureEnabled() {
49
- return (this.#uiAppearEnabled
50
- || this.#uiDisappearEnabled
51
- || this.#dom.getAttribute('exposure-id') !== null);
52
- }
53
21
  constructor(currentElement) {
54
22
  __runInitializers(this, ___handleScrollUpperThresholdEventEnabled_extraInitializers);
55
23
  this.#dom = currentElement;
56
24
  }
57
- onExposureParamsChanged = () => {
58
- if (!this.#afterAttributeChanged) {
59
- this.#afterAttributeChanged = Promise.resolve().then(() => {
60
- this.#updateExposure();
61
- this.#afterAttributeChanged = undefined;
62
- });
63
- }
64
- };
65
- onExposureIdChanged(_, oldValue) {
66
- if (oldValue) {
67
- if (this.#exposureEnabled) {
68
- this.#sendOneExposureEvent({ isIntersecting: false }, oldValue);
69
- }
70
- }
71
- this.onExposureParamsChanged();
72
- }
73
- attributeChangedHandler = new Proxy(this, {
74
- get(target, attribute) {
75
- if (CommonEventsAndMethods.observedAttributes.includes(attribute)) {
76
- if (attribute === 'exposure-id') {
77
- return { handler: target.onExposureIdChanged, noDomMeasure: true };
78
- }
79
- else {
80
- return {
81
- handler: target.onExposureParamsChanged,
82
- noDomMeasure: true,
83
- };
84
- }
85
- }
86
- return;
87
- },
88
- });
89
- eventStatusChangedHandler = {
90
- 'uiappear': (status) => {
91
- this.#uiAppearEnabled = status;
92
- this.onExposureParamsChanged();
93
- },
94
- 'uidisappear': (status) => {
95
- this.#uiDisappearEnabled = status;
96
- this.onExposureParamsChanged();
97
- },
98
- };
99
- #updateExposure() {
100
- const newParams = {
101
- exposureID: this.#dom.getAttribute('exposure-id'),
102
- exposureArea: this.#dom.getAttribute('exposure-area'),
103
- exposureScene: this.#dom.getAttribute('exposure-scene'),
104
- exposureScreenMarginTop: this.#dom.getAttribute('exposure-screen-margin-top'),
105
- exposureScreenMarginRight: this.#dom.getAttribute('exposure-screen-margin-right'),
106
- exposureScreenMarginBottom: this.#dom.getAttribute('exposure-screen-margin-bottom'),
107
- exposureScreenMarginLeft: this.#dom.getAttribute('exposure-screen-margin-left'),
108
- exposureUIMarginTop: this.#dom.getAttribute('exposure-ui-margin-top'),
109
- exposureUIMarginRight: this.#dom.getAttribute('exposure-ui-margin-right'),
110
- exposureUIMarginBottom: this.#dom.getAttribute('exposure-ui-margin-bottom'),
111
- exposureUIMarginLeft: this.#dom.getAttribute('exposure-ui-margin-left'),
112
- };
113
- if (this.#exposureEnabled) {
114
- if (IntersectionObserver) {
115
- const uiMargin = {
116
- top: convertLengthToPx(this.#dom, newParams.exposureUIMarginTop),
117
- right: convertLengthToPx(this.#dom, newParams.exposureUIMarginRight, true),
118
- bottom: convertLengthToPx(this.#dom, newParams.exposureUIMarginBottom),
119
- left: convertLengthToPx(this.#dom, newParams.exposureUIMarginLeft, true),
120
- };
121
- const screenMargin = {
122
- top: convertLengthToPx(this.#dom, newParams.exposureScreenMarginTop),
123
- right: convertLengthToPx(this.#dom, newParams.exposureScreenMarginRight, true),
124
- bottom: convertLengthToPx(this.#dom, newParams.exposureScreenMarginBottom),
125
- left: convertLengthToPx(this.#dom, newParams.exposureScreenMarginLeft, true),
126
- };
127
- /**
128
- * TODO: @haoyang.wang support the switch `enableExposureUIMargin`
129
- */
130
- const calcedRootMargin = {
131
- top: (uiMargin.bottom ? -1 : 1)
132
- * (screenMargin.top - uiMargin.bottom),
133
- right: (uiMargin.left ? -1 : 1)
134
- * (screenMargin.right - uiMargin.left),
135
- bottom: (uiMargin.top ? -1 : 1)
136
- * (screenMargin.bottom - uiMargin.top),
137
- left: (uiMargin.right ? -1 : 1)
138
- * (screenMargin.left - uiMargin.right),
139
- };
140
- const exposureArea = this.#dom.getAttribute('exposure-area');
141
- const rootMargin = `${calcedRootMargin.top}px ${calcedRootMargin.right}px ${calcedRootMargin.bottom}px ${calcedRootMargin.left}px`;
142
- const threshold = exposureArea ? parseFloat(exposureArea) / 100 : 0;
143
- if (this.#exposureObserver) {
144
- this.#exposureObserver.disconnect();
145
- }
146
- /**
147
- * Get the closest scrollable ancestor
148
- */
149
- let root = this.#dom.parentElement;
150
- while (root) {
151
- // @ts-expect-error
152
- if (root[scrollContainerDom]) {
153
- // @ts-expect-error
154
- root = root[scrollContainerDom];
155
- break;
156
- }
157
- else {
158
- root = root.parentElement;
159
- }
160
- }
161
- this.#exposureTriggered = false;
162
- this.#exposureObserver = new IntersectionObserver(([entry]) => {
163
- if (entry) {
164
- if (entry.isIntersecting) {
165
- this.#exposureTriggered = true;
166
- }
167
- this.#sendOneExposureEvent(entry);
168
- }
169
- }, {
170
- rootMargin,
171
- threshold,
172
- root,
173
- });
174
- this.#exposureObserver.observe(this.#dom);
175
- }
176
- }
177
- else {
178
- this.#disableExposure();
179
- }
180
- }
181
- #sendOneExposureEvent(entry, overrideExposureId) {
182
- if (!this.#exposureTriggered) {
183
- return;
184
- }
185
- const exposureID = overrideExposureId
186
- ?? this.#dom.getAttribute('exposure-id') ?? '';
187
- const exposureScene = this.#dom.getAttribute('exposure-scene')
188
- ?? '';
189
- const detail = {
190
- 'exposure-id': exposureID,
191
- 'exposure-scene': exposureScene,
192
- exposureID,
193
- exposureScene,
194
- };
195
- const appearEvent = new CustomEvent(entry.isIntersecting ? 'uiappear' : 'uidisappear', {
196
- ...commonComponentEventSetting,
197
- detail,
198
- });
199
- const exposureEvent = new CustomEvent(entry.isIntersecting ? 'exposure' : 'disexposure', {
200
- bubbles: true,
201
- composed: false,
202
- cancelable: false,
203
- detail,
204
- });
205
- Object.assign(appearEvent, detail);
206
- this.#dom.dispatchEvent(appearEvent);
207
- this.#dom.dispatchEvent(exposureEvent);
208
- }
209
25
  #resizeObserving = false;
210
26
  #resizeObserver;
211
27
  __handleScrollUpperThresholdEventEnabled = __runInitializers(this, ___handleScrollUpperThresholdEventEnabled_initializers, (enabled) => {
@@ -241,10 +57,7 @@ let CommonEventsAndMethods = (() => {
241
57
  }
242
58
  });
243
59
  #disableExposure() {
244
- if (this.#exposureObserver) {
245
- this.#exposureObserver.disconnect();
246
- this.#exposureObserver = undefined;
247
- }
60
+ this.#resizeObserver?.disconnect();
248
61
  }
249
62
  };
250
63
  })();
@@ -10,5 +10,5 @@ export const isWebkit = /\b(iPad|iPhone|iPod|OS X)\b/.test(UA)
10
10
  && /WebKit/.test(UA)
11
11
  // @ts-expect-error
12
12
  && !window.MSStream;
13
- export const scrollContainerDom = Symbol();
13
+ export const scrollContainerDom = Symbol.for('lynx-scroll-container-dom');
14
14
  //# sourceMappingURL=constants.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lynx-js/web-elements",
3
- "version": "0.7.7",
3
+ "version": "0.8.0",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
@@ -101,7 +101,7 @@
101
101
  ],
102
102
  "dependencies": {
103
103
  "@lynx-js/web-elements-reactive": "0.2.2",
104
- "@lynx-js/web-elements-template": "0.7.7"
104
+ "@lynx-js/web-elements-template": "0.8.0"
105
105
  },
106
106
  "devDependencies": {
107
107
  "tslib": "^2.8.1"
@@ -283,13 +283,28 @@ list-item {
283
283
  x-textarea,
284
284
  x-list,
285
285
  list-item {
286
+ /*
287
+ `--lynx-linear-weight-sum`
288
+ 0 -> 1
289
+ <value> -> <value>
290
+ */
286
291
  flex-shrink: 0;
292
+ /* The following `calc` and `clamp` logic ensures that if
293
+ `--lynx-linear-weight-sum` is zero, it defaults to 1. This prevents
294
+ division by zero and ensures consistent behavior. */
287
295
  flex-grow: calc(
288
- var(--lynx-linear-weight) / var(--lynx-linear-weight-sum)
296
+ var(--lynx-linear-weight) /
297
+ calc(
298
+ var(--lynx-linear-weight-sum) +
299
+ (
300
+ 1 - clamp(0, var(--lynx-linear-weight-sum) * 999999, 1)
301
+ )
302
+ )
289
303
  );
290
304
  flex-basis: var(--lynx-linear-weight-basis);
291
305
  }
292
306
  }
307
+
293
308
  @container not style(--lynx-display: linear) {
294
309
  x-view,
295
310
  x-blur-view,
@@ -1 +0,0 @@
1
- export declare function convertLengthToPx(targetElement: HTMLElement, str: string | null, isWidth?: boolean): number;
@@ -1,25 +0,0 @@
1
- // Copyright 2024 The Lynx Authors. All rights reserved.
2
- // Licensed under the Apache License Version 2.0 that can be found in the
3
- // LICENSE file in the root directory of this source tree.
4
- export function convertLengthToPx(targetElement, str, isWidth) {
5
- if (str) {
6
- str = str.trim();
7
- if (str.endsWith('px')) {
8
- return Number(str.substring(0, str.length - 2));
9
- }
10
- else if (str.endsWith('%')) {
11
- const pct = Number(str.substring(0, str.length - 1));
12
- const { width, height } = targetElement.getBoundingClientRect();
13
- const base = isWidth ? width : height;
14
- return (base * pct) / 100;
15
- }
16
- else {
17
- /**
18
- * TODO (haoyang.wang): support rpx
19
- */
20
- return 0;
21
- }
22
- }
23
- return 0;
24
- }
25
- //# sourceMappingURL=convertLengthToPx.js.map