@applicaster/zapp-react-native-ui-components 12.0.0-alpha.3893335783 → 12.0.0-alpha.4054240460
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.
- package/Components/BaseFocusable/index.tsx +1 -1
- package/Components/Cell/Cell.tsx +14 -5
- package/Components/Cell/CellWithFocusable.tsx +17 -5
- package/Components/FocusableGroup/index.tsx +1 -0
- package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
- package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
- package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
- package/Components/Layout/TV/__tests__/index.test.tsx +19 -75
- package/Components/Layout/TV/index.tsx +0 -2
- package/Components/Layout/TV/index.web.tsx +33 -0
- package/Components/Layout/index.tsx +15 -11
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +80 -109
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +4 -1
- package/Components/MasterCell/utils/index.ts +17 -30
- package/Components/Navigator/StackNavigator.tsx +48 -0
- package/Components/Navigator/index.ts +1 -0
- package/Components/PlayerContainer/PlayerContainer.tsx +8 -1
- package/Components/River/TV/River.tsx +0 -23
- package/Components/Screen/TV/hooks/index.ts +3 -0
- package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
- package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
- package/Components/Screen/TV/index.web.tsx +179 -0
- package/Components/Screen/__tests__/Screen.test.tsx +3 -0
- package/Components/Screen/index.tsx +29 -1
- package/Components/ScreenResolver/index.tsx +3 -1
- package/Components/Transitioner/Transitioner.tsx +15 -29
- package/Components/VideoLive/LiveImageManager.ts +15 -1
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +48 -38
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +40 -33
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +94 -71
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
- package/Components/VideoModal/PlayerDetails.tsx +3 -3
- package/Components/VideoModal/PlayerWrapper.tsx +8 -20
- package/Components/VideoModal/VideoModal.tsx +4 -4
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +0 -7
- package/Components/VideoModal/utils.ts +5 -2
- package/Contexts/ScreenDataContext/index.tsx +20 -0
- package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
- package/package.json +5 -5
- package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
- package/Contexts/ScreenDataContext/index.ts +0 -3
package/Components/Cell/Cell.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import
|
|
2
|
+
import { join, path, merge } from "ramda";
|
|
3
3
|
import { View } from "react-native";
|
|
4
4
|
|
|
5
5
|
import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
|
|
@@ -11,6 +11,10 @@ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
|
11
11
|
|
|
12
12
|
import { CellWithFocusable } from "./CellWithFocusable";
|
|
13
13
|
import { BaseFocusable } from "../BaseFocusable";
|
|
14
|
+
import {
|
|
15
|
+
isVizioPlatform,
|
|
16
|
+
TTSManager,
|
|
17
|
+
} from "@applicaster/zapp-react-native-utils/appUtils/platform/platformUtils";
|
|
14
18
|
|
|
15
19
|
type Props = {
|
|
16
20
|
item: {
|
|
@@ -105,8 +109,8 @@ export class CellComponent extends React.Component<Props> {
|
|
|
105
109
|
onPress(item, index);
|
|
106
110
|
} else {
|
|
107
111
|
const { item, navigator, component, appData } = this.props;
|
|
108
|
-
const itemTargetScreen =
|
|
109
|
-
const componentTargetScreen =
|
|
112
|
+
const itemTargetScreen = path(["screen_type"], item);
|
|
113
|
+
const componentTargetScreen = path(["data", "target"], component);
|
|
110
114
|
const itemType = getItemType(item, appData?.layoutVersion);
|
|
111
115
|
|
|
112
116
|
sendSelectCellEvent(item, component, item.title, index);
|
|
@@ -116,7 +120,7 @@ export class CellComponent extends React.Component<Props> {
|
|
|
116
120
|
componentTargetScreen &&
|
|
117
121
|
!itemTargetScreen
|
|
118
122
|
) {
|
|
119
|
-
navigator.push(
|
|
123
|
+
navigator.push(merge(item, { screen_type: componentTargetScreen }));
|
|
120
124
|
|
|
121
125
|
return;
|
|
122
126
|
}
|
|
@@ -139,6 +143,11 @@ export class CellComponent extends React.Component<Props> {
|
|
|
139
143
|
shouldScrollVertically?.(mouse, focusable, id, title)
|
|
140
144
|
) {
|
|
141
145
|
this.scrollVertically(focusable);
|
|
146
|
+
|
|
147
|
+
if (isVizioPlatform()) {
|
|
148
|
+
const ttsManager = TTSManager.getInstance();
|
|
149
|
+
ttsManager.readText(title);
|
|
150
|
+
}
|
|
142
151
|
}
|
|
143
152
|
}
|
|
144
153
|
}
|
|
@@ -207,7 +216,7 @@ export class CellComponent extends React.Component<Props> {
|
|
|
207
216
|
|
|
208
217
|
const { id } = item;
|
|
209
218
|
|
|
210
|
-
const focusableId =
|
|
219
|
+
const focusableId = join("-", [component?.id, id, index]);
|
|
211
220
|
|
|
212
221
|
const handleFocus = (focusable, mouse) => {
|
|
213
222
|
const focusFn = onFocus || noop;
|
|
@@ -6,12 +6,20 @@ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
|
6
6
|
import { isAppleTV } from "../../Helpers/Platform";
|
|
7
7
|
import { useCellState } from "../MasterCell/utils";
|
|
8
8
|
|
|
9
|
-
const useCellFocusedState = (
|
|
9
|
+
const useCellFocusedState = (
|
|
10
|
+
skipFocusManagerRegistration: boolean,
|
|
11
|
+
groupId: string,
|
|
12
|
+
id: string
|
|
13
|
+
) => {
|
|
10
14
|
const [currentCellFocused, setCurrentCellFocused] = React.useState(false);
|
|
11
15
|
|
|
12
16
|
React.useEffect(() => {
|
|
13
|
-
const isGroupItemFocused = () =>
|
|
14
|
-
|
|
17
|
+
const isGroupItemFocused = () => {
|
|
18
|
+
if (!skipFocusManagerRegistration) {
|
|
19
|
+
const isFocused = focusManager.isGroupItemFocused(groupId, id);
|
|
20
|
+
setCurrentCellFocused(isFocused);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
15
23
|
|
|
16
24
|
const handler = () => {
|
|
17
25
|
// tvOS hack for properly checking focus
|
|
@@ -29,7 +37,7 @@ const useCellFocusedState = (groupId: string, id: string) => {
|
|
|
29
37
|
return () => {
|
|
30
38
|
focusManager.removeHandler(FOCUS_EVENTS.FOCUS, handler);
|
|
31
39
|
};
|
|
32
|
-
}, [groupId]);
|
|
40
|
+
}, [groupId, skipFocusManagerRegistration]);
|
|
33
41
|
|
|
34
42
|
return currentCellFocused;
|
|
35
43
|
};
|
|
@@ -65,7 +73,11 @@ export function CellWithFocusable(props: Props) {
|
|
|
65
73
|
selected,
|
|
66
74
|
} = props;
|
|
67
75
|
|
|
68
|
-
const isFocused = useCellFocusedState(
|
|
76
|
+
const isFocused = useCellFocusedState(
|
|
77
|
+
skipFocusManagerRegistration,
|
|
78
|
+
groupId,
|
|
79
|
+
id
|
|
80
|
+
);
|
|
69
81
|
|
|
70
82
|
const state = useCellState({
|
|
71
83
|
id: item.id,
|
|
@@ -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
|
}
|
|
@@ -60,7 +60,7 @@ export function ScreenLayoutContextProvider(props: {
|
|
|
60
60
|
initialState
|
|
61
61
|
);
|
|
62
62
|
|
|
63
|
-
const
|
|
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={
|
|
72
|
+
<ScreenLayoutContext.Provider value={screenLayoutValue}>
|
|
73
73
|
{props.children}
|
|
74
74
|
</ScreenLayoutContext.Provider>
|
|
75
75
|
);
|
|
@@ -2,58 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`Layout TV renders 1`] = `
|
|
4
4
|
<View
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
style={
|
|
20
|
-
Array [
|
|
21
|
-
Object {
|
|
22
|
-
"position": "absolute",
|
|
23
|
-
"top": 0,
|
|
24
|
-
"width": 1920,
|
|
25
|
-
"zIndex": 10,
|
|
26
|
-
},
|
|
27
|
-
Object {
|
|
28
|
-
"maxHeight": 10,
|
|
29
|
-
},
|
|
30
|
-
]
|
|
10
|
+
Components={
|
|
11
|
+
Object {
|
|
12
|
+
"Background": [Function],
|
|
13
|
+
"NavBar": [Function],
|
|
31
14
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
isVisible={true}
|
|
36
|
-
menuPlugin={
|
|
37
|
-
Object {
|
|
38
|
-
"module": [Function],
|
|
39
|
-
"name": "resolved-menu-plug",
|
|
40
|
-
"type": "menu",
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
navigationProps={
|
|
44
|
-
Object {
|
|
45
|
-
"assets": undefined,
|
|
46
|
-
"home": undefined,
|
|
47
|
-
"nav_items": undefined,
|
|
48
|
-
"rules": undefined,
|
|
49
|
-
"selected": undefined,
|
|
50
|
-
"styles": undefined,
|
|
51
|
-
"title": "",
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
testID="nav-bar-component"
|
|
55
|
-
/>
|
|
56
|
-
</View>
|
|
57
|
-
</View>
|
|
15
|
+
}
|
|
16
|
+
route="/river/A1234"
|
|
17
|
+
/>
|
|
58
18
|
</View>
|
|
59
19
|
`;
|
|
@@ -1,79 +1,48 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import
|
|
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
|
-
|
|
9
|
+
const withoutChildren = omit(["children"]);
|
|
10
10
|
|
|
11
|
-
jest.mock("
|
|
12
|
-
|
|
11
|
+
jest.mock("../../../Screen/TV/index.web", () => ({
|
|
12
|
+
Screen: View,
|
|
13
13
|
}));
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
const Layout = require("../index.web").default;
|
|
16
16
|
|
|
17
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
24
|
+
mainStack: [
|
|
25
|
+
{
|
|
26
|
+
route: "/river/A1234",
|
|
27
|
+
state: "state",
|
|
28
|
+
},
|
|
29
|
+
],
|
|
55
30
|
};
|
|
56
31
|
|
|
57
32
|
const wrapper = ({ children }) => (
|
|
58
|
-
<Provider store={
|
|
33
|
+
<Provider store={mockStore}>
|
|
59
34
|
<NavigationContext.Provider value={navigator}>
|
|
60
|
-
|
|
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" {...
|
|
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" {...
|
|
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,7 +14,6 @@ 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";
|
|
@@ -122,7 +121,6 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
|
122
121
|
{isTVOS ? navBarContainer : null}
|
|
123
122
|
</PathnameContext.Provider>
|
|
124
123
|
</ScreenDataContext.Provider>
|
|
125
|
-
<TransitionOverlay />
|
|
126
124
|
</Components.Background>
|
|
127
125
|
</ScreenLayoutContextProvider>
|
|
128
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 {
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
10
|
-
|
|
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
|
}
|
|
@@ -7,50 +7,24 @@ 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
|
|
14
|
-
"marginTop",
|
|
15
|
-
"marginBottom",
|
|
16
|
-
"marginLeft",
|
|
17
|
-
"marginRight",
|
|
18
|
-
] as const;
|
|
17
|
+
const COLOR_TRANSPARENT = "transparent";
|
|
19
18
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
const styles = StyleSheet.create({
|
|
20
|
+
container: {
|
|
21
|
+
overflow: "visible",
|
|
22
|
+
borderWidth: 0,
|
|
23
|
+
borderColor: COLOR_TRANSPARENT,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
26
|
|
|
27
|
-
const
|
|
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
|
-
) => {
|
|
27
|
+
const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
|
|
54
28
|
switch (borderPosition) {
|
|
55
29
|
case "inside":
|
|
56
30
|
return 0;
|
|
@@ -63,104 +37,101 @@ const getContainerBorderWidth = (
|
|
|
63
37
|
}
|
|
64
38
|
};
|
|
65
39
|
|
|
66
|
-
const
|
|
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 = ({
|
|
40
|
+
const Border = ({
|
|
80
41
|
style,
|
|
42
|
+
padding,
|
|
81
43
|
borderPosition,
|
|
82
44
|
}: {
|
|
83
45
|
style: ViewStyle;
|
|
46
|
+
padding: ViewStyle;
|
|
84
47
|
borderPosition: BorderPosition;
|
|
85
48
|
}) => {
|
|
86
|
-
if (borderPosition
|
|
49
|
+
if (!borderPosition) {
|
|
87
50
|
return null;
|
|
88
51
|
}
|
|
89
52
|
|
|
90
|
-
const
|
|
91
|
-
|
|
53
|
+
const borderWidth = toNumberWithDefaultZero(style.borderWidth);
|
|
54
|
+
const containerPadding = getBorderPadding(borderPosition, borderWidth);
|
|
55
|
+
|
|
56
|
+
const initialBorderRadius = toNumberWithDefaultZero(style.borderRadius);
|
|
57
|
+
const paddingTop = toNumberWithDefaultZero(padding.paddingTop);
|
|
58
|
+
const paddingRight = toNumberWithDefaultZero(padding.paddingRight);
|
|
59
|
+
const paddingBottom = toNumberWithDefaultZero(padding.paddingBottom);
|
|
60
|
+
const paddingLeft = toNumberWithDefaultZero(padding.paddingLeft);
|
|
61
|
+
|
|
62
|
+
const borderRadiusStyle = initialBorderRadius
|
|
63
|
+
? {
|
|
64
|
+
borderTopLeftRadius:
|
|
65
|
+
initialBorderRadius +
|
|
66
|
+
containerPadding +
|
|
67
|
+
Math.min(paddingTop, paddingLeft),
|
|
68
|
+
borderTopRightRadius:
|
|
69
|
+
initialBorderRadius +
|
|
70
|
+
containerPadding +
|
|
71
|
+
Math.min(paddingTop, paddingRight),
|
|
72
|
+
borderBottomLeftRadius:
|
|
73
|
+
initialBorderRadius +
|
|
74
|
+
containerPadding +
|
|
75
|
+
Math.min(paddingBottom, paddingLeft),
|
|
76
|
+
borderBottomRightRadius:
|
|
77
|
+
initialBorderRadius +
|
|
78
|
+
containerPadding +
|
|
79
|
+
Math.min(paddingBottom, paddingRight),
|
|
80
|
+
}
|
|
81
|
+
: {
|
|
82
|
+
borderTopLeftRadius: 0,
|
|
83
|
+
borderTopRightRadius: 0,
|
|
84
|
+
borderBottomLeftRadius: 0,
|
|
85
|
+
borderBottomRightRadius: 0,
|
|
86
|
+
};
|
|
92
87
|
|
|
93
88
|
return (
|
|
94
89
|
<View
|
|
95
90
|
style={{
|
|
96
91
|
...StyleSheet.absoluteFillObject,
|
|
97
|
-
|
|
92
|
+
right: -containerPadding - paddingRight,
|
|
93
|
+
bottom: -containerPadding - paddingBottom,
|
|
94
|
+
left: -containerPadding - paddingLeft,
|
|
95
|
+
top: -containerPadding - paddingTop,
|
|
96
|
+
borderWidth,
|
|
98
97
|
borderColor: style.borderColor,
|
|
99
|
-
|
|
98
|
+
...borderRadiusStyle,
|
|
100
99
|
}}
|
|
101
100
|
/>
|
|
102
101
|
);
|
|
103
102
|
};
|
|
104
103
|
|
|
105
104
|
export const BorderContainerView = (props: Props) => {
|
|
106
|
-
const {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
const {
|
|
106
|
+
borderPosition,
|
|
107
|
+
borderPaddingTop,
|
|
108
|
+
borderPaddingRight,
|
|
109
|
+
borderPaddingBottom,
|
|
110
|
+
borderPaddingLeft,
|
|
111
|
+
style,
|
|
112
|
+
children,
|
|
113
|
+
} = props;
|
|
112
114
|
|
|
113
115
|
const borderStyleForMissingBorder =
|
|
114
116
|
style?.borderWidth === 0 || !style?.borderColor ? { borderRadius: 0 } : {};
|
|
115
117
|
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
};
|
|
118
|
+
const padding =
|
|
119
|
+
borderPosition === "outside"
|
|
120
|
+
? {
|
|
121
|
+
paddingTop: borderPaddingTop,
|
|
122
|
+
paddingRight: borderPaddingRight,
|
|
123
|
+
paddingBottom: borderPaddingBottom,
|
|
124
|
+
paddingLeft: borderPaddingLeft,
|
|
125
|
+
}
|
|
126
|
+
: {};
|
|
156
127
|
|
|
157
128
|
return (
|
|
158
|
-
<View
|
|
129
|
+
<View
|
|
130
|
+
{...props}
|
|
131
|
+
style={[style, styles.container, borderStyleForMissingBorder]}
|
|
132
|
+
>
|
|
159
133
|
{children}
|
|
160
|
-
<
|
|
161
|
-
style={{ ...style, ...borderStyleForMissingBorder }}
|
|
162
|
-
borderPosition={borderPosition}
|
|
163
|
-
/>
|
|
134
|
+
<Border style={style} padding={padding} borderPosition={borderPosition} />
|
|
164
135
|
</View>
|
|
165
136
|
);
|
|
166
137
|
};
|