diff --git a/modules/backend/assets/css/october.css b/modules/backend/assets/css/october.css index 956c138f0..5f4fdedd1 100644 --- a/modules/backend/assets/css/october.css +++ b/modules/backend/assets/css/october.css @@ -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)} diff --git a/modules/backend/assets/js/october-min.js b/modules/backend/assets/js/october-min.js index 071d5884c..9df076e65 100644 --- a/modules/backend/assets/js/october-min.js +++ b/modules/backend/assets/js/october-min.js @@ -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'+match+''})},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:'',item:'
  • ',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'} diff --git a/modules/backend/assets/js/october.js b/modules/backend/assets/js/october.js index e964042e3..ebf91faa6 100644 --- a/modules/backend/assets/js/october.js +++ b/modules/backend/assets/js/october.js @@ -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 diff --git a/modules/backend/assets/less/october.less b/modules/backend/assets/less/october.less index 753e2e6d1..0b2e2971f 100644 --- a/modules/backend/assets/less/october.less +++ b/modules/backend/assets/less/october.less @@ -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"; // diff --git a/modules/system/assets/ui/docs/autocomplete.md b/modules/system/assets/ui/docs/autocomplete.md new file mode 100644 index 000000000..d9fe73afe --- /dev/null +++ b/modules/system/assets/ui/docs/autocomplete.md @@ -0,0 +1,19 @@ +# Autocomplete + +### Autocomplete + +Autocomplete control. + + + +## JavaScript API + +```js +$('input').autocomplete({ + source: { something: 'Something', else: 'Else' } +}) +``` diff --git a/modules/system/assets/ui/docs/select.md b/modules/system/assets/ui/docs/select.md index d462a1e3b..4e475d9ec 100644 --- a/modules/system/assets/ui/docs/select.md +++ b/modules/system/assets/ui/docs/select.md @@ -43,3 +43,31 @@ Add the `select-no-search` CSS class to disable searching. + +## AJAX search + +Use the `data-handler` attribute to source the select options from an AJAX handler. + +```html + +``` + +The AJAX handler should return results as an array. + +```php +public function onGetOptions() +{ + $results = [ + 'key' => 'value', + ... + ]; + + return ['result' => $results]; +} +``` diff --git a/modules/backend/assets/js/october.autocomplete.js b/modules/system/assets/ui/js/autocomplete.js similarity index 95% rename from modules/backend/assets/js/october.autocomplete.js rename to modules/system/assets/ui/js/autocomplete.js index 3b65797fa..59d2b9689 100644 --- a/modules/backend/assets/js/october.autocomplete.js +++ b/modules/system/assets/ui/js/autocomplete.js @@ -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); diff --git a/modules/system/assets/ui/js/select.js b/modules/system/assets/ui/js/select.js index 8b29a4ad8..71b790a4b 100644 --- a/modules/system/assets/ui/js/select.js +++ b/modules/system/assets/ui/js/select.js @@ -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) { diff --git a/modules/backend/assets/less/controls/autocomplete.less b/modules/system/assets/ui/less/autocomplete.less similarity index 98% rename from modules/backend/assets/less/controls/autocomplete.less rename to modules/system/assets/ui/less/autocomplete.less index 26ed4d335..5bb6a0fd1 100644 --- a/modules/backend/assets/less/controls/autocomplete.less +++ b/modules/system/assets/ui/less/autocomplete.less @@ -4,4 +4,4 @@ li a { padding: 3px 12px; } -} \ No newline at end of file +} diff --git a/modules/system/assets/ui/storm-min.js b/modules/system/assets/ui/storm-min.js index 800d00ce6..fd97be051 100644 --- a/modules/system/assets/ui/storm-min.js +++ b/modules/system/assets/ui/storm-min.js @@ -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'+match+''})},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:'',item:'
  • ',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' '+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('|') diff --git a/modules/system/assets/ui/storm.css b/modules/system/assets/ui/storm.css index 4f7bc42a7..b9c1a8b2b 100644 --- a/modules/system/assets/ui/storm.css +++ b/modules/system/assets/ui/storm.css @@ -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} diff --git a/modules/system/assets/ui/storm.js b/modules/system/assets/ui/storm.js index ecac6c205..0f477dafb 100644 --- a/modules/system/assets/ui/storm.js +++ b/modules/system/assets/ui/storm.js @@ -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 diff --git a/modules/system/assets/ui/storm.less b/modules/system/assets/ui/storm.less index fa0d23ea1..490f0e431 100644 --- a/modules/system/assets/ui/storm.less +++ b/modules/system/assets/ui/storm.less @@ -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";