motion-prime 0.1.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 (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