dom-rb 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/csr/dom/browser_ext/node.rb +101 -0
- data/csr/dom/builder/base.rb +210 -0
- data/csr/dom/builder/binding.rb +44 -0
- data/csr/dom/builder/helpers.rb +343 -0
- data/csr/dom/builder.rb +7 -0
- data/csr/dom/content.rb +269 -0
- data/csr/dom/debug.rb +61 -0
- data/csr/dom/globals.rb +19 -0
- data/csr/dom/root.rb +37 -0
- data/csr/dom/table/base.rb +300 -0
- data/csr/dom/table/caption.rb +59 -0
- data/csr/dom/table/column.rb +84 -0
- data/csr/dom/table/row.rb +118 -0
- data/csr/dom/table/section.rb +208 -0
- data/csr/dom/table.rb +1 -0
- data/csr/dom/util.rb +86 -0
- data/csr/dom.rb +5 -0
- data/lib/dom/version.rb +5 -0
- data/lib/dom-rb.rb +11 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7bea94521028abe3b88220fd369b2ed399d49094
|
4
|
+
data.tar.gz: 2fd651272b2923fb793a1b38d2c46ce814ce6856
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2c72e3408c30567670e74eecd2461bc22b4d72606b19dcfb282664451c5feba2abbbf34b408633875c5cd0c09ee91dfce4a1779a2754484127ba7e9c6bf948af
|
7
|
+
data.tar.gz: ee2775001232bf81ea60da2d4b5c0b985b38857384369efeba9eccab04a501fb6d3f1ef6a232a3a9125fe581c73e94ea3171a2218680c637e4ecf98de27538e1
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'opal-browser'
|
2
|
+
|
3
|
+
# Add binding management to Browser::DOM::Node
|
4
|
+
|
5
|
+
class Browser
|
6
|
+
class DOM
|
7
|
+
class Document
|
8
|
+
def active_element
|
9
|
+
el = `#@native.activeElement()`
|
10
|
+
if el
|
11
|
+
Browser::DOM::Node.new(el)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Browser
|
19
|
+
class DOM
|
20
|
+
class Element
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Browser
|
26
|
+
class DOM
|
27
|
+
class Node
|
28
|
+
# requires jquery and bootstrap.js - will fail otherwise
|
29
|
+
def check_jquery
|
30
|
+
%x(
|
31
|
+
if (typeof jQuery === 'undefined') {
|
32
|
+
throw new Error('Bootstrap\'s JavaScript requires jQuery')
|
33
|
+
}
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def check_bootstrap
|
38
|
+
%x(
|
39
|
+
if (typeof($.fn.tooltip) === 'undefined') {
|
40
|
+
throw new Error('Bootstrap.js not loaded')
|
41
|
+
}
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
# TODO: replicate this for all Bootstrap js features
|
46
|
+
# TODO: refactor all bootstrap stuff to separate module
|
47
|
+
# arg may be:
|
48
|
+
# 0. nil - attaches tooltip to node
|
49
|
+
# 1. hash of options and attaches tooltip to node
|
50
|
+
# 2. 'show'
|
51
|
+
# 3. 'hide'
|
52
|
+
# 4. 'toggle'
|
53
|
+
# 4. 'destroy'
|
54
|
+
# see http://getbootstrap.com/javascript/#tooltips
|
55
|
+
def tooltip(arg=nil)
|
56
|
+
check_bootstrap
|
57
|
+
`$(#@native).tooltip(#{arg.to_n})`
|
58
|
+
end
|
59
|
+
|
60
|
+
def popover(arg=nil)
|
61
|
+
check_bootstrap
|
62
|
+
`$(#@native).popover(#{arg.to_n})`
|
63
|
+
end
|
64
|
+
|
65
|
+
def bindings=(bindings)
|
66
|
+
# store bindings in js element
|
67
|
+
`#@native.voltBindings = #{bindings}`
|
68
|
+
end
|
69
|
+
|
70
|
+
def bindings
|
71
|
+
# bindings stored in js element
|
72
|
+
b = `#@native.voltBindings`
|
73
|
+
`b === undefined` ? nil : b
|
74
|
+
end
|
75
|
+
|
76
|
+
# bindings set (if required) by Paggio::HTML::Element
|
77
|
+
def start_bindings
|
78
|
+
if bindings
|
79
|
+
bindings.each do |b|
|
80
|
+
# cannot use self as self is transient
|
81
|
+
b.start(self.id)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def stop_bindings
|
87
|
+
if bindings
|
88
|
+
bindings.each do |b|
|
89
|
+
b.stop
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def bindings_count
|
95
|
+
b = bindings
|
96
|
+
b ? b.size : 0
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
require 'opal-browser'
|
2
|
+
require 'dom/globals'
|
3
|
+
require 'dom/builder/binding'
|
4
|
+
require 'dom/browser_ext/node'
|
5
|
+
require 'dom/util'
|
6
|
+
require 'dom/debug'
|
7
|
+
|
8
|
+
module CSR
|
9
|
+
module DOM
|
10
|
+
module Builder
|
11
|
+
include CSR::DOM::Globals
|
12
|
+
include CSR::DOM::Util
|
13
|
+
include CSR::DOM::Debug
|
14
|
+
|
15
|
+
module_function
|
16
|
+
|
17
|
+
def br
|
18
|
+
tag(:br)
|
19
|
+
end
|
20
|
+
|
21
|
+
def div(attributes: nil, content: nil)
|
22
|
+
tag(:div, attributes: attributes, content: content)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Any attribute or content which is a BoundProc will be
|
26
|
+
# converted to bindings. See CSR::DOM::Binding.
|
27
|
+
# Any attributes or content which is a Proc will be
|
28
|
+
# resolved by proc.call.
|
29
|
+
def tag(name, attributes: nil, content: nil)
|
30
|
+
# self.debug_level = 3
|
31
|
+
attributes = attributes ? attributes : {}
|
32
|
+
namespace = attributes[:namespace]
|
33
|
+
element = document.create_element(name, {namespace: namespace})
|
34
|
+
id = attributes.delete(:id) || attributes.delete('id')
|
35
|
+
if id
|
36
|
+
id = id.call if Proc === id
|
37
|
+
element.id = id
|
38
|
+
end
|
39
|
+
bindings = []
|
40
|
+
attributes.each do |key, value|
|
41
|
+
if key == :style
|
42
|
+
value = resolve_value(element, value, bindings, id, :style, name)
|
43
|
+
if value
|
44
|
+
value = normalize_style(value)
|
45
|
+
element.style(value)
|
46
|
+
end
|
47
|
+
elsif key[0,2] == 'on'
|
48
|
+
# event stuff is in browser/event/base.rb
|
49
|
+
event = key[2..-1]
|
50
|
+
element.on(event, &value)
|
51
|
+
else
|
52
|
+
value = resolve_value(element, value, bindings, id, key, name)
|
53
|
+
if value
|
54
|
+
key = sanitize_attribute(key)
|
55
|
+
element[key] = value
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
content = resolve_value(element, content, bindings, id, :content, name)
|
60
|
+
if content
|
61
|
+
content = sanitize_content(content)
|
62
|
+
element << content
|
63
|
+
end
|
64
|
+
if bindings
|
65
|
+
element.bindings = bindings
|
66
|
+
end
|
67
|
+
element
|
68
|
+
end
|
69
|
+
|
70
|
+
def bind(proc=nil, &block)
|
71
|
+
BoundProc.new(proc, &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
def resolve_value(element, value, bindings, id, attr, tag_name)
|
75
|
+
# debug 1, ->{[ __FILE__, __LINE__, __method__, "tag=#{tag_name} value=#{value} bindings=#{bindings} id=#{id} attr=#{attr}" ]}
|
76
|
+
result = case value
|
77
|
+
when BoundProc
|
78
|
+
# debug 1, ->{[ __FILE__, __LINE__, __method__, "BoundProc===value" ]}
|
79
|
+
binding = Binding.new(
|
80
|
+
# This proc was set in bind and should expect a Binding as argument
|
81
|
+
# It can set up a watch, and should immediately set the binding value.
|
82
|
+
value.proc,
|
83
|
+
# this is local proc to update element with new value
|
84
|
+
-> (value) {
|
85
|
+
update_element(element, id, attr, value)
|
86
|
+
}
|
87
|
+
)
|
88
|
+
bindings << binding
|
89
|
+
# value proc should have set value in binding
|
90
|
+
binding.value
|
91
|
+
when Proc
|
92
|
+
# debug 1, ->{[ __FILE__, __LINE__, __method__, "Proc===value" ]}
|
93
|
+
value.call
|
94
|
+
else
|
95
|
+
# debug 1, ->{[ __FILE__, __LINE__, __method__, "value=#{value}" ]}
|
96
|
+
value
|
97
|
+
end
|
98
|
+
# debug 1, ->{[ __FILE__, __LINE__, __method__, "result=#{result}" ]}
|
99
|
+
result
|
100
|
+
end
|
101
|
+
|
102
|
+
def sanitize_attribute(key)
|
103
|
+
# key.to_s.gsub(/_([a-z])/) {|v| v[1].upcase}
|
104
|
+
key.to_s.gsub(/_/, '-')
|
105
|
+
end
|
106
|
+
|
107
|
+
def sanitize_content(content)
|
108
|
+
debug 3, ->{[ __FILE__, __LINE__, __method__, "content.class=#{content.class}" ]}
|
109
|
+
case content
|
110
|
+
when Browser::DOM::Element
|
111
|
+
content
|
112
|
+
when Enumerable
|
113
|
+
content.map {|e| sanitize_content(e)}
|
114
|
+
else
|
115
|
+
content.to_s
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def update_element(element, id, attr, value)
|
120
|
+
debug 2, ->{[ __FILE__, __LINE__, __method__, "element=#{element} id=#{id} attr=#{attr} value=#{value}" ]}
|
121
|
+
case attr
|
122
|
+
when :content
|
123
|
+
content = sanitize_content(value)
|
124
|
+
debug 2, ->{[ __FILE__, __LINE__, __method__, "content=#{content}"]}
|
125
|
+
element.clear
|
126
|
+
element << content
|
127
|
+
when :style
|
128
|
+
debug 2, ->{[ __FILE__, __LINE__, __method__ ]}
|
129
|
+
element.style(normalize_style(value || {}))
|
130
|
+
else
|
131
|
+
debug 2, ->{[ __FILE__, __LINE__, __method__ ]}
|
132
|
+
element[attr] = value
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def <<(*args, &block)
|
137
|
+
debug 1, ->{[ __FILE__, __LINE__, __method__, "size=#{args.size} args=#{args}" ]}
|
138
|
+
if block
|
139
|
+
self << block.call
|
140
|
+
else
|
141
|
+
if args.size == 1
|
142
|
+
dom_root << case args[0]
|
143
|
+
when Hash
|
144
|
+
name = args[0].delete(:tag)
|
145
|
+
tag(name, args[0])
|
146
|
+
else
|
147
|
+
args[0]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
alias_method :add, :<<
|
154
|
+
|
155
|
+
# Returns a Browser::DOM::Element with given id or nil.
|
156
|
+
def element(id)
|
157
|
+
document[id.to_s]
|
158
|
+
end
|
159
|
+
|
160
|
+
alias_method :el, :element
|
161
|
+
alias_method :[], :element
|
162
|
+
|
163
|
+
def remove(element)
|
164
|
+
fail "#{self.class.name}##{__method__} not implemented"
|
165
|
+
end
|
166
|
+
|
167
|
+
def replace(element)
|
168
|
+
fail "#{self.class.name}##{__method__} not implemented"
|
169
|
+
end
|
170
|
+
|
171
|
+
def traverse_dom(node_or_nodeset = nil, &block)
|
172
|
+
node_or_nodeset ||= dom_root
|
173
|
+
(NodeSet === node_or_nodeset ? node_or_nodeset : [node_or_nodeset]).each do |node|
|
174
|
+
yield DOM(node)
|
175
|
+
node.children.each do |child|
|
176
|
+
traverse_dom(DOM(child), &block)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def stop_bindings(id = nil)
|
182
|
+
switch_bindings(id, start: false)
|
183
|
+
end
|
184
|
+
|
185
|
+
def start_bindings(id = nil)
|
186
|
+
switch_bindings(id, start: true)
|
187
|
+
end
|
188
|
+
|
189
|
+
def switch_bindings(id = nil, start: true)
|
190
|
+
first = nil
|
191
|
+
if id
|
192
|
+
first = self[id]
|
193
|
+
unless first
|
194
|
+
fail "#{self.class.name}##{__method__}:#{__LINE_} : no element with id '#{id}'"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
traverse_dom(first) do |node|
|
198
|
+
start ? node.start_bindings : node.stop_bindings
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def shutdown
|
203
|
+
stop_bindings
|
204
|
+
clear_interval
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Usage
|
2
|
+
#
|
3
|
+
# tag(
|
4
|
+
# 'h3',
|
5
|
+
# content: bind do |binding|
|
6
|
+
# # set up the watch
|
7
|
+
# -> {
|
8
|
+
# binding.value = user.name
|
9
|
+
# }.watch!
|
10
|
+
# end
|
11
|
+
# )
|
12
|
+
|
13
|
+
module CSR
|
14
|
+
module DOM
|
15
|
+
class BoundProc
|
16
|
+
attr_reader :proc
|
17
|
+
|
18
|
+
def initialize(proc=nil, &block)
|
19
|
+
@proc = proc || block
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Binding
|
24
|
+
|
25
|
+
attr_reader :value
|
26
|
+
|
27
|
+
def initialize(value_proc, action_proc)
|
28
|
+
@value_proc = value_proc
|
29
|
+
@action_proc = action_proc
|
30
|
+
@value = nil
|
31
|
+
@value_proc.call(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
# TODO: make equality checking optional?
|
35
|
+
def value=(arg)
|
36
|
+
unless @value == arg
|
37
|
+
@value = arg
|
38
|
+
@action_proc.call(arg)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,343 @@
|
|
1
|
+
require 'dom/builder/base'
|
2
|
+
|
3
|
+
# adds helper to CSR::DOM::Builder
|
4
|
+
|
5
|
+
module CSR
|
6
|
+
module DOM
|
7
|
+
module Builder
|
8
|
+
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def icon(name, callback: nil, css: nil, float: nil, margins: nil, style: nil)
|
12
|
+
_style = {
|
13
|
+
text_align: 'center',
|
14
|
+
vertical_align: 'middle',
|
15
|
+
color: 'inherit',
|
16
|
+
background_color: 'inherit',
|
17
|
+
cursor: 'pointer',
|
18
|
+
}
|
19
|
+
if margins
|
20
|
+
_style =_style.merge(margins)
|
21
|
+
elsif float
|
22
|
+
_style = _style.merge(
|
23
|
+
case float.to_sym
|
24
|
+
when :left
|
25
|
+
{ margin_right: '0.5em' } # { margin_top: '0.2em', margin_right: '0.5em' }
|
26
|
+
when :right
|
27
|
+
{ margin_left: '0.5em' } # { margin_top: '0.2em', margin_right: '0.5em' }
|
28
|
+
else
|
29
|
+
{ } # { margin_top: '0.2em', margin_right: '0.5em' }
|
30
|
+
end
|
31
|
+
)
|
32
|
+
end
|
33
|
+
# arg style overrides any defaults
|
34
|
+
_style = _style.merge(style) if style
|
35
|
+
_class = iconify(name)
|
36
|
+
_class = "#{_class} #{css}" if css
|
37
|
+
_class = "#{_class} pull-#{float}" if float
|
38
|
+
attributes = {
|
39
|
+
class: _class,
|
40
|
+
style: _style
|
41
|
+
}.merge(
|
42
|
+
callback ? { onclick: callback } : {}
|
43
|
+
)
|
44
|
+
tag(
|
45
|
+
:span,
|
46
|
+
attributes: attributes
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def icon_with_anchor(icon_name, href, float: nil, margins: nil, icon_css: nil, anchor_css: nil, icon_style: nil, anchor_style: nil)
|
51
|
+
_icon = icon(icon_name, css: icon_css, float: float, margins: margins, style: icon_style)
|
52
|
+
tag(
|
53
|
+
:a,
|
54
|
+
attributes: {
|
55
|
+
href: href,
|
56
|
+
style: anchor_style,
|
57
|
+
class: anchor_css || '',
|
58
|
+
},
|
59
|
+
content: _icon
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
def plus_sign_icon(href = nil)
|
64
|
+
icon_with_anchor(
|
65
|
+
:plus_sign,
|
66
|
+
href,
|
67
|
+
icon_style: { color: 'lightgreen'}
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def remove_sign_icon(callback = nil)
|
72
|
+
icon(
|
73
|
+
:remove_sign,
|
74
|
+
callback: callback,
|
75
|
+
style: {color: 'red'}
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
def refresh_icon(callback = nil)
|
80
|
+
icon(
|
81
|
+
:refresh,
|
82
|
+
callback: callback,
|
83
|
+
style: {color: 'inherit'}
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
def save_icon(callback = nil)
|
88
|
+
icon(
|
89
|
+
:save,
|
90
|
+
callback: callback,
|
91
|
+
style: {color: 'inherit'}
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
def hamburger_icon(callback = nil)
|
96
|
+
icon(
|
97
|
+
:menu_hamburger,
|
98
|
+
callback: callback,
|
99
|
+
style: {color: 'inherit'}
|
100
|
+
)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns a span element with icon and dropdown menu
|
104
|
+
#
|
105
|
+
# 1. icon: is the name of the (bootstrap) icon
|
106
|
+
# 2. items: should be hashes containing menu items
|
107
|
+
# e.g. { callback: ->{}, href: '#', content: 'list item'}
|
108
|
+
# 3. content: of the div (apart from the icon)
|
109
|
+
# 4. pull: which side of div to pull the span, 'right' or 'left'
|
110
|
+
# 5. style: any additional styling fro the span/icon
|
111
|
+
def drop_down_icon(icon: 'menu-hamburger', items: [], pull: 'right', style: {})
|
112
|
+
pull = pull.to_s
|
113
|
+
right = pull == 'right'
|
114
|
+
tag(
|
115
|
+
:span,
|
116
|
+
attributes: {
|
117
|
+
class: "pull-#{pull}"
|
118
|
+
},
|
119
|
+
content: tag(
|
120
|
+
:div,
|
121
|
+
attributes: {
|
122
|
+
class: 'dropdown',
|
123
|
+
},
|
124
|
+
content: [
|
125
|
+
tag(
|
126
|
+
:div,
|
127
|
+
attributes: {
|
128
|
+
class: 'dropdown-toggle',
|
129
|
+
# type: 'button',
|
130
|
+
data_toggle: 'dropdown',
|
131
|
+
style: {
|
132
|
+
font_size: 'smaller',
|
133
|
+
# margin_top: '0.2em',
|
134
|
+
# margin_bottom: '0.1em',
|
135
|
+
margin_left: right ? '0.5em' : '0.3em',
|
136
|
+
margin_right: right ? '0.3em' : '0.5em',
|
137
|
+
vertical_align: 'middle',
|
138
|
+
color: 'inherit',
|
139
|
+
background_color: 'inherit',
|
140
|
+
}.merge(
|
141
|
+
style
|
142
|
+
)
|
143
|
+
},
|
144
|
+
content: icon(
|
145
|
+
icon,
|
146
|
+
style: {
|
147
|
+
font_size: 'smaller',
|
148
|
+
# margin_top: '0.2em',
|
149
|
+
margin_bottom: '0.2em',
|
150
|
+
margin_left: right ? '0.5em' : '0.3em',
|
151
|
+
margin_right: right ? '0.3em' : '0.5em',
|
152
|
+
vertical_align: 'middle',
|
153
|
+
color: 'inherit',
|
154
|
+
background_color: 'inherit',
|
155
|
+
}.merge(
|
156
|
+
style
|
157
|
+
)
|
158
|
+
)
|
159
|
+
),
|
160
|
+
tag(
|
161
|
+
:ul,
|
162
|
+
attributes: {
|
163
|
+
class: "dropdown-menu#{right ? ' dropdown-menu-right' : nil}"
|
164
|
+
},
|
165
|
+
content: items.map { |item|
|
166
|
+
content = item[:content]
|
167
|
+
`console.log(#{"#{__FILE__}[#{__LINE__}]: adding item #{content} to menu icon #{icon}"})`
|
168
|
+
if content == 'divider' || content == 'separator'
|
169
|
+
tag(
|
170
|
+
:li,
|
171
|
+
attributes: {
|
172
|
+
role: 'separator',
|
173
|
+
class: 'divider',
|
174
|
+
}
|
175
|
+
)
|
176
|
+
else
|
177
|
+
attrs = {}
|
178
|
+
attrs[:href] = item[:href] if item[:href]
|
179
|
+
attrs[:onclick] = item[:callback] if item[:callback]
|
180
|
+
tag(
|
181
|
+
:li,
|
182
|
+
content: tag(
|
183
|
+
:div,
|
184
|
+
content: content,
|
185
|
+
attributes: attrs
|
186
|
+
)
|
187
|
+
)
|
188
|
+
end
|
189
|
+
}
|
190
|
+
)
|
191
|
+
]
|
192
|
+
)
|
193
|
+
)
|
194
|
+
end
|
195
|
+
|
196
|
+
def plain_anchor(content, href)
|
197
|
+
tag(
|
198
|
+
:a,
|
199
|
+
attributes: {
|
200
|
+
href: href,
|
201
|
+
style: { color: 'inherit', background_color: 'inherit'}
|
202
|
+
},
|
203
|
+
content: content
|
204
|
+
)
|
205
|
+
end
|
206
|
+
|
207
|
+
def div_with_icon(callback, icon: nil, pull: nil, attributes: nil, content: nil, icon_style: {}, tooltip: nil, popover: nil)
|
208
|
+
icon ||= 'question-sign'
|
209
|
+
pull ||= 'left'
|
210
|
+
pull = pull.to_s
|
211
|
+
icon = tag(
|
212
|
+
:span,
|
213
|
+
attributes: {
|
214
|
+
onclick: callback,
|
215
|
+
class: "glyphicon glyphicon-#{icon} pull-#{pull}",
|
216
|
+
style: {
|
217
|
+
font_size: 'smaller',
|
218
|
+
margin_left: '0.5em',
|
219
|
+
margin_right: '0.5em',
|
220
|
+
vertical_align: 'middle',
|
221
|
+
color: 'inherit',
|
222
|
+
background_color: 'inherit',
|
223
|
+
}.merge(
|
224
|
+
icon_style # argument style overrides default
|
225
|
+
)
|
226
|
+
}
|
227
|
+
)
|
228
|
+
debug 1, ->{[__FILE__, __LINE__, __method__, "tooltip=#{tooltip} popover=#{popover}"]}
|
229
|
+
if tooltip
|
230
|
+
icon.tooltip(tooltip)
|
231
|
+
elsif popover
|
232
|
+
icon.popover(popover)
|
233
|
+
end
|
234
|
+
tag(
|
235
|
+
:div,
|
236
|
+
attributes: {
|
237
|
+
style: { cursor: 'pointer' }
|
238
|
+
}.merge(attributes || {}),
|
239
|
+
content: arrify(content, icon)
|
240
|
+
)
|
241
|
+
end
|
242
|
+
|
243
|
+
def div_with_sort_icon(callback, direction: 0, content: nil)
|
244
|
+
if direction != 0
|
245
|
+
tag(
|
246
|
+
:div,
|
247
|
+
attributes: {
|
248
|
+
onclick: callback,
|
249
|
+
style: { cursor: 'pointer' }
|
250
|
+
},
|
251
|
+
content: arrify(content) + [
|
252
|
+
tag(:span,
|
253
|
+
attributes: {
|
254
|
+
class: "glyphicon glyphicon-triangle-#{direction > 0 ? 'top' : 'bottom'}",
|
255
|
+
style: {
|
256
|
+
font_size: 'smaller',
|
257
|
+
margin_left: '0.5em',
|
258
|
+
vertical_align: 'middle',
|
259
|
+
color: 'inherit',
|
260
|
+
background_color: 'inherit',
|
261
|
+
}
|
262
|
+
}
|
263
|
+
)
|
264
|
+
]
|
265
|
+
)
|
266
|
+
else
|
267
|
+
tag(
|
268
|
+
:div,
|
269
|
+
attributes: {
|
270
|
+
onclick: callback,
|
271
|
+
style: { cursor: 'pointer' }
|
272
|
+
},
|
273
|
+
content: content
|
274
|
+
)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
def div_with_menu_up_down(callback, up: true, down: false, content: nil, pull: 'left')
|
279
|
+
div_with_up_down_icon(callback, which: :menu, up: up, down: down, content: content, pull: pull)
|
280
|
+
end
|
281
|
+
|
282
|
+
def div_with_collapse_up_down(callback, up: true, down: false, content: nil, pull: 'left')
|
283
|
+
div_with_up_down_icon(callback, which: :collapse, up: up, down: down, content: content, pull: pull)
|
284
|
+
end
|
285
|
+
|
286
|
+
# which can be :collapse or :menu (or string equivalents)
|
287
|
+
def div_with_up_down_icon(callback, which: :menu, up: true, down: false, content: nil, pull: 'left')
|
288
|
+
up = up && !down
|
289
|
+
pull = pull.to_s
|
290
|
+
left = pull == 'left'
|
291
|
+
tag(
|
292
|
+
:div,
|
293
|
+
attributes: {
|
294
|
+
onclick: callback,
|
295
|
+
style: { cursor: 'pointer' }
|
296
|
+
},
|
297
|
+
content: [
|
298
|
+
tag(
|
299
|
+
:span,
|
300
|
+
attributes: {
|
301
|
+
class: "glyphicon glyphicon-#{which}-#{up ? 'up' : 'down'} pull-#{pull}",
|
302
|
+
style: {
|
303
|
+
font_size: 'smaller',
|
304
|
+
margin_top: '0.2em',
|
305
|
+
margin_left: left ? '0.3em' : '0.5em',
|
306
|
+
margin_right: left ? '0.5em' : '0.3em',
|
307
|
+
vertical_align: 'middle',
|
308
|
+
color: 'inherit',
|
309
|
+
background_color: 'inherit',
|
310
|
+
}
|
311
|
+
}
|
312
|
+
)
|
313
|
+
] + arrify(content)
|
314
|
+
)
|
315
|
+
end
|
316
|
+
|
317
|
+
# Returns a div element with given content and an icon to left or right.
|
318
|
+
#
|
319
|
+
# 1. icon: is the name of the (bootstrap) icon
|
320
|
+
# 2. items: should be hashes containing menu items
|
321
|
+
# e.g. { callback: ->{}, href: '#', content: 'list item'}
|
322
|
+
# 3. content: of the div (apart from the icon)
|
323
|
+
# 4. pull: which side of div to pull the icon, 'right' or 'left'
|
324
|
+
def div_with_dropdown_icon(icon: 'menu-hamburger', items: [], content: nil, pull: 'right')
|
325
|
+
tag(
|
326
|
+
:div,
|
327
|
+
content: arrify(
|
328
|
+
drop_down_icon(icon: icon, items: items, pull: pull),
|
329
|
+
content
|
330
|
+
)
|
331
|
+
)
|
332
|
+
end
|
333
|
+
|
334
|
+
# TODO: generalize from bootstrap
|
335
|
+
def iconify(icon_name)
|
336
|
+
"glyphicon glyphicon-#{icon_name.to_s.gsub('_', '-')}"
|
337
|
+
end
|
338
|
+
|
339
|
+
end
|
340
|
+
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|