diff --git a/js/src/bootstrap5/widget.js b/js/src/bootstrap5/widget.js
index 7017914..69f8858 100644
--- a/js/src/bootstrap5/widget.js
+++ b/js/src/bootstrap5/widget.js
@@ -72,6 +72,50 @@ export class AutocompleteWidget extends BaseAutocomplete {
this.ul_elem = $('
')
.addClass('list-group list-group-flush')
.appendTo(this.dd_elem);
+ this.no_results = $('')
+ .hide()
+ .addClass('no-results px-3 py-2')
+ .text('No results found.')
+ .appendTo(this.dd_elem);
+ }
+
+ autocomplete() {
+ this.no_results.hide();
+ let val = this.input_elem.val();
+ this.source({term: val}, (data) => {
+ if (!Array.isArray(data)) {
+ throw 'yafowil.widget.autocomplete: invalid datatype, data must ' +
+ 'be array of strings or {key: value} objects'
+ }
+ if (!data.length) {
+ this.no_results.show();
+ } else {
+ for (let item of data) {
+ this.suggestions.push(new this.Suggestion(this, item, val));
+ }
+ }
+ let scrolltop = $(document).scrollTop(),
+ input_top = this.elem.offset().top,
+ input_left = this.elem.offset().left,
+ input_height = this.elem.outerHeight(),
+ dd_height = this.dd_elem.outerHeight(),
+ top;
+
+ let viewport_edge = scrolltop + $(window).outerHeight();
+ let dd_bottom = input_top + input_height + dd_height;
+
+ if (dd_bottom >= viewport_edge) {
+ top = input_top - dd_height;
+ } else {
+ top = input_top + input_height;
+ }
+
+ this.dd_elem.css({
+ top: `${top}px`,
+ left: `${input_left}px`
+ });
+ this.dd_elem.show();
+ });
}
/**
diff --git a/scss/bootstrap5/widget.scss b/scss/bootstrap5/widget.scss
index ac95bbe..6e722cd 100644
--- a/scss/bootstrap5/widget.scss
+++ b/scss/bootstrap5/widget.scss
@@ -9,6 +9,12 @@ div.autocomplete-dropdown {
max-height: 320px;
overflow: hidden;
overflow-y: auto;
+
+ .no-results {
+ background-color: var(--bs-secondary-bg);
+ color: var(--bs-secondary-color);
+ font-size: 14px;
+ }
}
.autocomplete-suggestion {
diff --git a/src/yafowil/widget/autocomplete/resources/bootstrap5/widget.js b/src/yafowil/widget/autocomplete/resources/bootstrap5/widget.js
index 0cca5c9..19a55c8 100644
--- a/src/yafowil/widget/autocomplete/resources/bootstrap5/widget.js
+++ b/src/yafowil/widget/autocomplete/resources/bootstrap5/widget.js
@@ -374,6 +374,46 @@ var yafowil_autocomplete = (function (exports, $) {
this.ul_elem = $('')
.addClass('list-group list-group-flush')
.appendTo(this.dd_elem);
+ this.no_results = $('')
+ .hide()
+ .addClass('no-results px-3 py-2')
+ .text('No results found.')
+ .appendTo(this.dd_elem);
+ }
+ autocomplete() {
+ this.no_results.hide();
+ let val = this.input_elem.val();
+ this.source({term: val}, (data) => {
+ if (!Array.isArray(data)) {
+ throw 'yafowil.widget.autocomplete: invalid datatype, data must ' +
+ 'be array of strings or {key: value} objects'
+ }
+ if (!data.length) {
+ this.no_results.show();
+ } else {
+ for (let item of data) {
+ this.suggestions.push(new this.Suggestion(this, item, val));
+ }
+ }
+ let scrolltop = $(document).scrollTop(),
+ input_top = this.elem.offset().top,
+ input_left = this.elem.offset().left,
+ input_height = this.elem.outerHeight(),
+ dd_height = this.dd_elem.outerHeight(),
+ top;
+ let viewport_edge = scrolltop + $(window).outerHeight();
+ let dd_bottom = input_top + input_height + dd_height;
+ if (dd_bottom >= viewport_edge) {
+ top = input_top - dd_height;
+ } else {
+ top = input_top + input_height;
+ }
+ this.dd_elem.css({
+ top: `${top}px`,
+ left: `${input_left}px`
+ });
+ this.dd_elem.show();
+ });
}
on_input(e) {
clearTimeout(this.timeout);
diff --git a/src/yafowil/widget/autocomplete/resources/bootstrap5/widget.min.css b/src/yafowil/widget/autocomplete/resources/bootstrap5/widget.min.css
index c431e1e..07efdcf 100644
--- a/src/yafowil/widget/autocomplete/resources/bootstrap5/widget.min.css
+++ b/src/yafowil/widget/autocomplete/resources/bootstrap5/widget.min.css
@@ -1 +1 @@
-div.yafowil-widget-autocomplete .hiddenStructure{display:none}div.autocomplete-dropdown{max-height:320px;min-width:200px;overflow:hidden;overflow-y:auto;position:absolute;z-index:2000}.autocomplete-suggestion{padding-bottom:7px;padding-top:7px}.autocomplete-suggestion.selected{background:var(--yafowil-accent-color,#0d6efd);color:var(--yafowil-accent-font-color,#fff)}
\ No newline at end of file
+div.yafowil-widget-autocomplete .hiddenStructure{display:none}div.autocomplete-dropdown{max-height:320px;min-width:200px;overflow:hidden;overflow-y:auto;position:absolute;z-index:2000}div.autocomplete-dropdown .no-results{background-color:var(--bs-secondary-bg);color:var(--bs-secondary-color);font-size:14px}.autocomplete-suggestion{padding-bottom:7px;padding-top:7px}.autocomplete-suggestion.selected{background:var(--yafowil-accent-color,#0d6efd);color:var(--yafowil-accent-font-color,#fff)}
\ No newline at end of file
diff --git a/src/yafowil/widget/autocomplete/resources/bootstrap5/widget.min.js b/src/yafowil/widget/autocomplete/resources/bootstrap5/widget.min.js
index 7ade47f..5b262ce 100644
--- a/src/yafowil/widget/autocomplete/resources/bootstrap5/widget.min.js
+++ b/src/yafowil/widget/autocomplete/resources/bootstrap5/widget.min.js
@@ -1 +1 @@
-var yafowil_autocomplete=function(e,t){"use strict";class s{constructor(e,s,i){if(this.widget=e,t.isPlainObject(s))this.key=Object.keys(s)[0],this.value=Object.values(s)[0];else{if("string"!=typeof s)throw"yafowil.widget.autocomplete: Invalid Suggestion type. Suggestionmust be string or {key: value} object.";this.key=null,this.value=s}this.val=i,this.compile(),this.selected=!1,this.select=this.select.bind(this),this.elem.off("mousedown",this.select).on("mousedown",this.select)}compile(){let e=this.value.toUpperCase().indexOf(this.val.toUpperCase());this.elem=t("").addClass("autocomplete-suggestion").appendTo(this.widget.dd_elem),t("").text(this.value.substring(0,e)).appendTo(this.elem),t("").text(this.value.substring(e,e+this.val.length)).appendTo(this.elem),t("").text(this.value.substring(e+this.val.length)).appendTo(this.elem)}get selected(){return this._selected}set selected(e){e?(this._selected=!0,this.elem.addClass("selected")):(this._selected=!1,this.elem.removeClass("selected"))}select(){this.selected=!0,this.widget.select_suggestion(this.key,this.value)}}class i{static initialize(e){t("div.yafowil-widget-autocomplete",e).each((function(){let e=t(this);void 0!==window.yafowil_array&&window.yafowil_array.inside_template(e)||new i(e)}))}constructor(e){e.data("yafowil-autocomplete",this),this.elem=e,this.result_key_elem=t("input.autocomplete-result-key",e),this.Suggestion=s,this.compile(),this.suggestions=[],this.current_focus=0;let i=this.parse_options();this.sourcetype=i.type,this.delay=i.delay,this.min_length=i.minLength,this.parse_source(),this.on_input=this.on_input.bind(this),this.hide_dropdown=this.hide_dropdown.bind(this),this.on_keydown=this.on_keydown.bind(this),this.input_elem.on("focusout",this.hide_dropdown).on("focus input",this.on_input).on("keydown",this.on_keydown),this.autocomplete=this.autocomplete.bind(this)}compile(){this.input_elem=t("input.autocomplete",this.elem).attr("spellcheck",!1).attr("autocomplete","off"),this.dd_elem=t("").addClass("autocomplete-dropdown").appendTo("body")}unload(){clearTimeout(this.timeout),this.input_elem.off("focusout",this.hide_dropdown).off("focus input",this.on_input).off("keydown",this.on_keydown)}parse_options(){let e=t(".autocomplete-params",this.elem).text().split("|"),s=[];for(let t=0;t-1&&l.push(e);s(l)}}else"remote"===this.sourcetype&&(this.source=function(s,i){t.ajax({url:e,data:{term:s.term},dataType:"json",success:function(e){i(e)},error:function(){throw i([]),new Error("Cannot locate JSON at: "+e)}})})}on_input(e){clearTimeout(this.timeout),this.dd_elem.empty().hide(),this.suggestions=[],this.current_focus=-1,this.input_elem.val().length>=this.min_length&&(this.timeout=setTimeout(this.autocomplete,this.delay))}autocomplete(){let e=this.input_elem.val();this.source({term:e},(s=>{if(!Array.isArray(s))throw"yafowil.widget.autocomplete: invalid datatype, data must be array of strings or {key: value} objects";if(!s.length)return;for(let t of s)this.suggestions.push(new this.Suggestion(this,t,e));let i,o=t(document).scrollTop(),l=this.elem.offset().top,n=this.elem.offset().left,h=this.elem.outerHeight(),a=this.dd_elem.outerHeight();i=l+h+a>=o+t(window).outerHeight()?l-a:l+h,this.dd_elem.css({top:`${i}px`,left:`${n}px`}),this.dd_elem.show()}))}on_keydown(e){let t=this.dd_elem.scrollTop();switch(e.key){case"ArrowDown":this.current_focus++,this.add_active(!0);break;case"ArrowUp":this.current_focus--,this.add_active(!1);break;case"Enter":if(e.preventDefault(),this.current_focus>-1){let e=this.suggestions[this.current_focus];e.select(),this.input_elem.val(e.value),this.hide_dropdown(),this.input_elem.trigger("blur")}break;case"Escape":this.hide_dropdown(),this.input_elem.trigger("blur");break;case"Tab":if(this.hide_dropdown(),this.current_focus>-1){let e=this.suggestions[this.current_focus];this.input_elem.val(e.value),this.hide_dropdown(),this.input_elem.trigger("blur")}break;case"PageDown":if(e.preventDefault(),this.dd_elem.scrollTop(t+this.dd_elem.height()),this.current_focus>-1){let e=0;for(let t in this.suggestions){this.suggestions[t].elem.offset().top-1){let e=0;for(let t in this.suggestions){this.suggestions[t].elem.offset().top=this.suggestions.length?this.current_focus=0:this.current_focus<0&&(this.current_focus=this.suggestions.length-1);let t=this.suggestions[this.current_focus];t.selected=!0;let s=this.dd_elem.scrollTop(),i=t.elem.offset().top,o=t.elem.outerHeight(),l=this.dd_elem.offset().top,n=this.dd_elem.outerHeight();e?0===this.current_focus?this.dd_elem.scrollTop(0):i+o>l+n&&this.dd_elem.scrollTop(s+o):this.current_focus>=this.suggestions.length-1?this.dd_elem.scrollTop(o*this.suggestions.length):i").addClass("autocomplete-suggestion list-group-item").appendTo(this.widget.ul_elem),t("").text(this.value.substring(0,e)).appendTo(this.elem),t("").text(this.value.substring(e,e+this.val.length)).appendTo(this.elem),t("").text(this.value.substring(e+this.val.length)).appendTo(this.elem)}}class l extends i{static initialize(e){t("div.yafowil-widget-autocomplete",e).each((function(){let e=t(this);void 0!==window.yafowil_array&&window.yafowil_array.inside_template(e)||new l(e)}))}constructor(e){super(e),this.Suggestion=o}compile(){this.input_elem=t("input.autocomplete",this.elem).attr("spellcheck",!1).attr("autocomplete","off"),this.dd_elem=t("").addClass("autocomplete-dropdown card shadow").appendTo("body"),this.ul_elem=t("").addClass("list-group list-group-flush").appendTo(this.dd_elem)}on_input(e){clearTimeout(this.timeout),this.ul_elem.empty(),this.dd_elem.hide(),this.suggestions=[],this.current_focus=-1,this.input_elem.val().length>=this.min_length&&(this.timeout=setTimeout(this.autocomplete,this.delay))}}function n(e,t){l.initialize(t)}function h(){void 0!==window.yafowil_array?window.yafowil_array.on_array_event("on_add",n):void 0!==yafowil.array&&t.extend(yafowil.array.hooks.add,{autocomplete_binder:l.initialize})}return t((function(){void 0!==window.ts?ts.ajax.register(l.initialize,!0):void 0!==window.bdajax?bdajax.register(l.initialize,!0):l.initialize(),h()})),e.AutocompleteSuggestion=o,e.AutocompleteWidget=l,e.register_array_subscribers=h,Object.defineProperty(e,"__esModule",{value:!0}),window.yafowil=window.yafowil||{},window.yafowil.autocomplete=e,e}({},jQuery);
+var yafowil_autocomplete=function(e,t){"use strict";class s{constructor(e,s,i){if(this.widget=e,t.isPlainObject(s))this.key=Object.keys(s)[0],this.value=Object.values(s)[0];else{if("string"!=typeof s)throw"yafowil.widget.autocomplete: Invalid Suggestion type. Suggestionmust be string or {key: value} object.";this.key=null,this.value=s}this.val=i,this.compile(),this.selected=!1,this.select=this.select.bind(this),this.elem.off("mousedown",this.select).on("mousedown",this.select)}compile(){let e=this.value.toUpperCase().indexOf(this.val.toUpperCase());this.elem=t("").addClass("autocomplete-suggestion").appendTo(this.widget.dd_elem),t("").text(this.value.substring(0,e)).appendTo(this.elem),t("").text(this.value.substring(e,e+this.val.length)).appendTo(this.elem),t("").text(this.value.substring(e+this.val.length)).appendTo(this.elem)}get selected(){return this._selected}set selected(e){e?(this._selected=!0,this.elem.addClass("selected")):(this._selected=!1,this.elem.removeClass("selected"))}select(){this.selected=!0,this.widget.select_suggestion(this.key,this.value)}}class i{static initialize(e){t("div.yafowil-widget-autocomplete",e).each((function(){let e=t(this);void 0!==window.yafowil_array&&window.yafowil_array.inside_template(e)||new i(e)}))}constructor(e){e.data("yafowil-autocomplete",this),this.elem=e,this.result_key_elem=t("input.autocomplete-result-key",e),this.Suggestion=s,this.compile(),this.suggestions=[],this.current_focus=0;let i=this.parse_options();this.sourcetype=i.type,this.delay=i.delay,this.min_length=i.minLength,this.parse_source(),this.on_input=this.on_input.bind(this),this.hide_dropdown=this.hide_dropdown.bind(this),this.on_keydown=this.on_keydown.bind(this),this.input_elem.on("focusout",this.hide_dropdown).on("focus input",this.on_input).on("keydown",this.on_keydown),this.autocomplete=this.autocomplete.bind(this)}compile(){this.input_elem=t("input.autocomplete",this.elem).attr("spellcheck",!1).attr("autocomplete","off"),this.dd_elem=t("").addClass("autocomplete-dropdown").appendTo("body")}unload(){clearTimeout(this.timeout),this.input_elem.off("focusout",this.hide_dropdown).off("focus input",this.on_input).off("keydown",this.on_keydown)}parse_options(){let e=t(".autocomplete-params",this.elem).text().split("|"),s=[];for(let t=0;t-1&&l.push(e);s(l)}}else"remote"===this.sourcetype&&(this.source=function(s,i){t.ajax({url:e,data:{term:s.term},dataType:"json",success:function(e){i(e)},error:function(){throw i([]),new Error("Cannot locate JSON at: "+e)}})})}on_input(e){clearTimeout(this.timeout),this.dd_elem.empty().hide(),this.suggestions=[],this.current_focus=-1,this.input_elem.val().length>=this.min_length&&(this.timeout=setTimeout(this.autocomplete,this.delay))}autocomplete(){let e=this.input_elem.val();this.source({term:e},(s=>{if(!Array.isArray(s))throw"yafowil.widget.autocomplete: invalid datatype, data must be array of strings or {key: value} objects";if(!s.length)return;for(let t of s)this.suggestions.push(new this.Suggestion(this,t,e));let i,o=t(document).scrollTop(),l=this.elem.offset().top,n=this.elem.offset().left,h=this.elem.outerHeight(),r=this.dd_elem.outerHeight();i=l+h+r>=o+t(window).outerHeight()?l-r:l+h,this.dd_elem.css({top:`${i}px`,left:`${n}px`}),this.dd_elem.show()}))}on_keydown(e){let t=this.dd_elem.scrollTop();switch(e.key){case"ArrowDown":this.current_focus++,this.add_active(!0);break;case"ArrowUp":this.current_focus--,this.add_active(!1);break;case"Enter":if(e.preventDefault(),this.current_focus>-1){let e=this.suggestions[this.current_focus];e.select(),this.input_elem.val(e.value),this.hide_dropdown(),this.input_elem.trigger("blur")}break;case"Escape":this.hide_dropdown(),this.input_elem.trigger("blur");break;case"Tab":if(this.hide_dropdown(),this.current_focus>-1){let e=this.suggestions[this.current_focus];this.input_elem.val(e.value),this.hide_dropdown(),this.input_elem.trigger("blur")}break;case"PageDown":if(e.preventDefault(),this.dd_elem.scrollTop(t+this.dd_elem.height()),this.current_focus>-1){let e=0;for(let t in this.suggestions){this.suggestions[t].elem.offset().top-1){let e=0;for(let t in this.suggestions){this.suggestions[t].elem.offset().top=this.suggestions.length?this.current_focus=0:this.current_focus<0&&(this.current_focus=this.suggestions.length-1);let t=this.suggestions[this.current_focus];t.selected=!0;let s=this.dd_elem.scrollTop(),i=t.elem.offset().top,o=t.elem.outerHeight(),l=this.dd_elem.offset().top,n=this.dd_elem.outerHeight();e?0===this.current_focus?this.dd_elem.scrollTop(0):i+o>l+n&&this.dd_elem.scrollTop(s+o):this.current_focus>=this.suggestions.length-1?this.dd_elem.scrollTop(o*this.suggestions.length):i").addClass("autocomplete-suggestion list-group-item").appendTo(this.widget.ul_elem),t("").text(this.value.substring(0,e)).appendTo(this.elem),t("").text(this.value.substring(e,e+this.val.length)).appendTo(this.elem),t("").text(this.value.substring(e+this.val.length)).appendTo(this.elem)}}class l extends i{static initialize(e){t("div.yafowil-widget-autocomplete",e).each((function(){let e=t(this);void 0!==window.yafowil_array&&window.yafowil_array.inside_template(e)||new l(e)}))}constructor(e){super(e),this.Suggestion=o}compile(){this.input_elem=t("input.autocomplete",this.elem).attr("spellcheck",!1).attr("autocomplete","off"),this.dd_elem=t("").addClass("autocomplete-dropdown card shadow").appendTo("body"),this.ul_elem=t("").addClass("list-group list-group-flush").appendTo(this.dd_elem),this.no_results=t("").hide().addClass("no-results px-3 py-2").text("No results found.").appendTo(this.dd_elem)}autocomplete(){this.no_results.hide();let e=this.input_elem.val();this.source({term:e},(s=>{if(!Array.isArray(s))throw"yafowil.widget.autocomplete: invalid datatype, data must be array of strings or {key: value} objects";if(s.length)for(let t of s)this.suggestions.push(new this.Suggestion(this,t,e));else this.no_results.show();let i,o=t(document).scrollTop(),l=this.elem.offset().top,n=this.elem.offset().left,h=this.elem.outerHeight(),r=this.dd_elem.outerHeight();i=l+h+r>=o+t(window).outerHeight()?l-r:l+h,this.dd_elem.css({top:`${i}px`,left:`${n}px`}),this.dd_elem.show()}))}on_input(e){clearTimeout(this.timeout),this.ul_elem.empty(),this.dd_elem.hide(),this.suggestions=[],this.current_focus=-1,this.input_elem.val().length>=this.min_length&&(this.timeout=setTimeout(this.autocomplete,this.delay))}}function n(e,t){l.initialize(t)}function h(){void 0!==window.yafowil_array?window.yafowil_array.on_array_event("on_add",n):void 0!==yafowil.array&&t.extend(yafowil.array.hooks.add,{autocomplete_binder:l.initialize})}return t((function(){void 0!==window.ts?ts.ajax.register(l.initialize,!0):void 0!==window.bdajax?bdajax.register(l.initialize,!0):l.initialize(),h()})),e.AutocompleteSuggestion=o,e.AutocompleteWidget=l,e.register_array_subscribers=h,Object.defineProperty(e,"__esModule",{value:!0}),window.yafowil=window.yafowil||{},window.yafowil.autocomplete=e,e}({},jQuery);