@localnerve/editable-object 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/editable-object.js +2 -2
- package/package.json +6 -6
package/dist/editable-object.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! editable-object@0.3.
|
|
2
|
-
class e extends HTMLElement{#e=null;#t=!1;#o=()=>!0;#r=()=>!0;#i=()=>!0;#s=[];#n=[];#l=[];static#d=["object","add-property-placeholder","disable-edit"];static#a={object:{},"add-property-placeholder":"Add new property in key:value format","disabled-edit":!1};static get observedAttributes(){return this.#d}constructor(){super(),this.attachShadow({mode:"open",delegatesFocus:!0})}#c(t){if(this.hasAttribute(t)){const e=this.getAttribute(t);return/^\s*(?:true|false)\s*$/i.test(e)?"false"!==e:e}return e.#a[t]}#p(e){if("string"==typeof e||"number"==typeof e||"boolean"==typeof e||null===e)return e;if(void 0===e||"function"==typeof e||"symbol"==typeof e)return null;if("bigint"==typeof e)return`${e}n`;"[object RegExp]"===Object.prototype.toString.call(e)&&(e={__pattern:e.source,flags:e.flags});let t=JSON.stringify(e);return"{"===t[0]&&(t=t.replaceAll('"',"'")),t}#h(e,t=null){const o=e.trim();let r=parseFloat(o);if(r)return r;if(/\d+n$/.test(o))return BigInt(o.slice(0,-1));if("false"===o.toLowerCase())return!1;if("true"===o.toLowerCase())return!0;if("null"===o.toLowerCase())return null;let i,s=!1;try{let e=o;"{"===e[0]&&(s=!0,e=e.replaceAll("'",'"'));const t=JSON.parse(e);i=Object.keys(t).includes("__pattern")?new RegExp(t.__pattern,t.flags):t}catch{if(s&&t)throw t.classList.add("error"),new Error("Bad object input");i=o}return i}#u(e,t){return`\n <div class="property-wrapper">\n <label for="eo-${e}-value">${e}</label>\n <input readonly="true" id="eo-${e}-value" type="text" value="${t}" />\n </div>\n <div class="toolbar">\n <button class="editable-object-up-property icon" title="Move up">\n <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">\n <path d="m5 9 1.41 1.41L11 5.83V22h2V5.83l4.59 4.59L19 9l-7-7-7 7z"></path>\n </svg>\n </button>\n <button class="editable-object-down-property icon" title="Move down">\n <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">\n <path d="m19 15-1.41-1.41L13 18.17V2h-2v16.17l-4.59-4.59L5 15l7 7 7-7z"></path>\n </svg>\n </button>\n <button class="editable-object-remove-property icon" title="Remove">\n <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">\n <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"></path>\n </svg>\n </button>\n </div>\n `}#b(){const e=this.shadowRoot.querySelectorAll(".editable-object-up-property"),t=this.shadowRoot.querySelectorAll(".editable-object-down-property"),o=e.length;for(let r=0;r<o;r++)e[r].style.visibility=0==r?"hidden":"visible",t[r].style.visibility=r==o-1?"hidden":"visible";const r=this.#t?"add":"remove";this.shadowRoot.querySelectorAll(".editable-object-remove-property").forEach(e=>e.classList[r]("hide"))}#y(){return/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)}#v(e){for(;"LI"!==e.tagName;)e=e.parentNode;return e}#f(e){this.#w();const t=this.#v(e.target);t.classList.toggle("selected",!0),[...t.querySelectorAll("button")].forEach(e=>{e.tabIndex=0}),t.querySelector("input").focus()}#g(e){return new CustomEvent("change",{bubbles:!0,cancelable:!1,composed:!0,detail:e})}#m(e){"INPUT"!==e.target.nodeName||e.target.classList.contains("error")||"Enter"!==e.key&&" "!==e.key||e.target.click()}#x(e){if(this._editing)return;this._editing=!0;const t=this.#v(e.target).querySelector(".property-wrapper > input");t.classList.remove("error"),t.readOnly=!1,t._value=t.value;const o=this.#j.bind(this),r=this.#j.bind(this);t.addEventListener("blur",o,!1),this.#l.push({host:t,type:"blur",listener:o}),t.addEventListener("keypress",r,!1),this.#l.push({host:t,type:"keypress",listener:r}),t.focus()}#j(e){if(this._editing&&(e instanceof KeyboardEvent&&"Enter"===e.key||!(e instanceof KeyboardEvent))){e.preventDefault();const t=this.#v(e.target),o=t.querySelector(".property-wrapper > label").innerText,r=t.querySelector(".property-wrapper > input"),i=r._value,s=r.value;let n;this._editing=!1,r.readOnly=!0;let l=!1;try{n=this.#h(s,r)}catch{l=!0}this.#l.forEach(e=>{e.host.removeEventListener(e.type,e.listener)}),this.#l.length=0,l?r.classList.add("error"):this.#o(o,n)?(this.#e[o]=n,this.dispatchEvent(this.#g({action:"edit",key:o,previous:this.#h(i),new:n}))):r.classList.add("error")}}#E(e){const t=e.previousElementSibling;return!!t&&(e.parentNode.insertBefore(e,t),this.#b(),e.querySelector(".editable-object-up-property").focus(),!0)}#k(e){const t=e.nextElementSibling;return!!t&&(e.parentNode.insertBefore(t,e),this.#b(),e.querySelector(".editable-object-down-property").focus(),!0)}#L(e){e.remove(),this.#b()}#S(e){e.stopPropagation();const t=this.#v(e.target),o=t.querySelector(".property-wrapper > label").innerText.trim(),r=t.querySelector(".property-wrapper > input"),i=r.value.trim(),s=this.#h(i);this.#r(o,s)?(this.#L(t),delete this.#e[o],this.dispatchEvent(this.#g({action:"remove",key:o,previous:s,new:null}))):r.classList.add("error")}#q(e){e.stopPropagation();const t=this.#v(e.target);this.#E(t)}#A(e){e.stopPropagation();const t=this.#v(e.target);this.#k(t)}#R(){let e,t=0;return function(o){const r=(new Date).getTime(),i=r-t;i<500&&i>0?(o.preventDefault(),this.#x(o)):e=setTimeout(()=>{clearTimeout(e)},500),t=r}.bind(this)}#P(e){const t=this.#f.bind(this);e.forEach(e=>{e.addEventListener("click",t,!1),this.#n.push({host:e,type:"click",listener:t})})}#O(){const e=this.shadowRoot.querySelectorAll(".object-properties .property-wrapper"),t={remove:this.shadowRoot.querySelectorAll('.object-properties button[title="Remove"]')};this.#_(e,t)}#_(e,t){const o=this.#y(),r=this.#R(),i=this.#x.bind(this),s=this.#S.bind(this);if(this.#t){const e=["dblclick","touchend"],t=[];this.#n.filter((o,r)=>{let i=e.includes(o.type);return i?t.push(r):"remove"===o.host.title.toLowerCase()&&(i=!0,t.push(r)),i}).forEach(e=>{e.host.removeEventListener(e.type,e.listener)});let o=0;for(const e of t)this.#n.splice(e-o++,1)}else e.forEach(e=>{e.addEventListener("dblclick",i,!1),this.#n.push({host:e,type:"dblclick",listener:i}),o&&(e.addEventListener("touchend",r),this.#n.push({host:e,type:"touchend",listener:r}))}),t.remove.forEach(e=>{e.addEventListener("click",s,!1),this.#n.push({host:e,type:"click",listener:s})})}#C(e,t){const o=this.#m.bind(this),r=this.#q.bind(this),i=this.#A.bind(this);e.forEach(e=>{e.addEventListener("keypress",o,!1),this.#n.push({host:e,type:"keypress",listener:o})}),t.up.forEach(e=>{e.addEventListener("click",r,!1),this.#n.push({host:e,type:"click",listener:r})}),t.down.forEach(e=>{e.addEventListener("click",i,!1),this.#n.push({host:e,type:"click",listener:i})}),this.#_(e,t)}#M(e){e.composedPath().includes(this)||this.shadowRoot.querySelector(".editable-object").classList.add("defocused")}#T(){this.shadowRoot.querySelector(".editable-object").classList.remove("defocused")}#N(e){return e in this.#e}#w(){this.shadowRoot.querySelector(".add-new-object-property-input").classList.toggle("error",!1),[...this.shadowRoot.querySelectorAll("li")].forEach(e=>{const t=e.querySelector(".property-wrapper > input");t.classList.contains("error")&&t._value&&(t.value=t._value),t.classList.toggle("error",!1),e.classList.toggle("selected",!1),[...e.querySelectorAll("button")].forEach(e=>{e.tabIndex=-1})})}#B(e){if(e instanceof KeyboardEvent&&"Enter"===e.key||!(e instanceof KeyboardEvent)){const e=this.shadowRoot.querySelector(".add-new-object-property-input"),t=e.value.trim();if(""!==t){const o=/^\s*(?<property>[^\s:]+)\s*:\s*(?<value>[^$]+)$/,r=t.match(o)?.groups,[i,s]=r?Object.values(r):["",""],n=i.trim(),l=s.trim(),d=()=>{e.classList.add("error"),e.focus()};if(!n||!l||this.#N(n))return void d();let a,c=!1;try{a=this.#h(l,e)}catch{c=!0}if(c)d();else if(this.#i(n,a)){const t={[n]:l};this.mergeObject(t),this.dispatchEvent(this.#g({action:"add",key:n,previous:null,new:a})),this.shadowRoot.querySelector(".object-properties").lastChild.click(),e.value=""}else d()}}}#H(){const{shadowRoot:e}=this,t=e.querySelector(".new-object-property"),o=e.querySelector(".add-new-object-property-input"),r=e.querySelector(".editable-object-add-property");if(this.#t){const e=[t,o,r],i=[];this.#s.filter((t,o)=>{const r=e.includes(t.host);return r&&i.push(o),r}).forEach(e=>{e.host.removeEventListener(e.type,e.listener)});let s=0;for(const e of i)this.#s.splice(e-s++,1);t.classList.add("hide")}else{const e=this.getAttribute("add-property-placeholder");this.addPropertyPlaceholder=e;const i=this.#w.bind(this);t.addEventListener("click",i,!0),this.#s.push({host:t,type:"click",listener:i});const s=this.#B.bind(this);o.addEventListener("keypress",s,!1),this.#s.push({host:o,type:"keypress",listener:s}),r.addEventListener("click",s,!1),this.#s.push({host:r,type:"click",listener:s}),t.classList.remove("hide")}}get object(){return this.#e}set object(e){if(!e)return;this.#e&&Object.keys(this.#e).length>0&&[this.#n,this.#l].forEach(e=>{e.forEach(e=>{e.host.removeEventListener(e.type,e.listener)})});const t=this.shadowRoot.querySelector("#loading"),o=this.shadowRoot.querySelector(".object-properties");o.innerHTML="";const r=[],i=[],s={up:[],down:[],remove:[]};for(const[t,n]of Object.entries(e)){const e=document.createElement("li");e.innerHTML=this.#u(t,this.#p(n)),o.appendChild(e),r.push(e),i.push(e.querySelector(".property-wrapper")),s.up.push(e.querySelector(".editable-object-up-property")),s.down.push(e.querySelector(".editable-object-down-property")),s.remove.push(e.querySelector(".editable-object-remove-property"))}this.#P(r),this.#C(i,s),this.#b(),t.classList.add("hide"),this.#e=e}get addPropertyPlaceholder(){return this.#c("add-property-placeholder")}set addPropertyPlaceholder(t){const o="add-property-placeholder",r=this.shadowRoot.querySelector(".add-new-object-property-input");t?(this.setAttribute(o,t),r.placeholder=t):(this.removeAttribute(o),r.placeholder=e.#a[o])}set disableEdit(e){const t="disable-edit";e?(this.setAttribute(t,!0),this.#t=!0):(this.setAttribute(t,!1),this.#t=!1),this.#H(),this.#O(),this.#b()}get disableEdit(){return this.#c("disable-edit")}set onEdit(e){this.#o="function"==typeof e?e:()=>!0}get onEdit(){return this.#o}set onAdd(e){this.#i="function"==typeof e?e:()=>!0}get onAdd(){return this.#i}set onRemove(e){this.#r="function"==typeof e?e:()=>!0}get onRemove(){return this.#r}mergeObject(e){this.object={...this.#e,...e}}connectedCallback(){const{shadowRoot:e}=this;e.innerHTML='<style>:host{--eo-min-width:300px;--eo-bg-color:#fafafa;--eo-border-radius:4px;--eo-border-focused-color:#444;--eo-border-defocused-color:#aaa;--eo-item-selected-bg-color:#999;--eo-item-selected-color:#222;--eo-item-selected-border-radius:4px;--eo-item-hover-border-width:1px;--eo-item-hover-border-color:#ddd;--eo-item-hover-border-radius:4px;--eo-icon-color:#444;--eo-add-new-icon-color:#444;--eo-input-focus-outline-color:#26b;--eo-input-focus-outline-width:1px;--eo-input-focus-outline-style:auto;--eo-input-border-color:#bbb;--eo-input-border-radius:4px;--eo-input-bg-color:#444;--eo-input-color:#eee;--eo-input-font-family:sans-serif;--eo-input-placeholder-color:#aaa}:host(.disabled){pointer-events:none}.editable-object{background:var(--eo-bg-color);border-radius:var(--eo-border-radius);min-width:var(--eo-min-width);display:flex;flex-flow:column nowrap;justify-content:center;align-items:center}@media (min-width:360px){.editable-object{padding:0 .5rem}}@media (min-width:464px){.editable-object{border:1px solid var(--eo-border-focused-color);padding:1rem}.editable-object.defocused{border:1px solid var(--eo-border-defocused-color)}}ul{padding:0;margin:0;width:100%}li{line-height:2;list-style:none;cursor:default;padding:.5rem}input{padding:6px 8px;border-radius:var(--eo-input-border-radius);line-height:1.5;border:1px solid var(--eo-input-border-color);color:var(--eo-input-color);background:var(--eo-input-bg-color);font-family:var(--eo-input-font-family)}input:focus-visible{outline:var(--eo-input-focus-outline-color) var(--eo-input-focus-outline-style) var(--eo-input-focus-outline-width)}input::placeholder{color:var(--eo-input-placeholder-color)}.editable-object:not(.defocused) li.selected{background:var(--eo-item-selected-bg-color);color:var(--eo-item-selected-color);border-radius:var(--eo-item-selected-border-radius)}div>div,li{display:flex;align-items:center;justify-content:space-around}.editable-object:not(.defocused,.touch) li:hover{border:var(--eo-item-hover-border-width) solid var(--eo-item-hover-border-color);border-radius:var(--eo-item-hover-border-radius)}.property-wrapper{display:flex;flex-flow:row wrap;flex-grow:1;align-items:baseline;min-width:10em;touch-action:manipulation}.property-wrapper label{flex:1 1 100%;min-width:8em;max-width:100%}.property-wrapper input{flex:1;min-width:10em}.editable-object.defocused input{opacity:.7}@media (min-width:41.69em){.property-wrapper label{flex-basis:auto;max-width:45%;min-width:15em;padding-right:.5rem}.property-wrapper input{flex:1 1;min-width:16em}}.toolbar{display:flex;gap:1.5rem;padding-left:1.5rem}.editable-object.defocused .toolbar,li:not(.selected) .toolbar{opacity:0;pointer-events:none}.toolbar button{position:relative;fill:var(--eo-icon-color)}.editable-object-add-property.icon{fill:var(--eo-add-new-icon-color)}.toolbar button:hover::before{content:"";position:absolute;width:24px;height:24px;background:rgb(0 0 0 / 10%);left:-4px;top:-4px;border-radius:50%}.icon{background-color:transparent;border:none;cursor:pointer;font-size:0;fill:var(--eo-icon-color);padding:0}.icon svg{width:1rem;height:1rem}.new-object-property{display:flex;flex-flow:row wrap;line-height:2;margin-top:2rem;width:100%}label[for=new-property]{flex:1 0 100%}.add-new-object-property-input{min-width:18em;flex:1}.add-new-object-property-input.error,.property-wrapper input.error{outline:red auto 1px}.hide{display:none}</style><div class="editable-object defocused"><slot id=loading name=loading></slot><ul class=object-properties></ul><div class=new-object-property><label for=new-property>New Property and Value</label> <input id=new-property name=new-property class=add-new-object-property-input type=text placeholder="Add new property in key:value format"><div class=toolbar><button class="editable-object-add-property icon" title=Add><svg version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink width=24 height=24 viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"></path></svg></button></div></div></div>';const t=this.getAttribute("object");this.object=JSON.parse(t);const o=this.getAttribute("disable-edit");this.#t="true"===o?.toLowerCase();const r=e.querySelector(".editable-object"),i=e.querySelector("#loading"),s=this.#y(),n=this.#M.bind(this),l=this.#T.bind(this);this.object&&i.classList.add("hide"),s&&r.classList.add("touch"),document.addEventListener("click",n,!1),this.#s.push({host:document,type:"click",listener:n}),r.addEventListener("click",l,!0),this.#s.push({host:r,type:"click",listener:l}),this.#H()}disconnectedCallback(){[this.#s,this.#n,this.#l].forEach(e=>{e.forEach(e=>{e.host.removeEventListener(e.type,e.listener)})})}attributeChangedCallback(e,t,o){o!==t&&(this[e]=this.getAttribute(e))}}customElements.define("editable-object",e);
|
|
1
|
+
/*! editable-object@0.3.9, Copyright (c) 2025 Alex Grant <alex@localnerve.com> (https://www.localnerve.com), LocalNerve LLC, BSD-3-Clause */
|
|
2
|
+
class e extends HTMLElement{#e=null;#t=!1;#o=()=>!0;#r=()=>!0;#i=()=>!0;#s=[];#n=[];#l=[];static#d=["object","add-property-placeholder","disable-edit"];static#a={object:{},"add-property-placeholder":"Add new property in key:value format","disabled-edit":!1};static get observedAttributes(){return this.#d}constructor(){super(),this.attachShadow({mode:"open",delegatesFocus:!0})}#c(t){if(this.hasAttribute(t)){const e=this.getAttribute(t);return/^\s*(?:true|false)\s*$/i.test(e)?"false"!==e:e}return e.#a[t]}#p(e){if("string"==typeof e||"number"==typeof e||"boolean"==typeof e||null===e)return e;if(void 0===e||"function"==typeof e||"symbol"==typeof e)return null;if("bigint"==typeof e)return`${e}n`;"[object RegExp]"===Object.prototype.toString.call(e)&&(e={__pattern:e.source,flags:e.flags});let t=JSON.stringify(e);return"{"===t[0]&&(t=t.replaceAll('"',"'")),t}#h(e,t=null){const o=e.trim();let r=parseFloat(o);if(r)return r;if(/\d+n$/.test(o))return BigInt(o.slice(0,-1));if("false"===o.toLowerCase())return!1;if("true"===o.toLowerCase())return!0;if("null"===o.toLowerCase())return null;let i,s=!1;try{let e=o;"{"===e[0]&&(s=!0,e=e.replaceAll("'",'"'));const t=JSON.parse(e);i=Object.keys(t).includes("__pattern")?new RegExp(t.__pattern,t.flags):t}catch{if(s&&t)throw t.classList.add("error"),new Error("Bad object input");i=o}return i}#u(e,t){return`\n <div class="property-wrapper">\n <label for="eo-${e}-value">${e}</label>\n <input name="${e}" readonly="true" id="eo-${e}-value" type="text" value="${t}" />\n </div>\n <div class="toolbar">\n <button class="editable-object-up-property icon" title="Move up">\n <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">\n <path d="m5 9 1.41 1.41L11 5.83V22h2V5.83l4.59 4.59L19 9l-7-7-7 7z"></path>\n </svg>\n </button>\n <button class="editable-object-down-property icon" title="Move down">\n <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">\n <path d="m19 15-1.41-1.41L13 18.17V2h-2v16.17l-4.59-4.59L5 15l7 7 7-7z"></path>\n </svg>\n </button>\n <button class="editable-object-remove-property icon" title="Remove">\n <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24">\n <path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"></path>\n </svg>\n </button>\n </div>\n `}#b(){const e=this.shadowRoot.querySelectorAll(".editable-object-up-property"),t=this.shadowRoot.querySelectorAll(".editable-object-down-property"),o=e.length;for(let r=0;r<o;r++)e[r].style.visibility=0==r?"hidden":"visible",t[r].style.visibility=r==o-1?"hidden":"visible";const r=this.#t?"add":"remove";this.shadowRoot.querySelectorAll(".editable-object-remove-property").forEach(e=>e.classList[r]("hide"))}#y(){return/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)}#v(e){for(;"LI"!==e.tagName;)e=e.parentNode;return e}#f(e){this.#w();const t=this.#v(e.target);t.classList.toggle("selected",!0),[...t.querySelectorAll("button")].forEach(e=>{e.tabIndex=0}),t.querySelector("input").focus()}#g(e){return new CustomEvent("change",{bubbles:!0,cancelable:!1,composed:!0,detail:e})}#m(e){"INPUT"!==e.target.nodeName||e.target.classList.contains("error")||"Enter"!==e.key&&" "!==e.key||e.target.click()}#x(e){if(this._editing)return;this._editing=!0;const t=this.#v(e.target).querySelector(".property-wrapper > input");t.classList.remove("error"),t.readOnly=!1,t._value=t.value;const o=this.#j.bind(this),r=this.#j.bind(this);t.addEventListener("blur",o,!1),this.#l.push({host:t,type:"blur",listener:o}),t.addEventListener("keypress",r,!1),this.#l.push({host:t,type:"keypress",listener:r}),t.focus()}#j(e){if(this._editing&&(e instanceof KeyboardEvent&&"Enter"===e.key||!(e instanceof KeyboardEvent))){e.preventDefault();const t=this.#v(e.target),o=t.querySelector(".property-wrapper > label").innerText,r=t.querySelector(".property-wrapper > input"),i=r._value,s=r.value;let n;this._editing=!1,r.readOnly=!0;let l=!1;try{n=this.#h(s,r)}catch{l=!0}this.#l.forEach(e=>{e.host.removeEventListener(e.type,e.listener)}),this.#l.length=0,l?r.classList.add("error"):this.#o(o,n)?(this.#e[o]=n,this.dispatchEvent(this.#g({action:"edit",key:o,previous:this.#h(i),new:n}))):r.classList.add("error")}}#E(e){const t=e.previousElementSibling;return!!t&&(e.parentNode.insertBefore(e,t),this.#b(),e.querySelector(".editable-object-up-property").focus(),!0)}#k(e){const t=e.nextElementSibling;return!!t&&(e.parentNode.insertBefore(t,e),this.#b(),e.querySelector(".editable-object-down-property").focus(),!0)}#L(e){e.remove(),this.#b()}#S(e){e.stopPropagation();const t=this.#v(e.target),o=t.querySelector(".property-wrapper > label").innerText.trim(),r=t.querySelector(".property-wrapper > input"),i=r.value.trim(),s=this.#h(i);this.#r(o,s)?(this.#L(t),delete this.#e[o],this.dispatchEvent(this.#g({action:"remove",key:o,previous:s,new:null}))):r.classList.add("error")}#q(e){e.stopPropagation();const t=this.#v(e.target);this.#E(t)}#A(e){e.stopPropagation();const t=this.#v(e.target);this.#k(t)}#R(){let e,t=0;return function(o){const r=(new Date).getTime(),i=r-t;i<500&&i>0?(o.preventDefault(),this.#x(o)):e=setTimeout(()=>{clearTimeout(e)},500),t=r}.bind(this)}#P(e){const t=this.#f.bind(this);e.forEach(e=>{e.addEventListener("click",t,!1),this.#n.push({host:e,type:"click",listener:t})})}#O(){const e=this.shadowRoot.querySelectorAll(".object-properties .property-wrapper"),t={remove:this.shadowRoot.querySelectorAll('.object-properties button[title="Remove"]')};this.#_(e,t)}#_(e,t){const o=this.#y(),r=this.#R(),i=this.#x.bind(this),s=this.#S.bind(this);if(this.#t){const e=["dblclick","touchend"],t=[];this.#n.filter((o,r)=>{let i=e.includes(o.type);return i?t.push(r):"remove"===o.host.title.toLowerCase()&&(i=!0,t.push(r)),i}).forEach(e=>{e.host.removeEventListener(e.type,e.listener)});let o=0;for(const e of t)this.#n.splice(e-o++,1)}else e.forEach(e=>{e.addEventListener("dblclick",i,!1),this.#n.push({host:e,type:"dblclick",listener:i}),o&&(e.addEventListener("touchend",r),this.#n.push({host:e,type:"touchend",listener:r}))}),t.remove.forEach(e=>{e.addEventListener("click",s,!1),this.#n.push({host:e,type:"click",listener:s})})}#C(e,t){const o=this.#m.bind(this),r=this.#q.bind(this),i=this.#A.bind(this);e.forEach(e=>{e.addEventListener("keypress",o,!1),this.#n.push({host:e,type:"keypress",listener:o})}),t.up.forEach(e=>{e.addEventListener("click",r,!1),this.#n.push({host:e,type:"click",listener:r})}),t.down.forEach(e=>{e.addEventListener("click",i,!1),this.#n.push({host:e,type:"click",listener:i})}),this.#_(e,t)}#M(e){e.composedPath().includes(this)||this.shadowRoot.querySelector(".editable-object").classList.add("defocused")}#T(){this.shadowRoot.querySelector(".editable-object").classList.remove("defocused")}#N(e){return e in this.#e}#w(){this.shadowRoot.querySelector(".add-new-object-property-input").classList.toggle("error",!1),[...this.shadowRoot.querySelectorAll("li")].forEach(e=>{const t=e.querySelector(".property-wrapper > input");t.classList.contains("error")&&t._value&&(t.value=t._value),t.classList.toggle("error",!1),e.classList.toggle("selected",!1),[...e.querySelectorAll("button")].forEach(e=>{e.tabIndex=-1})})}#B(e){if(e instanceof KeyboardEvent&&"Enter"===e.key||!(e instanceof KeyboardEvent)){const e=this.shadowRoot.querySelector(".add-new-object-property-input"),t=e.value.trim();if(""!==t){const o=/^\s*(?<property>[^\s:]+)\s*:\s*(?<value>[^$]+)$/,r=t.match(o)?.groups,[i,s]=r?Object.values(r):["",""],n=i.trim(),l=s.trim(),d=()=>{e.classList.add("error"),e.focus()};if(!n||!l||this.#N(n))return void d();let a,c=!1;try{a=this.#h(l,e)}catch{c=!0}if(c)d();else if(this.#i(n,a)){const t={[n]:l};this.mergeObject(t),this.dispatchEvent(this.#g({action:"add",key:n,previous:null,new:a})),this.shadowRoot.querySelector(".object-properties").lastChild.click(),e.value=""}else d()}}}#$(){const{shadowRoot:e}=this,t=e.querySelector(".new-object-property"),o=e.querySelector(".add-new-object-property-input"),r=e.querySelector(".editable-object-add-property");if(this.#t){const e=[t,o,r],i=[];this.#s.filter((t,o)=>{const r=e.includes(t.host);return r&&i.push(o),r}).forEach(e=>{e.host.removeEventListener(e.type,e.listener)});let s=0;for(const e of i)this.#s.splice(e-s++,1);t.classList.add("hide")}else{const e=this.getAttribute("add-property-placeholder");this.addPropertyPlaceholder=e;const i=this.#w.bind(this);t.addEventListener("click",i,!0),this.#s.push({host:t,type:"click",listener:i});const s=this.#B.bind(this);o.addEventListener("keypress",s,!1),this.#s.push({host:o,type:"keypress",listener:s}),r.addEventListener("click",s,!1),this.#s.push({host:r,type:"click",listener:s}),t.classList.remove("hide")}}get object(){return this.#e}set object(e){if(!e)return;this.#e&&Object.keys(this.#e).length>0&&[this.#n,this.#l].forEach(e=>{e.forEach(e=>{e.host.removeEventListener(e.type,e.listener)})});const t=this.shadowRoot.querySelector("#loading"),o=this.shadowRoot.querySelector(".object-properties");o.innerHTML="";const r=[],i=[],s={up:[],down:[],remove:[]};for(const[t,n]of Object.entries(e)){const e=document.createElement("li");e.innerHTML=this.#u(t,this.#p(n)),o.appendChild(e),r.push(e),i.push(e.querySelector(".property-wrapper")),s.up.push(e.querySelector(".editable-object-up-property")),s.down.push(e.querySelector(".editable-object-down-property")),s.remove.push(e.querySelector(".editable-object-remove-property"))}this.#P(r),this.#C(i,s),this.#b(),t.classList.add("hide"),this.#e=e}get addPropertyPlaceholder(){return this.#c("add-property-placeholder")}set addPropertyPlaceholder(t){const o="add-property-placeholder",r=this.shadowRoot.querySelector(".add-new-object-property-input");t?(this.setAttribute(o,t),r.placeholder=t):(this.removeAttribute(o),r.placeholder=e.#a[o])}set disableEdit(e){const t="disable-edit";e?(this.setAttribute(t,!0),this.#t=!0):(this.setAttribute(t,!1),this.#t=!1),this.#$(),this.#O(),this.#b()}get disableEdit(){return this.#c("disable-edit")}set onEdit(e){this.#o="function"==typeof e?e:()=>!0}get onEdit(){return this.#o}set onAdd(e){this.#i="function"==typeof e?e:()=>!0}get onAdd(){return this.#i}set onRemove(e){this.#r="function"==typeof e?e:()=>!0}get onRemove(){return this.#r}mergeObject(e){this.object={...this.#e,...e}}connectedCallback(){const{shadowRoot:e}=this;e.innerHTML='<style>:host{--eo-min-width:300px;--eo-bg-color:#fafafa;--eo-border-radius:4px;--eo-border-focused-color:#444;--eo-border-defocused-color:#aaa;--eo-item-selected-bg-color:#999;--eo-item-selected-color:#222;--eo-item-selected-border-radius:4px;--eo-item-hover-border-width:1px;--eo-item-hover-border-color:#ddd;--eo-item-hover-border-radius:4px;--eo-icon-color:#444;--eo-add-new-icon-color:#444;--eo-input-focus-outline-color:#26b;--eo-input-focus-outline-width:1px;--eo-input-focus-outline-style:auto;--eo-input-border-color:#bbb;--eo-input-border-radius:4px;--eo-input-bg-color:#444;--eo-input-color:#eee;--eo-input-font-family:sans-serif;--eo-input-placeholder-color:#aaa}:host(.disabled){pointer-events:none}.editable-object{background:var(--eo-bg-color);border-radius:var(--eo-border-radius);min-width:var(--eo-min-width);display:flex;flex-flow:column nowrap;justify-content:center;align-items:center}@media (min-width:360px){.editable-object{padding:0 .5rem}}@media (min-width:464px){.editable-object{border:1px solid var(--eo-border-focused-color);padding:1rem}.editable-object.defocused{border:1px solid var(--eo-border-defocused-color)}}ul{padding:0;margin:0;width:100%}li{line-height:2;list-style:none;cursor:default;padding:.5rem}input{padding:6px 8px;border-radius:var(--eo-input-border-radius);line-height:1.5;border:1px solid var(--eo-input-border-color);color:var(--eo-input-color);background:var(--eo-input-bg-color);font-family:var(--eo-input-font-family)}input:focus-visible{outline:var(--eo-input-focus-outline-color) var(--eo-input-focus-outline-style) var(--eo-input-focus-outline-width)}input::placeholder{color:var(--eo-input-placeholder-color)}.editable-object:not(.defocused) li.selected{background:var(--eo-item-selected-bg-color);color:var(--eo-item-selected-color);border-radius:var(--eo-item-selected-border-radius)}div>div,li{display:flex;align-items:center;justify-content:space-around}.editable-object:not(.defocused,.touch) li:hover{border:var(--eo-item-hover-border-width) solid var(--eo-item-hover-border-color);border-radius:var(--eo-item-hover-border-radius)}.property-wrapper{display:flex;flex-flow:row wrap;flex-grow:1;align-items:baseline;min-width:10em;touch-action:manipulation}.property-wrapper label{flex:1 1 100%;min-width:8em;max-width:100%}.property-wrapper input{flex:1;min-width:10em}.editable-object.defocused input{opacity:.7}@media (min-width:41.69em){.property-wrapper label{flex-basis:auto;max-width:45%;min-width:15em;padding-right:.5rem}.property-wrapper input{flex:1 1;min-width:16em}}.toolbar{display:flex;gap:1.5rem;padding-left:1.5rem}.editable-object.defocused .toolbar,li:not(.selected) .toolbar{opacity:0;pointer-events:none}.toolbar button{position:relative;fill:var(--eo-icon-color)}.editable-object-add-property.icon{fill:var(--eo-add-new-icon-color)}.toolbar button:hover::before{content:"";position:absolute;width:24px;height:24px;background:rgb(0 0 0 / 10%);left:-4px;top:-4px;border-radius:50%}.icon{background-color:transparent;border:none;cursor:pointer;font-size:0;fill:var(--eo-icon-color);padding:0}.icon svg{width:1rem;height:1rem}.new-object-property{display:flex;flex-flow:row wrap;line-height:2;margin-top:2rem;width:100%}label[for=new-property]{flex:1 0 100%}.add-new-object-property-input{min-width:18em;flex:1}.add-new-object-property-input.error,.property-wrapper input.error{outline:red auto 1px}.hide{display:none}</style><div class="editable-object defocused"><slot id=loading name=loading></slot><ul class=object-properties></ul><div class=new-object-property><label for=new-property>New Property and Value</label> <input id=new-property name=new-property class=add-new-object-property-input type=text placeholder="Add new property in key:value format"><div class=toolbar><button class="editable-object-add-property icon" title=Add><svg version=1.1 xmlns=http://www.w3.org/2000/svg xmlns:xlink=http://www.w3.org/1999/xlink width=24 height=24 viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"></path></svg></button></div></div></div>';const t=this.getAttribute("object");this.object=JSON.parse(t);const o=this.getAttribute("disable-edit");this.#t="true"===o?.toLowerCase();const r=e.querySelector(".editable-object"),i=e.querySelector("#loading"),s=this.#y(),n=this.#M.bind(this),l=this.#T.bind(this);this.object&&i.classList.add("hide"),s&&r.classList.add("touch"),document.addEventListener("click",n,!1),this.#s.push({host:document,type:"click",listener:n}),r.addEventListener("click",l,!0),this.#s.push({host:r,type:"click",listener:l}),this.#$()}disconnectedCallback(){[this.#s,this.#n,this.#l].forEach(e=>{e.forEach(e=>{e.host.removeEventListener(e.type,e.listener)})})}attributeChangedCallback(e,t,o){o!==t&&(this[e]=this.getAttribute(e))}}customElements.define("editable-object",e);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@localnerve/editable-object",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "A vanillajs editable-object web component for visual object display and editing",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"browser": "dist/editable-object.js",
|
|
@@ -18,16 +18,16 @@
|
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@axe-core/cli": "^4.10.2",
|
|
21
|
-
"@eslint/js": "^9.
|
|
22
|
-
"@localnerve/web-component-build": "^1.12.
|
|
21
|
+
"@eslint/js": "^9.32.0",
|
|
22
|
+
"@localnerve/web-component-build": "^1.12.2",
|
|
23
23
|
"babel-loader": "10.0.0",
|
|
24
|
-
"eslint": "^9.
|
|
24
|
+
"eslint": "^9.32.0",
|
|
25
25
|
"express": "5.1.0",
|
|
26
26
|
"globals": "^16.3.0",
|
|
27
27
|
"modern-normalize": "^3.0.1",
|
|
28
28
|
"rimraf": "^6.0.1",
|
|
29
|
-
"stylelint": "^16.
|
|
30
|
-
"stylelint-config-standard": "^
|
|
29
|
+
"stylelint": "^16.23.0",
|
|
30
|
+
"stylelint-config-standard": "^39.0.0",
|
|
31
31
|
"stylelint-no-unsupported-browser-features": "^8.0.4",
|
|
32
32
|
"webpack-cli": "6.0.1"
|
|
33
33
|
},
|