@lynx-js/web-elements 0.11.3 → 0.12.1

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,59 @@
1
1
  # @lynx-js/web-elements
2
2
 
3
+ ## 0.12.1
4
+
5
+ ### Patch Changes
6
+
7
+ - fix: XMarkdown slot created should not have prefix ([#2520](https://github.com/lynx-family/lynx-stack/pull/2520))
8
+
9
+ - feat: add x-markdown support ([#2412](https://github.com/lynx-family/lynx-stack/pull/2412))
10
+
11
+ Add opt-in support for the `x-markdown` element on Lynx Web, including
12
+ Markdown rendering together with its related styling, interaction, animation,
13
+ truncation, range rendering, and effect capabilities exposed through the
14
+ component API.
15
+
16
+ Update the `web-core`, `web-core-wasm`, and `web-mainthread-apis` runtime
17
+ paths to use the shared property-or-attribute setter from `web-constants`, so
18
+ custom elements such as `x-markdown` can receive structured property values
19
+ correctly instead of being forced through string-only attribute updates.
20
+
21
+ ```javascript
22
+ import '@lynx-js/web-elements/XMarkdown';
23
+ ```
24
+
25
+ - fix: x-markdown inline view injection no longer queries light DOM children when the content attribute changes. Consumers must now pre-set `slot="{id}"` on the child element they want to project into `inlineview://{id}`. ([#2516](https://github.com/lynx-family/lynx-stack/pull/2516))
26
+
27
+ - fix: list cannot drag-scroll inside x-foldview-slot-ng ([#2507](https://github.com/lynx-family/lynx-stack/pull/2507))
28
+
29
+ Cause: `touchstart` used `elementsFromPoint(pageX, pageY)` (expects `clientX/clientY`), so hit-testing can miss the real inner scroller (e.g. `x-list` shadow `#content`) when the document is scrolled.
30
+
31
+ Fix: use `elementsFromPoint(clientX, clientY)` + `event.composedPath()` for Shadow DOM, and keep `previousPageX` updated during `touchmove`.
32
+
33
+ - fix: line-height of markdown-style should be added `px` ([#2509](https://github.com/lynx-family/lynx-stack/pull/2509))
34
+
35
+ - fix: list `bindscrolltolower` may not trigger because the lower threshold ([#2484](https://github.com/lynx-family/lynx-stack/pull/2484))
36
+ sentinel had no effective size or offset, causing the bottom
37
+ `IntersectionObserver` to miss the list boundary
38
+
39
+ ## 0.12.0
40
+
41
+ ### Minor Changes
42
+
43
+ - feat: add `willchange` event to `x-viewpager-ng` ([#2305](https://github.com/lynx-family/lynx-stack/pull/2305))
44
+
45
+ ### Patch Changes
46
+
47
+ - fix: firefox `@supports(width:1rex)` ([#2288](https://github.com/lynx-family/lynx-stack/pull/2288))
48
+
49
+ - fix: check computed overflow style in `getTheMostScrollableKid` to avoid treating `overflow: visible` elements as scroll containers ([#2309](https://github.com/lynx-family/lynx-stack/pull/2309))
50
+
51
+ - fix: the inline-truncation should only work as a direct child of x-text ([#2287](https://github.com/lynx-family/lynx-stack/pull/2287))
52
+
53
+ - fix: getVisibleCells cannot work in firefox due to contentvisibilityautostatechange not propagate list-item ([#2308](https://github.com/lynx-family/lynx-stack/pull/2308))
54
+
55
+ - fix: foldview stuck issue ([#2304](https://github.com/lynx-family/lynx-stack/pull/2304))
56
+
3
57
  ## 0.11.3
4
58
 
5
59
  ### Patch Changes
@@ -62,14 +116,14 @@
62
116
 
63
117
  - chore: migrate all @lynx-js/web-elements-\* packages into one ([#2057](https://github.com/lynx-family/lynx-stack/pull/2057))
64
118
 
65
- ### Before
119
+ #### Before
66
120
 
67
121
  ```js
68
122
  import '@lynx-js/web-elements-template';
69
123
  import '@lynx-js/web-elements-compat/LinearContainer';
70
124
  ```
71
125
 
72
- ### After
126
+ #### After
73
127
 
74
128
  ```js
75
129
  import '@lynx-js/web-elements/html-templates';
@@ -692,17 +746,17 @@
692
746
  - 3547621: feat(web): use `<lynx-wrapper/>` to replace `<div style="display:content"/>`
693
747
  - bed4f24: feat(web): implement <x-list> with list-type="single"
694
748
 
695
- ## 1. RFC
749
+ #### 1. RFC
696
750
 
697
751
  https://github.com/lynx-wg/lynx-stack/issues/106
698
752
 
699
- ## 2. Implementation differences with RFC
753
+ #### 2. Implementation differences with RFC
700
754
 
701
- ### paging-enabled
755
+ ##### paging-enabled
702
756
 
703
757
  deprecated, no need to implement
704
758
 
705
- ### layoutcomplete
759
+ ##### layoutcomplete
706
760
 
707
761
  Triggered only after the first screen because using contentvisibilityautostatechange.
708
762
 
@@ -711,28 +765,28 @@
711
765
  > This is because content is the parent container of list-item, and content is always visible before list-item.
712
766
  > We cannot obtain the timing of all the successfully visible list-items on the screen, so 100ms is used to delay this behavior.
713
767
 
714
- ### event-scrolltoedge
768
+ ##### event-scrolltoedge
715
769
 
716
770
  split bindscrolltoupperedge and bindscrooltolowerdge.
717
771
 
718
- ### event-scrolltoupper/lower
772
+ ##### event-scrolltoupper/lower
719
773
 
720
774
  Can be used with upper/lower-threshold-item-count.
721
775
 
722
776
  Attention, when the number of x-list children changes, scrolltoupper/lower will be re-triggered (if the new node is on the screen).
723
777
 
724
- ### getVisibleCells, layoutcomplete
778
+ ##### getVisibleCells, layoutcomplete
725
779
 
726
780
  The returned cells may be an empty array, because there is a high probability that the contentvisibilityautostatechange event of list-item will not be captured when the first screen is displayed.
727
781
 
728
- ## 3. Tests not implemented
782
+ #### 3. Tests not implemented
729
783
 
730
- ### HTML Tests
784
+ ##### HTML Tests
731
785
 
732
786
  1. event-layoutcomplete skipped webkit, firefox due to contentvisibilityautostatechange not propagate
733
787
  2. get-visible-cells skipped webkit, firefox due to contentvisibilityautostatechange not propagate
734
788
 
735
- ### React Tests
789
+ ##### React Tests
736
790
 
737
791
  1. lynx.createQuery not supported.
738
792
 
@@ -797,7 +851,7 @@
797
851
  - 8c6eeb9: fix(web): rename x-swiper-itrm to swiper-item
798
852
  - 1fe49a2: feat(web): add custom element x-audio-tt
799
853
 
800
- ## The behavior is different from the native x-audio-tt:
854
+ #### The behavior is different from the native x-audio-tt:
801
855
 
802
856
  - When src changes, resources will not be loaded immediately. Resources will only be loaded when the play and prepare methods are triggered.
803
857
 
@@ -806,13 +860,13 @@
806
860
 
807
861
  - The code returned by the binderror event does not include -4
808
862
 
809
- ## Unimplemented properties:
863
+ #### Unimplemented properties:
810
864
 
811
865
  - autoplay
812
866
  - playertype
813
867
  - experimental-ios-async-prepare
814
868
 
815
- ## Unimplemented methods:
869
+ #### Unimplemented methods:
816
870
 
817
871
  - requestFocus
818
872
  - releaseFocus
@@ -1,3 +1,4 @@
1
+ import { scrollContainerDom } from '../common/constants.js';
1
2
  import { isHeaderShowing } from './XFoldviewNg.js';
2
3
  export class XFoldviewSlotNgTouchEventsHandler {
3
4
  #parentScrollTop = 0;
@@ -25,9 +26,38 @@ export class XFoldviewSlotNgTouchEventsHandler {
25
26
  passive: false,
26
27
  });
27
28
  }
29
+ #resolveScrollContainer(element) {
30
+ const maybeScrollContainer = element[scrollContainerDom];
31
+ return maybeScrollContainer instanceof Element
32
+ ? maybeScrollContainer
33
+ : element;
34
+ }
35
+ #collectCandidateElements(elements) {
36
+ return [
37
+ ...new Set(elements.map(element => this.#resolveScrollContainer(element))),
38
+ ];
39
+ }
40
+ #isScrollContainer(element) {
41
+ let overflowY;
42
+ if (typeof element.computedStyleMap === 'function') {
43
+ try {
44
+ overflowY = element.computedStyleMap().get('overflow-y')?.toString()
45
+ ?? 'visible';
46
+ }
47
+ catch {
48
+ overflowY = getComputedStyle(element).overflowY || 'visible';
49
+ }
50
+ }
51
+ else {
52
+ overflowY = getComputedStyle(element).overflowY || 'visible';
53
+ }
54
+ return overflowY === 'auto' || overflowY === 'scroll'
55
+ || overflowY === 'hidden' || overflowY === 'overlay';
56
+ }
28
57
  #getTheMostScrollableKid(delta) {
29
58
  const scrollableKid = this.#elements?.find((element) => {
30
- if (element.scrollHeight > element.clientHeight) {
59
+ if (this.#isScrollContainer(element)
60
+ && element.scrollHeight > element.clientHeight) {
31
61
  const couldScrollNear = delta < 0
32
62
  && element.scrollTop !== 0;
33
63
  const couldScrollFar = delta > 0
@@ -65,6 +95,7 @@ export class XFoldviewSlotNgTouchEventsHandler {
65
95
  }
66
96
  this.#handleScrollDelta(deltaY, parentElement);
67
97
  this.#previousPageY = pageY;
98
+ this.#previousPageX = pageX;
68
99
  };
69
100
  #handleWheel = (event) => {
70
101
  const parentElement = this.#getParentElement();
@@ -74,10 +105,14 @@ export class XFoldviewSlotNgTouchEventsHandler {
74
105
  if (Math.abs(event.deltaY) <= Math.abs(event.deltaX)) {
75
106
  return;
76
107
  }
77
- const pathElements = event.composedPath().filter((element) => element instanceof Element && this.#dom.contains(element));
108
+ const pathElements = event.composedPath().filter((element) => element instanceof Element && this.#dom.contains(element)
109
+ && element !== this.#dom);
78
110
  const { clientX, clientY } = event;
79
111
  const pointElements = document.elementsFromPoint(clientX, clientY).filter(e => this.#dom.contains(e));
80
- this.#elements = [...new Set([...pathElements, ...pointElements])];
112
+ this.#elements = this.#collectCandidateElements([
113
+ ...pathElements,
114
+ ...pointElements,
115
+ ]);
81
116
  this.#parentScrollTop = parentElement.scrollTop;
82
117
  if (this.#elements) {
83
118
  for (const element of this.#elements) {
@@ -96,8 +131,18 @@ export class XFoldviewSlotNgTouchEventsHandler {
96
131
  }
97
132
  }
98
133
  #touchStart = (event) => {
99
- const { pageX, pageY } = event.touches.item(0);
100
- this.#elements = document.elementsFromPoint(pageX, pageY).filter(e => this.#dom.contains(e));
134
+ const touch = event.touches.item(0);
135
+ const { pageX, pageY, clientX, clientY } = touch;
136
+ // `elementsFromPoint()` doesn't reliably pierce into Shadow DOM; combine with
137
+ // the composed path so we can pick up internal scroll containers like
138
+ // `x-list`'s `#content` inside the shadow root.
139
+ const pathElements = event.composedPath().filter((element) => element instanceof Element && this.#dom.contains(element)
140
+ && element !== this.#dom);
141
+ const pointElements = document.elementsFromPoint(clientX, clientY).filter(e => this.#dom.contains(e) && e !== this.#dom);
142
+ this.#elements = this.#collectCandidateElements([
143
+ ...pathElements,
144
+ ...pointElements,
145
+ ]);
101
146
  this.#previousPageY = pageY;
102
147
  this.#previousPageX = pageX;
103
148
  this.#parentScrollTop = this.#getParentElement()?.scrollTop ?? 0;
@@ -1,3 +1,4 @@
1
+ import { scrollContainerDom } from '../common/constants.js';
1
2
  export declare class XList extends HTMLElement {
2
3
  #private;
3
4
  static readonly notToFilterFalseAttributes: Set<string>;
@@ -7,6 +8,7 @@ export declare class XList extends HTMLElement {
7
8
  set scrollLeft(val: number);
8
9
  get scrollHeight(): number;
9
10
  get scrollWidth(): number;
11
+ get [scrollContainerDom](): HTMLElement;
10
12
  get __scrollTop(): number;
11
13
  get __scrollLeft(): number;
12
14
  scrollToPosition(params: {
@@ -10,6 +10,7 @@ import { XListEvents } from './XListEvents.js';
10
10
  import { XListWaterfall } from './XListWaterfall.js';
11
11
  import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js';
12
12
  import { commonComponentEventSetting } from '../common/commonEventInitConfiguration.js';
13
+ import { scrollContainerDom } from '../common/constants.js';
13
14
  import { templateXList } from '../htmlTemplates.js';
14
15
  let XList = (() => {
15
16
  let _classDecorators = [Component('x-list', [CommonEventsAndMethods, XListAttributes, XListEvents, XListWaterfall], templateXList)];
@@ -52,6 +53,9 @@ let XList = (() => {
52
53
  get scrollWidth() {
53
54
  return this.#getListContainer().scrollWidth;
54
55
  }
56
+ get [scrollContainerDom]() {
57
+ return this.#getListContainer();
58
+ }
55
59
  get __scrollTop() {
56
60
  return super.scrollTop;
57
61
  }
@@ -174,11 +178,22 @@ let XList = (() => {
174
178
  };
175
179
  }
176
180
  getVisibleCells = () => {
177
- const cells = Object.values(this.#cellsMap);
178
- const children = Array.from(this.children).filter(node => {
181
+ let cells = Object.values(this.#cellsMap);
182
+ const children = Array.from(this.children).filter((node) => {
179
183
  return node.tagName === 'LIST-ITEM';
180
184
  });
181
- return cells.map(cell => {
185
+ // firfox cannot triiger contentvisibilityautostatechange event of list-item
186
+ if (cells.length === 0) {
187
+ const listRect = this.#getListContainer().getBoundingClientRect();
188
+ cells = children.filter((cell) => {
189
+ const rect = cell.getBoundingClientRect();
190
+ return (rect.bottom >= listRect.top
191
+ && rect.top <= listRect.bottom
192
+ && rect.right >= listRect.left
193
+ && rect.left <= listRect.right);
194
+ });
195
+ }
196
+ return cells.map((cell) => {
182
197
  const rect = cell.getBoundingClientRect();
183
198
  return {
184
199
  id: cell.getAttribute('id'),
@@ -0,0 +1,43 @@
1
+ import type { MarkdownStyleConfig } from './XMarkdownAttributes.js';
2
+ export declare class XMarkdown extends HTMLElement {
3
+ #private;
4
+ static readonly notToFilterFalseAttributes: Set<string>;
5
+ get markdownStyle(): MarkdownStyleConfig;
6
+ set markdownStyle(value: MarkdownStyleConfig | string | null);
7
+ get ['markdown-style'](): MarkdownStyleConfig;
8
+ set ['markdown-style'](value: MarkdownStyleConfig | string | null);
9
+ /**
10
+ * 获取当前渲染内容中的所有图片 URL。
11
+ */
12
+ getImages(): string[];
13
+ getContent(params?: {
14
+ start?: number;
15
+ end?: number;
16
+ }): {
17
+ content: string;
18
+ };
19
+ pauseAnimation(): void;
20
+ resumeAnimation(params?: {
21
+ animationStep?: number;
22
+ }): void;
23
+ getSelectedText(): string;
24
+ getTextBoundingRect(params?: {
25
+ start?: number;
26
+ end?: number;
27
+ indexType?: 'char' | 'source';
28
+ }): {
29
+ boundingRect: DOMRect;
30
+ } | null;
31
+ setTextSelection(params: {
32
+ startX: number;
33
+ startY: number;
34
+ endX: number;
35
+ endY: number;
36
+ }): void;
37
+ getParseResult(params: {
38
+ tags: string[];
39
+ }): Record<string, {
40
+ start: number;
41
+ end: number;
42
+ }[]>;
43
+ }