motion-prime 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +13 -0
  3. data/Gemfile.lock +83 -0
  4. data/README.md +67 -0
  5. data/Rakefile +23 -0
  6. data/app/app_delegate.rb +5 -0
  7. data/doc/SECTION.md +7 -0
  8. data/doc/STYLE.md +39 -0
  9. data/files/Gemfile +3 -0
  10. data/files/Rakefile +16 -0
  11. data/files/app/app_delegate.rb +4 -0
  12. data/files/app/screens/application_screen.rb +3 -0
  13. data/lib/motion-prime.rb +14 -0
  14. data/lib/view_styler.rb +141 -0
  15. data/motion-prime.gemspec +27 -0
  16. data/motion-prime/app_delegate.rb +56 -0
  17. data/motion-prime/elements/base.rb +94 -0
  18. data/motion-prime/elements/button.rb +7 -0
  19. data/motion-prime/elements/draw.rb +56 -0
  20. data/motion-prime/elements/draw/image.rb +43 -0
  21. data/motion-prime/elements/draw/label.rb +13 -0
  22. data/motion-prime/elements/image.rb +14 -0
  23. data/motion-prime/elements/label.rb +20 -0
  24. data/motion-prime/elements/text_field.rb +7 -0
  25. data/motion-prime/elements/text_view.rb +7 -0
  26. data/motion-prime/helpers/has_authorization.rb +10 -0
  27. data/motion-prime/helpers/has_search_bar.rb +25 -0
  28. data/motion-prime/models/base.rb +220 -0
  29. data/motion-prime/screens/_aliases_mixin.rb +32 -0
  30. data/motion-prime/screens/_base_mixin.rb +119 -0
  31. data/motion-prime/screens/_navigation_bar_mixin.rb +57 -0
  32. data/motion-prime/screens/_navigation_mixin.rb +118 -0
  33. data/motion-prime/screens/_orientations_mixin.rb +39 -0
  34. data/motion-prime/screens/base_screen.rb +22 -0
  35. data/motion-prime/screens/sidebar_container_screen.rb +58 -0
  36. data/motion-prime/sections/base.rb +101 -0
  37. data/motion-prime/sections/draw.rb +62 -0
  38. data/motion-prime/sections/form.rb +103 -0
  39. data/motion-prime/sections/form/base_field_section.rb +26 -0
  40. data/motion-prime/sections/form/password_field_section.rb +33 -0
  41. data/motion-prime/sections/form/select_field_section.rb +40 -0
  42. data/motion-prime/sections/form/string_field_section.rb +32 -0
  43. data/motion-prime/sections/form/submit_field_section.rb +20 -0
  44. data/motion-prime/sections/form/text_field_section.rb +33 -0
  45. data/motion-prime/sections/table.rb +97 -0
  46. data/motion-prime/sections/table/refresh_mixin.rb +13 -0
  47. data/motion-prime/styles/forms.rb +93 -0
  48. data/motion-prime/support/_key_value_store.rb +10 -0
  49. data/motion-prime/support/dm_button.rb +22 -0
  50. data/motion-prime/support/dm_cell_with_section.rb +12 -0
  51. data/motion-prime/support/dm_text_field.rb +30 -0
  52. data/motion-prime/support/dm_text_view.rb +93 -0
  53. data/motion-prime/support/dm_view_controller.rb +50 -0
  54. data/motion-prime/support/dm_view_with_section.rb +11 -0
  55. data/motion-prime/support/navigation_controller.rb +4 -0
  56. data/motion-prime/support/ui_search_bar_custom.rb +10 -0
  57. data/motion-prime/support/ui_view.rb +59 -0
  58. data/motion-prime/version.rb +3 -0
  59. data/motion-prime/views/layout.rb +45 -0
  60. data/motion-prime/views/styles.rb +44 -0
  61. data/motion-prime/views/view_builder.rb +80 -0
  62. data/motion-prime/views/view_styler.rb +141 -0
  63. data/resources/Default-568h@2x.png +0 -0
  64. data/spec/main_spec.rb +9 -0
  65. metadata +245 -0
@@ -0,0 +1,101 @@
1
+ motion_require '../helpers/has_authorization'
2
+ module MotionPrime
3
+ class BaseSection
4
+ # MotionPrime::BaseSection is container for Elements.
5
+ # Sections are located inside Screen and can contain multiple Elements.
6
+ # On render, each element will be added to parent screen.
7
+
8
+ # == Basic Sample
9
+ # class MySection < MotionPrime::BaseSection
10
+ # element :title, text: "Hello World"
11
+ # element :avatar, type: :image, image: 'defaults/avatar.jpg'
12
+ # end
13
+ #
14
+ DEFAULT_CONTENT_HEIGHT = 65
15
+ include ::MotionSupport::Callbacks
16
+ include MotionPrime::HasAuthorization
17
+
18
+ attr_accessor :screen, :model, :name, :options, :elements
19
+ class_attribute :elements_options, :container_options
20
+ define_callbacks :render
21
+
22
+ def initialize(options = {})
23
+ @options = options
24
+ @model = options[:model]
25
+ @name = options[:name] ||= self.class.name.underscore.gsub(/\_section$/, '')
26
+ create_elements
27
+ end
28
+
29
+ def create_elements
30
+ self.elements = {}
31
+ (self.class.elements_options || {}).each do |key, opts|
32
+ # we should clone options to prevent overriding options
33
+ # in next element with same name in another class
34
+ options = opts.clone
35
+ options[:section] = self
36
+ self.elements[key] = MotionPrime::BaseElement.factory(options.delete(:type), options)
37
+ end
38
+ end
39
+
40
+ def render(container_options = {})
41
+ self.container_options.merge!(container_options)
42
+ self.screen = WeakRef.new(container_options.delete(:to))
43
+ run_callbacks :render do
44
+ render!
45
+ end
46
+ end
47
+
48
+ def render!
49
+ elements.each do |key, element|
50
+ element.render(to: screen)
51
+ end
52
+ end
53
+
54
+ def element(name)
55
+ elements[name.to_sym]
56
+ end
57
+
58
+ def view(name)
59
+ element(name).view
60
+ end
61
+
62
+ def container_options
63
+ @container_options ||= self.class.container_options.try(:clone) || {}
64
+ end
65
+
66
+ def container_height
67
+ container_options[:height] || DEFAULT_CONTENT_HEIGHT
68
+ end
69
+
70
+ def container_styles
71
+ container_options[:styles]
72
+ end
73
+
74
+ def benchmark(name, &block)
75
+ Benchmark.bm do |x|
76
+ puts "Benchmark: #{name}"
77
+ x.report(&block)
78
+ end
79
+ end
80
+
81
+ class << self
82
+ def element(name, options = {}, &block)
83
+ options[:type] ||= :label
84
+ options[:name] = name
85
+ options[:block] = block
86
+ self.elements_options ||= {}
87
+ self.elements_options[name] = options
88
+ self.elements_options[name]
89
+ end
90
+ def container(options)
91
+ self.container_options = options
92
+ end
93
+ def before_render(method_name)
94
+ set_callback :render, :before, method_name
95
+ end
96
+ def after_render(method_name)
97
+ set_callback :render, :after, method_name
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,62 @@
1
+ module MotionPrime
2
+ class DrawSection < BaseSection
3
+ # MotionPrime::DrawSection is container for Elements.
4
+ # Unlike BaseSection, DrawSection renders elements using drawRect, instead of creating subviews
5
+ # NOTE: only image and label elements are supported at this moment
6
+
7
+ # == Basic Sample
8
+ # class MySection < MotionPrime::DrawSection
9
+ # element :title, text: "Hello World"
10
+ # element :avatar, type: :image, image: 'defaults/avatar.jpg'
11
+ # end
12
+ #
13
+
14
+ attr_accessor :container_view
15
+
16
+ def create_elements
17
+ self.elements = {}
18
+ (self.class.elements_options || {}).each do |key, opts|
19
+ # we should clone options to prevent overriding options
20
+ # in next element with same name in another class
21
+ options = opts.clone
22
+ options[:section] = self
23
+ self.elements[key] = MotionPrime::DrawElement.factory(options.delete(:type), options)
24
+ end
25
+ end
26
+
27
+ def render!
28
+ if container_options[:as].to_s == 'cell'
29
+ @container_view = screen.add_view DMCellWithSection, {
30
+ section: self, styles: container_options[:styles],
31
+ reuse_identifier: container_options[:reuse_identifier]
32
+ }
33
+ else
34
+ @container_view = screen.add_view DMViewWithSection, {
35
+ section: self, styles: container_options[:styles],
36
+ width: container_options[:width] || 320,
37
+ height: container_options[:height] || 100,
38
+ top: container_options[:top] || 0,
39
+ left: container_options[:left] || 0
40
+ }
41
+ end
42
+ end
43
+
44
+ def draw_in(rect)
45
+ draw_background(rect)
46
+ draw_elements(rect)
47
+ end
48
+
49
+ def draw_elements(rect)
50
+ elements.each do |key, element|
51
+ element.draw_in(rect)
52
+ end
53
+ end
54
+
55
+ def draw_background(rect)
56
+ if container_options[:background_color]
57
+ container_options[:background_color].uicolor.setFill
58
+ UIRectFill(rect)
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,103 @@
1
+ motion_require './table.rb'
2
+ module MotionPrime
3
+ class FormSection < TableSection
4
+ # MotionPrime::FormSection is container for Field Sections.
5
+ # Forms are located inside Screen and can contain multiple Field Sections.
6
+ # On render, each field will be added to parent screen.
7
+
8
+ # == Basic Sample
9
+ # class MyLoginForm < MotionPrime::FormSection
10
+ # field :email, label: { text: 'E-mail' }, input: { placeholder: 'Your E-mail' }
11
+ # field :submit, title: 'Login', type: :submit
12
+ #
13
+ # def on_submit
14
+ # email = view("email:input").text
15
+ # puts "Submitted email: #{email}"
16
+ # end
17
+ # end
18
+ #
19
+
20
+ KEYBOARD_HEIGHT_PORTRAIT = 216
21
+ KEYBOARD_HEIGHT_LANDSCAPE = 162
22
+
23
+ class_attribute :fields_options
24
+ attr_accessor :table, :fields, :keyboard_visible
25
+
26
+ after_render :bind_keyboard_events
27
+
28
+ def table_data
29
+ fields.values
30
+ end
31
+
32
+ def render_table
33
+ init_form_fields
34
+ screen.table_view styles: [:base_form, name.to_sym], delegate: self, dataSource: self do |table|
35
+ self.table = table
36
+ end
37
+ end
38
+
39
+ def render_cell(index, table)
40
+ screen.table_view_cell styles: [:base_form_field, :"#{name}_field"], reuse_identifier: cell_name(table, index) do
41
+ data[index.row].render(to: screen)
42
+ end
43
+ end
44
+
45
+ # accepts following syntax to find field element:
46
+ # element("fieldname:elementname"), e.g. element("email:input")
47
+ def element(name)
48
+ field_name, element_name = name.split(':')
49
+ self.fields[field_name.to_sym].element(element_name.to_sym)
50
+ end
51
+
52
+ def on_edit(field); end
53
+
54
+ class << self
55
+ def field(name, options = {})
56
+ options[:name] = name
57
+ options[:type] ||= :string
58
+ self.fields_options ||= {}
59
+ self.fields_options[name] = options
60
+ self.fields_options[name]
61
+ end
62
+ end
63
+
64
+ def set_height_with_keyboard
65
+ return if keyboard_visible
66
+ self.table.height -= KEYBOARD_HEIGHT_PORTRAIT
67
+ self.keyboard_visible = true
68
+ end
69
+
70
+ def set_height_without_keyboard
71
+ return unless keyboard_visible
72
+ self.table.height += KEYBOARD_HEIGHT_PORTRAIT
73
+ self.keyboard_visible = false
74
+ end
75
+
76
+ def on_keyboard_show
77
+ end
78
+
79
+ def on_keyboard_hide
80
+ end
81
+
82
+ def bind_keyboard_events
83
+ NSNotificationCenter.defaultCenter.addObserver self,
84
+ selector: :on_keyboard_show,
85
+ name: UIKeyboardDidShowNotification,
86
+ object: nil
87
+ NSNotificationCenter.defaultCenter.addObserver self,
88
+ selector: :on_keyboard_hide,
89
+ name: UIKeyboardDidHideNotification,
90
+ object: nil
91
+ end
92
+
93
+ private
94
+
95
+ def init_form_fields
96
+ self.fields = {}
97
+ (self.class.fields_options || []).each do |key, field|
98
+ klass = "MotionPrime::#{field[:type].classify}FieldSection".constantize
99
+ self.fields[key] = klass.new(field.merge(form: self))
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,26 @@
1
+ module MotionPrime
2
+ class BaseFieldSection < BaseSection
3
+ attr_accessor :form
4
+
5
+ def initialize(options = {})
6
+ super
7
+ @form = WeakRef.new(options.delete(:form))
8
+ @container_options = options.delete(:container)
9
+ end
10
+
11
+ def form_name
12
+ form.name
13
+ end
14
+
15
+ def container_options
16
+ @container_options || super
17
+ end
18
+
19
+ def scroll_to_and_make_visible
20
+ first_element = elements.first.last
21
+ path = form.table.indexPathForCell first_element.view.superview
22
+ form.table.scrollToRowAtIndexPath path,
23
+ atScrollPosition: UITableViewScrollPositionTop, animated: true
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ module MotionPrime
2
+ class PasswordFieldSection < BaseFieldSection
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] || {})
12
+ end
13
+ 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] || {})
23
+ end
24
+ after_render :render_input
25
+
26
+ def render_input
27
+ view(:input).on :editing_did_begin do |view|
28
+ scroll_to_and_make_visible
29
+ form.on_edit(self)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,40 @@
1
+ module MotionPrime
2
+ class SelectFieldSection < BaseFieldSection
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] || {})
12
+ end
13
+ 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] || {})
21
+ end
22
+ 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
+
32
+ after_render :render_button
33
+
34
+ def render_button
35
+ view(:button).on :touch do
36
+ form.send(options[:action]) if options[:action]
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,32 @@
1
+ module MotionPrime
2
+ class StringFieldSection < BaseFieldSection
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] || {})
12
+ end
13
+ element :input, type: :text_field do
14
+ {
15
+ styles: [
16
+ :base_field_input,
17
+ :base_string_field_input,
18
+ :"#{form_name}_field_input",
19
+ :"#{form_name}_#{name}_field_input"
20
+ ],
21
+ }.merge(options[:input] || {})
22
+ end
23
+ after_render :render_input
24
+
25
+ def render_input
26
+ view(:input).on :editing_did_begin do |view|
27
+ scroll_to_and_make_visible
28
+ form.on_edit(self)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,20 @@
1
+ module MotionPrime
2
+ class SubmitFieldSection < BaseFieldSection
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])
11
+ end
12
+ after_render :render_submit
13
+
14
+ def render_submit
15
+ view(:submit).on :touch do
16
+ form.send(options[:action]) if options[:action]
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,33 @@
1
+ module MotionPrime
2
+ class TextFieldSection < BaseFieldSection
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] || {})
12
+ end
13
+ element :input, type: :text_view do
14
+ {
15
+ styles: [
16
+ :base_field_input,
17
+ :base_text_field_input,
18
+ :"#{form_name}_field_input",
19
+ :"#{form_name}_#{name}_field_input"
20
+ ],
21
+ editable: true
22
+ }.merge(options[:input] || {})
23
+ end
24
+ after_render :render_input
25
+
26
+ def render_input
27
+ view(:input).on :editing_did_begin do |view|
28
+ scroll_to_and_make_visible
29
+ form.on_edit(self)
30
+ end
31
+ end
32
+ end
33
+ end