@salesforcedevs/docs-components 0.0.11-edit → 0.0.13-chat

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,225 +0,0 @@
1
- <template>
2
- <!-- Edit File Trigger Button -->
3
- <button
4
- class="edit-file-trigger"
5
- onclick={handleEditClick}
6
- disabled={disabled}
7
- aria-label="Edit file"
8
- title="Edit file"
9
- >
10
- <svg
11
- class="edit-file-icon"
12
- viewBox="0 0 24 24"
13
- fill="none"
14
- stroke="currentColor"
15
- >
16
- <path
17
- stroke-linecap="round"
18
- stroke-linejoin="round"
19
- stroke-width="2"
20
- d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
21
- />
22
- </svg>
23
- <span class="edit-file-trigger-text">Edit</span>
24
- </button>
25
-
26
- <!-- Popover Overlay -->
27
- <template lwc:if={isPopoverOpen}>
28
- <div class={overlayClass} onclick={handleOverlayClick}>
29
- <div class={popoverClass}>
30
- <!-- Popover Header -->
31
- <div class="edit-file-header">
32
- <h2 class="edit-file-title">{project}</h2>
33
- <template lwc:if={fileName}>
34
- <p class="edit-file-filename">
35
- <svg
36
- class="edit-file-file-icon"
37
- viewBox="0 0 24 24"
38
- fill="none"
39
- stroke="currentColor"
40
- >
41
- <path
42
- stroke-linecap="round"
43
- stroke-linejoin="round"
44
- stroke-width="2"
45
- d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
46
- />
47
- </svg>
48
- {fileName}
49
- </p>
50
- </template>
51
- <button
52
- class="edit-file-close"
53
- onclick={handleCancel}
54
- aria-label="Close editor"
55
- >
56
- <svg
57
- class="edit-file-close-icon"
58
- viewBox="0 0 24 24"
59
- fill="none"
60
- stroke="currentColor"
61
- >
62
- <path
63
- stroke-linecap="round"
64
- stroke-linejoin="round"
65
- stroke-width="2"
66
- d="M6 18L18 6M6 6l12 12"
67
- />
68
- </svg>
69
- </button>
70
- </div>
71
-
72
- <!-- Popover Content -->
73
- <div class="edit-file-content">
74
- <!-- Loading State -->
75
- <template lwc:if={isLoading}>
76
- <div class="edit-file-loading">
77
- <div class="edit-file-spinner"></div>
78
- <p class="edit-file-loading-text">
79
- Loading file content...
80
- </p>
81
- </div>
82
- </template>
83
-
84
- <!-- Error Message -->
85
- <template lwc:if={errorMessage}>
86
- <div class="edit-file-error">
87
- <svg
88
- class="edit-file-error-icon"
89
- viewBox="0 0 24 24"
90
- fill="none"
91
- stroke="currentColor"
92
- >
93
- <circle cx="12" cy="12" r="10"></circle>
94
- <line x1="15" y1="9" x2="9" y2="15"></line>
95
- <line x1="9" y1="9" x2="15" y2="15"></line>
96
- </svg>
97
- <span class="edit-file-error-text">
98
- {errorMessage}
99
- </span>
100
- </div>
101
- </template>
102
-
103
- <!-- Success Message -->
104
- <template lwc:if={isSuccessState}>
105
- <div class="edit-file-success">
106
- <svg
107
- class="edit-file-success-icon"
108
- viewBox="0 0 24 24"
109
- fill="none"
110
- stroke="currentColor"
111
- >
112
- <path
113
- stroke-linecap="round"
114
- stroke-linejoin="round"
115
- stroke-width="2"
116
- d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
117
- />
118
- </svg>
119
- <div class="edit-file-success-content">
120
- <span class="edit-file-success-text">
121
- {successMessage}
122
- </span>
123
- <template lwc:if={prUrl}>
124
- <a
125
- href={prUrl}
126
- target="_blank"
127
- rel="noopener noreferrer"
128
- class="edit-file-pr-link"
129
- >
130
- View Pull Request
131
- </a>
132
- </template>
133
- </div>
134
- </div>
135
- </template>
136
-
137
- <!-- Content Editor -->
138
- <template lwc:if={fileContent}>
139
- <template lwc:if={isSuccessState}>
140
- <div class="edit-file-editor edit-file-editor_readonly">
141
- <label class="edit-file-label" for="file-content-readonly">
142
- File Content (Saved)
143
- </label>
144
- <textarea
145
- id="file-content-readonly"
146
- class="edit-file-textarea edit-file-textarea_readonly"
147
- readonly
148
- placeholder="Enter file content..."
149
- rows="20"
150
- spellcheck="false"
151
- ></textarea>
152
- </div>
153
- </template>
154
- <template lwc:else>
155
- <div class="edit-file-editor">
156
- <label class="edit-file-label" for="file-content">
157
- File Content
158
- </label>
159
- <textarea
160
- id="file-content"
161
- class="edit-file-textarea"
162
- oninput={handleContentChange}
163
- placeholder="Enter file content..."
164
- rows="20"
165
- spellcheck="false"
166
- ></textarea>
167
- </div>
168
- </template>
169
- </template>
170
- </div>
171
-
172
- <!-- Popover Footer -->
173
- <div class="edit-file-footer">
174
- <template lwc:if={isSuccessState}>
175
- <div class="edit-file-success-footer">
176
- <button
177
- class="edit-file-button edit-file-button_primary"
178
- onclick={handleCancel}
179
- >
180
- Close
181
- </button>
182
- </div>
183
- </template>
184
- <template lwc:else>
185
- <div class="edit-file-status">
186
- <template lwc:if={isSaving}>
187
- <div class="edit-file-saving">
188
- <div class="edit-file-mini-spinner"></div>
189
- <span>Saving...</span>
190
- </div>
191
- </template>
192
- <template lwc:elseif={isContentUnchanged}>
193
- <span class="edit-file-unchanged">No changes</span>
194
- </template>
195
- <template lwc:else>
196
- <span class="edit-file-modified">Modified</span>
197
- </template>
198
- </div>
199
-
200
- <div class="edit-file-actions">
201
- <button
202
- class="edit-file-button edit-file-button_secondary"
203
- onclick={handleCancel}
204
- disabled={isSaving}
205
- >
206
- Cancel
207
- </button>
208
- <button
209
- class={saveButtonClass}
210
- onclick={handleSave}
211
- disabled={isSaving}
212
- >
213
- <template lwc:if={isSaving}>
214
- <div class="edit-file-button-spinner"></div>
215
- Saving...
216
- </template>
217
- <template lwc:else>Save</template>
218
- </button>
219
- </div>
220
- </template>
221
- </div>
222
- </div>
223
- </div>
224
- </template>
225
- </template>
@@ -1,294 +0,0 @@
1
- import { LightningElement, api, track } from "lwc";
2
- import cx from "classnames";
3
- import { normalizeBoolean } from "dxUtils/normalizers";
4
-
5
- interface FetchApiResponse {
6
- success: boolean;
7
- data?: {
8
- content: string;
9
- filename: string;
10
- format: string;
11
- };
12
- repositoryUrl?: string;
13
- filePath?: string;
14
- href?: string;
15
- source?: string;
16
- timestamp?: string;
17
- message?: string;
18
- error?: string;
19
- }
20
-
21
- interface SaveApiResponse {
22
- success: boolean;
23
- message?: string;
24
- branchName?: string;
25
- prUrl?: string;
26
- error?: string;
27
- }
28
-
29
- export default class EditFile extends LightningElement {
30
- @api fileName: string = "";
31
- @api repoName: string = "";
32
- @api source: string = "";
33
- @api apiEndpoint: string = "/api/edit-file";
34
- @api project: string = "Edit File";
35
-
36
- @api
37
- get disabled() {
38
- return this._disabled;
39
- }
40
-
41
- set disabled(value) {
42
- this._disabled = normalizeBoolean(value);
43
- }
44
-
45
- @track isPopoverOpen: boolean = false;
46
- @track isLoading: boolean = false;
47
- @track isSaving: boolean = false;
48
- @track fileContent: string = "";
49
- @track originalContent: string = "";
50
- @track errorMessage: string = "";
51
- @track repositoryUrl: string = "";
52
- @track filePath: string = "";
53
- @track successMessage: string = "";
54
- @track prUrl: string = "";
55
-
56
- private _disabled: boolean = false;
57
-
58
- // API Configuration
59
- private static readonly FETCH_ENDPOINT =
60
- "https://1208ddd77328.ngrok-free.app/api/file-retrieval/retrieve";
61
- private static readonly SAVE_ENDPOINT = "https://1208ddd77328.ngrok-free.app/api/github/create-branch-commit-pr";
62
-
63
- get popoverClass() {
64
- return cx(
65
- "edit-file-popover",
66
- this.isPopoverOpen && "edit-file-popover_open"
67
- );
68
- }
69
-
70
- get overlayClass() {
71
- return cx(
72
- "edit-file-overlay",
73
- this.isPopoverOpen && "edit-file-overlay_open"
74
- );
75
- }
76
-
77
- get saveButtonClass() {
78
- return cx(
79
- "edit-file-button",
80
- "edit-file-button_primary",
81
- (this.isSaving || this.isContentUnchanged) &&
82
- "edit-file-button_disabled"
83
- );
84
- }
85
-
86
- get isContentUnchanged() {
87
- return this.fileContent === this.originalContent;
88
- }
89
-
90
- get isSuccessState() {
91
- return this.successMessage !== "";
92
- }
93
-
94
- // Open the edit file popover
95
- async handleEditClick() {
96
- if (this.disabled) {
97
- return;
98
- }
99
-
100
- this.isPopoverOpen = true;
101
- this.errorMessage = "";
102
- await this.fetchFileContent();
103
- }
104
-
105
- // Fetch file content from API
106
- private async fetchFileContent() {
107
- if (!this.fileName) {
108
- this.errorMessage = "No file specified to edit";
109
- return;
110
- }
111
-
112
- this.isLoading = true;
113
-
114
- try {
115
- const response = await fetch(EditFile.FETCH_ENDPOINT, {
116
- method: "POST",
117
- headers: {
118
- "Content-Type": "application/json"
119
- },
120
- body: JSON.stringify({
121
- source: this.source,
122
- href: this.fileName
123
- })
124
- });
125
-
126
- if (!response.ok) {
127
- throw new Error(`Failed to fetch file: ${response.status}`);
128
- }
129
-
130
- const data: FetchApiResponse = await response.json();
131
-
132
- if (data.success && data.data !== undefined) {
133
- this.fileContent = data.data.content;
134
- this.originalContent = data.data.content;
135
- this.repositoryUrl = data.repositoryUrl || "";
136
- this.filePath = data.filePath || "";
137
- this.errorMessage = "";
138
-
139
- // Set textarea value directly using ref
140
- this.updateTextareaValue();
141
- } else {
142
- this.errorMessage =
143
- data.error || data.message || "Failed to load file content";
144
- }
145
- } catch (error) {
146
- console.error("Error fetching file content:", error);
147
- this.errorMessage =
148
- error instanceof Error
149
- ? error.message
150
- : "Failed to connect to server";
151
- } finally {
152
- this.isLoading = false;
153
- }
154
- }
155
-
156
- // Handle content change in textarea
157
- handleContentChange(event: Event) {
158
- const target = event.target as HTMLTextAreaElement;
159
- this.fileContent = target.value;
160
- }
161
-
162
- // Handle cancel action
163
- handleCancel() {
164
- this.isPopoverOpen = false;
165
- this.fileContent = "";
166
- this.originalContent = "";
167
- this.errorMessage = "";
168
- this.successMessage = "";
169
- this.prUrl = "";
170
- this.repositoryUrl = "";
171
- this.filePath = "";
172
- this.isLoading = false;
173
- this.isSaving = false;
174
-
175
- // Dispatch cancel event
176
- this.dispatchEvent(
177
- new CustomEvent("editcancel", {
178
- detail: { fileName: this.fileName }
179
- })
180
- );
181
- }
182
-
183
- // Handle save action
184
- async handleSave() {
185
- if (this.isSaving || this.isContentUnchanged || this.disabled) {
186
- return;
187
- }
188
-
189
- this.isSaving = true;
190
- this.errorMessage = "";
191
-
192
- try {
193
- const response = await fetch(EditFile.SAVE_ENDPOINT, {
194
- method: "POST",
195
- headers: {
196
- "Content-Type": "application/json"
197
- },
198
- body: JSON.stringify({
199
- repositoryUrl: this.repositoryUrl,
200
- filePath: this.filePath,
201
- content: this.fileContent
202
- })
203
- });
204
-
205
- if (!response.ok) {
206
- throw new Error(`Failed to save file: ${response.status}`);
207
- }
208
-
209
- const data: SaveApiResponse = await response.json();
210
-
211
- if (data.success) {
212
- this.originalContent = this.fileContent;
213
- this.successMessage = data.message || "File saved successfully";
214
- this.prUrl = data.prUrl || "";
215
-
216
- // Dispatch success event
217
- this.dispatchEvent(
218
- new CustomEvent("editsuccess", {
219
- detail: {
220
- fileName: this.fileName,
221
- content: this.fileContent,
222
- message: this.successMessage,
223
- prUrl: this.prUrl,
224
- branchName: data.branchName
225
- }
226
- })
227
- );
228
- } else {
229
- this.errorMessage =
230
- data.error || data.message || "Failed to save file";
231
- }
232
- } catch (error) {
233
- console.error("Error saving file:", error);
234
- this.errorMessage =
235
- error instanceof Error
236
- ? error.message
237
- : "Failed to connect to server";
238
- } finally {
239
- this.isSaving = false;
240
- }
241
- }
242
-
243
- // Handle overlay click to close popover
244
- handleOverlayClick(event: Event) {
245
- if (event.target === event.currentTarget) {
246
- this.handleCancel();
247
- }
248
- }
249
-
250
- // Handle escape key to close popover
251
- handleKeyDown(event: KeyboardEvent) {
252
- if (event.key === "Escape" && this.isPopoverOpen) {
253
- this.handleCancel();
254
- }
255
- }
256
-
257
- // Update textarea value using ref
258
- private updateTextareaValue() {
259
- // Use setTimeout to ensure DOM is updated
260
- setTimeout(() => {
261
- const editableTextarea = this.template.querySelector(
262
- "#file-content"
263
- ) as HTMLTextAreaElement;
264
- const readonlyTextarea = this.template.querySelector(
265
- "#file-content-readonly"
266
- ) as HTMLTextAreaElement;
267
-
268
- if (editableTextarea && this.fileContent !== undefined) {
269
- editableTextarea.value = this.fileContent;
270
- }
271
-
272
- if (readonlyTextarea && this.fileContent !== undefined) {
273
- readonlyTextarea.value = this.fileContent;
274
- }
275
- }, 0);
276
- }
277
-
278
- connectedCallback() {
279
- // Add escape key listener
280
- document.addEventListener("keydown", this.handleKeyDown.bind(this));
281
- }
282
-
283
- renderedCallback() {
284
- // Ensure textarea value is set correctly when component renders
285
- if (this.isPopoverOpen && this.fileContent && !this.isLoading) {
286
- this.updateTextareaValue();
287
- }
288
- }
289
-
290
- disconnectedCallback() {
291
- // Remove escape key listener
292
- document.removeEventListener("keydown", this.handleKeyDown.bind(this));
293
- }
294
- }