@salesforcedevs/docs-components 1.29.0-alpha1 → 1.29.0-llm-alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lwc.config.json +3 -1
- package/package.json +3 -3
- package/src/modules/doc/aiToolbar/aiToolbar.css +40 -0
- package/src/modules/doc/aiToolbar/aiToolbar.html +53 -0
- package/src/modules/doc/aiToolbar/aiToolbar.ts +83 -0
- package/src/modules/doc/aiToolbar/aiToolbarMocks.ts +48 -0
- package/src/modules/doc/amfReference/amfReference.ts +52 -10
- package/src/modules/doc/amfReference/types.ts +5 -0
- package/src/modules/doc/banner/banner.css +88 -0
- package/src/modules/doc/banner/banner.html +47 -0
- package/src/modules/doc/banner/banner.ts +73 -0
- package/src/modules/doc/contentLayout/contentLayout.html +1 -1
- package/src/modules/doc/contentLayout/contentLayout.ts +106 -2
- package/src/modules/doc/header/header.html +0 -1
- package/src/modules/doc/localeBanner/localeBanner.css +3 -0
- package/src/modules/doc/localeBanner/localeBanner.html +9 -0
- package/src/modules/doc/localeBanner/localeBanner.ts +195 -0
- package/src/modules/doc/lwcContentLayout/lwcContentLayout.html +5 -2
- package/src/modules/doc/redocReference/redocReference.ts +157 -121
- package/src/modules/doc/xmlContent/xmlContent.html +1 -1
- package/src/modules/doc/xmlContent/xmlContent.ts +28 -1
- package/src/modules/doc/apiPlayground/apiPlayground.css +0 -186
- package/src/modules/doc/apiPlayground/apiPlayground.html +0 -136
- package/src/modules/doc/apiPlayground/apiPlayground.ts +0 -240
- package/src/modules/docUtils/apiRequestExecutor/apiRequestExecutor.ts +0 -96
- package/src/modules/docUtils/openApiParser/openApiParser.ts +0 -187
package/lwc.config.json
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforcedevs/docs-components",
|
|
3
|
-
"version": "1.29.0-alpha1",
|
|
3
|
+
"version": "1.29.0-llm-alpha1",
|
|
4
4
|
"description": "Docs Lightning web components for DSC",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"engines": {
|
|
8
|
-
"node": "
|
|
8
|
+
"node": "22.x"
|
|
9
9
|
},
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public"
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
"@types/lodash.orderby": "4.6.9",
|
|
26
26
|
"@types/lodash.uniqby": "4.7.9"
|
|
27
27
|
},
|
|
28
|
-
"
|
|
28
|
+
"gitHead": "4629fdd9ca18a13480044ad43515b91945d16aad"
|
|
29
29
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
@import "dxHelpers/reset";
|
|
2
|
+
|
|
3
|
+
:host {
|
|
4
|
+
display: inline-flex;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.ai-toolbar {
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: var(--dx-g-spacing-sm);
|
|
11
|
+
padding-bottom: var(--dx-g-spacing-sm);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.toolbar-button {
|
|
15
|
+
--dx-g-button-inline-color: var(--dx-g-blue-vibrant-50);
|
|
16
|
+
--dx-g-button-inline-color-hover: var(--dx-g-blue-vibrant-30);
|
|
17
|
+
--dx-c-button-font-size: var(--dx-g-text-sm);
|
|
18
|
+
--dx-c-button-horizontal-spacing: 0;
|
|
19
|
+
--dx-c-button-icon-vertical-align: middle;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.toolbar-button::part(content) {
|
|
23
|
+
font-family: var(--dx-g-font-display);
|
|
24
|
+
font-weight: var(--dx-g-font-demi);
|
|
25
|
+
line-height: var(--dx-g-spacing-mlg);
|
|
26
|
+
letter-spacing: 0.07px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.divider {
|
|
30
|
+
width: 1px;
|
|
31
|
+
height: var(--dx-g-spacing-md);
|
|
32
|
+
background-color: var(--dx-g-gray-70);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@media screen and (max-width: 480px) {
|
|
36
|
+
.toolbar-button_copy-url,
|
|
37
|
+
.divider_copy-url {
|
|
38
|
+
display: none;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="ai-toolbar">
|
|
3
|
+
<dx-tooltip placement="top-right" label={copyMarkdownLabel}>
|
|
4
|
+
<dx-button
|
|
5
|
+
class="toolbar-button"
|
|
6
|
+
variant="inline"
|
|
7
|
+
size="small"
|
|
8
|
+
icon-sprite="utility"
|
|
9
|
+
icon-symbol="copy"
|
|
10
|
+
icon-size="medium"
|
|
11
|
+
icon-position="right"
|
|
12
|
+
aria-label="Copy as Markdown"
|
|
13
|
+
onclick={handleCopyMarkdown}
|
|
14
|
+
>
|
|
15
|
+
Copy as Markdown
|
|
16
|
+
</dx-button>
|
|
17
|
+
</dx-tooltip>
|
|
18
|
+
|
|
19
|
+
<div class="divider"></div>
|
|
20
|
+
|
|
21
|
+
<dx-button
|
|
22
|
+
class="toolbar-button"
|
|
23
|
+
variant="inline"
|
|
24
|
+
size="small"
|
|
25
|
+
icon-sprite="utility"
|
|
26
|
+
icon-symbol="new_window"
|
|
27
|
+
icon-size="medium"
|
|
28
|
+
icon-position="right"
|
|
29
|
+
aria-label="View as Markdown"
|
|
30
|
+
onclick={handleViewMarkdown}
|
|
31
|
+
>
|
|
32
|
+
View as Markdown
|
|
33
|
+
</dx-button>
|
|
34
|
+
|
|
35
|
+
<div class="divider divider_copy-url"></div>
|
|
36
|
+
|
|
37
|
+
<dx-tooltip placement="top-right" label={copyUrlLabel}>
|
|
38
|
+
<dx-button
|
|
39
|
+
class="toolbar-button toolbar-button_copy-url"
|
|
40
|
+
variant="inline"
|
|
41
|
+
size="small"
|
|
42
|
+
icon-sprite="utility"
|
|
43
|
+
icon-symbol="link"
|
|
44
|
+
icon-size="medium"
|
|
45
|
+
icon-position="right"
|
|
46
|
+
aria-label="Copy URL to Markdown"
|
|
47
|
+
onclick={handleCopyUrl}
|
|
48
|
+
>
|
|
49
|
+
Copy URL to Markdown
|
|
50
|
+
</dx-button>
|
|
51
|
+
</dx-tooltip>
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { LightningElement } from "lwc";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_COPY_TOOLTIP_LABEL = "Click to copy";
|
|
4
|
+
const COPIED_TOOLTIP_LABEL = "Copied!";
|
|
5
|
+
const COPIED_TOOLTIP_RESET_MS = 2000;
|
|
6
|
+
|
|
7
|
+
export default class AiToolbar extends LightningElement {
|
|
8
|
+
copyMarkdownLabel: string = DEFAULT_COPY_TOOLTIP_LABEL;
|
|
9
|
+
copyUrlLabel: string = DEFAULT_COPY_TOOLTIP_LABEL;
|
|
10
|
+
|
|
11
|
+
private copyTooltipResetTimeout: number | null = null;
|
|
12
|
+
|
|
13
|
+
async handleCopyMarkdown() {
|
|
14
|
+
const markdownUrl = this.getMarkdownUrl();
|
|
15
|
+
if (!markdownUrl) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const response = await fetch(markdownUrl);
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const markdown = await response.text();
|
|
25
|
+
await navigator.clipboard.writeText(markdown);
|
|
26
|
+
this.flashCopied("copyMarkdownLabel");
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error(error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
handleViewMarkdown() {
|
|
33
|
+
const markdownUrl = this.getMarkdownUrl();
|
|
34
|
+
if (!markdownUrl) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
window.open(markdownUrl, "_blank", "noopener,noreferrer");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async handleCopyUrl() {
|
|
41
|
+
const markdownUrl = this.getMarkdownUrl();
|
|
42
|
+
if (!markdownUrl) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
await navigator.clipboard.writeText(markdownUrl);
|
|
48
|
+
this.flashCopied("copyUrlLabel");
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error(error);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Returns the `.md` equivalent of the current page URL with any hash and
|
|
56
|
+
* query string stripped, or `null` when the current page does not end
|
|
57
|
+
* with `.html`.
|
|
58
|
+
*/
|
|
59
|
+
private getMarkdownUrl(): string | null {
|
|
60
|
+
const url = new URL(window.location.href);
|
|
61
|
+
url.hash = "";
|
|
62
|
+
url.search = "";
|
|
63
|
+
|
|
64
|
+
if (!url.pathname.endsWith(".html")) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
url.pathname = url.pathname.replace(/\.html$/, ".md");
|
|
69
|
+
return url.toString();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private flashCopied(labelKey: "copyMarkdownLabel" | "copyUrlLabel") {
|
|
73
|
+
if (this.copyTooltipResetTimeout !== null) {
|
|
74
|
+
window.clearTimeout(this.copyTooltipResetTimeout);
|
|
75
|
+
}
|
|
76
|
+
this[labelKey] = COPIED_TOOLTIP_LABEL;
|
|
77
|
+
this.copyTooltipResetTimeout = window.setTimeout(() => {
|
|
78
|
+
this.copyMarkdownLabel = DEFAULT_COPY_TOOLTIP_LABEL;
|
|
79
|
+
this.copyUrlLabel = DEFAULT_COPY_TOOLTIP_LABEL;
|
|
80
|
+
this.copyTooltipResetTimeout = null;
|
|
81
|
+
}, COPIED_TOOLTIP_RESET_MS);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { http, HttpResponse } from "msw";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mocks for the AI toolbar so stories never hit the real
|
|
5
|
+
* docs backend. Any story rendering `doc-ai-toolbar` (directly or via
|
|
6
|
+
* `doc-content-layout`) must register `aiToolbarMswHandlers` and call
|
|
7
|
+
* `interceptWindowOpenForAiToolbar()`.
|
|
8
|
+
*/
|
|
9
|
+
export const DUMMY_MARKDOWN_CONTENT = `# Dummy Markdown
|
|
10
|
+
|
|
11
|
+
Storybook serves this placeholder content in place of the real markdown
|
|
12
|
+
that the docs backend would return for the current page. It exists so
|
|
13
|
+
the "Copied" tooltip, the "View as Markdown" new tab, and the
|
|
14
|
+
"Copy URL to Markdown" clipboard behavior are all exercisable in
|
|
15
|
+
storybook without hitting the live backend.
|
|
16
|
+
|
|
17
|
+
- Item 1
|
|
18
|
+
- Item 2
|
|
19
|
+
- Item 3
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
export const DUMMY_MARKDOWN_DATA_URL = `data:text/markdown;charset=utf-8,${encodeURIComponent(
|
|
23
|
+
DUMMY_MARKDOWN_CONTENT
|
|
24
|
+
)}`;
|
|
25
|
+
|
|
26
|
+
/** Intercepts any `.md` GET request and returns the dummy markdown. */
|
|
27
|
+
export const aiToolbarMswHandlers = [
|
|
28
|
+
http.get(/\.md(\?.*)?$/, () => HttpResponse.text(DUMMY_MARKDOWN_CONTENT))
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
let windowOpenIntercepted = false;
|
|
32
|
+
|
|
33
|
+
/** Redirects `window.open` calls for `.md` URLs to the dummy markdown data URL (MSW does not cover new tabs). */
|
|
34
|
+
export function interceptWindowOpenForAiToolbar() {
|
|
35
|
+
if (windowOpenIntercepted) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
windowOpenIntercepted = true;
|
|
39
|
+
|
|
40
|
+
const originalOpen = window.open.bind(window);
|
|
41
|
+
window.open = ((url?: string | URL, target?: string, features?: string) => {
|
|
42
|
+
const stringUrl = url?.toString() ?? "";
|
|
43
|
+
if (stringUrl.endsWith(".md")) {
|
|
44
|
+
return originalOpen(DUMMY_MARKDOWN_DATA_URL, target, features);
|
|
45
|
+
}
|
|
46
|
+
return originalOpen(url ?? "", target, features);
|
|
47
|
+
}) as typeof window.open;
|
|
48
|
+
}
|
|
@@ -37,6 +37,7 @@ type NavigationItem = {
|
|
|
37
37
|
isExpanded: boolean;
|
|
38
38
|
children: ParsedMarkdownTopic[];
|
|
39
39
|
isChildrenLoading: boolean;
|
|
40
|
+
showForwardArrow?: boolean;
|
|
40
41
|
};
|
|
41
42
|
|
|
42
43
|
export default class AmfReference extends LightningElement {
|
|
@@ -204,6 +205,7 @@ export default class AmfReference extends LightningElement {
|
|
|
204
205
|
|
|
205
206
|
_boundOnApiNavigationChanged;
|
|
206
207
|
_boundUpdateSelectedItemFromUrlQuery;
|
|
208
|
+
_boundOnPageShow;
|
|
207
209
|
|
|
208
210
|
constructor() {
|
|
209
211
|
super();
|
|
@@ -212,6 +214,7 @@ export default class AmfReference extends LightningElement {
|
|
|
212
214
|
this.onApiNavigationChanged.bind(this);
|
|
213
215
|
this._boundUpdateSelectedItemFromUrlQuery =
|
|
214
216
|
this.updateSelectedItemFromUrlQuery.bind(this);
|
|
217
|
+
this._boundOnPageShow = this.onPageShow.bind(this);
|
|
215
218
|
}
|
|
216
219
|
|
|
217
220
|
connectedCallback(): void {
|
|
@@ -223,6 +226,7 @@ export default class AmfReference extends LightningElement {
|
|
|
223
226
|
"popstate",
|
|
224
227
|
this._boundUpdateSelectedItemFromUrlQuery
|
|
225
228
|
);
|
|
229
|
+
window.addEventListener("pageshow", this._boundOnPageShow);
|
|
226
230
|
}
|
|
227
231
|
|
|
228
232
|
disconnectedCallback(): void {
|
|
@@ -234,6 +238,22 @@ export default class AmfReference extends LightningElement {
|
|
|
234
238
|
"popstate",
|
|
235
239
|
this._boundUpdateSelectedItemFromUrlQuery
|
|
236
240
|
);
|
|
241
|
+
window.removeEventListener("pageshow", this._boundOnPageShow);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* On bfcache restore, reset the sidebar selection so the tree re-syncs
|
|
246
|
+
* its highlighted tile with the current URL.
|
|
247
|
+
*/
|
|
248
|
+
protected onPageShow(event: PageTransitionEvent): void {
|
|
249
|
+
if (!event.persisted) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const currentPath = window.location.pathname;
|
|
253
|
+
this.selectedSidebarValue = "";
|
|
254
|
+
Promise.resolve().then(() => {
|
|
255
|
+
this.selectedSidebarValue = currentPath;
|
|
256
|
+
});
|
|
237
257
|
}
|
|
238
258
|
|
|
239
259
|
renderedCallback(): void {
|
|
@@ -443,16 +463,21 @@ export default class AmfReference extends LightningElement {
|
|
|
443
463
|
let navItemChildren = [] as ParsedMarkdownTopic[];
|
|
444
464
|
let isChildrenLoading = false;
|
|
445
465
|
if (amfConfig.referenceType !== REFERENCE_TYPES.markdown) {
|
|
446
|
-
if (amfConfig.
|
|
447
|
-
|
|
448
|
-
(
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
466
|
+
if (amfConfig.amf) {
|
|
467
|
+
if (amfConfig.isSelected) {
|
|
468
|
+
const amfPromise = this.fetchAmf(amfConfig).then(
|
|
469
|
+
(amfJson) => {
|
|
470
|
+
this.updateModel(amfConfig.id, amfJson);
|
|
471
|
+
this.assignNavigationItemsFromAmf(
|
|
472
|
+
amfConfig,
|
|
473
|
+
index
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
);
|
|
477
|
+
this.amfFetchPromiseMap[amfConfig.id] = amfPromise;
|
|
478
|
+
}
|
|
479
|
+
isChildrenLoading = true;
|
|
454
480
|
}
|
|
455
|
-
isChildrenLoading = true;
|
|
456
481
|
} else {
|
|
457
482
|
const isExpandChildrenEnabled = this.isExpandChildrenEnabled(
|
|
458
483
|
amfConfig.id
|
|
@@ -473,13 +498,30 @@ export default class AmfReference extends LightningElement {
|
|
|
473
498
|
amfConfig.isSelected ||
|
|
474
499
|
this.isExpandChildrenEnabled(amfConfig.id),
|
|
475
500
|
children: navItemChildren,
|
|
476
|
-
isChildrenLoading
|
|
501
|
+
isChildrenLoading,
|
|
502
|
+
showForwardArrow: this.resolveNavRenderWith(amfConfig)
|
|
477
503
|
};
|
|
478
504
|
this.parentReferenceUrls.push(amfConfig.href);
|
|
479
505
|
}
|
|
480
506
|
this.navigation = navAmfOrder;
|
|
481
507
|
}
|
|
482
508
|
|
|
509
|
+
/**
|
|
510
|
+
* Determines whether the sidebar tile should render a forward arrow for
|
|
511
|
+
* this reference. Honors `redoc` set on the config, and also infers it
|
|
512
|
+
* for non-markdown references that have no AMF URL (i.e. those rendered
|
|
513
|
+
* by Redoc).
|
|
514
|
+
*/
|
|
515
|
+
private resolveNavRenderWith(amfConfig: AmfConfig): boolean {
|
|
516
|
+
if (amfConfig.renderWith) {
|
|
517
|
+
return amfConfig.renderWith === "redoc";
|
|
518
|
+
}
|
|
519
|
+
return (
|
|
520
|
+
amfConfig.referenceType !== REFERENCE_TYPES.markdown &&
|
|
521
|
+
!amfConfig.amf
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
483
525
|
/**
|
|
484
526
|
* Returns a boolean indicating whether the children should be expanded or not.
|
|
485
527
|
*/
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
@import "dxHelpers/reset";
|
|
2
|
+
@import "dxHelpers/text";
|
|
3
|
+
|
|
4
|
+
:host {
|
|
5
|
+
display: block;
|
|
6
|
+
|
|
7
|
+
--doc-banner-padding-left: var(--dx-g-spacing-2xl);
|
|
8
|
+
--doc-banner-padding-right: var(--dx-g-spacing-lg);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.container {
|
|
12
|
+
display: flex;
|
|
13
|
+
align-items: flex-start;
|
|
14
|
+
background: var(--dx-g-gray-90);
|
|
15
|
+
padding: 0 var(--doc-banner-padding-right) 0 var(--doc-banner-padding-left);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.icon {
|
|
19
|
+
--dx-c-icon-size: var(--dx-g-icon-size-lg);
|
|
20
|
+
|
|
21
|
+
flex-shrink: 0;
|
|
22
|
+
margin-top: var(--dx-g-spacing-smd);
|
|
23
|
+
margin-right: var(--dx-g-spacing-sm);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.main {
|
|
27
|
+
flex: 1;
|
|
28
|
+
min-width: 0;
|
|
29
|
+
display: flex;
|
|
30
|
+
flex-wrap: wrap;
|
|
31
|
+
align-items: flex-start;
|
|
32
|
+
column-gap: var(--dx-g-spacing-md);
|
|
33
|
+
padding: calc((var(--dx-g-spacing-xs) + var(--dx-g-spacing-sm)) / 2) 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.message {
|
|
37
|
+
flex: 0 1 auto;
|
|
38
|
+
font-size: var(--dx-g-text-sm);
|
|
39
|
+
color: var(--dx-g-gray-10);
|
|
40
|
+
padding: calc((var(--dx-g-spacing-xs) + var(--dx-g-spacing-sm)) / 2) 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.message a {
|
|
44
|
+
color: var(--dx-g-cloud-blue-vibrant-50);
|
|
45
|
+
text-decoration: underline;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.actions {
|
|
49
|
+
flex: 0 0 auto;
|
|
50
|
+
display: flex;
|
|
51
|
+
align-items: center;
|
|
52
|
+
gap: var(--dx-g-spacing-smd);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.actions dx-button {
|
|
56
|
+
--dx-c-button-font-size: var(--dx-g-text-sm);
|
|
57
|
+
--dx-c-button-font-weight: var(--dx-g-font-normal);
|
|
58
|
+
|
|
59
|
+
flex: 0 0 auto;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.close {
|
|
63
|
+
--dx-c-icon-size: var(--dx-g-icon-size-lg);
|
|
64
|
+
|
|
65
|
+
flex-shrink: 0;
|
|
66
|
+
align-self: flex-start;
|
|
67
|
+
width: calc(var(--dx-g-spacing-3xl) + var(--dx-g-spacing-xs));
|
|
68
|
+
height: calc(var(--dx-g-spacing-2xl) + var(--dx-g-spacing-xs));
|
|
69
|
+
margin-left: auto;
|
|
70
|
+
display: flex;
|
|
71
|
+
align-items: center;
|
|
72
|
+
justify-content: center;
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@media (max-width: 1279px) {
|
|
77
|
+
:host {
|
|
78
|
+
--doc-banner-padding-left: var(--dx-g-spacing-xl);
|
|
79
|
+
--doc-banner-padding-right: var(--dx-g-spacing-md);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@media (max-width: 768px) {
|
|
84
|
+
:host {
|
|
85
|
+
--doc-banner-padding-left: var(--dx-g-spacing-lg);
|
|
86
|
+
--doc-banner-padding-right: var(--dx-g-spacing-sm);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template lwc:if={showBanner}>
|
|
3
|
+
<div class="container" part="container">
|
|
4
|
+
<dx-icon
|
|
5
|
+
class="icon"
|
|
6
|
+
symbol="info"
|
|
7
|
+
size="override"
|
|
8
|
+
color="gray-50"
|
|
9
|
+
part="icon"
|
|
10
|
+
></dx-icon>
|
|
11
|
+
<div class="main">
|
|
12
|
+
<div class="message" part="message" lwc:dom="manual"></div>
|
|
13
|
+
<div class="actions" part="actions">
|
|
14
|
+
<template lwc:if={hasPrimaryButton}>
|
|
15
|
+
<dx-button
|
|
16
|
+
href={buttonHref}
|
|
17
|
+
variant="primary"
|
|
18
|
+
size="small"
|
|
19
|
+
part="button"
|
|
20
|
+
>
|
|
21
|
+
{buttonLabel}
|
|
22
|
+
</dx-button>
|
|
23
|
+
</template>
|
|
24
|
+
<template lwc:if={hasSecondaryAction}>
|
|
25
|
+
<dx-button
|
|
26
|
+
variant="inline"
|
|
27
|
+
onclick={handleSecondaryClick}
|
|
28
|
+
part="secondary"
|
|
29
|
+
>
|
|
30
|
+
{secondaryLabel}
|
|
31
|
+
</dx-button>
|
|
32
|
+
</template>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<dx-button
|
|
36
|
+
class="close"
|
|
37
|
+
variant="icon-only"
|
|
38
|
+
icon-symbol="close"
|
|
39
|
+
icon-size="override"
|
|
40
|
+
icon-color="gray-50"
|
|
41
|
+
aria-label="Close"
|
|
42
|
+
onclick={handleCloseClick}
|
|
43
|
+
part="close"
|
|
44
|
+
></dx-button>
|
|
45
|
+
</div>
|
|
46
|
+
</template>
|
|
47
|
+
</template>
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { LightningElement, api } from "lwc";
|
|
2
|
+
|
|
3
|
+
const BANNER_STORAGE_PREFIX = "doc-banner-";
|
|
4
|
+
|
|
5
|
+
export default class Banner extends LightningElement {
|
|
6
|
+
@api message = "";
|
|
7
|
+
|
|
8
|
+
@api buttonLabel = "";
|
|
9
|
+
|
|
10
|
+
@api buttonHref = "";
|
|
11
|
+
|
|
12
|
+
@api secondaryLabel = "";
|
|
13
|
+
|
|
14
|
+
@api dismissStorageKey = "";
|
|
15
|
+
|
|
16
|
+
private _dismissed = false;
|
|
17
|
+
|
|
18
|
+
connectedCallback() {
|
|
19
|
+
if (this.dismissStorageKey && window?.sessionStorage) {
|
|
20
|
+
this._dismissed =
|
|
21
|
+
window.sessionStorage.getItem(
|
|
22
|
+
`${BANNER_STORAGE_PREFIX}${this.dismissStorageKey}`
|
|
23
|
+
) === "true";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
renderedCallback() {
|
|
28
|
+
if (this.message) {
|
|
29
|
+
const messageElement = this.template.querySelector(".message");
|
|
30
|
+
if (messageElement) {
|
|
31
|
+
messageElement.innerHTML = this.message;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get showBanner(): boolean {
|
|
37
|
+
return !this._dismissed;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get hasPrimaryButton(): boolean {
|
|
41
|
+
return !!(this.buttonLabel && this.buttonHref);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get hasSecondaryAction(): boolean {
|
|
45
|
+
return !!this.secondaryLabel;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private dismissBanner() {
|
|
49
|
+
if (this.dismissStorageKey && window?.sessionStorage) {
|
|
50
|
+
window.sessionStorage.setItem(
|
|
51
|
+
`${BANNER_STORAGE_PREFIX}${this.dismissStorageKey}`,
|
|
52
|
+
"true"
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
this._dismissed = true;
|
|
56
|
+
this.dispatchEvent(
|
|
57
|
+
new CustomEvent("dismissbanner", {
|
|
58
|
+
bubbles: true,
|
|
59
|
+
composed: true
|
|
60
|
+
})
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@api
|
|
65
|
+
handleSecondaryClick() {
|
|
66
|
+
this.dismissBanner();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@api
|
|
70
|
+
handleCloseClick() {
|
|
71
|
+
this.dismissBanner();
|
|
72
|
+
}
|
|
73
|
+
}
|