Move autocomplete to Storm UI

Rename select AJAX from "source" to "handler" (Refs #2836)
Document select handler usage
Recompile assets
This commit is contained in:
Samuel Georges 2017-04-22 11:44:27 +10:00
parent d5965bdb04
commit 4644218312
13 changed files with 190 additions and 111 deletions

View File

@ -509,8 +509,6 @@ div.control-scrollpad > .scrollpad-scrollbar[data-visible]{opacity:0.7;filter:al
div.control-scrollpad > .scrollpad-scrollbar[data-hidden]{display:none}
div.control-scrollpad[data-direction=horizontal] > .scrollpad-scrollbar{top:auto;left:0;width:auto;height:11px}
div.control-scrollpad[data-direction=horizontal] > .scrollpad-scrollbar .drag-handle{right:auto;top:2px;height:7px;min-height:0;min-width:10px;width:auto}
.autocomplete.dropdown-menu{background:white}
.autocomplete.dropdown-menu li a{padding:3px 12px}
.svg-icon-container img.svg-icon{display:none}
.svg-icon-container.svg-active-effects img.svg-icon{-webkit-filter:grayscale(100%);filter:grayscale(100%);opacity:0.6;filter:alpha(opacity=60)}
.svg-icon-container.svg-active-effects:hover img.svg-icon,.svg-icon-container.svg-active-effects.active img.svg-icon{-webkit-filter:none;filter:none;opacity:1;filter:alpha(opacity=100)}

View File

@ -1071,107 +1071,7 @@ return result?result:this}
$.fn.treeListWidget.Constructor=TreeListWidget
$.fn.treeListWidget.noConflict=function(){$.fn.treeListWidget=old
return this}
$(document).render(function(){$('[data-control="treelist"]').treeListWidget();})}(window.jQuery);!function($){"use strict";var Autocomplete=function(element,options){this.$element=$(element)
this.options=$.extend({},$.fn.autocomplete.defaults,options)
this.matcher=this.options.matcher||this.matcher
this.sorter=this.options.sorter||this.sorter
this.highlighter=this.options.highlighter||this.highlighter
this.updater=this.options.updater||this.updater
this.source=this.options.source
this.$menu=$(this.options.menu)
this.shown=false
this.listen()}
Autocomplete.prototype={constructor:Autocomplete,select:function(){var val=this.$menu.find('.active').attr('data-value')
this.$element.val(this.updater(val)).change()
return this.hide()},updater:function(item){return item},show:function(){var offset=this.options.bodyContainer?this.$element.offset():this.$element.position(),pos=$.extend({},offset,{height:this.$element[0].offsetHeight}),cssOptions={top:pos.top+pos.height,left:pos.left}
if(this.options.matchWidth){cssOptions.width=this.$element[0].offsetWidth}
this.$menu.css(cssOptions)
if(this.options.bodyContainer){$(document.body).append(this.$menu)}
else{this.$menu.insertAfter(this.$element)}
this.$menu.show()
this.shown=true
return this},hide:function(){this.$menu.hide()
this.shown=false
return this},lookup:function(event){var items
this.query=this.$element.val()
if(!this.query||this.query.length<this.options.minLength){return this.shown?this.hide():this}
items=$.isFunction(this.source)?this.source(this.query,$.proxy(this.process,this)):this.source
return items?this.process(items):this},itemValue:function(item){if(typeof item==='object')
return item.value;return item;},itemLabel:function(item){if(typeof item==='object')
return item.label;return item;},itemsToArray:function(items){var newArray=[]
$.each(items,function(value,label){newArray.push({label:label,value:value})})
return newArray},process:function(items){var that=this
if(typeof items=='object')
items=this.itemsToArray(items)
items=$.grep(items,function(item){return that.matcher(item)})
items=this.sorter(items)
if(!items.length){return this.shown?this.hide():this}
return this.render(items.slice(0,this.options.items)).show()},matcher:function(item){return~this.itemValue(item).toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(items){var beginswith=[],caseSensitive=[],caseInsensitive=[],item,itemValue
while(item=items.shift()){itemValue=this.itemValue(item)
if(!itemValue.toLowerCase().indexOf(this.query.toLowerCase()))beginswith.push(item)
else if(~itemValue.indexOf(this.query))caseSensitive.push(item)
else caseInsensitive.push(item)}
return beginswith.concat(caseSensitive,caseInsensitive)},highlighter:function(item){var query=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,'\\$&')
return item.replace(new RegExp('('+query+')','ig'),function($1,match){return'<strong>'+match+'</strong>'})},render:function(items){var that=this
items=$(items).map(function(i,item){i=$(that.options.item).attr('data-value',that.itemValue(item))
i.find('a').html(that.highlighter(that.itemLabel(item)))
return i[0]})
items.first().addClass('active')
this.$menu.html(items)
return this},next:function(event){var active=this.$menu.find('.active').removeClass('active'),next=active.next()
if(!next.length){next=$(this.$menu.find('li')[0])}
next.addClass('active')},prev:function(event){var active=this.$menu.find('.active').removeClass('active'),prev=active.prev()
if(!prev.length){prev=this.$menu.find('li').last()}
prev.addClass('active')},listen:function(){this.$element.on('focus.autocomplete',$.proxy(this.focus,this)).on('blur.autocomplete',$.proxy(this.blur,this)).on('keypress.autocomplete',$.proxy(this.keypress,this)).on('keyup.autocomplete',$.proxy(this.keyup,this))
if(this.eventSupported('keydown')){this.$element.on('keydown.autocomplete',$.proxy(this.keydown,this))}
this.$menu.on('click.autocomplete',$.proxy(this.click,this)).on('mouseenter.autocomplete','li',$.proxy(this.mouseenter,this)).on('mouseleave.autocomplete','li',$.proxy(this.mouseleave,this))},eventSupported:function(eventName){var isSupported=eventName in this.$element
if(!isSupported){this.$element.setAttribute(eventName,'return;')
isSupported=typeof this.$element[eventName]==='function'}
return isSupported},move:function(e){if(!this.shown)return
switch(e.keyCode){case 9:case 13:case 27:e.preventDefault()
break
case 38:e.preventDefault()
this.prev()
break
case 40:e.preventDefault()
this.next()
break}
e.stopPropagation()},keydown:function(e){this.suppressKeyPressRepeat=~$.inArray(e.keyCode,[40,38,9,13,27])
this.move(e)},keypress:function(e){if(this.suppressKeyPressRepeat)return
this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break
case 9:case 13:if(!this.shown)return
this.select()
break
case 27:if(!this.shown)return
this.hide()
break
default:this.lookup()}
e.stopPropagation()
e.preventDefault()},focus:function(e){this.focused=true},blur:function(e){this.focused=false
if(!this.mousedover&&this.shown)this.hide()},click:function(e){e.stopPropagation()
e.preventDefault()
this.select()
this.$element.focus()},mouseenter:function(e){this.mousedover=true
this.$menu.find('.active').removeClass('active')
$(e.currentTarget).addClass('active')},mouseleave:function(e){this.mousedover=false
if(!this.focused&&this.shown)this.hide()},destroy:function(){this.hide()
this.$element.removeData('autocomplete')
this.$menu.remove()
this.$element.off('.autocomplete')
this.$menu.off('.autocomplete')
this.$element=null
this.$menu=null}}
var old=$.fn.autocomplete
$.fn.autocomplete=function(option){return this.each(function(){var $this=$(this),data=$this.data('autocomplete'),options=typeof option=='object'&&option
if(!data)$this.data('autocomplete',(data=new Autocomplete(this,options)))
if(typeof option=='string')data[option]()})}
$.fn.autocomplete.defaults={source:[],items:8,menu:'<ul class="autocomplete dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1,bodyContainer:false}
$.fn.autocomplete.Constructor=Autocomplete
$.fn.autocomplete.noConflict=function(){$.fn.autocomplete=old
return this}
$(document).on('focus.autocomplete.data-api','[data-control="autocomplete"]',function(e){var $this=$(this)
if($this.data('autocomplete'))return
$this.autocomplete($this.data())})}(window.jQuery);+function($){"use strict";var SidenavTree=function(element,options){this.options=options
$(document).render(function(){$('[data-control="treelist"]').treeListWidget();})}(window.jQuery);+function($){"use strict";var SidenavTree=function(element,options){this.options=options
this.$el=$(element)
this.init();}
SidenavTree.DEFAULTS={treeName:'sidenav_tree'}

View File

@ -29,7 +29,6 @@
=require october.sidepaneltab.js
=require october.simplelist.js
=require october.treelist.js
=require october.autocomplete.js
=require october.sidenav-tree.js
=require october.datetime.js

View File

@ -23,7 +23,6 @@
@import "controls/tree-path.less";
@import "controls/namevaluelist.less";
@import "controls/scrollpad.less";
@import "controls/autocomplete.less";
@import "controls/svg-icons.less";
//

View File

@ -0,0 +1,19 @@
# Autocomplete
### Autocomplete
Autocomplete control.
<input
class="form-control"
placeholder="Search for something or else"
data-control="autocomplete"
data-source="something: 'Something', else: 'Else'" />
## JavaScript API
```js
$('input').autocomplete({
source: { something: 'Something', else: 'Else' }
})
```

View File

@ -43,3 +43,31 @@ Add the `select-no-search` CSS class to disable searching.
<option value="2" selected="selected">Two</option>
</select>
</div>
## AJAX search
Use the `data-handler` attribute to source the select options from an AJAX handler.
```html
<select
class="form-control custom-select"
data-handler="onGetOptions"
data-minimum-input-length="2"
data-ajax--delay="300"
data-request-data="foo: 'bar'"
></select>
```
The AJAX handler should return results as an array.
```php
public function onGetOptions()
{
$results = [
'key' => 'value',
...
];
return ['result' => $results];
}
```

View File

@ -373,10 +373,29 @@
/* AUTOCOMPLETE DATA-API
* ================== */
function paramToObj(name, value) {
if (value === undefined) value = ''
if (typeof value == 'object') return value
try {
return JSON.parse(JSON.stringify(eval("({" + value + "})")))
}
catch (e) {
throw new Error('Error parsing the '+name+' attribute value. '+e)
}
}
$(document).on('focus.autocomplete.data-api', '[data-control="autocomplete"]', function (e) {
var $this = $(this)
if ($this.data('autocomplete')) return
$this.autocomplete($this.data())
var opts = $this.data()
if (opts.source) {
opts.source = paramToObj('data-source', opts.source)
}
$this.autocomplete(opts)
})
}(window.jQuery);

View File

@ -9,7 +9,7 @@
(function($){
/*
* Custom drop downs (Desktop only)
* Custom drop downs
*/
$(document).render(function(){
var formatSelectOption = function(state) {
@ -70,7 +70,10 @@
extraOptions.dropdownCssClass += ' select-hide-selected'
}
var source = $element.data('source');
/*
* October AJAX
*/
var source = $element.data('handler');
if (source) {
extraOptions.ajax = {
transport: function(params, success, failure) {

View File

@ -2632,7 +2632,113 @@ FlashMessage.DEFAULTS={class:'success',text:'Default text',interval:5}
if($.oc===undefined)
$.oc={}
$.oc.flashMsg=FlashMessage
$(document).render(function(){$('[data-control=flash-message]').each(function(){$.oc.flashMsg($(this).data(),this)})})}(window.jQuery);(function($){$(document).on('keydown','div.custom-checkbox',function(e){if(e.keyCode==32)
$(document).render(function(){$('[data-control=flash-message]').each(function(){$.oc.flashMsg($(this).data(),this)})})}(window.jQuery);!function($){"use strict";var Autocomplete=function(element,options){this.$element=$(element)
this.options=$.extend({},$.fn.autocomplete.defaults,options)
this.matcher=this.options.matcher||this.matcher
this.sorter=this.options.sorter||this.sorter
this.highlighter=this.options.highlighter||this.highlighter
this.updater=this.options.updater||this.updater
this.source=this.options.source
this.$menu=$(this.options.menu)
this.shown=false
this.listen()}
Autocomplete.prototype={constructor:Autocomplete,select:function(){var val=this.$menu.find('.active').attr('data-value')
this.$element.val(this.updater(val)).change()
return this.hide()},updater:function(item){return item},show:function(){var offset=this.options.bodyContainer?this.$element.offset():this.$element.position(),pos=$.extend({},offset,{height:this.$element[0].offsetHeight}),cssOptions={top:pos.top+pos.height,left:pos.left}
if(this.options.matchWidth){cssOptions.width=this.$element[0].offsetWidth}
this.$menu.css(cssOptions)
if(this.options.bodyContainer){$(document.body).append(this.$menu)}
else{this.$menu.insertAfter(this.$element)}
this.$menu.show()
this.shown=true
return this},hide:function(){this.$menu.hide()
this.shown=false
return this},lookup:function(event){var items
this.query=this.$element.val()
if(!this.query||this.query.length<this.options.minLength){return this.shown?this.hide():this}
items=$.isFunction(this.source)?this.source(this.query,$.proxy(this.process,this)):this.source
return items?this.process(items):this},itemValue:function(item){if(typeof item==='object')
return item.value;return item;},itemLabel:function(item){if(typeof item==='object')
return item.label;return item;},itemsToArray:function(items){var newArray=[]
$.each(items,function(value,label){newArray.push({label:label,value:value})})
return newArray},process:function(items){var that=this
if(typeof items=='object')
items=this.itemsToArray(items)
items=$.grep(items,function(item){return that.matcher(item)})
items=this.sorter(items)
if(!items.length){return this.shown?this.hide():this}
return this.render(items.slice(0,this.options.items)).show()},matcher:function(item){return~this.itemValue(item).toLowerCase().indexOf(this.query.toLowerCase())},sorter:function(items){var beginswith=[],caseSensitive=[],caseInsensitive=[],item,itemValue
while(item=items.shift()){itemValue=this.itemValue(item)
if(!itemValue.toLowerCase().indexOf(this.query.toLowerCase()))beginswith.push(item)
else if(~itemValue.indexOf(this.query))caseSensitive.push(item)
else caseInsensitive.push(item)}
return beginswith.concat(caseSensitive,caseInsensitive)},highlighter:function(item){var query=this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,'\\$&')
return item.replace(new RegExp('('+query+')','ig'),function($1,match){return'<strong>'+match+'</strong>'})},render:function(items){var that=this
items=$(items).map(function(i,item){i=$(that.options.item).attr('data-value',that.itemValue(item))
i.find('a').html(that.highlighter(that.itemLabel(item)))
return i[0]})
items.first().addClass('active')
this.$menu.html(items)
return this},next:function(event){var active=this.$menu.find('.active').removeClass('active'),next=active.next()
if(!next.length){next=$(this.$menu.find('li')[0])}
next.addClass('active')},prev:function(event){var active=this.$menu.find('.active').removeClass('active'),prev=active.prev()
if(!prev.length){prev=this.$menu.find('li').last()}
prev.addClass('active')},listen:function(){this.$element.on('focus.autocomplete',$.proxy(this.focus,this)).on('blur.autocomplete',$.proxy(this.blur,this)).on('keypress.autocomplete',$.proxy(this.keypress,this)).on('keyup.autocomplete',$.proxy(this.keyup,this))
if(this.eventSupported('keydown')){this.$element.on('keydown.autocomplete',$.proxy(this.keydown,this))}
this.$menu.on('click.autocomplete',$.proxy(this.click,this)).on('mouseenter.autocomplete','li',$.proxy(this.mouseenter,this)).on('mouseleave.autocomplete','li',$.proxy(this.mouseleave,this))},eventSupported:function(eventName){var isSupported=eventName in this.$element
if(!isSupported){this.$element.setAttribute(eventName,'return;')
isSupported=typeof this.$element[eventName]==='function'}
return isSupported},move:function(e){if(!this.shown)return
switch(e.keyCode){case 9:case 13:case 27:e.preventDefault()
break
case 38:e.preventDefault()
this.prev()
break
case 40:e.preventDefault()
this.next()
break}
e.stopPropagation()},keydown:function(e){this.suppressKeyPressRepeat=~$.inArray(e.keyCode,[40,38,9,13,27])
this.move(e)},keypress:function(e){if(this.suppressKeyPressRepeat)return
this.move(e)},keyup:function(e){switch(e.keyCode){case 40:case 38:case 16:case 17:case 18:break
case 9:case 13:if(!this.shown)return
this.select()
break
case 27:if(!this.shown)return
this.hide()
break
default:this.lookup()}
e.stopPropagation()
e.preventDefault()},focus:function(e){this.focused=true},blur:function(e){this.focused=false
if(!this.mousedover&&this.shown)this.hide()},click:function(e){e.stopPropagation()
e.preventDefault()
this.select()
this.$element.focus()},mouseenter:function(e){this.mousedover=true
this.$menu.find('.active').removeClass('active')
$(e.currentTarget).addClass('active')},mouseleave:function(e){this.mousedover=false
if(!this.focused&&this.shown)this.hide()},destroy:function(){this.hide()
this.$element.removeData('autocomplete')
this.$menu.remove()
this.$element.off('.autocomplete')
this.$menu.off('.autocomplete')
this.$element=null
this.$menu=null}}
var old=$.fn.autocomplete
$.fn.autocomplete=function(option){return this.each(function(){var $this=$(this),data=$this.data('autocomplete'),options=typeof option=='object'&&option
if(!data)$this.data('autocomplete',(data=new Autocomplete(this,options)))
if(typeof option=='string')data[option]()})}
$.fn.autocomplete.defaults={source:[],items:8,menu:'<ul class="autocomplete dropdown-menu"></ul>',item:'<li><a href="#"></a></li>',minLength:1,bodyContainer:false}
$.fn.autocomplete.Constructor=Autocomplete
$.fn.autocomplete.noConflict=function(){$.fn.autocomplete=old
return this}
function paramToObj(name,value){if(value===undefined)value=''
if(typeof value=='object')return value
try{return JSON.parse(JSON.stringify(eval("({"+value+"})")))}
catch(e){throw new Error('Error parsing the '+name+' attribute value. '+e)}}
$(document).on('focus.autocomplete.data-api','[data-control="autocomplete"]',function(e){var $this=$(this)
if($this.data('autocomplete'))return
var opts=$this.data()
if(opts.source){opts.source=paramToObj('data-source',opts.source)}
$this.autocomplete(opts)})}(window.jQuery);(function($){$(document).on('keydown','div.custom-checkbox',function(e){if(e.keyCode==32)
e.preventDefault()})
$(document).on('keyup','div.custom-checkbox',function(e){if(e.keyCode==32){var $cb=$('input',this)
if($cb.data('oc-space-timestamp')==e.timeStamp)
@ -3160,7 +3266,7 @@ this.timezone=$('meta[name="backend-timezone"]').attr('content')
this.appTimezone=$('meta[name="app-timezone"]').attr('content')
if(!this.appTimezone){this.appTimezone='UTC'}
if(!this.timezone){this.timezone='UTC'}}}(window.jQuery);(function($){$(document).render(function(){var formatSelectOption=function(state){if(!state.id)
return state.text;var $option=$(state.element),iconClass=$option.data('icon'),imageSrc=$option.data('image')
return state.text;var $option=$(state.element),iconClass=state.icon?state.icon:$option.data('icon'),imageSrc=state.image?state.image:$option.data('image')
if(iconClass)
return'<i class="select-icon '+iconClass+'"></i> '+state.text
if(imageSrc)
@ -3175,6 +3281,10 @@ if($element.hasClass('select-no-search')){extraOptions.minimumResultsForSearch=I
if($element.hasClass('select-no-dropdown')){extraOptions.dropdownCssClass+=' select-no-dropdown'
extraOptions.containerCssClass+=' select-no-dropdown'}
if($element.hasClass('select-hide-selected')){extraOptions.dropdownCssClass+=' select-hide-selected'}
var source=$element.data('handler');if(source){extraOptions.ajax={transport:function(params,success,failure){var $request=$element.request(source,{data:params.data})
$request.done(success)
$request.fail(failure)
return $request},dataType:'json'}}
var separators=$element.data('token-separators')
if(separators){extraOptions.tags=true
extraOptions.tokenSeparators=separators.split('|')

View File

@ -1434,6 +1434,8 @@ body.popover-open .control-popover > div:before,body.popover-open .control-popov
body.popover-open .control-popover div.popover-fixed-height{height:100%;min-height:100%}
body.popover-open .control-popover .popover-head:before{display:none}
}
.autocomplete.dropdown-menu{background:white}
.autocomplete.dropdown-menu li a{padding:3px 12px}
.control-breadcrumb{margin:-20px -20px 20px -20px;background-color:#d0d9dd}
.control-breadcrumb ul{padding:0;margin:0;font-size:0}
.control-breadcrumb li{font-size:14px;list-style:none;margin:0;padding:12px 10px 12px 30px;display:inline-block;position:relative;color:#ffffff;background-color:#9098a2}

View File

@ -29,6 +29,7 @@
=require js/foundation.event.js
=require js/foundation.controlutils.js
=require js/flashmessage.js
=require js/autocomplete.js
=require js/checkbox.js
=require js/checkbox.balloon.js
=require js/dropdown.js

View File

@ -10,6 +10,7 @@
@import "less/button.less";
@import "less/tooltip.less";
@import "less/popover.less";
@import "less/autocomplete.less";
@import "less/breadcrumb.less";
@import "less/progressbar.less";
@import "less/dropdown.less";