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
@@ -1,26 +1,12 @@
1
1
  module MotionPrime
2
2
  class PasswordFieldSection < BaseFieldSection
3
3
  element :label, type: :label do
4
- {
5
- styles: [
6
- :base_field_label,
7
- :base_password_field_label,
8
- :"#{form_name}_field_label",
9
- :"#{form_name}_#{name}_field_label"
10
- ]
11
- }.merge(options[:label] || {})
4
+ options[:label] || {}
12
5
  end
13
6
  element :input, type: :text_field do
14
- {
15
- styles: [
16
- :base_field_input,
17
- :base_password_field_input,
18
- :"#{form_name}_field_input",
19
- :"#{form_name}_#{name}_field_input"
20
- ],
21
- secureTextEntry: true
22
- }.merge(options[:input] || {})
7
+ options[:input] || {}
23
8
  end
9
+ element :error_message, type: :error_message, text: proc { all_errors.join("\n") if observing_errors? }
24
10
  after_render :bind_text_input
25
11
  end
26
12
  end
@@ -1,46 +1,15 @@
1
1
  module MotionPrime
2
2
  class SelectFieldSection < BaseFieldSection
3
3
  element :label, type: :label do
4
- {
5
- styles: [
6
- :base_field_label,
7
- :base_select_field_label,
8
- :"#{form_name}_field_label",
9
- :"#{form_name}_#{name}_field_label"
10
- ]
11
- }.merge(options[:label] || {})
4
+ options[:label] || {}
12
5
  end
13
6
  element :button, type: :button do
14
- {
15
- styles: [
16
- :base_select_field_button,
17
- :"#{form_name}_field_button",
18
- :"#{form_name}_#{name}_field_button"
19
- ],
20
- }.merge(options[:button] || {})
7
+ options[:button] || {}
21
8
  end
22
9
  element :arrow, type: :image do
23
- {
24
- styles: [
25
- :base_select_field_arrow,
26
- :"#{form_name}_field_arrow",
27
- :"#{form_name}_#{name}_field_arrow"
28
- ],
29
- }.merge(options[:arrow] || {})
30
- end
31
- element :error_message, type: :error_message do
32
- {
33
- styles: [
34
- :base_field_label,
35
- :base_string_field_label,
36
- :"#{form_name}_field_label",
37
- :"#{form_name}_#{name}_field_label",
38
- :base_error_message
39
- ],
40
- hidden: proc { form.model && form.model.errors[name].blank? },
41
- text: proc { form.model and form.model.errors[name].join("\n") }
42
- }
10
+ options[:arrow] || {}
43
11
  end
12
+ element :error_message, type: :error_message, text: proc { observing_errors? and all_errors.join("\n") }
44
13
 
45
14
  after_render :bind_select_button
46
15
 
@@ -1,40 +1,14 @@
1
1
  module MotionPrime
2
2
  class StringFieldSection < BaseFieldSection
3
3
  element :label, type: :label do
4
- {
5
- styles: [
6
- :base_field_label,
7
- :base_string_field_label,
8
- :"#{form_name}_field_label",
9
- :"#{form_name}_#{name}_field_label"
10
- ]
11
- }.merge(options[:label] || {})
4
+ options[:label] || {}
12
5
  end
13
6
 
14
7
  element :input, type: :text_field do
15
- styles = [
16
- :base_field_input,
17
- :base_string_field_input,
18
- :"#{form_name}_field_input",
19
- :"#{form_name}_#{name}_field_input"
20
- ]
21
- styles << :base_field_input_with_errors if form.model && form.model.errors[name].present?
22
- {styles: styles}.merge(options[:input] || {})
8
+ options[:input] || {}
23
9
  end
24
10
 
25
- element :error_message, type: :error_message do
26
- {
27
- styles: [
28
- :base_field_label,
29
- :base_string_field_label,
30
- :"#{form_name}_field_label",
31
- :"#{form_name}_#{name}_field_label",
32
- :base_error_message
33
- ],
34
- hidden: proc { form.model && form.model.errors[name].blank? },
35
- text: proc { form.model and form.model.errors[name].join("\n") }
36
- }
37
- end
11
+ element :error_message, type: :error_message, text: proc { all_errors.join("\n") if observing_errors? }
38
12
  after_render :bind_text_input
39
13
  end
40
14
  end
@@ -1,13 +1,7 @@
1
1
  module MotionPrime
2
2
  class SubmitFieldSection < BaseFieldSection
3
3
  element :submit, type: :button do
4
- {
5
- styles: [
6
- :base_submit_button,
7
- :"#{form_name}_submit_button",
8
- :"#{form_name}_#{name}_button"
9
- ]
10
- }.merge(title: options[:title])
4
+ {title: options[:title]}
11
5
  end
12
6
  after_render :bind_submit
13
7
 
@@ -1,33 +1,13 @@
1
1
  module MotionPrime
2
2
  class SwitchFieldSection < BaseFieldSection
3
3
  element :label, type: :label do
4
- {
5
- styles: [
6
- :base_field_label,
7
- :base_switch_label,
8
- :"#{form_name}_switch_label",
9
- :"#{form_name}_#{name}_switch_label"
10
- ]
11
- }.merge(options[:label] || {})
4
+ options[:label] || {}
12
5
  end
13
6
  element :input, type: :switch do
14
- {
15
- styles: [
16
- :base_field_switch,
17
- :"#{form_name}_field_switch",
18
- :"#{form_name}_#{name}_field_switch"
19
- ]
20
- }.merge(options[:input] || {})
7
+ options[:input] || {}
21
8
  end
22
9
  element :hint, type: :label do
23
- {
24
- styles: [
25
- :base_field_label,
26
- :base_switch_hint,
27
- :"#{form_name}_switch_hint",
28
- :"#{form_name}_#{name}_switch_hint"
29
- ]
30
- }.merge(options[:hint] || {})
10
+ options[:hint] || {}
31
11
  end
32
12
  end
33
13
  end
@@ -1,43 +1,13 @@
1
1
  module MotionPrime
2
2
  class TextFieldSection < BaseFieldSection
3
3
  element :label, type: :label do
4
- {
5
- styles: [
6
- :base_field_label,
7
- :base_text_field_label,
8
- :"#{form_name}_field_label",
9
- :"#{form_name}_#{name}_field_label"
10
- ]
11
- }.merge(options[:label] || {})
4
+ options[:label] || {}
12
5
  end
13
6
  element :input, type: :text_view do
14
- styles = [
15
- :base_field_input,
16
- :base_text_field_input,
17
- :"#{form_name}_field_input",
18
- :"#{form_name}_#{name}_field_input"
19
- ]
20
- styles << :base_field_input_with_errors if form.model && form.model.errors[name].present?
21
-
22
- {
23
- styles: styles,
24
- editable: true
25
- }.merge(options[:input] || {})
7
+ {editable: true}.merge(options[:input] || {})
26
8
  end
27
9
 
28
- element :error_message, type: :error_message do
29
- {
30
- styles: [
31
- :base_field_label,
32
- :base_string_field_label,
33
- :"#{form_name}_field_label",
34
- :"#{form_name}_#{name}_field_label",
35
- :base_error_message
36
- ],
37
- hidden: proc { form.model && form.model.errors[name].blank? },
38
- text: proc { form.model and form.model.errors[name].join("\n") }
39
- }
40
- end
10
+ element :error_message, type: :error_message, text: proc { observing_errors? and all_errors.join("\n") }
41
11
  after_render :bind_text_input
42
12
  end
43
13
  end
@@ -1,51 +1,15 @@
1
1
  module MotionPrime
2
2
  class TextWithButtonFieldSection < BaseFieldSection
3
3
  element :label, type: :label do
4
- {
5
- styles: [
6
- :base_field_label,
7
- :base_text_field_label,
8
- :"#{form_name}_field_label",
9
- :"#{form_name}_#{name}_field_label"
10
- ]
11
- }.merge(options[:label] || {})
4
+ options[:label] || {}
12
5
  end
13
6
  element :input, type: :text_view do
14
- styles = [
15
- :base_field_input,
16
- :base_text_field_input,
17
- :"#{form_name}_field_input",
18
- :"#{form_name}_#{name}_field_input"
19
- ]
20
- styles << :base_field_input_with_errors if form.model && form.model.errors[name].present?
21
-
22
- {
23
- styles: styles,
24
- editable: true
25
- }.merge(options[:input] || {})
7
+ {editable: true}.merge(options[:input] || {})
26
8
  end
27
9
  element :button, type: :button do
28
- {
29
- styles: [
30
- :base_text_button,
31
- :"#{form_name}_text_button",
32
- :"#{form_name}_#{name}_text_button"
33
- ]
34
- }.merge(options[:button] || {}).except(:action)
35
- end
36
- element :error_message, type: :error_message do
37
- {
38
- styles: [
39
- :base_field_label,
40
- :base_string_field_label,
41
- :"#{form_name}_field_label",
42
- :"#{form_name}_#{name}_field_label",
43
- :base_error_message
44
- ],
45
- hidden: proc { form.model && form.model.errors[name].blank? },
46
- text: proc { form.model and form.model.errors[name].join("\n") }
47
- }
10
+ (options[:button] || {}).except(:action)
48
11
  end
12
+ element :error_message, type: :error_message, text: proc { observing_errors? and all_errors.join("\n") }
49
13
 
50
14
  after_render :bind_text_input
51
15
  after_render :bind_button_action
@@ -10,6 +10,7 @@ module MotionPrime
10
10
  # # e.g. in this sample: InfoTabSection.new(model: model).render(to: screen)
11
11
  # end
12
12
  #
13
+ include MotionPrime::HasNormalizer
13
14
 
14
15
  class_attribute :tabs_options, :tabs_default, :tabs_indexes
15
16
  attr_accessor :tab_pages
@@ -21,7 +22,7 @@ module MotionPrime
21
22
  after_render :render_tab_controls
22
23
 
23
24
  def tab_options
24
- @tab_options ||= self.class.tabs_options
25
+ @tab_options ||= normalize_options(self.class.tabs_options.clone)
25
26
  end
26
27
 
27
28
  def tab_control_items
@@ -32,7 +33,6 @@ module MotionPrime
32
33
  @tab_default ||= self.class.tabs_default || 0
33
34
  end
34
35
 
35
-
36
36
  # Make tab button disabled by index
37
37
  # @param Fixnum index
38
38
  def disable_at(index)
@@ -58,15 +58,35 @@ module MotionPrime
58
58
  # on click to segment tab
59
59
  # @param UISegemtedControl control
60
60
  def on_click(*control)
61
+ show_at_index(control.selectedSegment)
62
+ end
63
+
64
+ def show_at_index(index)
61
65
  @tab_pages.each_with_index do |page, i|
62
- page.hide if control.selectedSegment != i
66
+ page.hide if index != i
63
67
  end
64
- @tab_pages[control.selectedSegment].show
68
+ view(:control).setSelectedSegmentIndex index
69
+ @tab_pages[index].show
70
+ end
71
+
72
+ def show_by_key(key)
73
+ id = self.class.tabs_indexes[key]
74
+ show_at_index(id)
75
+ end
76
+
77
+ def tab_page(key)
78
+ id = self.class.tabs_indexes[key]
79
+ tab_pages[id]
80
+ end
81
+
82
+ def set_title(key, title)
83
+ id = self.class.tabs_indexes[key]
84
+ view(:control).setTitle(title, forSegmentAtIndex: id)
65
85
  end
66
86
 
67
87
  class << self
68
88
  def tab(id, options = {})
69
- options[:name] = id.to_s.titleize
89
+ options[:name] ||= id.to_s.titleize
70
90
  options[:id] = id
71
91
 
72
92
  self.tabs_indexes ||= {}
@@ -4,7 +4,7 @@ module MotionPrime
4
4
  include TableSectionRefreshMixin
5
5
  include HasSearchBar
6
6
 
7
- attr_accessor :table_view, :did_appear
7
+ attr_accessor :table_element, :did_appear
8
8
  before_render :render_table
9
9
 
10
10
  def table_data
@@ -16,18 +16,32 @@ module MotionPrime
16
16
  end
17
17
 
18
18
  def data_stamp_for(id)
19
- @data_stamp[id.to_i]
19
+ @data_stamp[id]
20
20
  end
21
21
 
22
22
  def set_data_stamp(cell_ids)
23
- @data_stamp ||= []
24
- cell_ids.each { |id| @data_stamp[id] = Time.now.to_f }
23
+ @data_stamp ||= {}
24
+ [*cell_ids].each { |id| @data_stamp[id] = Time.now.to_f }
25
+ end
26
+
27
+ def reset_data_stamps
28
+ keys = data.each_with_index.map do |row, id|
29
+ if row.is_a?(Array)
30
+ section = id
31
+ rows = (0...row.count)
32
+ else
33
+ section = 0
34
+ rows = [id]
35
+ end
36
+ rows.map { |row| "#{section}_#{row}" }
37
+ end.flatten
38
+ set_data_stamp(keys)
25
39
  end
26
40
 
27
41
  def reload_data
28
42
  @did_appear = false
29
43
  @data = nil
30
- set_data_stamp((0..elements_options.count-1).to_a)
44
+ reset_data_stamps
31
45
  table_view.reloadData
32
46
  end
33
47
 
@@ -38,15 +52,46 @@ module MotionPrime
38
52
  end
39
53
 
40
54
  def render_table
41
- set_data_stamp((0..elements_options.count-1))
55
+ reset_data_stamps
56
+ options = {
57
+ styles: table_styles,
58
+ delegate: self,
59
+ data_source: self,
60
+ style: (UITableViewStyleGrouped unless flat_data?)
61
+ }
62
+ self.table_element = screen.table_view(options)
63
+ end
64
+
65
+ def table_view
66
+ table_element.view
67
+ end
68
+
69
+ def hide
70
+ table_view.try(:hide)
71
+ end
72
+
73
+ def show
74
+ table_view.try(:show)
75
+ end
76
+
77
+ def numberOfSectionsInTableView(tableView)
78
+ number_of_sections
79
+ end
80
+
81
+ def number_of_sections
82
+ has_many_sections? ? data.count : 1
83
+ end
42
84
 
43
- self.table_view = screen.table_view(
44
- styles: table_styles, delegate: self, data_source: self
45
- ).view
85
+ def has_many_sections?
86
+ data.any? && data.first.is_a?(Array)
87
+ end
88
+
89
+ def row_by_index(index)
90
+ rows_for_section(index.section)[index.row]
46
91
  end
47
92
 
48
93
  def render_cell(index, table)
49
- item = data[index.row]
94
+ item = row_by_index(index)
50
95
 
51
96
  # define default styles for cell
52
97
  styles = [:"#{name}_cell"]
@@ -67,7 +112,7 @@ module MotionPrime
67
112
  reuse_identifier: cell_name(table, index)
68
113
  )
69
114
  else
70
- screen.table_view_cell styles: [:base_table_cell] + styles, reuse_identifier: cell_name(table, index) do
115
+ screen.table_view_cell styles: [:base_table_cell] + styles, reuse_identifier: cell_name(table, index), parent_view: table_view do
71
116
  item.render(to: screen)
72
117
  end
73
118
  end
@@ -76,16 +121,16 @@ module MotionPrime
76
121
  def on_click(table, index)
77
122
  end
78
123
 
79
- def on_appear
80
- end
124
+ def on_appear; end
125
+ def on_row_render(cell, index); end
81
126
 
82
127
  def cell_name(table, index)
83
- record = data[index.row]
128
+ record = row_by_index(index)
84
129
  if record && record.model &&
85
130
  record.model.respond_to?(:id) && record.model.id.present?
86
- "cell_#{record.model.id}_#{data_stamp_for(index.row)}"
131
+ "cell_#{record.model.id}_#{data_stamp_for("#{index.section}_#{index.row}")}"
87
132
  else
88
- "cell_#{index.section}_#{index.row}_#{data_stamp_for(index.row)}"
133
+ "cell_#{index.section}_#{index.row}_#{data_stamp_for("#{index.section}_#{index.row}")}"
89
134
  end
90
135
  end
91
136
 
@@ -94,23 +139,34 @@ module MotionPrime
94
139
  table.dequeueReusableCellWithIdentifier(cell_name(table, index))
95
140
  end
96
141
 
142
+ # def tableView(table, viewForFooterInSection: section) # cause bug in ios7.0.0-7.0.2
143
+ # UIView.new
144
+ # end
145
+ # def tableView(table, heightForFooterInSection: section)
146
+ # 0.1
147
+ # end
148
+
97
149
  # ALIASES
98
150
  # ---------------------
99
151
 
100
152
  def tableView(table, cellForRowAtIndexPath:index)
101
- cell = cached_cell(index, table) || render_cell(index, table)
153
+ @rendered_cells ||= []
154
+ @rendered_cells[index.section] ||= []
155
+
156
+ cell = cached_cell(index, table) || render_cell(index, table).tap do |cell|
157
+ @rendered_cells[index.section][index.row] = cell
158
+ on_row_render(cell, index)
159
+ end
102
160
 
103
161
  # run table view is appeared callback if needed
104
- if index.row == data.size - 1 && !@did_appear
105
- @did_appear = true
162
+ if !@did_appear && index.row == rows_for_section(index.section).size - 1
106
163
  on_appear
107
164
  end
108
-
109
165
  cell.is_a?(UIView) ? cell : cell.view
110
166
  end
111
167
 
112
168
  def tableView(table, numberOfRowsInSection:section)
113
- data.length
169
+ rows_for_section(section).length
114
170
  end
115
171
 
116
172
  def tableView(table, didSelectRowAtIndexPath:index)
@@ -118,7 +174,15 @@ module MotionPrime
118
174
  end
119
175
 
120
176
  def tableView(table, heightForRowAtIndexPath:index)
121
- data[index.row].container_height
177
+ rows_for_section(index.section)[index.row].container_height
178
+ end
179
+
180
+ def flat_data?
181
+ number_of_sections == 1
182
+ end
183
+
184
+ def rows_for_section(section)
185
+ flat_data? ? data : data[section]
122
186
  end
123
187
  end
124
188
  end