@lynx-js/web-elements 0.12.0 → 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,41 @@
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
+
3
39
  ## 0.12.0
4
40
 
5
41
  ### Minor Changes
@@ -80,14 +116,14 @@
80
116
 
81
117
  - chore: migrate all @lynx-js/web-elements-\* packages into one ([#2057](https://github.com/lynx-family/lynx-stack/pull/2057))
82
118
 
83
- ### Before
119
+ #### Before
84
120
 
85
121
  ```js
86
122
  import '@lynx-js/web-elements-template';
87
123
  import '@lynx-js/web-elements-compat/LinearContainer';
88
124
  ```
89
125
 
90
- ### After
126
+ #### After
91
127
 
92
128
  ```js
93
129
  import '@lynx-js/web-elements/html-templates';
@@ -710,17 +746,17 @@
710
746
  - 3547621: feat(web): use `<lynx-wrapper/>` to replace `<div style="display:content"/>`
711
747
  - bed4f24: feat(web): implement <x-list> with list-type="single"
712
748
 
713
- ## 1. RFC
749
+ #### 1. RFC
714
750
 
715
751
  https://github.com/lynx-wg/lynx-stack/issues/106
716
752
 
717
- ## 2. Implementation differences with RFC
753
+ #### 2. Implementation differences with RFC
718
754
 
719
- ### paging-enabled
755
+ ##### paging-enabled
720
756
 
721
757
  deprecated, no need to implement
722
758
 
723
- ### layoutcomplete
759
+ ##### layoutcomplete
724
760
 
725
761
  Triggered only after the first screen because using contentvisibilityautostatechange.
726
762
 
@@ -729,28 +765,28 @@
729
765
  > This is because content is the parent container of list-item, and content is always visible before list-item.
730
766
  > We cannot obtain the timing of all the successfully visible list-items on the screen, so 100ms is used to delay this behavior.
731
767
 
732
- ### event-scrolltoedge
768
+ ##### event-scrolltoedge
733
769
 
734
770
  split bindscrolltoupperedge and bindscrooltolowerdge.
735
771
 
736
- ### event-scrolltoupper/lower
772
+ ##### event-scrolltoupper/lower
737
773
 
738
774
  Can be used with upper/lower-threshold-item-count.
739
775
 
740
776
  Attention, when the number of x-list children changes, scrolltoupper/lower will be re-triggered (if the new node is on the screen).
741
777
 
742
- ### getVisibleCells, layoutcomplete
778
+ ##### getVisibleCells, layoutcomplete
743
779
 
744
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.
745
781
 
746
- ## 3. Tests not implemented
782
+ #### 3. Tests not implemented
747
783
 
748
- ### HTML Tests
784
+ ##### HTML Tests
749
785
 
750
786
  1. event-layoutcomplete skipped webkit, firefox due to contentvisibilityautostatechange not propagate
751
787
  2. get-visible-cells skipped webkit, firefox due to contentvisibilityautostatechange not propagate
752
788
 
753
- ### React Tests
789
+ ##### React Tests
754
790
 
755
791
  1. lynx.createQuery not supported.
756
792
 
@@ -815,7 +851,7 @@
815
851
  - 8c6eeb9: fix(web): rename x-swiper-itrm to swiper-item
816
852
  - 1fe49a2: feat(web): add custom element x-audio-tt
817
853
 
818
- ## The behavior is different from the native x-audio-tt:
854
+ #### The behavior is different from the native x-audio-tt:
819
855
 
820
856
  - When src changes, resources will not be loaded immediately. Resources will only be loaded when the play and prepare methods are triggered.
821
857
 
@@ -824,13 +860,13 @@
824
860
 
825
861
  - The code returned by the binderror event does not include -4
826
862
 
827
- ## Unimplemented properties:
863
+ #### Unimplemented properties:
828
864
 
829
865
  - autoplay
830
866
  - playertype
831
867
  - experimental-ios-async-prepare
832
868
 
833
- ## Unimplemented methods:
869
+ #### Unimplemented methods:
834
870
 
835
871
  - requestFocus
836
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,6 +26,17 @@ 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
+ }
28
40
  #isScrollContainer(element) {
29
41
  let overflowY;
30
42
  if (typeof element.computedStyleMap === 'function') {
@@ -83,6 +95,7 @@ export class XFoldviewSlotNgTouchEventsHandler {
83
95
  }
84
96
  this.#handleScrollDelta(deltaY, parentElement);
85
97
  this.#previousPageY = pageY;
98
+ this.#previousPageX = pageX;
86
99
  };
87
100
  #handleWheel = (event) => {
88
101
  const parentElement = this.#getParentElement();
@@ -96,7 +109,10 @@ export class XFoldviewSlotNgTouchEventsHandler {
96
109
  && element !== this.#dom);
97
110
  const { clientX, clientY } = event;
98
111
  const pointElements = document.elementsFromPoint(clientX, clientY).filter(e => this.#dom.contains(e));
99
- this.#elements = [...new Set([...pathElements, ...pointElements])];
112
+ this.#elements = this.#collectCandidateElements([
113
+ ...pathElements,
114
+ ...pointElements,
115
+ ]);
100
116
  this.#parentScrollTop = parentElement.scrollTop;
101
117
  if (this.#elements) {
102
118
  for (const element of this.#elements) {
@@ -115,8 +131,18 @@ export class XFoldviewSlotNgTouchEventsHandler {
115
131
  }
116
132
  }
117
133
  #touchStart = (event) => {
118
- const { pageX, pageY } = event.touches.item(0);
119
- this.#elements = document.elementsFromPoint(pageX, pageY).filter(e => this.#dom.contains(e) && e !== this.#dom);
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
+ ]);
120
146
  this.#previousPageY = pageY;
121
147
  this.#previousPageX = pageX;
122
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
  }
@@ -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
+ }
@@ -0,0 +1,346 @@
1
+ import { __esDecorate, __runInitializers } from "tslib";
2
+ /*
3
+ // Copyright 2024 The Lynx Authors. All rights reserved.
4
+ // Licensed under the Apache License Version 2.0 that can be found in the
5
+ // LICENSE file in the root directory of this source tree.
6
+ */
7
+ import { Component } from '../../element-reactive/index.js';
8
+ import { CommonEventsAndMethods } from '../common/CommonEventsAndMethods.js';
9
+ import { templateXMarkdown } from '../htmlTemplates.js';
10
+ import { parseMarkdownStyle, serializeMarkdownStyle, XMarkdownAttributes, } from './XMarkdownAttributes.js';
11
+ const getComposedRange = (selection, shadowRoot) => {
12
+ const getComposedRanges = selection
13
+ .getComposedRanges;
14
+ if (!shadowRoot || typeof getComposedRanges !== 'function')
15
+ return null;
16
+ try {
17
+ return getComposedRanges.call(selection, {
18
+ shadowRoots: [shadowRoot],
19
+ })[0] ?? null;
20
+ }
21
+ catch {
22
+ try {
23
+ return getComposedRanges.call(selection, shadowRoot)[0] ?? null;
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ }
29
+ };
30
+ const createRangeByOffsets = (doc, root, start, end) => {
31
+ const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT);
32
+ let pos = 0;
33
+ let startNode = null;
34
+ let startOffset = 0;
35
+ let endNode = null;
36
+ let endOffset = 0;
37
+ let node = walker.nextNode();
38
+ while (node) {
39
+ const len = node.nodeValue?.length ?? 0;
40
+ if (!startNode && pos + len >= start) {
41
+ startNode = node;
42
+ startOffset = start - pos;
43
+ }
44
+ if (pos + len >= end) {
45
+ endNode = node;
46
+ endOffset = end - pos;
47
+ break;
48
+ }
49
+ pos += len;
50
+ node = walker.nextNode();
51
+ }
52
+ if (!startNode)
53
+ return null;
54
+ if (!endNode) {
55
+ endNode = startNode;
56
+ endOffset = startOffset;
57
+ }
58
+ const range = doc.createRange();
59
+ range.setStart(startNode, Math.max(0, Math.min(startOffset, startNode.length)));
60
+ range.setEnd(endNode, Math.max(0, Math.min(endOffset, endNode.length)));
61
+ return range;
62
+ };
63
+ const getRenderedPrefixLengthForSourceOffset = (source, rendered, sourceOffset) => {
64
+ let renderedIndex = 0;
65
+ const limit = Math.max(0, Math.min(sourceOffset, source.length));
66
+ for (let sourceIndex = 0; sourceIndex < limit; sourceIndex += 1) {
67
+ if (renderedIndex >= rendered.length)
68
+ break;
69
+ if (source[sourceIndex] === rendered[renderedIndex]) {
70
+ renderedIndex += 1;
71
+ }
72
+ }
73
+ return renderedIndex;
74
+ };
75
+ const mapSourceRangeToCharRange = (source, rendered, start, end) => {
76
+ const renderedStart = getRenderedPrefixLengthForSourceOffset(source, rendered, start);
77
+ const renderedEnd = getRenderedPrefixLengthForSourceOffset(source, rendered, end);
78
+ return {
79
+ start: renderedStart,
80
+ end: Math.max(renderedStart, renderedEnd),
81
+ };
82
+ };
83
+ const isSelectionNodeInsideHost = (dom, shadowRoot, node) => !!node
84
+ && (node === dom
85
+ || node === shadowRoot
86
+ || dom.contains(node)
87
+ || !!shadowRoot?.contains(node));
88
+ const getRangeInRoot = (dom, root, selection) => {
89
+ if (!selection)
90
+ return null;
91
+ const shadowRoot = dom.shadowRoot;
92
+ const sourceRange = getComposedRange(selection, shadowRoot)
93
+ ?? (selection.rangeCount > 0 ? selection.getRangeAt(0) : null);
94
+ if (!sourceRange)
95
+ return null;
96
+ if (!root.contains(sourceRange.startContainer)
97
+ || !root.contains(sourceRange.endContainer)) {
98
+ if (!isSelectionNodeInsideHost(dom, shadowRoot, sourceRange.startContainer)
99
+ || !isSelectionNodeInsideHost(dom, shadowRoot, sourceRange.endContainer)
100
+ || !isSelectionNodeInsideHost(dom, shadowRoot, selection.anchorNode)
101
+ || !isSelectionNodeInsideHost(dom, shadowRoot, selection.focusNode)) {
102
+ return null;
103
+ }
104
+ const selectedText = selection.toString();
105
+ if (!selectedText) {
106
+ return null;
107
+ }
108
+ const start = root.textContent?.indexOf(selectedText) ?? -1;
109
+ if (start < 0)
110
+ return null;
111
+ return createRangeByOffsets(dom.ownerDocument, root, start, start + selectedText.length);
112
+ }
113
+ const range = dom.ownerDocument.createRange();
114
+ range.setStart(sourceRange.startContainer, sourceRange.startOffset);
115
+ range.setEnd(sourceRange.endContainer, sourceRange.endOffset);
116
+ return range;
117
+ };
118
+ const getSelectionCandidates = (dom) => {
119
+ const shadowRoot = dom.shadowRoot;
120
+ const shadowSelection = shadowRoot && typeof shadowRoot.getSelection === 'function'
121
+ ? shadowRoot.getSelection()
122
+ : null;
123
+ const documentSelection = dom.ownerDocument.getSelection();
124
+ return [shadowSelection, documentSelection].filter((selection, index, selections) => !!selection && selections.indexOf(selection) === index);
125
+ };
126
+ const getSelectionForRoot = (dom, root) => {
127
+ for (const selection of getSelectionCandidates(dom)) {
128
+ const range = getRangeInRoot(dom, root, selection);
129
+ if (range)
130
+ return { selection, range };
131
+ }
132
+ return null;
133
+ };
134
+ const getPreferredSelectionTarget = (dom) => {
135
+ const candidates = getSelectionCandidates(dom);
136
+ return candidates[0] ?? null;
137
+ };
138
+ let XMarkdown = (() => {
139
+ let _classDecorators = [Component('x-markdown', [CommonEventsAndMethods, XMarkdownAttributes], templateXMarkdown)];
140
+ let _classDescriptor;
141
+ let _classExtraInitializers = [];
142
+ let _classThis;
143
+ let _classSuper = HTMLElement;
144
+ var XMarkdown = class extends _classSuper {
145
+ static { _classThis = this; }
146
+ static {
147
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
148
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
149
+ XMarkdown = _classThis = _classDescriptor.value;
150
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
151
+ }
152
+ static notToFilterFalseAttributes = new Set(['content-complete']);
153
+ #getMarkdownStyle() {
154
+ return parseMarkdownStyle(this.getAttribute('markdown-style'));
155
+ }
156
+ #setMarkdownStyle(value) {
157
+ const serialized = serializeMarkdownStyle(value);
158
+ if (serialized === null) {
159
+ this.removeAttribute('markdown-style');
160
+ }
161
+ else {
162
+ this.setAttribute('markdown-style', serialized);
163
+ }
164
+ }
165
+ get markdownStyle() {
166
+ return this.#getMarkdownStyle();
167
+ }
168
+ set markdownStyle(value) {
169
+ this.#setMarkdownStyle(value);
170
+ }
171
+ get ['markdown-style']() {
172
+ return this.#getMarkdownStyle();
173
+ }
174
+ set ['markdown-style'](value) {
175
+ this.#setMarkdownStyle(value);
176
+ }
177
+ /**
178
+ * 获取当前渲染内容中的所有图片 URL。
179
+ */
180
+ getImages() {
181
+ const root = this.shadowRoot?.querySelector('#markdown-root');
182
+ if (!root)
183
+ return [];
184
+ return Array.from(root.querySelectorAll('img'))
185
+ .map((img) => img.getAttribute('src') || '')
186
+ .filter((v) => !!v);
187
+ }
188
+ getContent(params) {
189
+ const content = this.getAttribute('content') ?? '';
190
+ const s = Math.max(0, params?.start ?? 0);
191
+ const eInclusive = params?.end ?? (content.length - 1);
192
+ const e = Math.min(content.length, eInclusive + 1);
193
+ const slice = e >= s ? content.slice(s, e) : '';
194
+ return { content: slice };
195
+ }
196
+ pauseAnimation() {
197
+ this.setAttribute('animation-paused', 'true');
198
+ }
199
+ resumeAnimation(params) {
200
+ if (params?.animationStep !== undefined) {
201
+ this.setAttribute('initial-animation-step', String(params.animationStep));
202
+ }
203
+ if (this.getAttribute('animation-type') !== 'typewriter') {
204
+ this.setAttribute('animation-type', 'typewriter');
205
+ }
206
+ const velocity = this.getAttribute('animation-velocity');
207
+ if (!velocity || Number(velocity) <= 0) {
208
+ this.setAttribute('animation-velocity', '40');
209
+ }
210
+ this.removeAttribute('animation-paused');
211
+ }
212
+ getSelectedText() {
213
+ const root = this.shadowRoot?.querySelector('#markdown-root');
214
+ if (!root)
215
+ return '';
216
+ const result = getSelectionForRoot(this, root);
217
+ return result?.range.toString() ?? '';
218
+ }
219
+ getTextBoundingRect(params) {
220
+ const root = this.shadowRoot?.querySelector('#markdown-root');
221
+ if (!root)
222
+ return null;
223
+ const doc = this.ownerDocument;
224
+ const createRangeByChar = (s, e) => {
225
+ const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT);
226
+ let pos = 0;
227
+ let startNode = null;
228
+ let startOffset = 0;
229
+ let endNode = null;
230
+ let endOffset = 0;
231
+ let node = walker.nextNode();
232
+ while (node) {
233
+ const len = node.nodeValue?.length ?? 0;
234
+ if (!startNode && pos + len >= s) {
235
+ startNode = node;
236
+ startOffset = s - pos;
237
+ }
238
+ if (pos + len >= e) {
239
+ endNode = node;
240
+ endOffset = e - pos;
241
+ break;
242
+ }
243
+ pos += len;
244
+ node = walker.nextNode();
245
+ }
246
+ if (!startNode)
247
+ return null;
248
+ if (!endNode) {
249
+ endNode = startNode;
250
+ endOffset = startOffset;
251
+ }
252
+ const r = doc.createRange();
253
+ r.setStart(startNode, Math.max(0, Math.min(startOffset, startNode.length)));
254
+ r.setEnd(endNode, Math.max(0, Math.min(endOffset, endNode.length)));
255
+ return r;
256
+ };
257
+ if (params?.start !== undefined || params?.end !== undefined) {
258
+ const s = Math.max(0, params?.start ?? 0);
259
+ const e = Math.max(s, params?.end ?? s);
260
+ const rangeOffsets = params?.indexType === 'source'
261
+ ? mapSourceRangeToCharRange(this.getAttribute('content') ?? '', root.textContent ?? '', s, e)
262
+ : { start: s, end: e };
263
+ if (!rangeOffsets)
264
+ return null;
265
+ const r = createRangeByChar(rangeOffsets.start, rangeOffsets.end);
266
+ if (!r)
267
+ return null;
268
+ return { boundingRect: r.getBoundingClientRect() };
269
+ }
270
+ const result = getSelectionForRoot(this, root);
271
+ if (!result)
272
+ return null;
273
+ return { boundingRect: result.range.getBoundingClientRect() };
274
+ }
275
+ setTextSelection(params) {
276
+ const doc = this.ownerDocument;
277
+ const getRangeAtPoint = (x, y) => {
278
+ if (doc.caretRangeFromPoint)
279
+ return doc.caretRangeFromPoint(x, y);
280
+ if (doc.caretPositionFromPoint) {
281
+ const pos = doc.caretPositionFromPoint(x, y);
282
+ if (!pos)
283
+ return null;
284
+ const r = this.ownerDocument.createRange();
285
+ r.setStart(pos.offsetNode, pos.offset);
286
+ r.collapse(true);
287
+ return r;
288
+ }
289
+ return null;
290
+ };
291
+ const r1 = getRangeAtPoint(params.startX, params.startY);
292
+ const r2 = getRangeAtPoint(params.endX, params.endY);
293
+ if (r1 && r2) {
294
+ const sel = getPreferredSelectionTarget(this);
295
+ if (!sel)
296
+ return;
297
+ sel.removeAllRanges();
298
+ const range = this.ownerDocument.createRange();
299
+ range.setStart(r1.startContainer, r1.startOffset);
300
+ range.setEnd(r2.startContainer, r2.startOffset);
301
+ sel.addRange(range);
302
+ }
303
+ }
304
+ getParseResult(params) {
305
+ const root = this.shadowRoot?.querySelector('#markdown-root');
306
+ if (!root)
307
+ return {};
308
+ const text = root.textContent || '';
309
+ const result = {};
310
+ const doc = this.ownerDocument;
311
+ const calcOffset = (node) => {
312
+ const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT);
313
+ let pos = 0;
314
+ let start = -1;
315
+ let end = -1;
316
+ let current = walker.nextNode();
317
+ while (current) {
318
+ const len = (current.nodeValue || '').length;
319
+ if (current === node || node.contains(current)) {
320
+ if (start < 0) {
321
+ start = pos;
322
+ }
323
+ end = pos + len;
324
+ }
325
+ pos += len;
326
+ current = walker.nextNode();
327
+ }
328
+ return {
329
+ start: Math.max(0, Math.min(start, text.length)),
330
+ end: Math.max(0, Math.min(end, text.length)),
331
+ };
332
+ };
333
+ for (const tag of params.tags) {
334
+ const nodes = Array.from(root.querySelectorAll(tag));
335
+ result[tag] = nodes.map((el) => calcOffset(el));
336
+ }
337
+ return result;
338
+ }
339
+ static {
340
+ __runInitializers(_classThis, _classExtraInitializers);
341
+ }
342
+ };
343
+ return XMarkdown = _classThis;
344
+ })();
345
+ export { XMarkdown };
346
+ //# sourceMappingURL=XMarkdown.js.map
@@ -0,0 +1,35 @@
1
+ import type { XMarkdown } from './XMarkdown.js';
2
+ export type MarkdownStyleMap = Record<string, Record<string, unknown>>;
3
+ export type MarkdownStyleConfig = MarkdownStyleMap & {
4
+ truncation?: {
5
+ content?: string;
6
+ truncationType?: 'text' | 'view';
7
+ };
8
+ typewriterCursor?: {
9
+ customCursor?: string;
10
+ verticalAlign?: string;
11
+ };
12
+ };
13
+ export declare const parseMarkdownStyle: (value: string | null | undefined) => MarkdownStyleConfig;
14
+ export declare const serializeMarkdownStyle: (value: string | MarkdownStyleConfig | null | undefined) => string | null;
15
+ export declare class XMarkdownAttributes {
16
+ #private;
17
+ static observedAttributes: string[];
18
+ constructor(dom: XMarkdown);
19
+ connectedCallback(): void;
20
+ dispose(): void;
21
+ _handleContent(newVal: string | null): void;
22
+ _handleMarkdownEffect(newVal: string | null): void;
23
+ _handleMarkdownStyle(newVal: string | null): void;
24
+ _handleContentId(newVal: string | null): void;
25
+ _handleTextSelection(newVal: string | null): void;
26
+ _handleAnimationType(newVal: string | null): void;
27
+ _handleAnimationVelocity(newVal: string | null): void;
28
+ _handleAnimationPaused(newVal: string | null): void;
29
+ _handleInitialAnimationStep(newVal: string | null): void;
30
+ _handleContentComplete(newVal: string | null): void;
31
+ _handleTypewriterDynamicHeight(newVal: string | null): void;
32
+ _handleTypewriterHeightTransition(newVal: string | null): void;
33
+ _handleTextMaxline(newVal: string | null): void;
34
+ _handleContentRange(newVal: string | null): void;
35
+ }