@salesforcedevs/docs-components 1.21.1-redocly2 → 1.21.1-redocly3
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
|
|
|
@@ -81,13 +82,40 @@ export default class RedocReference extends LightningElement {
|
|
|
81
82
|
disconnectedCallback(): void {
|
|
82
83
|
window.removeEventListener("scroll", this.handleScrollAndResize);
|
|
83
84
|
window.removeEventListener("resize", this.handleScrollAndResize);
|
|
85
|
+
|
|
86
|
+
// Clean up cached DOM element references to prevent memory leaks
|
|
87
|
+
this.docHeaderElement = null;
|
|
88
|
+
this.docPhaseWrapperElement = null;
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
this.
|
|
91
|
+
// Displays error UI and logs error message for debugging
|
|
92
|
+
private showErrorUI(message: string, error?: any): void {
|
|
93
|
+
this.showError = true;
|
|
94
|
+
console.error(message, error);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private getRedocContainer(): HTMLElement | null {
|
|
98
|
+
return document.querySelector(".redoc-container");
|
|
89
99
|
}
|
|
90
100
|
|
|
101
|
+
private getSelectedReference(): ReferenceItem | null {
|
|
102
|
+
return (
|
|
103
|
+
this._referenceConfig?.refList?.find((ref) => ref.isSelected) ||
|
|
104
|
+
this._referenceConfig?.refList?.[0]
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private getDocPhaseInfo(): string | null {
|
|
109
|
+
if (this._parentDocPhaseInfo) {
|
|
110
|
+
return this._parentDocPhaseInfo;
|
|
111
|
+
}
|
|
112
|
+
const selectedRef = this.getSelectedReference();
|
|
113
|
+
return selectedRef?.docPhase
|
|
114
|
+
? JSON.stringify(selectedRef.docPhase)
|
|
115
|
+
: null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Extracts numeric value from CSS custom properties
|
|
91
119
|
private getGlobalCSSVariableValue(variableName: string): number {
|
|
92
120
|
const value = getComputedStyle(
|
|
93
121
|
document.documentElement
|
|
@@ -95,11 +123,11 @@ export default class RedocReference extends LightningElement {
|
|
|
95
123
|
return parseInt(value, 10) || 0;
|
|
96
124
|
}
|
|
97
125
|
|
|
126
|
+
/*
|
|
127
|
+
** Since we could not use --dx-g-global-header-height as getPropertyValue returns a calc expression,
|
|
128
|
+
** we are using the respective CSS variables to calculate the height.
|
|
129
|
+
*/
|
|
98
130
|
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
131
|
const rowHeight = this.getGlobalCSSVariableValue(
|
|
104
132
|
"--dx-g-global-header-nav-row-height"
|
|
105
133
|
);
|
|
@@ -109,59 +137,62 @@ export default class RedocReference extends LightningElement {
|
|
|
109
137
|
return rowHeight * rowCount;
|
|
110
138
|
}
|
|
111
139
|
|
|
140
|
+
// Gets doc header height with element caching for performance
|
|
112
141
|
private getDocHeaderHeight(): number {
|
|
113
142
|
if (!this.docHeaderElement) {
|
|
114
143
|
this.docHeaderElement =
|
|
115
144
|
document.querySelector(".sticky-doc-header");
|
|
116
145
|
}
|
|
117
|
-
return this.docHeaderElement?.getBoundingClientRect()
|
|
146
|
+
return this.docHeaderElement?.getBoundingClientRect()?.height || 0;
|
|
118
147
|
}
|
|
119
148
|
|
|
120
|
-
|
|
149
|
+
// Gets doc phase wrapper height with element caching for performance
|
|
150
|
+
private getDocPhaseWrapperHeight(): number {
|
|
151
|
+
if (!this.docPhaseWrapperElement) {
|
|
152
|
+
this.docPhaseWrapperElement =
|
|
153
|
+
document.querySelector(".doc-phase-wrapper");
|
|
154
|
+
}
|
|
155
|
+
return (
|
|
156
|
+
this.docPhaseWrapperElement?.getBoundingClientRect()?.height || 0
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
calculateHeaderOffset = () => {
|
|
161
|
+
const globalHeaderHeight = this.getGlobalHeaderHeight();
|
|
162
|
+
const docHeaderHeight = this.getDocHeaderHeight();
|
|
163
|
+
return globalHeaderHeight + docHeaderHeight;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Dynamic scroll offset calculation that Redoc will call
|
|
167
|
+
calculateScrollYOffset = () => {
|
|
168
|
+
const headerOffset = this.calculateHeaderOffset();
|
|
169
|
+
const phaseHeight = this.getDocPhaseWrapperHeight();
|
|
170
|
+
return headerOffset + phaseHeight;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// Updates sidebar positioning based on current header heights
|
|
174
|
+
private updateSidebarPosition = () => {
|
|
121
175
|
requestAnimationFrame(() => {
|
|
122
176
|
const redocContainer = this.getRedocContainer();
|
|
123
177
|
if (!redocContainer) {
|
|
124
178
|
return;
|
|
125
179
|
}
|
|
126
180
|
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
181
|
+
const currentSidebarTop = this.calculateHeaderOffset();
|
|
182
|
+
if (currentSidebarTop === this.lastSidebarTop) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
130
185
|
|
|
131
|
-
|
|
186
|
+
const sidebarTopValue = `${currentSidebarTop}px`;
|
|
132
187
|
this.template.host.style.setProperty(
|
|
133
188
|
"--doc-c-redoc-sidebar-top",
|
|
134
|
-
|
|
189
|
+
sidebarTopValue
|
|
135
190
|
);
|
|
191
|
+
this.lastSidebarTop = currentSidebarTop;
|
|
136
192
|
});
|
|
137
193
|
};
|
|
138
194
|
|
|
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
|
-
|
|
195
|
+
// Updates browser URL while preserving query params and hash
|
|
165
196
|
private updateUrlWithReference(selectedRef: ReferenceItem): void {
|
|
166
197
|
if (selectedRef?.href) {
|
|
167
198
|
const parentReferencePath = selectedRef.href;
|
|
@@ -176,46 +207,62 @@ export default class RedocReference extends LightningElement {
|
|
|
176
207
|
}
|
|
177
208
|
}
|
|
178
209
|
|
|
210
|
+
private handleScrollAndResize = throttle(
|
|
211
|
+
SCROLL_THROTTLE_DELAY,
|
|
212
|
+
() => !this.showError && this.updateSidebarPosition()
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
// Initializes Redoc library with selected reference configuration
|
|
179
216
|
private async initializeRedoc(): Promise<void> {
|
|
180
217
|
try {
|
|
181
218
|
this.redocInitialized = true;
|
|
182
|
-
|
|
183
219
|
await this.waitForRedoc();
|
|
220
|
+
|
|
221
|
+
const redocContainer = this.getRedocContainer();
|
|
222
|
+
if (!redocContainer) {
|
|
223
|
+
this.showErrorUI("Redoc container is not found.");
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
184
227
|
const selectedRef = this.getSelectedReference();
|
|
185
228
|
if (selectedRef) {
|
|
186
229
|
this.updateUrlWithReference(selectedRef);
|
|
187
230
|
|
|
188
231
|
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
|
-
}
|
|
232
|
+
if (!specUrl) {
|
|
233
|
+
this.showErrorUI(
|
|
234
|
+
"Spec URL or Redoc container is not found."
|
|
208
235
|
);
|
|
209
|
-
|
|
210
|
-
this.showError = true;
|
|
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
|
}
|