dom-rb 0.1.1
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 +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
|
+
|