Browse Source

Implement selected and unselected toTab color (#2429)

* Implement selected and unselected toTab color

* Test options are applied when layout is visible
Guy Carmeli 6 years ago
parent
commit
5624c4f309
No account linked to committer's email address
27 changed files with 295 additions and 116 deletions
  1. 12
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/ColorParser.java
  2. 3
    1
      lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java
  3. 2
    4
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java
  4. 23
    4
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabsOptions.java
  5. 6
    0
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java
  6. 27
    83
      lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java
  7. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsAdapter.java
  8. 9
    5
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java
  9. 5
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopBar.java
  10. 14
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabs.java
  11. 4
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsLayout.java
  12. 22
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsLayoutCreator.java
  13. 26
    1
      lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsStyleHelper.java
  14. 30
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/style/Color.java
  15. 18
    0
      lib/android/app/src/main/java/com/reactnativenavigation/views/style/NullColor.java
  16. 18
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java
  17. 4
    3
      lib/src/Navigation.js
  18. 1
    0
      lib/src/commands/Commands.js
  19. 4
    3
      lib/src/commands/LayoutTreeParser.js
  20. 2
    1
      lib/src/commands/LayoutTreeParser.test.js
  21. 8
    4
      lib/src/params/containers/Container.js
  22. 12
    3
      lib/src/params/containers/Container.test.js
  23. 3
    0
      lib/src/params/options/NavigationOptions.js
  24. 8
    1
      lib/src/params/options/NavigationOptions.test.js
  25. 12
    0
      lib/src/params/options/TopTabs.js
  26. 14
    0
      lib/src/params/options/TopTabs.test.js
  27. 7
    1
      playground/src/containers/WelcomeScreen.js

+ 12
- 0
lib/android/app/src/main/java/com/reactnativenavigation/parse/ColorParser.java View File

1
+package com.reactnativenavigation.parse;
2
+
3
+import com.reactnativenavigation.views.style.Color;
4
+import com.reactnativenavigation.views.style.NullColor;
5
+
6
+import org.json.JSONObject;
7
+
8
+public class ColorParser {
9
+    public static Color parse(JSONObject json, String color) {
10
+        return json.has(color) ? new Color(json.optInt(color)) : new NullColor();
11
+    }
12
+}

+ 3
- 1
lib/android/app/src/main/java/com/reactnativenavigation/parse/LayoutFactory.java View File

14
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabController;
14
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabController;
15
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
15
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
16
 import com.reactnativenavigation.views.ContainerViewCreator;
16
 import com.reactnativenavigation.views.ContainerViewCreator;
17
+import com.reactnativenavigation.views.TopTabsLayoutCreator;
17
 
18
 
18
 import java.util.ArrayList;
19
 import java.util.ArrayList;
19
 import java.util.List;
20
 import java.util.List;
136
             tabController.setTabIndex(i);
137
             tabController.setTabIndex(i);
137
             tabs.add(tabController);
138
             tabs.add(tabController);
138
         }
139
         }
139
-        return new TopTabsController(activity, node.id, tabs);
140
+        NavigationOptions navigationOptions = NavigationOptions.parse(typefaceManager, node.getNavigationOptions(), defaultOptions);
141
+        return new TopTabsController(activity, node.id, tabs, new TopTabsLayoutCreator(activity, tabs), navigationOptions);
140
     }
142
     }
141
 
143
 
142
     private ViewController createTopTab(LayoutNode node) {
144
     private ViewController createTopTab(LayoutNode node) {

+ 2
- 4
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java View File

44
 			textFontSize = other.textFontSize;
44
 			textFontSize = other.textFontSize;
45
 		if (other.textFontFamily != null)
45
 		if (other.textFontFamily != null)
46
 			textFontFamily = other.textFontFamily;
46
 			textFontFamily = other.textFontFamily;
47
-		if (other.hidden != NavigationOptions.BooleanOptions.NoValue) {
47
+		if (other.hidden != NavigationOptions.BooleanOptions.NoValue)
48
 			hidden = other.hidden;
48
 			hidden = other.hidden;
49
-		}
50
-		if (other.animateHide != NavigationOptions.BooleanOptions.NoValue) {
49
+		if (other.animateHide != NavigationOptions.BooleanOptions.NoValue)
51
 			animateHide = other.animateHide;
50
 			animateHide = other.animateHide;
52
-		}
53
 	}
51
 	}
54
 
52
 
55
     void mergeWithDefault(TopBarOptions defaultOptions) {
53
     void mergeWithDefault(TopBarOptions defaultOptions) {

+ 23
- 4
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopTabsOptions.java View File

1
 package com.reactnativenavigation.parse;
1
 package com.reactnativenavigation.parse;
2
 
2
 
3
+import android.support.annotation.NonNull;
3
 import android.support.annotation.Nullable;
4
 import android.support.annotation.Nullable;
4
 
5
 
6
+import com.reactnativenavigation.views.style.Color;
7
+import com.reactnativenavigation.views.style.NullColor;
8
+
5
 import org.json.JSONObject;
9
 import org.json.JSONObject;
6
 
10
 
7
 public class TopTabsOptions implements DEFAULT_VALUES {
11
 public class TopTabsOptions implements DEFAULT_VALUES {
8
 
12
 
13
+    @NonNull public Color selectedTabColor = new NullColor();
14
+    @NonNull public Color unselectedTabColor = new NullColor();
15
+
9
     public static TopTabsOptions parse(@Nullable JSONObject json) {
16
     public static TopTabsOptions parse(@Nullable JSONObject json) {
10
         TopTabsOptions result = new TopTabsOptions();
17
         TopTabsOptions result = new TopTabsOptions();
11
         if (json == null) return result;
18
         if (json == null) return result;
19
+        result.selectedTabColor = ColorParser.parse(json, "selectedTabColor");
20
+        result.unselectedTabColor = ColorParser.parse(json, "unselectedTabColor");
12
         return result;
21
         return result;
13
     }
22
     }
14
 
23
 
15
-    void mergeWith(TopTabsOptions topTabsOptions) {
16
-
24
+    void mergeWith(TopTabsOptions other) {
25
+        if (other.selectedTabColor.hasColor()) {
26
+            selectedTabColor = other.selectedTabColor;
27
+        }
28
+        if (other.unselectedTabColor.hasColor()) {
29
+            unselectedTabColor = other.unselectedTabColor;
30
+        }
17
     }
31
     }
18
 
32
 
19
-    void mergeWithDefault(TopTabsOptions topTabsOptions) {
20
-
33
+    void mergeWithDefault(TopTabsOptions defaultOptions) {
34
+        if (!selectedTabColor.hasColor()) {
35
+            selectedTabColor = defaultOptions.selectedTabColor;
36
+        }
37
+        if (!unselectedTabColor.hasColor()) {
38
+            unselectedTabColor = defaultOptions.unselectedTabColor;
39
+        }
21
     }
40
     }
22
 }
41
 }

+ 6
- 0
lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java View File

7
 import com.reactnativenavigation.parse.NavigationOptions;
7
 import com.reactnativenavigation.parse.NavigationOptions;
8
 import com.reactnativenavigation.parse.TopBarOptions;
8
 import com.reactnativenavigation.parse.TopBarOptions;
9
 import com.reactnativenavigation.parse.TopTabOptions;
9
 import com.reactnativenavigation.parse.TopTabOptions;
10
+import com.reactnativenavigation.parse.TopTabsOptions;
10
 import com.reactnativenavigation.views.TopBar;
11
 import com.reactnativenavigation.views.TopBar;
11
 
12
 
12
 import java.util.ArrayList;
13
 import java.util.ArrayList;
26
     public void applyOptions(NavigationOptions options) {
27
     public void applyOptions(NavigationOptions options) {
27
         applyTopBarOptions(options.topBarOptions);
28
         applyTopBarOptions(options.topBarOptions);
28
         applyButtons(options.leftButtons, options.rightButtons);
29
         applyButtons(options.leftButtons, options.rightButtons);
30
+        applyTopTabsOptions(options.topTabsOptions);
29
         applyTopTabOptions(options.topTabOptions);
31
         applyTopTabOptions(options.topTabOptions);
30
     }
32
     }
31
 
33
 
70
         topBar.setButtons(leftButtons, rightButtons);
72
         topBar.setButtons(leftButtons, rightButtons);
71
     }
73
     }
72
 
74
 
75
+    private void applyTopTabsOptions(TopTabsOptions options) {
76
+        topBar.applyTopTabsColors(options.selectedTabColor, options.unselectedTabColor);
77
+    }
78
+
73
     private void applyTopTabOptions(TopTabOptions topTabOptions) {
79
     private void applyTopTabOptions(TopTabOptions topTabOptions) {
74
         if (topTabOptions.fontFamily != null) {
80
         if (topTabOptions.fontFamily != null) {
75
             topBar.setTopTabFontFamily(topTabOptions.tabIndex, topTabOptions.fontFamily);
81
             topBar.setTopTabFontFamily(topTabOptions.tabIndex, topTabOptions.fontFamily);

+ 27
- 83
lib/android/app/src/main/java/com/reactnativenavigation/react/NavigationModule.java View File

25
 	private static final String NAME = "RNNBridgeModule";
25
 	private static final String NAME = "RNNBridgeModule";
26
 	private final ReactInstanceManager reactInstanceManager;
26
 	private final ReactInstanceManager reactInstanceManager;
27
 
27
 
28
-	public NavigationModule(final ReactApplicationContext reactContext, final ReactInstanceManager reactInstanceManager) {
28
+	@SuppressWarnings("WeakerAccess")
29
+    public NavigationModule(final ReactApplicationContext reactContext, final ReactInstanceManager reactInstanceManager) {
29
 		super(reactContext);
30
 		super(reactContext);
30
 		this.reactInstanceManager = reactInstanceManager;
31
 		this.reactInstanceManager = reactInstanceManager;
31
 	}
32
 	}
38
 	@ReactMethod
39
 	@ReactMethod
39
 	public void setRoot(final ReadableMap rawLayoutTree, final Promise promise) {
40
 	public void setRoot(final ReadableMap rawLayoutTree, final Promise promise) {
40
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
41
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
41
-		handle(new Runnable() {
42
-			@Override
43
-			public void run() {
44
-				final ViewController viewController = newLayoutFactory().create(layoutTree);
45
-				navigator().setRoot(viewController, promise);
46
-			}
47
-		});
42
+		handle(() -> {
43
+            final ViewController viewController = newLayoutFactory().create(layoutTree);
44
+            navigator().setRoot(viewController, promise);
45
+        });
48
 	}
46
 	}
49
 
47
 
50
 	@ReactMethod
48
 	@ReactMethod
51
 	public void setDefaultOptions(final ReadableMap options) {
49
 	public void setDefaultOptions(final ReadableMap options) {
52
         final NavigationOptions defaultOptions = NavigationOptions.parse(new TypefaceLoader(activity()), JSONParser.parse(options));
50
         final NavigationOptions defaultOptions = NavigationOptions.parse(new TypefaceLoader(activity()), JSONParser.parse(options));
53
-        handle(new Runnable() {
54
-            @Override
55
-            public void run() {
56
-                navigator().setDefaultOptions(defaultOptions);
57
-            }
58
-        });
51
+        handle(() -> navigator().setDefaultOptions(defaultOptions));
59
     }
52
     }
60
 
53
 
61
 	@ReactMethod
54
 	@ReactMethod
62
 	public void setOptions(final String onContainerId, final ReadableMap options) {
55
 	public void setOptions(final String onContainerId, final ReadableMap options) {
63
 		final NavigationOptions navOptions = NavigationOptions.parse(new TypefaceLoader(activity()), JSONParser.parse(options));
56
 		final NavigationOptions navOptions = NavigationOptions.parse(new TypefaceLoader(activity()), JSONParser.parse(options));
64
-		handle(new Runnable() {
65
-			@Override
66
-			public void run() {
67
-				navigator().setOptions(onContainerId, navOptions);
68
-			}
69
-		});
57
+		handle(() -> navigator().setOptions(onContainerId, navOptions));
70
 	}
58
 	}
71
 
59
 
72
 	@ReactMethod
60
 	@ReactMethod
73
 	public void push(final String onContainerId, final ReadableMap rawLayoutTree, final Promise promise) {
61
 	public void push(final String onContainerId, final ReadableMap rawLayoutTree, final Promise promise) {
74
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
62
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
75
-		handle(new Runnable() {
76
-			@Override
77
-			public void run() {
78
-				final ViewController viewController = newLayoutFactory().create(layoutTree);
79
-				navigator().push(onContainerId, viewController, promise);
80
-			}
81
-		});
63
+		handle(() -> {
64
+            final ViewController viewController = newLayoutFactory().create(layoutTree);
65
+            navigator().push(onContainerId, viewController, promise);
66
+        });
82
 	}
67
 	}
83
 
68
 
84
 	@ReactMethod
69
 	@ReactMethod
85
 	public void pop(final String onContainerId, final ReadableMap options, final Promise promise) {
70
 	public void pop(final String onContainerId, final ReadableMap options, final Promise promise) {
86
-		handle(new Runnable() {
87
-			@Override
88
-			public void run() {
89
-				navigator().popSpecific(onContainerId, promise);
90
-			}
91
-		});
71
+		handle(() -> navigator().popSpecific(onContainerId, promise));
92
 	}
72
 	}
93
 
73
 
94
 	@ReactMethod
74
 	@ReactMethod
95
 	public void popTo(final String containerId, final Promise promise) {
75
 	public void popTo(final String containerId, final Promise promise) {
96
-		handle(new Runnable() {
97
-			@Override
98
-			public void run() {
99
-				navigator().popTo(containerId, promise);
100
-			}
101
-		});
76
+		handle(() -> navigator().popTo(containerId, promise));
102
 	}
77
 	}
103
 
78
 
104
 	@ReactMethod
79
 	@ReactMethod
105
 	public void popToRoot(final String containerId, final Promise promise) {
80
 	public void popToRoot(final String containerId, final Promise promise) {
106
-		handle(new Runnable() {
107
-			@Override
108
-			public void run() {
109
-				navigator().popToRoot(containerId, promise);
110
-			}
111
-		});
81
+		handle(() -> navigator().popToRoot(containerId, promise));
112
 	}
82
 	}
113
 
83
 
114
 	@ReactMethod
84
 	@ReactMethod
115
 	public void showModal(final ReadableMap rawLayoutTree, final Promise promise) {
85
 	public void showModal(final ReadableMap rawLayoutTree, final Promise promise) {
116
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
86
 		final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(rawLayoutTree));
117
-		handle(new Runnable() {
118
-			@Override
119
-			public void run() {
120
-				final ViewController viewController = newLayoutFactory().create(layoutTree);
121
-				navigator().showModal(viewController, promise);
122
-			}
123
-		});
87
+		handle(() -> {
88
+            final ViewController viewController = newLayoutFactory().create(layoutTree);
89
+            navigator().showModal(viewController, promise);
90
+        });
124
 	}
91
 	}
125
 
92
 
126
 	@ReactMethod
93
 	@ReactMethod
127
 	public void dismissModal(final String containerId, final Promise promise) {
94
 	public void dismissModal(final String containerId, final Promise promise) {
128
-		handle(new Runnable() {
129
-			@Override
130
-			public void run() {
131
-				navigator().dismissModal(containerId, promise);
132
-			}
133
-		});
95
+		handle(() -> navigator().dismissModal(containerId, promise));
134
 	}
96
 	}
135
 
97
 
136
 	@ReactMethod
98
 	@ReactMethod
137
 	public void dismissAllModals(final Promise promise) {
99
 	public void dismissAllModals(final Promise promise) {
138
-		handle(new Runnable() {
139
-			@Override
140
-			public void run() {
141
-				navigator().dismissAllModals(promise);
142
-			}
143
-		});
100
+		handle(() -> navigator().dismissAllModals(promise));
144
 	}
101
 	}
145
 
102
 
146
 	@ReactMethod
103
 	@ReactMethod
147
 	public void showOverlay(final String type, final ReadableMap data, final Promise promise) {
104
 	public void showOverlay(final String type, final ReadableMap data, final Promise promise) {
148
 		if (OverlayFactory.Overlay.create(type) == OverlayFactory.Overlay.CustomDialog) {
105
 		if (OverlayFactory.Overlay.create(type) == OverlayFactory.Overlay.CustomDialog) {
149
 			final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(data));
106
 			final LayoutNode layoutTree = LayoutNodeParser.parse(JSONParser.parse(data));
150
-			handle(new Runnable() {
151
-				@Override
152
-				public void run() {
153
-					ViewController viewController = newLayoutFactory().create(layoutTree);
154
-					navigator().showOverlay(type, OverlayOptions.create(viewController), promise);
155
-				}
156
-			});
107
+			handle(() -> {
108
+                ViewController viewController = newLayoutFactory().create(layoutTree);
109
+                navigator().showOverlay(type, OverlayOptions.create(viewController), promise);
110
+            });
157
 		} else {
111
 		} else {
158
 			final OverlayOptions overlayOptions = OverlayOptions.parse(JSONParser.parse(data));
112
 			final OverlayOptions overlayOptions = OverlayOptions.parse(JSONParser.parse(data));
159
-			handle(new Runnable() {
160
-				@Override
161
-				public void run() {
162
-					navigator().showOverlay(type, overlayOptions, promise);
163
-				}
164
-			});
113
+			handle(() -> navigator().showOverlay(type, overlayOptions, promise));
165
 		}
114
 		}
166
 
115
 
167
 
116
 
169
 
118
 
170
 	@ReactMethod
119
 	@ReactMethod
171
 	public void dismissOverlay() {
120
 	public void dismissOverlay() {
172
-		handle(new Runnable() {
173
-			@Override
174
-			public void run() {
175
-				navigator().dismissOverlay();
176
-			}
177
-		});
121
+		handle(() -> navigator().dismissOverlay());
178
 	}
122
 	}
179
 
123
 
180
 	private NavigationActivity activity() {
124
 	private NavigationActivity activity() {

+ 1
- 1
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsAdapter.java View File

11
     private List<TopTabController> tabs;
11
     private List<TopTabController> tabs;
12
     private int currentPage = 0;
12
     private int currentPage = 0;
13
 
13
 
14
-    TopTabsAdapter(List<TopTabController> tabs) {
14
+    public TopTabsAdapter(List<TopTabController> tabs) {
15
         this.tabs = tabs;
15
         this.tabs = tabs;
16
     }
16
     }
17
 
17
 

+ 9
- 5
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/toptabs/TopTabsController.java View File

10
 import com.reactnativenavigation.viewcontrollers.ParentController;
10
 import com.reactnativenavigation.viewcontrollers.ParentController;
11
 import com.reactnativenavigation.viewcontrollers.ViewController;
11
 import com.reactnativenavigation.viewcontrollers.ViewController;
12
 import com.reactnativenavigation.views.TopTabsLayout;
12
 import com.reactnativenavigation.views.TopTabsLayout;
13
+import com.reactnativenavigation.views.TopTabsLayoutCreator;
13
 
14
 
14
 import java.util.Collection;
15
 import java.util.Collection;
15
 import java.util.List;
16
 import java.util.List;
18
 
19
 
19
     private List<TopTabController> tabs;
20
     private List<TopTabController> tabs;
20
     private TopTabsLayout topTabsLayout;
21
     private TopTabsLayout topTabsLayout;
21
-    private TopTabsAdapter adapter;
22
+    private TopTabsLayoutCreator viewCreator;
23
+    private NavigationOptions navigationOptions;
22
 
24
 
23
-    public TopTabsController(Activity activity, String id, List<TopTabController> tabs) {
25
+    public TopTabsController(Activity activity, String id, List<TopTabController> tabs, TopTabsLayoutCreator viewCreator, NavigationOptions navigationOptions) {
24
         super(activity, id);
26
         super(activity, id);
27
+        this.viewCreator = viewCreator;
28
+        this.navigationOptions = navigationOptions;
25
         this.tabs = tabs;
29
         this.tabs = tabs;
26
-        this.adapter = new TopTabsAdapter(tabs);
27
         for (ViewController tab : tabs) {
30
         for (ViewController tab : tabs) {
28
             tab.setParentController(this);
31
             tab.setParentController(this);
29
         }
32
         }
32
     @NonNull
35
     @NonNull
33
     @Override
36
     @Override
34
     protected ViewGroup createView() {
37
     protected ViewGroup createView() {
35
-        topTabsLayout = new TopTabsLayout(getActivity(), tabs, adapter);
38
+        topTabsLayout = viewCreator.create();
36
         return topTabsLayout;
39
         return topTabsLayout;
37
     }
40
     }
38
 
41
 
44
 
47
 
45
     @Override
48
     @Override
46
     public void onViewAppeared() {
49
     public void onViewAppeared() {
50
+        applyOptions(navigationOptions);
47
         performOnCurrentTab(TopTabController::onViewAppeared);
51
         performOnCurrentTab(TopTabController::onViewAppeared);
48
     }
52
     }
49
 
53
 
67
     }
71
     }
68
 
72
 
69
     private void performOnCurrentTab(Task<TopTabController> task) {
73
     private void performOnCurrentTab(Task<TopTabController> task) {
70
-        task.run(tabs.get(adapter.getCurrentItem()));
74
+        task.run(tabs.get(topTabsLayout.getCurrentItem()));
71
     }
75
     }
72
 }
76
 }

+ 5
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/TopBar.java View File

15
 
15
 
16
 import com.reactnativenavigation.parse.Button;
16
 import com.reactnativenavigation.parse.Button;
17
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsViewPager;
17
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsViewPager;
18
+import com.reactnativenavigation.views.style.Color;
18
 
19
 
19
 import java.util.ArrayList;
20
 import java.util.ArrayList;
20
 
21
 
62
         topTabs.setFontFamily(tabIndex, fontFamily);
63
         topTabs.setFontFamily(tabIndex, fontFamily);
63
     }
64
     }
64
 
65
 
66
+    public void applyTopTabsColors(Color selectedTabColor, Color unselectedTabColor) {
67
+        topTabs.applyTopTabsColors(selectedTabColor, unselectedTabColor);
68
+    }
69
+
65
 	public void setButtons(ArrayList<Button> leftButtons, ArrayList<Button> rightButtons) {
70
 	public void setButtons(ArrayList<Button> leftButtons, ArrayList<Button> rightButtons) {
66
 		setLeftButtons(leftButtons);
71
 		setLeftButtons(leftButtons);
67
 		setRightButtons(rightButtons);
72
 		setRightButtons(rightButtons);

+ 14
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabs.java View File

4
 import android.graphics.Typeface;
4
 import android.graphics.Typeface;
5
 import android.support.design.widget.TabLayout;
5
 import android.support.design.widget.TabLayout;
6
 
6
 
7
+import com.reactnativenavigation.views.style.Color;
8
+
7
 public class TopTabs extends TabLayout {
9
 public class TopTabs extends TabLayout {
8
     private final TopTabsStyleHelper styleHelper;
10
     private final TopTabsStyleHelper styleHelper;
9
 
11
 
15
     public void setFontFamily(int tabIndex, Typeface fontFamily) {
17
     public void setFontFamily(int tabIndex, Typeface fontFamily) {
16
         styleHelper.setFontFamily(tabIndex, fontFamily);
18
         styleHelper.setFontFamily(tabIndex, fontFamily);
17
     }
19
     }
20
+
21
+    public int[] getSelectedTabColors() {
22
+        return SELECTED_STATE_SET;
23
+    }
24
+
25
+    public int[] getDefaultTabColors() {
26
+        return EMPTY_STATE_SET;
27
+    }
28
+
29
+    public void applyTopTabsColors(Color selectedTabColor, Color unselectedTabColor) {
30
+        styleHelper.applyTopTabsColors(selectedTabColor, unselectedTabColor);
31
+    }
18
 }
32
 }

+ 4
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsLayout.java View File

60
     public void switchToTab(int index) {
60
     public void switchToTab(int index) {
61
         viewPager.setCurrentItem(index);
61
         viewPager.setCurrentItem(index);
62
     }
62
     }
63
+
64
+    public int getCurrentItem() {
65
+        return viewPager.getCurrentItem();
66
+    }
63
 }
67
 }

+ 22
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsLayoutCreator.java View File

1
+package com.reactnativenavigation.views;
2
+
3
+import android.content.Context;
4
+
5
+import com.reactnativenavigation.viewcontrollers.toptabs.TopTabController;
6
+import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
7
+
8
+import java.util.List;
9
+
10
+public class TopTabsLayoutCreator {
11
+    private Context context;
12
+    private List<TopTabController> tabs;
13
+
14
+    public TopTabsLayoutCreator(Context context, List<TopTabController> tabs) {
15
+        this.context = context;
16
+        this.tabs = tabs;
17
+    }
18
+
19
+    public TopTabsLayout create() {
20
+        return new TopTabsLayout(context, tabs, new TopTabsAdapter(tabs));
21
+    }
22
+}

+ 26
- 1
lib/android/app/src/main/java/com/reactnativenavigation/views/TopTabsStyleHelper.java View File

1
 package com.reactnativenavigation.views;
1
 package com.reactnativenavigation.views;
2
 
2
 
3
+import android.content.res.ColorStateList;
3
 import android.graphics.Typeface;
4
 import android.graphics.Typeface;
4
 import android.view.ViewGroup;
5
 import android.view.ViewGroup;
5
 import android.widget.TextView;
6
 import android.widget.TextView;
6
 
7
 
8
+import com.reactnativenavigation.utils.Task;
7
 import com.reactnativenavigation.utils.ViewUtils;
9
 import com.reactnativenavigation.utils.ViewUtils;
10
+import com.reactnativenavigation.views.style.Color;
8
 
11
 
9
 class TopTabsStyleHelper {
12
 class TopTabsStyleHelper {
10
     private TopTabs topTabs;
13
     private TopTabs topTabs;
13
         this.topTabs = topTabs;
16
         this.topTabs = topTabs;
14
     }
17
     }
15
 
18
 
19
+    void applyTopTabsColors(Color selected, Color unselected) {
20
+        if (!selected.hasColor() && !unselected.hasColor()) return;
21
+
22
+        ColorStateList originalColors = topTabs.getTabTextColors();
23
+        int selectedTabColor = originalColors != null ? originalColors.getColorForState(topTabs.getSelectedTabColors(), -1) : -1;
24
+        int tabTextColor = originalColors != null ? originalColors.getColorForState(topTabs.getDefaultTabColors(), -1) : -1;
25
+
26
+        if (selected.hasColor()) {
27
+            tabTextColor = selected.get();
28
+        }
29
+
30
+        if (unselected.hasColor()) {
31
+            selectedTabColor = unselected.get();
32
+        }
33
+
34
+        topTabs.setTabTextColors(tabTextColor, selectedTabColor);
35
+    }
36
+
16
     void setFontFamily(int tabIndex, Typeface fontFamily) {
37
     void setFontFamily(int tabIndex, Typeface fontFamily) {
38
+        applyOnTabTitle(tabIndex, (title) -> title.setTypeface(fontFamily));
39
+    }
40
+
41
+    private void applyOnTabTitle(int tabIndex, Task<TextView> action) {
17
         TextView title = ViewUtils.findChildByClass(getTabView(tabIndex), TextView.class);
42
         TextView title = ViewUtils.findChildByClass(getTabView(tabIndex), TextView.class);
18
         if (title != null) {
43
         if (title != null) {
19
-            title.setTypeface(fontFamily);
44
+            action.run(title);
20
         }
45
         }
21
     }
46
     }
22
 
47
 

+ 30
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/style/Color.java View File

1
+package com.reactnativenavigation.views.style;
2
+
3
+import android.support.annotation.ColorInt;
4
+
5
+public class Color {
6
+
7
+    private Integer color;
8
+
9
+    public Color(@ColorInt int color) {
10
+        this.color = color;
11
+    }
12
+
13
+    public boolean hasColor() {
14
+        return color != null;
15
+    }
16
+
17
+    @ColorInt
18
+    public int get() {
19
+        if (hasColor()) {
20
+            return color;
21
+        }
22
+        throw new RuntimeException("Tried to get null color!");
23
+    }
24
+
25
+    @SuppressWarnings("MagicNumber")
26
+    @Override
27
+    public String toString() {
28
+        return String.format("#%06X", (0xFFFFFF & get()));
29
+    }
30
+}

+ 18
- 0
lib/android/app/src/main/java/com/reactnativenavigation/views/style/NullColor.java View File

1
+package com.reactnativenavigation.views.style;
2
+
3
+public class NullColor extends Color {
4
+
5
+    public NullColor() {
6
+        super(0);
7
+    }
8
+
9
+    public boolean hasColor() {
10
+        return false;
11
+    }
12
+
13
+    @SuppressWarnings("MagicNumber")
14
+    @Override
15
+    public String toString() {
16
+        return "Null Color";
17
+    }
18
+}

+ 18
- 1
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopTabsViewControllerTest.java View File

8
 import com.reactnativenavigation.parse.NavigationOptions;
8
 import com.reactnativenavigation.parse.NavigationOptions;
9
 import com.reactnativenavigation.viewcontrollers.ContainerViewController.IReactView;
9
 import com.reactnativenavigation.viewcontrollers.ContainerViewController.IReactView;
10
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabController;
10
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabController;
11
+import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsAdapter;
11
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
12
 import com.reactnativenavigation.viewcontrollers.toptabs.TopTabsController;
12
 import com.reactnativenavigation.views.TopTabsLayout;
13
 import com.reactnativenavigation.views.TopTabsLayout;
14
+import com.reactnativenavigation.views.TopTabsLayoutCreator;
13
 
15
 
14
 import org.junit.Test;
16
 import org.junit.Test;
17
+import org.mockito.Mockito;
15
 
18
 
16
 import java.util.ArrayList;
19
 import java.util.ArrayList;
17
 import java.util.List;
20
 import java.util.List;
28
     private List<TopTabLayoutMock> tabs = new ArrayList<>(SIZE);
31
     private List<TopTabLayoutMock> tabs = new ArrayList<>(SIZE);
29
     private List<TopTabController> tabControllers = new ArrayList<>(SIZE);
32
     private List<TopTabController> tabControllers = new ArrayList<>(SIZE);
30
     private List<NavigationOptions> tabOptions = new ArrayList<>(SIZE);
33
     private List<NavigationOptions> tabOptions = new ArrayList<>(SIZE);
34
+    private NavigationOptions navigationOptions;
35
+    private TopTabsLayout topTabsLayout;
31
 
36
 
32
     @Override
37
     @Override
33
     public void beforeEach() {
38
     public void beforeEach() {
36
         tabs.clear();
41
         tabs.clear();
37
         Activity activity = newActivity();
42
         Activity activity = newActivity();
38
         createTabs(activity);
43
         createTabs(activity);
39
-        uut = new TopTabsController(activity, "containerId", tabControllers);
44
+        navigationOptions = new NavigationOptions();
45
+        topTabsLayout = spy(new TopTabsLayout(activity, tabControllers, new TopTabsAdapter(tabControllers)));
46
+
47
+        TopTabsLayoutCreator layoutCreator = Mockito.mock(TopTabsLayoutCreator.class);
48
+        Mockito.when(layoutCreator.create()).thenReturn(topTabsLayout);
49
+        uut = new TopTabsController(activity, "containerId", tabControllers, layoutCreator, navigationOptions);
40
     }
50
     }
41
 
51
 
42
     private void createTabs(Activity activity) {
52
     private void createTabs(Activity activity) {
116
         verify(tabControllers.get(0), times(2)).applyOptions(tabOptions.get(0));
126
         verify(tabControllers.get(0), times(2)).applyOptions(tabOptions.get(0));
117
     }
127
     }
118
 
128
 
129
+    @Test
130
+    public void appliesOptionsOnLayoutWhenVisible() throws Exception {
131
+        uut.ensureViewIsCreated();
132
+        uut.onViewAppeared();
133
+        verify(topTabsLayout, times(1)).applyOptions(navigationOptions);
134
+    }
135
+
119
     private IReactView tab(TopTabsLayout topTabs, final int index) {
136
     private IReactView tab(TopTabsLayout topTabs, final int index) {
120
         return (IReactView) ((ViewGroup) topTabs.getViewPager().getChildAt(index)).getChildAt(0);
137
         return (IReactView) ((ViewGroup) topTabs.getViewPager().getChildAt(index)).getChildAt(0);
121
     }
138
     }

+ 4
- 3
lib/src/Navigation.js View File

10
 const PublicEventsRegistry = require('./events/PublicEventsRegistry');
10
 const PublicEventsRegistry = require('./events/PublicEventsRegistry');
11
 const Element = require('./adapters/Element');
11
 const Element = require('./adapters/Element');
12
 const Root = require('./params/containers/Root');
12
 const Root = require('./params/containers/Root');
13
+const Container = require('./params/containers/Container');
13
 const NavigationOptions = require('./params/options/NavigationOptions');
14
 const NavigationOptions = require('./params/options/NavigationOptions');
14
 
15
 
15
 /** @constructor */
16
 /** @constructor */
89
   /**
90
   /**
90
    * Push a new screen into this screen's navigation stack.
91
    * Push a new screen into this screen's navigation stack.
91
    * @param {string} containerId The container's id.
92
    * @param {string} containerId The container's id.
92
-   * @param {*} params
93
+   * @param {Container} container
93
    */
94
    */
94
-  push(containerId, params) {
95
-    return this.commands.push(containerId, params);
95
+  push(containerId, container) {
96
+    return this.commands.push(containerId, new Container(container));
96
   }
97
   }
97
 
98
 
98
   /**
99
   /**

+ 1
- 0
lib/src/commands/Commands.js View File

44
 
44
 
45
   push(onContainerId, containerData) {
45
   push(onContainerId, containerData) {
46
     const input = _.cloneDeep(containerData);
46
     const input = _.cloneDeep(containerData);
47
+    OptionsProcessor.processOptions(input);
47
     const layout = this.layoutTreeParser.parseFromSimpleJSON(input);
48
     const layout = this.layoutTreeParser.parseFromSimpleJSON(input);
48
     this.layoutTreeCrawler.crawl(layout);
49
     this.layoutTreeCrawler.crawl(layout);
49
     return this.nativeCommandsSender.push(onContainerId, layout);
50
     return this.nativeCommandsSender.push(onContainerId, layout);

+ 4
- 3
lib/src/commands/LayoutTreeParser.js View File

13
       return this._createTabs(simpleJsonApi.bottomTabs);
13
       return this._createTabs(simpleJsonApi.bottomTabs);
14
     }
14
     }
15
     if (simpleJsonApi.topTabs) {
15
     if (simpleJsonApi.topTabs) {
16
-      return this._createTopTabs(simpleJsonApi.topTabs);
16
+      return this._createTopTabs(simpleJsonApi);
17
     }
17
     }
18
     if (simpleJsonApi.name) {
18
     if (simpleJsonApi.name) {
19
       return this._createContainer(simpleJsonApi);
19
       return this._createContainer(simpleJsonApi);
36
     };
36
     };
37
   }
37
   }
38
 
38
 
39
-  _createTopTabs(topTabs) {
39
+  _createTopTabs(node) {
40
     return {
40
     return {
41
       type: LayoutTypes.TopTabs,
41
       type: LayoutTypes.TopTabs,
42
-      children: _.map(topTabs, (t) => this._createTopTab(t))
42
+      data: _.pick(node, 'navigationOptions'),
43
+      children: _.map(node.topTabs, (t) => this._createTopTab(t))
43
     };
44
     };
44
   }
45
   }
45
 
46
 

+ 2
- 1
lib/src/commands/LayoutTreeParser.test.js View File

312
               },
312
               },
313
               children: []
313
               children: []
314
             }
314
             }
315
-          ]
315
+          ],
316
+          data: {}
316
         });
317
         });
317
     });
318
     });
318
   });
319
   });

+ 8
- 4
lib/src/params/containers/Container.js View File

1
+const NavigationOptions = require('./../options/NavigationOptions');
2
+
1
 class Container {
3
 class Container {
2
   /**
4
   /**
3
    * @property {string} name The container's registered name
5
    * @property {string} name The container's registered name
6
+   * @property {Container[]} [topTabs]
4
    * @property {object} [passProps] props
7
    * @property {object} [passProps] props
5
    * @property {NavigationOptions} navigationOptions
8
    * @property {NavigationOptions} navigationOptions
6
    */
9
    */
7
   constructor(params) {
10
   constructor(params) {
8
-    if (!params || !params.name) {
9
-      throw new Error('Container name is undefined');
10
-    }
11
     this.name = params.name;
11
     this.name = params.name;
12
+    if (params.topTabs) {
13
+      params.topTabs.map((t) => new Container(t));
14
+      this.topTabs = params.topTabs;
15
+    }
12
     this.passProps = params.passProps;
16
     this.passProps = params.passProps;
13
-    this.navigationOptions = params.navigationOptions;
17
+    this.navigationOptions = params.navigationOptions && new NavigationOptions(params.navigationOptions);
14
   }
18
   }
15
 }
19
 }
16
 
20
 

+ 12
- 3
lib/src/params/containers/Container.test.js View File

5
 };
5
 };
6
 const CONTAINER = {
6
 const CONTAINER = {
7
   name: 'myScreen',
7
   name: 'myScreen',
8
-  passProps: PASS_PROPS
8
+  passProps: PASS_PROPS,
9
+  navigationOptions: {}
10
+};
11
+const TOP_TABS_CONTAINER = {
12
+  topTabs: [
13
+    CONTAINER,
14
+    CONTAINER
15
+  ]
9
 };
16
 };
10
 
17
 
11
 describe('ContainerRegistry', () => {
18
 describe('ContainerRegistry', () => {
13
     const uut = new Container(CONTAINER);
20
     const uut = new Container(CONTAINER);
14
     expect(uut.name).toBe('myScreen');
21
     expect(uut.name).toBe('myScreen');
15
     expect(uut.passProps).toEqual(PASS_PROPS);
22
     expect(uut.passProps).toEqual(PASS_PROPS);
23
+    expect(uut.navigationOptions).toEqual({});
16
   });
24
   });
17
 
25
 
18
-  it('throws if container name is missing', () => {
19
-    expect(() => new Container()).toThrowError('Container name is undefined');
26
+  it('parses TopTabs container', () => {
27
+    const uut = new Container(TOP_TABS_CONTAINER);
28
+    expect(uut.topTabs).toEqual([CONTAINER, CONTAINER]);
20
   });
29
   });
21
 });
30
 });

+ 3
- 0
lib/src/params/options/NavigationOptions.js View File

2
 const BottomTabs = require('./BottomTabs');
2
 const BottomTabs = require('./BottomTabs');
3
 const Button = require('./Button');
3
 const Button = require('./Button');
4
 const BottomTab = require('./BottomTab');
4
 const BottomTab = require('./BottomTab');
5
+const TopTabs = require('./TopTabs');
5
 
6
 
6
 class NavigationOptions {
7
 class NavigationOptions {
7
   /**
8
   /**
9
    * @property {options:BottomTabs} [bottomTabs]
10
    * @property {options:BottomTabs} [bottomTabs]
10
    * @property {options:BottomTab} [bottomTab]
11
    * @property {options:BottomTab} [bottomTab]
11
    * @property {string} [orientation]
12
    * @property {string} [orientation]
13
+   * @property {options:TopTabs} [topTabs]
12
    * @property {options:Button[]} [rightButtons]
14
    * @property {options:Button[]} [rightButtons]
13
    * @property {options:Button[]} [leftButtons]
15
    * @property {options:Button[]} [leftButtons]
14
    */
16
    */
23
     this.backgroundImage = options.backgroundImage;
25
     this.backgroundImage = options.backgroundImage;
24
     this.rootBackgroundImage = options.rootBackgroundImage;
26
     this.rootBackgroundImage = options.rootBackgroundImage;
25
     this.screenBackgroundColor = options.screenBackgroundColor;
27
     this.screenBackgroundColor = options.screenBackgroundColor;
28
+    this.topTabs = options.topTabs && new TopTabs(options.topTabs);
26
   }
29
   }
27
 }
30
 }
28
 
31
 

+ 8
- 1
lib/src/params/options/NavigationOptions.test.js View File

2
 const BottomTabs = require('./BottomTabs');
2
 const BottomTabs = require('./BottomTabs');
3
 const TopBar = require('./TopBar');
3
 const TopBar = require('./TopBar');
4
 const BottomTab = require('./BottomTab');
4
 const BottomTab = require('./BottomTab');
5
+const TopTabs = require('./TopTabs');
5
 
6
 
6
 const TAB_BAR = {};
7
 const TAB_BAR = {};
7
 const TOP_BAR = {};
8
 const TOP_BAR = {};
22
     buttonColor: 'red'
23
     buttonColor: 'red'
23
   }
24
   }
24
 ];
25
 ];
26
+const TOP_TABS = {
27
+  selectedTabColor: 'blue',
28
+  unselectedTabColor: 'red'
29
+};
25
 const NAVIGATION_OPTIONS = {
30
 const NAVIGATION_OPTIONS = {
26
   topBar: TOP_BAR,
31
   topBar: TOP_BAR,
27
   bottomTabs: TAB_BAR,
32
   bottomTabs: TAB_BAR,
28
   bottomTab: TAB_ITEM,
33
   bottomTab: TAB_ITEM,
29
   orientation: 'portrait',
34
   orientation: 'portrait',
30
   rightButtons: RIGHT_BUTTONS,
35
   rightButtons: RIGHT_BUTTONS,
31
-  leftButtons: LEFT_BUTTONS
36
+  leftButtons: LEFT_BUTTONS,
37
+  topTabs: TOP_TABS
32
 };
38
 };
33
 
39
 
34
 describe('NavigationOptions', () => {
40
 describe('NavigationOptions', () => {
40
     expect(uut.orientation).toEqual('portrait');
46
     expect(uut.orientation).toEqual('portrait');
41
     expect(uut.rightButtons).toEqual(RIGHT_BUTTONS);
47
     expect(uut.rightButtons).toEqual(RIGHT_BUTTONS);
42
     expect(uut.leftButtons).toEqual(LEFT_BUTTONS);
48
     expect(uut.leftButtons).toEqual(LEFT_BUTTONS);
49
+    expect(uut.topTabs).toBeInstanceOf(TopTabs);
43
   });
50
   });
44
 });
51
 });

+ 12
- 0
lib/src/params/options/TopTabs.js View File

1
+class TopTabs {
2
+  /**
3
+   * @property {string} [selectedTabColor] Selected tab color
4
+   * @property {string} unselectedTabColor Unselected tab color
5
+   */
6
+  constructor(topTabs) {
7
+    this.selectedTabColor = topTabs.selectedTabColor;
8
+    this.unselectedTabColor = topTabs.unselectedTabColor;
9
+  }
10
+}
11
+
12
+module.exports = TopTabs;

+ 14
- 0
lib/src/params/options/TopTabs.test.js View File

1
+const TopTabs = require('./TopTabs');
2
+
3
+const TOP_TABS = {
4
+  selectedTabColor: 'red',
5
+  unselectedTabColor: 'blue'
6
+};
7
+
8
+describe('TopTabs', () => {
9
+  it('Parses TopTabs', () => {
10
+    const uut = new TopTabs(TOP_TABS);
11
+    expect(uut.selectedTabColor).toEqual('red');
12
+    expect(uut.unselectedTabColor).toEqual('blue');
13
+  });
14
+});

+ 7
- 1
playground/src/containers/WelcomeScreen.js View File

199
             }
199
             }
200
           }
200
           }
201
         }
201
         }
202
-      ]
202
+      ],
203
+      navigationOptions: {
204
+        topTabs: {
205
+          selectedTabColor: '#12766b',
206
+          unselectedTabColor: 'red'
207
+        }
208
+      }
203
     });
209
     });
204
   }
210
   }
205
 
211