@salesforcedevs/dx-components 1.2.12 → 1.2.15-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 (46) hide show
  1. package/lwc.config.json +5 -1
  2. package/package.json +2 -3
  3. package/src/assets/icons/salesforcebrand-sprite/svg/symbols.svg +40 -0
  4. package/src/assets/svg/big-moon.svg +1 -0
  5. package/src/assets/svg/blue-circle.svg +3 -0
  6. package/src/assets/svg/trial-left.svg +6 -0
  7. package/src/assets/svg/trial-right.svg +26 -0
  8. package/src/modules/dx/button/button.css +10 -3
  9. package/src/modules/dx/buttonToggle/buttonToggle.css +50 -0
  10. package/src/modules/dx/buttonToggle/buttonToggle.html +17 -0
  11. package/src/modules/dx/buttonToggle/buttonToggle.ts +75 -0
  12. package/src/modules/dx/cardTrial/cardTrial.css +65 -4
  13. package/src/modules/dx/cardTrial/cardTrial.html +95 -12
  14. package/src/modules/dx/cardTrial/cardTrial.ts +69 -2
  15. package/src/modules/dx/cardTrialExpanded/cardTrialExpanded.css +149 -0
  16. package/src/modules/dx/cardTrialExpanded/cardTrialExpanded.html +85 -0
  17. package/src/modules/dx/cardTrialExpanded/cardTrialExpanded.ts +67 -0
  18. package/src/modules/dx/featuredContentHeader/featuredContentHeader.css +60 -2
  19. package/src/modules/dx/featuredContentHeader/featuredContentHeader.html +8 -0
  20. package/src/modules/dx/featuredContentHeader/featuredContentHeader.ts +13 -1
  21. package/src/modules/dx/headerMobileNavMenu/headerMobileNavMenu.css +1 -0
  22. package/src/modules/dx/headerSearch/headerSearch.html +2 -0
  23. package/src/modules/dx/headerSearch/headerSearch.ts +1 -0
  24. package/src/modules/dx/input/input.ts +2 -0
  25. package/src/modules/dx/modal/modal.css +40 -0
  26. package/src/modules/dx/modal/modal.html +10 -0
  27. package/src/modules/dx/modal/modal.ts +50 -0
  28. package/src/modules/dx/modalDrawer/modalDrawer.html +2 -2
  29. package/src/modules/dx/modalDrawer/modalDrawer.ts +7 -1
  30. package/src/modules/dx/popover/popover.css +2 -1
  31. package/src/modules/dx/section/section.css +10 -1
  32. package/src/modules/dx/section/section.ts +1 -1
  33. package/src/modules/dx/sidebarSearch/sidebarSearch.html +1 -0
  34. package/src/modules/dx/sidebarSearch/sidebarSearch.ts +1 -0
  35. package/src/modules/dx/tabPanel/tabPanel.css +20 -0
  36. package/src/modules/dx/tabPanel/tabPanel.html +13 -0
  37. package/src/modules/dx/tabPanel/tabPanel.ts +27 -0
  38. package/src/modules/dx/tabPanelItem/tabPanelItem.css +39 -0
  39. package/src/modules/dx/tabPanelItem/tabPanelItem.html +15 -0
  40. package/src/modules/dx/tabPanelItem/tabPanelItem.ts +39 -0
  41. package/src/modules/dx/tabPanelList/tabPanelList.css +35 -0
  42. package/src/modules/dx/tabPanelList/tabPanelList.html +47 -0
  43. package/src/modules/dx/tabPanelList/tabPanelList.ts +164 -0
  44. package/src/modules/dx/tabPanelList/uniqueId.ts +6 -0
  45. package/yarn-error.log +17934 -0
  46. package/LICENSE +0 -12
@@ -7,12 +7,33 @@ export default class CardTrial extends LightningElement {
7
7
  @api badgeSprite?: string = "salesforcebrand";
8
8
  @api badgeSymbol!: string;
9
9
  @api body!: string;
10
+ @api buttonOneCta?: string;
11
+ @api buttonOneHref?: string;
12
+ @api buttonOneIcon?: string;
13
+ @api buttonOneIconSize?: string = "large";
14
+ @api buttonTwoCta?: string;
15
+ @api buttonTwoHref?: string;
16
+ @api buttonTwoIcon?: string;
17
+ @api buttonTwoIconSize?: string = "large";
18
+ @api hideCardDetails?: boolean = false;
10
19
  @api href!: string;
11
- @api label!: string;
12
20
  @api imgSrc?: string;
21
+ @api label!: string;
22
+ @api recommended?: boolean = false;
13
23
  @api target?: string;
14
24
  @api terms!: string;
15
25
  @api title!: string;
26
+ @api topText?: string;
27
+ @api modalTitle?: string;
28
+
29
+ @api twoColTitle?: string;
30
+ @api twoColTarget?: string;
31
+ @api twoColHref!: string;
32
+ @api typeBadgeValue?: string;
33
+ @api typeBadgeColor?: string = "default";
34
+ @api typeBadgeSize?: string = "default";
35
+ @api typeBadgeType?: string = "content-type";
36
+
16
37
  @api
17
38
  get details() {
18
39
  return this._details;
@@ -21,14 +42,60 @@ export default class CardTrial extends LightningElement {
21
42
  this._details = toJson(value);
22
43
  }
23
44
 
45
+ @api
46
+ get modalDetails() {
47
+ return this._modalDetails;
48
+ }
49
+ set modalDetails(value) {
50
+ this._modalDetails = toJson(value);
51
+ }
52
+
53
+ get hasDetails() {
54
+ return this.details?.length > 0 && !this.hideCardDetails;
55
+ }
56
+
57
+ get hasOneButton() {
58
+ return !!(this.buttonOneCta && !this.buttonTwoCta);
59
+ }
60
+
61
+ get hasTwoButtons() {
62
+ return !!(this.buttonOneCta && this.buttonTwoCta);
63
+ }
64
+
65
+ get hasButtons() {
66
+ return !!(this.buttonOneCta || this.buttonTwoCta);
67
+ }
68
+
69
+ get ariaRole() {
70
+ return this.hasButtons ? "menu" : "link";
71
+ }
72
+
24
73
  private _details!: string[];
74
+ private _modalDetails!: [{ title: string; subtitle: string }];
75
+ private _modalOpen = false;
25
76
 
26
77
  private get className() {
27
78
  return cx(
28
79
  "card-trial",
29
80
  "dx-card-base",
30
81
  "dx-card-base_section-vertical",
31
- this.imgSrc && "has-image"
82
+ this.imgSrc && "has-image",
83
+ this.recommended && "recommended-border"
32
84
  );
33
85
  }
86
+
87
+ private get detailsClassName() {
88
+ return cx("details", this.hasButtons && "details_top-margin");
89
+ }
90
+
91
+ private toggleModal() {
92
+ this._modalOpen = !this._modalOpen;
93
+ }
94
+
95
+ private handleClick() {
96
+ // card is clickable only if it doesn't contain buttons (for accessibility reasons)
97
+ if (!this.hasButtons) {
98
+ window.location.assign(this.href);
99
+ }
100
+ }
34
101
  }
@@ -0,0 +1,149 @@
1
+ @import "dxHelpers/reset";
2
+ @import "dxHelpers/text";
3
+
4
+ :host {
5
+ display: block;
6
+ }
7
+
8
+ .container {
9
+ max-width: 976px;
10
+ margin: 0 var(--dx-g-spacing-lg);
11
+ overflow: auto;
12
+ max-height: 100vh;
13
+ }
14
+
15
+ .content-container {
16
+ display: flex;
17
+ border-radius: var(--dx-g-spacing-md);
18
+ overflow: hidden;
19
+ pointer-events: all;
20
+ }
21
+
22
+ .badge-main {
23
+ --dx-c-icon-badge-icon-size: var(--dx-g-spacing-mlg);
24
+ --dx-c-icon-badge-size: var(--dx-g-spacing-2xl);
25
+
26
+ margin-bottom: var(--dx-g-spacing-md);
27
+ }
28
+
29
+ .title-main {
30
+ margin-bottom: var(--dx-g-spacing-xs);
31
+ }
32
+
33
+ .button-cta {
34
+ --dx-c-button-font-size: var(--dx-g-text-base);
35
+ --dx-c-button-font-weight: var(--dx-g-font-bold);
36
+
37
+ margin-top: 15px;
38
+ width: 100%;
39
+ }
40
+
41
+ .button-container {
42
+ margin-bottom: var(--dx-g-spacing-smd);
43
+ position: sticky;
44
+ top: -1px;
45
+ }
46
+
47
+ .is-sticky {
48
+ background-color: var(--dx-g-gray-95);
49
+ border-bottom: 1px solid var(--dx-g-gray-80);
50
+ margin-bottom: 0;
51
+ padding: 10px 10px 10px 0;
52
+ }
53
+
54
+ .button-close {
55
+ --dx-c-button-background-color: white;
56
+
57
+ margin-left: auto;
58
+ pointer-events: all;
59
+ }
60
+
61
+ .label {
62
+ color: var(--dx-g-gray-30);
63
+ margin-top: 5px;
64
+ margin-bottom: var(--dx-g-spacing-md);
65
+ }
66
+
67
+ .terms {
68
+ margin-top: var(--dx-g-spacing-md);
69
+ font-weight: var(--dx-g-font-bold);
70
+ }
71
+
72
+ .label,
73
+ .terms,
74
+ dx-button {
75
+ display: block;
76
+ }
77
+
78
+ .details {
79
+ display: flex;
80
+ flex-direction: column;
81
+ margin-top: var(--dx-g-spacing-lg);
82
+ }
83
+
84
+ .detail {
85
+ display: flex;
86
+ flex-direction: row;
87
+ }
88
+
89
+ .detail:not(:last-of-type) {
90
+ margin-bottom: var(--dx-g-spacing-smd);
91
+ }
92
+
93
+ .badge-detail {
94
+ --dx-c-icon-badge-icon-size: 10px;
95
+ --dx-c-icon-badge-size: 18px;
96
+ --dx-c-icon-badge-color: var(--dx-g-blue-vibrant-40);
97
+
98
+ padding-right: var(--dx-g-spacing-sm);
99
+ }
100
+
101
+ .col-one {
102
+ background-color: white;
103
+ padding: var(--dx-g-spacing-mlg);
104
+ flex: 0 0 388px;
105
+ }
106
+
107
+ .col-two {
108
+ background-color: var(--dx-g-indigo-vibrant-15);
109
+ padding: var(--dx-g-spacing-2xl);
110
+ flex: 1;
111
+ }
112
+
113
+ .title-secondary {
114
+ --dx-c-card-title-color: white;
115
+ }
116
+
117
+ .col-two-detail-title {
118
+ font-family: var(--dx-g-font-display);
119
+ font-size: var(--dx-g-text-base);
120
+ font-weight: var(--dx-g-font-demi);
121
+ margin-top: var(--dx-g-spacing-mlg);
122
+ }
123
+
124
+ .col-two-detail-subtitle,
125
+ .bullet-list {
126
+ font-family: var(--dx-g-font-sans);
127
+ font-size: var(--dx-g-text-sm);
128
+ font-weight: var(--dx-g-font-normal);
129
+ }
130
+
131
+ .bullet-list {
132
+ list-style: inside;
133
+ }
134
+
135
+ .col-two-detail-title,
136
+ .col-two-detail-subtitle,
137
+ .bullet-list {
138
+ color: white;
139
+ }
140
+
141
+ @media screen and (max-width: 768px) {
142
+ .container {
143
+ max-width: 550px;
144
+ }
145
+
146
+ .content-container {
147
+ flex-direction: column;
148
+ }
149
+ }
@@ -0,0 +1,85 @@
1
+ <template>
2
+ <div class="container">
3
+ <div class="button-container">
4
+ <dx-button
5
+ class="button-close"
6
+ variant="secondary"
7
+ icon-symbol="close"
8
+ onclick={handleModalClose}
9
+ >
10
+ Close
11
+ </dx-button>
12
+ </div>
13
+ <div class="content-container">
14
+ <div class="col-one">
15
+ <dx-icon-badge
16
+ class="badge-main"
17
+ background-color={badgeBackgroundColor}
18
+ symbol={badgeSymbol}
19
+ sprite={badgeSprite}
20
+ ></dx-icon-badge>
21
+ <dx-card-title
22
+ class="title-main"
23
+ href={href}
24
+ target={target}
25
+ title={title}
26
+ ></dx-card-title>
27
+ <span class="label dx-text-body-3">{label}</span>
28
+ <p class="body dx-text-body-2 static-font">{body}</p>
29
+ <span class="terms dx-text-body-3 static-font">{terms}</span>
30
+ <dx-button
31
+ class="button-cta"
32
+ size="large"
33
+ href={href}
34
+ variant="primary"
35
+ font="sans"
36
+ icon-symbol={buttonIcon}
37
+ icon-size={buttonIconSize}
38
+ >
39
+ {buttonCta}
40
+ </dx-button>
41
+ <ul class="details">
42
+ <template for:each={details} for:item="item">
43
+ <li class="detail" key={item}>
44
+ <dx-icon-badge
45
+ class="badge-detail"
46
+ background-color="indigo-vibrant-90"
47
+ symbol="check"
48
+ ></dx-icon-badge>
49
+ <span class="detail-text dx-text-body-4">
50
+ {item}
51
+ </span>
52
+ </li>
53
+ </template>
54
+ </ul>
55
+ </div>
56
+ <div class="col-two">
57
+ <dx-card-title
58
+ class="title-secondary"
59
+ title={colTwoTitle}
60
+ ></dx-card-title>
61
+ <template for:each={colTwoDetails} for:item="colTwoItem">
62
+ <div key={colTwoItem.title}>
63
+ <p class="col-two-detail-title">{colTwoItem.title}</p>
64
+ <p
65
+ if:true={colTwoItem.subtitle}
66
+ class="col-two-detail-subtitle"
67
+ >
68
+ {colTwoItem.subtitle}
69
+ </p>
70
+ <ul if:true={colTwoItem.bullets} class="bullet-list">
71
+ <template
72
+ for:each={colTwoItem.bullets}
73
+ for:item="bullet"
74
+ >
75
+ <li key={colTwoItem.bullet} class="bullet">
76
+ {bullet}
77
+ </li>
78
+ </template>
79
+ </ul>
80
+ </div>
81
+ </template>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ </template>
@@ -0,0 +1,67 @@
1
+ import { LightningElement, api } from "lwc";
2
+ import { toJson } from "dxUtils/normalizers";
3
+
4
+ export default class CardTrial extends LightningElement {
5
+ @api badgeBackgroundColor?: string = "indigo-vibrant-90";
6
+ @api badgeSprite?: string = "salesforcebrand";
7
+ @api badgeSymbol!: string;
8
+ @api body!: string;
9
+ @api buttonCta?: string;
10
+ @api buttonHref?: string;
11
+ @api buttonIcon?: string;
12
+ @api buttonIconSize?: string = "large";
13
+ @api colTwoTitle!: string;
14
+ @api href!: string;
15
+ @api label!: string;
16
+ @api target?: string;
17
+ @api terms!: string;
18
+ @api title!: string;
19
+
20
+ @api
21
+ get details() {
22
+ return this._details;
23
+ }
24
+ set details(value) {
25
+ this._details = toJson(value);
26
+ }
27
+
28
+ @api
29
+ get colTwoDetails() {
30
+ return this._colTwoDetails;
31
+ }
32
+ set colTwoDetails(value) {
33
+ this._colTwoDetails = toJson(value);
34
+ }
35
+
36
+ private _details!: [{ title: string; subtitle: string }];
37
+ private _colTwoDetails!: string[];
38
+ private stickyCloseObserver!: IntersectionObserver;
39
+
40
+ renderedCallback(): void {
41
+ this.observeStickyClose();
42
+ }
43
+
44
+ disconnectedCallback(): void {
45
+ this.stickyCloseObserver.disconnect();
46
+ }
47
+
48
+ private observeStickyClose() {
49
+ const el = this.template.querySelector(".button-container");
50
+ if (el && !this.stickyCloseObserver) {
51
+ this.stickyCloseObserver = new IntersectionObserver(
52
+ ([e]) => {
53
+ e.target.classList.toggle(
54
+ "is-sticky",
55
+ e.intersectionRatio < 1
56
+ );
57
+ },
58
+ { threshold: [0.99] }
59
+ );
60
+ this.stickyCloseObserver.observe(el);
61
+ }
62
+ }
63
+
64
+ private handleModalClose() {
65
+ this.dispatchEvent(new CustomEvent("togglemodal"));
66
+ }
67
+ }
@@ -13,6 +13,7 @@
13
13
  --dx-c-featured-content-header-padding-horizontal: var(
14
14
  --dx-g-page-padding-horizontal
15
15
  );
16
+ --dx-c-featured-content-header-padding-vertical: var(--dx-g-spacing-6xl);
16
17
  --dx-c-featured-content-main-content-max-width: 840px;
17
18
  --dx-c-bottom-image-width: calc(
18
19
  100vw - var(--dx-g-page-padding-horizontal-lg) * 2
@@ -21,6 +22,8 @@
21
22
  --dx-c-moon-graphic-color: var(--dx-g-indigo-vibrant-40);
22
23
  --dx-c-featured-content-header-image-container-transform: scale(0.7)
23
24
  translateY(14px);
25
+ --dx-c-featured-content-header-swoop-display: block;
26
+ --dx-c-featured-content-header-swoop-height: 30%;
24
27
  }
25
28
 
26
29
  .container {
@@ -32,12 +35,12 @@
32
35
  /* actual spacing between bottom of graphic and rest of page content */
33
36
  --bottom-spacing: var(--dx-g-spacing-4xl);
34
37
  --item-spacing: var(--dx-g-spacing-xl);
35
- --swoop-height: 30%;
38
+ --swoop-height: var(--dx-c-featured-content-header-swoop-height);
36
39
  --swoop-inset: calc(2.5 * var(--vertical-padding));
37
40
  --swoop-z-index: 11;
38
41
 
39
42
  position: relative;
40
- padding: var(--vertical-padding)
43
+ padding: var(--dx-c-featured-content-header-padding-vertical)
41
44
  var(--dx-c-featured-content-header-padding-horizontal);
42
45
  background-color: var(--dx-c-featured-content-header-background-color);
43
46
  }
@@ -64,6 +67,7 @@
64
67
  /* SWOOP SILHOUETTE */
65
68
 
66
69
  .swoop-silhouette {
70
+ display: var(--dx-c-featured-content-header-swoop-display);
67
71
  position: absolute;
68
72
  bottom: -2px;
69
73
  left: -1px;
@@ -228,6 +232,60 @@
228
232
  }
229
233
  }
230
234
 
235
+ .custom-bg-trial::after {
236
+ content: "";
237
+ background-image: url(/assets/svg/trial-left.svg),
238
+ url(/assets/svg/trial-right.svg);
239
+ background-position: var(--dx-g-page-padding-horizontal) 50px,
240
+ right 15px top;
241
+ background-repeat: no-repeat;
242
+ position: absolute;
243
+ left: 0;
244
+ top: 0;
245
+ width: 100%;
246
+ height: 100%;
247
+ }
248
+
249
+ @media screen and (max-width: 1024px) {
250
+ .custom-bg-trial::after {
251
+ background-image: url(/assets/svg/trial-left.svg);
252
+ background-position: 48px 30px;
253
+ background-size: contain;
254
+ }
255
+ }
256
+
257
+ .custom-bg-big-moon::after {
258
+ content: "";
259
+ background-image: url(/assets/svg/big-moon.svg);
260
+ background-position: top right;
261
+ background-repeat: no-repeat;
262
+ position: absolute;
263
+ right: 0;
264
+ top: 0;
265
+ width: 100%;
266
+ height: 100%;
267
+ }
268
+
269
+ .blue-circle {
270
+ display: flex;
271
+ justify-content: center;
272
+ align-items: center;
273
+ position: absolute;
274
+ background-image: url(/assets/svg/blue-circle.svg);
275
+ top: 110px;
276
+ right: 230px;
277
+ left: unset;
278
+ width: 126px;
279
+ height: 126px;
280
+ }
281
+
282
+ @media screen and (max-width: 1230px) {
283
+ .custom-bg-big-moon::after,
284
+ .blue-circle {
285
+ display: none;
286
+ }
287
+ }
288
+
231
289
  /* Z INDEX STUFF */
232
290
 
233
291
  .label,
@@ -106,4 +106,12 @@
106
106
  </svg>
107
107
  </template>
108
108
  </div>
109
+ <div class="blue-circle" if:true={hasIcon}>
110
+ <dx-icon
111
+ class="icon"
112
+ size="2xlarge"
113
+ symbol={icon}
114
+ sprite="salesforcebrand"
115
+ ></dx-icon>
116
+ </div>
109
117
  </template>
@@ -17,8 +17,16 @@ export default class FeaturedContentHeader extends LightningElement {
17
17
  @api href?: string;
18
18
  @api target?: string | null = null;
19
19
  @api title!: string;
20
- @api backgroundImg?: "trees" | "codey" | "blog" | "moon" | null = null;
20
+ @api backgroundImg?:
21
+ | "trees"
22
+ | "codey"
23
+ | "blog"
24
+ | "moon"
25
+ | "trial"
26
+ | "big-moon"
27
+ | null = null;
21
28
  @api noSwoop: boolean = false;
29
+ @api icon: string = "";
22
30
 
23
31
  private _authors?: Array<ImageAndLabel>;
24
32
  private isSlotEmpty: boolean = true;
@@ -66,6 +74,10 @@ export default class FeaturedContentHeader extends LightningElement {
66
74
  return this.backgroundImg !== null;
67
75
  }
68
76
 
77
+ private get hasIcon(): boolean {
78
+ return this.icon !== "" && this.backgroundImg === "big-moon";
79
+ }
80
+
69
81
  private get backgroundImageClass() {
70
82
  return cx(
71
83
  "bg-image-container",
@@ -41,6 +41,7 @@
41
41
 
42
42
  .nav-menu.state-closed {
43
43
  opacity: 0;
44
+ visibility: hidden;
44
45
  transform: translateY(-16px);
45
46
  pointer-events: none;
46
47
  }
@@ -20,6 +20,7 @@
20
20
  onclear={onInputEscape}
21
21
  onsubmit={onInputSubmit}
22
22
  onfocus={openDropdown}
23
+ onblur={onDropdownRequestClose}
23
24
  placeholder="Search"
24
25
  size="override"
25
26
  type="search"
@@ -51,6 +52,7 @@
51
52
  onclear={onInputEscape}
52
53
  onsubmit={onInputSubmit}
53
54
  onfocus={openDropdown}
55
+ onblur={onDropdownRequestClose}
54
56
  placeholder="Search"
55
57
  shortcut-key="k"
56
58
  size="small"
@@ -69,6 +69,7 @@ export default class HeaderSearch extends LightningElement {
69
69
  switch (e.detail as PopoverRequestCloseType) {
70
70
  case "interact-outside":
71
71
  case "keypress-escape":
72
+ case "input-blur":
72
73
  this.closeDropdown();
73
74
  break;
74
75
  default:
@@ -186,6 +186,8 @@ export default class Input extends LightningElement {
186
186
  if (this.validityOnBlur) {
187
187
  this.reportValidity();
188
188
  }
189
+
190
+ this.dispatchEvent(new CustomEvent("blur", { detail: "input-blur" }));
189
191
  }
190
192
 
191
193
  onInputChange(e: InputEvent) {
@@ -0,0 +1,40 @@
1
+ :host {
2
+ --dx-c-modal-drawer-z-index: 5000;
3
+ }
4
+
5
+ .modal-drawer_container {
6
+ display: none;
7
+ }
8
+
9
+ .modal-drawer_container.modal-drawer_active {
10
+ position: fixed;
11
+ top: 50%;
12
+ left: 50%;
13
+ transform: translate(-50%, -50%);
14
+ display: grid;
15
+ justify-content: center;
16
+ align-items: center;
17
+ z-index: var(--dx-c-modal-drawer-z-index);
18
+ height: 100%;
19
+ width: 100%;
20
+ pointer-events: none;
21
+ }
22
+
23
+ .modal-drawer_overlay {
24
+ background-color: #195594;
25
+ bottom: 0;
26
+ height: 100%;
27
+ left: 0;
28
+ opacity: 0.62;
29
+ position: fixed;
30
+ right: 0;
31
+ top: 0;
32
+ width: 100%;
33
+ z-index: calc(var(--dx-c-modal-drawer-z-index) - 1);
34
+ }
35
+
36
+ @media screen and (max-width: 768px) {
37
+ .modal-drawer_container.modal-drawer_active {
38
+ pointer-events: all;
39
+ }
40
+ }
@@ -0,0 +1,10 @@
1
+ <template>
2
+ <div class={containerClass} tabindex="0" role="dialog">
3
+ <slot></slot>
4
+ </div>
5
+ <div
6
+ if:true={open}
7
+ class="modal-drawer_overlay"
8
+ onclick={handleToggleModal}
9
+ ></div>
10
+ </template>
@@ -0,0 +1,50 @@
1
+ import cx from "classnames";
2
+ import { LightningElement, api } from "lwc";
3
+ import { normalizeBoolean } from "dxUtils/normalizers";
4
+
5
+ export default class Modal extends LightningElement {
6
+ private _open = false;
7
+
8
+ @api get open() {
9
+ return this._open;
10
+ }
11
+
12
+ set open(value) {
13
+ this._open = normalizeBoolean(value);
14
+ }
15
+
16
+ get containerClass(): string {
17
+ return cx(
18
+ "modal-drawer_container",
19
+ this._open && "modal-drawer_active"
20
+ );
21
+ }
22
+
23
+ renderedCallback(): void {
24
+ if (this._open) {
25
+ this.focusFirstElement();
26
+ }
27
+ }
28
+
29
+ connectedCallback(): void {
30
+ this.template.addEventListener(
31
+ "keydown",
32
+ this.handleKeyDown.bind(this)
33
+ );
34
+ }
35
+
36
+ focusFirstElement(): void {
37
+ this.template.querySelector("div")!.focus();
38
+ }
39
+
40
+ handleKeyDown(event: KeyboardEvent): void {
41
+ if (event.key === "Escape") {
42
+ this.handleToggleModal();
43
+ }
44
+ }
45
+
46
+ handleToggleModal() {
47
+ this._open = !this._open;
48
+ this.dispatchEvent(new CustomEvent("togglemodal"));
49
+ }
50
+ }