native-document 1.0.9 โ 1.0.11
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/native-document.dev.js +2671 -0
- package/dist/native-document.min.js +1 -0
- package/docs/anchor.md +208 -0
- package/docs/conditional-rendering.md +628 -0
- package/docs/contributing.md +51 -0
- package/docs/core-concepts.md +513 -0
- package/docs/elements.md +383 -0
- package/docs/getting-started.md +403 -0
- package/docs/lifecycle-events.md +106 -0
- package/docs/memory-management.md +90 -0
- package/docs/observables.md +265 -0
- package/docs/routing.md +817 -0
- package/docs/state-management.md +423 -0
- package/docs/validation.md +193 -0
- package/elements.js +3 -1
- package/index.js +2 -0
- package/package.json +1 -1
- package/readme.md +189 -425
- package/router.js +2 -0
- package/src/data/MemoryManager.js +15 -5
- package/src/data/Observable.js +35 -2
- package/src/data/ObservableChecker.js +3 -0
- package/src/data/ObservableItem.js +4 -0
- package/src/data/Store.js +6 -6
- package/src/router/Router.js +13 -13
- package/src/router/link.js +8 -5
- package/src/utils/plugins-manager.js +12 -0
- package/src/utils/prototypes.js +13 -0
- package/src/utils/validator.js +23 -1
- package/src/wrappers/AttributesWrapper.js +11 -1
- package/src/wrappers/HtmlElementWrapper.js +10 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var NativeDocument=function(t){"use strict";function n(e,t,n){return e.addEventListener(t,n),e}const r={enabled:!1,enable(){this.enabled=!0,console.log("๐ NativeDocument Debug Mode enabled")},disable(){this.enabled=!1},log(e,t,n){this.enabled&&(console.group(`๐ [${e}] ${t}`),n&&console.log(n),console.trace(),console.groupEnd())},warn(e,t,n){this.enabled&&console.warn(`โ ๏ธ [${e}] ${t}`,n)},error(e,t,n){console.error(`โ [${e}] ${t}`,n)}},o=function(){let e=0;const t=new Map;let n=null;try{n=new FinalizationRegistry(e=>{r.log("MemoryManager","๐งน Auto-cleanup observable:",e),e.listeners.splice(0)})}catch(e){r.warn("MemoryManager","FinalizationRegistry not supported, observables will not be cleaned automatically")}return{register(r,o){const s=++e,i={id:s,listeners:o};return n&&n.register(r,i),t.set(s,new WeakRef(r)),s},getObservableById:e=>t.get(e)?.deref(),cleanup(){for(const[e,n]of t){const e=n.deref();e&&e.cleanup()}t.clear()},cleanObservables(e){if(t.size<e)return;let n=0;for(const[e,r]of t)r.deref()||(t.delete(e),n++);n>0&&r.log("Memory Auto Clean",`๐งน Cleaned ${n} orphaned observables`)}}}();class s extends Error{constructor(e,t={}){super(e),this.name="NativeDocumentError",this.context=t,this.timestamp=(new Date).toISOString()}}function i(e,t){this.observable=e,this.checker=t,this.subscribe=function(n){return e.subscribe(e=>{n&&n(t(e))})},this.val=function(){return t&&t(e.val())},this.check=function(t){return e.check(()=>t(this.val()))},this.set=function(t){return e.set(t)},this.trigger=function(){return e.trigger()},this.cleanup=function(){return e.cleanup()}}function a(e){if(void 0===e)throw new s("ObservableItem requires an initial value");if(e instanceof a)throw new s("ObservableItem cannot be an Observable");const t="object"==typeof e?JSON.parse(JSON.stringify(e)):e;let n=e,u=e,c=!1;const l=[],d=o.register(this,l);this.trigger=()=>{l.forEach(e=>{try{e(u,n)}catch(t){r.error("Listener Undefined","Error in observable listener:",t),this.unsubscribe(e)}})},this.originalValue=()=>t,this.set=e=>{const t="function"==typeof e?e(u):e;u!==t&&(n=u,u=t,this.trigger())},this.val=()=>u,this.cleanup=function(){l.splice(0),c=!0},this.subscribe=e=>{if(c)return r.warn("Observable subscription","โ ๏ธ Attempted to subscribe to a cleaned up observable."),()=>{};if("function"!=typeof e)throw new s("Callback must be a function");return l.push(e),()=>this.unsubscribe(e)},this.unsubscribe=e=>{const t=l.indexOf(e);t>-1&&l.splice(t,1)},this.check=function(e){return new i(this,e)};const p=this;Object.defineProperty(p,"$value",{get:()=>p.val(),set:e=>(p.set(e),p)}),this.toString=function(){return"{{#ObItem::("+d+")}}"}}const u={isObservable:e=>e instanceof a||e instanceof i,isProxy:e=>e?.__isProxy__,isObservableChecker:e=>e instanceof i,isArray:e=>Array.isArray(e),isString:e=>"string"==typeof e,isNumber:e=>"number"==typeof e,isBoolean:e=>"boolean"==typeof e,isFunction:e=>"function"==typeof e,isObject:e=>"object"==typeof e,isJson:e=>"object"==typeof e&&null!==e&&"Object"===e.constructor.name&&!Array.isArray(e),isElement:e=>e instanceof HTMLElement||e instanceof DocumentFragment||e instanceof Text,isFragment:e=>e instanceof DocumentFragment,isStringOrObservable(e){return this.isString(e)||this.isObservable(e)},isValidChild(e){return null===e||this.isElement(e)||this.isObservable(e)||["string","number","boolean"].includes(typeof e)},isValidChildren(e){Array.isArray(e)||(e=[e]);return 0===e.filter(e=>!this.isValidChild(e)).length},validateChildren(e){Array.isArray(e)||(e=[e]);const t=e.filter(e=>!this.isValidChild(e));if(t.length>0)throw new s(`Invalid children detected: ${t.map(e=>typeof e).join(", ")}`);return e},containsObservables:e=>!!e&&(u.isObject(e)&&Object.values(e).some(e=>u.isObservable(e))),containsObservableReference:e=>!(!e||"string"!=typeof e)&&/\{\{#ObItem::\([0-9]+\)\}\}/.test(e),validateAttributes(e){if(!e||"object"!=typeof e)return e;const t=[],n=Object.keys(e).filter(e=>t.includes(e));return n.length>0&&r.warn("Validator",`Reserved attributes found: ${n.join(", ")}`),e},validateEventCallback(e){if("function"!=typeof e)throw new s("Event callback must be a function")}},c=["checked","selected","disabled","readonly","required","autofocus","multiple","autocomplete","hidden","contenteditable","spellcheck","translate","draggable","async","defer","autoplay","controls","loop","muted","download","reversed","open","default","formnovalidate","novalidate","scoped","itemscope","allowfullscreen","allowpaymentrequest","playsinline"],l=function(e,t,n={}){let r=null,o=0;const{leading:s=!0,trailing:i=!0,debounce:a=!1}=n;return function(...n){const u=Date.now();if(a)return clearTimeout(r),void(r=setTimeout(()=>e.apply(this,n),t));s&&u-o>=t&&(e.apply(this,n),o=u),i&&!r&&(r=setTimeout(()=>{e.apply(this,n),o=Date.now(),r=null},t-(u-o)))}},d=function(e,t){return e.replace(new RegExp(`^[${t}]+|[${t}]+$`,"g"),"")};function p(e){return new a(e)}p.computed=function(e,t=[]){const n=new a(e()),r=()=>n.set(e());return t.forEach(e=>e.subscribe(r)),n},p.getById=function(e){const t=o.getObservableById(parseInt(e));if(!t)throw new s("Observable.getById : No observable found with id "+e);return t},p.cleanup=function(e){e.cleanup()},p.value=function(e){if(u.isObservable(e))return e.val();if(u.isProxy(e))return e.$val();if(u.isArray(e)){const t=[];return e.forEach(e=>{t.push(p.value(e))}),t}return e},p.init=function(e){const t={};for(const n in e){const r=e[n];u.isJson(r)?t[n]=p.init(r):u.isArray(r)?t[n]=p.array(r):t[n]=p(r)}const n=function(){const e={};for(const n in t){const r=t[n];u.isObservable(r)?e[n]=r.val():u.isProxy(r)?e[n]=r.$val():e[n]=r}return e},r=function(){};return new Proxy(t,{get:(e,t)=>"__isProxy__"===t||("$val"===t?n:"$clone"===t?r:void 0!==e[t]?e[t]:void 0),set(e,t,n){void 0!==e[t]&&e[t].set(n)}})},p.object=p.init,p.json=p.init,p.update=function(e,t){for(const n in t){const r=e[n],o=t[n];if(u.isObservable(r)){if(u.isArray(o)){p.update(r,o);continue}r.set(o)}else u.isProxy(r)?p.update(r,o):e[n]=o}},p.array=function(e){if(!Array.isArray(e))throw new s("Observable.array : target must be an array");const t=p(e);["push","pop","shift","unshift","reverse","sort","splice"].forEach(e=>{t[e]=function(...n){const r=t.val(),o=r[e].apply(r,arguments);return t.trigger(),o}});return["map","filter","reduce","some","every","find"].forEach(e=>{t[e]=function(n){return t.val()[e](n)}}),t},p.autoCleanup=function(e=!1,t={}){if(!e)return;const{interval:n=6e4,threshold:r=100}=t;window.addEventListener("beforeunload",()=>{o.cleanup()}),setInterval(()=>o.cleanObservables(r),n)};const h=function(e,t,n){n?e.classList.add(t):e.classList.remove(t)};function f(e,t){for(let n in t){const r=t[n];u.isObservable(r)?(h(e,n,r.val()),r.subscribe(t=>h(e,n,t))):h(e,n,r)}}function b(e,t){for(let n in t){const r=t[n];u.isObservable(r)?(e.style[n]=r.val(),r.subscribe(t=>{e.style[n]=t})):e.style[n]=r}}function m(e,t,n){const r=u.isObservable(n)?n.val():n;u.isBoolean(r)?e[t]=r:e[t]=r===e.value,u.isObservable(n)&&(["checked"].includes(t)&&e.addEventListener("input",()=>{u.isBoolean(r)?n.set(e[t]):n.set(e.value)}),n.subscribe(n=>{u.isBoolean(n)?e[t]=n:e[t]=n===e.value}))}function v(e,t,n){const r=n=>{"value"!==t?e.setAttribute(t,n):e.value=n};n.subscribe(r),r(n.val()),"value"===t&&e.addEventListener("input",()=>n.set(e.value))}const y={elements:new Map,observer:null,checkMutation:l(function(){for(const[e,t]of y.elements.entries()){const n=document.body.contains(e);n&&!t.inDom?(t.inDom=!0,t.mounted.forEach(t=>t(e))):!n&&t.inDom&&(t.inDom=!1,t.unmounted.forEach(t=>t(e)))}},10,{debounce:!0}),watch:function(e){let t={};if(y.elements.has(e))t=y.elements.get(e);else{const n=document.body.contains(e);t={inDom:n,mounted:new Set,unmounted:new Set},y.elements.set(e,t)}return{watch:()=>y.elements.set(e,t),disconnect:()=>y.elements.delete(e),mounted:e=>t.mounted.add(e),unmounted:e=>t.unmounted.add(e)}}};y.observer=new MutationObserver(y.checkMutation),y.observer.observe(document.body,{childList:!0,subtree:!0});const g=e=>u.isFunction(e)?g(e()):u.isElement(e)?e:S(String(e));function w(e){const t=document.createDocumentFragment(),n=document.createComment("Anchor Start : "+e),o=document.createComment("/ Anchor End "+e);t.appendChild(n),t.appendChild(o),t.nativeInsertBefore=t.insertBefore,t.nativeAppendChild=t.appendChild;const s=function(e,n,r){e!==t?e.insertBefore(g(n),r):e.nativeInsertBefore(g(n),r)};return t.appendChild=function(e,n=null){const i=o.parentNode;if(i){if(n=n??o,u.isArray(e))return e.forEach(e=>{s(i,e,n)}),t;s(i,e,n)}else r.error("Anchor","Anchor : parent not found",e)},t.remove=function(e){if(o.parentNode===t)return;let r,s=n.nextSibling;for(;s!==o;)r=s.nextSibling,e?s.remove():t.nativeAppendChild(s),s=r;e&&(o.remove(),n.remove())},t.insertBefore=function(e,n=null){t.appendChild(e,n)},t.clear=function(){t.remove()},t.endElement=function(){return o},t.startElement=function(){return n},t}const O=function(){const e=[];return{list:()=>e,add:t=>e.push(t)}}(),C=function(e,t){const n=document.createTextNode("");return t.subscribe(e=>n.textContent=String(e)),n.textContent=t.val(),e&&e.appendChild(n),n},E=function(e,t){const n=document.createTextNode("");return n.textContent=String(t),e&&e.appendChild(n),n},S=function(e){return u.isObservable(e)?C(null,e):E(null,e)},A={createElement:e=>e?document.createElement(e):new w("Fragment"),processChildren(e,t){if(null===e)return;(Array.isArray(e)?e:[e]).forEach(e=>{null!==e&&(u.isString(e)&&u.isFunction(e.resolveObservableTemplate)&&(e=e.resolveObservableTemplate()),u.isFunction(e)?this.processChildren(e(),t):u.isArray(e)?this.processChildren(e,t):u.isElement(e)?t.appendChild(e):u.isObservable(e)?C(t,e):e&&E(t,e))})},processAttributes(e,t){u.isFragment(e)||t&&function(e,t){if(u.validateAttributes(t),!u.isObject(t))throw new s("Attributes must be an object");for(let n in t){const r=n.toLowerCase();let o=t[r];if(u.isString(o)&&u.isFunction(o.resolveObservableTemplate)&&(o=o.resolveObservableTemplate(),u.isArray(o))){const e=o.filter(e=>u.isObservable(e));o=p.computed(()=>o.map(e=>u.isObservable(e)?e.val():e).join(" ")||" ",e)}c.includes(r)?m(e,r,o):u.isObservable(o)?v(e,r,o):"class"===r&&u.isJson(o)?f(e,o):"style"===r&&u.isJson(o)?b(e,o):e.setAttribute(r,o)}}(e,t)},setup(e,t,r){e.nd={},function(e){e.nd||(e.nd={}),e.nd.on=function(t){for(const r in t){const o=t[r];n(e,r,o)}return e},e.nd.on.prevent=function(t){for(const r in t){const o=t[r];n(e,r,t=>(t.preventDefault(),o&&o(t),e))}return e};const t={click:t=>n(e,"click",t),focus:t=>n(e,"focus",t),blur:t=>n(e,"blur",t),input:t=>n(e,"input",t),change:t=>n(e,"change",t),keyup:t=>n(e,"keyup",t),keydown:t=>n(e,"keydown",t),beforeInput:t=>n(e,"beforeinput",t),mouseOver:t=>n(e,"mouseover",t),mouseOut:t=>n(e,"mouseout",t),mouseDown:t=>n(e,"mousedown",t),mouseUp:t=>n(e,"mouseup",t),mouseMove:t=>n(e,"mousemove",t),hover:(t,n)=>{e.addEventListener("mouseover",t),e.addEventListener("mouseout",n)},dropped:t=>n(e,"drop",t),submit:t=>n(e,"submit",t),dragEnd:t=>n(e,"dragend",t),dragStart:t=>n(e,"dragstart",t),drop:t=>n(e,"drop",t),dragOver:t=>n(e,"dragover",t),dragEnter:t=>n(e,"dragenter",t),dragLeave:t=>n(e,"dragleave",t)};for(let r in t)e.nd.on[r]=t[r],e.nd.on.prevent[r]=function(t){return n(e,r.toLowerCase(),e=>{e.preventDefault(),t&&t(e)}),e}}(e);const o="function"==typeof r?r(e):e;return function(e){e.nd.wrap=t=>{if(!u.isFunction(t))throw new s("Callback must be a function");return t&&t(e),e},e.nd.ref=(t,n)=>(t[n]=e,e);let t=null;e.nd.appendChild=function(t){u.isArray(t)?A.processChildren(t,e):(u.isFunction(t)&&(t=t(),A.processChildren(t(),e)),u.isElement(t)&&A.processChildren(t,e))},e.nd.lifecycle=function(n){return t=t||y.watch(e),n.mounted&&t.mounted(n.mounted),n.unmounted&&t.unmounted(n.unmounted),e},e.nd.mounted=n=>(t=t||y.watch(e),t.mounted(n),e),e.nd.unmounted=n=>(t=t||y.watch(e),t.unmounted(n),e)}(o),O.list().forEach(e=>{e?.element?.setup&&e.element.setup(o,t)}),o}};function I(e,t){const n=e.toLowerCase().trim(),o=function(e,o=null){try{if(u.isValidChildren(e)){const t=o;o=e,e=t}const r=A.createElement(n);return A.processAttributes(r,e),A.processChildren(o,r),A.setup(r,e,t)}catch(e){r.error("ElementCreation",`Error creating ${n}`,e)}};return o.hold=(e,t)=>()=>o(e,t),o}class k extends Error{constructor(e,t){super(`${e}\n\n${t.join("\n")}\n\n`)}}const $={string:e=>({name:e,type:"string",validate:e=>u.isString(e)}),number:e=>({name:e,type:"number",validate:e=>u.isNumber(e)}),boolean:e=>({name:e,type:"boolean",validate:e=>u.isBoolean(e)}),observable:e=>({name:e,type:"observable",validate:e=>u.isObservable(e)}),element:e=>({name:e,type:"element",validate:e=>u.isElement(e)}),function:e=>({name:e,type:"function",validate:e=>u.isFunction(e)}),object:e=>({name:e,type:"object",validate:e=>u.isObject(e)}),objectNotNull:e=>({name:e,type:"object",validate:e=>u.isObject(e)&&null!==e}),children:e=>({name:e,type:"children",validate:e=>u.validateChildren(e)}),attributes:e=>({name:e,type:"attributes",validate:e=>u.validateAttributes(e)}),optional:e=>({...e,optional:!0}),oneOf:(e,...t)=>({name:e,type:"oneOf",types:t,validate:e=>t.some(t=>t.validate(e))})},R=(e,t,n="Function")=>{if(!u.isArray(t))throw new s("withValidation : argSchema must be an array");return function(...r){return((e,t,n="Function")=>{if(!t)return;const r=[],o=t.filter(e=>!e.optional).length;if(e.length<o&&r.push(`${n}: Expected at least ${o} arguments, got ${e.length}`),t.forEach((t,o)=>{const s=o+1,i=e[o];if(void 0!==i){if(!t.validate(i)){const e=i?.constructor?.name||typeof i;r.push(`${n}: Invalid argument '${t.name}' at position ${s}, expected ${t.type}, got ${e}`)}}else t.optional||r.push(`${n}: Missing required argument '${t.name}' at position ${s}`)}),r.length>0)throw new k("Argument validation failed",r)})(r,t,e.name||n),e.apply(this,r)}};Function.prototype.args=function(...e){return R(this,e)},Function.prototype.errorBoundary=function(e){return(...t)=>{try{return this.apply(this,t)}catch(t){return e(t)}}},String.prototype.use=function(e){const t=this;return p.computed(()=>t.replace(/\$\{(.*?)}/g,(t,n)=>{const r=e[n];return u.isObservable(r)?r.val():r}),Object.values(e))},String.prototype.resolveObservableTemplate=function(){return u.containsObservableReference(this)?this.split(/(\{\{#ObItem::\([0-9]+\)\}\})/g).filter(Boolean).map(e=>{if(!u.containsObservableReference(e))return e;const[t,n]=e.match(/\{\{#ObItem::\(([0-9]+)\)\}\}/);return p.getById(n)}):this};const F=function(){const e=new Map;return{use(t){const{observer:n,subscribers:r}=e.get(t),o=p(n.val()),s=n.subscribe(e=>o.set(e)),i=o.subscribe(e=>n.set(e));return o.destroy=()=>{s(),i(),o.cleanup()},r.add(o),o},follow(e){return this.use(e)},create(t,n){const r=p(n);return e.set(t,{observer:r,subscribers:new Set}),r},get(t){const n=e.get(t);return n?n.observer:null},getWithSubscribers:t=>e.get(t),delete(t){const n=e.get(t);n&&(n.observer.cleanup(),n.subscribers.forEach(e=>e.destroy()),n.observer.clear())}}}();const q=function(e,t,n=null){if(!u.isObservable(e))return r.warn("ShowIf","ShowIf : condition must be an Observable / "+n,e);const o=new w("Show if : "+(n||""));let s=null;const i=()=>s||(s="function"==typeof t?t():t,u.isStringOrObservable(s)&&(s=S(s)),s);return e.val()&&o.appendChild(i()),e.subscribe(e=>{e?o.appendChild(i()):o.remove()}),o},x=function(e,t){if(!u.isObservable(e))throw new s("Toggle : condition must be an Observable");const n=new w,r=new Map,o=function(e){if(r.has(e))return r.get(e);let n=t[e];return n?(u.isFunction(n)&&(n=n()),r.set(e,n),n):null},i=e.val(),a=o(i);return a&&n.appendChild(a),e.subscribe(e=>{const t=o(e);n.remove(),t&&n.appendChild(t)}),n},T=function(e,t,n){if(!u.isObservable(e))throw new s("Toggle : condition must be an Observable");return x(e,{true:t,false:n})},D=I("div"),j=I("span"),L=I("label"),B=I("p"),M=B,H=I("strong"),P=I("h1"),N=I("h2"),_=I("h3"),V=I("h4"),U=I("h5"),z=I("h6"),J=I("br"),W=I("a"),K=I("pre"),Q=I("code"),G=I("blockquote"),X=I("hr"),Y=I("em"),Z=I("small"),ee=I("mark"),te=I("del"),ne=I("ins"),re=I("sub"),oe=I("sup"),se=I("abbr"),ie=I("cite"),ae=I("q"),ue=I("dl"),ce=I("dt"),le=I("dd"),de=I("form",function(e){return e.submit=function(t){return"function"==typeof t?(e.on.submit(e=>{e.preventDefault(),t(e)}),e):(this.setAttribute("action",t),e)},e.multipartFormData=function(){return this.setAttribute("enctype","multipart/form-data"),e},e.post=function(t){return this.setAttribute("method","post"),this.setAttribute("action",t),e},e.get=function(e){this.setAttribute("method","get"),this.setAttribute("action",e)},e}),pe=I("input"),he=I("textarea"),fe=he,be=I("select"),me=I("fieldset"),ve=I("option"),ye=I("legend"),ge=I("datalist"),we=I("output"),Oe=I("progress"),Ce=I("meter"),Ee=I("button"),Se=I("main"),Ae=I("section"),Ie=I("article"),ke=I("aside"),$e=I("nav"),Re=I("figure"),Fe=I("figcaption"),qe=I("header"),xe=I("footer"),Te=I("img"),De=function(e,t){return Te({src:e,...t})},je=I("details"),Le=I("summary"),Be=I("dialog"),Me=I("menu"),He=I("ol"),Pe=I("ul"),Ne=I("li"),_e=I("audio"),Ve=I("video"),Ue=I("source"),ze=I("track"),Je=I("canvas"),We=I("svg"),Ke=I("time"),Qe=I("data"),Ge=I("address"),Xe=I("kbd"),Ye=I("samp"),Ze=I("var"),et=I("wbr"),tt=I("caption"),nt=I("table"),rt=I("thead"),ot=I("tfoot"),st=I("tbody"),it=I("tr"),at=it,ut=I("th"),ct=ut,lt=ut,dt=I("td"),pt=dt,ht=I("");var ft=Object.freeze({__proto__:null,Abbr:se,Address:Ge,Anchor:w,Article:Ie,Aside:ke,AsyncImg:function(e,t,n,r){const o=De(t||e,n),i=new Image;return i.onload=()=>{u.isFunction(r)&&r(null,o),o.src=e},i.onerror=()=>{u.isFunction(r)&&r(new s("Image not found"))},u.isObservable(e)&&e.subscribe(e=>{i.src=e}),i.src=e,o},Audio:_e,BaseImage:Te,Blockquote:G,Br:J,Button:Ee,Canvas:Je,Caption:tt,Checkbox:e=>pe({type:"checkbox",...e}),Cite:ie,Code:Q,ColorInput:e=>pe({type:"color",...e}),Data:Qe,Datalist:ge,DateInput:e=>pe({type:"date",...e}),DateTimeInput:e=>pe({type:"datetime-local",...e}),Dd:le,Del:te,Details:je,Dialog:Be,Div:D,Dl:ue,Dt:ce,Em:Y,EmailInput:e=>pe({type:"email",...e}),FieldSet:me,FigCaption:Fe,Figure:Re,FileInput:e=>pe({type:"file",...e}),Footer:xe,ForEach:function(e,t,n){const r=new w("ForEach"),o=r.endElement();let s=new Map;const i=(e,r)=>{const o=((e,t,n)=>{if(u.isFunction(n))return n(e,t);if(u.isObservable(e)){const r=e.val();return r&&n?r[n]:t}return e[n]??t})(e,r,n);if(s.has(o))s.get(o).indexObserver.set(r);else{const n=p(r);let i=t(e,n);u.isStringOrObservable(i)&&(i=S(i)),s.set(o,{child:i,indexObserver:n})}return o},a=new Set,c=()=>{const t=u.isObservable(e)?e.val():e,n=o.parentNode;if(!n)return;if(a.clear(),Array.isArray(t))t.forEach((e,t)=>a.add(i(e,t)));else for(const e in t)a.add(i(t[e],e));((e,t)=>{for(const[n,{child:r}]of e.entries())t.has(n)||r.remove()})(s,a);let r=o;for(const e of[...a].reverse()){const{child:t}=s.get(e);if(t){if(r&&r.previousSibling===t){r=t;continue}n.insertBefore(t,r),r=t}}};return c(),u.isObservable(e)&&e.subscribe(l((e,t)=>{c()},50,{debounce:!0})),r},Form:de,Fragment:ht,H1:P,H2:N,H3:_,H4:V,H5:U,H6:z,Header:qe,HiddenInput:e=>pe({type:"hidden",...e}),HideIf:function(e,t,n){const r=p(!e.val());return e.subscribe(e=>r.set(!e)),q(r,t,n)},HideIfNot:function(e,t,n){return q(e,t,n)},Hr:X,Img:De,Input:pe,Ins:ne,Kbd:Xe,Label:L,LazyImg:function(e,t){return De(e,{...t,loading:"lazy"})},Legend:ye,Link:W,ListItem:Ne,Main:Se,Mark:ee,Match:x,Menu:Me,Meter:Ce,MonthInput:e=>pe({type:"month",...e}),Nav:$e,NumberInput:e=>pe({type:"number",...e}),Option:ve,OrderedList:He,Output:we,P:B,Paragraph:M,PasswordInput:e=>pe({type:"password",...e}),Pre:K,Progress:Oe,Quote:ae,Radio:e=>pe({type:"radio",...e}),RangeInput:e=>pe({type:"range",...e}),ReadonlyInput:e=>pe({readonly:!0,...e}),Samp:Ye,SearchInput:e=>pe({type:"search",...e}),Section:Ae,Select:be,ShowIf:q,SimpleButton:(e,t)=>Ee(e,{type:"button",...t}),Small:Z,Source:Ue,Span:j,Strong:H,Sub:re,SubmitButton:(e,t)=>Ee(e,{type:"submit",...t}),Summary:Le,Sup:oe,Svg:We,Switch:T,TBody:st,TBodyCell:pt,TFoot:ot,TFootCell:lt,THead:rt,THeadCell:ct,TRow:at,Table:nt,Td:dt,TelInput:e=>pe({type:"tel",...e}),TextArea:he,TextInput:fe,Th:ut,Time:Ke,TimeInput:e=>pe({type:"time",...e}),Tr:it,Track:ze,UnorderedList:Pe,UrlInput:e=>pe({type:"url",...e}),Var:Ze,Video:Ve,Wbr:et,WeekInput:e=>pe({type:"week",...e}),When:function(e){if(!u.isObservable(e))throw new s("When : condition must be an Observable");let t=null,n=null;return{show(e){return t=e,this},otherwise:r=>(n=r,T(e,t,n))}}});const bt={};function mt(e,t,n={}){e="/"+d(e,"/");let r=null,o=n.name||null;const s=n.middlewares||[],i=n.shouldRebuild||!1,a=n.with||{},u={},c=[],l=e=>{if(!e)return null;const[t,n]=e.split(":");let r=a[t];return!r&&n&&(r=bt[n]),r||(r="[^/]+"),r=r.replace("(","(?:"),{name:t,pattern:`(${r})`}},p=()=>{if(r)return r;const t=e.replace(/\{(.*?)}/gi,(e,t)=>{const n=l(t);return n&&n.pattern?(u[n.name]=n.pattern,c.push(n.name),n.pattern):e});return r=new RegExp("^"+t+"$"),r};this.name=()=>o,this.component=()=>t,this.middlewares=()=>s,this.shouldRebuild=()=>i,this.path=()=>e,this.match=function(e){e="/"+d(e,"/");if(!p().exec(e))return!1;const t={};return p().exec(e).forEach((e,n)=>{if(n<1)return;const r=c[n-1];t[r]=e}),t},this.url=function(t){const n=e.replace(/\{(.*?)}/gi,(e,n)=>{const r=l(n);if(t.params&&t.params[r.name])return t.params[r.name];throw new Error(`Missing parameter '${r.name}'`)}),r="object"==typeof t.query?new URLSearchParams(t.query).toString():null;return(t.basePath?t.basePath:"")+(r?`${n}?${r}`:n)}}class vt extends Error{constructor(e,t){super(e),this.context=t}}const yt=(e,t)=>{const n=[];return e.forEach(e=>{n.push(d(e.suffix,"/"))}),n.push(d(t,"/")),n.join("/")},gt=(e,t)=>{const n=[];return e.forEach(e=>{e.options.middlewares&&n.push(...e.options.middlewares)}),t&&n.push(...t),n},wt=(e,t)=>{const n=[];return e.forEach(e=>{e.options?.name&&n.push(e.options.name)}),t&&n.push(t),n.join(".")};function Ot(){const e=[];let t=0;const n=n=>{const o=t+n;if(!e[o])return;t=o;const{route:s,params:i,query:a,path:u}=e[o];r(u)},r=e=>{window.location.replace(`${window.location.pathname}${window.location.search}#${e}`)},o=()=>window.location.hash.slice(1);this.push=function(n){const{route:s,params:i,query:a,path:u}=this.resolve(n);u!==o()&&(e.splice(t+1),e.push({route:s,params:i,query:a,path:u}),t++,r(u))},this.replace=function(n){const{route:r,params:s,query:i,path:a}=this.resolve(n);a!==o()&&(e[t]={route:r,params:s,query:i,path:a})},this.forward=function(){return t<e.length-1&&n(1)},this.back=function(){return t>0&&n(-1)},this.init=function(n){window.addEventListener("hashchange",()=>{const{route:e,params:t,query:n,path:r}=this.resolve(o());this.handleRouteChange(e,t,n,r)});const{route:r,params:s,query:i,path:a}=this.resolve(n||o());e.push({route:r,params:s,query:i,path:a}),t=0,this.handleRouteChange(r,s,i,a)}}function Ct(){this.push=function(e){try{const{route:t,path:n,params:r,query:o}=this.resolve(e);if(window.history.state&&window.history.state.path===n)return;window.history.pushState({name:t.name(),params:r,path:n},t.name()||n,n),this.handleRouteChange(t,r,o,n)}catch(e){r.error("HistoryRouter","Error in pushState",e)}},this.replace=function(e){const{route:t,path:n,params:o}=this.resolve(e);try{window.history.replaceState({name:t.name(),params:o,path:n},t.name()||n,n),this.handleRouteChange(t,o,{},n)}catch(e){r.error("HistoryRouter","Error in replaceState",e)}},this.forward=function(){window.history.forward()},this.back=function(){window.history.back()},this.init=function(e){window.addEventListener("popstate",e=>{try{if(!e.state||!e.state.path)return;const t=e.state.path,{route:n,params:r,query:o,path:s}=this.resolve(t);if(!n)return;this.handleRouteChange(n,r,o,s)}catch(e){r.error("HistoryRouter","Error in popstate event",e)}});const{route:t,params:n,query:o,path:s}=this.resolve(e||window.location.pathname+window.location.search);this.handleRouteChange(t,n,o,s)}}function Et(){const e=[];let t=0;const n=n=>{const r=t+n;if(!e[r])return;t=r;const{route:o,params:s,query:i,path:a}=e[r];this.handleRouteChange(o,s,i,a)};this.push=function(n){const{route:r,params:o,query:s,path:i}=this.resolve(n);e[t]&&e[t].path===i||(e.splice(t+1),e.push({route:r,params:o,query:s,path:i}),t++,this.handleRouteChange(r,o,s,i))},this.replace=function(n){const{route:r,params:o,query:s,path:i}=this.resolve(n);e[t]={route:r,params:o,query:s,path:i},this.handleRouteChange(r,o,s,i)},this.forward=function(){return t<e.length-1&&n(1)},this.back=function(){return t>0&&n(-1)},this.init=function(n){const r=n||window.location.pathname+window.location.search,{route:o,params:s,query:i,path:a}=this.resolve(r);e.push({route:o,params:s,query:i,path:a}),t=0,this.handleRouteChange(o,s,i,a)}}const St="default";function At(e={}){const t=[],n={},o=[],s=[],i={route:null,params:null,query:null,path:null,hash:null};if("hash"===e.mode)Ot.apply(this,[]);else if("history"===e.mode)Ct.apply(this,[]);else{if("memory"!==e.mode)throw new vt("Invalid router mode "+e.mode);Et.apply(this,[])}const a=function(e,t){for(const n of s)try{n(e),t&&t(e)}catch(e){r.warn("Route Listener","Error in listener:",e)}};this.routes=()=>[...t],this.currentState=()=>({...i}),this.add=function(e,r,s){const i=new mt(yt(o,e),r,{...s,middlewares:gt(o,s?.middlewares||[]),name:s?.name?wt(o,s.name):null});return t.push(i),i.name()&&(n[i.name()]=i),this},this.group=function(e,t,n){if(!u.isFunction(n))throw new vt("Callback must be a function");return o.push({suffix:e,options:t}),n(),o.pop(),this},this.generateUrl=function(e,t={},r={}){const o=n[e];if(!o)throw new vt(`Route not found for name: ${e}`);return o.url({params:t,query:r})},this.resolve=function(e){if(u.isJson(e)){const t=n[e.name];if(!t)throw new vt(`Route not found for name: ${e.name}`);return{route:t,params:e.params,query:e.query,path:t.url({...e})}}const[r,o]=e.split("?"),s="/"+d(r,"/");let i,a=null;for(const e of t)if(i=e.match(s),i){a=e;break}if(!a)throw new vt(`Route not found for url: ${r}`);const c={};if(o){const e=new URLSearchParams(o).entries();for(const[t,n]of e)c[t]=n}return{route:a,params:i,query:c,path:e}},this.subscribe=function(e){if(!u.isFunction(e))throw new vt("Listener must be a function");return s.push(e),()=>{s.splice(s.indexOf(e),1)}},this.handleRouteChange=function(e,t,n,r){i.route=e,i.params=t,i.query=n,i.path=r;const o=[...e.middlewares(),a];let s=0;const u={...i},c=e=>{if(s++,!(s>=o.length))return o[s](e||u,c)};return o[s](u,c)}}function It(e,t){const{to:n,href:r,...o}=e,s=n||r;if(u.isString(s)){const e=At.get();return W({...o,href:s},t).nd.on.prevent.click(()=>{e.push(s)})}const i=s.router||St,a=At.get(i);if(console.log(i),!a)throw new vt('Router not found "'+i+'" for link "'+s.name+'"');const c=a.generateUrl(s.name,s.params,s.query);return W({...o,href:c},t).nd.on.prevent.click(()=>{a.push(c)})}At.routers={},At.create=function(t,n){if(!u.isFunction(n))throw r.error("Router","Callback must be a function",e),new vt("Callback must be a function");const o=new At(t);return At.routers[t.name||St]=o,n(o),o.init(t.entry),o.mount=function(e){if(u.isString(e)){const t=document.querySelector(e);if(!t)throw new vt(`Container not found for selector: ${e}`);e=t}else if(!u.isElement(e))throw new vt("Container must be a string or an Element");return function(e,t){const n=new Map,r=function(e){t.innerHTML="",t.appendChild(e)},o=function(e){if(!e.route)return;const{route:t,params:o,query:s,path:i}=e;if(n.has(i)){const e=n.get(i);return void r(e)}const a=t.component()({params:o,query:s});n.set(i,a),r(a)};return e.subscribe(o),o(e.currentState()),t}(o,e)},o},At.get=function(e){const t=At.routers[e||St];if(!t)throw new vt(`Router not found for name: ${e}`);return t},At.push=function(e,t=null){return At.get(t).push(e)},At.replace=function(e,t=null){return At.get(t).replace(e)},At.forward=function(e=null){return At.get(e).forward()},At.back=function(e=null){return At.get(e).back()},It.blank=function(e,t){return W({...e,target:"_blank"},t)};var kt=Object.freeze({__proto__:null,Link:It,RouteParamPatterns:bt,Router:At});return t.ArgTypes=$,t.ElementCreator=A,t.HtmlElementWrapper=I,t.Observable=p,t.Store=F,t.elements=ft,t.router=kt,t.withValidation=R,t}({});
|
package/docs/anchor.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Anchor
|
|
2
|
+
|
|
3
|
+
Anchors are a NativeDocument class that enables dynamic DOM manipulation without wrapper elements. They create two invisible comment nodes that act as boundaries, allowing you to insert, remove, and replace content between them.
|
|
4
|
+
|
|
5
|
+
## What are Anchors?
|
|
6
|
+
|
|
7
|
+
Anchors are instances of the Anchor class that use two comment nodes as invisible markers:
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
// Create an anchor instance
|
|
11
|
+
const anchor = new Anchor("My Content");
|
|
12
|
+
|
|
13
|
+
// In the DOM, this creates:
|
|
14
|
+
// <!-- Anchor Start : My Content -->
|
|
15
|
+
// <!-- / Anchor End My Content -->
|
|
16
|
+
|
|
17
|
+
// Content can be inserted between these markers
|
|
18
|
+
anchor.appendChild(Div("Dynamic content"));
|
|
19
|
+
// <!-- Anchor Start : My Content -->
|
|
20
|
+
// <div>Dynamic content</div>
|
|
21
|
+
// <!-- / Anchor End My Content -->
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Fragment vs Anchor
|
|
25
|
+
|
|
26
|
+
**Fragment** is a wrapper around `document.createDocumentFragment()`:
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
// Fragment is standard DOM DocumentFragment
|
|
30
|
+
const fragment = Fragment(); // Wraps document.createDocumentFragment()
|
|
31
|
+
fragment.appendChild(Div("Standard fragment content"));
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Anchor** is a NativeDocument class for dynamic content management:
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
// Anchor is a NativeDocument class
|
|
38
|
+
const anchor = new Anchor("Dynamic Area");
|
|
39
|
+
anchor.appendChild(Div("Dynamic content")); // Uses comment markers system
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Dynamic Content Insertion
|
|
43
|
+
|
|
44
|
+
### Creating Anchors
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
// Create anchor with custom identifier
|
|
48
|
+
const contentAnchor = new Anchor("Content Area");
|
|
49
|
+
const listAnchor = new Anchor("Todo List");
|
|
50
|
+
|
|
51
|
+
// Anchor needs to be added to parent container
|
|
52
|
+
const container = Div();
|
|
53
|
+
container.appendChild(contentAnchor);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### appendChild() - Add Content Between Markers
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
const anchor = new Anchor("Dynamic Section");
|
|
60
|
+
const container = Div();
|
|
61
|
+
container.appendChild(anchor);
|
|
62
|
+
|
|
63
|
+
// Add content between the markers
|
|
64
|
+
anchor.appendChild(Div("Dynamic content 1"));
|
|
65
|
+
anchor.appendChild(Div("Dynamic content 2"));
|
|
66
|
+
|
|
67
|
+
// DOM structure:
|
|
68
|
+
// <div>
|
|
69
|
+
// <!-- Anchor Start : Dynamic Section -->
|
|
70
|
+
// <div>Dynamic content 1</div>
|
|
71
|
+
// <div>Dynamic content 2</div>
|
|
72
|
+
// <!-- / Anchor End Dynamic Section -->
|
|
73
|
+
// </div>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### remove() - Clear Content Between Markers
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
// Remove all content between markers (markers remain)
|
|
80
|
+
anchor.remove(); // Content cleared, anchor can be reused
|
|
81
|
+
|
|
82
|
+
// Remove markers and all content permanently
|
|
83
|
+
anchor.remove(true); // Destroys the entire anchor system
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### clear() - Empty Content
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
// Alias for remove() - clears content but keeps anchor
|
|
90
|
+
anchor.clear();
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Anchor Methods
|
|
94
|
+
|
|
95
|
+
### insertBefore() - Positioned Insertion
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
const anchor = Fragment();
|
|
99
|
+
const element1 = Div("First");
|
|
100
|
+
const element2 = Div("Second");
|
|
101
|
+
|
|
102
|
+
anchor.appendChild(element1);
|
|
103
|
+
anchor.insertBefore(element2, element1); // Inserts before element1
|
|
104
|
+
// Result: element2, element1
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Access Markers
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
const anchor = Fragment();
|
|
111
|
+
|
|
112
|
+
// Get the start and end comment nodes
|
|
113
|
+
const start = anchor.startElement();
|
|
114
|
+
const end = anchor.endElement();
|
|
115
|
+
|
|
116
|
+
console.log(start.textContent); // "Anchor Start : Fragment"
|
|
117
|
+
console.log(end.textContent); // "/ Anchor End Fragment"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Practical Usage with Conditionals
|
|
121
|
+
|
|
122
|
+
### ShowIf with Anchors
|
|
123
|
+
|
|
124
|
+
```javascript
|
|
125
|
+
const isVisible = Observable(false);
|
|
126
|
+
|
|
127
|
+
// ShowIf returns an anchor, not a wrapper element
|
|
128
|
+
const content = ShowIf(isVisible, () =>
|
|
129
|
+
Div("This content appears/disappears dynamically")
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// No wrapper div created - content inserted directly between markers
|
|
133
|
+
isVisible.set(true); // Content appears between comment nodes
|
|
134
|
+
isVisible.set(false); // Content disappears, markers remain for reuse
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### ForEach with Anchors
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
const items = Observable.array(["Item 1", "Item 2"]);
|
|
141
|
+
|
|
142
|
+
// ForEach returns an anchor managing multiple elements
|
|
143
|
+
const list = ForEach(items, (item) =>
|
|
144
|
+
Div(item)
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Multiple divs managed between the same anchor markers
|
|
148
|
+
items.push("Item 3"); // New div inserted at anchor position
|
|
149
|
+
items.splice(0, 1); // First div removed, others shift within markers
|
|
150
|
+
```
|
|
151
|
+
:m
|
|
152
|
+
### Multiple Elements Without Wrapper
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
const anchor = new Anchor("Multi Content");
|
|
156
|
+
const container = Div();
|
|
157
|
+
container.appendChild(anchor);
|
|
158
|
+
|
|
159
|
+
// Insert multiple elements without a containing wrapper
|
|
160
|
+
anchor.appendChild([
|
|
161
|
+
H1("Title"),
|
|
162
|
+
P("Paragraph"),
|
|
163
|
+
Button("Action")
|
|
164
|
+
]);
|
|
165
|
+
|
|
166
|
+
// DOM: No wrapper element, just the three elements between markers
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Why Use Anchors vs Fragment?
|
|
170
|
+
|
|
171
|
+
**Use Fragment** for standard DOM operations:
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
// Standard DocumentFragment behavior
|
|
175
|
+
const fragment = Fragment();
|
|
176
|
+
fragment.appendChild(Div("Content"));
|
|
177
|
+
// Gets replaced entirely when appended to parent
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Use Anchor** for dynamic content management:
|
|
181
|
+
|
|
182
|
+
```javascript
|
|
183
|
+
// Dynamic content area that can be updated multiple times
|
|
184
|
+
const anchor = new Anchor("Updates");
|
|
185
|
+
anchor.appendChild(Div("Initial content"));
|
|
186
|
+
anchor.remove(); // Clear content
|
|
187
|
+
anchor.appendChild(Div("New content")); // Add different content - markers remain
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Best Practices
|
|
191
|
+
|
|
192
|
+
1. **Use anchors for custom rendering systems** - Build optimized conditional logic
|
|
193
|
+
2. **Anchors are reusable** - Content can be added/removed multiple times
|
|
194
|
+
3. **Use remove(true)** only when permanently destroying the anchor
|
|
195
|
+
4. **Anchors are invisible** - They don't affect layout or styling
|
|
196
|
+
5. **Create your own patterns** - Anchors enable custom rendering solutions that can outperform built-in functions
|
|
197
|
+
|
|
198
|
+
## Next Steps
|
|
199
|
+
|
|
200
|
+
- **[Getting Started](docs/getting-started.md)** - Installation and first steps
|
|
201
|
+
- **[Core Concepts](docs/core-concepts.md)** - Understanding the fundamentals
|
|
202
|
+
- **[Observables](docs/observables.md)** - Reactive state management
|
|
203
|
+
- **[Elements](docs/elements.md)** - Creating and composing UI
|
|
204
|
+
- **[Conditional Rendering](docs/conditional-rendering.md)** - Dynamic content
|
|
205
|
+
- **[Routing](docs/routing.md)** - Navigation and URL management
|
|
206
|
+
- **[State Management](docs/state-management.md)** - Global state patterns
|
|
207
|
+
- **[Lifecycle Events](docs/lifecycle-events.md)** - Lifecycle events
|
|
208
|
+
- **[Memory Management](docs/memory-management.md)** - Memory management
|