From b69ce0ef298e52e0197bf9a70f14ec1e14662f7f Mon Sep 17 00:00:00 2001 From: unclead Date: Sun, 21 Apr 2024 13:52:42 +0000 Subject: [PATCH] Deployed 7022e48 with MkDocs version: 1.4.2 --- .nojekyll | 0 404.html | 418 + assets/images/favicon.png | Bin 0 -> 1870 bytes assets/javascripts/bundle.51d95adb.min.js | 29 + assets/javascripts/bundle.51d95adb.min.js.map | 8 + assets/javascripts/lunr/min/lunr.ar.min.js | 1 + assets/javascripts/lunr/min/lunr.da.min.js | 18 + assets/javascripts/lunr/min/lunr.de.min.js | 18 + assets/javascripts/lunr/min/lunr.du.min.js | 18 + assets/javascripts/lunr/min/lunr.es.min.js | 18 + assets/javascripts/lunr/min/lunr.fi.min.js | 18 + assets/javascripts/lunr/min/lunr.fr.min.js | 18 + assets/javascripts/lunr/min/lunr.hi.min.js | 1 + assets/javascripts/lunr/min/lunr.hu.min.js | 18 + assets/javascripts/lunr/min/lunr.it.min.js | 18 + assets/javascripts/lunr/min/lunr.ja.min.js | 1 + assets/javascripts/lunr/min/lunr.jp.min.js | 1 + assets/javascripts/lunr/min/lunr.ko.min.js | 1 + assets/javascripts/lunr/min/lunr.multi.min.js | 1 + assets/javascripts/lunr/min/lunr.nl.min.js | 18 + assets/javascripts/lunr/min/lunr.no.min.js | 18 + assets/javascripts/lunr/min/lunr.pt.min.js | 18 + assets/javascripts/lunr/min/lunr.ro.min.js | 18 + assets/javascripts/lunr/min/lunr.ru.min.js | 18 + .../lunr/min/lunr.stemmer.support.min.js | 1 + assets/javascripts/lunr/min/lunr.sv.min.js | 18 + assets/javascripts/lunr/min/lunr.ta.min.js | 1 + assets/javascripts/lunr/min/lunr.th.min.js | 1 + assets/javascripts/lunr/min/lunr.tr.min.js | 18 + assets/javascripts/lunr/min/lunr.vi.min.js | 1 + assets/javascripts/lunr/min/lunr.zh.min.js | 1 + assets/javascripts/lunr/tinyseg.js | 206 + assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.e5c33ebb.min.js | 42 + .../workers/search.e5c33ebb.min.js.map | 8 + assets/stylesheets/main.558e4712.min.css | 1 + assets/stylesheets/main.558e4712.min.css.map | 1 + assets/stylesheets/palette.2505c338.min.css | 1 + .../stylesheets/palette.2505c338.min.css.map | 1 + clonning/index.html | 492 ++ configuration/index.html | 637 ++ getting-started/index.html | 609 ++ icons/index.html | 484 ++ index.html | 503 ++ installation/index.html | 459 ++ javascript-events/index.html | 626 ++ renderers/index.html | 569 ++ search/search_index.json | 1 + sitemap.xml | 53 + sitemap.xml.gz | Bin 0 -> 204 bytes tips-and-tricks/index.html | 788 ++ usage/index.html | 915 +++ 52 files changed, 13840 insertions(+) create mode 100644 .nojekyll create mode 100644 404.html create mode 100644 assets/images/favicon.png create mode 100644 assets/javascripts/bundle.51d95adb.min.js create mode 100644 assets/javascripts/bundle.51d95adb.min.js.map create mode 100644 assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 assets/javascripts/lunr/tinyseg.js create mode 100644 assets/javascripts/lunr/wordcut.js create mode 100644 assets/javascripts/workers/search.e5c33ebb.min.js create mode 100644 assets/javascripts/workers/search.e5c33ebb.min.js.map create mode 100644 assets/stylesheets/main.558e4712.min.css create mode 100644 assets/stylesheets/main.558e4712.min.css.map create mode 100644 assets/stylesheets/palette.2505c338.min.css create mode 100644 assets/stylesheets/palette.2505c338.min.css.map create mode 100644 clonning/index.html create mode 100644 configuration/index.html create mode 100644 getting-started/index.html create mode 100644 icons/index.html create mode 100644 index.html create mode 100644 installation/index.html create mode 100644 javascript-events/index.html create mode 100644 renderers/index.html create mode 100644 search/search_index.json create mode 100644 sitemap.xml create mode 100644 sitemap.xml.gz create mode 100644 tips-and-tricks/index.html create mode 100644 usage/index.html diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..e78adf4 --- /dev/null +++ b/404.html @@ -0,0 +1,418 @@ + + + + + + + + + + + + + + + + + + + + + + Yii2 multiple input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ +

404 - Not found

+ +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..1cf13b9f9d978896599290a74f77d5dbe7d1655c GIT binary patch literal 1870 zcmV-U2eJ5xP)Gc)JR9QMau)O=X#!i9;T z37kk-upj^(fsR36MHs_+1RCI)NNu9}lD0S{B^g8PN?Ww(5|~L#Ng*g{WsqleV}|#l zz8@ri&cTzw_h33bHI+12+kK6WN$h#n5cD8OQt`5kw6p~9H3()bUQ8OS4Q4HTQ=1Ol z_JAocz`fLbT2^{`8n~UAo=#AUOf=SOq4pYkt;XbC&f#7lb$*7=$na!mWCQ`dBQsO0 zLFBSPj*N?#u5&pf2t4XjEGH|=pPQ8xh7tpx;US5Cx_Ju;!O`ya-yF`)b%TEt5>eP1ZX~}sjjA%FJF?h7cX8=b!DZl<6%Cv z*G0uvvU+vmnpLZ2paivG-(cd*y3$hCIcsZcYOGh{$&)A6*XX&kXZd3G8m)G$Zz-LV z^GF3VAW^Mdv!)4OM8EgqRiz~*Cji;uzl2uC9^=8I84vNp;ltJ|q-*uQwGp2ma6cY7 z;`%`!9UXO@fr&Ebapfs34OmS9^u6$)bJxrucutf>`dKPKT%%*d3XlFVKunp9 zasduxjrjs>f8V=D|J=XNZp;_Zy^WgQ$9WDjgY=z@stwiEBm9u5*|34&1Na8BMjjgf3+SHcr`5~>oz1Y?SW^=K z^bTyO6>Gar#P_W2gEMwq)ot3; zREHn~U&Dp0l6YT0&k-wLwYjb?5zGK`W6S2v+K>AM(95m2C20L|3m~rN8dprPr@t)5lsk9Hu*W z?pS990s;Ez=+Rj{x7p``4>+c0G5^pYnB1^!TL=(?HLHZ+HicG{~4F1d^5Awl_2!1jICM-!9eoLhbbT^;yHcefyTAaqRcY zmuctDopPT!%k+}x%lZRKnzykr2}}XfG_ne?nRQO~?%hkzo;@RN{P6o`&mMUWBYMTe z6i8ChtjX&gXl`nvrU>jah)2iNM%JdjqoaeaU%yVn!^70x-flljp6Q5tK}5}&X8&&G zX3fpb3E(!rH=zVI_9Gjl45w@{(ITqngWFe7@9{mX;tO25Z_8 zQHEpI+FkTU#4xu>RkN>b3Tnc3UpWzPXWm#o55GKF09j^Mh~)K7{QqbO_~(@CVq! zS<8954|P8mXN2MRs86xZ&Q4EfM@JB94b=(YGuk)s&^jiSF=t3*oNK3`rD{H`yQ?d; ztE=laAUoZx5?RC8*WKOj`%LXEkgDd>&^Q4M^z`%u0rg-It=hLCVsq!Z%^6eB-OvOT zFZ28TN&cRmgU}Elrnk43)!>Z1FCPL2K$7}gwzIc48NX}#!A1BpJP?#v5wkNprhV** z?Cpalt1oH&{r!o3eSKc&ap)iz2BTn_VV`4>9M^b3;(YY}4>#ML6{~(4mH+?%07*qo IM6N<$f(jP3KmY&$ literal 0 HcmV?d00001 diff --git a/assets/javascripts/bundle.51d95adb.min.js b/assets/javascripts/bundle.51d95adb.min.js new file mode 100644 index 0000000..b20ec68 --- /dev/null +++ b/assets/javascripts/bundle.51d95adb.min.js @@ -0,0 +1,29 @@ +"use strict";(()=>{var Hi=Object.create;var xr=Object.defineProperty;var Pi=Object.getOwnPropertyDescriptor;var $i=Object.getOwnPropertyNames,kt=Object.getOwnPropertySymbols,Ii=Object.getPrototypeOf,Er=Object.prototype.hasOwnProperty,an=Object.prototype.propertyIsEnumerable;var on=(e,t,r)=>t in e?xr(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Er.call(t,r)&&on(e,r,t[r]);if(kt)for(var r of kt(t))an.call(t,r)&&on(e,r,t[r]);return e};var sn=(e,t)=>{var r={};for(var n in e)Er.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(e!=null&&kt)for(var n of kt(e))t.indexOf(n)<0&&an.call(e,n)&&(r[n]=e[n]);return r};var Ht=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Fi=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of $i(t))!Er.call(e,o)&&o!==r&&xr(e,o,{get:()=>t[o],enumerable:!(n=Pi(t,o))||n.enumerable});return e};var yt=(e,t,r)=>(r=e!=null?Hi(Ii(e)):{},Fi(t||!e||!e.__esModule?xr(r,"default",{value:e,enumerable:!0}):r,e));var fn=Ht((wr,cn)=>{(function(e,t){typeof wr=="object"&&typeof cn!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(wr,function(){"use strict";function e(r){var n=!0,o=!1,i=null,a={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function s(T){return!!(T&&T!==document&&T.nodeName!=="HTML"&&T.nodeName!=="BODY"&&"classList"in T&&"contains"in T.classList)}function f(T){var Ke=T.type,We=T.tagName;return!!(We==="INPUT"&&a[Ke]&&!T.readOnly||We==="TEXTAREA"&&!T.readOnly||T.isContentEditable)}function c(T){T.classList.contains("focus-visible")||(T.classList.add("focus-visible"),T.setAttribute("data-focus-visible-added",""))}function u(T){T.hasAttribute("data-focus-visible-added")&&(T.classList.remove("focus-visible"),T.removeAttribute("data-focus-visible-added"))}function p(T){T.metaKey||T.altKey||T.ctrlKey||(s(r.activeElement)&&c(r.activeElement),n=!0)}function m(T){n=!1}function d(T){s(T.target)&&(n||f(T.target))&&c(T.target)}function h(T){s(T.target)&&(T.target.classList.contains("focus-visible")||T.target.hasAttribute("data-focus-visible-added"))&&(o=!0,window.clearTimeout(i),i=window.setTimeout(function(){o=!1},100),u(T.target))}function v(T){document.visibilityState==="hidden"&&(o&&(n=!0),B())}function B(){document.addEventListener("mousemove",z),document.addEventListener("mousedown",z),document.addEventListener("mouseup",z),document.addEventListener("pointermove",z),document.addEventListener("pointerdown",z),document.addEventListener("pointerup",z),document.addEventListener("touchmove",z),document.addEventListener("touchstart",z),document.addEventListener("touchend",z)}function re(){document.removeEventListener("mousemove",z),document.removeEventListener("mousedown",z),document.removeEventListener("mouseup",z),document.removeEventListener("pointermove",z),document.removeEventListener("pointerdown",z),document.removeEventListener("pointerup",z),document.removeEventListener("touchmove",z),document.removeEventListener("touchstart",z),document.removeEventListener("touchend",z)}function z(T){T.target.nodeName&&T.target.nodeName.toLowerCase()==="html"||(n=!1,re())}document.addEventListener("keydown",p,!0),document.addEventListener("mousedown",m,!0),document.addEventListener("pointerdown",m,!0),document.addEventListener("touchstart",m,!0),document.addEventListener("visibilitychange",v,!0),B(),r.addEventListener("focus",d,!0),r.addEventListener("blur",h,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)})});var un=Ht(Sr=>{(function(e){var t=function(){try{return!!Symbol.iterator}catch(c){return!1}},r=t(),n=function(c){var u={next:function(){var p=c.shift();return{done:p===void 0,value:p}}};return r&&(u[Symbol.iterator]=function(){return u}),u},o=function(c){return encodeURIComponent(c).replace(/%20/g,"+")},i=function(c){return decodeURIComponent(String(c).replace(/\+/g," "))},a=function(){var c=function(p){Object.defineProperty(this,"_entries",{writable:!0,value:{}});var m=typeof p;if(m!=="undefined")if(m==="string")p!==""&&this._fromString(p);else if(p instanceof c){var d=this;p.forEach(function(re,z){d.append(z,re)})}else if(p!==null&&m==="object")if(Object.prototype.toString.call(p)==="[object Array]")for(var h=0;hd[0]?1:0}),c._entries&&(c._entries={});for(var p=0;p1?i(d[1]):"")}})})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr);(function(e){var t=function(){try{var o=new e.URL("b","http://a");return o.pathname="c d",o.href==="http://a/c%20d"&&o.searchParams}catch(i){return!1}},r=function(){var o=e.URL,i=function(f,c){typeof f!="string"&&(f=String(f)),c&&typeof c!="string"&&(c=String(c));var u=document,p;if(c&&(e.location===void 0||c!==e.location.href)){c=c.toLowerCase(),u=document.implementation.createHTMLDocument(""),p=u.createElement("base"),p.href=c,u.head.appendChild(p);try{if(p.href.indexOf(c)!==0)throw new Error(p.href)}catch(T){throw new Error("URL unable to set base "+c+" due to "+T)}}var m=u.createElement("a");m.href=f,p&&(u.body.appendChild(m),m.href=m.href);var d=u.createElement("input");if(d.type="url",d.value=f,m.protocol===":"||!/:/.test(m.href)||!d.checkValidity()&&!c)throw new TypeError("Invalid URL");Object.defineProperty(this,"_anchorElement",{value:m});var h=new e.URLSearchParams(this.search),v=!0,B=!0,re=this;["append","delete","set"].forEach(function(T){var Ke=h[T];h[T]=function(){Ke.apply(h,arguments),v&&(B=!1,re.search=h.toString(),B=!0)}}),Object.defineProperty(this,"searchParams",{value:h,enumerable:!0});var z=void 0;Object.defineProperty(this,"_updateSearchParams",{enumerable:!1,configurable:!1,writable:!1,value:function(){this.search!==z&&(z=this.search,B&&(v=!1,this.searchParams._fromString(this.search),v=!0))}})},a=i.prototype,s=function(f){Object.defineProperty(a,f,{get:function(){return this._anchorElement[f]},set:function(c){this._anchorElement[f]=c},enumerable:!0})};["hash","host","hostname","port","protocol"].forEach(function(f){s(f)}),Object.defineProperty(a,"search",{get:function(){return this._anchorElement.search},set:function(f){this._anchorElement.search=f,this._updateSearchParams()},enumerable:!0}),Object.defineProperties(a,{toString:{get:function(){var f=this;return function(){return f.href}}},href:{get:function(){return this._anchorElement.href.replace(/\?$/,"")},set:function(f){this._anchorElement.href=f,this._updateSearchParams()},enumerable:!0},pathname:{get:function(){return this._anchorElement.pathname.replace(/(^\/?)/,"/")},set:function(f){this._anchorElement.pathname=f},enumerable:!0},origin:{get:function(){var f={"http:":80,"https:":443,"ftp:":21}[this._anchorElement.protocol],c=this._anchorElement.port!=f&&this._anchorElement.port!=="";return this._anchorElement.protocol+"//"+this._anchorElement.hostname+(c?":"+this._anchorElement.port:"")},enumerable:!0},password:{get:function(){return""},set:function(f){},enumerable:!0},username:{get:function(){return""},set:function(f){},enumerable:!0}}),i.createObjectURL=function(f){return o.createObjectURL.apply(o,arguments)},i.revokeObjectURL=function(f){return o.revokeObjectURL.apply(o,arguments)},e.URL=i};if(t()||r(),e.location!==void 0&&!("origin"in e.location)){var n=function(){return e.location.protocol+"//"+e.location.hostname+(e.location.port?":"+e.location.port:"")};try{Object.defineProperty(e.location,"origin",{get:n,enumerable:!0})}catch(o){setInterval(function(){e.location.origin=n()},100)}}})(typeof global!="undefined"?global:typeof window!="undefined"?window:typeof self!="undefined"?self:Sr)});var Qr=Ht((Lt,Kr)=>{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Lt=="object"&&typeof Kr=="object"?Kr.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Lt=="object"?Lt.ClipboardJS=r():t.ClipboardJS=r()})(Lt,function(){return function(){var e={686:function(n,o,i){"use strict";i.d(o,{default:function(){return ki}});var a=i(279),s=i.n(a),f=i(370),c=i.n(f),u=i(817),p=i.n(u);function m(j){try{return document.execCommand(j)}catch(O){return!1}}var d=function(O){var w=p()(O);return m("cut"),w},h=d;function v(j){var O=document.documentElement.getAttribute("dir")==="rtl",w=document.createElement("textarea");w.style.fontSize="12pt",w.style.border="0",w.style.padding="0",w.style.margin="0",w.style.position="absolute",w.style[O?"right":"left"]="-9999px";var k=window.pageYOffset||document.documentElement.scrollTop;return w.style.top="".concat(k,"px"),w.setAttribute("readonly",""),w.value=j,w}var B=function(O,w){var k=v(O);w.container.appendChild(k);var F=p()(k);return m("copy"),k.remove(),F},re=function(O){var w=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},k="";return typeof O=="string"?k=B(O,w):O instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(O==null?void 0:O.type)?k=B(O.value,w):(k=p()(O),m("copy")),k},z=re;function T(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?T=function(w){return typeof w}:T=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},T(j)}var Ke=function(){var O=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},w=O.action,k=w===void 0?"copy":w,F=O.container,q=O.target,Le=O.text;if(k!=="copy"&&k!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(q!==void 0)if(q&&T(q)==="object"&&q.nodeType===1){if(k==="copy"&&q.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(k==="cut"&&(q.hasAttribute("readonly")||q.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(Le)return z(Le,{container:F});if(q)return k==="cut"?h(q):z(q,{container:F})},We=Ke;function Ie(j){return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?Ie=function(w){return typeof w}:Ie=function(w){return w&&typeof Symbol=="function"&&w.constructor===Symbol&&w!==Symbol.prototype?"symbol":typeof w},Ie(j)}function Ti(j,O){if(!(j instanceof O))throw new TypeError("Cannot call a class as a function")}function nn(j,O){for(var w=0;w0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof F.action=="function"?F.action:this.defaultAction,this.target=typeof F.target=="function"?F.target:this.defaultTarget,this.text=typeof F.text=="function"?F.text:this.defaultText,this.container=Ie(F.container)==="object"?F.container:document.body}},{key:"listenClick",value:function(F){var q=this;this.listener=c()(F,"click",function(Le){return q.onClick(Le)})}},{key:"onClick",value:function(F){var q=F.delegateTarget||F.currentTarget,Le=this.action(q)||"copy",Rt=We({action:Le,container:this.container,target:this.target(q),text:this.text(q)});this.emit(Rt?"success":"error",{action:Le,text:Rt,trigger:q,clearSelection:function(){q&&q.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(F){return yr("action",F)}},{key:"defaultTarget",value:function(F){var q=yr("target",F);if(q)return document.querySelector(q)}},{key:"defaultText",value:function(F){return yr("text",F)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(F){var q=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return z(F,q)}},{key:"cut",value:function(F){return h(F)}},{key:"isSupported",value:function(){var F=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],q=typeof F=="string"?[F]:F,Le=!!document.queryCommandSupported;return q.forEach(function(Rt){Le=Le&&!!document.queryCommandSupported(Rt)}),Le}}]),w}(s()),ki=Ri},828:function(n){var o=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function a(s,f){for(;s&&s.nodeType!==o;){if(typeof s.matches=="function"&&s.matches(f))return s;s=s.parentNode}}n.exports=a},438:function(n,o,i){var a=i(828);function s(u,p,m,d,h){var v=c.apply(this,arguments);return u.addEventListener(m,v,h),{destroy:function(){u.removeEventListener(m,v,h)}}}function f(u,p,m,d,h){return typeof u.addEventListener=="function"?s.apply(null,arguments):typeof m=="function"?s.bind(null,document).apply(null,arguments):(typeof u=="string"&&(u=document.querySelectorAll(u)),Array.prototype.map.call(u,function(v){return s(v,p,m,d,h)}))}function c(u,p,m,d){return function(h){h.delegateTarget=a(h.target,p),h.delegateTarget&&d.call(u,h)}}n.exports=f},879:function(n,o){o.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},o.nodeList=function(i){var a=Object.prototype.toString.call(i);return i!==void 0&&(a==="[object NodeList]"||a==="[object HTMLCollection]")&&"length"in i&&(i.length===0||o.node(i[0]))},o.string=function(i){return typeof i=="string"||i instanceof String},o.fn=function(i){var a=Object.prototype.toString.call(i);return a==="[object Function]"}},370:function(n,o,i){var a=i(879),s=i(438);function f(m,d,h){if(!m&&!d&&!h)throw new Error("Missing required arguments");if(!a.string(d))throw new TypeError("Second argument must be a String");if(!a.fn(h))throw new TypeError("Third argument must be a Function");if(a.node(m))return c(m,d,h);if(a.nodeList(m))return u(m,d,h);if(a.string(m))return p(m,d,h);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function c(m,d,h){return m.addEventListener(d,h),{destroy:function(){m.removeEventListener(d,h)}}}function u(m,d,h){return Array.prototype.forEach.call(m,function(v){v.addEventListener(d,h)}),{destroy:function(){Array.prototype.forEach.call(m,function(v){v.removeEventListener(d,h)})}}}function p(m,d,h){return s(document.body,m,d,h)}n.exports=f},817:function(n){function o(i){var a;if(i.nodeName==="SELECT")i.focus(),a=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var s=i.hasAttribute("readonly");s||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),s||i.removeAttribute("readonly"),a=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var f=window.getSelection(),c=document.createRange();c.selectNodeContents(i),f.removeAllRanges(),f.addRange(c),a=f.toString()}return a}n.exports=o},279:function(n){function o(){}o.prototype={on:function(i,a,s){var f=this.e||(this.e={});return(f[i]||(f[i]=[])).push({fn:a,ctx:s}),this},once:function(i,a,s){var f=this;function c(){f.off(i,c),a.apply(s,arguments)}return c._=a,this.on(i,c,s)},emit:function(i){var a=[].slice.call(arguments,1),s=((this.e||(this.e={}))[i]||[]).slice(),f=0,c=s.length;for(f;f{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var is=/["'&<>]/;Jo.exports=as;function as(e){var t=""+e,r=is.exec(t);if(!r)return t;var n,o="",i=0,a=0;for(i=r.index;i0&&i[i.length-1])&&(c[0]===6||c[0]===2)){r=0;continue}if(c[0]===3&&(!i||c[1]>i[0]&&c[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function W(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var n=r.call(e),o,i=[],a;try{for(;(t===void 0||t-- >0)&&!(o=n.next()).done;)i.push(o.value)}catch(s){a={error:s}}finally{try{o&&!o.done&&(r=n.return)&&r.call(n)}finally{if(a)throw a.error}}return i}function D(e,t,r){if(r||arguments.length===2)for(var n=0,o=t.length,i;n1||s(m,d)})})}function s(m,d){try{f(n[m](d))}catch(h){p(i[0][3],h)}}function f(m){m.value instanceof Xe?Promise.resolve(m.value.v).then(c,u):p(i[0][2],m)}function c(m){s("next",m)}function u(m){s("throw",m)}function p(m,d){m(d),i.shift(),i.length&&s(i[0][0],i[0][1])}}function mn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof xe=="function"?xe(e):e[Symbol.iterator](),r={},n("next"),n("throw"),n("return"),r[Symbol.asyncIterator]=function(){return this},r);function n(i){r[i]=e[i]&&function(a){return new Promise(function(s,f){a=e[i](a),o(s,f,a.done,a.value)})}}function o(i,a,s,f){Promise.resolve(f).then(function(c){i({value:c,done:s})},a)}}function A(e){return typeof e=="function"}function at(e){var t=function(n){Error.call(n),n.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var $t=at(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(n,o){return o+1+") "+n.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function De(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var Fe=function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,n,o,i;if(!this.closed){this.closed=!0;var a=this._parentage;if(a)if(this._parentage=null,Array.isArray(a))try{for(var s=xe(a),f=s.next();!f.done;f=s.next()){var c=f.value;c.remove(this)}}catch(v){t={error:v}}finally{try{f&&!f.done&&(r=s.return)&&r.call(s)}finally{if(t)throw t.error}}else a.remove(this);var u=this.initialTeardown;if(A(u))try{u()}catch(v){i=v instanceof $t?v.errors:[v]}var p=this._finalizers;if(p){this._finalizers=null;try{for(var m=xe(p),d=m.next();!d.done;d=m.next()){var h=d.value;try{dn(h)}catch(v){i=i!=null?i:[],v instanceof $t?i=D(D([],W(i)),W(v.errors)):i.push(v)}}}catch(v){n={error:v}}finally{try{d&&!d.done&&(o=m.return)&&o.call(m)}finally{if(n)throw n.error}}}if(i)throw new $t(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)dn(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&De(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&De(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=function(){var t=new e;return t.closed=!0,t}(),e}();var Or=Fe.EMPTY;function It(e){return e instanceof Fe||e&&"closed"in e&&A(e.remove)&&A(e.add)&&A(e.unsubscribe)}function dn(e){A(e)?e():e.unsubscribe()}var Ae={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var st={setTimeout:function(e,t){for(var r=[],n=2;n0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var n=this,o=this,i=o.hasError,a=o.isStopped,s=o.observers;return i||a?Or:(this.currentObservers=null,s.push(r),new Fe(function(){n.currentObservers=null,De(s,r)}))},t.prototype._checkFinalizedStatuses=function(r){var n=this,o=n.hasError,i=n.thrownError,a=n.isStopped;o?r.error(i):a&&r.complete()},t.prototype.asObservable=function(){var r=new U;return r.source=this,r},t.create=function(r,n){return new wn(r,n)},t}(U);var wn=function(e){ne(t,e);function t(r,n){var o=e.call(this)||this;return o.destination=r,o.source=n,o}return t.prototype.next=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.next)===null||o===void 0||o.call(n,r)},t.prototype.error=function(r){var n,o;(o=(n=this.destination)===null||n===void 0?void 0:n.error)===null||o===void 0||o.call(n,r)},t.prototype.complete=function(){var r,n;(n=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||n===void 0||n.call(r)},t.prototype._subscribe=function(r){var n,o;return(o=(n=this.source)===null||n===void 0?void 0:n.subscribe(r))!==null&&o!==void 0?o:Or},t}(E);var Et={now:function(){return(Et.delegate||Date).now()},delegate:void 0};var wt=function(e){ne(t,e);function t(r,n,o){r===void 0&&(r=1/0),n===void 0&&(n=1/0),o===void 0&&(o=Et);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=n,i._timestampProvider=o,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=n===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,n),i}return t.prototype.next=function(r){var n=this,o=n.isStopped,i=n._buffer,a=n._infiniteTimeWindow,s=n._timestampProvider,f=n._windowTime;o||(i.push(r),!a&&i.push(s.now()+f)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var n=this._innerSubscribe(r),o=this,i=o._infiniteTimeWindow,a=o._buffer,s=a.slice(),f=0;f0?e.prototype.requestAsyncId.call(this,r,n,o):(r.actions.push(this),r._scheduled||(r._scheduled=ut.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,n,o){var i;if(o===void 0&&(o=0),o!=null?o>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,n,o);var a=r.actions;n!=null&&((i=a[a.length-1])===null||i===void 0?void 0:i.id)!==n&&(ut.cancelAnimationFrame(n),r._scheduled=void 0)},t}(Ut);var On=function(e){ne(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var n=this._scheduled;this._scheduled=void 0;var o=this.actions,i;r=r||o.shift();do if(i=r.execute(r.state,r.delay))break;while((r=o[0])&&r.id===n&&o.shift());if(this._active=!1,i){for(;(r=o[0])&&r.id===n&&o.shift();)r.unsubscribe();throw i}},t}(Wt);var we=new On(Tn);var R=new U(function(e){return e.complete()});function Dt(e){return e&&A(e.schedule)}function kr(e){return e[e.length-1]}function Qe(e){return A(kr(e))?e.pop():void 0}function Se(e){return Dt(kr(e))?e.pop():void 0}function Vt(e,t){return typeof kr(e)=="number"?e.pop():t}var pt=function(e){return e&&typeof e.length=="number"&&typeof e!="function"};function zt(e){return A(e==null?void 0:e.then)}function Nt(e){return A(e[ft])}function qt(e){return Symbol.asyncIterator&&A(e==null?void 0:e[Symbol.asyncIterator])}function Kt(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function Ki(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var Qt=Ki();function Yt(e){return A(e==null?void 0:e[Qt])}function Gt(e){return ln(this,arguments,function(){var r,n,o,i;return Pt(this,function(a){switch(a.label){case 0:r=e.getReader(),a.label=1;case 1:a.trys.push([1,,9,10]),a.label=2;case 2:return[4,Xe(r.read())];case 3:return n=a.sent(),o=n.value,i=n.done,i?[4,Xe(void 0)]:[3,5];case 4:return[2,a.sent()];case 5:return[4,Xe(o)];case 6:return[4,a.sent()];case 7:return a.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function Bt(e){return A(e==null?void 0:e.getReader)}function $(e){if(e instanceof U)return e;if(e!=null){if(Nt(e))return Qi(e);if(pt(e))return Yi(e);if(zt(e))return Gi(e);if(qt(e))return _n(e);if(Yt(e))return Bi(e);if(Bt(e))return Ji(e)}throw Kt(e)}function Qi(e){return new U(function(t){var r=e[ft]();if(A(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function Yi(e){return new U(function(t){for(var r=0;r=2;return function(n){return n.pipe(e?_(function(o,i){return e(o,i,n)}):me,Oe(1),r?He(t):zn(function(){return new Xt}))}}function Nn(){for(var e=[],t=0;t=2,!0))}function fe(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new E}:t,n=e.resetOnError,o=n===void 0?!0:n,i=e.resetOnComplete,a=i===void 0?!0:i,s=e.resetOnRefCountZero,f=s===void 0?!0:s;return function(c){var u,p,m,d=0,h=!1,v=!1,B=function(){p==null||p.unsubscribe(),p=void 0},re=function(){B(),u=m=void 0,h=v=!1},z=function(){var T=u;re(),T==null||T.unsubscribe()};return g(function(T,Ke){d++,!v&&!h&&B();var We=m=m!=null?m:r();Ke.add(function(){d--,d===0&&!v&&!h&&(p=jr(z,f))}),We.subscribe(Ke),!u&&d>0&&(u=new et({next:function(Ie){return We.next(Ie)},error:function(Ie){v=!0,B(),p=jr(re,o,Ie),We.error(Ie)},complete:function(){h=!0,B(),p=jr(re,a),We.complete()}}),$(T).subscribe(u))})(c)}}function jr(e,t){for(var r=[],n=2;ne.next(document)),e}function K(e,t=document){return Array.from(t.querySelectorAll(e))}function V(e,t=document){let r=se(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function se(e,t=document){return t.querySelector(e)||void 0}function _e(){return document.activeElement instanceof HTMLElement&&document.activeElement||void 0}function tr(e){return L(b(document.body,"focusin"),b(document.body,"focusout")).pipe(ke(1),l(()=>{let t=_e();return typeof t!="undefined"?e.contains(t):!1}),N(e===_e()),Y())}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function Yn(e){return L(b(window,"load"),b(window,"resize")).pipe(Ce(0,we),l(()=>Be(e)),N(Be(e)))}function rr(e){return{x:e.scrollLeft,y:e.scrollTop}}function dt(e){return L(b(e,"scroll"),b(window,"resize")).pipe(Ce(0,we),l(()=>rr(e)),N(rr(e)))}var Bn=function(){if(typeof Map!="undefined")return Map;function e(t,r){var n=-1;return t.some(function(o,i){return o[0]===r?(n=i,!0):!1}),n}return function(){function t(){this.__entries__=[]}return Object.defineProperty(t.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),t.prototype.get=function(r){var n=e(this.__entries__,r),o=this.__entries__[n];return o&&o[1]},t.prototype.set=function(r,n){var o=e(this.__entries__,r);~o?this.__entries__[o][1]=n:this.__entries__.push([r,n])},t.prototype.delete=function(r){var n=this.__entries__,o=e(n,r);~o&&n.splice(o,1)},t.prototype.has=function(r){return!!~e(this.__entries__,r)},t.prototype.clear=function(){this.__entries__.splice(0)},t.prototype.forEach=function(r,n){n===void 0&&(n=null);for(var o=0,i=this.__entries__;o0},e.prototype.connect_=function(){!zr||this.connected_||(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),xa?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},e.prototype.disconnect_=function(){!zr||!this.connected_||(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},e.prototype.onTransitionEnd_=function(t){var r=t.propertyName,n=r===void 0?"":r,o=ya.some(function(i){return!!~n.indexOf(i)});o&&this.refresh()},e.getInstance=function(){return this.instance_||(this.instance_=new e),this.instance_},e.instance_=null,e}(),Jn=function(e,t){for(var r=0,n=Object.keys(t);r0},e}(),Zn=typeof WeakMap!="undefined"?new WeakMap:new Bn,eo=function(){function e(t){if(!(this instanceof e))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var r=Ea.getInstance(),n=new Ra(t,r,this);Zn.set(this,n)}return e}();["observe","unobserve","disconnect"].forEach(function(e){eo.prototype[e]=function(){var t;return(t=Zn.get(this))[e].apply(t,arguments)}});var ka=function(){return typeof nr.ResizeObserver!="undefined"?nr.ResizeObserver:eo}(),to=ka;var ro=new E,Ha=I(()=>H(new to(e=>{for(let t of e)ro.next(t)}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function ge(e){return Ha.pipe(S(t=>t.observe(e)),x(t=>ro.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(()=>de(e)))),N(de(e)))}function bt(e){return{width:e.scrollWidth,height:e.scrollHeight}}function ar(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}var no=new E,Pa=I(()=>H(new IntersectionObserver(e=>{for(let t of e)no.next(t)},{threshold:0}))).pipe(x(e=>L(Te,H(e)).pipe(C(()=>e.disconnect()))),J(1));function sr(e){return Pa.pipe(S(t=>t.observe(e)),x(t=>no.pipe(_(({target:r})=>r===e),C(()=>t.unobserve(e)),l(({isIntersecting:r})=>r))))}function oo(e,t=16){return dt(e).pipe(l(({y:r})=>{let n=de(e),o=bt(e);return r>=o.height-n.height-t}),Y())}var cr={drawer:V("[data-md-toggle=drawer]"),search:V("[data-md-toggle=search]")};function io(e){return cr[e].checked}function qe(e,t){cr[e].checked!==t&&cr[e].click()}function je(e){let t=cr[e];return b(t,"change").pipe(l(()=>t.checked),N(t.checked))}function $a(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ia(){return L(b(window,"compositionstart").pipe(l(()=>!0)),b(window,"compositionend").pipe(l(()=>!1))).pipe(N(!1))}function ao(){let e=b(window,"keydown").pipe(_(t=>!(t.metaKey||t.ctrlKey)),l(t=>({mode:io("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),_(({mode:t,type:r})=>{if(t==="global"){let n=_e();if(typeof n!="undefined")return!$a(n,r)}return!0}),fe());return Ia().pipe(x(t=>t?R:e))}function Me(){return new URL(location.href)}function ot(e){location.href=e.href}function so(){return new E}function co(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)co(e,r)}function M(e,t,...r){let n=document.createElement(e);if(t)for(let o of Object.keys(t))typeof t[o]!="undefined"&&(typeof t[o]!="boolean"?n.setAttribute(o,t[o]):n.setAttribute(o,""));for(let o of r)co(n,o);return n}function fr(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function fo(){return location.hash.substring(1)}function uo(e){let t=M("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Fa(){return b(window,"hashchange").pipe(l(fo),N(fo()),_(e=>e.length>0),J(1))}function po(){return Fa().pipe(l(e=>se(`[id="${e}"]`)),_(e=>typeof e!="undefined"))}function Nr(e){let t=matchMedia(e);return Zt(r=>t.addListener(()=>r(t.matches))).pipe(N(t.matches))}function lo(){let e=matchMedia("print");return L(b(window,"beforeprint").pipe(l(()=>!0)),b(window,"afterprint").pipe(l(()=>!1))).pipe(N(e.matches))}function qr(e,t){return e.pipe(x(r=>r?t():R))}function ur(e,t={credentials:"same-origin"}){return ve(fetch(`${e}`,t)).pipe(ce(()=>R),x(r=>r.status!==200?Tt(()=>new Error(r.statusText)):H(r)))}function Ue(e,t){return ur(e,t).pipe(x(r=>r.json()),J(1))}function mo(e,t){let r=new DOMParser;return ur(e,t).pipe(x(n=>n.text()),l(n=>r.parseFromString(n,"text/xml")),J(1))}function pr(e){let t=M("script",{src:e});return I(()=>(document.head.appendChild(t),L(b(t,"load"),b(t,"error").pipe(x(()=>Tt(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(l(()=>{}),C(()=>document.head.removeChild(t)),Oe(1))))}function ho(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function bo(){return L(b(window,"scroll",{passive:!0}),b(window,"resize",{passive:!0})).pipe(l(ho),N(ho()))}function vo(){return{width:innerWidth,height:innerHeight}}function go(){return b(window,"resize",{passive:!0}).pipe(l(vo),N(vo()))}function yo(){return Q([bo(),go()]).pipe(l(([e,t])=>({offset:e,size:t})),J(1))}function lr(e,{viewport$:t,header$:r}){let n=t.pipe(X("size")),o=Q([n,r]).pipe(l(()=>Be(e)));return Q([r,t,o]).pipe(l(([{height:i},{offset:a,size:s},{x:f,y:c}])=>({offset:{x:a.x-f,y:a.y-c+i},size:s})))}(()=>{function e(n,o){parent.postMessage(n,o||"*")}function t(...n){return n.reduce((o,i)=>o.then(()=>new Promise(a=>{let s=document.createElement("script");s.src=i,s.onload=a,document.body.appendChild(s)})),Promise.resolve())}var r=class{constructor(n){this.url=n,this.onerror=null,this.onmessage=null,this.onmessageerror=null,this.m=a=>{a.source===this.w&&(a.stopImmediatePropagation(),this.dispatchEvent(new MessageEvent("message",{data:a.data})),this.onmessage&&this.onmessage(a))},this.e=(a,s,f,c,u)=>{if(s===this.url.toString()){let p=new ErrorEvent("error",{message:a,filename:s,lineno:f,colno:c,error:u});this.dispatchEvent(p),this.onerror&&this.onerror(p)}};let o=new EventTarget;this.addEventListener=o.addEventListener.bind(o),this.removeEventListener=o.removeEventListener.bind(o),this.dispatchEvent=o.dispatchEvent.bind(o);let i=document.createElement("iframe");i.width=i.height=i.frameBorder="0",document.body.appendChild(this.iframe=i),this.w.document.open(),this.w.document.write(` + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Clonning

+

Clone button example

+
use unclead\multipleinput\MultipleInput;
+
+...
+
+$form->field($model, 'products')->widget(MultipleInput::className(), [
+    'max' => 10,
+    'cloneButton' => true,
+    'columns' => [
+        [
+            'name'  => 'product_id',
+            'type'  => 'dropDownList',
+            'title' => 'Special Products',
+            'defaultValue' => 1,
+            'items' => [
+                1 => 'id: 1, price: $19.99, title: product1',
+                2 => 'id: 2, price: $29.99, title: product2',
+                3 => 'id: 3, price: $39.99, title: product3',
+                4 => 'id: 4, price: $49.99, title: product4',
+                5 => 'id: 5, price: $59.99, title: product5',
+            ],
+        ],
+        [
+            'name'  => 'time',
+            'type'  => DateTimePicker::className(),
+            'title' => 'due date',
+            'defaultValue' => date('d-m-Y h:i')
+        ],
+        [
+            'name'  => 'count',
+            'title' => 'Count',
+            'defaultValue' => 1,
+            'enableError' => true,
+            'options' => [
+                'type' => 'number',
+                'class' => 'input-priority',
+            ]
+        ]
+    ]
+])->label(false);
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/configuration/index.html b/configuration/index.html new file mode 100644 index 0000000..f988c84 --- /dev/null +++ b/configuration/index.html @@ -0,0 +1,637 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Configuration - Yii2 multiple input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Configuration

+

Widget support the following options that are additionally recognized over and above the configuration options in the InputWidget.

+

Base options

+

theme string: specify the theme of the widget. Available 2 themes:

+
    +
  • default with only widget css classes
  • +
  • bs (twitter bootstrap) theme with additional BS ccs classes).
  • +
+

Default value is bs

+

max integer: maximum number of rows. If not set will default to unlimited

+

min integer: minimum number of rows. Set to 0 if you need the empty list in case you don't have any data

+

prepend boolean: add a new row to the beginning of the list, not to the end

+

attributeOptions array: client-side attribute options, e.g. enableAjaxValidation. You may use this property in case when you use widget without a model, since in this case widget is not able to detect client-side options automatically

+

addButtonPosition integer|array: the position(s) of add button. This can be MultipleInput::POS_HEADER, MultipleInput::POS_ROW, MultipleInput::POS_ROW_BEGIN or MultipleInput::POS_FOOTER.

+

addButtonOptions array: the HTML options for add button. Can contains class and label keys

+

removeButtonOptions array: the HTML options for remove button. Can contains class and label keys

+

cloneButton bool: whether need to enable clone buttons or not

+

cloneButtonOptions array: the HTML options for remove button. Can contains class and label keys

+

data array: array of values in case you use widget without model

+

models array: the list of models. Required in case you use TabularInput widget

+

allowEmptyList boolean: whether to allow the empty list. Deprecateed use the min option instead

+

columnClass string: the name of column class. You can specify your own class to extend base functionality. Defaults to unclead\multipleinput\MultipleInputColumn for MultipleInput and unclead\multipleinput\TabularColumn for TabularInput.

+

rendererClass string: the name of renderer class. You can specify your own class to extend base functionality. Defaults to unclead\multipleinput\renderers\TableRenderer.

+

columns array: the row columns configuration where you can set the properties which is described below

+

rowOptions array|\Closure: the HTML attributes for the table body rows. This can be either an array specifying the common HTML attributes for all body rows, or an anonymous function that returns an array of the HTML attributes. It should have the following signature:

+
function ($model, $index, $context)
+
+
    +
  • $model: the current data model being rendered
  • +
  • $index: the zero-based index of the data model in the model array
  • +
  • $context: the widget object
  • +
+

sortable bool: whether need to enable sorting or not

+

modelClass string: a class of model which is used to render TabularInput. You must specify this property when a list of models is empty. If this property is not specified the widget will detect it based on a class of models

+

extraButtons string|\Closure: the HTML content that will be rendered after the buttons. It can be either string or an anonymous function that returns a string which will be treated as HTML content. It should have the following signature:

+
function ($model, $index, $context)
+
+
    +
  • $model: the current data model being rendered
  • +
  • $index: the zero-based index of the data model in the model array
  • +
  • $context: the MultipleInput widget object
  • +
+

layoutConfig array: CSS grid classes for horizontal layout (only supported for ListRenderer class). This must be an array with these keys:

+
    +
  • 'offsetClass': the offset grid class to append to the wrapper if no label is rendered
  • +
  • 'labelClass': the label grid class
  • +
  • 'wrapperClass': the wrapper grid class
  • +
  • 'errorClass': the error grid class
  • +
+

showGeneralError bool: whether need to show error message for main attribute, when you don't want to validate particular input and want to validate a filed in general.

+

Column options

+

name string: input name. Required options

+

type string: type of the input. If not set will default to textInput. Read more about the types described below

+

title string: the column title

+

value Closure: you can set it to an anonymous function with the following signature:

+
function($data) {}
+
+

defaultValue string: default value of input

+

items array|Closure: the items for input with type dropDownList, listBox, checkboxList, radioList or anonymous function which return array of items and has the following signature:

+
function($data) {}
+
+

options array|Closure: the HTML attributes for the input, you can set it as array or an anonymous function with the following signature:

+
function($data) {}
+
+

headerOptions array: the HTML attributes for the header cell

+

enableError boolean: whether to render inline error for the input. Default to false

+

errorOptions array: the HTMl attributes for the error tag

+

nameSuffix string: the unique prefix for attribute's name to avoid id duplication e.g. in case of using several copies of the widget on a page and one column is a Select2 widget

+

tabindex integer: use it to customize a form element tabindex

+

attributeOptions array: client-side options of the attribute, e.g. enableAjaxValidation. You can use this property for custom configuration of the column (attribute). By default, the column will use options which are defined on widget level.

+

_Supported versions >= 2.1.0

+

columnOptions array|\Closure: the HTML attributes for the indivdual table body column. This can be either an array specifying the common HTML attributes for indivdual body column, or an anonymous function that returns an array of the HTML attributes.

+

It should have the following signature:

+
function ($model, $index, $context)
+
+
    +
  • $model: the current data model being rendered
  • +
  • $index: the zero-based index of the data model in the model array
  • +
  • $context: the widget object
  • +
+

Supported versions >= 2.18.0

+

inputTemplate string: the template of input for customize view. Default is {input}.

+

Example

+

<div class="input-group"><span class="input-group-addon"><i class="fas fa-mobile-alt"></i></span>{input}</div>

+

Input types

+

Each column in a row can has their own type. Widget supports:

+
    +
  • all yii2 html input types:
  • +
  • textInput
  • +
  • dropDownList
  • +
  • radioList
  • +
  • textarea
  • +
  • For more detail look at Html helper class
  • +
  • input widget (widget that extends from InputWidget class). For example, yii\widgets\MaskedInput
  • +
  • static to output a static HTML content
  • +
+

For using widget as column input you may use the following code:

+
echo $form->field($model, 'phones')->widget(MultipleInput::className(), [
+...
+    'columns' => [
+        ...
+        [
+            'name'  => 'phones',
+            'title' => $model->getAttributeLabel('phones'),
+            'type' => \yii\widgets\MaskedInput::className(),
+            'options' => [
+                'class' => 'input-phone',
+                'mask' => '999-999-99-99',
+            ],
+        ],
+    ],
+])->label(false);
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/getting-started/index.html b/getting-started/index.html new file mode 100644 index 0000000..225531d --- /dev/null +++ b/getting-started/index.html @@ -0,0 +1,609 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Getting started - Yii2 multiple input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Getting started

+

I found this small guide here https://stackoverflow.com/a/51849747 and I think it is a good example of basic usage of the widget

+

Question

+

I want to generate a different number of rows with values from my database. How can I do this?

+

I can design my columns in view and edit data manually after a page was generated. But miss how to program the number of rows and their values in the view.

+

My code is as follows:

+
 <?= $form->field($User, 'User')->widget(MultipleInput::className(), [
+        'min' => 0,
+        'max' => 4,
+        'columns' => [
+            [
+                'name'  => 'name',
+                'title' => 'Name',
+                'type' => 'textInput',
+                'options' => [
+                    'onchange' => $onchange,
+                ],
+            ],
+            [
+                'name'  => 'birth',
+                'type'  => \kartik\date\DatePicker::className(),
+                'title' => 'Birth',
+                'value' => function($data) {
+                    return $data['day'];
+                },
+
+                'options' => [
+                    'pluginOptions' => [
+                        'format' => 'dd.mm.yyyy',
+                        'todayHighlight' => true
+                    ]
+                ]
+            ],
+
+        ]
+        ])->label(false);
+
+

How can I make (for example) 8 rows with different values, and also have the ability to edit/remove/update some of them?

+

Answer

+

You need to look into the documentation as it says that you need to assign a separate field into the model which will store all the schedule in form of JSON and then provide it back to the field when editing/updating the model.

+

You have not added the appropriate model to verify how are you creating the field User in your given case above. so, I will try to create a simple example that will help you implement it in your scenario.

+

For Example.

+

You have to store a user in the database along with his favorite books.

+
User
+id, name, email
+
+Books
+id, name
+
+

Create a field/column in your User table with the name schedule of type text, you can write a migration or add manually. Add it to the rules in the User model as safe.

+

like below

+
public function rules() {
+    return [
+        ....//other rules
+        [ [ 'schedule'] , 'safe' ]
+    ];
+}
+
+

Add the widget to the newly created column in ActiveForm

+
echo $form->field($model,'schedule')->widget(MultipleInput::class,[
+    'max' => 4,
+    'columns' => [
+        [
+            'name'  => 'book_id',
+            'type'  => 'dropDownList',
+            'title' => 'Book',
+            'items' => ArrayHelper::map( Books::find()->asArray()->all (),'id','name'),
+        ],
+    ]
+
+]);
+
+

When saving the User model convert the array to JSON string

+
if( Yii::$app->request->isPost && $model->load(Yii::$app->request->post()) ){
+    $model->schedule = \yii\helpers\Json::encode($model->schedule);
+    $model->save();
+}
+
+

Override the afterFind() of the User model to covert the JSON back to the array before loading the form

+
public function afterFind() {
+    parent::afterFind();
+    $this->schedule = \yii\helpers\Json::decode($this->schedule);
+}
+
+

Now when saved the schedule field against the current user will have the JSON for the selected rows for the books, as many selected, for example, if I saved three books having ids(1,2,3) then it will have JSON

+
{
+  "0": {
+    "book_id": "1"
+  },
+  "2": {
+    "book_id": "2"
+  },
+  "3": {
+    "book_id": "3"
+  }
+}
+
+

The above JSON will be converted to an array in the afterFind() so that the widget loads the saved schedule when you EDIT the record.

+

Now go to your update page or edit the newly saved model you will see the books loaded automatically.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/icons/index.html b/icons/index.html new file mode 100644 index 0000000..f7ce367 --- /dev/null +++ b/icons/index.html @@ -0,0 +1,484 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Custom icons - Yii2 multiple input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Using other icon libraries

+

Multiple input and Tabular input widgets now support FontAwesome and indeed any other icon library you chose to integrate into your project.

+

To take advantage of this, please proceed as follows:

+
    +
  1. Include the preferred icon library in your project. If you wish to use font awesome, you can use the included FontAwesomeAsset which will integrate the free fa from their CDN;
  2. +
  3. Add a mapping for your preferred icon library if it is not in the iconMap array of the widget, like the following;
  4. +
+
public $iconMap = [
+    'glyphicons' => [
+        'drag-handle' => 'glyphicon glyphicon-menu-hamburger',
+        'remove' => 'glyphicon glyphicon-remove',
+        'add' => 'glyphicon glyphicon-plus',
+        'clone' => 'glyphicon glyphicon-duplicate',
+    ],
+    'fa' => [
+        'drag-handle' => 'fa fa-bars',
+        'remove' => 'fa fa-times',
+        'add' => 'fa fa-plus',
+        'clone' => 'fa fa-files-o',
+    ],
+    'my-amazing-icons' => [
+        'drag-handle' => 'my my-bars',
+        'remove' => 'my my-times',
+        'add' => 'my my-plus',
+        'clone' => 'my my-files',
+    ]
+];
+
+
    +
  1. Set the preferred icon source
  2. +
+
    public $iconSource = 'my-amazing-icons';
+
+

If you do none of the above, the default behavior which assumes you are using glyphicons is retained.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..ca2c75b --- /dev/null +++ b/index.html @@ -0,0 +1,503 @@ + + + + + + + + + + + + + + + + + + + + + + + + Yii2 multiple input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Yii2 Multiple input widget.

+

Yii2 widget for handle multiple inputs for an attribute of model and tabular input for batch of models.

+

Latest Stable Version +Total Downloads +Daily Downloads +Latest Unstable Version +License

+

Latest release

+

The latest stable version of the extension is v2.25.0 Follow the instruction for upgrading from previous versions

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/installation/index.html b/installation/index.html new file mode 100644 index 0000000..979fb5b --- /dev/null +++ b/installation/index.html @@ -0,0 +1,459 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Installation - Yii2 multiple input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Installation

+

The preferred way to install this extension is through composer.

+

Either run

+
php composer.phar require  unclead/yii2-multiple-input "~2.0"
+
+

or add

+
"unclead/yii2-multiple-input": "~2.0"
+
+

to the requiresection of your composer.json file.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/javascript-events/index.html b/javascript-events/index.html new file mode 100644 index 0000000..5eaa2cb --- /dev/null +++ b/javascript-events/index.html @@ -0,0 +1,626 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + JS events - Yii2 multiple input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Javascript events

+

This widget has following events:

+
    +
  • afterInit: triggered after initialization
  • +
  • afterAddRow: triggered after new row insertion
  • +
  • beforeDeleteRow: triggered before the row removal
  • +
  • afterDeleteRow: triggered after the row removal
  • +
  • afterDropRow: triggered after drop the row when sortable mode is on
  • +
+

Example

+
jQuery('#multiple-input').on('afterInit', function(){
+    console.log('calls on after initialization event');
+}).on('beforeAddRow', function(e, row, currentIndex) {
+    console.log('calls on before add row event');
+}).on('afterAddRow', function(e, row, currentIndex) {
+    console.log('calls on after add row event');
+}).on('beforeDeleteRow', function(e, row, currentIndex){
+    // row - HTML container of the current row for removal.
+    // For TableRenderer it is tr.multiple-input-list__item
+    console.log('calls on before remove row event.');
+    return confirm('Are you sure you want to delete row?')
+}).on('afterDeleteRow', function(e, row, currentIndex){
+    console.log('calls on after remove row event');
+    console.log(row);
+}).on('afterDropRow', function(e, item){       
+    console.log('calls on after drop row', item);
+});
+
+

JavaScript operations

+

add

+

Adding new row with specified settings.

+

Input arguments:

+
    +
  • object - values for inputs, can be filled with tags for dynamically added options for select (for ajax select).
  • +
+

Example:

+
$('#multiple-input').multipleInput('add', {first: 10, second: '<option value="2" selected="selected">second</option>'});
+
+

remove

+

Remove row with specified index.

+

Input arguments:

+
    +
  • integer - row number for removing, if not specified then removes last row.
  • +
+

Example:

+
$('#multiple-input').multipleInput('remove', 2);
+
+

clear

+

Remove all rows

+
$('#multiple-input').multipleInput('clear');
+
+

option

+

Get or set a particular option

+

Input arguments:

+
    +
  • string - a name of an option
  • +
  • mixed - a value of an option (optional). If specified will be used as a new value of an option;
  • +
+

Example:

+
$('#multiple-input').multipleInput('option', 'max');
+$('#multiple-input').multipleInput('option', 'max', 10);
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/renderers/index.html b/renderers/index.html new file mode 100644 index 0000000..5ed7bf9 --- /dev/null +++ b/renderers/index.html @@ -0,0 +1,569 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Renderers - Yii2 multiple input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Renderers

+

Currently widget supports three type of renderers

+

TableRenderer

+

Table renderer

+

This renderer is enabled by default.

+

ListRenderer

+

List renderer

+

To enable this renderer you have to use an option rendererClass

+
<?php
+echo $form->field($model, 'schedule')->widget(MultipleInput::className(), [
+    'rendererClass' => \unclead\multipleinput\renderers\ListRenderer::className(),
+    'max' => 4,
+    'allowEmptyList' => true,
+    'rowOptions' => function($model) {
+        $options = [];
+
+        if ($model['priority'] > 1) {
+            $options['class'] = 'danger';
+        }
+        return $options;
+    },
+
+

DivRenderer

+

List renderer

+

To enable this renderer you have to use an option rendererClass

+
<?php
+echo $form->field($model, 'schedule')->widget(MultipleInput::className(), [
+    'rendererClass' => \unclead\multipleinput\renderers\ListRenderer::class,
+    'addButtonPosition' => MultipleInput::POS_ROW, // show add button inside of the row
+    'extraButtons' => function ($model, $index, $context) {
+        if ($index === 0) {
+            return Html::tag('div', Yii::t('object', 'Add object'), ['class' => 'mi-after-add']);
+        }
+
+        return Html::tag('div', Yii::t('object', 'Remove object'), ['class' => 'mi-after-remove']);
+    },
+    'layoutConfig' => [
+        'offsetClass' => 'col-md-offset-2',
+        'labelClass' => 'col-md-2',
+        'wrapperClass' => 'col-md-6',
+        'errorClass' => 'col-md-offset-2 col-md-6',
+        'buttonActionClass' => 'col-md-offset-1 col-md-2',
+    ],
+...
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/search/search_index.json b/search/search_index.json new file mode 100644 index 0000000..dfb1b74 --- /dev/null +++ b/search/search_index.json @@ -0,0 +1 @@ +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Yii2 Multiple input widget.","text":"

Yii2 widget for handle multiple inputs for an attribute of model and tabular input for batch of models.

"},{"location":"#latest-release","title":"Latest release","text":"

The latest stable version of the extension is v2.25.0 Follow the instruction for upgrading from previous versions

"},{"location":"clonning/","title":"Clonning","text":"
use unclead\\multipleinput\\MultipleInput;\n\n...\n\n$form->field($model, 'products')->widget(MultipleInput::className(), [\n    'max' => 10,\n    'cloneButton' => true,\n    'columns' => [\n        [\n            'name'  => 'product_id',\n            'type'  => 'dropDownList',\n            'title' => 'Special Products',\n            'defaultValue' => 1,\n            'items' => [\n                1 => 'id: 1, price: $19.99, title: product1',\n                2 => 'id: 2, price: $29.99, title: product2',\n                3 => 'id: 3, price: $39.99, title: product3',\n                4 => 'id: 4, price: $49.99, title: product4',\n                5 => 'id: 5, price: $59.99, title: product5',\n            ],\n        ],\n        [\n            'name'  => 'time',\n            'type'  => DateTimePicker::className(),\n            'title' => 'due date',\n            'defaultValue' => date('d-m-Y h:i')\n        ],\n        [\n            'name'  => 'count',\n            'title' => 'Count',\n            'defaultValue' => 1,\n            'enableError' => true,\n            'options' => [\n                'type' => 'number',\n                'class' => 'input-priority',\n            ]\n        ]\n    ]\n])->label(false);\n
"},{"location":"configuration/","title":"Configuration","text":"

Widget support the following options that are additionally recognized over and above the configuration options in the InputWidget.

"},{"location":"configuration/#base-options","title":"Base options","text":"

theme string: specify the theme of the widget. Available 2 themes:

  • default with only widget css classes
  • bs (twitter bootstrap) theme with additional BS ccs classes).

Default value is bs

max integer: maximum number of rows. If not set will default to unlimited

min integer: minimum number of rows. Set to 0 if you need the empty list in case you don't have any data

prepend boolean: add a new row to the beginning of the list, not to the end

attributeOptions array: client-side attribute options, e.g. enableAjaxValidation. You may use this property in case when you use widget without a model, since in this case widget is not able to detect client-side options automatically

addButtonPosition integer|array: the position(s) of add button. This can be MultipleInput::POS_HEADER, MultipleInput::POS_ROW, MultipleInput::POS_ROW_BEGIN or MultipleInput::POS_FOOTER.

addButtonOptions array: the HTML options for add button. Can contains class and label keys

removeButtonOptions array: the HTML options for remove button. Can contains class and label keys

cloneButton bool: whether need to enable clone buttons or not

cloneButtonOptions array: the HTML options for remove button. Can contains class and label keys

data array: array of values in case you use widget without model

models array: the list of models. Required in case you use TabularInput widget

allowEmptyList boolean: whether to allow the empty list. Deprecateed use the min option instead

columnClass string: the name of column class. You can specify your own class to extend base functionality. Defaults to unclead\\multipleinput\\MultipleInputColumn for MultipleInput and unclead\\multipleinput\\TabularColumn for TabularInput.

rendererClass string: the name of renderer class. You can specify your own class to extend base functionality. Defaults to unclead\\multipleinput\\renderers\\TableRenderer.

columns array: the row columns configuration where you can set the properties which is described below

rowOptions array|\\Closure: the HTML attributes for the table body rows. This can be either an array specifying the common HTML attributes for all body rows, or an anonymous function that returns an array of the HTML attributes. It should have the following signature:

function ($model, $index, $context)\n
  • $model: the current data model being rendered
  • $index: the zero-based index of the data model in the model array
  • $context: the widget object

sortable bool: whether need to enable sorting or not

modelClass string: a class of model which is used to render TabularInput. You must specify this property when a list of models is empty. If this property is not specified the widget will detect it based on a class of models

extraButtons string|\\Closure: the HTML content that will be rendered after the buttons. It can be either string or an anonymous function that returns a string which will be treated as HTML content. It should have the following signature:

function ($model, $index, $context)\n
  • $model: the current data model being rendered
  • $index: the zero-based index of the data model in the model array
  • $context: the MultipleInput widget object

layoutConfig array: CSS grid classes for horizontal layout (only supported for ListRenderer class). This must be an array with these keys:

  • 'offsetClass': the offset grid class to append to the wrapper if no label is rendered
  • 'labelClass': the label grid class
  • 'wrapperClass': the wrapper grid class
  • 'errorClass': the error grid class

showGeneralError bool: whether need to show error message for main attribute, when you don't want to validate particular input and want to validate a filed in general.

"},{"location":"configuration/#column-options","title":"Column options","text":"

name string: input name. Required options

type string: type of the input. If not set will default to textInput. Read more about the types described below

title string: the column title

value Closure: you can set it to an anonymous function with the following signature:

function($data) {}\n

defaultValue string: default value of input

items array|Closure: the items for input with type dropDownList, listBox, checkboxList, radioList or anonymous function which return array of items and has the following signature:

function($data) {}\n

options array|Closure: the HTML attributes for the input, you can set it as array or an anonymous function with the following signature:

function($data) {}\n

headerOptions array: the HTML attributes for the header cell

enableError boolean: whether to render inline error for the input. Default to false

errorOptions array: the HTMl attributes for the error tag

nameSuffix string: the unique prefix for attribute's name to avoid id duplication e.g. in case of using several copies of the widget on a page and one column is a Select2 widget

tabindex integer: use it to customize a form element tabindex

attributeOptions array: client-side options of the attribute, e.g. enableAjaxValidation. You can use this property for custom configuration of the column (attribute). By default, the column will use options which are defined on widget level.

_Supported versions >= 2.1.0

columnOptions array|\\Closure: the HTML attributes for the indivdual table body column. This can be either an array specifying the common HTML attributes for indivdual body column, or an anonymous function that returns an array of the HTML attributes.

It should have the following signature:

function ($model, $index, $context)\n
  • $model: the current data model being rendered
  • $index: the zero-based index of the data model in the model array
  • $context: the widget object

Supported versions >= 2.18.0

inputTemplate string: the template of input for customize view. Default is {input}.

Example

<div class=\"input-group\"><span class=\"input-group-addon\"><i class=\"fas fa-mobile-alt\"></i></span>{input}</div>

"},{"location":"configuration/#input-types","title":"Input types","text":"

Each column in a row can has their own type. Widget supports:

  • all yii2 html input types:
  • textInput
  • dropDownList
  • radioList
  • textarea
  • For more detail look at Html helper class
  • input widget (widget that extends from InputWidget class). For example, yii\\widgets\\MaskedInput
  • static to output a static HTML content

For using widget as column input you may use the following code:

echo $form->field($model, 'phones')->widget(MultipleInput::className(), [\n...\n    'columns' => [\n        ...\n        [\n            'name'  => 'phones',\n            'title' => $model->getAttributeLabel('phones'),\n            'type' => \\yii\\widgets\\MaskedInput::className(),\n            'options' => [\n                'class' => 'input-phone',\n                'mask' => '999-999-99-99',\n            ],\n        ],\n    ],\n])->label(false);\n
"},{"location":"getting-started/","title":"Getting started","text":"

I found this small guide here https://stackoverflow.com/a/51849747 and I think it is a good example of basic usage of the widget

"},{"location":"getting-started/#question","title":"Question","text":"

I want to generate a different number of rows with values from my database. How can I do this?

I can design my columns in view and edit data manually after a page was generated. But miss how to program the number of rows and their values in the view.

My code is as follows:

 <?= $form->field($User, 'User')->widget(MultipleInput::className(), [\n        'min' => 0,\n        'max' => 4,\n        'columns' => [\n            [\n                'name'  => 'name',\n                'title' => 'Name',\n                'type' => 'textInput',\n                'options' => [\n                    'onchange' => $onchange,\n                ],\n            ],\n            [\n                'name'  => 'birth',\n                'type'  => \\kartik\\date\\DatePicker::className(),\n                'title' => 'Birth',\n                'value' => function($data) {\n                    return $data['day'];\n                },\n\n                'options' => [\n                    'pluginOptions' => [\n                        'format' => 'dd.mm.yyyy',\n                        'todayHighlight' => true\n                    ]\n                ]\n            ],\n\n        ]\n        ])->label(false);\n

How can I make (for example) 8 rows with different values, and also have the ability to edit/remove/update some of them?

"},{"location":"getting-started/#answer","title":"Answer","text":"

You need to look into the documentation as it says that you need to assign a separate field into the model which will store all the schedule in form of JSON and then provide it back to the field when editing/updating the model.

You have not added the appropriate model to verify how are you creating the field User in your given case above. so, I will try to create a simple example that will help you implement it in your scenario.

For Example.

You have to store a user in the database along with his favorite books.

User\nid, name, email\n\nBooks\nid, name\n

Create a field/column in your User table with the name schedule of type text, you can write a migration or add manually. Add it to the rules in the User model as safe.

like below

public function rules() {\n    return [\n        ....//other rules\n        [ [ 'schedule'] , 'safe' ]\n    ];\n}\n

Add the widget to the newly created column in ActiveForm

echo $form->field($model,'schedule')->widget(MultipleInput::class,[\n    'max' => 4,\n    'columns' => [\n        [\n            'name'  => 'book_id',\n            'type'  => 'dropDownList',\n            'title' => 'Book',\n            'items' => ArrayHelper::map( Books::find()->asArray()->all (),'id','name'),\n        ],\n    ]\n\n]);\n

When saving the User model convert the array to JSON string

if( Yii::$app->request->isPost && $model->load(Yii::$app->request->post()) ){\n    $model->schedule = \\yii\\helpers\\Json::encode($model->schedule);\n    $model->save();\n}\n

Override the afterFind() of the User model to covert the JSON back to the array before loading the form

public function afterFind() {\n    parent::afterFind();\n    $this->schedule = \\yii\\helpers\\Json::decode($this->schedule);\n}\n

Now when saved the schedule field against the current user will have the JSON for the selected rows for the books, as many selected, for example, if I saved three books having ids(1,2,3) then it will have JSON

{\n  \"0\": {\n    \"book_id\": \"1\"\n  },\n  \"2\": {\n    \"book_id\": \"2\"\n  },\n  \"3\": {\n    \"book_id\": \"3\"\n  }\n}\n

The above JSON will be converted to an array in the afterFind() so that the widget loads the saved schedule when you EDIT the record.

Now go to your update page or edit the newly saved model you will see the books loaded automatically.

"},{"location":"icons/","title":"Using other icon libraries","text":"

Multiple input and Tabular input widgets now support FontAwesome and indeed any other icon library you chose to integrate into your project.

To take advantage of this, please proceed as follows:

  1. Include the preferred icon library in your project. If you wish to use font awesome, you can use the included FontAwesomeAsset which will integrate the free fa from their CDN;
  2. Add a mapping for your preferred icon library if it is not in the iconMap array of the widget, like the following;
public $iconMap = [\n    'glyphicons' => [\n        'drag-handle' => 'glyphicon glyphicon-menu-hamburger',\n        'remove' => 'glyphicon glyphicon-remove',\n        'add' => 'glyphicon glyphicon-plus',\n        'clone' => 'glyphicon glyphicon-duplicate',\n    ],\n    'fa' => [\n        'drag-handle' => 'fa fa-bars',\n        'remove' => 'fa fa-times',\n        'add' => 'fa fa-plus',\n        'clone' => 'fa fa-files-o',\n    ],\n    'my-amazing-icons' => [\n        'drag-handle' => 'my my-bars',\n        'remove' => 'my my-times',\n        'add' => 'my my-plus',\n        'clone' => 'my my-files',\n    ]\n];\n
  1. Set the preferred icon source
    public $iconSource = 'my-amazing-icons';\n

If you do none of the above, the default behavior which assumes you are using glyphicons is retained.

"},{"location":"installation/","title":"Installation","text":"

The preferred way to install this extension is through composer.

Either run

php composer.phar require  unclead/yii2-multiple-input \"~2.0\"\n

or add

\"unclead/yii2-multiple-input\": \"~2.0\"\n

to the requiresection of your composer.json file.

"},{"location":"javascript-events/","title":"Javascript events","text":"

This widget has following events:

  • afterInit: triggered after initialization
  • afterAddRow: triggered after new row insertion
  • beforeDeleteRow: triggered before the row removal
  • afterDeleteRow: triggered after the row removal
  • afterDropRow: triggered after drop the row when sortable mode is on

Example

jQuery('#multiple-input').on('afterInit', function(){\n    console.log('calls on after initialization event');\n}).on('beforeAddRow', function(e, row, currentIndex) {\n    console.log('calls on before add row event');\n}).on('afterAddRow', function(e, row, currentIndex) {\n    console.log('calls on after add row event');\n}).on('beforeDeleteRow', function(e, row, currentIndex){\n    // row - HTML container of the current row for removal.\n    // For TableRenderer it is tr.multiple-input-list__item\n    console.log('calls on before remove row event.');\n    return confirm('Are you sure you want to delete row?')\n}).on('afterDeleteRow', function(e, row, currentIndex){\n    console.log('calls on after remove row event');\n    console.log(row);\n}).on('afterDropRow', function(e, item){       \n    console.log('calls on after drop row', item);\n});\n
"},{"location":"javascript-events/#javascript-operations","title":"JavaScript operations","text":""},{"location":"javascript-events/#add","title":"add","text":"

Adding new row with specified settings.

Input arguments:

  • object - values for inputs, can be filled with tags for dynamically added options for select (for ajax select).

Example:

$('#multiple-input').multipleInput('add', {first: 10, second: '<option value=\"2\" selected=\"selected\">second</option>'});\n
"},{"location":"javascript-events/#remove","title":"remove","text":"

Remove row with specified index.

Input arguments:

  • integer - row number for removing, if not specified then removes last row.

Example:

$('#multiple-input').multipleInput('remove', 2);\n
"},{"location":"javascript-events/#clear","title":"clear","text":"

Remove all rows

$('#multiple-input').multipleInput('clear');\n
"},{"location":"javascript-events/#option","title":"option","text":"

Get or set a particular option

Input arguments:

  • string - a name of an option
  • mixed - a value of an option (optional). If specified will be used as a new value of an option;

Example:

$('#multiple-input').multipleInput('option', 'max');\n$('#multiple-input').multipleInput('option', 'max', 10);\n
"},{"location":"renderers/","title":"Renderers","text":"

Currently widget supports three type of renderers

"},{"location":"renderers/#tablerenderer","title":"TableRenderer","text":"

This renderer is enabled by default.

"},{"location":"renderers/#listrenderer","title":"ListRenderer","text":"

To enable this renderer you have to use an option rendererClass

<?php\necho $form->field($model, 'schedule')->widget(MultipleInput::className(), [\n    'rendererClass' => \\unclead\\multipleinput\\renderers\\ListRenderer::className(),\n    'max' => 4,\n    'allowEmptyList' => true,\n    'rowOptions' => function($model) {\n        $options = [];\n\n        if ($model['priority'] > 1) {\n            $options['class'] = 'danger';\n        }\n        return $options;\n    },\n
"},{"location":"renderers/#divrenderer","title":"DivRenderer","text":"

To enable this renderer you have to use an option rendererClass

<?php\necho $form->field($model, 'schedule')->widget(MultipleInput::className(), [\n    'rendererClass' => \\unclead\\multipleinput\\renderers\\ListRenderer::class,\n    'addButtonPosition' => MultipleInput::POS_ROW, // show add button inside of the row\n    'extraButtons' => function ($model, $index, $context) {\n        if ($index === 0) {\n            return Html::tag('div', Yii::t('object', 'Add object'), ['class' => 'mi-after-add']);\n        }\n\n        return Html::tag('div', Yii::t('object', 'Remove object'), ['class' => 'mi-after-remove']);\n    },\n    'layoutConfig' => [\n        'offsetClass' => 'col-md-offset-2',\n        'labelClass' => 'col-md-2',\n        'wrapperClass' => 'col-md-6',\n        'errorClass' => 'col-md-offset-2 col-md-6',\n        'buttonActionClass' => 'col-md-offset-1 col-md-2',\n    ],\n...\n
"},{"location":"tips-and-tricks/","title":"Tips and tricks","text":""},{"location":"tips-and-tricks/#how-to-customize-buttons","title":"How to customize buttons","text":"

You can customize add and remove buttons via addButtonOptions and removeButtonOptions. Here is a simple example of how you can use those options:

    echo $form->field($model, 'emails')->widget(MultipleInput::className(), [\n        'max' => 5,\n        'addButtonOptions' => [\n            'class' => 'btn btn-success',\n            'label' => 'add' // also you can use html code\n        ],\n        'removeButtonOptions' => [\n            'label' => 'remove'\n        ]\n    ])\n    ->label(false);\n
"},{"location":"tips-and-tricks/#how-to-add-content-after-the-buttons","title":"How to add content after the buttons","text":"

You can add html content after add and remove buttons via extraButtons.

    echo $form->field($model, 'field')->widget(MultipleInput::className(), [\n        'rendererClass' => \\unclead\\multipleinput\\renderers\\ListRenderer::class,\n        'extraButtons' => function ($model, $index, $context) {\n            return Html::tag('span', '', ['class' => \"btn-show-hide-{$index} glyphicon glyphicon-eye-open btn btn-info\"]);\n        },\n    ])\n    ->label(false);\n
"},{"location":"tips-and-tricks/#work-with-an-empty-list","title":"Work with an empty list","text":"

In some cases, you need to have the ability to delete all rows in the list. For this purpose, you can use option allowEmptyList like in the example below:

    echo $form->field($model, 'emails')->widget(MultipleInput::className(), [\n        'max' => 5,\n        'allowEmptyList' => true\n    ])\n    ->label(false);\n

Also, you can set 0 in min option if you don't need the first blank row when data is empty.

"},{"location":"tips-and-tricks/#guess-column-title","title":"Guess column title","text":"

Sometimes you can use the widget without defining columns but you want to have the column header of the table. In this case, you can use the option enableGuessTitle like in the example below:

    echo $form->field($model, 'emails')->widget(MultipleInput::className(), [\n        'max' => 5,\n        'allowEmptyList' => true,\n        'enableGuessTitle' => true\n    ])\n    ->label(false);\n
"},{"location":"tips-and-tricks/#ajax-loading-of-a-widget","title":"Ajax loading of a widget","text":"

Assume you want to load a widget via ajax and then show it inside the modal window. In this case, you MUST:

  • Ensure that you specified the ID of the widget otherwise the widget will get a random ID and it can be the same as the ID of others elements on the page.
  • Ensure that you use the widget inside ActiveForm because it works incorrectly in this case.
"},{"location":"tips-and-tricks/#use-of-a-widgets-placeholder","title":"Use of a widget's placeholder","text":"

You can use a placeholder {multiple_index} in a widget configuration, e.g. for implementation of dependent drop-down lists.

    <?= $form->field($model, 'field')->widget(MultipleInput::className(), [\n            'id' => 'my_id',\n            'allowEmptyList' => false,\n            'rowOptions' => [\n                'id' => 'row{multiple_index_my_id}',\n            ],\n            'columns' => [\n                [\n                    'name'  => 'category',\n                    'type'  => 'dropDownList',\n                    'title' => 'Category',\n                    'defaultValue' => '1',\n                    'items' => [\n                        '1' => 'Test 1',\n                        '2' => 'Test 2',\n                        '3' => 'Test 3',\n                        '4' => 'Test 4',\n                    ],\n                    'options' => [\n                        'onchange' => <<< JS\n$.post(\"list?id=\" + $(this).val(), function(data){\n    console.log(data);\n    $(\"select#subcat-{multiple_index_my_id}\").html(data);\n});\nJS\n                    ],\n                ],\n                [\n                    'name'  => 'subcategory',\n                    'type'  => 'dropDownList',\n                    'title' => 'Subcategory',\n                    'items' => [],\n                    'options'=> [\n                        'id' => 'subcat-{multiple_index_my_id}'\n                    ],\n                ],\n            ]\n    ]);\n    ?>\n

Important Ensure that you added ID of widget to a base placeholder multiple_index

"},{"location":"tips-and-tricks/#custom-index-of-the-row","title":"Custom index of the row","text":"

Assume that you want to set a specific index for each row. In this case, you can pass the data attribute as an associative array as in the example below:

    <?= $form->field($model, 'field')->widget(MultipleInput::className(), [\n            'allowEmptyList' => false,\n            'data' => [\n                3 => [\n                    'day'       => '27.02.2015',\n                    'user_id'   => 31,\n                    'priority'  => 1,\n                    'enable'    => 1\n                ],\n\n                'some-key' => [\n                    'day'       => '27.02.2015',\n                    'user_id'   => 33,\n                    'priority'  => 2,\n                    'enable'    => 0\n                ],\n            ]\n\n    ...\n
"},{"location":"tips-and-tricks/#embedded-multipleinput-widget","title":"Embedded MultipleInput widget","text":"

You can use nested MultipleInput as in the example below:

echo MultipleInput::widget([\n    'model' => $model,\n    'attribute' => 'questions',\n    'attributeOptions' => $commonAttributeOptions,\n    'columns' => [\n        [\n            'name' => 'question',\n            'type' => 'textarea',\n        ],\n        [\n            'name' => 'answers',\n            'type'  => MultipleInput::class,\n            'options' => [\n                'attributeOptions' => $commonAttributeOptions,\n                'columns' => [\n                    [\n                        'name' => 'right',\n                        'type' => MultipleInputColumn::TYPE_CHECKBOX\n                    ],\n                    [\n                        'name' => 'answer'\n                    ]\n                ]\n            ]\n        ]\n    ],\n]);\n

But in this case, you have to pass attributeOptions to the widget otherwise, you will not be able to use ajax or client-side validation of data.

"},{"location":"tips-and-tricks/#client-validation","title":"Client validation","text":"

Apart from ajax validation, you can use client validation but in this case, you MUST set property form. Also, ensure that you set enableClientValidation to true value in property attributeOptions. If you want to use client validation for a particular column you can use the property attributeOptions. An example of using client validation is listed below:

<?= TabularInput::widget([\n    'models' => $models,\n    'form' => $form,\n    'attributeOptions' => [\n        'enableAjaxValidation' => true,\n        'enableClientValidation' => false,\n        'validateOnChange' => false,\n        'validateOnSubmit' => true,\n        'validateOnBlur' => false,\n    ],\n    'columns' => [\n        [\n            'name' => 'id',\n            'type' => TabularColumn::TYPE_HIDDEN_INPUT\n        ],\n        [\n            'name' => 'title',\n            'title' => 'Title',\n            'type' => TabularColumn::TYPE_TEXT_INPUT,\n            'attributeOptions' => [\n                'enableClientValidation' => true,\n                'validateOnChange' => true,\n            ],\n            'enableError' => true\n        ],\n        [\n            'name' => 'description',\n            'title' => 'Description',\n        ],\n    ],\n]) ?>\n

In the example above we use client validation for column title and ajax validation for column description. As you can seee we also enabled validateOnChange for column title thus you can use all client-side options from the ActiveField class.

"},{"location":"usage/","title":"Usage","text":"

You can find the source code of examples here

"},{"location":"usage/#one-column","title":"One column","text":"

For example, your application contains the model User that has the related model UserEmail You can add virtual attribute emails for collect emails from a form and then you can save them to the database.

In this case, you can use yii2-multiple-input widget for supporting multiple inputs how to describe below.

First of all, we have to declare a virtual attribute in the model

class ExampleModel extends Model\n{\n    /**\n     * @var array virtual attribute for keeping emails\n     */\n    public $emails;\n

Then we have to use MultipleInput widget for rendering form field in the view file

use yii\\bootstrap\\ActiveForm;\nuse unclead\\multipleinput\\MultipleInput;\nuse unclead\\multipleinput\\examples\\models\\ExampleModel;\nuse yii\\helpers\\Html;\n\n/* @var $this \\yii\\base\\View */\n/* @var $model ExampleModel */\n?>\n\n<?php $form = ActiveForm::begin([\n    'enableAjaxValidation'      => true,\n    'enableClientValidation'    => false,\n    'validateOnChange'          => false,\n    'validateOnSubmit'          => true,\n    'validateOnBlur'            => false,\n]);?>\n\n<?= $form->field($model, 'emails')->widget(MultipleInput::className(), [\n    'max' => 4,\n ]);\n?>\n<?= Html::submitButton('Update', ['class' => 'btn btn-success']);?>\n<?php ActiveForm::end();?>\n

Options max means that a user is able to input only 4 emails

For validation emails, you can use the following code

    /**\n     * Email validation.\n     *\n     * @param $attribute\n     */\n    public function validateEmails($attribute)\n    {\n        $items = $this->$attribute;\n\n        if (!is_array($items)) {\n            $items = [];\n        }\n\n        foreach ($items as $index => $item) {\n            $validator = new EmailValidator();\n            $error = null;\n            $validator->validate($item, $error);\n            if (!empty($error)) {\n                $key = $attribute . '[' . $index . ']';\n                $this->addError($key, $error);\n            }\n        }\n    }\n
"},{"location":"usage/#multiple-columns","title":"Multiple columns","text":"

For example, you want to have an interface for manage a user schedule. For simplicity, we will store the schedule in json string.

In this case, you can use yii2-multiple-input widget for supporting multiple inputs how to describe below.

Our test model can look like as the following snippet

class ExampleModel extends Model\n{\n    public $schedule;\n\n    public function init()\n    {\n        parent::init();\n\n        $this->schedule = [\n            [\n                'day'       => '27.02.2015',\n                'user_id'   => 1,\n                'priority'  => 1\n            ],\n            [\n                'day'       => '27.02.2015',\n                'user_id'   => 2,\n                'priority'  => 2\n            ],\n        ];\n    }\n

Then we have to use MultipleInput widget for rendering form field in the view file

use yii\\bootstrap\\ActiveForm;\nuse unclead\\multipleinput\\MultipleInput;\nuse unclead\\multipleinput\\examples\\models\\ExampleModel;\nuse yii\\helpers\\Html;\n\n/* @var $this \\yii\\base\\View */\n/* @var $model ExampleModel */\n?>\n\n<?php $form = ActiveForm::begin([\n    'enableAjaxValidation'      => true,\n    'enableClientValidation'    => false,\n    'validateOnChange'          => false,\n    'validateOnSubmit'          => true,\n    'validateOnBlur'            => false,\n]);?>\n\n<?= $form->field($model, 'schedule')->widget(MultipleInput::className(), [\n    'max' => 4,\n    'columns' => [\n        [\n            'name'  => 'user_id',\n            'type'  => 'dropDownList',\n            'title' => 'User',\n            'defaultValue' => 1,\n            'items' => [\n                1 => 'User 1',\n                2 => 'User 2'\n            ]\n        ],\n        [\n            'name'  => 'day',\n            'type'  => \\kartik\\date\\DatePicker::className(),\n            'title' => 'Day',\n            'value' => function($data) {\n                return $data['day'];\n            },\n            'items' => [\n                '0' => 'Saturday',\n                '1' => 'Monday'\n            ],\n            'options' => [\n                'pluginOptions' => [\n                    'format' => 'dd.mm.yyyy',\n                    'todayHighlight' => true\n                ]\n            ]\n        ],\n        [\n            'name'  => 'priority',\n            'title' => 'Priority',\n            'enableError' => true,\n            'options' => [\n                'class' => 'input-priority'\n            ]\n        ]\n    ]\n ]);\n?>\n<?= Html::submitButton('Update', ['class' => 'btn btn-success']);?>\n<?php ActiveForm::end();?>\n

For validation of the schedule you can use the following code

    public function validateSchedule($attribute)\n    {\n        $requiredValidator = new RequiredValidator();\n\n        foreach($this->$attribute as $index => $row) {\n            $error = null;\n            $requiredValidator->validate($row['priority'], $error);\n            if (!empty($error)) {\n                $key = $attribute . '[' . $index . '][priority]';\n                $this->addError($key, $error);\n            }\n        }\n    }\n

For example, you keep some data in json format in an attribute of a model. Imagine that it is an abstract user schedule with keys: user_id, day, priority

On the edit page, you want to be able to manage this schedule and you can you yii2-multiple-input widget like in the following code

use unclead\\multipleinput\\MultipleInput;\n\n...\n\n<?= $form->field($model, 'schedule')->widget(MultipleInput::className(), [\n    'max' => 4,\n    'columns' => [\n        [\n            'name'  => 'user_id',\n            'type'  => 'dropDownList',\n            'title' => 'User',\n            'defaultValue' => 1,\n            'items' => [\n                1 => 'User 1',\n                2 => 'User 2'\n            ]\n        ],\n        [\n            'name'  => 'day',\n            'type'  => \\kartik\\date\\DatePicker::className(),\n            'title' => 'Day',\n            'value' => function($data) {\n                return $data['day'];\n            },\n            'items' => [\n                '0' => 'Saturday',\n                '1' => 'Monday'\n            ],\n            'options' => [\n                'pluginOptions' => [\n                    'format' => 'dd.mm.yyyy',\n                    'todayHighlight' => true\n                ]\n            ],\n            'headerOptions' => [\n                'style' => 'width: 250px;',\n                'class' => 'day-css-class'\n            ]\n        ],\n        [\n            'name'  => 'priority',\n            'enableError' => true,\n            'title' => 'Priority',\n            'options' => [\n                'class' => 'input-priority'\n            ]\n        ],\n        [\n            'name'  => 'comment',\n            'type'  => 'static',\n            'value' => function($data) {\n                return Html::tag('span', 'static content', ['class' => 'label label-info']);\n            },\n            'headerOptions' => [\n                'style' => 'width: 70px;',\n            ]\n        ]\n    ]\n ]);\n?>\n
"},{"location":"usage/#tabular-input","title":"Tabular input","text":"

For example, you want to have an interface for manage some abstract items via tabular input.

In this case, you can use yii2-multiple-input widget for supporting tabular input how to describe below.

Our test model can look like as the following snippet

namespace unclead\\multipleinput\\examples\\models;\n\nuse Yii;\nuse yii\\base\\Model;\n// you have to install https://github.com/vova07/yii2-fileapi-widget\nuse vova07\\fileapi\\behaviors\\UploadBehavior;\n\n/**\n * Class Item\n * @package unclead\\multipleinput\\examples\\models\n */\nclass Item extends Model\n{\n    public $title;\n    public $description;\n    public $file;\n    public $date;\n\n    public function behaviors()\n    {\n        return [\n            'uploadBehavior' => [\n                'class' => UploadBehavior::className(),\n                'attributes' => [\n                    'file' => [\n                        'path' => Yii::getAlias('@webroot') . '/images/',\n                        'tempPath' => Yii::getAlias('@webroot') . '/images/tmp/',\n                        'url' => '/images/'\n                    ],\n                ]\n            ]\n        ];\n    }\n\n    public function rules()\n    {\n        return [\n            [['title', 'description'], 'required'],\n            ['file', 'safe']\n        ];\n    }\n}\n

Then we have to use TabularInput widget for rendering form field in the view file

Since version 2.18.0 you can configure columnOptions also.

<?php\n\nuse yii\\bootstrap\\ActiveForm;\nuse unclead\\multipleinput\\TabularInput;\nuse yii\\helpers\\Html;\nuse \\unclead\\multipleinput\\examples\\models\\Item;\n\n/* @var $this \\yii\\web\\View */\n/* @var $models Item[] */\n?>\n\n<?php $form = \\yii\\bootstrap\\ActiveForm::begin([\n    'id'                        => 'tabular-form',\n    'enableAjaxValidation'      => true,\n    'enableClientValidation'    => false,\n    'validateOnChange'          => false,\n    'validateOnSubmit'          => true,\n    'validateOnBlur'            => false,\n    'options' => [\n        'enctype' => 'multipart/form-data'\n    ]\n]) ?>\n\n<?= TabularInput::widget([\n    'models' => $models,\n    'attributeOptions' => [\n        'enableAjaxValidation'      => true,\n        'enableClientValidation'    => false,\n        'validateOnChange'          => false,\n        'validateOnSubmit'          => true,\n        'validateOnBlur'            => false,\n    ],\n    'columns' => [\n        [\n            'name'  => 'title',\n            'title' => 'Title',\n            'type'  => \\unclead\\multipleinput\\MultipleInputColumn::TYPE_TEXT_INPUT,\n        ],\n        [\n            'name'  => 'description',\n            'title' => 'Description',\n        ],\n        [\n            'name'  => 'file',\n            'title' => 'File',\n            'type'  => \\vova07\\fileapi\\Widget::className(),\n            'options' => [\n                'settings' => [\n                    'url' => ['site/fileapi-upload']\n                ]\n            ],\n            'columnOptions' => [\n                'style' => 'width: 250px;',\n                'class' => 'custom-css-class'\n            ]\n        ],\n        [\n            'name'  => 'date',\n            'type'  => \\kartik\\date\\DatePicker::className(),\n            'title' => 'Day',\n            'options' => [\n                'pluginOptions' => [\n                    'format' => 'dd.mm.yyyy',\n                    'todayHighlight' => true\n                ]\n            ],\n            'headerOptions' => [\n                'style' => 'width: 250px;',\n                'class' => 'day-css-class'\n            ]\n        ],\n    ],\n]) ?>\n\n\n<?= Html::submitButton('Update', ['class' => 'btn btn-success']);?>\n<?php ActiveForm::end();?>\n

Your action can look like the following code

/**\n * Class TabularInputAction\n * @package unclead\\multipleinput\\examples\\actions\n */\nclass TabularInputAction extends Action\n{\n    public function run()\n    {\n        Yii::setAlias('@unclead-examples', realpath(__DIR__ . '/../'));\n\n        $models = [new Item()];\n        $request = Yii::$app->getRequest();\n        if ($request->isPost && $request->post('ajax') !== null) {\n            $data = Yii::$app->request->post('Item', []);\n            foreach (array_keys($data) as $index) {\n                $models[$index] = new Item();\n            }\n            Model::loadMultiple($models, Yii::$app->request->post());\n            Yii::$app->response->format = Response::FORMAT_JSON;\n            $result = ActiveForm::validateMultiple($models);\n            return $result;\n        }\n\n        if (Model::loadMultiple($models, Yii::$app->request->post())) {\n            // your magic\n        }\n\n\n        return $this->controller->render('@unclead-examples/views/tabular-input.php', ['models' => $models]);\n    }\n}\n
"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..0992ecf --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,53 @@ + + + + None + 2024-04-21 + daily + + + None + 2024-04-21 + daily + + + None + 2024-04-21 + daily + + + None + 2024-04-21 + daily + + + None + 2024-04-21 + daily + + + None + 2024-04-21 + daily + + + None + 2024-04-21 + daily + + + None + 2024-04-21 + daily + + + None + 2024-04-21 + daily + + + None + 2024-04-21 + daily + + \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz new file mode 100644 index 0000000000000000000000000000000000000000..56ba20730a6213f6b13e6503980095623adce323 GIT binary patch literal 204 zcmV;-05ks|iwFoR8YN}||8r?{Wo=<_E_iKh0PU1b4#FT1h4(!LVPBw47iuWoxpbum zAQW2?3Kh`m?WI34y?{GrGjHC!%(olbyax?l(s^Y~4GTmdkVe>6nXZPn>p4HbhGq2W zw3h+Iu<Zzd+_2w4VV%?DPs6xCL+i#3wsS?w3x zhrzb2BxRM8Dldx|*}Pp$^xSmv?&O2DT0A2PucJi!zEZ}6qX|H!@opAG#_ G1pol>BwWh? literal 0 HcmV?d00001 diff --git a/tips-and-tricks/index.html b/tips-and-tricks/index.html new file mode 100644 index 0000000..246cb20 --- /dev/null +++ b/tips-and-tricks/index.html @@ -0,0 +1,788 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + Tips and Tricks - Yii2 multiple input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + + + + + +
+
+ + + + + + + + +

Tips and tricks

+

How to customize buttons

+

You can customize add and remove buttons via addButtonOptions and removeButtonOptions. Here is a simple example of how you can use those options:

+
    echo $form->field($model, 'emails')->widget(MultipleInput::className(), [
+        'max' => 5,
+        'addButtonOptions' => [
+            'class' => 'btn btn-success',
+            'label' => 'add' // also you can use html code
+        ],
+        'removeButtonOptions' => [
+            'label' => 'remove'
+        ]
+    ])
+    ->label(false);
+
+

How to add content after the buttons

+

You can add html content after add and remove buttons via extraButtons.

+
    echo $form->field($model, 'field')->widget(MultipleInput::className(), [
+        'rendererClass' => \unclead\multipleinput\renderers\ListRenderer::class,
+        'extraButtons' => function ($model, $index, $context) {
+            return Html::tag('span', '', ['class' => "btn-show-hide-{$index} glyphicon glyphicon-eye-open btn btn-info"]);
+        },
+    ])
+    ->label(false);
+
+

Work with an empty list

+

In some cases, you need to have the ability to delete all rows in the list. For this purpose, you can use option allowEmptyList like in the example below:

+
    echo $form->field($model, 'emails')->widget(MultipleInput::className(), [
+        'max' => 5,
+        'allowEmptyList' => true
+    ])
+    ->label(false);
+
+

Also, you can set 0 in min option if you don't need the first blank row when data is empty.

+

Guess column title

+

Sometimes you can use the widget without defining columns but you want to have the column header of the table. In this case, you can use the option enableGuessTitle like in the example below:

+
    echo $form->field($model, 'emails')->widget(MultipleInput::className(), [
+        'max' => 5,
+        'allowEmptyList' => true,
+        'enableGuessTitle' => true
+    ])
+    ->label(false);
+
+

Ajax loading of a widget

+

Assume you want to load a widget via ajax and then show it inside the modal window. In this case, you MUST:

+
    +
  • Ensure that you specified the ID of the widget otherwise the widget will get a random ID and it can be the same as the ID of others elements on the page.
  • +
  • Ensure that you use the widget inside ActiveForm because it works incorrectly in this case.
  • +
+

Use of a widget's placeholder

+

You can use a placeholder {multiple_index} in a widget configuration, e.g. for implementation of dependent drop-down lists.

+
    <?= $form->field($model, 'field')->widget(MultipleInput::className(), [
+            'id' => 'my_id',
+            'allowEmptyList' => false,
+            'rowOptions' => [
+                'id' => 'row{multiple_index_my_id}',
+            ],
+            'columns' => [
+                [
+                    'name'  => 'category',
+                    'type'  => 'dropDownList',
+                    'title' => 'Category',
+                    'defaultValue' => '1',
+                    'items' => [
+                        '1' => 'Test 1',
+                        '2' => 'Test 2',
+                        '3' => 'Test 3',
+                        '4' => 'Test 4',
+                    ],
+                    'options' => [
+                        'onchange' => <<< JS
+$.post("list?id=" + $(this).val(), function(data){
+    console.log(data);
+    $("select#subcat-{multiple_index_my_id}").html(data);
+});
+JS
+                    ],
+                ],
+                [
+                    'name'  => 'subcategory',
+                    'type'  => 'dropDownList',
+                    'title' => 'Subcategory',
+                    'items' => [],
+                    'options'=> [
+                        'id' => 'subcat-{multiple_index_my_id}'
+                    ],
+                ],
+            ]
+    ]);
+    ?>
+
+

Important Ensure that you added ID of widget to a base placeholder multiple_index

+

Custom index of the row

+

Assume that you want to set a specific index for each row. In this case, you can pass the data attribute as an associative array as in the example below:

+
    <?= $form->field($model, 'field')->widget(MultipleInput::className(), [
+            'allowEmptyList' => false,
+            'data' => [
+                3 => [
+                    'day'       => '27.02.2015',
+                    'user_id'   => 31,
+                    'priority'  => 1,
+                    'enable'    => 1
+                ],
+
+                'some-key' => [
+                    'day'       => '27.02.2015',
+                    'user_id'   => 33,
+                    'priority'  => 2,
+                    'enable'    => 0
+                ],
+            ]
+
+    ...
+
+

Embedded MultipleInput widget

+

You can use nested MultipleInput as in the example below:

+
echo MultipleInput::widget([
+    'model' => $model,
+    'attribute' => 'questions',
+    'attributeOptions' => $commonAttributeOptions,
+    'columns' => [
+        [
+            'name' => 'question',
+            'type' => 'textarea',
+        ],
+        [
+            'name' => 'answers',
+            'type'  => MultipleInput::class,
+            'options' => [
+                'attributeOptions' => $commonAttributeOptions,
+                'columns' => [
+                    [
+                        'name' => 'right',
+                        'type' => MultipleInputColumn::TYPE_CHECKBOX
+                    ],
+                    [
+                        'name' => 'answer'
+                    ]
+                ]
+            ]
+        ]
+    ],
+]);
+
+

But in this case, you have to pass attributeOptions to the widget otherwise, you will not be able to use ajax or client-side validation of data.

+

Client validation

+

Apart from ajax validation, you can use client validation but in this case, you MUST set property form. Also, ensure that you set enableClientValidation to true value in property attributeOptions. If you want to use client validation for a particular column you can use the property attributeOptions. An example of using client validation is listed below:

+
<?= TabularInput::widget([
+    'models' => $models,
+    'form' => $form,
+    'attributeOptions' => [
+        'enableAjaxValidation' => true,
+        'enableClientValidation' => false,
+        'validateOnChange' => false,
+        'validateOnSubmit' => true,
+        'validateOnBlur' => false,
+    ],
+    'columns' => [
+        [
+            'name' => 'id',
+            'type' => TabularColumn::TYPE_HIDDEN_INPUT
+        ],
+        [
+            'name' => 'title',
+            'title' => 'Title',
+            'type' => TabularColumn::TYPE_TEXT_INPUT,
+            'attributeOptions' => [
+                'enableClientValidation' => true,
+                'validateOnChange' => true,
+            ],
+            'enableError' => true
+        ],
+        [
+            'name' => 'description',
+            'title' => 'Description',
+        ],
+    ],
+]) ?>
+
+

In the example above we use client validation for column title and ajax validation for column description. As you can seee we also enabled validateOnChange for column title thus you can use all client-side options from the ActiveField class.

+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/usage/index.html b/usage/index.html new file mode 100644 index 0000000..e5f2590 --- /dev/null +++ b/usage/index.html @@ -0,0 +1,915 @@ + + + + + + + + + + + + + + + + + + + + + + + + Usage - Yii2 multiple input + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + +
+ +
+ + + + + + +
+
+ + + +
+
+
+ + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+ + + + + + + + +

Usage

+

You can find the source code of examples here

+

One column

+

Single column example

+

For example, your application contains the model User that has the related model UserEmail You can add virtual attribute emails for collect emails from a form and then you can save them to the database.

+

In this case, you can use yii2-multiple-input widget for supporting multiple inputs how to describe below.

+

First of all, we have to declare a virtual attribute in the model

+
class ExampleModel extends Model
+{
+    /**
+     * @var array virtual attribute for keeping emails
+     */
+    public $emails;
+
+

Then we have to use MultipleInput widget for rendering form field in the view file

+
use yii\bootstrap\ActiveForm;
+use unclead\multipleinput\MultipleInput;
+use unclead\multipleinput\examples\models\ExampleModel;
+use yii\helpers\Html;
+
+/* @var $this \yii\base\View */
+/* @var $model ExampleModel */
+?>
+
+<?php $form = ActiveForm::begin([
+    'enableAjaxValidation'      => true,
+    'enableClientValidation'    => false,
+    'validateOnChange'          => false,
+    'validateOnSubmit'          => true,
+    'validateOnBlur'            => false,
+]);?>
+
+<?= $form->field($model, 'emails')->widget(MultipleInput::className(), [
+    'max' => 4,
+ ]);
+?>
+<?= Html::submitButton('Update', ['class' => 'btn btn-success']);?>
+<?php ActiveForm::end();?>
+
+

Options max means that a user is able to input only 4 emails

+

For validation emails, you can use the following code

+
    /**
+     * Email validation.
+     *
+     * @param $attribute
+     */
+    public function validateEmails($attribute)
+    {
+        $items = $this->$attribute;
+
+        if (!is_array($items)) {
+            $items = [];
+        }
+
+        foreach ($items as $index => $item) {
+            $validator = new EmailValidator();
+            $error = null;
+            $validator->validate($item, $error);
+            if (!empty($error)) {
+                $key = $attribute . '[' . $index . ']';
+                $this->addError($key, $error);
+            }
+        }
+    }
+
+

Multiple columns

+

Multiple columns example

+

For example, you want to have an interface for manage a user schedule. For simplicity, we will store the schedule in json string.

+

In this case, you can use yii2-multiple-input widget for supporting multiple inputs how to describe below.

+

Our test model can look like as the following snippet

+
class ExampleModel extends Model
+{
+    public $schedule;
+
+    public function init()
+    {
+        parent::init();
+
+        $this->schedule = [
+            [
+                'day'       => '27.02.2015',
+                'user_id'   => 1,
+                'priority'  => 1
+            ],
+            [
+                'day'       => '27.02.2015',
+                'user_id'   => 2,
+                'priority'  => 2
+            ],
+        ];
+    }
+
+

Then we have to use MultipleInput widget for rendering form field in the view file

+
use yii\bootstrap\ActiveForm;
+use unclead\multipleinput\MultipleInput;
+use unclead\multipleinput\examples\models\ExampleModel;
+use yii\helpers\Html;
+
+/* @var $this \yii\base\View */
+/* @var $model ExampleModel */
+?>
+
+<?php $form = ActiveForm::begin([
+    'enableAjaxValidation'      => true,
+    'enableClientValidation'    => false,
+    'validateOnChange'          => false,
+    'validateOnSubmit'          => true,
+    'validateOnBlur'            => false,
+]);?>
+
+<?= $form->field($model, 'schedule')->widget(MultipleInput::className(), [
+    'max' => 4,
+    'columns' => [
+        [
+            'name'  => 'user_id',
+            'type'  => 'dropDownList',
+            'title' => 'User',
+            'defaultValue' => 1,
+            'items' => [
+                1 => 'User 1',
+                2 => 'User 2'
+            ]
+        ],
+        [
+            'name'  => 'day',
+            'type'  => \kartik\date\DatePicker::className(),
+            'title' => 'Day',
+            'value' => function($data) {
+                return $data['day'];
+            },
+            'items' => [
+                '0' => 'Saturday',
+                '1' => 'Monday'
+            ],
+            'options' => [
+                'pluginOptions' => [
+                    'format' => 'dd.mm.yyyy',
+                    'todayHighlight' => true
+                ]
+            ]
+        ],
+        [
+            'name'  => 'priority',
+            'title' => 'Priority',
+            'enableError' => true,
+            'options' => [
+                'class' => 'input-priority'
+            ]
+        ]
+    ]
+ ]);
+?>
+<?= Html::submitButton('Update', ['class' => 'btn btn-success']);?>
+<?php ActiveForm::end();?>
+
+

For validation of the schedule you can use the following code

+
    public function validateSchedule($attribute)
+    {
+        $requiredValidator = new RequiredValidator();
+
+        foreach($this->$attribute as $index => $row) {
+            $error = null;
+            $requiredValidator->validate($row['priority'], $error);
+            if (!empty($error)) {
+                $key = $attribute . '[' . $index . '][priority]';
+                $this->addError($key, $error);
+            }
+        }
+    }
+
+

For example, you keep some data in json format in an attribute of a model. Imagine that it is an abstract user schedule with keys: user_id, day, priority

+

On the edit page, you want to be able to manage this schedule and you can you yii2-multiple-input widget like in the following code

+
use unclead\multipleinput\MultipleInput;
+
+...
+
+<?= $form->field($model, 'schedule')->widget(MultipleInput::className(), [
+    'max' => 4,
+    'columns' => [
+        [
+            'name'  => 'user_id',
+            'type'  => 'dropDownList',
+            'title' => 'User',
+            'defaultValue' => 1,
+            'items' => [
+                1 => 'User 1',
+                2 => 'User 2'
+            ]
+        ],
+        [
+            'name'  => 'day',
+            'type'  => \kartik\date\DatePicker::className(),
+            'title' => 'Day',
+            'value' => function($data) {
+                return $data['day'];
+            },
+            'items' => [
+                '0' => 'Saturday',
+                '1' => 'Monday'
+            ],
+            'options' => [
+                'pluginOptions' => [
+                    'format' => 'dd.mm.yyyy',
+                    'todayHighlight' => true
+                ]
+            ],
+            'headerOptions' => [
+                'style' => 'width: 250px;',
+                'class' => 'day-css-class'
+            ]
+        ],
+        [
+            'name'  => 'priority',
+            'enableError' => true,
+            'title' => 'Priority',
+            'options' => [
+                'class' => 'input-priority'
+            ]
+        ],
+        [
+            'name'  => 'comment',
+            'type'  => 'static',
+            'value' => function($data) {
+                return Html::tag('span', 'static content', ['class' => 'label label-info']);
+            },
+            'headerOptions' => [
+                'style' => 'width: 70px;',
+            ]
+        ]
+    ]
+ ]);
+?>
+
+

Tabular input

+

For example, you want to have an interface for manage some abstract items via tabular input.

+

In this case, you can use yii2-multiple-input widget for supporting tabular input how to describe below.

+

Our test model can look like as the following snippet

+
namespace unclead\multipleinput\examples\models;
+
+use Yii;
+use yii\base\Model;
+// you have to install https://github.com/vova07/yii2-fileapi-widget
+use vova07\fileapi\behaviors\UploadBehavior;
+
+/**
+ * Class Item
+ * @package unclead\multipleinput\examples\models
+ */
+class Item extends Model
+{
+    public $title;
+    public $description;
+    public $file;
+    public $date;
+
+    public function behaviors()
+    {
+        return [
+            'uploadBehavior' => [
+                'class' => UploadBehavior::className(),
+                'attributes' => [
+                    'file' => [
+                        'path' => Yii::getAlias('@webroot') . '/images/',
+                        'tempPath' => Yii::getAlias('@webroot') . '/images/tmp/',
+                        'url' => '/images/'
+                    ],
+                ]
+            ]
+        ];
+    }
+
+    public function rules()
+    {
+        return [
+            [['title', 'description'], 'required'],
+            ['file', 'safe']
+        ];
+    }
+}
+
+

Then we have to use TabularInput widget for rendering form field in the view file

+

Since version 2.18.0 you can configure columnOptions also.

+
<?php
+
+use yii\bootstrap\ActiveForm;
+use unclead\multipleinput\TabularInput;
+use yii\helpers\Html;
+use \unclead\multipleinput\examples\models\Item;
+
+/* @var $this \yii\web\View */
+/* @var $models Item[] */
+?>
+
+<?php $form = \yii\bootstrap\ActiveForm::begin([
+    'id'                        => 'tabular-form',
+    'enableAjaxValidation'      => true,
+    'enableClientValidation'    => false,
+    'validateOnChange'          => false,
+    'validateOnSubmit'          => true,
+    'validateOnBlur'            => false,
+    'options' => [
+        'enctype' => 'multipart/form-data'
+    ]
+]) ?>
+
+<?= TabularInput::widget([
+    'models' => $models,
+    'attributeOptions' => [
+        'enableAjaxValidation'      => true,
+        'enableClientValidation'    => false,
+        'validateOnChange'          => false,
+        'validateOnSubmit'          => true,
+        'validateOnBlur'            => false,
+    ],
+    'columns' => [
+        [
+            'name'  => 'title',
+            'title' => 'Title',
+            'type'  => \unclead\multipleinput\MultipleInputColumn::TYPE_TEXT_INPUT,
+        ],
+        [
+            'name'  => 'description',
+            'title' => 'Description',
+        ],
+        [
+            'name'  => 'file',
+            'title' => 'File',
+            'type'  => \vova07\fileapi\Widget::className(),
+            'options' => [
+                'settings' => [
+                    'url' => ['site/fileapi-upload']
+                ]
+            ],
+            'columnOptions' => [
+                'style' => 'width: 250px;',
+                'class' => 'custom-css-class'
+            ]
+        ],
+        [
+            'name'  => 'date',
+            'type'  => \kartik\date\DatePicker::className(),
+            'title' => 'Day',
+            'options' => [
+                'pluginOptions' => [
+                    'format' => 'dd.mm.yyyy',
+                    'todayHighlight' => true
+                ]
+            ],
+            'headerOptions' => [
+                'style' => 'width: 250px;',
+                'class' => 'day-css-class'
+            ]
+        ],
+    ],
+]) ?>
+
+
+<?= Html::submitButton('Update', ['class' => 'btn btn-success']);?>
+<?php ActiveForm::end();?>
+
+

Your action can look like the following code

+
/**
+ * Class TabularInputAction
+ * @package unclead\multipleinput\examples\actions
+ */
+class TabularInputAction extends Action
+{
+    public function run()
+    {
+        Yii::setAlias('@unclead-examples', realpath(__DIR__ . '/../'));
+
+        $models = [new Item()];
+        $request = Yii::$app->getRequest();
+        if ($request->isPost && $request->post('ajax') !== null) {
+            $data = Yii::$app->request->post('Item', []);
+            foreach (array_keys($data) as $index) {
+                $models[$index] = new Item();
+            }
+            Model::loadMultiple($models, Yii::$app->request->post());
+            Yii::$app->response->format = Response::FORMAT_JSON;
+            $result = ActiveForm::validateMultiple($models);
+            return $result;
+        }
+
+        if (Model::loadMultiple($models, Yii::$app->request->post())) {
+            // your magic
+        }
+
+
+        return $this->controller->render('@unclead-examples/views/tabular-input.php', ['models' => $models]);
+    }
+}
+
+ + + + + + +
+
+ + +
+ +
+ + + +
+
+
+
+ + + + + + + + + \ No newline at end of file