@lynx-js/web-elements 0.12.0 → 0.12.2
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 +57 -15
- package/dist/elements/XFoldViewNg/XFoldviewSlotNgTouchEventsHandler.js +29 -3
- package/dist/elements/XList/XList.d.ts +2 -0
- package/dist/elements/XList/XList.js +4 -0
- package/dist/elements/XMarkdown/XMarkdown.d.ts +43 -0
- package/dist/elements/XMarkdown/XMarkdown.js +346 -0
- package/dist/elements/XMarkdown/XMarkdownAttributes.d.ts +35 -0
- package/dist/elements/XMarkdown/XMarkdownAttributes.js +1233 -0
- package/dist/elements/XMarkdown/XMarkdownDeps.d.ts +2 -0
- package/dist/elements/XMarkdown/XMarkdownDeps.js +11 -0
- package/dist/elements/XMarkdown/index.d.ts +7 -0
- package/dist/elements/XMarkdown/index.js +11 -0
- package/dist/elements/all.d.ts +1 -0
- package/dist/elements/all.js +1 -0
- package/dist/elements/htmlTemplates.d.ts +2 -1
- package/dist/elements/htmlTemplates.js +87 -0
- package/index.css +1 -0
- package/package.json +15 -3
- package/src/elements/XList/x-list.css +31 -0
- package/src/elements/XMarkdown/x-markdown.css +15 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# @lynx-js/web-elements
|
|
2
2
|
|
|
3
|
+
## 0.12.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fix: xmarkdown create img incorrectly ([#2540](https://github.com/lynx-family/lynx-stack/pull/2540))
|
|
8
|
+
|
|
9
|
+
## 0.12.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- fix: XMarkdown slot created should not have prefix ([#2520](https://github.com/lynx-family/lynx-stack/pull/2520))
|
|
14
|
+
|
|
15
|
+
- feat: add x-markdown support ([#2412](https://github.com/lynx-family/lynx-stack/pull/2412))
|
|
16
|
+
|
|
17
|
+
Add opt-in support for the `x-markdown` element on Lynx Web, including
|
|
18
|
+
Markdown rendering together with its related styling, interaction, animation,
|
|
19
|
+
truncation, range rendering, and effect capabilities exposed through the
|
|
20
|
+
component API.
|
|
21
|
+
|
|
22
|
+
Update the `web-core`, `web-core-wasm`, and `web-mainthread-apis` runtime
|
|
23
|
+
paths to use the shared property-or-attribute setter from `web-constants`, so
|
|
24
|
+
custom elements such as `x-markdown` can receive structured property values
|
|
25
|
+
correctly instead of being forced through string-only attribute updates.
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
import '@lynx-js/web-elements/XMarkdown';
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- 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))
|
|
32
|
+
|
|
33
|
+
- fix: list cannot drag-scroll inside x-foldview-slot-ng ([#2507](https://github.com/lynx-family/lynx-stack/pull/2507))
|
|
34
|
+
|
|
35
|
+
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.
|
|
36
|
+
|
|
37
|
+
Fix: use `elementsFromPoint(clientX, clientY)` + `event.composedPath()` for Shadow DOM, and keep `previousPageX` updated during `touchmove`.
|
|
38
|
+
|
|
39
|
+
- fix: line-height of markdown-style should be added `px` ([#2509](https://github.com/lynx-family/lynx-stack/pull/2509))
|
|
40
|
+
|
|
41
|
+
- fix: list `bindscrolltolower` may not trigger because the lower threshold ([#2484](https://github.com/lynx-family/lynx-stack/pull/2484))
|
|
42
|
+
sentinel had no effective size or offset, causing the bottom
|
|
43
|
+
`IntersectionObserver` to miss the list boundary
|
|
44
|
+
|
|
3
45
|
## 0.12.0
|
|
4
46
|
|
|
5
47
|
### Minor Changes
|
|
@@ -80,14 +122,14 @@
|
|
|
80
122
|
|
|
81
123
|
- chore: migrate all @lynx-js/web-elements-\* packages into one ([#2057](https://github.com/lynx-family/lynx-stack/pull/2057))
|
|
82
124
|
|
|
83
|
-
|
|
125
|
+
#### Before
|
|
84
126
|
|
|
85
127
|
```js
|
|
86
128
|
import '@lynx-js/web-elements-template';
|
|
87
129
|
import '@lynx-js/web-elements-compat/LinearContainer';
|
|
88
130
|
```
|
|
89
131
|
|
|
90
|
-
|
|
132
|
+
#### After
|
|
91
133
|
|
|
92
134
|
```js
|
|
93
135
|
import '@lynx-js/web-elements/html-templates';
|
|
@@ -710,17 +752,17 @@
|
|
|
710
752
|
- 3547621: feat(web): use `<lynx-wrapper/>` to replace `<div style="display:content"/>`
|
|
711
753
|
- bed4f24: feat(web): implement <x-list> with list-type="single"
|
|
712
754
|
|
|
713
|
-
|
|
755
|
+
#### 1. RFC
|
|
714
756
|
|
|
715
757
|
https://github.com/lynx-wg/lynx-stack/issues/106
|
|
716
758
|
|
|
717
|
-
|
|
759
|
+
#### 2. Implementation differences with RFC
|
|
718
760
|
|
|
719
|
-
|
|
761
|
+
##### paging-enabled
|
|
720
762
|
|
|
721
763
|
deprecated, no need to implement
|
|
722
764
|
|
|
723
|
-
|
|
765
|
+
##### layoutcomplete
|
|
724
766
|
|
|
725
767
|
Triggered only after the first screen because using contentvisibilityautostatechange.
|
|
726
768
|
|
|
@@ -729,28 +771,28 @@
|
|
|
729
771
|
> This is because content is the parent container of list-item, and content is always visible before list-item.
|
|
730
772
|
> We cannot obtain the timing of all the successfully visible list-items on the screen, so 100ms is used to delay this behavior.
|
|
731
773
|
|
|
732
|
-
|
|
774
|
+
##### event-scrolltoedge
|
|
733
775
|
|
|
734
776
|
split bindscrolltoupperedge and bindscrooltolowerdge.
|
|
735
777
|
|
|
736
|
-
|
|
778
|
+
##### event-scrolltoupper/lower
|
|
737
779
|
|
|
738
780
|
Can be used with upper/lower-threshold-item-count.
|
|
739
781
|
|
|
740
782
|
Attention, when the number of x-list children changes, scrolltoupper/lower will be re-triggered (if the new node is on the screen).
|
|
741
783
|
|
|
742
|
-
|
|
784
|
+
##### getVisibleCells, layoutcomplete
|
|
743
785
|
|
|
744
786
|
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
787
|
|
|
746
|
-
|
|
788
|
+
#### 3. Tests not implemented
|
|
747
789
|
|
|
748
|
-
|
|
790
|
+
##### HTML Tests
|
|
749
791
|
|
|
750
792
|
1. event-layoutcomplete skipped webkit, firefox due to contentvisibilityautostatechange not propagate
|
|
751
793
|
2. get-visible-cells skipped webkit, firefox due to contentvisibilityautostatechange not propagate
|
|
752
794
|
|
|
753
|
-
|
|
795
|
+
##### React Tests
|
|
754
796
|
|
|
755
797
|
1. lynx.createQuery not supported.
|
|
756
798
|
|
|
@@ -815,7 +857,7 @@
|
|
|
815
857
|
- 8c6eeb9: fix(web): rename x-swiper-itrm to swiper-item
|
|
816
858
|
- 1fe49a2: feat(web): add custom element x-audio-tt
|
|
817
859
|
|
|
818
|
-
|
|
860
|
+
#### The behavior is different from the native x-audio-tt:
|
|
819
861
|
|
|
820
862
|
- When src changes, resources will not be loaded immediately. Resources will only be loaded when the play and prepare methods are triggered.
|
|
821
863
|
|
|
@@ -824,13 +866,13 @@
|
|
|
824
866
|
|
|
825
867
|
- The code returned by the binderror event does not include -4
|
|
826
868
|
|
|
827
|
-
|
|
869
|
+
#### Unimplemented properties:
|
|
828
870
|
|
|
829
871
|
- autoplay
|
|
830
872
|
- playertype
|
|
831
873
|
- experimental-ios-async-prepare
|
|
832
874
|
|
|
833
|
-
|
|
875
|
+
#### Unimplemented methods:
|
|
834
876
|
|
|
835
877
|
- requestFocus
|
|
836
878
|
- 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 =
|
|
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
|
|
119
|
-
|
|
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
|
+
}
|