@redvars/peacock 3.5.1 → 3.6.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.
Files changed (225) 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/code-highlighter.js +1 -1
  34. package/dist/code-highlighter.js.map +1 -1
  35. package/dist/condition-builder.js +58 -0
  36. package/dist/condition-builder.js.map +1 -0
  37. package/dist/custom-elements-jsdocs.json +8479 -3965
  38. package/dist/custom-elements.json +15228 -7544
  39. package/dist/dropdown-button.js +216 -0
  40. package/dist/dropdown-button.js.map +1 -0
  41. package/dist/event-manager-D-QCmUgR.js +113 -0
  42. package/dist/event-manager-D-QCmUgR.js.map +1 -0
  43. package/dist/fab.js +1 -1
  44. package/dist/flow-designer-DvTUrDp5.js +1656 -0
  45. package/dist/flow-designer-DvTUrDp5.js.map +1 -0
  46. package/dist/flow-designer-node-BWrPuxAR.js +548 -0
  47. package/dist/flow-designer-node-BWrPuxAR.js.map +1 -0
  48. package/dist/flow-designer-node.js +4 -0
  49. package/dist/flow-designer-node.js.map +1 -0
  50. package/dist/flow-designer.js +16 -0
  51. package/dist/flow-designer.js.map +1 -0
  52. package/dist/html-editor.js +27516 -0
  53. package/dist/html-editor.js.map +1 -0
  54. package/dist/icon-button-CK1ZuE-2.js +247 -0
  55. package/dist/icon-button-CK1ZuE-2.js.map +1 -0
  56. package/dist/index.js +29 -6
  57. package/dist/index.js.map +1 -1
  58. package/dist/{is-dark-mode-DicqGkCJ.js → is-dark-mode-DOcaw4Yq.js} +2 -27
  59. package/dist/is-dark-mode-DOcaw4Yq.js.map +1 -0
  60. package/dist/modal.js +412 -0
  61. package/dist/modal.js.map +1 -0
  62. package/dist/{navigation-rail-Lxetd5-Z.js → navigation-rail-DTTkqohi.js} +1049 -2391
  63. package/dist/navigation-rail-DTTkqohi.js.map +1 -0
  64. package/dist/notification-manager.js +268 -0
  65. package/dist/notification-manager.js.map +1 -0
  66. package/dist/peacock-loader.js +93 -8
  67. package/dist/peacock-loader.js.map +1 -1
  68. package/dist/popover-NC7b1lTq.js +1971 -0
  69. package/dist/popover-NC7b1lTq.js.map +1 -0
  70. package/dist/popover-content.js +125 -0
  71. package/dist/popover-content.js.map +1 -0
  72. package/dist/popover.js +4 -0
  73. package/dist/popover.js.map +1 -0
  74. package/dist/split-button.js +388 -0
  75. package/dist/split-button.js.map +1 -0
  76. package/dist/src/__controllers/floating-controller.d.ts +35 -0
  77. package/dist/src/calendar/base-event.d.ts +10 -0
  78. package/dist/src/calendar/calendar-column-view.d.ts +41 -0
  79. package/dist/src/calendar/calendar-event.d.ts +7 -0
  80. package/dist/src/calendar/calendar-month-view.d.ts +31 -0
  81. package/dist/src/calendar/calendar.d.ts +65 -0
  82. package/dist/src/calendar/event-manager.d.ts +17 -0
  83. package/dist/src/calendar/index.d.ts +4 -0
  84. package/dist/src/calendar/types.d.ts +13 -0
  85. package/dist/src/calendar/utils.d.ts +31 -0
  86. package/dist/src/canvas/canvas.d.ts +92 -0
  87. package/dist/src/canvas/index.d.ts +2 -0
  88. package/dist/src/condition-builder/cb-compound-expression.d.ts +31 -0
  89. package/dist/src/condition-builder/cb-divider.d.ts +26 -0
  90. package/dist/src/condition-builder/cb-expression.d.ts +31 -0
  91. package/dist/src/condition-builder/cb-predicate.d.ts +30 -0
  92. package/dist/src/condition-builder/condition-builder.d.ts +27 -0
  93. package/dist/src/condition-builder/index.d.ts +5 -0
  94. package/dist/src/dropdown-button/dropdown-button.d.ts +68 -0
  95. package/dist/src/dropdown-button/index.d.ts +1 -0
  96. package/dist/src/flow-designer/commands.d.ts +66 -0
  97. package/dist/src/flow-designer/flow-designer-node.d.ts +46 -0
  98. package/dist/src/flow-designer/flow-designer.d.ts +133 -0
  99. package/dist/src/flow-designer/index.d.ts +7 -0
  100. package/dist/src/flow-designer/layout.d.ts +30 -0
  101. package/dist/src/flow-designer/types.d.ts +142 -0
  102. package/dist/src/flow-designer/validation.d.ts +43 -0
  103. package/dist/src/flow-designer/workflow-utils.d.ts +40 -0
  104. package/dist/src/html-editor/html-editor.d.ts +89 -0
  105. package/dist/src/html-editor/index.d.ts +2 -0
  106. package/dist/src/index.d.ts +15 -0
  107. package/dist/src/list/index.d.ts +2 -0
  108. package/dist/src/list/list-item.d.ts +35 -0
  109. package/dist/src/list/list.d.ts +28 -0
  110. package/dist/src/menu/menu/menu.d.ts +5 -7
  111. package/dist/src/menu/menu-item/menu-item.d.ts +14 -13
  112. package/dist/src/modal/index.d.ts +1 -0
  113. package/dist/src/modal/modal.d.ts +57 -0
  114. package/dist/src/navigation-rail/navigation-rail.d.ts +3 -7
  115. package/dist/src/notification-manager/index.d.ts +1 -0
  116. package/dist/src/notification-manager/notification-manager.d.ts +44 -0
  117. package/dist/src/number-field/number-field.d.ts +2 -2
  118. package/dist/src/popover/index.d.ts +2 -0
  119. package/dist/src/popover/popover-content.d.ts +29 -0
  120. package/dist/src/popover/popover.d.ts +62 -0
  121. package/dist/src/split-button/index.d.ts +1 -0
  122. package/dist/src/split-button/split-button.d.ts +72 -0
  123. package/dist/src/svg/index.d.ts +1 -0
  124. package/dist/src/svg/svg.d.ts +38 -0
  125. package/dist/src/toolbar/toolbar.d.ts +3 -3
  126. package/dist/src/tooltip/tooltip.d.ts +2 -15
  127. package/dist/test/flow-designer.test.d.ts +1 -0
  128. package/dist/toolbar.js +3 -3
  129. package/dist/toolbar.js.map +1 -1
  130. package/dist/tsconfig.tsbuildinfo +1 -1
  131. package/package.json +10 -2
  132. package/readme.md +3 -3
  133. package/src/__controllers/floating-controller.ts +237 -0
  134. package/src/banner/banner.scss +2 -3
  135. package/src/button/button/button.ts +1 -0
  136. package/src/calendar/base-event.ts +49 -0
  137. package/src/calendar/calendar-column-view.scss +326 -0
  138. package/src/calendar/calendar-column-view.ts +392 -0
  139. package/src/calendar/calendar-event.ts +20 -0
  140. package/src/calendar/calendar-month-view.scss +192 -0
  141. package/src/calendar/calendar-month-view.ts +244 -0
  142. package/src/calendar/calendar.scss +71 -0
  143. package/src/calendar/calendar.ts +298 -0
  144. package/src/calendar/event-manager.ts +117 -0
  145. package/src/calendar/index.ts +4 -0
  146. package/src/calendar/types.ts +14 -0
  147. package/src/calendar/utils.ts +180 -0
  148. package/src/canvas/canvas.scss +60 -0
  149. package/src/canvas/canvas.ts +391 -0
  150. package/src/canvas/index.ts +2 -0
  151. package/src/code-highlighter/code-highlighter.ts +1 -1
  152. package/src/condition-builder/cb-compound-expression.scss +37 -0
  153. package/src/condition-builder/cb-compound-expression.ts +80 -0
  154. package/src/condition-builder/cb-divider.scss +93 -0
  155. package/src/condition-builder/cb-divider.ts +56 -0
  156. package/src/condition-builder/cb-expression.scss +14 -0
  157. package/src/condition-builder/cb-expression.ts +49 -0
  158. package/src/condition-builder/cb-predicate.scss +35 -0
  159. package/src/condition-builder/cb-predicate.ts +102 -0
  160. package/src/condition-builder/condition-builder.scss +13 -0
  161. package/src/condition-builder/condition-builder.ts +38 -0
  162. package/src/condition-builder/index.ts +5 -0
  163. package/src/dropdown-button/demo/index.html +110 -0
  164. package/src/dropdown-button/dropdown-button.scss +22 -0
  165. package/src/dropdown-button/dropdown-button.ts +206 -0
  166. package/src/dropdown-button/index.ts +1 -0
  167. package/src/flow-designer/DEMO.md +239 -0
  168. package/src/flow-designer/commands.ts +278 -0
  169. package/src/flow-designer/flow-designer-node.ts +172 -0
  170. package/src/flow-designer/flow-designer.scss +457 -0
  171. package/src/flow-designer/flow-designer.ts +611 -0
  172. package/src/flow-designer/index.ts +41 -0
  173. package/src/flow-designer/layout.ts +357 -0
  174. package/src/flow-designer/types.ts +166 -0
  175. package/src/flow-designer/validation.ts +284 -0
  176. package/src/flow-designer/workflow-utils.ts +282 -0
  177. package/src/html-editor/html-editor.scss +188 -0
  178. package/src/html-editor/html-editor.ts +491 -0
  179. package/src/html-editor/index.ts +3 -0
  180. package/src/index.ts +27 -1
  181. package/src/list/index.ts +2 -0
  182. package/src/list/list-item.scss +111 -0
  183. package/src/list/list-item.ts +175 -0
  184. package/src/list/list.scss +24 -0
  185. package/src/list/list.ts +51 -0
  186. package/src/menu/menu/menu.scss +2 -2
  187. package/src/menu/menu/menu.ts +91 -101
  188. package/src/menu/menu-item/menu-item.scss +4 -0
  189. package/src/menu/menu-item/menu-item.ts +82 -78
  190. package/src/modal/index.ts +1 -0
  191. package/src/modal/modal.scss +206 -0
  192. package/src/modal/modal.ts +195 -0
  193. package/src/navigation-rail/navigation-rail-item.scss +7 -38
  194. package/src/navigation-rail/navigation-rail-item.ts +1 -2
  195. package/src/navigation-rail/navigation-rail.scss +17 -21
  196. package/src/navigation-rail/navigation-rail.ts +6 -9
  197. package/src/notification-manager/index.ts +1 -0
  198. package/src/notification-manager/notification-manager.scss +113 -0
  199. package/src/notification-manager/notification-manager.ts +199 -0
  200. package/src/number-field/number-field.ts +2 -2
  201. package/src/peacock-loader.ts +83 -0
  202. package/src/popover/index.ts +2 -0
  203. package/src/popover/popover-content.scss +69 -0
  204. package/src/popover/popover-content.ts +51 -0
  205. package/src/popover/popover.scss +7 -0
  206. package/src/popover/popover.ts +170 -0
  207. package/src/split-button/index.ts +1 -0
  208. package/src/split-button/split-button-colors.scss +56 -0
  209. package/src/split-button/split-button-sizes.scss +28 -0
  210. package/src/split-button/split-button.scss +79 -0
  211. package/src/split-button/split-button.ts +236 -0
  212. package/src/svg/index.ts +1 -0
  213. package/src/svg/svg.scss +91 -0
  214. package/src/svg/svg.ts +160 -0
  215. package/src/table/table.ts +2 -2
  216. package/src/toolbar/toolbar.ts +3 -3
  217. package/src/tooltip/tooltip.scss +4 -3
  218. package/src/tooltip/tooltip.ts +46 -104
  219. package/dist/button-DouvOfEU.js.map +0 -1
  220. package/dist/button-group-CEdMwvJJ.js +0 -464
  221. package/dist/button-group-CEdMwvJJ.js.map +0 -1
  222. package/dist/is-dark-mode-DicqGkCJ.js.map +0 -1
  223. package/dist/navigation-rail-Lxetd5-Z.js.map +0 -1
  224. package/dist/src/menu/menu/MenuSurfaceController.d.ts +0 -18
  225. package/src/menu/menu/MenuSurfaceController.ts +0 -61
@@ -3,8 +3,8 @@ import { property, query, state } from 'lit/decorators.js';
3
3
  import { classMap } from 'lit/directives/class-map.js';
4
4
  import type { Placement } from '@floating-ui/dom';
5
5
  import styles from './menu.scss';
6
+ import { FloatingController } from '../../__controllers/floating-controller.js';
6
7
  import { MenuItem } from '../menu-item/menu-item.js';
7
- import { MenuSurfaceController } from './MenuSurfaceController.js';
8
8
 
9
9
  type CloseReason =
10
10
  | { kind: 'click-selection' }
@@ -60,7 +60,18 @@ export class Menu extends LitElement {
60
60
 
61
61
  anchorElement: HTMLElement | null = null;
62
62
 
63
- private readonly _surfaceController = new MenuSurfaceController(this);
63
+ private readonly _floatingController = new FloatingController(this, {
64
+ trigger: 'manual',
65
+ closeOnClickOutside: false,
66
+ strategy: 'fixed',
67
+ onOpenChange: (isOpen) => {
68
+ if (isOpen || !this.open) {
69
+ return;
70
+ }
71
+
72
+ this.close({ kind: 'outside-click' });
73
+ },
74
+ });
64
75
 
65
76
  private _lastFocusedElement: HTMLElement | null = null;
66
77
 
@@ -72,22 +83,14 @@ export class Menu extends LitElement {
72
83
  this.setAttribute('role', 'menu');
73
84
 
74
85
  this.addEventListener('keydown', this._onKeyDown);
75
- this.addEventListener('focusout', this._onFocusOut);
76
- this.addEventListener('menu-item-activate', this._onItemActivate);
77
- this.addEventListener('menu-item-request-close', this._onItemRequestClose);
78
- window.addEventListener('click', this._onWindowClick, { capture: true });
86
+ this.addEventListener('click', this._onClick);
79
87
  this._syncAnchorAria();
80
88
  }
81
89
 
82
90
  disconnectedCallback() {
83
91
  this.removeEventListener('keydown', this._onKeyDown);
84
- this.removeEventListener('focusout', this._onFocusOut);
85
- this.removeEventListener('menu-item-activate', this._onItemActivate);
86
- this.removeEventListener(
87
- 'menu-item-request-close',
88
- this._onItemRequestClose,
89
- );
90
- window.removeEventListener('click', this._onWindowClick, { capture: true });
92
+ this.removeEventListener('click', this._onClick);
93
+ this._floatingController.close();
91
94
  super.disconnectedCallback();
92
95
  }
93
96
 
@@ -170,7 +173,12 @@ export class Menu extends LitElement {
170
173
  }
171
174
 
172
175
  private _syncRovingTabIndex() {
176
+ const ownedItems = this.items;
173
177
  const enabledItems = this._enabledItems();
178
+ for (const item of ownedItems) {
179
+ item.tabIndex = -1;
180
+ }
181
+
174
182
  if (!enabledItems.length) {
175
183
  this.activeIndex = -1;
176
184
  return;
@@ -180,10 +188,7 @@ export class Menu extends LitElement {
180
188
  this.activeIndex = 0;
181
189
  }
182
190
 
183
- for (let index = 0; index < enabledItems.length; index += 1) {
184
- const currentItem = enabledItems[index];
185
- currentItem.tabIndex = index === this.activeIndex ? 0 : -1;
186
- }
191
+ enabledItems[this.activeIndex].tabIndex = 0;
187
192
  }
188
193
 
189
194
  private _setActiveByOffset(offset: 1 | -1) {
@@ -227,50 +232,66 @@ export class Menu extends LitElement {
227
232
  return this._enabledItems()[0] ?? null;
228
233
  }
229
234
 
230
- private _ownsKeyboardEvent(event: KeyboardEvent) {
235
+ private _isEventFromThisMenu(event: Event) {
231
236
  const path = event.composedPath();
232
- const ownedItems = this.items;
237
+ const sourceMenu = path.find(
238
+ target =>
239
+ target instanceof HTMLElement &&
240
+ target.tagName.toLowerCase() === 'wc-menu',
241
+ );
233
242
 
234
- return path.some(target => target instanceof MenuItem && ownedItems.includes(target));
243
+ return sourceMenu === this;
235
244
  }
236
245
 
237
- private _onItemActivate = (event: Event) => {
238
- const customEvent = event as CustomEvent<{ item: MenuItem }>;
239
- const { item } = customEvent.detail;
246
+ private _ownedItemFromEvent(event: Event) {
247
+ if (!this._isEventFromThisMenu(event)) {
248
+ return null;
249
+ }
250
+
251
+ const path = event.composedPath();
240
252
  const ownedItems = this.items;
241
- if (!ownedItems.includes(item)) {
242
- return;
253
+
254
+ for (const target of path) {
255
+ if (target instanceof HTMLElement) {
256
+ if (target.tagName.toLowerCase() === 'wc-menu-item') {
257
+ const ownedItem = ownedItems.find(item => item === target);
258
+ if (ownedItem) {
259
+ return ownedItem;
260
+ }
261
+ }
262
+ }
243
263
  }
244
264
 
265
+ return null;
266
+ }
267
+
268
+ private _setActiveItem(item: MenuItem) {
245
269
  const enabledItems = this._enabledItems();
246
270
  const nextIndex = enabledItems.indexOf(item);
247
- if (nextIndex >= 0) {
248
- this.activeIndex = nextIndex;
249
- this._syncRovingTabIndex();
271
+ if (nextIndex < 0) {
272
+ return;
250
273
  }
251
- };
252
274
 
253
- private _onItemRequestClose = (event: Event) => {
254
- const customEvent = event as CustomEvent<{
255
- item: MenuItem;
256
- reason: 'click-selection' | 'keydown';
257
- key?: string;
258
- }>;
275
+ this.activeIndex = nextIndex;
276
+ this._syncRovingTabIndex();
277
+ }
259
278
 
260
- if (!this.items.includes(customEvent.detail.item)) {
279
+ private _onClick = (event: Event) => {
280
+ if (!this.open) {
261
281
  return;
262
282
  }
263
283
 
264
- if (customEvent.defaultPrevented) {
284
+ const item = this._ownedItemFromEvent(event);
285
+ if (!item) {
265
286
  return;
266
287
  }
267
288
 
268
- if (customEvent.detail.reason === 'click-selection') {
269
- this.close({ kind: 'click-selection' });
289
+ this._setActiveItem(item);
290
+ if (item.keepOpen) {
270
291
  return;
271
292
  }
272
293
 
273
- this.close({ kind: 'keydown', key: customEvent.detail.key ?? 'Enter' });
294
+ this.close({ kind: 'click-selection' });
274
295
  };
275
296
 
276
297
  private _onKeyDown = (event: KeyboardEvent) => {
@@ -278,10 +299,15 @@ export class Menu extends LitElement {
278
299
  return;
279
300
  }
280
301
 
281
- if (!this._ownsKeyboardEvent(event)) {
302
+ if (!this._isEventFromThisMenu(event)) {
282
303
  return;
283
304
  }
284
305
 
306
+ const eventItem = this._ownedItemFromEvent(event);
307
+ if (eventItem) {
308
+ this._setActiveItem(eventItem);
309
+ }
310
+
285
311
  switch (event.key) {
286
312
  case 'ArrowDown':
287
313
  event.preventDefault();
@@ -305,67 +331,27 @@ export class Menu extends LitElement {
305
331
  event.preventDefault();
306
332
  this.close({ kind: 'keydown', key: 'Escape' });
307
333
  break;
308
- default:
334
+ case 'Tab':
335
+ this.close({ kind: 'keydown', key: 'Tab' });
309
336
  break;
310
- }
311
- };
312
-
313
- private _onWindowClick = (event: MouseEvent) => {
314
- if (!this.open || this.stayOpenOnOutsideClick) {
315
- return;
316
- }
317
-
318
- const path = event.composedPath();
319
- const anchorEl = this._resolveAnchorElement();
320
- const inMenuTree = path.some(
321
- target => target === this || (target instanceof Node && this.contains(target)),
322
- );
323
-
324
- if (inMenuTree || (anchorEl && path.includes(anchorEl))) {
325
- return;
326
- }
327
-
328
- this.close({ kind: 'outside-click' });
329
- };
330
-
331
- private _isWithinMenuTree(node: Node | null) {
332
- if (!node) {
333
- return false;
334
- }
335
-
336
- let current: Node | null = node;
337
- while (current) {
338
- if (current === this || this.contains(current)) {
339
- return true;
340
- }
337
+ case 'Enter':
338
+ case ' ': {
339
+ event.preventDefault();
340
+ const activeItem = this._getActiveItem() ?? this._getFirstEnabledItem();
341
+ if (!activeItem) {
342
+ return;
343
+ }
341
344
 
342
- const root = current.getRootNode();
343
- if (root instanceof ShadowRoot) {
344
- current = root.host;
345
- } else {
346
- current = null;
345
+ this._setActiveItem(activeItem);
346
+ activeItem.click();
347
+ break;
347
348
  }
349
+ default:
350
+ break;
348
351
  }
352
+ };
349
353
 
350
- return false;
351
- }
352
-
353
- private _onFocusOut = (event: FocusEvent) => {
354
- if (!this.open || this.stayOpenOnFocusout) {
355
- return;
356
- }
357
-
358
- const next = event.relatedTarget;
359
- if (!next) {
360
- return;
361
- }
362
-
363
- if (next instanceof Node && this._isWithinMenuTree(next)) {
364
- return;
365
- }
366
354
 
367
- this.close({ kind: 'focusout' });
368
- };
369
355
 
370
356
  private _onSlotChange = () => {
371
357
  this._syncRovingTabIndex();
@@ -381,13 +367,14 @@ export class Menu extends LitElement {
381
367
  return;
382
368
  }
383
369
 
384
- this._surfaceController.start({
385
- reference: anchorEl,
386
- floating: this.menuListElement,
370
+ this._floatingController.setOptions({
387
371
  placement: this.placement,
388
372
  offset: this.offset,
389
373
  strategy: 'fixed',
374
+ closeOnClickOutside: !this.stayOpenOnOutsideClick,
390
375
  });
376
+ this._floatingController.setElements(anchorEl, this.menuListElement);
377
+ this._floatingController.open();
391
378
  }
392
379
 
393
380
  protected override updated(changedProperties: Map<string, unknown>) {
@@ -408,7 +395,7 @@ export class Menu extends LitElement {
408
395
 
409
396
  this._applyPositioning();
410
397
  } else {
411
- this._surfaceController.stop();
398
+ this._floatingController.close();
412
399
 
413
400
  const reason = this._closeReason;
414
401
  this.dispatchEvent(
@@ -429,7 +416,10 @@ export class Menu extends LitElement {
429
416
  }),
430
417
  );
431
418
 
432
- if (!this.isSubmenu) {
419
+ const shouldRestoreFocus =
420
+ reason.kind !== 'keydown' || reason.key !== 'Tab';
421
+
422
+ if (!this.isSubmenu && shouldRestoreFocus) {
433
423
  this._lastFocusedElement?.focus();
434
424
  }
435
425
  }
@@ -92,6 +92,10 @@
92
92
  --_container-state-opacity: 0.08;
93
93
  }
94
94
 
95
+ &.pressed:not(:where(.disabled)) {
96
+ --_container-state-opacity: 0.12;
97
+ }
98
+
95
99
  &.selected {
96
100
  --_container-color: var(--menu-item-container-selected-color);
97
101
  --_label-text-color: var(--menu-item-label-selected-color);
@@ -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';