@salesforcedevs/dx-components 1.28.3 → 1.28.5-node22-1
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 +7 -1
- package/package.json +46 -47
- package/src/modules/dx/codeBlock/codeBlock.ts +6 -6
- package/src/modules/dx/emptyState/emptyState.html +1 -1
- package/src/modules/dx/emptyState/emptyState.ts +2 -2
- package/src/modules/dx/error/error.css +3 -3
- package/src/modules/dx/errorFallback/errorFallback.html +1 -1
- package/src/modules/dx/feature/feature.css +2 -2
- package/src/modules/dx/feature/feature.html +1 -1
- package/src/modules/dx/featuredContentHeader/featuredContentHeader.css +5 -5
- package/src/modules/dx/featuredContentHeader/svgs.ts +2 -2
- package/src/modules/dx/footer/footer.css +176 -4
- package/src/modules/dx/footer/footer.html +91 -3
- package/src/modules/dx/footer/footer.ts +279 -1
- package/src/modules/dx/footerMfe/footerMfe.ts +2 -0
- package/src/modules/dx/globalHeader/globalHeader.html +5 -0
- package/src/modules/dx/globalHeader/globalHeader.ts +84 -0
- package/src/modules/dx/input/input.ts +1 -1
- package/src/modules/dx/logo/logo.ts +1 -1
- package/src/modules/dx/searchResults/resultsTemplate.js +1 -1
- package/src/modules/dx/searchResults/searchResults.css +4 -4
- package/src/modules/dx/sectionBanner/sectionBanner.html +1 -1
- package/src/modules/dx/sidebar/sidebar.html +1 -1
- package/src/modules/dx/socials/socials.css +36 -0
- package/src/modules/dx/socials/socials.html +22 -0
- package/src/modules/dx/socials/socials.ts +76 -0
- package/src/modules/dx/treeItem/treeItem.html +1 -1
- package/src/modules/dxUtils/shiki/__mocks__/shiki.ts +2 -1
- package/src/modules/dxUtils/shiki/__mocks__/shikijs.ts +6 -0
- package/src/modules/dxUtils/shiki/shiki.ts +26 -176
- package/src/modules/dxUtils/shikiCore/shikiCore.ts +188 -0
- package/src/modules/dxUtils/shikiGrammars/shikiGrammars.ts +151 -41
- package/src/modules/dxUtils/shikiStatic/shikiStatic.html +3 -0
- package/src/modules/dxUtils/shikiStatic/shikiStatic.ts +46 -0
- package/LICENSE +0 -12
|
@@ -4,15 +4,21 @@ import { FooterVariant, LightningSlotElement } from "typings/custom";
|
|
|
4
4
|
import { track } from "dxUtils/analytics";
|
|
5
5
|
import { isSlotEmpty } from "dxUtils/slot";
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
class Footer extends LightningElement {
|
|
8
8
|
private _variant: FooterVariant = "small-signup";
|
|
9
9
|
private isSlotEmpty = true;
|
|
10
10
|
private signupUrl =
|
|
11
11
|
"https://www.salesforce.com/form/other/role-based-newsletter/?Developer=true";
|
|
12
12
|
|
|
13
|
+
@api
|
|
14
|
+
mfeHomeHref: string = `/${window.location.host}`; // ugly hack: ideally this wouldn't be necessary, but the only way to remove the "See all ways to contact us" link from the footer MFE is to set this to a non-empty value other than "us"; and given the way that the footer works, the non-empty value needs to be something that can be appended to `/` and work correctly
|
|
15
|
+
|
|
13
16
|
@api
|
|
14
17
|
mfeConfigOrigin: string = `${window.location.origin}/developer/en-us/wp-json`;
|
|
15
18
|
|
|
19
|
+
@api
|
|
20
|
+
useMfe: boolean = false; // TODO: Probably just remove this once the footer MFE supports rendering in non-full-width contexts
|
|
21
|
+
|
|
16
22
|
@api
|
|
17
23
|
set variant(value: FooterVariant) {
|
|
18
24
|
if (!this.isFooterVariant(value)) {
|
|
@@ -93,3 +99,275 @@ export default class Footer extends LightningElement {
|
|
|
93
99
|
});
|
|
94
100
|
}
|
|
95
101
|
}
|
|
102
|
+
|
|
103
|
+
// This encapsulates the old non-MFE footer functionality that should be removed once the footer MFE supports rendering in non-full-width contexts.
|
|
104
|
+
// It's encapsulated like this solely for ease of deletion later.
|
|
105
|
+
function augmentWithNonMFEFooterFunctionality(FooterClass: typeof Footer) {
|
|
106
|
+
const baseSocialIconUrl = `/assets/icons/brand-sprite/svg/symbols.svg`;
|
|
107
|
+
|
|
108
|
+
return class NonMFEFooter extends FooterClass {
|
|
109
|
+
private didRequestFooterConfig = false;
|
|
110
|
+
private footerConfig: any = []; // NOTE: See mockProps.ts for the expected format of the footer config
|
|
111
|
+
private configItemTitleToItemLookup: Map<string, any> = new Map();
|
|
112
|
+
private configItemParentToChildrenLookup: Map<number, any[]> =
|
|
113
|
+
new Map();
|
|
114
|
+
private generalLinks: any = [];
|
|
115
|
+
private socialLinks: any = [];
|
|
116
|
+
private termsLinks: any = [];
|
|
117
|
+
|
|
118
|
+
async renderedCallback() {
|
|
119
|
+
super.renderedCallback?.();
|
|
120
|
+
|
|
121
|
+
if (this.didRequestFooterConfig || this.useMfe) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
this.didRequestFooterConfig = true;
|
|
126
|
+
this.footerConfig = await this.requestFooterConfig();
|
|
127
|
+
this.buildFooterConfigLookupTables(this.footerConfig);
|
|
128
|
+
this.setSocialLinks();
|
|
129
|
+
this.setGeneralLinks();
|
|
130
|
+
this.setLegalFooter();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
get showContainerMiddle() {
|
|
134
|
+
return !this.showLegalOnly;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private async requestFooterConfig() {
|
|
138
|
+
let config: any = [];
|
|
139
|
+
try {
|
|
140
|
+
const response = await fetch(
|
|
141
|
+
`${this.mfeConfigOrigin}/c360/experience/v1/navigation`
|
|
142
|
+
);
|
|
143
|
+
const data = await response.json();
|
|
144
|
+
config = data["footer-navigation"] || [];
|
|
145
|
+
} catch (error) {
|
|
146
|
+
console.error("Error requesting footer config:", error);
|
|
147
|
+
}
|
|
148
|
+
return config;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private buildFooterConfigLookupTables(config: any[]) {
|
|
152
|
+
config.forEach((item: any) => {
|
|
153
|
+
// attr_title is preferable to title because it is not language-specific
|
|
154
|
+
this.configItemTitleToItemLookup.set(item.attr_title || item.title, item);
|
|
155
|
+
if (item.menu_item_parent) {
|
|
156
|
+
const parentId = parseInt(item.menu_item_parent, 10);
|
|
157
|
+
const children =
|
|
158
|
+
this.configItemParentToChildrenLookup.get(parentId) ||
|
|
159
|
+
[];
|
|
160
|
+
children.push(item);
|
|
161
|
+
this.configItemParentToChildrenLookup.set(
|
|
162
|
+
parentId,
|
|
163
|
+
children
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private setSocialLinks() {
|
|
170
|
+
const socialLinksParentItem =
|
|
171
|
+
this.configItemTitleToItemLookup.get("social-icons");
|
|
172
|
+
|
|
173
|
+
if (!socialLinksParentItem || !socialLinksParentItem.ID) {
|
|
174
|
+
console.error(
|
|
175
|
+
"Social links parent item not found in footer config"
|
|
176
|
+
);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const socialLinksItems = this.configItemParentToChildrenLookup.get(
|
|
181
|
+
socialLinksParentItem.ID
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
if (!socialLinksItems) {
|
|
185
|
+
console.error(
|
|
186
|
+
"Social links children items not found in footer config"
|
|
187
|
+
);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
this.socialLinks =
|
|
192
|
+
socialLinksItems.map((child: any) => {
|
|
193
|
+
const childTitle: string = child.attr_title || child.title || ""; // attr_title is preferable to title because it is not language-specific
|
|
194
|
+
const iconSymbol =
|
|
195
|
+
childTitle === "LinkedIn"
|
|
196
|
+
? "linked-in"
|
|
197
|
+
: childTitle.toLocaleLowerCase();
|
|
198
|
+
const iconUrlHash =
|
|
199
|
+
iconSymbol === "twitter"
|
|
200
|
+
? "#twitter-x"
|
|
201
|
+
: `#themed-${iconSymbol}`;
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
href: child.url,
|
|
205
|
+
iconSprite: "brand",
|
|
206
|
+
iconURL: `${baseSocialIconUrl}${iconUrlHash}`,
|
|
207
|
+
label: child.title,
|
|
208
|
+
iconSymbol
|
|
209
|
+
};
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
private setGeneralLinks() {
|
|
214
|
+
const topFooterItem =
|
|
215
|
+
this.configItemTitleToItemLookup.get("top-footer");
|
|
216
|
+
const mediaItem = this.configItemTitleToItemLookup.get("media");
|
|
217
|
+
|
|
218
|
+
if (!topFooterItem || !topFooterItem.ID) {
|
|
219
|
+
console.error("Top footer item not found in footer config");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const generalLinksHeadingsItems =
|
|
224
|
+
this.configItemParentToChildrenLookup
|
|
225
|
+
.get(topFooterItem.ID)
|
|
226
|
+
?.filter((item: any) => item.ID !== mediaItem?.ID);
|
|
227
|
+
|
|
228
|
+
if (!generalLinksHeadingsItems) {
|
|
229
|
+
console.error(
|
|
230
|
+
"General links children items not found in footer config"
|
|
231
|
+
);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const generalLinks: any[] = [];
|
|
236
|
+
generalLinksHeadingsItems.forEach((item: any) => {
|
|
237
|
+
const childrenItems = this.configItemParentToChildrenLookup.get(item.ID);
|
|
238
|
+
|
|
239
|
+
if (!childrenItems) {
|
|
240
|
+
console.error(`General links children items not found for item with title "${item.title}"`);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
generalLinks.push({
|
|
245
|
+
id: item.title,
|
|
246
|
+
label: item.title,
|
|
247
|
+
options: childrenItems.map((option: any) => {
|
|
248
|
+
return {
|
|
249
|
+
id: option.title,
|
|
250
|
+
label: option.title,
|
|
251
|
+
link: {
|
|
252
|
+
href: option.url,
|
|
253
|
+
target: option.target
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
})
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
this.generalLinks = generalLinks;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private setLegalFooter() {
|
|
263
|
+
const copyrightNoticeItem = this.configItemTitleToItemLookup.get("All rights reserved");
|
|
264
|
+
|
|
265
|
+
if (!copyrightNoticeItem) {
|
|
266
|
+
console.error("All rights reserved item not found in footer config");
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const { url, description } = copyrightNoticeItem;
|
|
271
|
+
const copyrightNoticeInnerHtml = description.replace("{{All rights reserved}}", `<a href="${url}">All rights reserved</a>`);
|
|
272
|
+
const copyrightNoticeEl = this.template.querySelector(".copyright-notice")!;
|
|
273
|
+
copyrightNoticeEl.innerHTML = copyrightNoticeInnerHtml;
|
|
274
|
+
const copyrightLink = copyrightNoticeEl.querySelector("a");
|
|
275
|
+
if (copyrightLink) {
|
|
276
|
+
copyrightLink.addEventListener("click", (e) =>
|
|
277
|
+
this.handleCopyrightLinkClick(e as Event)
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const legalLinksItems = this.configItemParentToChildrenLookup.get(copyrightNoticeItem.ID);
|
|
282
|
+
|
|
283
|
+
if (!legalLinksItems) {
|
|
284
|
+
console.error("Legal links children items not found in footer config");
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
this.termsLinks = legalLinksItems.map((item: any) => {
|
|
289
|
+
const link: any = {
|
|
290
|
+
label: item.title,
|
|
291
|
+
href: item.url,
|
|
292
|
+
target: item.target,
|
|
293
|
+
rel: item.rel
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const itemTitle: string = item.attr_title || item.title || ""; // attr_title is preferable to title because it is not language-specific
|
|
297
|
+
if (itemTitle === "Cookie Preferences") {
|
|
298
|
+
link.href = "#";
|
|
299
|
+
link.onclick = this.handleCookiePreferencesClick;
|
|
300
|
+
} else if (itemTitle === "Your Privacy Choices") {
|
|
301
|
+
link.img = "https://developer.salesforce.com/ns-assets/privacyoptions.svg";
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return link;
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private openOneTrustInfoDisplay() {
|
|
309
|
+
if ((window as any).OneTrust && (window as any).OneTrust.ToggleInfoDisplay) {
|
|
310
|
+
(window as any).OneTrust.ToggleInfoDisplay();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private trackFooterLinkClick(
|
|
315
|
+
e: Event,
|
|
316
|
+
options?: {
|
|
317
|
+
label?: string;
|
|
318
|
+
elementTitle?: string;
|
|
319
|
+
clickUrl?: string;
|
|
320
|
+
}
|
|
321
|
+
) {
|
|
322
|
+
const anchor = e.currentTarget as HTMLAnchorElement;
|
|
323
|
+
const clickText =
|
|
324
|
+
options?.label ??
|
|
325
|
+
anchor.textContent?.trim() ??
|
|
326
|
+
anchor.getAttribute("aria-label") ??
|
|
327
|
+
anchor.href;
|
|
328
|
+
const elementTitle = options?.elementTitle ?? clickText;
|
|
329
|
+
const clickUrl = options?.clickUrl ?? anchor.href;
|
|
330
|
+
|
|
331
|
+
track(anchor, "custEv_linkClick", {
|
|
332
|
+
click_text: clickText,
|
|
333
|
+
click_url: clickUrl,
|
|
334
|
+
element_type: "link",
|
|
335
|
+
element_title: elementTitle,
|
|
336
|
+
content_category: "footer",
|
|
337
|
+
nav_item: clickText,
|
|
338
|
+
nav_level: "1",
|
|
339
|
+
nav_type: "footer"
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private handleLogoClick(e: Event) {
|
|
344
|
+
this.trackFooterLinkClick(e, {
|
|
345
|
+
label: "Salesforce",
|
|
346
|
+
elementTitle: "Salesforce logo",
|
|
347
|
+
clickUrl: `${window.location.origin}/`
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
private handleSocialLinkClick(e: Event) {
|
|
352
|
+
this.trackFooterLinkClick(e);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
private handleCookiePreferencesClick(e: Event) {
|
|
356
|
+
this.trackFooterLinkClick(e, {
|
|
357
|
+
label: "Cookie Preferences",
|
|
358
|
+
clickUrl: (e.currentTarget as HTMLAnchorElement).href || "#"
|
|
359
|
+
});
|
|
360
|
+
this.openOneTrustInfoDisplay();
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private handleTermsClick(e: Event) {
|
|
364
|
+
this.trackFooterLinkClick(e);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private handleCopyrightLinkClick(e: Event) {
|
|
368
|
+
this.trackFooterLinkClick(e, { label: "All rights reserved" });
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export default augmentWithNonMFEFooterFunctionality(Footer);
|
|
@@ -4,6 +4,7 @@ export default class FooterMfe extends LightningElement {
|
|
|
4
4
|
private footerElement: HTMLElement | null = null;
|
|
5
5
|
|
|
6
6
|
@api legalOnly = false;
|
|
7
|
+
@api homeHref: string = `/${window.location.host}`; // ugly hack by default: ideally this wouldn't be necessary, but the only way to remove the "See all ways to contact us" link from the footer MFE is to set this to a non-empty value other than "us"; and given the way that the footer works, the non-empty value needs to be something that can be appended to `/` and work correctly
|
|
7
8
|
@api origin: string = `${window.location.origin}/developer/en-us/wp-json`;
|
|
8
9
|
|
|
9
10
|
renderedCallback() {
|
|
@@ -14,6 +15,7 @@ export default class FooterMfe extends LightningElement {
|
|
|
14
15
|
this.footerElement = document.createElement("hgf-footer");
|
|
15
16
|
this.footerElement.setAttribute("origin", this.origin);
|
|
16
17
|
this.footerElement.setAttribute("hide-language-selector", "true");
|
|
18
|
+
this.footerElement.setAttribute("home-href", this.homeHref);
|
|
17
19
|
if (this.legalOnly) {
|
|
18
20
|
this.footerElement.setAttribute("legal-only", "true");
|
|
19
21
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import kebabCase from "lodash.kebabcase";
|
|
2
|
+
import { LightningElement, api } from "lwc";
|
|
3
|
+
|
|
4
|
+
const defaultDomain = "https://developer.salesforce.com";
|
|
5
|
+
const defaultLocale = "en-us";
|
|
6
|
+
const defaultHeaderSettingsBasePath = "/c/public/header-settings";
|
|
7
|
+
const headerSettingsJsonKeys = [
|
|
8
|
+
"regionSelectorOverride",
|
|
9
|
+
"contactLinksOverride"
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
export default class DevExNavigation extends LightningElement {
|
|
13
|
+
static renderMode = "light";
|
|
14
|
+
|
|
15
|
+
@api locale: string = defaultLocale;
|
|
16
|
+
@api path: string = defaultHeaderSettingsBasePath;
|
|
17
|
+
@api domain: string = defaultDomain;
|
|
18
|
+
|
|
19
|
+
async connectedCallback(): Promise<void> {
|
|
20
|
+
try {
|
|
21
|
+
const headerSettingsResponse = await fetch(
|
|
22
|
+
`${this.domain}${this.path}/${this.locale}.json`,
|
|
23
|
+
{
|
|
24
|
+
headers: {
|
|
25
|
+
"Content-Type": "application/json"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
);
|
|
29
|
+
if (headerSettingsResponse.ok) {
|
|
30
|
+
this.createFullNav(await headerSettingsResponse.json());
|
|
31
|
+
} else {
|
|
32
|
+
this.createBarebonesNav();
|
|
33
|
+
}
|
|
34
|
+
} catch (ex) {
|
|
35
|
+
console.error(`Navigation error: ${ex}`);
|
|
36
|
+
this.createBarebonesNav();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private createGlobalNav(globalNavSettings: any): HTMLElement {
|
|
41
|
+
const hgfNav = document.createElement("hgf-c360nav");
|
|
42
|
+
|
|
43
|
+
Object.entries(globalNavSettings).forEach(([key, value]) => {
|
|
44
|
+
if (headerSettingsJsonKeys.includes(key)) {
|
|
45
|
+
value = JSON.stringify(value);
|
|
46
|
+
}
|
|
47
|
+
hgfNav.setAttribute(kebabCase(key), value as string);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return hgfNav;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private createContextNav(contextNavData: any): HTMLElement {
|
|
54
|
+
const hgfNavContext = document.createElement("hgf-c360contextnav");
|
|
55
|
+
hgfNavContext.setAttribute("data", JSON.stringify(contextNavData));
|
|
56
|
+
return hgfNavContext;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private createFullNav(headerData: any): void {
|
|
60
|
+
const hgfNav = this.createGlobalNav(headerData.headerSettings);
|
|
61
|
+
const hgfNavContext = this.createContextNav(headerData.navItems);
|
|
62
|
+
const containerEl = this.refs.globalNavContainer as Element;
|
|
63
|
+
containerEl.appendChild(hgfNav);
|
|
64
|
+
containerEl.appendChild(hgfNavContext);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private createBarebonesNav(): void {
|
|
68
|
+
const hgfNav = this.createGlobalNav({
|
|
69
|
+
origin: "",
|
|
70
|
+
contextNavEnabled: "true"
|
|
71
|
+
});
|
|
72
|
+
const hgfNavContext = this.createContextNav({
|
|
73
|
+
variation: "static",
|
|
74
|
+
propertyTitle: {
|
|
75
|
+
label: "Developers",
|
|
76
|
+
url: "/"
|
|
77
|
+
},
|
|
78
|
+
menuGroup: {}
|
|
79
|
+
});
|
|
80
|
+
const containerEl = this.refs.globalNavContainer as Element;
|
|
81
|
+
containerEl.appendChild(hgfNav);
|
|
82
|
+
containerEl.appendChild(hgfNavContext);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -195,7 +195,7 @@ export default class Input extends LightningElement {
|
|
|
195
195
|
}
|
|
196
196
|
|
|
197
197
|
private get shortcutImgSrc() {
|
|
198
|
-
return `https://
|
|
198
|
+
return `https://developer.salesforce.com/ns-assets/${this.commandKey}.svg`;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
private get shortcutImgAlt() {
|
|
@@ -3,7 +3,7 @@ import { LightningElement, api } from "lwc";
|
|
|
3
3
|
export default class Logo extends LightningElement {
|
|
4
4
|
@api href: string = "/";
|
|
5
5
|
@api imgSrc: string =
|
|
6
|
-
"https://
|
|
6
|
+
"https://developer.salesforce.com/ns-assets/salesforce-cloud.svg";
|
|
7
7
|
@api imgAlt: string = "Salesforce logo";
|
|
8
8
|
@api label!: string;
|
|
9
9
|
}
|
|
@@ -9,7 +9,7 @@ export default html`
|
|
|
9
9
|
<div class="coveo-show-if-no-results">
|
|
10
10
|
<div class="no-results">
|
|
11
11
|
<img
|
|
12
|
-
src="https://
|
|
12
|
+
src="https://developer.salesforce.com/ns-assets/binary-cloud-circle-small.svg"
|
|
13
13
|
alt="purple cloud with floating binary numbers above"
|
|
14
14
|
/>
|
|
15
15
|
<div class="no-results-info">
|
|
@@ -311,7 +311,7 @@ li.coveo-dynamic-facet-breadcrumb-value-list-item {
|
|
|
311
311
|
|
|
312
312
|
.dx-search-header {
|
|
313
313
|
padding: var(--dx-g-spacing-xl);
|
|
314
|
-
background: url("https://
|
|
314
|
+
background: url("https://developer.salesforce.com/ns-assets/binary-cloud-trees.svg")
|
|
315
315
|
no-repeat 100% 0,
|
|
316
316
|
linear-gradient(
|
|
317
317
|
77deg,
|
|
@@ -352,7 +352,7 @@ li.coveo-dynamic-facet-breadcrumb-value-list-item {
|
|
|
352
352
|
|
|
353
353
|
@media (min-width: 928px) and (max-width: 1024px) {
|
|
354
354
|
.dx-search-header {
|
|
355
|
-
background: url("https://
|
|
355
|
+
background: url("https://developer.salesforce.com/ns-assets/binary-cloud-trees.svg")
|
|
356
356
|
no-repeat 200% 0,
|
|
357
357
|
linear-gradient(
|
|
358
358
|
77deg,
|
|
@@ -380,7 +380,7 @@ li.coveo-dynamic-facet-breadcrumb-value-list-item {
|
|
|
380
380
|
|
|
381
381
|
@media (min-width: 768px) and (max-width: 928px) {
|
|
382
382
|
.dx-search-header {
|
|
383
|
-
background: url("https://
|
|
383
|
+
background: url("https://developer.salesforce.com/ns-assets/binary-trees.svg")
|
|
384
384
|
no-repeat 90% 50%,
|
|
385
385
|
linear-gradient(
|
|
386
386
|
77deg,
|
|
@@ -392,7 +392,7 @@ li.coveo-dynamic-facet-breadcrumb-value-list-item {
|
|
|
392
392
|
|
|
393
393
|
@media (max-width: 768px) {
|
|
394
394
|
.dx-search-header {
|
|
395
|
-
background: url("https://
|
|
395
|
+
background: url("https://developer.salesforce.com/ns-assets/binary.svg")
|
|
396
396
|
no-repeat 90% 50%,
|
|
397
397
|
linear-gradient(
|
|
398
398
|
77deg,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<img
|
|
4
4
|
if:false={hideTopGraphic}
|
|
5
5
|
class="graphic"
|
|
6
|
-
src="https://
|
|
6
|
+
src="https://developer.salesforce.com/ns-assets/dx-section-banner-graphic-1.svg"
|
|
7
7
|
alt=""
|
|
8
8
|
/>
|
|
9
9
|
<div class="content">
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
<img
|
|
70
70
|
lwc:if={isSearchLoading}
|
|
71
71
|
class="loading-skeleton padding-horizontal"
|
|
72
|
-
src="https://
|
|
72
|
+
src="https://developer.salesforce.com/ns-assets/sidebar-loading.svg"
|
|
73
73
|
alt="loading"
|
|
74
74
|
/>
|
|
75
75
|
<template
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.details-section {
|
|
6
|
+
color: var(--dx-g-text-label-color);
|
|
7
|
+
font-family: var(--dx-g-font-display);
|
|
8
|
+
font-size: var(--dx-g-text-xs);
|
|
9
|
+
letter-spacing: 0.7px;
|
|
10
|
+
margin-bottom: var(--dx-g-spacing-sm);
|
|
11
|
+
text-transform: uppercase;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.details-section span {
|
|
15
|
+
display: block;
|
|
16
|
+
line-height: var(--dx-g-spacing-md);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.socials-group {
|
|
20
|
+
display: flex;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.socials-group a:not(:last-child) {
|
|
24
|
+
margin-right: var(--dx-g-spacing-md);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
dx-icon-badge {
|
|
28
|
+
--dx-c-icon-badge-background-color: var(--dx-g-social-svg-background-color);
|
|
29
|
+
--dx-c-icon-badge-color: var(--dx-g-social-svg-icon-color);
|
|
30
|
+
--dx-c-icon-badge-size: 34px;
|
|
31
|
+
--dx-c-icon-badge-icon-size: 18px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
dx-icon-badge:hover {
|
|
35
|
+
--dx-c-icon-badge-background-color: var(--dx-g-social-svg-hover-color);
|
|
36
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="details-section" if:true={hasDetails}>
|
|
3
|
+
<template for:each={details} for:item="detail">
|
|
4
|
+
<span key={detail}>{detail}</span>
|
|
5
|
+
</template>
|
|
6
|
+
</div>
|
|
7
|
+
<div class="socials-group">
|
|
8
|
+
<template for:each={socials} for:item="social">
|
|
9
|
+
<a
|
|
10
|
+
href={social.href}
|
|
11
|
+
key={social.name}
|
|
12
|
+
target={linksTarget}
|
|
13
|
+
title={social.name}
|
|
14
|
+
>
|
|
15
|
+
<dx-icon-badge
|
|
16
|
+
sprite={spriteName}
|
|
17
|
+
symbol={social.symbol}
|
|
18
|
+
></dx-icon-badge>
|
|
19
|
+
</a>
|
|
20
|
+
</template>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { LightningElement, api } from "lwc";
|
|
2
|
+
import { normalizeToArray } from "dxUtils/normalizers";
|
|
3
|
+
|
|
4
|
+
type Icon = { name: string; symbol: string; baseShareUrl: string };
|
|
5
|
+
|
|
6
|
+
const URL_TOKEN = "{url}";
|
|
7
|
+
const TEXT_TOKEN = "{text}";
|
|
8
|
+
|
|
9
|
+
const SOCIAL_ICONS: Array<Icon> = [
|
|
10
|
+
{
|
|
11
|
+
name: "Twitter",
|
|
12
|
+
symbol: "twitter",
|
|
13
|
+
baseShareUrl: `https://twitter.com/intent/tweet?url=${URL_TOKEN}&text=${TEXT_TOKEN}`
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: "Linkedin",
|
|
17
|
+
symbol: "linkedin-in",
|
|
18
|
+
baseShareUrl: `http://www.linkedin.com/sharing/share-offsite/?url=${URL_TOKEN}`
|
|
19
|
+
}
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const SPRITE_NAME = "brand";
|
|
23
|
+
|
|
24
|
+
const VALID_TARGETS = ["_self", "_blank", "_parent", "_top", "framename"];
|
|
25
|
+
|
|
26
|
+
export default class Socials extends LightningElement {
|
|
27
|
+
private _linksTarget: string = "_blank";
|
|
28
|
+
private _details: Array<string> = [];
|
|
29
|
+
|
|
30
|
+
@api
|
|
31
|
+
get details() {
|
|
32
|
+
return this._details;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
set details(value) {
|
|
36
|
+
this._details = normalizeToArray(value, "|") as Array<string>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@api
|
|
40
|
+
get linksTarget(): string {
|
|
41
|
+
return this._linksTarget;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
set linksTarget(value: string) {
|
|
45
|
+
if (value && !VALID_TARGETS.includes(value)) {
|
|
46
|
+
console.error(
|
|
47
|
+
`Invalid links target, valid options are: ${VALID_TARGETS.join(
|
|
48
|
+
", "
|
|
49
|
+
)}`
|
|
50
|
+
);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
this._linksTarget = value;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
get socials() {
|
|
57
|
+
const url = encodeURIComponent(document.location.href);
|
|
58
|
+
const text = encodeURIComponent(document.title);
|
|
59
|
+
return SOCIAL_ICONS.map(({ baseShareUrl, ...value }: Icon) => {
|
|
60
|
+
return {
|
|
61
|
+
...value,
|
|
62
|
+
href: baseShareUrl
|
|
63
|
+
.replace(URL_TOKEN, url)
|
|
64
|
+
.replace(TEXT_TOKEN, text)
|
|
65
|
+
};
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get spriteName() {
|
|
70
|
+
return SPRITE_NAME;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get hasDetails() {
|
|
74
|
+
return this.details && this.details.length > 0;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
<img
|
|
17
17
|
if:true={showChildrenLoading}
|
|
18
18
|
class="tree-children-loading"
|
|
19
|
-
src="https://
|
|
19
|
+
src="https://developer.salesforce.com/ns-assets/sidebar-item-loading.svg"
|
|
20
20
|
alt="childrenLoading"
|
|
21
21
|
/>
|
|
22
22
|
<template if:true={showChildren} tabindex="0">
|
|
@@ -21,3 +21,9 @@ export default mockLanguage;
|
|
|
21
21
|
// Also export as named export for themes
|
|
22
22
|
export const theme = mockTheme;
|
|
23
23
|
export const lang = mockLanguage;
|
|
24
|
+
|
|
25
|
+
// Mock for @shikijs/colorized-brackets
|
|
26
|
+
export const transformerColorizedBrackets = () => ({
|
|
27
|
+
name: "colorized-brackets",
|
|
28
|
+
preprocess: (code: string) => code
|
|
29
|
+
});
|