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.
- checksums.yaml +8 -8
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +14 -11
- data/README.md +8 -11
- data/Rakefile +2 -1
- data/bin/prime.rb +47 -0
- data/doc/FAQ.md +1 -1
- data/files/app/app_delegate.rb +1 -1
- data/files/app/config/base.rb +8 -4
- data/files/app/screens/application_screen.rb +1 -1
- data/files/app/screens/sidebar_screen.rb +1 -1
- data/files/app/sections/sidebar/action.rb +1 -1
- data/files/app/sections/sidebar/table.rb +1 -1
- data/files/app/styles/sidebar.rb +5 -5
- data/motion-prime.gemspec +1 -0
- data/motion-prime/api_client.rb +81 -0
- data/motion-prime/app_delegate.rb +22 -5
- data/motion-prime/config/base.rb +5 -0
- data/motion-prime/core_ext/kernel.rb +5 -0
- data/motion-prime/elements/_field_dimensions_mixin.rb +43 -0
- data/motion-prime/elements/_text_dimensions_mixin.rb +39 -0
- data/motion-prime/elements/base.rb +40 -17
- data/motion-prime/elements/button.rb +20 -0
- data/motion-prime/elements/draw.rb +2 -2
- data/motion-prime/elements/draw/image.rb +4 -2
- data/motion-prime/elements/draw/label.rb +1 -1
- data/motion-prime/elements/error_message.rb +3 -16
- data/motion-prime/elements/label.rb +13 -2
- data/motion-prime/elements/text_field.rb +1 -0
- data/motion-prime/helpers/cell_section.rb +9 -0
- data/motion-prime/helpers/has_authorization.rb +4 -3
- data/motion-prime/helpers/has_normalizer.rb +20 -6
- data/motion-prime/helpers/has_search_bar.rb +19 -7
- data/motion-prime/helpers/has_style_chain_builder.rb +7 -0
- data/motion-prime/models/association.rb +22 -9
- data/motion-prime/models/association_collection.rb +54 -23
- data/motion-prime/models/bag.rb +13 -12
- data/motion-prime/models/base.rb +2 -0
- data/motion-prime/models/errors.rb +23 -14
- data/motion-prime/models/finder.rb +4 -1
- data/motion-prime/models/model.rb +25 -5
- data/motion-prime/models/store_extension.rb +1 -7
- data/motion-prime/models/sync.rb +75 -43
- data/motion-prime/mp.rb +4 -0
- data/motion-prime/screens/_base_mixin.rb +18 -12
- data/motion-prime/screens/_navigation_bar_mixin.rb +15 -6
- data/motion-prime/screens/_navigation_mixin.rb +15 -16
- data/motion-prime/screens/base_screen.rb +5 -1
- data/motion-prime/screens/sidebar_container_screen.rb +37 -22
- data/motion-prime/sections/base.rb +82 -16
- data/motion-prime/sections/form.rb +144 -26
- data/motion-prime/sections/form/base_field_section.rb +62 -29
- data/motion-prime/sections/form/base_header_section.rb +27 -0
- data/motion-prime/sections/form/date_field_section.rb +2 -17
- data/motion-prime/sections/form/password_field_section.rb +3 -17
- data/motion-prime/sections/form/select_field_section.rb +4 -35
- data/motion-prime/sections/form/string_field_section.rb +3 -29
- data/motion-prime/sections/form/submit_field_section.rb +1 -7
- data/motion-prime/sections/form/switch_field_section.rb +3 -23
- data/motion-prime/sections/form/text_field_section.rb +3 -33
- data/motion-prime/sections/form/text_with_button_field_section.rb +4 -40
- data/motion-prime/sections/tabbed.rb +25 -5
- data/motion-prime/sections/table.rb +86 -22
- data/motion-prime/sections/table/refresh_mixin.rb +3 -1
- data/motion-prime/styles/base.rb +7 -89
- data/motion-prime/styles/form.rb +116 -0
- data/motion-prime/support/dm_button.rb +32 -5
- data/motion-prime/support/dm_text_field.rb +31 -7
- data/motion-prime/support/dm_text_view.rb +6 -3
- data/motion-prime/support/dm_view_controller.rb +3 -3
- data/motion-prime/support/ui_search_bar_custom.rb +1 -1
- data/motion-prime/version.rb +1 -1
- data/motion-prime/views/layout.rb +18 -10
- data/motion-prime/views/styles.rb +19 -9
- data/motion-prime/views/view_builder.rb +18 -2
- data/motion-prime/views/view_styler.rb +59 -5
- data/spec/models/errors_spec.rb +3 -3
- data/travis.sh +3 -2
- metadata +28 -5
- data/motion-prime/elements/_text_height_mixin.rb +0 -17
- data/motion-prime/sections/form/table_field_section.rb +0 -51
data/motion-prime/config/base.rb
CHANGED
@@ -10,4 +10,9 @@ MotionPrime::Config.font.name = "Ubuntu"
|
|
10
10
|
MotionPrime::Config.color do |color|
|
11
11
|
color.base = 0x424242
|
12
12
|
color.error = 0xef471f
|
13
|
+
end
|
14
|
+
MotionPrime::Config.api do |api|
|
15
|
+
api.base = "http://example.com"
|
16
|
+
api.client_id = ""
|
17
|
+
api.client_secret = ""
|
13
18
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module MotionPrime
|
2
|
+
module ElementFieldDimensionsMixin
|
3
|
+
def text_value
|
4
|
+
text = view ? view.text : computed_options[:text].to_s
|
5
|
+
text.empty? ? computed_options[:placeholder] : text
|
6
|
+
end
|
7
|
+
|
8
|
+
def font
|
9
|
+
computed_options[:font] || :system.uifont
|
10
|
+
end
|
11
|
+
|
12
|
+
def computed_width
|
13
|
+
min_width = computed_options[:min_width] || 20
|
14
|
+
return min_width if text_value.to_s.empty?
|
15
|
+
|
16
|
+
padding_left = view.try(:padding_left) || computed_options[:padding_left] || computed_options[:padding] || view_class.constantize::DEFAULT_PADDING_LEFT
|
17
|
+
padding_right = view.try(:padding_right) || computed_options[:padding_right] || padding_left
|
18
|
+
max_width = computed_options[:max_width] || Float::MAX
|
19
|
+
|
20
|
+
attributed_text = NSAttributedString.alloc.initWithString(text_value, attributes: {NSFontAttributeName => font })
|
21
|
+
rect = attributed_text.boundingRectWithSize([Float::MAX, Float::MAX], options:NSStringDrawingUsesLineFragmentOrigin, context:nil)
|
22
|
+
|
23
|
+
width = (rect.size.width + padding_left + padding_right).ceil
|
24
|
+
[[width, max_width].min, min_width].max
|
25
|
+
end
|
26
|
+
|
27
|
+
def computed_height
|
28
|
+
text = view ? view.titleLabel.text : computed_options[:title]
|
29
|
+
return 0 if text.blank?
|
30
|
+
|
31
|
+
width = computed_options[:width]
|
32
|
+
font = computed_options[:title_label][:font] || :system.uifont
|
33
|
+
raise "Please set element width for height calculation" unless width
|
34
|
+
|
35
|
+
attributes = {NSFontAttributeName => font }
|
36
|
+
attributed_text = NSAttributedString.alloc.initWithString(text, attributes: attributes)
|
37
|
+
rect = attributed_text.boundingRectWithSize([width, Float::MAX], options:NSStringDrawingUsesLineFragmentOrigin, context:nil)
|
38
|
+
|
39
|
+
padding_top = computed_options[:padding_top] || computed_options[:padding] || view.try(:default_padding_top)
|
40
|
+
rect.size.height + padding_top*2
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module MotionPrime
|
2
|
+
module ElementTextDimensionsMixin
|
3
|
+
def content_height
|
4
|
+
text = view.try(:text) || computed_options[:text]
|
5
|
+
return 0 if text.blank?
|
6
|
+
|
7
|
+
width = computed_options[:width]
|
8
|
+
font = computed_options[:font] || :system.uifont
|
9
|
+
raise "Please set element width for height calculation" unless width
|
10
|
+
|
11
|
+
attributes = {NSFontAttributeName => font }
|
12
|
+
if computed_options[:line_spacing]
|
13
|
+
paragrahStyle = NSMutableParagraphStyle.alloc.init
|
14
|
+
paragrahStyle.setLineSpacing(computed_options[:line_spacing])
|
15
|
+
attributes[NSParagraphStyleAttributeName] = paragrahStyle
|
16
|
+
end
|
17
|
+
attributed_text = NSAttributedString.alloc.initWithString(computed_options[:text], attributes: attributes)
|
18
|
+
rect = attributed_text.boundingRectWithSize([width, Float::MAX], options:NSStringDrawingUsesLineFragmentOrigin, context:nil)
|
19
|
+
rect.size.height
|
20
|
+
end
|
21
|
+
|
22
|
+
def content_width
|
23
|
+
text = view.try(:text) || computed_options[:text]
|
24
|
+
return 0 if text.blank?
|
25
|
+
|
26
|
+
width = computed_options[:width]
|
27
|
+
font = computed_options[:font] || :system.uifont
|
28
|
+
|
29
|
+
attributed_text = NSAttributedString.alloc.initWithString(computed_options[:text], attributes: {NSFontAttributeName => font })
|
30
|
+
rect = attributed_text.boundingRectWithSize([Float::MAX, Float::MAX], options:NSStringDrawingUsesLineFragmentOrigin, context:nil)
|
31
|
+
|
32
|
+
rect.size.width
|
33
|
+
end
|
34
|
+
|
35
|
+
def content_outer_height
|
36
|
+
content_height + computed_inner_top + computed_inner_bottom
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
motion_require '../helpers/has_normalizer'
|
2
|
+
motion_require '../helpers/has_style_chain_builder'
|
2
3
|
module MotionPrime
|
3
4
|
class BaseElement
|
4
5
|
# MotionPrime::BaseElement is container for UIView class elements with options.
|
@@ -6,20 +7,21 @@ module MotionPrime
|
|
6
7
|
|
7
8
|
include ::MotionSupport::Callbacks
|
8
9
|
include HasNormalizer
|
10
|
+
include HasStyleChainBuilder
|
9
11
|
|
10
12
|
attr_accessor :options, :section, :name,
|
11
13
|
:view_class, :view, :view_name, :styles, :screen
|
12
|
-
|
14
|
+
delegate :observing_errors?, :has_errors?, :errors_observer_fields, :observing_errors_for, to: :section, allow_nil: true
|
13
15
|
define_callbacks :render
|
14
16
|
|
15
17
|
def initialize(options = {})
|
16
18
|
@options = options
|
17
19
|
@section = options.delete(:section)
|
18
|
-
|
20
|
+
|
19
21
|
@name = options[:name]
|
20
22
|
@block = options.delete(:block)
|
21
23
|
@view_class = options.delete(:view_class) || "UIView"
|
22
|
-
@view_name = self.
|
24
|
+
@view_name = self.class_name_without_kvo.demodulize.underscore.gsub('_element', '')
|
23
25
|
end
|
24
26
|
|
25
27
|
def render(options = {}, &block)
|
@@ -41,10 +43,12 @@ module MotionPrime
|
|
41
43
|
end
|
42
44
|
|
43
45
|
def compute_options!
|
44
|
-
@computed_options
|
45
|
-
compute_block_options
|
46
|
-
compute_style_options
|
47
|
-
@computed_options
|
46
|
+
@computed_options ||= {}
|
47
|
+
block_options = compute_block_options || {}
|
48
|
+
compute_style_options(options, block_options)
|
49
|
+
@computed_options.merge!(options)
|
50
|
+
@computed_options.merge!(block_options)
|
51
|
+
normalize_options(@computed_options, section, %w[text font title_label padding padding_left padding_right max_width width left right])
|
48
52
|
end
|
49
53
|
|
50
54
|
# Compute options sent inside block, e.g.
|
@@ -52,20 +56,37 @@ module MotionPrime
|
|
52
56
|
# {name: model.name}
|
53
57
|
# end
|
54
58
|
def compute_block_options
|
55
|
-
|
56
|
-
@computed_options.merge!(section.send :instance_eval, &block)
|
57
|
-
end
|
59
|
+
section.send(:instance_exec, self, &@block) if @block
|
58
60
|
end
|
59
61
|
|
60
|
-
def compute_style_options
|
62
|
+
def compute_style_options(*style_sources)
|
61
63
|
@styles = []
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
64
|
+
base_styles = {common: [], specific: []}
|
65
|
+
suffixes = {common: [@view_name.to_sym, name.try(:to_sym)].compact, specific: []}
|
66
|
+
|
67
|
+
if section
|
68
|
+
field_section = section.respond_to?(:section_styles)
|
69
|
+
if field_section
|
70
|
+
section.section_styles.each { |type, values| base_styles[type] += values }
|
71
|
+
end
|
72
|
+
if section.respond_to?(:observing_errors?) && observing_errors? && has_errors?
|
73
|
+
suffixes[:common] += [:"#{name}_with_errors", :"#{@view_name}_with_errors"]
|
74
|
+
end
|
66
75
|
end
|
67
|
-
|
68
|
-
|
76
|
+
|
77
|
+
# common + specific base - common suffixes
|
78
|
+
@styles += build_styles_chain(base_styles[:common], suffixes[:common])
|
79
|
+
@styles << :"#{section.name}_#{name}" if section
|
80
|
+
@styles += build_styles_chain(base_styles[:specific], suffixes[:common])
|
81
|
+
# specific base - specific suffixes
|
82
|
+
@styles += build_styles_chain(base_styles[:specific], suffixes[:specific])
|
83
|
+
@styles << :"#{section.form.name}_field_#{section.name}_#{name}" if field_section
|
84
|
+
# custom style (from options or block options)
|
85
|
+
custom_styles = style_sources.map do |source|
|
86
|
+
normalize_object(source.delete(:styles), section)
|
87
|
+
end.compact.flatten
|
88
|
+
@styles += custom_styles
|
89
|
+
# puts @view_class.to_s + @styles.inspect, ''
|
69
90
|
@computed_options.merge!(style_options)
|
70
91
|
end
|
71
92
|
|
@@ -73,6 +94,8 @@ module MotionPrime
|
|
73
94
|
Styles.for(styles)
|
74
95
|
end
|
75
96
|
|
97
|
+
private
|
98
|
+
|
76
99
|
class << self
|
77
100
|
def factory(type, options = {})
|
78
101
|
class_name = "#{type.classify}Element"
|
@@ -1,7 +1,27 @@
|
|
1
1
|
module MotionPrime
|
2
2
|
class ButtonElement < BaseElement
|
3
|
+
include MotionPrime::ElementFieldDimensionsMixin
|
4
|
+
|
5
|
+
after_render :size_to_fit
|
6
|
+
|
7
|
+
def size_to_fit
|
8
|
+
if computed_options[:size_to_fit] || style_options[:size_to_fit]
|
9
|
+
if computed_options[:width]
|
10
|
+
view.setHeight computed_height
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
3
15
|
def view_class
|
4
16
|
"DMButton"
|
5
17
|
end
|
18
|
+
|
19
|
+
def text_value
|
20
|
+
view.try(:currentTitle) || computed_options[:title]
|
21
|
+
end
|
22
|
+
|
23
|
+
def font
|
24
|
+
computed_options[:title_label].try(:[], :font) || :system.uifont
|
25
|
+
end
|
6
26
|
end
|
7
27
|
end
|
@@ -134,8 +134,8 @@ module MotionPrime
|
|
134
134
|
|
135
135
|
def reset_computed_values
|
136
136
|
[:left, :top, :right, :bottom, :width, :height].each do |key|
|
137
|
-
instance_variable_set "@
|
138
|
-
instance_variable_set "@
|
137
|
+
instance_variable_set "@computed_#{key}", nil
|
138
|
+
instance_variable_set "@computed_inner_#{key}", nil
|
139
139
|
end
|
140
140
|
end
|
141
141
|
|
@@ -12,7 +12,7 @@ module MotionPrime
|
|
12
12
|
)
|
13
13
|
# draw already initialized image
|
14
14
|
if image_data
|
15
|
-
image_data
|
15
|
+
draw_with_layer(image_data, image_rect)
|
16
16
|
# draw image from resources
|
17
17
|
elsif computed_options[:image]
|
18
18
|
self.image_data = computed_options[:image].uiimage
|
@@ -50,10 +50,12 @@ module MotionPrime
|
|
50
50
|
|
51
51
|
if radius = computed_options[:layer][:corner_radius]
|
52
52
|
layer.masksToBounds = true
|
53
|
+
k = image.size.width / rect.size.width
|
54
|
+
radius = radius * k
|
53
55
|
layer.cornerRadius = radius
|
54
56
|
end
|
55
57
|
|
56
|
-
UIGraphicsBeginImageContext(image.size)
|
58
|
+
UIGraphicsBeginImageContext(image.size)
|
57
59
|
layer.renderInContext(UIGraphicsGetCurrentContext())
|
58
60
|
image = UIGraphicsGetImageFromCurrentImageContext()
|
59
61
|
UIGraphicsEndImageContext()
|
@@ -1,23 +1,10 @@
|
|
1
|
+
motion_require './label'
|
1
2
|
module MotionPrime
|
2
|
-
class ErrorMessageElement <
|
3
|
-
include MotionPrime::
|
4
|
-
|
5
|
-
after_render :size_to_fit
|
3
|
+
class ErrorMessageElement < LabelElement
|
4
|
+
include MotionPrime::ElementTextDimensionsMixin
|
6
5
|
|
7
6
|
def view_class
|
8
7
|
"UILabel"
|
9
8
|
end
|
10
|
-
|
11
|
-
def size_to_fit
|
12
|
-
view.size.height = self.content_height
|
13
|
-
end
|
14
|
-
|
15
|
-
def computed_inner_top
|
16
|
-
computed_options[:top].to_i
|
17
|
-
end
|
18
|
-
|
19
|
-
def computed_inner_bottom
|
20
|
-
computed_options[:bottom].to_i
|
21
|
-
end
|
22
9
|
end
|
23
10
|
end
|
@@ -1,12 +1,23 @@
|
|
1
1
|
module MotionPrime
|
2
2
|
class LabelElement < BaseElement
|
3
|
-
include MotionPrime::
|
3
|
+
include MotionPrime::ElementTextDimensionsMixin
|
4
4
|
|
5
|
+
before_render :size_to_fit_if_needed
|
5
6
|
after_render :size_to_fit
|
6
7
|
|
7
8
|
def size_to_fit
|
8
9
|
if computed_options[:size_to_fit] || style_options[:size_to_fit]
|
9
|
-
|
10
|
+
if computed_options[:width]
|
11
|
+
view.setHeight content_height + 2 # TODO maybe set width too as it can be wider
|
12
|
+
else
|
13
|
+
view.sizeToFit
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def size_to_fit_if_needed
|
19
|
+
if computed_options[:size_to_fit] && computed_options[:width]
|
20
|
+
@computed_options[:height_to_fit] = content_height
|
10
21
|
end
|
11
22
|
end
|
12
23
|
|
@@ -1,9 +1,10 @@
|
|
1
1
|
module MotionPrime
|
2
2
|
module HasAuthorization
|
3
3
|
def current_user
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
App.delegate.current_user
|
5
|
+
end
|
6
|
+
def update_current_user
|
7
|
+
App.delegate.update_current_user
|
7
8
|
end
|
8
9
|
def user_signed_in?
|
9
10
|
current_user.present?
|
@@ -1,13 +1,27 @@
|
|
1
1
|
module MotionPrime
|
2
2
|
module HasNormalizer
|
3
|
-
def normalize_options(
|
4
|
-
|
3
|
+
def normalize_options(unordered_options, receiver = nil, order = nil)
|
4
|
+
options = if order
|
5
|
+
Hash[unordered_options.sort_by { |k,v| order.index(k.to_s).to_i }]
|
6
|
+
else
|
7
|
+
unordered_options
|
8
|
+
end
|
9
|
+
|
5
10
|
options.each do |key, option|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
11
|
+
unordered_options[key] = normalize_object(option, receiver)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def normalize_object(object, receiver)
|
16
|
+
receiver ||= self
|
17
|
+
if object.is_a?(Proc)
|
18
|
+
receiver.send(:instance_exec, self, &object)
|
19
|
+
elsif object.is_a?(Hash)
|
20
|
+
object.inject({}) do |result, (key, nested_object)|
|
21
|
+
result.merge(key => normalize_object(nested_object, receiver))
|
10
22
|
end
|
23
|
+
else
|
24
|
+
object
|
11
25
|
end
|
12
26
|
end
|
13
27
|
end
|
@@ -1,19 +1,31 @@
|
|
1
1
|
# This module adds search functionality, to Screen or TableSection
|
2
2
|
module MotionPrime
|
3
3
|
module HasSearchBar
|
4
|
-
def add_search_bar(&block)
|
5
|
-
|
4
|
+
def add_search_bar(options = {}, &block)
|
5
|
+
target = options.delete(:target)
|
6
|
+
|
7
|
+
search_bar = create_search_bar(options)
|
6
8
|
search_bar.delegate = self
|
7
|
-
|
9
|
+
|
10
|
+
if target
|
11
|
+
target.addSubview search_bar
|
12
|
+
elsif is_a?(TableSection)
|
13
|
+
self.table_view.tableHeaderView = search_bar
|
14
|
+
end
|
15
|
+
|
8
16
|
@search_callback = block
|
17
|
+
search_bar
|
9
18
|
rescue
|
10
|
-
puts "can't add search bar to #{self.
|
19
|
+
puts "can't add search bar to #{self.class_name_without_kvo}"
|
11
20
|
end
|
12
21
|
|
13
|
-
def create_search_bar
|
14
|
-
name = is_a?(TableSection) ? name : self.
|
22
|
+
def create_search_bar(options = {})
|
23
|
+
name = is_a?(TableSection) ? name : self.class_name_without_kvo.underscore
|
15
24
|
screen = is_a?(TableSection) ? self.screen : self
|
16
|
-
|
25
|
+
options[:styles] ||= []
|
26
|
+
options[:styles] += [:"base_search_bar", :"base_#{name}_search_bar"]
|
27
|
+
|
28
|
+
screen.search_bar(options).view
|
17
29
|
end
|
18
30
|
|
19
31
|
def searchBar(search_bar, textDidChange: text)
|