+const e=(e,i=!0)=>`\n<ul part="tree" role="${i?"tree":"group"}">\n ${[...e.values()].reduce((e,i)=>e+t(i),"")}\n</ul>`,t=({id:t,title:r,icon:a,collapsed:s,children:n})=>`\n<li id="item_${t}" part="item" role="treeitem" aria-expanded="${void 0===s?"undefined":!s}">\n ${void 0!==n?'<button type="button" part="toggle"></button>':""}\n <label part="label">\n <input type="checkbox" id="cbx_${t}" part="checkbox">\n ${i(a)}\n <span part="title">${r}</span>\n </label>\n ${n?.size>0?e(n,!1):""}\n</li>`,i=e=>e?e.startsWith("<svg ")&&e.endsWith("</svg>")?'<svg part="icon"'+e.slice(4):`<img src="${(e=>["&",'"'].reduce((e,t)=>e.replaceAll(t,`&#${t.charCodeAt(0)};`),e))(e)}" alt="" part="icon">`:"",r=e=>e?.slice(e.indexOf("_")+1),a=e=>{if(!Array.isArray(e))throw new TypeError("Tree data must be an array of tree items")},s=new CSSStyleSheet;s.replaceSync(':host{--cbx-tree-toggle-closed-mask: url(\'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 14" width="8" height="14"><path d="M1.5 2.5v9L7 7z"/></svg>\');--cbx-tree-toggle-open-mask: url(\'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 8" width="14" height="8"><path d="M2.5 1.5h9L7 7z"/></svg>\');--cbx-tree-toggle-pending-mask: url(\'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14" width="14" height="14"><path d="M7 1A6 6 0 1 1 1 7" fill="none" stroke="black" stroke-width="2"><animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0 7 7" to="360 7 7" dur="1s" repeatCount="indefinite"/></path></svg>\');--cbx-tree-label-hover-bg: SelectedItem;--cbx-tree-label-hover-fg: SelectedItemText;--cbx-tree-nesting-indent: 1em}:host(:dir(rtl)){--cbx-tree-toggle-closed-mask: url(\'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 14" width="8" height="14"><path d="M6.5 2.5v9L1 7z"/></svg>\')}:host(:not([hidden])){display:block}[part=tree]{list-style:none;margin:0;padding:0;&:has([inert]){cursor:progress}&:not([part=tree] [part=tree]){overflow-x:clip}}[part=item]{align-items:center;display:grid;gap:0 .6ch;grid-template-areas:"toggle label" "tree tree";grid-template-columns:max(1em,16px) 1fr;&[aria-expanded=false]>[part=tree]{display:none}}[part=toggle]{background:none;border:none;color:inherit;font:inherit;grid-area:toggle;height:max(1em,16px);padding:0;position:relative;width:max(1em,16px);z-index:1;&:not(:disabled){cursor:pointer}&:before{background:currentColor;content:"";inset:-4px;mask:var(--cbx-tree-toggle-closed-mask) 50% 50% / contain no-repeat content-box;padding:4px;position:absolute}[aria-expanded=true]>&:before{mask-image:var(--cbx-tree-toggle-open-mask)}[inert]>&:before{mask-image:var(--cbx-tree-toggle-pending-mask)}&:where(:hover,:focus-visible):before,&:has(+[part=label]:hover):before,[part=item]:has(>[part=label] :focus-visible)>&:before{color:var(--cbx-tree-label-hover-fg)}}[part=label]{align-items:inherit;display:flex;gap:inherit;grid-area:label;isolation:isolate;padding-block:.2em;position:relative;&:hover,&:has(:focus-visible),[part=toggle]:where(:hover,:focus-visible)+&{color:var(--cbx-tree-label-hover-fg);&:before{background:var(--cbx-tree-label-hover-bg)}}&:before{content:"";inset:0;inset-inline-start:-100vw;position:absolute;z-index:-1}}:where([part=item]) [part=tree]{grid-area:tree;padding-inline-start:var(--cbx-tree-nesting-indent)}');class n extends HTMLElement{static get formAssociated(){return!0}#e;#t;#i=new Map;#r=new Set;subtreeProvider=null;get formData(){const e=new FormData,{name:t}=this;return this.#r.forEach(i=>{const r=this.#a(i)?.value;void 0!==r&&e.append(t,r)}),e}get form(){return this.#t.form}get name(){return this.getAttribute("name")}set name(e){this.setAttribute("name",e)}get disabled(){return this.hasAttribute("disabled")}set disabled(e){e?this.setAttribute("disabled",""):this.removeAttribute("disabled")}get type(){return this.localName}constructor(){super(),this.#e=this.attachShadow({mode:"open"}),this.#e.adoptedStyleSheets=[s],this.#t=this.attachInternals(),this.setData(this.#s()),this.hasAttribute("tabindex")||(this.tabIndex=0),this.#e.addEventListener("change",e=>this.#n(e)),this.#e.addEventListener("click",e=>this.#l(e))}formDisabledCallback(e){this.#o(e)}formResetCallback(){this.setData(this.#s())}formStateRestoreCallback(e,t){if("restore"===t)try{this.setData(JSON.parse(e))}catch(e){console.warn("Failed to restore the tree state",e)}}#n({target:e}){if(!e.part.contains("checkbox"))return;const t=r(e.id),i=e.checked?"add":"delete";this.#r[i](t);const a=this.#a(t);this.#d(a),this.#h(a),this.#c(),this.dispatchEvent(new CustomEvent("cbxtreechange",{bubbles:!0,detail:this.formData}))}#l({target:e}){if(!e.part.contains("toggle"))return;const t=e.closest('[part="item"]'),i="true"!==t.ariaExpanded;t.ariaExpanded=i?"true":"false";const a=r(t.id);i&&this.#g(a);const s=this.#a(a);s.collapsed=!i,this.#c(),this.dispatchEvent(new CustomEvent("cbxtreetoggle",{bubbles:!0,detail:{title:s.title,value:s.value,newState:i?"expanded":"collapsed"}}))}#p(){this.#e.setHTMLUnsafe(e(this.#i)),[...this.#e.querySelectorAll('[part="checkbox"]')].forEach(e=>{const t=this.#a(r(e.id))?.state;e.checked="checked"===t,e.indeterminate="indeterminate"===t})}#u(e,t){return new Map(e.map((e,i)=>{const r=t?`${t}:${i}`:String(i);e.checked&&this.#r.add(r);const a={id:r,title:e.title,value:e.value,icon:e.icon,collapsed:e.children?.length?!!e.collapsed:null===e.children||void 0,children:e.children?this.#u(e.children,r):e.children};return Object.defineProperty(a,"state",{get:()=>this.#r.has(a.id)?"checked":a.children?.size?this.#b(a):"unchecked"}),[r,a]}))}async#g(t){if("function"!=typeof this.subtreeProvider)return;const i=this.#a(t);if(null!==i?.children)return;const r=this.#e.getElementById(`item_${t}`);r.inert=!0;try{const e=await this.subtreeProvider(i.value);a(e),i.children=this.#u(e,i.id)}finally{r.inert=!1}i.children.size&&(r.insertAdjacentHTML("beforeend",e(i.children,!1)),this.disabled&&this.#o(!0,r),this.#d(i),this.#c())}#a(e){const t=e.split(":");return t.slice(1).reduce((e,t)=>e?.children?.get(`${e?.id}:${t}`),this.#i.get(t[0]))}#b(e){const t=new Set([...e.children.values()].map(({state:e})=>e));return t.has("indeterminate")?"indeterminate":t.has("checked")?t.has("unchecked")?"indeterminate":"checked":"unchecked"}#m(e,t){if(!t?.size)return;const i=e?"add":"delete";t.forEach((t,r)=>{this.#r[i](r);const a=this.#e.getElementById(`cbx_${r}`);a.checked=e,a.indeterminate=!1,this.#m(e,t.children)})}#o(e,t=this.#e){[...t.querySelectorAll("button, input")].forEach(t=>t.disabled=e)}#d(e){e.children&&this.#m(this.#r.has(e.id),e.children)}#h(e){if(this.#i.has(e.id))return;const t=this.#a(e.id.slice(0,e.id.lastIndexOf(":"))),i=this.#b(t);this.#r["checked"===i?"add":"delete"](t.id);const r=this.#e.getElementById(`cbx_${t.id}`);r.checked="checked"===i,r.indeterminate="indeterminate"===i,this.#h(t)}#c(){this.#t.setFormValue(this.formData,JSON.stringify(this))}#s(){const e=this.textContent.trim()||"[]";try{const t=JSON.parse(e);return a(t),t}catch{return console.error(new DOMException("<cbx-tree> contents must be a valid JSON array representation","DataError")),[]}}#v(e=this.#i){return[...e.values()].map(e=>({title:e.title,value:e.value,icon:e.icon,checked:this.#r.has(e.id),collapsed:!0===e.collapsed||void 0,children:e.children?this.#v(e.children):e.children}))}setData(e){a(e),this.#r.clear(),this.#i=this.#u(e),this.#p(),this.#c()}toJSON(){return this.#v()}toggleChecked(e){void 0===e&&(e=!!this.#e.querySelector('[part="checkbox"]:not(:checked)')),this.#m(e,this.#i),this.#c()}toggle(e){let t=[...this.#e.querySelectorAll('[part="item"]:has([part="tree"])')];void 0===e&&(e=t.some(({ariaExpanded:e})=>"false"===e));const i=String(e);t.forEach(t=>{if(t.ariaExpanded===i)return;t.ariaExpanded=i;this.#a(r(t.id)).collapsed=!e}),this.#c()}get validity(){return this.#t.validity}get validationMessage(){return this.#t.validationMessage}get willValidate(){return this.#t.willValidate}checkValidity(){return this.#t.checkValidity()}reportValidity(){return this.#t.reportValidity()}setValidity(...e){return this.#t.setValidity(...e)}}customElements.define("cbx-tree",n);export{n as default};
0 commit comments