motion-prime 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +5 -0
  3. data/Gemfile.lock +14 -11
  4. data/README.md +8 -11
  5. data/Rakefile +2 -1
  6. data/bin/prime.rb +47 -0
  7. data/doc/FAQ.md +1 -1
  8. data/files/app/app_delegate.rb +1 -1
  9. data/files/app/config/base.rb +8 -4
  10. data/files/app/screens/application_screen.rb +1 -1
  11. data/files/app/screens/sidebar_screen.rb +1 -1
  12. data/files/app/sections/sidebar/action.rb +1 -1
  13. data/files/app/sections/sidebar/table.rb +1 -1
  14. data/files/app/styles/sidebar.rb +5 -5
  15. data/motion-prime.gemspec +1 -0
  16. data/motion-prime/api_client.rb +81 -0
  17. data/motion-prime/app_delegate.rb +22 -5
  18. data/motion-prime/config/base.rb +5 -0
  19. data/motion-prime/core_ext/kernel.rb +5 -0
  20. data/motion-prime/elements/_field_dimensions_mixin.rb +43 -0
  21. data/motion-prime/elements/_text_dimensions_mixin.rb +39 -0
  22. data/motion-prime/elements/base.rb +40 -17
  23. data/motion-prime/elements/button.rb +20 -0
  24. data/motion-prime/elements/draw.rb +2 -2
  25. data/motion-prime/elements/draw/image.rb +4 -2
  26. data/motion-prime/elements/draw/label.rb +1 -1
  27. data/motion-prime/elements/error_message.rb +3 -16
  28. data/motion-prime/elements/label.rb +13 -2
  29. data/motion-prime/elements/text_field.rb +1 -0
  30. data/motion-prime/helpers/cell_section.rb +9 -0
  31. data/motion-prime/helpers/has_authorization.rb +4 -3
  32. data/motion-prime/helpers/has_normalizer.rb +20 -6
  33. data/motion-prime/helpers/has_search_bar.rb +19 -7
  34. data/motion-prime/helpers/has_style_chain_builder.rb +7 -0
  35. data/motion-prime/models/association.rb +22 -9
  36. data/motion-prime/models/association_collection.rb +54 -23
  37. data/motion-prime/models/bag.rb +13 -12
  38. data/motion-prime/models/base.rb +2 -0
  39. data/motion-prime/models/errors.rb +23 -14
  40. data/motion-prime/models/finder.rb +4 -1
  41. data/motion-prime/models/model.rb +25 -5
  42. data/motion-prime/models/store_extension.rb +1 -7
  43. data/motion-prime/models/sync.rb +75 -43
  44. data/motion-prime/mp.rb +4 -0
  45. data/motion-prime/screens/_base_mixin.rb +18 -12
  46. data/motion-prime/screens/_navigation_bar_mixin.rb +15 -6
  47. data/motion-prime/screens/_navigation_mixin.rb +15 -16
  48. data/motion-prime/screens/base_screen.rb +5 -1
  49. data/motion-prime/screens/sidebar_container_screen.rb +37 -22
  50. data/motion-prime/sections/base.rb +82 -16
  51. data/motion-prime/sections/form.rb +144 -26
  52. data/motion-prime/sections/form/base_field_section.rb +62 -29
  53. data/motion-prime/sections/form/base_header_section.rb +27 -0
  54. data/motion-prime/sections/form/date_field_section.rb +2 -17
  55. data/motion-prime/sections/form/password_field_section.rb +3 -17
  56. data/motion-prime/sections/form/select_field_section.rb +4 -35
  57. data/motion-prime/sections/form/string_field_section.rb +3 -29
  58. data/motion-prime/sections/form/submit_field_section.rb +1 -7
  59. data/motion-prime/sections/form/switch_field_section.rb +3 -23
  60. data/motion-prime/sections/form/text_field_section.rb +3 -33
  61. data/motion-prime/sections/form/text_with_button_field_section.rb +4 -40
  62. data/motion-prime/sections/tabbed.rb +25 -5
  63. data/motion-prime/sections/table.rb +86 -22
  64. data/motion-prime/sections/table/refresh_mixin.rb +3 -1
  65. data/motion-prime/styles/base.rb +7 -89
  66. data/motion-prime/styles/form.rb +116 -0
  67. data/motion-prime/support/dm_button.rb +32 -5
  68. data/motion-prime/support/dm_text_field.rb +31 -7
  69. data/motion-prime/support/dm_text_view.rb +6 -3
  70. data/motion-prime/support/dm_view_controller.rb +3 -3
  71. data/motion-prime/support/ui_search_bar_custom.rb +1 -1
  72. data/motion-prime/version.rb +1 -1
  73. data/motion-prime/views/layout.rb +18 -10
  74. data/motion-prime/views/styles.rb +19 -9
  75. data/motion-prime/views/view_builder.rb +18 -2
  76. data/motion-prime/views/view_styler.rb +59 -5
  77. data/spec/models/errors_spec.rb +3 -3
  78. data/travis.sh +3 -2
  79. metadata +28 -5
  80. data/motion-prime/elements/_text_height_mixin.rb +0 -17
  81. data/motion-prime/sections/form/table_field_section.rb +0 -51
@@ -0,0 +1,4 @@
1
+ module MotionPrime
2
+ end
3
+ ::MP = MotionPrime unless defined?(::MP)
4
+ ::Prime = MotionPrime unless defined?(::Prime)
@@ -37,14 +37,16 @@ module MotionPrime
37
37
  self.send("#{k}=", v) if self.respond_to?("#{k}=")
38
38
  end
39
39
 
40
- if @wrap_in_navigation = args[:navigation]
41
- self.add_navigation_controller
42
- end
40
+ @wrap_in_navigation = args[:navigation]
43
41
 
44
42
  self.on_init if respond_to?(:on_init)
45
43
  self
46
44
  end
47
45
 
46
+ def wrap_in_navigation?
47
+ @wrap_in_navigation
48
+ end
49
+
48
50
  def modal?
49
51
  !!self.modal
50
52
  end
@@ -61,10 +63,6 @@ module MotionPrime
61
63
  @navigation_controller = val
62
64
  end
63
65
 
64
- def add_navigation_controller
65
- self.navigation_controller = NavigationController.alloc.initWithRootViewController(self)
66
- end
67
-
68
66
  def title
69
67
  title = self.class.title
70
68
  title = self.instance_eval(&title) if title.is_a?(Proc)
@@ -97,19 +95,27 @@ module MotionPrime
97
95
  @activity_indicator_view.stopAnimating
98
96
  end
99
97
 
100
- def show_notice(message, time = 1.0)
98
+ def show_notice(message, time = 1.0, type = :notice)
99
+ hud_type = case type.to_s
100
+ when 'alert' then MBAlertViewHUDTypeExclamationMark
101
+ else MBAlertViewHUDTypeCheckmark
102
+ end
101
103
  MBHUDView.hudWithBody message,
102
- type: MBAlertViewHUDTypeCheckmark, hidesAfter: time, show: true
104
+ type: hud_type, hidesAfter: time, show: true
103
105
  end
104
106
 
105
107
  def refresh
106
- main_section.reload_data
108
+ main_section.try(:reload_data)
107
109
  end
108
110
 
109
111
  # Class methods
110
112
  module ClassMethods
111
- def title(t = nil)
112
- t ? @title = t : @title ||= self.to_s
113
+ def title(t = nil, &block)
114
+ if block_given?
115
+ @title = block
116
+ else
117
+ t ? @title = t : @title ||= self.to_s
118
+ end
113
119
  end
114
120
  def before_load(method_name)
115
121
  set_callback :load, :before, method_name
@@ -8,6 +8,10 @@ module MotionPrime
8
8
  navigationItem.leftBarButtonItem
9
9
  end
10
10
 
11
+ def remove_navigation_right_button(args = {})
12
+ navigationItem.setRightBarButtonItem(nil, animated: args[:animated])
13
+ end
14
+
11
15
  def set_navigation_right_button(title, args = {})
12
16
  navigationItem.rightBarButtonItem = create_navigation_button(title, args)
13
17
  end
@@ -20,11 +24,18 @@ module MotionPrime
20
24
  navigationItem.leftBarButtonItem = create_navigation_button(title, {action: :back}.merge(args))
21
25
  end
22
26
 
27
+ def set_navigation_back_or_menu(back_title = 'Back')
28
+ if parent_screen.is_a?(MotionPrime::SidebarContainerScreen)
29
+ set_navigation_left_button 'Menu', image: 'images/navigation/menu_button.png', action: :show_sidebar
30
+ else
31
+ set_navigation_back_button back_title, icon: 'images/navigation/back_icon.png'
32
+ end
33
+ end
34
+
23
35
  def create_navigation_button(title, args = {})
24
36
  args[:style] ||= UIBarButtonItemStylePlain
25
37
  args[:target] ||= self
26
38
  args[:action] ||= nil
27
-
28
39
  # TODO: Find better place for this code, may be just create custom control
29
40
  if args[:image]
30
41
  image = args[:image].uiimage
@@ -34,8 +45,7 @@ module MotionPrime
34
45
  face.on :touch do
35
46
  args[:action].to_proc.call(self)
36
47
  end
37
- button = UIBarButtonItem.alloc.initWithCustomView(face)
38
- button
48
+ UIBarButtonItem.alloc.initWithCustomView(face)
39
49
  elsif args[:icon]
40
50
  image = args[:icon].uiimage
41
51
  face = UIButton.buttonWithType UIButtonTypeCustom
@@ -46,11 +56,10 @@ module MotionPrime
46
56
  face.on :touch do
47
57
  args[:action].to_proc.call(self)
48
58
  end
49
- button = UIBarButtonItem.alloc.initWithCustomView(face)
50
- button
59
+ UIBarButtonItem.alloc.initWithCustomView(face)
51
60
  else
52
61
  UIBarButtonItem.alloc.initWithTitle(title,
53
- style: args[:style], target: args[:target], action: args[:action])
62
+ style: args[:style], target: args[:target], action: args[:action])
54
63
  end
55
64
  end
56
65
  end
@@ -6,13 +6,14 @@ module MotionPrime
6
6
 
7
7
  def open_screen(screen, args = {})
8
8
  # Apply properties to instance
9
- screen = setup_screen_for_open(screen, args)
10
- ensure_wrapper_controller_in_place(screen, args)
11
- screen.send(:on_screen_load) if screen.respond_to?(:on_screen_load)
12
- if args[:modal]
13
- present_modal_view_controller screen, (args.has_key?(:animated) ? args[:animated] : true)
14
- elsif has_navigation?
15
- push_view_controller screen, args
9
+ if args[:modal] || has_navigation?
10
+ screen = setup_screen_for_open(screen, args)
11
+ screen.send(:on_screen_load) if screen.respond_to?(:on_screen_load)
12
+ if args[:modal] || !has_navigation?
13
+ present_modal_view_controller screen, (args.has_key?(:animated) ? args[:animated] : true)
14
+ else
15
+ push_view_controller screen, args
16
+ end
16
17
  else
17
18
  app_delegate.open_screen(screen.main_controller)
18
19
  end
@@ -56,6 +57,13 @@ module MotionPrime
56
57
  navigation_controller.pushViewController(vc, animated: (args.has_key?(:animated) ? args[:animated] : true))
57
58
  end
58
59
 
60
+ def ensure_wrapper_controller_in_place(args = {})
61
+ # Wrap in a NavigationController?
62
+ if wrap_in_navigation? && !args[:modal]
63
+ add_navigation_controller
64
+ end
65
+ end
66
+
59
67
  protected
60
68
 
61
69
  def setup_screen_for_open(screen, args = {})
@@ -67,19 +75,10 @@ module MotionPrime
67
75
  screen.title = args[:title] if args[:title] && screen.respond_to?("title=")
68
76
  screen.modal = args[:modal] if args[:modal] && screen.respond_to?("modal=")
69
77
 
70
- # Wrap in a NavigationController?
71
- screen.add_navigation if args[:navigation] && screen.respond_to?(:add_navigation)
72
-
73
78
  # Return modified screen instance
74
79
  screen
75
80
  end
76
81
 
77
- def ensure_wrapper_controller_in_place(screen, args={})
78
- if !args[:modal] && screen.respond_to?(:navigation_controller=)
79
- screen.navigation_controller ||= navigation_controller
80
- end
81
- end
82
-
83
82
  def present_modal_view_controller(screen, animated)
84
83
  self.presentModalViewController(screen.main_controller, animated: animated)
85
84
  end
@@ -13,8 +13,12 @@ module MotionPrime
13
13
  def render
14
14
  end
15
15
 
16
+ def default_styles
17
+ [:base_screen, self.class_name_without_kvo.underscore.to_sym]
18
+ end
19
+
16
20
  def on_load
17
- setup view, styles: [:base_screen, self.class.name.underscore.to_sym] do
21
+ setup view, styles: default_styles do
18
22
  render
19
23
  end
20
24
  end
@@ -1,58 +1,73 @@
1
1
  module MotionPrime
2
- class SidebarContainerScreen < PKRevealController
3
-
2
+ class SidebarContainerScreen < RESideMenu
4
3
  include ::MotionPrime::ScreenBaseMixin
5
4
 
6
5
  def self.new(menu, content, options={})
7
- screen = self.revealControllerWithFrontViewController(nil, leftViewController: nil, options: nil)
6
+ screen = self.alloc.initWithContentViewController(nil, menuViewController: nil)
7
+ screen.backgroundImage = "images/sidebar/background.png".uiimage
8
+
9
+ full_width = UIScreen.mainScreen.bounds.size.width
10
+
11
+ if scale = options[:content_scale_value]
12
+ screen.contentViewScaleValue = scale
13
+ end
14
+ x_offset = options[:x_offset] || 45
15
+ screen.contentViewInPortraitOffsetCenterX = full_width*(1 + screen.contentViewScaleValue/2) - x_offset
16
+
17
+ if y_offset = options[:y_offset]
18
+ screen.contentViewInPortraitOffsetCenterY = UIScreen.mainScreen.bounds.size.height/2 + y_offset
19
+ end
20
+
8
21
  screen.on_create(options) if screen.respond_to?(:on_create)
9
22
  screen.menu_controller = menu unless menu.nil?
10
23
  screen.content_controller = content unless content.nil?
24
+
11
25
  screen
12
26
  end
13
27
 
14
28
  def show_sidebar
15
- show_controller menu_controller
29
+ self.presentMenuViewController
16
30
  end
17
31
 
18
32
  def hide_sidebar
19
- show_controller content_controller
33
+ self.hideMenuViewController
20
34
  end
21
35
 
22
36
  def menu_controller=(c)
23
- setLeftViewController prepare_controller(c),
24
- focusAfterChange: true, completion: default_completion_block
37
+ self.setMenuViewController prepare_controller(c)
25
38
  end
26
39
 
27
40
  def content_controller=(c)
28
- setFrontViewController prepare_controller(c),
29
- focusAfterChange: true, completion: default_completion_block
41
+ controller = prepare_controller(c)
42
+ if content_controller.nil?
43
+ self.setContentViewController controller
44
+ else
45
+ content_controller.viewControllers = [controller]
46
+ end
47
+ hide_sidebar
30
48
  end
31
49
 
32
50
  def menu_controller
33
- self.leftViewController
51
+ self.menuViewController
34
52
  end
35
53
 
36
54
  def content_controller
37
- self.frontViewController
55
+ self.contentViewController
38
56
  end
39
57
 
40
58
  private
41
59
 
42
- def show_controller(controller)
43
- showViewController controller, animated: true, completion: default_completion_block
44
- end
45
-
46
60
  def prepare_controller(controller)
47
- controller.on_screen_load if controller.respond_to?(:on_screen_load)
48
61
  controller = setup_screen_for_open(controller, {})
49
- ensure_wrapper_controller_in_place(controller, {})
50
- controller = controller.main_controller if controller.respond_to?(:main_controller)
62
+ if content_controller.nil?
63
+ controller.ensure_wrapper_controller_in_place
64
+ controller.send(:on_screen_load) if controller.respond_to?(:on_screen_load)
65
+ controller = controller.main_controller if controller.respond_to?(:main_controller)
66
+ else
67
+ controller.navigation_controller = content_controller
68
+ controller.send(:on_screen_load) if controller.respond_to?(:on_screen_load)
69
+ end
51
70
  controller
52
71
  end
53
-
54
- def default_completion_block
55
- -> (completed) { true }
56
- end
57
72
  end
58
73
  end
@@ -19,31 +19,73 @@ module MotionPrime
19
19
  include MotionPrime::HasNormalizer
20
20
 
21
21
  attr_accessor :screen, :model, :name, :options, :elements
22
- class_attribute :elements_options, :container_options
22
+ class_attribute :elements_options, :container_options, :keyboard_close_bindings
23
23
  define_callbacks :render
24
24
 
25
25
  def initialize(options = {})
26
26
  @options = options
27
27
  @model = options[:model]
28
28
  @name = options[:name] ||= default_name
29
- create_elements
30
- self.hide if container_options[:hidden]
29
+ @options_block = options[:block]
30
+ load_section
31
+ end
32
+
33
+ def container_options
34
+ @normalized_container_options or begin
35
+ # priority: class; from styles; passed directly
36
+ raw_container_options = self.class.container_options.try(:clone) || {}
37
+ passed_container_options = options.delete(:container) || {}
38
+ raw_container_options.merge!(passed_container_options)
39
+ @normalized_container_options = normalize_options(raw_container_options)
40
+
41
+ style_container_options = style_options.delete(:container) || {}
42
+ @normalized_container_options.merge!(style_container_options.except(*passed_container_options.keys))
43
+ end
44
+ end
45
+
46
+ def style_options
47
+ {}
31
48
  end
32
49
 
33
50
  def default_name
34
- self.class.name.demodulize.underscore.gsub(/\_section$/, '')
51
+ self.class_name_without_kvo.demodulize.underscore.gsub(/\_section$/, '')
35
52
  end
36
53
 
37
54
  def elements_options
38
55
  self.class.elements_options || {}
39
56
  end
40
57
 
58
+ def load_section
59
+ create_elements
60
+ self.instance_eval(&@options_block) if @options_block.is_a?(Proc)
61
+ end
62
+
63
+ def reload_section
64
+ self.elements.values.map(&:view).flatten.compact.each { |view| view.removeFromSuperview }
65
+ load_section
66
+ run_callbacks :render do
67
+ render!
68
+ end
69
+ end
70
+
41
71
  def create_elements
42
72
  self.elements = {}
43
73
  elements_options.each do |key, opts|
44
- next unless render_element?(key)
45
- options = build_options_for_element(opts)
46
- self.elements[key] = MotionPrime::BaseElement.factory(options.delete(:type), options)
74
+ add_element(key, opts)
75
+ end
76
+ end
77
+
78
+ def add_element(key, opts)
79
+ return unless render_element?(key)
80
+ index = opts.delete(:at)
81
+ options = build_options_for_element(opts)
82
+ options[:name] ||= key
83
+ options[:type] ||= :label
84
+ element = MotionPrime::BaseElement.factory(options.delete(:type), options)
85
+ if index
86
+ self.elements = Hash[self.elements.to_a.insert index, [key, element]]
87
+ else
88
+ self.elements[key] = element
47
89
  end
48
90
  end
49
91
 
@@ -60,7 +102,7 @@ module MotionPrime
60
102
 
61
103
  def cell
62
104
  first_element = elements.values.first
63
- first_element.view.superview
105
+ first_element.view.superview.superview
64
106
  end
65
107
 
66
108
  def render(container_options = {})
@@ -97,12 +139,6 @@ module MotionPrime
97
139
  end
98
140
  end
99
141
 
100
- def container_options
101
- @container_options ||= normalize_options(
102
- self.class.container_options.try(:clone) || {}
103
- )
104
- end
105
-
106
142
  def container_height
107
143
  container_options[:height] || DEFAULT_CONTENT_HEIGHT
108
144
  end
@@ -116,6 +152,12 @@ module MotionPrime
116
152
  def keyboard_will_show; end
117
153
  def keyboard_will_hide; end
118
154
 
155
+ def dealloc
156
+ NSNotificationCenter.defaultCenter.removeObserver self
157
+ self.delegate = nil if self.respond_to?(:delegate)
158
+ super
159
+ end
160
+
119
161
  def bind_keyboard_events
120
162
  NSNotificationCenter.defaultCenter.addObserver self,
121
163
  selector: :on_keyboard_show,
@@ -135,10 +177,31 @@ module MotionPrime
135
177
  object: nil
136
178
  end
137
179
 
180
+ def bind_keyboard_close
181
+ return unless self.class.keyboard_close_bindings.present?
182
+ Array.wrap(self.instance_eval(&self.class.keyboard_close_bindings[:tap_on])).each do |view|
183
+ gesture_recognizer = UITapGestureRecognizer.alloc.initWithTarget(self, action: :hide_keyboard)
184
+ view.addGestureRecognizer(gesture_recognizer)
185
+ gesture_recognizer.cancelsTouchesInView = false
186
+ end
187
+ end
188
+
189
+ def keyboard_close_bindings_options
190
+ @keyboard_close_bindings_options ||= normalize_options(self.class.keyboard_close_bindings.clone, self)
191
+ end
192
+
193
+ def hide_keyboard
194
+ elements = Array.wrap(keyboard_close_bindings_options[:elements])
195
+ views = Array.wrap(keyboard_close_bindings_options[:views])
196
+
197
+ elements.each { |el| views << el.view if %w[text_field text_view].include?(el.view_name) && el.view }
198
+ (views + Array.wrap(keyboard_close_bindings_options[:views])).compact.each(&:resignFirstResponder)
199
+ end
200
+
138
201
  class << self
139
202
  def element(name, options = {}, &block)
203
+ options[:name] ||= name
140
204
  options[:type] ||= :label
141
- options[:name] = name
142
205
  options[:block] = block
143
206
  self.elements_options ||= {}
144
207
  self.elements_options[name] = options
@@ -153,8 +216,11 @@ module MotionPrime
153
216
  def after_render(method_name)
154
217
  set_callback :render, :after, method_name
155
218
  end
219
+ def bind_keyboard_close(options)
220
+ self.keyboard_close_bindings = options
221
+ end
156
222
  end
157
223
  after_render :bind_keyboard_events
158
-
224
+ after_render :bind_keyboard_close
159
225
  end
160
226
  end