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
@@ -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