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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 877c716db7b58ef210bf6c74e61a3eb3bbeb487c
4
- data.tar.gz: 723d3273bedb197b867abcddefa2965468dd030a
2
+ SHA256:
3
+ metadata.gz: 2c33585944dd942302f745e6ffdcff2406dd755568ead6ecb195df26fc8abfb2
4
+ data.tar.gz: f311a4e680d52a839a6fcb2ef3fe4ab28d16ff6f355c15af6383e6eb3d6c81db
5
5
  SHA512:
6
- metadata.gz: 4e64e8e34bee05dd83f5c0334078e7930757d1c31f00ca4321f88740ebefcb246e9e84c55eb6b95d9b4d9df6534b2322c2ac9067fda376ccc0b711dd4cc25642
7
- data.tar.gz: f2c06a3bddb62d8fe4f98c0ca43f8422c79b88db92c7a9b9fff54d77e2bcea35b04cb5cfd16d074872cbe32a4aac629352569edbf7b1f6c1805be09d4aae6d9c
6
+ metadata.gz: 462aed1760ab381a3ef0277301b6540c5127fe2c7eca293816288ac8674f44d372346bb05ad3a723ee0bbd7c2e7038d2ef01b286b26795bf7246b5978b505109
7
+ data.tar.gz: 6beab32309f9d49e26f572eff928002040d053c1ca9d1f49a740597af39c5a1f0da0fc07c5d2b9afcfcd306c1714f328266f99d0b08b03e77f1f858218430f03
@@ -1,3 +1,3 @@
1
1
  module UberSelectRails
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.8"
3
3
  end
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 ));