@salesforcedevs/docs-components 1.34.4-topic-alpha.3 → 1.34.4
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/LICENSE +12 -0
- package/package.json +2 -2
- package/src/modules/doc/banner/banner.css +41 -2
- package/src/modules/doc/banner/banner.html +6 -5
- package/src/modules/doc/banner/banner.ts +28 -0
- package/src/modules/doc/breadcrumbs/breadcrumbs.css +0 -1
- package/src/modules/doc/contentActionToolbar/contentActionToolbar.ts +12 -12
- package/src/modules/doc/header/header.ts +19 -0
- package/src/modules/doc/nav/nav.css +1 -1
- package/src/modules/doc/redocReference/redocReference.css +0 -1
- package/src/modules/doc/redocReference/redocReference.ts +20 -192
- package/src/modules/doc/unifiedContentLayout/unifiedContentLayout.html +0 -8
- package/src/modules/doc/unifiedContentLayout/unifiedContentLayout.ts +7 -57
- package/src/modules/doc/versionPicker/versionPicker.css +20 -154
- package/src/modules/doc/versionPicker/versionPicker.html +22 -55
- package/src/modules/doc/versionPicker/versionPicker.ts +0 -92
- package/src/modules/docUtils/utils/utils.ts +2 -20
package/LICENSE
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Copyright (c) 2020, Salesforce.com, Inc.
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
|
5
|
+
|
|
6
|
+
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
7
|
+
|
|
8
|
+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
|
9
|
+
|
|
10
|
+
* Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
|
11
|
+
|
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforcedevs/docs-components",
|
|
3
|
-
"version": "1.34.4
|
|
3
|
+
"version": "1.34.4",
|
|
4
4
|
"description": "Docs Lightning web components for DSC",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "index.js",
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
"@types/lodash.orderby": "4.6.9",
|
|
26
26
|
"@types/lodash.uniqby": "4.7.9"
|
|
27
27
|
},
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "563b708d0438eb63783483178fc116897aa12b7a"
|
|
29
29
|
}
|
|
@@ -16,8 +16,6 @@
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
.icon {
|
|
19
|
-
--dx-c-icon-size: var(--dx-g-icon-size-lg);
|
|
20
|
-
|
|
21
19
|
flex-shrink: 0;
|
|
22
20
|
margin-top: var(--dx-g-spacing-smd);
|
|
23
21
|
margin-right: var(--dx-g-spacing-sm);
|
|
@@ -73,6 +71,47 @@
|
|
|
73
71
|
cursor: pointer;
|
|
74
72
|
}
|
|
75
73
|
|
|
74
|
+
/* Centered variant: the message is centered in the banner and the icon hangs
|
|
75
|
+
to its left. The close button is taken out of the flow so it doesn't offset
|
|
76
|
+
the centering. */
|
|
77
|
+
.container_centered {
|
|
78
|
+
position: relative;
|
|
79
|
+
display: grid;
|
|
80
|
+
grid-template-columns: 1fr auto 1fr;
|
|
81
|
+
align-items: center;
|
|
82
|
+
|
|
83
|
+
/* Reserve the close button's width plus its right inset on both sides */
|
|
84
|
+
padding: 0
|
|
85
|
+
calc(
|
|
86
|
+
var(--dx-g-spacing-3xl) + var(--dx-g-spacing-xs) +
|
|
87
|
+
var(--doc-banner-padding-right)
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.container_centered .icon {
|
|
92
|
+
grid-column: 1;
|
|
93
|
+
justify-self: end;
|
|
94
|
+
margin-top: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.container_centered .main {
|
|
98
|
+
grid-column: 2;
|
|
99
|
+
align-items: center;
|
|
100
|
+
justify-content: center;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.container_centered .message {
|
|
104
|
+
text-align: center;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.container_centered .close {
|
|
108
|
+
position: absolute;
|
|
109
|
+
top: 50%;
|
|
110
|
+
right: var(--doc-banner-padding-right);
|
|
111
|
+
margin-left: 0;
|
|
112
|
+
transform: translateY(-50%);
|
|
113
|
+
}
|
|
114
|
+
|
|
76
115
|
@media (max-width: 1279px) {
|
|
77
116
|
:host {
|
|
78
117
|
--doc-banner-padding-left: var(--dx-g-spacing-xl);
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<template lwc:if={showBanner}>
|
|
3
|
-
<div class=
|
|
3
|
+
<div class={containerClass} part="container">
|
|
4
4
|
<dx-icon
|
|
5
5
|
class="icon"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
sprite={iconSprite}
|
|
7
|
+
symbol={iconSymbol}
|
|
8
|
+
size={iconSize}
|
|
9
|
+
color={iconColor}
|
|
9
10
|
part="icon"
|
|
10
11
|
></dx-icon>
|
|
11
12
|
<div class="main">
|
|
12
13
|
<div class="message" part="message" lwc:dom="manual"></div>
|
|
13
|
-
<div class="actions" part="actions">
|
|
14
|
+
<div lwc:if={hasActions} class="actions" part="actions">
|
|
14
15
|
<template lwc:if={hasPrimaryButton}>
|
|
15
16
|
<dx-button
|
|
16
17
|
href={buttonHref}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { LightningElement, api } from "lwc";
|
|
2
|
+
import { IconSize, IconSprite, IconSymbol } from "typings/custom";
|
|
3
|
+
import { normalizeBoolean } from "dxUtils/normalizers";
|
|
2
4
|
|
|
3
5
|
const BANNER_STORAGE_PREFIX = "doc-banner-";
|
|
4
6
|
|
|
@@ -13,6 +15,24 @@ export default class Banner extends LightningElement {
|
|
|
13
15
|
|
|
14
16
|
@api dismissStorageKey = "";
|
|
15
17
|
|
|
18
|
+
@api
|
|
19
|
+
get centered(): boolean {
|
|
20
|
+
return this._centered;
|
|
21
|
+
}
|
|
22
|
+
set centered(value: boolean | string) {
|
|
23
|
+
this._centered = normalizeBoolean(value);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@api iconSprite: IconSprite = "utility";
|
|
27
|
+
|
|
28
|
+
@api iconSymbol: IconSymbol = "info";
|
|
29
|
+
|
|
30
|
+
@api iconColor = "gray-50";
|
|
31
|
+
|
|
32
|
+
@api iconSize: IconSize = "large";
|
|
33
|
+
|
|
34
|
+
private _centered = false;
|
|
35
|
+
|
|
16
36
|
private _dismissed = false;
|
|
17
37
|
|
|
18
38
|
connectedCallback() {
|
|
@@ -33,10 +53,18 @@ export default class Banner extends LightningElement {
|
|
|
33
53
|
}
|
|
34
54
|
}
|
|
35
55
|
|
|
56
|
+
get containerClass(): string {
|
|
57
|
+
return this._centered ? "container container_centered" : "container";
|
|
58
|
+
}
|
|
59
|
+
|
|
36
60
|
get showBanner(): boolean {
|
|
37
61
|
return !this._dismissed;
|
|
38
62
|
}
|
|
39
63
|
|
|
64
|
+
get hasActions(): boolean {
|
|
65
|
+
return this.hasPrimaryButton || this.hasSecondaryAction;
|
|
66
|
+
}
|
|
67
|
+
|
|
40
68
|
get hasPrimaryButton(): boolean {
|
|
41
69
|
return !!(this.buttonLabel && this.buttonHref);
|
|
42
70
|
}
|
|
@@ -127,26 +127,26 @@ export default class ContentActionToolbar extends LightningElement {
|
|
|
127
127
|
{
|
|
128
128
|
id: ACTION_COPY_MARKDOWN,
|
|
129
129
|
label: COPY_MARKDOWN_LABEL,
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
iconSprite: "utility",
|
|
131
|
+
iconSymbol: "copy"
|
|
132
132
|
},
|
|
133
133
|
{
|
|
134
134
|
id: ACTION_VIEW_MARKDOWN,
|
|
135
135
|
label: VIEW_MARKDOWN_LABEL,
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
iconSprite: "utility",
|
|
137
|
+
iconSymbol: "new_window"
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
id: ACTION_COPY_MARKDOWN_URL,
|
|
141
141
|
label: COPY_MARKDOWN_URL_LABEL,
|
|
142
|
-
|
|
143
|
-
|
|
142
|
+
iconSprite: "utility",
|
|
143
|
+
iconSymbol: "link"
|
|
144
144
|
},
|
|
145
145
|
...AI_ASSISTANTS.map(({ action, label }) => ({
|
|
146
146
|
id: action,
|
|
147
147
|
label,
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
iconSprite: "utility",
|
|
149
|
+
iconSymbol: "new_window"
|
|
150
150
|
}))
|
|
151
151
|
]
|
|
152
152
|
});
|
|
@@ -159,14 +159,14 @@ export default class ContentActionToolbar extends LightningElement {
|
|
|
159
159
|
{
|
|
160
160
|
id: ACTION_VIEW_LLMS,
|
|
161
161
|
label: VIEW_LLMS_LABEL,
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
iconSprite: "utility",
|
|
163
|
+
iconSymbol: "new_window"
|
|
164
164
|
},
|
|
165
165
|
{
|
|
166
166
|
id: ACTION_COPY_LLMS_URL,
|
|
167
167
|
label: COPY_LLMS_URL_LABEL,
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
iconSprite: "utility",
|
|
169
|
+
iconSymbol: "link"
|
|
170
170
|
}
|
|
171
171
|
]
|
|
172
172
|
});
|
|
@@ -5,6 +5,7 @@ import { HeaderBase } from "dxBaseElements/headerBase";
|
|
|
5
5
|
import { toJson, normalizeBoolean } from "dxUtils/normalizers";
|
|
6
6
|
import { track } from "dxUtils/analytics";
|
|
7
7
|
|
|
8
|
+
const TABLET_MATCH = "980px";
|
|
8
9
|
const MOBILE_MATCH = "768px";
|
|
9
10
|
|
|
10
11
|
const isStorybook = () => {
|
|
@@ -46,6 +47,8 @@ export default class Header extends HeaderBase {
|
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
private _scopedNavItems!: OptionWithNested[];
|
|
50
|
+
private tablet = false;
|
|
51
|
+
private tabletMatchMedia!: MediaQueryList;
|
|
49
52
|
private shouldRender: boolean = false;
|
|
50
53
|
private showDocDivider: boolean = false;
|
|
51
54
|
private _devCenter!: DevCenterConfig;
|
|
@@ -80,6 +83,11 @@ export default class Header extends HeaderBase {
|
|
|
80
83
|
|
|
81
84
|
connectedCallback(): void {
|
|
82
85
|
super.connectedCallback();
|
|
86
|
+
this.tabletMatchMedia = window.matchMedia(
|
|
87
|
+
`(max-width: ${TABLET_MATCH})`
|
|
88
|
+
);
|
|
89
|
+
this.onTabletChange(this.tabletMatchMedia);
|
|
90
|
+
this.tabletMatchMedia.addEventListener("change", this.onTabletChange);
|
|
83
91
|
|
|
84
92
|
if (
|
|
85
93
|
(!this.shouldHideHeader && this.isDocsPath) ||
|
|
@@ -98,12 +106,23 @@ export default class Header extends HeaderBase {
|
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
108
|
|
|
109
|
+
disconnectedCallback(): void {
|
|
110
|
+
super.disconnectedCallback();
|
|
111
|
+
this.tabletMatchMedia.removeEventListener(
|
|
112
|
+
"change",
|
|
113
|
+
this.onTabletChange
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
101
117
|
renderedCallback(): void {
|
|
102
118
|
if (this.hideDocHeader) {
|
|
103
119
|
this.shouldRender = !this.shouldHideHeader && this.isDocsPath;
|
|
104
120
|
}
|
|
105
121
|
}
|
|
106
122
|
|
|
123
|
+
private onTabletChange = (e: MediaQueryListEvent | MediaQueryList) =>
|
|
124
|
+
(this.tablet = e.matches);
|
|
125
|
+
|
|
107
126
|
protected additionalClasses(): string {
|
|
108
127
|
return cx(
|
|
109
128
|
this.brand && "has-brand",
|
|
@@ -4,13 +4,11 @@ import DocPhase from "doc/phase";
|
|
|
4
4
|
import DxFooter from "dx/footer";
|
|
5
5
|
import DxIcon from "dx/icon";
|
|
6
6
|
import SidebarFooterNav from "dx/sidebarFooterNav";
|
|
7
|
-
import VersionPicker from "doc/versionPicker";
|
|
8
7
|
import SprigSurvey from "doc/sprigSurvey";
|
|
9
8
|
import { throttle } from "throttle-debounce";
|
|
10
9
|
import { pollUntil } from "dxUtils/async";
|
|
11
10
|
import { toJson } from "dxUtils/normalizers";
|
|
12
|
-
import {
|
|
13
|
-
import type { DocPhaseInfo, OptionWithLink } from "typings/custom";
|
|
11
|
+
import type { OptionWithLink } from "typings/custom";
|
|
14
12
|
|
|
15
13
|
declare global {
|
|
16
14
|
interface Window {
|
|
@@ -35,18 +33,8 @@ type ReferenceItem = {
|
|
|
35
33
|
topic?: ReferenceTopic;
|
|
36
34
|
};
|
|
37
35
|
|
|
38
|
-
/** A selectable spec version. Mirrors `ReferenceVersion` in `doc/amfReference`. */
|
|
39
|
-
type ReferenceVersion = {
|
|
40
|
-
id: string;
|
|
41
|
-
label: string;
|
|
42
|
-
deprecated?: boolean;
|
|
43
|
-
selected?: boolean;
|
|
44
|
-
link: { href: string };
|
|
45
|
-
};
|
|
46
|
-
|
|
47
36
|
type ReferenceConfig = {
|
|
48
37
|
refList: ReferenceItem[];
|
|
49
|
-
versions?: ReferenceVersion[];
|
|
50
38
|
};
|
|
51
39
|
|
|
52
40
|
const SCROLL_THROTTLE_DELAY = 50;
|
|
@@ -54,7 +42,6 @@ const ELEMENT_TIMEOUT = 10000;
|
|
|
54
42
|
const ELEMENT_CHECK_INTERVAL = 100;
|
|
55
43
|
const DEFAULT_PROJECT_TITLE = "All Reference";
|
|
56
44
|
const BACK_TARGET_STORAGE_KEY = "redoc-back-target";
|
|
57
|
-
const SELECTED_VIEW_STORAGE_KEY = "redoc-selected-view";
|
|
58
45
|
const DOCS_PATH_SEGMENT = "docs";
|
|
59
46
|
const DEFAULT_LOCALE = "en-us";
|
|
60
47
|
|
|
@@ -165,40 +152,6 @@ export default class RedocReference extends LightningElement {
|
|
|
165
152
|
return refCount > 1;
|
|
166
153
|
}
|
|
167
154
|
|
|
168
|
-
get versions(): ReferenceVersion[] {
|
|
169
|
-
return this._referenceConfig?.versions ?? [];
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/** The version flagged `selected`, else the first (the latest/GA version). */
|
|
173
|
-
get selectedVersion(): ReferenceVersion | null {
|
|
174
|
-
const versions = this.versions;
|
|
175
|
-
return versions.find((v) => v.selected) ?? versions[0] ?? null;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/** True when the selected version is the latest/GA one (index 0). */
|
|
179
|
-
get latestVersion(): boolean {
|
|
180
|
-
return this.versions.length
|
|
181
|
-
? this.selectedVersion?.id === this.versions[0].id
|
|
182
|
-
: false;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
get hasVersionPicker(): boolean {
|
|
186
|
-
return this.versions.length > 0;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/** "Newer version available" banner info when on a non-latest version, else null. */
|
|
190
|
-
private get oldVersionInfo(): DocPhaseInfo | null {
|
|
191
|
-
if (this.versions.length > 1 && !this.latestVersion) {
|
|
192
|
-
return oldVersionDocInfo(this.versions[0].link.href);
|
|
193
|
-
}
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** A single version has nothing to choose, so it renders read-only. */
|
|
198
|
-
get isVersionReadOnly(): boolean {
|
|
199
|
-
return this.versions.length === 1;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
155
|
// Reads stored back target
|
|
203
156
|
private getBackTargetFromSession(): string | null {
|
|
204
157
|
return sessionStorage.getItem(BACK_TARGET_STORAGE_KEY);
|
|
@@ -249,9 +202,7 @@ export default class RedocReference extends LightningElement {
|
|
|
249
202
|
}
|
|
250
203
|
|
|
251
204
|
const referrer = this.getSameOriginReferrerHref();
|
|
252
|
-
return referrer &&
|
|
253
|
-
!this.isLocaleHref(new URL(referrer).pathname) &&
|
|
254
|
-
!this.isVersionHref(new URL(referrer).pathname)
|
|
205
|
+
return referrer && !this.isLocaleHref(new URL(referrer).pathname)
|
|
255
206
|
? referrer
|
|
256
207
|
: null;
|
|
257
208
|
}
|
|
@@ -290,10 +241,7 @@ export default class RedocReference extends LightningElement {
|
|
|
290
241
|
}
|
|
291
242
|
|
|
292
243
|
const referrerUrl = new URL(referrerHref);
|
|
293
|
-
if (
|
|
294
|
-
this.isLocaleHref(referrerUrl.pathname) ||
|
|
295
|
-
this.isVersionHref(referrerUrl.pathname)
|
|
296
|
-
) {
|
|
244
|
+
if (this.isLocaleHref(referrerUrl.pathname)) {
|
|
297
245
|
return;
|
|
298
246
|
}
|
|
299
247
|
|
|
@@ -334,19 +282,6 @@ export default class RedocReference extends LightningElement {
|
|
|
334
282
|
});
|
|
335
283
|
}
|
|
336
284
|
|
|
337
|
-
/**
|
|
338
|
-
* switching version on this same page.
|
|
339
|
-
*/
|
|
340
|
-
private isVersionHref(pathname: string): boolean {
|
|
341
|
-
return this.versions.some((version) => {
|
|
342
|
-
const href = version?.link?.href;
|
|
343
|
-
return (
|
|
344
|
-
!!href &&
|
|
345
|
-
new URL(href, window.location.origin).pathname === pathname
|
|
346
|
-
);
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
|
|
350
285
|
/** When origin is provided, pass it to the footer; otherwise use dx-footer's default. */
|
|
351
286
|
get effectiveFooterOrigin(): string {
|
|
352
287
|
return (
|
|
@@ -419,14 +354,6 @@ export default class RedocReference extends LightningElement {
|
|
|
419
354
|
return parseInt(value, 10) || 0;
|
|
420
355
|
}
|
|
421
356
|
|
|
422
|
-
/** Redoc's LNB background color, themeable via `--doc-c-redoc-sidebar-bg`. */
|
|
423
|
-
private get sidebarBackgroundColor(): string {
|
|
424
|
-
const value = getComputedStyle(this.template.host).getPropertyValue(
|
|
425
|
-
"--doc-c-redoc-sidebar-bg"
|
|
426
|
-
);
|
|
427
|
-
return value.trim() || "white";
|
|
428
|
-
}
|
|
429
|
-
|
|
430
357
|
/*
|
|
431
358
|
** Since we could not use --dx-g-global-header-height as getPropertyValue returns a calc expression,
|
|
432
359
|
** we are using the respective CSS variables to calculate the height.
|
|
@@ -542,14 +469,7 @@ export default class RedocReference extends LightningElement {
|
|
|
542
469
|
specUrl,
|
|
543
470
|
{
|
|
544
471
|
// Dynamic scroll offset to account for headers
|
|
545
|
-
scrollYOffset: this.calculateScrollYOffset
|
|
546
|
-
// Redoc's own styled background outranks injected
|
|
547
|
-
// CSS, so the LNB background must be set via theme.
|
|
548
|
-
theme: {
|
|
549
|
-
sidebar: {
|
|
550
|
-
backgroundColor: this.sidebarBackgroundColor
|
|
551
|
-
}
|
|
552
|
-
}
|
|
472
|
+
scrollYOffset: this.calculateScrollYOffset
|
|
553
473
|
},
|
|
554
474
|
redocContainer,
|
|
555
475
|
(error: any) => {
|
|
@@ -595,7 +515,10 @@ export default class RedocReference extends LightningElement {
|
|
|
595
515
|
const apiContentDiv = await this.waitForApiContent(redocContainer);
|
|
596
516
|
apiContentDiv.setAttribute("lwc:dom", "manual");
|
|
597
517
|
|
|
598
|
-
this.
|
|
518
|
+
const docPhaseInfo = this.getDocPhaseInfo();
|
|
519
|
+
if (docPhaseInfo) {
|
|
520
|
+
this.insertDocPhase(apiContentDiv, docPhaseInfo);
|
|
521
|
+
}
|
|
599
522
|
|
|
600
523
|
this.appendFooterItems(apiContentDiv);
|
|
601
524
|
|
|
@@ -604,22 +527,10 @@ export default class RedocReference extends LightningElement {
|
|
|
604
527
|
|
|
605
528
|
// Wait for footer to be rendered before updating styles
|
|
606
529
|
requestAnimationFrame(() => {
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
// after all layout mutations (footer, header) are
|
|
612
|
-
// complete, so the scroll lands on the correct element.
|
|
613
|
-
this.restoreSelectedView();
|
|
614
|
-
|
|
615
|
-
// Fix initial hash scroll after doc phase insertion
|
|
616
|
-
this.handleInitialHashScrollFix();
|
|
617
|
-
} catch (error) {
|
|
618
|
-
this.showErrorUI(
|
|
619
|
-
"Failed to integrate custom components:",
|
|
620
|
-
error
|
|
621
|
-
);
|
|
622
|
-
}
|
|
530
|
+
this.updateRedocThirdColumnStyle(redocContainer);
|
|
531
|
+
|
|
532
|
+
// Fix initial hash scroll after doc phase insertion
|
|
533
|
+
this.handleInitialHashScrollFix();
|
|
623
534
|
});
|
|
624
535
|
} catch (error) {
|
|
625
536
|
this.showErrorUI("Failed to integrate custom components:", error);
|
|
@@ -649,53 +560,6 @@ export default class RedocReference extends LightningElement {
|
|
|
649
560
|
}
|
|
650
561
|
}
|
|
651
562
|
|
|
652
|
-
/** Builds the version picker DOM by reusing `doc-version-picker`. */
|
|
653
|
-
private buildVersionPickerDom(): HTMLElement {
|
|
654
|
-
const wrapper = document.createElement("div");
|
|
655
|
-
wrapper.className = "redoc-version-picker";
|
|
656
|
-
|
|
657
|
-
const picker = createElement("doc-version-picker", {
|
|
658
|
-
is: VersionPicker
|
|
659
|
-
});
|
|
660
|
-
|
|
661
|
-
Object.assign(picker, {
|
|
662
|
-
versions: this.versions,
|
|
663
|
-
selectedVersion: this.selectedVersion,
|
|
664
|
-
latestVersion: this.latestVersion,
|
|
665
|
-
readOnly: this.isVersionReadOnly
|
|
666
|
-
});
|
|
667
|
-
picker.addEventListener("change", this.onVersionChange);
|
|
668
|
-
wrapper.appendChild(picker);
|
|
669
|
-
|
|
670
|
-
return wrapper;
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
/** Stashes the current view (URL hash) before the version link navigates. */
|
|
674
|
-
private onVersionChange = (): void => {
|
|
675
|
-
sessionStorage.setItem(
|
|
676
|
-
SELECTED_VIEW_STORAGE_KEY,
|
|
677
|
-
window.location.hash || ""
|
|
678
|
-
);
|
|
679
|
-
};
|
|
680
|
-
|
|
681
|
-
/**
|
|
682
|
-
* Re-applies the view stashed before a version switch, if its anchor exists
|
|
683
|
-
* in the new spec; otherwise a no-op (lands on the spec root).
|
|
684
|
-
*/
|
|
685
|
-
private restoreSelectedView(): void {
|
|
686
|
-
const storedHash = sessionStorage.getItem(SELECTED_VIEW_STORAGE_KEY);
|
|
687
|
-
sessionStorage.removeItem(SELECTED_VIEW_STORAGE_KEY);
|
|
688
|
-
|
|
689
|
-
if (!storedHash || window.location.hash) {
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
const targetId = storedHash.replace(/^#/, "");
|
|
694
|
-
if (targetId && document.getElementById(targetId)) {
|
|
695
|
-
window.location.hash = storedHash;
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
563
|
/**
|
|
700
564
|
* Builds the locale picker DOM by reusing `dx-sidebar-footer-nav`
|
|
701
565
|
*/
|
|
@@ -717,17 +581,12 @@ export default class RedocReference extends LightningElement {
|
|
|
717
581
|
}
|
|
718
582
|
|
|
719
583
|
/**
|
|
720
|
-
* Builds
|
|
721
|
-
* versions are available, the version picker as a sibling row. Layout/gaps
|
|
722
|
-
* are styled by the developer-website's redoc CSS.
|
|
584
|
+
* Builds a fresh project-title/spec-title header DOM node.
|
|
723
585
|
*/
|
|
724
586
|
private buildProjectHeaderDom(): HTMLElement {
|
|
725
587
|
const wrapper = document.createElement("div");
|
|
726
588
|
wrapper.className = "redoc-project-header";
|
|
727
589
|
|
|
728
|
-
const main = document.createElement("div");
|
|
729
|
-
main.className = "redoc-project-header-main";
|
|
730
|
-
|
|
731
590
|
if (this.projectTitle) {
|
|
732
591
|
const backLink = document.createElement("a");
|
|
733
592
|
backLink.className = "redoc-project-back";
|
|
@@ -748,20 +607,14 @@ export default class RedocReference extends LightningElement {
|
|
|
748
607
|
|
|
749
608
|
backLink.appendChild(icon);
|
|
750
609
|
backLink.appendChild(label);
|
|
751
|
-
|
|
610
|
+
wrapper.appendChild(backLink);
|
|
752
611
|
}
|
|
753
612
|
|
|
754
613
|
if (this.specTitle) {
|
|
755
614
|
const specEl = document.createElement("h2");
|
|
756
615
|
specEl.className = "redoc-spec-title dx-text-display-7";
|
|
757
616
|
specEl.textContent = this.specTitle;
|
|
758
|
-
|
|
759
|
-
}
|
|
760
|
-
|
|
761
|
-
wrapper.appendChild(main);
|
|
762
|
-
|
|
763
|
-
if (this.hasVersionPicker) {
|
|
764
|
-
wrapper.appendChild(this.buildVersionPickerDom());
|
|
617
|
+
wrapper.appendChild(specEl);
|
|
765
618
|
}
|
|
766
619
|
|
|
767
620
|
return wrapper;
|
|
@@ -786,40 +639,15 @@ export default class RedocReference extends LightningElement {
|
|
|
786
639
|
return container.querySelector<HTMLElement>(".api-content")!;
|
|
787
640
|
}
|
|
788
641
|
|
|
789
|
-
//
|
|
790
|
-
|
|
791
|
-
private insertStatusItems(container: HTMLElement): void {
|
|
792
|
-
const docPhaseInfo = this.getDocPhaseInfo();
|
|
793
|
-
const oldVersionInfo = this.oldVersionInfo;
|
|
794
|
-
if (!docPhaseInfo && !oldVersionInfo) {
|
|
795
|
-
return;
|
|
796
|
-
}
|
|
797
|
-
|
|
642
|
+
// Creates and inserts doc phase component at container start
|
|
643
|
+
private insertDocPhase(container: HTMLElement, docPhaseInfo: string): void {
|
|
798
644
|
const wrapper = document.createElement("div");
|
|
799
645
|
wrapper.className = "doc-phase-wrapper";
|
|
800
646
|
container.insertBefore(wrapper, container.firstChild);
|
|
801
647
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
});
|
|
806
|
-
Object.assign(docPhaseElement, { docPhaseInfo });
|
|
807
|
-
wrapper.appendChild(docPhaseElement);
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
if (oldVersionInfo) {
|
|
811
|
-
const versionBanner = createElement("doc-phase", { is: DocPhase });
|
|
812
|
-
Object.assign(versionBanner, {
|
|
813
|
-
docPhaseInfo: oldVersionInfo,
|
|
814
|
-
dismissible: true,
|
|
815
|
-
iconName: "warning"
|
|
816
|
-
});
|
|
817
|
-
versionBanner.addEventListener("dismissphase", () => {
|
|
818
|
-
versionBanner.remove();
|
|
819
|
-
this.updateSidebarPosition();
|
|
820
|
-
});
|
|
821
|
-
wrapper.appendChild(versionBanner);
|
|
822
|
-
}
|
|
648
|
+
const docPhaseElement = createElement("doc-phase", { is: DocPhase });
|
|
649
|
+
Object.assign(docPhaseElement, { docPhaseInfo });
|
|
650
|
+
wrapper.appendChild(docPhaseElement);
|
|
823
651
|
}
|
|
824
652
|
|
|
825
653
|
// Appends footer component to container
|
|
@@ -24,14 +24,6 @@
|
|
|
24
24
|
lwc:if={docPhaseInfo}
|
|
25
25
|
doc-phase-info={docPhaseInfo}
|
|
26
26
|
></doc-phase>
|
|
27
|
-
<doc-phase
|
|
28
|
-
slot="version-banner"
|
|
29
|
-
lwc:if={showVersionBanner}
|
|
30
|
-
doc-phase-info={oldVersionInfo}
|
|
31
|
-
icon-name="warning"
|
|
32
|
-
dismissible="true"
|
|
33
|
-
ondismissphase={handleDismissVersionBanner}
|
|
34
|
-
></doc-phase>
|
|
35
27
|
<slot></slot>
|
|
36
28
|
</doc-content-layout>
|
|
37
29
|
</template>
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import { LightningElement, api
|
|
1
|
+
import { LightningElement, api } from "lwc";
|
|
2
2
|
import { toJson } from "dxUtils/normalizers";
|
|
3
|
-
import type {
|
|
4
|
-
import {
|
|
5
|
-
findTopicVersionForCurrentPage,
|
|
6
|
-
latestHrefForCurrentPage
|
|
7
|
-
} from "dxUtils/topicVersion";
|
|
8
|
-
import { oldVersionDocInfo } from "docUtils/utils";
|
|
9
|
-
import type { DocPhaseInfo, OptionWithLink, TreeNode } from "typings/custom";
|
|
3
|
+
import type { OptionWithLink, TreeNode } from "typings/custom";
|
|
10
4
|
|
|
11
5
|
/**
|
|
12
6
|
* Per-topic type emitted by the docs content-type parser
|
|
@@ -47,24 +41,16 @@ function firstChildContentPage(node: TreeNode): TreeNode | undefined {
|
|
|
47
41
|
* that `dx-tree-tile` reads (spec topics get the forward arrow icon for Redoc),
|
|
48
42
|
* and redirects content-less parent topics whose first child is a content page
|
|
49
43
|
*/
|
|
50
|
-
function
|
|
51
|
-
topics:
|
|
52
|
-
| Array<TreeNode & { topicType?: string; versions?: unknown }>
|
|
53
|
-
| undefined
|
|
44
|
+
function decorateTopics(
|
|
45
|
+
topics: Array<TreeNode & { topicType?: string }> | undefined
|
|
54
46
|
): TreeNode[] | undefined {
|
|
55
47
|
return topics?.map((topic) => {
|
|
56
|
-
const { topicType,
|
|
48
|
+
const { topicType, ...decorated } = topic;
|
|
57
49
|
if (topicType === TOPIC_TYPE_SPEC) {
|
|
58
50
|
decorated.showForwardArrow = true;
|
|
59
|
-
// Nested and standalone specs never get a sidebar picker; only the
|
|
60
|
-
// parent MD topic (when versioned) shows one.
|
|
61
|
-
} else if (versions) {
|
|
62
|
-
decorated.versions = versions;
|
|
63
51
|
}
|
|
64
52
|
if (decorated.children) {
|
|
65
|
-
decorated.children =
|
|
66
|
-
decorated.children
|
|
67
|
-
);
|
|
53
|
+
decorated.children = decorateTopics(decorated.children);
|
|
68
54
|
}
|
|
69
55
|
|
|
70
56
|
/** Redirect a parent that has no content of its own to its first child
|
|
@@ -115,9 +101,6 @@ export default class UnifiedContentLayout extends LightningElement {
|
|
|
115
101
|
|
|
116
102
|
private _docPhaseInfo: string | null = null;
|
|
117
103
|
private _sidebarContent: unknown = null;
|
|
118
|
-
private _oldVersionInfo: DocPhaseInfo | null = null;
|
|
119
|
-
|
|
120
|
-
@track showVersionBanner = false;
|
|
121
104
|
|
|
122
105
|
@api
|
|
123
106
|
get docPhaseInfo(): string | null {
|
|
@@ -134,40 +117,7 @@ export default class UnifiedContentLayout extends LightningElement {
|
|
|
134
117
|
}
|
|
135
118
|
|
|
136
119
|
set sidebarContent(value: string) {
|
|
137
|
-
|
|
138
|
-
this._sidebarContent = topics;
|
|
139
|
-
this.updateTopicVersionBanner(topics as VersionedNode[] | undefined);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
get oldVersionInfo(): DocPhaseInfo | null {
|
|
143
|
-
return this._oldVersionInfo;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
handleDismissVersionBanner(): void {
|
|
147
|
-
this.showVersionBanner = false;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Same banner references use when the reader is on an older version.
|
|
152
|
-
* Topic versions are not a separate page property: the sidebar tree
|
|
153
|
-
* carries each version's switching URL, and the current pathname says
|
|
154
|
-
* which one is open.
|
|
155
|
-
*/
|
|
156
|
-
private updateTopicVersionBanner(
|
|
157
|
-
topics: VersionedNode[] | undefined
|
|
158
|
-
): void {
|
|
159
|
-
const match = findTopicVersionForCurrentPage(topics);
|
|
160
|
-
const latest = match?.node.versions?.[0];
|
|
161
|
-
const latestHref = latestHrefForCurrentPage(match?.node.versions);
|
|
162
|
-
|
|
163
|
-
if (match && latest && match.version.id !== latest.id && latestHref) {
|
|
164
|
-
this._oldVersionInfo = oldVersionDocInfo(latestHref);
|
|
165
|
-
this.showVersionBanner = true;
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
this._oldVersionInfo = null;
|
|
170
|
-
this.showVersionBanner = false;
|
|
120
|
+
this._sidebarContent = decorateTopics(toJson(value)?.topics);
|
|
171
121
|
}
|
|
172
122
|
|
|
173
123
|
private get enableFooter(): boolean {
|
|
@@ -1,81 +1,29 @@
|
|
|
1
1
|
@import "dxHelpers/reset";
|
|
2
2
|
|
|
3
|
-
/*
|
|
4
|
-
menu stops at --doc-version-picker-width, which the sidebar sets. */
|
|
3
|
+
/* NOTE: doc-version-picker-width width variable is used by both dx-button and dx-dropdown to maintain a consistent width. */
|
|
5
4
|
:host {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/* Menu labels wrap; break long unspaced labels instead of overflowing. */
|
|
9
|
-
--dx-c-dropdown-option-overflow-wrap: anywhere;
|
|
5
|
+
--dx-c-dropdown-option-font-weight: normal;
|
|
6
|
+
--dx-c-dropdown-option-label-color: var(--dx-g-gray-10);
|
|
10
7
|
--popover-container-open-transform: translateY(4px);
|
|
11
8
|
}
|
|
12
9
|
|
|
13
|
-
/* Open menus must stack above later sibling pickers in the sidebar. */
|
|
14
|
-
:host(:has([aria-expanded="true"])) {
|
|
15
|
-
position: relative;
|
|
16
|
-
z-index: 2;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
10
|
.version-picker-container {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
var(--dx-g-spacing-sm) var(--dx-g-spacing-lg) var(--dx-g-spacing-sm)
|
|
25
|
-
var(--dx-g-global-header-padding-horizontal)
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
/* Override --doc-version-picker-divider (e.g. `none`) to restyle dividers. */
|
|
29
|
-
border-top: var(
|
|
30
|
-
--doc-version-picker-divider,
|
|
31
|
-
1px solid var(--dx-g-gray-90)
|
|
32
|
-
);
|
|
33
|
-
border-bottom: var(
|
|
34
|
-
--doc-version-picker-divider,
|
|
35
|
-
1px solid var(--dx-g-gray-90)
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/* Inline sidebar variant: drop the header-slot chrome so the picker sits
|
|
40
|
-
beside a tree node without extra padding or divider lines. */
|
|
41
|
-
.version-picker-container-small {
|
|
42
|
-
display: flex;
|
|
43
|
-
align-items: center;
|
|
44
|
-
padding: 0;
|
|
45
|
-
border: none;
|
|
11
|
+
padding: 8px var(--dx-g-spacing-lg) 8px
|
|
12
|
+
var(--dx-g-global-header-padding-horizontal);
|
|
13
|
+
border-top: 1px solid var(--dx-g-gray-90);
|
|
14
|
+
border-bottom: 1px solid var(--dx-g-gray-90);
|
|
46
15
|
}
|
|
47
16
|
|
|
48
17
|
.version-picker-button {
|
|
49
18
|
display: flex;
|
|
50
|
-
width:
|
|
51
|
-
|
|
52
|
-
--dx-c-button-horizontal-spacing: var(--dx-g-spacing-sm);
|
|
53
|
-
--dx-g-button-icon-color: var(--dx-g-gray-50);
|
|
54
|
-
|
|
55
|
-
/* A real 1px border at rest. Hover and the open menu turn it dark blue and
|
|
56
|
-
add a 1px inset shadow, so the edge looks 2px without resizing. */
|
|
57
|
-
--dx-c-button-custom-color: var(--dx-g-gray-10);
|
|
58
|
-
--dx-c-button-custom-color-hover: var(--dx-g-gray-10);
|
|
59
|
-
--dx-c-button-custom-background: white;
|
|
60
|
-
--dx-c-button-custom-background-hover: var(--dx-g-blue-vibrant-95);
|
|
61
|
-
--dx-c-button-custom-border: var(--dx-g-gray-50);
|
|
62
|
-
--dx-c-button-custom-border-hover: var(--dx-g-blue-vibrant-20);
|
|
63
|
-
--dx-c-button-custom-border-shadow-hover: inset 0 0 0 1px
|
|
64
|
-
var(--dx-g-blue-vibrant-20);
|
|
65
|
-
|
|
66
|
-
/* Focus keeps the 2px edge inside the white band. WES shows the ring while
|
|
67
|
-
the menu is open too, as a select does. */
|
|
68
|
-
--dx-c-button-focus-shadow: inset 0 0 0 1px var(--dx-g-blue-vibrant-20),
|
|
69
|
-
0 0 0 2px white;
|
|
70
|
-
--dx-c-button-custom-focus-outline-active: var(--dx-c-button-focus-outline);
|
|
71
|
-
--dx-c-button-custom-focus-offset-active: var(--dx-c-button-focus-offset);
|
|
72
|
-
--dx-c-button-custom-focus-shadow-active: var(--dx-c-button-focus-shadow);
|
|
19
|
+
width: var(--doc-version-picker-width, 296px);
|
|
73
20
|
}
|
|
74
21
|
|
|
75
22
|
.version-picker-button:hover,
|
|
76
|
-
.version-picker-button:
|
|
77
|
-
.version-picker-button
|
|
78
|
-
--dx-
|
|
23
|
+
.version-picker-button:active,
|
|
24
|
+
.version-picker-button:focus {
|
|
25
|
+
--dx-c-button-secondary-color-hover: var(--dx-g-cloud-blue-vibrant-95);
|
|
26
|
+
--dx-c-button-primary-color: var(--dx-g-blue-vibrant-40);
|
|
79
27
|
}
|
|
80
28
|
|
|
81
29
|
/**
|
|
@@ -101,98 +49,16 @@ dx-button::part(content) {
|
|
|
101
49
|
white-space: nowrap;
|
|
102
50
|
}
|
|
103
51
|
|
|
104
|
-
dx-type-badge.latest-badge
|
|
105
|
-
dx-type-badge
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/* ------------------------------------------------------------------ *
|
|
110
|
-
* Small "dot" variant (opt-in via `small`). Additive
|
|
111
|
-
* ------------------------------------------------------------------ */
|
|
112
|
-
|
|
113
|
-
/* The small menu hugs its longest label but never grows wider than the sidebar
|
|
114
|
-
content (--doc-version-picker-width, set by the sidebar). Labels wrap only at
|
|
115
|
-
that width. The large menu always matches its trigger (width-mode="full"). */
|
|
116
|
-
.version-picker-dropdown-small {
|
|
117
|
-
--dx-c-dropdown-menu-max-width: var(--doc-version-picker-width, 296px);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
.version-picker-button-small {
|
|
121
|
-
width: fit-content;
|
|
122
|
-
max-width: 104px;
|
|
52
|
+
dx-type-badge.latest-badge {
|
|
53
|
+
--dx-c-type-badge-color: var(--dx-g-green-vibrant-40);
|
|
54
|
+
--dx-c-type-badge-background: var(--dx-g-green-vibrant-95);
|
|
123
55
|
|
|
124
|
-
|
|
125
|
-
--dx-c-button-font-weight: var(--dx-g-font-normal);
|
|
126
|
-
--dx-c-button-line-height: var(--dx-g-spacing-lg);
|
|
127
|
-
--dx-c-button-icon-gap: var(--dx-g-spacing-2xs);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/* min-width: 0 on each flex ancestor lets the label truncate: a flex item's
|
|
131
|
-
default min-width: auto refuses to shrink below its content and overrides the
|
|
132
|
-
host's max-width, so without this the trigger overflows instead. */
|
|
133
|
-
.version-picker-button-small::part(content) {
|
|
134
|
-
display: flex;
|
|
135
|
-
flex: 1;
|
|
136
|
-
min-width: 0;
|
|
137
|
-
width: auto;
|
|
138
|
-
overflow: hidden;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/* width:100% re-ties to the 104px-capped host (container is width:inherit,
|
|
142
|
-
which copies width but not max-width). Border, fill, and states come from the
|
|
143
|
-
dx-button hooks on .version-picker-button. */
|
|
144
|
-
.version-picker-button-small::part(container) {
|
|
145
|
-
width: 100%;
|
|
146
|
-
height: var(--dx-g-spacing-lg);
|
|
147
|
-
padding: 0 var(--dx-g-spacing-xs);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.selected-version-small {
|
|
151
|
-
flex: 1;
|
|
152
|
-
min-width: 0;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
.version-picker-dot {
|
|
156
|
-
flex: 0 0 auto;
|
|
157
|
-
width: var(--dx-g-spacing-sm);
|
|
158
|
-
height: var(--dx-g-spacing-sm);
|
|
159
|
-
margin-right: var(--dx-g-spacing-xs);
|
|
160
|
-
border-radius: 50%;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
.version-picker-dot-latest {
|
|
164
|
-
background: var(--dx-g-green-vibrant-60);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
.version-picker-dot-not-latest {
|
|
168
|
-
background: var(--dx-g-yellow-vibrant-80);
|
|
56
|
+
margin-left: var(--dx-g-spacing-sm);
|
|
169
57
|
}
|
|
170
58
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
display: inline-block;
|
|
175
|
-
width: 100%;
|
|
176
|
-
height: var(--dx-g-spacing-xl);
|
|
177
|
-
margin: 0;
|
|
178
|
-
padding: 0 var(--dx-g-spacing-sm);
|
|
179
|
-
border: 1px solid var(--dx-g-gray-80);
|
|
180
|
-
border-radius: var(--dx-g-spacing-xs);
|
|
181
|
-
overflow: hidden;
|
|
182
|
-
color: var(--dx-g-gray-10);
|
|
183
|
-
text-overflow: ellipsis;
|
|
184
|
-
white-space: nowrap;
|
|
185
|
-
font: var(--dx-g-font-normal) var(--dx-g-text-sm) / var(--dx-g-spacing-xl)
|
|
186
|
-
var(--dx-g-font-sans),
|
|
187
|
-
sans-serif;
|
|
188
|
-
}
|
|
59
|
+
dx-type-badge.not-latest-badge {
|
|
60
|
+
--dx-c-type-badge-color: var(--dx-g-red-vibrant-40);
|
|
61
|
+
--dx-c-type-badge-background: var(--dx-g-red-vibrant-95);
|
|
189
62
|
|
|
190
|
-
|
|
191
|
-
width: fit-content;
|
|
192
|
-
max-width: 104px;
|
|
193
|
-
height: var(--dx-g-spacing-lg);
|
|
194
|
-
padding: 0 var(--dx-g-spacing-xs);
|
|
195
|
-
font: var(--dx-g-font-normal) var(--dx-g-text-xs) / var(--dx-g-spacing-lg)
|
|
196
|
-
var(--dx-g-font-sans),
|
|
197
|
-
sans-serif;
|
|
63
|
+
margin-left: var(--dx-g-spacing-sm);
|
|
198
64
|
}
|
|
@@ -1,70 +1,37 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div lwc:if={showVersionPicker} class=
|
|
3
|
-
<!-- Small read-only: plain value, no dropdown -->
|
|
4
|
-
<p
|
|
5
|
-
lwc:if={readOnly}
|
|
6
|
-
class={readOnlyClass}
|
|
7
|
-
title={selectedVersion.label}
|
|
8
|
-
>
|
|
9
|
-
{selectedVersion.label}
|
|
10
|
-
</p>
|
|
11
|
-
|
|
2
|
+
<div lwc:if={showVersionPicker} class="version-picker-container">
|
|
12
3
|
<dx-dropdown
|
|
13
|
-
lwc:else
|
|
14
|
-
class={dropdownClass}
|
|
15
4
|
options={versions}
|
|
16
5
|
analytics-event="custEv_docVersionSelect"
|
|
17
6
|
analytics-payload={analyticsPayload}
|
|
18
7
|
value={selectedVersion.id}
|
|
19
|
-
width-
|
|
8
|
+
width="var(--doc-version-picker-width)"
|
|
20
9
|
onchange={onVersionChange}
|
|
21
10
|
>
|
|
22
11
|
<dx-button
|
|
23
|
-
class=
|
|
24
|
-
variant="
|
|
12
|
+
class="version-picker-button"
|
|
13
|
+
variant="tertiary"
|
|
25
14
|
size="small"
|
|
26
|
-
font="sans"
|
|
27
15
|
icon-symbol="chevrondown"
|
|
28
|
-
icon-size=
|
|
29
|
-
aria-label={triggerAriaLabel}
|
|
16
|
+
icon-size="medium"
|
|
30
17
|
>
|
|
31
|
-
<div class=
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
title={selectedVersion.label}
|
|
49
|
-
>
|
|
50
|
-
{selectedVersion.label}
|
|
51
|
-
</p>
|
|
52
|
-
<template lwc:if={showLatestTag}>
|
|
53
|
-
<dx-type-badge
|
|
54
|
-
class="latest-badge"
|
|
55
|
-
lwc:if={latestVersion}
|
|
56
|
-
variant="status-success"
|
|
57
|
-
value="Latest"
|
|
58
|
-
size="small"
|
|
59
|
-
></dx-type-badge>
|
|
60
|
-
<dx-type-badge
|
|
61
|
-
class="not-latest-badge"
|
|
62
|
-
lwc:else
|
|
63
|
-
variant="status-warning"
|
|
64
|
-
value="Not Latest"
|
|
65
|
-
size="small"
|
|
66
|
-
></dx-type-badge>
|
|
67
|
-
</template>
|
|
18
|
+
<div class="selected-version">
|
|
19
|
+
<p class="selected-version-label">
|
|
20
|
+
{selectedVersion.label}
|
|
21
|
+
</p>
|
|
22
|
+
<template lwc:if={showLatestTag}>
|
|
23
|
+
<dx-type-badge
|
|
24
|
+
class="latest-badge"
|
|
25
|
+
lwc:if={latestVersion}
|
|
26
|
+
value="Latest"
|
|
27
|
+
size="small"
|
|
28
|
+
></dx-type-badge>
|
|
29
|
+
<dx-type-badge
|
|
30
|
+
class="not-latest-badge"
|
|
31
|
+
lwc:else
|
|
32
|
+
value="Not Latest"
|
|
33
|
+
size="small"
|
|
34
|
+
></dx-type-badge>
|
|
68
35
|
</template>
|
|
69
36
|
</div>
|
|
70
37
|
</dx-button>
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { LightningElement, api, track } from "lwc";
|
|
2
|
-
import cx from "classnames";
|
|
3
2
|
|
|
4
3
|
import { AnalyticsPayload, OptionWithNested } from "typings/custom";
|
|
5
4
|
|
|
@@ -13,8 +12,6 @@ export default class VersionPicker extends LightningElement {
|
|
|
13
12
|
private _selectedVersion?: OptionWithNested;
|
|
14
13
|
private _latestVersion: boolean = false;
|
|
15
14
|
private _hideBadge: boolean = false;
|
|
16
|
-
private _small: boolean = false;
|
|
17
|
-
private _readOnly: boolean = false;
|
|
18
15
|
|
|
19
16
|
@api
|
|
20
17
|
get versions() {
|
|
@@ -54,103 +51,14 @@ export default class VersionPicker extends LightningElement {
|
|
|
54
51
|
this._hideBadge = normalizeBoolean(value);
|
|
55
52
|
}
|
|
56
53
|
|
|
57
|
-
// Opt-in compact "dot" variant (24px, abbreviated label). Off by default so
|
|
58
|
-
// existing consumers render the default trigger unchanged.
|
|
59
|
-
@api
|
|
60
|
-
get small() {
|
|
61
|
-
return this._small;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
set small(value) {
|
|
65
|
-
this._small = normalizeBoolean(value);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Opt-in read-only state: plain value, no dropdown menu.
|
|
69
|
-
@api
|
|
70
|
-
get readOnly() {
|
|
71
|
-
return this._readOnly;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
set readOnly(value) {
|
|
75
|
-
this._readOnly = normalizeBoolean(value);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
54
|
private get showVersionPicker() {
|
|
79
55
|
return this._versions && this._versions.length !== 0;
|
|
80
56
|
}
|
|
81
57
|
|
|
82
|
-
private get containerClass(): string {
|
|
83
|
-
return this.small
|
|
84
|
-
? "version-picker-container-small"
|
|
85
|
-
: "version-picker-container";
|
|
86
|
-
}
|
|
87
|
-
|
|
88
58
|
private get showLatestTag(): boolean {
|
|
89
59
|
return !this.hideBadge;
|
|
90
60
|
}
|
|
91
61
|
|
|
92
|
-
private get readOnlyClass(): string {
|
|
93
|
-
return cx(
|
|
94
|
-
"version-picker-readonly",
|
|
95
|
-
this.small && "version-picker-readonly-small"
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
private get dotClass(): string {
|
|
100
|
-
return `version-picker-dot ${
|
|
101
|
-
this.latestVersion
|
|
102
|
-
? "version-picker-dot-latest"
|
|
103
|
-
: "version-picker-dot-not-latest"
|
|
104
|
-
}`;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Accessible name for the small trigger: includes the latest state so it
|
|
108
|
-
// isn't conveyed by the dot's color alone (WCAG 1.4.1). Omits the state
|
|
109
|
-
// when the indicator is hidden.
|
|
110
|
-
private get smallTriggerAriaLabel(): string {
|
|
111
|
-
const label = this.selectedVersion?.label ?? "";
|
|
112
|
-
if (!this.showLatestTag) {
|
|
113
|
-
return label;
|
|
114
|
-
}
|
|
115
|
-
return `${label}, ${this.latestVersion ? "Latest" : "Not Latest"}`;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// --- Per-variant values for the shared dropdown/button markup below. The
|
|
119
|
-
// small variant opts into extra classes and a compact chevron.
|
|
120
|
-
|
|
121
|
-
// Not cx(): the default must have NO class attribute (cx returns "", which
|
|
122
|
-
// would render class="" and break the frozen consumer snapshots).
|
|
123
|
-
private get dropdownClass(): string | undefined {
|
|
124
|
-
return this.small ? "version-picker-dropdown-small" : undefined;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// "full" matches the trigger (ToC width). "fit-end" sizes to the labels,
|
|
128
|
-
// caps at the ToC width, and opens inward.
|
|
129
|
-
private get dropdownWidthMode(): "full" | "fit-end" {
|
|
130
|
-
return this.small ? "fit-end" : "full";
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
private get triggerClass(): string {
|
|
134
|
-
return cx(
|
|
135
|
-
"version-picker-button",
|
|
136
|
-
this.small && "version-picker-button-small"
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
private get triggerIconSize(): string {
|
|
141
|
-
return this.small ? "xsmall" : "medium";
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Default returns "" (dx-button's own default) so the trigger's rendered
|
|
145
|
-
// aria-label is unchanged for the default variant.
|
|
146
|
-
private get triggerAriaLabel(): string {
|
|
147
|
-
return this.small ? this.smallTriggerAriaLabel : "";
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
private get selectedVersionClass(): string {
|
|
151
|
-
return cx("selected-version", this.small && "selected-version-small");
|
|
152
|
-
}
|
|
153
|
-
|
|
154
62
|
private onVersionChange(e: CustomEvent) {
|
|
155
63
|
this.dispatchEvent(new CustomEvent("change", { detail: e.detail }));
|
|
156
64
|
}
|
|
@@ -1,28 +1,10 @@
|
|
|
1
1
|
import { CoveoAnalyticsClient } from "coveo.analytics/dist/browser.mjs"; // This fix is required for Node 20 upgrade, so that Coveo analytics is loaded properly by LWR.
|
|
2
2
|
|
|
3
|
-
// Resolve against the current origin (version links are usually relative) and
|
|
4
|
-
// keep only http/https. Blocks executable schemes like javascript:/data: that
|
|
5
|
-
// would run when the innerHTML-rendered link is clicked
|
|
6
|
-
// encodeURI then keeps the value from breaking out of the href attribute.
|
|
7
|
-
const toSafeHref = (link: string): string => {
|
|
8
|
-
try {
|
|
9
|
-
const { protocol } = new URL(link, window.location.origin);
|
|
10
|
-
if (protocol === "http:" || protocol === "https:") {
|
|
11
|
-
return encodeURI(link);
|
|
12
|
-
}
|
|
13
|
-
} catch {
|
|
14
|
-
// Fall through to the rejection below.
|
|
15
|
-
}
|
|
16
|
-
console.warn(`Blocked unsafe version link: ${link}`);
|
|
17
|
-
return "#";
|
|
18
|
-
};
|
|
19
|
-
|
|
20
3
|
export const oldVersionDocInfo = (latestVersionLink: string) => {
|
|
21
|
-
const safeLink = toSafeHref(latestVersionLink);
|
|
22
4
|
return {
|
|
23
5
|
title: "Newer Version Available",
|
|
24
|
-
body: `This content describes an older version of this product.
|
|
25
|
-
<a style="font-weight: bold;" href="${
|
|
6
|
+
body: `This content describes an older version of this product.
|
|
7
|
+
<a style="font-weight: bold;" href="${latestVersionLink}">View Latest</a>`
|
|
26
8
|
};
|
|
27
9
|
};
|
|
28
10
|
|