@fluffjs/fluff 0.2.4 → 0.3.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/bundle.min.js +1 -0
- package/decorators/Component.d.ts +0 -8
- package/decorators/Component.js +0 -11
- package/decorators/Input.d.ts +1 -1
- package/decorators/InputOutputHelper.d.ts +2 -1
- package/decorators/InputOutputHelper.js +3 -1
- package/decorators/Output.d.ts +1 -1
- package/decorators/Pipe.d.ts +1 -0
- package/decorators/Pipe.js +1 -1
- package/decorators/Reactive.d.ts +2 -1
- package/decorators/Reactive.js +1 -1
- package/index.d.ts +2 -1
- package/index.js +1 -1
- package/interfaces/FluffHostElement.d.ts +13 -3
- package/interfaces/ReactiveOptions.d.ts +6 -0
- package/interfaces/ReactiveOptions.js +1 -0
- package/package.json +1 -1
- package/runtime/FluffBase.d.ts +11 -1
- package/runtime/FluffBase.js +50 -17
- package/runtime/FluffElementImpl.d.ts +9 -21
- package/runtime/FluffElementImpl.js +48 -149
- package/runtime/ForController.js +1 -2
- package/runtime/IfController.js +1 -2
- package/runtime/MarkerConfigGuards.d.ts +0 -2
- package/runtime/MarkerConfigGuards.js +0 -3
- package/runtime/MarkerController.d.ts +1 -9
- package/runtime/MarkerController.js +28 -144
- package/runtime/MarkerManager.d.ts +2 -1
- package/runtime/MarkerManager.js +1 -2
- package/runtime/MarkerManagerInterface.d.ts +3 -1
- package/runtime/SwitchController.js +1 -2
- package/runtime/TextController.d.ts +0 -1
- package/runtime/TextController.js +5 -19
- package/runtime/tests/TestChildTasksListComponent.js +2 -2
- package/runtime/tests/TestForComponent.js +2 -2
- package/runtime/tests/TestForReinsertBindsInputParentComponent.js +2 -2
- package/runtime/tests/TestForTextMarkerCollisionNoTrackParentComponent.js +2 -2
- package/runtime/tests/TestForTextMarkerCollisionParentComponent.js +2 -2
- package/runtime/tests/TestForUnsubscribeNestedParentComponent.js +2 -2
- package/runtime/tests/TestGetterReactivityComponent.js +2 -2
- package/runtime/tests/TestHarness.d.ts +9 -0
- package/runtime/tests/TestHarness.js +29 -0
- package/runtime/tests/TestIfReinsertBindsInputChildComponent.js +2 -2
- package/runtime/tests/TestIfReinsertBindsInputParentComponent.js +2 -2
- package/runtime/tests/TestIfUnsubscribeNestedParentComponent.js +2 -2
- package/runtime/tests/TestInterpolationNestedPropertyComponentBase.js +2 -2
- package/runtime/tests/TestLateDefineForComponent.js +2 -2
- package/runtime/tests/TestNullInputTextComponent.js +2 -2
- package/runtime/tests/TestOutputBindingParentComponent.js +2 -2
- package/runtime/tests/TestParentBindsTasksComponent.js +2 -2
- package/runtime/tests/TestSwitchReinsertBindsInputChildComponent.js +2 -2
- package/runtime/tests/TestSwitchReinsertBindsInputParentComponent.js +2 -2
- package/runtime/tests/TestSwitchUnsubscribeNestedParentComponent.js +2 -2
- package/runtime/tests/TestTemplateNestedMarkersComponent.js +2 -2
- package/runtime/tests/TestUnsubscribeNestedGrandchildComponent.js +2 -2
- package/runtime/tests/createPipeUnwrapTestComponent.js +2 -2
- package/runtime/tests/createTestInterpolationPipeComponent.js +2 -2
- package/runtime/tests/createTestInterpolationPipeWithArgsComponent.js +2 -2
- package/utils/Property.d.ts +8 -2
- package/utils/Property.js +48 -8
- package/utils/DomUtils.d.ts +0 -3
- package/utils/DomUtils.js +0 -6
package/bundle.min.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var V=new Map;function N(a){return function(t){let e={...a,inputs:new Map,outputs:new Map};return V.set(t,e),t}}function $(a){return V.get(a)}var C=class{static getOrCreateArray(t,e){let n=Reflect.get(t,e);if(this.isTypedArray(n))return n;let r=[];return Reflect.set(t,e,r),r}static isTypedArray(t){return Array.isArray(t)}};function H(a){return function(t,e){let n=t.constructor;C.getOrCreateArray(n,"__hostBindings").push({property:String(e),hostProperty:a})}}function W(a){return function(t,e,n){let r=t.constructor;return C.getOrCreateArray(r,"__hostListeners").push({method:String(e),event:a}),n}}function z(a){return typeof a=="function"}function I(a){return function(t){return function(e,n){let{constructor:r}=e;if(!z(r))return;let s=$(r);if(s){let i=typeof t=="string"?t:t?.alias??String(n);s[a].set(String(n),i)}}}}var D=I("inputs");function j(a){return(t,e,n)=>{}}var q=I("outputs");var B=new Map;function R(a){let t=B.get(a);if(!t)return;let e=new t;return(n,...r)=>e.transform(n,...r)}function J(a){return function(t){return t.__pipeName=a,B.set(a,t),t}}function G(a){return(t,e)=>{}}function K(a){return function(t,e){let n=t.constructor;C.getOrCreateArray(n,"__viewChildren").push({property:String(e),selector:a})}}function X(...a){return function(t,e,n){let r=n.value;return typeof r=="function"&&Reflect.set(t,`__watch_${String(e)}`,r),n}}var _;(function(a){a[a.Any=0]="Any",a[a.Inbound=1]="Inbound",a[a.Outbound=2]="Outbound"})(_||(_={}));var g=class{callbacks=new Set;emit(t){for(let e of this.callbacks)try{let n=e(t);n instanceof Promise&&n.catch(r=>{console.error(r)})}catch(n){console.error(n)}}async emitAsync(t){for(let e of this.callbacks)try{await e(t)}catch(n){console.error(n)}}subscribe(t){return this.callbacks.add(t),{unsubscribe:()=>{this.callbacks.delete(t)}}}subscribeOnce(t){let e=async n=>{this.callbacks.delete(e),await t(n)};return this.callbacks.add(e),{unsubscribe:()=>{this.callbacks.delete(e)}}}};function T(a){let t=new WeakSet;return JSON.stringify(a,(e,n)=>{if(typeof n=="object"&&n!==null){if(t.has(n))return"[Circular]";t.add(n)}return n})}var p=class a{onChange=new g;onInboundChange=new g;onOutboundChange=new g;onMetadataChange=new g;value;committed=!0;_isChanging=!1;_parentProperty;_parentSubscription;_commitTriggerSub;_pendingCommitValue;_options;constructor(t){this.isOptionsObject(t)?(this._options=t,t.initialValue!==void 0&&(this.value=t.initialValue),t.commitTrigger!==void 0&&this.setCommitTrigger(t.commitTrigger)):this.value=t}prop(){return this._parentProperty}setValue(t,e=!1,n=!0){if(t instanceof a){let s=this._options?.linkHandler;s&&s(t),this.linkToParent(t);return}if(this._isChanging){let s=this._options?.propertyName;console.error((s?s+": ":"")+"Binding loop detected: setValue called while change is in progress");return}if(!(!(t!==this.value&&T(t)!==T(this.value))&&!(n&&!this.committed))){this._isChanging=!0;try{this.value=t,this.onChange.emit(t),n||(this.committed=!1),e?(this.committed=!0,this.onInboundChange.emit(t)):n&&(this.committed=!0,this.value!==void 0&&this.onOutboundChange.emit(this.value)),!e&&this._parentProperty&&this.pushToParent(t)}finally{this._isChanging=!1}}}linkToParent(t){this.clearParentLink();let e=t;for(;e._parentProperty;)e=e._parentProperty;this._parentProperty=e;let n=this._options?.direction??_.Any;this._parentSubscription=e.subscribe(n,s=>{this.setValueInternal(s,!0)});let r=e.getValue();r!==null&&this.setValueInternal(r,!0)}setValueInternal(t,e){if(!(this._isChanging||!(t!==this.value&&T(t)!==T(this.value)))){this._isChanging=!0;try{this.value=t,this.onChange.emit(t),e&&this.onInboundChange.emit(t)}finally{this._isChanging=!1}}}pushToParent(t){if(!this._parentProperty)return;let e=this._options?.commitTrigger;if(e){if(!!!e.getValue()){this._pendingCommitValue={value:t},this._parentProperty.setValue(t,!1,!1);return}this._pendingCommitValue=void 0}let n=this._parentSubscription;this._parentSubscription=void 0,n?.unsubscribe(),this._parentProperty.setValue(t);let r=this._options?.direction??_.Any;this._parentSubscription=this._parentProperty.subscribe(r,s=>{this.setValueInternal(s,!0)})}clearParentLink(){this._parentSubscription&&(this._parentSubscription.unsubscribe(),this._parentSubscription=void 0),this._parentProperty=void 0}setCommitTrigger(t){this._commitTriggerSub&&(this._commitTriggerSub.unsubscribe(),this._commitTriggerSub=void 0),this._options??={},this._options.commitTrigger=t,this._commitTriggerSub=t.onChange.subscribe(e=>{if(!e||!this._parentProperty||!this._pendingCommitValue)return;let n=this._pendingCommitValue.value;this._pendingCommitValue=void 0,this._parentProperty.setValue(n,!1,!0)})}reset(){this.clearParentLink()}triggerChange(t=_.Any){this.value!==void 0&&(this.onChange.emit(this.value),t==_.Outbound&&this.onOutboundChange.emit(this.value),t==_.Inbound&&this.onOutboundChange.emit(this.value))}subscribe(t,e){return t==_.Inbound?this.onInboundChange.subscribe(n=>{e(n)}):t==_.Outbound?this.onOutboundChange.subscribe(n=>{e(n)}):this.onChange.subscribe(n=>{e(n)})}getValue(){return this.value===void 0?null:this.value}isOptionsObject(t){return typeof t=="object"&&t!==null&&"initialValue"in t}};var m=class a extends HTMLElement{static __e=[];static __h=[];static __bindings={};static __expressionsReady=!1;static __pendingInitCallbacks=[];static __setExpressionTable(t,e){a.__e=t,a.__h=e,a.__expressionsReady=!0;let n=a.__pendingInitCallbacks.splice(0,a.__pendingInitCallbacks.length);for(let r of n)r()}static __areExpressionsReady(){return a.__expressionsReady}static __addPendingInit(t){a.__pendingInitCallbacks.push(t)}__parentScope;__loopContext={};__baseSubscriptions=[];__getScope(){return{host:this,locals:this.__loopContext,parent:this.__parentScope}}__subscribeToExpression(t,e,n,r){this.__subscribeToExpressionInScope(t,e,n,r)}__evaluateExpr(t,e){let n=this.__getCompiledExprFn(t);try{return n(this,e)}catch{return}}__applyPipesForController(t,e,n){let r=t;r instanceof p&&(r=r.getValue());for(let s of e){let i=this.__getPipeFn(s.name);if(!i){console.warn(`Pipe "${s.name}" not found`);continue}let o=s.argExprIds.map(c=>this.__getCompiledExprFn(c)(this,n));r=i(r,...o)}return r}__processBindingsOnElement(t,e,n){let r=t.getAttribute("data-lid");if(!r)return;let s=this.__getBindingsForLid(r);if(!(!s||s.length===0))for(let i of s)this.__applyBindingWithScope(t,i,e,n)}__getBindingsForLid(t){let e=this.constructor;if(typeof e=="function"){let n=Reflect.get(e,"__bindings");if(this.__isBindingsMap(n))return n[t]}}__isBindingsMap(t){return!(!t||typeof t!="object")}__applyBindingWithScope(t,e,n,r){switch(e.b){case"property":this.__applyPropertyBindingWithScope(t,e,n,r);break;case"event":this.__applyEventBindingWithScope(t,e,n);break;case"two-way":this.__applyTwoWayBindingWithScope(t,e,n,r);break;case"class":this.__applyClassBindingWithScope(t,e,n,r);break;case"style":this.__applyStyleBindingWithScope(t,e,n,r);break;case"ref":break}}__getCompiledExprFn(t){let e=a.__e[t];if(typeof e!="function")throw new Error(`Missing compiled expression function for exprId ${t}`);return e}__getCompiledHandlerFn(t){let e=a.__h[t];if(typeof e!="function")throw new Error(`Missing compiled handler function for handlerId ${t}`);return e}__applyPipes(t,e,n){return this.__applyPipesForController(t,e.map(r=>({name:r.n,argExprIds:r.a})),n)}__getPipeFn(t){}__subscribeToExpressionInScope(t,e,n,r){if(!t)return;let s=i=>{r?r.push(i):this.__baseSubscriptions.push(i)};for(let i of t)if(Array.isArray(i))this.__subscribeToPropertyChain(i,e,n,s);else{let o=this.__getReactivePropFromScope(i,e);o?s(o.onChange.subscribe(n)):!(i in e.locals)&&!(i in e.host)&&console.warn(`Binding dependency "${i}" not found on component ${e.host.constructor.name}`)}}__subscribeToPropertyChain(t,e,n,r){if(t.length===0)return;let[s,...i]=t,o=this.__getReactivePropFromScope(s,e);if(o)if(i.length===0)r(o.onChange.subscribe(n));else{let c=[],u=()=>{for(let f of c)f.unsubscribe();c=[];let l=o.getValue();l!=null&&this.__subscribeToNestedChain(l,i,n,f=>{c.push(f),r(f)}),n()};r(o.onChange.subscribe(u));let h=o.getValue();h!=null&&this.__subscribeToNestedChain(h,i,n,l=>{c.push(l),r(l)})}else if(s in e.locals){let c=e.locals[s];c!=null&&i.length>0&&this.__subscribeToNestedChain(c,i,n,r)}else s in e.host||console.warn(`Binding dependency "${s}" not found on component ${e.host.constructor.name}`)}__subscribeToNestedChain(t,e,n,r){if(e.length===0||t===null||t===void 0)return;let[s,...i]=e,o=Reflect.get(t,s);if(o instanceof p)if(i.length===0)r(o.onChange.subscribe(n));else{let c=[],u=()=>{for(let f of c)f.unsubscribe();c=[];let l=o.getValue();l!=null&&this.__subscribeToNestedChain(l,i,n,f=>{c.push(f),r(f)}),n()};r(o.onChange.subscribe(u));let h=o.getValue();h!=null&&this.__subscribeToNestedChain(h,i,n,l=>{c.push(l),r(l)})}else i.length>0&&o!==null&&o!==void 0&&this.__subscribeToNestedChain(o,i,n,r)}__getReactivePropFromScope(t,e){if(t in e.locals)return;let n=`__${t}`;if(n in e.host){let r=Reflect.get(e.host,n);if(r instanceof p)return r}if(e.parent)return this.__getReactivePropFromScope(t,e.parent)}__setChildProperty(t,e,n){let r=Reflect.get(t,e);r instanceof p?r.setValue(n,!0):t instanceof a?Reflect.set(t,e,n):e in t&&t instanceof HTMLElement?Reflect.set(t,e,this.__unwrap(n)):t.setAttribute(e,String(this.__unwrap(n)))}__unwrap(t){return t instanceof p?t.getValue():t}__applyPropertyBindingWithScope(t,e,n,r){let s=t.tagName.toLowerCase(),i=customElements.get(s)!==void 0,o=()=>{try{if(typeof e.e!="number")throw new Error(`Binding for ${e.n} is missing exprId`);let u=this.__getCompiledExprFn(e.e)(this,n.locals);e.p&&e.p.length>0&&(u=this.__applyPipes(u,e.p,n.locals)),this.__setChildProperty(t,e.n,u)}catch(c){console.error("Property binding error:",c)}};this.__subscribeToExpressionInScope(e.d,n,o,r),e.s&&this.__subscribeToExpressionInScope([e.s],n,o,r),i?t instanceof a?o():this.__whenDefined(s,()=>{t instanceof a?o():console.warn(`Element <${s}> is not a FluffBase instance after whenDefined - binding for "${e.n}" skipped`)}):o()}__applyEventBindingWithScope(t,e,n){let r=`__fluff_event_${e.n}`;if(Reflect.has(t,r))return;if(Reflect.set(t,r,!0),typeof e.h!="number")throw new Error(`Event binding for ${e.n} is missing handlerId`);let s=this.__getCompiledHandlerFn(e.h);t.hasAttribute("x-fluff-component")?this.__applyOutputBinding(t,e.n,s,n):t.addEventListener(e.n,i=>{try{s(this,n.locals,i)}catch(o){console.error("Event binding error:",o)}})}__applyOutputBinding(t,e,n,r){let s=()=>{let i=Reflect.get(t,e);if(i instanceof g){let o=i.subscribe(c=>{try{n(this,r.locals,c)}catch(u){console.error("Output binding error:",u)}});return this.__baseSubscriptions.push(o),!0}return!1};s()||this.__whenDefined(t.tagName.toLowerCase(),()=>{s()})}__whenDefined(t,e){customElements.whenDefined(t).then(e).catch(console.error)}__applyTwoWayBindingWithScope(t,e,n,r){if(this.__applyPropertyBindingWithScope(t,e,n,r),typeof e.t!="string"||e.t.length===0)throw new Error(`Two-way binding for ${e.n} is missing targetProp`);let s=this.__getReactivePropFromScope(e.t,n),i=Reflect.get(t,e.n);if(s&&i instanceof p){let o=i.onChange.subscribe(c=>{s.setValue(c,!0)});r?r.push(o):this.__baseSubscriptions.push(o)}}__applyClassBindingWithScope(t,e,n,r){let s=()=>{try{if(typeof e.e!="number")throw new Error(`Class binding for ${e.n} is missing exprId`);let i=this.__getCompiledExprFn(e.e);this.__unwrap(i(this,n.locals))?t.classList.add(e.n):t.classList.remove(e.n)}catch(i){console.error("Class binding error:",i)}};this.__subscribeToExpressionInScope(e.d,n,s,r),s()}__applyStyleBindingWithScope(t,e,n,r){let s=()=>{try{if(typeof e.e!="number")throw new Error(`Style binding for ${e.n} is missing exprId`);let i=this.__getCompiledExprFn(e.e),o=this.__unwrap(i(this,n.locals));this.__hasStyle(t)&&t.style.setProperty(e.n,String(o))}catch(i){console.error("Style binding error:",i)}};this.__subscribeToExpressionInScope(e.d,n,s,r),s()}__hasStyle(t){return"style"in t}};var L=new Map,Q=0;function A(a){let t=`scope_${Q++}`;return L.set(t,a),t}function F(a){return L.get(a)}var v=/^fluff:(if|for|switch|text|break):(\d+)$/,d=class{id;startMarker;endMarker;subscriptions=[];host;shadowRoot;parentScope;loopContext={};markerManager;constructor(t,e,n,r,s){this.id=t,this.startMarker=e,this.endMarker=n,this.host=r,this.shadowRoot=s}setParentScope(t){this.parentScope=t}setLoopContext(t){this.loopContext=t}setMarkerManager(t){this.markerManager=t}cleanup(){for(let t of this.subscriptions)t.unsubscribe();this.subscriptions.length=0}updateRenderContext(t){}evaluateExpr(t){let e=this.getScope(),n=this.collectLocalsFromScope(e);if(e.host.__evaluateExpr)return e.host.__evaluateExpr(t,n);let r=m.__e[t];if(typeof r=="function")try{return r(e.host,n)}catch{return}}getScope(){if(this.parentScope)return this.parentScope;let t=this.__getFluffElementHost();return t?t.__getScope():{host:this.host,locals:this.loopContext,parent:void 0}}collectLocalsFromScope(t){let e={};return t.parent&&Object.assign(e,this.collectLocalsFromScope(t.parent)),Object.assign(e,t.locals),e}subscribeAndRun(t,e){let n=this.getScope(),r=t.filter(s=>Array.isArray(s)?s.length>0&&!s[0].startsWith("["):!s.startsWith("["));n.host.__subscribeToExpression&&n.host.__subscribeToExpression(r,n,e,this.subscriptions),e()}createChildScope(t){return{host:this.host,locals:t,parent:this.parentScope}}clearContentBetweenMarkersWithCleanup(t){for(let r of t)r.unsubscribe();if(t.length=0,!this.endMarker)return;let e=this.startMarker.parentNode;if(!e)return;let n=this.startMarker.nextSibling;for(;n&&n!==this.endMarker;){let r=n.nextSibling;if(n instanceof Comment){let s=v.exec(n.data);if(s&&this.markerManager?.cleanupController){let i=parseInt(s[2],10);this.markerManager.cleanupController(i,n)}}n instanceof HTMLTemplateElement||e.removeChild(n),n=r}}insertBeforeEndMarker(t){if(!this.endMarker)return;let e=this.endMarker.parentNode;e&&e.insertBefore(t,this.endMarker)}refreshParentBindings(){let t=this.startMarker.parentNode;if(!(t instanceof HTMLElement)||!t.hasAttribute("data-lid"))return;let e=this.__getFluffElementHost();if(!e)return;let n=this.getScope();e.__processBindingsOnElementPublic(t,n)}__getFluffElementHost(){return this.host instanceof y?this.host:null}setScopeOnChildren(t,e,n,r,s){if(t instanceof Comment){let i=v.exec(t.data);if(i&&r){let[,o,c]=i,u=parseInt(c,10),h=`/fluff:${o}:${u}`,l=r.getController(u,t),f=l===void 0;if(!l&&r.ensureController){let O=null,x=t.nextSibling;for(;x;){if(x instanceof Comment&&x.data===h){O=x;break}x=x.nextSibling}l=r.ensureController(u,o,t,O)}l&&(l.setParentScope(e),l.setLoopContext(e.locals),l.updateRenderContext(n),f&&l.initialize())}}else if(t instanceof y)t.__loopContext=e.locals,t.__parentScope=e,this.processBindingsOnNode(t,e,s);else if(t instanceof HTMLElement&&customElements.get(t.tagName.toLowerCase())!==void 0){let i=A(e);t.setAttribute("data-fluff-scope-id",i),this.processBindingsOnNode(t,e,s)}else t instanceof HTMLElement&&t.hasAttribute("data-lid")&&this.processBindingsOnNode(t,e,s);for(let i of Array.from(t.childNodes))this.setScopeOnChildren(i,e,n,r,s)}cloneAndInsertTemplate(t,e,n,r){let s=t.content.cloneNode(!0);if(!(s instanceof DocumentFragment))throw new Error("Expected DocumentFragment from template clone");let i=Array.from(s.childNodes),o=this.createChildScope(e);this.insertBeforeEndMarker(s);for(let c of i)this.setScopeOnChildren(c,o,n,this.markerManager,r)}processBindingsOnNode(t,e,n){let r=this.__getFluffElementHost();r&&r.__processBindingsOnElementPublic(t,e,n)}};var w=class extends d{constructor(t,e,n,r,s,i){super(t,e,n,r,s)}initialize(){}updateRenderContext(t){t&&(t.shouldBreak=!0)}};var k=class extends d{config;itemTemplate=null;emptyTemplate=null;bindingsSubscriptions=[];constructor(t,e,n,r,s,i){super(t,e,n,r,s),this.config=i}initialize(){let e=`${this.host.tagName.toLowerCase()}-${this.id}`;this.itemTemplate=this.shadowRoot.querySelector(`template[data-fluff-tpl="${e}"]`),this.emptyTemplate=this.shadowRoot.querySelector(`template[data-fluff-empty="${e}"]`);let n=this.config.deps??[],r=()=>{this.clearContentBetweenMarkersWithCleanup(this.bindingsSubscriptions);let s=this.evaluateExpr(this.config.iterableExprId);if(!Array.isArray(s)||s.length===0){this.emptyTemplate&&this.cloneAndInsertTemplate(this.emptyTemplate,this.loopContext,void 0,this.bindingsSubscriptions);return}if(!this.itemTemplate)return;let i={shouldBreak:!1};for(let o=0;o<s.length&&!i.shouldBreak;o++){let c={...this.loopContext,[this.config.iterator]:s[o],$index:o};this.cloneAndInsertTemplate(this.itemTemplate,c,i,this.bindingsSubscriptions)}this.refreshParentBindings()};this.subscribeAndRun(n,r)}};var P=class extends d{config;templates=[];currentBranchIndex=-1;bindingsSubscriptions=[];constructor(t,e,n,r,s,i){super(t,e,n,r,s),this.config=i}initialize(){let e=`${this.host.tagName.toLowerCase()}-${this.id}-`;this.templates=Array.from(this.shadowRoot.querySelectorAll(`template[data-fluff-branch^="${e}"]`));let n=[];for(let s of this.config.branches)s.deps&&n.push(...s.deps);let r=()=>{let s=-1;for(let i=0;i<this.config.branches.length;i++){let o=this.config.branches[i];if(o.exprId===void 0){s=i;break}if(this.evaluateExpr(o.exprId)){s=i;break}}s!==this.currentBranchIndex&&(this.clearContentBetweenMarkersWithCleanup(this.bindingsSubscriptions),this.currentBranchIndex=s,s>=0&&s<this.templates.length&&this.cloneAndInsertTemplate(this.templates[s],this.loopContext,void 0,this.bindingsSubscriptions),this.refreshParentBindings())};this.subscribeAndRun(n,r)}};var b=class{static isIfConfig(t){return t.type==="if"}static isForConfig(t){return t.type==="for"}static isSwitchConfig(t){return t.type==="switch"}static isTextConfig(t){return t.type==="text"}static isBreakConfig(t){return t.type==="break"}};var E=class extends d{config;templates=[];bindingsSubscriptions=[];constructor(t,e,n,r,s,i){super(t,e,n,r,s),this.config=i}initialize(){let e=`${this.host.tagName.toLowerCase()}-${this.id}-`;this.templates=Array.from(this.shadowRoot.querySelectorAll(`template[data-fluff-case^="${e}"]`));let n=this.config.deps??[],r=()=>{this.clearContentBetweenMarkersWithCleanup(this.bindingsSubscriptions);let s=this.evaluateExpr(this.config.expressionExprId),i=!1,o=!1,c={shouldBreak:!1};for(let u=0;u<this.config.cases.length&&!c.shouldBreak;u++){let h=this.config.cases[u],l=this.templates[u];if(!l)continue;let f=h.isDefault||h.valueExprId!==void 0&&this.evaluateExpr(h.valueExprId)===s;(o||!i&&f)&&(i=!0,this.cloneAndInsertTemplate(l,this.loopContext,c,this.bindingsSubscriptions),o=h.fallthrough)}this.refreshParentBindings()};this.subscribeAndRun(n,r)}};var M=class extends d{config;textNode=null;constructor(t,e,n,r,s,i){super(t,e,n,r,s),this.config=i}initialize(){this.textNode=document.createTextNode(""),this.insertBeforeEndMarker(this.textNode);let t=this.config.deps??[],e=this.config.pipes??[],n=()=>{let r=this.evaluateExpr(this.config.exprId);if(this.host.__applyPipesForController){let s=this.getScope(),i=this.collectLocalsFromScope(s);r=this.host.__applyPipesForController(r,e,i)}this.textNode&&(this.textNode.textContent=this.formatValue(r))};this.subscribeAndRun(t,n)}formatValue(t){return t==null?"":typeof t=="object"?JSON.stringify(t):typeof t=="string"?t:typeof t=="number"||typeof t=="boolean"?String(t):""}};var S=class{controllers=new Map;configs=new Map;host;shadowRoot;constructor(t,e){this.host=t,this.shadowRoot=e}initializeFromConfig(t){this.configs.clear();for(let[n,r]of t)this.configs.set(n,r);for(let[n,r]of t){let{startMarker:s,endMarker:i}=this.findMarkers(n,r.type);if(!s)continue;let o=this.createController(n,s,i,r);o&&(o.setMarkerManager(this),this.registerController(n,s,o))}let e=[];for(let n of this.controllers.values())e.push(...n.values());for(let n of e)n.initialize()}ensureController(t,e,n,r){let s=this.getController(t,n);if(s)return s;let i=this.configs.get(t);if(i?.type!==e)return;let o=this.createController(t,n,r,i);if(o)return o.setMarkerManager(this),this.registerController(t,n,o),o}getController(t,e){return this.controllers.get(t)?.get(e)}cleanupController(t,e){let n=this.controllers.get(t);if(n){if(e){let r=n.get(e);if(!r)return;r.cleanup(),n.delete(e),n.size===0&&this.controllers.delete(t);return}for(let r of n.values())r.cleanup();this.controllers.delete(t)}}cleanup(){for(let t of this.controllers.values())for(let e of t.values())e.cleanup();this.controllers.clear()}registerController(t,e,n){let r=this.controllers.get(t);r||(r=new Map,this.controllers.set(t,r)),r.set(e,n)}findMarkers(t,e){let n=`fluff:${e}:${t}`,r=`/fluff:${e}:${t}`,s=null,i=null,o=document.createTreeWalker(this.shadowRoot,NodeFilter.SHOW_COMMENT,null),c=o.nextNode();for(;c;)c instanceof Comment&&(c.data===n?s=c:c.data===r&&(i=c)),c=o.nextNode();return{startMarker:s,endMarker:i}}createController(t,e,n,r){return b.isIfConfig(r)?new P(t,e,n,this.host,this.shadowRoot,r):b.isForConfig(r)?new k(t,e,n,this.host,this.shadowRoot,r):b.isSwitchConfig(r)?new E(t,e,n,this.host,this.shadowRoot,r):b.isTextConfig(r)?new M(t,e,n,this.host,this.shadowRoot,r):b.isBreakConfig(r)?new w(t,e,n,this.host,this.shadowRoot,r):null}};var y=class extends m{__pipes={};_shadowRoot;_subscriptions=[];_initialized=!1;_pendingInit=!1;_markerManager=null;_markerConfigEntries=null;_MarkerManagerClass=null;constructor(){super(),this._shadowRoot=this.attachShadow({mode:"open"})}connectedCallback(){if(!this._initialized){if(!m.__areExpressionsReady()){this._pendingInit||(this._pendingInit=!0,m.__addPendingInit(()=>{this._pendingInit=!1,this.connectedCallback()}));return}let t=this.getAttribute("data-fluff-scope-id");t&&!this.__parentScope&&(this.__parentScope=F(t),this.__parentScope&&(this.__loopContext=this.__parentScope.locals));let e=this.getAttribute("data-fluff-loop-context");if(e&&Object.keys(this.__loopContext).length===0)try{this.__loopContext=JSON.parse(e)}catch{}this.__applyPendingProps(),this.__render(),this.__setupBindings(),this.getAttribute("data-fluff-scope-id")&&this.__processBindings(),this._initialized=!0,"onInit"in this&&typeof this.onInit=="function"&&this.onInit()}}disconnectedCallback(){"onDestroy"in this&&typeof this.onDestroy=="function"&&this.onDestroy(),this._markerManager&&(this._markerManager.cleanup(),this._markerManager=null);for(let t of this._subscriptions)t.unsubscribe();this._subscriptions=[];for(let t of this.__baseSubscriptions)t.unsubscribe();this.__baseSubscriptions=[]}$watch=(t,e)=>(e(""),{unsubscribe:()=>{}});__processBindingsOnElementPublic(t,e,n){this.__processBindingsOnElement(t,e,n)}__setupBindings(){this.__initializeMarkers(S),this.__processBindings(),this.__initializeMarkersInternal()}__pipe(t,e,...n){let r=this.__pipes[t];return r?r(e,...n):(console.warn(`Pipe "${t}" not found`),e)}__getPipeFn(t){return this.__pipes[t]??R(t)}__getShadowRoot(){return this._shadowRoot}__createProp(t,e){let n=new p(e);return Object.defineProperty(this,t,{get(){return n.getValue()},set(r){n.setValue(r)},enumerable:!0,configurable:!0}),n}__defineClassHostBinding(t,e,n){Object.defineProperty(this,t,{get:()=>!!Reflect.get(this,n),set:r=>{Reflect.set(this,n,r),r?this.classList.add(e):this.classList.remove(e)},enumerable:!0,configurable:!0})}__setMarkerConfigs(t){this._markerConfigEntries=t}__initializeMarkers(t){this._MarkerManagerClass=t}__initializeMarkersInternal(){!this._markerConfigEntries||!this._MarkerManagerClass||(this._markerManager=new this._MarkerManagerClass(this,this._shadowRoot),this._markerManager.initializeFromConfig(this._markerConfigEntries))}__setChildProperty(t,e,n){if(t instanceof HTMLElement&&t.hasAttribute("x-fluff-component")){let r=t.tagName.toLowerCase();if(customElements.get(r)===void 0){this.__whenDefined(r,()=>{this.__setChildProperty(t,e,n)});return}}super.__setChildProperty(t,e,n)}__getReactivePropFromScope(t,e){let n=`__${t}`;if(n in e.host){let r=Reflect.get(e.host,n);if(r instanceof p)return r}if(e.parent)return this.__getReactivePropFromScope(t,e.parent)}__processBindings(){let t=this._shadowRoot.querySelectorAll("[data-lid]"),e=this.__getScope();for(let n of Array.from(t)){let r=n.closest("[x-fluff-component]");r&&r!==n||this.__processBindingsOnElement(n,e)}}__applyPendingProps(){let t=Reflect.get(this,"__pendingProps");if(this.isRecord(t)){for(let[e,n]of Object.entries(t)){console.log("apply-pending-prop",{propName:e,value:n,el:this.tagName});let r=`__${e}`;if(r in this){let s=Reflect.get(this,r);s instanceof p&&s.setValue(n,!0)}else e in this&&Reflect.set(this,e,n)}Reflect.deleteProperty(this,"__pendingProps")}}isRecord(t){return t!==null&&typeof t=="object"&&!Array.isArray(t)}};export{N as Component,_ as Direction,m as FluffBase,y as FluffElement,H as HostBinding,W as HostListener,D as Input,j as LinkedProperty,S as MarkerManager,q as Output,J as Pipe,p as Property,g as Publisher,G as Reactive,K as ViewChild,X as Watch,R as getPipeTransform,B as pipeRegistry};
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
interface PipeInstance {
|
|
2
|
-
transform: (value: unknown, ...args: unknown[]) => unknown;
|
|
3
|
-
}
|
|
4
|
-
interface PipeClass {
|
|
5
|
-
__pipeName?: string;
|
|
6
|
-
new (): PipeInstance;
|
|
7
|
-
}
|
|
8
1
|
type Constructor = new (...args: unknown[]) => object;
|
|
9
2
|
export interface ComponentConfig {
|
|
10
3
|
selector: string;
|
|
@@ -12,7 +5,6 @@ export interface ComponentConfig {
|
|
|
12
5
|
template?: string;
|
|
13
6
|
styleUrl?: string;
|
|
14
7
|
styles?: string;
|
|
15
|
-
pipes?: PipeClass[];
|
|
16
8
|
}
|
|
17
9
|
export interface ComponentMetadata extends ComponentConfig {
|
|
18
10
|
inputs: Map<string, string>;
|
package/decorators/Component.js
CHANGED
|
@@ -5,17 +5,6 @@ export function Component(config) {
|
|
|
5
5
|
...config, inputs: new Map(), outputs: new Map()
|
|
6
6
|
};
|
|
7
7
|
componentRegistry.set(target, metadata);
|
|
8
|
-
if (config.pipes && config.pipes.length > 0) {
|
|
9
|
-
const pipesObj = {};
|
|
10
|
-
for (const PipeClassItem of config.pipes) {
|
|
11
|
-
const pipeName = PipeClassItem.__pipeName;
|
|
12
|
-
if (pipeName) {
|
|
13
|
-
const instance = new PipeClassItem();
|
|
14
|
-
pipesObj[pipeName] = (value, ...args) => instance.transform(value, ...args);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
Reflect.set(target.prototype, '__pipes', pipesObj);
|
|
18
|
-
}
|
|
19
8
|
return target;
|
|
20
9
|
};
|
|
21
10
|
}
|
package/decorators/Input.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const Input: (bindingName?: string) => PropertyDecorator;
|
|
1
|
+
export declare const Input: (bindingName?: string | import("../index.js").ReactiveOptions) => PropertyDecorator;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import type { ReactiveOptions } from '../interfaces/ReactiveOptions.js';
|
|
2
|
+
export declare function createInputOutputDecorator(type: 'inputs' | 'outputs'): (bindingName?: string | ReactiveOptions) => PropertyDecorator;
|
|
@@ -10,7 +10,9 @@ export function createInputOutputDecorator(type) {
|
|
|
10
10
|
return;
|
|
11
11
|
const metadata = getComponentMetadata(constructor);
|
|
12
12
|
if (metadata) {
|
|
13
|
-
const name = bindingName
|
|
13
|
+
const name = typeof bindingName === 'string'
|
|
14
|
+
? bindingName
|
|
15
|
+
: bindingName?.alias ?? String(propertyKey);
|
|
14
16
|
metadata[type].set(String(propertyKey), name);
|
|
15
17
|
}
|
|
16
18
|
};
|
package/decorators/Output.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const Output: (bindingName?: string) => PropertyDecorator;
|
|
1
|
+
export declare const Output: (bindingName?: string | import("../index.js").ReactiveOptions) => PropertyDecorator;
|
package/decorators/Pipe.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ interface PipeInstance {
|
|
|
4
4
|
type PipeConstructor = (new () => PipeInstance) & {
|
|
5
5
|
__pipeName?: string;
|
|
6
6
|
};
|
|
7
|
+
export declare const pipeRegistry: Map<string, PipeConstructor>;
|
|
7
8
|
export declare function getPipeTransform(name: string): ((value: unknown, ...args: unknown[]) => unknown) | undefined;
|
|
8
9
|
export declare function Pipe(name: string): <T extends PipeConstructor>(target: T) => T;
|
|
9
10
|
export interface PipeTransform<TArgs extends unknown[] = unknown[], TReturn = unknown> {
|
package/decorators/Pipe.js
CHANGED
package/decorators/Reactive.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
import type { ReactiveOptions } from '../interfaces/ReactiveOptions.js';
|
|
2
|
+
export declare function Reactive(_options?: ReactiveOptions): PropertyDecorator;
|
package/decorators/Reactive.js
CHANGED
package/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { HostListener } from './decorators/HostListener.js';
|
|
|
5
5
|
export { Input } from './decorators/Input.js';
|
|
6
6
|
export { LinkedProperty } from './decorators/LinkedProperty.js';
|
|
7
7
|
export { Output } from './decorators/Output.js';
|
|
8
|
-
export { getPipeTransform, Pipe } from './decorators/Pipe.js';
|
|
8
|
+
export { getPipeTransform, Pipe, pipeRegistry } from './decorators/Pipe.js';
|
|
9
9
|
export type { PipeTransform } from './decorators/Pipe.js';
|
|
10
10
|
export { Reactive } from './decorators/Reactive.js';
|
|
11
11
|
export { ViewChild } from './decorators/ViewChild.js';
|
|
@@ -16,6 +16,7 @@ export { MarkerManager } from './runtime/FluffMarkers.js';
|
|
|
16
16
|
export { Property } from './utils/Property.js';
|
|
17
17
|
export { Publisher } from './utils/Publisher.js';
|
|
18
18
|
export { Direction } from './enums/Direction.js';
|
|
19
|
+
export type { ReactiveOptions } from './interfaces/ReactiveOptions.js';
|
|
19
20
|
export type { OnInit } from './interfaces/OnInit.js';
|
|
20
21
|
export type { OnDestroy } from './interfaces/OnDestroy.js';
|
|
21
22
|
export type { Subscription } from './interfaces/Subscription.js';
|
package/index.js
CHANGED
|
@@ -4,7 +4,7 @@ export { HostListener } from './decorators/HostListener.js';
|
|
|
4
4
|
export { Input } from './decorators/Input.js';
|
|
5
5
|
export { LinkedProperty } from './decorators/LinkedProperty.js';
|
|
6
6
|
export { Output } from './decorators/Output.js';
|
|
7
|
-
export { getPipeTransform, Pipe } from './decorators/Pipe.js';
|
|
7
|
+
export { getPipeTransform, Pipe, pipeRegistry } from './decorators/Pipe.js';
|
|
8
8
|
export { Reactive } from './decorators/Reactive.js';
|
|
9
9
|
export { ViewChild } from './decorators/ViewChild.js';
|
|
10
10
|
export { Watch } from './decorators/Watch.js';
|
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PropertyChain } from './PropertyChain.js';
|
|
2
|
+
import type { Subscription } from './Subscription.js';
|
|
3
|
+
export interface FluffHostScope {
|
|
4
|
+
host: FluffHostElement;
|
|
5
|
+
locals: Record<string, unknown>;
|
|
6
|
+
parent?: FluffHostScope;
|
|
7
|
+
}
|
|
2
8
|
export interface FluffHostElement extends HTMLElement {
|
|
3
|
-
|
|
4
|
-
|
|
9
|
+
__subscribeToExpression?: (deps: PropertyChain[], scope: FluffHostScope, callback: () => void, subscriptions: Subscription[]) => void;
|
|
10
|
+
__evaluateExpr?: (exprId: number, locals: Record<string, unknown>) => unknown;
|
|
11
|
+
__applyPipesForController?: (value: unknown, pipes: {
|
|
12
|
+
name: string;
|
|
13
|
+
argExprIds: number[];
|
|
14
|
+
}[], locals: Record<string, unknown>) => unknown;
|
|
5
15
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
package/runtime/FluffBase.d.ts
CHANGED
|
@@ -21,10 +21,21 @@ export declare abstract class FluffBase extends HTMLElement {
|
|
|
21
21
|
static __e: ExpressionFn[];
|
|
22
22
|
static __h: HandlerFn[];
|
|
23
23
|
static __bindings: Record<string, BindingInfo[]>;
|
|
24
|
+
private static __expressionsReady;
|
|
25
|
+
private static readonly __pendingInitCallbacks;
|
|
26
|
+
static __setExpressionTable(expressions: ExpressionFn[], handlers: HandlerFn[]): void;
|
|
27
|
+
static __areExpressionsReady(): boolean;
|
|
28
|
+
static __addPendingInit(callback: () => void): void;
|
|
24
29
|
__parentScope?: Scope;
|
|
25
30
|
__loopContext: Record<string, unknown>;
|
|
26
31
|
protected __baseSubscriptions: Subscription[];
|
|
27
32
|
__getScope(): Scope;
|
|
33
|
+
__subscribeToExpression(deps: PropertyChain[], scope: Scope, callback: () => void, subscriptions: Subscription[]): void;
|
|
34
|
+
__evaluateExpr(exprId: number, locals: Record<string, unknown>): unknown;
|
|
35
|
+
__applyPipesForController(value: unknown, pipes: {
|
|
36
|
+
name: string;
|
|
37
|
+
argExprIds: number[];
|
|
38
|
+
}[], locals: Record<string, unknown>): unknown;
|
|
28
39
|
protected __processBindingsOnElement(el: Element, scope: Scope, subscriptions?: Subscription[]): void;
|
|
29
40
|
private __getBindingsForLid;
|
|
30
41
|
private __isBindingsMap;
|
|
@@ -35,7 +46,6 @@ export declare abstract class FluffBase extends HTMLElement {
|
|
|
35
46
|
n: string;
|
|
36
47
|
a: number[];
|
|
37
48
|
}[], locals: Record<string, unknown>): unknown;
|
|
38
|
-
private __applyPipe;
|
|
39
49
|
protected __getPipeFn(_name: string): ((value: unknown, ...args: unknown[]) => unknown) | undefined;
|
|
40
50
|
protected __subscribeToExpressionInScope(deps: PropertyChain[] | undefined, scope: Scope, callback: () => void, subscriptions?: Subscription[]): void;
|
|
41
51
|
private __subscribeToPropertyChain;
|
package/runtime/FluffBase.js
CHANGED
|
@@ -4,6 +4,23 @@ export class FluffBase extends HTMLElement {
|
|
|
4
4
|
static __e = [];
|
|
5
5
|
static __h = [];
|
|
6
6
|
static __bindings = {};
|
|
7
|
+
static __expressionsReady = false;
|
|
8
|
+
static __pendingInitCallbacks = [];
|
|
9
|
+
static __setExpressionTable(expressions, handlers) {
|
|
10
|
+
FluffBase.__e = expressions;
|
|
11
|
+
FluffBase.__h = handlers;
|
|
12
|
+
FluffBase.__expressionsReady = true;
|
|
13
|
+
const pending = FluffBase.__pendingInitCallbacks.splice(0, FluffBase.__pendingInitCallbacks.length);
|
|
14
|
+
for (const callback of pending) {
|
|
15
|
+
callback();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
static __areExpressionsReady() {
|
|
19
|
+
return FluffBase.__expressionsReady;
|
|
20
|
+
}
|
|
21
|
+
static __addPendingInit(callback) {
|
|
22
|
+
FluffBase.__pendingInitCallbacks.push(callback);
|
|
23
|
+
}
|
|
7
24
|
__parentScope;
|
|
8
25
|
__loopContext = {};
|
|
9
26
|
__baseSubscriptions = [];
|
|
@@ -14,6 +31,34 @@ export class FluffBase extends HTMLElement {
|
|
|
14
31
|
parent: this.__parentScope
|
|
15
32
|
};
|
|
16
33
|
}
|
|
34
|
+
__subscribeToExpression(deps, scope, callback, subscriptions) {
|
|
35
|
+
this.__subscribeToExpressionInScope(deps, scope, callback, subscriptions);
|
|
36
|
+
}
|
|
37
|
+
__evaluateExpr(exprId, locals) {
|
|
38
|
+
const fn = this.__getCompiledExprFn(exprId);
|
|
39
|
+
try {
|
|
40
|
+
return fn(this, locals);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
__applyPipesForController(value, pipes, locals) {
|
|
47
|
+
let result = value;
|
|
48
|
+
if (result instanceof Property) {
|
|
49
|
+
result = result.getValue();
|
|
50
|
+
}
|
|
51
|
+
for (const pipe of pipes) {
|
|
52
|
+
const pipeFn = this.__getPipeFn(pipe.name);
|
|
53
|
+
if (!pipeFn) {
|
|
54
|
+
console.warn(`Pipe "${pipe.name}" not found`);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const args = pipe.argExprIds.map(id => this.__getCompiledExprFn(id)(this, locals));
|
|
58
|
+
result = pipeFn(result, ...args);
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
17
62
|
__processBindingsOnElement(el, scope, subscriptions) {
|
|
18
63
|
const lid = el.getAttribute('data-lid');
|
|
19
64
|
if (!lid)
|
|
@@ -74,23 +119,7 @@ export class FluffBase extends HTMLElement {
|
|
|
74
119
|
return fn;
|
|
75
120
|
}
|
|
76
121
|
__applyPipes(value, pipes, locals) {
|
|
77
|
-
|
|
78
|
-
if (result instanceof Property) {
|
|
79
|
-
result = result.getValue();
|
|
80
|
-
}
|
|
81
|
-
for (const pipe of pipes) {
|
|
82
|
-
result = this.__applyPipe(pipe.n, result, pipe.a, locals);
|
|
83
|
-
}
|
|
84
|
-
return result;
|
|
85
|
-
}
|
|
86
|
-
__applyPipe(name, value, argExprIds, locals) {
|
|
87
|
-
const pipeFn = this.__getPipeFn(name);
|
|
88
|
-
if (!pipeFn) {
|
|
89
|
-
console.warn(`Pipe "${name}" not found`);
|
|
90
|
-
return value;
|
|
91
|
-
}
|
|
92
|
-
const args = argExprIds.map(id => this.__getCompiledExprFn(id)(this, locals));
|
|
93
|
-
return pipeFn(value, ...args);
|
|
122
|
+
return this.__applyPipesForController(value, pipes.map(p => ({ name: p.n, argExprIds: p.a })), locals);
|
|
94
123
|
}
|
|
95
124
|
__getPipeFn(_name) {
|
|
96
125
|
return undefined;
|
|
@@ -285,6 +314,10 @@ export class FluffBase extends HTMLElement {
|
|
|
285
314
|
}
|
|
286
315
|
}
|
|
287
316
|
__applyEventBindingWithScope(el, binding, scope) {
|
|
317
|
+
const boundKey = `__fluff_event_${binding.n}`;
|
|
318
|
+
if (Reflect.has(el, boundKey))
|
|
319
|
+
return;
|
|
320
|
+
Reflect.set(el, boundKey, true);
|
|
288
321
|
if (typeof binding.h !== 'number') {
|
|
289
322
|
throw new Error(`Event binding for ${binding.n} is missing handlerId`);
|
|
290
323
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import type { Subscription } from '../interfaces/Subscription.js';
|
|
2
2
|
import { Property } from '../utils/Property.js';
|
|
3
|
-
import { Publisher } from '../utils/Publisher.js';
|
|
4
3
|
import { FluffBase } from './FluffBase.js';
|
|
5
|
-
import type { MarkerManagerInterface } from './MarkerManagerInterface.js';
|
|
4
|
+
import type { MarkerConfigEntries, MarkerManagerInterface } from './MarkerManagerInterface.js';
|
|
6
5
|
import { type Scope } from './ScopeRegistry.js';
|
|
7
6
|
export declare abstract class FluffElement extends FluffBase {
|
|
8
7
|
protected __pipes: Record<string, (value: unknown, ...args: unknown[]) => unknown>;
|
|
9
8
|
protected readonly _shadowRoot: ShadowRoot;
|
|
10
9
|
private _subscriptions;
|
|
11
10
|
private _initialized;
|
|
11
|
+
private _pendingInit;
|
|
12
12
|
private _markerManager;
|
|
13
|
-
private
|
|
13
|
+
private _markerConfigEntries;
|
|
14
14
|
private _MarkerManagerClass;
|
|
15
15
|
constructor();
|
|
16
16
|
connectedCallback(): void;
|
|
@@ -19,32 +19,20 @@ export declare abstract class FluffElement extends FluffBase {
|
|
|
19
19
|
__processBindingsOnElementPublic(el: Element, scope: Scope, subscriptions?: Subscription[]): void;
|
|
20
20
|
protected abstract __render(): void;
|
|
21
21
|
protected __setupBindings(): void;
|
|
22
|
-
protected __addSubscription(sub: Subscription): void;
|
|
23
22
|
protected __pipe(name: string, value: unknown, ...args: unknown[]): unknown;
|
|
24
23
|
protected __getPipeFn(name: string): ((value: unknown, ...args: unknown[]) => unknown) | undefined;
|
|
25
24
|
protected __getShadowRoot(): ShadowRoot;
|
|
26
|
-
protected
|
|
25
|
+
protected __createProp<T>(name: string, options: T | {
|
|
26
|
+
initialValue: T;
|
|
27
|
+
[key: string]: unknown;
|
|
28
|
+
}): Property<T>;
|
|
29
|
+
protected __defineClassHostBinding(name: string, className: string, privateName: string): void;
|
|
30
|
+
protected __setMarkerConfigs(entries: MarkerConfigEntries): void;
|
|
27
31
|
protected __initializeMarkers(MarkerManagerClass: new (host: FluffElement, shadowRoot: ShadowRoot) => MarkerManagerInterface): void;
|
|
28
32
|
private __initializeMarkersInternal;
|
|
29
|
-
protected __getElement(id: string): Element | null;
|
|
30
|
-
protected __setText(id: string, text: string): void;
|
|
31
|
-
protected __bindText(id: string, getter: () => string): void;
|
|
32
|
-
protected __setProperty(id: string, prop: string, value: unknown): void;
|
|
33
|
-
protected __addClass(id: string, className: string): void;
|
|
34
|
-
protected __removeClass(id: string, className: string): void;
|
|
35
|
-
protected __bindEvent(id: string, event: string, handler: (e: Event) => void): void;
|
|
36
|
-
protected __bindPropertyChange<T>(prop: Property<T>, callback: (val: T) => void): void;
|
|
37
|
-
protected __connectProperties<T>(source: Property<T>, target: Property<T>): void;
|
|
38
|
-
protected __connectOutput<T>(source: Publisher<T>, handler: (val: T) => void): void;
|
|
39
|
-
protected __bindOutput(id: string, outputName: string, handler: (val: Event) => void): void;
|
|
40
33
|
protected __setChildProperty(el: Element, propName: string, value: unknown): void;
|
|
41
|
-
protected __bindToChild(id: string, propName: string, value: unknown): void;
|
|
42
|
-
protected __setChildPropertyDeferred(el: Element, propName: string, value: unknown): void;
|
|
43
|
-
protected __bindOutputOnElement(el: Element, outputName: string, handler: (val: Event) => void): void;
|
|
44
34
|
protected __getReactivePropFromScope(propName: string, scope: Scope): Property<unknown> | undefined;
|
|
45
35
|
protected __processBindings(): void;
|
|
46
36
|
private __applyPendingProps;
|
|
47
|
-
private isHTMLElement;
|
|
48
37
|
private isRecord;
|
|
49
|
-
private __getReactiveProp;
|
|
50
38
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { getPipeTransform } from '../decorators/Pipe.js';
|
|
2
|
-
import { DomUtils } from '../utils/DomUtils.js';
|
|
3
2
|
import { Property } from '../utils/Property.js';
|
|
4
|
-
import { Publisher } from '../utils/Publisher.js';
|
|
5
3
|
import { FluffBase } from './FluffBase.js';
|
|
4
|
+
import { MarkerManager } from './MarkerManager.js';
|
|
6
5
|
import { getScope } from './ScopeRegistry.js';
|
|
7
6
|
export class FluffElement extends FluffBase {
|
|
8
7
|
__pipes = {};
|
|
9
8
|
_shadowRoot;
|
|
10
9
|
_subscriptions = [];
|
|
11
10
|
_initialized = false;
|
|
11
|
+
_pendingInit = false;
|
|
12
12
|
_markerManager = null;
|
|
13
|
-
|
|
13
|
+
_markerConfigEntries = null;
|
|
14
14
|
_MarkerManagerClass = null;
|
|
15
15
|
constructor() {
|
|
16
16
|
super();
|
|
@@ -18,6 +18,16 @@ export class FluffElement extends FluffBase {
|
|
|
18
18
|
}
|
|
19
19
|
connectedCallback() {
|
|
20
20
|
if (!this._initialized) {
|
|
21
|
+
if (!FluffBase.__areExpressionsReady()) {
|
|
22
|
+
if (!this._pendingInit) {
|
|
23
|
+
this._pendingInit = true;
|
|
24
|
+
FluffBase.__addPendingInit(() => {
|
|
25
|
+
this._pendingInit = false;
|
|
26
|
+
this.connectedCallback();
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
21
31
|
const scopeId = this.getAttribute('data-fluff-scope-id');
|
|
22
32
|
if (scopeId && !this.__parentScope) {
|
|
23
33
|
this.__parentScope = getScope(scopeId);
|
|
@@ -73,12 +83,10 @@ export class FluffElement extends FluffBase {
|
|
|
73
83
|
this.__processBindingsOnElement(el, scope, subscriptions);
|
|
74
84
|
}
|
|
75
85
|
__setupBindings() {
|
|
86
|
+
this.__initializeMarkers(MarkerManager);
|
|
76
87
|
this.__processBindings();
|
|
77
88
|
this.__initializeMarkersInternal();
|
|
78
89
|
}
|
|
79
|
-
__addSubscription(sub) {
|
|
80
|
-
this._subscriptions.push(sub);
|
|
81
|
-
}
|
|
82
90
|
__pipe(name, value, ...args) {
|
|
83
91
|
const pipe = this.__pipes[name];
|
|
84
92
|
if (!pipe) {
|
|
@@ -93,102 +101,47 @@ export class FluffElement extends FluffBase {
|
|
|
93
101
|
__getShadowRoot() {
|
|
94
102
|
return this._shadowRoot;
|
|
95
103
|
}
|
|
96
|
-
|
|
97
|
-
|
|
104
|
+
__createProp(name, options) {
|
|
105
|
+
const prop = new Property(options);
|
|
106
|
+
Object.defineProperty(this, name, {
|
|
107
|
+
get() {
|
|
108
|
+
return prop.getValue();
|
|
109
|
+
},
|
|
110
|
+
set(v) {
|
|
111
|
+
prop.setValue(v);
|
|
112
|
+
},
|
|
113
|
+
enumerable: true,
|
|
114
|
+
configurable: true
|
|
115
|
+
});
|
|
116
|
+
return prop;
|
|
117
|
+
}
|
|
118
|
+
__defineClassHostBinding(name, className, privateName) {
|
|
119
|
+
Object.defineProperty(this, name, {
|
|
120
|
+
get: () => Boolean(Reflect.get(this, privateName)),
|
|
121
|
+
set: (v) => {
|
|
122
|
+
Reflect.set(this, privateName, v);
|
|
123
|
+
if (v) {
|
|
124
|
+
this.classList.add(className);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
this.classList.remove(className);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
enumerable: true,
|
|
131
|
+
configurable: true
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
__setMarkerConfigs(entries) {
|
|
135
|
+
this._markerConfigEntries = entries;
|
|
98
136
|
}
|
|
99
137
|
__initializeMarkers(MarkerManagerClass) {
|
|
100
138
|
this._MarkerManagerClass = MarkerManagerClass;
|
|
101
139
|
}
|
|
102
140
|
__initializeMarkersInternal() {
|
|
103
|
-
if (!this.
|
|
141
|
+
if (!this._markerConfigEntries || !this._MarkerManagerClass)
|
|
104
142
|
return;
|
|
105
143
|
this._markerManager = new this._MarkerManagerClass(this, this._shadowRoot);
|
|
106
|
-
this._markerManager.initializeFromConfig(this.
|
|
107
|
-
}
|
|
108
|
-
__getElement(id) {
|
|
109
|
-
return this._shadowRoot.querySelector(`[data-lid="${id}"]`);
|
|
110
|
-
}
|
|
111
|
-
__setText(id, text) {
|
|
112
|
-
const el = this.__getElement(id);
|
|
113
|
-
if (el)
|
|
114
|
-
el.textContent = text;
|
|
115
|
-
}
|
|
116
|
-
__bindText(id, getter) {
|
|
117
|
-
const el = this.__getElement(id);
|
|
118
|
-
if (!el)
|
|
119
|
-
return;
|
|
120
|
-
const expr = el.getAttribute('data-text-bind') ?? '';
|
|
121
|
-
const propMatch = /this\.([a-zA-Z_][a-zA-Z0-9_]*)/.exec(expr);
|
|
122
|
-
const update = () => {
|
|
123
|
-
try {
|
|
124
|
-
el.textContent = getter();
|
|
125
|
-
}
|
|
126
|
-
catch {
|
|
127
|
-
el.textContent = '';
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
if (propMatch) {
|
|
131
|
-
const [, propName] = propMatch;
|
|
132
|
-
const reactiveProp = this.__getReactiveProp(propName);
|
|
133
|
-
if (reactiveProp) {
|
|
134
|
-
this.__bindPropertyChange(reactiveProp, update);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
update();
|
|
138
|
-
}
|
|
139
|
-
__setProperty(id, prop, value) {
|
|
140
|
-
const el = this.__getElement(id);
|
|
141
|
-
if (this.isHTMLElement(el)) {
|
|
142
|
-
if (prop in el) {
|
|
143
|
-
Reflect.set(el, prop, value);
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
el.setAttribute(prop, String(value));
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
__addClass(id, className) {
|
|
151
|
-
const el = this.__getElement(id);
|
|
152
|
-
if (this.isHTMLElement(el))
|
|
153
|
-
el.classList.add(className);
|
|
154
|
-
}
|
|
155
|
-
__removeClass(id, className) {
|
|
156
|
-
const el = this.__getElement(id);
|
|
157
|
-
if (this.isHTMLElement(el))
|
|
158
|
-
el.classList.remove(className);
|
|
159
|
-
}
|
|
160
|
-
__bindEvent(id, event, handler) {
|
|
161
|
-
const el = this.__getElement(id);
|
|
162
|
-
if (el) {
|
|
163
|
-
el.addEventListener(event, handler);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
__bindPropertyChange(prop, callback) {
|
|
167
|
-
const sub = prop.onChange.subscribe(callback);
|
|
168
|
-
this._subscriptions.push(sub);
|
|
169
|
-
const currentVal = prop.getValue();
|
|
170
|
-
if (currentVal !== null) {
|
|
171
|
-
callback(currentVal);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
__connectProperties(source, target) {
|
|
175
|
-
const sub = source.onChange.subscribe((val) => {
|
|
176
|
-
target.setValue(val, true);
|
|
177
|
-
});
|
|
178
|
-
this._subscriptions.push(sub);
|
|
179
|
-
const currentVal = source.getValue();
|
|
180
|
-
if (currentVal !== null) {
|
|
181
|
-
target.setValue(currentVal, true);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
__connectOutput(source, handler) {
|
|
185
|
-
const sub = source.subscribe(handler);
|
|
186
|
-
this._subscriptions.push(sub);
|
|
187
|
-
}
|
|
188
|
-
__bindOutput(id, outputName, handler) {
|
|
189
|
-
const el = this.__getElement(id);
|
|
190
|
-
if (el)
|
|
191
|
-
this.__bindOutputOnElement(el, outputName, handler);
|
|
144
|
+
this._markerManager.initializeFromConfig(this._markerConfigEntries);
|
|
192
145
|
}
|
|
193
146
|
__setChildProperty(el, propName, value) {
|
|
194
147
|
if (el instanceof HTMLElement && el.hasAttribute('x-fluff-component')) {
|
|
@@ -202,47 +155,6 @@ export class FluffElement extends FluffBase {
|
|
|
202
155
|
}
|
|
203
156
|
super.__setChildProperty(el, propName, value);
|
|
204
157
|
}
|
|
205
|
-
__bindToChild(id, propName, value) {
|
|
206
|
-
const el = this.__getElement(id);
|
|
207
|
-
if (!el)
|
|
208
|
-
return;
|
|
209
|
-
this.__setChildPropertyDeferred(el, propName, value);
|
|
210
|
-
}
|
|
211
|
-
__setChildPropertyDeferred(el, propName, value) {
|
|
212
|
-
if (Reflect.get(el, propName) !== undefined) {
|
|
213
|
-
this.__setChildProperty(el, propName, value);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
if (DomUtils.isCustomElement(el)) {
|
|
217
|
-
this.__whenDefined(el.tagName.toLowerCase(), () => {
|
|
218
|
-
this.__setChildProperty(el, propName, value);
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
else {
|
|
222
|
-
this.__setChildProperty(el, propName, value);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
__bindOutputOnElement(el, outputName, handler) {
|
|
226
|
-
const maybeOutput = Reflect.get(el, outputName);
|
|
227
|
-
if (maybeOutput instanceof Publisher) {
|
|
228
|
-
this.__connectOutput(maybeOutput, handler);
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
if (DomUtils.isCustomElement(el)) {
|
|
232
|
-
this.__whenDefined(el.tagName.toLowerCase(), () => {
|
|
233
|
-
const innerOutput = Reflect.get(el, outputName);
|
|
234
|
-
if (innerOutput instanceof Publisher) {
|
|
235
|
-
this.__connectOutput(innerOutput, handler);
|
|
236
|
-
}
|
|
237
|
-
else {
|
|
238
|
-
el.addEventListener(outputName, handler);
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
else {
|
|
243
|
-
el.addEventListener(outputName, handler);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
158
|
__getReactivePropFromScope(propName, scope) {
|
|
247
159
|
const key = `__${propName}`;
|
|
248
160
|
if (key in scope.host) {
|
|
@@ -286,20 +198,7 @@ export class FluffElement extends FluffBase {
|
|
|
286
198
|
}
|
|
287
199
|
Reflect.deleteProperty(this, '__pendingProps');
|
|
288
200
|
}
|
|
289
|
-
isHTMLElement(el) {
|
|
290
|
-
return el !== null;
|
|
291
|
-
}
|
|
292
201
|
isRecord(value) {
|
|
293
202
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
294
203
|
}
|
|
295
|
-
__getReactiveProp(propName) {
|
|
296
|
-
const key = `__${propName}`;
|
|
297
|
-
if (key in this) {
|
|
298
|
-
const candidate = Reflect.get(this, key);
|
|
299
|
-
if (candidate instanceof Property) {
|
|
300
|
-
return candidate;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return undefined;
|
|
304
|
-
}
|
|
305
204
|
}
|