better_select 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
data/better_select_rails.gemspec
CHANGED
@@ -8,10 +8,10 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.summary = "A better HTMLSelectElement replacement, for Rails"
|
9
9
|
gem.homepage = "http://www.benjaminbergstein.com"
|
10
10
|
|
11
|
-
gem.files = `git ls-files`.split($\) + ['vendor/javascripts', 'vendor/stylesheets']
|
12
11
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
12
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
13
|
gem.name = "better_select"
|
15
14
|
gem.require_paths = ["lib", "vendor"]
|
15
|
+
gem.files = `git ls-files`.split($\) + ['vendor/javascripts/better-select.js.coffee', 'vendor/stylesheets/better-select.css.scss']
|
16
16
|
gem.version = BetterSelect::VERSION
|
17
17
|
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
$$ = (html) ->
|
2
|
+
elm = document.createElement 'div'
|
3
|
+
elm.innerHTML = html
|
4
|
+
if elm.children.length > 1 then elm.children else elm.children[0]
|
5
|
+
|
6
|
+
getPos = (tgt, method) ->
|
7
|
+
pos = 0
|
8
|
+
while tgt
|
9
|
+
pos += tgt[method]
|
10
|
+
tgt = tgt.offsetParent
|
11
|
+
pos
|
12
|
+
|
13
|
+
getTop = (tgt) -> getPos tgt, 'offsetTop'
|
14
|
+
getLeft = (tgt) -> getPos tgt, 'offsetLeft'
|
15
|
+
|
16
|
+
getClasses = (tgt) -> tgt.getAttribute('class').split ' '
|
17
|
+
|
18
|
+
setClasses = (tgt, classes) ->
|
19
|
+
tgt.setAttribute 'class', classes.join ' '
|
20
|
+
tgt
|
21
|
+
|
22
|
+
addClass = (tgt, className) ->
|
23
|
+
classes = getClasses tgt
|
24
|
+
classes.push(className) if classes.indexOf(className) is -1
|
25
|
+
setClasses tgt, classes
|
26
|
+
|
27
|
+
removeClass = (tgt, className) ->
|
28
|
+
classes.splice(index, 1) unless index = (classes = getClasses tgt).indexOf className is -1
|
29
|
+
setClasses tgt, classes
|
30
|
+
|
31
|
+
letters = 'a b c d e f g h i j k l m n o p q r s t u v w x y z'.split ' '
|
32
|
+
numbers = '0 1 2 3 4 5 6 7 8 9 0'.split ' '
|
33
|
+
|
34
|
+
build_element = (what, orig, obj) ->
|
35
|
+
elm = $$ _.template(obj["#{what}_template"]) obj["process_#{what}"] orig
|
36
|
+
elm.orig = orig
|
37
|
+
orig.better_version = elm
|
38
|
+
elm.better_select = obj
|
39
|
+
elm
|
40
|
+
|
41
|
+
renderOption = (orig_option, bs) ->
|
42
|
+
option = build_element 'option', orig_option, bs
|
43
|
+
option.reset = ->
|
44
|
+
@orig.selected = undefined
|
45
|
+
@setAttribute 'class', 'option'
|
46
|
+
bs.reset @
|
47
|
+
option.select = ->
|
48
|
+
@orig.selected = 'selected'
|
49
|
+
@setAttribute 'class', 'option selected'
|
50
|
+
bs.toggle() if bs.select.getAttribute('class').indexOf('open') isnt -1
|
51
|
+
bs.set_selected option
|
52
|
+
option.addEventListener 'click', -> option.select()
|
53
|
+
option.addEventListener 'mouseover', -> option.better_select.set_focused option
|
54
|
+
bs.options.push option
|
55
|
+
first_char = option.innerHTML.substr(0, 1).toLowerCase()
|
56
|
+
bs.options_by_first_char[first_char] = [] unless bs.options_by_first_char[first_char]
|
57
|
+
bs.options_by_first_char[first_char].push option
|
58
|
+
bs.options_by_first_char[first_char].sort()
|
59
|
+
option
|
60
|
+
|
61
|
+
renderOptionGroup = (orig_group, bs) ->
|
62
|
+
group = build_element 'option_group', orig_group, bs
|
63
|
+
group.appendChild(renderOption child, bs) for child in orig_group.children
|
64
|
+
bs.option_groups.push group
|
65
|
+
group
|
66
|
+
|
67
|
+
class BetterSelect
|
68
|
+
defaults:
|
69
|
+
positionDropdown: true
|
70
|
+
constructor: (elm, options) ->
|
71
|
+
return unless elm && elm.tagName && elm.tagName is 'SELECT'
|
72
|
+
@settings = _.extend {}, @defaults, options
|
73
|
+
@options = []
|
74
|
+
@options_by_first_char = {}
|
75
|
+
@option_groups = []
|
76
|
+
selected = elm.selectedOptions
|
77
|
+
@select = build_element 'select', elm, @
|
78
|
+
[@selected_option, @dropdown] = @select.children
|
79
|
+
if elm.id
|
80
|
+
@select.id = "#{elm.id}-better-select"
|
81
|
+
@dropdown.id = "#{elm.id}-better-select-dropdown"
|
82
|
+
@default_selected = [elm.children[elm.selectedIndex]]
|
83
|
+
elm.parentNode.insertBefore @select, elm
|
84
|
+
elm.parentNode.insertBefore elm, @select
|
85
|
+
elm.style.display = 'none'
|
86
|
+
children = elm.children
|
87
|
+
if @settings.positionDropdown
|
88
|
+
document.body.appendChild @dropdown
|
89
|
+
@dropdown.style.left = '-9999px'
|
90
|
+
for child in children
|
91
|
+
switch child.tagName
|
92
|
+
when 'OPTION'
|
93
|
+
method = renderOption
|
94
|
+
when 'OPTGROUP'
|
95
|
+
method = renderOptionGroup
|
96
|
+
@dropdown.appendChild(method child, @)
|
97
|
+
@default_selected[0].better_version.select() if @default_selected
|
98
|
+
@selected_option.addEventListener 'click', =>
|
99
|
+
@toggle()
|
100
|
+
@set_selected @dropdown_selected_option
|
101
|
+
window.addEventListener 'click', (e) =>
|
102
|
+
unless e.target == @selected_option || e.target == @select || @options.indexOf(e.target) isnt -1
|
103
|
+
@toggle() if @open
|
104
|
+
last_char = false
|
105
|
+
@selected_option.addEventListener 'focus', =>
|
106
|
+
document.body.style.overflow = 'hidden'
|
107
|
+
addClass @select, 'focus'
|
108
|
+
@selected_option.addEventListener 'blur', =>
|
109
|
+
removeClass @select, 'focus'
|
110
|
+
document.body.style.overflow = 'auto'
|
111
|
+
@toggle() if @open is true
|
112
|
+
true
|
113
|
+
@selected_option.addEventListener 'keydown', (e) => e.preventDefault() unless [38, 40].indexOf(e.keyCode) is -1
|
114
|
+
@selected_option.addEventListener 'keyup', (e) =>
|
115
|
+
@toggle() if @open is false
|
116
|
+
keyCode = e.keyCode
|
117
|
+
switch keyCode
|
118
|
+
when 38 then @set_focused @options[if (@focus_index -= 1) < 0 then @focus_index = @options.length - 1 else @focus_index]
|
119
|
+
when 40 then @set_focused @options[if (@focus_index += 1) >= @options.length then @focus_index = 0 else @focus_index]
|
120
|
+
when 13
|
121
|
+
@focused_option.select()
|
122
|
+
else
|
123
|
+
if keyCode > 47 && keyCode < 58
|
124
|
+
char = numbers[keyCode - 47]
|
125
|
+
else if keyCode > 64 && keyCode < 91
|
126
|
+
char = letters[keyCode - 65]
|
127
|
+
else
|
128
|
+
if @focused_option
|
129
|
+
removeClass @focused_option, 'focus'
|
130
|
+
@focused_option = false
|
131
|
+
@focus_index = -1
|
132
|
+
@toggle()
|
133
|
+
if char && @options_by_first_char[char]
|
134
|
+
@options_by_first_char[char].sort() unless last_char is char
|
135
|
+
option = @options_by_first_char[char].shift()
|
136
|
+
@options_by_first_char[char].push option
|
137
|
+
@set_focused option
|
138
|
+
@focus_index = @options.indexOf option
|
139
|
+
last_character = char
|
140
|
+
@dropdown.addEventListener('click', -> console.log arguments)
|
141
|
+
e.preventDefault()
|
142
|
+
e.stopPropagation()
|
143
|
+
e.returnValue = false
|
144
|
+
false
|
145
|
+
focused_option: false
|
146
|
+
focus_index: -1
|
147
|
+
set_focused: (option) ->
|
148
|
+
class_for_selected = (option) => if @selected_option && option.innerHTML is @selected_option.innerHTML then " selected" else ""
|
149
|
+
@focused_option.setAttribute('class', "option#{class_for_selected(@focused_option)}") if @focused_option
|
150
|
+
@focused_option = option
|
151
|
+
@focused_option.setAttribute("class", "option focus#{class_for_selected(option)}")
|
152
|
+
@focus_index = @options.indexOf @focused_option
|
153
|
+
open: false
|
154
|
+
toggle: ->
|
155
|
+
(if @open = !@open then addClass else removeClass)(@select, 'open')
|
156
|
+
if @settings.positionDropdown
|
157
|
+
if @dropdown.offsetHeight > window.innerHeight
|
158
|
+
height = window.innerHeight * .50
|
159
|
+
@dropdown.style.height = height + 'px'
|
160
|
+
@dropdown.style['overflow-y'] = 'auto'
|
161
|
+
if @dropdown_selected_option.offsetTop > height || @adjust_height
|
162
|
+
@dropdown_selected_option.scrollIntoView()
|
163
|
+
@adjust_height = true
|
164
|
+
top = top || (getTop(@select) - @dropdown_selected_option.offsetTop)
|
165
|
+
top = getTop(@select) - (@dropdown_selected_option.offsetTop - @dropdown.scrollTop)
|
166
|
+
@dropdown.style.top = (if top < 0 then 0 else top) + 'px'
|
167
|
+
@dropdown.style.left = if @open then getLeft(@select) + 'px' else '-9999px'
|
168
|
+
reset: (option) -> @default_selected[0].better_version.select() if @default_selected
|
169
|
+
set_selected: (option) ->
|
170
|
+
unless @selected_option.innerHTML == option.innerHTML
|
171
|
+
removeClass(@dropdown_selected_option, 'selected') if @dropdown_selected_option
|
172
|
+
addClass @dropdown_selected_option = option, 'selected'
|
173
|
+
@selected_option.innerHTML = option.innerHTML
|
174
|
+
e = document.createEvent('Event')
|
175
|
+
e.initEvent 'change', true, true
|
176
|
+
@select.orig.dispatchEvent e
|
177
|
+
option_template: '<div class="option"><%= innerHTML %></div>'
|
178
|
+
option_group_template: '<div class="optgroup"><div class="option-group-label"><%= label %></div></div>'
|
179
|
+
select_template: '<div class="select"><a href="javascript:void(0)" class="selected-option"></a><div class="better-select-dropdown dropdown"></div></div>'
|
180
|
+
process_option: (option) -> option
|
181
|
+
process_option_group: (option_group) -> option_group
|
182
|
+
process_select: (select) -> select
|
183
|
+
|
184
|
+
|
185
|
+
window.BetterSelect = BetterSelect
|
@@ -0,0 +1,32 @@
|
|
1
|
+
.better-select-dropdown {
|
2
|
+
position: fixed;
|
3
|
+
|
4
|
+
.option {
|
5
|
+
cursor: pointer;
|
6
|
+
}
|
7
|
+
}
|
8
|
+
|
9
|
+
.select {
|
10
|
+
position: static;
|
11
|
+
|
12
|
+
&.dont-position-dropdown {
|
13
|
+
position: relative;
|
14
|
+
|
15
|
+
.dropdown {
|
16
|
+
top: 100%;
|
17
|
+
left: -99999px;
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
&.open {
|
22
|
+
&.dont-position-dropdown {
|
23
|
+
.dropdown {
|
24
|
+
left: 0px;
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
|
29
|
+
.selected-option {
|
30
|
+
cursor: pointer;
|
31
|
+
}
|
32
|
+
}
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: better_select
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -28,6 +28,8 @@ files:
|
|
28
28
|
- lib/better_select.rb
|
29
29
|
- lib/better_select/engine.rb
|
30
30
|
- lib/better_select/version.rb
|
31
|
+
- vendor/javascripts/better-select.js.coffee
|
32
|
+
- vendor/stylesheets/better-select.css.scss
|
31
33
|
homepage: http://www.benjaminbergstein.com
|
32
34
|
licenses: []
|
33
35
|
post_install_message:
|