From 1b2d75dc963c0f44e24a6fe3c57e19fef8f06361 Mon Sep 17 00:00:00 2001 From: mynamesleon Date: Wed, 22 Jan 2020 12:16:47 +0000 Subject: [PATCH] fix API filter method + adjust screen reader announcements to be limited by max results + update version number to 1.0.1 --- README.md | 42 ++++++-------- dist/aria-autocomplete.min.js | 18 ++++-- package-lock.json | 20 +++++-- package.json | 8 ++- src/aria-autocomplete.js | 106 +++++++++------------------------- src/autocomplete-helpers.js | 49 ---------------- src/autocomplete-ids.js | 17 ++++++ src/closest-polyfill.js | 15 +++++ 8 files changed, 111 insertions(+), 164 deletions(-) create mode 100644 src/autocomplete-ids.js create mode 100644 src/closest-polyfill.js diff --git a/README.md b/README.md index 0ad5005..b10a12b 100644 --- a/README.md +++ b/README.md @@ -38,45 +38,39 @@ new AriaAutocomplete(document.getElementById('some-element'), { }); ``` -### Plain JavaScript module +At its core, the autocomplete requires only an element and a `source`. When the element is an input, its value will be set using the user's selection(s). If a `source` option isn't provided (is falsy, or an empty Array), and the element is either a ``, or has child checkboxes, those will be used to build up the `source`. - -```javascript -new AriaAutocomplete(document.getElementById('some-input'), { - source: ['Afghanistan', 'Albania', 'Algeria', ...more] -}); - -const select = document.getElementById('some-select'); -new AriaAutocomplete(select); - -const div = document.getElementById('some-div-with-child-checkboxes'); -new AriaAutocomplete(div); -``` - -### Options +## Options The full list of options, and their defaults: diff --git a/dist/aria-autocomplete.min.js b/dist/aria-autocomplete.min.js index afd8df1..5b8f6d5 100644 --- a/dist/aria-autocomplete.min.js +++ b/dist/aria-autocomplete.min.js @@ -3,12 +3,20 @@ parcelRequire=function(e,r,t,n){var i,o="function"==typeof parcelRequire&&parcel },{}],"AEQi":[function(require,module,exports) { var define; var t;parcelRequire=function(e,n,i,r){var o,s="function"==typeof parcelRequire&&parcelRequire,a="function"==typeof require&&require;function u(t,i){if(!n[t]){if(!e[t]){var r="function"==typeof parcelRequire&&parcelRequire;if(!i&&r)return r(t,!0);if(s)return s(t,!0);if(a&&"string"==typeof t)return a(t);var o=new Error("Cannot find module '"+t+"'");throw o.code="MODULE_NOT_FOUND",o}h.resolve=function(n){return e[t][1][n]||n},h.cache={};var c=n[t]=new u.Module(t);e[t][0].call(c.exports,h,c,c.exports,this)}return n[t].exports;function h(t){return u(h.resolve(t))}}u.isParcelRequire=!0,u.Module=function(t){this.id=t,this.bundle=u,this.exports={}},u.modules=e,u.cache=n,u.parent=s,u.register=function(t,n){e[t]=[function(t,e){e.exports=n},{}]};for(var c=0;c=48&&t<=57||t>=65&&t<=90||t>=96&&t<=111||t>=186&&t<=222||32===t||8===t||46===t}},{}],Focm:[function(t,e,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.default=void 0;var i,r=function(t){return t&&t.__esModule?t:{default:t}}(t("is-printable-keycode"));function o(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};if(!t.metaKey&&!t.altKey){var e,n=this.input.value;if(t.type&&"keydown"===t.type.toLowerCase()){var i=t.keyCode,o=46===i,s=8===i;if(o||s){var a=function(t){var e={};if("selectionStart"in t)e.start=t.selectionStart,e.length=t.selectionEnd-e.start;else if(document.selection){t.focus();var n=document.selection.createRange(),i=n.text.length;n.moveStart("character",-t.value.length),e.start=n.text.length-i,e.length=i}return e}(this.input);a.length?n=n.substring(0,a.start)+n.substring(a.start+a.length):s&&a.start?n=n.substring(0,a.start-1)+n.substring(a.start+1):o&&void 0!==a.start&&(n=n.substring(0,a.start)+n.substring(a.start+1))}else if((0,r.default)(i)){var u=String.fromCharCode(i);n+=u=t.shiftKey?u.toUpperCase():u.toLowerCase()}}!n&&(e=this.input.getAttribute("placeholder"))&&(n=e);var c=this.measureString(n)+4;this.options&&this.options.cache&&this.cache&&(this.cache[n]=c);var h=this.options&&this.options.minWidth;"number"==typeof h&&cl&&(c=l),c!==this.currentWidth&&(this.currentWidth=c,this.input.style.width="".concat(c,"px"))}}},{key:"destroy",value:function(){this.input.removeEventListener("blur",this.eventHandler),this.input.removeEventListener("input",this.eventHandler),this.input.removeEventListener("keyup",this.eventHandler),this.input.removeEventListener("keydown",this.eventHandler),this.input=this.cache=null}}]),t}();n.default=a},{"is-printable-keycode":"vL70"}]},{},["Focm"]); +},{}],"KO9f":[function(require,module,exports) { +"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=t;var e=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;function t(r,l){if(r&&"number"==typeof r.length)for(var s=0,u=r.length;s-1}function t(t,r){if(t&&"number"==typeof t.length){for(var n=0,u=t.length;n-1}function o(e,t){for(var r=n(e),a=" ".concat(r," "),o="",i=0,l=t.split(" "),s=l.length;i1&&void 0!==arguments[1]&&arguments[1];return e=r(e).toLowerCase().replace(c,"").replace(l,"and").replace(s," "),t&&(e=e.replace(u,"\\$&")),e}function f(){for(var e={},t=0,r=arguments.length;t1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0,a=[],o=r.value,i=r.label,l=0,s=t.length;l3&&void 0!==arguments[3]&&arguments[3];for(var a in n&&(r=v(r,!0)),e)if(e.hasOwnProperty(a)){var o=!1;if("string"==typeof e[a]){for(var i=t.length;i--;)if(t[i]===a){o=!0;break}}else o=Array.isArray(e[a]);if(o&&d(e[a],r,a))return!0}return!1}function m(e){for(var t=[],n=0,a=e.length;n1&&void 0!==arguments[1]&&arguments[1];return e=t(e).toLowerCase().replace(o,"").replace(n,"and").replace(a," "),r&&(e=e.replace(l,"\\$&")),e}function s(){for(var e={},r=0,t=arguments.length;r1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0,a=[],o=t.value,l=t.label,c=0,v=r.length;c3&&void 0!==arguments[3]&&arguments[3];for(var a in n&&(t=i(t,!0)),e)if(e.hasOwnProperty(a)){var o=!1;if("string"==typeof e[a]){for(var l=r.length;l--;)if(r[l]===a){o=!0;break}}else o=Array.isArray(e[a]);if(o&&f(e[a],t,a))return!0}return!1}function g(e){for(var r=[],n=0,a=e.length;n1&&void 0!==arguments[1]&&arguments[1];this.input.value=this.term=e,t&&(this.inputPollingValue=e),this.autoGrow&&this.inputAutoWidth&&this.inputAutoWidth.trigger()}},{key:"indexOfQueryIn",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.input.value,s=arguments.length>2?arguments[2]:void 0;if(t=(0,i.trimString)(t).toLowerCase()){s=s||"label";for(var n=0,l=e.length;n-1&&this.selected[t]){var l=(0,i.mergeObjects)(this.selected[t]),r=l.label;(0,i.setElementState)(l.element,!1,this),this.selected.splice(t,1),this.triggerOptionCallback("onDelete",[l]),this.setSourceElementValues(),this.buildMultiSelected(),this.announce("".concat(r," ").concat(this.options.srDeletedText),0)}}},{key:"buildMultiSelected",value:function(){if(this.multiple){this.multiple&&this.selected.length>=this.options.maxItems?this.disable():this.enable();var e=this.getSelectedElems();if(this.selected.length||e.length){for(var t=[],s=e.length;s--;){for(var n=e[s][i.SELECTED_OPTION_PROP],l=this.selected.length,r=!1;l--;){var a=this.selected[l];if(a===n||a.value===n.value){r=!0;break}}r?t.push(e[s]):this.wrapper.removeChild(e[s])}for(var o=this.options.srDeleteText,c=document.createDocumentFragment(),h="".concat(this.cssNameSpace,"__selected"),u=0,d=this.selected.length;u=s.length)return this.currentSelectedIndex=s.length-1,void this.setOptionFocus(e,this.currentSelectedIndex);var n=s[t];if(n&&"string"==typeof n.getAttribute("tabindex")){this.currentSelectedIndex=t;var l="".concat(this.cssNameSpace,"__option--focused focused focus");return(0,i.addClass)(n,l),n.setAttribute("aria-selected","true"),void n.focus()}this.currentSelectedIndex=-1}},{key:"setSourceElementValues",value:function(){for(var e=[],t=0,s=this.selected.length;t2&&void 0!==arguments[2])||arguments[2];if(!("number"!=typeof t||t<0||this.multiple&&this.selected.length>=this.options.maxItems)&&this.filteredSource.length&&this.filteredSource[t]){for(var n=(0,i.mergeObjects)(this.filteredSource[t]),l=this.selected.length,r=!1;l--;)if(this.selected[l].value===n.value){r=!0;break}this.setInputValue(this.multiple?"":n.label,!0),r||this.multiple||(this.selected=[]),r||(this.selected.push(n),this.setSourceElementValues(),this.buildMultiSelected()),this.triggerOptionCallback("onConfirm",[n]),this.announce("".concat(n.label," ").concat(this.options.srSelectedText),0),this.disabled||!1===s||this.input.focus(),this.hide()}}},{key:"removeSelectedFromResults",value:function(e){if(!this.multiple||!this.selected.length)return e;var t=[];e:for(var i=0,s=e.length;i').concat(m,""))}t.length?((0,i.addClass)(this.list,"".concat(o,"__list--has-results")),(0,i.removeClass)(this.list,"".concat(o,"__list--no-results"))):((0,i.removeClass)(this.list,"".concat(o,"__list--has-results")),(0,i.addClass)(this.list,"".concat(o,"__list--no-results")));var f=this.options.noResultsText;if(!t.length&&"string"==typeof f&&f.length){r=f;var v="".concat(o,"__option");t.push('
  • ').concat(f,"
  • "))}this.cancelFilterPrep(),r||(r=this.triggerOptionCallback("srResultsText",[c])),this.announce(r);var g=t.join("");if(this.currentListHtml!==g?(this.currentListHtml=g,this.list.innerHTML=g):this.resetOptionAttributes(),!t.length)return this.hide(),void(this.forceShowAll=!1);this.show(),this.forceShowAll=!1}},{key:"handleAsync",value:function(e){var t=this,s=arguments.length>1&&void 0!==arguments[1]&&arguments[1];this.xhr&&this.xhr.abort();var n=new XMLHttpRequest,l=encodeURIComponent,r=this.forceShowAll,a=r||s,o=s?null:this.api,c=this.multiple?this.selected.length:0,h=/\?/.test(this.source)?"&":"?",u=this.source+h+"".concat(l(this.options.asyncQueryParam),"=").concat(l(e),"&")+"".concat(l(this.options.asyncMaxResultsParam),"=")+"".concat(a?9999:c+this.options.maxResults);u=this.triggerOptionCallback("onAsyncPrep",[u],o)||u,n.open("GET",u),n.onload=function(){if(n.readyState===n.DONE&&200===n.status){t.forceShowAll=r;var l=t.triggerOptionCallback("onAsyncSuccess",[e,n],o),a=t.options.sourceMapping,c=l||n.responseText,h=(0,i.processSourceArray)(c,a,!1);s?(t.prepSelectedFromArray(h),t.setInputStartingStates(!1)):t.setListOptions(h)}},s||(this.xhr=n),n.send()}},{key:"filter",value:function(e){var t=this;if("string"==typeof e){var s=this.forceShowAll,n=this.triggerOptionCallback("onSearch",[e]);if(s||"string"!=typeof n||(e=n),this.term=this.inputPollingValue=e,"string"==typeof this.source&&this.source.length)return this.handleAsync(e),void(this.forceShowAll=!1);if("function"!=typeof this.source){e||(s=!0);var l=[];if(this.source&&this.source.length){var r=[i.CLEANED_LABEL_PROP];if(!s){e=(0,i.cleanString)(e,!0);var a=this.options.alsoSearchIn;Array.isArray(a)&&a.length&&(r=(0,i.removeDuplicatesAndLabel)(r.concat(a)))}for(var o=0,c=this.source.length;o1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],l=this.forceShowAll,r=l||n?0:this.options.delay;this.cancelFilterPrep(),this.filtering=!0,this.filterTimer=setTimeout(function(){var n=t.input.value;if((l||""===n||s&&!t.multiple&&t.selected.length&&(0,i.trimString)(t.selected[0].label)===(0,i.trimString)(n))&&(n=""),t.setInputDescription(),t.inputPollingValue=n,!l&&n.length1&&void 0!==arguments[1]&&arguments[1];this.componentBlurTimer&&clearTimeout(this.componentBlurTimer);var n=s?0:100;this.componentBlurTimer=setTimeout(function(){var e=document.activeElement;if(s||!e||t.showAll&&t.showAll===e||e[i.SELECTED_OPTION_PROP]||!t.wrapper.contains(e)){t.xhr&&t.xhr.abort();var n=t.indexOfQueryIn.bind(t);if(!s&&t.options.confirmOnBlur&&t.menuOpen){var l=t.currentSelectedIndex;"number"==typeof l&&-1!==l||(l=n(t.filteredSource)),t.handleOptionSelect({},l,!1)}if(t.cancelFilterPrep(),t.hide(),!t.multiple&&-1===n(t.selected))(t.elementIsInput||t.elementIsSelect)&&""!==t.element.value&&(t.element.value="",(0,i.dispatchEvent)(t.element,"change")),t.selected.length&&t.removeEntryFromSelected(t.selected[0]),t.setInputValue("",!0);t.multiple&&t.setInputValue("",!0),t.documentClickBound&&(t.documentClickBound=!1,document.removeEventListener("click",t.documentClick))}},n)}},{key:"handleEnterKey",value:function(e){if(this.isSelectedElem(e.target)){var t=e.target[i.SELECTED_OPTION_PROP];return this.removeEntryFromSelected(t)}this.disabled||(this.showAll&&e.target===this.showAll?this.filterPrepShowAll(e):(this.menuOpen&&(e.preventDefault(),this.currentSelectedIndex>-1&&this.handleOptionSelect(e,this.currentSelectedIndex)),e.target===this.input&&this.filterPrep(e,!1,!0)))}},{key:"handleDownArrowKey",value:function(e){if(e.preventDefault(),this.menuOpen||(this.forceShowAll=this.options.minLength<1,(this.forceShowAll||this.input.value.length>=this.options.minLength)&&this.filterPrep(e)),this.menuOpen&&!this.filtering){var t=this.currentSelectedIndex;"number"!=typeof t||t<0?this.setOptionFocus(e,0):this.setOptionFocus(e,t+1)}}},{key:"handleUpArrowKey",value:function(e){e.preventDefault(),!this.disabled&&this.menuOpen&&"number"==typeof this.currentSelectedIndex&&this.setOptionFocus(e,this.currentSelectedIndex-1)}},{key:"handleKeyDownDefault",value:function(t){var i=t.target===this.input;if(32===t.keyCode&&!i)return t.preventDefault(),this.handleEnterKey(t);if(!this.disabled){var s=this.selected&&this.selected.length;if(this.options.deleteOnBackspace&&""===this.input.value&&8===t.keyCode&&s&&i&&this.multiple)this.removeEntryFromSelected(this.selected[s-1]);else{var n=(0,e.default)(t.keyCode),l=!i&&n;l&&this.input.focus(),(l||i&&n)&&this.filterPrep(t)}}}},{key:"prepKeyDown",value:function(e){switch(e.keyCode){case 13:this.handleEnterKey(e);break;case 27:this.handleComponentBlur(e,!0);break;case 38:this.handleUpArrowKey(e);break;case 40:this.handleDownArrowKey(e);break;default:this.handleKeyDownDefault(e)}}},{key:"cancelPolling",value:function(){this.pollingTimer&&clearTimeout(this.pollingTimer)}},{key:"startPolling",value:function(){var e=this;this.filtering||this.input.value===this.inputPollingValue||this.filterPrep({}),this.pollingTimer=setTimeout(function(){e.startPolling()},200)}},{key:"bindEvents",value:function(){var e=this;this.wrapper.addEventListener("focusout",function(t){e.handleComponentBlur(t,!1)}),this.wrapper.addEventListener("focusin",function(t){e.list.contains(t.target)||(e.currentSelectedIndex=-1)}),this.wrapper.addEventListener("keydown",function(t){e.prepKeyDown(t)}),this.wrapper.addEventListener("click",function(t){if(t.target!==e.wrapper){if(e.isSelectedElem(t.target)){var s=t.target[i.SELECTED_OPTION_PROP];e.removeEntryFromSelected(s)}}else e.input.focus()});var s="".concat(this.cssNameSpace,"__wrapper--focused focused focus"),n="".concat(this.cssNameSpace,"__input--focused focused focus");this.input.addEventListener("blur",function(){(0,i.removeClass)(e.wrapper,s),(0,i.removeClass)(e.input,n),e.cancelPolling()}),this.input.addEventListener("input",function(t){e.filterPrep(t)}),this.input.addEventListener("click",function(t){!e.menuOpen&&e.input.value.length>=e.options.minLength&&e.filterPrep(t,!0)}),this.input.addEventListener("focusin",function(){(0,i.addClass)(e.wrapper,s),(0,i.addClass)(e.input,n),e.startPolling(),e.disabled||e.menuOpen||e.filterPrep(event,!0)}),this.showAll&&this.showAll.addEventListener("click",function(t){e.filterPrepShowAll(t)}),this.list.addEventListener("mouseenter",function(t){e.resetOptionAttributes()}),this.list.addEventListener("click",function(t){if(t.target!==e.list){var i=e.list.childNodes;if(i.length){var s=[].indexOf.call(i,t.target);e.handleOptionSelect(t,s)}}}),this.autoGrow&&(this.inputAutoWidth=new t.default(this.input))}},{key:"prepListSourceCheckboxes",value:function(){this.multiple=!0,this.source=[];for(var e=this.element.querySelectorAll('input[type="checkbox"]'),t=0,s=e.length;t-1)){var c=o(e,a,"value");c>-1&&this.selected.push(e[c])}}}},{key:"prepListSourceArray",value:function(){var e=this.options.sourceMapping;this.source=(0,i.processSourceArray)(this.source,e),this.prepSelectedFromArray(this.source)}},{key:"prepListSourceAsync",value:function(){this.elementIsInput&&this.element.value&&this.handleAsync(this.element.value,!0)}},{key:"prepListSourceFunction",value:function(){var e=this;this.elementIsInput&&this.element.value&&this.source.call(void 0,this.element.value,function(t){e.prepSelectedFromArray((0,i.processSourceArray)(t,e.options.sourceMapping)),e.setInputStartingStates(!1)})}},{key:"prepListSource",value:function(){return"function"==typeof this.source?this.prepListSourceFunction():"string"==typeof this.source&&this.source.length?this.prepListSourceAsync():Array.isArray(this.source)&&this.source.length?this.prepListSourceArray():this.elementIsSelect?this.prepListSourceDdl():void(this.element.querySelector('input[type="checkbox"]')&&this.prepListSourceCheckboxes())}},{key:"setInputStartingStates",value:function(){if(!(arguments.length>0&&void 0!==arguments[0])||arguments[0]){if(this.ids.ELEMENT){var e=document.querySelector('[for="'+this.ids.ELEMENT+'"]');e&&(e.ariaAutocompleteOriginalFor=this.ids.ELEMENT,e.setAttribute("for",this.ids.INPUT))}var t=this.element.getAttribute("aria-describedby");t&&this.input.setAttribute("aria-describedby",t);var i=this.element.getAttribute("aria-labelledby");i&&this.input.setAttribute("aria-labelledby",i)}this.selected.length&&(this.multiple?this.buildMultiSelected():this.setInputValue(this.selected[0].label||"",!0)),this.setInputDescription(),this.element.disabled&&this.disable()}},{key:"setHtml",value:function(){var e=this.options,t=this.cssNameSpace,i=e.wrapperClassName?" ".concat(e.wrapperClassName):"",s=['
    ')],n=e.name?" ".concat(e.name):"",l=e.inputClassName?" ".concat(e.inputClassName):"",r=e.placeholder?' placeholder="'.concat(e.placeholder,'" aria-placeholder="').concat(e.placeholder,'"'):"";s.push('")),e.showAllControl&&s.push(''));var a=e.srListLabelText,o=e.listClassName?" ".concat(e.listClassName):"",c=a?' aria-label="'.concat(a,'"'):"";s.push('
      ")),s.push('').concat(e.srAssistiveText,"")),s.push('')),s.push("
      "),this.element.insertAdjacentHTML("afterend",s.join(""))}},{key:"generateApi",value:function(){var e=this;this.api={open:function(){return e.show.call(e)},close:function(){return e.hide.call(e)},filter:function(t){return e.filter.call(t)}};for(var t=["options","destroy","enable","disable","input","wrapper","list","selected"],i=function(i,s){e.api[t[i]]="function"==typeof e[t[i]]?function(){return e[t[i]].call(e)}:e[t[i]]},s=0,n=t.length;s1&&void 0!==arguments[1]&&arguments[1];this.input.value=this.term=e,t&&(this.inputPollingValue=e),this.autoGrow&&this.inputAutoWidth&&this.inputAutoWidth.trigger()}},{key:"indexOfQueryIn",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.input.value,i=arguments.length>2?arguments[2]:void 0;if(t=(0,l.trimString)(t).toLowerCase()){i=i||"label";for(var s=0,n=e.length;s-1&&this.selected[t]){var n=(0,l.mergeObjects)(this.selected[t]),r=n.label;(0,l.setElementState)(n.element,!1,this),this.selected.splice(t,1),this.triggerOptionCallback("onDelete",[n]),this.setSourceElementValues(),this.buildMultiSelected(),this.announce("".concat(r," ").concat(this.options.srDeletedText),0)}}},{key:"buildMultiSelected",value:function(){if(this.multiple){this.multiple&&this.selected.length>=this.options.maxItems?this.disable():this.enable();var e=this.getSelectedElems();if(this.selected.length||e.length){for(var t=[],i=e.length;i--;){for(var s=e[i][l.SELECTED_OPTION_PROP],n=this.selected.length,r=!1;n--;){var a=this.selected[n];if(a===s||a.value===s.value){r=!0;break}}r?t.push(e[i]):this.wrapper.removeChild(e[i])}for(var o=this.options.srDeleteText,c=document.createDocumentFragment(),h="".concat(this.cssNameSpace,"__selected"),u=0,p=this.selected.length;u=i.length)return this.currentSelectedIndex=i.length-1,void this.setOptionFocus(e,this.currentSelectedIndex);var n=i[t];if(n&&"string"==typeof n.getAttribute("tabindex")){this.currentSelectedIndex=t;var l="".concat(this.cssNameSpace,"__option--focused focused focus");return(0,s.default)(n,l),n.setAttribute("aria-selected","true"),void n.focus()}this.currentSelectedIndex=-1}},{key:"setSourceElementValues",value:function(){for(var e=[],t=0,i=this.selected.length;t2&&void 0!==arguments[2])||arguments[2];if(!("number"!=typeof t||t<0||this.multiple&&this.selected.length>=this.options.maxItems)&&this.filteredSource.length&&this.filteredSource[t]){for(var s=(0,l.mergeObjects)(this.filteredSource[t]),n=this.selected.length,r=!1;n--;)if(this.selected[n].value===s.value){r=!0;break}this.setInputValue(this.multiple?"":s.label,!0),r||this.multiple||this.selected.splice(0),r||(this.selected.push(s),this.setSourceElementValues(),this.buildMultiSelected()),this.triggerOptionCallback("onConfirm",[s]),this.announce("".concat(s.label," ").concat(this.options.srSelectedText),0),this.disabled||!1===i||this.input.focus(),this.hide()}}},{key:"removeSelectedFromResults",value:function(e){if(!this.multiple||!this.selected.length)return e;var t=[];e:for(var i=0,s=e.length;i').concat(g,""))}t.length?((0,s.default)(this.list,"".concat(h,"__list--has-results")),(0,i.default)(this.list,"".concat(h,"__list--no-results"))):((0,i.default)(this.list,"".concat(h,"__list--has-results")),(0,s.default)(this.list,"".concat(h,"__list--no-results")));var S=this.options.noResultsText;if(!t.length&&"string"==typeof S&&S.length){o=S;var b="".concat(h,"__option");t.push('
    • ').concat(S,"
    • "))}this.cancelFilterPrep(),o||(o=this.triggerOptionCallback("srResultsText",[f])),this.announce(o);var y=t.join("");if(this.currentListHtml!==y?(this.currentListHtml=y,this.list.innerHTML=y):this.resetOptionAttributes(),!t.length)return this.hide(),void(this.forceShowAll=!1);this.show(),this.forceShowAll=!1}},{key:"handleAsync",value:function(e){var t=this,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1];this.xhr&&this.xhr.abort();var s=new XMLHttpRequest,n=encodeURIComponent,r=this.forceShowAll,o=i?null:this.api,c=this.multiple?this.selected.length:0,h=/\?/.test(this.source)?"&":"?",u=r||i||this.options.maxResults===a.default.maxResults,p=this.source+h+"".concat(n(this.options.asyncQueryParam),"=").concat(n(e),"&")+"".concat(n(this.options.asyncMaxResultsParam),"=")+"".concat(u?9999:c+this.options.maxResults);p=this.triggerOptionCallback("onAsyncPrep",[p],o)||p,s.open("GET",p),s.onload=function(){if(s.readyState===s.DONE&&200===s.status){t.forceShowAll=r;var n=t.triggerOptionCallback("onAsyncSuccess",[e,s],o),a=t.options.sourceMapping,c=n||s.responseText,h=(0,l.processSourceArray)(c,a,!1);i?(t.prepSelectedFromArray(h),t.setInputStartingStates(!1)):t.setListOptions(h)}},i||(this.xhr=s),s.send()}},{key:"filter",value:function(e){var t=this;if("string"==typeof e){var i=this.forceShowAll,s=this.triggerOptionCallback("onSearch",[e]);if(i||"string"!=typeof s||(e=s),this.term=this.inputPollingValue=e,"string"==typeof this.source&&this.source.length)return this.handleAsync(e),void(this.forceShowAll=!1);if("function"!=typeof this.source){e||(i=!0);var n=[];if(this.source&&this.source.length){var r=[l.CLEANED_LABEL_PROP];if(!i){e=(0,l.cleanString)(e,!0);var a=this.options.alsoSearchIn;Array.isArray(a)&&a.length&&(r=(0,l.removeDuplicatesAndLabel)(r.concat(a)))}for(var o=0,c=this.source.length;o1&&void 0!==arguments[1]&&arguments[1],n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=this.forceShowAll,a=r||n?0:this.options.delay;this.cancelFilterPrep(),this.filtering=!0,this.filterTimer=setTimeout(function(){var n=t.input.value;if((r||""===n||i&&!t.multiple&&t.selected.length&&(0,l.trimString)(t.selected[0].label)===(0,l.trimString)(n))&&(n=""),t.setInputDescription(),t.inputPollingValue=n,!r&&n.length1&&void 0!==arguments[1]&&arguments[1];this.componentBlurTimer&&clearTimeout(this.componentBlurTimer);var s=i?0:100;this.componentBlurTimer=setTimeout(function(){var e=document.activeElement;if(i||!e||t.showAll&&t.showAll===e||e[l.SELECTED_OPTION_PROP]||!t.wrapper.contains(e)){t.xhr&&t.xhr.abort();var s=t.indexOfQueryIn.bind(t);if(!i&&t.options.confirmOnBlur&&t.menuOpen){var n=t.currentSelectedIndex;"number"==typeof n&&-1!==n||(n=s(t.filteredSource)),t.handleOptionSelect({},n,!1)}if(t.cancelFilterPrep(),t.hide(),!t.multiple&&-1===s(t.selected))(t.elementIsInput||t.elementIsSelect)&&""!==t.element.value&&(t.element.value="",(0,l.dispatchEvent)(t.element,"change")),t.selected.length&&t.removeEntryFromSelected(t.selected[0]),t.setInputValue("",!0);t.multiple&&t.setInputValue("",!0),t.documentClickBound&&(t.documentClickBound=!1,document.removeEventListener("click",t.documentClick))}},s)}},{key:"handleEnterKey",value:function(e){if(this.isSelectedElem(e.target)){var t=e.target[l.SELECTED_OPTION_PROP];return this.removeEntryFromSelected(t)}this.disabled||(this.showAll&&e.target===this.showAll?this.filterPrepShowAll(e):(this.menuOpen&&(e.preventDefault(),this.currentSelectedIndex>-1&&this.handleOptionSelect(e,this.currentSelectedIndex)),e.target===this.input&&this.filterPrep(e,!1,!0)))}},{key:"handleDownArrowKey",value:function(e){if(e.preventDefault(),this.menuOpen||(this.forceShowAll=this.options.minLength<1,(this.forceShowAll||this.input.value.length>=this.options.minLength)&&this.filterPrep(e)),this.menuOpen&&!this.filtering){var t=this.currentSelectedIndex;"number"!=typeof t||t<0?this.setOptionFocus(e,0):this.setOptionFocus(e,t+1)}}},{key:"handleUpArrowKey",value:function(e){e.preventDefault(),!this.disabled&&this.menuOpen&&"number"==typeof this.currentSelectedIndex&&this.setOptionFocus(e,this.currentSelectedIndex-1)}},{key:"handleKeyDownDefault",value:function(t){var i=t.target===this.input;if(32===t.keyCode&&!i)return t.preventDefault(),this.handleEnterKey(t);if(!this.disabled){var s=this.selected&&this.selected.length;if(this.options.deleteOnBackspace&&""===this.input.value&&8===t.keyCode&&s&&i&&this.multiple)this.removeEntryFromSelected(this.selected[s-1]);else{var n=(0,e.default)(t.keyCode),l=!i&&n;l&&this.input.focus(),(l||i&&n)&&this.filterPrep(t)}}}},{key:"prepKeyDown",value:function(e){switch(e.keyCode){case 13:this.handleEnterKey(e);break;case 27:this.handleComponentBlur(e,!0);break;case 38:this.handleUpArrowKey(e);break;case 40:this.handleDownArrowKey(e);break;default:this.handleKeyDownDefault(e)}}},{key:"cancelPolling",value:function(){this.pollingTimer&&clearTimeout(this.pollingTimer)}},{key:"startPolling",value:function(){var e=this;this.filtering||this.input.value===this.inputPollingValue||this.filterPrep({}),this.pollingTimer=setTimeout(function(){e.startPolling()},200)}},{key:"bindEvents",value:function(){var e=this;this.wrapper.addEventListener("focusout",function(t){e.handleComponentBlur(t,!1)}),this.wrapper.addEventListener("focusin",function(t){e.list.contains(t.target)||(e.currentSelectedIndex=-1)}),this.wrapper.addEventListener("keydown",function(t){e.prepKeyDown(t)}),this.wrapper.addEventListener("click",function(t){if(t.target!==e.wrapper){if(e.isSelectedElem(t.target)){var i=t.target[l.SELECTED_OPTION_PROP];e.removeEntryFromSelected(i)}}else e.input.focus()});var n="".concat(this.cssNameSpace,"__wrapper--focused focused focus"),r="".concat(this.cssNameSpace,"__input--focused focused focus");this.input.addEventListener("blur",function(){(0,i.default)(e.wrapper,n),(0,i.default)(e.input,r),e.cancelPolling()}),this.input.addEventListener("input",function(t){e.filterPrep(t)}),this.input.addEventListener("click",function(t){!e.menuOpen&&e.input.value.length>=e.options.minLength&&e.filterPrep(t,!0)}),this.input.addEventListener("focusin",function(){(0,s.default)(e.wrapper,n),(0,s.default)(e.input,r),e.startPolling(),e.disabled||e.menuOpen||e.filterPrep(event,!0)}),this.showAll&&this.showAll.addEventListener("click",function(t){e.filterPrepShowAll(t)}),this.list.addEventListener("mouseenter",function(t){e.resetOptionAttributes()}),this.list.addEventListener("click",function(t){if(t.target!==e.list){var i=e.list.childNodes;if(i.length){var s=[].indexOf.call(i,t.target);e.handleOptionSelect(t,s)}}}),this.autoGrow&&(this.inputAutoWidth=new t.default(this.input))}},{key:"prepListSourceCheckboxes",value:function(){this.multiple=!0,this.source=[];for(var e=this.element.querySelectorAll('input[type="checkbox"]'),t=0,i=e.length;t-1)){var c=o(e,a,"value");c>-1&&this.selected.push(e[c])}}}},{key:"prepListSourceArray",value:function(){var e=this.options.sourceMapping;this.source=(0,l.processSourceArray)(this.source,e),this.prepSelectedFromArray(this.source)}},{key:"prepListSourceAsync",value:function(){this.elementIsInput&&this.element.value&&this.handleAsync(this.element.value,!0)}},{key:"prepListSourceFunction",value:function(){var e=this;this.elementIsInput&&this.element.value&&this.source.call(void 0,this.element.value,function(t){e.prepSelectedFromArray((0,l.processSourceArray)(t,e.options.sourceMapping)),e.setInputStartingStates(!1)})}},{key:"prepListSource",value:function(){return"function"==typeof this.source?this.prepListSourceFunction():"string"==typeof this.source&&this.source.length?this.prepListSourceAsync():Array.isArray(this.source)&&this.source.length?this.prepListSourceArray():this.elementIsSelect?this.prepListSourceDdl():void(this.element.querySelector('input[type="checkbox"]')&&this.prepListSourceCheckboxes())}},{key:"setInputStartingStates",value:function(){if(!(arguments.length>0&&void 0!==arguments[0])||arguments[0]){if(this.ids.ELEMENT){var e=document.querySelector('[for="'+this.ids.ELEMENT+'"]');e&&(e.ariaAutocompleteOriginalFor=this.ids.ELEMENT,e.setAttribute("for",this.ids.INPUT))}var t=this.element.getAttribute("aria-describedby");t&&this.input.setAttribute("aria-describedby",t);var i=this.element.getAttribute("aria-labelledby");i&&this.input.setAttribute("aria-labelledby",i)}this.selected.length&&(this.multiple?this.buildMultiSelected():this.setInputValue(this.selected[0].label||"",!0)),this.setInputDescription(),this.element.disabled&&this.disable()}},{key:"setHtml",value:function(){var e=this.options,t=this.cssNameSpace,i=e.wrapperClassName?" ".concat(e.wrapperClassName):"",s=['
      ')],n=e.name?" ".concat(e.name):"",l=e.inputClassName?" ".concat(e.inputClassName):"",r=e.placeholder?' placeholder="'.concat(e.placeholder,'" aria-placeholder="').concat(e.placeholder,'"'):"";s.push('")),e.showAllControl&&s.push(''));var a=e.srListLabelText,o=e.listClassName?" ".concat(e.listClassName):"",c=a?' aria-label="'.concat(a,'"'):"";s.push('
        ")),s.push('').concat(e.srAssistiveText,"")),s.push('')),s.push("
        "),this.element.insertAdjacentHTML("afterend",s.join(""))}},{key:"generateApi",value:function(){var e=this;this.api={open:function(){return e.show.call(e)},close:function(){return e.hide.call(e)},filter:function(t){return e.filter.call(e,t)}};for(var t=["options","destroy","enable","disable","input","wrapper","list","selected"],i=function(i,s){e.api[t[i]]="function"==typeof e[t[i]]?function(){return e[t[i]].call(e)}:e[t[i]]},s=0,n=t.length;s${itemContent}` + `aria-setsize="${lengthToUse}">${itemContent}` ); } @@ -636,9 +625,7 @@ class AriaAutocomplete { if (!toShow.length && typeof noText === 'string' && noText.length) { announce = noText; let optionClass = `${cssName}__option`; - toShow.push( - `
      • ${noText}
      • ` - ); + toShow.push(`
      • ${noText}
      • `); } // remove loading class(es) and reset variables @@ -646,7 +633,7 @@ class AriaAutocomplete { // announce to screen reader if (!announce) { - announce = this.triggerOptionCallback('srResultsText', [length]); + announce = this.triggerOptionCallback('srResultsText', [lengthToUse]); } this.announce(announce); @@ -689,10 +676,11 @@ class AriaAutocomplete { const xhr = new XMLHttpRequest(); const encode = encodeURIComponent; const isShowAll = this.forceShowAll; - const unlimited = isShowAll || isFirstCall; const context = isFirstCall ? null : this.api; const baseAmount = this.multiple ? this.selected.length : 0; const ampersandOrQuestionMark = /\?/.test(this.source) ? '&' : '?'; + const unlimited = + isShowAll || isFirstCall || this.options.maxResults === DEFAULT_OPTIONS.maxResults; let url = this.source + ampersandOrQuestionMark + @@ -1005,10 +993,7 @@ class AriaAutocomplete { // if closed, and text is long enough, run search if (!this.menuOpen) { this.forceShowAll = this.options.minLength < 1; - if ( - this.forceShowAll || - this.input.value.length >= this.options.minLength - ) { + if (this.forceShowAll || this.input.value.length >= this.options.minLength) { this.filterPrep(event); } } @@ -1218,9 +1203,7 @@ class AriaAutocomplete { this.multiple = true; // force multiple in this case // reset source and use checkboxes this.source = []; - const elements = this.element.querySelectorAll( - 'input[type="checkbox"]' - ); + const elements = this.element.querySelectorAll('input[type="checkbox"]'); for (let i = 0, l = elements.length; i < l; i += 1) { const checkbox = elements[i]; // must have a value other than empty string @@ -1330,9 +1313,7 @@ class AriaAutocomplete { prepListSourceFunction() { if (this.elementIsInput && this.element.value) { this.source.call(undefined, this.element.value, response => { - this.prepSelectedFromArray( - processSourceArray(response, this.options.sourceMapping) - ); + this.prepSelectedFromArray(processSourceArray(response, this.options.sourceMapping)); this.setInputStartingStates(false); }); } @@ -1376,9 +1357,7 @@ class AriaAutocomplete { if (setAriaAttrs) { // update corresponding label to now focus on the new input if (this.ids.ELEMENT) { - const label = document.querySelector( - '[for="' + this.ids.ELEMENT + '"]' - ); + const label = document.querySelector('[for="' + this.ids.ELEMENT + '"]'); if (label) { label.ariaAutocompleteOriginalFor = this.ids.ELEMENT; label.setAttribute('for', this.ids.INPUT); @@ -1424,9 +1403,7 @@ class AriaAutocomplete { const o = this.options; const cssName = this.cssNameSpace; const wrapperClass = o.wrapperClassName ? ` ${o.wrapperClassName}` : ''; - const newHtml = [ - `
        ` - ]; + const newHtml = [`
        `]; // add input const name = o.name ? ` ${o.name}` : ``; @@ -1478,25 +1455,13 @@ class AriaAutocomplete { this.api = { open: () => this.show.call(this), close: () => this.hide.call(this), - filter: val => this.filter.call(val) + filter: val => this.filter.call(this, val) }; - const a = [ - 'options', - 'destroy', - 'enable', - 'disable', - 'input', - 'wrapper', - 'list', - 'selected' - ]; + const a = ['options', 'destroy', 'enable', 'disable', 'input', 'wrapper', 'list', 'selected']; for (let i = 0, l = a.length; i < l; i += 1) { - this.api[a[i]] = - typeof this[a[i]] === 'function' - ? () => this[a[i]].call(this) - : this[a[i]]; + this.api[a[i]] = typeof this[a[i]] === 'function' ? () => this[a[i]].call(this) : this[a[i]]; } // store api on original element @@ -1540,22 +1505,9 @@ class AriaAutocomplete { * @param {Object=} options */ init(element, options) { - // ids used for DOM queries and accessibility attributes e.g. aria-controls - appIndex += 1; - this.ids = {}; - this.ids.ELEMENT = element.id; - this.ids.PREFIX = `${element.id || ''}aria-autocomplete-${appIndex}`; - this.ids.LIST = `${this.ids.PREFIX}-list`; - this.ids.INPUT = `${this.ids.PREFIX}-input`; - this.ids.BUTTON = `${this.ids.PREFIX}-button`; - this.ids.OPTION = `${this.ids.PREFIX}-option`; - this.ids.WRAPPER = `${this.ids.PREFIX}-wrapper`; - this.ids.OPTION_SELECTED = `${this.ids.OPTION}-selected`; - this.ids.SR_ASSISTANCE = `${this.ids.PREFIX}-sr-assistance`; - this.ids.SR_ANNOUNCEMENTS = `${this.ids.PREFIX}-sr-announcements`; - this.selected = []; this.element = element; + this.ids = new AutocompleteIds(element.id); this.elementIsInput = element.nodeName === 'INPUT'; this.elementIsSelect = element.nodeName === 'SELECT'; this.options = mergeObjects(DEFAULT_OPTIONS, options); @@ -1575,9 +1527,7 @@ class AriaAutocomplete { this.input = document.getElementById(this.ids.INPUT); this.wrapper = document.getElementById(this.ids.WRAPPER); this.showAll = document.getElementById(this.ids.BUTTON); - this.srAnnouncements = document.getElementById( - this.ids.SR_ANNOUNCEMENTS - ); + this.srAnnouncements = document.getElementById(this.ids.SR_ANNOUNCEMENTS); // set internal source array, from static elements if necessary this.prepListSource(); diff --git a/src/autocomplete-helpers.js b/src/autocomplete-helpers.js index 256a929..16c381c 100644 --- a/src/autocomplete-helpers.js +++ b/src/autocomplete-helpers.js @@ -9,55 +9,6 @@ export function trimString(theString) { return theString == null ? '' : (theString + '').trim(); } -const getClass = elem => - (elem.getAttribute && elem.getAttribute('class')) || ''; -/** - * @description check if element has class - support pre `classList` - * @param {Element} elem - * @param {String} className - * @returns {Boolean} - */ -export function hasClass(elem, className) { - return ` ${getClass(elem)} `.indexOf(` ${className} `) > -1; -} - -/** - * @description add class(es) to element - support pre `classList` - * @param {Element} elem - * @param {String} classes - space delimitted class(es) to add - */ -export function addClass(elem, classes) { - const curValue = getClass(elem); - const cur = ` ${curValue} `; - let finalValue = ''; - - for (let i = 0, cs = classes.split(' '), l = cs.length; i < l; i += 1) { - if (cs[i] !== '' && cur.indexOf(` ${cs[i]} `) === -1) { - finalValue += ' ' + cs[i]; - } - } - if (curValue !== (finalValue = (curValue + finalValue).trim())) { - elem.setAttribute('class', finalValue); - } -} - -/** - * @description remove class(es) from element - support pre `classList` - * @param {Element} elem - * @param {String} classes - space delimitted class(es) to remove - */ -export function removeClass(elem, classes) { - const curValue = getClass(elem); - let finalValue = ` ${curValue} `; - - for (let i = 0, cs = classes.split(' '), l = cs.length; i < l; i += 1) { - finalValue = finalValue.replace(` ${cs[i]} `, ' '); - } - if (curValue !== (finalValue = finalValue.trim())) { - elem.setAttribute('class', finalValue); - } -} - // regex constants used for string cleaning const REGEX_AMPERSAND = /&/g; const REGEX_DUPE_WHITESPACE = /\s\s+/g; diff --git a/src/autocomplete-ids.js b/src/autocomplete-ids.js new file mode 100644 index 0000000..43f5c62 --- /dev/null +++ b/src/autocomplete-ids.js @@ -0,0 +1,17 @@ +// ids used for DOM queries and accessibility attributes e.g. aria-controls +let index = 0; +export default class { + constructor(id) { + index += 1; + this.ELEMENT = id; + this.PREFIX = `${id || ''}aria-autocomplete-${index}`; + this.LIST = `${this.PREFIX}-list`; + this.INPUT = `${this.PREFIX}-input`; + this.BUTTON = `${this.PREFIX}-button`; + this.OPTION = `${this.PREFIX}-option`; + this.WRAPPER = `${this.PREFIX}-wrapper`; + this.SR_ASSISTANCE = `${this.PREFIX}-sr-assistance`; + this.OPTION_SELECTED = `${this.PREFIX}-option-selected`; + this.SR_ANNOUNCEMENTS = `${this.PREFIX}-sr-announcements`; + } +} diff --git a/src/closest-polyfill.js b/src/closest-polyfill.js new file mode 100644 index 0000000..ef1fb4f --- /dev/null +++ b/src/closest-polyfill.js @@ -0,0 +1,15 @@ +if (!Element.prototype.matches) { + Element.prototype.matches = + Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; +} + +if (!Element.prototype.closest) { + Element.prototype.closest = function(s) { + let el = this; + do { + if (el.matches(s)) return el; + el = el.parentElement || el.parentNode; + } while (el !== null && el.nodeType === 1); + return null; + }; +}