@redvars/peacock 3.5.1 → 3.6.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 (198) hide show
  1. package/dist/{BaseButton-DuASuVth.js → BaseButton-BNFAYn-S.js} +2 -2
  2. package/dist/{BaseButton-DuASuVth.js.map → BaseButton-BNFAYn-S.js.map} +1 -1
  3. package/dist/BaseInput-14YmcfK7.js +27 -0
  4. package/dist/BaseInput-14YmcfK7.js.map +1 -0
  5. package/dist/banner.js +2 -3
  6. package/dist/banner.js.map +1 -1
  7. package/dist/{button-DouvOfEU.js → button-colors-Ccys3hvS.js} +5 -294
  8. package/dist/button-colors-Ccys3hvS.js.map +1 -0
  9. package/dist/button-group.js +226 -6
  10. package/dist/button-group.js.map +1 -1
  11. package/dist/button.js +294 -8
  12. package/dist/button.js.map +1 -1
  13. package/dist/calendar-column-view.js +634 -0
  14. package/dist/calendar-column-view.js.map +1 -0
  15. package/dist/calendar-event-BrQ_SEKD.js +199 -0
  16. package/dist/calendar-event-BrQ_SEKD.js.map +1 -0
  17. package/dist/calendar-month-view.js +376 -0
  18. package/dist/calendar-month-view.js.map +1 -0
  19. package/dist/calendar.js +339 -0
  20. package/dist/calendar.js.map +1 -0
  21. package/dist/canvas.js +361 -0
  22. package/dist/canvas.js.map +1 -0
  23. package/dist/cb-compound-expression.js +125 -0
  24. package/dist/cb-compound-expression.js.map +1 -0
  25. package/dist/cb-divider.js +150 -0
  26. package/dist/cb-divider.js.map +1 -0
  27. package/dist/cb-expression.js +75 -0
  28. package/dist/cb-expression.js.map +1 -0
  29. package/dist/cb-predicate.js +137 -0
  30. package/dist/cb-predicate.js.map +1 -0
  31. package/dist/code-editor.js +2 -1
  32. package/dist/code-editor.js.map +1 -1
  33. package/dist/condition-builder.js +58 -0
  34. package/dist/condition-builder.js.map +1 -0
  35. package/dist/custom-elements-jsdocs.json +7976 -4294
  36. package/dist/custom-elements.json +14358 -7589
  37. package/dist/dropdown-button.js +216 -0
  38. package/dist/dropdown-button.js.map +1 -0
  39. package/dist/event-manager-D-QCmUgR.js +113 -0
  40. package/dist/event-manager-D-QCmUgR.js.map +1 -0
  41. package/dist/fab.js +1 -1
  42. package/dist/flow-designer-dZnLJOQT.js +1656 -0
  43. package/dist/flow-designer-dZnLJOQT.js.map +1 -0
  44. package/dist/flow-designer-node-XMe-jlKg.js +548 -0
  45. package/dist/flow-designer-node-XMe-jlKg.js.map +1 -0
  46. package/dist/flow-designer-node.js +4 -0
  47. package/dist/flow-designer-node.js.map +1 -0
  48. package/dist/flow-designer.js +16 -0
  49. package/dist/flow-designer.js.map +1 -0
  50. package/dist/html-editor.js +358 -0
  51. package/dist/html-editor.js.map +1 -0
  52. package/dist/icon-button-CK1ZuE-2.js +247 -0
  53. package/dist/icon-button-CK1ZuE-2.js.map +1 -0
  54. package/dist/index.js +29 -6
  55. package/dist/index.js.map +1 -1
  56. package/dist/{is-dark-mode-DicqGkCJ.js → is-dark-mode-DOcaw4Yq.js} +2 -27
  57. package/dist/is-dark-mode-DOcaw4Yq.js.map +1 -0
  58. package/dist/modal.js +418 -0
  59. package/dist/modal.js.map +1 -0
  60. package/dist/{navigation-rail-Lxetd5-Z.js → navigation-rail-DyO0oAZU.js} +306 -2197
  61. package/dist/navigation-rail-DyO0oAZU.js.map +1 -0
  62. package/dist/notification-manager.js +268 -0
  63. package/dist/notification-manager.js.map +1 -0
  64. package/dist/peacock-loader.js +84 -8
  65. package/dist/peacock-loader.js.map +1 -1
  66. package/dist/popover-NC7b1lTq.js +1971 -0
  67. package/dist/popover-NC7b1lTq.js.map +1 -0
  68. package/dist/popover-content.js +125 -0
  69. package/dist/popover-content.js.map +1 -0
  70. package/dist/popover.js +4 -0
  71. package/dist/popover.js.map +1 -0
  72. package/dist/split-button.js +388 -0
  73. package/dist/split-button.js.map +1 -0
  74. package/dist/src/__controllers/floating-controller.d.ts +35 -0
  75. package/dist/src/calendar/base-event.d.ts +10 -0
  76. package/dist/src/calendar/calendar-column-view.d.ts +41 -0
  77. package/dist/src/calendar/calendar-event.d.ts +7 -0
  78. package/dist/src/calendar/calendar-month-view.d.ts +31 -0
  79. package/dist/src/calendar/calendar.d.ts +65 -0
  80. package/dist/src/calendar/event-manager.d.ts +17 -0
  81. package/dist/src/calendar/index.d.ts +4 -0
  82. package/dist/src/calendar/types.d.ts +13 -0
  83. package/dist/src/calendar/utils.d.ts +31 -0
  84. package/dist/src/canvas/canvas.d.ts +92 -0
  85. package/dist/src/canvas/index.d.ts +2 -0
  86. package/dist/src/condition-builder/cb-compound-expression.d.ts +31 -0
  87. package/dist/src/condition-builder/cb-divider.d.ts +26 -0
  88. package/dist/src/condition-builder/cb-expression.d.ts +31 -0
  89. package/dist/src/condition-builder/cb-predicate.d.ts +30 -0
  90. package/dist/src/condition-builder/condition-builder.d.ts +27 -0
  91. package/dist/src/condition-builder/index.d.ts +5 -0
  92. package/dist/src/dropdown-button/dropdown-button.d.ts +68 -0
  93. package/dist/src/dropdown-button/index.d.ts +1 -0
  94. package/dist/src/flow-designer/commands.d.ts +66 -0
  95. package/dist/src/flow-designer/flow-designer-node.d.ts +46 -0
  96. package/dist/src/flow-designer/flow-designer.d.ts +133 -0
  97. package/dist/src/flow-designer/index.d.ts +7 -0
  98. package/dist/src/flow-designer/layout.d.ts +30 -0
  99. package/dist/src/flow-designer/types.d.ts +142 -0
  100. package/dist/src/flow-designer/validation.d.ts +43 -0
  101. package/dist/src/flow-designer/workflow-utils.d.ts +40 -0
  102. package/dist/src/html-editor/html-editor.d.ts +56 -0
  103. package/dist/src/html-editor/index.d.ts +2 -0
  104. package/dist/src/index.d.ts +13 -0
  105. package/dist/src/menu/menu/menu.d.ts +5 -7
  106. package/dist/src/menu/menu-item/menu-item.d.ts +14 -13
  107. package/dist/src/modal/index.d.ts +1 -0
  108. package/dist/src/modal/modal.d.ts +63 -0
  109. package/dist/src/notification-manager/index.d.ts +1 -0
  110. package/dist/src/notification-manager/notification-manager.d.ts +44 -0
  111. package/dist/src/popover/index.d.ts +2 -0
  112. package/dist/src/popover/popover-content.d.ts +29 -0
  113. package/dist/src/popover/popover.d.ts +62 -0
  114. package/dist/src/split-button/index.d.ts +1 -0
  115. package/dist/src/split-button/split-button.d.ts +72 -0
  116. package/dist/src/tooltip/tooltip.d.ts +2 -15
  117. package/dist/test/flow-designer.test.d.ts +1 -0
  118. package/dist/tsconfig.tsbuildinfo +1 -1
  119. package/package.json +4 -2
  120. package/readme.md +2 -2
  121. package/src/__controllers/floating-controller.ts +237 -0
  122. package/src/banner/banner.scss +2 -3
  123. package/src/button/button/button.ts +1 -0
  124. package/src/calendar/base-event.ts +49 -0
  125. package/src/calendar/calendar-column-view.scss +326 -0
  126. package/src/calendar/calendar-column-view.ts +392 -0
  127. package/src/calendar/calendar-event.ts +20 -0
  128. package/src/calendar/calendar-month-view.scss +192 -0
  129. package/src/calendar/calendar-month-view.ts +244 -0
  130. package/src/calendar/calendar.scss +71 -0
  131. package/src/calendar/calendar.ts +298 -0
  132. package/src/calendar/event-manager.ts +117 -0
  133. package/src/calendar/index.ts +4 -0
  134. package/src/calendar/types.ts +14 -0
  135. package/src/calendar/utils.ts +180 -0
  136. package/src/canvas/canvas.scss +60 -0
  137. package/src/canvas/canvas.ts +391 -0
  138. package/src/canvas/index.ts +2 -0
  139. package/src/condition-builder/cb-compound-expression.scss +37 -0
  140. package/src/condition-builder/cb-compound-expression.ts +80 -0
  141. package/src/condition-builder/cb-divider.scss +93 -0
  142. package/src/condition-builder/cb-divider.ts +56 -0
  143. package/src/condition-builder/cb-expression.scss +14 -0
  144. package/src/condition-builder/cb-expression.ts +49 -0
  145. package/src/condition-builder/cb-predicate.scss +35 -0
  146. package/src/condition-builder/cb-predicate.ts +102 -0
  147. package/src/condition-builder/condition-builder.scss +13 -0
  148. package/src/condition-builder/condition-builder.ts +38 -0
  149. package/src/condition-builder/index.ts +5 -0
  150. package/src/dropdown-button/demo/index.html +110 -0
  151. package/src/dropdown-button/dropdown-button.scss +22 -0
  152. package/src/dropdown-button/dropdown-button.ts +206 -0
  153. package/src/dropdown-button/index.ts +1 -0
  154. package/src/flow-designer/DEMO.md +239 -0
  155. package/src/flow-designer/commands.ts +278 -0
  156. package/src/flow-designer/flow-designer-node.ts +172 -0
  157. package/src/flow-designer/flow-designer.scss +457 -0
  158. package/src/flow-designer/flow-designer.ts +611 -0
  159. package/src/flow-designer/index.ts +41 -0
  160. package/src/flow-designer/layout.ts +357 -0
  161. package/src/flow-designer/types.ts +166 -0
  162. package/src/flow-designer/validation.ts +284 -0
  163. package/src/flow-designer/workflow-utils.ts +282 -0
  164. package/src/html-editor/html-editor.scss +146 -0
  165. package/src/html-editor/html-editor.ts +276 -0
  166. package/src/html-editor/index.ts +3 -0
  167. package/src/index.ts +25 -0
  168. package/src/menu/menu/menu.scss +2 -2
  169. package/src/menu/menu/menu.ts +91 -101
  170. package/src/menu/menu-item/menu-item.scss +4 -0
  171. package/src/menu/menu-item/menu-item.ts +82 -78
  172. package/src/modal/index.ts +1 -0
  173. package/src/modal/modal.scss +206 -0
  174. package/src/modal/modal.ts +201 -0
  175. package/src/notification-manager/index.ts +1 -0
  176. package/src/notification-manager/notification-manager.scss +113 -0
  177. package/src/notification-manager/notification-manager.ts +199 -0
  178. package/src/peacock-loader.ts +71 -0
  179. package/src/popover/index.ts +2 -0
  180. package/src/popover/popover-content.scss +69 -0
  181. package/src/popover/popover-content.ts +51 -0
  182. package/src/popover/popover.scss +7 -0
  183. package/src/popover/popover.ts +170 -0
  184. package/src/split-button/index.ts +1 -0
  185. package/src/split-button/split-button-colors.scss +56 -0
  186. package/src/split-button/split-button-sizes.scss +28 -0
  187. package/src/split-button/split-button.scss +79 -0
  188. package/src/split-button/split-button.ts +236 -0
  189. package/src/table/table.ts +2 -2
  190. package/src/tooltip/tooltip.scss +4 -3
  191. package/src/tooltip/tooltip.ts +46 -104
  192. package/dist/button-DouvOfEU.js.map +0 -1
  193. package/dist/button-group-CEdMwvJJ.js +0 -464
  194. package/dist/button-group-CEdMwvJJ.js.map +0 -1
  195. package/dist/is-dark-mode-DicqGkCJ.js.map +0 -1
  196. package/dist/navigation-rail-Lxetd5-Z.js.map +0 -1
  197. package/dist/src/menu/menu/MenuSurfaceController.d.ts +0 -18
  198. package/src/menu/menu/MenuSurfaceController.ts +0 -61
@@ -1,8 +1,11 @@
1
1
  import { html, LitElement, nothing } from 'lit';
2
- import { property } from 'lit/decorators.js';
2
+ import { property, query, state } from 'lit/decorators.js';
3
3
  import { classMap } from 'lit/directives/class-map.js';
4
4
  import styles from './menu-item.scss';
5
5
  import colorStyles from './menu-item-colors.scss';
6
+ import BaseButtonMixin from '@/__mixins/BaseButtonMixin.js';
7
+ import BaseHyperlinkMixin from '@/__mixins/BaseHyperlinkMixin.js';
8
+ import { dispatchActivationClick, isActivationClick } from '@/__utils/dispatch-event-utils.js';
6
9
 
7
10
  /**
8
11
  * @label Menu Item
@@ -17,9 +20,8 @@ import colorStyles from './menu-item-colors.scss';
17
20
  * <wc-menu-item>Menu Item</wc-menu-item>
18
21
  * ```
19
22
  */
20
- export class MenuItem extends LitElement {
21
- @property({ type: Boolean, reflect: true }) disabled = false;
22
-
23
+ export class MenuItem extends BaseButtonMixin(BaseHyperlinkMixin(LitElement)) {
24
+
23
25
  @property({ type: String }) value = '';
24
26
 
25
27
  @property({ type: Boolean, reflect: true }) selected = false;
@@ -30,21 +32,19 @@ export class MenuItem extends LitElement {
30
32
 
31
33
  @property({ type: Boolean, attribute: 'submenu-open' }) submenuOpen = false;
32
34
 
33
- /*
34
- * Hyperlink to navigate to on click.
35
- */
36
- @property({ reflect: true }) href?: string;
37
-
38
- /**
39
- * Sets or retrieves the window or frame at which to target content.
40
- */
41
- @property() target: string = '_self';
42
-
43
35
  @property({ type: String, reflect: true }) variant: 'standard' | 'vibrant' =
44
36
  'standard';
45
37
 
46
38
  static styles = [styles, colorStyles];
47
39
 
40
+ @query('#item') readonly itemElement!: HTMLElement | null;
41
+
42
+ /**
43
+ * States
44
+ */
45
+ @state()
46
+ isPressed = false;
47
+
48
48
  connectedCallback() {
49
49
  // eslint-disable-next-line wc/guard-super-call
50
50
  super.connectedCallback();
@@ -52,91 +52,80 @@ export class MenuItem extends LitElement {
52
52
  this.setAttribute('role', 'menuitem');
53
53
  }
54
54
 
55
- if (!this.hasAttribute('tabindex')) {
56
- this.tabIndex = -1;
57
- }
58
-
59
- this.addEventListener('click', this._handleClick);
60
- this.addEventListener('keydown', this._handleKeyDown);
55
+ this.addEventListener('click', this.__dispatchClickWithThrottle);
56
+ window.addEventListener('mouseup', this.__handlePress);
61
57
  }
62
58
 
63
59
  disconnectedCallback() {
64
- this.removeEventListener('click', this._handleClick);
65
- this.removeEventListener('keydown', this._handleKeyDown);
60
+ window.removeEventListener('mouseup', this.__handlePress);
61
+ this.removeEventListener('click', this.__dispatchClickWithThrottle);
66
62
  super.disconnectedCallback();
67
63
  }
68
64
 
69
- private emitActivate(source: 'click' | 'keydown', key?: string) {
70
- this.dispatchEvent(
71
- new CustomEvent('menu-item-activate', {
72
- bubbles: true,
73
- composed: true,
74
- detail: { item: this, source, key },
75
- }),
76
- );
65
+ override focus() {
66
+ this.itemElement?.focus();
77
67
  }
78
68
 
79
- private requestClose(source: 'click' | 'keydown', key?: string) {
80
- this.dispatchEvent(
81
- new CustomEvent('menu-item-request-close', {
82
- bubbles: true,
83
- composed: true,
84
- detail: {
85
- item: this,
86
- source,
87
- key,
88
- reason: source === 'click' ? 'click-selection' : 'keydown',
89
- },
90
- }),
91
- );
69
+ override blur() {
70
+ this.itemElement?.blur();
92
71
  }
93
72
 
94
- private requestSubmenuKey(key: string) {
95
- this.dispatchEvent(
96
- new CustomEvent('menu-item-submenu-keydown', {
97
- bubbles: true,
98
- composed: true,
99
- detail: { item: this, key },
100
- }),
101
- );
102
- }
103
-
104
- private _handleKeyDown(e: KeyboardEvent) {
105
- if (this.disabled) {
106
- e.preventDefault();
73
+ __dispatchClickWithThrottle: (event: MouseEvent | KeyboardEvent) => void =
74
+ event => {
75
+ this.__dispatchClick(event);
76
+ };
77
+
78
+ __dispatchClick = (event: MouseEvent | KeyboardEvent) => {
79
+ // If the button is soft-disabled or a disabled link, we need to explicitly
80
+ // prevent the click from propagating to other event listeners as well as
81
+ // prevent the default action.
82
+ if (this.softDisabled || (this.disabled && this.href)) {
83
+ event.stopImmediatePropagation();
84
+ event.preventDefault();
107
85
  return;
108
86
  }
109
87
 
110
- if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
111
- this.requestSubmenuKey(e.key);
88
+ if (!isActivationClick(event) || !this.itemElement) {
112
89
  return;
113
90
  }
114
91
 
115
- if (e.key === 'Enter' || e.key === ' ') {
116
- e.preventDefault();
117
- this.emitActivate('keydown', e.key);
118
- if (!this.keepOpen) {
119
- this.requestClose('keydown', e.key);
120
- }
121
- }
122
- }
92
+ this.focus();
93
+ dispatchActivationClick(this.itemElement);
94
+ };
95
+
96
+ __handleKeyDown = (event: KeyboardEvent) => {
97
+ this.__handlePress(event);
123
98
 
124
- private _handleClick(e: MouseEvent) {
125
- if (this.disabled) {
126
- e.preventDefault();
127
- e.stopPropagation();
99
+ if (this.disabled || this.softDisabled || !this.itemElement) {
128
100
  return;
129
101
  }
130
102
 
131
- this.emitActivate('click');
132
- if (!this.keepOpen) {
133
- this.requestClose('click');
103
+ if (event.key === ' ') {
104
+ event.preventDefault();
105
+ this.itemElement.click();
106
+ return;
134
107
  }
135
- }
136
108
 
137
- __isLink() {
138
- return !!this.href;
139
- }
109
+ if (event.key === 'Enter' && !this.__isLink()) {
110
+ event.preventDefault();
111
+ this.itemElement.click();
112
+ }
113
+ };
114
+
115
+ __handlePress = (event: KeyboardEvent | MouseEvent) => {
116
+ if (this.disabled || this.softDisabled) return;
117
+ if (
118
+ event instanceof KeyboardEvent &&
119
+ event.type === 'keydown' &&
120
+ (event.key === 'Enter' || event.key === ' ')
121
+ ) {
122
+ this.isPressed = true;
123
+ } else if (event.type === 'mousedown') {
124
+ this.isPressed = true;
125
+ } else {
126
+ this.isPressed = false;
127
+ }
128
+ };
140
129
 
141
130
  render() {
142
131
  const isLink = this.__isLink();
@@ -145,6 +134,7 @@ export class MenuItem extends LitElement {
145
134
  'menu-item': true,
146
135
  disabled: this.disabled,
147
136
  selected: this.selected,
137
+ pressed: this.isPressed,
148
138
  };
149
139
 
150
140
  const controls = this.getAttribute('aria-controls');
@@ -155,6 +145,13 @@ export class MenuItem extends LitElement {
155
145
  class=${classMap(cssClasses)}
156
146
  href=${this.href}
157
147
  target=${this.target}
148
+ tabindex=${this.disabled ? '-1' : '0'}
149
+
150
+ @click=${this.__dispatchClickWithThrottle}
151
+ @mousedown=${this.__handlePress}
152
+ @keydown=${this.__handleKeyDown}
153
+ @keyup=${this.__handlePress}
154
+
158
155
  aria-disabled=${String(this.disabled)}
159
156
  aria-haspopup=${this.hasSubmenu ? 'menu' : nothing}
160
157
  aria-controls=${this.hasSubmenu && controls ? controls : nothing}
@@ -167,6 +164,13 @@ export class MenuItem extends LitElement {
167
164
  return html`<div
168
165
  id="item"
169
166
  class=${classMap(cssClasses)}
167
+ tabindex=${this.disabled ? '-1' : '0'}
168
+
169
+ @click=${this.__dispatchClick}
170
+ @mousedown=${this.__handlePress}
171
+ @keydown=${this.__handleKeyDown}
172
+ @keyup=${this.__handlePress}
173
+
170
174
  aria-disabled=${String(this.disabled)}
171
175
  aria-haspopup=${this.hasSubmenu ? 'menu' : nothing}
172
176
  aria-controls=${this.hasSubmenu && controls ? controls : nothing}
@@ -182,7 +186,7 @@ export class MenuItem extends LitElement {
182
186
  <div class="background"></div>
183
187
  <wc-ripple class="ripple"></wc-ripple>
184
188
 
185
- <div class="menu-item-content">
189
+ <div class="menu-item-content" data-variant=${this.variant}>
186
190
  <slot name="leading-icon"></slot>
187
191
  <div class="slot-container">
188
192
  <slot></slot>
@@ -0,0 +1 @@
1
+ export { Modal } from './modal.js';
@@ -0,0 +1,206 @@
1
+ @use "../../scss/mixin";
2
+
3
+ @include mixin.base-styles;
4
+
5
+ :host {
6
+ display: contents;
7
+
8
+ --modal-container-color: var(--color-surface-container-high, #ece6f0);
9
+ --modal-scrim-color: rgba(0, 0, 0, 0.32);
10
+ --modal-shape: var(--shape-corner-extra-large, 28px);
11
+ --modal-min-width: 280px;
12
+ --modal-max-width: 560px;
13
+ --modal-max-height: 90dvh;
14
+ --modal-transition-duration: var(--duration-medium2, 300ms);
15
+ --modal-transition-easing: var(--easing-emphasized, cubic-bezier(0.2, 0, 0, 1));
16
+ --modal-heading-color: var(--color-on-surface, #1c1b1f);
17
+ --modal-subheading-color: var(--color-on-surface-variant, #49454f);
18
+ --modal-content-color: var(--color-on-surface-variant, #49454f);
19
+ --modal-divider-color: var(--color-outline-variant, #cac4d0);
20
+ --modal-elevation: var(--elevation-level3, 0 4px 8px 3px rgba(0, 0, 0, 0.15), 0 1px 3px rgba(0, 0, 0, 0.3));
21
+ }
22
+
23
+ /* Scrim backdrop */
24
+ .scrim {
25
+ background-color: var(--modal-scrim-color);
26
+ inset: 0;
27
+ opacity: 0;
28
+ pointer-events: none;
29
+ position: fixed;
30
+ transition: opacity var(--modal-transition-duration) var(--modal-transition-easing);
31
+ z-index: 1000;
32
+ }
33
+
34
+ .scrim.visible {
35
+ opacity: 1;
36
+ pointer-events: auto;
37
+ }
38
+
39
+ /* Dialog wrapper — centres the dialog on screen */
40
+ .dialog-wrapper {
41
+ align-items: center;
42
+ display: flex;
43
+ inset: 0;
44
+ justify-content: center;
45
+ overflow-y: auto;
46
+ padding: var(--spacing-400, 2rem) var(--spacing-200, 1rem);
47
+ position: fixed;
48
+ z-index: 1001;
49
+ }
50
+
51
+ /* Dialog container */
52
+ .dialog {
53
+ background-color: var(--modal-container-color);
54
+ border-radius: var(--modal-shape);
55
+ box-shadow: var(--modal-elevation);
56
+ display: flex;
57
+ flex-direction: column;
58
+ max-height: var(--modal-max-height);
59
+ max-width: var(--modal-max-width);
60
+ min-width: var(--modal-min-width);
61
+ opacity: 0;
62
+ outline: none;
63
+ overflow: hidden;
64
+ position: relative;
65
+ transform: scale(0.9);
66
+ width: 100%;
67
+
68
+ &.open {
69
+ animation: modal-enter var(--modal-transition-duration) var(--modal-transition-easing) forwards;
70
+ }
71
+
72
+ &:not(.open) {
73
+ animation: modal-exit var(--modal-transition-duration) var(--modal-transition-easing) forwards;
74
+ }
75
+ }
76
+
77
+ /* Sizes */
78
+ .dialog.size-xs {
79
+ --modal-max-width: 320px;
80
+ }
81
+
82
+ .dialog.size-sm {
83
+ --modal-max-width: 420px;
84
+ }
85
+
86
+ .dialog.size-md {
87
+ --modal-max-width: 560px;
88
+ }
89
+
90
+ .dialog.size-lg {
91
+ --modal-max-width: 800px;
92
+ }
93
+
94
+ .dialog.size-fullscreen {
95
+ --modal-max-width: 100%;
96
+ --modal-max-height: 100%;
97
+ border-radius: 0;
98
+ max-height: 100dvh;
99
+ max-width: 100vw;
100
+ }
101
+
102
+ .dialog-wrapper:has(.dialog.size-fullscreen) {
103
+ padding: 0;
104
+ }
105
+
106
+ /* Header */
107
+ .dialog-header {
108
+ align-items: flex-start;
109
+ display: flex;
110
+ gap: var(--spacing-200, 1rem);
111
+ padding: var(--spacing-300, 1.5rem) var(--spacing-300, 1.5rem) var(--spacing-200, 1rem);
112
+ }
113
+
114
+ .dialog-heading-section {
115
+ flex: 1;
116
+ min-width: 0;
117
+ }
118
+
119
+ .dialog-subheading {
120
+ color: var(--modal-subheading-color);
121
+ font-family: var(--typography-label-medium-font-family, sans-serif);
122
+ font-size: var(--typography-label-medium-font-size, 0.75rem);
123
+ font-weight: var(--typography-label-medium-font-weight, 500);
124
+ letter-spacing: var(--typography-label-medium-letter-spacing, 0.05em);
125
+ line-height: var(--typography-label-medium-line-height, 1.25rem);
126
+ margin: 0 0 var(--spacing-050, 0.25rem);
127
+ text-transform: uppercase;
128
+ }
129
+
130
+ .dialog-heading {
131
+ color: var(--modal-heading-color);
132
+ font-family: var(--typography-headline-small-font-family, sans-serif);
133
+ font-size: var(--typography-headline-small-font-size, 1.5rem);
134
+ font-weight: var(--typography-headline-small-font-weight, 400);
135
+ letter-spacing: var(--typography-headline-small-letter-spacing, 0);
136
+ line-height: var(--typography-headline-small-line-height, 2rem);
137
+ margin: 0;
138
+ }
139
+
140
+ .dialog-close {
141
+ flex-shrink: 0;
142
+ margin-block-start: -0.25rem;
143
+ margin-inline-end: -0.25rem;
144
+ }
145
+
146
+ /* Body content */
147
+ .dialog-content {
148
+ color: var(--modal-content-color);
149
+ flex: 1 1 auto;
150
+ overflow-y: auto;
151
+ padding: 0 var(--spacing-300, 1.5rem) var(--spacing-300, 1.5rem);
152
+ }
153
+
154
+ /* When there is no header */
155
+ .dialog:not(:has(.dialog-header)) .dialog-content {
156
+ padding-top: var(--spacing-300, 1.5rem);
157
+ }
158
+
159
+ /* Footer */
160
+ .dialog-footer {
161
+ border-top: 1px solid transparent;
162
+ }
163
+
164
+ .dialog-footer:has(slot:not(:empty)),
165
+ .dialog-footer slot::slotted(*) {
166
+ border-top-color: var(--modal-divider-color);
167
+ }
168
+
169
+ /* Loading overlay */
170
+ .dialog-loader {
171
+ align-items: center;
172
+ display: flex;
173
+ inset: 0;
174
+ justify-content: center;
175
+ position: absolute;
176
+ }
177
+
178
+ .dialog-loader-scrim {
179
+ background-color: var(--modal-container-color);
180
+ inset: 0;
181
+ opacity: 0.72;
182
+ position: absolute;
183
+ }
184
+
185
+ /* Animations */
186
+ @keyframes modal-enter {
187
+ from {
188
+ opacity: 0;
189
+ transform: scale(0.9);
190
+ }
191
+ to {
192
+ opacity: 1;
193
+ transform: scale(1);
194
+ }
195
+ }
196
+
197
+ @keyframes modal-exit {
198
+ from {
199
+ opacity: 1;
200
+ transform: scale(1);
201
+ }
202
+ to {
203
+ opacity: 0;
204
+ transform: scale(0.9);
205
+ }
206
+ }
@@ -0,0 +1,201 @@
1
+ import { LitElement, html, nothing } from 'lit';
2
+ import { property, state } from 'lit/decorators.js';
3
+ import { classMap } from 'lit/directives/class-map.js';
4
+ import IndividualComponent from '../IndividualComponent.js';
5
+ import styles from './modal.scss';
6
+
7
+ type ModalSize = 'xs' | 'sm' | 'md' | 'lg' | 'fullscreen';
8
+
9
+ /**
10
+ * @label Modal
11
+ * @tag wc-modal
12
+ * @rawTag modal-wc
13
+ * @summary A Material Design 3 dialog/modal for displaying content in a layer above the page, with optional header, body, and footer slots.
14
+ *
15
+ * @cssprop --modal-container-color - Background color of the dialog container.
16
+ * @cssprop --modal-scrim-color - Color of the scrim backdrop.
17
+ * @cssprop --modal-shape - Corner radius of the dialog container.
18
+ * @cssprop --modal-min-width - Minimum width of the dialog.
19
+ * @cssprop --modal-max-width - Maximum width of the dialog.
20
+ * @cssprop --modal-max-height - Maximum height of the dialog.
21
+ *
22
+ * @example
23
+ * ```html
24
+ * <wc-modal open heading="Confirm Action">
25
+ * <p>Are you sure you want to continue?</p>
26
+ * <div slot="footer">
27
+ * <wc-button variant="text">Cancel</wc-button>
28
+ * <wc-button>Confirm</wc-button>
29
+ * </div>
30
+ * </wc-modal>
31
+ * ```
32
+ * @tags overlay, dialog, feedback
33
+ */
34
+ @IndividualComponent
35
+ export class Modal extends LitElement {
36
+ static styles = [styles];
37
+
38
+ /** Whether the modal is open. */
39
+ @property({ type: Boolean, reflect: true }) open = false;
40
+
41
+ /** Heading text shown in the modal header. */
42
+ @property({ type: String, reflect: true }) heading = '';
43
+
44
+ /** Optional subheading / label text shown above the heading. */
45
+ @property({ type: String, reflect: true }) subheading = '';
46
+
47
+ /**
48
+ * Size of the modal dialog.
49
+ * - `"xs"`: Extra-small.
50
+ * - `"sm"`: Small.
51
+ * - `"md"`: Medium (default).
52
+ * - `"lg"`: Large.
53
+ * - `"fullscreen"`: Full-screen dialog.
54
+ */
55
+ @property({ type: String, reflect: true }) size: ModalSize = 'md';
56
+
57
+ /** When true, hides the close button in the header. */
58
+ @property({ type: Boolean, reflect: true, attribute: 'hide-close' })
59
+ hideClose = false;
60
+
61
+ /** When true, renders a loading overlay inside the modal. */
62
+ @property({ type: Boolean, reflect: true, attribute: 'show-loader' })
63
+ showLoader = false;
64
+
65
+ /** When true, clicking the scrim will not close the modal. */
66
+ @property({ type: Boolean, attribute: 'no-scrim-close' }) noScrimClose = false;
67
+
68
+ @state() private _visible = false;
69
+
70
+ show() {
71
+ this.open = true;
72
+ }
73
+
74
+ hide() {
75
+ this._close('programmatic');
76
+ }
77
+
78
+ private _close(reason: string) {
79
+ if (!this.open) return;
80
+ this.open = false;
81
+ this.dispatchEvent(
82
+ new CustomEvent('modal-close', {
83
+ detail: { reason },
84
+ bubbles: true,
85
+ composed: true,
86
+ }),
87
+ );
88
+ }
89
+
90
+ private _handleScrimClick() {
91
+ if (!this.noScrimClose) {
92
+ this._close('scrim');
93
+ }
94
+ }
95
+
96
+ private _handleCloseClick() {
97
+ this._close('close-button');
98
+ }
99
+
100
+ private readonly _handleKeydown = (e: KeyboardEvent) => {
101
+ if (e.key === 'Escape') {
102
+ this._close('escape');
103
+ }
104
+ };
105
+
106
+ protected updated(changedProperties: Map<string, unknown>) {
107
+ if (changedProperties.has('open')) {
108
+ if (this.open) {
109
+ this._visible = true;
110
+ document.body.style.overflow = 'hidden';
111
+ document.addEventListener('keydown', this._handleKeydown);
112
+ } else {
113
+ document.body.style.overflow = '';
114
+ document.removeEventListener('keydown', this._handleKeydown);
115
+ }
116
+ }
117
+ }
118
+
119
+ disconnectedCallback() {
120
+ super.disconnectedCallback();
121
+ document.body.style.overflow = '';
122
+ document.removeEventListener('keydown', this._handleKeydown);
123
+ }
124
+
125
+ private _handleAnimationEnd(e: AnimationEvent) {
126
+ if (e.animationName === 'modal-exit') {
127
+ this._visible = false;
128
+ }
129
+ }
130
+
131
+ render() {
132
+ if (!this.open && !this._visible) return nothing;
133
+
134
+ const hasHeader = this.heading || this.subheading || !this.hideClose;
135
+
136
+ return html`
137
+ <div
138
+ class=${classMap({ scrim: true, visible: this.open })}
139
+ @click=${this._handleScrimClick}
140
+ aria-hidden="true"
141
+ ></div>
142
+
143
+ <div class="dialog-wrapper" role="presentation">
144
+ <div
145
+ class=${classMap({
146
+ dialog: true,
147
+ open: this.open,
148
+ [`size-${this.size}`]: true,
149
+ 'show-loader': this.showLoader,
150
+ })}
151
+ role="dialog"
152
+ aria-modal="true"
153
+ aria-labelledby=${this.heading ? 'modal-heading' : nothing}
154
+ @animationend=${this._handleAnimationEnd}
155
+ >
156
+ ${hasHeader
157
+ ? html`
158
+ <div class="dialog-header">
159
+ <div class="dialog-heading-section">
160
+ ${this.subheading
161
+ ? html`<p class="dialog-subheading">${this.subheading}</p>`
162
+ : nothing}
163
+ ${this.heading
164
+ ? html`<h2 id="modal-heading" class="dialog-heading">
165
+ ${this.heading}
166
+ </h2>`
167
+ : nothing}
168
+ </div>
169
+ ${!this.hideClose
170
+ ? html`<wc-icon-button
171
+ class="dialog-close"
172
+ variant="text"
173
+ aria-label="Close dialog"
174
+ @click=${this._handleCloseClick}
175
+ >
176
+ <wc-icon name="close"></wc-icon>
177
+ </wc-icon-button>`
178
+ : nothing}
179
+ </div>
180
+ `
181
+ : nothing}
182
+
183
+ <div class="dialog-content">
184
+ <slot></slot>
185
+ </div>
186
+
187
+ <div class="dialog-footer">
188
+ <slot name="footer"></slot>
189
+ </div>
190
+
191
+ ${this.showLoader
192
+ ? html`<div class="dialog-loader" aria-hidden="true">
193
+ <div class="dialog-loader-scrim"></div>
194
+ <wc-spinner></wc-spinner>
195
+ </div>`
196
+ : nothing}
197
+ </div>
198
+ </div>
199
+ `;
200
+ }
201
+ }
@@ -0,0 +1 @@
1
+ export { NotificationManager } from './notification-manager.js';