@24i/bigscreen-sdk 1.0.4 → 1.0.5-4.alpha-2832

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 (292) hide show
  1. package/README.md +2 -3
  2. package/ci/is_ci_job_skippable.sh +153 -0
  3. package/package.json +55 -34
  4. package/packages/adobe-heartbeat/src/AdobeHeartbeat.ts +9 -9
  5. package/packages/analytics/README.md +8 -0
  6. package/packages/analytics/src/A.ts +1 -0
  7. package/packages/analytics/src/Analytics.ts +1 -0
  8. package/packages/analytics/src/analyticsOnScroll.ts +1 -0
  9. package/packages/analytics/src/clients/ConsoleAnalytics/ConsoleAnalytics.ts +1 -0
  10. package/packages/analytics/src/clients/ConsoleAnalytics/index.ts +1 -0
  11. package/packages/analytics/src/clients/GoogleAnalytics/GoogleAnalytics.ts +196 -0
  12. package/packages/analytics/src/clients/GoogleAnalytics/constants.ts +126 -0
  13. package/packages/analytics/src/clients/GoogleAnalytics/getUrl.ts +17 -0
  14. package/packages/analytics/src/clients/GoogleAnalytics/index.ts +2 -0
  15. package/packages/analytics/src/clients/GoogleAnalytics/interface.ts +82 -0
  16. package/packages/analytics/src/clients/GoogleAnalytics/mapPayload.ts +51 -0
  17. package/packages/analytics/src/clients/GoogleAnalytics/prepareBody.ts +14 -0
  18. package/packages/analytics/src/clients/TealiumAnalytics/TealiumAnalytics.ts +181 -0
  19. package/packages/analytics/src/clients/TealiumAnalytics/constants.ts +106 -0
  20. package/packages/analytics/src/clients/TealiumAnalytics/findForLanguage.ts +9 -0
  21. package/packages/analytics/src/clients/TealiumAnalytics/index.ts +4 -0
  22. package/packages/analytics/src/clients/TealiumAnalytics/interface.ts +35 -0
  23. package/packages/analytics/src/clients/TealiumAnalytics/mappers/__mocks__/mediaPayload.ts +72 -0
  24. package/packages/analytics/src/clients/TealiumAnalytics/mappers/helper.ts +36 -0
  25. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapAppError.ts +13 -0
  26. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapAppOpen.ts +13 -0
  27. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapFavoriteAdd.ts +11 -0
  28. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapFavoriteRemove.ts +11 -0
  29. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapPlayerClose.ts +15 -0
  30. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapPlayerOpen.ts +15 -0
  31. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapSceneView.ts +18 -0
  32. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapSearch.ts +18 -0
  33. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapSearchFailed.ts +8 -0
  34. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapSearchSuccess.ts +8 -0
  35. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapVideoPause.ts +15 -0
  36. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapVideoProgress.ts +18 -0
  37. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapVideoResume.ts +15 -0
  38. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapVideoStart.ts +15 -0
  39. package/packages/analytics/src/clients/TealiumAnalytics/mappers/mapVideoStop.ts +15 -0
  40. package/packages/analytics/src/clients/TealiumAnalytics/types.ts +161 -0
  41. package/packages/analytics/src/clients/TwentyFourIQ/TwentyFourIQClient.ts +107 -0
  42. package/packages/analytics/src/clients/TwentyFourIQ/constants.ts +111 -0
  43. package/packages/analytics/src/clients/TwentyFourIQ/helper.ts +34 -0
  44. package/packages/analytics/src/clients/TwentyFourIQ/index.ts +2 -0
  45. package/packages/analytics/src/clients/TwentyFourIQ/interface.ts +34 -0
  46. package/packages/analytics/src/clients/TwentyFourIQ/mappers/mapAdEvent.ts +20 -0
  47. package/packages/analytics/src/clients/TwentyFourIQ/mappers/mapBase.ts +24 -0
  48. package/packages/analytics/src/clients/TwentyFourIQ/mappers/mapBuffering.ts +15 -0
  49. package/packages/analytics/src/clients/TwentyFourIQ/mappers/mapPlayerClose.ts +25 -0
  50. package/packages/analytics/src/clients/TwentyFourIQ/mappers/mapPlayerOpen.ts +17 -0
  51. package/packages/analytics/src/clients/TwentyFourIQ/mappers/mapSceneView.ts +15 -0
  52. package/packages/analytics/src/clients/TwentyFourIQ/mappers/mapScroll.ts +17 -0
  53. package/packages/analytics/src/clients/TwentyFourIQ/mappers/mapVideoComplete.ts +22 -0
  54. package/packages/analytics/src/clients/TwentyFourIQ/mappers/mapVideoPause.ts +25 -0
  55. package/packages/analytics/src/clients/TwentyFourIQ/mappers/mapVideoProgress.ts +25 -0
  56. package/packages/analytics/src/clients/TwentyFourIQ/mappers/mapVideoStart.ts +34 -0
  57. package/packages/analytics/src/clients/TwentyFourIQ/mappers/mapVideoStop.ts +32 -0
  58. package/packages/analytics/src/clients/TwentyFourIQ/types.ts +132 -0
  59. package/packages/analytics/src/constants.ts +1 -0
  60. package/packages/analytics/src/index.ts +3 -0
  61. package/packages/analytics/src/interface.ts +1 -0
  62. package/packages/analytics/src/utils/generateSessionId.ts +8 -0
  63. package/packages/async-image/src/AsyncImage.tsx +15 -10
  64. package/packages/developer-tools/PlayerDebugWindow/README.md +1 -1
  65. package/packages/developer-tools/src/DeveloperConsole/DeveloperConsole.tsx +1 -0
  66. package/packages/developer-tools/src/DeveloperToolsService/DeveloperToolsList.tsx +11 -0
  67. package/packages/developer-tools/src/PlayerDebugWindow/PlayerDebugWindow.tsx +5 -5
  68. package/packages/developer-tools/src/PlayerDebugWindow/constants.ts +15 -0
  69. package/packages/developer-tools/src/TechnicalInfo/TechnicalInfo.tsx +2 -0
  70. package/packages/developer-tools/src/TechnicalInfo/TechnicalInfoDetail.tsx +2 -1
  71. package/packages/developer-tools/src/TechnicalInfo/TechnicalInfoProvider.ts +57 -5
  72. package/packages/developer-tools/src/TechnicalInfo/__mocks__/Device.ts +12 -0
  73. package/packages/developer-tools/src/TechnicalInfo/translations.ts +8 -0
  74. package/packages/device/src/driver/index.android.tv.ts +1 -0
  75. package/packages/device/src/driver/index.firetv.ts +1 -0
  76. package/packages/device/src/driver/index.smartcast.tv.ts +1 -0
  77. package/packages/device/src/getKeyDigit.ts +2 -2
  78. package/packages/device/src/resolver/resolver.ts +19 -0
  79. package/packages/digital-clock/src/DigitalClock.tsx +15 -4
  80. package/packages/driver-androidtv/src/DeviceAndroidTV.ts +262 -0
  81. package/packages/driver-androidtv/src/__mocks__/javaScriptBridge.ts +45 -0
  82. package/packages/driver-androidtv/src/formatUserAgent.ts +11 -0
  83. package/packages/driver-androidtv/src/index.ts +2 -0
  84. package/packages/driver-androidtv/src/keymap.ts +27 -0
  85. package/packages/driver-androidtv/src/types.ts +155 -0
  86. package/packages/driver-base/src/DeviceBase.ts +73 -2
  87. package/packages/driver-base/src/KeyMap/Key.ts +8 -1
  88. package/packages/driver-base/src/KeyMap/KeyMap.ts +4 -0
  89. package/packages/driver-base/src/KeyMap/interface.ts +1 -0
  90. package/packages/driver-base/src/KeyMap/overrideValues.ts +2 -1
  91. package/packages/driver-base/src/__mocks__/DeviceBase.ts +24 -0
  92. package/packages/driver-base/src/__mocks__/keyMap.ts +1 -0
  93. package/packages/driver-base/src/index.ts +3 -0
  94. package/packages/driver-base/src/types/AdInfo.ts +5 -0
  95. package/packages/driver-base/src/types/DeviceEvent.ts +2 -0
  96. package/packages/driver-base/src/types/InAppPurchase.ts +20 -0
  97. package/packages/driver-base/src/types/KeycodeKeyMap.ts +1 -0
  98. package/packages/driver-browser/src/DeviceBrowser.ts +26 -0
  99. package/packages/driver-browser/src/keymap.ts +8 -0
  100. package/packages/driver-entone/src/DeviceEntone.ts +31 -1
  101. package/packages/driver-entone/src/keymap.ts +1 -0
  102. package/packages/driver-firetv/src/DeviceFireTV.ts +86 -0
  103. package/packages/driver-firetv/src/__mocks__/javaScriptBridge.ts +45 -0
  104. package/packages/driver-firetv/src/index.ts +1 -0
  105. package/packages/driver-firetv/src/types.ts +1 -0
  106. package/packages/driver-hbbtv/src/DeviceHbbTV.ts +26 -0
  107. package/packages/driver-kreatv/src/DeviceKreaTV.ts +26 -0
  108. package/packages/driver-saphi/src/DeviceSaphi.ts +26 -0
  109. package/packages/driver-smartcast/src/DeviceSmartCast.ts +380 -0
  110. package/packages/driver-smartcast/src/__mocks__/api.ts +69 -0
  111. package/packages/driver-smartcast/src/index.ts +1 -0
  112. package/packages/driver-smartcast/src/keymap.ts +9 -0
  113. package/packages/driver-smartcast/src/types.ts +102 -0
  114. package/packages/driver-tizen/src/DeviceTizen.ts +121 -7
  115. package/packages/driver-tizen/src/ITizen.ts +15116 -202
  116. package/packages/driver-tizen/src/IWebapis.ts +8643 -87
  117. package/packages/driver-tizen/src/TizenKeys.ts +4 -2
  118. package/packages/driver-tizen/src/__mocks__/tizen.ts +7 -8
  119. package/packages/driver-tizen/src/constants.ts +1 -195
  120. package/packages/driver-vidaa/src/DeviceVidaa.ts +29 -2
  121. package/packages/driver-vidaa/src/__mocks__/vidaa.ts +1 -1
  122. package/packages/driver-vidaa/src/keymap.ts +2 -0
  123. package/packages/driver-webos/src/DeviceWebos.ts +55 -0
  124. package/packages/driver-webos/src/__mocks__/webos.ts +33 -0
  125. package/packages/driver-webos/src/constants.ts +1 -0
  126. package/packages/driver-webos/src/keymap.ts +2 -1
  127. package/packages/driver-webos/src/types.ts +30 -0
  128. package/packages/driver-xbox/src/DeviceXbox.ts +30 -1
  129. package/packages/driver-xbox/src/keymap.ts +1 -1
  130. package/packages/epg/README.md +3 -1
  131. package/packages/epg/src/v2/Cell.tsx +16 -2
  132. package/packages/epg/src/v2/DataManager.ts +112 -41
  133. package/packages/epg/src/v2/DatePicker.tsx +9 -3
  134. package/packages/epg/src/v2/DateToast/DateToast.tsx +12 -9
  135. package/packages/epg/src/v2/Day.tsx +6 -3
  136. package/packages/epg/src/v2/Epg.tsx +5 -5
  137. package/packages/epg/src/v2/MockGenerator/generator.ts +3 -2
  138. package/packages/epg/src/v2/ProgramInfo/utils.ts +4 -3
  139. package/packages/epg/src/v2/Row.tsx +3 -1
  140. package/packages/epg/src/v2/TimelineSection.tsx +4 -1
  141. package/packages/epg/src/v2/basic/CellsManager.tsx +7 -2
  142. package/packages/epg/src/v2/basic/RowsManager.tsx +3 -1
  143. package/packages/epg/src/v2/interface.ts +13 -0
  144. package/packages/events-manager/src/EventsManager.ts +1 -56
  145. package/packages/focus/README.md +33 -0
  146. package/packages/focus/src/IFocusable.ts +1 -0
  147. package/packages/focus/src/Layout/Base.tsx +41 -6
  148. package/packages/focus/src/Layout/Matrix.tsx +51 -19
  149. package/packages/focus/src/focusFirstExisting.ts +7 -1
  150. package/packages/focus/src/hasFocus.ts +36 -0
  151. package/packages/focus/src/index.ts +2 -1
  152. package/packages/gallup/src/Gallup.ts +3 -3
  153. package/packages/grid/src/Base/Base.tsx +15 -10
  154. package/packages/grid/src/FirstOnlyGrid/FirstOnlyGrid.tsx +4 -2
  155. package/packages/grid/src/UnifiedGridController/UnifiedGridController.ts +6 -6
  156. package/packages/icon/src/Icon.tsx +27 -5
  157. package/packages/input/README.md +1 -1
  158. package/packages/input/src/Input.tsx +10 -10
  159. package/packages/interactable/README.md +2 -1
  160. package/packages/interactable/src/Interactable.tsx +9 -3
  161. package/packages/interactable/src/interface.ts +1 -0
  162. package/packages/jsx/src/index.ts +1 -1
  163. package/packages/jsx/src/test-utils/index.ts +1 -0
  164. package/packages/jsx/src/test-utils/keyup.ts +27 -0
  165. package/packages/keyboard/README.md +2 -0
  166. package/packages/keyboard/src/Backdrop/Backdrop.scss +2 -0
  167. package/packages/keyboard/src/KeyboardBase.tsx +16 -4
  168. package/packages/keyboard/src/WhitelabelKeyboard/Keyboard.scss +10 -0
  169. package/packages/keyboard/src/WhitelabelKeyboard/Keyboard.tsx +128 -142
  170. package/packages/keyboard/src/WhitelabelKeyboard/WhitelabelStyles/whitelabel.scss +4 -0
  171. package/packages/keyboard/src/WhitelabelKeyboard/_sizes.1080.scss +1 -0
  172. package/packages/keyboard/src/WhitelabelKeyboard/_sizes.scss +1 -0
  173. package/packages/keyboard/src/WhitelabelKeyboard/icons/key_language.svg +20 -0
  174. package/packages/keyboard/src/WhitelabelKeyboard/index.ts +1 -1
  175. package/packages/keyboard/src/WhitelabelKeyboard/layouts/arabic.ts +27 -0
  176. package/packages/keyboard/src/WhitelabelKeyboard/layouts/burmese.ts +27 -0
  177. package/packages/keyboard/src/WhitelabelKeyboard/layouts/dari.ts +28 -0
  178. package/packages/keyboard/src/WhitelabelKeyboard/layouts/hebrew.ts +28 -0
  179. package/packages/keyboard/src/WhitelabelKeyboard/layouts/index.ts +15 -0
  180. package/packages/keyboard/src/WhitelabelKeyboard/layouts/korean.ts +26 -0
  181. package/packages/keyboard/src/WhitelabelKeyboard/layouts/latin.ts +26 -0
  182. package/packages/keyboard/src/WhitelabelKeyboard/layouts/pashto.ts +27 -0
  183. package/packages/keyboard/src/WhitelabelKeyboard/layouts/persian.ts +27 -0
  184. package/packages/keyboard/src/WhitelabelKeyboard/layouts/russian.ts +27 -0
  185. package/packages/keyboard/src/WhitelabelKeyboard/layouts/spanish.ts +26 -0
  186. package/packages/keyboard/src/WhitelabelKeyboard/layouts/special.ts +28 -0
  187. package/packages/keyboard/src/WhitelabelKeyboard/layouts/tibetan.ts +26 -0
  188. package/packages/keyboard/src/WhitelabelKeyboard/layouts/turkish.ts +26 -0
  189. package/packages/keyboard/src/WhitelabelKeyboard/layouts/ukrainian.ts +27 -0
  190. package/packages/keyboard/src/WhitelabelKeyboard/layouts/urdu.ts +26 -0
  191. package/packages/keyboard/src/WhitelabelKeyboard/types.ts +22 -2
  192. package/packages/keyboard/src/types/index.ts +1 -1
  193. package/packages/keyboard/src/utils/caret.ts +5 -8
  194. package/packages/keyboard/src/utils/input.ts +5 -6
  195. package/packages/keyboard/src/utils/setInputSelectionRange.ts +10 -0
  196. package/packages/l10n/README.md +18 -0
  197. package/packages/l10n/src/date/common.ts +18 -6
  198. package/packages/l10n/src/date/cs.ts +2 -8
  199. package/packages/l10n/src/date/de.ts +97 -0
  200. package/packages/l10n/src/date/en-001.ts +3 -3
  201. package/packages/l10n/src/date/en-common.ts +5 -4
  202. package/packages/l10n/src/date/en-us.ts +3 -3
  203. package/packages/l10n/src/date/es.ts +71 -0
  204. package/packages/l10n/src/date/fr.ts +94 -0
  205. package/packages/l10n/src/date/he.ts +101 -0
  206. package/packages/l10n/src/date/iso.ts +12 -13
  207. package/packages/l10n/src/date/types.ts +9 -0
  208. package/packages/l10n/src/l10n.ts +61 -3
  209. package/packages/list/src/Base/Base.tsx +19 -9
  210. package/packages/list/src/Base/interface.ts +2 -2
  211. package/packages/list/src/BasicList/BasicList.tsx +7 -7
  212. package/packages/list/src/CenteredList/CenteredList.tsx +2 -2
  213. package/packages/list/src/CenteredList/controller.ts +0 -1
  214. package/packages/list/src/EdgeOffsetList/EdgeOffsetList.tsx +10 -10
  215. package/packages/list/src/FirstOnlyList/FirstOnlyList.tsx +2 -2
  216. package/packages/list/src/FirstOnlyVariedList/FirstOnlyVariedList.tsx +2 -2
  217. package/packages/list/src/FixedToEndList/FixedToEndList.tsx +2 -2
  218. package/packages/list/src/UnifiedListController/UnifiedListController.ts +6 -2
  219. package/packages/list/src/interface.ts +2 -2
  220. package/packages/logger/src/loggers/ConsoleLogger/ConsoleLogger.ts +7 -1
  221. package/packages/logger/src/loggers/SentryLogger/Sentry.ts +1 -0
  222. package/packages/logger/src/loggers/SentryLogger/SentryLogger.ts +5 -3
  223. package/packages/menu/src/MenuAndContentContainer/MenuAndContentContainer.tsx +31 -3
  224. package/packages/menu/src/MenuAndContentContainer/interface.ts +8 -0
  225. package/packages/modal-service/README.md +4 -1
  226. package/packages/modal-service/src/ModalService.tsx +29 -5
  227. package/packages/perf-utils/README.md +1 -120
  228. package/packages/perf-utils/src/array/filter.ts +1 -54
  229. package/packages/perf-utils/src/array/find.ts +1 -43
  230. package/packages/perf-utils/src/array/findIndex.ts +1 -43
  231. package/packages/perf-utils/src/array/forEach.ts +1 -48
  232. package/packages/perf-utils/src/array/includes.ts +1 -41
  233. package/packages/perf-utils/src/array/includesNaN.ts +1 -32
  234. package/packages/perf-utils/src/array/index.ts +1 -7
  235. package/packages/perf-utils/src/array/map.ts +1 -52
  236. package/packages/player-ui/README.md +1 -1
  237. package/packages/player-ui/src/CurrentTime.tsx +2 -2
  238. package/packages/player-ui/src/Duration.tsx +2 -2
  239. package/packages/player-ui/src/PauseButton.tsx +8 -0
  240. package/packages/player-ui/src/PlayerInteractable.tsx +1 -0
  241. package/packages/player-ui/src/PlayerTime.tsx +3 -1
  242. package/packages/player-ui/src/PlayerUI.tsx +68 -35
  243. package/packages/player-ui/src/RemainingTime.tsx +2 -2
  244. package/packages/player-ui/src/Seekbar.tsx +110 -18
  245. package/packages/player-ui/src/Seeking.ts +57 -16
  246. package/packages/player-ui/src/Subtitles.tsx +2 -2
  247. package/packages/player-ui/src/index.ts +1 -0
  248. package/packages/player-ui/src/mocks.ts +32 -8
  249. package/packages/player-ui/src/timeUtils.ts +3 -3
  250. package/packages/player-ui/src/types.ts +9 -3
  251. package/packages/router/README.md +1 -0
  252. package/packages/router/src/Route.tsx +61 -27
  253. package/packages/router/src/Router.tsx +63 -14
  254. package/packages/router/src/history.ts +8 -1
  255. package/packages/router/src/index.ts +1 -1
  256. package/packages/router/src/utils.ts +9 -4
  257. package/packages/sass-utils/src/linear-gradient.scss +9 -4
  258. package/packages/sass-utils/src/scale.1080.scss +3 -1
  259. package/packages/sass-utils/src/scale.720.scss +1 -1
  260. package/packages/time/README.md +4 -1
  261. package/packages/time/src/Time.ts +35 -13
  262. package/packages/time/src/adapters/24iMediaTimeApi.ts +47 -0
  263. package/packages/time/src/constants.ts +1 -0
  264. package/packages/time/src/services/24iMediaTimeApi/24iMediaTimeApi.ts +43 -0
  265. package/packages/time/src/services/24iMediaTimeApi/index.ts +6 -0
  266. package/packages/toast/src/ToastService.scss +1 -0
  267. package/packages/toast/src/ToastService.tsx +16 -5
  268. package/packages/types-bigscreen-jsx/README.md +5 -4
  269. package/packages/types-bigscreen-jsx/index.d.ts +72 -67
  270. package/packages/utils/README.md +27 -0
  271. package/packages/utils/src/elementUtils.ts +25 -0
  272. package/packages/utils/src/forceReflow.ts +14 -0
  273. package/packages/utils/src/index.ts +8 -0
  274. package/packages/utils/src/keyPress/index.ts +6 -0
  275. package/packages/utils/src/keyPress/keyPressSimulator.ts +74 -0
  276. package/packages/utils/src/keyPress/simulateKeyPress.ts +25 -0
  277. package/packages/utils/src/scaledImage.ts +6 -3
  278. package/packages/utils/src/textUtils.ts +46 -14
  279. package/packages/utils/src/wait.ts +3 -1
  280. package/packages/utils/src/xhr/xhrSend.ts +3 -1
  281. package/packages/utils/src/xhr/xhrSendRetry.ts +3 -1
  282. package/packages/zapping/src/ChannelZapping.ts +9 -0
  283. package/packages/zapping/src/Zapping.tsx +10 -0
  284. package/utils/release/release.ts +156 -0
  285. package/packages/driver-tizen/src/types.ts +0 -14
  286. package/packages/focus/src/Layout/__test__/Base.spec.ts +0 -180
  287. package/packages/focus/src/Layout/__test__/Horizontal.spec.ts +0 -94
  288. package/packages/focus/src/Layout/__test__/Matrix.spec.ts +0 -261
  289. package/packages/focus/src/Layout/__test__/Vertical.spec.ts +0 -46
  290. package/packages/focus/src/Layout/__test__/isRtl.spec.ts +0 -23
  291. package/packages/focus/src/Layout/__test__/shared.ts +0 -19
  292. package/packages/keyboard/src/WhitelabelKeyboard/focusMatrix.ts +0 -19
@@ -1,4 +1,5 @@
1
1
  export interface IFocusable {
2
2
  focus(options?: FocusOptions): void,
3
3
  hasDom?(element: HTMLElement): boolean,
4
+ hasFocus?(): boolean,
4
5
  }
@@ -1,14 +1,22 @@
1
1
  import { Component, DeclareProps, Reference } from '@24i/bigscreen-sdk/jsx';
2
2
  import { noop } from '@24i/bigscreen-sdk/utils/noop';
3
+ import { stopEvent } from '@24i/bigscreen-sdk/utils/stopEvent';
3
4
  import { IFocusable } from '../IFocusable';
4
5
  import { Errors, hasValidDomRef } from './constants';
5
6
  import { SharedProps } from './types';
6
7
 
8
+ const PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS = 2;
9
+
7
10
  export type Props = SharedProps & {
8
11
  focusOptions?: FocusOptions;
9
12
  focusableChildren: Array<Reference>,
10
13
  onFocusChanged?: (focused: Reference, index: number) => any,
11
14
  onBeforeFocusChange?: (focused: Reference, index: number) => any,
15
+ circular?: boolean,
16
+ preventBlurOnLongPress?: {
17
+ forward?: boolean,
18
+ backward?: boolean,
19
+ }
12
20
  };
13
21
 
14
22
  export interface ILayout {
@@ -25,12 +33,16 @@ export const getLayoutBase = (onKeyDown: (event: KeyboardEvent, layout: ILayout)
25
33
  class Layout extends Component<Props> implements ILayout, IFocusable {
26
34
  index = 0;
27
35
 
36
+ preventBlurCounter = 0;
37
+
28
38
  static defaultProps = {
29
39
  onFocusChanged: noop,
30
40
  onBeforeFocusChange: noop,
31
41
  focusOptions: { preventScroll: true },
32
42
  dir: 'auto',
33
- };
43
+ circular: false,
44
+ preventBlurOnLongPress: {},
45
+ } as const;
34
46
 
35
47
  declare props: DeclareProps<Props, typeof Layout.defaultProps>;
36
48
 
@@ -40,6 +52,7 @@ export const getLayoutBase = (onKeyDown: (event: KeyboardEvent, layout: ILayout)
40
52
  throw new Error(Errors.DOM_REF_ERROR);
41
53
  }
42
54
  domRef.current!.addEventListener('keydown', this.onKeyDown, false);
55
+ window.document.addEventListener('keyup', this.onGlobalKeyUp, true);
43
56
  }
44
57
 
45
58
  componentWillUnmount() {
@@ -48,12 +61,17 @@ export const getLayoutBase = (onKeyDown: (event: KeyboardEvent, layout: ILayout)
48
61
  throw new Error(Errors.DOM_REF_ERROR);
49
62
  }
50
63
  domRef.current!.removeEventListener('keydown', this.onKeyDown, false);
64
+ window.document.removeEventListener('keyup', this.onGlobalKeyUp, true);
51
65
  }
52
66
 
53
67
  onKeyDown = (e: KeyboardEvent) => {
54
68
  onKeyDown(e, this);
55
69
  };
56
70
 
71
+ onGlobalKeyUp = () => {
72
+ this.preventBlurCounter = 0;
73
+ };
74
+
57
75
  getFromIndex(target: HTMLElement) {
58
76
  const { focusableChildren } = this.props;
59
77
  if (
@@ -78,25 +96,42 @@ export const getLayoutBase = (onKeyDown: (event: KeyboardEvent, layout: ILayout)
78
96
  }
79
97
 
80
98
  backward(e: KeyboardEvent) {
99
+ const { focusableChildren, preventBlurOnLongPress, circular } = this.props;
81
100
  const { target } = e;
82
101
  const fromIndex = this.getFromIndex(target as HTMLElement);
102
+ if (preventBlurOnLongPress.backward) {
103
+ this.preventBlurCounter += 1;
104
+ }
83
105
  if (fromIndex > 0) {
84
106
  this.setIndex(fromIndex - 1);
85
107
  this.focusCurrentIndex();
86
- e.preventDefault();
87
- e.stopPropagation();
108
+ stopEvent(e);
109
+ } else if (circular) {
110
+ this.setIndex(focusableChildren.length - 1);
111
+ this.focusCurrentIndex();
112
+ stopEvent(e);
113
+ } else if (this.preventBlurCounter >= PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS) {
114
+ stopEvent(e);
88
115
  }
89
116
  }
90
117
 
91
118
  forward(e: KeyboardEvent) {
92
- const { focusableChildren } = this.props;
119
+ const { focusableChildren, preventBlurOnLongPress, circular } = this.props;
93
120
  const { target } = e;
94
121
  const fromIndex = this.getFromIndex(target as HTMLElement);
122
+ if (preventBlurOnLongPress.forward) {
123
+ this.preventBlurCounter += 1;
124
+ }
95
125
  if (fromIndex < focusableChildren.length - 1) {
96
126
  this.setIndex(fromIndex + 1);
97
127
  this.focusCurrentIndex();
98
- e.preventDefault();
99
- e.stopPropagation();
128
+ stopEvent(e);
129
+ } else if (circular) {
130
+ this.setIndex(0);
131
+ this.focusCurrentIndex();
132
+ stopEvent(e);
133
+ } else if (this.preventBlurCounter >= PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS) {
134
+ stopEvent(e);
100
135
  }
101
136
  }
102
137
 
@@ -8,6 +8,8 @@ import { isRtl } from './isRtl';
8
8
  import { Errors, hasValidDomRef } from './constants';
9
9
  import { SharedProps } from './types';
10
10
 
11
+ const PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS = 2;
12
+
11
13
  // Used in binary operations for shorter checks
12
14
  export enum CircularDirection {
13
15
  NONE = 0,
@@ -21,7 +23,13 @@ export type Props = SharedProps & {
21
23
  matrix: Array<Array<Reference>>,
22
24
  onFocusChanged?: (focused: Reference, index: Index2D) => any,
23
25
  onBeforeFocusChange?: (focused: Reference, index: Index2D) => any,
24
- circular?: CircularDirection;
26
+ circular?: CircularDirection,
27
+ preventBlurOnLongPress?: {
28
+ left?: boolean,
29
+ right?: boolean,
30
+ up?: boolean,
31
+ down?: boolean,
32
+ },
25
33
  };
26
34
 
27
35
  export type Index2D = {
@@ -32,11 +40,14 @@ export type Index2D = {
32
40
  export class Matrix extends Component<Props> implements IFocusable {
33
41
  index: Index2D = { x: 0, y: 0 };
34
42
 
43
+ preventBlurCounter = 0;
44
+
35
45
  static defaultProps = {
36
46
  onFocusChanged: noop,
37
47
  onBeforeFocusChange: noop,
38
48
  circular: CircularDirection.NONE,
39
49
  dir: 'auto',
50
+ preventBlurOnLongPress: {},
40
51
  } as const;
41
52
 
42
53
  declare props: DeclareProps<Props, typeof Matrix.defaultProps>;
@@ -47,6 +58,7 @@ export class Matrix extends Component<Props> implements IFocusable {
47
58
  throw new Error(Errors.DOM_REF_ERROR);
48
59
  }
49
60
  domRef.current!.addEventListener('keydown', this.onKeyDown, false);
61
+ window.document.addEventListener('keyup', this.onGlobalKeyUp, true);
50
62
  }
51
63
 
52
64
  componentWillUnmount() {
@@ -55,26 +67,46 @@ export class Matrix extends Component<Props> implements IFocusable {
55
67
  throw new Error(Errors.DOM_REF_ERROR);
56
68
  }
57
69
  domRef.current!.removeEventListener('keydown', this.onKeyDown, false);
70
+ window.document.removeEventListener('keyup', this.onGlobalKeyUp, true);
58
71
  }
59
72
 
60
73
  onKeyDown = (e: KeyboardEvent) => {
74
+ const { preventBlurOnLongPress } = this.props;
61
75
  let newIndex: Index2D | null = null;
62
76
  if (this.isDirectionLeft(e)) {
63
77
  newIndex = this.moveHorizontaly('left');
78
+ if (preventBlurOnLongPress.left) {
79
+ this.preventBlurCounter += 1;
80
+ }
64
81
  } else if (this.isDirectionRight(e)) {
65
82
  newIndex = this.moveHorizontaly('right');
83
+ if (preventBlurOnLongPress.right) {
84
+ this.preventBlurCounter += 1;
85
+ }
66
86
  } else if (device.isDirectionUp(e)) {
67
87
  newIndex = this.moveVerticaly('up');
88
+ if (preventBlurOnLongPress.up) {
89
+ this.preventBlurCounter += 1;
90
+ }
68
91
  } else if (device.isDirectionDown(e)) {
69
92
  newIndex = this.moveVerticaly('down');
93
+ if (preventBlurOnLongPress.down) {
94
+ this.preventBlurCounter += 1;
95
+ }
70
96
  }
71
97
  if (newIndex) {
72
98
  stopEvent(e);
73
99
  this.index = newIndex;
74
100
  this.focusCurrentIndex();
101
+ } else if (this.preventBlurCounter >= PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS) {
102
+ stopEvent(e);
75
103
  }
76
104
  };
77
105
 
106
+ onGlobalKeyUp = () => {
107
+ this.preventBlurCounter = 0;
108
+ };
109
+
78
110
  getRefAt({ x, y }: Index2D): Reference {
79
111
  const { matrix } = this.props;
80
112
  if (matrix[y] && matrix[y][x]) return matrix[y][x];
@@ -112,24 +144,6 @@ export class Matrix extends Component<Props> implements IFocusable {
112
144
  return false;
113
145
  }
114
146
 
115
- isDirectionLeft(event: KeyboardEvent) {
116
- const { dir } = this.props;
117
- const rtl = isRtl(dir);
118
- return (
119
- (!rtl && LEFT.is(event))
120
- || (rtl && RIGHT.is(event))
121
- );
122
- }
123
-
124
- isDirectionRight(event: KeyboardEvent) {
125
- const { dir } = this.props;
126
- const rtl = isRtl(dir);
127
- return (
128
- (!rtl && RIGHT.is(event))
129
- || (rtl && LEFT.is(event))
130
- );
131
- }
132
-
133
147
  moveForwards = (isCircular: boolean) => (x: number, length: number) => {
134
148
  if (x === length - 1) {
135
149
  return isCircular ? 0 : length - 1;
@@ -172,6 +186,24 @@ export class Matrix extends Component<Props> implements IFocusable {
172
186
  return this.searchVertical(column, fromY, this.moveForwards(!!isCircular));
173
187
  };
174
188
 
189
+ isDirectionLeft(event: KeyboardEvent) {
190
+ const { dir } = this.props;
191
+ const rtl = isRtl(dir);
192
+ return (
193
+ (!rtl && LEFT.is(event))
194
+ || (rtl && RIGHT.is(event))
195
+ );
196
+ }
197
+
198
+ isDirectionRight(event: KeyboardEvent) {
199
+ const { dir } = this.props;
200
+ const rtl = isRtl(dir);
201
+ return (
202
+ (!rtl && RIGHT.is(event))
203
+ || (rtl && LEFT.is(event))
204
+ );
205
+ }
206
+
175
207
  isDifferentReferenceFromCurrent(index: Index2D): boolean {
176
208
  const { current } = this.getRefAt(this.index);
177
209
  const { current: searched } = this.getRefAt(index);
@@ -1,6 +1,12 @@
1
- import { Reference } from '@24i/bigscreen-sdk/jsx';
1
+ import { createRef, Reference } from '@24i/bigscreen-sdk/jsx';
2
2
  import { IFocusable } from './IFocusable';
3
3
 
4
+ export const preventScrollOnFocus = (ref: Reference<IFocusable>) => (
5
+ createRef<IFocusable>({
6
+ focus: () => ref.current?.focus({ preventScroll: true }),
7
+ })
8
+ );
9
+
4
10
  export const focusFirstExisting = (...toFocus: Reference<IFocusable>[]) => (
5
11
  () => {
6
12
  for (let i = 0; i < toFocus.length; i++) {
@@ -0,0 +1,36 @@
1
+ import { Reference, unwrapReference } from '@24i/bigscreen-sdk/jsx';
2
+ import { isElementWrappedBy } from '@24i/bigscreen-sdk/utils/elementUtils';
3
+
4
+ /**
5
+ * Test if the element (or element in the reference) has the `activeElement` inside or optionally,
6
+ * if the element itself is the `activeElement`.
7
+ * @param elementOrReference DOM element or reference to element on which we test focus
8
+ * @param includeElementItself if true, active element is also tested with the provided element.
9
+ * Default is true
10
+ * @returns true if the element has focused element inside or is itself focused
11
+ */
12
+ export const hasFocus = (
13
+ elementOrReference: HTMLElement | Reference<HTMLElement>,
14
+ includeElementItself = true,
15
+ ) => {
16
+ const element = unwrapReference(elementOrReference);
17
+ if (!element) return false;
18
+ return isElementWrappedBy(
19
+ document.activeElement as HTMLElement,
20
+ element,
21
+ includeElementItself,
22
+ );
23
+ };
24
+
25
+ /**
26
+ * Test if the element (or element in the reference) is the `activeElement`.
27
+ * @param elementOrReference DOM element or reference to element on which we test focus
28
+ * @returns true if the element is focused
29
+ */
30
+ export const isFocused = (
31
+ elementOrReference: HTMLElement | Reference<HTMLElement>,
32
+ ) => {
33
+ const element = unwrapReference(elementOrReference);
34
+ if (!element) return false;
35
+ return document.activeElement === element;
36
+ };
@@ -11,5 +11,6 @@ export {
11
11
  setNextFocusCandidate,
12
12
  focusCandidate,
13
13
  } from './candidate';
14
- export { focusFirstExisting } from './focusFirstExisting';
14
+ export { focusFirstExisting, preventScrollOnFocus } from './focusFirstExisting';
15
15
  export { refocusAfterHashChange } from './refocusAfterHashChange';
16
+ export { hasFocus, isFocused } from './hasFocus';
@@ -1,4 +1,4 @@
1
- import { PlayerBase } from '@24i/player-base';
1
+ import { PlayerBase } from '@24i/bigscreen-players-engine-base';
2
2
  import './__external__/SpringStreams';
3
3
 
4
4
  const SECOND_IN_MS = 1000;
@@ -16,7 +16,7 @@ export class Gallup {
16
16
  */
17
17
  constructor(
18
18
  siteCode: string,
19
- private readonly player: PlayerBase<any>,
19
+ private readonly player: PlayerBase,
20
20
  private readonly playerName: string,
21
21
  ) {
22
22
  this.sensors = new SpringStreams(siteCode);
@@ -67,7 +67,7 @@ export class Gallup {
67
67
  getPosition: () => Math.floor(player.currentTime / SECOND_IN_MS),
68
68
  getMeta: () => {
69
69
  const { width, height } = window.screen;
70
- const { playerVersion } = player.getMediaInfo();
70
+ const { playerVersion } = player.mediaInfo;
71
71
  return {
72
72
  pl: playerName,
73
73
  plv: playerVersion || '',
@@ -111,11 +111,13 @@ export class GridBase<T extends Item<U>, U>
111
111
  componentDidMount() {
112
112
  this.scrollerDom.current!.addEventListener('wheel', this.onWheel);
113
113
  this.scroller.current!.wrapRef.current!.addEventListener('keyup', this.onKeyUp);
114
+ window.document.addEventListener('keyup', this.onGlobalKeyUp, true);
114
115
  }
115
116
 
116
117
  componentWillUnmount() {
117
118
  this.scrollerDom.current!.removeEventListener('wheel', this.onWheel);
118
119
  this.scroller.current!.wrapRef.current!.removeEventListener('keyup', this.onKeyUp);
120
+ window.document.removeEventListener('keyup', this.onGlobalKeyUp, true);
119
121
  }
120
122
 
121
123
  // eslint-disable-next-line sonarjs/cognitive-complexity
@@ -155,6 +157,9 @@ export class GridBase<T extends Item<U>, U>
155
157
  onKeyUp = () => {
156
158
  this.stopFastScrolling();
157
159
  this.fastFocusingOptimizer.stop();
160
+ };
161
+
162
+ onGlobalKeyUp = () => {
158
163
  this.preventBlurCounter = 0;
159
164
  };
160
165
 
@@ -178,16 +183,6 @@ export class GridBase<T extends Item<U>, U>
178
183
  }
179
184
  };
180
185
 
181
- shouldPreventBlur(event: KeyboardEvent) {
182
- const { horizontal, preventBlurOnLongPress } = this.props;
183
- return (this.preventBlurCounter >= PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS && (
184
- (preventBlurOnLongPress.forward && isForward(event, horizontal))
185
- || (preventBlurOnLongPress.backward && isBackward(event, horizontal))
186
- || (preventBlurOnLongPress.forwardGroup && isFwGroup(event, horizontal))
187
- || (preventBlurOnLongPress.backwardGroup && isBwGroup(event, horizontal))
188
- ));
189
- }
190
-
191
186
  getRefAtIndex(i: number): Reference<T> {
192
187
  const ref = this.itemRefs[i];
193
188
  if (!ref) {
@@ -253,6 +248,16 @@ export class GridBase<T extends Item<U>, U>
253
248
  this.fastScrollingCounter.reset();
254
249
  };
255
250
 
251
+ shouldPreventBlur(event: KeyboardEvent) {
252
+ const { horizontal, preventBlurOnLongPress } = this.props;
253
+ return (this.preventBlurCounter >= PREVENT_BLUR_ON_LONG_PRESS_KEYDOWNS && (
254
+ (preventBlurOnLongPress.forward && isForward(event, horizontal))
255
+ || (preventBlurOnLongPress.backward && isBackward(event, horizontal))
256
+ || (preventBlurOnLongPress.forwardGroup && isFwGroup(event, horizontal))
257
+ || (preventBlurOnLongPress.backwardGroup && isBwGroup(event, horizontal))
258
+ ));
259
+ }
260
+
256
261
  animatedScrollStarted() {
257
262
  this.isAnimatedScrollRunning = true;
258
263
  }
@@ -61,6 +61,8 @@ export class FirstOnlyGrid<T extends Item<U>, U>
61
61
  const linesDiff = Math.floor(grid.index / itemsInGroup) - oldLine;
62
62
  if (linesDiff > 0) {
63
63
  this.scrollForward(linesDiff);
64
+ } else if (linesDiff < 0) {
65
+ this.scrollBackward(Math.abs(linesDiff));
64
66
  }
65
67
  };
66
68
 
@@ -77,8 +79,8 @@ export class FirstOnlyGrid<T extends Item<U>, U>
77
79
  this.controller.scroll(targetScrollIndex);
78
80
  }
79
81
 
80
- scrollBackward() {
81
- const targetScrollIndex = this.controller.scrollIndex - 1;
82
+ scrollBackward(diff = 1) {
83
+ const targetScrollIndex = this.controller.scrollIndex - diff;
82
84
  this.controller.scroll(targetScrollIndex);
83
85
  }
84
86
 
@@ -298,13 +298,13 @@ export class UnifiedGridController<T> implements IUnifiedGridController<T> {
298
298
  const { grid } = this;
299
299
  const base = grid.base.current!;
300
300
  const targetScrollIndex = this.itemIndexToScrollIndex(dataIndex);
301
- if (targetScrollIndex > this.scrollIndex) {
302
- this.adjustGroupsOffsetsForward(targetScrollIndex);
303
- } else if (targetScrollIndex < this.scrollIndex) {
304
- this.adjustGroupsOffsetsBackward(targetScrollIndex);
305
- }
306
301
  if (targetScrollIndex !== this.scrollIndex) {
307
- this.scroll(targetScrollIndex, animate);
302
+ const backwards = targetScrollIndex < this.scrollIndex;
303
+ const lastScrollIndex = this.scrollIndex;
304
+ const scrollToIndex = Math.abs(targetScrollIndex - this.scrollIndex);
305
+ for (let i = 1; i <= scrollToIndex; i++) {
306
+ this.scroll(backwards ? lastScrollIndex - i : lastScrollIndex + i, animate);
307
+ }
308
308
  }
309
309
  base.index = dataIndex;
310
310
  this.focusCurrentItem();
@@ -1,12 +1,19 @@
1
1
  import { createRef, Reference, Component } from '@24i/bigscreen-sdk/jsx';
2
- import { addClass } from '@24i/bigscreen-sdk/utils/addClass';
3
- import { removeClass } from '@24i/bigscreen-sdk/utils/removeClass';
2
+ import {
3
+ getDisplayToggler,
4
+ IDisplayToggler,
5
+ removeClass,
6
+ addClass,
7
+ DISPLAY_NONE,
8
+ } from '@24i/bigscreen-sdk/utils';
4
9
 
5
10
  const ICON_PREFIX = 'icon-';
6
11
 
7
12
  type Props<SpritesJSON> = {
8
13
  icon?: keyof SpritesJSON,
9
- domRef?: Reference<HTMLDivElement>;
14
+ domRef?: Reference<HTMLDivElement>,
15
+ className?: string,
16
+ initialIsHidden?: boolean,
10
17
  };
11
18
 
12
19
  export class Icon<T> extends Component<Props<T>> {
@@ -14,10 +21,13 @@ export class Icon<T> extends Component<Props<T>> {
14
21
 
15
22
  currentIcon: keyof T | undefined;
16
23
 
24
+ iconDisplay: IDisplayToggler;
25
+
17
26
  constructor(props: Props<T>) {
18
27
  super(props);
19
28
  this.ref = props.domRef || createRef();
20
29
  this.currentIcon = props.icon;
30
+ this.iconDisplay = getDisplayToggler(this.ref, props.initialIsHidden);
21
31
  }
22
32
 
23
33
  getClass(icon: keyof T): string {
@@ -31,6 +41,16 @@ export class Icon<T> extends Component<Props<T>> {
31
41
  this.currentIcon = icon;
32
42
  }
33
43
 
44
+ show() {
45
+ if (!this.currentIcon) return;
46
+ this.iconDisplay.show();
47
+ }
48
+
49
+ hide() {
50
+ if (!this.currentIcon) return;
51
+ this.iconDisplay.hide();
52
+ }
53
+
34
54
  clearIcon() {
35
55
  if (!this.currentIcon) return;
36
56
  removeClass(this.ref, this.getClass(this.currentIcon));
@@ -38,11 +58,13 @@ export class Icon<T> extends Component<Props<T>> {
38
58
  }
39
59
 
40
60
  render() {
41
- const { icon } = this.props;
61
+ const { icon, className, initialIsHidden } = this.props;
42
62
  return (
43
63
  <div
44
64
  ref={this.ref}
45
- className={`icon ${icon ? this.getClass(icon) : ''}`}
65
+ className={`${className || ''} icon ${icon
66
+ ? this.getClass(icon) : ''} ${initialIsHidden
67
+ ? DISPLAY_NONE : ''}`}
46
68
  />
47
69
  );
48
70
  }
@@ -16,9 +16,9 @@ It is connected through ime package to either system or virtual keyboard.
16
16
  - `passwordChar` - optional string which replaces password characters (default: `'●'`)
17
17
  - `shouldHandleOverflow` - optional flag which enables overflow handling by moving text to the left when caret should get out of its container (default: `false`)
18
18
  - `maxLength` - optional number to limit value length (default: `Infinity`)
19
- - `onChange` - optional callback with new value `(newValue: string) => void`
20
19
  - `onFocus` - optional callback which is called when input gets focus
21
20
  - `onBlur` - optional callback which is called when input loses focus
21
+ - `onChange` - optional callback with new value `(newValue: string) => void`
22
22
  - `onFinish` - optional callback which is called when input is finished, its only argument is a boolean to determine if input was cancelled or not `(wasCancelled: boolean) => void`
23
23
 
24
24
  ## Usage
@@ -19,13 +19,11 @@ type Props = {
19
19
  passwordChar?: string,
20
20
  /** Moves text to the left (with negative margin-left) when caret overflows */
21
21
  shouldHandleOverflow?: boolean,
22
- onChange?: (newValue: string) => void,
23
- onFocus?: () => void,
24
- onBlur?: () => void,
25
- // eslint-disable-next-line react/no-unused-prop-types
26
22
  maxLength?: number,
27
- // eslint-disable-next-line react/require-default-props
23
+ onFocus?: (event: FocusEvent) => void,
24
+ onBlur?: (event: FocusEvent) => void,
28
25
  onFinish?: (wasCancelled?: boolean) => void,
26
+ onChange?: (newValue: string) => void,
29
27
  };
30
28
 
31
29
  const ACTIVE = 'active';
@@ -126,12 +124,14 @@ export class Input extends Component<Props> implements IFocusable {
126
124
  const range = new Range();
127
125
  range.selectNode(textNode);
128
126
  const wholeText = range.getBoundingClientRect();
129
- range.setStart(textNode, position);
127
+ range.setStart(textNode, 0);
130
128
  range.setEnd(textNode, position);
131
129
  const caretText = range.getBoundingClientRect();
132
- const offset = isRtl()
133
- ? wholeText.left - caretText.left
134
- : wholeText.left + wholeText.width - caretText.left;
130
+ const [initialCaretPosition, currentCarretPosition, offsetBase] = isRtl()
131
+ ? [wholeText.right, caretText.left, wholeText.left]
132
+ : [wholeText.left, caretText.right, wholeText.right];
133
+ const caretPos = position === 0 ? initialCaretPosition : currentCarretPosition;
134
+ const offset = offsetBase - caretPos;
135
135
  offsetPosition(this.caret, { x: -offset }, false);
136
136
  }
137
137
 
@@ -220,7 +220,7 @@ export class Input extends Component<Props> implements IFocusable {
220
220
  >
221
221
  {placeholder}
222
222
  </div>
223
- <div ref={this.text} className="text" dir="auto">
223
+ <div ref={this.text} className="text">
224
224
  {this.filterText(value)}
225
225
  </div>
226
226
  <div ref={this.caret} className="caret" />
@@ -12,6 +12,7 @@ It provides common functionality for you.
12
12
  ## Props
13
13
  - `onPress` - called for onClick and onKeyDown
14
14
  - `ref` is forwarded to underlying `div`
15
+ - `blockRepeatedKeyDown` - interactable must suppress repeated onPress calls, until the key is released. This is because because keyDown event is repeatedly fired when user holds key on webTv and Adrodid Tv. This may lead to unpredictable behavior.
15
16
  - other `div` props
16
17
 
17
18
  ## Provided functionality
@@ -34,4 +35,4 @@ renders
34
35
  <div class="btn btn-primary" tabindex="0">
35
36
  Play video
36
37
  </div>
37
- ```
38
+ ```
@@ -3,14 +3,20 @@ import { isClickOrEnter } from '@24i/bigscreen-sdk/device';
3
3
  import { Props } from './interface';
4
4
 
5
5
  export const Interactable = forwardRef<Props>(
6
- ({ children, onPress, ...otherProps }, ref: Reference<HTMLDivElement> | null) => {
6
+ ({ children, onPress, blockRepeatedKeyDown, ...otherProps },
7
+ ref: Reference<HTMLDivElement> | null) => {
7
8
  const domRef = ref || createRef<HTMLDivElement>();
8
- const onPressProxy = (e: KeyboardEvent | MouseEvent) => {
9
- if (typeof onPress === 'function' && isClickOrEnter(e)) onPress(e);
9
+
10
+ const onPressProxy = (e: MouseEvent | KeyboardEvent) => {
11
+ if (typeof onPress === 'function' && isClickOrEnter(e)) {
12
+ onPress(e);
13
+ }
10
14
  };
15
+
11
16
  const onMouseEnter = () => {
12
17
  domRef.current!.focus({ preventScroll: true });
13
18
  };
19
+
14
20
  return (
15
21
  <div
16
22
  {...otherProps}
@@ -3,4 +3,5 @@ import { Children } from '@24i/bigscreen-sdk/jsx';
3
3
  export type Props = {
4
4
  children?: Children,
5
5
  onPress?: (event: KeyboardEvent | MouseEvent) => void,
6
+ blockRepeatedKeyDown?: boolean,
6
7
  } & Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>;
@@ -27,5 +27,5 @@ export type {
27
27
  ClassComponentInterface,
28
28
  DeclareProps,
29
29
  } from './types';
30
- // eslint-disable-next-line import/no-default-export
30
+ // eslint-disable-next-line import/no-default-export, no-restricted-exports
31
31
  export { createElement as default } from './virtualDOM';
@@ -1,5 +1,6 @@
1
1
  export { click } from './click';
2
2
  export { enter } from './enter';
3
3
  export { keydown } from './keydown';
4
+ export { keyup } from './keyup';
4
5
  export { shallow } from './shallow';
5
6
  export { MountedComponent } from './MountedComponent';
@@ -0,0 +1,27 @@
1
+ import { Reference, Component } from '../index';
2
+ import { unwrapReference } from '../utils';
3
+
4
+ type ToPress = Reference<HTMLElement> | HTMLElement | Window | Document | Component<any>;
5
+
6
+ interface Key {
7
+ keyCode: number,
8
+ code: string,
9
+ key?: string,
10
+ }
11
+
12
+ export const keyup = (toPress: ToPress, keyOrKeyCode: number | Key) => {
13
+ const element = unwrapReference<ToPress>(toPress);
14
+ let kbEventOptions: KeyboardEventInit;
15
+ if (typeof keyOrKeyCode === 'number') {
16
+ kbEventOptions = { keyCode: keyOrKeyCode };
17
+ } else {
18
+ const { keyCode, code, key } = keyOrKeyCode;
19
+ kbEventOptions = { keyCode, code, key };
20
+ }
21
+ const event = new KeyboardEvent('keyup', kbEventOptions);
22
+ if ((element as any).onKeyUp) {
23
+ (element as any).onKeyUp(event);
24
+ } else if ((element as any).dispatchEvent) {
25
+ (element as Element).dispatchEvent(event);
26
+ }
27
+ };