@jinntec/fore
Version:
Fore - declarative user interfaces in plain HTML
136 lines (118 loc) • 3.88 kB
JavaScript
// 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);
}