motion-prime 0.3.1 → 0.3.2

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 (43) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +7 -3
  3. data/Gemfile.lock +1 -1
  4. data/README.md +1 -1
  5. data/ROADMAP.md +12 -0
  6. data/motion-prime/api_client.rb +1 -0
  7. data/motion-prime/app_delegate.rb +1 -1
  8. data/motion-prime/elements/_content_padding_mixin.rb +49 -0
  9. data/motion-prime/elements/_field_dimensions_mixin.rb +37 -21
  10. data/motion-prime/elements/_text_dimensions_mixin.rb +0 -4
  11. data/motion-prime/elements/base.rb +20 -7
  12. data/motion-prime/elements/button.rb +2 -1
  13. data/motion-prime/elements/draw/label.rb +19 -8
  14. data/motion-prime/elements/error_message.rb +2 -1
  15. data/motion-prime/elements/image.rb +1 -0
  16. data/motion-prime/elements/label.rb +10 -10
  17. data/motion-prime/elements/text_field.rb +2 -0
  18. data/motion-prime/models/bag.rb +8 -3
  19. data/motion-prime/models/model.rb +1 -1
  20. data/motion-prime/models/sync.rb +17 -8
  21. data/motion-prime/screens/_aliases_mixin.rb +2 -0
  22. data/motion-prime/screens/_base_mixin.rb +19 -52
  23. data/motion-prime/screens/_navigation_mixin.rb +31 -29
  24. data/motion-prime/screens/base_screen.rb +8 -0
  25. data/motion-prime/screens/extensions/_indicators_mixin.rb +26 -0
  26. data/motion-prime/screens/{_navigation_bar_mixin.rb → extensions/_navigation_bar_mixin.rb} +0 -0
  27. data/motion-prime/screens/sidebar_container_screen.rb +1 -1
  28. data/motion-prime/sections/base.rb +1 -1
  29. data/motion-prime/sections/form.rb +5 -2
  30. data/motion-prime/sections/form/base_field_section.rb +2 -2
  31. data/motion-prime/support/_key_value_store.rb +1 -1
  32. data/motion-prime/support/_padding_attribute.rb +60 -0
  33. data/motion-prime/support/dm_button.rb +20 -20
  34. data/motion-prime/support/dm_text_field.rb +12 -17
  35. data/motion-prime/support/dm_text_view.rb +1 -1
  36. data/motion-prime/support/mp_label.rb +9 -0
  37. data/motion-prime/version.rb +1 -1
  38. data/motion-prime/views/layout.rb +1 -0
  39. data/motion-prime/views/styles.rb +1 -1
  40. data/motion-prime/views/view_styler.rb +5 -1
  41. data/spec/helpers/models.rb +8 -1
  42. data/travis.sh +1 -3
  43. metadata +8 -3
@@ -74,7 +74,7 @@ module MotionPrime
74
74
  # User.new(name: "Bob", age: 10)
75
75
  #
76
76
  # @return MotionPrime::BaseModel unsaved model
77
- def new(data = {})
77
+ def new(data = {}, options = {})
78
78
  data.keys.each { |k|
79
79
  unless self.attributes.member? k.to_sym
80
80
  raise StoreError, "'#{k}' is not a defined attribute for this model"
@@ -116,6 +116,7 @@ module MotionPrime
116
116
  self.send(:"#{key}=", value)
117
117
  end
118
118
  end
119
+ self
119
120
  end
120
121
 
121
122
  def fetch_associations(sync_options = {}, &block)
@@ -153,8 +154,8 @@ module MotionPrime
153
154
 
154
155
  use_callback = block_given?
155
156
  puts "SYNC: started sync for #{key} in #{self.class_name_without_kvo}"
156
- api_client.get normalize_sync_url(options[:sync_url]) do |data, status_code|
157
- data = data[options[:sync_key]] if options[:sync_key]
157
+ api_client.get normalize_sync_url(options[:sync_url]) do |response, status_code|
158
+ data = options.has_key?(:sync_key) ? response[options[:sync_key]] : response
158
159
  if data
159
160
  # Update/Create existing records
160
161
  data.each do |attributes|
@@ -174,10 +175,10 @@ module MotionPrime
174
175
  end
175
176
  save if sync_options[:save]
176
177
  puts "SYNC: finished sync for #{key} in #{self.class_name_without_kvo}"
177
- block.call(data, status_code) if use_callback
178
+ block.call(data, status_code, response) if use_callback
178
179
  else
179
180
  puts "SYNC ERROR: failed sync for #{key} in #{self.class_name_without_kvo}"
180
- block.call(data, status_code) if use_callback
181
+ block.call(data, status_code, response) if use_callback
181
182
  end
182
183
  end
183
184
  end
@@ -185,8 +186,8 @@ module MotionPrime
185
186
  def fetch_has_one(key, options = {}, &block)
186
187
  use_callback = block_given?
187
188
  puts "SYNC: started sync for #{key} in #{self.class_name_without_kvo}"
188
- api_client.get normalize_sync_url(options[:sync_url]) do |data, status_code|
189
- data = data[options[:sync_key]] if options[:sync_key]
189
+ api_client.get normalize_sync_url(options[:sync_url]) do |response, status_code|
190
+ data = options.has_key?(:sync_key) ? response[options[:sync_key]] : response
190
191
  if data.present?
191
192
  model = self.send(key)
192
193
  unless model
@@ -195,10 +196,10 @@ module MotionPrime
195
196
  end
196
197
  model.fetch_with_attributes(data)
197
198
  model.save if sync_options[:save]
198
- block.call(data, status_code) if use_callback
199
+ block.call(data, status_code, response) if use_callback
199
200
  else
200
201
  puts "SYNC ERROR: failed sync for #{key} in #{self.class_name_without_kvo}"
201
- block.call(data, status_code) if use_callback
202
+ block.call(data, status_code, response) if use_callback
202
203
  end
203
204
  end
204
205
  end
@@ -240,6 +241,14 @@ module MotionPrime
240
241
  end
241
242
 
242
243
  module ModelSyncClassMethods
244
+ def new(data = {}, options = {})
245
+ model = super
246
+ if fetch_attributes = options[:fetch_attributes]
247
+ model.fetch_with_attributes(fetch_attributes)
248
+ end
249
+ model
250
+ end
251
+
243
252
  def sync_url(url = nil, &block)
244
253
  if url || block_given?
245
254
  self._sync_url = url || block
@@ -28,5 +28,7 @@ module MotionPrime
28
28
  end
29
29
 
30
30
  def on_rotate; end;
31
+
32
+ def on_load; end
31
33
  end
32
34
  end
@@ -1,7 +1,6 @@
1
1
  motion_require "./_aliases_mixin"
2
2
  motion_require "./_orientations_mixin"
3
3
  motion_require "./_navigation_mixin"
4
- motion_require "./_navigation_bar_mixin"
5
4
  module MotionPrime
6
5
  module ScreenBaseMixin
7
6
  extend ::MotionSupport::Concern
@@ -10,16 +9,25 @@ module MotionPrime
10
9
  include MotionPrime::ScreenAliasesMixin
11
10
  include MotionPrime::ScreenOrientationsMixin
12
11
  include MotionPrime::ScreenNavigationMixin
13
- include MotionPrime::ScreenNavigationBarMixin
14
12
 
15
- attr_accessor :parent_screen, :modal, :params, :main_section
13
+ attr_accessor :parent_screen, :modal, :params, :main_section, :options
16
14
  class_attribute :current_screen
17
15
 
18
16
  included do
19
17
  define_callbacks :load
20
18
  end
21
19
 
22
- def on_load; end
20
+ def app_delegate
21
+ UIApplication.sharedApplication.delegate
22
+ end
23
+
24
+ def show_sidebar
25
+ app_delegate.show_sidebar
26
+ end
27
+
28
+ def hide_sidebar
29
+ app_delegate.hide_sidebar
30
+ end
23
31
 
24
32
  def on_screen_load
25
33
  run_callbacks :load do
@@ -27,42 +35,28 @@ module MotionPrime
27
35
  end
28
36
  end
29
37
 
30
- def on_create(args = {})
38
+ # Setup the screen, will be called when you run DMViewController.new
39
+ # @param options [hash] Options passed to setup
40
+ # @return [MotionPrime::BaseScreen] Ready to use screen
41
+ def on_create(options = {})
31
42
  unless self.is_a?(UIViewController)
32
43
  raise StandardError.new("ERROR: Screens must extend UIViewController.")
33
44
  end
34
45
 
35
- self.params = args[:params] || {}
36
- args.each do |k, v|
46
+ self.options = options
47
+ self.params = options[:params] || {}
48
+ options.each do |k, v|
37
49
  self.send("#{k}=", v) if self.respond_to?("#{k}=")
38
50
  end
39
51
 
40
- @wrap_in_navigation = args[:navigation]
41
-
42
52
  self.on_init if respond_to?(:on_init)
43
53
  self
44
54
  end
45
55
 
46
- def wrap_in_navigation?
47
- @wrap_in_navigation
48
- end
49
-
50
56
  def modal?
51
57
  !!self.modal
52
58
  end
53
59
 
54
- def has_navigation?
55
- !navigation_controller.nil?
56
- end
57
-
58
- def navigation_controller
59
- @navigation_controller ||= self.navigationController
60
- end
61
-
62
- def navigation_controller=(val)
63
- @navigation_controller = val
64
- end
65
-
66
60
  def title
67
61
  title = self.class.title
68
62
  title = self.instance_eval(&title) if title.is_a?(Proc)
@@ -71,39 +65,12 @@ module MotionPrime
71
65
 
72
66
  def title=(new_title)
73
67
  self.class.title(new_title)
74
- super
75
68
  end
76
69
 
77
70
  def main_controller
78
71
  has_navigation? ? navigation_controller : self
79
72
  end
80
73
 
81
- # ACTIVITY INDICATOR
82
- # ---------------------
83
-
84
- def show_activity_indicator
85
- if @activity_indicator_view.nil?
86
- @activity_indicator_view = UIActivityIndicatorView.gray
87
- @activity_indicator_view.center = CGPointMake(view.center.x, view.center.y - 50)
88
- view.addSubview @activity_indicator_view
89
- end
90
- @activity_indicator_view.startAnimating
91
- end
92
-
93
- def hide_activity_indicator
94
- return unless @activity_indicator_view
95
- @activity_indicator_view.stopAnimating
96
- end
97
-
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
103
- MBHUDView.hudWithBody message,
104
- type: hud_type, hidesAfter: time, show: true
105
- end
106
-
107
74
  def refresh
108
75
  main_section.try(:reload_data)
109
76
  end
@@ -1,17 +1,14 @@
1
1
  module MotionPrime
2
2
  module ScreenNavigationMixin
3
- def app_delegate
4
- UIApplication.sharedApplication.delegate
5
- end
6
-
7
- def show_sidebar
8
- app_delegate.show_sidebar
9
- end
10
-
11
- def hide_sidebar
12
- app_delegate.hide_sidebar
13
- end
14
-
3
+ # Open screen as child for current screen if current screen have navigation or new screen is modal.
4
+ # Otherwise will create screen using app_delegate.open_screen.
5
+ # Available options:
6
+ # animated: open screen with animation.
7
+ # modal: open screen as model
8
+
9
+ # @param screen [MotionPrime::BaseScreen] Screen to be opened
10
+ # @param args [Hash] Options for opening screen
11
+ # @return [MotionPrime::BaseScreen]
15
12
  def open_screen(screen, args = {})
16
13
  if args[:modal] || has_navigation?
17
14
  screen = setup_screen_for_open(screen, args)
@@ -25,8 +22,10 @@ module MotionPrime
25
22
  else
26
23
  app_delegate.open_screen(screen.main_controller)
27
24
  end
25
+ screen
28
26
  end
29
27
 
28
+
30
29
  def close_screen(args = {})
31
30
  args[:animated] = args.has_key?(:animated) ? args[:animated] : true
32
31
  # Pop current view, maybe with arguments, if in navigation controller
@@ -47,13 +46,28 @@ module MotionPrime
47
46
  end
48
47
  end
49
48
 
50
- def ensure_wrapper_controller_in_place(args = {})
51
- # Wrap in a NavigationController?
52
- if wrap_in_navigation? && !args[:modal]
53
- add_navigation_controller
49
+ def wrap_in_navigation?
50
+ options[:navigation]
51
+ end
52
+
53
+ def wrap_in_navigation
54
+ if wrap_in_navigation?
55
+ wrap_in_navigation!
54
56
  end
55
57
  end
56
58
 
59
+ def has_navigation?
60
+ !navigation_controller.nil?
61
+ end
62
+
63
+ def navigation_controller
64
+ @navigation_controller ||= self.navigationController
65
+ end
66
+
67
+ def navigation_controller=(val)
68
+ @navigation_controller = val
69
+ end
70
+
57
71
  private
58
72
  def setup_screen_for_open(screen, args = {})
59
73
  # Instantiate screen if given a class
@@ -92,19 +106,7 @@ module MotionPrime
92
106
  send_on_return(args)
93
107
  end
94
108
 
95
- def has_navigation?
96
- !navigation_controller.nil?
97
- end
98
-
99
- def navigation_controller
100
- @navigation_controller ||= self.navigationController
101
- end
102
-
103
- def navigation_controller=(val)
104
- @navigation_controller = val
105
- end
106
-
107
- def add_navigation_controller
109
+ def wrap_in_navigation!
108
110
  self.navigation_controller = NavigationController.alloc.initWithRootViewController(self)
109
111
  end
110
112
  end
@@ -1,12 +1,20 @@
1
1
  motion_require '../support/dm_view_controller.rb'
2
2
  motion_require '../views/layout.rb'
3
3
  motion_require '../screens/_base_mixin.rb'
4
+ motion_require './extensions/_indicators_mixin'
5
+ motion_require './extensions/_navigation_bar_mixin'
4
6
  motion_require '../helpers/has_authorization'
5
7
  motion_require '../helpers/has_search_bar'
6
8
  module MotionPrime
7
9
  class BaseScreen < DMViewController
8
10
  include Layout
9
11
  include ScreenBaseMixin
12
+
13
+ # extensions
14
+ include ScreenIndicatorsMixin
15
+ include ScreenNavigationBarMixin
16
+
17
+ # helpers
10
18
  include HasAuthorization
11
19
  include HasSearchBar
12
20
 
@@ -0,0 +1,26 @@
1
+ module MotionPrime
2
+ module ScreenIndicatorsMixin
3
+ def show_activity_indicator
4
+ if @activity_indicator_view.nil?
5
+ @activity_indicator_view = UIActivityIndicatorView.gray
6
+ @activity_indicator_view.center = CGPointMake(view.center.x, view.center.y - 50)
7
+ view.addSubview @activity_indicator_view
8
+ end
9
+ @activity_indicator_view.startAnimating
10
+ end
11
+
12
+ def hide_activity_indicator
13
+ return unless @activity_indicator_view
14
+ @activity_indicator_view.stopAnimating
15
+ end
16
+
17
+ def show_notice(message, time = 1.0, type = :notice)
18
+ hud_type = case type.to_s
19
+ when 'alert' then MBAlertViewHUDTypeExclamationMark
20
+ else MBAlertViewHUDTypeCheckmark
21
+ end
22
+ MBHUDView.hudWithBody message,
23
+ type: hud_type, hidesAfter: time, show: true
24
+ end
25
+ end
26
+ end
@@ -60,7 +60,7 @@ module MotionPrime
60
60
  def prepare_controller(controller)
61
61
  controller = setup_screen_for_open(controller, {})
62
62
  if content_controller.nil?
63
- controller.ensure_wrapper_controller_in_place
63
+ controller.wrap_in_navigation if controller.respond_to?(:wrap_in_navigation)
64
64
  controller.send(:on_screen_load) if controller.respond_to?(:on_screen_load)
65
65
  controller = controller.main_controller if controller.respond_to?(:main_controller)
66
66
  else
@@ -83,7 +83,7 @@ module MotionPrime
83
83
  index = opts.delete(:at)
84
84
  options = build_options_for_element(opts)
85
85
  options[:name] ||= key
86
- options[:type] ||= options[:text] ? :label : :view
86
+ options[:type] ||= (options[:text] || options[:attributed_text_options]) ? :label : :view
87
87
  element = MotionPrime::BaseElement.factory(options.delete(:type), options)
88
88
  if index
89
89
  self.elements = Hash[self.elements.to_a.insert index, [key, element]]
@@ -147,7 +147,7 @@ module MotionPrime
147
147
  end
148
148
 
149
149
  def keyboard_will_show
150
- return if table_view.contentSize.height + table_view.top <= UIScreen.mainScreen.bounds.size.height
150
+ return if table_view.contentSize.height + table_view.top <= UIScreen.mainScreen.bounds.size.height - KEYBOARD_HEIGHT_PORTRAIT
151
151
  current_inset = table_view.contentInset
152
152
  current_inset.bottom = KEYBOARD_HEIGHT_PORTRAIT + (self.table_element.computed_options[:bottom_content_offset] || 0)
153
153
  table_view.contentInset = current_inset
@@ -164,13 +164,16 @@ module MotionPrime
164
164
  def on_input_edit(text_field); end
165
165
  def on_input_return(text_field)
166
166
  text_field.resignFirstResponder
167
- end;
167
+ end
168
168
  def textFieldShouldReturn(text_field)
169
169
  on_input_return(text_field)
170
170
  end
171
171
  def textFieldDidBeginEditing(text_field)
172
172
  on_input_edit(text_field)
173
173
  end
174
+ def textViewDidBeginEditing(text_view)
175
+ on_input_edit(text_view)
176
+ end
174
177
 
175
178
  def textView(text_view, shouldChangeTextInRange:range, replacementText:string)
176
179
  limit = (self.class.text_view_limits || {}).find do |field_name, limit|
@@ -56,8 +56,8 @@ module MotionPrime
56
56
  # focus on text field
57
57
  return unless begin_editing
58
58
  elements.values.each do |element|
59
- if element.view.is_a?(UITextField)
60
- element.view.becomeFirstResponder && return
59
+ if element.view.is_a?(UITextField) || element.view.is_a?(UITextView)
60
+ element.view.becomeFirstResponder and return
61
61
  end
62
62
  end
63
63
  self
@@ -1,5 +1,5 @@
1
1
  module MotionPrime
2
- module KeyValueStore
2
+ module SupportKeyValueStore
3
3
  extend ::MotionSupport::Concern
4
4
 
5
5
  # Key-Value accessors
@@ -0,0 +1,60 @@
1
+ module MotionPrime
2
+ module SupportPaddingAttribute
3
+ extend ::MotionSupport::Concern
4
+
5
+ included do
6
+ attr_accessor :paddingLeft, :paddingRight, :paddingTop, :paddingBottom, :padding
7
+ end
8
+
9
+ module ClassMethods
10
+ def default_padding_top
11
+ 0
12
+ end
13
+
14
+ def default_padding_left
15
+ 0
16
+ end
17
+
18
+ def default_padding_right
19
+ 0
20
+ end
21
+
22
+ def default_padding_bottom
23
+ 0
24
+ end
25
+ end
26
+
27
+ def padding_left
28
+ self.paddingLeft || self.padding || self.class.default_padding_left
29
+ end
30
+
31
+ def padding_right
32
+ self.paddingRight || self.padding || self.class.default_padding_right
33
+ end
34
+
35
+ def padding_top
36
+ self.paddingTop || self.padding || self.class.default_padding_top
37
+ end
38
+
39
+ def padding_bottom
40
+ self.paddingBottom || self.padding || self.class.default_padding_bottom
41
+ end
42
+
43
+ def padding_insets
44
+ UIEdgeInsetsMake(padding_top, padding_left, padding_bottom, padding_right)
45
+ end
46
+
47
+ def apply_padding(rect)
48
+ return unless apply_padding?
49
+ apply_padding!(rect)
50
+ end
51
+
52
+ def apply_padding!(rect)
53
+ raise "requires implementation"
54
+ end
55
+
56
+ def apply_padding?
57
+ ![padding_top, padding_left, padding_right, padding_bottom].all?(&:zero?)
58
+ end
59
+ end
60
+ end