UNPKG

sortable-tree

Version:
2 lines 8.37 kB
/*! Sortable Tree 0.7.4, (c) 2025 Marc Anton Dahmen, MIT license */ !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SortableTree=t():e.SortableTree=t()}(self,(()=>(()=>{"use strict";var e={d:(t,n)=>{for(var o in n)e.o(n,o)&&!e.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:n[o]})},o:(e,t)=>Object.prototype.hasOwnProperty.call(e,t)},t={};e.d(t,{default:()=>L});var n=function(e,t,n,o){return new(n||(n=Promise))((function(s,r){function i(e){try{a(o.next(e))}catch(e){r(e)}}function l(e){try{a(o.throw(e))}catch(e){r(e)}}function a(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,l)}a((o=o.apply(e,t||[])).next())}))};const o=e=>`<span>${e.title}</span>`,s={collapsed:'\n\t\t<svg\n\t\txmlns="http://www.w3.org/2000/svg" \n\t\twidth="1em" \n\t\theight="1em" \n\t\tfill="currentColor" \n\t\tclass="bi bi-caret-right-fill" \n\t\tviewBox="0 0 16 16">\n\t\t\t<path d="m12.14 8.753-5.482 4.796c-.646.566-1.658.106-1.658-.753V3.204a1 1 0 0 1 1.659-.753l5.48 4.796a1 1 0 0 1 0 1.506z"/>\n\t\t</svg>',open:'\n\t\t<svg \n\t\txmlns="http://www.w3.org/2000/svg" \n\t\twidth="1em" \n\t\theight="1em" \n\t\tfill="currentColor" \n\t\tclass="bi bi-caret-down-fill" \n\t\tviewBox="0 0 16 16">\n\t\t\t<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>\n\t\t</svg>\n\t'},r={tree:"tree",node:"tree__node",nodeHover:"tree__node--hover",nodeDragging:"tree__node--dragging",nodeDropBefore:"tree__node--drop-before",nodeDropInside:"tree__node--drop-inside",nodeDropAfter:"tree__node--drop-after",label:"tree__label",subnodes:"tree__subnodes",collapse:"tree__collapse"},i=({nodes:e,movedNode:t,srcParentNode:o,targetParentNode:s})=>n(void 0,void 0,void 0,(function*(){})),l=(e,t)=>n(void 0,void 0,void 0,(function*(){})),a=(e,t)=>n(void 0,void 0,void 0,(function*(){return!0}));const d=(e,t=[],n=null)=>{const o=document.createElement(e);return t.forEach((e=>{o.classList.add(e)})),n&&n.appendChild(o),o},c=()=>((e=21)=>crypto.getRandomValues(new Uint8Array(e)).reduce(((e,t)=>e+((t&=63)<36?t.toString(36):t<62?(t-26).toString(36).toUpperCase():t>62?"-":"_")),""))();class u extends HTMLElement{static create({data:e,renderLabel:t,icons:n,styles:o,parent:s,onClick:r,draggable:i,eventBus:l}){const a=d(u.TAG_NAME,[o.node],s),c=d("div",[o.label],a),h=d("div",[o.subnodes],a),p=d("span",[o.collapse],a);return c.innerHTML=t(e),p.innerHTML=n.collapsed,l.listen(p,"click",a.toggle.bind(a)),l.listen(c,"click",(e=>{r(e,a)})),i&&a.setAttribute("draggable","true"),a._data=e,a._icons=n,a._label=c,a._nodes=h,a._collapseButton=p,a}get data(){return this._data}get label(){return this._label}get subnodes(){return this._nodes}get subnodesData(){const e=[];return Array.from(this._nodes.children).forEach((t=>{e.push(t.data)})),e}get id(){return this._id}constructor(){super(),this._id=c()}collapse(e){e?(this.removeAttribute("open"),this._collapseButton.innerHTML=this._icons.collapsed):(this.setAttribute("open","true"),this._collapseButton.innerHTML=this._icons.open)}toggle(){this.collapse(this.hasAttribute("open"))}reveal(){((e,t)=>{const n=[];let o=t.closest(e);for(;null!==o;)n.push(o),o=o.parentNode.closest(e);return n})(u.TAG_NAME,this).forEach((e=>{e.collapse(!1)}))}}u.TAG_NAME="sortable-tree-node";var h=function(e,t,n,o){return new(n||(n=Promise))((function(s,r){function i(e){try{a(o.next(e))}catch(e){r(e)}}function l(e){try{a(o.throw(e))}catch(e){r(e)}}function a(e){var t;e.done?s(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(i,l)}a((o=o.apply(e,t||[])).next())}))};var p;!function(e){e.BEFORE="BEFORE",e.INSIDE="INSIDE",e.AFTER="AFTER"}(p||(p={}));const f=e=>{const t=e.clientY,n=g(e).label.getBoundingClientRect(),o=Math.round(n.height/4);return n.top+o>t?p.BEFORE:n.bottom-o<t?p.AFTER:p.INSIDE},g=e=>e.target.closest(u.TAG_NAME),v=e=>{const t=null==e?void 0:e.parentElement.parentElement;return(null==t?void 0:t.tagName.toLowerCase())!==u.TAG_NAME?null:t},b=(e,t)=>{g(e).classList.remove(t.styles.nodeDropAfter,t.styles.nodeDropBefore,t.styles.nodeDropInside)},m=(e,t)=>{e.stopPropagation();const n=e.target;n.tagName.toLowerCase()===u.TAG_NAME&&(e.dataTransfer.setData("text",n.id),e.dataTransfer.effectAllowed="move",n.classList.add(t.styles.nodeDragging))},E=(e,t)=>h(void 0,void 0,void 0,(function*(){e.stopPropagation(),e.preventDefault(),b(e,t);const n=g(e);if(!n)return!1;const o=v(n),s=e.dataTransfer.getData("text"),r=f(e),i=t.getNode(s);if(!i||(null==i?void 0:i.contains(n)))return!1;const l=v(i);if(t.lockRootLevel&&!(null==n?void 0:n.parentElement.closest(u.TAG_NAME))&&(r===p.BEFORE||r===p.AFTER))return!1;if(r===p.BEFORE)return!!(yield t.confirm(i,o))&&(n.parentNode.insertBefore(i,n),t.onDrop(i,l,o),!1);if(r===p.AFTER){if(!(yield t.confirm(i,o)))return!1;const e=n.nextElementSibling;return e?(n.parentNode.insertBefore(i,e),t.onDrop(i,l,o)):(n.parentNode.appendChild(i),t.onDrop(i,l,o)),!1}return!!(yield t.confirm(i,n))&&(n.subnodes.appendChild(i),t.onDrop(i,l,n),!1)})),_=(e,t)=>{e.preventDefault(),e.dataTransfer.dropEffect="move",((e,t)=>{const n=g(e),o=f(e);n.classList.toggle(t.styles.nodeDropBefore,o===p.BEFORE),n.classList.toggle(t.styles.nodeDropInside,o===p.INSIDE),n.classList.toggle(t.styles.nodeDropAfter,o===p.AFTER)})(e,t)},y=(e,t)=>{b(e,t)},A=(e,t)=>{e.target.classList.remove(t.styles.nodeDragging),b(e,t)};class T{constructor(){this.listeners=[]}listen(e,t,n){e.addEventListener(t,n,!1),this.listeners.push({element:e,eventName:t,handler:n})}clear(){this.listeners.forEach((({element:e,eventName:t,handler:n})=>{e.removeEventListener(t,n)})),this.listeners=[]}}const N=e=>`sortableTreeState-${e.replace(/[^\w]+/,"-")}`;const L=class{constructor({nodes:e,element:t,renderLabel:n,icons:d,styles:c,lockRootLevel:u,onChange:h,onClick:p,initCollapseLevel:f,confirm:g,disableSorting:v,stateId:b}){this.nodeCollection={},e&&(t?(this.defineElements(),this.eventBus=new T,this.root=t,this.icons=Object.assign(Object.assign({},s),d),this.styles=Object.assign(Object.assign({},r),c),this.renderLabel=n||o,this.lockRootLevel=void 0===u||u,this.onChange=h||i,this.onClick=p||l,this.confirm=g||a,this.initCollapseLevel=void 0===f?2:f,this.disableSorting=v||!1,t.classList.add(this.styles.tree),this.render({nodes:e,element:t}),b&&(((e,t)=>{const n=sessionStorage.getItem(N(e));if(!n)return;const o=JSON.parse(n),s=(e,t)=>{const[n,o]=t;e.element.collapse(0===n),o&&e.subnodes.forEach(((e,t)=>{void 0!==o[t]&&s(e,o[t])}))};t.forEach(((e,t)=>{s(e,o[t])}))})(b,this.parseTree(this.root)),this.initStateObserver(b))):console.error('Error: "element" is not a valid HTML element!'))}getNode(e){return this.nodeCollection[e]}findNode(e,t){const n=Object.values(this.nodeCollection);for(let o=0;o<n.length;o++){const s=n[o],r=s.data;if(e in r&&r[e]==t)return s}return null}onDrop(e,t,n){const o={nodes:this.parseTree(this.root),movedNode:e,srcParentNode:t,targetParentNode:n};null==n||n.collapse(!1),this.onChange(o)}destroy(){var e;this.eventBus.clear(),null===(e=this.observer)||void 0===e||e.disconnect(),this.observer=null}initStateObserver(e){this.observer=new MutationObserver((()=>{((e,t)=>{const n=[],o=e=>{const t=[],n=[e.element.getAttribute("open")?1:0,t];return e.subnodes.forEach((e=>{t.push(o(e))})),n};t.forEach((e=>{n.push(o(e))})),sessionStorage.setItem(N(e),JSON.stringify(n))})(e,this.parseTree(this.root))})),this.observer.observe(this.root,{childList:!0,attributes:!0,subtree:!0})}defineElements(){try{customElements.define(u.TAG_NAME,u)}catch(e){}}render({nodes:e,element:t},n=0){n++,e.forEach((e=>{const o=u.create({icons:this.icons,styles:this.styles,parent:t,renderLabel:this.renderLabel,data:e.data,onClick:this.onClick,draggable:!this.disableSorting,eventBus:this.eventBus});this.disableSorting||((e,t)=>{const n={dragstart:m,drop:E,dragover:_,dragend:A,dragleave:y};for(const[o,s]of Object.entries(n))t.eventBus.listen(e,o,(e=>{s(e,t)}))})(o,this),o.collapse(n>this.initCollapseLevel),this.nodeCollection[o.id]=o,e.nodes&&this.render({nodes:e.nodes,element:o.subnodes},n)}))}parseTree(e){const t=Array.from(e.querySelectorAll(`:scope > ${u.TAG_NAME}`)),n=[];return t.forEach((e=>{n.push({element:e,id:e.id,subnodes:this.parseTree(e.subnodes)})})),n}};return t=t.default})()));