@angular/material 10.0.0-rc.3 → 10.1.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 (627) hide show
  1. package/_theming.scss +17 -17
  2. package/autocomplete/index.metadata.json +1 -1
  3. package/autocomplete/testing/autocomplete-harness.d.ts +2 -0
  4. package/bundles/material-autocomplete-testing.umd.js +11 -0
  5. package/bundles/material-autocomplete-testing.umd.js.map +1 -1
  6. package/bundles/material-autocomplete-testing.umd.min.js +2 -2
  7. package/bundles/material-autocomplete-testing.umd.min.js.map +1 -1
  8. package/bundles/material-autocomplete.umd.js +3 -1
  9. package/bundles/material-autocomplete.umd.js.map +1 -1
  10. package/bundles/material-autocomplete.umd.min.js +5 -5
  11. package/bundles/material-autocomplete.umd.min.js.map +1 -1
  12. package/bundles/material-badge-testing.umd.min.js +1 -1
  13. package/bundles/material-badge-testing.umd.min.js.map +1 -1
  14. package/bundles/material-bottom-sheet-testing.umd.min.js +1 -1
  15. package/bundles/material-bottom-sheet-testing.umd.min.js.map +1 -1
  16. package/bundles/material-button-testing.umd.js +11 -0
  17. package/bundles/material-button-testing.umd.js.map +1 -1
  18. package/bundles/material-button-testing.umd.min.js +2 -2
  19. package/bundles/material-button-testing.umd.min.js.map +1 -1
  20. package/bundles/material-button-toggle-testing.umd.js +11 -0
  21. package/bundles/material-button-toggle-testing.umd.js.map +1 -1
  22. package/bundles/material-button-toggle-testing.umd.min.js +3 -3
  23. package/bundles/material-button-toggle-testing.umd.min.js.map +1 -1
  24. package/bundles/material-button-toggle.umd.js +12 -3
  25. package/bundles/material-button-toggle.umd.js.map +1 -1
  26. package/bundles/material-button-toggle.umd.min.js +2 -2
  27. package/bundles/material-button-toggle.umd.min.js.map +1 -1
  28. package/bundles/material-button.umd.js +7 -2
  29. package/bundles/material-button.umd.js.map +1 -1
  30. package/bundles/material-button.umd.min.js +4 -4
  31. package/bundles/material-button.umd.min.js.map +1 -1
  32. package/bundles/material-card-testing.umd.js +335 -0
  33. package/bundles/material-card-testing.umd.js.map +1 -0
  34. package/bundles/material-card-testing.umd.min.js +44 -0
  35. package/bundles/material-card-testing.umd.min.js.map +1 -0
  36. package/bundles/material-checkbox-testing.umd.js +11 -0
  37. package/bundles/material-checkbox-testing.umd.js.map +1 -1
  38. package/bundles/material-checkbox-testing.umd.min.js +3 -3
  39. package/bundles/material-checkbox-testing.umd.min.js.map +1 -1
  40. package/bundles/material-checkbox.umd.js.map +1 -1
  41. package/bundles/material-chips.umd.js +36 -12
  42. package/bundles/material-chips.umd.js.map +1 -1
  43. package/bundles/material-chips.umd.min.js +4 -4
  44. package/bundles/material-chips.umd.min.js.map +1 -1
  45. package/bundles/material-core-testing.umd.min.js +1 -1
  46. package/bundles/material-core-testing.umd.min.js.map +1 -1
  47. package/bundles/material-core.umd.js +11 -3
  48. package/bundles/material-core.umd.js.map +1 -1
  49. package/bundles/material-core.umd.min.js +11 -11
  50. package/bundles/material-core.umd.min.js.map +1 -1
  51. package/bundles/material-datepicker.umd.js +20 -11
  52. package/bundles/material-datepicker.umd.js.map +1 -1
  53. package/bundles/material-datepicker.umd.min.js +5 -5
  54. package/bundles/material-datepicker.umd.min.js.map +1 -1
  55. package/bundles/material-dialog-testing.umd.min.js +1 -1
  56. package/bundles/material-dialog-testing.umd.min.js.map +1 -1
  57. package/bundles/material-dialog.umd.js +45 -8
  58. package/bundles/material-dialog.umd.js.map +1 -1
  59. package/bundles/material-dialog.umd.min.js +14 -7
  60. package/bundles/material-dialog.umd.min.js.map +1 -1
  61. package/bundles/material-divider-testing.umd.min.js +1 -1
  62. package/bundles/material-divider-testing.umd.min.js.map +1 -1
  63. package/bundles/material-expansion-testing.umd.js +11 -0
  64. package/bundles/material-expansion-testing.umd.js.map +1 -1
  65. package/bundles/material-expansion-testing.umd.min.js +3 -3
  66. package/bundles/material-expansion-testing.umd.min.js.map +1 -1
  67. package/bundles/material-form-field-testing.umd.js +32 -0
  68. package/bundles/material-form-field-testing.umd.js.map +1 -1
  69. package/bundles/material-form-field-testing.umd.min.js +4 -4
  70. package/bundles/material-form-field-testing.umd.min.js.map +1 -1
  71. package/bundles/material-form-field.umd.js +47 -21
  72. package/bundles/material-form-field.umd.js.map +1 -1
  73. package/bundles/material-form-field.umd.min.js +5 -5
  74. package/bundles/material-form-field.umd.min.js.map +1 -1
  75. package/bundles/material-grid-list-testing.umd.min.js +1 -1
  76. package/bundles/material-grid-list-testing.umd.min.js.map +1 -1
  77. package/bundles/material-grid-list.umd.js +2 -2
  78. package/bundles/material-grid-list.umd.js.map +1 -1
  79. package/bundles/material-grid-list.umd.min.js +2 -2
  80. package/bundles/material-grid-list.umd.min.js.map +1 -1
  81. package/bundles/material-input-testing.umd.js +23 -6
  82. package/bundles/material-input-testing.umd.js.map +1 -1
  83. package/bundles/material-input-testing.umd.min.js +3 -3
  84. package/bundles/material-input-testing.umd.min.js.map +1 -1
  85. package/bundles/material-input.umd.js +28 -3
  86. package/bundles/material-input.umd.js.map +1 -1
  87. package/bundles/material-input.umd.min.js +3 -3
  88. package/bundles/material-input.umd.min.js.map +1 -1
  89. package/bundles/material-list-testing.umd.js +33 -0
  90. package/bundles/material-list-testing.umd.js.map +1 -1
  91. package/bundles/material-list-testing.umd.min.js +2 -2
  92. package/bundles/material-list-testing.umd.min.js.map +1 -1
  93. package/bundles/material-list.umd.js +3 -3
  94. package/bundles/material-list.umd.min.js +1 -1
  95. package/bundles/material-list.umd.min.js.map +1 -1
  96. package/bundles/material-menu-testing.umd.js +22 -0
  97. package/bundles/material-menu-testing.umd.js.map +1 -1
  98. package/bundles/material-menu-testing.umd.min.js +2 -2
  99. package/bundles/material-menu-testing.umd.min.js.map +1 -1
  100. package/bundles/material-menu.umd.js +13 -2
  101. package/bundles/material-menu.umd.js.map +1 -1
  102. package/bundles/material-menu.umd.min.js +4 -4
  103. package/bundles/material-menu.umd.min.js.map +1 -1
  104. package/bundles/material-paginator-testing.umd.min.js +1 -1
  105. package/bundles/material-paginator-testing.umd.min.js.map +1 -1
  106. package/bundles/material-progress-bar-testing.umd.min.js +1 -1
  107. package/bundles/material-progress-bar-testing.umd.min.js.map +1 -1
  108. package/bundles/material-progress-spinner-testing.umd.js +1 -1
  109. package/bundles/material-progress-spinner-testing.umd.js.map +1 -1
  110. package/bundles/material-progress-spinner-testing.umd.min.js +2 -2
  111. package/bundles/material-progress-spinner-testing.umd.min.js.map +1 -1
  112. package/bundles/material-progress-spinner.umd.js +2 -2
  113. package/bundles/material-progress-spinner.umd.min.js +3 -3
  114. package/bundles/material-progress-spinner.umd.min.js.map +1 -1
  115. package/bundles/material-radio-testing.umd.js +11 -0
  116. package/bundles/material-radio-testing.umd.js.map +1 -1
  117. package/bundles/material-radio-testing.umd.min.js +3 -3
  118. package/bundles/material-radio-testing.umd.min.js.map +1 -1
  119. package/bundles/material-radio.umd.js +15 -5
  120. package/bundles/material-radio.umd.js.map +1 -1
  121. package/bundles/material-radio.umd.min.js +2 -2
  122. package/bundles/material-radio.umd.min.js.map +1 -1
  123. package/bundles/material-select-testing.umd.js +11 -0
  124. package/bundles/material-select-testing.umd.js.map +1 -1
  125. package/bundles/material-select-testing.umd.min.js +2 -2
  126. package/bundles/material-select-testing.umd.min.js.map +1 -1
  127. package/bundles/material-select.umd.js +11 -3
  128. package/bundles/material-select.umd.js.map +1 -1
  129. package/bundles/material-select.umd.min.js +4 -4
  130. package/bundles/material-select.umd.min.js.map +1 -1
  131. package/bundles/material-sidenav-testing.umd.min.js +1 -1
  132. package/bundles/material-sidenav-testing.umd.min.js.map +1 -1
  133. package/bundles/material-sidenav.umd.js +41 -17
  134. package/bundles/material-sidenav.umd.js.map +1 -1
  135. package/bundles/material-sidenav.umd.min.js +2 -2
  136. package/bundles/material-sidenav.umd.min.js.map +1 -1
  137. package/bundles/material-slide-toggle-testing.umd.js +11 -0
  138. package/bundles/material-slide-toggle-testing.umd.js.map +1 -1
  139. package/bundles/material-slide-toggle-testing.umd.min.js +2 -2
  140. package/bundles/material-slide-toggle-testing.umd.min.js.map +1 -1
  141. package/bundles/material-slide-toggle.umd.js.map +1 -1
  142. package/bundles/material-slider-testing.umd.js +11 -0
  143. package/bundles/material-slider-testing.umd.js.map +1 -1
  144. package/bundles/material-slider-testing.umd.min.js +2 -2
  145. package/bundles/material-slider-testing.umd.min.js.map +1 -1
  146. package/bundles/material-snack-bar-testing.umd.min.js +1 -1
  147. package/bundles/material-snack-bar-testing.umd.min.js.map +1 -1
  148. package/bundles/material-snack-bar.umd.js +47 -39
  149. package/bundles/material-snack-bar.umd.js.map +1 -1
  150. package/bundles/material-snack-bar.umd.min.js +2 -2
  151. package/bundles/material-snack-bar.umd.min.js.map +1 -1
  152. package/bundles/material-sort-testing.umd.js +10 -10
  153. package/bundles/material-sort-testing.umd.js.map +1 -1
  154. package/bundles/material-sort-testing.umd.min.js +3 -3
  155. package/bundles/material-sort-testing.umd.min.js.map +1 -1
  156. package/bundles/material-sort.umd.js +25 -12
  157. package/bundles/material-sort.umd.js.map +1 -1
  158. package/bundles/material-sort.umd.min.js +5 -5
  159. package/bundles/material-sort.umd.min.js.map +1 -1
  160. package/bundles/material-table-testing.umd.min.js +1 -1
  161. package/bundles/material-table-testing.umd.min.js.map +1 -1
  162. package/bundles/material-table.umd.min.js +2 -2
  163. package/bundles/material-table.umd.min.js.map +1 -1
  164. package/bundles/material-tabs-testing.umd.min.js +1 -1
  165. package/bundles/material-tabs-testing.umd.min.js.map +1 -1
  166. package/bundles/material-tabs.umd.js +23 -4
  167. package/bundles/material-tabs.umd.js.map +1 -1
  168. package/bundles/material-tabs.umd.min.js +5 -12
  169. package/bundles/material-tabs.umd.min.js.map +1 -1
  170. package/bundles/material-toolbar-testing.umd.js +330 -0
  171. package/bundles/material-toolbar-testing.umd.js.map +1 -0
  172. package/bundles/material-toolbar-testing.umd.min.js +44 -0
  173. package/bundles/material-toolbar-testing.umd.min.js.map +1 -0
  174. package/bundles/material-tooltip-testing.umd.min.js +1 -1
  175. package/bundles/material-tooltip-testing.umd.min.js.map +1 -1
  176. package/bundles/material-tooltip.umd.js +14 -1
  177. package/bundles/material-tooltip.umd.js.map +1 -1
  178. package/bundles/material-tooltip.umd.min.js +3 -3
  179. package/bundles/material-tooltip.umd.min.js.map +1 -1
  180. package/bundles/material-tree.umd.js.map +1 -1
  181. package/button/_button-base.scss +1 -1
  182. package/button/_button-theme.scss +8 -8
  183. package/button/index.metadata.json +1 -1
  184. package/button/testing/button-harness.d.ts +2 -0
  185. package/button-toggle/button-toggle.d.ts +10 -3
  186. package/button-toggle/index.metadata.json +1 -1
  187. package/button-toggle/testing/button-toggle-harness.d.ts +2 -0
  188. package/card/testing/card-harness-filters.d.ts +17 -0
  189. package/card/testing/card-harness.d.ts +36 -0
  190. package/card/testing/index.d.ts +8 -0
  191. package/card/testing/package.json +9 -0
  192. package/card/testing/public-api.d.ts +9 -0
  193. package/checkbox/index.metadata.json +1 -1
  194. package/checkbox/testing/checkbox-harness.d.ts +2 -0
  195. package/chips/chip.d.ts +19 -1
  196. package/chips/index.metadata.json +1 -1
  197. package/core/focus-indicators/_focus-indicators.scss +6 -6
  198. package/core/index.metadata.json +1 -1
  199. package/core/option/optgroup.d.ts +7 -0
  200. package/core/ripple/ripple-ref.d.ts +21 -2
  201. package/core/ripple/ripple-renderer.d.ts +1 -19
  202. package/core/ripple/ripple.d.ts +2 -2
  203. package/core/style/_list-common.scss +1 -1
  204. package/datepicker/date-range-input-parts.d.ts +2 -4
  205. package/datepicker/date-range-input.d.ts +4 -4
  206. package/datepicker/date-range-picker.d.ts +10 -3
  207. package/datepicker/datepicker.d.ts +2 -3
  208. package/datepicker/index.metadata.json +1 -1
  209. package/datepicker/public-api.d.ts +1 -1
  210. package/dialog/dialog-container.d.ts +9 -2
  211. package/dialog/dialog-content-directives.d.ts +1 -0
  212. package/dialog/dialog-ref.d.ts +7 -0
  213. package/dialog/index.metadata.json +1 -1
  214. package/esm2015/autocomplete/autocomplete-module.js +18 -22
  215. package/esm2015/autocomplete/autocomplete-origin.js +16 -20
  216. package/esm2015/autocomplete/autocomplete-trigger.js +519 -521
  217. package/esm2015/autocomplete/autocomplete.js +126 -130
  218. package/esm2015/autocomplete/testing/autocomplete-harness.js +99 -97
  219. package/esm2015/badge/badge-module.js +13 -17
  220. package/esm2015/badge/badge.js +184 -188
  221. package/esm2015/badge/testing/badge-harness.js +74 -78
  222. package/esm2015/bottom-sheet/bottom-sheet-container.js +161 -165
  223. package/esm2015/bottom-sheet/bottom-sheet-module.js +15 -19
  224. package/esm2015/bottom-sheet/bottom-sheet.js +124 -128
  225. package/esm2015/bottom-sheet/testing/bottom-sheet-harness.js +29 -33
  226. package/esm2015/button/button-module.js +20 -24
  227. package/esm2015/button/button.js +111 -114
  228. package/esm2015/button/testing/button-harness.js +60 -58
  229. package/esm2015/button-toggle/button-toggle-module.js +10 -14
  230. package/esm2015/button-toggle/button-toggle.js +368 -368
  231. package/esm2015/button-toggle/testing/button-toggle-group-harness.js +42 -46
  232. package/esm2015/button-toggle/testing/button-toggle-harness.js +113 -111
  233. package/esm2015/card/card-module.js +30 -34
  234. package/esm2015/card/card.js +147 -203
  235. package/esm2015/card/testing/card-harness-filters.js +8 -0
  236. package/esm2015/card/testing/card-harness.js +52 -0
  237. package/esm2015/card/testing/index.js +9 -0
  238. package/esm2015/card/testing/public-api.js +10 -0
  239. package/esm2015/card/testing/testing.externs.js +0 -0
  240. package/esm2015/checkbox/checkbox-module.js +21 -29
  241. package/esm2015/checkbox/checkbox-required-validator.js +9 -13
  242. package/esm2015/checkbox/checkbox.js +315 -319
  243. package/esm2015/checkbox/testing/checkbox-harness.js +150 -148
  244. package/esm2015/chips/chip-input.js +119 -123
  245. package/esm2015/chips/chip-list.js +572 -576
  246. package/esm2015/chips/chip.js +333 -328
  247. package/esm2015/chips/chips-module.js +16 -20
  248. package/esm2015/core/animation/animation.js +12 -20
  249. package/esm2015/core/common-behaviors/common-module.js +91 -95
  250. package/esm2015/core/datetime/index.js +19 -27
  251. package/esm2015/core/datetime/native-date-adapter.js +202 -206
  252. package/esm2015/core/error/error-options.js +16 -24
  253. package/esm2015/core/line/line.js +18 -26
  254. package/esm2015/core/option/index.js +10 -14
  255. package/esm2015/core/option/optgroup.js +36 -33
  256. package/esm2015/core/option/option.js +181 -185
  257. package/esm2015/core/ripple/index.js +10 -14
  258. package/esm2015/core/ripple/ripple-ref.js +1 -1
  259. package/esm2015/core/ripple/ripple-renderer.js +1 -1
  260. package/esm2015/core/ripple/ripple.js +102 -106
  261. package/esm2015/core/selection/index.js +9 -13
  262. package/esm2015/core/selection/pseudo-checkbox/pseudo-checkbox.js +32 -36
  263. package/esm2015/core/testing/optgroup-harness.js +39 -43
  264. package/esm2015/core/testing/option-harness.js +51 -55
  265. package/esm2015/core/version.js +1 -1
  266. package/esm2015/datepicker/calendar-body.js +214 -218
  267. package/esm2015/datepicker/calendar.js +295 -303
  268. package/esm2015/datepicker/date-range-input-parts.js +214 -229
  269. package/esm2015/datepicker/date-range-input.js +244 -242
  270. package/esm2015/datepicker/date-range-picker.js +19 -23
  271. package/esm2015/datepicker/date-range-selection-strategy.js +31 -35
  272. package/esm2015/datepicker/date-selection-model.js +110 -122
  273. package/esm2015/datepicker/datepicker-base.js +418 -426
  274. package/esm2015/datepicker/datepicker-input-base.js +227 -224
  275. package/esm2015/datepicker/datepicker-input.js +111 -115
  276. package/esm2015/datepicker/datepicker-intl.js +37 -41
  277. package/esm2015/datepicker/datepicker-module.js +62 -66
  278. package/esm2015/datepicker/datepicker-toggle.js +81 -89
  279. package/esm2015/datepicker/datepicker.js +13 -17
  280. package/esm2015/datepicker/month-view.js +283 -287
  281. package/esm2015/datepicker/multi-year-view.js +198 -202
  282. package/esm2015/datepicker/public-api.js +2 -2
  283. package/esm2015/datepicker/year-view.js +220 -224
  284. package/esm2015/dialog/dialog-container.js +175 -164
  285. package/esm2015/dialog/dialog-content-directives.js +105 -114
  286. package/esm2015/dialog/dialog-module.js +32 -36
  287. package/esm2015/dialog/dialog-ref.js +17 -3
  288. package/esm2015/dialog/dialog.js +236 -240
  289. package/esm2015/dialog/testing/dialog-harness.js +58 -62
  290. package/esm2015/divider/divider-module.js +10 -14
  291. package/esm2015/divider/divider.js +33 -37
  292. package/esm2015/divider/testing/divider-harness.js +16 -20
  293. package/esm2015/expansion/accordion.js +73 -77
  294. package/esm2015/expansion/expansion-module.js +26 -30
  295. package/esm2015/expansion/expansion-panel-content.js +13 -17
  296. package/esm2015/expansion/expansion-panel-header.js +168 -180
  297. package/esm2015/expansion/expansion-panel.js +144 -152
  298. package/esm2015/expansion/testing/accordion-harness.js +24 -28
  299. package/esm2015/expansion/testing/expansion-harness.js +136 -134
  300. package/esm2015/form-field/error.js +26 -23
  301. package/esm2015/form-field/form-field-control.js +6 -10
  302. package/esm2015/form-field/form-field-module.js +31 -35
  303. package/esm2015/form-field/form-field.js +407 -409
  304. package/esm2015/form-field/hint.js +35 -29
  305. package/esm2015/form-field/label.js +8 -12
  306. package/esm2015/form-field/placeholder.js +8 -12
  307. package/esm2015/form-field/prefix.js +16 -13
  308. package/esm2015/form-field/suffix.js +16 -13
  309. package/esm2015/form-field/testing/form-field-harness.js +220 -206
  310. package/esm2015/grid-list/grid-list-module.js +26 -30
  311. package/esm2015/grid-list/grid-list.js +108 -112
  312. package/esm2015/grid-list/grid-tile.js +91 -111
  313. package/esm2015/grid-list/testing/grid-list-harness.js +62 -66
  314. package/esm2015/grid-list/testing/grid-tile-harness.js +69 -73
  315. package/esm2015/grid-list/tile-styler.js +1 -1
  316. package/esm2015/icon/icon-module.js +10 -14
  317. package/esm2015/icon/icon-registry.js +406 -410
  318. package/esm2015/icon/icon.js +228 -232
  319. package/esm2015/icon/testing/fake-icon-registry.js +66 -74
  320. package/esm2015/input/autosize.js +30 -34
  321. package/esm2015/input/input-module.js +21 -25
  322. package/esm2015/input/input.js +305 -284
  323. package/esm2015/input/testing/input-harness.js +129 -123
  324. package/esm2015/list/list-module.js +32 -36
  325. package/esm2015/list/list.js +165 -189
  326. package/esm2015/list/selection-list.js +503 -511
  327. package/esm2015/list/testing/action-list-harness.js +55 -57
  328. package/esm2015/list/testing/list-harness.js +31 -39
  329. package/esm2015/list/testing/list-item-harness-base.js +13 -17
  330. package/esm2015/list/testing/nav-list-harness.js +62 -64
  331. package/esm2015/list/testing/selection-list-harness.js +136 -138
  332. package/esm2015/menu/menu-content.js +74 -71
  333. package/esm2015/menu/menu-item.js +119 -123
  334. package/esm2015/menu/menu-module.js +29 -37
  335. package/esm2015/menu/menu-panel.js +1 -1
  336. package/esm2015/menu/menu-trigger.js +402 -405
  337. package/esm2015/menu/menu.js +329 -339
  338. package/esm2015/menu/testing/menu-harness.js +193 -189
  339. package/esm2015/paginator/paginator-intl.js +36 -40
  340. package/esm2015/paginator/paginator-module.js +16 -20
  341. package/esm2015/paginator/paginator.js +205 -209
  342. package/esm2015/paginator/testing/paginator-harness.js +91 -95
  343. package/esm2015/progress-bar/progress-bar-module.js +10 -14
  344. package/esm2015/progress-bar/progress-bar.js +114 -118
  345. package/esm2015/progress-bar/testing/progress-bar-harness.js +27 -31
  346. package/esm2015/progress-spinner/progress-spinner-module.js +17 -21
  347. package/esm2015/progress-spinner/progress-spinner.js +181 -189
  348. package/esm2015/progress-spinner/testing/progress-spinner-harness.js +28 -32
  349. package/esm2015/radio/radio-module.js +10 -14
  350. package/esm2015/radio/radio.js +437 -444
  351. package/esm2015/radio/testing/radio-harness.js +241 -243
  352. package/esm2015/select/select-module.js +23 -27
  353. package/esm2015/select/select.js +917 -918
  354. package/esm2015/select/testing/select-harness.js +138 -136
  355. package/esm2015/sidenav/drawer.js +632 -620
  356. package/esm2015/sidenav/sidenav-module.js +31 -35
  357. package/esm2015/sidenav/sidenav.js +104 -116
  358. package/esm2015/sidenav/testing/drawer-harness.js +40 -44
  359. package/esm2015/sidenav/testing/sidenav-harness.js +20 -24
  360. package/esm2015/slide-toggle/slide-toggle-module.js +27 -35
  361. package/esm2015/slide-toggle/slide-toggle-required-validator.js +9 -13
  362. package/esm2015/slide-toggle/slide-toggle.js +184 -188
  363. package/esm2015/slide-toggle/testing/slide-toggle-harness.js +123 -121
  364. package/esm2015/slider/slider-module.js +10 -14
  365. package/esm2015/slider/slider.js +640 -644
  366. package/esm2015/slider/testing/slider-harness.js +129 -127
  367. package/esm2015/snack-bar/simple-snack-bar.js +32 -36
  368. package/esm2015/snack-bar/snack-bar-container.js +144 -148
  369. package/esm2015/snack-bar/snack-bar-module.js +17 -21
  370. package/esm2015/snack-bar/snack-bar-ref.js +1 -1
  371. package/esm2015/snack-bar/snack-bar.js +208 -205
  372. package/esm2015/snack-bar/testing/snack-bar-harness.js +112 -116
  373. package/esm2015/sort/sort-header-intl.js +21 -21
  374. package/esm2015/sort/sort-header.js +200 -194
  375. package/esm2015/sort/sort-module.js +11 -15
  376. package/esm2015/sort/sort.js +92 -96
  377. package/esm2015/sort/testing/sort-harness.js +28 -32
  378. package/esm2015/sort/testing/sort-header-harness.js +66 -67
  379. package/esm2015/stepper/step-header.js +78 -82
  380. package/esm2015/stepper/step-label.js +8 -12
  381. package/esm2015/stepper/stepper-button.js +23 -31
  382. package/esm2015/stepper/stepper-icon.js +16 -20
  383. package/esm2015/stepper/stepper-intl.js +15 -19
  384. package/esm2015/stepper/stepper-module.js +40 -44
  385. package/esm2015/stepper/stepper.js +139 -155
  386. package/esm2015/table/cell.js +94 -122
  387. package/esm2015/table/row.js +90 -118
  388. package/esm2015/table/table-module.js +13 -17
  389. package/esm2015/table/table.js +26 -30
  390. package/esm2015/table/testing/cell-harness.js +56 -68
  391. package/esm2015/table/testing/row-harness.js +90 -102
  392. package/esm2015/table/testing/table-harness.js +65 -69
  393. package/esm2015/table/text-column.js +17 -21
  394. package/esm2015/tabs/index.js +4 -2
  395. package/esm2015/tabs/ink-bar.js +55 -59
  396. package/esm2015/tabs/paginated-tab-header.js +415 -419
  397. package/esm2015/tabs/tab-body.js +179 -191
  398. package/esm2015/tabs/tab-content.js +21 -16
  399. package/esm2015/tabs/tab-group.js +263 -271
  400. package/esm2015/tabs/tab-header.js +69 -77
  401. package/esm2015/tabs/tab-label-wrapper.js +29 -33
  402. package/esm2015/tabs/tab-label.js +16 -13
  403. package/esm2015/tabs/tab-nav-bar/tab-nav-bar.js +199 -215
  404. package/esm2015/tabs/tab.js +80 -83
  405. package/esm2015/tabs/tabs-module.js +38 -42
  406. package/esm2015/tabs/testing/tab-group-harness.js +52 -56
  407. package/esm2015/tabs/testing/tab-harness.js +78 -82
  408. package/esm2015/toolbar/testing/index.js +9 -0
  409. package/esm2015/toolbar/testing/public-api.js +10 -0
  410. package/esm2015/toolbar/testing/testing.externs.js +0 -0
  411. package/esm2015/toolbar/testing/toolbar-harness-filters.js +8 -0
  412. package/esm2015/toolbar/testing/toolbar-harness.js +47 -0
  413. package/esm2015/toolbar/toolbar-module.js +10 -14
  414. package/esm2015/toolbar/toolbar.js +61 -69
  415. package/esm2015/tooltip/testing/tooltip-harness.js +43 -47
  416. package/esm2015/tooltip/tooltip-module.js +17 -21
  417. package/esm2015/tooltip/tooltip.js +502 -497
  418. package/esm2015/tree/data-source/flat-data-source.js +1 -1
  419. package/esm2015/tree/node.js +99 -111
  420. package/esm2015/tree/outlet.js +19 -23
  421. package/esm2015/tree/padding.js +13 -17
  422. package/esm2015/tree/toggle.js +15 -19
  423. package/esm2015/tree/tree-module.js +10 -14
  424. package/esm2015/tree/tree.js +23 -27
  425. package/expansion/testing/expansion-harness.d.ts +2 -0
  426. package/fesm2015/autocomplete/testing.js +98 -95
  427. package/fesm2015/autocomplete/testing.js.map +1 -1
  428. package/fesm2015/autocomplete.js +677 -687
  429. package/fesm2015/autocomplete.js.map +1 -1
  430. package/fesm2015/badge/testing.js +73 -76
  431. package/fesm2015/badge/testing.js.map +1 -1
  432. package/fesm2015/badge.js +195 -201
  433. package/fesm2015/badge.js.map +1 -1
  434. package/fesm2015/bottom-sheet/testing.js +28 -31
  435. package/fesm2015/bottom-sheet/testing.js.map +1 -1
  436. package/fesm2015/bottom-sheet.js +297 -306
  437. package/fesm2015/bottom-sheet.js.map +1 -1
  438. package/fesm2015/button/testing.js +59 -56
  439. package/fesm2015/button/testing.js.map +1 -1
  440. package/fesm2015/button-toggle/testing.js +155 -155
  441. package/fesm2015/button-toggle/testing.js.map +1 -1
  442. package/fesm2015/button-toggle.js +377 -378
  443. package/fesm2015/button-toggle.js.map +1 -1
  444. package/fesm2015/button.js +129 -133
  445. package/fesm2015/button.js.map +1 -1
  446. package/fesm2015/card/testing.js +79 -0
  447. package/fesm2015/card/testing.js.map +1 -0
  448. package/fesm2015/card.js +175 -220
  449. package/fesm2015/card.js.map +1 -1
  450. package/fesm2015/checkbox/testing.js +149 -146
  451. package/fesm2015/checkbox/testing.js.map +1 -1
  452. package/fesm2015/checkbox.js +342 -354
  453. package/fesm2015/checkbox.js.map +1 -1
  454. package/fesm2015/chips.js +1039 -1039
  455. package/fesm2015/chips.js.map +1 -1
  456. package/fesm2015/core/testing.js +88 -94
  457. package/fesm2015/core/testing.js.map +1 -1
  458. package/fesm2015/core.js +729 -773
  459. package/fesm2015/core.js.map +1 -1
  460. package/fesm2015/datepicker.js +2821 -2884
  461. package/fesm2015/datepicker.js.map +1 -1
  462. package/fesm2015/dialog/testing.js +57 -60
  463. package/fesm2015/dialog/testing.js.map +1 -1
  464. package/fesm2015/dialog.js +564 -549
  465. package/fesm2015/dialog.js.map +1 -1
  466. package/fesm2015/divider/testing.js +15 -18
  467. package/fesm2015/divider/testing.js.map +1 -1
  468. package/fesm2015/divider.js +41 -47
  469. package/fesm2015/divider.js.map +1 -1
  470. package/fesm2015/expansion/testing.js +159 -159
  471. package/fesm2015/expansion/testing.js.map +1 -1
  472. package/fesm2015/expansion.js +422 -446
  473. package/fesm2015/expansion.js.map +1 -1
  474. package/fesm2015/form-field/testing.js +219 -204
  475. package/fesm2015/form-field/testing.js.map +1 -1
  476. package/fesm2015/form-field.js +538 -532
  477. package/fesm2015/form-field.js.map +1 -1
  478. package/fesm2015/grid-list/testing.js +129 -135
  479. package/fesm2015/grid-list/testing.js.map +1 -1
  480. package/fesm2015/grid-list.js +221 -242
  481. package/fesm2015/grid-list.js.map +1 -1
  482. package/fesm2015/icon/testing.js +65 -71
  483. package/fesm2015/icon/testing.js.map +1 -1
  484. package/fesm2015/icon.js +660 -669
  485. package/fesm2015/icon.js.map +1 -1
  486. package/fesm2015/input/testing.js +128 -121
  487. package/fesm2015/input/testing.js.map +1 -1
  488. package/fesm2015/input.js +353 -337
  489. package/fesm2015/input.js.map +1 -1
  490. package/fesm2015/list/testing.js +298 -307
  491. package/fesm2015/list/testing.js.map +1 -1
  492. package/fesm2015/list.js +698 -725
  493. package/fesm2015/list.js.map +1 -1
  494. package/fesm2015/menu/testing.js +192 -186
  495. package/fesm2015/menu/testing.js.map +1 -1
  496. package/fesm2015/menu.js +948 -962
  497. package/fesm2015/menu.js.map +1 -1
  498. package/fesm2015/paginator/testing.js +90 -93
  499. package/fesm2015/paginator/testing.js.map +1 -1
  500. package/fesm2015/paginator.js +247 -256
  501. package/fesm2015/paginator.js.map +1 -1
  502. package/fesm2015/progress-bar/testing.js +26 -29
  503. package/fesm2015/progress-bar/testing.js.map +1 -1
  504. package/fesm2015/progress-bar.js +122 -128
  505. package/fesm2015/progress-bar.js.map +1 -1
  506. package/fesm2015/progress-spinner/testing.js +27 -30
  507. package/fesm2015/progress-spinner/testing.js.map +1 -1
  508. package/fesm2015/progress-spinner.js +196 -205
  509. package/fesm2015/progress-spinner.js.map +1 -1
  510. package/fesm2015/radio/testing.js +240 -240
  511. package/fesm2015/radio/testing.js.map +1 -1
  512. package/fesm2015/radio.js +447 -453
  513. package/fesm2015/radio.js.map +1 -1
  514. package/fesm2015/select/testing.js +137 -134
  515. package/fesm2015/select/testing.js.map +1 -1
  516. package/fesm2015/select.js +939 -941
  517. package/fesm2015/select.js.map +1 -1
  518. package/fesm2015/sidenav/testing.js +58 -64
  519. package/fesm2015/sidenav/testing.js.map +1 -1
  520. package/fesm2015/sidenav.js +765 -762
  521. package/fesm2015/sidenav.js.map +1 -1
  522. package/fesm2015/slide-toggle/testing.js +122 -119
  523. package/fesm2015/slide-toggle/testing.js.map +1 -1
  524. package/fesm2015/slide-toggle.js +217 -229
  525. package/fesm2015/slide-toggle.js.map +1 -1
  526. package/fesm2015/slider/testing.js +128 -125
  527. package/fesm2015/slider/testing.js.map +1 -1
  528. package/fesm2015/slider.js +648 -654
  529. package/fesm2015/slider.js.map +1 -1
  530. package/fesm2015/snack-bar/testing.js +111 -114
  531. package/fesm2015/snack-bar/testing.js.map +1 -1
  532. package/fesm2015/snack-bar.js +426 -431
  533. package/fesm2015/snack-bar.js.map +1 -1
  534. package/fesm2015/sort/testing.js +92 -95
  535. package/fesm2015/sort/testing.js.map +1 -1
  536. package/fesm2015/sort.js +320 -318
  537. package/fesm2015/sort.js.map +1 -1
  538. package/fesm2015/stepper.js +312 -345
  539. package/fesm2015/stepper.js.map +1 -1
  540. package/fesm2015/table/testing.js +208 -229
  541. package/fesm2015/table/testing.js.map +1 -1
  542. package/fesm2015/table.js +235 -286
  543. package/fesm2015/table.js.map +1 -1
  544. package/fesm2015/tabs/testing.js +128 -134
  545. package/fesm2015/tabs/testing.js.map +1 -1
  546. package/fesm2015/tabs.js +1362 -1399
  547. package/fesm2015/tabs.js.map +1 -1
  548. package/fesm2015/toolbar/testing.js +74 -0
  549. package/fesm2015/toolbar/testing.js.map +1 -0
  550. package/fesm2015/toolbar.js +69 -78
  551. package/fesm2015/toolbar.js.map +1 -1
  552. package/fesm2015/tooltip/testing.js +42 -45
  553. package/fesm2015/tooltip/testing.js.map +1 -1
  554. package/fesm2015/tooltip.js +517 -513
  555. package/fesm2015/tooltip.js.map +1 -1
  556. package/fesm2015/tree.js +173 -197
  557. package/fesm2015/tree.js.map +1 -1
  558. package/form-field/error.d.ts +7 -0
  559. package/form-field/hint.d.ts +10 -0
  560. package/form-field/index.metadata.json +1 -1
  561. package/form-field/prefix.d.ts +7 -0
  562. package/form-field/suffix.d.ts +7 -0
  563. package/form-field/testing/form-field-harness.d.ts +8 -0
  564. package/grid-list/grid-list.d.ts +2 -1
  565. package/grid-list/index.metadata.json +1 -1
  566. package/grid-list/tile-styler.d.ts +10 -5
  567. package/input/_input-theme.scss +2 -2
  568. package/input/index.metadata.json +1 -1
  569. package/input/input.d.ts +6 -2
  570. package/input/testing/input-harness.d.ts +2 -0
  571. package/list/index.metadata.json +1 -1
  572. package/list/testing/action-list-harness.d.ts +2 -0
  573. package/list/testing/nav-list-harness.d.ts +2 -0
  574. package/list/testing/selection-list-harness.d.ts +2 -0
  575. package/menu/index.metadata.json +1 -1
  576. package/menu/menu-content.d.ts +7 -1
  577. package/menu/menu-panel.d.ts +1 -0
  578. package/menu/menu.d.ts +4 -0
  579. package/menu/testing/menu-harness.d.ts +4 -0
  580. package/package.json +6 -6
  581. package/prebuilt-themes/deeppurple-amber.css +1 -1
  582. package/prebuilt-themes/indigo-pink.css +1 -1
  583. package/prebuilt-themes/pink-bluegrey.css +1 -1
  584. package/prebuilt-themes/purple-green.css +1 -1
  585. package/progress-spinner/index.metadata.json +1 -1
  586. package/radio/index.metadata.json +1 -1
  587. package/radio/radio.d.ts +6 -0
  588. package/radio/testing/radio-harness.d.ts +2 -0
  589. package/schematics/migration.json +5 -0
  590. package/schematics/ng-add/index.js +2 -2
  591. package/schematics/ng-generate/navigation/files/__path__/__name@dasherize@if-flat__/__name@dasherize__.component.html.template +3 -3
  592. package/schematics/ng-generate/navigation/schema.json +5 -0
  593. package/schematics/ng-update/data/index.js +1 -1
  594. package/schematics/ng-update/index.d.ts +2 -0
  595. package/schematics/ng-update/index.js +7 -2
  596. package/schematics/ng-update/migrations/hammer-gestures-v9/hammer-gestures-migration.js +639 -639
  597. package/schematics/ng-update/migrations/misc-ripples-v7/ripple-speed-factor-migration.js +1 -1
  598. package/select/index.metadata.json +1 -1
  599. package/select/select.d.ts +6 -0
  600. package/select/testing/select-harness.d.ts +2 -0
  601. package/sidenav/drawer.d.ts +15 -3
  602. package/sidenav/index.metadata.json +1 -1
  603. package/slide-toggle/index.metadata.json +1 -1
  604. package/slide-toggle/testing/slide-toggle-harness.d.ts +2 -0
  605. package/slider/testing/slider-harness.d.ts +2 -0
  606. package/snack-bar/index.metadata.json +1 -1
  607. package/snack-bar/simple-snack-bar.d.ts +13 -1
  608. package/snack-bar/snack-bar-container.d.ts +16 -3
  609. package/snack-bar/snack-bar-ref.d.ts +3 -3
  610. package/snack-bar/snack-bar.d.ts +10 -3
  611. package/sort/index.metadata.json +1 -1
  612. package/sort/sort-header-intl.d.ts +5 -1
  613. package/sort/sort-header.d.ts +3 -1
  614. package/sort/testing/sort-header-harness.d.ts +6 -2
  615. package/tabs/index.d.ts +3 -1
  616. package/tabs/index.metadata.json +1 -1
  617. package/tabs/tab-content.d.ts +7 -1
  618. package/tabs/tab-label.d.ts +7 -0
  619. package/toolbar/testing/index.d.ts +8 -0
  620. package/toolbar/testing/package.json +9 -0
  621. package/toolbar/testing/public-api.d.ts +9 -0
  622. package/toolbar/testing/toolbar-harness-filters.d.ts +13 -0
  623. package/toolbar/testing/toolbar-harness.d.ts +31 -0
  624. package/tooltip/index.metadata.json +1 -1
  625. package/tooltip/tooltip.d.ts +1 -0
  626. package/tree/data-source/flat-data-source.d.ts +4 -4
  627. package/tree/index.metadata.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import { Overlay, CdkConnectedOverlay, OverlayModule } from '@angular/cdk/overlay';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import { InjectionToken, Directive, EventEmitter, isDevMode, Component, ViewEncapsulation, ChangeDetectionStrategy, ChangeDetectorRef, NgZone, ElementRef, Optional, Inject, Self, Attribute, ViewChild, ContentChildren, Input, ContentChild, Output, NgModule } from '@angular/core';
4
- import { mixinDisableRipple, mixinTabIndex, mixinDisabled, mixinErrorState, _countGroupLabelsBeforeOption, _getOptionScrollPosition, MAT_OPTION_PARENT_COMPONENT, ErrorStateMatcher, MatOption, MatOptgroup, MatOptionModule, MatCommonModule } from '@angular/material/core';
4
+ import { mixinDisableRipple, mixinTabIndex, mixinDisabled, mixinErrorState, _countGroupLabelsBeforeOption, _getOptionScrollPosition, MAT_OPTION_PARENT_COMPONENT, ErrorStateMatcher, MatOption, MAT_OPTGROUP, MatOptionModule, MatCommonModule } from '@angular/material/core';
5
5
  import { MatFormFieldControl, MatFormField, MAT_FORM_FIELD, MatFormFieldModule } from '@angular/material/form-field';
6
6
  import { ViewportRuler, CdkScrollableModule } from '@angular/cdk/scrolling';
7
7
  import { ActiveDescendantKeyManager, LiveAnnouncer } from '@angular/cdk/a11y';
@@ -174,1005 +174,1006 @@ class MatSelectBase {
174
174
  }
175
175
  }
176
176
  const _MatSelectMixinBase = mixinDisableRipple(mixinTabIndex(mixinDisabled(mixinErrorState(MatSelectBase))));
177
+ /**
178
+ * Injection token that can be used to reference instances of `MatSelectTrigger`. It serves as
179
+ * alternative token to the actual `MatSelectTrigger` class which could cause unnecessary
180
+ * retention of the class and its directive metadata.
181
+ */
182
+ const MAT_SELECT_TRIGGER = new InjectionToken('MatSelectTrigger');
177
183
  /**
178
184
  * Allows the user to customize the trigger that is displayed when the select has a value.
179
185
  */
180
- let MatSelectTrigger = /** @class */ (() => {
181
- class MatSelectTrigger {
182
- }
183
- MatSelectTrigger.decorators = [
184
- { type: Directive, args: [{
185
- selector: 'mat-select-trigger'
186
- },] }
187
- ];
188
- return MatSelectTrigger;
189
- })();
190
- let MatSelect = /** @class */ (() => {
191
- class MatSelect extends _MatSelectMixinBase {
192
- constructor(_viewportRuler, _changeDetectorRef, _ngZone, _defaultErrorStateMatcher, elementRef, _dir, _parentForm, _parentFormGroup, _parentFormField, ngControl, tabIndex, scrollStrategyFactory, _liveAnnouncer, defaults) {
193
- super(elementRef, _defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl);
194
- this._viewportRuler = _viewportRuler;
195
- this._changeDetectorRef = _changeDetectorRef;
196
- this._ngZone = _ngZone;
197
- this._dir = _dir;
198
- this._parentFormField = _parentFormField;
199
- this.ngControl = ngControl;
200
- this._liveAnnouncer = _liveAnnouncer;
201
- /** Whether or not the overlay panel is open. */
202
- this._panelOpen = false;
203
- /** Whether filling out the select is required in the form. */
204
- this._required = false;
205
- /** The scroll position of the overlay panel, calculated to center the selected option. */
206
- this._scrollTop = 0;
207
- /** Whether the component is in multiple selection mode. */
208
- this._multiple = false;
209
- /** Comparison function to specify which option is displayed. Defaults to object equality. */
210
- this._compareWith = (o1, o2) => o1 === o2;
211
- /** Unique id for this input. */
212
- this._uid = `mat-select-${nextUniqueId++}`;
213
- /** Emits whenever the component is destroyed. */
214
- this._destroy = new Subject();
215
- /** The cached font-size of the trigger element. */
216
- this._triggerFontSize = 0;
217
- /** `View -> model callback called when value changes` */
218
- this._onChange = () => { };
219
- /** `View -> model callback called when select has been touched` */
220
- this._onTouched = () => { };
221
- /** The IDs of child options to be passed to the aria-owns attribute. */
222
- this._optionIds = '';
223
- /** The value of the select panel's transform-origin property. */
224
- this._transformOrigin = 'top';
225
- /** Emits when the panel element is finished transforming in. */
226
- this._panelDoneAnimatingStream = new Subject();
227
- /**
228
- * The y-offset of the overlay panel in relation to the trigger's top start corner.
229
- * This must be adjusted to align the selected option text over the trigger text.
230
- * when the panel opens. Will change based on the y-position of the selected option.
231
- */
232
- this._offsetY = 0;
233
- /**
234
- * This position config ensures that the top "start" corner of the overlay
235
- * is aligned with with the top "start" of the origin by default (overlapping
236
- * the trigger completely). If the panel cannot fit below the trigger, it
237
- * will fall back to a position above the trigger.
238
- */
239
- this._positions = [
240
- {
241
- originX: 'start',
242
- originY: 'top',
243
- overlayX: 'start',
244
- overlayY: 'top',
245
- },
246
- {
247
- originX: 'start',
248
- originY: 'bottom',
249
- overlayX: 'start',
250
- overlayY: 'bottom',
251
- },
252
- ];
253
- /** Whether the component is disabling centering of the active option over the trigger. */
254
- this._disableOptionCentering = false;
255
- this._focused = false;
256
- /** A name for this control that can be used by `mat-form-field`. */
257
- this.controlType = 'mat-select';
258
- /** Aria label of the select. If not specified, the placeholder will be used as label. */
259
- this.ariaLabel = '';
260
- /** Combined stream of all of the child options' change events. */
261
- this.optionSelectionChanges = defer(() => {
262
- const options = this.options;
263
- if (options) {
264
- return options.changes.pipe(startWith(options), switchMap(() => merge(...options.map(option => option.onSelectionChange))));
265
- }
266
- return this._ngZone.onStable
267
- .asObservable()
268
- .pipe(take(1), switchMap(() => this.optionSelectionChanges));
269
- });
270
- /** Event emitted when the select panel has been toggled. */
271
- this.openedChange = new EventEmitter();
272
- /** Event emitted when the select has been opened. */
273
- this._openedStream = this.openedChange.pipe(filter(o => o), map(() => { }));
274
- /** Event emitted when the select has been closed. */
275
- this._closedStream = this.openedChange.pipe(filter(o => !o), map(() => { }));
276
- /** Event emitted when the selected value has been changed by the user. */
277
- this.selectionChange = new EventEmitter();
278
- /**
279
- * Event that emits whenever the raw value of the select changes. This is here primarily
280
- * to facilitate the two-way binding for the `value` input.
281
- * @docs-private
282
- */
283
- this.valueChange = new EventEmitter();
284
- if (this.ngControl) {
285
- // Note: we provide the value accessor through here, instead of
286
- // the `providers` to avoid running into a circular import.
287
- this.ngControl.valueAccessor = this;
288
- }
289
- this._scrollStrategyFactory = scrollStrategyFactory;
290
- this._scrollStrategy = this._scrollStrategyFactory();
291
- this.tabIndex = parseInt(tabIndex) || 0;
292
- // Force setter to be called in case id was not specified.
293
- this.id = this.id;
294
- if (defaults) {
295
- if (defaults.disableOptionCentering != null) {
296
- this.disableOptionCentering = defaults.disableOptionCentering;
297
- }
298
- if (defaults.typeaheadDebounceInterval != null) {
299
- this.typeaheadDebounceInterval = defaults.typeaheadDebounceInterval;
300
- }
301
- }
302
- }
303
- /** Whether the select is focused. */
304
- get focused() {
305
- return this._focused || this._panelOpen;
306
- }
307
- /** Placeholder to be shown if no value has been selected. */
308
- get placeholder() { return this._placeholder; }
309
- set placeholder(value) {
310
- this._placeholder = value;
311
- this.stateChanges.next();
312
- }
313
- /** Whether the component is required. */
314
- get required() { return this._required; }
315
- set required(value) {
316
- this._required = coerceBooleanProperty(value);
317
- this.stateChanges.next();
318
- }
319
- /** Whether the user should be allowed to select multiple options. */
320
- get multiple() { return this._multiple; }
321
- set multiple(value) {
322
- if (this._selectionModel) {
323
- throw getMatSelectDynamicMultipleError();
324
- }
325
- this._multiple = coerceBooleanProperty(value);
326
- }
327
- /** Whether to center the active option over the trigger. */
328
- get disableOptionCentering() { return this._disableOptionCentering; }
329
- set disableOptionCentering(value) {
330
- this._disableOptionCentering = coerceBooleanProperty(value);
331
- }
186
+ class MatSelectTrigger {
187
+ }
188
+ MatSelectTrigger.decorators = [
189
+ { type: Directive, args: [{
190
+ selector: 'mat-select-trigger',
191
+ providers: [{ provide: MAT_SELECT_TRIGGER, useExisting: MatSelectTrigger }],
192
+ },] }
193
+ ];
194
+ class MatSelect extends _MatSelectMixinBase {
195
+ constructor(_viewportRuler, _changeDetectorRef, _ngZone, _defaultErrorStateMatcher, elementRef, _dir, _parentForm, _parentFormGroup, _parentFormField, ngControl, tabIndex, scrollStrategyFactory, _liveAnnouncer, defaults) {
196
+ super(elementRef, _defaultErrorStateMatcher, _parentForm, _parentFormGroup, ngControl);
197
+ this._viewportRuler = _viewportRuler;
198
+ this._changeDetectorRef = _changeDetectorRef;
199
+ this._ngZone = _ngZone;
200
+ this._dir = _dir;
201
+ this._parentFormField = _parentFormField;
202
+ this.ngControl = ngControl;
203
+ this._liveAnnouncer = _liveAnnouncer;
204
+ /** Whether or not the overlay panel is open. */
205
+ this._panelOpen = false;
206
+ /** Whether filling out the select is required in the form. */
207
+ this._required = false;
208
+ /** The scroll position of the overlay panel, calculated to center the selected option. */
209
+ this._scrollTop = 0;
210
+ /** Whether the component is in multiple selection mode. */
211
+ this._multiple = false;
212
+ /** Comparison function to specify which option is displayed. Defaults to object equality. */
213
+ this._compareWith = (o1, o2) => o1 === o2;
214
+ /** Unique id for this input. */
215
+ this._uid = `mat-select-${nextUniqueId++}`;
216
+ /** Emits whenever the component is destroyed. */
217
+ this._destroy = new Subject();
218
+ /** The cached font-size of the trigger element. */
219
+ this._triggerFontSize = 0;
220
+ /** `View -> model callback called when value changes` */
221
+ this._onChange = () => { };
222
+ /** `View -> model callback called when select has been touched` */
223
+ this._onTouched = () => { };
224
+ /** The IDs of child options to be passed to the aria-owns attribute. */
225
+ this._optionIds = '';
226
+ /** The value of the select panel's transform-origin property. */
227
+ this._transformOrigin = 'top';
228
+ /** Emits when the panel element is finished transforming in. */
229
+ this._panelDoneAnimatingStream = new Subject();
332
230
  /**
333
- * Function to compare the option values with the selected values. The first argument
334
- * is a value from an option. The second is a value from the selection. A boolean
335
- * should be returned.
231
+ * The y-offset of the overlay panel in relation to the trigger's top start corner.
232
+ * This must be adjusted to align the selected option text over the trigger text.
233
+ * when the panel opens. Will change based on the y-position of the selected option.
336
234
  */
337
- get compareWith() { return this._compareWith; }
338
- set compareWith(fn) {
339
- if (typeof fn !== 'function') {
340
- throw getMatSelectNonFunctionValueError();
341
- }
342
- this._compareWith = fn;
343
- if (this._selectionModel) {
344
- // A different comparator means the selection could change.
345
- this._initializeSelection();
235
+ this._offsetY = 0;
236
+ /**
237
+ * This position config ensures that the top "start" corner of the overlay
238
+ * is aligned with with the top "start" of the origin by default (overlapping
239
+ * the trigger completely). If the panel cannot fit below the trigger, it
240
+ * will fall back to a position above the trigger.
241
+ */
242
+ this._positions = [
243
+ {
244
+ originX: 'start',
245
+ originY: 'top',
246
+ overlayX: 'start',
247
+ overlayY: 'top',
248
+ },
249
+ {
250
+ originX: 'start',
251
+ originY: 'bottom',
252
+ overlayX: 'start',
253
+ overlayY: 'bottom',
254
+ },
255
+ ];
256
+ /** Whether the component is disabling centering of the active option over the trigger. */
257
+ this._disableOptionCentering = false;
258
+ this._focused = false;
259
+ /** A name for this control that can be used by `mat-form-field`. */
260
+ this.controlType = 'mat-select';
261
+ /** Aria label of the select. If not specified, the placeholder will be used as label. */
262
+ this.ariaLabel = '';
263
+ /** Combined stream of all of the child options' change events. */
264
+ this.optionSelectionChanges = defer(() => {
265
+ const options = this.options;
266
+ if (options) {
267
+ return options.changes.pipe(startWith(options), switchMap(() => merge(...options.map(option => option.onSelectionChange))));
346
268
  }
269
+ return this._ngZone.onStable
270
+ .asObservable()
271
+ .pipe(take(1), switchMap(() => this.optionSelectionChanges));
272
+ });
273
+ /** Event emitted when the select panel has been toggled. */
274
+ this.openedChange = new EventEmitter();
275
+ /** Event emitted when the select has been opened. */
276
+ this._openedStream = this.openedChange.pipe(filter(o => o), map(() => { }));
277
+ /** Event emitted when the select has been closed. */
278
+ this._closedStream = this.openedChange.pipe(filter(o => !o), map(() => { }));
279
+ /** Event emitted when the selected value has been changed by the user. */
280
+ this.selectionChange = new EventEmitter();
281
+ /**
282
+ * Event that emits whenever the raw value of the select changes. This is here primarily
283
+ * to facilitate the two-way binding for the `value` input.
284
+ * @docs-private
285
+ */
286
+ this.valueChange = new EventEmitter();
287
+ if (this.ngControl) {
288
+ // Note: we provide the value accessor through here, instead of
289
+ // the `providers` to avoid running into a circular import.
290
+ this.ngControl.valueAccessor = this;
347
291
  }
348
- /** Value of the select control. */
349
- get value() { return this._value; }
350
- set value(newValue) {
351
- if (newValue !== this._value) {
352
- this.writeValue(newValue);
353
- this._value = newValue;
292
+ this._scrollStrategyFactory = scrollStrategyFactory;
293
+ this._scrollStrategy = this._scrollStrategyFactory();
294
+ this.tabIndex = parseInt(tabIndex) || 0;
295
+ // Force setter to be called in case id was not specified.
296
+ this.id = this.id;
297
+ if (defaults) {
298
+ if (defaults.disableOptionCentering != null) {
299
+ this.disableOptionCentering = defaults.disableOptionCentering;
300
+ }
301
+ if (defaults.typeaheadDebounceInterval != null) {
302
+ this.typeaheadDebounceInterval = defaults.typeaheadDebounceInterval;
354
303
  }
355
304
  }
356
- /** Time to wait in milliseconds after the last keystroke before moving focus to an item. */
357
- get typeaheadDebounceInterval() { return this._typeaheadDebounceInterval; }
358
- set typeaheadDebounceInterval(value) {
359
- this._typeaheadDebounceInterval = coerceNumberProperty(value);
360
- }
361
- /** Unique id of the element. */
362
- get id() { return this._id; }
363
- set id(value) {
364
- this._id = value || this._uid;
365
- this.stateChanges.next();
305
+ }
306
+ /** Whether the select is focused. */
307
+ get focused() {
308
+ return this._focused || this._panelOpen;
309
+ }
310
+ /** Placeholder to be shown if no value has been selected. */
311
+ get placeholder() { return this._placeholder; }
312
+ set placeholder(value) {
313
+ this._placeholder = value;
314
+ this.stateChanges.next();
315
+ }
316
+ /** Whether the component is required. */
317
+ get required() { return this._required; }
318
+ set required(value) {
319
+ this._required = coerceBooleanProperty(value);
320
+ this.stateChanges.next();
321
+ }
322
+ /** Whether the user should be allowed to select multiple options. */
323
+ get multiple() { return this._multiple; }
324
+ set multiple(value) {
325
+ if (this._selectionModel) {
326
+ throw getMatSelectDynamicMultipleError();
366
327
  }
367
- ngOnInit() {
368
- this._selectionModel = new SelectionModel(this.multiple);
369
- this.stateChanges.next();
370
- // We need `distinctUntilChanged` here, because some browsers will
371
- // fire the animation end event twice for the same animation. See:
372
- // https://github.com/angular/angular/issues/24084
373
- this._panelDoneAnimatingStream
374
- .pipe(distinctUntilChanged(), takeUntil(this._destroy))
375
- .subscribe(() => {
376
- if (this.panelOpen) {
377
- this._scrollTop = 0;
378
- this.openedChange.emit(true);
379
- }
380
- else {
381
- this.openedChange.emit(false);
382
- this.overlayDir.offsetX = 0;
383
- this._changeDetectorRef.markForCheck();
384
- }
385
- });
386
- this._viewportRuler.change()
387
- .pipe(takeUntil(this._destroy))
388
- .subscribe(() => {
389
- if (this._panelOpen) {
390
- this._triggerRect = this.trigger.nativeElement.getBoundingClientRect();
391
- this._changeDetectorRef.markForCheck();
392
- }
393
- });
328
+ this._multiple = coerceBooleanProperty(value);
329
+ }
330
+ /** Whether to center the active option over the trigger. */
331
+ get disableOptionCentering() { return this._disableOptionCentering; }
332
+ set disableOptionCentering(value) {
333
+ this._disableOptionCentering = coerceBooleanProperty(value);
334
+ }
335
+ /**
336
+ * Function to compare the option values with the selected values. The first argument
337
+ * is a value from an option. The second is a value from the selection. A boolean
338
+ * should be returned.
339
+ */
340
+ get compareWith() { return this._compareWith; }
341
+ set compareWith(fn) {
342
+ if (typeof fn !== 'function') {
343
+ throw getMatSelectNonFunctionValueError();
394
344
  }
395
- ngAfterContentInit() {
396
- this._initKeyManager();
397
- this._selectionModel.changed.pipe(takeUntil(this._destroy)).subscribe(event => {
398
- event.added.forEach(option => option.select());
399
- event.removed.forEach(option => option.deselect());
400
- });
401
- this.options.changes.pipe(startWith(null), takeUntil(this._destroy)).subscribe(() => {
402
- this._resetOptions();
403
- this._initializeSelection();
404
- });
345
+ this._compareWith = fn;
346
+ if (this._selectionModel) {
347
+ // A different comparator means the selection could change.
348
+ this._initializeSelection();
405
349
  }
406
- ngDoCheck() {
407
- if (this.ngControl) {
408
- this.updateErrorState();
409
- }
350
+ }
351
+ /** Value of the select control. */
352
+ get value() { return this._value; }
353
+ set value(newValue) {
354
+ if (newValue !== this._value) {
355
+ this.writeValue(newValue);
356
+ this._value = newValue;
410
357
  }
411
- ngOnChanges(changes) {
412
- // Updating the disabled state is handled by `mixinDisabled`, but we need to additionally let
413
- // the parent form field know to run change detection when the disabled state changes.
414
- if (changes['disabled']) {
415
- this.stateChanges.next();
416
- }
417
- if (changes['typeaheadDebounceInterval'] && this._keyManager) {
418
- this._keyManager.withTypeAhead(this._typeaheadDebounceInterval);
358
+ }
359
+ /** Time to wait in milliseconds after the last keystroke before moving focus to an item. */
360
+ get typeaheadDebounceInterval() { return this._typeaheadDebounceInterval; }
361
+ set typeaheadDebounceInterval(value) {
362
+ this._typeaheadDebounceInterval = coerceNumberProperty(value);
363
+ }
364
+ /** Unique id of the element. */
365
+ get id() { return this._id; }
366
+ set id(value) {
367
+ this._id = value || this._uid;
368
+ this.stateChanges.next();
369
+ }
370
+ ngOnInit() {
371
+ this._selectionModel = new SelectionModel(this.multiple);
372
+ this.stateChanges.next();
373
+ // We need `distinctUntilChanged` here, because some browsers will
374
+ // fire the animation end event twice for the same animation. See:
375
+ // https://github.com/angular/angular/issues/24084
376
+ this._panelDoneAnimatingStream
377
+ .pipe(distinctUntilChanged(), takeUntil(this._destroy))
378
+ .subscribe(() => {
379
+ if (this.panelOpen) {
380
+ this._scrollTop = 0;
381
+ this.openedChange.emit(true);
419
382
  }
420
- }
421
- ngOnDestroy() {
422
- this._destroy.next();
423
- this._destroy.complete();
424
- this.stateChanges.complete();
425
- }
426
- /** Toggles the overlay panel open or closed. */
427
- toggle() {
428
- this.panelOpen ? this.close() : this.open();
429
- }
430
- /** Opens the overlay panel. */
431
- open() {
432
- if (this.disabled || !this.options || !this.options.length || this._panelOpen) {
433
- return;
383
+ else {
384
+ this.openedChange.emit(false);
385
+ this.overlayDir.offsetX = 0;
386
+ this._changeDetectorRef.markForCheck();
434
387
  }
435
- this._triggerRect = this.trigger.nativeElement.getBoundingClientRect();
436
- // Note: The computed font-size will be a string pixel value (e.g. "16px").
437
- // `parseInt` ignores the trailing 'px' and converts this to a number.
438
- this._triggerFontSize = parseInt(getComputedStyle(this.trigger.nativeElement).fontSize || '0');
439
- this._panelOpen = true;
440
- this._keyManager.withHorizontalOrientation(null);
441
- this._calculateOverlayPosition();
442
- this._highlightCorrectOption();
443
- this._changeDetectorRef.markForCheck();
444
- // Set the font size on the panel element once it exists.
445
- this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(() => {
446
- if (this._triggerFontSize && this.overlayDir.overlayRef &&
447
- this.overlayDir.overlayRef.overlayElement) {
448
- this.overlayDir.overlayRef.overlayElement.style.fontSize = `${this._triggerFontSize}px`;
449
- }
450
- });
451
- }
452
- /** Closes the overlay panel and focuses the host element. */
453
- close() {
388
+ });
389
+ this._viewportRuler.change()
390
+ .pipe(takeUntil(this._destroy))
391
+ .subscribe(() => {
454
392
  if (this._panelOpen) {
455
- this._panelOpen = false;
456
- this._keyManager.withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr');
393
+ this._triggerRect = this.trigger.nativeElement.getBoundingClientRect();
457
394
  this._changeDetectorRef.markForCheck();
458
- this._onTouched();
459
395
  }
396
+ });
397
+ }
398
+ ngAfterContentInit() {
399
+ this._initKeyManager();
400
+ this._selectionModel.changed.pipe(takeUntil(this._destroy)).subscribe(event => {
401
+ event.added.forEach(option => option.select());
402
+ event.removed.forEach(option => option.deselect());
403
+ });
404
+ this.options.changes.pipe(startWith(null), takeUntil(this._destroy)).subscribe(() => {
405
+ this._resetOptions();
406
+ this._initializeSelection();
407
+ });
408
+ }
409
+ ngDoCheck() {
410
+ if (this.ngControl) {
411
+ this.updateErrorState();
460
412
  }
461
- /**
462
- * Sets the select's value. Part of the ControlValueAccessor interface
463
- * required to integrate with Angular's core forms API.
464
- *
465
- * @param value New value to be written to the model.
466
- */
467
- writeValue(value) {
468
- if (this.options) {
469
- this._setSelectionByValue(value);
470
- }
413
+ }
414
+ ngOnChanges(changes) {
415
+ // Updating the disabled state is handled by `mixinDisabled`, but we need to additionally let
416
+ // the parent form field know to run change detection when the disabled state changes.
417
+ if (changes['disabled']) {
418
+ this.stateChanges.next();
471
419
  }
472
- /**
473
- * Saves a callback function to be invoked when the select's value
474
- * changes from user input. Part of the ControlValueAccessor interface
475
- * required to integrate with Angular's core forms API.
476
- *
477
- * @param fn Callback to be triggered when the value changes.
478
- */
479
- registerOnChange(fn) {
480
- this._onChange = fn;
420
+ if (changes['typeaheadDebounceInterval'] && this._keyManager) {
421
+ this._keyManager.withTypeAhead(this._typeaheadDebounceInterval);
481
422
  }
482
- /**
483
- * Saves a callback function to be invoked when the select is blurred
484
- * by the user. Part of the ControlValueAccessor interface required
485
- * to integrate with Angular's core forms API.
486
- *
487
- * @param fn Callback to be triggered when the component has been touched.
488
- */
489
- registerOnTouched(fn) {
490
- this._onTouched = fn;
423
+ }
424
+ ngOnDestroy() {
425
+ this._destroy.next();
426
+ this._destroy.complete();
427
+ this.stateChanges.complete();
428
+ }
429
+ /** Toggles the overlay panel open or closed. */
430
+ toggle() {
431
+ this.panelOpen ? this.close() : this.open();
432
+ }
433
+ /** Opens the overlay panel. */
434
+ open() {
435
+ if (this.disabled || !this.options || !this.options.length || this._panelOpen) {
436
+ return;
491
437
  }
492
- /**
493
- * Disables the select. Part of the ControlValueAccessor interface required
494
- * to integrate with Angular's core forms API.
495
- *
496
- * @param isDisabled Sets whether the component is disabled.
497
- */
498
- setDisabledState(isDisabled) {
499
- this.disabled = isDisabled;
438
+ this._triggerRect = this.trigger.nativeElement.getBoundingClientRect();
439
+ // Note: The computed font-size will be a string pixel value (e.g. "16px").
440
+ // `parseInt` ignores the trailing 'px' and converts this to a number.
441
+ this._triggerFontSize = parseInt(getComputedStyle(this.trigger.nativeElement).fontSize || '0');
442
+ this._panelOpen = true;
443
+ this._keyManager.withHorizontalOrientation(null);
444
+ this._calculateOverlayPosition();
445
+ this._highlightCorrectOption();
446
+ this._changeDetectorRef.markForCheck();
447
+ // Set the font size on the panel element once it exists.
448
+ this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(() => {
449
+ if (this._triggerFontSize && this.overlayDir.overlayRef &&
450
+ this.overlayDir.overlayRef.overlayElement) {
451
+ this.overlayDir.overlayRef.overlayElement.style.fontSize = `${this._triggerFontSize}px`;
452
+ }
453
+ });
454
+ }
455
+ /** Closes the overlay panel and focuses the host element. */
456
+ close() {
457
+ if (this._panelOpen) {
458
+ this._panelOpen = false;
459
+ this._keyManager.withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr');
500
460
  this._changeDetectorRef.markForCheck();
501
- this.stateChanges.next();
461
+ this._onTouched();
502
462
  }
503
- /** Whether or not the overlay panel is open. */
504
- get panelOpen() {
505
- return this._panelOpen;
463
+ }
464
+ /**
465
+ * Sets the select's value. Part of the ControlValueAccessor interface
466
+ * required to integrate with Angular's core forms API.
467
+ *
468
+ * @param value New value to be written to the model.
469
+ */
470
+ writeValue(value) {
471
+ if (this.options) {
472
+ this._setSelectionByValue(value);
506
473
  }
507
- /** The currently selected option. */
508
- get selected() {
509
- return this.multiple ? this._selectionModel.selected : this._selectionModel.selected[0];
474
+ }
475
+ /**
476
+ * Saves a callback function to be invoked when the select's value
477
+ * changes from user input. Part of the ControlValueAccessor interface
478
+ * required to integrate with Angular's core forms API.
479
+ *
480
+ * @param fn Callback to be triggered when the value changes.
481
+ */
482
+ registerOnChange(fn) {
483
+ this._onChange = fn;
484
+ }
485
+ /**
486
+ * Saves a callback function to be invoked when the select is blurred
487
+ * by the user. Part of the ControlValueAccessor interface required
488
+ * to integrate with Angular's core forms API.
489
+ *
490
+ * @param fn Callback to be triggered when the component has been touched.
491
+ */
492
+ registerOnTouched(fn) {
493
+ this._onTouched = fn;
494
+ }
495
+ /**
496
+ * Disables the select. Part of the ControlValueAccessor interface required
497
+ * to integrate with Angular's core forms API.
498
+ *
499
+ * @param isDisabled Sets whether the component is disabled.
500
+ */
501
+ setDisabledState(isDisabled) {
502
+ this.disabled = isDisabled;
503
+ this._changeDetectorRef.markForCheck();
504
+ this.stateChanges.next();
505
+ }
506
+ /** Whether or not the overlay panel is open. */
507
+ get panelOpen() {
508
+ return this._panelOpen;
509
+ }
510
+ /** The currently selected option. */
511
+ get selected() {
512
+ return this.multiple ? this._selectionModel.selected : this._selectionModel.selected[0];
513
+ }
514
+ /** The value displayed in the trigger. */
515
+ get triggerValue() {
516
+ if (this.empty) {
517
+ return '';
510
518
  }
511
- /** The value displayed in the trigger. */
512
- get triggerValue() {
513
- if (this.empty) {
514
- return '';
519
+ if (this._multiple) {
520
+ const selectedOptions = this._selectionModel.selected.map(option => option.viewValue);
521
+ if (this._isRtl()) {
522
+ selectedOptions.reverse();
515
523
  }
516
- if (this._multiple) {
517
- const selectedOptions = this._selectionModel.selected.map(option => option.viewValue);
518
- if (this._isRtl()) {
519
- selectedOptions.reverse();
520
- }
521
- // TODO(crisbeto): delimiter should be configurable for proper localization.
522
- return selectedOptions.join(', ');
523
- }
524
- return this._selectionModel.selected[0].viewValue;
525
- }
526
- /** Whether the element is in RTL mode. */
527
- _isRtl() {
528
- return this._dir ? this._dir.value === 'rtl' : false;
524
+ // TODO(crisbeto): delimiter should be configurable for proper localization.
525
+ return selectedOptions.join(', ');
529
526
  }
530
- /** Handles all keydown events on the select. */
531
- _handleKeydown(event) {
532
- if (!this.disabled) {
533
- this.panelOpen ? this._handleOpenKeydown(event) : this._handleClosedKeydown(event);
534
- }
527
+ return this._selectionModel.selected[0].viewValue;
528
+ }
529
+ /** Whether the element is in RTL mode. */
530
+ _isRtl() {
531
+ return this._dir ? this._dir.value === 'rtl' : false;
532
+ }
533
+ /** Handles all keydown events on the select. */
534
+ _handleKeydown(event) {
535
+ if (!this.disabled) {
536
+ this.panelOpen ? this._handleOpenKeydown(event) : this._handleClosedKeydown(event);
535
537
  }
536
- /** Handles keyboard events while the select is closed. */
537
- _handleClosedKeydown(event) {
538
- const keyCode = event.keyCode;
539
- const isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW ||
540
- keyCode === LEFT_ARROW || keyCode === RIGHT_ARROW;
541
- const isOpenKey = keyCode === ENTER || keyCode === SPACE;
542
- const manager = this._keyManager;
543
- // Open the select on ALT + arrow key to match the native <select>
544
- if (!manager.isTyping() && (isOpenKey && !hasModifierKey(event)) ||
545
- ((this.multiple || event.altKey) && isArrowKey)) {
546
- event.preventDefault(); // prevents the page from scrolling down when pressing space
547
- this.open();
548
- }
549
- else if (!this.multiple) {
550
- const previouslySelectedOption = this.selected;
551
- if (keyCode === HOME || keyCode === END) {
552
- keyCode === HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
553
- event.preventDefault();
554
- }
555
- else {
556
- manager.onKeydown(event);
557
- }
558
- const selectedOption = this.selected;
559
- // Since the value has changed, we need to announce it ourselves.
560
- if (selectedOption && previouslySelectedOption !== selectedOption) {
561
- // We set a duration on the live announcement, because we want the live element to be
562
- // cleared after a while so that users can't navigate to it using the arrow keys.
563
- this._liveAnnouncer.announce(selectedOption.viewValue, 10000);
564
- }
565
- }
538
+ }
539
+ /** Handles keyboard events while the select is closed. */
540
+ _handleClosedKeydown(event) {
541
+ const keyCode = event.keyCode;
542
+ const isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW ||
543
+ keyCode === LEFT_ARROW || keyCode === RIGHT_ARROW;
544
+ const isOpenKey = keyCode === ENTER || keyCode === SPACE;
545
+ const manager = this._keyManager;
546
+ // Open the select on ALT + arrow key to match the native <select>
547
+ if (!manager.isTyping() && (isOpenKey && !hasModifierKey(event)) ||
548
+ ((this.multiple || event.altKey) && isArrowKey)) {
549
+ event.preventDefault(); // prevents the page from scrolling down when pressing space
550
+ this.open();
566
551
  }
567
- /** Handles keyboard events when the selected is open. */
568
- _handleOpenKeydown(event) {
569
- const manager = this._keyManager;
570
- const keyCode = event.keyCode;
571
- const isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW;
572
- const isTyping = manager.isTyping();
552
+ else if (!this.multiple) {
553
+ const previouslySelectedOption = this.selected;
573
554
  if (keyCode === HOME || keyCode === END) {
574
- event.preventDefault();
575
555
  keyCode === HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
576
- }
577
- else if (isArrowKey && event.altKey) {
578
- // Close the select on ALT + arrow key to match the native <select>
579
- event.preventDefault();
580
- this.close();
581
- // Don't do anything in this case if the user is typing,
582
- // because the typing sequence can include the space key.
583
- }
584
- else if (!isTyping && (keyCode === ENTER || keyCode === SPACE) && manager.activeItem &&
585
- !hasModifierKey(event)) {
586
556
  event.preventDefault();
587
- manager.activeItem._selectViaInteraction();
588
- }
589
- else if (!isTyping && this._multiple && keyCode === A && event.ctrlKey) {
590
- event.preventDefault();
591
- const hasDeselectedOptions = this.options.some(opt => !opt.disabled && !opt.selected);
592
- this.options.forEach(option => {
593
- if (!option.disabled) {
594
- hasDeselectedOptions ? option.select() : option.deselect();
595
- }
596
- });
597
557
  }
598
558
  else {
599
- const previouslyFocusedIndex = manager.activeItemIndex;
600
559
  manager.onKeydown(event);
601
- if (this._multiple && isArrowKey && event.shiftKey && manager.activeItem &&
602
- manager.activeItemIndex !== previouslyFocusedIndex) {
603
- manager.activeItem._selectViaInteraction();
604
- }
605
- }
606
- }
607
- _onFocus() {
608
- if (!this.disabled) {
609
- this._focused = true;
610
- this.stateChanges.next();
611
560
  }
612
- }
613
- /**
614
- * Calls the touched callback only if the panel is closed. Otherwise, the trigger will
615
- * "blur" to the panel when it opens, causing a false positive.
616
- */
617
- _onBlur() {
618
- this._focused = false;
619
- if (!this.disabled && !this.panelOpen) {
620
- this._onTouched();
621
- this._changeDetectorRef.markForCheck();
622
- this.stateChanges.next();
561
+ const selectedOption = this.selected;
562
+ // Since the value has changed, we need to announce it ourselves.
563
+ if (selectedOption && previouslySelectedOption !== selectedOption) {
564
+ // We set a duration on the live announcement, because we want the live element to be
565
+ // cleared after a while so that users can't navigate to it using the arrow keys.
566
+ this._liveAnnouncer.announce(selectedOption.viewValue, 10000);
623
567
  }
624
568
  }
625
- /**
626
- * Callback that is invoked when the overlay panel has been attached.
627
- */
628
- _onAttached() {
629
- this.overlayDir.positionChange.pipe(take(1)).subscribe(() => {
630
- this._changeDetectorRef.detectChanges();
631
- this._calculateOverlayOffsetX();
632
- this.panel.nativeElement.scrollTop = this._scrollTop;
633
- });
569
+ }
570
+ /** Handles keyboard events when the selected is open. */
571
+ _handleOpenKeydown(event) {
572
+ const manager = this._keyManager;
573
+ const keyCode = event.keyCode;
574
+ const isArrowKey = keyCode === DOWN_ARROW || keyCode === UP_ARROW;
575
+ const isTyping = manager.isTyping();
576
+ if (keyCode === HOME || keyCode === END) {
577
+ event.preventDefault();
578
+ keyCode === HOME ? manager.setFirstItemActive() : manager.setLastItemActive();
634
579
  }
635
- /** Returns the theme to be used on the panel. */
636
- _getPanelTheme() {
637
- return this._parentFormField ? `mat-${this._parentFormField.color}` : '';
580
+ else if (isArrowKey && event.altKey) {
581
+ // Close the select on ALT + arrow key to match the native <select>
582
+ event.preventDefault();
583
+ this.close();
584
+ // Don't do anything in this case if the user is typing,
585
+ // because the typing sequence can include the space key.
638
586
  }
639
- /** Whether the select has a value. */
640
- get empty() {
641
- return !this._selectionModel || this._selectionModel.isEmpty();
587
+ else if (!isTyping && (keyCode === ENTER || keyCode === SPACE) && manager.activeItem &&
588
+ !hasModifierKey(event)) {
589
+ event.preventDefault();
590
+ manager.activeItem._selectViaInteraction();
642
591
  }
643
- _initializeSelection() {
644
- // Defer setting the value in order to avoid the "Expression
645
- // has changed after it was checked" errors from Angular.
646
- Promise.resolve().then(() => {
647
- this._setSelectionByValue(this.ngControl ? this.ngControl.value : this._value);
648
- this.stateChanges.next();
592
+ else if (!isTyping && this._multiple && keyCode === A && event.ctrlKey) {
593
+ event.preventDefault();
594
+ const hasDeselectedOptions = this.options.some(opt => !opt.disabled && !opt.selected);
595
+ this.options.forEach(option => {
596
+ if (!option.disabled) {
597
+ hasDeselectedOptions ? option.select() : option.deselect();
598
+ }
649
599
  });
650
600
  }
651
- /**
652
- * Sets the selected option based on a value. If no option can be
653
- * found with the designated value, the select trigger is cleared.
654
- */
655
- _setSelectionByValue(value) {
656
- if (this.multiple && value) {
657
- if (!Array.isArray(value)) {
658
- throw getMatSelectNonArrayValueError();
659
- }
660
- this._selectionModel.clear();
661
- value.forEach((currentValue) => this._selectValue(currentValue));
662
- this._sortValues();
663
- }
664
- else {
665
- this._selectionModel.clear();
666
- const correspondingOption = this._selectValue(value);
667
- // Shift focus to the active item. Note that we shouldn't do this in multiple
668
- // mode, because we don't know what option the user interacted with last.
669
- if (correspondingOption) {
670
- this._keyManager.setActiveItem(correspondingOption);
671
- }
672
- else if (!this.panelOpen) {
673
- // Otherwise reset the highlighted option. Note that we only want to do this while
674
- // closed, because doing it while open can shift the user's focus unnecessarily.
675
- this._keyManager.setActiveItem(-1);
676
- }
601
+ else {
602
+ const previouslyFocusedIndex = manager.activeItemIndex;
603
+ manager.onKeydown(event);
604
+ if (this._multiple && isArrowKey && event.shiftKey && manager.activeItem &&
605
+ manager.activeItemIndex !== previouslyFocusedIndex) {
606
+ manager.activeItem._selectViaInteraction();
677
607
  }
608
+ }
609
+ }
610
+ _onFocus() {
611
+ if (!this.disabled) {
612
+ this._focused = true;
613
+ this.stateChanges.next();
614
+ }
615
+ }
616
+ /**
617
+ * Calls the touched callback only if the panel is closed. Otherwise, the trigger will
618
+ * "blur" to the panel when it opens, causing a false positive.
619
+ */
620
+ _onBlur() {
621
+ this._focused = false;
622
+ if (!this.disabled && !this.panelOpen) {
623
+ this._onTouched();
678
624
  this._changeDetectorRef.markForCheck();
625
+ this.stateChanges.next();
679
626
  }
680
- /**
681
- * Finds and selects and option based on its value.
682
- * @returns Option that has the corresponding value.
683
- */
684
- _selectValue(value) {
685
- const correspondingOption = this.options.find((option) => {
686
- try {
687
- // Treat null as a special reset value.
688
- return option.value != null && this._compareWith(option.value, value);
689
- }
690
- catch (error) {
691
- if (isDevMode()) {
692
- // Notify developers of errors in their comparator.
693
- console.warn(error);
694
- }
695
- return false;
696
- }
697
- });
627
+ }
628
+ /**
629
+ * Callback that is invoked when the overlay panel has been attached.
630
+ */
631
+ _onAttached() {
632
+ this.overlayDir.positionChange.pipe(take(1)).subscribe(() => {
633
+ this._changeDetectorRef.detectChanges();
634
+ this._calculateOverlayOffsetX();
635
+ this.panel.nativeElement.scrollTop = this._scrollTop;
636
+ });
637
+ }
638
+ /** Returns the theme to be used on the panel. */
639
+ _getPanelTheme() {
640
+ return this._parentFormField ? `mat-${this._parentFormField.color}` : '';
641
+ }
642
+ /** Whether the select has a value. */
643
+ get empty() {
644
+ return !this._selectionModel || this._selectionModel.isEmpty();
645
+ }
646
+ _initializeSelection() {
647
+ // Defer setting the value in order to avoid the "Expression
648
+ // has changed after it was checked" errors from Angular.
649
+ Promise.resolve().then(() => {
650
+ this._setSelectionByValue(this.ngControl ? this.ngControl.value : this._value);
651
+ this.stateChanges.next();
652
+ });
653
+ }
654
+ /**
655
+ * Sets the selected option based on a value. If no option can be
656
+ * found with the designated value, the select trigger is cleared.
657
+ */
658
+ _setSelectionByValue(value) {
659
+ if (this.multiple && value) {
660
+ if (!Array.isArray(value)) {
661
+ throw getMatSelectNonArrayValueError();
662
+ }
663
+ this._selectionModel.clear();
664
+ value.forEach((currentValue) => this._selectValue(currentValue));
665
+ this._sortValues();
666
+ }
667
+ else {
668
+ this._selectionModel.clear();
669
+ const correspondingOption = this._selectValue(value);
670
+ // Shift focus to the active item. Note that we shouldn't do this in multiple
671
+ // mode, because we don't know what option the user interacted with last.
698
672
  if (correspondingOption) {
699
- this._selectionModel.select(correspondingOption);
673
+ this._keyManager.setActiveItem(correspondingOption);
674
+ }
675
+ else if (!this.panelOpen) {
676
+ // Otherwise reset the highlighted option. Note that we only want to do this while
677
+ // closed, because doing it while open can shift the user's focus unnecessarily.
678
+ this._keyManager.setActiveItem(-1);
700
679
  }
701
- return correspondingOption;
702
680
  }
703
- /** Sets up a key manager to listen to keyboard events on the overlay panel. */
704
- _initKeyManager() {
705
- this._keyManager = new ActiveDescendantKeyManager(this.options)
706
- .withTypeAhead(this._typeaheadDebounceInterval)
707
- .withVerticalOrientation()
708
- .withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr')
709
- .withAllowedModifierKeys(['shiftKey']);
710
- this._keyManager.tabOut.pipe(takeUntil(this._destroy)).subscribe(() => {
711
- if (this.panelOpen) {
712
- // Select the active item when tabbing away. This is consistent with how the native
713
- // select behaves. Note that we only want to do this in single selection mode.
714
- if (!this.multiple && this._keyManager.activeItem) {
715
- this._keyManager.activeItem._selectViaInteraction();
716
- }
717
- // Restore focus to the trigger before closing. Ensures that the focus
718
- // position won't be lost if the user got focus into the overlay.
719
- this.focus();
720
- this.close();
721
- }
722
- });
723
- this._keyManager.change.pipe(takeUntil(this._destroy)).subscribe(() => {
724
- if (this._panelOpen && this.panel) {
725
- this._scrollActiveOptionIntoView();
726
- }
727
- else if (!this._panelOpen && !this.multiple && this._keyManager.activeItem) {
728
- this._keyManager.activeItem._selectViaInteraction();
681
+ this._changeDetectorRef.markForCheck();
682
+ }
683
+ /**
684
+ * Finds and selects and option based on its value.
685
+ * @returns Option that has the corresponding value.
686
+ */
687
+ _selectValue(value) {
688
+ const correspondingOption = this.options.find((option) => {
689
+ try {
690
+ // Treat null as a special reset value.
691
+ return option.value != null && this._compareWith(option.value, value);
692
+ }
693
+ catch (error) {
694
+ if (isDevMode()) {
695
+ // Notify developers of errors in their comparator.
696
+ console.warn(error);
729
697
  }
730
- });
698
+ return false;
699
+ }
700
+ });
701
+ if (correspondingOption) {
702
+ this._selectionModel.select(correspondingOption);
731
703
  }
732
- /** Drops current option subscriptions and IDs and resets from scratch. */
733
- _resetOptions() {
734
- const changedOrDestroyed = merge(this.options.changes, this._destroy);
735
- this.optionSelectionChanges.pipe(takeUntil(changedOrDestroyed)).subscribe(event => {
736
- this._onSelect(event.source, event.isUserInput);
737
- if (event.isUserInput && !this.multiple && this._panelOpen) {
738
- this.close();
739
- this.focus();
704
+ return correspondingOption;
705
+ }
706
+ /** Sets up a key manager to listen to keyboard events on the overlay panel. */
707
+ _initKeyManager() {
708
+ this._keyManager = new ActiveDescendantKeyManager(this.options)
709
+ .withTypeAhead(this._typeaheadDebounceInterval)
710
+ .withVerticalOrientation()
711
+ .withHorizontalOrientation(this._isRtl() ? 'rtl' : 'ltr')
712
+ .withAllowedModifierKeys(['shiftKey']);
713
+ this._keyManager.tabOut.pipe(takeUntil(this._destroy)).subscribe(() => {
714
+ if (this.panelOpen) {
715
+ // Select the active item when tabbing away. This is consistent with how the native
716
+ // select behaves. Note that we only want to do this in single selection mode.
717
+ if (!this.multiple && this._keyManager.activeItem) {
718
+ this._keyManager.activeItem._selectViaInteraction();
740
719
  }
741
- });
742
- // Listen to changes in the internal state of the options and react accordingly.
743
- // Handles cases like the labels of the selected options changing.
744
- merge(...this.options.map(option => option._stateChanges))
745
- .pipe(takeUntil(changedOrDestroyed))
746
- .subscribe(() => {
747
- this._changeDetectorRef.markForCheck();
748
- this.stateChanges.next();
749
- });
750
- this._setOptionIds();
751
- }
752
- /** Invoked when an option is clicked. */
753
- _onSelect(option, isUserInput) {
754
- const wasSelected = this._selectionModel.isSelected(option);
755
- if (option.value == null && !this._multiple) {
756
- option.deselect();
757
- this._selectionModel.clear();
758
- this._propagateChanges(option.value);
720
+ // Restore focus to the trigger before closing. Ensures that the focus
721
+ // position won't be lost if the user got focus into the overlay.
722
+ this.focus();
723
+ this.close();
759
724
  }
760
- else {
761
- if (wasSelected !== option.selected) {
762
- option.selected ? this._selectionModel.select(option) :
763
- this._selectionModel.deselect(option);
764
- }
765
- if (isUserInput) {
766
- this._keyManager.setActiveItem(option);
767
- }
768
- if (this.multiple) {
769
- this._sortValues();
770
- if (isUserInput) {
771
- // In case the user selected the option with their mouse, we
772
- // want to restore focus back to the trigger, in order to
773
- // prevent the select keyboard controls from clashing with
774
- // the ones from `mat-option`.
775
- this.focus();
776
- }
777
- }
725
+ });
726
+ this._keyManager.change.pipe(takeUntil(this._destroy)).subscribe(() => {
727
+ if (this._panelOpen && this.panel) {
728
+ this._scrollActiveOptionIntoView();
778
729
  }
779
- if (wasSelected !== this._selectionModel.isSelected(option)) {
780
- this._propagateChanges();
730
+ else if (!this._panelOpen && !this.multiple && this._keyManager.activeItem) {
731
+ this._keyManager.activeItem._selectViaInteraction();
781
732
  }
782
- this.stateChanges.next();
783
- }
784
- /** Sorts the selected values in the selected based on their order in the panel. */
785
- _sortValues() {
786
- if (this.multiple) {
787
- const options = this.options.toArray();
788
- this._selectionModel.sort((a, b) => {
789
- return this.sortComparator ? this.sortComparator(a, b, options) :
790
- options.indexOf(a) - options.indexOf(b);
791
- });
792
- this.stateChanges.next();
733
+ });
734
+ }
735
+ /** Drops current option subscriptions and IDs and resets from scratch. */
736
+ _resetOptions() {
737
+ const changedOrDestroyed = merge(this.options.changes, this._destroy);
738
+ this.optionSelectionChanges.pipe(takeUntil(changedOrDestroyed)).subscribe(event => {
739
+ this._onSelect(event.source, event.isUserInput);
740
+ if (event.isUserInput && !this.multiple && this._panelOpen) {
741
+ this.close();
742
+ this.focus();
793
743
  }
744
+ });
745
+ // Listen to changes in the internal state of the options and react accordingly.
746
+ // Handles cases like the labels of the selected options changing.
747
+ merge(...this.options.map(option => option._stateChanges))
748
+ .pipe(takeUntil(changedOrDestroyed))
749
+ .subscribe(() => {
750
+ this._changeDetectorRef.markForCheck();
751
+ this.stateChanges.next();
752
+ });
753
+ this._setOptionIds();
754
+ }
755
+ /** Invoked when an option is clicked. */
756
+ _onSelect(option, isUserInput) {
757
+ const wasSelected = this._selectionModel.isSelected(option);
758
+ if (option.value == null && !this._multiple) {
759
+ option.deselect();
760
+ this._selectionModel.clear();
761
+ this._propagateChanges(option.value);
794
762
  }
795
- /** Emits change event to set the model value. */
796
- _propagateChanges(fallbackValue) {
797
- let valueToEmit = null;
798
- if (this.multiple) {
799
- valueToEmit = this.selected.map(option => option.value);
763
+ else {
764
+ if (wasSelected !== option.selected) {
765
+ option.selected ? this._selectionModel.select(option) :
766
+ this._selectionModel.deselect(option);
800
767
  }
801
- else {
802
- valueToEmit = this.selected ? this.selected.value : fallbackValue;
768
+ if (isUserInput) {
769
+ this._keyManager.setActiveItem(option);
803
770
  }
804
- this._value = valueToEmit;
805
- this.valueChange.emit(valueToEmit);
806
- this._onChange(valueToEmit);
807
- this.selectionChange.emit(new MatSelectChange(this, valueToEmit));
808
- this._changeDetectorRef.markForCheck();
809
- }
810
- /** Records option IDs to pass to the aria-owns property. */
811
- _setOptionIds() {
812
- this._optionIds = this.options.map(option => option.id).join(' ');
813
- }
814
- /**
815
- * Highlights the selected item. If no option is selected, it will highlight
816
- * the first item instead.
817
- */
818
- _highlightCorrectOption() {
819
- if (this._keyManager) {
820
- if (this.empty) {
821
- this._keyManager.setFirstItemActive();
822
- }
823
- else {
824
- this._keyManager.setActiveItem(this._selectionModel.selected[0]);
771
+ if (this.multiple) {
772
+ this._sortValues();
773
+ if (isUserInput) {
774
+ // In case the user selected the option with their mouse, we
775
+ // want to restore focus back to the trigger, in order to
776
+ // prevent the select keyboard controls from clashing with
777
+ // the ones from `mat-option`.
778
+ this.focus();
825
779
  }
826
780
  }
827
781
  }
828
- /** Scrolls the active option into view. */
829
- _scrollActiveOptionIntoView() {
830
- const activeOptionIndex = this._keyManager.activeItemIndex || 0;
831
- const labelCount = _countGroupLabelsBeforeOption(activeOptionIndex, this.options, this.optionGroups);
832
- this.panel.nativeElement.scrollTop = _getOptionScrollPosition(activeOptionIndex + labelCount, this._getItemHeight(), this.panel.nativeElement.scrollTop, SELECT_PANEL_MAX_HEIGHT);
782
+ if (wasSelected !== this._selectionModel.isSelected(option)) {
783
+ this._propagateChanges();
833
784
  }
834
- /** Focuses the select element. */
835
- focus(options) {
836
- this._elementRef.nativeElement.focus(options);
837
- }
838
- /** Gets the index of the provided option in the option list. */
839
- _getOptionIndex(option) {
840
- return this.options.reduce((result, current, index) => {
841
- if (result !== undefined) {
842
- return result;
843
- }
844
- return option === current ? index : undefined;
845
- }, undefined);
846
- }
847
- /** Calculates the scroll position and x- and y-offsets of the overlay panel. */
848
- _calculateOverlayPosition() {
849
- const itemHeight = this._getItemHeight();
850
- const items = this._getItemCount();
851
- const panelHeight = Math.min(items * itemHeight, SELECT_PANEL_MAX_HEIGHT);
852
- const scrollContainerHeight = items * itemHeight;
853
- // The farthest the panel can be scrolled before it hits the bottom
854
- const maxScroll = scrollContainerHeight - panelHeight;
855
- // If no value is selected we open the popup to the first item.
856
- let selectedOptionOffset = this.empty ? 0 : this._getOptionIndex(this._selectionModel.selected[0]);
857
- selectedOptionOffset += _countGroupLabelsBeforeOption(selectedOptionOffset, this.options, this.optionGroups);
858
- // We must maintain a scroll buffer so the selected option will be scrolled to the
859
- // center of the overlay panel rather than the top.
860
- const scrollBuffer = panelHeight / 2;
861
- this._scrollTop = this._calculateOverlayScroll(selectedOptionOffset, scrollBuffer, maxScroll);
862
- this._offsetY = this._calculateOverlayOffsetY(selectedOptionOffset, scrollBuffer, maxScroll);
863
- this._checkOverlayWithinViewport(maxScroll);
785
+ this.stateChanges.next();
786
+ }
787
+ /** Sorts the selected values in the selected based on their order in the panel. */
788
+ _sortValues() {
789
+ if (this.multiple) {
790
+ const options = this.options.toArray();
791
+ this._selectionModel.sort((a, b) => {
792
+ return this.sortComparator ? this.sortComparator(a, b, options) :
793
+ options.indexOf(a) - options.indexOf(b);
794
+ });
795
+ this.stateChanges.next();
864
796
  }
865
- /**
866
- * Calculates the scroll position of the select's overlay panel.
867
- *
868
- * Attempts to center the selected option in the panel. If the option is
869
- * too high or too low in the panel to be scrolled to the center, it clamps the
870
- * scroll position to the min or max scroll positions respectively.
871
- */
872
- _calculateOverlayScroll(selectedIndex, scrollBuffer, maxScroll) {
873
- const itemHeight = this._getItemHeight();
874
- const optionOffsetFromScrollTop = itemHeight * selectedIndex;
875
- const halfOptionHeight = itemHeight / 2;
876
- // Starts at the optionOffsetFromScrollTop, which scrolls the option to the top of the
877
- // scroll container, then subtracts the scroll buffer to scroll the option down to
878
- // the center of the overlay panel. Half the option height must be re-added to the
879
- // scrollTop so the option is centered based on its middle, not its top edge.
880
- const optimalScrollPosition = optionOffsetFromScrollTop - scrollBuffer + halfOptionHeight;
881
- return Math.min(Math.max(0, optimalScrollPosition), maxScroll);
797
+ }
798
+ /** Emits change event to set the model value. */
799
+ _propagateChanges(fallbackValue) {
800
+ let valueToEmit = null;
801
+ if (this.multiple) {
802
+ valueToEmit = this.selected.map(option => option.value);
882
803
  }
883
- /** Returns the aria-label of the select component. */
884
- _getAriaLabel() {
885
- // If an ariaLabelledby value has been set by the consumer, the select should not overwrite the
886
- // `aria-labelledby` value by setting the ariaLabel to the placeholder.
887
- return this.ariaLabelledby ? null : this.ariaLabel || this.placeholder;
804
+ else {
805
+ valueToEmit = this.selected ? this.selected.value : fallbackValue;
888
806
  }
889
- /** Returns the aria-labelledby of the select component. */
890
- _getAriaLabelledby() {
891
- if (this.ariaLabelledby) {
892
- return this.ariaLabelledby;
807
+ this._value = valueToEmit;
808
+ this.valueChange.emit(valueToEmit);
809
+ this._onChange(valueToEmit);
810
+ this.selectionChange.emit(new MatSelectChange(this, valueToEmit));
811
+ this._changeDetectorRef.markForCheck();
812
+ }
813
+ /** Records option IDs to pass to the aria-owns property. */
814
+ _setOptionIds() {
815
+ this._optionIds = this.options.map(option => option.id).join(' ');
816
+ }
817
+ /**
818
+ * Highlights the selected item. If no option is selected, it will highlight
819
+ * the first item instead.
820
+ */
821
+ _highlightCorrectOption() {
822
+ if (this._keyManager) {
823
+ if (this.empty) {
824
+ this._keyManager.setFirstItemActive();
893
825
  }
894
- // Note: we use `_getAriaLabel` here, because we want to check whether there's a
895
- // computed label. `this.ariaLabel` is only the user-specified label.
896
- if (!this._parentFormField || !this._parentFormField._hasFloatingLabel() ||
897
- this._getAriaLabel()) {
898
- return null;
826
+ else {
827
+ this._keyManager.setActiveItem(this._selectionModel.selected[0]);
899
828
  }
900
- return this._parentFormField._labelId || null;
901
829
  }
902
- /** Determines the `aria-activedescendant` to be set on the host. */
903
- _getAriaActiveDescendant() {
904
- if (this.panelOpen && this._keyManager && this._keyManager.activeItem) {
905
- return this._keyManager.activeItem.id;
830
+ }
831
+ /** Scrolls the active option into view. */
832
+ _scrollActiveOptionIntoView() {
833
+ const activeOptionIndex = this._keyManager.activeItemIndex || 0;
834
+ const labelCount = _countGroupLabelsBeforeOption(activeOptionIndex, this.options, this.optionGroups);
835
+ this.panel.nativeElement.scrollTop = _getOptionScrollPosition(activeOptionIndex + labelCount, this._getItemHeight(), this.panel.nativeElement.scrollTop, SELECT_PANEL_MAX_HEIGHT);
836
+ }
837
+ /** Focuses the select element. */
838
+ focus(options) {
839
+ this._elementRef.nativeElement.focus(options);
840
+ }
841
+ /** Gets the index of the provided option in the option list. */
842
+ _getOptionIndex(option) {
843
+ return this.options.reduce((result, current, index) => {
844
+ if (result !== undefined) {
845
+ return result;
906
846
  }
847
+ return option === current ? index : undefined;
848
+ }, undefined);
849
+ }
850
+ /** Calculates the scroll position and x- and y-offsets of the overlay panel. */
851
+ _calculateOverlayPosition() {
852
+ const itemHeight = this._getItemHeight();
853
+ const items = this._getItemCount();
854
+ const panelHeight = Math.min(items * itemHeight, SELECT_PANEL_MAX_HEIGHT);
855
+ const scrollContainerHeight = items * itemHeight;
856
+ // The farthest the panel can be scrolled before it hits the bottom
857
+ const maxScroll = scrollContainerHeight - panelHeight;
858
+ // If no value is selected we open the popup to the first item.
859
+ let selectedOptionOffset = this.empty ? 0 : this._getOptionIndex(this._selectionModel.selected[0]);
860
+ selectedOptionOffset += _countGroupLabelsBeforeOption(selectedOptionOffset, this.options, this.optionGroups);
861
+ // We must maintain a scroll buffer so the selected option will be scrolled to the
862
+ // center of the overlay panel rather than the top.
863
+ const scrollBuffer = panelHeight / 2;
864
+ this._scrollTop = this._calculateOverlayScroll(selectedOptionOffset, scrollBuffer, maxScroll);
865
+ this._offsetY = this._calculateOverlayOffsetY(selectedOptionOffset, scrollBuffer, maxScroll);
866
+ this._checkOverlayWithinViewport(maxScroll);
867
+ }
868
+ /**
869
+ * Calculates the scroll position of the select's overlay panel.
870
+ *
871
+ * Attempts to center the selected option in the panel. If the option is
872
+ * too high or too low in the panel to be scrolled to the center, it clamps the
873
+ * scroll position to the min or max scroll positions respectively.
874
+ */
875
+ _calculateOverlayScroll(selectedIndex, scrollBuffer, maxScroll) {
876
+ const itemHeight = this._getItemHeight();
877
+ const optionOffsetFromScrollTop = itemHeight * selectedIndex;
878
+ const halfOptionHeight = itemHeight / 2;
879
+ // Starts at the optionOffsetFromScrollTop, which scrolls the option to the top of the
880
+ // scroll container, then subtracts the scroll buffer to scroll the option down to
881
+ // the center of the overlay panel. Half the option height must be re-added to the
882
+ // scrollTop so the option is centered based on its middle, not its top edge.
883
+ const optimalScrollPosition = optionOffsetFromScrollTop - scrollBuffer + halfOptionHeight;
884
+ return Math.min(Math.max(0, optimalScrollPosition), maxScroll);
885
+ }
886
+ /** Returns the aria-label of the select component. */
887
+ _getAriaLabel() {
888
+ // If an ariaLabelledby value has been set by the consumer, the select should not overwrite the
889
+ // `aria-labelledby` value by setting the ariaLabel to the placeholder.
890
+ return this.ariaLabelledby ? null : this.ariaLabel || this.placeholder;
891
+ }
892
+ /** Returns the aria-labelledby of the select component. */
893
+ _getAriaLabelledby() {
894
+ if (this.ariaLabelledby) {
895
+ return this.ariaLabelledby;
896
+ }
897
+ // Note: we use `_getAriaLabel` here, because we want to check whether there's a
898
+ // computed label. `this.ariaLabel` is only the user-specified label.
899
+ if (!this._parentFormField || !this._parentFormField._hasFloatingLabel() ||
900
+ this._getAriaLabel()) {
907
901
  return null;
908
902
  }
909
- /**
910
- * Sets the x-offset of the overlay panel in relation to the trigger's top start corner.
911
- * This must be adjusted to align the selected option text over the trigger text when
912
- * the panel opens. Will change based on LTR or RTL text direction. Note that the offset
913
- * can't be calculated until the panel has been attached, because we need to know the
914
- * content width in order to constrain the panel within the viewport.
915
- */
916
- _calculateOverlayOffsetX() {
917
- const overlayRect = this.overlayDir.overlayRef.overlayElement.getBoundingClientRect();
918
- const viewportSize = this._viewportRuler.getViewportSize();
919
- const isRtl = this._isRtl();
920
- const paddingWidth = this.multiple ? SELECT_MULTIPLE_PANEL_PADDING_X + SELECT_PANEL_PADDING_X :
921
- SELECT_PANEL_PADDING_X * 2;
922
- let offsetX;
923
- // Adjust the offset, depending on the option padding.
924
- if (this.multiple) {
925
- offsetX = SELECT_MULTIPLE_PANEL_PADDING_X;
926
- }
927
- else {
928
- let selected = this._selectionModel.selected[0] || this.options.first;
929
- offsetX = selected && selected.group ? SELECT_PANEL_INDENT_PADDING_X : SELECT_PANEL_PADDING_X;
930
- }
931
- // Invert the offset in LTR.
932
- if (!isRtl) {
933
- offsetX *= -1;
934
- }
935
- // Determine how much the select overflows on each side.
936
- const leftOverflow = 0 - (overlayRect.left + offsetX - (isRtl ? paddingWidth : 0));
937
- const rightOverflow = overlayRect.right + offsetX - viewportSize.width
938
- + (isRtl ? 0 : paddingWidth);
939
- // If the element overflows on either side, reduce the offset to allow it to fit.
940
- if (leftOverflow > 0) {
941
- offsetX += leftOverflow + SELECT_PANEL_VIEWPORT_PADDING;
942
- }
943
- else if (rightOverflow > 0) {
944
- offsetX -= rightOverflow + SELECT_PANEL_VIEWPORT_PADDING;
945
- }
946
- // Set the offset directly in order to avoid having to go through change detection and
947
- // potentially triggering "changed after it was checked" errors. Round the value to avoid
948
- // blurry content in some browsers.
949
- this.overlayDir.offsetX = Math.round(offsetX);
950
- this.overlayDir.overlayRef.updatePosition();
903
+ return this._parentFormField._labelId || null;
904
+ }
905
+ /** Determines the `aria-activedescendant` to be set on the host. */
906
+ _getAriaActiveDescendant() {
907
+ if (this.panelOpen && this._keyManager && this._keyManager.activeItem) {
908
+ return this._keyManager.activeItem.id;
951
909
  }
952
- /**
953
- * Calculates the y-offset of the select's overlay panel in relation to the
954
- * top start corner of the trigger. It has to be adjusted in order for the
955
- * selected option to be aligned over the trigger when the panel opens.
956
- */
957
- _calculateOverlayOffsetY(selectedIndex, scrollBuffer, maxScroll) {
958
- const itemHeight = this._getItemHeight();
959
- const optionHeightAdjustment = (itemHeight - this._triggerRect.height) / 2;
960
- const maxOptionsDisplayed = Math.floor(SELECT_PANEL_MAX_HEIGHT / itemHeight);
961
- let optionOffsetFromPanelTop;
962
- // Disable offset if requested by user by returning 0 as value to offset
963
- if (this._disableOptionCentering) {
964
- return 0;
965
- }
966
- if (this._scrollTop === 0) {
967
- optionOffsetFromPanelTop = selectedIndex * itemHeight;
968
- }
969
- else if (this._scrollTop === maxScroll) {
970
- const firstDisplayedIndex = this._getItemCount() - maxOptionsDisplayed;
971
- const selectedDisplayIndex = selectedIndex - firstDisplayedIndex;
972
- // The first item is partially out of the viewport. Therefore we need to calculate what
973
- // portion of it is shown in the viewport and account for it in our offset.
974
- let partialItemHeight = itemHeight - (this._getItemCount() * itemHeight - SELECT_PANEL_MAX_HEIGHT) % itemHeight;
975
- // Because the panel height is longer than the height of the options alone,
976
- // there is always extra padding at the top or bottom of the panel. When
977
- // scrolled to the very bottom, this padding is at the top of the panel and
978
- // must be added to the offset.
979
- optionOffsetFromPanelTop = selectedDisplayIndex * itemHeight + partialItemHeight;
980
- }
981
- else {
982
- // If the option was scrolled to the middle of the panel using a scroll buffer,
983
- // its offset will be the scroll buffer minus the half height that was added to
984
- // center it.
985
- optionOffsetFromPanelTop = scrollBuffer - itemHeight / 2;
986
- }
987
- // The final offset is the option's offset from the top, adjusted for the height difference,
988
- // multiplied by -1 to ensure that the overlay moves in the correct direction up the page.
989
- // The value is rounded to prevent some browsers from blurring the content.
990
- return Math.round(optionOffsetFromPanelTop * -1 - optionHeightAdjustment);
910
+ return null;
911
+ }
912
+ /**
913
+ * Sets the x-offset of the overlay panel in relation to the trigger's top start corner.
914
+ * This must be adjusted to align the selected option text over the trigger text when
915
+ * the panel opens. Will change based on LTR or RTL text direction. Note that the offset
916
+ * can't be calculated until the panel has been attached, because we need to know the
917
+ * content width in order to constrain the panel within the viewport.
918
+ */
919
+ _calculateOverlayOffsetX() {
920
+ const overlayRect = this.overlayDir.overlayRef.overlayElement.getBoundingClientRect();
921
+ const viewportSize = this._viewportRuler.getViewportSize();
922
+ const isRtl = this._isRtl();
923
+ const paddingWidth = this.multiple ? SELECT_MULTIPLE_PANEL_PADDING_X + SELECT_PANEL_PADDING_X :
924
+ SELECT_PANEL_PADDING_X * 2;
925
+ let offsetX;
926
+ // Adjust the offset, depending on the option padding.
927
+ if (this.multiple) {
928
+ offsetX = SELECT_MULTIPLE_PANEL_PADDING_X;
991
929
  }
992
- /**
993
- * Checks that the attempted overlay position will fit within the viewport.
994
- * If it will not fit, tries to adjust the scroll position and the associated
995
- * y-offset so the panel can open fully on-screen. If it still won't fit,
996
- * sets the offset back to 0 to allow the fallback position to take over.
997
- */
998
- _checkOverlayWithinViewport(maxScroll) {
999
- const itemHeight = this._getItemHeight();
1000
- const viewportSize = this._viewportRuler.getViewportSize();
1001
- const topSpaceAvailable = this._triggerRect.top - SELECT_PANEL_VIEWPORT_PADDING;
1002
- const bottomSpaceAvailable = viewportSize.height - this._triggerRect.bottom - SELECT_PANEL_VIEWPORT_PADDING;
1003
- const panelHeightTop = Math.abs(this._offsetY);
1004
- const totalPanelHeight = Math.min(this._getItemCount() * itemHeight, SELECT_PANEL_MAX_HEIGHT);
1005
- const panelHeightBottom = totalPanelHeight - panelHeightTop - this._triggerRect.height;
1006
- if (panelHeightBottom > bottomSpaceAvailable) {
1007
- this._adjustPanelUp(panelHeightBottom, bottomSpaceAvailable);
1008
- }
1009
- else if (panelHeightTop > topSpaceAvailable) {
1010
- this._adjustPanelDown(panelHeightTop, topSpaceAvailable, maxScroll);
1011
- }
1012
- else {
1013
- this._transformOrigin = this._getOriginBasedOnOption();
1014
- }
930
+ else {
931
+ let selected = this._selectionModel.selected[0] || this.options.first;
932
+ offsetX = selected && selected.group ? SELECT_PANEL_INDENT_PADDING_X : SELECT_PANEL_PADDING_X;
1015
933
  }
1016
- /** Adjusts the overlay panel up to fit in the viewport. */
1017
- _adjustPanelUp(panelHeightBottom, bottomSpaceAvailable) {
1018
- // Browsers ignore fractional scroll offsets, so we need to round.
1019
- const distanceBelowViewport = Math.round(panelHeightBottom - bottomSpaceAvailable);
1020
- // Scrolls the panel up by the distance it was extending past the boundary, then
1021
- // adjusts the offset by that amount to move the panel up into the viewport.
1022
- this._scrollTop -= distanceBelowViewport;
1023
- this._offsetY -= distanceBelowViewport;
1024
- this._transformOrigin = this._getOriginBasedOnOption();
1025
- // If the panel is scrolled to the very top, it won't be able to fit the panel
1026
- // by scrolling, so set the offset to 0 to allow the fallback position to take
1027
- // effect.
1028
- if (this._scrollTop <= 0) {
1029
- this._scrollTop = 0;
1030
- this._offsetY = 0;
1031
- this._transformOrigin = `50% bottom 0px`;
1032
- }
934
+ // Invert the offset in LTR.
935
+ if (!isRtl) {
936
+ offsetX *= -1;
1033
937
  }
1034
- /** Adjusts the overlay panel down to fit in the viewport. */
1035
- _adjustPanelDown(panelHeightTop, topSpaceAvailable, maxScroll) {
1036
- // Browsers ignore fractional scroll offsets, so we need to round.
1037
- const distanceAboveViewport = Math.round(panelHeightTop - topSpaceAvailable);
1038
- // Scrolls the panel down by the distance it was extending past the boundary, then
1039
- // adjusts the offset by that amount to move the panel down into the viewport.
1040
- this._scrollTop += distanceAboveViewport;
1041
- this._offsetY += distanceAboveViewport;
1042
- this._transformOrigin = this._getOriginBasedOnOption();
1043
- // If the panel is scrolled to the very bottom, it won't be able to fit the
1044
- // panel by scrolling, so set the offset to 0 to allow the fallback position
1045
- // to take effect.
1046
- if (this._scrollTop >= maxScroll) {
1047
- this._scrollTop = maxScroll;
1048
- this._offsetY = 0;
1049
- this._transformOrigin = `50% top 0px`;
1050
- return;
1051
- }
938
+ // Determine how much the select overflows on each side.
939
+ const leftOverflow = 0 - (overlayRect.left + offsetX - (isRtl ? paddingWidth : 0));
940
+ const rightOverflow = overlayRect.right + offsetX - viewportSize.width
941
+ + (isRtl ? 0 : paddingWidth);
942
+ // If the element overflows on either side, reduce the offset to allow it to fit.
943
+ if (leftOverflow > 0) {
944
+ offsetX += leftOverflow + SELECT_PANEL_VIEWPORT_PADDING;
1052
945
  }
1053
- /** Sets the transform origin point based on the selected option. */
1054
- _getOriginBasedOnOption() {
1055
- const itemHeight = this._getItemHeight();
1056
- const optionHeightAdjustment = (itemHeight - this._triggerRect.height) / 2;
1057
- const originY = Math.abs(this._offsetY) - optionHeightAdjustment + itemHeight / 2;
1058
- return `50% ${originY}px 0px`;
946
+ else if (rightOverflow > 0) {
947
+ offsetX -= rightOverflow + SELECT_PANEL_VIEWPORT_PADDING;
1059
948
  }
1060
- /** Calculates the amount of items in the select. This includes options and group labels. */
1061
- _getItemCount() {
1062
- return this.options.length + this.optionGroups.length;
949
+ // Set the offset directly in order to avoid having to go through change detection and
950
+ // potentially triggering "changed after it was checked" errors. Round the value to avoid
951
+ // blurry content in some browsers.
952
+ this.overlayDir.offsetX = Math.round(offsetX);
953
+ this.overlayDir.overlayRef.updatePosition();
954
+ }
955
+ /**
956
+ * Calculates the y-offset of the select's overlay panel in relation to the
957
+ * top start corner of the trigger. It has to be adjusted in order for the
958
+ * selected option to be aligned over the trigger when the panel opens.
959
+ */
960
+ _calculateOverlayOffsetY(selectedIndex, scrollBuffer, maxScroll) {
961
+ const itemHeight = this._getItemHeight();
962
+ const optionHeightAdjustment = (itemHeight - this._triggerRect.height) / 2;
963
+ const maxOptionsDisplayed = Math.floor(SELECT_PANEL_MAX_HEIGHT / itemHeight);
964
+ let optionOffsetFromPanelTop;
965
+ // Disable offset if requested by user by returning 0 as value to offset
966
+ if (this._disableOptionCentering) {
967
+ return 0;
1063
968
  }
1064
- /** Calculates the height of the select's options. */
1065
- _getItemHeight() {
1066
- return this._triggerFontSize * SELECT_ITEM_HEIGHT_EM;
969
+ if (this._scrollTop === 0) {
970
+ optionOffsetFromPanelTop = selectedIndex * itemHeight;
1067
971
  }
1068
- /**
1069
- * Implemented as part of MatFormFieldControl.
1070
- * @docs-private
1071
- */
1072
- setDescribedByIds(ids) {
1073
- this._ariaDescribedby = ids.join(' ');
972
+ else if (this._scrollTop === maxScroll) {
973
+ const firstDisplayedIndex = this._getItemCount() - maxOptionsDisplayed;
974
+ const selectedDisplayIndex = selectedIndex - firstDisplayedIndex;
975
+ // The first item is partially out of the viewport. Therefore we need to calculate what
976
+ // portion of it is shown in the viewport and account for it in our offset.
977
+ let partialItemHeight = itemHeight - (this._getItemCount() * itemHeight - SELECT_PANEL_MAX_HEIGHT) % itemHeight;
978
+ // Because the panel height is longer than the height of the options alone,
979
+ // there is always extra padding at the top or bottom of the panel. When
980
+ // scrolled to the very bottom, this padding is at the top of the panel and
981
+ // must be added to the offset.
982
+ optionOffsetFromPanelTop = selectedDisplayIndex * itemHeight + partialItemHeight;
1074
983
  }
1075
- /**
1076
- * Implemented as part of MatFormFieldControl.
1077
- * @docs-private
1078
- */
1079
- onContainerClick() {
1080
- this.focus();
1081
- this.open();
984
+ else {
985
+ // If the option was scrolled to the middle of the panel using a scroll buffer,
986
+ // its offset will be the scroll buffer minus the half height that was added to
987
+ // center it.
988
+ optionOffsetFromPanelTop = scrollBuffer - itemHeight / 2;
1082
989
  }
1083
- /**
1084
- * Implemented as part of MatFormFieldControl.
1085
- * @docs-private
1086
- */
1087
- get shouldLabelFloat() {
1088
- return this._panelOpen || !this.empty;
990
+ // The final offset is the option's offset from the top, adjusted for the height difference,
991
+ // multiplied by -1 to ensure that the overlay moves in the correct direction up the page.
992
+ // The value is rounded to prevent some browsers from blurring the content.
993
+ return Math.round(optionOffsetFromPanelTop * -1 - optionHeightAdjustment);
994
+ }
995
+ /**
996
+ * Checks that the attempted overlay position will fit within the viewport.
997
+ * If it will not fit, tries to adjust the scroll position and the associated
998
+ * y-offset so the panel can open fully on-screen. If it still won't fit,
999
+ * sets the offset back to 0 to allow the fallback position to take over.
1000
+ */
1001
+ _checkOverlayWithinViewport(maxScroll) {
1002
+ const itemHeight = this._getItemHeight();
1003
+ const viewportSize = this._viewportRuler.getViewportSize();
1004
+ const topSpaceAvailable = this._triggerRect.top - SELECT_PANEL_VIEWPORT_PADDING;
1005
+ const bottomSpaceAvailable = viewportSize.height - this._triggerRect.bottom - SELECT_PANEL_VIEWPORT_PADDING;
1006
+ const panelHeightTop = Math.abs(this._offsetY);
1007
+ const totalPanelHeight = Math.min(this._getItemCount() * itemHeight, SELECT_PANEL_MAX_HEIGHT);
1008
+ const panelHeightBottom = totalPanelHeight - panelHeightTop - this._triggerRect.height;
1009
+ if (panelHeightBottom > bottomSpaceAvailable) {
1010
+ this._adjustPanelUp(panelHeightBottom, bottomSpaceAvailable);
1011
+ }
1012
+ else if (panelHeightTop > topSpaceAvailable) {
1013
+ this._adjustPanelDown(panelHeightTop, topSpaceAvailable, maxScroll);
1014
+ }
1015
+ else {
1016
+ this._transformOrigin = this._getOriginBasedOnOption();
1017
+ }
1018
+ }
1019
+ /** Adjusts the overlay panel up to fit in the viewport. */
1020
+ _adjustPanelUp(panelHeightBottom, bottomSpaceAvailable) {
1021
+ // Browsers ignore fractional scroll offsets, so we need to round.
1022
+ const distanceBelowViewport = Math.round(panelHeightBottom - bottomSpaceAvailable);
1023
+ // Scrolls the panel up by the distance it was extending past the boundary, then
1024
+ // adjusts the offset by that amount to move the panel up into the viewport.
1025
+ this._scrollTop -= distanceBelowViewport;
1026
+ this._offsetY -= distanceBelowViewport;
1027
+ this._transformOrigin = this._getOriginBasedOnOption();
1028
+ // If the panel is scrolled to the very top, it won't be able to fit the panel
1029
+ // by scrolling, so set the offset to 0 to allow the fallback position to take
1030
+ // effect.
1031
+ if (this._scrollTop <= 0) {
1032
+ this._scrollTop = 0;
1033
+ this._offsetY = 0;
1034
+ this._transformOrigin = `50% bottom 0px`;
1089
1035
  }
1090
1036
  }
1091
- MatSelect.decorators = [
1092
- { type: Component, args: [{
1093
- selector: 'mat-select',
1094
- exportAs: 'matSelect',
1095
- template: "<div cdk-overlay-origin\n class=\"mat-select-trigger\"\n aria-hidden=\"true\"\n (click)=\"toggle()\"\n #origin=\"cdkOverlayOrigin\"\n #trigger>\n <div class=\"mat-select-value\" [ngSwitch]=\"empty\">\n <span class=\"mat-select-placeholder\" *ngSwitchCase=\"true\">{{placeholder || '\\u00A0'}}</span>\n <span class=\"mat-select-value-text\" *ngSwitchCase=\"false\" [ngSwitch]=\"!!customTrigger\">\n <span *ngSwitchDefault>{{triggerValue || '\\u00A0'}}</span>\n <ng-content select=\"mat-select-trigger\" *ngSwitchCase=\"true\"></ng-content>\n </span>\n </div>\n\n <div class=\"mat-select-arrow-wrapper\"><div class=\"mat-select-arrow\"></div></div>\n</div>\n\n<ng-template\n cdk-connected-overlay\n cdkConnectedOverlayLockPosition\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayScrollStrategy]=\"_scrollStrategy\"\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"panelOpen\"\n [cdkConnectedOverlayPositions]=\"_positions\"\n [cdkConnectedOverlayMinWidth]=\"_triggerRect?.width\"\n [cdkConnectedOverlayOffsetY]=\"_offsetY\"\n (backdropClick)=\"close()\"\n (attach)=\"_onAttached()\"\n (detach)=\"close()\">\n <div class=\"mat-select-panel-wrap\" [@transformPanelWrap]>\n <div\n #panel\n [attr.id]=\"id + '-panel'\"\n class=\"mat-select-panel {{ _getPanelTheme() }}\"\n [ngClass]=\"panelClass\"\n [@transformPanel]=\"multiple ? 'showing-multiple' : 'showing'\"\n (@transformPanel.done)=\"_panelDoneAnimatingStream.next($event.toState)\"\n [style.transformOrigin]=\"_transformOrigin\"\n [style.font-size.px]=\"_triggerFontSize\"\n (keydown)=\"_handleKeydown($event)\">\n <ng-content></ng-content>\n </div>\n </div>\n</ng-template>\n",
1096
- inputs: ['disabled', 'disableRipple', 'tabIndex'],
1097
- encapsulation: ViewEncapsulation.None,
1098
- changeDetection: ChangeDetectionStrategy.OnPush,
1099
- host: {
1100
- 'role': 'listbox',
1101
- '[attr.id]': 'id',
1102
- '[attr.tabindex]': 'tabIndex',
1103
- '[attr.aria-label]': '_getAriaLabel()',
1104
- '[attr.aria-labelledby]': '_getAriaLabelledby()',
1105
- '[attr.aria-required]': 'required.toString()',
1106
- '[attr.aria-disabled]': 'disabled.toString()',
1107
- '[attr.aria-invalid]': 'errorState',
1108
- '[attr.aria-owns]': 'panelOpen ? _optionIds : null',
1109
- '[attr.aria-multiselectable]': 'multiple',
1110
- '[attr.aria-describedby]': '_ariaDescribedby || null',
1111
- '[attr.aria-activedescendant]': '_getAriaActiveDescendant()',
1112
- '[class.mat-select-disabled]': 'disabled',
1113
- '[class.mat-select-invalid]': 'errorState',
1114
- '[class.mat-select-required]': 'required',
1115
- '[class.mat-select-empty]': 'empty',
1116
- 'class': 'mat-select',
1117
- '(keydown)': '_handleKeydown($event)',
1118
- '(focus)': '_onFocus()',
1119
- '(blur)': '_onBlur()',
1120
- },
1121
- animations: [
1122
- matSelectAnimations.transformPanelWrap,
1123
- matSelectAnimations.transformPanel
1124
- ],
1125
- providers: [
1126
- { provide: MatFormFieldControl, useExisting: MatSelect },
1127
- { provide: MAT_OPTION_PARENT_COMPONENT, useExisting: MatSelect }
1128
- ],
1129
- styles: [".mat-select{display:inline-block;width:100%;outline:none}.mat-select-trigger{display:inline-table;cursor:pointer;position:relative;box-sizing:border-box}.mat-select-disabled .mat-select-trigger{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.mat-select-value{display:table-cell;max-width:0;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mat-select-value-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mat-select-arrow-wrapper{display:table-cell;vertical-align:middle}.mat-form-field-appearance-fill .mat-select-arrow-wrapper{transform:translateY(-50%)}.mat-form-field-appearance-outline .mat-select-arrow-wrapper{transform:translateY(-25%)}.mat-form-field-appearance-standard.mat-form-field-has-label .mat-select:not(.mat-select-empty) .mat-select-arrow-wrapper{transform:translateY(-50%)}.mat-form-field-appearance-standard .mat-select.mat-select-empty .mat-select-arrow-wrapper{transition:transform 400ms cubic-bezier(0.25, 0.8, 0.25, 1)}._mat-animation-noopable.mat-form-field-appearance-standard .mat-select.mat-select-empty .mat-select-arrow-wrapper{transition:none}.mat-select-arrow{width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid;margin:0 4px}.mat-select-panel-wrap{flex-basis:100%}.mat-select-panel{min-width:112px;max-width:280px;overflow:auto;-webkit-overflow-scrolling:touch;padding-top:0;padding-bottom:0;max-height:256px;min-width:100%;border-radius:4px}.cdk-high-contrast-active .mat-select-panel{outline:solid 1px}.mat-select-panel .mat-optgroup-label,.mat-select-panel .mat-option{font-size:inherit;line-height:3em;height:3em}.mat-form-field-type-mat-select:not(.mat-form-field-disabled) .mat-form-field-flex{cursor:pointer}.mat-form-field-type-mat-select .mat-form-field-label{width:calc(100% - 18px)}.mat-select-placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1)}._mat-animation-noopable .mat-select-placeholder{transition:none}.mat-form-field-hide-placeholder .mat-select-placeholder{color:transparent;-webkit-text-fill-color:transparent;transition:none;display:block}\n"]
1130
- },] }
1131
- ];
1132
- MatSelect.ctorParameters = () => [
1133
- { type: ViewportRuler },
1134
- { type: ChangeDetectorRef },
1135
- { type: NgZone },
1136
- { type: ErrorStateMatcher },
1137
- { type: ElementRef },
1138
- { type: Directionality, decorators: [{ type: Optional }] },
1139
- { type: NgForm, decorators: [{ type: Optional }] },
1140
- { type: FormGroupDirective, decorators: [{ type: Optional }] },
1141
- { type: MatFormField, decorators: [{ type: Optional }, { type: Inject, args: [MAT_FORM_FIELD,] }] },
1142
- { type: NgControl, decorators: [{ type: Self }, { type: Optional }] },
1143
- { type: String, decorators: [{ type: Attribute, args: ['tabindex',] }] },
1144
- { type: undefined, decorators: [{ type: Inject, args: [MAT_SELECT_SCROLL_STRATEGY,] }] },
1145
- { type: LiveAnnouncer },
1146
- { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [MAT_SELECT_CONFIG,] }] }
1147
- ];
1148
- MatSelect.propDecorators = {
1149
- trigger: [{ type: ViewChild, args: ['trigger',] }],
1150
- panel: [{ type: ViewChild, args: ['panel',] }],
1151
- overlayDir: [{ type: ViewChild, args: [CdkConnectedOverlay,] }],
1152
- options: [{ type: ContentChildren, args: [MatOption, { descendants: true },] }],
1153
- optionGroups: [{ type: ContentChildren, args: [MatOptgroup, { descendants: true },] }],
1154
- panelClass: [{ type: Input }],
1155
- customTrigger: [{ type: ContentChild, args: [MatSelectTrigger,] }],
1156
- placeholder: [{ type: Input }],
1157
- required: [{ type: Input }],
1158
- multiple: [{ type: Input }],
1159
- disableOptionCentering: [{ type: Input }],
1160
- compareWith: [{ type: Input }],
1161
- value: [{ type: Input }],
1162
- ariaLabel: [{ type: Input, args: ['aria-label',] }],
1163
- ariaLabelledby: [{ type: Input, args: ['aria-labelledby',] }],
1164
- errorStateMatcher: [{ type: Input }],
1165
- typeaheadDebounceInterval: [{ type: Input }],
1166
- sortComparator: [{ type: Input }],
1167
- id: [{ type: Input }],
1168
- openedChange: [{ type: Output }],
1169
- _openedStream: [{ type: Output, args: ['opened',] }],
1170
- _closedStream: [{ type: Output, args: ['closed',] }],
1171
- selectionChange: [{ type: Output }],
1172
- valueChange: [{ type: Output }]
1173
- };
1174
- return MatSelect;
1175
- })();
1037
+ /** Adjusts the overlay panel down to fit in the viewport. */
1038
+ _adjustPanelDown(panelHeightTop, topSpaceAvailable, maxScroll) {
1039
+ // Browsers ignore fractional scroll offsets, so we need to round.
1040
+ const distanceAboveViewport = Math.round(panelHeightTop - topSpaceAvailable);
1041
+ // Scrolls the panel down by the distance it was extending past the boundary, then
1042
+ // adjusts the offset by that amount to move the panel down into the viewport.
1043
+ this._scrollTop += distanceAboveViewport;
1044
+ this._offsetY += distanceAboveViewport;
1045
+ this._transformOrigin = this._getOriginBasedOnOption();
1046
+ // If the panel is scrolled to the very bottom, it won't be able to fit the
1047
+ // panel by scrolling, so set the offset to 0 to allow the fallback position
1048
+ // to take effect.
1049
+ if (this._scrollTop >= maxScroll) {
1050
+ this._scrollTop = maxScroll;
1051
+ this._offsetY = 0;
1052
+ this._transformOrigin = `50% top 0px`;
1053
+ return;
1054
+ }
1055
+ }
1056
+ /** Sets the transform origin point based on the selected option. */
1057
+ _getOriginBasedOnOption() {
1058
+ const itemHeight = this._getItemHeight();
1059
+ const optionHeightAdjustment = (itemHeight - this._triggerRect.height) / 2;
1060
+ const originY = Math.abs(this._offsetY) - optionHeightAdjustment + itemHeight / 2;
1061
+ return `50% ${originY}px 0px`;
1062
+ }
1063
+ /** Calculates the amount of items in the select. This includes options and group labels. */
1064
+ _getItemCount() {
1065
+ return this.options.length + this.optionGroups.length;
1066
+ }
1067
+ /** Calculates the height of the select's options. */
1068
+ _getItemHeight() {
1069
+ return this._triggerFontSize * SELECT_ITEM_HEIGHT_EM;
1070
+ }
1071
+ /**
1072
+ * Implemented as part of MatFormFieldControl.
1073
+ * @docs-private
1074
+ */
1075
+ setDescribedByIds(ids) {
1076
+ this._ariaDescribedby = ids.join(' ');
1077
+ }
1078
+ /**
1079
+ * Implemented as part of MatFormFieldControl.
1080
+ * @docs-private
1081
+ */
1082
+ onContainerClick() {
1083
+ this.focus();
1084
+ this.open();
1085
+ }
1086
+ /**
1087
+ * Implemented as part of MatFormFieldControl.
1088
+ * @docs-private
1089
+ */
1090
+ get shouldLabelFloat() {
1091
+ return this._panelOpen || !this.empty;
1092
+ }
1093
+ }
1094
+ MatSelect.decorators = [
1095
+ { type: Component, args: [{
1096
+ selector: 'mat-select',
1097
+ exportAs: 'matSelect',
1098
+ template: "<div cdk-overlay-origin\n class=\"mat-select-trigger\"\n aria-hidden=\"true\"\n (click)=\"toggle()\"\n #origin=\"cdkOverlayOrigin\"\n #trigger>\n <div class=\"mat-select-value\" [ngSwitch]=\"empty\">\n <span class=\"mat-select-placeholder\" *ngSwitchCase=\"true\">{{placeholder || '\\u00A0'}}</span>\n <span class=\"mat-select-value-text\" *ngSwitchCase=\"false\" [ngSwitch]=\"!!customTrigger\">\n <span *ngSwitchDefault>{{triggerValue || '\\u00A0'}}</span>\n <ng-content select=\"mat-select-trigger\" *ngSwitchCase=\"true\"></ng-content>\n </span>\n </div>\n\n <div class=\"mat-select-arrow-wrapper\"><div class=\"mat-select-arrow\"></div></div>\n</div>\n\n<ng-template\n cdk-connected-overlay\n cdkConnectedOverlayLockPosition\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayScrollStrategy]=\"_scrollStrategy\"\n [cdkConnectedOverlayOrigin]=\"origin\"\n [cdkConnectedOverlayOpen]=\"panelOpen\"\n [cdkConnectedOverlayPositions]=\"_positions\"\n [cdkConnectedOverlayMinWidth]=\"_triggerRect?.width\"\n [cdkConnectedOverlayOffsetY]=\"_offsetY\"\n (backdropClick)=\"close()\"\n (attach)=\"_onAttached()\"\n (detach)=\"close()\">\n <div class=\"mat-select-panel-wrap\" [@transformPanelWrap]>\n <div\n #panel\n [attr.id]=\"id + '-panel'\"\n class=\"mat-select-panel {{ _getPanelTheme() }}\"\n [ngClass]=\"panelClass\"\n [@transformPanel]=\"multiple ? 'showing-multiple' : 'showing'\"\n (@transformPanel.done)=\"_panelDoneAnimatingStream.next($event.toState)\"\n [style.transformOrigin]=\"_transformOrigin\"\n [style.font-size.px]=\"_triggerFontSize\"\n (keydown)=\"_handleKeydown($event)\">\n <ng-content></ng-content>\n </div>\n </div>\n</ng-template>\n",
1099
+ inputs: ['disabled', 'disableRipple', 'tabIndex'],
1100
+ encapsulation: ViewEncapsulation.None,
1101
+ changeDetection: ChangeDetectionStrategy.OnPush,
1102
+ host: {
1103
+ 'role': 'listbox',
1104
+ '[attr.id]': 'id',
1105
+ '[attr.tabindex]': 'tabIndex',
1106
+ '[attr.aria-label]': '_getAriaLabel()',
1107
+ '[attr.aria-labelledby]': '_getAriaLabelledby()',
1108
+ '[attr.aria-required]': 'required.toString()',
1109
+ '[attr.aria-disabled]': 'disabled.toString()',
1110
+ '[attr.aria-invalid]': 'errorState',
1111
+ '[attr.aria-owns]': 'panelOpen ? _optionIds : null',
1112
+ '[attr.aria-multiselectable]': 'multiple',
1113
+ '[attr.aria-describedby]': '_ariaDescribedby || null',
1114
+ '[attr.aria-activedescendant]': '_getAriaActiveDescendant()',
1115
+ '[class.mat-select-disabled]': 'disabled',
1116
+ '[class.mat-select-invalid]': 'errorState',
1117
+ '[class.mat-select-required]': 'required',
1118
+ '[class.mat-select-empty]': 'empty',
1119
+ 'class': 'mat-select',
1120
+ '(keydown)': '_handleKeydown($event)',
1121
+ '(focus)': '_onFocus()',
1122
+ '(blur)': '_onBlur()',
1123
+ },
1124
+ animations: [
1125
+ matSelectAnimations.transformPanelWrap,
1126
+ matSelectAnimations.transformPanel
1127
+ ],
1128
+ providers: [
1129
+ { provide: MatFormFieldControl, useExisting: MatSelect },
1130
+ { provide: MAT_OPTION_PARENT_COMPONENT, useExisting: MatSelect }
1131
+ ],
1132
+ styles: [".mat-select{display:inline-block;width:100%;outline:none}.mat-select-trigger{display:inline-table;cursor:pointer;position:relative;box-sizing:border-box}.mat-select-disabled .mat-select-trigger{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.mat-select-value{display:table-cell;max-width:0;width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mat-select-value-text{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.mat-select-arrow-wrapper{display:table-cell;vertical-align:middle}.mat-form-field-appearance-fill .mat-select-arrow-wrapper{transform:translateY(-50%)}.mat-form-field-appearance-outline .mat-select-arrow-wrapper{transform:translateY(-25%)}.mat-form-field-appearance-standard.mat-form-field-has-label .mat-select:not(.mat-select-empty) .mat-select-arrow-wrapper{transform:translateY(-50%)}.mat-form-field-appearance-standard .mat-select.mat-select-empty .mat-select-arrow-wrapper{transition:transform 400ms cubic-bezier(0.25, 0.8, 0.25, 1)}._mat-animation-noopable.mat-form-field-appearance-standard .mat-select.mat-select-empty .mat-select-arrow-wrapper{transition:none}.mat-select-arrow{width:0;height:0;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid;margin:0 4px}.mat-select-panel-wrap{flex-basis:100%}.mat-select-panel{min-width:112px;max-width:280px;overflow:auto;-webkit-overflow-scrolling:touch;padding-top:0;padding-bottom:0;max-height:256px;min-width:100%;border-radius:4px}.cdk-high-contrast-active .mat-select-panel{outline:solid 1px}.mat-select-panel .mat-optgroup-label,.mat-select-panel .mat-option{font-size:inherit;line-height:3em;height:3em}.mat-form-field-type-mat-select:not(.mat-form-field-disabled) .mat-form-field-flex{cursor:pointer}.mat-form-field-type-mat-select .mat-form-field-label{width:calc(100% - 18px)}.mat-select-placeholder{transition:color 400ms 133.3333333333ms cubic-bezier(0.25, 0.8, 0.25, 1)}._mat-animation-noopable .mat-select-placeholder{transition:none}.mat-form-field-hide-placeholder .mat-select-placeholder{color:transparent;-webkit-text-fill-color:transparent;transition:none;display:block}\n"]
1133
+ },] }
1134
+ ];
1135
+ MatSelect.ctorParameters = () => [
1136
+ { type: ViewportRuler },
1137
+ { type: ChangeDetectorRef },
1138
+ { type: NgZone },
1139
+ { type: ErrorStateMatcher },
1140
+ { type: ElementRef },
1141
+ { type: Directionality, decorators: [{ type: Optional }] },
1142
+ { type: NgForm, decorators: [{ type: Optional }] },
1143
+ { type: FormGroupDirective, decorators: [{ type: Optional }] },
1144
+ { type: MatFormField, decorators: [{ type: Optional }, { type: Inject, args: [MAT_FORM_FIELD,] }] },
1145
+ { type: NgControl, decorators: [{ type: Self }, { type: Optional }] },
1146
+ { type: String, decorators: [{ type: Attribute, args: ['tabindex',] }] },
1147
+ { type: undefined, decorators: [{ type: Inject, args: [MAT_SELECT_SCROLL_STRATEGY,] }] },
1148
+ { type: LiveAnnouncer },
1149
+ { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [MAT_SELECT_CONFIG,] }] }
1150
+ ];
1151
+ MatSelect.propDecorators = {
1152
+ trigger: [{ type: ViewChild, args: ['trigger',] }],
1153
+ panel: [{ type: ViewChild, args: ['panel',] }],
1154
+ overlayDir: [{ type: ViewChild, args: [CdkConnectedOverlay,] }],
1155
+ options: [{ type: ContentChildren, args: [MatOption, { descendants: true },] }],
1156
+ optionGroups: [{ type: ContentChildren, args: [MAT_OPTGROUP, { descendants: true },] }],
1157
+ panelClass: [{ type: Input }],
1158
+ customTrigger: [{ type: ContentChild, args: [MAT_SELECT_TRIGGER,] }],
1159
+ placeholder: [{ type: Input }],
1160
+ required: [{ type: Input }],
1161
+ multiple: [{ type: Input }],
1162
+ disableOptionCentering: [{ type: Input }],
1163
+ compareWith: [{ type: Input }],
1164
+ value: [{ type: Input }],
1165
+ ariaLabel: [{ type: Input, args: ['aria-label',] }],
1166
+ ariaLabelledby: [{ type: Input, args: ['aria-labelledby',] }],
1167
+ errorStateMatcher: [{ type: Input }],
1168
+ typeaheadDebounceInterval: [{ type: Input }],
1169
+ sortComparator: [{ type: Input }],
1170
+ id: [{ type: Input }],
1171
+ openedChange: [{ type: Output }],
1172
+ _openedStream: [{ type: Output, args: ['opened',] }],
1173
+ _closedStream: [{ type: Output, args: ['closed',] }],
1174
+ selectionChange: [{ type: Output }],
1175
+ valueChange: [{ type: Output }]
1176
+ };
1176
1177
 
1177
1178
  /**
1178
1179
  * @license
@@ -1181,31 +1182,28 @@ let MatSelect = /** @class */ (() => {
1181
1182
  * Use of this source code is governed by an MIT-style license that can be
1182
1183
  * found in the LICENSE file at https://angular.io/license
1183
1184
  */
1184
- let MatSelectModule = /** @class */ (() => {
1185
- class MatSelectModule {
1186
- }
1187
- MatSelectModule.decorators = [
1188
- { type: NgModule, args: [{
1189
- imports: [
1190
- CommonModule,
1191
- OverlayModule,
1192
- MatOptionModule,
1193
- MatCommonModule,
1194
- ],
1195
- exports: [
1196
- CdkScrollableModule,
1197
- MatFormFieldModule,
1198
- MatSelect,
1199
- MatSelectTrigger,
1200
- MatOptionModule,
1201
- MatCommonModule
1202
- ],
1203
- declarations: [MatSelect, MatSelectTrigger],
1204
- providers: [MAT_SELECT_SCROLL_STRATEGY_PROVIDER]
1205
- },] }
1206
- ];
1207
- return MatSelectModule;
1208
- })();
1185
+ class MatSelectModule {
1186
+ }
1187
+ MatSelectModule.decorators = [
1188
+ { type: NgModule, args: [{
1189
+ imports: [
1190
+ CommonModule,
1191
+ OverlayModule,
1192
+ MatOptionModule,
1193
+ MatCommonModule,
1194
+ ],
1195
+ exports: [
1196
+ CdkScrollableModule,
1197
+ MatFormFieldModule,
1198
+ MatSelect,
1199
+ MatSelectTrigger,
1200
+ MatOptionModule,
1201
+ MatCommonModule
1202
+ ],
1203
+ declarations: [MatSelect, MatSelectTrigger],
1204
+ providers: [MAT_SELECT_SCROLL_STRATEGY_PROVIDER]
1205
+ },] }
1206
+ ];
1209
1207
 
1210
1208
  /**
1211
1209
  * @license
@@ -1219,5 +1217,5 @@ let MatSelectModule = /** @class */ (() => {
1219
1217
  * Generated bundle index. Do not edit.
1220
1218
  */
1221
1219
 
1222
- export { MAT_SELECT_CONFIG, MAT_SELECT_SCROLL_STRATEGY, MAT_SELECT_SCROLL_STRATEGY_PROVIDER, MAT_SELECT_SCROLL_STRATEGY_PROVIDER_FACTORY, MatSelect, MatSelectChange, MatSelectModule, MatSelectTrigger, SELECT_ITEM_HEIGHT_EM, SELECT_MULTIPLE_PANEL_PADDING_X, SELECT_PANEL_INDENT_PADDING_X, SELECT_PANEL_MAX_HEIGHT, SELECT_PANEL_PADDING_X, SELECT_PANEL_VIEWPORT_PADDING, matSelectAnimations };
1220
+ export { MAT_SELECT_CONFIG, MAT_SELECT_SCROLL_STRATEGY, MAT_SELECT_SCROLL_STRATEGY_PROVIDER, MAT_SELECT_SCROLL_STRATEGY_PROVIDER_FACTORY, MAT_SELECT_TRIGGER, MatSelect, MatSelectChange, MatSelectModule, MatSelectTrigger, SELECT_ITEM_HEIGHT_EM, SELECT_MULTIPLE_PANEL_PADDING_X, SELECT_PANEL_INDENT_PADDING_X, SELECT_PANEL_MAX_HEIGHT, SELECT_PANEL_PADDING_X, SELECT_PANEL_VIEWPORT_PADDING, matSelectAnimations };
1223
1221
  //# sourceMappingURL=select.js.map