2 lines
28 KiB
JavaScript
2 lines
28 KiB
JavaScript
import{Component as t,NewState as e,Util as i,$ as s,Hash as n,RefreshState as a}from"@web/state";const l={get:({url:t,...e})=>l.request({url:t,method:"GET",...e}),post:({url:t,data:e,...i})=>l.request({url:t,method:"POST",data:e,...i}),put:({url:t,data:e,...i})=>l.request({url:t,method:"PUT",data:e,...i}),delete:({url:t,...e})=>l.request({url:t,method:"DELETE",...e}),head:({url:t,...e})=>l.request({url:t,method:"HEAD",...e}),request:async({url:t,method:e="POST",data:i,headers:s={},responseType:n,timeout:a=1e4})=>{var l;const o={method:e=e.toUpperCase(),signal:null==(l=AbortSignal.timeout)?void 0:l.call(AbortSignal,a)};if(void 0!==i&&"GET"!==e&&"HEAD"!==e){if(i instanceof HTMLFormElement&&(i=new FormData(i)),i&&"object"==typeof i&&!(i instanceof FormData)&&!(i instanceof ArrayBuffer||ArrayBuffer.isView(i))&&Object.values(i).some(t=>t instanceof File||t instanceof Blob||t instanceof FileList||Array.isArray(t)&&t.some(t=>t instanceof File||t instanceof Blob))){const t=new FormData;for(const[e,s]of Object.entries(i))s instanceof FileList||Array.isArray(s)?Array.from(s).forEach(i=>t.append(e,i)):null!=s&&t.append(e,s);i=t}i instanceof FormData?delete s["Content-Type"]:"string"==typeof i||i instanceof ArrayBuffer||ArrayBuffer.isView(i)||(i=JSON.stringify(i),s["Content-Type"]||(s["Content-Type"]="application/json")),o.body=i}Object.keys(s).length&&(o.headers=s);const r={error:null,ok:null,status:0,headers:{},responseType:"",result:null};try{const e=await fetch(t,o);if(Object.assign(r,{ok:e.ok,status:e.status,headers:Object.fromEntries(e.headers.entries())}),!n){const t=e.headers.get("Content-Type")||"";n=t.includes("application/json")?"json":/image|video|audio|pdf|zip|octet-stream/.test(t)?"binary":"text",r.responseType=n}!1===r.ok&&(r.error=(r.statusText||"HTTP "+r.status+" error")+" for "+t),r.result="json"===n?await e.json():"binary"===n?await e.arrayBuffer():await e.text()}catch(t){Object.assign(r,{error:t.message||String(t),ok:!1})}return r}},o=t.register("API",t=>{t.request=e({url:"",method:"GET",headers:{},data:null,timeout:1e4,responseType:""}),t.response=e({loading:!1,ok:null,status:null,error:null,headers:{},responseType:"",result:null}),t.result=e(),t.do=(e={})=>new Promise((i,s)=>{const n={...t.request,...e};if(!n.url)throw new Error(".url is required");n.headers={...t.request.headers,...e.headers},t.response.loading=!0,l.request(n).then(e=>{if(Object.keys(e).forEach(i=>{"result"!==i&&(t.response[i]=e[i])}),e.result&&"object"==typeof e.result&&t.result&&"object"==typeof t.result?Object.assign(t.result,e.result):t.result=e.result,t.response.loading=!1,!1===e.ok)throw new Error(e.error);if("object"==typeof e.result&&e.result.error)throw new Error(e.result.error);t.dispatchEvent(new CustomEvent("response",{detail:e,bubbles:!1})),i(e)}).catch(i=>{var n;!e.noui&&(null==(n=globalThis.UI)?void 0:n.toast)&&UI.toast(i.message,{type:"danger"}),t.dispatchEvent(new CustomEvent("error",{detail:i,bubbles:!0})),s(i)})});let i=null;t.request.__watch(null,()=>{t.hasAttribute("auto")&&t.request.url&&(i||(i=Promise.resolve().then(()=>{t.do(),i=null})))})}),r={};t.register("Modal",t=>{t.modal=new bootstrap.Modal(t),t.addEventListener("bind",e=>{e.detail?t.modal.show():t.modal.hide()}),t.addEventListener("hide.bs.modal",()=>{var e;null==(e=document.activeElement)||e.blur(),t.dispatchEvent(new CustomEvent("change",{bubbles:!1,detail:!1}))}),i.copyFunction(t,t.modal,"show","hide")},i.makeDom('\n<div class="modal fade" data-bs-backdrop="static">\n <div class="modal-dialog modal-dialog-centered">\n <div $class="modal-content text-bg-${this.state?.type || \'primary\'}">\n <div slot-id="header" class="modal-header">\n <h6 class="modal-title" $text="this.state?.title"></h6>\n <button type="button" class="btn btn-link ms-2 bi bi-x-lg link-reset" style="color:inherit" data-bs-dismiss="modal"></button>\n </div>\n <div slot-id="body" class="modal-body"></div>\n <div slot-id="footer" class="modal-footer"></div>\n </div>\n </div>\n</div>\n')),t.register("Dialog",t.getSetupFunction("Modal"),i.makeDom('\n<div class="modal fade" data-bs-backdrop="static">\n <div class="modal-dialog modal-dialog-centered">\n <div $class="modal-content text-bg-${this.state?.type || \'primary\'}">\n <div $if="this.state?.title" class="modal-header" $text="this.state?.title"></div>\n <div slot-id="body" class="modal-body"><div $html="this.state?.message"></div></div>\n <div class="modal-footer">\n <button $each="this.state?.buttons || [\'{#Close#}\']" type="button" class="btn btn-sm btn-${index === buttons.length - 1 ? (type && type !== \'body\' ? type : \'primary\') : \'secondary\'}" $onclick="this.result=index+1;this.hide()" $text="${item}"></button>\n </div>\n </div>\n </div>\n</div>\n'));let d=0;r.showDialog=function({title:t="",message:e="",buttons:i=["{#Close#}"],type:s="body"}){const n=document.body.appendChild(document.createElement("Dialog"));return n.style.zIndex=2e3+ ++d,Promise.resolve().then(()=>{Object.assign(n.state,{message:e,title:t,type:s,buttons:i}),n.show()}),new Promise(t=>{n.addEventListener("change",e=>{d--,t(n.result||0),n.remove()})})},r.alert=function(t,e={}){return r.showDialog({message:t,...e})},r.confirm=function(t,e={}){return new Promise(i=>r.showDialog({message:t,buttons:["{#Cancel#}","{#Confirm#}"],...e}).then(t=>i(t>=2)).catch(()=>i(!1)))},t.register("Toast",t=>{t.toast=new bootstrap.Toast(t,{autohide:t.state.delay>0}),i.copyFunction(t,t.toast,"show","hide"),t.addEventListener("show.bs.toast",()=>{if(t.state.delay>0){let e;const i=()=>{t.state.left=t.state.delay/1e3,e=setInterval(()=>{(!t.isConnected||--t.state.left<=0)&&clearInterval(e)},1e3)};i(),t.addEventListener("mouseenter",()=>{clearInterval(e),t.state.left=void 0}),t.addEventListener("mouseleave",i)}})},i.makeDom('\n<div class="toast align-items-center border-0 m-1">\n <div $class="toast-body rounded p-3 text-bg-${this.state?.type}">\n <div class="d-flex align-items-center">\n <div class="flex-grow-1">\n <span style="white-space:pre-wrap" class="fs-6" $text="this.state?.message"></span>\n <span $if="this.state?.left !== undefined" class="small text-dim ms-2" $text="${this.state?.left}s"></span>\n </div>\n <button type="button" class="btn btn-link ms-3 bi bi-x-lg link-reset" style="color:inherit" data-bs-dismiss="toast"></button>\n </div>\n <div class="d-flex justify-content-end gap-3">\n <button $each="this.state?.buttons || [\'{#Close#}\']" type="button" $class="btn btn-sm btn-${this.state?.type} mt-2" data-bs-dismiss="toast" $onclick="this.result=index+1" $text="item"></button>\n </div>\n </div>\n</div>\n'),i.makeDom('<div toast-container="default" class="position-fixed bottom-0 end-0 overflow-auto" style="z-index:3000;max-height:80%"></div>')),r.toast=function(t,e={}){const i=e.delay??5e3,n=document.createElement("Toast");n.state={delay:i,left:i?i/1e3:void 0,type:e.type||"primary",message:t,buttons:e.buttons||[]},s(`[toast-container="${e.container||"default"}"]`).appendChild(n),Promise.resolve().then(()=>n.show())},r.toastConfirm=function(t,e={}){return new Promise(i=>r.toast(t,{buttons:["{#Confirm#}"],...e}).then(t=>i(1===t)).catch(()=>i(!1)))},t.register("AutoForm",t=>{t.state.schema||(t.state.schema=[]),t.vertical=t.hasAttribute("vertical"),t.inline=t.hasAttribute("inline"),t.request={method:"POST"},t.response={},t.result=null;const i=()=>{t.data&&t.data.__watch||(t.data=e(t.data||{})),t.data.__watch("*",()=>{if(t.inline){const e=t.closest("DataTable");e&&e.refresh&&e.refresh()}})};t.data?i():requestAnimationFrame(i),t.form=s(t,"form"),t.submit=(e={})=>{var i,s;if(!t.form.reportValidity())return null==(s=null==(i=globalThis.UI)?void 0:i.toast)?void 0:s.call(i,"{#verify failed#}",{type:"danger"});if(!t.dispatchEvent(new CustomEvent("submit",{detail:t.data,cancelable:!0,bubbles:!1})))return;const n={...t.request,data:t.data,noui:!0,...e};let a=null;if(t.api)a=t.api.do(n);else{if(!t.request.url)return console.warn("{#please config .api or .request.url to auto submit#}");a=l.request(n)}a.then(e=>{if(t.response=e,t.result=e.result,"object"==typeof e.result&&e.result.error)throw new Error(e.result.error);t.dispatchEvent(new CustomEvent("response",{detail:e,bubbles:!1}))}).catch(e=>{var i;(null==(i=globalThis.UI)?void 0:i.toast)&&UI.toast(e.message,{type:"danger"}),t.dispatchEvent(new CustomEvent("error",{detail:e,bubbles:!0}))})}},i.makeDom('\n<div $class="\'auto-form-root\' + (this.inline ? \' auto-form-inline min-h-100 w-100\' : \'\')">\n <form $class="\'h-100 w-100 d-flex \' + (this.inline ? \'align-items-center\' : (this.vertical ? \'flex-column\' : \'auto-grid-form\'))" $onsubmit="event.stopPropagation();event.preventDefault();this.submit()">\n <div $each="this.state.schema || []" style="display:contents">\n <label $if="!this.inline" $name="item.name" class="col-form-label text-muted" $text="item.label"></label>\n <div control-wrapper $class="this.inline ? \'flex-grow-1 h-100 d-flex align-items-center\' : \'mb-3\'">\n <input $if="[\'text\', \'password\', \'email\', \'number\', \'date\', \'datetime\', \'file\'].includes(item.type)" $name="item.name" class="form-control" $type="item.type" $.="item.setting || {}" $bind="this.data[item.name]" $class="item.type === \'number\' ? \'text-end\' : \'\'">\n <select $if="item.type === \'select\'" $name="item.name" class="form-select" $.="item.setting || {}" $bind="this.data[item.name]">\n <option value="" $if="item.placeholder" $text="item.placeholder" disabled></option>\n <option $each="item.options" $value="item.value || item" $text="item.label || item"></option>\n </select>\n <div $if="[\'checkbox\', \'radio\'].includes(item.type)" $class="this.inline ? \'h-100 d-flex align-items-center px-2 gap-3 justify-content-center w-100\' : \'\'" style="white-space:nowrap">\n <label $each="item.options || [item.text||item.label||item.name]" as="option" $class="\'form-check\' + (item.vertical ? \'\' : \' form-check-inline\') + \' mb-0 d-flex align-items-center p-0\'">\n <input $name="item.name" class="form-check-input me-1" $type="item.type" $.="item.setting || {}" $value="item.options?option:\'on\'" $bind="this.data[item.name]">\n <span $if="!this.inline || (item.options && item.options.length > 0)" $text="option" class="form-check-label"></span>\n </label>\n </div>\n <div $if="item.type===\'switch\'" class="form-check form-switch fs-4 h-100 d-flex align-items-center px-2 m-0 justify-content-center w-100" style="padding-left:0">\n <input $name="item.name" class="form-check-input m-0" type="checkbox" style="cursor:pointer" $bind="this.data[item.name]">\n </div>\n <textarea $if="item.type===\'textarea\'" $name="item.name" class="form-control" $.="item.setting || {}" $bind="this.data[item.name]"></textarea>\n </div>\n </div>\n <div $if="!this.inline" class="d-flex justify-content-end align-items-baseline gap-3 mt-3" style="grid-column:1/-1">\n <div slot-id="actions"></div>\n <button type="submit" class="btn btn-primary" $text="this.submitlabel || \'{#Submit#}\'"></button>\n </div>\n </form>\n</div>\n'),i.makeDom("<style>\n\t@media (min-width: 576px) { .auto-grid-form {display: grid;grid-template-columns: max-content 1fr} .auto-grid-form .col-form-label {text-align: right; margin-bottom: 1rem;padding-right: 1rem;max-width: 200px} }\n\t.auto-form-inline { background-color: var(--bs-body-bg); }\n\t.auto-form-inline.min-h-100 { min-height: 100%; height: auto; }\n\t.auto-form-inline .form-control, .auto-form-inline .form-select { border-radius: 0; border: none; background-color: transparent !important; min-height: 100%; height: auto; width: 100%; padding: 0 8px; box-shadow: none; color: inherit; }\n\t.auto-form-inline .form-select { background-position: right 4px center; background-size: 12px 10px; padding-right: 20px; }\n\t.auto-form-inline textarea.form-control { padding: 8px; min-height: 100px; height: auto; }\n</style>"));const c=[],m={register:e=>{"undefined"!=typeof document&&("loading"!==document.readyState&&t.getTemplate("AutoForm")?m._addAutoFormComponent(e):c.push(e))},_addAutoFormComponent:e=>{var n;const a=t.getTemplate("AutoForm");a&&(null==(n=s(a.content,"[control-wrapper]"))||n.appendChild(i.makeDom(`<${e} $if="item.type?.toUpperCase() === '${e.toUpperCase()}'" $name="item.name" $.="item.setting || {}" $bind="this.data[item.name]"></${e}>`)))}};if("undefined"!=typeof document){const t=()=>{c.forEach(t=>m._addAutoFormComponent(t)),c.length=0};"loading"!==document.readyState?setTimeout(t,100):document.addEventListener("DOMContentLoaded",()=>setTimeout(t,100),!0)}t.register("TagsInput",t=>{t.addEventListener("bind",e=>{t.state.tags=e.detail||[]})},i.makeDom(`\n<div class="form-control d-flex flex-wrap gap-1 align-items-center" style="min-height:38px;cursor:text">\n <button $each="this.state.tags" type="button" class="btn btn-sm btn-outline-primary rounded-pill py-0 px-2" $onkeydown="${i.getFunctionBody(t=>{["Backspace","Delete"].includes(t.key)&&(t.preventDefault(),(void 0).state.tags.splice(index,1),(void 0).state.tags=(void 0).state.tags,(void 0).dispatchEvent(new CustomEvent("change",{bubbles:!1,detail:(void 0).state.tags})),Promise.resolve().then(()=>{const t=$$(void 0,"button");(t.length>0?t[index>0?index-1:0]:s(void 0,"input")).focus()}))})}" $text="item"></button>\n <input type="text" class="border-0 shadow-none py-0 px-2 flex-grow-1 bg-transparent" placeholder="{#new tag name#}" style="min-width:100px;width:0;outline:none" $onkeydown="${i.getFunctionBody(t=>{if(!t.isComposing&&["Enter",","," "].includes(t.key)){t.preventDefault();const e=thisNode.value.trim();e&&!(void 0).state.tags.includes(e)&&((void 0).state.tags.push(e),(void 0).state.tags=(void 0).state.tags,(void 0).dispatchEvent(new CustomEvent("change",{bubbles:!1,detail:(void 0).state.tags}))),thisNode.value=""}})}">\n</div>\n`),i.makeDom("<style>TagsInput button:focus {background-color:var(--bs-btn-hover-bg);color:var(--bs-btn-hover-color)}</style>")),m.register("TagsInput");const u=()=>{const t=new Map,e=new Map;let s=1;const n=i.newAvg();let a=0,l=0,o=0,r=null,d=null,c=!1;return{reset:(i,m)=>{if(c=!1,t.clear(),e.clear(),n.clear(),o=0,r=null,d=null,!(null==i?void 0:i.length))return[];const u=i.length;s=Math.ceil(Math.sqrt(u))||10;const p=window.getComputedStyle(m);return a=parseFloat(p.paddingTop)||0,l=parseFloat(p.rowGap)||0,i.slice(0,Math.min(30,u))},init:(i,a)=>{if(c)return;const l=i.length,o=n.get()||32;for(let e=0;e<l;e++)t.has(e)||t.set(e,o);for(let i=0;i<l;i+=s){let n=0;for(let e=i;e<Math.min(i+s,l);e++)n+=t.get(e);e.set(i,n)}c=!0,a()},update:(i,a)=>{if(null===r){const t=window.getComputedStyle(a);r=parseFloat(t.marginTop)||0,d=parseFloat(t.marginBottom)||0}0===i&&(o=r);const c=a.offsetHeight+r+d+l,m=t.get(i);if(c!==m){t.set(i,c),n.add(c);const a=c-(m||0),l=i-i%s;e.has(l)&&e.set(l,e.get(l)+a)}},calc:(i,r)=>{if(!c||!r)return null;const d=r.length,m=Math.ceil((i.clientHeight||100)/(n.get()||32));let u=a+o+l,p=0,h=0,v=0,f=0;for(let n=0;n<d;n++)if(0===h){const a=e.get(n);if(a&&u+a<i.scrollTop)u+=a,n+=Math.min(s,d-n)-1;else{const e=t.get(n);if(u+e<i.scrollTop)u+=e;else{h=1;let e=Math.max(0,n);v=Math.max(0,e-m),f=Math.min(v+3*m,d),n=f-1;for(let i=v;i<e;i++)u-=t.get(i)}}}else if(1===h){const i=e.get(n);i?(p+=i,n+=s-1):p+=t.get(n)}return{prevHeight:u-a-o-l,postHeight:p,renderedList:r.slice(v,f),listStartIndex:v}}}},p=t.register("FastList",t=>{const e=u();t.state.renderedList=[],t.onItemUpdate=(i,s)=>{e.update(i+(t.state._listStartIndex||0),s)},t.refresh=()=>{const i=e.calc(t,t.state.list);i&&(t.state.prevHeight=i.prevHeight,t.state.postHeight=i.postHeight,t.state._listStartIndex=i.listStartIndex,t.state.renderedList=i.renderedList)},t.state.__watch("list",i=>{t.state._listStartIndex=0,t.state.renderedList=e.reset(i,t)||[],requestAnimationFrame(()=>{t.state.list===i&&e.init(i,t.refresh)})})},i.makeDom('\n<div class="overflow-auto" onscroll="this.refresh()" style="overflow-anchor:none">\n <div $if="this.state.prevHeight>0" $style="height:${this.state.prevHeight}px;" class="flex-shrink-0"></div>\n <div slot-id="item" class="flex-shrink-0" $each="this.state.renderedList" $onupdate="this.onItemUpdate(index,thisNode)"></div>\n <div $if="this.state.postHeight>0" $style="height:${this.state.postHeight}px;" class="flex-shrink-0"></div>\n</div>\n'));t.attachSelectFeature=(t,e="itemclick",i="selectItem",s="itemActiveTag",n="selectedItem",a="idfield",l="auto-select")=>{const o=l&&t.hasAttribute(l);t[i]=(i,s)=>{o&&(t.state[n]=t.state[n]===i[t[a]]?null:i[t[a]]),t.dispatchEvent(new CustomEvent(e,{bubbles:!1,detail:!o||t.state[n]?{item:i,index:s}:{item:{},index:null}}))},t[s]=e=>null!==t.state[n]&&t.state[n]===e[t[a]]?"active":""},t.register("List",e=>{t.attachSelectFeature(e);const s={idfield:"id",labelfield:"label",summaryfield:"summary"};e.state.__watch("list",t=>{i.updateDefaults(e,s)})},i.makeDom('\n<div class="list-group overflow-auto">\n <div slot="item" slot-id="item" $each="this.state.list" $onclick="this.selectItem(item,index)" $class="list-group-item list-group-item-action list-group-item-heading d-inline-flex pe-auto align-items-baseline ${this.itemActiveTag?.(item)}">\n <span $if="this.itemicon" class="bi bi-${this.itemicon} text-body"></span>\n <div class="flex-shrink-0 px-1" $text>${item[this.labelfield]}</div>\n <div class="text-muted small flex-fill text-end text-truncate" style="width: 0" $text="item[this.summaryfield]"></div>\n <div slot-id="item-actions"></div>\n </div>\n</div>\n')),t.register("GroupedList",e=>{t.getSetupFunction("List")(e),e._selectItem=e.selectItem,e.selectItem=(t,i)=>{e.state.selectedGroup=null,e._selectItem(t,i)},t.attachSelectFeature(e,"groupclick","selectGroup","groupActiveTag","selectedGroup","groupidfield",e.hasAttribute("auto-select-group")||e.hasAttribute("auto-select")&&"group-selectable"),e._selectGroup=e.selectGroup,e.selectGroup=(t,i)=>{e.state.selectedItem=null,e._selectGroup(t,i)};const s={typefield:"type",groupidfield:"id",grouplabelfield:"label",groupsummaryfield:"summary",groupfield:"group"};e.state.__watch("list",t=>{i.updateDefaults(e,s);const n=[];return(e.state.groups||[]).forEach(i=>{n.push({[e.typefield]:"group",...i}),t&&t.forEach(t=>{t[e.groupfield]===i[e.groupidfield]&&n.push({[e.typefield]:"item",...t})})}),n})},i.makeDom('\n<div class="list-group overflow-auto">\n <template $each="this.state.list">\n <div slot-id="group" $if="item.type === \'group\'" $onclick="this.selectGroup(item,index)" style="height: 36px" $class="list-group-item list-group-item-action small d-inline-flex align-items-center ps-2 pe-2 ${this.groupActiveTag(item)}">\n <span $if="this.groupicon" $class="bi bi-${this.groupicon} text-body"></span>\n <div class="flex-shrink-0 px-1" $text>${item[this.grouplabelfield]}</div>\n <div class="text-muted small flex-fill text-end text-truncate" style="width: 0" $text="item[this.groupsummaryfield]"></div>\n <div slot-id="group-actions"></div>\n </div>\n <div slot-id="item" $if="item.type === \'item\'" $onclick="this.selectItem(item,index)" $class="list-group-item list-group-item-action d-inline-flex align-items-baseline ps-3 pe-2 ${this.itemActiveTag?.(item)}">\n <span $if="this.itemicon" $class="bi bi-${this.itemicon} text-body"></span>\n <div class="flex-shrink-0 px-1" $text>${item[this.labelfield]}</div>\n <div class="text-muted small flex-fill text-end text-truncate" style="width: 0" $text="item[this.summaryfield]"></div>\n <div slot-id="item-actions"></div>\n </div>\n </template>\n</div>\n')),t.register("FastGroupedList",e=>{t.getSetupFunction("List")(e),t.getSetupFunction("GroupedList")(e)},i.makeDom('\n<FastList class="list-group">\n <div slot="item">\n <div slot-id="group" $if="item.type === \'group\'" $onclick="this.selectGroup(item,index)" style="height: 36px" $class="list-group-item list-group-item-action small d-inline-flex align-items-center ps-2 pe-2 ${this.groupActiveTag(item)}">\n <span $if="this.groupicon" $class="bi bi-${this.groupicon} text-body"></span>\n <div class="flex-shrink-0 px-1" $text>${item[this.grouplabelfield]}</div>\n <div class="text-muted small flex-fill text-end text-truncate" style="width: 0" $text="item[this.groupsummaryfield]"></div>\n <div slot-id="group-actions"></div>\n </div>\n <div slot-id="item" $if="item.type === \'item\'" $onclick="this.selectItem(item,index)" $class="list-group-item list-group-item-action d-inline-flex align-items-baseline ps-3 pe-2 ${this.itemActiveTag?.(item)}">\n <span $if="this.itemicon" $class="bi bi-${this.itemicon} text-body"></span>\n <div class="flex-shrink-0 px-1" $text>${item[this.labelfield]}</div>\n <div class="text-muted small flex-fill text-end text-truncate" style="width: 0" $text="item[this.summaryfield]"></div>\n <div slot-id="item-actions"></div>\n </div>\n </div>\n</FastList>\n')),t.register("Tree",t=>{const e={parentfield:"parent",groupicon:"folder",itemicon:"file"};t.state.__watch("list",()=>{i.updateDefaults(t,e);const s={};(t.state.list||[]).forEach(e=>{var i;return(s[i=e[t.parentfield]||""]??(s[i]=[])).push(e)});const n=[],a=(e,i)=>e.forEach(e=>{var l;const o=e[t.idfield],r=!!(null==(l=s[o])?void 0:l.length);n.push({...e,_level:i,_hasChildren:r}),r&&a(s[o],i+1)});return a(s[""]||[],0),n})},i.makeDom('\n<List>\n\t<div slot="item" $class="list-group-item list-group-item-action d-flex ps-2 align-items-center ${this.itemActiveTag?.(item)}" $onclick="this.selectItem(item,index)">\n\t\t<div $style="width:${item._level * 16}px" class="flex-shrink-0"></div>\n\t\t<div $class="text-muted bi bi-${item._hasChildren?this.groupicon:this.itemicon}"></div>\n\t\t<div class="flex-grow-1 text-truncate px-2" $text="item[this.labelfield]"></div>\n\t\t<div class="text-muted small flex-fill text-end text-truncate" style="width: 0" $text="item[this.summaryfield]"></div>\n\t\t<div slot-id="item-actions"></div>\n\t</div>\n</List>\n'));const h=t.register("FastTree",e=>{t.getSetupFunction("List")(e),t.getSetupFunction("Tree")(e)},i.makeDom('\n<FastList class="list-group list-group-action">\n\t<div slot="item" $class="list-group-item list-group-item-action d-flex ps-2 align-items-center ${this.itemActiveTag?.(item)}" $onclick="this.selectItem(item,index)">\n\t\t<div $style="width:${item._level * 16}px" class="flex-shrink-0"></div>\n\t\t<div $class="text-muted bi bi-${item._hasChildren?this.groupicon:this.itemicon}"></div>\n\t\t<div class="flex-grow-1 text-truncate px-2" $text="item[this.labelfield]"></div>\n\t\t<div class="text-muted small flex-fill text-end text-truncate" style="width: 0" $text="item[this.summaryfield]"></div>\n\t\t<div slot-id="item-actions"></div>\n\t</div>\n</FastList>\n'));t.register("CollapseTree",s=>{t.attachSelectFeature(s);const n={idfield:"id",parentfield:"parent",labelfield:"label",summaryfield:"summary"};s.collapsed=e({}),s.state.__watch("list",()=>{i.updateDefaults(s,n);const t={};(s.state.list||[]).forEach(e=>{var i;return(t[i=e[s.parentfield]||""]??(t[i]=[])).push(e)});const e=[],a=(i,n,l)=>i.forEach(i=>{var o;const r=i[s.idfield],d=!!(null==(o=t[r])?void 0:o.length);e.push({...i,_level:n,_hasChildren:d,_parents:l}),d&&a(t[r],n+1,[...l,r])});return a(t[""]||[],0,[]),e})},i.makeDom('\n<List>\n\t<div slot="item" slot-id="item" $if="!item._parents.some(cur=>this.collapsed[cur])" $class="list-group-item list-group-item-action d-flex ps-2 align-items-center ${this.itemActiveTag?.(item)}" $onclick="this.selectItem(item,index)">\n\t\t<div $style="width:${item._level * 16 + 16}px; cursor: pointer;" class="text-end text-muted flex-shrink-0" $onclick="event.stopPropagation();this.collapsed[item[this.idfield]] = !this.collapsed[item[this.idfield]]">\n\t\t\t<i $if="item._hasChildren" $class="bi ${this.collapsed[item[this.idfield]] ? \'bi-caret-right-fill\' : \'bi-caret-down-fill\'}"></i>\n\t\t</div>\n\t\t<div class="flex-grow-1 text-truncate px-1" $text="item[this.labelfield]"></div>\n\t\t<div class="text-muted small flex-fill text-end text-truncate" style="width: 0" $text="item[this.summaryfield]"></div>\n\t\t<div slot-id="item-actions"></div>\n\t</div>\n</List>\n')),t.register("Nav",t=>{t.click=(e,i)=>{e.noselect||i||(n.nav=e.name),t.dispatchEvent(new CustomEvent("nav",{detail:{item:e},bubbles:!1}))}},i.makeDom('\n<div class="navbar navbar-expand bg-body-secondary px-3 pb-0 border-bottom align-items-center">\n\t<img $if="this.state.brand.image" $src="this.state.brand.image" class="me-2" style="height:30px;width:auto;max-width:300px">\n\t<i $if="this.state.brand.icon" $class="bi bi-${this.state.brand.icon} me-2"></i>\n\t<span $if="this.state.brand.label" class="me-2" $text="this.state.brand.label"></span>\n\t<div class="ms-2"></div>\n\t<div $each="this.state.list" $class="navbar-nav text-truncate ${item.type===\'fill\'?\'flex-fill\':\'\'}">\n\t\t<button $if="item.type===\'button\'" $class="nav-link ${Hash.nav===item.name?\'active\':\'\'}" $onclick="this.click(item)">\n\t\t\t<i $class="bi bi-${item.icon} me-2"></i><span $class="d-none d-${this.state.list.length>5?\'lg\':\'md\'}-inline" $text="item.label"></span>\n\t\t</button>\n\t\t<div $if="item.type===\'dropdown\'" class="dropdown">\n\t\t\t<button $class="nav-link ${Hash.nav===item.name?\'active\':\'\'}" data-bs-toggle="dropdown">\n\t\t\t\t<i $class="bi bi-${item.icon} me-2"></i><span $class="d-none d-${this.state.list.length>5?\'lg\':\'md\'}-inline" $text="item.label"></span>\n\t\t\t</button>\n\t\t\t<div class="dropdown-menu dropdown-menu-end p-3 bg-body-secondary shadow" $style="width: ${item.width || 250}px;">\n\t\t\t\t<template $each="item.list" as="subitem">\n\t\t\t\t\t<button $if="subitem.type===\'button\'" class="nav-link px-0 w-100 text-start" $onclick="this.click(subitem, true)">\n\t\t\t\t\t\t<i $class="bi bi-${subitem.icon} me-2 d-inline-block" style="width: 16px;"></i><span $text="subitem.label"></span>\n\t\t\t\t\t</button>\n\t\t\t\t\t<div $if="subitem.type===\'switch\'" class="d-flex align-items-center">\n\t\t\t\t\t\t<i $class="bi bi-${subitem.icon} me-2 d-inline-block" style="width: 16px;"></i><span $text="subitem.label"></span><div class="flex-fill"></div>\n\t\t\t\t\t\t<div class="form-switch"><input class="form-check-input mx-0" type="checkbox" $bind="subitem.bind[subitem.name]"></div>\n\t\t\t\t\t</div>\n\t\t\t\t</template>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>\n'));let v=!1,f={},g={};const b={start:(t,{onmousemove:e,onmouseup:i})=>{f={x:t.clientX,y:t.clientY,w:0,h:0},g={onmousemove:e,onmouseup:i},v=!0}};globalThis.MouseMover=b,"undefined"!=typeof document&&(document.addEventListener("mouseup",t=>{var e;v&&(v=!1,null==(e=g.onmouseup)||e.call(g,{event:t,...f}))}),document.addEventListener("mousemove",t=>{var e;v&&(f.w=t.clientX-f.x,f.h=t.clientY-f.y,null==(e=g.onmousemove)||e.call(g,{event:t,...f}))})),t.register("Resizer",t=>{t.isVertical=t.hasAttribute("vertical");const e=parseInt(t.getAttribute("min"))||10,i=parseInt(t.getAttribute("max"))||1e3,s=t.target||t.previousElementSibling;t.addEventListener("bind",e=>{void 0!==e.detail&&null!==e.detail&&(s.style[t.isVertical?"height":"width"]=e.detail+"px")});const n=(s,n,a)=>{const l=s+(t.isVertical?a:n);return l<e?e:l>i?i:l};t.addEventListener("mousedown",e=>{const i=t.isVertical?s.offsetHeight:s.offsetWidth;b.start(e,{onmousemove:({w:e,h:a})=>{const l=n(i,e,a);s.style[t.isVertical?"height":"width"]=l+"px",t.dispatchEvent(new CustomEvent("resizing",{detail:{oldSize:i,newSize:l},bubbles:!1}))},onmouseup:({w:e,h:s})=>{const a=n(i,e,s);t.dispatchEvent(new CustomEvent("resize",{detail:{oldSize:i,newSize:a},bubbles:!1})),t.dispatchEvent(new CustomEvent("change",{detail:a,bubbles:!1}))}})})},i.makeDom("\n<div $class=\"border-${this.isVertical?'top':'start'}\" $style=\"${this.isVertical?'height':'width'}:3px;cursor:${this.isVertical?'row-resize':'col-resize'}\"></div>\n"));const x=e({exitBlocks:0});if(globalThis.State=x,"undefined"!=typeof window&&window.addEventListener("beforeunload",t=>{x.exitBlocks>0&&t.preventDefault()}),globalThis.HTTP=l,globalThis.UI=r,globalThis.AutoForm=m,globalThis.MouseMover=b,"undefined"!=typeof document){const t=()=>{console.log("Base project triggering RefreshState"),a(document.documentElement)};"loading"!==document.readyState?t():document.addEventListener("DOMContentLoaded",t,!0)}export{o as APIComponent,m as AutoForm,p as FastListComponent,h as FastTreeComponent,l as HTTP,b as MouseMover,x as State,r as UI,u as VirtualScroll};
|