@craftile/preview-client-html 0.1.0
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/LICENSE.md +21 -0
- package/dist/html.cdn.js +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +430 -0
- package/package.json +55 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Craftile Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/html.cdn.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var Te=Object.defineProperty;var Pe=(y,k,m)=>k in y?Te(y,k,{enumerable:!0,configurable:!0,writable:!0,value:m}):y[k]=m;var A=(y,k,m)=>Pe(y,typeof k!="symbol"?k+"":k,m);var HtmlPreviewClient=(function(){"use strict";var y=Object.defineProperty,k=(o,e,t)=>e in o?y(o,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):o[e]=t,m=(o,e,t)=>k(o,typeof e!="symbol"?e+"":e,t);class re{constructor(e,t="*"){m(this,"handlers",new Map),m(this,"targetWindow"),m(this,"targetOrigin"),m(this,"fallbackHandler"),this.targetWindow=e,this.targetOrigin=t,window.addEventListener("message",this.handleMessage.bind(this))}handleMessage(e){const t=e.data;if(!t.type)return;const n=this.handlers.get(t.type);n?n.forEach(i=>i(t.payload,e)):this.fallbackHandler&&this.fallbackHandler(e.data,e)}send(e,t){const n={type:e,payload:t};this.targetWindow.postMessage(n,this.targetOrigin)}listen(e,t){return this.handlers.has(e)||this.handlers.set(e,[]),this.handlers.get(e).push(t),()=>{const n=this.handlers.get(e);if(n){const i=n.indexOf(t);i>-1&&n.splice(i,1)}}}registerFallbackHandler(e){this.fallbackHandler=e}}function se(o="*"){if(window.parent===window)throw new Error("Not inside an iframe");return new re(window.parent,o)}class oe{constructor(){m(this,"listeners",new Map)}on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>{this.off(e,t)}}off(e,t){const n=this.listeners.get(e);n&&(n.delete(t),n.size===0&&this.listeners.delete(e))}once(e,t){const n=i=>{t(i),this.off(e,n)};this.on(e,n)}emit(e,...t){const n=this.listeners.get(e);if(n){const i=t[0];n.forEach(r=>{try{r(i)}catch(s){console.error(`Error in event listener for '${String(e)}':`,s)}})}}removeAllListeners(e){e?this.listeners.delete(e):this.listeners.clear()}}class le{constructor(e){m(this,"active",!0),m(this,"messenger"),m(this,"currentHoveredBlock",null),m(this,"currentSelectedBlock",null),m(this,"overlayButtonHovered",!1),m(this,"resizeObserver",null),m(this,"mutationObserver",null),this.messenger=e,this.messenger.listen("craftile.inspector.enable",()=>this.enable()),this.messenger.listen("craftile.inspector.disable",()=>this.disable()),this.messenger.listen("craftile.inspector.overlay-button-enter",this.handleOverlayButtonEnter.bind(this)),this.messenger.listen("craftile.inspector.overlay-button-leave",this.handleOverlayButtonLeave.bind(this)),window.addEventListener("scroll",this.handleScroll.bind(this),{passive:!0}),this.setupGlobalEventListeners()}enable(){this.active=!0,document.body.classList.add("craftile-inspector-active")}disable(){this.active=!1,document.body.classList.remove("craftile-inspector-active"),this.currentHoveredBlock=null,this.currentSelectedBlock=null,this.overlayButtonHovered=!1}handleOverlayButtonEnter(){this.overlayButtonHovered=!0}handleOverlayButtonLeave(e){this.overlayButtonHovered=!1,this.currentHoveredBlock||this.messenger.send("craftile.preview.block-leave")}handleScroll(){this.currentSelectedBlock&&this.sendSelectedBlockPosition(),this.currentHoveredBlock&&this.sendHoveredBlockPosition()}setupGlobalEventListeners(){document.addEventListener("mouseover",this.handleGlobalHover.bind(this)),document.addEventListener("mouseout",this.handleGlobalLeave.bind(this)),document.addEventListener("click",this.handleGlobalClick.bind(this))}handleGlobalHover(e){if(!this.active)return;const t=e.target.closest("[data-block]");t&&(e.stopPropagation(),this.currentHoveredBlock=t,this.sendHoveredBlockPosition())}handleGlobalLeave(e){!this.active||!e.target.closest("[data-block]")||this.overlayButtonHovered||(this.currentHoveredBlock=null,this.messenger.send("craftile.preview.block-leave"))}handleGlobalClick(e){if(this.messenger.send("craftile.preview.click"),!this.active)return;const t=e.target.closest("[data-block]");if(!t)return;e.stopPropagation(),t.getAttribute("data-block")&&(this.currentSelectedBlock=t,this.sendSelectedBlockPosition(),this.trackSelectedBlock())}sendHoveredBlockPosition(){const e=this.computeElementPositioning(this.currentHoveredBlock);this.messenger.send("craftile.preview.block-hover",{blockRect:e.rect,parentRect:e.parentRect,scrollTop:e.scrollTop,scrollLeft:e.scrollLeft,blockId:this.currentHoveredBlock.dataset.block,parentFlexDirection:e.parentFlexDirection})}sendSelectedBlockPosition(e=!1){const t=this.computeElementPositioning(this.currentSelectedBlock);this.messenger.send(e?"craftile.preview.update-selected-block":"craftile.preview.block-select",{blockRect:t.rect,scrollTop:t.scrollTop,scrollLeft:t.scrollLeft,blockId:this.currentSelectedBlock.dataset.block})}trackSelectedBlock(){if(!this.currentSelectedBlock)return;this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=null),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=null),this.resizeObserver=new ResizeObserver(()=>{this.sendSelectedBlockPosition(!0)}),this.resizeObserver.observe(this.currentSelectedBlock),this.mutationObserver=new MutationObserver(()=>{this.sendSelectedBlockPosition(!0)}),this.mutationObserver.observe(this.currentSelectedBlock,{attributes:!0,subtree:!1});let e=this.currentSelectedBlock.parentElement;for(;e&&(this.mutationObserver.observe(e,{childList:!0,subtree:!1}),e!==document.body);)e=e.parentElement}computeElementPositioning(e){const t=e.getBoundingClientRect(),n=window.pageYOffset||document.documentElement.scrollTop,i=window.pageXOffset||document.documentElement.scrollLeft,r=this.findEldestParentBlock(e),s=r&&r!==e?r.getBoundingClientRect():void 0,l=this.getElementFlexDirection(e.parentElement);return{rect:t,parentRect:s,scrollTop:n,scrollLeft:i,parentFlexDirection:l}}findEldestParentBlock(e){let t=null,n=e.parentElement;for(;n&&n!==document.body;)n.hasAttribute("data-block")&&(t=n),n=n.parentElement;return t}getElementFlexDirection(e){if(!e)return"column";const t=window.getComputedStyle(e),n=t.display.includes("flex")?t.flexDirection:"column";return n==="row"||n==="row-reverse"?"row":"column"}}class ae extends oe{constructor(){super(),m(this,"messenger"),m(this,"inspector"),this.messenger=se(window.origin),this.inspector=new le(this.messenger),this.initialize()}initialize(){window.addEventListener("load",()=>{this.messenger.send("craftile.preview.ready",{})}),document.addEventListener("DOMContentLoaded",()=>{this.sendPageData()}),this.messenger.registerFallbackHandler(e=>{const{type:t,payload:n}=e;this.emit(t,n)})}sendPageData(){const e=document.getElementById("page-data");if(e)try{const t=JSON.parse(e.textContent||"{}");this.messenger.send("craftile.preview.page-data",{pageData:t})}catch(t){console.error("Failed to parse page data:",t)}}}var Y=11;function ce(o,e){var t=e.attributes,n,i,r,s,l;if(!(e.nodeType===Y||o.nodeType===Y)){for(var h=t.length-1;h>=0;h--)n=t[h],i=n.name,r=n.namespaceURI,s=n.value,r?(i=n.localName||i,l=o.getAttributeNS(r,i),l!==s&&(n.prefix==="xmlns"&&(i=n.name),o.setAttributeNS(r,i,s))):(l=o.getAttribute(i),l!==s&&o.setAttribute(i,s));for(var p=o.attributes,v=p.length-1;v>=0;v--)n=p[v],i=n.name,r=n.namespaceURI,r?(i=n.localName||i,e.hasAttributeNS(r,i)||o.removeAttributeNS(r,i)):e.hasAttribute(i)||o.removeAttribute(i)}}var H,de="http://www.w3.org/1999/xhtml",b=typeof document>"u"?void 0:document,fe=!!b&&"content"in b.createElement("template"),he=!!b&&b.createRange&&"createContextualFragment"in b.createRange();function ue(o){var e=b.createElement("template");return e.innerHTML=o,e.content.childNodes[0]}function ve(o){H||(H=b.createRange(),H.selectNode(b.body));var e=H.createContextualFragment(o);return e.childNodes[0]}function me(o){var e=b.createElement("body");return e.innerHTML=o,e.childNodes[0]}function pe(o){return o=o.trim(),fe?ue(o):he?ve(o):me(o)}function I(o,e){var t=o.nodeName,n=e.nodeName,i,r;return t===n?!0:(i=t.charCodeAt(0),r=n.charCodeAt(0),i<=90&&r>=97?t===n.toUpperCase():r<=90&&i>=97?n===t.toUpperCase():!1)}function ge(o,e){return!e||e===de?b.createElement(o):b.createElementNS(e,o)}function be(o,e){for(var t=o.firstChild;t;){var n=t.nextSibling;e.appendChild(t),t=n}return e}function _(o,e,t){o[t]!==e[t]&&(o[t]=e[t],o[t]?o.setAttribute(t,""):o.removeAttribute(t))}var q={OPTION:function(o,e){var t=o.parentNode;if(t){var n=t.nodeName.toUpperCase();n==="OPTGROUP"&&(t=t.parentNode,n=t&&t.nodeName.toUpperCase()),n==="SELECT"&&!t.hasAttribute("multiple")&&(o.hasAttribute("selected")&&!e.selected&&(o.setAttribute("selected","selected"),o.removeAttribute("selected")),t.selectedIndex=-1)}_(o,e,"selected")},INPUT:function(o,e){_(o,e,"checked"),_(o,e,"disabled"),o.value!==e.value&&(o.value=e.value),e.hasAttribute("value")||o.removeAttribute("value")},TEXTAREA:function(o,e){var t=e.value;o.value!==t&&(o.value=t);var n=o.firstChild;if(n){var i=n.nodeValue;if(i==t||!t&&i==o.placeholder)return;n.nodeValue=t}},SELECT:function(o,e){if(!e.hasAttribute("multiple")){for(var t=-1,n=0,i=o.firstChild,r,s;i;)if(s=i.nodeName&&i.nodeName.toUpperCase(),s==="OPTGROUP")r=i,i=r.firstChild,i||(i=r.nextSibling,r=null);else{if(s==="OPTION"){if(i.hasAttribute("selected")){t=n;break}n++}i=i.nextSibling,!i&&r&&(i=r.nextSibling,r=null)}o.selectedIndex=t}}},P=1,J=11,Q=3,Z=8;function B(){}function ke(o){if(o)return o.getAttribute&&o.getAttribute("id")||o.id}function we(o){return function(t,n,i){if(i||(i={}),typeof n=="string")if(t.nodeName==="#document"||t.nodeName==="HTML"||t.nodeName==="BODY"){var r=n;n=b.createElement("html"),n.innerHTML=r}else n=pe(n);else n.nodeType===J&&(n=n.firstElementChild);var s=i.getNodeKey||ke,l=i.onBeforeNodeAdded||B,h=i.onNodeAdded||B,p=i.onBeforeElUpdated||B,v=i.onElUpdated||B,G=i.onBeforeNodeDiscarded||B,N=i.onNodeDiscarded||B,Ee=i.onBeforeElChildrenUpdated||B,Ce=i.skipFromChildren||B,ee=i.addChild||function(a,c){return a.appendChild(c)},V=i.childrenOnly===!0,S=Object.create(null),L=[];function M(a){L.push(a)}function te(a,c){if(a.nodeType===P)for(var u=a.firstChild;u;){var d=void 0;c&&(d=s(u))?M(d):(N(u),u.firstChild&&te(u,c)),u=u.nextSibling}}function x(a,c,u){G(a)!==!1&&(c&&c.removeChild(a),N(a),te(a,u))}function z(a){if(a.nodeType===P||a.nodeType===J)for(var c=a.firstChild;c;){var u=s(c);u&&(S[u]=c),z(c),c=c.nextSibling}}z(t);function W(a){h(a);for(var c=a.firstChild;c;){var u=c.nextSibling,d=s(c);if(d){var f=S[d];f&&I(c,f)?(c.parentNode.replaceChild(f,c),D(f,c)):W(c)}else W(c);c=u}}function ye(a,c,u){for(;c;){var d=c.nextSibling;(u=s(c))?M(u):x(c,a,!0),c=d}}function D(a,c,u){var d=s(c);if(d&&delete S[d],!u){var f=p(a,c);if(f===!1||(f instanceof HTMLElement&&(a=f,z(a)),o(a,c),v(a),Ee(a,c)===!1))return}a.nodeName!=="TEXTAREA"?Se(a,c):q.TEXTAREA(a,c)}function Se(a,c){var u=Ce(a,c),d=c.firstChild,f=a.firstChild,O,w,T,$,E;e:for(;d;){for($=d.nextSibling,O=s(d);!u&&f;){if(T=f.nextSibling,d.isSameNode&&d.isSameNode(f)){d=$,f=T;continue e}w=s(f);var U=f.nodeType,C=void 0;if(U===d.nodeType&&(U===P?(O?O!==w&&((E=S[O])?T===E?C=!1:(a.insertBefore(E,f),w?M(w):x(f,a,!0),f=E,w=s(f)):C=!1):w&&(C=!1),C=C!==!1&&I(f,d),C&&D(f,d)):(U===Q||U==Z)&&(C=!0,f.nodeValue!==d.nodeValue&&(f.nodeValue=d.nodeValue))),C){d=$,f=T;continue e}w?M(w):x(f,a,!0),f=T}if(O&&(E=S[O])&&I(E,d))u||ee(a,E),D(E,d);else{var j=l(d);j!==!1&&(j&&(d=j),d.actualize&&(d=d.actualize(a.ownerDocument||b)),ee(a,d),W(d))}d=$,f=T}ye(a,f,w);var ie=q[a.nodeName];ie&&ie(a,c)}var g=t,R=g.nodeType,ne=n.nodeType;if(!V){if(R===P)ne===P?I(t,n)||(N(t),g=be(t,ge(n.nodeName,n.namespaceURI))):g=n;else if(R===Q||R===Z){if(ne===R)return g.nodeValue!==n.nodeValue&&(g.nodeValue=n.nodeValue),g;g=n}}if(g===n)N(t);else{if(n.isSameNode&&n.isSameNode(g))return;if(D(g,n,V),L)for(var X=0,Oe=L.length;X<Oe;X++){var K=S[L[X]];K&&x(K,K.parentNode,!1)}}return!V&&g!==t&&t.parentNode&&(g.actualize&&(g=g.actualize(t.ownerDocument||b)),t.parentNode.replaceChild(g,t)),g}}var Be=we(ce);class F{constructor(e,t){A(this,"previewClient");A(this,"morphdomOptions");A(this,"elementCache",new Map);A(this,"regionCommentsCache",new Map);this.previewClient=e||new ae,this.morphdomOptions=t?.morphdom||{},this.previewClient.on("updates.effects",this.handleEffects.bind(this)),this.previewClient.on("craftile.editor.updates",this.handleDirectUpdates.bind(this)),this.cacheRegionComments()}static init(e,t){return new F(e,t)}getElementCached(e){if(this.elementCache.has(e)){const n=this.elementCache.get(e);if(n.isConnected)return n;this.elementCache.delete(e)}const t=document.querySelector(`[data-block="${e}"]`);return t&&this.elementCache.set(e,t),t}cacheRegionComments(){const e=document.createTreeWalker(document.body,NodeFilter.SHOW_COMMENT,null);let t;const n=new Map;for(;t=e.nextNode();){const i=t.textContent?.trim();if(i){if(i.startsWith("BEGIN region: ")){const r=i.substring(14);n.set(r,t)}else if(i.startsWith("END region: ")){const r=i.substring(12),s=n.get(r);s&&(this.regionCommentsCache.set(r,{begin:s,end:t}),n.delete(r))}}}}invalidateCache(e){[...e.updated,...e.removed].forEach(t=>{this.elementCache.delete(t)})}handleEffects(e){const{effects:t,blocks:n,regions:i,changes:r}=e;t&&(this.invalidateCache(r),t.html&&this.handleHtmlEffects(t.html,{blocks:n,regions:i,changes:r}))}handleDirectUpdates(e){const{changes:t}=e;t.moved&&this.handleMoves(e),t.updated.length>0&&this.handleDisabledBlocks(e),t.removed.length>0&&this.handleRemoves(e)}handleMoves(e){const{blocks:t,changes:n}=e;for(const[i,r]of Object.entries(e.changes.moved)){if(r?.toParent&&n.updated?.includes(r?.toParent))continue;if(!this.isValidMoveInstruction(r)){console.warn(`Invalid move instruction for block ${i}:`,r);continue}const s=t[i];this.moveBlockUsingDOM(s,r)}}handleDisabledBlocks(e){const{blocks:t,changes:n}=e;for(const i of n.updated){const r=t[i];if(!r)continue;const l=!!this.getElementCached(i);r.disabled&&l?this.removeBlock(r.id):r.disabled}}handleRemoves(e){const{changes:t}=e;for(const n of t.removed)this.removeBlock(n)}handleHtmlEffects(e,t){const{blocks:n,regions:i,changes:r}=t;for(const s of r.added){const l=e[s];if(!l)continue;const h=n[s];if(!h){console.warn(`Block data not found for ${s}`);continue}if(h.disabled){console.debug(`Skipping disabled block ${s} during add operation`);continue}const p=this.calculatePosition(h,i,n);this.insertBlock(h,l,p)}for(const s of r.updated){const l=e[s];if(!l)continue;const h=n[s];if(!h){console.warn(`Block data not found for ${s}`);continue}if(h.disabled)continue;if(!!this.getElementCached(s))this.updateBlockHtml(h,l);else{const G=this.calculatePosition(h,i,n);this.insertBlock(h,l,G)}}}isValidMoveInstruction(e){const t=!!(e.toParent||e.toRegion),n=e.toIndex===void 0||e.toIndex>=0;return t&&n}removeBlock(e){const t=this.getElementCached(e);t?(this.previewClient.emit("block.remove.before",{blockId:e,element:t}),t.remove(),this.elementCache.delete(e),this.previewClient.emit("block.remove.after",{blockId:e,element:t})):console.warn(`Block ${e} not found for removal`)}moveBlockUsingDOM(e,t){const n=this.getElementCached(e.id),{toParent:i,toRegion:r,toIndex:s}=t;if(!n){console.warn(`Block element ${e.id} not found for move operation`);return}if(this.previewClient.emit("block.move.before",{blockId:e.id,blockType:e.type,block:e,element:n,toParent:i,toRegion:r,toIndex:s}),i){const l=this.getElementCached(i);if(!l){console.error(`Parent element ${i} not found for move operation`);return}this.insertElementInContainer(n,l,e.id,s)}else if(r){const l=this.findRegionInsertionPoint(r,s,void 0,void 0,e.id);if(!l){console.error(`Failed to find insertion point for region: ${r}`);return}l.parent.insertBefore(n,l.before)}this.previewClient.emit("block.move.after",{blockId:e.id,blockType:e.type,block:e,element:n,toParent:i,toRegion:r,toIndex:s})}insertElementInContainer(e,t,n,i){const s=Array.from(t.children).filter(h=>h.getAttribute("data-block")!==n),l=i!==void 0&&i<s.length?s[i]:null;t.insertBefore(e,l)}insertBlock(e,t,n){if(this.getElementCached(e.id))return;this.previewClient.emit("block.insert.before",{blockId:e.id,blockType:e.type,block:e,html:t,positionInfo:n});const i=this.parseHtmlElement(t,e.id);if(!i)return;const{parentId:r,regionName:s,position:l,afterId:h,beforeId:p}=n;if(r){const v=this.getElementCached(r);if(!v){console.error(`Parent element ${r} not found for block ${e.id}`);return}this.insertElementByPosition(i,v,l,h,p)}else if(s){const v=this.findRegionInsertionPoint(s,l,h,p);if(!v){console.error(`Failed to find insertion point for region: ${s}`);return}v.parent.insertBefore(i,v.before)}else console.warn(`No parent or region specified for block ${e.id}, inserting into body`),document.body.appendChild(i);this.elementCache.set(e.id,i),this.previewClient.emit("block.insert.after",{blockId:e.id,blockType:e.type,block:e,html:t,positionInfo:n})}updateBlockHtml(e,t){if(!t){console.warn(`No HTML provided for block ${e.id}`);return}const n=this.getElementCached(e.id);if(!n){console.warn(`Block element ${e.id} not found for HTML update`);return}this.previewClient.emit("block.update.before",{blockId:e.id,blockType:e.type,block:e,element:n,html:t}),Be(n,t,this.morphdomOptions),this.elementCache.delete(e.id),this.previewClient.emit("block.update.after",{blockId:e.id,blockType:e.type,block:e,element:n,html:t})}insertElementByPosition(e,t,n,i,r){let s=null;if(r)s=this.getElementCached(r);else if(i){const l=this.getElementCached(i);l&&(s=l.nextElementSibling)}else typeof n=="number"&&(s=Array.from(t.children)[n]||null);t.insertBefore(e,s)}findRegionInsertionPoint(e,t,n,i,r){const s=this.regionCommentsCache.get(e);if(!s)return console.error(`Region comments not found for region: ${e}`),null;const{begin:l,end:h}=s;let p=null;if(i){const v=this.getElementCached(i);v&&(p=v)}else if(n){const v=this.getElementCached(n);v&&v.nextSibling&&(p=v.nextSibling)}else typeof t=="number"&&(p=this.findPositionInRegion(l,h,t,r));return p||(p=h),{parent:l.parentNode,before:p}}findPositionInRegion(e,t,n,i){const r=[];let s=e.nextSibling;for(;s&&s!==t;){if(s.nodeType===Node.ELEMENT_NODE&&s.hasAttribute("data-block")){const l=s.getAttribute("data-block");(!i||l!==i)&&r.push(s)}s=s.nextSibling}return n<r.length?r[n]:null}calculatePosition(e,t,n){if(e.parentId){const i=n[e.parentId];i||console.warn(`Block data not found for block ${e.id}`);const r=i.children.indexOf(e.id);if(r===-1)return console.warn(`Block ${e.id} not found in parent ${e.parentId} children`),{parentId:e.parentId};const{afterId:s,beforeId:l}=this.findAdjacentSiblings(i.children,r,n);return{parentId:e.parentId,position:r,afterId:s,beforeId:l}}else{for(const i of t){const r=i.blocks.indexOf(e.id);if(r!==-1){const{afterId:s,beforeId:l}=this.findAdjacentSiblings(i.blocks,r,n);return{regionName:i.name,position:r,afterId:s,beforeId:l}}}return{}}}findAdjacentSiblings(e,t,n){let i,r;for(let s=t-1;s>=0;s--){const l=n[e[s]];if(l&&!l.disabled){i=e[s];break}}for(let s=t+1;s<e.length;s++){const l=n[e[s]];if(l&&!l.disabled){r=e[s];break}}return{afterId:i,beforeId:r}}parseHtmlElement(e,t){if(!e.trim())return console.error(`Empty HTML provided for block ${t}`),null;const n=document.createElement("div");n.innerHTML=e;const i=n.firstElementChild;return i||(console.error(`Invalid HTML for block ${t}: no element found`),null)}}return F})();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { PreviewClient } from '@craftile/preview-client';
|
|
2
|
+
import { default as morphdom } from 'morphdom';
|
|
3
|
+
type MorphdomFunction = typeof morphdom;
|
|
4
|
+
export type MorphdomOptions = Parameters<MorphdomFunction>[2];
|
|
5
|
+
export interface HtmlPreviewClientOptions {
|
|
6
|
+
morphdom?: MorphdomOptions;
|
|
7
|
+
}
|
|
8
|
+
export default class RawHtmlRenderer {
|
|
9
|
+
private previewClient;
|
|
10
|
+
private morphdomOptions;
|
|
11
|
+
private elementCache;
|
|
12
|
+
private regionCommentsCache;
|
|
13
|
+
constructor(previewClient?: PreviewClient, options?: HtmlPreviewClientOptions);
|
|
14
|
+
static init(previewClient?: PreviewClient, options?: HtmlPreviewClientOptions): RawHtmlRenderer;
|
|
15
|
+
private getElementCached;
|
|
16
|
+
private cacheRegionComments;
|
|
17
|
+
private invalidateCache;
|
|
18
|
+
private handleEffects;
|
|
19
|
+
private handleDirectUpdates;
|
|
20
|
+
private handleMoves;
|
|
21
|
+
private handleDisabledBlocks;
|
|
22
|
+
private handleRemoves;
|
|
23
|
+
private handleHtmlEffects;
|
|
24
|
+
private isValidMoveInstruction;
|
|
25
|
+
private removeBlock;
|
|
26
|
+
private moveBlockUsingDOM;
|
|
27
|
+
private insertElementInContainer;
|
|
28
|
+
private insertBlock;
|
|
29
|
+
private updateBlockHtml;
|
|
30
|
+
private insertElementByPosition;
|
|
31
|
+
private findRegionInsertionPoint;
|
|
32
|
+
private findPositionInRegion;
|
|
33
|
+
private calculatePosition;
|
|
34
|
+
private findAdjacentSiblings;
|
|
35
|
+
private parseHtmlElement;
|
|
36
|
+
}
|
|
37
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { PreviewClient } from "@craftile/preview-client";
|
|
5
|
+
import morphdom from "morphdom";
|
|
6
|
+
class RawHtmlRenderer {
|
|
7
|
+
constructor(previewClient, options) {
|
|
8
|
+
__publicField(this, "previewClient");
|
|
9
|
+
__publicField(this, "morphdomOptions");
|
|
10
|
+
__publicField(this, "elementCache", /* @__PURE__ */ new Map());
|
|
11
|
+
__publicField(this, "regionCommentsCache", /* @__PURE__ */ new Map());
|
|
12
|
+
this.previewClient = previewClient || new PreviewClient();
|
|
13
|
+
this.morphdomOptions = options?.morphdom || {};
|
|
14
|
+
this.previewClient.on("updates.effects", this.handleEffects.bind(this));
|
|
15
|
+
this.previewClient.on("craftile.editor.updates", this.handleDirectUpdates.bind(this));
|
|
16
|
+
this.cacheRegionComments();
|
|
17
|
+
}
|
|
18
|
+
static init(previewClient, options) {
|
|
19
|
+
return new RawHtmlRenderer(previewClient, options);
|
|
20
|
+
}
|
|
21
|
+
getElementCached(blockId) {
|
|
22
|
+
if (this.elementCache.has(blockId)) {
|
|
23
|
+
const element2 = this.elementCache.get(blockId);
|
|
24
|
+
if (element2.isConnected) {
|
|
25
|
+
return element2;
|
|
26
|
+
}
|
|
27
|
+
this.elementCache.delete(blockId);
|
|
28
|
+
}
|
|
29
|
+
const element = document.querySelector(`[data-block="${blockId}"]`);
|
|
30
|
+
if (element) {
|
|
31
|
+
this.elementCache.set(blockId, element);
|
|
32
|
+
}
|
|
33
|
+
return element;
|
|
34
|
+
}
|
|
35
|
+
cacheRegionComments() {
|
|
36
|
+
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_COMMENT, null);
|
|
37
|
+
let node;
|
|
38
|
+
const pendingRegions = /* @__PURE__ */ new Map();
|
|
39
|
+
while (node = walker.nextNode()) {
|
|
40
|
+
const text = node.textContent?.trim();
|
|
41
|
+
if (!text) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (text.startsWith("BEGIN region: ")) {
|
|
45
|
+
const regionName = text.substring("BEGIN region: ".length);
|
|
46
|
+
pendingRegions.set(regionName, node);
|
|
47
|
+
} else if (text.startsWith("END region: ")) {
|
|
48
|
+
const regionName = text.substring("END region: ".length);
|
|
49
|
+
const beginComment = pendingRegions.get(regionName);
|
|
50
|
+
if (beginComment) {
|
|
51
|
+
this.regionCommentsCache.set(regionName, {
|
|
52
|
+
begin: beginComment,
|
|
53
|
+
end: node
|
|
54
|
+
});
|
|
55
|
+
pendingRegions.delete(regionName);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
invalidateCache(changes) {
|
|
61
|
+
[...changes.updated, ...changes.removed].forEach((blockId) => {
|
|
62
|
+
this.elementCache.delete(blockId);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
handleEffects(data) {
|
|
66
|
+
const { effects, blocks, regions, changes } = data;
|
|
67
|
+
if (!effects) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
this.invalidateCache(changes);
|
|
71
|
+
if (effects.html) {
|
|
72
|
+
this.handleHtmlEffects(effects.html, { blocks, regions, changes });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
handleDirectUpdates(data) {
|
|
76
|
+
const { changes } = data;
|
|
77
|
+
if (changes.moved) {
|
|
78
|
+
this.handleMoves(data);
|
|
79
|
+
}
|
|
80
|
+
if (changes.updated.length > 0) {
|
|
81
|
+
this.handleDisabledBlocks(data);
|
|
82
|
+
}
|
|
83
|
+
if (changes.removed.length > 0) {
|
|
84
|
+
this.handleRemoves(data);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
handleMoves(updates) {
|
|
88
|
+
const { blocks, changes } = updates;
|
|
89
|
+
for (const [blockId, moveInstruction] of Object.entries(updates.changes.moved)) {
|
|
90
|
+
if (moveInstruction?.toParent && changes.updated?.includes(moveInstruction?.toParent)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (!this.isValidMoveInstruction(moveInstruction)) {
|
|
94
|
+
console.warn(`Invalid move instruction for block ${blockId}:`, moveInstruction);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const block = blocks[blockId];
|
|
98
|
+
this.moveBlockUsingDOM(block, moveInstruction);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
handleDisabledBlocks(updates) {
|
|
102
|
+
const { blocks, changes } = updates;
|
|
103
|
+
for (const blockId of changes.updated) {
|
|
104
|
+
const block = blocks[blockId];
|
|
105
|
+
if (!block) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const blockElement = this.getElementCached(blockId);
|
|
109
|
+
const isCurrentlyInDOM = !!blockElement;
|
|
110
|
+
if (block.disabled && isCurrentlyInDOM) {
|
|
111
|
+
this.removeBlock(block.id);
|
|
112
|
+
} else if (!block.disabled && !isCurrentlyInDOM) ;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
handleRemoves(updates) {
|
|
116
|
+
const { changes } = updates;
|
|
117
|
+
for (const blockId of changes.removed) {
|
|
118
|
+
this.removeBlock(blockId);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
handleHtmlEffects(htmlEffects, updates) {
|
|
122
|
+
const { blocks, regions, changes } = updates;
|
|
123
|
+
for (const blockId of changes.added) {
|
|
124
|
+
const html = htmlEffects[blockId];
|
|
125
|
+
if (!html) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const block = blocks[blockId];
|
|
129
|
+
if (!block) {
|
|
130
|
+
console.warn(`Block data not found for ${blockId}`);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (block.disabled) {
|
|
134
|
+
console.debug(`Skipping disabled block ${blockId} during add operation`);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const positionInfo = this.calculatePosition(block, regions, blocks);
|
|
138
|
+
this.insertBlock(block, html, positionInfo);
|
|
139
|
+
}
|
|
140
|
+
for (const blockId of changes.updated) {
|
|
141
|
+
const html = htmlEffects[blockId];
|
|
142
|
+
if (!html) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const block = blocks[blockId];
|
|
146
|
+
if (!block) {
|
|
147
|
+
console.warn(`Block data not found for ${blockId}`);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (block.disabled) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const blockElement = this.getElementCached(blockId);
|
|
154
|
+
const isCurrentlyInDOM = !!blockElement;
|
|
155
|
+
if (!isCurrentlyInDOM) {
|
|
156
|
+
const positionInfo = this.calculatePosition(block, regions, blocks);
|
|
157
|
+
this.insertBlock(block, html, positionInfo);
|
|
158
|
+
} else {
|
|
159
|
+
this.updateBlockHtml(block, html);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
isValidMoveInstruction(instruction) {
|
|
164
|
+
const hasTarget = !!(instruction.toParent || instruction.toRegion);
|
|
165
|
+
const hasValidIndex = instruction.toIndex === void 0 || instruction.toIndex >= 0;
|
|
166
|
+
return hasTarget && hasValidIndex;
|
|
167
|
+
}
|
|
168
|
+
removeBlock(blockId) {
|
|
169
|
+
const blockElement = this.getElementCached(blockId);
|
|
170
|
+
if (blockElement) {
|
|
171
|
+
this.previewClient.emit("block.remove.before", {
|
|
172
|
+
blockId,
|
|
173
|
+
element: blockElement
|
|
174
|
+
});
|
|
175
|
+
blockElement.remove();
|
|
176
|
+
this.elementCache.delete(blockId);
|
|
177
|
+
this.previewClient.emit("block.remove.after", {
|
|
178
|
+
blockId,
|
|
179
|
+
element: blockElement
|
|
180
|
+
});
|
|
181
|
+
} else {
|
|
182
|
+
console.warn(`Block ${blockId} not found for removal`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
moveBlockUsingDOM(block, moveInstruction) {
|
|
186
|
+
const blockElement = this.getElementCached(block.id);
|
|
187
|
+
const { toParent, toRegion, toIndex } = moveInstruction;
|
|
188
|
+
if (!blockElement) {
|
|
189
|
+
console.warn(`Block element ${block.id} not found for move operation`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
this.previewClient.emit("block.move.before", {
|
|
193
|
+
blockId: block.id,
|
|
194
|
+
blockType: block.type,
|
|
195
|
+
block,
|
|
196
|
+
element: blockElement,
|
|
197
|
+
toParent,
|
|
198
|
+
toRegion,
|
|
199
|
+
toIndex
|
|
200
|
+
});
|
|
201
|
+
if (toParent) {
|
|
202
|
+
const parentElement = this.getElementCached(toParent);
|
|
203
|
+
if (!parentElement) {
|
|
204
|
+
console.error(`Parent element ${toParent} not found for move operation`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
this.insertElementInContainer(blockElement, parentElement, block.id, toIndex);
|
|
208
|
+
} else if (toRegion) {
|
|
209
|
+
const insertionPoint = this.findRegionInsertionPoint(toRegion, toIndex, void 0, void 0, block.id);
|
|
210
|
+
if (!insertionPoint) {
|
|
211
|
+
console.error(`Failed to find insertion point for region: ${toRegion}`);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
insertionPoint.parent.insertBefore(blockElement, insertionPoint.before);
|
|
215
|
+
}
|
|
216
|
+
this.previewClient.emit("block.move.after", {
|
|
217
|
+
blockId: block.id,
|
|
218
|
+
blockType: block.type,
|
|
219
|
+
block,
|
|
220
|
+
element: blockElement,
|
|
221
|
+
toParent,
|
|
222
|
+
toRegion,
|
|
223
|
+
toIndex
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
insertElementInContainer(element, container, elementId, index) {
|
|
227
|
+
const children = Array.from(container.children);
|
|
228
|
+
const otherChildren = children.filter((child) => child.getAttribute("data-block") !== elementId);
|
|
229
|
+
const insertBefore = index !== void 0 && index < otherChildren.length ? otherChildren[index] : null;
|
|
230
|
+
container.insertBefore(element, insertBefore);
|
|
231
|
+
}
|
|
232
|
+
insertBlock(block, html, positionInfo) {
|
|
233
|
+
if (this.getElementCached(block.id)) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
this.previewClient.emit("block.insert.before", {
|
|
237
|
+
blockId: block.id,
|
|
238
|
+
blockType: block.type,
|
|
239
|
+
block,
|
|
240
|
+
html,
|
|
241
|
+
positionInfo
|
|
242
|
+
});
|
|
243
|
+
const newElement = this.parseHtmlElement(html, block.id);
|
|
244
|
+
if (!newElement) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const { parentId, regionName, position, afterId, beforeId } = positionInfo;
|
|
248
|
+
if (parentId) {
|
|
249
|
+
const parentElement = this.getElementCached(parentId);
|
|
250
|
+
if (!parentElement) {
|
|
251
|
+
console.error(`Parent element ${parentId} not found for block ${block.id}`);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
this.insertElementByPosition(newElement, parentElement, position, afterId, beforeId);
|
|
255
|
+
} else if (regionName) {
|
|
256
|
+
const insertionPoint = this.findRegionInsertionPoint(regionName, position, afterId, beforeId);
|
|
257
|
+
if (!insertionPoint) {
|
|
258
|
+
console.error(`Failed to find insertion point for region: ${regionName}`);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
insertionPoint.parent.insertBefore(newElement, insertionPoint.before);
|
|
262
|
+
} else {
|
|
263
|
+
console.warn(`No parent or region specified for block ${block.id}, inserting into body`);
|
|
264
|
+
document.body.appendChild(newElement);
|
|
265
|
+
}
|
|
266
|
+
this.elementCache.set(block.id, newElement);
|
|
267
|
+
this.previewClient.emit("block.insert.after", {
|
|
268
|
+
blockId: block.id,
|
|
269
|
+
blockType: block.type,
|
|
270
|
+
block,
|
|
271
|
+
html,
|
|
272
|
+
positionInfo
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
updateBlockHtml(block, html) {
|
|
276
|
+
if (!html) {
|
|
277
|
+
console.warn(`No HTML provided for block ${block.id}`);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const blockElement = this.getElementCached(block.id);
|
|
281
|
+
if (!blockElement) {
|
|
282
|
+
console.warn(`Block element ${block.id} not found for HTML update`);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
this.previewClient.emit("block.update.before", {
|
|
286
|
+
blockId: block.id,
|
|
287
|
+
blockType: block.type,
|
|
288
|
+
block,
|
|
289
|
+
element: blockElement,
|
|
290
|
+
html
|
|
291
|
+
});
|
|
292
|
+
morphdom(blockElement, html, this.morphdomOptions);
|
|
293
|
+
this.elementCache.delete(block.id);
|
|
294
|
+
this.previewClient.emit("block.update.after", {
|
|
295
|
+
blockId: block.id,
|
|
296
|
+
blockType: block.type,
|
|
297
|
+
block,
|
|
298
|
+
element: blockElement,
|
|
299
|
+
html
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
insertElementByPosition(element, parentElement, position, afterId, beforeId) {
|
|
303
|
+
let insertBefore = null;
|
|
304
|
+
if (beforeId) {
|
|
305
|
+
insertBefore = this.getElementCached(beforeId);
|
|
306
|
+
} else if (afterId) {
|
|
307
|
+
const afterElement = this.getElementCached(afterId);
|
|
308
|
+
if (afterElement) {
|
|
309
|
+
insertBefore = afterElement.nextElementSibling;
|
|
310
|
+
}
|
|
311
|
+
} else if (typeof position === "number") {
|
|
312
|
+
const children = Array.from(parentElement.children);
|
|
313
|
+
insertBefore = children[position] || null;
|
|
314
|
+
}
|
|
315
|
+
parentElement.insertBefore(element, insertBefore);
|
|
316
|
+
}
|
|
317
|
+
findRegionInsertionPoint(regionName, position, afterId, beforeId, excludeBlockId) {
|
|
318
|
+
const regionComments = this.regionCommentsCache.get(regionName);
|
|
319
|
+
if (!regionComments) {
|
|
320
|
+
console.error(`Region comments not found for region: ${regionName}`);
|
|
321
|
+
return null;
|
|
322
|
+
}
|
|
323
|
+
const { begin: beginComment, end: endComment } = regionComments;
|
|
324
|
+
let insertBefore = null;
|
|
325
|
+
if (beforeId) {
|
|
326
|
+
const beforeElement = this.getElementCached(beforeId);
|
|
327
|
+
if (beforeElement) {
|
|
328
|
+
insertBefore = beforeElement;
|
|
329
|
+
}
|
|
330
|
+
} else if (afterId) {
|
|
331
|
+
const afterElement = this.getElementCached(afterId);
|
|
332
|
+
if (afterElement && afterElement.nextSibling) {
|
|
333
|
+
insertBefore = afterElement.nextSibling;
|
|
334
|
+
}
|
|
335
|
+
} else if (typeof position === "number") {
|
|
336
|
+
insertBefore = this.findPositionInRegion(beginComment, endComment, position, excludeBlockId);
|
|
337
|
+
}
|
|
338
|
+
if (!insertBefore) {
|
|
339
|
+
insertBefore = endComment;
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
parent: beginComment.parentNode,
|
|
343
|
+
before: insertBefore
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
findPositionInRegion(beginComment, endComment, position, excludeBlockId) {
|
|
347
|
+
const regionBlocks = [];
|
|
348
|
+
let currentNode = beginComment.nextSibling;
|
|
349
|
+
while (currentNode && currentNode !== endComment) {
|
|
350
|
+
if (currentNode.nodeType === Node.ELEMENT_NODE && currentNode.hasAttribute("data-block")) {
|
|
351
|
+
const blockId = currentNode.getAttribute("data-block");
|
|
352
|
+
if (!excludeBlockId || blockId !== excludeBlockId) {
|
|
353
|
+
regionBlocks.push(currentNode);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
currentNode = currentNode.nextSibling;
|
|
357
|
+
}
|
|
358
|
+
return position < regionBlocks.length ? regionBlocks[position] : null;
|
|
359
|
+
}
|
|
360
|
+
calculatePosition(block, regions, blocks) {
|
|
361
|
+
if (block.parentId) {
|
|
362
|
+
const parentBlock = blocks[block.parentId];
|
|
363
|
+
if (!parentBlock) {
|
|
364
|
+
console.warn(`Block data not found for block ${block.id}`);
|
|
365
|
+
}
|
|
366
|
+
const position = parentBlock.children.indexOf(block.id);
|
|
367
|
+
if (position === -1) {
|
|
368
|
+
console.warn(`Block ${block.id} not found in parent ${block.parentId} children`);
|
|
369
|
+
return { parentId: block.parentId };
|
|
370
|
+
}
|
|
371
|
+
const { afterId, beforeId } = this.findAdjacentSiblings(parentBlock.children, position, blocks);
|
|
372
|
+
return {
|
|
373
|
+
parentId: block.parentId,
|
|
374
|
+
position,
|
|
375
|
+
afterId,
|
|
376
|
+
beforeId
|
|
377
|
+
};
|
|
378
|
+
} else {
|
|
379
|
+
for (const region of regions) {
|
|
380
|
+
const position = region.blocks.indexOf(block.id);
|
|
381
|
+
if (position !== -1) {
|
|
382
|
+
const { afterId, beforeId } = this.findAdjacentSiblings(region.blocks, position, blocks);
|
|
383
|
+
return {
|
|
384
|
+
regionName: region.name,
|
|
385
|
+
position,
|
|
386
|
+
afterId,
|
|
387
|
+
beforeId
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return {};
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
findAdjacentSiblings(siblings, currentIndex, blocks) {
|
|
395
|
+
let afterId;
|
|
396
|
+
let beforeId;
|
|
397
|
+
for (let i = currentIndex - 1; i >= 0; i--) {
|
|
398
|
+
const siblingBlock = blocks[siblings[i]];
|
|
399
|
+
if (siblingBlock && !siblingBlock.disabled) {
|
|
400
|
+
afterId = siblings[i];
|
|
401
|
+
break;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
for (let i = currentIndex + 1; i < siblings.length; i++) {
|
|
405
|
+
const siblingBlock = blocks[siblings[i]];
|
|
406
|
+
if (siblingBlock && !siblingBlock.disabled) {
|
|
407
|
+
beforeId = siblings[i];
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return { afterId, beforeId };
|
|
412
|
+
}
|
|
413
|
+
parseHtmlElement(html, blockId) {
|
|
414
|
+
if (!html.trim()) {
|
|
415
|
+
console.error(`Empty HTML provided for block ${blockId}`);
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
const temp = document.createElement("div");
|
|
419
|
+
temp.innerHTML = html;
|
|
420
|
+
const newElement = temp.firstElementChild;
|
|
421
|
+
if (!newElement) {
|
|
422
|
+
console.error(`Invalid HTML for block ${blockId}: no element found`);
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
return newElement;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
export {
|
|
429
|
+
RawHtmlRenderer as default
|
|
430
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@craftile/preview-client-html",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "HTML preview client for static block rendering in Craftile",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"craftile",
|
|
7
|
+
"preview",
|
|
8
|
+
"html",
|
|
9
|
+
"static",
|
|
10
|
+
"rendering"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"authors": [
|
|
14
|
+
{
|
|
15
|
+
"name": "Eldo Magan",
|
|
16
|
+
"email": "magan.eldo@gmail.com"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"type": "module",
|
|
20
|
+
"main": "dist/index.cjs",
|
|
21
|
+
"module": "dist/index.js",
|
|
22
|
+
"types": "dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"source": "./src/index.ts",
|
|
27
|
+
"import": "./dist/index.js",
|
|
28
|
+
"require": "./dist/index.cjs"
|
|
29
|
+
},
|
|
30
|
+
"./html.cdn.js": "./dist/html.cdn.js"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"morphdom": "^2.7.5",
|
|
37
|
+
"jsdom": "^25.0.1",
|
|
38
|
+
"typescript": "~5.8.3",
|
|
39
|
+
"vite": "^7.0.0",
|
|
40
|
+
"vite-plugin-dts": "^4.5.4",
|
|
41
|
+
"vitest": "^3.2.4",
|
|
42
|
+
"@craftile/preview-client": "0.1.0",
|
|
43
|
+
"@craftile/types": "0.1.0"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/craftile/editor.git"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsc && vite build && vite build --mode cdn"
|
|
54
|
+
}
|
|
55
|
+
}
|