@videojs/html 10.0.0-beta.15 → 10.0.0-beta.17

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 (377) hide show
  1. package/cdn/{abort-CzqmAxxH.js → abort-BL-SGV3o.js} +1 -1
  2. package/cdn/{abort-CzqmAxxH.js.map → abort-BL-SGV3o.js.map} +1 -1
  3. package/cdn/{abort-KaHR4JO1.js → abort-DGNIECLn.js} +1 -1
  4. package/cdn/{abort-KaHR4JO1.js.map → abort-DGNIECLn.js.map} +1 -1
  5. package/cdn/audio-minimal-ui.dev.js +4 -5
  6. package/cdn/audio-minimal-ui.js +1 -1
  7. package/cdn/audio-minimal.dev.js +7 -8
  8. package/cdn/audio-minimal.dev.js.map +1 -1
  9. package/cdn/audio-minimal.js +1 -1
  10. package/cdn/audio-minimal.js.map +1 -1
  11. package/cdn/audio-ui.dev.js +4 -5
  12. package/cdn/audio-ui.js +1 -1
  13. package/cdn/audio.dev.js +22 -7
  14. package/cdn/audio.dev.js.map +1 -1
  15. package/cdn/audio.js +1 -1
  16. package/cdn/audio.js.map +1 -1
  17. package/cdn/background.dev.js +3 -3
  18. package/cdn/background.js +1 -1
  19. package/cdn/background.js.map +1 -1
  20. package/cdn/compounds-Df_1JbZD.js +8 -0
  21. package/cdn/compounds-Df_1JbZD.js.map +1 -0
  22. package/cdn/{volume-slider-Co5X4Z1t.js → compounds-VFPcCeRV.js} +2439 -946
  23. package/cdn/compounds-VFPcCeRV.js.map +1 -0
  24. package/cdn/context-C-IpVHEt.js +14 -0
  25. package/cdn/context-C-IpVHEt.js.map +1 -0
  26. package/cdn/{context-BjrbhiSk.js → context-CvJ83xGk.js} +2 -83
  27. package/cdn/context-CvJ83xGk.js.map +1 -0
  28. package/cdn/{create-player-p9xCO0yC.js → create-player-CyG77t-m.js} +778 -1955
  29. package/cdn/create-player-CyG77t-m.js.map +1 -0
  30. package/cdn/create-player-Gom2ZZe3.js +7 -0
  31. package/cdn/create-player-Gom2ZZe3.js.map +1 -0
  32. package/cdn/{hls-26YJVjvD.js → hls-BAbdl84H.js} +3 -3
  33. package/cdn/{hls-26YJVjvD.js.map → hls-BAbdl84H.js.map} +1 -1
  34. package/cdn/{hls-BriQI4q1.js → hls-BrzPpueW.js} +9 -12
  35. package/cdn/{hls-BriQI4q1.js.map → hls-BrzPpueW.js.map} +1 -1
  36. package/cdn/hotkey-element-DR3ZAqKa.js +2 -0
  37. package/cdn/hotkey-element-DR3ZAqKa.js.map +1 -0
  38. package/cdn/hotkey-element-Dlta37Be.js +369 -0
  39. package/cdn/hotkey-element-Dlta37Be.js.map +1 -0
  40. package/cdn/media/dash-video.dev.js +3 -4
  41. package/cdn/media/dash-video.dev.js.map +1 -1
  42. package/cdn/media/dash-video.js +2 -2
  43. package/cdn/media/hls-video.dev.js +4 -5
  44. package/cdn/media/hls-video.dev.js.map +1 -1
  45. package/cdn/media/hls-video.js +1 -1
  46. package/cdn/media/hls-video.js.map +1 -1
  47. package/cdn/media/mux-audio.dev.d.ts +1 -0
  48. package/cdn/media/mux-audio.dev.js +29 -0
  49. package/cdn/media/mux-audio.dev.js.map +1 -0
  50. package/cdn/media/mux-audio.js +2 -0
  51. package/cdn/media/mux-audio.js.map +1 -0
  52. package/cdn/media/mux-video.dev.js +5 -3099
  53. package/cdn/media/mux-video.dev.js.map +1 -1
  54. package/cdn/media/mux-video.js +1 -24
  55. package/cdn/media/mux-video.js.map +1 -1
  56. package/cdn/media/native-hls-video.dev.js +4 -4
  57. package/cdn/media/native-hls-video.js +1 -1
  58. package/cdn/media/simple-hls-video.dev.js +966 -681
  59. package/cdn/media/simple-hls-video.dev.js.map +1 -1
  60. package/cdn/media/simple-hls-video.js +6 -6
  61. package/cdn/media/simple-hls-video.js.map +1 -1
  62. package/cdn/{media-attach-mixin-DfKkofgF.js → media-attach-mixin-CMH1ziXP.js} +2 -2
  63. package/cdn/{media-attach-mixin-DfKkofgF.js.map → media-attach-mixin-CMH1ziXP.js.map} +1 -1
  64. package/cdn/{media-attach-mixin-Dv2D43tb.js → media-attach-mixin-DUzmXEJ3.js} +2 -2
  65. package/cdn/{media-attach-mixin-Dv2D43tb.js.map → media-attach-mixin-DUzmXEJ3.js.map} +1 -1
  66. package/cdn/{media-props-mixin-Dc5dpCV4.js → media-props-mixin-hheRmkup.js} +92 -8
  67. package/cdn/media-props-mixin-hheRmkup.js.map +1 -0
  68. package/cdn/media-props-mixin-yRWj8yXF.js +47 -0
  69. package/cdn/media-props-mixin-yRWj8yXF.js.map +1 -0
  70. package/cdn/{minimal-BUUGNZK7.js → minimal-D9Dw-L2P.js} +1 -1
  71. package/cdn/{minimal-BUUGNZK7.js.map → minimal-D9Dw-L2P.js.map} +1 -1
  72. package/cdn/{minimal-CJGOJ6pE.js → minimal-Dc5nNl_-.js} +1 -1
  73. package/cdn/{minimal-CJGOJ6pE.js.map → minimal-Dc5nNl_-.js.map} +1 -1
  74. package/cdn/minimal-ui-B3BBK5nX.js +2 -0
  75. package/cdn/minimal-ui-B3BBK5nX.js.map +1 -0
  76. package/cdn/minimal-ui-CPRSYky_.js +28 -0
  77. package/cdn/minimal-ui-CPRSYky_.js.map +1 -0
  78. package/cdn/minimal-ui-Cnaig4X6.js +22 -0
  79. package/cdn/minimal-ui-Cnaig4X6.js.map +1 -0
  80. package/cdn/minimal-ui-DOePmQfq.js +2 -0
  81. package/cdn/minimal-ui-DOePmQfq.js.map +1 -0
  82. package/cdn/mux-DGgCyz_A.js +3102 -0
  83. package/cdn/mux-DGgCyz_A.js.map +1 -0
  84. package/cdn/mux-fYfPUpq3.js +25 -0
  85. package/cdn/mux-fYfPUpq3.js.map +1 -0
  86. package/cdn/{native-hls-D2rfyyyW.js → native-hls-BuCYu7Rd.js} +2 -3
  87. package/cdn/{native-hls-D2rfyyyW.js.map → native-hls-BuCYu7Rd.js.map} +1 -1
  88. package/cdn/native-hls-DnqJ-0zL.js +2 -0
  89. package/cdn/{native-hls-CjRvSXAJ.js.map → native-hls-DnqJ-0zL.js.map} +1 -1
  90. package/cdn/player-Cu5_r0Gk.js +2 -0
  91. package/cdn/{player-Cz9-_j7J.js.map → player-Cu5_r0Gk.js.map} +1 -1
  92. package/cdn/player-DIOgBqQf.js +382 -0
  93. package/cdn/player-DIOgBqQf.js.map +1 -0
  94. package/cdn/{player-D0bhjT8X.js → player-DxBoqZVw.js} +4 -3
  95. package/cdn/{player-D0bhjT8X.js.map → player-DxBoqZVw.js.map} +1 -1
  96. package/cdn/player-eEH-XjnF.js +2 -0
  97. package/cdn/player-eEH-XjnF.js.map +1 -0
  98. package/cdn/{shallow-equal-uPuCQhnd.js → shallow-equal-1KBb-lrM.js} +1 -1
  99. package/cdn/{shallow-equal-uPuCQhnd.js.map → shallow-equal-1KBb-lrM.js.map} +1 -1
  100. package/cdn/{shallow-equal-C8Fs4TTE.js → shallow-equal-Ca0qjpLJ.js} +1 -1
  101. package/cdn/{shallow-equal-C8Fs4TTE.js.map → shallow-equal-Ca0qjpLJ.js.map} +1 -1
  102. package/cdn/{skin-element-zakTHELS.js → skin-element-BTPr5DoY.js} +2 -2
  103. package/cdn/{skin-element-zakTHELS.js.map → skin-element-BTPr5DoY.js.map} +1 -1
  104. package/cdn/{skin-element-DB-cKAAx.js → skin-element-u6pdmSSs.js} +2 -2
  105. package/cdn/{skin-element-DB-cKAAx.js.map → skin-element-u6pdmSSs.js.map} +1 -1
  106. package/cdn/ui-34pkHFdT.js +25 -0
  107. package/cdn/ui-34pkHFdT.js.map +1 -0
  108. package/cdn/ui-DWoV_Da1.js +2 -0
  109. package/cdn/ui-DWoV_Da1.js.map +1 -0
  110. package/cdn/ui-DXtTKomo.js +2 -0
  111. package/cdn/ui-DXtTKomo.js.map +1 -0
  112. package/cdn/ui-X5WOdCCw.js +31 -0
  113. package/cdn/ui-X5WOdCCw.js.map +1 -0
  114. package/cdn/video-minimal-ui.dev.js +4 -5
  115. package/cdn/video-minimal-ui.js +1 -1
  116. package/cdn/video-minimal.dev.js +7 -8
  117. package/cdn/video-minimal.dev.js.map +1 -1
  118. package/cdn/video-minimal.js +1 -1
  119. package/cdn/video-minimal.js.map +1 -1
  120. package/cdn/video-ui.dev.js +4 -5
  121. package/cdn/video-ui.js +1 -1
  122. package/cdn/video.dev.js +32 -7
  123. package/cdn/video.dev.js.map +1 -1
  124. package/cdn/video.js +1 -1
  125. package/cdn/video.js.map +1 -1
  126. package/dist/default/_virtual/inline-css_src/define/audio/minimal-skin.js +1 -1
  127. package/dist/default/_virtual/inline-css_src/define/audio/minimal-skin.js.map +1 -1
  128. package/dist/default/_virtual/inline-css_src/define/audio/skin.js +1 -1
  129. package/dist/default/_virtual/inline-css_src/define/audio/skin.js.map +1 -1
  130. package/dist/default/_virtual/inline-css_src/define/video/minimal-skin.js +1 -1
  131. package/dist/default/_virtual/inline-css_src/define/video/minimal-skin.js.map +1 -1
  132. package/dist/default/_virtual/inline-css_src/define/video/skin.js +1 -1
  133. package/dist/default/_virtual/inline-css_src/define/video/skin.js.map +1 -1
  134. package/dist/default/define/audio/minimal-skin.css +23 -0
  135. package/dist/default/define/audio/minimal-ui.js +28 -12
  136. package/dist/default/define/audio/minimal-ui.js.map +1 -0
  137. package/dist/default/define/audio/skin.css +23 -0
  138. package/dist/default/define/audio/skin.js +1 -1
  139. package/dist/default/define/audio/skin.js.map +1 -1
  140. package/dist/default/define/audio/ui.js +32 -12
  141. package/dist/default/define/audio/ui.js.map +1 -0
  142. package/dist/default/define/background/skin.js +1 -1
  143. package/dist/default/define/media/mux-audio.js +13 -0
  144. package/dist/default/define/media/mux-audio.js.map +1 -0
  145. package/dist/default/define/skin-element.js +1 -1
  146. package/dist/default/define/ui/compounds.js +62 -0
  147. package/dist/default/define/ui/compounds.js.map +1 -0
  148. package/dist/default/define/ui/controls.js +2 -5
  149. package/dist/default/define/ui/controls.js.map +1 -1
  150. package/dist/default/define/ui/error-dialog.js +2 -9
  151. package/dist/default/define/ui/error-dialog.js.map +1 -1
  152. package/dist/default/define/ui/hotkey.js +7 -0
  153. package/dist/default/define/ui/hotkey.js.map +1 -0
  154. package/dist/default/define/ui/slider.js +2 -13
  155. package/dist/default/define/ui/slider.js.map +1 -1
  156. package/dist/default/define/ui/time-slider.js +2 -17
  157. package/dist/default/define/ui/time-slider.js.map +1 -1
  158. package/dist/default/define/ui/time.js +2 -7
  159. package/dist/default/define/ui/time.js.map +1 -1
  160. package/dist/default/define/ui/volume-slider.js +2 -13
  161. package/dist/default/define/ui/volume-slider.js.map +1 -1
  162. package/dist/default/define/video/minimal-skin.css +23 -0
  163. package/dist/default/define/video/minimal-ui.js +39 -18
  164. package/dist/default/define/video/minimal-ui.js.map +1 -0
  165. package/dist/default/define/video/skin.css +23 -0
  166. package/dist/default/define/video/skin.js +1 -1
  167. package/dist/default/define/video/skin.js.map +1 -1
  168. package/dist/default/define/video/ui.js +43 -18
  169. package/dist/default/define/video/ui.js.map +1 -0
  170. package/dist/default/index.js +6 -1
  171. package/dist/default/media/container-element.js +16 -0
  172. package/dist/default/media/container-element.js.map +1 -1
  173. package/dist/default/media/mux-audio/index.js +18 -0
  174. package/dist/default/media/mux-audio/index.js.map +1 -0
  175. package/dist/default/player/context.js.map +1 -1
  176. package/dist/default/ui/alert-dialog/alert-dialog-element.js +1 -0
  177. package/dist/default/ui/alert-dialog/alert-dialog-element.js.map +1 -1
  178. package/dist/default/ui/buffering-indicator/buffering-indicator-element.js +1 -0
  179. package/dist/default/ui/buffering-indicator/buffering-indicator-element.js.map +1 -1
  180. package/dist/default/ui/captions-button/captions-button-element.js +1 -0
  181. package/dist/default/ui/captions-button/captions-button-element.js.map +1 -1
  182. package/dist/default/ui/error-dialog/error-dialog-element.js +1 -0
  183. package/dist/default/ui/error-dialog/error-dialog-element.js.map +1 -1
  184. package/dist/default/ui/fullscreen-button/fullscreen-button-element.js +1 -0
  185. package/dist/default/ui/fullscreen-button/fullscreen-button-element.js.map +1 -1
  186. package/dist/default/ui/gesture/gesture-element.js +84 -0
  187. package/dist/default/ui/gesture/gesture-element.js.map +1 -0
  188. package/dist/default/ui/hotkey/aria-key-shortcuts-controller.js +28 -0
  189. package/dist/default/ui/hotkey/aria-key-shortcuts-controller.js.map +1 -0
  190. package/dist/default/ui/hotkey/hotkey-element.js +81 -0
  191. package/dist/default/ui/hotkey/hotkey-element.js.map +1 -0
  192. package/dist/default/ui/media-button-element.js +9 -1
  193. package/dist/default/ui/media-button-element.js.map +1 -1
  194. package/dist/default/ui/mute-button/mute-button-element.js +1 -0
  195. package/dist/default/ui/mute-button/mute-button-element.js.map +1 -1
  196. package/dist/default/ui/pip-button/pip-button-element.js +1 -0
  197. package/dist/default/ui/pip-button/pip-button-element.js.map +1 -1
  198. package/dist/default/ui/play-button/play-button-element.js +1 -0
  199. package/dist/default/ui/play-button/play-button-element.js.map +1 -1
  200. package/dist/default/ui/popover/popover-element.js +7 -55
  201. package/dist/default/ui/popover/popover-element.js.map +1 -1
  202. package/dist/default/ui/position-controller.js +76 -0
  203. package/dist/default/ui/position-controller.js.map +1 -0
  204. package/dist/default/ui/slider/slider-element.js +1 -1
  205. package/dist/default/ui/slider/slider-preview-element.js +1 -1
  206. package/dist/default/ui/time-slider/time-slider-element.js +5 -5
  207. package/dist/default/ui/time-slider/time-slider-element.js.map +1 -1
  208. package/dist/default/ui/tooltip/tooltip-element.js +8 -54
  209. package/dist/default/ui/tooltip/tooltip-element.js.map +1 -1
  210. package/dist/default/ui/volume-slider/volume-slider-element.js +1 -1
  211. package/dist/dev/_virtual/inline-css_src/define/audio/minimal-skin.js +1 -1
  212. package/dist/dev/_virtual/inline-css_src/define/audio/minimal-skin.js.map +1 -1
  213. package/dist/dev/_virtual/inline-css_src/define/audio/skin.js +1 -1
  214. package/dist/dev/_virtual/inline-css_src/define/audio/skin.js.map +1 -1
  215. package/dist/dev/_virtual/inline-css_src/define/video/minimal-skin.js +1 -1
  216. package/dist/dev/_virtual/inline-css_src/define/video/minimal-skin.js.map +1 -1
  217. package/dist/dev/_virtual/inline-css_src/define/video/skin.js +1 -1
  218. package/dist/dev/_virtual/inline-css_src/define/video/skin.js.map +1 -1
  219. package/dist/dev/define/audio/minimal-skin.css +23 -0
  220. package/dist/dev/define/audio/minimal-ui.js +28 -12
  221. package/dist/dev/define/audio/minimal-ui.js.map +1 -0
  222. package/dist/dev/define/audio/skin.css +23 -0
  223. package/dist/dev/define/audio/skin.d.ts.map +1 -1
  224. package/dist/dev/define/audio/skin.js +16 -0
  225. package/dist/dev/define/audio/skin.js.map +1 -1
  226. package/dist/dev/define/audio/ui.js +32 -12
  227. package/dist/dev/define/audio/ui.js.map +1 -0
  228. package/dist/dev/define/background/skin.js +1 -1
  229. package/dist/dev/define/media/mux-audio.d.ts +14 -0
  230. package/dist/dev/define/media/mux-audio.d.ts.map +1 -0
  231. package/dist/dev/define/media/mux-audio.js +13 -0
  232. package/dist/dev/define/media/mux-audio.js.map +1 -0
  233. package/dist/dev/define/skin-element.d.ts +1 -1
  234. package/dist/dev/define/skin-element.js +1 -1
  235. package/dist/dev/define/ui/compounds.d.ts +12 -0
  236. package/dist/dev/define/ui/compounds.d.ts.map +1 -0
  237. package/dist/dev/define/ui/compounds.js +62 -0
  238. package/dist/dev/define/ui/compounds.js.map +1 -0
  239. package/dist/dev/define/ui/controls.d.ts +2 -0
  240. package/dist/dev/define/ui/controls.d.ts.map +1 -1
  241. package/dist/dev/define/ui/controls.js +2 -5
  242. package/dist/dev/define/ui/controls.js.map +1 -1
  243. package/dist/dev/define/ui/error-dialog.d.ts +6 -0
  244. package/dist/dev/define/ui/error-dialog.d.ts.map +1 -1
  245. package/dist/dev/define/ui/error-dialog.js +2 -9
  246. package/dist/dev/define/ui/error-dialog.js.map +1 -1
  247. package/dist/dev/define/ui/hotkey.d.ts +9 -0
  248. package/dist/dev/define/ui/hotkey.d.ts.map +1 -0
  249. package/dist/dev/define/ui/hotkey.js +7 -0
  250. package/dist/dev/define/ui/hotkey.js.map +1 -0
  251. package/dist/dev/define/ui/slider.d.ts +10 -0
  252. package/dist/dev/define/ui/slider.d.ts.map +1 -1
  253. package/dist/dev/define/ui/slider.js +2 -13
  254. package/dist/dev/define/ui/slider.js.map +1 -1
  255. package/dist/dev/define/ui/time-slider.d.ts +14 -0
  256. package/dist/dev/define/ui/time-slider.d.ts.map +1 -1
  257. package/dist/dev/define/ui/time-slider.js +2 -17
  258. package/dist/dev/define/ui/time-slider.js.map +1 -1
  259. package/dist/dev/define/ui/time.d.ts +4 -0
  260. package/dist/dev/define/ui/time.d.ts.map +1 -1
  261. package/dist/dev/define/ui/time.js +2 -7
  262. package/dist/dev/define/ui/time.js.map +1 -1
  263. package/dist/dev/define/ui/volume-slider.d.ts +10 -0
  264. package/dist/dev/define/ui/volume-slider.d.ts.map +1 -1
  265. package/dist/dev/define/ui/volume-slider.js +2 -13
  266. package/dist/dev/define/ui/volume-slider.js.map +1 -1
  267. package/dist/dev/define/video/minimal-skin.css +23 -0
  268. package/dist/dev/define/video/minimal-ui.js +39 -18
  269. package/dist/dev/define/video/minimal-ui.js.map +1 -0
  270. package/dist/dev/define/video/skin.css +23 -0
  271. package/dist/dev/define/video/skin.d.ts.map +1 -1
  272. package/dist/dev/define/video/skin.js +26 -0
  273. package/dist/dev/define/video/skin.js.map +1 -1
  274. package/dist/dev/define/video/ui.js +43 -18
  275. package/dist/dev/define/video/ui.js.map +1 -0
  276. package/dist/dev/index.d.ts +7 -2
  277. package/dist/dev/index.js +6 -1
  278. package/dist/dev/media/container-element.d.ts +3 -0
  279. package/dist/dev/media/container-element.d.ts.map +1 -1
  280. package/dist/dev/media/container-element.js +16 -0
  281. package/dist/dev/media/container-element.js.map +1 -1
  282. package/dist/dev/media/mux-audio/index.d.ts +11 -0
  283. package/dist/dev/media/mux-audio/index.d.ts.map +1 -0
  284. package/dist/dev/media/mux-audio/index.js +18 -0
  285. package/dist/dev/media/mux-audio/index.js.map +1 -0
  286. package/dist/dev/player/context.d.ts +4 -2
  287. package/dist/dev/player/context.d.ts.map +1 -1
  288. package/dist/dev/player/context.js.map +1 -1
  289. package/dist/dev/ui/alert-dialog/alert-dialog-element.d.ts.map +1 -1
  290. package/dist/dev/ui/alert-dialog/alert-dialog-element.js +1 -0
  291. package/dist/dev/ui/alert-dialog/alert-dialog-element.js.map +1 -1
  292. package/dist/dev/ui/buffering-indicator/buffering-indicator-element.d.ts.map +1 -1
  293. package/dist/dev/ui/buffering-indicator/buffering-indicator-element.js +1 -0
  294. package/dist/dev/ui/buffering-indicator/buffering-indicator-element.js.map +1 -1
  295. package/dist/dev/ui/captions-button/captions-button-element.d.ts +1 -0
  296. package/dist/dev/ui/captions-button/captions-button-element.d.ts.map +1 -1
  297. package/dist/dev/ui/captions-button/captions-button-element.js +1 -0
  298. package/dist/dev/ui/captions-button/captions-button-element.js.map +1 -1
  299. package/dist/dev/ui/context-part-element.d.ts +1 -1
  300. package/dist/dev/ui/error-dialog/error-dialog-element.d.ts.map +1 -1
  301. package/dist/dev/ui/error-dialog/error-dialog-element.js +1 -0
  302. package/dist/dev/ui/error-dialog/error-dialog-element.js.map +1 -1
  303. package/dist/dev/ui/fullscreen-button/fullscreen-button-element.d.ts +1 -0
  304. package/dist/dev/ui/fullscreen-button/fullscreen-button-element.d.ts.map +1 -1
  305. package/dist/dev/ui/fullscreen-button/fullscreen-button-element.js +1 -0
  306. package/dist/dev/ui/fullscreen-button/fullscreen-button-element.js.map +1 -1
  307. package/dist/dev/ui/gesture/gesture-element.d.ts +22 -0
  308. package/dist/dev/ui/gesture/gesture-element.d.ts.map +1 -0
  309. package/dist/dev/ui/gesture/gesture-element.js +84 -0
  310. package/dist/dev/ui/gesture/gesture-element.js.map +1 -0
  311. package/dist/dev/ui/hotkey/aria-key-shortcuts-controller.d.ts +15 -0
  312. package/dist/dev/ui/hotkey/aria-key-shortcuts-controller.d.ts.map +1 -0
  313. package/dist/dev/ui/hotkey/aria-key-shortcuts-controller.js +28 -0
  314. package/dist/dev/ui/hotkey/aria-key-shortcuts-controller.js.map +1 -0
  315. package/dist/dev/ui/hotkey/hotkey-element.d.ts +21 -0
  316. package/dist/dev/ui/hotkey/hotkey-element.d.ts.map +1 -0
  317. package/dist/dev/ui/hotkey/hotkey-element.js +81 -0
  318. package/dist/dev/ui/hotkey/hotkey-element.js.map +1 -0
  319. package/dist/dev/ui/media-button-element.d.ts +2 -0
  320. package/dist/dev/ui/media-button-element.d.ts.map +1 -1
  321. package/dist/dev/ui/media-button-element.js +9 -1
  322. package/dist/dev/ui/media-button-element.js.map +1 -1
  323. package/dist/dev/ui/mute-button/mute-button-element.d.ts +1 -0
  324. package/dist/dev/ui/mute-button/mute-button-element.d.ts.map +1 -1
  325. package/dist/dev/ui/mute-button/mute-button-element.js +1 -0
  326. package/dist/dev/ui/mute-button/mute-button-element.js.map +1 -1
  327. package/dist/dev/ui/pip-button/pip-button-element.d.ts +1 -0
  328. package/dist/dev/ui/pip-button/pip-button-element.d.ts.map +1 -1
  329. package/dist/dev/ui/pip-button/pip-button-element.js +1 -0
  330. package/dist/dev/ui/pip-button/pip-button-element.js.map +1 -1
  331. package/dist/dev/ui/play-button/play-button-element.d.ts +1 -0
  332. package/dist/dev/ui/play-button/play-button-element.d.ts.map +1 -1
  333. package/dist/dev/ui/play-button/play-button-element.js +1 -0
  334. package/dist/dev/ui/play-button/play-button-element.js.map +1 -1
  335. package/dist/dev/ui/popover/popover-element.d.ts.map +1 -1
  336. package/dist/dev/ui/popover/popover-element.js +7 -55
  337. package/dist/dev/ui/popover/popover-element.js.map +1 -1
  338. package/dist/dev/ui/position-controller.js +76 -0
  339. package/dist/dev/ui/position-controller.js.map +1 -0
  340. package/dist/dev/ui/slider/slider-element.js +1 -1
  341. package/dist/dev/ui/slider/slider-preview-element.d.ts +21 -0
  342. package/dist/dev/ui/slider/slider-preview-element.d.ts.map +1 -0
  343. package/dist/dev/ui/slider/slider-preview-element.js +1 -1
  344. package/dist/dev/ui/time-slider/time-slider-element.d.ts +2 -2
  345. package/dist/dev/ui/time-slider/time-slider-element.js +5 -5
  346. package/dist/dev/ui/time-slider/time-slider-element.js.map +1 -1
  347. package/dist/dev/ui/tooltip/tooltip-element.d.ts.map +1 -1
  348. package/dist/dev/ui/tooltip/tooltip-element.js +8 -54
  349. package/dist/dev/ui/tooltip/tooltip-element.js.map +1 -1
  350. package/dist/dev/ui/volume-slider/volume-slider-element.js +1 -1
  351. package/package.json +8 -8
  352. package/cdn/context-BjrbhiSk.js.map +0 -1
  353. package/cdn/context-CdkhXQEw.js +0 -14
  354. package/cdn/context-CdkhXQEw.js.map +0 -1
  355. package/cdn/create-player-BqU8r8H4.js +0 -7
  356. package/cdn/create-player-BqU8r8H4.js.map +0 -1
  357. package/cdn/create-player-p9xCO0yC.js.map +0 -1
  358. package/cdn/delegate-B626t_Va.js +0 -2
  359. package/cdn/delegate-B626t_Va.js.map +0 -1
  360. package/cdn/delegate-BCw-LjP_.js +0 -56
  361. package/cdn/delegate-BCw-LjP_.js.map +0 -1
  362. package/cdn/media-props-mixin-Bd_N2HWy.js +0 -47
  363. package/cdn/media-props-mixin-Bd_N2HWy.js.map +0 -1
  364. package/cdn/media-props-mixin-Dc5dpCV4.js.map +0 -1
  365. package/cdn/minimal-ui-BL9eBXck.js +0 -0
  366. package/cdn/minimal-ui-a1UOKF79.js +0 -0
  367. package/cdn/native-hls-CjRvSXAJ.js +0 -2
  368. package/cdn/player-Cz9-_j7J.js +0 -2
  369. package/cdn/poster-2FPYTjyv.js +0 -2
  370. package/cdn/poster-2FPYTjyv.js.map +0 -1
  371. package/cdn/poster-gVT-J1-z.js +0 -215
  372. package/cdn/poster-gVT-J1-z.js.map +0 -1
  373. package/cdn/ui-CFIgmrlA.js +0 -0
  374. package/cdn/ui-Ccp_LyCf.js +0 -0
  375. package/cdn/volume-slider-Co5X4Z1t.js.map +0 -1
  376. package/cdn/volume-slider-D18sXnIA.js +0 -8
  377. package/cdn/volume-slider-D18sXnIA.js.map +0 -1
@@ -1,5 +1,6 @@
1
- import { C as getAnchorPositionStyle, D as createButton, E as createPopover, F as selectPlaybackRate, G as isUndefined, H as createState, I as selectTextTrack, J as tryHidePopover, L as selectTime, O as createAlertDialog, P as selectPlayback, R as selectVolume, S as getAnchorNameStyle, T as resolveOffsets, U as isFunction, W as isNumber, X as supportsAnchorPositioning, Y as tryShowPopover, _ as getSliderPreviewStyle, a as MediaElement, b as clamp, c as applyStateDataAttrs, d as createWheelStep, f as createTransition, g as getSliderCSSVars, h as ThumbnailCore, j as selectError, k as selectBuffer, l as logMissingFeature, m as createThumbnail, n as PlayerController, o as ContextProvider, p as createTooltip, q as applyStyles, r as SnapshotController, s as ContextConsumer, u as applyElementProps, v as getTimeSliderCSSVars, w as getPopupPositionRect, x as roundToStep, y as createSlider } from "./create-player-p9xCO0yC.js";
2
- import { i as createContext, r as playerContext, s as safeDefine } from "./context-BjrbhiSk.js";
1
+ import { A as ContextConsumer, C as isFunction, E as isUndefined, M as isEditableTarget, N as resolveEventTarget, O as MediaElement, S as AbortControllerRegistry, T as isNumber, b as noop, c as timeFeature, d as playbackRateFeature, f as playbackFeature, g as controlsFeature, h as errorFeature, k as ContextProvider, l as textTrackFeature, m as fullscreenFeature, n as PlayerController, p as pipFeature, r as SnapshotController, s as volumeFeature, u as sourceFeature, v as bufferFeature, w as isNull, x as throwNoTargetError, y as createState } from "./create-player-CyG77t-m.js";
2
+ import { t as listen } from "./listen-w-Aun0yN.js";
3
+ import { i as createContext, o as safeDefine, r as playerContext, t as containerContext } from "./context-CvJ83xGk.js";
3
4
 
4
5
  //#region ../utils/dist/dom/direction.js
5
6
  /** Check whether an element's text direction is right-to-left. */
@@ -10,21 +11,70 @@ function isRTL(element) {
10
11
  }
11
12
 
12
13
  //#endregion
13
- //#region ../utils/dist/object/defaults.js
14
- /**
15
- * Creates a new object with default values filled in for undefined properties.
16
- *
17
- * @example
18
- * ```ts
19
- * const props = { label: undefined, disabled: true };
20
- * const defaultProps = { label: '', disabled: false };
21
- * defaults(props, defaultProps); // { label: '', disabled: true }
22
- * ```
23
- */
24
- function defaults(object, defaultValues) {
25
- const result = { ...defaultValues };
26
- for (const key in object) if (!isUndefined(object[key])) result[key] = object[key];
27
- return result;
14
+ //#region ../utils/dist/dom/supports.js
15
+ function supportsAnchorPositioning() {
16
+ return typeof CSS !== "undefined" && CSS.supports("anchor-name: --a");
17
+ }
18
+
19
+ //#endregion
20
+ //#region ../utils/dist/dom/platform.js
21
+ function isMacOS() {
22
+ return typeof navigator !== "undefined" && /mac/i.test(navigator.userAgent);
23
+ }
24
+
25
+ //#endregion
26
+ //#region ../utils/dist/dom/popover.js
27
+ function tryShowPopover(el) {
28
+ try {
29
+ el?.showPopover?.();
30
+ } catch {}
31
+ }
32
+ function tryHidePopover(el) {
33
+ try {
34
+ el?.hidePopover?.();
35
+ } catch {}
36
+ }
37
+
38
+ //#endregion
39
+ //#region ../utils/dist/string/casing.js
40
+ function kebabCase(str) {
41
+ return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
42
+ }
43
+
44
+ //#endregion
45
+ //#region ../utils/dist/dom/style.js
46
+ function applyStyles(element, styles) {
47
+ for (const [prop, value] of Object.entries(styles)) if (typeof value === "string") {
48
+ const key = prop.startsWith("--") ? prop : kebabCase(prop);
49
+ element.style.setProperty(key, value);
50
+ }
51
+ }
52
+ function resolveCSSLength(el, value) {
53
+ const trimmed = value.trim();
54
+ if (!trimmed) return 0;
55
+ const parsed = Number.parseFloat(trimmed);
56
+ if (Number.isNaN(parsed)) return 0;
57
+ if (/^-?\d*\.?\d+$/.test(trimmed) || trimmed.endsWith("px")) return parsed;
58
+ const doc = el.ownerDocument;
59
+ const root = doc?.documentElement;
60
+ if (trimmed.endsWith("rem")) return parsed * (root ? Number.parseFloat(getComputedStyle(root).fontSize) || 16 : 16);
61
+ if (trimmed.endsWith("em")) return parsed * (el instanceof HTMLElement ? Number.parseFloat(getComputedStyle(el).fontSize) || 16 : 16);
62
+ if (!doc) return parsed;
63
+ const measurementEl = doc.createElement("div");
64
+ measurementEl.style.position = "absolute";
65
+ measurementEl.style.visibility = "hidden";
66
+ measurementEl.style.pointerEvents = "none";
67
+ measurementEl.style.inlineSize = trimmed;
68
+ measurementEl.style.blockSize = "0";
69
+ measurementEl.style.padding = "0";
70
+ measurementEl.style.border = "0";
71
+ measurementEl.style.inset = "0";
72
+ const parent = doc.body ?? doc.documentElement;
73
+ if (!parent) return parsed;
74
+ parent.appendChild(measurementEl);
75
+ const pixels = measurementEl.getBoundingClientRect().width;
76
+ measurementEl.remove();
77
+ return Number.isFinite(pixels) ? pixels : parsed;
28
78
  }
29
79
 
30
80
  //#endregion
@@ -86,100 +136,122 @@ const AlertDialogDataAttrs = {
86
136
  };
87
137
 
88
138
  //#endregion
89
- //#region ../core/dist/dev/core/ui/buffering-indicator/buffering-indicator-core.js
90
- var BufferingIndicatorCore = class BufferingIndicatorCore {
91
- static defaultProps = { delay: 500 };
92
- state = createState({ visible: false });
93
- #props = { ...BufferingIndicatorCore.defaultProps };
94
- #timer = null;
95
- setProps(props) {
96
- this.#props = defaults(props, BufferingIndicatorCore.defaultProps);
97
- }
98
- destroy() {
99
- this.#clearTimer();
100
- }
101
- update(media) {
102
- const buffering = media.waiting && !media.paused;
103
- if (buffering && !this.state.current.visible && !this.#timer) this.#timer = setTimeout(() => {
104
- this.#timer = null;
105
- this.state.patch({ visible: true });
106
- }, this.#props.delay);
107
- else if (!buffering) {
108
- this.#clearTimer();
109
- this.state.patch({ visible: false });
110
- }
111
- }
112
- #clearTimer() {
113
- if (this.#timer !== null) {
114
- clearTimeout(this.#timer);
115
- this.#timer = null;
116
- }
117
- }
118
- };
139
+ //#region ../utils/dist/object/defaults.js
140
+ /**
141
+ * Creates a new object with default values filled in for undefined properties.
142
+ *
143
+ * @example
144
+ * ```ts
145
+ * const props = { label: undefined, disabled: true };
146
+ * const defaultProps = { label: '', disabled: false };
147
+ * defaults(props, defaultProps); // { label: '', disabled: true }
148
+ * ```
149
+ */
150
+ function defaults(object, defaultValues) {
151
+ const result = { ...defaultValues };
152
+ for (const key in object) if (!isUndefined(object[key])) result[key] = object[key];
153
+ return result;
154
+ }
119
155
 
120
156
  //#endregion
121
- //#region ../core/dist/dev/core/ui/buffering-indicator/buffering-indicator-data-attrs.js
122
- const BufferingIndicatorDataAttrs = { visible: "data-visible" };
157
+ //#region ../utils/dist/object/pick.js
158
+ /**
159
+ * Creates a new object with only the specified keys.
160
+ *
161
+ * @example
162
+ * const obj = { a: 1, b: 2, c: 3 };
163
+ * pick(obj, ['a', 'c']); // { a: 1, c: 3 }
164
+ */
165
+ function pick(obj, keys) {
166
+ const result = {};
167
+ for (const key of keys) result[key] = obj[key];
168
+ return result;
169
+ }
123
170
 
124
171
  //#endregion
125
- //#region ../core/dist/dev/core/ui/captions-button/captions-button-core.js
126
- var CaptionsButtonCore = class CaptionsButtonCore {
127
- static defaultProps = {
128
- label: "",
129
- disabled: false
130
- };
131
- state = createState({
132
- subtitlesShowing: false,
133
- availability: "unavailable",
134
- label: ""
135
- });
136
- #props = { ...CaptionsButtonCore.defaultProps };
137
- #media = null;
138
- constructor(props) {
139
- if (props) this.setProps(props);
140
- }
141
- setProps(props) {
142
- this.#props = defaults(props, CaptionsButtonCore.defaultProps);
143
- }
144
- getLabel(state) {
145
- const { label } = this.#props;
146
- if (isFunction(label)) {
147
- const customLabel = label(state);
148
- if (customLabel) return customLabel;
149
- } else if (label) return label;
150
- return state.subtitlesShowing ? "Disable captions" : "Enable captions";
151
- }
152
- getAttrs(state) {
153
- return {
154
- "aria-label": this.getLabel(state),
155
- "aria-disabled": this.#props.disabled ? "true" : void 0
156
- };
157
- }
158
- setMedia(media) {
159
- this.#media = media;
160
- }
161
- getState() {
162
- const media = this.#media;
163
- const availability = media.textTrackList.some((t) => t.kind === "captions" || t.kind === "subtitles") ? "available" : "unavailable";
164
- this.state.patch({
165
- subtitlesShowing: media.subtitlesShowing,
166
- availability
167
- });
168
- this.state.patch({ label: this.getLabel(this.state.current) });
169
- return this.state.current;
170
- }
171
- toggle(media) {
172
- if (this.#props.disabled) return;
173
- media.toggleSubtitles();
174
- }
172
+ //#region ../store/dist/dev/core/selector.js
173
+ const stateContext = {
174
+ target: throwNoTargetError,
175
+ signals: new AbortControllerRegistry(),
176
+ get: throwNoTargetError,
177
+ set: throwNoTargetError
175
178
  };
179
+ /**
180
+ * Create a type-safe selector for a slice's state.
181
+ *
182
+ * The selector returns the slice's state, or `undefined` if the slice
183
+ * is not configured in the store.
184
+ *
185
+ * @example
186
+ * ```ts
187
+ * const selectPlayback = createSelector(playbackSlice);
188
+ * selectPlayback(store.state); // { paused, play, pause, ... } | undefined
189
+ * selectPlayback.displayName; // 'playback' (from slice name)
190
+ * ```
191
+ *
192
+ * @param slice - The slice to create a selector for.
193
+ */
194
+ function createSelector(slice) {
195
+ const initialState = slice.state(stateContext);
196
+ const keys = Object.keys(initialState);
197
+ const firstKey = keys[0];
198
+ if (!firstKey) return Object.assign(() => void 0, { displayName: slice.name });
199
+ return Object.assign((state) => {
200
+ if (!(firstKey in state)) return void 0;
201
+ return pick(state, keys);
202
+ }, { displayName: slice.name });
203
+ }
176
204
 
177
205
  //#endregion
178
- //#region ../core/dist/dev/core/ui/captions-button/captions-button-data-attrs.js
179
- const CaptionsButtonDataAttrs = {
180
- subtitlesShowing: "data-active",
181
- availability: "data-availability"
182
- };
206
+ //#region ../utils/dist/function/throttle.js
207
+ /**
208
+ * Throttle: limits `fn` to at most once per `ms` window.
209
+ *
210
+ * - Default (no options): trailing-edge only — the first call schedules a
211
+ * timer; subsequent calls within the window update the arguments. The
212
+ * function fires once per window with the latest arguments.
213
+ * - `{ leading: true }`: leading + trailing — the first call invokes
214
+ * immediately and opens a cooldown window. Subsequent calls within the
215
+ * window are coalesced to a single trailing-edge invocation.
216
+ */
217
+ function throttle(fn, ms, options) {
218
+ const leading = options?.leading ?? false;
219
+ let timerId = null;
220
+ let latestArgs;
221
+ let hasPending = false;
222
+ function startCooldown() {
223
+ timerId = setTimeout(() => {
224
+ timerId = null;
225
+ if (hasPending) {
226
+ hasPending = false;
227
+ fn(...latestArgs);
228
+ startCooldown();
229
+ }
230
+ }, ms);
231
+ }
232
+ const throttled = (...args) => {
233
+ latestArgs = args;
234
+ if (leading) if (timerId === null) {
235
+ fn(...latestArgs);
236
+ startCooldown();
237
+ } else hasPending = true;
238
+ else {
239
+ if (timerId !== null) return;
240
+ timerId = setTimeout(() => {
241
+ timerId = null;
242
+ fn(...latestArgs);
243
+ }, ms);
244
+ }
245
+ };
246
+ throttled.cancel = () => {
247
+ if (timerId !== null) {
248
+ clearTimeout(timerId);
249
+ timerId = null;
250
+ }
251
+ hasPending = false;
252
+ };
253
+ return throttled;
254
+ }
183
255
 
184
256
  //#endregion
185
257
  //#region ../core/dist/dev/core/ui/controls/controls-core.js
@@ -211,69 +283,6 @@ var ErrorDialogCore = class extends AlertDialogCore {
211
283
  setProps() {}
212
284
  };
213
285
 
214
- //#endregion
215
- //#region ../core/dist/dev/core/ui/fullscreen-button/fullscreen-button-core.js
216
- var FullscreenButtonCore = class FullscreenButtonCore {
217
- static defaultProps = {
218
- label: "",
219
- disabled: false
220
- };
221
- state = createState({
222
- fullscreen: false,
223
- availability: "available",
224
- label: ""
225
- });
226
- #props = { ...FullscreenButtonCore.defaultProps };
227
- #media = null;
228
- constructor(props) {
229
- if (props) this.setProps(props);
230
- }
231
- setProps(props) {
232
- this.#props = defaults(props, FullscreenButtonCore.defaultProps);
233
- }
234
- getLabel(state) {
235
- const { label } = this.#props;
236
- if (isFunction(label)) {
237
- const customLabel = label(state);
238
- if (customLabel) return customLabel;
239
- } else if (label) return label;
240
- return state.fullscreen ? "Exit fullscreen" : "Enter fullscreen";
241
- }
242
- getAttrs(state) {
243
- return {
244
- "aria-label": this.getLabel(state),
245
- "aria-disabled": this.#props.disabled ? "true" : void 0
246
- };
247
- }
248
- setMedia(media) {
249
- this.#media = media;
250
- }
251
- getState() {
252
- const media = this.#media;
253
- this.state.patch({
254
- fullscreen: media.fullscreen,
255
- availability: media.fullscreenAvailability
256
- });
257
- this.state.patch({ label: this.getLabel(this.state.current) });
258
- return this.state.current;
259
- }
260
- async toggle(media) {
261
- if (this.#props.disabled) return;
262
- if (media.fullscreenAvailability !== "available") return;
263
- try {
264
- if (media.fullscreen) await media.exitFullscreen();
265
- else await media.requestFullscreen();
266
- } catch {}
267
- }
268
- };
269
-
270
- //#endregion
271
- //#region ../core/dist/dev/core/ui/fullscreen-button/fullscreen-button-data-attrs.js
272
- const FullscreenButtonDataAttrs = {
273
- fullscreen: "data-fullscreen",
274
- availability: "data-availability"
275
- };
276
-
277
286
  //#endregion
278
287
  //#region ../core/dist/dev/core/ui/mute-button/mute-button-core.js
279
288
  var MuteButtonCore = class MuteButtonCore {
@@ -339,69 +348,6 @@ const MuteButtonDataAttrs = {
339
348
  volumeLevel: "data-volume-level"
340
349
  };
341
350
 
342
- //#endregion
343
- //#region ../core/dist/dev/core/ui/pip-button/pip-button-core.js
344
- var PiPButtonCore = class PiPButtonCore {
345
- static defaultProps = {
346
- label: "",
347
- disabled: false
348
- };
349
- state = createState({
350
- pip: false,
351
- availability: "available",
352
- label: ""
353
- });
354
- #props = { ...PiPButtonCore.defaultProps };
355
- #media = null;
356
- constructor(props) {
357
- if (props) this.setProps(props);
358
- }
359
- setProps(props) {
360
- this.#props = defaults(props, PiPButtonCore.defaultProps);
361
- }
362
- getLabel(state) {
363
- const { label } = this.#props;
364
- if (isFunction(label)) {
365
- const customLabel = label(state);
366
- if (customLabel) return customLabel;
367
- } else if (label) return label;
368
- return state.pip ? "Exit picture-in-picture" : "Enter picture-in-picture";
369
- }
370
- getAttrs(state) {
371
- return {
372
- "aria-label": this.getLabel(state),
373
- "aria-disabled": this.#props.disabled ? "true" : void 0
374
- };
375
- }
376
- setMedia(media) {
377
- this.#media = media;
378
- }
379
- getState() {
380
- const media = this.#media;
381
- this.state.patch({
382
- pip: media.pip,
383
- availability: media.pipAvailability
384
- });
385
- this.state.patch({ label: this.getLabel(this.state.current) });
386
- return this.state.current;
387
- }
388
- async toggle(media) {
389
- if (this.#props.disabled) return;
390
- if (media.pipAvailability !== "available") return;
391
- try {
392
- if (media.pip) await media.exitPictureInPicture();
393
- else await media.requestPictureInPicture();
394
- } catch {}
395
- }
396
- };
397
-
398
- //#endregion
399
- //#region ../core/dist/dev/core/ui/pip-button/pip-button-data-attrs.js
400
- const PiPButtonDataAttrs = {
401
- pip: "data-pip",
402
- availability: "data-availability"
403
- };
404
-
405
351
  //#endregion
406
352
  //#region ../core/dist/dev/core/ui/play-button/play-button-core.js
407
353
  var PlayButtonCore = class PlayButtonCore {
@@ -575,6 +521,17 @@ var PopoverCore = class PopoverCore {
575
521
  }
576
522
  };
577
523
 
524
+ //#endregion
525
+ //#region ../core/dist/dev/core/ui/popover/popover-css-vars.js
526
+ const PopoverCSSVars = {
527
+ sideOffset: "--media-popover-side-offset",
528
+ alignOffset: "--media-popover-align-offset",
529
+ anchorWidth: "--media-popover-anchor-width",
530
+ anchorHeight: "--media-popover-anchor-height",
531
+ availableWidth: "--media-popover-available-width",
532
+ availableHeight: "--media-popover-available-height"
533
+ };
534
+
578
535
  //#endregion
579
536
  //#region ../core/dist/dev/core/ui/popover/popover-data-attrs.js
580
537
  const PopoverDataAttrs = {
@@ -585,22 +542,6 @@ const PopoverDataAttrs = {
585
542
  transitionEnding: "data-ending-style"
586
543
  };
587
544
 
588
- //#endregion
589
- //#region ../core/dist/dev/core/ui/poster/poster-core.js
590
- var PosterCore = class {
591
- #media = null;
592
- setMedia(media) {
593
- this.#media = media;
594
- }
595
- getState() {
596
- return { visible: !this.#media.started };
597
- }
598
- };
599
-
600
- //#endregion
601
- //#region ../core/dist/dev/core/ui/poster/poster-data-attrs.js
602
- const PosterDataAttrs = { visible: "data-visible" };
603
-
604
545
  //#endregion
605
546
  //#region ../core/dist/dev/core/ui/seek-button/seek-button-core.js
606
547
  var SeekButtonCore = class SeekButtonCore {
@@ -663,6 +604,19 @@ const SeekButtonDataAttrs = {
663
604
  direction: "data-direction"
664
605
  };
665
606
 
607
+ //#endregion
608
+ //#region ../utils/dist/number/number.js
609
+ /** Clamp a value between min and max (inclusive). */
610
+ function clamp(value, min, max) {
611
+ return Math.max(min, Math.min(max, value));
612
+ }
613
+ /** Snap a value to the nearest step, offset from min. */
614
+ function roundToStep(value, step, min) {
615
+ const nearest = Math.round((value - min) / step) * step + min;
616
+ const dot = `${step}`.indexOf(".");
617
+ return dot === -1 ? nearest : Number(nearest.toFixed(`${step}`.length - dot - 1));
618
+ }
619
+
666
620
  //#endregion
667
621
  //#region ../core/dist/dev/core/ui/slider/slider-core.js
668
622
  /** Base slider logic: value mapping, ARIA attrs, and step calculations. */
@@ -773,6 +727,15 @@ var SliderCore = class SliderCore {
773
727
  }
774
728
  };
775
729
 
730
+ //#endregion
731
+ //#region ../core/dist/dev/core/ui/slider/slider-css-vars.js
732
+ /** CSS custom property names for slider visual state. */
733
+ const SliderCSSVars = {
734
+ fill: "--media-slider-fill",
735
+ pointer: "--media-slider-pointer",
736
+ buffer: "--media-slider-buffer"
737
+ };
738
+
776
739
  //#endregion
777
740
  //#region ../core/dist/dev/core/ui/slider/slider-data-attrs.js
778
741
  const SliderDataAttrs = {
@@ -784,25 +747,117 @@ const SliderDataAttrs = {
784
747
  };
785
748
 
786
749
  //#endregion
787
- //#region ../core/dist/dev/core/ui/thumbnail/thumbnail-data-attrs.js
788
- const ThumbnailDataAttrs = {
789
- loading: "data-loading",
790
- error: "data-error",
791
- hidden: "data-hidden"
792
- };
793
-
794
- //#endregion
795
- //#region ../core/dist/dev/core/ui/thumbnail/thumbnail-media-fragment.js
796
- /** Parse `url#xywh=x,y,w,h` into a URL and optional sprite coordinates. */
797
- function parseMediaFragment(text, baseURL) {
798
- const parts = text.trim().split("#");
799
- const rawURL = parts[0] ?? "";
800
- const hash = parts[1];
801
- const url = baseURL ? new URL(rawURL, baseURL).href : rawURL;
802
- if (!hash) return { url };
803
- const eqIndex = hash.indexOf("=");
804
- if (eqIndex === -1) return { url };
805
- const keys = hash.slice(0, eqIndex);
750
+ //#region ../core/dist/dev/core/ui/thumbnail/thumbnail-core.js
751
+ var ThumbnailCore = class {
752
+ findActiveThumbnail(thumbnails, time) {
753
+ if (thumbnails.length === 0) return void 0;
754
+ let low = 0;
755
+ let high = thumbnails.length - 1;
756
+ let result;
757
+ while (low <= high) {
758
+ const mid = low + high >>> 1;
759
+ const image = thumbnails[mid];
760
+ if (time >= image.startTime) {
761
+ result = image;
762
+ low = mid + 1;
763
+ } else high = mid - 1;
764
+ }
765
+ return result;
766
+ }
767
+ /**
768
+ * Parse CSS constraint strings into numeric `ThumbnailConstraints`.
769
+ *
770
+ * Accepts any object with string `minWidth`/`maxWidth`/`minHeight`/`maxHeight`
771
+ * properties — `CSSStyleDeclaration` satisfies this structurally.
772
+ */
773
+ parseConstraints(raw) {
774
+ const minW = parseFloat(raw.minWidth);
775
+ const maxW = parseFloat(raw.maxWidth);
776
+ const minH = parseFloat(raw.minHeight);
777
+ const maxH = parseFloat(raw.maxHeight);
778
+ return {
779
+ minWidth: Number.isFinite(minW) ? minW : 0,
780
+ maxWidth: Number.isFinite(maxW) ? maxW : Infinity,
781
+ minHeight: Number.isFinite(minH) ? minH : 0,
782
+ maxHeight: Number.isFinite(maxH) ? maxH : Infinity
783
+ };
784
+ }
785
+ /**
786
+ * Calculate a uniform scale factor that fits `tileWidth × tileHeight` within the
787
+ * given CSS min/max constraints while preserving aspect ratio.
788
+ *
789
+ * - Scales down when the tile exceeds max constraints.
790
+ * - Scales up when the tile is smaller than min constraints.
791
+ * - Returns `1` when no scaling is needed.
792
+ */
793
+ calculateScale(tileWidth, tileHeight, constraints) {
794
+ const { minWidth, maxWidth, minHeight, maxHeight } = constraints;
795
+ const maxRatio = Math.min(maxWidth / tileWidth, maxHeight / tileHeight);
796
+ const minRatio = Math.max(minWidth / tileWidth, minHeight / tileHeight);
797
+ if (Number.isFinite(maxRatio) && maxRatio < 1) return maxRatio;
798
+ if (Number.isFinite(minRatio) && minRatio > 1) return minRatio;
799
+ return 1;
800
+ }
801
+ /**
802
+ * Compute container and image dimensions for the current thumbnail, scaled to
803
+ * fit within the element's CSS min/max constraints.
804
+ *
805
+ * The container clips the sprite sheet via `overflow: hidden`, and the image is
806
+ * positioned with `transform: translate()` to show the correct tile.
807
+ */
808
+ resize(thumbnail, imgNaturalWidth, imgNaturalHeight, constraints) {
809
+ const tileWidth = thumbnail.width ?? imgNaturalWidth;
810
+ const tileHeight = thumbnail.height ?? imgNaturalHeight;
811
+ if (!tileWidth || !tileHeight) return void 0;
812
+ const scale = this.calculateScale(tileWidth, tileHeight, constraints);
813
+ const coordX = thumbnail.coords?.x ?? 0;
814
+ const coordY = thumbnail.coords?.y ?? 0;
815
+ const inset = scale !== 1 ? 1 : 0;
816
+ return {
817
+ scale,
818
+ containerWidth: Math.max(0, Math.floor(tileWidth * scale) - inset * 2),
819
+ containerHeight: Math.max(0, Math.floor(tileHeight * scale) - inset * 2),
820
+ imageWidth: Math.ceil(imgNaturalWidth * scale),
821
+ imageHeight: Math.ceil(imgNaturalHeight * scale),
822
+ offsetX: Math.ceil(coordX * scale) + inset,
823
+ offsetY: Math.ceil(coordY * scale) + inset
824
+ };
825
+ }
826
+ getState(loading, error, thumbnail) {
827
+ return {
828
+ loading,
829
+ error,
830
+ hidden: !loading && !thumbnail
831
+ };
832
+ }
833
+ getAttrs(_state) {
834
+ return {
835
+ role: "img",
836
+ "aria-hidden": "true"
837
+ };
838
+ }
839
+ };
840
+
841
+ //#endregion
842
+ //#region ../core/dist/dev/core/ui/thumbnail/thumbnail-data-attrs.js
843
+ const ThumbnailDataAttrs = {
844
+ loading: "data-loading",
845
+ error: "data-error",
846
+ hidden: "data-hidden"
847
+ };
848
+
849
+ //#endregion
850
+ //#region ../core/dist/dev/core/ui/thumbnail/thumbnail-media-fragment.js
851
+ /** Parse `url#xywh=x,y,w,h` into a URL and optional sprite coordinates. */
852
+ function parseMediaFragment(text, baseURL) {
853
+ const parts = text.trim().split("#");
854
+ const rawURL = parts[0] ?? "";
855
+ const hash = parts[1];
856
+ const url = baseURL ? new URL(rawURL, baseURL).href : rawURL;
857
+ if (!hash) return { url };
858
+ const eqIndex = hash.indexOf("=");
859
+ if (eqIndex === -1) return { url };
860
+ const keys = hash.slice(0, eqIndex);
806
861
  const values = hash.slice(eqIndex + 1).split(",").map(Number);
807
862
  const data = {};
808
863
  for (let i = 0; i < keys.length; i++) {
@@ -1028,7 +1083,7 @@ var TimeSliderCore = class TimeSliderCore extends SliderCore {
1028
1083
  static defaultProps = {
1029
1084
  ...SliderCore.defaultProps,
1030
1085
  label: "Seek",
1031
- commitThrottle: 100
1086
+ changeThrottle: 100
1032
1087
  };
1033
1088
  #props = { ...TimeSliderCore.defaultProps };
1034
1089
  #media = null;
@@ -1048,14 +1103,12 @@ var TimeSliderCore = class TimeSliderCore extends SliderCore {
1048
1103
  }
1049
1104
  getState() {
1050
1105
  const { duration, currentTime, seeking, buffered } = this.#media;
1051
- const { dragging, dragPercent } = this.input;
1052
1106
  super.setProps({
1053
1107
  ...this.#props,
1054
1108
  min: 0,
1055
1109
  max: duration
1056
1110
  });
1057
- const value = dragging ? clamp(dragPercent / 100 * duration, 0, duration) : currentTime;
1058
- const base = super.getSliderState(value);
1111
+ const base = super.getSliderState(currentTime);
1059
1112
  const bufferedEnd = buffered.length > 0 ? buffered[buffered.length - 1][1] : 0;
1060
1113
  const bufferPercent = duration > 0 ? bufferedEnd / duration * 100 : 0;
1061
1114
  return {
@@ -1071,11 +1124,13 @@ var TimeSliderCore = class TimeSliderCore extends SliderCore {
1071
1124
  }
1072
1125
  getAttrs(state) {
1073
1126
  const base = super.getAttrs(state);
1074
- const currentPhrase = formatTimeAsPhrase(state.value);
1127
+ const announceValue = state.dragging ? this.rawValueFromPercent(state.pointerPercent) : state.value;
1128
+ const currentPhrase = formatTimeAsPhrase(announceValue);
1075
1129
  const durationPhrase = formatTimeAsPhrase(state.duration);
1076
1130
  const valuetext = durationPhrase ? `${currentPhrase} of ${durationPhrase}` : currentPhrase;
1077
1131
  return {
1078
1132
  ...base,
1133
+ "aria-valuenow": announceValue,
1079
1134
  "aria-valuetext": valuetext
1080
1135
  };
1081
1136
  }
@@ -1250,238 +1305,1490 @@ const VolumeSliderDataAttrs = {
1250
1305
  };
1251
1306
 
1252
1307
  //#endregion
1253
- //#region src/ui/media-button-element.ts
1254
- /** Abstract base for HTML custom elements that render a media-control button. */
1255
- var MediaButtonElement = class extends MediaElement {
1256
- constructor(..._args) {
1257
- super(..._args);
1258
- this.disabled = false;
1259
- this.label = "";
1260
- }
1261
- static {
1262
- this.properties = {
1263
- label: { type: String },
1264
- disabled: { type: Boolean }
1308
+ //#region ../core/dist/dev/dom/store/selectors.js
1309
+ /** Select the buffer state (buffered ranges, percent buffered). */
1310
+ const selectBuffer = createSelector(bufferFeature);
1311
+ /** Select the controls state (controls visible, user-active). */
1312
+ const selectControls = createSelector(controlsFeature);
1313
+ /** Select the error state (error, dismissed, dismissError). */
1314
+ const selectError = createSelector(errorFeature);
1315
+ /** Select the fullscreen state (fullscreen active, availability). */
1316
+ const selectFullscreen = createSelector(fullscreenFeature);
1317
+ /** Select the PiP state (picture-in-picture active, availability). */
1318
+ const selectPiP = createSelector(pipFeature);
1319
+ /** Select the playback state (paused, ended, play, pause, toggle). */
1320
+ const selectPlayback = createSelector(playbackFeature);
1321
+ /** Select the playback rate state (playbackRate, playbackRates, setPlaybackRate). */
1322
+ const selectPlaybackRate = createSelector(playbackRateFeature);
1323
+ /** Select the source state (src, type). */
1324
+ const selectSource = createSelector(sourceFeature);
1325
+ /** Select the text track state (chapters cues, thumbnail cues). */
1326
+ const selectTextTrack = createSelector(textTrackFeature);
1327
+ /** Select the time state (currentTime, duration, seek). */
1328
+ const selectTime = createSelector(timeFeature);
1329
+ /** Select the volume state (volume, muted, setVolume, setMuted). */
1330
+ const selectVolume = createSelector(volumeFeature);
1331
+
1332
+ //#endregion
1333
+ //#region ../core/dist/dev/dom/hotkey/aria.js
1334
+ const ARIA_MODIFIER_MAP = {
1335
+ shift: "Shift",
1336
+ ctrl: "Control",
1337
+ alt: "Alt",
1338
+ meta: "Meta"
1339
+ };
1340
+ const MODIFIER_ORDER = [
1341
+ "ctrl",
1342
+ "shift",
1343
+ "alt",
1344
+ "meta"
1345
+ ];
1346
+ /**
1347
+ * Convert parsed key bindings to a WAI-ARIA `aria-keyshortcuts` formatted string.
1348
+ *
1349
+ * @example
1350
+ * ```ts
1351
+ * toAriaKeyShortcut(parseHotkeyPattern('Ctrl+Shift+f'));
1352
+ * // "Control+Shift+f"
1353
+ *
1354
+ * toAriaKeyShortcut([...parseHotkeyPattern('k'), ...parseHotkeyPattern('Space')]);
1355
+ * // "k Space"
1356
+ * ```
1357
+ */
1358
+ function toAriaKeyShortcut(bindings) {
1359
+ return bindings.map((b) => {
1360
+ const parts = [];
1361
+ for (const mod of MODIFIER_ORDER) if (b.modifiers.has(mod)) parts.push(ARIA_MODIFIER_MAP[mod]);
1362
+ parts.push(b.originalKey);
1363
+ return parts.join("+");
1364
+ }).join(" ");
1365
+ }
1366
+
1367
+ //#endregion
1368
+ //#region ../core/dist/dev/dom/hotkey/coordinator.js
1369
+ const ACTIVATION_KEYS = new Set([" ", "Enter"]);
1370
+ /** Whether the event is an activation key on an interactive element (button, slider). */
1371
+ function isInteractiveActivation(event) {
1372
+ if (!ACTIVATION_KEYS.has(event.key)) return false;
1373
+ const target = resolveEventTarget(event);
1374
+ if (!(target instanceof HTMLElement)) return false;
1375
+ if (target instanceof HTMLButtonElement) return true;
1376
+ const role = target.getAttribute("role");
1377
+ return role === "button" || role === "slider";
1378
+ }
1379
+ var HotkeyCoordinator = class {
1380
+ #target;
1381
+ #bindings = [];
1382
+ #nextId = 0;
1383
+ #disconnect = null;
1384
+ #docDisconnect = null;
1385
+ /** Action name → bound keys. Controls query this to set `aria-keyshortcuts`. */
1386
+ #ariaRegistry = /* @__PURE__ */ new Map();
1387
+ #destroyed = false;
1388
+ constructor(target) {
1389
+ this.#target = target;
1390
+ }
1391
+ add(options) {
1392
+ const parsed = parseHotkeyPattern(options.keys);
1393
+ const binding = {
1394
+ parsed,
1395
+ options,
1396
+ id: this.#nextId++
1397
+ };
1398
+ this.#bindings.push(binding);
1399
+ this.#sortBindings();
1400
+ if (options.action) this.#addToAriaRegistry(options.action, parsed);
1401
+ if (options.target === "document") this.#connectDocument();
1402
+ else this.#connect();
1403
+ let removed = false;
1404
+ return () => {
1405
+ if (removed) return;
1406
+ removed = true;
1407
+ const idx = this.#bindings.indexOf(binding);
1408
+ if (idx !== -1) this.#bindings.splice(idx, 1);
1409
+ if (options.action) this.#removeFromAriaRegistry(options.action, parsed);
1410
+ this.#maybeDisconnect();
1265
1411
  };
1266
1412
  }
1267
- get $state() {
1268
- return this.core.state;
1269
- }
1270
- #disconnect = null;
1271
- connectedCallback() {
1272
- super.connectedCallback();
1273
- this.#disconnect = new AbortController();
1274
- const buttonProps = createButton({
1275
- onActivate: () => this.activate(this.mediaState.value),
1276
- isDisabled: () => this.disabled || !this.mediaState.value
1277
- });
1278
- applyElementProps(this, buttonProps, { signal: this.#disconnect.signal });
1279
- if (!this.mediaState.value && this.mediaState.displayName) logMissingFeature(this.localName, this.mediaState.displayName);
1413
+ getAriaKeys(action) {
1414
+ const bindings = this.#ariaRegistry.get(action);
1415
+ if (!bindings?.length) return void 0;
1416
+ return toAriaKeyShortcut(bindings);
1280
1417
  }
1281
- disconnectedCallback() {
1282
- super.disconnectedCallback();
1418
+ destroy() {
1419
+ if (this.#destroyed) return;
1420
+ this.#destroyed = true;
1283
1421
  this.#disconnect?.abort();
1284
1422
  this.#disconnect = null;
1423
+ this.#docDisconnect?.abort();
1424
+ this.#docDisconnect = null;
1425
+ this.#bindings = [];
1426
+ this.#ariaRegistry.clear();
1427
+ }
1428
+ #sortBindings() {
1429
+ this.#bindings.sort((a, b) => {
1430
+ const specDiff = b.parsed[0].modifiers.size - a.parsed[0].modifiers.size;
1431
+ if (specDiff !== 0) return specDiff;
1432
+ return a.id - b.id;
1433
+ });
1285
1434
  }
1286
- /** Returns the button's current label derived from media state. */
1287
- getLabel() {
1288
- return this.core.state.current.label || void 0;
1435
+ #connect() {
1436
+ if (this.#disconnect) return;
1437
+ this.#disconnect = new AbortController();
1438
+ listen(this.#target, "keydown", this.#handleEvent, { signal: this.#disconnect.signal });
1439
+ }
1440
+ #connectDocument() {
1441
+ if (this.#docDisconnect) return;
1442
+ this.#docDisconnect = new AbortController();
1443
+ listen(document, "keydown", this.#handleEvent, { signal: this.#docDisconnect.signal });
1444
+ }
1445
+ #maybeDisconnect() {
1446
+ const hasPlayer = this.#bindings.some((b) => b.options.target !== "document");
1447
+ const hasDoc = this.#bindings.some((b) => b.options.target === "document");
1448
+ if (!hasPlayer) {
1449
+ this.#disconnect?.abort();
1450
+ this.#disconnect = null;
1451
+ }
1452
+ if (!hasDoc) {
1453
+ this.#docDisconnect?.abort();
1454
+ this.#docDisconnect = null;
1455
+ }
1289
1456
  }
1290
- willUpdate(changed) {
1291
- super.willUpdate(changed);
1292
- this.core.setProps?.(this);
1457
+ #handleEvent = (event) => {
1458
+ if (event.key === "Unidentified") return;
1459
+ if (isInteractiveActivation(event)) return;
1460
+ const editable = isEditableTarget(event);
1461
+ for (const binding of this.#bindings) {
1462
+ const { options, parsed } = binding;
1463
+ if (options.disabled) continue;
1464
+ if (event.repeat && options.repeatable === false) continue;
1465
+ if (options.target === "document" !== (event.currentTarget === document)) continue;
1466
+ for (const p of parsed) {
1467
+ if (!matchesHotkeyEvent(p, event)) continue;
1468
+ if (editable && p.modifiers.size === 0) continue;
1469
+ event.preventDefault();
1470
+ options.onActivate(event, p.originalKey);
1471
+ return;
1472
+ }
1473
+ }
1474
+ };
1475
+ #addToAriaRegistry(action, bindings) {
1476
+ let existing = this.#ariaRegistry.get(action);
1477
+ if (!existing) {
1478
+ existing = [];
1479
+ this.#ariaRegistry.set(action, existing);
1480
+ }
1481
+ existing.push(...bindings);
1293
1482
  }
1294
- update(changed) {
1295
- super.update(changed);
1296
- const media = this.mediaState.value;
1297
- if (!media) return;
1298
- this.core.setMedia(media);
1299
- const state = this.core.getState();
1300
- applyElementProps(this, this.core.getAttrs?.(state) ?? {});
1301
- applyStateDataAttrs(this, state, this.stateAttrMap);
1483
+ #removeFromAriaRegistry(action, bindings) {
1484
+ const existing = this.#ariaRegistry.get(action);
1485
+ if (!existing) return;
1486
+ const filtered = existing.filter((b) => !bindings.includes(b));
1487
+ if (filtered.length === 0) this.#ariaRegistry.delete(action);
1488
+ else this.#ariaRegistry.set(action, filtered);
1302
1489
  }
1303
1490
  };
1304
1491
 
1305
1492
  //#endregion
1306
- //#region src/ui/context-part-element.ts
1493
+ //#region ../core/dist/dev/dom/hotkey/hotkey.js
1494
+ const MODIFIER_KEYS = new Set([
1495
+ "shift",
1496
+ "ctrl",
1497
+ "alt",
1498
+ "meta"
1499
+ ]);
1307
1500
  /**
1308
- * Abstract base for compound-component part elements that consume a parent
1309
- * context and apply data attributes from `ctx.state` + `ctx.stateAttrMap`.
1310
- *
1311
- * Subclasses only need to declare the `consumer` property:
1501
+ * Parse a key pattern string into one or more bindings.
1312
1502
  *
1503
+ * @example
1313
1504
  * ```ts
1314
- * export class SliderTrackElement extends ContextPartElement<SliderState> {
1315
- * static readonly tagName = 'media-slider-track';
1316
- * protected readonly consumer = new ContextConsumer(this, { context: sliderContext, subscribe: true });
1317
- * }
1505
+ * parseHotkeyPattern('>');
1506
+ * // [{ modifiers: Set(), key: '>', originalKey: '>' }]
1507
+ *
1508
+ * parseHotkeyPattern('0-9');
1509
+ * // 10 bindings, one per digit
1318
1510
  * ```
1319
1511
  */
1320
- var ContextPartElement = class extends MediaElement {
1321
- update(_changed) {
1322
- super.update(_changed);
1323
- const ctx = this.consumer.value;
1324
- if (ctx) applyStateDataAttrs(this, ctx.state, ctx.stateAttrMap);
1512
+ function parseHotkeyPattern(pattern) {
1513
+ if (pattern === "0-9") return Array.from({ length: 10 }, (_, i) => ({
1514
+ modifiers: /* @__PURE__ */ new Set(),
1515
+ key: String(i),
1516
+ originalKey: String(i)
1517
+ }));
1518
+ const segments = pattern.split("+");
1519
+ const rawKey = segments.pop();
1520
+ const modifiers = /* @__PURE__ */ new Set();
1521
+ for (const seg of segments) {
1522
+ const lower = seg.toLowerCase();
1523
+ if (lower === "mod") modifiers.add(isMacOS() ? "meta" : "ctrl");
1524
+ else if (MODIFIER_KEYS.has(lower)) modifiers.add(lower);
1525
+ else console.warn(`[vjs-hotkey] Unknown modifier: "${seg}" in pattern "${pattern}"`);
1526
+ }
1527
+ return [{
1528
+ modifiers,
1529
+ key: rawKey === "Space" ? " " : rawKey.toLowerCase(),
1530
+ originalKey: rawKey
1531
+ }];
1532
+ }
1533
+ /**
1534
+ * Single non-letter character — layout-dependent modifiers (Shift, Alt/Option)
1535
+ * were used to produce the character itself, not as deliberate modifiers
1536
+ * (e.g. Shift+. → ">", Option+Shift → ">" on some Mac layouts).
1537
+ * Letters excluded because Shift changes case intentionally (k vs K).
1538
+ * Named keys excluded because event.key.length > 1 (ArrowLeft, Tab, etc.).
1539
+ */
1540
+ function isImplicitModifierKey(key) {
1541
+ return key.length === 1 && !/[a-z]/i.test(key);
1542
+ }
1543
+ /** Whether a parsed binding matches a keyboard event. */
1544
+ function matchesHotkeyEvent(binding, event) {
1545
+ if (event.key === "Unidentified") return false;
1546
+ if (event.key.toLowerCase() !== binding.key) return false;
1547
+ const implicit = isImplicitModifierKey(event.key);
1548
+ const shiftKey = implicit ? event.shiftKey && binding.modifiers.has("shift") : event.shiftKey;
1549
+ const altKey = implicit ? event.altKey && binding.modifiers.has("alt") : event.altKey;
1550
+ if (shiftKey !== binding.modifiers.has("shift")) return false;
1551
+ if (event.ctrlKey !== binding.modifiers.has("ctrl")) return false;
1552
+ if (altKey !== binding.modifiers.has("alt")) return false;
1553
+ if (event.metaKey !== binding.modifiers.has("meta")) return false;
1554
+ return true;
1555
+ }
1556
+ const coordinators = /* @__PURE__ */ new WeakMap();
1557
+ /** Look up the coordinator for a target element, if one exists. */
1558
+ function findHotkeyCoordinator(target) {
1559
+ return coordinators.get(target);
1560
+ }
1561
+ function getCoordinator(target) {
1562
+ let coordinator = coordinators.get(target);
1563
+ if (!coordinator) {
1564
+ coordinator = new HotkeyCoordinator(target);
1565
+ coordinators.set(target, coordinator);
1325
1566
  }
1326
- };
1567
+ return coordinator;
1568
+ }
1569
+ /**
1570
+ * Register a hotkey binding on a target element.
1571
+ *
1572
+ * @example
1573
+ * ```ts
1574
+ * const cleanup = createHotkey(container, {
1575
+ * keys: 'k',
1576
+ * onActivate: () => store.paused ? store.play() : store.pause(),
1577
+ * });
1578
+ *
1579
+ * // Later: remove the binding
1580
+ * cleanup();
1581
+ * ```
1582
+ *
1583
+ * @returns A cleanup function that removes the binding.
1584
+ */
1585
+ function createHotkey(target, options) {
1586
+ return getCoordinator(target).add(options);
1587
+ }
1327
1588
 
1328
1589
  //#endregion
1329
- //#region src/ui/alert-dialog/context.ts
1330
- const ALERT_DIALOG_CONTEXT_KEY = Symbol("@videojs/alert-dialog");
1331
- const alertDialogContext = createContext(ALERT_DIALOG_CONTEXT_KEY);
1590
+ //#region ../core/dist/dev/dom/ui/dismiss-layer.js
1591
+ function createDismissLayer(options) {
1592
+ const { transition } = options;
1593
+ const state = transition.state;
1594
+ const abort = new AbortController();
1595
+ let docAbort = null;
1596
+ function open() {
1597
+ if (abort.signal.aborted) return null;
1598
+ const { active, status } = state.current;
1599
+ if (active && status !== "ending") return null;
1600
+ if (status === "ending") transition.cancel();
1601
+ return transition.open();
1602
+ }
1603
+ function close(element) {
1604
+ const { active, status } = state.current;
1605
+ if (abort.signal.aborted || !active || status === "ending") return null;
1606
+ return transition.close(element);
1607
+ }
1608
+ function setupDocumentListeners() {
1609
+ cleanupDocumentListeners();
1610
+ if (typeof document === "undefined") return;
1611
+ docAbort = new AbortController();
1612
+ const { signal } = docAbort;
1613
+ listen(document, "keydown", handleKeydown, { signal });
1614
+ options.onDocumentActive?.(signal);
1615
+ }
1616
+ function cleanupDocumentListeners() {
1617
+ docAbort?.abort();
1618
+ docAbort = null;
1619
+ }
1620
+ function handleKeydown(event) {
1621
+ if (event.key !== "Escape") return;
1622
+ if (!state.current.active) return;
1623
+ if (!(options.closeOnEscape?.() ?? true)) return;
1624
+ options.onEscapeDismiss(event);
1625
+ }
1626
+ const unsubscribe = state.subscribe(() => {
1627
+ if (state.current.active) setupDocumentListeners();
1628
+ else cleanupDocumentListeners();
1629
+ });
1630
+ abort.signal.addEventListener("abort", () => {
1631
+ unsubscribe();
1632
+ transition.destroy();
1633
+ cleanupDocumentListeners();
1634
+ });
1635
+ function destroy() {
1636
+ if (abort.signal.aborted) return;
1637
+ abort.abort();
1638
+ }
1639
+ return {
1640
+ input: state,
1641
+ open,
1642
+ close,
1643
+ signal: abort.signal,
1644
+ destroy
1645
+ };
1646
+ }
1332
1647
 
1333
1648
  //#endregion
1334
- //#region src/ui/alert-dialog/alert-dialog-close-element.ts
1335
- var AlertDialogCloseElement = class extends MediaElement {
1336
- constructor(..._args) {
1337
- super(..._args);
1338
- this.disabled = false;
1649
+ //#region ../core/dist/dev/dom/ui/alert-dialog.js
1650
+ function createAlertDialog(options) {
1651
+ const { onOpenChange } = options;
1652
+ let element = null;
1653
+ let previousFocus = null;
1654
+ let elementAbort = null;
1655
+ const layer = createDismissLayer({
1656
+ transition: options.transition,
1657
+ closeOnEscape: options.closeOnEscape,
1658
+ onEscapeDismiss(event) {
1659
+ event.stopPropagation();
1660
+ applyClose();
1661
+ }
1662
+ });
1663
+ const state = layer.input;
1664
+ function applyOpen() {
1665
+ previousFocus = document.activeElement;
1666
+ const opening = layer.open();
1667
+ if (!opening) return;
1668
+ onOpenChange(true);
1669
+ requestAnimationFrame(() => {
1670
+ if (layer.signal.aborted || !state.current.active) return;
1671
+ element?.focus();
1672
+ });
1673
+ opening.then(() => {
1674
+ if (layer.signal.aborted || !state.current.active) return;
1675
+ options.onOpenChangeComplete?.(true);
1676
+ });
1339
1677
  }
1340
- static {
1341
- this.tagName = "media-alert-dialog-close";
1678
+ function applyClose() {
1679
+ const closing = layer.close(element);
1680
+ if (!closing) return;
1681
+ onOpenChange(false);
1682
+ closing.then(() => {
1683
+ if (layer.signal.aborted) return;
1684
+ if (previousFocus) {
1685
+ previousFocus.focus();
1686
+ previousFocus = null;
1687
+ }
1688
+ options.onOpenChangeComplete?.(false);
1689
+ });
1342
1690
  }
1343
- static {
1344
- this.properties = { disabled: { type: Boolean } };
1691
+ function setupElementListeners() {
1692
+ cleanupElementListeners();
1693
+ if (!element) return;
1694
+ elementAbort = new AbortController();
1695
+ const { signal } = elementAbort;
1696
+ listen(element, "click", handleElementClick, { signal });
1345
1697
  }
1346
- #ctx = new ContextConsumer(this, {
1347
- context: alertDialogContext,
1348
- subscribe: true
1349
- });
1350
- #disconnect = null;
1351
- connectedCallback() {
1352
- super.connectedCallback();
1353
- this.#disconnect = new AbortController();
1354
- const buttonProps = createButton({
1355
- onActivate: () => this.#ctx.value?.close(),
1356
- isDisabled: () => this.disabled
1357
- });
1358
- applyElementProps(this, buttonProps, { signal: this.#disconnect.signal });
1698
+ function cleanupElementListeners() {
1699
+ elementAbort?.abort();
1700
+ elementAbort = null;
1359
1701
  }
1360
- disconnectedCallback() {
1361
- super.disconnectedCallback();
1362
- this.#disconnect?.abort();
1363
- this.#disconnect = null;
1702
+ function handleElementClick(event) {
1703
+ if (event.target instanceof HTMLButtonElement) applyClose();
1364
1704
  }
1365
- update(_changed) {
1366
- super.update(_changed);
1367
- const ctx = this.#ctx.value;
1368
- if (ctx) applyStateDataAttrs(this, ctx.state, ctx.stateAttrMap);
1705
+ function setElement(el) {
1706
+ element = el;
1707
+ setupElementListeners();
1369
1708
  }
1370
- };
1709
+ layer.signal.addEventListener("abort", () => {
1710
+ cleanupElementListeners();
1711
+ element = null;
1712
+ previousFocus = null;
1713
+ });
1714
+ return {
1715
+ input: state,
1716
+ open: applyOpen,
1717
+ close: applyClose,
1718
+ setElement,
1719
+ destroy: layer.destroy
1720
+ };
1721
+ }
1371
1722
 
1372
1723
  //#endregion
1373
- //#region src/ui/alert-dialog/alert-dialog-description-element.ts
1374
- var AlertDialogDescriptionElement = class extends ContextPartElement {
1375
- constructor(..._args) {
1376
- super(..._args);
1377
- this.consumer = new ContextConsumer(this, {
1378
- context: alertDialogContext,
1379
- subscribe: true
1380
- });
1724
+ //#region ../core/dist/dev/dom/ui/button.js
1725
+ function createButton(options) {
1726
+ const { onActivate, isDisabled } = options;
1727
+ return {
1728
+ role: "button",
1729
+ tabIndex: 0,
1730
+ onClick(event) {
1731
+ if (isDisabled()) {
1732
+ event.preventDefault();
1733
+ return;
1734
+ }
1735
+ onActivate();
1736
+ },
1737
+ onPointerDown(event) {
1738
+ if (isDisabled()) event.preventDefault();
1739
+ },
1740
+ onMouseDown(event) {
1741
+ if (isDisabled()) event.preventDefault();
1742
+ },
1743
+ onKeyDown(event) {
1744
+ if (event.target !== event.currentTarget) return;
1745
+ if (isDisabled()) {
1746
+ if (event.key !== "Tab") event.preventDefault();
1747
+ return;
1748
+ }
1749
+ if (event.key === "Enter") {
1750
+ event.preventDefault();
1751
+ onActivate();
1752
+ } else if (event.key === " ") event.preventDefault();
1753
+ },
1754
+ onKeyUp(event) {
1755
+ if (event.target !== event.currentTarget) return;
1756
+ if (isDisabled()) return;
1757
+ if (event.key === " ") onActivate();
1758
+ }
1759
+ };
1760
+ }
1761
+
1762
+ //#endregion
1763
+ //#region ../core/dist/dev/dom/ui/popover/popover.js
1764
+ function createPopover(options) {
1765
+ const { onOpenChange, closeOnOutsideClick } = options;
1766
+ let triggerEl = null;
1767
+ let popupEl = null;
1768
+ let hoverTimeout = null;
1769
+ const capturedPointers = /* @__PURE__ */ new Set();
1770
+ const layer = createDismissLayer({
1771
+ transition: options.transition,
1772
+ closeOnEscape: options.closeOnEscape,
1773
+ onEscapeDismiss(event) {
1774
+ event.preventDefault();
1775
+ applyClose("escape", event);
1776
+ },
1777
+ onDocumentActive(signal) {
1778
+ listen(document, "pointerdown", handleDocumentPointerdown, {
1779
+ capture: true,
1780
+ signal
1781
+ });
1782
+ }
1783
+ });
1784
+ const state = layer.input;
1785
+ function clearHoverTimeout() {
1786
+ if (hoverTimeout !== null) {
1787
+ clearTimeout(hoverTimeout);
1788
+ hoverTimeout = null;
1789
+ }
1381
1790
  }
1382
- static {
1383
- this.tagName = "media-alert-dialog-description";
1791
+ function canHover() {
1792
+ return globalThis.matchMedia?.("(hover: hover)")?.matches ?? false;
1384
1793
  }
1385
- update(changed) {
1386
- super.update(changed);
1387
- const descriptionId = this.consumer.value?.state.descriptionId;
1388
- if (descriptionId) this.id = descriptionId;
1794
+ function canOpenOnFocus() {
1795
+ if (!canHover()) return false;
1796
+ return globalThis.matchMedia?.("(pointer: fine)")?.matches ?? false;
1389
1797
  }
1390
- };
1391
-
1392
- //#endregion
1393
- //#region src/ui/alert-dialog/alert-dialog-title-element.ts
1394
- var AlertDialogTitleElement = class extends ContextPartElement {
1395
- constructor(..._args) {
1396
- super(..._args);
1397
- this.consumer = new ContextConsumer(this, {
1398
- context: alertDialogContext,
1399
- subscribe: true
1798
+ function canToggleOnClick() {
1799
+ if (!options.openOnHover?.()) return true;
1800
+ return canHover();
1801
+ }
1802
+ /**
1803
+ * The transition handler manages animation lifecycle via `createState`:
1804
+ *
1805
+ * **Open:** `transition.open()` patches `{ active: true, status: 'starting' }`.
1806
+ * After one RAF it patches `{ status: 'idle' }` and the promise resolves.
1807
+ * Frameworks render `data-starting-style` / `data-ending-style` via
1808
+ * `getPopupAttrs(state)` — no imperative DOM mutation needed.
1809
+ *
1810
+ * **Close:** `transition.close(el)` patches `{ status: 'ending' }` (keeping
1811
+ * `active: true` so the element stays mounted). After a double-RAF it waits
1812
+ * for `getAnimations()` to settle, then patches `{ active: false, status: 'idle' }`.
1813
+ *
1814
+ * `onOpenChange` fires immediately (before animations).
1815
+ * `onOpenChangeComplete` fires after animations finish.
1816
+ */
1817
+ function applyOpen(reason, event) {
1818
+ const opening = layer.open();
1819
+ if (!opening) return;
1820
+ onOpenChange(true, event ? {
1821
+ reason,
1822
+ event
1823
+ } : { reason });
1824
+ opening.then(() => {
1825
+ if (layer.signal.aborted || !state.current.active) return;
1826
+ options.onOpenChangeComplete?.(true);
1400
1827
  });
1401
1828
  }
1402
- static {
1403
- this.tagName = "media-alert-dialog-title";
1829
+ function applyClose(reason, event) {
1830
+ const closing = layer.close(popupEl);
1831
+ if (!closing) return;
1832
+ onOpenChange(false, event ? {
1833
+ reason,
1834
+ event
1835
+ } : { reason });
1836
+ closing.then(() => {
1837
+ if (layer.signal.aborted) return;
1838
+ tryHidePopover(popupEl);
1839
+ options.onOpenChangeComplete?.(false);
1840
+ });
1404
1841
  }
1405
- update(changed) {
1406
- super.update(changed);
1407
- const titleId = this.consumer.value?.state.titleId;
1408
- if (titleId) this.id = titleId;
1842
+ function open(reason = "click") {
1843
+ applyOpen(reason);
1409
1844
  }
1410
- };
1845
+ function close(reason = "click") {
1846
+ applyClose(reason);
1847
+ }
1848
+ function handleDocumentPointerdown(event) {
1849
+ if (!closeOnOutsideClick() || !state.current.active) return;
1850
+ const path = event.composedPath();
1851
+ if (triggerEl && path.includes(triggerEl) || popupEl && path.includes(popupEl)) return;
1852
+ applyClose("outside-click", event);
1853
+ }
1854
+ layer.signal.addEventListener("abort", () => {
1855
+ clearHoverTimeout();
1856
+ capturedPointers.clear();
1857
+ triggerEl = null;
1858
+ popupEl = null;
1859
+ });
1860
+ const triggerProps = {
1861
+ onClick(event) {
1862
+ if (!canToggleOnClick()) return;
1863
+ if (state.current.active && state.current.status !== "ending") applyClose("click", event);
1864
+ else applyOpen("click", event);
1865
+ },
1866
+ onPointerEnter(_event) {
1867
+ if (!options.openOnHover?.()) return;
1868
+ if (!canHover()) return;
1869
+ clearHoverTimeout();
1870
+ if (state.current.active) return;
1871
+ const delay = options.delay?.() ?? 300;
1872
+ hoverTimeout = setTimeout(() => applyOpen("hover"), delay);
1873
+ },
1874
+ onPointerLeave(_event) {
1875
+ if (!options.openOnHover?.()) return;
1876
+ if (!canHover()) return;
1877
+ clearHoverTimeout();
1878
+ if (!state.current.active) return;
1879
+ const closeDelay = options.closeDelay?.() ?? 0;
1880
+ hoverTimeout = setTimeout(() => applyClose("hover"), closeDelay);
1881
+ },
1882
+ onFocusIn(_event) {
1883
+ if (options.openOnHover?.()) {
1884
+ if (!canOpenOnFocus()) return;
1885
+ applyOpen("focus");
1886
+ }
1887
+ },
1888
+ onFocusOut(event) {
1889
+ const relatedTarget = event.relatedTarget;
1890
+ if (relatedTarget && (triggerEl?.contains(relatedTarget) || popupEl?.contains(relatedTarget))) return;
1891
+ if (options.openOnHover?.()) applyClose("blur");
1892
+ }
1893
+ };
1894
+ const popupProps = {
1895
+ onPointerEnter(_event) {
1896
+ if (!options.openOnHover?.()) return;
1897
+ clearHoverTimeout();
1898
+ },
1899
+ onPointerLeave(_event) {
1900
+ if (!options.openOnHover?.()) return;
1901
+ if (capturedPointers.size > 0) return;
1902
+ clearHoverTimeout();
1903
+ if (!state.current.active) return;
1904
+ const closeDelay = options.closeDelay?.() ?? 0;
1905
+ hoverTimeout = setTimeout(() => applyClose("hover"), closeDelay);
1906
+ },
1907
+ onGotPointerCapture(event) {
1908
+ capturedPointers.add(event.pointerId);
1909
+ },
1910
+ onLostPointerCapture(event) {
1911
+ capturedPointers.delete(event.pointerId);
1912
+ },
1913
+ onFocusOut(event) {
1914
+ const relatedTarget = event.relatedTarget;
1915
+ if (relatedTarget && (triggerEl?.contains(relatedTarget) || popupEl?.contains(relatedTarget))) return;
1916
+ applyClose("blur");
1917
+ }
1918
+ };
1919
+ function setTriggerElement(el) {
1920
+ triggerEl = el;
1921
+ }
1922
+ function setPopupElement(el) {
1923
+ if (!el && popupEl && state.current.active) tryHidePopover(popupEl);
1924
+ popupEl = el;
1925
+ if (el) {
1926
+ if (state.current.active) tryShowPopover(el);
1927
+ }
1928
+ }
1929
+ return {
1930
+ input: state,
1931
+ triggerProps,
1932
+ popupProps,
1933
+ get triggerElement() {
1934
+ return triggerEl;
1935
+ },
1936
+ setTriggerElement,
1937
+ setPopupElement,
1938
+ open,
1939
+ close,
1940
+ destroy: layer.destroy
1941
+ };
1942
+ }
1411
1943
 
1412
1944
  //#endregion
1413
- //#region src/ui/error-dialog/error-dialog-element.ts
1414
- const FALLBACK_MESSAGE = "An error occurred. Please try again.";
1415
- let idCounter = 0;
1416
- var ErrorDialogElement = class extends MediaElement {
1945
+ //#region ../core/dist/dev/dom/ui/popover/popover-positioning.js
1946
+ const OPPOSITE_SIDE = {
1947
+ top: "bottom",
1948
+ bottom: "top",
1949
+ left: "right",
1950
+ right: "left"
1951
+ };
1952
+ /**
1953
+ * Get positioning styles for the popup element.
1954
+ *
1955
+ * When the browser supports CSS Anchor Positioning, returns native CSS properties
1956
+ * that reference the provided CSS var names for side/align offsets — no JS offset
1957
+ * values needed.
1958
+ *
1959
+ * When rects are provided and anchor positioning is unsupported, falls back to
1960
+ * manual JS-computed positioning. The caller must resolve offset CSS vars via
1961
+ * `getComputedStyle` and pass them as `offsets`.
1962
+ *
1963
+ * Returns camelCase keys for standard CSS properties and `--*` keys for
1964
+ * custom properties — compatible with both React's `style` prop and
1965
+ * `applyStyles()` from `@videojs/utils/dom`.
1966
+ */
1967
+ function getAnchorPositionStyle(anchorName, opts, triggerRect, popupRect, boundaryRect, offsets, cssVars = PopoverCSSVars) {
1968
+ if (supportsAnchorPositioning()) return getAnchorPositionCSS(anchorName, opts, cssVars);
1969
+ if (triggerRect && popupRect) return {
1970
+ ...getManualPositionStyle(triggerRect, popupRect, opts, offsets ?? {
1971
+ sideOffset: 0,
1972
+ alignOffset: 0
1973
+ }),
1974
+ ...boundaryRect ? getPositioningCSSVars(triggerRect, boundaryRect, opts.side, cssVars) : {},
1975
+ position: "fixed",
1976
+ inset: "auto",
1977
+ margin: "0"
1978
+ };
1979
+ return {};
1980
+ }
1981
+ /** Generate style to set on the trigger for CSS Anchor Positioning. */
1982
+ function getAnchorNameStyle(anchorName) {
1983
+ if (!supportsAnchorPositioning()) return {};
1984
+ return { anchorName: `--${anchorName}` };
1985
+ }
1986
+ function getAnchorPositionCSS(anchorName, opts, cssVars = PopoverCSSVars) {
1987
+ const SIDE_OFFSET_VAR = `var(${cssVars.sideOffset}, 0px)`;
1988
+ const ALIGN_OFFSET_VAR = `var(${cssVars.alignOffset}, 0px)`;
1989
+ const { side, align } = opts;
1990
+ const style = {
1991
+ positionAnchor: `--${anchorName}`,
1992
+ position: "fixed",
1993
+ inset: "auto",
1994
+ margin: "0",
1995
+ justifySelf: "normal",
1996
+ alignSelf: "normal",
1997
+ marginInlineStart: "0",
1998
+ marginBlockStart: "0"
1999
+ };
2000
+ const insetProp = OPPOSITE_SIDE[side];
2001
+ if (side === "top" || side === "bottom") {
2002
+ style[insetProp] = `calc(anchor(${side}) + ${SIDE_OFFSET_VAR})`;
2003
+ if (align === "start") style.left = `calc(anchor(left) + ${ALIGN_OFFSET_VAR})`;
2004
+ else if (align === "end") style.right = `calc(anchor(right) + ${ALIGN_OFFSET_VAR})`;
2005
+ else {
2006
+ style.justifySelf = "anchor-center";
2007
+ style.marginInlineStart = ALIGN_OFFSET_VAR;
2008
+ }
2009
+ } else {
2010
+ style[insetProp] = `calc(anchor(${side}) + ${SIDE_OFFSET_VAR})`;
2011
+ if (align === "start") style.top = `calc(anchor(top) + ${ALIGN_OFFSET_VAR})`;
2012
+ else if (align === "end") style.bottom = `calc(anchor(bottom) + ${ALIGN_OFFSET_VAR})`;
2013
+ else {
2014
+ style.alignSelf = "anchor-center";
2015
+ style.marginBlockStart = ALIGN_OFFSET_VAR;
2016
+ }
2017
+ }
2018
+ return style;
2019
+ }
2020
+ /**
2021
+ * Compute CSS variables for sizing constraints relative to the anchor/boundary.
2022
+ *
2023
+ * Accepts a `cssVars` map so the same logic works for both popover
2024
+ * (`--media-popover-*`) and tooltip (`--media-tooltip-*`) namespaces.
2025
+ */
2026
+ function getPositioningCSSVars(triggerRect, boundaryRect, side, cssVars = PopoverCSSVars) {
2027
+ const vars = {};
2028
+ vars[cssVars.anchorWidth] = `${triggerRect.width}px`;
2029
+ vars[cssVars.anchorHeight] = `${triggerRect.height}px`;
2030
+ if (side === "top" || side === "bottom") {
2031
+ vars[cssVars.availableHeight] = side === "top" ? `${triggerRect.top - boundaryRect.top}px` : `${boundaryRect.bottom - triggerRect.bottom}px`;
2032
+ vars[cssVars.availableWidth] = `${boundaryRect.width}px`;
2033
+ } else {
2034
+ vars[cssVars.availableWidth] = side === "left" ? `${triggerRect.left - boundaryRect.left}px` : `${boundaryRect.right - triggerRect.right}px`;
2035
+ vars[cssVars.availableHeight] = `${boundaryRect.height}px`;
2036
+ }
2037
+ return vars;
2038
+ }
2039
+ /**
2040
+ * Compute manual positioning when CSS Anchor Positioning is not supported.
2041
+ *
2042
+ * Returns inline `top`/`left` styles in **viewport coordinates** for use
2043
+ * with `position: fixed` (the popup is in the top layer). All rects from
2044
+ * `getBoundingClientRect()` are already viewport-relative.
2045
+ *
2046
+ * Offsets are resolved by the caller from CSS custom properties via
2047
+ * `getComputedStyle()` and passed as `offsets`.
2048
+ */
2049
+ function getManualPositionStyle(triggerRect, popupRect, opts, offsets = {
2050
+ sideOffset: 0,
2051
+ alignOffset: 0
2052
+ }) {
2053
+ const { side, align } = opts;
2054
+ const { sideOffset, alignOffset } = offsets;
2055
+ let top = 0;
2056
+ let left = 0;
2057
+ if (side === "top") top = triggerRect.top - popupRect.height - sideOffset;
2058
+ else if (side === "bottom") top = triggerRect.bottom + sideOffset;
2059
+ else if (side === "left") left = triggerRect.left - popupRect.width - sideOffset;
2060
+ else left = triggerRect.right + sideOffset;
2061
+ if (side === "top" || side === "bottom") if (align === "start") left = triggerRect.left + alignOffset;
2062
+ else if (align === "end") left = triggerRect.right - popupRect.width + alignOffset;
2063
+ else left = triggerRect.left + (triggerRect.width - popupRect.width) / 2 + alignOffset;
2064
+ else if (align === "start") top = triggerRect.top + alignOffset;
2065
+ else if (align === "end") top = triggerRect.bottom - popupRect.height + alignOffset;
2066
+ else top = triggerRect.top + (triggerRect.height - popupRect.height) / 2 + alignOffset;
2067
+ return {
2068
+ top: `${top}px`,
2069
+ left: `${left}px`
2070
+ };
2071
+ }
2072
+ /**
2073
+ * Read side-offset and align-offset CSS custom properties from the
2074
+ * popup element's computed style, returning numeric pixel values.
2075
+ */
2076
+ function resolveOffsets(el, cssVars = PopoverCSSVars) {
2077
+ const computed = getComputedStyle(el);
2078
+ return {
2079
+ sideOffset: resolveCSSLength(el, computed.getPropertyValue(cssVars.sideOffset)),
2080
+ alignOffset: resolveCSSLength(el, computed.getPropertyValue(cssVars.alignOffset))
2081
+ };
2082
+ }
2083
+ /**
2084
+ * Measure the popup's layout box for positioning.
2085
+ *
2086
+ * `getBoundingClientRect()` includes active transforms, which causes the
2087
+ * fallback position to drift while opening/closing animations scale the popup.
2088
+ * Using `offsetWidth`/`offsetHeight` preserves the untransformed size.
2089
+ */
2090
+ function getPopupPositionRect(el) {
2091
+ const rect = el.getBoundingClientRect();
2092
+ const width = el.offsetWidth || rect.width;
2093
+ const height = el.offsetHeight || rect.height;
2094
+ const adjustedRect = {
2095
+ ...rect,
2096
+ width,
2097
+ height,
2098
+ right: rect.left + width,
2099
+ bottom: rect.top + height
2100
+ };
2101
+ return {
2102
+ ...adjustedRect,
2103
+ toJSON: () => adjustedRect
2104
+ };
2105
+ }
2106
+
2107
+ //#endregion
2108
+ //#region ../core/dist/dev/dom/utils/pointer.js
2109
+ /** Convert a pointer event position to a 0–100 percent along an element's rect. */
2110
+ function getPercentFromPointerEvent(event, rect, orientation, isRTL) {
2111
+ let ratio;
2112
+ if (orientation === "vertical") ratio = 1 - (event.clientY - rect.top) / rect.height;
2113
+ else if (isRTL) ratio = (rect.right - event.clientX) / rect.width;
2114
+ else ratio = (event.clientX - rect.left) / rect.width;
2115
+ if (!Number.isFinite(ratio)) return 0;
2116
+ return clamp(ratio * 100, 0, 100);
2117
+ }
2118
+
2119
+ //#endregion
2120
+ //#region ../core/dist/dev/dom/ui/slider.js
2121
+ function createSlider(options) {
2122
+ const input = createState({
2123
+ pointerPercent: 0,
2124
+ dragPercent: 0,
2125
+ dragging: false,
2126
+ pointing: false,
2127
+ focused: false
2128
+ });
2129
+ const abort = new AbortController();
2130
+ const changeThrottleMs = options.changeThrottle ?? 0;
2131
+ let isDragging = false, cachedRTL = false, cachedRect = null, capturedPointerId = null, lastDragPercent = 0, committedOnRelease = false;
2132
+ const throttledChange = changeThrottleMs > 0 ? throttle((percent) => options.onValueChange?.(percent), changeThrottleMs, { leading: true }) : null;
2133
+ /** Fire `onValueChange` — throttled during drag when `changeThrottle > 0`. */
2134
+ function fireChange(percent, duringDrag) {
2135
+ if (duringDrag && throttledChange) throttledChange(percent);
2136
+ else options.onValueChange?.(percent);
2137
+ }
2138
+ function releaseCapture() {
2139
+ if (isNull(capturedPointerId)) return;
2140
+ const id = capturedPointerId;
2141
+ capturedPointerId = null;
2142
+ try {
2143
+ options.getElement().releasePointerCapture(id);
2144
+ } catch {}
2145
+ }
2146
+ function endDrag() {
2147
+ if (!isDragging) input.patch({ pointing: false });
2148
+ else {
2149
+ if (!committedOnRelease) options.onValueCommit?.(lastDragPercent);
2150
+ isDragging = false;
2151
+ input.patch({
2152
+ dragging: false,
2153
+ pointing: false
2154
+ });
2155
+ options.onDragEnd?.();
2156
+ }
2157
+ committedOnRelease = false;
2158
+ cleanup();
2159
+ }
2160
+ function cleanup() {
2161
+ throttledChange?.cancel();
2162
+ capturedPointerId = null;
2163
+ cachedRect = null;
2164
+ }
2165
+ const rootProps = {
2166
+ onPointerDown(event) {
2167
+ if (options.isDisabled()) return;
2168
+ event.preventDefault();
2169
+ const el = options.getElement();
2170
+ cachedRect = el.getBoundingClientRect();
2171
+ cachedRTL = options.isRTL();
2172
+ committedOnRelease = false;
2173
+ releaseCapture();
2174
+ capturedPointerId = event.pointerId;
2175
+ el.setPointerCapture(event.pointerId);
2176
+ const percent = getPercentFromPointerEvent(event, cachedRect, options.getOrientation(), cachedRTL);
2177
+ isDragging = true;
2178
+ lastDragPercent = percent;
2179
+ input.patch({
2180
+ pointing: true,
2181
+ dragging: true,
2182
+ pointerPercent: percent,
2183
+ dragPercent: percent
2184
+ });
2185
+ options.onDragStart?.();
2186
+ options.onValueChange?.(percent);
2187
+ options.getThumbElement?.()?.focus({
2188
+ preventScroll: true,
2189
+ focusVisible: false
2190
+ });
2191
+ },
2192
+ onPointerMove(event) {
2193
+ if (options.isDisabled()) return;
2194
+ if (!isNull(capturedPointerId)) {
2195
+ if (event.pointerType !== "touch" && event.buttons === 0) {
2196
+ endDrag();
2197
+ return;
2198
+ }
2199
+ const percent = getPercentFromPointerEvent(event, cachedRect, options.getOrientation(), cachedRTL);
2200
+ lastDragPercent = percent;
2201
+ input.patch({
2202
+ dragPercent: percent,
2203
+ pointerPercent: percent
2204
+ });
2205
+ fireChange(percent, true);
2206
+ return;
2207
+ }
2208
+ const percent = getPercentFromPointerEvent(event, options.getElement().getBoundingClientRect(), options.getOrientation(), options.isRTL());
2209
+ input.patch({
2210
+ pointing: true,
2211
+ pointerPercent: percent
2212
+ });
2213
+ },
2214
+ onPointerUp(event) {
2215
+ if (isNull(capturedPointerId)) return;
2216
+ const percent = getPercentFromPointerEvent(event, cachedRect, options.getOrientation(), cachedRTL);
2217
+ throttledChange?.cancel();
2218
+ options.onValueChange?.(percent);
2219
+ options.onValueCommit?.(percent);
2220
+ committedOnRelease = true;
2221
+ },
2222
+ onPointerLeave() {
2223
+ if (!isNull(capturedPointerId)) return;
2224
+ input.patch({ pointing: false });
2225
+ },
2226
+ onLostPointerCapture() {
2227
+ endDrag();
2228
+ }
2229
+ };
2230
+ const thumbProps = {
2231
+ onKeyDown(event) {
2232
+ if (options.isDisabled()) {
2233
+ if (event.key !== "Tab") event.preventDefault();
2234
+ return;
2235
+ }
2236
+ const stepPercent = options.getStepPercent();
2237
+ const largeStepPercent = options.getLargeStepPercent();
2238
+ const rounded = roundToStep(options.getPercent(), stepPercent, 0);
2239
+ const horizontalSign = options.isRTL() ? -1 : 1;
2240
+ const step = event.shiftKey ? largeStepPercent : stepPercent;
2241
+ let newPercent = null;
2242
+ switch (event.key) {
2243
+ case "ArrowRight":
2244
+ newPercent = rounded + step * horizontalSign;
2245
+ break;
2246
+ case "ArrowLeft":
2247
+ newPercent = rounded - step * horizontalSign;
2248
+ break;
2249
+ case "ArrowUp":
2250
+ newPercent = rounded + step;
2251
+ break;
2252
+ case "ArrowDown":
2253
+ newPercent = rounded - step;
2254
+ break;
2255
+ case "PageUp":
2256
+ newPercent = rounded + largeStepPercent;
2257
+ break;
2258
+ case "PageDown":
2259
+ newPercent = rounded - largeStepPercent;
2260
+ break;
2261
+ case "Home":
2262
+ newPercent = 0;
2263
+ break;
2264
+ case "End":
2265
+ newPercent = 100;
2266
+ break;
2267
+ default:
2268
+ if (!event.metaKey && !event.ctrlKey && !event.altKey && event.key >= "0" && event.key <= "9") newPercent = Number(event.key) * 10;
2269
+ break;
2270
+ }
2271
+ if (newPercent !== null) {
2272
+ event.preventDefault();
2273
+ newPercent = clamp(newPercent, 0, 100);
2274
+ input.patch({
2275
+ pointerPercent: newPercent,
2276
+ dragPercent: newPercent
2277
+ });
2278
+ options.onValueChange?.(newPercent);
2279
+ options.onValueCommit?.(newPercent);
2280
+ }
2281
+ },
2282
+ onFocus() {
2283
+ input.patch({ focused: true });
2284
+ },
2285
+ onBlur() {
2286
+ input.patch({ focused: false });
2287
+ }
2288
+ };
2289
+ function adjustForAlignment(state) {
2290
+ if (!options.adjustPercent || state.thumbAlignment !== "edge") return state;
2291
+ const rootEl = options.getElement();
2292
+ const thumbEl = options.getThumbElement?.();
2293
+ if (!thumbEl) return state;
2294
+ const isHorizontal = state.orientation === "horizontal";
2295
+ const thumbSize = isHorizontal ? thumbEl.offsetWidth : thumbEl.offsetHeight;
2296
+ const trackSize = isHorizontal ? rootEl.offsetWidth : rootEl.offsetHeight;
2297
+ return {
2298
+ ...state,
2299
+ fillPercent: options.adjustPercent(state.fillPercent, thumbSize, trackSize),
2300
+ pointerPercent: options.adjustPercent(state.pointerPercent, thumbSize, trackSize)
2301
+ };
2302
+ }
2303
+ let resizeObserver = null;
2304
+ if (options.onResize) {
2305
+ resizeObserver = new ResizeObserver(() => options.onResize());
2306
+ resizeObserver.observe(options.getElement());
2307
+ }
2308
+ return {
2309
+ input,
2310
+ rootProps,
2311
+ rootStyle: {
2312
+ touchAction: "none",
2313
+ userSelect: "none"
2314
+ },
2315
+ thumbProps,
2316
+ adjustForAlignment,
2317
+ destroy() {
2318
+ if (abort.signal.aborted) return;
2319
+ abort.abort();
2320
+ resizeObserver?.disconnect();
2321
+ releaseCapture();
2322
+ cleanup();
2323
+ }
2324
+ };
2325
+ }
2326
+
2327
+ //#endregion
2328
+ //#region ../core/dist/dev/dom/ui/slider-css-vars.js
2329
+ function getSliderCSSVars(state) {
2330
+ return {
2331
+ [SliderCSSVars.fill]: `${state.fillPercent.toFixed(3)}%`,
2332
+ [SliderCSSVars.pointer]: `${state.pointerPercent.toFixed(3)}%`
2333
+ };
2334
+ }
2335
+ function getTimeSliderCSSVars(state) {
2336
+ return {
2337
+ ...getSliderCSSVars(state),
2338
+ [SliderCSSVars.buffer]: `${state.bufferPercent.toFixed(3)}%`
2339
+ };
2340
+ }
2341
+ /** Compute structural positioning styles for a slider preview element. */
2342
+ function getSliderPreviewStyle(width, overflow) {
2343
+ const halfWidth = width / 2;
2344
+ return {
2345
+ position: "absolute",
2346
+ left: overflow === "visible" ? `calc(var(${SliderCSSVars.pointer}) - ${halfWidth}px)` : `min(max(0px, calc(var(${SliderCSSVars.pointer}) - ${halfWidth}px)), calc(100% - ${width}px))`,
2347
+ width: "max-content",
2348
+ pointerEvents: "none"
2349
+ };
2350
+ }
2351
+
2352
+ //#endregion
2353
+ //#region ../core/dist/dev/dom/ui/thumbnail.js
2354
+ function createThumbnail(options) {
2355
+ const { getContainer, getImg, onStateChange } = options;
2356
+ const core = new ThumbnailCore();
2357
+ const abort = new AbortController();
2358
+ const signal = abort.signal;
2359
+ let loading = false;
2360
+ let error = false;
2361
+ let naturalWidth = 0;
2362
+ let naturalHeight = 0;
2363
+ let lastSrc = "";
2364
+ let imgBound = false;
2365
+ let resizeObserver = null;
2366
+ function onImgLoad() {
2367
+ const img = getImg();
2368
+ if (img) {
2369
+ naturalWidth = img.naturalWidth;
2370
+ naturalHeight = img.naturalHeight;
2371
+ }
2372
+ loading = false;
2373
+ error = false;
2374
+ onStateChange();
2375
+ }
2376
+ function onImgError() {
2377
+ loading = false;
2378
+ error = true;
2379
+ onStateChange();
2380
+ }
2381
+ function bindImg(img) {
2382
+ listen(img, "load", onImgLoad, { signal });
2383
+ listen(img, "error", onImgError, { signal });
2384
+ }
2385
+ function ensureBindings() {
2386
+ if (!imgBound) {
2387
+ const img = getImg();
2388
+ if (img) {
2389
+ bindImg(img);
2390
+ imgBound = true;
2391
+ }
2392
+ }
2393
+ if (!resizeObserver) {
2394
+ const container = getContainer();
2395
+ if (container) {
2396
+ resizeObserver = new ResizeObserver(onStateChange);
2397
+ resizeObserver.observe(container);
2398
+ }
2399
+ }
2400
+ }
2401
+ function updateSrc(url) {
2402
+ ensureBindings();
2403
+ const src = url ?? "";
2404
+ if (src === lastSrc) return;
2405
+ lastSrc = src;
2406
+ if (src) {
2407
+ loading = true;
2408
+ error = false;
2409
+ } else {
2410
+ loading = false;
2411
+ error = false;
2412
+ naturalWidth = 0;
2413
+ naturalHeight = 0;
2414
+ }
2415
+ }
2416
+ function connect() {
2417
+ ensureBindings();
2418
+ const img = getImg();
2419
+ if (img?.complete && lastSrc) {
2420
+ if (img.naturalWidth > 0) {
2421
+ naturalWidth = img.naturalWidth;
2422
+ naturalHeight = img.naturalHeight;
2423
+ loading = false;
2424
+ error = false;
2425
+ } else {
2426
+ loading = false;
2427
+ error = true;
2428
+ }
2429
+ onStateChange();
2430
+ }
2431
+ }
2432
+ function destroy() {
2433
+ abort.abort();
2434
+ resizeObserver?.disconnect();
2435
+ resizeObserver = null;
2436
+ }
2437
+ return {
2438
+ get loading() {
2439
+ return loading;
2440
+ },
2441
+ get error() {
2442
+ return error;
2443
+ },
2444
+ get naturalWidth() {
2445
+ return naturalWidth;
2446
+ },
2447
+ get naturalHeight() {
2448
+ return naturalHeight;
2449
+ },
2450
+ readConstraints() {
2451
+ const el = getContainer();
2452
+ if (!el) return {
2453
+ minWidth: 0,
2454
+ maxWidth: Infinity,
2455
+ minHeight: 0,
2456
+ maxHeight: Infinity
2457
+ };
2458
+ return core.parseConstraints(getComputedStyle(el));
2459
+ },
2460
+ updateSrc,
2461
+ connect,
2462
+ destroy
2463
+ };
2464
+ }
2465
+
2466
+ //#endregion
2467
+ //#region ../core/dist/dev/dom/ui/tooltip/tooltip.js
2468
+ /** Map popover reasons to tooltip reasons, filtering out click/outside-click. */
2469
+ const REASON_MAP = {
2470
+ hover: "hover",
2471
+ focus: "focus",
2472
+ escape: "escape",
2473
+ blur: "blur"
2474
+ };
2475
+ function createTooltip(options) {
2476
+ const popoverOpts = {
2477
+ transition: options.transition,
2478
+ onOpenChange(open, details) {
2479
+ const reason = REASON_MAP[details.reason];
2480
+ if (!reason) return;
2481
+ const group = options.group?.();
2482
+ if (open) group?.notifyOpen();
2483
+ else group?.notifyClose();
2484
+ const tooltipDetails = details.event ? {
2485
+ reason,
2486
+ event: details.event
2487
+ } : { reason };
2488
+ options.onOpenChange(open, tooltipDetails);
2489
+ },
2490
+ closeOnEscape: () => true,
2491
+ closeOnOutsideClick: () => false,
2492
+ openOnHover: () => true,
2493
+ delay: () => {
2494
+ const group = options.group?.();
2495
+ if (group?.shouldSkipDelay()) return 0;
2496
+ return options.delay?.() ?? group?.delay ?? 600;
2497
+ },
2498
+ closeDelay: () => {
2499
+ const group = options.group?.();
2500
+ return options.closeDelay?.() ?? group?.closeDelay ?? 0;
2501
+ }
2502
+ };
2503
+ if (options.onOpenChangeComplete) popoverOpts.onOpenChangeComplete = options.onOpenChangeComplete;
2504
+ const popover = createPopover(popoverOpts);
2505
+ let isPointerDown = false;
2506
+ const { onClick: _, ...baseTriggerProps } = popover.triggerProps;
2507
+ const triggerProps = {
2508
+ ...baseTriggerProps,
2509
+ onPointerDown() {
2510
+ isPointerDown = true;
2511
+ },
2512
+ onPointerEnter(event) {
2513
+ if (options.disabled?.()) return;
2514
+ if (event.pointerType === "touch") return;
2515
+ baseTriggerProps.onPointerEnter(event);
2516
+ },
2517
+ onFocusIn(event) {
2518
+ if (options.disabled?.()) return;
2519
+ if (isPointerDown) {
2520
+ isPointerDown = false;
2521
+ return;
2522
+ }
2523
+ baseTriggerProps.onFocusIn(event);
2524
+ }
2525
+ };
2526
+ const popupProps = {
2527
+ ...popover.popupProps,
2528
+ onPointerEnter(event) {
2529
+ if (options.disableHoverablePopup?.()) return;
2530
+ popover.popupProps.onPointerEnter(event);
2531
+ }
2532
+ };
2533
+ return {
2534
+ ...popover,
2535
+ triggerProps,
2536
+ popupProps,
2537
+ get triggerElement() {
2538
+ return popover.triggerElement;
2539
+ },
2540
+ open: () => popover.open("hover"),
2541
+ close: () => popover.close("hover")
2542
+ };
2543
+ }
2544
+
2545
+ //#endregion
2546
+ //#region ../core/dist/dev/dom/ui/transition.js
2547
+ /**
2548
+ * Manages open/close transition lifecycle via `createState`.
2549
+ *
2550
+ * **Open:** patches `{ active: true, status: 'starting' }`, then after a
2551
+ * double-RAF patches `{ status: 'idle' }` so the browser paints the
2552
+ * initial ("from") state before transitioning.
2553
+ *
2554
+ * **Close:** patches `{ status: 'ending' }` (keeping `active: true` so the
2555
+ * element stays mounted), then after a double-RAF waits for
2556
+ * `getAnimations()` to settle before patching `{ active: false, status: 'idle' }`.
2557
+ */
2558
+ function createTransition() {
2559
+ const state = createState({
2560
+ active: false,
2561
+ status: "idle"
2562
+ });
2563
+ let destroyed = false;
2564
+ let rafId1 = 0;
2565
+ let rafId2 = 0;
2566
+ function open() {
2567
+ cancelAnimationFrame(rafId1);
2568
+ cancelAnimationFrame(rafId2);
2569
+ rafId1 = 0;
2570
+ rafId2 = 0;
2571
+ state.patch({
2572
+ active: true,
2573
+ status: "starting"
2574
+ });
2575
+ return new Promise((resolve) => {
2576
+ rafId1 = requestAnimationFrame(() => {
2577
+ rafId1 = 0;
2578
+ rafId2 = requestAnimationFrame(() => {
2579
+ rafId2 = 0;
2580
+ if (destroyed || !state.current.active) return resolve();
2581
+ state.patch({ status: "idle" });
2582
+ resolve();
2583
+ });
2584
+ });
2585
+ });
2586
+ }
2587
+ function close(el) {
2588
+ cancelAnimationFrame(rafId1);
2589
+ cancelAnimationFrame(rafId2);
2590
+ rafId1 = 0;
2591
+ rafId2 = 0;
2592
+ state.patch({ status: "ending" });
2593
+ return new Promise((resolve) => {
2594
+ rafId1 = requestAnimationFrame(() => {
2595
+ rafId1 = 0;
2596
+ rafId2 = requestAnimationFrame(() => {
2597
+ rafId2 = 0;
2598
+ if (destroyed) return resolve();
2599
+ waitForAnimations(el).finally(() => {
2600
+ if (destroyed || state.current.status !== "ending") return resolve();
2601
+ state.patch({
2602
+ active: false,
2603
+ status: "idle"
2604
+ });
2605
+ resolve();
2606
+ });
2607
+ });
2608
+ });
2609
+ });
2610
+ }
2611
+ function cancel() {
2612
+ cancelAnimationFrame(rafId1);
2613
+ cancelAnimationFrame(rafId2);
2614
+ rafId1 = 0;
2615
+ rafId2 = 0;
2616
+ if (state.current.status !== "idle") state.patch({ status: "idle" });
2617
+ }
2618
+ return {
2619
+ state,
2620
+ open,
2621
+ close,
2622
+ cancel,
2623
+ destroy() {
2624
+ if (destroyed) return;
2625
+ destroyed = true;
2626
+ cancel();
2627
+ }
2628
+ };
2629
+ }
2630
+ function waitForAnimations(el) {
2631
+ if (!el) return Promise.resolve();
2632
+ const animations = el.getAnimations?.() ?? [];
2633
+ if (animations.length === 0) return Promise.resolve();
2634
+ return Promise.all(animations.map((a) => a.finished)).then(noop, noop);
2635
+ }
2636
+
2637
+ //#endregion
2638
+ //#region ../core/dist/dev/dom/ui/wheel-step.js
2639
+ function createWheelStep(options) {
2640
+ return { onWheel(event) {
2641
+ if (options.isDisabled()) return;
2642
+ const direction = Math.sign(event.deltaY);
2643
+ if (direction === 0) return;
2644
+ event.preventDefault();
2645
+ const stepPercent = options.getStepPercent();
2646
+ const newPercent = clamp(options.getPercent() - direction * stepPercent, 0, 100);
2647
+ options.onValueChange?.(newPercent);
2648
+ } };
2649
+ }
2650
+
2651
+ //#endregion
2652
+ //#region ../core/dist/dev/dom/utils/element-props.js
2653
+ /**
2654
+ * Apply props to a DOM element.
2655
+ *
2656
+ * Handles both attributes and event listeners:
2657
+ * - Event props (onClick, onKeyDown, etc.) are attached as listeners
2658
+ * - Boolean props: `true` sets empty attribute, `false` removes
2659
+ * - `undefined` removes the attribute
2660
+ * - Other props are set as string attributes
2661
+ */
2662
+ function applyElementProps(element, props, options) {
2663
+ const signal = options?.signal;
2664
+ for (const [key, value] of Object.entries(props)) if (isFunction(value) && key.startsWith("on")) listen(element, key.slice(2).toLowerCase(), value, signal ? { signal } : void 0);
2665
+ else if (isUndefined(value) || value === false) element.removeAttribute(key);
2666
+ else if (value === true) element.setAttribute(key, "");
2667
+ else element.setAttribute(key, String(value));
2668
+ }
2669
+
2670
+ //#endregion
2671
+ //#region ../core/dist/dev/dom/utils/log.js
2672
+ const warned = /* @__PURE__ */ new Set();
2673
+ function logMissingFeature(displayName, featureName) {
2674
+ const key = `${displayName}:${featureName}`;
2675
+ if (warned.has(key)) return;
2676
+ warned.add(key);
2677
+ console.warn(`${displayName} requires ${featureName} feature`);
2678
+ }
2679
+
2680
+ //#endregion
2681
+ //#region ../core/dist/dev/dom/utils/state-data-attrs.js
2682
+ /**
2683
+ * Apply state as data attributes to an element.
2684
+ *
2685
+ * - `true` → sets `data-keyname=""`
2686
+ * - truthy string/number → sets `data-keyname="value"`
2687
+ * - falsy → removes the attribute
2688
+ *
2689
+ * @example
2690
+ * ```ts
2691
+ * const state = { paused: true, ended: false };
2692
+ * applyStateDataAttrs(element, state);
2693
+ * // element has data-paused="", data-ended is removed
2694
+ * ```
2695
+ */
2696
+ function applyStateDataAttrs(element, state, map) {
2697
+ for (const key in state) {
2698
+ if (map && !(key in map)) continue;
2699
+ const name = map?.[key] ?? toDataAttrName(key), value = state[key];
2700
+ if (value === true) element.setAttribute(name, "");
2701
+ else if (value) element.setAttribute(name, String(value));
2702
+ else element.removeAttribute(name);
2703
+ }
2704
+ }
2705
+ function toDataAttrName(key) {
2706
+ return `data-${key.toLowerCase()}`;
2707
+ }
2708
+
2709
+ //#endregion
2710
+ //#region src/ui/hotkey/aria-key-shortcuts-controller.ts
2711
+ /** Provides `aria-keyshortcuts` for a given hotkey action name. */
2712
+ var AriaKeyShortcutsController = class {
2713
+ #action;
2714
+ #container;
2715
+ constructor(host, action) {
2716
+ this.#action = action;
2717
+ this.#container = new ContextConsumer(host, {
2718
+ context: containerContext,
2719
+ subscribe: true
2720
+ });
2721
+ host.addController(this);
2722
+ }
2723
+ get value() {
2724
+ const container = this.#container.value?.container;
2725
+ if (!container) return void 0;
2726
+ return findHotkeyCoordinator(container)?.getAriaKeys(this.#action);
2727
+ }
2728
+ hostConnected() {}
2729
+ hostDisconnected() {}
2730
+ };
2731
+
2732
+ //#endregion
2733
+ //#region src/ui/media-button-element.ts
2734
+ /** Abstract base for HTML custom elements that render a media-control button. */
2735
+ var MediaButtonElement = class extends MediaElement {
2736
+ constructor(..._args) {
2737
+ super(..._args);
2738
+ this.disabled = false;
2739
+ this.label = "";
2740
+ this.hotkeyAction = void 0;
2741
+ }
1417
2742
  static {
1418
- this.tagName = "media-error-dialog";
2743
+ this.properties = {
2744
+ label: { type: String },
2745
+ disabled: { type: Boolean }
2746
+ };
1419
2747
  }
1420
- #core = new ErrorDialogCore();
1421
- #provider = new ContextProvider(this, { context: alertDialogContext });
1422
- #titleId = `vjs-error-dialog-title-${idCounter++}`;
1423
- #descriptionId = `vjs-error-dialog-desc-${idCounter++}`;
1424
- #errorState = new PlayerController(this, playerContext, selectError);
1425
- #dialog = null;
1426
- #snapshot = null;
1427
- #lastErrorMessage = null;
1428
- constructor() {
1429
- super();
1430
- this.#core.setTitleId(this.#titleId);
1431
- this.#core.setDescriptionId(this.#descriptionId);
2748
+ get $state() {
2749
+ return this.core.state;
1432
2750
  }
2751
+ #disconnect = null;
2752
+ #hotkeyRegistry = null;
1433
2753
  connectedCallback() {
1434
2754
  super.connectedCallback();
1435
- this.#dialog = createAlertDialog({
1436
- transition: createTransition(),
1437
- onOpenChange: (nextOpen) => {
1438
- if (!nextOpen) this.#errorState.value?.dismissError();
1439
- }
2755
+ if (this.destroyed) return;
2756
+ if (this.hotkeyAction && !this.#hotkeyRegistry) this.#hotkeyRegistry = new AriaKeyShortcutsController(this, this.hotkeyAction);
2757
+ this.#disconnect = new AbortController();
2758
+ const buttonProps = createButton({
2759
+ onActivate: () => this.activate(this.mediaState.value),
2760
+ isDisabled: () => this.disabled || !this.mediaState.value
1440
2761
  });
1441
- this.#dialog.setElement(this);
1442
- if (this.#snapshot) this.#snapshot.track(this.#dialog.input);
1443
- else this.#snapshot = new SnapshotController(this, this.#dialog.input);
2762
+ applyElementProps(this, buttonProps, { signal: this.#disconnect.signal });
2763
+ if (!this.mediaState.value && this.mediaState.displayName) logMissingFeature(this.localName, this.mediaState.displayName);
1444
2764
  }
1445
2765
  disconnectedCallback() {
1446
2766
  super.disconnectedCallback();
1447
- this.#dialog?.destroy();
1448
- this.#dialog = null;
2767
+ this.#disconnect?.abort();
2768
+ this.#disconnect = null;
1449
2769
  }
1450
- willUpdate(_changed) {
1451
- super.willUpdate(_changed);
1452
- if (!this.#dialog) return;
1453
- const errorState = this.#errorState.value;
1454
- const hasError = Boolean(errorState?.error);
1455
- const { active: isOpen } = this.#dialog.input.current;
1456
- if (errorState?.error) this.#lastErrorMessage = errorState.error.message?.trim() || null;
1457
- const desc = this.querySelector("media-alert-dialog-description");
1458
- if (desc) desc.textContent = this.#lastErrorMessage ?? FALLBACK_MESSAGE;
1459
- if (hasError && !isOpen) this.#dialog.open();
1460
- else if (!hasError && isOpen) this.#dialog.close();
2770
+ /** Returns the button's current label derived from media state. */
2771
+ getLabel() {
2772
+ return this.core.state.current.label || void 0;
1461
2773
  }
1462
- update(_changed) {
1463
- super.update(_changed);
1464
- if (!this.#dialog) return;
1465
- const input = this.#dialog.input.current;
1466
- this.#core.setInput(input);
1467
- const state = this.#core.getState();
1468
- applyElementProps(this, this.#core.getAttrs(state));
1469
- applyStateDataAttrs(this, state, AlertDialogDataAttrs);
1470
- this.#provider.setValue({
1471
- state,
1472
- stateAttrMap: AlertDialogDataAttrs,
1473
- close: () => this.#dialog?.close()
2774
+ willUpdate(changed) {
2775
+ super.willUpdate(changed);
2776
+ this.core.setProps?.(this);
2777
+ }
2778
+ update(changed) {
2779
+ super.update(changed);
2780
+ const media = this.mediaState.value;
2781
+ if (!media) return;
2782
+ this.core.setMedia(media);
2783
+ const state = this.core.getState();
2784
+ applyElementProps(this, {
2785
+ ...this.core.getAttrs?.(state),
2786
+ "aria-keyshortcuts": this.#hotkeyRegistry?.value
1474
2787
  });
2788
+ applyStateDataAttrs(this, state, this.stateAttrMap);
1475
2789
  }
1476
2790
  };
1477
2791
 
1478
- //#endregion
1479
- //#region src/define/ui/error-dialog.ts
1480
- safeDefine(ErrorDialogElement);
1481
- safeDefine(AlertDialogCloseElement);
1482
- safeDefine(AlertDialogDescriptionElement);
1483
- safeDefine(AlertDialogTitleElement);
1484
-
1485
2792
  //#endregion
1486
2793
  //#region src/ui/mute-button/mute-button-element.ts
1487
2794
  var MuteButtonElement = class extends MediaButtonElement {
@@ -1490,6 +2797,7 @@ var MuteButtonElement = class extends MediaButtonElement {
1490
2797
  this.core = new MuteButtonCore();
1491
2798
  this.stateAttrMap = MuteButtonDataAttrs;
1492
2799
  this.mediaState = new PlayerController(this, playerContext, selectVolume);
2800
+ this.hotkeyAction = "toggleMuted";
1493
2801
  }
1494
2802
  static {
1495
2803
  this.tagName = "media-mute-button";
@@ -1499,10 +2807,6 @@ var MuteButtonElement = class extends MediaButtonElement {
1499
2807
  }
1500
2808
  };
1501
2809
 
1502
- //#endregion
1503
- //#region src/define/ui/mute-button.ts
1504
- safeDefine(MuteButtonElement);
1505
-
1506
2810
  //#endregion
1507
2811
  //#region src/ui/play-button/play-button-element.ts
1508
2812
  var PlayButtonElement = class extends MediaButtonElement {
@@ -1511,6 +2815,7 @@ var PlayButtonElement = class extends MediaButtonElement {
1511
2815
  this.core = new PlayButtonCore();
1512
2816
  this.stateAttrMap = PlayButtonDataAttrs;
1513
2817
  this.mediaState = new PlayerController(this, playerContext, selectPlayback);
2818
+ this.hotkeyAction = "togglePaused";
1514
2819
  }
1515
2820
  static {
1516
2821
  this.tagName = "media-play-button";
@@ -1520,49 +2825,306 @@ var PlayButtonElement = class extends MediaButtonElement {
1520
2825
  }
1521
2826
  };
1522
2827
 
1523
- //#endregion
1524
- //#region src/define/ui/play-button.ts
1525
- safeDefine(PlayButtonElement);
1526
-
1527
2828
  //#endregion
1528
2829
  //#region src/ui/playback-rate-button/playback-rate-button-element.ts
1529
2830
  var PlaybackRateButtonElement = class extends MediaButtonElement {
1530
2831
  constructor(..._args) {
1531
2832
  super(..._args);
1532
- this.core = new PlaybackRateButtonCore();
1533
- this.stateAttrMap = PlaybackRateButtonDataAttrs;
1534
- this.mediaState = new PlayerController(this, playerContext, selectPlaybackRate);
2833
+ this.core = new PlaybackRateButtonCore();
2834
+ this.stateAttrMap = PlaybackRateButtonDataAttrs;
2835
+ this.mediaState = new PlayerController(this, playerContext, selectPlaybackRate);
2836
+ }
2837
+ static {
2838
+ this.tagName = "media-playback-rate-button";
2839
+ }
2840
+ activate(state) {
2841
+ this.core.cycle(state);
2842
+ }
2843
+ };
2844
+
2845
+ //#endregion
2846
+ //#region src/ui/position-controller.ts
2847
+ /**
2848
+ * Reactive controller that manages JS-fallback positioning for floating
2849
+ * popup elements (tooltips, popovers). Tracks scroll, resize, and
2850
+ * ResizeObserver events to keep the popup aligned with its trigger.
2851
+ *
2852
+ * When native CSS Anchor Positioning is supported, `sync()` is a no-op.
2853
+ */
2854
+ var PositionController = class {
2855
+ #host;
2856
+ #abort = null;
2857
+ #frame = 0;
2858
+ #resizeObserver = null;
2859
+ #trigger = null;
2860
+ constructor(host) {
2861
+ this.#host = host;
2862
+ host.addController(this);
2863
+ }
2864
+ /** Discover a trigger element linked via `commandfor` attribute. */
2865
+ findTrigger() {
2866
+ if (!this.#host.id) return null;
2867
+ return this.#host.getRootNode().querySelector(`[commandfor="${this.#host.id}"]`);
2868
+ }
2869
+ /** Start or update position tracking for the given trigger. */
2870
+ sync(trigger) {
2871
+ if (supportsAnchorPositioning()) return;
2872
+ if (!trigger) return;
2873
+ if (this.#abort && this.#trigger === trigger) return;
2874
+ this.cleanup();
2875
+ this.#abort = new AbortController();
2876
+ this.#trigger = trigger;
2877
+ const { signal } = this.#abort;
2878
+ const reposition = () => {
2879
+ cancelAnimationFrame(this.#frame);
2880
+ this.#frame = requestAnimationFrame(() => {
2881
+ if (signal.aborted) return;
2882
+ this.#host.requestUpdate();
2883
+ });
2884
+ };
2885
+ window.addEventListener("scroll", reposition, {
2886
+ capture: true,
2887
+ passive: true,
2888
+ signal
2889
+ });
2890
+ window.addEventListener("resize", reposition, { signal });
2891
+ if (typeof ResizeObserver === "function") {
2892
+ this.#resizeObserver = new ResizeObserver(() => {
2893
+ reposition();
2894
+ });
2895
+ this.#resizeObserver.observe(trigger);
2896
+ this.#resizeObserver.observe(this.#host);
2897
+ }
2898
+ reposition();
2899
+ }
2900
+ /** Stop all position tracking. */
2901
+ cleanup() {
2902
+ this.#abort?.abort();
2903
+ this.#abort = null;
2904
+ this.#trigger = null;
2905
+ cancelAnimationFrame(this.#frame);
2906
+ this.#frame = 0;
2907
+ this.#resizeObserver?.disconnect();
2908
+ this.#resizeObserver = null;
2909
+ }
2910
+ hostDisconnected() {
2911
+ this.cleanup();
2912
+ }
2913
+ hostDestroyed() {
2914
+ this.cleanup();
2915
+ }
2916
+ };
2917
+
2918
+ //#endregion
2919
+ //#region src/ui/popover/popover-element.ts
2920
+ var PopoverElement = class extends MediaElement {
2921
+ constructor(..._args) {
2922
+ super(..._args);
2923
+ this.open = PopoverCore.defaultProps.open;
2924
+ this.defaultOpen = PopoverCore.defaultProps.defaultOpen;
2925
+ this.side = PopoverCore.defaultProps.side;
2926
+ this.align = PopoverCore.defaultProps.align;
2927
+ this.modal = PopoverCore.defaultProps.modal;
2928
+ this.closeOnEscape = PopoverCore.defaultProps.closeOnEscape;
2929
+ this.closeOnOutsideClick = PopoverCore.defaultProps.closeOnOutsideClick;
2930
+ this.openOnHover = PopoverCore.defaultProps.openOnHover;
2931
+ this.delay = PopoverCore.defaultProps.delay;
2932
+ this.closeDelay = PopoverCore.defaultProps.closeDelay;
2933
+ }
2934
+ static {
2935
+ this.tagName = "media-popover";
2936
+ }
2937
+ static {
2938
+ this.properties = {
2939
+ open: { type: Boolean },
2940
+ defaultOpen: {
2941
+ type: Boolean,
2942
+ attribute: "default-open"
2943
+ },
2944
+ side: { type: String },
2945
+ align: { type: String },
2946
+ modal: { type: Boolean },
2947
+ closeOnEscape: {
2948
+ type: Boolean,
2949
+ attribute: "close-on-escape"
2950
+ },
2951
+ closeOnOutsideClick: {
2952
+ type: Boolean,
2953
+ attribute: "close-on-outside-click"
2954
+ },
2955
+ openOnHover: {
2956
+ type: Boolean,
2957
+ attribute: "open-on-hover"
2958
+ },
2959
+ delay: { type: Number },
2960
+ closeDelay: {
2961
+ type: Number,
2962
+ attribute: "close-delay"
2963
+ }
2964
+ };
2965
+ }
2966
+ #core = new PopoverCore();
2967
+ #position = new PositionController(this);
2968
+ #popover = null;
2969
+ #snapshot = null;
2970
+ #disconnect = null;
2971
+ #triggerAbort = null;
2972
+ #currentTrigger = null;
2973
+ connectedCallback() {
2974
+ super.connectedCallback();
2975
+ if (this.destroyed) return;
2976
+ this.#disconnect = new AbortController();
2977
+ this.#popover = createPopover({
2978
+ transition: createTransition(),
2979
+ onOpenChange: (nextOpen, details) => {
2980
+ this.open = nextOpen;
2981
+ this.dispatchEvent(new CustomEvent("open-change", { detail: {
2982
+ open: nextOpen,
2983
+ ...details
2984
+ } }));
2985
+ },
2986
+ closeOnEscape: () => this.closeOnEscape,
2987
+ closeOnOutsideClick: () => this.closeOnOutsideClick,
2988
+ openOnHover: () => this.openOnHover,
2989
+ delay: () => this.delay,
2990
+ closeDelay: () => this.closeDelay
2991
+ });
2992
+ this.#popover.setPopupElement(this);
2993
+ applyElementProps(this, this.#popover.popupProps, { signal: this.#disconnect.signal });
2994
+ if (this.#snapshot) this.#snapshot.track(this.#popover.input);
2995
+ else this.#snapshot = new SnapshotController(this, this.#popover.input);
2996
+ }
2997
+ firstUpdated(changed) {
2998
+ super.firstUpdated(changed);
2999
+ if (this.defaultOpen && !this.open) this.#popover?.open();
3000
+ }
3001
+ disconnectedCallback() {
3002
+ super.disconnectedCallback();
3003
+ this.#disconnect?.abort();
3004
+ this.#disconnect = null;
3005
+ }
3006
+ destroyCallback() {
3007
+ this.#cleanupTrigger();
3008
+ this.#popover?.destroy();
3009
+ super.destroyCallback();
3010
+ }
3011
+ willUpdate(changed) {
3012
+ super.willUpdate(changed);
3013
+ this.#core.setProps(this);
3014
+ if (this.#popover && changed.has("open")) {
3015
+ const { active: interactionOpen } = this.#popover.input.current;
3016
+ if (this.open !== interactionOpen) if (this.open) this.#popover.open();
3017
+ else this.#popover.close();
3018
+ }
3019
+ }
3020
+ update(_changed) {
3021
+ super.update(_changed);
3022
+ if (!this.#popover) return;
3023
+ const triggerEl = this.#position.findTrigger();
3024
+ this.#syncTrigger(triggerEl);
3025
+ const input = this.#popover.input.current;
3026
+ this.#core.setInput(input);
3027
+ const state = this.#core.getState();
3028
+ applyElementProps(this, this.#core.getPopupAttrs(state));
3029
+ applyStateDataAttrs(this, state, PopoverDataAttrs);
3030
+ if (state.open) tryShowPopover(this);
3031
+ else tryHidePopover(this);
3032
+ if (this.#currentTrigger) {
3033
+ applyElementProps(this.#currentTrigger, this.#core.getTriggerAttrs(state, this.id));
3034
+ applyStyles(this.#currentTrigger, getAnchorNameStyle(this.id));
3035
+ }
3036
+ if (!state.open) {
3037
+ this.#position.cleanup();
3038
+ return;
3039
+ }
3040
+ const posOpts = {
3041
+ side: state.side,
3042
+ align: state.align
3043
+ };
3044
+ if (supportsAnchorPositioning()) applyStyles(this, getAnchorPositionStyle(this.id, posOpts));
3045
+ else {
3046
+ const triggerRect = this.#currentTrigger?.getBoundingClientRect();
3047
+ const selfRect = getPopupPositionRect(this);
3048
+ const boundaryRect = document.documentElement.getBoundingClientRect();
3049
+ const offsets = resolveOffsets(this);
3050
+ applyStyles(this, getAnchorPositionStyle(this.id, posOpts, triggerRect, selfRect, boundaryRect, offsets));
3051
+ }
3052
+ this.#position.sync(this.#currentTrigger);
3053
+ }
3054
+ #syncTrigger(triggerEl) {
3055
+ if (triggerEl === this.#currentTrigger) return;
3056
+ this.#position.cleanup();
3057
+ this.#cleanupTrigger();
3058
+ this.#currentTrigger = triggerEl;
3059
+ this.#popover?.setTriggerElement(triggerEl);
3060
+ if (triggerEl && this.#popover) {
3061
+ this.#triggerAbort = new AbortController();
3062
+ applyElementProps(triggerEl, this.#popover.triggerProps, { signal: this.#triggerAbort.signal });
3063
+ }
3064
+ }
3065
+ #cleanupTrigger() {
3066
+ if (this.#currentTrigger) {
3067
+ applyElementProps(this.#currentTrigger, {
3068
+ "aria-expanded": void 0,
3069
+ "aria-haspopup": void 0,
3070
+ "aria-controls": void 0
3071
+ });
3072
+ this.#currentTrigger.style.removeProperty("anchor-name");
3073
+ }
3074
+ this.#triggerAbort?.abort();
3075
+ this.#triggerAbort = null;
3076
+ this.#currentTrigger = null;
3077
+ }
3078
+ };
3079
+
3080
+ //#endregion
3081
+ //#region src/ui/seek-button/seek-button-element.ts
3082
+ var SeekButtonElement = class extends MediaButtonElement {
3083
+ constructor(..._args) {
3084
+ super(..._args);
3085
+ this.seconds = SeekButtonCore.defaultProps.seconds;
3086
+ this.core = new SeekButtonCore();
3087
+ this.stateAttrMap = SeekButtonDataAttrs;
3088
+ this.mediaState = new PlayerController(this, playerContext, selectTime);
1535
3089
  }
1536
3090
  static {
1537
- this.tagName = "media-playback-rate-button";
3091
+ this.tagName = "media-seek-button";
3092
+ }
3093
+ static {
3094
+ this.properties = {
3095
+ ...MediaButtonElement.properties,
3096
+ seconds: { type: Number }
3097
+ };
1538
3098
  }
1539
3099
  activate(state) {
1540
- this.core.cycle(state);
3100
+ this.core.seek(state);
1541
3101
  }
1542
3102
  };
1543
3103
 
1544
3104
  //#endregion
1545
- //#region src/define/ui/playback-rate-button.ts
1546
- safeDefine(PlaybackRateButtonElement);
3105
+ //#region src/ui/tooltip/context.ts
3106
+ const TOOLTIP_GROUP_CONTEXT_KEY = Symbol("@videojs/tooltip-group");
3107
+ const tooltipGroupContext = createContext(TOOLTIP_GROUP_CONTEXT_KEY);
1547
3108
 
1548
3109
  //#endregion
1549
- //#region src/ui/popover/popover-element.ts
1550
- var PopoverElement = class extends MediaElement {
3110
+ //#region src/ui/tooltip/tooltip-element.ts
3111
+ function isLabelTrigger(el) {
3112
+ return "$state" in el;
3113
+ }
3114
+ var TooltipElement = class extends MediaElement {
1551
3115
  constructor(..._args) {
1552
3116
  super(..._args);
1553
- this.open = PopoverCore.defaultProps.open;
1554
- this.defaultOpen = PopoverCore.defaultProps.defaultOpen;
1555
- this.side = PopoverCore.defaultProps.side;
1556
- this.align = PopoverCore.defaultProps.align;
1557
- this.modal = PopoverCore.defaultProps.modal;
1558
- this.closeOnEscape = PopoverCore.defaultProps.closeOnEscape;
1559
- this.closeOnOutsideClick = PopoverCore.defaultProps.closeOnOutsideClick;
1560
- this.openOnHover = PopoverCore.defaultProps.openOnHover;
1561
- this.delay = PopoverCore.defaultProps.delay;
1562
- this.closeDelay = PopoverCore.defaultProps.closeDelay;
3117
+ this.open = TooltipCore.defaultProps.open;
3118
+ this.defaultOpen = TooltipCore.defaultProps.defaultOpen;
3119
+ this.side = TooltipCore.defaultProps.side;
3120
+ this.align = TooltipCore.defaultProps.align;
3121
+ this.delay = TooltipCore.defaultProps.delay;
3122
+ this.closeDelay = TooltipCore.defaultProps.closeDelay;
3123
+ this.disableHoverablePopup = TooltipCore.defaultProps.disableHoverablePopup;
3124
+ this.disabled = TooltipCore.defaultProps.disabled;
1563
3125
  }
1564
3126
  static {
1565
- this.tagName = "media-popover";
3127
+ this.tagName = "media-tooltip";
1566
3128
  }
1567
3129
  static {
1568
3130
  this.properties = {
@@ -1573,41 +3135,31 @@ var PopoverElement = class extends MediaElement {
1573
3135
  },
1574
3136
  side: { type: String },
1575
3137
  align: { type: String },
1576
- modal: { type: Boolean },
1577
- closeOnEscape: {
1578
- type: Boolean,
1579
- attribute: "close-on-escape"
1580
- },
1581
- closeOnOutsideClick: {
1582
- type: Boolean,
1583
- attribute: "close-on-outside-click"
1584
- },
1585
- openOnHover: {
1586
- type: Boolean,
1587
- attribute: "open-on-hover"
1588
- },
1589
3138
  delay: { type: Number },
1590
3139
  closeDelay: {
1591
3140
  type: Number,
1592
3141
  attribute: "close-delay"
1593
- }
3142
+ },
3143
+ disableHoverablePopup: {
3144
+ type: Boolean,
3145
+ attribute: "disable-hoverable-popup"
3146
+ },
3147
+ disabled: { type: Boolean }
1594
3148
  };
1595
3149
  }
1596
- #core = new PopoverCore();
1597
- #popover = null;
3150
+ #core = new TooltipCore();
3151
+ #groupConsumer = new ContextConsumer(this, { context: tooltipGroupContext });
3152
+ #position = new PositionController(this);
3153
+ #tooltip = null;
1598
3154
  #snapshot = null;
1599
3155
  #disconnect = null;
1600
3156
  #triggerAbort = null;
1601
3157
  #currentTrigger = null;
1602
- #positionAbort = null;
1603
- #positionFrame = 0;
1604
- #resizeObserver = null;
1605
- #positionTrigger = null;
1606
3158
  connectedCallback() {
1607
3159
  super.connectedCallback();
1608
3160
  if (this.destroyed) return;
1609
3161
  this.#disconnect = new AbortController();
1610
- this.#popover = createPopover({
3162
+ this.#tooltip = createTooltip({
1611
3163
  transition: createTransition(),
1612
3164
  onOpenChange: (nextOpen, details) => {
1613
3165
  this.open = nextOpen;
@@ -1616,259 +3168,354 @@ var PopoverElement = class extends MediaElement {
1616
3168
  ...details
1617
3169
  } }));
1618
3170
  },
1619
- closeOnEscape: () => this.closeOnEscape,
1620
- closeOnOutsideClick: () => this.closeOnOutsideClick,
1621
- openOnHover: () => this.openOnHover,
1622
3171
  delay: () => this.delay,
1623
- closeDelay: () => this.closeDelay
3172
+ closeDelay: () => this.closeDelay,
3173
+ disableHoverablePopup: () => this.disableHoverablePopup,
3174
+ disabled: () => this.disabled,
3175
+ group: () => this.#groupConsumer.value
1624
3176
  });
1625
- this.#popover.setPopupElement(this);
1626
- applyElementProps(this, this.#popover.popupProps, { signal: this.#disconnect.signal });
1627
- if (this.#snapshot) this.#snapshot.track(this.#popover.input);
1628
- else this.#snapshot = new SnapshotController(this, this.#popover.input);
3177
+ this.#tooltip.setPopupElement(this);
3178
+ applyElementProps(this, this.#tooltip.popupProps, { signal: this.#disconnect.signal });
3179
+ if (this.#snapshot) this.#snapshot.track(this.#tooltip.input);
3180
+ else this.#snapshot = new SnapshotController(this, this.#tooltip.input);
1629
3181
  }
1630
3182
  firstUpdated(changed) {
1631
3183
  super.firstUpdated(changed);
1632
- if (this.defaultOpen && !this.open) this.#popover?.open();
3184
+ if (this.defaultOpen && !this.open) this.#tooltip?.open();
1633
3185
  }
1634
3186
  disconnectedCallback() {
1635
3187
  super.disconnectedCallback();
1636
- this.#cleanupPositioning();
3188
+ this.#cleanupTrigger();
3189
+ this.#tooltip?.destroy();
3190
+ this.#tooltip = null;
1637
3191
  this.#disconnect?.abort();
1638
3192
  this.#disconnect = null;
1639
3193
  }
1640
- destroyCallback() {
1641
- this.#cleanupPositioning();
1642
- this.#cleanupTrigger();
1643
- this.#popover?.destroy();
1644
- super.destroyCallback();
1645
- }
1646
3194
  willUpdate(changed) {
1647
3195
  super.willUpdate(changed);
1648
3196
  this.#core.setProps(this);
1649
- if (this.#popover && changed.has("open")) {
1650
- const { active: interactionOpen } = this.#popover.input.current;
1651
- if (this.open !== interactionOpen) if (this.open) this.#popover.open();
1652
- else this.#popover.close();
3197
+ if (this.#tooltip && changed.has("open")) {
3198
+ const { active: interactionOpen } = this.#tooltip.input.current;
3199
+ if (this.open !== interactionOpen) if (this.open) this.#tooltip.open();
3200
+ else this.#tooltip.close();
1653
3201
  }
1654
3202
  }
1655
3203
  update(_changed) {
1656
3204
  super.update(_changed);
1657
- if (!this.#popover) return;
1658
- const triggerEl = this.#findTrigger();
3205
+ if (!this.#tooltip) return;
3206
+ const triggerEl = this.#position.findTrigger();
1659
3207
  this.#syncTrigger(triggerEl);
1660
- const input = this.#popover.input.current;
3208
+ const input = this.#tooltip.input.current;
1661
3209
  this.#core.setInput(input);
1662
3210
  const state = this.#core.getState();
1663
3211
  applyElementProps(this, this.#core.getPopupAttrs(state));
1664
- applyStateDataAttrs(this, state, PopoverDataAttrs);
3212
+ applyStateDataAttrs(this, state, TooltipDataAttrs);
1665
3213
  if (state.open) tryShowPopover(this);
1666
3214
  else tryHidePopover(this);
1667
- if (this.#currentTrigger) {
1668
- applyElementProps(this.#currentTrigger, this.#core.getTriggerAttrs(state, this.id));
1669
- applyStyles(this.#currentTrigger, getAnchorNameStyle(this.id));
1670
- }
3215
+ if (this.#currentTrigger) applyStyles(this.#currentTrigger, getAnchorNameStyle(this.id));
1671
3216
  if (!state.open) {
1672
- this.#cleanupPositioning();
3217
+ this.#position.cleanup();
1673
3218
  return;
1674
3219
  }
1675
3220
  const posOpts = {
1676
3221
  side: state.side,
1677
3222
  align: state.align
1678
3223
  };
1679
- if (supportsAnchorPositioning()) applyStyles(this, getAnchorPositionStyle(this.id, posOpts));
3224
+ if (supportsAnchorPositioning()) applyStyles(this, getAnchorPositionStyle(this.id, posOpts, void 0, void 0, void 0, void 0, TooltipCSSVars));
1680
3225
  else {
1681
3226
  const triggerRect = this.#currentTrigger?.getBoundingClientRect();
1682
3227
  const selfRect = getPopupPositionRect(this);
1683
3228
  const boundaryRect = document.documentElement.getBoundingClientRect();
1684
- const offsets = resolveOffsets(this);
1685
- applyStyles(this, getAnchorPositionStyle(this.id, posOpts, triggerRect, selfRect, boundaryRect, offsets));
3229
+ const offsets = resolveOffsets(this, TooltipCSSVars);
3230
+ applyStyles(this, getAnchorPositionStyle(this.id, posOpts, triggerRect, selfRect, boundaryRect, offsets, TooltipCSSVars));
1686
3231
  }
1687
- this.#syncPositioning();
1688
- }
1689
- #findTrigger() {
1690
- if (!this.id) return null;
1691
- return this.getRootNode().querySelector(`[commandfor="${this.id}"]`);
3232
+ this.#position.sync(this.#currentTrigger);
1692
3233
  }
1693
3234
  #syncTrigger(triggerEl) {
1694
3235
  if (triggerEl === this.#currentTrigger) return;
1695
- this.#cleanupPositioning();
3236
+ this.#position.cleanup();
1696
3237
  this.#cleanupTrigger();
1697
3238
  this.#currentTrigger = triggerEl;
1698
- this.#popover?.setTriggerElement(triggerEl);
1699
- if (triggerEl && this.#popover) {
3239
+ this.#tooltip?.setTriggerElement(triggerEl);
3240
+ if (triggerEl && this.#tooltip) {
1700
3241
  this.#triggerAbort = new AbortController();
1701
- applyElementProps(triggerEl, this.#popover.triggerProps, { signal: this.#triggerAbort.signal });
3242
+ applyElementProps(triggerEl, this.#tooltip.triggerProps, { signal: this.#triggerAbort.signal });
3243
+ if (isLabelTrigger(triggerEl)) {
3244
+ this.#syncContent(triggerEl);
3245
+ triggerEl.$state.subscribe(() => this.#syncContent(triggerEl), { signal: this.#triggerAbort.signal });
3246
+ }
1702
3247
  }
1703
3248
  }
3249
+ #syncContent(triggerEl) {
3250
+ this.textContent = triggerEl.getLabel() ?? "";
3251
+ }
1704
3252
  #cleanupTrigger() {
1705
- if (this.#currentTrigger) {
1706
- applyElementProps(this.#currentTrigger, {
1707
- "aria-expanded": void 0,
1708
- "aria-haspopup": void 0,
1709
- "aria-controls": void 0
1710
- });
1711
- this.#currentTrigger.style.removeProperty("anchor-name");
1712
- }
3253
+ if (this.#currentTrigger) this.#currentTrigger.style.removeProperty("anchor-name");
1713
3254
  this.#triggerAbort?.abort();
1714
3255
  this.#triggerAbort = null;
1715
3256
  this.#currentTrigger = null;
1716
3257
  }
1717
- #syncPositioning() {
1718
- if (supportsAnchorPositioning()) return;
1719
- const triggerEl = this.#currentTrigger;
1720
- if (!triggerEl) return;
1721
- if (this.#positionAbort && this.#positionTrigger === triggerEl) return;
1722
- this.#cleanupPositioning();
1723
- this.#positionAbort = new AbortController();
1724
- this.#positionTrigger = triggerEl;
1725
- const { signal } = this.#positionAbort;
1726
- const reposition = () => {
1727
- cancelAnimationFrame(this.#positionFrame);
1728
- this.#positionFrame = requestAnimationFrame(() => {
1729
- if (signal.aborted) return;
1730
- this.requestUpdate();
1731
- });
3258
+ };
3259
+
3260
+ //#endregion
3261
+ //#region src/ui/tooltip/tooltip-group-element.ts
3262
+ var TooltipGroupElement = class extends MediaElement {
3263
+ constructor(..._args) {
3264
+ super(..._args);
3265
+ this.delay = TooltipGroupCore.defaultProps.delay;
3266
+ this.closeDelay = TooltipGroupCore.defaultProps.closeDelay;
3267
+ this.timeout = TooltipGroupCore.defaultProps.timeout;
3268
+ }
3269
+ static {
3270
+ this.tagName = "media-tooltip-group";
3271
+ }
3272
+ static {
3273
+ this.properties = {
3274
+ delay: { type: Number },
3275
+ closeDelay: {
3276
+ type: Number,
3277
+ attribute: "close-delay"
3278
+ },
3279
+ timeout: { type: Number }
1732
3280
  };
1733
- window.addEventListener("scroll", reposition, {
1734
- capture: true,
1735
- passive: true,
1736
- signal
3281
+ }
3282
+ #core = new TooltipGroupCore();
3283
+ #provider = new ContextProvider(this, {
3284
+ context: tooltipGroupContext,
3285
+ initialValue: this.#core
3286
+ });
3287
+ update(_changed) {
3288
+ super.update(_changed);
3289
+ this.#core.setProps(this);
3290
+ this.#provider.setValue(this.#core);
3291
+ }
3292
+ };
3293
+
3294
+ //#endregion
3295
+ //#region src/ui/alert-dialog/context.ts
3296
+ const ALERT_DIALOG_CONTEXT_KEY = Symbol("@videojs/alert-dialog");
3297
+ const alertDialogContext = createContext(ALERT_DIALOG_CONTEXT_KEY);
3298
+
3299
+ //#endregion
3300
+ //#region src/ui/alert-dialog/alert-dialog-close-element.ts
3301
+ var AlertDialogCloseElement = class extends MediaElement {
3302
+ constructor(..._args) {
3303
+ super(..._args);
3304
+ this.disabled = false;
3305
+ }
3306
+ static {
3307
+ this.tagName = "media-alert-dialog-close";
3308
+ }
3309
+ static {
3310
+ this.properties = { disabled: { type: Boolean } };
3311
+ }
3312
+ #ctx = new ContextConsumer(this, {
3313
+ context: alertDialogContext,
3314
+ subscribe: true
3315
+ });
3316
+ #disconnect = null;
3317
+ connectedCallback() {
3318
+ super.connectedCallback();
3319
+ this.#disconnect = new AbortController();
3320
+ const buttonProps = createButton({
3321
+ onActivate: () => this.#ctx.value?.close(),
3322
+ isDisabled: () => this.disabled
1737
3323
  });
1738
- window.addEventListener("resize", reposition, { signal });
1739
- if (typeof ResizeObserver === "function") {
1740
- this.#resizeObserver = new ResizeObserver(() => {
1741
- reposition();
1742
- });
1743
- this.#resizeObserver.observe(triggerEl);
1744
- this.#resizeObserver.observe(this);
1745
- }
1746
- reposition();
3324
+ applyElementProps(this, buttonProps, { signal: this.#disconnect.signal });
1747
3325
  }
1748
- #cleanupPositioning() {
1749
- this.#positionAbort?.abort();
1750
- this.#positionAbort = null;
1751
- this.#positionTrigger = null;
1752
- cancelAnimationFrame(this.#positionFrame);
1753
- this.#positionFrame = 0;
1754
- this.#resizeObserver?.disconnect();
1755
- this.#resizeObserver = null;
3326
+ disconnectedCallback() {
3327
+ super.disconnectedCallback();
3328
+ this.#disconnect?.abort();
3329
+ this.#disconnect = null;
3330
+ }
3331
+ update(_changed) {
3332
+ super.update(_changed);
3333
+ const ctx = this.#ctx.value;
3334
+ if (ctx) applyStateDataAttrs(this, ctx.state, ctx.stateAttrMap);
3335
+ }
3336
+ };
3337
+
3338
+ //#endregion
3339
+ //#region src/ui/context-part-element.ts
3340
+ /**
3341
+ * Abstract base for compound-component part elements that consume a parent
3342
+ * context and apply data attributes from `ctx.state` + `ctx.stateAttrMap`.
3343
+ *
3344
+ * Subclasses only need to declare the `consumer` property:
3345
+ *
3346
+ * ```ts
3347
+ * export class SliderTrackElement extends ContextPartElement<SliderState> {
3348
+ * static readonly tagName = 'media-slider-track';
3349
+ * protected readonly consumer = new ContextConsumer(this, { context: sliderContext, subscribe: true });
3350
+ * }
3351
+ * ```
3352
+ */
3353
+ var ContextPartElement = class extends MediaElement {
3354
+ update(_changed) {
3355
+ super.update(_changed);
3356
+ const ctx = this.consumer.value;
3357
+ if (ctx) applyStateDataAttrs(this, ctx.state, ctx.stateAttrMap);
1756
3358
  }
1757
3359
  };
1758
3360
 
1759
3361
  //#endregion
1760
- //#region src/define/ui/popover.ts
1761
- safeDefine(PopoverElement);
1762
-
1763
- //#endregion
1764
- //#region src/ui/seek-button/seek-button-element.ts
1765
- var SeekButtonElement = class extends MediaButtonElement {
3362
+ //#region src/ui/alert-dialog/alert-dialog-description-element.ts
3363
+ var AlertDialogDescriptionElement = class extends ContextPartElement {
1766
3364
  constructor(..._args) {
1767
3365
  super(..._args);
1768
- this.seconds = SeekButtonCore.defaultProps.seconds;
1769
- this.core = new SeekButtonCore();
1770
- this.stateAttrMap = SeekButtonDataAttrs;
1771
- this.mediaState = new PlayerController(this, playerContext, selectTime);
1772
- }
1773
- static {
1774
- this.tagName = "media-seek-button";
3366
+ this.consumer = new ContextConsumer(this, {
3367
+ context: alertDialogContext,
3368
+ subscribe: true
3369
+ });
1775
3370
  }
1776
3371
  static {
1777
- this.properties = {
1778
- ...MediaButtonElement.properties,
1779
- seconds: { type: Number }
1780
- };
3372
+ this.tagName = "media-alert-dialog-description";
1781
3373
  }
1782
- activate(state) {
1783
- this.core.seek(state);
3374
+ update(changed) {
3375
+ super.update(changed);
3376
+ const descriptionId = this.consumer.value?.state.descriptionId;
3377
+ if (descriptionId) this.id = descriptionId;
1784
3378
  }
1785
3379
  };
1786
3380
 
1787
3381
  //#endregion
1788
- //#region src/define/ui/seek-button.ts
1789
- safeDefine(SeekButtonElement);
1790
-
1791
- //#endregion
1792
- //#region src/ui/time/time-element.ts
1793
- var TimeElement = class extends MediaElement {
3382
+ //#region src/ui/alert-dialog/alert-dialog-title-element.ts
3383
+ var AlertDialogTitleElement = class extends ContextPartElement {
1794
3384
  constructor(..._args) {
1795
3385
  super(..._args);
1796
- this.type = TimeCore.defaultProps.type;
1797
- this.negativeSign = TimeCore.defaultProps.negativeSign;
1798
- this.label = TimeCore.defaultProps.label;
3386
+ this.consumer = new ContextConsumer(this, {
3387
+ context: alertDialogContext,
3388
+ subscribe: true
3389
+ });
1799
3390
  }
1800
3391
  static {
1801
- this.tagName = "media-time";
3392
+ this.tagName = "media-alert-dialog-title";
3393
+ }
3394
+ update(changed) {
3395
+ super.update(changed);
3396
+ const titleId = this.consumer.value?.state.titleId;
3397
+ if (titleId) this.id = titleId;
1802
3398
  }
3399
+ };
3400
+
3401
+ //#endregion
3402
+ //#region src/ui/controls/context.ts
3403
+ const CONTROLS_CONTEXT_KEY = Symbol("@videojs/controls");
3404
+ const controlsContext = createContext(CONTROLS_CONTEXT_KEY);
3405
+
3406
+ //#endregion
3407
+ //#region src/ui/controls/controls-element.ts
3408
+ var ControlsElement = class extends MediaElement {
1803
3409
  static {
1804
- this.properties = {
1805
- type: { type: String },
1806
- negativeSign: {
1807
- type: String,
1808
- attribute: "negative-sign"
1809
- },
1810
- label: { type: String }
1811
- };
3410
+ this.tagName = "media-controls";
1812
3411
  }
1813
- #core = new TimeCore();
1814
- #state = new PlayerController(this, playerContext, selectTime);
1815
- #signSpan = document.createElement("span");
1816
- #textNode = document.createTextNode("");
3412
+ #core = new ControlsCore();
3413
+ #mediaState = new PlayerController(this, playerContext, selectControls);
3414
+ #provider = new ContextProvider(this, { context: controlsContext });
1817
3415
  connectedCallback() {
1818
3416
  super.connectedCallback();
1819
- if (!this.#signSpan.parentNode) {
1820
- this.#signSpan.setAttribute("aria-hidden", "true");
1821
- this.#signSpan.hidden = true;
1822
- this.appendChild(this.#signSpan);
1823
- this.appendChild(this.#textNode);
1824
- }
1825
- if (!this.#state.value) logMissingFeature(this.localName, this.#state.displayName);
1826
- }
1827
- willUpdate(changed) {
1828
- super.willUpdate(changed);
1829
- this.#core.setProps(this);
3417
+ if (!this.#mediaState.value && this.#mediaState.displayName) logMissingFeature(this.localName, this.#mediaState.displayName);
1830
3418
  }
1831
- update(changed) {
1832
- super.update(changed);
1833
- const media = this.#state.value;
3419
+ update(_changed) {
3420
+ super.update(_changed);
3421
+ const media = this.#mediaState.value;
1834
3422
  if (!media) return;
1835
3423
  this.#core.setMedia(media);
1836
3424
  const state = this.#core.getState();
1837
- this.#signSpan.hidden = !state.negative;
1838
- this.#signSpan.textContent = state.negative ? this.negativeSign : "";
1839
- this.#textNode.textContent = state.text;
1840
- applyElementProps(this, this.#core.getAttrs(state));
1841
- applyStateDataAttrs(this, state, TimeDataAttrs);
3425
+ applyStateDataAttrs(this, state, ControlsDataAttrs);
3426
+ this.#provider.setValue({
3427
+ state,
3428
+ stateAttrMap: ControlsDataAttrs
3429
+ });
1842
3430
  }
1843
3431
  };
1844
3432
 
1845
3433
  //#endregion
1846
- //#region src/ui/time/time-group-element.ts
1847
- var TimeGroupElement = class extends MediaElement {
3434
+ //#region src/ui/controls/controls-group-element.ts
3435
+ var ControlsGroupElement = class extends ContextPartElement {
3436
+ constructor(..._args) {
3437
+ super(..._args);
3438
+ this.consumer = new ContextConsumer(this, {
3439
+ context: controlsContext,
3440
+ subscribe: true
3441
+ });
3442
+ }
1848
3443
  static {
1849
- this.tagName = "media-time-group";
3444
+ this.tagName = "media-controls-group";
3445
+ }
3446
+ connectedCallback() {
3447
+ super.connectedCallback();
3448
+ if (this.hasAttribute("aria-label") || this.hasAttribute("aria-labelledby")) this.setAttribute("role", "group");
1850
3449
  }
1851
3450
  };
1852
3451
 
1853
3452
  //#endregion
1854
- //#region src/ui/time/time-separator-element.ts
1855
- var TimeSeparatorElement = class extends MediaElement {
3453
+ //#region src/ui/error-dialog/error-dialog-element.ts
3454
+ const FALLBACK_MESSAGE = "An error occurred. Please try again.";
3455
+ let idCounter = 0;
3456
+ var ErrorDialogElement = class extends MediaElement {
1856
3457
  static {
1857
- this.tagName = "media-time-separator";
3458
+ this.tagName = "media-error-dialog";
3459
+ }
3460
+ #core = new ErrorDialogCore();
3461
+ #provider = new ContextProvider(this, { context: alertDialogContext });
3462
+ #titleId = `vjs-error-dialog-title-${idCounter++}`;
3463
+ #descriptionId = `vjs-error-dialog-desc-${idCounter++}`;
3464
+ #errorState = new PlayerController(this, playerContext, selectError);
3465
+ #dialog = null;
3466
+ #snapshot = null;
3467
+ #lastErrorMessage = null;
3468
+ constructor() {
3469
+ super();
3470
+ this.#core.setTitleId(this.#titleId);
3471
+ this.#core.setDescriptionId(this.#descriptionId);
1858
3472
  }
1859
3473
  connectedCallback() {
1860
3474
  super.connectedCallback();
1861
- this.setAttribute("aria-hidden", "true");
1862
- if (!this.textContent?.trim()) this.textContent = "/";
3475
+ if (this.destroyed) return;
3476
+ this.#dialog = createAlertDialog({
3477
+ transition: createTransition(),
3478
+ onOpenChange: (nextOpen) => {
3479
+ if (!nextOpen) this.#errorState.value?.dismissError();
3480
+ }
3481
+ });
3482
+ this.#dialog.setElement(this);
3483
+ if (this.#snapshot) this.#snapshot.track(this.#dialog.input);
3484
+ else this.#snapshot = new SnapshotController(this, this.#dialog.input);
3485
+ }
3486
+ disconnectedCallback() {
3487
+ super.disconnectedCallback();
3488
+ this.#dialog?.destroy();
3489
+ this.#dialog = null;
3490
+ }
3491
+ willUpdate(_changed) {
3492
+ super.willUpdate(_changed);
3493
+ if (!this.#dialog) return;
3494
+ const errorState = this.#errorState.value;
3495
+ const hasError = Boolean(errorState?.error);
3496
+ const { active: isOpen } = this.#dialog.input.current;
3497
+ if (errorState?.error) this.#lastErrorMessage = errorState.error.message?.trim() || null;
3498
+ const desc = this.querySelector("media-alert-dialog-description");
3499
+ if (desc) desc.textContent = this.#lastErrorMessage ?? FALLBACK_MESSAGE;
3500
+ if (hasError && !isOpen) this.#dialog.open();
3501
+ else if (!hasError && isOpen) this.#dialog.close();
3502
+ }
3503
+ update(_changed) {
3504
+ super.update(_changed);
3505
+ if (!this.#dialog) return;
3506
+ const input = this.#dialog.input.current;
3507
+ this.#core.setInput(input);
3508
+ const state = this.#core.getState();
3509
+ applyElementProps(this, this.#core.getAttrs(state));
3510
+ applyStateDataAttrs(this, state, AlertDialogDataAttrs);
3511
+ this.#provider.setValue({
3512
+ state,
3513
+ stateAttrMap: AlertDialogDataAttrs,
3514
+ close: () => this.#dialog?.close()
3515
+ });
1863
3516
  }
1864
3517
  };
1865
3518
 
1866
- //#endregion
1867
- //#region src/define/ui/time.ts
1868
- safeDefine(TimeElement);
1869
- safeDefine(TimeGroupElement);
1870
- safeDefine(TimeSeparatorElement);
1871
-
1872
3519
  //#endregion
1873
3520
  //#region src/ui/slider/context.ts
1874
3521
  const SLIDER_CONTEXT_KEY = Symbol("@videojs/slider");
@@ -2147,33 +3794,108 @@ var SliderTrackElement = class extends ContextPartElement {
2147
3794
  };
2148
3795
 
2149
3796
  //#endregion
2150
- //#region src/ui/slider/slider-value-element.ts
2151
- var SliderValueElement = class extends MediaElement {
2152
- constructor(..._args) {
2153
- super(..._args);
2154
- this.type = "current";
2155
- }
3797
+ //#region src/ui/slider/slider-value-element.ts
3798
+ var SliderValueElement = class extends MediaElement {
3799
+ constructor(..._args) {
3800
+ super(..._args);
3801
+ this.type = "current";
3802
+ }
3803
+ static {
3804
+ this.tagName = "media-slider-value";
3805
+ }
3806
+ static {
3807
+ this.properties = { type: { type: String } };
3808
+ }
3809
+ #ctx = new ContextConsumer(this, {
3810
+ context: sliderContext,
3811
+ subscribe: true
3812
+ });
3813
+ connectedCallback() {
3814
+ super.connectedCallback();
3815
+ this.setAttribute("aria-live", "off");
3816
+ }
3817
+ update(_changed) {
3818
+ super.update(_changed);
3819
+ const ctx = this.#ctx.value;
3820
+ if (!ctx) return;
3821
+ const value = this.type === "pointer" ? ctx.pointerValue : ctx.state.value;
3822
+ this.textContent = ctx.formatValue ? ctx.formatValue(value, this.type) : String(Math.round(value));
3823
+ applyStateDataAttrs(this, ctx.state, ctx.stateAttrMap);
3824
+ }
3825
+ };
3826
+
3827
+ //#endregion
3828
+ //#region src/ui/time/time-element.ts
3829
+ var TimeElement = class extends MediaElement {
3830
+ constructor(..._args) {
3831
+ super(..._args);
3832
+ this.type = TimeCore.defaultProps.type;
3833
+ this.negativeSign = TimeCore.defaultProps.negativeSign;
3834
+ this.label = TimeCore.defaultProps.label;
3835
+ }
3836
+ static {
3837
+ this.tagName = "media-time";
3838
+ }
3839
+ static {
3840
+ this.properties = {
3841
+ type: { type: String },
3842
+ negativeSign: {
3843
+ type: String,
3844
+ attribute: "negative-sign"
3845
+ },
3846
+ label: { type: String }
3847
+ };
3848
+ }
3849
+ #core = new TimeCore();
3850
+ #state = new PlayerController(this, playerContext, selectTime);
3851
+ #signSpan = document.createElement("span");
3852
+ #textNode = document.createTextNode("");
3853
+ connectedCallback() {
3854
+ super.connectedCallback();
3855
+ if (!this.#signSpan.parentNode) {
3856
+ this.#signSpan.setAttribute("aria-hidden", "true");
3857
+ this.#signSpan.hidden = true;
3858
+ this.appendChild(this.#signSpan);
3859
+ this.appendChild(this.#textNode);
3860
+ }
3861
+ if (!this.#state.value) logMissingFeature(this.localName, this.#state.displayName);
3862
+ }
3863
+ willUpdate(changed) {
3864
+ super.willUpdate(changed);
3865
+ this.#core.setProps(this);
3866
+ }
3867
+ update(changed) {
3868
+ super.update(changed);
3869
+ const media = this.#state.value;
3870
+ if (!media) return;
3871
+ this.#core.setMedia(media);
3872
+ const state = this.#core.getState();
3873
+ this.#signSpan.hidden = !state.negative;
3874
+ this.#signSpan.textContent = state.negative ? this.negativeSign : "";
3875
+ this.#textNode.textContent = state.text;
3876
+ applyElementProps(this, this.#core.getAttrs(state));
3877
+ applyStateDataAttrs(this, state, TimeDataAttrs);
3878
+ }
3879
+ };
3880
+
3881
+ //#endregion
3882
+ //#region src/ui/time/time-group-element.ts
3883
+ var TimeGroupElement = class extends MediaElement {
2156
3884
  static {
2157
- this.tagName = "media-slider-value";
3885
+ this.tagName = "media-time-group";
2158
3886
  }
3887
+ };
3888
+
3889
+ //#endregion
3890
+ //#region src/ui/time/time-separator-element.ts
3891
+ var TimeSeparatorElement = class extends MediaElement {
2159
3892
  static {
2160
- this.properties = { type: { type: String } };
3893
+ this.tagName = "media-time-separator";
2161
3894
  }
2162
- #ctx = new ContextConsumer(this, {
2163
- context: sliderContext,
2164
- subscribe: true
2165
- });
2166
3895
  connectedCallback() {
2167
3896
  super.connectedCallback();
2168
- this.setAttribute("aria-live", "off");
2169
- }
2170
- update(_changed) {
2171
- super.update(_changed);
2172
- const ctx = this.#ctx.value;
2173
- if (!ctx) return;
2174
- const value = this.type === "pointer" ? ctx.pointerValue : ctx.state.value;
2175
- this.textContent = ctx.formatValue ? ctx.formatValue(value, this.type) : String(Math.round(value));
2176
- applyStateDataAttrs(this, ctx.state, ctx.stateAttrMap);
3897
+ this.setAttribute("aria-hidden", "true");
3898
+ if (!this.textContent?.trim()) this.textContent = "/";
2177
3899
  }
2178
3900
  };
2179
3901
 
@@ -2183,7 +3905,7 @@ var TimeSliderElement = class extends MediaElement {
2183
3905
  constructor(..._args) {
2184
3906
  super(..._args);
2185
3907
  this.label = TimeSliderCore.defaultProps.label;
2186
- this.commitThrottle = TimeSliderCore.defaultProps.commitThrottle;
3908
+ this.changeThrottle = TimeSliderCore.defaultProps.changeThrottle;
2187
3909
  this.step = TimeSliderCore.defaultProps.step;
2188
3910
  this.largeStep = TimeSliderCore.defaultProps.largeStep;
2189
3911
  this.orientation = TimeSliderCore.defaultProps.orientation;
@@ -2196,9 +3918,9 @@ var TimeSliderElement = class extends MediaElement {
2196
3918
  static {
2197
3919
  this.properties = {
2198
3920
  label: { type: String },
2199
- commitThrottle: {
3921
+ changeThrottle: {
2200
3922
  type: Number,
2201
- attribute: "commit-throttle"
3923
+ attribute: "change-throttle"
2202
3924
  },
2203
3925
  step: { type: Number },
2204
3926
  largeStep: {
@@ -2241,7 +3963,7 @@ var TimeSliderElement = class extends MediaElement {
2241
3963
  const media = this.#timeState.value;
2242
3964
  if (media) media.seek(this.#core.rawValueFromPercent(percent));
2243
3965
  },
2244
- commitThrottle: this.commitThrottle,
3966
+ changeThrottle: this.changeThrottle,
2245
3967
  onDragStart: () => {
2246
3968
  this.dispatchEvent(new CustomEvent("drag-start", { bubbles: true }));
2247
3969
  },
@@ -2299,262 +4021,6 @@ var TimeSliderElement = class extends MediaElement {
2299
4021
  }
2300
4022
  };
2301
4023
 
2302
- //#endregion
2303
- //#region src/define/ui/time-slider.ts
2304
- safeDefine(TimeSliderElement);
2305
- safeDefine(SliderBufferElement);
2306
- safeDefine(SliderFillElement);
2307
- safeDefine(SliderPreviewElement);
2308
- safeDefine(SliderThumbElement);
2309
- safeDefine(SliderThumbnailElement);
2310
- safeDefine(SliderTrackElement);
2311
- safeDefine(SliderValueElement);
2312
-
2313
- //#endregion
2314
- //#region src/ui/tooltip/context.ts
2315
- const TOOLTIP_GROUP_CONTEXT_KEY = Symbol("@videojs/tooltip-group");
2316
- const tooltipGroupContext = createContext(TOOLTIP_GROUP_CONTEXT_KEY);
2317
-
2318
- //#endregion
2319
- //#region src/ui/tooltip/tooltip-element.ts
2320
- function isLabelTrigger(el) {
2321
- return "$state" in el;
2322
- }
2323
- var TooltipElement = class extends MediaElement {
2324
- constructor(..._args) {
2325
- super(..._args);
2326
- this.open = TooltipCore.defaultProps.open;
2327
- this.defaultOpen = TooltipCore.defaultProps.defaultOpen;
2328
- this.side = TooltipCore.defaultProps.side;
2329
- this.align = TooltipCore.defaultProps.align;
2330
- this.delay = TooltipCore.defaultProps.delay;
2331
- this.closeDelay = TooltipCore.defaultProps.closeDelay;
2332
- this.disableHoverablePopup = TooltipCore.defaultProps.disableHoverablePopup;
2333
- this.disabled = TooltipCore.defaultProps.disabled;
2334
- }
2335
- static {
2336
- this.tagName = "media-tooltip";
2337
- }
2338
- static {
2339
- this.properties = {
2340
- open: { type: Boolean },
2341
- defaultOpen: {
2342
- type: Boolean,
2343
- attribute: "default-open"
2344
- },
2345
- side: { type: String },
2346
- align: { type: String },
2347
- delay: { type: Number },
2348
- closeDelay: {
2349
- type: Number,
2350
- attribute: "close-delay"
2351
- },
2352
- disableHoverablePopup: {
2353
- type: Boolean,
2354
- attribute: "disable-hoverable-popup"
2355
- },
2356
- disabled: { type: Boolean }
2357
- };
2358
- }
2359
- #core = new TooltipCore();
2360
- #groupConsumer = new ContextConsumer(this, { context: tooltipGroupContext });
2361
- #tooltip = null;
2362
- #snapshot = null;
2363
- #disconnect = null;
2364
- #triggerAbort = null;
2365
- #currentTrigger = null;
2366
- #positionAbort = null;
2367
- #positionFrame = 0;
2368
- #resizeObserver = null;
2369
- #positionTrigger = null;
2370
- connectedCallback() {
2371
- super.connectedCallback();
2372
- this.#disconnect = new AbortController();
2373
- this.#tooltip = createTooltip({
2374
- transition: createTransition(),
2375
- onOpenChange: (nextOpen, details) => {
2376
- this.open = nextOpen;
2377
- this.dispatchEvent(new CustomEvent("open-change", { detail: {
2378
- open: nextOpen,
2379
- ...details
2380
- } }));
2381
- },
2382
- delay: () => this.delay,
2383
- closeDelay: () => this.closeDelay,
2384
- disableHoverablePopup: () => this.disableHoverablePopup,
2385
- disabled: () => this.disabled,
2386
- group: () => this.#groupConsumer.value
2387
- });
2388
- this.#tooltip.setPopupElement(this);
2389
- applyElementProps(this, this.#tooltip.popupProps, { signal: this.#disconnect.signal });
2390
- if (this.#snapshot) this.#snapshot.track(this.#tooltip.input);
2391
- else this.#snapshot = new SnapshotController(this, this.#tooltip.input);
2392
- }
2393
- firstUpdated(changed) {
2394
- super.firstUpdated(changed);
2395
- if (this.defaultOpen && !this.open) this.#tooltip?.open();
2396
- }
2397
- disconnectedCallback() {
2398
- super.disconnectedCallback();
2399
- this.#cleanupPositioning();
2400
- this.#cleanupTrigger();
2401
- this.#tooltip?.destroy();
2402
- this.#tooltip = null;
2403
- this.#disconnect?.abort();
2404
- this.#disconnect = null;
2405
- }
2406
- willUpdate(changed) {
2407
- super.willUpdate(changed);
2408
- this.#core.setProps(this);
2409
- if (this.#tooltip && changed.has("open")) {
2410
- const { active: interactionOpen } = this.#tooltip.input.current;
2411
- if (this.open !== interactionOpen) if (this.open) this.#tooltip.open();
2412
- else this.#tooltip.close();
2413
- }
2414
- }
2415
- update(_changed) {
2416
- super.update(_changed);
2417
- if (!this.#tooltip) return;
2418
- const triggerEl = this.#findTrigger();
2419
- this.#syncTrigger(triggerEl);
2420
- const input = this.#tooltip.input.current;
2421
- this.#core.setInput(input);
2422
- const state = this.#core.getState();
2423
- applyElementProps(this, this.#core.getPopupAttrs(state));
2424
- applyStateDataAttrs(this, state, TooltipDataAttrs);
2425
- if (state.open) tryShowPopover(this);
2426
- else tryHidePopover(this);
2427
- if (this.#currentTrigger) applyStyles(this.#currentTrigger, getAnchorNameStyle(this.id));
2428
- if (!state.open) {
2429
- this.#cleanupPositioning();
2430
- return;
2431
- }
2432
- const posOpts = {
2433
- side: state.side,
2434
- align: state.align
2435
- };
2436
- if (supportsAnchorPositioning()) applyStyles(this, getAnchorPositionStyle(this.id, posOpts, void 0, void 0, void 0, void 0, TooltipCSSVars));
2437
- else {
2438
- const triggerRect = this.#currentTrigger?.getBoundingClientRect();
2439
- const selfRect = getPopupPositionRect(this);
2440
- const boundaryRect = document.documentElement.getBoundingClientRect();
2441
- const offsets = resolveOffsets(this, TooltipCSSVars);
2442
- applyStyles(this, getAnchorPositionStyle(this.id, posOpts, triggerRect, selfRect, boundaryRect, offsets, TooltipCSSVars));
2443
- }
2444
- this.#syncPositioning();
2445
- }
2446
- #findTrigger() {
2447
- if (!this.id) return null;
2448
- return this.getRootNode().querySelector(`[commandfor="${this.id}"]`);
2449
- }
2450
- #syncTrigger(triggerEl) {
2451
- if (triggerEl === this.#currentTrigger) return;
2452
- this.#cleanupPositioning();
2453
- this.#cleanupTrigger();
2454
- this.#currentTrigger = triggerEl;
2455
- this.#tooltip?.setTriggerElement(triggerEl);
2456
- if (triggerEl && this.#tooltip) {
2457
- this.#triggerAbort = new AbortController();
2458
- applyElementProps(triggerEl, this.#tooltip.triggerProps, { signal: this.#triggerAbort.signal });
2459
- if (isLabelTrigger(triggerEl)) {
2460
- this.#syncContent(triggerEl);
2461
- triggerEl.$state.subscribe(() => this.#syncContent(triggerEl), { signal: this.#triggerAbort.signal });
2462
- }
2463
- }
2464
- }
2465
- #syncContent(triggerEl) {
2466
- this.textContent = triggerEl.getLabel() ?? "";
2467
- }
2468
- #cleanupTrigger() {
2469
- if (this.#currentTrigger) this.#currentTrigger.style.removeProperty("anchor-name");
2470
- this.#triggerAbort?.abort();
2471
- this.#triggerAbort = null;
2472
- this.#currentTrigger = null;
2473
- }
2474
- #syncPositioning() {
2475
- if (supportsAnchorPositioning()) return;
2476
- const triggerEl = this.#currentTrigger;
2477
- if (!triggerEl) return;
2478
- if (this.#positionAbort && this.#positionTrigger === triggerEl) return;
2479
- this.#cleanupPositioning();
2480
- this.#positionAbort = new AbortController();
2481
- this.#positionTrigger = triggerEl;
2482
- const { signal } = this.#positionAbort;
2483
- const reposition = () => {
2484
- cancelAnimationFrame(this.#positionFrame);
2485
- this.#positionFrame = requestAnimationFrame(() => {
2486
- if (signal.aborted) return;
2487
- this.requestUpdate();
2488
- });
2489
- };
2490
- window.addEventListener("scroll", reposition, {
2491
- capture: true,
2492
- passive: true,
2493
- signal
2494
- });
2495
- window.addEventListener("resize", reposition, { signal });
2496
- if (typeof ResizeObserver === "function") {
2497
- this.#resizeObserver = new ResizeObserver(() => {
2498
- reposition();
2499
- });
2500
- this.#resizeObserver.observe(triggerEl);
2501
- this.#resizeObserver.observe(this);
2502
- }
2503
- reposition();
2504
- }
2505
- #cleanupPositioning() {
2506
- this.#positionAbort?.abort();
2507
- this.#positionAbort = null;
2508
- this.#positionTrigger = null;
2509
- cancelAnimationFrame(this.#positionFrame);
2510
- this.#positionFrame = 0;
2511
- this.#resizeObserver?.disconnect();
2512
- this.#resizeObserver = null;
2513
- }
2514
- };
2515
-
2516
- //#endregion
2517
- //#region src/define/ui/tooltip.ts
2518
- safeDefine(TooltipElement);
2519
-
2520
- //#endregion
2521
- //#region src/ui/tooltip/tooltip-group-element.ts
2522
- var TooltipGroupElement = class extends MediaElement {
2523
- constructor(..._args) {
2524
- super(..._args);
2525
- this.delay = TooltipGroupCore.defaultProps.delay;
2526
- this.closeDelay = TooltipGroupCore.defaultProps.closeDelay;
2527
- this.timeout = TooltipGroupCore.defaultProps.timeout;
2528
- }
2529
- static {
2530
- this.tagName = "media-tooltip-group";
2531
- }
2532
- static {
2533
- this.properties = {
2534
- delay: { type: Number },
2535
- closeDelay: {
2536
- type: Number,
2537
- attribute: "close-delay"
2538
- },
2539
- timeout: { type: Number }
2540
- };
2541
- }
2542
- #core = new TooltipGroupCore();
2543
- #provider = new ContextProvider(this, {
2544
- context: tooltipGroupContext,
2545
- initialValue: this.#core
2546
- });
2547
- update(_changed) {
2548
- super.update(_changed);
2549
- this.#core.setProps(this);
2550
- this.#provider.setValue(this.#core);
2551
- }
2552
- };
2553
-
2554
- //#endregion
2555
- //#region src/define/ui/tooltip-group.ts
2556
- safeDefine(TooltipGroupElement);
2557
-
2558
4024
  //#endregion
2559
4025
  //#region src/ui/volume-slider/volume-slider-element.ts
2560
4026
  var VolumeSliderElement = class extends MediaElement {
@@ -2676,14 +4142,41 @@ var VolumeSliderElement = class extends MediaElement {
2676
4142
  };
2677
4143
 
2678
4144
  //#endregion
2679
- //#region src/define/ui/volume-slider.ts
2680
- safeDefine(VolumeSliderElement);
2681
- safeDefine(SliderFillElement);
2682
- safeDefine(SliderPreviewElement);
2683
- safeDefine(SliderThumbElement);
2684
- safeDefine(SliderTrackElement);
2685
- safeDefine(SliderValueElement);
4145
+ //#region src/define/ui/compounds.ts
4146
+ function defineControls() {
4147
+ safeDefine(ControlsElement);
4148
+ safeDefine(ControlsGroupElement);
4149
+ }
4150
+ function defineErrorDialog() {
4151
+ safeDefine(ErrorDialogElement);
4152
+ safeDefine(AlertDialogCloseElement);
4153
+ safeDefine(AlertDialogDescriptionElement);
4154
+ safeDefine(AlertDialogTitleElement);
4155
+ }
4156
+ /** Shared slider sub-elements used by all slider types. */
4157
+ function defineSliderParts() {
4158
+ safeDefine(SliderFillElement);
4159
+ safeDefine(SliderPreviewElement);
4160
+ safeDefine(SliderThumbElement);
4161
+ safeDefine(SliderTrackElement);
4162
+ safeDefine(SliderValueElement);
4163
+ }
4164
+ function defineTime() {
4165
+ safeDefine(TimeElement);
4166
+ safeDefine(TimeGroupElement);
4167
+ safeDefine(TimeSeparatorElement);
4168
+ }
4169
+ function defineTimeSlider() {
4170
+ safeDefine(TimeSliderElement);
4171
+ defineSliderParts();
4172
+ safeDefine(SliderBufferElement);
4173
+ safeDefine(SliderThumbnailElement);
4174
+ }
4175
+ function defineVolumeSlider() {
4176
+ safeDefine(VolumeSliderElement);
4177
+ defineSliderParts();
4178
+ }
2686
4179
 
2687
4180
  //#endregion
2688
- export { PiPButtonDataAttrs as a, FullscreenButtonCore as c, CaptionsButtonDataAttrs as d, CaptionsButtonCore as f, PosterCore as i, ControlsDataAttrs as l, BufferingIndicatorCore as m, MediaButtonElement as n, PiPButtonCore as o, BufferingIndicatorDataAttrs as p, PosterDataAttrs as r, FullscreenButtonDataAttrs as s, ContextPartElement as t, ControlsCore as u };
2689
- //# sourceMappingURL=volume-slider-Co5X4Z1t.js.map
4181
+ export { selectVolume as C, selectTime as S, selectFullscreen as _, defineVolumeSlider as a, selectPlaybackRate as b, SeekButtonElement as c, PlayButtonElement as d, MuteButtonElement as f, createHotkey as g, logMissingFeature as h, defineTimeSlider as i, PopoverElement as l, applyStateDataAttrs as m, defineErrorDialog as n, TooltipGroupElement as o, MediaButtonElement as p, defineTime as r, TooltipElement as s, defineControls as t, PlaybackRateButtonElement as u, selectPiP as v, defaults as w, selectTextTrack as x, selectPlayback as y };
4182
+ //# sourceMappingURL=compounds-VFPcCeRV.js.map