@material/web 2.4.2-nightly.fd6fe2a.0 → 2.5.1-nightly.70e259d.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 (209) hide show
  1. package/custom-elements.json +7148 -7166
  2. package/labs/aria/query-associated.d.ts +23 -0
  3. package/labs/aria/query-associated.js +36 -0
  4. package/labs/aria/query-associated.js.map +1 -0
  5. package/labs/aria/tabs/md-aria-tab.d.ts +11 -0
  6. package/labs/aria/tabs/md-aria-tab.js +8 -0
  7. package/labs/aria/tabs/md-aria-tab.js.map +1 -0
  8. package/labs/aria/tabs/md-aria-tablist.d.ts +11 -0
  9. package/labs/aria/tabs/md-aria-tablist.js +8 -0
  10. package/labs/aria/tabs/md-aria-tablist.js.map +1 -0
  11. package/labs/aria/tabs/md-aria-tabpanel.d.ts +11 -0
  12. package/labs/aria/tabs/md-aria-tabpanel.js +8 -0
  13. package/labs/aria/tabs/md-aria-tabpanel.js.map +1 -0
  14. package/labs/aria/tabs/tab.d.ts +33 -0
  15. package/labs/aria/tabs/tab.js +88 -0
  16. package/labs/aria/tabs/tab.js.map +1 -0
  17. package/labs/aria/tabs/tablist.d.ts +67 -0
  18. package/labs/aria/tabs/tablist.js +271 -0
  19. package/labs/aria/tabs/tablist.js.map +1 -0
  20. package/labs/aria/tabs/tabpanel.d.ts +26 -0
  21. package/labs/aria/tabs/tabpanel.js +53 -0
  22. package/labs/aria/tabs/tabpanel.js.map +1 -0
  23. package/labs/gb/components/appbar/_app-bar-tokens.scss +136 -0
  24. package/labs/gb/components/appbar/app-bar-element.d.ts +53 -0
  25. package/labs/gb/components/appbar/app-bar-element.js +144 -0
  26. package/labs/gb/components/appbar/app-bar-element.js.map +1 -0
  27. package/labs/gb/components/appbar/app-bar.css +4 -0
  28. package/labs/gb/components/appbar/app-bar.css.map +1 -0
  29. package/labs/gb/components/appbar/app-bar.cssresult.d.ts +3 -0
  30. package/labs/gb/components/appbar/app-bar.cssresult.js +14 -0
  31. package/labs/gb/components/appbar/app-bar.cssresult.js.map +1 -0
  32. package/labs/gb/components/appbar/app-bar.d.ts +71 -0
  33. package/labs/gb/components/appbar/app-bar.js +96 -0
  34. package/labs/gb/components/appbar/app-bar.js.map +1 -0
  35. package/labs/gb/components/appbar/app-bar.scss +167 -0
  36. package/labs/gb/components/appbar/md-gb-app-bar.d.ts +12 -0
  37. package/labs/gb/components/appbar/md-gb-app-bar.js +8 -0
  38. package/labs/gb/components/appbar/md-gb-app-bar.js.map +1 -0
  39. package/labs/gb/components/badge/_badge-tokens.scss +18 -0
  40. package/labs/gb/components/badge/badge-element.d.ts +15 -0
  41. package/labs/gb/components/badge/badge-element.js +49 -0
  42. package/labs/gb/components/badge/badge-element.js.map +1 -0
  43. package/labs/gb/components/badge/badge.css +4 -0
  44. package/labs/gb/components/badge/badge.css.map +1 -0
  45. package/labs/gb/components/badge/badge.cssresult.d.ts +3 -0
  46. package/labs/gb/components/badge/badge.cssresult.js +14 -0
  47. package/labs/gb/components/badge/badge.cssresult.js.map +1 -0
  48. package/labs/gb/components/badge/badge.d.ts +32 -0
  49. package/labs/gb/components/badge/badge.js +35 -0
  50. package/labs/gb/components/badge/badge.js.map +1 -0
  51. package/labs/gb/components/badge/badge.scss +49 -0
  52. package/labs/gb/components/badge/md-gb-badge.d.ts +12 -0
  53. package/labs/gb/components/badge/md-gb-badge.js +8 -0
  54. package/labs/gb/components/badge/md-gb-badge.js.map +1 -0
  55. package/labs/gb/components/button/_button-tokens.scss +2 -0
  56. package/labs/gb/components/button/{md-button.d.ts → button-element.d.ts} +22 -7
  57. package/labs/gb/components/button/{md-button.js → button-element.js} +38 -20
  58. package/labs/gb/components/button/button-element.js.map +1 -0
  59. package/labs/gb/components/button/button.css.map +1 -1
  60. package/labs/gb/components/button/md-gb-button.d.ts +12 -0
  61. package/labs/gb/components/button/md-gb-button.js +8 -0
  62. package/labs/gb/components/button/md-gb-button.js.map +1 -0
  63. package/labs/gb/components/card/_card-tokens.scss +2 -0
  64. package/labs/gb/components/card/{md-card.d.ts → card-element.d.ts} +12 -7
  65. package/labs/gb/components/card/{md-card.js → card-element.js} +21 -14
  66. package/labs/gb/components/card/card-element.js.map +1 -0
  67. package/labs/gb/components/card/card.css.map +1 -1
  68. package/labs/gb/components/card/md-gb-card.d.ts +12 -0
  69. package/labs/gb/components/card/md-gb-card.js +8 -0
  70. package/labs/gb/components/card/md-gb-card.js.map +1 -0
  71. package/labs/gb/components/checkbox/_checkbox-tokens.scss +2 -0
  72. package/labs/gb/components/checkbox/{md-checkbox.d.ts → checkbox-element.d.ts} +15 -7
  73. package/labs/gb/components/checkbox/{md-checkbox.js → checkbox-element.js} +26 -16
  74. package/labs/gb/components/checkbox/checkbox-element.js.map +1 -0
  75. package/labs/gb/components/checkbox/checkbox.css.map +1 -1
  76. package/labs/gb/components/checkbox/md-gb-checkbox.d.ts +12 -0
  77. package/labs/gb/components/checkbox/md-gb-checkbox.js +8 -0
  78. package/labs/gb/components/checkbox/md-gb-checkbox.js.map +1 -0
  79. package/labs/gb/components/chip/_chip-tokens.scss +167 -0
  80. package/labs/gb/components/chip/chip-element.d.ts +73 -0
  81. package/labs/gb/components/chip/chip-element.js +233 -0
  82. package/labs/gb/components/chip/chip-element.js.map +1 -0
  83. package/labs/gb/components/chip/chip.css +4 -0
  84. package/labs/gb/components/chip/chip.css.map +1 -0
  85. package/labs/gb/components/chip/chip.cssresult.d.ts +3 -0
  86. package/labs/gb/components/chip/chip.cssresult.js +14 -0
  87. package/labs/gb/components/chip/chip.cssresult.js.map +1 -0
  88. package/labs/gb/components/chip/chip.d.ts +77 -0
  89. package/labs/gb/components/chip/chip.js +99 -0
  90. package/labs/gb/components/chip/chip.js.map +1 -0
  91. package/labs/gb/components/chip/chip.scss +97 -0
  92. package/labs/gb/components/chip/md-gb-chip.d.ts +13 -0
  93. package/labs/gb/components/chip/md-gb-chip.js +9 -0
  94. package/labs/gb/components/chip/md-gb-chip.js.map +1 -0
  95. package/labs/gb/components/divider/_divider-tokens.scss +2 -0
  96. package/labs/gb/components/divider/{md-divider.d.ts → divider-element.d.ts} +5 -7
  97. package/labs/gb/components/divider/{md-divider.js → divider-element.js} +10 -10
  98. package/labs/gb/components/divider/divider-element.js.map +1 -0
  99. package/labs/gb/components/divider/divider.css.map +1 -1
  100. package/labs/gb/components/divider/md-gb-divider.d.ts +12 -0
  101. package/labs/gb/components/divider/md-gb-divider.js +8 -0
  102. package/labs/gb/components/divider/md-gb-divider.js.map +1 -0
  103. package/labs/gb/components/fab/_fab-tokens.scss +2 -0
  104. package/labs/gb/components/fab/fab-element.d.ts +31 -0
  105. package/labs/gb/components/fab/{md-fab.js → fab-element.js} +22 -11
  106. package/labs/gb/components/fab/fab-element.js.map +1 -0
  107. package/labs/gb/components/fab/fab.css.map +1 -1
  108. package/labs/gb/components/fab/md-gb-fab.d.ts +12 -0
  109. package/labs/gb/components/fab/md-gb-fab.js +8 -0
  110. package/labs/gb/components/fab/md-gb-fab.js.map +1 -0
  111. package/labs/gb/components/iconbutton/_icon-button-tokens.scss +2 -0
  112. package/labs/gb/components/iconbutton/{md-icon-button.d.ts → icon-button-element.d.ts} +15 -7
  113. package/labs/gb/components/iconbutton/{md-icon-button.js → icon-button-element.js} +30 -20
  114. package/labs/gb/components/iconbutton/icon-button-element.js.map +1 -0
  115. package/labs/gb/components/iconbutton/icon-button.css.map +1 -1
  116. package/labs/gb/components/iconbutton/md-gb-icon-button.d.ts +12 -0
  117. package/labs/gb/components/iconbutton/md-gb-icon-button.js +8 -0
  118. package/labs/gb/components/iconbutton/md-gb-icon-button.js.map +1 -0
  119. package/labs/gb/components/list/_list-tokens.scss +4 -0
  120. package/labs/gb/components/list/{md-list.d.ts → list-element.d.ts} +6 -7
  121. package/labs/gb/components/list/{md-list.js → list-element.js} +11 -10
  122. package/labs/gb/components/list/list-element.js.map +1 -0
  123. package/labs/gb/components/list/list-item-element.d.ts +77 -0
  124. package/labs/gb/components/list/{md-list-item.js → list-item-element.js} +48 -13
  125. package/labs/gb/components/list/list-item-element.js.map +1 -0
  126. package/labs/gb/components/list/list.css.map +1 -1
  127. package/labs/gb/components/list/md-gb-list-item.d.ts +12 -0
  128. package/labs/gb/components/list/md-gb-list-item.js +8 -0
  129. package/labs/gb/components/list/md-gb-list-item.js.map +1 -0
  130. package/labs/gb/components/list/md-gb-list.d.ts +12 -0
  131. package/labs/gb/components/list/md-gb-list.js +8 -0
  132. package/labs/gb/components/list/md-gb-list.js.map +1 -0
  133. package/labs/gb/components/menu/_menu-tokens.scss +4 -0
  134. package/labs/gb/components/menu/md-gb-menu-group.d.ts +12 -0
  135. package/labs/gb/components/menu/md-gb-menu-group.js +8 -0
  136. package/labs/gb/components/menu/md-gb-menu-group.js.map +1 -0
  137. package/labs/gb/components/menu/md-gb-menu-item.d.ts +12 -0
  138. package/labs/gb/components/menu/md-gb-menu-item.js +8 -0
  139. package/labs/gb/components/menu/md-gb-menu-item.js.map +1 -0
  140. package/labs/gb/components/menu/md-gb-menu.d.ts +12 -0
  141. package/labs/gb/components/menu/md-gb-menu.js +8 -0
  142. package/labs/gb/components/menu/md-gb-menu.js.map +1 -0
  143. package/labs/gb/components/menu/{md-menu.d.ts → menu-element.d.ts} +11 -7
  144. package/labs/gb/components/menu/{md-menu.js → menu-element.js} +16 -10
  145. package/labs/gb/components/menu/menu-element.js.map +1 -0
  146. package/labs/gb/components/menu/{md-menu-group.d.ts → menu-group-element.d.ts} +3 -6
  147. package/labs/gb/components/menu/{md-menu-group.js → menu-group-element.js} +11 -11
  148. package/labs/gb/components/menu/menu-group-element.js.map +1 -0
  149. package/labs/gb/components/menu/menu-item-element.d.ts +56 -0
  150. package/labs/gb/components/menu/{md-menu-item.js → menu-item-element.js} +41 -13
  151. package/labs/gb/components/menu/menu-item-element.js.map +1 -0
  152. package/labs/gb/components/menu/menu.css.map +1 -1
  153. package/labs/gb/components/menu/menu.js +1 -1
  154. package/labs/gb/components/menu/menu.js.map +1 -1
  155. package/labs/gb/components/radio/_radio-tokens.scss +2 -0
  156. package/labs/gb/components/radio/md-gb-radio.d.ts +12 -0
  157. package/labs/gb/components/radio/md-gb-radio.js +8 -0
  158. package/labs/gb/components/radio/md-gb-radio.js.map +1 -0
  159. package/labs/gb/components/radio/{md-radio.d.ts → radio-element.d.ts} +10 -7
  160. package/labs/gb/components/radio/{md-radio.js → radio-element.js} +19 -14
  161. package/labs/gb/components/radio/radio-element.js.map +1 -0
  162. package/labs/gb/components/radio/radio.css.map +1 -1
  163. package/labs/gb/components/splitbutton/_split-button-tokens.scss +4 -0
  164. package/labs/gb/components/splitbutton/md-gb-split-button.d.ts +12 -0
  165. package/labs/gb/components/splitbutton/md-gb-split-button.js +8 -0
  166. package/labs/gb/components/splitbutton/md-gb-split-button.js.map +1 -0
  167. package/labs/gb/components/splitbutton/split-button-element.d.ts +33 -0
  168. package/labs/gb/components/splitbutton/{md-split-button.js → split-button-element.js} +21 -12
  169. package/labs/gb/components/splitbutton/split-button-element.js.map +1 -0
  170. package/labs/gb/components/splitbutton/split-button.css.map +1 -1
  171. package/labs/gb/components/splitbutton/split-button.d.ts +4 -4
  172. package/labs/gb/components/splitbutton/split-button.js +4 -4
  173. package/labs/gb/components/splitbutton/split-button.js.map +1 -1
  174. package/labs/gb/components/switch/_switch-tokens.scss +2 -0
  175. package/labs/gb/components/switch/md-gb-switch.d.ts +12 -0
  176. package/labs/gb/components/switch/md-gb-switch.js +8 -0
  177. package/labs/gb/components/switch/md-gb-switch.js.map +1 -0
  178. package/labs/gb/components/switch/{md-switch.d.ts → switch-element.d.ts} +19 -7
  179. package/labs/gb/components/switch/{md-switch.js → switch-element.js} +28 -14
  180. package/labs/gb/components/switch/switch-element.js.map +1 -0
  181. package/labs/gb/components/switch/switch.css.map +1 -1
  182. package/labs/gb/styles/adopt-styles.js +6 -1
  183. package/labs/gb/styles/adopt-styles.js.map +1 -1
  184. package/labs/gb/styles/icon/{md-icon.d.ts → icon-element.d.ts} +1 -7
  185. package/labs/gb/styles/icon/{md-icon.js → icon-element.js} +8 -10
  186. package/labs/gb/styles/icon/icon-element.js.map +1 -0
  187. package/labs/gb/styles/icon/md-gb-icon.d.ts +12 -0
  188. package/labs/gb/styles/icon/md-gb-icon.js +8 -0
  189. package/labs/gb/styles/icon/md-gb-icon.js.map +1 -0
  190. package/package.json +3 -3
  191. package/labs/gb/components/button/md-button.js.map +0 -1
  192. package/labs/gb/components/card/md-card.js.map +0 -1
  193. package/labs/gb/components/checkbox/md-checkbox.js.map +0 -1
  194. package/labs/gb/components/divider/md-divider.js.map +0 -1
  195. package/labs/gb/components/fab/md-fab.d.ts +0 -22
  196. package/labs/gb/components/fab/md-fab.js.map +0 -1
  197. package/labs/gb/components/iconbutton/md-icon-button.js.map +0 -1
  198. package/labs/gb/components/list/md-list-item.d.ts +0 -44
  199. package/labs/gb/components/list/md-list-item.js.map +0 -1
  200. package/labs/gb/components/list/md-list.js.map +0 -1
  201. package/labs/gb/components/menu/md-menu-group.js.map +0 -1
  202. package/labs/gb/components/menu/md-menu-item.d.ts +0 -30
  203. package/labs/gb/components/menu/md-menu-item.js.map +0 -1
  204. package/labs/gb/components/menu/md-menu.js.map +0 -1
  205. package/labs/gb/components/radio/md-radio.js.map +0 -1
  206. package/labs/gb/components/splitbutton/md-split-button.d.ts +0 -26
  207. package/labs/gb/components/splitbutton/md-split-button.js.map +0 -1
  208. package/labs/gb/components/switch/md-switch.js.map +0 -1
  209. package/labs/gb/styles/icon/md-icon.js.map +0 -1
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /**
7
+ * Queries for an associated element with the given ID that is in the same root
8
+ * as the given element.
9
+ *
10
+ * @param element The element to query from.
11
+ * @param id The ID of the associated element to query for.
12
+ * @return The associated element with the given ID, or null if not found.
13
+ */
14
+ export declare function queryAssociatedById(element: Element, id: string): Element | null;
15
+ /**
16
+ * Queries for one or more associated elements that match the given
17
+ * space-separated ID string that are in the same root as the given element.
18
+ *
19
+ * @param element The element to query from.
20
+ * @param ids The space-separated IDs of the associated elements to query for.
21
+ * @return An array of associated elements with the given IDs.
22
+ */
23
+ export declare function queryAssociatedByIds(element: Element, ids: string): Element[];
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /**
7
+ * Queries for an associated element with the given ID that is in the same root
8
+ * as the given element.
9
+ *
10
+ * @param element The element to query from.
11
+ * @param id The ID of the associated element to query for.
12
+ * @return The associated element with the given ID, or null if not found.
13
+ */
14
+ export function queryAssociatedById(element, id) {
15
+ if (!id)
16
+ return null;
17
+ return element.getRootNode().querySelector(`#${id}`);
18
+ }
19
+ /**
20
+ * Queries for one or more associated elements that match the given
21
+ * space-separated ID string that are in the same root as the given element.
22
+ *
23
+ * @param element The element to query from.
24
+ * @param ids The space-separated IDs of the associated elements to query for.
25
+ * @return An array of associated elements with the given IDs.
26
+ */
27
+ export function queryAssociatedByIds(element, ids) {
28
+ const selectors = ids
29
+ .split(/\s+/)
30
+ .filter((id) => !!id)
31
+ .map((id) => `#${id}`);
32
+ if (!selectors.length)
33
+ return [];
34
+ return Array.from(element.getRootNode().querySelectorAll(selectors.join(',')));
35
+ }
36
+ //# sourceMappingURL=query-associated.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-associated.js","sourceRoot":"","sources":["query-associated.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAgB,EAChB,EAAU;IAEV,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IACrB,OACE,OAAO,CAAC,WAAW,EACpB,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AAC5B,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAgB,EAAE,GAAW;IAChE,MAAM,SAAS,GAAG,GAAG;SAClB,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpB,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACzB,IAAI,CAAC,SAAS,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACjC,OAAO,KAAK,CAAC,IAAI,CACd,OAAO,CAAC,WAAW,EAAsC,CAAC,gBAAgB,CACzE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CACpB,CACF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\n/**\n * Queries for an associated element with the given ID that is in the same root\n * as the given element.\n *\n * @param element The element to query from.\n * @param id The ID of the associated element to query for.\n * @return The associated element with the given ID, or null if not found.\n */\nexport function queryAssociatedById(\n element: Element,\n id: string,\n): Element | null {\n if (!id) return null;\n return (\n element.getRootNode() as Document | ShadowRoot | Element\n ).querySelector(`#${id}`);\n}\n\n/**\n * Queries for one or more associated elements that match the given\n * space-separated ID string that are in the same root as the given element.\n *\n * @param element The element to query from.\n * @param ids The space-separated IDs of the associated elements to query for.\n * @return An array of associated elements with the given IDs.\n */\nexport function queryAssociatedByIds(element: Element, ids: string): Element[] {\n const selectors = ids\n .split(/\\s+/)\n .filter((id) => !!id)\n .map((id) => `#${id}`);\n if (!selectors.length) return [];\n return Array.from(\n (element.getRootNode() as Document | ShadowRoot | Element).querySelectorAll(\n selectors.join(','),\n ),\n );\n}\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { AriaTabElement } from './tab.js';
7
+ declare global {
8
+ interface HTMLElementTagNameMap {
9
+ 'md-aria-tab': AriaTabElement;
10
+ }
11
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { AriaTabElement } from './tab.js';
7
+ customElements.define('md-aria-tab', AriaTabElement);
8
+ //# sourceMappingURL=md-aria-tab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"md-aria-tab.js","sourceRoot":"","sources":["md-aria-tab.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,cAAc,EAAC,MAAM,UAAU,CAAC;AAQxC,cAAc,CAAC,MAAM,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {AriaTabElement} from './tab.js';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'md-aria-tab': AriaTabElement;\n }\n}\n\ncustomElements.define('md-aria-tab', AriaTabElement);\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { AriaTablistElement } from './tablist.js';
7
+ declare global {
8
+ interface HTMLElementTagNameMap {
9
+ 'md-aria-tablist': AriaTablistElement;
10
+ }
11
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { AriaTablistElement } from './tablist.js';
7
+ customElements.define('md-aria-tablist', AriaTablistElement);
8
+ //# sourceMappingURL=md-aria-tablist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"md-aria-tablist.js","sourceRoot":"","sources":["md-aria-tablist.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,kBAAkB,EAAC,MAAM,cAAc,CAAC;AAQhD,cAAc,CAAC,MAAM,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {AriaTablistElement} from './tablist.js';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'md-aria-tablist': AriaTablistElement;\n }\n}\n\ncustomElements.define('md-aria-tablist', AriaTablistElement);\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { AriaTabpanelElement } from './tabpanel.js';
7
+ declare global {
8
+ interface HTMLElementTagNameMap {
9
+ 'md-aria-tabpanel': AriaTabpanelElement;
10
+ }
11
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { AriaTabpanelElement } from './tabpanel.js';
7
+ customElements.define('md-aria-tabpanel', AriaTabpanelElement);
8
+ //# sourceMappingURL=md-aria-tabpanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"md-aria-tabpanel.js","sourceRoot":"","sources":["md-aria-tabpanel.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAC,mBAAmB,EAAC,MAAM,eAAe,CAAC;AAQlD,cAAc,CAAC,MAAM,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {AriaTabpanelElement} from './tabpanel.js';\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'md-aria-tabpanel': AriaTabpanelElement;\n }\n}\n\ncustomElements.define('md-aria-tabpanel', AriaTabpanelElement);\n"]}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { CSSResultGroup, LitElement } from 'lit';
7
+ declare const baseClass: import("../../behaviors/mixin.js").MixinReturn<import("../../behaviors/mixin.js").MixinReturn<import("../../behaviors/mixin.js").MixinReturn<typeof LitElement, import("../../behaviors/element-internals.js").WithElementInternals>, import("../../behaviors/custom-state-set.js").WithCustomStateSet>, import("../../behaviors/focusable.js").Focusable>;
8
+ /**
9
+ * An ARIA tab element.
10
+ *
11
+ * @cssstate selected - Whether the tab is selected.
12
+ */
13
+ export declare class AriaTabElement extends baseClass {
14
+ static styles: CSSResultGroup;
15
+ /**
16
+ * ID of the associated `md-aria-tabpanel` element.
17
+ */
18
+ get tabpanel(): string;
19
+ set tabpanel(value: string);
20
+ /**
21
+ * The tabpanel element that is controlled by this tab.
22
+ */
23
+ get tabpanelElement(): Element | null;
24
+ set tabpanelElement(value: Element | null);
25
+ /**
26
+ * Whether the tab is selected.
27
+ */
28
+ get selected(): boolean;
29
+ set selected(value: boolean);
30
+ constructor();
31
+ protected render(): import("lit-html").TemplateResult<1>;
32
+ }
33
+ export {};
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { __decorate } from "tslib";
7
+ import { css, html, isServer, LitElement } from 'lit';
8
+ import { property } from 'lit/decorators.js';
9
+ import { afterDispatch, setupDispatchHooks, } from '../../../internal/events/dispatch-hooks.js';
10
+ import { queryAssociatedById } from '../../aria/query-associated.js';
11
+ import { mixinCustomStateSet, toggleState, } from '../../behaviors/custom-state-set.js';
12
+ import { internals, mixinElementInternals, } from '../../behaviors/element-internals.js';
13
+ import { mixinFocusable } from '../../behaviors/focusable.js';
14
+ const baseClass = mixinFocusable(mixinCustomStateSet(mixinElementInternals(LitElement)));
15
+ /**
16
+ * An ARIA tab element.
17
+ *
18
+ * @cssstate selected - Whether the tab is selected.
19
+ */
20
+ export class AriaTabElement extends baseClass {
21
+ /**
22
+ * ID of the associated `md-aria-tabpanel` element.
23
+ */
24
+ get tabpanel() {
25
+ return this.tabpanelElement?.id || '';
26
+ }
27
+ set tabpanel(value) {
28
+ this.tabpanelElement = queryAssociatedById(this, value);
29
+ }
30
+ /**
31
+ * The tabpanel element that is controlled by this tab.
32
+ */
33
+ get tabpanelElement() {
34
+ return this[internals].ariaControlsElements?.[0] || null;
35
+ }
36
+ set tabpanelElement(value) {
37
+ const previousValue = this.tabpanelElement;
38
+ if (value === previousValue)
39
+ return;
40
+ this[internals].ariaControlsElements = value ? [value] : [];
41
+ }
42
+ /**
43
+ * Whether the tab is selected.
44
+ */
45
+ get selected() {
46
+ return this[internals].ariaSelected === 'true';
47
+ }
48
+ set selected(value) {
49
+ value = Boolean(value); // coerce for safety
50
+ this[internals].ariaSelected = String(value);
51
+ this[toggleState]('selected', value);
52
+ }
53
+ constructor() {
54
+ super();
55
+ if (isServer)
56
+ return;
57
+ this[internals].role = 'tab';
58
+ setupDispatchHooks(this, 'keydown');
59
+ this.addEventListener('keydown', (event) => {
60
+ afterDispatch(event, () => {
61
+ if (event.defaultPrevented) {
62
+ return;
63
+ }
64
+ if (event.key === 'Enter' || event.key === ' ') {
65
+ // Prevent default behavior such as scrolling when pressing spacebar.
66
+ event.preventDefault();
67
+ this.click();
68
+ }
69
+ });
70
+ });
71
+ }
72
+ render() {
73
+ return html `<slot></slot>`;
74
+ }
75
+ }
76
+ AriaTabElement.styles = css `
77
+ :host {
78
+ display: inline-block;
79
+ cursor: pointer;
80
+ }
81
+ `;
82
+ __decorate([
83
+ property({ type: String, reflect: true, noAccessor: true })
84
+ ], AriaTabElement.prototype, "tabpanel", null);
85
+ __decorate([
86
+ property({ type: Boolean, reflect: true })
87
+ ], AriaTabElement.prototype, "selected", null);
88
+ //# sourceMappingURL=tab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tab.js","sourceRoot":"","sources":["tab.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2026 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {css, CSSResultGroup, html, isServer, LitElement} from 'lit';\nimport {property} from 'lit/decorators.js';\n\nimport {\n afterDispatch,\n setupDispatchHooks,\n} from '../../../internal/events/dispatch-hooks.js';\nimport {queryAssociatedById} from '../../aria/query-associated.js';\nimport {\n mixinCustomStateSet,\n toggleState,\n} from '../../behaviors/custom-state-set.js';\nimport {\n internals,\n mixinElementInternals,\n} from '../../behaviors/element-internals.js';\nimport {mixinFocusable} from '../../behaviors/focusable.js';\n\nconst baseClass = mixinFocusable(\n mixinCustomStateSet(mixinElementInternals(LitElement)),\n);\n\n/**\n * An ARIA tab element.\n *\n * @cssstate selected - Whether the tab is selected.\n */\nexport class AriaTabElement extends baseClass {\n static override styles: CSSResultGroup = css`\n :host {\n display: inline-block;\n cursor: pointer;\n }\n `;\n\n /**\n * ID of the associated `md-aria-tabpanel` element.\n */\n @property({type: String, reflect: true, noAccessor: true})\n get tabpanel(): string {\n return this.tabpanelElement?.id || '';\n }\n set tabpanel(value: string) {\n this.tabpanelElement = queryAssociatedById(this, value);\n }\n\n /**\n * The tabpanel element that is controlled by this tab.\n */\n get tabpanelElement(): Element | null {\n return this[internals].ariaControlsElements?.[0] || null;\n }\n set tabpanelElement(value: Element | null) {\n const previousValue = this.tabpanelElement;\n if (value === previousValue) return;\n this[internals].ariaControlsElements = value ? [value] : [];\n }\n\n /**\n * Whether the tab is selected.\n */\n @property({type: Boolean, reflect: true})\n get selected(): boolean {\n return this[internals].ariaSelected === 'true';\n }\n set selected(value: boolean) {\n value = Boolean(value); // coerce for safety\n this[internals].ariaSelected = String(value);\n this[toggleState]('selected', value);\n }\n\n constructor() {\n super();\n if (isServer) return;\n this[internals].role = 'tab';\n setupDispatchHooks(this, 'keydown');\n this.addEventListener('keydown', (event: KeyboardEvent) => {\n afterDispatch(event, () => {\n if (event.defaultPrevented) {\n return;\n }\n\n if (event.key === 'Enter' || event.key === ' ') {\n // Prevent default behavior such as scrolling when pressing spacebar.\n event.preventDefault();\n this.click();\n }\n });\n });\n }\n\n protected override render() {\n return html`<slot></slot>`;\n }\n}\n"]}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2023 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { CSSResultGroup, LitElement } from 'lit';
7
+ declare const baseClass: import("../../behaviors/mixin.js").MixinReturn<typeof LitElement, import("../../behaviors/element-internals.js").WithElementInternals>;
8
+ /**
9
+ * An ARIA tablist element.
10
+ */
11
+ export declare class AriaTablistElement extends baseClass {
12
+ static styles: CSSResultGroup;
13
+ /**
14
+ * The tabs of this tablist.
15
+ */
16
+ get tabs(): HTMLElement[];
17
+ /**
18
+ * The currently selected tab, `null` only when there are no tab children.
19
+ *
20
+ * @export
21
+ */
22
+ get selectedTab(): HTMLElement | null;
23
+ set selectedTab(tab: HTMLElement | null);
24
+ /**
25
+ * The index of the currently selected tab.
26
+ *
27
+ * @export
28
+ */
29
+ get selectedTabIndex(): number;
30
+ set selectedTabIndex(index: number);
31
+ /**
32
+ * Whether or not to automatically select a tab when it is focused.
33
+ */
34
+ autoSelect: boolean;
35
+ /**
36
+ * Orientation of the tablist ('horizontal' or 'vertical').
37
+ */
38
+ orientation: 'horizontal' | 'vertical';
39
+ private readonly slotElement;
40
+ protected get focusedTab(): HTMLElement;
41
+ constructor();
42
+ protected render(): import("lit-html").TemplateResult<1>;
43
+ /**
44
+ * Returns true if the element is a valid tab component or role. Subclasses
45
+ * may override this method to determine if an element is a supported tab.
46
+ */
47
+ protected isTab(element: HTMLElement): boolean;
48
+ /**
49
+ * Returns true if the tab is currently selected. Subclasses may override this
50
+ * method to support custom tab selection implementations.
51
+ */
52
+ protected isTabSelected(tab: HTMLElement): boolean;
53
+ /**
54
+ * Updates the selection state of the tab element and visibility of its
55
+ * tabpanel. Subclasses may override this method to support custom tab
56
+ * selection implementations.
57
+ */
58
+ protected setTabSelected(tab: HTMLElement, isSelected: boolean): void;
59
+ protected onTabChange(previousTab: HTMLElement | null): void;
60
+ protected updateSelectedTab(tabToSelect: HTMLElement): void;
61
+ protected updateFocusableTab(focusableTab: HTMLElement): void;
62
+ private handleClick;
63
+ protected handleSlotChange(): void;
64
+ private handleKeydown;
65
+ private handleFocusout;
66
+ }
67
+ export {};
@@ -0,0 +1,271 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2023 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { __decorate } from "tslib";
7
+ import { css, html, isServer, LitElement } from 'lit';
8
+ import { property, query } from 'lit/decorators.js';
9
+ import { afterDispatch, setupDispatchHooks, } from '../../../internal/events/dispatch-hooks.js';
10
+ import { internals, mixinElementInternals, } from '../../behaviors/element-internals.js';
11
+ const baseClass = mixinElementInternals(LitElement);
12
+ /**
13
+ * An ARIA tablist element.
14
+ */
15
+ export class AriaTablistElement extends baseClass {
16
+ /**
17
+ * The tabs of this tablist.
18
+ */
19
+ get tabs() {
20
+ return (this.slotElement
21
+ ?.assignedElements({ flatten: true })
22
+ .filter((el) => this.isTab(el)) ?? []);
23
+ }
24
+ /**
25
+ * The currently selected tab, `null` only when there are no tab children.
26
+ *
27
+ * @export
28
+ */
29
+ get selectedTab() {
30
+ return this.tabs.find((tab) => this.isTabSelected(tab)) ?? null;
31
+ }
32
+ set selectedTab(tab) {
33
+ // Ignore setting selectedTab to null. As long as there are children, one
34
+ // tab must be selected.
35
+ if (tab) {
36
+ this.updateSelectedTab(tab);
37
+ }
38
+ }
39
+ /**
40
+ * The index of the currently selected tab.
41
+ *
42
+ * @export
43
+ */
44
+ get selectedTabIndex() {
45
+ return this.tabs.findIndex((tab) => this.isTabSelected(tab));
46
+ }
47
+ set selectedTabIndex(index) {
48
+ const selectTabAtIndex = () => {
49
+ const tab = this.tabs[index];
50
+ // Ignore out-of-bound indices.
51
+ if (tab) {
52
+ this.updateSelectedTab(tab);
53
+ }
54
+ };
55
+ if (!this.slotElement) {
56
+ // This is needed to support setting the selectedTabIndex via a lit
57
+ // property binding.
58
+ //
59
+ // ```ts
60
+ // html`
61
+ // <md-aria-tablist .selectedTabIndex=${1}>
62
+ // <md-aria-tab>First</md-aria-tab>
63
+ // <md-aria-tab>Second</md-aria-tab>
64
+ // </md-aria-tablist>
65
+ // `;
66
+ // ```
67
+ //
68
+ // It's needed since lit's rendering lifecycle is asynchronous, and the
69
+ // `<slot>` element hasn't rendered, so `tabs` is empty.
70
+ this.updateComplete.then(selectTabAtIndex);
71
+ return;
72
+ }
73
+ selectTabAtIndex();
74
+ }
75
+ get focusedTab() {
76
+ return this.tabs.find((tab) => tab.matches(':focus-within'));
77
+ }
78
+ constructor() {
79
+ super();
80
+ /**
81
+ * Whether or not to automatically select a tab when it is focused.
82
+ */
83
+ this.autoSelect = false;
84
+ /**
85
+ * Orientation of the tablist ('horizontal' or 'vertical').
86
+ */
87
+ this.orientation = 'horizontal';
88
+ if (isServer)
89
+ return;
90
+ this[internals].role = 'tablist';
91
+ setupDispatchHooks(this, 'click', 'keydown');
92
+ this.addEventListener('click', this.handleClick.bind(this));
93
+ this.addEventListener('keydown', this.handleKeydown.bind(this));
94
+ this.addEventListener('focusout', this.handleFocusout.bind(this));
95
+ }
96
+ render() {
97
+ return html `<slot @slotchange=${this.handleSlotChange}></slot>`;
98
+ }
99
+ /**
100
+ * Returns true if the element is a valid tab component or role. Subclasses
101
+ * may override this method to determine if an element is a supported tab.
102
+ */
103
+ isTab(element) {
104
+ return isAriaTabLike(element);
105
+ }
106
+ /**
107
+ * Returns true if the tab is currently selected. Subclasses may override this
108
+ * method to support custom tab selection implementations.
109
+ */
110
+ isTabSelected(tab) {
111
+ if (!isAriaTabLike(tab))
112
+ return false;
113
+ return tab.selected;
114
+ }
115
+ /**
116
+ * Updates the selection state of the tab element and visibility of its
117
+ * tabpanel. Subclasses may override this method to support custom tab
118
+ * selection implementations.
119
+ */
120
+ setTabSelected(tab, isSelected) {
121
+ if (!isAriaTabLike(tab))
122
+ return;
123
+ tab.selected = isSelected;
124
+ const panel = tab.tabpanelElement;
125
+ if (panel) {
126
+ panel.hidden = !isSelected;
127
+ }
128
+ }
129
+ onTabChange(previousTab) {
130
+ this.dispatchEvent(new Event('change', { bubbles: true }));
131
+ }
132
+ updateSelectedTab(tabToSelect) {
133
+ const { tabs } = this;
134
+ const tabToSelectIndex = tabs.indexOf(tabToSelect);
135
+ if (tabToSelectIndex === -1) {
136
+ // Ignore setting selectedTab to a tab element that is not a child.
137
+ return;
138
+ }
139
+ for (const tab of tabs) {
140
+ this.setTabSelected(tab, tab === tabToSelect);
141
+ }
142
+ this.updateFocusableTab(tabToSelect);
143
+ }
144
+ updateFocusableTab(focusableTab) {
145
+ for (const tab of this.tabs) {
146
+ tab.tabIndex = tab === focusableTab ? 0 : -1;
147
+ }
148
+ }
149
+ async handleClick(event) {
150
+ // event.composedPath() needs to be called before dispatch completes.
151
+ const tab = event
152
+ .composedPath()
153
+ .find((el) => this.isTab(el));
154
+ // Allow event to bubble
155
+ afterDispatch(event, () => {
156
+ if (event.defaultPrevented) {
157
+ return;
158
+ }
159
+ if (tab && !this.isTabSelected(tab)) {
160
+ const previousTab = this.selectedTab;
161
+ this.updateSelectedTab(tab);
162
+ this.onTabChange(previousTab);
163
+ }
164
+ });
165
+ }
166
+ handleSlotChange() {
167
+ const tabToSelect = this.selectedTab ?? this.tabs[0];
168
+ if (tabToSelect) {
169
+ // Sync tab selection state when slotted content changes. If the active
170
+ // tab was removed or none selected, auto-select the first tab. There
171
+ // should always be a single selected tab while the tablist has children.
172
+ this.updateSelectedTab(tabToSelect);
173
+ }
174
+ }
175
+ // focus item on keydown and optionally select it
176
+ handleKeydown(event) {
177
+ // Allow event to bubble.
178
+ afterDispatch(event, () => {
179
+ const isLeft = event.key === 'ArrowLeft';
180
+ const isRight = event.key === 'ArrowRight';
181
+ const isUp = event.key === 'ArrowUp';
182
+ const isDown = event.key === 'ArrowDown';
183
+ const isHome = event.key === 'Home';
184
+ const isEnd = event.key === 'End';
185
+ const isVertical = this.orientation === 'vertical';
186
+ const isDirectionKey = isVertical ? isUp || isDown : isLeft || isRight;
187
+ // Ignore non-navigation keys
188
+ if (event.defaultPrevented || (!isDirectionKey && !isHome && !isEnd)) {
189
+ return;
190
+ }
191
+ const { tabs } = this;
192
+ // Don't try to select another tab if there aren't any.
193
+ if (tabs.length < 2) {
194
+ return;
195
+ }
196
+ // Prevent default interactions, such as scrolling.
197
+ event.preventDefault();
198
+ let indexToFocus;
199
+ if (isHome || isEnd) {
200
+ indexToFocus = isHome ? 0 : tabs.length - 1;
201
+ }
202
+ else {
203
+ // Check if moving forwards or backwards
204
+ const isRtl = getComputedStyle(this).direction === 'rtl';
205
+ const forwards = isVertical ? isDown : isRtl ? isLeft : isRight;
206
+ const { focusedTab } = this;
207
+ if (!focusedTab) {
208
+ // If there is not already a tab focused, select the first or last tab
209
+ // based on the direction we're traveling.
210
+ indexToFocus = forwards ? 0 : tabs.length - 1;
211
+ }
212
+ else {
213
+ const focusedIndex = this.tabs.indexOf(focusedTab);
214
+ indexToFocus = forwards ? focusedIndex + 1 : focusedIndex - 1;
215
+ if (indexToFocus >= tabs.length) {
216
+ // Return to start if moving past the last item.
217
+ indexToFocus = 0;
218
+ }
219
+ else if (indexToFocus < 0) {
220
+ // Go to end if moving before the first item.
221
+ indexToFocus = tabs.length - 1;
222
+ }
223
+ }
224
+ }
225
+ const tabToFocus = tabs[indexToFocus];
226
+ tabToFocus.focus();
227
+ if (this.autoSelect) {
228
+ const previousTab = this.selectedTab;
229
+ this.updateSelectedTab(tabToFocus);
230
+ this.onTabChange(previousTab);
231
+ }
232
+ else {
233
+ this.updateFocusableTab(tabToFocus);
234
+ }
235
+ });
236
+ }
237
+ handleFocusout() {
238
+ // restore focus to selected item when blurring the tab bar.
239
+ if (this.matches(':focus-within')) {
240
+ return;
241
+ }
242
+ const { selectedTab } = this;
243
+ if (selectedTab) {
244
+ this.updateFocusableTab(selectedTab);
245
+ }
246
+ }
247
+ }
248
+ AriaTablistElement.styles = css `
249
+ :host {
250
+ display: flex;
251
+ }
252
+ :host([orientation='vertical']) {
253
+ flex-direction: column;
254
+ }
255
+ `;
256
+ __decorate([
257
+ property({ type: Number })
258
+ ], AriaTablistElement.prototype, "selectedTabIndex", null);
259
+ __decorate([
260
+ property({ type: Boolean })
261
+ ], AriaTablistElement.prototype, "autoSelect", void 0);
262
+ __decorate([
263
+ property({ type: String, reflect: true })
264
+ ], AriaTablistElement.prototype, "orientation", void 0);
265
+ __decorate([
266
+ query('slot:not([name])')
267
+ ], AriaTablistElement.prototype, "slotElement", void 0);
268
+ function isAriaTabLike(element) {
269
+ return 'selected' in element && 'tabpanelElement' in element;
270
+ }
271
+ //# sourceMappingURL=tablist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tablist.js","sourceRoot":"","sources":["tablist.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @license\n * Copyright 2023 Google LLC\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport {css, CSSResultGroup, html, isServer, LitElement} from 'lit';\nimport {property, query} from 'lit/decorators.js';\n\nimport {\n afterDispatch,\n setupDispatchHooks,\n} from '../../../internal/events/dispatch-hooks.js';\nimport {\n internals,\n mixinElementInternals,\n} from '../../behaviors/element-internals.js';\n\nconst baseClass = mixinElementInternals(LitElement);\n\n/**\n * An ARIA tablist element.\n */\nexport class AriaTablistElement extends baseClass {\n static override styles: CSSResultGroup = css`\n :host {\n display: flex;\n }\n :host([orientation='vertical']) {\n flex-direction: column;\n }\n `;\n\n /**\n * The tabs of this tablist.\n */\n get tabs(): HTMLElement[] {\n return (\n this.slotElement\n ?.assignedElements({flatten: true})\n .filter((el): el is HTMLElement => this.isTab(el as HTMLElement)) ?? []\n );\n }\n\n /**\n * The currently selected tab, `null` only when there are no tab children.\n *\n * @export\n */\n get selectedTab() {\n return this.tabs.find((tab) => this.isTabSelected(tab)) ?? null;\n }\n set selectedTab(tab: HTMLElement | null) {\n // Ignore setting selectedTab to null. As long as there are children, one\n // tab must be selected.\n if (tab) {\n this.updateSelectedTab(tab);\n }\n }\n\n /**\n * The index of the currently selected tab.\n *\n * @export\n */\n @property({type: Number})\n get selectedTabIndex() {\n return this.tabs.findIndex((tab) => this.isTabSelected(tab));\n }\n set selectedTabIndex(index: number) {\n const selectTabAtIndex = () => {\n const tab = this.tabs[index];\n // Ignore out-of-bound indices.\n if (tab) {\n this.updateSelectedTab(tab);\n }\n };\n\n if (!this.slotElement) {\n // This is needed to support setting the selectedTabIndex via a lit\n // property binding.\n //\n // ```ts\n // html`\n // <md-aria-tablist .selectedTabIndex=${1}>\n // <md-aria-tab>First</md-aria-tab>\n // <md-aria-tab>Second</md-aria-tab>\n // </md-aria-tablist>\n // `;\n // ```\n //\n // It's needed since lit's rendering lifecycle is asynchronous, and the\n // `<slot>` element hasn't rendered, so `tabs` is empty.\n this.updateComplete.then(selectTabAtIndex);\n return;\n }\n\n selectTabAtIndex();\n }\n\n /**\n * Whether or not to automatically select a tab when it is focused.\n */\n @property({type: Boolean}) autoSelect = false;\n\n /**\n * Orientation of the tablist ('horizontal' or 'vertical').\n */\n @property({type: String, reflect: true})\n orientation: 'horizontal' | 'vertical' = 'horizontal';\n\n @query('slot:not([name])')\n private readonly slotElement!: HTMLSlotElement | null;\n\n protected get focusedTab() {\n return this.tabs.find((tab) => tab.matches(':focus-within'));\n }\n\n constructor() {\n super();\n if (isServer) return;\n this[internals].role = 'tablist';\n setupDispatchHooks(this, 'click', 'keydown');\n this.addEventListener('click', this.handleClick.bind(this));\n this.addEventListener('keydown', this.handleKeydown.bind(this));\n this.addEventListener('focusout', this.handleFocusout.bind(this));\n }\n\n protected override render() {\n return html`<slot @slotchange=${this.handleSlotChange}></slot>`;\n }\n\n /**\n * Returns true if the element is a valid tab component or role. Subclasses\n * may override this method to determine if an element is a supported tab.\n */\n protected isTab(element: HTMLElement): boolean {\n return isAriaTabLike(element);\n }\n\n /**\n * Returns true if the tab is currently selected. Subclasses may override this\n * method to support custom tab selection implementations.\n */\n protected isTabSelected(tab: HTMLElement): boolean {\n if (!isAriaTabLike(tab)) return false;\n return tab.selected;\n }\n\n /**\n * Updates the selection state of the tab element and visibility of its\n * tabpanel. Subclasses may override this method to support custom tab\n * selection implementations.\n */\n protected setTabSelected(tab: HTMLElement, isSelected: boolean): void {\n if (!isAriaTabLike(tab)) return;\n tab.selected = isSelected;\n const panel = tab.tabpanelElement as HTMLElement | null;\n if (panel) {\n panel.hidden = !isSelected;\n }\n }\n\n protected onTabChange(previousTab: HTMLElement | null): void {\n this.dispatchEvent(new Event('change', {bubbles: true}));\n }\n\n protected updateSelectedTab(tabToSelect: HTMLElement) {\n const {tabs} = this;\n const tabToSelectIndex = tabs.indexOf(tabToSelect);\n if (tabToSelectIndex === -1) {\n // Ignore setting selectedTab to a tab element that is not a child.\n return;\n }\n\n for (const tab of tabs) {\n this.setTabSelected(tab, tab === tabToSelect);\n }\n\n this.updateFocusableTab(tabToSelect);\n }\n\n protected updateFocusableTab(focusableTab: HTMLElement) {\n for (const tab of this.tabs) {\n tab.tabIndex = tab === focusableTab ? 0 : -1;\n }\n }\n\n private async handleClick(event: Event) {\n // event.composedPath() needs to be called before dispatch completes.\n const tab = event\n .composedPath()\n .find((el): el is HTMLElement => this.isTab(el as HTMLElement));\n\n // Allow event to bubble\n afterDispatch(event, () => {\n if (event.defaultPrevented) {\n return;\n }\n\n if (tab && !this.isTabSelected(tab)) {\n const previousTab = this.selectedTab;\n this.updateSelectedTab(tab);\n this.onTabChange(previousTab);\n }\n });\n }\n\n protected handleSlotChange() {\n const tabToSelect = this.selectedTab ?? this.tabs[0];\n if (tabToSelect) {\n // Sync tab selection state when slotted content changes. If the active\n // tab was removed or none selected, auto-select the first tab. There\n // should always be a single selected tab while the tablist has children.\n this.updateSelectedTab(tabToSelect);\n }\n }\n\n // focus item on keydown and optionally select it\n private handleKeydown(event: KeyboardEvent) {\n // Allow event to bubble.\n afterDispatch(event, () => {\n const isLeft = event.key === 'ArrowLeft';\n const isRight = event.key === 'ArrowRight';\n const isUp = event.key === 'ArrowUp';\n const isDown = event.key === 'ArrowDown';\n const isHome = event.key === 'Home';\n const isEnd = event.key === 'End';\n const isVertical = this.orientation === 'vertical';\n const isDirectionKey = isVertical ? isUp || isDown : isLeft || isRight;\n // Ignore non-navigation keys\n if (event.defaultPrevented || (!isDirectionKey && !isHome && !isEnd)) {\n return;\n }\n\n const {tabs} = this;\n // Don't try to select another tab if there aren't any.\n if (tabs.length < 2) {\n return;\n }\n\n // Prevent default interactions, such as scrolling.\n event.preventDefault();\n\n let indexToFocus: number;\n if (isHome || isEnd) {\n indexToFocus = isHome ? 0 : tabs.length - 1;\n } else {\n // Check if moving forwards or backwards\n const isRtl = getComputedStyle(this).direction === 'rtl';\n const forwards = isVertical ? isDown : isRtl ? isLeft : isRight;\n const {focusedTab} = this;\n if (!focusedTab) {\n // If there is not already a tab focused, select the first or last tab\n // based on the direction we're traveling.\n indexToFocus = forwards ? 0 : tabs.length - 1;\n } else {\n const focusedIndex = this.tabs.indexOf(focusedTab);\n indexToFocus = forwards ? focusedIndex + 1 : focusedIndex - 1;\n if (indexToFocus >= tabs.length) {\n // Return to start if moving past the last item.\n indexToFocus = 0;\n } else if (indexToFocus < 0) {\n // Go to end if moving before the first item.\n indexToFocus = tabs.length - 1;\n }\n }\n }\n\n const tabToFocus = tabs[indexToFocus];\n tabToFocus.focus();\n if (this.autoSelect) {\n const previousTab = this.selectedTab;\n this.updateSelectedTab(tabToFocus);\n this.onTabChange(previousTab);\n } else {\n this.updateFocusableTab(tabToFocus);\n }\n });\n }\n\n private handleFocusout() {\n // restore focus to selected item when blurring the tab bar.\n if (this.matches(':focus-within')) {\n return;\n }\n\n const {selectedTab} = this;\n if (selectedTab) {\n this.updateFocusableTab(selectedTab);\n }\n }\n}\n\ninterface AriaTabLike extends HTMLElement {\n selected: boolean;\n tabpanelElement: Element | null;\n}\n\nfunction isAriaTabLike(element: Element): element is AriaTabLike {\n return 'selected' in element && 'tabpanelElement' in element;\n}\n"]}