UNPKG

@jinntec/fore

Version:

Fore - declarative user interfaces in plain HTML

136 lines (118 loc) 3.88 kB
// import { FxAction } from './fx-action.js'; import '../fx-model.js'; import { AbstractAction } from './abstract-action.js'; import { evaluateXPath } from '../xpath-evaluation.js'; import { Fore } from '../fore.js'; import getInScopeContext from '../getInScopeContext.js'; /** * `fx-setvalue` * * @customElement */ export default class FxSetvalue extends AbstractAction { static get properties() { return { ...super.properties, ref: { type: String, }, valueAttr: { type: String, }, }; } constructor() { super(); this.ref = ''; this.valueAttr = ''; } connectedCallback() { if (super.connectedCallback) { super.connectedCallback(); } if (this.hasAttribute('ref')) { this.ref = this.getAttribute('ref'); } else { throw new Error('fx-setvalue must specify a "ref" attribute'); } this.valueAttr = this.getAttribute('value'); } async perform() { super.perform(); let { value } = this; if (this.valueAttr !== null) { const inscopeContext = getInScopeContext(this, this.valueAttr); /* todo: review @martin - shouldn't we always return a string value? this comes down to the question if setvalue should only allow setting of strings which i tend to agree. Can't remember a case where i wanted to set an attribute or element (for json wouldn't make much sense either) - for cases like that fx-replace would be more appropriate. This is of practical relevance cause currently forces to append 'text()' to value expressions or getting unexpected results. */ [value] = evaluateXPath(this.valueAttr, inscopeContext, this, this.detail); } else if (this.textContent !== '') { value = this.textContent; } else { value = ''; } if (value?.nodeType && value.nodeType === Node.ATTRIBUTE_NODE) { value = value.nodeValue; } // Get the ModelItem without re-evaluating the context const mi = this.getModelItem(); this.setValue(mi, value); // todo: check this again - logically needsUpate should be set but makes tests fail // this.needsUpdate = true; } /** * need to overwrite default dispatchExecute to do it ourselves. This is necessary for tracking control value changes * which call setvalue directly without perform(). */ dispatchExecute() {} // Adjustment in setValue logic to ensure we work with JSONNode, not just raw values setValue(modelItem, newVal) { console.log('setValue', modelItem, newVal); const item = modelItem; if (!item) return; // Check if current node is a JSONNode const node = Array.isArray(item.node) ? item.node[0] : item.node; if (item.value !== newVal) { const path = Fore.getDomNodeIndexString(node); const ev = this.event; const targetElem = this; this.dispatchEvent( new CustomEvent('execute-action', { composed: true, bubbles: true, cancelable: true, detail: { action: targetElem, event: ev, value: newVal, path, }, }), ); // Use ModelItem's value setter which handles both DOM nodes and JSON lenses if (newVal?.nodeType) { if (newVal.nodeType === Node.ELEMENT_NODE) { item.value = newVal; } else if (newVal.nodeType === Node.ATTRIBUTE_NODE) { item.value = newVal.nodeValue; } else if (newVal.nodeType === Node.TEXT_NODE) { item.value = newVal.textContent; } } else { item.value = newVal; } this.getModel().changed.push(modelItem); this.needsUpdate = true; modelItem.notify(); } } } if (!customElements.get('fx-setvalue')) { window.customElements.define('fx-setvalue', FxSetvalue); }