@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5251355865 → 12.0.0-alpha.5347482137

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 (112) hide show
  1. package/Components/BaseFocusable/index.tsx +3 -3
  2. package/Components/Cell/Cell.tsx +7 -7
  3. package/Components/Cell/CellWithFocusable.tsx +40 -19
  4. package/Components/Cell/TvOSCellComponent.tsx +5 -9
  5. package/Components/ErrorScreen/index.tsx +1 -1
  6. package/Components/Focusable/Focusable.tsx +6 -1
  7. package/Components/FocusableCell/index.tsx +7 -7
  8. package/Components/FocusableGroup/index.tsx +11 -2
  9. package/Components/FocusableList/FocusableChild.ts +24 -14
  10. package/Components/FocusableList/FocusableItem.tsx +1 -1
  11. package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
  12. package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
  13. package/Components/FocusableList/index.tsx +26 -25
  14. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +6 -3
  15. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  16. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  17. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  18. package/Components/Image/Image.android.tsx +3 -25
  19. package/Components/Image/Image.tsx +18 -2
  20. package/Components/Image/utils/index.ts +39 -0
  21. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  22. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -10
  23. package/Components/Layout/TV/__tests__/index.test.tsx +19 -75
  24. package/Components/Layout/TV/index.tsx +12 -11
  25. package/Components/Layout/TV/index.web.tsx +33 -0
  26. package/Components/Layout/index.tsx +15 -11
  27. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  28. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +88 -112
  29. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  30. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
  31. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  32. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
  33. package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
  34. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  35. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +17 -2
  36. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
  37. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  38. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  39. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  40. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  41. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  42. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  43. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
  44. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  45. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  46. package/Components/MasterCell/utils/index.ts +33 -35
  47. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  48. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  49. package/Components/Navigator/StackNavigator.tsx +54 -0
  50. package/Components/Navigator/index.ts +1 -0
  51. package/Components/PlayerContainer/PlayerContainer.tsx +69 -31
  52. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  53. package/Components/PlayerImageBackground/index.tsx +34 -8
  54. package/Components/River/ComponentsMap/ComponentsMap.tsx +92 -22
  55. package/Components/River/RefreshControl.tsx +55 -1
  56. package/Components/River/TV/River.tsx +2 -25
  57. package/Components/River/__tests__/componentsMap.test.js +7 -0
  58. package/Components/River/useScreenConfiguration.ts +2 -2
  59. package/Components/Screen/TV/hooks/index.ts +3 -0
  60. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  61. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  62. package/Components/Screen/TV/index.web.tsx +186 -0
  63. package/Components/Screen/__tests__/Screen.test.tsx +8 -1
  64. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +14 -0
  65. package/Components/Screen/index.tsx +78 -30
  66. package/Components/ScreenResolver/index.tsx +3 -1
  67. package/Components/Tabs/TV/styles.ts +1 -1
  68. package/Components/Transitioner/Scene.tsx +24 -19
  69. package/Components/Transitioner/Transitioner.tsx +21 -28
  70. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +6 -2
  71. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  72. package/Components/VideoLive/LiveImageManager.ts +28 -1
  73. package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -28
  74. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
  75. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +18 -28
  76. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
  77. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +352 -0
  78. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +158 -0
  79. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +455 -0
  80. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +162 -0
  81. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  82. package/Components/VideoModal/ModalAnimation/utils.ts +301 -0
  83. package/Components/VideoModal/PlayerDetails.tsx +3 -3
  84. package/Components/VideoModal/PlayerWrapper.tsx +77 -47
  85. package/Components/VideoModal/VideoModal.tsx +23 -40
  86. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +13 -1
  87. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +157 -105
  88. package/Components/VideoModal/utils.ts +8 -5
  89. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  90. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  91. package/Components/Viewport/utils.js +1 -1
  92. package/Components/ZappUIComponent/index.tsx +22 -12
  93. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  94. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  95. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  96. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  97. package/Contexts/FocusableGroupContext/index.ts +7 -0
  98. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  99. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  100. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  101. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  102. package/Contexts/ScreenContext/index.tsx +94 -28
  103. package/Contexts/ScreenDataContext/index.tsx +20 -0
  104. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  105. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  106. package/events/index.ts +1 -0
  107. package/package.json +5 -5
  108. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  109. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  110. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  111. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
  112. /package/Contexts/{index.js → index.ts} +0 -0
@@ -19,6 +19,7 @@ type Props = {
19
19
  modalHeight?: number;
20
20
  modalWidth?: number;
21
21
  mode: VideoModalMode;
22
+ groupId?: string;
22
23
  };
23
24
 
24
25
  const YOUTUBE_PLUGIN_ID = "youtube-player-qb";
@@ -93,6 +94,7 @@ export function HandlePlayable({
93
94
  modalHeight,
94
95
  modalWidth,
95
96
  mode,
97
+ groupId,
96
98
  }: Props): React.ReactElement | null {
97
99
  const state = usePickFromState();
98
100
 
@@ -167,6 +169,7 @@ export function HandlePlayable({
167
169
  isModal={isModal}
168
170
  mode={mode}
169
171
  disableCastAction={playerModuleProperties?.disableCastAction}
172
+ groupId={groupId}
170
173
  />
171
174
  );
172
175
  }
@@ -3,32 +3,10 @@ import FastImage, {
3
3
  FastImageProps,
4
4
  } from "@applicaster/react-native-fast-image";
5
5
 
6
- export const QBImage = (props: FastImageProps) => {
7
- const source = React.useMemo(() => {
8
- if (typeof props.source === "number") {
9
- return props.source;
10
- }
6
+ import { useImageSource } from "./utils";
11
7
 
12
- return {
13
- uri: props.source.uri,
14
- headers: props.source.headers,
15
- priority: props.source.priority,
16
- cache: props.source.cache,
17
- maxWidth: props.source.maxWidth,
18
- maxHeight: props.source.maxHeight,
19
- };
20
- }, [
21
- ...(typeof props.source === "object"
22
- ? [
23
- props.source?.uri,
24
- props.source?.priority,
25
- props.source?.cache,
26
- props.source?.headers,
27
- props.source?.maxWidth,
28
- props.source?.maxHeight,
29
- ]
30
- : []),
31
- ]);
8
+ export const QBImage = (props: FastImageProps) => {
9
+ const source = useImageSource(props.source);
32
10
 
33
11
  return <FastImage {...props} source={source} />;
34
12
  };
@@ -1,8 +1,24 @@
1
- import { Image, ImageProps, ImageStyle, StyleProp } from "react-native";
1
+ import React from "react";
2
+ import {
3
+ Image,
4
+ ImageBackground,
5
+ ImageProps,
6
+ ImageStyle,
7
+ StyleProp,
8
+ } from "react-native";
2
9
 
3
10
  export interface Props extends ImageProps {
4
11
  style?: StyleProp<ImageStyle> | undefined;
5
12
  }
6
13
 
7
14
  // TODO - React.FC<Props> QBImage should process incoming url and apply any transformations if needed
8
- export const QBImage = Image;
15
+ export const QBImage = ({
16
+ children,
17
+ ...props
18
+ }: Props & { children?: React.ReactNode }) => {
19
+ if (children) {
20
+ return <ImageBackground {...props}>{children}</ImageBackground>;
21
+ }
22
+
23
+ return <Image {...props} />;
24
+ };
@@ -0,0 +1,39 @@
1
+ import FastImage, { Source } from "@applicaster/react-native-fast-image";
2
+ import { useMemo } from "react";
3
+ import { ImageRequireSource } from "react-native";
4
+
5
+ function getSourceCache(cache: Source["cache"]) {
6
+ return cache || FastImage.cacheControl.web;
7
+ }
8
+
9
+ export const useImageSource = (source?: Source | ImageRequireSource) => {
10
+ return useMemo(() => {
11
+ if (source) {
12
+ if (typeof source === "number") {
13
+ return source;
14
+ }
15
+
16
+ return {
17
+ uri: source.uri,
18
+ headers: source.headers,
19
+ priority: source.priority,
20
+ cache: getSourceCache(source?.cache),
21
+ maxWidth: source.maxWidth,
22
+ maxHeight: source.maxHeight,
23
+ };
24
+ }
25
+
26
+ return undefined;
27
+ }, [
28
+ ...(typeof source === "object"
29
+ ? [
30
+ source?.uri,
31
+ source?.priority,
32
+ source?.cache,
33
+ source?.headers,
34
+ source?.maxWidth,
35
+ source?.maxHeight,
36
+ ]
37
+ : []),
38
+ ]);
39
+ };
@@ -60,7 +60,7 @@ export function ScreenLayoutContextProvider(props: {
60
60
  initialState
61
61
  );
62
62
 
63
- const screeenLayoutValue = React.useMemo(
63
+ const screenLayoutValue = React.useMemo(
64
64
  () => ({
65
65
  ...screenLayout,
66
66
  setScreenLayout,
@@ -69,7 +69,7 @@ export function ScreenLayoutContextProvider(props: {
69
69
  );
70
70
 
71
71
  return (
72
- <ScreenLayoutContext.Provider value={screeenLayoutValue}>
72
+ <ScreenLayoutContext.Provider value={screenLayoutValue}>
73
73
  {props.children}
74
74
  </ScreenLayoutContext.Provider>
75
75
  );
@@ -2,18 +2,18 @@
2
2
 
3
3
  exports[`Layout TV renders 1`] = `
4
4
  <View
5
- style={
6
- Object {
7
- "flex": 1,
8
- "height": 1080,
9
- "width": 1920,
10
- }
11
- }
12
- testID="layout-container"
5
+ excludeFromFocusSearching={true}
6
+ id="/river/A1234"
7
+ preferredFocus={true}
13
8
  >
14
9
  <View
15
- backgroundImageUrl="http://applicaster.com"
16
- testID="background-component"
10
+ Components={
11
+ Object {
12
+ "Background": [Function],
13
+ "NavBar": [Function],
14
+ }
15
+ }
16
+ route="/river/A1234"
17
17
  />
18
18
  </View>
19
19
  `;
@@ -1,79 +1,48 @@
1
1
  import * as React from "react";
2
- import * as R from "ramda";
2
+ import { omit } from "ramda";
3
3
  import { View } from "react-native";
4
4
  import { render } from "@testing-library/react-native";
5
5
  import { Provider } from "react-redux";
6
- import { createStore } from "redux";
7
6
  import { NavigationContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NavigationContext";
7
+ import configureStore from "redux-mock-store";
8
8
 
9
- import { ThemeContext } from "@applicaster/zapp-react-native-utils/theme";
9
+ const withoutChildren = omit(["children"]);
10
10
 
11
- jest.mock("@applicaster/quick-brick-core/App/ActionSetters", () => ({
12
- useRiverInitialState: () => ({}),
11
+ jest.mock("../../../Screen/TV/index.web", () => ({
12
+ Screen: View,
13
13
  }));
14
14
 
15
- jest.mock("@applicaster/zapp-react-native-utils/reactHooks/screen");
15
+ const Layout = require("../index.web").default;
16
16
 
17
- const Layout = require("..").default;
18
-
19
- const ResolvedMenuPlug = () => <View testID="resolved-menu-plug" />;
20
-
21
- const plugins = [
22
- {
23
- module: ResolvedMenuPlug,
24
- type: "menu",
25
- name: "resolved-menu-plug",
26
- },
27
- ];
28
-
29
- const bgImage = "http://applicaster.com";
30
- const isNavBarVisible = true;
31
-
32
- const store = createStore(() => ({
17
+ const mockStore = configureStore()({
33
18
  appState: { appReady: true },
34
- remoteConfigurations: { sdkAssets: { bgImage } },
35
- plugins,
36
- }));
19
+ });
37
20
 
38
21
  describe("Layout TV", () => {
39
- const themes = {
40
- myTheme: { screen_margin_top: 10, screen_margin_bottom: 10 },
41
- };
42
-
43
- const selectedThemeId = "myTheme";
44
-
45
22
  const navigator = {
46
- activeRiver: {
47
- navigations: [{ category: "menu", navigation_type: "menu" }],
48
- },
49
- canGoBack: () => false,
50
23
  currentRoute: "/river/A1234",
51
- screenData: {
52
- navigations: [{ category: "menu", navigation_type: "menu" }],
53
- },
54
- isNavBarVisible: isNavBarVisible,
24
+ mainStack: [
25
+ {
26
+ route: "/river/A1234",
27
+ state: "state",
28
+ },
29
+ ],
55
30
  };
56
31
 
57
32
  const wrapper = ({ children }) => (
58
- <Provider store={store}>
33
+ <Provider store={mockStore}>
59
34
  <NavigationContext.Provider value={navigator}>
60
- <ThemeContext.Provider value={{ themes, selectedThemeId }}>
61
- {children}
62
- </ThemeContext.Provider>
35
+ {children}
63
36
  </NavigationContext.Provider>
64
37
  </Provider>
65
38
  );
66
39
 
67
- const Screen = (props) => (
68
- <View testID="screen-component" {...R.omit(["children"])(props)} />
69
- );
70
-
71
40
  const NavBar = (props) => (
72
- <View testID="nav-bar-component" {...R.omit(["children"])(props)} />
41
+ <View testID="nav-bar-component" {...withoutChildren(props)} />
73
42
  );
74
43
 
75
44
  const Background = (props: Record<any, any>) => (
76
- <View testID="background-component" {...R.omit(["children"])(props)}>
45
+ <View testID="background-component" {...withoutChildren(props)}>
77
46
  {props.children}
78
47
  </View>
79
48
  );
@@ -84,33 +53,8 @@ describe("Layout TV", () => {
84
53
  };
85
54
 
86
55
  it("renders", () => {
87
- const { toJSON } = render(
88
- <Layout Components={Components}>
89
- <Screen />
90
- </Layout>,
91
- { wrapper }
92
- );
56
+ const { toJSON } = render(<Layout Components={Components} />, { wrapper });
93
57
 
94
58
  expect(toJSON()).toMatchSnapshot();
95
59
  });
96
-
97
- describe("props passed to background component", () => {
98
- it("passes correct props", () => {
99
- const { getByTestId } = render(
100
- <Layout
101
- Components={Components}
102
- ComponentsExtraProps={{
103
- Background: { testProp: true },
104
- }}
105
- >
106
- <Screen />
107
- </Layout>,
108
- { wrapper }
109
- );
110
-
111
- const bgComponent = getByTestId("background-component");
112
- expect(bgComponent.props.backgroundImageUrl).toBe(bgImage);
113
- expect(bgComponent.props.testProp).toBe(true);
114
- });
115
- });
116
60
  });
@@ -14,12 +14,12 @@ import { getBackgroundImageUrl } from "../utils";
14
14
  import { LayoutContainer } from "./LayoutContainer";
15
15
  import { NavBarContainer } from "./NavBarContainer";
16
16
  import { ScreenContainer } from "./ScreenContainer";
17
- import { TransitionOverlay } from "./TransitionOverlay";
18
17
 
19
18
  import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
20
19
  import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
21
20
  import { PathnameContext } from "../../../Contexts/PathnameContext";
22
21
  import { useNavbarState } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
22
+ import { ScreenDataContext } from "../../../Contexts/ScreenDataContext";
23
23
 
24
24
  type Components = {
25
25
  NavBar: React.ComponentType<any>;
@@ -105,21 +105,22 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
105
105
  backgroundImageUrl={backgroundImageUrl}
106
106
  {...R.omit(["ref"])(ComponentsExtraProps?.Background)}
107
107
  >
108
- {!isTVOS ? navBarContainer : null}
109
- {navBarReady ? (
110
- /**
108
+ <ScreenDataContext.Provider value={navigator.data}>
109
+ <PathnameContext.Provider value={navigator.currentRoute}>
110
+ {!isTVOS ? navBarContainer : null}
111
+ {navBarReady ? (
112
+ /**
111
113
  Need to re-mount this component on each navigation event
112
114
  It's a hack to work around the fact that we don't support
113
115
  correct screen rendering in our navigator (shared currentRoute state)
114
116
  **/
115
- <PathnameContext.Provider value={navigator.currentRoute}>
116
- <ScreenContainer>{children}</ScreenContainer>
117
- </PathnameContext.Provider>
118
- ) : null}
119
- {/** Render NavBarContainer over ScreenContainer to let tvOS native
117
+ <ScreenContainer>{children}</ScreenContainer>
118
+ ) : null}
119
+ {/** Render NavBarContainer over ScreenContainer to let tvOS native
120
120
  focus manager works correctly to focus nav bar items first **/}
121
- {!isTVOS ? navBarContainer : null}
122
- <TransitionOverlay />
121
+ {isTVOS ? navBarContainer : null}
122
+ </PathnameContext.Provider>
123
+ </ScreenDataContext.Provider>
123
124
  </Components.Background>
124
125
  </ScreenLayoutContextProvider>
125
126
  </LayoutContainer>
@@ -0,0 +1,33 @@
1
+ import * as React from "react";
2
+ import { pathOr } from "ramda";
3
+
4
+ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
5
+
6
+ import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
7
+ import { StackNavigator } from "../../Navigator";
8
+
9
+ type Components = {
10
+ NavBar: React.ComponentType<any>;
11
+ Background: React.ComponentType<any>;
12
+ };
13
+
14
+ type Props = {
15
+ Components: Components;
16
+ };
17
+
18
+ const Layout = ({ Components }: Props) => {
19
+ const { appState } = usePickFromState(["appState"]);
20
+ const appReady = pathOr(false, ["appReady"], appState);
21
+
22
+ if (!appReady) {
23
+ return null;
24
+ }
25
+
26
+ return (
27
+ <ScreenLayoutContextProvider>
28
+ <StackNavigator Components={Components} />
29
+ </ScreenLayoutContextProvider>
30
+ );
31
+ };
32
+
33
+ export default Layout;
@@ -1,20 +1,24 @@
1
1
  import * as React from "react";
2
- import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
3
- import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
4
- import { ScreenContextProvider } from "../../Contexts/ScreenContext";
2
+ import {
3
+ isTV,
4
+ platformSelect,
5
+ } from "@applicaster/zapp-react-native-utils/reactUtils";
5
6
 
6
7
  import Mobile from "./Mobile";
7
- import TV from "./TV";
8
8
 
9
- export default function Layout(props) {
10
- const { currentRoute: pathname } = useNavigation();
9
+ import TVWeb from "./TV/index.web";
10
+ import TVDefault from "./TV/index";
11
+
12
+ const TV = platformSelect({
13
+ samsung_tv: TVWeb,
14
+ lg_tv: TVWeb,
15
+ web: TVWeb,
16
+ default: TVDefault,
17
+ });
11
18
 
19
+ export default function Layout(props) {
12
20
  if (isTV()) {
13
- return (
14
- <ScreenContextProvider pathname={pathname}>
15
- <TV {...props} />
16
- </ScreenContextProvider>
17
- );
21
+ return <TV {...props} />;
18
22
  } else {
19
23
  return <Mobile {...props} />;
20
24
  }
@@ -25,6 +25,7 @@ type Props = {
25
25
  testID?: string;
26
26
  accessibilityLabel?: string;
27
27
  cellUUID?: string;
28
+ extraProps?: Record<string, any>;
28
29
  };
29
30
 
30
31
  function isStringAsset(asset) {
@@ -108,6 +109,7 @@ export function ActionButton(props: Props) {
108
109
  flavour={flavour}
109
110
  asset={getAssetValue(asset, flavour)}
110
111
  cellUUID={cellUUID}
112
+ {...(props?.extraProps ?? {})}
111
113
  />
112
114
  )}
113
115
  </TouchableOpacity>
@@ -7,50 +7,28 @@ type BorderPosition = "inside" | "outside" | "center";
7
7
  interface Props {
8
8
  style: ImageStyle | ViewStyle;
9
9
  borderPosition: BorderPosition;
10
+ borderPaddingTop: number;
11
+ borderPaddingRight: number;
12
+ borderPaddingBottom: number;
13
+ borderPaddingLeft: number;
10
14
  children: React.ReactNode;
11
15
  }
12
16
 
13
- const MARGINS = [
14
- "marginTop",
15
- "marginBottom",
16
- "marginLeft",
17
- "marginRight",
18
- ] as const;
19
-
20
- const PADDINGS = [
21
- "paddingTop",
22
- "paddingBottom",
23
- "paddingLeft",
24
- "paddingRight",
25
- ] as const;
26
-
27
- const isPercentageValue = (value: number | string) => {
28
- if (typeof value === "string" && value.endsWith("%")) {
29
- return true;
30
- }
31
-
32
- return false;
33
- };
34
-
35
- const calculateContainerSpacing = ({
36
- spacingKey,
37
- style,
38
- additionalSpacing,
39
- }: {
40
- spacingKey: typeof MARGINS[number] | typeof PADDINGS[number];
41
- style: ImageStyle | ViewStyle;
42
- additionalSpacing: number;
43
- }) => {
44
- return {
45
- [spacingKey]:
46
- toNumberWithDefaultZero(style[spacingKey]) + additionalSpacing,
47
- };
48
- };
49
-
50
- const getContainerBorderWidth = (
51
- borderPosition: BorderPosition,
52
- borderWidth
53
- ) => {
17
+ const COLOR_TRANSPARENT = "transparent";
18
+
19
+ const styles = StyleSheet.create({
20
+ container: {
21
+ overflow: "visible",
22
+ borderWidth: 0,
23
+ borderColor: COLOR_TRANSPARENT,
24
+ },
25
+ border: {
26
+ ...StyleSheet.absoluteFillObject,
27
+ zIndex: 1,
28
+ },
29
+ });
30
+
31
+ const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
54
32
  switch (borderPosition) {
55
33
  case "inside":
56
34
  return 0;
@@ -63,104 +41,102 @@ const getContainerBorderWidth = (
63
41
  }
64
42
  };
65
43
 
66
- const getContainerPadding = (borderPosition: BorderPosition, borderWidth) => {
67
- switch (borderPosition) {
68
- case "inside":
69
- return borderWidth;
70
- case "center":
71
- return borderWidth / 2;
72
- case "outside":
73
- return 0;
74
- default:
75
- return 0;
76
- }
77
- };
78
-
79
- const InsideBorder = ({
44
+ const Border = ({
80
45
  style,
46
+ padding,
81
47
  borderPosition,
82
48
  }: {
83
49
  style: ViewStyle;
50
+ padding: ViewStyle;
84
51
  borderPosition: BorderPosition;
85
52
  }) => {
86
- if (borderPosition === "outside") {
53
+ if (!borderPosition) {
87
54
  return null;
88
55
  }
89
56
 
90
- const insideBorderWidth =
91
- borderPosition === "center" ? style.borderWidth / 2 : style.borderWidth;
57
+ const borderWidth = toNumberWithDefaultZero(style.borderWidth);
58
+ const containerPadding = getBorderPadding(borderPosition, borderWidth);
59
+
60
+ const initialBorderRadius = toNumberWithDefaultZero(style.borderRadius);
61
+ const paddingTop = toNumberWithDefaultZero(padding.paddingTop);
62
+ const paddingRight = toNumberWithDefaultZero(padding.paddingRight);
63
+ const paddingBottom = toNumberWithDefaultZero(padding.paddingBottom);
64
+ const paddingLeft = toNumberWithDefaultZero(padding.paddingLeft);
65
+
66
+ const borderRadiusStyle = initialBorderRadius
67
+ ? {
68
+ borderTopLeftRadius:
69
+ initialBorderRadius +
70
+ containerPadding +
71
+ Math.min(paddingTop, paddingLeft),
72
+ borderTopRightRadius:
73
+ initialBorderRadius +
74
+ containerPadding +
75
+ Math.min(paddingTop, paddingRight),
76
+ borderBottomLeftRadius:
77
+ initialBorderRadius +
78
+ containerPadding +
79
+ Math.min(paddingBottom, paddingLeft),
80
+ borderBottomRightRadius:
81
+ initialBorderRadius +
82
+ containerPadding +
83
+ Math.min(paddingBottom, paddingRight),
84
+ }
85
+ : {
86
+ borderTopLeftRadius: 0,
87
+ borderTopRightRadius: 0,
88
+ borderBottomLeftRadius: 0,
89
+ borderBottomRightRadius: 0,
90
+ };
92
91
 
93
92
  return (
94
93
  <View
94
+ pointerEvents="none"
95
95
  style={{
96
- ...StyleSheet.absoluteFillObject,
97
- borderWidth: insideBorderWidth,
96
+ ...styles.border,
97
+ right: -containerPadding - paddingRight,
98
+ bottom: -containerPadding - paddingBottom,
99
+ left: -containerPadding - paddingLeft,
100
+ top: -containerPadding - paddingTop,
101
+ borderWidth,
98
102
  borderColor: style.borderColor,
99
- borderRadius: style.borderRadius,
103
+ ...borderRadiusStyle,
100
104
  }}
101
105
  />
102
106
  );
103
107
  };
104
108
 
105
109
  export const BorderContainerView = (props: Props) => {
106
- const { style, children } = props;
107
- // Return border position prop after re-implementing
108
- const borderPosition = "outside";
109
-
110
- const borderWidth = toNumberWithDefaultZero(style.borderWidth);
111
- const spacing = getContainerPadding(borderPosition, borderWidth);
110
+ const {
111
+ borderPosition,
112
+ borderPaddingTop,
113
+ borderPaddingRight,
114
+ borderPaddingBottom,
115
+ borderPaddingLeft,
116
+ style,
117
+ children,
118
+ } = props;
112
119
 
113
120
  const borderStyleForMissingBorder =
114
121
  style?.borderWidth === 0 || !style?.borderColor ? { borderRadius: 0 } : {};
115
122
 
116
- const updatedMargins = MARGINS.reduce((acc, margin) => {
117
- const marginValue = calculateContainerSpacing({
118
- spacingKey: margin,
119
- style,
120
- additionalSpacing: spacing,
121
- });
122
-
123
- return { ...acc, ...marginValue };
124
- }, {});
125
-
126
- const updatedPaddings = PADDINGS.reduce((acc, padding) => {
127
- const paddingValue = calculateContainerSpacing({
128
- spacingKey: padding,
129
- style,
130
- additionalSpacing: spacing,
131
- });
132
-
133
- return { ...acc, ...paddingValue };
134
- }, {});
135
-
136
- const updatedWidth = style.width
137
- ? isPercentageValue(style.width)
138
- ? style.width
139
- : toNumberWithDefaultZero(style.width) - spacing * 2
140
- : undefined;
141
-
142
- const updatedHeight = style.height
143
- ? isPercentageValue(style.height)
144
- ? style.height
145
- : toNumberWithDefaultZero(style.height) - spacing * 2
146
- : undefined;
147
-
148
- const containerStyle = {
149
- borderWidth: getContainerBorderWidth(borderPosition, borderWidth),
150
- padding: spacing,
151
- ...updatedMargins,
152
- ...updatedPaddings,
153
- width: updatedWidth,
154
- height: updatedHeight,
155
- };
123
+ const padding =
124
+ borderPosition === "outside"
125
+ ? {
126
+ paddingTop: borderPaddingTop,
127
+ paddingRight: borderPaddingRight,
128
+ paddingBottom: borderPaddingBottom,
129
+ paddingLeft: borderPaddingLeft,
130
+ }
131
+ : {};
156
132
 
157
133
  return (
158
- <View {...props} style={[style, containerStyle]}>
134
+ <View
135
+ {...props}
136
+ style={[style, styles.container, borderStyleForMissingBorder]}
137
+ >
138
+ <Border style={style} padding={padding} borderPosition={borderPosition} />
159
139
  {children}
160
- <InsideBorder
161
- style={{ ...style, ...borderStyleForMissingBorder }}
162
- borderPosition={borderPosition}
163
- />
164
140
  </View>
165
141
  );
166
142
  };