tekivex-ui 2.5.15 → 2.6.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 (395) hide show
  1. package/dist/charts.cjs +1 -1
  2. package/dist/charts.js +22 -553
  3. package/dist/chunk-B3ph66Zb.js +1 -0
  4. package/dist/chunk-Bc16rJT-.js +1 -0
  5. package/dist/chunk-Dike2DLU.js +3 -0
  6. package/dist/chunk-DjxOP8W4.js +1 -0
  7. package/dist/chunk-DkoFTL1O.js +1 -0
  8. package/dist/chunk-DmRB1Blb.js +1 -0
  9. package/dist/chunk-DnCXUUx5.js +1 -0
  10. package/dist/chunk-LWkKqC8d.js +1 -0
  11. package/dist/chunk-PJ50l0JC.js +1 -0
  12. package/dist/headless.cjs +1 -1
  13. package/dist/headless.js +1 -442
  14. package/dist/i18n.cjs +1 -1
  15. package/dist/i18n.js +1 -733
  16. package/dist/index.cjs +1 -152
  17. package/dist/index.d.ts +4 -2
  18. package/dist/index.js +429 -16786
  19. package/dist/quantum.cjs +1 -89
  20. package/dist/quantum.js +74 -2551
  21. package/dist/realtime.cjs +1 -23
  22. package/dist/realtime.js +1 -804
  23. package/dist/src/a11y/index.d.ts +0 -1
  24. package/dist/src/charts/TkxAreaChart.d.ts +0 -5
  25. package/dist/src/charts/TkxBarChart.d.ts +0 -3
  26. package/dist/src/charts/TkxDonutChart.d.ts +0 -5
  27. package/dist/src/charts/TkxLineChart.d.ts +0 -1
  28. package/dist/src/charts/TkxPieChart.d.ts +0 -3
  29. package/dist/src/charts/TkxRadarChart.d.ts +0 -3
  30. package/dist/src/charts/TkxScatterChart.d.ts +0 -2
  31. package/dist/src/charts/index.d.ts +0 -1
  32. package/dist/src/charts/shared.d.ts +0 -3
  33. package/dist/src/components/TkxAIChatBubble.d.ts +0 -9
  34. package/dist/src/components/TkxAIConfidenceBar.d.ts +0 -6
  35. package/dist/src/components/TkxAIThinking.d.ts +0 -6
  36. package/dist/src/components/TkxAccordion.d.ts +0 -1
  37. package/dist/src/components/TkxAffix.d.ts +0 -1
  38. package/dist/src/components/TkxAlert.d.ts +0 -1
  39. package/dist/src/components/TkxAnchor.d.ts +0 -1
  40. package/dist/src/components/TkxAppBar.d.ts +0 -1
  41. package/dist/src/components/TkxAutocomplete.d.ts +0 -1
  42. package/dist/src/components/TkxAvatar.d.ts +0 -1
  43. package/dist/src/components/TkxBadge.d.ts +0 -1
  44. package/dist/src/components/TkxBottomNav.d.ts +0 -1
  45. package/dist/src/components/TkxBreadcrumb.d.ts +0 -1
  46. package/dist/src/components/TkxButton.d.ts +0 -1
  47. package/dist/src/components/TkxCard.d.ts +0 -1
  48. package/dist/src/components/TkxCarousel.d.ts +0 -1
  49. package/dist/src/components/TkxCascader.d.ts +0 -1
  50. package/dist/src/components/TkxChat.d.ts +0 -1
  51. package/dist/src/components/TkxCheckbox.d.ts +0 -1
  52. package/dist/src/components/TkxClock.d.ts +0 -1
  53. package/dist/src/components/TkxColorPicker.d.ts +0 -1
  54. package/dist/src/components/TkxCommand.d.ts +0 -1
  55. package/dist/src/components/TkxConfigProvider.d.ts +0 -30
  56. package/dist/src/components/TkxDataGrid.d.ts +0 -15
  57. package/dist/src/components/TkxDatePicker.d.ts +0 -1
  58. package/dist/src/components/TkxDivider.d.ts +0 -1
  59. package/dist/src/components/TkxDrawer.d.ts +0 -1
  60. package/dist/src/components/TkxDropdown.d.ts +0 -1
  61. package/dist/src/components/TkxEmpty.d.ts +0 -1
  62. package/dist/src/components/TkxFileUpload.d.ts +0 -1
  63. package/dist/src/components/TkxForm.d.ts +0 -35
  64. package/dist/src/components/TkxIcon.d.ts +0 -1
  65. package/dist/src/components/TkxImage.d.ts +0 -1
  66. package/dist/src/components/TkxInput.d.ts +1 -1
  67. package/dist/src/components/TkxLayout.d.ts +0 -22
  68. package/dist/src/components/TkxList.d.ts +0 -1
  69. package/dist/src/components/TkxLiveFeed.d.ts +0 -1
  70. package/dist/src/components/TkxLiveLog.d.ts +0 -1
  71. package/dist/src/components/TkxLiveMetrics.d.ts +0 -1
  72. package/dist/src/components/TkxLogo.d.ts +0 -1
  73. package/dist/src/components/TkxMarkdown.d.ts +0 -10
  74. package/dist/src/components/TkxMasonry.d.ts +0 -1
  75. package/dist/src/components/TkxMentions.d.ts +0 -1
  76. package/dist/src/components/TkxMenu.d.ts +0 -1
  77. package/dist/src/components/TkxModal.d.ts +0 -1
  78. package/dist/src/components/TkxNumberInput.d.ts +0 -1
  79. package/dist/src/components/TkxOTP.d.ts +0 -1
  80. package/dist/src/components/TkxOrgChart.d.ts +32 -0
  81. package/dist/src/components/TkxPagination.d.ts +0 -1
  82. package/dist/src/components/TkxPlayground.d.ts +0 -13
  83. package/dist/src/components/TkxPopover.d.ts +0 -1
  84. package/dist/src/components/TkxProgress.d.ts +0 -1
  85. package/dist/src/components/TkxQRCode.d.ts +0 -1
  86. package/dist/src/components/TkxQuantumForm.d.ts +0 -1
  87. package/dist/src/components/TkxRadio.d.ts +0 -1
  88. package/dist/src/components/TkxRating.d.ts +0 -1
  89. package/dist/src/components/TkxRealTimeChart.d.ts +0 -1
  90. package/dist/src/components/TkxResult.d.ts +0 -1
  91. package/dist/src/components/TkxRichTextDisplay.d.ts +0 -1
  92. package/dist/src/components/TkxSegmented.d.ts +0 -1
  93. package/dist/src/components/TkxSelect.d.ts +0 -3
  94. package/dist/src/components/TkxSkeleton.d.ts +0 -1
  95. package/dist/src/components/TkxSlider.d.ts +0 -1
  96. package/dist/src/components/TkxSnackbar.d.ts +0 -1
  97. package/dist/src/components/TkxSpeedDial.d.ts +0 -1
  98. package/dist/src/components/TkxSpin.d.ts +0 -1
  99. package/dist/src/components/TkxStatistic.d.ts +0 -1
  100. package/dist/src/components/TkxStepper.d.ts +0 -1
  101. package/dist/src/components/TkxTable.d.ts +0 -1
  102. package/dist/src/components/TkxTabs.d.ts +0 -1
  103. package/dist/src/components/TkxTag.d.ts +0 -1
  104. package/dist/src/components/TkxThemeBuilder.d.ts +0 -1
  105. package/dist/src/components/TkxTimeline.d.ts +0 -1
  106. package/dist/src/components/TkxToast.d.ts +0 -1
  107. package/dist/src/components/TkxToggle.d.ts +0 -1
  108. package/dist/src/components/TkxToolbar.d.ts +0 -1
  109. package/dist/src/components/TkxTooltip.d.ts +0 -1
  110. package/dist/src/components/TkxTour.d.ts +0 -1
  111. package/dist/src/components/TkxTransferList.d.ts +0 -1
  112. package/dist/src/components/TkxTreeView.d.ts +0 -1
  113. package/dist/src/components/TkxTypography.d.ts +0 -1
  114. package/dist/src/components/TkxVideoPlayer.d.ts +0 -1
  115. package/dist/src/components/TkxWatermark.d.ts +0 -1
  116. package/dist/src/components/index.d.ts +1 -1
  117. package/dist/src/engine/css.d.ts +0 -1
  118. package/dist/src/engine/index.d.ts +0 -1
  119. package/dist/src/engine/quantum-ai.d.ts +0 -73
  120. package/dist/src/engine/quantum.d.ts +0 -1
  121. package/dist/src/engine/security.d.ts +45 -9
  122. package/dist/src/engine/tkx.d.ts +0 -53
  123. package/dist/src/engine/wcag.d.ts +0 -1
  124. package/dist/src/headless/index.d.ts +0 -1
  125. package/dist/src/headless/useControllable.d.ts +0 -15
  126. package/dist/src/headless/useDebounce.d.ts +0 -12
  127. package/dist/src/headless/useDisclosure.d.ts +0 -13
  128. package/dist/src/headless/useFormState.d.ts +0 -13
  129. package/dist/src/headless/useInfiniteQuery.d.ts +0 -11
  130. package/dist/src/headless/useIntersectionObserver.d.ts +0 -10
  131. package/dist/src/headless/useListSelection.d.ts +0 -9
  132. package/dist/src/headless/useLocalStorage.d.ts +0 -8
  133. package/dist/src/headless/useMediaQuery.d.ts +0 -10
  134. package/dist/src/headless/useRovingTabIndex.d.ts +0 -15
  135. package/dist/src/headless/useSSE.d.ts +0 -9
  136. package/dist/src/headless/useThrottle.d.ts +0 -7
  137. package/dist/src/headless/useWebSocket.d.ts +0 -9
  138. package/dist/src/hooks/index.d.ts +0 -1
  139. package/dist/src/i18n/I18nProvider.d.ts +0 -1
  140. package/dist/src/i18n/index.d.ts +0 -1
  141. package/dist/src/quantum/index.d.ts +0 -1
  142. package/dist/src/realtime/index.d.ts +0 -1
  143. package/dist/src/themes/index.d.ts +0 -1
  144. package/package.json +5 -3
  145. package/dist/TkxForm-BaO4wvXt.js +0 -320
  146. package/dist/TkxForm-M6ttomry.cjs +0 -12
  147. package/dist/chunk-Bmb41Sf3.cjs +0 -1
  148. package/dist/hooks-6pRsPOyG.js +0 -58
  149. package/dist/hooks-mivgRljH.cjs +0 -1
  150. package/dist/index.d.ts.map +0 -1
  151. package/dist/src/a11y/index.d.ts.map +0 -1
  152. package/dist/src/charts/TkxAreaChart.d.ts.map +0 -1
  153. package/dist/src/charts/TkxBarChart.d.ts.map +0 -1
  154. package/dist/src/charts/TkxDonutChart.d.ts.map +0 -1
  155. package/dist/src/charts/TkxLineChart.d.ts.map +0 -1
  156. package/dist/src/charts/TkxPieChart.d.ts.map +0 -1
  157. package/dist/src/charts/TkxRadarChart.d.ts.map +0 -1
  158. package/dist/src/charts/TkxScatterChart.d.ts.map +0 -1
  159. package/dist/src/charts/index.d.ts.map +0 -1
  160. package/dist/src/charts/shared.d.ts.map +0 -1
  161. package/dist/src/components/TkxAIChatBubble.d.ts.map +0 -1
  162. package/dist/src/components/TkxAIConfidenceBar.d.ts.map +0 -1
  163. package/dist/src/components/TkxAIThinking.d.ts.map +0 -1
  164. package/dist/src/components/TkxAccordion.d.ts.map +0 -1
  165. package/dist/src/components/TkxAffix.d.ts.map +0 -1
  166. package/dist/src/components/TkxAlert.d.ts.map +0 -1
  167. package/dist/src/components/TkxAnchor.d.ts.map +0 -1
  168. package/dist/src/components/TkxAppBar.d.ts.map +0 -1
  169. package/dist/src/components/TkxAutocomplete.d.ts.map +0 -1
  170. package/dist/src/components/TkxAvatar.d.ts.map +0 -1
  171. package/dist/src/components/TkxBadge.d.ts.map +0 -1
  172. package/dist/src/components/TkxBottomNav.d.ts.map +0 -1
  173. package/dist/src/components/TkxBreadcrumb.d.ts.map +0 -1
  174. package/dist/src/components/TkxButton.d.ts.map +0 -1
  175. package/dist/src/components/TkxCard.d.ts.map +0 -1
  176. package/dist/src/components/TkxCarousel.d.ts.map +0 -1
  177. package/dist/src/components/TkxCascader.d.ts.map +0 -1
  178. package/dist/src/components/TkxChat.d.ts.map +0 -1
  179. package/dist/src/components/TkxCheckbox.d.ts.map +0 -1
  180. package/dist/src/components/TkxClock.d.ts.map +0 -1
  181. package/dist/src/components/TkxColorPicker.d.ts.map +0 -1
  182. package/dist/src/components/TkxCommand.d.ts.map +0 -1
  183. package/dist/src/components/TkxConfigProvider.d.ts.map +0 -1
  184. package/dist/src/components/TkxDataGrid.d.ts.map +0 -1
  185. package/dist/src/components/TkxDatePicker.d.ts.map +0 -1
  186. package/dist/src/components/TkxDivider.d.ts.map +0 -1
  187. package/dist/src/components/TkxDrawer.d.ts.map +0 -1
  188. package/dist/src/components/TkxDropdown.d.ts.map +0 -1
  189. package/dist/src/components/TkxEmpty.d.ts.map +0 -1
  190. package/dist/src/components/TkxFileUpload.d.ts.map +0 -1
  191. package/dist/src/components/TkxForm.d.ts.map +0 -1
  192. package/dist/src/components/TkxIcon.d.ts.map +0 -1
  193. package/dist/src/components/TkxImage.d.ts.map +0 -1
  194. package/dist/src/components/TkxInput.d.ts.map +0 -1
  195. package/dist/src/components/TkxLayout.d.ts.map +0 -1
  196. package/dist/src/components/TkxList.d.ts.map +0 -1
  197. package/dist/src/components/TkxLiveFeed.d.ts.map +0 -1
  198. package/dist/src/components/TkxLiveLog.d.ts.map +0 -1
  199. package/dist/src/components/TkxLiveMetrics.d.ts.map +0 -1
  200. package/dist/src/components/TkxLogo.d.ts.map +0 -1
  201. package/dist/src/components/TkxMarkdown.d.ts.map +0 -1
  202. package/dist/src/components/TkxMasonry.d.ts.map +0 -1
  203. package/dist/src/components/TkxMentions.d.ts.map +0 -1
  204. package/dist/src/components/TkxMenu.d.ts.map +0 -1
  205. package/dist/src/components/TkxModal.d.ts.map +0 -1
  206. package/dist/src/components/TkxNumberInput.d.ts.map +0 -1
  207. package/dist/src/components/TkxOTP.d.ts.map +0 -1
  208. package/dist/src/components/TkxPagination.d.ts.map +0 -1
  209. package/dist/src/components/TkxPlayground.d.ts.map +0 -1
  210. package/dist/src/components/TkxPopover.d.ts.map +0 -1
  211. package/dist/src/components/TkxProgress.d.ts.map +0 -1
  212. package/dist/src/components/TkxQRCode.d.ts.map +0 -1
  213. package/dist/src/components/TkxQuantumForm.d.ts.map +0 -1
  214. package/dist/src/components/TkxRadio.d.ts.map +0 -1
  215. package/dist/src/components/TkxRating.d.ts.map +0 -1
  216. package/dist/src/components/TkxRealTimeChart.d.ts.map +0 -1
  217. package/dist/src/components/TkxResult.d.ts.map +0 -1
  218. package/dist/src/components/TkxRichTextDisplay.d.ts.map +0 -1
  219. package/dist/src/components/TkxSegmented.d.ts.map +0 -1
  220. package/dist/src/components/TkxSelect.d.ts.map +0 -1
  221. package/dist/src/components/TkxSkeleton.d.ts.map +0 -1
  222. package/dist/src/components/TkxSlider.d.ts.map +0 -1
  223. package/dist/src/components/TkxSnackbar.d.ts.map +0 -1
  224. package/dist/src/components/TkxSpeedDial.d.ts.map +0 -1
  225. package/dist/src/components/TkxSpin.d.ts.map +0 -1
  226. package/dist/src/components/TkxStatistic.d.ts.map +0 -1
  227. package/dist/src/components/TkxStepper.d.ts.map +0 -1
  228. package/dist/src/components/TkxTable.d.ts.map +0 -1
  229. package/dist/src/components/TkxTabs.d.ts.map +0 -1
  230. package/dist/src/components/TkxTag.d.ts.map +0 -1
  231. package/dist/src/components/TkxThemeBuilder.d.ts.map +0 -1
  232. package/dist/src/components/TkxTimeline.d.ts.map +0 -1
  233. package/dist/src/components/TkxToast.d.ts.map +0 -1
  234. package/dist/src/components/TkxToggle.d.ts.map +0 -1
  235. package/dist/src/components/TkxToolbar.d.ts.map +0 -1
  236. package/dist/src/components/TkxTooltip.d.ts.map +0 -1
  237. package/dist/src/components/TkxTour.d.ts.map +0 -1
  238. package/dist/src/components/TkxTransferList.d.ts.map +0 -1
  239. package/dist/src/components/TkxTreeView.d.ts.map +0 -1
  240. package/dist/src/components/TkxTypography.d.ts.map +0 -1
  241. package/dist/src/components/TkxVideoPlayer.d.ts.map +0 -1
  242. package/dist/src/components/TkxWatermark.d.ts.map +0 -1
  243. package/dist/src/components/index.d.ts.map +0 -1
  244. package/dist/src/engine/css.d.ts.map +0 -1
  245. package/dist/src/engine/index.d.ts.map +0 -1
  246. package/dist/src/engine/quantum-ai.d.ts.map +0 -1
  247. package/dist/src/engine/quantum.d.ts.map +0 -1
  248. package/dist/src/engine/security.d.ts.map +0 -1
  249. package/dist/src/engine/tkx.d.ts.map +0 -1
  250. package/dist/src/engine/wcag.d.ts.map +0 -1
  251. package/dist/src/headless/index.d.ts.map +0 -1
  252. package/dist/src/headless/useControllable.d.ts.map +0 -1
  253. package/dist/src/headless/useDebounce.d.ts.map +0 -1
  254. package/dist/src/headless/useDisclosure.d.ts.map +0 -1
  255. package/dist/src/headless/useFormState.d.ts.map +0 -1
  256. package/dist/src/headless/useInfiniteQuery.d.ts.map +0 -1
  257. package/dist/src/headless/useIntersectionObserver.d.ts.map +0 -1
  258. package/dist/src/headless/useListSelection.d.ts.map +0 -1
  259. package/dist/src/headless/useLocalStorage.d.ts.map +0 -1
  260. package/dist/src/headless/useMediaQuery.d.ts.map +0 -1
  261. package/dist/src/headless/useRovingTabIndex.d.ts.map +0 -1
  262. package/dist/src/headless/useSSE.d.ts.map +0 -1
  263. package/dist/src/headless/useThrottle.d.ts.map +0 -1
  264. package/dist/src/headless/useWebSocket.d.ts.map +0 -1
  265. package/dist/src/hooks/index.d.ts.map +0 -1
  266. package/dist/src/i18n/I18nProvider.d.ts.map +0 -1
  267. package/dist/src/i18n/index.d.ts.map +0 -1
  268. package/dist/src/quantum/index.d.ts.map +0 -1
  269. package/dist/src/realtime/index.d.ts.map +0 -1
  270. package/dist/src/themes/index.d.ts.map +0 -1
  271. package/dist/themes-BbJanqiC.js +0 -498
  272. package/dist/themes-CMq2vWXj.cjs +0 -2
  273. package/dist/tkx-BoP5V5zy.cjs +0 -4
  274. package/dist/tkx-mOKMzvUK.js +0 -1174
  275. package/src/a11y/index.tsx +0 -87
  276. package/src/charts/TkxAreaChart.tsx +0 -121
  277. package/src/charts/TkxBarChart.tsx +0 -117
  278. package/src/charts/TkxDonutChart.tsx +0 -164
  279. package/src/charts/TkxLineChart.tsx +0 -137
  280. package/src/charts/TkxPieChart.tsx +0 -103
  281. package/src/charts/TkxRadarChart.tsx +0 -92
  282. package/src/charts/TkxScatterChart.tsx +0 -104
  283. package/src/charts/index.ts +0 -22
  284. package/src/charts/shared.ts +0 -47
  285. package/src/components/TkxAIChatBubble.tsx +0 -256
  286. package/src/components/TkxAIConfidenceBar.tsx +0 -133
  287. package/src/components/TkxAIThinking.tsx +0 -193
  288. package/src/components/TkxAccordion.tsx +0 -590
  289. package/src/components/TkxAffix.tsx +0 -140
  290. package/src/components/TkxAlert.tsx +0 -94
  291. package/src/components/TkxAnchor.tsx +0 -195
  292. package/src/components/TkxAppBar.tsx +0 -262
  293. package/src/components/TkxAutocomplete.tsx +0 -455
  294. package/src/components/TkxAvatar.tsx +0 -103
  295. package/src/components/TkxBadge.tsx +0 -75
  296. package/src/components/TkxBottomNav.tsx +0 -241
  297. package/src/components/TkxBreadcrumb.tsx +0 -324
  298. package/src/components/TkxButton.tsx +0 -155
  299. package/src/components/TkxCard.tsx +0 -96
  300. package/src/components/TkxCarousel.tsx +0 -554
  301. package/src/components/TkxCascader.tsx +0 -278
  302. package/src/components/TkxChat.tsx +0 -356
  303. package/src/components/TkxCheckbox.tsx +0 -220
  304. package/src/components/TkxClock.tsx +0 -603
  305. package/src/components/TkxColorPicker.tsx +0 -635
  306. package/src/components/TkxCommand.tsx +0 -646
  307. package/src/components/TkxConfigProvider.tsx +0 -460
  308. package/src/components/TkxDataGrid.tsx +0 -941
  309. package/src/components/TkxDatePicker.tsx +0 -1625
  310. package/src/components/TkxDivider.tsx +0 -51
  311. package/src/components/TkxDrawer.tsx +0 -236
  312. package/src/components/TkxDropdown.tsx +0 -804
  313. package/src/components/TkxEmpty.tsx +0 -221
  314. package/src/components/TkxFileUpload.tsx +0 -373
  315. package/src/components/TkxForm.tsx +0 -683
  316. package/src/components/TkxIcon.tsx +0 -685
  317. package/src/components/TkxImage.tsx +0 -279
  318. package/src/components/TkxInput.tsx +0 -103
  319. package/src/components/TkxLayout.tsx +0 -649
  320. package/src/components/TkxList.tsx +0 -244
  321. package/src/components/TkxLiveFeed.tsx +0 -331
  322. package/src/components/TkxLiveLog.tsx +0 -472
  323. package/src/components/TkxLiveMetrics.tsx +0 -315
  324. package/src/components/TkxLogo.tsx +0 -299
  325. package/src/components/TkxMarkdown.tsx +0 -855
  326. package/src/components/TkxMasonry.tsx +0 -178
  327. package/src/components/TkxMentions.tsx +0 -212
  328. package/src/components/TkxMenu.tsx +0 -1044
  329. package/src/components/TkxModal.tsx +0 -122
  330. package/src/components/TkxNumberInput.tsx +0 -410
  331. package/src/components/TkxOTP.tsx +0 -297
  332. package/src/components/TkxPagination.tsx +0 -390
  333. package/src/components/TkxPlayground.tsx +0 -845
  334. package/src/components/TkxPopover.tsx +0 -297
  335. package/src/components/TkxProgress.tsx +0 -114
  336. package/src/components/TkxQRCode.tsx +0 -176
  337. package/src/components/TkxQuantumForm.tsx +0 -594
  338. package/src/components/TkxRadio.tsx +0 -286
  339. package/src/components/TkxRating.tsx +0 -288
  340. package/src/components/TkxRealTimeChart.tsx +0 -383
  341. package/src/components/TkxResult.tsx +0 -130
  342. package/src/components/TkxRichTextDisplay.tsx +0 -396
  343. package/src/components/TkxSegmented.tsx +0 -171
  344. package/src/components/TkxSelect.tsx +0 -1130
  345. package/src/components/TkxSkeleton.tsx +0 -107
  346. package/src/components/TkxSlider.tsx +0 -495
  347. package/src/components/TkxSnackbar.tsx +0 -316
  348. package/src/components/TkxSpeedDial.tsx +0 -397
  349. package/src/components/TkxSpin.tsx +0 -263
  350. package/src/components/TkxStatistic.tsx +0 -263
  351. package/src/components/TkxStepper.tsx +0 -487
  352. package/src/components/TkxTable.tsx +0 -424
  353. package/src/components/TkxTabs.tsx +0 -126
  354. package/src/components/TkxTag.tsx +0 -321
  355. package/src/components/TkxThemeBuilder.tsx +0 -687
  356. package/src/components/TkxTimeline.tsx +0 -305
  357. package/src/components/TkxToast.tsx +0 -287
  358. package/src/components/TkxToggle.tsx +0 -95
  359. package/src/components/TkxToolbar.tsx +0 -262
  360. package/src/components/TkxTooltip.tsx +0 -85
  361. package/src/components/TkxTour.tsx +0 -241
  362. package/src/components/TkxTransferList.tsx +0 -489
  363. package/src/components/TkxTreeView.tsx +0 -423
  364. package/src/components/TkxTypography.tsx +0 -267
  365. package/src/components/TkxVideoPlayer.tsx +0 -1015
  366. package/src/components/TkxWatermark.tsx +0 -145
  367. package/src/components/index.ts +0 -80
  368. package/src/engine/css.ts +0 -126
  369. package/src/engine/index.ts +0 -12
  370. package/src/engine/quantum-ai.ts +0 -1045
  371. package/src/engine/quantum.ts +0 -207
  372. package/src/engine/security.ts +0 -232
  373. package/src/engine/tkx.ts +0 -1084
  374. package/src/engine/wcag.ts +0 -276
  375. package/src/headless/index.ts +0 -52
  376. package/src/headless/useControllable.ts +0 -42
  377. package/src/headless/useDebounce.ts +0 -37
  378. package/src/headless/useDisclosure.ts +0 -30
  379. package/src/headless/useFormState.ts +0 -113
  380. package/src/headless/useInfiniteQuery.ts +0 -155
  381. package/src/headless/useIntersectionObserver.ts +0 -53
  382. package/src/headless/useListSelection.ts +0 -103
  383. package/src/headless/useLocalStorage.ts +0 -48
  384. package/src/headless/useMediaQuery.ts +0 -39
  385. package/src/headless/useRovingTabIndex.ts +0 -86
  386. package/src/headless/useSSE.ts +0 -167
  387. package/src/headless/useThrottle.ts +0 -48
  388. package/src/headless/useWebSocket.ts +0 -231
  389. package/src/hooks/index.ts +0 -122
  390. package/src/i18n/I18nProvider.tsx +0 -36
  391. package/src/i18n/index.ts +0 -877
  392. package/src/quantum/index.ts +0 -39
  393. package/src/realtime/index.ts +0 -21
  394. package/src/styles/global.css +0 -167
  395. package/src/themes/index.ts +0 -294
@@ -1,1015 +0,0 @@
1
- 'use client';
2
-
3
- import {
4
- useState,
5
- useEffect,
6
- useRef,
7
- useCallback,
8
- type CSSProperties,
9
- type ReactNode,
10
- } from 'react';
11
- import { useTheme } from '../themes';
12
- import { sanitizeString } from '../engine/security';
13
- import { useReducedMotion } from '../hooks';
14
-
15
- // ── Types ────────────────────────────────────────────────────────────────────
16
-
17
- export interface VideoChapter {
18
- time: number;
19
- label: string;
20
- }
21
-
22
- export interface VideoSubtitle {
23
- label: string;
24
- src: string;
25
- lang: string;
26
- default?: boolean;
27
- }
28
-
29
- export interface TkxVideoPlayerProps {
30
- src: string | { src: string; type: string }[];
31
- poster?: string;
32
- title?: string;
33
- width?: string | number;
34
- height?: string | number;
35
- autoPlay?: boolean;
36
- loop?: boolean;
37
- muted?: boolean;
38
- controls?: boolean;
39
- showTitle?: boolean;
40
- allowFullscreen?: boolean;
41
- allowPiP?: boolean;
42
- allowDownload?: boolean;
43
- chapters?: VideoChapter[];
44
- subtitles?: VideoSubtitle[];
45
- startTime?: number;
46
- onPlay?: () => void;
47
- onPause?: () => void;
48
- onEnded?: () => void;
49
- onTimeUpdate?: (currentTime: number, duration: number) => void;
50
- className?: string;
51
- style?: CSSProperties;
52
- }
53
-
54
- // ── Utility ──────────────────────────────────────────────────────────────────
55
-
56
- function formatTime(seconds: number): string {
57
- if (!isFinite(seconds) || isNaN(seconds)) return '0:00';
58
- const h = Math.floor(seconds / 3600);
59
- const m = Math.floor((seconds % 3600) / 60);
60
- const s = Math.floor(seconds % 60);
61
- if (h > 0) {
62
- return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
63
- }
64
- return `${m}:${String(s).padStart(2, '0')}`;
65
- }
66
-
67
- // ── Icon primitives (inline SVG) ─────────────────────────────────────────────
68
-
69
- function SvgIcon({ path, size = 20, color = 'currentColor', title }: { path: string; size?: number; color?: string; title?: string }) {
70
- return (
71
- <svg
72
- width={size}
73
- height={size}
74
- viewBox="0 0 24 24"
75
- fill={color}
76
- aria-hidden={title ? undefined : 'true'}
77
- role={title ? 'img' : undefined}
78
- aria-label={title}
79
- style={{ display: 'block', flexShrink: 0 }}
80
- >
81
- <path d={path} />
82
- </svg>
83
- );
84
- }
85
-
86
- // Material-style path data
87
- const ICONS = {
88
- play: 'M8 5v14l11-7z',
89
- pause: 'M6 19h4V5H6v14zm8-14v14h4V5h-4z',
90
- volumeUp: 'M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z',
91
- volumeMute: 'M16.5 12c0-1.77-1.02-3.29-2.5-4.03v2.21l2.45 2.45c.03-.2.05-.41.05-.63zm2.5 0c0 .94-.2 1.82-.54 2.64l1.51 1.51C20.63 14.91 21 13.5 21 12c0-4.28-2.99-7.86-7-8.77v2.06c2.89.86 5 3.54 5 6.71zM4.27 3L3 4.27 7.73 9H3v6h4l5 5v-6.73l4.25 4.25c-.67.52-1.42.93-2.25 1.18v2.06c1.38-.31 2.63-.95 3.69-1.81L19.73 21 21 19.73l-9-9L4.27 3zM12 4L9.91 6.09 12 8.18V4z',
92
- volumeDown: 'M18.5 12c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM5 9v6h4l5 5V4L9 9H5z',
93
- fullscreen: 'M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z',
94
- fullscreenExit: 'M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z',
95
- pip: 'M19 11h-8v6h8v-6zm4 8V4.98C23 3.88 22.1 3 21 3H3c-1.1 0-2 .88-2 1.98V19c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2zm-2 .02H3V4.97h18v14.05z',
96
- download: 'M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z',
97
- closedCaption: 'M19 4H5c-1.11 0-2 .9-2 2v12c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-8 7H9.5v-.5h-2v3h2V13H11v1c0 .55-.45 1-1 1H7c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1zm7 0h-1.5v-.5h-2v3h2V13H18v1c0 .55-.45 1-1 1h-3c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1h3c.55 0 1 .45 1 1v1z',
98
- settings: 'M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z',
99
- replay10: 'M11.99 5V1l-5 5 5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6h-2c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z',
100
- forward10: 'M18 13c0 3.31-2.69 6-6 6s-6-2.69-6-6h-2c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8V1l-5 5 5 5V7c3.31 0 6 2.69 6 6z',
101
- errorOutline: 'M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z',
102
- refresh: 'M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z',
103
- };
104
-
105
- // ── Control button ────────────────────────────────────────────────────────────
106
-
107
- function CtrlBtn({
108
- onClick,
109
- title,
110
- children,
111
- active,
112
- primary,
113
- }: {
114
- onClick: () => void;
115
- title: string;
116
- children: ReactNode;
117
- active?: boolean;
118
- primary: string;
119
- }) {
120
- const [hovered, setHovered] = useState(false);
121
-
122
- return (
123
- <button
124
- type="button"
125
- onClick={onClick}
126
- title={title}
127
- aria-label={title}
128
- onMouseEnter={() => setHovered(true)}
129
- onMouseLeave={() => setHovered(false)}
130
- style={{
131
- background: 'none',
132
- border: 'none',
133
- padding: '6px',
134
- cursor: 'pointer',
135
- color: active ? primary : hovered ? primary : '#ffffff',
136
- display: 'flex',
137
- alignItems: 'center',
138
- justifyContent: 'center',
139
- borderRadius: '4px',
140
- flexShrink: 0,
141
- transition: 'color 0.15s ease',
142
- }}
143
- >
144
- {children}
145
- </button>
146
- );
147
- }
148
-
149
- // ── Main component ────────────────────────────────────────────────────────────
150
-
151
- export function TkxVideoPlayer({
152
- src,
153
- poster,
154
- title,
155
- width = '100%',
156
- height,
157
- autoPlay = false,
158
- loop = false,
159
- muted: initialMuted = false,
160
- controls = true,
161
- showTitle = true,
162
- allowFullscreen = true,
163
- allowPiP = true,
164
- allowDownload = false,
165
- chapters = [],
166
- subtitles = [],
167
- startTime,
168
- onPlay,
169
- onPause,
170
- onEnded,
171
- onTimeUpdate,
172
- className,
173
- style,
174
- }: TkxVideoPlayerProps) {
175
- const theme = useTheme();
176
- const reducedMotion = useReducedMotion();
177
-
178
- const videoRef = useRef<HTMLVideoElement>(null);
179
- const containerRef = useRef<HTMLDivElement>(null);
180
- const progressRef = useRef<HTMLDivElement>(null);
181
- const hideTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
182
-
183
- const [playing, setPlaying] = useState(false);
184
- const [currentTime, setCurrentTime] = useState(0);
185
- const [duration, setDuration] = useState(0);
186
- const [buffered, setBuffered] = useState(0);
187
- const [volume, setVolume] = useState(1);
188
- const [muted, setMuted] = useState(initialMuted);
189
- const [fullscreen, setFullscreen] = useState(false);
190
- const [showControls, setShowControls] = useState(true);
191
- const [controlsLocked, setControlsLocked] = useState(false); // locked visible when paused
192
- const [playbackRate, setPlaybackRate] = useState(1);
193
- const [showSpeedMenu, setShowSpeedMenu] = useState(false);
194
- const [subtitleIdx, setSubtitleIdx] = useState<number>(-1); // -1 = off
195
- const [hoverTime, setHoverTime] = useState<number | null>(null);
196
- const [hoverX, setHoverX] = useState(0);
197
- const [centerFlash, setCenterFlash] = useState<'play' | 'pause' | null>(null);
198
- const [isPiP, setIsPiP] = useState(false);
199
- const [isBuffering, setIsBuffering] = useState(false);
200
- const [videoError, setVideoError] = useState<string | null>(null);
201
- const [volumeHover, setVolumeHover] = useState(false);
202
-
203
- // ── Video event wiring ────────────────────────────────────────────────────
204
-
205
- useEffect(() => {
206
- const v = videoRef.current;
207
- if (!v) return;
208
-
209
- if (startTime !== undefined) v.currentTime = startTime;
210
-
211
- const onTimeUpdate = () => {
212
- setCurrentTime(v.currentTime);
213
- if (v.buffered.length > 0) {
214
- setBuffered(v.buffered.end(v.buffered.length - 1));
215
- }
216
- };
217
- const onDurationChange = () => setDuration(v.duration);
218
- const onPlayEvt = () => setPlaying(true);
219
- const onPauseEvt = () => { setPlaying(false); setControlsLocked(true); };
220
- const onEndedEvt = () => { setPlaying(false); setControlsLocked(true); onEnded?.(); };
221
- const onPiPEnter = () => setIsPiP(true);
222
- const onPiPLeave = () => setIsPiP(false);
223
- const onWaiting = () => setIsBuffering(true);
224
- const onCanPlay = () => setIsBuffering(false);
225
- const onError = () => {
226
- const err = v.error;
227
- setVideoError(err ? `Error ${err.code}: ${err.message || 'Failed to load video'}` : 'Failed to load video');
228
- setIsBuffering(false);
229
- };
230
-
231
- v.addEventListener('timeupdate', onTimeUpdate);
232
- v.addEventListener('durationchange', onDurationChange);
233
- v.addEventListener('play', onPlayEvt);
234
- v.addEventListener('pause', onPauseEvt);
235
- v.addEventListener('ended', onEndedEvt);
236
- v.addEventListener('enterpictureinpicture', onPiPEnter);
237
- v.addEventListener('leavepictureinpicture', onPiPLeave);
238
- v.addEventListener('waiting', onWaiting);
239
- v.addEventListener('canplay', onCanPlay);
240
- v.addEventListener('error', onError);
241
-
242
- return () => {
243
- v.removeEventListener('timeupdate', onTimeUpdate);
244
- v.removeEventListener('durationchange', onDurationChange);
245
- v.removeEventListener('play', onPlayEvt);
246
- v.removeEventListener('pause', onPauseEvt);
247
- v.removeEventListener('ended', onEndedEvt);
248
- v.removeEventListener('enterpictureinpicture', onPiPEnter);
249
- v.removeEventListener('leavepictureinpicture', onPiPLeave);
250
- v.removeEventListener('waiting', onWaiting);
251
- v.removeEventListener('canplay', onCanPlay);
252
- v.removeEventListener('error', onError);
253
- };
254
- }, [onEnded, startTime]);
255
-
256
- useEffect(() => {
257
- const v = videoRef.current;
258
- if (!v) return;
259
- if (playing) {
260
- onPlay?.();
261
- } else {
262
- onPause?.();
263
- }
264
- }, [playing, onPlay, onPause]);
265
-
266
- useEffect(() => {
267
- onTimeUpdate?.(currentTime, duration);
268
- }, [currentTime, duration, onTimeUpdate]);
269
-
270
- // ── Subtitle track activation ─────────────────────────────────────────────
271
-
272
- useEffect(() => {
273
- const v = videoRef.current;
274
- if (!v) return;
275
- const tracks = v.textTracks;
276
- for (let i = 0; i < tracks.length; i++) {
277
- tracks[i].mode = i === subtitleIdx ? 'showing' : 'disabled';
278
- }
279
- }, [subtitleIdx]);
280
-
281
- // ── Fullscreen listener ───────────────────────────────────────────────────
282
-
283
- useEffect(() => {
284
- const onFSChange = () => setFullscreen(!!document.fullscreenElement);
285
- document.addEventListener('fullscreenchange', onFSChange);
286
- return () => document.removeEventListener('fullscreenchange', onFSChange);
287
- }, []);
288
-
289
- // ── Keyboard shortcuts ────────────────────────────────────────────────────
290
-
291
- useEffect(() => {
292
- const container = containerRef.current;
293
- if (!container) return;
294
-
295
- const onKey = (e: KeyboardEvent) => {
296
- const v = videoRef.current;
297
- if (!v) return;
298
- if ((e.target as HTMLElement).tagName === 'INPUT') return;
299
-
300
- switch (e.key) {
301
- case ' ':
302
- case 'k':
303
- e.preventDefault();
304
- togglePlay();
305
- break;
306
- case 'f':
307
- case 'F':
308
- if (allowFullscreen) toggleFullscreen();
309
- break;
310
- case 'm':
311
- case 'M':
312
- toggleMute();
313
- break;
314
- case 'ArrowRight':
315
- e.preventDefault();
316
- v.currentTime = Math.min(v.currentTime + 10, v.duration);
317
- break;
318
- case 'ArrowLeft':
319
- e.preventDefault();
320
- v.currentTime = Math.max(v.currentTime - 10, 0);
321
- break;
322
- case 'ArrowUp':
323
- e.preventDefault();
324
- setVolume(Math.min(volume + 0.1, 1));
325
- if (videoRef.current) videoRef.current.volume = Math.min(volume + 0.1, 1);
326
- break;
327
- case 'ArrowDown':
328
- e.preventDefault();
329
- setVolume(Math.max(volume - 0.1, 0));
330
- if (videoRef.current) videoRef.current.volume = Math.max(volume - 0.1, 0);
331
- break;
332
- }
333
- };
334
-
335
- container.addEventListener('keydown', onKey);
336
- return () => container.removeEventListener('keydown', onKey);
337
- // eslint-disable-next-line react-hooks/exhaustive-deps
338
- }, [volume, allowFullscreen]);
339
-
340
- // ── Controls visibility ───────────────────────────────────────────────────
341
-
342
- const resetHideTimer = useCallback(() => {
343
- setShowControls(true);
344
- if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
345
- hideTimerRef.current = setTimeout(() => {
346
- if (playing) setShowControls(false);
347
- }, 3000);
348
- }, [playing]);
349
-
350
- useEffect(() => {
351
- resetHideTimer();
352
- return () => {
353
- if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
354
- };
355
- }, [playing, resetHideTimer]);
356
-
357
- // ── Actions ───────────────────────────────────────────────────────────────
358
-
359
- const togglePlay = useCallback(() => {
360
- const v = videoRef.current;
361
- if (!v) return;
362
- if (v.paused) {
363
- v.play().catch(() => {});
364
- setCenterFlash('play');
365
- setControlsLocked(false);
366
- } else {
367
- v.pause();
368
- setCenterFlash('pause');
369
- }
370
- setTimeout(() => setCenterFlash(null), 600);
371
- }, []);
372
-
373
- const toggleMute = useCallback(() => {
374
- const v = videoRef.current;
375
- if (!v) return;
376
- const next = !muted;
377
- setMuted(next);
378
- v.muted = next;
379
- }, [muted]);
380
-
381
- const toggleFullscreen = useCallback(() => {
382
- const c = containerRef.current;
383
- if (!c) return;
384
- if (!document.fullscreenElement) {
385
- c.requestFullscreen().catch(() => {});
386
- } else {
387
- document.exitFullscreen().catch(() => {});
388
- }
389
- }, []);
390
-
391
- const togglePiP = useCallback(async () => {
392
- const v = videoRef.current;
393
- if (!v) return;
394
- try {
395
- if (document.pictureInPictureElement) {
396
- await document.exitPictureInPicture();
397
- } else {
398
- await v.requestPictureInPicture();
399
- }
400
- } catch {
401
- // PiP not supported or denied
402
- }
403
- }, []);
404
-
405
- const cycleSubtitle = useCallback(() => {
406
- if (subtitles.length === 0) return;
407
- setSubtitleIdx((i) => (i + 1) % (subtitles.length + 1) - 1);
408
- // cycles: -1, 0, 1, ..., subtitles.length-1, -1
409
- setSubtitleIdx((i) => {
410
- const next = i + 1;
411
- return next >= subtitles.length ? -1 : next;
412
- });
413
- }, [subtitles.length]);
414
-
415
- const skipForward = useCallback(() => {
416
- const v = videoRef.current;
417
- if (!v) return;
418
- v.currentTime = Math.min(v.currentTime + 10, v.duration || 0);
419
- }, []);
420
-
421
- const skipBack = useCallback(() => {
422
- const v = videoRef.current;
423
- if (!v) return;
424
- v.currentTime = Math.max(v.currentTime - 10, 0);
425
- }, []);
426
-
427
- const retryLoad = useCallback(() => {
428
- const v = videoRef.current;
429
- if (!v) return;
430
- setVideoError(null);
431
- v.load();
432
- }, []);
433
-
434
- const handleDoubleClick = useCallback(() => {
435
- if (allowFullscreen) toggleFullscreen();
436
- }, [allowFullscreen, toggleFullscreen]);
437
-
438
- const seek = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
439
- const v = videoRef.current;
440
- const bar = progressRef.current;
441
- if (!v || !bar || !duration) return;
442
- const rect = bar.getBoundingClientRect();
443
- const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
444
- v.currentTime = ratio * duration;
445
- }, [duration]);
446
-
447
- const onProgressHover = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
448
- const bar = progressRef.current;
449
- if (!bar || !duration) return;
450
- const rect = bar.getBoundingClientRect();
451
- const ratio = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
452
- setHoverTime(ratio * duration);
453
- setHoverX(e.clientX - rect.left);
454
- }, [duration]);
455
-
456
- const changeVolume = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
457
- const v = parseFloat(e.target.value);
458
- setVolume(v);
459
- if (videoRef.current) {
460
- videoRef.current.volume = v;
461
- videoRef.current.muted = v === 0;
462
- setMuted(v === 0);
463
- }
464
- }, []);
465
-
466
- const changeSpeed = useCallback((rate: number) => {
467
- setPlaybackRate(rate);
468
- if (videoRef.current) videoRef.current.playbackRate = rate;
469
- setShowSpeedMenu(false);
470
- }, []);
471
-
472
- // ── Chapter lookup ────────────────────────────────────────────────────────
473
-
474
- const currentChapter = chapters.length > 0
475
- ? [...chapters].reverse().find((c) => currentTime >= c.time)
476
- : null;
477
-
478
- const getChapterAtTime = (t: number) => {
479
- return [...chapters].reverse().find((c) => t >= c.time) ?? null;
480
- };
481
-
482
- // ── Source resolution ─────────────────────────────────────────────────────
483
-
484
- const sources = Array.isArray(src) ? src : [{ src, type: 'video/mp4' }];
485
- const downloadSrc = Array.isArray(src) ? src[0].src : src;
486
-
487
- // ── Render ────────────────────────────────────────────────────────────────
488
-
489
- const controlsVisible = showControls || controlsLocked || !playing;
490
- const transition = reducedMotion ? 'none' : 'opacity 0.25s ease';
491
-
492
- const volumeIcon = muted || volume === 0
493
- ? ICONS.volumeMute
494
- : volume < 0.5
495
- ? ICONS.volumeDown
496
- : ICONS.volumeUp;
497
-
498
- const progressPct = duration > 0 ? (currentTime / duration) * 100 : 0;
499
- const bufferedPct = duration > 0 ? (buffered / duration) * 100 : 0;
500
-
501
- return (
502
- <div
503
- ref={containerRef}
504
- className={className}
505
- tabIndex={0}
506
- onMouseMove={resetHideTimer}
507
- onMouseLeave={() => { if (playing) setShowControls(false); }}
508
- style={{
509
- position: 'relative',
510
- width,
511
- height: height ?? 'auto',
512
- background: '#000',
513
- borderRadius: '8px',
514
- overflow: 'hidden',
515
- outline: 'none',
516
- userSelect: 'none',
517
- ...style,
518
- }}
519
- >
520
- {/* Video element */}
521
- <video
522
- ref={videoRef}
523
- style={{ width: '100%', height: '100%', display: 'block', objectFit: 'contain' }}
524
- poster={poster}
525
- autoPlay={autoPlay}
526
- loop={loop}
527
- muted={initialMuted}
528
- playsInline
529
- onClick={togglePlay}
530
- onDoubleClick={handleDoubleClick}
531
- >
532
- {sources.map((s, i) => (
533
- <source key={i} src={s.src} type={s.type} />
534
- ))}
535
- {subtitles.map((sub, i) => (
536
- <track
537
- key={i}
538
- kind="subtitles"
539
- label={sanitizeString(sub.label)}
540
- srcLang={sub.lang}
541
- src={sub.src}
542
- default={sub.default}
543
- />
544
- ))}
545
- </video>
546
-
547
- {/* Title overlay */}
548
- {showTitle && title && (
549
- <div
550
- style={{
551
- position: 'absolute',
552
- top: 0,
553
- left: 0,
554
- right: 0,
555
- background: 'linear-gradient(to bottom, rgba(0,0,0,0.75) 0%, transparent 100%)',
556
- padding: '16px 16px 32px',
557
- opacity: controlsVisible ? 1 : 0,
558
- transition,
559
- pointerEvents: 'none',
560
- }}
561
- >
562
- <span
563
- style={{
564
- color: '#fff',
565
- fontSize: '0.95rem',
566
- fontWeight: 600,
567
- textShadow: '0 1px 3px rgba(0,0,0,0.7)',
568
- }}
569
- >
570
- {sanitizeString(title)}
571
- </span>
572
- {currentChapter && (
573
- <span style={{ color: 'rgba(255,255,255,0.65)', fontSize: '0.8rem', marginLeft: 8 }}>
574
- — {sanitizeString(currentChapter.label)}
575
- </span>
576
- )}
577
- </div>
578
- )}
579
-
580
- {/* Center play/pause flash */}
581
- {centerFlash && (
582
- <div
583
- aria-hidden="true"
584
- style={{
585
- position: 'absolute',
586
- top: '50%',
587
- left: '50%',
588
- transform: 'translate(-50%, -50%)',
589
- background: 'rgba(0,0,0,0.45)',
590
- borderRadius: '50%',
591
- width: 64,
592
- height: 64,
593
- display: 'flex',
594
- alignItems: 'center',
595
- justifyContent: 'center',
596
- animation: reducedMotion ? 'none' : 'tkxvp-flash 0.6s ease forwards',
597
- pointerEvents: 'none',
598
- }}
599
- >
600
- <SvgIcon path={centerFlash === 'play' ? ICONS.play : ICONS.pause} size={32} color="#fff" />
601
- </div>
602
- )}
603
-
604
- {/* Loading spinner overlay */}
605
- {isBuffering && !videoError && (
606
- <div
607
- aria-label="Loading"
608
- style={{
609
- position: 'absolute',
610
- top: '50%',
611
- left: '50%',
612
- transform: 'translate(-50%, -50%)',
613
- pointerEvents: 'none',
614
- zIndex: 10,
615
- }}
616
- >
617
- <div
618
- style={{
619
- width: 48,
620
- height: 48,
621
- border: '4px solid rgba(255,255,255,0.25)',
622
- borderTopColor: theme.primary,
623
- borderRadius: '50%',
624
- animation: reducedMotion ? 'none' : 'tkxvp-spin 0.8s linear infinite',
625
- }}
626
- />
627
- </div>
628
- )}
629
-
630
- {/* Error state overlay */}
631
- {videoError && (
632
- <div
633
- style={{
634
- position: 'absolute',
635
- inset: 0,
636
- display: 'flex',
637
- flexDirection: 'column',
638
- alignItems: 'center',
639
- justifyContent: 'center',
640
- background: 'rgba(0,0,0,0.8)',
641
- zIndex: 20,
642
- gap: 12,
643
- }}
644
- >
645
- <SvgIcon path={ICONS.errorOutline} size={48} color="#ef4444" />
646
- <span style={{ color: '#fff', fontSize: '0.9rem', maxWidth: '80%', textAlign: 'center' }}>
647
- {videoError}
648
- </span>
649
- <button
650
- type="button"
651
- onClick={retryLoad}
652
- style={{
653
- display: 'flex',
654
- alignItems: 'center',
655
- gap: 6,
656
- background: theme.primary,
657
- color: '#fff',
658
- border: 'none',
659
- borderRadius: 6,
660
- padding: '8px 18px',
661
- fontSize: '0.85rem',
662
- fontWeight: 600,
663
- cursor: 'pointer',
664
- marginTop: 4,
665
- }}
666
- >
667
- <SvgIcon path={ICONS.refresh} size={18} color="#fff" />
668
- Retry
669
- </button>
670
- </div>
671
- )}
672
-
673
- {/* Controls bar */}
674
- {controls && (
675
- <div
676
- style={{
677
- position: 'absolute',
678
- bottom: 0,
679
- left: 0,
680
- right: 0,
681
- background: 'linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.4) 70%, transparent 100%)',
682
- padding: '32px 12px 8px',
683
- opacity: controlsVisible ? 1 : 0,
684
- transition,
685
- }}
686
- onMouseEnter={() => setControlsLocked(true)}
687
- onMouseLeave={() => setControlsLocked(false)}
688
- >
689
- {/* Progress bar */}
690
- <div
691
- ref={progressRef}
692
- role="slider"
693
- aria-label="Seek"
694
- aria-valuemin={0}
695
- aria-valuemax={Math.floor(duration)}
696
- aria-valuenow={Math.floor(currentTime)}
697
- tabIndex={0}
698
- onClick={seek}
699
- onMouseMove={onProgressHover}
700
- style={{
701
- position: 'relative',
702
- height: 4,
703
- borderRadius: 2,
704
- background: 'rgba(255,255,255,0.25)',
705
- cursor: 'pointer',
706
- marginBottom: 8,
707
- transition: 'height 0.15s ease',
708
- }}
709
- onMouseEnter={(e) => { (e.currentTarget as HTMLDivElement).style.height = '8px'; }}
710
- onMouseLeave={(e) => { (e.currentTarget as HTMLDivElement).style.height = '4px'; setHoverTime(null); }}
711
- >
712
- {/* Buffered */}
713
- <div
714
- style={{
715
- position: 'absolute',
716
- top: 0, left: 0, bottom: 0,
717
- width: `${bufferedPct}%`,
718
- background: 'rgba(255,255,255,0.3)',
719
- borderRadius: 2,
720
- pointerEvents: 'none',
721
- }}
722
- />
723
- {/* Progress fill */}
724
- <div
725
- style={{
726
- position: 'absolute',
727
- top: 0, left: 0, bottom: 0,
728
- width: `${progressPct}%`,
729
- background: theme.primary,
730
- borderRadius: 2,
731
- pointerEvents: 'none',
732
- }}
733
- />
734
- {/* Scrubber thumb */}
735
- <div
736
- style={{
737
- position: 'absolute',
738
- top: '50%',
739
- left: `${progressPct}%`,
740
- transform: 'translate(-50%, -50%)',
741
- width: 12,
742
- height: 12,
743
- borderRadius: '50%',
744
- background: theme.primary,
745
- pointerEvents: 'none',
746
- }}
747
- />
748
- {/* Chapter markers (dots with hover labels) */}
749
- {chapters.map((ch, i) => {
750
- const pct = duration > 0 ? (ch.time / duration) * 100 : 0;
751
- return (
752
- <div
753
- key={i}
754
- title={sanitizeString(ch.label)}
755
- style={{
756
- position: 'absolute',
757
- top: '50%',
758
- left: `${pct}%`,
759
- transform: 'translate(-50%, -50%)',
760
- width: 8,
761
- height: 8,
762
- borderRadius: '50%',
763
- background: '#fff',
764
- border: `2px solid ${theme.primary}`,
765
- zIndex: 2,
766
- cursor: 'pointer',
767
- pointerEvents: 'auto',
768
- }}
769
- />
770
- );
771
- })}
772
- {/* Hover tooltip */}
773
- {hoverTime !== null && (
774
- <div
775
- style={{
776
- position: 'absolute',
777
- bottom: 12,
778
- left: hoverX,
779
- transform: 'translateX(-50%)',
780
- background: 'rgba(0,0,0,0.8)',
781
- color: '#fff',
782
- fontSize: '0.72rem',
783
- padding: '2px 6px',
784
- borderRadius: 4,
785
- whiteSpace: 'nowrap',
786
- pointerEvents: 'none',
787
- }}
788
- >
789
- {formatTime(hoverTime)}
790
- {getChapterAtTime(hoverTime) && (
791
- <span style={{ display: 'block', fontSize: '0.65rem', color: 'rgba(255,255,255,0.7)' }}>
792
- {sanitizeString(getChapterAtTime(hoverTime)!.label)}
793
- </span>
794
- )}
795
- </div>
796
- )}
797
- </div>
798
-
799
- {/* Button row */}
800
- <div style={{ display: 'flex', alignItems: 'center', gap: 2 }}>
801
- {/* Play/Pause */}
802
- <CtrlBtn onClick={togglePlay} title={playing ? 'Pause' : 'Play'} primary={theme.primary}>
803
- <SvgIcon path={playing ? ICONS.pause : ICONS.play} size={22} />
804
- </CtrlBtn>
805
-
806
- {/* Skip back 10s */}
807
- <CtrlBtn onClick={skipBack} title="Skip back 10 seconds" primary={theme.primary}>
808
- <div style={{ position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
809
- <SvgIcon path={ICONS.replay10} size={22} />
810
- <span style={{ position: 'absolute', fontSize: '7px', fontWeight: 700, color: 'currentColor', marginTop: 1 }}>10</span>
811
- </div>
812
- </CtrlBtn>
813
-
814
- {/* Skip forward 10s */}
815
- <CtrlBtn onClick={skipForward} title="Skip forward 10 seconds" primary={theme.primary}>
816
- <div style={{ position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
817
- <SvgIcon path={ICONS.forward10} size={22} />
818
- <span style={{ position: 'absolute', fontSize: '7px', fontWeight: 700, color: 'currentColor', marginTop: 1 }}>10</span>
819
- </div>
820
- </CtrlBtn>
821
-
822
- {/* Volume with hover slider */}
823
- <div
824
- style={{ display: 'flex', alignItems: 'center', position: 'relative' }}
825
- onMouseEnter={() => setVolumeHover(true)}
826
- onMouseLeave={() => setVolumeHover(false)}
827
- >
828
- <CtrlBtn onClick={toggleMute} title={muted ? 'Unmute' : 'Mute'} primary={theme.primary}>
829
- <SvgIcon path={volumeIcon} size={20} />
830
- </CtrlBtn>
831
- <div
832
- style={{
833
- overflow: 'hidden',
834
- width: volumeHover ? 64 : 0,
835
- opacity: volumeHover ? 1 : 0,
836
- transition: reducedMotion ? 'none' : 'width 0.2s ease, opacity 0.2s ease',
837
- display: 'flex',
838
- alignItems: 'center',
839
- }}
840
- >
841
- <input
842
- type="range"
843
- min={0}
844
- max={1}
845
- step={0.05}
846
- value={muted ? 0 : volume}
847
- onChange={changeVolume}
848
- aria-label="Volume"
849
- style={{
850
- width: 64,
851
- accentColor: theme.primary,
852
- cursor: 'pointer',
853
- height: 4,
854
- }}
855
- />
856
- </div>
857
- </div>
858
-
859
- {/* Time */}
860
- <span
861
- style={{
862
- color: '#fff',
863
- fontSize: '0.78rem',
864
- fontFamily: 'monospace',
865
- fontVariantNumeric: 'tabular-nums',
866
- marginLeft: 6,
867
- whiteSpace: 'nowrap',
868
- flexShrink: 0,
869
- }}
870
- >
871
- {formatTime(currentTime)} / {formatTime(duration)}
872
- </span>
873
-
874
- {/* Spacer */}
875
- <div style={{ flex: 1 }} />
876
-
877
- {/* Subtitles */}
878
- {subtitles.length > 0 && (
879
- <CtrlBtn
880
- onClick={cycleSubtitle}
881
- title="Subtitles"
882
- primary={theme.primary}
883
- active={subtitleIdx >= 0}
884
- >
885
- <SvgIcon path={ICONS.closedCaption} size={20} />
886
- </CtrlBtn>
887
- )}
888
-
889
- {/* Playback speed */}
890
- <div style={{ position: 'relative', display: 'flex', alignItems: 'center' }}>
891
- <button
892
- type="button"
893
- onClick={() => setShowSpeedMenu((v) => !v)}
894
- title="Playback speed"
895
- style={{
896
- background: 'none',
897
- border: 'none',
898
- cursor: 'pointer',
899
- color: '#fff',
900
- fontSize: '0.75rem',
901
- fontWeight: 600,
902
- padding: '6px 8px',
903
- letterSpacing: '0.02em',
904
- position: 'relative',
905
- }}
906
- >
907
- {playbackRate}×
908
- </button>
909
- {playbackRate !== 1 && (
910
- <span
911
- style={{
912
- position: 'absolute',
913
- top: 0,
914
- right: 0,
915
- background: theme.primary,
916
- color: '#fff',
917
- fontSize: '0.55rem',
918
- fontWeight: 700,
919
- borderRadius: 6,
920
- padding: '1px 4px',
921
- lineHeight: 1.2,
922
- pointerEvents: 'none',
923
- }}
924
- >
925
- {playbackRate}x
926
- </span>
927
- )}
928
- {showSpeedMenu && (
929
- <div
930
- style={{
931
- position: 'absolute',
932
- bottom: '100%',
933
- right: 0,
934
- background: 'rgba(20,20,20,0.97)',
935
- border: `1px solid ${theme.border}`,
936
- borderRadius: 6,
937
- overflow: 'hidden',
938
- zIndex: 100,
939
- minWidth: 80,
940
- }}
941
- >
942
- {[0.5, 0.75, 1, 1.25, 1.5, 2].map((rate) => (
943
- <button
944
- key={rate}
945
- type="button"
946
- onClick={() => changeSpeed(rate)}
947
- style={{
948
- display: 'block',
949
- width: '100%',
950
- padding: '6px 14px',
951
- background: rate === playbackRate ? theme.primary + '30' : 'none',
952
- border: 'none',
953
- color: rate === playbackRate ? theme.primary : '#fff',
954
- fontSize: '0.8rem',
955
- cursor: 'pointer',
956
- textAlign: 'left',
957
- }}
958
- >
959
- {rate}×
960
- </button>
961
- ))}
962
- </div>
963
- )}
964
- </div>
965
-
966
- {/* PiP */}
967
- {allowPiP && typeof document !== 'undefined' && 'pictureInPictureEnabled' in document && (
968
- <CtrlBtn onClick={togglePiP} title="Picture-in-Picture" primary={theme.primary} active={isPiP}>
969
- <SvgIcon path={ICONS.pip} size={20} />
970
- </CtrlBtn>
971
- )}
972
-
973
- {/* Download */}
974
- {allowDownload && (
975
- <a
976
- href={downloadSrc}
977
- download
978
- title="Download"
979
- style={{
980
- color: '#fff',
981
- display: 'flex',
982
- alignItems: 'center',
983
- padding: '6px',
984
- }}
985
- >
986
- <SvgIcon path={ICONS.download} size={20} />
987
- </a>
988
- )}
989
-
990
- {/* Fullscreen */}
991
- {allowFullscreen && (
992
- <CtrlBtn onClick={toggleFullscreen} title={fullscreen ? 'Exit fullscreen' : 'Fullscreen'} primary={theme.primary}>
993
- <SvgIcon path={fullscreen ? ICONS.fullscreenExit : ICONS.fullscreen} size={20} />
994
- </CtrlBtn>
995
- )}
996
- </div>
997
- </div>
998
- )}
999
-
1000
- {/* Keyframe for center flash animation */}
1001
- <style>{`
1002
- @keyframes tkxvp-flash {
1003
- 0% { opacity: 1; transform: translate(-50%,-50%) scale(1); }
1004
- 100% { opacity: 0; transform: translate(-50%,-50%) scale(1.5); }
1005
- }
1006
- @keyframes tkxvp-spin {
1007
- 0% { transform: rotate(0deg); }
1008
- 100% { transform: rotate(360deg); }
1009
- }
1010
- `}</style>
1011
- </div>
1012
- );
1013
- }
1014
-
1015
- TkxVideoPlayer.displayName = 'TkxVideoPlayer';