From 68313da4122ec7f76274b22926e762914c96723d Mon Sep 17 00:00:00 2001 From: Josh Johnson Date: Mon, 23 Dec 2019 18:22:54 +0000 Subject: [PATCH] Convert to typescript (#795) * Typescript config setup * Add type annotations to components * Further type additions * And more... * Add types to actions * Add types to templates * Further type checks * Further type additons * Install fuse latest * Housekeeping * Remove old type definitions * Fix remaning type issues * Fix some failing tests * Remove types workflow * Fix failing unit tests * Resolve back space event regression * Convert cypress files to .ts * Fix eslint issues * Remove cachebusting urls * Resolve delete button bug * Resolve regression bugs * Fix lint script * Fix lint workflow * Pass args instead of object to keyboard handlers * Flatten misc reducer * Resolve keyboad action test failures * Use Pick instead of Partial * Use interfaces in action tests * Update firefox image * Incorporate #791 * Incorporate #788 --- .eslintrc.json | 35 +- .../__snapshots__/firefox-darwin.png | Bin 45603 -> 45377 bytes .github/workflows/lint.yml | 8 +- .github/workflows/types.yml | 27 - .mocharc.yml | 9 +- .vscode/settings.json | 5 +- ...ltiple.spec.js => select-multiple.spec.ts} | 0 ...{select-one.spec.js => select-one.spec.ts} | 0 .../{text.spec.js => text.spec.ts} | 0 package-lock.json | 326 +- package.json | 23 +- public/assets/scripts/choices.js | 7718 ++++++++--------- public/assets/scripts/choices.min.js | 6 +- public/test/select-multiple/index.html | 12 +- public/test/select-one/index.html | 12 +- public/test/text/index.html | 12 +- src/scripts/actions/choices.js | 58 - .../{choices.test.js => choices.test.ts} | 20 +- src/scripts/actions/choices.ts | 73 + src/scripts/actions/groups.js | 18 - .../{groups.test.js => groups.test.ts} | 5 +- src/scripts/actions/groups.ts | 27 + src/scripts/actions/items.js | 53 - .../actions/{items.test.js => items.test.ts} | 19 +- src/scripts/actions/items.ts | 70 + src/scripts/actions/misc.js | 28 - .../actions/{misc.test.js => misc.test.ts} | 16 +- src/scripts/actions/misc.ts | 30 + .../{choices.test.js => choices.test.ts} | 326 +- src/scripts/{choices.js => choices.ts} | 675 +- .../{container.test.js => container.test.ts} | 6 +- .../components/{container.js => container.ts} | 95 +- .../{dropdown.test.js => dropdown.test.ts} | 0 .../components/{dropdown.js => dropdown.ts} | 43 +- src/scripts/components/{index.js => index.ts} | 0 .../{input.test.js => input.test.ts} | 3 +- src/scripts/components/{input.js => input.ts} | 101 +- .../components/{list.test.js => list.test.ts} | 0 src/scripts/components/{list.js => list.ts} | 58 +- ...lement.test.js => wrapped-element.test.ts} | 0 ...{wrapped-element.js => wrapped-element.ts} | 34 +- src/scripts/components/wrapped-input.js | 38 - ...ed-input.test.js => wrapped-input.test.ts} | 30 +- src/scripts/components/wrapped-input.ts | 29 + ...-select.test.js => wrapped-select.test.ts} | 8 +- .../{wrapped-select.js => wrapped-select.ts} | 53 +- .../{constants.test.js => constants.test.ts} | 3 + src/scripts/{constants.js => constants.ts} | 34 +- src/scripts/interfaces.ts | 754 ++ src/scripts/lib/utils.js | 214 - .../lib/{utils.test.js => utils.test.ts} | 21 +- src/scripts/lib/utils.ts | 180 + src/scripts/reducers/choices.js | 110 - .../{choices.test.js => choices.test.ts} | 75 +- src/scripts/reducers/choices.ts | 127 + src/scripts/reducers/general.js | 19 - src/scripts/reducers/groups.js | 25 - .../{groups.test.js => groups.test.ts} | 4 +- src/scripts/reducers/groups.ts | 36 + .../reducers/{index.test.js => index.test.ts} | 14 +- src/scripts/reducers/{index.js => index.ts} | 15 +- src/scripts/reducers/items.js | 58 - .../reducers/{items.test.js => items.test.ts} | 7 +- src/scripts/reducers/items.ts | 73 + .../{general.test.js => loading.test.ts} | 10 +- src/scripts/reducers/loading.ts | 23 + .../store/{store.test.js => store.test.ts} | 2 +- src/scripts/store/{store.js => store.ts} | 70 +- .../{templates.test.js => templates.test.ts} | 6 +- src/scripts/{templates.js => templates.ts} | 182 +- tsconfig.json | 18 + types/index.d.ts | 1041 --- webpack.config.base.js | 23 +- 73 files changed, 6618 insertions(+), 6635 deletions(-) mode change 100644 => 100755 .github/actions-scripts/__snapshots__/firefox-darwin.png delete mode 100644 .github/workflows/types.yml rename cypress/integration/{select-multiple.spec.js => select-multiple.spec.ts} (100%) rename cypress/integration/{select-one.spec.js => select-one.spec.ts} (100%) rename cypress/integration/{text.spec.js => text.spec.ts} (100%) delete mode 100644 src/scripts/actions/choices.js rename src/scripts/actions/{choices.test.js => choices.test.ts} (80%) create mode 100644 src/scripts/actions/choices.ts delete mode 100644 src/scripts/actions/groups.js rename src/scripts/actions/{groups.test.js => groups.test.ts} (86%) create mode 100644 src/scripts/actions/groups.ts delete mode 100644 src/scripts/actions/items.js rename src/scripts/actions/{items.test.js => items.test.ts} (80%) create mode 100644 src/scripts/actions/items.ts delete mode 100644 src/scripts/actions/misc.js rename src/scripts/actions/{misc.test.js => misc.test.ts} (73%) create mode 100644 src/scripts/actions/misc.ts rename src/scripts/{choices.test.js => choices.test.ts} (91%) rename src/scripts/{choices.js => choices.ts} (80%) rename src/scripts/components/{container.test.js => container.test.ts} (98%) rename src/scripts/components/{container.js => container.ts} (70%) rename src/scripts/components/{dropdown.test.js => dropdown.test.ts} (100%) rename src/scripts/components/{dropdown.js => dropdown.ts} (55%) rename src/scripts/components/{index.js => index.ts} (100%) rename src/scripts/components/{input.test.js => input.test.ts} (99%) rename src/scripts/components/{input.js => input.ts} (60%) rename src/scripts/components/{list.test.js => list.test.ts} (100%) rename src/scripts/components/{list.js => list.ts} (67%) rename src/scripts/components/{wrapped-element.test.js => wrapped-element.test.ts} (100%) rename src/scripts/components/{wrapped-element.js => wrapped-element.ts} (82%) delete mode 100644 src/scripts/components/wrapped-input.js rename src/scripts/components/{wrapped-input.test.js => wrapped-input.test.ts} (72%) create mode 100644 src/scripts/components/wrapped-input.ts rename src/scripts/components/{wrapped-select.test.js => wrapped-select.test.ts} (95%) rename src/scripts/components/{wrapped-select.js => wrapped-select.ts} (56%) rename src/scripts/{constants.test.js => constants.test.ts} (98%) rename src/scripts/{constants.js => constants.ts} (81%) create mode 100644 src/scripts/interfaces.ts delete mode 100644 src/scripts/lib/utils.js rename src/scripts/lib/{utils.test.js => utils.test.ts} (92%) create mode 100644 src/scripts/lib/utils.ts delete mode 100644 src/scripts/reducers/choices.js rename src/scripts/reducers/{choices.test.js => choices.test.ts} (81%) create mode 100644 src/scripts/reducers/choices.ts delete mode 100644 src/scripts/reducers/general.js delete mode 100644 src/scripts/reducers/groups.js rename src/scripts/reducers/{groups.test.js => groups.test.ts} (94%) create mode 100644 src/scripts/reducers/groups.ts rename src/scripts/reducers/{index.test.js => index.test.ts} (75%) rename src/scripts/reducers/{index.js => index.ts} (75%) delete mode 100644 src/scripts/reducers/items.js rename src/scripts/reducers/{items.test.js => items.test.ts} (95%) create mode 100644 src/scripts/reducers/items.ts rename src/scripts/reducers/{general.test.js => loading.test.ts} (62%) create mode 100644 src/scripts/reducers/loading.ts rename src/scripts/store/{store.test.js => store.test.ts} (99%) rename src/scripts/store/{store.js => store.ts} (58%) rename src/scripts/{templates.test.js => templates.test.ts} (99%) rename src/scripts/{templates.js => templates.ts} (65%) create mode 100644 tsconfig.json delete mode 100644 types/index.d.ts diff --git a/.eslintrc.json b/.eslintrc.json index ad9bb6ac5..916e411ad 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,16 +1,21 @@ { - "parserOptions": { - "ecmaVersion": 2020 - }, + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint", "prettier", "sort-class-members"], "extends": [ "airbnb-base", "plugin:prettier/recommended", - "plugin:compat/recommended" + "plugin:compat/recommended", + "plugin:@typescript-eslint/recommended" ], - "plugins": ["prettier", "sort-class-members"], "env": { "es6": true, - "browser": true + "browser": true, + "mocha": true, + "cypress/globals": true + }, + "parserOptions": { + "project": "./tsconfig.json", + "ecmaVersion": 2020 }, "rules": { "import/prefer-default-export": "off", @@ -54,18 +59,23 @@ ], "accessorPairPositioning": "getThenSet" } - ] + ], + "lines-between-class-members": "off", + "@typescript-eslint/no-namespace": "off" }, "overrides": [ { - "files": ["*.test.js"], + "files": ["*.test.ts"], "env": { "mocha": true }, "rules": { "no-restricted-syntax": "off", "compat/compat": "off", - "no-new": "off" + "no-new": "off", + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off" } }, { @@ -92,6 +102,11 @@ "Element.prototype.classList", "Element.prototype.closest", "Element.prototype.dataset" - ] + ], + "import/resolver": { + "node": { + "extensions": [".js", ".ts"] + } + } } } diff --git a/.github/actions-scripts/__snapshots__/firefox-darwin.png b/.github/actions-scripts/__snapshots__/firefox-darwin.png old mode 100644 new mode 100755 index ca328d13e52c3f81785d05c8016e57dbb4e4c74d..34c05c95456a4b596cbdcfab067a60289d4bd589 GIT binary patch literal 45377 zcmeFZ2{hJi+dg_ng^H90WK3yLsAL{WA(Ccf9#b-gTgXt!kZ2+erb6nGd5n;dP=rh& zAxUmi=6N5NdcXaB-`abxz1Fwh^fV9?XAbF(b>MOF_M>p;=|shdq0eKFIDFF7Re$FXQ)SRb=c5s`_xpiN;st zJ4 zxAxS=@q53?aq8t7Q@Hq@(VMj~)x5&X`Q}dDOrzPU(Y&!pDF?wIZW-}!LmcFZx00t1 zluVIv`nBfr<;ziZmdABG)F2=)&l9H+es^?wdZ1v{>eW}30@jrk_rJZq+|SR?D=u!!yTA>;yq+`1e~tIl zZi$JDGo6_j8XO%hd3NOS;75tJLniklB7)eqXnboh^ep-O;^ffpuEU!)Z{`;fsft~A z#AjKwuX)WquP%-Ew+bJbZ+>@GwD>cTV>*P%fO|%RRS0-o*9f^MNf@#sBMO%05 z2=;Gn&2>(3>eXx#o0*xh8~t_qb3?;{=<07Xlfx$p?%!Xh{y=oUAXQ#o{`o)u-1SNE z4;Sj(Ln16q@^0>&T6oFA@*Ab3Y413ss=8lUdFhUI%Z2rGywBG@D>O-YUtDRD67}ra zQmrI|l0^MnQHx40Mt<@(vYvBXij1#4=MBVfF>ermUu-k^yDN20JjKEMG6_-p&8=NF zd?zeYNKgsr(UvV+7S+&OT~l-Q`ttQ1-wLlKYkxNx$C~r=@llqq7cM@kFzYSj+`liW zp%Y6of!*Zy-go3kpcPLmuZMP?>)sP5PnyqKn{<~?kg)Hxqz-(QGkTGxjR?+NB}`37 zNchw{D(UcJg{++1@#DuA1tE6A2u}N^{{P;0hJ10V~Z!S2##ALs|KDYek3DIvZE2s;bz+pueXMWk=4C3=YJ>dWL z45D#7MMy-%x5-NLvWgKo>34Y2npWH^6Z@MBC2s!zNW+ZC-Mdqdk)*svMiF0SGyTb< z0?6rpkN?j%{s&7UakV%xKcA;6QbEPkw9_I|g4D^dXi~nEy&C0QelI>5rxEheZK$JU ztB{aG#mI#V7wkKVnO=L$e%V>OA#s>iHaXmlqjPV|Q9aH%T2tEAty|eUVmD&T?jB#L zqk1&tpT!wEff*MXI=*CFxHL1d>*B?WtUNqHQBi!SPM>CW?ZryBMI+3vX!XiA zm$6L}5?O{Oadnm{7rV;&4Yp&~1vtp2lYsf>pJk?{K_yy-?&b{xs88DYm!)i4bC%pI z^TrA5KqfvhKQnXrLzooX=FOXH1`RzXH;ZvH)6vn90`aXj?&w5cR$EN`mCn*zr8m|J zQ7FhY6aBdZqV^pJ7cXAS$IrhAsr%lu0;57YE=fDeix)2jhlci>nQh{cz2t?LKB%O$ zAEio8j(c4|iA|H+<2V$AOzURWb?bbX*aZ3T4bAwql$hNH*>;PNa6X+A^9u~T-ejeG z_N<`dfdi|!xdXATizxCQGk)ZmJ4yl$sjGWy#+Rg@uO)e9r^?Y3q3zq>BJCq{&>I>W zZW0uHYb73y55LEv(%aXUVPQomz1<)O;WADp9^PG zWaP-(+9$Xs*LVjt%ck`zD_z0d)P=~Rkl^5bj90KOO24STK5t{>KX&SrcTf<^PenIsx6=U%$YNcEG#~ek-V0c zmK4hV{rd;U#*|G=HhRnqsbDGJR8_5X&Et9a@S%2&<2G+rN%yJr*56~|Rpu4<3mY2N z;~R|`7uF-g-?@8tqfoSFypoO%XQoZ-vJkcFsIKad#hHCsxD`*GTI0=nX?kqub|E2t zZ!hKO{Ra;&PEAWA3xN97`Hq=#F$JM{O(U)-+rE<&Y%>dsBD}-l`#VS)9iuf}OEE3o zxO?|%w=vrOlPB4hEhlLirFvy_wI5kKF-`<^PL-1eBfr_m(**#R(#R30j6@$9+171W zS)7Q%smQo#%a-W0G&aiZd-tvdtQVn6F4#FmYudxepX@wfaNbo(Nr_d_s3B3Gl2dF0 zY;w-lmVpxU>={B>e^q=!!gW@@Oa@)3T8VyqX-nXm$Yitx#HoP-&p8Rc&RqJn& z%Jm2j4@dpGQfKM3ol0R{zn+0V$Hv`b_@e}t(Pxva+~x}L{)$i$^WiW_`;F^%Xj7sN z(^juu9pk~UlE<5s4(OTUg~(y#AHZ$-8%SKksrjPrNBG zv2Z()m6n!fXkxL!v+keMjmgH>c6iQB#ue=r)=6i4^5jX)06ur4zdn_kX_c>=X>~;{ z_G+DFh`;{n(`(tXFsqb;RpZ>0qVQ~ z*vA9fT-Qm^yyWI)S`(uR;96|xIeY!nr(>!#02T@k@0ql-?RD{5TSY`lk*Jp>XaTD=x+IA;8~j-<5F8wQXypM}x3Lv(zLg|)%j)XruyS#&xK`+e z2zmPaIpeNfy9UR{RSqAn@G7Y+Dxyb=ps1v@1StTh5-6AQs*zjTkx`K`OxkG~uYyN$ zS4AlBI1}F2$7d;JpPilP)2B}ds}$y=5)zJB(ogm#72q%Ewj7P{X4ThX3Qzb?pZh}z z3x!9)<0d&p`P)O5VO>?+-Sfmgpwz`0D=I49zJFiPpPLdU<*?Ml!=pxQ#8u9wEuT3m zUakcR8`UsI^&W+Svmwg9qGk|h(8a~2W@f1nHR#7AQpFjmEk_=0%JAIqyfNWS+S2y+ z_8PHJVf`CFew@X*MvhQ&bW9i1L%k8VYNYTlzAs|PLTSu(7C|q9q>K$V%FwxU|Nhbp z9cKQ;?{0EjLpOG)qrO3U3(BrE%5LzAlElbHo5z{y=_AtW=H})}%p=WTz8urhx$*n= zMc*P|+oxEE`h?8Pb%OU)T~ljYB_G8#S^1QiaeltGR6J$6Cob|60yN9EO*(JM7yKzT zb$8R@$cplEGdk-GoxXZcUEK|8YHC~_zdklCyl>6|5ED2=^Xqk7PjK3IhbJV9Zly-w z{qjYJfZ~QELoL_ifGacW|9*>f<@Ri9xeL850eTYu&TrVT2k5J)FsKW%-3SOE?JAo3 zrPIt8wf-QL|NT0C<*-KmSEHP_A|J-InRk00==wo_>G;g5kRLiPrb`=akDr|6F&7CK zntJb2;w-+f9={g)I}O^pff=?q56_h+yWBoEc)WQ$oHca0jA~|`=eIx+t!2^U_bl(z#sk~ccgibUiK)9NZW(scCJed zEaq)n&P*$8ZAoQT5n1C(tqS&AI=4JnX1Fy!nlLRNx)-Q~j^f13M4kbK z&!)EZ1MjD$=sd%R_DyQ6{Z+5@gm!D~#6;raAGF?cyB~g<84}>!!1<}B=2ty;={d14 z2}0o-zTdx_wd6Y6zISf<^~;hyP#N8skO%9+Cp@~)o^GM83s^EbdSt@5LMg0DJIFU~ z&NJBT!Y4cJmBCX_+9M^`%nh+;Md;Y|8wjK()qnferIyt%sT{ou=>jk&As9u40VRQb z&)F3QuP^P2k?BX0K`OJh?z>&-O9Jx@p@gu1dmALa%?4;?+lj87Hc9$6_O)m0t3ie5 z`Rz=@3CFnPGkJT*zZO*rh}iwg;+&bwX1Nv7@XD!l7iShx0;1qSCQrtd7#$ zCr+F=rmr7}zc4Nj-o~oK;=>3hahnk=kqRO6W)>soT{={+Mp zwf8-ozcw>kCMnm=@zrpe=Vur*-@3Zcd6E8SbDr6rJ2?ivwy4xSwyGtQ( za|cyaOz}rXW@e>x=e8Q;UuFs@xr%D?rnHm+jepH06s~s@L!H%F;AoXw9Y6#p3M{H3 z_A_2VOwCMoM*{PKf(rjUkB8SzHDlVb?&}S6Y`nGoCg<6Zza-HWlZH>ne50E+1m>Hq zpO;VbinUA?i0Xg!{-^eA`kI!aqCM5`WsKg`y1jayl%P1&w&apWmqk`T5b`Q85Gp}j zcT(NPZGgm$z-uLI?~wGEkwhbO8)(}cR0ONy8?#@og%{79;Xq54>^@CJp$CSz#jDVP zhew#GB@M?B{_$qMV`%P`Lw^aTeJO6kzgQ9nBE1Fn2zPwVoZZ(YMyWM&K=!vE`+&au&7{OU)OqGL<)vP|7_*Sh`wuKK*r66dM&(~|@25;k1a{+9Eh<>hKK zQ@u%`@s!lmmId+1-Pj;ubI2q#JUozN&)E@aPr_(^`1t{W#<0U_-EzVf(v=8X%!^#| zsAm|cJn|;~+REdPre%2abplJBJ(R8W8?39eV$X_+XGG=yL)W#E_en<2JzeM5UyJ)o z0@edXfoz$aoFq>m?>^=HH6$cN(&hIi9bMg3oSe&|tGAA#`?_1)UlV)ylV!#^E34qC zwgOMy9Hkeb@ttTvD3h#z!3Gb6P}u}{xmyePc8 z`7odNKA@amzkU&5nVv32Xrh-dSAaRaH&+q@G{tzOyDAd=q3RGl6Csaf|I)8+F4JrJ z&vKW{1{8F5`u>*p1+G@YTkGhy&R;cAF(KqBIvy@yCS@w6HNC5~LJ8Ye|kB>=Zm=vCGqVtvlI(U{qC%I@!0J2v{@|zI4q1QutOhh~ASF6FOh{=pUb+fj1O)~0D>w`NZORG! z;W$HDbddIhdTH?{zZ>1S3izq`$p87q|7=M*RzuKvo{(_bbr+g??FCxP=T+m!aG3_1 ze9H_@$OOghMZVK}6F>?TRa9K?yPxiS_1d+e;VK2svB@B+eD~Og^75-1alIK|EzHgN zckH00?O47D&D1rpA!AbN5D1_M2n$mi=aUNNK(GL7A4u`L_Vme_pUEe1dC+*pW~MJDSxIQ(;+2h?7nkZC6BG4U_1j8qu- zogTb87yQdc1%>&vD(Cd!SD6+#$LK3lQ&R~|?OSw=CNC1LPqV6+`|)u@?E2WY6(Nn2 z6BFNx{n^}EqQ9q5LEdSC-2VzC&vM+#HFZfkEQhf{JHPYA;wIrrj5NGUFB;D%chgC z4kTB$`*h~#symEOMEC^-mqMa=Anz`Xr$isYM9FsOW?8Y69^E0|W1m8!+uH&2&_J$kfIwOd{}JP^0${8KTn zqJ!m{XqzzS)`EldgHEWEBDu#wEoB76#8_8fBNulm2_HWF#k4_hkkB>JN zWw#stainQ*>9s{#iF$W(PM?+}0t>5w<;gj9HtIqyfX`9!K$L_c zBhfcj;^R5OAJ6U~EAZ``(Zuw$06%}B#>*)khJ9M@8ix)gKOTE|CM{;P=_E}@M<*G| zg?gAI!3`jMO2I4Xug-NF@8^diXJBB!uHYey%`__-JzeTK-6!GO`yBz6y8HE|Av)|n z#JwWVxnW)|IoI?Cr%T`7ECQ2=^7P_loXN*Wd+FuZ)z#N;l#=4W241;xMf>%ol#^#J z&(Fz`P*+e;0F@wsr~$!no4HoYg$u$~S5;YAAw)SaL!#$vHj!Edw(3OF>FsOLa3h!= zJ$}qB>^5$iAXHj{M-~tkW+B)RZTYv}-T;8KFjYH@vFPff57IDvCWEdK)t2F4K2LMLx`w9E}r-jC!vx9p~Hv zZdsA>`}gmMTJPSwmts_S)u7^2UaJZy45+1cBR`L~WZTm}7Pr32`t&H1?yHNN5qpHr z)GP4dZu6Ml>CJjK-3*xsr{SL95k)FhoN(#f+{}T>wHBuHF)^ENZjfLB{CoB4)q}kk z){VI4Io@?0{IA3T zT)9%k(NU~2LN0(tv%1N#XDJAn2eK}^S-7PaM^`i2+S(#=ly$^CJw3_$VMXJPM*38r zD+X!=g1Q0H#kaY(Y0TL>P5RieV_pfmSqKK(f?3VX*4%p`Axn69dA(x=bAtZm1@Q41 z%VCa?cfUbcS_A?fAE2KR*kj|yjYcWj6w0bqtH`+!YXz19M1`85)`|yQj;_Gha}!n} z>ck5(I{fIjzCOaPzdCicnW~#*6K?eda29E8?b@|04f_EApP!#U0T#p#%CKs&-l|-%TZHmnFJe#(i2><6T*jFqnbcWV zS2s8~c)LC!aDJv+p)tov$V-@i?DucQ6DL*^m&g`%I7(pSMyLS+2yWA_uU!bY?&Oql zxT|LyH8FS|qAQqYV8Lv=b~i(KE1o1MT+y!w15gDk{=}MU2gB2do?3q4QlJq2XEAtl9K&9-2^% z-H{_l(2wGP1P2zlj#@&cSadsJAJT8SP6BPyGD_(!USC!^5ySk&6jYRx((O)E>f5$9 zUVD3c=-7AOF)=fjpum2)z>&jiWnyaDm#B;YeAeJO2w+S41`x6@-HeQ)9P5OIgqABZ zw&b~K{hIG6ShCU1GkcsMVl0c z(Z*bIbye%?&zIA0nv=87V99nBs&5h}mB!30M*Tf$6GPNC?;Ia*^CS)dwOKYcwpAeA z^9)3sbaScJNm-X4J$e*Xea@`69c|&@$jHG12d-6^^Y}IPwH5Gk1}alWe<3rYcf5N! zJC$Whh`MCS5~@>PTH49gycQ;*ckb{zrRgN#VGkWXe6O6Q#yueXkXQcl?a_HOSShG~ zO|Oqe$THyoqx~prPnF79+uUY)=ZFu?F0pe^!x4?tirTyx90f_opSM3Jq@=7$JNxnO z+(8+kqeoL5#9t?9^tZe^DXxzAhL#oEme_KmZyU98Otm?Zt2t6@1EIwuBO@JWdJfa9 zMqaN4s4RsUu)G z!4rFaxqkin36gR67jtrQnr*b3pK}L}Gog3%4@Rb%OW>BY+xNxgu$mgF|G-r}->3Uq zWgusrIn#83!_^>VcyRCybh$yqw1I8gn6kNftmEmk5ceoKEJmDx?`*WO9pQ{_ztPSp zCN)&lp9Y>F^%BJV;i8Q6bR#exXaE^ktO%aV7%#TWc$%Hfj?>Gkcu1=Z97W$IGX#68 z3uTN!6}j;8{OmY&SXEuUIMmRCX`e7P(P};-TZch!ZEk+%elVu!j%XTu`?RO}Xv35g1s4cDL_Y9@;nTmvorOC#RB1D0m9iNEYKv>w-u761MFH?hFr)Z)ihLc`eWeLFz^ z=zdaYHd%-Rg+#|k$xvyxeH_x|roSwl=@mcPnVQ8R*Ii?Mn;6dWX|qA5^k;N>;0@C( zs-B`fM@j*X1=?3JFyI07e{$l*)aw!tL@#912OrO-XJrZS@$Fm9s8u1$nzDTP@_UJn zQg7>=J{hJU3G93t9o^^SIr?j@_s{tvw&CuoQ2iwinxz{yY}iGmX}{n$Q&hZ~&SPcX zzcbJOhCoX1Knety6?n?z$5a~|VQ*Hfm$Rg8TGLIS0W|rH{LoTv=DCf(wamyl710mN z&0wZ8cFQ#=O*S0Tik>kw(=D?|w% zg04`xWNLNp>({bS{C?F50dXVQzILBphcZY_(Mj07iiqoc-kn=I0B8tu>B+(o*U(_8 zPcjs7x^?5m+YlkGWYhq-uyU(*OS)#Au&~$y67&k5P%*lC#5FfLHFfpZuU|ooD5|RZ zJDyRHmDRIsk!MXgeE2X)q(x4rPo5ND*3#7tfP>(}qrJ-&9ruc5+0RZnL97iA-`VN` z-RvrB|Ek>nm41yVAccT1l=mf-A$@IZTD@K}#g){jKV2?Tat=mKj~#`I3SYt<6k6bF z`ky(Q^O4(u9a9@-3zhM3K${S~e4u{uVcWJB#G6yoTdlPCT-kC7ysdq9gxY#UkPSFd z-+&>g-u%Lac-p~ceHxY4$e-(OO;97NiJeKe8aIO%gm3;)4qr)R2=<;BKk6rtQt|%1-F9kgoHOhj0Dk8(z8v=) z)i^J4eD@le^*;~OaD+fNSzGV?THv`7|IExy52P9=>b<(^)LZ`q1xAn{s8p()xK-9j zj>5wS=#fCSk_=f>tAU(Ipby-6-6JV;{S1$RLYiAWR1Y8S9nB!137tGuDJeE~33m3{ zLImC9mZ|Tr{{>Rd9%60JczKA>DwslGa@(|N6W@*<>e5@STe1QBR;3Qj&kP@JQneK| zk+@L5qe--{uMd#vjz&&vR-90C!Is4OMb3WQ6wloz1$dG^OKQmb>}blb=1c4ZoC#ZAp>ztQ;WMCp~pzUVqjdo;)7 z_kAJ}Ncz($ckiw)E-f9O^0v1%X^1qf_F>+DW4S~37Dd$dtE{h<$X>D!Qnsb^O;QqW=gV9AsHY%PuidE4v@@IGVmg%&Wj;*N! zhiRla>kGN!IqwxTww?nF(?W?2GB_3%8F?!>cny+Vxy{N%-7F2)E}#l}lpgW`+$&t= zl^%wLfqD;!*GhVda(|0Q4hV@&)b!norm`1NQ53XFfFi*xcj39aUQnQL_3BkFwFAz8 zV(UZKFNk z*IAaJd)(#RulL$@bQuWq_vrN=>@p=e1qXr-WGNg1luu5hpGr$rq3ng zzX3Zd?%U@za@#-c_sAL3JQC*0~5itYcM~~NSqUXhf6a9W}Zrb*gmR9GREA&9&A>3&MJRx(DPYu?buxnUu zJ7ng+Y&Yz~%RgIY)HYWNaL2YDEIUTidsDro#G940oH{y>XqxZPNfM)$5v?($rV?;? zd#lf{ZFHH=hD~4YA{Fj9lUm%rT@zpm1%{Q4O~5HF&m(GDXrpWNbRk$~Q&2LgsUITb zHUQ^2NZPo$$=tnrH^$f<{WX%QRdXgYI`FP8=0h|})a%z=1cbuH!AgfbUPQbWT`QoR z2}Snxlo3>QHjM+7JoNsNq=sl51t1V?d40K2u`pa-=!$Db4jedOepG&}C89F3KtS&W zegA4895t`LTC+^2{^uW$T)r%ePyEv3=)>J5?K?}q0^OCsAh7h*56I#`C~6gxCF>2p z0rs#ybxxtrvC;SO_I^KB8-fV_bw?ZId??GjQ@F#M+hdJD`@r9|sE zdi3adeGOQdsS@YXVc$XX*veXs%FuoCAGw~NKfTJ#re2B!S)Dugra8;@xRzFT&7hN$ zljuYBRL}2l%Q(Acd|C_6YU*;&%RT@6^N(3)Rz}9?q^O?|Sgyla%RioVD7c?g(9AnA z;}rGJKbG&Cn_wmT*spoaS#O&ITE?WKisRRXnm;s2!&G&$Aj=d)E$vrauALUQCvaLp zq_;BzipcEOQUzG_YHDf>49n<73S=vNY%g3m0NY#AzzYWnaqQNYHTiDii4C);kZe$q zX7j()!xRlM6rsP;M#|vU;$Si@uyu{h?%zGa)M6)02vmc3MN$NdG(KgmtLUdk^PB!_ zW8J;tNh9;koE&NFog$<0xT>h4AJ>i6Pw23TGH=HI8y$|X$XY@7+OJ=Ou3n|{ZM(DC z`~CTI=YD3m@8OXAw^%8%4?>sr{J4J8(m!PN6O(QbY=FzBtIdCk2-rWIz7{?3mL(W1 zSd)aT+jM_tx6$-iUyX0;`W@Qe91D`&ZL=f{@?4kjFGdSIDYec}8Oa2hRB)Q*nGjq- zU)(!Wr=6t~w%~YWI}b!OKWi9ne%4xWp!a8P+<`))?r5{{i|Z5fcQopcRyGY~cVC9z zCU9`jj**|1?`hSE5|nD)*gWp1BJ*bf#+%svD_VG4wh^m5eEZ%bZrOhNXzu#I<_Rgq z*KL%SH>m&EG=3yRmTCRkwNPaxY`>0Nc#S<1K!Z?kjjrx}Rh4sR%`2bWl1gM1*x_pe zEVoh1MO%s!EoLKRoQ08ghlo);JzbK^;xEZQ^4Mg&ZT*c_K6d}RVyi=6Da=Ut=^sVG zBq?p=f@`!AE%uNni}aL;@jDekFLYC zSz#0R6272|7k5LRASlVYIkRl;^v6gAUU-WdUpZ_cq9V!MZ~~q_eLB@e>am8U<#wW_ zqJt2SmgWSXMTiK{>jj}rtMFTX{{B~P-CFtVXk^gKmpjh5v1oO-u~~G{c|or>0jfjE z_eQa=Ptf&+GwB5}{NNz4u3EJS5D2c-a+|8A;{Nw{1V9;pnxbY~K{(Kcb`Q#uSZS>Q z-av0ryg(d*wB8qc0;X9I(^0QpomriW&?W9pFw1`E36R{(YCVQJmY_bjcX#^|DoxUL z%45I`53tZ@vckf+P!SftDcI6Z0Q#<7tH5u88hs1M z{;9tH$3+(|vPm!XUaclD1{9jQ`c22?pQu=VH*bQkCDj-tZp_fsm$8cbI}d1UvqRXY zzgEZ>-m*?u&#$FL?>uLw^s|QERS;pJ9#<^>jZ6pb7@(sVpcO_0QE>lYWmSvNT*t)5 z@=Hpx6N_^2wv%R8SB~tY0$||hQ(e!TI<*+4HW(WZwZiCUB+5QGGwA@c9{G%;y$zHp z5rPrGx-aOlIAl;ra$Wbr@hN+Gf>mK=kX}&z0R^ol0A0`hOXHJ`^|i1uo7ZmLw(Uxx zk=<6?183s&I7-hbB0XaFw> zIwHaV%Gd)bhqDFun^uA3RAhXqo5eyfqN}SkF>Jt-d8jCRH@KJ+i4grjV}j@J#NJ6> z8svKy#2o---r!LsBppH?gZc~uZX~J(su)f~jQMgF7SzjwNfl!#7ie78q4K}~_^|{> zH>$RWoC&mMK*#)h_OO8fWnv-(qc=u4G&R=|m;&krL^^nXKAc4G1JL~`UPPw>_`^_3 zwqws~YTuV7sEa*(U+U|Z@-HS>Y+_=EYnh}WniU;1^j&uVFa=F z41)uzm!KLz;@d22@Y;_w`gm15LXBi)d8Z$onEV?z(t%LVzC5`btRe%*KZ4ot$Hvz# zd%Rf>9XV2hMwTMkHU=nt0C)vh5N(kCc)vd7H=7k`TC~8Rr|f8ryTat$*6lg_!3&uK zoY0~fx6zIGNT|}KI2tLKs)6^myONtiT$%Xf|3%VJ6dXh&xAIqSubV!@Up{|ida%6+ zJT_=AcyBGL-og*78SnJTnuhcW3#UIm&qfiEl|q^?y;v0m^RT*vSpl^zte5QtPXv(3 z42FBC!#y;bw?R@VK9>2gl-)N9$Vu3yjk8w#>qsD)pWqa)UAqL9Z}d7FAs7J>O`Wt5 zSRb&KPsr^W6+_k$r#X73gwD~gZj<+8^gbC={#Ka}E-{4cqeXK1l z72)b9)+3Sp2|761aTM?T0+Dwv$%%&o=qYN`A_<`Z5pMFwg)92_W1euEo${aFd&}p# zy8Y4Bd^>j@sh@=_2GpWV7nzS~a#{e;qmy55^Gw4NVV5hLe zn;uef=9ri955m(C8v1GrkrdGUvaP0YDR^)x&bS4XG|h0gTfTyQn{R_H0O7>1_#_Gi z9yItxCotF+I|oPvf$z0rS80fOz#qDr_GSWOPGGF?>@OZi>!v6&s&4m6jAz6v*8Gc2 zKEr_|@|R6kT`dFqR@plvj+>2}1qI7}UR~^3=FM6Y!fDaf<6mb9*@*#c?k1-TXbgz( z*hl+`(KzdslKE?!XBS_%H`GtpQd2_*GCZV^dKY}f?VV{FareeTkpv(5QS_g7m7m_c z0iaw?P$O*XtcJS!K|MV#C|sYd5j{0K(+d4saazG*7#KvnoQB{_OvC{tbW!md=m&Jg z)iIf{V(GOam~hh=ph5zUhWgwJ3c5dA{j2g=-8QXKd(C__S48Rqq&P7TQ1Jf#$Fb5e z(7AVxSfD&W=e!rx+S(hQX4<^(R;gS!{fd4~D?_I_{5)}V4Ru@>Q-?(eY1-?yt^esXXmV`lenh}Qd&P6#17PHxY0uDKSs#8 zAt%vzhHcNEzr&JUc^`TvGdfx-gH2St@o(6|W7~p?HKMB<1LID-fWEXG-6E0-w4?Xs z6Wd56VVs0y_;~jCax z@IJ!new;($2a#utLB&VoRjq`^l!x{`qtfTkriRx`~rROKCSSdO~O|5bF&Xb zjjt9>28VrM@yHE6^S3Mxzp0k@;g}%o|MCpd>5<%SPnKjT;rEUnQYpAziV=*%mdWHfdgETarRCV25~^&G4L;botKAkoU7;& zrFSF&I!Ys)h%`yoIlB6dWrhjBDfB~N$!$zDpB15?tO%p;dt9OS*+cq4uqbT=gEHeBHFsh~eR`>!3D8Edv>eLJUiN=3!@rMoxK z0zScUeSPcoy3@@v6?0$1fejG)GapkiW2!EA?{LLgnSJB3izxe0c_6WvnfKpn+$$jg zB^pIbdWXf~!vUM9tsAOWuG1~9=>{-<$ zvle>V%?DB($P5M|pjDv^@qNJ0QzY8$ilr91W9Pgv6j476`18 zb9pZt1y$f&1Iu$*tuTvH&+@Fmgn@!Rdj~U)`7BgHRt$HgHO({?L&Q3|+I|E0Gpb2L zUTdKBnd_{=p>bQlXJa7gPOECMk?5hPH799$O=DgTDb2~o^k}pCBG!drHs;j0o>W`AVV&ktu6azgEJ%)Ii zp~O`Aj2t)}r^gMfE^gBTw-VzSPu)F%{U%$yUvuyLR2OlSkw2r2W-t)O-MrLQwqJ_Z z=x4DokPzYtgU=aO4=^g;2@`;OrET^f4kdg!Cf)a=yM;gx_5$7(epE-RxXZAde-@6z zybJC)K;pe-kQB-(GCe(g;6Jmcitye-D=1uy4Z864E80xBYy8opJ;A67`b)I1_gK<{if!PB zsJU=x=~aQ0$7sdTMGQeykA`s__Asxcq@?ghw7BiC55fNjWGVuW%+3pT1EAHRQju9A zU;#W84&~fbuL%02Lt8F4`_Dls#>E7IhnsnRu66?)m%TJd0pY~Jr!WM+tqD>bSl)y1 zOXD(#n%Y{_l)hxd$4-OS>h)&c?CX5r%dw~+nez&F6aL!7)A z`P`J}+6|8F=g*(Fi~k=;+M*4BDMF>RD?GO#(vo_HOfyK{OK}aC>jnTZIk)DYJJxHC)zZwRE_~qe@K09s{ko!4{-+jVof!Q59AmBP- z7Wi|%EO{0eSHUyG{=Bh?=bs#CBa!CZ<oAMZjnI{ykS7m;Ytt?)1Ya5im( zN9K?5>VQtX@s-cX5&WQ5n7*xssbL-2IWABz&(R zSoIZ>brPOGe{MIy5=Q`|^kqsRa7@m}^Wu-g^Ld|V^^7oRp?!d57Cb9eWStX(dq_M4 zJ6Kv%Cm(%0>pNhz`=R=*u+~v9u1oWR+&VEZNbmZzM&FT2eVUqDV;tQVQG^CXN`K|- z&MM&O<2pKDdWCklcKph8zV74Wv%kPI9@(1sZS?h9qn-^*orf)5N$I+-0xKLNE76$7 zS#QtXbIQpws?#wJmU2yrFJHcJ$+Mf{0tX0%9^CZd+-VH1vU6cz-EKBIH#n&*7*knO zHtc9E31U_B*@Ndlci}=2Abtwurr7nqw>|!2rfm01h^`FiZjs*2t&cKsFfDKt^Q%+9 zQG}0Jf-~o+1k8C773EdBWB0~|xv=@oCMpodZ$DXhtK&2>rq&@ySY6z@g3`rrqe!0N z|Kl6-GI*#OiA@ZO*c(_qCyKf7dd9vBd|#J|GvJhm=f2BZ%gW2|%1hO5_{)O6e2lSj z!1^b3mgdc1wvJCpiW?bzg`1U_D#Q~aBFi~;0LQT^j!Y^~6;=)d{5;rW5i~Mgc#VNo zu`wa8s_YLz(IVd(0a$kX&R=`EyEwd)5N2+Nglu*{)VfNpBoP}8_(_-{q=EAC@?#K` z&?dt;3OVYq7Gj% zvCuM`otkai*SaS>E%9i4+LfT0aJY5(GWM~k9UBiG45WM7@m7#-)x)p$3LJxpB?zD>C>!}w_k2KEevQse*DR(UA2s`zpBn@=H5J1p_77v(HK_$_# zPwt;WEhV-ibZ&Oe&MK!)1&a+dCId*Z11;aYuXW}O6KX&-uVLkf55>3y=iASpjBqZZ z5m1->igahyn7j!3Q3)=CVZdzAEZ-%Z@%-5_U1*&UO-QTMaZ%Z~b#PErPfrgc$hP)# z+1Zkqv!@6saOMMRQph~*w;w;2%E-t-p#?9^Lm8|+s-QYdFQ5>SM@GsA+5jR0Jse-7 zjhmaBv%7flEg)aAanaH>>(;5&CNSlu+eiw`U>|2FP!OlB=>5^( zAGBooI>N_anwxngmWLt~5fO3D+L|c2c&cQxven4!(OnoWDFVYMEG)c0;?mQvVq-I1 zP3@Fp@Ve7&2-r&q`@ZX=pinWoQR0Mn>hZ3zew+OGt+Dg;m^BmI22rD1C{&JR-<>jr*=6wIG|yLV;q9$H3S_|8{;&>Q!mXY zCJIN(-LLo7cXpO&X>BEnz8)@tfO)$RB(o?4Zv-$M<Tqoo*4KT*WaB8tC5)42g z5_ooY_8auba4F+oav{w(0QEIgwuP-|gIPBP?u3|_7(Q|Fb%}|I+Hkr4*ZpW{Xz+Cz z{=pIM7>PwgD;kxRwVs}yo(y}J8NWwB9CGUu+&~4i@l8wHgC!39F^M(}^rtm7thKe( z&e5?LStQWrDB4L@I_wp_(d?7PYTS2qfN_`2si3p8sYfzvWTaDJS89hoknT&aN65^~ z#59DDL6UDm!cMe)f%OgGA~6ZAnN<3-bOl_TuU&>k#l+0SDpNc|hmr+pD7`T5-V_$@ z>Fc|mn~Ss8xaeDCG6>iEKdO62?d;oC+ z#Ol-4piN(bDR+I=vLGdOnVVj6Z=uOY{rFU$BB!?SE=AA#hyjSB%{V`HR7ERk%}x42V(Q5F#qiIT~)s;J1}uy1n+s!S?aqQMpI__z9ozwJTUy zObV|qA?_`doc8bEH-($EVVV-bOs>~>9v^>?rEjNUzFyA0w~vzd(DqxNKffP-cyD&8 zqyZpW!AAiP;X-^ANKZkK2m@&^x~$iTwy3bIj0s}cy6!W1t{Len=TnWl(PCUNAZ+O3BR@P@`iZ_1w(gC$ac^H(l2@c}fl?O1_|K{Dhci&&j z78&ao@w~gmBxCgb(-T%Y7(xVVSNKwx*P9|r!M1wCC(n6=m!16$y5c)<0>NH2(>6aF zshW&(^N8yN*RASXFRIU}KmEk|*>t794%V`{z*9j_U%$Gc!NPGGX*|{`Q;>#nVK3ap z_VC~=>R?QA+L+XvWC8CFjtdbe#+XZJQ?!DRL6v#G`>KdS`)p5gapay=xd?9`o_l#!5v<*3%GAR7CqZ# zSTH9y_qd+kO^jENp^_6TJgge?@FK9u`d+(6Pt*?#9^e`mPNNzqPq^NqdSX zwXkr1&lvtg`;u{(vkGHLaE<%oxv{V~srNqh{QC74=r4Bq@^oJol+s;OMtwa!g^&qE zVVJ{-{qe&GSSQd;;|z8M?a=mc-)ARY48p;*L0@$;#hBryo|Dv+tOWmJD=RBIAf#xa z9i@r3xF|>SrS>9{D^2`Te3ftsOENT`^DQuWV~+fa*euMAWc-0g3!9G{^^~=0G>U(g za&d7H?~gIwY^MVxEI|c#aUP%Kv>5&ts3p5VX#eyuq{R+& z?8?R#A04~^`yTdeZ5322Prm05A|A4F(@)X!y1LETTDD;2INZNIGR1b(Xl-XUuw-|=qhBQ zMWW`kVN8%}ckzmxk^;wjl4D`{>EABk`;&jc=fZts>Ug9TGCm8_nr4dRXzyC+2sW~* zWib^65wPmb`fves;`HN-_X`-wbYZ0PpddLe+(ip7yZedbV^JCbsfvtunEv3~f6H4u zJHBvA(3znC&CzZhA>EAsae|9$`Do;(lhg5<9CBlDAc5y$q!4V#N(Ysa@~N(lOawoA z#Ce_r;>WdHx2h~Q?7;{8z4cgx>91nPj&DPCZYgLoB$TMJ;!-ym+~6&)=A{u*|;v*|Qm zkBLP94R%!OJDh}1wY9@#4WU@=hy7#|bGLC(akc@wq@-M0CRg~HL`O%X^}2#mfYeWJ zAi_TsFJzmJQeMx+l;F@%q_{#9W9T-x8;BlJe(dDQYfql=1F#uESR)rQJvrcuOIpx( z$xlwL#)qr@bCjI>*{b@6BdZc_ z9tO&`_pl36-n@B35jM;RA4D~R&kpwoR8~39%D0EsrzP>}ah|w4n9Xjzn zkIqWoN{opyt1-@k?c3_>>!x!qSa)0$x9^WP@0hOcatbk#3=dyprL+C|dNMd3_bYm< zyZr}@BlQ63lyJ&e3-mo502e?)sN)p%s+4$STULSYveQ~x^r9>>MX%gImlNwLm_Gn@ zjHTne8=0EYVV4dZp}x1$CRo?Y=n^xtq^v`{jba<|2bJnTraXYca9NPR)S%T4L+gPQ;_`X6$jjqkuzz!3z(-p`2y5& zAjhfc>23ItDG?ftcF@6Lc*{IZbD=F4Bf)|jV&f;4Upb++Eqq&jeZ9%0OB44zJcJgG zC?}P|PQ{DAxQXw0{I0V3lP9C`R|Sk5RFaAD!qNxKkcJn2uxG^`@C2rf$JMsQ?m0+u zt+5K;yOQBxvB2%?huq!@Nr$;+}3*TCr^OlW!iY8A{izJdK^0#MF^yXWr5fz z0M=?`;U&ZfZ%~IwYSsB8N0?1aOkzA0a2YFr>Wy0gSk-Rwc^*@Z$Khfk*Pj3}DZ07k zI_g4b1MPiWTib7LPGk=ar|lXNbx#{C2-%+V`5m24vEnXdVV>zXw!-L(CP~H6FtRKi ze<1qy&jDVhTnIM;n3%(0vZQlA7kHJMKayU&xPqJbFcOCu)#Im6n~#pb+R{#DL@`qh z;}FPqf4G3yTO}~cfQ$*R^a<}rY0wvtFo}^D2M*ljyH+yKyAPBIz&G*3pKk(Ng+^V; z(^CN?<_(N&gL!~v@dWl37#$>ZGyphokt)Ut*Ld!E8eAh%4Xca##F@)KK5)c`l6^gr zn|v646~J@BuqwOfW2g{Bd+_j(hsk!$e$4po!-Xurhlj}!BlIWl>(^CHADx@R)CpwF zPxwoGdNL{u9^mJ|fC{hxo(@dq4V*eMS&Nn$U9kj*1vD&hM#nL|iuRUVU6!DU(cq6X zS1`iRL5X)5??%!u0b}|3^3exU;H4&B9%2&5G$3?0SP5`j;7J`#uxii?)_R&o<5M*a zb=d4a-q2P>K2T9I92Z6CakhksU?xt&{p-kZJG!@VJ_dE&kBi)-ux39fd4kbiN zN~$J(YRVNAHL9S17j8EgYn8{3EXWcM%OqjgwS#ddx*3f*nJ>BL;^yiK8hLZV0W5n? zPL3UR0*vjWFjI-KV=p!1_kN+m>U5UZmjqj#Uds{jUsKaKExG5}sYs*mD=RZ4TkV`) zwxeF1S5;N5dGZC6hphY5=W|ogK3O?9&eu5>MPxuHfUWuoE+cs~0QQx*jc_gJW)9qj z41ZyoDF&eAfB zN?hE|ylhUhvpDFjVk>e1cJ*FeETN@C_}qs*C}^80VT&#j-o3kKQ-tM(3#-rjhdh8G(L1IGQm|^sTF_Myw?rc&O$s~#^H5 zp7X`V<70ig;66LC!KhUe9m2v-f@@y%Lz0r0{=ZiUX3Jyobn#3F<^1w721_!i*pY?_Q<=ihY$U6 zF-&x`7-wKpvKz(~5YyFK4^YHA(Jh0As`EB(XluI#+Y^WwEENY1)Qtjvq?(YU$aOYf zuIn2b^1^fkzQXt2JLQql(Iz`6dTVJw2chmi>@|~16HI*F%YkT5pi;rpJ4C3hzJ%~4-x6)WnqSgc}m*Q_9eNA8D3M& z4c#J>5Fk3p8JLac1!3D@ z=OqS0OdEjJRbEf0zJ{b!RDN9bxELZmNuF?f_JpPRKLfu-&M>kTwvi+*3}3}g|G-@R z_>n`0M5lg#udZLXIGb<#_GS1fn2LFD{NOn=9=qL(&chyeV3O2e7;DnVCMizC#iGO* z23JjMeRK12c)Z~I{J+|J^Kh!y_icE!tDSa2X&_P>Xf$NZScoJVR7#;m8ACERU!!E7$8SeYK zuj{4R)(LhGM=+;-Zk?Pr-AsOmg?$@R*s0R z1l*ek{P1v|UT$iGEnwy`6k$0^L`m3~{FkYUPQQ z2IR<6n>(^b7Y99j=mwH!>e{fQI2UL3;B;e_#xK}-yhqRwX5hjqIXB@$ijv7;Xmpz# zg>+wXi1PmL|K-qUWOKNojxEq*B4@n~d@|Xwi~e!ebFEvafW{AlS)0z4=7XS(LzpPIBXLeHra0Bkr{|~J8{&^S3(aK6o&6|iUHnw0A zu!jN@*xQ4}MN26z|JI|C2ZSOQ5c z{zI1m{II~lZOtPS!)9KiVs1J%HbeXy_$jb*MlPIi%52yLum`a->2AdViF)sVkx^$1 z$0kxY7XMu&D7V4am=`HDrb1vH0m3GbJ8vo4DeDP)7jlo|bwu+^S{?vhh`C6l>;=kE z;6I@AzNx5K0lf%N&QwVccvBJzD1Plaz-)q4g@y58Ppw7M8$|*ud9yo@O7@hZN+C)MLnbUuL8!u6!e`x!g8eMMv# zq%H*5vVekQjl66^ntgNds>9W*=PzGA8r=8w>rE)LmK7Sq;)-P-y58A0Z{N6C~WwgW= za7{k$xhvZK_Z6{hkh_%j1esy}+qVne9OOvQPX7ZWxCecGeSoNhq<=?$nMwG{t6^~e z-=o#~tO9s2>Sh=KA#WvA@F$_6xKyx3DBc_FJZv3C7(o>r0IWSAw@_^xg~IJQXNKyz zb4_k-q_C4)JXhFxcR%5af*eV_5<3UL(rFf%iYN_(KtBfKB9H{Q%y z#1hZ zyiD^(ULHpmke24chbqRPAit55Ba3ZCIX{&g zVw{aTe;>1NuEMKIt^#B#V#ETJ@CLAdq{;e#fcdCKW@cVEzR;!L2sal6rbSDZETmv@ zM~?xfpD)Y1b>%3?o38u#EXV#K7Lzy$A#`00u$P0ITM1%IVEHj%&1g%h^x>W1mx<4O zGIR}dmgS2mXy{0g0T9cURp10(We0I3!Gf_P!A*y3%o85xixs8@r(N3Io0b0g&VoJG z3;lG@_2|z$3rp(X7A{oJ?*o?#MpO}^9pqmD)1?TlM1g(-&0*jiXgb19F@-}8Sd<6Z zD!b-udp7HC2j(D}1|}0fek897e%$r_I}YVk8{3`>fpc%}_LDj6 zi<4~*cMMwhZv|%pJOb?nW*>lr)R09~x>8Y}4LMH#BZ+`|fUwbEI*x4U;mk5TVnpO0 z#qzXAb4|bHv@c(%Xu$25QFuxcqr1>r;I+&|g}2V_*1I0-DRZE~j!68?-}%dYLM5p1 zuL2DI=eUK1_8c6F#1{C77hY+B-cj67>;sbBRqY{`T_|WF33UU*6P?11qN1zNb*q#? zMTq{G=F|bcOdMbsSM$k4=0}cr z!kZuHL~$DG9a0TO8f;;p)c*r@1EG?*yMIADM|lt#1_!cyV)TZOJv<`rBaxGs$b#B; zA53S_bH9as92p&5(3K0#(g8^LiH4U+jHJ_KdIBUMsH_28lrWrusVSkUMEG*-?6>aT z{R0*VpHVI;4>ltOK=n~MatMbGq(CU=iFS^ZSiQtO;@vwXINxqWA%VW?OUW9Xo}W>< zO1eyl0$GW^CoYkJWrnufCKM&;?<>HUJMEJ7U@Og@73M3I2G!7aW4;TSQiE#SCqoV3 zA%RDMJHQ||WSZ;fq3#nB8#kWMFaHYa{C^-A(jc~@9O0isFi=k5{3qx+8ynlk#iu|n zL_D(Uso9MZeQ%g^A!M9nQ)0?am+cMDc+64V%z>vxv{3k1K)^8^9*n%v4zKK8TeMro@!81T)ZC~ z4xBZx;kg5kK2-W=&jnO?>SL!uW@9z*^#H(wRrp8O!IHaK{i7f60i>%h;2TtmYfbnB zA3wz3cGXHE=+B<6^JIBa>kcR@A9A`=s2NiQZ^6Q@3}|D}qJN4JURW~sz8Vd=Kr4|D zoCvKshC!G#KqTS=Mk#J;AYaQ$gB)c+!YWhd!_1NrWvK zK?R9Bp!eOoFFDpDnZ};a2Y2N@H2zq1fWVy(*ZJgZj6j-z_YV%>K1e%k%AUPNmVxR% z$YlywMXnGJ^9}MRf<{|i0%r|!mc?Y|3Bm_{j~90jagkx?hV^z3*X^m-#s9(9YaftE z@>}oUAN1-`-yU66T46ORe5|(O?L54NU9lhD8ucb9Gb$tGtk)j#W}#vA_+Qoe zG{pCQvtX(OZOAEq_&LgQ<9++ysHptMDuK(`aSjK z%c#>FmcV(s z9$6=YXpTja&araUa!)0IcZB7H%*nqS!)y5b%M8=@_+Ms9+NFxLVX$2D? zRf>TqqRz5H9{Y)0W>S(7fbUq&U(gCT)Wq2V6q}>Ey1tO;l9^UmpGaydc`JZZAs~>k zA>&jJStB%WQ%xNK`a<~^9j68i)DPI}!BJ$$0Rn#!F!JER+Z9x`TNo%pta)%vXz$&B z^vIpOFXx<;bu9bE#l@c?y+kLevL(~k_kNre*vjUs>~L)afx+)rpsx;ylgu;g>f)&H zxjHdqvi%#*bVT*tz3yD*7}f&)aMZGc%v2*LdWwNGjOlFTE&y1-YS`u;^m<&yQwN$1 zgve;?A*2dyTwFVK7JMWqh~}^RCKOOK9)L+{gvNwG7c2- zQoI&@%rQonE?V)y`t_Tgrg8P;bFsB9T)4pDu5eaGSu69ST6W z!C)o7gVocmzm()N!M|D!B6Wz(I5fYY_q8F%8*d#Jo%xK>uV+MZV8Hx!2!CSI|Cdq^ z@?)b_k0O&s(h8yKSlX|trzfa$-_#^4dRSCK*Q~IvvQmH5eOSU7-m-mWH8|P5E!swZ z?g_6a9hvRZD*ryo(m$I#2k2y^6&wfe=CKi!tlI)j1uPV~aih#%bj28quKV=qI(Qhr za>#Y-kZ8;Q_g|PJ{C3Y(^8YUVw*Pk^Qvc;$sDDZB>FOdGd#v|0)4yRSRJm@41^@3L zP5cu^`!6s0-=6yMFSrYEK+p?E?Wvuhn>4$tZGzjz);0n*QecrDV!f!nM^f@Ox(Hww z@04Aoz%;Hu2iIuIFpwGXi#-n-;+^RXcLm`B#jtD6t{h!I!X`~mFBWZFWbO%~U+;lPXZnm~$VkJE>DTx)$$0D#PR&j6w^bDG>iJRBH4VZg=a`L}m zo=`a1>FV-8KY(GCKFDB*qbqP!KB5Ovh^zjJRK79~-fnl%Pa$avdy_#F=I3N#rgC`*ZAh=`XOE%Qiv1u=yGIwz!{Xo8|+!q<#yfDAE0wMBVDl3IK) zjPtnQkyMCm$>%ZVOTg>U3t1pYUSGc=0|H3vh3D;Gv}@;1MUWGT%^6xNt5&a$$jfWJ zkV}+$pu?@T!(`FTVq(?blC7Xu1|CTuMMl$Wc5tgzaA<)LWnz^BV9E-HIHYg`U5yUY z(#zU4iU-BJsHv$*NJhq+Sk45? zgTze{g1!nU+f&F%0PhN)=|dk_7%Pp?I9-}RRf#LI?Bk}P{=v#ZD+|J;Ko{cfYyi#z z1)^uOfqsbzwu_dQNkdl+bW-#eBRTOY1ceotETQZnNjW%(J5(D)Q|b-K2aG+ybkIno zzAOVaeLCIrQT?RzaJANP?z5@7ZJ`Ea?%1oWtl*qHIBu#AJtm^$1prw=Rv{`lzEPNw zVt7PcwW}T&NO!QtKn_T~>9?GQ#Yar5DwkL&EB}sR4@8m;aWo~Tv z#?@eG@h>hw@<}x^@`V0~cn=b1b^Ie>0fb%NJpjU{Cz21K@ewEn(NPWHscNw9C|pKE z6ORrU3$F;U_kNU_(C*)XJp}MSB>0qlXn5o0ph9NhRUWy-IU?ojyB0S9ErVg{I<&?k zP@$vyJcic?loYJz0Z`RVt|`_7NbZF3! z!P0^w!4fqqE)(cDQ9h!X37@omF;!GpX9H;ivX8prR6;;>vJ(nZ@DVq@JNRe&w{IAI z#)5Kw45HuYB2E`xUtR{VSuk&x5N~tjvVc1P$P;IF0K}*!6DAs0f`EJA7o!+e2bn>c zH#?nlK*iOw8Axl?l#XcciRa7aG@D zbFNv#*KEin4TompDF$p&$Z3OO7XVK(QSf7kM0uATz4a3ApU=}3Kp){)!nTu#$D-OX zXc^%ib@%^3lnic)$;{MhD3O+`M0(tPjX2TjUr0h{#ku(J1y$_q7y6~?Pjo;41>)6` z>Zhc)ug}BFYZ-*uNW-tY=4&u508E@ZD&iLUy1gaW1>7*kfQTn=7TOh-)meUP?ch-( z>J*MxfRH*nGn%KI>9=;@oH2Q!lb}jfW#y)XfG=!GybM?XUK)9lhA0Ot|0_&v_^Oc3 zNB3T0>O`_jTz{BuU=@a}F9LRhq0#|w0ax$WgososS2kw?K)_&h3YNT) zWGMH)7X8%RZ2nVb3j)N7=td<~PPdX6Ih}DSC@plkYd9h?A}zQM)fp*e#T%@Bn^BhYzI#7z)rK08iFXkJ z)p4)8LKj)$dbO(8<@b4RxF+PJx0l`rshVNG2zUUPvmG_lfpU;Cvy=uv|dyU=*DbuOeigiAtPxJ`^m98qzmYmsMJpm8B*3fRT}S&d1L$Aa~EZ2Ogir+?o+n zKl*NlObzjOtCIV z`a*L}4WGFRJ>p{p*{_#6!QoN7eV^X>_*oXV5Kcq!hHL)IX;`%Tm==KK=hY)FB((8> zHmb@0!9-orn{*!a)-i>I`-NLVJ+RSP? z6o{fw>=F8y_j6=g-7o4Z^k#*1>sAvf&H_Zg$o`uPl}A2uj|2cSTlrqC|yme6%)Q@#;f+XE;wx}Sr4T7q_UU=xvl z=q3$ZJ01^m5#SZlN2+MsbvA<;sy5M}@PEAeK9Qeb@&WG*;$FAqIbqU(NCw{6?1X18 zq)A|+nU3@rpk6JUHm?TZ0#I-8ltw}m5!uGad+*0U+C5JRTQ&FI97o}O%$&gF|1U*A zM?X`C2T9T%_-1H+8$dgrO@BVMT%MnZHvmp^CQ$+q zJ8J-60TzQ8@Phn%755_t9A=;ppE(m$sW^TCh&&q7zzw23_w=H3!Lp8vt0*)EcmtSE zvxhea%XH|5hjW=_M2e9#ort!o2mM@fLK@fj>vW(rA$e=h5_1o~dLGSb2k~E=X&DAG z%@hp(MJm9GL!B@hLC`|K^b3R%P;5oU#VvdDkx5$dpp5Ixxhx-Q3^%j%IMFqwY4^Sw z1gnY)tyH*)l7CTC&!DwIsa=`}@%`JeNBUgXh~HG*>U~#+y_Lk-sS%pzeM_ z$$><|i(J9}m?|P=L&i4{K0CDp=W-!3CD8ZHZNfpOLPwf-&k>Ra4BL^hMsH zBw{`b@z6qC1t7OCP`*MeMwsvDl`4;x|Bo8p)6VhWHRQt|51GAkrdhf+OgF(sIC$`l zM62^cls+HfuZVfkZ_!Gi1bQ@MLVFg^7k(aG7iDu9Taa1M@B|KpeR6bBqSts z-b#mWoe>a9ba+X1hQ~wh49*AOStyr4e^LO=3>n3M`sF-ypCIebyrR~A`a~gq6Btzi z2^eILZ{c6{1A&dx#{o`9L{Ek05=5#VKrGK>6kN!xF7hSR1)}W3@c;_~R>WU4+&M}O zy2k@hL*V1U@OT3n00h64t%jmtA6&=D2NE`Uo+uJbv;ksb$n16E0zvD~y`>!a7JcJ_ zVbqQo96+4UfwK}b1hN2c-MR%J5~s)#IEh`Ko$hy4PKrch64^Ic?@OUydka@<7&S1x z`PswHapwtG6L0J5iE@OFY`mhmdAB+-TaQrmB(r5P7=?kIs>!i{rmTC&`u>PE0=9P?Wpe{hJVCE ze?#VrzzOX)jyp2Ei!`1{-uPu{h(R9tXw)qT^ay<|>xGOGW^*fqD#Yh4p$JNkkcL|) zQ3{y(hsto5e5=QEa|TJ}gq#30S|Z9pM6|F-OnZV%fyg}2jW5J6M6pH<;c%aDot)%xVS5B|^vp;v1%p+@#THL-IVp9zYc$cFRD6k{t#O$yH%o z^~Xuw zAC=>xd+VB;mxDL53)93&uZ@gCKy3}fDf=eb(#Uep)DE#d6T=oJXT5a|K`a(p6u zyuYGehIuKtSP!1rKO^$!LI6yp!-w83alpht03)NbO+4&5MQ0tu)JJ3=o}O3IHZPjf zY*UAqsYO;m+m>_m#{GLD*()f7$&ZbEKCKAKRtQ;CXHUhRIGuMbr{X1iRS+STQpO-# zAb0}crAUXV>1p0~@}7LxrUq2tcSbi315rvJA-$`+TbRc3n56&cZU!9=oWbA5_!lYnH?Qa6@qoXaTc9KZj6-h=M0nAL z$5>|2f&)|u=orsz{A!Ro36lqqis~Sa5-J3ZP(ok`{og8gp4NGlJLO}ZTux3- z3cMdkt_YXxu(!bC8u)Pe_%7Y_*+ zq8^`XZAIWR+JNq61HJx^jDoIqeSbEUXZkdF9c&7xr%VGugU)Yh(Up4l1a5NZMujw=g`m(K?N<^CiJ9&7V$V>q z#x4KU$SZrM#=mVMuiT(~G4|2CBL3Sgd*nBKJ=gkMWSNAm>U?A`h=3f4h`)UXZ`uze zIn}E6x-}b86XXoJfjlQ?W){tR^L~N{Xxd@3a90$)DBIW+I0`r@Am!nC@(Ku)F|IIE z?ZLI^&HzaIibmt|NkKk7JJ~w>1BJ!{j(UU6Z{OUoAf9Dp)O;Hk8X4XzBt$`?j;?#l z1!f>3`D9>?w@e;@Dp14COeU~n;9+dECk%;O+qYu>HJKH4y>P@+p88oL$Tx_oMkHpg z2`~^~6gv4pr>z`W+e1h`Q0VTz?K)%B94u@1OQKBFvR~^fM>u?UQMkU1^9t5GMA`XHSP-DRko)4zH0q6txMDRTHjU{L-HQrS}BE&{^uE5+# z!U|nLA$S=O!I}&89+yM5F+Qs6r5gIAP2mJ3v-

7pEgpO^~zWhWk)mnh857bHGg; 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 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5071399cb..1d29224f9 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -27,10 +27,10 @@ jobs: - name: run eslint run: | - CHANGED_JS=$(git --no-pager diff --name-only ..origin/master | grep '^src\/scripts\/.*\.js$' | xargs ls -d 2>/dev/null | paste -sd " " -) - if [[ -z $(sed -e 's/[[:space:]]*$//' <<<${CHANGED_JS}) ]]; then CHANGED_JS="src/scripts"; fi - echo $CHANGED_JS - node node_modules/eslint/bin/eslint.js $CHANGED_JS + CHANGED_TS=$(git --no-pager diff --name-only ..origin/master | grep '^src\/scripts\/.*\.ts$' | xargs ls -d 2>/dev/null | paste -sd " " -) + if [[ -z $(sed -e 's/[[:space:]]*$//' <<<${CHANGED_TS}) ]]; then CHANGED_TS="src/scripts"; fi + echo $CHANGED_TS + node node_modules/eslint/bin/eslint.js $CHANGED_TS - name: Lint JS bundle run: | diff --git a/.github/workflows/types.yml b/.github/workflows/types.yml deleted file mode 100644 index 395834db8..000000000 --- a/.github/workflows/types.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: TypeScript Check - -on: - pull_request: - paths: - - 'types/index.d.ts' - -jobs: - tsc: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 1 - - - uses: actions/setup-node@v1 - with: - node-version: 12 - - - name: Install TypeScript - run: npm install -g typescript - - - name: Install required dependencies - run: npm i --only=production --no-optional --no-audit --ignore-scripts - - - name: Check typings file - run: tsc types/index.d.ts diff --git a/.mocharc.yml b/.mocharc.yml index 8a31a63fd..40fed2d6f 100644 --- a/.mocharc.yml +++ b/.mocharc.yml @@ -1,7 +1,8 @@ require: - - '@babel/register' + - 'ts-node/register' - './config/jsdom.js' - exit: true - -spec: src/**/*.test.js +spec: src/**/**/*.test.ts +extension: + - ts + - js diff --git a/.vscode/settings.json b/.vscode/settings.json index 71464a51e..53ef4c806 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -59,5 +59,8 @@ "fileMatch": [".prettierrc.json"], "url": "http://json.schemastore.org/prettierrc" } - ] + ], + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + } } diff --git a/cypress/integration/select-multiple.spec.js b/cypress/integration/select-multiple.spec.ts similarity index 100% rename from cypress/integration/select-multiple.spec.js rename to cypress/integration/select-multiple.spec.ts diff --git a/cypress/integration/select-one.spec.js b/cypress/integration/select-one.spec.ts similarity index 100% rename from cypress/integration/select-one.spec.js rename to cypress/integration/select-one.spec.ts diff --git a/cypress/integration/text.spec.js b/cypress/integration/text.spec.ts similarity index 100% rename from cypress/integration/text.spec.js rename to cypress/integration/text.spec.ts diff --git a/package-lock.json b/package-lock.json index c19e82860..7036d3336 100644 --- a/package-lock.json +++ b/package-lock.json @@ -950,6 +950,18 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, + "@types/chai": { + "version": "4.2.7", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.7.tgz", + "integrity": "sha512-luq8meHGYwvky0O7u0eQZdA7B4Wd9owUCqvbw2m3XCrCU8mplYOujMBbvyS547AxJkC+pGnd0Cm15eNxEUNU8g==", + "dev": true + }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -967,12 +979,24 @@ "@types/node": "*" } }, + "@types/json-schema": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz", + "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==", + "dev": true + }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", "dev": true }, + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, "@types/node": { "version": "12.11.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.11.2.tgz", @@ -985,12 +1009,109 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, + "@types/sinon": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-7.5.1.tgz", + "integrity": "sha512-EZQUP3hSZQyTQRfiLqelC9NMWd1kqLcmQE0dMiklxBkgi84T+cHOhnKpgk4NnOWpGX863yE6+IaGnOXUNFqDnQ==", + "dev": true + }, + "@types/sinon-chai": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@types/sinon-chai/-/sinon-chai-3.2.3.tgz", + "integrity": "sha512-TOUFS6vqS0PVL1I8NGVSNcFaNJtFoyZPXZ5zur+qlhDfOmQECZZM4H4kKgca6O8L+QceX/ymODZASfUfn+y4yQ==", + "dev": true, + "requires": { + "@types/chai": "*", + "@types/sinon": "*" + } + }, "@types/sizzle": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.11.0.tgz", + "integrity": "sha512-G2HHA1vpMN0EEbUuWubiCCfd0R3a30BB+UdvnFkxwZIxYEGOrWEXDv8tBFO9f44CWc47Xv9lLM3VSn4ORLI2bA==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "2.11.0", + "eslint-utils": "^1.4.3", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + }, + "dependencies": { + "regexpp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", + "dev": true + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.11.0.tgz", + "integrity": "sha512-YxcA/y0ZJaCc/fB/MClhcDxHI0nOBB7v2/WxBju2cOTanX7jO9ttQq6Fy4yW9UaY5bPd9xL3cun3lDVqk67sPQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.11.0", + "eslint-scope": "^5.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.11.0.tgz", + "integrity": "sha512-DyGXeqhb3moMioEFZIHIp7oXBBh7dEfPTzGrlyP0Mi9ScCra4SWEGs3kPd18mG7Sy9Wy8z88zmrw5tSGL6r/6A==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.11.0", + "@typescript-eslint/typescript-estree": "2.11.0", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.11.0.tgz", + "integrity": "sha512-HGY4+d4MagO6cKMcKfIKaTMxcAv7dEVnji2Zi+vi5VV8uWAM631KjAB5GxFcexMYrwKT0EekRiiGK1/Sd7VFGA==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "eslint-visitor-keys": "^1.1.0", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash.unescape": "4.0.1", + "semver": "^6.3.0", + "tsutils": "^3.17.1" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "@webassemblyjs/ast": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", @@ -1413,6 +1534,12 @@ "readable-stream": "^2.0.6" } }, + "arg": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.2.tgz", + "integrity": "sha512-+ytCkGcBtHZ3V2r2Z06AncYO8jz46UEamcspGoU8lHcEbpn6J77QK0vdWvChsclg/tM5XIJC5tnjmPp7Eq6Obg==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -3681,9 +3808,9 @@ } }, "eslint": { - "version": "6.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.6.0.tgz", - "integrity": "sha512-PpEBq7b6qY/qrOmpYQ/jTMDYfuQMELR4g4WI1M/NaSDDD/bdcMb+dj4Hgks7p41kW2caXsPsEZAEAyAgjVVC0g==", + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", @@ -3701,7 +3828,7 @@ "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", "glob-parent": "^5.0.0", - "globals": "^11.7.0", + "globals": "^12.1.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", @@ -3714,7 +3841,7 @@ "minimatch": "^3.0.4", "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.2", + "optionator": "^0.8.3", "progress": "^2.0.0", "regexpp": "^2.0.1", "semver": "^6.1.2", @@ -3731,16 +3858,39 @@ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", "dev": true }, + "globals": { + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz", + "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, "import-fresh": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", - "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", "dev": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3767,6 +3917,12 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true } } }, @@ -3955,9 +4111,9 @@ } }, "eslint-plugin-cypress": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.7.0.tgz", - "integrity": "sha512-52Lq5ePCD/8jc536e1RqtLfj33BAy1s7BlYgCjbG39J5kqUitcTlRY5i3NRoeAyPHueDwETsq0eASF44ugLosQ==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.8.1.tgz", + "integrity": "sha512-jDpcP+MmjmqQO/x3bwIXgp4cl7Q66RYS5/IsuOQP4Qo2sEqE3DI8tTxBQ1EhnV5qEDd2Z2TYHR+5vYI6oCN4uw==", "dev": true, "requires": { "globals": "^11.12.0" @@ -5870,9 +6026,9 @@ "dev": true }, "fuse.js": { - "version": "3.4.5", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.4.5.tgz", - "integrity": "sha512-s9PGTaQIkT69HaeoTVjwGsLfb8V8ScJLx5XGFcKHg0MqLUH/UZ4EKOtqtXX9k7AFqCGxD1aJmYb8Q5VYDibVRQ==" + "version": "3.4.6", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.4.6.tgz", + "integrity": "sha512-H6aJY4UpLFwxj1+5nAvufom5b2BT2v45P1MkPvdGIK8fWjQx/7o6tTT1+ALV0yawQvbmvCF0ufl2et8eJ7v7Cg==" }, "gauge": { "version": "2.7.4", @@ -6653,9 +6809,9 @@ "dev": true }, "inquirer": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.0.tgz", - "integrity": "sha512-rSdC7zelHdRQFkWnhsMu2+2SO41mpv2oF2zy4tMhmiLWkcKbOAs87fWAJhVXttKVwhdZvymvnuM95EyEXg2/tQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.1.tgz", + "integrity": "sha512-V1FFQ3TIO15det8PijPLFR9M9baSlnRs9nL7zWu1MNVA2T9YVl9ZbrHJhYs7e9X8jeMZ3lr2JH/rdHFgNCBdYw==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -6667,25 +6823,25 @@ "lodash": "^4.17.15", "mute-stream": "0.0.8", "run-async": "^2.2.0", - "rxjs": "^6.4.0", + "rxjs": "^6.5.3", "string-width": "^4.1.0", "strip-ansi": "^5.1.0", "through": "^2.3.6" }, "dependencies": { "ansi-escapes": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.2.1.tgz", - "integrity": "sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", + "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", "dev": true, "requires": { - "type-fest": "^0.5.2" + "type-fest": "^0.8.1" } }, "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", "dev": true }, "cli-cursor": { @@ -6747,14 +6903,25 @@ } }, "string-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.1.0.tgz", - "integrity": "sha512-NrX+1dVVh+6Y9dnQ19pR0pP4FiEIlUvdTGn8pw6CKTNq5sgib2nIhmUNT5TAmhWmvKr3WcxBcP3E8nWezuipuQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^5.2.0" + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } } }, "strip-ansi": { @@ -6764,12 +6931,20 @@ "dev": true, "requires": { "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } } }, "type-fest": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.5.2.tgz", - "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", "dev": true } } @@ -8064,6 +8239,12 @@ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "lodash.unescape": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz", + "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=", + "dev": true + }, "log-symbols": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", @@ -8133,6 +8314,12 @@ "semver": "^5.6.0" } }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, "mamacro": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", @@ -10794,9 +10981,9 @@ "dev": true }, "prettier": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz", - "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "dev": true }, "prettier-linter-helpers": { @@ -13195,12 +13382,63 @@ "glob": "^7.1.2" } }, + "ts-loader": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.2.1.tgz", + "integrity": "sha512-Dd9FekWuABGgjE1g0TlQJ+4dFUfYGbYcs52/HQObE0ZmUNjQlmLAS7xXsSzy23AMaMwipsx5sNHvoEpT2CZq1g==", + "dev": true, + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^4.0.0", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "ts-node": { + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.5.4.tgz", + "integrity": "sha512-izbVCRV68EasEPQ8MSIGBNK9dc/4sYJJKYA+IarMQct1RtEot6Xp0bXuClsbUSnKpg50ho+aOAx8en5c+y4OFw==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.6", + "yn": "^3.0.0" + }, + "dependencies": { + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "dev": true + } + } + }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", "dev": true }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", @@ -13259,6 +13497,12 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typescript": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.3.tgz", + "integrity": "sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw==", + "dev": true + }, "uglify-js": { "version": "3.6.3", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.3.tgz", @@ -14667,6 +14911,12 @@ } } }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -14891,6 +15141,12 @@ } } } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/package.json b/package.json index 8a7a44d03..3fc668758 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,14 @@ "scripts": { "start": "run-p js:watch css:watch", "build": "run-p js:build css:build", - "lint": "eslint src/scripts", + "lint": "eslint src/scripts/**/*.ts", "bundlesize": "bundlesize", "cypress:run": "cypress run", "cypress:open": "cypress open", "cypress:ci": "cypress run --record --group $GITHUB_REF --ci-build-id $GITHUB_SHA", "test": "run-s test:unit test:e2e", - "test:unit": "NODE_ENV=test mocha", - "test:unit:watch": "NODE_ENV=test mocha --watch --inspect=5556", + "test:unit": "TS_NODE_TRANSPILE_ONLY=true NODE_ENV=test mocha", + "test:unit:watch": "npm run test:unit -- --watch --inspect=5556", "test:unit:coverage": "NODE_ENV=test nyc --reporter=lcov --reporter=text --reporter=text-summary mocha", "test:e2e": "run-p --race start cypress:run", "js:watch": "cross-env NODE_ENV=development node server.js", @@ -56,6 +56,12 @@ "@babel/core": "^7.6.4", "@babel/preset-env": "^7.6.3", "@babel/register": "^7.6.2", + "@types/chai": "^4.2.7", + "@types/mocha": "^5.2.7", + "@types/sinon": "^7.5.1", + "@types/sinon-chai": "^3.2.3", + "@typescript-eslint/eslint-plugin": "^2.11.0", + "@typescript-eslint/parser": "^2.11.0", "autoprefixer": "^9.6.5", "babel-loader": "^8.0.6", "bundlesize": "^0.18.0", @@ -63,12 +69,12 @@ "cross-env": "^6.0.3", "csso-cli": "^3.0.0", "cypress": "3.6.0", - "eslint": "^6.6.0", + "eslint": "^6.8.0", "eslint-config-airbnb-base": "^14.0.0", "eslint-config-prettier": "^6.5.0", "eslint-loader": "^3.0.2", "eslint-plugin-compat": "3.3.0", - "eslint-plugin-cypress": "^2.7.0", + "eslint-plugin-cypress": "^2.8.1", "eslint-plugin-import": "^2.18.2", "eslint-plugin-prettier": "^3.1.1", "eslint-plugin-sort-class-members": "^1.6.0", @@ -82,9 +88,12 @@ "npm-run-all": "^4.1.5", "nyc": "^14.1.1", "postcss-cli": "^6.1.3", - "prettier": "^1.18.2", + "prettier": "^1.19.1", "sinon": "^7.5.0", "sinon-chai": "^3.3.0", + "ts-loader": "^6.2.1", + "ts-node": "^8.5.4", + "typescript": "^3.7.3", "webpack": "^4.41.2", "webpack-cli": "^3.3.9", "webpack-dev-middleware": "^3.7.2", @@ -92,7 +101,7 @@ }, "dependencies": { "deepmerge": "^4.2.0", - "fuse.js": "^3.4.5", + "fuse.js": "^3.4.6", "redux": "^4.0.4" }, "npmName": "choices.js", diff --git a/public/assets/scripts/choices.js b/public/assets/scripts/choices.js index 98a61a795..ce3999df2 100644 --- a/public/assets/scripts/choices.js +++ b/public/assets/scripts/choices.js @@ -92,7 +92,7 @@ return /******/ (function(modules) { // webpackBootstrap /******/ /******/ /******/ // Load entry module and return exports -/******/ return __webpack_require__(__webpack_require__.s = 4); +/******/ return __webpack_require__(__webpack_require__.s = 7); /******/ }) /************************************************************************/ /******/ ([ @@ -102,143 +102,321 @@ return /******/ (function(modules) { // webpackBootstrap "use strict"; -var isMergeableObject = function isMergeableObject(value) { - return isNonNullObject(value) - && !isSpecial(value) +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var utils_1 = __webpack_require__(1); + +exports.DEFAULT_CLASSNAMES = { + containerOuter: 'choices', + containerInner: 'choices__inner', + input: 'choices__input', + inputCloned: 'choices__input--cloned', + list: 'choices__list', + listItems: 'choices__list--multiple', + listSingle: 'choices__list--single', + listDropdown: 'choices__list--dropdown', + item: 'choices__item', + itemSelectable: 'choices__item--selectable', + itemDisabled: 'choices__item--disabled', + itemChoice: 'choices__item--choice', + placeholder: 'choices__placeholder', + group: 'choices__group', + groupHeading: 'choices__heading', + button: 'choices__button', + activeState: 'is-active', + focusState: 'is-focused', + openState: 'is-open', + disabledState: 'is-disabled', + highlightedState: 'is-highlighted', + selectedState: 'is-selected', + flippedState: 'is-flipped', + loadingState: 'is-loading', + noResults: 'has-no-results', + noChoices: 'has-no-choices' }; +exports.DEFAULT_CONFIG = { + items: [], + choices: [], + silent: false, + renderChoiceLimit: -1, + maxItemCount: -1, + addItems: true, + addItemFilter: null, + removeItems: true, + removeItemButton: false, + editItems: false, + duplicateItemsAllowed: true, + delimiter: ',', + paste: true, + searchEnabled: true, + searchChoices: true, + searchFloor: 1, + searchResultLimit: 4, + searchFields: ['label', 'value'], + position: 'auto', + resetScrollPosition: true, + shouldSort: true, + shouldSortItems: false, + sorter: utils_1.sortByAlpha, + placeholder: true, + placeholderValue: null, + searchPlaceholderValue: null, + prependValue: null, + appendValue: null, + renderSelectedChoices: 'auto', + loadingText: 'Loading...', + noResultsText: 'No results found', + noChoicesText: 'No choices to choose from', + itemSelectText: 'Press to select', + uniqueItemText: 'Only unique values can be added', + customAddItemText: 'Only values matching specific conditions can be added', + addItemText: function addItemText(value) { + return "Press Enter to add \"" + utils_1.sanitise(value) + "\""; + }, + maxItemText: function maxItemText(maxItemCount) { + return "Only " + maxItemCount + " values can be added"; + }, + valueComparer: function valueComparer(value1, value2) { + return value1 === value2; + }, + fuseOptions: { + includeScore: true + }, + callbackOnInit: null, + callbackOnCreateTemplates: null, + classNames: exports.DEFAULT_CLASSNAMES +}; +exports.EVENTS = { + showDropdown: 'showDropdown', + hideDropdown: 'hideDropdown', + change: 'change', + choice: 'choice', + search: 'search', + addItem: 'addItem', + removeItem: 'removeItem', + highlightItem: 'highlightItem', + highlightChoice: 'highlightChoice', + unhighlightItem: 'unhighlightItem' +}; +exports.ACTION_TYPES = { + ADD_CHOICE: 'ADD_CHOICE', + FILTER_CHOICES: 'FILTER_CHOICES', + ACTIVATE_CHOICES: 'ACTIVATE_CHOICES', + CLEAR_CHOICES: 'CLEAR_CHOICES', + ADD_GROUP: 'ADD_GROUP', + ADD_ITEM: 'ADD_ITEM', + REMOVE_ITEM: 'REMOVE_ITEM', + HIGHLIGHT_ITEM: 'HIGHLIGHT_ITEM', + CLEAR_ALL: 'CLEAR_ALL', + RESET_TO: 'RESET_TO', + SET_IS_LOADING: 'SET_IS_LOADING' +}; +exports.KEY_CODES = { + BACK_KEY: 46, + DELETE_KEY: 8, + ENTER_KEY: 13, + A_KEY: 65, + ESC_KEY: 27, + UP_KEY: 38, + DOWN_KEY: 40, + PAGE_UP_KEY: 33, + PAGE_DOWN_KEY: 34 +}; +exports.TEXT_TYPE = 'text'; +exports.SELECT_ONE_TYPE = 'select-one'; +exports.SELECT_MULTIPLE_TYPE = 'select-multiple'; +exports.SCROLLING_SPEED = 4; -function isNonNullObject(value) { - return !!value && typeof value === 'object' -} +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { -function isSpecial(value) { - var stringValue = Object.prototype.toString.call(value); +"use strict"; - return stringValue === '[object RegExp]' - || stringValue === '[object Date]' - || isReactElement(value) -} -// see https://github.com/facebook/react/blob/b5ac963fb791d1298e7f396236383bc955f916c1/src/isomorphic/classic/element/ReactElement.js#L21-L25 -var canUseSymbol = typeof Symbol === 'function' && Symbol.for; -var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for('react.element') : 0xeac7; +Object.defineProperty(exports, "__esModule", { + value: true +}); +/* eslint-disable @typescript-eslint/no-explicit-any */ -function isReactElement(value) { - return value.$$typeof === REACT_ELEMENT_TYPE -} +exports.getRandomNumber = function (min, max) { + return Math.floor(Math.random() * (max - min) + min); +}; -function emptyTarget(val) { - return Array.isArray(val) ? [] : {} -} +exports.generateChars = function (length) { + return Array.from({ + length: length + }, function () { + return exports.getRandomNumber(0, 36).toString(36); + }).join(''); +}; -function cloneUnlessOtherwiseSpecified(value, options) { - return (options.clone !== false && options.isMergeableObject(value)) - ? deepmerge(emptyTarget(value), value, options) - : value -} +exports.generateId = function (element, prefix) { + var id = element.id || element.name && element.name + "-" + exports.generateChars(2) || exports.generateChars(4); + id = id.replace(/(:|\.|\[|\]|,)/g, ''); + id = prefix + "-" + id; + return id; +}; -function defaultArrayMerge(target, source, options) { - return target.concat(source).map(function(element) { - return cloneUnlessOtherwiseSpecified(element, options) - }) -} +exports.getType = function (obj) { + return Object.prototype.toString.call(obj).slice(8, -1); +}; -function getMergeFunction(key, options) { - if (!options.customMerge) { - return deepmerge - } - var customMerge = options.customMerge(key); - return typeof customMerge === 'function' ? customMerge : deepmerge -} +exports.isType = function (type, obj) { + return obj !== undefined && obj !== null && exports.getType(obj) === type; +}; -function getEnumerableOwnPropertySymbols(target) { - return Object.getOwnPropertySymbols - ? Object.getOwnPropertySymbols(target).filter(function(symbol) { - return target.propertyIsEnumerable(symbol) - }) - : [] -} +exports.wrap = function (element, wrapper) { + if (wrapper === void 0) { + wrapper = document.createElement('div'); + } -function getKeys(target) { - return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target)) -} + if (element.nextSibling) { + element.parentNode && element.parentNode.insertBefore(wrapper, element.nextSibling); + } else { + element.parentNode && element.parentNode.appendChild(wrapper); + } -// Protects from prototype poisoning and unexpected merging up the prototype chain. -function propertyIsUnsafe(target, key) { - try { - return (key in target) // Properties are safe to merge if they don't exist in the target yet, - && !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain, - && Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable. - } catch (unused) { - // Counterintuitively, it's safe to merge any property on a target that causes the `in` operator to throw. - // This happens when trying to copy an object in the source over a plain string in the target. - return false - } -} + return wrapper.appendChild(element); +}; -function mergeObject(target, source, options) { - var destination = {}; - if (options.isMergeableObject(target)) { - getKeys(target).forEach(function(key) { - destination[key] = cloneUnlessOtherwiseSpecified(target[key], options); - }); - } - getKeys(source).forEach(function(key) { - if (propertyIsUnsafe(target, key)) { - return - } +exports.getAdjacentEl = function (startEl, selector, direction) { + if (direction === void 0) { + direction = 1; + } - if (!options.isMergeableObject(source[key]) || !target[key]) { - destination[key] = cloneUnlessOtherwiseSpecified(source[key], options); - } else { - destination[key] = getMergeFunction(key, options)(target[key], source[key], options); - } - }); - return destination -} + var prop = (direction > 0 ? 'next' : 'previous') + "ElementSibling"; + var sibling = startEl[prop]; -function deepmerge(target, source, options) { - options = options || {}; - options.arrayMerge = options.arrayMerge || defaultArrayMerge; - options.isMergeableObject = options.isMergeableObject || isMergeableObject; - // cloneUnlessOtherwiseSpecified is added to `options` so that custom arrayMerge() - // implementations can use it. The caller may not replace it. - options.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified; + while (sibling) { + if (sibling.matches(selector)) { + return sibling; + } - var sourceIsArray = Array.isArray(source); - var targetIsArray = Array.isArray(target); - var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray; + sibling = sibling[prop]; + } - if (!sourceAndTargetTypesMatch) { - return cloneUnlessOtherwiseSpecified(source, options) - } else if (sourceIsArray) { - return options.arrayMerge(target, source, options) - } else { - return mergeObject(target, source, options) - } -} + return sibling; +}; + +exports.isScrolledIntoView = function (element, parent, direction) { + if (direction === void 0) { + direction = 1; + } + + if (!element) { + return false; + } + + var isVisible; + + if (direction > 0) { + // In view from bottom + isVisible = parent.scrollTop + parent.offsetHeight >= element.offsetTop + element.offsetHeight; + } else { + // In view from top + isVisible = element.offsetTop >= parent.scrollTop; + } + + return isVisible; +}; + +exports.sanitise = function (value) { + if (typeof value !== 'string') { + return value; + } + + return value.replace(/&/g, '&').replace(/>/g, '&rt;').replace(/1&&void 0!==arguments[1]?arguments[1]:{limit:!1};this._log('---------\nSearch pattern: "'.concat(e,'"'));var n=this._prepareSearchers(e),r=n.tokenSearchers,o=n.fullSearcher,i=this._search(r,o),a=i.weights,s=i.results;return this._computeScore(a,s),this.options.shouldSort&&this._sort(s),t.limit&&"number"==typeof t.limit&&(s=s.slice(0,t.limit)),this._format(s)}},{key:"_prepareSearchers",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=[];if(this.options.tokenize)for(var n=e.split(this.options.tokenSeparator),r=0,o=n.length;r0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1?arguments[1]:void 0,n=this.list,r={},o=[];if("string"==typeof n[0]){for(var i=0,a=n.length;i1)throw new Error("Key weight has to be > 0 and <= 1");d=d.name}else s[d]={weight:1};this._analyze({key:d,value:this.options.getFn(l,d),record:l,index:c},{resultMap:r,results:o,tokenSearchers:e,fullSearcher:t})}return{weights:s,results:o}}},{key:"_analyze",value:function(e,t){var n=e.key,r=e.arrayIndex,o=void 0===r?-1:r,i=e.value,a=e.record,c=e.index,h=t.tokenSearchers,l=void 0===h?[]:h,u=t.fullSearcher,f=void 0===u?[]:u,d=t.resultMap,v=void 0===d?{}:d,p=t.results,g=void 0===p?[]:p;if(null!=i){var y=!1,m=-1,k=0;if("string"==typeof i){this._log("\nKey: ".concat(""===n?"-":n));var S=f.search(i);if(this._log('Full text: "'.concat(i,'", score: ').concat(S.score)),this.options.tokenize){for(var x=i.split(this.options.tokenSeparator),b=[],M=0;M-1&&(P=(P+m)/2),this._log("Score average:",P);var F=!this.options.tokenize||!this.options.matchAllTokens||k>=l.length;if(this._log("\nCheck Matches: ".concat(F)),(y||S.isMatch)&&F){var T=v[c];T?T.output.push({key:n,arrayIndex:o,value:i,score:P,matchedIndices:S.matchedIndices}):(v[c]={item:a,output:[{key:n,arrayIndex:o,value:i,score:P,matchedIndices:S.matchedIndices}]},g.push(v[c]))}}else if(s(i))for(var z=0,E=i.length;z-1&&(a.arrayIndex=i.arrayIndex),t.matches.push(a)}}}),this.options.includeScore&&o.push(function(e,t){t.score=e.score});for(var i=0,a=e.length;in)return o(e,this.pattern,r);var a=this.options,s=a.location,c=a.distance,h=a.threshold,l=a.findAllMatches,u=a.minMatchCharLength;return i(e,this.pattern,this.patternAlphabet,{location:s,distance:c,threshold:h,findAllMatches:l,minMatchCharLength:u})}}])&&r(t.prototype,n),s&&r(t,s),e}();e.exports=s},function(e,t){var n=/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;e.exports=function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:/ +/g,o=new RegExp(t.replace(n,"\\$&").replace(r,"|")),i=e.match(o),a=!!i,s=[];if(a)for(var c=0,h=i.length;c=P;z-=1){var E=z-1,K=n[e.charAt(E)];if(K&&(x[E]=1),T[z]=(T[z+1]<<1|1)&K,0!==I&&(T[z]|=(L[z+1]|L[z])<<1|1|L[z+1]),T[z]&C&&(w=r(t,{errors:I,currentLocation:E,expectedLocation:g,distance:h}))<=m){if(m=w,(k=E)<=g)break;P=Math.max(1,2*g-k)}}if(r(t,{errors:I+1,currentLocation:g,expectedLocation:g,distance:h})>m)break;L=T}return{isMatch:k>=0,score:0===w?.001:w,matchedIndices:o(x,p)}}},function(e,t){e.exports=function(e,t){var n=t.errors,r=void 0===n?0:n,o=t.currentLocation,i=void 0===o?0:o,a=t.expectedLocation,s=void 0===a?0:a,c=t.distance,h=void 0===c?100:c,l=r/e.length,u=Math.abs(s-i);return h?l+u/h:u?1:l}},function(e,t){e.exports=function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1,n=[],r=-1,o=-1,i=0,a=e.length;i=t&&n.push([r,o]),r=-1)}return e[i-1]&&i-r>=t&&n.push([r,i-1]),n}},function(e,t){e.exports=function(e){for(var t={},n=e.length,r=0;r -1) { - return state.map(function (obj) { - var choice = obj; + WrappedElement.prototype.enable = function () { + this.element.removeAttribute('disabled'); + this.element.disabled = false; + this.isDisabled = false; + }; - if (choice.id === parseInt(action.choiceId, 10)) { - choice.selected = true; - } + WrappedElement.prototype.disable = function () { + this.element.setAttribute('disabled', ''); + this.element.disabled = true; + this.isDisabled = true; + }; - return choice; - }); - } + WrappedElement.prototype.triggerEvent = function (eventType, data) { + utils_1.dispatchEvent(this.element, eventType, data); + }; - return state; - } + return WrappedElement; +}(); - case 'REMOVE_ITEM': - { - // When an item is removed and it has an associated choice, - // we want to re-enable it so it can be chosen again - if (action.choiceId > -1) { - return state.map(function (obj) { - var choice = obj; +exports.default = WrappedElement; - if (choice.id === parseInt(action.choiceId, 10)) { - choice.selected = false; - } +/***/ }), +/* 6 */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { - return choice; - }); - } +"use strict"; +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return symbolObservablePonyfill; }); +function symbolObservablePonyfill(root) { + var result; + var Symbol = root.Symbol; - return state; - } + if (typeof Symbol === 'function') { + if (Symbol.observable) { + result = Symbol.observable; + } else { + result = Symbol('observable'); + Symbol.observable = result; + } + } else { + result = '@@observable'; + } - case 'FILTER_CHOICES': - { - return state.map(function (obj) { - var choice = obj; // Set active state based on whether choice is - // within filtered results + return result; +}; - choice.active = action.results.some(function (_ref) { - var item = _ref.item, - score = _ref.score; - if (item.id === choice.id) { - choice.score = score; - return true; - } +/***/ }), +/* 7 */ +/***/ (function(module, exports, __webpack_require__) { - return false; - }); - return choice; - }); - } +module.exports = __webpack_require__(8); - case 'ACTIVATE_CHOICES': - { - return state.map(function (obj) { - var choice = obj; - choice.active = action.active; - return choice; - }); - } - case 'CLEAR_CHOICES': - { - return choices_defaultState; - } +/***/ }), +/* 8 */ +/***/ (function(module, exports, __webpack_require__) { - default: - { - return state; - } - } -} -// CONCATENATED MODULE: ./src/scripts/reducers/general.js -var general_defaultState = { - loading: false -}; +"use strict"; -var general = function general(state, action) { - if (state === void 0) { - state = general_defaultState; - } - switch (action.type) { - case 'SET_IS_LOADING': - { - return { - loading: action.isLoading - }; - } +var __spreadArrays = this && this.__spreadArrays || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) { + s += arguments[i].length; + } - default: - { - return state; - } + for (var r = Array(s), k = 0, i = 0; i < il; i++) { + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) { + r[k] = a[j]; + } } -}; -/* harmony default export */ var reducers_general = (general); -// CONCATENATED MODULE: ./src/scripts/lib/utils.js -/** - * @param {number} min - * @param {number} max - * @returns {number} - */ -var getRandomNumber = function getRandomNumber(min, max) { - return Math.floor(Math.random() * (max - min) + min); + return r; }; -/** - * @param {number} length - * @returns {string} - */ -var generateChars = function generateChars(length) { - return Array.from({ - length: length - }, function () { - return getRandomNumber(0, 36).toString(36); - }).join(''); +var __importDefault = this && this.__importDefault || function (mod) { + return mod && mod.__esModule ? mod : { + "default": mod + }; }; -/** - * @param {HTMLInputElement | HTMLSelectElement} element - * @param {string} prefix - * @returns {string} - */ -var generateId = function generateId(element, prefix) { - var id = element.id || element.name && element.name + "-" + generateChars(2) || generateChars(4); - id = id.replace(/(:|\.|\[|\]|,)/g, ''); - id = prefix + "-" + id; - return id; -}; -/** - * @param {any} obj - * @returns {string} - */ +Object.defineProperty(exports, "__esModule", { + value: true +}); +/* eslint-disable @typescript-eslint/no-explicit-any */ -var getType = function getType(obj) { - return Object.prototype.toString.call(obj).slice(8, -1); -}; -/** - * @param {string} type - * @param {any} obj - * @returns {boolean} - */ +var fuse_js_1 = __importDefault(__webpack_require__(9)); -var isType = function isType(type, obj) { - return obj !== undefined && obj !== null && getType(obj) === type; -}; -/** - * @param {HTMLElement} element - * @param {HTMLElement} [wrapper={HTMLDivElement}] - * @returns {HTMLElement} - */ +var deepmerge_1 = __importDefault(__webpack_require__(10)); -var utils_wrap = function wrap(element, wrapper) { - if (wrapper === void 0) { - wrapper = document.createElement('div'); - } +var store_1 = __importDefault(__webpack_require__(11)); - if (element.nextSibling) { - element.parentNode.insertBefore(wrapper, element.nextSibling); - } else { - element.parentNode.appendChild(wrapper); - } +var components_1 = __webpack_require__(18); - return wrapper.appendChild(element); -}; -/** - * @param {Element} startEl - * @param {string} selector - * @param {1 | -1} direction - * @returns {Element | undefined} - */ +var constants_1 = __webpack_require__(0); -var getAdjacentEl = function getAdjacentEl(startEl, selector, direction) { - if (direction === void 0) { - direction = 1; - } +var templates_1 = __importDefault(__webpack_require__(25)); - if (!(startEl instanceof Element) || typeof selector !== 'string') { - return undefined; - } +var choices_1 = __webpack_require__(26); - var prop = (direction > 0 ? 'next' : 'previous') + "ElementSibling"; - var sibling = startEl[prop]; +var items_1 = __webpack_require__(27); - while (sibling) { - if (sibling.matches(selector)) { - return sibling; - } +var groups_1 = __webpack_require__(28); - sibling = sibling[prop]; - } +var misc_1 = __webpack_require__(29); - return sibling; -}; +var utils_1 = __webpack_require__(1); + +var reducers_1 = __webpack_require__(4); +/** @see {@link http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c} */ + + +var IS_IE11 = '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style; +var USER_DEFAULTS = {}; /** - * @param {Element} element - * @param {Element} parent - * @param {-1 | 1} direction - * @returns {boolean} + * Choices + * @author Josh Johnson */ -var isScrolledIntoView = function isScrolledIntoView(element, parent, direction) { - if (direction === void 0) { - direction = 1; - } +var Choices = +/** @class */ +function () { + function Choices(element, userConfig) { + var _this = this; - if (!element) { - return false; - } + if (element === void 0) { + element = '[data-choice]'; + } - var isVisible; + if (userConfig === void 0) { + userConfig = {}; + } - if (direction > 0) { - // In view from bottom - isVisible = parent.scrollTop + parent.offsetHeight >= element.offsetTop + element.offsetHeight; - } else { - // In view from top - isVisible = element.offsetTop >= parent.scrollTop; - } + this.config = deepmerge_1.default.all([constants_1.DEFAULT_CONFIG, Choices.defaults.options, userConfig], // When merging array configs, replace with a copy of the userConfig array, + // instead of concatenating with the default array + { + arrayMerge: function arrayMerge(_, sourceArray) { + return __spreadArrays(sourceArray); + } + }); + var invalidConfigOptions = utils_1.diff(this.config, constants_1.DEFAULT_CONFIG); - return isVisible; -}; -/** - * @param {any} value - * @returns {any} - */ + if (invalidConfigOptions.length) { + console.warn('Unknown config option(s) passed', invalidConfigOptions.join(', ')); + } + + var passedElement = typeof element === 'string' ? document.querySelector(element) : element; + + if (!(passedElement instanceof HTMLInputElement || passedElement instanceof HTMLSelectElement)) { + throw TypeError('Expected one of the following types text|select-one|select-multiple'); + } -var sanitise = function sanitise(value) { - if (typeof value !== 'string') { - return value; - } + this._isTextElement = passedElement.type === constants_1.TEXT_TYPE; + this._isSelectOneElement = passedElement.type === constants_1.SELECT_ONE_TYPE; + this._isSelectMultipleElement = passedElement.type === constants_1.SELECT_MULTIPLE_TYPE; + this._isSelectElement = this._isSelectOneElement || this._isSelectMultipleElement; + this.config.searchEnabled = this._isSelectMultipleElement || this.config.searchEnabled; - return value.replace(/&/g, '&').replace(/>/g, '&rt;').replace(/ (str: string) => Element} - */ + if (!['auto', 'always'].includes("" + this.config.renderSelectedChoices)) { + this.config.renderSelectedChoices = 'auto'; + } -var strToEl = function () { - var tmpEl = document.createElement('div'); - return function (str) { - var cleanedInput = str.trim(); - tmpEl.innerHTML = cleanedInput; - var firldChild = tmpEl.children[0]; + if (userConfig.addItemFilter && typeof userConfig.addItemFilter !== 'function') { + var re = userConfig.addItemFilter instanceof RegExp ? userConfig.addItemFilter : new RegExp(userConfig.addItemFilter); + this.config.addItemFilter = re.test.bind(re); + } - while (tmpEl.firstChild) { - tmpEl.removeChild(tmpEl.firstChild); + if (this._isTextElement) { + this.passedElement = new components_1.WrappedInput({ + element: passedElement, + classNames: this.config.classNames, + delimiter: this.config.delimiter + }); + } else { + this.passedElement = new components_1.WrappedSelect({ + element: passedElement, + classNames: this.config.classNames, + template: function template(data) { + return _this._templates.option(data); + } + }); } - return firldChild; - }; -}(); -/** - * @param {{ label?: string, value: string }} a - * @param {{ label?: string, value: string }} b - * @returns {number} - */ + this.initialised = false; + this._store = new store_1.default(); + this._initialState = reducers_1.defaultState; + this._currentState = reducers_1.defaultState; + this._prevState = reducers_1.defaultState; + this._currentValue = ''; + this._canSearch = !!this.config.searchEnabled; + this._isScrollingOnIe = false; + this._highlightPosition = 0; + this._wasTap = true; + this._placeholderValue = this._generatePlaceholderValue(); + this._baseId = utils_1.generateId(this.passedElement.element, 'choices-'); + /** + * setting direction in cases where it's explicitly set on passedElement + * or when calculated direction is different from the document + */ -var sortByAlpha = function sortByAlpha(_ref, _ref2) { - var value = _ref.value, - _ref$label = _ref.label, - label = _ref$label === void 0 ? value : _ref$label; - var value2 = _ref2.value, - _ref2$label = _ref2.label, - label2 = _ref2$label === void 0 ? value2 : _ref2$label; - return label.localeCompare(label2, [], { - sensitivity: 'base', - ignorePunctuation: true, - numeric: true - }); -}; -/** - * @param {{ score: number }} a - * @param {{ score: number }} b - */ + this._direction = this.passedElement.dir; -var sortByScore = function sortByScore(a, b) { - return a.score - b.score; -}; -/** - * @param {HTMLElement} element - * @param {string} type - * @param {object} customArgs - */ + if (!this._direction) { + var elementDirection = window.getComputedStyle(this.passedElement.element).direction; + var documentDirection = window.getComputedStyle(document.documentElement).direction; -var dispatchEvent = function dispatchEvent(element, type, customArgs) { - if (customArgs === void 0) { - customArgs = null; - } + if (elementDirection !== documentDirection) { + this._direction = elementDirection; + } + } - var event = new CustomEvent(type, { - detail: customArgs, - bubbles: true, - cancelable: true - }); - return element.dispatchEvent(event); -}; -/** - * @param {array} array - * @param {any} value - * @param {string} [key="value"] - * @returns {boolean} - */ + this._idNames = { + itemChoice: 'item-choice' + }; -var existsInArray = function existsInArray(array, value, key) { - if (key === void 0) { - key = 'value'; - } + if (this._isSelectElement) { + // Assign preset groups from passed element + this._presetGroups = this.passedElement.optionGroups; // Assign preset options from passed element - return array.some(function (item) { - if (typeof value === 'string') { - return item[key] === value.trim(); - } + this._presetOptions = this.passedElement.options; + } // Assign preset choices from passed object - return item[key] === value; - }); -}; -/** - * @param {any} obj - * @returns {any} - */ -var cloneObject = function cloneObject(obj) { - return JSON.parse(JSON.stringify(obj)); -}; -/** - * Returns an array of keys present on the first but missing on the second object - * @param {object} a - * @param {object} b - * @returns {string[]} - */ + this._presetChoices = this.config.choices; // Assign preset items from passed object first -var diff = function diff(a, b) { - var aKeys = Object.keys(a).sort(); - var bKeys = Object.keys(b).sort(); - return aKeys.filter(function (i) { - return bKeys.indexOf(i) < 0; - }); -}; -// CONCATENATED MODULE: ./src/scripts/reducers/index.js + this._presetItems = this.config.items; // Add any values passed from attribute + if (this.passedElement.value && this._isTextElement) { + var splitValues = this.passedElement.value.split(this.config.delimiter); + this._presetItems = this._presetItems.concat(splitValues); + } // Create array of choices from option elements + if (this.passedElement.options) { + this.passedElement.options.forEach(function (option) { + _this._presetChoices.push({ + value: option.value, + label: option.innerHTML, + selected: !!option.selected, + disabled: option.disabled || option.parentNode.disabled, + placeholder: option.value === '' || option.hasAttribute('placeholder'), + customProperties: option.dataset['custom-properties'] + }); + }); + } + this._render = this._render.bind(this); + this._onFocus = this._onFocus.bind(this); + this._onBlur = this._onBlur.bind(this); + this._onKeyUp = this._onKeyUp.bind(this); + this._onKeyDown = this._onKeyDown.bind(this); + this._onClick = this._onClick.bind(this); + this._onTouchMove = this._onTouchMove.bind(this); + this._onTouchEnd = this._onTouchEnd.bind(this); + this._onMouseDown = this._onMouseDown.bind(this); + this._onMouseOver = this._onMouseOver.bind(this); + this._onFormReset = this._onFormReset.bind(this); + this._onSelectKey = this._onSelectKey.bind(this); + this._onEnterKey = this._onEnterKey.bind(this); + this._onEscapeKey = this._onEscapeKey.bind(this); + this._onDirectionKey = this._onDirectionKey.bind(this); + this._onDeleteKey = this._onDeleteKey.bind(this); // If element has already been initialised with Choices, fail silently + if (this.passedElement.isActive) { + if (!this.config.silent) { + console.warn('Trying to initialise Choices on element already initialised', { + element: element + }); + } -var appReducer = combineReducers({ - items: items_items, - groups: groups, - choices: choices_choices, - general: reducers_general -}); + this.initialised = true; + return; + } // Let's go -var reducers_rootReducer = function rootReducer(passedState, action) { - var state = passedState; // If we are clearing all items, groups and options we reassign - // state and then pass that state to our proper reducer. This isn't - // mutating our actual state - // See: http://stackoverflow.com/a/35641992 - if (action.type === 'CLEAR_ALL') { - state = undefined; - } else if (action.type === 'RESET_TO') { - return cloneObject(action.state); + this.init(); } - return appReducer(state, action); -}; + Object.defineProperty(Choices, "defaults", { + get: function get() { + return Object.preventExtensions({ + get options() { + return USER_DEFAULTS; + }, -/* harmony default export */ var reducers = (reducers_rootReducer); -// CONCATENATED MODULE: ./src/scripts/store/store.js -function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + get templates() { + return templates_1.default; + } -function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + }); + }, + enumerable: true, + configurable: true + }); + Choices.prototype.init = function () { + if (this.initialised) { + return; + } + this._createTemplates(); -/** - * @typedef {import('../../../types/index').Choices.Choice} Choice - * @typedef {import('../../../types/index').Choices.Group} Group - * @typedef {import('../../../types/index').Choices.Item} Item - */ + this._createElements(); -var store_Store = -/*#__PURE__*/ -function () { - function Store() { - this._store = createStore(reducers, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()); - } - /** - * Subscribe store to function call (wrapped Redux method) - * @param {Function} onChange Function to trigger when state changes - * @return - */ + this._createStructure(); + this._store.subscribe(this._render); - var _proto = Store.prototype; + this._render(); - _proto.subscribe = function subscribe(onChange) { - this._store.subscribe(onChange); - } - /** - * Dispatch event to store (wrapped Redux method) - * @param {{ type: string, [x: string]: any }} action Action to trigger - * @return - */ - ; + this._addEventListeners(); - _proto.dispatch = function dispatch(action) { - this._store.dispatch(action); - } - /** - * Get store object (wrapping Redux method) - * @returns {object} State - */ - ; + var shouldDisable = !this.config.addItems || this.passedElement.element.hasAttribute('disabled'); - /** - * Get loading state from store - * @returns {boolean} Loading State - */ - _proto.isLoading = function isLoading() { - return this.state.general.loading; - } - /** - * Get single choice by it's ID - * @param {string} id - * @returns {Choice | undefined} Found choice - */ - ; + if (shouldDisable) { + this.disable(); + } - _proto.getChoiceById = function getChoiceById(id) { - return this.activeChoices.find(function (choice) { - return choice.id === parseInt(id, 10); - }); - } - /** - * Get group by group id - * @param {number} id Group ID - * @returns {Group | undefined} Group data - */ - ; + this.initialised = true; + var callbackOnInit = this.config.callbackOnInit; // Run callback if it is a function - _proto.getGroupById = function getGroupById(id) { - return this.groups.find(function (group) { - return group.id === id; - }); + if (callbackOnInit && typeof callbackOnInit === 'function') { + callbackOnInit.call(this); + } }; - _createClass(Store, [{ - key: "state", - get: function get() { - return this._store.getState(); + Choices.prototype.destroy = function () { + if (!this.initialised) { + return; } - /** - * Get items from store - * @returns {Item[]} Item objects - */ - }, { - key: "items", - get: function get() { - return this.state.items; + this._removeEventListeners(); + + this.passedElement.reveal(); + this.containerOuter.unwrap(this.passedElement.element); + this.clearStore(); + + if (this._isSelectElement) { + this.passedElement.options = this._presetOptions; } - /** - * Get active items from store - * @returns {Item[]} Item objects - */ - }, { - key: "activeItems", - get: function get() { - return this.items.filter(function (item) { - return item.active === true; - }); + this._templates = templates_1.default; + this.initialised = false; + }; + + Choices.prototype.enable = function () { + if (this.passedElement.isDisabled) { + this.passedElement.enable(); } - /** - * Get highlighted items from store - * @returns {Item[]} Item objects - */ - }, { - key: "highlightedActiveItems", - get: function get() { - return this.items.filter(function (item) { - return item.active && item.highlighted; - }); + if (this.containerOuter.isDisabled) { + this._addEventListeners(); + + this.input.enable(); + this.containerOuter.enable(); } - /** - * Get choices from store - * @returns {Choice[]} Option objects - */ - }, { - key: "choices", - get: function get() { - return this.state.choices; + return this; + }; + + Choices.prototype.disable = function () { + if (!this.passedElement.isDisabled) { + this.passedElement.disable(); } - /** - * Get active choices from store - * @returns {Choice[]} Option objects - */ - }, { - key: "activeChoices", - get: function get() { - return this.choices.filter(function (choice) { - return choice.active === true; - }); + if (!this.containerOuter.isDisabled) { + this._removeEventListeners(); + + this.input.disable(); + this.containerOuter.disable(); } - /** - * Get selectable choices from store - * @returns {Choice[]} Option objects - */ - }, { - key: "selectableChoices", - get: function get() { - return this.choices.filter(function (choice) { - return choice.disabled !== true; - }); + return this; + }; + + Choices.prototype.highlightItem = function (item, runEvent) { + if (runEvent === void 0) { + runEvent = true; } - /** - * Get choices that can be searched (excluding placeholders) - * @returns {Choice[]} Option objects - */ - }, { - key: "searchableChoices", - get: function get() { - return this.selectableChoices.filter(function (choice) { - return choice.placeholder !== true; - }); + if (!item || !item.id) { + return this; } - /** - * Get placeholder choice from store - * @returns {Choice | undefined} Found placeholder - */ - }, { - key: "placeholderChoice", - get: function get() { - return [].concat(this.choices).reverse().find(function (choice) { - return choice.placeholder === true; + var id = item.id, + _a = item.groupId, + groupId = _a === void 0 ? -1 : _a, + _b = item.value, + value = _b === void 0 ? '' : _b, + _c = item.label, + label = _c === void 0 ? '' : _c; + var group = groupId >= 0 ? this._store.getGroupById(groupId) : null; + + this._store.dispatch(items_1.highlightItem(id, true)); + + if (runEvent) { + this.passedElement.triggerEvent(constants_1.EVENTS.highlightItem, { + id: id, + value: value, + label: label, + groupValue: group && group.value ? group.value : null }); } - /** - * Get groups from store - * @returns {Group[]} Group objects - */ - }, { - key: "groups", - get: function get() { - return this.state.groups; - } - /** - * Get active groups from store - * @returns {Group[]} Group objects - */ + return this; + }; - }, { - key: "activeGroups", - get: function get() { - var groups = this.groups, - choices = this.choices; - return groups.filter(function (group) { - var isActive = group.active === true && group.disabled === false; - var hasActiveOptions = choices.some(function (choice) { - return choice.active === true && choice.disabled === false; - }); - return isActive && hasActiveOptions; - }, []); + Choices.prototype.unhighlightItem = function (item) { + if (!item || !item.id) { + return this; } - }]); - return Store; -}(); + var id = item.id, + _a = item.groupId, + groupId = _a === void 0 ? -1 : _a, + _b = item.value, + value = _b === void 0 ? '' : _b, + _c = item.label, + label = _c === void 0 ? '' : _c; + var group = groupId >= 0 ? this._store.getGroupById(groupId) : null; + this._store.dispatch(items_1.highlightItem(id, false)); -// CONCATENATED MODULE: ./src/scripts/components/dropdown.js -function dropdown_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + this.passedElement.triggerEvent(constants_1.EVENTS.highlightItem, { + id: id, + value: value, + label: label, + groupValue: group && group.value ? group.value : null + }); + return this; + }; -function dropdown_createClass(Constructor, protoProps, staticProps) { if (protoProps) dropdown_defineProperties(Constructor.prototype, protoProps); if (staticProps) dropdown_defineProperties(Constructor, staticProps); return Constructor; } + Choices.prototype.highlightAll = function () { + var _this = this; -/** - * @typedef {import('../../../types/index').Choices.passedElement} passedElement - * @typedef {import('../../../types/index').Choices.ClassNames} ClassNames - */ -var Dropdown = -/*#__PURE__*/ -function () { - /** - * @param {{ - * element: HTMLElement, - * type: passedElement['type'], - * classNames: ClassNames, - * }} args - */ - function Dropdown(_ref) { - var element = _ref.element, - type = _ref.type, - classNames = _ref.classNames; - this.element = element; - this.classNames = classNames; - this.type = type; - this.isActive = false; - } - /** - * Bottom position of dropdown in viewport coordinates - * @returns {number} Vertical position - */ + this._store.items.forEach(function (item) { + return _this.highlightItem(item); + }); + return this; + }; - var _proto = Dropdown.prototype; + Choices.prototype.unhighlightAll = function () { + var _this = this; - /** - * Find element that matches passed selector - * @param {string} selector - * @returns {HTMLElement | null} - */ - _proto.getChild = function getChild(selector) { - return this.element.querySelector(selector); - } - /** - * Show dropdown to user by adding active state class - * @returns {this} - */ - ; + this._store.items.forEach(function (item) { + return _this.unhighlightItem(item); + }); - _proto.show = function show() { - this.element.classList.add(this.classNames.activeState); - this.element.setAttribute('aria-expanded', 'true'); - this.isActive = true; return this; - } - /** - * Hide dropdown from user - * @returns {this} - */ - ; + }; + + Choices.prototype.removeActiveItemsByValue = function (value) { + var _this = this; + + this._store.activeItems.filter(function (item) { + return item.value === value; + }).forEach(function (item) { + return _this._removeItem(item); + }); - _proto.hide = function hide() { - this.element.classList.remove(this.classNames.activeState); - this.element.setAttribute('aria-expanded', 'false'); - this.isActive = false; return this; }; - dropdown_createClass(Dropdown, [{ - key: "distanceFromTopWindow", - get: function get() { - return this.element.getBoundingClientRect().bottom; - } - }]); + Choices.prototype.removeActiveItems = function (excludedId) { + var _this = this; - return Dropdown; -}(); + this._store.activeItems.filter(function (_a) { + var id = _a.id; + return id !== excludedId; + }).forEach(function (item) { + return _this._removeItem(item); + }); + return this; + }; -// CONCATENATED MODULE: ./src/scripts/constants.js + Choices.prototype.removeHighlightedItems = function (runEvent) { + var _this = this; -/** - * @typedef {import('../../types/index').Choices.ClassNames} ClassNames - * @typedef {import('../../types/index').Choices.Options} Options - */ + if (runEvent === void 0) { + runEvent = false; + } -/** @type {ClassNames} */ + this._store.highlightedActiveItems.forEach(function (item) { + _this._removeItem(item); // If this action was performed by the user + // trigger the event -var DEFAULT_CLASSNAMES = { - containerOuter: 'choices', - containerInner: 'choices__inner', - input: 'choices__input', - inputCloned: 'choices__input--cloned', - list: 'choices__list', - listItems: 'choices__list--multiple', - listSingle: 'choices__list--single', - listDropdown: 'choices__list--dropdown', - item: 'choices__item', - itemSelectable: 'choices__item--selectable', - itemDisabled: 'choices__item--disabled', - itemChoice: 'choices__item--choice', - placeholder: 'choices__placeholder', - group: 'choices__group', - groupHeading: 'choices__heading', - button: 'choices__button', - activeState: 'is-active', - focusState: 'is-focused', - openState: 'is-open', - disabledState: 'is-disabled', - highlightedState: 'is-highlighted', - selectedState: 'is-selected', - flippedState: 'is-flipped', - loadingState: 'is-loading', - noResults: 'has-no-results', - noChoices: 'has-no-choices' -}; -/** @type {Options} */ -var DEFAULT_CONFIG = { - items: [], - choices: [], - silent: false, - renderChoiceLimit: -1, - maxItemCount: -1, - addItems: true, - addItemFilter: null, - removeItems: true, - removeItemButton: false, - editItems: false, - duplicateItemsAllowed: true, - delimiter: ',', - paste: true, - searchEnabled: true, - searchChoices: true, - searchFloor: 1, - searchResultLimit: 4, - searchFields: ['label', 'value'], - position: 'auto', - resetScrollPosition: true, - shouldSort: true, - shouldSortItems: false, - sorter: sortByAlpha, - placeholder: true, - placeholderValue: null, - searchPlaceholderValue: null, - prependValue: null, - appendValue: null, - renderSelectedChoices: 'auto', - loadingText: 'Loading...', - noResultsText: 'No results found', - noChoicesText: 'No choices to choose from', - itemSelectText: 'Press to select', - uniqueItemText: 'Only unique values can be added', - customAddItemText: 'Only values matching specific conditions can be added', - addItemText: function addItemText(value) { - return "Press Enter to add \"" + sanitise(value) + "\""; - }, - maxItemText: function maxItemText(maxItemCount) { - return "Only " + maxItemCount + " values can be added"; - }, - valueComparer: function valueComparer(value1, value2) { - return value1 === value2; - }, - fuseOptions: { - includeScore: true - }, - callbackOnInit: null, - callbackOnCreateTemplates: null, - classNames: DEFAULT_CLASSNAMES -}; -var EVENTS = { - showDropdown: 'showDropdown', - hideDropdown: 'hideDropdown', - change: 'change', - choice: 'choice', - search: 'search', - addItem: 'addItem', - removeItem: 'removeItem', - highlightItem: 'highlightItem', - highlightChoice: 'highlightChoice' -}; -var ACTION_TYPES = { - ADD_CHOICE: 'ADD_CHOICE', - FILTER_CHOICES: 'FILTER_CHOICES', - ACTIVATE_CHOICES: 'ACTIVATE_CHOICES', - CLEAR_CHOICES: 'CLEAR_CHOICES', - ADD_GROUP: 'ADD_GROUP', - ADD_ITEM: 'ADD_ITEM', - REMOVE_ITEM: 'REMOVE_ITEM', - HIGHLIGHT_ITEM: 'HIGHLIGHT_ITEM', - CLEAR_ALL: 'CLEAR_ALL' -}; -var KEY_CODES = { - BACK_KEY: 46, - DELETE_KEY: 8, - ENTER_KEY: 13, - A_KEY: 65, - ESC_KEY: 27, - UP_KEY: 38, - DOWN_KEY: 40, - PAGE_UP_KEY: 33, - PAGE_DOWN_KEY: 34 -}; -var TEXT_TYPE = 'text'; -var SELECT_ONE_TYPE = 'select-one'; -var SELECT_MULTIPLE_TYPE = 'select-multiple'; -var SCROLLING_SPEED = 4; -// CONCATENATED MODULE: ./src/scripts/components/container.js + if (runEvent) { + _this._triggerChange(item.value); + } + }); + + return this; + }; + + Choices.prototype.showDropdown = function (preventInputFocus) { + var _this = this; + if (this.dropdown.isActive) { + return this; + } -/** - * @typedef {import('../../../types/index').Choices.passedElement} passedElement - * @typedef {import('../../../types/index').Choices.ClassNames} ClassNames - */ + requestAnimationFrame(function () { + _this.dropdown.show(); -var container_Container = -/*#__PURE__*/ -function () { - /** - * @param {{ - * element: HTMLElement, - * type: passedElement['type'], - * classNames: ClassNames, - * position - * }} args - */ - function Container(_ref) { - var element = _ref.element, - type = _ref.type, - classNames = _ref.classNames, - position = _ref.position; - this.element = element; - this.classNames = classNames; - this.type = type; - this.position = position; - this.isOpen = false; - this.isFlipped = false; - this.isFocussed = false; - this.isDisabled = false; - this.isLoading = false; - this._onFocus = this._onFocus.bind(this); - this._onBlur = this._onBlur.bind(this); - } + _this.containerOuter.open(_this.dropdown.distanceFromTopWindow); - var _proto = Container.prototype; + if (!preventInputFocus && _this._canSearch) { + _this.input.focus(); + } - _proto.addEventListeners = function addEventListeners() { - this.element.addEventListener('focus', this._onFocus); - this.element.addEventListener('blur', this._onBlur); + _this.passedElement.triggerEvent(constants_1.EVENTS.showDropdown, {}); + }); + return this; }; - _proto.removeEventListeners = function removeEventListeners() { - this.element.removeEventListener('focus', this._onFocus); - this.element.removeEventListener('blur', this._onBlur); - } - /** - * Determine whether container should be flipped based on passed - * dropdown position - * @param {number} dropdownPos - * @returns {boolean} - */ - ; + Choices.prototype.hideDropdown = function (preventInputBlur) { + var _this = this; - _proto.shouldFlip = function shouldFlip(dropdownPos) { - if (typeof dropdownPos !== 'number') { - return false; - } // If flip is enabled and the dropdown bottom position is - // greater than the window height flip the dropdown. + if (!this.dropdown.isActive) { + return this; + } + requestAnimationFrame(function () { + _this.dropdown.hide(); - var shouldFlip = false; + _this.containerOuter.close(); - if (this.position === 'auto') { - shouldFlip = !window.matchMedia("(min-height: " + (dropdownPos + 1) + "px)").matches; - } else if (this.position === 'top') { - shouldFlip = true; + if (!preventInputBlur && _this._canSearch) { + _this.input.removeActiveDescendant(); + + _this.input.blur(); + } + + _this.passedElement.triggerEvent(constants_1.EVENTS.hideDropdown, {}); + }); + return this; + }; + + Choices.prototype.getValue = function (valueOnly) { + if (valueOnly === void 0) { + valueOnly = false; } - return shouldFlip; - } - /** - * @param {string} activeDescendantID - */ - ; + var values = this._store.activeItems.reduce(function (selectedItems, item) { + var itemValue = valueOnly ? item.value : item; + selectedItems.push(itemValue); + return selectedItems; + }, []); - _proto.setActiveDescendant = function setActiveDescendant(activeDescendantID) { - this.element.setAttribute('aria-activedescendant', activeDescendantID); + return this._isSelectOneElement ? values[0] : values; }; - _proto.removeActiveDescendant = function removeActiveDescendant() { - this.element.removeAttribute('aria-activedescendant'); - } + Choices.prototype.setValue = function (items) { + var _this = this; + + if (!this.initialised) { + return this; + } + + items.forEach(function (value) { + return _this._setChoiceOrItem(value); + }); + return this; + }; + + Choices.prototype.setChoiceByValue = function (value) { + var _this = this; + + if (!this.initialised || this._isTextElement) { + return this; + } // If only one value has been passed, convert to array + + + var choiceValue = Array.isArray(value) ? value : [value]; // Loop through each value and + + choiceValue.forEach(function (val) { + return _this._findAndSelectChoiceByValue(val); + }); + return this; + }; /** - * @param {number} dropdownPos + * Set choices of select input via an array of objects (or function that returns array of object or promise of it), + * a value field name and a label field name. + * This behaves the same as passing items via the choices option but can be called after initialising Choices. + * This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices. + * Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc). + * + * **Input types affected:** select-one, select-multiple + * + * @example + * ```js + * const example = new Choices(element); + * + * example.setChoices([ + * {value: 'One', label: 'Label One', disabled: true}, + * {value: 'Two', label: 'Label Two', selected: true}, + * {value: 'Three', label: 'Label Three'}, + * ], 'value', 'label', false); + * ``` + * + * @example + * ```js + * const example = new Choices(element); + * + * example.setChoices(async () => { + * try { + * const items = await fetch('/items'); + * return items.json() + * } catch(err) { + * console.error(err) + * } + * }); + * ``` + * + * @example + * ```js + * const example = new Choices(element); + * + * example.setChoices([{ + * label: 'Group one', + * id: 1, + * disabled: false, + * choices: [ + * {value: 'Child One', label: 'Child One', selected: true}, + * {value: 'Child Two', label: 'Child Two', disabled: true}, + * {value: 'Child Three', label: 'Child Three'}, + * ] + * }, + * { + * label: 'Group two', + * id: 2, + * disabled: false, + * choices: [ + * {value: 'Child Four', label: 'Child Four', disabled: true}, + * {value: 'Child Five', label: 'Child Five'}, + * {value: 'Child Six', label: 'Child Six', customProperties: { + * description: 'Custom description about child six', + * random: 'Another random custom property' + * }}, + * ] + * }], 'value', 'label', false); + * ``` */ - ; - _proto.open = function open(dropdownPos) { - this.element.classList.add(this.classNames.openState); - this.element.setAttribute('aria-expanded', 'true'); - this.isOpen = true; - if (this.shouldFlip(dropdownPos)) { - this.element.classList.add(this.classNames.flippedState); - this.isFlipped = true; + Choices.prototype.setChoices = function (choicesArrayOrFetcher, value, label, replaceChoices) { + var _this = this; + + if (choicesArrayOrFetcher === void 0) { + choicesArrayOrFetcher = []; } - }; - _proto.close = function close() { - this.element.classList.remove(this.classNames.openState); - this.element.setAttribute('aria-expanded', 'false'); - this.removeActiveDescendant(); - this.isOpen = false; // A dropdown flips if it does not have space within the page + if (value === void 0) { + value = 'value'; + } - if (this.isFlipped) { - this.element.classList.remove(this.classNames.flippedState); - this.isFlipped = false; + if (label === void 0) { + label = 'label'; } - }; - _proto.focus = function focus() { - if (!this.isFocussed) { - this.element.focus(); + if (replaceChoices === void 0) { + replaceChoices = false; } - }; - _proto.addFocusState = function addFocusState() { - this.element.classList.add(this.classNames.focusState); - }; + if (!this.initialised) { + throw new ReferenceError("setChoices was called on a non-initialized instance of Choices"); + } - _proto.removeFocusState = function removeFocusState() { - this.element.classList.remove(this.classNames.focusState); - }; + if (!this._isSelectElement) { + throw new TypeError("setChoices can't be used with INPUT based Choices"); + } - _proto.enable = function enable() { - this.element.classList.remove(this.classNames.disabledState); - this.element.removeAttribute('aria-disabled'); + if (typeof value !== 'string' || !value) { + throw new TypeError("value parameter must be a name of 'value' field in passed objects"); + } // Clear choices if needed - if (this.type === SELECT_ONE_TYPE) { - this.element.setAttribute('tabindex', '0'); + + if (replaceChoices) { + this.clearChoices(); } - this.isDisabled = false; + if (typeof choicesArrayOrFetcher === 'function') { + // it's a choices fetcher function + var fetcher_1 = choicesArrayOrFetcher(this); + + if (typeof Promise === 'function' && fetcher_1 instanceof Promise) { + // that's a promise + // eslint-disable-next-line compat/compat + return new Promise(function (resolve) { + return requestAnimationFrame(resolve); + }) // eslint-disable-line compat/compat + .then(function () { + return _this._handleLoadingState(true); + }).then(function () { + return fetcher_1; + }).then(function (data) { + return _this.setChoices(data, value, label, replaceChoices); + }).catch(function (err) { + if (!_this.config.silent) { + console.error(err); + } + }).then(function () { + return _this._handleLoadingState(false); + }).then(function () { + return _this; + }); + } // function returned something else than promise, let's check if it's an array of choices + + + if (!Array.isArray(fetcher_1)) { + throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: " + typeof fetcher_1); + } // recursion with results, it's sync and choices were cleared already + + + return this.setChoices(fetcher_1, value, label, false); + } + + if (!Array.isArray(choicesArrayOrFetcher)) { + throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices"); + } + + this.containerOuter.removeLoadingState(); + + this._startLoading(); + + choicesArrayOrFetcher.forEach(function (groupOrChoice) { + if (groupOrChoice.choices) { + _this._addGroup({ + id: groupOrChoice.id ? parseInt("" + groupOrChoice.id, 10) : null, + group: groupOrChoice, + valueKey: value, + labelKey: label + }); + } else { + var choice = groupOrChoice; + + _this._addChoice({ + value: choice[value], + label: choice[label], + isSelected: !!choice.selected, + isDisabled: !!choice.disabled, + placeholder: !!choice.placeholder, + customProperties: choice.customProperties + }); + } + }); + + this._stopLoading(); + + return this; + }; + + Choices.prototype.clearChoices = function () { + this._store.dispatch(choices_1.clearChoices()); + + return this; + }; + + Choices.prototype.clearStore = function () { + this._store.dispatch(misc_1.clearAll()); + + return this; }; - _proto.disable = function disable() { - this.element.classList.add(this.classNames.disabledState); - this.element.setAttribute('aria-disabled', 'true'); + Choices.prototype.clearInput = function () { + var shouldSetInputWidth = !this._isSelectOneElement; + this.input.clear(shouldSetInputWidth); - if (this.type === SELECT_ONE_TYPE) { - this.element.setAttribute('tabindex', '-1'); + if (!this._isTextElement && this._canSearch) { + this._isSearching = false; + + this._store.dispatch(choices_1.activateChoices(true)); } - this.isDisabled = true; - } - /** - * @param {HTMLElement} element - */ - ; + return this; + }; - _proto.wrap = function wrap(element) { - utils_wrap(element, this.element); - } - /** - * @param {Element} element - */ - ; + Choices.prototype._render = function () { + if (this._store.isLoading()) { + return; + } - _proto.unwrap = function unwrap(element) { - // Move passed element outside this element - this.element.parentNode.insertBefore(element, this.element); // Remove this element + this._currentState = this._store.state; + var stateChanged = this._currentState.choices !== this._prevState.choices || this._currentState.groups !== this._prevState.groups || this._currentState.items !== this._prevState.items; + var shouldRenderChoices = this._isSelectElement; + var shouldRenderItems = this._currentState.items !== this._prevState.items; - this.element.parentNode.removeChild(this.element); - }; + if (!stateChanged) { + return; + } - _proto.addLoadingState = function addLoadingState() { - this.element.classList.add(this.classNames.loadingState); - this.element.setAttribute('aria-busy', 'true'); - this.isLoading = true; - }; + if (shouldRenderChoices) { + this._renderChoices(); + } - _proto.removeLoadingState = function removeLoadingState() { - this.element.classList.remove(this.classNames.loadingState); - this.element.removeAttribute('aria-busy'); - this.isLoading = false; - }; + if (shouldRenderItems) { + this._renderItems(); + } - _proto._onFocus = function _onFocus() { - this.isFocussed = true; + this._prevState = this._currentState; }; - _proto._onBlur = function _onBlur() { - this.isFocussed = false; - }; + Choices.prototype._renderChoices = function () { + var _this = this; - return Container; -}(); + var _a = this._store, + activeGroups = _a.activeGroups, + activeChoices = _a.activeChoices; + var choiceListFragment = document.createDocumentFragment(); + this.choiceList.clear(); + if (this.config.resetScrollPosition) { + requestAnimationFrame(function () { + return _this.choiceList.scrollToTop(); + }); + } // If we have grouped options -// CONCATENATED MODULE: ./src/scripts/components/input.js -function input_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } -function input_createClass(Constructor, protoProps, staticProps) { if (protoProps) input_defineProperties(Constructor.prototype, protoProps); if (staticProps) input_defineProperties(Constructor, staticProps); return Constructor; } + if (activeGroups.length >= 1 && !this._isSearching) { + // If we have a placeholder choice along with groups + var activePlaceholders = activeChoices.filter(function (activeChoice) { + return activeChoice.placeholder === true && activeChoice.groupId === -1; + }); + if (activePlaceholders.length >= 1) { + choiceListFragment = this._createChoicesFragment(activePlaceholders, choiceListFragment); + } + choiceListFragment = this._createGroupsFragment(activeGroups, activeChoices, choiceListFragment); + } else if (activeChoices.length >= 1) { + choiceListFragment = this._createChoicesFragment(activeChoices, choiceListFragment); + } // If we have choices to show -/** - * @typedef {import('../../../types/index').Choices.passedElement} passedElement - * @typedef {import('../../../types/index').Choices.ClassNames} ClassNames - */ -var input_Input = -/*#__PURE__*/ -function () { - /** - * @param {{ - * element: HTMLInputElement, - * type: passedElement['type'], - * classNames: ClassNames, - * preventPaste: boolean - * }} args - */ - function Input(_ref) { - var element = _ref.element, - type = _ref.type, - classNames = _ref.classNames, - preventPaste = _ref.preventPaste; - this.element = element; - this.type = type; - this.classNames = classNames; - this.preventPaste = preventPaste; - this.isFocussed = this.element === document.activeElement; - this.isDisabled = element.disabled; - this._onPaste = this._onPaste.bind(this); - this._onInput = this._onInput.bind(this); - this._onFocus = this._onFocus.bind(this); - this._onBlur = this._onBlur.bind(this); - } - /** - * @param {string} placeholder - */ + if (choiceListFragment.childNodes && choiceListFragment.childNodes.length > 0) { + var activeItems = this._store.activeItems; + var canAddItem = this._canAddItem(activeItems, this.input.value); // ...and we can select them - var _proto = Input.prototype; - _proto.addEventListeners = function addEventListeners() { - this.element.addEventListener('paste', this._onPaste); - this.element.addEventListener('input', this._onInput, { - passive: true - }); - this.element.addEventListener('focus', this._onFocus, { - passive: true - }); - this.element.addEventListener('blur', this._onBlur, { - passive: true - }); - }; + if (canAddItem.response) { + // ...append them and highlight the first choice + this.choiceList.append(choiceListFragment); - _proto.removeEventListeners = function removeEventListeners() { - this.element.removeEventListener('input', this._onInput, { - passive: true - }); - this.element.removeEventListener('paste', this._onPaste); - this.element.removeEventListener('focus', this._onFocus, { - passive: true - }); - this.element.removeEventListener('blur', this._onBlur, { - passive: true - }); - }; + this._highlightChoice(); + } else { + var notice = this._getTemplate('notice', canAddItem.notice); - _proto.enable = function enable() { - this.element.removeAttribute('disabled'); - this.isDisabled = false; - }; + this.choiceList.append(notice); + } + } else { + // Otherwise show a notice + var dropdownItem = void 0; + var notice = void 0; - _proto.disable = function disable() { - this.element.setAttribute('disabled', ''); - this.isDisabled = true; - }; + if (this._isSearching) { + notice = typeof this.config.noResultsText === 'function' ? this.config.noResultsText() : this.config.noResultsText; + dropdownItem = this._getTemplate('notice', notice, 'no-results'); + } else { + notice = typeof this.config.noChoicesText === 'function' ? this.config.noChoicesText() : this.config.noChoicesText; + dropdownItem = this._getTemplate('notice', notice, 'no-choices'); + } - _proto.focus = function focus() { - if (!this.isFocussed) { - this.element.focus(); + this.choiceList.append(dropdownItem); } }; - _proto.blur = function blur() { - if (this.isFocussed) { - this.element.blur(); - } - } - /** - * Set value of input to blank - * @param {boolean} setWidth - * @returns {this} - */ - ; + Choices.prototype._renderItems = function () { + var activeItems = this._store.activeItems || []; + this.itemList.clear(); // Create a fragment to store our list items + // (so we don't have to update the DOM for each item) - _proto.clear = function clear(setWidth) { - if (setWidth === void 0) { - setWidth = true; - } + var itemListFragment = this._createItemsFragment(activeItems); // If we have items to add, append them - if (this.element.value) { - this.element.value = ''; - } - if (setWidth) { - this.setWidth(); + if (itemListFragment.childNodes) { + this.itemList.append(itemListFragment); } - - return this; - } - /** - * Set the correct input width based on placeholder - * value or input value - */ - ; - - _proto.setWidth = function setWidth() { - // Resize input to contents or placeholder - var _this$element = this.element, - style = _this$element.style, - value = _this$element.value, - placeholder = _this$element.placeholder; - style.minWidth = placeholder.length + 1 + "ch"; - style.width = value.length + 1 + "ch"; - } - /** - * @param {string} activeDescendantID - */ - ; - - _proto.setActiveDescendant = function setActiveDescendant(activeDescendantID) { - this.element.setAttribute('aria-activedescendant', activeDescendantID); - }; - - _proto.removeActiveDescendant = function removeActiveDescendant() { - this.element.removeAttribute('aria-activedescendant'); }; - _proto._onInput = function _onInput() { - if (this.type !== SELECT_ONE_TYPE) { - this.setWidth(); - } - } - /** - * @param {Event} event - */ - ; + Choices.prototype._createGroupsFragment = function (groups, choices, fragment) { + var _this = this; - _proto._onPaste = function _onPaste(event) { - if (this.preventPaste) { - event.preventDefault(); + if (fragment === void 0) { + fragment = document.createDocumentFragment(); } - }; - _proto._onFocus = function _onFocus() { - this.isFocussed = true; - }; + var getGroupChoices = function getGroupChoices(group) { + return choices.filter(function (choice) { + if (_this._isSelectOneElement) { + return choice.groupId === group.id; + } - _proto._onBlur = function _onBlur() { - this.isFocussed = false; - }; + return choice.groupId === group.id && (_this.config.renderSelectedChoices === 'always' || !choice.selected); + }); + }; // If sorting is enabled, filter groups - input_createClass(Input, [{ - key: "placeholder", - set: function set(placeholder) { - this.element.placeholder = placeholder; - } - /** - * @returns {string} - */ - }, { - key: "value", - get: function get() { - return sanitise(this.element.value); - } - /** - * @param {string} value - */ - , - set: function set(value) { - this.element.value = value; + if (this.config.shouldSort) { + groups.sort(this.config.sorter); } - }]); - return Input; -}(); + groups.forEach(function (group) { + var groupChoices = getGroupChoices(group); + if (groupChoices.length >= 1) { + var dropdownGroup = _this._getTemplate('choiceGroup', group); -// CONCATENATED MODULE: ./src/scripts/components/list.js + fragment.appendChild(dropdownGroup); -/** - * @typedef {import('../../../types/index').Choices.Choice} Choice - */ + _this._createChoicesFragment(groupChoices, fragment, true); + } + }); + return fragment; + }; -var list_List = -/*#__PURE__*/ -function () { - /** - * @param {{ element: HTMLElement }} args - */ - function List(_ref) { - var element = _ref.element; - this.element = element; - this.scrollPos = this.element.scrollTop; - this.height = this.element.offsetHeight; - } + Choices.prototype._createChoicesFragment = function (choices, fragment, withinGroup) { + var _this = this; - var _proto = List.prototype; + if (fragment === void 0) { + fragment = document.createDocumentFragment(); + } - _proto.clear = function clear() { - this.element.innerHTML = ''; - } - /** - * @param {Element | DocumentFragment} node - */ - ; + if (withinGroup === void 0) { + withinGroup = false; + } // Create a fragment to store our list items (so we don't have to update the DOM for each item) - _proto.append = function append(node) { - this.element.appendChild(node); - } - /** - * @param {string} selector - * @returns {Element | null} - */ - ; - _proto.getChild = function getChild(selector) { - return this.element.querySelector(selector); - } - /** - * @returns {boolean} - */ - ; + var _a = this.config, + renderSelectedChoices = _a.renderSelectedChoices, + searchResultLimit = _a.searchResultLimit, + renderChoiceLimit = _a.renderChoiceLimit; + var filter = this._isSearching ? utils_1.sortByScore : this.config.sorter; - _proto.hasChildren = function hasChildren() { - return this.element.hasChildNodes(); - }; + var appendChoice = function appendChoice(choice) { + var shouldRender = renderSelectedChoices === 'auto' ? _this._isSelectOneElement || !choice.selected : true; - _proto.scrollToTop = function scrollToTop() { - this.element.scrollTop = 0; - } - /** - * @param {Element} element - * @param {1 | -1} direction - */ - ; + if (shouldRender) { + var dropdownItem = _this._getTemplate('choice', choice, _this.config.itemSelectText); - _proto.scrollToChildElement = function scrollToChildElement(element, direction) { - var _this = this; + fragment.appendChild(dropdownItem); + } + }; - if (!element) { - return; - } + var rendererableChoices = choices; - var listHeight = this.element.offsetHeight; // Scroll position of dropdown + if (renderSelectedChoices === 'auto' && !this._isSelectOneElement) { + rendererableChoices = choices.filter(function (choice) { + return !choice.selected; + }); + } // Split array into placeholders and "normal" choices - var listScrollPosition = this.element.scrollTop + listHeight; - var elementHeight = element.offsetHeight; // Distance from bottom of element to top of parent - var elementPos = element.offsetTop + elementHeight; // Difference between the element and scroll position + var _b = rendererableChoices.reduce(function (acc, choice) { + if (choice.placeholder) { + acc.placeholderChoices.push(choice); + } else { + acc.normalChoices.push(choice); + } - var destination = direction > 0 ? this.element.scrollTop + elementPos - listScrollPosition : element.offsetTop; - requestAnimationFrame(function () { - _this._animateScroll(destination, direction); - }); - } - /** - * @param {number} scrollPos - * @param {number} strength - * @param {number} destination - */ - ; + return acc; + }, { + placeholderChoices: [], + normalChoices: [] + }), + placeholderChoices = _b.placeholderChoices, + normalChoices = _b.normalChoices; // If sorting is enabled or the user is searching, filter choices - _proto._scrollDown = function _scrollDown(scrollPos, strength, destination) { - var easing = (destination - scrollPos) / strength; - var distance = easing > 1 ? easing : 1; - this.element.scrollTop = scrollPos + distance; - } - /** - * @param {number} scrollPos - * @param {number} strength - * @param {number} destination - */ - ; - _proto._scrollUp = function _scrollUp(scrollPos, strength, destination) { - var easing = (scrollPos - destination) / strength; - var distance = easing > 1 ? easing : 1; - this.element.scrollTop = scrollPos - distance; - } - /** - * @param {*} destination - * @param {*} direction - */ - ; + if (this.config.shouldSort || this._isSearching) { + normalChoices.sort(filter); + } - _proto._animateScroll = function _animateScroll(destination, direction) { - var _this2 = this; + var choiceLimit = rendererableChoices.length; // Prepend placeholeder - var strength = SCROLLING_SPEED; - var choiceListScrollTop = this.element.scrollTop; - var continueAnimation = false; + var sortedChoices = this._isSelectOneElement ? __spreadArrays(placeholderChoices, normalChoices) : normalChoices; - if (direction > 0) { - this._scrollDown(choiceListScrollTop, strength, destination); + if (this._isSearching) { + choiceLimit = searchResultLimit; + } else if (renderChoiceLimit && renderChoiceLimit > 0 && !withinGroup) { + choiceLimit = renderChoiceLimit; + } // Add each choice to dropdown within range - if (choiceListScrollTop < destination) { - continueAnimation = true; - } - } else { - this._scrollUp(choiceListScrollTop, strength, destination); - if (choiceListScrollTop > destination) { - continueAnimation = true; + for (var i = 0; i < choiceLimit; i += 1) { + if (sortedChoices[i]) { + appendChoice(sortedChoices[i]); } } - if (continueAnimation) { - requestAnimationFrame(function () { - _this2._animateScroll(destination, direction); - }); - } + return fragment; }; - return List; -}(); - - -// CONCATENATED MODULE: ./src/scripts/components/wrapped-element.js -function wrapped_element_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + Choices.prototype._createItemsFragment = function (items, fragment) { + var _this = this; -function wrapped_element_createClass(Constructor, protoProps, staticProps) { if (protoProps) wrapped_element_defineProperties(Constructor.prototype, protoProps); if (staticProps) wrapped_element_defineProperties(Constructor, staticProps); return Constructor; } + if (fragment === void 0) { + fragment = document.createDocumentFragment(); + } // Create fragment to add elements to -/** - * @typedef {import('../../../types/index').Choices.passedElement} passedElement - * @typedef {import('../../../types/index').Choices.ClassNames} ClassNames - */ + var _a = this.config, + shouldSortItems = _a.shouldSortItems, + sorter = _a.sorter, + removeItemButton = _a.removeItemButton; // If sorting is enabled, filter items -var wrapped_element_WrappedElement = -/*#__PURE__*/ -function () { - /** - * @param {{ - * element: HTMLInputElement | HTMLSelectElement, - * classNames: ClassNames, - * }} args - */ - function WrappedElement(_ref) { - var element = _ref.element, - classNames = _ref.classNames; - this.element = element; - this.classNames = classNames; + if (shouldSortItems && !this._isSelectOneElement) { + items.sort(sorter); + } - if (!(element instanceof HTMLInputElement) && !(element instanceof HTMLSelectElement)) { - throw new TypeError('Invalid element passed'); + if (this._isTextElement) { + // Update the value of the hidden input + this.passedElement.value = items.map(function (_a) { + var value = _a.value; + return value; + }).join(this.config.delimiter); + } else { + // Update the options of the hidden input + this.passedElement.options = items; } - this.isDisabled = false; - } + var addItemToFragment = function addItemToFragment(item) { + // Create new list element + var listItem = _this._getTemplate('item', item, removeItemButton); // Append it to list - var _proto = WrappedElement.prototype; - _proto.conceal = function conceal() { - // Hide passed input - this.element.classList.add(this.classNames.input); - this.element.hidden = true; // Remove element from tab index + fragment.appendChild(listItem); + }; // Add each list item to list - this.element.tabIndex = -1; // Backup original styles if any - var origStyle = this.element.getAttribute('style'); + items.forEach(addItemToFragment); + return fragment; + }; - if (origStyle) { - this.element.setAttribute('data-choice-orig-style', origStyle); + Choices.prototype._triggerChange = function (value) { + if (value === undefined || value === null) { + return; } - this.element.setAttribute('data-choice', 'active'); + this.passedElement.triggerEvent(constants_1.EVENTS.change, { + value: value + }); }; - _proto.reveal = function reveal() { - // Reinstate passed element - this.element.classList.remove(this.classNames.input); - this.element.hidden = false; - this.element.removeAttribute('tabindex'); // Recover original styles if any + Choices.prototype._selectPlaceholderChoice = function (placeholderChoice) { + this._addItem({ + value: placeholderChoice.value, + label: placeholderChoice.label, + choiceId: placeholderChoice.id, + groupId: placeholderChoice.groupId, + placeholder: placeholderChoice.placeholder + }); - var origStyle = this.element.getAttribute('data-choice-orig-style'); + this._triggerChange(placeholderChoice.value); + }; - if (origStyle) { - this.element.removeAttribute('data-choice-orig-style'); - this.element.setAttribute('style', origStyle); - } else { - this.element.removeAttribute('style'); + Choices.prototype._handleButtonAction = function (activeItems, element) { + if (!activeItems || !element || !this.config.removeItems || !this.config.removeItemButton) { + return; } - this.element.removeAttribute('data-choice'); // Re-assign values - this is weird, I know - // @todo Figure out why we need to do this + var itemId = element.parentNode && element.parentNode.dataset.id; + var itemToRemove = itemId && activeItems.find(function (item) { + return item.id === parseInt(itemId, 10); + }); - this.element.value = this.element.value; // eslint-disable-line no-self-assign - }; + if (!itemToRemove) { + return; + } // Remove item associated with button - _proto.enable = function enable() { - this.element.removeAttribute('disabled'); - this.element.disabled = false; - this.isDisabled = false; - }; - _proto.disable = function disable() { - this.element.setAttribute('disabled', ''); - this.element.disabled = true; - this.isDisabled = true; - }; + this._removeItem(itemToRemove); - _proto.triggerEvent = function triggerEvent(eventType, data) { - dispatchEvent(this.element, eventType, data); - }; + this._triggerChange(itemToRemove.value); - wrapped_element_createClass(WrappedElement, [{ - key: "isActive", - get: function get() { - return this.element.dataset.choice === 'active'; - } - }, { - key: "dir", - get: function get() { - return this.element.dir; - } - }, { - key: "value", - get: function get() { - return this.element.value; - }, - set: function set(value) { - // you must define setter here otherwise it will be readonly property - this.element.value = value; + if (this._isSelectOneElement && this._store.placeholderChoice) { + this._selectPlaceholderChoice(this._store.placeholderChoice); } - }]); - - return WrappedElement; -}(); + }; + Choices.prototype._handleItemAction = function (activeItems, element, hasShiftKey) { + var _this = this; -// CONCATENATED MODULE: ./src/scripts/components/wrapped-input.js -function wrapped_input_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + if (hasShiftKey === void 0) { + hasShiftKey = false; + } -function wrapped_input_createClass(Constructor, protoProps, staticProps) { if (protoProps) wrapped_input_defineProperties(Constructor.prototype, protoProps); if (staticProps) wrapped_input_defineProperties(Constructor, staticProps); return Constructor; } + if (!activeItems || !element || !this.config.removeItems || this._isSelectOneElement) { + return; + } -function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } + var passedId = element.dataset.id; // We only want to select one item with a click + // so we deselect any items that aren't the target + // unless shift is being pressed + activeItems.forEach(function (item) { + if (item.id === parseInt("" + passedId, 10) && !item.highlighted) { + _this.highlightItem(item); + } else if (!hasShiftKey && item.highlighted) { + _this.unhighlightItem(item); + } + }); // Focus input as without focus, a user cannot do anything with a + // highlighted item -/** - * @typedef {import('../../../types/index').Choices.ClassNames} ClassNames - * @typedef {import('../../../types/index').Choices.Item} Item - */ + this.input.focus(); + }; -var WrappedInput = -/*#__PURE__*/ -function (_WrappedElement) { - _inheritsLoose(WrappedInput, _WrappedElement); + Choices.prototype._handleChoiceAction = function (activeItems, element) { + if (!activeItems || !element) { + return; + } // If we are clicking on an option - /** - * @param {{ - * element: HTMLInputElement, - * classNames: ClassNames, - * delimiter: string - * }} args - */ - function WrappedInput(_ref) { - var _this; - var element = _ref.element, - classNames = _ref.classNames, - delimiter = _ref.delimiter; - _this = _WrappedElement.call(this, { - element: element, - classNames: classNames - }) || this; - _this.delimiter = delimiter; - return _this; - } - /** - * @returns {string} - */ + var id = element.dataset.id; + var choice = id && this._store.getChoiceById(id); - wrapped_input_createClass(WrappedInput, [{ - key: "value", - get: function get() { - return this.element.value; - } - /** - * @param {Item[]} items - */ - , - set: function set(items) { - var itemValues = items.map(function (_ref2) { - var value = _ref2.value; - return value; - }); - var joinedValues = itemValues.join(this.delimiter); - this.element.setAttribute('value', joinedValues); - this.element.value = joinedValues; + if (!choice) { + return; } - }]); - return WrappedInput; -}(wrapped_element_WrappedElement); + var passedKeyCode = activeItems[0] && activeItems[0].keyCode ? activeItems[0].keyCode : undefined; + var hasActiveDropdown = this.dropdown.isActive; // Update choice keyCode + choice.keyCode = passedKeyCode; + this.passedElement.triggerEvent(constants_1.EVENTS.choice, { + choice: choice + }); -// CONCATENATED MODULE: ./src/scripts/components/wrapped-select.js -function wrapped_select_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + if (!choice.selected && !choice.disabled) { + var canAddItem = this._canAddItem(activeItems, choice.value); -function wrapped_select_createClass(Constructor, protoProps, staticProps) { if (protoProps) wrapped_select_defineProperties(Constructor.prototype, protoProps); if (staticProps) wrapped_select_defineProperties(Constructor, staticProps); return Constructor; } + if (canAddItem.response) { + this._addItem({ + value: choice.value, + label: choice.label, + choiceId: choice.id, + groupId: choice.groupId, + customProperties: choice.customProperties, + placeholder: choice.placeholder, + keyCode: choice.keyCode + }); -function wrapped_select_inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } + this._triggerChange(choice.value); + } + } + this.clearInput(); // We want to close the dropdown if we are dealing with a single select box -/** - * @typedef {import('../../../types/index').Choices.ClassNames} ClassNames - * @typedef {import('../../../types/index').Choices.Item} Item - * @typedef {import('../../../types/index').Choices.Choice} Choice - */ + if (hasActiveDropdown && this._isSelectOneElement) { + this.hideDropdown(true); + this.containerOuter.focus(); + } + }; -var WrappedSelect = -/*#__PURE__*/ -function (_WrappedElement) { - wrapped_select_inheritsLoose(WrappedSelect, _WrappedElement); + Choices.prototype._handleBackspace = function (activeItems) { + if (!this.config.removeItems || !activeItems) { + return; + } - /** - * @param {{ - * element: HTMLSelectElement, - * classNames: ClassNames, - * delimiter: string - * template: function - * }} args - */ - function WrappedSelect(_ref) { - var _this; + var lastItem = activeItems[activeItems.length - 1]; + var hasHighlightedItems = activeItems.some(function (item) { + return item.highlighted; + }); // If editing the last item is allowed and there are not other selected items, + // we can edit the item value. Otherwise if we can remove items, remove all selected items - var element = _ref.element, - classNames = _ref.classNames, - template = _ref.template; - _this = _WrappedElement.call(this, { - element: element, - classNames: classNames - }) || this; - _this.template = template; - return _this; - } + if (this.config.editItems && !hasHighlightedItems && lastItem) { + this.input.value = lastItem.value; + this.input.setWidth(); + + this._removeItem(lastItem); - var _proto = WrappedSelect.prototype; + this._triggerChange(lastItem.value); + } else { + if (!hasHighlightedItems) { + // Highlight last item if none already highlighted + this.highlightItem(lastItem, false); + } - /** - * @param {DocumentFragment} fragment - */ - _proto.appendDocFragment = function appendDocFragment(fragment) { - this.element.innerHTML = ''; - this.element.appendChild(fragment); + this.removeHighlightedItems(true); + } }; - wrapped_select_createClass(WrappedSelect, [{ - key: "placeholderOption", - get: function get() { - return this.element.querySelector('option[value=""]') || // Backward compatibility layer for the non-standard placeholder attribute supported in older versions. - this.element.querySelector('option[placeholder]'); - } - /** - * @returns {Element[]} - */ + Choices.prototype._startLoading = function () { + this._store.dispatch(misc_1.setIsLoading(true)); + }; - }, { - key: "optionGroups", - get: function get() { - return Array.from(this.element.getElementsByTagName('OPTGROUP')); - } - /** - * @returns {Item[] | Choice[]} - */ + Choices.prototype._stopLoading = function () { + this._store.dispatch(misc_1.setIsLoading(false)); + }; - }, { - key: "options", - get: function get() { - return Array.from(this.element.options); + Choices.prototype._handleLoadingState = function (setLoading) { + if (setLoading === void 0) { + setLoading = true; } - /** - * @param {Item[] | Choice[]} options - */ - , - set: function set(options) { - var _this2 = this; - var fragment = document.createDocumentFragment(); - - var addOptionToFragment = function addOptionToFragment(data) { - // Create a standard select option - var option = _this2.template(data); // Append it to fragment + var placeholderItem = this.itemList.getChild("." + this.config.classNames.placeholder); + if (setLoading) { + this.disable(); + this.containerOuter.addLoadingState(); - fragment.appendChild(option); - }; // Add each list item to list + if (this._isSelectOneElement) { + if (!placeholderItem) { + placeholderItem = this._getTemplate('placeholder', this.config.loadingText); + if (placeholderItem) { + this.itemList.append(placeholderItem); + } + } else { + placeholderItem.innerHTML = this.config.loadingText; + } + } else { + this.input.placeholder = this.config.loadingText; + } + } else { + this.enable(); + this.containerOuter.removeLoadingState(); - options.forEach(function (optionData) { - return addOptionToFragment(optionData); - }); - this.appendDocFragment(fragment); + if (this._isSelectOneElement) { + if (placeholderItem) { + placeholderItem.innerHTML = this._placeholderValue || ''; + } + } else { + this.input.placeholder = this._placeholderValue || ''; + } } - }]); - - return WrappedSelect; -}(wrapped_element_WrappedElement); - + }; -// CONCATENATED MODULE: ./src/scripts/components/index.js + Choices.prototype._handleSearch = function (value) { + if (!value || !this.input.isFocussed) { + return; + } + var choices = this._store.choices; + var _a = this.config, + searchFloor = _a.searchFloor, + searchChoices = _a.searchChoices; + var hasUnactiveChoices = choices.some(function (option) { + return !option.active; + }); // Check that we have a value to search and the input was an alphanumeric character + if (value && value.length >= searchFloor) { + var resultCount = searchChoices ? this._searchChoices(value) : 0; // Trigger search event + this.passedElement.triggerEvent(constants_1.EVENTS.search, { + value: value, + resultCount: resultCount + }); + } else if (hasUnactiveChoices) { + // Otherwise reset choices to active + this._isSearching = false; + this._store.dispatch(choices_1.activateChoices(true)); + } + }; + Choices.prototype._canAddItem = function (activeItems, value) { + var canAddItem = true; + var notice = typeof this.config.addItemText === 'function' ? this.config.addItemText(value) : this.config.addItemText; + if (!this._isSelectOneElement) { + var isDuplicateValue = utils_1.existsInArray(activeItems, value); -// CONCATENATED MODULE: ./src/scripts/templates.js -/** - * Helpers to create HTML elements used by Choices - * Can be overridden by providing `callbackOnCreateTemplates` option - * @typedef {import('../../types/index').Choices.Templates} Templates - * @typedef {import('../../types/index').Choices.ClassNames} ClassNames - * @typedef {import('../../types/index').Choices.Options} Options - * @typedef {import('../../types/index').Choices.Item} Item - * @typedef {import('../../types/index').Choices.Choice} Choice - * @typedef {import('../../types/index').Choices.Group} Group - */ -var TEMPLATES = -/** @type {Templates} */ -{ - /** - * @param {Partial} classNames - * @param {"ltr" | "rtl" | "auto"} dir - * @param {boolean} isSelectElement - * @param {boolean} isSelectOneElement - * @param {boolean} searchEnabled - * @param {"select-one" | "select-multiple" | "text"} passedElementType - */ - containerOuter: function containerOuter(_ref, dir, isSelectElement, isSelectOneElement, searchEnabled, passedElementType) { - var _containerOuter = _ref.containerOuter; - var div = Object.assign(document.createElement('div'), { - className: _containerOuter - }); - div.dataset.type = passedElementType; + if (this.config.maxItemCount > 0 && this.config.maxItemCount <= activeItems.length) { + // If there is a max entry limit and we have reached that limit + // don't update + canAddItem = false; + notice = typeof this.config.maxItemText === 'function' ? this.config.maxItemText(this.config.maxItemCount) : this.config.maxItemText; + } - if (dir) { - div.dir = dir; - } + if (!this.config.duplicateItemsAllowed && isDuplicateValue && canAddItem) { + canAddItem = false; + notice = typeof this.config.uniqueItemText === 'function' ? this.config.uniqueItemText(value) : this.config.uniqueItemText; + } - if (isSelectOneElement) { - div.tabIndex = 0; + if (this._isTextElement && this.config.addItems && canAddItem && typeof this.config.addItemFilter === 'function' && !this.config.addItemFilter(value)) { + canAddItem = false; + notice = typeof this.config.customAddItemText === 'function' ? this.config.customAddItemText(value) : this.config.customAddItemText; + } } - if (isSelectElement) { - div.setAttribute('role', searchEnabled ? 'combobox' : 'listbox'); + return { + response: canAddItem, + notice: notice + }; + }; - if (searchEnabled) { - div.setAttribute('aria-autocomplete', 'list'); - } - } + Choices.prototype._searchChoices = function (value) { + var newValue = typeof value === 'string' ? value.trim() : value; + var currentValue = typeof this._currentValue === 'string' ? this._currentValue.trim() : this._currentValue; - div.setAttribute('aria-haspopup', 'true'); - div.setAttribute('aria-expanded', 'false'); - return div; - }, + if (newValue.length < 1 && newValue === currentValue + " ") { + return 0; + } // If new value matches the desired length and is not the same as the current value with a space - /** - * @param {Partial} classNames - */ - containerInner: function containerInner(_ref2) { - var _containerInner = _ref2.containerInner; - return Object.assign(document.createElement('div'), { - className: _containerInner - }); - }, - /** - * @param {Partial} classNames - * @param {boolean} isSelectOneElement - */ - itemList: function itemList(_ref3, isSelectOneElement) { - var list = _ref3.list, - listSingle = _ref3.listSingle, - listItems = _ref3.listItems; - return Object.assign(document.createElement('div'), { - className: list + " " + (isSelectOneElement ? listSingle : listItems) - }); - }, + var haystack = this._store.searchableChoices; + var needle = newValue; - /** - * @param {Partial} classNames - * @param {string} value - */ - placeholder: function placeholder(_ref4, value) { - var _placeholder = _ref4.placeholder; - return Object.assign(document.createElement('div'), { - className: _placeholder, - innerHTML: value - }); - }, + var keys = __spreadArrays(this.config.searchFields); - /** - * @param {Partial} classNames - * @param {Item} item - * @param {boolean} removeItemButton - */ - item: function item(_ref5, _ref6, removeItemButton) { - var _item = _ref5.item, - button = _ref5.button, - highlightedState = _ref5.highlightedState, - itemSelectable = _ref5.itemSelectable, - placeholder = _ref5.placeholder; - var id = _ref6.id, - value = _ref6.value, - label = _ref6.label, - customProperties = _ref6.customProperties, - active = _ref6.active, - disabled = _ref6.disabled, - highlighted = _ref6.highlighted, - isPlaceholder = _ref6.placeholder; - var div = Object.assign(document.createElement('div'), { - className: _item, - innerHTML: label - }); - Object.assign(div.dataset, { - item: '', - id: id, - value: value, - customProperties: customProperties + var options = Object.assign(this.config.fuseOptions, { + keys: keys, + includeMatches: true }); + var fuse = new fuse_js_1.default(haystack, options); + var results = fuse.search(needle); // see https://github.com/krisk/Fuse/issues/303 - if (active) { - div.setAttribute('aria-selected', 'true'); - } + this._currentValue = newValue; + this._highlightPosition = 0; + this._isSearching = true; - if (disabled) { - div.setAttribute('aria-disabled', 'true'); - } + this._store.dispatch(choices_1.filterChoices(results)); - if (isPlaceholder) { - div.classList.add(placeholder); - } + return results.length; + }; - div.classList.add(highlighted ? highlightedState : itemSelectable); + Choices.prototype._addEventListeners = function () { + var documentElement = document.documentElement; // capture events - can cancel event processing or propagation - if (removeItemButton) { - if (disabled) { - div.classList.remove(itemSelectable); - } + documentElement.addEventListener('touchend', this._onTouchEnd, true); + this.containerOuter.element.addEventListener('keydown', this._onKeyDown, true); + this.containerOuter.element.addEventListener('mousedown', this._onMouseDown, true); // passive events - doesn't call `preventDefault` or `stopPropagation` - div.dataset.deletable = ''; - /** @todo This MUST be localizable, not hardcoded! */ + documentElement.addEventListener('click', this._onClick, { + passive: true + }); + documentElement.addEventListener('touchmove', this._onTouchMove, { + passive: true + }); + this.dropdown.element.addEventListener('mouseover', this._onMouseOver, { + passive: true + }); - var REMOVE_ITEM_TEXT = 'Remove item'; - var removeButton = Object.assign(document.createElement('button'), { - type: 'button', - className: button, - innerHTML: REMOVE_ITEM_TEXT + if (this._isSelectOneElement) { + this.containerOuter.element.addEventListener('focus', this._onFocus, { + passive: true + }); + this.containerOuter.element.addEventListener('blur', this._onBlur, { + passive: true }); - removeButton.setAttribute('aria-label', REMOVE_ITEM_TEXT + ": '" + value + "'"); - removeButton.dataset.button = ''; - div.appendChild(removeButton); } - return div; - }, - - /** - * @param {Partial} classNames - * @param {boolean} isSelectOneElement - */ - choiceList: function choiceList(_ref7, isSelectOneElement) { - var list = _ref7.list; - var div = Object.assign(document.createElement('div'), { - className: list + this.input.element.addEventListener('keyup', this._onKeyUp, { + passive: true + }); + this.input.element.addEventListener('focus', this._onFocus, { + passive: true + }); + this.input.element.addEventListener('blur', this._onBlur, { + passive: true }); - if (!isSelectOneElement) { - div.setAttribute('aria-multiselectable', 'true'); + if (this.input.element.form) { + this.input.element.form.addEventListener('reset', this._onFormReset, { + passive: true + }); } - div.setAttribute('role', 'listbox'); - return div; - }, + this.input.addEventListeners(); + }; - /** - * @param {Partial} classNames - * @param {Group} group - */ - choiceGroup: function choiceGroup(_ref8, _ref9) { - var group = _ref8.group, - groupHeading = _ref8.groupHeading, - itemDisabled = _ref8.itemDisabled; - var id = _ref9.id, - value = _ref9.value, - disabled = _ref9.disabled; - var div = Object.assign(document.createElement('div'), { - className: group + " " + (disabled ? itemDisabled : '') - }); - div.setAttribute('role', 'group'); - Object.assign(div.dataset, { - group: '', - id: id, - value: value - }); + Choices.prototype._removeEventListeners = function () { + var documentElement = document.documentElement; + documentElement.removeEventListener('touchend', this._onTouchEnd, true); + this.containerOuter.element.removeEventListener('keydown', this._onKeyDown, true); + this.containerOuter.element.removeEventListener('mousedown', this._onMouseDown, true); + documentElement.removeEventListener('click', this._onClick); + documentElement.removeEventListener('touchmove', this._onTouchMove); + this.dropdown.element.removeEventListener('mouseover', this._onMouseOver); - if (disabled) { - div.setAttribute('aria-disabled', 'true'); + if (this._isSelectOneElement) { + this.containerOuter.element.removeEventListener('focus', this._onFocus); + this.containerOuter.element.removeEventListener('blur', this._onBlur); } - div.appendChild(Object.assign(document.createElement('div'), { - className: groupHeading, - innerHTML: value - })); - return div; - }, - - /** - * @param {Partial} classNames - * @param {Choice} choice - * @param {Options['itemSelectText']} selectText - */ - choice: function choice(_ref10, _ref11, selectText) { - var item = _ref10.item, - itemChoice = _ref10.itemChoice, - itemSelectable = _ref10.itemSelectable, - selectedState = _ref10.selectedState, - itemDisabled = _ref10.itemDisabled, - placeholder = _ref10.placeholder; - var id = _ref11.id, - value = _ref11.value, - label = _ref11.label, - groupId = _ref11.groupId, - elementId = _ref11.elementId, - isDisabled = _ref11.disabled, - isSelected = _ref11.selected, - isPlaceholder = _ref11.placeholder; - var div = Object.assign(document.createElement('div'), { - id: elementId, - innerHTML: label, - className: item + " " + itemChoice - }); + this.input.element.removeEventListener('keyup', this._onKeyUp); + this.input.element.removeEventListener('focus', this._onFocus); + this.input.element.removeEventListener('blur', this._onBlur); - if (isSelected) { - div.classList.add(selectedState); + if (this.input.element.form) { + this.input.element.form.removeEventListener('reset', this._onFormReset); } - if (isPlaceholder) { - div.classList.add(placeholder); - } + this.input.removeEventListeners(); + }; - div.setAttribute('role', groupId > 0 ? 'treeitem' : 'option'); - Object.assign(div.dataset, { - choice: '', - id: id, - value: value, - selectText: selectText - }); + Choices.prototype._onKeyDown = function (event) { + var keyCode = event.keyCode; + var activeItems = this._store.activeItems; + var hasFocusedInput = this.input.isFocussed; + var hasActiveDropdown = this.dropdown.isActive; + var hasItems = this.itemList.hasChildren(); + var keyString = String.fromCharCode(keyCode); + var wasAlphaNumericChar = /[a-zA-Z0-9-_ ]/.test(keyString); + var BACK_KEY = constants_1.KEY_CODES.BACK_KEY, + DELETE_KEY = constants_1.KEY_CODES.DELETE_KEY, + ENTER_KEY = constants_1.KEY_CODES.ENTER_KEY, + A_KEY = constants_1.KEY_CODES.A_KEY, + ESC_KEY = constants_1.KEY_CODES.ESC_KEY, + UP_KEY = constants_1.KEY_CODES.UP_KEY, + DOWN_KEY = constants_1.KEY_CODES.DOWN_KEY, + PAGE_UP_KEY = constants_1.KEY_CODES.PAGE_UP_KEY, + PAGE_DOWN_KEY = constants_1.KEY_CODES.PAGE_DOWN_KEY; - if (isDisabled) { - div.classList.add(itemDisabled); - div.dataset.choiceDisabled = ''; - div.setAttribute('aria-disabled', 'true'); - } else { - div.classList.add(itemSelectable); - div.dataset.choiceSelectable = ''; + if (!this._isTextElement && !hasActiveDropdown && wasAlphaNumericChar) { + this.showDropdown(); + + if (!this.input.isFocussed) { + /* + We update the input value with the pressed key as + the input was not focussed at the time of key press + therefore does not have the value of the key. + */ + this.input.value += keyString.toLowerCase(); + } } - return div; - }, + switch (keyCode) { + case A_KEY: + return this._onSelectKey(event, hasItems); - /** - * @param {Partial} classNames - * @param {string} placeholderValue - */ - input: function input(_ref12, placeholderValue) { - var _input = _ref12.input, - inputCloned = _ref12.inputCloned; - var inp = Object.assign(document.createElement('input'), { - type: 'text', - className: _input + " " + inputCloned, - autocomplete: 'off', - autocapitalize: 'off', - spellcheck: false - }); - inp.setAttribute('role', 'textbox'); - inp.setAttribute('aria-autocomplete', 'list'); - inp.setAttribute('aria-label', placeholderValue); - return inp; - }, + case ENTER_KEY: + return this._onEnterKey(event, activeItems, hasActiveDropdown); - /** - * @param {Partial} classNames - */ - dropdown: function dropdown(_ref13) { - var list = _ref13.list, - listDropdown = _ref13.listDropdown; - var div = document.createElement('div'); - div.classList.add(list, listDropdown); - div.setAttribute('aria-expanded', 'false'); - return div; - }, + case ESC_KEY: + return this._onEscapeKey(hasActiveDropdown); - /** - * - * @param {Partial} classNames - * @param {string} innerHTML - * @param {"no-choices" | "no-results" | ""} type - */ - notice: function notice(_ref14, innerHTML, type) { - var item = _ref14.item, - itemChoice = _ref14.itemChoice, - noResults = _ref14.noResults, - noChoices = _ref14.noChoices; + case UP_KEY: + case PAGE_UP_KEY: + case DOWN_KEY: + case PAGE_DOWN_KEY: + return this._onDirectionKey(event, hasActiveDropdown); - if (type === void 0) { - type = ''; + case DELETE_KEY: + case BACK_KEY: + return this._onDeleteKey(event, activeItems, hasFocusedInput); + + default: } + }; - var classes = [item, itemChoice]; + Choices.prototype._onKeyUp = function (_a) { + var target = _a.target, + keyCode = _a.keyCode; + var value = this.input.value; + var activeItems = this._store.activeItems; - if (type === 'no-choices') { - classes.push(noChoices); - } else if (type === 'no-results') { - classes.push(noResults); - } + var canAddItem = this._canAddItem(activeItems, value); - return Object.assign(document.createElement('div'), { - innerHTML: innerHTML, - className: classes.join(' ') - }); - }, + var backKey = constants_1.KEY_CODES.BACK_KEY, + deleteKey = constants_1.KEY_CODES.DELETE_KEY; // We are typing into a text input and have a value, we want to show a dropdown + // notice. Otherwise hide the dropdown - /** - * @param {Item} option - */ - option: function option(_ref15) { - var label = _ref15.label, - value = _ref15.value, - customProperties = _ref15.customProperties, - active = _ref15.active, - disabled = _ref15.disabled; - var opt = new Option(label, value, false, active); + if (this._isTextElement) { + var canShowDropdownNotice = canAddItem.notice && value; - if (customProperties) { - opt.dataset.customProperties = customProperties; - } + if (canShowDropdownNotice) { + var dropdownItem = this._getTemplate('notice', canAddItem.notice); - opt.disabled = disabled; - return opt; - } -}; -/* harmony default export */ var templates = (TEMPLATES); -// CONCATENATED MODULE: ./src/scripts/actions/choices.js -/** - * @typedef {import('redux').Action} Action - * @typedef {import('../../../types/index').Choices.Choice} Choice - */ + this.dropdown.element.innerHTML = dropdownItem.outerHTML; + this.showDropdown(true); + } else { + this.hideDropdown(true); + } + } else { + var wasRemovalKeyCode = keyCode === backKey || keyCode === deleteKey; + var userHasRemovedValue = wasRemovalKeyCode && target && !target.value; + var canReactivateChoices = !this._isTextElement && this._isSearching; + var canSearch = this._canSearch && canAddItem.response; -/** - * @argument {Choice} choice - * @returns {Action & Choice} - */ + if (userHasRemovedValue && canReactivateChoices) { + this._isSearching = false; -var choices_addChoice = function addChoice(_ref) { - var value = _ref.value, - label = _ref.label, - id = _ref.id, - groupId = _ref.groupId, - disabled = _ref.disabled, - elementId = _ref.elementId, - customProperties = _ref.customProperties, - placeholder = _ref.placeholder, - keyCode = _ref.keyCode; - return { - type: ACTION_TYPES.ADD_CHOICE, - value: value, - label: label, - id: id, - groupId: groupId, - disabled: disabled, - elementId: elementId, - customProperties: customProperties, - placeholder: placeholder, - keyCode: keyCode - }; -}; -/** - * @argument {Choice[]} results - * @returns {Action & { results: Choice[] }} - */ + this._store.dispatch(choices_1.activateChoices(true)); + } else if (canSearch) { + this._handleSearch(this.input.value); + } + } -var choices_filterChoices = function filterChoices(results) { - return { - type: ACTION_TYPES.FILTER_CHOICES, - results: results + this._canSearch = this.config.searchEnabled; }; -}; -/** - * @argument {boolean} active - * @returns {Action & { active: boolean }} - */ -var choices_activateChoices = function activateChoices(active) { - if (active === void 0) { - active = true; - } + Choices.prototype._onSelectKey = function (event, hasItems) { + var ctrlKey = event.ctrlKey, + metaKey = event.metaKey; + var hasCtrlDownKeyPressed = ctrlKey || metaKey; // If CTRL + A or CMD + A have been pressed and there are items to select - return { - type: ACTION_TYPES.ACTIVATE_CHOICES, - active: active - }; -}; -/** - * @returns {Action} - */ + if (hasCtrlDownKeyPressed && hasItems) { + this._canSearch = false; + var shouldHightlightAll = this.config.removeItems && !this.input.value && this.input.element === document.activeElement; -var choices_clearChoices = function clearChoices() { - return { - type: ACTION_TYPES.CLEAR_CHOICES + if (shouldHightlightAll) { + this.highlightAll(); + } + } }; -}; -// CONCATENATED MODULE: ./src/scripts/actions/items.js -/** - * @typedef {import('redux').Action} Action - * @typedef {import('../../../types/index').Choices.Item} Item - */ + Choices.prototype._onEnterKey = function (event, activeItems, hasActiveDropdown) { + var target = event.target; + var enterKey = constants_1.KEY_CODES.ENTER_KEY; + var targetWasButton = target && target.hasAttribute('data-button'); -/** - * @param {Item} item - * @returns {Action & Item} - */ + if (this._isTextElement && target && target.value) { + var value = this.input.value; -var items_addItem = function addItem(_ref) { - var value = _ref.value, - label = _ref.label, - id = _ref.id, - choiceId = _ref.choiceId, - groupId = _ref.groupId, - customProperties = _ref.customProperties, - placeholder = _ref.placeholder, - keyCode = _ref.keyCode; - return { - type: ACTION_TYPES.ADD_ITEM, - value: value, - label: label, - id: id, - choiceId: choiceId, - groupId: groupId, - customProperties: customProperties, - placeholder: placeholder, - keyCode: keyCode - }; -}; -/** - * @param {string} id - * @param {string} choiceId - * @returns {Action & { id: string, choiceId: string }} - */ + var canAddItem = this._canAddItem(activeItems, value); -var items_removeItem = function removeItem(id, choiceId) { - return { - type: ACTION_TYPES.REMOVE_ITEM, - id: id, - choiceId: choiceId - }; -}; -/** - * @param {string} id - * @param {boolean} highlighted - * @returns {Action & { id: string, highlighted: boolean }} - */ + if (canAddItem.response) { + this.hideDropdown(true); -var items_highlightItem = function highlightItem(id, highlighted) { - return { - type: ACTION_TYPES.HIGHLIGHT_ITEM, - id: id, - highlighted: highlighted - }; -}; -// CONCATENATED MODULE: ./src/scripts/actions/groups.js + this._addItem({ + value: value + }); -/** - * @typedef {import('redux').Action} Action - * @typedef {import('../../../types/index').Choices.Group} Group - */ + this._triggerChange(value); -/** - * @param {Group} group - * @returns {Action & Group} - */ + this.clearInput(); + } + } -var groups_addGroup = function addGroup(_ref) { - var value = _ref.value, - id = _ref.id, - active = _ref.active, - disabled = _ref.disabled; - return { - type: ACTION_TYPES.ADD_GROUP, - value: value, - id: id, - active: active, - disabled: disabled - }; -}; -// CONCATENATED MODULE: ./src/scripts/actions/misc.js -/** - * @typedef {import('redux').Action} Action - */ + if (targetWasButton) { + this._handleButtonAction(activeItems, target); -/** - * @returns {Action} - */ -var clearAll = function clearAll() { - return { - type: 'CLEAR_ALL' - }; -}; -/** - * @param {any} state - * @returns {Action & { state: object }} - */ + event.preventDefault(); + } -var resetTo = function resetTo(state) { - return { - type: 'RESET_TO', - state: state + if (hasActiveDropdown) { + var highlightedChoice = this.dropdown.getChild("." + this.config.classNames.highlightedState); + + if (highlightedChoice) { + // add enter keyCode value + if (activeItems[0]) { + activeItems[0].keyCode = enterKey; // eslint-disable-line no-param-reassign + } + + this._handleChoiceAction(activeItems, highlightedChoice); + } + + event.preventDefault(); + } else if (this._isSelectOneElement) { + this.showDropdown(); + event.preventDefault(); + } }; -}; -/** - * @param {boolean} isLoading - * @returns {Action & { isLoading: boolean }} - */ -var setIsLoading = function setIsLoading(isLoading) { - return { - type: 'SET_IS_LOADING', - isLoading: isLoading + Choices.prototype._onEscapeKey = function (hasActiveDropdown) { + if (hasActiveDropdown) { + this.hideDropdown(true); + this.containerOuter.focus(); + } }; -}; -// CONCATENATED MODULE: ./src/scripts/choices.js -function choices_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } -function choices_createClass(Constructor, protoProps, staticProps) { if (protoProps) choices_defineProperties(Constructor.prototype, protoProps); if (staticProps) choices_defineProperties(Constructor, staticProps); return Constructor; } + Choices.prototype._onDirectionKey = function (event, hasActiveDropdown) { + var keyCode = event.keyCode, + metaKey = event.metaKey; + var downKey = constants_1.KEY_CODES.DOWN_KEY, + pageUpKey = constants_1.KEY_CODES.PAGE_UP_KEY, + pageDownKey = constants_1.KEY_CODES.PAGE_DOWN_KEY; // If up or down key is pressed, traverse through options + + if (hasActiveDropdown || this._isSelectOneElement) { + this.showDropdown(); + this._canSearch = false; + var directionInt = keyCode === downKey || keyCode === pageDownKey ? 1 : -1; + var skipKey = metaKey || keyCode === pageDownKey || keyCode === pageUpKey; + var selectableChoiceIdentifier = '[data-choice-selectable]'; + var nextEl = void 0; + if (skipKey) { + if (directionInt > 0) { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier + ":last-of-type"); + } else { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); + } + } else { + var currentEl = this.dropdown.element.querySelector("." + this.config.classNames.highlightedState); + if (currentEl) { + nextEl = utils_1.getAdjacentEl(currentEl, selectableChoiceIdentifier, directionInt); + } else { + nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); + } + } + if (nextEl) { + // We prevent default to stop the cursor moving + // when pressing the arrow + if (!utils_1.isScrolledIntoView(nextEl, this.choiceList.element, directionInt)) { + this.choiceList.scrollToChildElement(nextEl, directionInt); + } + this._highlightChoice(nextEl); + } // Prevent default to maintain cursor position whilst + // traversing dropdown options + event.preventDefault(); + } + }; + Choices.prototype._onDeleteKey = function (event, activeItems, hasFocusedInput) { + var target = event.target; // If backspace or delete key is pressed and the input has no value + if (!this._isSelectOneElement && !target.value && hasFocusedInput) { + this._handleBackspace(activeItems); + event.preventDefault(); + } + }; + Choices.prototype._onTouchMove = function () { + if (this._wasTap) { + this._wasTap = false; + } + }; + Choices.prototype._onTouchEnd = function (event) { + var target = (event || event.touches[0]).target; + var touchWasWithinContainer = this._wasTap && this.containerOuter.element.contains(target); -/** @see {@link http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c} */ + if (touchWasWithinContainer) { + var containerWasExactTarget = target === this.containerOuter.element || target === this.containerInner.element; -var IS_IE11 = '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style; -/** - * @typedef {import('../../types/index').Choices.Choice} Choice - * @typedef {import('../../types/index').Choices.Item} Item - * @typedef {import('../../types/index').Choices.Group} Group - * @typedef {import('../../types/index').Choices.Options} Options - */ + if (containerWasExactTarget) { + if (this._isTextElement) { + this.input.focus(); + } else if (this._isSelectMultipleElement) { + this.showDropdown(); + } + } // Prevents focus event firing -/** @type {Partial} */ -var USER_DEFAULTS = {}; -/** - * Choices - * @author Josh Johnson - */ + event.stopPropagation(); + } -var choices_Choices = -/*#__PURE__*/ -function () { - choices_createClass(Choices, null, [{ - key: "defaults", - get: function get() { - return Object.preventExtensions({ - get options() { - return USER_DEFAULTS; - }, + this._wasTap = true; + }; + /** + * Handles mousedown event in capture mode for containetOuter.element + */ - get templates() { - return TEMPLATES; - } - }); - } - /** - * @param {string | HTMLInputElement | HTMLSelectElement} element - * @param {Partial} userConfig - */ + Choices.prototype._onMouseDown = function (event) { + var target = event.target; - }]); + if (!(target instanceof HTMLElement)) { + return; + } // If we have our mouse down on the scrollbar and are on IE11... - function Choices(element, userConfig) { - var _this = this; - if (element === void 0) { - element = '[data-choice]'; + if (IS_IE11 && this.choiceList.element.contains(target)) { + // check if click was on a scrollbar area + var firstChoice = this.choiceList.element.firstElementChild; + var isOnScrollbar = this._direction === 'ltr' ? event.offsetX >= firstChoice.offsetWidth : event.offsetX < firstChoice.offsetLeft; + this._isScrollingOnIe = isOnScrollbar; } - if (userConfig === void 0) { - userConfig = {}; + if (target === this.input.element) { + return; } - /** @type {Partial} */ - this.config = cjs_default.a.all([DEFAULT_CONFIG, Choices.defaults.options, userConfig], // When merging array configs, replace with a copy of the userConfig array, - // instead of concatenating with the default array - { - arrayMerge: function arrayMerge(_, sourceArray) { - return [].concat(sourceArray); - } - }); - var invalidConfigOptions = diff(this.config, DEFAULT_CONFIG); + var item = target.closest('[data-button],[data-item],[data-choice]'); - if (invalidConfigOptions.length) { - console.warn('Unknown config option(s) passed', invalidConfigOptions.join(', ')); + if (item instanceof HTMLElement) { + var hasShiftKey = event.shiftKey; + var activeItems = this._store.activeItems; + var dataset = item.dataset; + + if ('button' in dataset) { + this._handleButtonAction(activeItems, item); + } else if ('item' in dataset) { + this._handleItemAction(activeItems, item, hasShiftKey); + } else if ('choice' in dataset) { + this._handleChoiceAction(activeItems, item); + } } - var passedElement = typeof element === 'string' ? document.querySelector(element) : element; + event.preventDefault(); + }; + /** + * Handles mouseover event over this.dropdown + * @param {MouseEvent} event + */ - if (!(passedElement instanceof HTMLInputElement || passedElement instanceof HTMLSelectElement)) { - throw TypeError('Expected one of the following types text|select-one|select-multiple'); - } - this._isTextElement = passedElement.type === TEXT_TYPE; - this._isSelectOneElement = passedElement.type === SELECT_ONE_TYPE; - this._isSelectMultipleElement = passedElement.type === SELECT_MULTIPLE_TYPE; - this._isSelectElement = this._isSelectOneElement || this._isSelectMultipleElement; - this.config.searchEnabled = this._isSelectMultipleElement || this.config.searchEnabled; + Choices.prototype._onMouseOver = function (_a) { + var target = _a.target; - if (!['auto', 'always'].includes(this.config.renderSelectedChoices)) { - this.config.renderSelectedChoices = 'auto'; + if (target instanceof HTMLElement && 'choice' in target.dataset) { + this._highlightChoice(target); } + }; - if (userConfig.addItemFilter && typeof userConfig.addItemFilter !== 'function') { - var re = userConfig.addItemFilter instanceof RegExp ? userConfig.addItemFilter : new RegExp(userConfig.addItemFilter); - this.config.addItemFilter = re.test.bind(re); - } + Choices.prototype._onClick = function (_a) { + var target = _a.target; + var clickWasWithinContainer = this.containerOuter.element.contains(target); - if (this._isTextElement) { - this.passedElement = new WrappedInput({ - element: passedElement, - classNames: this.config.classNames, - delimiter: this.config.delimiter - }); - } else { - this.passedElement = new WrappedSelect({ - element: passedElement, - classNames: this.config.classNames, - template: function template(data) { - return _this._templates.option(data); + if (clickWasWithinContainer) { + if (!this.dropdown.isActive && !this.containerOuter.isDisabled) { + if (this._isTextElement) { + if (document.activeElement !== this.input.element) { + this.input.focus(); + } + } else { + this.showDropdown(); + this.containerOuter.focus(); } - }); - } + } else if (this._isSelectOneElement && target !== this.input.element && !this.dropdown.element.contains(target)) { + this.hideDropdown(); + } + } else { + var hasHighlightedItems = this._store.highlightedActiveItems.length > 0; - this.initialised = false; - this._store = new store_Store(); - this._initialState = {}; - this._currentState = {}; - this._prevState = {}; - this._currentValue = ''; - this._canSearch = this.config.searchEnabled; - this._isScrollingOnIe = false; - this._highlightPosition = 0; - this._wasTap = true; - this._placeholderValue = this._generatePlaceholderValue(); - this._baseId = generateId(this.passedElement.element, 'choices-'); - /** - * setting direction in cases where it's explicitly set on passedElement - * or when calculated direction is different from the document - * @type {HTMLElement['dir']} - */ + if (hasHighlightedItems) { + this.unhighlightAll(); + } - this._direction = this.passedElement.dir; + this.containerOuter.removeFocusState(); + this.hideDropdown(true); + } + }; - if (!this._direction) { - var _window$getComputedSt = window.getComputedStyle(this.passedElement.element), - elementDirection = _window$getComputedSt.direction; + Choices.prototype._onFocus = function (_a) { + var _b; + + var _this = this; - var _window$getComputedSt2 = window.getComputedStyle(document.documentElement), - documentDirection = _window$getComputedSt2.direction; + var target = _a.target; + var focusWasWithinContainer = target && this.containerOuter.element.contains(target); - if (elementDirection !== documentDirection) { - this._direction = elementDirection; - } + if (!focusWasWithinContainer) { + return; } - this._idNames = { - itemChoice: 'item-choice' - }; // Assign preset groups from passed element + var focusActions = (_b = {}, _b[constants_1.TEXT_TYPE] = function () { + if (target === _this.input.element) { + _this.containerOuter.addFocusState(); + } + }, _b[constants_1.SELECT_ONE_TYPE] = function () { + _this.containerOuter.addFocusState(); - this._presetGroups = this.passedElement.optionGroups; // Assign preset options from passed element + if (target === _this.input.element) { + _this.showDropdown(true); + } + }, _b[constants_1.SELECT_MULTIPLE_TYPE] = function () { + if (target === _this.input.element) { + _this.showDropdown(true); // If element is a select box, the focused element is the container and the dropdown + // isn't already open, focus and show dropdown - this._presetOptions = this.passedElement.options; // Assign preset choices from passed object - this._presetChoices = this.config.choices; // Assign preset items from passed object first + _this.containerOuter.addFocusState(); + } + }, _b); + focusActions[this.passedElement.element.type](); + }; - this._presetItems = this.config.items; // Add any values passed from attribute + Choices.prototype._onBlur = function (_a) { + var _b; - if (this.passedElement.value) { - this._presetItems = this._presetItems.concat(this.passedElement.value.split(this.config.delimiter)); - } // Create array of choices from option elements + var _this = this; + var target = _a.target; + var blurWasWithinContainer = target && this.containerOuter.element.contains(target); - if (this.passedElement.options) { - this.passedElement.options.forEach(function (o) { - _this._presetChoices.push({ - value: o.value, - label: o.innerHTML, - selected: o.selected, - disabled: o.disabled || o.parentNode.disabled, - placeholder: o.value === '' || o.hasAttribute('placeholder'), - customProperties: o.getAttribute('data-custom-properties') - }); + if (blurWasWithinContainer && !this._isScrollingOnIe) { + var activeItems = this._store.activeItems; + var hasHighlightedItems_1 = activeItems.some(function (item) { + return item.highlighted; }); - } + var blurActions = (_b = {}, _b[constants_1.TEXT_TYPE] = function () { + if (target === _this.input.element) { + _this.containerOuter.removeFocusState(); - this._render = this._render.bind(this); - this._onFocus = this._onFocus.bind(this); - this._onBlur = this._onBlur.bind(this); - this._onKeyUp = this._onKeyUp.bind(this); - this._onKeyDown = this._onKeyDown.bind(this); - this._onClick = this._onClick.bind(this); - this._onTouchMove = this._onTouchMove.bind(this); - this._onTouchEnd = this._onTouchEnd.bind(this); - this._onMouseDown = this._onMouseDown.bind(this); - this._onMouseOver = this._onMouseOver.bind(this); - this._onFormReset = this._onFormReset.bind(this); - this._onAKey = this._onAKey.bind(this); - this._onEnterKey = this._onEnterKey.bind(this); - this._onEscapeKey = this._onEscapeKey.bind(this); - this._onDirectionKey = this._onDirectionKey.bind(this); - this._onDeleteKey = this._onDeleteKey.bind(this); // If element has already been initialised with Choices, fail silently + if (hasHighlightedItems_1) { + _this.unhighlightAll(); + } - if (this.passedElement.isActive) { - if (!this.config.silent) { - console.warn('Trying to initialise Choices on element already initialised'); - } + _this.hideDropdown(true); + } + }, _b[constants_1.SELECT_ONE_TYPE] = function () { + _this.containerOuter.removeFocusState(); + + if (target === _this.input.element || target === _this.containerOuter.element && !_this._canSearch) { + _this.hideDropdown(true); + } + }, _b[constants_1.SELECT_MULTIPLE_TYPE] = function () { + if (target === _this.input.element) { + _this.containerOuter.removeFocusState(); + + _this.hideDropdown(true); + + if (hasHighlightedItems_1) { + _this.unhighlightAll(); + } + } + }, _b); + blurActions[this.passedElement.element.type](); + } else { + // On IE11, clicking the scollbar blurs our input and thus + // closes the dropdown. To stop this, we refocus our input + // if we know we are on IE *and* are scrolling. + this._isScrollingOnIe = false; + this.input.element.focus(); + } + }; - this.initialised = true; - return; - } // Let's go + Choices.prototype._onFormReset = function () { + this._store.dispatch(misc_1.resetTo(this._initialState)); + }; + Choices.prototype._highlightChoice = function (el) { + var _this = this; - this.init(); - } + if (el === void 0) { + el = null; + } - var _proto = Choices.prototype; + var choices = Array.from(this.dropdown.element.querySelectorAll('[data-choice-selectable]')); - _proto.init = function init() { - if (this.initialised) { + if (!choices.length) { return; } - this._createTemplates(); + var passedEl = el; + var highlightedChoices = Array.from(this.dropdown.element.querySelectorAll("." + this.config.classNames.highlightedState)); // Remove any highlighted choices - this._createElements(); + highlightedChoices.forEach(function (choice) { + choice.classList.remove(_this.config.classNames.highlightedState); + choice.setAttribute('aria-selected', 'false'); + }); - this._createStructure(); // Set initial state (We need to clone the state because some reducers - // modify the inner objects properties in the state) 🤢 + if (passedEl) { + this._highlightPosition = choices.indexOf(passedEl); + } else { + // Highlight choice based on last known highlight location + if (choices.length > this._highlightPosition) { + // If we have an option to highlight + passedEl = choices[this._highlightPosition]; + } else { + // Otherwise highlight the option before + passedEl = choices[choices.length - 1]; + } + if (!passedEl) { + passedEl = choices[0]; + } + } - this._initialState = cloneObject(this._store.state); + passedEl.classList.add(this.config.classNames.highlightedState); + passedEl.setAttribute('aria-selected', 'true'); + this.passedElement.triggerEvent(constants_1.EVENTS.highlightChoice, { + el: passedEl + }); - this._store.subscribe(this._render); + if (this.dropdown.isActive) { + // IE11 ignores aria-label and blocks virtual keyboard + // if aria-activedescendant is set without a dropdown + this.input.setActiveDescendant(passedEl.id); + this.containerOuter.setActiveDescendant(passedEl.id); + } + }; - this._render(); + Choices.prototype._addItem = function (_a) { + var value = _a.value, + _b = _a.label, + label = _b === void 0 ? null : _b, + _c = _a.choiceId, + choiceId = _c === void 0 ? -1 : _c, + _d = _a.groupId, + groupId = _d === void 0 ? -1 : _d, + _e = _a.customProperties, + customProperties = _e === void 0 ? {} : _e, + _f = _a.placeholder, + placeholder = _f === void 0 ? false : _f, + _g = _a.keyCode, + keyCode = _g === void 0 ? -1 : _g; + var passedValue = typeof value === 'string' ? value.trim() : value; + var items = this._store.items; + var passedLabel = label || passedValue; + var passedOptionId = choiceId || -1; + var group = groupId >= 0 ? this._store.getGroupById(groupId) : null; + var id = items ? items.length + 1 : 1; // If a prepended value has been passed, prepend it - this._addEventListeners(); + if (this.config.prependValue) { + passedValue = this.config.prependValue + passedValue.toString(); + } // If an appended value has been passed, append it - var shouldDisable = !this.config.addItems || this.passedElement.element.hasAttribute('disabled'); - if (shouldDisable) { - this.disable(); + if (this.config.appendValue) { + passedValue += this.config.appendValue.toString(); } - this.initialised = true; - var callbackOnInit = this.config.callbackOnInit; // Run callback if it is a function + this._store.dispatch(items_1.addItem({ + value: passedValue, + label: passedLabel, + id: id, + choiceId: passedOptionId, + groupId: groupId, + customProperties: customProperties, + placeholder: placeholder, + keyCode: keyCode + })); - if (callbackOnInit && typeof callbackOnInit === 'function') { - callbackOnInit.call(this); - } + if (this._isSelectOneElement) { + this.removeActiveItems(id); + } // Trigger change event + + + this.passedElement.triggerEvent(constants_1.EVENTS.addItem, { + id: id, + value: passedValue, + label: passedLabel, + customProperties: customProperties, + groupValue: group && group.value ? group.value : null, + keyCode: keyCode + }); }; - _proto.destroy = function destroy() { - if (!this.initialised) { + Choices.prototype._removeItem = function (item) { + var id = item.id, + value = item.value, + label = item.label, + customProperties = item.customProperties, + choiceId = item.choiceId, + groupId = item.groupId; + var group = groupId && groupId >= 0 ? this._store.getGroupById(groupId) : null; + + if (!id || !choiceId) { return; } - this._removeEventListeners(); + this._store.dispatch(items_1.removeItem(id, choiceId)); - this.passedElement.reveal(); - this.containerOuter.unwrap(this.passedElement.element); - this.clearStore(); + this.passedElement.triggerEvent(constants_1.EVENTS.removeItem, { + id: id, + value: value, + label: label, + customProperties: customProperties, + groupValue: group && group.value ? group.value : null + }); + }; - if (this._isSelectElement) { - this.passedElement.options = this._presetOptions; - } + Choices.prototype._addChoice = function (_a) { + var value = _a.value, + _b = _a.label, + label = _b === void 0 ? null : _b, + _c = _a.isSelected, + isSelected = _c === void 0 ? false : _c, + _d = _a.isDisabled, + isDisabled = _d === void 0 ? false : _d, + _e = _a.groupId, + groupId = _e === void 0 ? -1 : _e, + _f = _a.customProperties, + customProperties = _f === void 0 ? {} : _f, + _g = _a.placeholder, + placeholder = _g === void 0 ? false : _g, + _h = _a.keyCode, + keyCode = _h === void 0 ? -1 : _h; - this._templates = null; - this.initialised = false; - }; + if (typeof value === 'undefined' || value === null) { + return; + } // Generate unique id - _proto.enable = function enable() { - if (this.passedElement.isDisabled) { - this.passedElement.enable(); - } - if (this.containerOuter.isDisabled) { - this._addEventListeners(); + var choices = this._store.choices; + var choiceLabel = label || value; + var choiceId = choices ? choices.length + 1 : 1; + var choiceElementId = this._baseId + "-" + this._idNames.itemChoice + "-" + choiceId; - this.input.enable(); - this.containerOuter.enable(); - } + this._store.dispatch(choices_1.addChoice({ + id: choiceId, + groupId: groupId, + elementId: choiceElementId, + value: value, + label: choiceLabel, + disabled: isDisabled, + customProperties: customProperties, + placeholder: placeholder, + keyCode: keyCode + })); - return this; + if (isSelected) { + this._addItem({ + value: value, + label: choiceLabel, + choiceId: choiceId, + customProperties: customProperties, + placeholder: placeholder, + keyCode: keyCode + }); + } }; - _proto.disable = function disable() { - if (!this.passedElement.isDisabled) { - this.passedElement.disable(); - } + Choices.prototype._addGroup = function (_a) { + var _this = this; - if (!this.containerOuter.isDisabled) { - this._removeEventListeners(); + var group = _a.group, + id = _a.id, + _b = _a.valueKey, + valueKey = _b === void 0 ? 'value' : _b, + _c = _a.labelKey, + labelKey = _c === void 0 ? 'label' : _c; + var groupChoices = utils_1.isType('Object', group) ? group.choices : Array.from(group.getElementsByTagName('OPTION')); + var groupId = id || Math.floor(new Date().valueOf() * Math.random()); + var isDisabled = group.disabled ? group.disabled : false; - this.input.disable(); - this.containerOuter.disable(); - } + if (groupChoices) { + this._store.dispatch(groups_1.addGroup({ + value: group.label, + id: groupId, + active: true, + disabled: isDisabled + })); - return this; - }; + var addGroupChoices = function addGroupChoices(choice) { + var isOptDisabled = choice.disabled || choice.parentNode && choice.parentNode.disabled; - _proto.highlightItem = function highlightItem(item, runEvent) { - if (runEvent === void 0) { - runEvent = true; - } + _this._addChoice({ + value: choice[valueKey], + label: utils_1.isType('Object', choice) ? choice[labelKey] : choice.innerHTML, + isSelected: choice.selected, + isDisabled: isOptDisabled, + groupId: groupId, + customProperties: choice.customProperties, + placeholder: choice.placeholder + }); + }; - if (!item) { - return this; + groupChoices.forEach(addGroupChoices); + } else { + this._store.dispatch(groups_1.addGroup({ + value: group.label, + id: group.id, + active: false, + disabled: group.disabled + })); } + }; - var id = item.id, - _item$groupId = item.groupId, - groupId = _item$groupId === void 0 ? -1 : _item$groupId, - _item$value = item.value, - value = _item$value === void 0 ? '' : _item$value, - _item$label = item.label, - label = _item$label === void 0 ? '' : _item$label; - var group = groupId >= 0 ? this._store.getGroupById(groupId) : null; + Choices.prototype._getTemplate = function (template) { + var _a; - this._store.dispatch(items_highlightItem(id, true)); + var args = []; - if (runEvent) { - this.passedElement.triggerEvent(EVENTS.highlightItem, { - id: id, - value: value, - label: label, - groupValue: group && group.value ? group.value : null - }); + for (var _i = 1; _i < arguments.length; _i++) { + args[_i - 1] = arguments[_i]; } - return this; + var classNames = this.config.classNames; + return (_a = this._templates[template]).call.apply(_a, __spreadArrays([this, classNames], args)); }; - _proto.unhighlightItem = function unhighlightItem(item) { - if (!item) { - return this; - } + Choices.prototype._createTemplates = function () { + var callbackOnCreateTemplates = this.config.callbackOnCreateTemplates; + var userTemplates = {}; - var id = item.id, - _item$groupId2 = item.groupId, - groupId = _item$groupId2 === void 0 ? -1 : _item$groupId2, - _item$value2 = item.value, - value = _item$value2 === void 0 ? '' : _item$value2, - _item$label2 = item.label, - label = _item$label2 === void 0 ? '' : _item$label2; - var group = groupId >= 0 ? this._store.getGroupById(groupId) : null; + if (callbackOnCreateTemplates && typeof callbackOnCreateTemplates === 'function') { + userTemplates = callbackOnCreateTemplates.call(this, utils_1.strToEl); + } - this._store.dispatch(items_highlightItem(id, false)); + this._templates = deepmerge_1.default(templates_1.default, userTemplates); + }; - this.passedElement.triggerEvent(EVENTS.highlightItem, { - id: id, - value: value, - label: label, - groupValue: group && group.value ? group.value : null + Choices.prototype._createElements = function () { + this.containerOuter = new components_1.Container({ + element: this._getTemplate('containerOuter', this._direction, this._isSelectElement, this._isSelectOneElement, this.config.searchEnabled, this.passedElement.element.type), + classNames: this.config.classNames, + type: this.passedElement.element.type, + position: this.config.position + }); + this.containerInner = new components_1.Container({ + element: this._getTemplate('containerInner'), + classNames: this.config.classNames, + type: this.passedElement.element.type, + position: this.config.position + }); + this.input = new components_1.Input({ + element: this._getTemplate('input', this._placeholderValue), + classNames: this.config.classNames, + type: this.passedElement.element.type, + preventPaste: !this.config.paste + }); + this.choiceList = new components_1.List({ + element: this._getTemplate('choiceList', this._isSelectOneElement) + }); + this.itemList = new components_1.List({ + element: this._getTemplate('itemList', this._isSelectOneElement) + }); + this.dropdown = new components_1.Dropdown({ + element: this._getTemplate('dropdown'), + classNames: this.config.classNames, + type: this.passedElement.element.type }); - return this; }; - _proto.highlightAll = function highlightAll() { - var _this2 = this; - - this._store.items.forEach(function (item) { - return _this2.highlightItem(item); - }); + Choices.prototype._createStructure = function () { + // Hide original element + this.passedElement.conceal(); // Wrap input in container preserving DOM ordering - return this; - }; + this.containerInner.wrap(this.passedElement.element); // Wrapper inner container with outer container - _proto.unhighlightAll = function unhighlightAll() { - var _this3 = this; + this.containerOuter.wrap(this.containerInner.element); - this._store.items.forEach(function (item) { - return _this3.unhighlightItem(item); - }); + if (this._isSelectOneElement) { + this.input.placeholder = this.config.searchPlaceholderValue || ''; + } else if (this._placeholderValue) { + this.input.placeholder = this._placeholderValue; + this.input.setWidth(); + } - return this; - }; + this.containerOuter.element.appendChild(this.containerInner.element); + this.containerOuter.element.appendChild(this.dropdown.element); + this.containerInner.element.appendChild(this.itemList.element); - _proto.removeActiveItemsByValue = function removeActiveItemsByValue(value) { - var _this4 = this; + if (!this._isTextElement) { + this.dropdown.element.appendChild(this.choiceList.element); + } - this._store.activeItems.filter(function (item) { - return item.value === value; - }).forEach(function (item) { - return _this4._removeItem(item); - }); + if (!this._isSelectOneElement) { + this.containerInner.element.appendChild(this.input.element); + } else if (this.config.searchEnabled) { + this.dropdown.element.insertBefore(this.input.element, this.dropdown.element.firstChild); + } - return this; - }; + if (this._isSelectElement) { + this._highlightPosition = 0; + this._isSearching = false; - _proto.removeActiveItems = function removeActiveItems(excludedId) { - var _this5 = this; + this._startLoading(); - this._store.activeItems.filter(function (_ref) { - var id = _ref.id; - return id !== excludedId; - }).forEach(function (item) { - return _this5._removeItem(item); - }); + if (this._presetGroups.length) { + this._addPredefinedGroups(this._presetGroups); + } else { + this._addPredefinedChoices(this._presetChoices); + } - return this; + this._stopLoading(); + } + + if (this._isTextElement) { + this._addPredefinedItems(this._presetItems); + } }; - _proto.removeHighlightedItems = function removeHighlightedItems(runEvent) { - var _this6 = this; + Choices.prototype._addPredefinedGroups = function (groups) { + var _this = this; // If we have a placeholder option - if (runEvent === void 0) { - runEvent = false; - } - this._store.highlightedActiveItems.forEach(function (item) { - _this6._removeItem(item); // If this action was performed by the user - // trigger the event + var placeholderChoice = this.passedElement.placeholderOption; + if (placeholderChoice && placeholderChoice.parentNode && placeholderChoice.parentNode.tagName === 'SELECT') { + this._addChoice({ + value: placeholderChoice.value, + label: placeholderChoice.innerHTML, + isSelected: placeholderChoice.selected, + isDisabled: placeholderChoice.disabled, + placeholder: true + }); + } - if (runEvent) { - _this6._triggerChange(item.value); - } + groups.forEach(function (group) { + return _this._addGroup({ + group: group, + id: group.id || null + }); }); - - return this; }; - _proto.showDropdown = function showDropdown(preventInputFocus) { - var _this7 = this; + Choices.prototype._addPredefinedChoices = function (choices) { + var _this = this; // If sorting is enabled or the user is searching, filter choices - if (this.dropdown.isActive) { - return this; + + if (this.config.shouldSort) { + choices.sort(this.config.sorter); } - requestAnimationFrame(function () { - _this7.dropdown.show(); + var hasSelectedChoice = choices.some(function (choice) { + return choice.selected; + }); + var firstEnabledChoiceIndex = choices.findIndex(function (choice) { + return choice.disabled === undefined || !choice.disabled; + }); + choices.forEach(function (choice, index) { + var _a = choice.value, + value = _a === void 0 ? '' : _a, + label = choice.label, + customProperties = choice.customProperties, + placeholder = choice.placeholder; - _this7.containerOuter.open(_this7.dropdown.distanceFromTopWindow); + if (_this._isSelectElement) { + // If the choice is actually a group + if (choice.choices) { + _this._addGroup({ + group: choice, + id: choice.id || null + }); + } else { + /** + * If there is a selected choice already or the choice is not the first in + * the array, add each choice normally. + * + * Otherwise we pre-select the first enabled choice in the array ("select-one" only) + */ + var shouldPreselect = _this._isSelectOneElement && !hasSelectedChoice && index === firstEnabledChoiceIndex; + var isSelected = shouldPreselect ? true : choice.selected; + var isDisabled = choice.disabled; + console.log(isDisabled, choice); - if (!preventInputFocus && _this7._canSearch) { - _this7.input.focus(); + _this._addChoice({ + value: value, + label: label, + isSelected: !!isSelected, + isDisabled: !!isDisabled, + placeholder: !!placeholder, + customProperties: customProperties + }); + } + } else { + _this._addChoice({ + value: value, + label: label, + isSelected: !!choice.selected, + isDisabled: !!choice.disabled, + placeholder: !!choice.placeholder, + customProperties: customProperties + }); } - - _this7.passedElement.triggerEvent(EVENTS.showDropdown, {}); }); - return this; }; - _proto.hideDropdown = function hideDropdown(preventInputBlur) { - var _this8 = this; - - if (!this.dropdown.isActive) { - return this; - } - - requestAnimationFrame(function () { - _this8.dropdown.hide(); - - _this8.containerOuter.close(); - - if (!preventInputBlur && _this8._canSearch) { - _this8.input.removeActiveDescendant(); + Choices.prototype._addPredefinedItems = function (items) { + var _this = this; - _this8.input.blur(); + items.forEach(function (item) { + if (typeof item === 'object' && item.value) { + _this._addItem({ + value: item.value, + label: item.label, + choiceId: item.id, + customProperties: item.customProperties, + placeholder: item.placeholder + }); } - _this8.passedElement.triggerEvent(EVENTS.hideDropdown, {}); + if (typeof item === 'string') { + _this._addItem({ + value: item + }); + } }); - return this; }; - _proto.getValue = function getValue(valueOnly) { - if (valueOnly === void 0) { - valueOnly = false; - } - - var values = this._store.activeItems.reduce(function (selectedItems, item) { - var itemValue = valueOnly ? item.value : item; - selectedItems.push(itemValue); - return selectedItems; - }, []); - - return this._isSelectOneElement ? values[0] : values; - } - /** - * @param {string[] | import('../../types/index').Choices.Item[]} items - */ - ; + Choices.prototype._setChoiceOrItem = function (item) { + var _this = this; - _proto.setValue = function setValue(items) { - var _this9 = this; + var itemType = utils_1.getType(item).toLowerCase(); + var handleType = { + object: function object() { + if (!item.value) { + return; + } // If we are dealing with a select input, we need to create an option first + // that is then selected. For text inputs we can just add items normally. - if (!this.initialised) { - return this; - } - items.forEach(function (value) { - return _this9._setChoiceOrItem(value); - }); - return this; + if (!_this._isTextElement) { + _this._addChoice({ + value: item.value, + label: item.label, + isSelected: true, + isDisabled: false, + customProperties: item.customProperties, + placeholder: item.placeholder + }); + } else { + _this._addItem({ + value: item.value, + label: item.label, + choiceId: item.id, + customProperties: item.customProperties, + placeholder: item.placeholder + }); + } + }, + string: function string() { + if (!_this._isTextElement) { + _this._addChoice({ + value: item, + label: item, + isSelected: true, + isDisabled: false + }); + } else { + _this._addItem({ + value: item + }); + } + } + }; + handleType[itemType](); }; - _proto.setChoiceByValue = function setChoiceByValue(value) { - var _this10 = this; - - if (!this.initialised || this._isTextElement) { - return this; - } // If only one value has been passed, convert to array - + Choices.prototype._findAndSelectChoiceByValue = function (value) { + var _this = this; - var choiceValue = Array.isArray(value) ? value : [value]; // Loop through each value and + var choices = this._store.choices; // Check 'value' property exists and the choice isn't already selected - choiceValue.forEach(function (val) { - return _this10._findAndSelectChoiceByValue(val); + var foundChoice = choices.find(function (choice) { + return _this.config.valueComparer(choice.value, value); }); - return this; - } - /** - * Set choices of select input via an array of objects (or function that returns array of object or promise of it), - * a value field name and a label field name. - * This behaves the same as passing items via the choices option but can be called after initialising Choices. - * This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices. - * Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc). - * - * **Input types affected:** select-one, select-multiple - * - * @template {Choice[] | ((instance: Choices) => object[] | Promise)} T - * @param {T} [choicesArrayOrFetcher] - * @param {string} [value = 'value'] - name of `value` field - * @param {string} [label = 'label'] - name of 'label' field - * @param {boolean} [replaceChoices = false] - whether to replace of add choices - * @returns {this | Promise} - * - * @example - * ```js - * const example = new Choices(element); - * - * example.setChoices([ - * {value: 'One', label: 'Label One', disabled: true}, - * {value: 'Two', label: 'Label Two', selected: true}, - * {value: 'Three', label: 'Label Three'}, - * ], 'value', 'label', false); - * ``` - * - * @example - * ```js - * const example = new Choices(element); - * - * example.setChoices(async () => { - * try { - * const items = await fetch('/items'); - * return items.json() - * } catch(err) { - * console.error(err) - * } - * }); - * ``` - * - * @example - * ```js - * const example = new Choices(element); - * - * example.setChoices([{ - * label: 'Group one', - * id: 1, - * disabled: false, - * choices: [ - * {value: 'Child One', label: 'Child One', selected: true}, - * {value: 'Child Two', label: 'Child Two', disabled: true}, - * {value: 'Child Three', label: 'Child Three'}, - * ] - * }, - * { - * label: 'Group two', - * id: 2, - * disabled: false, - * choices: [ - * {value: 'Child Four', label: 'Child Four', disabled: true}, - * {value: 'Child Five', label: 'Child Five'}, - * {value: 'Child Six', label: 'Child Six', customProperties: { - * description: 'Custom description about child six', - * random: 'Another random custom property' - * }}, - * ] - * }], 'value', 'label', false); - * ``` - */ - ; - - _proto.setChoices = function setChoices(choicesArrayOrFetcher, value, label, replaceChoices) { - var _this11 = this; - if (choicesArrayOrFetcher === void 0) { - choicesArrayOrFetcher = []; + if (foundChoice && !foundChoice.selected) { + this._addItem({ + value: foundChoice.value, + label: foundChoice.label, + choiceId: foundChoice.id, + groupId: foundChoice.groupId, + customProperties: foundChoice.customProperties, + placeholder: foundChoice.placeholder, + keyCode: foundChoice.keyCode + }); } + }; - if (value === void 0) { - value = 'value'; + Choices.prototype._generatePlaceholderValue = function () { + if (this._isSelectElement) { + var placeholderOption = this.passedElement.placeholderOption; + return placeholderOption ? placeholderOption.text : null; } - if (label === void 0) { - label = 'label'; - } + var _a = this.config, + placeholder = _a.placeholder, + placeholderValue = _a.placeholderValue; + var dataset = this.passedElement.element.dataset; - if (replaceChoices === void 0) { - replaceChoices = false; - } + if (placeholder) { + if (placeholderValue) { + return placeholderValue; + } - if (!this.initialised) { - throw new ReferenceError("setChoices was called on a non-initialized instance of Choices"); + if (dataset.placeholder) { + return dataset.placeholder; + } } - if (!this._isSelectElement) { - throw new TypeError("setChoices can't be used with INPUT based Choices"); - } + return null; + }; - if (typeof value !== 'string' || !value) { - throw new TypeError("value parameter must be a name of 'value' field in passed objects"); - } // Clear choices if needed + return Choices; +}(); +exports.default = Choices; - if (replaceChoices) { - this.clearChoices(); - } +/***/ }), +/* 9 */ +/***/ (function(module, exports, __webpack_require__) { - if (typeof choicesArrayOrFetcher === 'function') { - // it's a choices fetcher function - var fetcher = choicesArrayOrFetcher(this); +/*! + * Fuse.js v3.4.6 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2012-2017 Kirollos Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +!function(e,t){ true?module.exports=t():undefined}(this,function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=1)}([function(e,t){e.exports=function(e){return Array.isArray?Array.isArray(e):"[object Array]"===Object.prototype.toString.call(e)}},function(e,t,n){function r(e){return(r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}function o(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{limit:!1};this._log('---------\nSearch pattern: "'.concat(e,'"'));var n=this._prepareSearchers(e),r=n.tokenSearchers,o=n.fullSearcher,i=this._search(r,o),a=i.weights,s=i.results;return this._computeScore(a,s),this.options.shouldSort&&this._sort(s),t.limit&&"number"==typeof t.limit&&(s=s.slice(0,t.limit)),this._format(s)}},{key:"_prepareSearchers",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=[];if(this.options.tokenize)for(var n=e.split(this.options.tokenSeparator),r=0,o=n.length;r0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1?arguments[1]:void 0,n=this.list,r={},o=[];if("string"==typeof n[0]){for(var i=0,a=n.length;i1)throw new Error("Key weight has to be > 0 and <= 1");d=d.name}else s[d]={weight:1};this._analyze({key:d,value:this.options.getFn(l,d),record:l,index:c},{resultMap:r,results:o,tokenSearchers:e,fullSearcher:t})}return{weights:s,results:o}}},{key:"_analyze",value:function(e,t){var n=e.key,r=e.arrayIndex,o=void 0===r?-1:r,i=e.value,a=e.record,c=e.index,h=t.tokenSearchers,l=void 0===h?[]:h,u=t.fullSearcher,f=void 0===u?[]:u,d=t.resultMap,v=void 0===d?{}:d,p=t.results,g=void 0===p?[]:p;if(null!=i){var y=!1,m=-1,k=0;if("string"==typeof i){this._log("\nKey: ".concat(""===n?"-":n));var S=f.search(i);if(this._log('Full text: "'.concat(i,'", score: ').concat(S.score)),this.options.tokenize){for(var x=i.split(this.options.tokenSeparator),b=[],M=0;M-1&&(P=(P+m)/2),this._log("Score average:",P);var F=!this.options.tokenize||!this.options.matchAllTokens||k>=l.length;if(this._log("\nCheck Matches: ".concat(F)),(y||S.isMatch)&&F){var T=v[c];T?T.output.push({key:n,arrayIndex:o,value:i,score:P,matchedIndices:S.matchedIndices}):(v[c]={item:a,output:[{key:n,arrayIndex:o,value:i,score:P,matchedIndices:S.matchedIndices}]},g.push(v[c]))}}else if(s(i))for(var z=0,E=i.length;z-1&&(a.arrayIndex=i.arrayIndex),t.matches.push(a)}}}),this.options.includeScore&&o.push(function(e,t){t.score=e.score});for(var i=0,a=e.length;in)return o(e,this.pattern,r);var a=this.options,s=a.location,c=a.distance,h=a.threshold,l=a.findAllMatches,u=a.minMatchCharLength;return i(e,this.pattern,this.patternAlphabet,{location:s,distance:c,threshold:h,findAllMatches:l,minMatchCharLength:u})}}])&&r(t.prototype,n),s&&r(t,s),e}();e.exports=s},function(e,t){var n=/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;e.exports=function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:/ +/g,o=new RegExp(t.replace(n,"\\$&").replace(r,"|")),i=e.match(o),a=!!i,s=[];if(a)for(var c=0,h=i.length;c=P;z-=1){var E=z-1,K=n[e.charAt(E)];if(K&&(x[E]=1),T[z]=(T[z+1]<<1|1)&K,0!==I&&(T[z]|=(L[z+1]|L[z])<<1|1|L[z+1]),T[z]&C&&(w=r(t,{errors:I,currentLocation:E,expectedLocation:g,distance:h}))<=m){if(m=w,(k=E)<=g)break;P=Math.max(1,2*g-k)}}if(r(t,{errors:I+1,currentLocation:g,expectedLocation:g,distance:h})>m)break;L=T}return{isMatch:k>=0,score:0===w?.001:w,matchedIndices:o(x,p)}}},function(e,t){e.exports=function(e,t){var n=t.errors,r=void 0===n?0:n,o=t.currentLocation,i=void 0===o?0:o,a=t.expectedLocation,s=void 0===a?0:a,c=t.distance,h=void 0===c?100:c,l=r/e.length,u=Math.abs(s-i);return h?l+u/h:u?1:l}},function(e,t){e.exports=function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1,n=[],r=-1,o=-1,i=0,a=e.length;i=t&&n.push([r,o]),r=-1)}return e[i-1]&&i-r>=t&&n.push([r,i-1]),n}},function(e,t){e.exports=function(e){for(var t={},n=e.length,r=0;r= 1 && !this._isSearching) { - // If we have a placeholder choice along with groups - var activePlaceholders = activeChoices.filter(function (activeChoice) { - return activeChoice.placeholder === true && activeChoice.groupId === -1; - }); +var __spreadArrays = this && this.__spreadArrays || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) { + s += arguments[i].length; + } - if (activePlaceholders.length >= 1) { - choiceListFragment = this._createChoicesFragment(activePlaceholders, choiceListFragment); - } + for (var r = Array(s), k = 0, i = 0; i < il; i++) { + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) { + r[k] = a[j]; + } + } - choiceListFragment = this._createGroupsFragment(activeGroups, activeChoices, choiceListFragment); - } else if (activeChoices.length >= 1) { - choiceListFragment = this._createChoicesFragment(activeChoices, choiceListFragment); - } // If we have choices to show + return r; +}; + +var __importDefault = this && this.__importDefault || function (mod) { + return mod && mod.__esModule ? mod : { + "default": mod + }; +}; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +/* eslint-disable @typescript-eslint/no-explicit-any */ + +var redux_1 = __webpack_require__(3); + +var index_1 = __importDefault(__webpack_require__(4)); +var Store = +/** @class */ +function () { + function Store() { + this._store = redux_1.createStore(index_1.default, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()); + } + /** + * Subscribe store to function call (wrapped Redux method) + */ - if (choiceListFragment.childNodes && choiceListFragment.childNodes.length > 0) { - var activeItems = this._store.activeItems; - var canAddItem = this._canAddItem(activeItems, this.input.value); // ...and we can select them + Store.prototype.subscribe = function (onChange) { + this._store.subscribe(onChange); + }; + /** + * Dispatch event to store (wrapped Redux method) + */ - if (canAddItem.response) { - // ...append them and highlight the first choice - this.choiceList.append(choiceListFragment); + Store.prototype.dispatch = function (action) { + this._store.dispatch(action); + }; - this._highlightChoice(); - } else { - // ...otherwise show a notice - this.choiceList.append(this._getTemplate('notice', canAddItem.notice)); - } - } else { - // Otherwise show a notice - var dropdownItem; - var notice; + Object.defineProperty(Store.prototype, "state", { + /** + * Get store object (wrapping Redux method) + */ + get: function get() { + return this._store.getState(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "items", { + /** + * Get items from store + */ + get: function get() { + return this.state.items; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "activeItems", { + /** + * Get active items from store + */ + get: function get() { + return this.items.filter(function (item) { + return item.active === true; + }); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "highlightedActiveItems", { + /** + * Get highlighted items from store + */ + get: function get() { + return this.items.filter(function (item) { + return item.active && item.highlighted; + }); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "choices", { + /** + * Get choices from store + */ + get: function get() { + return this.state.choices; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "activeChoices", { + /** + * Get active choices from store + */ + get: function get() { + return this.choices.filter(function (choice) { + return choice.active === true; + }); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "selectableChoices", { + /** + * Get selectable choices from store + */ + get: function get() { + return this.choices.filter(function (choice) { + return choice.disabled !== true; + }); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "searchableChoices", { + /** + * Get choices that can be searched (excluding placeholders) + */ + get: function get() { + return this.selectableChoices.filter(function (choice) { + return choice.placeholder !== true; + }); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "placeholderChoice", { + /** + * Get placeholder choice from store + */ + get: function get() { + return __spreadArrays(this.choices).reverse().find(function (choice) { + return choice.placeholder === true; + }); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "groups", { + /** + * Get groups from store + */ + get: function get() { + return this.state.groups; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "activeGroups", { + /** + * Get active groups from store + */ + get: function get() { + var _a = this, + groups = _a.groups, + choices = _a.choices; - if (this._isSearching) { - notice = typeof this.config.noResultsText === 'function' ? this.config.noResultsText() : this.config.noResultsText; - dropdownItem = this._getTemplate('notice', notice, 'no-results'); - } else { - notice = typeof this.config.noChoicesText === 'function' ? this.config.noChoicesText() : this.config.noChoicesText; - dropdownItem = this._getTemplate('notice', notice, 'no-choices'); - } + return groups.filter(function (group) { + var isActive = group.active === true && group.disabled === false; + var hasActiveOptions = choices.some(function (choice) { + return choice.active === true && choice.disabled === false; + }); + return isActive && hasActiveOptions; + }, []); + }, + enumerable: true, + configurable: true + }); + /** + * Get loading state from store + */ - this.choiceList.append(dropdownItem); - } + Store.prototype.isLoading = function () { + return this.state.loading; }; + /** + * Get single choice by it's ID + */ - _proto._renderItems = function _renderItems() { - var activeItems = this._store.activeItems || []; - this.itemList.clear(); // Create a fragment to store our list items - // (so we don't have to update the DOM for each item) - var itemListFragment = this._createItemsFragment(activeItems); // If we have items to add, append them + Store.prototype.getChoiceById = function (id) { + return this.activeChoices.find(function (choice) { + return choice.id === parseInt(id, 10); + }); + }; + /** + * Get group by group id + */ - if (itemListFragment.childNodes) { - this.itemList.append(itemListFragment); - } + Store.prototype.getGroupById = function (id) { + return this.groups.find(function (group) { + return group.id === id; + }); }; - _proto._createGroupsFragment = function _createGroupsFragment(groups, choices, fragment) { - var _this13 = this; + return Store; +}(); - if (fragment === void 0) { - fragment = document.createDocumentFragment(); - } +exports.default = Store; - var getGroupChoices = function getGroupChoices(group) { - return choices.filter(function (choice) { - if (_this13._isSelectOneElement) { - return choice.groupId === group.id; - } +/***/ }), +/* 12 */ +/***/ (function(module, exports) { - return choice.groupId === group.id && (_this13.config.renderSelectedChoices === 'always' || !choice.selected); - }); - }; // If sorting is enabled, filter groups +var g; +// This works in non-strict mode +g = (function() { + return this; +})(); - if (this.config.shouldSort) { - groups.sort(this.config.sorter); - } +try { + // This works if eval is allowed (see CSP) + g = g || new Function("return this")(); +} catch (e) { + // This works if the window reference is available + if (typeof window === "object") g = window; +} - groups.forEach(function (group) { - var groupChoices = getGroupChoices(group); +// g can still be undefined, but nothing to do about it... +// We return undefined, instead of nothing here, so it's +// easier to handle this case. if(!global) { ...} - if (groupChoices.length >= 1) { - var dropdownGroup = _this13._getTemplate('choiceGroup', group); +module.exports = g; - fragment.appendChild(dropdownGroup); - _this13._createChoicesFragment(groupChoices, fragment, true); - } - }); - return fragment; - }; +/***/ }), +/* 13 */ +/***/ (function(module, exports) { + +module.exports = function(originalModule) { + if (!originalModule.webpackPolyfill) { + var module = Object.create(originalModule); + // module.parent = undefined by default + if (!module.children) module.children = []; + Object.defineProperty(module, "loaded", { + enumerable: true, + get: function() { + return module.l; + } + }); + Object.defineProperty(module, "id", { + enumerable: true, + get: function() { + return module.i; + } + }); + Object.defineProperty(module, "exports", { + enumerable: true + }); + module.webpackPolyfill = 1; + } + return module; +}; - _proto._createChoicesFragment = function _createChoicesFragment(choices, fragment, withinGroup) { - var _this14 = this; - if (fragment === void 0) { - fragment = document.createDocumentFragment(); - } +/***/ }), +/* 14 */ +/***/ (function(module, exports, __webpack_require__) { - if (withinGroup === void 0) { - withinGroup = false; - } +"use strict"; - // Create a fragment to store our list items (so we don't have to update the DOM for each item) - var _this$config = this.config, - renderSelectedChoices = _this$config.renderSelectedChoices, - searchResultLimit = _this$config.searchResultLimit, - renderChoiceLimit = _this$config.renderChoiceLimit; - var filter = this._isSearching ? sortByScore : this.config.sorter; - var appendChoice = function appendChoice(choice) { - var shouldRender = renderSelectedChoices === 'auto' ? _this14._isSelectOneElement || !choice.selected : true; +var __spreadArrays = this && this.__spreadArrays || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) { + s += arguments[i].length; + } - if (shouldRender) { - var dropdownItem = _this14._getTemplate('choice', choice, _this14.config.itemSelectText); + for (var r = Array(s), k = 0, i = 0; i < il; i++) { + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) { + r[k] = a[j]; + } + } - fragment.appendChild(dropdownItem); - } - }; + return r; +}; - var rendererableChoices = choices; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.defaultState = []; - if (renderSelectedChoices === 'auto' && !this._isSelectOneElement) { - rendererableChoices = choices.filter(function (choice) { - return !choice.selected; - }); - } // Split array into placeholders and "normal" choices +function items(state, action) { + if (state === void 0) { + state = exports.defaultState; + } + switch (action.type) { + case 'ADD_ITEM': + { + var addItemAction = action; // Add object to items array + + var newState = __spreadArrays(state, [{ + id: addItemAction.id, + choiceId: addItemAction.choiceId, + groupId: addItemAction.groupId, + value: addItemAction.value, + label: addItemAction.label, + active: true, + highlighted: false, + customProperties: addItemAction.customProperties, + placeholder: addItemAction.placeholder || false, + keyCode: null + }]); - var _rendererableChoices$ = rendererableChoices.reduce(function (acc, choice) { - if (choice.placeholder) { - acc.placeholderChoices.push(choice); - } else { - acc.normalChoices.push(choice); + return newState.map(function (obj) { + var item = obj; + item.highlighted = false; + return item; + }); } - return acc; - }, { - placeholderChoices: [], - normalChoices: [] - }), - placeholderChoices = _rendererableChoices$.placeholderChoices, - normalChoices = _rendererableChoices$.normalChoices; // If sorting is enabled or the user is searching, filter choices - + case 'REMOVE_ITEM': + { + // Set item to inactive + return state.map(function (obj) { + var item = obj; - if (this.config.shouldSort || this._isSearching) { - normalChoices.sort(filter); - } + if (item.id === action.id) { + item.active = false; + } - var choiceLimit = rendererableChoices.length; // Prepend placeholeder + return item; + }); + } - var sortedChoices = this._isSelectOneElement ? [].concat(placeholderChoices, normalChoices) : normalChoices; + case 'HIGHLIGHT_ITEM': + { + var highlightItemAction_1 = action; + return state.map(function (obj) { + var item = obj; - if (this._isSearching) { - choiceLimit = searchResultLimit; - } else if (renderChoiceLimit && renderChoiceLimit > 0 && !withinGroup) { - choiceLimit = renderChoiceLimit; - } // Add each choice to dropdown within range + if (item.id === highlightItemAction_1.id) { + item.highlighted = highlightItemAction_1.highlighted; + } + return item; + }); + } - for (var i = 0; i < choiceLimit; i += 1) { - if (sortedChoices[i]) { - appendChoice(sortedChoices[i]); + default: + { + return state; } - } + } +} - return fragment; - }; +exports.default = items; - _proto._createItemsFragment = function _createItemsFragment(items, fragment) { - var _this15 = this; +/***/ }), +/* 15 */ +/***/ (function(module, exports, __webpack_require__) { - if (fragment === void 0) { - fragment = document.createDocumentFragment(); - } +"use strict"; - // Create fragment to add elements to - var _this$config2 = this.config, - shouldSortItems = _this$config2.shouldSortItems, - sorter = _this$config2.sorter, - removeItemButton = _this$config2.removeItemButton; // If sorting is enabled, filter items - if (shouldSortItems && !this._isSelectOneElement) { - items.sort(sorter); - } +var __spreadArrays = this && this.__spreadArrays || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) { + s += arguments[i].length; + } - if (this._isTextElement) { - // Update the value of the hidden input - this.passedElement.value = items; - } else { - // Update the options of the hidden input - this.passedElement.options = items; + for (var r = Array(s), k = 0, i = 0; i < il; i++) { + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) { + r[k] = a[j]; } + } - var addItemToFragment = function addItemToFragment(item) { - // Create new list element - var listItem = _this15._getTemplate('item', item, removeItemButton); // Append it to list + return r; +}; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.defaultState = []; - fragment.appendChild(listItem); - }; // Add each list item to list +function groups(state, action) { + if (state === void 0) { + state = exports.defaultState; + } + switch (action.type) { + case 'ADD_GROUP': + { + var addGroupAction = action; + return __spreadArrays(state, [{ + id: addGroupAction.id, + value: addGroupAction.value, + active: addGroupAction.active, + disabled: addGroupAction.disabled + }]); + } - items.forEach(addItemToFragment); - return fragment; - }; + case 'CLEAR_CHOICES': + { + return []; + } - _proto._triggerChange = function _triggerChange(value) { - if (value === undefined || value === null) { - return; - } + default: + { + return state; + } + } +} - this.passedElement.triggerEvent(EVENTS.change, { - value: value - }); - }; +exports.default = groups; - _proto._selectPlaceholderChoice = function _selectPlaceholderChoice() { - var placeholderChoice = this._store.placeholderChoice; +/***/ }), +/* 16 */ +/***/ (function(module, exports, __webpack_require__) { - if (placeholderChoice) { - this._addItem({ - value: placeholderChoice.value, - label: placeholderChoice.label, - choiceId: placeholderChoice.id, - groupId: placeholderChoice.groupId, - placeholder: placeholderChoice.placeholder - }); +"use strict"; - this._triggerChange(placeholderChoice.value); - } - }; - _proto._handleButtonAction = function _handleButtonAction(activeItems, element) { - if (!activeItems || !element || !this.config.removeItems || !this.config.removeItemButton) { - return; +var __spreadArrays = this && this.__spreadArrays || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) { + s += arguments[i].length; + } + + for (var r = Array(s), k = 0, i = 0; i < il; i++) { + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) { + r[k] = a[j]; } + } - var itemId = element.parentNode.getAttribute('data-id'); - var itemToRemove = activeItems.find(function (item) { - return item.id === parseInt(itemId, 10); - }); // Remove item associated with button + return r; +}; - this._removeItem(itemToRemove); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.defaultState = []; - this._triggerChange(itemToRemove.value); +function choices(state, action) { + if (state === void 0) { + state = exports.defaultState; + } - if (this._isSelectOneElement) { - this._selectPlaceholderChoice(); - } - }; + switch (action.type) { + case 'ADD_CHOICE': + { + var addChoiceAction = action; + var choice = { + id: addChoiceAction.id, + elementId: addChoiceAction.elementId, + groupId: addChoiceAction.groupId, + value: addChoiceAction.value, + label: addChoiceAction.label || addChoiceAction.value, + disabled: addChoiceAction.disabled || false, + selected: false, + active: true, + score: 9999, + customProperties: addChoiceAction.customProperties, + placeholder: addChoiceAction.placeholder || false + }; + /* + A disabled choice appears in the choice dropdown but cannot be selected + A selected choice has been added to the passed input's value (added as an item) + An active choice appears within the choice dropdown + */ - _proto._handleItemAction = function _handleItemAction(activeItems, element, hasShiftKey) { - var _this16 = this; + return __spreadArrays(state, [choice]); + } - if (hasShiftKey === void 0) { - hasShiftKey = false; - } + case 'ADD_ITEM': + { + var addItemAction_1 = action; // When an item is added and it has an associated choice, + // we want to disable it so it can't be chosen again - if (!activeItems || !element || !this.config.removeItems || this._isSelectOneElement) { - return; - } + if (addItemAction_1.choiceId > -1) { + return state.map(function (obj) { + var choice = obj; - var passedId = element.getAttribute('data-id'); // We only want to select one item with a click - // so we deselect any items that aren't the target - // unless shift is being pressed + if (choice.id === parseInt("" + addItemAction_1.choiceId, 10)) { + choice.selected = true; + } - activeItems.forEach(function (item) { - if (item.id === parseInt(passedId, 10) && !item.highlighted) { - _this16.highlightItem(item); - } else if (!hasShiftKey && item.highlighted) { - _this16.unhighlightItem(item); + return choice; + }); + } + + return state; } - }); // Focus input as without focus, a user cannot do anything with a - // highlighted item - this.input.focus(); - }; + case 'REMOVE_ITEM': + { + var removeItemAction_1 = action; // When an item is removed and it has an associated choice, + // we want to re-enable it so it can be chosen again - _proto._handleChoiceAction = function _handleChoiceAction(activeItems, element) { - if (!activeItems || !element) { - return; - } // If we are clicking on an option + if (removeItemAction_1.choiceId && removeItemAction_1.choiceId > -1) { + return state.map(function (obj) { + var choice = obj; + if (choice.id === parseInt("" + removeItemAction_1.choiceId, 10)) { + choice.selected = false; + } - var id = element.dataset.id; + return choice; + }); + } - var choice = this._store.getChoiceById(id); + return state; + } - if (!choice) { - return; - } + case 'FILTER_CHOICES': + { + var filterChoicesAction_1 = action; + return state.map(function (obj) { + var choice = obj; // Set active state based on whether choice is + // within filtered results - var passedKeyCode = activeItems[0] && activeItems[0].keyCode ? activeItems[0].keyCode : null; - var hasActiveDropdown = this.dropdown.isActive; // Update choice keyCode + choice.active = filterChoicesAction_1.results.some(function (_a) { + var item = _a.item, + score = _a.score; - choice.keyCode = passedKeyCode; - this.passedElement.triggerEvent(EVENTS.choice, { - choice: choice - }); + if (item.id === choice.id) { + choice.score = score; + return true; + } - if (!choice.selected && !choice.disabled) { - var canAddItem = this._canAddItem(activeItems, choice.value); + return false; + }); + return choice; + }); + } - if (canAddItem.response) { - this._addItem({ - value: choice.value, - label: choice.label, - choiceId: choice.id, - groupId: choice.groupId, - customProperties: choice.customProperties, - placeholder: choice.placeholder, - keyCode: choice.keyCode + case 'ACTIVATE_CHOICES': + { + var activateChoicesAction_1 = action; + return state.map(function (obj) { + var choice = obj; + choice.active = activateChoicesAction_1.active; + return choice; }); + } - this._triggerChange(choice.value); + case 'CLEAR_CHOICES': + { + return exports.defaultState; } - } - this.clearInput(); // We want to close the dropdown if we are dealing with a single select box + default: + { + return state; + } + } +} - if (hasActiveDropdown && this._isSelectOneElement) { - this.hideDropdown(true); - this.containerOuter.focus(); - } - }; +exports.default = choices; - _proto._handleBackspace = function _handleBackspace(activeItems) { - if (!this.config.removeItems || !activeItems) { - return; - } +/***/ }), +/* 17 */ +/***/ (function(module, exports, __webpack_require__) { - var lastItem = activeItems[activeItems.length - 1]; - var hasHighlightedItems = activeItems.some(function (item) { - return item.highlighted; - }); // If editing the last item is allowed and there are not other selected items, - // we can edit the item value. Otherwise if we can remove items, remove all selected items +"use strict"; - if (this.config.editItems && !hasHighlightedItems && lastItem) { - this.input.value = lastItem.value; - this.input.setWidth(); - this._removeItem(lastItem); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.defaultState = false; + +var general = function general(state, action) { + if (state === void 0) { + state = exports.defaultState; + } + + switch (action.type) { + case 'SET_IS_LOADING': + { + return action.isLoading; + } + + default: + { + return state; + } + } +}; + +exports.default = general; - this._triggerChange(lastItem.value); - } else { - if (!hasHighlightedItems) { - // Highlight last item if none already highlighted - this.highlightItem(lastItem, false); - } +/***/ }), +/* 18 */ +/***/ (function(module, exports, __webpack_require__) { - this.removeHighlightedItems(true); - } - }; +"use strict"; - _proto._startLoading = function _startLoading() { - this._store.dispatch(setIsLoading(true)); - }; - _proto._stopLoading = function _stopLoading() { - this._store.dispatch(setIsLoading(false)); +var __importDefault = this && this.__importDefault || function (mod) { + return mod && mod.__esModule ? mod : { + "default": mod }; +}; - _proto._handleLoadingState = function _handleLoadingState(setLoading) { - if (setLoading === void 0) { - setLoading = true; - } +Object.defineProperty(exports, "__esModule", { + value: true +}); - var placeholderItem = this.itemList.getChild("." + this.config.classNames.placeholder); +var dropdown_1 = __importDefault(__webpack_require__(19)); - if (setLoading) { - this.disable(); - this.containerOuter.addLoadingState(); +exports.Dropdown = dropdown_1.default; - if (this._isSelectOneElement) { - if (!placeholderItem) { - placeholderItem = this._getTemplate('placeholder', this.config.loadingText); - this.itemList.append(placeholderItem); - } else { - placeholderItem.innerHTML = this.config.loadingText; - } - } else { - this.input.placeholder = this.config.loadingText; - } - } else { - this.enable(); - this.containerOuter.removeLoadingState(); +var container_1 = __importDefault(__webpack_require__(20)); - if (this._isSelectOneElement) { - placeholderItem.innerHTML = this._placeholderValue || ''; - } else { - this.input.placeholder = this._placeholderValue || ''; - } - } - }; +exports.Container = container_1.default; - _proto._handleSearch = function _handleSearch(value) { - if (!value || !this.input.isFocussed) { - return; - } +var input_1 = __importDefault(__webpack_require__(21)); - var choices = this._store.choices; - var _this$config3 = this.config, - searchFloor = _this$config3.searchFloor, - searchChoices = _this$config3.searchChoices; - var hasUnactiveChoices = choices.some(function (option) { - return !option.active; - }); // Check that we have a value to search and the input was an alphanumeric character +exports.Input = input_1.default; - if (value && value.length >= searchFloor) { - var resultCount = searchChoices ? this._searchChoices(value) : 0; // Trigger search event +var list_1 = __importDefault(__webpack_require__(22)); - this.passedElement.triggerEvent(EVENTS.search, { - value: value, - resultCount: resultCount - }); - } else if (hasUnactiveChoices) { - // Otherwise reset choices to active - this._isSearching = false; +exports.List = list_1.default; - this._store.dispatch(choices_activateChoices(true)); - } - }; +var wrapped_input_1 = __importDefault(__webpack_require__(23)); - _proto._canAddItem = function _canAddItem(activeItems, value) { - var canAddItem = true; - var notice = typeof this.config.addItemText === 'function' ? this.config.addItemText(value) : this.config.addItemText; +exports.WrappedInput = wrapped_input_1.default; - if (!this._isSelectOneElement) { - var isDuplicateValue = existsInArray(activeItems, value); +var wrapped_select_1 = __importDefault(__webpack_require__(24)); - if (this.config.maxItemCount > 0 && this.config.maxItemCount <= activeItems.length) { - // If there is a max entry limit and we have reached that limit - // don't update - canAddItem = false; - notice = typeof this.config.maxItemText === 'function' ? this.config.maxItemText(this.config.maxItemCount) : this.config.maxItemText; - } +exports.WrappedSelect = wrapped_select_1.default; - if (!this.config.duplicateItemsAllowed && isDuplicateValue && canAddItem) { - canAddItem = false; - notice = typeof this.config.uniqueItemText === 'function' ? this.config.uniqueItemText(value) : this.config.uniqueItemText; - } +/***/ }), +/* 19 */ +/***/ (function(module, exports, __webpack_require__) { - if (this._isTextElement && this.config.addItems && canAddItem && typeof this.config.addItemFilter === 'function' && !this.config.addItemFilter(value)) { - canAddItem = false; - notice = typeof this.config.customAddItemText === 'function' ? this.config.customAddItemText(value) : this.config.customAddItemText; - } - } +"use strict"; - return { - response: canAddItem, - notice: notice - }; - }; - _proto._searchChoices = function _searchChoices(value) { - var newValue = typeof value === 'string' ? value.trim() : value; - var currentValue = typeof this._currentValue === 'string' ? this._currentValue.trim() : this._currentValue; +Object.defineProperty(exports, "__esModule", { + value: true +}); - if (newValue.length < 1 && newValue === currentValue + " ") { - return 0; - } // If new value matches the desired length and is not the same as the current value with a space +var Dropdown = +/** @class */ +function () { + function Dropdown(_a) { + var element = _a.element, + type = _a.type, + classNames = _a.classNames; + this.element = element; + this.classNames = classNames; + this.type = type; + this.isActive = false; + } + Object.defineProperty(Dropdown.prototype, "distanceFromTopWindow", { + /** + * Bottom position of dropdown in viewport coordinates + */ + get: function get() { + return this.element.getBoundingClientRect().bottom; + }, + enumerable: true, + configurable: true + }); - var haystack = this._store.searchableChoices; - var needle = newValue; - var keys = [].concat(this.config.searchFields); - var options = Object.assign(this.config.fuseOptions, { - keys: keys - }); - var fuse = new fuse_default.a(haystack, options); - var results = fuse.search(needle); - this._currentValue = newValue; - this._highlightPosition = 0; - this._isSearching = true; + Dropdown.prototype.getChild = function (selector) { + return this.element.querySelector(selector); + }; + /** + * Show dropdown to user by adding active state class + */ - this._store.dispatch(choices_filterChoices(results)); - return results.length; + Dropdown.prototype.show = function () { + this.element.classList.add(this.classNames.activeState); + this.element.setAttribute('aria-expanded', 'true'); + this.isActive = true; + return this; }; + /** + * Hide dropdown from user + */ - _proto._addEventListeners = function _addEventListeners() { - var _document = document, - documentElement = _document.documentElement; // capture events - can cancel event processing or propagation - - documentElement.addEventListener('touchend', this._onTouchEnd, true); - this.containerOuter.element.addEventListener('keydown', this._onKeyDown, true); - this.containerOuter.element.addEventListener('mousedown', this._onMouseDown, true); // passive events - doesn't call `preventDefault` or `stopPropagation` - documentElement.addEventListener('click', this._onClick, { - passive: true - }); - documentElement.addEventListener('touchmove', this._onTouchMove, { - passive: true - }); - this.dropdown.element.addEventListener('mouseover', this._onMouseOver, { - passive: true - }); + Dropdown.prototype.hide = function () { + this.element.classList.remove(this.classNames.activeState); + this.element.setAttribute('aria-expanded', 'false'); + this.isActive = false; + return this; + }; - if (this._isSelectOneElement) { - this.containerOuter.element.addEventListener('focus', this._onFocus, { - passive: true - }); - this.containerOuter.element.addEventListener('blur', this._onBlur, { - passive: true - }); - } + return Dropdown; +}(); - this.input.element.addEventListener('keyup', this._onKeyUp, { - passive: true - }); - this.input.element.addEventListener('focus', this._onFocus, { - passive: true - }); - this.input.element.addEventListener('blur', this._onBlur, { - passive: true - }); +exports.default = Dropdown; - if (this.input.element.form) { - this.input.element.form.addEventListener('reset', this._onFormReset, { - passive: true - }); - } +/***/ }), +/* 20 */ +/***/ (function(module, exports, __webpack_require__) { - this.input.addEventListeners(); - }; +"use strict"; - _proto._removeEventListeners = function _removeEventListeners() { - var _document2 = document, - documentElement = _document2.documentElement; - documentElement.removeEventListener('touchend', this._onTouchEnd, true); - this.containerOuter.element.removeEventListener('keydown', this._onKeyDown, true); - this.containerOuter.element.removeEventListener('mousedown', this._onMouseDown, true); - documentElement.removeEventListener('click', this._onClick); - documentElement.removeEventListener('touchmove', this._onTouchMove); - this.dropdown.element.removeEventListener('mouseover', this._onMouseOver); - if (this._isSelectOneElement) { - this.containerOuter.element.removeEventListener('focus', this._onFocus); - this.containerOuter.element.removeEventListener('blur', this._onBlur); - } +Object.defineProperty(exports, "__esModule", { + value: true +}); - this.input.element.removeEventListener('keyup', this._onKeyUp); - this.input.element.removeEventListener('focus', this._onFocus); - this.input.element.removeEventListener('blur', this._onBlur); +var utils_1 = __webpack_require__(1); - if (this.input.element.form) { - this.input.element.form.removeEventListener('reset', this._onFormReset); - } +var constants_1 = __webpack_require__(0); - this.input.removeEventListeners(); +var Container = +/** @class */ +function () { + function Container(_a) { + var element = _a.element, + type = _a.type, + classNames = _a.classNames, + position = _a.position; + this.element = element; + this.classNames = classNames; + this.type = type; + this.position = position; + this.isOpen = false; + this.isFlipped = false; + this.isFocussed = false; + this.isDisabled = false; + this.isLoading = false; + this._onFocus = this._onFocus.bind(this); + this._onBlur = this._onBlur.bind(this); } + + Container.prototype.addEventListeners = function () { + this.element.addEventListener('focus', this._onFocus); + this.element.addEventListener('blur', this._onBlur); + }; + + Container.prototype.removeEventListeners = function () { + this.element.removeEventListener('focus', this._onFocus); + this.element.removeEventListener('blur', this._onBlur); + }; /** - * @param {KeyboardEvent} event + * Determine whether container should be flipped based on passed + * dropdown position */ - ; - _proto._onKeyDown = function _onKeyDown(event) { - var _keyDownActions; - var keyCode = event.keyCode; - var activeItems = this._store.activeItems; - var hasFocusedInput = this.input.isFocussed; - var hasActiveDropdown = this.dropdown.isActive; - var hasItems = this.itemList.hasChildren(); - var keyString = String.fromCharCode(keyCode); - var wasAlphaNumericChar = /[a-zA-Z0-9-_ ]/.test(keyString); - var BACK_KEY = KEY_CODES.BACK_KEY, - DELETE_KEY = KEY_CODES.DELETE_KEY, - ENTER_KEY = KEY_CODES.ENTER_KEY, - A_KEY = KEY_CODES.A_KEY, - ESC_KEY = KEY_CODES.ESC_KEY, - UP_KEY = KEY_CODES.UP_KEY, - DOWN_KEY = KEY_CODES.DOWN_KEY, - PAGE_UP_KEY = KEY_CODES.PAGE_UP_KEY, - PAGE_DOWN_KEY = KEY_CODES.PAGE_DOWN_KEY; + Container.prototype.shouldFlip = function (dropdownPos) { + if (typeof dropdownPos !== 'number') { + return false; + } // If flip is enabled and the dropdown bottom position is + // greater than the window height flip the dropdown. - if (!this._isTextElement && !hasActiveDropdown && wasAlphaNumericChar) { - this.showDropdown(); - if (!this.input.isFocussed) { - /* - We update the input value with the pressed key as - the input was not focussed at the time of key press - therefore does not have the value of the key. - */ - this.input.value += keyString.toLowerCase(); - } - } // Map keys to key actions + var shouldFlip = false; + + if (this.position === 'auto') { + shouldFlip = !window.matchMedia("(min-height: " + (dropdownPos + 1) + "px)").matches; + } else if (this.position === 'top') { + shouldFlip = true; + } + return shouldFlip; + }; + + Container.prototype.setActiveDescendant = function (activeDescendantID) { + this.element.setAttribute('aria-activedescendant', activeDescendantID); + }; + + Container.prototype.removeActiveDescendant = function () { + this.element.removeAttribute('aria-activedescendant'); + }; + + Container.prototype.open = function (dropdownPos) { + this.element.classList.add(this.classNames.openState); + this.element.setAttribute('aria-expanded', 'true'); + this.isOpen = true; + + if (this.shouldFlip(dropdownPos)) { + this.element.classList.add(this.classNames.flippedState); + this.isFlipped = true; + } + }; - var keyDownActions = (_keyDownActions = {}, _keyDownActions[A_KEY] = this._onAKey, _keyDownActions[ENTER_KEY] = this._onEnterKey, _keyDownActions[ESC_KEY] = this._onEscapeKey, _keyDownActions[UP_KEY] = this._onDirectionKey, _keyDownActions[PAGE_UP_KEY] = this._onDirectionKey, _keyDownActions[DOWN_KEY] = this._onDirectionKey, _keyDownActions[PAGE_DOWN_KEY] = this._onDirectionKey, _keyDownActions[DELETE_KEY] = this._onDeleteKey, _keyDownActions[BACK_KEY] = this._onDeleteKey, _keyDownActions); + Container.prototype.close = function () { + this.element.classList.remove(this.classNames.openState); + this.element.setAttribute('aria-expanded', 'false'); + this.removeActiveDescendant(); + this.isOpen = false; // A dropdown flips if it does not have space within the page - if (keyDownActions[keyCode]) { - keyDownActions[keyCode]({ - event: event, - activeItems: activeItems, - hasFocusedInput: hasFocusedInput, - hasActiveDropdown: hasActiveDropdown, - hasItems: hasItems - }); + if (this.isFlipped) { + this.element.classList.remove(this.classNames.flippedState); + this.isFlipped = false; } }; - _proto._onKeyUp = function _onKeyUp(_ref2) { - var target = _ref2.target, - keyCode = _ref2.keyCode; - var value = this.input.value; - var activeItems = this._store.activeItems; + Container.prototype.focus = function () { + if (!this.isFocussed) { + this.element.focus(); + } + }; - var canAddItem = this._canAddItem(activeItems, value); + Container.prototype.addFocusState = function () { + this.element.classList.add(this.classNames.focusState); + }; - var backKey = KEY_CODES.BACK_KEY, - deleteKey = KEY_CODES.DELETE_KEY; // We are typing into a text input and have a value, we want to show a dropdown - // notice. Otherwise hide the dropdown + Container.prototype.removeFocusState = function () { + this.element.classList.remove(this.classNames.focusState); + }; - if (this._isTextElement) { - var canShowDropdownNotice = canAddItem.notice && value; + Container.prototype.enable = function () { + this.element.classList.remove(this.classNames.disabledState); + this.element.removeAttribute('aria-disabled'); - if (canShowDropdownNotice) { - var dropdownItem = this._getTemplate('notice', canAddItem.notice); + if (this.type === constants_1.SELECT_ONE_TYPE) { + this.element.setAttribute('tabindex', '0'); + } - this.dropdown.element.innerHTML = dropdownItem.outerHTML; - this.showDropdown(true); - } else { - this.hideDropdown(true); - } - } else { - var wasRemovalKeyCode = keyCode === backKey || keyCode === deleteKey; - var userHasRemovedValue = wasRemovalKeyCode && !target.value; - var canReactivateChoices = !this._isTextElement && this._isSearching; - var canSearch = this._canSearch && canAddItem.response; + this.isDisabled = false; + }; - if (userHasRemovedValue && canReactivateChoices) { - this._isSearching = false; + Container.prototype.disable = function () { + this.element.classList.add(this.classNames.disabledState); + this.element.setAttribute('aria-disabled', 'true'); - this._store.dispatch(choices_activateChoices(true)); - } else if (canSearch) { - this._handleSearch(this.input.value); - } + if (this.type === constants_1.SELECT_ONE_TYPE) { + this.element.setAttribute('tabindex', '-1'); } - this._canSearch = this.config.searchEnabled; + this.isDisabled = true; }; - _proto._onAKey = function _onAKey(_ref3) { - var event = _ref3.event, - hasItems = _ref3.hasItems; - var ctrlKey = event.ctrlKey, - metaKey = event.metaKey; - var hasCtrlDownKeyPressed = ctrlKey || metaKey; // If CTRL + A or CMD + A have been pressed and there are items to select + Container.prototype.wrap = function (element) { + utils_1.wrap(element, this.element); + }; - if (hasCtrlDownKeyPressed && hasItems) { - this._canSearch = false; - var shouldHightlightAll = this.config.removeItems && !this.input.value && this.input.element === document.activeElement; + Container.prototype.unwrap = function (element) { + if (this.element.parentNode) { + // Move passed element outside this element + this.element.parentNode.insertBefore(element, this.element); // Remove this element - if (shouldHightlightAll) { - this.highlightAll(); - } + this.element.parentNode.removeChild(this.element); } }; - _proto._onEnterKey = function _onEnterKey(_ref4) { - var event = _ref4.event, - activeItems = _ref4.activeItems, - hasActiveDropdown = _ref4.hasActiveDropdown; - var target = event.target; - var enterKey = KEY_CODES.ENTER_KEY; - var targetWasButton = target.hasAttribute('data-button'); + Container.prototype.addLoadingState = function () { + this.element.classList.add(this.classNames.loadingState); + this.element.setAttribute('aria-busy', 'true'); + this.isLoading = true; + }; - if (this._isTextElement && target.value) { - var value = this.input.value; + Container.prototype.removeLoadingState = function () { + this.element.classList.remove(this.classNames.loadingState); + this.element.removeAttribute('aria-busy'); + this.isLoading = false; + }; - var canAddItem = this._canAddItem(activeItems, value); + Container.prototype._onFocus = function () { + this.isFocussed = true; + }; - if (canAddItem.response) { - this.hideDropdown(true); + Container.prototype._onBlur = function () { + this.isFocussed = false; + }; - this._addItem({ - value: value - }); + return Container; +}(); - this._triggerChange(value); +exports.default = Container; - this.clearInput(); - } - } +/***/ }), +/* 21 */ +/***/ (function(module, exports, __webpack_require__) { - if (targetWasButton) { - this._handleButtonAction(activeItems, target); +"use strict"; - event.preventDefault(); - } - if (hasActiveDropdown) { - var highlightedChoice = this.dropdown.getChild("." + this.config.classNames.highlightedState); +Object.defineProperty(exports, "__esModule", { + value: true +}); - if (highlightedChoice) { - // add enter keyCode value - if (activeItems[0]) { - activeItems[0].keyCode = enterKey; // eslint-disable-line no-param-reassign - } +var utils_1 = __webpack_require__(1); - this._handleChoiceAction(activeItems, highlightedChoice); - } +var constants_1 = __webpack_require__(0); - event.preventDefault(); - } else if (this._isSelectOneElement) { - this.showDropdown(); - event.preventDefault(); - } +var Input = +/** @class */ +function () { + function Input(_a) { + var element = _a.element, + type = _a.type, + classNames = _a.classNames, + preventPaste = _a.preventPaste; + this.element = element; + this.type = type; + this.classNames = classNames; + this.preventPaste = preventPaste; + this.isFocussed = this.element.isEqualNode(document.activeElement); + this.isDisabled = element.disabled; + this._onPaste = this._onPaste.bind(this); + this._onInput = this._onInput.bind(this); + this._onFocus = this._onFocus.bind(this); + this._onBlur = this._onBlur.bind(this); + } + + Object.defineProperty(Input.prototype, "placeholder", { + set: function set(placeholder) { + this.element.placeholder = placeholder; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Input.prototype, "value", { + get: function get() { + return utils_1.sanitise(this.element.value); + }, + set: function set(value) { + this.element.value = value; + }, + enumerable: true, + configurable: true + }); + + Input.prototype.addEventListeners = function () { + this.element.addEventListener('paste', this._onPaste); + this.element.addEventListener('input', this._onInput, { + passive: true + }); + this.element.addEventListener('focus', this._onFocus, { + passive: true + }); + this.element.addEventListener('blur', this._onBlur, { + passive: true + }); + }; + + Input.prototype.removeEventListeners = function () { + this.element.removeEventListener('input', this._onInput); + this.element.removeEventListener('paste', this._onPaste); + this.element.removeEventListener('focus', this._onFocus); + this.element.removeEventListener('blur', this._onBlur); }; - _proto._onEscapeKey = function _onEscapeKey(_ref5) { - var hasActiveDropdown = _ref5.hasActiveDropdown; + Input.prototype.enable = function () { + this.element.removeAttribute('disabled'); + this.isDisabled = false; + }; - if (hasActiveDropdown) { - this.hideDropdown(true); - this.containerOuter.focus(); + Input.prototype.disable = function () { + this.element.setAttribute('disabled', ''); + this.isDisabled = true; + }; + + Input.prototype.focus = function () { + if (!this.isFocussed) { + this.element.focus(); } }; - _proto._onDirectionKey = function _onDirectionKey(_ref6) { - var event = _ref6.event, - hasActiveDropdown = _ref6.hasActiveDropdown; - var keyCode = event.keyCode, - metaKey = event.metaKey; - var downKey = KEY_CODES.DOWN_KEY, - pageUpKey = KEY_CODES.PAGE_UP_KEY, - pageDownKey = KEY_CODES.PAGE_DOWN_KEY; // If up or down key is pressed, traverse through options + Input.prototype.blur = function () { + if (this.isFocussed) { + this.element.blur(); + } + }; - if (hasActiveDropdown || this._isSelectOneElement) { - this.showDropdown(); - this._canSearch = false; - var directionInt = keyCode === downKey || keyCode === pageDownKey ? 1 : -1; - var skipKey = metaKey || keyCode === pageDownKey || keyCode === pageUpKey; - var selectableChoiceIdentifier = '[data-choice-selectable]'; - var nextEl; + Input.prototype.clear = function (setWidth) { + if (setWidth === void 0) { + setWidth = true; + } - if (skipKey) { - if (directionInt > 0) { - nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier + ":last-of-type"); - } else { - nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); - } - } else { - var currentEl = this.dropdown.element.querySelector("." + this.config.classNames.highlightedState); + if (this.element.value) { + this.element.value = ''; + } - if (currentEl) { - nextEl = getAdjacentEl(currentEl, selectableChoiceIdentifier, directionInt); - } else { - nextEl = this.dropdown.element.querySelector(selectableChoiceIdentifier); - } - } + if (setWidth) { + this.setWidth(); + } - if (nextEl) { - // We prevent default to stop the cursor moving - // when pressing the arrow - if (!isScrolledIntoView(nextEl, this.choiceList.element, directionInt)) { - this.choiceList.scrollToChildElement(nextEl, directionInt); - } + return this; + }; + /** + * Set the correct input width based on placeholder + * value or input value + */ - this._highlightChoice(nextEl); - } // Prevent default to maintain cursor position whilst - // traversing dropdown options + Input.prototype.setWidth = function () { + // Resize input to contents or placeholder + var _a = this.element, + style = _a.style, + value = _a.value, + placeholder = _a.placeholder; + style.minWidth = placeholder.length + 1 + "ch"; + style.width = value.length + 1 + "ch"; + }; - event.preventDefault(); - } + Input.prototype.setActiveDescendant = function (activeDescendantID) { + this.element.setAttribute('aria-activedescendant', activeDescendantID); }; - _proto._onDeleteKey = function _onDeleteKey(_ref7) { - var event = _ref7.event, - hasFocusedInput = _ref7.hasFocusedInput, - activeItems = _ref7.activeItems; - var target = event.target; // If backspace or delete key is pressed and the input has no value + Input.prototype.removeActiveDescendant = function () { + this.element.removeAttribute('aria-activedescendant'); + }; - if (hasFocusedInput && !target.value && !this._isSelectOneElement) { - this._handleBackspace(activeItems); + Input.prototype._onInput = function () { + if (this.type !== constants_1.SELECT_ONE_TYPE) { + this.setWidth(); + } + }; + Input.prototype._onPaste = function (event) { + if (this.preventPaste) { event.preventDefault(); } }; - _proto._onTouchMove = function _onTouchMove() { - if (this._wasTap) { - this._wasTap = false; - } + Input.prototype._onFocus = function () { + this.isFocussed = true; }; - _proto._onTouchEnd = function _onTouchEnd(event) { - var _ref8 = event || event.touches[0], - target = _ref8.target; + Input.prototype._onBlur = function () { + this.isFocussed = false; + }; - var touchWasWithinContainer = this._wasTap && this.containerOuter.element.contains(target); + return Input; +}(); - if (touchWasWithinContainer) { - var containerWasExactTarget = target === this.containerOuter.element || target === this.containerInner.element; +exports.default = Input; - if (containerWasExactTarget) { - if (this._isTextElement) { - this.input.focus(); - } else if (this._isSelectMultipleElement) { - this.showDropdown(); - } - } // Prevents focus event firing +/***/ }), +/* 22 */ +/***/ (function(module, exports, __webpack_require__) { +"use strict"; - event.stopPropagation(); - } - this._wasTap = true; - } - /** - * Handles mousedown event in capture mode for containetOuter.element - * @param {MouseEvent} event - */ - ; +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var constants_1 = __webpack_require__(0); + +var List = +/** @class */ +function () { + function List(_a) { + var element = _a.element; + this.element = element; + this.scrollPos = this.element.scrollTop; + this.height = this.element.offsetHeight; + } + + List.prototype.clear = function () { + this.element.innerHTML = ''; + }; + + List.prototype.append = function (node) { + this.element.appendChild(node); + }; - _proto._onMouseDown = function _onMouseDown(event) { - var target = event.target; + List.prototype.getChild = function (selector) { + return this.element.querySelector(selector); + }; - if (!(target instanceof HTMLElement)) { - return; - } // If we have our mouse down on the scrollbar and are on IE11... + List.prototype.hasChildren = function () { + return this.element.hasChildNodes(); + }; + List.prototype.scrollToTop = function () { + this.element.scrollTop = 0; + }; - if (IS_IE11 && this.choiceList.element.contains(target)) { - // check if click was on a scrollbar area - var firstChoice = - /** @type {HTMLElement} */ - this.choiceList.element.firstElementChild; - var isOnScrollbar = this._direction === 'ltr' ? event.offsetX >= firstChoice.offsetWidth : event.offsetX < firstChoice.offsetLeft; - this._isScrollingOnIe = isOnScrollbar; - } + List.prototype.scrollToChildElement = function (element, direction) { + var _this = this; - if (target === this.input.element) { + if (!element) { return; } - var item = target.closest('[data-button],[data-item],[data-choice]'); + var listHeight = this.element.offsetHeight; // Scroll position of dropdown - if (item instanceof HTMLElement) { - var hasShiftKey = event.shiftKey; - var activeItems = this._store.activeItems; - var dataset = item.dataset; + var listScrollPosition = this.element.scrollTop + listHeight; + var elementHeight = element.offsetHeight; // Distance from bottom of element to top of parent - if ('button' in dataset) { - this._handleButtonAction(activeItems, item); - } else if ('item' in dataset) { - this._handleItemAction(activeItems, item, hasShiftKey); - } else if ('choice' in dataset) { - this._handleChoiceAction(activeItems, item); - } - } + var elementPos = element.offsetTop + elementHeight; // Difference between the element and scroll position - event.preventDefault(); - } - /** - * Handles mouseover event over this.dropdown - * @param {MouseEvent} event - */ - ; + var destination = direction > 0 ? this.element.scrollTop + elementPos - listScrollPosition : element.offsetTop; + requestAnimationFrame(function () { + _this._animateScroll(destination, direction); + }); + }; - _proto._onMouseOver = function _onMouseOver(_ref9) { - var target = _ref9.target; + List.prototype._scrollDown = function (scrollPos, strength, destination) { + var easing = (destination - scrollPos) / strength; + var distance = easing > 1 ? easing : 1; + this.element.scrollTop = scrollPos + distance; + }; - if (target instanceof HTMLElement && 'choice' in target.dataset) { - this._highlightChoice(target); - } + List.prototype._scrollUp = function (scrollPos, strength, destination) { + var easing = (scrollPos - destination) / strength; + var distance = easing > 1 ? easing : 1; + this.element.scrollTop = scrollPos - distance; }; - _proto._onClick = function _onClick(_ref10) { - var target = _ref10.target; - var clickWasWithinContainer = this.containerOuter.element.contains(target); + List.prototype._animateScroll = function (destination, direction) { + var _this = this; - if (clickWasWithinContainer) { - if (!this.dropdown.isActive && !this.containerOuter.isDisabled) { - if (this._isTextElement) { - if (document.activeElement !== this.input.element) { - this.input.focus(); - } - } else { - this.showDropdown(); - this.containerOuter.focus(); - } - } else if (this._isSelectOneElement && target !== this.input.element && !this.dropdown.element.contains(target)) { - this.hideDropdown(); + var strength = constants_1.SCROLLING_SPEED; + var choiceListScrollTop = this.element.scrollTop; + var continueAnimation = false; + + if (direction > 0) { + this._scrollDown(choiceListScrollTop, strength, destination); + + if (choiceListScrollTop < destination) { + continueAnimation = true; } } else { - var hasHighlightedItems = this._store.highlightedActiveItems.length > 0; + this._scrollUp(choiceListScrollTop, strength, destination); - if (hasHighlightedItems) { - this.unhighlightAll(); + if (choiceListScrollTop > destination) { + continueAnimation = true; } + } - this.containerOuter.removeFocusState(); - this.hideDropdown(true); + if (continueAnimation) { + requestAnimationFrame(function () { + _this._animateScroll(destination, direction); + }); } }; - _proto._onFocus = function _onFocus(_ref11) { - var _this17 = this, - _focusActions; - - var target = _ref11.target; - var focusWasWithinContainer = this.containerOuter.element.contains(target); + return List; +}(); - if (!focusWasWithinContainer) { - return; - } +exports.default = List; - var focusActions = (_focusActions = {}, _focusActions[TEXT_TYPE] = function () { - if (target === _this17.input.element) { - _this17.containerOuter.addFocusState(); - } - }, _focusActions[SELECT_ONE_TYPE] = function () { - _this17.containerOuter.addFocusState(); +/***/ }), +/* 23 */ +/***/ (function(module, exports, __webpack_require__) { - if (target === _this17.input.element) { - _this17.showDropdown(true); - } - }, _focusActions[SELECT_MULTIPLE_TYPE] = function () { - if (target === _this17.input.element) { - _this17.showDropdown(true); // If element is a select box, the focused element is the container and the dropdown - // isn't already open, focus and show dropdown +"use strict"; - _this17.containerOuter.addFocusState(); +var __extends = this && this.__extends || function () { + var _extendStatics = function extendStatics(d, b) { + _extendStatics = Object.setPrototypeOf || { + __proto__: [] + } instanceof Array && function (d, b) { + d.__proto__ = b; + } || function (d, b) { + for (var p in b) { + if (b.hasOwnProperty(p)) d[p] = b[p]; } - }, _focusActions); - focusActions[this.passedElement.element.type](); - }; + }; - _proto._onBlur = function _onBlur(_ref12) { - var _this18 = this; + return _extendStatics(d, b); + }; - var target = _ref12.target; - var blurWasWithinContainer = this.containerOuter.element.contains(target); + return function (d, b) { + _extendStatics(d, b); - if (blurWasWithinContainer && !this._isScrollingOnIe) { - var _blurActions; + function __() { + this.constructor = d; + } - var activeItems = this._store.activeItems; - var hasHighlightedItems = activeItems.some(function (item) { - return item.highlighted; - }); - var blurActions = (_blurActions = {}, _blurActions[TEXT_TYPE] = function () { - if (target === _this18.input.element) { - _this18.containerOuter.removeFocusState(); + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +}(); - if (hasHighlightedItems) { - _this18.unhighlightAll(); - } +var __importDefault = this && this.__importDefault || function (mod) { + return mod && mod.__esModule ? mod : { + "default": mod + }; +}; - _this18.hideDropdown(true); - } - }, _blurActions[SELECT_ONE_TYPE] = function () { - _this18.containerOuter.removeFocusState(); +Object.defineProperty(exports, "__esModule", { + value: true +}); - if (target === _this18.input.element || target === _this18.containerOuter.element && !_this18._canSearch) { - _this18.hideDropdown(true); - } - }, _blurActions[SELECT_MULTIPLE_TYPE] = function () { - if (target === _this18.input.element) { - _this18.containerOuter.removeFocusState(); +var wrapped_element_1 = __importDefault(__webpack_require__(5)); - _this18.hideDropdown(true); +var WrappedInput = +/** @class */ +function (_super) { + __extends(WrappedInput, _super); - if (hasHighlightedItems) { - _this18.unhighlightAll(); - } - } - }, _blurActions); - blurActions[this.passedElement.element.type](); - } else { - // On IE11, clicking the scollbar blurs our input and thus - // closes the dropdown. To stop this, we refocus our input - // if we know we are on IE *and* are scrolling. - this._isScrollingOnIe = false; - this.input.element.focus(); - } - }; + function WrappedInput(_a) { + var element = _a.element, + classNames = _a.classNames, + delimiter = _a.delimiter; - _proto._onFormReset = function _onFormReset() { - this._store.dispatch(resetTo(this._initialState)); - }; + var _this = _super.call(this, { + element: element, + classNames: classNames + }) || this; - _proto._highlightChoice = function _highlightChoice(el) { - var _this19 = this; + _this.delimiter = delimiter; + return _this; + } - if (el === void 0) { - el = null; - } + Object.defineProperty(WrappedInput.prototype, "value", { + get: function get() { + return this.element.value; + }, + set: function set(value) { + this.element.setAttribute('value', value); + this.element.value = value; + }, + enumerable: true, + configurable: true + }); + return WrappedInput; +}(wrapped_element_1.default); - var choices = Array.from(this.dropdown.element.querySelectorAll('[data-choice-selectable]')); +exports.default = WrappedInput; - if (!choices.length) { - return; - } +/***/ }), +/* 24 */ +/***/ (function(module, exports, __webpack_require__) { - var passedEl = el; - var highlightedChoices = Array.from(this.dropdown.element.querySelectorAll("." + this.config.classNames.highlightedState)); // Remove any highlighted choices +"use strict"; - highlightedChoices.forEach(function (choice) { - choice.classList.remove(_this19.config.classNames.highlightedState); - choice.setAttribute('aria-selected', 'false'); - }); - if (passedEl) { - this._highlightPosition = choices.indexOf(passedEl); - } else { - // Highlight choice based on last known highlight location - if (choices.length > this._highlightPosition) { - // If we have an option to highlight - passedEl = choices[this._highlightPosition]; - } else { - // Otherwise highlight the option before - passedEl = choices[choices.length - 1]; +var __extends = this && this.__extends || function () { + var _extendStatics = function extendStatics(d, b) { + _extendStatics = Object.setPrototypeOf || { + __proto__: [] + } instanceof Array && function (d, b) { + d.__proto__ = b; + } || function (d, b) { + for (var p in b) { + if (b.hasOwnProperty(p)) d[p] = b[p]; } + }; - if (!passedEl) { - passedEl = choices[0]; - } - } + return _extendStatics(d, b); + }; - passedEl.classList.add(this.config.classNames.highlightedState); - passedEl.setAttribute('aria-selected', 'true'); - this.passedElement.triggerEvent(EVENTS.highlightChoice, { - el: passedEl - }); + return function (d, b) { + _extendStatics(d, b); - if (this.dropdown.isActive) { - // IE11 ignores aria-label and blocks virtual keyboard - // if aria-activedescendant is set without a dropdown - this.input.setActiveDescendant(passedEl.id); - this.containerOuter.setActiveDescendant(passedEl.id); + function __() { + this.constructor = d; } + + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; +}(); - _proto._addItem = function _addItem(_ref13) { - var value = _ref13.value, - _ref13$label = _ref13.label, - label = _ref13$label === void 0 ? null : _ref13$label, - _ref13$choiceId = _ref13.choiceId, - choiceId = _ref13$choiceId === void 0 ? -1 : _ref13$choiceId, - _ref13$groupId = _ref13.groupId, - groupId = _ref13$groupId === void 0 ? -1 : _ref13$groupId, - _ref13$customProperti = _ref13.customProperties, - customProperties = _ref13$customProperti === void 0 ? null : _ref13$customProperti, - _ref13$placeholder = _ref13.placeholder, - placeholder = _ref13$placeholder === void 0 ? false : _ref13$placeholder, - _ref13$keyCode = _ref13.keyCode, - keyCode = _ref13$keyCode === void 0 ? null : _ref13$keyCode; - var passedValue = typeof value === 'string' ? value.trim() : value; - var passedKeyCode = keyCode; - var passedCustomProperties = customProperties; - var items = this._store.items; - var passedLabel = label || passedValue; - var passedOptionId = choiceId || -1; - var group = groupId >= 0 ? this._store.getGroupById(groupId) : null; - var id = items ? items.length + 1 : 1; // If a prepended value has been passed, prepend it +var __importDefault = this && this.__importDefault || function (mod) { + return mod && mod.__esModule ? mod : { + "default": mod + }; +}; - if (this.config.prependValue) { - passedValue = this.config.prependValue + passedValue.toString(); - } // If an appended value has been passed, append it +Object.defineProperty(exports, "__esModule", { + value: true +}); +var wrapped_element_1 = __importDefault(__webpack_require__(5)); - if (this.config.appendValue) { - passedValue += this.config.appendValue.toString(); - } +var WrappedSelect = +/** @class */ +function (_super) { + __extends(WrappedSelect, _super); - this._store.dispatch(items_addItem({ - value: passedValue, - label: passedLabel, - id: id, - choiceId: passedOptionId, - groupId: groupId, - customProperties: customProperties, - placeholder: placeholder, - keyCode: passedKeyCode - })); + function WrappedSelect(_a) { + var element = _a.element, + classNames = _a.classNames, + template = _a.template; - if (this._isSelectOneElement) { - this.removeActiveItems(id); - } // Trigger change event + var _this = _super.call(this, { + element: element, + classNames: classNames + }) || this; + _this.template = template; + return _this; + } - this.passedElement.triggerEvent(EVENTS.addItem, { - id: id, - value: passedValue, - label: passedLabel, - customProperties: passedCustomProperties, - groupValue: group && group.value ? group.value : undefined, - keyCode: passedKeyCode - }); - return this; - }; + Object.defineProperty(WrappedSelect.prototype, "placeholderOption", { + get: function get() { + return this.element.querySelector('option[value=""]') || // Backward compatibility layer for the non-standard placeholder attribute supported in older versions. + this.element.querySelector('option[placeholder]'); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(WrappedSelect.prototype, "optionGroups", { + get: function get() { + return Array.from(this.element.getElementsByTagName('OPTGROUP')); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(WrappedSelect.prototype, "options", { + get: function get() { + return Array.from(this.element.options); + }, + set: function set(options) { + var _this = this; + + var fragment = document.createDocumentFragment(); + + var addOptionToFragment = function addOptionToFragment(data) { + // Create a standard select option + var option = _this.template(data); // Append it to fragment - _proto._removeItem = function _removeItem(item) { - if (!item || !isType('Object', item)) { - return this; - } - var id = item.id, - value = item.value, - label = item.label, - choiceId = item.choiceId, - groupId = item.groupId; - var group = groupId >= 0 ? this._store.getGroupById(groupId) : null; + fragment.appendChild(option); + }; // Add each list item to list - this._store.dispatch(items_removeItem(id, choiceId)); - if (group && group.value) { - this.passedElement.triggerEvent(EVENTS.removeItem, { - id: id, - value: value, - label: label, - groupValue: group.value - }); - } else { - this.passedElement.triggerEvent(EVENTS.removeItem, { - id: id, - value: value, - label: label + options.forEach(function (optionData) { + return addOptionToFragment(optionData); }); - } + this.appendDocFragment(fragment); + }, + enumerable: true, + configurable: true + }); - return this; + WrappedSelect.prototype.appendDocFragment = function (fragment) { + this.element.innerHTML = ''; + this.element.appendChild(fragment); }; - _proto._addChoice = function _addChoice(_ref14) { - var value = _ref14.value, - _ref14$label = _ref14.label, - label = _ref14$label === void 0 ? null : _ref14$label, - _ref14$isSelected = _ref14.isSelected, - isSelected = _ref14$isSelected === void 0 ? false : _ref14$isSelected, - _ref14$isDisabled = _ref14.isDisabled, - isDisabled = _ref14$isDisabled === void 0 ? false : _ref14$isDisabled, - _ref14$groupId = _ref14.groupId, - groupId = _ref14$groupId === void 0 ? -1 : _ref14$groupId, - _ref14$customProperti = _ref14.customProperties, - customProperties = _ref14$customProperti === void 0 ? null : _ref14$customProperti, - _ref14$placeholder = _ref14.placeholder, - placeholder = _ref14$placeholder === void 0 ? false : _ref14$placeholder, - _ref14$keyCode = _ref14.keyCode, - keyCode = _ref14$keyCode === void 0 ? null : _ref14$keyCode; - - if (typeof value === 'undefined' || value === null) { - return; - } // Generate unique id + return WrappedSelect; +}(wrapped_element_1.default); +exports.default = WrappedSelect; - var choices = this._store.choices; - var choiceLabel = label || value; - var choiceId = choices ? choices.length + 1 : 1; - var choiceElementId = this._baseId + "-" + this._idNames.itemChoice + "-" + choiceId; +/***/ }), +/* 25 */ +/***/ (function(module, exports, __webpack_require__) { - this._store.dispatch(choices_addChoice({ - id: choiceId, - groupId: groupId, - elementId: choiceElementId, - value: value, - label: choiceLabel, - disabled: isDisabled, - customProperties: customProperties, - placeholder: placeholder, - keyCode: keyCode - })); +"use strict"; - if (isSelected) { - this._addItem({ - value: value, - label: choiceLabel, - choiceId: choiceId, - customProperties: customProperties, - placeholder: placeholder, - keyCode: keyCode - }); - } - }; - _proto._addGroup = function _addGroup(_ref15) { - var _this20 = this; +Object.defineProperty(exports, "__esModule", { + value: true +}); +/** + * Helpers to create HTML elements used by Choices + * Can be overridden by providing `callbackOnCreateTemplates` option + */ - var group = _ref15.group, - id = _ref15.id, - _ref15$valueKey = _ref15.valueKey, - valueKey = _ref15$valueKey === void 0 ? 'value' : _ref15$valueKey, - _ref15$labelKey = _ref15.labelKey, - labelKey = _ref15$labelKey === void 0 ? 'label' : _ref15$labelKey; - var groupChoices = isType('Object', group) ? group.choices : Array.from(group.getElementsByTagName('OPTION')); - var groupId = id || Math.floor(new Date().valueOf() * Math.random()); - var isDisabled = group.disabled ? group.disabled : false; +var templates = { + containerOuter: function containerOuter(_a, dir, isSelectElement, isSelectOneElement, searchEnabled, passedElementType) { + var containerOuter = _a.containerOuter; + var div = Object.assign(document.createElement('div'), { + className: containerOuter + }); + div.dataset.type = passedElementType; - if (groupChoices) { - this._store.dispatch(groups_addGroup({ - value: group.label, - id: groupId, - active: true, - disabled: isDisabled - })); + if (dir) { + div.dir = dir; + } - var addGroupChoices = function addGroupChoices(choice) { - var isOptDisabled = choice.disabled || choice.parentNode && choice.parentNode.disabled; + if (isSelectOneElement) { + div.tabIndex = 0; + } - _this20._addChoice({ - value: choice[valueKey], - label: isType('Object', choice) ? choice[labelKey] : choice.innerHTML, - isSelected: choice.selected, - isDisabled: isOptDisabled, - groupId: groupId, - customProperties: choice.customProperties, - placeholder: choice.placeholder - }); - }; + if (isSelectElement) { + div.setAttribute('role', searchEnabled ? 'combobox' : 'listbox'); - groupChoices.forEach(addGroupChoices); - } else { - this._store.dispatch(groups_addGroup({ - value: group.label, - id: group.id, - active: false, - disabled: group.disabled - })); + if (searchEnabled) { + div.setAttribute('aria-autocomplete', 'list'); + } } - }; - _proto._getTemplate = function _getTemplate(template) { - var _this$_templates$temp; + div.setAttribute('aria-haspopup', 'true'); + div.setAttribute('aria-expanded', 'false'); + return div; + }, + containerInner: function containerInner(_a) { + var containerInner = _a.containerInner; + return Object.assign(document.createElement('div'), { + className: containerInner + }); + }, + itemList: function itemList(_a, isSelectOneElement) { + var list = _a.list, + listSingle = _a.listSingle, + listItems = _a.listItems; + return Object.assign(document.createElement('div'), { + className: list + " " + (isSelectOneElement ? listSingle : listItems) + }); + }, + placeholder: function placeholder(_a, value) { + var placeholder = _a.placeholder; + return Object.assign(document.createElement('div'), { + className: placeholder, + innerHTML: value + }); + }, + item: function item(_a, _b, removeItemButton) { + var item = _a.item, + button = _a.button, + highlightedState = _a.highlightedState, + itemSelectable = _a.itemSelectable, + placeholder = _a.placeholder; + var id = _b.id, + value = _b.value, + label = _b.label, + customProperties = _b.customProperties, + active = _b.active, + disabled = _b.disabled, + highlighted = _b.highlighted, + isPlaceholder = _b.placeholder; + var div = Object.assign(document.createElement('div'), { + className: item, + innerHTML: label + }); + Object.assign(div.dataset, { + item: '', + id: id, + value: value, + customProperties: customProperties + }); - if (!template) { - return null; + if (active) { + div.setAttribute('aria-selected', 'true'); } - var classNames = this.config.classNames; + if (disabled) { + div.setAttribute('aria-disabled', 'true'); + } - for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { - args[_key - 1] = arguments[_key]; + if (isPlaceholder) { + div.classList.add(placeholder); } - return (_this$_templates$temp = this._templates[template]).call.apply(_this$_templates$temp, [this, classNames].concat(args)); - }; + div.classList.add(highlighted ? highlightedState : itemSelectable); - _proto._createTemplates = function _createTemplates() { - var callbackOnCreateTemplates = this.config.callbackOnCreateTemplates; - var userTemplates = {}; + if (removeItemButton) { + if (disabled) { + div.classList.remove(itemSelectable); + } - if (callbackOnCreateTemplates && typeof callbackOnCreateTemplates === 'function') { - userTemplates = callbackOnCreateTemplates.call(this, strToEl); - } + div.dataset.deletable = ''; + /** @todo This MUST be localizable, not hardcoded! */ - this._templates = cjs_default()(TEMPLATES, userTemplates); - }; + var REMOVE_ITEM_TEXT = 'Remove item'; + var removeButton = Object.assign(document.createElement('button'), { + type: 'button', + className: button, + innerHTML: REMOVE_ITEM_TEXT + }); + removeButton.setAttribute('aria-label', REMOVE_ITEM_TEXT + ": '" + value + "'"); + removeButton.dataset.button = ''; + div.appendChild(removeButton); + } - _proto._createElements = function _createElements() { - this.containerOuter = new container_Container({ - element: this._getTemplate('containerOuter', this._direction, this._isSelectElement, this._isSelectOneElement, this.config.searchEnabled, this.passedElement.element.type), - classNames: this.config.classNames, - type: this.passedElement.element.type, - position: this.config.position - }); - this.containerInner = new container_Container({ - element: this._getTemplate('containerInner'), - classNames: this.config.classNames, - type: this.passedElement.element.type, - position: this.config.position - }); - this.input = new input_Input({ - element: this._getTemplate('input', this._placeholderValue), - classNames: this.config.classNames, - type: this.passedElement.element.type, - preventPaste: !this.config.paste + return div; + }, + choiceList: function choiceList(_a, isSelectOneElement) { + var list = _a.list; + var div = Object.assign(document.createElement('div'), { + className: list }); - this.choiceList = new list_List({ - element: this._getTemplate('choiceList', this._isSelectOneElement) + + if (!isSelectOneElement) { + div.setAttribute('aria-multiselectable', 'true'); + } + + div.setAttribute('role', 'listbox'); + return div; + }, + choiceGroup: function choiceGroup(_a, _b) { + var group = _a.group, + groupHeading = _a.groupHeading, + itemDisabled = _a.itemDisabled; + var id = _b.id, + value = _b.value, + disabled = _b.disabled; + var div = Object.assign(document.createElement('div'), { + className: group + " " + (disabled ? itemDisabled : '') }); - this.itemList = new list_List({ - element: this._getTemplate('itemList', this._isSelectOneElement) + div.setAttribute('role', 'group'); + Object.assign(div.dataset, { + group: '', + id: id, + value: value }); - this.dropdown = new Dropdown({ - element: this._getTemplate('dropdown'), - classNames: this.config.classNames, - type: this.passedElement.element.type + + if (disabled) { + div.setAttribute('aria-disabled', 'true'); + } + + div.appendChild(Object.assign(document.createElement('div'), { + className: groupHeading, + innerHTML: value + })); + return div; + }, + choice: function choice(_a, _b, selectText) { + var item = _a.item, + itemChoice = _a.itemChoice, + itemSelectable = _a.itemSelectable, + selectedState = _a.selectedState, + itemDisabled = _a.itemDisabled, + placeholder = _a.placeholder; + var id = _b.id, + value = _b.value, + label = _b.label, + groupId = _b.groupId, + elementId = _b.elementId, + isDisabled = _b.disabled, + isSelected = _b.selected, + isPlaceholder = _b.placeholder; + var div = Object.assign(document.createElement('div'), { + id: elementId, + innerHTML: label, + className: item + " " + itemChoice }); - }; - _proto._createStructure = function _createStructure() { - // Hide original element - this.passedElement.conceal(); // Wrap input in container preserving DOM ordering + if (isSelected) { + div.classList.add(selectedState); + } - this.containerInner.wrap(this.passedElement.element); // Wrapper inner container with outer container + if (isPlaceholder) { + div.classList.add(placeholder); + } - this.containerOuter.wrap(this.containerInner.element); + div.setAttribute('role', groupId && groupId > 0 ? 'treeitem' : 'option'); + Object.assign(div.dataset, { + choice: '', + id: id, + value: value, + selectText: selectText + }); - if (this._isSelectOneElement) { - this.input.placeholder = this.config.searchPlaceholderValue || ''; - } else if (this._placeholderValue) { - this.input.placeholder = this._placeholderValue; - this.input.setWidth(); + if (isDisabled) { + div.classList.add(itemDisabled); + div.dataset.choiceDisabled = ''; + div.setAttribute('aria-disabled', 'true'); + } else { + div.classList.add(itemSelectable); + div.dataset.choiceSelectable = ''; } - this.containerOuter.element.appendChild(this.containerInner.element); - this.containerOuter.element.appendChild(this.dropdown.element); - this.containerInner.element.appendChild(this.itemList.element); + return div; + }, + input: function input(_a, placeholderValue) { + var input = _a.input, + inputCloned = _a.inputCloned; + var inp = Object.assign(document.createElement('input'), { + type: 'text', + className: input + " " + inputCloned, + autocomplete: 'off', + autocapitalize: 'off', + spellcheck: false + }); + inp.setAttribute('role', 'textbox'); + inp.setAttribute('aria-autocomplete', 'list'); + inp.setAttribute('aria-label', placeholderValue); + return inp; + }, + dropdown: function dropdown(_a) { + var list = _a.list, + listDropdown = _a.listDropdown; + var div = document.createElement('div'); + div.classList.add(list, listDropdown); + div.setAttribute('aria-expanded', 'false'); + return div; + }, + notice: function notice(_a, innerHTML, type) { + var item = _a.item, + itemChoice = _a.itemChoice, + noResults = _a.noResults, + noChoices = _a.noChoices; - if (!this._isTextElement) { - this.dropdown.element.appendChild(this.choiceList.element); + if (type === void 0) { + type = ''; } - if (!this._isSelectOneElement) { - this.containerInner.element.appendChild(this.input.element); - } else if (this.config.searchEnabled) { - this.dropdown.element.insertBefore(this.input.element, this.dropdown.element.firstChild); + var classes = [item, itemChoice]; + + if (type === 'no-choices') { + classes.push(noChoices); + } else if (type === 'no-results') { + classes.push(noResults); } - if (this._isSelectElement) { - this._highlightPosition = 0; - this._isSearching = false; + return Object.assign(document.createElement('div'), { + innerHTML: innerHTML, + className: classes.join(' ') + }); + }, + option: function option(_a) { + var label = _a.label, + value = _a.value, + customProperties = _a.customProperties, + active = _a.active, + disabled = _a.disabled; + var opt = new Option(label, value, false, active); - this._startLoading(); + if (customProperties) { + opt.dataset.customProperties = "" + customProperties; + } - if (this._presetGroups.length) { - this._addPredefinedGroups(this._presetGroups); - } else { - this._addPredefinedChoices(this._presetChoices); - } + opt.disabled = !!disabled; + return opt; + } +}; +exports.default = templates; - this._stopLoading(); - } +/***/ }), +/* 26 */ +/***/ (function(module, exports, __webpack_require__) { - if (this._isTextElement) { - this._addPredefinedItems(this._presetItems); - } - }; +"use strict"; - _proto._addPredefinedGroups = function _addPredefinedGroups(groups) { - var _this21 = this; - // If we have a placeholder option - var placeholderChoice = this.passedElement.placeholderOption; +Object.defineProperty(exports, "__esModule", { + value: true +}); - if (placeholderChoice && placeholderChoice.parentNode.tagName === 'SELECT') { - this._addChoice({ - value: placeholderChoice.value, - label: placeholderChoice.innerHTML, - isSelected: placeholderChoice.selected, - isDisabled: placeholderChoice.disabled, - placeholder: true - }); - } +var constants_1 = __webpack_require__(0); + +exports.addChoice = function (_a) { + var value = _a.value, + label = _a.label, + id = _a.id, + groupId = _a.groupId, + disabled = _a.disabled, + elementId = _a.elementId, + customProperties = _a.customProperties, + placeholder = _a.placeholder, + keyCode = _a.keyCode; + return { + type: constants_1.ACTION_TYPES.ADD_CHOICE, + value: value, + label: label, + id: id, + groupId: groupId, + disabled: disabled, + elementId: elementId, + customProperties: customProperties, + placeholder: placeholder, + keyCode: keyCode + }; +}; - groups.forEach(function (group) { - return _this21._addGroup({ - group: group, - id: group.id || null - }); - }); +exports.filterChoices = function (results) { + return { + type: constants_1.ACTION_TYPES.FILTER_CHOICES, + results: results }; +}; - _proto._addPredefinedChoices = function _addPredefinedChoices(choices) { - var _this22 = this; +exports.activateChoices = function (active) { + if (active === void 0) { + active = true; + } - // If sorting is enabled or the user is searching, filter choices - if (this.config.shouldSort) { - choices.sort(this.config.sorter); - } + return { + type: constants_1.ACTION_TYPES.ACTIVATE_CHOICES, + active: active + }; +}; - var hasSelectedChoice = choices.some(function (choice) { - return choice.selected; - }); - var firstEnabledChoiceIndex = choices.findIndex(function (choice) { - return choice.disabled === undefined || !choice.disabled; - }); - choices.forEach(function (choice, index) { - var value = choice.value, - label = choice.label, - customProperties = choice.customProperties, - placeholder = choice.placeholder; +exports.clearChoices = function () { + return { + type: constants_1.ACTION_TYPES.CLEAR_CHOICES + }; +}; - if (_this22._isSelectElement) { - // If the choice is actually a group - if (choice.choices) { - _this22._addGroup({ - group: choice, - id: choice.id || null - }); - } else { - /** - * If there is a selected choice already or the choice is not the first in - * the array, add each choice normally. - * - * Otherwise we pre-select the first enabled choice in the array ("select-one" only) - */ - var shouldPreselect = _this22._isSelectOneElement && !hasSelectedChoice && index === firstEnabledChoiceIndex; - var isSelected = shouldPreselect ? true : choice.selected; - var isDisabled = choice.disabled; +/***/ }), +/* 27 */ +/***/ (function(module, exports, __webpack_require__) { - _this22._addChoice({ - value: value, - label: label, - isSelected: isSelected, - isDisabled: isDisabled, - customProperties: customProperties, - placeholder: placeholder - }); - } - } else { - _this22._addChoice({ - value: value, - label: label, - isSelected: choice.selected, - isDisabled: choice.disabled, - customProperties: customProperties, - placeholder: placeholder - }); - } - }); - } - /** - * @param {Item[]} items - */ - ; +"use strict"; - _proto._addPredefinedItems = function _addPredefinedItems(items) { - var _this23 = this; - items.forEach(function (item) { - if (typeof item === 'object' && item.value) { - _this23._addItem({ - value: item.value, - label: item.label, - choiceId: item.id, - customProperties: item.customProperties, - placeholder: item.placeholder - }); - } +Object.defineProperty(exports, "__esModule", { + value: true +}); - if (typeof item === 'string') { - _this23._addItem({ - value: item - }); - } - }); +var constants_1 = __webpack_require__(0); + +exports.addItem = function (_a) { + var value = _a.value, + label = _a.label, + id = _a.id, + choiceId = _a.choiceId, + groupId = _a.groupId, + customProperties = _a.customProperties, + placeholder = _a.placeholder, + keyCode = _a.keyCode; + return { + type: constants_1.ACTION_TYPES.ADD_ITEM, + value: value, + label: label, + id: id, + choiceId: choiceId, + groupId: groupId, + customProperties: customProperties, + placeholder: placeholder, + keyCode: keyCode }; +}; - _proto._setChoiceOrItem = function _setChoiceOrItem(item) { - var _this24 = this; +exports.removeItem = function (id, choiceId) { + return { + type: constants_1.ACTION_TYPES.REMOVE_ITEM, + id: id, + choiceId: choiceId + }; +}; - var itemType = getType(item).toLowerCase(); - var handleType = { - object: function object() { - if (!item.value) { - return; - } // If we are dealing with a select input, we need to create an option first - // that is then selected. For text inputs we can just add items normally. +exports.highlightItem = function (id, highlighted) { + return { + type: constants_1.ACTION_TYPES.HIGHLIGHT_ITEM, + id: id, + highlighted: highlighted + }; +}; +/***/ }), +/* 28 */ +/***/ (function(module, exports, __webpack_require__) { - if (!_this24._isTextElement) { - _this24._addChoice({ - value: item.value, - label: item.label, - isSelected: true, - isDisabled: false, - customProperties: item.customProperties, - placeholder: item.placeholder - }); - } else { - _this24._addItem({ - value: item.value, - label: item.label, - choiceId: item.id, - customProperties: item.customProperties, - placeholder: item.placeholder - }); - } - }, - string: function string() { - if (!_this24._isTextElement) { - _this24._addChoice({ - value: item, - label: item, - isSelected: true, - isDisabled: false - }); - } else { - _this24._addItem({ - value: item - }); - } - } - }; - handleType[itemType](); - }; +"use strict"; - _proto._findAndSelectChoiceByValue = function _findAndSelectChoiceByValue(val) { - var _this25 = this; - var choices = this._store.choices; // Check 'value' property exists and the choice isn't already selected +Object.defineProperty(exports, "__esModule", { + value: true +}); - var foundChoice = choices.find(function (choice) { - return _this25.config.valueComparer(choice.value, val); - }); +var constants_1 = __webpack_require__(0); - if (foundChoice && !foundChoice.selected) { - this._addItem({ - value: foundChoice.value, - label: foundChoice.label, - choiceId: foundChoice.id, - groupId: foundChoice.groupId, - customProperties: foundChoice.customProperties, - placeholder: foundChoice.placeholder, - keyCode: foundChoice.keyCode - }); - } +exports.addGroup = function (_a) { + var value = _a.value, + id = _a.id, + active = _a.active, + disabled = _a.disabled; + return { + type: constants_1.ACTION_TYPES.ADD_GROUP, + value: value, + id: id, + active: active, + disabled: disabled }; +}; - _proto._generatePlaceholderValue = function _generatePlaceholderValue() { - if (this._isSelectElement) { - var placeholderOption = this.passedElement.placeholderOption; - return placeholderOption ? placeholderOption.text : false; - } +/***/ }), +/* 29 */ +/***/ (function(module, exports, __webpack_require__) { - var _this$config4 = this.config, - placeholder = _this$config4.placeholder, - placeholderValue = _this$config4.placeholderValue; - var dataset = this.passedElement.element.dataset; +"use strict"; - if (placeholder) { - if (placeholderValue) { - return placeholderValue; - } - if (dataset.placeholder) { - return dataset.placeholder; - } - } +Object.defineProperty(exports, "__esModule", { + value: true +}); - return false; +var constants_1 = __webpack_require__(0); + +exports.clearAll = function () { + return { + type: constants_1.ACTION_TYPES.CLEAR_ALL }; +}; - return Choices; -}(); +exports.resetTo = function (state) { + return { + type: constants_1.ACTION_TYPES.RESET_TO, + state: state + }; +}; -/* harmony default export */ var scripts_choices = __webpack_exports__["default"] = (choices_Choices); +exports.setIsLoading = function (isLoading) { + return { + type: constants_1.ACTION_TYPES.SET_IS_LOADING, + isLoading: isLoading + }; +}; /***/ }) /******/ ])["default"]; diff --git a/public/assets/scripts/choices.min.js b/public/assets/scripts/choices.min.js index 8223c620e..eb8462b6b 100644 --- a/public/assets/scripts/choices.min.js +++ b/public/assets/scripts/choices.min.js @@ -1,11 +1,11 @@ /*! choices.js v9.0.1 | © 2019 Josh Johnson | https://github.com/jshjohnson/Choices#readme */ -window.Choices=function(e){var t={};function i(n){if(t[n])return t[n].exports;var s=t[n]={i:n,l:!1,exports:{}};return e[n].call(s.exports,s,s.exports,i),s.l=!0,s.exports}return i.m=e,i.c=t,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)i.d(n,s,function(t){return e[t]}.bind(null,s));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/public/assets/scripts/",i(i.s=4)}([function(e,t,i){"use strict";var n=function(e){return function(e){return!!e&&"object"==typeof e}(e)&&!function(e){var t=Object.prototype.toString.call(e);return"[object RegExp]"===t||"[object Date]"===t||function(e){return e.$$typeof===s}(e)}(e)};var s="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function r(e,t){return!1!==t.clone&&t.isMergeableObject(e)?l((i=e,Array.isArray(i)?[]:{}),e,t):e;var i}function o(e,t,i){return e.concat(t).map((function(e){return r(e,i)}))}function a(e){return Object.keys(e).concat(function(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter((function(t){return e.propertyIsEnumerable(t)})):[]}(e))}function c(e,t,i){var n={};return i.isMergeableObject(e)&&a(e).forEach((function(t){n[t]=r(e[t],i)})),a(t).forEach((function(s){(function(e,t){try{return t in e&&!(Object.hasOwnProperty.call(e,t)&&Object.propertyIsEnumerable.call(e,t))}catch(e){return!1}})(e,s)||(i.isMergeableObject(t[s])&&e[s]?n[s]=function(e,t){if(!t.customMerge)return l;var i=t.customMerge(e);return"function"==typeof i?i:l}(s,i)(e[s],t[s],i):n[s]=r(t[s],i))})),n}function l(e,t,i){(i=i||{}).arrayMerge=i.arrayMerge||o,i.isMergeableObject=i.isMergeableObject||n,i.cloneUnlessOtherwiseSpecified=r;var s=Array.isArray(t);return s===Array.isArray(e)?s?i.arrayMerge(e,t,i):c(e,t,i):r(t,i)}l.all=function(e,t){if(!Array.isArray(e))throw new Error("first argument should be an array");return e.reduce((function(e,i){return l(e,i,t)}),{})};var h=l;e.exports=h},function(e,t,i){"use strict";(function(e,n){var s,r=i(3);s="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==e?e:n;var o=Object(r.a)(s);t.a=o}).call(this,i(5),i(6)(e))},function(e,t,i){ +window.Choices=function(e){var t={};function i(n){if(t[n])return t[n].exports;var r=t[n]={i:n,l:!1,exports:{}};return e[n].call(r.exports,r,r.exports,i),r.l=!0,r.exports}return i.m=e,i.c=t,i.d=function(e,t,n){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)i.d(n,r,function(t){return e[t]}.bind(null,r));return n},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="/public/assets/scripts/",i(i.s=7)}([function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(1);t.DEFAULT_CLASSNAMES={containerOuter:"choices",containerInner:"choices__inner",input:"choices__input",inputCloned:"choices__input--cloned",list:"choices__list",listItems:"choices__list--multiple",listSingle:"choices__list--single",listDropdown:"choices__list--dropdown",item:"choices__item",itemSelectable:"choices__item--selectable",itemDisabled:"choices__item--disabled",itemChoice:"choices__item--choice",placeholder:"choices__placeholder",group:"choices__group",groupHeading:"choices__heading",button:"choices__button",activeState:"is-active",focusState:"is-focused",openState:"is-open",disabledState:"is-disabled",highlightedState:"is-highlighted",selectedState:"is-selected",flippedState:"is-flipped",loadingState:"is-loading",noResults:"has-no-results",noChoices:"has-no-choices"},t.DEFAULT_CONFIG={items:[],choices:[],silent:!1,renderChoiceLimit:-1,maxItemCount:-1,addItems:!0,addItemFilter:null,removeItems:!0,removeItemButton:!1,editItems:!1,duplicateItemsAllowed:!0,delimiter:",",paste:!0,searchEnabled:!0,searchChoices:!0,searchFloor:1,searchResultLimit:4,searchFields:["label","value"],position:"auto",resetScrollPosition:!0,shouldSort:!0,shouldSortItems:!1,sorter:n.sortByAlpha,placeholder:!0,placeholderValue:null,searchPlaceholderValue:null,prependValue:null,appendValue:null,renderSelectedChoices:"auto",loadingText:"Loading...",noResultsText:"No results found",noChoicesText:"No choices to choose from",itemSelectText:"Press to select",uniqueItemText:"Only unique values can be added",customAddItemText:"Only values matching specific conditions can be added",addItemText:function(e){return'Press Enter to add "'+n.sanitise(e)+'"'},maxItemText:function(e){return"Only "+e+" values can be added"},valueComparer:function(e,t){return e===t},fuseOptions:{includeScore:!0},callbackOnInit:null,callbackOnCreateTemplates:null,classNames:t.DEFAULT_CLASSNAMES},t.EVENTS={showDropdown:"showDropdown",hideDropdown:"hideDropdown",change:"change",choice:"choice",search:"search",addItem:"addItem",removeItem:"removeItem",highlightItem:"highlightItem",highlightChoice:"highlightChoice",unhighlightItem:"unhighlightItem"},t.ACTION_TYPES={ADD_CHOICE:"ADD_CHOICE",FILTER_CHOICES:"FILTER_CHOICES",ACTIVATE_CHOICES:"ACTIVATE_CHOICES",CLEAR_CHOICES:"CLEAR_CHOICES",ADD_GROUP:"ADD_GROUP",ADD_ITEM:"ADD_ITEM",REMOVE_ITEM:"REMOVE_ITEM",HIGHLIGHT_ITEM:"HIGHLIGHT_ITEM",CLEAR_ALL:"CLEAR_ALL",RESET_TO:"RESET_TO",SET_IS_LOADING:"SET_IS_LOADING"},t.KEY_CODES={BACK_KEY:46,DELETE_KEY:8,ENTER_KEY:13,A_KEY:65,ESC_KEY:27,UP_KEY:38,DOWN_KEY:40,PAGE_UP_KEY:33,PAGE_DOWN_KEY:34},t.TEXT_TYPE="text",t.SELECT_ONE_TYPE="select-one",t.SELECT_MULTIPLE_TYPE="select-multiple",t.SCROLLING_SPEED=4},function(e,t,i){"use strict";var n;Object.defineProperty(t,"__esModule",{value:!0}),t.getRandomNumber=function(e,t){return Math.floor(Math.random()*(t-e)+e)},t.generateChars=function(e){return Array.from({length:e},(function(){return t.getRandomNumber(0,36).toString(36)})).join("")},t.generateId=function(e,i){var n=e.id||e.name&&e.name+"-"+t.generateChars(2)||t.generateChars(4);return n=i+"-"+(n=n.replace(/(:|\.|\[|\]|,)/g,""))},t.getType=function(e){return Object.prototype.toString.call(e).slice(8,-1)},t.isType=function(e,i){return null!=i&&t.getType(i)===e},t.wrap=function(e,t){return void 0===t&&(t=document.createElement("div")),e.nextSibling?e.parentNode&&e.parentNode.insertBefore(t,e.nextSibling):e.parentNode&&e.parentNode.appendChild(t),t.appendChild(e)},t.getAdjacentEl=function(e,t,i){void 0===i&&(i=1);for(var n=(i>0?"next":"previous")+"ElementSibling",r=e[n];r;){if(r.matches(t))return r;r=r[n]}return r},t.isScrolledIntoView=function(e,t,i){return void 0===i&&(i=1),!!e&&(i>0?t.scrollTop+t.offsetHeight>=e.offsetTop+e.offsetHeight:e.offsetTop>=t.scrollTop)},t.sanitise=function(e){return"string"!=typeof e?e:e.replace(/&/g,"&").replace(/>/g,"&rt;").replace(/=0?this._store.getGroupById(r):null;return this._store.dispatch(d.highlightItem(i,!0)),t&&this.passedElement.triggerEvent(l.EVENTS.highlightItem,{id:i,value:s,label:c,groupValue:u&&u.value?u.value:null}),this},e.prototype.unhighlightItem=function(e){if(!e||!e.id)return this;var t=e.id,i=e.groupId,n=void 0===i?-1:i,r=e.value,o=void 0===r?"":r,s=e.label,a=void 0===s?"":s,c=n>=0?this._store.getGroupById(n):null;return this._store.dispatch(d.highlightItem(t,!1)),this.passedElement.triggerEvent(l.EVENTS.highlightItem,{id:t,value:o,label:a,groupValue:c&&c.value?c.value:null}),this},e.prototype.highlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.highlightItem(t)})),this},e.prototype.unhighlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.unhighlightItem(t)})),this},e.prototype.removeActiveItemsByValue=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.value===e})).forEach((function(e){return t._removeItem(e)})),this},e.prototype.removeActiveItems=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.id!==e})).forEach((function(e){return t._removeItem(e)})),this},e.prototype.removeHighlightedItems=function(e){var t=this;return void 0===e&&(e=!1),this._store.highlightedActiveItems.forEach((function(i){t._removeItem(i),e&&t._triggerChange(i.value)})),this},e.prototype.showDropdown=function(e){var t=this;return this.dropdown.isActive?this:(requestAnimationFrame((function(){t.dropdown.show(),t.containerOuter.open(t.dropdown.distanceFromTopWindow),!e&&t._canSearch&&t.input.focus(),t.passedElement.triggerEvent(l.EVENTS.showDropdown,{})})),this)},e.prototype.hideDropdown=function(e){var t=this;return this.dropdown.isActive?(requestAnimationFrame((function(){t.dropdown.hide(),t.containerOuter.close(),!e&&t._canSearch&&(t.input.removeActiveDescendant(),t.input.blur()),t.passedElement.triggerEvent(l.EVENTS.hideDropdown,{})})),this):this},e.prototype.getValue=function(e){void 0===e&&(e=!1);var t=this._store.activeItems.reduce((function(t,i){var n=e?i.value:i;return t.push(n),t}),[]);return this._isSelectOneElement?t[0]:t},e.prototype.setValue=function(e){var t=this;return this.initialised?(e.forEach((function(e){return t._setChoiceOrItem(e)})),this):this},e.prototype.setChoiceByValue=function(e){var t=this;return!this.initialised||this._isTextElement?this:((Array.isArray(e)?e:[e]).forEach((function(e){return t._findAndSelectChoiceByValue(e)})),this)},e.prototype.setChoices=function(e,t,i,n){var r=this;if(void 0===e&&(e=[]),void 0===t&&(t="value"),void 0===i&&(i="label"),void 0===n&&(n=!1),!this.initialised)throw new ReferenceError("setChoices was called on a non-initialized instance of Choices");if(!this._isSelectElement)throw new TypeError("setChoices can't be used with INPUT based Choices");if("string"!=typeof t||!t)throw new TypeError("value parameter must be a name of 'value' field in passed objects");if(n&&this.clearChoices(),"function"==typeof e){var o=e(this);if("function"==typeof Promise&&o instanceof Promise)return new Promise((function(e){return requestAnimationFrame(e)})).then((function(){return r._handleLoadingState(!0)})).then((function(){return o})).then((function(e){return r.setChoices(e,t,i,n)})).catch((function(e){r.config.silent||console.error(e)})).then((function(){return r._handleLoadingState(!1)})).then((function(){return r}));if(!Array.isArray(o))throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: "+typeof o);return this.setChoices(o,t,i,!1)}if(!Array.isArray(e))throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices");return this.containerOuter.removeLoadingState(),this._startLoading(),e.forEach((function(e){if(e.choices)r._addGroup({id:e.id?parseInt(""+e.id,10):null,group:e,valueKey:t,labelKey:i});else{var n=e;r._addChoice({value:n[t],label:n[i],isSelected:!!n.selected,isDisabled:!!n.disabled,placeholder:!!n.placeholder,customProperties:n.customProperties})}})),this._stopLoading(),this},e.prototype.clearChoices=function(){return this._store.dispatch(h.clearChoices()),this},e.prototype.clearStore=function(){return this._store.dispatch(f.clearAll()),this},e.prototype.clearInput=function(){var e=!this._isSelectOneElement;return this.input.clear(e),!this._isTextElement&&this._canSearch&&(this._isSearching=!1,this._store.dispatch(h.activateChoices(!0))),this},e.prototype._render=function(){if(!this._store.isLoading()){this._currentState=this._store.state;var e=this._currentState.choices!==this._prevState.choices||this._currentState.groups!==this._prevState.groups||this._currentState.items!==this._prevState.items,t=this._isSelectElement,i=this._currentState.items!==this._prevState.items;e&&(t&&this._renderChoices(),i&&this._renderItems(),this._prevState=this._currentState)}},e.prototype._renderChoices=function(){var e=this,t=this._store,i=t.activeGroups,n=t.activeChoices,r=document.createDocumentFragment();if(this.choiceList.clear(),this.config.resetScrollPosition&&requestAnimationFrame((function(){return e.choiceList.scrollToTop()})),i.length>=1&&!this._isSearching){var o=n.filter((function(e){return!0===e.placeholder&&-1===e.groupId}));o.length>=1&&(r=this._createChoicesFragment(o,r)),r=this._createGroupsFragment(i,n,r)}else n.length>=1&&(r=this._createChoicesFragment(n,r));if(r.childNodes&&r.childNodes.length>0){var s=this._store.activeItems,a=this._canAddItem(s,this.input.value);if(a.response)this.choiceList.append(r),this._highlightChoice();else{var c=this._getTemplate("notice",a.notice);this.choiceList.append(c)}}else{var l=void 0;c=void 0;this._isSearching?(c="function"==typeof this.config.noResultsText?this.config.noResultsText():this.config.noResultsText,l=this._getTemplate("notice",c,"no-results")):(c="function"==typeof this.config.noChoicesText?this.config.noChoicesText():this.config.noChoicesText,l=this._getTemplate("notice",c,"no-choices")),this.choiceList.append(l)}},e.prototype._renderItems=function(){var e=this._store.activeItems||[];this.itemList.clear();var t=this._createItemsFragment(e);t.childNodes&&this.itemList.append(t)},e.prototype._createGroupsFragment=function(e,t,i){var n=this;void 0===i&&(i=document.createDocumentFragment());return this.config.shouldSort&&e.sort(this.config.sorter),e.forEach((function(e){var r=function(e){return t.filter((function(t){return n._isSelectOneElement?t.groupId===e.id:t.groupId===e.id&&("always"===n.config.renderSelectedChoices||!t.selected)}))}(e);if(r.length>=1){var o=n._getTemplate("choiceGroup",e);i.appendChild(o),n._createChoicesFragment(r,i,!0)}})),i},e.prototype._createChoicesFragment=function(e,t,i){var r=this;void 0===t&&(t=document.createDocumentFragment()),void 0===i&&(i=!1);var o=this.config,s=o.renderSelectedChoices,a=o.searchResultLimit,c=o.renderChoiceLimit,l=this._isSearching?m.sortByScore:this.config.sorter,u=function(e){if("auto"!==s||(r._isSelectOneElement||!e.selected)){var i=r._getTemplate("choice",e,r.config.itemSelectText);t.appendChild(i)}},h=e;"auto"!==s||this._isSelectOneElement||(h=e.filter((function(e){return!e.selected})));var d=h.reduce((function(e,t){return t.placeholder?e.placeholderChoices.push(t):e.normalChoices.push(t),e}),{placeholderChoices:[],normalChoices:[]}),p=d.placeholderChoices,f=d.normalChoices;(this.config.shouldSort||this._isSearching)&&f.sort(l);var v=h.length,_=this._isSelectOneElement?n(p,f):f;this._isSearching?v=a:c&&c>0&&!i&&(v=c);for(var g=0;g=n){var s=r?this._searchChoices(e):0;this.passedElement.triggerEvent(l.EVENTS.search,{value:e,resultCount:s})}else o&&(this._isSearching=!1,this._store.dispatch(h.activateChoices(!0)))}},e.prototype._canAddItem=function(e,t){var i=!0,n="function"==typeof this.config.addItemText?this.config.addItemText(t):this.config.addItemText;if(!this._isSelectOneElement){var r=m.existsInArray(e,t);this.config.maxItemCount>0&&this.config.maxItemCount<=e.length&&(i=!1,n="function"==typeof this.config.maxItemText?this.config.maxItemText(this.config.maxItemCount):this.config.maxItemText),!this.config.duplicateItemsAllowed&&r&&i&&(i=!1,n="function"==typeof this.config.uniqueItemText?this.config.uniqueItemText(t):this.config.uniqueItemText),this._isTextElement&&this.config.addItems&&i&&"function"==typeof this.config.addItemFilter&&!this.config.addItemFilter(t)&&(i=!1,n="function"==typeof this.config.customAddItemText?this.config.customAddItemText(t):this.config.customAddItemText)}return{response:i,notice:n}},e.prototype._searchChoices=function(e){var t="string"==typeof e?e.trim():e,i="string"==typeof this._currentValue?this._currentValue.trim():this._currentValue;if(t.length<1&&t===i+" ")return 0;var r=this._store.searchableChoices,s=t,a=n(this.config.searchFields),c=Object.assign(this.config.fuseOptions,{keys:a,includeMatches:!0}),l=new o.default(r,c).search(s);return this._currentValue=t,this._highlightPosition=0,this._isSearching=!0,this._store.dispatch(h.filterChoices(l)),l.length},e.prototype._addEventListeners=function(){var e=document.documentElement;e.addEventListener("touchend",this._onTouchEnd,!0),this.containerOuter.element.addEventListener("keydown",this._onKeyDown,!0),this.containerOuter.element.addEventListener("mousedown",this._onMouseDown,!0),e.addEventListener("click",this._onClick,{passive:!0}),e.addEventListener("touchmove",this._onTouchMove,{passive:!0}),this.dropdown.element.addEventListener("mouseover",this._onMouseOver,{passive:!0}),this._isSelectOneElement&&(this.containerOuter.element.addEventListener("focus",this._onFocus,{passive:!0}),this.containerOuter.element.addEventListener("blur",this._onBlur,{passive:!0})),this.input.element.addEventListener("keyup",this._onKeyUp,{passive:!0}),this.input.element.addEventListener("focus",this._onFocus,{passive:!0}),this.input.element.addEventListener("blur",this._onBlur,{passive:!0}),this.input.element.form&&this.input.element.form.addEventListener("reset",this._onFormReset,{passive:!0}),this.input.addEventListeners()},e.prototype._removeEventListeners=function(){var e=document.documentElement;e.removeEventListener("touchend",this._onTouchEnd,!0),this.containerOuter.element.removeEventListener("keydown",this._onKeyDown,!0),this.containerOuter.element.removeEventListener("mousedown",this._onMouseDown,!0),e.removeEventListener("click",this._onClick),e.removeEventListener("touchmove",this._onTouchMove),this.dropdown.element.removeEventListener("mouseover",this._onMouseOver),this._isSelectOneElement&&(this.containerOuter.element.removeEventListener("focus",this._onFocus),this.containerOuter.element.removeEventListener("blur",this._onBlur)),this.input.element.removeEventListener("keyup",this._onKeyUp),this.input.element.removeEventListener("focus",this._onFocus),this.input.element.removeEventListener("blur",this._onBlur),this.input.element.form&&this.input.element.form.removeEventListener("reset",this._onFormReset),this.input.removeEventListeners()},e.prototype._onKeyDown=function(e){var t=e.keyCode,i=this._store.activeItems,n=this.input.isFocussed,r=this.dropdown.isActive,o=this.itemList.hasChildren(),s=String.fromCharCode(t),a=/[a-zA-Z0-9-_ ]/.test(s),c=l.KEY_CODES.BACK_KEY,u=l.KEY_CODES.DELETE_KEY,h=l.KEY_CODES.ENTER_KEY,d=l.KEY_CODES.A_KEY,p=l.KEY_CODES.ESC_KEY,f=l.KEY_CODES.UP_KEY,m=l.KEY_CODES.DOWN_KEY,v=l.KEY_CODES.PAGE_UP_KEY,_=l.KEY_CODES.PAGE_DOWN_KEY;switch(this._isTextElement||r||!a||(this.showDropdown(),this.input.isFocussed||(this.input.value+=s.toLowerCase())),t){case d:return this._onSelectKey(e,o);case h:return this._onEnterKey(e,i,r);case p:return this._onEscapeKey(r);case f:case v:case m:case _:return this._onDirectionKey(e,r);case u:case c:return this._onDeleteKey(e,i,n)}},e.prototype._onKeyUp=function(e){var t=e.target,i=e.keyCode,n=this.input.value,r=this._store.activeItems,o=this._canAddItem(r,n),s=l.KEY_CODES.BACK_KEY,a=l.KEY_CODES.DELETE_KEY;if(this._isTextElement){if(o.notice&&n){var c=this._getTemplate("notice",o.notice);this.dropdown.element.innerHTML=c.outerHTML,this.showDropdown(!0)}else this.hideDropdown(!0)}else{var u=(i===s||i===a)&&t&&!t.value,d=!this._isTextElement&&this._isSearching,p=this._canSearch&&o.response;u&&d?(this._isSearching=!1,this._store.dispatch(h.activateChoices(!0))):p&&this._handleSearch(this.input.value)}this._canSearch=this.config.searchEnabled},e.prototype._onSelectKey=function(e,t){var i=e.ctrlKey,n=e.metaKey;(i||n)&&t&&(this._canSearch=!1,this.config.removeItems&&!this.input.value&&this.input.element===document.activeElement&&this.highlightAll())},e.prototype._onEnterKey=function(e,t,i){var n=e.target,r=l.KEY_CODES.ENTER_KEY,o=n&&n.hasAttribute("data-button");if(this._isTextElement&&n&&n.value){var s=this.input.value;this._canAddItem(t,s).response&&(this.hideDropdown(!0),this._addItem({value:s}),this._triggerChange(s),this.clearInput())}if(o&&(this._handleButtonAction(t,n),e.preventDefault()),i){var a=this.dropdown.getChild("."+this.config.classNames.highlightedState);a&&(t[0]&&(t[0].keyCode=r),this._handleChoiceAction(t,a)),e.preventDefault()}else this._isSelectOneElement&&(this.showDropdown(),e.preventDefault())},e.prototype._onEscapeKey=function(e){e&&(this.hideDropdown(!0),this.containerOuter.focus())},e.prototype._onDirectionKey=function(e,t){var i=e.keyCode,n=e.metaKey,r=l.KEY_CODES.DOWN_KEY,o=l.KEY_CODES.PAGE_UP_KEY,s=l.KEY_CODES.PAGE_DOWN_KEY;if(t||this._isSelectOneElement){this.showDropdown(),this._canSearch=!1;var a=i===r||i===s?1:-1,c=void 0;if(n||i===s||i===o)c=a>0?this.dropdown.element.querySelector("[data-choice-selectable]:last-of-type"):this.dropdown.element.querySelector("[data-choice-selectable]");else{var u=this.dropdown.element.querySelector("."+this.config.classNames.highlightedState);c=u?m.getAdjacentEl(u,"[data-choice-selectable]",a):this.dropdown.element.querySelector("[data-choice-selectable]")}c&&(m.isScrolledIntoView(c,this.choiceList.element,a)||this.choiceList.scrollToChildElement(c,a),this._highlightChoice(c)),e.preventDefault()}},e.prototype._onDeleteKey=function(e,t,i){var n=e.target;this._isSelectOneElement||n.value||!i||(this._handleBackspace(t),e.preventDefault())},e.prototype._onTouchMove=function(){this._wasTap&&(this._wasTap=!1)},e.prototype._onTouchEnd=function(e){var t=(e||e.touches[0]).target;this._wasTap&&this.containerOuter.element.contains(t)&&((t===this.containerOuter.element||t===this.containerInner.element)&&(this._isTextElement?this.input.focus():this._isSelectMultipleElement&&this.showDropdown()),e.stopPropagation());this._wasTap=!0},e.prototype._onMouseDown=function(e){var t=e.target;if(t instanceof HTMLElement){if(_&&this.choiceList.element.contains(t)){var i=this.choiceList.element.firstElementChild,n="ltr"===this._direction?e.offsetX>=i.offsetWidth:e.offsetX0&&this.unhighlightAll(),this.containerOuter.removeFocusState(),this.hideDropdown(!0))},e.prototype._onFocus=function(e){var t,i=this,n=e.target;n&&this.containerOuter.element.contains(n)&&((t={})[l.TEXT_TYPE]=function(){n===i.input.element&&i.containerOuter.addFocusState()},t[l.SELECT_ONE_TYPE]=function(){i.containerOuter.addFocusState(),n===i.input.element&&i.showDropdown(!0)},t[l.SELECT_MULTIPLE_TYPE]=function(){n===i.input.element&&(i.showDropdown(!0),i.containerOuter.addFocusState())},t)[this.passedElement.element.type]()},e.prototype._onBlur=function(e){var t,i=this,n=e.target;if(n&&this.containerOuter.element.contains(n)&&!this._isScrollingOnIe){var r=this._store.activeItems.some((function(e){return e.highlighted}));((t={})[l.TEXT_TYPE]=function(){n===i.input.element&&(i.containerOuter.removeFocusState(),r&&i.unhighlightAll(),i.hideDropdown(!0))},t[l.SELECT_ONE_TYPE]=function(){i.containerOuter.removeFocusState(),(n===i.input.element||n===i.containerOuter.element&&!i._canSearch)&&i.hideDropdown(!0)},t[l.SELECT_MULTIPLE_TYPE]=function(){n===i.input.element&&(i.containerOuter.removeFocusState(),i.hideDropdown(!0),r&&i.unhighlightAll())},t)[this.passedElement.element.type]()}else this._isScrollingOnIe=!1,this.input.element.focus()},e.prototype._onFormReset=function(){this._store.dispatch(f.resetTo(this._initialState))},e.prototype._highlightChoice=function(e){var t=this;void 0===e&&(e=null);var i=Array.from(this.dropdown.element.querySelectorAll("[data-choice-selectable]"));if(i.length){var n=e;Array.from(this.dropdown.element.querySelectorAll("."+this.config.classNames.highlightedState)).forEach((function(e){e.classList.remove(t.config.classNames.highlightedState),e.setAttribute("aria-selected","false")})),n?this._highlightPosition=i.indexOf(n):(n=i.length>this._highlightPosition?i[this._highlightPosition]:i[i.length-1])||(n=i[0]),n.classList.add(this.config.classNames.highlightedState),n.setAttribute("aria-selected","true"),this.passedElement.triggerEvent(l.EVENTS.highlightChoice,{el:n}),this.dropdown.isActive&&(this.input.setActiveDescendant(n.id),this.containerOuter.setActiveDescendant(n.id))}},e.prototype._addItem=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,r=e.choiceId,o=void 0===r?-1:r,s=e.groupId,a=void 0===s?-1:s,c=e.customProperties,u=void 0===c?{}:c,h=e.placeholder,p=void 0!==h&&h,f=e.keyCode,m=void 0===f?-1:f,v="string"==typeof t?t.trim():t,_=this._store.items,g=n||v,y=o||-1,b=a>=0?this._store.getGroupById(a):null,E=_?_.length+1:1;this.config.prependValue&&(v=this.config.prependValue+v.toString()),this.config.appendValue&&(v+=this.config.appendValue.toString()),this._store.dispatch(d.addItem({value:v,label:g,id:E,choiceId:y,groupId:a,customProperties:u,placeholder:p,keyCode:m})),this._isSelectOneElement&&this.removeActiveItems(E),this.passedElement.triggerEvent(l.EVENTS.addItem,{id:E,value:v,label:g,customProperties:u,groupValue:b&&b.value?b.value:null,keyCode:m})},e.prototype._removeItem=function(e){var t=e.id,i=e.value,n=e.label,r=e.customProperties,o=e.choiceId,s=e.groupId,a=s&&s>=0?this._store.getGroupById(s):null;t&&o&&(this._store.dispatch(d.removeItem(t,o)),this.passedElement.triggerEvent(l.EVENTS.removeItem,{id:t,value:i,label:n,customProperties:r,groupValue:a&&a.value?a.value:null}))},e.prototype._addChoice=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,r=e.isSelected,o=void 0!==r&&r,s=e.isDisabled,a=void 0!==s&&s,c=e.groupId,l=void 0===c?-1:c,u=e.customProperties,d=void 0===u?{}:u,p=e.placeholder,f=void 0!==p&&p,m=e.keyCode,v=void 0===m?-1:m;if(null!=t){var _=this._store.choices,g=n||t,y=_?_.length+1:1,b=this._baseId+"-"+this._idNames.itemChoice+"-"+y;this._store.dispatch(h.addChoice({id:y,groupId:l,elementId:b,value:t,label:g,disabled:a,customProperties:d,placeholder:f,keyCode:v})),o&&this._addItem({value:t,label:g,choiceId:y,customProperties:d,placeholder:f,keyCode:v})}},e.prototype._addGroup=function(e){var t=this,i=e.group,n=e.id,r=e.valueKey,o=void 0===r?"value":r,s=e.labelKey,a=void 0===s?"label":s,c=m.isType("Object",i)?i.choices:Array.from(i.getElementsByTagName("OPTION")),l=n||Math.floor((new Date).valueOf()*Math.random()),u=!!i.disabled&&i.disabled;if(c){this._store.dispatch(p.addGroup({value:i.label,id:l,active:!0,disabled:u}));c.forEach((function(e){var i=e.disabled||e.parentNode&&e.parentNode.disabled;t._addChoice({value:e[o],label:m.isType("Object",e)?e[a]:e.innerHTML,isSelected:e.selected,isDisabled:i,groupId:l,customProperties:e.customProperties,placeholder:e.placeholder})}))}else this._store.dispatch(p.addGroup({value:i.label,id:i.id,active:!1,disabled:i.disabled}))},e.prototype._getTemplate=function(e){for(var t,i=[],r=1;r1&&void 0!==arguments[1]?arguments[1]:{limit:!1};this._log('---------\nSearch pattern: "'.concat(e,'"'));var i=this._prepareSearchers(e),n=i.tokenSearchers,s=i.fullSearcher,r=this._search(n,s),o=r.weights,a=r.results;return this._computeScore(o,a),this.options.shouldSort&&this._sort(a),t.limit&&"number"==typeof t.limit&&(a=a.slice(0,t.limit)),this._format(a)}},{key:"_prepareSearchers",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=[];if(this.options.tokenize)for(var i=e.split(this.options.tokenSeparator),n=0,s=i.length;n0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1?arguments[1]:void 0,i=this.list,n={},s=[];if("string"==typeof i[0]){for(var r=0,o=i.length;r1)throw new Error("Key weight has to be > 0 and <= 1");p=p.name}else a[p]={weight:1};this._analyze({key:p,value:this.options.getFn(h,p),record:h,index:c},{resultMap:n,results:s,tokenSearchers:e,fullSearcher:t})}return{weights:a,results:s}}},{key:"_analyze",value:function(e,t){var i=e.key,n=e.arrayIndex,s=void 0===n?-1:n,r=e.value,o=e.record,c=e.index,l=t.tokenSearchers,h=void 0===l?[]:l,u=t.fullSearcher,d=void 0===u?[]:u,p=t.resultMap,m=void 0===p?{}:p,f=t.results,v=void 0===f?[]:f;if(null!=r){var g=!1,_=-1,b=0;if("string"==typeof r){this._log("\nKey: ".concat(""===i?"-":i));var y=d.search(r);if(this._log('Full text: "'.concat(r,'", score: ').concat(y.score)),this.options.tokenize){for(var E=r.split(this.options.tokenSeparator),I=[],S=0;S-1&&(P=(P+_)/2),this._log("Score average:",P);var D=!this.options.tokenize||!this.options.matchAllTokens||b>=h.length;if(this._log("\nCheck Matches: ".concat(D)),(g||y.isMatch)&&D){var M=m[c];M?M.output.push({key:i,arrayIndex:s,value:r,score:P,matchedIndices:y.matchedIndices}):(m[c]={item:o,output:[{key:i,arrayIndex:s,value:r,score:P,matchedIndices:y.matchedIndices}]},v.push(m[c]))}}else if(a(r))for(var N=0,F=r.length;N-1&&(o.arrayIndex=r.arrayIndex),t.matches.push(o)}}})),this.options.includeScore&&s.push((function(e,t){t.score=e.score}));for(var r=0,o=e.length;ri)return s(e,this.pattern,n);var o=this.options,a=o.location,c=o.distance,l=o.threshold,h=o.findAllMatches,u=o.minMatchCharLength;return r(e,this.pattern,this.patternAlphabet,{location:a,distance:c,threshold:l,findAllMatches:h,minMatchCharLength:u})}}])&&n(t.prototype,i),e}();e.exports=a},function(e,t){var i=/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;e.exports=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:/ +/g,s=new RegExp(t.replace(i,"\\$&").replace(n,"|")),r=e.match(s),o=!!r,a=[];if(o)for(var c=0,l=r.length;c=P;N-=1){var F=N-1,j=i[e.charAt(F)];if(j&&(E[F]=1),M[N]=(M[N+1]<<1|1)&j,0!==T&&(M[N]|=(O[N+1]|O[N])<<1|1|O[N+1]),M[N]&L&&(C=n(t,{errors:T,currentLocation:F,expectedLocation:v,distance:l}))<=_){if(_=C,(b=F)<=v)break;P=Math.max(1,2*v-b)}}if(n(t,{errors:T+1,currentLocation:v,expectedLocation:v,distance:l})>_)break;O=M}return{isMatch:b>=0,score:0===C?.001:C,matchedIndices:s(E,f)}}},function(e,t){e.exports=function(e,t){var i=t.errors,n=void 0===i?0:i,s=t.currentLocation,r=void 0===s?0:s,o=t.expectedLocation,a=void 0===o?0:o,c=t.distance,l=void 0===c?100:c,h=n/e.length,u=Math.abs(a-r);return l?h+u/l:u?1:h}},function(e,t){e.exports=function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1,i=[],n=-1,s=-1,r=0,o=e.length;r=t&&i.push([n,s]),n=-1)}return e[r-1]&&r-n>=t&&i.push([n,r-1]),i}},function(e,t){e.exports=function(e){for(var t={},i=e.length,n=0;n/g,"&rt;").replace(/-1?e.map((function(e){var i=e;return i.id===parseInt(t.choiceId,10)&&(i.selected=!0),i})):e;case"REMOVE_ITEM":return t.choiceId>-1?e.map((function(e){var i=e;return i.id===parseInt(t.choiceId,10)&&(i.selected=!1),i})):e;case"FILTER_CHOICES":return e.map((function(e){var i=e;return i.active=t.results.some((function(e){var t=e.item,n=e.score;return t.id===i.id&&(i.score=n,!0)})),i}));case"ACTIVATE_CHOICES":return e.map((function(e){var i=e;return i.active=t.active,i}));case"CLEAR_CHOICES":return f;default:return e}},general:_}),T=function(e,t){var i=e;if("CLEAR_ALL"===t.type)i=void 0;else if("RESET_TO"===t.type)return C(t.state);return L(i,t)};function x(e,t){for(var i=0;i"'+S(e)+'"'},maxItemText:function(e){return"Only "+e+" values can be added"},valueComparer:function(e,t){return e===t},fuseOptions:{includeScore:!0},callbackOnInit:null,callbackOnCreateTemplates:null,classNames:{containerOuter:"choices",containerInner:"choices__inner",input:"choices__input",inputCloned:"choices__input--cloned",list:"choices__list",listItems:"choices__list--multiple",listSingle:"choices__list--single",listDropdown:"choices__list--dropdown",item:"choices__item",itemSelectable:"choices__item--selectable",itemDisabled:"choices__item--disabled",itemChoice:"choices__item--choice",placeholder:"choices__placeholder",group:"choices__group",groupHeading:"choices__heading",button:"choices__button",activeState:"is-active",focusState:"is-focused",openState:"is-open",disabledState:"is-disabled",highlightedState:"is-highlighted",selectedState:"is-selected",flippedState:"is-flipped",loadingState:"is-loading",noResults:"has-no-results",noChoices:"has-no-choices"}},N="showDropdown",F="hideDropdown",j="change",R="choice",H="search",K="addItem",B="removeItem",V="highlightItem",G="highlightChoice",q="ADD_CHOICE",U="FILTER_CHOICES",z="ACTIVATE_CHOICES",W="CLEAR_CHOICES",X="ADD_GROUP",$="ADD_ITEM",J="REMOVE_ITEM",Y="HIGHLIGHT_ITEM",Z=46,Q=8,ee=13,te=65,ie=27,ne=38,se=40,re=33,oe=34,ae="text",ce="select-one",le="select-multiple",he=function(){function e(e){var t=e.element,i=e.type,n=e.classNames,s=e.position;this.element=t,this.classNames=n,this.type=i,this.position=s,this.isOpen=!1,this.isFlipped=!1,this.isFocussed=!1,this.isDisabled=!1,this.isLoading=!1,this._onFocus=this._onFocus.bind(this),this._onBlur=this._onBlur.bind(this)}var t=e.prototype;return t.addEventListeners=function(){this.element.addEventListener("focus",this._onFocus),this.element.addEventListener("blur",this._onBlur)},t.removeEventListeners=function(){this.element.removeEventListener("focus",this._onFocus),this.element.removeEventListener("blur",this._onBlur)},t.shouldFlip=function(e){if("number"!=typeof e)return!1;var t=!1;return"auto"===this.position?t=!window.matchMedia("(min-height: "+(e+1)+"px)").matches:"top"===this.position&&(t=!0),t},t.setActiveDescendant=function(e){this.element.setAttribute("aria-activedescendant",e)},t.removeActiveDescendant=function(){this.element.removeAttribute("aria-activedescendant")},t.open=function(e){this.element.classList.add(this.classNames.openState),this.element.setAttribute("aria-expanded","true"),this.isOpen=!0,this.shouldFlip(e)&&(this.element.classList.add(this.classNames.flippedState),this.isFlipped=!0)},t.close=function(){this.element.classList.remove(this.classNames.openState),this.element.setAttribute("aria-expanded","false"),this.removeActiveDescendant(),this.isOpen=!1,this.isFlipped&&(this.element.classList.remove(this.classNames.flippedState),this.isFlipped=!1)},t.focus=function(){this.isFocussed||this.element.focus()},t.addFocusState=function(){this.element.classList.add(this.classNames.focusState)},t.removeFocusState=function(){this.element.classList.remove(this.classNames.focusState)},t.enable=function(){this.element.classList.remove(this.classNames.disabledState),this.element.removeAttribute("aria-disabled"),this.type===ce&&this.element.setAttribute("tabindex","0"),this.isDisabled=!1},t.disable=function(){this.element.classList.add(this.classNames.disabledState),this.element.setAttribute("aria-disabled","true"),this.type===ce&&this.element.setAttribute("tabindex","-1"),this.isDisabled=!0},t.wrap=function(e){!function(e,t){void 0===t&&(t=document.createElement("div")),e.nextSibling?e.parentNode.insertBefore(t,e.nextSibling):e.parentNode.appendChild(t),t.appendChild(e)}(e,this.element)},t.unwrap=function(e){this.element.parentNode.insertBefore(e,this.element),this.element.parentNode.removeChild(this.element)},t.addLoadingState=function(){this.element.classList.add(this.classNames.loadingState),this.element.setAttribute("aria-busy","true"),this.isLoading=!0},t.removeLoadingState=function(){this.element.classList.remove(this.classNames.loadingState),this.element.removeAttribute("aria-busy"),this.isLoading=!1},t._onFocus=function(){this.isFocussed=!0},t._onBlur=function(){this.isFocussed=!1},e}();function ue(e,t){for(var i=0;i0?this.element.scrollTop+o-s:e.offsetTop;requestAnimationFrame((function(){i._animateScroll(a,t)}))}},t._scrollDown=function(e,t,i){var n=(i-e)/t,s=n>1?n:1;this.element.scrollTop=e+s},t._scrollUp=function(e,t,i){var n=(e-i)/t,s=n>1?n:1;this.element.scrollTop=e-s},t._animateScroll=function(e,t){var i=this,n=this.element.scrollTop,s=!1;t>0?(this._scrollDown(n,4,e),ne&&(s=!0)),s&&requestAnimationFrame((function(){i._animateScroll(e,t)}))},e}();function me(e,t){for(var i=0;i0?"treeitem":"option"),Object.assign(g.dataset,{choice:"",id:l,value:h,selectText:i}),m?(g.classList.add(a),g.dataset.choiceDisabled="",g.setAttribute("aria-disabled","true")):(g.classList.add(r),g.dataset.choiceSelectable=""),g},input:function(e,t){var i=e.input,n=e.inputCloned,s=Object.assign(document.createElement("input"),{type:"text",className:i+" "+n,autocomplete:"off",autocapitalize:"off",spellcheck:!1});return s.setAttribute("role","textbox"),s.setAttribute("aria-autocomplete","list"),s.setAttribute("aria-label",t),s},dropdown:function(e){var t=e.list,i=e.listDropdown,n=document.createElement("div");return n.classList.add(t,i),n.setAttribute("aria-expanded","false"),n},notice:function(e,t,i){var n=e.item,s=e.itemChoice,r=e.noResults,o=e.noChoices;void 0===i&&(i="");var a=[n,s];return"no-choices"===i?a.push(o):"no-results"===i&&a.push(r),Object.assign(document.createElement("div"),{innerHTML:t,className:a.join(" ")})},option:function(e){var t=e.label,i=e.value,n=e.customProperties,s=e.active,r=e.disabled,o=new Option(t,i,!1,s);return n&&(o.dataset.customProperties=n),o.disabled=r,o}},Ee=function(e){return void 0===e&&(e=!0),{type:z,active:e}},Ie=function(e,t){return{type:Y,id:e,highlighted:t}},Se=function(e){var t=e.value,i=e.id,n=e.active,s=e.disabled;return{type:X,value:t,id:i,active:n,disabled:s}},we=function(e){return{type:"SET_IS_LOADING",isLoading:e}};function Oe(e,t){for(var i=0;i=0?this._store.getGroupById(s):null;return this._store.dispatch(Ie(i,!0)),t&&this.passedElement.triggerEvent(V,{id:i,value:o,label:c,groupValue:l&&l.value?l.value:null}),this},r.unhighlightItem=function(e){if(!e)return this;var t=e.id,i=e.groupId,n=void 0===i?-1:i,s=e.value,r=void 0===s?"":s,o=e.label,a=void 0===o?"":o,c=n>=0?this._store.getGroupById(n):null;return this._store.dispatch(Ie(t,!1)),this.passedElement.triggerEvent(V,{id:t,value:r,label:a,groupValue:c&&c.value?c.value:null}),this},r.highlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.highlightItem(t)})),this},r.unhighlightAll=function(){var e=this;return this._store.items.forEach((function(t){return e.unhighlightItem(t)})),this},r.removeActiveItemsByValue=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.value===e})).forEach((function(e){return t._removeItem(e)})),this},r.removeActiveItems=function(e){var t=this;return this._store.activeItems.filter((function(t){return t.id!==e})).forEach((function(e){return t._removeItem(e)})),this},r.removeHighlightedItems=function(e){var t=this;return void 0===e&&(e=!1),this._store.highlightedActiveItems.forEach((function(i){t._removeItem(i),e&&t._triggerChange(i.value)})),this},r.showDropdown=function(e){var t=this;return this.dropdown.isActive?this:(requestAnimationFrame((function(){t.dropdown.show(),t.containerOuter.open(t.dropdown.distanceFromTopWindow),!e&&t._canSearch&&t.input.focus(),t.passedElement.triggerEvent(N,{})})),this)},r.hideDropdown=function(e){var t=this;return this.dropdown.isActive?(requestAnimationFrame((function(){t.dropdown.hide(),t.containerOuter.close(),!e&&t._canSearch&&(t.input.removeActiveDescendant(),t.input.blur()),t.passedElement.triggerEvent(F,{})})),this):this},r.getValue=function(e){void 0===e&&(e=!1);var t=this._store.activeItems.reduce((function(t,i){var n=e?i.value:i;return t.push(n),t}),[]);return this._isSelectOneElement?t[0]:t},r.setValue=function(e){var t=this;return this.initialised?(e.forEach((function(e){return t._setChoiceOrItem(e)})),this):this},r.setChoiceByValue=function(e){var t=this;return!this.initialised||this._isTextElement?this:((Array.isArray(e)?e:[e]).forEach((function(e){return t._findAndSelectChoiceByValue(e)})),this)},r.setChoices=function(e,t,i,n){var s=this;if(void 0===e&&(e=[]),void 0===t&&(t="value"),void 0===i&&(i="label"),void 0===n&&(n=!1),!this.initialised)throw new ReferenceError("setChoices was called on a non-initialized instance of Choices");if(!this._isSelectElement)throw new TypeError("setChoices can't be used with INPUT based Choices");if("string"!=typeof t||!t)throw new TypeError("value parameter must be a name of 'value' field in passed objects");if(n&&this.clearChoices(),"function"==typeof e){var r=e(this);if("function"==typeof Promise&&r instanceof Promise)return new Promise((function(e){return requestAnimationFrame(e)})).then((function(){return s._handleLoadingState(!0)})).then((function(){return r})).then((function(e){return s.setChoices(e,t,i,n)})).catch((function(e){s.config.silent||console.error(e)})).then((function(){return s._handleLoadingState(!1)})).then((function(){return s}));if(!Array.isArray(r))throw new TypeError(".setChoices first argument function must return either array of choices or Promise, got: "+typeof r);return this.setChoices(r,t,i,!1)}if(!Array.isArray(e))throw new TypeError(".setChoices must be called either with array of choices with a function resulting into Promise of array of choices");return this.containerOuter.removeLoadingState(),this._startLoading(),e.forEach((function(e){e.choices?s._addGroup({id:parseInt(e.id,10)||null,group:e,valueKey:t,labelKey:i}):s._addChoice({value:e[t],label:e[i],isSelected:e.selected,isDisabled:e.disabled,customProperties:e.customProperties,placeholder:e.placeholder})})),this._stopLoading(),this},r.clearChoices=function(){return this._store.dispatch({type:W}),this},r.clearStore=function(){return this._store.dispatch({type:"CLEAR_ALL"}),this},r.clearInput=function(){var e=!this._isSelectOneElement;return this.input.clear(e),!this._isTextElement&&this._canSearch&&(this._isSearching=!1,this._store.dispatch(Ee(!0))),this},r._render=function(){if(!this._store.isLoading()){this._currentState=this._store.state;var e=this._currentState.choices!==this._prevState.choices||this._currentState.groups!==this._prevState.groups||this._currentState.items!==this._prevState.items,t=this._isSelectElement,i=this._currentState.items!==this._prevState.items;e&&(t&&this._renderChoices(),i&&this._renderItems(),this._prevState=this._currentState)}},r._renderChoices=function(){var e=this,t=this._store,i=t.activeGroups,n=t.activeChoices,s=document.createDocumentFragment();if(this.choiceList.clear(),this.config.resetScrollPosition&&requestAnimationFrame((function(){return e.choiceList.scrollToTop()})),i.length>=1&&!this._isSearching){var r=n.filter((function(e){return!0===e.placeholder&&-1===e.groupId}));r.length>=1&&(s=this._createChoicesFragment(r,s)),s=this._createGroupsFragment(i,n,s)}else n.length>=1&&(s=this._createChoicesFragment(n,s));if(s.childNodes&&s.childNodes.length>0){var o=this._store.activeItems,a=this._canAddItem(o,this.input.value);a.response?(this.choiceList.append(s),this._highlightChoice()):this.choiceList.append(this._getTemplate("notice",a.notice))}else{var c,l;this._isSearching?(l="function"==typeof this.config.noResultsText?this.config.noResultsText():this.config.noResultsText,c=this._getTemplate("notice",l,"no-results")):(l="function"==typeof this.config.noChoicesText?this.config.noChoicesText():this.config.noChoicesText,c=this._getTemplate("notice",l,"no-choices")),this.choiceList.append(c)}},r._renderItems=function(){var e=this._store.activeItems||[];this.itemList.clear();var t=this._createItemsFragment(e);t.childNodes&&this.itemList.append(t)},r._createGroupsFragment=function(e,t,i){var n=this;void 0===i&&(i=document.createDocumentFragment());return this.config.shouldSort&&e.sort(this.config.sorter),e.forEach((function(e){var s=function(e){return t.filter((function(t){return n._isSelectOneElement?t.groupId===e.id:t.groupId===e.id&&("always"===n.config.renderSelectedChoices||!t.selected)}))}(e);if(s.length>=1){var r=n._getTemplate("choiceGroup",e);i.appendChild(r),n._createChoicesFragment(s,i,!0)}})),i},r._createChoicesFragment=function(e,t,i){var n=this;void 0===t&&(t=document.createDocumentFragment()),void 0===i&&(i=!1);var s=this.config,r=s.renderSelectedChoices,o=s.searchResultLimit,a=s.renderChoiceLimit,c=this._isSearching?O:this.config.sorter,l=function(e){if("auto"!==r||(n._isSelectOneElement||!e.selected)){var i=n._getTemplate("choice",e,n.config.itemSelectText);t.appendChild(i)}},h=e;"auto"!==r||this._isSelectOneElement||(h=e.filter((function(e){return!e.selected})));var u=h.reduce((function(e,t){return t.placeholder?e.placeholderChoices.push(t):e.normalChoices.push(t),e}),{placeholderChoices:[],normalChoices:[]}),d=u.placeholderChoices,p=u.normalChoices;(this.config.shouldSort||this._isSearching)&&p.sort(c);var m=h.length,f=this._isSelectOneElement?[].concat(d,p):p;this._isSearching?m=o:a&&a>0&&!i&&(m=a);for(var v=0;v=n){var o=s?this._searchChoices(e):0;this.passedElement.triggerEvent(H,{value:e,resultCount:o})}else r&&(this._isSearching=!1,this._store.dispatch(Ee(!0)))}},r._canAddItem=function(e,t){var i=!0,n="function"==typeof this.config.addItemText?this.config.addItemText(t):this.config.addItemText;if(!this._isSelectOneElement){var s=function(e,t,i){return void 0===i&&(i="value"),e.some((function(e){return"string"==typeof t?e[i]===t.trim():e[i]===t}))}(e,t);this.config.maxItemCount>0&&this.config.maxItemCount<=e.length&&(i=!1,n="function"==typeof this.config.maxItemText?this.config.maxItemText(this.config.maxItemCount):this.config.maxItemText),!this.config.duplicateItemsAllowed&&s&&i&&(i=!1,n="function"==typeof this.config.uniqueItemText?this.config.uniqueItemText(t):this.config.uniqueItemText),this._isTextElement&&this.config.addItems&&i&&"function"==typeof this.config.addItemFilter&&!this.config.addItemFilter(t)&&(i=!1,n="function"==typeof this.config.customAddItemText?this.config.customAddItemText(t):this.config.customAddItemText)}return{response:i,notice:n}},r._searchChoices=function(e){var t="string"==typeof e?e.trim():e,i="string"==typeof this._currentValue?this._currentValue.trim():this._currentValue;if(t.length<1&&t===i+" ")return 0;var n=this._store.searchableChoices,r=t,o=[].concat(this.config.searchFields),a=Object.assign(this.config.fuseOptions,{keys:o}),c=new s.a(n,a).search(r);return this._currentValue=t,this._highlightPosition=0,this._isSearching=!0,this._store.dispatch(function(e){return{type:U,results:e}}(c)),c.length},r._addEventListeners=function(){var e=document.documentElement;e.addEventListener("touchend",this._onTouchEnd,!0),this.containerOuter.element.addEventListener("keydown",this._onKeyDown,!0),this.containerOuter.element.addEventListener("mousedown",this._onMouseDown,!0),e.addEventListener("click",this._onClick,{passive:!0}),e.addEventListener("touchmove",this._onTouchMove,{passive:!0}),this.dropdown.element.addEventListener("mouseover",this._onMouseOver,{passive:!0}),this._isSelectOneElement&&(this.containerOuter.element.addEventListener("focus",this._onFocus,{passive:!0}),this.containerOuter.element.addEventListener("blur",this._onBlur,{passive:!0})),this.input.element.addEventListener("keyup",this._onKeyUp,{passive:!0}),this.input.element.addEventListener("focus",this._onFocus,{passive:!0}),this.input.element.addEventListener("blur",this._onBlur,{passive:!0}),this.input.element.form&&this.input.element.form.addEventListener("reset",this._onFormReset,{passive:!0}),this.input.addEventListeners()},r._removeEventListeners=function(){var e=document.documentElement;e.removeEventListener("touchend",this._onTouchEnd,!0),this.containerOuter.element.removeEventListener("keydown",this._onKeyDown,!0),this.containerOuter.element.removeEventListener("mousedown",this._onMouseDown,!0),e.removeEventListener("click",this._onClick),e.removeEventListener("touchmove",this._onTouchMove),this.dropdown.element.removeEventListener("mouseover",this._onMouseOver),this._isSelectOneElement&&(this.containerOuter.element.removeEventListener("focus",this._onFocus),this.containerOuter.element.removeEventListener("blur",this._onBlur)),this.input.element.removeEventListener("keyup",this._onKeyUp),this.input.element.removeEventListener("focus",this._onFocus),this.input.element.removeEventListener("blur",this._onBlur),this.input.element.form&&this.input.element.form.removeEventListener("reset",this._onFormReset),this.input.removeEventListeners()},r._onKeyDown=function(e){var t,i=e.keyCode,n=this._store.activeItems,s=this.input.isFocussed,r=this.dropdown.isActive,o=this.itemList.hasChildren(),a=String.fromCharCode(i),c=/[a-zA-Z0-9-_ ]/.test(a),l=Z,h=Q,u=ee,d=te,p=ie,m=ne,f=se,v=re,g=oe;this._isTextElement||r||!c||(this.showDropdown(),this.input.isFocussed||(this.input.value+=a.toLowerCase()));var _=((t={})[d]=this._onAKey,t[u]=this._onEnterKey,t[p]=this._onEscapeKey,t[m]=this._onDirectionKey,t[v]=this._onDirectionKey,t[f]=this._onDirectionKey,t[g]=this._onDirectionKey,t[h]=this._onDeleteKey,t[l]=this._onDeleteKey,t);_[i]&&_[i]({event:e,activeItems:n,hasFocusedInput:s,hasActiveDropdown:r,hasItems:o})},r._onKeyUp=function(e){var t=e.target,i=e.keyCode,n=this.input.value,s=this._store.activeItems,r=this._canAddItem(s,n),o=Z,a=Q;if(this._isTextElement){if(r.notice&&n){var c=this._getTemplate("notice",r.notice);this.dropdown.element.innerHTML=c.outerHTML,this.showDropdown(!0)}else this.hideDropdown(!0)}else{var l=(i===o||i===a)&&!t.value,h=!this._isTextElement&&this._isSearching,u=this._canSearch&&r.response;l&&h?(this._isSearching=!1,this._store.dispatch(Ee(!0))):u&&this._handleSearch(this.input.value)}this._canSearch=this.config.searchEnabled},r._onAKey=function(e){var t=e.event,i=e.hasItems,n=t.ctrlKey,s=t.metaKey;(n||s)&&i&&(this._canSearch=!1,this.config.removeItems&&!this.input.value&&this.input.element===document.activeElement&&this.highlightAll())},r._onEnterKey=function(e){var t=e.event,i=e.activeItems,n=e.hasActiveDropdown,s=t.target,r=ee,o=s.hasAttribute("data-button");if(this._isTextElement&&s.value){var a=this.input.value;this._canAddItem(i,a).response&&(this.hideDropdown(!0),this._addItem({value:a}),this._triggerChange(a),this.clearInput())}if(o&&(this._handleButtonAction(i,s),t.preventDefault()),n){var c=this.dropdown.getChild("."+this.config.classNames.highlightedState);c&&(i[0]&&(i[0].keyCode=r),this._handleChoiceAction(i,c)),t.preventDefault()}else this._isSelectOneElement&&(this.showDropdown(),t.preventDefault())},r._onEscapeKey=function(e){e.hasActiveDropdown&&(this.hideDropdown(!0),this.containerOuter.focus())},r._onDirectionKey=function(e){var t,i,n,s=e.event,r=e.hasActiveDropdown,o=s.keyCode,a=s.metaKey,c=se,l=re,h=oe;if(r||this._isSelectOneElement){this.showDropdown(),this._canSearch=!1;var u,d=o===c||o===h?1:-1;if(a||o===h||o===l)u=d>0?this.dropdown.element.querySelector("[data-choice-selectable]:last-of-type"):this.dropdown.element.querySelector("[data-choice-selectable]");else{var p=this.dropdown.element.querySelector("."+this.config.classNames.highlightedState);u=p?function(e,t,i){if(void 0===i&&(i=1),e instanceof Element&&"string"==typeof t){for(var n=(i>0?"next":"previous")+"ElementSibling",s=e[n];s;){if(s.matches(t))return s;s=s[n]}return s}}(p,"[data-choice-selectable]",d):this.dropdown.element.querySelector("[data-choice-selectable]")}u&&(t=u,i=this.choiceList.element,void 0===(n=d)&&(n=1),t&&(n>0?i.scrollTop+i.offsetHeight>=t.offsetTop+t.offsetHeight:t.offsetTop>=i.scrollTop)||this.choiceList.scrollToChildElement(u,d),this._highlightChoice(u)),s.preventDefault()}},r._onDeleteKey=function(e){var t=e.event,i=e.hasFocusedInput,n=e.activeItems,s=t.target;!i||s.value||this._isSelectOneElement||(this._handleBackspace(n),t.preventDefault())},r._onTouchMove=function(){this._wasTap&&(this._wasTap=!1)},r._onTouchEnd=function(e){var t=(e||e.touches[0]).target;this._wasTap&&this.containerOuter.element.contains(t)&&((t===this.containerOuter.element||t===this.containerInner.element)&&(this._isTextElement?this.input.focus():this._isSelectMultipleElement&&this.showDropdown()),e.stopPropagation());this._wasTap=!0},r._onMouseDown=function(e){var t=e.target;if(t instanceof HTMLElement){if(Ce&&this.choiceList.element.contains(t)){var i=this.choiceList.element.firstElementChild,n="ltr"===this._direction?e.offsetX>=i.offsetWidth:e.offsetX0&&this.unhighlightAll(),this.containerOuter.removeFocusState(),this.hideDropdown(!0))},r._onFocus=function(e){var t,i=this,n=e.target;this.containerOuter.element.contains(n)&&((t={})[ae]=function(){n===i.input.element&&i.containerOuter.addFocusState()},t[ce]=function(){i.containerOuter.addFocusState(),n===i.input.element&&i.showDropdown(!0)},t[le]=function(){n===i.input.element&&(i.showDropdown(!0),i.containerOuter.addFocusState())},t)[this.passedElement.element.type]()},r._onBlur=function(e){var t=this,i=e.target;if(this.containerOuter.element.contains(i)&&!this._isScrollingOnIe){var n,s=this._store.activeItems.some((function(e){return e.highlighted}));((n={})[ae]=function(){i===t.input.element&&(t.containerOuter.removeFocusState(),s&&t.unhighlightAll(),t.hideDropdown(!0))},n[ce]=function(){t.containerOuter.removeFocusState(),(i===t.input.element||i===t.containerOuter.element&&!t._canSearch)&&t.hideDropdown(!0)},n[le]=function(){i===t.input.element&&(t.containerOuter.removeFocusState(),t.hideDropdown(!0),s&&t.unhighlightAll())},n)[this.passedElement.element.type]()}else this._isScrollingOnIe=!1,this.input.element.focus()},r._onFormReset=function(){this._store.dispatch({type:"RESET_TO",state:this._initialState})},r._highlightChoice=function(e){var t=this;void 0===e&&(e=null);var i=Array.from(this.dropdown.element.querySelectorAll("[data-choice-selectable]"));if(i.length){var n=e;Array.from(this.dropdown.element.querySelectorAll("."+this.config.classNames.highlightedState)).forEach((function(e){e.classList.remove(t.config.classNames.highlightedState),e.setAttribute("aria-selected","false")})),n?this._highlightPosition=i.indexOf(n):(n=i.length>this._highlightPosition?i[this._highlightPosition]:i[i.length-1])||(n=i[0]),n.classList.add(this.config.classNames.highlightedState),n.setAttribute("aria-selected","true"),this.passedElement.triggerEvent(G,{el:n}),this.dropdown.isActive&&(this.input.setActiveDescendant(n.id),this.containerOuter.setActiveDescendant(n.id))}},r._addItem=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,s=e.choiceId,r=void 0===s?-1:s,o=e.groupId,a=void 0===o?-1:o,c=e.customProperties,l=void 0===c?null:c,h=e.placeholder,u=void 0!==h&&h,d=e.keyCode,p=void 0===d?null:d,m="string"==typeof t?t.trim():t,f=p,v=l,g=this._store.items,_=n||m,b=r||-1,y=a>=0?this._store.getGroupById(a):null,E=g?g.length+1:1;return this.config.prependValue&&(m=this.config.prependValue+m.toString()),this.config.appendValue&&(m+=this.config.appendValue.toString()),this._store.dispatch(function(e){var t=e.value,i=e.label,n=e.id,s=e.choiceId,r=e.groupId,o=e.customProperties,a=e.placeholder,c=e.keyCode;return{type:$,value:t,label:i,id:n,choiceId:s,groupId:r,customProperties:o,placeholder:a,keyCode:c}}({value:m,label:_,id:E,choiceId:b,groupId:a,customProperties:l,placeholder:u,keyCode:f})),this._isSelectOneElement&&this.removeActiveItems(E),this.passedElement.triggerEvent(K,{id:E,value:m,label:_,customProperties:v,groupValue:y&&y.value?y.value:void 0,keyCode:f}),this},r._removeItem=function(e){if(!e||!I("Object",e))return this;var t=e.id,i=e.value,n=e.label,s=e.choiceId,r=e.groupId,o=r>=0?this._store.getGroupById(r):null;return this._store.dispatch(function(e,t){return{type:J,id:e,choiceId:t}}(t,s)),o&&o.value?this.passedElement.triggerEvent(B,{id:t,value:i,label:n,groupValue:o.value}):this.passedElement.triggerEvent(B,{id:t,value:i,label:n}),this},r._addChoice=function(e){var t=e.value,i=e.label,n=void 0===i?null:i,s=e.isSelected,r=void 0!==s&&s,o=e.isDisabled,a=void 0!==o&&o,c=e.groupId,l=void 0===c?-1:c,h=e.customProperties,u=void 0===h?null:h,d=e.placeholder,p=void 0!==d&&d,m=e.keyCode,f=void 0===m?null:m;if(null!=t){var v=this._store.choices,g=n||t,_=v?v.length+1:1,b=this._baseId+"-"+this._idNames.itemChoice+"-"+_;this._store.dispatch(function(e){var t=e.value,i=e.label,n=e.id,s=e.groupId,r=e.disabled,o=e.elementId,a=e.customProperties,c=e.placeholder,l=e.keyCode;return{type:q,value:t,label:i,id:n,groupId:s,disabled:r,elementId:o,customProperties:a,placeholder:c,keyCode:l}}({id:_,groupId:l,elementId:b,value:t,label:g,disabled:a,customProperties:u,placeholder:p,keyCode:f})),r&&this._addItem({value:t,label:g,choiceId:_,customProperties:u,placeholder:p,keyCode:f})}},r._addGroup=function(e){var t=this,i=e.group,n=e.id,s=e.valueKey,r=void 0===s?"value":s,o=e.labelKey,a=void 0===o?"label":o,c=I("Object",i)?i.choices:Array.from(i.getElementsByTagName("OPTION")),l=n||Math.floor((new Date).valueOf()*Math.random()),h=!!i.disabled&&i.disabled;if(c){this._store.dispatch(Se({value:i.label,id:l,active:!0,disabled:h}));c.forEach((function(e){var i=e.disabled||e.parentNode&&e.parentNode.disabled;t._addChoice({value:e[r],label:I("Object",e)?e[a]:e.innerHTML,isSelected:e.selected,isDisabled:i,groupId:l,customProperties:e.customProperties,placeholder:e.placeholder})}))}else this._store.dispatch(Se({value:i.label,id:i.id,active:!1,disabled:i.disabled}))},r._getTemplate=function(e){var t;if(!e)return null;for(var i=this.config.classNames,n=arguments.length,s=new Array(n>1?n-1:0),r=1;r1&&void 0!==arguments[1]?arguments[1]:{limit:!1};this._log('---------\nSearch pattern: "'.concat(e,'"'));var i=this._prepareSearchers(e),n=i.tokenSearchers,r=i.fullSearcher,o=this._search(n,r),s=o.weights,a=o.results;return this._computeScore(s,a),this.options.shouldSort&&this._sort(a),t.limit&&"number"==typeof t.limit&&(a=a.slice(0,t.limit)),this._format(a)}},{key:"_prepareSearchers",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=[];if(this.options.tokenize)for(var i=e.split(this.options.tokenSeparator),n=0,r=i.length;n0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1?arguments[1]:void 0,i=this.list,n={},r=[];if("string"==typeof i[0]){for(var o=0,s=i.length;o1)throw new Error("Key weight has to be > 0 and <= 1");p=p.name}else a[p]={weight:1};this._analyze({key:p,value:this.options.getFn(u,p),record:u,index:c},{resultMap:n,results:r,tokenSearchers:e,fullSearcher:t})}return{weights:a,results:r}}},{key:"_analyze",value:function(e,t){var i=e.key,n=e.arrayIndex,r=void 0===n?-1:n,o=e.value,s=e.record,c=e.index,l=t.tokenSearchers,u=void 0===l?[]:l,h=t.fullSearcher,d=void 0===h?[]:h,p=t.resultMap,f=void 0===p?{}:p,m=t.results,v=void 0===m?[]:m;if(null!=o){var _=!1,g=-1,y=0;if("string"==typeof o){this._log("\nKey: ".concat(""===i?"-":i));var b=d.search(o);if(this._log('Full text: "'.concat(o,'", score: ').concat(b.score)),this.options.tokenize){for(var E=o.split(this.options.tokenSeparator),S=[],I=0;I-1&&(x=(x+g)/2),this._log("Score average:",x);var N=!this.options.tokenize||!this.options.matchAllTokens||y>=u.length;if(this._log("\nCheck Matches: ".concat(N)),(_||b.isMatch)&&N){var M=f[c];M?M.output.push({key:i,arrayIndex:r,value:o,score:x,matchedIndices:b.matchedIndices}):(f[c]={item:s,output:[{key:i,arrayIndex:r,value:o,score:x,matchedIndices:b.matchedIndices}]},v.push(f[c]))}}else if(a(o))for(var j=0,k=o.length;j-1&&(s.arrayIndex=o.arrayIndex),t.matches.push(s)}}})),this.options.includeScore&&r.push((function(e,t){t.score=e.score}));for(var o=0,s=e.length;oi)return r(e,this.pattern,n);var s=this.options,a=s.location,c=s.distance,l=s.threshold,u=s.findAllMatches,h=s.minMatchCharLength;return o(e,this.pattern,this.patternAlphabet,{location:a,distance:c,threshold:l,findAllMatches:u,minMatchCharLength:h})}}])&&n(t.prototype,i),e}();e.exports=a},function(e,t){var i=/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g;e.exports=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:/ +/g,r=new RegExp(t.replace(i,"\\$&").replace(n,"|")),o=e.match(r),s=!!o,a=[];if(s)for(var c=0,l=o.length;c=x;j-=1){var k=j-1,F=i[e.charAt(k)];if(F&&(E[k]=1),M[j]=(M[j+1]<<1|1)&F,0!==L&&(M[j]|=(C[j+1]|C[j])<<1|1|C[j+1]),M[j]&A&&(T=n(t,{errors:L,currentLocation:k,expectedLocation:v,distance:l}))<=g){if(g=T,(y=k)<=v)break;x=Math.max(1,2*v-y)}}if(n(t,{errors:L+1,currentLocation:v,expectedLocation:v,distance:l})>g)break;C=M}return{isMatch:y>=0,score:0===T?.001:T,matchedIndices:r(E,m)}}},function(e,t){e.exports=function(e,t){var i=t.errors,n=void 0===i?0:i,r=t.currentLocation,o=void 0===r?0:r,s=t.expectedLocation,a=void 0===s?0:s,c=t.distance,l=void 0===c?100:c,u=n/e.length,h=Math.abs(a-o);return l?u+h/l:h?1:u}},function(e,t){e.exports=function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:1,i=[],n=-1,r=-1,o=0,s=e.length;o=t&&i.push([n,r]),n=-1)}return e[o-1]&&o-n>=t&&i.push([n,o-1]),i}},function(e,t){e.exports=function(e){for(var t={},i=e.length,n=0;n-1?e.map((function(e){var t=e;return t.id===parseInt(""+s.choiceId,10)&&(t.selected=!0),t})):e;case"REMOVE_ITEM":var a=i;return a.choiceId&&a.choiceId>-1?e.map((function(e){var t=e;return t.id===parseInt(""+a.choiceId,10)&&(t.selected=!1),t})):e;case"FILTER_CHOICES":var c=i;return e.map((function(e){var t=e;return t.active=c.results.some((function(e){var i=e.item,n=e.score;return i.id===t.id&&(t.score=n,!0)})),t}));case"ACTIVATE_CHOICES":var l=i;return e.map((function(e){var t=e;return t.active=l.active,t}));case"CLEAR_CHOICES":return t.defaultState;default:return e}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.defaultState=!1;t.default=function(e,i){switch(void 0===e&&(e=t.defaultState),i.type){case"SET_IS_LOADING":return i.isLoading;default:return e}}},function(e,t,i){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var r=n(i(19));t.Dropdown=r.default;var o=n(i(20));t.Container=o.default;var s=n(i(21));t.Input=s.default;var a=n(i(22));t.List=a.default;var c=n(i(23));t.WrappedInput=c.default;var l=n(i(24));t.WrappedSelect=l.default},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(e){var t=e.element,i=e.type,n=e.classNames;this.element=t,this.classNames=n,this.type=i,this.isActive=!1}return Object.defineProperty(e.prototype,"distanceFromTopWindow",{get:function(){return this.element.getBoundingClientRect().bottom},enumerable:!0,configurable:!0}),e.prototype.getChild=function(e){return this.element.querySelector(e)},e.prototype.show=function(){return this.element.classList.add(this.classNames.activeState),this.element.setAttribute("aria-expanded","true"),this.isActive=!0,this},e.prototype.hide=function(){return this.element.classList.remove(this.classNames.activeState),this.element.setAttribute("aria-expanded","false"),this.isActive=!1,this},e}();t.default=n},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(1),r=i(0),o=function(){function e(e){var t=e.element,i=e.type,n=e.classNames,r=e.position;this.element=t,this.classNames=n,this.type=i,this.position=r,this.isOpen=!1,this.isFlipped=!1,this.isFocussed=!1,this.isDisabled=!1,this.isLoading=!1,this._onFocus=this._onFocus.bind(this),this._onBlur=this._onBlur.bind(this)}return e.prototype.addEventListeners=function(){this.element.addEventListener("focus",this._onFocus),this.element.addEventListener("blur",this._onBlur)},e.prototype.removeEventListeners=function(){this.element.removeEventListener("focus",this._onFocus),this.element.removeEventListener("blur",this._onBlur)},e.prototype.shouldFlip=function(e){if("number"!=typeof e)return!1;var t=!1;return"auto"===this.position?t=!window.matchMedia("(min-height: "+(e+1)+"px)").matches:"top"===this.position&&(t=!0),t},e.prototype.setActiveDescendant=function(e){this.element.setAttribute("aria-activedescendant",e)},e.prototype.removeActiveDescendant=function(){this.element.removeAttribute("aria-activedescendant")},e.prototype.open=function(e){this.element.classList.add(this.classNames.openState),this.element.setAttribute("aria-expanded","true"),this.isOpen=!0,this.shouldFlip(e)&&(this.element.classList.add(this.classNames.flippedState),this.isFlipped=!0)},e.prototype.close=function(){this.element.classList.remove(this.classNames.openState),this.element.setAttribute("aria-expanded","false"),this.removeActiveDescendant(),this.isOpen=!1,this.isFlipped&&(this.element.classList.remove(this.classNames.flippedState),this.isFlipped=!1)},e.prototype.focus=function(){this.isFocussed||this.element.focus()},e.prototype.addFocusState=function(){this.element.classList.add(this.classNames.focusState)},e.prototype.removeFocusState=function(){this.element.classList.remove(this.classNames.focusState)},e.prototype.enable=function(){this.element.classList.remove(this.classNames.disabledState),this.element.removeAttribute("aria-disabled"),this.type===r.SELECT_ONE_TYPE&&this.element.setAttribute("tabindex","0"),this.isDisabled=!1},e.prototype.disable=function(){this.element.classList.add(this.classNames.disabledState),this.element.setAttribute("aria-disabled","true"),this.type===r.SELECT_ONE_TYPE&&this.element.setAttribute("tabindex","-1"),this.isDisabled=!0},e.prototype.wrap=function(e){n.wrap(e,this.element)},e.prototype.unwrap=function(e){this.element.parentNode&&(this.element.parentNode.insertBefore(e,this.element),this.element.parentNode.removeChild(this.element))},e.prototype.addLoadingState=function(){this.element.classList.add(this.classNames.loadingState),this.element.setAttribute("aria-busy","true"),this.isLoading=!0},e.prototype.removeLoadingState=function(){this.element.classList.remove(this.classNames.loadingState),this.element.removeAttribute("aria-busy"),this.isLoading=!1},e.prototype._onFocus=function(){this.isFocussed=!0},e.prototype._onBlur=function(){this.isFocussed=!1},e}();t.default=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(1),r=i(0),o=function(){function e(e){var t=e.element,i=e.type,n=e.classNames,r=e.preventPaste;this.element=t,this.type=i,this.classNames=n,this.preventPaste=r,this.isFocussed=this.element.isEqualNode(document.activeElement),this.isDisabled=t.disabled,this._onPaste=this._onPaste.bind(this),this._onInput=this._onInput.bind(this),this._onFocus=this._onFocus.bind(this),this._onBlur=this._onBlur.bind(this)}return Object.defineProperty(e.prototype,"placeholder",{set:function(e){this.element.placeholder=e},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"value",{get:function(){return n.sanitise(this.element.value)},set:function(e){this.element.value=e},enumerable:!0,configurable:!0}),e.prototype.addEventListeners=function(){this.element.addEventListener("paste",this._onPaste),this.element.addEventListener("input",this._onInput,{passive:!0}),this.element.addEventListener("focus",this._onFocus,{passive:!0}),this.element.addEventListener("blur",this._onBlur,{passive:!0})},e.prototype.removeEventListeners=function(){this.element.removeEventListener("input",this._onInput),this.element.removeEventListener("paste",this._onPaste),this.element.removeEventListener("focus",this._onFocus),this.element.removeEventListener("blur",this._onBlur)},e.prototype.enable=function(){this.element.removeAttribute("disabled"),this.isDisabled=!1},e.prototype.disable=function(){this.element.setAttribute("disabled",""),this.isDisabled=!0},e.prototype.focus=function(){this.isFocussed||this.element.focus()},e.prototype.blur=function(){this.isFocussed&&this.element.blur()},e.prototype.clear=function(e){return void 0===e&&(e=!0),this.element.value&&(this.element.value=""),e&&this.setWidth(),this},e.prototype.setWidth=function(){var e=this.element,t=e.style,i=e.value,n=e.placeholder;t.minWidth=n.length+1+"ch",t.width=i.length+1+"ch"},e.prototype.setActiveDescendant=function(e){this.element.setAttribute("aria-activedescendant",e)},e.prototype.removeActiveDescendant=function(){this.element.removeAttribute("aria-activedescendant")},e.prototype._onInput=function(){this.type!==r.SELECT_ONE_TYPE&&this.setWidth()},e.prototype._onPaste=function(e){this.preventPaste&&e.preventDefault()},e.prototype._onFocus=function(){this.isFocussed=!0},e.prototype._onBlur=function(){this.isFocussed=!1},e}();t.default=o},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(0),r=function(){function e(e){var t=e.element;this.element=t,this.scrollPos=this.element.scrollTop,this.height=this.element.offsetHeight}return e.prototype.clear=function(){this.element.innerHTML=""},e.prototype.append=function(e){this.element.appendChild(e)},e.prototype.getChild=function(e){return this.element.querySelector(e)},e.prototype.hasChildren=function(){return this.element.hasChildNodes()},e.prototype.scrollToTop=function(){this.element.scrollTop=0},e.prototype.scrollToChildElement=function(e,t){var i=this;if(e){var n=this.element.offsetHeight,r=this.element.scrollTop+n,o=e.offsetHeight,s=e.offsetTop+o,a=t>0?this.element.scrollTop+s-r:e.offsetTop;requestAnimationFrame((function(){i._animateScroll(a,t)}))}},e.prototype._scrollDown=function(e,t,i){var n=(i-e)/t,r=n>1?n:1;this.element.scrollTop=e+r},e.prototype._scrollUp=function(e,t,i){var n=(e-i)/t,r=n>1?n:1;this.element.scrollTop=e-r},e.prototype._animateScroll=function(e,t){var i=this,r=n.SCROLLING_SPEED,o=this.element.scrollTop,s=!1;t>0?(this._scrollDown(o,r,e),oe&&(s=!0)),s&&requestAnimationFrame((function(){i._animateScroll(e,t)}))},e}();t.default=r},function(e,t,i){"use strict";var n,r=this&&this.__extends||(n=function(e,t){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)t.hasOwnProperty(i)&&(e[i]=t[i])})(e,t)},function(e,t){function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var s=function(e){function t(t){var i=t.element,n=t.classNames,r=t.delimiter,o=e.call(this,{element:i,classNames:n})||this;return o.delimiter=r,o}return r(t,e),Object.defineProperty(t.prototype,"value",{get:function(){return this.element.value},set:function(e){this.element.setAttribute("value",e),this.element.value=e},enumerable:!0,configurable:!0}),t}(o(i(5)).default);t.default=s},function(e,t,i){"use strict";var n,r=this&&this.__extends||(n=function(e,t){return(n=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var i in t)t.hasOwnProperty(i)&&(e[i]=t[i])})(e,t)},function(e,t){function i(){this.constructor=e}n(e,t),e.prototype=null===t?Object.create(t):(i.prototype=t.prototype,new i)}),o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var s=function(e){function t(t){var i=t.element,n=t.classNames,r=t.template,o=e.call(this,{element:i,classNames:n})||this;return o.template=r,o}return r(t,e),Object.defineProperty(t.prototype,"placeholderOption",{get:function(){return this.element.querySelector('option[value=""]')||this.element.querySelector("option[placeholder]")},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"optionGroups",{get:function(){return Array.from(this.element.getElementsByTagName("OPTGROUP"))},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"options",{get:function(){return Array.from(this.element.options)},set:function(e){var t=this,i=document.createDocumentFragment();e.forEach((function(e){return n=e,r=t.template(n),void i.appendChild(r);var n,r})),this.appendDocFragment(i)},enumerable:!0,configurable:!0}),t.prototype.appendDocFragment=function(e){this.element.innerHTML="",this.element.appendChild(e)},t}(o(i(5)).default);t.default=s},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n={containerOuter:function(e,t,i,n,r,o){var s=e.containerOuter,a=Object.assign(document.createElement("div"),{className:s});return a.dataset.type=o,t&&(a.dir=t),n&&(a.tabIndex=0),i&&(a.setAttribute("role",r?"combobox":"listbox"),r&&a.setAttribute("aria-autocomplete","list")),a.setAttribute("aria-haspopup","true"),a.setAttribute("aria-expanded","false"),a},containerInner:function(e){var t=e.containerInner;return Object.assign(document.createElement("div"),{className:t})},itemList:function(e,t){var i=e.list,n=e.listSingle,r=e.listItems;return Object.assign(document.createElement("div"),{className:i+" "+(t?n:r)})},placeholder:function(e,t){var i=e.placeholder;return Object.assign(document.createElement("div"),{className:i,innerHTML:t})},item:function(e,t,i){var n=e.item,r=e.button,o=e.highlightedState,s=e.itemSelectable,a=e.placeholder,c=t.id,l=t.value,u=t.label,h=t.customProperties,d=t.active,p=t.disabled,f=t.highlighted,m=t.placeholder,v=Object.assign(document.createElement("div"),{className:n,innerHTML:u});if(Object.assign(v.dataset,{item:"",id:c,value:l,customProperties:h}),d&&v.setAttribute("aria-selected","true"),p&&v.setAttribute("aria-disabled","true"),m&&v.classList.add(a),v.classList.add(f?o:s),i){p&&v.classList.remove(s),v.dataset.deletable="";var _=Object.assign(document.createElement("button"),{type:"button",className:r,innerHTML:"Remove item"});_.setAttribute("aria-label","Remove item: '"+l+"'"),_.dataset.button="",v.appendChild(_)}return v},choiceList:function(e,t){var i=e.list,n=Object.assign(document.createElement("div"),{className:i});return t||n.setAttribute("aria-multiselectable","true"),n.setAttribute("role","listbox"),n},choiceGroup:function(e,t){var i=e.group,n=e.groupHeading,r=e.itemDisabled,o=t.id,s=t.value,a=t.disabled,c=Object.assign(document.createElement("div"),{className:i+" "+(a?r:"")});return c.setAttribute("role","group"),Object.assign(c.dataset,{group:"",id:o,value:s}),a&&c.setAttribute("aria-disabled","true"),c.appendChild(Object.assign(document.createElement("div"),{className:n,innerHTML:s})),c},choice:function(e,t,i){var n=e.item,r=e.itemChoice,o=e.itemSelectable,s=e.selectedState,a=e.itemDisabled,c=e.placeholder,l=t.id,u=t.value,h=t.label,d=t.groupId,p=t.elementId,f=t.disabled,m=t.selected,v=t.placeholder,_=Object.assign(document.createElement("div"),{id:p,innerHTML:h,className:n+" "+r});return m&&_.classList.add(s),v&&_.classList.add(c),_.setAttribute("role",d&&d>0?"treeitem":"option"),Object.assign(_.dataset,{choice:"",id:l,value:u,selectText:i}),f?(_.classList.add(a),_.dataset.choiceDisabled="",_.setAttribute("aria-disabled","true")):(_.classList.add(o),_.dataset.choiceSelectable=""),_},input:function(e,t){var i=e.input,n=e.inputCloned,r=Object.assign(document.createElement("input"),{type:"text",className:i+" "+n,autocomplete:"off",autocapitalize:"off",spellcheck:!1});return r.setAttribute("role","textbox"),r.setAttribute("aria-autocomplete","list"),r.setAttribute("aria-label",t),r},dropdown:function(e){var t=e.list,i=e.listDropdown,n=document.createElement("div");return n.classList.add(t,i),n.setAttribute("aria-expanded","false"),n},notice:function(e,t,i){var n=e.item,r=e.itemChoice,o=e.noResults,s=e.noChoices;void 0===i&&(i="");var a=[n,r];return"no-choices"===i?a.push(s):"no-results"===i&&a.push(o),Object.assign(document.createElement("div"),{innerHTML:t,className:a.join(" ")})},option:function(e){var t=e.label,i=e.value,n=e.customProperties,r=e.active,o=e.disabled,s=new Option(t,i,!1,r);return n&&(s.dataset.customProperties=""+n),s.disabled=!!o,s}};t.default=n},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(0);t.addChoice=function(e){var t=e.value,i=e.label,r=e.id,o=e.groupId,s=e.disabled,a=e.elementId,c=e.customProperties,l=e.placeholder,u=e.keyCode;return{type:n.ACTION_TYPES.ADD_CHOICE,value:t,label:i,id:r,groupId:o,disabled:s,elementId:a,customProperties:c,placeholder:l,keyCode:u}},t.filterChoices=function(e){return{type:n.ACTION_TYPES.FILTER_CHOICES,results:e}},t.activateChoices=function(e){return void 0===e&&(e=!0),{type:n.ACTION_TYPES.ACTIVATE_CHOICES,active:e}},t.clearChoices=function(){return{type:n.ACTION_TYPES.CLEAR_CHOICES}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(0);t.addItem=function(e){var t=e.value,i=e.label,r=e.id,o=e.choiceId,s=e.groupId,a=e.customProperties,c=e.placeholder,l=e.keyCode;return{type:n.ACTION_TYPES.ADD_ITEM,value:t,label:i,id:r,choiceId:o,groupId:s,customProperties:a,placeholder:c,keyCode:l}},t.removeItem=function(e,t){return{type:n.ACTION_TYPES.REMOVE_ITEM,id:e,choiceId:t}},t.highlightItem=function(e,t){return{type:n.ACTION_TYPES.HIGHLIGHT_ITEM,id:e,highlighted:t}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(0);t.addGroup=function(e){var t=e.value,i=e.id,r=e.active,o=e.disabled;return{type:n.ACTION_TYPES.ADD_GROUP,value:t,id:i,active:r,disabled:o}}},function(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=i(0);t.clearAll=function(){return{type:n.ACTION_TYPES.CLEAR_ALL}},t.resetTo=function(e){return{type:n.ACTION_TYPES.RESET_TO,state:e}},t.setIsLoading=function(e){return{type:n.ACTION_TYPES.SET_IS_LOADING,isLoading:e}}}]).default; \ No newline at end of file diff --git a/public/test/select-multiple/index.html b/public/test/select-multiple/index.html index c97eeca0a..113fe48f3 100644 --- a/public/test/select-multiple/index.html +++ b/public/test/select-multiple/index.html @@ -43,18 +43,12 @@ - + - - + + diff --git a/public/test/select-one/index.html b/public/test/select-one/index.html index b567b28f4..e54b39141 100644 --- a/public/test/select-one/index.html +++ b/public/test/select-one/index.html @@ -43,18 +43,12 @@ - + - - + + diff --git a/public/test/text/index.html b/public/test/text/index.html index cb86435ef..1fc30d45c 100644 --- a/public/test/text/index.html +++ b/public/test/text/index.html @@ -43,18 +43,12 @@ - + - - + + diff --git a/src/scripts/actions/choices.js b/src/scripts/actions/choices.js deleted file mode 100644 index ad22b2956..000000000 --- a/src/scripts/actions/choices.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @typedef {import('redux').Action} Action - * @typedef {import('../../../types/index').Choices.Choice} Choice - */ - -import { ACTION_TYPES } from '../constants'; - -/** - * @argument {Choice} choice - * @returns {Action & Choice} - */ -export const addChoice = ({ - value, - label, - id, - groupId, - disabled, - elementId, - customProperties, - placeholder, - keyCode, -}) => ({ - type: ACTION_TYPES.ADD_CHOICE, - value, - label, - id, - groupId, - disabled, - elementId, - customProperties, - placeholder, - keyCode, -}); - -/** - * @argument {Choice[]} results - * @returns {Action & { results: Choice[] }} - */ -export const filterChoices = results => ({ - type: ACTION_TYPES.FILTER_CHOICES, - results, -}); - -/** - * @argument {boolean} active - * @returns {Action & { active: boolean }} - */ -export const activateChoices = (active = true) => ({ - type: ACTION_TYPES.ACTIVATE_CHOICES, - active, -}); - -/** - * @returns {Action} - */ -export const clearChoices = () => ({ - type: ACTION_TYPES.CLEAR_CHOICES, -}); diff --git a/src/scripts/actions/choices.test.js b/src/scripts/actions/choices.test.ts similarity index 80% rename from src/scripts/actions/choices.test.js rename to src/scripts/actions/choices.test.ts index aedb0fdd5..d023b2550 100644 --- a/src/scripts/actions/choices.test.js +++ b/src/scripts/actions/choices.test.ts @@ -6,15 +6,15 @@ describe('actions/choices', () => { it('returns ADD_CHOICE action', () => { const value = 'test'; const label = 'test'; - const id = 'test'; - const groupId = 'test'; + const id = 1; + const groupId = 1; const disabled = false; - const elementId = 'test'; - const customProperties = 'test'; - const placeholder = 'test'; + const elementId = 1; + const customProperties = { test: true }; + const placeholder = true; const keyCode = 10; - const expectedAction = { + const expectedAction: actions.AddChoiceAction = { type: 'ADD_CHOICE', value, label, @@ -46,7 +46,7 @@ describe('actions/choices', () => { describe('filterChoices action', () => { it('returns FILTER_CHOICES action', () => { const results = Array(10); - const expectedAction = { + const expectedAction: actions.FilterChoicesAction = { type: 'FILTER_CHOICES', results, }; @@ -58,7 +58,7 @@ describe('actions/choices', () => { describe('activateChoices action', () => { describe('not passing active parameter', () => { it('returns ACTIVATE_CHOICES action', () => { - const expectedAction = { + const expectedAction: actions.ActivateChoicesAction = { type: 'ACTIVATE_CHOICES', active: true, }; @@ -70,7 +70,7 @@ describe('actions/choices', () => { describe('passing active parameter', () => { it('returns ACTIVATE_CHOICES action', () => { const active = true; - const expectedAction = { + const expectedAction: actions.ActivateChoicesAction = { type: 'ACTIVATE_CHOICES', active, }; @@ -82,7 +82,7 @@ describe('actions/choices', () => { describe('clearChoices action', () => { it('returns CLEAR_CHOICES action', () => { - const expectedAction = { + const expectedAction: actions.ClearChoicesAction = { type: 'CLEAR_CHOICES', }; diff --git a/src/scripts/actions/choices.ts b/src/scripts/actions/choices.ts new file mode 100644 index 000000000..526f4d481 --- /dev/null +++ b/src/scripts/actions/choices.ts @@ -0,0 +1,73 @@ +import { ACTION_TYPES } from '../constants'; +import { Choice } from '../interfaces'; + +export interface AddChoiceAction { + type: typeof ACTION_TYPES.ADD_CHOICE; + id: number; + value: string; + label: string; + groupId: number; + disabled: boolean; + elementId: number; + customProperties: object; + placeholder: boolean; + keyCode: number; +} + +export interface Result { + item: T; + score: number; +} + +export interface FilterChoicesAction { + type: typeof ACTION_TYPES.FILTER_CHOICES; + results: Result[]; +} + +export interface ActivateChoicesAction { + type: typeof ACTION_TYPES.ACTIVATE_CHOICES; + active: boolean; +} + +export interface ClearChoicesAction { + type: typeof ACTION_TYPES.CLEAR_CHOICES; +} + +export const addChoice = ({ + value, + label, + id, + groupId, + disabled, + elementId, + customProperties, + placeholder, + keyCode, +}): AddChoiceAction => ({ + type: ACTION_TYPES.ADD_CHOICE, + value, + label, + id, + groupId, + disabled, + elementId, + customProperties, + placeholder, + keyCode, +}); + +export const filterChoices = ( + results: Result[], +): FilterChoicesAction => ({ + type: ACTION_TYPES.FILTER_CHOICES, + results, +}); + +export const activateChoices = (active = true): ActivateChoicesAction => ({ + type: ACTION_TYPES.ACTIVATE_CHOICES, + active, +}); + +export const clearChoices = (): ClearChoicesAction => ({ + type: ACTION_TYPES.CLEAR_CHOICES, +}); diff --git a/src/scripts/actions/groups.js b/src/scripts/actions/groups.js deleted file mode 100644 index 41ac60c96..000000000 --- a/src/scripts/actions/groups.js +++ /dev/null @@ -1,18 +0,0 @@ -import { ACTION_TYPES } from '../constants'; - -/** - * @typedef {import('redux').Action} Action - * @typedef {import('../../../types/index').Choices.Group} Group - */ - -/** - * @param {Group} group - * @returns {Action & Group} - */ -export const addGroup = ({ value, id, active, disabled }) => ({ - type: ACTION_TYPES.ADD_GROUP, - value, - id, - active, - disabled, -}); diff --git a/src/scripts/actions/groups.test.js b/src/scripts/actions/groups.test.ts similarity index 86% rename from src/scripts/actions/groups.test.js rename to src/scripts/actions/groups.test.ts index 07d501d4d..f56e29c27 100644 --- a/src/scripts/actions/groups.test.js +++ b/src/scripts/actions/groups.test.ts @@ -5,10 +5,11 @@ describe('actions/groups', () => { describe('addGroup action', () => { it('returns ADD_GROUP action', () => { const value = 'test'; - const id = 'test'; + const id = 1; const active = true; const disabled = false; - const expectedAction = { + + const expectedAction: actions.AddGroupAction = { type: 'ADD_GROUP', value, id, diff --git a/src/scripts/actions/groups.ts b/src/scripts/actions/groups.ts new file mode 100644 index 000000000..9c4a24e6e --- /dev/null +++ b/src/scripts/actions/groups.ts @@ -0,0 +1,27 @@ +import { ACTION_TYPES } from '../constants'; + +export interface AddGroupAction { + type: typeof ACTION_TYPES.ADD_GROUP; + id: number; + value: string; + active: boolean; + disabled: boolean; +} + +export const addGroup = ({ + value, + id, + active, + disabled, +}: { + id: number; + value: string; + active: boolean; + disabled: boolean; +}): AddGroupAction => ({ + type: ACTION_TYPES.ADD_GROUP, + value, + id, + active, + disabled, +}); diff --git a/src/scripts/actions/items.js b/src/scripts/actions/items.js deleted file mode 100644 index e28075925..000000000 --- a/src/scripts/actions/items.js +++ /dev/null @@ -1,53 +0,0 @@ -import { ACTION_TYPES } from '../constants'; - -/** - * @typedef {import('redux').Action} Action - * @typedef {import('../../../types/index').Choices.Item} Item - */ - -/** - * @param {Item} item - * @returns {Action & Item} - */ -export const addItem = ({ - value, - label, - id, - choiceId, - groupId, - customProperties, - placeholder, - keyCode, -}) => ({ - type: ACTION_TYPES.ADD_ITEM, - value, - label, - id, - choiceId, - groupId, - customProperties, - placeholder, - keyCode, -}); - -/** - * @param {number} id - * @param {number} choiceId - * @returns {Action & { id: number, choiceId: number }} - */ -export const removeItem = (id, choiceId) => ({ - type: ACTION_TYPES.REMOVE_ITEM, - id, - choiceId, -}); - -/** - * @param {number} id - * @param {boolean} highlighted - * @returns {Action & { id: number, highlighted: boolean }} - */ -export const highlightItem = (id, highlighted) => ({ - type: ACTION_TYPES.HIGHLIGHT_ITEM, - id, - highlighted, -}); diff --git a/src/scripts/actions/items.test.js b/src/scripts/actions/items.test.ts similarity index 80% rename from src/scripts/actions/items.test.js rename to src/scripts/actions/items.test.ts index c637975b3..28b9ba037 100644 --- a/src/scripts/actions/items.test.js +++ b/src/scripts/actions/items.test.ts @@ -6,14 +6,14 @@ describe('actions/items', () => { it('returns ADD_ITEM action', () => { const value = 'test'; const label = 'test'; - const id = '1234'; - const choiceId = '1234'; - const groupId = 'test'; + const id = 1; + const choiceId = 1; + const groupId = 1; const customProperties = { test: true }; const placeholder = true; const keyCode = 10; - const expectedAction = { + const expectedAction: actions.AddItemAction = { type: 'ADD_ITEM', value, label, @@ -42,9 +42,10 @@ describe('actions/items', () => { describe('removeItem action', () => { it('returns REMOVE_ITEM action', () => { - const id = '1234'; - const choiceId = '1'; - const expectedAction = { + const id = 1; + const choiceId = 1; + + const expectedAction: actions.RemoveItemAction = { type: 'REMOVE_ITEM', id, choiceId, @@ -56,10 +57,10 @@ describe('actions/items', () => { describe('highlightItem action', () => { it('returns HIGHLIGHT_ITEM action', () => { - const id = '1234'; + const id = 1; const highlighted = true; - const expectedAction = { + const expectedAction: actions.HighlightItemAction = { type: 'HIGHLIGHT_ITEM', id, highlighted, diff --git a/src/scripts/actions/items.ts b/src/scripts/actions/items.ts new file mode 100644 index 000000000..e7ec8a497 --- /dev/null +++ b/src/scripts/actions/items.ts @@ -0,0 +1,70 @@ +import { ACTION_TYPES } from '../constants'; + +export interface AddItemAction { + type: typeof ACTION_TYPES.ADD_ITEM; + id: number; + value: string; + label: string; + choiceId: number; + groupId: number; + customProperties: object; + placeholder: boolean; + keyCode: number; +} + +export interface RemoveItemAction { + type: typeof ACTION_TYPES.REMOVE_ITEM; + id: number; + choiceId: number; +} + +export interface HighlightItemAction { + type: typeof ACTION_TYPES.HIGHLIGHT_ITEM; + id: number; + highlighted: boolean; +} + +export const addItem = ({ + value, + label, + id, + choiceId, + groupId, + customProperties, + placeholder, + keyCode, +}: { + id: number; + value: string; + label: string; + choiceId: number; + groupId: number; + customProperties: object; + placeholder: boolean; + keyCode: number; +}): AddItemAction => ({ + type: ACTION_TYPES.ADD_ITEM, + value, + label, + id, + choiceId, + groupId, + customProperties, + placeholder, + keyCode, +}); + +export const removeItem = (id: number, choiceId: number): RemoveItemAction => ({ + type: ACTION_TYPES.REMOVE_ITEM, + id, + choiceId, +}); + +export const highlightItem = ( + id: number, + highlighted: boolean, +): HighlightItemAction => ({ + type: ACTION_TYPES.HIGHLIGHT_ITEM, + id, + highlighted, +}); diff --git a/src/scripts/actions/misc.js b/src/scripts/actions/misc.js deleted file mode 100644 index e512610c9..000000000 --- a/src/scripts/actions/misc.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @typedef {import('redux').Action} Action - */ - -/** - * @returns {Action} - */ -export const clearAll = () => ({ - type: 'CLEAR_ALL', -}); - -/** - * @param {any} state - * @returns {Action & { state: object }} - */ -export const resetTo = state => ({ - type: 'RESET_TO', - state, -}); - -/** - * @param {boolean} isLoading - * @returns {Action & { isLoading: boolean }} - */ -export const setIsLoading = isLoading => ({ - type: 'SET_IS_LOADING', - isLoading, -}); diff --git a/src/scripts/actions/misc.test.js b/src/scripts/actions/misc.test.ts similarity index 73% rename from src/scripts/actions/misc.test.js rename to src/scripts/actions/misc.test.ts index 784244418..b1577b491 100644 --- a/src/scripts/actions/misc.test.js +++ b/src/scripts/actions/misc.test.ts @@ -1,10 +1,11 @@ import { expect } from 'chai'; import * as actions from './misc'; +import { State } from '../interfaces'; describe('actions/misc', () => { describe('clearAll action', () => { it('returns CLEAR_ALL action', () => { - const expectedAction = { + const expectedAction: actions.ClearAllAction = { type: 'CLEAR_ALL', }; @@ -14,8 +15,13 @@ describe('actions/misc', () => { describe('resetTo action', () => { it('returns RESET_TO action', () => { - const state = { test: true }; - const expectedAction = { + const state: State = { + choices: [], + items: [], + groups: [], + loading: false, + }; + const expectedAction: actions.ResetToAction = { type: 'RESET_TO', state, }; @@ -27,7 +33,7 @@ describe('actions/misc', () => { describe('setIsLoading action', () => { describe('setting loading state to true', () => { it('returns expected action', () => { - const expectedAction = { + const expectedAction: actions.SetIsLoadingAction = { type: 'SET_IS_LOADING', isLoading: true, }; @@ -38,7 +44,7 @@ describe('actions/misc', () => { describe('setting loading state to false', () => { it('returns expected action', () => { - const expectedAction = { + const expectedAction: actions.SetIsLoadingAction = { type: 'SET_IS_LOADING', isLoading: false, }; diff --git a/src/scripts/actions/misc.ts b/src/scripts/actions/misc.ts new file mode 100644 index 000000000..24a6f5a72 --- /dev/null +++ b/src/scripts/actions/misc.ts @@ -0,0 +1,30 @@ +import { State } from '../interfaces'; +import { ACTION_TYPES } from '../constants'; + +export interface ClearAllAction { + type: typeof ACTION_TYPES.CLEAR_ALL; +} + +export interface ResetToAction { + type: typeof ACTION_TYPES.RESET_TO; + state: State; +} + +export interface SetIsLoadingAction { + type: typeof ACTION_TYPES.SET_IS_LOADING; + isLoading: boolean; +} + +export const clearAll = (): ClearAllAction => ({ + type: ACTION_TYPES.CLEAR_ALL, +}); + +export const resetTo = (state: State): ResetToAction => ({ + type: ACTION_TYPES.RESET_TO, + state, +}); + +export const setIsLoading = (isLoading: boolean): SetIsLoadingAction => ({ + type: ACTION_TYPES.SET_IS_LOADING, + isLoading, +}); diff --git a/src/scripts/choices.test.js b/src/scripts/choices.test.ts similarity index 91% rename from src/scripts/choices.test.js rename to src/scripts/choices.test.ts index f5c0bd117..d22926505 100644 --- a/src/scripts/choices.test.js +++ b/src/scripts/choices.test.ts @@ -3,9 +3,12 @@ import { spy, stub } from 'sinon'; import sinonChai from 'sinon-chai'; import Choices from './choices'; + import { EVENTS, ACTION_TYPES, DEFAULT_CONFIG, KEY_CODES } from './constants'; import { WrappedSelect, WrappedInput } from './components/index'; import { removeItem } from './actions/items'; +import { Item, Choice, Group } from './interfaces'; +import templates from './templates'; chai.use(sinonChai); @@ -28,12 +31,6 @@ describe('choices', () => { instance = null; }); - const returnsInstance = () => { - it('returns this', () => { - expect(output).to.eql(instance); - }); - }; - describe('constructor', () => { describe('config', () => { describe('not passing config options', () => { @@ -88,7 +85,7 @@ describe('choices', () => { `; instance = new Choices('[data-choice]', { - renderSelectedChoices: 'test', + renderSelectedChoices: 'test' as any, }); expect(instance.config.renderSelectedChoices).to.equal('auto'); @@ -211,7 +208,7 @@ describe('choices', () => { `; - instance = new Choices(document.querySelector('[data-choice]')); + instance = new Choices('[data-choice]'); expect(instance.passedElement).to.be.an.instanceOf(WrappedInput); }); @@ -223,7 +220,7 @@ describe('choices', () => { `; - instance = new Choices(document.querySelector('[data-choice]')); + instance = new Choices('[data-choice]'); expect(instance.passedElement).to.be.an.instanceOf(WrappedSelect); }); @@ -386,8 +383,8 @@ describe('choices', () => { expect(clearStoreSpy.called).to.equal(true); }); - it('nullifys templates config', () => { - expect(instance._templates).to.equal(null); + it('restes templates config', () => { + expect(instance._templates).to.deep.equal(templates); }); it('resets initialise flag', () => { @@ -423,7 +420,9 @@ describe('choices', () => { output = instance.enable(); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('returns early', () => { expect(passedElementEnableSpy.called).to.equal(false); @@ -481,7 +480,9 @@ describe('choices', () => { output = instance.disable(); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('returns early', () => { expect(removeEventListenersSpy.called).to.equal(false); @@ -638,7 +639,9 @@ describe('choices', () => { output = instance.hideDropdown(); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('returns early', () => { expect(containerOuterCloseSpy.called).to.equal(false); @@ -735,7 +738,9 @@ describe('choices', () => { output = instance.highlightItem(); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('returns early', () => { expect(passedElementTriggerEventStub.called).to.equal(false); @@ -745,7 +750,7 @@ describe('choices', () => { }); describe('item passed', () => { - const item = { + const item: Item = { id: 1234, value: 'Test', label: 'Test', @@ -756,7 +761,9 @@ describe('choices', () => { output = instance.highlightItem(item, true); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('dispatches highlightItem action with correct arguments', () => { expect(storeDispatchSpy.called).to.equal(true); @@ -817,7 +824,9 @@ describe('choices', () => { expect(passedElementTriggerEventStub.called).to.equal(false); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); }); }); }); @@ -850,7 +859,9 @@ describe('choices', () => { output = instance.unhighlightItem(); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('returns early', () => { expect(passedElementTriggerEventStub.called).to.equal(false); @@ -860,7 +871,7 @@ describe('choices', () => { }); describe('item passed', () => { - const item = { + const item: Item = { id: 1234, value: 'Test', label: 'Test', @@ -871,7 +882,9 @@ describe('choices', () => { output = instance.unhighlightItem(item, true); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('dispatches highlightItem action with correct arguments', () => { expect(storeDispatchSpy.called).to.equal(true); @@ -932,7 +945,9 @@ describe('choices', () => { expect(passedElementTriggerEventStub.called).to.equal(false); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); }); }); }); @@ -966,7 +981,9 @@ describe('choices', () => { storeGetItemsStub.reset(); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('highlights each item in store', () => { expect(highlightItemStub.callCount).to.equal(items.length); @@ -1004,7 +1021,9 @@ describe('choices', () => { storeGetItemsStub.reset(); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('unhighlights each item in store', () => { expect(unhighlightItemStub.callCount).to.equal(items.length); @@ -1027,7 +1046,9 @@ describe('choices', () => { instance._store.dispatch.reset(); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('dispatches clearChoices action', () => { expect(storeDispatchStub.lastCall.args[0]).to.eql({ @@ -1050,7 +1071,9 @@ describe('choices', () => { instance._store.dispatch.reset(); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('dispatches clearAll action', () => { expect(storeDispatchStub.lastCall.args[0]).to.eql({ @@ -1075,7 +1098,9 @@ describe('choices', () => { instance._store.dispatch.reset(); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); describe('text element', () => { beforeEach(() => { @@ -1164,14 +1189,14 @@ describe('choices', () => { const handleLoadingStateSpy = spy(choice, '_handleLoadingState'); let fetcherCalled = false; - const fetcher = async inst => { + const fetcher = async (inst): Promise => { expect(inst).to.eq(choice); fetcherCalled = true; await new Promise(resolve => setTimeout(resolve, 800)); return [ - { label: 'l1', value: 'v1', customProperties: 'prop1' }, - { label: 'l2', value: 'v2', customProperties: 'prop2' }, + { label: 'l1', value: 'v1', customProperties: { prop1: true } }, + { label: 'l2', value: 'v2', customProperties: { prop2: false } }, ]; }; expect(choice._store.choices.length).to.equal(0); @@ -1182,7 +1207,9 @@ describe('choices', () => { expect(handleLoadingStateSpy.callCount).to.equal(2); expect(choice._store.choices[1].value).to.equal('v2'); expect(choice._store.choices[1].label).to.equal('l2'); - expect(choice._store.choices[1].customProperties).to.equal('prop2'); + expect(choice._store.choices[1].customProperties).to.deep.equal({ + prop2: false, + }); }); }); }); @@ -1211,7 +1238,9 @@ describe('choices', () => { output = instance.setValue(values); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('returns early', () => { expect(setChoiceOrItemStub.called).to.equal(false); @@ -1224,7 +1253,9 @@ describe('choices', () => { output = instance.setValue(values); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('sets each value', () => { expect(setChoiceOrItemStub.callCount).to.equal(2); @@ -1252,7 +1283,9 @@ describe('choices', () => { output = instance.setChoiceByValue([]); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('returns early', () => { expect(findAndSelectChoiceByValueStub.called).to.equal(false); @@ -1272,7 +1305,9 @@ describe('choices', () => { output = instance.setChoiceByValue(value); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('sets each choice with same value', () => { expect(findAndSelectChoiceByValueStub.called).to.equal(true); @@ -1289,7 +1324,9 @@ describe('choices', () => { output = instance.setChoiceByValue(values); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('sets each choice with same value', () => { expect(findAndSelectChoiceByValueStub.callCount).to.equal(2); @@ -1509,7 +1546,9 @@ describe('choices', () => { output = instance.removeHighlightedItems(); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('removes each highlighted item in store', () => { expect(removeItemStub.callCount).to.equal(2); @@ -1521,7 +1560,9 @@ describe('choices', () => { output = instance.removeHighlightedItems(true); }); - returnsInstance(output); + it('returns this', () => { + expect(output).to.eql(instance); + }); it('triggers event with item value', () => { expect(triggerChangeStub.callCount).to.equal(2); @@ -1538,19 +1579,23 @@ describe('choices', () => { let containerOuterRemoveLoadingStateStub; const value = 'value'; const label = 'label'; - const choices = [ + const choices: Choice[] = [ { id: 1, value: '1', label: 'Test 1', + selected: false, + disabled: false, }, { id: 2, value: '2', label: 'Test 2', + selected: false, + disabled: true, }, ]; - const groups = [ + const groups: Group[] = [ { ...choices[0], choices, @@ -1634,10 +1679,10 @@ describe('choices', () => { expect(call.args[0]).to.eql({ value: choices[index][value], label: choices[index][label], - isSelected: choices[index].selected, - isDisabled: choices[index].disabled, + isSelected: !!choices[index].selected, + isDisabled: !!choices[index].disabled, customProperties: choices[index].customProperties, - placeholder: choices[index].placeholder, + placeholder: !!choices[index].placeholder, }); }); }); @@ -1679,7 +1724,7 @@ describe('choices', () => { describe('private methods', () => { describe('_createGroupsFragment', () => { let _createChoicesFragmentStub; - const choices = [ + const choices: Choice[] = [ { id: 1, selected: true, @@ -1703,7 +1748,7 @@ describe('choices', () => { }, ]; - const groups = [ + const groups: Group[] = [ { id: 2, value: 'Group 2', @@ -1925,12 +1970,12 @@ describe('choices', () => { }); describe('when a placeholder option is not defined', () => { - it('returns false', () => { + it('returns null', () => { instance._isSelectElement = true; instance.passedElement.placeholderOption = undefined; const value = instance._generatePlaceholderValue(); - expect(value).to.equal(false); + expect(value).to.equal(null); }); }); }); @@ -1970,7 +2015,7 @@ describe('choices', () => { }); describe('when the placeholder attribute is not defined on the passed element', () => { - it('returns false', () => { + it('returns null', () => { instance._isSelectElement = false; instance.config.placeholder = true; instance.config.placeholderValue = undefined; @@ -1981,32 +2026,25 @@ describe('choices', () => { }; const value = instance._generatePlaceholderValue(); - expect(value).to.equal(false); + expect(value).to.equal(null); }); }); }); }); describe('when the placeholder config option is set to false', () => { - it('returns false', () => { + it('returns null', () => { instance._isSelectElement = false; instance.config.placeholder = false; const value = instance._generatePlaceholderValue(); - expect(value).to.equal(false); + expect(value).to.equal(null); }); }); }); }); describe('_getTemplate', () => { - describe('when not passing a template key', () => { - it('returns null', () => { - output = instance._getTemplate(); - expect(output).to.equal(null); - }); - }); - describe('when passing a template key', () => { it('returns the generated template for the given template key', () => { const templateKey = 'test'; @@ -2028,130 +2066,96 @@ describe('choices', () => { }); describe('_onKeyDown', () => { + let activeItems; + let hasItems; + let hasActiveDropdown; + let hasFocussedInput; + beforeEach(() => { instance.showDropdown = stub(); - instance._onAKey = stub(); + instance._onSelectKey = stub(); instance._onEnterKey = stub(); instance._onEscapeKey = stub(); instance._onDirectionKey = stub(); instance._onDeleteKey = stub(); + + ({ activeItems } = instance._store); + hasItems = instance.itemList.hasChildren(); + hasActiveDropdown = instance.dropdown.isActive; + hasFocussedInput = instance.input.isFocussed; }); - const scenarios = [ - { - keyCode: KEY_CODES.BACK_KEY, - expectedFunctionCall: '_onDeleteKey', - }, - { - keyCode: KEY_CODES.DELETE_KEY, - expectedFunctionCall: '_onDeleteKey', - }, - { - keyCode: KEY_CODES.A_KEY, - expectedFunctionCall: '_onAKey', - }, - { - keyCode: KEY_CODES.ENTER_KEY, - expectedFunctionCall: '_onEnterKey', - }, - { - keyCode: KEY_CODES.UP_KEY, - expectedFunctionCall: '_onDirectionKey', - }, - { - keyCode: KEY_CODES.DOWN_KEY, - expectedFunctionCall: '_onDirectionKey', - }, - { - keyCode: KEY_CODES.DOWN_KEY, - expectedFunctionCall: '_onDirectionKey', - }, - { - keyCode: KEY_CODES.ESC_KEY, - expectedFunctionCall: '_onEscapeKey', - }, - ]; + describe('direction key', () => { + const keyCodes = [ + KEY_CODES.UP_KEY, + KEY_CODES.DOWN_KEY, + KEY_CODES.PAGE_UP_KEY, + KEY_CODES.PAGE_DOWN_KEY, + ]; - describe('when called with a keydown event', () => { - scenarios.forEach(({ keyCode, expectedFunctionCall }) => { - describe(`when the keyCode is ${keyCode}`, () => { - it(`calls ${expectedFunctionCall} with the expected arguments`, () => { - const mockEvent = { - keyCode, - }; - - instance._onKeyDown(mockEvent); - - expect(instance[expectedFunctionCall]).to.have.been.calledWith({ - event: mockEvent, - activeItems: instance._store.activeItems, - hasActiveDropdown: instance.dropdown.isActive, - hasFocusedInput: instance.input.isFocussed, - hasItems: instance.itemList.hasChildren(), - }); - }); - }); - }); + keyCodes.forEach(keyCode => { + it(`calls _onDirectionKey with the expected arguments`, () => { + const event = { + keyCode, + }; - describe('select input', () => { - describe('when the dropdown is not active', () => { - describe('when the key was alpha-numeric', () => { - beforeEach(() => { - instance._isTextElement = false; - instance.dropdown.isActive = false; - }); + instance._onKeyDown(event); - it('shows the dropdown', () => { - instance._onKeyDown({ - keyCode: KEY_CODES.A_KEY, - }); + expect(instance._onDirectionKey).to.have.been.calledWith( + event, + hasActiveDropdown, + ); + }); + }); + }); - expect(instance.showDropdown).to.have.been.calledWith(); - }); + describe('select key', () => { + it(`calls _onSelectKey with the expected arguments`, () => { + const event = { + keyCode: KEY_CODES.A_KEY, + }; - describe('when the input is not focussed', () => { - beforeEach(() => { - instance.input.isFocussed = false; - }); + instance._onKeyDown(event); - it('updates the input value with the character corresponding to the key code', () => { - instance._onKeyDown({ - keyCode: KEY_CODES.A_KEY, - }); + expect(instance._onSelectKey).to.have.been.calledWith( + event, + hasItems, + ); + }); + }); - expect(instance.input.value).to.contain('a'); - }); - }); + describe('enter key', () => { + it(`calls _onEnterKey with the expected arguments`, () => { + const event = { + keyCode: KEY_CODES.ENTER_KEY, + }; - describe('when the input is focussed', () => { - beforeEach(() => { - instance.input.isFocussed = true; - }); + instance._onKeyDown(event); - it('does not update the input value', () => { - instance._onKeyDown({ - keyCode: KEY_CODES.A_KEY, - }); + expect(instance._onEnterKey).to.have.been.calledWith( + event, + activeItems, + hasActiveDropdown, + ); + }); + }); - expect(instance.input.value).to.not.contain('a'); - }); - }); - }); + describe('delete key', () => { + const keyCodes = [KEY_CODES.DELETE_KEY, KEY_CODES.BACK_KEY]; - describe('when the input was not alpha-numeric', () => { - beforeEach(() => { - instance._isTextElement = false; - instance.dropdown.isActive = false; - }); + keyCodes.forEach(keyCode => { + it(`calls _onDeleteKey with the expected arguments`, () => { + const event = { + keyCode, + }; - it('does not show the dropdown', () => { - instance._onKeyDown({ - keyCode: KEY_CODES.DELETE_KEY, - }); + instance._onKeyDown(event); - expect(instance.showDropdown).to.not.have.been.called; - }); - }); + expect(instance._onDeleteKey).to.have.been.calledWith( + event, + activeItems, + hasFocussedInput, + ); }); }); }); diff --git a/src/scripts/choices.js b/src/scripts/choices.ts similarity index 80% rename from src/scripts/choices.js rename to src/scripts/choices.ts index a88982929..238bef8bd 100644 --- a/src/scripts/choices.js +++ b/src/scripts/choices.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import Fuse from 'fuse.js'; import merge from 'deepmerge'; @@ -18,12 +19,13 @@ import { SELECT_ONE_TYPE, SELECT_MULTIPLE_TYPE, } from './constants'; -import { TEMPLATES } from './templates'; +import templates from './templates'; import { addChoice, filterChoices, activateChoices, clearChoices, + Result, } from './actions/choices'; import { addItem, removeItem, highlightItem } from './actions/items'; import { addGroup } from './actions/groups'; @@ -37,48 +39,90 @@ import { sortByScore, generateId, existsInArray, - cloneObject, diff, } from './lib/utils'; +import { + Options, + Choice, + Item, + Group, + Notice, + State, + PassedElement, +} from './interfaces'; +import { defaultState } from './reducers'; /** @see {@link http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c} */ const IS_IE11 = '-ms-scroll-limit' in document.documentElement.style && '-ms-ime-align' in document.documentElement.style; -/** - * @typedef {import('../../types/index').Choices.Choice} Choice - * @typedef {import('../../types/index').Choices.Item} Item - * @typedef {import('../../types/index').Choices.Group} Group - * @typedef {import('../../types/index').Choices.Options} Options - */ - -/** @type {Partial} */ -const USER_DEFAULTS = {}; +const USER_DEFAULTS: Partial = {}; /** * Choices * @author Josh Johnson */ class Choices { - static get defaults() { + static get defaults(): { + options: Partial; + templates: typeof templates; + } { return Object.preventExtensions({ - get options() { + get options(): Partial { return USER_DEFAULTS; }, - get templates() { - return TEMPLATES; + get templates(): typeof templates { + return templates; }, }); } - /** - * @param {string | HTMLInputElement | HTMLSelectElement} element - * @param {Partial} userConfig - */ - constructor(element = '[data-choice]', userConfig = {}) { - /** @type {Partial} */ - this.config = merge.all( + initialised: boolean; + config: Options; + passedElement: WrappedInput | WrappedSelect; + containerOuter: Container; + containerInner: Container; + choiceList: List; + itemList: List; + input: Input; + dropdown: Dropdown; + + _isTextElement: boolean; + _isSelectOneElement: boolean; + _isSelectMultipleElement: boolean; + _isSelectElement: boolean; + _store: Store; + _templates: typeof templates; + _initialState: State; + _currentState: State; + _prevState: State; + _currentValue: string; + _canSearch: boolean; + _isScrollingOnIe: boolean; + _highlightPosition: number; + _wasTap: boolean; + _isSearching: boolean; + _placeholderValue: string | null; + _baseId: string; + _direction: HTMLElement['dir']; + _idNames: { + itemChoice: string; + }; + _presetGroups: Group[] | HTMLOptGroupElement[] | Element[]; + _presetOptions: Item[] | HTMLOptionElement[]; + _presetChoices: Partial[]; + _presetItems: Item[] | string[]; + + constructor( + element: + | string + | Element + | HTMLInputElement + | HTMLSelectElement = '[data-choice]', + userConfig: Partial = {}, + ) { + this.config = merge.all( [DEFAULT_CONFIG, Choices.defaults.options, userConfig], // When merging array configs, replace with a copy of the userConfig array, // instead of concatenating with the default array @@ -116,7 +160,7 @@ class Choices { this.config.searchEnabled = this._isSelectMultipleElement || this.config.searchEnabled; - if (!['auto', 'always'].includes(this.config.renderSelectedChoices)) { + if (!['auto', 'always'].includes(`${this.config.renderSelectedChoices}`)) { this.config.renderSelectedChoices = 'auto'; } @@ -134,35 +178,36 @@ class Choices { if (this._isTextElement) { this.passedElement = new WrappedInput({ - element: passedElement, + element: passedElement as HTMLInputElement, classNames: this.config.classNames, delimiter: this.config.delimiter, }); } else { this.passedElement = new WrappedSelect({ - element: passedElement, + element: passedElement as HTMLSelectElement, classNames: this.config.classNames, - template: data => this._templates.option(data), + template: (data: Item): HTMLOptionElement => + this._templates.option(data), }); } this.initialised = false; this._store = new Store(); - this._initialState = {}; - this._currentState = {}; - this._prevState = {}; + this._initialState = defaultState; + this._currentState = defaultState; + this._prevState = defaultState; this._currentValue = ''; - this._canSearch = this.config.searchEnabled; + this._canSearch = !!this.config.searchEnabled; this._isScrollingOnIe = false; this._highlightPosition = 0; this._wasTap = true; this._placeholderValue = this._generatePlaceholderValue(); this._baseId = generateId(this.passedElement.element, 'choices-'); + /** * setting direction in cases where it's explicitly set on passedElement * or when calculated direction is different from the document - * @type {HTMLElement['dir']} */ this._direction = this.passedElement.dir; @@ -181,30 +226,36 @@ class Choices { this._idNames = { itemChoice: 'item-choice', }; - // Assign preset groups from passed element - this._presetGroups = this.passedElement.optionGroups; - // Assign preset options from passed element - this._presetOptions = this.passedElement.options; + + if (this._isSelectElement) { + // Assign preset groups from passed element + this._presetGroups = (this.passedElement as WrappedSelect).optionGroups; + // Assign preset options from passed element + this._presetOptions = (this.passedElement as WrappedSelect).options; + } + // Assign preset choices from passed object this._presetChoices = this.config.choices; // Assign preset items from passed object first this._presetItems = this.config.items; // Add any values passed from attribute - if (this.passedElement.value) { - this._presetItems = this._presetItems.concat( - this.passedElement.value.split(this.config.delimiter), + if (this.passedElement.value && this._isTextElement) { + const splitValues: string[] = this.passedElement.value.split( + this.config.delimiter, ); + this._presetItems = (this._presetItems as string[]).concat(splitValues); } // Create array of choices from option elements - if (this.passedElement.options) { - this.passedElement.options.forEach(o => { + if ((this.passedElement as WrappedSelect).options) { + (this.passedElement as WrappedSelect).options.forEach(option => { this._presetChoices.push({ - value: o.value, - label: o.innerHTML, - selected: o.selected, - disabled: o.disabled || o.parentNode.disabled, - placeholder: o.value === '' || o.hasAttribute('placeholder'), - customProperties: o.getAttribute('data-custom-properties'), + value: option.value, + label: option.innerHTML, + selected: !!option.selected, + disabled: option.disabled || option.parentNode.disabled, + placeholder: + option.value === '' || option.hasAttribute('placeholder'), + customProperties: option.dataset['custom-properties'], }); }); } @@ -220,7 +271,7 @@ class Choices { this._onMouseDown = this._onMouseDown.bind(this); this._onMouseOver = this._onMouseOver.bind(this); this._onFormReset = this._onFormReset.bind(this); - this._onAKey = this._onAKey.bind(this); + this._onSelectKey = this._onSelectKey.bind(this); this._onEnterKey = this._onEnterKey.bind(this); this._onEscapeKey = this._onEscapeKey.bind(this); this._onDirectionKey = this._onDirectionKey.bind(this); @@ -244,7 +295,7 @@ class Choices { this.init(); } - init() { + init(): void { if (this.initialised) { return; } @@ -253,10 +304,8 @@ class Choices { this._createElements(); this._createStructure(); - // Set initial state (We need to clone the state because some reducers - // modify the inner objects properties in the state) 🤢 - this._initialState = cloneObject(this._store.state); this._store.subscribe(this._render); + this._render(); this._addEventListeners(); @@ -277,7 +326,7 @@ class Choices { } } - destroy() { + destroy(): void { if (!this.initialised) { return; } @@ -289,14 +338,14 @@ class Choices { this.clearStore(); if (this._isSelectElement) { - this.passedElement.options = this._presetOptions; + (this.passedElement as WrappedSelect).options = this._presetOptions; } - this._templates = null; + this._templates = templates; this.initialised = false; } - enable() { + enable(): this { if (this.passedElement.isDisabled) { this.passedElement.enable(); } @@ -310,7 +359,7 @@ class Choices { return this; } - disable() { + disable(): this { if (!this.passedElement.isDisabled) { this.passedElement.disable(); } @@ -324,8 +373,8 @@ class Choices { return this; } - highlightItem(item, runEvent = true) { - if (!item) { + highlightItem(item: Item, runEvent = true): this { + if (!item || !item.id) { return this; } @@ -346,8 +395,8 @@ class Choices { return this; } - unhighlightItem(item) { - if (!item) { + unhighlightItem(item: Item): this { + if (!item || !item.id) { return this; } @@ -365,19 +414,19 @@ class Choices { return this; } - highlightAll() { + highlightAll(): this { this._store.items.forEach(item => this.highlightItem(item)); return this; } - unhighlightAll() { + unhighlightAll(): this { this._store.items.forEach(item => this.unhighlightItem(item)); return this; } - removeActiveItemsByValue(value) { + removeActiveItemsByValue(value: string): this { this._store.activeItems .filter(item => item.value === value) .forEach(item => this._removeItem(item)); @@ -385,7 +434,7 @@ class Choices { return this; } - removeActiveItems(excludedId) { + removeActiveItems(excludedId: number): this { this._store.activeItems .filter(({ id }) => id !== excludedId) .forEach(item => this._removeItem(item)); @@ -393,7 +442,7 @@ class Choices { return this; } - removeHighlightedItems(runEvent = false) { + removeHighlightedItems(runEvent = false): this { this._store.highlightedActiveItems.forEach(item => { this._removeItem(item); // If this action was performed by the user @@ -406,7 +455,7 @@ class Choices { return this; } - showDropdown(preventInputFocus) { + showDropdown(preventInputFocus?: boolean): this { if (this.dropdown.isActive) { return this; } @@ -425,7 +474,7 @@ class Choices { return this; } - hideDropdown(preventInputBlur) { + hideDropdown(preventInputBlur?: boolean): this { if (!this.dropdown.isActive) { return this; } @@ -445,21 +494,21 @@ class Choices { return this; } - getValue(valueOnly = false) { - const values = this._store.activeItems.reduce((selectedItems, item) => { - const itemValue = valueOnly ? item.value : item; - selectedItems.push(itemValue); + getValue(valueOnly = false): string[] | Item[] | Item | string { + const values = this._store.activeItems.reduce( + (selectedItems, item) => { + const itemValue = valueOnly ? item.value : item; + selectedItems.push(itemValue); - return selectedItems; - }, []); + return selectedItems; + }, + [], + ); return this._isSelectOneElement ? values[0] : values; } - /** - * @param {string[] | Item[]} items - */ - setValue(items) { + setValue(items: string[] | Item[]): this { if (!this.initialised) { return this; } @@ -469,7 +518,7 @@ class Choices { return this; } - setChoiceByValue(value) { + setChoiceByValue(value: string): this { if (!this.initialised || this._isTextElement) { return this; } @@ -492,13 +541,6 @@ class Choices { * * **Input types affected:** select-one, select-multiple * - * @template {Choice[] | ((instance: Choices) => object[] | Promise)} T - * @param {T} [choicesArrayOrFetcher] - * @param {string} [value = 'value'] - name of `value` field - * @param {string} [label = 'label'] - name of 'label' field - * @param {boolean} [replaceChoices = false] - whether to replace of add choices - * @returns {this | Promise} - * * @example * ```js * const example = new Choices(element); @@ -554,11 +596,14 @@ class Choices { * ``` */ setChoices( - choicesArrayOrFetcher = [], + choicesArrayOrFetcher: + | Choice[] + | Group[] + | ((instance: Choices) => Choice[] | Promise) = [], value = 'value', label = 'label', replaceChoices = false, - ) { + ): this | Promise { if (!this.initialised) { throw new ReferenceError( `setChoices was called on a non-initialized instance of Choices`, @@ -586,10 +631,12 @@ class Choices { if (typeof Promise === 'function' && fetcher instanceof Promise) { // that's a promise // eslint-disable-next-line compat/compat - return new Promise(resolve => requestAnimationFrame(resolve)) + return new Promise(resolve => requestAnimationFrame(resolve)) // eslint-disable-line compat/compat .then(() => this._handleLoadingState(true)) .then(() => fetcher) - .then(data => this.setChoices(data, value, label, replaceChoices)) + .then((data: Choice[]) => + this.setChoices(data, value, label, replaceChoices), + ) .catch(err => { if (!this.config.silent) { console.error(err); @@ -620,22 +667,28 @@ class Choices { this._startLoading(); - choicesArrayOrFetcher.forEach(groupOrChoice => { - if (groupOrChoice.choices) { + type ChoiceGroup = { + id: string; + choices: Choice[]; + }; + + choicesArrayOrFetcher.forEach((groupOrChoice: ChoiceGroup | Choice) => { + if ((groupOrChoice as ChoiceGroup).choices) { this._addGroup({ - id: parseInt(groupOrChoice.id, 10) || null, + id: groupOrChoice.id ? parseInt(`${groupOrChoice.id}`, 10) : null, group: groupOrChoice, valueKey: value, labelKey: label, }); } else { + const choice = groupOrChoice as Choice; this._addChoice({ - value: groupOrChoice[value], - label: groupOrChoice[label], - isSelected: groupOrChoice.selected, - isDisabled: groupOrChoice.disabled, - customProperties: groupOrChoice.customProperties, - placeholder: groupOrChoice.placeholder, + value: choice[value], + label: choice[label], + isSelected: !!choice.selected, + isDisabled: !!choice.disabled, + placeholder: !!choice.placeholder, + customProperties: choice.customProperties, }); } }); @@ -645,19 +698,19 @@ class Choices { return this; } - clearChoices() { + clearChoices(): this { this._store.dispatch(clearChoices()); return this; } - clearStore() { + clearStore(): this { this._store.dispatch(clearAll()); return this; } - clearInput() { + clearInput(): this { const shouldSetInputWidth = !this._isSelectOneElement; this.input.clear(shouldSetInputWidth); @@ -669,7 +722,7 @@ class Choices { return this; } - _render() { + _render(): void { if (this._store.isLoading()) { return; } @@ -699,7 +752,7 @@ class Choices { this._prevState = this._currentState; } - _renderChoices() { + _renderChoices(): void { const { activeGroups, activeChoices } = this._store; let choiceListFragment = document.createDocumentFragment(); @@ -748,8 +801,8 @@ class Choices { this.choiceList.append(choiceListFragment); this._highlightChoice(); } else { - // ...otherwise show a notice - this.choiceList.append(this._getTemplate('notice', canAddItem.notice)); + const notice = this._getTemplate('notice', canAddItem.notice); + this.choiceList.append(notice); } } else { // Otherwise show a notice @@ -776,7 +829,7 @@ class Choices { } } - _renderItems() { + _renderItems(): void { const activeItems = this._store.activeItems || []; this.itemList.clear(); @@ -791,11 +844,11 @@ class Choices { } _createGroupsFragment( - groups, - choices, - fragment = document.createDocumentFragment(), - ) { - const getGroupChoices = group => + groups: Group[], + choices: Choice[], + fragment: DocumentFragment = document.createDocumentFragment(), + ): DocumentFragment { + const getGroupChoices = (group): Choice[] => choices.filter(choice => { if (this._isSelectOneElement) { return choice.groupId === group.id; @@ -825,10 +878,10 @@ class Choices { } _createChoicesFragment( - choices, - fragment = document.createDocumentFragment(), + choices: Choice[], + fragment: DocumentFragment = document.createDocumentFragment(), withinGroup = false, - ) { + ): DocumentFragment { // Create a fragment to store our list items (so we don't have to update the DOM for each item) const { renderSelectedChoices, @@ -836,17 +889,19 @@ class Choices { renderChoiceLimit, } = this.config; const filter = this._isSearching ? sortByScore : this.config.sorter; - const appendChoice = choice => { + const appendChoice = (choice: Choice): void => { const shouldRender = renderSelectedChoices === 'auto' ? this._isSelectOneElement || !choice.selected : true; + if (shouldRender) { const dropdownItem = this._getTemplate( 'choice', choice, this.config.itemSelectText, ); + fragment.appendChild(dropdownItem); } }; @@ -859,7 +914,7 @@ class Choices { // Split array into placeholders and "normal" choices const { placeholderChoices, normalChoices } = rendererableChoices.reduce( - (acc, choice) => { + (acc, choice: Choice) => { if (choice.placeholder) { acc.placeholderChoices.push(choice); } else { @@ -868,7 +923,10 @@ class Choices { return acc; }, - { placeholderChoices: [], normalChoices: [] }, + { + placeholderChoices: [] as Choice[], + normalChoices: [] as Choice[], + }, ); // If sorting is enabled or the user is searching, filter choices @@ -899,7 +957,10 @@ class Choices { return fragment; } - _createItemsFragment(items, fragment = document.createDocumentFragment()) { + _createItemsFragment( + items: Item[], + fragment: DocumentFragment = document.createDocumentFragment(), + ): DocumentFragment { // Create fragment to add elements to const { shouldSortItems, sorter, removeItemButton } = this.config; @@ -910,13 +971,15 @@ class Choices { if (this._isTextElement) { // Update the value of the hidden input - this.passedElement.value = items; + this.passedElement.value = items + .map(({ value }) => value) + .join(this.config.delimiter); } else { // Update the options of the hidden input - this.passedElement.options = items; + (this.passedElement as WrappedSelect).options = items; } - const addItemToFragment = item => { + const addItemToFragment = (item: Item): void => { // Create new list element const listItem = this._getTemplate('item', item, removeItemButton); // Append it to list @@ -929,7 +992,7 @@ class Choices { return fragment; } - _triggerChange(value) { + _triggerChange(value): void { if (value === undefined || value === null) { return; } @@ -939,23 +1002,19 @@ class Choices { }); } - _selectPlaceholderChoice() { - const { placeholderChoice } = this._store; - - if (placeholderChoice) { - this._addItem({ - value: placeholderChoice.value, - label: placeholderChoice.label, - choiceId: placeholderChoice.id, - groupId: placeholderChoice.groupId, - placeholder: placeholderChoice.placeholder, - }); + _selectPlaceholderChoice(placeholderChoice: Choice): void { + this._addItem({ + value: placeholderChoice.value, + label: placeholderChoice.label, + choiceId: placeholderChoice.id, + groupId: placeholderChoice.groupId, + placeholder: placeholderChoice.placeholder, + }); - this._triggerChange(placeholderChoice.value); - } + this._triggerChange(placeholderChoice.value); } - _handleButtonAction(activeItems, element) { + _handleButtonAction(activeItems?: Item[], element?: HTMLElement): void { if ( !activeItems || !element || @@ -965,21 +1024,29 @@ class Choices { return; } - const itemId = element.parentNode.getAttribute('data-id'); - const itemToRemove = activeItems.find( - item => item.id === parseInt(itemId, 10), - ); + const itemId = + element.parentNode && (element.parentNode as HTMLElement).dataset.id; + const itemToRemove = + itemId && activeItems.find(item => item.id === parseInt(itemId, 10)); + + if (!itemToRemove) { + return; + } // Remove item associated with button this._removeItem(itemToRemove); this._triggerChange(itemToRemove.value); - if (this._isSelectOneElement) { - this._selectPlaceholderChoice(); + if (this._isSelectOneElement && this._store.placeholderChoice) { + this._selectPlaceholderChoice(this._store.placeholderChoice); } } - _handleItemAction(activeItems, element, hasShiftKey = false) { + _handleItemAction( + activeItems?: Item[], + element?: HTMLElement, + hasShiftKey = false, + ): void { if ( !activeItems || !element || @@ -989,13 +1056,13 @@ class Choices { return; } - const passedId = element.getAttribute('data-id'); + const passedId = element.dataset.id; // We only want to select one item with a click // so we deselect any items that aren't the target // unless shift is being pressed activeItems.forEach(item => { - if (item.id === parseInt(passedId, 10) && !item.highlighted) { + if (item.id === parseInt(`${passedId}`, 10) && !item.highlighted) { this.highlightItem(item); } else if (!hasShiftKey && item.highlighted) { this.unhighlightItem(item); @@ -1007,19 +1074,22 @@ class Choices { this.input.focus(); } - _handleChoiceAction(activeItems, element) { + _handleChoiceAction(activeItems?: Item[], element?: HTMLElement): void { if (!activeItems || !element) { return; } // If we are clicking on an option const { id } = element.dataset; - const choice = this._store.getChoiceById(id); + const choice = id && this._store.getChoiceById(id); if (!choice) { return; } + const passedKeyCode = - activeItems[0] && activeItems[0].keyCode ? activeItems[0].keyCode : null; + activeItems[0] && activeItems[0].keyCode + ? activeItems[0].keyCode + : undefined; const hasActiveDropdown = this.dropdown.isActive; // Update choice keyCode @@ -1056,7 +1126,7 @@ class Choices { } } - _handleBackspace(activeItems) { + _handleBackspace(activeItems?: Item[]): void { if (!this.config.removeItems || !activeItems) { return; } @@ -1080,15 +1150,15 @@ class Choices { } } - _startLoading() { + _startLoading(): void { this._store.dispatch(setIsLoading(true)); } - _stopLoading() { + _stopLoading(): void { this._store.dispatch(setIsLoading(false)); } - _handleLoadingState(setLoading = true) { + _handleLoadingState(setLoading = true): void { let placeholderItem = this.itemList.getChild( `.${this.config.classNames.placeholder}`, ); @@ -1103,7 +1173,10 @@ class Choices { 'placeholder', this.config.loadingText, ); - this.itemList.append(placeholderItem); + + if (placeholderItem) { + this.itemList.append(placeholderItem); + } } else { placeholderItem.innerHTML = this.config.loadingText; } @@ -1115,14 +1188,16 @@ class Choices { this.containerOuter.removeLoadingState(); if (this._isSelectOneElement) { - placeholderItem.innerHTML = this._placeholderValue || ''; + if (placeholderItem) { + placeholderItem.innerHTML = this._placeholderValue || ''; + } } else { this.input.placeholder = this._placeholderValue || ''; } } } - _handleSearch(value) { + _handleSearch(value: string): void { if (!value || !this.input.isFocussed) { return; } @@ -1146,7 +1221,7 @@ class Choices { } } - _canAddItem(activeItems, value) { + _canAddItem(activeItems: Item[], value: string): Notice { let canAddItem = true; let notice = typeof this.config.addItemText === 'function' @@ -1202,7 +1277,7 @@ class Choices { }; } - _searchChoices(value) { + _searchChoices(value: string): number { const newValue = typeof value === 'string' ? value.trim() : value; const currentValue = typeof this._currentValue === 'string' @@ -1217,9 +1292,12 @@ class Choices { const haystack = this._store.searchableChoices; const needle = newValue; const keys = [...this.config.searchFields]; - const options = Object.assign(this.config.fuseOptions, { keys }); + const options = Object.assign(this.config.fuseOptions, { + keys, + includeMatches: true, + }); const fuse = new Fuse(haystack, options); - const results = fuse.search(needle); + const results: Result[] = fuse.search(needle) as any[]; // see https://github.com/krisk/Fuse/issues/303 this._currentValue = newValue; this._highlightPosition = 0; @@ -1229,7 +1307,7 @@ class Choices { return results.length; } - _addEventListeners() { + _addEventListeners(): void { const { documentElement } = document; // capture events - can cancel event processing or propagation @@ -1283,7 +1361,7 @@ class Choices { this.input.addEventListeners(); } - _removeEventListeners() { + _removeEventListeners(): void { const { documentElement } = document; documentElement.removeEventListener('touchend', this._onTouchEnd, true); @@ -1318,10 +1396,7 @@ class Choices { this.input.removeEventListeners(); } - /** - * @param {KeyboardEvent} event - */ - _onKeyDown(event) { + _onKeyDown(event: KeyboardEvent): void { const { keyCode } = event; const { activeItems } = this._store; const hasFocusedInput = this.input.isFocussed; @@ -1355,31 +1430,29 @@ class Choices { } } - // Map keys to key actions - const keyDownActions = { - [A_KEY]: this._onAKey, - [ENTER_KEY]: this._onEnterKey, - [ESC_KEY]: this._onEscapeKey, - [UP_KEY]: this._onDirectionKey, - [PAGE_UP_KEY]: this._onDirectionKey, - [DOWN_KEY]: this._onDirectionKey, - [PAGE_DOWN_KEY]: this._onDirectionKey, - [DELETE_KEY]: this._onDeleteKey, - [BACK_KEY]: this._onDeleteKey, - }; - - if (keyDownActions[keyCode]) { - keyDownActions[keyCode]({ - event, - activeItems, - hasFocusedInput, - hasActiveDropdown, - hasItems, - }); - } - } - - _onKeyUp({ target, keyCode }) { + switch (keyCode) { + case A_KEY: + return this._onSelectKey(event, hasItems); + case ENTER_KEY: + return this._onEnterKey(event, activeItems, hasActiveDropdown); + case ESC_KEY: + return this._onEscapeKey(hasActiveDropdown); + case UP_KEY: + case PAGE_UP_KEY: + case DOWN_KEY: + case PAGE_DOWN_KEY: + return this._onDirectionKey(event, hasActiveDropdown); + case DELETE_KEY: + case BACK_KEY: + return this._onDeleteKey(event, activeItems, hasFocusedInput); + default: + } + } + + _onKeyUp({ + target, + keyCode, + }: Pick): void { const { value } = this.input; const { activeItems } = this._store; const canAddItem = this._canAddItem(activeItems, value); @@ -1399,7 +1472,8 @@ class Choices { } } else { const wasRemovalKeyCode = keyCode === backKey || keyCode === deleteKey; - const userHasRemovedValue = wasRemovalKeyCode && !target.value; + const userHasRemovedValue = + wasRemovalKeyCode && target && !(target as HTMLSelectElement).value; const canReactivateChoices = !this._isTextElement && this._isSearching; const canSearch = this._canSearch && canAddItem.response; @@ -1414,7 +1488,7 @@ class Choices { this._canSearch = this.config.searchEnabled; } - _onAKey({ event, hasItems }) { + _onSelectKey(event: KeyboardEvent, hasItems: boolean): void { const { ctrlKey, metaKey } = event; const hasCtrlDownKeyPressed = ctrlKey || metaKey; @@ -1433,12 +1507,17 @@ class Choices { } } - _onEnterKey({ event, activeItems, hasActiveDropdown }) { + _onEnterKey( + event: KeyboardEvent, + activeItems: Item[], + hasActiveDropdown: boolean, + ): void { const { target } = event; const { ENTER_KEY: enterKey } = KEY_CODES; - const targetWasButton = target.hasAttribute('data-button'); + const targetWasButton = + target && (target as HTMLElement).hasAttribute('data-button'); - if (this._isTextElement && target.value) { + if (this._isTextElement && target && (target as HTMLInputElement).value) { const { value } = this.input; const canAddItem = this._canAddItem(activeItems, value); @@ -1451,7 +1530,7 @@ class Choices { } if (targetWasButton) { - this._handleButtonAction(activeItems, target); + this._handleButtonAction(activeItems, target as HTMLElement); event.preventDefault(); } @@ -1475,14 +1554,14 @@ class Choices { } } - _onEscapeKey({ hasActiveDropdown }) { + _onEscapeKey(hasActiveDropdown: boolean): void { if (hasActiveDropdown) { this.hideDropdown(true); this.containerOuter.focus(); } } - _onDirectionKey({ event, hasActiveDropdown }) { + _onDirectionKey(event: KeyboardEvent, hasActiveDropdown: boolean): void { const { keyCode, metaKey } = event; const { DOWN_KEY: downKey, @@ -1546,25 +1625,33 @@ class Choices { } } - _onDeleteKey({ event, hasFocusedInput, activeItems }) { + _onDeleteKey( + event: KeyboardEvent, + activeItems: Item[], + hasFocusedInput: boolean, + ): void { const { target } = event; // If backspace or delete key is pressed and the input has no value - if (hasFocusedInput && !target.value && !this._isSelectOneElement) { + if ( + !this._isSelectOneElement && + !(target as HTMLInputElement).value && + hasFocusedInput + ) { this._handleBackspace(activeItems); event.preventDefault(); } } - _onTouchMove() { + _onTouchMove(): void { if (this._wasTap) { this._wasTap = false; } } - _onTouchEnd(event) { - const { target } = event || event.touches[0]; + _onTouchEnd(event: TouchEvent): void { + const { target } = event || (event as TouchEvent).touches[0]; const touchWasWithinContainer = - this._wasTap && this.containerOuter.element.contains(target); + this._wasTap && this.containerOuter.element.contains(target as Node); if (touchWasWithinContainer) { const containerWasExactTarget = @@ -1588,9 +1675,8 @@ class Choices { /** * Handles mousedown event in capture mode for containetOuter.element - * @param {MouseEvent} event */ - _onMouseDown(event) { + _onMouseDown(event: MouseEvent): void { const { target } = event; if (!(target instanceof HTMLElement)) { return; @@ -1599,8 +1685,9 @@ class Choices { // If we have our mouse down on the scrollbar and are on IE11... if (IS_IE11 && this.choiceList.element.contains(target)) { // check if click was on a scrollbar area - const firstChoice = /** @type {HTMLElement} */ (this.choiceList.element - .firstElementChild); + const firstChoice = this.choiceList.element + .firstElementChild as HTMLElement; + const isOnScrollbar = this._direction === 'ltr' ? event.offsetX >= firstChoice.offsetWidth @@ -1634,15 +1721,15 @@ class Choices { * Handles mouseover event over this.dropdown * @param {MouseEvent} event */ - _onMouseOver({ target }) { + _onMouseOver({ target }: Pick): void { if (target instanceof HTMLElement && 'choice' in target.dataset) { this._highlightChoice(target); } } - _onClick({ target }) { + _onClick({ target }: Pick): void { const clickWasWithinContainer = this.containerOuter.element.contains( - target, + target as Node, ); if (clickWasWithinContainer) { @@ -1658,7 +1745,7 @@ class Choices { } else if ( this._isSelectOneElement && target !== this.input.element && - !this.dropdown.element.contains(target) + !this.dropdown.element.contains(target as Node) ) { this.hideDropdown(); } @@ -1674,28 +1761,27 @@ class Choices { } } - _onFocus({ target }) { - const focusWasWithinContainer = this.containerOuter.element.contains( - target, - ); + _onFocus({ target }: Pick): void { + const focusWasWithinContainer = + target && this.containerOuter.element.contains(target as Node); if (!focusWasWithinContainer) { return; } const focusActions = { - [TEXT_TYPE]: () => { + [TEXT_TYPE]: (): void => { if (target === this.input.element) { this.containerOuter.addFocusState(); } }, - [SELECT_ONE_TYPE]: () => { + [SELECT_ONE_TYPE]: (): void => { this.containerOuter.addFocusState(); if (target === this.input.element) { this.showDropdown(true); } }, - [SELECT_MULTIPLE_TYPE]: () => { + [SELECT_MULTIPLE_TYPE]: (): void => { if (target === this.input.element) { this.showDropdown(true); // If element is a select box, the focused element is the container and the dropdown @@ -1708,14 +1794,15 @@ class Choices { focusActions[this.passedElement.element.type](); } - _onBlur({ target }) { - const blurWasWithinContainer = this.containerOuter.element.contains(target); + _onBlur({ target }: Pick): void { + const blurWasWithinContainer = + target && this.containerOuter.element.contains(target as Node); if (blurWasWithinContainer && !this._isScrollingOnIe) { const { activeItems } = this._store; const hasHighlightedItems = activeItems.some(item => item.highlighted); const blurActions = { - [TEXT_TYPE]: () => { + [TEXT_TYPE]: (): void => { if (target === this.input.element) { this.containerOuter.removeFocusState(); if (hasHighlightedItems) { @@ -1724,7 +1811,7 @@ class Choices { this.hideDropdown(true); } }, - [SELECT_ONE_TYPE]: () => { + [SELECT_ONE_TYPE]: (): void => { this.containerOuter.removeFocusState(); if ( target === this.input.element || @@ -1733,7 +1820,7 @@ class Choices { this.hideDropdown(true); } }, - [SELECT_MULTIPLE_TYPE]: () => { + [SELECT_MULTIPLE_TYPE]: (): void => { if (target === this.input.element) { this.containerOuter.removeFocusState(); this.hideDropdown(true); @@ -1754,12 +1841,12 @@ class Choices { } } - _onFormReset() { + _onFormReset(): void { this._store.dispatch(resetTo(this._initialState)); } - _highlightChoice(el = null) { - const choices = Array.from( + _highlightChoice(el: HTMLElement | null = null): void { + const choices: HTMLElement[] = Array.from( this.dropdown.element.querySelectorAll('[data-choice-selectable]'), ); @@ -1814,14 +1901,20 @@ class Choices { label = null, choiceId = -1, groupId = -1, - customProperties = null, + customProperties = {}, placeholder = false, - keyCode = null, - }) { + keyCode = -1, + }: { + value: string; + label?: string | null; + choiceId?: number; + groupId?: number; + customProperties?: object; + placeholder?: boolean; + keyCode?: number; + }): void { let passedValue = typeof value === 'string' ? value.trim() : value; - const passedKeyCode = keyCode; - const passedCustomProperties = customProperties; const { items } = this._store; const passedLabel = label || passedValue; const passedOptionId = choiceId || -1; @@ -1847,7 +1940,7 @@ class Choices { groupId, customProperties, placeholder, - keyCode: passedKeyCode, + keyCode, }), ); @@ -1860,18 +1953,20 @@ class Choices { id, value: passedValue, label: passedLabel, - customProperties: passedCustomProperties, + customProperties, groupValue: group && group.value ? group.value : null, - keyCode: passedKeyCode, + keyCode, }); } - /** - * @param {Item} item - */ - _removeItem(item) { + _removeItem(item: Item): void { const { id, value, label, customProperties, choiceId, groupId } = item; - const group = groupId >= 0 ? this._store.getGroupById(groupId) : null; + const group = + groupId && groupId >= 0 ? this._store.getGroupById(groupId) : null; + + if (!id || !choiceId) { + return; + } this._store.dispatch(removeItem(id, choiceId)); this.passedElement.triggerEvent(EVENTS.removeItem, { @@ -1889,10 +1984,19 @@ class Choices { isSelected = false, isDisabled = false, groupId = -1, - customProperties = null, + customProperties = {}, placeholder = false, - keyCode = null, - }) { + keyCode = -1, + }: { + value: string; + label?: string | null; + isSelected?: boolean; + isDisabled?: boolean; + groupId?: number; + customProperties?: Record; + placeholder?: boolean; + keyCode?: number; + }): void { if (typeof value === 'undefined' || value === null) { return; } @@ -1929,8 +2033,8 @@ class Choices { } } - _addGroup({ group, id, valueKey = 'value', labelKey = 'label' }) { - const groupChoices = isType('Object', group) + _addGroup({ group, id, valueKey = 'value', labelKey = 'label' }): void { + const groupChoices: Choice[] | HTMLOptionElement[] = isType('Object', group) ? group.choices : Array.from(group.getElementsByTagName('OPTION')); const groupId = id || Math.floor(new Date().valueOf() * Math.random()); @@ -1946,7 +2050,7 @@ class Choices { }), ); - const addGroupChoices = choice => { + const addGroupChoices = (choice: any): void => { const isOptDisabled = choice.disabled || (choice.parentNode && choice.parentNode.disabled); @@ -1974,17 +2078,13 @@ class Choices { } } - _getTemplate(template, ...args) { - if (!template) { - return null; - } - + _getTemplate(template: string, ...args: any): any { const { classNames } = this.config; return this._templates[template].call(this, classNames, ...args); } - _createTemplates() { + _createTemplates(): void { const { callbackOnCreateTemplates } = this.config; let userTemplates = {}; @@ -1995,10 +2095,10 @@ class Choices { userTemplates = callbackOnCreateTemplates.call(this, strToEl); } - this._templates = merge(TEMPLATES, userTemplates); + this._templates = merge(templates, userTemplates); } - _createElements() { + _createElements(): void { this.containerOuter = new Container({ element: this._getTemplate( 'containerOuter', @@ -2009,21 +2109,21 @@ class Choices { this.passedElement.element.type, ), classNames: this.config.classNames, - type: this.passedElement.element.type, + type: this.passedElement.element.type as PassedElement['type'], position: this.config.position, }); this.containerInner = new Container({ element: this._getTemplate('containerInner'), classNames: this.config.classNames, - type: this.passedElement.element.type, + type: this.passedElement.element.type as PassedElement['type'], position: this.config.position, }); this.input = new Input({ element: this._getTemplate('input', this._placeholderValue), classNames: this.config.classNames, - type: this.passedElement.element.type, + type: this.passedElement.element.type as PassedElement['type'], preventPaste: !this.config.paste, }); @@ -2038,11 +2138,11 @@ class Choices { this.dropdown = new Dropdown({ element: this._getTemplate('dropdown'), classNames: this.config.classNames, - type: this.passedElement.element.type, + type: this.passedElement.element.type as PassedElement['type'], }); } - _createStructure() { + _createStructure(): void { // Hide original element this.passedElement.conceal(); // Wrap input in container preserving DOM ordering @@ -2093,12 +2193,17 @@ class Choices { } } - _addPredefinedGroups(groups) { + _addPredefinedGroups( + groups: Group[] | HTMLOptGroupElement[] | Element[], + ): void { // If we have a placeholder option - const placeholderChoice = this.passedElement.placeholderOption; + const placeholderChoice = (this.passedElement as WrappedSelect) + .placeholderOption; + if ( placeholderChoice && - placeholderChoice.parentNode.tagName === 'SELECT' + placeholderChoice.parentNode && + (placeholderChoice.parentNode as HTMLElement).tagName === 'SELECT' ) { this._addChoice({ value: placeholderChoice.value, @@ -2117,7 +2222,7 @@ class Choices { ); } - _addPredefinedChoices(choices) { + _addPredefinedChoices(choices: Partial[]): void { // If sorting is enabled or the user is searching, filter choices if (this.config.shouldSort) { choices.sort(this.config.sorter); @@ -2129,7 +2234,7 @@ class Choices { ); choices.forEach((choice, index) => { - const { value, label, customProperties, placeholder } = choice; + const { value = '', label, customProperties, placeholder } = choice; if (this._isSelectElement) { // If the choice is actually a group @@ -2153,32 +2258,31 @@ class Choices { const isSelected = shouldPreselect ? true : choice.selected; const isDisabled = choice.disabled; + console.log(isDisabled, choice); + this._addChoice({ value, label, - isSelected, - isDisabled, + isSelected: !!isSelected, + isDisabled: !!isDisabled, + placeholder: !!placeholder, customProperties, - placeholder, }); } } else { this._addChoice({ value, label, - isSelected: choice.selected, - isDisabled: choice.disabled, + isSelected: !!choice.selected, + isDisabled: !!choice.disabled, + placeholder: !!choice.placeholder, customProperties, - placeholder, }); } }); } - /** - * @param {Item[]} items - */ - _addPredefinedItems(items) { + _addPredefinedItems(items: Item[] | string[]): void { items.forEach(item => { if (typeof item === 'object' && item.value) { this._addItem({ @@ -2198,10 +2302,10 @@ class Choices { }); } - _setChoiceOrItem(item) { + _setChoiceOrItem(item: any): void { const itemType = getType(item).toLowerCase(); const handleType = { - object: () => { + object: (): void => { if (!item.value) { return; } @@ -2227,7 +2331,7 @@ class Choices { }); } }, - string: () => { + string: (): void => { if (!this._isTextElement) { this._addChoice({ value: item, @@ -2246,11 +2350,11 @@ class Choices { handleType[itemType](); } - _findAndSelectChoiceByValue(val) { + _findAndSelectChoiceByValue(value: string): void { const { choices } = this._store; // Check 'value' property exists and the choice isn't already selected const foundChoice = choices.find(choice => - this.config.valueComparer(choice.value, val), + this.config.valueComparer(choice.value, value), ); if (foundChoice && !foundChoice.selected) { @@ -2266,11 +2370,14 @@ class Choices { } } - _generatePlaceholderValue() { - if (this._isSelectElement) { - const { placeholderOption } = this.passedElement; + _generatePlaceholderValue(): string | null { + if ( + this._isSelectElement && + (this.passedElement as WrappedSelect).placeholderOption + ) { + const { placeholderOption } = this.passedElement as WrappedSelect; - return placeholderOption ? placeholderOption.text : false; + return placeholderOption ? placeholderOption.text : null; } const { placeholder, placeholderValue } = this.config; @@ -2288,7 +2395,7 @@ class Choices { } } - return false; + return null; } } diff --git a/src/scripts/components/container.test.js b/src/scripts/components/container.test.ts similarity index 98% rename from src/scripts/components/container.test.js rename to src/scripts/components/container.test.ts index a10cb97c4..065576e1a 100644 --- a/src/scripts/components/container.test.js +++ b/src/scripts/components/container.test.ts @@ -13,7 +13,7 @@ describe('components/container', () => { document.body.appendChild(element); instance = new Container({ - element: document.getElementById('container'), + element: document.getElementById('container') as HTMLElement, classNames: DEFAULT_CLASSNAMES, position: 'auto', type: 'text', @@ -383,7 +383,7 @@ describe('components/container', () => { }); afterEach(() => { - document.getElementById('wrap-test').remove(); + document.getElementById('wrap-test')!.remove(); }); it('wraps passed element inside element', () => { @@ -406,7 +406,7 @@ describe('components/container', () => { }); afterEach(() => { - document.body.removeChild(document.getElementById('unwrap-test')); + document.body.removeChild(document.getElementById('unwrap-test') as Node); }); it('moves wrapped element outside of element', () => { diff --git a/src/scripts/components/container.js b/src/scripts/components/container.ts similarity index 70% rename from src/scripts/components/container.js rename to src/scripts/components/container.ts index 9867d6735..641361509 100644 --- a/src/scripts/components/container.js +++ b/src/scripts/components/container.ts @@ -1,20 +1,29 @@ import { wrap } from '../lib/utils'; import { SELECT_ONE_TYPE } from '../constants'; +import { PassedElement, ClassNames, Options } from '../interfaces'; -/** - * @typedef {import('../../../types/index').Choices.passedElement} passedElement - * @typedef {import('../../../types/index').Choices.ClassNames} ClassNames - */ export default class Container { - /** - * @param {{ - * element: HTMLElement, - * type: passedElement['type'], - * classNames: ClassNames, - * position - * }} args - */ - constructor({ element, type, classNames, position }) { + element: HTMLElement; + type: PassedElement['type']; + classNames: ClassNames; + position: Options['position']; + isOpen: boolean; + isFlipped: boolean; + isFocussed: boolean; + isDisabled: boolean; + isLoading: boolean; + + constructor({ + element, + type, + classNames, + position, + }: { + element: HTMLElement; + type: PassedElement['type']; + classNames: ClassNames; + position: Options['position']; + }) { this.element = element; this.classNames = classNames; this.type = type; @@ -28,12 +37,12 @@ export default class Container { this._onBlur = this._onBlur.bind(this); } - addEventListeners() { + addEventListeners(): void { this.element.addEventListener('focus', this._onFocus); this.element.addEventListener('blur', this._onBlur); } - removeEventListeners() { + removeEventListeners(): void { this.element.removeEventListener('focus', this._onFocus); this.element.removeEventListener('blur', this._onBlur); } @@ -41,10 +50,8 @@ export default class Container { /** * Determine whether container should be flipped based on passed * dropdown position - * @param {number} dropdownPos - * @returns {boolean} */ - shouldFlip(dropdownPos) { + shouldFlip(dropdownPos: number): boolean { if (typeof dropdownPos !== 'number') { return false; } @@ -62,21 +69,15 @@ export default class Container { return shouldFlip; } - /** - * @param {string} activeDescendantID - */ - setActiveDescendant(activeDescendantID) { + setActiveDescendant(activeDescendantID: string): void { this.element.setAttribute('aria-activedescendant', activeDescendantID); } - removeActiveDescendant() { + removeActiveDescendant(): void { this.element.removeAttribute('aria-activedescendant'); } - /** - * @param {number} dropdownPos - */ - open(dropdownPos) { + open(dropdownPos: number): void { this.element.classList.add(this.classNames.openState); this.element.setAttribute('aria-expanded', 'true'); this.isOpen = true; @@ -87,7 +88,7 @@ export default class Container { } } - close() { + close(): void { this.element.classList.remove(this.classNames.openState); this.element.setAttribute('aria-expanded', 'false'); this.removeActiveDescendant(); @@ -100,21 +101,21 @@ export default class Container { } } - focus() { + focus(): void { if (!this.isFocussed) { this.element.focus(); } } - addFocusState() { + addFocusState(): void { this.element.classList.add(this.classNames.focusState); } - removeFocusState() { + removeFocusState(): void { this.element.classList.remove(this.classNames.focusState); } - enable() { + enable(): void { this.element.classList.remove(this.classNames.disabledState); this.element.removeAttribute('aria-disabled'); if (this.type === SELECT_ONE_TYPE) { @@ -123,7 +124,7 @@ export default class Container { this.isDisabled = false; } - disable() { + disable(): void { this.element.classList.add(this.classNames.disabledState); this.element.setAttribute('aria-disabled', 'true'); if (this.type === SELECT_ONE_TYPE) { @@ -132,40 +133,36 @@ export default class Container { this.isDisabled = true; } - /** - * @param {HTMLElement} element - */ - wrap(element) { + wrap(element: HTMLSelectElement | HTMLInputElement | HTMLElement): void { wrap(element, this.element); } - /** - * @param {Element} element - */ - unwrap(element) { - // Move passed element outside this element - this.element.parentNode.insertBefore(element, this.element); - // Remove this element - this.element.parentNode.removeChild(this.element); + unwrap(element: HTMLElement): void { + if (this.element.parentNode) { + // Move passed element outside this element + this.element.parentNode.insertBefore(element, this.element); + // Remove this element + this.element.parentNode.removeChild(this.element); + } } - addLoadingState() { + addLoadingState(): void { this.element.classList.add(this.classNames.loadingState); this.element.setAttribute('aria-busy', 'true'); this.isLoading = true; } - removeLoadingState() { + removeLoadingState(): void { this.element.classList.remove(this.classNames.loadingState); this.element.removeAttribute('aria-busy'); this.isLoading = false; } - _onFocus() { + _onFocus(): void { this.isFocussed = true; } - _onBlur() { + _onBlur(): void { this.isFocussed = false; } } diff --git a/src/scripts/components/dropdown.test.js b/src/scripts/components/dropdown.test.ts similarity index 100% rename from src/scripts/components/dropdown.test.js rename to src/scripts/components/dropdown.test.ts diff --git a/src/scripts/components/dropdown.js b/src/scripts/components/dropdown.ts similarity index 55% rename from src/scripts/components/dropdown.js rename to src/scripts/components/dropdown.ts index 78d55f44f..ea188ae9b 100644 --- a/src/scripts/components/dropdown.js +++ b/src/scripts/components/dropdown.ts @@ -1,17 +1,20 @@ -/** - * @typedef {import('../../../types/index').Choices.passedElement} passedElement - * @typedef {import('../../../types/index').Choices.ClassNames} ClassNames - */ +import { PassedElement, ClassNames } from '../interfaces'; export default class Dropdown { - /** - * @param {{ - * element: HTMLElement, - * type: passedElement['type'], - * classNames: ClassNames, - * }} args - */ - constructor({ element, type, classNames }) { + element: HTMLElement; + type: PassedElement['type']; + classNames: ClassNames; + isActive: boolean; + + constructor({ + element, + type, + classNames, + }: { + element: HTMLElement; + type: PassedElement['type']; + classNames: ClassNames; + }) { this.element = element; this.classNames = classNames; this.type = type; @@ -20,26 +23,19 @@ export default class Dropdown { /** * Bottom position of dropdown in viewport coordinates - * @returns {number} Vertical position */ - get distanceFromTopWindow() { + get distanceFromTopWindow(): number { return this.element.getBoundingClientRect().bottom; } - /** - * Find element that matches passed selector - * @param {string} selector - * @returns {HTMLElement | null} - */ - getChild(selector) { + getChild(selector: string): HTMLElement | null { return this.element.querySelector(selector); } /** * Show dropdown to user by adding active state class - * @returns {this} */ - show() { + show(): this { this.element.classList.add(this.classNames.activeState); this.element.setAttribute('aria-expanded', 'true'); this.isActive = true; @@ -49,9 +45,8 @@ export default class Dropdown { /** * Hide dropdown from user - * @returns {this} */ - hide() { + hide(): this { this.element.classList.remove(this.classNames.activeState); this.element.setAttribute('aria-expanded', 'false'); this.isActive = false; diff --git a/src/scripts/components/index.js b/src/scripts/components/index.ts similarity index 100% rename from src/scripts/components/index.js rename to src/scripts/components/index.ts diff --git a/src/scripts/components/input.test.js b/src/scripts/components/input.test.ts similarity index 99% rename from src/scripts/components/input.test.js rename to src/scripts/components/input.test.ts index 3ea3d71a8..9f3f394b1 100644 --- a/src/scripts/components/input.test.js +++ b/src/scripts/components/input.test.ts @@ -13,7 +13,6 @@ describe('components/input', () => { element: choicesElement, type: 'text', classNames: DEFAULT_CLASSNAMES, - placeholderValue: null, preventPaste: false, }); }); @@ -49,7 +48,7 @@ describe('components/input', () => { expect(['input', 'paste', 'focus', 'blur']).to.have.members( Array.from( { length: addEventListenerStub.callCount }, - (v, i) => addEventListenerStub.getCall(i).args[0], + (_, i) => addEventListenerStub.getCall(i).args[0], ), ); }); diff --git a/src/scripts/components/input.js b/src/scripts/components/input.ts similarity index 60% rename from src/scripts/components/input.js rename to src/scripts/components/input.ts index 9f47ff2a5..8e44def8d 100644 --- a/src/scripts/components/input.js +++ b/src/scripts/components/input.ts @@ -1,27 +1,32 @@ import { sanitise } from '../lib/utils'; import { SELECT_ONE_TYPE } from '../constants'; - -/** - * @typedef {import('../../../types/index').Choices.passedElement} passedElement - * @typedef {import('../../../types/index').Choices.ClassNames} ClassNames - */ +import { PassedElement, ClassNames } from '../interfaces'; export default class Input { - /** - * @param {{ - * element: HTMLInputElement, - * type: passedElement['type'], - * classNames: ClassNames, - * preventPaste: boolean - * }} args - */ - constructor({ element, type, classNames, preventPaste }) { + element: HTMLInputElement; + type: PassedElement['type']; + classNames: ClassNames; + preventPaste: boolean; + isFocussed: boolean; + isDisabled: boolean; + + constructor({ + element, + type, + classNames, + preventPaste, + }: { + element: HTMLInputElement; + type: PassedElement['type']; + classNames: ClassNames; + preventPaste: boolean; + }) { this.element = element; this.type = type; this.classNames = classNames; this.preventPaste = preventPaste; - this.isFocussed = this.element === document.activeElement; + this.isFocussed = this.element.isEqualNode(document.activeElement); this.isDisabled = element.disabled; this._onPaste = this._onPaste.bind(this); this._onInput = this._onInput.bind(this); @@ -29,28 +34,19 @@ export default class Input { this._onBlur = this._onBlur.bind(this); } - /** - * @param {string} placeholder - */ - set placeholder(placeholder) { + set placeholder(placeholder: string) { this.element.placeholder = placeholder; } - /** - * @returns {string} - */ - get value() { + get value(): string { return sanitise(this.element.value); } - /** - * @param {string} value - */ - set value(value) { + set value(value: string) { this.element.value = value; } - addEventListeners() { + addEventListeners(): void { this.element.addEventListener('paste', this._onPaste); this.element.addEventListener('input', this._onInput, { passive: true, @@ -63,47 +59,36 @@ export default class Input { }); } - removeEventListeners() { - this.element.removeEventListener('input', this._onInput, { - passive: true, - }); + removeEventListeners(): void { + this.element.removeEventListener('input', this._onInput); this.element.removeEventListener('paste', this._onPaste); - this.element.removeEventListener('focus', this._onFocus, { - passive: true, - }); - this.element.removeEventListener('blur', this._onBlur, { - passive: true, - }); + this.element.removeEventListener('focus', this._onFocus); + this.element.removeEventListener('blur', this._onBlur); } - enable() { + enable(): void { this.element.removeAttribute('disabled'); this.isDisabled = false; } - disable() { + disable(): void { this.element.setAttribute('disabled', ''); this.isDisabled = true; } - focus() { + focus(): void { if (!this.isFocussed) { this.element.focus(); } } - blur() { + blur(): void { if (this.isFocussed) { this.element.blur(); } } - /** - * Set value of input to blank - * @param {boolean} setWidth - * @returns {this} - */ - clear(setWidth = true) { + clear(setWidth = true): this { if (this.element.value) { this.element.value = ''; } @@ -119,44 +104,38 @@ export default class Input { * Set the correct input width based on placeholder * value or input value */ - setWidth() { + setWidth(): void { // Resize input to contents or placeholder const { style, value, placeholder } = this.element; style.minWidth = `${placeholder.length + 1}ch`; style.width = `${value.length + 1}ch`; } - /** - * @param {string} activeDescendantID - */ - setActiveDescendant(activeDescendantID) { + setActiveDescendant(activeDescendantID: string): void { this.element.setAttribute('aria-activedescendant', activeDescendantID); } - removeActiveDescendant() { + removeActiveDescendant(): void { this.element.removeAttribute('aria-activedescendant'); } - _onInput() { + _onInput(): void { if (this.type !== SELECT_ONE_TYPE) { this.setWidth(); } } - /** - * @param {Event} event - */ - _onPaste(event) { + _onPaste(event: ClipboardEvent): void { if (this.preventPaste) { event.preventDefault(); } } - _onFocus() { + _onFocus(): void { this.isFocussed = true; } - _onBlur() { + _onBlur(): void { this.isFocussed = false; } } diff --git a/src/scripts/components/list.test.js b/src/scripts/components/list.test.ts similarity index 100% rename from src/scripts/components/list.test.js rename to src/scripts/components/list.test.ts diff --git a/src/scripts/components/list.js b/src/scripts/components/list.ts similarity index 67% rename from src/scripts/components/list.js rename to src/scripts/components/list.ts index 2474b92dc..26facb06c 100644 --- a/src/scripts/components/list.js +++ b/src/scripts/components/list.ts @@ -1,53 +1,37 @@ import { SCROLLING_SPEED } from '../constants'; -/** - * @typedef {import('../../../types/index').Choices.Choice} Choice - */ export default class List { - /** - * @param {{ element: HTMLElement }} args - */ - constructor({ element }) { + element: HTMLElement; + scrollPos: number; + height: number; + + constructor({ element }: { element: HTMLElement }) { this.element = element; this.scrollPos = this.element.scrollTop; this.height = this.element.offsetHeight; } - clear() { + clear(): void { this.element.innerHTML = ''; } - /** - * @param {Element | DocumentFragment} node - */ - append(node) { + append(node: Element | DocumentFragment): void { this.element.appendChild(node); } - /** - * @param {string} selector - * @returns {Element | null} - */ - getChild(selector) { + getChild(selector: string): HTMLElement | null { return this.element.querySelector(selector); } - /** - * @returns {boolean} - */ - hasChildren() { + hasChildren(): boolean { return this.element.hasChildNodes(); } - scrollToTop() { + scrollToTop(): void { this.element.scrollTop = 0; } - /** - * @param {Element} element - * @param {1 | -1} direction - */ - scrollToChildElement(element, direction) { + scrollToChildElement(element: HTMLElement, direction: 1 | -1): void { if (!element) { return; } @@ -71,35 +55,21 @@ export default class List { }); } - /** - * @param {number} scrollPos - * @param {number} strength - * @param {number} destination - */ - _scrollDown(scrollPos, strength, destination) { + _scrollDown(scrollPos: number, strength: number, destination: number): void { const easing = (destination - scrollPos) / strength; const distance = easing > 1 ? easing : 1; this.element.scrollTop = scrollPos + distance; } - /** - * @param {number} scrollPos - * @param {number} strength - * @param {number} destination - */ - _scrollUp(scrollPos, strength, destination) { + _scrollUp(scrollPos: number, strength: number, destination: number): void { const easing = (scrollPos - destination) / strength; const distance = easing > 1 ? easing : 1; this.element.scrollTop = scrollPos - distance; } - /** - * @param {*} destination - * @param {*} direction - */ - _animateScroll(destination, direction) { + _animateScroll(destination: number, direction: number): void { const strength = SCROLLING_SPEED; const choiceListScrollTop = this.element.scrollTop; let continueAnimation = false; diff --git a/src/scripts/components/wrapped-element.test.js b/src/scripts/components/wrapped-element.test.ts similarity index 100% rename from src/scripts/components/wrapped-element.test.js rename to src/scripts/components/wrapped-element.test.ts diff --git a/src/scripts/components/wrapped-element.js b/src/scripts/components/wrapped-element.ts similarity index 82% rename from src/scripts/components/wrapped-element.js rename to src/scripts/components/wrapped-element.ts index c42e3b811..17af9dcf8 100644 --- a/src/scripts/components/wrapped-element.js +++ b/src/scripts/components/wrapped-element.ts @@ -1,17 +1,11 @@ import { dispatchEvent } from '../lib/utils'; - -/** - * @typedef {import('../../../types/index').Choices.passedElement} passedElement - * @typedef {import('../../../types/index').Choices.ClassNames} ClassNames - */ +import { ClassNames, EventMap } from '../interfaces'; export default class WrappedElement { - /** - * @param {{ - * element: HTMLInputElement | HTMLSelectElement, - * classNames: ClassNames, - * }} args - */ + element: HTMLInputElement | HTMLSelectElement; + classNames: ClassNames; + isDisabled: boolean; + constructor({ element, classNames }) { this.element = element; this.classNames = classNames; @@ -26,24 +20,24 @@ export default class WrappedElement { this.isDisabled = false; } - get isActive() { + get isActive(): boolean { return this.element.dataset.choice === 'active'; } - get dir() { + get dir(): string { return this.element.dir; } - get value() { + get value(): string { return this.element.value; } - set value(value) { + set value(value: string) { // you must define setter here otherwise it will be readonly property this.element.value = value; } - conceal() { + conceal(): void { // Hide passed input this.element.classList.add(this.classNames.input); this.element.hidden = true; @@ -61,7 +55,7 @@ export default class WrappedElement { this.element.setAttribute('data-choice', 'active'); } - reveal() { + reveal(): void { // Reinstate passed element this.element.classList.remove(this.classNames.input); this.element.hidden = false; @@ -83,19 +77,19 @@ export default class WrappedElement { this.element.value = this.element.value; // eslint-disable-line no-self-assign } - enable() { + enable(): void { this.element.removeAttribute('disabled'); this.element.disabled = false; this.isDisabled = false; } - disable() { + disable(): void { this.element.setAttribute('disabled', ''); this.element.disabled = true; this.isDisabled = true; } - triggerEvent(eventType, data) { + triggerEvent(eventType: K, data?: object): void { dispatchEvent(this.element, eventType, data); } } diff --git a/src/scripts/components/wrapped-input.js b/src/scripts/components/wrapped-input.js deleted file mode 100644 index 8bf25457c..000000000 --- a/src/scripts/components/wrapped-input.js +++ /dev/null @@ -1,38 +0,0 @@ -import WrappedElement from './wrapped-element'; - -/** - * @typedef {import('../../../types/index').Choices.ClassNames} ClassNames - * @typedef {import('../../../types/index').Choices.Item} Item - */ - -export default class WrappedInput extends WrappedElement { - /** - * @param {{ - * element: HTMLInputElement, - * classNames: ClassNames, - * delimiter: string - * }} args - */ - constructor({ element, classNames, delimiter }) { - super({ element, classNames }); - this.delimiter = delimiter; - } - - /** - * @returns {string} - */ - get value() { - return this.element.value; - } - - /** - * @param {Item[]} items - */ - set value(items) { - const itemValues = items.map(({ value }) => value); - const joinedValues = itemValues.join(this.delimiter); - - this.element.setAttribute('value', joinedValues); - this.element.value = joinedValues; - } -} diff --git a/src/scripts/components/wrapped-input.test.js b/src/scripts/components/wrapped-input.test.ts similarity index 72% rename from src/scripts/components/wrapped-input.test.js rename to src/scripts/components/wrapped-input.test.ts index 48adf845d..36426f05d 100644 --- a/src/scripts/components/wrapped-input.test.js +++ b/src/scripts/components/wrapped-input.test.ts @@ -34,10 +34,12 @@ describe('components/wrappedInput', () => { }); describe('inherited methods', () => { - ['conceal', 'reveal', 'enable', 'disable'].forEach(method => { + const methods: string[] = ['conceal', 'reveal', 'enable', 'disable']; + + methods.forEach(method => { describe(method, () => { beforeEach(() => { - stub(WrappedElement.prototype, method); + stub(WrappedElement.prototype, method as keyof WrappedElement); }); afterEach(() => { @@ -54,27 +56,11 @@ describe('components/wrappedInput', () => { }); describe('value setter', () => { - const data = [ - { - id: 'ID 1', - value: 'Value 1', - }, - { - id: 'ID 2', - value: 'Value 2', - }, - { - id: 'ID 3', - value: 'Value 3', - }, - ]; - - it('sets delimited value of element based on passed data', () => { + it('sets the value of the input to the given value', () => { + const newValue = 'Value 1, Value 2, Value 3'; expect(instance.element.value).to.equal(''); - instance.value = data; - expect(instance.value).to.equal( - `Value 1${delimiter}Value 2${delimiter}Value 3`, - ); + instance.value = newValue; + expect(instance.value).to.equal(newValue); }); }); }); diff --git a/src/scripts/components/wrapped-input.ts b/src/scripts/components/wrapped-input.ts new file mode 100644 index 000000000..b5a1c9819 --- /dev/null +++ b/src/scripts/components/wrapped-input.ts @@ -0,0 +1,29 @@ +import WrappedElement from './wrapped-element'; +import { ClassNames } from '../interfaces'; + +export default class WrappedInput extends WrappedElement { + element: HTMLInputElement; + delimiter: string; + + constructor({ + element, + classNames, + delimiter, + }: { + element: HTMLInputElement; + classNames: ClassNames; + delimiter: string; + }) { + super({ element, classNames }); + this.delimiter = delimiter; + } + + get value(): string { + return this.element.value; + } + + set value(value: string) { + this.element.setAttribute('value', value); + this.element.value = value; + } +} diff --git a/src/scripts/components/wrapped-select.test.js b/src/scripts/components/wrapped-select.test.ts similarity index 95% rename from src/scripts/components/wrapped-select.test.js rename to src/scripts/components/wrapped-select.test.ts index 58efd3db2..4f723e6f5 100644 --- a/src/scripts/components/wrapped-select.test.js +++ b/src/scripts/components/wrapped-select.test.ts @@ -32,7 +32,7 @@ describe('components/wrappedSelect', () => { document.body.appendChild(element); instance = new WrappedSelect({ - element: document.getElementById('target'), + element: document.getElementById('target') as HTMLSelectElement, classNames: DEFAULT_CLASSNAMES, template: spy(Templates.option), }); @@ -54,9 +54,11 @@ describe('components/wrappedSelect', () => { }); describe('inherited methods', () => { - ['conceal', 'reveal', 'enable', 'disable'].forEach(method => { + const methods: string[] = ['conceal', 'reveal', 'enable', 'disable']; + + methods.forEach(method => { beforeEach(() => { - stub(WrappedElement.prototype, method); + stub(WrappedElement.prototype, method as keyof WrappedElement); }); afterEach(() => { diff --git a/src/scripts/components/wrapped-select.js b/src/scripts/components/wrapped-select.ts similarity index 56% rename from src/scripts/components/wrapped-select.js rename to src/scripts/components/wrapped-select.ts index 35671a16a..eb82eeb63 100644 --- a/src/scripts/components/wrapped-select.js +++ b/src/scripts/components/wrapped-select.ts @@ -1,26 +1,25 @@ import WrappedElement from './wrapped-element'; - -/** - * @typedef {import('../../../types/index').Choices.ClassNames} ClassNames - * @typedef {import('../../../types/index').Choices.Item} Item - * @typedef {import('../../../types/index').Choices.Choice} Choice - */ +import { ClassNames, Item } from '../interfaces'; export default class WrappedSelect extends WrappedElement { - /** - * @param {{ - * element: HTMLSelectElement, - * classNames: ClassNames, - * delimiter: string - * template: function - * }} args - */ - constructor({ element, classNames, template }) { + element: HTMLSelectElement; + classNames: ClassNames; + template: (data: object) => HTMLOptionElement; + + constructor({ + element, + classNames, + template, + }: { + element: HTMLSelectElement; + classNames: ClassNames; + template: (data: object) => HTMLOptionElement; + }) { super({ element, classNames }); this.template = template; } - get placeholderOption() { + get placeholderOption(): HTMLOptionElement | null { return ( this.element.querySelector('option[value=""]') || // Backward compatibility layer for the non-standard placeholder attribute supported in older versions. @@ -28,26 +27,17 @@ export default class WrappedSelect extends WrappedElement { ); } - /** - * @returns {Element[]} - */ - get optionGroups() { + get optionGroups(): Element[] { return Array.from(this.element.getElementsByTagName('OPTGROUP')); } - /** - * @returns {Item[] | Choice[]} - */ - get options() { + get options(): Item[] | HTMLOptionElement[] { return Array.from(this.element.options); } - /** - * @param {Item[] | Choice[]} options - */ - set options(options) { + set options(options: Item[] | HTMLOptionElement[]) { const fragment = document.createDocumentFragment(); - const addOptionToFragment = data => { + const addOptionToFragment = (data): void => { // Create a standard select option const option = this.template(data); // Append it to fragment @@ -60,10 +50,7 @@ export default class WrappedSelect extends WrappedElement { this.appendDocFragment(fragment); } - /** - * @param {DocumentFragment} fragment - */ - appendDocFragment(fragment) { + appendDocFragment(fragment: DocumentFragment): void { this.element.innerHTML = ''; this.element.appendChild(fragment); } diff --git a/src/scripts/constants.test.js b/src/scripts/constants.test.ts similarity index 98% rename from src/scripts/constants.test.js rename to src/scripts/constants.test.ts index 3579dcb81..f415bc65a 100644 --- a/src/scripts/constants.test.js +++ b/src/scripts/constants.test.ts @@ -104,6 +104,7 @@ describe('constants', () => { 'removeItem', 'highlightItem', 'highlightChoice', + 'unhighlightItem', ]); }); }); @@ -121,6 +122,8 @@ describe('constants', () => { 'REMOVE_ITEM', 'HIGHLIGHT_ITEM', 'CLEAR_ALL', + 'RESET_TO', + 'SET_IS_LOADING', ]); }); }); diff --git a/src/scripts/constants.js b/src/scripts/constants.ts similarity index 81% rename from src/scripts/constants.js rename to src/scripts/constants.ts index 5718c1c3f..e8338077e 100644 --- a/src/scripts/constants.js +++ b/src/scripts/constants.ts @@ -1,12 +1,13 @@ import { sanitise, sortByAlpha } from './lib/utils'; +import { + Options, + ClassNames, + EventMap, + ActionType, + KeyCodeMap, +} from './interfaces'; -/** - * @typedef {import('../../types/index').Choices.ClassNames} ClassNames - * @typedef {import('../../types/index').Choices.Options} Options - */ - -/** @type {ClassNames} */ -export const DEFAULT_CLASSNAMES = { +export const DEFAULT_CLASSNAMES: ClassNames = { containerOuter: 'choices', containerInner: 'choices__inner', input: 'choices__input', @@ -35,8 +36,7 @@ export const DEFAULT_CLASSNAMES = { noChoices: 'has-no-choices', }; -/** @type {Options} */ -export const DEFAULT_CONFIG = { +export const DEFAULT_CONFIG: Options = { items: [], choices: [], silent: false, @@ -83,7 +83,7 @@ export const DEFAULT_CONFIG = { classNames: DEFAULT_CLASSNAMES, }; -export const EVENTS = { +export const EVENTS: Record = { showDropdown: 'showDropdown', hideDropdown: 'hideDropdown', change: 'change', @@ -93,9 +93,10 @@ export const EVENTS = { removeItem: 'removeItem', highlightItem: 'highlightItem', highlightChoice: 'highlightChoice', + unhighlightItem: 'unhighlightItem', }; -export const ACTION_TYPES = { +export const ACTION_TYPES: Record = { ADD_CHOICE: 'ADD_CHOICE', FILTER_CHOICES: 'FILTER_CHOICES', ACTIVATE_CHOICES: 'ACTIVATE_CHOICES', @@ -105,9 +106,11 @@ export const ACTION_TYPES = { REMOVE_ITEM: 'REMOVE_ITEM', HIGHLIGHT_ITEM: 'HIGHLIGHT_ITEM', CLEAR_ALL: 'CLEAR_ALL', + RESET_TO: 'RESET_TO', + SET_IS_LOADING: 'SET_IS_LOADING', }; -export const KEY_CODES = { +export const KEY_CODES: KeyCodeMap = { BACK_KEY: 46, DELETE_KEY: 8, ENTER_KEY: 13, @@ -119,8 +122,9 @@ export const KEY_CODES = { PAGE_DOWN_KEY: 34, }; -export const TEXT_TYPE = 'text'; -export const SELECT_ONE_TYPE = 'select-one'; -export const SELECT_MULTIPLE_TYPE = 'select-multiple'; +export const TEXT_TYPE: HTMLInputElement['type'] = 'text'; +export const SELECT_ONE_TYPE: HTMLSelectElement['type'] = 'select-one'; +export const SELECT_MULTIPLE_TYPE: HTMLSelectElement['type'] = + 'select-multiple'; export const SCROLLING_SPEED = 4; diff --git a/src/scripts/interfaces.ts b/src/scripts/interfaces.ts new file mode 100644 index 000000000..9d0236823 --- /dev/null +++ b/src/scripts/interfaces.ts @@ -0,0 +1,754 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { FuseOptions } from 'fuse.js'; +import Choices from './choices'; + +export namespace Types { + export type strToEl = ( + str: string, + ) => HTMLElement | HTMLInputElement | HTMLOptionElement; + export type stringFunction = () => string; + export type noticeStringFunction = (value: string) => string; + export type noticeLimitFunction = (maxItemCount: number) => string; + export type filterFunction = (value: string) => boolean; + export type valueCompareFunction = ( + value1: string, + value2: string, + ) => boolean; +} + +export interface Choice { + id?: number; + customProperties?: Record; + disabled?: boolean; + active?: boolean; + elementId?: number; + groupId?: number; + keyCode?: number; + label: string; + placeholder?: boolean; + selected?: boolean; + value: string; + score?: number; + choices?: Choice[]; +} + +export interface Group { + id?: number; + active?: boolean; + disabled?: boolean; + value: any; +} +export interface Item extends Choice { + choiceId?: number; + highlighted?: boolean; +} + +/** + * Events fired by Choices behave the same as standard events. Each event is triggered on the element passed to Choices (accessible via `this.passedElement`. Arguments are accessible within the `event.detail` object. + */ +export interface EventMap { + /** + * Triggered each time an item is added (programmatically or by the user). + * + * **Input types affected:** text, select-one, select-multiple + * + * Arguments: id, value, label, groupValue, keyCode + */ + addItem: CustomEvent<{ + id: number; + value: string; + label: string; + groupValue: string; + keyCode: number; + }>; + + /** + * Triggered each time an item is removed (programmatically or by the user). + * + * **Input types affected:** text, select-one, select-multiple + * + * Arguments: id, value, label, groupValue + */ + removeItem: CustomEvent<{ + id: number; + value: string; + label: string; + groupValue: string; + }>; + + /** + * Triggered each time an item is highlighted. + * + * **Input types affected:** text, select-multiple + * + * Arguments: id, value, label, groupValue + */ + highlightItem: CustomEvent<{ + id: number; + value: string; + label: string; + groupValue: string; + }>; + + /** + * Triggered each time an item is unhighlighted. + * + * **Input types affected:** text, select-multiple + * + * Arguments: id, value, label, groupValue + */ + unhighlightItem: CustomEvent<{ + id: number; + value: string; + label: string; + groupValue: string; + }>; + + /** + * Triggered each time a choice is selected **by a user**, regardless if it changes the value of the input. + * + * **Input types affected:** select-one, select-multiple + * + * Arguments: choice: Choice + */ + choice: CustomEvent<{ choice: Choice }>; + + /** + * Triggered each time an item is added/removed **by a user**. + * + * **Input types affected:** text, select-one, select-multiple + * + * Arguments: value + */ + change: CustomEvent<{ value: string }>; + + /** + * Triggered when a user types into an input to search choices. + * + * **Input types affected:** select-one, select-multiple + * + * Arguments: value, resultCount + */ + search: CustomEvent<{ value: string; resultCount: number }>; + + /** + * Triggered when the dropdown is shown. + * + * **Input types affected:** select-one, select-multiple + * + * Arguments: - + */ + showDropdown: CustomEvent; + + /** + * Triggered when the dropdown is hidden. + * + * **Input types affected:** select-one, select-multiple + * + * Arguments: - + */ + hideDropdown: CustomEvent; + + /** + * Triggered when a choice from the dropdown is highlighted. + * + * Input types affected: select-one, select-multiple + * Arguments: el is the choice.passedElement that was affected. + */ + highlightChoice: CustomEvent<{ el: PassedElement }>; +} + +export interface KeyCodeMap { + BACK_KEY: 46; + DELETE_KEY: 8; + ENTER_KEY: 13; + A_KEY: 65; + ESC_KEY: 27; + UP_KEY: 38; + DOWN_KEY: 40; + PAGE_UP_KEY: 33; + PAGE_DOWN_KEY: 34; +} + +export type ActionType = + | 'ADD_CHOICE' + | 'FILTER_CHOICES' + | 'ACTIVATE_CHOICES' + | 'CLEAR_CHOICES' + | 'ADD_GROUP' + | 'ADD_ITEM' + | 'REMOVE_ITEM' + | 'HIGHLIGHT_ITEM' + | 'CLEAR_ALL' + | 'RESET_TO' + | 'SET_IS_LOADING'; + +/** Classes added to HTML generated by By default classnames follow the BEM notation. */ +export interface ClassNames { + /** @default 'choices' */ + containerOuter: string; + /** @default 'choices__inner' */ + containerInner: string; + /** @default 'choices__input' */ + input: string; + /** @default 'choices__input--cloned' */ + inputCloned: string; + /** @default 'choices__list' */ + list: string; + /** @default 'choices__list--multiple' */ + listItems: string; + /** @default 'choices__list--single' */ + listSingle: string; + /** @default 'choices__list--dropdown' */ + listDropdown: string; + /** @default 'choices__item' */ + item: string; + /** @default 'choices__item--selectable' */ + itemSelectable: string; + /** @default 'choices__item--disabled' */ + itemDisabled: string; + /** @default 'choices__item--choice' */ + itemChoice: string; + /** @default 'choices__placeholder' */ + placeholder: string; + /** @default 'choices__group' */ + group: string; + /** @default 'choices__heading' */ + groupHeading: string; + /** @default 'choices__button' */ + button: string; + /** @default 'is-active' */ + activeState: string; + /** @default 'is-focused' */ + focusState: string; + /** @default 'is-open' */ + openState: string; + /** @default 'is-disabled' */ + disabledState: string; + /** @default 'is-highlighted' */ + highlightedState: string; + /** @default 'is-selected' */ + selectedState: string; + /** @default 'is-flipped' */ + flippedState: string; + /** @default 'is-loading' */ + loadingState: string; + /** @default 'has-no-results' */ + noResults: string; + /** @default 'has-no-choices' */ + noChoices: string; +} + +export interface PassedElement extends HTMLElement { + classNames: ClassNames; + element: (HTMLInputElement | HTMLSelectElement) & { + // Extends HTMLElement addEventListener with Choices events + addEventListener( + type: K, + listener: ( + this: HTMLInputElement | HTMLSelectElement, + ev: EventMap[K], + ) => void, + options?: boolean | AddEventListenerOptions, + ): void; + }; + type: 'text' | 'select-one' | 'select-multiple'; + isDisabled: boolean; + parentInstance: Choices; +} + +/** + * Choices options interface + * + * **Terminology** + * + * - **Choice:** A choice is a value a user can select. A choice would be equivalent to the `` element within a select input. + * - **Group:** A group is a collection of choices. A group should be seen as equivalent to a `` element within a select input. + * - **Item:** An item is an inputted value **_(text input)_** or a selected choice **_(select element)_**. In the context of a select element, an item is equivelent to a selected option element: `` whereas in the context of a text input an item is equivelant to `` + */ +export interface Options { + /** + * Optionally suppress console errors and warnings. + * + * **Input types affected:** text, select-single, select-multiple + * + * @default false + */ + silent: boolean; + + /** + * Add pre-selected items (see terminology) to text input. + * + * **Input types affected:** text + * + * @example + * ``` + * ['value 1', 'value 2', 'value 3'] + * ``` + * + * @example + * ``` + * [{ + * value: 'Value 1', + * label: 'Label 1', + * id: 1 + * }, + * { + * value: 'Value 2', + * label: 'Label 2', + * id: 2, + * customProperties: { + * random: 'I am a custom property' + * } + * }] + * ``` + * + * @default [] + */ + items: string[] | Choice[]; + + /** + * Add choices (see terminology) to select input. + * + * **Input types affected:** select-one, select-multiple + * + * @example + * ``` + * [{ + * value: 'Option 1', + * label: 'Option 1', + * selected: true, + * disabled: false, + * }, + * { + * value: 'Option 2', + * label: 'Option 2', + * selected: false, + * disabled: true, + * customProperties: { + * description: 'Custom description about Option 2', + * random: 'Another random custom property' + * }, + * }] + * ``` + * + * @default [] + */ + choices: Choice[]; + + /** + * The amount of choices to be rendered within the dropdown list `("-1" indicates no limit)`. This is useful if you have a lot of choices where it is easier for a user to use the search area to find a choice. + * + * **Input types affected:** select-one, select-multiple + * + * @default -1 + */ + renderChoiceLimit: number; + + /** + * The amount of items a user can input/select `("-1" indicates no limit)`. + * + * **Input types affected:** text, select-multiple + * + * @default -1 + */ + maxItemCount: number; + + /** + * Whether a user can add items. + * + * **Input types affected:** text + * + * @default true + */ + addItems: boolean; + + /** + * A filter that will need to pass for a user to successfully add an item. + * + * **Input types affected:** text + * + * @default null + */ + addItemFilter: string | RegExp | Types.filterFunction | null; + + /** + * The text that is shown when a user has inputted a new item but has not pressed the enter key. To access the current input value, pass a function with a `value` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string. + * + * **Input types affected:** text + * + * @default + * ``` + * (value) => `Press Enter to add "${value}"`; + * ``` + */ + addItemText: string | Types.noticeStringFunction; + + /** + * Whether a user can remove items. + * + * **Input types affected:** text, select-multiple + * + * @default true + */ + removeItems: boolean; + + /** + * Whether each item should have a remove button. + * + * **Input types affected:** text, select-one, select-multiple + * + * @default false + */ + removeItemButton: boolean; + + /** + * Whether a user can edit items. An item's value can be edited by pressing the backspace. + * + * **Input types affected:** text + * + * @default false + */ + editItems: boolean; + + /** + * Whether each inputted/chosen item should be unique. + * + * **Input types affected:** text, select-multiple + * + * @default true + */ + duplicateItemsAllowed: boolean; + + /** + * What divides each value. The default delimiter separates each value with a comma: `"Value 1, Value 2, Value 3"`. + * + * **Input types affected:** text + * + * @default ',' + */ + delimiter: string; + + /** + * Whether a user can paste into the input. + * + * **Input types affected:** text, select-multiple + * + * @default true + */ + paste: boolean; + + /** + * Whether a search area should be shown. + * + * @note Multiple select boxes will always show search areas. + * + * **Input types affected:** select-one + * + * @default true + */ + searchEnabled: boolean; + + /** + * Whether choices should be filtered by input or not. If `false`, the search event will still emit, but choices will not be filtered. + * + * **Input types affected:** select-one + * + * @default true + */ + searchChoices: boolean; + + /** + * The minimum length a search value should be before choices are searched. + * + * **Input types affected:** select-one, select-multiple + * + * @default 1 + */ + searchFloor: number; + + /** + * The maximum amount of search results to show. + * + * **Input types affected:** select-one, select-multiple + * + * @default 4 + */ + searchResultLimit: number; + + /** + * Specify which fields should be used when a user is searching. If you have added custom properties to your choices, you can add these values thus: `['label', 'value', 'customProperties.example']`. + * + * Input types affected:select-one, select-multiple + * + * @default ['label', 'value'] + */ + searchFields: string[]; + + /** + * Whether the dropdown should appear above `(top)` or below `(bottom)` the input. By default, if there is not enough space within the window the dropdown will appear above the input, otherwise below it. + * + * **Input types affected:** select-one, select-multiple + * + * @default 'auto' + */ + position: 'auto' | 'top' | 'bottom'; + + /** + * Whether the scroll position should reset after adding an item. + * + * **Input types affected:** select-multiple + * + * @default true + */ + resetScrollPosition: boolean; + + /** + * Whether choices and groups should be sorted. If false, choices/groups will appear in the order they were given. + * + * **Input types affected:** select-one, select-multiple + * + * @default true + */ + shouldSort: boolean; + + /** + * Whether items should be sorted. If false, items will appear in the order they were selected. + * + * **Input types affected:** text, select-multiple + * + * @default false + */ + shouldSortItems: boolean; + + /** + * The function that will sort choices and items before they are displayed (unless a user is searching). By default choices and items are sorted by alphabetical order. + * + * **Input types affected:** select-one, select-multiple + * + * @example + * ``` + * // Sorting via length of label from largest to smallest + * const example = new Choices(element, { + * sorter: function(a, b) { + * return b.label.length - a.label.length; + * }, + * }; + * ``` + * + * @default sortByAlpha + */ + sorter: (current: Choice, next: Choice) => number; + + /** + * Whether the input should show a placeholder. Used in conjunction with `placeholderValue`. If `placeholder` is set to true and no value is passed to `placeholderValue`, the passed input's placeholder attribute will be used as the placeholder value. + * + * **Input types affected:** text, select-multiple + * + * @note For single select boxes, the recommended way of adding a placeholder is as follows: + * ``` + * + * ``` + * + * @default true + */ + placeholder: boolean; + + /** + * The value of the inputs placeholder. + * + * **Input types affected:** text, select-multiple + * + * @default null + */ + placeholderValue: string | null; + + /** + * The value of the search inputs placeholder. + * + * **Input types affected:** select-one + * + * @default null + */ + searchPlaceholderValue: string | null; + + /** + * Prepend a value to each item added/selected. + * + * **Input types affected:** text, select-one, select-multiple + * + * @default null + */ + prependValue: string | null; + + /** + * Append a value to each item added/selected. + * + * **Input types affected:** text, select-one, select-multiple + * + * @default null + */ + appendValue: string | null; + + /** + * Whether selected choices should be removed from the list. By default choices are removed when they are selected in multiple select box. To always render choices pass `always`. + * + * **Input types affected:** select-one, select-multiple + * + * @default 'auto'; + */ + renderSelectedChoices: 'auto' | 'always'; + + /** + * The text that is shown whilst choices are being populated via AJAX. + * + * **Input types affected:** select-one, select-multiple + * + * @default 'Loading...' + */ + loadingText: string; + + /** + * The text that is shown when a user's search has returned no results. Optionally pass a function returning a string. + * + * **Input types affected:** select-one, select-multiple + * + * @default 'No results found' + */ + noResultsText: string | Types.stringFunction; + + /** + * The text that is shown when a user has selected all possible choices. Optionally pass a function returning a string. + * + * **Input types affected:** select-multiple + * + * @default 'No choices to choose from' + */ + noChoicesText: string | Types.stringFunction; + + /** + * The text that is shown when a user hovers over a selectable choice. + * + * **Input types affected:** select-multiple, select-one + * + * @default 'Press to select' + */ + itemSelectText: string; + + /** + * The text that is shown when a user has focus on the input but has already reached the **max item count** [https://github.com/jshjohnson/Choices#maxitemcount]. To access the max item count, pass a function with a `maxItemCount` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string. + * + * **Input types affected:** text + * + * @default + * ``` + * (maxItemCount) => `Only ${maxItemCount} values can be added.`; + * ``` + */ + maxItemText: string | Types.noticeLimitFunction; + + /** + * If no duplicates are allowed, and the value already exists in the array. + * + * @default 'Only unique values can be added' + */ + uniqueItemText: string | Types.noticeStringFunction; + + /** + * The text that is shown when addItemFilter is passed and it returns false + * + * **Input types affected:** text + * + * @default 'Only values matching specific conditions can be added' + */ + customAddItemText: string | Types.noticeStringFunction; + + /** + * Compare choice and value in appropriate way (e.g. deep equality for objects). To compare choice and value, pass a function with a `valueComparer` argument (see the [default config](https://github.com/jshjohnson/Choices#setup) for an example). + * + * **Input types affected:** select-one, select-multiple + * + * @default + * ``` + * (choice, item) => choice === item; + * ``` + */ + valueComparer: Types.valueCompareFunction; + + /** + * Classes added to HTML generated by By default classnames follow the BEM notation. + * + * **Input types affected:** text, select-one, select-multiple + */ + classNames: ClassNames; + + /** + * Choices uses the great Fuse library for searching. You can find more options here: https://github.com/krisk/Fuse#options + */ + fuseOptions: FuseOptions; + + /** + * Function to run once Choices initialises. + * + * **Input types affected:** text, select-one, select-multiple + * + * @note For each callback, this refers to the current instance of This can be useful if you need access to methods `(this.disable())` or the config object `(this.config)`. + * + * @default null + */ + callbackOnInit: ((this: Choices) => void) | null; + + /** + * Function to run on template creation. Through this callback it is possible to provide custom templates for the various components of Choices (see terminology). For Choices to work with custom templates, it is important you maintain the various data attributes defined here [https://github.com/jshjohnson/Choices/blob/67f29c286aa21d88847adfcd6304dc7d068dc01f/assets/scripts/src/choices.js#L1993-L2067]. + * + * **Input types affected:** text, select-one, select-multiple + * + * @note For each callback, this refers to the current instance of This can be useful if you need access to methods `(this.disable())` or the config object `(this.config)`. + * + * @example + * ``` + * const example = new Choices(element, { + * callbackOnCreateTemplates: function (template) { + * var classNames = this.config.classNames; + * return { + * item: (data) => { + * return template(` + *

+ * ${data.label} + *
+ * `); + * }, + * choice: (data) => { + * return template(` + *
0 ? 'role="treeitem"' : 'role="option"'}> + * ${data.label} + *
+ * `); + * }, + * }; + * } + * }); + * ``` + * + * @default null + */ + callbackOnCreateTemplates: ((template: Types.strToEl) => void) | null; +} + +// @todo rename +export interface Notice { + response: boolean; + notice: string; +} + +export interface State { + choices: Choice[]; + groups: Group[]; + items: Item[]; + loading: boolean; +} diff --git a/src/scripts/lib/utils.js b/src/scripts/lib/utils.js deleted file mode 100644 index 8cbd576c6..000000000 --- a/src/scripts/lib/utils.js +++ /dev/null @@ -1,214 +0,0 @@ -/** - * @param {number} min - * @param {number} max - * @returns {number} - */ -export const getRandomNumber = (min, max) => - Math.floor(Math.random() * (max - min) + min); - -/** - * @param {number} length - * @returns {string} - */ -export const generateChars = length => - Array.from({ length }, () => getRandomNumber(0, 36).toString(36)).join(''); - -/** - * @param {HTMLInputElement | HTMLSelectElement} element - * @param {string} prefix - * @returns {string} - */ -export const generateId = (element, prefix) => { - let id = - element.id || - (element.name && `${element.name}-${generateChars(2)}`) || - generateChars(4); - id = id.replace(/(:|\.|\[|\]|,)/g, ''); - id = `${prefix}-${id}`; - - return id; -}; - -/** - * @param {any} obj - * @returns {string} - */ -export const getType = obj => Object.prototype.toString.call(obj).slice(8, -1); - -/** - * @param {string} type - * @param {any} obj - * @returns {boolean} - */ -export const isType = (type, obj) => - obj !== undefined && obj !== null && getType(obj) === type; - -/** - * @param {HTMLElement} element - * @param {HTMLElement} [wrapper={HTMLDivElement}] - * @returns {HTMLElement} - */ -export const wrap = (element, wrapper = document.createElement('div')) => { - if (element.nextSibling) { - element.parentNode.insertBefore(wrapper, element.nextSibling); - } else { - element.parentNode.appendChild(wrapper); - } - - return wrapper.appendChild(element); -}; - -/** - * @param {Element} startEl - * @param {string} selector - * @param {1 | -1} direction - * @returns {Element | undefined} - */ -export const getAdjacentEl = (startEl, selector, direction = 1) => { - if (!(startEl instanceof Element) || typeof selector !== 'string') { - return undefined; - } - - const prop = `${direction > 0 ? 'next' : 'previous'}ElementSibling`; - - let sibling = startEl[prop]; - while (sibling) { - if (sibling.matches(selector)) { - return sibling; - } - sibling = sibling[prop]; - } - - return sibling; -}; - -/** - * @param {Element} element - * @param {Element} parent - * @param {-1 | 1} direction - * @returns {boolean} - */ -export const isScrolledIntoView = (element, parent, direction = 1) => { - if (!element) { - return false; - } - - let isVisible; - - if (direction > 0) { - // In view from bottom - isVisible = - parent.scrollTop + parent.offsetHeight >= - element.offsetTop + element.offsetHeight; - } else { - // In view from top - isVisible = element.offsetTop >= parent.scrollTop; - } - - return isVisible; -}; - -/** - * @param {any} value - * @returns {any} - */ -export const sanitise = value => { - if (typeof value !== 'string') { - return value; - } - - return value - .replace(/&/g, '&') - .replace(/>/g, '&rt;') - .replace(/ (str: string) => Element} - */ -export const strToEl = (() => { - const tmpEl = document.createElement('div'); - - return str => { - const cleanedInput = str.trim(); - tmpEl.innerHTML = cleanedInput; - const firldChild = tmpEl.children[0]; - - while (tmpEl.firstChild) { - tmpEl.removeChild(tmpEl.firstChild); - } - - return firldChild; - }; -})(); - -/** - * @param {{ label?: string, value: string }} a - * @param {{ label?: string, value: string }} b - * @returns {number} - */ -export const sortByAlpha = ( - { value, label = value }, - { value: value2, label: label2 = value2 }, -) => - label.localeCompare(label2, [], { - sensitivity: 'base', - ignorePunctuation: true, - numeric: true, - }); - -/** - * @param {{ score: number }} a - * @param {{ score: number }} b - */ -export const sortByScore = (a, b) => a.score - b.score; - -/** - * @param {HTMLElement} element - * @param {string} type - * @param {object} customArgs - */ -export const dispatchEvent = (element, type, customArgs = null) => { - const event = new CustomEvent(type, { - detail: customArgs, - bubbles: true, - cancelable: true, - }); - - return element.dispatchEvent(event); -}; - -/** - * @param {array} array - * @param {any} value - * @param {string} [key="value"] - * @returns {boolean} - */ -export const existsInArray = (array, value, key = 'value') => - array.some(item => { - if (typeof value === 'string') { - return item[key] === value.trim(); - } - - return item[key] === value; - }); - -/** - * @param {any} obj - * @returns {any} - */ -export const cloneObject = obj => JSON.parse(JSON.stringify(obj)); - -/** - * Returns an array of keys present on the first but missing on the second object - * @param {object} a - * @param {object} b - * @returns {string[]} - */ -export const diff = (a, b) => { - const aKeys = Object.keys(a).sort(); - const bKeys = Object.keys(b).sort(); - - return aKeys.filter(i => bKeys.indexOf(i) < 0); -}; diff --git a/src/scripts/lib/utils.test.js b/src/scripts/lib/utils.test.ts similarity index 92% rename from src/scripts/lib/utils.test.js rename to src/scripts/lib/utils.test.ts index 2366f5635..3cac1a122 100644 --- a/src/scripts/lib/utils.test.js +++ b/src/scripts/lib/utils.test.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-new-wrappers */ import { expect } from 'chai'; import { stub } from 'sinon'; import { @@ -140,19 +141,19 @@ describe('utils', () => { it('sorts by label alphabetically', () => { const values = [ - { label: 'The Strokes' }, - { label: 'Arctic Monkeys' }, - { label: 'Oasis' }, - { label: 'Tame Impala' }, + { value: '0', label: 'The Strokes' }, + { value: '0', label: 'Arctic Monkeys' }, + { value: '0', label: 'Oasis' }, + { value: '0', label: 'Tame Impala' }, ]; const output = values.sort(sortByAlpha); expect(output).to.eql([ - { label: 'Arctic Monkeys' }, - { label: 'Oasis' }, - { label: 'Tame Impala' }, - { label: 'The Strokes' }, + { value: '0', label: 'Arctic Monkeys' }, + { value: '0', label: 'Oasis' }, + { value: '0', label: 'Tame Impala' }, + { value: '0', label: 'The Strokes' }, ]); }); }); @@ -185,12 +186,12 @@ describe('utils', () => { const fakeElement = { dispatchEvent: stub(), }; - const eventType = 'testEvent'; + const eventType = 'addItem'; const customArgs = { testing: true, }; - dispatchEvent(fakeElement, eventType, customArgs); + dispatchEvent(fakeElement as any, eventType, customArgs); expect(fakeElement.dispatchEvent.called).to.equal(true); const event = fakeElement.dispatchEvent.lastCall.args[0]; diff --git a/src/scripts/lib/utils.ts b/src/scripts/lib/utils.ts new file mode 100644 index 000000000..1508e0985 --- /dev/null +++ b/src/scripts/lib/utils.ts @@ -0,0 +1,180 @@ +import { EventMap, Choice } from '../interfaces'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export const getRandomNumber = (min: number, max: number): number => + Math.floor(Math.random() * (max - min) + min); + +export const generateChars = (length: number): string => + Array.from({ length }, () => getRandomNumber(0, 36).toString(36)).join(''); + +export const generateId = ( + element: HTMLInputElement | HTMLSelectElement, + prefix: string, +): string => { + let id = + element.id || + (element.name && `${element.name}-${generateChars(2)}`) || + generateChars(4); + id = id.replace(/(:|\.|\[|\]|,)/g, ''); + id = `${prefix}-${id}`; + + return id; +}; + +export const getType = (obj: any): string => + Object.prototype.toString.call(obj).slice(8, -1); + +export const isType = (type: string, obj: any): boolean => + obj !== undefined && obj !== null && getType(obj) === type; + +export const wrap = ( + element: HTMLElement, + wrapper: HTMLElement = document.createElement('div'), +): HTMLElement => { + if (element.nextSibling) { + element.parentNode && + element.parentNode.insertBefore(wrapper, element.nextSibling); + } else { + element.parentNode && element.parentNode.appendChild(wrapper); + } + + return wrapper.appendChild(element); +}; + +export const getAdjacentEl = ( + startEl: Element, + selector: string, + direction = 1, +): Element => { + const prop = `${direction > 0 ? 'next' : 'previous'}ElementSibling`; + + let sibling = startEl[prop]; + while (sibling) { + if (sibling.matches(selector)) { + return sibling; + } + sibling = sibling[prop]; + } + + return sibling; +}; + +export const isScrolledIntoView = ( + element: HTMLElement, + parent: HTMLElement, + direction = 1, +): boolean => { + if (!element) { + return false; + } + + let isVisible; + + if (direction > 0) { + // In view from bottom + isVisible = + parent.scrollTop + parent.offsetHeight >= + element.offsetTop + element.offsetHeight; + } else { + // In view from top + isVisible = element.offsetTop >= parent.scrollTop; + } + + return isVisible; +}; + +export const sanitise = (value: T | string): T | string => { + if (typeof value !== 'string') { + return value; + } + + return value + .replace(/&/g, '&') + .replace(/>/g, '&rt;') + .replace(/ Element) => { + const tmpEl = document.createElement('div'); + + return (str): Element => { + const cleanedInput = str.trim(); + tmpEl.innerHTML = cleanedInput; + const firldChild = tmpEl.children[0]; + + while (tmpEl.firstChild) { + tmpEl.removeChild(tmpEl.firstChild); + } + + return firldChild; + }; +})(); + +interface RecordToCompare { + value: string; + label?: string; +} +export const sortByAlpha = ( + { value, label = value }: RecordToCompare, + { value: value2, label: label2 = value2 }: RecordToCompare, +): number => + label.localeCompare(label2, [], { + sensitivity: 'base', + ignorePunctuation: true, + numeric: true, + }); + +export const sortByScore = ( + a: Pick, + b: Pick, +): number => { + const { score: scoreA = 0 } = a; + const { score: scoreB = 0 } = b; + + return scoreA - scoreB; +}; + +export const dispatchEvent = ( + element: HTMLElement, + type: keyof EventMap, + customArgs: object | null = null, +): boolean => { + const event = new CustomEvent(type, { + detail: customArgs, + bubbles: true, + cancelable: true, + }); + + return element.dispatchEvent(event); +}; + +export const existsInArray = ( + array: any[], + value: string, + key = 'value', +): boolean => + array.some(item => { + if (typeof value === 'string') { + return item[key] === value.trim(); + } + + return item[key] === value; + }); + +export const cloneObject = (obj: object): object => + JSON.parse(JSON.stringify(obj)); + +/** + * Returns an array of keys present on the first but missing on the second object + */ +export const diff = ( + a: Record, + b: Record, +): string[] => { + const aKeys = Object.keys(a).sort(); + const bKeys = Object.keys(b).sort(); + + return aKeys.filter(i => bKeys.indexOf(i) < 0); +}; diff --git a/src/scripts/reducers/choices.js b/src/scripts/reducers/choices.js deleted file mode 100644 index 8f1051689..000000000 --- a/src/scripts/reducers/choices.js +++ /dev/null @@ -1,110 +0,0 @@ -export const defaultState = []; - -export default function choices(state = defaultState, action) { - switch (action.type) { - case 'ADD_CHOICE': { - /* - A disabled choice appears in the choice dropdown but cannot be selected - A selected choice has been added to the passed input's value (added as an item) - An active choice appears within the choice dropdown - */ - return [ - ...state, - { - id: action.id, - elementId: action.elementId, - groupId: action.groupId, - value: action.value, - label: action.label || action.value, - disabled: action.disabled || false, - selected: false, - active: true, - score: 9999, - customProperties: action.customProperties, - placeholder: action.placeholder || false, - keyCode: null, - }, - ]; - } - - case 'ADD_ITEM': { - // If all choices need to be activated - if (action.activateOptions) { - return state.map(obj => { - const choice = obj; - choice.active = action.active; - - return choice; - }); - } - - // When an item is added and it has an associated choice, - // we want to disable it so it can't be chosen again - if (action.choiceId > -1) { - return state.map(obj => { - const choice = obj; - if (choice.id === parseInt(action.choiceId, 10)) { - choice.selected = true; - } - - return choice; - }); - } - - return state; - } - - case 'REMOVE_ITEM': { - // When an item is removed and it has an associated choice, - // we want to re-enable it so it can be chosen again - if (action.choiceId > -1) { - return state.map(obj => { - const choice = obj; - if (choice.id === parseInt(action.choiceId, 10)) { - choice.selected = false; - } - - return choice; - }); - } - - return state; - } - - case 'FILTER_CHOICES': { - return state.map(obj => { - const choice = obj; - // Set active state based on whether choice is - // within filtered results - choice.active = action.results.some(({ item, score }) => { - if (item.id === choice.id) { - choice.score = score; - - return true; - } - - return false; - }); - - return choice; - }); - } - - case 'ACTIVATE_CHOICES': { - return state.map(obj => { - const choice = obj; - choice.active = action.active; - - return choice; - }); - } - - case 'CLEAR_CHOICES': { - return defaultState; - } - - default: { - return state; - } - } -} diff --git a/src/scripts/reducers/choices.test.js b/src/scripts/reducers/choices.test.ts similarity index 81% rename from src/scripts/reducers/choices.test.js rename to src/scripts/reducers/choices.test.ts index 9563f6c13..a46c1ee1a 100644 --- a/src/scripts/reducers/choices.test.js +++ b/src/scripts/reducers/choices.test.ts @@ -1,21 +1,22 @@ import { expect } from 'chai'; import choices, { defaultState } from './choices'; +import { Choice } from '../interfaces'; describe('reducers/choices', () => { it('should return same state when no action matches', () => { - expect(choices(defaultState, {})).to.equal(defaultState); + expect(choices(defaultState, {} as any)).to.equal(defaultState); }); describe('when choices do not exist', () => { describe('ADD_CHOICE', () => { const value = 'test'; const label = 'test'; - const id = 'test'; - const groupId = 'test'; + const id = 1; + const groupId = 1; const disabled = false; - const elementId = 'test'; - const customProperties = 'test'; - const placeholder = 'test'; + const elementId = 1; + const customProperties = { test: true }; + const placeholder = true; describe('passing expected values', () => { it('adds choice', () => { @@ -32,7 +33,6 @@ describe('reducers/choices', () => { selected: false, active: true, score: 9999, - keyCode: null, }, ]; @@ -68,14 +68,13 @@ describe('reducers/choices', () => { selected: false, active: true, score: 9999, - keyCode: null, }, ]; const actualResponse = choices(undefined, { type: 'ADD_CHOICE', value, - label: null, + label: undefined, id, groupId, disabled, @@ -103,14 +102,13 @@ describe('reducers/choices', () => { selected: false, active: true, score: 9999, - keyCode: null, }, ]; const actualResponse = choices(undefined, { type: 'ADD_CHOICE', value, - label: null, + label: undefined, id, groupId, disabled, @@ -143,7 +141,6 @@ describe('reducers/choices', () => { score: 9999, customProperties: null, placeholder: false, - keyCode: null, }, { id: 2, @@ -157,7 +154,6 @@ describe('reducers/choices', () => { score: 9999, customProperties: null, placeholder: false, - keyCode: null, }, ]; }); @@ -178,9 +174,7 @@ describe('reducers/choices', () => { type: 'FILTER_CHOICES', results: [ { - item: { - id, - }, + item: { id } as Choice, score, }, ], @@ -227,45 +221,34 @@ describe('reducers/choices', () => { }); describe('ADD_ITEM', () => { - it('disables choice if action has choice id', () => { - const id = 2; - const clonedState = state.slice(0); - const expectedResponse = [ - { - ...state[0], - }, - { - ...state[1], - selected: true, - }, - ]; - - const actualResponse = choices(clonedState, { - type: 'ADD_ITEM', - choiceId: id, - }); + describe('when action has a choice id', () => { + it('disables choice corresponding with id', () => { + const id = 2; + const clonedState = state.slice(0); + const expectedResponse = [ + { + ...state[0], + }, + { + ...state[1], + selected: true, + }, + ]; - expect(actualResponse).to.eql(expectedResponse); - }); + const actualResponse = choices(clonedState, { + type: 'ADD_ITEM', + choiceId: id, + }); - it('activates all choices if activateOptions flag passed', () => { - const clonedState = state.slice(0); - const actualResponse = choices(clonedState, { - type: 'ADD_ITEM', - activateOptions: true, - active: true, + expect(actualResponse).to.eql(expectedResponse); }); - - expect(actualResponse[0].active).to.equal(true); - expect(actualResponse[1].active).to.equal(true); }); - describe('neither of the above conditions are satisified', () => { + describe('when action has no choice id', () => { it('returns state', () => { const clonedState = state.slice(0); const actualResponse = choices(clonedState, { type: 'ADD_ITEM', - activateOptions: false, choiceId: undefined, }); diff --git a/src/scripts/reducers/choices.ts b/src/scripts/reducers/choices.ts new file mode 100644 index 000000000..bd73e1921 --- /dev/null +++ b/src/scripts/reducers/choices.ts @@ -0,0 +1,127 @@ +import { Choice } from '../interfaces'; +import { + AddChoiceAction, + FilterChoicesAction, + ActivateChoicesAction, + ClearChoicesAction, +} from '../actions/choices'; +import { AddItemAction, RemoveItemAction } from '../actions/items'; + +export const defaultState = []; + +type ActionTypes = + | AddChoiceAction + | FilterChoicesAction + | ActivateChoicesAction + | ClearChoicesAction + | AddItemAction + | RemoveItemAction; + +export default function choices( + state: Choice[] = defaultState, + action: ActionTypes, +): Choice[] { + switch (action.type) { + case 'ADD_CHOICE': { + const addChoiceAction = action as AddChoiceAction; + const choice = { + id: addChoiceAction.id, + elementId: addChoiceAction.elementId, + groupId: addChoiceAction.groupId, + value: addChoiceAction.value, + label: addChoiceAction.label || addChoiceAction.value, + disabled: addChoiceAction.disabled || false, + selected: false, + active: true, + score: 9999, + customProperties: addChoiceAction.customProperties, + placeholder: addChoiceAction.placeholder || false, + }; + + /* + A disabled choice appears in the choice dropdown but cannot be selected + A selected choice has been added to the passed input's value (added as an item) + An active choice appears within the choice dropdown + */ + return [...state, choice as Choice]; + } + + case 'ADD_ITEM': { + const addItemAction = action as AddItemAction; + + // When an item is added and it has an associated choice, + // we want to disable it so it can't be chosen again + if (addItemAction.choiceId > -1) { + return state.map(obj => { + const choice = obj; + if (choice.id === parseInt(`${addItemAction.choiceId}`, 10)) { + choice.selected = true; + } + + return choice; + }); + } + + return state; + } + + case 'REMOVE_ITEM': { + const removeItemAction = action as RemoveItemAction; + + // When an item is removed and it has an associated choice, + // we want to re-enable it so it can be chosen again + if (removeItemAction.choiceId && removeItemAction.choiceId > -1) { + return state.map(obj => { + const choice = obj; + if (choice.id === parseInt(`${removeItemAction.choiceId}`, 10)) { + choice.selected = false; + } + + return choice; + }); + } + + return state; + } + + case 'FILTER_CHOICES': { + const filterChoicesAction = action as FilterChoicesAction; + + return state.map(obj => { + const choice = obj; + // Set active state based on whether choice is + // within filtered results + choice.active = filterChoicesAction.results.some(({ item, score }) => { + if (item.id === choice.id) { + choice.score = score; + + return true; + } + + return false; + }); + + return choice; + }); + } + + case 'ACTIVATE_CHOICES': { + const activateChoicesAction = action as ActivateChoicesAction; + + return state.map(obj => { + const choice = obj; + choice.active = activateChoicesAction.active; + + return choice; + }); + } + + case 'CLEAR_CHOICES': { + return defaultState; + } + + default: { + return state; + } + } +} diff --git a/src/scripts/reducers/general.js b/src/scripts/reducers/general.js deleted file mode 100644 index 2a72cb387..000000000 --- a/src/scripts/reducers/general.js +++ /dev/null @@ -1,19 +0,0 @@ -export const defaultState = { - loading: false, -}; - -const general = (state = defaultState, action) => { - switch (action.type) { - case 'SET_IS_LOADING': { - return { - loading: action.isLoading, - }; - } - - default: { - return state; - } - } -}; - -export default general; diff --git a/src/scripts/reducers/groups.js b/src/scripts/reducers/groups.js deleted file mode 100644 index d704e926c..000000000 --- a/src/scripts/reducers/groups.js +++ /dev/null @@ -1,25 +0,0 @@ -export const defaultState = []; - -export default function groups(state = defaultState, action) { - switch (action.type) { - case 'ADD_GROUP': { - return [ - ...state, - { - id: action.id, - value: action.value, - active: action.active, - disabled: action.disabled, - }, - ]; - } - - case 'CLEAR_CHOICES': { - return []; - } - - default: { - return state; - } - } -} diff --git a/src/scripts/reducers/groups.test.js b/src/scripts/reducers/groups.test.ts similarity index 94% rename from src/scripts/reducers/groups.test.js rename to src/scripts/reducers/groups.test.ts index e67ee04a1..95008efb6 100644 --- a/src/scripts/reducers/groups.test.js +++ b/src/scripts/reducers/groups.test.ts @@ -3,13 +3,13 @@ import groups, { defaultState } from './groups'; describe('reducers/groups', () => { it('should return same state when no action matches', () => { - expect(groups(defaultState, {})).to.equal(defaultState); + expect(groups(defaultState, {} as any)).to.equal(defaultState); }); describe('when groups do not exist', () => { describe('ADD_GROUP', () => { it('adds group', () => { - const id = '1'; + const id = 1; const value = 'Group one'; const active = true; const disabled = false; diff --git a/src/scripts/reducers/groups.ts b/src/scripts/reducers/groups.ts new file mode 100644 index 000000000..c845e9127 --- /dev/null +++ b/src/scripts/reducers/groups.ts @@ -0,0 +1,36 @@ +import { Group, State } from '../interfaces'; +import { AddGroupAction } from '../actions/groups'; +import { ClearChoicesAction } from '../actions/choices'; + +export const defaultState = []; + +type ActionTypes = AddGroupAction | ClearChoicesAction; + +export default function groups( + state: Group[] = defaultState, + action: ActionTypes, +): State['groups'] { + switch (action.type) { + case 'ADD_GROUP': { + const addGroupAction = action as AddGroupAction; + + return [ + ...state, + { + id: addGroupAction.id, + value: addGroupAction.value, + active: addGroupAction.active, + disabled: addGroupAction.disabled, + }, + ]; + } + + case 'CLEAR_CHOICES': { + return []; + } + + default: { + return state; + } + } +} diff --git a/src/scripts/reducers/index.test.js b/src/scripts/reducers/index.test.ts similarity index 75% rename from src/scripts/reducers/index.test.js rename to src/scripts/reducers/index.test.ts index c662cc85f..50e252cf3 100644 --- a/src/scripts/reducers/index.test.js +++ b/src/scripts/reducers/index.test.ts @@ -1,9 +1,10 @@ import { createStore } from 'redux'; import { expect } from 'chai'; -import rootReducer from './index'; +import rootReducer from '.'; import groups from './groups'; import choices from './choices'; import items from './items'; +import loading from './loading'; describe('reducers/rootReducer', () => { const store = createStore(rootReducer); @@ -11,9 +12,10 @@ describe('reducers/rootReducer', () => { it('returns expected reducers', () => { const state = store.getState(); - expect(state.groups).to.equal(groups(undefined, {})); - expect(state.choices).to.equal(choices(undefined, {})); - expect(state.items).to.equal(items(undefined, {})); + expect(state.groups).to.equal(groups(undefined, {} as any)); + expect(state.choices).to.equal(choices(undefined, {} as any)); + expect(state.items).to.equal(items(undefined, {} as any)); + expect(state.loading).to.equal(loading(undefined, {} as any)); }); describe('CLEAR_ALL', () => { @@ -33,9 +35,7 @@ describe('reducers/rootReducer', () => { items: [], groups: [], choices: [], - general: { - loading: false, - }, + loading: false, }); }); }); diff --git a/src/scripts/reducers/index.js b/src/scripts/reducers/index.ts similarity index 75% rename from src/scripts/reducers/index.js rename to src/scripts/reducers/index.ts index 31dc7b395..92b10e442 100644 --- a/src/scripts/reducers/index.js +++ b/src/scripts/reducers/index.ts @@ -2,24 +2,31 @@ import { combineReducers } from 'redux'; import items from './items'; import groups from './groups'; import choices from './choices'; -import general from './general'; +import loading from './loading'; import { cloneObject } from '../lib/utils'; +export const defaultState = { + groups: [], + items: [], + choices: [], + loading: false, +}; + const appReducer = combineReducers({ items, groups, choices, - general, + loading, }); -const rootReducer = (passedState, action) => { +const rootReducer = (passedState, action): object => { let state = passedState; // If we are clearing all items, groups and options we reassign // state and then pass that state to our proper reducer. This isn't // mutating our actual state // See: http://stackoverflow.com/a/35641992 if (action.type === 'CLEAR_ALL') { - state = undefined; + state = defaultState; } else if (action.type === 'RESET_TO') { return cloneObject(action.state); } diff --git a/src/scripts/reducers/items.js b/src/scripts/reducers/items.js deleted file mode 100644 index 2a520f9d2..000000000 --- a/src/scripts/reducers/items.js +++ /dev/null @@ -1,58 +0,0 @@ -export const defaultState = []; - -export default function items(state = defaultState, action) { - switch (action.type) { - case 'ADD_ITEM': { - // Add object to items array - const newState = [ - ...state, - { - id: action.id, - choiceId: action.choiceId, - groupId: action.groupId, - value: action.value, - label: action.label, - active: true, - highlighted: false, - customProperties: action.customProperties, - placeholder: action.placeholder || false, - keyCode: null, - }, - ]; - - return newState.map(obj => { - const item = obj; - item.highlighted = false; - - return item; - }); - } - - case 'REMOVE_ITEM': { - // Set item to inactive - return state.map(obj => { - const item = obj; - if (item.id === action.id) { - item.active = false; - } - - return item; - }); - } - - case 'HIGHLIGHT_ITEM': { - return state.map(obj => { - const item = obj; - if (item.id === action.id) { - item.highlighted = action.highlighted; - } - - return item; - }); - } - - default: { - return state; - } - } -} diff --git a/src/scripts/reducers/items.test.js b/src/scripts/reducers/items.test.ts similarity index 95% rename from src/scripts/reducers/items.test.js rename to src/scripts/reducers/items.test.ts index 4041a3b3e..0a7262f32 100644 --- a/src/scripts/reducers/items.test.js +++ b/src/scripts/reducers/items.test.ts @@ -1,9 +1,10 @@ import { expect } from 'chai'; import items, { defaultState } from './items'; +import { RemoveItemAction } from '../actions/items'; describe('reducers/items', () => { it('should return same state when no action matches', () => { - expect(items(defaultState, {})).to.equal(defaultState); + expect(items(defaultState, {} as any)).to.equal(defaultState); }); describe('when items do not exist', () => { @@ -16,7 +17,7 @@ describe('reducers/items', () => { const customProperties = { property: 'value', }; - const placeholder = 'This is a placeholder'; + const placeholder = true; const keyCode = 10; describe('passing expected values', () => { @@ -148,7 +149,7 @@ describe('reducers/items', () => { const actualResponse = items(clonedState, { type: 'REMOVE_ITEM', id, - }); + } as RemoveItemAction); expect(actualResponse).to.eql(expectedResponse); }); diff --git a/src/scripts/reducers/items.ts b/src/scripts/reducers/items.ts new file mode 100644 index 000000000..fb8432ace --- /dev/null +++ b/src/scripts/reducers/items.ts @@ -0,0 +1,73 @@ +import { Item, State } from '../interfaces'; +import { + AddItemAction, + RemoveItemAction, + HighlightItemAction, +} from '../actions/items'; + +export const defaultState = []; + +type ActionTypes = AddItemAction | RemoveItemAction | HighlightItemAction; + +export default function items( + state: Item[] = defaultState, + action: ActionTypes, +): State['items'] { + switch (action.type) { + case 'ADD_ITEM': { + const addItemAction = action as AddItemAction; + // Add object to items array + const newState = [ + ...state, + { + id: addItemAction.id, + choiceId: addItemAction.choiceId, + groupId: addItemAction.groupId, + value: addItemAction.value, + label: addItemAction.label, + active: true, + highlighted: false, + customProperties: addItemAction.customProperties, + placeholder: addItemAction.placeholder || false, + keyCode: null, + }, + ]; + + return newState.map((obj: Item) => { + const item = obj; + item.highlighted = false; + + return item; + }); + } + + case 'REMOVE_ITEM': { + // Set item to inactive + return state.map(obj => { + const item = obj; + if (item.id === action.id) { + item.active = false; + } + + return item; + }); + } + + case 'HIGHLIGHT_ITEM': { + const highlightItemAction = action as HighlightItemAction; + + return state.map(obj => { + const item = obj; + if (item.id === highlightItemAction.id) { + item.highlighted = highlightItemAction.highlighted; + } + + return item; + }); + } + + default: { + return state; + } + } +} diff --git a/src/scripts/reducers/general.test.js b/src/scripts/reducers/loading.test.ts similarity index 62% rename from src/scripts/reducers/general.test.js rename to src/scripts/reducers/loading.test.ts index 01203dcc4..cbf9c4bf0 100644 --- a/src/scripts/reducers/general.test.js +++ b/src/scripts/reducers/loading.test.ts @@ -1,16 +1,14 @@ import { expect } from 'chai'; -import general, { defaultState } from './general'; +import general, { defaultState } from './loading'; -describe('reducers/general', () => { +describe('reducers/loading', () => { it('should return same state when no action matches', () => { - expect(general(defaultState, {})).to.equal(defaultState); + expect(general(defaultState, {} as any)).to.equal(defaultState); }); describe('SET_IS_LOADING', () => { it('sets loading state', () => { - const expectedState = { - loading: true, - }; + const expectedState = true; const actualState = general(undefined, { type: 'SET_IS_LOADING', diff --git a/src/scripts/reducers/loading.ts b/src/scripts/reducers/loading.ts new file mode 100644 index 000000000..58f6469ac --- /dev/null +++ b/src/scripts/reducers/loading.ts @@ -0,0 +1,23 @@ +import { SetIsLoadingAction } from '../actions/misc'; +import { State } from '../interfaces'; + +export const defaultState = false; + +type ActionTypes = SetIsLoadingAction; + +const general = ( + state = defaultState, + action: ActionTypes, +): State['loading'] => { + switch (action.type) { + case 'SET_IS_LOADING': { + return action.isLoading; + } + + default: { + return state; + } + } +}; + +export default general; diff --git a/src/scripts/store/store.test.js b/src/scripts/store/store.test.ts similarity index 99% rename from src/scripts/store/store.test.js rename to src/scripts/store/store.test.ts index 63ebcc556..44fcaacf4 100644 --- a/src/scripts/store/store.test.js +++ b/src/scripts/store/store.test.ts @@ -33,7 +33,7 @@ describe('reducers/store', () => { describe('subscribe', () => { it('wraps redux subscribe method', () => { - const onChange = () => {}; + const onChange = (): void => {}; expect(subscribeStub.callCount).to.equal(0); instance.subscribe(onChange); expect(subscribeStub.callCount).to.equal(1); diff --git a/src/scripts/store/store.js b/src/scripts/store/store.ts similarity index 58% rename from src/scripts/store/store.js rename to src/scripts/store/store.ts index 4fb7888ab..cffbf5196 100644 --- a/src/scripts/store/store.js +++ b/src/scripts/store/store.ts @@ -1,108 +1,93 @@ -import { createStore } from 'redux'; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { createStore, Store as IStore, AnyAction } from 'redux'; import rootReducer from '../reducers/index'; - -/** - * @typedef {import('../../../types/index').Choices.Choice} Choice - * @typedef {import('../../../types/index').Choices.Group} Group - * @typedef {import('../../../types/index').Choices.Item} Item - */ +import { Choice, Group, Item, State } from '../interfaces'; export default class Store { + _store: IStore; + constructor() { this._store = createStore( rootReducer, - window.__REDUX_DEVTOOLS_EXTENSION__ && - window.__REDUX_DEVTOOLS_EXTENSION__(), + (window as any).__REDUX_DEVTOOLS_EXTENSION__ && + (window as any).__REDUX_DEVTOOLS_EXTENSION__(), ); } /** * Subscribe store to function call (wrapped Redux method) - * @param {Function} onChange Function to trigger when state changes - * @return */ - subscribe(onChange) { + subscribe(onChange: () => void): void { this._store.subscribe(onChange); } /** * Dispatch event to store (wrapped Redux method) - * @param {{ type: string, [x: string]: any }} action Action to trigger - * @return */ - dispatch(action) { + dispatch(action: AnyAction): void { this._store.dispatch(action); } /** * Get store object (wrapping Redux method) - * @returns {object} State */ - get state() { + get state(): State { return this._store.getState(); } /** * Get items from store - * @returns {Item[]} Item objects */ - get items() { + get items(): Item[] { return this.state.items; } /** * Get active items from store - * @returns {Item[]} Item objects */ - get activeItems() { + get activeItems(): Item[] { return this.items.filter(item => item.active === true); } /** * Get highlighted items from store - * @returns {Item[]} Item objects */ - get highlightedActiveItems() { + get highlightedActiveItems(): Item[] { return this.items.filter(item => item.active && item.highlighted); } /** * Get choices from store - * @returns {Choice[]} Option objects */ - get choices() { + get choices(): Choice[] { return this.state.choices; } /** * Get active choices from store - * @returns {Choice[]} Option objects */ - get activeChoices() { + get activeChoices(): Choice[] { return this.choices.filter(choice => choice.active === true); } /** * Get selectable choices from store - * @returns {Choice[]} Option objects */ - get selectableChoices() { + get selectableChoices(): Choice[] { return this.choices.filter(choice => choice.disabled !== true); } /** * Get choices that can be searched (excluding placeholders) - * @returns {Choice[]} Option objects */ - get searchableChoices() { + get searchableChoices(): Choice[] { return this.selectableChoices.filter(choice => choice.placeholder !== true); } /** * Get placeholder choice from store - * @returns {Choice | undefined} Found placeholder */ - get placeholderChoice() { + get placeholderChoice(): Choice | undefined { return [...this.choices] .reverse() .find(choice => choice.placeholder === true); @@ -110,17 +95,15 @@ export default class Store { /** * Get groups from store - * @returns {Group[]} Group objects */ - get groups() { + get groups(): Group[] { return this.state.groups; } /** * Get active groups from store - * @returns {Group[]} Group objects */ - get activeGroups() { + get activeGroups(): Group[] { const { groups, choices } = this; return groups.filter(group => { @@ -135,27 +118,22 @@ export default class Store { /** * Get loading state from store - * @returns {boolean} Loading State */ - isLoading() { - return this.state.general.loading; + isLoading(): boolean { + return this.state.loading; } /** * Get single choice by it's ID - * @param {string} id - * @returns {Choice | undefined} Found choice */ - getChoiceById(id) { + getChoiceById(id: string): Choice | undefined { return this.activeChoices.find(choice => choice.id === parseInt(id, 10)); } /** * Get group by group id - * @param {number} id Group ID - * @returns {Group | undefined} Group data */ - getGroupById(id) { + getGroupById(id: number): Group | undefined { return this.groups.find(group => group.id === id); } } diff --git a/src/scripts/templates.test.js b/src/scripts/templates.test.ts similarity index 99% rename from src/scripts/templates.test.js rename to src/scripts/templates.test.ts index 2db8ed830..363cfa798 100644 --- a/src/scripts/templates.test.js +++ b/src/scripts/templates.test.ts @@ -3,11 +3,10 @@ import templates from './templates'; import { strToEl } from './lib/utils'; /** - * * @param {HTMLElement} element1 * @param {HTMLElement} element2 */ -function expectEqualElements(element1, element2) { +function expectEqualElements(element1, element2): void { expect(element1.tagName).to.equal(element2.tagName); expect(element1.attributes.length).to.equal(element2.attributes.length); expect(Object.keys(element1.dataset)).to.have.members( @@ -516,11 +515,10 @@ describe('templates', () => { }; it('returns expected html', () => { - const value = 'test'; const expectedOutput = strToEl( ``, ); - const actualOutput = templates.dropdown(classes, value); + const actualOutput = templates.dropdown(classes); expectEqualElements(actualOutput, expectedOutput); }); diff --git a/src/scripts/templates.js b/src/scripts/templates.ts similarity index 65% rename from src/scripts/templates.js rename to src/scripts/templates.ts index eb4f2eadb..cb569210e 100644 --- a/src/scripts/templates.js +++ b/src/scripts/templates.ts @@ -1,31 +1,19 @@ +import { ClassNames, Item, Choice, Group, PassedElement } from './interfaces'; + /** * Helpers to create HTML elements used by Choices * Can be overridden by providing `callbackOnCreateTemplates` option - * @typedef {import('../../types/index').Choices.Templates} Templates - * @typedef {import('../../types/index').Choices.ClassNames} ClassNames - * @typedef {import('../../types/index').Choices.Options} Options - * @typedef {import('../../types/index').Choices.Item} Item - * @typedef {import('../../types/index').Choices.Choice} Choice - * @typedef {import('../../types/index').Choices.Group} Group */ -export const TEMPLATES = /** @type {Templates} */ ({ - /** - * @param {Partial} classNames - * @param {"ltr" | "rtl" | "auto"} dir - * @param {boolean} isSelectElement - * @param {boolean} isSelectOneElement - * @param {boolean} searchEnabled - * @param {"select-one" | "select-multiple" | "text"} passedElementType - */ +const templates = { containerOuter( - { containerOuter }, - dir, - isSelectElement, - isSelectOneElement, - searchEnabled, - passedElementType, - ) { + { containerOuter }: Pick, + dir: HTMLElement['dir'], + isSelectElement: boolean, + isSelectOneElement: boolean, + searchEnabled: boolean, + passedElementType: PassedElement['type'], + ): HTMLDivElement { const div = Object.assign(document.createElement('div'), { className: containerOuter, }); @@ -53,43 +41,48 @@ export const TEMPLATES = /** @type {Templates} */ ({ return div; }, - /** - * @param {Partial} classNames - */ - containerInner({ containerInner }) { + containerInner({ + containerInner, + }: Pick): HTMLDivElement { return Object.assign(document.createElement('div'), { className: containerInner, }); }, - /** - * @param {Partial} classNames - * @param {boolean} isSelectOneElement - */ - itemList({ list, listSingle, listItems }, isSelectOneElement) { + itemList( + { + list, + listSingle, + listItems, + }: Pick, + isSelectOneElement: boolean, + ): HTMLDivElement { return Object.assign(document.createElement('div'), { className: `${list} ${isSelectOneElement ? listSingle : listItems}`, }); }, - /** - * @param {Partial} classNames - * @param {string} value - */ - placeholder({ placeholder }, value) { + placeholder( + { placeholder }: Pick, + value: string, + ): HTMLDivElement { return Object.assign(document.createElement('div'), { className: placeholder, innerHTML: value, }); }, - /** - * @param {Partial} classNames - * @param {Item} item - * @param {boolean} removeItemButton - */ item( - { item, button, highlightedState, itemSelectable, placeholder }, + { + item, + button, + highlightedState, + itemSelectable, + placeholder, + }: Pick< + ClassNames, + 'item' | 'button' | 'highlightedState' | 'itemSelectable' | 'placeholder' + >, { id, value, @@ -99,9 +92,9 @@ export const TEMPLATES = /** @type {Templates} */ ({ disabled, highlighted, placeholder: isPlaceholder, - }, - removeItemButton, - ) { + }: Item, + removeItemButton: boolean, + ): HTMLDivElement { const div = Object.assign(document.createElement('div'), { className: item, innerHTML: label, @@ -151,11 +144,10 @@ export const TEMPLATES = /** @type {Templates} */ ({ return div; }, - /** - * @param {Partial} classNames - * @param {boolean} isSelectOneElement - */ - choiceList({ list }, isSelectOneElement) { + choiceList( + { list }: Pick, + isSelectOneElement: boolean, + ): HTMLDivElement { const div = Object.assign(document.createElement('div'), { className: list, }); @@ -168,11 +160,14 @@ export const TEMPLATES = /** @type {Templates} */ ({ return div; }, - /** - * @param {Partial} classNames - * @param {Group} group - */ - choiceGroup({ group, groupHeading, itemDisabled }, { id, value, disabled }) { + choiceGroup( + { + group, + groupHeading, + itemDisabled, + }: Pick, + { id, value, disabled }: Group, + ): HTMLDivElement { const div = Object.assign(document.createElement('div'), { className: `${group} ${disabled ? itemDisabled : ''}`, }); @@ -199,11 +194,6 @@ export const TEMPLATES = /** @type {Templates} */ ({ return div; }, - /** - * @param {Partial} classNames - * @param {Choice} choice - * @param {Options['itemSelectText']} selectText - */ choice( { item, @@ -212,7 +202,15 @@ export const TEMPLATES = /** @type {Templates} */ ({ selectedState, itemDisabled, placeholder, - }, + }: Pick< + ClassNames, + | 'item' + | 'itemChoice' + | 'itemSelectable' + | 'selectedState' + | 'itemDisabled' + | 'placeholder' + >, { id, value, @@ -222,9 +220,9 @@ export const TEMPLATES = /** @type {Templates} */ ({ disabled: isDisabled, selected: isSelected, placeholder: isPlaceholder, - }, - selectText, - ) { + }: Choice, + selectText: string, + ): HTMLDivElement { const div = Object.assign(document.createElement('div'), { id: elementId, innerHTML: label, @@ -239,7 +237,7 @@ export const TEMPLATES = /** @type {Templates} */ ({ div.classList.add(placeholder); } - div.setAttribute('role', groupId > 0 ? 'treeitem' : 'option'); + div.setAttribute('role', groupId && groupId > 0 ? 'treeitem' : 'option'); Object.assign(div.dataset, { choice: '', @@ -260,11 +258,10 @@ export const TEMPLATES = /** @type {Templates} */ ({ return div; }, - /** - * @param {Partial} classNames - * @param {string} placeholderValue - */ - input({ input, inputCloned }, placeholderValue) { + input( + { input, inputCloned }: Pick, + placeholderValue: string, + ): HTMLInputElement { const inp = Object.assign(document.createElement('input'), { type: 'text', className: `${input} ${inputCloned}`, @@ -280,10 +277,10 @@ export const TEMPLATES = /** @type {Templates} */ ({ return inp; }, - /** - * @param {Partial} classNames - */ - dropdown({ list, listDropdown }) { + dropdown({ + list, + listDropdown, + }: Pick): HTMLDivElement { const div = document.createElement('div'); div.classList.add(list, listDropdown); @@ -292,13 +289,16 @@ export const TEMPLATES = /** @type {Templates} */ ({ return div; }, - /** - * - * @param {Partial} classNames - * @param {string} innerHTML - * @param {"no-choices" | "no-results" | ""} type - */ - notice({ item, itemChoice, noResults, noChoices }, innerHTML, type = '') { + notice( + { + item, + itemChoice, + noResults, + noChoices, + }: Pick, + innerHTML: string, + type: 'no-choices' | 'no-results' | '' = '', + ): HTMLDivElement { const classes = [item, itemChoice]; if (type === 'no-choices') { @@ -313,19 +313,23 @@ export const TEMPLATES = /** @type {Templates} */ ({ }); }, - /** - * @param {Item} option - */ - option({ label, value, customProperties, active, disabled }) { + option({ + label, + value, + customProperties, + active, + disabled, + }: Item): HTMLOptionElement { const opt = new Option(label, value, false, active); if (customProperties) { - opt.dataset.customProperties = customProperties; + opt.dataset.customProperties = `${customProperties}`; } - opt.disabled = disabled; + + opt.disabled = !!disabled; return opt; }, -}); +}; -export default TEMPLATES; +export default templates; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..fa1641323 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "baseUrl": "/", + "outDir": "./dist/", + "allowJs": true, + "sourceMap": true, + "module": "commonjs", + "esModuleInterop": true, + "target": "es5", + "lib": ["es2017", "dom"], + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitAny": false, + "strictNullChecks": true, + "types": ["cypress"] + }, + "include": ["./src/**/*", "./cypress/**/*"] +} diff --git a/types/index.d.ts b/types/index.d.ts deleted file mode 100644 index d3520bdfd..000000000 --- a/types/index.d.ts +++ /dev/null @@ -1,1041 +0,0 @@ -// Type definitions for Choices.js -// Project: https://github.com/jshjohnson/Choices -// Definitions by: -// Arthur vasconcelos , -// Josh Johnson , -// Zack Schuster -// Konstantin Vyatkin -// Definitions: https://github.com/jshjohnson/Choices - -import { FuseOptions } from 'fuse.js'; - -// Choices Namespace -declare namespace Choices { - namespace Types { - type strToEl = ( - str: string, - ) => HTMLElement | HTMLInputElement | HTMLOptionElement; - type stringFunction = () => string; - type noticeStringFunction = (value: string) => string; - type noticeLimitFunction = (maxItemCount: number) => string; - type filterFunction = (value: string) => boolean; - type valueCompareFunction = (value1: string, value2: string) => boolean; - } - - interface Choice { - id?: number; - customProperties?: Record; - disabled?: boolean; - active?: boolean; - elementId?: string; - groupId?: number; - keyCode?: number; - label: string; - placeholder?: boolean; - selected?: boolean; - value: string; - } - - interface Group { - id?: number; - active?: boolean; - disabled?: boolean; - value: any; - } - - interface Item extends Choice { - choiceId?: number; - keyCode?: number; - highlighted?: boolean; - } - - /** - * Events fired by Choices behave the same as standard events. Each event is triggered on the element passed to Choices (accessible via `this.passedElement`. Arguments are accessible within the `event.detail` object. - */ - interface EventMap { - /** - * Triggered each time an item is added (programmatically or by the user). - * - * **Input types affected:** text, select-one, select-multiple - * - * Arguments: id, value, label, groupValue, keyCode - */ - addItem: CustomEvent<{ - id: number; - value: string; - label: string; - groupValue: string; - keyCode: number; - }>; - - /** - * Triggered each time an item is removed (programmatically or by the user). - * - * **Input types affected:** text, select-one, select-multiple - * - * Arguments: id, value, label, groupValue - */ - removeItem: CustomEvent<{ - id: number; - value: string; - label: string; - groupValue: string; - }>; - - /** - * Triggered each time an item is highlighted. - * - * **Input types affected:** text, select-multiple - * - * Arguments: id, value, label, groupValue - */ - highlightItem: CustomEvent<{ - id: number; - value: string; - label: string; - groupValue: string; - }>; - - /** - * Triggered each time an item is unhighlighted. - * - * **Input types affected:** text, select-multiple - * - * Arguments: id, value, label, groupValue - */ - unhighlightItem: CustomEvent<{ - id: number; - value: string; - label: string; - groupValue: string; - }>; - - /** - * Triggered each time a choice is selected **by a user**, regardless if it changes the value of the input. - * - * **Input types affected:** select-one, select-multiple - * - * Arguments: choice: Choice - */ - choice: CustomEvent<{ choice: Choices.Choice }>; - - /** - * Triggered each time an item is added/removed **by a user**. - * - * **Input types affected:** text, select-one, select-multiple - * - * Arguments: value - */ - change: CustomEvent<{ value: string }>; - - /** - * Triggered when a user types into an input to search choices. - * - * **Input types affected:** select-one, select-multiple - * - * Arguments: value, resultCount - */ - search: CustomEvent<{ value: string; resultCount: number }>; - - /** - * Triggered when the dropdown is shown. - * - * **Input types affected:** select-one, select-multiple - * - * Arguments: - - */ - showDropdown: CustomEvent; - - /** - * Triggered when the dropdown is hidden. - * - * **Input types affected:** select-one, select-multiple - * - * Arguments: - - */ - hideDropdown: CustomEvent; - - /** - * Triggered when a choice from the dropdown is highlighted. - * - * Input types affected: select-one, select-multiple - * Arguments: el is the choice.passedElement that was affected. - */ - highlightChoice: CustomEvent<{ el: Choices.passedElement }>; - } - - interface Templates { - containerOuter: ( - this: Choices, - classNames: ClassNames, - direction: HTMLElement['dir'], - isSelectElement: boolean, - isSelectOneElement: boolean, - searchEnabled: boolean, - passedElementType: passedElement['type'], - ) => HTMLElement; - containerInner: (this: Choices, classNames: ClassNames) => HTMLElement; - itemList: ( - this: Choices, - classNames: ClassNames, - isSelectOneElement: boolean, - ) => HTMLElement; - placeholder: ( - this: Choices, - classNames: ClassNames, - value: string, - ) => HTMLElement; - item: ( - this: Choices, - classNames: ClassNames, - data: Choice, - removeItemButton: boolean, - ) => HTMLElement; - choiceList: ( - this: Choices, - classNames: ClassNames, - isSelectOneElement: boolean, - ) => HTMLElement; - choiceGroup: ( - this: Choices, - classNames: ClassNames, - data: Choice, - ) => HTMLElement; - choice: ( - this: Choices, - classNames: ClassNames, - data: Choice, - selectText: string, - ) => HTMLElement; - input: ( - this: Choices, - classNames: ClassNames, - placeholderValue: string, - ) => HTMLInputElement; - dropdown: (this: Choices, classNames: ClassNames) => HTMLElement; - notice: ( - this: Choices, - classNames: ClassNames, - label: string, - type: '' | 'no-results' | 'no-choices', - ) => HTMLElement; - option: (data: Choice) => HTMLOptionElement; - } - - /** Classes added to HTML generated by Choices. By default classnames follow the BEM notation. */ - interface ClassNames { - /** @default 'choices' */ - containerOuter: string; - /** @default 'choices__inner' */ - containerInner: string; - /** @default 'choices__input' */ - input: string; - /** @default 'choices__input--cloned' */ - inputCloned: string; - /** @default 'choices__list' */ - list: string; - /** @default 'choices__list--multiple' */ - listItems: string; - /** @default 'choices__list--single' */ - listSingle: string; - /** @default 'choices__list--dropdown' */ - listDropdown: string; - /** @default 'choices__item' */ - item: string; - /** @default 'choices__item--selectable' */ - itemSelectable: string; - /** @default 'choices__item--disabled' */ - itemDisabled: string; - /** @default 'choices__item--choice' */ - itemChoice: string; - /** @default 'choices__placeholder' */ - placeholder: string; - /** @default 'choices__group' */ - group: string; - /** @default 'choices__heading' */ - groupHeading: string; - /** @default 'choices__button' */ - button: string; - /** @default 'is-active' */ - activeState: string; - /** @default 'is-focused' */ - focusState: string; - /** @default 'is-open' */ - openState: string; - /** @default 'is-disabled' */ - disabledState: string; - /** @default 'is-highlighted' */ - highlightedState: string; - /** @default 'is-selected' */ - selectedState: string; - /** @default 'is-flipped' */ - flippedState: string; - /** @default 'is-loading' */ - loadingState: string; - /** @default 'has-no-results' */ - noResults: string; - /** @default 'has-no-choices' */ - noChoices: string; - } - - interface passedElement { - classNames: Choices.ClassNames; - element: (HTMLInputElement | HTMLSelectElement) & { - // Extends HTMLElement addEventListener with Choices events - addEventListener( - type: K, - listener: ( - this: HTMLInputElement | HTMLSelectElement, - ev: Choices.EventMap[K], - ) => void, - options?: boolean | AddEventListenerOptions, - ): void; - }; - type: 'text' | 'select-one' | 'select-multiple'; - isDisabled: boolean; - parentInstance: Choices; - } - - /** - * Choices options interface - * - * **Terminology** - * - * - **Choice:** A choice is a value a user can select. A choice would be equivalent to the `` element within a select input. - * - **Group:** A group is a collection of choices. A group should be seen as equivalent to a `` element within a select input. - * - **Item:** An item is an inputted value **_(text input)_** or a selected choice **_(select element)_**. In the context of a select element, an item is equivelent to a selected option element: `` whereas in the context of a text input an item is equivelant to `` - */ - interface Options { - /** - * Optionally suppress console errors and warnings. - * - * **Input types affected:** text, select-single, select-multiple - * - * @default false - */ - silent: boolean; - - /** - * Add pre-selected items (see terminology) to text input. - * - * **Input types affected:** text - * - * @example - * ``` - * ['value 1', 'value 2', 'value 3'] - * ``` - * - * @example - * ``` - * [{ - * value: 'Value 1', - * label: 'Label 1', - * id: 1 - * }, - * { - * value: 'Value 2', - * label: 'Label 2', - * id: 2, - * customProperties: { - * random: 'I am a custom property' - * } - * }] - * ``` - * - * @default [] - */ - items: string[] | Choice[]; - - /** - * Add choices (see terminology) to select input. - * - * **Input types affected:** select-one, select-multiple - * - * @example - * ``` - * [{ - * value: 'Option 1', - * label: 'Option 1', - * selected: true, - * disabled: false, - * }, - * { - * value: 'Option 2', - * label: 'Option 2', - * selected: false, - * disabled: true, - * customProperties: { - * description: 'Custom description about Option 2', - * random: 'Another random custom property' - * }, - * }] - * ``` - * - * @default [] - */ - choices: Choice[]; - - /** - * The amount of choices to be rendered within the dropdown list `("-1" indicates no limit)`. This is useful if you have a lot of choices where it is easier for a user to use the search area to find a choice. - * - * **Input types affected:** select-one, select-multiple - * - * @default -1 - */ - renderChoiceLimit: number; - - /** - * The amount of items a user can input/select `("-1" indicates no limit)`. - * - * **Input types affected:** text, select-multiple - * - * @default -1 - */ - maxItemCount: number; - - /** - * Whether a user can add items. - * - * **Input types affected:** text - * - * @default true - */ - addItems: boolean; - - /** - * A filter that will need to pass for a user to successfully add an item. - * - * **Input types affected:** text - * - * @default null - */ - addItemFilter: string | RegExp | Choices.Types.filterFunction | null; - - /** - * The text that is shown when a user has inputted a new item but has not pressed the enter key. To access the current input value, pass a function with a `value` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string. - * - * **Input types affected:** text - * - * @default - * ``` - * (value) => `Press Enter to add "${value}"`; - * ``` - */ - addItemText: string | Choices.Types.noticeStringFunction; - - /** - * Whether a user can remove items. - * - * **Input types affected:** text, select-multiple - * - * @default true - */ - removeItems: boolean; - - /** - * Whether each item should have a remove button. - * - * **Input types affected:** text, select-one, select-multiple - * - * @default false - */ - removeItemButton: boolean; - - /** - * Whether a user can edit items. An item's value can be edited by pressing the backspace. - * - * **Input types affected:** text - * - * @default false - */ - editItems: boolean; - - /** - * Whether each inputted/chosen item should be unique. - * - * **Input types affected:** text, select-multiple - * - * @default true - */ - duplicateItemsAllowed: boolean; - - /** - * What divides each value. The default delimiter separates each value with a comma: `"Value 1, Value 2, Value 3"`. - * - * **Input types affected:** text - * - * @default ',' - */ - delimiter: string; - - /** - * Whether a user can paste into the input. - * - * **Input types affected:** text, select-multiple - * - * @default true - */ - paste: boolean; - - /** - * Whether a search area should be shown. - * - * @note Multiple select boxes will always show search areas. - * - * **Input types affected:** select-one - * - * @default true - */ - searchEnabled: boolean; - - /** - * Whether choices should be filtered by input or not. If `false`, the search event will still emit, but choices will not be filtered. - * - * **Input types affected:** select-one - * - * @default true - */ - searchChoices: boolean; - - /** - * The minimum length a search value should be before choices are searched. - * - * **Input types affected:** select-one, select-multiple - * - * @default 1 - */ - searchFloor: number; - - /** - * The maximum amount of search results to show. - * - * **Input types affected:** select-one, select-multiple - * - * @default 4 - */ - searchResultLimit: number; - - /** - * Specify which fields should be used when a user is searching. If you have added custom properties to your choices, you can add these values thus: `['label', 'value', 'customProperties.example']`. - * - * Input types affected:select-one, select-multiple - * - * @default ['label', 'value'] - */ - searchFields: string[]; - - /** - * Whether the dropdown should appear above `(top)` or below `(bottom)` the input. By default, if there is not enough space within the window the dropdown will appear above the input, otherwise below it. - * - * **Input types affected:** select-one, select-multiple - * - * @default 'auto' - */ - position: 'auto' | 'top'; - - /** - * Whether the scroll position should reset after adding an item. - * - * **Input types affected:** select-multiple - * - * @default true - */ - resetScrollPosition: boolean; - - /** - * Whether choices and groups should be sorted. If false, choices/groups will appear in the order they were given. - * - * **Input types affected:** select-one, select-multiple - * - * @default true - */ - shouldSort: boolean; - - /** - * Whether items should be sorted. If false, items will appear in the order they were selected. - * - * **Input types affected:** text, select-multiple - * - * @default false - */ - shouldSortItems: boolean; - - /** - * The function that will sort choices and items before they are displayed (unless a user is searching). By default choices and items are sorted by alphabetical order. - * - * **Input types affected:** select-one, select-multiple - * - * @example - * ``` - * // Sorting via length of label from largest to smallest - * const example = new Choices(element, { - * sorter: function(a, b) { - * return b.label.length - a.label.length; - * }, - * }; - * ``` - * - * @default sortByAlpha - */ - sorter: (current: Choice, next: Choice) => number; - - /** - * Whether the input should show a placeholder. Used in conjunction with `placeholderValue`. If `placeholder` is set to true and no value is passed to `placeholderValue`, the passed input's placeholder attribute will be used as the placeholder value. - * - * **Input types affected:** text, select-multiple - * - * @note For single select boxes, the recommended way of adding a placeholder is as follows: - * ``` - * - * ``` - * - * @default true - */ - placeholder: boolean; - - /** - * The value of the inputs placeholder. - * - * **Input types affected:** text, select-multiple - * - * @default null - */ - placeholderValue: string | null; - - /** - * The value of the search inputs placeholder. - * - * **Input types affected:** select-one - * - * @default null - */ - searchPlaceholderValue: string | null; - - /** - * Prepend a value to each item added/selected. - * - * **Input types affected:** text, select-one, select-multiple - * - * @default null - */ - prependValue: string | null; - - /** - * Append a value to each item added/selected. - * - * **Input types affected:** text, select-one, select-multiple - * - * @default null - */ - appendValue: string | null; - - /** - * Whether selected choices should be removed from the list. By default choices are removed when they are selected in multiple select box. To always render choices pass `always`. - * - * **Input types affected:** select-one, select-multiple - * - * @default 'auto'; - */ - renderSelectedChoices: 'auto' | 'always'; - - /** - * The text that is shown whilst choices are being populated via AJAX. - * - * **Input types affected:** select-one, select-multiple - * - * @default 'Loading...' - */ - loadingText: string; - - /** - * The text that is shown when a user's search has returned no results. Optionally pass a function returning a string. - * - * **Input types affected:** select-one, select-multiple - * - * @default 'No results found' - */ - noResultsText: string | Choices.Types.stringFunction; - - /** - * The text that is shown when a user has selected all possible choices. Optionally pass a function returning a string. - * - * **Input types affected:** select-multiple - * - * @default 'No choices to choose from' - */ - noChoicesText: string | Choices.Types.stringFunction; - - /** - * The text that is shown when a user hovers over a selectable choice. - * - * **Input types affected:** select-multiple, select-one - * - * @default 'Press to select' - */ - itemSelectText: string; - - /** - * The text that is shown when a user has focus on the input but has already reached the **max item count** [https://github.com/jshjohnson/Choices#maxitemcount]. To access the max item count, pass a function with a `maxItemCount` argument (see the **default config** [https://github.com/jshjohnson/Choices#setup] for an example), otherwise pass a string. - * - * **Input types affected:** text - * - * @default - * ``` - * (maxItemCount) => `Only ${maxItemCount} values can be added.`; - * ``` - */ - maxItemText: string | Choices.Types.noticeLimitFunction; - - /** - * If no duplicates are allowed, and the value already exists in the array. - * - * @default 'Only unique values can be added' - */ - uniqueItemText: string | Choices.Types.noticeStringFunction; - - /** - * The text that is shown when addItemFilter is passed and it returns false - * - * **Input types affected:** text - * - * @default 'Only values matching specific conditions can be added' - */ - customAddItemText: string | Choices.Types.noticeStringFunction; - - /** - * Compare choice and value in appropriate way (e.g. deep equality for objects). To compare choice and value, pass a function with a `valueComparer` argument (see the [default config](https://github.com/jshjohnson/Choices#setup) for an example). - * - * **Input types affected:** select-one, select-multiple - * - * @default - * ``` - * (choice, item) => choice === item; - * ``` - */ - valueComparer: Choices.Types.valueCompareFunction; - - /** - * Classes added to HTML generated by Choices. By default classnames follow the BEM notation. - * - * **Input types affected:** text, select-one, select-multiple - */ - classNames: Choices.ClassNames; - - /** - * Choices uses the great Fuse library for searching. You can find more options here: https://github.com/krisk/Fuse#options - */ - fuseOptions: FuseOptions; - - /** - * Function to run once Choices initialises. - * - * **Input types affected:** text, select-one, select-multiple - * - * @note For each callback, this refers to the current instance of Choices. This can be useful if you need access to methods `(this.disable())` or the config object `(this.config)`. - * - * @default null - */ - callbackOnInit: ((this: Choices) => void) | null; - - /** - * Function to run on template creation. Through this callback it is possible to provide custom templates for the various components of Choices (see terminology). For Choices to work with custom templates, it is important you maintain the various data attributes defined here [https://github.com/jshjohnson/Choices/blob/67f29c286aa21d88847adfcd6304dc7d068dc01f/assets/scripts/src/choices.js#L1993-L2067]. - * - * **Input types affected:** text, select-one, select-multiple - * - * @note For each callback, this refers to the current instance of Choices. This can be useful if you need access to methods `(this.disable())` or the config object `(this.config)`. - * - * @example - * ``` - * const example = new Choices(element, { - * callbackOnCreateTemplates: function (template) { - * var classNames = this.config.classNames; - * return { - * item: (data) => { - * return template(` - *
- * ${data.label} - *
- * `); - * }, - * choice: (data) => { - * return template(` - *
0 ? 'role="treeitem"' : 'role="option"'}> - * ${data.label} - *
- * `); - * }, - * }; - * } - * }); - * ``` - * - * @default null - */ - callbackOnCreateTemplates: - | ((template: Choices.Types.strToEl) => Partial) - | null; - } - - interface KeyDownAction { - event: KeyboardEvent; - activeItems: Item[]; - hasFocusedInput: boolean; - hasActiveDropdown: boolean; - hasItems: boolean; - } -} - -// Exporting default class -export default class Choices { - static readonly defaults: { - readonly options: Partial; - readonly templates: Choices.Templates; - }; - readonly config: Choices.Options; - - // State Tracking - initialised: boolean; - - // Element - readonly passedElement: Choices.passedElement; - - placeholder: boolean; - - constructor( - selectorOrElement: string | HTMLInputElement | HTMLSelectElement, - userConfig?: Partial, - ); - - /** - * Creates a new instance of Choices, adds event listeners, creates templates and renders a Choices element to the DOM. - * - * @note This is called implicitly when a new instance of Choices is created. This would be used after a Choices instance had already been destroyed `(using destroy())`. - * - * **Input types affected:** text, select-multiple, select-one - */ - init(): void; - - /** - * Kills the instance of Choices, removes all event listeners and returns passed input to its initial state. - * - * **Input types affected:** text, select-multiple, select-one - */ - destroy(): void; - - /** Select item (a selected item can be deleted) */ - highlightItem(item: Element, runEvent?: boolean): this; - - /** Deselect item */ - unhighlightItem(item: Element): this; - - /** - * Highlight each chosen item (selected items can be removed). - * - * **Input types affected:** text, select-multiple - */ - highlightAll(): this; - - /** - * Un-highlight each chosen item. - * - * **Input types affected:** text, select-multiple - */ - unhighlightAll(): this; - - /** - * Remove each item by a given value. - * - * **Input types affected:** text, select-multiple - */ - removeActiveItemsByValue(value: string): this; - - /** - * Remove each selectable item. - * - * **Input types affected:** text, select-multiple - */ - removeActiveItems(excludedId: number): this; - - /** - * Remove each item the user has selected. - * - * **Input types affected:** text, select-multiple - */ - removeHighlightedItems(runEvent?: boolean): this; - - /** - * Show option list dropdown (only affects select inputs). - * - * **Input types affected:** select-one, select-multiple - */ - showDropdown(focusInput?: boolean): this; - - /** - * Hide option list dropdown (only affects select inputs). - * - * **Input types affected:** text, select-multiple - */ - hideDropdown(blurInput?: boolean): this; - - /** - * Get value(s) of input (i.e. inputted items (text) or selected choices (select)). Optionally pass an argument of `true` to only return values rather than value objects. - * - * **Input types affected:** text, select-one, select-multiple - * - * @example - * ``` - * const example = new Choices(element); - * const values = example.getValue(true); // returns ['value 1', 'value 2']; - * const valueArray = example.getValue(); // returns [{ active: true, choiceId: 1, highlighted: false, id: 1, label: 'Label 1', value: 'Value 1'}, { active: true, choiceId: 2, highlighted: false, id: 2, label: 'Label 2', value: 'Value 2'}]; - * ``` - */ - getValue(valueOnly?: boolean): string | string[]; - - /** Direct populate choices - * - * @param {string[] | Choices.Item[]} items - */ - setValue(items: string[] | Choices.Item[]): this; - - /** - * Set value of input based on existing Choice. `value` can be either a single string or an array of strings - * - * **Input types affected:** select-one, select-multiple - * - * @example - * ``` - * const example = new Choices(element, { - * choices: [ - * {value: 'One', label: 'Label One'}, - * {value: 'Two', label: 'Label Two', disabled: true}, - * {value: 'Three', label: 'Label Three'}, - * ], - * }); - * - * example.setChoiceByValue('Two'); // Choice with value of 'Two' has now been selected. - * ``` - */ - setChoiceByValue(value: string | string[]): this; - - /** - * Set choices of select input via an array of objects (or function that returns array of object or promise of it), - * a value field name and a label field name. - * This behaves the same as passing items via the choices option but can be called after initialising Choices. - * This can also be used to add groups of choices (see example 2); Optionally pass a true `replaceChoices` value to remove any existing choices. - * Optionally pass a `customProperties` object to add additional data to your choices (useful when searching/filtering etc). - * - * **Input types affected:** select-one, select-multiple - * - * @param {string} [value = 'value'] - name of `value` field - * @param {string} [label = 'label'] - name of 'label' field - * @param {boolean} [replaceChoices = false] - whether to replace of add choices - * - * @example - * ```js - * const example = new Choices(element); - * - * example.setChoices([ - * {value: 'One', label: 'Label One', disabled: true}, - * {value: 'Two', label: 'Label Two', selected: true}, - * {value: 'Three', label: 'Label Three'}, - * ], 'value', 'label', false); - * ``` - * - * @example - * ```js - * const example = new Choices(element); - * - * example.setChoices(async () => { - * try { - * const items = await fetch('/items'); - * return items.json() - * } catch(err) { - * console.error(err) - * } - * }); - * ``` - * - * @example - * ```js - * const example = new Choices(element); - * - * example.setChoices([{ - * label: 'Group one', - * id: 1, - * disabled: false, - * choices: [ - * {value: 'Child One', label: 'Child One', selected: true}, - * {value: 'Child Two', label: 'Child Two', disabled: true}, - * {value: 'Child Three', label: 'Child Three'}, - * ] - * }, - * { - * label: 'Group two', - * id: 2, - * disabled: false, - * choices: [ - * {value: 'Child Four', label: 'Child Four', disabled: true}, - * {value: 'Child Five', label: 'Child Five'}, - * {value: 'Child Six', label: 'Child Six', customProperties: { - * description: 'Custom description about child six', - * random: 'Another random custom property' - * }}, - * ] - * }], 'value', 'label', false); - * ``` - */ - setChoices< - T extends object[] | ((instance: Choices) => object[] | Promise) - >( - choices: T, - value?: string, - label?: string, - replaceChoices?: boolean, - ): T extends object[] ? this : Promise; - - /** - * Clear all choices from select. - * - * **Input types affected:** select-one, select-multiple - */ - clearChoices(): this; - - /** - * Removes all items, choices and groups. Use with caution. - * - * **Input types affected:** text, select-one, select-multiple - */ - clearStore(): this; - - /** - * Clear input of any user inputted text. - * - * **Input types affected:** text - */ - clearInput(): this; - - /** - * Enables input to accept new values/select further choices. - * - * **Input types affected:** text, select-one, select-multiple - */ - enable(): this; - - /** - * Disables input from accepting new value/selecting further choices. - * - * **Input types affected:** text, select-one, select-multiple - */ - disable(): this; - - _onEnterKey(keyDownAction: Partial): void; - _onAKey(keyDownAction: Partial): void; - _onEscapeKey(keyDownAction: Partial): void; - _onDirectionKey(keyDownAction: Partial): void; - _onDeleteKey(keyDownAction: Partial): void; -} diff --git a/webpack.config.base.js b/webpack.config.base.js index 44e4438d9..9372e776e 100644 --- a/webpack.config.base.js +++ b/webpack.config.base.js @@ -8,12 +8,6 @@ const exclude = /node_modules/; */ module.exports = { entry: ['./src/scripts/choices'], - output: { - library: 'Choices', - libraryTarget: 'window', - libraryExport: 'default', - globalObject: 'window', - }, module: { rules: [ { @@ -28,13 +22,28 @@ module.exports = { }, { loader: 'babel-loader', - test: /\.js?$/, + test: /\.ts?$/, include, exclude, options: { babelrc: true, }, }, + { + loader: 'ts-loader', + test: /\.ts?$/, + include, + exclude + } ], }, + resolve: { + extensions: [".ts", ".js"] + }, + output: { + library: 'Choices', + libraryTarget: 'window', + libraryExport: 'default', + globalObject: 'window', + }, };