@giddaa-housing/ui 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (334) hide show
  1. package/README.md +18 -6
  2. package/css/base.css +37 -0
  3. package/css/components.css +135 -0
  4. package/css/generated/shared.css +1 -0
  5. package/css/giddaa.css +402 -0
  6. package/css/preset.css +77 -0
  7. package/css/styles.css +2 -0
  8. package/css/theme.css +255 -0
  9. package/dist/accordion.js +5 -35
  10. package/dist/accordion.js.map +1 -1
  11. package/dist/activity-timeline.d.ts +33 -0
  12. package/dist/activity-timeline.js +212 -0
  13. package/dist/activity-timeline.js.map +1 -0
  14. package/dist/alert.js +5 -35
  15. package/dist/alert.js.map +1 -1
  16. package/dist/auth-layout.d.ts +23 -0
  17. package/dist/auth-layout.js +304 -0
  18. package/dist/auth-layout.js.map +1 -0
  19. package/dist/avatar.js +10 -142
  20. package/dist/avatar.js.map +1 -1
  21. package/dist/badge.d.ts +3 -2
  22. package/dist/badge.js +6 -114
  23. package/dist/badge.js.map +1 -1
  24. package/dist/bar-chart.d.ts +101 -77
  25. package/dist/bar-chart.js +142 -220
  26. package/dist/bar-chart.js.map +1 -1
  27. package/dist/breadcrumb.js +5 -35
  28. package/dist/breadcrumb.js.map +1 -1
  29. package/dist/bullet-chart.js +15 -48
  30. package/dist/bullet-chart.js.map +1 -1
  31. package/dist/button-group.js +14 -90
  32. package/dist/button-group.js.map +1 -1
  33. package/dist/button.d.ts +5 -5
  34. package/dist/button.js +7 -140
  35. package/dist/button.js.map +1 -1
  36. package/dist/calendar.js +12 -287
  37. package/dist/calendar.js.map +1 -1
  38. package/dist/card.js +5 -35
  39. package/dist/card.js.map +1 -1
  40. package/dist/carousel.js +12 -386
  41. package/dist/carousel.js.map +1 -1
  42. package/dist/chart.js +16 -480
  43. package/dist/chart.js.map +1 -1
  44. package/dist/checkbox.d.ts +1 -1
  45. package/dist/checkbox.js +13 -48
  46. package/dist/checkbox.js.map +1 -1
  47. package/dist/chunk-322YCNVL.js +278 -0
  48. package/dist/chunk-322YCNVL.js.map +1 -0
  49. package/dist/chunk-3ACQVT7A.js +25 -0
  50. package/dist/chunk-3ACQVT7A.js.map +1 -0
  51. package/dist/chunk-3T65TLJX.js +334 -0
  52. package/dist/chunk-3T65TLJX.js.map +1 -0
  53. package/dist/chunk-6ZSGQHH2.js +192 -0
  54. package/dist/chunk-6ZSGQHH2.js.map +1 -0
  55. package/dist/chunk-7TBMLFGW.js +41 -0
  56. package/dist/chunk-7TBMLFGW.js.map +1 -0
  57. package/dist/chunk-B4H6RNDH.js +135 -0
  58. package/dist/chunk-B4H6RNDH.js.map +1 -0
  59. package/dist/chunk-BUXCGZ6X.js +219 -0
  60. package/dist/chunk-BUXCGZ6X.js.map +1 -0
  61. package/dist/chunk-CHII2QHH.js +58 -0
  62. package/dist/chunk-CHII2QHH.js.map +1 -0
  63. package/dist/chunk-DMNOXRRW.js +147 -0
  64. package/dist/chunk-DMNOXRRW.js.map +1 -0
  65. package/dist/chunk-EFSOSSIC.js +130 -0
  66. package/dist/chunk-EFSOSSIC.js.map +1 -0
  67. package/dist/chunk-FZDSVMKY.js +363 -0
  68. package/dist/chunk-FZDSVMKY.js.map +1 -0
  69. package/dist/chunk-GJOFIJS3.js +246 -0
  70. package/dist/chunk-GJOFIJS3.js.map +1 -0
  71. package/dist/chunk-HL3ZSYES.js +23 -0
  72. package/dist/chunk-HL3ZSYES.js.map +1 -0
  73. package/dist/chunk-JVLUBAPB.js +129 -0
  74. package/dist/chunk-JVLUBAPB.js.map +1 -0
  75. package/dist/chunk-KDPYSOYZ.js +64 -0
  76. package/dist/chunk-KDPYSOYZ.js.map +1 -0
  77. package/dist/chunk-KSMDANKI.js +43 -0
  78. package/dist/chunk-KSMDANKI.js.map +1 -0
  79. package/dist/chunk-MEKCCOP5.js +46 -0
  80. package/dist/chunk-MEKCCOP5.js.map +1 -0
  81. package/dist/chunk-NOFXVRWL.js +451 -0
  82. package/dist/chunk-NOFXVRWL.js.map +1 -0
  83. package/dist/chunk-PC5MHNVA.js +171 -0
  84. package/dist/chunk-PC5MHNVA.js.map +1 -0
  85. package/dist/chunk-QKAQZ2GU.js +122 -0
  86. package/dist/chunk-QKAQZ2GU.js.map +1 -0
  87. package/dist/chunk-QZHSHSVV.js +26 -0
  88. package/dist/chunk-QZHSHSVV.js.map +1 -0
  89. package/dist/chunk-RBMK26XD.js +19 -0
  90. package/dist/chunk-RBMK26XD.js.map +1 -0
  91. package/dist/chunk-SFNJGF6M.js +262 -0
  92. package/dist/chunk-SFNJGF6M.js.map +1 -0
  93. package/dist/chunk-UJJNLGTM.js +112 -0
  94. package/dist/chunk-UJJNLGTM.js.map +1 -0
  95. package/dist/chunk-VGR4ZWJ3.js +418 -0
  96. package/dist/chunk-VGR4ZWJ3.js.map +1 -0
  97. package/dist/chunk-XG47P6D4.js +99 -0
  98. package/dist/chunk-XG47P6D4.js.map +1 -0
  99. package/dist/chunk-XZZAHNVM.js +21 -0
  100. package/dist/chunk-XZZAHNVM.js.map +1 -0
  101. package/dist/chunk-Y4TTYVNE.js +36 -0
  102. package/dist/chunk-Y4TTYVNE.js.map +1 -0
  103. package/dist/chunk-YDWIHW6Q.js +69 -0
  104. package/dist/chunk-YDWIHW6Q.js.map +1 -0
  105. package/dist/collapsible.js +6 -35
  106. package/dist/collapsible.js.map +1 -1
  107. package/dist/combobox.js +26 -694
  108. package/dist/combobox.js.map +1 -1
  109. package/dist/cta.d.ts +66 -0
  110. package/dist/cta.js +358 -0
  111. package/dist/cta.js.map +1 -0
  112. package/dist/currency-selector.d.ts +42 -0
  113. package/dist/currency-selector.js +192 -0
  114. package/dist/currency-selector.js.map +1 -0
  115. package/dist/date-picker.d.ts +18 -10
  116. package/dist/date-picker.js +105 -430
  117. package/dist/date-picker.js.map +1 -1
  118. package/dist/details-card.d.ts +19 -0
  119. package/dist/details-card.js +267 -0
  120. package/dist/details-card.js.map +1 -0
  121. package/dist/dialog.js +36 -146
  122. package/dist/dialog.js.map +1 -1
  123. package/dist/dropdown-menu.d.ts +1 -1
  124. package/dist/dropdown-menu.js +20 -358
  125. package/dist/dropdown-menu.js.map +1 -1
  126. package/dist/dropzone.js +13 -40
  127. package/dist/dropzone.js.map +1 -1
  128. package/dist/editorial-card.d.ts +29 -0
  129. package/dist/editorial-card.js +311 -0
  130. package/dist/editorial-card.js.map +1 -0
  131. package/dist/empty-state.js +5 -35
  132. package/dist/empty-state.js.map +1 -1
  133. package/dist/field.js +30 -135
  134. package/dist/field.js.map +1 -1
  135. package/dist/footer.d.ts +47 -0
  136. package/dist/footer.js +430 -0
  137. package/dist/footer.js.map +1 -0
  138. package/dist/funnel-chart.js +42 -415
  139. package/dist/funnel-chart.js.map +1 -1
  140. package/dist/gallery-indicator.d.ts +18 -0
  141. package/dist/gallery-indicator.js +12 -0
  142. package/dist/gallery-indicator.js.map +1 -0
  143. package/dist/heatmap-chart.d.ts +98 -0
  144. package/dist/heatmap-chart.js +248 -0
  145. package/dist/heatmap-chart.js.map +1 -0
  146. package/dist/infotip.d.ts +11 -0
  147. package/dist/infotip.js +66 -0
  148. package/dist/infotip.js.map +1 -0
  149. package/dist/input-group.d.ts +2 -1
  150. package/dist/input-group.js +15 -371
  151. package/dist/input-group.js.map +1 -1
  152. package/dist/input-size-context.js +5 -19
  153. package/dist/input-size-context.js.map +1 -1
  154. package/dist/input.js +6 -78
  155. package/dist/input.js.map +1 -1
  156. package/dist/item.js +5 -35
  157. package/dist/item.js.map +1 -1
  158. package/dist/kpi-card.js +12 -52
  159. package/dist/kpi-card.js.map +1 -1
  160. package/dist/label.js +5 -64
  161. package/dist/label.js.map +1 -1
  162. package/dist/line-chart.js +9 -166
  163. package/dist/line-chart.js.map +1 -1
  164. package/dist/logo.js +4 -141
  165. package/dist/logo.js.map +1 -1
  166. package/dist/map-indicator.d.ts +13 -0
  167. package/dist/map-indicator.js +109 -0
  168. package/dist/map-indicator.js.map +1 -0
  169. package/dist/match.d.ts +16 -0
  170. package/dist/match.js +113 -0
  171. package/dist/match.js.map +1 -0
  172. package/dist/media-gallery-hero.d.ts +60 -0
  173. package/dist/media-gallery-hero.js +362 -0
  174. package/dist/media-gallery-hero.js.map +1 -0
  175. package/dist/media-player.d.ts +60 -0
  176. package/dist/media-player.js +1394 -0
  177. package/dist/media-player.js.map +1 -0
  178. package/dist/number-input.js +30 -325
  179. package/dist/number-input.js.map +1 -1
  180. package/dist/page-header.js +4 -32
  181. package/dist/page-header.js.map +1 -1
  182. package/dist/pagination.d.ts +1 -0
  183. package/dist/pagination.js +27 -161
  184. package/dist/pagination.js.map +1 -1
  185. package/dist/password-input.d.ts +1 -0
  186. package/dist/password-input.js +21 -319
  187. package/dist/password-input.js.map +1 -1
  188. package/dist/phone-input.d.ts +2 -1
  189. package/dist/phone-input.js +148 -359
  190. package/dist/phone-input.js.map +1 -1
  191. package/dist/photo-gallery.d.ts +1 -1
  192. package/dist/photo-gallery.js +97 -393
  193. package/dist/photo-gallery.js.map +1 -1
  194. package/dist/pie-chart.js +17 -52
  195. package/dist/pie-chart.js.map +1 -1
  196. package/dist/popover.js +13 -158
  197. package/dist/popover.js.map +1 -1
  198. package/dist/price-tag.d.ts +35 -0
  199. package/dist/price-tag.js +178 -0
  200. package/dist/price-tag.js.map +1 -0
  201. package/dist/progress.d.ts +5 -2
  202. package/dist/progress.js +14 -303
  203. package/dist/progress.js.map +1 -1
  204. package/dist/property-listing-card.d.ts +32 -0
  205. package/dist/property-listing-card.js +235 -0
  206. package/dist/property-listing-card.js.map +1 -0
  207. package/dist/radar-chart.d.ts +110 -0
  208. package/dist/radar-chart.js +147 -0
  209. package/dist/radar-chart.js.map +1 -0
  210. package/dist/radio-group.js +15 -50
  211. package/dist/radio-group.js.map +1 -1
  212. package/dist/review-block.d.ts +20 -0
  213. package/dist/review-block.js +10 -0
  214. package/dist/review-block.js.map +1 -0
  215. package/dist/rich-text-editor.js +172 -433
  216. package/dist/rich-text-editor.js.map +1 -1
  217. package/dist/scatter-chart.d.ts +142 -0
  218. package/dist/scatter-chart.js +252 -0
  219. package/dist/scatter-chart.js.map +1 -0
  220. package/dist/search-input.d.ts +1 -0
  221. package/dist/search-input.js +22 -320
  222. package/dist/search-input.js.map +1 -1
  223. package/dist/select.js +15 -292
  224. package/dist/select.js.map +1 -1
  225. package/dist/separator.js +5 -68
  226. package/dist/separator.js.map +1 -1
  227. package/dist/sheet.js +14 -271
  228. package/dist/sheet.js.map +1 -1
  229. package/dist/sidebar.d.ts +1 -1
  230. package/dist/sidebar.js +89 -494
  231. package/dist/sidebar.js.map +1 -1
  232. package/dist/size-context.js +5 -17
  233. package/dist/size-context.js.map +1 -1
  234. package/dist/skeleton.js +4 -44
  235. package/dist/skeleton.js.map +1 -1
  236. package/dist/slider.d.ts +28 -0
  237. package/dist/slider.js +152 -0
  238. package/dist/slider.js.map +1 -0
  239. package/dist/sparkline.js +33 -341
  240. package/dist/sparkline.js.map +1 -1
  241. package/dist/stepper.js +5 -35
  242. package/dist/stepper.js.map +1 -1
  243. package/dist/styles.css +9593 -2
  244. package/dist/switch.js +13 -48
  245. package/dist/switch.js.map +1 -1
  246. package/dist/tab-list.js +35 -306
  247. package/dist/tab-list.js.map +1 -1
  248. package/dist/table.js +5 -32
  249. package/dist/table.js.map +1 -1
  250. package/dist/tabs.js +10 -150
  251. package/dist/tabs.js.map +1 -1
  252. package/dist/tag.d.ts +7 -6
  253. package/dist/tag.js +101 -97
  254. package/dist/tag.js.map +1 -1
  255. package/dist/testimonial-block.d.ts +34 -0
  256. package/dist/testimonial-block.js +377 -0
  257. package/dist/testimonial-block.js.map +1 -0
  258. package/dist/textarea.js +6 -73
  259. package/dist/textarea.js.map +1 -1
  260. package/dist/time-picker.js +119 -279
  261. package/dist/time-picker.js.map +1 -1
  262. package/dist/toast.js +27 -132
  263. package/dist/toast.js.map +1 -1
  264. package/dist/tooltip.d.ts +5 -5
  265. package/dist/tooltip.js +7 -90
  266. package/dist/tooltip.js.map +1 -1
  267. package/dist/tree-map.js +41 -414
  268. package/dist/tree-map.js.map +1 -1
  269. package/dist/waterfall-chart.d.ts +129 -0
  270. package/dist/waterfall-chart.js +577 -0
  271. package/dist/waterfall-chart.js.map +1 -0
  272. package/package.json +98 -28
  273. package/dist/base.css +0 -32
  274. package/dist/files/red-hat-display-latin-300-italic.woff +0 -0
  275. package/dist/files/red-hat-display-latin-300-italic.woff2 +0 -0
  276. package/dist/files/red-hat-display-latin-300-normal.woff +0 -0
  277. package/dist/files/red-hat-display-latin-300-normal.woff2 +0 -0
  278. package/dist/files/red-hat-display-latin-400-italic.woff +0 -0
  279. package/dist/files/red-hat-display-latin-400-italic.woff2 +0 -0
  280. package/dist/files/red-hat-display-latin-400-normal.woff +0 -0
  281. package/dist/files/red-hat-display-latin-400-normal.woff2 +0 -0
  282. package/dist/files/red-hat-display-latin-500-italic.woff +0 -0
  283. package/dist/files/red-hat-display-latin-500-italic.woff2 +0 -0
  284. package/dist/files/red-hat-display-latin-500-normal.woff +0 -0
  285. package/dist/files/red-hat-display-latin-500-normal.woff2 +0 -0
  286. package/dist/files/red-hat-display-latin-600-italic.woff +0 -0
  287. package/dist/files/red-hat-display-latin-600-italic.woff2 +0 -0
  288. package/dist/files/red-hat-display-latin-600-normal.woff +0 -0
  289. package/dist/files/red-hat-display-latin-600-normal.woff2 +0 -0
  290. package/dist/files/red-hat-display-latin-700-italic.woff +0 -0
  291. package/dist/files/red-hat-display-latin-700-italic.woff2 +0 -0
  292. package/dist/files/red-hat-display-latin-700-normal.woff +0 -0
  293. package/dist/files/red-hat-display-latin-700-normal.woff2 +0 -0
  294. package/dist/files/red-hat-display-latin-800-italic.woff +0 -0
  295. package/dist/files/red-hat-display-latin-800-italic.woff2 +0 -0
  296. package/dist/files/red-hat-display-latin-800-normal.woff +0 -0
  297. package/dist/files/red-hat-display-latin-800-normal.woff2 +0 -0
  298. package/dist/files/red-hat-display-latin-900-italic.woff +0 -0
  299. package/dist/files/red-hat-display-latin-900-italic.woff2 +0 -0
  300. package/dist/files/red-hat-display-latin-900-normal.woff +0 -0
  301. package/dist/files/red-hat-display-latin-900-normal.woff2 +0 -0
  302. package/dist/files/red-hat-display-latin-ext-300-italic.woff +0 -0
  303. package/dist/files/red-hat-display-latin-ext-300-italic.woff2 +0 -0
  304. package/dist/files/red-hat-display-latin-ext-300-normal.woff +0 -0
  305. package/dist/files/red-hat-display-latin-ext-300-normal.woff2 +0 -0
  306. package/dist/files/red-hat-display-latin-ext-400-italic.woff +0 -0
  307. package/dist/files/red-hat-display-latin-ext-400-italic.woff2 +0 -0
  308. package/dist/files/red-hat-display-latin-ext-400-normal.woff +0 -0
  309. package/dist/files/red-hat-display-latin-ext-400-normal.woff2 +0 -0
  310. package/dist/files/red-hat-display-latin-ext-500-italic.woff +0 -0
  311. package/dist/files/red-hat-display-latin-ext-500-italic.woff2 +0 -0
  312. package/dist/files/red-hat-display-latin-ext-500-normal.woff +0 -0
  313. package/dist/files/red-hat-display-latin-ext-500-normal.woff2 +0 -0
  314. package/dist/files/red-hat-display-latin-ext-600-italic.woff +0 -0
  315. package/dist/files/red-hat-display-latin-ext-600-italic.woff2 +0 -0
  316. package/dist/files/red-hat-display-latin-ext-600-normal.woff +0 -0
  317. package/dist/files/red-hat-display-latin-ext-600-normal.woff2 +0 -0
  318. package/dist/files/red-hat-display-latin-ext-700-italic.woff +0 -0
  319. package/dist/files/red-hat-display-latin-ext-700-italic.woff2 +0 -0
  320. package/dist/files/red-hat-display-latin-ext-700-normal.woff +0 -0
  321. package/dist/files/red-hat-display-latin-ext-700-normal.woff2 +0 -0
  322. package/dist/files/red-hat-display-latin-ext-800-italic.woff +0 -0
  323. package/dist/files/red-hat-display-latin-ext-800-italic.woff2 +0 -0
  324. package/dist/files/red-hat-display-latin-ext-800-normal.woff +0 -0
  325. package/dist/files/red-hat-display-latin-ext-800-normal.woff2 +0 -0
  326. package/dist/files/red-hat-display-latin-ext-900-italic.woff +0 -0
  327. package/dist/files/red-hat-display-latin-ext-900-italic.woff2 +0 -0
  328. package/dist/files/red-hat-display-latin-ext-900-normal.woff +0 -0
  329. package/dist/files/red-hat-display-latin-ext-900-normal.woff2 +0 -0
  330. package/dist/index.d.ts +0 -87
  331. package/dist/index.js +0 -10448
  332. package/dist/index.js.map +0 -1
  333. package/dist/red-hat-display.css +0 -2
  334. package/dist/theme/giddaa.css +0 -2
@@ -0,0 +1,1394 @@
1
+ "use client";
2
+ import {
3
+ Tooltip,
4
+ TooltipContent,
5
+ TooltipTrigger
6
+ } from "./chunk-KDPYSOYZ.js";
7
+ import {
8
+ DropdownMenu,
9
+ DropdownMenuContent,
10
+ DropdownMenuRadioGroup,
11
+ DropdownMenuRadioItem,
12
+ DropdownMenuSub,
13
+ DropdownMenuSubContent,
14
+ DropdownMenuSubTrigger,
15
+ DropdownMenuTrigger
16
+ } from "./chunk-3T65TLJX.js";
17
+ import "./chunk-HL3ZSYES.js";
18
+ import {
19
+ cn
20
+ } from "./chunk-Y4TTYVNE.js";
21
+
22
+ // src/media-player.tsx
23
+ import { Slider as SliderPrimitive } from "@base-ui/react/slider";
24
+ import {
25
+ Captions,
26
+ CaptionsOff,
27
+ Maximize,
28
+ Minimize,
29
+ Pause,
30
+ PictureInPicture2,
31
+ Play,
32
+ RefreshCcw,
33
+ Repeat2,
34
+ RotateCcw,
35
+ RotateCw,
36
+ Settings,
37
+ TriangleAlert,
38
+ Volume1,
39
+ Volume2,
40
+ VolumeX
41
+ } from "lucide-react";
42
+ import {
43
+ MediaActionTypes,
44
+ MediaProvider,
45
+ useMediaDispatch,
46
+ useMediaFullscreenRef,
47
+ useMediaRef,
48
+ useMediaSelector,
49
+ useMediaStore
50
+ } from "media-chrome/react/media-store";
51
+ import * as React from "react";
52
+ import * as ReactDOM from "react-dom";
53
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
54
+ var DEFAULT_PLAYBACK_RATES = [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2];
55
+ var CONTROLS_HIDE_DELAY = 2500;
56
+ var SEEK_PREVIEW_WIDTH = 160;
57
+ var MediaPlayerContext = React.createContext({
58
+ onMenuOpenChange: () => {
59
+ },
60
+ portalContainer: null,
61
+ rootNode: null
62
+ });
63
+ function setRef(ref, value) {
64
+ if (typeof ref === "function") {
65
+ ref(value);
66
+ return;
67
+ }
68
+ if (ref) {
69
+ ref.current = value;
70
+ }
71
+ }
72
+ function formatTime(value) {
73
+ if (!Number.isFinite(value)) {
74
+ return "0:00";
75
+ }
76
+ const safeValue = Math.max(0, Math.floor(value ?? 0));
77
+ const hours = Math.floor(safeValue / 3600);
78
+ const minutes = Math.floor(safeValue % 3600 / 60);
79
+ const seconds = safeValue % 60;
80
+ if (hours > 0) {
81
+ return `${hours}:${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
82
+ }
83
+ return `${minutes}:${String(seconds).padStart(2, "0")}`;
84
+ }
85
+ function formatRate(value) {
86
+ if (!value || value === 1) {
87
+ return "1x";
88
+ }
89
+ return `${Number.parseFloat(value.toFixed(2))}x`;
90
+ }
91
+ function subtitleTrackId(track) {
92
+ return `${track.kind}:${track.language}:${track.label}`;
93
+ }
94
+ function subtitleTrackLabel(track) {
95
+ return track.label || track.language || "Captions";
96
+ }
97
+ function hasAudioChild(children) {
98
+ return React.Children.toArray(children).some((child) => {
99
+ if (!React.isValidElement(child)) {
100
+ return false;
101
+ }
102
+ if (child.type === MediaPlayerAudio) {
103
+ return true;
104
+ }
105
+ const props = child.props;
106
+ return hasAudioChild(props.children);
107
+ });
108
+ }
109
+ function getSeekBounds(seekable, duration) {
110
+ const min = seekable?.[0] ?? 0;
111
+ const fallbackMax = Number.isFinite(duration) ? duration ?? 0 : 0;
112
+ const max = seekable?.[1] ?? fallbackMax;
113
+ return {
114
+ min,
115
+ max: Number.isFinite(max) ? Math.max(min, max) : min
116
+ };
117
+ }
118
+ function clamp(value, min, max) {
119
+ return Math.min(Math.max(value, min), max);
120
+ }
121
+ function getSeekValueFromPointer(event, min, max) {
122
+ const rect = event.currentTarget.getBoundingClientRect();
123
+ const percent = clamp((event.clientX - rect.left) / rect.width, 0, 1);
124
+ return {
125
+ percent: percent * 100,
126
+ value: min + percent * (max - min)
127
+ };
128
+ }
129
+ function MediaPlayer({ mediaStore, ...props }) {
130
+ const audio = props.audio ?? (hasAudioChild(props.children) ? true : void 0);
131
+ return /* @__PURE__ */ jsx(MediaProvider, { mediaStore, children: /* @__PURE__ */ jsx(MediaPlayerRoot, { ...props, audio }) });
132
+ }
133
+ function MediaPlayerRoot({
134
+ audio,
135
+ children,
136
+ className,
137
+ onBlurCapture,
138
+ onFocusCapture,
139
+ onKeyDown,
140
+ onPointerDown,
141
+ onPointerLeave,
142
+ onPointerMove,
143
+ ref,
144
+ ...props
145
+ }) {
146
+ const dispatch = useMediaDispatch();
147
+ const store = useMediaStore();
148
+ const fullscreenRef = useMediaFullscreenRef();
149
+ const paused = useMediaSelector((state) => state.mediaPaused !== false);
150
+ const muted = useMediaSelector((state) => state.mediaMuted === true);
151
+ const isFullscreen = useMediaSelector(
152
+ (state) => state.mediaIsFullscreen === true
153
+ );
154
+ const [rootNode, setRootNode] = React.useState(null);
155
+ const [controlsVisible, setControlsVisible] = React.useState(true);
156
+ const [menuOpenCount, setMenuOpenCount] = React.useState(0);
157
+ const [hoveringControls, setHoveringControls] = React.useState(false);
158
+ const [focusPinned, setFocusPinned] = React.useState(false);
159
+ const hideTimerRef = React.useRef(void 0);
160
+ const lastActivityRef = React.useRef(0);
161
+ const alwaysVisible = Boolean(audio) || paused || menuOpenCount > 0 || hoveringControls || focusPinned;
162
+ const alwaysVisibleRef = React.useRef(alwaysVisible);
163
+ alwaysVisibleRef.current = alwaysVisible;
164
+ const scheduleHide = React.useCallback(function schedule(delay = CONTROLS_HIDE_DELAY) {
165
+ window.clearTimeout(hideTimerRef.current);
166
+ hideTimerRef.current = window.setTimeout(() => {
167
+ if (alwaysVisibleRef.current) {
168
+ return;
169
+ }
170
+ const idleFor = Date.now() - lastActivityRef.current;
171
+ if (idleFor < CONTROLS_HIDE_DELAY) {
172
+ schedule(CONTROLS_HIDE_DELAY - idleFor);
173
+ return;
174
+ }
175
+ setControlsVisible(false);
176
+ }, delay);
177
+ }, []);
178
+ const wake = React.useCallback(() => {
179
+ lastActivityRef.current = Date.now();
180
+ setControlsVisible(true);
181
+ scheduleHide();
182
+ }, [scheduleHide]);
183
+ React.useEffect(() => {
184
+ if (alwaysVisible) {
185
+ window.clearTimeout(hideTimerRef.current);
186
+ setControlsVisible(true);
187
+ return;
188
+ }
189
+ scheduleHide();
190
+ }, [alwaysVisible, scheduleHide]);
191
+ React.useEffect(() => {
192
+ return () => window.clearTimeout(hideTimerRef.current);
193
+ }, []);
194
+ React.useEffect(() => {
195
+ if (!rootNode) {
196
+ return;
197
+ }
198
+ const loadChapters = () => {
199
+ const media = rootNode.querySelector("video, audio");
200
+ for (const track of media?.textTracks ?? []) {
201
+ if (track.kind === "chapters" && track.mode === "disabled") {
202
+ track.mode = "hidden";
203
+ }
204
+ }
205
+ };
206
+ loadChapters();
207
+ const textTracks = rootNode.querySelector("video, audio")?.textTracks;
208
+ textTracks?.addEventListener("addtrack", loadChapters);
209
+ return () => textTracks?.removeEventListener("addtrack", loadChapters);
210
+ }, [rootNode]);
211
+ const onMenuOpenChange = React.useCallback((open) => {
212
+ setMenuOpenCount((count) => open ? count + 1 : Math.max(0, count - 1));
213
+ }, []);
214
+ const contextValue = React.useMemo(
215
+ () => ({
216
+ onMenuOpenChange,
217
+ portalContainer: isFullscreen ? rootNode : null,
218
+ rootNode
219
+ }),
220
+ [isFullscreen, onMenuOpenChange, rootNode]
221
+ );
222
+ const fullscreenRefRef = React.useRef(fullscreenRef);
223
+ fullscreenRefRef.current = fullscreenRef;
224
+ const rootRef = React.useCallback(
225
+ (node) => {
226
+ fullscreenRefRef.current(node);
227
+ setRootNode(node);
228
+ setRef(ref, node);
229
+ },
230
+ [ref]
231
+ );
232
+ return /* @__PURE__ */ jsx(MediaPlayerContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(
233
+ "div",
234
+ {
235
+ ref: rootRef,
236
+ "aria-label": audio ? "Audio player" : "Video player",
237
+ "data-audio": audio ? "" : void 0,
238
+ "data-controls": controlsVisible ? "visible" : "hidden",
239
+ "data-fullscreen": isFullscreen ? "" : void 0,
240
+ "data-slot": "media-player",
241
+ role: "region",
242
+ tabIndex: 0,
243
+ className: cn(
244
+ "group/media-player relative isolate block w-full overflow-hidden rounded-2xl bg-fg-primary text-fg-on-brand shadow-2 outline-none",
245
+ "data-[controls=hidden]:cursor-none",
246
+ "data-[audio]:bg-[image:var(--gradient-accent-forest-to-brass)]",
247
+ "data-[fullscreen]:rounded-none data-[fullscreen]:shadow-none",
248
+ className
249
+ ),
250
+ ...props,
251
+ onBlurCapture: (event) => {
252
+ onBlurCapture?.(event);
253
+ if (!event.currentTarget.contains(event.relatedTarget)) {
254
+ setFocusPinned(false);
255
+ }
256
+ },
257
+ onFocusCapture: (event) => {
258
+ onFocusCapture?.(event);
259
+ const keyboardFocus = event.target.matches?.(":focus-visible") ?? false;
260
+ setFocusPinned(keyboardFocus);
261
+ if (keyboardFocus) {
262
+ setControlsVisible(true);
263
+ }
264
+ },
265
+ onKeyDown: (event) => {
266
+ onKeyDown?.(event);
267
+ if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.altKey) {
268
+ return;
269
+ }
270
+ const target = event.target;
271
+ if (target.closest(
272
+ "input, textarea, select, [contenteditable], [role='slider'], [role='menu'], [role='menuitem'], [role='menuitemradio']"
273
+ )) {
274
+ return;
275
+ }
276
+ const state = store.getState();
277
+ const currentTime = state.mediaCurrentTime ?? 0;
278
+ const { min, max } = getSeekBounds(
279
+ state.mediaSeekable,
280
+ state.mediaDuration
281
+ );
282
+ switch (event.key) {
283
+ case " ":
284
+ if (target.closest("button")) {
285
+ return;
286
+ }
287
+ event.preventDefault();
288
+ dispatch({
289
+ type: paused ? MediaActionTypes.MEDIA_PLAY_REQUEST : MediaActionTypes.MEDIA_PAUSE_REQUEST
290
+ });
291
+ break;
292
+ case "k":
293
+ case "K":
294
+ dispatch({
295
+ type: paused ? MediaActionTypes.MEDIA_PLAY_REQUEST : MediaActionTypes.MEDIA_PAUSE_REQUEST
296
+ });
297
+ break;
298
+ case "m":
299
+ case "M":
300
+ dispatch({
301
+ type: muted ? MediaActionTypes.MEDIA_UNMUTE_REQUEST : MediaActionTypes.MEDIA_MUTE_REQUEST
302
+ });
303
+ break;
304
+ case "f":
305
+ case "F":
306
+ if (audio) {
307
+ return;
308
+ }
309
+ dispatch({
310
+ type: isFullscreen ? MediaActionTypes.MEDIA_EXIT_FULLSCREEN_REQUEST : MediaActionTypes.MEDIA_ENTER_FULLSCREEN_REQUEST
311
+ });
312
+ break;
313
+ case "c":
314
+ case "C":
315
+ dispatch({
316
+ type: MediaActionTypes.MEDIA_TOGGLE_SUBTITLES_REQUEST
317
+ });
318
+ break;
319
+ case "ArrowLeft":
320
+ event.preventDefault();
321
+ dispatch({
322
+ type: MediaActionTypes.MEDIA_SEEK_REQUEST,
323
+ detail: clamp(currentTime - 5, min, max)
324
+ });
325
+ break;
326
+ case "ArrowRight":
327
+ event.preventDefault();
328
+ dispatch({
329
+ type: MediaActionTypes.MEDIA_SEEK_REQUEST,
330
+ detail: clamp(currentTime + 5, min, max)
331
+ });
332
+ break;
333
+ default:
334
+ return;
335
+ }
336
+ wake();
337
+ },
338
+ onPointerDown: (event) => {
339
+ onPointerDown?.(event);
340
+ wake();
341
+ },
342
+ onPointerLeave: (event) => {
343
+ onPointerLeave?.(event);
344
+ setHoveringControls(false);
345
+ if (!alwaysVisibleRef.current) {
346
+ window.clearTimeout(hideTimerRef.current);
347
+ setControlsVisible(false);
348
+ }
349
+ },
350
+ onPointerMove: (event) => {
351
+ onPointerMove?.(event);
352
+ wake();
353
+ const controls = event.currentTarget.querySelector(
354
+ "[data-slot='media-player-controls']"
355
+ );
356
+ const rect = controls?.getBoundingClientRect();
357
+ setHoveringControls(
358
+ !!rect && event.clientX >= rect.left && event.clientX <= rect.right && event.clientY >= rect.top && event.clientY <= rect.bottom
359
+ );
360
+ },
361
+ children
362
+ }
363
+ ) });
364
+ }
365
+ function MediaPlayerVideo({ className, ref, ...props }) {
366
+ const mediaRef = useMediaRef();
367
+ const mediaRefRef = React.useRef(mediaRef);
368
+ mediaRefRef.current = mediaRef;
369
+ const videoRef = React.useCallback(
370
+ (node) => {
371
+ mediaRefRef.current(node);
372
+ setRef(ref, node);
373
+ },
374
+ [ref]
375
+ );
376
+ return /* @__PURE__ */ jsx(
377
+ "video",
378
+ {
379
+ ref: videoRef,
380
+ "data-slot": "media-player-video",
381
+ className: cn(
382
+ "block aspect-video size-full object-cover",
383
+ "group-data-[fullscreen]/media-player:aspect-auto group-data-[fullscreen]/media-player:object-contain",
384
+ className
385
+ ),
386
+ playsInline: true,
387
+ ...props
388
+ }
389
+ );
390
+ }
391
+ function MediaPlayerAudio({ className, ref, ...props }) {
392
+ const mediaRef = useMediaRef();
393
+ const mediaRefRef = React.useRef(mediaRef);
394
+ mediaRefRef.current = mediaRef;
395
+ const audioRef = React.useCallback(
396
+ (node) => {
397
+ mediaRefRef.current(node);
398
+ setRef(ref, node);
399
+ },
400
+ [ref]
401
+ );
402
+ return /* @__PURE__ */ jsx(
403
+ "audio",
404
+ {
405
+ ref: audioRef,
406
+ "data-slot": "media-player-audio",
407
+ className: cn("sr-only", className),
408
+ ...props
409
+ }
410
+ );
411
+ }
412
+ function MediaPlayerControls({
413
+ className,
414
+ ...props
415
+ }) {
416
+ return /* @__PURE__ */ jsx(
417
+ "div",
418
+ {
419
+ "data-slot": "media-player-controls",
420
+ className: cn(
421
+ "absolute inset-x-0 bottom-0 z-10 flex w-full items-center gap-2 p-3 text-fg-on-brand md:p-4",
422
+ "transition-opacity duration-300 ease-out group-data-[controls=hidden]/media-player:pointer-events-none group-data-[controls=hidden]/media-player:opacity-0",
423
+ className
424
+ ),
425
+ ...props
426
+ }
427
+ );
428
+ }
429
+ function MediaPlayerControlsOverlay({
430
+ className,
431
+ ...props
432
+ }) {
433
+ return /* @__PURE__ */ jsx(
434
+ "div",
435
+ {
436
+ "aria-hidden": "true",
437
+ "data-slot": "media-player-controls-overlay",
438
+ className: cn(
439
+ "pointer-events-none absolute inset-x-0 bottom-0 -z-10 h-36 bg-[image:var(--gradient-overlay-photo-bottom)]",
440
+ className
441
+ ),
442
+ ...props
443
+ }
444
+ );
445
+ }
446
+ function MediaControlButton({
447
+ className,
448
+ shortcut,
449
+ tooltip,
450
+ ...props
451
+ }) {
452
+ const { portalContainer } = React.useContext(MediaPlayerContext);
453
+ const button = /* @__PURE__ */ jsx(
454
+ "button",
455
+ {
456
+ type: "button",
457
+ className: cn(
458
+ "inline-flex size-9 shrink-0 items-center justify-center rounded-full bg-surface-glass-overlay-button text-fg-on-brand shadow-1 backdrop-blur-sm transition-[background-color,color,box-shadow,transform] outline-none select-none hover:bg-surface-brand hover:text-fg-on-brand focus-visible:shadow-[0px_0px_0px_2px_var(--color-line-focus)] active:scale-95 aria-pressed:bg-surface-brand disabled:pointer-events-none disabled:opacity-45 [&_svg]:size-4.5",
459
+ className
460
+ ),
461
+ ...props
462
+ }
463
+ );
464
+ if (!tooltip) {
465
+ return button;
466
+ }
467
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
468
+ /* @__PURE__ */ jsx(TooltipTrigger, { delay: 300, render: button }),
469
+ /* @__PURE__ */ jsxs(
470
+ TooltipContent,
471
+ {
472
+ className: "border-line-subtle px-2.5 py-1.5 text-gdt-xs font-bold",
473
+ container: portalContainer ?? void 0,
474
+ side: "top",
475
+ sideOffset: 8,
476
+ children: [
477
+ tooltip,
478
+ shortcut ? /* @__PURE__ */ jsx(
479
+ "kbd",
480
+ {
481
+ "data-slot": "kbd",
482
+ className: "rounded-sm border border-line bg-surface px-1.5 py-0.5 font-sans text-gdt-xs font-bold text-fg-primary",
483
+ children: shortcut
484
+ }
485
+ ) : null
486
+ ]
487
+ }
488
+ )
489
+ ] });
490
+ }
491
+ function MediaPlayerPlay({
492
+ className,
493
+ onClick,
494
+ ...props
495
+ }) {
496
+ const dispatch = useMediaDispatch();
497
+ const paused = useMediaSelector((state) => state.mediaPaused !== false);
498
+ return /* @__PURE__ */ jsx(
499
+ MediaControlButton,
500
+ {
501
+ "aria-label": paused ? "Play media" : "Pause media",
502
+ "aria-pressed": !paused,
503
+ "data-slot": "media-player-play",
504
+ className,
505
+ shortcut: "Space",
506
+ tooltip: paused ? "Play" : "Pause",
507
+ onClick: (event) => {
508
+ onClick?.(event);
509
+ if (event.defaultPrevented) {
510
+ return;
511
+ }
512
+ dispatch({
513
+ type: paused ? MediaActionTypes.MEDIA_PLAY_REQUEST : MediaActionTypes.MEDIA_PAUSE_REQUEST
514
+ });
515
+ },
516
+ ...props,
517
+ children: paused ? /* @__PURE__ */ jsx(Play, { fill: "currentColor" }) : /* @__PURE__ */ jsx(Pause, { fill: "currentColor" })
518
+ }
519
+ );
520
+ }
521
+ function MediaPlayerSeekBackward({
522
+ className,
523
+ onClick,
524
+ seekOffset = 10,
525
+ ...props
526
+ }) {
527
+ const dispatch = useMediaDispatch();
528
+ const currentTime = useMediaSelector((state) => state.mediaCurrentTime ?? 0);
529
+ return /* @__PURE__ */ jsx(
530
+ MediaControlButton,
531
+ {
532
+ "aria-label": `Seek back ${seekOffset} seconds`,
533
+ "data-slot": "media-player-seek-backward",
534
+ className,
535
+ tooltip: `Back ${seekOffset}s`,
536
+ onClick: (event) => {
537
+ onClick?.(event);
538
+ if (event.defaultPrevented) {
539
+ return;
540
+ }
541
+ dispatch({
542
+ type: MediaActionTypes.MEDIA_SEEK_REQUEST,
543
+ detail: Math.max(0, currentTime - seekOffset)
544
+ });
545
+ },
546
+ ...props,
547
+ children: /* @__PURE__ */ jsx(RotateCcw, {})
548
+ }
549
+ );
550
+ }
551
+ function MediaPlayerSeekForward({
552
+ className,
553
+ onClick,
554
+ seekOffset = 10,
555
+ ...props
556
+ }) {
557
+ const dispatch = useMediaDispatch();
558
+ const currentTime = useMediaSelector((state) => state.mediaCurrentTime ?? 0);
559
+ const duration = useMediaSelector((state) => state.mediaDuration);
560
+ const seekable = useMediaSelector((state) => state.mediaSeekable);
561
+ const { max } = getSeekBounds(seekable, duration);
562
+ return /* @__PURE__ */ jsx(
563
+ MediaControlButton,
564
+ {
565
+ "aria-label": `Seek forward ${seekOffset} seconds`,
566
+ "data-slot": "media-player-seek-forward",
567
+ className,
568
+ tooltip: `Forward ${seekOffset}s`,
569
+ onClick: (event) => {
570
+ onClick?.(event);
571
+ if (event.defaultPrevented) {
572
+ return;
573
+ }
574
+ dispatch({
575
+ type: MediaActionTypes.MEDIA_SEEK_REQUEST,
576
+ detail: max > 0 ? Math.min(max, currentTime + seekOffset) : currentTime + seekOffset
577
+ });
578
+ },
579
+ ...props,
580
+ children: /* @__PURE__ */ jsx(RotateCw, {})
581
+ }
582
+ );
583
+ }
584
+ function MediaPlayerVolume({
585
+ className,
586
+ disabled,
587
+ expandable = false,
588
+ onClick,
589
+ rangeClassName,
590
+ ...props
591
+ }) {
592
+ const dispatch = useMediaDispatch();
593
+ const muted = useMediaSelector((state) => state.mediaMuted === true);
594
+ const volume = useMediaSelector((state) => state.mediaVolume ?? 1);
595
+ const unavailable = useMediaSelector(
596
+ (state) => !!state.mediaVolumeUnavailable
597
+ );
598
+ const resolvedDisabled = disabled || unavailable;
599
+ const Icon = muted || volume === 0 ? VolumeX : volume < 0.5 ? Volume1 : Volume2;
600
+ return /* @__PURE__ */ jsxs(
601
+ "div",
602
+ {
603
+ "data-slot": "media-player-volume",
604
+ className: cn(
605
+ "group/volume flex items-center gap-2 rounded-full",
606
+ expandable && "w-9 overflow-visible transition-[width] hover:w-32 focus-within:w-32"
607
+ ),
608
+ children: [
609
+ /* @__PURE__ */ jsx(
610
+ MediaControlButton,
611
+ {
612
+ "aria-label": muted ? "Unmute media" : "Mute media",
613
+ "aria-pressed": muted,
614
+ className,
615
+ disabled: resolvedDisabled,
616
+ shortcut: "M",
617
+ tooltip: "Volume",
618
+ onClick: (event) => {
619
+ onClick?.(event);
620
+ if (event.defaultPrevented) {
621
+ return;
622
+ }
623
+ dispatch({
624
+ type: muted ? MediaActionTypes.MEDIA_UNMUTE_REQUEST : MediaActionTypes.MEDIA_MUTE_REQUEST
625
+ });
626
+ },
627
+ ...props,
628
+ children: /* @__PURE__ */ jsx(Icon, {})
629
+ }
630
+ ),
631
+ /* @__PURE__ */ jsx(
632
+ SliderPrimitive.Root,
633
+ {
634
+ "data-slot": "media-player-volume-slider",
635
+ className: cn(
636
+ "flex h-8 w-20 min-w-20 touch-none items-center",
637
+ resolvedDisabled && "pointer-events-none opacity-45",
638
+ expandable && "pointer-events-none opacity-0 transition-opacity group-hover/volume:pointer-events-auto group-hover/volume:opacity-100 group-focus-within/volume:pointer-events-auto group-focus-within/volume:opacity-100",
639
+ rangeClassName
640
+ ),
641
+ disabled: resolvedDisabled,
642
+ max: 1,
643
+ min: 0,
644
+ onValueChange: (value) => {
645
+ const nextValue = Array.isArray(value) ? value[0] ?? 0 : value;
646
+ dispatch({
647
+ type: MediaActionTypes.MEDIA_VOLUME_REQUEST,
648
+ detail: nextValue
649
+ });
650
+ if (muted && nextValue > 0) {
651
+ dispatch({ type: MediaActionTypes.MEDIA_UNMUTE_REQUEST });
652
+ }
653
+ },
654
+ step: 0.02,
655
+ value: muted ? 0 : volume,
656
+ children: /* @__PURE__ */ jsx(SliderPrimitive.Control, { className: "flex h-8 w-full items-center", children: /* @__PURE__ */ jsxs(SliderPrimitive.Track, { className: "h-1.5 w-full rounded-full bg-fg-on-brand/30", children: [
657
+ /* @__PURE__ */ jsx(SliderPrimitive.Indicator, { className: "rounded-full bg-fg-on-brand" }),
658
+ /* @__PURE__ */ jsx(
659
+ SliderPrimitive.Thumb,
660
+ {
661
+ getAriaLabel: () => "Media volume",
662
+ className: "size-3 rounded-full bg-fg-on-brand shadow-1 outline-none focus-visible:[filter:drop-shadow(0_0_0.25rem_var(--color-line-focus))]"
663
+ }
664
+ )
665
+ ] }) })
666
+ }
667
+ )
668
+ ]
669
+ }
670
+ );
671
+ }
672
+ function MediaPlayerPlaybackSpeed({
673
+ className,
674
+ rates = DEFAULT_PLAYBACK_RATES,
675
+ ...props
676
+ }) {
677
+ const dispatch = useMediaDispatch();
678
+ const { onMenuOpenChange, portalContainer } = React.useContext(MediaPlayerContext);
679
+ const playbackRate = useMediaSelector(
680
+ (state) => state.mediaPlaybackRate ?? 1
681
+ );
682
+ const rateLabel = formatRate(playbackRate);
683
+ return /* @__PURE__ */ jsxs(DropdownMenu, { size: "sm", onOpenChange: onMenuOpenChange, children: [
684
+ /* @__PURE__ */ jsx(
685
+ DropdownMenuTrigger,
686
+ {
687
+ render: /* @__PURE__ */ jsx(
688
+ MediaControlButton,
689
+ {
690
+ "aria-label": `Playback speed ${rateLabel}`,
691
+ "data-slot": "media-player-playback-speed",
692
+ className: cn(
693
+ "w-auto px-3 text-gdt-xs font-bold tabular-nums",
694
+ className
695
+ ),
696
+ tooltip: "Playback speed",
697
+ ...props,
698
+ children: rateLabel
699
+ }
700
+ )
701
+ }
702
+ ),
703
+ /* @__PURE__ */ jsx(
704
+ DropdownMenuContent,
705
+ {
706
+ align: "end",
707
+ className: "min-w-24",
708
+ container: portalContainer ?? void 0,
709
+ side: "top",
710
+ sideOffset: 8,
711
+ children: /* @__PURE__ */ jsx(
712
+ DropdownMenuRadioGroup,
713
+ {
714
+ value: String(playbackRate),
715
+ onValueChange: (value) => {
716
+ dispatch({
717
+ type: MediaActionTypes.MEDIA_PLAYBACK_RATE_REQUEST,
718
+ detail: Number(value)
719
+ });
720
+ },
721
+ children: rates.map((rate) => /* @__PURE__ */ jsx(DropdownMenuRadioItem, { closeOnClick: true, value: String(rate), children: formatRate(rate) }, rate))
722
+ }
723
+ )
724
+ }
725
+ )
726
+ ] });
727
+ }
728
+ function MediaPlayerCaptions({
729
+ className,
730
+ disabled,
731
+ onClick,
732
+ ...props
733
+ }) {
734
+ const dispatch = useMediaDispatch();
735
+ const subtitlesList = useMediaSelector((state) => state.mediaSubtitlesList);
736
+ const subtitlesShowing = useMediaSelector(
737
+ (state) => state.mediaSubtitlesShowing
738
+ );
739
+ const hasSubtitles = (subtitlesList?.length ?? 0) > 0;
740
+ const showing = (subtitlesShowing?.length ?? 0) > 0;
741
+ const Icon = showing ? Captions : CaptionsOff;
742
+ return /* @__PURE__ */ jsx(
743
+ MediaControlButton,
744
+ {
745
+ "aria-label": showing ? "Disable captions" : "Enable captions",
746
+ "aria-pressed": showing,
747
+ "data-slot": "media-player-captions",
748
+ className,
749
+ disabled: disabled || !hasSubtitles,
750
+ shortcut: "C",
751
+ tooltip: "Captions",
752
+ onClick: (event) => {
753
+ onClick?.(event);
754
+ if (event.defaultPrevented) {
755
+ return;
756
+ }
757
+ dispatch({ type: MediaActionTypes.MEDIA_TOGGLE_SUBTITLES_REQUEST });
758
+ },
759
+ ...props,
760
+ children: /* @__PURE__ */ jsx(Icon, {})
761
+ }
762
+ );
763
+ }
764
+ function MediaPlayerSettings({
765
+ className,
766
+ rates = DEFAULT_PLAYBACK_RATES,
767
+ ...props
768
+ }) {
769
+ const dispatch = useMediaDispatch();
770
+ const { onMenuOpenChange, portalContainer } = React.useContext(MediaPlayerContext);
771
+ const playbackRate = useMediaSelector(
772
+ (state) => state.mediaPlaybackRate ?? 1
773
+ );
774
+ const subtitlesList = useMediaSelector((state) => state.mediaSubtitlesList);
775
+ const subtitlesShowing = useMediaSelector(
776
+ (state) => state.mediaSubtitlesShowing
777
+ );
778
+ const subtitles = subtitlesList ?? [];
779
+ const showingTrack = subtitlesShowing?.[0];
780
+ const captionsValue = showingTrack ? subtitleTrackId(showingTrack) : "off";
781
+ const captionsLabel = showingTrack ? subtitleTrackLabel(showingTrack) : "Off";
782
+ return /* @__PURE__ */ jsxs(DropdownMenu, { size: "sm", onOpenChange: onMenuOpenChange, children: [
783
+ /* @__PURE__ */ jsx(
784
+ DropdownMenuTrigger,
785
+ {
786
+ render: /* @__PURE__ */ jsx(
787
+ MediaControlButton,
788
+ {
789
+ "aria-label": "Player settings",
790
+ "data-slot": "media-player-settings",
791
+ className,
792
+ tooltip: "Settings",
793
+ ...props,
794
+ children: /* @__PURE__ */ jsx(Settings, {})
795
+ }
796
+ )
797
+ }
798
+ ),
799
+ /* @__PURE__ */ jsxs(
800
+ DropdownMenuContent,
801
+ {
802
+ align: "end",
803
+ className: "min-w-48",
804
+ container: portalContainer ?? void 0,
805
+ side: "top",
806
+ sideOffset: 8,
807
+ children: [
808
+ /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
809
+ /* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { children: [
810
+ "Speed",
811
+ /* @__PURE__ */ jsx("span", { className: "ml-auto rounded-md bg-surface px-1.5 py-0.5 text-gdt-xs font-bold tabular-nums", children: formatRate(playbackRate) })
812
+ ] }),
813
+ /* @__PURE__ */ jsx(DropdownMenuSubContent, { container: portalContainer ?? void 0, children: /* @__PURE__ */ jsx(
814
+ DropdownMenuRadioGroup,
815
+ {
816
+ value: String(playbackRate),
817
+ onValueChange: (value) => {
818
+ dispatch({
819
+ type: MediaActionTypes.MEDIA_PLAYBACK_RATE_REQUEST,
820
+ detail: Number(value)
821
+ });
822
+ },
823
+ children: rates.map((rate) => /* @__PURE__ */ jsx(
824
+ DropdownMenuRadioItem,
825
+ {
826
+ closeOnClick: true,
827
+ value: String(rate),
828
+ children: formatRate(rate)
829
+ },
830
+ rate
831
+ ))
832
+ }
833
+ ) })
834
+ ] }),
835
+ subtitles.length > 0 ? /* @__PURE__ */ jsxs(DropdownMenuSub, { children: [
836
+ /* @__PURE__ */ jsxs(DropdownMenuSubTrigger, { children: [
837
+ "Captions",
838
+ /* @__PURE__ */ jsx("span", { className: "ml-auto rounded-md bg-surface px-1.5 py-0.5 text-gdt-xs font-bold", children: captionsLabel })
839
+ ] }),
840
+ /* @__PURE__ */ jsx(DropdownMenuSubContent, { container: portalContainer ?? void 0, children: /* @__PURE__ */ jsxs(
841
+ DropdownMenuRadioGroup,
842
+ {
843
+ value: captionsValue,
844
+ onValueChange: (value) => {
845
+ if (subtitlesShowing?.length) {
846
+ dispatch({
847
+ type: MediaActionTypes.MEDIA_DISABLE_SUBTITLES_REQUEST,
848
+ detail: subtitlesShowing
849
+ });
850
+ }
851
+ const track = subtitles.find(
852
+ (candidate) => subtitleTrackId(candidate) === value
853
+ );
854
+ if (track) {
855
+ dispatch({
856
+ type: MediaActionTypes.MEDIA_SHOW_SUBTITLES_REQUEST,
857
+ detail: [track]
858
+ });
859
+ }
860
+ },
861
+ children: [
862
+ /* @__PURE__ */ jsx(DropdownMenuRadioItem, { closeOnClick: true, value: "off", children: "Off" }),
863
+ subtitles.map((track) => /* @__PURE__ */ jsx(
864
+ DropdownMenuRadioItem,
865
+ {
866
+ closeOnClick: true,
867
+ value: subtitleTrackId(track),
868
+ children: subtitleTrackLabel(track)
869
+ },
870
+ subtitleTrackId(track)
871
+ ))
872
+ ]
873
+ }
874
+ ) })
875
+ ] }) : null
876
+ ]
877
+ }
878
+ )
879
+ ] });
880
+ }
881
+ function MediaPlayerTime({
882
+ className,
883
+ showDuration = true,
884
+ ...props
885
+ }) {
886
+ const currentTime = useMediaSelector((state) => state.mediaCurrentTime ?? 0);
887
+ const duration = useMediaSelector((state) => state.mediaDuration);
888
+ return /* @__PURE__ */ jsx(
889
+ "span",
890
+ {
891
+ "data-slot": "media-player-time",
892
+ className: cn(
893
+ "inline-flex shrink-0 items-center px-1 text-gdt-sm font-medium tabular-nums text-fg-on-brand/80",
894
+ className
895
+ ),
896
+ ...props,
897
+ children: showDuration ? `${formatTime(currentTime)} / ${formatTime(duration)}` : formatTime(currentTime)
898
+ }
899
+ );
900
+ }
901
+ function MediaPlayerSeek({
902
+ className,
903
+ disabled,
904
+ onValueChange,
905
+ "aria-label": ariaLabel,
906
+ step = 5,
907
+ withTime = true,
908
+ ...props
909
+ }) {
910
+ const dispatch = useMediaDispatch();
911
+ const { portalContainer } = React.useContext(MediaPlayerContext);
912
+ const controlRef = React.useRef(null);
913
+ const [dragValue, setDragValue] = React.useState(null);
914
+ const [hoverValue, setHoverValue] = React.useState(null);
915
+ const [pendingSeek, setPendingSeek] = React.useState(null);
916
+ const currentTime = useMediaSelector((state) => state.mediaCurrentTime ?? 0);
917
+ const duration = useMediaSelector((state) => state.mediaDuration);
918
+ const seekable = useMediaSelector((state) => state.mediaSeekable);
919
+ const buffered = useMediaSelector((state) => state.mediaBuffered);
920
+ const previewImage = useMediaSelector((state) => state.mediaPreviewImage);
921
+ const previewCoords = useMediaSelector((state) => state.mediaPreviewCoords);
922
+ const chapterCues = useMediaSelector((state) => state.mediaChaptersCues);
923
+ const { min, max } = getSeekBounds(seekable, duration);
924
+ const canSeek = max > min;
925
+ const resolvedDisabled = disabled || !canSeek;
926
+ const seekRafRef = React.useRef(null);
927
+ const latestSeekValueRef = React.useRef(0);
928
+ const displayTime = clamp(dragValue ?? pendingSeek ?? currentTime, min, max);
929
+ const sliderStep = canSeek ? clamp((max - min) / 500, 0.05, 1) : 1;
930
+ React.useEffect(() => {
931
+ if (pendingSeek !== null && Math.abs(currentTime - pendingSeek) < 0.5) {
932
+ setPendingSeek(null);
933
+ }
934
+ }, [currentTime, pendingSeek]);
935
+ React.useEffect(() => {
936
+ return () => {
937
+ if (seekRafRef.current !== null) {
938
+ cancelAnimationFrame(seekRafRef.current);
939
+ }
940
+ };
941
+ }, []);
942
+ const queueSeek = (value) => {
943
+ latestSeekValueRef.current = value;
944
+ if (seekRafRef.current !== null) {
945
+ return;
946
+ }
947
+ seekRafRef.current = requestAnimationFrame(() => {
948
+ seekRafRef.current = null;
949
+ dispatch({
950
+ type: MediaActionTypes.MEDIA_SEEK_REQUEST,
951
+ detail: latestSeekValueRef.current
952
+ });
953
+ });
954
+ };
955
+ const previewAt = (value) => {
956
+ dispatch({
957
+ type: MediaActionTypes.MEDIA_PREVIEW_REQUEST,
958
+ detail: clamp(value, min, max)
959
+ });
960
+ };
961
+ const clearPreview = () => {
962
+ setHoverValue(null);
963
+ dispatch({
964
+ type: MediaActionTypes.MEDIA_PREVIEW_REQUEST,
965
+ detail: void 0
966
+ });
967
+ };
968
+ const previewValue = dragValue ?? hoverValue;
969
+ const previewPercent = previewValue !== null && canSeek ? (clamp(previewValue, min, max) - min) / (max - min) * 100 : 0;
970
+ const previewChapter = previewValue !== null ? chapterCues?.find(
971
+ (cue) => previewValue >= cue.startTime && previewValue < cue.endTime
972
+ ) : void 0;
973
+ const previewCoordValues = previewCoords?.map(Number);
974
+ const hasSpriteCoords = previewCoordValues?.length === 4 && (previewCoordValues[2] ?? 0) > 0;
975
+ const previewScale = hasSpriteCoords ? SEEK_PREVIEW_WIDTH / (previewCoordValues?.[2] ?? 1) : 1;
976
+ const previewClampPx = previewImage ? SEEK_PREVIEW_WIDTH / 2 + 4 : 32;
977
+ const controlRect = withTime && previewValue !== null ? controlRef.current?.getBoundingClientRect() : void 0;
978
+ const previewLeft = controlRect ? clamp(
979
+ controlRect.left + previewPercent / 100 * controlRect.width,
980
+ controlRect.left + previewClampPx,
981
+ controlRect.right - previewClampPx
982
+ ) : 0;
983
+ const segmentBounds = canSeek ? [
984
+ min,
985
+ ...(chapterCues ?? []).map((cue) => cue.startTime).filter((time) => time > min && time < max).sort((a, b) => a - b),
986
+ max
987
+ ] : [0, 1];
988
+ const segments = segmentBounds.slice(0, -1).map((start, index) => ({
989
+ start,
990
+ end: segmentBounds[index + 1] ?? start
991
+ }));
992
+ return /* @__PURE__ */ jsxs(
993
+ SliderPrimitive.Root,
994
+ {
995
+ "data-slot": "media-player-seek",
996
+ className: cn(
997
+ "relative flex h-8 w-full min-w-0 touch-none items-center",
998
+ resolvedDisabled && "pointer-events-none opacity-45",
999
+ className
1000
+ ),
1001
+ disabled: resolvedDisabled,
1002
+ largeStep: step,
1003
+ max: canSeek ? max : 100,
1004
+ min,
1005
+ onValueChange: (value) => {
1006
+ const nextValue = Array.isArray(value) ? value[0] ?? 0 : value;
1007
+ setDragValue(nextValue);
1008
+ previewAt(nextValue);
1009
+ queueSeek(nextValue);
1010
+ onValueChange?.(nextValue);
1011
+ },
1012
+ onValueCommitted: (value) => {
1013
+ const nextValue = clamp(
1014
+ Array.isArray(value) ? value[0] ?? 0 : value,
1015
+ min,
1016
+ max
1017
+ );
1018
+ if (seekRafRef.current !== null) {
1019
+ cancelAnimationFrame(seekRafRef.current);
1020
+ seekRafRef.current = null;
1021
+ }
1022
+ dispatch({
1023
+ type: MediaActionTypes.MEDIA_SEEK_REQUEST,
1024
+ detail: nextValue
1025
+ });
1026
+ setPendingSeek(nextValue);
1027
+ setDragValue(null);
1028
+ clearPreview();
1029
+ },
1030
+ step: sliderStep,
1031
+ value: displayTime,
1032
+ ...props,
1033
+ children: [
1034
+ withTime && previewValue !== null && controlRect && ReactDOM.createPortal(
1035
+ /* @__PURE__ */ jsxs(
1036
+ "div",
1037
+ {
1038
+ className: "pointer-events-none fixed z-50 flex -translate-x-1/2 -translate-y-full flex-col items-center overflow-hidden rounded-lg bg-surface-overlay text-fg-primary shadow-2",
1039
+ "data-slot": "media-player-seek-preview",
1040
+ style: { left: previewLeft, top: controlRect.top - 6 },
1041
+ children: [
1042
+ previewImage ? hasSpriteCoords ? /* @__PURE__ */ jsx(
1043
+ "span",
1044
+ {
1045
+ className: "relative block overflow-hidden bg-line-subtle/50",
1046
+ style: {
1047
+ width: SEEK_PREVIEW_WIDTH,
1048
+ height: (previewCoordValues?.[3] ?? 0) * previewScale
1049
+ },
1050
+ children: /* @__PURE__ */ jsx(
1051
+ "img",
1052
+ {
1053
+ src: previewImage,
1054
+ alt: "",
1055
+ draggable: false,
1056
+ className: "absolute top-0 left-0 max-w-none origin-top-left",
1057
+ style: {
1058
+ transform: `scale(${previewScale}) translate(${-(previewCoordValues?.[0] ?? 0)}px, ${-(previewCoordValues?.[1] ?? 0)}px)`
1059
+ }
1060
+ }
1061
+ )
1062
+ }
1063
+ ) : /* @__PURE__ */ jsx(
1064
+ "img",
1065
+ {
1066
+ src: previewImage,
1067
+ alt: "",
1068
+ draggable: false,
1069
+ className: "block aspect-video bg-line-subtle/50 object-cover",
1070
+ style: { width: SEEK_PREVIEW_WIDTH }
1071
+ }
1072
+ ) : null,
1073
+ previewChapter?.text ? /* @__PURE__ */ jsx("span", { className: "max-w-40 truncate px-2.5 pt-1 text-gdt-xs font-bold", children: previewChapter.text }) : null,
1074
+ /* @__PURE__ */ jsx(
1075
+ "span",
1076
+ {
1077
+ className: cn(
1078
+ "px-2.5 py-1 text-gdt-xs font-bold tabular-nums",
1079
+ (previewImage || previewChapter?.text) && "pt-0.5 pb-1.5"
1080
+ ),
1081
+ children: formatTime(previewValue)
1082
+ }
1083
+ )
1084
+ ]
1085
+ }
1086
+ ),
1087
+ portalContainer ?? document.body
1088
+ ),
1089
+ /* @__PURE__ */ jsxs(
1090
+ SliderPrimitive.Control,
1091
+ {
1092
+ ref: controlRef,
1093
+ className: "flex h-8 w-full items-center",
1094
+ onPointerLeave: () => {
1095
+ if (dragValue === null) {
1096
+ clearPreview();
1097
+ }
1098
+ },
1099
+ onPointerMove: (event) => {
1100
+ if (resolvedDisabled || dragValue !== null) {
1101
+ return;
1102
+ }
1103
+ const next = getSeekValueFromPointer(event, min, max);
1104
+ setHoverValue(next.value);
1105
+ previewAt(next.value);
1106
+ },
1107
+ children: [
1108
+ /* @__PURE__ */ jsx(SliderPrimitive.Track, { className: "relative flex h-2 w-full items-center gap-[3px]", children: segments.map(({ start, end }) => {
1109
+ const span = end - start || 1;
1110
+ const fraction = (value) => clamp((clamp(value, start, end) - start) / span, 0, 1);
1111
+ const playedWidth = fraction(displayTime) * 100;
1112
+ return /* @__PURE__ */ jsxs(
1113
+ "span",
1114
+ {
1115
+ "data-slot": "media-player-seek-segment",
1116
+ className: "relative h-full overflow-hidden rounded-full bg-line-subtle/65",
1117
+ style: { width: `${span / (max - min || 1) * 100}%` },
1118
+ children: [
1119
+ canSeek && buffered?.map(
1120
+ ([bufferStart, bufferEnd]) => bufferEnd > start && bufferStart < end ? /* @__PURE__ */ jsx(
1121
+ "span",
1122
+ {
1123
+ "data-slot": "media-player-seek-buffered",
1124
+ className: "absolute inset-y-0 bg-fg-on-brand/25",
1125
+ style: {
1126
+ left: `${fraction(bufferStart) * 100}%`,
1127
+ width: `${(fraction(bufferEnd) - fraction(bufferStart)) * 100}%`
1128
+ }
1129
+ },
1130
+ `${bufferStart}-${bufferEnd}`
1131
+ ) : null
1132
+ ),
1133
+ /* @__PURE__ */ jsx(
1134
+ "span",
1135
+ {
1136
+ "data-slot": "media-player-seek-progress",
1137
+ className: cn(
1138
+ "absolute inset-y-0 left-0 bg-surface-brand",
1139
+ dragValue === null && "transition-[width] duration-200 ease-linear"
1140
+ ),
1141
+ style: { width: `${playedWidth}%` }
1142
+ }
1143
+ )
1144
+ ]
1145
+ },
1146
+ start
1147
+ );
1148
+ }) }),
1149
+ /* @__PURE__ */ jsx(
1150
+ SliderPrimitive.Thumb,
1151
+ {
1152
+ getAriaLabel: () => ariaLabel ?? "Media timeline",
1153
+ getAriaValueText: (_formatted, value) => `${formatTime(value)} of ${formatTime(max)}`,
1154
+ className: cn(
1155
+ "size-3.5 -translate-x-1/2 rounded-full bg-surface-overlay shadow-2 ring-2 ring-surface-brand outline-none",
1156
+ "transition-[inset-inline-start,transform] duration-200 ease-linear data-[dragging]:scale-125 data-[dragging]:transition-transform",
1157
+ "focus-visible:[filter:drop-shadow(0_0_0.25rem_var(--color-line-focus))]"
1158
+ )
1159
+ }
1160
+ )
1161
+ ]
1162
+ }
1163
+ )
1164
+ ]
1165
+ }
1166
+ );
1167
+ }
1168
+ function MediaPlayerLoop({
1169
+ className,
1170
+ onClick,
1171
+ ...props
1172
+ }) {
1173
+ const dispatch = useMediaDispatch();
1174
+ const loop = useMediaSelector((state) => state.mediaLoop === true);
1175
+ return /* @__PURE__ */ jsx(
1176
+ MediaControlButton,
1177
+ {
1178
+ "aria-label": loop ? "Disable loop" : "Enable loop",
1179
+ "aria-pressed": loop,
1180
+ "data-slot": "media-player-loop",
1181
+ className,
1182
+ tooltip: loop ? "Disable loop" : "Enable loop",
1183
+ onClick: (event) => {
1184
+ onClick?.(event);
1185
+ if (event.defaultPrevented) {
1186
+ return;
1187
+ }
1188
+ dispatch({
1189
+ type: MediaActionTypes.MEDIA_LOOP_REQUEST,
1190
+ detail: !loop
1191
+ });
1192
+ },
1193
+ ...props,
1194
+ children: /* @__PURE__ */ jsx(Repeat2, {})
1195
+ }
1196
+ );
1197
+ }
1198
+ function MediaPlayerPiP({
1199
+ className,
1200
+ disabled,
1201
+ onClick,
1202
+ ...props
1203
+ }) {
1204
+ const dispatch = useMediaDispatch();
1205
+ const isPip = useMediaSelector((state) => state.mediaIsPip === true);
1206
+ const unavailable = useMediaSelector((state) => !!state.mediaPipUnavailable);
1207
+ return /* @__PURE__ */ jsx(
1208
+ MediaControlButton,
1209
+ {
1210
+ "aria-label": isPip ? "Exit picture in picture" : "Enter picture in picture",
1211
+ "aria-pressed": isPip,
1212
+ "data-slot": "media-player-pip",
1213
+ className,
1214
+ disabled: disabled || unavailable,
1215
+ tooltip: isPip ? "Exit picture in picture" : "Picture in picture",
1216
+ onClick: (event) => {
1217
+ onClick?.(event);
1218
+ if (event.defaultPrevented) {
1219
+ return;
1220
+ }
1221
+ dispatch({
1222
+ type: isPip ? MediaActionTypes.MEDIA_EXIT_PIP_REQUEST : MediaActionTypes.MEDIA_ENTER_PIP_REQUEST
1223
+ });
1224
+ },
1225
+ ...props,
1226
+ children: /* @__PURE__ */ jsx(PictureInPicture2, {})
1227
+ }
1228
+ );
1229
+ }
1230
+ function MediaPlayerFullscreen({
1231
+ className,
1232
+ disabled,
1233
+ onClick,
1234
+ ...props
1235
+ }) {
1236
+ const dispatch = useMediaDispatch();
1237
+ const isFullscreen = useMediaSelector(
1238
+ (state) => state.mediaIsFullscreen === true
1239
+ );
1240
+ const unavailable = useMediaSelector(
1241
+ (state) => !!state.mediaFullscreenUnavailable
1242
+ );
1243
+ const Icon = isFullscreen ? Minimize : Maximize;
1244
+ return /* @__PURE__ */ jsx(
1245
+ MediaControlButton,
1246
+ {
1247
+ "aria-label": isFullscreen ? "Exit fullscreen" : "Enter fullscreen",
1248
+ "aria-pressed": isFullscreen,
1249
+ "data-slot": "media-player-fullscreen",
1250
+ className,
1251
+ disabled: disabled || unavailable,
1252
+ shortcut: "F",
1253
+ tooltip: isFullscreen ? "Exit fullscreen" : "Fullscreen",
1254
+ onClick: (event) => {
1255
+ onClick?.(event);
1256
+ if (event.defaultPrevented) {
1257
+ return;
1258
+ }
1259
+ dispatch({
1260
+ type: isFullscreen ? MediaActionTypes.MEDIA_EXIT_FULLSCREEN_REQUEST : MediaActionTypes.MEDIA_ENTER_FULLSCREEN_REQUEST
1261
+ });
1262
+ },
1263
+ ...props,
1264
+ children: /* @__PURE__ */ jsx(Icon, {})
1265
+ }
1266
+ );
1267
+ }
1268
+ var MEDIA_ERROR_CONTENT = {
1269
+ 1: {
1270
+ title: "Playback interrupted",
1271
+ description: "Loading the media was interrupted."
1272
+ },
1273
+ 2: {
1274
+ title: "Connection error",
1275
+ description: "A network error kept the media from loading."
1276
+ },
1277
+ 3: {
1278
+ title: "Playback error",
1279
+ description: "The media could not be decoded."
1280
+ },
1281
+ 4: {
1282
+ title: "Unsupported format",
1283
+ description: "The media format is not supported."
1284
+ }
1285
+ };
1286
+ function MediaPlayerError({
1287
+ children,
1288
+ className,
1289
+ ...props
1290
+ }) {
1291
+ const { rootNode } = React.useContext(MediaPlayerContext);
1292
+ const mediaError = useMediaSelector((state) => state.mediaError);
1293
+ const [sourceFailed, setSourceFailed] = React.useState(false);
1294
+ React.useEffect(() => {
1295
+ if (!rootNode) {
1296
+ return;
1297
+ }
1298
+ const update = () => {
1299
+ const media = rootNode.querySelector("video, audio");
1300
+ setSourceFailed(
1301
+ !!media && media.networkState === HTMLMediaElement.NETWORK_NO_SOURCE && media.readyState === HTMLMediaElement.HAVE_NOTHING
1302
+ );
1303
+ };
1304
+ update();
1305
+ rootNode.addEventListener("error", update, true);
1306
+ rootNode.addEventListener("loadstart", update, true);
1307
+ rootNode.addEventListener("loadedmetadata", update, true);
1308
+ return () => {
1309
+ rootNode.removeEventListener("error", update, true);
1310
+ rootNode.removeEventListener("loadstart", update, true);
1311
+ rootNode.removeEventListener("loadedmetadata", update, true);
1312
+ };
1313
+ }, [rootNode]);
1314
+ const error = mediaError ?? (sourceFailed ? { code: 4 } : null);
1315
+ if (!error) {
1316
+ return null;
1317
+ }
1318
+ const content = MEDIA_ERROR_CONTENT[error.code ?? 0] ?? {
1319
+ title: "Something went wrong",
1320
+ description: "The media could not be played."
1321
+ };
1322
+ return /* @__PURE__ */ jsx(
1323
+ "div",
1324
+ {
1325
+ "data-slot": "media-player-error",
1326
+ role: "alert",
1327
+ className: cn(
1328
+ "absolute inset-0 z-20 flex flex-col items-center justify-center gap-1 bg-canvas/85 px-6 text-center text-fg-primary backdrop-blur-sm rounded-2xl",
1329
+ className
1330
+ ),
1331
+ ...props,
1332
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
1333
+ /* @__PURE__ */ jsx(
1334
+ TriangleAlert,
1335
+ {
1336
+ "aria-hidden": "true",
1337
+ className: "mb-2 size-9 text-fg-danger"
1338
+ }
1339
+ ),
1340
+ /* @__PURE__ */ jsx("p", { className: "text-gdt-lg font-bold", children: content.title }),
1341
+ /* @__PURE__ */ jsx("p", { className: "text-gdt-sm text-fg-secondary", children: content.description }),
1342
+ /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center gap-2", children: [
1343
+ /* @__PURE__ */ jsxs(
1344
+ MediaControlButton,
1345
+ {
1346
+ className: "w-auto gap-1.5 px-4 text-gdt-xs font-bold [&_svg]:size-3.5",
1347
+ onClick: () => {
1348
+ rootNode?.querySelector("video, audio")?.load();
1349
+ },
1350
+ children: [
1351
+ /* @__PURE__ */ jsx(RefreshCcw, {}),
1352
+ "Try again"
1353
+ ]
1354
+ }
1355
+ ),
1356
+ /* @__PURE__ */ jsxs(
1357
+ MediaControlButton,
1358
+ {
1359
+ className: "w-auto gap-1.5 px-4 text-gdt-xs font-bold [&_svg]:size-3.5",
1360
+ onClick: () => {
1361
+ window.location.reload();
1362
+ },
1363
+ children: [
1364
+ /* @__PURE__ */ jsx(RotateCcw, {}),
1365
+ "Reload page"
1366
+ ]
1367
+ }
1368
+ )
1369
+ ] })
1370
+ ] })
1371
+ }
1372
+ );
1373
+ }
1374
+ export {
1375
+ MediaPlayer,
1376
+ MediaPlayerAudio,
1377
+ MediaPlayerCaptions,
1378
+ MediaPlayerControls,
1379
+ MediaPlayerControlsOverlay,
1380
+ MediaPlayerError,
1381
+ MediaPlayerFullscreen,
1382
+ MediaPlayerLoop,
1383
+ MediaPlayerPiP,
1384
+ MediaPlayerPlay,
1385
+ MediaPlayerPlaybackSpeed,
1386
+ MediaPlayerSeek,
1387
+ MediaPlayerSeekBackward,
1388
+ MediaPlayerSeekForward,
1389
+ MediaPlayerSettings,
1390
+ MediaPlayerTime,
1391
+ MediaPlayerVideo,
1392
+ MediaPlayerVolume
1393
+ };
1394
+ //# sourceMappingURL=media-player.js.map