@salesforcedevs/docs-components 1.21.1-redocly2 → 1.21.1-redocly4
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.
package/package.json
CHANGED
|
@@ -35,6 +35,8 @@ export default class RedocReference extends LightningElement {
|
|
|
35
35
|
private redocInitialized = false;
|
|
36
36
|
|
|
37
37
|
private docHeaderElement: Element | null = null;
|
|
38
|
+
private docPhaseWrapperElement: Element | null = null;
|
|
39
|
+
private lastSidebarTop = 0;
|
|
38
40
|
|
|
39
41
|
showError = false;
|
|
40
42
|
|
|
@@ -49,12 +51,11 @@ export default class RedocReference extends LightningElement {
|
|
|
49
51
|
typeof value === "string" ? JSON.parse(value) : value;
|
|
50
52
|
this._referenceConfig = refConfig;
|
|
51
53
|
} catch (error) {
|
|
52
|
-
this.
|
|
53
|
-
|
|
54
|
+
this._referenceConfig = { refList: [] };
|
|
55
|
+
this.showErrorUI(
|
|
54
56
|
"Failed to parse reference configuration data",
|
|
55
57
|
error
|
|
56
58
|
);
|
|
57
|
-
this._referenceConfig = { refList: [] };
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -74,6 +75,7 @@ export default class RedocReference extends LightningElement {
|
|
|
74
75
|
|
|
75
76
|
renderedCallback(): void {
|
|
76
77
|
if (!this.redocInitialized) {
|
|
78
|
+
this.redocInitialized = true;
|
|
77
79
|
this.initializeRedoc();
|
|
78
80
|
}
|
|
79
81
|
}
|
|
@@ -81,13 +83,42 @@ export default class RedocReference extends LightningElement {
|
|
|
81
83
|
disconnectedCallback(): void {
|
|
82
84
|
window.removeEventListener("scroll", this.handleScrollAndResize);
|
|
83
85
|
window.removeEventListener("resize", this.handleScrollAndResize);
|
|
86
|
+
|
|
87
|
+
this.handleScrollAndResize?.cancel?.();
|
|
88
|
+
|
|
89
|
+
// Clean up cached DOM element references to prevent memory leaks
|
|
90
|
+
this.docHeaderElement = null;
|
|
91
|
+
this.docPhaseWrapperElement = null;
|
|
84
92
|
}
|
|
85
93
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
this.
|
|
94
|
+
// Displays error UI and logs error message for debugging
|
|
95
|
+
private showErrorUI(message: string, error?: any): void {
|
|
96
|
+
this.showError = true;
|
|
97
|
+
console.error(message, error);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private getRedocContainer(): HTMLElement | null {
|
|
101
|
+
return document.querySelector(".redoc-container");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private getSelectedReference(): ReferenceItem | null {
|
|
105
|
+
return (
|
|
106
|
+
this._referenceConfig?.refList?.find((ref) => ref.isSelected) ||
|
|
107
|
+
this._referenceConfig?.refList?.[0]
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private getDocPhaseInfo(): string | null {
|
|
112
|
+
if (this._parentDocPhaseInfo) {
|
|
113
|
+
return this._parentDocPhaseInfo;
|
|
114
|
+
}
|
|
115
|
+
const selectedRef = this.getSelectedReference();
|
|
116
|
+
return selectedRef?.docPhase
|
|
117
|
+
? JSON.stringify(selectedRef.docPhase)
|
|
118
|
+
: null;
|
|
89
119
|
}
|
|
90
120
|
|
|
121
|
+
// Extracts numeric value from CSS custom properties
|
|
91
122
|
private getGlobalCSSVariableValue(variableName: string): number {
|
|
92
123
|
const value = getComputedStyle(
|
|
93
124
|
document.documentElement
|
|
@@ -95,11 +126,11 @@ export default class RedocReference extends LightningElement {
|
|
|
95
126
|
return parseInt(value, 10) || 0;
|
|
96
127
|
}
|
|
97
128
|
|
|
129
|
+
/*
|
|
130
|
+
** Since we could not use --dx-g-global-header-height as getPropertyValue returns a calc expression,
|
|
131
|
+
** we are using the respective CSS variables to calculate the height.
|
|
132
|
+
*/
|
|
98
133
|
private getGlobalHeaderHeight(): number {
|
|
99
|
-
/*
|
|
100
|
-
** Since we could not use --dx-g-global-header-height as getPropertyValue returns a calc expression,
|
|
101
|
-
** we are using the respective CSS variables to calculate the height.
|
|
102
|
-
*/
|
|
103
134
|
const rowHeight = this.getGlobalCSSVariableValue(
|
|
104
135
|
"--dx-g-global-header-nav-row-height"
|
|
105
136
|
);
|
|
@@ -109,59 +140,62 @@ export default class RedocReference extends LightningElement {
|
|
|
109
140
|
return rowHeight * rowCount;
|
|
110
141
|
}
|
|
111
142
|
|
|
143
|
+
// Gets doc header height with element caching for performance
|
|
112
144
|
private getDocHeaderHeight(): number {
|
|
113
145
|
if (!this.docHeaderElement) {
|
|
114
146
|
this.docHeaderElement =
|
|
115
147
|
document.querySelector(".sticky-doc-header");
|
|
116
148
|
}
|
|
117
|
-
return this.docHeaderElement?.getBoundingClientRect()
|
|
149
|
+
return this.docHeaderElement?.getBoundingClientRect()?.height || 0;
|
|
118
150
|
}
|
|
119
151
|
|
|
120
|
-
|
|
152
|
+
// Gets doc phase wrapper height with element caching for performance
|
|
153
|
+
private getDocPhaseWrapperHeight(): number {
|
|
154
|
+
if (!this.docPhaseWrapperElement) {
|
|
155
|
+
this.docPhaseWrapperElement =
|
|
156
|
+
document.querySelector(".doc-phase-wrapper");
|
|
157
|
+
}
|
|
158
|
+
return (
|
|
159
|
+
this.docPhaseWrapperElement?.getBoundingClientRect()?.height || 0
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
calculateHeaderOffset = () => {
|
|
164
|
+
const globalHeaderHeight = this.getGlobalHeaderHeight();
|
|
165
|
+
const docHeaderHeight = this.getDocHeaderHeight();
|
|
166
|
+
return globalHeaderHeight + docHeaderHeight;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// Dynamic scroll offset calculation that Redoc will call
|
|
170
|
+
calculateScrollYOffset = () => {
|
|
171
|
+
const headerOffset = this.calculateHeaderOffset();
|
|
172
|
+
const phaseHeight = this.getDocPhaseWrapperHeight();
|
|
173
|
+
return headerOffset + phaseHeight;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// Updates sidebar positioning based on current header heights
|
|
177
|
+
private updateSidebarPosition = () => {
|
|
121
178
|
requestAnimationFrame(() => {
|
|
122
179
|
const redocContainer = this.getRedocContainer();
|
|
123
180
|
if (!redocContainer) {
|
|
124
181
|
return;
|
|
125
182
|
}
|
|
126
183
|
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
184
|
+
const currentSidebarTop = this.calculateHeaderOffset();
|
|
185
|
+
if (currentSidebarTop === this.lastSidebarTop) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
130
188
|
|
|
131
|
-
|
|
189
|
+
const sidebarTopValue = `${currentSidebarTop}px`;
|
|
132
190
|
this.template.host.style.setProperty(
|
|
133
191
|
"--doc-c-redoc-sidebar-top",
|
|
134
|
-
|
|
192
|
+
sidebarTopValue
|
|
135
193
|
);
|
|
194
|
+
this.lastSidebarTop = currentSidebarTop;
|
|
136
195
|
});
|
|
137
196
|
};
|
|
138
197
|
|
|
139
|
-
|
|
140
|
-
SCROLL_THROTTLE_DELAY,
|
|
141
|
-
() => !this.showError && this.handleLayoutChange()
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
private getDocPhaseInfo(): string | null {
|
|
145
|
-
if (this._parentDocPhaseInfo) {
|
|
146
|
-
return this._parentDocPhaseInfo;
|
|
147
|
-
}
|
|
148
|
-
const selectedRef = this.getSelectedReference();
|
|
149
|
-
return selectedRef?.docPhase
|
|
150
|
-
? JSON.stringify(selectedRef.docPhase)
|
|
151
|
-
: null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
private getSelectedReference(): ReferenceItem | null {
|
|
155
|
-
return (
|
|
156
|
-
this._referenceConfig?.refList?.find((ref) => ref.isSelected) ||
|
|
157
|
-
this._referenceConfig?.refList?.[0]
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
private getRedocContainer(): HTMLElement | null {
|
|
162
|
-
return document.querySelector(".redoc-container");
|
|
163
|
-
}
|
|
164
|
-
|
|
198
|
+
// Updates browser URL while preserving query params and hash
|
|
165
199
|
private updateUrlWithReference(selectedRef: ReferenceItem): void {
|
|
166
200
|
if (selectedRef?.href) {
|
|
167
201
|
const parentReferencePath = selectedRef.href;
|
|
@@ -176,46 +210,59 @@ export default class RedocReference extends LightningElement {
|
|
|
176
210
|
}
|
|
177
211
|
}
|
|
178
212
|
|
|
213
|
+
private handleScrollAndResize = throttle(
|
|
214
|
+
SCROLL_THROTTLE_DELAY,
|
|
215
|
+
() => !this.showError && this.updateSidebarPosition()
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// Initializes Redoc library with selected reference configuration
|
|
179
219
|
private async initializeRedoc(): Promise<void> {
|
|
180
220
|
try {
|
|
181
|
-
this.redocInitialized = true;
|
|
182
|
-
|
|
183
221
|
await this.waitForRedoc();
|
|
222
|
+
|
|
223
|
+
const redocContainer = this.getRedocContainer();
|
|
224
|
+
if (!redocContainer) {
|
|
225
|
+
this.showErrorUI("Redoc container is not found.");
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
184
229
|
const selectedRef = this.getSelectedReference();
|
|
185
230
|
if (selectedRef) {
|
|
186
231
|
this.updateUrlWithReference(selectedRef);
|
|
187
232
|
|
|
188
233
|
const specUrl = selectedRef.source;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
specUrl,
|
|
193
|
-
{
|
|
194
|
-
expandResponses: "200,400"
|
|
195
|
-
},
|
|
196
|
-
redocContainer,
|
|
197
|
-
(error: any) => {
|
|
198
|
-
if (error) {
|
|
199
|
-
this.showError = true;
|
|
200
|
-
console.error(
|
|
201
|
-
"Failed to show Reference UI using Redoc: ",
|
|
202
|
-
error
|
|
203
|
-
);
|
|
204
|
-
} else {
|
|
205
|
-
this.insertCustomLayoutElements();
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
);
|
|
209
|
-
} else {
|
|
210
|
-
this.showError = true;
|
|
234
|
+
if (!specUrl) {
|
|
235
|
+
this.showErrorUI("Spec URL not found.");
|
|
236
|
+
return;
|
|
211
237
|
}
|
|
238
|
+
|
|
239
|
+
window.Redoc.init(
|
|
240
|
+
specUrl,
|
|
241
|
+
{
|
|
242
|
+
// Auto-expand HTTP 200 and 400 response sections
|
|
243
|
+
expandResponses: "200,400",
|
|
244
|
+
// Dynamic scroll offset to account for headers
|
|
245
|
+
scrollYOffset: this.calculateScrollYOffset
|
|
246
|
+
},
|
|
247
|
+
redocContainer,
|
|
248
|
+
(error: any) => {
|
|
249
|
+
if (error) {
|
|
250
|
+
this.showErrorUI(
|
|
251
|
+
"Failed to show Reference UI using Redoc: ",
|
|
252
|
+
error
|
|
253
|
+
);
|
|
254
|
+
} else {
|
|
255
|
+
this.integrateCustomComponents();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
);
|
|
212
259
|
}
|
|
213
260
|
} catch (error) {
|
|
214
|
-
this.
|
|
215
|
-
console.error("Failed to load Redoc library:", error);
|
|
261
|
+
this.showErrorUI("Failed to load Redoc library:", error);
|
|
216
262
|
}
|
|
217
263
|
}
|
|
218
264
|
|
|
265
|
+
// Polls for Redoc library availability with timeout
|
|
219
266
|
private async waitForRedoc(timeout = ELEMENT_TIMEOUT): Promise<void> {
|
|
220
267
|
const success = await pollUntil(
|
|
221
268
|
() => !!window.Redoc,
|
|
@@ -230,39 +277,41 @@ export default class RedocReference extends LightningElement {
|
|
|
230
277
|
}
|
|
231
278
|
}
|
|
232
279
|
|
|
233
|
-
|
|
280
|
+
// Integrates custom elements (doc phase, footer, survey) into Redoc container
|
|
281
|
+
private async integrateCustomComponents(): Promise<void> {
|
|
234
282
|
try {
|
|
235
283
|
const redocContainer = this.getRedocContainer();
|
|
284
|
+
if (!redocContainer) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
236
287
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
redocContainer
|
|
240
|
-
);
|
|
241
|
-
apiContentDiv.setAttribute("lwc:dom", "manual");
|
|
288
|
+
const apiContentDiv = await this.waitForApiContent(redocContainer);
|
|
289
|
+
apiContentDiv.setAttribute("lwc:dom", "manual");
|
|
242
290
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
291
|
+
const docPhaseInfo = this.getDocPhaseInfo();
|
|
292
|
+
if (docPhaseInfo) {
|
|
293
|
+
this.insertDocPhase(apiContentDiv, docPhaseInfo);
|
|
294
|
+
}
|
|
247
295
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
296
|
+
if (typeof Sprig !== "undefined") {
|
|
297
|
+
this.insertSprigSurvey(apiContentDiv);
|
|
298
|
+
}
|
|
251
299
|
|
|
252
|
-
|
|
300
|
+
this.insertFooter(apiContentDiv);
|
|
253
301
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
302
|
+
// Wait for footer to be rendered before updating styles
|
|
303
|
+
requestAnimationFrame(() => {
|
|
304
|
+
this.updateRedocThirdColumnStyle(redocContainer);
|
|
305
|
+
|
|
306
|
+
// Fix initial hash scroll after doc phase insertion
|
|
307
|
+
this.handleInitialHashScrollFix();
|
|
308
|
+
});
|
|
260
309
|
} catch (error) {
|
|
261
|
-
this.
|
|
262
|
-
console.error("Failed to insert layout elements:", error);
|
|
310
|
+
this.showErrorUI("Failed to integrate custom components:", error);
|
|
263
311
|
}
|
|
264
312
|
}
|
|
265
313
|
|
|
314
|
+
// Waits for Redoc's API content element to be rendered
|
|
266
315
|
private async waitForApiContent(
|
|
267
316
|
container: HTMLElement
|
|
268
317
|
): Promise<HTMLElement> {
|
|
@@ -281,24 +330,25 @@ export default class RedocReference extends LightningElement {
|
|
|
281
330
|
return container.querySelector<HTMLElement>(".api-content")!;
|
|
282
331
|
}
|
|
283
332
|
|
|
333
|
+
// Creates and inserts doc phase component at container start
|
|
284
334
|
private insertDocPhase(container: HTMLElement, docPhaseInfo: string): void {
|
|
285
335
|
const wrapper = document.createElement("div");
|
|
286
336
|
wrapper.className = "doc-phase-wrapper";
|
|
287
|
-
|
|
288
337
|
container.insertBefore(wrapper, container.firstChild);
|
|
289
338
|
|
|
290
339
|
const docPhaseElement = createElement("doc-phase", { is: DocPhase });
|
|
291
340
|
Object.assign(docPhaseElement, { docPhaseInfo });
|
|
292
|
-
|
|
293
341
|
wrapper.appendChild(docPhaseElement);
|
|
294
342
|
}
|
|
295
343
|
|
|
344
|
+
// Appends footer component to container
|
|
296
345
|
private insertFooter(container: HTMLElement): void {
|
|
297
346
|
const footerElement = createElement("dx-footer", { is: DxFooter });
|
|
298
347
|
Object.assign(footerElement, { variant: "no-signup" });
|
|
299
348
|
container.appendChild(footerElement);
|
|
300
349
|
}
|
|
301
350
|
|
|
351
|
+
// Appends Sprig survey component to container
|
|
302
352
|
private insertSprigSurvey(container: HTMLElement): void {
|
|
303
353
|
const wrapper = document.createElement("div");
|
|
304
354
|
wrapper.className = "sprig-survey-wrapper";
|
|
@@ -310,24 +360,29 @@ export default class RedocReference extends LightningElement {
|
|
|
310
360
|
wrapper.appendChild(feedbackElement);
|
|
311
361
|
}
|
|
312
362
|
|
|
363
|
+
// Adjusts third column bottom position to prevent footer overlap
|
|
313
364
|
private updateRedocThirdColumnStyle(redocContainer: HTMLElement): void {
|
|
314
365
|
const footer = redocContainer.querySelector(
|
|
315
366
|
".redoc-wrap .api-content dx-footer"
|
|
316
367
|
) as HTMLElement;
|
|
317
|
-
|
|
368
|
+
if (!footer) {
|
|
369
|
+
console.warn(
|
|
370
|
+
"Footer element not found, skipping third column styling"
|
|
371
|
+
);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const redocThirdColumnElement = redocContainer.querySelector(
|
|
318
376
|
".redoc-wrap > div:last-child"
|
|
319
377
|
) as HTMLElement;
|
|
320
|
-
|
|
321
|
-
if (!thirdColumnContainer || !footer) {
|
|
378
|
+
if (!redocThirdColumnElement) {
|
|
322
379
|
console.warn(
|
|
323
|
-
|
|
324
|
-
? "Redoc Third column container not found"
|
|
325
|
-
: "Footer not found in DOM, skipping third column styling"
|
|
380
|
+
"Third column element not found, skipping third column styling"
|
|
326
381
|
);
|
|
327
382
|
return;
|
|
328
383
|
}
|
|
329
384
|
|
|
330
|
-
const footerHeight = footer.getBoundingClientRect()
|
|
385
|
+
const footerHeight = footer.getBoundingClientRect()?.height || 0;
|
|
331
386
|
const footerMarginTop = parseInt(
|
|
332
387
|
getComputedStyle(this.template.host).getPropertyValue(
|
|
333
388
|
"--dx-footer-margin-top"
|
|
@@ -335,9 +390,38 @@ export default class RedocReference extends LightningElement {
|
|
|
335
390
|
10
|
|
336
391
|
);
|
|
337
392
|
|
|
338
|
-
|
|
393
|
+
redocThirdColumnElement.style.setProperty(
|
|
339
394
|
"bottom",
|
|
340
395
|
`${footerHeight + footerMarginTop}px`
|
|
341
396
|
);
|
|
342
397
|
}
|
|
398
|
+
|
|
399
|
+
// Fixes initial hash scroll positioning after doc phase insertion
|
|
400
|
+
private handleInitialHashScrollFix(): void {
|
|
401
|
+
const hash = window.location.hash;
|
|
402
|
+
if (!hash || hash.length <= 1) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Small delay to ensure all components are fully rendered
|
|
407
|
+
requestAnimationFrame(() => {
|
|
408
|
+
const targetId = hash.substring(1);
|
|
409
|
+
const targetElement = document.getElementById(targetId);
|
|
410
|
+
|
|
411
|
+
if (!targetElement) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const elementTop =
|
|
416
|
+
(targetElement.getBoundingClientRect()?.top || 0) +
|
|
417
|
+
window.pageYOffset;
|
|
418
|
+
const scrollOffset = this.calculateScrollYOffset();
|
|
419
|
+
const correctedScrollPosition = elementTop - scrollOffset;
|
|
420
|
+
|
|
421
|
+
window.scrollTo({
|
|
422
|
+
top: correctedScrollPosition,
|
|
423
|
+
behavior: "smooth"
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
}
|
|
343
427
|
}
|