aggregator-fn
Version:
Evaluate expressions in inline script tag.
134 lines (117 loc) • 3.76 kB
text/typescript
import {XE} from 'xtal-element/src/XE.js';
import {getArgsFromString} from 'xtal-element/lib/getDestructArgs.js';
import {destruct} from 'xtal-element/lib/destruct.js';
import {AgFnProps, AgFnActions} from './types';
/**
* @tag 'ag-fn'
* @element 'ag-fn'
*/
export class AgFnCore extends HTMLElement implements AgFnProps, AgFnActions{
static _count = 0;
getScript(self: this){
const script = this.querySelector('script');
if(script === null){
setTimeout(() => {
this.getScript(this);
}, 10);
return;
}
this.script = script;
}
attachScript({script}: this){
const args = getArgsFromString(script!.innerHTML);
args.forEach(arg =>{
if(arg !== 'self'){
destruct(this, arg);
}
});
const ig = 'fn = ';
const inner = script!.innerHTML.trim().replace(ig, '');
const count = AgFnCore._count++;
const fn = `
var af = customElements.get('${tagName}');
af['fn_' + ${count}] = ${inner}
`
const headScript = document.createElement('script');
headScript.type = 'module';
headScript.innerHTML = fn;
document.head!.appendChild(headScript);
this.attachAggregator(this, count);
}
attachAggregator(self: this, count: number){
const agfn = customElements.get('ag-fn') as any;
const aggregator = agfn['fn_' + count];
if(aggregator === undefined){
setTimeout(() => {
this.attachAggregator(this, count);
}, 10);
return;
}
this.aggregator = aggregator;
}
updateValue({_input, disabled, hostSelector, aggregator}: this){
if(_input === undefined || disabled) return;
_input.self = this;
let host;
if(hostSelector!== undefined){
host = this.closest(hostSelector);
}else{
host = (<any>this.getRootNode()).host;
}
if(host) _input.host = host;
this.value = aggregator!(_input);
}
/**
* @private
* @prop {function} [aggregator] - The evaluated aggregator function
*/
declare aggregator: ((input: any) => any) | undefined;
declare disabled: boolean | undefined;
declare _input: any;
declare value: any;
declare script: HTMLScriptElement | undefined;
declare isC: boolean;
declare debug: boolean;
/**
* @prop {string} [hostSelector] - Specify the closest containing element matching this css selector as the host
* @attr {string} [host-selector] Specify the closest containing element matching this css selector as the host
*/
declare hostSelector: string;
}
const tagName = 'ag-fn';
const ce = new XE<AgFnProps, AgFnActions>({
config:{
tagName,
propDefaults:{
isC: true,
debug: false,
},
propInfo:{
value:{
notify:{
dispatch:true,
}
}
},
actions:{
getScript:{
ifAllOf:['isC']
},
attachScript:{
ifAllOf:['script']
},
updateValue: {
ifKeyIn: ['_input'],
ifAllOf: ['aggregator', 'isC'],
ifNoneOf: ['disabled']
}
}
},
superclass: AgFnCore
});
export const AgFn = ce.classDef!;
declare global {
interface HTMLElementTagNameMap {
'ag-fn': AgFnCore;
}
}