motion-prime 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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