ngwr 15.0.1 → 15.2.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 (420) hide show
  1. package/anchor/styles/_index.scss +13 -2
  2. package/bits/aurora/styles/_rules.scss +11 -0
  3. package/bits/dot-grid/dot-grid.scss +1 -0
  4. package/bits/dot-grid/styles/_index.scss +8 -0
  5. package/bits/dot-grid/styles/_rules.scss +54 -0
  6. package/bits/iridescence/iridescence.scss +1 -0
  7. package/bits/iridescence/styles/_index.scss +8 -0
  8. package/bits/iridescence/styles/_rules.scss +72 -0
  9. package/bits/marquee/styles/_rules.scss +7 -3
  10. package/bits/ripple-grid/ripple-grid.scss +1 -0
  11. package/bits/ripple-grid/styles/_index.scss +8 -0
  12. package/bits/ripple-grid/styles/_rules.scss +63 -0
  13. package/bits/spotlight-card/styles/_rules.scss +7 -4
  14. package/bits/star-border/styles/_rules.scss +5 -2
  15. package/bits/threads/styles/_index.scss +8 -0
  16. package/bits/threads/styles/_rules.scss +76 -0
  17. package/bits/threads/threads.scss +1 -0
  18. package/bits/waves/styles/_rules.scss +8 -1
  19. package/charts/bar-chart/styles/_index.scss +607 -7
  20. package/charts/boxplot-chart/styles/_index.scss +297 -0
  21. package/charts/bullet-chart/styles/_index.scss +268 -0
  22. package/charts/calendar-heatmap/styles/_index.scss +25 -3
  23. package/charts/candlestick-chart/styles/_index.scss +307 -0
  24. package/charts/chart-legend/styles/_index.scss +143 -0
  25. package/charts/core/styles/_index.scss +30 -0
  26. package/charts/donut-chart/styles/_index.scss +39 -3
  27. package/charts/funnel-chart/styles/_index.scss +237 -0
  28. package/charts/gauge/styles/_index.scss +4 -2
  29. package/charts/heatmap-chart/styles/_index.scss +329 -0
  30. package/charts/line-chart/styles/_index.scss +141 -8
  31. package/charts/meter-group/styles/_index.scss +44 -13
  32. package/charts/radar-chart/styles/_index.scss +206 -0
  33. package/charts/scatter-chart/styles/_index.scss +245 -0
  34. package/charts/sparkline/styles/_index.scss +10 -1
  35. package/charts/treemap-chart/styles/_index.scss +215 -0
  36. package/charts/waterfall-chart/styles/_index.scss +321 -0
  37. package/collapse/styles/_index.scss +6 -1
  38. package/fesm2022/ngwr-affix.mjs +3 -3
  39. package/fesm2022/ngwr-alert.mjs +3 -3
  40. package/fesm2022/ngwr-anchor.mjs +3 -3
  41. package/fesm2022/ngwr-avatar.mjs +3 -3
  42. package/fesm2022/ngwr-back-top.mjs +3 -3
  43. package/fesm2022/ngwr-badge.mjs +6 -6
  44. package/fesm2022/ngwr-bits-aurora.mjs +30 -38
  45. package/fesm2022/ngwr-bits-aurora.mjs.map +1 -1
  46. package/fesm2022/ngwr-bits-blur-text.mjs +3 -3
  47. package/fesm2022/ngwr-bits-border-glow-testing.mjs.map +1 -1
  48. package/fesm2022/ngwr-bits-border-glow.mjs +11 -5
  49. package/fesm2022/ngwr-bits-border-glow.mjs.map +1 -1
  50. package/fesm2022/ngwr-bits-circular-text.mjs +3 -3
  51. package/fesm2022/ngwr-bits-click-spark.mjs +3 -3
  52. package/fesm2022/ngwr-bits-confetti.mjs +3 -3
  53. package/fesm2022/ngwr-bits-decrypt-text.mjs +3 -3
  54. package/fesm2022/ngwr-bits-dot-grid-testing.mjs +40 -0
  55. package/fesm2022/ngwr-bits-dot-grid-testing.mjs.map +1 -0
  56. package/fesm2022/ngwr-bits-dot-grid.mjs +482 -0
  57. package/fesm2022/ngwr-bits-dot-grid.mjs.map +1 -0
  58. package/fesm2022/ngwr-bits-falling-text.mjs +3 -3
  59. package/fesm2022/ngwr-bits-fuzzy-text.mjs +3 -3
  60. package/fesm2022/ngwr-bits-glitch-text.mjs +3 -3
  61. package/fesm2022/ngwr-bits-gradient-text.mjs +3 -3
  62. package/fesm2022/ngwr-bits-iridescence-testing.mjs +31 -0
  63. package/fesm2022/ngwr-bits-iridescence-testing.mjs.map +1 -0
  64. package/fesm2022/ngwr-bits-iridescence.mjs +591 -0
  65. package/fesm2022/ngwr-bits-iridescence.mjs.map +1 -0
  66. package/fesm2022/ngwr-bits-marquee-testing.mjs +1 -1
  67. package/fesm2022/ngwr-bits-marquee-testing.mjs.map +1 -1
  68. package/fesm2022/ngwr-bits-marquee.mjs +7 -7
  69. package/fesm2022/ngwr-bits-marquee.mjs.map +1 -1
  70. package/fesm2022/ngwr-bits-ripple-grid-testing.mjs +40 -0
  71. package/fesm2022/ngwr-bits-ripple-grid-testing.mjs.map +1 -0
  72. package/fesm2022/ngwr-bits-ripple-grid.mjs +805 -0
  73. package/fesm2022/ngwr-bits-ripple-grid.mjs.map +1 -0
  74. package/fesm2022/ngwr-bits-rotating-text.mjs +3 -3
  75. package/fesm2022/ngwr-bits-shiny-text.mjs +6 -6
  76. package/fesm2022/ngwr-bits-splash-cursor.mjs +3 -3
  77. package/fesm2022/ngwr-bits-split-text.mjs +3 -3
  78. package/fesm2022/ngwr-bits-spotlight-card-testing.mjs +1 -1
  79. package/fesm2022/ngwr-bits-spotlight-card-testing.mjs.map +1 -1
  80. package/fesm2022/ngwr-bits-spotlight-card.mjs +10 -10
  81. package/fesm2022/ngwr-bits-spotlight-card.mjs.map +1 -1
  82. package/fesm2022/ngwr-bits-star-border-testing.mjs +1 -1
  83. package/fesm2022/ngwr-bits-star-border-testing.mjs.map +1 -1
  84. package/fesm2022/ngwr-bits-star-border.mjs +7 -7
  85. package/fesm2022/ngwr-bits-star-border.mjs.map +1 -1
  86. package/fesm2022/ngwr-bits-threads-testing.mjs +41 -0
  87. package/fesm2022/ngwr-bits-threads-testing.mjs.map +1 -0
  88. package/fesm2022/ngwr-bits-threads.mjs +482 -0
  89. package/fesm2022/ngwr-bits-threads.mjs.map +1 -0
  90. package/fesm2022/ngwr-bits-tilt-card.mjs +6 -6
  91. package/fesm2022/ngwr-bits-typewriter.mjs +3 -3
  92. package/fesm2022/ngwr-bits-waves-testing.mjs.map +1 -1
  93. package/fesm2022/ngwr-bits-waves.mjs +60 -41
  94. package/fesm2022/ngwr-bits-waves.mjs.map +1 -1
  95. package/fesm2022/ngwr-breadcrumbs.mjs +8 -8
  96. package/fesm2022/ngwr-breadcrumbs.mjs.map +1 -1
  97. package/fesm2022/ngwr-burger.mjs +3 -3
  98. package/fesm2022/ngwr-button.mjs +6 -6
  99. package/fesm2022/ngwr-calendar.mjs +3 -3
  100. package/fesm2022/ngwr-card.mjs +9 -9
  101. package/fesm2022/ngwr-carousel.mjs +6 -6
  102. package/fesm2022/ngwr-cascader.mjs +5 -5
  103. package/fesm2022/ngwr-cascader.mjs.map +1 -1
  104. package/fesm2022/ngwr-charts-bar-chart-testing.mjs +127 -3
  105. package/fesm2022/ngwr-charts-bar-chart-testing.mjs.map +1 -1
  106. package/fesm2022/ngwr-charts-bar-chart.mjs +683 -49
  107. package/fesm2022/ngwr-charts-bar-chart.mjs.map +1 -1
  108. package/fesm2022/ngwr-charts-boxplot-chart-testing.mjs +161 -0
  109. package/fesm2022/ngwr-charts-boxplot-chart-testing.mjs.map +1 -0
  110. package/fesm2022/ngwr-charts-boxplot-chart.mjs +517 -0
  111. package/fesm2022/ngwr-charts-boxplot-chart.mjs.map +1 -0
  112. package/fesm2022/ngwr-charts-bullet-chart-testing.mjs +68 -0
  113. package/fesm2022/ngwr-charts-bullet-chart-testing.mjs.map +1 -0
  114. package/fesm2022/ngwr-charts-bullet-chart.mjs +459 -0
  115. package/fesm2022/ngwr-charts-bullet-chart.mjs.map +1 -0
  116. package/fesm2022/ngwr-charts-calendar-heatmap-testing.mjs +27 -0
  117. package/fesm2022/ngwr-charts-calendar-heatmap-testing.mjs.map +1 -1
  118. package/fesm2022/ngwr-charts-calendar-heatmap.mjs +96 -17
  119. package/fesm2022/ngwr-charts-calendar-heatmap.mjs.map +1 -1
  120. package/fesm2022/ngwr-charts-candlestick-chart-testing.mjs +107 -0
  121. package/fesm2022/ngwr-charts-candlestick-chart-testing.mjs.map +1 -0
  122. package/fesm2022/ngwr-charts-candlestick-chart.mjs +508 -0
  123. package/fesm2022/ngwr-charts-candlestick-chart.mjs.map +1 -0
  124. package/fesm2022/ngwr-charts-chart-legend-testing.mjs +126 -0
  125. package/fesm2022/ngwr-charts-chart-legend-testing.mjs.map +1 -0
  126. package/fesm2022/ngwr-charts-chart-legend.mjs +251 -0
  127. package/fesm2022/ngwr-charts-chart-legend.mjs.map +1 -0
  128. package/fesm2022/ngwr-charts-core.mjs +383 -0
  129. package/fesm2022/ngwr-charts-core.mjs.map +1 -0
  130. package/fesm2022/ngwr-charts-donut-chart-testing.mjs +10 -1
  131. package/fesm2022/ngwr-charts-donut-chart-testing.mjs.map +1 -1
  132. package/fesm2022/ngwr-charts-donut-chart.mjs +187 -31
  133. package/fesm2022/ngwr-charts-donut-chart.mjs.map +1 -1
  134. package/fesm2022/ngwr-charts-funnel-chart-testing.mjs +100 -0
  135. package/fesm2022/ngwr-charts-funnel-chart-testing.mjs.map +1 -0
  136. package/fesm2022/ngwr-charts-funnel-chart.mjs +415 -0
  137. package/fesm2022/ngwr-charts-funnel-chart.mjs.map +1 -0
  138. package/fesm2022/ngwr-charts-gauge-testing.mjs +9 -3
  139. package/fesm2022/ngwr-charts-gauge-testing.mjs.map +1 -1
  140. package/fesm2022/ngwr-charts-gauge.mjs +9 -15
  141. package/fesm2022/ngwr-charts-gauge.mjs.map +1 -1
  142. package/fesm2022/ngwr-charts-heatmap-chart-testing.mjs +120 -0
  143. package/fesm2022/ngwr-charts-heatmap-chart-testing.mjs.map +1 -0
  144. package/fesm2022/ngwr-charts-heatmap-chart.mjs +519 -0
  145. package/fesm2022/ngwr-charts-heatmap-chart.mjs.map +1 -0
  146. package/fesm2022/ngwr-charts-line-chart-testing.mjs +43 -1
  147. package/fesm2022/ngwr-charts-line-chart-testing.mjs.map +1 -1
  148. package/fesm2022/ngwr-charts-line-chart.mjs +436 -122
  149. package/fesm2022/ngwr-charts-line-chart.mjs.map +1 -1
  150. package/fesm2022/ngwr-charts-meter-group-testing.mjs +14 -2
  151. package/fesm2022/ngwr-charts-meter-group-testing.mjs.map +1 -1
  152. package/fesm2022/ngwr-charts-meter-group.mjs +101 -32
  153. package/fesm2022/ngwr-charts-meter-group.mjs.map +1 -1
  154. package/fesm2022/ngwr-charts-radar-chart-testing.mjs +98 -0
  155. package/fesm2022/ngwr-charts-radar-chart-testing.mjs.map +1 -0
  156. package/fesm2022/ngwr-charts-radar-chart.mjs +720 -0
  157. package/fesm2022/ngwr-charts-radar-chart.mjs.map +1 -0
  158. package/fesm2022/ngwr-charts-scatter-chart-testing.mjs +132 -0
  159. package/fesm2022/ngwr-charts-scatter-chart-testing.mjs.map +1 -0
  160. package/fesm2022/ngwr-charts-scatter-chart.mjs +545 -0
  161. package/fesm2022/ngwr-charts-scatter-chart.mjs.map +1 -0
  162. package/fesm2022/ngwr-charts-sparkline-testing.mjs.map +1 -1
  163. package/fesm2022/ngwr-charts-sparkline.mjs +14 -16
  164. package/fesm2022/ngwr-charts-sparkline.mjs.map +1 -1
  165. package/fesm2022/ngwr-charts-treemap-chart-testing.mjs +103 -0
  166. package/fesm2022/ngwr-charts-treemap-chart-testing.mjs.map +1 -0
  167. package/fesm2022/ngwr-charts-treemap-chart.mjs +535 -0
  168. package/fesm2022/ngwr-charts-treemap-chart.mjs.map +1 -0
  169. package/fesm2022/ngwr-charts-waterfall-chart-testing.mjs +131 -0
  170. package/fesm2022/ngwr-charts-waterfall-chart-testing.mjs.map +1 -0
  171. package/fesm2022/ngwr-charts-waterfall-chart.mjs +462 -0
  172. package/fesm2022/ngwr-charts-waterfall-chart.mjs.map +1 -0
  173. package/fesm2022/ngwr-checkbox.mjs +6 -6
  174. package/fesm2022/ngwr-clipboard.mjs +3 -3
  175. package/fesm2022/ngwr-collapse.mjs +9 -9
  176. package/fesm2022/ngwr-color-picker.mjs +6 -6
  177. package/fesm2022/ngwr-command-palette.mjs +3 -3
  178. package/fesm2022/ngwr-compare.mjs +3 -3
  179. package/fesm2022/ngwr-context-menu.mjs +12 -12
  180. package/fesm2022/ngwr-cookie.mjs +3 -3
  181. package/fesm2022/ngwr-counter.mjs +6 -6
  182. package/fesm2022/ngwr-date-picker.mjs +21 -21
  183. package/fesm2022/ngwr-date.mjs +6 -6
  184. package/fesm2022/ngwr-density.mjs +6 -6
  185. package/fesm2022/ngwr-descriptions.mjs +6 -6
  186. package/fesm2022/ngwr-dialog.mjs +15 -15
  187. package/fesm2022/ngwr-directives.mjs +12 -12
  188. package/fesm2022/ngwr-divider.mjs +3 -3
  189. package/fesm2022/ngwr-drawer.mjs +18 -18
  190. package/fesm2022/ngwr-dropdown.mjs +9 -9
  191. package/fesm2022/ngwr-editor.mjs +3 -3
  192. package/fesm2022/ngwr-empty.mjs +3 -3
  193. package/fesm2022/ngwr-event-calendar.mjs +6 -6
  194. package/fesm2022/ngwr-file-upload.mjs +3 -3
  195. package/fesm2022/ngwr-form.mjs +9 -9
  196. package/fesm2022/ngwr-graph.mjs +6 -6
  197. package/fesm2022/ngwr-hotkey.mjs +6 -6
  198. package/fesm2022/ngwr-i18n-ar.mjs +90 -4
  199. package/fesm2022/ngwr-i18n-ar.mjs.map +1 -1
  200. package/fesm2022/ngwr-i18n-cs.mjs +90 -4
  201. package/fesm2022/ngwr-i18n-cs.mjs.map +1 -1
  202. package/fesm2022/ngwr-i18n-de.mjs +90 -4
  203. package/fesm2022/ngwr-i18n-de.mjs.map +1 -1
  204. package/fesm2022/ngwr-i18n-en.mjs +90 -4
  205. package/fesm2022/ngwr-i18n-en.mjs.map +1 -1
  206. package/fesm2022/ngwr-i18n-es.mjs +90 -4
  207. package/fesm2022/ngwr-i18n-es.mjs.map +1 -1
  208. package/fesm2022/ngwr-i18n-fr.mjs +90 -4
  209. package/fesm2022/ngwr-i18n-fr.mjs.map +1 -1
  210. package/fesm2022/ngwr-i18n-he.mjs +90 -4
  211. package/fesm2022/ngwr-i18n-he.mjs.map +1 -1
  212. package/fesm2022/ngwr-i18n-hi.mjs +90 -4
  213. package/fesm2022/ngwr-i18n-hi.mjs.map +1 -1
  214. package/fesm2022/ngwr-i18n-id.mjs +90 -4
  215. package/fesm2022/ngwr-i18n-id.mjs.map +1 -1
  216. package/fesm2022/ngwr-i18n-it.mjs +90 -4
  217. package/fesm2022/ngwr-i18n-it.mjs.map +1 -1
  218. package/fesm2022/ngwr-i18n-ja.mjs +90 -4
  219. package/fesm2022/ngwr-i18n-ja.mjs.map +1 -1
  220. package/fesm2022/ngwr-i18n-ko.mjs +90 -4
  221. package/fesm2022/ngwr-i18n-ko.mjs.map +1 -1
  222. package/fesm2022/ngwr-i18n-nl.mjs +90 -4
  223. package/fesm2022/ngwr-i18n-nl.mjs.map +1 -1
  224. package/fesm2022/ngwr-i18n-pl.mjs +90 -4
  225. package/fesm2022/ngwr-i18n-pl.mjs.map +1 -1
  226. package/fesm2022/ngwr-i18n-pt.mjs +90 -4
  227. package/fesm2022/ngwr-i18n-pt.mjs.map +1 -1
  228. package/fesm2022/ngwr-i18n-ru.mjs +90 -4
  229. package/fesm2022/ngwr-i18n-ru.mjs.map +1 -1
  230. package/fesm2022/ngwr-i18n-sv.mjs +90 -4
  231. package/fesm2022/ngwr-i18n-sv.mjs.map +1 -1
  232. package/fesm2022/ngwr-i18n-tr.mjs +90 -4
  233. package/fesm2022/ngwr-i18n-tr.mjs.map +1 -1
  234. package/fesm2022/ngwr-i18n-uk.mjs +90 -4
  235. package/fesm2022/ngwr-i18n-uk.mjs.map +1 -1
  236. package/fesm2022/ngwr-i18n-vi.mjs +90 -4
  237. package/fesm2022/ngwr-i18n-vi.mjs.map +1 -1
  238. package/fesm2022/ngwr-i18n-zh-tw.mjs +90 -4
  239. package/fesm2022/ngwr-i18n-zh-tw.mjs.map +1 -1
  240. package/fesm2022/ngwr-i18n-zh.mjs +90 -4
  241. package/fesm2022/ngwr-i18n-zh.mjs.map +1 -1
  242. package/fesm2022/ngwr-i18n.mjs +9 -9
  243. package/fesm2022/ngwr-icon.mjs +6 -6
  244. package/fesm2022/ngwr-image-cropper.mjs +3 -3
  245. package/fesm2022/ngwr-input-number.mjs +3 -3
  246. package/fesm2022/ngwr-input-otp.mjs +3 -3
  247. package/fesm2022/ngwr-input.mjs +15 -15
  248. package/fesm2022/ngwr-keyboard.mjs +3 -3
  249. package/fesm2022/ngwr-knob.mjs +3 -3
  250. package/fesm2022/ngwr-layout.mjs +15 -15
  251. package/fesm2022/ngwr-lightbox.mjs +5 -5
  252. package/fesm2022/ngwr-lightbox.mjs.map +1 -1
  253. package/fesm2022/ngwr-list.mjs +8 -8
  254. package/fesm2022/ngwr-list.mjs.map +1 -1
  255. package/fesm2022/ngwr-loading-bar.mjs +6 -6
  256. package/fesm2022/ngwr-mention.mjs +6 -6
  257. package/fesm2022/ngwr-meta.mjs +6 -6
  258. package/fesm2022/ngwr-overlay.mjs +6 -6
  259. package/fesm2022/ngwr-page-header.mjs +3 -3
  260. package/fesm2022/ngwr-pagination.mjs +3 -3
  261. package/fesm2022/ngwr-pipes.mjs +21 -21
  262. package/fesm2022/ngwr-platform.mjs +6 -6
  263. package/fesm2022/ngwr-popconfirm.mjs +6 -6
  264. package/fesm2022/ngwr-popover.mjs +43 -20
  265. package/fesm2022/ngwr-popover.mjs.map +1 -1
  266. package/fesm2022/ngwr-progress.mjs +3 -3
  267. package/fesm2022/ngwr-pull-to-refresh.mjs +3 -3
  268. package/fesm2022/ngwr-qr.mjs +4 -3
  269. package/fesm2022/ngwr-qr.mjs.map +1 -1
  270. package/fesm2022/ngwr-radio.mjs +6 -6
  271. package/fesm2022/ngwr-rating.mjs +3 -3
  272. package/fesm2022/ngwr-result.mjs +12 -12
  273. package/fesm2022/ngwr-schema-form.mjs +3 -3
  274. package/fesm2022/ngwr-scroll.mjs +3 -3
  275. package/fesm2022/ngwr-segmented.mjs +3 -3
  276. package/fesm2022/ngwr-select.mjs +12 -12
  277. package/fesm2022/ngwr-sidebar.mjs +3 -3
  278. package/fesm2022/ngwr-skeleton.mjs +3 -3
  279. package/fesm2022/ngwr-slider.mjs +3 -3
  280. package/fesm2022/ngwr-sortable-list.mjs +11 -11
  281. package/fesm2022/ngwr-sortable-list.mjs.map +1 -1
  282. package/fesm2022/ngwr-speed-dial.mjs +3 -3
  283. package/fesm2022/ngwr-spinner.mjs +3 -3
  284. package/fesm2022/ngwr-splitter.mjs +3 -3
  285. package/fesm2022/ngwr-statistic.mjs +9 -9
  286. package/fesm2022/ngwr-stepper.mjs +6 -6
  287. package/fesm2022/ngwr-storage.mjs +3 -3
  288. package/fesm2022/ngwr-switch.mjs +3 -3
  289. package/fesm2022/ngwr-table.mjs +18 -18
  290. package/fesm2022/ngwr-tabs-router.mjs +3 -3
  291. package/fesm2022/ngwr-tabs.mjs +6 -6
  292. package/fesm2022/ngwr-textarea.mjs +3 -3
  293. package/fesm2022/ngwr-theme.mjs +3 -3
  294. package/fesm2022/ngwr-timeline.mjs +6 -6
  295. package/fesm2022/ngwr-toast-testing.mjs +2 -1
  296. package/fesm2022/ngwr-toast-testing.mjs.map +1 -1
  297. package/fesm2022/ngwr-toast.mjs +34 -15
  298. package/fesm2022/ngwr-toast.mjs.map +1 -1
  299. package/fesm2022/ngwr-toolbar.mjs +3 -3
  300. package/fesm2022/ngwr-tour.mjs +6 -6
  301. package/fesm2022/ngwr-transfer.mjs +3 -3
  302. package/fesm2022/ngwr-tree.mjs +3 -3
  303. package/fesm2022/ngwr-typography.mjs +3 -3
  304. package/fesm2022/ngwr-utils.mjs +31 -1
  305. package/fesm2022/ngwr-utils.mjs.map +1 -1
  306. package/fesm2022/ngwr-version.mjs +1 -1
  307. package/fesm2022/ngwr-version.mjs.map +1 -1
  308. package/fesm2022/ngwr-virtual-scroll.mjs +3 -3
  309. package/fesm2022/ngwr-window-testing.mjs +61 -5
  310. package/fesm2022/ngwr-window-testing.mjs.map +1 -1
  311. package/fesm2022/ngwr-window.mjs +1538 -895
  312. package/fesm2022/ngwr-window.mjs.map +1 -1
  313. package/fesm2022/ngwr.mjs.map +1 -1
  314. package/i18n/ar.json +84 -4
  315. package/i18n/cs.json +84 -4
  316. package/i18n/de.json +84 -4
  317. package/i18n/en.json +84 -4
  318. package/i18n/es.json +84 -4
  319. package/i18n/fr.json +84 -4
  320. package/i18n/he.json +84 -4
  321. package/i18n/hi.json +84 -4
  322. package/i18n/id.json +84 -4
  323. package/i18n/it.json +84 -4
  324. package/i18n/ja.json +84 -4
  325. package/i18n/ko.json +84 -4
  326. package/i18n/nl.json +84 -4
  327. package/i18n/pl.json +84 -4
  328. package/i18n/pt.json +84 -4
  329. package/i18n/ru.json +84 -4
  330. package/i18n/sv.json +84 -4
  331. package/i18n/tr.json +84 -4
  332. package/i18n/uk.json +84 -4
  333. package/i18n/vi.json +84 -4
  334. package/i18n/zh-TW.json +84 -4
  335. package/i18n/zh.json +84 -4
  336. package/llms-full.txt +266 -9
  337. package/llms.txt +30 -7
  338. package/mcp/api.js +69 -1
  339. package/mcp/catalog.js +10 -1
  340. package/mcp/server.js +15 -1
  341. package/mcp/tools.js +122 -26
  342. package/package.json +132 -1
  343. package/popover/styles/_index.scss +75 -7
  344. package/schematics/component-style/index.js +3 -0
  345. package/schematics/migrations/v15/index.js +3 -26
  346. package/schematics/ng-add/index.js +2 -2
  347. package/schematics/use/symbol-map.json +14 -0
  348. package/schematics/utility/global-styles.js +19 -4
  349. package/schematics/utility/v15-moves.js +55 -0
  350. package/skills/ngwr/SKILL.md +8 -2
  351. package/skills/ngwr/references/catalog.md +28 -0
  352. package/skills/ngwr/references/setup.md +5 -4
  353. package/theme/styles/_focus.scss +8 -0
  354. package/toast/styles/_index.scss +103 -12
  355. package/types/ngwr-bits-aurora.d.ts +11 -0
  356. package/types/ngwr-bits-border-glow-testing.d.ts +5 -6
  357. package/types/ngwr-bits-border-glow.d.ts +5 -1
  358. package/types/ngwr-bits-dot-grid-testing.d.ts +92 -0
  359. package/types/ngwr-bits-dot-grid.d.ts +158 -0
  360. package/types/ngwr-bits-iridescence-testing.d.ts +85 -0
  361. package/types/ngwr-bits-iridescence.d.ts +244 -0
  362. package/types/ngwr-bits-ripple-grid-testing.d.ts +96 -0
  363. package/types/ngwr-bits-ripple-grid.d.ts +294 -0
  364. package/types/ngwr-bits-splash-cursor.d.ts +0 -1
  365. package/types/ngwr-bits-spotlight-card-testing.d.ts +3 -1
  366. package/types/ngwr-bits-star-border-testing.d.ts +7 -7
  367. package/types/ngwr-bits-threads-testing.d.ts +97 -0
  368. package/types/ngwr-bits-threads.d.ts +216 -0
  369. package/types/ngwr-bits-waves-testing.d.ts +1 -1
  370. package/types/ngwr-bits-waves.d.ts +20 -3
  371. package/types/ngwr-charts-bar-chart-testing.d.ts +193 -14
  372. package/types/ngwr-charts-bar-chart.d.ts +546 -38
  373. package/types/ngwr-charts-boxplot-chart-testing.d.ts +165 -0
  374. package/types/ngwr-charts-boxplot-chart.d.ts +308 -0
  375. package/types/ngwr-charts-bullet-chart-testing.d.ts +99 -0
  376. package/types/ngwr-charts-bullet-chart.d.ts +262 -0
  377. package/types/ngwr-charts-calendar-heatmap-testing.d.ts +32 -1
  378. package/types/ngwr-charts-calendar-heatmap.d.ts +55 -6
  379. package/types/ngwr-charts-candlestick-chart-testing.d.ts +135 -0
  380. package/types/ngwr-charts-candlestick-chart.d.ts +361 -0
  381. package/types/ngwr-charts-chart-legend-testing.d.ts +84 -0
  382. package/types/ngwr-charts-chart-legend.d.ts +178 -0
  383. package/types/ngwr-charts-core.d.ts +391 -0
  384. package/types/ngwr-charts-donut-chart-testing.d.ts +28 -5
  385. package/types/ngwr-charts-donut-chart.d.ts +124 -12
  386. package/types/ngwr-charts-funnel-chart-testing.d.ts +130 -0
  387. package/types/ngwr-charts-funnel-chart.d.ts +296 -0
  388. package/types/ngwr-charts-gauge-testing.d.ts +5 -0
  389. package/types/ngwr-charts-gauge.d.ts +6 -4
  390. package/types/ngwr-charts-heatmap-chart-testing.d.ts +148 -0
  391. package/types/ngwr-charts-heatmap-chart.d.ts +359 -0
  392. package/types/ngwr-charts-line-chart-testing.d.ts +72 -11
  393. package/types/ngwr-charts-line-chart.d.ts +291 -67
  394. package/types/ngwr-charts-meter-group-testing.d.ts +16 -2
  395. package/types/ngwr-charts-meter-group.d.ts +71 -11
  396. package/types/ngwr-charts-radar-chart-testing.d.ts +162 -0
  397. package/types/ngwr-charts-radar-chart.d.ts +461 -0
  398. package/types/ngwr-charts-scatter-chart-testing.d.ts +147 -0
  399. package/types/ngwr-charts-scatter-chart.d.ts +396 -0
  400. package/types/ngwr-charts-sparkline-testing.d.ts +4 -1
  401. package/types/ngwr-charts-sparkline.d.ts +8 -6
  402. package/types/ngwr-charts-treemap-chart-testing.d.ts +131 -0
  403. package/types/ngwr-charts-treemap-chart.d.ts +295 -0
  404. package/types/ngwr-charts-waterfall-chart-testing.d.ts +149 -0
  405. package/types/ngwr-charts-waterfall-chart.d.ts +338 -0
  406. package/types/ngwr-color-picker.d.ts +0 -21
  407. package/types/ngwr-dropdown.d.ts +0 -52
  408. package/types/ngwr-editor.d.ts +2 -84
  409. package/types/ngwr-i18n.d.ts +0 -15
  410. package/types/ngwr-mention.d.ts +0 -13
  411. package/types/ngwr-popconfirm.d.ts +0 -33
  412. package/types/ngwr-popover.d.ts +29 -53
  413. package/types/ngwr-toast-testing.d.ts +6 -1
  414. package/types/ngwr-tour.d.ts +0 -2
  415. package/types/ngwr-utils.d.ts +49 -7
  416. package/types/ngwr-version.d.ts +1 -1
  417. package/types/ngwr-window-testing.d.ts +65 -7
  418. package/types/ngwr-window.d.ts +831 -207
  419. package/types/ngwr.d.ts +1 -0
  420. package/window/styles/_index.scss +528 -82
@@ -1,15 +1,16 @@
1
1
  import * as i0 from "@angular/core";
2
- import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, EnvironmentInjector, InjectionToken, Injector, PLATFORM_ID, Service, ViewContainerRef, ViewEncapsulation, afterNextRender, computed, effect, inject, input, model, output, runInInjectionContext, signal, viewChild, viewChildren } from "@angular/core";
3
- import { coerceBooleanProperty, coerceNumberProperty } from "@angular/cdk/coercion";
4
- import { isPlatformBrowser } from "@angular/common";
2
+ import { ChangeDetectionStrategy, Component, DestroyRef, ElementRef, EnvironmentInjector, InjectionToken, Injector, PLATFORM_ID, Service, TemplateRef, ViewContainerRef, ViewEncapsulation, afterNextRender, afterRenderEffect, computed, effect, inject, input, output, runInInjectionContext, signal, untracked, viewChild, viewChildren } from "@angular/core";
3
+ import { coerceNumberProperty } from "@angular/cdk/coercion";
4
+ import { NgComponentOutlet, NgTemplateOutlet, isPlatformBrowser } from "@angular/common";
5
5
  import { useI18nFormatter, useI18nText } from "ngwr/i18n";
6
- import { clamp, randomId, toClassList } from "ngwr/utils";
6
+ import { clamp, randomId, toClassList, wrFeature } from "ngwr/utils";
7
7
  import { Directionality } from "@angular/cdk/bidi";
8
8
  import { ComponentPortal } from "@angular/cdk/portal";
9
9
  import { WR_OVERLAY, wrFollowDirection } from "ngwr/overlay";
10
10
  import { FocusTrapFactory } from "@angular/cdk/a11y";
11
11
  import { ReplaySubject, Subject } from "rxjs";
12
12
  import { WrStorage } from "ngwr/storage";
13
+ import { WrDropdown, WrDropdownItem, WrDropdownMenu } from "ngwr/dropdown";
13
14
  /**
14
15
  * @license
15
16
  *
@@ -18,6 +19,7 @@ import { WrStorage } from "ngwr/storage";
18
19
  */
19
20
  const WR_WINDOW_REF = new InjectionToken("WR_WINDOW_REF");
20
21
  const WR_WINDOW_DATA = new InjectionToken("WR_WINDOW_DATA");
22
+ const WR_WINDOW_DEFAULTS = new InjectionToken("WR_WINDOW_DEFAULTS");
21
23
  function storageKey(cfg) {
22
24
  return `wr:window:${cfg.prefix ? `${cfg.prefix}:` : ""}${cfg.key}`;
23
25
  }
@@ -40,215 +42,92 @@ function detectOs() {
40
42
  return "windows";
41
43
  }
42
44
  const DETECTED_OS = detectOs();
43
- function optionalBoolean(value) {
44
- return value === null || value === void 0 ? null : coerceBooleanProperty(value);
45
+ const SNAP_HOT_ZONE = 12;
46
+ const DRAG_THRESHOLD = 4;
47
+ const INTERACTIVE = "button, input, select, textarea, a[href], label, [contenteditable]:not([contenteditable=\"false\"]), [role=\"button\"], [role=\"link\"], [role=\"checkbox\"], [role=\"switch\"], [role=\"radio\"], [role=\"slider\"], [role=\"spinbutton\"], [role=\"textbox\"], [role=\"combobox\"], [role=\"tab\"], [role=\"menuitem\"], [role=\"option\"]";
48
+ function asTemplate(content) {
49
+ return content instanceof TemplateRef ? content : null;
45
50
  }
46
- const SIZE_PRESETS = {
47
- sm: {
48
- width: 320,
49
- height: 200
50
- },
51
- md: {
52
- width: 480,
53
- height: 320
54
- },
55
- lg: {
56
- width: 720,
57
- height: 480
58
- }
59
- };
60
- const MIN_ON_VIEWPORT = 24;
61
- function viewportWidth() {
62
- return typeof window === "undefined" ? 1024 : window.innerWidth;
63
- }
64
- function viewportHeight() {
65
- return typeof window === "undefined" ? 768 : window.innerHeight;
51
+ function asComponent(content) {
52
+ return content && !(content instanceof TemplateRef) ? content : null;
66
53
  }
67
54
  var WrWindow = class WrWindow {
68
- open = model(true, ...ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []);
69
- state = model("normal", ...ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []);
70
- title = input("", ...ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []);
55
+ model = input.required(...ngDevMode ? [{ debugName: "model" }] : /* istanbul ignore next */ []);
56
+ windowRef = input(null, ...ngDevMode ? [{ debugName: "windowRef" }] : /* istanbul ignore next */ []);
57
+ injector = input(void 0, ...ngDevMode ? [{ debugName: "injector" }] : /* istanbul ignore next */ []);
58
+ titleExtra = input(null, ...ngDevMode ? [{ debugName: "titleExtra" }] : /* istanbul ignore next */ []);
59
+ statusBar = input(null, ...ngDevMode ? [{ debugName: "statusBar" }] : /* istanbul ignore next */ []);
60
+ closed = output();
71
61
  titleId = randomId("wr-window-title");
72
62
  closeLabel = useI18nText(NO_OVERRIDE$1, "window.close", "Close");
73
63
  minimizeLabel = useI18nText(NO_OVERRIDE$1, "window.minimize", "Minimize");
74
64
  maximizeLabel = useI18nText(NO_OVERRIDE$1, "window.maximize", "Maximize");
75
65
  restoreLabel = useI18nText(NO_OVERRIDE$1, "window.restore", "Restore");
76
66
  restoreDownLabel = useI18nText(NO_OVERRIDE$1, "window.restoreDown", "Restore down");
77
- minimizeActionLabel = computed(() => this.state() === "minimized" ? this.restoreLabel() : this.minimizeLabel(), ...ngDevMode ? [{ debugName: "minimizeActionLabel" }] : /* istanbul ignore next */ []);
78
- maximizeActionLabel = computed(() => this.state() === "maximized" ? this.restoreDownLabel() : this.maximizeLabel(), ...ngDevMode ? [{ debugName: "maximizeActionLabel" }] : /* istanbul ignore next */ []);
79
- initialX = input(null, {
80
- ...ngDevMode ? { debugName: "initialX" } : /* istanbul ignore next */ {},
81
- transform: (v) => v == null ? null : coerceNumberProperty(v, 0)
82
- });
83
- initialY = input(null, {
84
- ...ngDevMode ? { debugName: "initialY" } : /* istanbul ignore next */ {},
85
- transform: (v) => v == null ? null : coerceNumberProperty(v, 0)
86
- });
87
- size = input("md", ...ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []);
88
- initialWidth = input(null, {
89
- ...ngDevMode ? { debugName: "initialWidth" } : /* istanbul ignore next */ {},
90
- transform: (v) => v == null ? null : coerceNumberProperty(v, 480)
91
- });
92
- initialHeight = input(null, {
93
- ...ngDevMode ? { debugName: "initialHeight" } : /* istanbul ignore next */ {},
94
- transform: (v) => v == null ? null : coerceNumberProperty(v, 320)
95
- });
96
- os = input("auto", ...ngDevMode ? [{ debugName: "os" }] : /* istanbul ignore next */ []);
97
- resolvedOs = computed(() => {
98
- const o = this.os();
99
- return o === "auto" ? DETECTED_OS : o;
100
- }, ...ngDevMode ? [{ debugName: "resolvedOs" }] : /* istanbul ignore next */ []);
101
- minimizeVisible = computed(() => this.showMinimize() ?? this.resolvedOs() !== "linux", ...ngDevMode ? [{ debugName: "minimizeVisible" }] : /* istanbul ignore next */ []);
102
- maximizeVisible = computed(() => this.showMaximize() ?? this.resolvedOs() !== "linux", ...ngDevMode ? [{ debugName: "maximizeVisible" }] : /* istanbul ignore next */ []);
103
- chromeSize = input("md", ...ngDevMode ? [{ debugName: "chromeSize" }] : /* istanbul ignore next */ []);
104
- animations = input(true, {
105
- ...ngDevMode ? { debugName: "animations" } : /* istanbul ignore next */ {},
106
- transform: coerceBooleanProperty
107
- });
108
- minWidth = input(220, {
109
- ...ngDevMode ? { debugName: "minWidth" } : /* istanbul ignore next */ {},
110
- transform: (v) => coerceNumberProperty(v, 220)
111
- });
112
- minHeight = input(140, {
113
- ...ngDevMode ? { debugName: "minHeight" } : /* istanbul ignore next */ {},
114
- transform: (v) => coerceNumberProperty(v, 140)
115
- });
116
- maxWidth = input(Number.POSITIVE_INFINITY, {
117
- ...ngDevMode ? { debugName: "maxWidth" } : /* istanbul ignore next */ {},
118
- transform: (v) => v == null ? Number.POSITIVE_INFINITY : coerceNumberProperty(v, Infinity)
119
- });
120
- maxHeight = input(Number.POSITIVE_INFINITY, {
121
- ...ngDevMode ? { debugName: "maxHeight" } : /* istanbul ignore next */ {},
122
- transform: (v) => v == null ? Number.POSITIVE_INFINITY : coerceNumberProperty(v, Infinity)
123
- });
124
- movable = input(true, {
125
- ...ngDevMode ? { debugName: "movable" } : /* istanbul ignore next */ {},
126
- transform: coerceBooleanProperty
127
- });
128
- resizable = input(true, {
129
- ...ngDevMode ? { debugName: "resizable" } : /* istanbul ignore next */ {},
130
- transform: coerceBooleanProperty
131
- });
132
- keepInViewport = input(true, {
133
- ...ngDevMode ? { debugName: "keepInViewport" } : /* istanbul ignore next */ {},
134
- transform: coerceBooleanProperty
135
- });
136
- snap = input("none", ...ngDevMode ? [{ debugName: "snap" }] : /* istanbul ignore next */ []);
137
- dragHandle = input(null, ...ngDevMode ? [{ debugName: "dragHandle" }] : /* istanbul ignore next */ []);
138
- showMinimize = input(null, {
139
- ...ngDevMode ? { debugName: "showMinimize" } : /* istanbul ignore next */ {},
140
- transform: optionalBoolean
141
- });
142
- showMaximize = input(null, {
143
- ...ngDevMode ? { debugName: "showMaximize" } : /* istanbul ignore next */ {},
144
- transform: optionalBoolean
145
- });
146
- showClose = input(true, {
147
- ...ngDevMode ? { debugName: "showClose" } : /* istanbul ignore next */ {},
148
- transform: coerceBooleanProperty
149
- });
150
- closed = output();
151
- moved = output();
152
- resized = output();
153
- minimizedWithFocus = output();
67
+ untitled = useI18nText(NO_OVERRIDE$1, "window.untitled", "Untitled");
68
+ moveModeText = useI18nText(NO_OVERRIDE$1, "window.moveMode", "Moving the window. Use the arrow keys, Enter to finish, Escape to cancel.");
69
+ resizeModeText = useI18nText(NO_OVERRIDE$1, "window.resizeMode", "Resizing the window. Use the arrow keys, Enter to finish, Escape to cancel.");
70
+ movedText = useI18nFormatter("window.moved", "Window moved to {{x}}, {{y}}");
71
+ resizedText = useI18nFormatter("window.resized", "Window resized to {{width}} by {{height}}");
154
72
  manager = inject(WrWindowManager);
155
- destroyRef = inject(DestroyRef);
156
73
  elRef = inject(ElementRef);
157
74
  focusTraps = inject(FocusTrapFactory);
158
- viewport = signal({
159
- width: viewportWidth(),
160
- height: viewportHeight()
161
- }, ...ngDevMode ? [{ debugName: "viewport" }] : /* istanbul ignore next */ []);
162
- x_ = signal(0, ...ngDevMode ? [{ debugName: "x_" }] : /* istanbul ignore next */ []);
163
- y_ = signal(0, ...ngDevMode ? [{ debugName: "y_" }] : /* istanbul ignore next */ []);
164
- width_ = signal(0, ...ngDevMode ? [{ debugName: "width_" }] : /* istanbul ignore next */ []);
165
- height_ = signal(0, ...ngDevMode ? [{ debugName: "height_" }] : /* istanbul ignore next */ []);
166
- restoreGeometry = null;
167
- z = signal(this.manager.bringToFront(), ...ngDevMode ? [{ debugName: "z" }] : /* istanbul ignore next */ []);
75
+ dir = inject(Directionality, { optional: true });
76
+ endMove = null;
77
+ constructor() {
78
+ inject(DestroyRef).onDestroy(() => this.endMove?.());
79
+ }
80
+ announcement = signal("", ...ngDevMode ? [{ debugName: "announcement" }] : /* istanbul ignore next */ []);
81
+ lastFocused = null;
82
+ state = computed(() => this.model().state(), ...ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []);
83
+ rect = computed(() => this.model().rect(), ...ngDevMode ? [{ debugName: "rect" }] : /* istanbul ignore next */ []);
84
+ options = computed(() => this.model().options(), ...ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []);
85
+ displayTitle = computed(() => this.model().title() || this.untitled(), ...ngDevMode ? [{ debugName: "displayTitle" }] : /* istanbul ignore next */ []);
86
+ resolvedOs = computed(() => {
87
+ const os = this.options().os;
88
+ return os === "auto" ? DETECTED_OS : os;
89
+ }, ...ngDevMode ? [{ debugName: "resolvedOs" }] : /* istanbul ignore next */ []);
90
+ minimizeVisible = computed(() => this.options().showMinimize ?? this.resolvedOs() !== "linux", ...ngDevMode ? [{ debugName: "minimizeVisible" }] : /* istanbul ignore next */ []);
91
+ maximizeVisible = computed(() => this.options().showMaximize ?? this.resolvedOs() !== "linux", ...ngDevMode ? [{ debugName: "maximizeVisible" }] : /* istanbul ignore next */ []);
92
+ showClose = computed(() => this.options().showClose, ...ngDevMode ? [{ debugName: "showClose" }] : /* istanbul ignore next */ []);
93
+ actionCount = computed(() => Number(this.minimizeVisible()) + Number(this.maximizeVisible()) + Number(this.showClose()), ...ngDevMode ? [{ debugName: "actionCount" }] : /* istanbul ignore next */ []);
94
+ minimizeActionLabel = computed(() => this.state() === "minimized" ? this.restoreLabel() : this.minimizeLabel(), ...ngDevMode ? [{ debugName: "minimizeActionLabel" }] : /* istanbul ignore next */ []);
95
+ maximizeActionLabel = computed(() => this.state() === "maximized" ? this.restoreDownLabel() : this.maximizeLabel(), ...ngDevMode ? [{ debugName: "maximizeActionLabel" }] : /* istanbul ignore next */ []);
96
+ resizeHandles = computed(() => this.options().resizable && this.state() === "normal", ...ngDevMode ? [{ debugName: "resizeHandles" }] : /* istanbul ignore next */ []);
168
97
  classes = computed(() => {
98
+ const model = this.model();
169
99
  const parts = [
170
100
  "wr-window",
171
101
  `wr-window--${this.state()}`,
172
102
  `wr-window--os-${this.resolvedOs()}`,
173
- `wr-window--chrome-${this.chromeSize()}`
103
+ `wr-window--chrome-${this.options().chromeSize}`,
104
+ model.isActive() ? "wr-window--active" : "wr-window--inactive"
174
105
  ];
175
- if (!this.open()) parts.push("wr-window--closed");
176
- if (this.resizable() && this.state() === "normal") parts.push("wr-window--resizable");
177
- if (!this.animations()) parts.push("wr-window--no-anim");
106
+ if (this.resizeHandles()) parts.push("wr-window--resizable");
107
+ if (!this.options().animations) parts.push("wr-window--no-anim");
108
+ const mode = model.keyboardMode();
109
+ if (mode) parts.push(mode === "move" ? "wr-window--keyboard-moving" : "wr-window--keyboard-resizing");
178
110
  return parts.join(" ");
179
111
  }, ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
180
- x = computed(() => this.state() === "maximized" ? 0 : this.x_(), ...ngDevMode ? [{ debugName: "x" }] : /* istanbul ignore next */ []);
181
- y = computed(() => this.state() === "maximized" ? 0 : this.y_(), ...ngDevMode ? [{ debugName: "y" }] : /* istanbul ignore next */ []);
182
- width = computed(() => this.state() === "maximized" ? this.viewport().width : this.width_(), ...ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []);
183
- height = computed(() => this.state() === "maximized" ? this.viewport().height : this.state() === "minimized" ? this.minimizedHeight() : this.height_(), ...ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []);
184
- minimizedHeight = signal(40, ...ngDevMode ? [{ debugName: "minimizedHeight" }] : /* istanbul ignore next */ []);
185
- snapTarget = signal(null, ...ngDevMode ? [{ debugName: "snapTarget" }] : /* istanbul ignore next */ []);
186
- constructor() {
187
- afterNextRender(() => {
188
- const measure = () => this.viewport.set({
189
- width: viewportWidth(),
190
- height: viewportHeight()
191
- });
192
- measure();
193
- window.addEventListener("resize", measure);
194
- this.destroyRef.onDestroy(() => window.removeEventListener("resize", measure));
195
- this.seedInitialGeometry();
196
- });
197
- effect(() => {
198
- if (this.state() !== "normal") return;
199
- this.clampToViewport();
200
- });
112
+ titleExtraTemplate = computed(() => asTemplate(this.titleExtra()), ...ngDevMode ? [{ debugName: "titleExtraTemplate" }] : /* istanbul ignore next */ []);
113
+ titleExtraComponent = computed(() => asComponent(this.titleExtra()), ...ngDevMode ? [{ debugName: "titleExtraComponent" }] : /* istanbul ignore next */ []);
114
+ statusBarTemplate = computed(() => asTemplate(this.statusBar()), ...ngDevMode ? [{ debugName: "statusBarTemplate" }] : /* istanbul ignore next */ []);
115
+ statusBarComponent = computed(() => asComponent(this.statusBar()), ...ngDevMode ? [{ debugName: "statusBarComponent" }] : /* istanbul ignore next */ []);
116
+ slotContext = computed(() => {
117
+ const ref = this.windowRef();
118
+ return ref ? { $implicit: ref } : null;
119
+ }, ...ngDevMode ? [{ debugName: "slotContext" }] : /* istanbul ignore next */ []);
120
+ ngOnInit() {
121
+ this.model().element = this.elRef.nativeElement;
201
122
  }
202
123
  close() {
203
124
  this.closed.emit();
204
125
  }
205
126
  minimize() {
206
- const minimizing = this.state() !== "minimized";
207
- const hadFocus = minimizing && this.hasFocus();
208
- this.state.set(minimizing ? "minimized" : "normal");
209
- if (hadFocus) this.minimizedWithFocus.emit();
127
+ this.model().minimize();
210
128
  }
211
129
  maximize() {
212
- if (this.state() === "maximized") this.restore();
213
- else {
214
- this.restoreGeometry = {
215
- x: this.x_(),
216
- y: this.y_(),
217
- width: this.width_(),
218
- height: this.height_()
219
- };
220
- this.state.set("maximized");
221
- }
222
- }
223
- restore() {
224
- if (this.state() === "maximized" && this.restoreGeometry) {
225
- this.x_.set(this.restoreGeometry.x);
226
- this.y_.set(this.restoreGeometry.y);
227
- this.width_.set(this.restoreGeometry.width);
228
- this.height_.set(this.restoreGeometry.height);
229
- this.restoreGeometry = null;
230
- }
231
- this.state.set("normal");
232
- }
233
- moveTo(x, y) {
234
- this.x_.set(x);
235
- this.y_.set(y);
236
- this.clampToViewport();
237
- }
238
- resizeTo(width, height) {
239
- this.width_.set(clamp(width, this.minWidth(), this.maxWidth()));
240
- this.height_.set(clamp(height, this.minHeight(), this.maxHeight()));
241
- this.clampToViewport();
242
- }
243
- center() {
244
- const w = this.width_();
245
- const h = this.height_();
246
- const { width: vw, height: vh } = this.viewport();
247
- this.x_.set(Math.max(0, (vw - w) / 2));
248
- this.y_.set(Math.max(0, (vh - h) / 2));
249
- }
250
- focus() {
251
- this.z.set(this.manager.bringToFront());
130
+ this.model().maximize();
252
131
  }
253
132
  focusInitial() {
254
133
  const host = this.elRef.nativeElement;
@@ -262,58 +141,155 @@ var WrWindow = class WrWindow {
262
141
  }
263
142
  host.focus({ preventScroll: true });
264
143
  }
265
- hasFocus() {
144
+ focusLast() {
266
145
  const host = this.elRef.nativeElement;
267
- return host.contains(host.ownerDocument.activeElement);
146
+ if (!host.isConnected || this.state() === "minimized") return;
147
+ const last = this.lastFocused;
148
+ if (last?.isConnected && host.contains(last)) {
149
+ last.focus({ preventScroll: true });
150
+ if (host.ownerDocument.activeElement === last) return;
151
+ }
152
+ this.focusInitial();
153
+ }
154
+ onFocusIn(event) {
155
+ const target = event.target;
156
+ if (!(target instanceof HTMLElement) || target === this.elRef.nativeElement) return;
157
+ if (target.closest(".wr-window__chrome-action")) return;
158
+ this.lastFocused = target;
268
159
  }
269
- focusWindow() {
270
- this.focus();
160
+ onFocusOut(event) {
161
+ const next = event.relatedTarget;
162
+ if (this.model().keyboardMode() && !(next instanceof Node && this.elRef.nativeElement.contains(next))) this.commitKeyboard();
163
+ }
164
+ onPointerDown(event) {
165
+ const model = this.model();
166
+ if (model.keyboardMode()) this.commitKeyboard();
167
+ const target = event.target instanceof Element ? event.target : null;
168
+ const grab = event.button === 0 && !!target && this.isGrabArea(target);
169
+ if (model.state() === "minimized") return;
170
+ const ref = this.windowRef();
171
+ if (ref) this.manager._activate(ref, { focus: grab });
172
+ if (grab) this.startMove(event);
173
+ }
174
+ isGrabArea(target) {
175
+ const host = this.elRef.nativeElement;
176
+ if (target.closest(".wr-window__chrome-action, .wr-window__handle")) return false;
177
+ const control = target.closest(INTERACTIVE);
178
+ if (control && host.contains(control)) return false;
179
+ const handle = this.options().dragHandle;
180
+ if (handle) {
181
+ const match = target.closest(handle);
182
+ return !!match && host.contains(match);
183
+ }
184
+ return !!target.closest(".wr-window__chrome");
185
+ }
186
+ isDblClickArea(target) {
187
+ const host = this.elRef.nativeElement;
188
+ if (target.closest(".wr-window__chrome-action, .wr-window__handle")) return false;
189
+ const control = target.closest(INTERACTIVE);
190
+ if (control && host.contains(control)) return false;
191
+ const chrome = target.closest(".wr-window__chrome");
192
+ return !!chrome && host.contains(chrome) || this.isGrabArea(target);
271
193
  }
272
194
  startMove(event) {
273
- if (!this.movable() || this.state() !== "normal") return;
274
- const targetEl = event.target;
275
- if (targetEl.closest(".wr-window__chrome-action")) return;
276
- const handle = this.dragHandle();
277
- if (handle && !targetEl.closest(handle)) return;
195
+ const model = this.model();
196
+ const o = model.options();
197
+ const state = model.state();
198
+ if (!o.movable || state === "minimized") return;
199
+ if (state === "maximized" && !o.restoreOnDrag) return;
278
200
  event.preventDefault();
279
- const target = event.currentTarget;
280
- target.setPointerCapture(event.pointerId);
281
- const startX = this.x_();
282
- const startY = this.y_();
201
+ this.endMove?.();
202
+ const host = this.elRef.nativeElement;
203
+ const doc = host.ownerDocument;
204
+ const pressed = event.target instanceof Element ? event.target : host;
205
+ const pointerId = event.pointerId;
283
206
  const originX = event.clientX;
284
207
  const originY = event.clientY;
208
+ const start = model.rect();
209
+ let detached = state === "normal" && !model.snap();
210
+ if (!detached && !o.restoreOnDrag) {
211
+ model.unsnapInPlace();
212
+ detached = true;
213
+ }
214
+ let startX = model.x();
215
+ let startY = model.y();
216
+ let dragging = false;
285
217
  const move = (e) => {
286
- this.x_.set(startX + (e.clientX - originX));
287
- this.y_.set(startY + (e.clientY - originY));
288
- this.clampToViewport();
289
- this.snapTarget.set(this.detectSnapTarget(e.clientX, e.clientY));
218
+ if (e.pointerId !== pointerId) return;
219
+ const dx = e.clientX - originX;
220
+ const dy = e.clientY - originY;
221
+ if (!dragging && Math.hypot(dx, dy) >= DRAG_THRESHOLD) {
222
+ dragging = true;
223
+ try {
224
+ pressed.setPointerCapture?.(pointerId);
225
+ } catch {}
226
+ }
227
+ if (!detached) {
228
+ if (!dragging) return;
229
+ const fraction = start.width > 0 ? (originX - start.left) / start.width : .5;
230
+ model.restore();
231
+ startX = originX - fraction * model.width();
232
+ startY = start.top;
233
+ detached = true;
234
+ }
235
+ model.setRaw({
236
+ x: startX + dx,
237
+ y: startY + dy
238
+ });
239
+ model.clamp();
240
+ model.dragTarget.set(this.detectSnapTarget(e.clientX, e.clientY));
290
241
  };
291
- const cleanup = (e) => {
292
- target.releasePointerCapture(e.pointerId);
293
- target.removeEventListener("pointermove", move);
294
- target.removeEventListener("pointerup", cleanup);
295
- target.removeEventListener("pointercancel", cleanup);
296
- const snap = this.snapTarget();
297
- this.snapTarget.set(null);
298
- if (snap) this.applySnap(snap);
299
- this.moved.emit({
300
- x: this.x_(),
301
- y: this.y_()
242
+ const stop = () => {
243
+ doc.removeEventListener("pointermove", move);
244
+ doc.removeEventListener("pointerup", end);
245
+ doc.removeEventListener("pointercancel", end);
246
+ if (dragging && pressed.hasPointerCapture?.(pointerId)) pressed.releasePointerCapture?.(pointerId);
247
+ model.dragTarget.set(null);
248
+ this.endMove = null;
249
+ };
250
+ const end = (e) => {
251
+ if (e.pointerId !== pointerId) return;
252
+ const snap = model.dragTarget();
253
+ stop();
254
+ if (dragging) this.swallowClick(doc, pressed);
255
+ if (!detached) return;
256
+ if (snap) {
257
+ model.snapTo(snap);
258
+ if (snap !== "top") model.resized.next({
259
+ width: model.rect().width,
260
+ height: model.rect().height
261
+ });
262
+ }
263
+ const r = model.rect();
264
+ if (snap || r.left !== start.left || r.top !== start.top) model.moved.next({
265
+ x: r.left,
266
+ y: r.top
302
267
  });
303
268
  };
304
- target.addEventListener("pointermove", move);
305
- target.addEventListener("pointerup", cleanup);
306
- target.addEventListener("pointercancel", cleanup);
269
+ doc.addEventListener("pointermove", move);
270
+ doc.addEventListener("pointerup", end);
271
+ doc.addEventListener("pointercancel", end);
272
+ this.endMove = stop;
273
+ }
274
+ swallowClick(doc, pressed) {
275
+ const swallow = (e) => {
276
+ if (e.target !== pressed) return;
277
+ doc.removeEventListener("click", swallow, true);
278
+ e.stopPropagation();
279
+ e.preventDefault();
280
+ };
281
+ doc.addEventListener("click", swallow, true);
282
+ setTimeout(() => doc.removeEventListener("click", swallow, true));
307
283
  }
308
284
  detectSnapTarget(clientX, clientY) {
309
- const mode = this.snap();
285
+ const model = this.model();
286
+ const mode = model.options().snap;
310
287
  if (mode === "none") return null;
311
- const HOT = 12;
312
- const { width: vw, height: vh } = this.viewport();
313
- const nearLeft = clientX <= HOT;
314
- const nearRight = clientX >= vw - HOT;
315
- const nearTop = clientY <= HOT;
316
- const nearBottom = clientY >= vh - HOT;
288
+ const wa = model.workArea();
289
+ const nearLeft = clientX <= wa.left + SNAP_HOT_ZONE;
290
+ const nearRight = clientX >= wa.left + wa.width - SNAP_HOT_ZONE;
291
+ const nearTop = clientY <= wa.top + SNAP_HOT_ZONE;
292
+ const nearBottom = clientY >= wa.top + wa.height - SNAP_HOT_ZONE;
317
293
  if (mode === "all") {
318
294
  if (nearTop && nearLeft) return "top-left";
319
295
  if (nearTop && nearRight) return "top-right";
@@ -325,74 +301,43 @@ var WrWindow = class WrWindow {
325
301
  if (nearRight) return "right";
326
302
  return null;
327
303
  }
328
- applySnap(target) {
329
- const { width: vw, height: vh } = this.viewport();
330
- const halfW = Math.floor(vw / 2);
331
- const halfH = Math.floor(vh / 2);
332
- switch (target) {
333
- case "top":
334
- this.restoreGeometry = {
335
- x: this.x_(),
336
- y: this.y_(),
337
- width: this.width_(),
338
- height: this.height_()
339
- };
340
- this.state.set("maximized");
341
- return;
342
- case "left":
343
- this.x_.set(0);
344
- this.y_.set(0);
345
- this.width_.set(halfW);
346
- this.height_.set(vh);
347
- break;
348
- case "right":
349
- this.x_.set(halfW);
350
- this.y_.set(0);
351
- this.width_.set(vw - halfW);
352
- this.height_.set(vh);
353
- break;
354
- case "top-left":
355
- this.x_.set(0);
356
- this.y_.set(0);
357
- this.width_.set(halfW);
358
- this.height_.set(halfH);
359
- break;
360
- case "top-right":
361
- this.x_.set(halfW);
362
- this.y_.set(0);
363
- this.width_.set(vw - halfW);
364
- this.height_.set(halfH);
365
- break;
366
- case "bottom-left":
367
- this.x_.set(0);
368
- this.y_.set(halfH);
369
- this.width_.set(halfW);
370
- this.height_.set(vh - halfH);
371
- break;
372
- case "bottom-right":
373
- this.x_.set(halfW);
374
- this.y_.set(halfH);
375
- this.width_.set(vw - halfW);
376
- this.height_.set(vh - halfH);
304
+ onChromeDblClick(event) {
305
+ this.toggleFromDblClick(event.target);
306
+ }
307
+ onBodyDblClick(event) {
308
+ const target = event.target;
309
+ if (target instanceof Element && target.closest(".wr-window__chrome")) return;
310
+ this.toggleFromDblClick(target);
311
+ }
312
+ toggleFromDblClick(target) {
313
+ if (!(target instanceof Element) || !this.isDblClickArea(target)) return;
314
+ const model = this.model();
315
+ if (model.state() === "minimized") return;
316
+ if (model.state() === "normal" && model.snap()) {
317
+ model.restore();
318
+ return;
377
319
  }
378
- this.resized.emit({
379
- width: this.width_(),
380
- height: this.height_()
381
- });
320
+ if (model.options().showMaximize !== false) model.maximize();
382
321
  }
383
322
  startResize(event, edges) {
384
- if (!this.resizable() || this.state() !== "normal") return;
323
+ const model = this.model();
324
+ if (!model.options().resizable || model.state() !== "normal") return;
385
325
  event.preventDefault();
386
326
  event.stopPropagation();
327
+ if (model.keyboardMode()) this.commitKeyboard();
328
+ const ref = this.windowRef();
329
+ if (ref) this.manager._activate(ref, { focus: true });
330
+ model.unsnapInPlace();
387
331
  const target = event.currentTarget;
388
- target.setPointerCapture(event.pointerId);
389
- const startX = this.x_();
390
- const startY = this.y_();
391
- const startW = this.width_();
392
- const startH = this.height_();
332
+ target.setPointerCapture?.(event.pointerId);
333
+ const startX = model.x();
334
+ const startY = model.y();
335
+ const startW = model.width();
336
+ const startH = model.height();
393
337
  const originX = event.clientX;
394
338
  const originY = event.clientY;
395
339
  const move = (e) => {
340
+ const o = model.options();
396
341
  const dx = e.clientX - originX;
397
342
  const dy = e.clientY - originY;
398
343
  let nextX = startX;
@@ -409,24 +354,26 @@ var WrWindow = class WrWindow {
409
354
  nextH = startH - dy;
410
355
  nextY = startY + dy;
411
356
  }
412
- const clampedW = clamp(nextW, this.minWidth(), this.maxWidth());
413
- const clampedH = clamp(nextH, this.minHeight(), this.maxHeight());
357
+ const clampedW = Math.min(o.maxWidth, Math.max(o.minWidth, nextW));
358
+ const clampedH = Math.min(o.maxHeight, Math.max(o.minHeight, nextH));
414
359
  if (edges.l) nextX += nextW - clampedW;
415
360
  if (edges.t) nextY += nextH - clampedH;
416
- this.x_.set(nextX);
417
- this.y_.set(nextY);
418
- this.width_.set(clampedW);
419
- this.height_.set(clampedH);
420
- this.clampToViewport();
361
+ model.setRaw({
362
+ x: nextX,
363
+ y: nextY,
364
+ width: clampedW,
365
+ height: clampedH
366
+ });
367
+ model.clamp();
421
368
  };
422
369
  const cleanup = (e) => {
423
- target.releasePointerCapture(e.pointerId);
370
+ target.releasePointerCapture?.(e.pointerId);
424
371
  target.removeEventListener("pointermove", move);
425
372
  target.removeEventListener("pointerup", cleanup);
426
373
  target.removeEventListener("pointercancel", cleanup);
427
- this.resized.emit({
428
- width: this.width_(),
429
- height: this.height_()
374
+ model.resized.next({
375
+ width: model.width(),
376
+ height: model.height()
430
377
  });
431
378
  };
432
379
  target.addEventListener("pointermove", move);
@@ -453,44 +400,73 @@ var WrWindow = class WrWindow {
453
400
  l: true
454
401
  });
455
402
  resizeL = (e) => this.startResize(e, { l: true });
456
- seedInitialGeometry() {
457
- const sizePreset = this.size();
458
- const preset = sizePreset ? SIZE_PRESETS[sizePreset] : SIZE_PRESETS.md;
459
- const explicitW = this.initialWidth();
460
- const explicitH = this.initialHeight();
461
- const keep = this.keepInViewport();
462
- const { width: vw, height: vh } = this.viewport();
463
- const fit = (size, room) => keep ? Math.min(size, room) : size;
464
- const w = clamp(fit(explicitW ?? preset.width, vw), this.minWidth(), this.maxWidth());
465
- const h = clamp(fit(explicitH ?? preset.height, vh), this.minHeight(), this.maxHeight());
466
- this.width_.set(w);
467
- this.height_.set(h);
468
- const xIn = this.initialX();
469
- const yIn = this.initialY();
470
- let x;
471
- let y;
472
- if (xIn != null && yIn != null) {
473
- x = xIn;
474
- y = yIn;
475
- } else {
476
- const cascade = this.manager.nextStartOffset();
477
- x = xIn ?? cascade.x;
478
- y = yIn ?? cascade.y;
403
+ onKeydown(event) {
404
+ if (this.model().keyboardMode()) {
405
+ this.onModeKey(event);
406
+ return;
407
+ }
408
+ if (event.defaultPrevented || !event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
409
+ if (event.key !== "F7" && event.key !== "F8") return;
410
+ if (this.startKeyboard(event.key === "F7" ? "move" : "resize")) event.preventDefault();
411
+ }
412
+ startKeyboard(mode) {
413
+ const model = this.model();
414
+ const ref = this.windowRef();
415
+ if (ref) this.manager._activate(ref, { focus: false });
416
+ if (!model.startKeyboard(mode)) return false;
417
+ this.elRef.nativeElement.focus({ preventScroll: true });
418
+ this.announce(mode === "move" ? this.moveModeText() : this.resizeModeText());
419
+ return true;
420
+ }
421
+ onModeKey(event) {
422
+ const model = this.model();
423
+ const step = event.shiftKey ? 1 : model.options().keyboardStep;
424
+ const delta = {
425
+ ArrowLeft: [-step, 0],
426
+ ArrowRight: [step, 0],
427
+ ArrowUp: [0, -step],
428
+ ArrowDown: [0, step]
429
+ }[event.key];
430
+ if (delta) {
431
+ event.preventDefault();
432
+ if (model.keyboardMode() === "move") model.nudge(delta[0], delta[1]);
433
+ else {
434
+ const rtl = this.dir?.value === "rtl";
435
+ model.grow(rtl ? -delta[0] : delta[0], delta[1], rtl);
436
+ }
437
+ return;
479
438
  }
480
- this.x_.set(keep && xIn == null ? clamp(x, 0, Math.max(0, vw - w)) : x);
481
- this.y_.set(keep && yIn == null ? clamp(y, 0, Math.max(0, vh - h)) : y);
482
- this.clampToViewport();
439
+ if (event.key === "Enter") {
440
+ event.preventDefault();
441
+ this.commitKeyboard();
442
+ this.focusLast();
443
+ } else if (event.key === "Escape") {
444
+ event.preventDefault();
445
+ const mode = model.cancelKeyboard();
446
+ if (mode) this.announceGeometry(mode);
447
+ this.focusLast();
448
+ } else if (event.key === "Tab") this.commitKeyboard();
483
449
  }
484
- clampToViewport() {
485
- if (!this.keepInViewport()) return;
486
- const { width: vw, height: vh } = this.viewport();
487
- const w = this.width_();
488
- this.x_.set(clamp(this.x_(), MIN_ON_VIEWPORT - w, vw - MIN_ON_VIEWPORT));
489
- this.y_.set(clamp(this.y_(), 0, vh - MIN_ON_VIEWPORT));
450
+ commitKeyboard() {
451
+ const mode = this.model().commitKeyboard();
452
+ if (mode) this.announceGeometry(mode);
453
+ }
454
+ announceGeometry(mode) {
455
+ const r = this.model().rect();
456
+ this.announce(mode === "move" ? this.movedText({
457
+ x: r.left,
458
+ y: r.top
459
+ }) : this.resizedText({
460
+ width: r.width,
461
+ height: r.height
462
+ }));
463
+ }
464
+ announce(text) {
465
+ this.announcement.set(text);
490
466
  }
491
467
  static ɵfac = i0.ɵɵngDeclareFactory({
492
468
  minVersion: "12.0.0",
493
- version: "22.2.3",
469
+ version: "22.2.4",
494
470
  ngImport: i0,
495
471
  type: WrWindow,
496
472
  deps: [],
@@ -498,207 +474,101 @@ var WrWindow = class WrWindow {
498
474
  });
499
475
  static ɵcmp = i0.ɵɵngDeclareComponent({
500
476
  minVersion: "17.0.0",
501
- version: "22.2.3",
477
+ version: "22.2.4",
502
478
  type: WrWindow,
503
479
  isStandalone: true,
504
480
  selector: "wr-window",
505
481
  inputs: {
506
- open: {
507
- classPropertyName: "open",
508
- publicName: "open",
482
+ model: {
483
+ classPropertyName: "model",
484
+ publicName: "model",
509
485
  isSignal: true,
510
- isRequired: false,
486
+ isRequired: true,
511
487
  transformFunction: null
512
488
  },
513
- state: {
514
- classPropertyName: "state",
515
- publicName: "state",
489
+ windowRef: {
490
+ classPropertyName: "windowRef",
491
+ publicName: "windowRef",
516
492
  isSignal: true,
517
493
  isRequired: false,
518
494
  transformFunction: null
519
495
  },
520
- title: {
521
- classPropertyName: "title",
522
- publicName: "title",
496
+ injector: {
497
+ classPropertyName: "injector",
498
+ publicName: "injector",
523
499
  isSignal: true,
524
500
  isRequired: false,
525
501
  transformFunction: null
526
502
  },
527
- initialX: {
528
- classPropertyName: "initialX",
529
- publicName: "initialX",
503
+ titleExtra: {
504
+ classPropertyName: "titleExtra",
505
+ publicName: "titleExtra",
530
506
  isSignal: true,
531
507
  isRequired: false,
532
508
  transformFunction: null
533
509
  },
534
- initialY: {
535
- classPropertyName: "initialY",
536
- publicName: "initialY",
537
- isSignal: true,
538
- isRequired: false,
539
- transformFunction: null
540
- },
541
- size: {
542
- classPropertyName: "size",
543
- publicName: "size",
544
- isSignal: true,
545
- isRequired: false,
546
- transformFunction: null
547
- },
548
- initialWidth: {
549
- classPropertyName: "initialWidth",
550
- publicName: "initialWidth",
551
- isSignal: true,
552
- isRequired: false,
553
- transformFunction: null
554
- },
555
- initialHeight: {
556
- classPropertyName: "initialHeight",
557
- publicName: "initialHeight",
558
- isSignal: true,
559
- isRequired: false,
560
- transformFunction: null
561
- },
562
- os: {
563
- classPropertyName: "os",
564
- publicName: "os",
565
- isSignal: true,
566
- isRequired: false,
567
- transformFunction: null
568
- },
569
- chromeSize: {
570
- classPropertyName: "chromeSize",
571
- publicName: "chromeSize",
572
- isSignal: true,
573
- isRequired: false,
574
- transformFunction: null
575
- },
576
- animations: {
577
- classPropertyName: "animations",
578
- publicName: "animations",
579
- isSignal: true,
580
- isRequired: false,
581
- transformFunction: null
582
- },
583
- minWidth: {
584
- classPropertyName: "minWidth",
585
- publicName: "minWidth",
586
- isSignal: true,
587
- isRequired: false,
588
- transformFunction: null
589
- },
590
- minHeight: {
591
- classPropertyName: "minHeight",
592
- publicName: "minHeight",
593
- isSignal: true,
594
- isRequired: false,
595
- transformFunction: null
596
- },
597
- maxWidth: {
598
- classPropertyName: "maxWidth",
599
- publicName: "maxWidth",
600
- isSignal: true,
601
- isRequired: false,
602
- transformFunction: null
603
- },
604
- maxHeight: {
605
- classPropertyName: "maxHeight",
606
- publicName: "maxHeight",
607
- isSignal: true,
608
- isRequired: false,
609
- transformFunction: null
610
- },
611
- movable: {
612
- classPropertyName: "movable",
613
- publicName: "movable",
614
- isSignal: true,
615
- isRequired: false,
616
- transformFunction: null
617
- },
618
- resizable: {
619
- classPropertyName: "resizable",
620
- publicName: "resizable",
621
- isSignal: true,
622
- isRequired: false,
623
- transformFunction: null
624
- },
625
- keepInViewport: {
626
- classPropertyName: "keepInViewport",
627
- publicName: "keepInViewport",
628
- isSignal: true,
629
- isRequired: false,
630
- transformFunction: null
631
- },
632
- snap: {
633
- classPropertyName: "snap",
634
- publicName: "snap",
635
- isSignal: true,
636
- isRequired: false,
637
- transformFunction: null
638
- },
639
- dragHandle: {
640
- classPropertyName: "dragHandle",
641
- publicName: "dragHandle",
642
- isSignal: true,
643
- isRequired: false,
644
- transformFunction: null
645
- },
646
- showMinimize: {
647
- classPropertyName: "showMinimize",
648
- publicName: "showMinimize",
649
- isSignal: true,
650
- isRequired: false,
651
- transformFunction: null
652
- },
653
- showMaximize: {
654
- classPropertyName: "showMaximize",
655
- publicName: "showMaximize",
656
- isSignal: true,
657
- isRequired: false,
658
- transformFunction: null
659
- },
660
- showClose: {
661
- classPropertyName: "showClose",
662
- publicName: "showClose",
510
+ statusBar: {
511
+ classPropertyName: "statusBar",
512
+ publicName: "statusBar",
663
513
  isSignal: true,
664
514
  isRequired: false,
665
515
  transformFunction: null
666
516
  }
667
517
  },
668
- outputs: {
669
- open: "openChange",
670
- state: "stateChange",
671
- closed: "closed",
672
- moved: "moved",
673
- resized: "resized",
674
- minimizedWithFocus: "minimizedWithFocus"
675
- },
518
+ outputs: { closed: "closed" },
676
519
  host: {
677
520
  attributes: {
678
521
  "role": "dialog",
679
522
  "tabindex": "-1"
680
523
  },
681
- listeners: { "pointerdown": "focusWindow()" },
524
+ listeners: {
525
+ "pointerdown": "onPointerDown($event)",
526
+ "dblclick": "onBodyDblClick($event)",
527
+ "keydown": "onKeydown($event)",
528
+ "focusin": "onFocusIn($event)",
529
+ "focusout": "onFocusOut($event)"
530
+ },
682
531
  properties: {
683
- "attr.title": "null",
684
532
  "class": "classes()",
685
- "style.left.px": "x()",
686
- "style.top.px": "y()",
687
- "style.width.px": "width()",
688
- "style.height.px": "height()",
689
- "style.z-index": "z()",
690
- "attr.aria-hidden": "!open()",
533
+ "style.left.px": "rect().left",
534
+ "style.top.px": "rect().top",
535
+ "style.width.px": "rect().width",
536
+ "style.height.px": "rect().height",
537
+ "style.z-index": "model().z()",
691
538
  "attr.aria-labelledby": "titleId"
692
539
  }
693
540
  },
694
541
  ngImport: i0,
695
- template: "<!--\n The double-click gate is `!== false`, NOT `maximizeVisible()`, and the\n difference is a window nobody can get out of. The Linux preset hides the\n maximize BUTTON; the gesture is how GNOME maximizes, and it worked on every\n chrome before this input went tri-state. Gating it on button visibility would\n have killed it there — and `maximized` is reachable on Linux with no button at\n all (a snap to the top edge, `ref.maximize()`, a restored layout), after which\n every exit disappears at once: no restore-down button, no minimize button,\n `startMove` refuses while the state is not `normal`, the resize handles are not\n rendered, and the only key the window listens for is Escape, which closes it.\n An explicit `showMaximize: false` still turns the gesture off everywhere.\n-->\n<div class=\"wr-window__chrome\" (pointerdown)=\"startMove($event)\" (dblclick)=\"showMaximize() !== false && maximize()\">\n <div class=\"wr-window__title\" [attr.id]=\"titleId\">{{ title() }}</div>\n <div class=\"wr-window__title-extra\">\n <ng-content select=\"[wrWindowTitleExtra]\" />\n </div>\n\n <div class=\"wr-window__chrome-actions\">\n @if (resolvedOs() === 'macos') {\n <!-- macOS-style traffic-light cluster: close → minimize → maximize.\n Glyphs (×, −, +) appear on cluster hover. -->\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path\n d=\"M2.2 2.2 L7.8 7.8 M7.8 2.2 L2.2 7.8\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n }\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <line x1=\"2\" y1=\"5\" x2=\"8\" y2=\"5\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n @if (state() === 'maximized') {\n <path\n d=\"M3 1 L1 1 L1 3 M7 1 L9 1 L9 3 M1 7 L1 9 L3 9 M9 7 L9 9 L7 9\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n fill=\"none\"\n />\n } @else {\n <path d=\"M2 5 L8 5 M5 2 L5 8\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n }\n </svg>\n </button>\n }\n } @else {\n <!-- Windows / Linux glyph buttons. Linux hides minimize and maximize by\n DEFAULT, not by rule: `minimizeVisible()` resolves the input first, so\n `[showMinimize]=\"true\"` puts the button back — which is the only way a\n user on Linux can reach `<wr-window-taskbar>` at all. -->\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <line x1=\"1\" y1=\"8\" x2=\"9\" y2=\"8\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n @if (state() === 'maximized') {\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <rect x=\"1.5\" y=\"2.5\" width=\"6\" height=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <rect x=\"3\" y=\"1\" width=\"6\" height=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n </svg>\n } @else {\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <rect x=\"1\" y=\"1\" width=\"8\" height=\"8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n </svg>\n }\n </button>\n }\n\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M1 1 L9 9 M9 1 L1 9\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n }\n </div>\n</div>\n\n<div class=\"wr-window__body\">\n <ng-content />\n</div>\n\n<div class=\"wr-window__status-bar\">\n <ng-content select=\"[wrWindowStatusBar]\" />\n</div>\n\n@if (resizable() && state() === 'normal') {\n <span class=\"wr-window__handle wr-window__handle--t\" (pointerdown)=\"resizeT($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--r\" (pointerdown)=\"resizeR($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--b\" (pointerdown)=\"resizeB($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--l\" (pointerdown)=\"resizeL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--tl\" (pointerdown)=\"resizeTL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--tr\" (pointerdown)=\"resizeTR($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--bl\" (pointerdown)=\"resizeBL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--br\" (pointerdown)=\"resizeBR($event)\"></span>\n}\n",
542
+ template: "<!--\n No pointerdown here: the host takes every press, so a `dragHandle` in the body\n can start a move as well as the title bar, and a press anywhere activates the\n window. The double-click is bound here and on the host; see `onChromeDblClick`.\n The action count is for the macOS chrome, which pads its title as wide as the\n cluster so the title centres on the whole bar.\n-->\n<div\n class=\"wr-window__chrome\"\n [style.--wr-window-chrome-action-count]=\"actionCount()\"\n (dblclick)=\"onChromeDblClick($event)\"\n>\n <div class=\"wr-window__title\" [attr.id]=\"titleId\">{{ displayTitle() }}</div>\n <div class=\"wr-window__title-extra\">\n @if (titleExtraTemplate(); as template) {\n <ng-container *ngTemplateOutlet=\"template; context: slotContext(); injector: injector()\" />\n } @else if (titleExtraComponent(); as component) {\n <ng-container *ngComponentOutlet=\"component; injector: injector()\" />\n }\n </div>\n\n <div class=\"wr-window__chrome-actions\">\n @if (resolvedOs() === 'macos') {\n <!--\n macOS traffic lights: close, minimize, zoom, in that order from the frame\n edge. The button is the hit target and the coloured dot is its\n `::before`, so the box can be big enough to press while the dot stays\n the platform's size. The glyphs sit on an 8px grid and appear only while\n the cluster is hovered or holds keyboard focus, as they do on macOS.\n Zoom draws the full-screen arrows of Big Sur and later: pointing out to\n maximize, pointing in to restore. Every straight edge of both sits on a\n whole pixel, so the filled triangles stay hard at 1x.\n -->\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"8\" height=\"8\" viewBox=\"0 0 8 8\" aria-hidden=\"true\">\n <path d=\"M1.5 1.5l5 5m0-5l-5 5\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"8\" height=\"8\" viewBox=\"0 0 8 8\" aria-hidden=\"true\">\n <path d=\"M1.5 3.5h5\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"8\" height=\"8\" viewBox=\"0 0 8 8\" aria-hidden=\"true\">\n @if (state() === 'maximized') {\n <path d=\"M3 3V.5L.5 3zM5 5v2.5L7.5 5z\" fill=\"currentColor\" />\n } @else {\n <path d=\"M1 1h4L1 5zm6 6H3l4-4z\" fill=\"currentColor\" />\n }\n </svg>\n </button>\n }\n } @else if (resolvedOs() === 'linux') {\n <!--\n GNOME (libadwaita) window controls: round buttons with Adwaita's 16px\n symbolic glyphs, 2px strokes on whole pixels. Restore is the SMALLER\n square, the way Adwaita draws it, not a second overlapping one. Close-only\n is the DEFAULT here, not a rule: `minimizeVisible()` resolves the input\n first, so `[showMinimize]=\"true\"` puts the button back, which is the only\n way a user on Linux can reach `<wr-window-taskbar>` at all.\n -->\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4 11h8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" />\n </svg>\n </button>\n }\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n @if (state() === 'maximized') {\n <rect x=\"6\" y=\"6\" width=\"4\" height=\"4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" />\n } @else {\n <rect x=\"5\" y=\"5\" width=\"6\" height=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" />\n }\n </svg>\n </button>\n }\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M5 5l6 6m0-6l-6 6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n } @else {\n <!--\n Windows 11 caption buttons: 10px glyphs in the full-height, 46px-wide\n buttons, drawn the way Segoe Fluent Icons draws them. 1px strokes on half\n pixels, so they land on whole device pixels at 1x and 2x, and the corners\n of maximize and restore are rounded by 1px.\n -->\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M0 4.5h10\" fill=\"none\" stroke=\"currentColor\" />\n </svg>\n </button>\n }\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n @if (state() === 'maximized') {\n <g fill=\"none\" stroke=\"currentColor\">\n <rect x=\".5\" y=\"2.5\" width=\"7\" height=\"7\" rx=\"1\" />\n <path d=\"M2.5 2.5v-1a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-1\" />\n </g>\n } @else {\n <rect x=\".5\" y=\".5\" width=\"9\" height=\"9\" rx=\"1\" fill=\"none\" stroke=\"currentColor\" />\n }\n </svg>\n </button>\n }\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M.5.5l9 9m0-9l-9 9\" fill=\"none\" stroke=\"currentColor\" />\n </svg>\n </button>\n }\n }\n </div>\n</div>\n\n<div class=\"wr-window__body\">\n <ng-content />\n</div>\n\n<div class=\"wr-window__status-bar\">\n @if (statusBarTemplate(); as template) {\n <ng-container *ngTemplateOutlet=\"template; context: slotContext(); injector: injector()\" />\n } @else if (statusBarComponent(); as component) {\n <ng-container *ngComponentOutlet=\"component; injector: injector()\" />\n }\n</div>\n\n<!--\n Keyboard move and resize say what they did here: the mode on entry, the\n landing on Enter or Escape. Not each step — that would flood the region.\n-->\n<p class=\"wr-window__sr-only\" role=\"status\">{{ announcement() }}</p>\n\n@if (resizeHandles()) {\n <span class=\"wr-window__handle wr-window__handle--t\" (pointerdown)=\"resizeT($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--r\" (pointerdown)=\"resizeR($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--b\" (pointerdown)=\"resizeB($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--l\" (pointerdown)=\"resizeL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--tl\" (pointerdown)=\"resizeTL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--tr\" (pointerdown)=\"resizeTR($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--bl\" (pointerdown)=\"resizeBL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--br\" (pointerdown)=\"resizeBR($event)\"></span>\n}\n",
543
+ dependencies: [{
544
+ kind: "directive",
545
+ type: NgTemplateOutlet,
546
+ selector: "[ngTemplateOutlet]",
547
+ inputs: [
548
+ "ngTemplateOutletContext",
549
+ "ngTemplateOutlet",
550
+ "ngTemplateOutletInjector"
551
+ ]
552
+ }, {
553
+ kind: "directive",
554
+ type: NgComponentOutlet,
555
+ selector: "[ngComponentOutlet]",
556
+ inputs: [
557
+ "ngComponentOutlet",
558
+ "ngComponentOutletInputs",
559
+ "ngComponentOutletInjector",
560
+ "ngComponentOutletEnvironmentInjector",
561
+ "ngComponentOutletContent",
562
+ "ngComponentOutletNgModule"
563
+ ],
564
+ exportAs: ["ngComponentOutlet"]
565
+ }],
696
566
  encapsulation: i0.ViewEncapsulation.None
697
567
  });
698
568
  };
699
569
  i0.ɵɵngDeclareClassMetadata({
700
570
  minVersion: "12.0.0",
701
- version: "22.2.3",
571
+ version: "22.2.4",
702
572
  ngImport: i0,
703
573
  type: WrWindow,
704
574
  decorators: [{
@@ -706,230 +576,71 @@ i0.ɵɵngDeclareClassMetadata({
706
576
  args: [{
707
577
  selector: "wr-window",
708
578
  encapsulation: ViewEncapsulation.None,
579
+ imports: [NgTemplateOutlet, NgComponentOutlet],
709
580
  host: {
710
- "[attr.title]": "null",
711
581
  role: "dialog",
712
582
  tabindex: "-1",
713
583
  "[class]": "classes()",
714
- "[style.left.px]": "x()",
715
- "[style.top.px]": "y()",
716
- "[style.width.px]": "width()",
717
- "[style.height.px]": "height()",
718
- "[style.z-index]": "z()",
719
- "[attr.aria-hidden]": "!open()",
720
- "[attr.aria-labelledby]": "titleId",
721
- "(pointerdown)": "focusWindow()"
722
- },
723
- template: "<!--\n The double-click gate is `!== false`, NOT `maximizeVisible()`, and the\n difference is a window nobody can get out of. The Linux preset hides the\n maximize BUTTON; the gesture is how GNOME maximizes, and it worked on every\n chrome before this input went tri-state. Gating it on button visibility would\n have killed it there — and `maximized` is reachable on Linux with no button at\n all (a snap to the top edge, `ref.maximize()`, a restored layout), after which\n every exit disappears at once: no restore-down button, no minimize button,\n `startMove` refuses while the state is not `normal`, the resize handles are not\n rendered, and the only key the window listens for is Escape, which closes it.\n An explicit `showMaximize: false` still turns the gesture off everywhere.\n-->\n<div class=\"wr-window__chrome\" (pointerdown)=\"startMove($event)\" (dblclick)=\"showMaximize() !== false && maximize()\">\n <div class=\"wr-window__title\" [attr.id]=\"titleId\">{{ title() }}</div>\n <div class=\"wr-window__title-extra\">\n <ng-content select=\"[wrWindowTitleExtra]\" />\n </div>\n\n <div class=\"wr-window__chrome-actions\">\n @if (resolvedOs() === 'macos') {\n <!-- macOS-style traffic-light cluster: close → minimize → maximize.\n Glyphs (×, −, +) appear on cluster hover. -->\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path\n d=\"M2.2 2.2 L7.8 7.8 M7.8 2.2 L2.2 7.8\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n />\n </svg>\n </button>\n }\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <line x1=\"2\" y1=\"5\" x2=\"8\" y2=\"5\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n @if (state() === 'maximized') {\n <path\n d=\"M3 1 L1 1 L1 3 M7 1 L9 1 L9 3 M1 7 L1 9 L3 9 M9 7 L9 9 L7 9\"\n stroke=\"currentColor\"\n stroke-width=\"1.4\"\n stroke-linecap=\"round\"\n fill=\"none\"\n />\n } @else {\n <path d=\"M2 5 L8 5 M5 2 L5 8\" stroke=\"currentColor\" stroke-width=\"1.4\" stroke-linecap=\"round\" />\n }\n </svg>\n </button>\n }\n } @else {\n <!-- Windows / Linux glyph buttons. Linux hides minimize and maximize by\n DEFAULT, not by rule: `minimizeVisible()` resolves the input first, so\n `[showMinimize]=\"true\"` puts the button back — which is the only way a\n user on Linux can reach `<wr-window-taskbar>` at all. -->\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <line x1=\"1\" y1=\"8\" x2=\"9\" y2=\"8\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n @if (state() === 'maximized') {\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <rect x=\"1.5\" y=\"2.5\" width=\"6\" height=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n <rect x=\"3\" y=\"1\" width=\"6\" height=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n </svg>\n } @else {\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <rect x=\"1\" y=\"1\" width=\"8\" height=\"8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.2\" />\n </svg>\n }\n </button>\n }\n\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M1 1 L9 9 M9 1 L1 9\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n }\n </div>\n</div>\n\n<div class=\"wr-window__body\">\n <ng-content />\n</div>\n\n<div class=\"wr-window__status-bar\">\n <ng-content select=\"[wrWindowStatusBar]\" />\n</div>\n\n@if (resizable() && state() === 'normal') {\n <span class=\"wr-window__handle wr-window__handle--t\" (pointerdown)=\"resizeT($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--r\" (pointerdown)=\"resizeR($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--b\" (pointerdown)=\"resizeB($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--l\" (pointerdown)=\"resizeL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--tl\" (pointerdown)=\"resizeTL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--tr\" (pointerdown)=\"resizeTR($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--bl\" (pointerdown)=\"resizeBL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--br\" (pointerdown)=\"resizeBR($event)\"></span>\n}\n"
724
- }]
725
- }],
726
- ctorParameters: () => [],
727
- propDecorators: {
728
- open: [{
729
- type: i0.Input,
730
- args: [{
731
- isSignal: true,
732
- alias: "open",
733
- required: false
734
- }]
735
- }, {
736
- type: i0.Output,
737
- args: ["openChange"]
738
- }],
739
- state: [{
740
- type: i0.Input,
741
- args: [{
742
- isSignal: true,
743
- alias: "state",
744
- required: false
745
- }]
746
- }, {
747
- type: i0.Output,
748
- args: ["stateChange"]
749
- }],
750
- title: [{
751
- type: i0.Input,
752
- args: [{
753
- isSignal: true,
754
- alias: "title",
755
- required: false
756
- }]
757
- }],
758
- initialX: [{
759
- type: i0.Input,
760
- args: [{
761
- isSignal: true,
762
- alias: "initialX",
763
- required: false
764
- }]
765
- }],
766
- initialY: [{
767
- type: i0.Input,
768
- args: [{
769
- isSignal: true,
770
- alias: "initialY",
771
- required: false
772
- }]
773
- }],
774
- size: [{
775
- type: i0.Input,
776
- args: [{
777
- isSignal: true,
778
- alias: "size",
779
- required: false
780
- }]
781
- }],
782
- initialWidth: [{
783
- type: i0.Input,
784
- args: [{
785
- isSignal: true,
786
- alias: "initialWidth",
787
- required: false
788
- }]
789
- }],
790
- initialHeight: [{
791
- type: i0.Input,
792
- args: [{
793
- isSignal: true,
794
- alias: "initialHeight",
795
- required: false
796
- }]
797
- }],
798
- os: [{
799
- type: i0.Input,
800
- args: [{
801
- isSignal: true,
802
- alias: "os",
803
- required: false
804
- }]
805
- }],
806
- chromeSize: [{
807
- type: i0.Input,
808
- args: [{
809
- isSignal: true,
810
- alias: "chromeSize",
811
- required: false
812
- }]
813
- }],
814
- animations: [{
815
- type: i0.Input,
816
- args: [{
817
- isSignal: true,
818
- alias: "animations",
819
- required: false
820
- }]
821
- }],
822
- minWidth: [{
823
- type: i0.Input,
824
- args: [{
825
- isSignal: true,
826
- alias: "minWidth",
827
- required: false
828
- }]
829
- }],
830
- minHeight: [{
831
- type: i0.Input,
832
- args: [{
833
- isSignal: true,
834
- alias: "minHeight",
835
- required: false
836
- }]
837
- }],
838
- maxWidth: [{
839
- type: i0.Input,
840
- args: [{
841
- isSignal: true,
842
- alias: "maxWidth",
843
- required: false
844
- }]
845
- }],
846
- maxHeight: [{
847
- type: i0.Input,
848
- args: [{
849
- isSignal: true,
850
- alias: "maxHeight",
851
- required: false
852
- }]
853
- }],
854
- movable: [{
855
- type: i0.Input,
856
- args: [{
857
- isSignal: true,
858
- alias: "movable",
859
- required: false
860
- }]
861
- }],
862
- resizable: [{
863
- type: i0.Input,
864
- args: [{
865
- isSignal: true,
866
- alias: "resizable",
867
- required: false
868
- }]
869
- }],
870
- keepInViewport: [{
871
- type: i0.Input,
872
- args: [{
873
- isSignal: true,
874
- alias: "keepInViewport",
875
- required: false
876
- }]
877
- }],
878
- snap: [{
584
+ "[style.left.px]": "rect().left",
585
+ "[style.top.px]": "rect().top",
586
+ "[style.width.px]": "rect().width",
587
+ "[style.height.px]": "rect().height",
588
+ "[style.z-index]": "model().z()",
589
+ "[attr.aria-labelledby]": "titleId",
590
+ "(pointerdown)": "onPointerDown($event)",
591
+ "(dblclick)": "onBodyDblClick($event)",
592
+ "(keydown)": "onKeydown($event)",
593
+ "(focusin)": "onFocusIn($event)",
594
+ "(focusout)": "onFocusOut($event)"
595
+ },
596
+ template: "<!--\n No pointerdown here: the host takes every press, so a `dragHandle` in the body\n can start a move as well as the title bar, and a press anywhere activates the\n window. The double-click is bound here and on the host; see `onChromeDblClick`.\n The action count is for the macOS chrome, which pads its title as wide as the\n cluster so the title centres on the whole bar.\n-->\n<div\n class=\"wr-window__chrome\"\n [style.--wr-window-chrome-action-count]=\"actionCount()\"\n (dblclick)=\"onChromeDblClick($event)\"\n>\n <div class=\"wr-window__title\" [attr.id]=\"titleId\">{{ displayTitle() }}</div>\n <div class=\"wr-window__title-extra\">\n @if (titleExtraTemplate(); as template) {\n <ng-container *ngTemplateOutlet=\"template; context: slotContext(); injector: injector()\" />\n } @else if (titleExtraComponent(); as component) {\n <ng-container *ngComponentOutlet=\"component; injector: injector()\" />\n }\n </div>\n\n <div class=\"wr-window__chrome-actions\">\n @if (resolvedOs() === 'macos') {\n <!--\n macOS traffic lights: close, minimize, zoom, in that order from the frame\n edge. The button is the hit target and the coloured dot is its\n `::before`, so the box can be big enough to press while the dot stays\n the platform's size. The glyphs sit on an 8px grid and appear only while\n the cluster is hovered or holds keyboard focus, as they do on macOS.\n Zoom draws the full-screen arrows of Big Sur and later: pointing out to\n maximize, pointing in to restore. Every straight edge of both sits on a\n whole pixel, so the filled triangles stay hard at 1x.\n -->\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"8\" height=\"8\" viewBox=\"0 0 8 8\" aria-hidden=\"true\">\n <path d=\"M1.5 1.5l5 5m0-5l-5 5\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"8\" height=\"8\" viewBox=\"0 0 8 8\" aria-hidden=\"true\">\n <path d=\"M1.5 3.5h5\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"8\" height=\"8\" viewBox=\"0 0 8 8\" aria-hidden=\"true\">\n @if (state() === 'maximized') {\n <path d=\"M3 3V.5L.5 3zM5 5v2.5L7.5 5z\" fill=\"currentColor\" />\n } @else {\n <path d=\"M1 1h4L1 5zm6 6H3l4-4z\" fill=\"currentColor\" />\n }\n </svg>\n </button>\n }\n } @else if (resolvedOs() === 'linux') {\n <!--\n GNOME (libadwaita) window controls: round buttons with Adwaita's 16px\n symbolic glyphs, 2px strokes on whole pixels. Restore is the SMALLER\n square, the way Adwaita draws it, not a second overlapping one. Close-only\n is the DEFAULT here, not a rule: `minimizeVisible()` resolves the input\n first, so `[showMinimize]=\"true\"` puts the button back, which is the only\n way a user on Linux can reach `<wr-window-taskbar>` at all.\n -->\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4 11h8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" />\n </svg>\n </button>\n }\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n @if (state() === 'maximized') {\n <rect x=\"6\" y=\"6\" width=\"4\" height=\"4\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" />\n } @else {\n <rect x=\"5\" y=\"5\" width=\"6\" height=\"6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" />\n }\n </svg>\n </button>\n }\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M5 5l6 6m0-6l-6 6\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" />\n </svg>\n </button>\n }\n } @else {\n <!--\n Windows 11 caption buttons: 10px glyphs in the full-height, 46px-wide\n buttons, drawn the way Segoe Fluent Icons draws them. 1px strokes on half\n pixels, so they land on whole device pixels at 1x and 2x, and the corners\n of maximize and restore are rounded by 1px.\n -->\n @if (minimizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--minimize\"\n [attr.aria-label]=\"minimizeActionLabel()\"\n (click)=\"minimize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M0 4.5h10\" fill=\"none\" stroke=\"currentColor\" />\n </svg>\n </button>\n }\n @if (maximizeVisible()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--maximize\"\n [attr.aria-label]=\"maximizeActionLabel()\"\n (click)=\"maximize()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n @if (state() === 'maximized') {\n <g fill=\"none\" stroke=\"currentColor\">\n <rect x=\".5\" y=\"2.5\" width=\"7\" height=\"7\" rx=\"1\" />\n <path d=\"M2.5 2.5v-1a1 1 0 0 1 1-1h5a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-1\" />\n </g>\n } @else {\n <rect x=\".5\" y=\".5\" width=\"9\" height=\"9\" rx=\"1\" fill=\"none\" stroke=\"currentColor\" />\n }\n </svg>\n </button>\n }\n @if (showClose()) {\n <button\n type=\"button\"\n class=\"wr-window__chrome-action wr-window__chrome-action--close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close()\"\n >\n <svg class=\"wr-window__chrome-action-glyph\" width=\"10\" height=\"10\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M.5.5l9 9m0-9l-9 9\" fill=\"none\" stroke=\"currentColor\" />\n </svg>\n </button>\n }\n }\n </div>\n</div>\n\n<div class=\"wr-window__body\">\n <ng-content />\n</div>\n\n<div class=\"wr-window__status-bar\">\n @if (statusBarTemplate(); as template) {\n <ng-container *ngTemplateOutlet=\"template; context: slotContext(); injector: injector()\" />\n } @else if (statusBarComponent(); as component) {\n <ng-container *ngComponentOutlet=\"component; injector: injector()\" />\n }\n</div>\n\n<!--\n Keyboard move and resize say what they did here: the mode on entry, the\n landing on Enter or Escape. Not each step — that would flood the region.\n-->\n<p class=\"wr-window__sr-only\" role=\"status\">{{ announcement() }}</p>\n\n@if (resizeHandles()) {\n <span class=\"wr-window__handle wr-window__handle--t\" (pointerdown)=\"resizeT($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--r\" (pointerdown)=\"resizeR($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--b\" (pointerdown)=\"resizeB($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--l\" (pointerdown)=\"resizeL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--tl\" (pointerdown)=\"resizeTL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--tr\" (pointerdown)=\"resizeTR($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--bl\" (pointerdown)=\"resizeBL($event)\"></span>\n <span class=\"wr-window__handle wr-window__handle--br\" (pointerdown)=\"resizeBR($event)\"></span>\n}\n"
597
+ }]
598
+ }],
599
+ ctorParameters: () => [],
600
+ propDecorators: {
601
+ model: [{
879
602
  type: i0.Input,
880
603
  args: [{
881
604
  isSignal: true,
882
- alias: "snap",
883
- required: false
605
+ alias: "model",
606
+ required: true
884
607
  }]
885
608
  }],
886
- dragHandle: [{
609
+ windowRef: [{
887
610
  type: i0.Input,
888
611
  args: [{
889
612
  isSignal: true,
890
- alias: "dragHandle",
613
+ alias: "windowRef",
891
614
  required: false
892
615
  }]
893
616
  }],
894
- showMinimize: [{
617
+ injector: [{
895
618
  type: i0.Input,
896
619
  args: [{
897
620
  isSignal: true,
898
- alias: "showMinimize",
621
+ alias: "injector",
899
622
  required: false
900
623
  }]
901
624
  }],
902
- showMaximize: [{
625
+ titleExtra: [{
903
626
  type: i0.Input,
904
627
  args: [{
905
628
  isSignal: true,
906
- alias: "showMaximize",
629
+ alias: "titleExtra",
907
630
  required: false
908
631
  }]
909
632
  }],
910
- showClose: [{
633
+ statusBar: [{
911
634
  type: i0.Input,
912
635
  args: [{
913
636
  isSignal: true,
914
- alias: "showClose",
637
+ alias: "statusBar",
915
638
  required: false
916
639
  }]
917
640
  }],
918
641
  closed: [{
919
642
  type: i0.Output,
920
643
  args: ["closed"]
921
- }],
922
- moved: [{
923
- type: i0.Output,
924
- args: ["moved"]
925
- }],
926
- resized: [{
927
- type: i0.Output,
928
- args: ["resized"]
929
- }],
930
- minimizedWithFocus: [{
931
- type: i0.Output,
932
- args: ["minimizedWithFocus"]
933
644
  }]
934
645
  }
935
646
  });
@@ -952,6 +663,11 @@ var WrWindowContainer = class WrWindowContainer {
952
663
  wrWindow = viewChild.required(WrWindow, ...ngDevMode ? [{ debugName: "wrWindow" }] : /* istanbul ignore next */ []);
953
664
  constructor() {
954
665
  afterNextRender(() => this.attach());
666
+ effect(() => {
667
+ const model = this.ref._model;
668
+ model.workArea();
669
+ untracked(() => model.clamp());
670
+ });
955
671
  this.destroyRef.onDestroy(() => {
956
672
  if (this.persistTimer !== null) clearTimeout(this.persistTimer);
957
673
  });
@@ -960,86 +676,64 @@ var WrWindowContainer = class WrWindowContainer {
960
676
  const componentRef = this.contentVcr().createComponent(this.componentType, { injector: this.childInjector });
961
677
  this.ref.componentRef = componentRef;
962
678
  const w = this.wrWindow();
963
- this.ref._doMinimize = () => w.minimize();
964
- this.ref._doMaximize = () => w.maximize();
965
- this.ref._doRestore = () => w.restore();
966
- this.ref._doFocus = () => w.focus();
967
- this.ref._doMoveTo = (x, y) => w.moveTo(x, y);
968
- this.ref._doResizeTo = (width, height) => w.resizeTo(width, height);
969
- this.ref._doCenter = () => w.center();
970
- this.ref._doSetTitle = (title) => this.ref._title.set(title);
971
- this.ref._doFocusInitial = () => afterNextRender(() => w.focusInitial(), { injector: this.hostInjector });
972
- this.destroyRef.onDestroy(() => this.ref._doFocusInitial = null);
973
- runInInjectionContext(this.hostInjector, () => {
974
- effect(() => this.ref._state.set(w.state()));
975
- effect(() => this.ref._x.set(w.x()));
976
- effect(() => this.ref._y.set(w.y()));
977
- effect(() => this.ref._width.set(w.width()));
978
- effect(() => this.ref._height.set(w.height()));
979
- effect(() => this.ref._rawX.set(w.x_()));
980
- effect(() => this.ref._rawY.set(w.y_()));
981
- effect(() => this.ref._rawWidth.set(w.width_()));
982
- effect(() => this.ref._rawHeight.set(w.height_()));
983
- effect(() => this.ref._z.set(w.z()));
984
- effect(() => {
985
- const host = this.ref._overlayRef?.hostElement;
986
- if (host) host.style.zIndex = String(w.z());
987
- });
679
+ const model = this.ref._model;
680
+ this.ref._focusWindow = (mode) => afterNextRender(() => {
681
+ if (mode === "initial") {
682
+ w.focusInitial();
683
+ return;
684
+ }
685
+ if (this.manager.active() === this.ref) w.focusLast();
686
+ }, { injector: this.hostInjector });
687
+ this.ref._startKeyboard = (mode) => {
688
+ if (model.state() !== "minimized") {
689
+ w.startKeyboard(mode);
690
+ return;
691
+ }
692
+ model.unminimize();
693
+ afterNextRender(() => w.startKeyboard(mode), { injector: this.hostInjector });
694
+ };
695
+ model.onMinimizedWithFocus = () => this.onMinimizedWithFocus();
696
+ this.destroyRef.onDestroy(() => {
697
+ this.ref._focusWindow = null;
698
+ this.ref._startKeyboard = null;
699
+ model.onMinimizedWithFocus = null;
988
700
  });
989
- this.ref._title.set(this.config.title ?? "");
990
- if (this.config.storage) {
991
- this.hydrateFromStorage(w);
992
- this.installAutosave(w);
993
- }
994
- const pending = this.ref.pendingStateOnMount;
701
+ if (this.config.storage) this.installAutosave();
702
+ if (this.autoFocus && model.state() !== "minimized") this.ref._focusWindow("initial");
703
+ const pending = this.ref._pendingKeyboard;
995
704
  if (pending) {
996
- this.ref.pendingStateOnMount = null;
997
- if (pending === "minimized") this.ref.minimize();
998
- else if (pending === "maximized") this.ref.maximize();
705
+ this.ref._pendingKeyboard = null;
706
+ this.ref._startKeyboard(pending);
999
707
  }
1000
- if (this.autoFocus && w.state() !== "minimized") this.ref._doFocusInitial();
1001
708
  }
1002
709
  onMinimizedWithFocus() {
1003
710
  afterNextRender(() => this.manager._focusAfterMinimize(this.ref), { injector: this.hostInjector });
1004
711
  }
1005
- hydrateFromStorage(w) {
1006
- const cfg = this.config.storage;
1007
- if (!cfg) return;
1008
- const saved = this.storage.get(storageKey(cfg));
1009
- if (!saved) return;
1010
- const persist = cfg.persist ?? "all";
1011
- if (persist !== "size") {
1012
- if (typeof saved.x === "number") w.x_.set(saved.x);
1013
- if (typeof saved.y === "number") w.y_.set(saved.y);
1014
- }
1015
- if (persist !== "position") {
1016
- if (typeof saved.width === "number") w.width_.set(saved.width);
1017
- if (typeof saved.height === "number") w.height_.set(saved.height);
1018
- }
1019
- }
1020
- installAutosave(w) {
712
+ installAutosave() {
1021
713
  const cfg = this.config.storage;
1022
714
  if (!cfg) return;
1023
715
  const persist = cfg.persist ?? "all";
1024
716
  const key = storageKey(cfg);
717
+ const model = this.ref._model;
1025
718
  runInInjectionContext(this.hostInjector, () => {
1026
719
  effect(() => {
1027
- const x = w.x_();
1028
- const y = w.y_();
1029
- const width = w.width_();
1030
- const height = w.height_();
720
+ const x = model.x();
721
+ const y = model.y();
722
+ const width = model.width();
723
+ const height = model.height();
1031
724
  if (this.persistTimer !== null) clearTimeout(this.persistTimer);
1032
725
  this.persistTimer = setTimeout(() => {
1033
726
  this.persistTimer = null;
1034
- const payload = {};
1035
- if (persist !== "size") {
1036
- payload.x = x;
1037
- payload.y = y;
1038
- }
1039
- if (persist !== "position") {
1040
- payload.width = width;
1041
- payload.height = height;
1042
- }
727
+ const payload = {
728
+ ...persist !== "size" ? {
729
+ x,
730
+ y
731
+ } : {},
732
+ ...persist !== "position" ? {
733
+ width,
734
+ height
735
+ } : {}
736
+ };
1043
737
  this.storage.set(key, payload);
1044
738
  }, PERSIST_DEBOUNCE_MS);
1045
739
  });
@@ -1047,7 +741,7 @@ var WrWindowContainer = class WrWindowContainer {
1047
741
  }
1048
742
  static ɵfac = i0.ɵɵngDeclareFactory({
1049
743
  minVersion: "12.0.0",
1050
- version: "22.2.3",
744
+ version: "22.2.4",
1051
745
  ngImport: i0,
1052
746
  type: WrWindowContainer,
1053
747
  deps: [],
@@ -1055,7 +749,7 @@ var WrWindowContainer = class WrWindowContainer {
1055
749
  });
1056
750
  static ɵcmp = i0.ɵɵngDeclareComponent({
1057
751
  minVersion: "17.0.0",
1058
- version: "22.2.3",
752
+ version: "22.2.4",
1059
753
  type: WrWindowContainer,
1060
754
  isStandalone: true,
1061
755
  selector: "wr-window-container",
@@ -1074,44 +768,19 @@ var WrWindowContainer = class WrWindowContainer {
1074
768
  isSignal: true
1075
769
  }],
1076
770
  ngImport: i0,
1077
- template: "<wr-window\n [open]=\"true\"\n [title]=\"ref.title()\"\n [os]=\"config.os ?? 'auto'\"\n [size]=\"config.size ?? 'md'\"\n [chromeSize]=\"config.chromeSize ?? 'md'\"\n [animations]=\"config.animations ?? true\"\n [initialX]=\"config.x ?? null\"\n [initialY]=\"config.y ?? null\"\n [initialWidth]=\"config.width ?? null\"\n [initialHeight]=\"config.height ?? null\"\n [minWidth]=\"config.minWidth ?? 220\"\n [minHeight]=\"config.minHeight ?? 140\"\n [maxWidth]=\"config.maxWidth ?? 1000000\"\n [maxHeight]=\"config.maxHeight ?? 1000000\"\n [movable]=\"config.movable ?? true\"\n [resizable]=\"config.resizable ?? true\"\n [keepInViewport]=\"config.keepInViewport ?? true\"\n [snap]=\"config.snap ?? 'none'\"\n [dragHandle]=\"config.dragHandle ?? null\"\n [showMinimize]=\"config.showMinimize ?? null\"\n [showMaximize]=\"config.showMaximize ?? null\"\n [showClose]=\"config.showClose ?? true\"\n (closed)=\"ref.close()\"\n (minimizedWithFocus)=\"onMinimizedWithFocus()\"\n #window\n>\n <ng-container #content />\n</wr-window>\n\n@if (window.snapTarget(); as target) {\n <div class=\"wr-window__snap-hint\" [attr.data-target]=\"target\" aria-hidden=\"true\"></div>\n}\n",
771
+ template: "<wr-window\n [model]=\"ref._model\"\n [windowRef]=\"ref\"\n [injector]=\"childInjector\"\n [titleExtra]=\"config.titleExtra ?? null\"\n [statusBar]=\"config.statusBar ?? null\"\n (closed)=\"ref.close()\"\n #window\n>\n <ng-container #content />\n</wr-window>\n\n<!--\n The snap hint lives here, not inside `<wr-window>`: `.wr-window` clips, and a\n fixed child is clipped with it while the open animation's transform is on.\n It takes the exact box the drop will give the window, from the same model\n arithmetic, so the hint and the landing cannot disagree — they did when the\n hint was a CSS 50% of the layout viewport and the snap was half of\n `innerWidth`, 7.5px apart with a classic scrollbar showing.\n-->\n@if (ref._model.previewRect(); as box) {\n <div\n class=\"wr-window__snap-hint\"\n [attr.data-target]=\"ref._model.dragTarget()\"\n [style.left.px]=\"box.left\"\n [style.top.px]=\"box.top\"\n [style.width.px]=\"box.width\"\n [style.height.px]=\"box.height\"\n [style.z-index]=\"ref._model.z() + 1\"\n aria-hidden=\"true\"\n ></div>\n}\n",
1078
772
  dependencies: [{
1079
773
  kind: "component",
1080
774
  type: WrWindow,
1081
775
  selector: "wr-window",
1082
776
  inputs: [
1083
- "open",
1084
- "state",
1085
- "title",
1086
- "initialX",
1087
- "initialY",
1088
- "size",
1089
- "initialWidth",
1090
- "initialHeight",
1091
- "os",
1092
- "chromeSize",
1093
- "animations",
1094
- "minWidth",
1095
- "minHeight",
1096
- "maxWidth",
1097
- "maxHeight",
1098
- "movable",
1099
- "resizable",
1100
- "keepInViewport",
1101
- "snap",
1102
- "dragHandle",
1103
- "showMinimize",
1104
- "showMaximize",
1105
- "showClose"
777
+ "model",
778
+ "windowRef",
779
+ "injector",
780
+ "titleExtra",
781
+ "statusBar"
1106
782
  ],
1107
- outputs: [
1108
- "openChange",
1109
- "stateChange",
1110
- "closed",
1111
- "moved",
1112
- "resized",
1113
- "minimizedWithFocus"
1114
- ]
783
+ outputs: ["closed"]
1115
784
  }],
1116
785
  changeDetection: i0.ChangeDetectionStrategy.OnPush,
1117
786
  encapsulation: i0.ViewEncapsulation.None
@@ -1119,7 +788,7 @@ var WrWindowContainer = class WrWindowContainer {
1119
788
  };
1120
789
  i0.ɵɵngDeclareClassMetadata({
1121
790
  minVersion: "12.0.0",
1122
- version: "22.2.3",
791
+ version: "22.2.4",
1123
792
  ngImport: i0,
1124
793
  type: WrWindowContainer,
1125
794
  decorators: [{
@@ -1129,7 +798,7 @@ i0.ɵɵngDeclareClassMetadata({
1129
798
  changeDetection: ChangeDetectionStrategy.OnPush,
1130
799
  encapsulation: ViewEncapsulation.None,
1131
800
  imports: [WrWindow],
1132
- template: "<wr-window\n [open]=\"true\"\n [title]=\"ref.title()\"\n [os]=\"config.os ?? 'auto'\"\n [size]=\"config.size ?? 'md'\"\n [chromeSize]=\"config.chromeSize ?? 'md'\"\n [animations]=\"config.animations ?? true\"\n [initialX]=\"config.x ?? null\"\n [initialY]=\"config.y ?? null\"\n [initialWidth]=\"config.width ?? null\"\n [initialHeight]=\"config.height ?? null\"\n [minWidth]=\"config.minWidth ?? 220\"\n [minHeight]=\"config.minHeight ?? 140\"\n [maxWidth]=\"config.maxWidth ?? 1000000\"\n [maxHeight]=\"config.maxHeight ?? 1000000\"\n [movable]=\"config.movable ?? true\"\n [resizable]=\"config.resizable ?? true\"\n [keepInViewport]=\"config.keepInViewport ?? true\"\n [snap]=\"config.snap ?? 'none'\"\n [dragHandle]=\"config.dragHandle ?? null\"\n [showMinimize]=\"config.showMinimize ?? null\"\n [showMaximize]=\"config.showMaximize ?? null\"\n [showClose]=\"config.showClose ?? true\"\n (closed)=\"ref.close()\"\n (minimizedWithFocus)=\"onMinimizedWithFocus()\"\n #window\n>\n <ng-container #content />\n</wr-window>\n\n@if (window.snapTarget(); as target) {\n <div class=\"wr-window__snap-hint\" [attr.data-target]=\"target\" aria-hidden=\"true\"></div>\n}\n"
801
+ template: "<wr-window\n [model]=\"ref._model\"\n [windowRef]=\"ref\"\n [injector]=\"childInjector\"\n [titleExtra]=\"config.titleExtra ?? null\"\n [statusBar]=\"config.statusBar ?? null\"\n (closed)=\"ref.close()\"\n #window\n>\n <ng-container #content />\n</wr-window>\n\n<!--\n The snap hint lives here, not inside `<wr-window>`: `.wr-window` clips, and a\n fixed child is clipped with it while the open animation's transform is on.\n It takes the exact box the drop will give the window, from the same model\n arithmetic, so the hint and the landing cannot disagree — they did when the\n hint was a CSS 50% of the layout viewport and the snap was half of\n `innerWidth`, 7.5px apart with a classic scrollbar showing.\n-->\n@if (ref._model.previewRect(); as box) {\n <div\n class=\"wr-window__snap-hint\"\n [attr.data-target]=\"ref._model.dragTarget()\"\n [style.left.px]=\"box.left\"\n [style.top.px]=\"box.top\"\n [style.width.px]=\"box.width\"\n [style.height.px]=\"box.height\"\n [style.z-index]=\"ref._model.z() + 1\"\n aria-hidden=\"true\"\n ></div>\n}\n"
1133
802
  }]
1134
803
  }],
1135
804
  ctorParameters: () => [],
@@ -1153,47 +822,444 @@ i0.ɵɵngDeclareClassMetadata({
1153
822
  * Use of this source code is governed by an MIT-style license that can be
1154
823
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
1155
824
  */
825
+ const MINIMIZED_HEIGHT = 40;
826
+ function num(value, fallback) {
827
+ return typeof value === "number" && !Number.isNaN(value) ? value : fallback;
828
+ }
829
+ const SIZE_PRESETS = {
830
+ sm: {
831
+ width: 320,
832
+ height: 200
833
+ },
834
+ md: {
835
+ width: 480,
836
+ height: 320
837
+ },
838
+ lg: {
839
+ width: 720,
840
+ height: 480
841
+ }
842
+ };
843
+ function resolveOptions(config) {
844
+ return {
845
+ os: config.os ?? "auto",
846
+ chromeSize: config.chromeSize ?? "md",
847
+ animations: config.animations ?? true,
848
+ minWidth: num(config.minWidth, 220),
849
+ minHeight: num(config.minHeight, 140),
850
+ maxWidth: num(config.maxWidth, Number.POSITIVE_INFINITY),
851
+ maxHeight: num(config.maxHeight, Number.POSITIVE_INFINITY),
852
+ movable: config.movable ?? true,
853
+ resizable: config.resizable ?? true,
854
+ keepInViewport: config.keepInViewport ?? true,
855
+ snap: config.snap ?? "none",
856
+ restoreOnDrag: config.restoreOnDrag ?? true,
857
+ dragHandle: config.dragHandle ?? null,
858
+ showMinimize: config.showMinimize ?? null,
859
+ showMaximize: config.showMaximize ?? null,
860
+ showClose: config.showClose ?? true,
861
+ closeOnEscape: config.closeOnEscape ?? true,
862
+ keyboard: config.keyboard ?? true,
863
+ keyboardStep: Math.max(1, num(config.keyboardStep, 10))
864
+ };
865
+ }
866
+ function readViewport() {
867
+ if (typeof window === "undefined" || typeof document === "undefined") return {
868
+ width: 1024,
869
+ height: 768
870
+ };
871
+ const root = document.documentElement;
872
+ return {
873
+ width: root.clientWidth || window.innerWidth,
874
+ height: root.clientHeight || window.innerHeight
875
+ };
876
+ }
877
+ function cascadeOffset(index) {
878
+ const step = 50 + index % 10 * 30;
879
+ return {
880
+ x: step + Math.floor(index / 10) % 5 * 15,
881
+ y: step
882
+ };
883
+ }
884
+ function seedGeometry(seed) {
885
+ const { options: o, workArea: wa } = seed;
886
+ const preset = SIZE_PRESETS[seed.size ?? "md"];
887
+ const keep = o.keepInViewport;
888
+ const fit = (size, room) => keep ? Math.min(size, room) : size;
889
+ const width = Math.round(clamp(fit(seed.width ?? preset.width, wa.width), o.minWidth, o.maxWidth));
890
+ const height = Math.round(clamp(fit(seed.height ?? preset.height, wa.height), o.minHeight, o.maxHeight));
891
+ let x = seed.x;
892
+ let y = seed.y;
893
+ let cascaded = false;
894
+ if (x == null || y == null) {
895
+ const offset = seed.cascade();
896
+ cascaded = true;
897
+ x ??= seed.rtl ? wa.left + wa.width - width - offset.x : wa.left + offset.x;
898
+ y ??= wa.top + offset.y;
899
+ }
900
+ const into = cascaded || !!seed.restored;
901
+ if (keep && (into || seed.x == null)) x = clamp(x, wa.left, Math.max(wa.left, wa.left + wa.width - width));
902
+ if (keep && (into || seed.y == null)) y = clamp(y, wa.top, Math.max(wa.top, wa.top + wa.height - height));
903
+ return {
904
+ x: Math.round(x),
905
+ y: Math.round(y),
906
+ width,
907
+ height
908
+ };
909
+ }
910
+ var WrWindowModel = class {
911
+ options;
912
+ title;
913
+ state;
914
+ snap;
915
+ x;
916
+ y;
917
+ width;
918
+ height;
919
+ z;
920
+ isActive = signal(false, ...ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []);
921
+ keyboardMode = signal(null, ...ngDevMode ? [{ debugName: "keyboardMode" }] : /* istanbul ignore next */ []);
922
+ dragTarget = signal(null, ...ngDevMode ? [{ debugName: "dragTarget" }] : /* istanbul ignore next */ []);
923
+ workArea;
924
+ moved = new Subject();
925
+ resized = new Subject();
926
+ stateChanges = new Subject();
927
+ element = null;
928
+ onMinimizedWithFocus = null;
929
+ restoreTo = "normal";
930
+ keyboardStart = null;
931
+ keyboardOrigin = null;
932
+ rect = computed(() => {
933
+ const state = this.state();
934
+ if (state === "maximized") return this.maximizedRect();
935
+ const raw = {
936
+ left: this.x(),
937
+ top: this.y(),
938
+ width: this.width(),
939
+ height: this.height()
940
+ };
941
+ if (state === "minimized") return {
942
+ ...raw,
943
+ height: MINIMIZED_HEIGHT
944
+ };
945
+ const snap = this.snap();
946
+ return snap ? this.snapRect(snap) : raw;
947
+ }, ...ngDevMode ? [{ debugName: "rect" }] : /* istanbul ignore next */ []);
948
+ previewRect = computed(() => {
949
+ const target = this.dragTarget();
950
+ return target ? this.targetRect(target) : null;
951
+ }, ...ngDevMode ? [{ debugName: "previewRect" }] : /* istanbul ignore next */ []);
952
+ constructor(init) {
953
+ this.options = signal(init.options, ...ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []);
954
+ this.title = signal(init.title ?? "", ...ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []);
955
+ this.state = signal(init.state ?? "normal", ...ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []);
956
+ this.snap = signal(init.snap && init.snap !== "top" ? init.snap : null, ...ngDevMode ? [{ debugName: "snap" }] : /* istanbul ignore next */ []);
957
+ const g = init.geometry ?? {
958
+ x: 0,
959
+ y: 0,
960
+ width: SIZE_PRESETS.md.width,
961
+ height: SIZE_PRESETS.md.height
962
+ };
963
+ this.x = signal(g.x, ...ngDevMode ? [{ debugName: "x" }] : /* istanbul ignore next */ []);
964
+ this.y = signal(g.y, ...ngDevMode ? [{ debugName: "y" }] : /* istanbul ignore next */ []);
965
+ this.width = signal(g.width, ...ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []);
966
+ this.height = signal(g.height, ...ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []);
967
+ this.z = signal(init.z ?? 0, ...ngDevMode ? [{ debugName: "z" }] : /* istanbul ignore next */ []);
968
+ this.workArea = init.workArea;
969
+ this.clamp();
970
+ }
971
+ setState(next) {
972
+ const current = this.state();
973
+ if (current === next) return;
974
+ if (next === "minimized") this.restoreTo = current === "maximized" ? "maximized" : "normal";
975
+ this.state.set(next);
976
+ this.stateChanges.next(next);
977
+ }
978
+ minimize() {
979
+ if (this.state() === "minimized") {
980
+ this.unminimize();
981
+ return;
982
+ }
983
+ const hadFocus = this.hasFocus();
984
+ this.setState("minimized");
985
+ if (hadFocus) this.onMinimizedWithFocus?.();
986
+ }
987
+ unminimize() {
988
+ if (this.state() === "minimized") this.setState(this.restoreTo);
989
+ }
990
+ maximize() {
991
+ this.setState(this.state() === "maximized" ? "normal" : "maximized");
992
+ }
993
+ restore() {
994
+ this.snap.set(null);
995
+ this.setState("normal");
996
+ }
997
+ snapTo(target) {
998
+ if (target === "top") {
999
+ this.setState("maximized");
1000
+ return;
1001
+ }
1002
+ this.snap.set(target);
1003
+ this.setState("normal");
1004
+ }
1005
+ setRaw(g) {
1006
+ if (g.x !== void 0) this.x.set(Math.round(g.x));
1007
+ if (g.y !== void 0) this.y.set(Math.round(g.y));
1008
+ if (g.width !== void 0) this.width.set(Math.round(g.width));
1009
+ if (g.height !== void 0) this.height.set(Math.round(g.height));
1010
+ }
1011
+ unsnapInPlace() {
1012
+ if (!this.snap()) return;
1013
+ if (this.state() === "normal") {
1014
+ const r = this.rect();
1015
+ this.setRaw({
1016
+ x: r.left,
1017
+ y: r.top,
1018
+ width: r.width,
1019
+ height: r.height
1020
+ });
1021
+ }
1022
+ this.snap.set(null);
1023
+ }
1024
+ moveTo(x, y) {
1025
+ this.unsnapInPlace();
1026
+ this.setRaw({
1027
+ x,
1028
+ y
1029
+ });
1030
+ this.clamp();
1031
+ }
1032
+ resizeTo(width, height) {
1033
+ this.unsnapInPlace();
1034
+ const o = this.options();
1035
+ this.setRaw({
1036
+ width: clamp(width, o.minWidth, o.maxWidth),
1037
+ height: clamp(height, o.minHeight, o.maxHeight)
1038
+ });
1039
+ this.clamp();
1040
+ }
1041
+ center() {
1042
+ this.unsnapInPlace();
1043
+ const wa = this.workArea();
1044
+ this.setRaw({
1045
+ x: wa.left + Math.max(0, (wa.width - this.width()) / 2),
1046
+ y: wa.top + Math.max(0, (wa.height - this.height()) / 2)
1047
+ });
1048
+ }
1049
+ clamp() {
1050
+ if (!this.options().keepInViewport) return;
1051
+ const wa = this.workArea();
1052
+ const w = this.width();
1053
+ this.x.set(Math.round(clamp(this.x(), wa.left + 24 - w, wa.left + wa.width - 24)));
1054
+ this.y.set(Math.round(clamp(this.y(), wa.top, wa.top + wa.height - 24)));
1055
+ }
1056
+ update(patch) {
1057
+ const { title, ...rest } = patch;
1058
+ if (title !== void 0) this.title.set(title);
1059
+ const defined = {};
1060
+ for (const [key, value] of Object.entries(rest)) if (value !== void 0) defined[key] = value;
1061
+ if (Object.keys(defined).length === 0) return;
1062
+ this.options.update((o) => ({
1063
+ ...o,
1064
+ ...defined
1065
+ }));
1066
+ if ([
1067
+ "minWidth",
1068
+ "minHeight",
1069
+ "maxWidth",
1070
+ "maxHeight"
1071
+ ].some((key) => key in defined)) {
1072
+ const o = this.options();
1073
+ this.setRaw({
1074
+ width: clamp(this.width(), o.minWidth, o.maxWidth),
1075
+ height: clamp(this.height(), o.minHeight, o.maxHeight)
1076
+ });
1077
+ this.clamp();
1078
+ }
1079
+ }
1080
+ maximizedRect() {
1081
+ const wa = this.workArea();
1082
+ const o = this.options();
1083
+ const width = Math.min(wa.width, o.maxWidth);
1084
+ const height = Math.min(wa.height, o.maxHeight);
1085
+ return {
1086
+ left: wa.left + Math.round((wa.width - width) / 2),
1087
+ top: wa.top + Math.round((wa.height - height) / 2),
1088
+ width,
1089
+ height
1090
+ };
1091
+ }
1092
+ snapRect(target) {
1093
+ const wa = this.workArea();
1094
+ const o = this.options();
1095
+ const halfW = Math.floor(wa.width / 2);
1096
+ const halfH = Math.floor(wa.height / 2);
1097
+ const right = target === "right" || target === "top-right" || target === "bottom-right";
1098
+ const bottom = target === "bottom-left" || target === "bottom-right";
1099
+ const quarter = target !== "left" && target !== "right";
1100
+ const regionW = right ? wa.width - halfW : halfW;
1101
+ const regionH = quarter ? bottom ? wa.height - halfH : halfH : wa.height;
1102
+ const regionL = wa.left + (right ? halfW : 0);
1103
+ const regionT = wa.top + (bottom ? halfH : 0);
1104
+ const width = clamp(regionW, o.minWidth, o.maxWidth);
1105
+ const height = clamp(regionH, o.minHeight, o.maxHeight);
1106
+ const left = right ? regionL + regionW - width : regionL;
1107
+ const top = bottom ? regionT + regionH - height : regionT;
1108
+ return {
1109
+ left: clamp(left, wa.left, Math.max(wa.left, wa.left + wa.width - width)),
1110
+ top: clamp(top, wa.top, Math.max(wa.top, wa.top + wa.height - height)),
1111
+ width,
1112
+ height
1113
+ };
1114
+ }
1115
+ targetRect(target) {
1116
+ return target === "top" ? this.maximizedRect() : this.snapRect(target);
1117
+ }
1118
+ startKeyboard(mode) {
1119
+ const o = this.options();
1120
+ if (!o.keyboard || this.state() === "minimized") return false;
1121
+ if (mode === "move" ? !o.movable : !o.resizable) return false;
1122
+ this.keyboardOrigin = {
1123
+ geometry: {
1124
+ x: this.x(),
1125
+ y: this.y(),
1126
+ width: this.width(),
1127
+ height: this.height()
1128
+ },
1129
+ state: this.state(),
1130
+ snap: this.snap()
1131
+ };
1132
+ if (this.state() === "maximized") this.setState("normal");
1133
+ this.unsnapInPlace();
1134
+ this.keyboardStart = {
1135
+ x: this.x(),
1136
+ y: this.y(),
1137
+ width: this.width(),
1138
+ height: this.height()
1139
+ };
1140
+ this.keyboardMode.set(mode);
1141
+ return true;
1142
+ }
1143
+ nudge(dx, dy) {
1144
+ this.setRaw({
1145
+ x: this.x() + dx,
1146
+ y: this.y() + dy
1147
+ });
1148
+ this.clamp();
1149
+ }
1150
+ grow(dw, dh, rtl) {
1151
+ const o = this.options();
1152
+ const width = clamp(this.width() + dw, o.minWidth, o.maxWidth);
1153
+ const height = clamp(this.height() + dh, o.minHeight, o.maxHeight);
1154
+ const x = rtl ? this.x() - (width - this.width()) : this.x();
1155
+ this.setRaw({
1156
+ x,
1157
+ width,
1158
+ height
1159
+ });
1160
+ this.clamp();
1161
+ }
1162
+ commitKeyboard() {
1163
+ const mode = this.keyboardMode();
1164
+ if (!mode) return null;
1165
+ this.keyboardMode.set(null);
1166
+ const start = this.keyboardStart;
1167
+ this.keyboardStart = null;
1168
+ this.keyboardOrigin = null;
1169
+ if (mode === "move" && (start?.x !== this.x() || start?.y !== this.y())) this.moved.next({
1170
+ x: this.x(),
1171
+ y: this.y()
1172
+ });
1173
+ if (mode === "resize" && (start?.width !== this.width() || start?.height !== this.height())) this.resized.next({
1174
+ width: this.width(),
1175
+ height: this.height()
1176
+ });
1177
+ return mode;
1178
+ }
1179
+ cancelKeyboard() {
1180
+ const mode = this.keyboardMode();
1181
+ if (!mode) return null;
1182
+ const origin = this.keyboardOrigin;
1183
+ this.keyboardStart = null;
1184
+ this.keyboardOrigin = null;
1185
+ this.keyboardMode.set(null);
1186
+ if (origin) {
1187
+ this.setRaw(origin.geometry);
1188
+ this.snap.set(origin.snap);
1189
+ this.setState(origin.state);
1190
+ }
1191
+ return mode;
1192
+ }
1193
+ hasFocus() {
1194
+ const el = this.element;
1195
+ return !!el && el.contains(el.ownerDocument.activeElement);
1196
+ }
1197
+ complete() {
1198
+ this.moved.complete();
1199
+ this.resized.complete();
1200
+ this.stateChanges.complete();
1201
+ }
1202
+ };
1203
+ /**
1204
+ * @license
1205
+ *
1206
+ * Use of this source code is governed by an MIT-style license that can be
1207
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
1208
+ */
1156
1209
  var WrWindowRef = class {
1157
1210
  _overlayRef;
1158
1211
  id;
1159
1212
  _closed = new ReplaySubject(1);
1160
- _stateChanged = new Subject();
1213
+ _model;
1214
+ _stateChanged;
1161
1215
  componentRef = null;
1162
1216
  taskbarVisible = true;
1163
- pendingStateOnMount = null;
1164
- _state = signal("normal", ...ngDevMode ? [{ debugName: "_state" }] : /* istanbul ignore next */ []);
1165
- _x = signal(0, ...ngDevMode ? [{ debugName: "_x" }] : /* istanbul ignore next */ []);
1166
- _y = signal(0, ...ngDevMode ? [{ debugName: "_y" }] : /* istanbul ignore next */ []);
1167
- _width = signal(0, ...ngDevMode ? [{ debugName: "_width" }] : /* istanbul ignore next */ []);
1168
- _height = signal(0, ...ngDevMode ? [{ debugName: "_height" }] : /* istanbul ignore next */ []);
1169
- _z = signal(0, ...ngDevMode ? [{ debugName: "_z" }] : /* istanbul ignore next */ []);
1170
- _rawX = signal(0, ...ngDevMode ? [{ debugName: "_rawX" }] : /* istanbul ignore next */ []);
1171
- _rawY = signal(0, ...ngDevMode ? [{ debugName: "_rawY" }] : /* istanbul ignore next */ []);
1172
- _rawWidth = signal(0, ...ngDevMode ? [{ debugName: "_rawWidth" }] : /* istanbul ignore next */ []);
1173
- _rawHeight = signal(0, ...ngDevMode ? [{ debugName: "_rawHeight" }] : /* istanbul ignore next */ []);
1174
- _title = signal("", ...ngDevMode ? [{ debugName: "_title" }] : /* istanbul ignore next */ []);
1175
- state = this._state.asReadonly();
1176
- x = this._x.asReadonly();
1177
- y = this._y.asReadonly();
1178
- width = this._width.asReadonly();
1179
- height = this._height.asReadonly();
1180
- z = this._z.asReadonly();
1181
- title = this._title.asReadonly();
1182
- _beforeCloseHook = null;
1217
+ _host = null;
1183
1218
  _doClose = null;
1184
- _doMinimize = null;
1185
- _doMaximize = null;
1186
- _doRestore = null;
1187
- _doFocus = null;
1188
- _doMoveTo = null;
1189
- _doResizeTo = null;
1190
- _doCenter = null;
1191
- _doSetTitle = null;
1192
- _doFocusInitial = null;
1219
+ _focusWindow = null;
1220
+ _startKeyboard = null;
1221
+ _pendingKeyboard = null;
1193
1222
  _returnFocus = null;
1194
- constructor(id, _overlayRef) {
1223
+ state;
1224
+ x;
1225
+ y;
1226
+ width;
1227
+ height;
1228
+ z;
1229
+ title;
1230
+ snap;
1231
+ isActive;
1232
+ closed;
1233
+ moved;
1234
+ resized;
1235
+ stateChanges;
1236
+ _beforeCloseHook = null;
1237
+ constructor(id, _overlayRef, model) {
1195
1238
  this._overlayRef = _overlayRef;
1196
1239
  this.id = id;
1240
+ const m = model ?? new WrWindowModel({
1241
+ options: resolveOptions({}),
1242
+ workArea: signal({
1243
+ left: 0,
1244
+ top: 0,
1245
+ ...readViewport()
1246
+ })
1247
+ });
1248
+ this._model = m;
1249
+ this._stateChanged = m.stateChanges;
1250
+ this.state = m.state.asReadonly();
1251
+ this.x = computed(() => m.rect().left, ...ngDevMode ? [{ debugName: "x" }] : /* istanbul ignore next */ []);
1252
+ this.y = computed(() => m.rect().top, ...ngDevMode ? [{ debugName: "y" }] : /* istanbul ignore next */ []);
1253
+ this.width = computed(() => m.rect().width, ...ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []);
1254
+ this.height = computed(() => m.rect().height, ...ngDevMode ? [{ debugName: "height" }] : /* istanbul ignore next */ []);
1255
+ this.z = m.z.asReadonly();
1256
+ this.title = m.title.asReadonly();
1257
+ this.snap = m.snap.asReadonly();
1258
+ this.isActive = m.isActive.asReadonly();
1259
+ this.closed = this._closed.asObservable();
1260
+ this.moved = m.moved.asObservable();
1261
+ this.resized = m.resized.asObservable();
1262
+ this.stateChanges = m.stateChanges.asObservable();
1197
1263
  }
1198
1264
  get componentInstance() {
1199
1265
  if (!this.componentRef) throw new Error("WrWindowRef: component not yet attached");
@@ -1212,28 +1278,48 @@ var WrWindowRef = class {
1212
1278
  if (proceed) this._doClose?.(result);
1213
1279
  }
1214
1280
  minimize() {
1215
- this._doMinimize?.();
1281
+ this._model.minimize();
1216
1282
  }
1217
1283
  maximize() {
1218
- this._doMaximize?.();
1284
+ this._model.maximize();
1219
1285
  }
1220
1286
  restore() {
1221
- this._doRestore?.();
1287
+ this._model.restore();
1222
1288
  }
1223
1289
  focus() {
1224
- this._doFocus?.();
1290
+ this._host?._raise(this);
1291
+ }
1292
+ activate() {
1293
+ if (this._host) this._host._activate(this);
1294
+ else this._model.unminimize();
1225
1295
  }
1226
1296
  moveTo(x, y) {
1227
- this._doMoveTo?.(x, y);
1297
+ this._model.moveTo(x, y);
1228
1298
  }
1229
1299
  resizeTo(width, height) {
1230
- this._doResizeTo?.(width, height);
1300
+ this._model.resizeTo(width, height);
1231
1301
  }
1232
1302
  center() {
1233
- this._doCenter?.();
1303
+ this._model.center();
1304
+ }
1305
+ snapTo(target) {
1306
+ this._model.snapTo(target);
1234
1307
  }
1235
1308
  setTitle(title) {
1236
- this._doSetTitle?.(title);
1309
+ this._model.title.set(title);
1310
+ }
1311
+ update(patch) {
1312
+ this._model.update(patch);
1313
+ }
1314
+ startMove() {
1315
+ this.startKeyboard("move");
1316
+ }
1317
+ startResize() {
1318
+ this.startKeyboard("resize");
1319
+ }
1320
+ startKeyboard(mode) {
1321
+ if (this._startKeyboard) this._startKeyboard(mode);
1322
+ else this._pendingKeyboard = mode;
1237
1323
  }
1238
1324
  beforeClose(hook) {
1239
1325
  this._beforeCloseHook = hook;
@@ -1246,6 +1332,9 @@ var WrWindowRef = class {
1246
1332
  });
1247
1333
  });
1248
1334
  }
1335
+ awaitClose() {
1336
+ return this.afterClosed();
1337
+ }
1249
1338
  get overlayRef() {
1250
1339
  return this._overlayRef;
1251
1340
  }
@@ -1256,58 +1345,111 @@ var WrWindowRef = class {
1256
1345
  * Use of this source code is governed by an MIT-style license that can be
1257
1346
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
1258
1347
  */
1348
+ const NO_RESERVE$1 = {
1349
+ top: 0,
1350
+ bottom: 0
1351
+ };
1352
+ function sameRect(a, b) {
1353
+ return a.left === b.left && a.top === b.top && a.width === b.width && a.height === b.height;
1354
+ }
1259
1355
  var WrWindowManager = class WrWindowManager {
1260
1356
  overlay = inject(WR_OVERLAY);
1261
1357
  parentInjector = inject(EnvironmentInjector);
1262
1358
  storage = inject(WrStorage);
1263
1359
  isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
1264
1360
  dir = inject(Directionality, { optional: true });
1361
+ defaults = inject(WR_WINDOW_DEFAULTS, { optional: true });
1362
+ destroyRef = inject(DestroyRef);
1265
1363
  baseZ = 1e3;
1266
1364
  topZ = this.baseZ;
1267
1365
  openCount = 0;
1268
1366
  _windows = signal([], ...ngDevMode ? [{ debugName: "_windows" }] : /* istanbul ignore next */ []);
1367
+ _active = signal(null, ...ngDevMode ? [{ debugName: "_active" }] : /* istanbul ignore next */ []);
1269
1368
  pendingRestores = /* @__PURE__ */ new Map();
1270
- taskbars = /* @__PURE__ */ new Set();
1369
+ taskbars = signal([], ...ngDevMode ? [{ debugName: "taskbars" }] : /* istanbul ignore next */ []);
1370
+ viewport = signal(readViewport(), {
1371
+ ...ngDevMode ? { debugName: "viewport" } : /* istanbul ignore next */ {},
1372
+ equal: (a, b) => a.width === b.width && a.height === b.height
1373
+ });
1374
+ layoutTick = signal(0, ...ngDevMode ? [{ debugName: "layoutTick" }] : /* istanbul ignore next */ []);
1375
+ listening = false;
1376
+ boundsObserver = null;
1377
+ boundElements = /* @__PURE__ */ new Map();
1378
+ bumpLayout = () => this.layoutTick.update((n) => n + 1);
1379
+ reserve = computed(() => {
1380
+ let top = 0;
1381
+ let bottom = 0;
1382
+ for (const taskbar of this.taskbars()) {
1383
+ const r = taskbar.reserve();
1384
+ top = Math.max(top, r.top);
1385
+ bottom = Math.max(bottom, r.bottom);
1386
+ }
1387
+ return top === 0 && bottom === 0 ? NO_RESERVE$1 : {
1388
+ top,
1389
+ bottom
1390
+ };
1391
+ }, ...ngDevMode ? [{ debugName: "reserve" }] : /* istanbul ignore next */ []);
1271
1392
  windows = this._windows.asReadonly();
1393
+ active = this._active.asReadonly();
1272
1394
  minimized = computed(() => this._windows().filter((w) => w.taskbarVisible && w.state() === "minimized"), ...ngDevMode ? [{ debugName: "minimized" }] : /* istanbul ignore next */ []);
1273
1395
  bringToFront() {
1274
1396
  this.topZ += 1;
1275
1397
  return this.topZ;
1276
1398
  }
1277
1399
  nextStartOffset() {
1278
- const offset = this.openCount % 10 * 30;
1400
+ const offset = cascadeOffset(this.openCount);
1279
1401
  this.openCount += 1;
1280
- return {
1281
- x: 50 + offset,
1282
- y: 50 + offset
1283
- };
1402
+ return offset;
1284
1403
  }
1285
1404
  open(component, config = {}) {
1286
1405
  const opener = this.focusedElement();
1287
1406
  if (config.id) {
1288
1407
  const existing = this.findById(config.id);
1289
1408
  if (existing) {
1290
- existing.restore();
1291
- existing.focus();
1292
1409
  if (config.autoFocus !== false) {
1293
1410
  if (opener && !existing._overlayRef.overlayElement?.contains(opener)) existing._returnFocus = opener;
1294
- existing._doFocusInitial?.();
1411
+ this._activate(existing);
1412
+ } else {
1413
+ existing._model.unminimize();
1414
+ this._raise(existing);
1295
1415
  }
1296
1416
  return existing;
1297
1417
  }
1298
1418
  }
1299
1419
  const pending = config.id ? this.pendingRestores.get(config.id) : void 0;
1300
- if (pending) {
1301
- this.pendingRestores.delete(config.id);
1302
- config = {
1303
- ...config,
1304
- x: pending.x,
1305
- y: pending.y,
1306
- width: pending.width,
1307
- height: pending.height,
1308
- title: pending.title || config.title
1309
- };
1310
- }
1420
+ if (pending) this.pendingRestores.delete(config.id);
1421
+ this.listen();
1422
+ const options = resolveOptions(config);
1423
+ const bounds = this.boundsElement(config.bounds ?? this.defaults?.bounds);
1424
+ const workArea = this.workAreaFor(config.bounds ?? this.defaults?.bounds, bounds);
1425
+ const releaseBounds = bounds ? this.observeBounds(bounds) : null;
1426
+ const saved = !pending && config.storage ? this.storage.get(storageKey(config.storage)) : null;
1427
+ const persist = config.storage?.persist ?? "all";
1428
+ const isNum = (v) => typeof v === "number" && !Number.isNaN(v);
1429
+ const savedPos = saved && persist !== "size" && isNum(saved.x) && isNum(saved.y) ? saved : null;
1430
+ const savedSize = saved && persist !== "position" && isNum(saved.width) && isNum(saved.height) ? saved : null;
1431
+ const geometry = untracked(() => seedGeometry({
1432
+ options,
1433
+ workArea: workArea(),
1434
+ size: config.size,
1435
+ x: pending?.x ?? savedPos?.x ?? config.x,
1436
+ y: pending?.y ?? savedPos?.y ?? config.y,
1437
+ width: pending?.width ?? savedSize?.width ?? config.width,
1438
+ height: pending?.height ?? savedSize?.height ?? config.height,
1439
+ restored: !!pending || !!savedPos,
1440
+ cascade: () => this.nextStartOffset(),
1441
+ rtl: this.dir?.value === "rtl"
1442
+ }));
1443
+ const taskbarOptIn = config.taskbar !== false;
1444
+ const model = new WrWindowModel({
1445
+ options,
1446
+ title: [pending?.title, config.title].find((t) => !!t) ?? "",
1447
+ geometry,
1448
+ state: pending?.state,
1449
+ snap: pending?.snap ?? null,
1450
+ z: this.bringToFront(),
1451
+ workArea
1452
+ });
1311
1453
  let self = null;
1312
1454
  const overlayRef = this.overlay.create({
1313
1455
  positionStrategy: this.overlay.position().global(),
@@ -1316,10 +1458,16 @@ var WrWindowManager = class WrWindowManager {
1316
1458
  eventPredicate: () => self === null || this.ownsKeyboard(self),
1317
1459
  usePopover: false
1318
1460
  });
1461
+ overlayRef.hostElement.style.zIndex = String(model.z());
1319
1462
  wrFollowDirection(overlayRef, this.dir, this.parentInjector);
1320
- const ref = new WrWindowRef(config.id ?? randomId("wr-window"), overlayRef);
1321
- self = ref;
1322
- ref.taskbarVisible = config.taskbar !== false;
1463
+ const ref = new WrWindowRef(config.id ?? randomId("wr-window"), overlayRef, model);
1464
+ const any = ref;
1465
+ self = any;
1466
+ ref._host = this;
1467
+ ref.taskbarVisible = taskbarOptIn;
1468
+ model.stateChanges.subscribe((state) => {
1469
+ if (state === "minimized" && this._active() === any) this.setActive(null);
1470
+ });
1323
1471
  const childInjector = Injector.create({
1324
1472
  parent: this.parentInjector,
1325
1473
  providers: [{
@@ -1338,47 +1486,172 @@ var WrWindowManager = class WrWindowManager {
1338
1486
  container.childInjector = childInjector;
1339
1487
  container.autoFocus = config.autoFocus ?? !pending;
1340
1488
  ref._returnFocus = opener;
1341
- if (config.closeOnEscape !== false) overlayRef.keydownEvents().subscribe((event) => {
1342
- if (event.key === "Escape") {
1343
- event.preventDefault();
1344
- ref.close();
1345
- }
1489
+ overlayRef.keydownEvents().subscribe((event) => {
1490
+ if (event.key !== "Escape" || event.defaultPrevented || !model.options().closeOnEscape) return;
1491
+ event.preventDefault();
1492
+ ref.close();
1346
1493
  });
1347
1494
  ref._doClose = (result) => {
1348
- const back = this.holdsFocus(ref) ? ref._returnFocus : null;
1495
+ const back = this.holdsFocus(any) ? ref._returnFocus : null;
1349
1496
  ref._returnFocus = null;
1350
1497
  overlayRef.dispose();
1498
+ releaseBounds?.();
1351
1499
  ref._closed.next(result);
1352
1500
  ref._closed.complete();
1353
- this._windows.update((list) => list.filter((r) => r !== ref));
1501
+ model.complete();
1502
+ this._windows.update((list) => list.filter((r) => r !== any));
1503
+ if (this._active() === any) this.setActive(null);
1354
1504
  if (back?.isConnected) back.focus();
1355
1505
  };
1356
- this._windows.update((list) => [...list, ref]);
1357
- if (pending && pending.state !== "normal") ref.pendingStateOnMount = pending.state;
1506
+ this._windows.update((list) => [...list, any]);
1358
1507
  return ref;
1359
1508
  }
1509
+ _activate(ref, options = {}) {
1510
+ if (!this._windows().includes(ref)) return;
1511
+ ref._model.unminimize();
1512
+ this._raise(ref);
1513
+ this.setActive(ref);
1514
+ if (options.focus !== false && !ref._model.hasFocus()) ref._focusWindow?.("last");
1515
+ }
1516
+ _raise(ref) {
1517
+ if (ref.z() >= this.topZ) return;
1518
+ const z = this.bringToFront();
1519
+ ref._model.z.set(z);
1520
+ const host = ref._overlayRef.hostElement;
1521
+ if (host) host.style.zIndex = String(z);
1522
+ }
1523
+ setActive(ref) {
1524
+ const previous = this._active();
1525
+ if (previous === ref) return;
1526
+ previous?._model.isActive.set(false);
1527
+ ref?._model.isActive.set(true);
1528
+ this._active.set(ref);
1529
+ }
1530
+ onFocusIn = (event) => {
1531
+ const target = event.target;
1532
+ if (!(target instanceof Node)) return;
1533
+ const ref = this._windows().find((w) => w._overlayRef.overlayElement?.contains(target));
1534
+ if (ref) {
1535
+ this._raise(ref);
1536
+ this.setActive(ref);
1537
+ return;
1538
+ }
1539
+ if (this.taskbars().some((t) => t.contains(target))) return;
1540
+ if (target instanceof Element && target.closest(".wr-overlay-container, .cdk-overlay-container")) return;
1541
+ this.setActive(null);
1542
+ };
1543
+ listen() {
1544
+ if (this.listening || !this.isBrowser) return;
1545
+ this.listening = true;
1546
+ const measure = () => this.viewport.set(readViewport());
1547
+ measure();
1548
+ window.addEventListener("resize", measure, { passive: true });
1549
+ document.addEventListener("focusin", this.onFocusIn, true);
1550
+ this.destroyRef.onDestroy(() => {
1551
+ window.removeEventListener("resize", measure);
1552
+ document.removeEventListener("focusin", this.onFocusIn, true);
1553
+ document.removeEventListener("scroll", this.bumpLayout, true);
1554
+ this.boundsObserver?.disconnect();
1555
+ });
1556
+ }
1557
+ boundsElement(bounds) {
1558
+ if (!bounds || bounds === "viewport") return null;
1559
+ if (bounds instanceof ElementRef) return bounds.nativeElement;
1560
+ return typeof bounds.getBoundingClientRect === "function" ? bounds : null;
1561
+ }
1562
+ workAreaFor(bounds, element) {
1563
+ const insets = !element && bounds && bounds !== "viewport" ? bounds : null;
1564
+ return computed(() => {
1565
+ const vp = this.viewport();
1566
+ const reserve = this.reserve();
1567
+ let left = 0;
1568
+ let top = 0;
1569
+ let right = vp.width;
1570
+ let bottom = vp.height;
1571
+ if (element && this.isBrowser) {
1572
+ this.layoutTick();
1573
+ const r = element.getBoundingClientRect();
1574
+ left = Math.max(0, r.left);
1575
+ top = Math.max(0, r.top);
1576
+ right = Math.min(vp.width, r.right);
1577
+ bottom = Math.min(vp.height, r.bottom);
1578
+ } else if (insets) {
1579
+ left = insets.left ?? 0;
1580
+ top = insets.top ?? 0;
1581
+ right = vp.width - (insets.right ?? 0);
1582
+ bottom = vp.height - (insets.bottom ?? 0);
1583
+ }
1584
+ top = Math.max(top, reserve.top);
1585
+ bottom = Math.min(bottom, vp.height - reserve.bottom);
1586
+ return {
1587
+ left: Math.round(left),
1588
+ top: Math.round(top),
1589
+ width: Math.max(0, Math.round(right - left)),
1590
+ height: Math.max(0, Math.round(bottom - top))
1591
+ };
1592
+ }, { equal: sameRect });
1593
+ }
1594
+ observeBounds(element) {
1595
+ if (!this.isBrowser) return () => void 0;
1596
+ const count = this.boundElements.get(element) ?? 0;
1597
+ this.boundElements.set(element, count + 1);
1598
+ if (count === 0) {
1599
+ if (typeof ResizeObserver !== "undefined") {
1600
+ this.boundsObserver ??= new ResizeObserver(this.bumpLayout);
1601
+ this.boundsObserver.observe(element);
1602
+ }
1603
+ if (this.boundElements.size === 1) document.addEventListener("scroll", this.bumpLayout, {
1604
+ capture: true,
1605
+ passive: true
1606
+ });
1607
+ }
1608
+ let released = false;
1609
+ return () => {
1610
+ if (released) return;
1611
+ released = true;
1612
+ const left = (this.boundElements.get(element) ?? 1) - 1;
1613
+ if (left > 0) {
1614
+ this.boundElements.set(element, left);
1615
+ return;
1616
+ }
1617
+ this.boundElements.delete(element);
1618
+ this.boundsObserver?.unobserve(element);
1619
+ if (this.boundElements.size === 0) document.removeEventListener("scroll", this.bumpLayout, true);
1620
+ };
1621
+ }
1360
1622
  ownsKeyboard(ref) {
1623
+ if (!this.isBrowser) return false;
1361
1624
  const open = this._windows();
1362
- const active = this.isBrowser ? document.activeElement : null;
1625
+ const active = document.activeElement;
1363
1626
  const focused = active ? open.find((w) => w._overlayRef.overlayElement?.contains(active)) : void 0;
1364
1627
  if (focused) return focused === ref;
1365
- if (active && open.some((w) => this.taskbarTab(w)?.contains(active))) return false;
1366
- if (ref.state() === "minimized") return false;
1367
- return open.every((w) => w === ref || w.state() === "minimized" || w.z() <= ref.z());
1628
+ if (active && active !== document.body) return false;
1629
+ const current = this._active();
1630
+ if (current && current.state() !== "minimized") return current === ref;
1631
+ let front = null;
1632
+ for (const w of open) if (w.state() !== "minimized" && (!front || w.z() > front.z())) front = w;
1633
+ return front === ref;
1368
1634
  }
1369
1635
  _registerTaskbar(taskbar) {
1370
- this.taskbars.add(taskbar);
1371
- return () => this.taskbars.delete(taskbar);
1636
+ this.taskbars.update((list) => [...list, taskbar]);
1637
+ return () => this.taskbars.update((list) => list.filter((t) => t !== taskbar));
1372
1638
  }
1373
- _focusAfterMinimize(ref) {
1374
- const restore = this.taskbarTab(ref)?.querySelector("button");
1639
+ _focusAfterMinimize(ref, attempt = 0) {
1640
+ if (attempt < 3 && this.taskbars().some((taskbar) => taskbar.settling(ref))) {
1641
+ const active = this.focusedElement();
1642
+ if (active && ref._overlayRef.overlayElement?.contains(active)) active.blur();
1643
+ afterNextRender(() => this._focusAfterMinimize(ref, attempt + 1), { injector: this.parentInjector });
1644
+ return;
1645
+ }
1646
+ const tab = this.taskbarTab(ref);
1647
+ const restore = tab?.matches("button") ? tab : tab?.querySelector("button");
1375
1648
  if (restore) restore.focus();
1376
1649
  else if (ref._returnFocus?.isConnected) ref._returnFocus.focus();
1377
1650
  const active = this.focusedElement();
1378
1651
  if (active && ref._overlayRef.overlayElement?.contains(active)) active.blur();
1379
1652
  }
1380
1653
  taskbarTab(ref) {
1381
- for (const taskbar of this.taskbars) {
1654
+ for (const taskbar of this.taskbars()) {
1382
1655
  const tab = taskbar.tabFor(ref);
1383
1656
  if (tab) return tab;
1384
1657
  }
@@ -1395,6 +1668,80 @@ var WrWindowManager = class WrWindowManager {
1395
1668
  if (!active) return true;
1396
1669
  return !!ref._overlayRef.overlayElement?.contains(active) || !!this.taskbarTab(ref)?.contains(active);
1397
1670
  }
1671
+ onScreen() {
1672
+ return this._windows().filter((w) => w.state() !== "minimized");
1673
+ }
1674
+ cascade() {
1675
+ const rtl = this.dir?.value === "rtl";
1676
+ this.onScreen().sort((a, b) => a.z() - b.z()).forEach((ref, index) => {
1677
+ const m = ref._model;
1678
+ m.restore();
1679
+ const wa = m.workArea();
1680
+ const o = m.options();
1681
+ const fit = (size, room) => o.keepInViewport ? Math.min(size, room) : size;
1682
+ const width = clamp(fit(m.width(), wa.width), o.minWidth, o.maxWidth);
1683
+ const height = clamp(fit(m.height(), wa.height), o.minHeight, o.maxHeight);
1684
+ const offset = cascadeOffset(index);
1685
+ const x = rtl ? wa.left + wa.width - width - offset.x : wa.left + offset.x;
1686
+ const y = wa.top + offset.y;
1687
+ m.setRaw({
1688
+ x: clamp(x, wa.left, Math.max(wa.left, wa.left + wa.width - width)),
1689
+ y: clamp(y, wa.top, Math.max(wa.top, wa.top + wa.height - height)),
1690
+ width,
1691
+ height
1692
+ });
1693
+ this._raise(ref);
1694
+ });
1695
+ }
1696
+ tile(mode = "grid") {
1697
+ const list = this.onScreen();
1698
+ const n = list.length;
1699
+ if (n === 0) return;
1700
+ const rtl = this.dir?.value === "rtl";
1701
+ const cols = mode === "columns" ? n : mode === "rows" ? 1 : Math.ceil(Math.sqrt(n));
1702
+ const rows = Math.ceil(n / cols);
1703
+ list.forEach((ref, index) => {
1704
+ const m = ref._model;
1705
+ m.restore();
1706
+ const wa = m.workArea();
1707
+ const o = m.options();
1708
+ const row = Math.floor(index / cols);
1709
+ const inRow = Math.min(cols, n - row * cols);
1710
+ let col = index % cols;
1711
+ if (rtl) col = inRow - 1 - col;
1712
+ const x0 = wa.left + Math.floor(wa.width * col / inRow);
1713
+ const x1 = wa.left + Math.floor(wa.width * (col + 1) / inRow);
1714
+ const y0 = wa.top + Math.floor(wa.height * row / rows);
1715
+ const y1 = wa.top + Math.floor(wa.height * (row + 1) / rows);
1716
+ m.setRaw({
1717
+ x: x0,
1718
+ y: y0,
1719
+ width: clamp(x1 - x0, o.minWidth, o.maxWidth),
1720
+ height: clamp(y1 - y0, o.minHeight, o.maxHeight)
1721
+ });
1722
+ m.clamp();
1723
+ });
1724
+ }
1725
+ minimizeAll() {
1726
+ for (const ref of this.onScreen()) ref._model.minimize();
1727
+ }
1728
+ restoreAll() {
1729
+ for (const ref of this._windows()) ref._model.unminimize();
1730
+ }
1731
+ activateNext() {
1732
+ this.cycle(1);
1733
+ }
1734
+ activatePrevious() {
1735
+ this.cycle(-1);
1736
+ }
1737
+ cycle(step) {
1738
+ const list = this._windows();
1739
+ if (list.length === 0) return;
1740
+ const current = this._active();
1741
+ const index = current ? list.indexOf(current) : -1;
1742
+ const next = index < 0 ? list[step > 0 ? 0 : list.length - 1] : list[(index + step + list.length) % list.length];
1743
+ this._activate(next);
1744
+ }
1398
1745
  closeAll() {
1399
1746
  for (const ref of [...this._windows()]) ref.close();
1400
1747
  }
@@ -1411,11 +1758,12 @@ var WrWindowManager = class WrWindowManager {
1411
1758
  const snapshot = this._windows().map((ref) => ({
1412
1759
  id: ref.id,
1413
1760
  state: ref.state(),
1414
- x: ref._rawX(),
1415
- y: ref._rawY(),
1416
- width: ref._rawWidth(),
1417
- height: ref._rawHeight(),
1418
- title: ref.title()
1761
+ x: ref._model.x(),
1762
+ y: ref._model.y(),
1763
+ width: ref._model.width(),
1764
+ height: ref._model.height(),
1765
+ title: ref.title(),
1766
+ snap: ref._model.snap()
1419
1767
  }));
1420
1768
  this.storage.set(this.layoutKey(name), snapshot);
1421
1769
  }
@@ -1438,19 +1786,20 @@ var WrWindowManager = class WrWindowManager {
1438
1786
  }
1439
1787
  }
1440
1788
  applyRestore(ref, snap) {
1441
- ref.restore();
1442
- if (snap.title) ref.setTitle(snap.title);
1443
- ref.moveTo(snap.x, snap.y);
1444
- ref.resizeTo(snap.width, snap.height);
1445
- if (snap.state === "minimized") ref.minimize();
1446
- else if (snap.state === "maximized") ref.maximize();
1789
+ const m = ref._model;
1790
+ m.restore();
1791
+ if (snap.title) m.title.set(snap.title);
1792
+ m.resizeTo(snap.width, snap.height);
1793
+ m.moveTo(snap.x, snap.y);
1794
+ if (snap.snap && snap.snap !== "top") m.snap.set(snap.snap);
1795
+ if (snap.state !== "normal") m.setState(snap.state);
1447
1796
  }
1448
1797
  clearLayout(name) {
1449
1798
  this.storage.remove(this.layoutKey(name));
1450
1799
  }
1451
1800
  static ɵfac = i0.ɵɵngDeclareFactory({
1452
1801
  minVersion: "12.0.0",
1453
- version: "22.2.3",
1802
+ version: "22.2.4",
1454
1803
  ngImport: i0,
1455
1804
  type: WrWindowManager,
1456
1805
  deps: [],
@@ -1458,14 +1807,14 @@ var WrWindowManager = class WrWindowManager {
1458
1807
  });
1459
1808
  static ɵprov = i0.ɵɵngDeclareService({
1460
1809
  minVersion: "22.0.0",
1461
- version: "22.2.3",
1810
+ version: "22.2.4",
1462
1811
  ngImport: i0,
1463
1812
  type: WrWindowManager
1464
1813
  });
1465
1814
  };
1466
1815
  i0.ɵɵngDeclareClassMetadata({
1467
1816
  minVersion: "12.0.0",
1468
- version: "22.2.3",
1817
+ version: "22.2.4",
1469
1818
  ngImport: i0,
1470
1819
  type: WrWindowManager,
1471
1820
  decorators: [{ type: Service }]
@@ -1477,38 +1826,239 @@ i0.ɵɵngDeclareClassMetadata({
1477
1826
  * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
1478
1827
  */
1479
1828
  const NO_OVERRIDE = signal(null).asReadonly();
1829
+ const NO_RESERVE = {
1830
+ top: 0,
1831
+ bottom: 0
1832
+ };
1833
+ const sameTabs = (a, b) => a.size === b.size && [...a].every(([id, width]) => b.get(id) === width);
1834
+ const sameRoom = (a, b) => a === b || !!a && !!b && a.width === b.width && a.gap === b.gap && a.more === b.more && sameTabs(a.tabs, b.tabs);
1835
+ const maxTabsAttr = (value) => value === null || value === void 0 || value === "" ? null : Math.max(1, Math.trunc(coerceNumberProperty(value, 1)));
1480
1836
  var WrWindowTaskbar = class WrWindowTaskbar {
1481
- railLabel = useI18nText(NO_OVERRIDE, "window.taskbar", "Minimized windows");
1837
+ minimizedLabel = useI18nText(NO_OVERRIDE, "window.taskbar", "Minimized windows");
1838
+ allLabel = useI18nText(NO_OVERRIDE, "window.taskbarAll", "Open windows");
1482
1839
  closeLabel = useI18nText(NO_OVERRIDE, "window.closeWindow", "Close window");
1483
1840
  untitled = useI18nText(NO_OVERRIDE, "window.untitled", "Untitled");
1484
1841
  restoreLabel = useI18nFormatter("window.restoreWindow", "Restore {{title}}");
1842
+ moreLabel = useI18nFormatter("window.taskbarMore", "More windows: {{count}}");
1485
1843
  position = input("bottom", ...ngDevMode ? [{ debugName: "position" }] : /* istanbul ignore next */ []);
1844
+ show = input("minimized", ...ngDevMode ? [{ debugName: "show" }] : /* istanbul ignore next */ []);
1845
+ maxTabs = input(null, {
1846
+ ...ngDevMode ? { debugName: "maxTabs" } : /* istanbul ignore next */ {},
1847
+ transform: maxTabsAttr
1848
+ });
1486
1849
  manager = inject(WrWindowManager);
1487
- items = computed(() => this.manager.minimized(), ...ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []);
1850
+ host = inject(ElementRef).nativeElement;
1851
+ dir = inject(Directionality, { optional: true });
1852
+ railLabel = computed(() => this.show() === "all" ? this.allLabel() : this.minimizedLabel(), ...ngDevMode ? [{ debugName: "railLabel" }] : /* istanbul ignore next */ []);
1853
+ items = computed(() => this.show() === "all" ? this.manager.windows().filter((w) => w.taskbarVisible) : this.manager.minimized(), ...ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []);
1854
+ room = signal(null, {
1855
+ ...ngDevMode ? { debugName: "room" } : /* istanbul ignore next */ {},
1856
+ equal: sameRoom
1857
+ });
1858
+ visible = computed(() => {
1859
+ const items = this.items();
1860
+ const max = this.maxTabs() ?? Infinity;
1861
+ const room = this.room();
1862
+ const fits = (shown, more) => {
1863
+ if (room === null) return true;
1864
+ let need = Math.max(0, shown.length - 1) * room.gap + (more ? room.gap + room.more : 0);
1865
+ for (const ref of shown) need += room.tabs.get(ref.id) ?? 0;
1866
+ return need <= room.width + .01;
1867
+ };
1868
+ if (items.length <= max && fits(items, false)) return items;
1869
+ const current = items.find((ref) => this.isCurrent(ref));
1870
+ const first = (count) => {
1871
+ const shown = items.slice(0, count);
1872
+ if (current && !shown.includes(current)) shown[count - 1] = current;
1873
+ return shown;
1874
+ };
1875
+ for (let count = Math.min(max, items.length - 1); count > 1; count--) {
1876
+ const shown = first(count);
1877
+ if (fits(shown, true)) return shown;
1878
+ }
1879
+ return first(1);
1880
+ }, ...ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []);
1881
+ slots = computed(() => new Map(this.visible().map((ref, index) => [ref, index])), ...ngDevMode ? [{ debugName: "slots" }] : /* istanbul ignore next */ []);
1882
+ folded = computed(() => {
1883
+ const visible = this.visible();
1884
+ return this.items().filter((ref) => !visible.includes(ref));
1885
+ }, ...ngDevMode ? [{ debugName: "folded" }] : /* istanbul ignore next */ []);
1886
+ menuPosition = computed(() => this.position() === "top" ? "bottom-end" : "top-end", ...ngDevMode ? [{ debugName: "menuPosition" }] : /* istanbul ignore next */ []);
1488
1887
  classes = computed(() => `wr-window-taskbar wr-window-taskbar--${this.position()}`, ...ngDevMode ? [{ debugName: "classes" }] : /* istanbul ignore next */ []);
1489
1888
  tabs = viewChildren("tab", ...ngDevMode ? [{ debugName: "tabs" }] : /* istanbul ignore next */ []);
1889
+ more = viewChild("more", ...ngDevMode ? [{ debugName: "more" }] : /* istanbul ignore next */ []);
1890
+ rovingIndex = signal(0, ...ngDevMode ? [{ debugName: "rovingIndex" }] : /* istanbul ignore next */ []);
1891
+ stop = computed(() => {
1892
+ const buttons = this.visible().length * 2 + (this.folded().length > 0 ? 1 : 0);
1893
+ return Math.max(0, Math.min(this.rovingIndex(), buttons - 1));
1894
+ }, ...ngDevMode ? [{ debugName: "stop" }] : /* istanbul ignore next */ []);
1895
+ reserve = signal(NO_RESERVE, {
1896
+ ...ngDevMode ? { debugName: "reserve" } : /* istanbul ignore next */ {},
1897
+ equal: (a, b) => a.top === b.top && a.bottom === b.bottom
1898
+ });
1490
1899
  constructor() {
1491
- const unregister = this.manager._registerTaskbar({ tabFor: (ref) => {
1492
- const index = this.items().indexOf(ref);
1493
- return index < 0 ? null : this.tabs()[index]?.nativeElement ?? null;
1494
- } });
1495
- inject(DestroyRef).onDestroy(unregister);
1900
+ const unregister = this.manager._registerTaskbar({
1901
+ tabFor: (ref) => {
1902
+ if (this.folded().includes(ref)) return this.more()?.nativeElement ?? null;
1903
+ const index = this.items().indexOf(ref);
1904
+ return index >= 0 ? this.tabs()[index]?.nativeElement ?? null : null;
1905
+ },
1906
+ settling: (ref) => {
1907
+ const room = this.room();
1908
+ const index = this.items().indexOf(ref);
1909
+ if (room === null || index < 0) return false;
1910
+ if (!room.tabs.has(ref.id)) return true;
1911
+ if (this.folded().includes(ref)) return !this.more();
1912
+ return !!this.tabs()[index]?.nativeElement.classList.contains("wr-window-taskbar__tab--folded");
1913
+ },
1914
+ contains: (node) => this.host.contains(node),
1915
+ reserve: this.reserve.asReadonly()
1916
+ });
1917
+ const destroyRef = inject(DestroyRef);
1918
+ destroyRef.onDestroy(unregister);
1919
+ let observer = null;
1920
+ let observedRail = null;
1921
+ afterRenderEffect(() => {
1922
+ for (const ref of this.items()) this.tabTitle(ref);
1923
+ this.folded().length;
1924
+ this.position();
1925
+ const rail = this.host.querySelector(".wr-window-taskbar__rail");
1926
+ if (observer && rail !== observedRail) {
1927
+ if (observedRail) observer.unobserve(observedRail);
1928
+ if (rail) observer.observe(rail);
1929
+ }
1930
+ observedRail = rail;
1931
+ this.measure();
1932
+ });
1933
+ afterNextRender(() => {
1934
+ const measure = () => this.measure();
1935
+ observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measure);
1936
+ observer?.observe(this.host);
1937
+ if (observedRail) observer?.observe(observedRail);
1938
+ window.addEventListener("resize", measure, { passive: true });
1939
+ destroyRef.onDestroy(() => {
1940
+ observer?.disconnect();
1941
+ window.removeEventListener("resize", measure);
1942
+ });
1943
+ });
1496
1944
  }
1497
1945
  tabTitle(ref) {
1498
1946
  return ref.title() || this.untitled();
1499
1947
  }
1500
- restore(ref) {
1501
- ref.restore();
1502
- ref.focus();
1503
- ref._doFocusInitial?.();
1948
+ isCurrent(ref) {
1949
+ return ref.isActive() && ref.state() !== "minimized";
1950
+ }
1951
+ tabLabel(ref) {
1952
+ const title = this.tabTitle(ref);
1953
+ return this.show() === "all" && ref.state() !== "minimized" ? title : this.restoreLabel({ title });
1954
+ }
1955
+ tabIndex(button) {
1956
+ return button >= 0 && button === this.stop() ? 0 : -1;
1957
+ }
1958
+ press(ref) {
1959
+ if (this.show() === "all" && this.isCurrent(ref)) {
1960
+ ref.minimize();
1961
+ return;
1962
+ }
1963
+ ref.activate();
1964
+ }
1965
+ pick(ref, menu) {
1966
+ menu.close();
1967
+ this.press(ref);
1504
1968
  }
1505
1969
  close(ref, event) {
1506
1970
  event.stopPropagation();
1507
1971
  ref.close();
1508
1972
  }
1973
+ onKeydown(event) {
1974
+ const buttons = this.buttons();
1975
+ if (buttons.length === 0) return;
1976
+ const current = buttons.indexOf(event.target);
1977
+ if (current < 0) return;
1978
+ const rtl = this.dir?.value === "rtl";
1979
+ let next;
1980
+ switch (event.key) {
1981
+ case "ArrowRight":
1982
+ next = current + (rtl ? -1 : 1);
1983
+ break;
1984
+ case "ArrowLeft":
1985
+ next = current + (rtl ? 1 : -1);
1986
+ break;
1987
+ case "Home":
1988
+ next = 0;
1989
+ break;
1990
+ case "End":
1991
+ next = buttons.length - 1;
1992
+ break;
1993
+ default: return;
1994
+ }
1995
+ event.preventDefault();
1996
+ next = (next + buttons.length) % buttons.length;
1997
+ this.rovingIndex.set(next);
1998
+ buttons[next].focus();
1999
+ }
2000
+ onFocusIn(event) {
2001
+ const index = this.buttons().indexOf(event.target);
2002
+ if (index >= 0) this.rovingIndex.set(index);
2003
+ }
2004
+ buttons() {
2005
+ return [...this.host.querySelectorAll(".wr-window-taskbar__tab:not(.wr-window-taskbar__tab--folded) button, .wr-window-taskbar__more")];
2006
+ }
2007
+ measure() {
2008
+ if (typeof window === "undefined") return;
2009
+ this.measureRoom();
2010
+ const rect = this.host.getBoundingClientRect();
2011
+ if (rect.height <= 0) {
2012
+ this.reserve.set(NO_RESERVE);
2013
+ return;
2014
+ }
2015
+ const vh = document.documentElement.clientHeight || window.innerHeight;
2016
+ this.reserve.set(this.position() === "top" ? {
2017
+ top: Math.ceil(rect.bottom),
2018
+ bottom: 0
2019
+ } : {
2020
+ top: 0,
2021
+ bottom: Math.max(0, Math.ceil(vh - rect.top))
2022
+ });
2023
+ }
2024
+ measureRoom() {
2025
+ const rail = this.host.querySelector(".wr-window-taskbar__rail");
2026
+ const items = this.items();
2027
+ const tabs = this.tabs().map((tab) => tab.nativeElement);
2028
+ const width = this.host.clientWidth;
2029
+ if (!rail || tabs.length === 0 || width <= 0) {
2030
+ this.room.set(null);
2031
+ return;
2032
+ }
2033
+ if (tabs.length !== items.length) return;
2034
+ const railStyle = getComputedStyle(rail);
2035
+ const px = (value) => parseFloat(value) || 0;
2036
+ const inner = width - px(railStyle.paddingInlineStart) - px(railStyle.paddingInlineEnd) - px(railStyle.borderInlineStartWidth) - px(railStyle.borderInlineEndWidth);
2037
+ const widths = /* @__PURE__ */ new Map();
2038
+ tabs.forEach((tab, index) => {
2039
+ const width = tab.getBoundingClientRect().width;
2040
+ const cap = getComputedStyle(tab).maxWidth;
2041
+ const fixed = cap.endsWith("px");
2042
+ if (!fixed && cap !== "none" && cap !== "") {
2043
+ widths.set(items[index].id, width);
2044
+ return;
2045
+ }
2046
+ const title = tab.querySelector(".wr-window-taskbar__tab-title");
2047
+ const clipped = title ? title.scrollWidth - title.clientWidth : 0;
2048
+ const natural = width + (clipped > 1 ? clipped : 0);
2049
+ widths.set(items[index].id, fixed ? Math.min(natural, parseFloat(cap)) : natural);
2050
+ });
2051
+ const more = this.more()?.nativeElement.getBoundingClientRect().width ?? tabs[0].getBoundingClientRect().height;
2052
+ this.room.set({
2053
+ width: inner,
2054
+ gap: px(railStyle.columnGap),
2055
+ more,
2056
+ tabs: widths
2057
+ });
2058
+ }
1509
2059
  static ɵfac = i0.ɵɵngDeclareFactory({
1510
2060
  minVersion: "12.0.0",
1511
- version: "22.2.3",
2061
+ version: "22.2.4",
1512
2062
  ngImport: i0,
1513
2063
  type: WrWindowTaskbar,
1514
2064
  deps: [],
@@ -1516,19 +2066,39 @@ var WrWindowTaskbar = class WrWindowTaskbar {
1516
2066
  });
1517
2067
  static ɵcmp = i0.ɵɵngDeclareComponent({
1518
2068
  minVersion: "17.0.0",
1519
- version: "22.2.3",
2069
+ version: "22.2.4",
1520
2070
  type: WrWindowTaskbar,
1521
2071
  isStandalone: true,
1522
2072
  selector: "wr-window-taskbar",
1523
- inputs: { position: {
1524
- classPropertyName: "position",
1525
- publicName: "position",
1526
- isSignal: true,
1527
- isRequired: false,
1528
- transformFunction: null
1529
- } },
2073
+ inputs: {
2074
+ position: {
2075
+ classPropertyName: "position",
2076
+ publicName: "position",
2077
+ isSignal: true,
2078
+ isRequired: false,
2079
+ transformFunction: null
2080
+ },
2081
+ show: {
2082
+ classPropertyName: "show",
2083
+ publicName: "show",
2084
+ isSignal: true,
2085
+ isRequired: false,
2086
+ transformFunction: null
2087
+ },
2088
+ maxTabs: {
2089
+ classPropertyName: "maxTabs",
2090
+ publicName: "maxTabs",
2091
+ isSignal: true,
2092
+ isRequired: false,
2093
+ transformFunction: null
2094
+ }
2095
+ },
1530
2096
  host: {
1531
2097
  attributes: { "role": "toolbar" },
2098
+ listeners: {
2099
+ "keydown": "onKeydown($event)",
2100
+ "focusin": "onFocusIn($event)"
2101
+ },
1532
2102
  properties: {
1533
2103
  "class": "classes()",
1534
2104
  "attr.aria-label": "railLabel()"
@@ -1539,30 +2109,71 @@ var WrWindowTaskbar = class WrWindowTaskbar {
1539
2109
  predicate: ["tab"],
1540
2110
  descendants: true,
1541
2111
  isSignal: true
2112
+ }, {
2113
+ propertyName: "more",
2114
+ first: true,
2115
+ predicate: ["more"],
2116
+ descendants: true,
2117
+ isSignal: true
1542
2118
  }],
1543
2119
  ngImport: i0,
1544
- template: "@if (items().length > 0) {\n <div class=\"wr-window-taskbar__rail\">\n @for (ref of items(); track ref.id) {\n <!--\n Two SIBLING buttons in a plain container, not a close button nested\n inside the tab. The close used to be a `role=\"button\"` span with\n `tabindex=\"0\"` inside the tab's own `<button>` — interactive content\n inside interactive content, which the HTML parser and `nested-interactive`\n both object to. Nothing caught it: a taskbar tab exists only while a\n window is minimized, and the a11y sweep reads prerendered HTML, where the\n rail is not rendered at all.\n -->\n <div #tab class=\"wr-window-taskbar__tab\">\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab-restore\"\n [attr.aria-label]=\"restoreLabel({ title: tabTitle(ref) })\"\n (click)=\"restore(ref)\"\n >\n <span class=\"wr-window-taskbar__tab-title\">{{ tabTitle(ref) }}</span>\n </button>\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab-close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close(ref, $event)\"\n >\n <svg width=\"9\" height=\"9\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M1 1 L9 9 M9 1 L1 9\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n }\n </div>\n}\n",
2120
+ template: "@if (items().length > 0) {\n <div class=\"wr-window-taskbar__rail\">\n @for (ref of items(); track ref.id) {\n @let slot = slots().get(ref) ?? -1;\n <!--\n Two SIBLING buttons in a plain container, not a close button nested\n inside the tab. The close used to be a `role=\"button\"` span with\n `tabindex=\"0\"` inside the tab's own `<button>` — interactive content\n inside interactive content, which the HTML parser and `nested-interactive`\n both object to. Nothing caught it: a taskbar tab exists only while a\n window is minimized, and the a11y sweep reads prerendered HTML, where the\n rail is not rendered at all.\n -->\n <!--\n A folded window keeps its tab, hidden and out of the row but laid out,\n so the rail can read the width it would take to draw it again.\n -->\n <div\n #tab\n class=\"wr-window-taskbar__tab\"\n [class.wr-window-taskbar__tab--active]=\"isCurrent(ref)\"\n [class.wr-window-taskbar__tab--minimized]=\"ref.state() === 'minimized'\"\n [class.wr-window-taskbar__tab--folded]=\"slot < 0\"\n [attr.inert]=\"slot < 0 ? '' : null\"\n >\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab-restore\"\n [attr.aria-label]=\"tabLabel(ref)\"\n [attr.aria-pressed]=\"show() === 'all' ? isCurrent(ref) : null\"\n [attr.tabindex]=\"tabIndex(slot < 0 ? -1 : slot * 2)\"\n (click)=\"press(ref)\"\n >\n <span class=\"wr-window-taskbar__tab-title\">{{ tabTitle(ref) }}</span>\n </button>\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab-close\"\n [attr.aria-label]=\"closeLabel()\"\n [attr.tabindex]=\"tabIndex(slot < 0 ? -1 : slot * 2 + 1)\"\n (click)=\"close(ref, $event)\"\n >\n <svg width=\"9\" height=\"9\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M1 1 L9 9 M9 1 L1 9\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n }\n @if (folded().length > 0) {\n <button\n #more\n #fold=\"wrDropdown\"\n type=\"button\"\n class=\"wr-window-taskbar__more\"\n [wrDropdown]=\"foldMenu\"\n [position]=\"menuPosition()\"\n [attr.aria-label]=\"moreLabel({ count: folded().length })\"\n [attr.tabindex]=\"tabIndex(visible().length * 2)\"\n >\n +{{ folded().length }}\n </button>\n <wr-dropdown-menu #foldMenu>\n @for (ref of folded(); track ref.id) {\n <wr-dropdown-item\n class=\"wr-window-taskbar__fold-item\"\n [class.wr-window-taskbar__fold-item--minimized]=\"ref.state() === 'minimized'\"\n [attr.aria-label]=\"tabLabel(ref)\"\n (click)=\"pick(ref, fold)\"\n >\n {{ tabTitle(ref) }}\n </wr-dropdown-item>\n }\n </wr-dropdown-menu>\n }\n </div>\n}\n",
2121
+ dependencies: [
2122
+ {
2123
+ kind: "directive",
2124
+ type: WrDropdown,
2125
+ selector: "[wrDropdown]",
2126
+ inputs: [
2127
+ "wrDropdown",
2128
+ "trigger",
2129
+ "position",
2130
+ "arrow",
2131
+ "panelClass"
2132
+ ],
2133
+ outputs: ["opened", "closed"],
2134
+ exportAs: ["wrDropdown"]
2135
+ },
2136
+ {
2137
+ kind: "component",
2138
+ type: WrDropdownMenu,
2139
+ selector: "wr-dropdown-menu",
2140
+ exportAs: ["wrDropdownMenu"]
2141
+ },
2142
+ {
2143
+ kind: "component",
2144
+ type: WrDropdownItem,
2145
+ selector: "wr-dropdown-item",
2146
+ inputs: ["icon", "disabled"]
2147
+ }
2148
+ ],
1545
2149
  changeDetection: i0.ChangeDetectionStrategy.OnPush,
1546
2150
  encapsulation: i0.ViewEncapsulation.None
1547
2151
  });
1548
2152
  };
1549
2153
  i0.ɵɵngDeclareClassMetadata({
1550
2154
  minVersion: "12.0.0",
1551
- version: "22.2.3",
2155
+ version: "22.2.4",
1552
2156
  ngImport: i0,
1553
2157
  type: WrWindowTaskbar,
1554
2158
  decorators: [{
1555
2159
  type: Component,
1556
2160
  args: [{
1557
2161
  selector: "wr-window-taskbar",
2162
+ imports: [
2163
+ WrDropdown,
2164
+ WrDropdownMenu,
2165
+ WrDropdownItem
2166
+ ],
1558
2167
  changeDetection: ChangeDetectionStrategy.OnPush,
1559
2168
  encapsulation: ViewEncapsulation.None,
1560
2169
  host: {
1561
2170
  "[class]": "classes()",
1562
2171
  role: "toolbar",
1563
- "[attr.aria-label]": "railLabel()"
2172
+ "[attr.aria-label]": "railLabel()",
2173
+ "(keydown)": "onKeydown($event)",
2174
+ "(focusin)": "onFocusIn($event)"
1564
2175
  },
1565
- template: "@if (items().length > 0) {\n <div class=\"wr-window-taskbar__rail\">\n @for (ref of items(); track ref.id) {\n <!--\n Two SIBLING buttons in a plain container, not a close button nested\n inside the tab. The close used to be a `role=\"button\"` span with\n `tabindex=\"0\"` inside the tab's own `<button>` — interactive content\n inside interactive content, which the HTML parser and `nested-interactive`\n both object to. Nothing caught it: a taskbar tab exists only while a\n window is minimized, and the a11y sweep reads prerendered HTML, where the\n rail is not rendered at all.\n -->\n <div #tab class=\"wr-window-taskbar__tab\">\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab-restore\"\n [attr.aria-label]=\"restoreLabel({ title: tabTitle(ref) })\"\n (click)=\"restore(ref)\"\n >\n <span class=\"wr-window-taskbar__tab-title\">{{ tabTitle(ref) }}</span>\n </button>\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab-close\"\n [attr.aria-label]=\"closeLabel()\"\n (click)=\"close(ref, $event)\"\n >\n <svg width=\"9\" height=\"9\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M1 1 L9 9 M9 1 L1 9\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n }\n </div>\n}\n"
2176
+ template: "@if (items().length > 0) {\n <div class=\"wr-window-taskbar__rail\">\n @for (ref of items(); track ref.id) {\n @let slot = slots().get(ref) ?? -1;\n <!--\n Two SIBLING buttons in a plain container, not a close button nested\n inside the tab. The close used to be a `role=\"button\"` span with\n `tabindex=\"0\"` inside the tab's own `<button>` — interactive content\n inside interactive content, which the HTML parser and `nested-interactive`\n both object to. Nothing caught it: a taskbar tab exists only while a\n window is minimized, and the a11y sweep reads prerendered HTML, where the\n rail is not rendered at all.\n -->\n <!--\n A folded window keeps its tab, hidden and out of the row but laid out,\n so the rail can read the width it would take to draw it again.\n -->\n <div\n #tab\n class=\"wr-window-taskbar__tab\"\n [class.wr-window-taskbar__tab--active]=\"isCurrent(ref)\"\n [class.wr-window-taskbar__tab--minimized]=\"ref.state() === 'minimized'\"\n [class.wr-window-taskbar__tab--folded]=\"slot < 0\"\n [attr.inert]=\"slot < 0 ? '' : null\"\n >\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab-restore\"\n [attr.aria-label]=\"tabLabel(ref)\"\n [attr.aria-pressed]=\"show() === 'all' ? isCurrent(ref) : null\"\n [attr.tabindex]=\"tabIndex(slot < 0 ? -1 : slot * 2)\"\n (click)=\"press(ref)\"\n >\n <span class=\"wr-window-taskbar__tab-title\">{{ tabTitle(ref) }}</span>\n </button>\n <button\n type=\"button\"\n class=\"wr-window-taskbar__tab-close\"\n [attr.aria-label]=\"closeLabel()\"\n [attr.tabindex]=\"tabIndex(slot < 0 ? -1 : slot * 2 + 1)\"\n (click)=\"close(ref, $event)\"\n >\n <svg width=\"9\" height=\"9\" viewBox=\"0 0 10 10\" aria-hidden=\"true\">\n <path d=\"M1 1 L9 9 M9 1 L1 9\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" />\n </svg>\n </button>\n </div>\n }\n @if (folded().length > 0) {\n <button\n #more\n #fold=\"wrDropdown\"\n type=\"button\"\n class=\"wr-window-taskbar__more\"\n [wrDropdown]=\"foldMenu\"\n [position]=\"menuPosition()\"\n [attr.aria-label]=\"moreLabel({ count: folded().length })\"\n [attr.tabindex]=\"tabIndex(visible().length * 2)\"\n >\n +{{ folded().length }}\n </button>\n <wr-dropdown-menu #foldMenu>\n @for (ref of folded(); track ref.id) {\n <wr-dropdown-item\n class=\"wr-window-taskbar__fold-item\"\n [class.wr-window-taskbar__fold-item--minimized]=\"ref.state() === 'minimized'\"\n [attr.aria-label]=\"tabLabel(ref)\"\n (click)=\"pick(ref, fold)\"\n >\n {{ tabTitle(ref) }}\n </wr-dropdown-item>\n }\n </wr-dropdown-menu>\n }\n </div>\n}\n"
1566
2177
  }]
1567
2178
  }],
1568
2179
  ctorParameters: () => [],
@@ -1575,12 +2186,44 @@ i0.ɵɵngDeclareClassMetadata({
1575
2186
  required: false
1576
2187
  }]
1577
2188
  }],
2189
+ show: [{
2190
+ type: i0.Input,
2191
+ args: [{
2192
+ isSignal: true,
2193
+ alias: "show",
2194
+ required: false
2195
+ }]
2196
+ }],
2197
+ maxTabs: [{
2198
+ type: i0.Input,
2199
+ args: [{
2200
+ isSignal: true,
2201
+ alias: "maxTabs",
2202
+ required: false
2203
+ }]
2204
+ }],
1578
2205
  tabs: [{
1579
2206
  type: i0.ViewChildren,
1580
2207
  args: ["tab", { isSignal: true }]
2208
+ }],
2209
+ more: [{
2210
+ type: i0.ViewChild,
2211
+ args: ["more", { isSignal: true }]
1581
2212
  }]
1582
2213
  }
1583
2214
  });
1584
- export { WR_WINDOW_DATA, WR_WINDOW_REF, WrWindowManager, WrWindowRef, WrWindowTaskbar };
2215
+ /**
2216
+ * @license
2217
+ *
2218
+ * Use of this source code is governed by an MIT-style license that can be
2219
+ * found in the LICENSE file at https://github.com/thekhegay/ngwr/blob/main/LICENSE
2220
+ */
2221
+ function withWindows(defaults) {
2222
+ return wrFeature("windows", [{
2223
+ provide: WR_WINDOW_DEFAULTS,
2224
+ useValue: defaults
2225
+ }]);
2226
+ }
2227
+ export { WR_WINDOW_DATA, WR_WINDOW_REF, WrWindowManager, WrWindowRef, WrWindowTaskbar, withWindows };
1585
2228
 
1586
2229
  //# sourceMappingURL=ngwr-window.mjs.map