@applicaster/zapp-react-native-utils 14.0.0-rc.8 → 15.0.0-alpha.1121049279

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 (138) hide show
  1. package/actionsExecutor/ActionExecutorContext.tsx +60 -84
  2. package/actionsExecutor/ScreenActions.ts +164 -0
  3. package/actionsExecutor/StorageActions.ts +110 -0
  4. package/actionsExecutor/feedDecorator.ts +171 -0
  5. package/actionsExecutor/screenResolver.ts +11 -0
  6. package/analyticsUtils/AnalyticPlayerListener.ts +5 -2
  7. package/analyticsUtils/AnalyticsEvents/helper.ts +81 -0
  8. package/analyticsUtils/AnalyticsEvents/sendHeaderClickEvent.ts +1 -1
  9. package/analyticsUtils/AnalyticsEvents/sendMenuClickEvent.ts +2 -1
  10. package/analyticsUtils/AnalyticsEvents/sendOnClickEvent.ts +14 -4
  11. package/analyticsUtils/__tests__/analyticsUtils.test.js +3 -0
  12. package/analyticsUtils/events.ts +8 -0
  13. package/analyticsUtils/index.tsx +3 -4
  14. package/analyticsUtils/manager.ts +1 -1
  15. package/analyticsUtils/playerAnalyticsTracker.ts +2 -1
  16. package/appUtils/HooksManager/Hook.ts +4 -4
  17. package/appUtils/HooksManager/index.ts +11 -1
  18. package/appUtils/accessibilityManager/const.ts +13 -0
  19. package/appUtils/accessibilityManager/hooks.ts +35 -1
  20. package/appUtils/accessibilityManager/index.ts +154 -30
  21. package/appUtils/accessibilityManager/utils.ts +24 -0
  22. package/appUtils/contextKeysManager/contextResolver.ts +42 -1
  23. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +7 -0
  24. package/appUtils/focusManager/__tests__/focusManager.test.js +1 -1
  25. package/appUtils/focusManager/events.ts +2 -0
  26. package/appUtils/focusManager/index.ios.ts +10 -0
  27. package/appUtils/focusManager/index.ts +86 -11
  28. package/appUtils/focusManager/treeDataStructure/Tree/index.js +1 -1
  29. package/appUtils/focusManagerAux/utils/index.ts +94 -3
  30. package/appUtils/platform/platformUtils.ts +31 -1
  31. package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +0 -15
  32. package/appUtils/playerManager/conts.ts +21 -0
  33. package/appUtils/playerManager/useChapterMarker.tsx +0 -1
  34. package/appUtils/playerManager/usePlayerControllerSetup.tsx +16 -0
  35. package/arrayUtils/__tests__/isEmptyArray.test.ts +63 -0
  36. package/arrayUtils/__tests__/isFilledArray.test.ts +1 -1
  37. package/arrayUtils/index.ts +8 -3
  38. package/audioPlayerUtils/__tests__/getArtworkImage.test.ts +144 -0
  39. package/audioPlayerUtils/__tests__/getBackgroundImage.test.ts +72 -0
  40. package/audioPlayerUtils/__tests__/getImageFromEntry.test.ts +110 -0
  41. package/audioPlayerUtils/assets/index.ts +2 -0
  42. package/audioPlayerUtils/index.ts +242 -0
  43. package/componentsUtils/__tests__/isTabsScreen.test.ts +38 -0
  44. package/componentsUtils/index.ts +4 -1
  45. package/conf/player/__tests__/selectors.test.ts +34 -0
  46. package/conf/player/selectors.ts +10 -0
  47. package/configurationUtils/__tests__/configurationUtils.test.js +0 -31
  48. package/configurationUtils/__tests__/getMediaItems.test.ts +65 -0
  49. package/configurationUtils/__tests__/imageSrcFromMediaItem.test.ts +34 -0
  50. package/configurationUtils/__tests__/manifestKeyParser.test.ts +546 -0
  51. package/configurationUtils/index.ts +64 -35
  52. package/configurationUtils/manifestKeyParser.ts +57 -32
  53. package/focusManager/FocusManager.ts +104 -20
  54. package/focusManager/Tree.ts +25 -21
  55. package/focusManager/__tests__/FocusManager.test.ts +50 -8
  56. package/focusManager/aux/index.ts +98 -0
  57. package/focusManager/utils.ts +12 -6
  58. package/index.d.ts +1 -10
  59. package/manifestUtils/_internals/getDefaultConfiguration.js +28 -0
  60. package/manifestUtils/{_internals.js → _internals/index.js} +2 -25
  61. package/manifestUtils/createConfig.js +4 -1
  62. package/manifestUtils/defaultManifestConfigurations/player.js +2764 -1539
  63. package/manifestUtils/index.js +4 -0
  64. package/manifestUtils/keys.js +12 -0
  65. package/manifestUtils/progressBar/__tests__/mobileProgressBar.test.js +0 -30
  66. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +6 -0
  67. package/navigationUtils/__tests__/mapContentTypesToRivers.test.ts +130 -0
  68. package/navigationUtils/index.ts +26 -21
  69. package/package.json +2 -3
  70. package/playerUtils/PlayerTTS/PlayerTTS.ts +359 -0
  71. package/playerUtils/PlayerTTS/index.ts +1 -0
  72. package/playerUtils/__tests__/configurationUtils.test.ts +1 -65
  73. package/playerUtils/__tests__/getPlayerActionButtons.test.ts +54 -0
  74. package/playerUtils/_internals/__tests__/utils.test.ts +71 -0
  75. package/playerUtils/_internals/index.ts +1 -0
  76. package/playerUtils/_internals/utils.ts +31 -0
  77. package/playerUtils/configurationUtils.ts +0 -44
  78. package/playerUtils/getPlayerActionButtons.ts +17 -0
  79. package/playerUtils/index.ts +53 -0
  80. package/playerUtils/usePlayerTTS.ts +21 -0
  81. package/playerUtils/useValidatePlayerConfig.tsx +22 -19
  82. package/reactHooks/autoscrolling/__tests__/useTrackedView.test.tsx +15 -14
  83. package/reactHooks/cell-click/__tests__/index.test.js +3 -0
  84. package/reactHooks/cell-click/index.ts +8 -1
  85. package/reactHooks/debugging/__tests__/index.test.js +0 -1
  86. package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +47 -90
  87. package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +71 -31
  88. package/reactHooks/feed/index.ts +2 -0
  89. package/reactHooks/feed/useBatchLoading.ts +17 -10
  90. package/reactHooks/feed/useFeedLoader.tsx +36 -43
  91. package/reactHooks/feed/useInflatedUrl.ts +23 -29
  92. package/reactHooks/feed/useLoadPipesDataDispatch.ts +63 -0
  93. package/reactHooks/feed/usePipesCacheReset.ts +3 -3
  94. package/reactHooks/flatList/useSequentialRenderItem.tsx +3 -3
  95. package/reactHooks/layout/__tests__/index.test.tsx +3 -1
  96. package/reactHooks/layout/isTablet/index.ts +12 -5
  97. package/reactHooks/layout/useDimensions/__tests__/useDimensions.test.ts +34 -36
  98. package/reactHooks/layout/useDimensions/useDimensions.ts +2 -3
  99. package/reactHooks/layout/useLayoutVersion.ts +5 -5
  100. package/reactHooks/navigation/index.ts +7 -5
  101. package/reactHooks/navigation/useIsScreenActive.ts +9 -5
  102. package/reactHooks/navigation/useRoute.ts +7 -2
  103. package/reactHooks/navigation/useScreenStateStore.ts +8 -0
  104. package/reactHooks/resolvers/__tests__/useCellResolver.test.tsx +4 -0
  105. package/reactHooks/screen/useScreenContext.ts +1 -1
  106. package/reactHooks/state/__tests__/ZStoreProvider.test.tsx +2 -1
  107. package/reactHooks/state/index.ts +1 -1
  108. package/reactHooks/state/useHomeRiver.ts +4 -2
  109. package/reactHooks/state/useRivers.ts +7 -8
  110. package/riverComponetsMeasurementProvider/index.tsx +1 -1
  111. package/screenPickerUtils/index.ts +13 -0
  112. package/services/js2native.ts +1 -0
  113. package/storage/ScreenSingleValueProvider.ts +204 -0
  114. package/storage/ScreenStateMultiSelectProvider.ts +293 -0
  115. package/storage/StorageMultiSelectProvider.ts +192 -0
  116. package/storage/StorageSingleSelectProvider.ts +108 -0
  117. package/testUtils/index.tsx +7 -8
  118. package/time/BackgroundTimer.ts +6 -4
  119. package/utils/__tests__/endsWith.test.ts +30 -0
  120. package/utils/__tests__/find.test.ts +36 -0
  121. package/utils/__tests__/omit.test.ts +19 -0
  122. package/utils/__tests__/path.test.ts +33 -0
  123. package/utils/__tests__/pathOr.test.ts +37 -0
  124. package/utils/__tests__/startsWith.test.ts +30 -0
  125. package/utils/__tests__/take.test.ts +40 -0
  126. package/utils/endsWith.ts +9 -0
  127. package/utils/find.ts +3 -0
  128. package/utils/index.ts +33 -1
  129. package/utils/omit.ts +5 -0
  130. package/utils/path.ts +5 -0
  131. package/utils/pathOr.ts +5 -0
  132. package/utils/startsWith.ts +9 -0
  133. package/utils/take.ts +5 -0
  134. package/zappFrameworkUtils/HookCallback/callbackNavigationAction.ts +217 -0
  135. package/zappFrameworkUtils/HookCallback/hookCallbackManifestExtensions.config.js +76 -0
  136. package/zappFrameworkUtils/HookCallback/useCallbackActions.ts +19 -0
  137. package/zappFrameworkUtils/loginPluginUtils.ts +1 -1
  138. package/playerUtils/configurationGenerator.ts +0 -2572
@@ -11,8 +11,11 @@ const currentPlatform = platformSelect({
11
11
  android_tv: "android",
12
12
  });
13
13
 
14
- const stateSuffixes = ["pressed", "focused"];
14
+ // Do not change the order, focused_selected should be first
15
+ const states = ["focused_selected", "pressed", "focused", "selected"];
16
+
15
17
  const platformSuffixes = ["ios", "tvos", "samsung", "lg", "android"];
18
+ type StateKey = (typeof states)[number] | "default";
16
19
 
17
20
  export type GetAllSpecificStylesProps = {
18
21
  componentName: string;
@@ -33,39 +36,47 @@ export const getAllSpecificStyles = ({
33
36
  subComponentName,
34
37
  outStyles,
35
38
  }: GetAllSpecificStylesProps) => {
36
- if (!outStyles) {
37
- throw new Error("outStyles is required");
38
- }
39
+ if (!outStyles) throw new Error("outStyles is required");
40
+ if (!componentName) throw new Error("componentName is required");
39
41
 
40
- // Pressed, platform specific styles are not supported
41
- const prefix = `${componentName}_style_${subComponentName}_`;
42
+ const prefix =
43
+ subComponentName?.length > 0
44
+ ? `${componentName}_style_${subComponentName}_`
45
+ : `${componentName}_style_`;
42
46
 
43
47
  const defaultKey = "default";
44
48
 
45
- if (!outStyles[defaultKey]) {
46
- outStyles[defaultKey] = {};
47
- }
49
+ if (!outStyles[defaultKey]) outStyles[defaultKey] = {};
50
+
51
+ const tmpGenericDict: Record<StateKey, Record<string, any>> = {};
52
+ const tmpPlatformDict: Record<StateKey, Record<string, any>> = {};
48
53
 
49
- Object.keys(configuration).forEach((key) => {
54
+ const parseKey = (key: string, value: any) => {
50
55
  if (!key.startsWith(prefix)) {
51
56
  return;
52
57
  }
53
58
 
54
59
  let styleName = key.slice(prefix.length);
60
+ if (!styleName || styleName.startsWith("_")) return;
55
61
 
56
- const platform = platformSuffixes.find((suffix) =>
57
- styleName.startsWith(suffix)
62
+ const platform = platformSuffixes.find((prefix) =>
63
+ styleName.startsWith(prefix)
58
64
  );
59
65
 
66
+ let tmpDict = tmpGenericDict;
67
+
60
68
  if (platform) {
61
69
  if (currentPlatform !== platform) {
62
- return; // Continue
70
+ return;
63
71
  }
64
72
 
73
+ tmpDict = tmpPlatformDict;
65
74
  styleName = styleName.slice(platform.length + 1);
66
75
  }
67
76
 
68
- let state = stateSuffixes.find((suffix) => styleName.startsWith(suffix));
77
+ let state = states.find((prefix) => styleName.startsWith(prefix)) as
78
+ | StateKey
79
+ | undefined;
69
80
 
70
81
  if (state) {
71
82
  styleName = styleName.slice(state.length + 1);
@@ -73,30 +84,44 @@ export const getAllSpecificStyles = ({
73
84
  state = defaultKey;
74
85
  }
75
86
 
76
- const camelCaseKey = toCamelCase(styleName);
87
+ if (!styleName) return;
77
88
 
78
- let stateOverrides = outStyles[state];
89
+ const camelCaseKey = toCamelCase(styleName);
79
90
 
80
- if (!stateOverrides) {
81
- stateOverrides = {};
82
- outStyles[state] = stateOverrides;
83
- }
91
+ if (!tmpDict[state]) tmpDict[state] = {};
92
+ tmpDict[state][camelCaseKey] = value;
93
+ };
84
94
 
85
- if (stateOverrides[camelCaseKey]) {
86
- // Add warning, key is already defined
87
- }
95
+ for (const [key, value] of Object.entries(configuration)) {
96
+ parseKey(key, value);
97
+ }
88
98
 
89
- stateOverrides[camelCaseKey] = configuration[key];
90
- });
99
+ const allStates = Array.from(
100
+ new Set<StateKey>([
101
+ defaultKey,
102
+ ...(Object.keys(tmpGenericDict) as StateKey[]),
103
+ ...(Object.keys(tmpPlatformDict) as StateKey[]),
104
+ ...(Object.keys(outStyles) as StateKey[]),
105
+ ])
106
+ );
107
+
108
+ for (const state of allStates) {
109
+ outStyles[state] = Object.assign(
110
+ {},
111
+ outStyles[state] || {},
112
+ tmpGenericDict[state] || {},
113
+ tmpPlatformDict[state] || {}
114
+ );
115
+ }
91
116
 
92
117
  // Merge default styles with state specific styles
93
- if (Object.keys(outStyles).length > 1) {
94
- Object.keys(outStyles).forEach((key) => {
95
- if (key === defaultKey) {
96
- return;
97
- }
118
+ for (const state of Object.keys(outStyles)) {
119
+ if (state === defaultKey) continue;
98
120
 
99
- outStyles[key] = Object.assign({}, outStyles[defaultKey], outStyles[key]);
100
- });
121
+ outStyles[state] = Object.assign(
122
+ {},
123
+ outStyles[defaultKey],
124
+ outStyles[state]
125
+ );
101
126
  }
102
127
  };
@@ -2,11 +2,24 @@ import { path } from "ramda";
2
2
  import { isString } from "@applicaster/zapp-react-native-utils/stringUtils";
3
3
  import * as FOCUS_EVENTS from "@applicaster/zapp-react-native-utils/appUtils/focusManager/events";
4
4
 
5
+ import {
6
+ QUICK_BRICK_CONTENT,
7
+ QUICK_BRICK_NAVBAR,
8
+ } from "@applicaster/quick-brick-core/const";
9
+
5
10
  import { logger } from "./logger";
6
11
  import { TreeNode } from "./TreeNode";
7
12
  import { Tree } from "./Tree";
8
13
  import { subscriber } from "../functionUtils";
9
14
  import { getFocusableId, toFocusDirection } from "./utils";
15
+ import {
16
+ findSelectedMenuId,
17
+ isTabsScreenContentFocused,
18
+ findSelectedTabId,
19
+ contextWithoutScrolling,
20
+ } from "./aux";
21
+
22
+ export { contextWithoutScrolling } from "./aux";
10
23
 
11
24
  export {
12
25
  toFocusDirection,
@@ -176,18 +189,30 @@ class FocusManager {
176
189
  }
177
190
  }
178
191
 
179
- registerFocusable(
180
- component: FocusManager.TouchableReactRef,
181
- parentFocusable: FocusManager.TouchableReactRef,
182
- isFocusableCell: boolean
183
- ) {
184
- const focusableId = getFocusableId(component);
192
+ registerFocusable({
193
+ touchableRef,
194
+ parentFocusableRef,
195
+ isFocusableCell,
196
+ parentFocusableId,
197
+ }: {
198
+ touchableRef: FocusManager.TouchableReactRef;
199
+ parentFocusableRef: FocusManager.TouchableReactRef;
200
+ isFocusableCell: boolean;
201
+ parentFocusableId: string;
202
+ }) {
203
+ const focusableId = getFocusableId(touchableRef);
204
+
185
205
  const focusableComponent = FocusManager.findFocusable(focusableId);
186
206
 
187
- if (!focusableComponent && component) {
188
- this.focusableComponents.push(component);
207
+ if (!focusableComponent && touchableRef) {
208
+ this.focusableComponents.push(touchableRef);
189
209
 
190
- this.tree.add(component, parentFocusable, isFocusableCell);
210
+ this.tree.add(
211
+ touchableRef,
212
+ parentFocusableRef,
213
+ isFocusableCell,
214
+ parentFocusableId
215
+ );
191
216
  } else {
192
217
  logger.warning("Focusable component already registered", {
193
218
  id: focusableId,
@@ -209,7 +234,8 @@ class FocusManager {
209
234
 
210
235
  private setNextFocus(
211
236
  nextFocus: FocusManager.TouchableReactRef,
212
- options?: FocusManager.Android.CallbackOptions
237
+ options?: FocusManager.Android.CallbackOptions,
238
+ context?: FocusManager.FocusContext
213
239
  ) {
214
240
  if (nextFocus?.current?.props?.blockFocus) {
215
241
  return;
@@ -238,17 +264,15 @@ class FocusManager {
238
264
 
239
265
  FocusManager.instance.setPreviousNavigationDirection(options ?? null);
240
266
 
241
- nextFocus?.current?.onFocus?.(nextFocus.current, options ?? {});
267
+ nextFocus?.current?.onFocus?.(nextFocus.current, options ?? {}, context);
242
268
  }
243
269
  }
244
270
 
245
271
  blurPrevious(options?: FocusManager.Android.CallbackOptions) {
246
- if (options) {
247
- FocusManager.instance.prevFocused?.onBlur?.(
248
- FocusManager.instance.prevFocused,
249
- options
250
- );
251
- }
272
+ FocusManager.instance.prevFocused?.onBlur?.(
273
+ FocusManager.instance.prevFocused,
274
+ options ?? {} // Adding fallback to avoid potential regression caused by #7509
275
+ );
252
276
  }
253
277
 
254
278
  onDisableFocusChange = (id) => {
@@ -269,7 +293,7 @@ class FocusManager {
269
293
 
270
294
  if (nextFocus) {
271
295
  // HACK: hack to fix the hack below
272
- // HACK: putting call to the end of the event loop so the next component has a chane to be registered
296
+ // HACK: putting call to the end of the event loop so the next component has a chance to be registered
273
297
  setTimeout(() => {
274
298
  FocusManager.instance.setFocus(nextFocus, {
275
299
  direction: "down",
@@ -281,7 +305,8 @@ class FocusManager {
281
305
 
282
306
  setFocus(
283
307
  newFocus: FocusManager.TouchableReactRef | string,
284
- options?: FocusManager.Android.CallbackOptions
308
+ options?: FocusManager.Android.CallbackOptions,
309
+ context?: FocusManager.FocusContext
285
310
  ) {
286
311
  // Checks if element is focusable
287
312
  const { isFocusable, error } = FocusManager.isFocusable(newFocus);
@@ -306,7 +331,7 @@ class FocusManager {
306
331
  }
307
332
 
308
333
  if (newFocusRef) {
309
- FocusManager.instance.setNextFocus(newFocusRef, options);
334
+ FocusManager.instance.setNextFocus(newFocusRef, options, context);
310
335
  }
311
336
  }
312
337
  }
@@ -341,6 +366,11 @@ class FocusManager {
341
366
  FocusManager.instance.focused.onBlur(FocusManager.instance.focused, {});
342
367
  }
343
368
 
369
+ // send reset event to some handler to reset their internal state, before real reset happens
370
+ this.eventHandler?.invokeHandler?.(FOCUS_EVENTS.RESET, {
371
+ focusedId: FocusManager.instance.focusedId,
372
+ });
373
+
344
374
  FocusManager.instance.setFocusLocal({ current: null });
345
375
  }
346
376
 
@@ -407,6 +437,60 @@ class FocusManager {
407
437
  throw new Error(`Group with id ${id} not found`);
408
438
  }
409
439
  }
440
+
441
+ isFocusOnMenu(): boolean {
442
+ return this.isFocusableChildOf(
443
+ FocusManager.instance.focusedId,
444
+ QUICK_BRICK_NAVBAR
445
+ );
446
+ }
447
+
448
+ isFocusOnContent(): boolean {
449
+ return this.isFocusableChildOf(
450
+ FocusManager.instance.focusedId,
451
+ QUICK_BRICK_CONTENT
452
+ );
453
+ }
454
+
455
+ private landFocusToWithoutScrolling = (id) => {
456
+ if (id) {
457
+ // set focus on selected menu item
458
+ const direction = undefined;
459
+
460
+ const context: FocusManager.FocusContext =
461
+ contextWithoutScrolling("back");
462
+
463
+ logger.log({ message: "landFocusToWithoutScrolling", data: { id } });
464
+
465
+ this.setFocus(id, direction, context);
466
+ }
467
+ };
468
+
469
+ // Move focus to appropriate top navigation tab with context
470
+ focusOnSelectedTab(index: number): void {
471
+ const selectedTabId = findSelectedTabId(this.tree, index);
472
+
473
+ // Set focus with back button context to tabs-menu
474
+ this.landFocusToWithoutScrolling(selectedTabId);
475
+ }
476
+
477
+ // Move focus to appropriate top navigation tab with context
478
+ focusOnSelectedTopMenuItem(index: number, sectionKey: string): void {
479
+ const selectedMenuItemId = findSelectedMenuId(this.tree, {
480
+ index,
481
+ sectionKey,
482
+ });
483
+
484
+ // Set focus with back button context to top-menu
485
+ this.landFocusToWithoutScrolling(selectedMenuItemId);
486
+ }
487
+
488
+ isTabsScreenContentFocused(): boolean {
489
+ return isTabsScreenContentFocused(
490
+ this.tree,
491
+ FocusManager.instance.focusedId
492
+ );
493
+ }
410
494
  }
411
495
 
412
496
  export const focusManager = FocusManager.getInstance();
@@ -8,37 +8,41 @@ export class Tree {
8
8
  this.tree = focusManagerTree;
9
9
  }
10
10
 
11
- add(component, parentFocusable, isFocusableCell) {
12
- const focusableId = getFocusableId(component);
13
- const parentId = getFocusableId(parentFocusable);
11
+ add(
12
+ touchableRef: FocusManager.TouchableReactRef,
13
+ parentFocusableRef: FocusManager.TouchableReactRef,
14
+ isFocusableCell: boolean,
15
+ parentFocusableId: string
16
+ ) {
17
+ const focusableId = getFocusableId(touchableRef);
18
+ const parentId = getFocusableId(parentFocusableRef) || parentFocusableId;
14
19
  const focusableComponentInTree = this.find(focusableId);
15
20
 
16
21
  // update node if it already exists
17
22
  if (focusableComponentInTree) {
18
- focusableComponentInTree.updateNode(component);
23
+ focusableComponentInTree.updateNode(touchableRef);
19
24
  }
20
25
 
21
- if (parentFocusable?.current) {
22
- if (!this.find(parentId)) {
23
- this.tree.push(new TreeNode(null, parentId, null, isFocusableCell));
24
- }
26
+ if (!this.find(parentId)) {
27
+ // create temporary node to the root of the tree
28
+ this.tree.push(new TreeNode(null, parentId, null, isFocusableCell));
29
+ }
25
30
 
26
- const parentNode = this.find(parentId);
31
+ const parentNode = this.find(parentId);
27
32
 
28
- if (parentNode) {
29
- if (focusableComponentInTree) {
30
- focusableComponentInTree.isFocusableCell = isFocusableCell;
31
- focusableComponentInTree.parentId = parentNode.id;
33
+ if (parentNode) {
34
+ if (focusableComponentInTree) {
35
+ focusableComponentInTree.isFocusableCell = isFocusableCell;
36
+ focusableComponentInTree.parentId = parentNode.id;
32
37
 
33
- parentNode.addChild(focusableComponentInTree);
38
+ parentNode.addChild(focusableComponentInTree);
34
39
 
35
- // remove root object from the list
36
- this.tree = this.tree.filter(
37
- (node) => node !== focusableComponentInTree
38
- );
39
- } else {
40
- parentNode.addChild(component, focusableId, isFocusableCell);
41
- }
40
+ // remove root object from the list
41
+ this.tree = this.tree.filter(
42
+ (node) => node !== focusableComponentInTree
43
+ );
44
+ } else {
45
+ parentNode.addChild(touchableRef, focusableId, isFocusableCell);
42
46
  }
43
47
  }
44
48
  }
@@ -1,5 +1,8 @@
1
1
  import { focusManager } from "../FocusManager";
2
2
 
3
+ const isFocusableCell = true;
4
+ const parentFocusableId = "parentFocusableId";
5
+
3
6
  const group = {
4
7
  current: {
5
8
  props: {
@@ -62,13 +65,47 @@ jest.useFakeTimers();
62
65
 
63
66
  describe("FocusManager", () => {
64
67
  beforeAll(() => {
65
- focusManager.registerFocusable(group, { current: null });
66
- focusManager.registerFocusable(child1, group);
67
- focusManager.registerFocusable(child2, group);
68
- focusManager.registerFocusable(child3, child2);
69
-
70
- focusManager.registerFocusable(child4, child2);
71
- focusManager.registerFocusable(child5, child2);
68
+ focusManager.registerFocusable({
69
+ touchableRef: group,
70
+ parentFocusableRef: { current: null },
71
+ isFocusableCell,
72
+ parentFocusableId,
73
+ });
74
+
75
+ focusManager.registerFocusable({
76
+ touchableRef: child1,
77
+ parentFocusableRef: group,
78
+ isFocusableCell,
79
+ parentFocusableId,
80
+ });
81
+
82
+ focusManager.registerFocusable({
83
+ touchableRef: child2,
84
+ parentFocusableRef: group,
85
+ isFocusableCell,
86
+ parentFocusableId,
87
+ });
88
+
89
+ focusManager.registerFocusable({
90
+ touchableRef: child3,
91
+ parentFocusableRef: child2,
92
+ isFocusableCell,
93
+ parentFocusableId,
94
+ });
95
+
96
+ focusManager.registerFocusable({
97
+ touchableRef: child4,
98
+ parentFocusableRef: child2,
99
+ isFocusableCell,
100
+ parentFocusableId,
101
+ });
102
+
103
+ focusManager.registerFocusable({
104
+ touchableRef: child5,
105
+ parentFocusableRef: child2,
106
+ isFocusableCell,
107
+ parentFocusableId,
108
+ });
72
109
  });
73
110
 
74
111
  it("focusManager should be defined", () => {
@@ -199,7 +236,12 @@ describe("FocusManager", () => {
199
236
  });
200
237
 
201
238
  it("focusManager registerFocusable should register", () => {
202
- focusManager.registerFocusable(child5, child2);
239
+ focusManager.registerFocusable({
240
+ touchableRef: child5,
241
+ parentFocusableRef: child2,
242
+ isFocusableCell,
243
+ parentFocusableId,
244
+ });
203
245
 
204
246
  expect(
205
247
  focusManager.isFocusableChildOf(child5.current.props.id, child2)
@@ -0,0 +1,98 @@
1
+ import { isNil, pathOr } from "@applicaster/zapp-react-native-utils/utils";
2
+
3
+ import {
4
+ QUICK_BRICK_CONTENT,
5
+ QUICK_BRICK_NAVBAR,
6
+ QUICK_BRICK_NAVBAR_SECTIONS,
7
+ } from "@applicaster/quick-brick-core/const";
8
+
9
+ const isNavBar = (node) => QUICK_BRICK_NAVBAR === node?.id;
10
+ const isContent = (node) => QUICK_BRICK_CONTENT === node?.id;
11
+
12
+ // SCREEN_PICKER_SELECTOR_CONTAINER(we assume there is only one SCREEN_PICKER)
13
+ let screenPickerSelectorContainerId;
14
+
15
+ export const onRegisterScreenPickerSelectorContainer = (id) => {
16
+ screenPickerSelectorContainerId = id;
17
+ };
18
+
19
+ export const onUnregisterScreenPickerSelectorContainer = (id) => {
20
+ // reset screenSelectorId on unregistration
21
+ if (screenPickerSelectorContainerId === id) {
22
+ screenPickerSelectorContainerId = undefined;
23
+ }
24
+ };
25
+ // SCREEN_PICKER_SELECTOR_CONTAINER
26
+
27
+ // SCREEN_PICKER_CONTENT_CONTAINER(we assume there is only one SCREEN_PICKER)
28
+ let screenPickerContentContainerId;
29
+
30
+ export const onRegisterScreenPickerContentContainer = (id) => {
31
+ screenPickerContentContainerId = id;
32
+ };
33
+
34
+ export const onUnregisterScreenPickerContentContainer = (id) => {
35
+ // reset screenSelectorId on unregistration
36
+ if (screenPickerContentContainerId === id) {
37
+ screenPickerContentContainerId = undefined;
38
+ }
39
+ };
40
+
41
+ const isScreenPickerContentContainer = (node) =>
42
+ screenPickerContentContainerId === node?.id;
43
+
44
+ // SCREEN_PICKER_CONTENT_CONTAINER
45
+
46
+ export const findSelectedMenuId = (
47
+ focusableTree,
48
+ { index, sectionKey }: { index: number; sectionKey: string }
49
+ ) => {
50
+ const sectionName = QUICK_BRICK_NAVBAR_SECTIONS[sectionKey];
51
+
52
+ return pathOr(
53
+ undefined,
54
+ ["children", index, "id"],
55
+ focusableTree.find(sectionName)
56
+ );
57
+ };
58
+
59
+ export const findSelectedTabId = (focusableTree, index: number): string => {
60
+ const screenSelectorContainerNode = focusableTree.find(
61
+ screenPickerSelectorContainerId
62
+ );
63
+
64
+ const selectedTabId = screenSelectorContainerNode.children[index]?.id;
65
+
66
+ return selectedTabId;
67
+ };
68
+
69
+ export const isTabsScreenContentFocused = (focusableTree, id) => {
70
+ const node = focusableTree.find(id);
71
+
72
+ if (isNil(node)) {
73
+ return false;
74
+ }
75
+
76
+ if (isNavBar(node)) {
77
+ return false;
78
+ }
79
+
80
+ if (isContent(node)) {
81
+ return false;
82
+ }
83
+
84
+ if (isScreenPickerContentContainer(node)) {
85
+ return true;
86
+ }
87
+
88
+ return isTabsScreenContentFocused(focusableTree, node.parentId);
89
+ };
90
+
91
+ export const contextWithoutScrolling = (
92
+ source: FocusManager.FocusContext["source"]
93
+ ): FocusManager.FocusContext => {
94
+ return {
95
+ source,
96
+ preserveScroll: true,
97
+ };
98
+ };
@@ -4,11 +4,11 @@ import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/he
4
4
 
5
5
  export const getFocusableId = (ref) => ref?.current?.props.id;
6
6
 
7
- type Direction = "up" | "down" | "left" | "right";
8
-
9
7
  const normalizeDirection = (direction) => direction.toLowerCase();
10
8
 
11
- const checkDirection = (direction: Direction) => {
9
+ const checkDirection = (
10
+ direction: FocusManager.Android.FocusNavigationDirections
11
+ ) => {
12
12
  invariant(!isNilOrEmpty(direction), "direction should not be empty");
13
13
 
14
14
  invariant(
@@ -17,19 +17,25 @@ const checkDirection = (direction: Direction) => {
17
17
  );
18
18
  };
19
19
 
20
- export const toFocusDirection = (direction: Direction) => {
20
+ export const toFocusDirection = (
21
+ direction: FocusManager.Android.FocusNavigationDirections
22
+ ) => {
21
23
  checkDirection(direction);
22
24
 
23
25
  return `nextFocus${capitalize(normalizeDirection(direction))}`;
24
26
  };
25
27
 
26
- export const isHorizontalDirection = (direction: Direction) => {
28
+ export const isHorizontalDirection = (
29
+ direction: FocusManager.Android.FocusNavigationDirections
30
+ ) => {
27
31
  checkDirection(direction);
28
32
 
29
33
  return ["left", "right"].includes(normalizeDirection(direction));
30
34
  };
31
35
 
32
- export const isVerticalDirection = (direction: Direction) => {
36
+ export const isVerticalDirection = (
37
+ direction: FocusManager.Android.FocusNavigationDirections
38
+ ) => {
33
39
  checkDirection(direction);
34
40
 
35
41
  return ["up", "down"].includes(normalizeDirection(direction));
package/index.d.ts CHANGED
@@ -69,7 +69,7 @@ declare type ExtraProps = ZappUIComponentProps & {
69
69
  };
70
70
 
71
71
  declare type WebConfirmationDialog = {
72
- message: string;
72
+ message?: string;
73
73
  confirmCompletion?: () => void;
74
74
  cancelCompletion?: () => void;
75
75
  };
@@ -137,12 +137,3 @@ declare type AccessibilityState = {
137
137
  reduceMotionEnabled: boolean;
138
138
  boldTextEnabled: boolean;
139
139
  };
140
-
141
- declare type AccessibilityProps = {
142
- accessibilityLabel?: string;
143
- accessibilityHint?: string;
144
- "aria-label"?: string;
145
- "aria-description"?: string;
146
- accessibilityRole?: string;
147
- "aria-role"?: string;
148
- };
@@ -0,0 +1,28 @@
1
+ const R = require("ramda");
2
+ const { defaultConfigurations } = require("../defaultManifestConfigurations");
3
+
4
+ /**
5
+ * returns default configuration keys for provided plugin type
6
+ * @param {('general-content'|'player')} pluginType
7
+ * @param options manifest generator information
8
+ * @param {string} options.version manifest version
9
+ * @param {string} options.platform qb platform value
10
+ */
11
+ function getDefaultConfiguration(pluginType, options) {
12
+ const defConfig = R.compose(
13
+ R.unless(R.isNil, (fn) => fn(options)),
14
+ R.propOr(null, pluginType)
15
+ )(defaultConfigurations);
16
+
17
+ if (!defConfig) {
18
+ const availableKeys = R.keys(defaultConfigurations);
19
+
20
+ const message = `Requested key "${pluginType}" doesn't exist in the default configuration\nAvailable keys: ${availableKeys}`;
21
+ // eslint-disable-next-line no-console
22
+ console.warn(message);
23
+ }
24
+
25
+ return defConfig;
26
+ }
27
+
28
+ module.exports = { getDefaultConfiguration };