mithril-materialized 1.4.2 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/autocomplete.d.ts +12 -4
  2. package/dist/button.d.ts +10 -10
  3. package/dist/carousel.d.ts +26 -7
  4. package/dist/chip.d.ts +2 -2
  5. package/dist/code-block.d.ts +2 -3
  6. package/dist/collapsible.d.ts +12 -7
  7. package/dist/collection.d.ts +8 -8
  8. package/dist/dropdown.d.ts +5 -5
  9. package/dist/floating-action-button.d.ts +9 -5
  10. package/dist/icon.d.ts +2 -2
  11. package/dist/index.css +7968 -2
  12. package/dist/index.d.ts +1 -2
  13. package/dist/index.esm.js +4211 -2
  14. package/dist/index.js +4265 -2
  15. package/dist/index.umd.js +4269 -2
  16. package/dist/input-options.d.ts +3 -1
  17. package/dist/input.d.ts +17 -12
  18. package/dist/label.d.ts +4 -5
  19. package/dist/material-box.d.ts +22 -4
  20. package/dist/material-icon.d.ts +14 -0
  21. package/dist/modal.d.ts +23 -7
  22. package/dist/option.d.ts +12 -11
  23. package/dist/pagination.d.ts +5 -9
  24. package/dist/parallax.d.ts +8 -6
  25. package/dist/pickers.d.ts +129 -5
  26. package/dist/radio.d.ts +10 -6
  27. package/dist/search-select.d.ts +2 -2
  28. package/dist/select.d.ts +5 -5
  29. package/dist/switch.d.ts +4 -5
  30. package/dist/tabs.d.ts +18 -5
  31. package/dist/utils.d.ts +17 -0
  32. package/package.json +35 -10
  33. package/sass/components/_badges.scss +59 -0
  34. package/sass/components/_buttons.scss +327 -0
  35. package/sass/components/_cards.scss +197 -0
  36. package/sass/components/_carousel.scss +92 -0
  37. package/sass/components/_chips.scss +92 -0
  38. package/sass/components/_collapsible.scss +94 -0
  39. package/sass/components/_color-classes.scss +34 -0
  40. package/sass/components/_color-variables.scss +371 -0
  41. package/sass/components/_datepicker.scss +251 -0
  42. package/sass/components/_dropdown.scss +90 -0
  43. package/sass/components/_global.scss +775 -0
  44. package/sass/components/_grid.scss +160 -0
  45. package/sass/components/_icons-material-design.scss +5 -0
  46. package/sass/components/_materialbox.scss +43 -0
  47. package/sass/components/_modal.scss +97 -0
  48. package/sass/components/_navbar.scss +211 -0
  49. package/sass/components/_normalize.scss +447 -0
  50. package/sass/components/_preloader.scss +336 -0
  51. package/sass/components/_pulse.scss +34 -0
  52. package/sass/components/_sidenav.scss +213 -0
  53. package/sass/components/_slider.scss +94 -0
  54. package/sass/components/_table_of_contents.scss +36 -0
  55. package/sass/components/_tabs.scss +102 -0
  56. package/sass/components/_tapTarget.scss +105 -0
  57. package/sass/components/_timepicker.scss +170 -0
  58. package/sass/components/_toast.scss +61 -0
  59. package/sass/components/_tooltip.scss +32 -0
  60. package/sass/components/_transitions.scss +13 -0
  61. package/sass/components/_typography.scss +61 -0
  62. package/sass/components/_variables.scss +352 -0
  63. package/sass/components/_waves.scss +114 -0
  64. package/sass/components/forms/_checkboxes.scss +203 -0
  65. package/sass/components/forms/_file-input.scss +50 -0
  66. package/sass/components/forms/_form-groups.scss +28 -0
  67. package/sass/components/forms/_forms.scss +24 -0
  68. package/sass/components/forms/_input-fields.scss +361 -0
  69. package/sass/components/forms/_radio-buttons.scss +118 -0
  70. package/sass/components/forms/_range.scss +164 -0
  71. package/sass/components/forms/_select.scss +193 -0
  72. package/sass/components/forms/_switches.scss +92 -0
  73. package/sass/materialize.scss +41 -0
  74. package/dist/index.css.map +0 -1
  75. package/dist/index.esm.js.map +0 -1
  76. package/dist/index.js.map +0 -1
  77. package/dist/index.modern.js +0 -2
  78. package/dist/index.modern.js.map +0 -1
  79. package/dist/index.umd.js.map +0 -1
  80. package/dist/map-editor.d.ts +0 -63
  81. package/dist/timeline.d.ts +0 -24
package/dist/index.esm.js CHANGED
@@ -1,2 +1,4211 @@
1
- import e from"mithril";function t(){return t=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},t.apply(this,arguments)}function n(e,t){if(null==e)return{};var n,i,a={},o=Object.keys(e);for(i=0;i<o.length;i++)t.indexOf(n=o[i])>=0||(a[n]=e[n]);return a}var i,a=function(){return"idxxxxxxxx".replace(/[x]/g,function(){return(16*Math.random()|0).toString(16)})},o=function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})},r=function(e){return!isNaN(parseFloat(e))&&isFinite(e)},l=function(e,t,n){return void 0===t&&(t=2),void 0===n&&(n="0"),String(e).padStart(t,n)},c=["label","id","isMandatory","isActive","className"],s={view:function(t){return e("span.mandatory",t.attrs,"*")}},d=function(){return{view:function(i){var a=i.attrs,o=a.label,r=a.id,l=a.isMandatory,d=a.isActive,u=a.className,p=n(a,c);return o?e("label",t({},p,{className:[u,d?"active":""].filter(Boolean).join(" ").trim(),for:r}),[e.trust(o),l?e(s):void 0]):void 0}}},u=function(){return{view:function(t){var n=t.attrs,i=n.helperText,a=n.dataError,o=n.dataSuccess;return i||a||o?e("span.helper-text.left",{className:n.className,"data-error":a,"data-success":o},i?e.trust(i):""):void 0}}},p=["label","helperText","initialValue","onchange","newRow","className","style","iconName","isMandatory"],f=function(){var i={id:a()};return{view:function(a){var o=a.attrs,r=o.id||i.id,l=o.label,c=o.helperText,s=o.initialValue,f=o.onchange,m=o.newRow,v=o.className,h=void 0===v?"col s12":v,g=o.style,b=o.iconName,y=o.isMandatory,w=n(o,p);return e(".input-field"+(m?".clear":""),{className:m?h+" clear":h,style:g},[b?e("i.material-icons.prefix",b):"",e("input",t({},w,{className:"autocomplete",type:"text",tabindex:0,id:r,oncreate:function(e){M.Autocomplete.init(e.dom,o)},onchange:f?function(e){e.target&&e.target.value&&f(e.target.value)}:void 0,value:s})),e(d,{label:l,id:r,isMandatory:y,isActive:s}),e(u,{helperText:c})])}}},m=["iconName"],v=function(){return{view:function(t){var i=t.attrs,a=i.iconName,o=n(i,m);return e("i.material-icons",o,a)}}},h=["modalId","tooltip","tooltipPostion","iconName","iconClass","label","className","attr"],g=function(i,a,o){return void 0===o&&(o=""),function(){return{view:function(r){var l=r.attrs,c=l.modalId,s=l.tooltip,d=l.tooltipPostion,u=l.iconName,p=l.iconClass,f=l.label,m=l.className,g=l.attr,b=n(l,h),y=[c?"modal-trigger":"",s?"tooltipped":"",a,m].filter(Boolean).join(" ").trim();return e(i,t({},b,g,{className:y,href:c?"#"+c:void 0,"data-position":s?d||"top":void 0,"data-tooltip":s||void 0,type:o}),u?e(v,{iconName:u,className:p||"left"}):void 0,f||void 0)}}}},b=g("a","waves-effect waves-light btn","button"),y=g("a","waves-effect waves-light btn-large","button"),w=g("a","waves-effect waves-light btn-small","button"),x=g("a","waves-effect waves-teal btn-flat","button"),k=g("button","btn-floating btn-large waves-effect waves-light","button"),N=g("button","btn waves-effect waves-light","submit"),I=["href","src","alt"],A=function(){return{view:function(i){var a=i.attrs,o=a.href,r=a.src,l=a.alt,c=n(a,I);return e("a.carousel-item",t({},c,{href:o}),e("img",{src:r,alt:l}))}}},C=function(){return{view:function(t){var n=t.attrs,i=n.items;return i&&i.length>0?e(".carousel",{oncreate:function(e){M.Carousel.init(e.dom,n)}},i.map(function(t){return e(A,t)})):void 0}}},T=function(){var t={chipsData:[],selectedChip:null,focused:!1,inputValue:"",inputId:a(),autocompleteItems:[],selectedAutocompleteIndex:-1,showAutocomplete:!1},n=null,i=function(){var e;if(null!=(e=n)&&null!=(e=e.attrs.autocompleteOptions)&&e.data){var i=n.attrs.autocompleteOptions,a=i.data,o=i.minLength,r=void 0===o?1:o,l=t.inputValue.toLowerCase();if(l.length<r)return t.autocompleteItems=[],void(t.showAutocomplete=!1);var c=function(e){return Array.isArray(e)?e.map(function(e){return"string"==typeof e?{tag:e}:e}):Object.entries(e).map(function(e){var t=e[0];return{tag:t,value:e[1]||t}})}(a),s=c.filter(function(e){return e.tag.toLowerCase().includes(l)});t.autocompleteItems=s.slice(0,n.attrs.autocompleteOptions.limit||Infinity),t.showAutocomplete=t.autocompleteItems.length>0,t.selectedAutocompleteIndex=-1}else t.autocompleteItems=[]},o=function(e){r({tag:e.tag,image:e.image,alt:e.alt}),t.inputValue="",t.showAutocomplete=!1,t.selectedAutocompleteIndex=-1},r=function(e){if(n){var i=n.attrs,a=i.limit,o=void 0===a?Infinity:a,r=i.onChipAdd,l=i.onchange;!function(e,t){return!(!e.tag||""===e.tag.trim()||t.some(function(t){return t.tag===e.tag}))}(e,t.chipsData)||t.chipsData.length>=o||(t.chipsData=[].concat(t.chipsData,[e]),t.inputValue="",r&&r(e),l&&l(t.chipsData))}},l=function(e){if(n){var i=n.attrs,a=i.onChipDelete,o=i.onchange,r=t.chipsData[e];t.chipsData=t.chipsData.filter(function(t,n){return n!==e}),t.selectedChip=null,a&&a(r),o&&o(t.chipsData)}},c=function(e){if(n){var i=n.attrs.onChipSelect;t.selectedChip=e,i&&t.chipsData[e]&&i(t.chipsData[e])}},s=function(i){var a=i.target;if(t.showAutocomplete){if("ArrowDown"===i.key){var s;i.preventDefault(),t.selectedAutocompleteIndex=Math.min(t.selectedAutocompleteIndex+1,t.autocompleteItems.length-1);var d=null==(s=n)?void 0:s.dom.querySelector(".autocomplete-item.selected");return d&&d.scrollIntoView({block:"nearest"}),void e.redraw()}if("ArrowUp"===i.key){var u;i.preventDefault(),t.selectedAutocompleteIndex=Math.max(t.selectedAutocompleteIndex-1,-1);var p=null==(u=n)?void 0:u.dom.querySelector(".autocomplete-item.selected");return p&&p.scrollIntoView({block:"nearest"}),void e.redraw()}if("Enter"===i.key&&t.selectedAutocompleteIndex>=0)return i.preventDefault(),void o(t.autocompleteItems[t.selectedAutocompleteIndex])}"Enter"===i.key&&a.value.trim()?(i.preventDefault(),r({tag:a.value.trim()})):"Backspace"===i.key&&!a.value&&t.chipsData.length>0?(i.preventDefault(),l(t.chipsData.length-1)):"ArrowLeft"===i.key&&!a.value&&t.chipsData.length&&(i.preventDefault(),c(t.chipsData.length-1))};return{oninit:function(e){t.chipsData=e.attrs.data||[]},oncreate:function(e){n=e},onremove:function(){n=null},view:function(a){var r=a.attrs,p=r.isMandatory,f=void 0===p?r.required:p,m=r.className,v=r.label,h=r.helperText,g=r.placeholder,b=r.secondaryPlaceholder;return e(".input-field",{id:r.id,className:void 0===m?"col s12":m},[e(".chips.chips-initial",{class:"chips-container "+(t.focused?"focused":"")+" "+(g?"chips-placeholder":"")},[t.chipsData.map(function(i,a){return e(".chip",{key:i.tag+"-"+a,tabindex:0,class:t.selectedChip===a?"selected":"",onkeydown:function(e){return function(e,i){if("Backspace"===e.key||"Delete"===e.key){e.preventDefault(),l(i);var a=Math.max(i-1,0);t.chipsData.length&&c(a)}else if("ArrowLeft"===e.key&&i>0)c(i-1);else if("ArrowRight"===e.key)if(i<t.chipsData.length-1)c(i+1);else{var o,r=null==(o=n)?void 0:o.dom.querySelector(".chips-input");r&&r.focus()}}(e,a)}},[i.image&&e("img",{src:i.image,alt:i.alt||i.tag}),i.tag,e("i.material-icons.close",{onclick:function(e){e.stopPropagation(),l(a)}},"close")])}),e("input.chips-input.input",{id:t.inputId,title:"label",value:t.inputValue,placeholder:!t.chipsData.length&&g?g:t.chipsData.length&&b?b:"",oninput:function(e){t.inputValue=e.target.value,i()},onfocus:function(){t.focused=!0,t.selectedChip=null,i()},onblur:function(){t.focused=!1,setTimeout(function(){t.showAutocomplete=!1,t.selectedChip=null,e.redraw()},150)},onkeydown:s}),t.showAutocomplete&&e("ul.autocomplete-content.dropdown-content",{style:{display:"block",opacity:1,transform:"scaleX(1) scaleY(1)",position:"absolute",width:"100%",left:0,top:"100%",maxHeight:"200px",overflow:"auto",zIndex:1e3,backgroundColor:"#fff",boxShadow:"0 2px 2px 0 rgba(0,0,0,0.14), 0 3px 1px -2px rgba(0,0,0,0.12), 0 1px 5px 0 rgba(0,0,0,0.2)"}},t.autocompleteItems.map(function(n,i){return e("li.autocomplete-item",{key:n.tag,class:t.selectedAutocompleteIndex===i?"selected":"",style:{padding:"12px 16px",cursor:"pointer",backgroundColor:t.selectedAutocompleteIndex===i?"#eee":"transparent"},onmousedown:function(e){e.preventDefault(),o(n)},onmouseover:function(){t.selectedAutocompleteIndex=i}},[n.image&&e("img.autocomplete-item-image",{src:n.image,alt:n.alt||n.tag,style:{width:"24px",height:"24px",marginRight:"8px",verticalAlign:"middle"}}),e("span.autocomplete-item-text",n.tag)])}))]),v&&e(d,{label:v,id:t.inputId,isMandatory:f,isActive:!!(t.focused||t.chipsData.length||g)}),h&&e(u,{helperText:h})])}}},D=["newRow","code","language","className"],V=function(){return{view:function(i){var a=i.attrs,o=a.newRow,r=a.code,l=a.language,c=a.className,s=n(a,D),d=l||"lang-TypeScript",u=d.replace("lang-",""),p=r instanceof Array?r.join("\n"):r,f=[o?"clear":"",d,c].filter(Boolean).join(" ").trim();return e("pre.codeblock"+(o?".clear":""),a,[e("div",e("label",u)),e("code",t({},s,{className:f}),p)])}}},O=function(){return{view:function(t){var n=t.attrs,i=n.header,a=n.body,o=n.iconName;return e(n.active?"li.active":"li",[i||o?e(".collapsible-header",[o?e("i.material-icons",o):void 0,i?"string"==typeof i?e("span",i):i:void 0]):void 0,a?e(".collapsible-body",a):void 0])}}},S=function(){return{oncreate:function(e){M.Collapsible.init(e.dom,e.attrs)},view:function(t){var n=t.attrs,i=n.items;return i&&i.length>0?e("ul.collapsible",{class:n.class||n.className,style:n.style,id:n.id},i.map(function(t){return e(O,t)})):void 0}}},R=["header","items","mode"],L=["title","active","href"],j=["items","header"],B=["items","header","mode"];!function(e){e[e.BASIC=0]="BASIC",e[e.LINKS=1]="LINKS",e[e.AVATAR=2]="AVATAR"}(i||(i={}));var E=function(e){return e&&/https?:\/\//.test(e)},F=function(){return{view:function(t){var n=t.attrs,i=n.href,a=n.iconName,o=void 0===a?"send":a,r=n.onclick,l=n.style,c={href:i,style:void 0===l?{cursor:"pointer"}:l,className:"secondary-content",onclick:r?function(){return r(n)}:void 0};return E(i)||!i?e("a[target=_]",c,e(v,{iconName:o})):e(e.route.Link,c,e(v,{iconName:o}))}}},K=function(e){return void 0===e&&(e=""),/\./.test(e)},P=function(){return{view:function(t){var n=t.attrs,a=n.item,o=a.title,r=a.content,l=void 0===r?"":r,c=a.active,s=a.iconName,d=a.avatar,u=a.className,p=a.onclick;return n.mode===i.AVATAR?e("li.collection-item.avatar",{className:c?"active":"",onclick:p?function(){return p(a)}:void 0},[K(d)?e("img.circle",{src:d}):e("i.material-icons.circle",{className:u},d),e("span.title",o),e("p",e.trust(l)),e(F,a)]):e("li.collection-item",{className:c?"active":""},s?e("div",[o,e(F,a)]):o)}}},_=function(){return{view:function(t){var a=t.attrs,o=a.header,r=a.items,l=a.mode,c=void 0===l?i.BASIC:l,s=n(a,R),d=r.map(function(t){return e(P,{key:t.id,item:t,mode:c})});return o?e("ul.collection.with-header",s,[e("li.collection-header",e("h4",o)),d]):e("ul.collection",s,d)}}},z=function(){return{view:function(i){var a=i.attrs.item,o=a.title,r=a.active,l=a.href,c=t({},n(a,L),{className:"collection-item "+(r?"active":""),href:l});return E(l)||!l?e("a[target=_]",c,o):e(e.route.Link,c,o)}}},H=function(){return{view:function(t){var i=t.attrs,a=i.items,o=i.header,r=n(i,j);return o?e(".collection.with-header",r,[e(".collection-header",e("h4",o)),a.map(function(t){return e(z,{key:t.id,item:t})})]):e(".collection",r,a.map(function(t){return e(z,{key:t.id,item:t})}))}}},U=function(){return{view:function(a){var o=a.attrs,r=o.items,l=o.header,c=o.mode,s=void 0===c?i.BASIC:c,d=n(o,B);return l||r&&r.length>0?s===i.LINKS?e(H,t({header:l,items:r},d)):e(_,t({header:l,items:r,mode:s},d)):void 0}}},q=["key","label","onchange","disabled","items","iconName","helperText","style","className"],W=function(){var t={};return{oninit:function(e){var n=e.attrs,i=n.id,o=void 0===i?a():i,r=n.initialValue,l=n.checkedId;t.id=o,t.initialValue=r||l},view:function(i){var a=i.attrs,o=a.key,r=a.label,l=a.onchange,c=a.disabled,s=void 0!==c&&c,d=a.items,p=a.iconName,f=a.helperText,m=a.style,v=a.className,h=void 0===v?"col s12":v,g=n(a,q),b=t.id,y=t.initialValue,w=y?d.filter(function(e){return e.id?e.id===y:e.label===y}).shift():void 0,x=w?w.label:r||"Select";return e(".input-field",{className:h,key:o,style:m},[p?e("i.material-icons.prefix",p):void 0,e(u,{helperText:f}),e("a.dropdown-trigger.btn.truncate[href=#]",{"data-target":b,disabled:s,className:"col s12",style:m||(p?"margin: 0.2em 0 0 3em;":void 0),oncreate:function(e){M.Dropdown.init(e.dom,g)}},x),e("ul.dropdown-content",{id:b},d.map(function(n){return e("li[tabindex=-1]",{className:n.divider?"divider":""},n.divider?void 0:e("a",{onclick:l?function(){t.initialValue=n.id||n.label,l(t.initialValue)}:void 0},[n.iconName?e("i.material-icons",n.iconName):void 0,n.label]))}))])}}},Y=["className","iconName","iconClass","position","style","buttons"],$=function(){return{view:function(t){var i=t.attrs,a=i.className,o=i.iconName,r=i.iconClass,l=void 0===r?"large":r,c=i.position,s=i.style,d=void 0===s?"left"===c||"inline-left"===c?"position: absolute; display: inline-block; left: 24px;":"right"===c||"inline-right"===c?"position: absolute; display: inline-block; right: 24px;":void 0:s,u=i.buttons,p=n(i,Y),f=e(".fixed-action-btn",{style:d,oncreate:function(e){return M.FloatingActionButton.init(e.dom,p)}},[e("a.btn-floating.btn-large",{className:a},e("i.material-icons",{classNames:l},o)),u?e("ul",u.map(function(t){return e("li",e("a.btn-floating",{className:t.className,onclick:function(e){return t.onClick&&t.onClick(e)}},e("i.material-icons",{className:t.iconClass},t.iconName)))})):void 0]);return"inline-right"===c||"inline-left"===c?e("div",{style:"position: relative; height: 70px;"},f):f}}},X=["className","helperText","iconName","id","initialValue","isMandatory","label","onchange","onkeydown","onkeypress","onkeyup","onblur","style"],G=["className","dataError","dataSuccess","helperText","iconName","id","initialValue","isMandatory","label","maxLength","newRow","onchange","onkeydown","onkeypress","onkeyup","onblur","style","validate"],J=function(){var i={id:a()};return{view:function(a){var o=a.attrs,r=o.className,l=void 0===r?"col s12":r,c=o.helperText,s=o.iconName,p=o.id,f=void 0===p?i.id:p,m=o.initialValue,v=o.isMandatory,h=o.label,g=o.onchange,b=o.onkeydown,y=o.onkeypress,w=o.onkeyup,x=o.onblur,k=o.style,N=n(o,X);return e(".input-field",{className:l,style:k},[s?e("i.material-icons.prefix",s):"",e("textarea.materialize-textarea",t({},N,{id:f,tabindex:0,oncreate:function(e){var t=e.dom;M.textareaAutoResize(t),o.maxLength&&M.CharacterCounter.init(t)},onchange:g?function(e){var t=e.target;g(t&&"string"==typeof t.value?t.value:"")}:void 0,value:m,onkeyup:w?function(e){w(e,e.target.value)}:void 0,onkeydown:b?function(e){b(e,e.target.value)}:void 0,onkeypress:y?function(e){y(e,e.target.value)}:void 0,onblur:x})),e(d,{label:h,id:f,isMandatory:v,isActive:m||o.placeholder}),e(u,{helperText:c})])}}},Q=function(i,o){return void 0===o&&(o=""),function(){var r={id:a()},l=function(e){var t=e.value;return!t||"number"!==i&&"range"!==i?t:+t},c=function(e,t){e.setCustomValidity("boolean"==typeof t?t?"":"Custom validation failed":t)};return{view:function(a){var s=a.attrs,p=s.className,f=void 0===p?"col s12":p,m=s.dataError,v=s.dataSuccess,h=s.helperText,g=s.iconName,b=s.id,y=void 0===b?r.id:b,w=s.initialValue,x=s.isMandatory,k=s.label,N=s.maxLength,I=s.newRow,A=s.onchange,C=s.onkeydown,T=s.onkeypress,D=s.onkeyup,V=s.onblur,O=s.style,S=s.validate,R=n(s,G),L=[I?"clear":"",o,f].filter(Boolean).join(" ").trim();return e(".input-field",{className:L,style:O},[g?e("i.material-icons.prefix",g):void 0,e("input.validate",t({},R,{type:i,tabindex:0,id:y,oncreate:function(e){var t,n=e.dom;(t=s.autofocus)&&("boolean"==typeof t?t:t())&&n.focus(),N&&M.CharacterCounter.init(n),"range"===i&&M.Range.init(n)},onkeyup:D?function(e){D(e,l(e.target))}:void 0,onkeydown:C?function(e){C(e,l(e.target))}:void 0,onkeypress:T?function(e){T(e,l(e.target))}:void 0,onblur:V,onupdate:S?function(e){var t=e.dom;c(t,S(l(t),t))}:void 0,onchange:function(e){var t=e.target;if(t){var n=l(t);A&&A(n),S&&c(t,S(n,t))}},value:w})),e(d,{label:k,id:y,isMandatory:x,isActive:!(void 0===w&&!s.placeholder&&"number"!==i&&"color"!==i&&"range"!==i)}),e(u,{helperText:h,dataError:m,dataSuccess:v})])}}}},Z=Q("text"),ee=Q("password"),te=Q("number"),ne=Q("url"),ie=Q("color"),ae=Q("range",".range-field"),oe=Q("email"),re=function(){var t,n=!1;return{view:function(i){var a=i.attrs,o=a.multiple,r=a.disabled,l=a.initialValue,c=a.placeholder,s=a.onchange,d=a.className,u=void 0===d?"col s12":d,p=a.accept,f=a.label,m=void 0===f?"File":f,v=p?p instanceof Array?p.join(", "):p:void 0;return e(".file-field.input-field",{className:a.class||u},[e(".btn",[e("span",m),e("input[type=file]",{title:m,accept:v,multiple:o,disabled:r,onchange:s?function(e){var t=e.target;t&&t.files&&s&&(n=!0,s(t.files))}:void 0})]),e(".file-path-wrapper",e("input.file-path.validate[type=text]",{placeholder:c,oncreate:function(e){t=e.dom,l&&(t.value=l)}})),(n||l)&&e("a.waves-effect.waves-teal.btn-flat",{style:{float:"right",position:"relative",top:"-3rem",padding:0},onclick:function(){n=!1,t.value="",s&&s({})}},e("i.material-icons","clear"))])}}},le=function(){return{view:function(t){var n=t.attrs,i=n.className,a=n.onchange,o=n.label,r=n.description;return e("div",{className:void 0===i?"col s12":i,style:n.style},e("label",[e("input[type=checkbox][tabindex=0]",{checked:n.checked,disabled:n.disabled,onclick:a?function(e){e.target&&void 0!==e.target.checked&&a(e.target.checked)}:void 0}),o?"string"==typeof o?e("span",o):o:void 0]),r&&e(u,{className:"input-checkbox-desc",helperText:r}))}}},ce=function(){var t={},n=function(e){return t.checkedIds.indexOf(e)>=0};return{oninit:function(e){var n=e.attrs,i=n.checkedId,a=i||n.initialValue;t.checkedId=i,t.checkedIds=a?a instanceof Array?[].concat(a):[a]:[]},view:function(i){var a=i.attrs,o=a.label,r=a.id,l=a.options,c=a.checkedId,s=a.description,p=a.className,f=void 0===p?"col s12":p,m=a.style,v=a.disabled,h=a.checkboxClass,g=a.newRow,b=a.isMandatory,y=a.onchange;c&&t.checkedId!==c&&(t.checkedId=c,t.checkedIds=c instanceof Array?c:[c]);var w=y?function(e,n){var i=t.checkedIds.filter(function(t){return t!==e});n&&i.push(e),t.checkedIds=i,y(i)}:void 0,x=[g?"clear":"",f].filter(Boolean).join(" ").trim();return e("div",{className:x,style:m},[e("div",{className:"input-field options"},e(d,{id:r,label:o,isMandatory:b})),e(u,{helperText:s})].concat(l.map(function(t){return e(le,{disabled:v||t.disabled,label:t.label,onchange:w?function(e){return w(t.id,e)}:void 0,className:t.className||h,checked:n(t.id),description:t.description})})))}}},se=function(){var t=function(e){return n.curKey=n.id=e},n={elementId:a(),id:"",curKey:"",kvc:function(t,n,i){var a=i.keyClass,o=void 0===a?".col.s4":a,r=i.valueClass,l=void 0===r?".col.s8":r,c=n instanceof Array?n.join(", "):"boolean"==typeof n?e(le,{label:" ",checked:n,disabled:!0,className:"checkbox-in-collection"}):n.toString();return{title:e(".row",{style:"margin-bottom: 0"},[e(o,e("b",t)),e(l,c)])}}},o=function(){n.id="",n.curKey=""};return{oninit:function(e){var t=e.attrs,i=t.keyValueConverter,a=t.id;i&&(n.kvc=i),a&&(n.elementId=a)},view:function(a){var r=a.attrs,l=r.className,c=void 0===l?"col s12":l,s=r.disabled,u=r.disallowArrays,p=r.header,f=r.iconName,m=r.iconNameKey,v=void 0===m?f?"label":void 0:m,h=r.isMandatory,g=r.label,b=r.labelKey,y=void 0===b?"Key":b,w=r.labelValue,k=void 0===w?"Value":w,N=r.properties,I=r.onchange,A=r.falsy,C=void 0===A?["false"]:A,M=r.truthy,T=void 0===M?["true"]:M,D=function(){return I?I(N):void 0},V=function(e,i){return Object.keys(e).map(function(t){return{key:t,value:e[t]}}).map(function(e){return function(e,i){var a=i.onclick;return i.id=i.id||e,i.active=e===n.curKey,i.onclick=a?function(){return t(e)&&a(i)}:function(){return t(e)},i}(e.key,n.kvc(e.key,e.value,{keyClass:i.keyClass,valueClass:i.valueClass}))})}(N,{keyClass:r.keyClass,valueClass:r.valueClass}),O=n.curKey,S=N[O],R="boolean"==typeof S||"number"==typeof S?S:S?S instanceof Array?"["+S.join(", ")+"]":S:"",L=n.elementId;return[e(".map-editor",e(".input-field",{className:c,style:"min-height: 1.5em;"},[f?e("i.material-icons.prefix",f):"",e(d,{label:g,isMandatory:h,isActive:V.length>0}),e(U,{id:L,items:V,mode:i.LINKS,header:p})])),s?void 0:[e(Z,{label:y,iconName:v,className:"col s5",initialValue:O,onchange:function(e){n.curKey=e,n.id&&(delete N[n.id],N[e]=S,n.id=e),D()}}),"string"==typeof R?e(J,{label:k,initialValue:R,className:"col s7",onchange:function(e){var t=function(e,t,n){return t.indexOf(e)>=0||!(n.indexOf(e)>=0)&&void 0}(e,T,C),n=void 0===t&&/^\s*\d+\s*$/i.test(e)?+e:void 0;N[O]="boolean"==typeof t?t:"number"==typeof n?n:function(e,t){if(void 0===t&&(t=!1),t)return e;if(e){var n=/\s*\[(.*)\]\s*/gi.exec(e);if(n&&2===n.length)return n[1].split(",").map(function(e){return e.trim()}).map(function(e){return/^\d+$/g.test(e)?+e:e})}}(e,u)||e,D()}}):"number"==typeof R?e(te,{label:k,initialValue:R,className:"col s7",onchange:function(e){N[O]=e,D()}}):e(le,{label:k,checked:R,className:"input-field col s7",onchange:function(e){N[O]=e,D()}}),e(".col.s12.right-align",[e(x,{iconName:"add",onclick:o}),e(x,{iconName:"delete",disabled:!O,onclick:function(){delete N[O],o(),D()}})])]]}}},de=function(){return{oncreate:function(e){M.Materialbox.init(e.dom,e.attrs)},view:function(t){return e("img.materialboxed",t.attrs)}}},ue=function(){return{oncreate:function(e){var t=e.attrs,n=t.onCreate,i=M.Modal.init(e.dom,t.options);n&&n(i)},view:function(n){var i=n.attrs,a=i.id,o=i.title,r=i.description,l=i.buttons,c=i.richContent,s=[i.className,i.fixedFooter?"modal-fixed-footer":"",i.bottomSheet?"bottom-sheet":""].filter(Boolean).join(" ").trim();return e(".modal",{id:a,className:s},[e(".modal-content",[e("h4",o),c&&"string"==typeof r?e.trust(r||""):"string"==typeof r?e("p",r):r]),l?e(".modal-footer",l.map(function(n){return e(x,t({},n,{className:"modal-close"}))})):void 0])}}},pe=function(){return{view:function(t){var n=t.attrs,i=n.title;return e("li",{className:n.active?"active":n.disabled?"disabled":"waves-effect"},"number"==typeof i?e(e.route.Link,{href:n.href},i):i)}}},fe=function(){var n={pagIndex:0};return{view:function(i){var a=i.attrs,o=a.items,r=a.curPage,l=void 0===r?1:r,c=a.size,s=void 0===c?Math.min(9,o.length):c,d=n.pagIndex,u=d*s,p=u+s,f=d>0,m=p<o.length,v=[{title:e("a",{onclick:function(){return f&&n.pagIndex--}},e("i.material-icons","chevron_left")),disabled:!f}].concat(o.filter(function(e,t){return u<=t&&t<p}),[{title:e("a",{onclick:function(){return m&&n.pagIndex++}},e("i.material-icons","chevron_right")),disabled:!m}]);return e("ul.pagination",v.map(function(n,i){return e(pe,t({title:u+i},n,{active:u+i===l}))}))}}},me=function(){return{oncreate:function(e){M.Parallax.init(e.dom,e.attrs)},view:function(t){var n=t.attrs.src;return n?e(".parallax-container",e(".parallax",e("img",{src:n}))):void 0}}},ve=["label","helperText","initialValue","newRow","className","iconName","isMandatory","onchange","disabled"],he=["label","helperText","initialValue","newRow","className","iconName","isMandatory","onchange","disabled"],ge=function(){var i={id:a()};return{view:function(a){var o=a.attrs,r=o.label,l=o.helperText,c=o.initialValue,s=o.newRow,p=o.className,f=void 0===p?"col s12":p,m=o.iconName,v=o.isMandatory,h=o.onchange,g=o.disabled,b=n(o,ve),y=i.id,w=h?function(){return i.dp&&h(i.dp.date)}:void 0,x=[s?"clear":"",f].filter(Boolean).join(" ").trim();return e(".input-field",{className:x,onremove:function(){return i.dp&&i.dp.destroy()}},[m?e("i.material-icons.prefix",m):"",e("input",t({},b,{type:"text",tabindex:0,className:"datepicker",id:y,disabled:g,oncreate:function(e){i.dp=M.Datepicker.init(e.dom,t({format:"yyyy/mm/dd",showClearBtn:!0,setDefaultDate:!0,defaultDate:c?new Date(c):new Date},b,{onClose:w}))}})),e(d,{label:r,id:y,isMandatory:v,isActive:!!c}),e(u,{helperText:l})])}}},be=function(){var i={id:a()};return{view:function(a){var o=a.attrs,r=o.label,l=o.helperText,c=o.initialValue,s=o.newRow,p=o.className,f=void 0===p?"col s12":p,m=o.iconName,v=o.isMandatory,h=o.onchange,g=o.disabled,b=n(o,he),y=i.id,w=new Date,x=h?function(){return i.tp&&h(i.tp.time||c||w.getHours()+":"+w.getMinutes())}:void 0,k=["input-field","timepicker",s?"clear":"",f].filter(Boolean).join(" ").trim();return e("div",{className:k,onremove:function(){return i.tp&&i.tp.destroy()}},[m?e("i.material-icons.prefix",m):"",e("input",t({},b,{type:"text",tabindex:0,id:y,disabled:g,value:c,oncreate:function(e){i.tp=M.Timepicker.init(e.dom,t({twelveHour:!1,showClearBtn:!0,defaultTime:c},b,{onCloseEnd:x}))}})),e(d,{label:r,id:y,isMandatory:v,isActive:c}),e(u,{helperText:l})])}}},ye=function(){return{view:function(t){var n=t.attrs,i=n.id,a=n.label,o=n.onchange,r=n.className;return e("div",{className:void 0===r?"col s12":r},e("label",[e("input[type=radio][tabindex=0]",{name:n.groupId,disabled:n.disabled,checked:n.checked,onclick:o?function(){return o(i)}:void 0}),e("span",e.trust(a))]))}}},we=function(){var n={groupId:a()};return{oninit:function(e){var t=e.attrs,i=t.checkedId,a=t.initialValue;n.oldCheckedId=i,n.checkedId=i||a},view:function(i){var a=i.attrs,o=a.id,r=a.checkedId,l=a.newRow,c=a.className,s=void 0===c?"col s12":c,u=a.label,p=void 0===u?"":u,f=a.disabled,m=a.description,v=a.options,h=a.isMandatory,g=a.checkboxClass,b=a.onchange;n.oldCheckedId!==r&&(n.oldCheckedId=n.checkedId=r);var y=n.groupId,w=n.checkedId,x=function(e){n.checkedId=e,b&&b(e)};return l&&(s+=" clear"),e("div",{id:o,className:s},[e("div",{className:"input-field options"},e(d,{id:o,label:p,isMandatory:h})),m?e("p.helper-text",e.trust(m)):""].concat(v.map(function(n){return e(ye,t({},n,{onchange:x,groupId:y,disabled:f,className:g,checked:n.id===w}))})))}}},xe=function(){var t={},n=function(e){return e.map(function(e){return e.id}).join("")},i=function(e,t,n){return void 0===n&&(n=!1),n||(t instanceof Array&&(e||"number"==typeof e)?t.indexOf(e)>=0:t===e)};return{oninit:function(e){var i=e.attrs,a=i.checkedId,o=i.initialValue;t.ids=n(i.options);var r=a||o;t.checkedId=a instanceof Array?[].concat(a):a,t.initialValue=null!=r?r instanceof Array?r.filter(function(e){return null!=e}):[r]:[]},view:function(a){var o=a.attrs,l=o.id,c=o.newRow,s=o.className,p=void 0===s?"col s12":s,f=o.checkedId,m=o.key,v=o.options,h=o.multiple,g=o.label,b=o.helperText,y=o.placeholder,w=void 0===y?"":y,x=o.isMandatory,k=o.iconName,N=o.disabled,I=o.classes,A=void 0===I?"":I,C=o.dropdownOptions,T=o.onchange;t.checkedId!==f&&(t.initialValue=f?f instanceof Array?f:[f]:void 0);var D=t.initialValue,V=T?h?function(){var e=t.instance&&t.instance.getSelectedValues(),n=e?e.length>0&&r(e[0])?e.map(function(e){return+e}):e.filter(function(e){return null!==e||void 0!==e}):void 0;t.initialValue=n||[],T(t.initialValue)}:function(e){if(e&&e.currentTarget){var n=e.currentTarget,i=r(n.value)?+n.value:n.value;t.initialValue=void 0!==typeof i?[i]:[]}t.initialValue&&T(t.initialValue)}:void 0;c&&(p+=" clear");var O=!v.some(function(e){return i(e.id,D)}),S=v.reduce(function(e,t){return t.group&&e.indexOf(t.group)<0&&e.push(t.group),e},[]);return e(".input-field.select-space",{className:p,key:m,oncreate:h?function(e){return t.wrapper=e.dom}:void 0},[k&&e("i.material-icons.prefix",k),e("select",{id:l,title:g,disabled:N,multiple:h,oncreate:function(e){t.instance=M.FormSelect.init(e.dom,{classes:A,dropdownOptions:C})},onupdate:function(e){var i=e.dom;if(h){var a=k?1:0;!t.inputEl&&t.wrapper&&t.wrapper.childNodes&&t.wrapper.childNodes.length>0&&t.wrapper.childNodes[a].childNodes&&t.wrapper.childNodes[a].childNodes[0]&&(t.inputEl=t.wrapper.childNodes[a].childNodes[0]),t.inputEl&&t.inputEl.value&&t.inputEl.value.startsWith(w+", ")&&(t.inputEl.value=t.inputEl.value.replace(w+", ",""))}var o=n(v),r=f&&t.checkedId!==f.toString();t.ids!==o&&(t.ids=o,r=!0),(t.checkedId instanceof Array&&f instanceof Array?t.checkedId.join()!==f.join():t.checkedId!==f)&&(t.checkedId=f,r=!0),r&&(t.instance=M.FormSelect.init(i,{classes:A,dropdownOptions:C}))},onchange:V},e("option",{value:"",disabled:!0,selected:!!O||void 0},w),0===S.length?v.map(function(t,n){var a;return e("option",{value:t.id,title:t.title||void 0,disabled:t.disabled?"true":void 0,"data-icon":t.img||void 0,selected:i(t.id,D,0===n&&O&&!w)},null==(a=t.label)?void 0:a.replace("&amp;","&"))}):S.map(function(t){return e("optgroup",{label:t},v.filter(function(e){return e.group===t}).map(function(t,n){var a;return e("option",{value:t.id,title:t.title||void 0,disabled:t.disabled?"true":void 0,"data-icon":t.img||void 0,selected:i(t.id,D,0===n&&O&&!w)},null==(a=t.label)?void 0:a.replace("&amp;","&"))}))})),e(d,{label:g,isMandatory:x}),b&&e(u,{helperText:b})])}}},ke=["label","left","right","disabled","newRow","onchange","checked","isMandatory","className"],Ne=function(){var t={id:a()};return{view:function(i){var a=i.attrs,o=a.id||t.id,r=a.label,l=a.left,c=a.right,s=a.disabled,u=a.newRow,p=a.onchange,f=a.checked,m=a.isMandatory,v=a.className,h=void 0===v?"col s12":v,g=n(a,ke),b=["input-field",u?"clear":"",h].filter(Boolean).join(" ").trim();return e("div",{className:b},[r?e(d,{label:r||"",id:o,isMandatory:m,className:"active"}):void 0,e(".switch",g,e("label",[l||"Off",e("input[type=checkbox]",{id:o,disabled:s,checked:f,onclick:p?function(e){e.target&&void 0!==e.target.checked&&p(e.target.checked)}:void 0}),e("span.lever"),c||"On"]))])}}},Ie=function(){var t={},n=function(e,t){return t||e.replace(/ /g,"").toLowerCase()};return{view:function(i){var a=i.attrs,o=a.tabWidth,r=a.selectedTabId,l=a.tabs,c=a.className,s=a.style,d=a.duration,u=a.onShow,p=a.swipeable,f=a.responsiveThreshold,m=l.filter(function(e){return e.active}).shift(),v=r||(m?n(m.title,m.id):""),h=["fill"===o?"tabs-fixed-width":"",c].filter(Boolean).join(" ").trim();return e(".row",[e(".col.s12",e("ul.tabs",{className:h,style:s,oncreate:function(e){t.instance=M.Tabs.init(e.dom,{duration:d,onShow:u,responsiveThreshold:f,swipeable:p})},onupdate:function(){if(v){var e=document.getElementById("tab_"+v);e&&e.click()}},onremove:function(){return t.instance.destroy()}},l.map(function(t){var i=t.className,a=t.title,r=t.id,c=t.active,s=t.disabled,d=t.target,u=t.href,p=["fixed"===o?"col s"+Math.floor(12/l.length):"",i].filter(Boolean).join(" ").trim(),f=n(a,r);return e("li.tab",{className:p,disabled:s},e("a",{id:"tab_"+f,className:c?"active":"",target:d,href:u||"#"+f},a))}))),l.filter(function(e){return void 0===e.href}).map(function(t){var i=t.vnode,a=t.contentClass;return e(".col.s12",{id:n(t.title,t.id),className:a},i)})])}}},Ae=function(){return{view:function(t){var n=t.attrs,i=n.id,a=n.title,o=n.datetime,r=n.active,l=n.content,c=n.iconName,s=n.timeFormatter,d=n.onSelect;return e("li",{id:i,className:r?"active":void 0,onclick:d?function(){return d({id:i,title:a,datetime:o,active:r,content:l})}:void 0,style:d?"cursor: pointer;":void 0},[e(".mm_time",{datetime:o},[e("span",(0,n.dateFormatter)(o)),e("span",s(o))]),c?e(".mm_icon",e("i.material-icons",c)):void 0,e(".mm_label",[a?"string"==typeof a?e("h5",a):a:void 0,l?"string"==typeof l?e("p",l):l:void 0])])}}},Ce=function(){var n=function(e){return e.getUTCDate()+"/"+(e.getUTCMonth()+1)+"/"+e.getUTCFullYear()},i=function(e){return l(e.getUTCHours())+":"+l(e.getUTCMinutes())};return{view:function(a){var o=a.attrs,r=o.onSelect,l=o.timeFormatter,c=void 0===l?i:l,s=o.dateFormatter,d=void 0===s?n:s;return e("ul.mm_timeline",o.items.map(function(n){return e(Ae,t({onSelect:r,dateFormatter:d,timeFormatter:c},n))}))}}},Me=function(){var t={isOpen:!1,selectedOptions:[],searchTerm:"",options:[],inputRef:null,dropdownRef:null,focusedIndex:-1,onchange:null},n=function(n){var i=n.target;t.inputRef&&t.inputRef.contains(i)?(t.isOpen=!t.isOpen,e.redraw()):t.dropdownRef&&!t.dropdownRef.contains(i)&&(t.isOpen=!1,e.redraw())},i=function(n){if(t.isOpen){var i=t.options.filter(function(e){return(e.label||e.id.toString()).toLowerCase().includes((t.searchTerm||"").toLowerCase())&&!t.selectedOptions.some(function(t){return t.id===e.id})});switch(n.key){case"ArrowDown":n.preventDefault(),t.focusedIndex=Math.min(t.focusedIndex+1,i.length-1),e.redraw();break;case"ArrowUp":n.preventDefault(),t.focusedIndex=Math.max(t.focusedIndex-1,-1),e.redraw();break;case"Enter":n.preventDefault(),t.focusedIndex>=0&&t.focusedIndex<i.length&&a(i[t.focusedIndex]);break;case"Escape":n.preventDefault(),t.isOpen=!1,t.focusedIndex=-1,e.redraw()}}},a=function(n){n.disabled||(t.selectedOptions=t.selectedOptions.some(function(e){return e.id===n.id})?t.selectedOptions.filter(function(e){return e.id!==n.id}):[].concat(t.selectedOptions,[n]),t.searchTerm="",t.focusedIndex=-1,t.onchange&&t.onchange(t.selectedOptions.map(function(e){return e.id})),e.redraw())};return{oninit:function(e){var n=e.attrs,i=n.options,a=void 0===i?[]:i,o=n.initialValue,r=void 0===o?[]:o,l=n.onchange;t.options=a,t.selectedOptions=a.filter(function(e){return r.includes(e.id)}),t.onchange=l},oncreate:function(){document.addEventListener("click",n),document.addEventListener("keydown",i)},onremove:function(){document.removeEventListener("click",n),document.removeEventListener("keydown",i)},view:function(n){var i=n.attrs,o=i.oncreateNewOption,r=i.className,l=i.placeholder,c=i.searchPlaceholder,s=void 0===c?"Search options...":c,d=i.noOptionsFound,u=void 0===d?"No options found":d,p=i.label,f=i.maxHeight,m=void 0===f?"25rem":f,v=t.options.filter(function(e){return(e.label||e.id.toString()).toLowerCase().includes((t.searchTerm||"").toLowerCase())&&!t.selectedOptions.some(function(t){return t.id===e.id})}),h=o&&t.searchTerm&&!v.some(function(e){return(e.label||e.id.toString()).toLowerCase()===t.searchTerm.toLowerCase()});return e(".multi-select-dropdown.input-field",{className:r},[e("label",{class:l||t.selectedOptions.length>0?"active":""},p),e(".dropdown-trigger",{oncreate:function(e){t.inputRef=e.dom},style:{borderBottom:"2px solid #d1d5db",display:"flex",justifyContent:"space-between",alignItems:"center",cursor:"pointer"}},[e(".selected-options",{style:{display:"flex",flexWrap:"wrap",minHeight:"50px",paddingTop:"12px"}},0===t.selectedOptions.length?[e("span",l)]:t.selectedOptions.map(function(n){return e(".chip",[n.label||n.id.toString(),e("button",{onclick:function(i){i.stopPropagation(),function(n){t.selectedOptions=t.selectedOptions.filter(function(e){return e.id!==n.id}),t.onchange&&t.onchange(t.selectedOptions.map(function(e){return e.id})),e.redraw()}(n)},style:{marginLeft:"0.25rem",background:"none",border:"none",cursor:"pointer"}},"×")])})),e("svg.caret",{class:"caret",height:"24",viewBox:"0 0 24 24",width:"24",xmlns:"http://www.w3.org/2000/svg"},[e("path",{d:"M7 10l5 5 5-5z"}),e("path",{d:"M0 0h24v24H0z",fill:"none"})])]),t.isOpen&&e(".dropdown-menu",{oncreate:function(e){t.dropdownRef=e.dom},onremove:function(){t.dropdownRef=null},style:{position:"absolute",width:"98%",marginTop:"0.4rem",zIndex:1e3}},[e("ul.dropdown-content.select-dropdown",{style:{maxHeight:m,opacity:1,display:"block",width:"100%"}},[e("li",{class:"search-wrapper",style:{padding:"0 16px",position:"relative"}},[e("input",{type:"text",placeholder:s,value:t.searchTerm||"",oninput:function(n){t.searchTerm=n.target.value,t.focusedIndex=-1,e.redraw()},style:{width:"100%",outline:"none",fontSize:"0.875rem"}})])].concat(0!==v.length||h?[]:[e("li",{style:{padding:"0.5rem",textAlign:"center",color:"#9ca3af"}},u)],h?[e("li",{onclick:function(){try{return Promise.resolve(o(t.searchTerm)).then(function(e){a(e)})}catch(e){return Promise.reject(e)}},style:{display:"flex",alignItems:"center",cursor:"pointer",background:t.focusedIndex===v.length?"#f3f4f6":""}},[e("span",'+ "'+t.searchTerm+'"')])]:[],v.map(function(n,i){return e("li",{onclick:function(){return a(n)},class:n.disabled?"disabled":void 0,style:{display:"flex",alignItems:"center",cursor:n.disabled?"not-allowed":"pointer",background:t.focusedIndex===i?"#f3f4f6":""}},e("span",[e("input",{type:"checkbox",checked:t.selectedOptions.some(function(e){return e.id===n.id}),style:{marginRight:"0.5rem"}}),n.label||n.id.toString()]))})))])])}}};export{z as AnchorItem,f as Autocomplete,b as Button,g as ButtonFactory,C as Carousel,A as CarouselItem,T as Chips,V as CodeBlock,S as Collapsible,O as CollapsibleItem,U as Collection,i as CollectionMode,ie as ColorInput,ge as DatePicker,W as Dropdown,oe as EmailInput,re as FileInput,x as FlatButton,$ as FloatingActionButton,u as HelperText,v as Icon,le as InputCheckbox,d as Label,y as LargeButton,P as ListItem,s as Mandatory,se as MapEditor,de as MaterialBox,ue as ModalPanel,te as NumberInput,ce as Options,fe as Pagination,me as Parallax,ee as PasswordInput,ye as RadioButton,we as RadioButtons,ae as RangeInput,k as RoundIconButton,Me as SearchSelect,F as SecondaryContent,xe as Select,w as SmallButton,N as SubmitButton,Ne as Switch,Ie as Tabs,J as TextArea,Z as TextInput,be as TimePicker,Ce as Timeline,ne as UrlInput,r as isNumeric,l as padLeft,a as uniqueId,o as uuid4};
2
- //# sourceMappingURL=index.esm.js.map
1
+ import m from 'mithril';
2
+
3
+ /******************************************************************************
4
+ Copyright (c) Microsoft Corporation.
5
+
6
+ Permission to use, copy, modify, and/or distribute this software for any
7
+ purpose with or without fee is hereby granted.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
16
+ ***************************************************************************** */
17
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
18
+
19
+
20
+ function __rest(s, e) {
21
+ var t = {};
22
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
23
+ t[p] = s[p];
24
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
25
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
26
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
27
+ t[p[i]] = s[p[i]];
28
+ }
29
+ return t;
30
+ }
31
+
32
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
33
+ var e = new Error(message);
34
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
35
+ };
36
+
37
+ /**
38
+ * Create a unique ID
39
+ * @see https://stackoverflow.com/a/2117523/319711
40
+ *
41
+ * @returns id followed by 8 hexadecimal characters.
42
+ */
43
+ const uniqueId = () => {
44
+ // tslint:disable-next-line:no-bitwise
45
+ return 'idxxxxxxxx'.replace(/[x]/g, () => ((Math.random() * 16) | 0).toString(16));
46
+ };
47
+ /**
48
+ * Create a GUID
49
+ * @see https://stackoverflow.com/a/2117523/319711
50
+ *
51
+ * @returns RFC4122 version 4 compliant GUID
52
+ */
53
+ const uuid4 = () => {
54
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
55
+ // tslint:disable-next-line:no-bitwise
56
+ const r = (Math.random() * 16) | 0;
57
+ // tslint:disable-next-line:no-bitwise
58
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
59
+ return v.toString(16);
60
+ });
61
+ };
62
+ /** Check if a string or number is numeric. @see https://stackoverflow.com/a/9716488/319711 */
63
+ const isNumeric = (n) => !isNaN(parseFloat(n)) && isFinite(n);
64
+ /**
65
+ * Pad left, default width 2 with a '0'
66
+ *
67
+ * @see http://stackoverflow.com/a/10073788/319711
68
+ * @param {(string | number)} n
69
+ * @param {number} [width=2]
70
+ * @param {string} [z='0']
71
+ * @returns
72
+ */
73
+ const padLeft = (n, width = 2, z = '0') => String(n).padStart(width, z);
74
+ // Keep only essential dropdown positioning styles
75
+ const getDropdownStyles = (inputRef, overlap = false, options, isDropDown = false) => {
76
+ if (!inputRef) {
77
+ return {
78
+ display: 'block',
79
+ opacity: 1,
80
+ position: 'absolute',
81
+ top: overlap ? 0 : '100%',
82
+ left: '0',
83
+ zIndex: 1000,
84
+ width: '100%',
85
+ };
86
+ }
87
+ const rect = inputRef.getBoundingClientRect();
88
+ const viewportHeight = window.innerHeight;
89
+ // Calculate dropdown height based on options
90
+ let estimatedHeight = 200; // Default fallback
91
+ const itemHeight = 52; // Standard height for dropdown items
92
+ if (options) {
93
+ const groupHeaderHeight = 52; // Height for group headers
94
+ // Count groups and total options
95
+ const groups = new Set();
96
+ let totalOptions = 0;
97
+ options
98
+ .filter((o) => !o.divider)
99
+ .forEach((option) => {
100
+ totalOptions++;
101
+ if (option.group) {
102
+ groups.add(option.group);
103
+ }
104
+ });
105
+ // Calculate total height: options + group headers + padding
106
+ estimatedHeight = totalOptions * itemHeight + groups.size * groupHeaderHeight;
107
+ }
108
+ const spaceBelow = viewportHeight - rect.bottom;
109
+ const spaceAbove = rect.top;
110
+ // If there's not enough space below and more space above, position dropdown above
111
+ const shouldPositionAbove = spaceBelow < estimatedHeight && spaceAbove > spaceBelow;
112
+ // Calculate available space and whether scrolling is needed
113
+ const availableSpace = shouldPositionAbove ? spaceAbove : spaceBelow;
114
+ // When positioning above, we need to consider the actual space from viewport top to input
115
+ let effectiveAvailableSpace = availableSpace;
116
+ if (shouldPositionAbove) {
117
+ effectiveAvailableSpace = rect.top - 10; // Space from viewport top to input, minus margin
118
+ }
119
+ const needsScrolling = estimatedHeight > effectiveAvailableSpace;
120
+ // Calculate the actual height the dropdown will take
121
+ const actualHeight = needsScrolling ? effectiveAvailableSpace : estimatedHeight;
122
+ // Calculate positioning when dropdown should appear above
123
+ let topOffset;
124
+ if (shouldPositionAbove) {
125
+ // Calculate how much space we actually have from top of viewport to top of input
126
+ const availableSpaceFromViewportTop = rect.top;
127
+ // If dropdown fits comfortably above input, use normal positioning
128
+ if (actualHeight <= availableSpaceFromViewportTop) {
129
+ topOffset = 12 - actualHeight + (isDropDown ? itemHeight : 0); // Bottom of dropdown aligns with top of input
130
+ }
131
+ else {
132
+ // If dropdown is too tall, position it at the very top of viewport
133
+ // This makes the dropdown use all available space from viewport top to input top
134
+ topOffset = -availableSpaceFromViewportTop + 5; // 5px margin from viewport top
135
+ }
136
+ }
137
+ else {
138
+ topOffset = overlap ? 0 : '100%';
139
+ }
140
+ const styles = {
141
+ display: 'block',
142
+ opacity: 1,
143
+ position: 'absolute',
144
+ top: typeof topOffset === 'number' ? `${topOffset}px` : topOffset,
145
+ left: '0',
146
+ zIndex: 1000,
147
+ width: `${rect.width}px`,
148
+ };
149
+ // Only add scrolling constraints when necessary
150
+ if (needsScrolling) {
151
+ styles.maxHeight = `${actualHeight}px`;
152
+ styles.overflowY = 'auto';
153
+ }
154
+ return styles;
155
+ };
156
+ /**
157
+ * Generate a range of numbers from a to and including b, i.e. [a, b]
158
+ * @example: console.log(range(5, 10)); // [5, 6, 7, 8, 9, 10]
159
+ */
160
+ const range = (a, b) => Array.from({ length: b - a + 1 }, (_, i) => a + i);
161
+
162
+ // import './styles/input.css';
163
+ const Mandatory = { view: ({ attrs }) => m('span.mandatory', attrs, '*') };
164
+ /** Simple label element, used for most components. */
165
+ const Label = () => {
166
+ return {
167
+ view: (_a) => {
168
+ var _b = _a.attrs, { label, id, isMandatory, isActive, className } = _b, params = __rest(_b, ["label", "id", "isMandatory", "isActive", "className"]);
169
+ return label
170
+ ? m('label', Object.assign(Object.assign({}, params), { className: [className, isActive ? 'active' : ''].filter(Boolean).join(' ').trim() || undefined, for: id }), [m.trust(label), isMandatory ? m(Mandatory) : undefined])
171
+ : undefined;
172
+ },
173
+ };
174
+ };
175
+ /** Create a helper text, often used for displaying a small help text. May be replaced by the validation message. */
176
+ const HelperText = () => {
177
+ return {
178
+ view: ({ attrs: { helperText, dataError, dataSuccess, className } }) => {
179
+ return helperText || dataError || dataSuccess
180
+ ? m('span.helper-text.left', { className, 'data-error': dataError, 'data-success': dataSuccess }, dataError ? m.trust(dataError) : dataSuccess ? m.trust(dataSuccess) : helperText ? m.trust(helperText) : '')
181
+ : undefined;
182
+ },
183
+ };
184
+ };
185
+
186
+ /** Component to auto complete your text input - Pure Mithril implementation */
187
+ const Autocomplete = () => {
188
+ const state = {
189
+ id: uniqueId(),
190
+ isActive: false,
191
+ inputValue: '',
192
+ isOpen: false,
193
+ suggestions: [],
194
+ selectedIndex: -1,
195
+ inputElement: null,
196
+ };
197
+ const filterSuggestions = (input, data, limit, minLength) => {
198
+ if (!input || input.length < minLength) {
199
+ return [];
200
+ }
201
+ const filtered = Object.entries(data || {})
202
+ .filter(([key]) => key.toLowerCase().includes(input.toLowerCase()))
203
+ .map(([key, value]) => ({ key, value }))
204
+ .slice(0, limit);
205
+ return filtered;
206
+ };
207
+ const selectSuggestion = (suggestion, attrs) => {
208
+ state.inputValue = suggestion.key;
209
+ state.isOpen = false;
210
+ state.selectedIndex = -1;
211
+ if (attrs.onchange) {
212
+ attrs.onchange(suggestion.key);
213
+ }
214
+ if (attrs.onAutocomplete) {
215
+ attrs.onAutocomplete(suggestion.key);
216
+ }
217
+ // Force redraw to update label state
218
+ m.redraw();
219
+ };
220
+ const handleKeydown = (e, attrs) => {
221
+ if (!state.isOpen)
222
+ return;
223
+ switch (e.key) {
224
+ case 'ArrowDown':
225
+ e.preventDefault();
226
+ state.selectedIndex = Math.min(state.selectedIndex + 1, state.suggestions.length - 1);
227
+ break;
228
+ case 'ArrowUp':
229
+ e.preventDefault();
230
+ state.selectedIndex = Math.max(state.selectedIndex - 1, -1);
231
+ break;
232
+ case 'Enter':
233
+ e.preventDefault();
234
+ if (state.selectedIndex >= 0 && state.suggestions[state.selectedIndex]) {
235
+ state.isOpen = false;
236
+ selectSuggestion(state.suggestions[state.selectedIndex], attrs);
237
+ }
238
+ break;
239
+ case 'Escape':
240
+ e.preventDefault();
241
+ state.isOpen = false;
242
+ state.selectedIndex = -1;
243
+ break;
244
+ }
245
+ };
246
+ const closeDropdown = (e) => {
247
+ const target = e.target;
248
+ const autocompleteWrapper = target.closest('.autocomplete-wrapper');
249
+ const dropdownContent = target.closest('.autocomplete-content');
250
+ // Close if clicking outside both the input wrapper and dropdown content
251
+ if (!autocompleteWrapper && !dropdownContent) {
252
+ state.isOpen = false;
253
+ state.selectedIndex = -1;
254
+ }
255
+ };
256
+ const getDropdownStyles = () => {
257
+ if (!state.inputElement) {
258
+ return {
259
+ display: 'block',
260
+ width: '100%',
261
+ height: `${state.suggestions.length * 50}px`,
262
+ transformOrigin: '0px 0px',
263
+ opacity: state.isOpen ? 1 : 0,
264
+ transform: 'scaleX(1) scaleY(1)',
265
+ };
266
+ }
267
+ const rect = state.inputElement.getBoundingClientRect();
268
+ const inputWidth = rect.width;
269
+ return {
270
+ display: 'block',
271
+ width: `${inputWidth}px`,
272
+ height: `${state.suggestions.length * 50}px`,
273
+ transformOrigin: '0px 0px',
274
+ opacity: state.isOpen ? 1 : 0,
275
+ transform: 'scaleX(1) scaleY(1)',
276
+ position: 'absolute',
277
+ top: '100%',
278
+ left: '0',
279
+ zIndex: 1000,
280
+ };
281
+ };
282
+ return {
283
+ oninit: ({ attrs }) => {
284
+ state.inputValue = attrs.initialValue || '';
285
+ document.addEventListener('click', closeDropdown);
286
+ },
287
+ onremove: () => {
288
+ document.removeEventListener('click', closeDropdown);
289
+ },
290
+ view: ({ attrs }) => {
291
+ const id = attrs.id || state.id;
292
+ const { label, helperText, onchange, newRow, className = 'col s12', style, iconName, isMandatory, data = {}, limit = Infinity, minLength = 1 } = attrs, params = __rest(attrs, ["label", "helperText", "onchange", "newRow", "className", "style", "iconName", "isMandatory", "data", "limit", "minLength"]);
293
+ const cn = newRow ? className + ' clear' : className;
294
+ // Update suggestions when input changes
295
+ state.suggestions = filterSuggestions(state.inputValue, data, limit, minLength);
296
+ // Check if there's a perfect match (exact key match, case-insensitive)
297
+ const hasExactMatch = state.inputValue.length >= minLength &&
298
+ Object.keys(data).some((key) => key.toLowerCase() === state.inputValue.toLowerCase());
299
+ // Only open dropdown if there are suggestions and no perfect match
300
+ state.isOpen = state.suggestions.length > 0 && state.inputValue.length >= minLength && !hasExactMatch;
301
+ const replacer = new RegExp(`(${state.inputValue})`, 'i');
302
+ return m('.input-field.autocomplete-wrapper', {
303
+ className: cn,
304
+ style,
305
+ }, [
306
+ iconName ? m('i.material-icons.prefix', iconName) : '',
307
+ m('input', Object.assign(Object.assign({}, params), { className: 'autocomplete', type: 'text', tabindex: 0, id, value: state.inputValue, oncreate: (vnode) => {
308
+ state.inputElement = vnode.dom;
309
+ }, oninput: (e) => {
310
+ const target = e.target;
311
+ state.inputValue = target.value;
312
+ state.selectedIndex = -1;
313
+ if (onchange) {
314
+ onchange(target.value);
315
+ }
316
+ }, onkeydown: (e) => {
317
+ handleKeydown(e, attrs);
318
+ // Call original onkeydown if provided
319
+ if (attrs.onkeydown) {
320
+ attrs.onkeydown(e, state.inputValue);
321
+ }
322
+ }, onfocus: () => {
323
+ state.isActive = true;
324
+ if (state.inputValue.length >= minLength) {
325
+ // Check for perfect match on focus too
326
+ const hasExactMatch = Object.keys(data).some((key) => key.toLowerCase() === state.inputValue.toLowerCase());
327
+ state.isOpen = state.suggestions.length > 0 && !hasExactMatch;
328
+ }
329
+ }, onblur: (e) => {
330
+ state.isActive = false;
331
+ // Delay closing to allow clicks on suggestions
332
+ setTimeout(() => {
333
+ if (!e.relatedTarget || !e.relatedTarget.closest('.autocomplete-content')) {
334
+ state.isOpen = false;
335
+ state.selectedIndex = -1;
336
+ m.redraw();
337
+ }
338
+ }, 150);
339
+ } })),
340
+ // Autocomplete dropdown
341
+ state.isOpen &&
342
+ m('ul.autocomplete-content.dropdown-content', {
343
+ style: getDropdownStyles(),
344
+ }, state.suggestions.map((suggestion, index) => m('li', {
345
+ key: suggestion.key,
346
+ class: state.selectedIndex === index ? 'active' : '',
347
+ onclick: (e) => {
348
+ e.preventDefault();
349
+ e.stopPropagation();
350
+ selectSuggestion(suggestion, attrs);
351
+ },
352
+ onmouseover: () => {
353
+ state.selectedIndex = index;
354
+ m.redraw();
355
+ },
356
+ }, [
357
+ // Check if value contains image URL or icon
358
+ suggestion.value && suggestion.value.includes('http')
359
+ ? m('img', {
360
+ src: suggestion.value,
361
+ class: 'right circle',
362
+ onerror: (e) => {
363
+ // Hide image if it fails to load
364
+ e.target.style.display = 'none';
365
+ },
366
+ })
367
+ : suggestion.value && suggestion.value.startsWith('icon:')
368
+ ? m('i.material-icons', {
369
+ style: {
370
+ fontSize: '24px',
371
+ color: 'var(--md-grey-600)',
372
+ },
373
+ }, suggestion.value.replace('icon:', ''))
374
+ : null,
375
+ m('span', suggestion.key
376
+ ? m.trust(suggestion.key.replace(replacer, (i) => `<span class="highlight">${i}</span>`))
377
+ : ''),
378
+ ]))),
379
+ m(Label, {
380
+ label,
381
+ id,
382
+ isMandatory,
383
+ isActive: state.isActive || state.inputValue.length > 0 || !!attrs.placeholder || !!attrs.initialValue,
384
+ }),
385
+ m(HelperText, { helperText }),
386
+ ]);
387
+ },
388
+ };
389
+ };
390
+
391
+ /**
392
+ * A simple material icon, defined by its icon name.
393
+ *
394
+ * @example m(Icon, { className: 'small' }, 'create') renders a small 'create' icon
395
+ * @example m(Icon, { className: 'prefix' }, iconName) renders the icon as a prefix
396
+ */
397
+ const Icon = () => ({
398
+ view: (_a) => {
399
+ var _b = _a.attrs, { iconName } = _b, passThrough = __rest(_b, ["iconName"]);
400
+ return m('i.material-icons', passThrough, iconName);
401
+ },
402
+ });
403
+
404
+ /**
405
+ * A factory to create new buttons.
406
+ *
407
+ * @example FlatButton = ButtonFactory('a.waves-effect.waves-teal.btn-flat');
408
+ */
409
+ const ButtonFactory = (element, defaultClassNames, type = '') => {
410
+ return () => {
411
+ return {
412
+ view: ({ attrs }) => {
413
+ const { modalId, tooltip, tooltipPostion, iconName, iconClass, label, className, attr } = attrs, params = __rest(attrs, ["modalId", "tooltip", "tooltipPostion", "iconName", "iconClass", "label", "className", "attr"]);
414
+ const cn = [modalId ? 'modal-trigger' : '', tooltip ? 'tooltipped' : '', defaultClassNames, className]
415
+ .filter(Boolean)
416
+ .join(' ')
417
+ .trim();
418
+ return m(element, Object.assign(Object.assign(Object.assign({}, params), attr), { className: cn, href: modalId ? `#${modalId}` : undefined, 'data-position': tooltip ? tooltipPostion || 'top' : undefined, 'data-tooltip': tooltip || undefined, type }),
419
+ // `${dca}${modalId ? `.modal-trigger[href=#${modalId}]` : ''}${
420
+ // tooltip ? `.tooltipped[data-position=${tooltipPostion || 'top'}][data-tooltip=${tooltip}]` : ''
421
+ // }${toAttributeString(attr)}`, {}
422
+ iconName ? m(Icon, { iconName, className: iconClass || 'left' }) : undefined, label ? label : undefined);
423
+ },
424
+ };
425
+ };
426
+ };
427
+ const Button = ButtonFactory('a', 'waves-effect waves-light btn', 'button');
428
+ const LargeButton = ButtonFactory('a', 'waves-effect waves-light btn-large', 'button');
429
+ const SmallButton = ButtonFactory('a', 'waves-effect waves-light btn-small', 'button');
430
+ const FlatButton = ButtonFactory('a', 'waves-effect waves-teal btn-flat', 'button');
431
+ const RoundIconButton = ButtonFactory('button', 'btn-floating btn-large waves-effect waves-light', 'button');
432
+ const SubmitButton = ButtonFactory('button', 'btn waves-effect waves-light', 'submit');
433
+
434
+ /**
435
+ * Materialize CSS Carousel component with dynamic positioning
436
+ * Port of the original MaterializeCSS carousel logic
437
+ */
438
+ const Carousel = () => {
439
+ // Default options based on original Materialize CSS
440
+ const defaults = {
441
+ duration: 200, // ms
442
+ dist: -100, // zoom scale
443
+ shift: 0, // spacing for center image
444
+ padding: 0, // Padding between non center items
445
+ numVisible: 5, // Number of visible items in carousel
446
+ fullWidth: false, // Change to full width styles
447
+ indicators: false, // Toggle indicators
448
+ noWrap: false, // Don't wrap around and cycle through items
449
+ };
450
+ const state = {
451
+ // Carousel state
452
+ hasMultipleSlides: false,
453
+ showIndicators: false,
454
+ noWrap: false,
455
+ pressed: false,
456
+ dragged: false,
457
+ verticalDragged: false,
458
+ offset: 0,
459
+ target: 0,
460
+ center: 0,
461
+ // Touch/drag state
462
+ reference: 0,
463
+ referenceY: 0,
464
+ velocity: 0,
465
+ amplitude: 0,
466
+ frame: 0,
467
+ timestamp: 0,
468
+ // Item measurements
469
+ itemWidth: 0,
470
+ itemHeight: 0,
471
+ dim: 1, // Make sure dim is non zero for divisions
472
+ // Animation
473
+ ticker: null,
474
+ scrollingTimeout: null};
475
+ // Utility functions
476
+ const xpos = (e) => {
477
+ // Touch event
478
+ if ('targetTouches' in e && e.targetTouches && e.targetTouches.length >= 1) {
479
+ return e.targetTouches[0].clientX;
480
+ }
481
+ // Mouse event
482
+ return e.clientX;
483
+ };
484
+ const ypos = (e) => {
485
+ // Touch event
486
+ if ('targetTouches' in e && e.targetTouches && e.targetTouches.length >= 1) {
487
+ return e.targetTouches[0].clientY;
488
+ }
489
+ // Mouse event
490
+ return e.clientY;
491
+ };
492
+ const wrap = (x, count) => {
493
+ return x >= count ? x % count : x < 0 ? wrap(count + (x % count), count) : x;
494
+ };
495
+ const track = () => {
496
+ const now = Date.now();
497
+ const elapsed = now - state.timestamp;
498
+ state.timestamp = now;
499
+ const delta = state.offset - state.frame;
500
+ state.frame = state.offset;
501
+ const v = (1000 * delta) / (1 + elapsed);
502
+ state.velocity = 0.8 * v + 0.2 * state.velocity;
503
+ };
504
+ const autoScroll = () => {
505
+ if (state.amplitude) {
506
+ const elapsed = Date.now() - state.timestamp;
507
+ const delta = state.amplitude * Math.exp(-elapsed / defaults.duration);
508
+ if (delta > 2 || delta < -2) {
509
+ scroll(state.target - delta);
510
+ requestAnimationFrame(autoScroll);
511
+ }
512
+ else {
513
+ scroll(state.target);
514
+ }
515
+ }
516
+ };
517
+ const updateItemStyle = (el, opacity, zIndex, transform) => {
518
+ el.style.transform = transform;
519
+ el.style.zIndex = zIndex.toString();
520
+ el.style.opacity = opacity.toString();
521
+ el.style.visibility = 'visible';
522
+ };
523
+ const scroll = (x, attrs) => {
524
+ const carouselEl = document.querySelector('.carousel');
525
+ if (!carouselEl)
526
+ return;
527
+ // Track scrolling state
528
+ if (!carouselEl.classList.contains('scrolling')) {
529
+ carouselEl.classList.add('scrolling');
530
+ }
531
+ if (state.scrollingTimeout != null) {
532
+ window.clearTimeout(state.scrollingTimeout);
533
+ }
534
+ state.scrollingTimeout = window.setTimeout(() => {
535
+ carouselEl.classList.remove('scrolling');
536
+ }, defaults.duration);
537
+ // Start actual scroll
538
+ const items = Array.from(carouselEl.querySelectorAll('.carousel-item'));
539
+ const count = items.length;
540
+ if (count === 0)
541
+ return;
542
+ const lastCenter = state.center;
543
+ const numVisibleOffset = 1 / defaults.numVisible;
544
+ state.offset = typeof x === 'number' ? x : state.offset;
545
+ state.center = Math.floor((state.offset + state.dim / 2) / state.dim);
546
+ const delta = state.offset - state.center * state.dim;
547
+ const dir = delta < 0 ? 1 : -1;
548
+ const tween = (-dir * delta * 2) / state.dim;
549
+ const half = count >> 1;
550
+ let alignment;
551
+ let centerTweenedOpacity;
552
+ if (defaults.fullWidth) {
553
+ alignment = 'translateX(0)';
554
+ centerTweenedOpacity = 1;
555
+ }
556
+ else {
557
+ alignment = `translateX(${(carouselEl.clientWidth - state.itemWidth) / 2}px) `;
558
+ alignment += `translateY(${(carouselEl.clientHeight - state.itemHeight) / 2}px)`;
559
+ centerTweenedOpacity = 1 - numVisibleOffset * tween;
560
+ }
561
+ // Set indicator active
562
+ if (state.showIndicators) {
563
+ const diff = state.center % count;
564
+ const indicators = carouselEl.querySelectorAll('.indicator-item');
565
+ indicators.forEach((indicator, index) => {
566
+ indicator.classList.toggle('active', index === diff);
567
+ });
568
+ }
569
+ // Center item
570
+ if (!state.noWrap || (state.center >= 0 && state.center < count)) {
571
+ const el = items[wrap(state.center, count)];
572
+ // Add active class to center item
573
+ items.forEach((item) => item.classList.remove('active'));
574
+ el.classList.add('active');
575
+ const transformString = `${alignment} translateX(${-delta / 2}px) translateX(${dir * defaults.shift * tween}px) translateZ(${defaults.dist * tween}px)`;
576
+ updateItemStyle(el, centerTweenedOpacity, 0, transformString);
577
+ }
578
+ // Side items
579
+ for (let i = 1; i <= half; ++i) {
580
+ let zTranslation;
581
+ let tweenedOpacity;
582
+ // Right side
583
+ if (defaults.fullWidth) {
584
+ zTranslation = defaults.dist;
585
+ tweenedOpacity = i === half && delta < 0 ? 1 - tween : 1;
586
+ }
587
+ else {
588
+ zTranslation = defaults.dist * (i * 2 + tween * dir);
589
+ tweenedOpacity = 1 - numVisibleOffset * (i * 2 + tween * dir);
590
+ }
591
+ if (!state.noWrap || state.center + i < count) {
592
+ const el = items[wrap(state.center + i, count)];
593
+ const transformString = `${alignment} translateX(${defaults.shift + (state.dim * i - delta) / 2}px) translateZ(${zTranslation}px)`;
594
+ updateItemStyle(el, tweenedOpacity, -i, transformString);
595
+ }
596
+ // Left side
597
+ if (defaults.fullWidth) {
598
+ zTranslation = defaults.dist;
599
+ tweenedOpacity = i === half && delta > 0 ? 1 - tween : 1;
600
+ }
601
+ else {
602
+ zTranslation = defaults.dist * (i * 2 - tween * dir);
603
+ tweenedOpacity = 1 - numVisibleOffset * (i * 2 - tween * dir);
604
+ }
605
+ if (!state.noWrap || state.center - i >= 0) {
606
+ const el = items[wrap(state.center - i, count)];
607
+ const transformString = `${alignment} translateX(${-defaults.shift + (-state.dim * i - delta) / 2}px) translateZ(${zTranslation}px)`;
608
+ updateItemStyle(el, tweenedOpacity, -i, transformString);
609
+ }
610
+ }
611
+ // onCycleTo callback
612
+ if (lastCenter !== state.center && attrs && attrs.onCycleTo) {
613
+ const currItem = items[wrap(state.center, count)];
614
+ if (currItem) {
615
+ const itemIndex = Array.from(items).indexOf(currItem);
616
+ attrs.onCycleTo(attrs.items[itemIndex], itemIndex, state.dragged);
617
+ }
618
+ }
619
+ };
620
+ const cycleTo = (n, callback, _attrs) => {
621
+ const items = document.querySelectorAll('.carousel-item');
622
+ const count = items.length;
623
+ if (count === 0)
624
+ return;
625
+ let diff = (state.center % count) - n;
626
+ // Account for wraparound
627
+ if (!state.noWrap) {
628
+ if (diff < 0) {
629
+ if (Math.abs(diff + count) < Math.abs(diff)) {
630
+ diff += count;
631
+ }
632
+ }
633
+ else if (diff > 0) {
634
+ if (Math.abs(diff - count) < diff) {
635
+ diff -= count;
636
+ }
637
+ }
638
+ }
639
+ state.target = state.dim * Math.round(state.offset / state.dim);
640
+ if (diff < 0) {
641
+ state.target += state.dim * Math.abs(diff);
642
+ }
643
+ else if (diff > 0) {
644
+ state.target -= state.dim * diff;
645
+ }
646
+ // Scroll
647
+ if (state.offset !== state.target) {
648
+ state.amplitude = state.target - state.offset;
649
+ state.timestamp = Date.now();
650
+ requestAnimationFrame(autoScroll);
651
+ }
652
+ };
653
+ // Event handlers
654
+ const handleCarouselTap = (e) => {
655
+ // Fixes firefox draggable image bug
656
+ if (e.type === 'mousedown' && e.target.tagName === 'IMG') {
657
+ e.preventDefault();
658
+ }
659
+ state.pressed = true;
660
+ state.dragged = false;
661
+ state.verticalDragged = false;
662
+ state.reference = xpos(e);
663
+ state.referenceY = ypos(e);
664
+ state.velocity = state.amplitude = 0;
665
+ state.frame = state.offset;
666
+ state.timestamp = Date.now();
667
+ if (state.ticker)
668
+ clearInterval(state.ticker);
669
+ state.ticker = setInterval(track, 100);
670
+ };
671
+ const handleCarouselDrag = (e, attrs) => {
672
+ if (state.pressed) {
673
+ const x = xpos(e);
674
+ const y = ypos(e);
675
+ const delta = state.reference - x;
676
+ const deltaY = Math.abs(state.referenceY - y);
677
+ if (deltaY < 30 && !state.verticalDragged) {
678
+ if (delta > 2 || delta < -2) {
679
+ state.dragged = true;
680
+ state.reference = x;
681
+ scroll(state.offset + delta, attrs);
682
+ }
683
+ }
684
+ else if (state.dragged) {
685
+ e.preventDefault();
686
+ e.stopPropagation();
687
+ return false;
688
+ }
689
+ else {
690
+ state.verticalDragged = true;
691
+ }
692
+ }
693
+ if (state.dragged) {
694
+ e.preventDefault();
695
+ e.stopPropagation();
696
+ return false;
697
+ }
698
+ return true;
699
+ };
700
+ const handleCarouselRelease = (e, _attrs) => {
701
+ if (state.pressed) {
702
+ state.pressed = false;
703
+ }
704
+ else {
705
+ return;
706
+ }
707
+ if (state.ticker)
708
+ clearInterval(state.ticker);
709
+ state.target = state.offset;
710
+ if (state.velocity > 10 || state.velocity < -10) {
711
+ state.amplitude = 0.9 * state.velocity;
712
+ state.target = state.offset + state.amplitude;
713
+ }
714
+ state.target = Math.round(state.target / state.dim) * state.dim;
715
+ // No wrap of items
716
+ if (state.noWrap) {
717
+ const items = document.querySelectorAll('.carousel-item');
718
+ if (state.target >= state.dim * (items.length - 1)) {
719
+ state.target = state.dim * (items.length - 1);
720
+ }
721
+ else if (state.target < 0) {
722
+ state.target = 0;
723
+ }
724
+ }
725
+ state.amplitude = state.target - state.offset;
726
+ state.timestamp = Date.now();
727
+ requestAnimationFrame(autoScroll);
728
+ if (state.dragged) {
729
+ e.preventDefault();
730
+ e.stopPropagation();
731
+ }
732
+ return false;
733
+ };
734
+ const handleCarouselClick = (e, attrs) => {
735
+ if (state.dragged) {
736
+ e.preventDefault();
737
+ e.stopPropagation();
738
+ return false;
739
+ }
740
+ else if (!defaults.fullWidth) {
741
+ const target = e.target.closest('.carousel-item');
742
+ if (target) {
743
+ const items = Array.from(document.querySelectorAll('.carousel-item'));
744
+ const clickedIndex = items.indexOf(target);
745
+ const diff = wrap(state.center, items.length) - clickedIndex;
746
+ if (diff !== 0) {
747
+ e.preventDefault();
748
+ e.stopPropagation();
749
+ }
750
+ cycleTo(clickedIndex);
751
+ }
752
+ }
753
+ return true;
754
+ };
755
+ const handleIndicatorClick = (e, attrs) => {
756
+ e.stopPropagation();
757
+ const indicator = e.target.closest('.indicator-item');
758
+ if (indicator) {
759
+ const indicators = Array.from(document.querySelectorAll('.indicator-item'));
760
+ const index = indicators.indexOf(indicator);
761
+ cycleTo(index);
762
+ }
763
+ };
764
+ return {
765
+ view: ({ attrs }) => {
766
+ const { items, indicators = false } = attrs;
767
+ if (!items || items.length === 0)
768
+ return undefined;
769
+ // Merge options
770
+ Object.assign(defaults, attrs);
771
+ const supportTouch = typeof window.ontouchstart !== 'undefined';
772
+ return m('.carousel', {
773
+ oncreate: ({ attrs, dom }) => {
774
+ const carouselEl = dom;
775
+ const items = carouselEl.querySelectorAll('.carousel-item');
776
+ state.hasMultipleSlides = items.length > 1;
777
+ state.showIndicators = defaults.indicators && state.hasMultipleSlides;
778
+ state.noWrap = defaults.noWrap || !state.hasMultipleSlides;
779
+ if (items.length > 0) {
780
+ const firstItem = items[0];
781
+ state.itemWidth = firstItem.offsetWidth;
782
+ state.itemHeight = firstItem.offsetHeight;
783
+ state.dim = state.itemWidth * 2 + defaults.padding || 1;
784
+ }
785
+ // Cap numVisible at count
786
+ defaults.numVisible = Math.min(items.length, defaults.numVisible);
787
+ // Initial scroll
788
+ scroll(state.offset, attrs);
789
+ },
790
+ onmousedown: (e) => handleCarouselTap(e),
791
+ onmousemove: (e) => handleCarouselDrag(e, attrs),
792
+ onmouseup: (e) => handleCarouselRelease(e),
793
+ onmouseleave: (e) => handleCarouselRelease(e),
794
+ onclick: (e) => handleCarouselClick(e),
795
+ ontouchstart: supportTouch ? (e) => handleCarouselTap(e) : undefined,
796
+ ontouchmove: supportTouch ? (e) => handleCarouselDrag(e, attrs) : undefined,
797
+ ontouchend: supportTouch ? (e) => handleCarouselRelease(e) : undefined,
798
+ }, [
799
+ // Carousel items
800
+ ...items.map((item) => m('a.carousel-item', {
801
+ // key: index,
802
+ href: item.href,
803
+ style: 'visibility: hidden;', // Initially hidden, will be shown by scroll
804
+ }, m('img', { src: item.src, alt: item.alt }))),
805
+ // Indicators
806
+ indicators &&
807
+ items.length > 1 &&
808
+ m('ul.indicators', items.map((_, index) => m('li.indicator-item', {
809
+ key: `indicator-${index}`,
810
+ className: index === 0 ? 'active' : '',
811
+ onclick: (e) => handleIndicatorClick(e),
812
+ }))),
813
+ ]);
814
+ },
815
+ };
816
+ };
817
+
818
+ const iconPaths = {
819
+ caret: [
820
+ 'M7 10l5 5 5-5z', // arrow
821
+ 'M0 0h24v24H0z', // background
822
+ ],
823
+ close: [
824
+ 'M18.3 5.71a1 1 0 0 0-1.41 0L12 10.59 7.11 5.7A1 1 0 0 0 5.7 7.11L10.59 12l-4.89 4.89a1 1 0 1 0 1.41 1.41L12 13.41l4.89 4.89a1 1 0 0 0 1.41-1.41L13.41 12l4.89-4.89a1 1 0 0 0 0-1.4z',
825
+ 'M0 0h24v24H0z',
826
+ ],
827
+ };
828
+ const MaterialIcon = () => {
829
+ return {
830
+ view: ({ attrs }) => {
831
+ var _a;
832
+ const { name, direction = 'down', style } = attrs, props = __rest(attrs, ["name", "direction", "style"]);
833
+ const rotationMap = {
834
+ down: 0,
835
+ up: 180,
836
+ left: -90,
837
+ right: 90,
838
+ };
839
+ const rotation = (_a = rotationMap[direction]) !== null && _a !== void 0 ? _a : 0;
840
+ const transform = rotation ? `rotate(${rotation}deg)` : undefined;
841
+ return m('svg', Object.assign(Object.assign({}, props), { style: Object.assign({ transform }, style), height: '1lh', width: '24', viewBox: '0 0 24 24', xmlns: 'http://www.w3.org/2000/svg' }), iconPaths[name].map((d) => m('path', {
842
+ d,
843
+ fill: d.includes('M0 0h24v24H0z') ? 'none' : 'currentColor',
844
+ })));
845
+ },
846
+ };
847
+ };
848
+
849
+ const Chips = () => {
850
+ const state = {
851
+ chipsData: [],
852
+ selectedChip: null,
853
+ focused: false,
854
+ inputValue: '',
855
+ inputId: uniqueId(),
856
+ autocompleteItems: [],
857
+ selectedAutocompleteIndex: -1,
858
+ showAutocomplete: false,
859
+ };
860
+ let currentVnode = null;
861
+ const processAutocompleteData = (data) => {
862
+ if (Array.isArray(data)) {
863
+ return data.map((item) => {
864
+ if (typeof item === 'string') {
865
+ return { tag: item };
866
+ }
867
+ return item;
868
+ });
869
+ }
870
+ return Object.entries(data).map(([text, value]) => ({
871
+ tag: text,
872
+ value: value || text,
873
+ }));
874
+ };
875
+ const updateAutocomplete = () => {
876
+ var _a;
877
+ if (!((_a = currentVnode === null || currentVnode === void 0 ? void 0 : currentVnode.attrs.autocompleteOptions) === null || _a === void 0 ? void 0 : _a.data)) {
878
+ state.autocompleteItems = [];
879
+ return;
880
+ }
881
+ const { data, minLength = 1 } = currentVnode.attrs.autocompleteOptions;
882
+ const input = state.inputValue.toLowerCase();
883
+ if (input.length < minLength) {
884
+ state.autocompleteItems = [];
885
+ state.showAutocomplete = false;
886
+ return;
887
+ }
888
+ const allOptions = processAutocompleteData(data);
889
+ const filtered = allOptions.filter((option) => option.tag.toLowerCase().includes(input));
890
+ const limit = currentVnode.attrs.autocompleteOptions.limit || Infinity;
891
+ state.autocompleteItems = filtered.slice(0, limit);
892
+ state.showAutocomplete = state.autocompleteItems.length > 0;
893
+ state.selectedAutocompleteIndex = -1;
894
+ };
895
+ const selectAutocompleteItem = (item) => {
896
+ addChip({
897
+ tag: item.tag,
898
+ image: item.image,
899
+ alt: item.alt, // Preserve alt text when converting to chip
900
+ });
901
+ state.inputValue = '';
902
+ state.showAutocomplete = false;
903
+ state.selectedAutocompleteIndex = -1;
904
+ };
905
+ const isValid = (chip, currentChips) => {
906
+ if (!chip.tag || chip.tag.trim() === '')
907
+ return false;
908
+ return !currentChips.some((c) => c.tag === chip.tag);
909
+ };
910
+ const addChip = (chip) => {
911
+ if (!currentVnode)
912
+ return;
913
+ const { limit = Infinity, onChipAdd, onchange } = currentVnode.attrs;
914
+ if (!isValid(chip, state.chipsData) || state.chipsData.length >= limit) {
915
+ return;
916
+ }
917
+ state.chipsData = [...state.chipsData, chip];
918
+ state.inputValue = '';
919
+ if (onChipAdd)
920
+ onChipAdd(chip);
921
+ if (onchange)
922
+ onchange(state.chipsData);
923
+ };
924
+ const deleteChip = (index) => {
925
+ if (!currentVnode)
926
+ return;
927
+ const { onChipDelete, onchange } = currentVnode.attrs;
928
+ const chip = state.chipsData[index];
929
+ state.chipsData = state.chipsData.filter((_, i) => i !== index);
930
+ state.selectedChip = null;
931
+ if (onChipDelete)
932
+ onChipDelete(chip);
933
+ if (onchange)
934
+ onchange(state.chipsData);
935
+ };
936
+ const selectChip = (index) => {
937
+ if (!currentVnode)
938
+ return;
939
+ const { onChipSelect } = currentVnode.attrs;
940
+ state.selectedChip = index;
941
+ if (onChipSelect && state.chipsData[index]) {
942
+ onChipSelect(state.chipsData[index]);
943
+ }
944
+ };
945
+ const handleKeydown = (e) => {
946
+ const target = e.target;
947
+ if (state.showAutocomplete) {
948
+ if (e.key === 'ArrowDown') {
949
+ e.preventDefault();
950
+ state.selectedAutocompleteIndex = Math.min(state.selectedAutocompleteIndex + 1, state.autocompleteItems.length - 1);
951
+ const selectedItem = currentVnode === null || currentVnode === void 0 ? void 0 : currentVnode.dom.querySelector('.autocomplete-item.selected');
952
+ if (selectedItem) {
953
+ selectedItem.scrollIntoView({ block: 'nearest' });
954
+ }
955
+ return;
956
+ }
957
+ if (e.key === 'ArrowUp') {
958
+ e.preventDefault();
959
+ state.selectedAutocompleteIndex = Math.max(state.selectedAutocompleteIndex - 1, -1);
960
+ const selectedItem = currentVnode === null || currentVnode === void 0 ? void 0 : currentVnode.dom.querySelector('.autocomplete-item.selected');
961
+ if (selectedItem) {
962
+ selectedItem.scrollIntoView({ block: 'nearest' });
963
+ }
964
+ return;
965
+ }
966
+ if (e.key === 'Enter' && state.selectedAutocompleteIndex >= 0) {
967
+ e.preventDefault();
968
+ selectAutocompleteItem(state.autocompleteItems[state.selectedAutocompleteIndex]);
969
+ return;
970
+ }
971
+ }
972
+ if (e.key === 'Enter' && target.value.trim()) {
973
+ e.preventDefault();
974
+ addChip({ tag: target.value.trim() });
975
+ }
976
+ else if (e.key === 'Backspace' && !target.value && state.chipsData.length > 0) {
977
+ e.preventDefault();
978
+ // Delete the last chip immediately when backspace is pressed in an empty input
979
+ deleteChip(state.chipsData.length - 1);
980
+ }
981
+ else if (e.key === 'ArrowLeft' && !target.value && state.chipsData.length) {
982
+ e.preventDefault();
983
+ selectChip(state.chipsData.length - 1);
984
+ }
985
+ };
986
+ const handleChipKeydown = (e, index) => {
987
+ if (e.key === 'Backspace' || e.key === 'Delete') {
988
+ e.preventDefault();
989
+ deleteChip(index);
990
+ const newIndex = Math.max(index - 1, 0);
991
+ if (state.chipsData.length)
992
+ selectChip(newIndex);
993
+ }
994
+ else if (e.key === 'ArrowLeft' && index > 0) {
995
+ selectChip(index - 1);
996
+ }
997
+ else if (e.key === 'ArrowRight') {
998
+ if (index < state.chipsData.length - 1) {
999
+ selectChip(index + 1);
1000
+ }
1001
+ else {
1002
+ const input = currentVnode === null || currentVnode === void 0 ? void 0 : currentVnode.dom.querySelector('.chips-input');
1003
+ if (input)
1004
+ input.focus();
1005
+ }
1006
+ }
1007
+ };
1008
+ return {
1009
+ oninit: ({ attrs }) => {
1010
+ state.chipsData = attrs.data || [];
1011
+ },
1012
+ oncreate: (vnode) => {
1013
+ currentVnode = vnode;
1014
+ },
1015
+ onremove: () => {
1016
+ currentVnode = null;
1017
+ },
1018
+ view: ({ attrs }) => {
1019
+ const { id, required, isMandatory = required, className = 'col s12', label, helperText, placeholder, secondaryPlaceholder, } = attrs;
1020
+ const getPlaceholder = () => {
1021
+ if (!state.chipsData.length && placeholder) {
1022
+ return placeholder;
1023
+ }
1024
+ if (state.chipsData.length && secondaryPlaceholder) {
1025
+ return secondaryPlaceholder;
1026
+ }
1027
+ return '';
1028
+ };
1029
+ return m('.input-field', { id, className }, [
1030
+ m('.chips.chips-initial', {
1031
+ class: `chips-container${state.focused ? ' focused' : ''}${placeholder ? ' chips-placeholder' : ''}`,
1032
+ }, [
1033
+ // Chips
1034
+ state.chipsData.map((chip, index) => m('.chip', {
1035
+ key: `${chip.tag}-${index}`,
1036
+ tabindex: 0,
1037
+ class: state.selectedChip === index ? 'selected' : undefined,
1038
+ onkeydown: (e) => handleChipKeydown(e, index),
1039
+ }, [
1040
+ chip.image &&
1041
+ m('img', {
1042
+ src: chip.image,
1043
+ alt: chip.alt || chip.tag,
1044
+ }),
1045
+ chip.tag,
1046
+ m(MaterialIcon, {
1047
+ name: 'close',
1048
+ className: 'close',
1049
+ onclick: (e) => {
1050
+ e.stopPropagation();
1051
+ deleteChip(index);
1052
+ },
1053
+ }),
1054
+ ])),
1055
+ // Input
1056
+ m('input[type=text].chips-input.input.browser-default', {
1057
+ id: state.inputId,
1058
+ title: 'label',
1059
+ value: state.inputValue,
1060
+ placeholder: getPlaceholder(),
1061
+ oninput: (e) => {
1062
+ state.inputValue = e.target.value;
1063
+ updateAutocomplete();
1064
+ },
1065
+ onfocus: () => {
1066
+ state.focused = true;
1067
+ state.selectedChip = null;
1068
+ updateAutocomplete();
1069
+ },
1070
+ onblur: () => {
1071
+ state.focused = false;
1072
+ setTimeout(() => {
1073
+ state.showAutocomplete = false;
1074
+ state.selectedChip = null;
1075
+ }, 150);
1076
+ },
1077
+ onkeydown: handleKeydown,
1078
+ }),
1079
+ state.showAutocomplete &&
1080
+ m('ul.autocomplete-content.dropdown-content', {
1081
+ style: {
1082
+ display: 'block',
1083
+ opacity: 1,
1084
+ transform: 'scaleX(1) scaleY(1)',
1085
+ position: 'absolute',
1086
+ width: '100%',
1087
+ left: 0,
1088
+ top: '100%',
1089
+ maxHeight: '200px',
1090
+ overflow: 'auto',
1091
+ zIndex: 1000,
1092
+ backgroundColor: '#fff',
1093
+ boxShadow: '0 2px 2px 0 rgba(0,0,0,0.14), 0 3px 1px -2px rgba(0,0,0,0.12), 0 1px 5px 0 rgba(0,0,0,0.2)',
1094
+ },
1095
+ }, state.autocompleteItems.map((item, index) => m('li.autocomplete-item', {
1096
+ key: item.tag,
1097
+ class: state.selectedAutocompleteIndex === index ? 'selected' : '',
1098
+ style: {
1099
+ padding: '12px 16px',
1100
+ cursor: 'pointer',
1101
+ backgroundColor: state.selectedAutocompleteIndex === index ? '#eee' : 'transparent',
1102
+ },
1103
+ onmousedown: (e) => {
1104
+ e.preventDefault();
1105
+ selectAutocompleteItem(item);
1106
+ },
1107
+ onmouseover: () => {
1108
+ state.selectedAutocompleteIndex = index;
1109
+ },
1110
+ }, [
1111
+ item.image &&
1112
+ m('img.autocomplete-item-image', {
1113
+ src: item.image,
1114
+ alt: item.alt || item.tag,
1115
+ style: {
1116
+ width: '24px',
1117
+ height: '24px',
1118
+ marginRight: '8px',
1119
+ verticalAlign: 'middle',
1120
+ },
1121
+ }),
1122
+ m('span.autocomplete-item-text', item.tag),
1123
+ ]))),
1124
+ ]),
1125
+ label &&
1126
+ m(Label, {
1127
+ label,
1128
+ id: state.inputId,
1129
+ isMandatory,
1130
+ isActive: state.focused || state.chipsData.length || placeholder ? true : false,
1131
+ }),
1132
+ helperText && m(HelperText, { helperText }),
1133
+ ]);
1134
+ },
1135
+ };
1136
+ };
1137
+
1138
+ /** A simple code block without syntax high-lighting */
1139
+ const CodeBlock = () => ({
1140
+ view: ({ attrs }) => {
1141
+ const { newRow, code, language, className } = attrs, params = __rest(attrs, ["newRow", "code", "language", "className"]);
1142
+ const lang = language || 'lang-TypeScript';
1143
+ const label = lang.replace('lang-', '');
1144
+ const cb = code instanceof Array ? code.join('\n') : code;
1145
+ const cn = [newRow ? 'clear' : '', lang, className].filter(Boolean).join(' ').trim();
1146
+ return m(`pre.codeblock${newRow ? '.clear' : ''}`, attrs, [
1147
+ m('div', m('label', label)),
1148
+ m('code', Object.assign(Object.assign({}, params), { className: cn }), cb),
1149
+ ]);
1150
+ },
1151
+ });
1152
+
1153
+ const CollapsibleItem = () => {
1154
+ return {
1155
+ view: ({ attrs: { header, body, iconName, isActive, onToggle } }) => {
1156
+ return m('li', { className: isActive ? 'active' : '' }, [
1157
+ header || iconName
1158
+ ? m('.collapsible-header', {
1159
+ onclick: onToggle,
1160
+ style: {
1161
+ cursor: 'pointer',
1162
+ padding: '1rem',
1163
+ backgroundColor: '#fff',
1164
+ borderBottom: '1px solid #ddd',
1165
+ display: 'flex',
1166
+ alignItems: 'center',
1167
+ transition: 'background-color 0.2s ease',
1168
+ },
1169
+ onmouseover: (e) => {
1170
+ e.target.style.backgroundColor = '#f5f5f5';
1171
+ },
1172
+ onmouseleave: (e) => {
1173
+ e.target.style.backgroundColor = '#fff';
1174
+ },
1175
+ }, [
1176
+ iconName ? m('i.material-icons', { style: { marginRight: '1rem' } }, iconName) : undefined,
1177
+ header ? (typeof header === 'string' ? m('span', header) : header) : undefined,
1178
+ ])
1179
+ : undefined,
1180
+ m('.collapsible-body', {
1181
+ style: {
1182
+ display: isActive ? 'block' : 'none',
1183
+ transition: 'display 0.3s ease',
1184
+ },
1185
+ }, [
1186
+ m('.collapsible-body-content', {
1187
+ style: { padding: '2rem' },
1188
+ }, body ? (typeof body === 'string' ? m('div', { innerHTML: body }) : body) : undefined),
1189
+ ]),
1190
+ ]);
1191
+ },
1192
+ };
1193
+ };
1194
+ /**
1195
+ * Creates a collabsible or accordion component with pure CSS/Mithril implementation.
1196
+ * No MaterializeCSS JavaScript dependencies.
1197
+ */
1198
+ const Collapsible = () => {
1199
+ const state = {
1200
+ activeItems: new Set(),
1201
+ };
1202
+ return {
1203
+ oninit: ({ attrs }) => {
1204
+ // Initialize active items from the items array
1205
+ attrs.items.forEach((item, index) => {
1206
+ if (item.active) {
1207
+ state.activeItems.add(index);
1208
+ }
1209
+ });
1210
+ },
1211
+ view: ({ attrs }) => {
1212
+ const { items, accordion = true, class: c, className, style, id } = attrs;
1213
+ const toggleItem = (index) => {
1214
+ if (accordion) {
1215
+ // Accordion mode: only one item can be active
1216
+ if (state.activeItems.has(index)) {
1217
+ state.activeItems.clear();
1218
+ }
1219
+ else {
1220
+ state.activeItems.clear();
1221
+ state.activeItems.add(index);
1222
+ }
1223
+ }
1224
+ else {
1225
+ // Expandable mode: multiple items can be active
1226
+ if (state.activeItems.has(index)) {
1227
+ state.activeItems.delete(index);
1228
+ }
1229
+ else {
1230
+ state.activeItems.add(index);
1231
+ }
1232
+ }
1233
+ };
1234
+ return items && items.length > 0
1235
+ ? m('ul.collapsible', {
1236
+ class: c || className,
1237
+ style: Object.assign({ border: '1px solid #ddd', borderRadius: '2px', margin: '0.5rem 0 1rem 0' }, style),
1238
+ id,
1239
+ }, items.map((item, index) => m(CollapsibleItem, Object.assign(Object.assign({}, item), { key: index, isActive: state.activeItems.has(index), onToggle: () => toggleItem(index) }))))
1240
+ : undefined;
1241
+ },
1242
+ };
1243
+ };
1244
+
1245
+ var CollectionMode;
1246
+ (function (CollectionMode) {
1247
+ CollectionMode[CollectionMode["BASIC"] = 0] = "BASIC";
1248
+ CollectionMode[CollectionMode["LINKS"] = 1] = "LINKS";
1249
+ CollectionMode[CollectionMode["AVATAR"] = 2] = "AVATAR";
1250
+ })(CollectionMode || (CollectionMode = {}));
1251
+ const isNonLocalRoute = (url) => url && /https?:\/\//.test(url);
1252
+ const SecondaryContent = () => {
1253
+ return {
1254
+ view: ({ attrs }) => {
1255
+ const { href, iconName = 'send', onclick, style = { cursor: 'pointer' } } = attrs;
1256
+ const props = {
1257
+ href,
1258
+ style,
1259
+ className: 'secondary-content',
1260
+ onclick: onclick ? () => onclick(attrs) : undefined,
1261
+ };
1262
+ return isNonLocalRoute(href) || !href
1263
+ ? m('a[target=_]', props, m(Icon, { iconName }))
1264
+ : m(m.route.Link, props, m(Icon, { iconName }));
1265
+ },
1266
+ };
1267
+ };
1268
+ const avatarIsImage = (avatar = '') => /\./.test(avatar);
1269
+ const ListItem = () => {
1270
+ return {
1271
+ view: ({ attrs: { item, mode } }) => {
1272
+ const { title, content = '', active, iconName, avatar, className, onclick } = item;
1273
+ return mode === CollectionMode.AVATAR
1274
+ ? m('li.collection-item.avatar', {
1275
+ className: active ? 'active' : '',
1276
+ onclick: onclick ? () => onclick(item) : undefined,
1277
+ }, [
1278
+ avatarIsImage(avatar)
1279
+ ? m('img.circle', { src: avatar })
1280
+ : m('i.material-icons.circle', { className }, avatar),
1281
+ m('span.title', title),
1282
+ m('p', m.trust(content)),
1283
+ m(SecondaryContent, item),
1284
+ ])
1285
+ : m('li.collection-item', {
1286
+ className: active ? 'active' : '',
1287
+ }, iconName ? m('div', [title, m(SecondaryContent, item)]) : title);
1288
+ },
1289
+ };
1290
+ };
1291
+ const BasicCollection = () => {
1292
+ return {
1293
+ view: (_a) => {
1294
+ var _b = _a.attrs, { header, items, mode = CollectionMode.BASIC } = _b, params = __rest(_b, ["header", "items", "mode"]);
1295
+ const collectionItems = items.map((item) => m(ListItem, { key: item.id, item, mode }));
1296
+ return header
1297
+ ? m('ul.collection.with-header', params, [m('li.collection-header', m('h4', header)), collectionItems])
1298
+ : m('ul.collection', params, collectionItems);
1299
+ },
1300
+ };
1301
+ };
1302
+ const AnchorItem = () => {
1303
+ return {
1304
+ view: ({ attrs: { item } }) => {
1305
+ const { title, active, href } = item, params = __rest(item, ["title", "active", "href"]);
1306
+ const props = Object.assign(Object.assign({}, params), { className: `collection-item ${active ? 'active' : ''}`, href });
1307
+ return isNonLocalRoute(href) || !href
1308
+ ? m('a[target=_]', props, title)
1309
+ : m(m.route.Link, props, title);
1310
+ },
1311
+ };
1312
+ };
1313
+ const LinksCollection = () => {
1314
+ return {
1315
+ view: (_a) => {
1316
+ var _b = _a.attrs, { items, header } = _b, params = __rest(_b, ["items", "header"]);
1317
+ return header
1318
+ ? m('.collection.with-header', params, [
1319
+ m('.collection-header', m('h4', header)),
1320
+ items.map((item) => m(AnchorItem, { key: item.id, item })),
1321
+ ])
1322
+ : m('.collection', params, items.map((item) => m(AnchorItem, { key: item.id, item })));
1323
+ },
1324
+ };
1325
+ };
1326
+ /**
1327
+ * Creates a Collection of items, optionally containing links, headers, secondary content or avatars.
1328
+ * @see https://materializecss.com/collections.html
1329
+ */
1330
+ const Collection = () => {
1331
+ return {
1332
+ view: (_a) => {
1333
+ var _b = _a.attrs, { items, header, mode = CollectionMode.BASIC } = _b, params = __rest(_b, ["items", "header", "mode"]);
1334
+ return header || (items && items.length > 0)
1335
+ ? mode === CollectionMode.LINKS
1336
+ ? m(LinksCollection, Object.assign({ header, items }, params))
1337
+ : m(BasicCollection, Object.assign({ header, items, mode }, params))
1338
+ : undefined;
1339
+ },
1340
+ };
1341
+ };
1342
+
1343
+ /** Pure TypeScript Dropdown component - no Materialize dependencies */
1344
+ const Dropdown = () => {
1345
+ const state = {
1346
+ isOpen: false,
1347
+ initialValue: undefined,
1348
+ id: '',
1349
+ focusedIndex: -1,
1350
+ inputRef: null,
1351
+ dropdownRef: null,
1352
+ };
1353
+ const handleKeyDown = (e, items, onchange) => {
1354
+ const availableItems = items.filter((item) => !item.divider && !item.disabled);
1355
+ switch (e.key) {
1356
+ case 'ArrowDown':
1357
+ e.preventDefault();
1358
+ if (!state.isOpen) {
1359
+ state.isOpen = true;
1360
+ state.focusedIndex = 0;
1361
+ }
1362
+ else {
1363
+ state.focusedIndex = Math.min(state.focusedIndex + 1, availableItems.length - 1);
1364
+ }
1365
+ break;
1366
+ case 'ArrowUp':
1367
+ e.preventDefault();
1368
+ if (state.isOpen) {
1369
+ state.focusedIndex = Math.max(state.focusedIndex - 1, 0);
1370
+ }
1371
+ break;
1372
+ case 'Enter':
1373
+ case ' ':
1374
+ e.preventDefault();
1375
+ if (state.isOpen && state.focusedIndex >= 0 && state.focusedIndex < availableItems.length) {
1376
+ const selectedItem = availableItems[state.focusedIndex];
1377
+ const value = (selectedItem.id || selectedItem.label);
1378
+ state.initialValue = value;
1379
+ state.isOpen = false;
1380
+ state.focusedIndex = -1;
1381
+ if (onchange)
1382
+ onchange(value);
1383
+ }
1384
+ else if (!state.isOpen) {
1385
+ state.isOpen = true;
1386
+ state.focusedIndex = 0;
1387
+ }
1388
+ break;
1389
+ case 'Escape':
1390
+ e.preventDefault();
1391
+ state.isOpen = false;
1392
+ state.focusedIndex = -1;
1393
+ break;
1394
+ }
1395
+ };
1396
+ return {
1397
+ oninit: ({ attrs: { id = uniqueId(), initialValue, checkedId } }) => {
1398
+ state.id = id;
1399
+ state.initialValue = initialValue || checkedId;
1400
+ // Mithril will handle click events through the component structure
1401
+ },
1402
+ view: ({ attrs: { key, label, onchange, disabled = false, items, iconName, helperText, style, className = 'col s12' }, }) => {
1403
+ const { initialValue } = state;
1404
+ const selectedItem = initialValue
1405
+ ? items.filter((i) => (i.id ? i.id === initialValue : i.label === initialValue)).shift()
1406
+ : undefined;
1407
+ const title = selectedItem ? selectedItem.label : label || 'Select';
1408
+ const availableItems = items.filter((item) => !item.divider && !item.disabled);
1409
+ return m('.dropdown-wrapper.input-field', { className, key, style }, [
1410
+ iconName ? m('i.material-icons.prefix', iconName) : undefined,
1411
+ m(HelperText, { helperText }),
1412
+ m('.select-wrapper', {
1413
+ onclick: disabled
1414
+ ? undefined
1415
+ : () => {
1416
+ state.isOpen = !state.isOpen;
1417
+ state.focusedIndex = state.isOpen ? 0 : -1;
1418
+ },
1419
+ onkeydown: disabled ? undefined : (e) => handleKeyDown(e, items, onchange),
1420
+ tabindex: disabled ? -1 : 0,
1421
+ 'aria-expanded': state.isOpen ? 'true' : 'false',
1422
+ 'aria-haspopup': 'listbox',
1423
+ role: 'combobox',
1424
+ }, [
1425
+ m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
1426
+ id: state.id,
1427
+ value: title,
1428
+ oncreate: ({ dom }) => {
1429
+ state.inputRef = dom;
1430
+ },
1431
+ onclick: (e) => {
1432
+ e.preventDefault();
1433
+ e.stopPropagation();
1434
+ if (!disabled) {
1435
+ state.isOpen = !state.isOpen;
1436
+ state.focusedIndex = state.isOpen ? 0 : -1;
1437
+ }
1438
+ },
1439
+ }),
1440
+ // Dropdown Menu using Select component's positioning logic
1441
+ state.isOpen &&
1442
+ m('ul.dropdown-content.select-dropdown', {
1443
+ tabindex: 0,
1444
+ role: 'listbox',
1445
+ 'aria-labelledby': state.id,
1446
+ oncreate: ({ dom }) => {
1447
+ state.dropdownRef = dom;
1448
+ },
1449
+ onremove: () => {
1450
+ state.dropdownRef = null;
1451
+ },
1452
+ style: getDropdownStyles(state.inputRef, true, items.map((item) => (Object.assign(Object.assign({}, item), {
1453
+ // Convert dropdown items to format expected by getDropdownStyles
1454
+ group: undefined }))), true),
1455
+ }, items.map((item, index) => {
1456
+ if (item.divider) {
1457
+ return m('li.divider', {
1458
+ key: `divider-${index}`,
1459
+ });
1460
+ }
1461
+ const itemIndex = availableItems.indexOf(item);
1462
+ const isFocused = itemIndex === state.focusedIndex;
1463
+ return m('li', Object.assign({ key: item.id || `item-${index}`, class: [
1464
+ item.disabled ? 'disabled' : '',
1465
+ isFocused ? 'focused' : '',
1466
+ (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.id) === item.id || (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.label) === item.label ? 'selected' : '',
1467
+ ]
1468
+ .filter(Boolean)
1469
+ .join(' ') }, (item.disabled
1470
+ ? {}
1471
+ : {
1472
+ onclick: (e) => {
1473
+ e.stopPropagation();
1474
+ const value = (item.id || item.label);
1475
+ state.initialValue = value;
1476
+ state.isOpen = false;
1477
+ state.focusedIndex = -1;
1478
+ if (onchange)
1479
+ onchange(value);
1480
+ },
1481
+ })), m('span', {
1482
+ style: {
1483
+ display: 'flex',
1484
+ alignItems: 'center',
1485
+ padding: '14px 16px',
1486
+ },
1487
+ }, [
1488
+ item.iconName
1489
+ ? m('i.material-icons', {
1490
+ style: { marginRight: '32px' },
1491
+ }, item.iconName)
1492
+ : undefined,
1493
+ item.label,
1494
+ ]));
1495
+ })),
1496
+ m(MaterialIcon, {
1497
+ name: 'caret',
1498
+ direction: 'down',
1499
+ }),
1500
+ ]),
1501
+ ]);
1502
+ },
1503
+ };
1504
+ };
1505
+
1506
+ /**
1507
+ * Floating Action Button
1508
+ */
1509
+ const FloatingActionButton = () => {
1510
+ const state = {
1511
+ isOpen: false,
1512
+ };
1513
+ const handleClickOutside = (e) => {
1514
+ const target = e.target;
1515
+ if (!target.closest('.fixed-action-btn')) {
1516
+ state.isOpen = false;
1517
+ }
1518
+ };
1519
+ return {
1520
+ oncreate: () => {
1521
+ document.addEventListener('click', handleClickOutside);
1522
+ },
1523
+ onremove: () => {
1524
+ document.removeEventListener('click', handleClickOutside);
1525
+ },
1526
+ view: ({ attrs: { className, iconName, iconClass, position, style = position === 'left' || position === 'inline-left'
1527
+ ? 'position: absolute; display: inline-block; left: 24px;'
1528
+ : position === 'right' || position === 'inline-right'
1529
+ ? 'position: absolute; display: inline-block; right: 24px;'
1530
+ : undefined, buttons, direction = 'top', hoverEnabled = true, }, }) => {
1531
+ const fabClasses = [
1532
+ 'fixed-action-btn',
1533
+ direction ? `direction-${direction}` : '',
1534
+ state.isOpen ? 'active' : '',
1535
+ // hoverEnabled ? 'hover-enabled' : '',
1536
+ ]
1537
+ .filter(Boolean)
1538
+ .join(' ');
1539
+ return m('div', {
1540
+ style: position === 'inline-right' || position === 'inline-left' ? 'position: relative; height: 70px;' : undefined,
1541
+ }, m(`.${fabClasses}`, {
1542
+ style,
1543
+ onclick: (e) => {
1544
+ e.stopPropagation();
1545
+ if (buttons && buttons.length > 0) {
1546
+ state.isOpen = !state.isOpen;
1547
+ }
1548
+ },
1549
+ onmouseover: hoverEnabled
1550
+ ? () => {
1551
+ if (buttons && buttons.length > 0) {
1552
+ state.isOpen = true;
1553
+ }
1554
+ }
1555
+ : undefined,
1556
+ onmouseleave: hoverEnabled
1557
+ ? () => {
1558
+ state.isOpen = false;
1559
+ }
1560
+ : undefined,
1561
+ }, [
1562
+ m('a.btn-floating.btn-large', {
1563
+ className,
1564
+ }, m('i.material-icons', { className: iconClass }, iconName)),
1565
+ buttons &&
1566
+ buttons.length > 0 &&
1567
+ m('ul', buttons.map((button, index) => m('li', m(`a.btn-floating.${button.className || 'red'}`, {
1568
+ style: {
1569
+ opacity: state.isOpen ? '1' : '0',
1570
+ transform: state.isOpen ? 'scale(1)' : 'scale(0.4)',
1571
+ transition: `all 0.3s ease ${index * 40}ms`,
1572
+ },
1573
+ onclick: (e) => {
1574
+ e.stopPropagation();
1575
+ if (button.onClick)
1576
+ button.onClick(e);
1577
+ },
1578
+ }, m('i.material-icons', { className: button.iconClass }, button.iconName))))),
1579
+ ]));
1580
+ },
1581
+ };
1582
+ };
1583
+
1584
+ /** Character counter component that tracks text length against maxLength */
1585
+ const CharacterCounter = () => {
1586
+ return {
1587
+ view: ({ attrs }) => {
1588
+ const { currentLength, maxLength, show } = attrs;
1589
+ if (!show)
1590
+ return null;
1591
+ const isOverLimit = currentLength > maxLength;
1592
+ return m('span.character-counter', {
1593
+ style: {
1594
+ color: isOverLimit ? '#F44336' : '#9e9e9e',
1595
+ fontSize: '12px',
1596
+ display: 'block',
1597
+ textAlign: 'right',
1598
+ marginTop: '8px',
1599
+ },
1600
+ }, `${currentLength}/${maxLength}`);
1601
+ },
1602
+ };
1603
+ };
1604
+ /** Create a TextArea */
1605
+ const TextArea = () => {
1606
+ const state = {
1607
+ id: uniqueId(),
1608
+ currentLength: 0,
1609
+ hasInteracted: false,
1610
+ height: undefined,
1611
+ active: false,
1612
+ textarea: undefined,
1613
+ };
1614
+ const updateHeight = (textarea) => {
1615
+ textarea.style.height = 'auto';
1616
+ const newHeight = textarea.scrollHeight + 'px';
1617
+ state.height = textarea.value.length === 0 ? undefined : newHeight;
1618
+ };
1619
+ return {
1620
+ onremove: () => {
1621
+ },
1622
+ view: ({ attrs }) => {
1623
+ var _a;
1624
+ const { className = 'col s12', helperText, iconName, id = state.id, initialValue, placeholder, isMandatory, label, maxLength, oninput, onchange, onkeydown, onkeypress, onkeyup, onblur, style } = attrs, params = __rest(attrs, ["className", "helperText", "iconName", "id", "initialValue", "placeholder", "isMandatory", "label", "maxLength", "oninput", "onchange", "onkeydown", "onkeypress", "onkeyup", "onblur", "style"]);
1625
+ // const attributes = toAttrs(params);
1626
+ return m('.input-field', { className, style }, [
1627
+ iconName ? m('i.material-icons.prefix', iconName) : '',
1628
+ m('textarea.materialize-textarea', Object.assign(Object.assign({}, params), { id, tabindex: 0, style: {
1629
+ height: state.height,
1630
+ }, oncreate: ({ dom }) => {
1631
+ const textarea = (state.textarea = dom);
1632
+ // Set initial value and height if provided
1633
+ if (initialValue !== undefined) {
1634
+ textarea.value = String(initialValue);
1635
+ updateHeight(textarea);
1636
+ // } else {
1637
+ // updateHeight(textarea);
1638
+ }
1639
+ // Update character count state for counter component
1640
+ if (maxLength) {
1641
+ state.currentLength = textarea.value.length; // Initial count
1642
+ m.redraw();
1643
+ }
1644
+ }, onupdate: ({ dom }) => {
1645
+ const textarea = dom;
1646
+ if (state.height)
1647
+ textarea.style.height = state.height;
1648
+ }, onfocus: () => {
1649
+ state.active = true;
1650
+ }, oninput: (e) => {
1651
+ state.active = true;
1652
+ state.hasInteracted = false;
1653
+ const target = e.target;
1654
+ // Update height for auto-resize
1655
+ updateHeight(target);
1656
+ // Update character count
1657
+ if (maxLength) {
1658
+ state.currentLength = target.value.length;
1659
+ state.hasInteracted = target.value.length > 0;
1660
+ }
1661
+ // Call onchange handler
1662
+ if (oninput) {
1663
+ oninput(target.value);
1664
+ }
1665
+ }, onblur: (e) => {
1666
+ state.active = false;
1667
+ // const target = e.target as HTMLTextAreaElement;
1668
+ state.hasInteracted = true;
1669
+ // Call original onblur if provided
1670
+ if (onblur) {
1671
+ onblur(e);
1672
+ }
1673
+ if (onchange && state.textarea) {
1674
+ onchange(state.textarea.value);
1675
+ }
1676
+ }, value: initialValue, onkeyup: onkeyup
1677
+ ? (ev) => {
1678
+ onkeyup(ev, ev.target.value);
1679
+ }
1680
+ : undefined, onkeydown: onkeydown
1681
+ ? (ev) => {
1682
+ onkeydown(ev, ev.target.value);
1683
+ }
1684
+ : undefined, onkeypress: onkeypress
1685
+ ? (ev) => {
1686
+ onkeypress(ev, ev.target.value);
1687
+ }
1688
+ : undefined })),
1689
+ m(Label, {
1690
+ label,
1691
+ id,
1692
+ isMandatory,
1693
+ isActive: ((_a = state.textarea) === null || _a === void 0 ? void 0 : _a.value) || initialValue || placeholder || state.active,
1694
+ }),
1695
+ m(HelperText, {
1696
+ helperText,
1697
+ dataError: state.hasInteracted && attrs.dataError ? attrs.dataError : undefined,
1698
+ dataSuccess: state.hasInteracted && attrs.dataSuccess ? attrs.dataSuccess : undefined,
1699
+ }),
1700
+ maxLength
1701
+ ? m(CharacterCounter, {
1702
+ currentLength: state.currentLength,
1703
+ maxLength,
1704
+ show: state.currentLength > 0,
1705
+ })
1706
+ : undefined,
1707
+ ]);
1708
+ },
1709
+ };
1710
+ };
1711
+ /** Default component for all kinds of input fields. */
1712
+ const InputField = (type, defaultClass = '') => () => {
1713
+ const state = {
1714
+ id: uniqueId(),
1715
+ currentLength: 0,
1716
+ hasInteracted: false,
1717
+ isValid: true,
1718
+ active: false,
1719
+ inputElement: null,
1720
+ };
1721
+ // let labelManager: { updateLabelState: () => void; cleanup: () => void } | null = null;
1722
+ // let lengthUpdateHandler: (() => void) | null = null;
1723
+ // let inputElement: HTMLInputElement | null = null;
1724
+ const getValue = (target) => {
1725
+ const val = target.value;
1726
+ return (val ? (type === 'number' || type === 'range' ? +val : val) : val);
1727
+ };
1728
+ const setValidity = (target, validationResult) => {
1729
+ if (typeof validationResult === 'boolean') {
1730
+ target.setCustomValidity(validationResult ? '' : 'Custom validation failed');
1731
+ }
1732
+ else {
1733
+ target.setCustomValidity(validationResult);
1734
+ }
1735
+ };
1736
+ const focus = ({ autofocus }) => autofocus ? (typeof autofocus === 'boolean' ? autofocus : autofocus()) : false;
1737
+ const lengthUpdateHandler = () => {
1738
+ var _a;
1739
+ const length = (_a = state.inputElement) === null || _a === void 0 ? void 0 : _a.value.length;
1740
+ if (length) {
1741
+ state.currentLength = length;
1742
+ state.hasInteracted = length > 0;
1743
+ }
1744
+ };
1745
+ return {
1746
+ view: ({ attrs }) => {
1747
+ var _a;
1748
+ const { className = 'col s12', dataError, dataSuccess, helperText, iconName, id = state.id, initialValue, placeholder, isMandatory, label, maxLength, newRow, oninput, onchange, onkeydown, onkeypress, onkeyup, style, validate } = attrs, params = __rest(attrs, ["className", "dataError", "dataSuccess", "helperText", "iconName", "id", "initialValue", "placeholder", "isMandatory", "label", "maxLength", "newRow", "oninput", "onchange", "onkeydown", "onkeypress", "onkeyup", "style", "validate"]);
1749
+ // const attributes = toAttrs(params);
1750
+ const cn = [newRow ? 'clear' : '', defaultClass, className].filter(Boolean).join(' ').trim();
1751
+ return m('.input-field', { className: cn, style }, [
1752
+ iconName ? m('i.material-icons.prefix', iconName) : undefined,
1753
+ m('input.validate', Object.assign(Object.assign({}, params), { type, tabindex: 0, id,
1754
+ // attributes,
1755
+ oncreate: ({ dom }) => {
1756
+ const input = (state.inputElement = dom);
1757
+ if (focus(attrs)) {
1758
+ input.focus();
1759
+ }
1760
+ // Set initial value if provided
1761
+ if (initialValue !== undefined) {
1762
+ input.value = String(initialValue);
1763
+ }
1764
+ // Update character count state for counter component
1765
+ if (maxLength) {
1766
+ state.currentLength = input.value.length; // Initial count
1767
+ }
1768
+ // Range input functionality
1769
+ if (type === 'range') {
1770
+ const updateThumb = () => {
1771
+ const value = input.value;
1772
+ const min = input.min || '0';
1773
+ const max = input.max || '100';
1774
+ const percentage = ((parseFloat(value) - parseFloat(min)) / (parseFloat(max) - parseFloat(min))) * 100;
1775
+ input.style.setProperty('--range-progress', `${percentage}%`);
1776
+ };
1777
+ input.addEventListener('input', updateThumb);
1778
+ updateThumb(); // Initial position
1779
+ }
1780
+ }, onkeyup: onkeyup
1781
+ ? (ev) => {
1782
+ onkeyup(ev, getValue(ev.target));
1783
+ }
1784
+ : undefined, onkeydown: onkeydown
1785
+ ? (ev) => {
1786
+ onkeydown(ev, getValue(ev.target));
1787
+ }
1788
+ : undefined, onkeypress: onkeypress
1789
+ ? (ev) => {
1790
+ onkeypress(ev, getValue(ev.target));
1791
+ }
1792
+ : undefined, onupdate: validate
1793
+ ? ({ dom }) => {
1794
+ const target = dom;
1795
+ setValidity(target, validate(getValue(target), target));
1796
+ }
1797
+ : undefined, oninput: (e) => {
1798
+ state.active = true;
1799
+ const target = e.target;
1800
+ // Handle original oninput logic
1801
+ const value = getValue(target);
1802
+ if (oninput) {
1803
+ oninput(value);
1804
+ }
1805
+ if (maxLength) {
1806
+ lengthUpdateHandler();
1807
+ }
1808
+ // Don't validate on input, only clear error states if user is typing
1809
+ if (validate && target.classList.contains('invalid') && target.value.length > 0) {
1810
+ const validationResult = validate(value, target);
1811
+ if (typeof validationResult === 'boolean' && validationResult) {
1812
+ target.classList.remove('invalid');
1813
+ target.classList.add('valid');
1814
+ state.isValid = true;
1815
+ }
1816
+ else if (typeof validationResult === 'string' && validationResult === '') {
1817
+ target.classList.remove('invalid');
1818
+ target.classList.add('valid');
1819
+ state.isValid = true;
1820
+ }
1821
+ }
1822
+ }, onfocus: () => {
1823
+ state.active = true;
1824
+ }, onblur: (e) => {
1825
+ state.active = false;
1826
+ const target = e.target;
1827
+ state.hasInteracted = true;
1828
+ if (target && validate) {
1829
+ const value = getValue(target);
1830
+ // Only validate if user has entered some text
1831
+ if (value && String(value).length > 0) {
1832
+ const validationResult = validate(value, target);
1833
+ state.isValid = typeof validationResult === 'boolean' ? validationResult : false;
1834
+ setValidity(target, validationResult);
1835
+ // Update visual validation state
1836
+ if (typeof validationResult === 'boolean') {
1837
+ if (validationResult) {
1838
+ target.classList.remove('invalid');
1839
+ target.classList.add('valid');
1840
+ }
1841
+ else {
1842
+ target.classList.remove('valid');
1843
+ target.classList.add('invalid');
1844
+ }
1845
+ }
1846
+ else if (typeof validationResult === 'string') {
1847
+ target.classList.remove('valid');
1848
+ target.classList.add('invalid');
1849
+ state.isValid = false;
1850
+ }
1851
+ }
1852
+ else {
1853
+ // Clear validation state if no text
1854
+ target.classList.remove('valid', 'invalid');
1855
+ state.isValid = true;
1856
+ }
1857
+ }
1858
+ // Also call the original onblur handler if provided
1859
+ if (attrs.onblur) {
1860
+ attrs.onblur(e);
1861
+ }
1862
+ if (onchange && state.inputElement) {
1863
+ onchange(getValue(state.inputElement));
1864
+ }
1865
+ }, value: initialValue })),
1866
+ m(Label, {
1867
+ label,
1868
+ id,
1869
+ isMandatory,
1870
+ isActive: state.active ||
1871
+ ((_a = state.inputElement) === null || _a === void 0 ? void 0 : _a.value) ||
1872
+ initialValue ||
1873
+ placeholder ||
1874
+ type === 'number' ||
1875
+ type === 'color' ||
1876
+ type === 'range'
1877
+ ? true
1878
+ : false,
1879
+ }),
1880
+ m(HelperText, {
1881
+ helperText,
1882
+ dataError: state.hasInteracted && !state.isValid ? dataError : undefined,
1883
+ dataSuccess: state.hasInteracted && state.isValid ? dataSuccess : undefined,
1884
+ }),
1885
+ maxLength
1886
+ ? m(CharacterCounter, {
1887
+ currentLength: state.currentLength,
1888
+ maxLength,
1889
+ show: state.currentLength > 0,
1890
+ })
1891
+ : undefined,
1892
+ ]);
1893
+ },
1894
+ };
1895
+ };
1896
+ /** Component for entering some text */
1897
+ const TextInput = InputField('text');
1898
+ /** Component for entering a password */
1899
+ const PasswordInput = InputField('password');
1900
+ /** Component for entering a number */
1901
+ const NumberInput = InputField('number');
1902
+ /** Component for entering a URL */
1903
+ const UrlInput = InputField('url');
1904
+ /** Component for entering a color */
1905
+ const ColorInput = InputField('color');
1906
+ /** Component for entering a range */
1907
+ const RangeInput = InputField('range', '.range-field');
1908
+ /** Component for entering an email */
1909
+ const EmailInput = InputField('email');
1910
+ /** Component for uploading a file */
1911
+ const FileInput = () => {
1912
+ let canClear = false;
1913
+ let i;
1914
+ return {
1915
+ view: ({ attrs }) => {
1916
+ const { multiple, disabled, initialValue, placeholder, onchange, className = 'col s12', accept: acceptedFiles, label = 'File', } = attrs;
1917
+ const accept = acceptedFiles
1918
+ ? acceptedFiles instanceof Array
1919
+ ? acceptedFiles.join(', ')
1920
+ : acceptedFiles
1921
+ : undefined;
1922
+ return m('.file-field.input-field', {
1923
+ className: attrs.class || className,
1924
+ }, [
1925
+ m('.btn', [
1926
+ m('span', label),
1927
+ m('input[type=file]', {
1928
+ title: label,
1929
+ accept,
1930
+ multiple,
1931
+ disabled,
1932
+ onchange: onchange
1933
+ ? (e) => {
1934
+ const i = e.target;
1935
+ if (i && i.files && onchange) {
1936
+ canClear = true;
1937
+ onchange(i.files);
1938
+ }
1939
+ }
1940
+ : undefined,
1941
+ }),
1942
+ ]),
1943
+ m('.file-path-wrapper', m('input.file-path.validate[type=text]', {
1944
+ placeholder,
1945
+ oncreate: ({ dom }) => {
1946
+ i = dom;
1947
+ if (initialValue)
1948
+ i.value = initialValue;
1949
+ },
1950
+ })),
1951
+ (canClear || initialValue) &&
1952
+ m('a.waves-effect.waves-teal.btn-flat', {
1953
+ style: {
1954
+ float: 'right',
1955
+ position: 'relative',
1956
+ top: '-3rem',
1957
+ padding: 0,
1958
+ },
1959
+ onclick: () => {
1960
+ canClear = false;
1961
+ i.value = '';
1962
+ onchange && onchange({});
1963
+ },
1964
+ }, m(MaterialIcon, {
1965
+ name: 'close',
1966
+ className: 'close',
1967
+ })),
1968
+ ]);
1969
+ },
1970
+ };
1971
+ };
1972
+
1973
+ /**
1974
+ * Pure TypeScript MaterialBox - creates an image lightbox that fills the screen when clicked
1975
+ * No MaterializeCSS dependencies
1976
+ */
1977
+ const MaterialBox = () => {
1978
+ const state = {
1979
+ isOpen: false,
1980
+ originalImage: null,
1981
+ overlay: null,
1982
+ overlayImage: null,
1983
+ };
1984
+ const openBox = (img, attrs) => {
1985
+ if (state.isOpen)
1986
+ return;
1987
+ state.isOpen = true;
1988
+ state.originalImage = img;
1989
+ if (attrs.onOpenStart)
1990
+ attrs.onOpenStart();
1991
+ // Create overlay
1992
+ const overlay = document.createElement('div');
1993
+ overlay.className = 'materialbox-overlay';
1994
+ overlay.style.cssText = `
1995
+ position: fixed;
1996
+ top: 0;
1997
+ left: 0;
1998
+ right: 0;
1999
+ bottom: 0;
2000
+ background-color: rgba(0, 0, 0, 0.85);
2001
+ z-index: 1000;
2002
+ opacity: 0;
2003
+ transition: opacity ${attrs.inDuration || 275}ms ease;
2004
+ cursor: zoom-out;
2005
+ `;
2006
+ // Create enlarged image
2007
+ const enlargedImg = document.createElement('img');
2008
+ enlargedImg.src = img.src;
2009
+ enlargedImg.alt = img.alt || '';
2010
+ enlargedImg.className = 'materialbox-image';
2011
+ // Get original image dimensions and position
2012
+ const imgRect = img.getBoundingClientRect();
2013
+ const windowWidth = window.innerWidth;
2014
+ const windowHeight = window.innerHeight;
2015
+ // Calculate final size maintaining aspect ratio
2016
+ const aspectRatio = img.naturalWidth / img.naturalHeight;
2017
+ const maxWidth = windowWidth * 0.9;
2018
+ const maxHeight = windowHeight * 0.9;
2019
+ let finalWidth = maxWidth;
2020
+ let finalHeight = maxWidth / aspectRatio;
2021
+ if (finalHeight > maxHeight) {
2022
+ finalHeight = maxHeight;
2023
+ finalWidth = maxHeight * aspectRatio;
2024
+ }
2025
+ // Set initial position and size (same as original image)
2026
+ enlargedImg.style.cssText = `
2027
+ position: fixed;
2028
+ top: ${imgRect.top}px;
2029
+ left: ${imgRect.left}px;
2030
+ width: ${imgRect.width}px;
2031
+ height: ${imgRect.height}px;
2032
+ transition: all ${attrs.inDuration || 275}ms ease;
2033
+ cursor: zoom-out;
2034
+ max-width: none;
2035
+ z-index: 1001;
2036
+ `;
2037
+ // Add caption if provided
2038
+ let caption = null;
2039
+ if (attrs.caption) {
2040
+ caption = document.createElement('div');
2041
+ caption.className = 'materialbox-caption';
2042
+ caption.textContent = attrs.caption;
2043
+ caption.style.cssText = `
2044
+ position: fixed;
2045
+ bottom: 20px;
2046
+ left: 50%;
2047
+ transform: translateX(-50%);
2048
+ color: white;
2049
+ font-size: 16px;
2050
+ text-align: center;
2051
+ opacity: 0;
2052
+ transition: opacity ${attrs.inDuration || 275}ms ease ${attrs.inDuration || 275}ms;
2053
+ z-index: 1002;
2054
+ pointer-events: none;
2055
+ `;
2056
+ }
2057
+ // Add to DOM
2058
+ document.body.appendChild(overlay);
2059
+ document.body.appendChild(enlargedImg);
2060
+ if (caption)
2061
+ document.body.appendChild(caption);
2062
+ // Store references
2063
+ state.overlay = overlay;
2064
+ state.overlayImage = enlargedImg;
2065
+ // Prevent body scrolling
2066
+ document.body.style.overflow = 'hidden';
2067
+ // Trigger animations
2068
+ requestAnimationFrame(() => {
2069
+ overlay.style.opacity = '1';
2070
+ enlargedImg.style.top = `${(windowHeight - finalHeight) / 2}px`;
2071
+ enlargedImg.style.left = `${(windowWidth - finalWidth) / 2}px`;
2072
+ enlargedImg.style.width = `${finalWidth}px`;
2073
+ enlargedImg.style.height = `${finalHeight}px`;
2074
+ if (caption) {
2075
+ caption.style.opacity = '1';
2076
+ }
2077
+ });
2078
+ // Add close handlers
2079
+ const closeHandler = () => closeBox(attrs);
2080
+ overlay.addEventListener('click', closeHandler);
2081
+ enlargedImg.addEventListener('click', closeHandler);
2082
+ document.addEventListener('keydown', (e) => {
2083
+ if (e.key === 'Escape')
2084
+ closeHandler();
2085
+ });
2086
+ // Call onOpenEnd after animation
2087
+ setTimeout(() => {
2088
+ if (attrs.onOpenEnd)
2089
+ attrs.onOpenEnd();
2090
+ }, attrs.inDuration || 275);
2091
+ };
2092
+ const closeBox = (attrs) => {
2093
+ if (!state.isOpen || !state.originalImage || !state.overlay || !state.overlayImage)
2094
+ return;
2095
+ if (attrs.onCloseStart)
2096
+ attrs.onCloseStart();
2097
+ const originalRect = state.originalImage.getBoundingClientRect();
2098
+ // Animate back to original position
2099
+ state.overlay.style.opacity = '0';
2100
+ state.overlayImage.style.top = `${originalRect.top}px`;
2101
+ state.overlayImage.style.left = `${originalRect.left}px`;
2102
+ state.overlayImage.style.width = `${originalRect.width}px`;
2103
+ state.overlayImage.style.height = `${originalRect.height}px`;
2104
+ // Hide caption
2105
+ const caption = document.querySelector('.materialbox-caption');
2106
+ if (caption) {
2107
+ caption.style.opacity = '0';
2108
+ }
2109
+ // Clean up after animation
2110
+ setTimeout(() => {
2111
+ if (state.overlay) {
2112
+ document.body.removeChild(state.overlay);
2113
+ state.overlay = null;
2114
+ }
2115
+ if (state.overlayImage) {
2116
+ document.body.removeChild(state.overlayImage);
2117
+ state.overlayImage = null;
2118
+ }
2119
+ if (caption) {
2120
+ document.body.removeChild(caption);
2121
+ }
2122
+ // Restore body scroll
2123
+ document.body.style.overflow = '';
2124
+ state.isOpen = false;
2125
+ state.originalImage = null;
2126
+ if (attrs.onCloseEnd)
2127
+ attrs.onCloseEnd();
2128
+ }, attrs.outDuration || 200);
2129
+ };
2130
+ return {
2131
+ onremove: () => {
2132
+ // Clean up if component is removed while open
2133
+ if (state.isOpen) {
2134
+ if (state.overlay)
2135
+ document.body.removeChild(state.overlay);
2136
+ if (state.overlayImage)
2137
+ document.body.removeChild(state.overlayImage);
2138
+ const caption = document.querySelector('.materialbox-caption');
2139
+ if (caption)
2140
+ document.body.removeChild(caption);
2141
+ document.body.style.overflow = '';
2142
+ }
2143
+ },
2144
+ view: ({ attrs }) => {
2145
+ const { src, alt, width, height, caption, className, style } = attrs, otherAttrs = __rest(attrs, ["src", "alt", "width", "height", "caption", "className", "style"]);
2146
+ return m('img.materialboxed', Object.assign(Object.assign({}, otherAttrs), { src, alt: alt || '', width,
2147
+ height, className: ['materialboxed', className].filter(Boolean).join(' '), style: Object.assign({ cursor: 'zoom-in', transition: 'opacity 200ms ease' }, style), onclick: (e) => {
2148
+ e.preventDefault();
2149
+ openBox(e.target, attrs);
2150
+ } }));
2151
+ },
2152
+ };
2153
+ };
2154
+
2155
+ /**
2156
+ * CSS-only Modal Panel component - no JavaScript dependencies
2157
+ * Uses modern CSS techniques for modal functionality
2158
+ */
2159
+ const ModalPanel = () => {
2160
+ const state = {
2161
+ isOpen: false,
2162
+ id: '',
2163
+ };
2164
+ let keydownHandler = null;
2165
+ const closeModal = (attrs) => {
2166
+ state.isOpen = false;
2167
+ if (attrs.onToggle)
2168
+ attrs.onToggle(false);
2169
+ if (attrs.onClose)
2170
+ attrs.onClose();
2171
+ // Remove keyboard listener
2172
+ if (keydownHandler) {
2173
+ document.removeEventListener('keydown', keydownHandler);
2174
+ keydownHandler = null;
2175
+ }
2176
+ // Restore body scroll
2177
+ document.body.style.overflow = '';
2178
+ };
2179
+ const openModal = (attrs) => {
2180
+ state.isOpen = true;
2181
+ if (attrs.onToggle)
2182
+ attrs.onToggle(true);
2183
+ // Add keyboard listener
2184
+ keydownHandler = (e) => {
2185
+ if (e.key === 'Escape' && attrs.closeOnEsc !== false && state.isOpen) {
2186
+ closeModal(attrs);
2187
+ }
2188
+ };
2189
+ document.addEventListener('keydown', keydownHandler);
2190
+ // Prevent body scroll
2191
+ document.body.style.overflow = 'hidden';
2192
+ };
2193
+ return {
2194
+ oninit: ({ attrs }) => {
2195
+ state.id = attrs.id;
2196
+ if (attrs.isOpen) {
2197
+ openModal(attrs);
2198
+ }
2199
+ },
2200
+ onremove: () => {
2201
+ // Cleanup on component removal
2202
+ if (keydownHandler) {
2203
+ document.removeEventListener('keydown', keydownHandler);
2204
+ keydownHandler = null;
2205
+ }
2206
+ document.body.style.overflow = '';
2207
+ },
2208
+ view: ({ attrs }) => {
2209
+ // Sync external isOpen prop with internal state - do this in view for immediate response
2210
+ if (attrs.isOpen !== undefined && attrs.isOpen !== state.isOpen) {
2211
+ if (attrs.isOpen) {
2212
+ openModal(attrs);
2213
+ }
2214
+ else {
2215
+ closeModal(attrs);
2216
+ }
2217
+ }
2218
+ const { id, title, description, fixedFooter, bottomSheet, buttons, richContent, className, showCloseButton = true, closeOnBackdropClick = true, } = attrs;
2219
+ const modalClasses = [
2220
+ 'modal',
2221
+ className || '',
2222
+ fixedFooter ? 'modal-fixed-footer' : '',
2223
+ bottomSheet ? 'bottom-sheet' : '',
2224
+ state.isOpen ? 'active' : '',
2225
+ ]
2226
+ .filter(Boolean)
2227
+ .join(' ')
2228
+ .trim();
2229
+ const overlayClasses = ['modal-overlay', state.isOpen ? 'active' : ''].filter(Boolean).join(' ').trim();
2230
+ return m('div', { className: 'modal-container' }, [
2231
+ // Modal overlay
2232
+ m('div', {
2233
+ className: overlayClasses,
2234
+ onclick: closeOnBackdropClick ? () => closeModal(attrs) : undefined,
2235
+ style: {
2236
+ display: state.isOpen ? 'block' : 'none',
2237
+ position: 'fixed',
2238
+ top: '0',
2239
+ left: '0',
2240
+ width: '100%',
2241
+ height: '100%',
2242
+ backgroundColor: 'rgba(0, 0, 0, 0.5)',
2243
+ zIndex: '1002',
2244
+ },
2245
+ }),
2246
+ // Modal content
2247
+ m('div', {
2248
+ id,
2249
+ className: modalClasses,
2250
+ 'aria-hidden': state.isOpen ? 'false' : 'true',
2251
+ role: 'dialog',
2252
+ 'aria-labelledby': `${id}-title`,
2253
+ 'aria-describedby': description ? `${id}-desc` : undefined,
2254
+ style: {
2255
+ display: state.isOpen ? 'block' : 'none',
2256
+ position: 'fixed',
2257
+ top: '50%',
2258
+ left: '50%',
2259
+ transform: 'translate(-50%, -50%)',
2260
+ backgroundColor: '#fff',
2261
+ borderRadius: '4px',
2262
+ maxWidth: '75%',
2263
+ maxHeight: '85%',
2264
+ overflow: 'auto',
2265
+ zIndex: '1003',
2266
+ padding: '0',
2267
+ boxShadow: '0 24px 38px 3px rgba(0,0,0,0.14), 0 9px 46px 8px rgba(0,0,0,0.12), 0 11px 15px -7px rgba(0,0,0,0.20)',
2268
+ },
2269
+ onclick: (e) => e.stopPropagation(), // Prevent backdrop click when clicking inside modal
2270
+ }, [
2271
+ // Close button
2272
+ showCloseButton &&
2273
+ m('button', {
2274
+ className: 'modal-close btn-flat',
2275
+ style: {
2276
+ position: 'absolute',
2277
+ top: '8px',
2278
+ right: '8px',
2279
+ padding: '8px',
2280
+ minWidth: 'auto',
2281
+ lineHeight: 1,
2282
+ },
2283
+ onclick: () => closeModal(attrs),
2284
+ 'aria-label': 'Close modal',
2285
+ }, '×'),
2286
+ // Modal content
2287
+ m('.modal-content', {
2288
+ style: { padding: '24px', paddingTop: showCloseButton ? '48px' : '24px' },
2289
+ }, [
2290
+ m('h4', { id: `${id}-title`, style: { margin: '0 0 20px 0' } }, title),
2291
+ description &&
2292
+ m('div', Object.assign({ id: `${id}-desc` }, (richContent && typeof description === 'string' ? { innerHTML: description } : {})), richContent && typeof description === 'string' ? undefined : description),
2293
+ ]),
2294
+ // Modal footer with buttons
2295
+ buttons &&
2296
+ buttons.length > 0 &&
2297
+ m('.modal-footer', {
2298
+ style: {
2299
+ padding: '4px 6px',
2300
+ borderTop: '1px solid rgba(160,160,160,0.2)',
2301
+ textAlign: 'right',
2302
+ },
2303
+ }, buttons.map((buttonProps) => m(FlatButton, Object.assign(Object.assign({}, buttonProps), { className: `modal-close ${buttonProps.className || ''}`, onclick: (e) => {
2304
+ if (buttonProps.onclick)
2305
+ buttonProps.onclick(e);
2306
+ closeModal(attrs);
2307
+ } })))),
2308
+ ]),
2309
+ ]);
2310
+ },
2311
+ };
2312
+ };
2313
+
2314
+ /** Component to show a check box */
2315
+ const InputCheckbox = () => {
2316
+ return {
2317
+ view: ({ attrs: { className = 'col s12', onchange, label, checked, disabled, description, style, inputId } }) => {
2318
+ const checkboxId = inputId || uniqueId();
2319
+ return m(`p`, { className, style }, m('label', { for: checkboxId }, [
2320
+ m('input[type=checkbox][tabindex=0]', {
2321
+ id: checkboxId,
2322
+ checked,
2323
+ disabled,
2324
+ onclick: onchange
2325
+ ? (e) => {
2326
+ if (e.target && typeof e.target.checked !== 'undefined') {
2327
+ onchange(e.target.checked);
2328
+ }
2329
+ }
2330
+ : undefined,
2331
+ }),
2332
+ label ? (typeof label === 'string' ? m('span', label) : label) : undefined,
2333
+ ]), description && m(HelperText, { className: 'input-checkbox-desc', helperText: description }));
2334
+ },
2335
+ };
2336
+ };
2337
+ /** A list of checkboxes */
2338
+ const Options = () => {
2339
+ const state = {};
2340
+ const isChecked = (id) => state.checkedIds.indexOf(id) >= 0;
2341
+ const selectAll = (options, callback) => {
2342
+ const allIds = options.map((option) => option.id);
2343
+ state.checkedIds = [...allIds];
2344
+ if (callback)
2345
+ callback(allIds);
2346
+ };
2347
+ const selectNone = (callback) => {
2348
+ state.checkedIds = [];
2349
+ if (callback)
2350
+ callback([]);
2351
+ };
2352
+ return {
2353
+ oninit: ({ attrs: { initialValue, checkedId, id } }) => {
2354
+ const iv = checkedId || initialValue;
2355
+ state.checkedId = checkedId;
2356
+ state.checkedIds = iv ? (iv instanceof Array ? [...iv] : [iv]) : [];
2357
+ state.componentId = id || uniqueId();
2358
+ },
2359
+ view: ({ attrs: { label, options, description, className = 'col s12', style, disabled, checkboxClass, newRow, isMandatory, layout = 'vertical', showSelectAll = false, onchange: callback, }, }) => {
2360
+ const onchange = callback
2361
+ ? (propId, checked) => {
2362
+ const checkedIds = state.checkedIds.filter((i) => i !== propId);
2363
+ if (checked) {
2364
+ checkedIds.push(propId);
2365
+ }
2366
+ state.checkedIds = checkedIds;
2367
+ callback(checkedIds);
2368
+ }
2369
+ : undefined;
2370
+ const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim();
2371
+ const optionsContent = layout === 'horizontal'
2372
+ ? m('div.grid-container', options.map((option) => m(InputCheckbox, {
2373
+ disabled: disabled || option.disabled,
2374
+ label: option.label,
2375
+ onchange: onchange ? (v) => onchange(option.id, v) : undefined,
2376
+ className: option.className || checkboxClass,
2377
+ checked: isChecked(option.id),
2378
+ description: option.description,
2379
+ inputId: `${state.componentId}-${option.id}`,
2380
+ })))
2381
+ : options.map((option) => m(InputCheckbox, {
2382
+ disabled: disabled || option.disabled,
2383
+ label: option.label,
2384
+ onchange: onchange ? (v) => onchange(option.id, v) : undefined,
2385
+ className: option.className || checkboxClass,
2386
+ checked: isChecked(option.id),
2387
+ description: option.description,
2388
+ inputId: `${state.componentId}-${option.id}`,
2389
+ }));
2390
+ return m('div', { id: state.componentId, className: cn, style }, [
2391
+ label && m('h5.form-group-label', label + (isMandatory ? ' *' : '')),
2392
+ showSelectAll &&
2393
+ m('div.select-all-controls', { style: 'margin-bottom: 10px;' }, [
2394
+ m('a', {
2395
+ href: '#',
2396
+ onclick: (e) => {
2397
+ e.preventDefault();
2398
+ selectAll(options, callback);
2399
+ },
2400
+ style: 'margin-right: 15px;',
2401
+ }, 'Select All'),
2402
+ m('a', {
2403
+ href: '#',
2404
+ onclick: (e) => {
2405
+ e.preventDefault();
2406
+ selectNone(callback);
2407
+ },
2408
+ }, 'Select None'),
2409
+ ]),
2410
+ description && m(HelperText, { helperText: description }),
2411
+ m('form', { action: '#' }, optionsContent),
2412
+ ]);
2413
+ },
2414
+ };
2415
+ };
2416
+
2417
+ const PaginationItem = () => ({
2418
+ view: ({ attrs: { title, href, active, disabled } }) => m('li', { className: active ? 'active' : disabled ? 'disabled' : 'waves-effect' }, typeof title === 'number' ? m(m.route.Link, { href }, title) : title),
2419
+ });
2420
+ const Pagination = () => {
2421
+ const state = {
2422
+ pagIndex: 0,
2423
+ };
2424
+ return {
2425
+ view: ({ attrs: { items, curPage = 1, size = Math.min(9, items.length) } }) => {
2426
+ const { pagIndex } = state;
2427
+ const startPage = pagIndex * size;
2428
+ const endPage = startPage + size;
2429
+ const canGoBack = pagIndex > 0;
2430
+ const canGoForward = endPage < items.length;
2431
+ const displayedItems = [
2432
+ {
2433
+ title: m('a', {
2434
+ onclick: () => canGoBack && state.pagIndex--,
2435
+ }, m('i.material-icons', 'chevron_left')),
2436
+ disabled: !canGoBack,
2437
+ },
2438
+ ...items.filter((_, i) => startPage <= i && i < endPage),
2439
+ {
2440
+ title: m('a', {
2441
+ onclick: () => canGoForward && state.pagIndex++,
2442
+ }, m('i.material-icons', 'chevron_right')),
2443
+ disabled: !canGoForward,
2444
+ },
2445
+ ];
2446
+ return m('ul.pagination', displayedItems.map((item, i) => m(PaginationItem, Object.assign(Object.assign({ title: startPage + i }, item), { active: startPage + i === curPage }))));
2447
+ },
2448
+ };
2449
+ };
2450
+
2451
+ /**
2452
+ * MaterializeCSS Parallax component with dynamic positioning
2453
+ * Port of the original MaterializeCSS parallax logic
2454
+ */
2455
+ const Parallax = () => {
2456
+ let containerEl = null;
2457
+ let imgEl = null;
2458
+ let scrollThrottle = null;
2459
+ let lastScrollTop = -1;
2460
+ // MaterializeCSS parallax logic - exact port from original source
2461
+ const updateParallax = () => {
2462
+ var _a;
2463
+ if (!containerEl || !imgEl)
2464
+ return;
2465
+ const containerHeight = containerEl.offsetHeight > 0 ? containerEl.offsetHeight : ((_a = containerEl.parentElement) === null || _a === void 0 ? void 0 : _a.offsetHeight) || 1;
2466
+ const imgHeight = imgEl.offsetHeight;
2467
+ const parallaxDist = imgHeight - containerHeight;
2468
+ const bottom = containerEl.offsetTop + containerHeight;
2469
+ const top = containerEl.offsetTop;
2470
+ const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0;
2471
+ const windowHeight = window.innerHeight;
2472
+ const windowBottom = scrollTop + windowHeight;
2473
+ const percentScrolled = (windowBottom - top) / (containerHeight + windowHeight);
2474
+ // MaterializeCSS formula: start at negative parallaxDist/2, move toward positive parallaxDist/2
2475
+ const parallax = Math.round(parallaxDist * percentScrolled - parallaxDist / 2);
2476
+ // Only update if we're in the viewport and scroll position changed
2477
+ if (bottom > scrollTop && top < windowBottom && scrollTop !== lastScrollTop) {
2478
+ // Match MaterializeCSS transform format: translate3d(-50%, Ypx, 0px) with opacity
2479
+ imgEl.style.transform = `translate3d(-50%, ${parallax}px, 0px)`;
2480
+ imgEl.style.opacity = '1';
2481
+ lastScrollTop = scrollTop;
2482
+ }
2483
+ };
2484
+ const handleScroll = () => {
2485
+ if (scrollThrottle)
2486
+ return;
2487
+ scrollThrottle = requestAnimationFrame(() => {
2488
+ updateParallax();
2489
+ scrollThrottle = null;
2490
+ });
2491
+ };
2492
+ const handleResize = () => {
2493
+ updateParallax();
2494
+ };
2495
+ const setupParallax = (containerElement, responsiveThreshold) => {
2496
+ containerEl = containerElement;
2497
+ imgEl = containerElement.querySelector('.parallax img');
2498
+ if (!imgEl)
2499
+ return;
2500
+ // Check if we should enable parallax based on screen size
2501
+ const shouldEnableParallax = window.innerWidth >= responsiveThreshold;
2502
+ if (shouldEnableParallax) {
2503
+ // Set initial MaterializeCSS styles on the image
2504
+ imgEl.style.transform = 'translate3d(-50%, 0px, 0px)';
2505
+ imgEl.style.opacity = '1';
2506
+ // Wait for image to load before calculating parallax
2507
+ if (imgEl.complete) {
2508
+ updateParallax();
2509
+ }
2510
+ else {
2511
+ imgEl.onload = () => updateParallax();
2512
+ }
2513
+ // Add event listeners
2514
+ window.addEventListener('scroll', handleScroll, { passive: true });
2515
+ window.addEventListener('resize', handleResize, { passive: true });
2516
+ // Store cleanup function
2517
+ containerEl._parallaxCleanup = () => {
2518
+ window.removeEventListener('scroll', handleScroll);
2519
+ window.removeEventListener('resize', handleResize);
2520
+ if (scrollThrottle) {
2521
+ cancelAnimationFrame(scrollThrottle);
2522
+ scrollThrottle = null;
2523
+ }
2524
+ };
2525
+ }
2526
+ };
2527
+ const cleanup = () => {
2528
+ if (containerEl && containerEl._parallaxCleanup) {
2529
+ containerEl._parallaxCleanup();
2530
+ }
2531
+ containerEl = null;
2532
+ imgEl = null;
2533
+ if (scrollThrottle) {
2534
+ cancelAnimationFrame(scrollThrottle);
2535
+ scrollThrottle = null;
2536
+ }
2537
+ lastScrollTop = -1;
2538
+ };
2539
+ return {
2540
+ oncreate: ({ dom, attrs }) => {
2541
+ const { responsiveThreshold = 768 } = attrs;
2542
+ setupParallax(dom, responsiveThreshold);
2543
+ },
2544
+ onremove: () => {
2545
+ cleanup();
2546
+ },
2547
+ view: ({ attrs }) => {
2548
+ const { src, alt = '' } = attrs;
2549
+ if (!src)
2550
+ return undefined;
2551
+ return m('.parallax-container', [
2552
+ m('.parallax', [
2553
+ m('img', {
2554
+ src,
2555
+ alt,
2556
+ onerror: (e) => {
2557
+ console.warn('Parallax image failed to load:', src);
2558
+ const img = e.target;
2559
+ img.style.backgroundColor = '#ddd';
2560
+ img.alt = 'Image failed to load';
2561
+ },
2562
+ }),
2563
+ ]),
2564
+ ]);
2565
+ },
2566
+ };
2567
+ };
2568
+
2569
+ const defaultI18n = {
2570
+ cancel: 'Cancel',
2571
+ clear: 'Clear',
2572
+ done: 'Ok',
2573
+ previousMonth: '\u2039',
2574
+ nextMonth: '\u203a',
2575
+ months: [
2576
+ 'January',
2577
+ 'February',
2578
+ 'March',
2579
+ 'April',
2580
+ 'May',
2581
+ 'June',
2582
+ 'July',
2583
+ 'August',
2584
+ 'September',
2585
+ 'October',
2586
+ 'November',
2587
+ 'December',
2588
+ ],
2589
+ monthsShort: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
2590
+ weekdays: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
2591
+ weekdaysShort: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
2592
+ weekdaysAbbrev: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
2593
+ };
2594
+ // Utility functions based on Materialize CSS implementation
2595
+ const isDate = (obj) => {
2596
+ return /Date/.test(Object.prototype.toString.call(obj)) && !isNaN(obj.getTime());
2597
+ };
2598
+ const isWeekend = (date) => {
2599
+ const day = date.getDay();
2600
+ return day === 0 || day === 6;
2601
+ };
2602
+ const setToStartOfDay = (date) => {
2603
+ if (isDate(date))
2604
+ date.setHours(0, 0, 0, 0);
2605
+ };
2606
+ const getDaysInMonth = (year, month) => {
2607
+ return [31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
2608
+ };
2609
+ const isLeapYear = (year) => {
2610
+ return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
2611
+ };
2612
+ const compareDates = (a, b) => {
2613
+ return a.getTime() === b.getTime();
2614
+ };
2615
+ /**
2616
+ * Enhanced DatePicker component based on Materialize CSS datepicker
2617
+ */
2618
+ const DatePicker = () => {
2619
+ let state;
2620
+ const mergeOptions = (attrs) => {
2621
+ // Handle HTML attributes
2622
+ let yearRange = 10;
2623
+ if (attrs.yearrange) {
2624
+ const parts = attrs.yearrange.split(',');
2625
+ if (parts.length === 2) {
2626
+ yearRange = [parseInt(parts[0], 10), parseInt(parts[1], 10)];
2627
+ }
2628
+ }
2629
+ else if (attrs.yearRange) {
2630
+ yearRange = attrs.yearRange;
2631
+ }
2632
+ // Handle format - priority: format attribute > displayFormat > default
2633
+ let finalFormat = 'mmm dd, yyyy';
2634
+ if (attrs.format) {
2635
+ finalFormat = attrs.format;
2636
+ }
2637
+ else if (attrs.displayFormat) {
2638
+ finalFormat = attrs.displayFormat;
2639
+ }
2640
+ const merged = Object.assign({ autoClose: false, format: finalFormat, parse: null, defaultDate: null, setDefaultDate: false, disableWeekends: false, disableDayFn: null, firstDay: 0, minDate: null, maxDate: null, yearRange, showClearBtn: false, i18n: defaultI18n, onSelect: null, onOpen: null, onClose: null }, attrs);
2641
+ // Merge i18n properly
2642
+ merged.i18n = Object.assign(Object.assign({}, defaultI18n), attrs.i18n);
2643
+ return merged;
2644
+ };
2645
+ const toString = (date, format) => {
2646
+ if (!date || !isDate(date)) {
2647
+ return '';
2648
+ }
2649
+ // Split format into tokens - match longer patterns first
2650
+ const formatTokens = /(dddd|ddd|dd|d|mmmm|mmm|mm|m|yyyy|yy)/g;
2651
+ let result = format;
2652
+ // Replace all format tokens with actual values
2653
+ result = result.replace(formatTokens, (match) => {
2654
+ if (state.formats[match]) {
2655
+ return String(state.formats[match]());
2656
+ }
2657
+ return match;
2658
+ });
2659
+ return result;
2660
+ };
2661
+ const setDate = (date, preventOnSelect = false, options) => {
2662
+ if (!date) {
2663
+ state.date = null;
2664
+ return;
2665
+ }
2666
+ if (typeof date === 'string') {
2667
+ date = new Date(Date.parse(date));
2668
+ }
2669
+ if (!isDate(date)) {
2670
+ return;
2671
+ }
2672
+ const min = options.minDate;
2673
+ const max = options.maxDate;
2674
+ if (isDate(min) && date < min) {
2675
+ date = min;
2676
+ }
2677
+ else if (isDate(max) && date > max) {
2678
+ date = max;
2679
+ }
2680
+ state.date = new Date(date.getTime());
2681
+ setToStartOfDay(state.date);
2682
+ gotoDate(state.date);
2683
+ if (!preventOnSelect && options.onSelect) {
2684
+ options.onSelect(state.date);
2685
+ }
2686
+ };
2687
+ const gotoDate = (date) => {
2688
+ if (!isDate(date)) {
2689
+ return;
2690
+ }
2691
+ state.calendars = [
2692
+ {
2693
+ month: date.getMonth(),
2694
+ year: date.getFullYear(),
2695
+ },
2696
+ ];
2697
+ };
2698
+ const nextMonth = () => {
2699
+ state.calendars[0].month++;
2700
+ adjustCalendars();
2701
+ };
2702
+ const prevMonth = () => {
2703
+ state.calendars[0].month--;
2704
+ adjustCalendars();
2705
+ };
2706
+ const adjustCalendars = () => {
2707
+ state.calendars[0] = adjustCalendar(state.calendars[0]);
2708
+ };
2709
+ const adjustCalendar = (calendar) => {
2710
+ if (calendar.month < 0) {
2711
+ calendar.year -= Math.ceil(Math.abs(calendar.month) / 12);
2712
+ calendar.month += 12;
2713
+ }
2714
+ if (calendar.month > 11) {
2715
+ calendar.year += Math.floor(Math.abs(calendar.month) / 12);
2716
+ calendar.month -= 12;
2717
+ }
2718
+ return calendar;
2719
+ };
2720
+ const renderDay = (opts, options) => {
2721
+ const arr = [];
2722
+ let ariaSelected = 'false';
2723
+ if (opts.isEmpty) {
2724
+ {
2725
+ return m('td.is-empty');
2726
+ }
2727
+ }
2728
+ if (opts.isDisabled) {
2729
+ arr.push('is-disabled');
2730
+ }
2731
+ if (opts.isToday) {
2732
+ arr.push('is-today');
2733
+ }
2734
+ if (opts.isSelected) {
2735
+ arr.push('is-selected');
2736
+ ariaSelected = 'true';
2737
+ }
2738
+ return m('td', {
2739
+ 'data-day': opts.day,
2740
+ class: arr.join(' '),
2741
+ 'aria-selected': ariaSelected,
2742
+ }, [
2743
+ m('button.datepicker-day-button', {
2744
+ type: 'button',
2745
+ 'data-year': opts.year,
2746
+ 'data-month': opts.month,
2747
+ 'data-day': opts.day,
2748
+ onclick: (e) => {
2749
+ const target = e.target;
2750
+ if (!opts.isDisabled) {
2751
+ const year = parseInt(target.getAttribute('data-year') || '0', 10);
2752
+ const month = parseInt(target.getAttribute('data-month') || '0', 10);
2753
+ const day = parseInt(target.getAttribute('data-day') || '0', 10);
2754
+ const selectedDate = new Date(year, month, day);
2755
+ setDate(selectedDate, false, options);
2756
+ if (options.autoClose) {
2757
+ state.isOpen = false;
2758
+ }
2759
+ }
2760
+ },
2761
+ }, opts.day),
2762
+ ]);
2763
+ };
2764
+ const renderCalendar = (year, month, options, randId) => {
2765
+ const now = new Date();
2766
+ const days = getDaysInMonth(year, month);
2767
+ let before = new Date(year, month, 1).getDay();
2768
+ const data = [];
2769
+ let row = [];
2770
+ setToStartOfDay(now);
2771
+ if (options.firstDay > 0) {
2772
+ before -= options.firstDay;
2773
+ if (before < 0) {
2774
+ before += 7;
2775
+ }
2776
+ }
2777
+ const previousMonth = month === 0 ? 11 : month - 1;
2778
+ const nextMonth = month === 11 ? 0 : month + 1;
2779
+ const yearOfPreviousMonth = month === 0 ? year - 1 : year;
2780
+ const yearOfNextMonth = month === 11 ? year + 1 : year;
2781
+ const daysInPreviousMonth = getDaysInMonth(yearOfPreviousMonth, previousMonth);
2782
+ let cells = days + before;
2783
+ let after = cells;
2784
+ while (after > 7) {
2785
+ after -= 7;
2786
+ }
2787
+ cells += 7 - after;
2788
+ for (let i = 0, r = 0; i < cells; i++) {
2789
+ const day = new Date(year, month, 1 + (i - before));
2790
+ const isSelected = isDate(state.date) ? compareDates(day, state.date) : false;
2791
+ const isToday = compareDates(day, now);
2792
+ const isEmpty = i < before || i >= days + before;
2793
+ let dayNumber = 1 + (i - before);
2794
+ let monthNumber = month;
2795
+ let yearNumber = year;
2796
+ if (isEmpty) {
2797
+ if (i < before) {
2798
+ dayNumber = daysInPreviousMonth + dayNumber;
2799
+ monthNumber = previousMonth;
2800
+ yearNumber = yearOfPreviousMonth;
2801
+ }
2802
+ else {
2803
+ dayNumber = dayNumber - days;
2804
+ monthNumber = nextMonth;
2805
+ yearNumber = yearOfNextMonth;
2806
+ }
2807
+ }
2808
+ const isDisabled = (options.minDate && day < options.minDate) ||
2809
+ (options.maxDate && day > options.maxDate) ||
2810
+ (options.disableWeekends && isWeekend(day)) ||
2811
+ (options.disableDayFn && options.disableDayFn(day));
2812
+ const dayConfig = {
2813
+ day: dayNumber,
2814
+ month: monthNumber,
2815
+ year: yearNumber,
2816
+ isSelected: isSelected,
2817
+ isToday: isToday,
2818
+ isDisabled: isDisabled,
2819
+ isEmpty: isEmpty};
2820
+ row.push(renderDay(dayConfig, options));
2821
+ if (++r === 7) {
2822
+ data.push(m('tr.datepicker-row', row));
2823
+ row = [];
2824
+ r = 0;
2825
+ }
2826
+ }
2827
+ const weekdayHeaders = [];
2828
+ for (let i = 0; i < 7; i++) {
2829
+ let day = i + options.firstDay;
2830
+ while (day >= 7) {
2831
+ day -= 7;
2832
+ }
2833
+ weekdayHeaders.push(m('th', { scope: 'col' }, [
2834
+ m('abbr', { title: options.i18n.weekdays[day] }, options.i18n.weekdaysAbbrev[day]),
2835
+ ]));
2836
+ }
2837
+ return m('.datepicker-table-wrapper', [
2838
+ m('table.datepicker-table', {
2839
+ cellpadding: '0',
2840
+ cellspacing: '0',
2841
+ role: 'grid',
2842
+ 'aria-labelledby': 'datepicker-controls',
2843
+ }, [m('thead', [m('tr', weekdayHeaders)]), m('tbody', data)]),
2844
+ ]);
2845
+ };
2846
+ const renderDateDisplay = (options) => {
2847
+ const displayDate = isDate(state.date) ? state.date : new Date();
2848
+ const day = options.i18n.weekdaysShort[displayDate.getDay()];
2849
+ const month = options.i18n.monthsShort[displayDate.getMonth()];
2850
+ const date = displayDate.getDate();
2851
+ return m('.datepicker-date-display', [
2852
+ m('span.year-text', displayDate.getFullYear()),
2853
+ m('span.date-text', `${day}, ${month} ${date}`),
2854
+ ]);
2855
+ };
2856
+ const renderControls = (options, randId) => {
2857
+ const calendar = state.calendars[0];
2858
+ const year = calendar.year;
2859
+ const month = calendar.month;
2860
+ // Month options
2861
+ const monthOptions = [];
2862
+ for (let i = 0; i < 12; i++) {
2863
+ monthOptions.push(m('option', {
2864
+ value: i,
2865
+ selected: i === month ? 'selected' : undefined,
2866
+ }, options.i18n.months[i]));
2867
+ }
2868
+ // Year options
2869
+ const yearOptions = [];
2870
+ let yearStart, yearEnd;
2871
+ if (Array.isArray(options.yearRange)) {
2872
+ yearStart = options.yearRange[0];
2873
+ yearEnd = options.yearRange[1];
2874
+ }
2875
+ else {
2876
+ yearStart = year - options.yearRange;
2877
+ yearEnd = year + options.yearRange;
2878
+ }
2879
+ for (let i = yearStart; i <= yearEnd; i++) {
2880
+ yearOptions.push(m('option', {
2881
+ value: i,
2882
+ selected: i === year ? 'selected' : undefined,
2883
+ }, i));
2884
+ }
2885
+ return m('.datepicker-controls', {
2886
+ id: randId,
2887
+ role: 'heading',
2888
+ 'aria-live': 'assertive',
2889
+ }, [
2890
+ m('button.month-prev', {
2891
+ type: 'button',
2892
+ onclick: (e) => {
2893
+ e.preventDefault();
2894
+ prevMonth();
2895
+ },
2896
+ }, m('svg', { fill: '#000000', height: '24', viewBox: '0 0 24 24', width: '24', xmlns: 'http://www.w3.org/2000/svg' }, [
2897
+ m('path', { d: 'M15.41 16.09l-4.58-4.59 4.58-4.59L14 5.5l-6 6 6 6z' }),
2898
+ m('path', { d: 'M0-.5h24v24H0z', fill: 'none' }),
2899
+ ])),
2900
+ m('.selects-container', [
2901
+ // Month select wrapper
2902
+ m('.select-wrapper.select-month', [
2903
+ m('input.select-dropdown.dropdown-trigger', {
2904
+ type: 'text',
2905
+ readonly: true,
2906
+ value: options.i18n.months[month],
2907
+ onclick: (e) => {
2908
+ e.preventDefault();
2909
+ state.monthDropdownOpen = !state.monthDropdownOpen;
2910
+ state.yearDropdownOpen = false; // Close year dropdown
2911
+ },
2912
+ }),
2913
+ // Custom dropdown menu
2914
+ state.monthDropdownOpen &&
2915
+ m('.dropdown-content', options.i18n.months.map((monthName, index) => m('.dropdown-item', {
2916
+ key: index,
2917
+ class: index === month ? 'selected' : '',
2918
+ onclick: (e) => {
2919
+ e.stopPropagation();
2920
+ gotoMonth(index);
2921
+ state.monthDropdownOpen = false;
2922
+ },
2923
+ }, monthName))),
2924
+ ]),
2925
+ // Year select wrapper
2926
+ m('.select-wrapper.select-year', [
2927
+ m('input.select-dropdown.dropdown-trigger', {
2928
+ type: 'text',
2929
+ readonly: true,
2930
+ value: year.toString(),
2931
+ onclick: (e) => {
2932
+ e.preventDefault();
2933
+ state.yearDropdownOpen = !state.yearDropdownOpen;
2934
+ state.monthDropdownOpen = false; // Close month dropdown
2935
+ },
2936
+ }),
2937
+ // Custom dropdown menu
2938
+ state.yearDropdownOpen &&
2939
+ m('.dropdown-content', range(yearStart, yearEnd).map((i) => m('.dropdown-item', {
2940
+ key: i,
2941
+ class: i === year ? 'selected' : '',
2942
+ onclick: (e) => {
2943
+ e.stopPropagation();
2944
+ gotoYear(i);
2945
+ state.yearDropdownOpen = false;
2946
+ },
2947
+ }, i))),
2948
+ ]),
2949
+ ]),
2950
+ m('button.month-next', {
2951
+ type: 'button',
2952
+ onclick: (e) => {
2953
+ e.preventDefault();
2954
+ nextMonth();
2955
+ },
2956
+ }, m('svg', { fill: '#000000', height: '24', viewBox: '0 0 24 24', width: '24', xmlns: 'http://www.w3.org/2000/svg' }, [
2957
+ m('path', { d: 'M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z' }),
2958
+ m('path', { d: 'M0-.25h24v24H0z', fill: 'none' }),
2959
+ ])),
2960
+ ]);
2961
+ };
2962
+ const gotoMonth = (month) => {
2963
+ if (!isNaN(month)) {
2964
+ state.calendars[0].month = month;
2965
+ adjustCalendars();
2966
+ }
2967
+ };
2968
+ const gotoYear = (year) => {
2969
+ if (!isNaN(year)) {
2970
+ state.calendars[0].year = year;
2971
+ adjustCalendars();
2972
+ }
2973
+ };
2974
+ const handleDocumentClick = (e) => {
2975
+ const target = e.target;
2976
+ if (!target.closest('.select-wrapper') && !target.closest('.dropdown-content')) {
2977
+ state.monthDropdownOpen = false;
2978
+ state.yearDropdownOpen = false;
2979
+ }
2980
+ };
2981
+ return {
2982
+ oninit: (vnode) => {
2983
+ const attrs = vnode.attrs;
2984
+ const options = mergeOptions(attrs);
2985
+ state = {
2986
+ id: uniqueId(),
2987
+ isOpen: false,
2988
+ date: null,
2989
+ calendars: [{ month: 0, year: 0 }],
2990
+ monthDropdownOpen: false,
2991
+ yearDropdownOpen: false,
2992
+ formats: {
2993
+ d: () => { var _a; return ((_a = state.date) === null || _a === void 0 ? void 0 : _a.getDate()) || 0; },
2994
+ dd: () => {
2995
+ var _a;
2996
+ const d = ((_a = state.date) === null || _a === void 0 ? void 0 : _a.getDate()) || 0;
2997
+ return (d < 10 ? '0' : '') + d;
2998
+ },
2999
+ ddd: () => { var _a; return options.i18n.weekdaysShort[((_a = state.date) === null || _a === void 0 ? void 0 : _a.getDay()) || 0]; },
3000
+ dddd: () => { var _a; return options.i18n.weekdays[((_a = state.date) === null || _a === void 0 ? void 0 : _a.getDay()) || 0]; },
3001
+ m: () => { var _a; return (((_a = state.date) === null || _a === void 0 ? void 0 : _a.getMonth()) || 0) + 1; },
3002
+ mm: () => {
3003
+ var _a;
3004
+ const m = (((_a = state.date) === null || _a === void 0 ? void 0 : _a.getMonth()) || 0) + 1;
3005
+ return (m < 10 ? '0' : '') + m;
3006
+ },
3007
+ mmm: () => { var _a; return options.i18n.monthsShort[((_a = state.date) === null || _a === void 0 ? void 0 : _a.getMonth()) || 0]; },
3008
+ mmmm: () => { var _a; return options.i18n.months[((_a = state.date) === null || _a === void 0 ? void 0 : _a.getMonth()) || 0]; },
3009
+ yy: () => { var _a; return ('' + (((_a = state.date) === null || _a === void 0 ? void 0 : _a.getFullYear()) || 0)).slice(2); },
3010
+ yyyy: () => { var _a; return ((_a = state.date) === null || _a === void 0 ? void 0 : _a.getFullYear()) || 0; },
3011
+ },
3012
+ };
3013
+ // Initialize date
3014
+ let defaultDate = attrs.defaultDate;
3015
+ if (!defaultDate && attrs.initialValue) {
3016
+ defaultDate = new Date(attrs.initialValue);
3017
+ }
3018
+ if (isDate(defaultDate)) {
3019
+ // Always set the date if we have initialValue or defaultDate
3020
+ setDate(defaultDate, true, options);
3021
+ }
3022
+ else {
3023
+ gotoDate(new Date());
3024
+ }
3025
+ // Add document click listener to close dropdowns
3026
+ document.addEventListener('click', handleDocumentClick);
3027
+ },
3028
+ onremove: () => {
3029
+ // Clean up event listener
3030
+ document.removeEventListener('click', handleDocumentClick);
3031
+ },
3032
+ view: (vnode) => {
3033
+ const attrs = vnode.attrs;
3034
+ const options = mergeOptions(attrs);
3035
+ const { id = state.id, label, dateLabel, placeholder, disabled, required, className, style, helperText, iconName, newRow, } = attrs;
3036
+ // Use dateLabel if label is not provided (backward compatibility)
3037
+ const finalLabel = label || dateLabel;
3038
+ const finalClassName = newRow ? `${className || ''} clear` : className;
3039
+ return m('.input-field', {
3040
+ className: finalClassName,
3041
+ style,
3042
+ }, [
3043
+ // Icon prefix
3044
+ iconName && m('i.material-icons.prefix', iconName),
3045
+ // Main date input
3046
+ m('input.datepicker', {
3047
+ id,
3048
+ type: 'text',
3049
+ value: toString(state.date, options.format),
3050
+ placeholder,
3051
+ disabled,
3052
+ required,
3053
+ readonly: true,
3054
+ format: attrs.format,
3055
+ yearrange: attrs.yearrange,
3056
+ tabindex: '0',
3057
+ onclick: () => {
3058
+ if (!disabled) {
3059
+ state.isOpen = true;
3060
+ if (options.onOpen)
3061
+ options.onOpen();
3062
+ }
3063
+ },
3064
+ }),
3065
+ // Label
3066
+ finalLabel &&
3067
+ m('label', {
3068
+ for: id,
3069
+ class: state.date || placeholder ? 'active' : '',
3070
+ }, finalLabel),
3071
+ // Helper text
3072
+ helperText && m('span.helper-text', helperText),
3073
+ // Modal datepicker
3074
+ state.isOpen && [
3075
+ m('.modal.datepicker-modal.open', {
3076
+ id: `modal-${state.id}`,
3077
+ tabindex: 0,
3078
+ style: {
3079
+ zIndex: 1003,
3080
+ display: 'block',
3081
+ opacity: 1,
3082
+ top: '10%',
3083
+ transform: 'scaleX(1) scaleY(1)',
3084
+ },
3085
+ }, [
3086
+ m('.modal-content.datepicker-container', {
3087
+ onclick: (e) => {
3088
+ // Close dropdowns when clicking anywhere in the modal content
3089
+ const target = e.target;
3090
+ if (!target.closest('.select-wrapper') && !target.closest('.dropdown-content')) {
3091
+ state.monthDropdownOpen = false;
3092
+ state.yearDropdownOpen = false;
3093
+ }
3094
+ },
3095
+ }, [
3096
+ renderDateDisplay(options),
3097
+ m('.datepicker-calendar-container', [
3098
+ m('.datepicker-calendar', [
3099
+ renderControls(options, `datepicker-title-${Math.random().toString(36).slice(2)}`),
3100
+ renderCalendar(state.calendars[0].year, state.calendars[0].month, options),
3101
+ ]),
3102
+ m('.datepicker-footer', [
3103
+ options.showClearBtn &&
3104
+ m('button.btn-flat.datepicker-clear.waves-effect', {
3105
+ type: 'button',
3106
+ style: '',
3107
+ onclick: () => {
3108
+ setDate(null, false, options);
3109
+ state.isOpen = false;
3110
+ },
3111
+ }, options.i18n.clear),
3112
+ m('.confirmation-btns', [
3113
+ m('button.btn-flat.datepicker-cancel.waves-effect', {
3114
+ type: 'button',
3115
+ onclick: () => {
3116
+ state.isOpen = false;
3117
+ if (options.onClose)
3118
+ options.onClose();
3119
+ },
3120
+ }, options.i18n.cancel),
3121
+ m('button.btn-flat.datepicker-done.waves-effect', {
3122
+ type: 'button',
3123
+ onclick: () => {
3124
+ if (attrs.onchange && state.date) {
3125
+ attrs.onchange(state.date.toISOString().split('T')[0]);
3126
+ }
3127
+ state.isOpen = false;
3128
+ if (options.onClose)
3129
+ options.onClose();
3130
+ },
3131
+ }, options.i18n.done),
3132
+ ]),
3133
+ ]),
3134
+ ]),
3135
+ ]),
3136
+ ]),
3137
+ // Modal overlay
3138
+ m('.modal-overlay', {
3139
+ style: {
3140
+ zIndex: 1002,
3141
+ display: 'block',
3142
+ opacity: 0.5,
3143
+ },
3144
+ onclick: () => {
3145
+ state.isOpen = false;
3146
+ if (options.onClose)
3147
+ options.onClose();
3148
+ },
3149
+ }),
3150
+ ],
3151
+ ]);
3152
+ },
3153
+ };
3154
+ };
3155
+ /**
3156
+ * Enhanced TimePicker component with i18n support and improved functionality.
3157
+ *
3158
+ * Usage:
3159
+ * - Use `initialValue` to set the current/initial time value (24h format: "HH:MM")
3160
+ * - Use `defaultTime` only if you need a fallback when the field is cleared
3161
+ * - The component accepts and outputs 24-hour format strings ("HH:MM")
3162
+ * - Display format (12h/24h) is controlled by the `twelveHour` property
3163
+ */
3164
+ const TimePicker = () => {
3165
+ const state = {
3166
+ id: uniqueId(),
3167
+ isOpen: false,
3168
+ hours: 12,
3169
+ minutes: 0,
3170
+ ampm: 'AM',
3171
+ use12Hour: false,
3172
+ time: ''};
3173
+ const parseTime = (timeString) => {
3174
+ if (!timeString)
3175
+ return { hours: 12, minutes: 0, ampm: 'AM' };
3176
+ const [time, ampm] = timeString.split(' ');
3177
+ const [hoursStr, minutesStr] = time.split(':');
3178
+ let hours = parseInt(hoursStr, 10) || 0;
3179
+ const minutes = parseInt(minutesStr, 10) || 0;
3180
+ if (ampm) {
3181
+ // 12-hour format
3182
+ if (ampm.toUpperCase() === 'PM' && hours !== 12)
3183
+ hours += 12;
3184
+ if (ampm.toUpperCase() === 'AM' && hours === 12)
3185
+ hours = 0;
3186
+ return { hours, minutes, ampm: ampm.toUpperCase() };
3187
+ }
3188
+ else {
3189
+ // 24-hour format
3190
+ const displayAmpm = hours >= 12 ? 'PM' : 'AM';
3191
+ return { hours, minutes, ampm: displayAmpm };
3192
+ }
3193
+ };
3194
+ const formatTime = (hours, minutes, use12Hour) => {
3195
+ if (use12Hour) {
3196
+ const displayHours = hours === 0 ? 12 : hours > 12 ? hours - 12 : hours;
3197
+ const ampm = hours >= 12 ? 'PM' : 'AM';
3198
+ return `${displayHours}:${minutes.toString().padStart(2, '0')} ${ampm}`;
3199
+ }
3200
+ else {
3201
+ return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
3202
+ }
3203
+ };
3204
+ const setTime = (timeString, attrs) => {
3205
+ const parsed = parseTime(timeString);
3206
+ state.hours = parsed.hours;
3207
+ state.minutes = parsed.minutes;
3208
+ state.ampm = parsed.ampm;
3209
+ state.time = timeString;
3210
+ if (attrs.onchange) {
3211
+ // Always output 24-hour format for consistency
3212
+ const output24h = `${state.hours.toString().padStart(2, '0')}:${state.minutes.toString().padStart(2, '0')}`;
3213
+ attrs.onchange(output24h);
3214
+ }
3215
+ if (attrs.oninput) {
3216
+ attrs.oninput(timeString);
3217
+ }
3218
+ if (attrs.onSelect) {
3219
+ attrs.onSelect(state.hours, state.minutes);
3220
+ }
3221
+ };
3222
+ return {
3223
+ oninit: (vnode) => {
3224
+ const { initialValue, defaultTime, twelveHour = false } = vnode.attrs;
3225
+ state.use12Hour = twelveHour;
3226
+ const timeValue = initialValue || defaultTime || '';
3227
+ if (timeValue) {
3228
+ const parsed = parseTime(timeValue);
3229
+ state.hours = parsed.hours;
3230
+ state.minutes = parsed.minutes;
3231
+ state.ampm = parsed.ampm;
3232
+ state.time = formatTime(state.hours, state.minutes, state.use12Hour);
3233
+ }
3234
+ },
3235
+ view: (vnode) => {
3236
+ const attrs = vnode.attrs;
3237
+ const { id = state.id, label = 'Time', placeholder = 'Select time', disabled, readonly, required, className, style, helperText, iconName = 'access_time', newRow, twelveHour = false, timeLabel = 'Time', nowLabel = 'Now', clearLabel = 'Clear', closeLabel = 'Close', amLabel = 'AM', pmLabel = 'PM', showClearBtn = false, showNowBtn = false, useModal = true, } = attrs;
3238
+ state.use12Hour = twelveHour;
3239
+ const finalClassName = newRow ? `${className || ''} clear` : className;
3240
+ const displayValue = state.time ? formatTime(state.hours, state.minutes, state.use12Hour) : '';
3241
+ return m('.input-field', {
3242
+ className: finalClassName,
3243
+ style,
3244
+ }, [
3245
+ // Icon prefix
3246
+ iconName && m('i.material-icons.prefix', iconName),
3247
+ // Time input field
3248
+ m('input.timepicker', {
3249
+ id,
3250
+ type: 'text',
3251
+ value: displayValue,
3252
+ placeholder,
3253
+ disabled,
3254
+ readonly,
3255
+ required,
3256
+ onclick: () => {
3257
+ if (!disabled && !readonly && useModal) {
3258
+ state.isOpen = true;
3259
+ if (attrs.onOpen)
3260
+ attrs.onOpen();
3261
+ }
3262
+ },
3263
+ }),
3264
+ // Label
3265
+ label &&
3266
+ m('label', {
3267
+ for: id,
3268
+ class: displayValue || placeholder ? 'active' : '',
3269
+ }, label),
3270
+ // Helper text
3271
+ helperText && m('span.helper-text', helperText),
3272
+ // Time picker modal (simplified version)
3273
+ useModal &&
3274
+ state.isOpen &&
3275
+ m('.timepicker-modal', {
3276
+ style: {
3277
+ position: 'fixed',
3278
+ top: '50%',
3279
+ left: '50%',
3280
+ transform: 'translate(-50%, -50%)',
3281
+ backgroundColor: 'white',
3282
+ padding: '20px',
3283
+ borderRadius: '4px',
3284
+ boxShadow: '0 4px 20px rgba(0,0,0,0.3)',
3285
+ zIndex: 1000,
3286
+ },
3287
+ }, [
3288
+ m('h4', timeLabel),
3289
+ m('.time-inputs', {
3290
+ style: { display: 'flex', alignItems: 'center', gap: '10px', margin: '20px 0' },
3291
+ }, [
3292
+ // Hours input
3293
+ m('input', {
3294
+ type: 'number',
3295
+ min: state.use12Hour ? 1 : 0,
3296
+ max: state.use12Hour ? 12 : 23,
3297
+ value: state.use12Hour
3298
+ ? state.hours === 0
3299
+ ? 12
3300
+ : state.hours > 12
3301
+ ? state.hours - 12
3302
+ : state.hours
3303
+ : state.hours,
3304
+ style: { width: '60px', textAlign: 'center', padding: '8px' },
3305
+ onchange: (e) => {
3306
+ const target = e.target;
3307
+ let hours = parseInt(target.value) || 0;
3308
+ if (state.use12Hour) {
3309
+ if (state.ampm === 'PM' && hours !== 12)
3310
+ hours += 12;
3311
+ if (state.ampm === 'AM' && hours === 12)
3312
+ hours = 0;
3313
+ }
3314
+ state.hours = hours;
3315
+ state.time = formatTime(state.hours, state.minutes, state.use12Hour);
3316
+ },
3317
+ }),
3318
+ m('span', ':'),
3319
+ // Minutes input
3320
+ m('input', {
3321
+ type: 'number',
3322
+ min: 0,
3323
+ max: 59,
3324
+ value: state.minutes,
3325
+ style: { width: '60px', textAlign: 'center', padding: '8px' },
3326
+ onchange: (e) => {
3327
+ const target = e.target;
3328
+ state.minutes = parseInt(target.value) || 0;
3329
+ state.time = formatTime(state.hours, state.minutes, state.use12Hour);
3330
+ },
3331
+ }),
3332
+ // AM/PM toggle for 12-hour format
3333
+ state.use12Hour &&
3334
+ m('select', {
3335
+ value: state.ampm,
3336
+ style: { padding: '8px' },
3337
+ onchange: (e) => {
3338
+ const target = e.target;
3339
+ const oldAmpm = state.ampm;
3340
+ state.ampm = target.value;
3341
+ // Adjust hours when switching AM/PM
3342
+ if (oldAmpm !== state.ampm) {
3343
+ if (state.ampm === 'PM' && state.hours < 12) {
3344
+ state.hours += 12;
3345
+ }
3346
+ else if (state.ampm === 'AM' && state.hours >= 12) {
3347
+ state.hours -= 12;
3348
+ }
3349
+ }
3350
+ state.time = formatTime(state.hours, state.minutes, state.use12Hour);
3351
+ },
3352
+ }, [m('option', { value: 'AM' }, amLabel), m('option', { value: 'PM' }, pmLabel)]),
3353
+ ]),
3354
+ // Action buttons
3355
+ m('.timepicker-actions', {
3356
+ style: { display: 'flex', justifyContent: 'flex-end', gap: '10px' },
3357
+ }, [
3358
+ showClearBtn &&
3359
+ m('button.btn-flat', {
3360
+ onclick: () => {
3361
+ setTime('', attrs);
3362
+ state.isOpen = false;
3363
+ },
3364
+ }, clearLabel),
3365
+ showNowBtn &&
3366
+ m('button.btn-flat', {
3367
+ onclick: () => {
3368
+ const now = new Date();
3369
+ state.hours = now.getHours();
3370
+ state.minutes = now.getMinutes();
3371
+ state.ampm = state.hours >= 12 ? 'PM' : 'AM';
3372
+ state.time = formatTime(state.hours, state.minutes, state.use12Hour);
3373
+ },
3374
+ }, nowLabel),
3375
+ m('button.btn-flat', {
3376
+ onclick: () => {
3377
+ state.isOpen = false;
3378
+ if (attrs.onClose)
3379
+ attrs.onClose();
3380
+ },
3381
+ }, closeLabel),
3382
+ m('button.btn-flat', {
3383
+ onclick: () => {
3384
+ setTime(state.time, attrs);
3385
+ state.isOpen = false;
3386
+ if (attrs.onClose)
3387
+ attrs.onClose();
3388
+ },
3389
+ }, 'OK'),
3390
+ ]),
3391
+ ]),
3392
+ // Modal backdrop
3393
+ useModal &&
3394
+ state.isOpen &&
3395
+ m('.modal-backdrop', {
3396
+ style: {
3397
+ position: 'fixed',
3398
+ top: 0,
3399
+ left: 0,
3400
+ width: '100%',
3401
+ height: '100%',
3402
+ backgroundColor: 'rgba(0,0,0,0.5)',
3403
+ zIndex: 999,
3404
+ },
3405
+ onclick: () => {
3406
+ state.isOpen = false;
3407
+ if (attrs.onClose)
3408
+ attrs.onClose();
3409
+ },
3410
+ }),
3411
+ ]);
3412
+ },
3413
+ };
3414
+ };
3415
+
3416
+ const RadioButton = () => ({
3417
+ view: ({ attrs: { id, groupId, label, onchange, className = 'col s12', checked, disabled, inputId } }) => {
3418
+ const radioId = inputId || `${groupId}-${id}`;
3419
+ return m('p', { className }, m('label', { for: radioId }, [
3420
+ m('input[type=radio][tabindex=0]', {
3421
+ id: radioId,
3422
+ name: groupId,
3423
+ disabled,
3424
+ checked,
3425
+ onclick: onchange ? () => onchange(id) : undefined,
3426
+ }),
3427
+ m('span', m.trust(label)),
3428
+ ]));
3429
+ },
3430
+ });
3431
+ /** Component to show a list of radio buttons, from which you can choose one. */
3432
+ // export const RadioButtons: FactoryComponent<IRadioButtons<T>> = () => {
3433
+ const RadioButtons = () => {
3434
+ const state = { groupId: uniqueId() };
3435
+ return {
3436
+ oninit: ({ attrs: { checkedId, initialValue, id } }) => {
3437
+ state.oldCheckedId = checkedId;
3438
+ state.checkedId = checkedId || initialValue;
3439
+ state.componentId = id || uniqueId();
3440
+ },
3441
+ view: ({ attrs: { checkedId: cid, newRow, className = 'col s12', label = '', disabled, description, options, isMandatory, checkboxClass, layout = 'vertical', onchange: callback, }, }) => {
3442
+ if (state.oldCheckedId !== cid) {
3443
+ state.oldCheckedId = state.checkedId = cid;
3444
+ }
3445
+ const { groupId, checkedId, componentId } = state;
3446
+ const onchange = (propId) => {
3447
+ state.checkedId = propId;
3448
+ if (callback) {
3449
+ callback(propId);
3450
+ }
3451
+ };
3452
+ const cn = [newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim();
3453
+ const optionsContent = layout === 'horizontal'
3454
+ ? m('div.grid-container', options.map((r) => m(RadioButton, Object.assign(Object.assign({}, r), { onchange,
3455
+ groupId, disabled: disabled || r.disabled, className: checkboxClass, checked: r.id === checkedId, inputId: `${componentId}-${r.id}` }))))
3456
+ : options.map((r) => m(RadioButton, Object.assign(Object.assign({}, r), { onchange,
3457
+ groupId, disabled: disabled || r.disabled, className: checkboxClass, checked: r.id === checkedId, inputId: `${componentId}-${r.id}` })));
3458
+ return m('div', { id: componentId, className: cn }, [
3459
+ label && m('h5.form-group-label', label + (isMandatory ? ' *' : '')),
3460
+ description && m('p.helper-text', m.trust(description)),
3461
+ m('form', { action: '#' }, optionsContent),
3462
+ ]);
3463
+ },
3464
+ };
3465
+ };
3466
+
3467
+ /** Select component */
3468
+ const Select = () => {
3469
+ const state = {
3470
+ id: '',
3471
+ isOpen: false,
3472
+ selectedIds: [],
3473
+ focusedIndex: -1,
3474
+ inputRef: null,
3475
+ dropdownRef: null,
3476
+ };
3477
+ const isSelected = (id, selectedIds) => {
3478
+ return selectedIds.some((selectedId) => selectedId === id);
3479
+ };
3480
+ const toggleOption = (id, multiple, attrs) => {
3481
+ if (multiple) {
3482
+ const newIds = state.selectedIds.includes(id)
3483
+ ? // isSelected(id, state.selectedIds)
3484
+ state.selectedIds.filter((selectedId) => selectedId !== id)
3485
+ : [...state.selectedIds, id];
3486
+ state.selectedIds = newIds;
3487
+ attrs.onchange(newIds);
3488
+ console.log(newIds);
3489
+ // Keep dropdown open for multiple select
3490
+ }
3491
+ else {
3492
+ state.selectedIds = [id];
3493
+ // Close dropdown for single select
3494
+ state.isOpen = false;
3495
+ attrs.onchange([id]);
3496
+ }
3497
+ };
3498
+ const handleKeyDown = (e, attrs) => {
3499
+ const { options } = attrs;
3500
+ const selectableOptions = options.filter((opt) => !opt.disabled);
3501
+ switch (e.key) {
3502
+ case 'ArrowDown':
3503
+ e.preventDefault();
3504
+ if (!state.isOpen) {
3505
+ state.isOpen = true;
3506
+ state.focusedIndex = 0;
3507
+ }
3508
+ else {
3509
+ const currentSelectableIndex = selectableOptions.findIndex((opt) => opt === options[state.focusedIndex]);
3510
+ const nextSelectableIndex = Math.min(currentSelectableIndex + 1, selectableOptions.length - 1);
3511
+ const nextOption = selectableOptions[nextSelectableIndex];
3512
+ state.focusedIndex = options.findIndex((opt) => opt === nextOption);
3513
+ }
3514
+ break;
3515
+ case 'ArrowUp':
3516
+ e.preventDefault();
3517
+ if (state.isOpen) {
3518
+ const currentSelectableIndex = selectableOptions.findIndex((opt) => opt === options[state.focusedIndex]);
3519
+ const prevSelectableIndex = Math.max(currentSelectableIndex - 1, 0);
3520
+ const prevOption = selectableOptions[prevSelectableIndex];
3521
+ state.focusedIndex = options.findIndex((opt) => opt === prevOption);
3522
+ }
3523
+ break;
3524
+ case 'Enter':
3525
+ case ' ':
3526
+ e.preventDefault();
3527
+ if (state.isOpen && state.focusedIndex >= 0 && state.focusedIndex < options.length) {
3528
+ const option = options[state.focusedIndex];
3529
+ if (option && !option.disabled) {
3530
+ toggleOption(option.id, attrs.multiple || false, attrs);
3531
+ }
3532
+ }
3533
+ else if (!state.isOpen) {
3534
+ state.isOpen = true;
3535
+ state.focusedIndex = 0;
3536
+ }
3537
+ break;
3538
+ case 'Escape':
3539
+ e.preventDefault();
3540
+ state.isOpen = false;
3541
+ state.focusedIndex = -1;
3542
+ break;
3543
+ }
3544
+ };
3545
+ const closeDropdown = (e) => {
3546
+ const target = e.target;
3547
+ if (!target.closest('.select-wrapper-container')) {
3548
+ state.isOpen = false;
3549
+ m.redraw();
3550
+ }
3551
+ };
3552
+ const renderGroupedOptions = (options, multiple, attrs) => {
3553
+ const groupedOptions = {};
3554
+ const ungroupedOptions = [];
3555
+ // Group options by their group property
3556
+ options.forEach((option) => {
3557
+ if (option.group) {
3558
+ if (!groupedOptions[option.group]) {
3559
+ groupedOptions[option.group] = [];
3560
+ }
3561
+ groupedOptions[option.group].push(option);
3562
+ }
3563
+ else {
3564
+ ungroupedOptions.push(option);
3565
+ }
3566
+ });
3567
+ const renderElements = [];
3568
+ // Render ungrouped options first
3569
+ ungroupedOptions.forEach((option) => {
3570
+ renderElements.push(m('li', Object.assign({ class: option.disabled ? 'disabled' : state.focusedIndex === options.indexOf(option) ? 'focused' : '' }, (option.disabled
3571
+ ? {}
3572
+ : {
3573
+ onclick: (e) => {
3574
+ e.stopPropagation();
3575
+ toggleOption(option.id, multiple, attrs);
3576
+ },
3577
+ })), m('span', multiple
3578
+ ? m('label', { for: option.id }, m('input', {
3579
+ id: option.id,
3580
+ type: 'checkbox',
3581
+ checked: state.selectedIds.includes(option.id),
3582
+ disabled: option.disabled ? true : undefined,
3583
+ onclick: (e) => {
3584
+ e.stopPropagation();
3585
+ },
3586
+ }), m('span', option.label))
3587
+ : option.label)));
3588
+ });
3589
+ // Render grouped options
3590
+ Object.keys(groupedOptions).forEach((groupName) => {
3591
+ // Add group header
3592
+ renderElements.push(m('li.optgroup', { tabindex: 0 }, m('span', groupName)));
3593
+ // Add group options
3594
+ groupedOptions[groupName].forEach((option) => {
3595
+ renderElements.push(m('li', Object.assign({ class: `optgroup-option${option.disabled ? ' disabled' : ''}${isSelected(option.id, state.selectedIds) ? ' selected' : ''}${state.focusedIndex === options.indexOf(option) ? ' focused' : ''}` }, (option.disabled
3596
+ ? {}
3597
+ : {
3598
+ onclick: (e) => {
3599
+ e.stopPropagation();
3600
+ toggleOption(option.id, multiple, attrs);
3601
+ },
3602
+ })), m('span', multiple
3603
+ ? m('label', { for: option.id }, m('input', {
3604
+ id: option.id,
3605
+ type: 'checkbox',
3606
+ checked: state.selectedIds.includes(option.id),
3607
+ disabled: option.disabled ? true : undefined,
3608
+ onclick: (e) => {
3609
+ e.stopPropagation();
3610
+ },
3611
+ }), m('span', option.label))
3612
+ : option.label)));
3613
+ });
3614
+ });
3615
+ return renderElements;
3616
+ };
3617
+ return {
3618
+ oninit: ({ attrs }) => {
3619
+ const { checkedId, initialValue, id } = attrs;
3620
+ state.id = id || uniqueId();
3621
+ const iv = checkedId || initialValue;
3622
+ if (iv !== null && typeof iv !== 'undefined') {
3623
+ if (iv instanceof Array) {
3624
+ state.selectedIds = [...iv];
3625
+ }
3626
+ else {
3627
+ state.selectedIds = [iv];
3628
+ }
3629
+ }
3630
+ // Add global click listener to close dropdown
3631
+ document.addEventListener('click', closeDropdown);
3632
+ },
3633
+ onremove: () => {
3634
+ // Cleanup global listener
3635
+ document.removeEventListener('click', closeDropdown);
3636
+ },
3637
+ view: ({ attrs }) => {
3638
+ // Sync external checkedId prop with internal state - do this in view for immediate response
3639
+ const { checkedId } = attrs;
3640
+ if (checkedId !== undefined) {
3641
+ const newIds = checkedId instanceof Array ? checkedId : [checkedId];
3642
+ if (JSON.stringify(newIds) !== JSON.stringify(state.selectedIds)) {
3643
+ state.selectedIds = newIds;
3644
+ }
3645
+ }
3646
+ const { newRow, className = 'col s12', key, options, multiple = false, label, helperText, placeholder = '', isMandatory, iconName, disabled, style, } = attrs;
3647
+ const finalClassName = newRow ? `${className} clear` : className;
3648
+ const selectedOptions = options.filter((opt) => isSelected(opt.id, state.selectedIds));
3649
+ return m('.input-field.select-space', {
3650
+ className: finalClassName,
3651
+ key,
3652
+ style,
3653
+ }, [
3654
+ // Icon prefix
3655
+ iconName && m('i.material-icons.prefix', iconName),
3656
+ m('.select-wrapper', {
3657
+ onclick: disabled
3658
+ ? undefined
3659
+ : () => {
3660
+ state.isOpen = !state.isOpen;
3661
+ },
3662
+ onkeydown: disabled ? undefined : (e) => handleKeyDown(e, attrs),
3663
+ tabindex: disabled ? -1 : 0,
3664
+ 'aria-expanded': state.isOpen ? 'true' : 'false',
3665
+ 'aria-haspopup': 'listbox',
3666
+ role: 'combobox',
3667
+ }, [
3668
+ m('input[type=text][readonly=true].select-dropdown.dropdown-trigger', {
3669
+ id: state.id,
3670
+ value: selectedOptions.length > 0 ? selectedOptions.map((o) => o.label || o.id).join(', ') : placeholder,
3671
+ oncreate: ({ dom }) => {
3672
+ state.inputRef = dom;
3673
+ },
3674
+ onclick: (e) => {
3675
+ e.preventDefault();
3676
+ e.stopPropagation();
3677
+ state.isOpen = !state.isOpen;
3678
+ },
3679
+ }),
3680
+ // Dropdown Menu
3681
+ state.isOpen &&
3682
+ m('ul.dropdown-content.select-dropdown', {
3683
+ tabindex: 0,
3684
+ oncreate: ({ dom }) => {
3685
+ state.dropdownRef = dom;
3686
+ },
3687
+ onremove: () => {
3688
+ state.dropdownRef = null;
3689
+ },
3690
+ style: getDropdownStyles(state.inputRef, true, options),
3691
+ }, [
3692
+ placeholder && m('li.disabled', { tabindex: 0 }, m('span', placeholder)),
3693
+ ...renderGroupedOptions(options, multiple, attrs),
3694
+ ]),
3695
+ m(MaterialIcon, {
3696
+ name: 'caret',
3697
+ direction: 'down',
3698
+ }),
3699
+ ]),
3700
+ // Label
3701
+ label &&
3702
+ m(Label, {
3703
+ id: state.id,
3704
+ label,
3705
+ isMandatory,
3706
+ }),
3707
+ // Helper text
3708
+ helperText && m(HelperText, { helperText }),
3709
+ ]);
3710
+ },
3711
+ };
3712
+ };
3713
+
3714
+ /** Component to display a switch with two values. */
3715
+ const Switch = () => {
3716
+ const state = { id: uniqueId(), checked: false };
3717
+ return {
3718
+ oninit: ({ attrs: { checked } }) => {
3719
+ state.checked = checked || false;
3720
+ },
3721
+ view: ({ attrs }) => {
3722
+ const id = attrs.id || state.id;
3723
+ const { label, left, right, disabled, newRow, onchange, isMandatory, className = 'col s12' } = attrs, params = __rest(attrs, ["label", "left", "right", "disabled", "newRow", "onchange", "isMandatory", "className"]);
3724
+ const cn = ['input-field', newRow ? 'clear' : '', className].filter(Boolean).join(' ').trim();
3725
+ return m('div', {
3726
+ className: cn,
3727
+ onclick: (e) => {
3728
+ state.checked = !state.checked;
3729
+ onchange && onchange(state.checked);
3730
+ e.preventDefault();
3731
+ },
3732
+ }, [
3733
+ label && m(Label, { label: label || '', id, isMandatory, className: 'active' }),
3734
+ m('.switch', params, m('label', {
3735
+ style: { cursor: 'pointer' },
3736
+ }, [
3737
+ m('span', left || 'Off'),
3738
+ m('input[type=checkbox]', {
3739
+ id,
3740
+ disabled,
3741
+ checked: state.checked,
3742
+ }),
3743
+ m('span.lever'),
3744
+ m('span', right || 'On'),
3745
+ ])),
3746
+ ]);
3747
+ },
3748
+ };
3749
+ };
3750
+
3751
+ /** CSS-only Tabs component - no MaterializeCSS dependencies */
3752
+ const Tabs = () => {
3753
+ const toAnchored = () => {
3754
+ let activeTabFound = false;
3755
+ return (tab) => {
3756
+ const active = activeTabFound ? false : tab.active;
3757
+ if (active)
3758
+ activeTabFound = true;
3759
+ const tabId = createId(tab.title, tab.id);
3760
+ return Object.assign(Object.assign({}, tab), { active, tabId, anchorId: `anchor-${tabId}` });
3761
+ };
3762
+ };
3763
+ const state = {
3764
+ activeTabId: '',
3765
+ isDragging: false,
3766
+ startX: 0,
3767
+ translateX: 0,
3768
+ indicatorStyle: {
3769
+ left: '0px',
3770
+ width: '0px',
3771
+ }};
3772
+ const createId = (title, id) => (id ? id : title.replace(/ /g, '').toLowerCase());
3773
+ const updateIndicator = () => {
3774
+ const tabElement = document.getElementById(state.activeTabId);
3775
+ if (tabElement) {
3776
+ const tabsContainer = tabElement.closest('.tabs');
3777
+ if (tabsContainer) {
3778
+ const containerRect = tabsContainer.getBoundingClientRect();
3779
+ const tabRect = tabElement.getBoundingClientRect();
3780
+ const newLeft = `${tabRect.left - containerRect.left}px`;
3781
+ const newWidth = `${tabRect.width}px`;
3782
+ // Only update if values actually changed - NO m.redraw()!
3783
+ if (state.indicatorStyle.left !== newLeft || state.indicatorStyle.width !== newWidth) {
3784
+ state.indicatorStyle = {
3785
+ left: newLeft,
3786
+ width: newWidth,
3787
+ };
3788
+ }
3789
+ }
3790
+ }
3791
+ };
3792
+ const handleTabClick = (tabId, tabElement, attrs) => {
3793
+ if (state.activeTabId === tabId)
3794
+ return;
3795
+ state.activeTabId = tabId;
3796
+ // Call onShow callback if provided
3797
+ if (attrs.onShow) {
3798
+ attrs.onShow(tabElement);
3799
+ }
3800
+ // Call onTabChange callback if provided
3801
+ if (attrs.onTabChange) {
3802
+ attrs.onTabChange(tabId);
3803
+ }
3804
+ };
3805
+ // Touch/swipe support for mobile
3806
+ const handleTouchStart = (e) => {
3807
+ if (!e.touches || e.touches.length === 0)
3808
+ return;
3809
+ state.isDragging = true;
3810
+ state.startX = e.touches[0].clientX;
3811
+ };
3812
+ const handleTouchEnd = (e, attrs) => {
3813
+ if (!state.isDragging || !e.changedTouches || e.changedTouches.length === 0)
3814
+ return;
3815
+ const endX = e.changedTouches[0].clientX;
3816
+ const deltaX = endX - state.startX;
3817
+ const threshold = 50; // Minimum swipe distance
3818
+ if (Math.abs(deltaX) > threshold) {
3819
+ const currentIndex = attrs.tabs.findIndex((tab) => createId(tab.title, tab.id) === state.activeTabId);
3820
+ if (deltaX > 0 && currentIndex > 0) {
3821
+ // Swipe right - go to previous tab
3822
+ const prevTab = attrs.tabs[currentIndex - 1];
3823
+ if (!prevTab.disabled && !prevTab.href) {
3824
+ const newTabId = createId(prevTab.title, prevTab.id);
3825
+ state.activeTabId = newTabId;
3826
+ if (attrs.onTabChange) {
3827
+ attrs.onTabChange(newTabId);
3828
+ }
3829
+ }
3830
+ }
3831
+ else if (deltaX < 0 && currentIndex < attrs.tabs.length - 1) {
3832
+ // Swipe left - go to next tab
3833
+ const nextTab = attrs.tabs[currentIndex + 1];
3834
+ if (!nextTab.disabled && !nextTab.href) {
3835
+ const newTabId = createId(nextTab.title, nextTab.id);
3836
+ state.activeTabId = newTabId;
3837
+ if (attrs.onTabChange) {
3838
+ attrs.onTabChange(newTabId);
3839
+ }
3840
+ }
3841
+ }
3842
+ }
3843
+ state.isDragging = false;
3844
+ state.translateX = 0;
3845
+ // m.redraw();
3846
+ };
3847
+ /** Initialize active tab - selectedTabId takes precedence, next active property or first available tab */
3848
+ const setActiveTabId = (anchoredTabs, selectedTabId) => {
3849
+ const selectedTab = selectedTabId ? anchoredTabs.find((a) => a.tabId === selectedTabId) : undefined;
3850
+ if (selectedTab) {
3851
+ state.activeTabId = selectedTab.tabId;
3852
+ selectedTab.active = true;
3853
+ return selectedTab;
3854
+ }
3855
+ const activeTab = anchoredTabs.find((t) => t.active);
3856
+ if (activeTab) {
3857
+ // Active tab property takes precedence over selectedTabId
3858
+ state.activeTabId = activeTab.tabId;
3859
+ return activeTab;
3860
+ }
3861
+ // Default to first non-disabled tab
3862
+ const firstAvailableTab = anchoredTabs.find((t) => !t.disabled && !t.href);
3863
+ if (firstAvailableTab) {
3864
+ state.activeTabId = firstAvailableTab.tabId;
3865
+ firstAvailableTab.active = true;
3866
+ return firstAvailableTab;
3867
+ }
3868
+ return undefined;
3869
+ };
3870
+ return {
3871
+ oninit: ({ attrs }) => {
3872
+ const anchoredTabs = attrs.tabs.map(toAnchored());
3873
+ setActiveTabId(anchoredTabs, attrs.selectedTabId);
3874
+ },
3875
+ oncreate: () => {
3876
+ updateIndicator();
3877
+ m.redraw();
3878
+ },
3879
+ view: ({ attrs }) => {
3880
+ const { tabWidth, tabs, className, style, swipeable = false } = attrs;
3881
+ const cn = [tabWidth === 'fill' ? 'tabs-fixed-width' : '', className].filter(Boolean).join(' ').trim();
3882
+ const anchoredTabs = tabs.map(toAnchored());
3883
+ const activeTab = setActiveTabId(anchoredTabs, attrs.selectedTabId);
3884
+ updateIndicator();
3885
+ return m('.row', [
3886
+ // Tab headers
3887
+ m('.col.s12', m('ul.tabs', {
3888
+ className: cn,
3889
+ style,
3890
+ }, [
3891
+ ...anchoredTabs.map((tab) => {
3892
+ const { className: tabClassName, title, anchorId, tabId, disabled, target, href } = tab;
3893
+ const cn = ['tab', tabWidth === 'fixed' ? `col s${Math.floor(12 / tabs.length)}` : '', tabClassName]
3894
+ .filter(Boolean)
3895
+ .join(' ')
3896
+ .trim();
3897
+ return m('li', {
3898
+ key: tabId,
3899
+ id: tabId,
3900
+ className: cn,
3901
+ disabled,
3902
+ }, m('a', {
3903
+ id: anchorId,
3904
+ className: tab.active ? 'active' : undefined,
3905
+ target,
3906
+ href: href || `#${anchorId}`,
3907
+ onclick: disabled || href
3908
+ ? undefined
3909
+ : (e) => {
3910
+ e.preventDefault();
3911
+ handleTabClick(tabId, e.target, attrs);
3912
+ },
3913
+ style: disabled ? { opacity: '0.6', cursor: 'not-allowed' } : undefined,
3914
+ }, title));
3915
+ }),
3916
+ // Add the indicator element
3917
+ m('li.indicator', {
3918
+ key: 'indicator',
3919
+ style: {
3920
+ display: state.activeTabId ? 'block' : 'none',
3921
+ left: state.indicatorStyle.left,
3922
+ width: state.indicatorStyle.width,
3923
+ transition: 'left 0.35s ease, width 0.35s ease',
3924
+ },
3925
+ }),
3926
+ ]), activeTab &&
3927
+ m('.col.s12', {
3928
+ ontouchstart: swipeable ? handleTouchStart : undefined,
3929
+ ontouchend: swipeable ? (e) => handleTouchEnd(e, attrs) : undefined,
3930
+ style: swipeable ? { touchAction: 'pan-y' } : undefined,
3931
+ }, m('.tab-content', {
3932
+ className: activeTab.contentClass,
3933
+ }, activeTab.vnode))),
3934
+ ]);
3935
+ },
3936
+ };
3937
+ };
3938
+
3939
+ /**
3940
+ * Mithril Factory Component for Multi-Select Dropdown with search
3941
+ */
3942
+ const SearchSelect = () => {
3943
+ // (): <T extends string | number>(): Component<SearchSelectAttrs<T>, SearchSelectState<T>> => {
3944
+ // State initialization
3945
+ const state = {
3946
+ isOpen: false,
3947
+ selectedOptions: [], //options.filter((o) => iv.includes(o.id)),
3948
+ searchTerm: '',
3949
+ options: [],
3950
+ inputRef: null,
3951
+ dropdownRef: null,
3952
+ focusedIndex: -1,
3953
+ onchange: null,
3954
+ };
3955
+ const componentId = uniqueId();
3956
+ const searchInputId = `${componentId}-search`;
3957
+ // Handle click outside
3958
+ const handleClickOutside = (e) => {
3959
+ const target = e.target;
3960
+ if (state.dropdownRef && state.dropdownRef.contains(target)) {
3961
+ // Click inside dropdown, do nothing
3962
+ return;
3963
+ }
3964
+ if (state.inputRef && state.inputRef.contains(target)) {
3965
+ // Click on trigger handled by onclick event
3966
+ return;
3967
+ }
3968
+ else {
3969
+ // Click outside, close dropdown
3970
+ state.isOpen = false;
3971
+ m.redraw();
3972
+ }
3973
+ };
3974
+ // Handle keyboard navigation
3975
+ const handleKeyDown = (e, filteredOptions, showAddNew) => {
3976
+ if (!state.isOpen)
3977
+ return;
3978
+ const totalOptions = filteredOptions.length + (showAddNew ? 1 : 0);
3979
+ switch (e.key) {
3980
+ case 'ArrowDown':
3981
+ e.preventDefault();
3982
+ state.focusedIndex = Math.min(state.focusedIndex + 1, totalOptions - 1);
3983
+ break;
3984
+ case 'ArrowUp':
3985
+ e.preventDefault();
3986
+ state.focusedIndex = Math.max(state.focusedIndex - 1, -1);
3987
+ break;
3988
+ case 'Enter':
3989
+ e.preventDefault();
3990
+ if (state.focusedIndex >= 0) {
3991
+ if (showAddNew && state.focusedIndex === filteredOptions.length) {
3992
+ // Handle add new option
3993
+ return 'addNew';
3994
+ }
3995
+ else if (state.focusedIndex < filteredOptions.length) {
3996
+ toggleOption(filteredOptions[state.focusedIndex]);
3997
+ }
3998
+ }
3999
+ break;
4000
+ case 'Escape':
4001
+ e.preventDefault();
4002
+ state.isOpen = false;
4003
+ state.focusedIndex = -1;
4004
+ break;
4005
+ }
4006
+ return null;
4007
+ };
4008
+ // Toggle option selection
4009
+ const toggleOption = (option) => {
4010
+ if (option.disabled)
4011
+ return;
4012
+ state.selectedOptions = state.selectedOptions.some((item) => item.id === option.id)
4013
+ ? state.selectedOptions.filter((item) => item.id !== option.id)
4014
+ : [...state.selectedOptions, option];
4015
+ state.searchTerm = '';
4016
+ state.focusedIndex = -1;
4017
+ state.onchange && state.onchange(state.selectedOptions.map((o) => o.id));
4018
+ };
4019
+ // Remove a selected option
4020
+ const removeOption = (option) => {
4021
+ state.selectedOptions = state.selectedOptions.filter((item) => item.id !== option.id);
4022
+ state.onchange && state.onchange(state.selectedOptions.map((o) => o.id));
4023
+ };
4024
+ return {
4025
+ oninit: ({ attrs: { options = [], initialValue = [], onchange } }) => {
4026
+ state.options = options;
4027
+ state.selectedOptions = options.filter((o) => initialValue.includes(o.id));
4028
+ state.onchange = onchange;
4029
+ },
4030
+ oncreate() {
4031
+ document.addEventListener('click', handleClickOutside);
4032
+ },
4033
+ onremove() {
4034
+ document.removeEventListener('click', handleClickOutside);
4035
+ },
4036
+ view({ attrs: {
4037
+ // onchange,
4038
+ oncreateNewOption, className, placeholder, searchPlaceholder = 'Search options...', noOptionsFound = 'No options found', label,
4039
+ // maxHeight = '25rem',
4040
+ }, }) {
4041
+ // Safely filter options
4042
+ const filteredOptions = state.options.filter((option) => (option.label || option.id.toString()).toLowerCase().includes((state.searchTerm || '').toLowerCase()) &&
4043
+ !state.selectedOptions.some((selected) => selected.id === option.id));
4044
+ // Check if we should show the "add new option" element
4045
+ const showAddNew = oncreateNewOption &&
4046
+ state.searchTerm &&
4047
+ !filteredOptions.some((o) => (o.label || o.id.toString()).toLowerCase() === state.searchTerm.toLowerCase());
4048
+ // Render the dropdown
4049
+ return m('.input-field.multi-select-dropdown', { className }, [
4050
+ m('.chips.chips-initial.chips-container', {
4051
+ oncreate: ({ dom }) => {
4052
+ state.inputRef = dom;
4053
+ },
4054
+ onclick: (e) => {
4055
+ // console.log('SearchSelect clicked', state.isOpen, e); // Debug log
4056
+ e.preventDefault();
4057
+ e.stopPropagation();
4058
+ state.isOpen = !state.isOpen;
4059
+ // console.log('SearchSelect state changed to', state.isOpen); // Debug log
4060
+ },
4061
+ style: {
4062
+ display: 'flex',
4063
+ alignItems: 'end',
4064
+ flexWrap: 'wrap',
4065
+ cursor: 'pointer',
4066
+ position: 'relative',
4067
+ },
4068
+ }, [
4069
+ // TODO FIXME Add to existing input
4070
+ // Hidden input for label association and accessibility
4071
+ m('input', {
4072
+ type: 'text',
4073
+ id: componentId,
4074
+ value: state.selectedOptions.map((o) => o.label || o.id.toString()).join(', '),
4075
+ readonly: true,
4076
+ style: { position: 'absolute', left: '-9999px', opacity: 0 },
4077
+ }),
4078
+ // Selected Options (chips)
4079
+ ...state.selectedOptions.map((option) => m('.chip', [
4080
+ option.label || option.id.toString(),
4081
+ m(MaterialIcon, {
4082
+ name: 'close',
4083
+ className: 'close',
4084
+ onclick: (e) => {
4085
+ e.stopPropagation();
4086
+ removeOption(option);
4087
+ },
4088
+ }),
4089
+ ])),
4090
+ // Placeholder when no options selected
4091
+ state.selectedOptions.length === 0 &&
4092
+ placeholder &&
4093
+ m('span.placeholder', {
4094
+ style: {
4095
+ color: '#9e9e9e',
4096
+ flexGrow: 1,
4097
+ padding: '8px 0',
4098
+ },
4099
+ }, placeholder),
4100
+ // Spacer to push caret to the right
4101
+ m('span.spacer', { style: { flexGrow: 1 } }),
4102
+ m(MaterialIcon, {
4103
+ name: 'caret',
4104
+ direction: state.isOpen ? 'up' : 'down',
4105
+ style: { marginLeft: 'auto', cursor: 'pointer' },
4106
+ }),
4107
+ ]),
4108
+ // Label
4109
+ label &&
4110
+ m('label', {
4111
+ for: componentId,
4112
+ class: placeholder || state.selectedOptions.length > 0 ? 'active' : '',
4113
+ }, label),
4114
+ // Dropdown Menu
4115
+ state.isOpen &&
4116
+ m('ul.dropdown-content.select-dropdown', {
4117
+ oncreate: ({ dom }) => {
4118
+ state.dropdownRef = dom;
4119
+ },
4120
+ onremove: () => {
4121
+ state.dropdownRef = null;
4122
+ },
4123
+ style: getDropdownStyles(state.inputRef),
4124
+ }, [
4125
+ m('li', // Search Input
4126
+ {
4127
+ class: 'search-wrapper',
4128
+ style: { padding: '0 16px', position: 'relative' },
4129
+ }, [
4130
+ m('input', {
4131
+ type: 'text',
4132
+ id: searchInputId,
4133
+ placeholder: searchPlaceholder,
4134
+ value: state.searchTerm || '',
4135
+ oncreate: ({ dom }) => {
4136
+ // Auto-focus the search input when dropdown opens
4137
+ dom.focus();
4138
+ },
4139
+ oninput: (e) => {
4140
+ state.searchTerm = e.target.value;
4141
+ state.focusedIndex = -1; // Reset focus when typing
4142
+ },
4143
+ onkeydown: async (e) => {
4144
+ const result = handleKeyDown(e, filteredOptions, !!showAddNew);
4145
+ if (result === 'addNew' && oncreateNewOption) {
4146
+ const option = await oncreateNewOption(state.searchTerm);
4147
+ toggleOption(option);
4148
+ }
4149
+ },
4150
+ style: {
4151
+ width: '100%',
4152
+ outline: 'none',
4153
+ fontSize: '0.875rem',
4154
+ border: 'none',
4155
+ padding: '8px 0',
4156
+ borderBottom: '1px solid #9e9e9e',
4157
+ },
4158
+ }),
4159
+ ]),
4160
+ // No options found message or list of options
4161
+ ...(filteredOptions.length === 0 && !showAddNew
4162
+ ? [
4163
+ m('li',
4164
+ // {
4165
+ // style: getNoOptionsStyles(),
4166
+ // },
4167
+ noOptionsFound),
4168
+ ]
4169
+ : []),
4170
+ // Add new option item
4171
+ ...(showAddNew
4172
+ ? [
4173
+ m('li', {
4174
+ onclick: async () => {
4175
+ const option = await oncreateNewOption(state.searchTerm);
4176
+ toggleOption(option);
4177
+ },
4178
+ class: state.focusedIndex === filteredOptions.length ? 'active' : '',
4179
+ onmouseover: () => {
4180
+ state.focusedIndex = filteredOptions.length;
4181
+ },
4182
+ }, [m('span', `+ "${state.searchTerm}"`)]),
4183
+ ]
4184
+ : []),
4185
+ // List of filtered options
4186
+ ...filteredOptions.map((option, index) => m('li', {
4187
+ onclick: (e) => {
4188
+ e.preventDefault();
4189
+ e.stopPropagation();
4190
+ toggleOption(option);
4191
+ },
4192
+ class: `${option.disabled ? 'disabled' : ''} ${state.focusedIndex === index ? 'active' : ''}`.trim(),
4193
+ onmouseover: () => {
4194
+ if (!option.disabled) {
4195
+ state.focusedIndex = index;
4196
+ }
4197
+ },
4198
+ }, m('span', [
4199
+ m('input', {
4200
+ type: 'checkbox',
4201
+ checked: state.selectedOptions.some((selected) => selected.id === option.id),
4202
+ }),
4203
+ option.label || option.id.toString(),
4204
+ ]))),
4205
+ ]),
4206
+ ]);
4207
+ },
4208
+ };
4209
+ };
4210
+
4211
+ export { AnchorItem, Autocomplete, Button, ButtonFactory, Carousel, CharacterCounter, Chips, CodeBlock, Collapsible, CollapsibleItem, Collection, CollectionMode, ColorInput, DatePicker, Dropdown, EmailInput, FileInput, FlatButton, FloatingActionButton, HelperText, Icon, InputCheckbox, Label, LargeButton, ListItem, Mandatory, MaterialBox, ModalPanel, NumberInput, Options, Pagination, Parallax, PasswordInput, RadioButton, RadioButtons, RangeInput, RoundIconButton, SearchSelect, SecondaryContent, Select, SmallButton, SubmitButton, Switch, Tabs, TextArea, TextInput, TimePicker, UrlInput, getDropdownStyles, isNumeric, padLeft, range, uniqueId, uuid4 };