motion-prime 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +83 -0
- data/README.md +67 -0
- data/Rakefile +23 -0
- data/app/app_delegate.rb +5 -0
- data/doc/SECTION.md +7 -0
- data/doc/STYLE.md +39 -0
- data/files/Gemfile +3 -0
- data/files/Rakefile +16 -0
- data/files/app/app_delegate.rb +4 -0
- data/files/app/screens/application_screen.rb +3 -0
- data/lib/motion-prime.rb +14 -0
- data/lib/view_styler.rb +141 -0
- data/motion-prime.gemspec +27 -0
- data/motion-prime/app_delegate.rb +56 -0
- data/motion-prime/elements/base.rb +94 -0
- data/motion-prime/elements/button.rb +7 -0
- data/motion-prime/elements/draw.rb +56 -0
- data/motion-prime/elements/draw/image.rb +43 -0
- data/motion-prime/elements/draw/label.rb +13 -0
- data/motion-prime/elements/image.rb +14 -0
- data/motion-prime/elements/label.rb +20 -0
- data/motion-prime/elements/text_field.rb +7 -0
- data/motion-prime/elements/text_view.rb +7 -0
- data/motion-prime/helpers/has_authorization.rb +10 -0
- data/motion-prime/helpers/has_search_bar.rb +25 -0
- data/motion-prime/models/base.rb +220 -0
- data/motion-prime/screens/_aliases_mixin.rb +32 -0
- data/motion-prime/screens/_base_mixin.rb +119 -0
- data/motion-prime/screens/_navigation_bar_mixin.rb +57 -0
- data/motion-prime/screens/_navigation_mixin.rb +118 -0
- data/motion-prime/screens/_orientations_mixin.rb +39 -0
- data/motion-prime/screens/base_screen.rb +22 -0
- data/motion-prime/screens/sidebar_container_screen.rb +58 -0
- data/motion-prime/sections/base.rb +101 -0
- data/motion-prime/sections/draw.rb +62 -0
- data/motion-prime/sections/form.rb +103 -0
- data/motion-prime/sections/form/base_field_section.rb +26 -0
- data/motion-prime/sections/form/password_field_section.rb +33 -0
- data/motion-prime/sections/form/select_field_section.rb +40 -0
- data/motion-prime/sections/form/string_field_section.rb +32 -0
- data/motion-prime/sections/form/submit_field_section.rb +20 -0
- data/motion-prime/sections/form/text_field_section.rb +33 -0
- data/motion-prime/sections/table.rb +97 -0
- data/motion-prime/sections/table/refresh_mixin.rb +13 -0
- data/motion-prime/styles/forms.rb +93 -0
- data/motion-prime/support/_key_value_store.rb +10 -0
- data/motion-prime/support/dm_button.rb +22 -0
- data/motion-prime/support/dm_cell_with_section.rb +12 -0
- data/motion-prime/support/dm_text_field.rb +30 -0
- data/motion-prime/support/dm_text_view.rb +93 -0
- data/motion-prime/support/dm_view_controller.rb +50 -0
- data/motion-prime/support/dm_view_with_section.rb +11 -0
- data/motion-prime/support/navigation_controller.rb +4 -0
- data/motion-prime/support/ui_search_bar_custom.rb +10 -0
- data/motion-prime/support/ui_view.rb +59 -0
- data/motion-prime/version.rb +3 -0
- data/motion-prime/views/layout.rb +45 -0
- data/motion-prime/views/styles.rb +44 -0
- data/motion-prime/views/view_builder.rb +80 -0
- data/motion-prime/views/view_styler.rb +141 -0
- data/resources/Default-568h@2x.png +0 -0
- data/spec/main_spec.rb +9 -0
- metadata +245 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
motion_require './table/refresh_mixin'
|
2
|
+
module MotionPrime
|
3
|
+
class TableSection < BaseSection
|
4
|
+
include TableSectionRefreshMixin
|
5
|
+
include HasSearchBar
|
6
|
+
|
7
|
+
attr_accessor :table_view
|
8
|
+
before_render :render_table
|
9
|
+
|
10
|
+
def table_data
|
11
|
+
[]
|
12
|
+
end
|
13
|
+
|
14
|
+
def data
|
15
|
+
@data ||= begin
|
16
|
+
table_data
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def reload_data
|
21
|
+
@data = nil
|
22
|
+
@data_stamp = Time.now.to_i
|
23
|
+
table_view.reloadData
|
24
|
+
end
|
25
|
+
|
26
|
+
def render_table
|
27
|
+
@data_stamp = Time.now.to_i
|
28
|
+
self.table_view = screen.table_view(styles: [:base_table, name.to_sym], delegate: self, data_source: self).view
|
29
|
+
end
|
30
|
+
|
31
|
+
def render_cell(index, table)
|
32
|
+
cell = cached_cell(index)
|
33
|
+
return cell if cell
|
34
|
+
item = data[index.row]
|
35
|
+
|
36
|
+
# define default styles for cell
|
37
|
+
styles = [:"#{name}_cell"]
|
38
|
+
if item.respond_to?(:container_styles) && item.container_styles.present?
|
39
|
+
styles += Array.wrap(item.container_styles)
|
40
|
+
end
|
41
|
+
if item.respond_to?(:name) && item.name.present?
|
42
|
+
styles += [item.name.to_sym]
|
43
|
+
end
|
44
|
+
|
45
|
+
# DrawSection allows as to draw inside the cell view, so we can setup
|
46
|
+
# to use cell view as container
|
47
|
+
if item.is_a?(MotionPrime::DrawSection)
|
48
|
+
item.render(to: screen, as: :cell,
|
49
|
+
styles: [:base_table_cell] + styles,
|
50
|
+
reuse_identifier: cell_name(table, index)
|
51
|
+
)
|
52
|
+
else
|
53
|
+
screen.table_view_cell styles: [:base_table_cell] + styles, reuse_identifier: cell_name(table, index) do
|
54
|
+
item.render(to: screen)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_click(table, index)
|
60
|
+
end
|
61
|
+
|
62
|
+
def cell_name(table, index)
|
63
|
+
record = data[index.row]
|
64
|
+
if record && record.model &&
|
65
|
+
record.model.respond_to?(:id) && record.model.id.present?
|
66
|
+
"cell_#{record.model.id}_#{@data_stamp}"
|
67
|
+
else
|
68
|
+
"cell_#{index.section}_#{index.row}_#{@data_stamp}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def cached_cell(index, table = nil)
|
73
|
+
table ||= self.table_view
|
74
|
+
table.dequeueReusableCellWithIdentifier(cell_name(table, index))
|
75
|
+
end
|
76
|
+
|
77
|
+
# ALIASES
|
78
|
+
# ---------------------
|
79
|
+
|
80
|
+
def tableView(table, cellForRowAtIndexPath:index)
|
81
|
+
cell = cached_cell(index, table) || render_cell(index, table)
|
82
|
+
cell.is_a?(UIView) ? cell : cell.view
|
83
|
+
end
|
84
|
+
|
85
|
+
def tableView(table, numberOfRowsInSection:section)
|
86
|
+
data.length
|
87
|
+
end
|
88
|
+
|
89
|
+
def tableView(table, didSelectRowAtIndexPath:index)
|
90
|
+
on_click(table, index)
|
91
|
+
end
|
92
|
+
|
93
|
+
def tableView(table, heightForRowAtIndexPath:index)
|
94
|
+
data[index.row].container_height
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module MotionPrime
|
2
|
+
module TableSectionRefreshMixin
|
3
|
+
def add_pull_to_refresh(&block)
|
4
|
+
table_view.addPullToRefreshWithActionHandler(block)
|
5
|
+
screen.setup table_view.pullToRefreshView, styles: [:base_pull_to_refresh]
|
6
|
+
end
|
7
|
+
|
8
|
+
def fisnish_pull_to_refresh
|
9
|
+
reload_data
|
10
|
+
table_view.pullToRefreshView.stopAnimating
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
motion_require '../views/styles.rb'
|
2
|
+
MotionPrime::Styles.define :base do
|
3
|
+
# basic table styles
|
4
|
+
# ----------
|
5
|
+
style :table,
|
6
|
+
top: 0,
|
7
|
+
left: 0,
|
8
|
+
width: 320,
|
9
|
+
bottom: 0
|
10
|
+
|
11
|
+
# basic form styles
|
12
|
+
# ----------
|
13
|
+
style :form,
|
14
|
+
width: 260,
|
15
|
+
left: 30,
|
16
|
+
top: 0,
|
17
|
+
right: 30,
|
18
|
+
bottom: 0,
|
19
|
+
background_color: :clear,
|
20
|
+
separator_color: :clear,
|
21
|
+
scroll_enabled: true
|
22
|
+
|
23
|
+
style :form_field,
|
24
|
+
selection_style: UITableViewCellSelectionStyleNone
|
25
|
+
|
26
|
+
# available options for string label:
|
27
|
+
# @background_color: COLOR
|
28
|
+
# @text_color: COLOR
|
29
|
+
style :field_label,
|
30
|
+
background_color: :clear,
|
31
|
+
text_color: :gray,
|
32
|
+
top: 6,
|
33
|
+
height: 20,
|
34
|
+
left: 0,
|
35
|
+
right: 0,
|
36
|
+
font: proc { APP_CONFIG[:css_font_base].uifont(12) },
|
37
|
+
size_to_fit: true
|
38
|
+
|
39
|
+
# available options for input:
|
40
|
+
# @layer: @border_width: FLOAT
|
41
|
+
# @layer: @border_color: COLOR
|
42
|
+
# @background_color: COLOR
|
43
|
+
# @background_image: PATH_TO_FILE
|
44
|
+
style :field_input,
|
45
|
+
layer: {
|
46
|
+
border_width: 1,
|
47
|
+
border_color: :gray
|
48
|
+
},
|
49
|
+
font: proc { APP_CONFIG[:css_font_base].uifont(16) },
|
50
|
+
placeholder_font: proc { APP_CONFIG[:css_font_base].uifont(16) },
|
51
|
+
background_color: :white,
|
52
|
+
left: 0,
|
53
|
+
right: 0,
|
54
|
+
top: 30,
|
55
|
+
bottom: 0,
|
56
|
+
padding_top: 4
|
57
|
+
|
58
|
+
# available options for submit button:
|
59
|
+
# @button_type: :rounded, :custom
|
60
|
+
# @background_color: COLOR
|
61
|
+
# @background_image: PATH_TO_FILE
|
62
|
+
style :submit_button,
|
63
|
+
background_color: :gray,
|
64
|
+
left: 0,
|
65
|
+
right: 0,
|
66
|
+
top: 10,
|
67
|
+
height: 44,
|
68
|
+
title_color: :white
|
69
|
+
|
70
|
+
style :select_field_button,
|
71
|
+
background_color: :white,
|
72
|
+
left: 0,
|
73
|
+
right: 0,
|
74
|
+
top: 30,
|
75
|
+
height: 36,
|
76
|
+
title_color: 0x16759a,
|
77
|
+
title_shadow_color: :white,
|
78
|
+
contentHorizontalAlignment: UIControlContentHorizontalAlignmentLeft,
|
79
|
+
layer: {
|
80
|
+
border_color: :gray,
|
81
|
+
border_width: 1
|
82
|
+
},
|
83
|
+
title_color: :gray,
|
84
|
+
title_label: {
|
85
|
+
font: proc { APP_CONFIG[:css_font_base].uifont(16) }
|
86
|
+
}
|
87
|
+
style :select_field_arrow,
|
88
|
+
image: "images/forms/select_arrow.png",
|
89
|
+
top: 42,
|
90
|
+
right: 5,
|
91
|
+
width: 9,
|
92
|
+
height: 14
|
93
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class DMButton < UIButton
|
2
|
+
include MotionPrime::KeyValueStore
|
3
|
+
attr_accessor :paddingLeft, :paddingTop, :padding
|
4
|
+
|
5
|
+
def setTitle(value)
|
6
|
+
setTitle value, forState: UIControlStateNormal
|
7
|
+
end
|
8
|
+
|
9
|
+
def drawPadding(rect)
|
10
|
+
padding_left = self.paddingLeft || self.padding || 5
|
11
|
+
padding_top = self.paddingTop || self.padding || 0
|
12
|
+
self.setTitleEdgeInsets UIEdgeInsetsMake(
|
13
|
+
padding_top, padding_left,
|
14
|
+
padding_top, padding_left
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def drawRect(rect)
|
19
|
+
drawPadding(rect)
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# This class have some modifications for UITextField:
|
2
|
+
# * support padding, padding_left, padding_right options
|
3
|
+
# * support placeholder_color, placeholder_font options
|
4
|
+
class DMTextField < UITextField
|
5
|
+
include MotionPrime::KeyValueStore
|
6
|
+
|
7
|
+
attr_accessor :paddingLeft, :paddingTop, :padding,
|
8
|
+
:placeholderColor, :placeholderFont
|
9
|
+
|
10
|
+
# placeholder position
|
11
|
+
def textRectForBounds(bounds)
|
12
|
+
padding_left = self.paddingLeft || self.padding || 5
|
13
|
+
padding_top = self.paddingTop || self.padding || 3
|
14
|
+
CGRectInset(bounds, padding_left, padding_top)
|
15
|
+
end
|
16
|
+
|
17
|
+
# text position
|
18
|
+
def editingRectForBounds(bounds)
|
19
|
+
padding_left = self.paddingLeft || self.padding || 5
|
20
|
+
padding_top = self.paddingTop || self.padding || 3
|
21
|
+
CGRectInset(bounds, padding_left, padding_top)
|
22
|
+
end
|
23
|
+
|
24
|
+
def drawPlaceholderInRect(rect)
|
25
|
+
color = self.placeholderColor || :gray.uicolor
|
26
|
+
color.setFill
|
27
|
+
font = self.placeholderFont || self.font || :system.uifont(16)
|
28
|
+
self.placeholder.drawInRect(rect, withFont: font)
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# This class have some modifications for UITextView:
|
2
|
+
# * support padding, padding_left, padding_right options
|
3
|
+
# * support placeholder, placeholder_color, placeholder_font options
|
4
|
+
class DMTextView < UITextView
|
5
|
+
include MotionPrime::KeyValueStore
|
6
|
+
DEFAULT_PADDING_LEFT = 7
|
7
|
+
|
8
|
+
attr_accessor :paddingLeft, :paddingTop, :padding,
|
9
|
+
:placeholderColor, :placeholderFont, :placeholder
|
10
|
+
|
11
|
+
def drawPadding(rect)
|
12
|
+
# add padding to UITextView
|
13
|
+
padding_left = 0 - DEFAULT_PADDING_LEFT + (self.paddingLeft || self.padding || 5)
|
14
|
+
padding_top = 0 - DEFAULT_PADDING_LEFT + (self.paddingTop || self.padding || 5)
|
15
|
+
padding = UIEdgeInsetsMake(
|
16
|
+
padding_top, padding_left,
|
17
|
+
padding_top, padding_left
|
18
|
+
)
|
19
|
+
self.contentInset = padding
|
20
|
+
|
21
|
+
# must change frame before bounds because the text wrap is reformatted based on frame,
|
22
|
+
# don't include the top and bottom insets
|
23
|
+
insetFrame = UIEdgeInsetsInsetRect(frame, UIEdgeInsetsMake(0, padding.left, 0, padding.right))
|
24
|
+
# offset frame back to original x
|
25
|
+
offsetX = frame.origin.x - (insetFrame.origin.x - ( padding.left + padding.right ) / 2)
|
26
|
+
insetFrame = CGRectApplyAffineTransform(insetFrame, CGAffineTransformMakeTranslation(offsetX, 0))
|
27
|
+
self.frame = insetFrame
|
28
|
+
self.bounds = UIEdgeInsetsInsetRect(self.bounds, UIEdgeInsetsMake(0, -padding.left, 0, -padding.right))
|
29
|
+
end
|
30
|
+
|
31
|
+
def drawPlaceholder(rect)
|
32
|
+
padding_left = self.paddingLeft || self.padding || 5
|
33
|
+
padding_top = self.paddingTop || self.padding || 5
|
34
|
+
padding = UIEdgeInsetsMake(
|
35
|
+
padding_top, padding_left,
|
36
|
+
padding_top, padding_left
|
37
|
+
)
|
38
|
+
if self.placeholder && self.text.blank?
|
39
|
+
color = self.placeholderColor || :gray.uicolor
|
40
|
+
color.setFill
|
41
|
+
font = self.placeholderFont || self.font || :system.uifont(16)
|
42
|
+
|
43
|
+
color.setFill
|
44
|
+
rect = CGRectMake(
|
45
|
+
rect.origin.x + padding_left,
|
46
|
+
rect.origin.y + padding_top,
|
47
|
+
self.frame.size.width - padding_left,
|
48
|
+
self.frame.size.height - padding_top
|
49
|
+
)
|
50
|
+
placeholder.drawInRect(rect, withFont: font)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def drawRect(rect)
|
55
|
+
drawPadding(rect)
|
56
|
+
drawPlaceholder(rect)
|
57
|
+
super
|
58
|
+
end
|
59
|
+
|
60
|
+
def initPlaceholder
|
61
|
+
NSNotificationCenter.defaultCenter.addObserver(self,
|
62
|
+
selector: :textChanged, name: UITextViewTextDidChangeNotification, object: self
|
63
|
+
)
|
64
|
+
@shouldDrawPlaceholder = placeholder && self.text.blank?
|
65
|
+
end
|
66
|
+
|
67
|
+
def textChanged
|
68
|
+
updatePlaceholderDraw
|
69
|
+
end
|
70
|
+
|
71
|
+
def updatePlaceholderDraw
|
72
|
+
prev = @shouldDrawPlaceholder
|
73
|
+
@shouldDrawPlaceholder = placeholder && self.text.blank?
|
74
|
+
if prev != @shouldDrawPlaceholder
|
75
|
+
self.setNeedsDisplay
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# custom initializer
|
80
|
+
def initWithCoder(aDecoder)
|
81
|
+
if super
|
82
|
+
initPlaceholder
|
83
|
+
end
|
84
|
+
self
|
85
|
+
end
|
86
|
+
|
87
|
+
def initWithFrame(frame)
|
88
|
+
if super
|
89
|
+
initPlaceholder
|
90
|
+
end
|
91
|
+
self
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class DMViewController < UIViewController
|
2
|
+
def self.new(args = {})
|
3
|
+
s = self.alloc.initWithNibName(nil, bundle:nil)
|
4
|
+
s.on_create(args) if s.respond_to?(:on_create)
|
5
|
+
s
|
6
|
+
end
|
7
|
+
|
8
|
+
def viewDidLoad
|
9
|
+
super
|
10
|
+
self.view_did_load if self.respond_to?(:view_did_load)
|
11
|
+
end
|
12
|
+
|
13
|
+
def viewWillAppear(animated)
|
14
|
+
super
|
15
|
+
self.view_will_appear(animated) if self.respond_to?("view_will_appear:")
|
16
|
+
end
|
17
|
+
|
18
|
+
def viewDidAppear(animated)
|
19
|
+
super
|
20
|
+
self.view_did_appear(animated) if self.respond_to?("view_did_appear:")
|
21
|
+
end
|
22
|
+
|
23
|
+
def viewWillDisappear(animated)
|
24
|
+
self.view_will_disappear(animated) if self.respond_to?("view_will_disappear:")
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
def viewDidDisappear(animated)
|
29
|
+
if self.respond_to?("view_did_disappear:")
|
30
|
+
self.view_did_disappear(animated)
|
31
|
+
end
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
def shouldAutorotateToInterfaceOrientation(orientation)
|
36
|
+
self.should_rotate(orientation)
|
37
|
+
end
|
38
|
+
|
39
|
+
def shouldAutorotate
|
40
|
+
self.should_autorotate
|
41
|
+
end
|
42
|
+
|
43
|
+
def willRotateToInterfaceOrientation(orientation, duration:duration)
|
44
|
+
self.will_rotate(orientation, duration)
|
45
|
+
end
|
46
|
+
|
47
|
+
def didRotateFromInterfaceOrientation(orientation)
|
48
|
+
self.on_rotate
|
49
|
+
end
|
50
|
+
end
|