@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.
@@ -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
+ }