uber_select_rails 0.1.2 → 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/lib/uber_select_rails/version.rb +1 -1
- data/uber_select.gemspec +1 -1
- data/vendor/assets/javascript/uber_select/.gitignore +18 -0
- data/vendor/assets/javascript/uber_select/README.md +250 -0
- data/vendor/assets/javascript/uber_select/javascript/jquery.uber-select.js +177 -0
- data/vendor/assets/javascript/uber_select/javascript/list.js +120 -0
- data/vendor/assets/javascript/uber_select/javascript/output_container.js +21 -0
- data/vendor/assets/javascript/uber_select/javascript/pane.js +90 -0
- data/vendor/assets/javascript/uber_select/javascript/search.js +151 -0
- data/vendor/assets/javascript/uber_select/javascript/search_field.js +80 -0
- data/vendor/assets/javascript/uber_select/javascript/string_extensions.js +8 -0
- data/vendor/assets/javascript/uber_select/javascript/uber_search.js +315 -0
- data/vendor/assets/javascript/uber_select/test.css +150 -0
- data/vendor/assets/javascript/uber_select/test.html +184 -0
- metadata +15 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2c33585944dd942302f745e6ffdcff2406dd755568ead6ecb195df26fc8abfb2
|
4
|
+
data.tar.gz: f311a4e680d52a839a6fcb2ef3fe4ab28d16ff6f355c15af6383e6eb3d6c81db
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 462aed1760ab381a3ef0277301b6540c5127fe2c7eca293816288ac8674f44d372346bb05ad3a723ee0bbd7c2e7038d2ef01b286b26795bf7246b5978b505109
|
7
|
+
data.tar.gz: 6beab32309f9d49e26f572eff928002040d053c1ca9d1f49a740597af39c5a1f0da0fc07c5d2b9afcfcd306c1714f328266f99d0b08b03e77f1f858218430f03
|
data/uber_select.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.summary = %q{A Rails gem containing a javascript plugin that adds a layer of UI goodness overtop of basic HTML select elements}
|
13
13
|
spec.homepage = "https://github.com/culturecode/uber_select_rails"
|
14
14
|
|
15
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
15
|
+
spec.files = `git ls-files -z --recurse-submodules`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
16
|
spec.bindir = "exe"
|
17
17
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
18
|
spec.require_paths = ["lib"]
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
|
2
|
+
#
|
3
|
+
# If you find yourself ignoring temporary files generated by your text editor
|
4
|
+
# or operating system, you probably want to add a global ignore instead:
|
5
|
+
# git config --global core.excludesfile '~/.gitignore_global'
|
6
|
+
|
7
|
+
# Ignore bundler config.
|
8
|
+
/.bundle
|
9
|
+
|
10
|
+
# Ignore the default SQLite database.
|
11
|
+
/db/*.sqlite3
|
12
|
+
/db/*.sqlite3-journal
|
13
|
+
|
14
|
+
# Ignore all logfiles and tempfiles.
|
15
|
+
/log/*
|
16
|
+
!/log/.keep
|
17
|
+
/tmp
|
18
|
+
.DS_Store
|
@@ -0,0 +1,250 @@
|
|
1
|
+
# UberSelect
|
2
|
+
|
3
|
+
UberSelect is a fancy UI on top of a regular `<select>` element.
|
4
|
+
|
5
|
+
## Requirements
|
6
|
+
Tested on jQuery 1.11.x to 3.3.x
|
7
|
+
|
8
|
+
## Parts
|
9
|
+
UberSelect is composed of two main parts, an UberSearch that does all the of searching and UI, and an UberSelect which
|
10
|
+
is used to connect an UberSearch to a `<select>` element.
|
11
|
+
|
12
|
+
### UberSelect
|
13
|
+
This is the object that allows an UberSearch and a `<select>` element to work together. The select element can be used
|
14
|
+
to control the state of the UberSearch, and vice-versa. This means you can programmatically change the state of the
|
15
|
+
select, and UberSearch will update. Users can interact with the UberSearch, and the select will update. This also means
|
16
|
+
you can use UberSelect to gussy up forms, without changing any of the underlying inputs.
|
17
|
+
|
18
|
+
#### Usage
|
19
|
+
|
20
|
+
```JS
|
21
|
+
$('.my_selects').uberSelect(options);
|
22
|
+
```
|
23
|
+
|
24
|
+
#### Options
|
25
|
+
Options can be specified by setting the `data-uber-options` attribute on the `<select>` element. Values should be passed
|
26
|
+
as a JSON string. All options on the are passed to the underlying UberSearch, see [UberSearch options](#UberSearchOptions).
|
27
|
+
|
28
|
+
- ##### prepopulateSearchOnOpen
|
29
|
+
Determines whether the search input starts with the selected value in it when the pane is opened.
|
30
|
+
|
31
|
+
Default: `false`
|
32
|
+
|
33
|
+
- ##### clearSearchClearsSelect
|
34
|
+
Determines whether the select value should be cleared when the search is cleared.
|
35
|
+
|
36
|
+
Default: `false`
|
37
|
+
|
38
|
+
- ##### placeholder
|
39
|
+
Placeholder to show in the selected text area.
|
40
|
+
|
41
|
+
Default: `<select>` element attributes `<select placeholder="my placeholder" data-placeholder="my placeholder">`
|
42
|
+
|
43
|
+
- ##### dataUrl
|
44
|
+
A url to pre-fetch select options from. JSON response should be of the form
|
45
|
+
`[{text:'option with explicit value', value: 'some value'}, {text:'option with implicit value'}]`. For a custom JSON response, use in conjunction with optionFromDatum.
|
46
|
+
|
47
|
+
Default: `null`
|
48
|
+
|
49
|
+
- ##### optionFromDatum
|
50
|
+
A function that is used to customize the options value and text built from a JSON response. `datum` is a single result returned from the JSON response.
|
51
|
+
|
52
|
+
The function signature is as follows:
|
53
|
+
```js
|
54
|
+
function(datum) {
|
55
|
+
return // a <option> element to represent the select
|
56
|
+
}
|
57
|
+
```
|
58
|
+
|
59
|
+
Default: `datum.value` populates the `<option>` value, `datum.text` populates the `<option>` text.
|
60
|
+
|
61
|
+
- ##### value
|
62
|
+
Initialize the UberSearch with this selected value
|
63
|
+
|
64
|
+
Default: `<select>` element `value` property
|
65
|
+
|
66
|
+
#### Events Triggered
|
67
|
+
- ##### uber-select:ready
|
68
|
+
This fires when the UberSelect has initialized and is ready for user interaction
|
69
|
+
|
70
|
+
#### Events Observed
|
71
|
+
The `<select>` element observes the following events:
|
72
|
+
|
73
|
+
- ##### uber-select:refreshOptions
|
74
|
+
The UberSearch options list will be updated to match the `<select>` element's `<option>` list.
|
75
|
+
|
76
|
+
- ##### uber-select:refresh
|
77
|
+
The UberSearch will update its selected value to match the `<select>` element's. This handler also runs when the
|
78
|
+
`<select>` element triggers a `change` event.
|
79
|
+
|
80
|
+
### UberSearch
|
81
|
+
The UberSearch performs all of the heavy lifting. It creates the UI views, maintains state, and performs the searching.
|
82
|
+
It can be instantiated without the use of an UberSelect, which can be useful for situations where the selected value is
|
83
|
+
being used in purely in JS, and not being linked to a `<select>` element in a form.
|
84
|
+
|
85
|
+
#### Usage
|
86
|
+
|
87
|
+
```JS
|
88
|
+
new UberSearch(data, options);
|
89
|
+
```
|
90
|
+
|
91
|
+
#### Data
|
92
|
+
Data is an array of objects. Each object may have the following properties:
|
93
|
+
|
94
|
+
- ##### text
|
95
|
+
String shown to the user in the list of results. This value is required if *value* is not provided.
|
96
|
+
|
97
|
+
- ##### value
|
98
|
+
This is matched against the *value* option passed UberSearch and will appear selected if it matches. It is also used to match against search input text when the user searches. This value is required if *text* is not provided.
|
99
|
+
|
100
|
+
- ##### matchValue
|
101
|
+
This overrides the value used to match against search input text when the user searches. *optional*
|
102
|
+
|
103
|
+
- ##### visibility
|
104
|
+
This is used to determine whether the option appears only when searching or only when not searching. Values accepted: `query`, `no-query`. *optional*
|
105
|
+
|
106
|
+
- ##### group
|
107
|
+
This is used to visually group options. All options with the same group will appear together. *optional*
|
108
|
+
|
109
|
+
|
110
|
+
#### Options <a name="UberSearch options"></a>
|
111
|
+
Options can be specified by setting the `data-uber-options` attribute on the `<select>` element. Values should be passed
|
112
|
+
as a JSON string.
|
113
|
+
|
114
|
+
- ##### value
|
115
|
+
Sets the initially selected value of the UberSearch. This value should match the `value` property of the desired
|
116
|
+
option data.
|
117
|
+
|
118
|
+
Default: `null`
|
119
|
+
|
120
|
+
- ##### search
|
121
|
+
Determines whether the search input be shown.
|
122
|
+
|
123
|
+
Default: `true`
|
124
|
+
|
125
|
+
- ##### clearSearchButton
|
126
|
+
Sets the text content of clear search button.
|
127
|
+
|
128
|
+
Default: `✕`
|
129
|
+
|
130
|
+
- ##### selectCaret
|
131
|
+
Sets the text content of clear select caret.
|
132
|
+
|
133
|
+
Default: `⌄`
|
134
|
+
|
135
|
+
- ##### hideBlankOption
|
136
|
+
Sets whether blank options should be hidden automatically.
|
137
|
+
|
138
|
+
Default: `false`
|
139
|
+
|
140
|
+
- ##### treatBlankOptionAsPlaceholder
|
141
|
+
Determines whether the `text` property of an option with a blank `value` property should be used as the placeholder
|
142
|
+
text if no placeholder is specified.
|
143
|
+
|
144
|
+
Default: `false`
|
145
|
+
|
146
|
+
- ##### highlightByDefault
|
147
|
+
Determines whether the first search result be auto-highlighted.
|
148
|
+
|
149
|
+
Default: `true`
|
150
|
+
|
151
|
+
- ##### minQueryLength
|
152
|
+
Sets minimum number of characters the user must type before a search will be performed.
|
153
|
+
|
154
|
+
Default: `0`
|
155
|
+
|
156
|
+
- ##### minQueryMessage
|
157
|
+
Sets the message shown to the user when the query doesn't exceed the minimum length. `true` for a default message,
|
158
|
+
`false` for none, or provide a string to set a custom message.
|
159
|
+
|
160
|
+
Default: `true`
|
161
|
+
|
162
|
+
- ##### placeholder
|
163
|
+
Sets the placeholder shown in the selected text area.
|
164
|
+
|
165
|
+
Default: `null`
|
166
|
+
|
167
|
+
- ##### searchPlaceholder
|
168
|
+
Sets the placeholder shown in the search input.
|
169
|
+
|
170
|
+
Default: `'Type to search'`
|
171
|
+
|
172
|
+
- ##### noResultsText
|
173
|
+
Sets the message shown when there are no results.
|
174
|
+
|
175
|
+
Default: `'No Matches Found'`
|
176
|
+
|
177
|
+
- ##### noDataText
|
178
|
+
Sets the text to show when the results list is empty and no search is in progress
|
179
|
+
|
180
|
+
Default: `'No options'`
|
181
|
+
|
182
|
+
- ##### buildResult
|
183
|
+
A function that is used to build result elements.
|
184
|
+
|
185
|
+
The function signature is as follows:
|
186
|
+
```js
|
187
|
+
function(listOptionData) {
|
188
|
+
return // HTML/element to insert into the the results list
|
189
|
+
}
|
190
|
+
```
|
191
|
+
|
192
|
+
- ##### resultPostprocessor
|
193
|
+
A function that is run after a result is built and can be used to decorate it. This can be useful when extending the
|
194
|
+
functionality of an existing UberSearch implementation.
|
195
|
+
|
196
|
+
The function signature is as follows:
|
197
|
+
```js
|
198
|
+
function(resultsListElement, listOptionData) { }
|
199
|
+
```
|
200
|
+
|
201
|
+
Default: No-op
|
202
|
+
|
203
|
+
- ##### onRender
|
204
|
+
A function to run when the results container is rendered. If the result returns false, the default `select` event
|
205
|
+
handler is not run and the event is cancelled.
|
206
|
+
|
207
|
+
The function signature is as follows:
|
208
|
+
```js
|
209
|
+
function(resultsContainer, searchResultsHTML) { }
|
210
|
+
```
|
211
|
+
|
212
|
+
- ##### onSelect
|
213
|
+
A function to run when a result is selected. If the result returns false, the default `select` event handler is not
|
214
|
+
run and the event is cancelled.
|
215
|
+
|
216
|
+
The function signature is as follows:
|
217
|
+
```js
|
218
|
+
function(listOptionData, resultsListElement, clickEvent) { }
|
219
|
+
```
|
220
|
+
|
221
|
+
- ##### onNoHighlightSubmit
|
222
|
+
A function to run when a user presses enter without selecting a result.
|
223
|
+
Should be used in combination with `highlightByDefault: false`.
|
224
|
+
|
225
|
+
The function signature is as follows:
|
226
|
+
```js
|
227
|
+
function(value) { }
|
228
|
+
```
|
229
|
+
|
230
|
+
- ##### outputContainer (Deprecated)
|
231
|
+
An object that receives the output once a result is selected. Must respond to `setValue(value)` and `view()`. This object serves to
|
232
|
+
attach the result list to the DOM at the desired location.
|
233
|
+
|
234
|
+
#### Events Triggered
|
235
|
+
- ##### shown
|
236
|
+
This fires when the UberSearch pane is opened.
|
237
|
+
|
238
|
+
- ##### renderedResults
|
239
|
+
This fires each time the list of results is updated.
|
240
|
+
|
241
|
+
- ##### clear
|
242
|
+
This fires when the user clicks the clear search button.
|
243
|
+
|
244
|
+
- ##### select
|
245
|
+
This fires when the user selects a result.
|
246
|
+
|
247
|
+
The handler function signature is as follows:
|
248
|
+
```js
|
249
|
+
function(event, [listOptionData, resultsContainer, originalEvent]) { }
|
250
|
+
```
|
@@ -0,0 +1,177 @@
|
|
1
|
+
(function( $ ) {
|
2
|
+
var eventsTriggered = {
|
3
|
+
ready: 'uber-select:ready'
|
4
|
+
}
|
5
|
+
var eventsObserved = {
|
6
|
+
refreshOptions: 'uber-select:refreshOptions',
|
7
|
+
refresh: 'uber-select:refresh change',
|
8
|
+
}
|
9
|
+
|
10
|
+
$.fn.uberSelect = function(opts) {
|
11
|
+
this.each(function(){
|
12
|
+
if (this.uberSearch) { return } // Prevent multiple initializations on the same element
|
13
|
+
var select = this
|
14
|
+
var options = $.extend({
|
15
|
+
prepopulateSearchOnOpen: false, // Should the search input start with the selected value in it when the pane is opened?
|
16
|
+
clearSearchClearsSelect: false, // Should the select value be cleared When the search is cleared?
|
17
|
+
placeholder: $(select).attr('placeholder') || $(select).attr('data-placeholder'), // Placeholder to show in the selected text area
|
18
|
+
dataUrl: null, // A url to pre-fetch select options from, see optionsFromData for data format
|
19
|
+
optionFromDatum: optionFromDatum, // A function to create select options
|
20
|
+
value: $(select).val() // Initialize the UberSearch with this selected value
|
21
|
+
}, opts, $(select).data('uber-options'))
|
22
|
+
|
23
|
+
var uberSearch = this.uberSearch = new UberSearch(dataFromSelect(select), options)
|
24
|
+
|
25
|
+
|
26
|
+
// BEHAVIOUR
|
27
|
+
|
28
|
+
// When the UberSearch pane is opened
|
29
|
+
$(uberSearch).on('shown', function(){
|
30
|
+
if (options.prepopulateSearchOnOpen){
|
31
|
+
updateSearchValueFromSelect()
|
32
|
+
}
|
33
|
+
})
|
34
|
+
|
35
|
+
// When the clear search button is clicked
|
36
|
+
$(uberSearch).on('clear', function(){
|
37
|
+
if (options.clearSearchClearsSelect){
|
38
|
+
clearSelect()
|
39
|
+
}
|
40
|
+
})
|
41
|
+
|
42
|
+
// When the list values change
|
43
|
+
$(select).on(eventsObserved.refreshOptions, refreshOptionsList)
|
44
|
+
|
45
|
+
// When the select value changes
|
46
|
+
$(select).on(eventsObserved.refresh, updateSelectedValue)
|
47
|
+
|
48
|
+
// When a result is selected
|
49
|
+
$(uberSearch).on('select', function(_, datum){
|
50
|
+
updateSelectValue(datum.value)
|
51
|
+
})
|
52
|
+
|
53
|
+
// INITIALIZATION
|
54
|
+
|
55
|
+
uberSearch.view.insertBefore(select).append(select)
|
56
|
+
hideSelect()
|
57
|
+
if (options.dataUrl) {
|
58
|
+
$.getJSON(options.dataUrl).done(function(data){
|
59
|
+
$(select).append(optionsFromData(data))
|
60
|
+
uberSearch.setData(dataFromSelect(select))
|
61
|
+
$(select).trigger(eventsTriggered.ready)
|
62
|
+
})
|
63
|
+
} else {
|
64
|
+
$(select).trigger(eventsTriggered.ready)
|
65
|
+
}
|
66
|
+
|
67
|
+
|
68
|
+
// HELPER FUNCTIONS
|
69
|
+
|
70
|
+
// Given a select element
|
71
|
+
// Returns an array of data to match against
|
72
|
+
function dataFromSelect(select){
|
73
|
+
var opts = $(select).find('option')
|
74
|
+
var datum;
|
75
|
+
var parent;
|
76
|
+
|
77
|
+
return $.map(opts, function(option){
|
78
|
+
// This is optimized for performance and does not use jQuery convenience methods. Seems to be about 30% faster loading during non-scientific tests.
|
79
|
+
datum = {
|
80
|
+
text: option.textContent,
|
81
|
+
value: getAttribute(option, 'value'),
|
82
|
+
matchValue: getAttribute(option, 'data-match-value'),
|
83
|
+
visibility: getAttribute(option, 'data-visibility'),
|
84
|
+
element: option
|
85
|
+
}
|
86
|
+
|
87
|
+
parent = option.parentElement
|
88
|
+
if (parent.nodeName == 'OPTGROUP') {
|
89
|
+
datum.group = getAttribute(parent, 'label')
|
90
|
+
datum.visibility = datum.visibility || getAttribute(parent, 'data-visibility')
|
91
|
+
}
|
92
|
+
|
93
|
+
return datum
|
94
|
+
})
|
95
|
+
}
|
96
|
+
|
97
|
+
// Generates select options from data
|
98
|
+
function optionsFromData(data){
|
99
|
+
var elements = []
|
100
|
+
var groups = {}
|
101
|
+
$.each(data, function(_, datum){
|
102
|
+
if (datum.group) {
|
103
|
+
groups[datum.group] || elements.push(groups[datum.group] = groupFromDatum(datum))
|
104
|
+
groups[datum.group].append(options.optionFromDatum(datum))
|
105
|
+
} else {
|
106
|
+
elements.push(options.optionFromDatum(datum))
|
107
|
+
}
|
108
|
+
})
|
109
|
+
|
110
|
+
return elements
|
111
|
+
}
|
112
|
+
|
113
|
+
function getAttribute(element, attribute) {
|
114
|
+
var value = element.getAttribute(attribute)
|
115
|
+
return value === null ? undefined : value // Allow $.extend to overwrite missing attributes by setting them to undefined
|
116
|
+
}
|
117
|
+
|
118
|
+
function groupFromDatum(datum){
|
119
|
+
return $('<optgroup>').attr('label', datum.group)
|
120
|
+
}
|
121
|
+
|
122
|
+
function optionFromDatum(datum){
|
123
|
+
return $('<option>').attr('value', datum.value || datum.text).text(datum.text || datum.value)
|
124
|
+
}
|
125
|
+
|
126
|
+
// Copies the value of the select into the search input
|
127
|
+
function updateSearchValueFromSelect(){
|
128
|
+
uberSearch.searchField.input.val($(select).find('option:selected').text())
|
129
|
+
uberSearch.searchField.refresh()
|
130
|
+
}
|
131
|
+
|
132
|
+
function refreshOptionsList(){
|
133
|
+
uberSearch.setData(dataFromSelect(select))
|
134
|
+
updateSelectedValue()
|
135
|
+
}
|
136
|
+
|
137
|
+
// Updates the UberSearch's selected value from the select element's value
|
138
|
+
function updateSelectedValue(){
|
139
|
+
uberSearch.setValue($(select).val())
|
140
|
+
}
|
141
|
+
|
142
|
+
function updateSelectValue(value){
|
143
|
+
$(select).val(value).trigger('change')
|
144
|
+
}
|
145
|
+
|
146
|
+
// Selects the option with an emptystring value, or the first option if there is no blank option
|
147
|
+
function clearSelect(){
|
148
|
+
var selectValue = $(select).val()
|
149
|
+
|
150
|
+
// If the select is already cleared, avoid firing a change event
|
151
|
+
if (!selectValue) { return }
|
152
|
+
|
153
|
+
// Clear the value
|
154
|
+
$(select).val('').trigger('change')
|
155
|
+
|
156
|
+
// If that cleared it then we're done, otherwise, select the first option
|
157
|
+
if ($(select).find('option:selected').length){ return }
|
158
|
+
|
159
|
+
var firstOptionValue = $(select).find('option').prop('value')
|
160
|
+
|
161
|
+
// If the first option is already set then we're done, otherwise, select the first option
|
162
|
+
if (firstOptionValue == selectValue) { return }
|
163
|
+
|
164
|
+
// Select the first option
|
165
|
+
$(select).val(firstOptionValue).trigger('change')
|
166
|
+
}
|
167
|
+
|
168
|
+
// Hide the select, but keep its width to allow it to set the min width of the uber select
|
169
|
+
// NOTE: IE doesn't like 0 height, so give it 1px height and then offset
|
170
|
+
function hideSelect(){
|
171
|
+
$(select).wrap($('<div>').css({visibility: 'hidden', height: '1px', marginTop: '-1px', pointerEvents: 'none'}).addClass('select_width_spacer'))
|
172
|
+
}
|
173
|
+
})
|
174
|
+
|
175
|
+
return this
|
176
|
+
}
|
177
|
+
}( jQuery ));
|