motion-prime 0.9.9 → 0.9.9.1

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 (58) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +8 -2
  3. data/Gemfile.lock +2 -2
  4. data/ROADMAP.md +1 -3
  5. data/files/Gemfile +1 -1
  6. data/generators/templates/scaffold/table.rb +1 -1
  7. data/generators/templates/table.rb +1 -1
  8. data/motion-prime/config/base.rb +2 -2
  9. data/motion-prime/elements/_content_text_mixin.rb +1 -1
  10. data/motion-prime/elements/base_element.rb +14 -7
  11. data/motion-prime/elements/collection_view_cell.rb +7 -0
  12. data/motion-prime/elements/draw/image.rb +7 -2
  13. data/motion-prime/elements/table_view_cell.rb +1 -1
  14. data/motion-prime/helpers/has_normalizer.rb +1 -1
  15. data/motion-prime/helpers/has_search_bar.rb +1 -1
  16. data/motion-prime/models/_nano_bag_mixin.rb +1 -1
  17. data/motion-prime/models/model.rb +8 -8
  18. data/motion-prime/models/store.rb +1 -1
  19. data/motion-prime/screens/screen.rb +3 -3
  20. data/motion-prime/sections/_async_form_mixin.rb +2 -2
  21. data/motion-prime/sections/_async_table_mixin.rb +7 -7
  22. data/motion-prime/sections/_cell_section_mixin.rb +16 -11
  23. data/motion-prime/sections/_draw_section_mixin.rb +1 -0
  24. data/motion-prime/sections/{__section_with_container_mixin.rb → _section_with_container_mixin.rb} +2 -2
  25. data/motion-prime/sections/abstract_collection.rb +291 -0
  26. data/motion-prime/sections/base_section.rb +2 -2
  27. data/motion-prime/sections/collection/collection_delegate.rb +62 -0
  28. data/motion-prime/sections/form.rb +22 -18
  29. data/motion-prime/sections/form/base_field_section.rb +7 -7
  30. data/motion-prime/sections/form/form_delegate.rb +1 -1
  31. data/motion-prime/sections/form/form_header_section.rb +1 -1
  32. data/motion-prime/sections/form/password_field_section.rb +1 -1
  33. data/motion-prime/sections/form/static_field_section.rb +1 -1
  34. data/motion-prime/sections/form/string_field_section.rb +1 -1
  35. data/motion-prime/sections/form/text_field_section.rb +1 -1
  36. data/motion-prime/sections/grid.rb +92 -0
  37. data/motion-prime/sections/table.rb +26 -260
  38. data/motion-prime/sections/table/refresh_mixin.rb +3 -3
  39. data/motion-prime/sections/table/table_delegate.rb +1 -0
  40. data/motion-prime/styles/_mixins.rb +1 -1
  41. data/motion-prime/styles/base.rb +20 -6
  42. data/motion-prime/styles/form.rb +1 -1
  43. data/motion-prime/support/_control_content_alignment.rb +39 -0
  44. data/motion-prime/support/mp_button.rb +5 -13
  45. data/motion-prime/support/mp_collection_cell_with_section.rb +12 -0
  46. data/motion-prime/support/{mp_cell_content_view.rb → mp_table_cell_content_view.rb} +0 -0
  47. data/motion-prime/support/{mp_cell_with_section.rb → mp_table_cell_with_section.rb} +1 -1
  48. data/motion-prime/support/mp_text_field.rb +24 -18
  49. data/motion-prime/support/mp_text_view.rb +1 -0
  50. data/motion-prime/version.rb +1 -1
  51. data/motion-prime/views/_frame_calculator_mixin.rb +15 -11
  52. data/motion-prime/views/layout.rb +6 -4
  53. data/motion-prime/views/view_builder.rb +10 -0
  54. data/motion-prime/views/view_styler.rb +5 -1
  55. data/spec/factories/scaffold/sections/tasks/index_table.rb +1 -1
  56. data/spec/unit/support/filter_mixin_spec.rb +7 -3
  57. data/spec/unit/support/frame_calculator_mixin_spec.rb +43 -0
  58. metadata +13 -5
@@ -3,13 +3,13 @@ module MotionPrime
3
3
  def add_pull_to_refresh(options = {}, &block)
4
4
  screen.automaticallyAdjustsScrollViewInsets = false
5
5
 
6
- table_view.addPullToRefreshWithActionHandler(block) # block must be a variable
7
- screen.set_options_for table_view.pullToRefreshView, styles: [:base_pull_to_refresh]
6
+ collection_view.addPullToRefreshWithActionHandler(block) # block must be a variable
7
+ screen.set_options_for collection_view.pullToRefreshView, styles: [:base_pull_to_refresh]
8
8
  end
9
9
 
10
10
  def finish_pull_to_refresh
11
11
  reload_data
12
- table_view.pullToRefreshView.stopAnimating
12
+ collection_view.pullToRefreshView.stopAnimating
13
13
  end
14
14
  end
15
15
  end
@@ -2,6 +2,7 @@ module MotionPrime
2
2
  class TableDelegate
3
3
  include DelegateMixin
4
4
  attr_accessor :table_section
5
+
5
6
  def initialize(options)
6
7
  self.table_section = options[:section].try(:weak_ref)
7
8
  @section_instance = table_section.to_s
@@ -1,4 +1,4 @@
1
- motion_require '../views/styles.rb'
1
+ motion_require '../views/styles'
2
2
  MotionPrime::Styles.define :_mixin do
3
3
  style :label_reset,
4
4
  top: nil, left: nil, height: nil, right: nil, bottom: nil, top: nil,
@@ -1,4 +1,4 @@
1
- motion_require '../views/styles.rb'
1
+ motion_require '../views/styles'
2
2
  MotionPrime::Styles.define :base do
3
3
  # basic screen styles
4
4
  style :screen,
@@ -9,17 +9,23 @@ MotionPrime::Styles.define :base do
9
9
  style :table,
10
10
  top: 0,
11
11
  left: 0,
12
- width: 320,
12
+ right: 0,
13
13
  bottom: 0,
14
14
  separator_inset: 0
15
15
 
16
+ style :collection,
17
+ top: 0,
18
+ left: 0,
19
+ right: 0,
20
+ bottom: 0
21
+
16
22
  style :table_cell,
17
23
  background_color: :clear
18
24
 
19
25
  # basic form styles
20
26
  # ----------
21
27
  style :form,
22
- width: 320,
28
+ right: 0,
23
29
  left: 0,
24
30
  top: 0,
25
31
  right: 0,
@@ -41,14 +47,22 @@ MotionPrime::Styles.define :base do
41
47
  height: 44
42
48
 
43
49
  style :segmented_control,
44
- height: 40, width: 320, top: 0
50
+ height: 40,
51
+ left: 0,
52
+ right: 0,
53
+ top: 0
45
54
 
46
55
  style :google_map,
47
- top: 0, left: 0, right: 0, bottom: 0
56
+ top: 0,
57
+ left: 0,
58
+ right: 0,
59
+ bottom: 0
48
60
 
49
61
  style :spinner,
50
62
  annular: true,
51
- center: proc { screen.view.center }, width: 37, height: 37,
63
+ center: proc { screen.view.center },
64
+ width: 37,
65
+ height: 37,
52
66
  progress_tint_color: :app_base.uicolor,
53
67
  background_tint_color: :black.uicolor(0.05),
54
68
  progress: 0.25
@@ -1,4 +1,4 @@
1
- motion_require '../views/styles.rb'
1
+ motion_require '../views/styles'
2
2
  MotionPrime::Styles.define :base_form do
3
3
  style :header, container: {height: 25}
4
4
  style :header_label, mixins: [:multiline],
@@ -0,0 +1,39 @@
1
+ module MotionPrime
2
+ module SupportControlContentAlignment
3
+ VERTICAL_ALIGNMENT_CONSTS = {
4
+ UIControlContentVerticalAlignmentCenter => :center,
5
+ UIControlContentVerticalAlignmentTop => :top,
6
+ UIControlContentVerticalAlignmentBottom => :bottom,
7
+ UIControlContentVerticalAlignmentFill => :fill # TODO: handle this value
8
+ }
9
+ def setContentVerticalAlignment(value)
10
+ return unless @_content_vertical_alignment = VERTICAL_ALIGNMENT_CONSTS[value]
11
+ super
12
+ end
13
+
14
+ def padding_top
15
+ padding_top = self.paddingTop || self.padding
16
+ if @_content_vertical_alignment == :bottom
17
+ padding_bottom = self.paddingBottom || self.padding
18
+ bounds_height - padding_bottom.to_i - line_height
19
+ elsif @_content_vertical_alignment == :top
20
+ padding_top.to_i
21
+ else # center label
22
+ padding_top_offset = padding_top.to_i - (self.paddingBottom || self.padding).to_i
23
+ (bounds_height - line_height)/2 + 1 + padding_top_offset
24
+ end
25
+ end
26
+
27
+ def padding_bottom
28
+ (bounds_height - (line_height + padding_top))
29
+ end
30
+
31
+ def line_height
32
+ @_line_height || self.font.pointSize
33
+ end
34
+
35
+ def bounds_height
36
+ self.bounds.size.height
37
+ end
38
+ end
39
+ end
@@ -1,8 +1,11 @@
1
- motion_require '../support/_key_value_store'
2
- motion_require '../support/_padding_attribute'
1
+ motion_require '_key_value_store'
2
+ motion_require '_padding_attribute'
3
+ motion_require '_control_content_alignment'
3
4
  class MPButton < UIButton
4
5
  include MotionPrime::SupportKeyValueStore
5
6
  include MotionPrime::SupportPaddingAttribute
7
+ include MotionPrime::SupportControlContentAlignment
8
+
6
9
  attr_accessor :sizeToFit
7
10
 
8
11
  def setTitle(value)
@@ -26,17 +29,6 @@ class MPButton < UIButton
26
29
  5
27
30
  end
28
31
 
29
- def padding_top # to center title label
30
- self.paddingTop || self.padding || begin
31
- single_line_height = self.font.pointSize
32
- (self.bounds.size.height - single_line_height)/2 + 1
33
- end
34
- end
35
-
36
- def padding_bottom
37
- self.bounds.size.height - (self.font.pointSize + padding_top)
38
- end
39
-
40
32
  def apply_padding!(rect)
41
33
  self.setTitleEdgeInsets(padding_insets)
42
34
  end
@@ -0,0 +1,12 @@
1
+ class MPCollectionCellWithSection < UICollectionViewCell
2
+ attr_reader :section
3
+
4
+ def setSection(section)
5
+ @section = section.try(:weak_ref)
6
+ end
7
+
8
+ def drawRect(rect)
9
+ section.try(:draw_in, rect)
10
+ super
11
+ end
12
+ end
@@ -1,4 +1,4 @@
1
- class MPCellWithSection < UITableViewCell
1
+ class MPTableCellWithSection < UITableViewCell
2
2
  attr_reader :section
3
3
  attr_accessor :scroll_view, :content_view
4
4
 
@@ -1,13 +1,15 @@
1
1
  # This class have some modifications for UITextField:
2
2
  # * support padding, padding_left, padding_right options
3
3
  # * support placeholder_color, placeholder_font options
4
- motion_require '../support/_key_value_store'
5
- motion_require '../support/_padding_attribute'
4
+ motion_require '_key_value_store'
5
+ motion_require '_padding_attribute'
6
+ motion_require '_control_content_alignment'
6
7
  class MPTextField < UITextField
7
8
  include MotionPrime::SupportKeyValueStore
8
9
  include MotionPrime::SupportPaddingAttribute
9
- attr_accessor :placeholderColor, :placeholderFont, :readonly, :placeholderAlignment
10
+ include MotionPrime::SupportControlContentAlignment
10
11
 
12
+ attr_accessor :placeholderColor, :placeholderFont, :readonly, :placeholderAlignment
11
13
 
12
14
  def self.default_padding_left
13
15
  5
@@ -19,38 +21,42 @@ class MPTextField < UITextField
19
21
 
20
22
  # placeholder position
21
23
  def textRectForBounds(bounds)
22
- calculate_rect_for(bounds)
24
+ @_line_height = placeholder_font.pointSize
25
+ rect = calculate_rect_for(bounds)
26
+ @_line_height = nil
27
+ rect
23
28
  end
24
29
 
25
30
  # text position
26
31
  def editingRectForBounds(bounds)
27
- calculate_rect_for(bounds)
32
+ @_line_height = self.font.pointSize
33
+ rect = calculate_rect_for(bounds)
34
+ @_line_height = nil
35
+ rect
28
36
  end
29
37
 
30
38
  def drawPlaceholderInRect(rect)
31
39
  color = self.placeholderColor || :gray.uicolor
32
40
  color.setFill
33
- font = self.placeholderFont || self.font || :system.uifont(16)
34
41
 
35
42
  truncation = :tail_truncation.uilinebreakmode
36
- alignment = (placeholderAlignment || :left)
37
- alignment = alignment.nstextalignment if alignment.is_a?(Symbol)
38
- self.placeholder.drawInRect(rect, withFont: font, lineBreakMode: truncation, alignment: alignment)
43
+ alignment = (placeholderAlignment || :left.nstextalignment)
44
+ self.placeholder.drawInRect(rect, withFont: placeholder_font, lineBreakMode: truncation, alignment: alignment)
39
45
  end
40
46
 
41
- def padding_top # to center title label
42
- self.paddingTop || self.padding || begin
43
- single_line_height = self.font.pointSize
44
- (self.bounds.size.height - single_line_height)/2 + 1
47
+ private
48
+ def placeholder_font
49
+ self.placeholderFont || self.font || :system.uifont(16)
45
50
  end
46
- end
47
51
 
48
- private
49
52
  def calculate_rect_for(bounds)
50
- height_diff = self.bounds.size.height - (self.font.pointSize + padding_top + padding_bottom)
53
+ height_diff = self.bounds.size.height - (line_height + padding_top + padding_bottom)
51
54
  bounds = CGRectMake(bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height - height_diff)
52
55
  CGRectMake(
53
- bounds.origin.x + padding_left, bounds.origin.y + padding_top,
54
- bounds.size.width - (padding_left + padding_right), bounds.size.height - (padding_top + padding_bottom))
56
+ bounds.origin.x + padding_left,
57
+ bounds.origin.y + padding_top,
58
+ bounds.size.width - (padding_left + padding_right),
59
+ bounds.size.height - (padding_top + padding_bottom)
60
+ )
55
61
  end
56
62
  end
@@ -6,6 +6,7 @@ motion_require '../support/_padding_attribute'
6
6
  class MPTextView < UITextView
7
7
  include MotionPrime::SupportKeyValueStore
8
8
  include MotionPrime::SupportPaddingAttribute
9
+
9
10
  attr_accessor :placeholderColor, :placeholderFont, :placeholder
10
11
 
11
12
  def self.default_padding_left
@@ -1,3 +1,3 @@
1
1
  module MotionPrime
2
- VERSION = "0.9.9"
2
+ VERSION = "0.9.9.1"
3
3
  end
@@ -19,8 +19,8 @@ module MotionPrime
19
19
 
20
20
  max_width = parent_bounds.size.width
21
21
  max_height = parent_bounds.size.height
22
- width = 0.0 if width.nil?
23
- height = 0.0 if height.nil?
22
+ temp_width = width || 0.0
23
+ temp_height = height || 0.0
24
24
 
25
25
  if left && left > 0 && left <= 1 && value_type != 'absolute' && left.is_a?(Float)
26
26
  left = max_width * left
@@ -41,7 +41,7 @@ module MotionPrime
41
41
  end
42
42
 
43
43
  # calculate top and bottom if height is relative, e.g 0.7
44
- if height && height > 0 && height <= 1 && value_type != 'absolute' && width.is_a?(Float)
44
+ if height && height > 0 && height <= 1 && value_type != 'absolute' && height.is_a?(Float)
45
45
  if bottom.nil?
46
46
  top ||= 0
47
47
  bottom = max_height - max_height * height
@@ -52,27 +52,31 @@ module MotionPrime
52
52
 
53
53
  if !left.nil? && !right.nil?
54
54
  frame.origin.x = left
55
- width = max_width - left - right if options[:height_to_fit].nil?
55
+ if options[:height_to_fit].nil? && width.nil?
56
+ width = max_width - left - right
57
+ end
56
58
  elsif !right.nil?
57
- frame.origin.x = max_width - width - right
59
+ frame.origin.x = max_width - temp_width - right
58
60
  elsif !left.nil?
59
61
  frame.origin.x = left
60
62
  else
61
- frame.origin.x = max_width / 2 - width / 2
63
+ frame.origin.x = max_width / 2 - temp_width / 2
62
64
  end
63
- frame.size.width = width
65
+ frame.size.width = width || 0.0
64
66
 
65
67
  if !top.nil? && !bottom.nil?
66
68
  frame.origin.y = top
67
- height = max_height - top - bottom if options[:height_to_fit].nil?
69
+ if options[:height_to_fit].nil? && height.nil?
70
+ height = max_height - top - bottom
71
+ end
68
72
  elsif !bottom.nil?
69
- frame.origin.y = max_height - height - bottom
73
+ frame.origin.y = max_height - temp_height - bottom
70
74
  elsif !top.nil?
71
75
  frame.origin.y = top
72
76
  else
73
- frame.origin.y = max_height / 2 - height / 2
77
+ frame.origin.y = max_height / 2 - temp_height / 2
74
78
  end
75
- frame.size.height = height
79
+ frame.size.height = height || 0.0
76
80
 
77
81
  frame
78
82
  rescue => e
@@ -1,5 +1,6 @@
1
1
  # TODO: make it part of Sections
2
- motion_require '../support/mp_cell_with_section'
2
+ motion_require '../support/mp_table_cell_with_section'
3
+ motion_require '../support/mp_collection_cell_with_section'
3
4
  motion_require '../support/mp_spinner'
4
5
  motion_require '../support/mp_button'
5
6
  motion_require '../support/mp_label'
@@ -17,7 +18,7 @@ module MotionPrime
17
18
  else
18
19
  view_stack.last.bounds
19
20
  end
20
- builder = ViewBuilder.new(klass, options)
21
+ builder = ViewBuilder.new(klass, options.merge(parent_bounds: parent_bounds))
21
22
  options = builder.options.merge(calculate_frame: true, parent_bounds: parent_bounds)
22
23
  view = builder.view
23
24
  insert_index = options.delete(:at_index)
@@ -51,8 +52,9 @@ module MotionPrime
51
52
  base.class_eval do
52
53
  [::UIActionSheet, ::UIActivityIndicatorView, ::MPButton, ::UIDatePicker, ::UIImageView, ::MPLabel,
53
54
  ::UIPageControl, ::UIPickerView, ::UIProgressView, ::UIScrollView, ::UISearchBar, ::UISegmentedControl,
54
- ::UISlider, ::UIStepper, ::UISwitch, ::UITabBar, ::UITableView, ::UITableViewCell, ::MPTextField, ::MPTextView,
55
- ::UIToolbar, ::UIWebView, ::UINavigationBar, ::MPCellWithSection, ::MBProgressHUD, ::MPSpinner].each do |klass|
55
+ ::UISlider, ::UIStepper, ::UISwitch, ::UITabBar, ::UICollectionView, ::UITableView, ::UITableViewCell,
56
+ ::MPTextField, ::MPTextView, ::UIToolbar, ::UIWebView, ::UINavigationBar,
57
+ ::MPTableCellWithSection, ::MPCollectionCellWithSection, ::MBProgressHUD, ::MPSpinner].each do |klass|
56
58
 
57
59
  shorthand = "#{klass}"[2..-1].underscore.to_sym
58
60
 
@@ -86,6 +86,16 @@ module MotionPrime
86
86
  view.tableFooterView = UIView.new
87
87
  view
88
88
  },
89
+ 'UICollectionView' => Proc.new{|klass, options|
90
+ unless layout = options.delete(:layout)
91
+ layout = UICollectionViewFlowLayout.alloc.init
92
+ total_width = options[:parent_bounds].size.width / (options.delete(:grid_size) || 4)
93
+ width = total_width - layout.minimumInteritemSpacing
94
+ layout.setItemSize CGSizeMake(width, options.delete(:item_height) || 100)
95
+ end
96
+ view = klass.alloc.initWithFrame CGRectZero, collectionViewLayout: layout
97
+ view
98
+ },
89
99
  'UITableViewCell' => Proc.new{|klass, options|
90
100
  style = options.delete(:style) || UITableViewCellStyleDefault
91
101
  if options[:has_drawn_content]
@@ -5,6 +5,8 @@ module MotionPrime
5
5
  include HasClassFactory
6
6
  include ElementTextMixin
7
7
 
8
+ ORDER = %w[font placeholder_font text]
9
+
8
10
  attr_reader :view, :options
9
11
 
10
12
  def initialize(view, parent_bounds = CGRectZero, options = {})
@@ -46,7 +48,7 @@ module MotionPrime
46
48
  def prepare_options!
47
49
  if options[:size_to_fit]
48
50
  options[:line_break_mode] ||= :word_wrap
49
- options[:number_of_lines] ||= 0
51
+ options[:number_of_lines] ||= 0 if view.is_a?(UILabel)
50
52
  end
51
53
 
52
54
  if options.slice(:html, :line_spacing, :line_height, :underline, :fragment_color).any?
@@ -63,6 +65,8 @@ module MotionPrime
63
65
  end
64
66
  extract_font_options(options)
65
67
  extract_font_options(options, 'placeholder')
68
+
69
+ @options = Hash[options.sort_by { |k,v| ORDER.index(k.to_s).to_i }]
66
70
  end
67
71
 
68
72
  def extract_font_options(options, prefix = nil)
@@ -1,5 +1,5 @@
1
1
  class TasksIndexTableSection < Prime::TableSection
2
- def table_data
2
+ def collection_data
3
3
  Task.all.map do |model|
4
4
  TasksIndexCellSection.new(model: model)
5
5
  end
@@ -1,4 +1,8 @@
1
1
  describe MotionPrime::FilterMixin do
2
+ before do
3
+ @subject = MotionPrime::FilterMixin.new
4
+ end
5
+
2
6
  def data
3
7
  model_stub = Struct.new(:info, :id) # :id is used for sorting
4
8
  data_array.map do |item|
@@ -11,15 +15,15 @@ describe MotionPrime::FilterMixin do
11
15
  end
12
16
 
13
17
  it "should filter array by inclusion" do
14
- MotionPrime::FilterMixin.new.filter_array(data, {id: %w[4 5]}).count.should.equal 2
18
+ @subject.filter_array(data, {id: %w[4 5]}).count.should.equal 2
15
19
  end
16
20
 
17
21
  it "should find a single record (case sensitive)" do
18
- MotionPrime::FilterMixin.new.filter_array(data, {id: 4, name: 'iPhone'}).count.should.equal 1
22
+ @subject.filter_array(data, {id: 4, name: 'iPhone'}).count.should.equal 1
19
23
  end
20
24
 
21
25
  it "order filtered records" do
22
- result = MotionPrime::FilterMixin.new.filter_array(data, {id: %w[4 5]}, sort: {id: :desc})
26
+ result = @subject.filter_array(data, {id: %w[4 5]}, sort: {id: :desc})
23
27
  result.first[:id].should.equal 5
24
28
  end
25
29
  end