@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,148 +0,0 @@
1
- import { LightningElement, api, track } from "lwc";
2
-
3
- export default class MarkdownEditor extends LightningElement {
4
- @api fetchUrl: string = "";
5
- @api saveUrl: string = "";
6
- @api filePath: string = "";
7
- @api htmlContent: string = "";
8
-
9
- @track isEditing: boolean = false;
10
- @track editorContent: string = "";
11
- @track isLoading: boolean = false;
12
- @track saveStatus: "idle" | "saving" | "success" | "error" = "idle";
13
-
14
- // Event Handlers
15
- async handleEdit() {
16
- if (!this.fetchUrl) {
17
- console.error("Fetch URL not configured");
18
- return;
19
- }
20
-
21
- this.isLoading = true;
22
- this.saveStatus = "idle";
23
-
24
- try {
25
- const response = await fetch(this.fetchUrl, {
26
- method: "GET",
27
- headers: {
28
- "Content-Type": "application/json"
29
- }
30
- });
31
-
32
- if (response.ok) {
33
- const data = await response.json();
34
- this.editorContent = data.content || data.text || data.body || "";
35
- this.isEditing = true;
36
- } else {
37
- throw new Error(`Failed to fetch content: ${response.status}`);
38
- }
39
- } catch (error) {
40
- console.error("Failed to fetch content:", error);
41
- this.saveStatus = "error";
42
- } finally {
43
- this.isLoading = false;
44
- }
45
- }
46
-
47
- handleCancel() {
48
- this.isEditing = false;
49
- this.saveStatus = "idle";
50
- this.editorContent = "";
51
- }
52
-
53
- handleContentChange(event: Event) {
54
- const target = event.target as HTMLTextAreaElement;
55
- this.editorContent = target.value;
56
- }
57
-
58
- async handleSave() {
59
- if (!this.saveUrl) {
60
- console.error("Save URL not configured");
61
- this.saveStatus = "error";
62
- return;
63
- }
64
-
65
- this.saveStatus = "saving";
66
- this.isLoading = true;
67
-
68
- try {
69
- const response = await fetch(this.saveUrl, {
70
- method: "POST",
71
- headers: {
72
- "Content-Type": "application/json"
73
- },
74
- body: JSON.stringify({
75
- content: this.editorContent,
76
- filePath: this.filePath
77
- })
78
- });
79
-
80
- if (response.ok) {
81
- this.saveStatus = "success";
82
- this.isEditing = false;
83
- this.editorContent = "";
84
-
85
- // Dispatch save success event
86
- this.dispatchEvent(
87
- new CustomEvent("saved", {
88
- detail: {
89
- success: true,
90
- content: this.editorContent,
91
- filePath: this.filePath
92
- }
93
- })
94
- );
95
- } else {
96
- throw new Error(`Save failed: ${response.status}`);
97
- }
98
- } catch (error) {
99
- console.error("Save failed:", error);
100
- this.saveStatus = "error";
101
- } finally {
102
- this.isLoading = false;
103
- }
104
- }
105
-
106
- // Getters for UI state
107
- get showEditor() {
108
- return this.isEditing;
109
- }
110
-
111
- get showViewer() {
112
- return !this.isEditing;
113
- }
114
-
115
- get saveButtonDisabled() {
116
- return this.saveStatus === "saving" || this.isLoading || !this.editorContent.trim();
117
- }
118
-
119
- get statusMessage() {
120
- switch (this.saveStatus) {
121
- case "saving":
122
- return "Saving...";
123
- case "success":
124
- return "Saved successfully!";
125
- case "error":
126
- return "Save failed. Please try again.";
127
- default:
128
- return "";
129
- }
130
- }
131
-
132
- get statusClass() {
133
- switch (this.saveStatus) {
134
- case "saving":
135
- return "status-saving";
136
- case "success":
137
- return "status-success";
138
- case "error":
139
- return "status-error";
140
- default:
141
- return "";
142
- }
143
- }
144
-
145
- get hasHtmlContent() {
146
- return this.htmlContent && this.htmlContent.trim().length > 0;
147
- }
148
- }
@@ -1,185 +0,0 @@
1
- # Text Selection Search Component
2
-
3
- A Lightning Web Component that detects text selection on the page and shows a search popover for finding related content.
4
-
5
- ## Features
6
-
7
- - **Automatic Text Detection**: Detects text selection via mouse and keyboard
8
- - **Positioned Popover**: Shows search interface near the selected text
9
- - **Search Functionality**: Makes API calls to backend search endpoints
10
- - **Modern UI**: Clean, responsive interface with loading states
11
- - **Keyboard Navigation**: Escape key to close popover
12
- - **Event Handling**: Dispatches events for search results and clicks
13
- - **Customizable**: Configurable appearance and behavior
14
-
15
- ## Usage
16
-
17
- ### Basic Implementation
18
-
19
- ```html
20
- <doc-text-selection-search
21
- search-api-url="/api/search"
22
- placeholder="Search for..."
23
- popover-position="top"
24
- max-results="10">
25
- </doc-text-selection-search>
26
- ```
27
-
28
- ### With Event Listeners
29
-
30
- ```html
31
- <doc-text-selection-search
32
- search-api-url="/api/search"
33
- @searchresults="handleSearchResults"
34
- @resultclick="handleResultClick">
35
- </doc-text-selection-search>
36
- ```
37
-
38
- ```javascript
39
- handleSearchResults(event) {
40
- const { query, results, selectedText } = event.detail;
41
- console.log('Search results:', results);
42
- }
43
-
44
- handleResultClick(event) {
45
- const { result, query, selectedText } = event.detail;
46
- console.log('Result clicked:', result);
47
- // Navigate to result or perform action
48
- }
49
- ```
50
-
51
- ## API Reference
52
-
53
- ### Properties
54
-
55
- | Property | Type | Default | Description |
56
- |----------|------|---------|-------------|
57
- | `searchApiUrl` | string | `""` | The URL endpoint for the search API |
58
- | `placeholder` | string | `"Search for..."` | Placeholder text for the search input |
59
- | `popoverPosition` | `"top" \| "bottom"` | `"top"` | Position of popover relative to selected text |
60
- | `maxResults` | number | `10` | Maximum number of search results to display |
61
-
62
- ### Events
63
-
64
- | Event | Detail | Description |
65
- |-------|--------|-------------|
66
- | `searchresults` | `{ query, results, selectedText }` | Fired when search results are received |
67
- | `resultclick` | `{ result, query, selectedText }` | Fired when a search result is clicked |
68
-
69
- ### Backend API Format
70
-
71
- The component expects your backend API to:
72
-
73
- **Request (POST):**
74
- ```json
75
- {
76
- "query": "search term",
77
- "maxResults": 10,
78
- "selectedText": "originally selected text"
79
- }
80
- ```
81
-
82
- **Response:**
83
- ```json
84
- {
85
- "results": [
86
- {
87
- "title": "Result Title",
88
- "description": "Result description or snippet",
89
- "url": "https://example.com/result"
90
- }
91
- ]
92
- }
93
- ```
94
-
95
- ## Examples
96
-
97
- ### Knowledge Base Search
98
-
99
- ```html
100
- <doc-text-selection-search
101
- search-api-url="https://api.example.com/kb/search"
102
- placeholder="Search knowledge base..."
103
- popover-position="bottom"
104
- max-results="15">
105
- </doc-text-selection-search>
106
- ```
107
-
108
- ### Documentation Search
109
-
110
- ```html
111
- <doc-text-selection-search
112
- search-api-url="/api/docs/search"
113
- placeholder="Search documentation..."
114
- popover-position="top"
115
- max-results="8">
116
- </doc-text-selection-search>
117
- ```
118
-
119
- ### Custom Styling
120
-
121
- You can customize the appearance by overriding CSS classes:
122
-
123
- ```css
124
- .text-selection-search-popover {
125
- /* Custom popover styling */
126
- border-radius: 12px;
127
- box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
128
- }
129
-
130
- .result-item:hover {
131
- /* Custom hover effects */
132
- background: #f0f8ff;
133
- border-color: #0066cc;
134
- }
135
- ```
136
-
137
- ## Browser Support
138
-
139
- - Chrome 60+
140
- - Firefox 55+
141
- - Safari 12+
142
- - Edge 79+
143
-
144
- ## Accessibility
145
-
146
- - Keyboard navigation support (Escape to close)
147
- - Screen reader friendly
148
- - High contrast mode support
149
- - Focus management
150
-
151
- ## Performance Considerations
152
-
153
- - Event listeners are properly cleaned up on component destruction
154
- - Debounced text selection detection
155
- - Efficient DOM updates
156
- - Minimal re-renders
157
-
158
- ## Troubleshooting
159
-
160
- ### Common Issues
161
-
162
- 1. **Popover not appearing**: Ensure text selection is enabled and the component is properly mounted
163
- 2. **API calls failing**: Check the `searchApiUrl` and ensure CORS is configured
164
- 3. **Positioning issues**: The popover position calculation may need adjustment for complex layouts
165
-
166
- ### Debug Mode
167
-
168
- Enable console logging for debugging:
169
-
170
- ```javascript
171
- // Add to your component
172
- connectedCallback() {
173
- this.debug = true; // Enable debug logging
174
- }
175
- ```
176
-
177
- ## Contributing
178
-
179
- When contributing to this component:
180
-
181
- 1. Follow the existing code style and patterns
182
- 2. Add appropriate tests for new features
183
- 3. Update documentation for API changes
184
- 4. Ensure accessibility compliance
185
- 5. Test across different browsers and devices
@@ -1,285 +0,0 @@
1
- .text-selection-search-popover {
2
- background: white;
3
- border: 1px solid #e1e5e9;
4
- border-radius: 8px;
5
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
6
- padding: 0;
7
- min-width: 320px;
8
- max-width: 480px;
9
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10
- position: fixed; /* Changed from relative to fixed for better positioning */
11
- overflow: hidden;
12
- opacity: 1;
13
- visibility: visible;
14
- transition: opacity 0.1s ease, visibility 0.1s ease;
15
- z-index: 9999; /* Ensure it's above other content */
16
- }
17
-
18
- .text-selection-search-popover.hidden {
19
- opacity: 0;
20
- visibility: hidden;
21
- pointer-events: none;
22
- /* Remove the absolute positioning that was causing issues */
23
- }
24
-
25
- /* Header Styles */
26
- .popover-header {
27
- background: #f8f9fa;
28
- border-bottom: 1px solid #e9ecef;
29
- padding: 12px 16px;
30
- margin: 0;
31
- }
32
-
33
- .popover-title {
34
- margin: 0;
35
- font-size: 14px;
36
- font-weight: 600;
37
- color: #495057;
38
- text-transform: uppercase;
39
- letter-spacing: 0.5px;
40
- }
41
-
42
- /* Content Container */
43
- .text-selection-search-popover > *:not(.popover-header):not(.popover-close) {
44
- padding: 16px;
45
- }
46
-
47
- /* Section Labels */
48
- .section-label {
49
- display: block;
50
- font-weight: 600;
51
- font-size: 12px;
52
- color: #6c757d;
53
- margin-bottom: 6px;
54
- text-transform: uppercase;
55
- letter-spacing: 0.5px;
56
- }
57
-
58
- /* Selected Text Display */
59
- .selected-text-section {
60
- margin-bottom: 16px;
61
- padding-bottom: 12px;
62
- border-bottom: 1px solid #f1f3f4;
63
- }
64
-
65
- .selected-text-display {
66
- background: #f8f9fa;
67
- border: 1px solid #e9ecef;
68
- border-radius: 4px;
69
- padding: 8px 12px;
70
- font-size: 13px;
71
- line-height: 1.4;
72
- color: #495057;
73
- min-height: 32px;
74
- max-height: 60px;
75
- overflow-y: auto;
76
- word-wrap: break-word;
77
- font-style: italic;
78
- }
79
-
80
- /* Edit Form */
81
- .edit-form {
82
- margin-bottom: 16px;
83
- }
84
-
85
- .input-group {
86
- margin-bottom: 16px;
87
- }
88
-
89
- .input-group:last-child {
90
- margin-bottom: 0;
91
- }
92
-
93
- .input-with-button {
94
- display: flex;
95
- gap: 8px;
96
- align-items: flex-end;
97
- }
98
-
99
- .edit-input {
100
- flex: 1;
101
- padding: 12px 16px;
102
- border: 2px solid #e5e7eb;
103
- border-radius: 6px;
104
- font-size: 14px;
105
- transition: border-color 0.2s ease;
106
- background: white;
107
- min-height: 48px;
108
- }
109
-
110
- .edit-input:focus {
111
- outline: none;
112
- border-color: #0070d2;
113
- box-shadow: 0 0 0 3px rgba(0, 112, 210, 0.1);
114
- }
115
-
116
- .edit-input::placeholder {
117
- color: #9ca3af;
118
- }
119
-
120
- .edit-button {
121
- padding: 8px 16px;
122
- background: #0070d2;
123
- color: white;
124
- border: none;
125
- border-radius: 4px;
126
- font-weight: 500;
127
- font-size: 13px;
128
- cursor: pointer;
129
- transition: all 0.2s ease;
130
- white-space: nowrap;
131
- min-height: 48px;
132
- text-transform: uppercase;
133
- letter-spacing: 0.5px;
134
- }
135
-
136
- .edit-button:hover:not(:disabled) {
137
- background: #005fb2;
138
- transform: translateY(-1px);
139
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
140
- }
141
-
142
- .edit-button:active:not(:disabled) {
143
- transform: translateY(0);
144
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
145
- }
146
-
147
- .edit-button:disabled {
148
- background: #9ca3af;
149
- cursor: not-allowed;
150
- transform: none;
151
- box-shadow: none;
152
- }
153
-
154
- /* Loading State */
155
- .loading-state {
156
- display: flex;
157
- align-items: center;
158
- gap: 12px;
159
- padding: 12px;
160
- background: #f8f9fa;
161
- border-radius: 6px;
162
- margin-top: 12px;
163
- }
164
-
165
- .loading-text {
166
- font-size: 13px;
167
- color: #6c757d;
168
- }
169
-
170
- /* Error State */
171
- .error-state {
172
- padding: 12px;
173
- background: #fef2f2;
174
- border: 1px solid #fecaca;
175
- border-radius: 6px;
176
- margin-top: 12px;
177
- }
178
-
179
- .error-message {
180
- color: #dc2626;
181
- font-size: 13px;
182
- }
183
-
184
- /* Success State */
185
- .success-state {
186
- padding: 12px;
187
- background: #f0fdf4;
188
- border: 1px solid #bbf7d0;
189
- border-radius: 6px;
190
- margin-top: 12px;
191
- }
192
-
193
- .success-message {
194
- color: #16a34a;
195
- font-size: 13px;
196
- font-weight: 500;
197
- }
198
-
199
- /* Close Button */
200
- .popover-close {
201
- position: absolute;
202
- top: 8px;
203
- right: 8px;
204
- z-index: 10;
205
- }
206
-
207
- .close-button {
208
- background: rgba(108, 117, 125, 0.1);
209
- border: none;
210
- color: #6c757d;
211
- width: 24px;
212
- height: 24px;
213
- border-radius: 4px;
214
- display: flex;
215
- align-items: center;
216
- justify-content: center;
217
- cursor: pointer;
218
- font-size: 14px;
219
- font-weight: bold;
220
- transition: background-color 0.2s ease;
221
- }
222
-
223
- .close-button:hover {
224
- background: rgba(108, 117, 125, 0.2);
225
- }
226
-
227
- /* Responsive Design */
228
- @media (max-width: 480px) {
229
- .text-selection-search-popover {
230
- min-width: 280px;
231
- max-width: 90vw;
232
- }
233
-
234
- .input-with-button {
235
- flex-direction: column;
236
- align-items: stretch;
237
- }
238
-
239
- .edit-button {
240
- margin-top: 8px;
241
- }
242
-
243
- .popover-header {
244
- padding: 10px 12px;
245
- }
246
-
247
- .popover-title {
248
- font-size: 13px;
249
- }
250
- }
251
-
252
- /* Animation for popover appearance */
253
- @keyframes popoverFadeIn {
254
- from {
255
- opacity: 0;
256
- transform: translateY(-10px);
257
- }
258
- to {
259
- opacity: 1;
260
- transform: translateY(0);
261
- }
262
- }
263
-
264
- .text-selection-search-popover {
265
- animation: popoverFadeIn 0.2s ease-out;
266
- }
267
-
268
- /* Scrollbar styling for results */
269
- .results-list::-webkit-scrollbar {
270
- width: 6px;
271
- }
272
-
273
- .results-list::-webkit-scrollbar-track {
274
- background: #f1f1f1;
275
- border-radius: 3px;
276
- }
277
-
278
- .results-list::-webkit-scrollbar-thumb {
279
- background: #c1c1c1;
280
- border-radius: 3px;
281
- }
282
-
283
- .results-list::-webkit-scrollbar-thumb:hover {
284
- background: #a8a8a8;
285
- }
@@ -1,91 +0,0 @@
1
- <template>
2
- <!-- Text Selection Search Popover -->
3
- <template if:true={isVisible}>
4
- <div
5
- class="text-selection-search-popover"
6
- class:hidden={isHidden}
7
- style={popoverStyle}
8
- onkeydown={handleKeyDown}
9
- onclick={handlePopoverClick}
10
- >
11
- <!-- Header with Title -->
12
- <div class="popover-header">
13
- <h3 class="popover-title">Text Editor</h3>
14
- </div>
15
-
16
- <!-- Selected Text Display -->
17
- <div class="selected-text-section">
18
- <label class="section-label">Selected Text</label>
19
- <div class="selected-text-display">
20
- {selectedText}
21
- </div>
22
- </div>
23
-
24
- <!-- Edit Form -->
25
- <div class="edit-form">
26
- <form onsubmit={handleSearchSubmit}>
27
- <div class="input-group">
28
- <label class="section-label">Edit to:</label>
29
- <div class="input-with-button">
30
- <input
31
- type="text"
32
- class="edit-input"
33
- value={searchQuery}
34
- onchange={handleSearchInputChange}
35
- onclick={handleInputClick}
36
- placeholder="Enter your edited text here..."
37
- autocomplete="off"
38
- autofocus
39
- />
40
- <dx-button
41
- type="submit"
42
- variant="primary"
43
- disabled={searchButtonDisabled}
44
- class="edit-button"
45
- >
46
- Edit
47
- </dx-button>
48
- </div>
49
- </div>
50
- </form>
51
- </div>
52
-
53
- <!-- Loading State -->
54
- <template if:true={isLoading}>
55
- <div class="loading-state">
56
- <dx-spinner
57
- size="small"
58
- alternative-text="Editing..."
59
- ></dx-spinner>
60
- <span class="loading-text">Processing your edit...</span>
61
- </div>
62
- </template>
63
-
64
- <!-- Error State -->
65
- <template if:true={hasError}>
66
- <div class="error-state">
67
- <span class="error-message">{errorMessage}</span>
68
- </div>
69
- </template>
70
-
71
- <!-- Success State -->
72
- <template if:true={isSuccess}>
73
- <div class="success-state">
74
- <span class="success-message">Text edited successfully!</span>
75
- </div>
76
- </template>
77
-
78
- <!-- Close Button -->
79
- <div class="popover-close">
80
- <dx-button
81
- onclick={hidePopover}
82
- variant="tertiary"
83
- class="close-button"
84
- title="Close editor"
85
- >
86
- ×
87
- </dx-button>
88
- </div>
89
- </div>
90
- </template>
91
- </template>