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