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

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