UNPKG

@selenite/graph-editor

Version:

A graph editor for visual programming, based on rete and svelte.

480 lines (479 loc) 19 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var XmlNode_1; var _a; import { Node, path, registerConverter, registerNode } from '../Node.svelte'; import { XMLData } from './XMLData'; import { assignControl, Control } from '../../socket'; import 'regenerator-runtime/runtime'; import wu from 'wu'; import { formatXml } from '../../../utils'; import { camlelcaseize, ComplexType, getSharedString, singular, splitCamelCase, XmlSchema } from '@selenite/commons'; import { SvelteSet } from 'svelte/reactivity'; export class AddXmlAttributeControl extends Control { xmlNode; constructor(xmlNode) { super(); this.xmlNode = xmlNode; } } let XmlToString = class XmlToString extends Node { constructor(params = {}) { super({ label: '', ...params }); this.addInData('xml', { type: 'xmlElement:*' }); this.addOutData('value', { label: 'string', type: 'string' }); } data(inputs) { const xml = this.getData('xml', inputs); return { value: xml ? formatXml({ xml: xml.toXml() }) : '' }; } }; XmlToString = __decorate([ registerNode('xml.ToString') // @ts-expect-error TODO: fix this type error , registerConverter('xmlElement:*', 'string'), path('XML'), __metadata("design:paramtypes", [Object]) ], XmlToString); export { XmlToString }; let XmlNode = class XmlNode extends Node { static { XmlNode_1 = this; } addOptionalAttribute(name) { this.usedOptionalAttrs.add(name); this.state.usedOptionalAttrs = Array.from(this.usedOptionalAttrs); const attr = this.complex.attributes.get(name); if (attr === undefined) throw new Error(`Property ${name} not found`); this.addInAttribute(attr); this.processDataflow(); } addAllOptionalAttributes() { for (const name of this.optionalXmlAttributes) { this.addOptionalAttribute(name); } } removeOptionalAttribute(name) { this.usedOptionalAttrs.delete(name); this.state.usedOptionalAttrs = Array.from(this.usedOptionalAttrs); this.removeInput(name); this.processDataflow(); } removeAllOptionalAttributes() { for (const name of this.usedOptionalAttrs) { this.removeOptionalAttribute(name); } } static counts = {}; xmlTag; xmlInputs = {}; xmlProperties = new Set(); optionalXmlAttributes = new SvelteSet(); usedOptionalAttrs = new SvelteSet(); xmlVectorProperties = new SvelteSet(); hasName = $state(false); geosSchema = $derived(this.factory?.xmlSchemas.get('geos')); set name(n) { super.name = !this.hasName || (typeof n === 'string' && n.trim() !== '') ? n : camlelcaseize(this.xmlTag) + XmlNode_1.counts[this.xmlTag]++; // Run dataflow in timeout to avoid running at setup this.processDataflow(); } get name() { return super.name; } toJSON() { const json = super.toJSON(); const complex = json.params.xmlConfig.complex; return { ...json, params: { ...json.params, xmlConfig: { ...json.params.xmlConfig, complex: complex instanceof ComplexType ? complex.toJSON() : complex } } }; } childrenSockets = new Map(); complex; outLabel; typePaths = $state(); // isMesh = $derived( // this.typePaths && // this.typePaths !== 'infinite' && // this.typePaths.length > 0 && // this.typePaths.at(0)?.at(-1) === 'Mesh' // ); constructor(xmlNodeParams) { let { initialValues = {} } = xmlNodeParams; const xmlConfig = xmlNodeParams.xmlConfig; const schema = xmlNodeParams.schema; if (schema) { const typePath = schema.typePaths.get(xmlConfig.complex.name); if (typePath) { xmlConfig.typePaths = typePath; } } xmlNodeParams.params = { ...xmlNodeParams.params, xmlConfig, initialValues }; const { priorities: indices = {} } = xmlConfig; let complex = xmlConfig.complex; if (!complex) { throw new Error('Complex type is missing.'); } super({ label: complex.name, ...xmlNodeParams }); this.typePaths = xmlConfig.typePaths; if (!(complex instanceof ComplexType)) { complex = ComplexType.fromObject(complex); } this.complex = complex; if (this.state.usedOptionalAttrs) { this.usedOptionalAttrs = new SvelteSet(this.state.usedOptionalAttrs); } if (!this.state.usedOptionalAttrs) { this.state.usedOptionalAttrs = []; } this.xmlTag = complex.name; initialValues = initialValues ?? {}; // this.initialValues = {inputs: initialValues}; for (const attr of complex.attributes.values()) { const { name, required } = attr; if (name === 'name') { this.hasName = true; continue; } if (required) { this.addInAttribute({ ...attr, initialValues }); } // if (!(name in this.initialValues.inputs!)) { // this.initialValues.inputs![name] = attr.default; // } if (!required) this.optionalXmlAttributes.add(name); } if (this.optionalXmlAttributes.size > 0) { this.addControl('addXmlAttr', new AddXmlAttributeControl(this)); } for (const name of this.usedOptionalAttrs) { this.addOptionalAttribute(name); } // Add XML element inputs const requiredChildren = complex.requiredChildren; const isOnlyOptChildren = requiredChildren.length === 0; for (const child of requiredChildren) { const isArray = child.maxOccurs === undefined || child.maxOccurs > 1; const key = `child:${child.type}`; this.addXmlInData({ index: -2 - (xmlConfig.priorities?.[this.xmlTag]?.[child.type] ?? 0), description: 'Required child.', name: key, type: `xmlElement:${child.type}`, isArray }); this.childrenSockets.set(child.type, key); } const optChildTypes = complex.optionalChildTypes; if (optChildTypes.length > 0) { const sharedChildType = getSharedString(optChildTypes, { pluralize: true }); const key = `child:${sharedChildType.length > 0 ? sharedChildType : isOnlyOptChildren ? this.xmlTag : 'optional'}`; this.addXmlInData({ index: -1, name: key, description: 'Optional children of this node.', isArray: true, type: `xmlElement:${optChildTypes.join('|')}` }); for (const childType of optChildTypes) { this.childrenSockets.set(childType, key); } } // Compute label of XML output // Computations are a bit complex in order to provide a meaningful label if (!xmlConfig.outLabel && schema) { xmlConfig.outLabel = complex.name; const parents = schema.parentsMap.get(complex.name); if (parents && parents.length > 0) { const sharedParent = getSharedString(parents); if (parents.length > 1 && sharedParent.length > 0) { xmlConfig.outLabel = sharedParent; } else if (parents.length === 1) { const parent = parents[0]; const parentComplex = schema.complexTypes.get(parent); if (parentComplex && parentComplex.requiredChildren.length === 0) { xmlConfig.outLabel = singular(parent); } } else { const parentsChildren = wu(parents) .map((p) => schema.complexTypes.get(p)?.childTypes) .filter((s) => s !== undefined) .map(getSharedString) .filter((s) => s.length > 0) .toArray(); const sharedParentChildren = getSharedString(parentsChildren); if (sharedParentChildren.length > 0) { xmlConfig.outLabel = sharedParentChildren; } } } } this.outLabel = xmlConfig.outLabel ?? complex.name; // Add XML output this.addOutData('value', { showLabel: true, label: xmlConfig.outLabel, description: 'XML representation of this element.\n\nIt can be downloaded with a `Download` node, or connected to other elements.', type: `xmlElement:${this.xmlTag}` }); this.description = (xmlConfig.outLabel !== complex.name ? `${xmlConfig.outLabel}: ` : '') + `${complex.name}\n\n` + (complex.requiredChildren.length > 0 ? `Required children: \n${complex.requiredChildren.map((s) => `- ${s.type}`).join('\n')}\n\n` : '') + (complex.optionalChildTypes.length > 0 ? `Optional children:\n${complex.optionalChildTypes.map((s) => `- ${s}`).join('\n')}` : ''); if (this.hasName) { this.addOutData('name', { type: 'groupNameRef', description: 'Name of the element.\n\nIt can be used as a reference in other nodes.' }); XmlNode_1.counts[this.xmlTag] = XmlNode_1.counts[this.xmlTag] ? XmlNode_1.counts[this.xmlTag] + 1 : 1; if (!this.state.name) { if ('name' in initialValues) this.name = initialValues['name']; else { let name = this.xmlTag; this.name = camlelcaseize(name) + XmlNode_1.counts[name]; } } } if (complex.name === 'InternalMesh') { this.addOutData('cellBlockNames', { type: 'groupNameRef', datastructure: 'array', label: 'Cell Block Names', description: 'The cell blocks defined by this internal mesh.' }); // console.log(" INTERNAL MESH") } } async getXml() { const inputs = await this.fetchInputs(); const data = this.data(inputs).value; return data.toXml(); } setName(name) { this.name = name; } addInAttribute({ name, type, default: default_, required, doc, initialValues = {} }) { this.xmlProperties.add(name); let options = undefined; let controlType; const simpleType = this.geosSchema?.simpleTypeMap.get(type); const xmlTypePattern = /([^\W_]+)(?:_([^\W_]+))?/gm; const [, xmlType, xmlSubType] = xmlTypePattern.exec(type) || []; // console.log('xmlType', xmlType, xmlSubType); if (simpleType?.options) { console.log('simpleType', simpleType); options = simpleType.options; } else if ((name === 'target' && this.complex.name.includes('Event')) || (name === 'sources' && this.complex.name.includes('History'))) { type = 'xmlElement:*'; } else if (options) { controlType = 'select'; } else if (xmlType === 'integer' && doc && doc.startsWith('Set to 1 to')) { doc = doc.replace('Set to 1 to', 'Set to true to'); type = 'boolean'; controlType = 'checkbox'; } else if (assignControl(xmlType) !== undefined) { type = xmlType; controlType = assignControl(xmlType); } else if (xmlType.startsWith('real') || xmlType.startsWith('integer')) { type = xmlSubType && xmlSubType.endsWith('2d') ? 'vector' : 'number'; controlType = assignControl(type); } else if (xmlType.startsWith('R1Tensor')) { type = 'vector'; controlType = assignControl(type); if (default_) { const a = default_; default_ = { x: a[0], y: a[1], z: a[2] }; } } else if (xmlType === 'string') { type = 'string'; } else { type = xmlType; } const isArray = xmlSubType && xmlSubType.startsWith('array'); if (type === 'vector') this.xmlVectorProperties.add(name); this.addInData(name, { label: splitCamelCase(name).join(' '), description: doc?.replaceAll(':ref:', ''), initial: initialValues[name] ?? default_, isRequired: required, alwaysShowLabel: true, type: type, options, datastructure: isArray ? 'array' : 'scalar', control: { canChangeType: false } }); } // @ts-expect-error TODO: fix this type error data(inputs) { let children = []; for (const [key, { tag }] of Object.entries(this.xmlInputs)) { const data = this.getData(key, inputs); if (data) { // if (tag) { // const childChildren: Array<XMLData> = data instanceof Array ? data : [data]; // children.push( // new XMLData({ // tag, // children: childChildren, // properties: {} // }) // ); // } else { children = [...children, ...(data instanceof Array ? data : [data])]; // } } } const xmlData = new XMLData({ node: this, tag: this.xmlTag, children: children, name: this.hasName ? this.name : undefined, properties: this.getProperties(inputs) }); const res = { value: xmlData, name: this.name }; if (this.complex.name === 'InternalMesh') { res.cellBlockNames = xmlData.properties['cellBlockNames']; } return res; } getProperties(inputs) { const properties = {}; const isArray = (key) => (this.inputs[key]?.socket).datastructure === 'array'; function prepData(data, type) { if (typeof data === 'boolean' && type === 'integer') { return data ? 1 : 0; } return data; } for (const key of this.xmlProperties) { let data = this.getData(key, inputs); if (data === undefined) continue; if (isArray(key) && !(data instanceof Array)) { data = [data]; } if (isArray(key)) data = data.filter((v) => (typeof v === 'boolean' ? true : Boolean(v))); if (key === 'target' && this.complex.name.endsWith('Event')) { const target = data; if (typeof target === 'object') { const node = target.node; // Only named nodes are valid targets (or so I think) if (!node.hasName) continue; const paths = node.typePaths; if (Array.isArray(paths)) { const path = paths[0]; if (path.at(0) === 'Problem') path.splice(0, 1); data = '/' + path.join('/') + '/' + node.name; } } } else if (key === 'sources' && this.complex.name.endsWith('History')) { const sources = data; data = sources.map((source) => { if (typeof source === 'object') { const node = source.node; if (!node.hasName) return ''; const paths = node.typePaths; if (Array.isArray(paths)) { const path = paths[0]; if (path.at(0) === 'Problem') path.splice(0, 1); return '/' + path.join('/') + '/' + node.name; } } return ''; }); } // Ensure that integer properties are not booleans const type = this.complex.attributes.get(key)?.type; if (type === 'integer') { if (isArray(key)) { data = data.map((v) => prepData(v, type)); } else { data = prepData(data, type); } } if (this.xmlVectorProperties.has(key)) { if (isArray(key)) { properties[key] = data.map((value) => Object.values(value)); } else properties[key] = Object.values(data); } else { properties[key] = data; } } return properties; } addXmlInData({ name, tag, type = 'any', isArray = false, index, required, description }) { this.xmlInputs[name] = { tag: tag }; this.addInData(name, { label: name.startsWith('child:') ? name.split(':')[1] : name, type: type, description, datastructure: isArray ? 'array' : 'scalar', index, isRequired: required }); } }; XmlNode = XmlNode_1 = __decorate([ registerNode('xml.XML', 'abstract'), __metadata("design:paramtypes", [typeof (_a = typeof XmlNodeParams !== "undefined" && XmlNodeParams) === "function" ? _a : Object]) ], XmlNode); export { XmlNode };