@tylertech/forge 3.10.5 → 3.12.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 (257) hide show
  1. package/custom-elements.json +594 -226
  2. package/dist/app-bar/forge-app-bar.css +13 -5
  3. package/dist/button/forge-button.css +14 -6
  4. package/dist/checkbox/forge-checkbox.css +23 -17
  5. package/dist/chips/forge-chips.css +12 -4
  6. package/dist/field/forge-field.css +7 -5
  7. package/dist/floating-action-button/forge-floating-action-button.css +12 -6
  8. package/dist/forge.css +6 -1
  9. package/dist/icon-button/forge-icon-button.css +12 -4
  10. package/dist/lib.js +22 -22
  11. package/dist/lib.js.map +4 -4
  12. package/dist/list/forge-list.css +59 -3
  13. package/dist/radio/forge-radio.css +22 -12
  14. package/dist/skip-link/forge-skip-link.css +38 -32
  15. package/dist/state-layer/forge-state-layer.css +2 -0
  16. package/dist/switch/forge-switch.css +9 -5
  17. package/dist/table/forge-table.css +6 -1
  18. package/dist/vscode.html-custom-data.json +104 -79
  19. package/esm/accordion/accordion.d.ts +2 -0
  20. package/esm/accordion/accordion.js +2 -0
  21. package/esm/app-bar/app-bar/app-bar-adapter.js +2 -2
  22. package/esm/app-bar/app-bar/app-bar.d.ts +1 -1
  23. package/esm/app-bar/app-bar/app-bar.js +1 -1
  24. package/esm/app-bar/help-button/app-bar-help-button.d.ts +1 -1
  25. package/esm/app-bar/help-button/app-bar-help-button.js +1 -1
  26. package/esm/app-bar/menu-button/app-bar-menu-button.d.ts +1 -1
  27. package/esm/app-bar/menu-button/app-bar-menu-button.js +1 -1
  28. package/esm/app-bar/notification-button/app-bar-notification-button.d.ts +2 -0
  29. package/esm/app-bar/notification-button/app-bar-notification-button.js +2 -0
  30. package/esm/app-bar/profile-button/app-bar-profile-button-adapter.d.ts +2 -1
  31. package/esm/app-bar/profile-button/app-bar-profile-button-adapter.js +2 -0
  32. package/esm/app-bar/profile-button/app-bar-profile-button.d.ts +2 -0
  33. package/esm/app-bar/profile-button/app-bar-profile-button.js +2 -0
  34. package/esm/app-bar/search/app-bar-search.d.ts +2 -0
  35. package/esm/app-bar/search/app-bar-search.js +2 -0
  36. package/esm/autocomplete/autocomplete-core.js +16 -3
  37. package/esm/autocomplete/autocomplete.d.ts +2 -0
  38. package/esm/autocomplete/autocomplete.js +2 -0
  39. package/esm/avatar/avatar.d.ts +1 -1
  40. package/esm/avatar/avatar.js +1 -1
  41. package/esm/backdrop/backdrop.d.ts +2 -0
  42. package/esm/backdrop/backdrop.js +2 -0
  43. package/esm/badge/badge.d.ts +2 -0
  44. package/esm/badge/badge.js +2 -0
  45. package/esm/banner/banner.d.ts +1 -1
  46. package/esm/banner/banner.js +1 -1
  47. package/esm/button/base/base-button-adapter.js +2 -2
  48. package/esm/button/button.d.ts +1 -6
  49. package/esm/button/button.js +1 -6
  50. package/esm/button-area/button-area-adapter.js +2 -2
  51. package/esm/button-area/button-area.d.ts +1 -4
  52. package/esm/button-area/button-area.js +1 -4
  53. package/esm/button-toggle/button-toggle/button-toggle-adapter.js +2 -2
  54. package/esm/button-toggle/button-toggle/button-toggle.d.ts +2 -0
  55. package/esm/button-toggle/button-toggle/button-toggle.js +2 -0
  56. package/esm/button-toggle/button-toggle-group/button-toggle-group.d.ts +1 -1
  57. package/esm/button-toggle/button-toggle-group/button-toggle-group.js +1 -1
  58. package/esm/calendar/calendar-menu/calendar-menu.js +1 -1
  59. package/esm/calendar/calendar.d.ts +2 -0
  60. package/esm/calendar/calendar.js +3 -1
  61. package/esm/card/card.d.ts +2 -0
  62. package/esm/card/card.js +2 -0
  63. package/esm/chip-field/chip-field.d.ts +2 -0
  64. package/esm/chip-field/chip-field.js +2 -0
  65. package/esm/chips/chip/chip-adapter.d.ts +3 -0
  66. package/esm/chips/chip/chip-adapter.js +14 -4
  67. package/esm/chips/chip/chip-constants.d.ts +2 -0
  68. package/esm/chips/chip/chip-constants.js +2 -1
  69. package/esm/chips/chip/chip-core.d.ts +4 -0
  70. package/esm/chips/chip/chip-core.js +9 -0
  71. package/esm/chips/chip/chip.d.ts +6 -0
  72. package/esm/chips/chip/chip.js +10 -0
  73. package/esm/chips/chip-set/chip-set.d.ts +1 -1
  74. package/esm/chips/chip-set/chip-set.js +1 -1
  75. package/esm/circular-progress/circular-progress.d.ts +1 -2
  76. package/esm/circular-progress/circular-progress.js +1 -2
  77. package/esm/color-picker/color-picker.d.ts +2 -0
  78. package/esm/color-picker/color-picker.js +3 -1
  79. package/esm/core/utils/a11y-utils.js +17 -0
  80. package/esm/core/utils/utils.js +8 -2
  81. package/esm/date-picker/date-picker.d.ts +2 -0
  82. package/esm/date-picker/date-picker.js +2 -0
  83. package/esm/date-range-picker/date-range-picker.d.ts +2 -0
  84. package/esm/date-range-picker/date-range-picker.js +2 -0
  85. package/esm/deprecated/button/deprecated-button.js +3 -3
  86. package/esm/deprecated/icon-button/deprecated-icon-button.js +2 -2
  87. package/esm/dialog/dialog.d.ts +1 -1
  88. package/esm/dialog/dialog.js +1 -1
  89. package/esm/divider/divider.d.ts +1 -1
  90. package/esm/divider/divider.js +1 -1
  91. package/esm/drawer/base/base-drawer-adapter.d.ts +2 -0
  92. package/esm/drawer/base/base-drawer-adapter.js +3 -0
  93. package/esm/drawer/base/base-drawer-core.js +3 -0
  94. package/esm/drawer/drawer/drawer.d.ts +2 -0
  95. package/esm/drawer/drawer/drawer.js +3 -1
  96. package/esm/drawer/mini-drawer/mini-drawer.d.ts +2 -0
  97. package/esm/drawer/mini-drawer/mini-drawer.js +3 -1
  98. package/esm/drawer/modal-drawer/modal-drawer.d.ts +2 -0
  99. package/esm/drawer/modal-drawer/modal-drawer.js +3 -1
  100. package/esm/expansion-panel/expansion-panel.d.ts +1 -1
  101. package/esm/expansion-panel/expansion-panel.js +1 -1
  102. package/esm/field/field-adapter.js +2 -2
  103. package/esm/field/field-core.d.ts +3 -3
  104. package/esm/field/field.d.ts +1 -2
  105. package/esm/field/field.js +1 -2
  106. package/esm/file-picker/file-picker.d.ts +2 -3
  107. package/esm/file-picker/file-picker.js +2 -3
  108. package/esm/floating-action-button/floating-action-button.d.ts +1 -1
  109. package/esm/floating-action-button/floating-action-button.js +1 -1
  110. package/esm/focus-indicator/focus-indicator-constants.d.ts +1 -11
  111. package/esm/focus-indicator/focus-indicator-constants.js +2 -17
  112. package/esm/focus-indicator/focus-indicator.d.ts +53 -26
  113. package/esm/focus-indicator/focus-indicator.js +139 -62
  114. package/esm/focus-indicator/index.d.ts +0 -2
  115. package/esm/focus-indicator/index.js +0 -2
  116. package/esm/icon/icon.d.ts +1 -1
  117. package/esm/icon/icon.js +1 -1
  118. package/esm/icon-button/icon-button.d.ts +2 -0
  119. package/esm/icon-button/icon-button.js +2 -0
  120. package/esm/inline-message/inline-message.d.ts +1 -1
  121. package/esm/inline-message/inline-message.js +1 -1
  122. package/esm/keyboard-shortcut/keyboard-shortcut.d.ts +2 -0
  123. package/esm/keyboard-shortcut/keyboard-shortcut.js +2 -0
  124. package/esm/label/label.d.ts +1 -1
  125. package/esm/label/label.js +1 -1
  126. package/esm/label-value/label-value.d.ts +1 -1
  127. package/esm/label-value/label-value.js +1 -1
  128. package/esm/linear-progress/linear-progress.d.ts +1 -2
  129. package/esm/linear-progress/linear-progress.js +1 -2
  130. package/esm/list/list/list.d.ts +1 -1
  131. package/esm/list/list/list.js +2 -2
  132. package/esm/list/list-item/list-item-constants.js +1 -1
  133. package/esm/list/list-item/list-item-core.d.ts +2 -0
  134. package/esm/list/list-item/list-item-core.js +29 -6
  135. package/esm/list/list-item/list-item.d.ts +1 -0
  136. package/esm/list/list-item/list-item.js +2 -1
  137. package/esm/list-dropdown/list-dropdown-constants.d.ts +11 -1
  138. package/esm/list-dropdown/list-dropdown-constants.js +6 -1
  139. package/esm/list-dropdown/list-dropdown-utils.d.ts +3 -1
  140. package/esm/list-dropdown/list-dropdown-utils.js +69 -21
  141. package/esm/menu/menu-adapter.d.ts +2 -0
  142. package/esm/menu/menu-adapter.js +12 -8
  143. package/esm/menu/menu-constants.d.ts +1 -0
  144. package/esm/menu/menu-constants.js +3 -2
  145. package/esm/menu/menu-core.d.ts +5 -0
  146. package/esm/menu/menu-core.js +41 -2
  147. package/esm/menu/menu.d.ts +17 -0
  148. package/esm/menu/menu.js +15 -1
  149. package/esm/open-icon/open-icon.d.ts +2 -1
  150. package/esm/open-icon/open-icon.js +2 -1
  151. package/esm/overlay/overlay.d.ts +1 -2
  152. package/esm/overlay/overlay.js +1 -2
  153. package/esm/page-state/page-state.d.ts +2 -1
  154. package/esm/page-state/page-state.js +2 -1
  155. package/esm/paginator/paginator-core.d.ts +16 -0
  156. package/esm/paginator/paginator-core.js +29 -9
  157. package/esm/paginator/paginator.d.ts +38 -0
  158. package/esm/paginator/paginator.js +46 -0
  159. package/esm/popover/popover-adapter.js +1 -1
  160. package/esm/popover/popover-constants.d.ts +4 -0
  161. package/esm/popover/popover-constants.js +4 -2
  162. package/esm/popover/popover-core.d.ts +5 -1
  163. package/esm/popover/popover-core.js +13 -0
  164. package/esm/popover/popover.d.ts +6 -2
  165. package/esm/popover/popover.js +10 -2
  166. package/esm/profile-card/profile-card.d.ts +3 -0
  167. package/esm/profile-card/profile-card.js +3 -0
  168. package/esm/radio/radio/radio.d.ts +1 -2
  169. package/esm/radio/radio/radio.js +1 -2
  170. package/esm/scaffold/scaffold.d.ts +1 -1
  171. package/esm/scaffold/scaffold.js +1 -1
  172. package/esm/select/core/base-select-constants.d.ts +4 -0
  173. package/esm/select/core/base-select-core.d.ts +22 -2
  174. package/esm/select/core/base-select-core.js +217 -40
  175. package/esm/select/option/option.d.ts +2 -0
  176. package/esm/select/option/option.js +2 -0
  177. package/esm/select/option-group/option-group.d.ts +2 -0
  178. package/esm/select/option-group/option-group.js +2 -0
  179. package/esm/select/select/select-constants.d.ts +5 -0
  180. package/esm/select/select/select-constants.js +5 -2
  181. package/esm/select/select/select.d.ts +14 -2
  182. package/esm/select/select/select.js +23 -2
  183. package/esm/select/select-dropdown/select-dropdown.d.ts +2 -0
  184. package/esm/select/select-dropdown/select-dropdown.js +2 -0
  185. package/esm/skeleton/skeleton.d.ts +1 -1
  186. package/esm/skeleton/skeleton.js +1 -1
  187. package/esm/skip-link/skip-link.d.ts +1 -1
  188. package/esm/skip-link/skip-link.js +1 -1
  189. package/esm/slider/slider.d.ts +1 -1
  190. package/esm/slider/slider.js +1 -1
  191. package/esm/split-button/split-button.d.ts +1 -1
  192. package/esm/split-button/split-button.js +2 -2
  193. package/esm/split-view/split-view/split-view.d.ts +2 -0
  194. package/esm/split-view/split-view/split-view.js +2 -0
  195. package/esm/split-view/split-view-panel/split-view-panel.d.ts +2 -0
  196. package/esm/split-view/split-view-panel/split-view-panel.js +3 -1
  197. package/esm/stack/stack.d.ts +1 -8
  198. package/esm/stack/stack.js +1 -8
  199. package/esm/state-layer/state-layer.d.ts +1 -1
  200. package/esm/state-layer/state-layer.js +2 -2
  201. package/esm/stepper/step/step.d.ts +2 -0
  202. package/esm/stepper/step/step.js +3 -1
  203. package/esm/stepper/stepper/stepper.d.ts +2 -0
  204. package/esm/stepper/stepper/stepper.js +2 -0
  205. package/esm/table/table-adapter.d.ts +4 -4
  206. package/esm/table/table-adapter.js +4 -4
  207. package/esm/table/table-core.js +2 -2
  208. package/esm/table/table-utils.d.ts +2 -2
  209. package/esm/table/table-utils.js +25 -18
  210. package/esm/table/table.d.ts +3 -1
  211. package/esm/table/table.js +4 -1
  212. package/esm/tabs/tab/tab-adapter.js +2 -2
  213. package/esm/tabs/tab/tab.d.ts +2 -0
  214. package/esm/tabs/tab/tab.js +2 -0
  215. package/esm/tabs/tab-bar/tab-bar.d.ts +1 -1
  216. package/esm/tabs/tab-bar/tab-bar.js +1 -1
  217. package/esm/text-field/text-field-adapter.d.ts +6 -4
  218. package/esm/text-field/text-field-adapter.js +11 -4
  219. package/esm/text-field/text-field-core.d.ts +4 -0
  220. package/esm/text-field/text-field-core.js +13 -2
  221. package/esm/text-field/text-field.d.ts +1 -1
  222. package/esm/text-field/text-field.js +1 -1
  223. package/esm/time-picker/time-picker-adapter.js +1 -0
  224. package/esm/time-picker/time-picker-core.js +3 -3
  225. package/esm/time-picker/time-picker.d.ts +2 -0
  226. package/esm/time-picker/time-picker.js +2 -0
  227. package/esm/toast/toast-adapter.d.ts +20 -0
  228. package/esm/toast/toast-adapter.js +30 -0
  229. package/esm/toast/toast-core.d.ts +17 -0
  230. package/esm/toast/toast-core.js +66 -0
  231. package/esm/toast/toast.d.ts +9 -2
  232. package/esm/toast/toast.js +10 -1
  233. package/esm/toolbar/toolbar.d.ts +1 -3
  234. package/esm/toolbar/toolbar.js +1 -3
  235. package/esm/tooltip/tooltip-adapter.d.ts +6 -0
  236. package/esm/tooltip/tooltip-adapter.js +9 -0
  237. package/esm/tooltip/tooltip-constants.d.ts +1 -0
  238. package/esm/tooltip/tooltip-constants.js +2 -1
  239. package/esm/tooltip/tooltip-core.d.ts +20 -0
  240. package/esm/tooltip/tooltip-core.js +96 -2
  241. package/esm/tooltip/tooltip.d.ts +1 -1
  242. package/esm/tooltip/tooltip.js +2 -2
  243. package/esm/view-switcher/view/view.d.ts +2 -0
  244. package/esm/view-switcher/view/view.js +2 -0
  245. package/esm/view-switcher/view-switcher.d.ts +2 -0
  246. package/esm/view-switcher/view-switcher.js +2 -0
  247. package/package.json +4 -4
  248. package/sass/focus-indicator/focus-indicator.scss +1 -1
  249. package/sass/icon-button/forge-icon-button.scss +3 -3
  250. package/sass/list/forge-list.scss +6 -6
  251. package/sass/state-layer/_core.scss +2 -0
  252. package/sass/table/_core.scss +13 -1
  253. package/sass/tooltip/_core.scss +0 -2
  254. package/esm/focus-indicator/focus-indicator-adapter.d.ts +0 -29
  255. package/esm/focus-indicator/focus-indicator-adapter.js +0 -37
  256. package/esm/focus-indicator/focus-indicator-core.d.ts +0 -48
  257. package/esm/focus-indicator/focus-indicator-core.js +0 -129
@@ -64,6 +64,7 @@ declare const ListItemComponent_base: import("../..").AbstractConstructor<import
64
64
  * @slot tertiary-text - The tertiary text.
65
65
  * @slot start - The start content.
66
66
  * @slot end - The end element.
67
+ * @slot additional-content - Additional content that appears below the main list item interactive area.
67
68
  *
68
69
  * @csspart root - The root container element.
69
70
  * @csspart text-container - The container for the text content.
@@ -13,7 +13,7 @@ import { FocusIndicatorComponent } from '../../focus-indicator';
13
13
  import { WithElementInternals } from '../../core/mixins/internals/with-element-internals';
14
14
  import { WithDefaultAria } from '../../core/mixins/internals/with-default-aria';
15
15
  import { BaseComponent } from '../../core/base/base-component';
16
- const template = '<template><div class=\"forge-list-item\" part=\"root\"><slot name=\"start\"><slot name=\"leading\"></slot></slot><div class=\"text-container\" part=\"text-container\"><slot></slot><slot name=\"secondary-text\"></slot><slot name=\"tertiary-text\"></slot></div><slot name=\"end\"><slot name=\"trailing\"></slot></slot></div></template>';
16
+ const template = '<template><div class=\"forge-list-item\" part=\"root\"><slot name=\"start\"><slot name=\"leading\"></slot></slot><div class=\"text-container\" part=\"text-container\"><slot></slot><slot name=\"secondary-text\"></slot><slot name=\"tertiary-text\"></slot></div><slot name=\"end\"><slot name=\"trailing\"></slot></slot></div><slot name=\"additional-content\"></slot></template>';
17
17
  const styles = ':host{--_list-item-indent:var(--forge-list-item-indent, var(--forge-spacing-xxlarge, 48px));--_list-item-dense-indent:var(--forge-list-item-dense-indent, var(--forge-spacing-xxlarge, 48px));--_list-item-disabled-cursor:var(--forge-list-item-disabled-cursor, not-allowed)}:host{display:block;outline:0}:host([hidden]){display:none}.forge-list-item{--_list-item-background:var(--forge-list-item-background, transparent);--_list-item-shape:var(--forge-list-item-shape, 0);--_list-item-padding:var(--forge-list-item-padding, 0 var(--forge-spacing-medium, 16px));--_list-item-margin:var(--forge-list-item-margin, 0);--_list-item-height:var(--forge-list-item-height, 48px);--_list-item-dense-height:var(--forge-list-item-dense-height, 32px);--_list-item-cursor:var(--forge-list-item-cursor, pointer);--_list-item-gap:var(--forge-list-item-gap, var(--forge-spacing-large, 24px));--_list-item-text-color:var(--forge-list-item-text-color, var(--forge-theme-text-medium, rgba(0, 0, 0, 0.6)));--_list-item-text-font-size:var(--forge-list-item-text-font-size, var(--forge-typography-body2-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 1))));--_list-item-text-font-weight:var(--forge-list-item-text-font-weight, var(--forge-typography-body2-font-weight, 400));--_list-item-text-line-height:var(--forge-list-item-text-line-height, 1.5rem);--_list-item-selected-color:var(--forge-list-item-selected-color, var(--forge-theme-primary, #3f51b5));--_list-item-selected-background:var(--forge-list-item-selected-background, var(--_list-item-selected-color));--_list-item-selected-opacity:var(--forge-list-item-selected-opacity, 0.08);--_list-item-selected-start-color:var(--forge-list-item-selected-start-color, var(--_list-item-selected-color));--_list-item-selected-end-color:var(--forge-list-item-selected-end-color, var(--_list-item-selected-color));--_list-item-selected-text-color:var(--forge-list-item-selected-text-color, var(--forge-theme-text-medium, rgba(0, 0, 0, 0.6)));--_list-item-disabled-opacity:var(--forge-list-item-disabled-opacity, 0.38);--_list-item-one-line-height:var(--forge-list-item-one-line-height, var(--_list-item-height));--_list-item-two-line-height:var(--forge-list-item-two-line-height, var(--forge-list-item-height, 72px));--_list-item-three-line-height:var(--forge-list-item-three-line-height, var(--forge-list-item-height, 88px));--_list-item-dense-one-line-height:var(--forge-list-item-dense-one-line-height, var(--_list-item-dense-height));--_list-item-dense-two-line-height:var(--forge-list-item-dense-two-line-height, var(--forge-list-item-dense-height, 56px));--_list-item-dense-three-line-height:var(--forge-list-item-dense-three-line-height, var(--forge-list-item-dense-height, 72px));--_list-item-dense-font-size:var(--forge-list-item-dense-font-size, 0.875rem);--_list-item-dense-gap:var(--forge-list-item-dense-gap, var(--forge-spacing-xsmall, 8px));--_list-item-start-selected-color:var(--forge-list-item-start-selected-color, var(--_list-item-selected-color));--_list-item-end-selected-color:var(--forge-list-item-end-selected-color, var(--_list-item-selected-color));--_list-item-wrap-padding:var(--forge-list-item-wrap-padding, var(--forge-spacing-xsmall, 8px) var(--forge-spacing-medium, 16px))}.forge-list-item{position:relative;display:flex;gap:var(--_list-item-gap);align-items:center;box-sizing:border-box;outline:0;text-decoration:none;border-radius:var(--_list-item-shape);-webkit-tap-highlight-color:transparent;background-color:var(--_list-item-background);height:var(--_list-item-height);min-height:var(--_list-item-height);padding:var(--_list-item-padding);margin:var(--_list-item-margin)}.forge-list-item.interactive{cursor:var(--_list-item-cursor)}.forge-list-item.disabled{cursor:var(--_list-item-disabled-cursor);opacity:var(--_list-item-disabled-opacity)}.forge-list-item.disabled ::slotted(button){cursor:var(--_list-item-disabled-cursor)}.anchor{position:absolute;inset:0}:host([two-line]) .forge-list-item{height:var(--_list-item-two-line-height);min-height:var(--_list-item-two-line-height)}:host([three-line]) .forge-list-item{height:var(--_list-item-three-line-height);min-height:var(--_list-item-three-line-height)}:host([dense]) .forge-list-item{--_list-item-gap:var(--_list-item-dense-gap);height:var(--_list-item-dense-one-line-height);min-height:var(--_list-item-dense-one-line-height)}:host([dense]) .text-container{font-size:var(--_list-item-dense-font-size)}:host([dense][indented]){margin-inline-start:var(--_list-item-dense-indent)}:host([dense][two-line]) .forge-list-item{height:var(--_list-item-dense-two-line-height);min-height:var(--_list-item-dense-two-line-height)}:host([dense][three-line]) .forge-list-item{height:var(--_list-item-dense-three-line-height);min-height:var(--_list-item-dense-three-line-height)}:host([selected]) .forge-list-item{color:var(--_list-item-selected-color)}:host([selected]) .forge-list-item::before{content:\"\";position:absolute;inset:0;border-radius:inherit;opacity:var(--_list-item-selected-opacity);background-color:var(--_list-item-selected-background);pointer-events:none}:host([selected]) .forge-list-item ::slotted([slot=end]),:host([selected]) .forge-list-item ::slotted([slot=leading]),:host([selected]) .forge-list-item ::slotted([slot=start]),:host([selected]) .forge-list-item ::slotted([slot=trailing]){color:var(--_list-item-selected-color);isolation:isolate}:host([selected]) .forge-list-item ::slotted([slot=leading]),:host([selected]) .forge-list-item ::slotted([slot=start]){color:var(--_list-item-start-selected-color)}:host([selected]) .forge-list-item ::slotted([slot=end]),:host([selected]) .forge-list-item ::slotted([slot=trailing]){color:var(--_list-item-end-selected-color)}:host([selected]) .text-container{color:var(--_list-item-selected-color)}:host([selected]) forge-state-layer{--forge-state-layer-color:var(--_list-item-selected-color)}:host([indented]) .forge-list-item{margin-inline-start:var(--_list-item-indent)}.text-container{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:var(--forge-typography-body2-font-family, var(--forge-typography-font-family, \"Roboto\", sans-serif));letter-spacing:var(--forge-typography-body2-letter-spacing, .015625em);text-transform:var(--forge-typography-body2-text-transform,inherit);text-decoration:var(--forge-typography-body2-text-decoration,inherit);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;box-sizing:border-box;isolation:isolate;font-size:var(--_list-item-text-font-size);font-weight:var(--_list-item-text-font-weight);line-height:var(--_list-item-text-line-height);flex:1;contain:layout}slot[name=secondary-text]::slotted(*),slot[name=tertiary-text]::slotted(*){-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:var(--forge-typography-body1-font-family, var(--forge-typography-font-family, \"Roboto\", sans-serif));font-size:var(--forge-typography-body1-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, .875)));font-weight:var(--forge-typography-body1-font-weight,400);line-height:var(--forge-typography-body1-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.125)));letter-spacing:var(--forge-typography-body1-letter-spacing, .0357142857em);text-transform:var(--forge-typography-body1-text-transform,inherit);text-decoration:var(--forge-typography-body1-text-decoration,inherit);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--_list-item-text-color);display:block}:host([selected]) slot[name=secondary-text]::slotted(*),:host([selected]) slot[name=tertiary-text]::slotted(*){color:var(--_list-item-selected-text-color)}::slotted(:is(button:not([slot]),[forge-list-item-button])){appearance:none;cursor:var(--_list-item-cursor);border:none;padding-block:0;padding-inline:0;margin:0;box-sizing:border-box;width:100%;background:0 0;color:inherit;outline:0;font:inherit;user-select:auto;text-align:inherit;letter-spacing:inherit;word-spacing:inherit;white-space:nowrap;text-overflow:ellipsis;overflow:hidden;display:block}::slotted(a){outline:0;color:inherit!important;text-decoration:none!important}::slotted([slot=end]),::slotted([slot=leading]),::slotted([slot=start]),::slotted([slot=trailing]){color:var(--_list-item-text-color);display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;fill:currentColor}:host(:not([noninteractive])) ::slotted(:is(forge-checkbox,forge-radio,forge-switch):is([slot=start],[slot=end],[slot=leading],[slot=trailing]):not([forge-ignore])){--forge-focus-indicator-display:none;--forge-state-layer-display:none}:host([selected]) ::slotted([slot=leading]),:host([selected]) ::slotted([slot=start]){color:var(--_list-item-start-selected-color)}:host([selected]) ::slotted([slot=end]),:host([selected]) ::slotted([slot=trailing]){color:var(--_list-item-end-selected-color)}:host([wrap]) .forge-list-item{--_list-item-padding:var(--_list-item-wrap-padding);height:auto}:host([wrap]) .text-container{white-space:normal;overflow:visible;text-overflow:clip;line-height:normal}:host([wrap]) ::slotted(:is(button,[role=button][tabindex],[forge-list-item-button])){white-space:normal}:host([wrap]) slot[name=secondary-text]::slotted(*),:host([wrap]) slot[name=tertiary-text]::slotted(*){white-space:normal;overflow:visible;text-overflow:clip;line-height:normal}forge-focus-indicator{z-index:1;--forge-focus-indicator-shape:calc(var(--forge-shape-medium, 4px) * var(--forge-shape-factor, 1))}forge-state-layer{border-radius:inherit}';
18
18
  /**
19
19
  * @tag forge-list-item
@@ -49,6 +49,7 @@ const styles = ':host{--_list-item-indent:var(--forge-list-item-indent, var(--fo
49
49
  * @slot tertiary-text - The tertiary text.
50
50
  * @slot start - The start content.
51
51
  * @slot end - The end element.
52
+ * @slot additional-content - Additional content that appears below the main list item interactive area.
52
53
  *
53
54
  * @csspart root - The root container element.
54
55
  * @csspart text-container - The container for the text content.
@@ -7,6 +7,7 @@ import type { IIconComponent } from '../icon';
7
7
  import type { IOverlayOffset, OverlayFlipState, OverlayShiftState } from '../overlay/overlay-constants';
8
8
  import { PositionPlacement } from '../core/utils/position-utils';
9
9
  import { TooltipPlacement, TooltipType } from '../tooltip';
10
+ import type { PopoverAnchorAccessibility } from '../popover/popover-constants';
10
11
  export declare const LIST_DROPDOWN_CONSTANTS: {
11
12
  observedAttributes: {
12
13
  POPUP_CLASSES: string;
@@ -39,12 +40,16 @@ export declare const LIST_DROPDOWN_CONSTANTS: {
39
40
  classes: {
40
41
  GROUP_WRAPPER: string;
41
42
  };
43
+ selectAllOption: {
44
+ VALUE: string;
45
+ LABEL: string;
46
+ };
42
47
  };
43
48
  export type ListDropdownOptionBuilder<T = HTMLElement> = (option: IListDropdownOption, parentElement: T) => HTMLElement | string | void;
44
49
  export type ListDropdownHeaderBuilder = () => HTMLElement;
45
50
  export type ListDropdownFooterBuilder = () => HTMLElement;
46
51
  export type ListDropdownOptionGroupBuilder<T = any> = (option: IListDropdownOptionGroup<T>) => HTMLElement | string;
47
- export type ListDropdownTransformCallback = (label: string) => string | HTMLElement;
52
+ export type ListDropdownTransformCallback = (label: string, option?: IListDropdownOption, isSelected?: boolean) => string | HTMLElement;
48
53
  export type ListDropdownIconType = 'font' | 'component';
49
54
  export interface IBaseListDropdownOption<T = any> {
50
55
  value: T;
@@ -84,6 +89,8 @@ export interface IListDropdownConfig<T = any> {
84
89
  options: Array<IListDropdownOption | IListDropdownOptionGroup>;
85
90
  selectCallback: (value: T) => void;
86
91
  activeChangeCallback?: (id: string) => void;
92
+ showSelectAll?: boolean;
93
+ selectAllLabel?: string;
87
94
  closeCallback?: () => void;
88
95
  syncWidth?: boolean;
89
96
  constrainViewportWidth?: boolean;
@@ -115,6 +122,7 @@ export interface IListDropdownConfig<T = any> {
115
122
  footerBuilder?: ListDropdownFooterBuilder;
116
123
  cascade?: boolean;
117
124
  cascadingElementFactory?: (config: IListDropdownCascadingElementFactoryConfig) => HTMLElement;
125
+ anchorAccessibility?: PopoverAnchorAccessibility;
118
126
  }
119
127
  export interface IListDropdownCascadingElementFactoryConfig {
120
128
  index: number;
@@ -141,4 +149,6 @@ export interface ListDropdownTooltipConfig {
141
149
  offset?: number;
142
150
  anchor?: string;
143
151
  anchorElement?: HTMLElement;
152
+ visibilityMode?: ListDropdownTooltipVisibilityMode;
144
153
  }
154
+ export type ListDropdownTooltipVisibilityMode = 'always' | 'overflow-only';
@@ -25,10 +25,15 @@ const attributes = {
25
25
  const classes = {
26
26
  GROUP_WRAPPER: 'forge-list-dropdown__group-wrapper'
27
27
  };
28
+ const selectAllOption = {
29
+ VALUE: '__forge_select_all__',
30
+ LABEL: 'Select All'
31
+ };
28
32
  export const LIST_DROPDOWN_CONSTANTS = {
29
33
  observedAttributes,
30
34
  attributes,
31
- classes
35
+ classes,
36
+ selectAllOption
32
37
  };
33
38
  export const DEFAULT_LIST_DROPDOWN_CONFIG = {
34
39
  options: [],
@@ -6,7 +6,7 @@
6
6
  import { ILinearProgressComponent } from '../linear-progress';
7
7
  import { IListComponent } from '../list/list';
8
8
  import { IPopoverComponent } from '../popover/popover';
9
- import { IListDropdownOpenConfig, IListDropdownOption, IListDropdownOptionGroup, ListDropdownAsyncStyle } from './list-dropdown-constants';
9
+ import { IListDropdownOpenConfig, IListDropdownOption, IListDropdownOptionGroup, ListDropdownAsyncStyle, ListDropdownTooltipConfig } from './list-dropdown-constants';
10
10
  export declare enum ListDropdownOptionType {
11
11
  Option = 0,
12
12
  Group = 1
@@ -24,6 +24,8 @@ export declare function createList(config: IListDropdownOpenConfig): IListCompon
24
24
  * @param config
25
25
  */
26
26
  export declare function createListItems(config: IListDropdownOpenConfig, listElement: IListComponent, options?: Array<IListDropdownOption | IListDropdownOptionGroup>, startIndex?: number, renderSelected?: boolean): void;
27
+ export declare function createListItemTooltip(tooltip: ListDropdownTooltipConfig, configId: string, optionIdIndex: number, listItemElement: HTMLElement, buttonElement: HTMLElement): HTMLElement;
28
+ export declare function asyncCreateListItemTooltipIfTextOverflows(tooltip: ListDropdownTooltipConfig, configId: string, optionIdIndex: number, listItemElement: HTMLElement, buttonElement: HTMLElement, secondaryLabelElement: HTMLElement): Promise<void>;
27
29
  export declare function createCheckboxElement(selected: boolean): HTMLElement;
28
30
  export declare function createAsyncElement(asyncStyle?: ListDropdownAsyncStyle): HTMLElement;
29
31
  export declare function createBusyElement(): ILinearProgressComponent;
@@ -11,6 +11,7 @@ import { LIST_CONSTANTS } from '../list/list';
11
11
  import { POPOVER_CONSTANTS } from '../popover';
12
12
  import { SKELETON_CONSTANTS } from '../skeleton';
13
13
  import { ListDropdownAsyncStyle, ListDropdownType, LIST_DROPDOWN_CONSTANTS } from './list-dropdown-constants';
14
+ import { frame } from '../core/utils/utils';
14
15
  export var ListDropdownOptionType;
15
16
  (function (ListDropdownOptionType) {
16
17
  ListDropdownOptionType[ListDropdownOptionType["Option"] = 0] = "Option";
@@ -66,6 +67,9 @@ export function createPopupDropdown(config, targetElement) {
66
67
  if (config.popupShift !== undefined) {
67
68
  popoverElement.shift = config.popupShift;
68
69
  }
70
+ if (config.anchorAccessibility) {
71
+ popoverElement.anchorAccessibility = config.anchorAccessibility;
72
+ }
69
73
  // Set the animations based on our type
70
74
  if (config.type === ListDropdownType.None) {
71
75
  popoverElement.animationType = 'none';
@@ -97,8 +101,35 @@ export function createList(config) {
97
101
  * @param config
98
102
  */
99
103
  export function createListItems(config, listElement, options, startIndex = 0, renderSelected = true) {
104
+ // Add select all option if enabled and in multiple mode
105
+ let optionsToRender = options || config.options;
106
+ if (config.showSelectAll && config.multiple && startIndex === 0) {
107
+ const selectAllOption = {
108
+ value: LIST_DROPDOWN_CONSTANTS.selectAllOption.VALUE,
109
+ label: config.selectAllLabel || LIST_DROPDOWN_CONSTANTS.selectAllOption.LABEL
110
+ };
111
+ const dividerOption = {
112
+ value: '',
113
+ label: '',
114
+ divider: true
115
+ };
116
+ // Inject select all at the beginning
117
+ if (isListDropdownOptionType(optionsToRender, ListDropdownOptionType.Group)) {
118
+ const optionGroups = optionsToRender;
119
+ if (optionGroups.length > 0) {
120
+ optionGroups[0] = { ...optionGroups[0], options: [selectAllOption, dividerOption, ...optionGroups[0].options] };
121
+ optionsToRender = optionGroups;
122
+ }
123
+ else {
124
+ optionsToRender = [{ text: '', options: [selectAllOption, dividerOption] }];
125
+ }
126
+ }
127
+ else {
128
+ optionsToRender = [selectAllOption, dividerOption, ...optionsToRender];
129
+ }
130
+ }
100
131
  // Ensure the options are provided in the form a group (if no groups provided, then we have one anonymous group of options)
101
- const groups = getOptionsByGroup(options || config.options);
132
+ const groups = getOptionsByGroup(optionsToRender);
102
133
  const flatOptions = getFlattenedOptions(groups);
103
134
  const limitOptions = config.optionLimit ? true : false;
104
135
  let optionLimit = config.optionLimit || 0;
@@ -193,7 +224,7 @@ export function createListItems(config, listElement, options, startIndex = 0, re
193
224
  buttonElement.textContent = option.label || '';
194
225
  }
195
226
  else {
196
- const result = config.transform(option.label);
227
+ const result = config.transform(option.label, option, isSelected);
197
228
  if (typeof result === 'string') {
198
229
  buttonElement.textContent = result;
199
230
  }
@@ -202,27 +233,9 @@ export function createListItems(config, listElement, options, startIndex = 0, re
202
233
  }
203
234
  }
204
235
  }
205
- // Check for a tooltip configuration
206
- if (option.tooltip) {
207
- const { text, type = 'presentation', ...restConfig } = option.tooltip;
208
- const tooltipElement = document.createElement('forge-tooltip');
209
- tooltipElement.id = `list-dropdown-option-${config.id}-${optionIdIndex++}-tooltip`;
210
- tooltipElement.textContent = option.tooltip.text;
211
- // We always anchor to the list item element unless an anchor element is provided
212
- if (!option.tooltip.anchor && !option.tooltip.anchorElement) {
213
- tooltipElement.anchorElement = listItemElement;
214
- }
215
- // We need to attach the tooltip ARIA attributes to the button element, not the anchor element
216
- if (type === 'label' || type === 'description') {
217
- const a11yAttr = type === 'label' ? 'aria-labelledby' : 'aria-describedby';
218
- buttonElement.setAttribute(a11yAttr, tooltipElement.id);
219
- }
220
- Object.assign(tooltipElement, restConfig);
221
- listItemElement.appendChild(tooltipElement);
222
- }
236
+ const secondaryLabelElement = document.createElement('span');
223
237
  // Check for secondary (subtitle) text
224
238
  if (option.secondaryLabel) {
225
- const secondaryLabelElement = document.createElement('span');
226
239
  secondaryLabelElement.slot = 'secondary-text';
227
240
  secondaryLabelElement.textContent = option.secondaryLabel;
228
241
  secondaryLabelElement.id = `list-dropdown-option-${config.id}-${optionIdIndex++}-secondary`;
@@ -230,6 +243,16 @@ export function createListItems(config, listElement, options, startIndex = 0, re
230
243
  listItemElement.appendChild(secondaryLabelElement);
231
244
  buttonElement.setAttribute('aria-describedby', secondaryLabelElement.id);
232
245
  }
246
+ // Check for a tooltip configuration
247
+ if (option.tooltip) {
248
+ if (!isDefined(option.tooltip?.visibilityMode) || option.tooltip?.visibilityMode === 'always') {
249
+ const tooltipElement = createListItemTooltip(option.tooltip, config.id, optionIdIndex, listItemElement, buttonElement);
250
+ listItemElement.appendChild(tooltipElement);
251
+ }
252
+ else if (option.tooltip?.visibilityMode === 'overflow-only') {
253
+ asyncCreateListItemTooltipIfTextOverflows(option.tooltip, config.id, optionIdIndex, listItemElement, buttonElement, secondaryLabelElement);
254
+ }
255
+ }
233
256
  // If multiple selections are enabled then we need to create and append a leading checkbox element
234
257
  if (config.multiple) {
235
258
  const checkboxElement = createCheckboxElement(isSelected);
@@ -303,6 +326,31 @@ export function createListItems(config, listElement, options, startIndex = 0, re
303
326
  }
304
327
  }
305
328
  }
329
+ export function createListItemTooltip(tooltip, configId, optionIdIndex, listItemElement, buttonElement) {
330
+ const { text, type = 'presentation', ...restConfig } = tooltip;
331
+ const tooltipElement = document.createElement('forge-tooltip');
332
+ tooltipElement.id = `list-dropdown-option-${configId}-${optionIdIndex++}-tooltip`;
333
+ tooltipElement.textContent = tooltip.text;
334
+ // We always anchor to the list item element unless an anchor element is provided
335
+ if (!tooltip.anchor && !tooltip.anchorElement) {
336
+ tooltipElement.anchorElement = listItemElement;
337
+ }
338
+ // We need to attach the tooltip ARIA attributes to the button element, not the anchor element
339
+ if (type === 'label' || type === 'description') {
340
+ const a11yAttr = type === 'label' ? 'aria-labelledby' : 'aria-describedby';
341
+ buttonElement.setAttribute(a11yAttr, tooltipElement.id);
342
+ }
343
+ Object.assign(tooltipElement, restConfig);
344
+ return tooltipElement;
345
+ }
346
+ export async function asyncCreateListItemTooltipIfTextOverflows(tooltip, configId, optionIdIndex, listItemElement, buttonElement, secondaryLabelElement) {
347
+ await frame();
348
+ // Only append the tooltip if the button or secondary label overflow the width of the parent list item.
349
+ if (buttonElement.scrollWidth > listItemElement.clientWidth || secondaryLabelElement.scrollWidth > listItemElement.clientWidth) {
350
+ const tooltipElement = createListItemTooltip(tooltip, configId, optionIdIndex, listItemElement, buttonElement);
351
+ listItemElement.appendChild(tooltipElement);
352
+ }
353
+ }
306
354
  export function createCheckboxElement(selected) {
307
355
  const checkboxElement = document.createElement('forge-icon');
308
356
  checkboxElement.setAttribute(LIST_DROPDOWN_CONSTANTS.attributes.CHECKBOX_ELEMENT, '');
@@ -35,6 +35,7 @@ export interface IMenuAdapter extends IBaseAdapter {
35
35
  createChildMenu(index: number, parentValue: any, openCb: (index: number) => void, closeCb: (index: number) => void, selectCb: (data: IMenuSelectEventData) => void): IMenuComponent;
36
36
  closeOtherChildMenus(excludeIndex?: number): void;
37
37
  setSelectedValues(values: any[]): void;
38
+ resolvePopupTargetById(id: string | null): HTMLElement | null;
38
39
  }
39
40
  export declare class MenuAdapter extends BaseAdapter<IMenuComponent> implements IMenuAdapter {
40
41
  private _targetElement;
@@ -70,4 +71,5 @@ export declare class MenuAdapter extends BaseAdapter<IMenuComponent> implements
70
71
  private _getOpenChildMenu;
71
72
  private _getOwnList;
72
73
  private _getListItems;
74
+ resolvePopupTargetById(id: string | null): HTMLElement | null;
73
75
  }
@@ -40,20 +40,16 @@ export class MenuAdapter extends BaseAdapter {
40
40
  }
41
41
  }
42
42
  addTargetListener(event, callback, bubbles = false) {
43
- if (this._targetElement) {
44
- this._targetElement.addEventListener(event, callback, bubbles);
45
- }
43
+ this._targetElement?.addEventListener(event, callback, bubbles);
46
44
  }
47
45
  removeTargetListener(event, callback) {
48
- if (this._targetElement) {
49
- this._targetElement.removeEventListener(event, callback);
50
- }
46
+ this._targetElement?.removeEventListener(event, callback);
51
47
  }
52
48
  attachMenu(config) {
53
- if (this._listDropdown || !this._targetElement) {
49
+ if (this._listDropdown || !this._targetElement || !config.referenceElement) {
54
50
  return;
55
51
  }
56
- this._listDropdown = new ListDropdown(this._targetElement, config);
52
+ this._listDropdown = new ListDropdown(config.referenceElement, config);
57
53
  this._listDropdown.open();
58
54
  this._targetElement.setAttribute('aria-expanded', 'true');
59
55
  this._targetElement.setAttribute('aria-controls', `list-dropdown-popup-${config.id}`);
@@ -201,4 +197,12 @@ export class MenuAdapter extends BaseAdapter {
201
197
  }
202
198
  return [];
203
199
  }
200
+ resolvePopupTargetById(id) {
201
+ if (!id) {
202
+ return null;
203
+ }
204
+ const root = this._component.getRootNode();
205
+ const contextDocument = root instanceof Document || root instanceof ShadowRoot ? root : document;
206
+ return contextDocument.getElementById(id);
207
+ }
204
208
  }
@@ -27,6 +27,7 @@ export declare const MENU_CONSTANTS: {
27
27
  OPTION_LIMIT: string;
28
28
  OBSERVE_SCROLL: string;
29
29
  OBSERVE_SCROLL_THRESHOLD: string;
30
+ POPUP_TARGET: string;
30
31
  };
31
32
  events: {
32
33
  SELECT: string;
@@ -9,7 +9,7 @@ const classes = {
9
9
  POPUP: 'forge-menu__popup'
10
10
  };
11
11
  const selectors = {
12
- TOGGLE: `.${elementName}__toggle,[${elementName}-toggle],forge-button,forge-icon-button,forge-fab,button,[type=button],[role=button],a,forge-list-item,[tabindex]:not([tabindex^="-"])`,
12
+ TOGGLE: `.${elementName}__toggle,[${elementName}-toggle],forge-button,forge-icon-button,forge-fab,button,[type=button],[role=button],a,forge-list-item > button,[tabindex]:not([tabindex^="-"])`,
13
13
  MENU_LIST: 'forge-list'
14
14
  };
15
15
  const attributes = {
@@ -25,7 +25,8 @@ const attributes = {
25
25
  POPUP_CLASSES: 'popup-classes',
26
26
  OPTION_LIMIT: 'option-limit',
27
27
  OBSERVE_SCROLL: 'observe-scroll',
28
- OBSERVE_SCROLL_THRESHOLD: 'observe-scroll-threshold'
28
+ OBSERVE_SCROLL_THRESHOLD: 'observe-scroll-threshold',
29
+ POPUP_TARGET: 'popup-target'
29
30
  };
30
31
  const events = {
31
32
  SELECT: `${elementName}-select`,
@@ -26,6 +26,7 @@ export interface IMenuCore {
26
26
  mode: MenuMode;
27
27
  popupOffset: IOverlayOffset;
28
28
  optionBuilder: MenuOptionBuilder | undefined;
29
+ popupTarget: string | null;
29
30
  activateFirstOption(): void;
30
31
  }
31
32
  export declare class MenuCore extends CascadingListDropdownAwareCore<IMenuOption | IMenuOptionGroup> implements IMenuCore {
@@ -40,6 +41,7 @@ export declare class MenuCore extends CascadingListDropdownAwareCore<IMenuOption
40
41
  private _mode;
41
42
  private _popupOffset;
42
43
  private _optionBuilder;
44
+ private _popupTarget;
43
45
  private _identifier;
44
46
  private _clickListener;
45
47
  private _blurListener;
@@ -82,6 +84,7 @@ export declare class MenuCore extends CascadingListDropdownAwareCore<IMenuOption
82
84
  protected _setCascadeTargetInactive(): void;
83
85
  protected _isOwnElement(element: Element): boolean;
84
86
  private _createCascadingElement;
87
+ private _tryResolvePopupTarget;
85
88
  private _mapIconToLeadingIcon;
86
89
  get open(): boolean;
87
90
  set open(value: boolean);
@@ -106,6 +109,8 @@ export declare class MenuCore extends CascadingListDropdownAwareCore<IMenuOption
106
109
  set mode(value: MenuMode);
107
110
  get popupOffset(): IOverlayOffset;
108
111
  set popupOffset(value: IOverlayOffset);
112
+ get popupTarget(): string | null;
113
+ set popupTarget(value: string | null);
109
114
  get optionBuilder(): MenuOptionBuilder | undefined;
110
115
  set optionBuilder(cb: MenuOptionBuilder | undefined);
111
116
  get popupElement(): HTMLElement | null;
@@ -21,6 +21,7 @@ export class MenuCore extends CascadingListDropdownAwareCore {
21
21
  this._iconClass = ICON_CLASS_NAME;
22
22
  this._persistSelection = false;
23
23
  this._mode = 'click';
24
+ this._popupTarget = null;
24
25
  this._identifier = randomChars();
25
26
  this._clickListener = evt => this._onTargetClick(evt);
26
27
  this._blurListener = evt => this._onTargetBlur(evt);
@@ -218,9 +219,22 @@ export class MenuCore extends CascadingListDropdownAwareCore {
218
219
  }
219
220
  this._mapIconToLeadingIcon();
220
221
  const selectedValues = this._persistSelection ? this._getSelectedValues() : [];
222
+ let referenceElement = this._adapter.targetElement;
223
+ if (this._popupTarget) {
224
+ const popupTargetElement = this._adapter.resolvePopupTargetById(this._popupTarget);
225
+ if (popupTargetElement) {
226
+ referenceElement = popupTargetElement;
227
+ }
228
+ }
229
+ else {
230
+ const resolvedTarget = this._tryResolvePopupTarget();
231
+ if (resolvedTarget) {
232
+ referenceElement = resolvedTarget;
233
+ }
234
+ }
221
235
  const config = {
222
236
  id: this._identifier,
223
- referenceElement: this._adapter.targetElement,
237
+ referenceElement,
224
238
  type: ListDropdownType.Menu,
225
239
  options: this._options,
226
240
  selectedValues,
@@ -246,7 +260,8 @@ export class MenuCore extends CascadingListDropdownAwareCore {
246
260
  activeChangeCallback: this._activeChangeListener,
247
261
  selectCallback: this._selectListener,
248
262
  popupOffset: this._popupOffset,
249
- cascadingElementFactory: params => this._createCascadingElement(params)
263
+ cascadingElementFactory: params => this._createCascadingElement(params),
264
+ anchorAccessibility: 'none'
250
265
  };
251
266
  this._adapter.setHostAttribute(MENU_CONSTANTS.attributes.OPEN, '');
252
267
  this._adapter.attachMenu(config);
@@ -391,6 +406,15 @@ export class MenuCore extends CascadingListDropdownAwareCore {
391
406
  menu.iconClass = this._iconClass;
392
407
  return menu;
393
408
  }
409
+ _tryResolvePopupTarget() {
410
+ if (this._popupTarget) {
411
+ return;
412
+ }
413
+ // Automatically detect forge-list-item > button case and set popup target if not explicitly set
414
+ if (this._adapter.targetElement?.matches('button')) {
415
+ return this._adapter.targetElement.closest('forge-list-item') ?? undefined;
416
+ }
417
+ }
394
418
  _mapIconToLeadingIcon() {
395
419
  // For backwards compatibility with old API, map the old "icon" property to the new "leadingIcon" property (if exists)
396
420
  this._flatOptions.filter(o => o.icon).forEach(o => (o.leadingIcon = o.icon));
@@ -516,6 +540,21 @@ export class MenuCore extends CascadingListDropdownAwareCore {
516
540
  set popupOffset(value) {
517
541
  this._popupOffset = value;
518
542
  }
543
+ get popupTarget() {
544
+ return this._popupTarget ?? null;
545
+ }
546
+ set popupTarget(value) {
547
+ value = value ?? null;
548
+ if (this._popupTarget !== value) {
549
+ this._popupTarget = value;
550
+ if (this._popupTarget) {
551
+ this._adapter.setHostAttribute(MENU_CONSTANTS.attributes.POPUP_TARGET, this._popupTarget);
552
+ }
553
+ else {
554
+ this._adapter.removeHostAttribute(MENU_CONSTANTS.attributes.POPUP_TARGET);
555
+ }
556
+ }
557
+ }
519
558
  get optionBuilder() {
520
559
  return this._optionBuilder;
521
560
  }
@@ -21,6 +21,7 @@ export interface IMenuComponent extends IListDropdownAware {
21
21
  popupOffset: IOverlayOffset;
22
22
  optionBuilder: MenuOptionBuilder | undefined;
23
23
  popupElement: HTMLElement | undefined;
24
+ popupTarget: string | null;
24
25
  propagateKeyEvent(evt: KeyboardEvent): void;
25
26
  activateFirstOption(): void;
26
27
  }
@@ -38,8 +39,15 @@ declare global {
38
39
  /**
39
40
  * @tag forge-menu
40
41
  *
42
+ * @summary Menus display a list of options or actions that users can select from a dropdown. Menus wrap button or list item elements to provide the trigger for displaying the menu options.
43
+ *
41
44
  * @dependency forge-popover
42
45
  * @dependency forge-list
46
+ *
47
+ * @event {CustomEvent<IMenuSelectEventData>} forge-menu-select - Dispatches when a menu option is selected.
48
+ * @event {CustomEvent<void>} forge-menu-open - Dispatches when the menu is opened.
49
+ * @event {CustomEvent<void>} forge-menu-close - Dispatches when the menu is closed.
50
+ * @event {CustomEvent<IMenuActiveChangeEventData>} forge-menu-active-change - Dispatches when the active menu option changes.
43
51
  */
44
52
  export declare class MenuComponent extends ListDropdownAware implements IMenuComponent {
45
53
  static get observedAttributes(): string[];
@@ -117,6 +125,15 @@ export declare class MenuComponent extends ListDropdownAware implements IMenuCom
117
125
  * @readonly
118
126
  */
119
127
  popupElement: HTMLElement | undefined;
128
+ /**
129
+ * Gets/sets the ID of the element to use as the popup anchor for positioning.
130
+ * When null or empty, the target element (button) is used for both interaction and positioning.
131
+ * This is useful for cases like forge-list-item > button where the menu should be
132
+ * attached to the button for listeners but positioned relative to the list item.
133
+ * @default null
134
+ * @attribute popup-target
135
+ */
136
+ popupTarget: string | null;
120
137
  /**
121
138
  * Force propagates the key event from another element to this component.
122
139
  */
package/esm/menu/menu.js CHANGED
@@ -19,8 +19,15 @@ const styles = ':host{display:inline-flex}:host([hidden]){display:none}';
19
19
  /**
20
20
  * @tag forge-menu
21
21
  *
22
+ * @summary Menus display a list of options or actions that users can select from a dropdown. Menus wrap button or list item elements to provide the trigger for displaying the menu options.
23
+ *
22
24
  * @dependency forge-popover
23
25
  * @dependency forge-list
26
+ *
27
+ * @event {CustomEvent<IMenuSelectEventData>} forge-menu-select - Dispatches when a menu option is selected.
28
+ * @event {CustomEvent<void>} forge-menu-open - Dispatches when the menu is opened.
29
+ * @event {CustomEvent<void>} forge-menu-close - Dispatches when the menu is closed.
30
+ * @event {CustomEvent<IMenuActiveChangeEventData>} forge-menu-active-change - Dispatches when the active menu option changes.
24
31
  */
25
32
  let MenuComponent = class MenuComponent extends ListDropdownAware {
26
33
  static get observedAttributes() {
@@ -38,7 +45,8 @@ let MenuComponent = class MenuComponent extends ListDropdownAware {
38
45
  MENU_CONSTANTS.attributes.POPUP_CLASSES,
39
46
  MENU_CONSTANTS.attributes.OPTION_LIMIT,
40
47
  MENU_CONSTANTS.attributes.OBSERVE_SCROLL,
41
- MENU_CONSTANTS.attributes.OBSERVE_SCROLL_THRESHOLD
48
+ MENU_CONSTANTS.attributes.OBSERVE_SCROLL_THRESHOLD,
49
+ MENU_CONSTANTS.attributes.POPUP_TARGET
42
50
  ];
43
51
  }
44
52
  constructor() {
@@ -82,6 +90,9 @@ let MenuComponent = class MenuComponent extends ListDropdownAware {
82
90
  case MENU_CONSTANTS.attributes.MODE:
83
91
  this.mode = newValue;
84
92
  break;
93
+ case MENU_CONSTANTS.attributes.POPUP_TARGET:
94
+ this._core.popupTarget = isDefined(newValue) ? newValue : null;
95
+ break;
85
96
  }
86
97
  }
87
98
  disconnectedCallback() {
@@ -139,6 +150,9 @@ __decorate([
139
150
  __decorate([
140
151
  coreProperty({ set: false })
141
152
  ], MenuComponent.prototype, "popupElement", void 0);
153
+ __decorate([
154
+ coreProperty()
155
+ ], MenuComponent.prototype, "popupTarget", void 0);
142
156
  MenuComponent = __decorate([
143
157
  customElement({
144
158
  name: MENU_CONSTANTS.elementName,
@@ -18,7 +18,8 @@ declare global {
18
18
  /**
19
19
  * @tag forge-open-icon
20
20
  *
21
- * @summary Open icons are used to indicate whether a section is open or closed.
21
+ * @summary Open icons are icons used to indicate whether a section is open or closed. They provide an animated transition between the two states to enhance the user experience.
22
+
22
23
  *
23
24
  * @property {boolean} [open=false] - Whether the icon is open or closed.
24
25
  * @property {OpenIconOrientation} [orientation=vertical] - The orientation of the rotation.
@@ -14,7 +14,8 @@ const styles = ':host{display:inline-flex}:host([hidden]){display:none}.forge-op
14
14
  /**
15
15
  * @tag forge-open-icon
16
16
  *
17
- * @summary Open icons are used to indicate whether a section is open or closed.
17
+ * @summary Open icons are icons used to indicate whether a section is open or closed. They provide an animated transition between the two states to enhance the user experience.
18
+
18
19
  *
19
20
  * @property {boolean} [open=false] - Whether the icon is open or closed.
20
21
  * @property {OpenIconOrientation} [orientation=vertical] - The orientation of the rotation.
@@ -21,8 +21,7 @@ declare global {
21
21
  /**
22
22
  * @tag forge-overlay
23
23
  *
24
- * @summary Overlays are used to render content in an element that rendered above all content on the page,
25
- * and positioned around a specified anchor element.
24
+ * @summary Overlays are used to show content in an element that is rendered above all other content on the page, and positioned around a specified anchor element. This is a low-level building block component that does not provide any visual styles, but is used within other components such as popovers, and tooltips.
26
25
  *
27
26
  * @description
28
27
  * An overlay is a low-level building block component that does not provide any visual styles. Its only
@@ -15,8 +15,7 @@ const styles = ':host{display:contents}:host([hidden]){display:none}.forge-overl
15
15
  /**
16
16
  * @tag forge-overlay
17
17
  *
18
- * @summary Overlays are used to render content in an element that rendered above all content on the page,
19
- * and positioned around a specified anchor element.
18
+ * @summary Overlays are used to show content in an element that is rendered above all other content on the page, and positioned around a specified anchor element. This is a low-level building block component that does not provide any visual styles, but is used within other components such as popovers, and tooltips.
20
19
  *
21
20
  * @description
22
21
  * An overlay is a low-level building block component that does not provide any visual styles. Its only
@@ -12,9 +12,10 @@ declare global {
12
12
  }
13
13
  }
14
14
  /**
15
- *
16
15
  * @tag forge-page-state
17
16
  *
17
+ * @summary Page states display full-height messages for empty states, errors, or other general information. They can be used as full page content, or within smaller containers and will adapt accordingly.
18
+ *
18
19
  * @cssproperty --forge-page-state-width - The width of the page state.
19
20
  * @cssproperty --forge-page-state-height - The height of the page state.
20
21
  * @cssproperty --forge-page-state-spacing - The spacing of the page state.
@@ -10,9 +10,10 @@ import { PAGE_STATE_CONSTANTS } from './page-state-constants';
10
10
  const template = '<template><div class=\"forge-page-state\" part=\"root\"><div class=\"graphic\" part=\"graphic-container\"><slot name=\"graphic\"></slot></div><div class=\"title\" part=\"title-container\"><slot name=\"title\"></slot></div><div class=\"message\" part=\"message-container\"><slot name=\"message\"></slot></div><div class=\"actions\" part=\"actions-container\"><slot name=\"action\"></slot></div></div></template>';
11
11
  const styles = ':host{--_page-state-width:var(--forge-page-state-width, 576px);--_page-state-height:var(--forge-page-state-height, auto);--_page-state-spacing:var(--forge-page-state-spacing, var(--forge-spacing-xxxlarge, 56px));--_page-state-mobile-width:var(--forge-page-state-mobile-width, 70%);--_page-state-graphic-height:var(--forge-page-state-graphic-height, 296px);--_page-state-graphic-spacing:var(--forge-page-state-graphic-spacing, var(--forge-spacing-xxxlarge, 56px));--_page-state-mobile-graphic-height:var(--forge-page-state-mobile-graphic-height, auto);--_page-state-title-color:var(--forge-page-state-title-color, var(--forge-theme-text-medium, rgba(0, 0, 0, 0.6)));--_page-state-title-spacing:var(--forge-page-state-title-spacing, var(--forge-spacing-xxxlarge, 56px));--_page-state-title-spacing-mobile:var(--forge-page-state-title-spacing-mobile, var(--forge-spacing-large, 24px));--_page-state-message-color:var(--forge-page-state-message-color, var(--forge-theme-text-high, rgba(0, 0, 0, 0.87)));--_page-state-message-spacing:var(--forge-page-state-message-spacing, var(--forge-spacing-xxxlarge, 56px));--_page-state-message-spacing-mobile:var(--forge-page-state-message-spacing-mobile, var(--forge-spacing-large, 24px));--_page-state-actions-spacing:var(--forge-page-state-actions-spacing, 16px)}:host{display:flex;justify-content:center;align-items:center;width:100%;container-type:inline-size}:host([hidden]){display:none}.forge-page-state{width:var(--_page-state-width);margin-block:var(--_page-state-spacing);text-align:center}.forge-page-state .graphic{display:flex;justify-content:center;align-items:center;height:var(--_page-state-graphic-height);width:100%;margin-block-end:var(--_page-state-graphic-spacing)}.forge-page-state .graphic ::slotted([slot=graphic]){height:100%;width:100%}.forge-page-state .title{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:var(--forge-typography-heading8-font-family, var(--forge-typography-font-family, \"Roboto\", sans-serif));font-size:var(--forge-typography-heading8-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading8-font-size-scale, 2.25)));font-weight:var(--forge-typography-heading8-font-weight,500);line-height:var(--forge-typography-heading8-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading8-line-height-scale, 2.625)));letter-spacing:var(--forge-typography-heading8-letter-spacing, -.0027777778em);text-transform:var(--forge-typography-heading8-text-transform,inherit);text-decoration:var(--forge-typography-heading8-text-decoration,inherit);--_page-state-title-spacing:var(--_page-state-title-spacing-mobile);color:var(--_page-state-title-color);margin-block:var(--_page-state-title-spacing)}.forge-page-state .message{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:var(--forge-typography-body4-font-family, var(--forge-typography-font-family, \"Roboto\", sans-serif));font-size:var(--forge-typography-body4-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 1.25)));font-weight:var(--forge-typography-body4-font-weight,400);line-height:var(--forge-typography-body4-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.75)));letter-spacing:var(--forge-typography-body4-letter-spacing, .005em);text-transform:var(--forge-typography-body4-text-transform,inherit);text-decoration:var(--forge-typography-body4-text-decoration,inherit);--_page-state-message-spacing:var(--_page-state-message-spacing-mobile);color:var(--_page-state-message-color);margin-block-end:var(--_page-state-message-spacing)}.forge-page-state .actions{display:grid;justify-content:center;grid-auto-flow:column;gap:var(--_page-state-actions-spacing)}@container (max-width:599px){.forge-page-state{--_page-state-width:var(--_page-state-mobile-width)}.forge-page-state .graphic{--_page-state-graphic-height:var(--_page-state-mobile-graphic-height);--_page-state-graphic-spacing:var(--forge-page-state-graphic-spacing, 0)}.forge-page-state .title{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:var(--forge-typography-heading5-font-family, var(--forge-typography-font-family, \"Roboto\", sans-serif));font-size:var(--forge-typography-heading5-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading5-font-size-scale, 1.5)));font-weight:var(--forge-typography-heading5-font-weight,500);line-height:var(--forge-typography-heading5-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-heading5-line-height-scale, 1.75)));letter-spacing:var(--forge-typography-heading5-letter-spacing, 0);text-transform:var(--forge-typography-heading5-text-transform,inherit);text-decoration:var(--forge-typography-heading5-text-decoration,inherit)}.forge-page-state .message{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:var(--forge-typography-body2-font-family, var(--forge-typography-font-family, \"Roboto\", sans-serif));font-size:var(--forge-typography-body2-font-size, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-font-size-scale, 1)));font-weight:var(--forge-typography-body2-font-weight,400);line-height:var(--forge-typography-body2-line-height, calc(var(--forge-typography-font-size, 1rem) * var(--forge-typography-body-line-height-scale, 1.375)));letter-spacing:var(--forge-typography-body2-letter-spacing, .015625em);text-transform:var(--forge-typography-body2-text-transform,inherit);text-decoration:var(--forge-typography-body2-text-decoration,inherit)}.forge-page-state .actions{grid-auto-flow:row}}';
12
12
  /**
13
- *
14
13
  * @tag forge-page-state
15
14
  *
15
+ * @summary Page states display full-height messages for empty states, errors, or other general information. They can be used as full page content, or within smaller containers and will adapt accordingly.
16
+ *
16
17
  * @cssproperty --forge-page-state-width - The width of the page state.
17
18
  * @cssproperty --forge-page-state-height - The height of the page state.
18
19
  * @cssproperty --forge-page-state-spacing - The spacing of the page state.