UNPKG

@optionfactory/ful

Version:

This is a minimalistic web components library built on top of [`@optionfactory/ftl`](https://github.com/optionfactory/ftl) to accelerate frontend development cycle-time. Strictly adhering to a no-build philosophy, it completely eliminates the downtime as

1 lines 65.7 kB
var ful=function(e,t){"use strict";class s{static encode(e,t){const r=t||s.URL_SAFE,i=e.byteLength,a=new Uint8Array(e);let n="";for(let e=0;e<i;e+=3){n+=r[a[e]>>2]+r[(3&a[e])<<4|a[e+1]>>4]+r[(15&a[e+1])<<2|a[e+2]>>6]+r[63&a[e+2]]}return i%3==2?n=n.substring(0,n.length-1):i%3==1&&(n=n.substring(0,n.length-2)),n}static decode(e,t){const r=t||s.URL_SAFE;let i=Math.floor(.75*e.length);for(let t=0;t!==e.length&&"="===e[e.length-t-1];++t)--i;const a=new Uint8Array(i);let n=0,l=0;for(;n<.75*e.length;){const t=r.indexOf(e.charAt(l++)),s=r.indexOf(e.charAt(l++)),i=r.indexOf(e.charAt(l++)),o=r.indexOf(e.charAt(l++));a[n++]=t<<2|s>>4,a[n++]=(15&s)<<4|i>>2,a[n++]=(3&i)<<6|o}return a.buffer}}s.STANDARD="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s.URL_SAFE="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";class r extends Error{constructor(e,t,s){super(e,{cause:s}),this.name="Failure",this.problems=t}dropping(e){return new r(this.message,r.dropProblemsContext(this.problems,e),this)}static dropProblemsContext(e,t){return e.map(({type:e,context:s,reason:r,details:i})=>({type:e,context:s?.startsWith(t)?s.substring(t.length):s,reason:r,details:i}))}}class i{#e;#t;constructor(e,t){this.#e=e,this.#t=t}get normalized(){return`${this.#e}/${this.#t}`}get type(){return this.#e}get subtype(){return this.#t}static parse(e){if(!e)return new i("unknown","unknown");const[t,s]=e.split(";"),[r,a]=t.trim().split("/");return new i(r.toLowerCase(),a?.toLowerCase())}}class a extends r{constructor(e,t,s,r){super(e,s,r),this.name="HttpClientError",this.status=t}dropping(e){return new a(this.message,this.status,r.dropProblemsContext(this.problems,e),this)}static of(e,t){return new a(t.message,0,[{type:e,context:null,reason:t.message,details:null}],t)}static async fromResponse(e){switch(i.parse(e.headers.get("Content-Type")).normalized){case"application/failures+json":{const t=await e.json(),s=`${e.status} ${e.statusText}: ${t.length} failures`;return new a(s,e.status,t)}case"application/problem+json":{const t=await e.json(),s=`${e.status} ${e.statusText}: ${t.title} ${t.detail}`;return new a(s,e.status,t.problems||[{type:"GENERIC_PROBLEM",context:null,reason:s,details:null}])}default:{const t=await e.text(),s=`${e.status} ${e.statusText}: ${t}`;return new a(s,e.status,[{type:"GENERIC_PROBLEM",context:null,reason:s,details:null}])}}}}class n{#s;#r;constructor(){this.#s=document.querySelector("meta[name='_csrf_header']")?.getAttribute("content"),this.#r=document.querySelector("meta[name='_csrf']")?.getAttribute("content")}async intercept(e,t,s){return this.#s&&this.#r&&t.headers.set(this.#s,this.#r),await s.proceed(e,t)}}class l{#i;constructor(e){this.#i=e}async intercept(e,t,s){const r=await s.proceed(e,t);return 401===r.status&&(window.location.href=this.#i),r}}class o{#a;constructor(){this.#a=[]}withCsrfToken(){return this.#a.push(new n),this}withRedirectOnUnauthorized(e){return this.#a.push(new l(e)),this}withInterceptors(...e){return this.#a.push(...e),this}build(){return new c(this.#a)}}class u{async intercept(e,t,s){return await fetch(new Request(e,t))}}class d{#a;#n;constructor(e,t){this.#a=e,this.#n=t}async proceed(e,t){const s=this.#a[this.#n];return await s.intercept(e,t,new d(this.#a,this.#n+1))}}class c{#a;static builder(){return new o}constructor(e){this.#a=e||[]}async exchange(e,t,s){const r=[...this.#a,...s||[],new u],i=new d(r,0),a=new URL(new Request(e).url);return await i.proceed(a,t??{})}request(e,t){return p.create(this,e,t)}get(e){return p.create(this,"GET",e)}head(e){return p.create(this,"HEAD",e)}post(e){return p.create(this,"POST",e)}put(e){return p.create(this,"PUT",e)}patch(e){return p.create(this,"PATCH",e)}delete(e){return p.create(this,"DELETE",e)}}const h=async(e,t)=>{try{return await e[t]()}catch(e){throw a.of("UNMARSHALING_PROBLEM",e)}};class p{#l;#o;#u;#d;#c;#h;#p;#a;static create(e,t,s){const[r,i=""]=s.split("?");return new p(e,t,r,new URLSearchParams(i),new Headers,void 0,{},[])}constructor(e,t,s,r,i,a,n,l){this.#l=e,this.#o=t,this.#u=s,this.#d=r,this.#h=a,this.#c=i,this.#p=n,this.#a=l}headers(e){for(const[t,s]of new Headers(e).entries())null==s?this.#c.delete(t):this.#c.set(t,s);return this}header(e,t){return null==t?this.#c.delete(e):this.#c.set(e,t),this}params(e){for(const[t,s]of new URLSearchParams(e).entries())null==s?this.#d.delete(t):this.#d.set(t,s);return this}param(e,...t){if(0===t.length||null==t[0])return this.#d.delete(e),this;for(const s of t)this.#d.append(e,s);return this}body(e){return this.#h=e,this}json(e){return this.#c.set("Content-Type","application/json"),this.#h=JSON.stringify(e),this}multipart(e){const t=new FormData;return e(new f(t)),this.#h=t,this}options(e){for(const[t,s]of Object.entries(e))this.#p[t]=s;return this}option(e,t){return this.#p[e]=t,this}interceptors(e){for(const t of e)this.#a.push(t);return this}interceptor(e){return this.#a.push(e),this}async exchange(){const e=this.#d.size?`${this.#u}?${this.#d}`:this.#u,t={...this.#p,headers:this.#c,method:this.#o,body:this.#h};return await this.#l.exchange(e,t,this.#a)}async fetch(){const e=this.#d.size?`${this.#u}?${this.#d}`:this.#u,t={...this.#p,headers:this.#c,method:this.#o,body:this.#h};try{const s=await this.#l.exchange(e,t,this.#a);if(!s.ok)throw await a.fromResponse(s);return s}catch(e){if(e instanceof r)throw e;throw a.of("CONNECTION_PROBLEM",e)}}async fetchText(){const e=await this.fetch();return await h(e,"text")}async fetchJson(){const e=await this.fetch();return await h(e,"json")}async fetchBlob(){const e=await this.fetch();return await h(e,"blob")}async fetchArrayBuffer(){const e=await this.fetch();return await h(e,"arrayBuffer")}}class f{#f;constructor(e){this.#f=e}field(e,t){return this.#f.append(e,t),this}blob(e,t,s){return this.#f.append(e,t,s),this}blobs(e,t){for(let s of t)this.#f.append(e,s);return this}json(e,t,s){const r=new Blob([JSON.stringify(t)],{type:"application/json"});return this.#f.append(e,r,s),this}}class m extends Storage{static save(e,t){localStorage.setItem(e,JSON.stringify(t))}static load(e){const t=localStorage.getItem(e);return null===t?void 0:JSON.parse(t)}static remove(e){localStorage.removeItem(e)}static pop(e){const t=m.load(e);return m.remove(e),t}}class b extends Storage{static save(e,t){sessionStorage.setItem(e,JSON.stringify(t))}static load(e){const t=sessionStorage.getItem(e);return null===t?void 0:JSON.parse(t)}static remove(e){sessionStorage.removeItem(e)}static pop(e){const t=b.load(e);return b.remove(e),t}}class g{static save(e,t,s){m.save(e,{revision:t,data:s})}static load(e,t){const s=m.load(e);if(void 0!==s){if(s.revision===t)return s.data;localStorage.removeItem(e)}}}class v{static forKeycloak(e,t,s,r){return new v(e,r??"openid profile",{auth:new URL("protocol/openid-connect/auth",t),token:new URL("protocol/openid-connect/token",t),logout:new URL("protocol/openid-connect/logout",t),registration:new URL("protocol/openid-connect/registrations",t),redirect:s})}constructor(e,t,{auth:s,token:r,registration:i,logout:a,redirect:n}){this.clientId=e,this.scope=t,this.uri={auth:s,token:r,registration:i,logout:a,redirect:n}}async action(e,t){const r=s.encode(crypto.getRandomValues(new Uint8Array(32)).buffer),i=s.encode(await crypto.subtle.digest("SHA-256",(new TextEncoder).encode(r))),a=this.clientId+s.encode(crypto.getRandomValues(new Uint8Array(16)).buffer);b.save(`${v.PKCE_AND_STATE_KEY}-${this.clientId}`,{state:a,verifier:r});const n=new URL(e);n.searchParams.set("client_id",this.clientId),n.searchParams.set("redirect_uri",this.uri.redirect),n.searchParams.set("response_type","code"),n.searchParams.set("scope",this.scope),n.searchParams.set("state",a),n.searchParams.set("code_challenge",i),n.searchParams.set("code_challenge_method","S256"),Object.entries(t||{}).forEach(e=>{n.searchParams.set(e[0],e[1])}),window.location.href=n.toString()}async registration(e){await this.action(this.uri.registration,e)}async applicationInitiatedAction(e,t){await this.action(this.uri.auth,{...t,kc_action:e})}async#m(e,t){window.history.replaceState("","",this.uri.redirect);const s=b.pop(`${v.PKCE_AND_STATE_KEY}-${this.clientId}`);if(s.state!==t)throw new Error("State mismatch");const r=await fetch(this.uri.token,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams([["client_id",this.clientId],["code",e],["grant_type","authorization_code"],["code_verifier",s.verifier],["state",s.state],["redirect_uri",this.uri.redirect]])});if(!r.ok){const e=await r.text();throw new Error("Error:"+r.status+": "+e)}const i=await r.json();return new y(this.clientId,i,this.uri)}async ensureLoggedIn(){const e=new URL(window.location.href),t=e.searchParams.get("code");if(t&&b.load(`${v.PKCE_AND_STATE_KEY}-${this.clientId}`)){const s=e.searchParams.get("state");return await this.#m(t,s)}return await this.action(this.uri.auth,{}),null}}v.PKCE_AND_STATE_KEY="state-and-verifier";class y{static parseToken(e){const[t,r,i]=e.split("."),a=new TextDecoder("utf-8");return{header:JSON.parse(a.decode(s.decode(t,s.STANDARD))),payload:JSON.parse(a.decode(s.decode(r,s.STANDARD))),signature:i}}constructor(e,t,{token:s,logout:r,redirect:i}){this.clientId=e,this.token=t,this.idToken=y.parseToken(t.id_token),this.accessToken=y.parseToken(t.access_token),this.refreshToken=y.parseToken(t.refresh_token),this.uri={token:s,logout:r,redirect:i},this.refreshCallback=null}onRefresh(e){this.refreshCallback=e}async refresh(){const e=await fetch(this.uri.token,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams([["client_id",this.clientId],["grant_type","refresh_token"],["refresh_token",this.token.refresh_token]])});if(!e.ok){const t=await e.text();throw new Error("Error:"+e.status+": "+t)}const t=await e.json();this.token=t,this.idToken=y.parseToken(t.id_token),this.accessToken=y.parseToken(t.access_token),this.refreshToken=y.parseToken(t.refresh_token),this.refreshCallback&&this.refreshCallback(this.token,this.accessToken,this.refreshToken)}shouldBeRefreshed(e){const t=(new Date).getTime(),s=1e3*this.refreshToken.payload.exp;return!(t>s)&&t-e>s}async refreshIf(e){this.shouldBeRefreshed(e)&&await this.refresh()}logout(){const e=new URL(this.uri.logout);e.searchParams.set("post_logout_redirect_uri",this.uri.redirect),e.searchParams.set("id_token_hint",this.token.id_token),window.location.href=e.toString()}bearerToken(){return`Bearer ${this.token.access_token}`}interceptor(e,t){return new w(this,e,t)}}class w{#b;#g;#v;constructor(e,t,s){this.#b=e,this.#g=t||2e3,this.#v=s||3e4}async intercept(e,t,s){await this.#b.refreshIf(this.#g),t.headers.set("Authorization",this.#b.bearerToken());const r=await s.proceed(e,t);return await this.#b.refreshIf(this.#v),r}}class E{static async fireAsync(e,t,s){e.dispatchEvent(t);const r=t.async?.promises??[],i=s?.mode??"broadcast";if("pipeline"===i&&r.length>1)throw new Error(`[AsyncEvents] Event "${t.type}" is configured in 'pipeline' mode and expects at most one async listener, but ${r.length} listeners were triggered on this element.`);if("delegate"===i&&1!==r.length)throw new Error(`[AsyncEvents] Event "${t.type}" is configured in 'delegate' mode and requires exactly one async listener, but ${r.length} were registered.`);return"broadcast"===i?Promise.all(r):Promise.resolve(r[0])}static asyncOn(e,t,s,r){const i=async e=>{const t=e;t.async||(t.async={promises:[]});const{promise:r,resolve:i,reject:a}=Promise.withResolvers();t.async.promises.push(r);try{i(await s(t))}catch(e){a(e)}};return e.addEventListener(t,i,r),i}static asyncOff(e,t,s,r){e.removeEventListener(t,s,r)}static mixInto(...e){for(const t of e)Object.assign(t.prototype,{async fireAsync(e,t){return await E.fireAsync(this,e,t)},asyncOn(e,t,s){return E.asyncOn(this,e,t,s)},asyncOff(e,t,s){E.asyncOff(this,e,t,s)}})}}class A{static sleep(e){return new Promise(t=>setTimeout(t,e))}static DEBOUNCE_DEFAULT=0;static DEBOUNCE_IMMEDIATE=1;static debounce(e,t,s){const r=s??A.DEBOUNCE_DEFAULT;let i=null,a=[],n=0;const l=()=>{const s=(new Date).getTime()-n;e>s?i=setTimeout(l,e-s):(i=null,r!==A.DEBOUNCE_IMMEDIATE&&t(...a),null===i&&(a=[]))};return[function(){a=[...arguments],n=(new Date).getTime(),null===i&&(i=setTimeout(l,e),r===A.DEBOUNCE_IMMEDIATE&&t(...a))},()=>clearTimeout(i)]}static THROTTLE_DEFAULT=0;static THROTTLE_NO_LEADING=1;static THROTTLE_NO_TRAILING=2;static throttle(e,t,s){const r=s??A.THROTTLE_DEFAULT;let i=null,a=[],n=0;const l=()=>{n=r&A.THROTTLE_NO_LEADING?0:(new Date).getTime(),i=null,t(...a),null===i&&(a=[])};return[function(){const s=(new Date).getTime();!n&&r&A.THROTTLE_NO_LEADING&&(n=s);const o=e-(s-n);a=[...arguments],o<=0||o>e?(null!==i&&(clearTimeout(i),i=null),n=s,t(...a),null===i&&(a=[])):null!==i||r&A.THROTTLE_NO_TRAILING||(i=setTimeout(l,o))},()=>clearTimeout(i)]}}class x{static flatten(e,t,s){return Object.keys(e).reduce((r,i)=>{const a=t.length?t+"."+i:i;return s.has(a)||"object"!=typeof e[i]||null===e[i]?r[a]=e[i]:Object.assign(r,x.flatten(e[i],a,s)),r},{})}static providePath(e,t,s){const r=t.split(".").map(e=>/^[0-9]+$/.test(e)?+e:e);let i=e??{},a=null;for(let t=0;;++t){const n=r[t],l=r[t-1];if(Number.isInteger(n)&&!Array.isArray(i)&&(null!==a?a[l]=i=[]:e=i=[]),t===r.length-1)return i[n]=void 0!==s?s:n in i?i[n]:null,e;void 0===i[n]&&(i[n]={}),a=i,i=i[n]}}static extract(e){if("radio"===e.getAttribute("type")){if(!e.checked)return;return"boolean"===e.dataset.fulBindType?"true"===e.value:e.value}return"checkbox"===e.getAttribute("type")?e.checked:"boolean"===e.dataset.fulBindType?e.value?"true"===e.value:null:!("INPUT"!==e.tagName&&"SELECT"!==e.tagName||""!==e.value&&void 0!==e.value)?null:e.value}static extractFrom(e,t){let s={};for(const r of e.elements)!r.hasAttribute("name")||r.matches(":disabled")&&r!==t||(s=x.providePath(s,r.getAttribute("name"),x.extract(r)));return s}static mutate(e,t){"radio"!==e.getAttribute("type")?"checkbox"!==e.getAttribute("type")?e.value=t:e.checked=t:e.checked=e.getAttribute("value")===t}static mutateIn(e,t){const s=Array.from(e.elements).map(e=>e.getAttribute("name")).filter(e=>e);for(const[r,i]of Object.entries(x.flatten(t,"",new Set(s))))for(const t of e.querySelectorAll(`[name='${CSS.escape(r)}']`))x.mutate(t,i)}static errors(e,t,s){const r=t.filter(e=>"FIELD_ERROR"===e.type||"INVALID_FORMAT"===e.type),i=t.filter(e=>"FIELD_ERROR"!==e.type&&"INVALID_FORMAT"!==e.type);e.querySelectorAll("[name]").forEach(e=>e.setCustomValidity?.("")),e.querySelectorAll("ful-errors").forEach(e=>{e.replaceChildren(),e.setAttribute("hidden","")}),r.forEach(t=>{const s=t.context.replace("[",".").replace("].",".").replace("]","").split(".");for(let r=s.length;0!=r;--r){const i=s.slice(0,r).join("."),a=s.slice(r,s.length).join(".");e.querySelectorAll(`[name='${CSS.escape(i)}']`).forEach(e=>e.setCustomValidity?.(t.reason,a))}}),e.querySelectorAll("ful-errors").forEach(e=>{e.innerText=i.map(e=>e.reason).join("\n"),0!==i.length&&e.removeAttribute("hidden")}),0!=t.length&&s&&Array.from(e.querySelectorAll(":invalid")).sort((e,t)=>e.getBoundingClientRect().y-t.getBoundingClientRect().y)[0]?.focus()}}class T{#y;#w;#o;#E;#A;constructor(e,t,s,r,i){this.#y=e,this.#w=t,this.#o=s,this.#E=r,this.#A=i}prepare(e,t){return this.#E(e,t)}async submit(e,t){return await this.#y.request(this.#o,this.#w).json(e).fetch()}transform(e,t){return this.#A(e,t)}}class S{#E;#A;constructor(e,t){this.#E=e,this.#A=t}async prepare(e,t){return await this.#E(e,t)}async submit(e,t,s){return s}async transform(e,t){return await this.#A(e,t)}}class q{static create(e,s){const r=t.registry.component("http-client"),i=e.hasAttribute("request-mapper")?t.registry.component(e.getAttribute("request-mapper")):e=>e,a=e.hasAttribute("response-mapper")?t.registry.component(e.getAttribute("response-mapper")):e=>e,n=e.getAttribute("action");if(!n)return new S(i,a);const l=e.getAttribute("method")??"POST";return new T(r,n,l,i,a)}}class k extends t.ParsedElement{form;render(){const e=this.form=document.createElement("form");e.setAttribute("novalidate",""),t.Attributes.forward("form-",this,e),e.replaceChildren(...this.childNodes),e.addEventListener("submit",async e=>{e.preventDefault(),e.stopPropagation(),await this.submit(e.submitter??void 0)}),this.hasAttribute("clear-invalid-on-change")&&this.addEventListener("change",e=>{e.target.setCustomValidity?.("")}),this.replaceChildren(e)}async submit(e){this.spinner(!0);try{const s=t.registry.component(this.getAttribute("loader")??"loaders:form").create(this),i=x.extractFrom(this.form,e);let a=await s.prepare(i,this);try{const t=new CustomEvent("submit",{bubbles:!0,cancelable:!0,detail:{submitter:e,values:i,request:a}});if(!this.dispatchEvent(t))return;this.errors=[];const r=new CustomEvent("submit:requested",{bubbles:!0,cancelable:!1,detail:{submitter:e,values:t.detail.values,request:t.detail.request}});let n=await E.fireAsync(this,r,{mode:"pipeline"});a=r.detail.request,n=await s.submit(a,this,n);const l=await s.transform(n,this);this.dispatchEvent(new CustomEvent("submit:success",{bubbles:!0,cancelable:!1,detail:{submitter:e,values:i,request:a,response:l}}))}catch(t){this.dispatchEvent(new CustomEvent("submit:failure",{bubbles:!0,cancelable:!1,detail:{submitter:e,values:i,request:a,exception:t}})),t instanceof r&&(this.errors=t.problems),console.warn("failed to submit form",this,"reason:",t)}}finally{this.spinner(!1)}}reset(){this.form.reset()}spinner(e){this.querySelectorAll("ful-spinner").forEach(t=>{t.hidden=!e}),this.querySelectorAll("input,button").forEach(t=>{const s=t;"submit"!==s.type&&"reset"!==s.type||(s.disabled=e)})}set values(e){x.mutateIn(this.form,e)}get values(){return x.extractFrom(this.form)}set errors(e){x.errors(this.form,e,this.hasAttribute("scroll-on-error"))}}class L extends t.ParsedElement{static observed=["value","readonly:presence","required:presence"];static slots=!0;static template='\n <div class="form-label">\n <label>{{{{ slots.default }}}}</label>\n {{{{ slots.info }}}}\n </div>\n <div class="input-group">\n <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>\n {{{{ slots.before }}}}\n <input data-tpl-if="type != \'textarea\'" class="form-control" data-tpl-type="type" placeholder=" " form="">\n <textarea data-tpl-if="type == \'textarea\'" class="form-control" placeholder=" " form=""></textarea>\n {{{{ slots.after }}}}\n <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>\n </div>\n <ful-field-error></ful-field-error>\n ';static formAssociated=!0;_input;_fieldError;constructor(){super(),this.internals=this.attachInternals(),this.internals.role="presentation"}_type(){return this.getAttribute("type")??"text"}_fragment(e,t){return this.template().withOverlay({type:e,slots:t}).render()}render({slots:e,observed:s,disabled:r,skipObservedSetup:i}){const a=this._type(),n=this._fragment(a,e);this._input=n.querySelector("input,textarea"),t.Attributes.forward("input-",this,this._input),i||(this.disabled=r,this.readonly=s.readonly,this.required=s.required,this.value=s.value),this._input.addEventListener("keydown",e=>{if("Enter"!==e.key||"textarea"===this._type())return;const t=this.internals.form;if(!t)return;const s=Array.from(t.querySelectorAll("button:not(:disabled), input:not(:disabled)")).find(e=>"submit"===e.type);t.requestSubmit(s)}),this._input.addEventListener("input",e=>{const t=this.getAttribute("mask");if(!t)return;const s=e.target.value,r=s.replace(new RegExp(t,"g"),"");if(s===r)return;const i=e.target.selectionStart,a=s.length-r.length;e.target.value=r,e.target.setSelectionRange(i-a,i-a)}),this._input.addEventListener("change",e=>{e.stopPropagation(),this.dispatchEvent(new CustomEvent("change",{bubbles:!0,cancelable:!1,detail:{value:this.value}}))});const l=n.querySelector("label");l.addEventListener("click",()=>this.focus()),this._fieldError=n.querySelector("ful-field-error"),this._input.ariaDescribedByElements=[this._fieldError],this._input.ariaLabelledByElements=[l],this.replaceChildren(n)}get value(){return""===this._input.value?null:this._input.value}set value(e){this._input.value=""===e?null:e}get readonly(){return this._input.readOnly}set readonly(e){this._input.readOnly=e,this.reflect(()=>{t.Attributes.toggle(this,"readonly",e)})}get disabled(){return this._input.hasAttribute("disabled")}set disabled(e){t.Attributes.toggle(this._input,"disabled",e)}get required(){return"true"===this._input.getAttribute("aria-required")}set required(e){t.Attributes.set(this._input,"aria-required",e?"true":null),this.reflect(()=>{t.Attributes.toggle(this,"required",e)})}focus(e){this._input.focus(e)}setCustomValidity(e){if(!e)return this.internals.setValidity({}),void(this._fieldError.innerText="");this.internals.setValidity({customError:!0}," "),this._fieldError.innerText=e}formResetCallback(){this.value=this.unmarshal("value",this.getAttribute("value"))}}class _ extends t.ParsedElement{render(){const e=this.innerHTML.trim();if(""===e)return void(this.innerHTML=this.getAttribute("default")??"");const t=this.getAttribute("locale")??Intl.DateTimeFormat().resolvedOptions().locale,s=new Intl.DateTimeFormat(t,{year:"numeric",month:"numeric",day:"numeric"}),[r,i,a]=e.split("-").map(Number);this.innerHTML=s.format(new Date(r,i-1,a))}}class O extends t.ParsedElement{render(){const e=this.innerHTML.trim();if(""===e)return void(this.innerHTML=this.getAttribute("default")??"");const t=this.getAttribute("locale")??Intl.DateTimeFormat().resolvedOptions().locale,s=new Intl.DateTimeFormat(t,{year:"numeric",month:"numeric",day:"numeric",hour:"numeric",minute:"numeric",second:"numeric",hour12:!1});this.innerHTML=s.format(new Date(O.isoToLocal(e)))}static isoToLocal(e){const t=new Date(e),s=(e,t)=>String(t).padStart(e,"0");return`${`${t.getFullYear()}-${s(2,t.getMonth()+1)}-${s(2,t.getDate())}`}T${`${s(2,t.getHours())}:${s(2,t.getMinutes())}:${s(2,t.getSeconds())}.${s(3,t.getMilliseconds())}`}`}}class R extends L{static observed=["value","readonly:presence","required:presence","min","max","step"];_type(){return"date"}render(e){const{observed:t}=e;super.render(e),this.min=t.min,this.max=t.max,this.step=t.step}get min(){const e=this._input.min;return""===e?null:e}set min(e){this._input.min=R.#x(e)}get max(){const e=this._input.max;return""===e?null:e}set max(e){this._input.max=R.#x(e)}get step(){const e=this._input.step;return""===e?null:e}set step(e){this._input.step=e??""}static#x(e){if(!e)return"";if("now"===e)return(new Date).toISOString().split("T")[0];const t=/^([+-])(\d+)([dmy])$/.exec(e);if(!t)return e;const s="-"===t[1]?-1:1,r=+t[2],i=new Date;switch(t[3]){case"d":i.setDate(i.getDate()+r*s);break;case"m":i.setMonth(i.getMonth()+r*s);break;case"y":i.setFullYear(i.getFullYear()+r*s)}return i.toISOString().split("T")[0]}}class C extends R{_type(){return"time"}}class z extends L{static observed=["value","readonly:presence","required:presence","min","max","step"];_type(){return"datetime-local"}render(e){const{observed:t}=e;super.render(e),this.min=t.min,this.max=t.min,this.step=t.min}get value(){const e=this._input.value;return""===e?null:new Date(e).toISOString()}set value(e){this._input.value=e?O.isoToLocal(e):""}get min(){const e=this._input.min;return""===e?null:new Date(e).toISOString()}set min(e){this._input.min=e?O.isoToLocal(e):""}get max(){const e=this._input.max;return""===e?null:new Date(e).toISOString()}set max(e){this._input.max=e?O.isoToLocal(e):""}get step(){const e=this._input.step;return""===e?null:e}set step(e){this._input.step=e??""}}class I extends L{static l10n={en:{dropzonelabel:"Click or drop your files here",unaccepptablefiletype:"Only files of type {0} are supported",maxfilesizeexceeded:"Maximum supported file size is {0}",maxtotalsizeexceeded:"Maximum supported total file size is {0}",maxfilesexceeded:"Maximum files count exceeded"},it:{dropzonelabel:"Clicca o trascina i file qui",unaccepptablefiletype:"Solo i file di tipo {0} sono supportati",maxfilesizeexceeded:"La dimensione massima di un file è di {0}",maxtotalsizeexceeded:"La dimensione massima complessiva dei file è di {0}",maxfilesexceeded:"Numero massimo di file superato"}};static observed=["value","readonly:presence","required:presence","accept:csv","multiple:presence","itemlist:presence","dropzone:presence","maxfiles:number","maxfilesize:number","maxtotalsize:number"];#T;#S;#q;#k;_type(){return"file"}static template='\n <div class="form-label">\n <label>{{{{ slots.default }}}}</label>\n {{{{ slots.info }}}}\n </div>\n <div class="input-group">\n <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>\n {{{{ slots.before }}}}\n <input class="form-control" data-tpl-type="type" placeholder=" " form="">\n {{{{ slots.after }}}}\n <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>\n </div>\n <div data-ref="dropzone" class="dropzone" data-tpl-if="slots.dropzone">\n {{{{ slots.dropzone }}}}\n </div>\n <div data-ref="dropzone" class="default-dropzone" data-tpl-if="!slots.dropzone">\n {{ #l10n:t(\'dropzonelabel\') }}\n </div>\n <ful-item-list></ful-item-list>\n <ful-field-warnings></ful-field-warnings>\n <ful-field-error></ful-field-error>\n ';static templates={items:'\n <ful-item data-tpl-each="files" data-tpl-var="file" data-tpl-data-name="file.name">\n <div>{{ file.name }}</div>\n <div>{{ #bytes:format(file.size) }}</div>\n <button type="button" class="btn btn-sm btn-outline-danger bi bi-x-lg" alt="Rimuovi"></button>\n </ful-item>\n ',warning:"<ful-field-warning>{{ #l10n:t(key, args) }}</ful-field-warning>"};render(e){const{observed:t}=e;super.render(e),this.#S=this.querySelector("ful-item-list"),this.#q=this.querySelector("[data-ref=dropzone]"),this.#k=this.querySelector("ful-field-warnings"),this.accept=t.accept,this.multiple=t.multiple,this.itemlist=t.itemlist,this.dropzone=t.dropzone,this.maxfiles=t.maxfiles,this.maxfilesize=t.maxfilesize,this.maxtotalsize=t.maxtotalsize,this.#k.addEventListener("animationend",e=>{e.target.remove()}),this.#S.addEventListener("click",e=>{if(!e.target.closest("button"))return;const t=e.target.closest("ful-item").dataset.name,s=new DataTransfer;[...this.files].filter(e=>e.name!==t).forEach(e=>s.items.add(e)),this.files=s.files,this.#L()}),this.#q.addEventListener("click",e=>{this.querySelector("input")?.click()}),this.#q.addEventListener("dragover",e=>{e.preventDefault()}),this.#q.addEventListener("drop",e=>{e.preventDefault();const t=new DataTransfer;[...e.dataTransfer.items].filter(e=>"file"===e.kind).forEach(e=>t.items.add(e.getAsFile())),this.files=t.files,this.#L()}),this._input.addEventListener("change",e=>{this.#L()})}#_(e){return e>1048576?Math.round(e/1024/1024*100)/100+"MiB":e>1024?Math.round(e/1024*100)/100+"KiB":`${e}B`}#L(){this.setCustomValidity(),this.#O(),this.#R(),this.#C(),this.#z(),this.template("items").withOverlay({files:this.files}).withModule("bytes",{format:this.#_}).renderTo(this.#S)}warning(e,t){this.template("warning").withOverlay({key:e,args:t}).renderTo(this.#k)}#O(){if(!this.#T.length)return;const e=[...this.files].filter(e=>!this.#T.some(t=>e.name.toLowerCase().endsWith(t.toLowerCase())));if(0===e.length)return;this.warning("unaccepptablefiletype",this.#T.join(", "));const t=new DataTransfer;[...this.files].filter(t=>!e.includes(t)).forEach(e=>t.items.add(e)),this.files=t.files}#z(){if(null===this.#I)return;if(this.files.length<=this.#I)return;this.warning("maxfilesexceeded");const e=new DataTransfer;this.files=e.files}#R(){if(null===this.#D)return;const e=[...this.files].filter(e=>e.size>this.#D);if(0===e.length)return;this.warning("maxfilesizeexceeded",this.#_(this.#D));const t=new DataTransfer;[...this.files].filter(t=>!e.includes(t)).forEach(e=>t.items.add(e)),this.files=t.files}#C(){if(null===this.#P)return;[...this.files].reduce((e,t)=>e+t.size,0)<=this.#P||(this.warning("maxtotalsizeexceeded",this.#_(this.#P)),this.files=(new DataTransfer).files)}get accept(){return this.#T}set accept(e){this._input.accept=e.join(","),this.#T=e,this.reflect(()=>{this.setAttribute("accept",this._input.accept)})}get multiple(){return this._input.multiple}set multiple(e){this._input.multiple=e,this.reflect(()=>{t.Attributes.toggle(this,"multiple",e)})}get files(){return this._input.files}set files(e){this._input.files=e}get file(){return this.files[0]??null}set file(e){const t=new DataTransfer;t.items.add(e),this.files=t.files}get value(){const e=Array.from(this._input.files).map(e=>e.name);return this.multiple?e:e[0]??null}set value(e){}get totalsize(){return Array.from(this.files).reduce((e,t)=>e+t.size,0)}#I;get maxfiles(){return this.#I}set maxfiles(e){this.#I=e,this.reflect(()=>{t.Attributes.set(this,"maxfiles",e)})}#D;get maxfilesize(){return this.#D}set maxfilesize(e){this.#D=e,this.reflect(()=>{t.Attributes.set(this,"maxfilesize",e)})}#P;get maxtotalsize(){return this.#P}set maxtotalsize(e){this.#P=e,this.reflect(()=>{t.Attributes.set(this,"maxtotalsize",e)})}#N;get itemlist(){return this.#N}set itemlist(e){this.#N=e,this.reflect(()=>{t.Attributes.toggle(this,"itemlist",e)})}#M;get dropzone(){return this.#M}set dropzone(e){this.#M=e,this.reflect(()=>{t.Attributes.toggle(this,"dropzone",e)})}}class D{#y;#w;#o;#A;#B;#F;#$;constructor({http:e,url:t,method:s,responseMapper:r,prefetch:i,revision:a}){this.#y=e,this.#w=t,this.#o=s,this.#A=r,this.#B=i,this.#F=a,this.#$=null}async prefetch(){this.#B&&await this.#U()}async exact(...e){return await this.#U(),this.#$.filter(([t,s])=>e.some(e=>e==t))}async load(e){return await this.#U(),this.#$.filter(([t,s])=>(s??"").toLowerCase().includes(e?.toLowerCase()))}async reconfigureUrl(e){this.#$=null,this.#w=e}async#U(){if(null!==this.#$)return;const e=await D.#H(this.#y,this.#o,this.#w,this.#F);this.#$=this.#A(e)}static async#H(e,t,s,r){const i=`${t}@${s}`;if(null!==r){const e=g.load(i,r);if(void 0!==e)return e}const a=await e.request(t,s).fetchJson();return null!==r&&g.save(i,r,a),a}}class P{#y;#w;#o;#A;constructor({http:e,url:t,method:s,responseMapper:r}){this.#y=e,this.#w=t,this.#o=s,this.#A=r}async exact(...e){const t=await this.#y.request(this.#o,this.#w).param("k",...e).fetchJson();return this.#A(t)}async load(e){const t=await this.#y.request(this.#o,this.#w).param("s",e).fetchJson();return this.#A(t)}}class N{#$;constructor(e){this.#$=e}update(e){this.#$=e}exact(...e){return this.#$.filter(([t,s])=>e.some(e=>e==t))}load(e){return this.#$.filter(([t,s])=>(s??"").toLowerCase().includes(e?.toLowerCase()))}}class M{static create(e,s){if(!e.hasAttribute("src")){const e=Array.from(s.options?.querySelectorAll("option")??[]).map(e=>[e.getAttribute("value")??e.innerText.trim(),e.innerText.trim()]);return new N(e)}const r=t.registry.component("http-client"),i=M.#j(e);return"chunked"==e.getAttribute("mode")?new P({http:r,url:e.getAttribute("src"),method:e.getAttribute("method")??"POST",responseMapper:i}):new D({http:r,url:e.getAttribute("src"),method:e.getAttribute("method")??"POST",responseMapper:i,prefetch:e.hasAttribute("preload"),revision:e.getAttribute("revision")})}static#j(e){return e.hasAttribute("k-expr")&&e.hasAttribute("l-expr")?s=>t.registry.evaluator().withOverlay(s).evaluateExpression(e.getAttribute("d-expr")??"self").map(s=>{const r=t.registry.evaluator().withOverlay(s);return[r.evaluateExpression(e.getAttribute("k-expr")),r.evaluateExpression(e.getAttribute("l-expr")),r.evaluateExpression(e.getAttribute("m-expr")??"self")]}):e.hasAttribute("response-mapper")?t.registry.component(e.getAttribute("response-mapper")):e=>e}}class B extends t.ParsedElement{static slots=!0;static template='\n <ful-spinner class="centered" hidden></ful-spinner>\n <menu tabindex="-1" hidden></menu>\n ';static templates={options:'\n <li data-tpl-each="self" data-tpl-selected="index == 0" data-tpl-value="index">\n {{ label }}\n </li>\n '};#V;#G;#J;#p=new Map;render({slots:e}){const s=this.template().render();this.#J=t.Fragments.isBlank(e.default)?this.template("options"):t.Templates.fromFragment(e.default),this.#V=s.querySelector("ful-spinner"),this.#G=s.querySelector("menu"),this.#G.addEventListener("click",e=>{e.stopPropagation();const t=e.target.closest("li");t?this.#W(t):this.hide()}),this.replaceChildren(s)}acceptSelection(){const e=this.#G.querySelector("[selected]")??this.#G.firstElementChild;this.#W(e)}update(e){if(void 0===e)throw new Error("null data");this.#p=new Map(e.map((e,t)=>[String(t),e]));const t=e.map(([e,t,s],r)=>({index:r,key:e,label:t,metadata:s}));this.#J.withOverlay(t).renderTo(this.#G)}#W(e){const t=e.getAttribute("value"),s=this.#p.get(t);this.hide(),this.dispatchEvent(new CustomEvent("change",{bubbles:!0,cancelable:!1,detail:{index:t,data:s}}))}hide(){this.setAttribute("hidden","")}get shown(){return!this.hasAttribute("hidden")}async show(e){this.removeAttribute("hidden"),this.#G.setAttribute("hidden",""),this.#V.removeAttribute("hidden");try{const t=await e();this.update(t)}finally{this.#V.setAttribute("hidden",""),this.#G.removeAttribute("hidden")}}async moveOrShow(e,t){if(this.shown){const t=this.#G.querySelector("[selected]")??this.#G.firstElementChild,s=t[(e?"next":"previous")+"ElementSibling"];return void(s&&(t.removeAttribute("selected"),s.setAttribute("selected",""),s.scrollIntoView({block:"nearest",behavior:"smooth"})))}await this.show(t)}}class F extends t.ParsedElement{static observed=["value:csvm","readonly:presence","required:presence","itemlist:presence"];static slots=!0;static template='\n <div class="form-label">\n <label>{{{{ slots.default }}}}</label>\n {{{{ slots.info }}}}\n </div>\n <div class="input-group flex-nowrap" tabindex="-1">\n <span data-tpl-if="slots.ibefore" class="input-group-text">{{{{ slots.ibefore }}}}</span>\n {{{{ slots.before }}}}\n <div class="ful-select-input-container">\n <div class="ful-select-input">\n <badges></badges>\n <input type="text" form="">\n </div>\n <ful-dropdown hidden popover="manual">{{{{ slots.dropdown }}}}</ful-dropdown>\n </div>\n {{{{ slots.after }}}}\n <span data-tpl-if="slots.iafter" class="input-group-text">{{{{ slots.iafter }}}}</span>\n </div>\n <ful-item-list></ful-item-list>\n <ful-field-error></ful-field-error>\n ';static templates={items:'\n <ful-item data-tpl-each="entries" data-tpl-var="entry" data-tpl-data-key="entry[0]">\n <div>{{ entry[1][0] }}</div>\n <button type="button" class="btn btn-sm btn-outline-danger bi bi-x-lg"></button>\n </ful-item>\n '};static formAssociated=!0;internals;#K;#Y;#Q;#X;#S;#Z;#ee;#te=new Map;constructor(){super(),this.internals=this.attachInternals(),this.internals.role="presentation"}async render({slots:e,observed:s,disabled:r}){const i=this.getAttribute("name");this.#K=t.registry.component(this.getAttribute("loader")??"loaders:select").create(this,{options:e.options}),this.#Z=this.hasAttribute("multiple"),await(this.#K.prefetch?.());const a=this.template().withOverlay({slots:e,name:i}).render();this.#X=a.querySelector("input"),this.#S=a.querySelector("ful-item-list"),t.Attributes.forward("input-",this,this.#X),this.#Y=a.querySelector("badges"),this.value=s.value,this.disabled=r,this.readonly=s.readonly,this.required=s.required,this.itemlist=s.itemlist,this.#Q=a.querySelector("ful-dropdown");const n=a.querySelector("label");n.addEventListener("click",()=>this.focus()),this.#ee=a.querySelector("ful-field-error"),this.#X.ariaDescribedByElements=[this.#ee],this.#X.ariaLabelledByElements=[n];const l=this,[o,u]=A.throttle(400,()=>l.#Q.show(()=>l.#K.load(l.#X.value)));this.addEventListener("click",e=>{e.target.matches("input")||this.disabled||this.readonly||(this.#Q.shown?this.#Q.hide():(this.#X.focus(),o()))}),this.#S.addEventListener("click",e=>{if(e.stopPropagation(),!e.target.closest("button"))return;if(this.disabled||this.readonly)return;const t=[...this.#S.children].indexOf(e.target.closest("ful-item"));-1!==t&&(this.#te.delete(Array.from(this.#te.keys())[t]),this.#se(),this.#re())}),this.#Y.addEventListener("click",e=>{if(e.stopPropagation(),this.disabled||this.readonly)return;const t=[...this.#Y.children].indexOf(e.target);-1!==t&&(this.#te.delete(Array.from(this.#te.keys())[t]),this.#se(),this.#re())}),this.#X.addEventListener("change",e=>{e.stopPropagation()}),this.#X.addEventListener("blur",e=>{e.stopPropagation(),e.relatedTarget&&this.contains(e.relatedTarget)||(u(),this.#Q.hide(),this.#X.value="")}),this.#X.addEventListener("keydown",e=>{if(!this.disabled&&!this.readonly)switch(e.code){case"ArrowUp":e.preventDefault(),this.#Q.moveOrShow(!1,()=>l.#K.load(l.#X.value));break;case"ArrowDown":e.preventDefault(),this.#Q.moveOrShow(!0,()=>l.#K.load(l.#X.value));break;case"Escape":this.#Q.hide();break;case"Enter":e.preventDefault(),this.#Q.acceptSelection(),this.#X.value="";break;case"Backspace":this.#te.size&&0===this.#X.selectionStart&&0===this.#X.selectionEnd&&(this.#te.delete(Array.from(this.#te.keys()).pop()),this.#se(),this.#re());break;case"Tab":this.#Q.hide(),u()}}),this.#X.addEventListener("input",e=>{e.stopPropagation(),this.disabled||this.readonly||o()}),this.#Q.addEventListener("change",e=>{e.stopPropagation(),this.#Z||this.#te.clear(),this.#te.set(e.detail.data[0],e.detail.data.slice(1)),this.#se(),this.#re(),this.#X.focus(),this.#Q.hide(),this.#X.value=""}),this.replaceChildren(a)}async withLoader(e){return await e(this.#K)}#se(){const e=[...this.#te.entries()].map(e=>({key:e[0],label:e[1][0],metadata:e[1].slice(1)})),t=this.#Z?e:e[0]??null;this.dispatchEvent(new CustomEvent("change",{bubbles:!0,cancelable:!1,detail:{value:t}}))}#re(){const e=Array.from(this.#te.entries()).map(([e,t])=>{const s=document.createElement("badge");return s.setAttribute("role","button"),s.setAttribute("value",e),s.innerText=t[0],s});this.#Y.replaceChildren(),this.#Y.append(...e),this.#S.replaceChildren(),this.template("items").withOverlay({entries:this.#te.entries()}).renderTo(this.#S)}set value(e){if(null===e)return this.#te=new Map,void this.#re();(async()=>{const t=await(this.#Z?this.#K.exact(...e):this.#K.exact(e));this.#te=new Map(t.map(e=>[e[0],e.slice(1)])),this.#re()})()}get value(){return this.#Z?[...this.#te.keys()]:[...this.#te.keys()][0]??null}get entry(){return this.#Z?[...this.#te.entries()]:[...this.#te.entries()][0]??null}get disabled(){return this.#X.hasAttribute("disabled")}set disabled(e){t.Attributes.toggle(this.#X,"disabled",e)}get readonly(){return this.#X.readOnly}set readonly(e){this.#X.readOnly=e,this.reflect(()=>{t.Attributes.toggle(this,"readonly",e)})}get required(){return"true"===this.#X.getAttribute("aria-required")}set required(e){t.Attributes.set(this.#X,"aria-required",e?"true":null),this.reflect(()=>{t.Attributes.toggle(this,"required",e)})}#N;get itemlist(){return this.#N}set itemlist(e){this.#N=e,this.reflect(()=>{t.Attributes.toggle(this,"itemlist",e)})}focus(e){this.#X.focus(e)}setCustomValidity(e){if(!e)return this.internals.setValidity({}),void(this.#ee.innerText="");this.internals.setValidity({customError:!0}," "),this.#ee.innerText=e}}class $ extends t.ParsedElement{static observed=["value","readonly:presence","required:presence"];static slots=!0;static template='\n <fieldset>\n <legend class="form-label">\n {{{{ slots.default }}}}\n </legend>\n <header data-tpl-if="slots.header">\n {{{{ slots.header }}}}\n </header>\n <section>\n <div class="label-wrapper" data-tpl-each="inputsAndLabels" data-tpl-var="ial">\n <label>\n {{{{ ial[0] }}}}\n <div>{{{{ ial[1] }}}}</div>\n </label>\n </div>\n </section>\n <ful-field-error></ful-field-error>\n <footer data-tpl-if="slots.footer">\n {{{{ slots.footer }}}}\n </footer>\n </fieldset>\n ';static formAssociated=!0;#ie;#ee;#ae;#ne;constructor(){super(),this.internals=this.attachInternals(),this.internals.role="radiogroup"}render({slots:e,observed:s,disabled:r}){const i=this.getAttribute("name")??t.Attributes.uid("ful-radiogroup"),a=Array.from(e.default.querySelectorAll("ful-radio")),n=a.map(e=>{const s=document.createElement("input");s.setAttribute("type","radio"),t.Attributes.forward("input-",this,s),t.Attributes.forward("",e,s),s.setAttribute("name",`${i}-ignore`),s.setAttribute("form",""),s.addEventListener("change",e=>{e.stopPropagation(),this.dispatchEvent(new CustomEvent("change",{bubbles:!0,cancelable:!1,detail:{value:this.value}}))});return[s,t.Fragments.fromChildNodes(e)]});a.forEach(e=>e.remove()),this.template().withOverlay({name:i,slots:e,inputsAndLabels:n}).renderTo(this),this.#ie=this.firstElementChild,this.disabled=r,this.readonly=s.readonly,this.required=s.required,this.value=s.value,this.#ee=this.querySelector("ful-field-error"),this.ariaDescribedByElements=[this.#ee],this.#ae=this.querySelector("input[type=radio]"),this.#ne="boolean"===this.getAttribute("type")}get value(){const e=this.querySelector("input[type=radio]:checked");return e?this.#ne?"true"===e.value:e.value:null}set value(e){if(null===e)return void this.querySelectorAll("input[type=radio]").forEach(e=>{e.checked=!1});const t=this.querySelector(`input[type=radio][value=${CSS.escape(String(e))}]`);t&&(t.checked=!0)}get readonly(){return this.#ie.inert}set readonly(e){this.#ie.inert=e,this.reflect(()=>{t.Attributes.toggle(this,"readonly",e)})}get disabled(){return this.#ie.hasAttribute("disabled")}set disabled(e){t.Attributes.toggle(this.#ie,"disabled",e)}get required(){return"true"===this.#ie.getAttribute("aria-required")}set required(e){t.Attributes.set(this.#ie,"aria-required",e?"true":null),this.reflect(()=>{t.Attributes.toggle(this,"required",e)})}focus(e){this.#ae.focus(e)}setCustomValidity(e){if(!e)return this.internals.setValidity({}),void(this.#ee.innerText="");this.internals.setValidity({customError:!0}," "),this.#ee.innerText=e}}class U extends t.ParsedElement{static observed=["value:bool","readonly:presence","required:presence"];static slots=!0;static template='\n <div data-tpl-class="klass">\n <div class="input-container">\n <input class="form-check-input" type="checkbox" data-tpl-role="isSwitch ? \'switch\' : false" form="" placeholder=" ">\n </div>\n <div class="form-check-label">\n <label>{{{{ slots.default }}}}</label>\n {{{{ slots.info }}}}\n </div>\n </div>\n <ful-field-error></ful-field-error>\n ';#le;#X;#ee;static formAssociated=!0;constructor(){super(),this.internals=this.attachInternals(),this.internals.role="presentation"}render({slots:e,observed:s,disabled:r}){const i="switch"==this.getAttribute("type"),a=i?"form-check form-switch":"form-check",n=this.template().withOverlay({slots:e,klass:a,isSwitch:i}).render();this.#le=n.firstElementChild,this.#X=n.querySelector("input"),t.Attributes.forward("input-",this,this.#X),this.disabled=r,this.readonly=s.readonly,this.required=s.required,this.value=s.value,this.#X.addEventListener("change",e=>{e.stopPropagation(),this.dispatchEvent(new CustomEvent("change",{bubbles:!0,cancelable:!1,detail:{value:this.value}}))});const l=n.querySelector("label");l.addEventListener("click",()=>{this.focus(),this.disabled||this.readonly||(this.value=!this.value,this.dispatchEvent(new CustomEvent("change",{bubbles:!0,cancelable:!1,detail:{value:this.value}})))}),this.#ee=n.querySelector("ful-field-error"),this.#X.ariaDescribedByElements=[this.#ee],this.#X.ariaLabelledByElements=[l],this.replaceChildren(n)}get value(){return this.#X.checked}set value(e){this.#X.checked=e}get readonly(){return this.#le.inert}set readonly(e){this.#le.inert=e,this.reflect(()=>{t.Attributes.toggle(this,"readonly",e)})}get disabled(){return this.#X.hasAttribute("disabled")}set disabled(e){t.Attributes.toggle(this.#X,"disabled",e)}get required(){return"true"===this.#X.getAttribute("aria-required")}set required(e){t.Attributes.set(this.#X,"aria-required",e?"true":null),this.reflect(()=>{t.Attributes.toggle(this,"required",e)})}focus(e){this.#X.focus(e)}setCustomValidity(e){if(!e)return this.internals.setValidity({}),void(this.#ee.innerText="");this.internals.setValidity({customError:!0}," "),this.#ee.innerText=e}}class H extends t.ParsedElement{static slots=!0;static template='\n <div class="ful-spinner-wrapper" role="status">\n <div class="ful-spinner-text">{{{{ slots.default }}}}</div>\n <div class="ful-spinner-icon"></div>\n </div>\n ';render({slots:e}){this.template().withOverlay({slots:e}).renderTo(this)}}class j extends t.ParsedElement{static observed=["order"];#oe;render(){const e=this.getAttribute("sorter"),t=["asc","desc",null];this.addEventListener("click",()=>{const s=t[(t.indexOf(this.order)+1)%3];this.dispatchEvent(new CustomEvent("sort-requested",{bubbles:!0,cancelable:!0,detail:{value:{sorter:e,order:s}}}))})}get order(){return this.#oe||null}set order(e){this.#oe=e||null,this.reflect(()=>{this.#oe?this.setAttribute("order",e):this.removeAttribute("order")})}}class V extends t.ParsedElement{static observed=["total:number","current:number"];static l10n={en:{showing:"Page {0} of {1}",navigation:"Page navigation",previous:"Previous",next:"Next"},it:{showing:"Pagina {0} di {1}",navigation:"Navigazione pagine",previous:"Precedente",next:"Successivo"}};static config={prevIcon:"bi bi-chevron-left",nextIcon:"bi bi-chevron-right",reloadIcon:"bi bi-arrow-clockwise"};static template='\n <nav data-tpl-aria-label="#l10n:t(\'navigation\')" class="user-select-none">\n <ul class="pagination">\n <li class="page-item ms-auto me-2 pagination-index"> {{ #l10n:t(\'showing\', curr.label, total) }}</li>\n <li class="page-item me-2 reload"><a role="button"><i data-tpl-class="config.reloadIcon"></i></a></li>\n <li class="page-item prev">\n <a data-tpl-class="prev.enabled?\'page-link\':\'page-link disabled\'" data-tpl-aria-label="#l10n:t(\'previous\')" role="button" data-tpl-data-page="prev.index">\n <i aria-hidden="true" data-tpl-class="config.prevIcon"></i>\n </a>\n </li>\n <li class="page-item" data-tpl-each="pages" data-tpl-var="page">\n <a data-tpl-class="curr.index != page.index ? \'page-link\': \'page-link disabled\'" role="button" data-tpl-data-page="page.index" >\n {{ page.label }}\n </a>\n </li>\n <li class="page-item next">\n <a data-tpl-class="next.enabled?\'page-link\':\'page-link disabled\'" data-tpl-aria-label="#l10n:t(\'next\')" role="button" data-tpl-data-page="next.index">\n <i aria-hidden="true" data-tpl-class="config.nextIcon"></i>\n </a>\n </li>\n </ul>\n </nav>\n ';#ue=0;#n=0;render({observed:e}){this.update(e.current??0,e.total??0),this.addEventListener("click",e=>{const t=e.target.closest("a");t&&this.dispatchEvent(new CustomEvent("page-requested",{bubbles:!0,cancelable:!0,detail:{value:Number(t.dataset.page??this.#n)}}))})}update(e,t){const s=Number(this.getAttribute("pages")??"5"),r={index:Math.max(0,e-1),enabled:e>0},i={index:e,label:e+1},a={index:Math.min(t,e+1),enabled:e+1<t},n=[{index:e,label:e+1}];for(let r=e,i=1;i!==s&&n.length!=s;++i){const e=r-i;e>=0&&n.unshift({index:e,label:e+1});const s=r+i;s<t&&n.push({index:s,label:s+1})}this.template().withOverlay({total:t,prev:r,curr:i,next:a,pages:n}).renderTo(this)}get total(){return this.#ue}set total(e){this.#ue=e,this.reflect(()=>{this.setAttribute("total",String(e)),this.update(this.#n??0,this.#ue)})}get current(){return this.#n}set current(e){this.#n=e,this.reflect(()=>{this.setAttribute("current",String(e)),this.update(this.#n,this.#ue??0)})}}class G{static parse(e,s){const r=t.Nodes.queryChildren(e,"schema");if(!r)throw new Error(`missing expected <schema> in ${e}`);const i=document.createElement("tr"),a=document.createElement("tr");a.setAttribute("data-tpl-each","rows");for(const e of r.getAttributeNames()){const t=r.getAttribute(e);i.setAttribute(e,t??""),a.setAttribute(e,t??"")}const n=t.Nodes.queryChildrenAll(r,"column"),l=n.filter(e=>e.hasAttribute("order")).map(e=>({sorter:e.getAttribute("sorter"),order:e.getAttribute("order")}))[0]??null;for(var o of n){const e=t.Nodes.queryChildren(o,"title"),s=o.getAttribute("sorter"),r=o.getAttribute("order"),n=e??document.createTextNode(o.getAttribute("title")??"");e?.remove(),o.removeAttribute("sorter"),o.removeAttribute("order"),o.removeAttribute("title");const l=s||r?(()=>{const e=document.createElement("ful-sorter");return s&&e.setAttribute("sorter",s),r&&e.setAttribute("order",r),e.append(n),e})():n,u=document.createElement("th"),d=document.createElement("td");for(const e of o.getAttributeNames()){const t=o.getAttribute(e);u.setAttribute(e,t??""),d.setAttribute(e,t??"")}u.append(l),d.append(...o.childNodes),i.append(u),a.append(d)}return{headersTemplate:s.withOverlay({inHeaders:!0,inRows:!1}).withFragment(t.Fragments.from(i)),rowsTemplate:s.withOverlay({inHeaders:!1,inRows:!0}).withFragment(t.Fragments.from(a)),sort:l,length:n.length}}}class J{#$;constructor(e){this.#$=e}async load(e,t,s){return{page:this.#$,size:this.#$.length}}update(e){this