@salesforcedevs/docs-components 1.17.10-hover-edit → 1.17.11-lwc-fix-alpha

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.
@@ -1,496 +0,0 @@
1
- import { LightningElement, api, track } from "lwc";
2
-
3
- export default class TextSelectionSearch extends LightningElement {
4
- @api searchApiUrl: string = "";
5
- @api repoUrl: string = "";
6
- @api placeholder: string = "Search for...";
7
- @api popoverPosition: "top" | "bottom" = "top";
8
- @api maxResults: number = 10;
9
-
10
- @track isVisible: boolean = false;
11
- @track isHidden: boolean = false;
12
- @track searchQuery: string = "";
13
- @track isLoading: boolean = false;
14
- @track isSuccess: boolean = false;
15
- @track errorMessage: string = "";
16
- @track popoverStyle: string = "";
17
-
18
- private selectedText: string = "";
19
- private selectionStart: number = 0;
20
- private selectionEnd: number = 0;
21
- private initialPopoverStyle: string = ""; // Store initial position
22
- private mutationObserver: MutationObserver | null = null;
23
-
24
- connectedCallback() {
25
- console.log('TextSelectionSearch: Component connected');
26
- console.log('TextSelectionSearch: Document ready state:', document.readyState);
27
- console.log('TextSelectionSearch: Current page URL:', window.location.href);
28
- this.setupTextSelectionListener();
29
- this.setupContentObserver();
30
-
31
- // Additional initialization for spec-based references
32
- if (this.isSpecBasedReferencePage()) {
33
- console.log('TextSelectionSearch: Detected spec-based reference page, setting up additional listeners');
34
- this.setupSpecBasedReferenceHandling();
35
- }
36
- }
37
-
38
- disconnectedCallback() {
39
- console.log('TextSelectionSearch: Component disconnected');
40
- this.removeTextSelectionListener();
41
- this.removeContentObserver();
42
- }
43
-
44
- // Setup text selection listener
45
- setupTextSelectionListener() {
46
- console.log('TextSelectionSearch: Setting up text selection listeners');
47
- try {
48
- // Use capture phase to ensure we get the events
49
- document.addEventListener("mouseup", (event) => this.handleTextSelection(event), true);
50
- document.addEventListener("keyup", (event) => this.handleTextSelection(event), true);
51
- console.log('TextSelectionSearch: Event listeners attached successfully');
52
- } catch (error) {
53
- console.error('TextSelectionSearch: Error setting up event listeners:', error);
54
- }
55
- }
56
-
57
- removeTextSelectionListener() {
58
- console.log('TextSelectionSearch: Removing text selection listeners');
59
- try {
60
- document.removeEventListener("mouseup", (event) => this.handleTextSelection(event), true);
61
- document.removeEventListener("keyup", (event) => this.handleTextSelection(event), true);
62
- console.log('TextSelectionSearch: Event listeners removed successfully');
63
- } catch (error) {
64
- console.error('TextSelectionSearch: Error removing event listeners:', error);
65
- }
66
- }
67
-
68
- // Setup observer to detect when content is dynamically loaded
69
- setupContentObserver() {
70
- console.log('TextSelectionSearch: Setting up content observer');
71
- try {
72
- this.mutationObserver = new MutationObserver((mutations) => {
73
- let shouldReinitialize = false;
74
-
75
- for (const mutation of mutations) {
76
- if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
77
- // Check if any added nodes contain text content
78
- for (const node of Array.from(mutation.addedNodes)) {
79
- if (node.nodeType === Node.ELEMENT_NODE) {
80
- const element = node as Element;
81
-
82
- // Check for spec-based reference content (.api-documentation)
83
- if (element.classList && element.classList.contains('api-documentation')) {
84
- console.log('TextSelectionSearch: Detected .api-documentation container');
85
- shouldReinitialize = true;
86
- break;
87
- }
88
-
89
- // Check for doc-amf-topic component
90
- if (element.tagName && element.tagName.toLowerCase() === 'doc-amf-topic') {
91
- console.log('TextSelectionSearch: Detected doc-amf-topic component');
92
- shouldReinitialize = true;
93
- break;
94
- }
95
-
96
- // Check for shadow DOM content in spec-based references
97
- if (this.isSpecBasedReferencePage() && element.shadowRoot) {
98
- console.log('TextSelectionSearch: Detected shadow DOM content');
99
- shouldReinitialize = true;
100
- break;
101
- }
102
-
103
- // Check for common content containers
104
- if (element.querySelector && (
105
- element.querySelector('p, h1, h2, h3, h4, h5, h6, li, td, th, div') ||
106
- element.matches('p, h1, h2, h3, h4, h5, h6, li, td, th, div')
107
- )) {
108
- shouldReinitialize = true;
109
- break;
110
- }
111
- }
112
- }
113
- }
114
- }
115
-
116
- if (shouldReinitialize) {
117
- console.log('TextSelectionSearch: Content detected, reinitializing listeners');
118
- // Small delay to ensure content is fully rendered
119
- setTimeout(() => {
120
- this.setupTextSelectionListener();
121
- }, 200); // Increased delay for spec-based references
122
- }
123
- });
124
-
125
- // Observe the entire document for content changes
126
- this.mutationObserver.observe(document.body, {
127
- childList: true,
128
- subtree: true
129
- });
130
-
131
- console.log('TextSelectionSearch: Content observer set up successfully');
132
- } catch (error) {
133
- console.error('TextSelectionSearch: Error setting up content observer:', error);
134
- }
135
- }
136
-
137
- removeContentObserver() {
138
- console.log('TextSelectionSearch: Removing content observer');
139
- if (this.mutationObserver) {
140
- this.mutationObserver.disconnect();
141
- this.mutationObserver = null;
142
- console.log('TextSelectionSearch: Content observer removed successfully');
143
- }
144
- }
145
-
146
- // Handle text selection
147
- handleTextSelection(event?: Event) {
148
- // If popover is visible, completely ignore all text selection events
149
- if (this.isVisible) {
150
- console.log('TextSelectionSearch: Popover is visible, ignoring text selection event');
151
- return;
152
- }
153
-
154
- console.log('TextSelectionSearch: Text selection event triggered');
155
- console.log('TextSelectionSearch: Event type:', event?.type);
156
- console.log('TextSelectionSearch: Event target:', event?.target);
157
-
158
- // Check if the event target is inside our popover
159
- if (event && event.target) {
160
- const target = event.target as Element;
161
- const popover = this.template.querySelector('.text-selection-search-popover');
162
- if (popover && popover.contains(target)) {
163
- console.log('TextSelectionSearch: Click inside popover, ignoring selection event');
164
- return;
165
- }
166
- }
167
-
168
- const selection = window.getSelection();
169
- console.log('TextSelectionSearch: Selection object:', selection);
170
- console.log('TextSelectionSearch: Selection string:', selection?.toString());
171
- console.log('TextSelectionSearch: Selection range count:', selection?.rangeCount);
172
-
173
- if (!selection || selection.toString().trim() === "") {
174
- // Only hide popover if it's currently visible and we're not clicking inside it
175
- if (this.isVisible) {
176
- console.log('TextSelectionSearch: No text selected, but popover is visible - keeping it open');
177
- return;
178
- }
179
- console.log('TextSelectionSearch: No text selected, hiding popover');
180
- this.hidePopover();
181
- return;
182
- }
183
-
184
- this.selectedText = selection.toString().trim();
185
- console.log('TextSelectionSearch: Selected text:', this.selectedText);
186
-
187
- // For spec-based references, check if selection is within the API documentation container
188
- if (this.isSpecBasedReferencePage()) {
189
- const apiDocContainer = document.querySelector('.api-documentation');
190
- if (apiDocContainer) {
191
- const range = selection.getRangeAt(0);
192
- const rect = range.getBoundingClientRect();
193
- const containerRect = apiDocContainer.getBoundingClientRect();
194
-
195
- // Check if the selection is within the API documentation container
196
- const isWithinContainer = (
197
- rect.top >= containerRect.top &&
198
- rect.bottom <= containerRect.bottom &&
199
- rect.left >= containerRect.left &&
200
- rect.right <= containerRect.right
201
- );
202
-
203
- console.log('TextSelectionSearch: Selection within API container:', isWithinContainer);
204
-
205
- if (!isWithinContainer) {
206
- console.log('TextSelectionSearch: Selection outside API container, ignoring');
207
- return;
208
- }
209
- }
210
- }
211
-
212
- if (this.selectedText.length > 0) {
213
- this.showPopover();
214
- }
215
- }
216
-
217
- // Show popover at selection position
218
- showPopover() {
219
- console.log('TextSelectionSearch: Showing popover');
220
- const selection = window.getSelection();
221
- if (!selection || selection.rangeCount === 0) {
222
- console.log('TextSelectionSearch: No selection range found');
223
- return;
224
- }
225
-
226
- const range = selection.getRangeAt(0);
227
- const rect = range.getBoundingClientRect();
228
-
229
- console.log('TextSelectionSearch: Selection rect:', rect);
230
- console.log('TextSelectionSearch: Window dimensions:', window.innerWidth, window.innerHeight);
231
- console.log('TextSelectionSearch: Scroll position:', window.scrollX, window.scrollY);
232
- console.log('TextSelectionSearch: Document height:', document.documentElement.scrollHeight);
233
-
234
- // Check if we're on a spec-based reference page
235
- const isSpecBased = this.isSpecBasedReferencePage();
236
- console.log('TextSelectionSearch: Is spec-based reference:', isSpecBased);
237
-
238
- // Calculate popover position with better positioning logic
239
- let popoverTop: number;
240
- let popoverLeft: number;
241
-
242
- if (isSpecBased) {
243
- // For spec-based references, we need to account for the dynamic content structure
244
- // The content might be inside shadow DOM or have different positioning
245
- const apiDocContainer = document.querySelector('.api-documentation');
246
- if (apiDocContainer) {
247
- const containerRect = apiDocContainer.getBoundingClientRect();
248
- console.log('TextSelectionSearch: API documentation container rect:', containerRect);
249
-
250
- // Position the popover relative to the selection within the container
251
- popoverLeft = Math.max(10, Math.min(window.innerWidth - 360, rect.left + (rect.width / 2) - 180));
252
-
253
- if (this.popoverPosition === "top") {
254
- // Position above the selection, but ensure it's within the container bounds
255
- popoverTop = Math.max(10, rect.top - 200);
256
- // If it would go above the container, position it below instead
257
- if (popoverTop < containerRect.top + 10) {
258
- popoverTop = Math.min(window.innerHeight - 250, rect.bottom + 20);
259
- }
260
- } else {
261
- // Position below the selection
262
- popoverTop = Math.min(window.innerHeight - 250, rect.bottom + 20);
263
- // If it would go below the container, position it above instead
264
- if (popoverTop > containerRect.bottom - 250) {
265
- popoverTop = Math.max(10, rect.top - 200);
266
- }
267
- }
268
- } else {
269
- // Fallback to standard positioning if container not found
270
- popoverLeft = Math.max(10, Math.min(window.innerWidth - 360, rect.left + (rect.width / 2) - 180));
271
- if (this.popoverPosition === "top") {
272
- popoverTop = Math.max(10, rect.top - 200);
273
- } else {
274
- popoverTop = Math.min(window.innerHeight - 250, rect.bottom + 20);
275
- }
276
- }
277
- } else {
278
- // Standard positioning for markdown-based references
279
- popoverLeft = Math.max(10, Math.min(window.innerWidth - 360, rect.left + (rect.width / 2) - 180));
280
-
281
- if (this.popoverPosition === "top") {
282
- // Position above the selection
283
- popoverTop = Math.max(10, rect.top - 200); // 200px above selection
284
- } else {
285
- // Position below the selection
286
- popoverTop = Math.min(window.innerHeight - 250, rect.bottom + 20);
287
- }
288
- }
289
-
290
- // Ensure the popover doesn't go off-screen
291
- if (popoverTop < 10) {
292
- popoverTop = 10;
293
- }
294
- if (popoverTop > window.innerHeight - 250) {
295
- popoverTop = window.innerHeight - 250;
296
- }
297
-
298
- this.popoverStyle = `position: fixed; top: ${popoverTop}px; left: ${popoverLeft}px; z-index: 9999; width: 350px;`;
299
- this.initialPopoverStyle = this.popoverStyle; // Store the initial position
300
- console.log('TextSelectionSearch: Popover style:', this.popoverStyle);
301
- console.log('TextSelectionSearch: Calculated position - top:', popoverTop, 'left:', popoverLeft);
302
-
303
- this.isVisible = true;
304
- this.isHidden = false;
305
- this.searchQuery = this.selectedText;
306
- this.errorMessage = "";
307
- this.isSuccess = false;
308
-
309
- console.log('TextSelectionSearch: Popover should now be visible');
310
- }
311
-
312
- // Hide popover
313
- hidePopover() {
314
- console.log('TextSelectionSearch: Hiding popover');
315
- this.isHidden = true;
316
-
317
- // Use setTimeout to ensure the hidden class is applied before hiding
318
- setTimeout(() => {
319
- this.isVisible = false;
320
- this.searchQuery = "";
321
- this.errorMessage = "";
322
- this.isSuccess = false;
323
- this.popoverStyle = ""; // Reset the positioning style
324
- this.initialPopoverStyle = ""; // Reset the initial position
325
-
326
- // Clear any text selection to prevent immediate re-showing
327
- const selection = window.getSelection();
328
- if (selection) {
329
- selection.removeAllRanges();
330
- }
331
- }, 100);
332
- }
333
-
334
- // Handle popover click to prevent closing
335
- handlePopoverClick(event: Event) {
336
- event.stopPropagation();
337
- }
338
-
339
- // Handle input click to prevent closing
340
- handleInputClick(event: Event) {
341
- event.stopPropagation();
342
- }
343
-
344
- // Handle search input change
345
- handleSearchInputChange(event: Event) {
346
- const target = event.target as HTMLInputElement;
347
- this.searchQuery = target.value;
348
- }
349
-
350
- // Handle search submission
351
- async handleSearchSubmit(event: Event) {
352
- event.preventDefault();
353
-
354
- if (!this.searchQuery.trim()) {
355
- this.errorMessage = "Please enter edited text";
356
- return;
357
- }
358
-
359
- if (!this.repoUrl.trim()) {
360
- this.errorMessage = "Repository URL not configured";
361
- return;
362
- }
363
-
364
- if (!this.searchApiUrl) {
365
- this.errorMessage = "Search API URL not configured";
366
- return;
367
- }
368
-
369
- await this.performSearch();
370
- }
371
-
372
- // Perform search API call
373
- async performSearch() {
374
- this.isLoading = true;
375
- this.errorMessage = "";
376
- this.isSuccess = false;
377
-
378
- try {
379
- const response = await fetch(this.searchApiUrl, {
380
- method: "POST",
381
- headers: {
382
- "Content-Type": "application/json"
383
- },
384
- body: JSON.stringify({
385
- repoUrl: this.repoUrl,
386
- selectedText: this.selectedText,
387
- editedText: this.searchQuery
388
- })
389
- });
390
-
391
- if (response.ok) {
392
- this.isSuccess = true;
393
-
394
- // Dispatch success event
395
- this.dispatchEvent(
396
- new CustomEvent("textedited", {
397
- detail: {
398
- repoUrl: this.repoUrl,
399
- selectedText: this.selectedText,
400
- editedText: this.searchQuery,
401
- success: true
402
- }
403
- })
404
- );
405
-
406
- // Auto-hide popover after success
407
- setTimeout(() => {
408
- this.hidePopover();
409
- }, 2000);
410
- } else {
411
- throw new Error(`Edit failed: ${response.status}`);
412
- }
413
- } catch (error) {
414
- console.error("Edit failed:", error);
415
- this.errorMessage = "Edit failed. Please try again.";
416
- } finally {
417
- this.isLoading = false;
418
- }
419
- }
420
-
421
- // Handle escape key
422
- handleKeyDown(event: KeyboardEvent) {
423
- if (event.key === "Escape") {
424
- this.hidePopover();
425
- }
426
- }
427
-
428
- // Getters for UI state
429
- get hasError() {
430
- return this.errorMessage.length > 0;
431
- }
432
-
433
- get searchButtonDisabled() {
434
- return this.isLoading || !this.searchQuery.trim();
435
- }
436
-
437
- // Find text content within shadow DOM elements
438
- findTextInShadowDOM(element: Element): boolean {
439
- // Check if element has shadow root
440
- if (element.shadowRoot) {
441
- const shadowContent = element.shadowRoot.querySelector('p, h1, h2, h3, h4, h5, h6, li, td, th, div, span');
442
- if (shadowContent) {
443
- console.log('TextSelectionSearch: Found content in shadow DOM');
444
- return true;
445
- }
446
- }
447
-
448
- // Recursively check child elements
449
- for (const child of Array.from(element.children)) {
450
- if (this.findTextInShadowDOM(child)) {
451
- return true;
452
- }
453
- }
454
-
455
- return false;
456
- }
457
-
458
- // Check if current page is a spec-based reference
459
- isSpecBasedReferencePage(): boolean {
460
- const url = window.location.href;
461
- const hasMetaParam = url.includes('?meta=');
462
- const isReferencePage = url.includes('/references/');
463
- console.log('TextSelectionSearch: URL analysis - hasMetaParam:', hasMetaParam, 'isReferencePage:', isReferencePage);
464
- return hasMetaParam && isReferencePage;
465
- }
466
-
467
- // Additional setup for spec-based references
468
- setupSpecBasedReferenceHandling() {
469
- // Wait for the page to be fully loaded
470
- if (document.readyState === 'loading') {
471
- document.addEventListener('DOMContentLoaded', () => {
472
- this.initializeForSpecBasedReference();
473
- });
474
- } else {
475
- this.initializeForSpecBasedReference();
476
- }
477
- }
478
-
479
- initializeForSpecBasedReference() {
480
- console.log('TextSelectionSearch: Initializing for spec-based reference');
481
-
482
- // Wait for the .api-documentation container to appear
483
- const checkForApiDocumentation = () => {
484
- const apiDocContainer = document.querySelector('.api-documentation');
485
- if (apiDocContainer) {
486
- console.log('TextSelectionSearch: Found .api-documentation container, setting up listeners');
487
- this.setupTextSelectionListener();
488
- } else {
489
- console.log('TextSelectionSearch: .api-documentation container not found yet, retrying...');
490
- setTimeout(checkForApiDocumentation, 500);
491
- }
492
- };
493
-
494
- checkForApiDocumentation();
495
- }
496
- }