@salesforcedevs/docs-components 0.0.4 → 0.0.5-edit
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 +25 -2
- package/package.json +18 -7
- package/src/modules/README.md +41 -0
- package/src/modules/doc/amfModelParser/amfModelParser.ts +674 -0
- package/src/modules/doc/amfReference/amfReference.css +25 -0
- package/src/modules/doc/amfReference/amfReference.html +60 -0
- package/src/modules/doc/amfReference/amfReference.ts +1494 -0
- package/src/modules/doc/amfReference/constants.ts +76 -0
- package/src/modules/doc/amfReference/types.ts +125 -0
- package/src/modules/doc/amfTopic/amfTopic.css +21 -0
- package/src/modules/doc/amfTopic/amfTopic.html +3 -0
- package/src/modules/doc/amfTopic/amfTopic.ts +111 -0
- package/src/modules/doc/amfTopic/types.ts +56 -0
- package/src/modules/doc/amfTopic/utils.ts +136 -0
- package/src/modules/doc/breadcrumbItem/breadcrumbItem.css +51 -0
- package/src/modules/doc/breadcrumbItem/breadcrumbItem.html +5 -0
- package/src/modules/doc/breadcrumbItem/breadcrumbItem.ts +71 -0
- package/src/modules/doc/breadcrumbs/breadcrumbs.css +27 -0
- package/src/modules/doc/breadcrumbs/breadcrumbs.html +58 -0
- package/src/modules/doc/breadcrumbs/breadcrumbs.ts +183 -0
- package/src/modules/doc/chat/README.md +179 -0
- package/src/modules/doc/chat/chat.css +821 -0
- package/src/modules/doc/chat/chat.html +241 -0
- package/src/modules/doc/chat/chat.ts +586 -0
- package/src/modules/doc/componentPlayground/componentPlayground.css +22 -0
- package/src/modules/doc/componentPlayground/componentPlayground.html +20 -0
- package/src/modules/doc/componentPlayground/componentPlayground.ts +29 -0
- package/src/modules/doc/content/content.css +382 -6
- package/src/modules/doc/content/content.html +3 -2
- package/src/modules/doc/content/content.ts +287 -110
- package/src/modules/doc/contentCallout/contentCallout.css +25 -26
- package/src/modules/doc/contentCallout/contentCallout.html +13 -4
- package/src/modules/doc/contentCallout/contentCallout.ts +22 -11
- package/src/modules/doc/contentLayout/contentLayout.css +13 -0
- package/src/modules/doc/contentLayout/contentLayout.html +73 -0
- package/src/modules/doc/contentLayout/contentLayout.ts +531 -0
- package/src/modules/doc/contentMedia/contentMedia.css +49 -0
- package/src/modules/doc/contentMedia/contentMedia.html +23 -0
- package/src/modules/doc/contentMedia/contentMedia.ts +34 -0
- package/src/modules/doc/doDont/doDont.css +47 -0
- package/src/modules/doc/doDont/doDont.html +27 -0
- package/src/modules/doc/doDont/doDont.ts +17 -0
- package/src/modules/doc/editFile/editFile.css +505 -0
- package/src/modules/doc/editFile/editFile.html +164 -0
- package/src/modules/doc/editFile/editFile.ts +213 -0
- package/src/modules/doc/header/header.css +132 -0
- package/src/modules/doc/header/header.html +55 -0
- package/src/modules/doc/header/header.ts +120 -0
- package/src/modules/doc/heading/heading.css +33 -0
- package/src/modules/doc/heading/heading.html +14 -0
- package/src/modules/doc/heading/heading.ts +67 -0
- package/src/modules/doc/headingAnchor/headingAnchor.css +33 -0
- package/src/modules/doc/headingAnchor/headingAnchor.html +19 -0
- package/src/modules/doc/headingAnchor/headingAnchor.ts +43 -0
- package/src/modules/doc/headingContent/headingContent.css +53 -0
- package/src/modules/doc/headingContent/headingContent.html +13 -0
- package/src/modules/doc/headingContent/headingContent.ts +30 -0
- package/src/modules/doc/lwcContentLayout/lwcContentLayout.css +1 -0
- package/src/modules/doc/lwcContentLayout/lwcContentLayout.html +68 -0
- package/src/modules/doc/lwcContentLayout/lwcContentLayout.ts +168 -0
- package/src/modules/doc/nav/nav.css +4 -2
- package/src/modules/doc/nav/nav.html +8 -13
- package/src/modules/doc/nav/nav.ts +1 -1
- package/src/modules/doc/overview/overview.css +40 -0
- package/src/modules/doc/overview/overview.html +34 -0
- package/src/modules/doc/overview/overview.ts +12 -0
- package/src/modules/doc/phase/phase.css +70 -0
- package/src/modules/doc/phase/phase.html +38 -0
- package/src/modules/doc/phase/phase.ts +93 -0
- package/src/modules/doc/specificationContent/specificationContent.css +36 -0
- package/src/modules/doc/specificationContent/specificationContent.html +171 -0
- package/src/modules/doc/specificationContent/specificationContent.ts +127 -0
- package/src/modules/doc/sprigSurvey/sprigSurvey.html +20 -0
- package/src/modules/doc/sprigSurvey/sprigSurvey.scoped.css +16 -0
- package/src/modules/doc/sprigSurvey/sprigSurvey.ts +16 -0
- package/src/modules/doc/toc/toc.html +11 -6
- package/src/modules/doc/toc/toc.ts +2 -6
- package/src/modules/doc/toolbar/toolbar.html +8 -1
- package/src/modules/doc/toolbar/toolbar.ts +1 -1
- package/src/modules/doc/versionPicker/versionPicker.css +64 -0
- package/src/modules/doc/versionPicker/versionPicker.html +38 -0
- package/src/modules/doc/versionPicker/versionPicker.ts +65 -0
- package/src/modules/doc/xmlContent/types.ts +120 -0
- package/src/modules/doc/xmlContent/utils.ts +163 -0
- package/src/modules/doc/xmlContent/xmlContent.css +54 -0
- package/src/modules/doc/xmlContent/xmlContent.html +52 -0
- package/src/modules/doc/xmlContent/xmlContent.ts +792 -0
- package/src/modules/docHelpers/amfStyle/amfStyle.css +355 -0
- package/src/modules/docHelpers/contentLayoutStyle/contentLayoutStyle.css +131 -0
- package/src/modules/docHelpers/imgStyle/imgStyle.css +59 -0
- package/src/modules/docHelpers/status/status.css +22 -0
- package/src/modules/docUtils/searchSyncer/searchSyncer.ts +86 -0
- package/src/modules/docUtils/utils/__mocks__/coveo.analytics.ts +16 -0
- package/src/modules/docUtils/utils/coveo.analytics.d.ts +10 -0
- package/src/modules/docUtils/utils/utils.ts +32 -0
- package/src/modules/doc/container/__benchmarks__/container.benchmark.js +0 -43
- package/src/modules/doc/container/__mocks__/mockAvailableLanguages.js +0 -8
- package/src/modules/doc/container/__mocks__/mockAvailableVersions.js +0 -122
- package/src/modules/doc/container/__mocks__/mockContentFetchResponse.json +0 -5
- package/src/modules/doc/container/__mocks__/mockDocContent.js +0 -29
- package/src/modules/doc/container/__mocks__/mockNavigationFetchResponse.json +0 -4061
- package/src/modules/doc/container/__mocks__/mockPageReference.js +0 -8
- package/src/modules/doc/container/__mocks__/mockPdfUrl.js +0 -1
- package/src/modules/doc/container/__mocks__/mockSelectedLanguage.js +0 -8
- package/src/modules/doc/container/__mocks__/mockSelectedVersion.js +0 -8
- package/src/modules/doc/container/__mocks__/mockToc.js +0 -146
- package/src/modules/doc/container/__tests__/container.test.ts +0 -82
- package/src/modules/doc/container/container.css +0 -33
- package/src/modules/doc/container/container.html +0 -23
- package/src/modules/doc/container/container.stories.ts +0 -18
- package/src/modules/doc/container/container.ts +0 -360
- package/src/modules/doc/content/__tests__/content.test.ts +0 -30
- package/src/modules/doc/content/__tests__/mockDocContent.ts +0 -29
- package/src/modules/doc/content/__tests__/mockPageReference.ts +0 -8
- package/src/modules/doc/contentCallout/__tests__/contentCallout.test.ts +0 -80
- package/src/modules/doc/contentCallout/__tests__/mockProps.ts +0 -14
- package/src/modules/doc/contentCallout/contentCallout.stories.ts +0 -29
- package/src/modules/doc/nav/__tests__/mockAvailableLanguages.ts +0 -8
- package/src/modules/doc/nav/__tests__/mockAvailableVersions.ts +0 -122
- package/src/modules/doc/nav/__tests__/mockPageReference.ts +0 -8
- package/src/modules/doc/nav/__tests__/mockPdfUrl.ts +0 -1
- package/src/modules/doc/nav/__tests__/mockSelectedLanguage.ts +0 -8
- package/src/modules/doc/nav/__tests__/mockSelectedVersion.ts +0 -8
- package/src/modules/doc/nav/__tests__/mockToc.ts +0 -146
- package/src/modules/doc/nav/__tests__/nav.test.ts +0 -66
- package/src/modules/doc/prismcss/prismcss.css +0 -184
- package/src/modules/doc/prismjs/prismjs.html +0 -3
- package/src/modules/doc/prismjs/prismjs.ts +0 -1842
- package/src/modules/doc/search/__tests__/search.test.ts +0 -20
- package/src/modules/doc/search/search.html +0 -1
- package/src/modules/doc/search/search.ts +0 -3
- package/src/modules/doc/toc/__tests__/mockPageReference.ts +0 -8
- package/src/modules/doc/toc/__tests__/mockToc.ts +0 -146
- package/src/modules/doc/toc/__tests__/toc.test.ts +0 -29
- package/src/modules/doc/toolbar/__tests__/mockAvailableLanguages.ts +0 -8
- package/src/modules/doc/toolbar/__tests__/mockAvailableVersions.ts +0 -122
- package/src/modules/doc/toolbar/__tests__/mockPdfUrl.ts +0 -1
- package/src/modules/doc/toolbar/__tests__/mockSelectedLanguage.ts +0 -8
- package/src/modules/doc/toolbar/__tests__/mockSelectedVersion.ts +0 -8
- package/src/modules/doc/toolbar/__tests__/toolbar.test.ts +0 -44
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- Edit File Trigger Button -->
|
|
3
|
+
<button
|
|
4
|
+
class="edit-file-trigger"
|
|
5
|
+
onclick={handleEditClick}
|
|
6
|
+
disabled={disabled}
|
|
7
|
+
aria-label="Edit file"
|
|
8
|
+
title="Edit file"
|
|
9
|
+
>
|
|
10
|
+
<svg
|
|
11
|
+
class="edit-file-icon"
|
|
12
|
+
viewBox="0 0 24 24"
|
|
13
|
+
fill="none"
|
|
14
|
+
stroke="currentColor"
|
|
15
|
+
>
|
|
16
|
+
<path
|
|
17
|
+
stroke-linecap="round"
|
|
18
|
+
stroke-linejoin="round"
|
|
19
|
+
stroke-width="2"
|
|
20
|
+
d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"
|
|
21
|
+
/>
|
|
22
|
+
</svg>
|
|
23
|
+
<span class="edit-file-trigger-text">Edit</span>
|
|
24
|
+
</button>
|
|
25
|
+
|
|
26
|
+
<!-- Popover Overlay -->
|
|
27
|
+
<template lwc:if={isPopoverOpen}>
|
|
28
|
+
<div
|
|
29
|
+
class={overlayClass}
|
|
30
|
+
onclick={handleOverlayClick}
|
|
31
|
+
>
|
|
32
|
+
<div class={popoverClass}>
|
|
33
|
+
<!-- Popover Header -->
|
|
34
|
+
<div class="edit-file-header">
|
|
35
|
+
<h2 class="edit-file-title">{title}</h2>
|
|
36
|
+
<template lwc:if={fileName}>
|
|
37
|
+
<p class="edit-file-filename">
|
|
38
|
+
<svg
|
|
39
|
+
class="edit-file-file-icon"
|
|
40
|
+
viewBox="0 0 24 24"
|
|
41
|
+
fill="none"
|
|
42
|
+
stroke="currentColor"
|
|
43
|
+
>
|
|
44
|
+
<path
|
|
45
|
+
stroke-linecap="round"
|
|
46
|
+
stroke-linejoin="round"
|
|
47
|
+
stroke-width="2"
|
|
48
|
+
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
49
|
+
/>
|
|
50
|
+
</svg>
|
|
51
|
+
{fileName}
|
|
52
|
+
</p>
|
|
53
|
+
</template>
|
|
54
|
+
<button
|
|
55
|
+
class="edit-file-close"
|
|
56
|
+
onclick={handleCancel}
|
|
57
|
+
aria-label="Close editor"
|
|
58
|
+
>
|
|
59
|
+
<svg
|
|
60
|
+
class="edit-file-close-icon"
|
|
61
|
+
viewBox="0 0 24 24"
|
|
62
|
+
fill="none"
|
|
63
|
+
stroke="currentColor"
|
|
64
|
+
>
|
|
65
|
+
<path
|
|
66
|
+
stroke-linecap="round"
|
|
67
|
+
stroke-linejoin="round"
|
|
68
|
+
stroke-width="2"
|
|
69
|
+
d="M6 18L18 6M6 6l12 12"
|
|
70
|
+
/>
|
|
71
|
+
</svg>
|
|
72
|
+
</button>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<!-- Popover Content -->
|
|
76
|
+
<div class="edit-file-content">
|
|
77
|
+
<!-- Loading State -->
|
|
78
|
+
<template lwc:if={isLoading}>
|
|
79
|
+
<div class="edit-file-loading">
|
|
80
|
+
<div class="edit-file-spinner"></div>
|
|
81
|
+
<p class="edit-file-loading-text">Loading file content...</p>
|
|
82
|
+
</div>
|
|
83
|
+
</template>
|
|
84
|
+
|
|
85
|
+
<!-- Error Message -->
|
|
86
|
+
<template lwc:if={errorMessage}>
|
|
87
|
+
<div class="edit-file-error">
|
|
88
|
+
<svg
|
|
89
|
+
class="edit-file-error-icon"
|
|
90
|
+
viewBox="0 0 24 24"
|
|
91
|
+
fill="none"
|
|
92
|
+
stroke="currentColor"
|
|
93
|
+
>
|
|
94
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
95
|
+
<line x1="15" y1="9" x2="9" y2="15"></line>
|
|
96
|
+
<line x1="9" y1="9" x2="15" y2="15"></line>
|
|
97
|
+
</svg>
|
|
98
|
+
<span class="edit-file-error-text">{errorMessage}</span>
|
|
99
|
+
</div>
|
|
100
|
+
</template>
|
|
101
|
+
|
|
102
|
+
<!-- Content Editor -->
|
|
103
|
+
<template lwc:if={fileContent}>
|
|
104
|
+
<div class="edit-file-editor">
|
|
105
|
+
<label class="edit-file-label" for="file-content">
|
|
106
|
+
File Content
|
|
107
|
+
</label>
|
|
108
|
+
<textarea
|
|
109
|
+
id="file-content"
|
|
110
|
+
class="edit-file-textarea"
|
|
111
|
+
value={fileContent}
|
|
112
|
+
oninput={handleContentChange}
|
|
113
|
+
placeholder="Enter file content..."
|
|
114
|
+
rows="20"
|
|
115
|
+
spellcheck="false"
|
|
116
|
+
></textarea>
|
|
117
|
+
</div>
|
|
118
|
+
</template>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<!-- Popover Footer -->
|
|
122
|
+
<div class="edit-file-footer">
|
|
123
|
+
<div class="edit-file-status">
|
|
124
|
+
<template lwc:if={isSaving}>
|
|
125
|
+
<div class="edit-file-saving">
|
|
126
|
+
<div class="edit-file-mini-spinner"></div>
|
|
127
|
+
<span>Saving...</span>
|
|
128
|
+
</div>
|
|
129
|
+
</template>
|
|
130
|
+
<template lwc:elseif={isContentUnchanged}>
|
|
131
|
+
<span class="edit-file-unchanged">No changes</span>
|
|
132
|
+
</template>
|
|
133
|
+
<template lwc:else>
|
|
134
|
+
<span class="edit-file-modified">Modified</span>
|
|
135
|
+
</template>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<div class="edit-file-actions">
|
|
139
|
+
<button
|
|
140
|
+
class="edit-file-button edit-file-button_secondary"
|
|
141
|
+
onclick={handleCancel}
|
|
142
|
+
disabled={isSaving}
|
|
143
|
+
>
|
|
144
|
+
Cancel
|
|
145
|
+
</button>
|
|
146
|
+
<button
|
|
147
|
+
class={saveButtonClass}
|
|
148
|
+
onclick={handleSave}
|
|
149
|
+
disabled={isSaving}
|
|
150
|
+
>
|
|
151
|
+
<template lwc:if={isSaving}>
|
|
152
|
+
<div class="edit-file-button-spinner"></div>
|
|
153
|
+
Saving...
|
|
154
|
+
</template>
|
|
155
|
+
<template lwc:else>
|
|
156
|
+
Save
|
|
157
|
+
</template>
|
|
158
|
+
</button>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</template>
|
|
164
|
+
</template>
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { LightningElement, api, track } from "lwc";
|
|
2
|
+
import cx from "classnames";
|
|
3
|
+
import { normalizeBoolean } from "dxUtils/normalizers";
|
|
4
|
+
|
|
5
|
+
interface ApiResponse {
|
|
6
|
+
success: boolean;
|
|
7
|
+
data?: string;
|
|
8
|
+
message?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default class EditFile extends LightningElement {
|
|
13
|
+
@api fileName: string = "";
|
|
14
|
+
@api apiEndpoint: string = "/api/edit-file";
|
|
15
|
+
@api title: string = "Edit File";
|
|
16
|
+
|
|
17
|
+
@api
|
|
18
|
+
get disabled() {
|
|
19
|
+
return this._disabled;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
set disabled(value) {
|
|
23
|
+
this._disabled = normalizeBoolean(value);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@track isPopoverOpen: boolean = false;
|
|
27
|
+
@track isLoading: boolean = false;
|
|
28
|
+
@track isSaving: boolean = false;
|
|
29
|
+
@track fileContent: string = "";
|
|
30
|
+
@track originalContent: string = "";
|
|
31
|
+
@track errorMessage: string = "";
|
|
32
|
+
|
|
33
|
+
private _disabled: boolean = false;
|
|
34
|
+
|
|
35
|
+
// API Configuration
|
|
36
|
+
private static readonly FETCH_ENDPOINT = "/api/file/fetch";
|
|
37
|
+
private static readonly SAVE_ENDPOINT = "/api/file/save";
|
|
38
|
+
|
|
39
|
+
get popoverClass() {
|
|
40
|
+
return cx(
|
|
41
|
+
"edit-file-popover",
|
|
42
|
+
this.isPopoverOpen && "edit-file-popover_open"
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
get overlayClass() {
|
|
47
|
+
return cx(
|
|
48
|
+
"edit-file-overlay",
|
|
49
|
+
this.isPopoverOpen && "edit-file-overlay_open"
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get saveButtonClass() {
|
|
54
|
+
return cx(
|
|
55
|
+
"edit-file-button",
|
|
56
|
+
"edit-file-button_primary",
|
|
57
|
+
(this.isSaving || this.isContentUnchanged) && "edit-file-button_disabled"
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get isContentUnchanged() {
|
|
62
|
+
return this.fileContent === this.originalContent;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Open the edit file popover
|
|
66
|
+
async handleEditClick() {
|
|
67
|
+
if (this.disabled) return;
|
|
68
|
+
|
|
69
|
+
this.isPopoverOpen = true;
|
|
70
|
+
this.errorMessage = "";
|
|
71
|
+
await this.fetchFileContent();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Fetch file content from API
|
|
75
|
+
private async fetchFileContent() {
|
|
76
|
+
if (!this.fileName) {
|
|
77
|
+
this.errorMessage = "No file specified to edit";
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.isLoading = true;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const response = await fetch(EditFile.FETCH_ENDPOINT, {
|
|
85
|
+
method: "POST",
|
|
86
|
+
headers: {
|
|
87
|
+
"Content-Type": "application/json",
|
|
88
|
+
},
|
|
89
|
+
body: JSON.stringify({
|
|
90
|
+
fileName: this.fileName,
|
|
91
|
+
action: "fetch"
|
|
92
|
+
})
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
throw new Error(`Failed to fetch file: ${response.status}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const data: ApiResponse = await response.json();
|
|
100
|
+
|
|
101
|
+
if (data.success && data.data !== undefined) {
|
|
102
|
+
this.fileContent = data.data;
|
|
103
|
+
this.originalContent = data.data;
|
|
104
|
+
this.errorMessage = "";
|
|
105
|
+
} else {
|
|
106
|
+
this.errorMessage = data.error || data.message || "Failed to load file content";
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error("Error fetching file content:", error);
|
|
110
|
+
this.errorMessage = error instanceof Error ? error.message : "Failed to connect to server";
|
|
111
|
+
} finally {
|
|
112
|
+
this.isLoading = false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Handle content change in textarea
|
|
117
|
+
handleContentChange(event: Event) {
|
|
118
|
+
const target = event.target as HTMLTextAreaElement;
|
|
119
|
+
this.fileContent = target.value;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Handle cancel action
|
|
123
|
+
handleCancel() {
|
|
124
|
+
this.isPopoverOpen = false;
|
|
125
|
+
this.fileContent = "";
|
|
126
|
+
this.originalContent = "";
|
|
127
|
+
this.errorMessage = "";
|
|
128
|
+
this.isLoading = false;
|
|
129
|
+
this.isSaving = false;
|
|
130
|
+
|
|
131
|
+
// Dispatch cancel event
|
|
132
|
+
this.dispatchEvent(
|
|
133
|
+
new CustomEvent("editcancel", {
|
|
134
|
+
detail: { fileName: this.fileName }
|
|
135
|
+
})
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Handle save action
|
|
140
|
+
async handleSave() {
|
|
141
|
+
if (this.isSaving || this.isContentUnchanged || this.disabled) return;
|
|
142
|
+
|
|
143
|
+
this.isSaving = true;
|
|
144
|
+
this.errorMessage = "";
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const response = await fetch(EditFile.SAVE_ENDPOINT, {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: {
|
|
150
|
+
"Content-Type": "application/json",
|
|
151
|
+
},
|
|
152
|
+
body: JSON.stringify({
|
|
153
|
+
fileName: this.fileName,
|
|
154
|
+
content: this.fileContent,
|
|
155
|
+
action: "save"
|
|
156
|
+
})
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
if (!response.ok) {
|
|
160
|
+
throw new Error(`Failed to save file: ${response.status}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const data: ApiResponse = await response.json();
|
|
164
|
+
|
|
165
|
+
if (data.success) {
|
|
166
|
+
this.originalContent = this.fileContent;
|
|
167
|
+
this.isPopoverOpen = false;
|
|
168
|
+
|
|
169
|
+
// Dispatch success event
|
|
170
|
+
this.dispatchEvent(
|
|
171
|
+
new CustomEvent("editsuccess", {
|
|
172
|
+
detail: {
|
|
173
|
+
fileName: this.fileName,
|
|
174
|
+
content: this.fileContent,
|
|
175
|
+
message: data.message || "File saved successfully"
|
|
176
|
+
}
|
|
177
|
+
})
|
|
178
|
+
);
|
|
179
|
+
} else {
|
|
180
|
+
this.errorMessage = data.error || data.message || "Failed to save file";
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
console.error("Error saving file:", error);
|
|
184
|
+
this.errorMessage = error instanceof Error ? error.message : "Failed to connect to server";
|
|
185
|
+
} finally {
|
|
186
|
+
this.isSaving = false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Handle overlay click to close popover
|
|
191
|
+
handleOverlayClick(event: Event) {
|
|
192
|
+
if (event.target === event.currentTarget) {
|
|
193
|
+
this.handleCancel();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Handle escape key to close popover
|
|
198
|
+
handleKeyDown(event: KeyboardEvent) {
|
|
199
|
+
if (event.key === "Escape" && this.isPopoverOpen) {
|
|
200
|
+
this.handleCancel();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
connectedCallback() {
|
|
205
|
+
// Add escape key listener
|
|
206
|
+
document.addEventListener("keydown", this.handleKeyDown.bind(this));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
disconnectedCallback() {
|
|
210
|
+
// Remove escape key listener
|
|
211
|
+
document.removeEventListener("keydown", this.handleKeyDown.bind(this));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
@import "dxHelpers/commonHeader";
|
|
2
|
+
|
|
3
|
+
dx-logo {
|
|
4
|
+
min-width: fit-content;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.dev-center-link {
|
|
8
|
+
display: inline-block;
|
|
9
|
+
padding: var(--dx-g-spacing-smd) var(--dx-g-spacing-lg) 0
|
|
10
|
+
var(--dx-g-global-header-padding-horizontal);
|
|
11
|
+
height: var(--dx-g-spacing-xl);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.dev-center-content {
|
|
15
|
+
display: flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
text-decoration: none;
|
|
18
|
+
color: inherit;
|
|
19
|
+
font-weight: var(--dx-g-font-bold);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
dx-icon {
|
|
23
|
+
--dx-c-icon-size: var(--dx-g-icon-size-xs);
|
|
24
|
+
--dx-c-icon-color: var(--dx-g-blue-vibrant-20);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.dev-center-content > * + * {
|
|
28
|
+
margin-left: 8px; /* Adjust this value to your desired spacing */
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.header_l2 {
|
|
32
|
+
justify-content: space-between;
|
|
33
|
+
height: var(--dx-g-doc-header-main-nav-height);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.has-brand .header_l2 {
|
|
37
|
+
height: var(--dx-g-spacing-3xl);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.nav_menu-button {
|
|
41
|
+
--dx-c-button-primary-color: var(--dx-g-blue-vibrant-20);
|
|
42
|
+
--dx-c-button-secondary-color-hover: var(
|
|
43
|
+
--dx-g-brand-default-button-color-background-inactive
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.has-brand.has-scoped-nav-items {
|
|
48
|
+
border-bottom: 1px solid var(--dx-g-gray-90);
|
|
49
|
+
border-top: 1px solid var(--dx-g-gray-90);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.nav_menu-ctas {
|
|
53
|
+
margin-right: var(--dx-g-spacing-sm);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
header:not(.has-brand) > .header_l1 {
|
|
57
|
+
background: white;
|
|
58
|
+
border-bottom: 1px solid var(--dx-g-gray-90);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
header:not(.has-brand) > .header_l2 {
|
|
62
|
+
border-bottom: 1px solid var(--dx-g-gray-90);
|
|
63
|
+
border-top: 1px solid var(--dx-g-gray-90);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.header_l2_group.header_l2_group-right-ctas {
|
|
67
|
+
align-items: baseline;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.has-brand .header_l2_group-title {
|
|
71
|
+
padding: var(--dx-g-spacing-sm) var(--dx-g-spacing-xl)
|
|
72
|
+
calc(var(--dx-g-spacing-smd) - 1px) 0;
|
|
73
|
+
min-width: 320px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.no-header-content {
|
|
77
|
+
border-top: 1px solid var(--dx-g-gray-90);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@media (max-width: 768px) {
|
|
81
|
+
.header_l2 {
|
|
82
|
+
padding: 0;
|
|
83
|
+
flex-wrap: wrap;
|
|
84
|
+
height: 100%;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.has-nav-items .header_l2 {
|
|
88
|
+
height: initial;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.header_l2_group {
|
|
92
|
+
width: 100%;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.header_l2_group-title {
|
|
96
|
+
margin-right: 0;
|
|
97
|
+
padding: var(--dx-g-spacing-sm) 0 2px
|
|
98
|
+
var(--dx-g-global-header-padding-horizontal);
|
|
99
|
+
min-height: var(--dx-g-doc-header-main-nav-height);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.header_l2_group-nav {
|
|
103
|
+
height: var(--dx-g-spacing-3xl);
|
|
104
|
+
padding: 0;
|
|
105
|
+
margin-top: calc(var(--dx-g-spacing-2xs) + 1px);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.header_l2_group-nav_overflow {
|
|
109
|
+
height: var(--dx-g-spacing-3xl);
|
|
110
|
+
margin-right: var(--dx-g-spacing-sm);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.subtitle {
|
|
114
|
+
font-weight: var(--dx-g-font-demi);
|
|
115
|
+
letter-spacing: -0.08px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.has-brand .header_l2_group-title {
|
|
119
|
+
margin-right: 0;
|
|
120
|
+
padding: var(--dx-g-spacing-sm) 0 var(--dx-g-spacing-2xs)
|
|
121
|
+
var(--dx-g-global-header-padding-horizontal);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.has-scoped-nav-items > .header_l2 {
|
|
125
|
+
height: unset;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
header:not(.has-brand) > .header_l2 {
|
|
129
|
+
padding-bottom: 0;
|
|
130
|
+
border: 0;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template lwc:if={shouldRender}>
|
|
3
|
+
<dx-brand-theme-provider brand={brand}>
|
|
4
|
+
<header class={className}>
|
|
5
|
+
<dx-banner
|
|
6
|
+
lwc:if={showBanner}
|
|
7
|
+
banner-markup={bannerMarkup}
|
|
8
|
+
></dx-banner>
|
|
9
|
+
<!-- To-Do: Move the devCenter as a new component and use it here, as devCenter is also used in Sidebar now -->
|
|
10
|
+
<div lwc:if={devCenter} class="dev-center-link">
|
|
11
|
+
<a href={devCenter.link} class="dev-center-content">
|
|
12
|
+
<dx-icon symbol="back"></dx-icon>
|
|
13
|
+
<dx-icon
|
|
14
|
+
class="brand-icon"
|
|
15
|
+
lwc:if={devCenter.icon}
|
|
16
|
+
sprite="salesforcebrand"
|
|
17
|
+
symbol={brand}
|
|
18
|
+
size="large"
|
|
19
|
+
></dx-icon>
|
|
20
|
+
<span class="subtitle dx-text-body-4">
|
|
21
|
+
{devCenter.title}
|
|
22
|
+
</span>
|
|
23
|
+
</a>
|
|
24
|
+
</div>
|
|
25
|
+
<div class="header_l2">
|
|
26
|
+
<div class="header_l2_group header_l2_group-title">
|
|
27
|
+
<a href={headerHref} class="home-link">
|
|
28
|
+
<span class="subtitle dx-text-display-7">
|
|
29
|
+
{subtitle}
|
|
30
|
+
</span>
|
|
31
|
+
</a>
|
|
32
|
+
</div>
|
|
33
|
+
<div
|
|
34
|
+
lwc:if={showScopedNavItems}
|
|
35
|
+
class="header_l2_group header_l2_group-nav"
|
|
36
|
+
>
|
|
37
|
+
<div
|
|
38
|
+
class="header_l2_group-nav_overflow"
|
|
39
|
+
onscroll={onNavScroll}
|
|
40
|
+
>
|
|
41
|
+
<dx-header-nav
|
|
42
|
+
aria-label="Scoped Navigation Bar"
|
|
43
|
+
nav-items={scopedNavItems}
|
|
44
|
+
pathname={pathname}
|
|
45
|
+
></dx-header-nav>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</header>
|
|
50
|
+
</dx-brand-theme-provider>
|
|
51
|
+
</template>
|
|
52
|
+
<template lwc:if={shouldHideHeader}>
|
|
53
|
+
<div class="no-header-content"></div>
|
|
54
|
+
</template>
|
|
55
|
+
</template>
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { api } from "lwc";
|
|
2
|
+
import cx from "classnames";
|
|
3
|
+
import type { OptionWithNested, DevCenterConfig } from "typings/custom";
|
|
4
|
+
import { HeaderBase } from "dxBaseElements/headerBase";
|
|
5
|
+
import { toJson, normalizeBoolean } from "dxUtils/normalizers";
|
|
6
|
+
|
|
7
|
+
const TABLET_MATCH = "980px";
|
|
8
|
+
const MOBILE_MATCH = "768px";
|
|
9
|
+
|
|
10
|
+
const isStorybook = () => {
|
|
11
|
+
const { host } = window.location;
|
|
12
|
+
return (
|
|
13
|
+
host === "localhost:6006" || /^dsc-comp.*\.herokuapp\.com$/.test(host)
|
|
14
|
+
);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default class Header extends HeaderBase {
|
|
18
|
+
@api langValuePath: string = "id"; // allows to override how language property is interpreted, follows valuePath dropdown api.
|
|
19
|
+
@api headerHref: string = "/";
|
|
20
|
+
|
|
21
|
+
@api
|
|
22
|
+
get hideDocHeader() {
|
|
23
|
+
return this._hideDocHeader;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
set hideDocHeader(value) {
|
|
27
|
+
this._hideDocHeader = normalizeBoolean(value);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@api
|
|
31
|
+
get scopedNavItems() {
|
|
32
|
+
return this._scopedNavItems;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
set scopedNavItems(value) {
|
|
36
|
+
this._scopedNavItems = toJson(value);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@api
|
|
40
|
+
get devCenter(): DevCenterConfig {
|
|
41
|
+
return this._devCenter;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
set devCenter(value) {
|
|
45
|
+
this._devCenter = toJson(value);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private _scopedNavItems!: OptionWithNested[];
|
|
49
|
+
private tablet = false;
|
|
50
|
+
private tabletMatchMedia!: MediaQueryList;
|
|
51
|
+
private shouldRender: boolean = false;
|
|
52
|
+
private showDocDivider: boolean = false;
|
|
53
|
+
private _devCenter!: DevCenterConfig;
|
|
54
|
+
private _hideDocHeader: boolean = false;
|
|
55
|
+
|
|
56
|
+
protected mobileBreakpoint(): string {
|
|
57
|
+
return MOBILE_MATCH;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private get showScopedNavItems(): boolean {
|
|
61
|
+
return (
|
|
62
|
+
this.scopedNavItems &&
|
|
63
|
+
this.scopedNavItems.length > 0 &&
|
|
64
|
+
!this.hideDocHeader
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* This function returns true if the hideDocHeader is true and the view is not mobile.
|
|
70
|
+
* Also we need to show the header border in case the doc is hidden or if the brand information doesn't exists.
|
|
71
|
+
*/
|
|
72
|
+
private get shouldHideHeader(): boolean {
|
|
73
|
+
return (this.hideDocHeader && !this.mobile) || this.showDocDivider;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
connectedCallback(): void {
|
|
77
|
+
super.connectedCallback();
|
|
78
|
+
this.tabletMatchMedia = window.matchMedia(
|
|
79
|
+
`(max-width: ${TABLET_MATCH})`
|
|
80
|
+
);
|
|
81
|
+
this.onTabletChange(this.tabletMatchMedia);
|
|
82
|
+
this.tabletMatchMedia.addEventListener("change", this.onTabletChange);
|
|
83
|
+
|
|
84
|
+
if (
|
|
85
|
+
(!this.shouldHideHeader &&
|
|
86
|
+
window.location.pathname.includes("/docs/") &&
|
|
87
|
+
window.location.pathname !== "/docs/apis") ||
|
|
88
|
+
window.location.pathname ===
|
|
89
|
+
"/tableau/embedding-playground/overview" ||
|
|
90
|
+
isStorybook()
|
|
91
|
+
) {
|
|
92
|
+
this.shouldRender = true;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (this.shouldRender && window.location.pathname.includes("/docs/")) {
|
|
96
|
+
if (!this.brand && !this.mobile) {
|
|
97
|
+
this.shouldRender = false;
|
|
98
|
+
this.showDocDivider = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
disconnectedCallback(): void {
|
|
104
|
+
super.disconnectedCallback();
|
|
105
|
+
this.tabletMatchMedia.removeEventListener(
|
|
106
|
+
"change",
|
|
107
|
+
this.onTabletChange
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private onTabletChange = (e: MediaQueryListEvent | MediaQueryList) =>
|
|
112
|
+
(this.tablet = e.matches);
|
|
113
|
+
|
|
114
|
+
protected additionalClasses(): string {
|
|
115
|
+
return cx(
|
|
116
|
+
this.brand && "has-brand",
|
|
117
|
+
this.showScopedNavItems && "has-scoped-nav-items"
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|