better_select 0.0.2 → 0.0.3
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.
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:
|