@salesforcedevs/dx-components 0.28.4 → 0.31.0-alpha.0

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.
Files changed (32) hide show
  1. package/lwc.config.json +8 -1
  2. package/package.json +4 -4
  3. package/src/assets/svg/no-results-combined.svg +80 -0
  4. package/src/modules/base-elements/matchMediaElement/matchMediaElement.ts +59 -0
  5. package/src/modules/dx/button/button.css +8 -0
  6. package/src/modules/dx/button/button.ts +3 -0
  7. package/src/modules/dx/cardExpanded/cardExpanded.css +2 -1
  8. package/src/modules/dx/cardExpanded/cardExpanded.html +1 -2
  9. package/src/modules/dx/cardExpanded/cardExpanded.ts +0 -1
  10. package/src/modules/dx/cardNews/cardNews.html +1 -0
  11. package/src/modules/dx/cardNews/cardNews.ts +2 -0
  12. package/src/modules/dx/checkbox/checkbox.ts +1 -0
  13. package/src/modules/dx/checkboxNative/checkboxNative.css +21 -0
  14. package/src/modules/dx/checkboxNative/checkboxNative.html +14 -0
  15. package/src/modules/dx/checkboxNative/checkboxNative.ts +38 -0
  16. package/src/modules/dx/contentArchive/contentArchive.css +246 -0
  17. package/src/modules/dx/contentArchive/contentArchive.html +336 -0
  18. package/src/modules/dx/contentArchive/contentArchive.ts +444 -0
  19. package/src/modules/dx/featuredContentHeader/featuredContentHeader.html +33 -31
  20. package/src/modules/dx/featuredContentHeader/featuredContentHeader.ts +1 -0
  21. package/src/modules/dx/filterMenu/filterMenu.css +106 -0
  22. package/src/modules/dx/filterMenu/filterMenu.html +107 -0
  23. package/src/modules/dx/filterMenu/filterMenu.ts +104 -0
  24. package/src/modules/dx/headerNav/headerNav.ts +1 -1
  25. package/src/modules/dx/modalDrawer/modalDrawer.css +1 -0
  26. package/src/modules/dx/spinner/spinner.css +0 -7
  27. package/src/modules/dx/topics/topics.css +1 -0
  28. package/src/modules/utils/constants/breakpoints.ts +9 -0
  29. package/src/modules/utils/constants/constants.ts +1 -0
  30. package/src/modules/utils/contentArchive/altData.js +3 -0
  31. package/src/modules/utils/contentArchive/contentArchive.ts +163 -0
  32. package/LICENSE +0 -12
@@ -0,0 +1,444 @@
1
+ import { LightningElement, track } from "lwc";
2
+ import { getArchivePosts, getArchiveFilterList } from "utils/contentArchive";
3
+ import cx from "classnames";
4
+
5
+ const MOBILE_MATCH = "767px";
6
+ const MONTHS = {
7
+ January: 0,
8
+ February: 1,
9
+ March: 2,
10
+ April: 3,
11
+ May: 4,
12
+ June: 5,
13
+ July: 6,
14
+ August: 7,
15
+ September: 8,
16
+ October: 9,
17
+ November: 10,
18
+ December: 11
19
+ };
20
+
21
+ export default class ContentArchive extends LightningElement {
22
+ private totalResults: number | null = null;
23
+ private currentPage: number = 1;
24
+ private totalPages: number = 1;
25
+ private loading: boolean = true;
26
+ private filtersLoading: boolean = true;
27
+ private modalOpen: boolean = false;
28
+ private cardType: string = "expanded";
29
+
30
+ private postsByDate: {} = {};
31
+ private dateList: any[] = [];
32
+ private authorList: any[] = [];
33
+ private categoryList: any[] = [];
34
+
35
+ private isMobile: boolean = false;
36
+ private mobileMatchMedia!: MediaQueryList;
37
+
38
+ @track selectedFilterMenuData: any = {
39
+ site: [],
40
+ dates: [],
41
+ authors: [],
42
+ categories: []
43
+ };
44
+ @track posts: any[] = [];
45
+
46
+ get currentPageLength() {
47
+ if (this.loading) {
48
+ return "Loading Results...";
49
+ }
50
+
51
+ let currentFirstResultNumber = 0;
52
+ const currentLastResultNumber = this.currentPage * this.posts.length;
53
+
54
+ if (this.currentPage > 1) {
55
+ currentFirstResultNumber =
56
+ currentLastResultNumber - this.posts.length;
57
+ } else if (this.currentPage === 1 && !this.isEmptyState) {
58
+ currentFirstResultNumber = 1;
59
+ }
60
+
61
+ return `Showing ${currentFirstResultNumber} - ${currentLastResultNumber} of ${this.totalResults}`;
62
+ }
63
+
64
+ get showPagination() {
65
+ return this.totalPages > 1;
66
+ }
67
+
68
+ get resultsHeading() {
69
+ let pageInfo = "";
70
+
71
+ if (this.currentPage !== 1) {
72
+ pageInfo = `- Page ${this.currentPage}`;
73
+ }
74
+ return this.totalResults ? `Archive Results ${pageInfo}` : "No results";
75
+ }
76
+
77
+ get isEmptyState() {
78
+ return this.totalResults === 0 && !this.loading;
79
+ }
80
+
81
+ get spinnerSize() {
82
+ return this.isMobile ? "medium" : "large";
83
+ }
84
+
85
+ get isMinimalCard() {
86
+ return this.cardType === "minimal";
87
+ }
88
+
89
+ get minimalCardClass() {
90
+ return cx("min-card-btn", this.cardType === "minimal" && "btn-active");
91
+ }
92
+
93
+ get expandedCardClass() {
94
+ return cx("exp-card-btn", this.cardType === "expanded" && "btn-active");
95
+ }
96
+
97
+ get postsByMonth() {
98
+ const posts = this.splitPostsByMonth(this.posts);
99
+
100
+ return Object.keys(posts).map((key) => [key, [...posts[key]]]);
101
+ }
102
+
103
+ get contentTypeList() {
104
+ return [
105
+ { id: 1, name: "Blogs" },
106
+ { id: 2, name: "Podcasts" }
107
+ ];
108
+ }
109
+
110
+ get hideBlogFilters() {
111
+ return (
112
+ this.selectedFilterMenuData.site.length === 1 &&
113
+ this.selectedFilterMenuData.site[0].id === 2
114
+ );
115
+ }
116
+
117
+ get contentTypeTitle() {
118
+ return `Content Type ${
119
+ this.selectedFilterMenuData.site.length > 0
120
+ ? `(${this.selectedFilterMenuData.site.length})`
121
+ : ""
122
+ }`;
123
+ }
124
+ get dateTitle() {
125
+ return `Date ${
126
+ this.selectedFilterMenuData.dates.length > 0
127
+ ? `(${this.selectedFilterMenuData.dates.length})`
128
+ : ""
129
+ }`;
130
+ }
131
+ get authorTitle() {
132
+ return `Blog Author ${
133
+ this.selectedFilterMenuData.authors.length > 0
134
+ ? `(${this.selectedFilterMenuData.authors.length})`
135
+ : ""
136
+ }`;
137
+ }
138
+ get categoryTitle() {
139
+ return `Topic ${
140
+ this.selectedFilterMenuData.categories.length > 0
141
+ ? `(${this.selectedFilterMenuData.categories.length})`
142
+ : ""
143
+ }`;
144
+ }
145
+
146
+ private filterSelectedDates(
147
+ currentDateList: Array<{ name: string }>,
148
+ selectedDate: string,
149
+ toRemove: boolean = false
150
+ ) {
151
+ return currentDateList.filter((currentDateItem: { name: string }) => {
152
+ const year = currentDateItem.name.slice(-4);
153
+
154
+ if (toRemove) {
155
+ return selectedDate.slice(-4) !== currentDateItem.name;
156
+ }
157
+
158
+ return year !== selectedDate;
159
+ });
160
+ }
161
+
162
+ private updateSelectedFilters(e: CustomEvent) {
163
+ const { value, name, checked } = e.detail;
164
+
165
+ let copy = {
166
+ ...this.selectedFilterMenuData
167
+ };
168
+
169
+ if (!checked) {
170
+ copy[name] = copy[name].filter(
171
+ (selectedFilterItem: { name: string }) => {
172
+ return selectedFilterItem.name !== value.name;
173
+ }
174
+ );
175
+ } else if (!copy[name].includes(value)) {
176
+ const regex = /^(\d{4})$/g;
177
+
178
+ if (regex.test(value.name) && copy[name].length > 0) {
179
+ copy[name] = this.filterSelectedDates(copy[name], value.name);
180
+ } else if (regex.test(value.name.slice(-4))) {
181
+ copy[name] = this.filterSelectedDates(
182
+ copy[name],
183
+ value.name,
184
+ true
185
+ );
186
+ }
187
+
188
+ copy[name].push(value);
189
+ }
190
+
191
+ // Remove filters when only podcast is selected
192
+ if (
193
+ name === "site" &&
194
+ copy[name].length === 1 &&
195
+ !copy[name].some((item: { id: number }) => item.id === 1)
196
+ ) {
197
+ copy = {
198
+ site: [...copy.site],
199
+ categories: [...copy.categories],
200
+ dates: [...copy.dates],
201
+ authors: []
202
+ };
203
+ }
204
+
205
+ this.selectedFilterMenuData = copy;
206
+ this.currentPage = 1;
207
+ this.updateCheckedStatus();
208
+ this.fetchPostsByCardType();
209
+ }
210
+
211
+ private updateCheckedStatus() {
212
+ const filterMenuElements =
213
+ this.template.querySelectorAll("dx-filter-menu");
214
+
215
+ filterMenuElements.forEach((filterMenuElement: any) =>
216
+ filterMenuElement.updateCheckedStatus(this.selectedFilterMenuData)
217
+ );
218
+ }
219
+
220
+ private removeFilterItem(e: any) {
221
+ const { name, value } = e.target;
222
+
223
+ this.updateSelectedFilters(
224
+ new CustomEvent("updateselectedfilters", {
225
+ detail: {
226
+ name,
227
+ value,
228
+ checked: false
229
+ }
230
+ })
231
+ );
232
+ }
233
+
234
+ private toggleModal() {
235
+ this.modalOpen = !this.modalOpen;
236
+ }
237
+
238
+ // split posts by month for the minimal view date header
239
+ private splitPostsByMonth(posts: any[]) {
240
+ const postsByMonth: { [key: string]: Array<{}> } = {};
241
+
242
+ posts.forEach((post) => {
243
+ const date = new Date(post.date);
244
+ const month = new Intl.DateTimeFormat("en-US", {
245
+ month: "long"
246
+ }).format(date);
247
+ const year = date.getFullYear();
248
+
249
+ if (!postsByMonth[`${month} ${year}`]) {
250
+ postsByMonth[`${month} ${year}`] = [];
251
+ }
252
+
253
+ postsByMonth[`${month} ${year}`].push({ ...post });
254
+ });
255
+
256
+ return this.sortSplittedPostsByMonth(postsByMonth);
257
+ }
258
+
259
+ private sortSplittedPostsByMonth(postsByMonth: any) {
260
+ const sortedPostsByMonth: { [key: string]: Array<{}> } = {};
261
+
262
+ Object.keys(postsByMonth)
263
+ .sort((a, b) => {
264
+ const [aMonth, aYear] = a.split(" ");
265
+ const [bMonth, bYear] = b.split(" ");
266
+
267
+ if (aYear < bYear) {
268
+ return 1;
269
+ } else if (aYear > bYear) {
270
+ return -1;
271
+ } else if (MONTHS[aMonth] < MONTHS[bMonth]) {
272
+ return 1;
273
+ } else if (MONTHS[aMonth] > MONTHS[bMonth]) {
274
+ return -1;
275
+ }
276
+ return 0;
277
+ })
278
+ .forEach((key) => {
279
+ sortedPostsByMonth[key] = [
280
+ ...postsByMonth[key].sort((a, b) => {
281
+ const aDate = new Date(a.date);
282
+ const bDate = new Date(b.date);
283
+
284
+ if (aDate < bDate) {
285
+ return 1;
286
+ } else if (aDate > bDate) {
287
+ return -1;
288
+ }
289
+ return 0;
290
+ })
291
+ ];
292
+ });
293
+
294
+ return sortedPostsByMonth;
295
+ }
296
+
297
+ private handleCardTypeChange(e: any) {
298
+ const selectedCardType = e.target.value;
299
+ if (this.cardType !== selectedCardType) {
300
+ this.cardType = selectedCardType;
301
+
302
+ this.fetchPostsByCardType();
303
+ }
304
+ }
305
+
306
+ private handleFilterClear() {
307
+ this.selectedFilterMenuData = {
308
+ site: [],
309
+ dates: [],
310
+ authors: [],
311
+ categories: []
312
+ };
313
+
314
+ this.updateCheckedStatus();
315
+ }
316
+
317
+ private goToPage(e: CustomEvent) {
318
+ const page = e.detail;
319
+ this.currentPage = page;
320
+ this.totalResults = 0;
321
+ this.totalPages = 0;
322
+
323
+ this.fetchPostsByCardType();
324
+ }
325
+
326
+ private fetchPostsByCardType() {
327
+ if (this.cardType === "minimal") {
328
+ this.fetchPosts(20);
329
+ } else {
330
+ this.fetchPosts(10);
331
+ }
332
+ }
333
+
334
+ private async fetchArchiveFilterList() {
335
+ this.filtersLoading = true;
336
+
337
+ if (this.dateList.length === 0) {
338
+ const filterListdata = await getArchiveFilterList();
339
+ this.dateList = this.splitOptionsByDate(filterListdata.dates);
340
+
341
+ this.authorList = filterListdata.authors;
342
+ this.categoryList = filterListdata.categories;
343
+ }
344
+
345
+ this.filtersLoading = false;
346
+ }
347
+
348
+ private async fetchPosts(numberOfPosts: number) {
349
+ this.loading = true;
350
+
351
+ const postData = await getArchivePosts(
352
+ numberOfPosts,
353
+ this.currentPage,
354
+ this.cardType,
355
+ this.selectedFilterMenuData
356
+ );
357
+
358
+ // get the list of filters for the filter menu
359
+ this.fetchArchiveFilterList();
360
+
361
+ this.posts = postData.posts;
362
+ this.totalResults = postData.totalResults;
363
+ this.totalPages = postData.totalPages;
364
+ this.loading = false;
365
+ }
366
+
367
+ private paddedMonth(date: Date) {
368
+ return `${(date.getMonth() + 1).toString().padStart(2, "0")}`;
369
+ }
370
+
371
+ // split filter options by year/month for nested checkboxes
372
+ private splitOptionsByDate(dateOptions: any) {
373
+ return dateOptions.reduce((acc: any, curr: any) => {
374
+ const date = new Date(curr);
375
+ const month = new Intl.DateTimeFormat("en-US", {
376
+ month: "long"
377
+ }).format(date);
378
+ const year = String(date.getFullYear());
379
+
380
+ const foundYear = acc.find((post: any) => post.name === year);
381
+ const foundMonth = foundYear?.months.find(
382
+ (m: any) => m.id === month
383
+ );
384
+
385
+ if (!foundYear) {
386
+ acc.push({
387
+ id: year,
388
+ name: year,
389
+ isYear: true,
390
+ months: [
391
+ {
392
+ id: month,
393
+ name: `${month} ${year}`,
394
+ shortName: `${year},${date.getMonth() + 1}`,
395
+ isYear: false,
396
+ numberOfPosts: 1
397
+ }
398
+ ],
399
+ totalNumberOfPosts: 1
400
+ });
401
+
402
+ return acc;
403
+ }
404
+
405
+ if (!foundMonth) {
406
+ foundYear.months.push({
407
+ id: month,
408
+ name: `${month} ${year}`,
409
+ shortName: `${year},${date.getMonth() + 1}`,
410
+ isYear: false,
411
+ numberOfPosts: 1
412
+ });
413
+ foundYear.totalNumberOfPosts += 1;
414
+
415
+ return acc;
416
+ }
417
+
418
+ foundMonth.numberOfPosts += 1;
419
+ foundYear.totalNumberOfPosts += 1;
420
+
421
+ return acc;
422
+ }, []);
423
+ }
424
+
425
+ private onMobileChange = (e: MediaQueryListEvent | MediaQueryList) => {
426
+ this.isMobile = e.matches;
427
+
428
+ setTimeout(() => {
429
+ this.updateCheckedStatus();
430
+ }, 0);
431
+ };
432
+
433
+ connectedCallback(): void {
434
+ this.mobileMatchMedia = window.matchMedia(
435
+ `(max-width: ${MOBILE_MATCH})`
436
+ );
437
+ this.onMobileChange(this.mobileMatchMedia);
438
+ this.mobileMatchMedia.addEventListener("change", this.onMobileChange);
439
+
440
+ if (this.posts.length === 0) {
441
+ this.fetchPostsByCardType();
442
+ }
443
+ }
444
+ }
@@ -60,37 +60,39 @@
60
60
  >
61
61
  <img src={imgSrc} alt={imgAlt} />
62
62
  </a>
63
- <svg
64
- class="swoop-silhouette"
65
- width="1920px"
66
- height="331px"
67
- viewBox="0 0 1920 331"
68
- version="1.1"
69
- xmlns="http://www.w3.org/2000/svg"
70
- preserveAspectRatio="none"
71
- >
72
- <defs>
73
- <path
74
- d="M1920,330 L76.7313881,330.008182 C51.2800917,330.255837 25.7029624,330.372286 0,330.357528 L0,330 L76.7313881,330.008182 C789.975765,323.067922 1404.39864,213.086385 1920,0.0635708029 L1920,0.0635708029 L1920,330 Z"
75
- id="path-1"
76
- ></path>
77
- </defs>
78
- <g
79
- id="Documentation-landing-page"
80
- stroke="none"
81
- stroke-width="1"
82
- fill="none"
83
- fill-rule="evenodd"
63
+ <template if:false={noSwoop}>
64
+ <svg
65
+ class="swoop-silhouette"
66
+ width="1920px"
67
+ height="331px"
68
+ viewBox="0 0 1920 331"
69
+ version="1.1"
70
+ xmlns="http://www.w3.org/2000/svg"
71
+ preserveAspectRatio="none"
84
72
  >
85
- <mask id="mask-2" fill="white">
86
- <use xlink:href="#path-1"></use>
87
- </mask>
88
- <use
89
- id="Combined-Shape"
90
- fill="#FFFFFF"
91
- xlink:href="#path-1"
92
- ></use>
93
- </g>
94
- </svg>
73
+ <defs>
74
+ <path
75
+ d="M1920,330 L76.7313881,330.008182 C51.2800917,330.255837 25.7029624,330.372286 0,330.357528 L0,330 L76.7313881,330.008182 C789.975765,323.067922 1404.39864,213.086385 1920,0.0635708029 L1920,0.0635708029 L1920,330 Z"
76
+ id="path-1"
77
+ ></path>
78
+ </defs>
79
+ <g
80
+ id="Documentation-landing-page"
81
+ stroke="none"
82
+ stroke-width="1"
83
+ fill="none"
84
+ fill-rule="evenodd"
85
+ >
86
+ <mask id="mask-2" fill="white">
87
+ <use xlink:href="#path-1"></use>
88
+ </mask>
89
+ <use
90
+ id="Combined-Shape"
91
+ fill="#FFFFFF"
92
+ xlink:href="#path-1"
93
+ ></use>
94
+ </g>
95
+ </svg>
96
+ </template>
95
97
  </div>
96
98
  </template>
@@ -18,6 +18,7 @@ export default class FeaturedContentHeader extends LightningElement {
18
18
  @api target?: string | null = null;
19
19
  @api title!: string;
20
20
  @api backgroundImg?: "trees" | "codey" | "blog" | "moon" | null = null;
21
+ @api noSwoop: boolean = false;
21
22
 
22
23
  private _authors?: Array<ImageAndLabel>;
23
24
  private isSlotEmpty: boolean = true;
@@ -0,0 +1,106 @@
1
+ @import "helpers/reset";
2
+
3
+ :host {
4
+ --dx-c-filter-menu-margin: 0;
5
+ --dx-c-checkbox-font-size: 13px;
6
+ --dx-g-text-body-color: var(--sds-g-gray-11);
7
+ }
8
+
9
+ .container {
10
+ display: flex;
11
+ flex-direction: column;
12
+ width: 100%;
13
+ border: 1px solid rgb(201, 201, 201);
14
+ border-radius: 4px;
15
+ padding: 15px 20px;
16
+ position: relative;
17
+ margin: var(--dx-c-filter-menu-margin);
18
+ max-height: 650px;
19
+ overflow-y: auto;
20
+ }
21
+
22
+ .caret-down {
23
+ -webkit-transform: rotate(180deg); /* Safari */
24
+ transform: rotate(180deg);
25
+ }
26
+
27
+ .nested {
28
+ display: none;
29
+ }
30
+
31
+ .active {
32
+ display: block;
33
+ }
34
+
35
+ .checkbox-month {
36
+ margin-left: 6px;
37
+ }
38
+
39
+ .filter-menu-title {
40
+ font-size: 14px;
41
+ font-family: var(--dx-g-font-sans);
42
+ font-weight: bold;
43
+ color: rgb(24, 24, 24);
44
+ }
45
+
46
+ .first-layer {
47
+ padding-top: 12px;
48
+ }
49
+
50
+ dx-checkbox:not(:first-of-type) {
51
+ margin-top: 6px;
52
+ }
53
+
54
+ li,
55
+ label {
56
+ position: relative;
57
+ }
58
+
59
+ .first-layer li:not(:first-of-type) {
60
+ margin-top: 9px;
61
+ }
62
+
63
+ .second-layer li:first-of-type {
64
+ margin-top: 7px;
65
+ }
66
+
67
+ .second-layer li:not(:first-of-type) {
68
+ margin-top: 5px;
69
+ }
70
+
71
+ svg {
72
+ position: absolute;
73
+ right: 0;
74
+ top: 0;
75
+ background: transparent;
76
+ stroke: var(--dx-g-blue-vibrant-50);
77
+ }
78
+
79
+ svg,
80
+ .filter-menu-title {
81
+ cursor: pointer;
82
+ -webkit-user-select: none; /* Safari 3.1+ */
83
+ -moz-user-select: none; /* Firefox 2+ */
84
+ -ms-user-select: none; /* IE 10+ */
85
+ user-select: none;
86
+ }
87
+
88
+ .year-svg {
89
+ right: 50px;
90
+ }
91
+
92
+ .checkbox-month,
93
+ .checkbox-year {
94
+ margin-right: 8px;
95
+ vertical-align: middle;
96
+ }
97
+
98
+ .checkbox-year {
99
+ --dx-c-filter-menu-font-weight: bold;
100
+ }
101
+
102
+ .view-more-btn {
103
+ color: var(--dx-g-blue-vibrant-50);
104
+ font-size: 14px;
105
+ font-family: var(--dx-g-font-sans);
106
+ }