Browse Source

V2 custom component refactor (#2997)

* pass props for topBar background component

* refactored custom components

* pass props for topBar background component

* refactored custom components

* refactored RNNOptions object

* Use margin to control component position relative to TopBar

RelativeLayout rules caused excessive CPU usage due to issues with RN's
keyboard detection mechanism (based on global layout listener)

* Skip custom transition e2e on Android

* Improved the clarity of the top-level API doc (#2984)

I clarified some of the language here so that it is easier for the beginner to understand. (It was a little bit awkward before)

* Increase gradle daemon process memory

* Disable Orientation test on Android

* Fix SideMenu e2e

* Fix popTo

* Use margin instead of RelativeLayout rules to align component to BottomTabs

Rules caused excessive CPU usage due to RN issue with global layout listener
and keyboard visibility detection.

* Update README.md

for triggering build

* Update README.md

* Consolidate pop and animatePop

* Update top-level-api.md ... more copyediting. (#3002)

Further clarification of some of the top level API stuff.

* popTo animates top screen

* Refactor component options on Android

* Stop processing passProps from options

* removed comments
Yogev B 6 years ago
parent
commit
796397e8e4
39 changed files with 283 additions and 118 deletions
  1. 40
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/Component.java
  2. 4
    23
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TitleOptions.java
  3. 5
    10
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarBackgroundOptions.java
  4. 13
    0
      lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarOptions.java
  5. 3
    2
      lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java
  6. 2
    2
      lib/android/app/src/main/java/com/reactnativenavigation/presentation/OptionsPresenter.java
  7. 5
    4
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarReactViewController.java
  8. 1
    1
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TopBarButtonController.java
  9. 7
    6
      lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarBackgroundViewController.java
  10. 7
    5
      lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBar.java
  11. 5
    5
      lib/android/app/src/main/java/com/reactnativenavigation/views/topbar/TopBar.java
  12. 1
    4
      lib/android/app/src/test/java/com/reactnativenavigation/parse/OptionsTest.java
  13. 1
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsApplyingTest.java
  14. 3
    3
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsMergingTest.java
  15. 20
    9
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TitleBarTest.java
  16. 2
    1
      lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TopBarButtonControllerTest.java
  17. 13
    3
      lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarBackgroundComponentTest.java
  18. 2
    1
      lib/ios/RNNBackgroundOptions.h
  19. 9
    0
      lib/ios/RNNComponentOptions.h
  20. 5
    0
      lib/ios/RNNComponentOptions.m
  21. 1
    1
      lib/ios/RNNNavigationButtons.m
  22. 1
    1
      lib/ios/RNNNavigationOptions.m
  23. 28
    1
      lib/ios/RNNOptions.m
  24. 4
    0
      lib/ios/RNNReactRootViewCreator.m
  25. 7
    8
      lib/ios/RNNRootViewController.m
  26. 3
    1
      lib/ios/RNNRootViewCreator.h
  27. 2
    1
      lib/ios/RNNTitleOptions.h
  28. 2
    1
      lib/ios/RNNTopBarOptions.h
  29. 1
    3
      lib/ios/RNNTopBarOptions.m
  30. 1
    1
      lib/ios/RNNTransitionsOptions.m
  31. 8
    0
      lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj
  32. 1
    1
      lib/src/commands/Commands.test.ts
  33. 2
    6
      lib/src/commands/Commands.ts
  34. 7
    2
      lib/src/commands/LayoutTreeCrawler.ts
  35. 1
    1
      lib/src/commands/LayoutType.test.ts
  36. 38
    1
      lib/src/commands/OptionsProcessor.test.ts
  37. 13
    2
      lib/src/commands/OptionsProcessor.ts
  38. 13
    4
      playground/src/screens/OptionsScreen.js
  39. 2
    3
      playground/src/screens/TopBarBackground.js

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

1
+package com.reactnativenavigation.parse;
2
+
3
+import com.reactnativenavigation.parse.params.NullText;
4
+import com.reactnativenavigation.parse.params.Text;
5
+import com.reactnativenavigation.parse.parsers.TextParser;
6
+
7
+import org.json.JSONObject;
8
+
9
+public class Component {
10
+    public static Component parse(JSONObject json) {
11
+        Component result = new Component();
12
+        if (json == null) return result;
13
+
14
+        result.name = TextParser.parse(json, "name");
15
+        result.componentId = TextParser.parse(json, "componentId");
16
+        result.alignment = Alignment.fromString(TextParser.parse(json, "alignment").get(""));
17
+
18
+        return result;
19
+    }
20
+
21
+    public Text name = new NullText();
22
+    public Text componentId = new NullText();
23
+    public Alignment alignment = Alignment.Default;
24
+
25
+    void mergeWith(Component other) {
26
+        if (other.componentId.hasValue()) componentId = other.componentId;
27
+        if (other.name.hasValue()) name = other.name;
28
+        if (other.alignment != Alignment.Default) alignment = other.alignment;
29
+    }
30
+
31
+    public void mergeWithDefault(Component defaultOptions) {
32
+        if (!componentId.hasValue()) componentId = defaultOptions.componentId;
33
+        if (!name.hasValue()) name = defaultOptions.name;
34
+        if (alignment == Alignment.Default) alignment = defaultOptions.alignment;
35
+    }
36
+
37
+    public boolean hasValue() {
38
+        return name.hasValue();
39
+    }
40
+}

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

2
 
2
 
3
 import android.graphics.Typeface;
3
 import android.graphics.Typeface;
4
 import android.support.annotation.Nullable;
4
 import android.support.annotation.Nullable;
5
-import android.util.Log;
6
 
5
 
7
-import com.reactnativenavigation.BuildConfig;
8
 import com.reactnativenavigation.parse.params.Color;
6
 import com.reactnativenavigation.parse.params.Color;
9
 import com.reactnativenavigation.parse.params.Fraction;
7
 import com.reactnativenavigation.parse.params.Fraction;
10
 import com.reactnativenavigation.parse.params.NullColor;
8
 import com.reactnativenavigation.parse.params.NullColor;
22
 
20
 
23
     public static TitleOptions parse(TypefaceLoader typefaceManager, JSONObject json) {
21
     public static TitleOptions parse(TypefaceLoader typefaceManager, JSONObject json) {
24
         final TitleOptions options = new TitleOptions();
22
         final TitleOptions options = new TitleOptions();
25
-        if (json == null) {
26
-            return options;
27
-        }
23
+        if (json == null) return options;
28
 
24
 
25
+        options.component = Component.parse(json.optJSONObject("component"));
29
         options.text = TextParser.parse(json, "text");
26
         options.text = TextParser.parse(json, "text");
30
         options.color = ColorParser.parse(json, "color");
27
         options.color = ColorParser.parse(json, "color");
31
         options.fontSize = FractionParser.parse(json, "fontSize");
28
         options.fontSize = FractionParser.parse(json, "fontSize");
32
         options.fontFamily = typefaceManager.getTypeFace(json.optString("fontFamily", ""));
29
         options.fontFamily = typefaceManager.getTypeFace(json.optString("fontFamily", ""));
33
         options.alignment = Alignment.fromString(TextParser.parse(json, "alignment").get(""));
30
         options.alignment = Alignment.fromString(TextParser.parse(json, "alignment").get(""));
34
-        options.component = TextParser.parse(json, "component");
35
-        options.componentAlignment = Alignment.fromString(TextParser.parse(json, "componentAlignment").get(""));
36
-
37
-        validate(options);
38
 
31
 
39
         return options;
32
         return options;
40
     }
33
     }
44
     public Fraction fontSize = new NullFraction();
37
     public Fraction fontSize = new NullFraction();
45
     public Alignment alignment = Alignment.Default;
38
     public Alignment alignment = Alignment.Default;
46
     @Nullable public Typeface fontFamily;
39
     @Nullable public Typeface fontFamily;
47
-    public Text component = new NullText();
48
-    public Alignment componentAlignment = Alignment.Default;
40
+    public Component component = new Component();
49
 
41
 
50
     void mergeWith(final TitleOptions other) {
42
     void mergeWith(final TitleOptions other) {
51
         if (other.text.hasValue()) text = other.text;
43
         if (other.text.hasValue()) text = other.text;
54
         if (other.fontFamily != null) fontFamily = other.fontFamily;
46
         if (other.fontFamily != null) fontFamily = other.fontFamily;
55
         if (other.alignment != Alignment.Default) alignment = other.alignment;
47
         if (other.alignment != Alignment.Default) alignment = other.alignment;
56
         if (other.component.hasValue()) component = other.component;
48
         if (other.component.hasValue()) component = other.component;
57
-        if (other.componentAlignment != Alignment.Default) componentAlignment = other.componentAlignment;
58
-        validate(this);
59
     }
49
     }
60
 
50
 
61
     void mergeWithDefault(TitleOptions defaultOptions) {
51
     void mergeWithDefault(TitleOptions defaultOptions) {
64
         if (!fontSize.hasValue()) fontSize = defaultOptions.fontSize;
54
         if (!fontSize.hasValue()) fontSize = defaultOptions.fontSize;
65
         if (fontFamily == null) fontFamily = defaultOptions.fontFamily;
55
         if (fontFamily == null) fontFamily = defaultOptions.fontFamily;
66
         if (alignment == Alignment.Default) alignment = defaultOptions.alignment;
56
         if (alignment == Alignment.Default) alignment = defaultOptions.alignment;
67
-        if (!component.hasValue()) component = defaultOptions.component;
68
-        if (componentAlignment == Alignment.Default) componentAlignment = defaultOptions.componentAlignment;
69
-        validate(this);
70
-    }
71
-
72
-    private static void validate(TitleOptions options) {
73
-        if (options.component.hasValue() && options.text.hasValue()) {
74
-            if (BuildConfig.DEBUG) Log.w("RNN", "A screen can't use both text and component - clearing text.");
75
-            options.text = new NullText();
76
-        }
57
+        component.mergeWithDefault(defaultOptions.component);
77
     }
58
     }
78
 }
59
 }

+ 5
- 10
lib/android/app/src/main/java/com/reactnativenavigation/parse/TopBarBackgroundOptions.java View File

2
 
2
 
3
 import com.reactnativenavigation.parse.params.Color;
3
 import com.reactnativenavigation.parse.params.Color;
4
 import com.reactnativenavigation.parse.params.NullColor;
4
 import com.reactnativenavigation.parse.params.NullColor;
5
-import com.reactnativenavigation.parse.params.NullText;
6
-import com.reactnativenavigation.parse.params.Text;
7
 import com.reactnativenavigation.parse.parsers.ColorParser;
5
 import com.reactnativenavigation.parse.parsers.ColorParser;
8
-import com.reactnativenavigation.parse.parsers.TextParser;
9
 
6
 
10
 import org.json.JSONObject;
7
 import org.json.JSONObject;
11
 
8
 
12
 public class TopBarBackgroundOptions {
9
 public class TopBarBackgroundOptions {
13
     public static TopBarBackgroundOptions parse(JSONObject json) {
10
     public static TopBarBackgroundOptions parse(JSONObject json) {
14
         TopBarBackgroundOptions options = new TopBarBackgroundOptions();
11
         TopBarBackgroundOptions options = new TopBarBackgroundOptions();
15
-        if (json == null) {
16
-            return options;
17
-        }
12
+        if (json == null) return options;
18
 
13
 
19
         options.color = ColorParser.parse(json, "color");
14
         options.color = ColorParser.parse(json, "color");
20
-        options.component = TextParser.parse(json, "component");
15
+        options.component = Component.parse(json.optJSONObject("component"));
21
 
16
 
22
         if (options.component.hasValue()) {
17
         if (options.component.hasValue()) {
23
             options.color = new Color(android.graphics.Color.TRANSPARENT);
18
             options.color = new Color(android.graphics.Color.TRANSPARENT);
27
     }
22
     }
28
 
23
 
29
     public Color color = new NullColor();
24
     public Color color = new NullColor();
30
-    public Text component = new NullText();
25
+    public Component component = new Component();
31
 
26
 
32
     void mergeWith(final TopBarBackgroundOptions other) {
27
     void mergeWith(final TopBarBackgroundOptions other) {
33
         if (other.color.hasValue()) color = other.color;
28
         if (other.color.hasValue()) color = other.color;
34
-        if (other.component.hasValue()) component = other.component;
29
+        component.mergeWith(other.component);
35
     }
30
     }
36
 
31
 
37
     void mergeWithDefault(TopBarBackgroundOptions defaultOptions) {
32
     void mergeWithDefault(TopBarBackgroundOptions defaultOptions) {
38
         if (!color.hasValue()) color = defaultOptions.color;
33
         if (!color.hasValue()) color = defaultOptions.color;
39
-        if (!component.hasValue()) component = defaultOptions.component;
34
+        component.mergeWithDefault(defaultOptions.component);
40
     }
35
     }
41
 }
36
 }

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

2
 
2
 
3
 
3
 
4
 import android.support.annotation.Nullable;
4
 import android.support.annotation.Nullable;
5
+import android.util.Log;
5
 
6
 
7
+import com.reactnativenavigation.BuildConfig;
6
 import com.reactnativenavigation.parse.params.Bool;
8
 import com.reactnativenavigation.parse.params.Bool;
7
 import com.reactnativenavigation.parse.params.Button;
9
 import com.reactnativenavigation.parse.params.Button;
8
 import com.reactnativenavigation.parse.params.NullBool;
10
 import com.reactnativenavigation.parse.params.NullBool;
33
         options.leftButtons = Button.parseJsonArray(json.optJSONArray("leftButtons"));
35
         options.leftButtons = Button.parseJsonArray(json.optJSONArray("leftButtons"));
34
         options.testId = TextParser.parse(json, "testID");
36
         options.testId = TextParser.parse(json, "testID");
35
 
37
 
38
+        options.validate();
36
         return options;
39
         return options;
37
     }
40
     }
38
 
41
 
58
         if (other.drawBehind.hasValue()) drawBehind = other.drawBehind;
61
         if (other.drawBehind.hasValue()) drawBehind = other.drawBehind;
59
         if (other.leftButtons != null) leftButtons = other.leftButtons;
62
         if (other.leftButtons != null) leftButtons = other.leftButtons;
60
         if (other.rightButtons != null) rightButtons = other.rightButtons;
63
         if (other.rightButtons != null) rightButtons = other.rightButtons;
64
+        validate();
61
     }
65
     }
62
 
66
 
63
     void mergeWithDefault(TopBarOptions defaultOptions) {
67
     void mergeWithDefault(TopBarOptions defaultOptions) {
71
         if (leftButtons == null) leftButtons = defaultOptions.leftButtons;
75
         if (leftButtons == null) leftButtons = defaultOptions.leftButtons;
72
         if (rightButtons == null) rightButtons = defaultOptions.rightButtons;
76
         if (rightButtons == null) rightButtons = defaultOptions.rightButtons;
73
         if (!testId.hasValue()) testId = defaultOptions.testId;
77
         if (!testId.hasValue()) testId = defaultOptions.testId;
78
+        validate();
79
+    }
80
+
81
+    private void validate() {
82
+        if (title.component.hasValue() && (title.text.hasValue() || subtitle.text.hasValue())) {
83
+            if (BuildConfig.DEBUG) Log.w("RNN", "A screen can't use both text and component - clearing text.");
84
+            title.text = new NullText();
85
+            subtitle.text = new NullText();
86
+        }
74
     }
87
     }
75
 }
88
 }

+ 3
- 2
lib/android/app/src/main/java/com/reactnativenavigation/parse/params/Button.java View File

2
 
2
 
3
 import android.view.MenuItem;
3
 import android.view.MenuItem;
4
 
4
 
5
+import com.reactnativenavigation.parse.Component;
5
 import com.reactnativenavigation.parse.parsers.BoolParser;
6
 import com.reactnativenavigation.parse.parsers.BoolParser;
6
 import com.reactnativenavigation.parse.parsers.ColorParser;
7
 import com.reactnativenavigation.parse.parsers.ColorParser;
7
 import com.reactnativenavigation.parse.parsers.NumberParser;
8
 import com.reactnativenavigation.parse.parsers.NumberParser;
23
     private Text buttonFontWeight = new NullText();
24
     private Text buttonFontWeight = new NullText();
24
     public Text icon = new NullText();
25
     public Text icon = new NullText();
25
     public Text testId = new NullText();
26
     public Text testId = new NullText();
26
-    public Text component = new NullText();
27
+    public Component component = new Component();
27
 
28
 
28
     private static Button parseJson(JSONObject json) {
29
     private static Button parseJson(JSONObject json) {
29
         Button button = new Button();
30
         Button button = new Button();
36
         button.buttonFontSize = NumberParser.parse(json, "buttonFontSize");
37
         button.buttonFontSize = NumberParser.parse(json, "buttonFontSize");
37
         button.buttonFontWeight = TextParser.parse(json, "buttonFontWeight");
38
         button.buttonFontWeight = TextParser.parse(json, "buttonFontWeight");
38
         button.testId = TextParser.parse(json, "testID");
39
         button.testId = TextParser.parse(json, "testID");
39
-        button.component = TextParser.parse(json, "component");
40
+        button.component = Component.parse(json.optJSONObject("component"));
40
 
41
 
41
         if (json.has("icon")) {
42
         if (json.has("icon")) {
42
             button.icon = TextParser.parse(json.optJSONObject("icon"), "uri");
43
             button.icon = TextParser.parse(json.optJSONObject("icon"), "uri");

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

47
 
47
 
48
     private void applyTopBarOptions(TopBarOptions options, AnimationsOptions animationOptions, Component component) {
48
     private void applyTopBarOptions(TopBarOptions options, AnimationsOptions animationOptions, Component component) {
49
         topBar.setTitle(options.title.text.get(""));
49
         topBar.setTitle(options.title.text.get(""));
50
-        if (options.title.component.hasValue()) topBar.setTitleComponent(options.title.component.get(), options.title.componentAlignment);
50
+        if (options.title.component.hasValue()) topBar.setTitleComponent(options.title.component);
51
         topBar.setTitleFontSize(options.title.fontSize.get(defaultTitleFontSize));
51
         topBar.setTitleFontSize(options.title.fontSize.get(defaultTitleFontSize));
52
         topBar.setTitleTextColor(options.title.color.get(DEFAULT_TITLE_COLOR));
52
         topBar.setTitleTextColor(options.title.color.get(DEFAULT_TITLE_COLOR));
53
         topBar.setTitleTypeface(options.title.fontFamily);
53
         topBar.setTitleTypeface(options.title.fontFamily);
138
 
138
 
139
     private void mergeTopBarOptions(TopBarOptions options, AnimationsOptions animationsOptions, Component component) {
139
     private void mergeTopBarOptions(TopBarOptions options, AnimationsOptions animationsOptions, Component component) {
140
         if (options.title.text.hasValue()) topBar.setTitle(options.title.text.get());
140
         if (options.title.text.hasValue()) topBar.setTitle(options.title.text.get());
141
-        if (options.title.component.hasValue()) topBar.setTitleComponent(options.title.component.get(), options.title.componentAlignment);
141
+        if (options.title.component.hasValue()) topBar.setTitleComponent(options.title.component);
142
         if (options.title.color.hasValue()) topBar.setTitleTextColor(options.title.color.get());
142
         if (options.title.color.hasValue()) topBar.setTitleTextColor(options.title.color.get());
143
         if (options.title.fontSize.hasValue()) topBar.setTitleFontSize(options.title.fontSize.get());
143
         if (options.title.fontSize.hasValue()) topBar.setTitleFontSize(options.title.fontSize.get());
144
         if (options.title.fontFamily != null) topBar.setTitleTypeface(options.title.fontFamily);
144
         if (options.title.fontFamily != null) topBar.setTitleTypeface(options.title.fontFamily);

+ 5
- 4
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/TitleBarReactViewController.java View File

2
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
 
4
 
5
+import com.reactnativenavigation.parse.Component;
5
 import com.reactnativenavigation.parse.Options;
6
 import com.reactnativenavigation.parse.Options;
6
 import com.reactnativenavigation.utils.CompatUtils;
7
 import com.reactnativenavigation.utils.CompatUtils;
7
 import com.reactnativenavigation.views.titlebar.TitleBarReactView;
8
 import com.reactnativenavigation.views.titlebar.TitleBarReactView;
10
 public class TitleBarReactViewController extends ViewController<TitleBarReactView> {
11
 public class TitleBarReactViewController extends ViewController<TitleBarReactView> {
11
 
12
 
12
     private final TitleBarReactViewCreator reactViewCreator;
13
     private final TitleBarReactViewCreator reactViewCreator;
13
-    private String componentName;
14
+    private Component component;
14
 
15
 
15
     public TitleBarReactViewController(Activity activity, TitleBarReactViewCreator reactViewCreator) {
16
     public TitleBarReactViewController(Activity activity, TitleBarReactViewCreator reactViewCreator) {
16
         super(activity, CompatUtils.generateViewId() + "", new Options());
17
         super(activity, CompatUtils.generateViewId() + "", new Options());
32
 
33
 
33
     @Override
34
     @Override
34
     protected TitleBarReactView createView() {
35
     protected TitleBarReactView createView() {
35
-        return reactViewCreator.create(getActivity(), getId(), componentName);
36
+        return reactViewCreator.create(getActivity(), getId(), component.name.get());
36
     }
37
     }
37
 
38
 
38
     @Override
39
     @Override
40
 
41
 
41
     }
42
     }
42
 
43
 
43
-    public void setComponent(String componentName) {
44
-        this.componentName = componentName;
44
+    public void setComponent(Component component) {
45
+        this.component = component;
45
     }
46
     }
46
 }
47
 }

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

63
     @NonNull
63
     @NonNull
64
     @Override
64
     @Override
65
     protected TitleBarReactButtonView createView() {
65
     protected TitleBarReactButtonView createView() {
66
-        view = (TitleBarReactButtonView) viewCreator.create(getActivity(), getId(), button.component.get());
66
+        view = (TitleBarReactButtonView) viewCreator.create(getActivity(), button.component.componentId.get(), button.component.name.get());
67
         return (TitleBarReactButtonView) view.asView();
67
         return (TitleBarReactButtonView) view.asView();
68
     }
68
     }
69
 
69
 

+ 7
- 6
lib/android/app/src/main/java/com/reactnativenavigation/viewcontrollers/topbar/TopBarBackgroundViewController.java View File

2
 
2
 
3
 import android.app.Activity;
3
 import android.app.Activity;
4
 
4
 
5
+import com.reactnativenavigation.parse.Component;
5
 import com.reactnativenavigation.parse.Options;
6
 import com.reactnativenavigation.parse.Options;
6
 import com.reactnativenavigation.utils.CompatUtils;
7
 import com.reactnativenavigation.utils.CompatUtils;
7
 import com.reactnativenavigation.viewcontrollers.ViewController;
8
 import com.reactnativenavigation.viewcontrollers.ViewController;
11
 public class TopBarBackgroundViewController extends ViewController<TopBarBackgroundView> {
12
 public class TopBarBackgroundViewController extends ViewController<TopBarBackgroundView> {
12
 
13
 
13
     private TopBarBackgroundViewCreator viewCreator;
14
     private TopBarBackgroundViewCreator viewCreator;
14
-    private String component;
15
+    private Component component;
15
 
16
 
16
     public TopBarBackgroundViewController(Activity activity, TopBarBackgroundViewCreator viewCreator) {
17
     public TopBarBackgroundViewController(Activity activity, TopBarBackgroundViewCreator viewCreator) {
17
         super(activity, CompatUtils.generateViewId() + "", new Options());
18
         super(activity, CompatUtils.generateViewId() + "", new Options());
18
         this.viewCreator = viewCreator;
19
         this.viewCreator = viewCreator;
19
     }
20
     }
20
 
21
 
21
-    public TopBarBackgroundViewController(TopBarBackgroundViewController topBarBackgroundViewController) {
22
-        super(topBarBackgroundViewController.getActivity(), CompatUtils.generateViewId() + "", new Options());
23
-        this.viewCreator = topBarBackgroundViewController.viewCreator;
22
+    public TopBarBackgroundViewController(TopBarBackgroundViewController controller) {
23
+        super(controller.getActivity(), controller.getId(), controller.options);
24
+        this.viewCreator = controller.viewCreator;
24
     }
25
     }
25
 
26
 
26
     @Override
27
     @Override
27
     protected TopBarBackgroundView createView() {
28
     protected TopBarBackgroundView createView() {
28
-        return viewCreator.create(getActivity(), getId(), component);
29
+        return viewCreator.create(getActivity(), component.componentId.get(), component.name.get());
29
     }
30
     }
30
 
31
 
31
     @Override
32
     @Override
45
 
46
 
46
     }
47
     }
47
 
48
 
48
-    public void setComponent(String component) {
49
+    public void setComponent(Component component) {
49
         this.component = component;
50
         this.component = component;
50
     }
51
     }
51
 }
52
 }

+ 7
- 5
lib/android/app/src/main/java/com/reactnativenavigation/views/titlebar/TitleBar.java View File

10
 import android.widget.TextView;
10
 import android.widget.TextView;
11
 
11
 
12
 import com.reactnativenavigation.parse.Alignment;
12
 import com.reactnativenavigation.parse.Alignment;
13
+import com.reactnativenavigation.parse.Component;
13
 import com.reactnativenavigation.parse.params.Button;
14
 import com.reactnativenavigation.parse.params.Button;
14
 import com.reactnativenavigation.parse.params.Color;
15
 import com.reactnativenavigation.parse.params.Color;
15
 import com.reactnativenavigation.utils.UiUtils;
16
 import com.reactnativenavigation.utils.UiUtils;
58
         if (color.hasValue()) setTitleTextColor(color.get());
59
         if (color.hasValue()) setTitleTextColor(color.get());
59
     }
60
     }
60
 
61
 
61
-    public void setComponent(String componentName, Alignment alignment) {
62
+    public void setComponent(Component component) {
62
         clearTitle();
63
         clearTitle();
63
-        reactViewController.setComponent(componentName);
64
-        addView(reactViewController.getView(), getComponentLayoutParams(alignment));
64
+        clearSubtitle();
65
+        reactViewController.setComponent(component);
66
+        addView(reactViewController.getView(), getComponentLayoutParams(component));
65
     }
67
     }
66
 
68
 
67
     public void setBackgroundColor(Color color) {
69
     public void setBackgroundColor(Color color) {
191
         return new TopBarButtonController((Activity) getContext(), button, buttonCreator, onClickListener);
193
         return new TopBarButtonController((Activity) getContext(), button, buttonCreator, onClickListener);
192
     }
194
     }
193
 
195
 
194
-    public Toolbar.LayoutParams getComponentLayoutParams(Alignment alignment) {
196
+    public Toolbar.LayoutParams getComponentLayoutParams(Component component) {
195
         LayoutParams lp = new LayoutParams(MATCH_PARENT, getHeight());
197
         LayoutParams lp = new LayoutParams(MATCH_PARENT, getHeight());
196
-        if (alignment == Alignment.Center) {
198
+        if (component.alignment == Alignment.Center) {
197
             lp.gravity = Gravity.CENTER;
199
             lp.gravity = Gravity.CENTER;
198
         }
200
         }
199
         return lp;
201
         return lp;

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

19
 import com.reactnativenavigation.interfaces.ScrollEventListener;
19
 import com.reactnativenavigation.interfaces.ScrollEventListener;
20
 import com.reactnativenavigation.parse.Alignment;
20
 import com.reactnativenavigation.parse.Alignment;
21
 import com.reactnativenavigation.parse.AnimationOptions;
21
 import com.reactnativenavigation.parse.AnimationOptions;
22
+import com.reactnativenavigation.parse.Component;
22
 import com.reactnativenavigation.parse.params.Button;
23
 import com.reactnativenavigation.parse.params.Button;
23
 import com.reactnativenavigation.parse.params.Color;
24
 import com.reactnativenavigation.parse.params.Color;
24
 import com.reactnativenavigation.parse.params.Number;
25
 import com.reactnativenavigation.parse.params.Number;
25
-import com.reactnativenavigation.parse.params.Text;
26
 import com.reactnativenavigation.utils.CompatUtils;
26
 import com.reactnativenavigation.utils.CompatUtils;
27
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
27
 import com.reactnativenavigation.viewcontrollers.ReactViewCreator;
28
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
28
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
119
         titleBar.setTitleAlignment(alignment);
119
         titleBar.setTitleAlignment(alignment);
120
     }
120
     }
121
 
121
 
122
-    public void setTitleComponent(String componentName, Alignment alignment) {
123
-        titleBar.setComponent(componentName, alignment);
122
+    public void setTitleComponent(Component component) {
123
+        titleBar.setComponent(component);
124
     }
124
     }
125
 
125
 
126
-    public void setBackgroundComponent(Text component) {
126
+    public void setBackgroundComponent(Component component) {
127
         if (component.hasValue()) {
127
         if (component.hasValue()) {
128
-            topBarBackgroundViewController.setComponent(component.get());
128
+            topBarBackgroundViewController.setComponent(component);
129
             RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, getHeight());
129
             RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT, getHeight());
130
             root.addView(topBarBackgroundViewController.getView(), 0, lp);
130
             root.addView(topBarBackgroundViewController.getView(), 0, lp);
131
         }
131
         }

+ 1
- 4
lib/android/app/src/test/java/com/reactnativenavigation/parse/OptionsTest.java View File

41
     private static final Typeface SUBTITLE_TYPEFACE = Typeface.create("HelveticaNeue-Condensed", Typeface.NORMAL);
41
     private static final Typeface SUBTITLE_TYPEFACE = Typeface.create("HelveticaNeue-Condensed", Typeface.NORMAL);
42
     private static final String SUBTITLE_ALIGNMENT = "center";
42
     private static final String SUBTITLE_ALIGNMENT = "center";
43
     private static final Typeface TOP_BAR_TYPEFACE = Typeface.create("HelveticaNeue-CondensedBold", Typeface.BOLD);
43
     private static final Typeface TOP_BAR_TYPEFACE = Typeface.create("HelveticaNeue-CondensedBold", Typeface.BOLD);
44
-    private static final String TITLE_ALIGNMENT = "center";
45
     private static final Bool TOP_BAR_VISIBLE = new Bool(true);
44
     private static final Bool TOP_BAR_VISIBLE = new Bool(true);
46
     private static final Bool TOP_BAR_DRAW_BEHIND = new Bool(true);
45
     private static final Bool TOP_BAR_DRAW_BEHIND = new Bool(true);
47
     private static final Bool TOP_BAR_HIDE_ON_SCROLL = new Bool(true);
46
     private static final Bool TOP_BAR_HIDE_ON_SCROLL = new Bool(true);
100
         assertThat(result.fabOptions.hideOnScroll.get()).isEqualTo(FAB_HIDE_ON_SCROLL);
99
         assertThat(result.fabOptions.hideOnScroll.get()).isEqualTo(FAB_HIDE_ON_SCROLL);
101
         assertThat(result.fabOptions.alignVertically.get()).isEqualTo(FAB_ALIGN_VERTICALLY);
100
         assertThat(result.fabOptions.alignVertically.get()).isEqualTo(FAB_ALIGN_VERTICALLY);
102
         assertThat(result.fabOptions.alignHorizontally.get()).isEqualTo(FAB_ALIGN_HORIZONTALLY);
101
         assertThat(result.fabOptions.alignHorizontally.get()).isEqualTo(FAB_ALIGN_HORIZONTALLY);
103
-        assertThat(result.topBarOptions.title.componentAlignment).isEqualTo(Alignment.Center);
104
     }
102
     }
105
 
103
 
106
     @NonNull
104
     @NonNull
133
                 .put("text", "the title")
131
                 .put("text", "the title")
134
                 .put("color", TOP_BAR_TEXT_COLOR)
132
                 .put("color", TOP_BAR_TEXT_COLOR)
135
                 .put("fontSize", TOP_BAR_FONT_SIZE)
133
                 .put("fontSize", TOP_BAR_FONT_SIZE)
136
-                .put("fontFamily", TOP_BAR_FONT_FAMILY)
137
-                .put("componentAlignment", TITLE_ALIGNMENT);
134
+                .put("fontFamily", TOP_BAR_FONT_FAMILY);
138
     }
135
     }
139
 
136
 
140
     private JSONObject createSubtitle() throws JSONException {
137
     private JSONObject createSubtitle() throws JSONException {

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

190
     @Test
190
     @Test
191
     public void appliesTopBarComponent() throws Exception {
191
     public void appliesTopBarComponent() throws Exception {
192
         JSONObject json = new JSONObject();
192
         JSONObject json = new JSONObject();
193
-        json.put("component", "someComponent");
193
+        json.put("component", new JSONObject().put("name","someComponent").put("componentId", "id"));
194
         uut.options.topBarOptions.background = TopBarBackgroundOptions.parse(json);
194
         uut.options.topBarOptions.background = TopBarBackgroundOptions.parse(json);
195
         uut.ensureViewIsCreated();
195
         uut.ensureViewIsCreated();
196
         stackController.push(uut);
196
         stackController.push(uut);

+ 3
- 3
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/OptionsMergingTest.java View File

84
 
84
 
85
         TitleOptions titleOptions = new TitleOptions();
85
         TitleOptions titleOptions = new TitleOptions();
86
         titleOptions.text = new Text("abc");
86
         titleOptions.text = new Text("abc");
87
-        titleOptions.component = new Text("someComponent");
87
+        titleOptions.component.name = new Text("someComponent");
88
+        titleOptions.component.componentId = new Text("compId");
88
         titleOptions.color = new Color(0);
89
         titleOptions.color = new Color(0);
89
         titleOptions.fontSize = new Fraction(1.0f);
90
         titleOptions.fontSize = new Fraction(1.0f);
90
         titleOptions.fontFamily = Typeface.DEFAULT_BOLD;
91
         titleOptions.fontFamily = Typeface.DEFAULT_BOLD;
140
         verify(topBar, times(1)).setTopTabFontFamily(1, Typeface.DEFAULT_BOLD);
141
         verify(topBar, times(1)).setTopTabFontFamily(1, Typeface.DEFAULT_BOLD);
141
     }
142
     }
142
 
143
 
143
-
144
     private void assertTopBarOptions(int t) {
144
     private void assertTopBarOptions(int t) {
145
         verify(topBar, times(t)).setTitle(any());
145
         verify(topBar, times(t)).setTitle(any());
146
         verify(topBar, times(t)).setSubtitle(any());
146
         verify(topBar, times(t)).setSubtitle(any());
147
-        verify(topBar, times(t)).setTitleComponent(any(), any());
147
+        verify(topBar, times(t)).setTitleComponent(any());
148
         verify(topBar, times(t)).setBackgroundColor(any());
148
         verify(topBar, times(t)).setBackgroundColor(any());
149
         verify(topBar, times(t)).setTitleTextColor(anyInt());
149
         verify(topBar, times(t)).setTitleTextColor(anyInt());
150
         verify(topBar, times(t)).setTitleFontSize(anyFloat());
150
         verify(topBar, times(t)).setTitleFontSize(anyFloat());

+ 20
- 9
lib/android/app/src/test/java/com/reactnativenavigation/viewcontrollers/TitleBarTest.java View File

9
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
9
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
10
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
10
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
11
 import com.reactnativenavigation.parse.Alignment;
11
 import com.reactnativenavigation.parse.Alignment;
12
+import com.reactnativenavigation.parse.Component;
12
 import com.reactnativenavigation.parse.params.Button;
13
 import com.reactnativenavigation.parse.params.Button;
13
 import com.reactnativenavigation.parse.params.Text;
14
 import com.reactnativenavigation.parse.params.Text;
14
 import com.reactnativenavigation.react.ReactView;
15
 import com.reactnativenavigation.react.ReactView;
60
     private void createButtons() {
61
     private void createButtons() {
61
         leftButton = new Button();
62
         leftButton = new Button();
62
         leftButton.id = "back";
63
         leftButton.id = "back";
63
-        leftButton.title = new Text("jfjf");
64
+        leftButton.title = new Text("abc");
64
 
65
 
65
         textButton = new Button();
66
         textButton = new Button();
66
         textButton.id = "textButton";
67
         textButton.id = "textButton";
68
 
69
 
69
         customButton = new Button();
70
         customButton = new Button();
70
         customButton.id = "customBtn";
71
         customButton.id = "customBtn";
71
-        customButton.component = new Text("com.rnn.customBtn");
72
+        customButton.component.name = new Text("com.rnn.customBtn");
73
+        customButton.component.componentId = new Text("component4");
72
     }
74
     }
73
 
75
 
74
     @Test
76
     @Test
83
         uut.setLeftButtons(leftButton(leftButton));
85
         uut.setLeftButtons(leftButton(leftButton));
84
         uut.setRightButtons(rightButtons(customButton));
86
         uut.setRightButtons(rightButtons(customButton));
85
         ReactView btnView = (ReactView) uut.getMenu().getItem(0).getActionView();
87
         ReactView btnView = (ReactView) uut.getMenu().getItem(0).getActionView();
86
-        assertThat(btnView.getComponentName()).isEqualTo(customButton.component.get());
88
+        assertThat(btnView.getComponentName()).isEqualTo(customButton.component.name.get());
87
     }
89
     }
88
 
90
 
89
     @Test
91
     @Test
144
 
146
 
145
     @Test
147
     @Test
146
     public void setComponent_addsComponentToTitleBar() {
148
     public void setComponent_addsComponentToTitleBar() {
147
-        uut.setComponent("com.rnn.CustomView", Alignment.Center);
149
+        uut.setComponent(component("com.rnn.CustomView", Alignment.Center));
148
         verify(uut, times(1)).addView(any(TitleBarReactView.class), any(Toolbar.LayoutParams.class));
150
         verify(uut, times(1)).addView(any(TitleBarReactView.class), any(Toolbar.LayoutParams.class));
149
     }
151
     }
150
 
152
 
153
+    private Component component(String name, Alignment alignment) {
154
+        Component component = new Component();
155
+        component.name = new Text(name);
156
+        component.alignment = alignment;
157
+        return component;
158
+    }
159
+
151
     @Test
160
     @Test
152
     public void setComponent_alignFill() {
161
     public void setComponent_alignFill() {
153
-        uut.setComponent("com.rnn.CustomView", Alignment.Fill);
154
-        verify(uut, times(1)).getComponentLayoutParams(Alignment.Fill);
162
+        Component component = component("com.rnn.CustomView", Alignment.Fill);
163
+        uut.setComponent(component);
164
+        verify(uut, times(1)).getComponentLayoutParams(component);
155
         ArgumentCaptor<Toolbar.LayoutParams> lpCaptor = ArgumentCaptor.forClass(Toolbar.LayoutParams.class);
165
         ArgumentCaptor<Toolbar.LayoutParams> lpCaptor = ArgumentCaptor.forClass(Toolbar.LayoutParams.class);
156
         verify(uut, times(1)).addView(any(TitleBarReactView.class), lpCaptor.capture());
166
         verify(uut, times(1)).addView(any(TitleBarReactView.class), lpCaptor.capture());
157
         assertThat(lpCaptor.getValue().width == ViewGroup.LayoutParams.MATCH_PARENT);
167
         assertThat(lpCaptor.getValue().width == ViewGroup.LayoutParams.MATCH_PARENT);
159
 
169
 
160
     @Test
170
     @Test
161
     public void setComponent_alignCenter() {
171
     public void setComponent_alignCenter() {
162
-        uut.setComponent("com.rnn.CustomView", Alignment.Center);
163
-        verify(uut, times(1)).getComponentLayoutParams(Alignment.Center);
172
+        Component component = component("com.rnn.CustomView", Alignment.Center);
173
+        uut.setComponent(component);
174
+        verify(uut, times(1)).getComponentLayoutParams(component);
164
         ArgumentCaptor<Toolbar.LayoutParams> lpCaptor = ArgumentCaptor.forClass(Toolbar.LayoutParams.class);
175
         ArgumentCaptor<Toolbar.LayoutParams> lpCaptor = ArgumentCaptor.forClass(Toolbar.LayoutParams.class);
165
         verify(uut, times(1)).addView(any(TitleBarReactView.class), lpCaptor.capture());
176
         verify(uut, times(1)).addView(any(TitleBarReactView.class), lpCaptor.capture());
166
         assertThat(lpCaptor.getValue().width == ViewGroup.LayoutParams.WRAP_CONTENT);
177
         assertThat(lpCaptor.getValue().width == ViewGroup.LayoutParams.WRAP_CONTENT);
169
 
180
 
170
     @Test
181
     @Test
171
     public void clear() {
182
     public void clear() {
172
-        uut.setComponent("someComponent", Alignment.Center);
183
+        uut.setComponent(component("someComponent", Alignment.Center));
173
         uut.clear();
184
         uut.clear();
174
         assertThat(uut.getTitle()).isNullOrEmpty();
185
         assertThat(uut.getTitle()).isNullOrEmpty();
175
         assertThat(uut.getMenu().size()).isZero();
186
         assertThat(uut.getMenu().size()).isZero();

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

55
     private Button createButton() {
55
     private Button createButton() {
56
         Button button = new Button();
56
         Button button = new Button();
57
         button.id = "btnId";
57
         button.id = "btnId";
58
-        button.component = new Text("com.example.customBtn");
58
+        button.component.name = new Text("com.example.customBtn");
59
+        button.component.componentId = new Text("component666");
59
         return button;
60
         return button;
60
     }
61
     }
61
 }
62
 }

+ 13
- 3
lib/android/app/src/test/java/com/reactnativenavigation/views/TopBarBackgroundComponentTest.java View File

9
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
9
 import com.reactnativenavigation.mocks.TitleBarReactViewCreatorMock;
10
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
10
 import com.reactnativenavigation.mocks.TopBarBackgroundViewCreatorMock;
11
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
11
 import com.reactnativenavigation.mocks.TopBarButtonCreatorMock;
12
+import com.reactnativenavigation.parse.Component;
12
 import com.reactnativenavigation.parse.params.Text;
13
 import com.reactnativenavigation.parse.params.Text;
13
 import com.reactnativenavigation.utils.ViewUtils;
14
 import com.reactnativenavigation.utils.ViewUtils;
14
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
15
 import com.reactnativenavigation.viewcontrollers.TopBarButtonController;
47
     @Test
48
     @Test
48
     public void setBackgroundComponent() {
49
     public void setBackgroundComponent() {
49
         uut.getLayoutParams().height = 100;
50
         uut.getLayoutParams().height = 100;
50
-        uut.setBackgroundComponent(new Text("someComponent"));
51
+        Component component = new Component();
52
+        component.name = new Text("someComponent");
53
+        component.componentId = new Text("id");
54
+        uut.setBackgroundComponent(component);
51
         TopBarBackgroundView background = (TopBarBackgroundView) ViewUtils.findChildrenByClassRecursive(uut, TopBarBackgroundView.class).get(0);
55
         TopBarBackgroundView background = (TopBarBackgroundView) ViewUtils.findChildrenByClassRecursive(uut, TopBarBackgroundView.class).get(0);
52
         assertThat(background).isNotNull();
56
         assertThat(background).isNotNull();
53
         assertThat(background.getLayoutParams().width).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT);
57
         assertThat(background.getLayoutParams().width).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT);
56
 
60
 
57
     @Test
61
     @Test
58
     public void setBackgroundComponent_doesNotSetIfNoComponentIsDefined() {
62
     public void setBackgroundComponent_doesNotSetIfNoComponentIsDefined() {
59
-        uut.setBackgroundComponent(new Text("someComponent"));
63
+        Component component = new Component();
64
+        component.name = new Text("someComponent");
65
+        component.componentId = new Text("id");
66
+        uut.setBackgroundComponent(component);
60
         assertThat(uut.findViewById(R.id.topBarBackgroundComponent)).isNull();
67
         assertThat(uut.findViewById(R.id.topBarBackgroundComponent)).isNull();
61
     }
68
     }
62
 
69
 
63
     @Test
70
     @Test
64
     public void clear_componentIsDestroyed() {
71
     public void clear_componentIsDestroyed() {
65
-        uut.setBackgroundComponent(new Text("someComponent"));
72
+        Component component = new Component();
73
+        component.name = new Text("someComponent");
74
+        component.componentId = new Text("id");
75
+        uut.setBackgroundComponent(component);
66
         uut.clear();
76
         uut.clear();
67
         verify(topBarBackgroundViewController, times(1)).destroy();
77
         verify(topBarBackgroundViewController, times(1)).destroy();
68
     }
78
     }

+ 2
- 1
lib/ios/RNNBackgroundOptions.h View File

1
 #import "RNNOptions.h"
1
 #import "RNNOptions.h"
2
+#import "RNNComponentOptions.h"
2
 
3
 
3
 @interface RNNBackgroundOptions : RNNOptions
4
 @interface RNNBackgroundOptions : RNNOptions
4
 
5
 
5
 @property (nonatomic, strong) NSNumber* color;
6
 @property (nonatomic, strong) NSNumber* color;
6
-@property (nonatomic, strong) NSString* component;
7
+@property (nonatomic, strong) RNNComponentOptions* component;
7
 
8
 
8
 @end
9
 @end

+ 9
- 0
lib/ios/RNNComponentOptions.h View File

1
+#import "RNNOptions.h"
2
+
3
+@interface RNNComponentOptions : RNNOptions
4
+
5
+@property (nonatomic, strong) NSString* name;
6
+@property (nonatomic, strong) NSString* componentId;
7
+@property (nonatomic, strong) NSString* alignment;
8
+
9
+@end

+ 5
- 0
lib/ios/RNNComponentOptions.m View File

1
+#import "RNNComponentOptions.h"
2
+
3
+@implementation RNNComponentOptions
4
+
5
+@end

+ 1
- 1
lib/ios/RNNNavigationButtons.m View File

54
 -(RNNUIBarButtonItem*)buildButton: (NSDictionary*)dictionary {
54
 -(RNNUIBarButtonItem*)buildButton: (NSDictionary*)dictionary {
55
 	NSString* buttonId = dictionary[@"id"];
55
 	NSString* buttonId = dictionary[@"id"];
56
 	NSString* title = dictionary[@"title"];
56
 	NSString* title = dictionary[@"title"];
57
-	NSString* component = dictionary[@"component"];
57
+	NSString* component = dictionary[@"component"][@"name"];
58
 	
58
 	
59
 	if (!buttonId) {
59
 	if (!buttonId) {
60
 		@throw [NSException exceptionWithName:@"NSInvalidArgumentException" reason:[@"button id is not specified " stringByAppendingString:title] userInfo:nil];
60
 		@throw [NSException exceptionWithName:@"NSInvalidArgumentException" reason:[@"button id is not specified " stringByAppendingString:title] userInfo:nil];

+ 1
- 1
lib/ios/RNNNavigationOptions.m View File

36
 	self.animated = [options objectForKey:@"animated"];
36
 	self.animated = [options objectForKey:@"animated"];
37
 	self.customTransition = [[RNNAnimationOptions alloc] initWithDict:[options objectForKey:@"customTransition"]];
37
 	self.customTransition = [[RNNAnimationOptions alloc] initWithDict:[options objectForKey:@"customTransition"]];
38
 	self.animations = [[RNNTransitionsOptions alloc] initWithDict:[options objectForKey:@"animations"]];
38
 	self.animations = [[RNNTransitionsOptions alloc] initWithDict:[options objectForKey:@"animations"]];
39
-	
39
+
40
 	return self;
40
 	return self;
41
 }
41
 }
42
 
42
 

+ 28
- 1
lib/ios/RNNOptions.m View File

1
 
1
 
2
 #import "RNNOptions.h"
2
 #import "RNNOptions.h"
3
+#import <objc/runtime.h>
3
 
4
 
4
 @implementation RNNOptions
5
 @implementation RNNOptions
5
 
6
 
6
--(instancetype)initWithDict:(NSDictionary *)dict {
7
+-(instancetype)	initWithDict:(NSDictionary *)dict {
7
 	self = [super init];
8
 	self = [super init];
9
+	[self initializeOptionsPropertiesWithDict:dict];
10
+	
8
 	[self mergeWith:dict];
11
 	[self mergeWith:dict];
9
 	return self;
12
 	return self;
10
 }
13
 }
44
 	return [self respondsToSelector:NSSelectorFromString(propName)];
47
 	return [self respondsToSelector:NSSelectorFromString(propName)];
45
 }
48
 }
46
 
49
 
50
+- (void)initializeOptionsPropertiesWithDict:(NSDictionary*)dict {
51
+	unsigned int count;
52
+	objc_property_t* props = class_copyPropertyList([self class], &count);
53
+	for (int i = 0; i < count; i++) {
54
+		objc_property_t property = props[i];
55
+		NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
56
+		const char * type = property_getAttributes(property);
57
+		NSString * typeString = [NSString stringWithUTF8String:type];
58
+		NSArray * attributes = [typeString componentsSeparatedByString:@","];
59
+		NSString * typeAttribute = [attributes objectAtIndex:0];
60
+		
61
+		if ([typeAttribute hasPrefix:@"T@"] && [typeAttribute length] > 3) {
62
+			NSString * typeClassName = [typeAttribute substringWithRange:NSMakeRange(3, [typeAttribute length]-4)];
63
+			Class typeClass = NSClassFromString(typeClassName);
64
+			if ([typeClass isSubclassOfClass:[RNNOptions class]]) {
65
+				RNNOptions* value = [[typeClass alloc] initWithDict:dict[propertyName]];
66
+				[self setValue:value forKey:propertyName];
67
+			}
68
+		}
69
+		
70
+	}
71
+	free(props);
72
+}
73
+
47
 @end
74
 @end

+ 4
- 0
lib/ios/RNNReactRootViewCreator.m View File

34
 	return view;
34
 	return view;
35
 }
35
 }
36
 
36
 
37
+-(UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions {
38
+	return [self createRootView:componentOptions.name rootViewId:componentOptions.componentId];
39
+}
40
+
37
 @end
41
 @end

+ 7
- 8
lib/ios/RNNRootViewController.m View File

44
 -(void)viewWillAppear:(BOOL)animated{
44
 -(void)viewWillAppear:(BOOL)animated{
45
 	[super viewWillAppear:animated];
45
 	[super viewWillAppear:animated];
46
 	[self.options applyOn:self];
46
 	[self.options applyOn:self];
47
-	[self optionsUpdated];
48
 }
47
 }
49
 
48
 
50
 -(void)viewDidAppear:(BOOL)animated {
49
 -(void)viewDidAppear:(BOOL)animated {
76
 }
75
 }
77
 
76
 
78
 - (void)setCustomNavigationTitleView {
77
 - (void)setCustomNavigationTitleView {
79
-	if (self.options.topBar.title.component) {
80
-		RCTRootView *reactView = (RCTRootView*)[_creator createRootView:self.options.topBar.title.component rootViewId:self.options.topBar.title.component];
78
+	if (self.options.topBar.title.component.name) {
79
+		RCTRootView *reactView = (RCTRootView*)[_creator createRootViewFromComponentOptions:self.options.topBar.title.component];
81
 
80
 
82
-		RNNCustomTitleView *titleView = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:self.options.topBar.title.componentAlignment];
81
+		RNNCustomTitleView *titleView = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:self.options.topBar.title.component.alignment];
83
         reactView.backgroundColor = UIColor.clearColor;
82
         reactView.backgroundColor = UIColor.clearColor;
84
 		titleView.backgroundColor = UIColor.clearColor;
83
 		titleView.backgroundColor = UIColor.clearColor;
85
 		self.navigationItem.titleView = titleView;
84
 		self.navigationItem.titleView = titleView;
89
 }
88
 }
90
 
89
 
91
 - (void)setCustomNavigationBarView {
90
 - (void)setCustomNavigationBarView {
92
-	if (self.options.topBar.componentName) {
93
-		RCTRootView *reactView = (RCTRootView*)[_creator createRootView:self.options.topBar.componentName rootViewId:@"navBar"];
91
+	if (self.options.topBar.component.name) {
92
+		RCTRootView *reactView = (RCTRootView*)[_creator createRootViewFromComponentOptions:self.options.topBar.component];
94
 
93
 
95
 		RNNCustomTitleView *titleView = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
94
 		RNNCustomTitleView *titleView = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
96
 		reactView.backgroundColor = UIColor.clearColor;
95
 		reactView.backgroundColor = UIColor.clearColor;
102
 }
101
 }
103
 
102
 
104
 - (void)setCustomNavigationComponentBackground {
103
 - (void)setCustomNavigationComponentBackground {
105
-	if (self.options.topBar.background.component) {
106
-		RCTRootView *reactView = (RCTRootView*)[_creator createRootView:self.options.topBar.background.component rootViewId:@"navBarBackground"];
104
+	if (self.options.topBar.background.component.name) {
105
+		RCTRootView *reactView = (RCTRootView*)[_creator createRootViewFromComponentOptions:self.options.topBar.background.component];
107
 
106
 
108
 		RNNCustomTitleView *titleView = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
107
 		RNNCustomTitleView *titleView = [[RNNCustomTitleView alloc] initWithFrame:self.navigationController.navigationBar.bounds subView:reactView alignment:@"fill"];
109
 		[self.navigationController.navigationBar insertSubview:titleView atIndex:1];
108
 		[self.navigationController.navigationBar insertSubview:titleView atIndex:1];

+ 3
- 1
lib/ios/RNNRootViewCreator.h View File

1
 
1
 
2
 #import <UIKit/UIKit.h>
2
 #import <UIKit/UIKit.h>
3
-
3
+#import "RNNComponentOptions.h"
4
 
4
 
5
 @protocol RNNRootViewCreator
5
 @protocol RNNRootViewCreator
6
 
6
 
7
 -(UIView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId;
7
 -(UIView*)createRootView:(NSString*)name rootViewId:(NSString*)rootViewId;
8
 
8
 
9
+-(UIView*)createRootViewFromComponentOptions:(RNNComponentOptions*)componentOptions;
10
+
9
 @end
11
 @end
10
 
12
 

+ 2
- 1
lib/ios/RNNTitleOptions.h View File

1
 #import "RNNOptions.h"
1
 #import "RNNOptions.h"
2
 #import "RNNSubtitleOptions.h"
2
 #import "RNNSubtitleOptions.h"
3
+#import "RNNComponentOptions.h"
3
 
4
 
4
 @interface RNNTitleOptions : RNNOptions
5
 @interface RNNTitleOptions : RNNOptions
5
 
6
 
8
 @property (nonatomic, strong) NSNumber* color;
9
 @property (nonatomic, strong) NSNumber* color;
9
 @property (nonatomic, strong) NSString* fontFamily;
10
 @property (nonatomic, strong) NSString* fontFamily;
10
 
11
 
11
-@property (nonatomic, strong) NSString* component;
12
+@property (nonatomic, strong) RNNComponentOptions* component;
12
 @property (nonatomic, strong) NSString* componentAlignment;
13
 @property (nonatomic, strong) NSString* componentAlignment;
13
 
14
 
14
 @property (nonatomic, strong) RNNSubtitleOptions* subtitle;
15
 @property (nonatomic, strong) RNNSubtitleOptions* subtitle;

+ 2
- 1
lib/ios/RNNTopBarOptions.h View File

2
 #import "RNNTitleOptions.h"
2
 #import "RNNTitleOptions.h"
3
 #import "RNNSubtitleOptions.h"
3
 #import "RNNSubtitleOptions.h"
4
 #import "RNNBackgroundOptions.h"
4
 #import "RNNBackgroundOptions.h"
5
+#import "RNNComponentOptions.h"
5
 
6
 
6
 @interface RNNTopBarOptions : RNNOptions
7
 @interface RNNTopBarOptions : RNNOptions
7
 
8
 
26
 @property (nonatomic, strong) NSString* backButtonTitle;
27
 @property (nonatomic, strong) NSString* backButtonTitle;
27
 @property (nonatomic, strong) NSNumber* hideBackButtonTitle;
28
 @property (nonatomic, strong) NSNumber* hideBackButtonTitle;
28
 
29
 
29
-@property (nonatomic, strong) NSString* componentName;
30
+@property (nonatomic, strong) RNNComponentOptions* component;
30
 
31
 
31
 @end
32
 @end

+ 1
- 3
lib/ios/RNNTopBarOptions.m View File

15
 
15
 
16
 - (instancetype)initWithDict:(NSDictionary *)dict {
16
 - (instancetype)initWithDict:(NSDictionary *)dict {
17
 	self = [super initWithDict:dict];
17
 	self = [super initWithDict:dict];
18
-	self.title = [[RNNTitleOptions alloc] initWithDict:dict[@"title"]];
19
-	self.subtitle = [[RNNSubtitleOptions alloc] initWithDict:dict[@"subtitle"]];
20
-	self.background = [[RNNBackgroundOptions alloc] initWithDict:dict[@"background"]];
18
+	
21
 	self.title.subtitle = self.subtitle;
19
 	self.title.subtitle = self.subtitle;
22
 	
20
 	
23
 	return self;
21
 	return self;

+ 1
- 1
lib/ios/RNNTransitionsOptions.m View File

4
 
4
 
5
 - (instancetype)initWithDict:(NSDictionary *)dict {
5
 - (instancetype)initWithDict:(NSDictionary *)dict {
6
 	self = [super init];
6
 	self = [super init];
7
-
7
+	
8
 	[self mergeWith:dict];
8
 	[self mergeWith:dict];
9
 	
9
 	
10
 	return self;
10
 	return self;

+ 8
- 0
lib/ios/ReactNativeNavigation.xcodeproj/project.pbxproj View File

63
 		390AD478200F499D00A8250D /* RNNSwizzles.m in Sources */ = {isa = PBXBuildFile; fileRef = 390AD476200F499D00A8250D /* RNNSwizzles.m */; };
63
 		390AD478200F499D00A8250D /* RNNSwizzles.m in Sources */ = {isa = PBXBuildFile; fileRef = 390AD476200F499D00A8250D /* RNNSwizzles.m */; };
64
 		5016E8EF20209690009D4F7C /* RNNCustomTitleView.h in Headers */ = {isa = PBXBuildFile; fileRef = 5016E8ED2020968F009D4F7C /* RNNCustomTitleView.h */; };
64
 		5016E8EF20209690009D4F7C /* RNNCustomTitleView.h in Headers */ = {isa = PBXBuildFile; fileRef = 5016E8ED2020968F009D4F7C /* RNNCustomTitleView.h */; };
65
 		5016E8F020209690009D4F7C /* RNNCustomTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5016E8EE2020968F009D4F7C /* RNNCustomTitleView.m */; };
65
 		5016E8F020209690009D4F7C /* RNNCustomTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5016E8EE2020968F009D4F7C /* RNNCustomTitleView.m */; };
66
+		50175CD1207A2AA1004FE91B /* RNNComponentOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 50175CCF207A2AA1004FE91B /* RNNComponentOptions.h */; };
67
+		50175CD2207A2AA1004FE91B /* RNNComponentOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 50175CD0207A2AA1004FE91B /* RNNComponentOptions.m */; };
66
 		50415CBA20553B8E00BB682E /* RNNScreenTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = 50415CB820553B8E00BB682E /* RNNScreenTransition.h */; };
68
 		50415CBA20553B8E00BB682E /* RNNScreenTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = 50415CB820553B8E00BB682E /* RNNScreenTransition.h */; };
67
 		50415CBB20553B8E00BB682E /* RNNScreenTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 50415CB920553B8E00BB682E /* RNNScreenTransition.m */; };
69
 		50415CBB20553B8E00BB682E /* RNNScreenTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 50415CB920553B8E00BB682E /* RNNScreenTransition.m */; };
68
 		50451D052042DAEB00695F00 /* RNNPushAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 50451D032042DAEB00695F00 /* RNNPushAnimation.h */; };
70
 		50451D052042DAEB00695F00 /* RNNPushAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 50451D032042DAEB00695F00 /* RNNPushAnimation.h */; };
256
 		390AD476200F499D00A8250D /* RNNSwizzles.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNSwizzles.m; sourceTree = "<group>"; };
258
 		390AD476200F499D00A8250D /* RNNSwizzles.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNSwizzles.m; sourceTree = "<group>"; };
257
 		5016E8ED2020968F009D4F7C /* RNNCustomTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNCustomTitleView.h; sourceTree = "<group>"; };
259
 		5016E8ED2020968F009D4F7C /* RNNCustomTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNCustomTitleView.h; sourceTree = "<group>"; };
258
 		5016E8EE2020968F009D4F7C /* RNNCustomTitleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNCustomTitleView.m; sourceTree = "<group>"; };
260
 		5016E8EE2020968F009D4F7C /* RNNCustomTitleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNCustomTitleView.m; sourceTree = "<group>"; };
261
+		50175CCF207A2AA1004FE91B /* RNNComponentOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNComponentOptions.h; sourceTree = "<group>"; };
262
+		50175CD0207A2AA1004FE91B /* RNNComponentOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNComponentOptions.m; sourceTree = "<group>"; };
259
 		50415CB820553B8E00BB682E /* RNNScreenTransition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNScreenTransition.h; sourceTree = "<group>"; };
263
 		50415CB820553B8E00BB682E /* RNNScreenTransition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNScreenTransition.h; sourceTree = "<group>"; };
260
 		50415CB920553B8E00BB682E /* RNNScreenTransition.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNScreenTransition.m; sourceTree = "<group>"; };
264
 		50415CB920553B8E00BB682E /* RNNScreenTransition.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNScreenTransition.m; sourceTree = "<group>"; };
261
 		50451D032042DAEB00695F00 /* RNNPushAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNPushAnimation.h; sourceTree = "<group>"; };
265
 		50451D032042DAEB00695F00 /* RNNPushAnimation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNPushAnimation.h; sourceTree = "<group>"; };
491
 				504AFE631FFE53070076E904 /* RNNOptions.m */,
495
 				504AFE631FFE53070076E904 /* RNNOptions.m */,
492
 				E83BAD691F27362500A9F3DD /* RNNNavigationOptions.h */,
496
 				E83BAD691F27362500A9F3DD /* RNNNavigationOptions.h */,
493
 				E83BAD6A1F27363A00A9F3DD /* RNNNavigationOptions.m */,
497
 				E83BAD6A1F27363A00A9F3DD /* RNNNavigationOptions.m */,
498
+				50175CCF207A2AA1004FE91B /* RNNComponentOptions.h */,
499
+				50175CD0207A2AA1004FE91B /* RNNComponentOptions.m */,
494
 				A7626BFE1FC2FB6700492FB8 /* RNNTopBarOptions.h */,
500
 				A7626BFE1FC2FB6700492FB8 /* RNNTopBarOptions.h */,
495
 				A7626BFC1FC2FB2C00492FB8 /* RNNTopBarOptions.m */,
501
 				A7626BFC1FC2FB2C00492FB8 /* RNNTopBarOptions.m */,
496
 				50570B242061473D006A1B5C /* RNNTitleOptions.h */,
502
 				50570B242061473D006A1B5C /* RNNTitleOptions.h */,
794
 				E8A5CD521F464F0400E89D0D /* RNNAnimator.h in Headers */,
800
 				E8A5CD521F464F0400E89D0D /* RNNAnimator.h in Headers */,
795
 				50451D0D2042F70900695F00 /* RNNTransition.h in Headers */,
801
 				50451D0D2042F70900695F00 /* RNNTransition.h in Headers */,
796
 				507F43F81FF525B500D9425B /* RNNSegmentedControl.h in Headers */,
802
 				507F43F81FF525B500D9425B /* RNNSegmentedControl.h in Headers */,
803
+				50175CD1207A2AA1004FE91B /* RNNComponentOptions.h in Headers */,
797
 			);
804
 			);
798
 			runOnlyForDeploymentPostprocessing = 0;
805
 			runOnlyForDeploymentPostprocessing = 0;
799
 		};
806
 		};
933
 				261F0E651E6EC94900989DE2 /* RNNModalManager.m in Sources */,
940
 				261F0E651E6EC94900989DE2 /* RNNModalManager.m in Sources */,
934
 				E8A5CD631F49114F00E89D0D /* RNNElement.m in Sources */,
941
 				E8A5CD631F49114F00E89D0D /* RNNElement.m in Sources */,
935
 				504AFE751FFFF0540076E904 /* RNNTopTabsOptions.m in Sources */,
942
 				504AFE751FFFF0540076E904 /* RNNTopTabsOptions.m in Sources */,
943
+				50175CD2207A2AA1004FE91B /* RNNComponentOptions.m in Sources */,
936
 				7BEF0D1D1E43771B003E96B0 /* RNNLayoutNode.m in Sources */,
944
 				7BEF0D1D1E43771B003E96B0 /* RNNLayoutNode.m in Sources */,
937
 				7BA500781E254908001B9E1B /* RNNSplashScreen.m in Sources */,
945
 				7BA500781E254908001B9E1B /* RNNSplashScreen.m in Sources */,
938
 				504AFE651FFE53070076E904 /* RNNOptions.m in Sources */,
946
 				504AFE651FFE53070076E904 /* RNNOptions.m in Sources */,

+ 1
- 1
lib/src/commands/Commands.test.ts View File

314
     beforeEach(() => {
314
     beforeEach(() => {
315
       cb = jest.fn();
315
       cb = jest.fn();
316
       const mockParser = { parse: () => 'parsed' };
316
       const mockParser = { parse: () => 'parsed' };
317
-      const mockCrawler = { crawl: (x) => x };
317
+      const mockCrawler = { crawl: (x) => x, processOptions: (x) => x };
318
       commandsObserver.register(cb);
318
       commandsObserver.register(cb);
319
       uut = new Commands(mockCommandsSender, mockParser, mockCrawler, commandsObserver);
319
       uut = new Commands(mockCommandsSender, mockParser, mockCrawler, commandsObserver);
320
     });
320
     });

+ 2
- 6
lib/src/commands/Commands.ts View File

1
 import * as _ from 'lodash';
1
 import * as _ from 'lodash';
2
-import { OptionsProcessor } from './OptionsProcessor';
3
 import { CommandsObserver } from '../events/CommandsObserver';
2
 import { CommandsObserver } from '../events/CommandsObserver';
4
 
3
 
5
 export class Commands {
4
 export class Commands {
6
-  private optionsProcessor: OptionsProcessor;
7
-
8
   constructor(
5
   constructor(
9
     private readonly nativeCommandsSender,
6
     private readonly nativeCommandsSender,
10
     private readonly layoutTreeParser,
7
     private readonly layoutTreeParser,
11
     private readonly layoutTreeCrawler,
8
     private readonly layoutTreeCrawler,
12
     private readonly commandsObserver: CommandsObserver) {
9
     private readonly commandsObserver: CommandsObserver) {
13
-    this.optionsProcessor = new OptionsProcessor(this.layoutTreeCrawler.store);
14
   }
10
   }
15
 
11
 
16
   public setRoot(simpleApi) {
12
   public setRoot(simpleApi) {
25
 
21
 
26
   public setDefaultOptions(options) {
22
   public setDefaultOptions(options) {
27
     const input = _.cloneDeep(options);
23
     const input = _.cloneDeep(options);
28
-    this.optionsProcessor.processOptions(input);
24
+    this.layoutTreeCrawler.processOptions(input);
29
 
25
 
30
     this.nativeCommandsSender.setDefaultOptions(input);
26
     this.nativeCommandsSender.setDefaultOptions(input);
31
     this.commandsObserver.notify('setDefaultOptions', { options });
27
     this.commandsObserver.notify('setDefaultOptions', { options });
33
 
29
 
34
   public setOptions(componentId, options) {
30
   public setOptions(componentId, options) {
35
     const input = _.cloneDeep(options);
31
     const input = _.cloneDeep(options);
36
-    this.optionsProcessor.processOptions(input);
32
+    this.layoutTreeCrawler.processOptions(input);
37
 
33
 
38
     this.nativeCommandsSender.setOptions(componentId, input);
34
     this.nativeCommandsSender.setOptions(componentId, input);
39
     this.commandsObserver.notify('setOptions', { componentId, options });
35
     this.commandsObserver.notify('setOptions', { componentId, options });

+ 7
- 2
lib/src/commands/LayoutTreeCrawler.ts View File

20
     private readonly uniqueIdProvider: any,
20
     private readonly uniqueIdProvider: any,
21
     public readonly store: any) {
21
     public readonly store: any) {
22
     this.crawl = this.crawl.bind(this);
22
     this.crawl = this.crawl.bind(this);
23
-    this.optionsProcessor = new OptionsProcessor(store);
23
+    this.processOptions = this.processOptions.bind(this);
24
+    this.optionsProcessor = new OptionsProcessor(store, uniqueIdProvider);
24
   }
25
   }
25
 
26
 
26
   crawl(node: LayoutNode): void {
27
   crawl(node: LayoutNode): void {
31
     if (node.type === LayoutType.Component) {
32
     if (node.type === LayoutType.Component) {
32
       this._handleComponent(node);
33
       this._handleComponent(node);
33
     }
34
     }
34
-    this.optionsProcessor.processOptions(node.data.options);
35
+    this.processOptions(node.data.options);
35
     _.forEach(node.children, this.crawl);
36
     _.forEach(node.children, this.crawl);
36
   }
37
   }
37
 
38
 
39
+  processOptions(options) {
40
+    this.optionsProcessor.processOptions(options);
41
+  }
42
+
38
   _handleComponent(node) {
43
   _handleComponent(node) {
39
     this._assertComponentDataName(node);
44
     this._assertComponentDataName(node);
40
     this._savePropsToStore(node);
45
     this._savePropsToStore(node);

+ 1
- 1
lib/src/commands/LayoutType.test.ts View File

1
 import { LayoutType, isLayoutType } from './LayoutType';
1
 import { LayoutType, isLayoutType } from './LayoutType';
2
 
2
 
3
-describe.only('LayoutType', () => {
3
+describe('LayoutType', () => {
4
   it('is an enum', () => {
4
   it('is an enum', () => {
5
     expect(LayoutType.Component).toEqual('Component');
5
     expect(LayoutType.Component).toEqual('Component');
6
     expect(LayoutType.Stack).toEqual('Stack');
6
     expect(LayoutType.Stack).toEqual('Stack');

+ 38
- 1
lib/src/commands/OptionsProcessor.test.ts View File

1
 import { OptionsProcessor } from './OptionsProcessor';
1
 import { OptionsProcessor } from './OptionsProcessor';
2
+import { UniqueIdProvider } from '../adapters/UniqueIdProvider';
2
 import { Store } from '../components/Store';
3
 import { Store } from '../components/Store';
4
+import * as _ from 'lodash';
3
 
5
 
4
 describe('navigation options', () => {
6
 describe('navigation options', () => {
5
   let uut: OptionsProcessor;
7
   let uut: OptionsProcessor;
8
   beforeEach(() => {
10
   beforeEach(() => {
9
     options = {};
11
     options = {};
10
     store = new Store();
12
     store = new Store();
11
-    uut = new OptionsProcessor(store);
13
+    uut = new OptionsProcessor(store, new UniqueIdProvider());
12
   });
14
   });
13
 
15
 
14
   it('processes colors into numeric AARRGGBB', () => {
16
   it('processes colors into numeric AARRGGBB', () => {
111
     expect(store.getPropsForId('1')).toEqual(passProps);
113
     expect(store.getPropsForId('1')).toEqual(passProps);
112
   });
114
   });
113
 
115
 
116
+  it('passProps for custom component', () => {
117
+    const passProps = { color: '#ff0000', some: 'thing' };
118
+    options.component = { passProps, name: 'a' };
119
+
120
+    uut.processOptions({ o: options });
121
+
122
+    expect(store.getPropsForId(options.component.componentId)).toEqual(passProps);
123
+    expect(Object.keys(options.component)).not.toContain('passProps');
124
+  });
125
+
126
+  it('generate component id for component in options', () => {
127
+    options.component = { name: 'a' };
128
+
129
+    uut.processOptions({ o: options });
130
+
131
+    expect(options.component.componentId).toBeDefined();
132
+  });
133
+
134
+  it('passProps from options are not processed', () => {
135
+    const passProps = { color: '#ff0000', some: 'thing' };
136
+    const clonedProps = _.cloneDeep(passProps);
137
+    options.component = { passProps, name: 'a' };
138
+
139
+    uut.processOptions(options);
140
+    expect(store.getPropsForId(options.component.componentId)).toEqual(clonedProps);
141
+  });
142
+
143
+  it('pass supplied componentId for component in options', () => {
144
+    options.component = { name: 'a', id: 'Component1' };
145
+
146
+    uut.processOptions({ o: options });
147
+
148
+    expect(options.component.componentId).toEqual('Component1');
149
+  });
150
+
114
   it('passProps must be with id next to it', () => {
151
   it('passProps must be with id next to it', () => {
115
     const passProps = { prop: 'prop' };
152
     const passProps = { prop: 'prop' };
116
     options.rightButtons = [{ passProps }];
153
     options.rightButtons = [{ passProps }];

+ 13
- 2
lib/src/commands/OptionsProcessor.ts View File

3
 import * as resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';
3
 import * as resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';
4
 
4
 
5
 export class OptionsProcessor {
5
 export class OptionsProcessor {
6
-  constructor(public store) { }
6
+  constructor(public store, public uniqueIdProvider) { }
7
 
7
 
8
   public processOptions(options) {
8
   public processOptions(options) {
9
     _.forEach(options, (value, key) => {
9
     _.forEach(options, (value, key) => {
10
       if (!value) { return; }
10
       if (!value) { return; }
11
 
11
 
12
+      this.processComponent(key, value, options);
12
       this.processColor(key, value, options);
13
       this.processColor(key, value, options);
13
       this.processImage(key, value, options);
14
       this.processImage(key, value, options);
14
       this.processButtonsPassProps(key, value);
15
       this.processButtonsPassProps(key, value);
15
 
16
 
16
-      if (_.isObject(value) || _.isArray(value)) {
17
+      if (!_.isEqual(key, 'passProps') && (_.isObject(value) || _.isArray(value))) {
17
         this.processOptions(value);
18
         this.processOptions(value);
18
       }
19
       }
19
     });
20
     });
40
       });
41
       });
41
     }
42
     }
42
   }
43
   }
44
+
45
+  private processComponent(key, value, options) {
46
+    if (_.isEqual(key, 'component')) {
47
+      value.componentId = value.id ? value.id : this.uniqueIdProvider.generate('CustomComponent');
48
+      if (value.passProps) {
49
+        this.store.setPropsForId(value.componentId, value.passProps);
50
+      }
51
+      options[key] = _.omit(value, 'passProps');
52
+    }
53
+  }
43
 }
54
 }

+ 13
- 4
playground/src/screens/OptionsScreen.js View File

32
           alignment: 'center'
32
           alignment: 'center'
33
         },
33
         },
34
         background: {
34
         background: {
35
-          component: 'TopBarBackground'
35
+          component: {
36
+            name: 'TopBarBackground',
37
+            passProps: {
38
+              color: '#bbdefb'
39
+            }
40
+          }
36
         },
41
         },
37
         ...Platform.select({
42
         ...Platform.select({
38
           android: { drawBehind: true },
43
           android: { drawBehind: true },
49
           {
54
           {
50
             id: CUSTOM_BUTTON2,
55
             id: CUSTOM_BUTTON2,
51
             testID: CUSTOM_BUTTON2,
56
             testID: CUSTOM_BUTTON2,
52
-            component: 'CustomRoundedButton'
57
+            component: {
58
+              name: 'CustomRoundedButton'
59
+            }
53
           },
60
           },
54
           {
61
           {
55
             id: BUTTON_ONE,
62
             id: BUTTON_ONE,
263
     Navigation.setOptions(this.props.componentId, {
270
     Navigation.setOptions(this.props.componentId, {
264
       topBar: {
271
       topBar: {
265
         title: {
272
         title: {
266
-          component: 'navigation.playground.CustomTopBar',
267
-          componentAlignment: 'center'
273
+          component: {
274
+            name: 'navigation.playground.CustomTopBar',
275
+            alignment: 'center'
276
+          }
268
         }
277
         }
269
       }
278
       }
270
     });
279
     });

+ 2
- 3
playground/src/screens/TopBarBackground.js View File

10
   constructor(props) {
10
   constructor(props) {
11
     super(props);
11
     super(props);
12
     this.state = {};
12
     this.state = {};
13
-    this.dots = new Array(55).fill('').map((ignored, i) => <View key={'dot' + i} style={styles.dot}/>);
13
+    this.dots = new Array(55).fill('').map((ignored, i) => <View key={'dot' + i} style={[styles.dot, {backgroundColor: this.props.color}]}/>);
14
   }
14
   }
15
 
15
 
16
   componentDidAppear() {
16
   componentDidAppear() {
51
     height: 16,
51
     height: 16,
52
     width: 16,
52
     width: 16,
53
     borderRadius: 8,
53
     borderRadius: 8,
54
-    margin: 4,
55
-    backgroundColor: '#bbdefb'
54
+    margin: 4
56
   }
55
   }
57
 });
56
 });