From 7fbcb13effa4e5abaae55fa6ea092c6b576592d5 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Tue, 8 Oct 2024 09:35:19 +0200 Subject: [PATCH 1/4] GEP-97 rewrite jquery to js --- .../PythonControlledCapability.js | 800 +++++++++--------- geppetto.js/geppetto-client/src/geppetto.ejs | 2 +- geppetto.js/geppetto-core/src/model/Node.js | 249 +++--- 3 files changed, 528 insertions(+), 523 deletions(-) diff --git a/geppetto.js/geppetto-client/src/communication/geppettoJupyter/PythonControlledCapability.js b/geppetto.js/geppetto-client/src/communication/geppettoJupyter/PythonControlledCapability.js index cb5fddf59..267b64d5b 100644 --- a/geppetto.js/geppetto-client/src/communication/geppettoJupyter/PythonControlledCapability.js +++ b/geppetto.js/geppetto-client/src/communication/geppettoJupyter/PythonControlledCapability.js @@ -5,467 +5,465 @@ * @author Adrian Quintana (adrian@metacell.us) * @author Matteo Cantarelli (matteo@metacell.us) */ -define(function (require) { - - var $ = require('jquery'); - var React = require('react'); - var Utils = require('./GeppettoJupyterUtils'); - - module.exports = { - createPythonControlledComponent (WrappedComponent) { - if (typeof WrappedComponent !== "function") { - // Fixes components defined as objects (e.g. Material-ui components) - class Wrapper extends React.Component { - render () { - return ; - } - } - WrappedComponent = Wrapper; - } - - class PythonControlledComponent extends WrappedComponent { - constructor (props) { - super(props); - if (this.state == undefined) { - this.state = {}; - } - this.state.model = props.model; - this.state.componentType = getNameFromWrappedComponent(WrappedComponent); - this.id = (this.props.id == undefined) ? this.props.model : this.props.id; - - this._isMounted = false; - } - - setSyncValueWithPythonHandler (handler) { - this.syncValueWithPython = handler; - } - - connectToPython (componentType, model) { - Utils.execPythonMessage('jupyter_geppetto.ComponentSync(componentType="' + componentType + '",model="' + model + '",id="' + this.id + '").connect()'); - } +import React from 'react'; +import Utils from './GeppettoJupyterUtils'; + +export function createPythonControlledComponent(WrappedComponent) { + if (typeof WrappedComponent !== 'function') { + class Wrapper extends React.Component { + render() { + return ; + } + } + WrappedComponent = Wrapper; + } - disconnectFromPython () { - Utils.execPythonMessage('jupyter_geppetto.remove_component_sync(componentType="' + this.state.componentType + '",model="' + this.id + '")'); - } + class PythonControlledComponent extends WrappedComponent { + constructor(props) { + super(props); + if (this.state == undefined) { + this.state = {}; + } + this.state.model = props.model; + this.state.componentType = getNameFromWrappedComponent(WrappedComponent); + this.id = (this.props.id == undefined) ? this.props.model : this.props.id; - componentWillUnmount () { - this._isMounted = false; - this.disconnectFromPython(); - } + this._isMounted = false; + } - UNSAFE_componentWillReceiveProps (nextProps) { - this.disconnectFromPython(); - this.id = (nextProps.id == undefined) ? nextProps.model : nextProps.id; - - this.connectToPython(this.state.componentType, nextProps.model); - if (this.state.value != nextProps.value) { - this.setState({ value: (nextProps.value === undefined) ? '' : nextProps.value }); - } - } + setSyncValueWithPythonHandler(handler) { + this.syncValueWithPython = handler; + } - componentDidMount () { - this._isMounted = true; - if (this.props.model != undefined) { - this.connectToPython(this.state.componentType, this.props.model); - } - if (this.props.value != undefined) { - this.setState({ value: this.props.value }); - } - } + connectToPython(componentType, model) { + Utils.execPythonMessage('jupyter_geppetto.ComponentSync(componentType="' + componentType + '",model="' + model + '",id="' + this.id + '").connect()'); + } - } + disconnectFromPython() { + Utils.execPythonMessage('jupyter_geppetto.remove_component_sync(componentType="' + this.state.componentType + '",model="' + this.id + '")'); + } - return PythonControlledComponent; - }, + componentWillUnmount() { + this._isMounted = false; + this.disconnectFromPython(); + } - createPythonControlledControl (WrappedComponent) { + UNSAFE_componentWillReceiveProps(nextProps) { + this.disconnectFromPython(); + this.id = (nextProps.id == undefined) ? nextProps.model : nextProps.id; - var PythonControlledComponent = this.createPythonControlledComponent(WrappedComponent); - class PythonControlledControl extends PythonControlledComponent { + this.connectToPython(this.state.componentType, nextProps.model); + if (this.state.value != nextProps.value) { + this.setState({ value: (nextProps.value === undefined) ? '' : nextProps.value }); + } + } - constructor (props) { - super(props); - this.state = $.extend(this.state, { - value: '', - searchText: '', - checked: false - }); + componentDidMount() { + this._isMounted = true; + if (this.props.model != undefined) { + this.connectToPython(this.state.componentType, this.props.model); + } + if (this.props.value != undefined) { + this.setState({ value: this.props.value }); + } + } - // If a handleChange method is passed as a props it will overwrite the handleChange python controlled capability - this.handleChange = (this.props.handleChange == undefined) ? this.handleChange.bind(this) : this.props.handleChange.bind(this); - this.handleUpdateInput = this.handleUpdateInput.bind(this); - this.handleUpdateCheckbox = this.handleUpdateCheckbox.bind(this); - - } + } - componentDidMount () { - super.componentDidMount() - this.UNRELIABLE_SyncDefaultValueWithPython() - } + return PythonControlledComponent; +} - /* - * since we don't know when a component will be synched with python, - * we can't know when to check if this.state.value should be replaced - * with this.props.default - */ - UNRELIABLE_SyncDefaultValueWithPython (timeInterval = 100, attemps = 0) { - if (attemps < 3) { - setTimeout(() => { - if (this.props.default && this.state.value === '') { - if (this.syncValueWithPython) { - // this function is added by jupyter_geppetto after the component is synched with python - this.syncValueWithPython(this.props.default); - } else { - this.UNRELIABLE_SyncDefaultValueWithPython(timeInterval * 2, attemps + 1) - } - } - }, timeInterval) - } else { - console.warn(`Tried to sync default value for ${this.props.model} and failed after 3 attemps.`) +export function createPythonControlledControl(WrappedComponent) { + var PythonControlledComponent = this.createPythonControlledComponent(WrappedComponent); + class PythonControlledControl extends PythonControlledComponent { + + constructor(props) { + super(props); + this.state = { + ...this.state, + value: '', + searchText: '', + checked: false + }; + + // If a handleChange method is passed as a props it will overwrite the handleChange python controlled capability + this.handleChange = (this.props.handleChange === undefined) ? this.handleChange.bind(this) : this.props.handleChange.bind(this); + this.handleUpdateInput = this.handleUpdateInput.bind(this); + this.handleUpdateCheckbox = this.handleUpdateCheckbox.bind(this); + + } + + componentDidMount() { + super.componentDidMount() + this.UNRELIABLE_SyncDefaultValueWithPython() + } + + /* + * since we don't know when a component will be synched with python, + * we can't know when to check if this.state.value should be replaced + * with this.props.default + */ + UNRELIABLE_SyncDefaultValueWithPython(timeInterval = 100, attemps = 0) { + if (attemps < 3) { + setTimeout(() => { + if (this.props.default && this.state.value === '') { + if (this.syncValueWithPython) { + // this function is added by jupyter_geppetto after the component is synched with python + this.syncValueWithPython(this.props.default); + } else { + this.UNRELIABLE_SyncDefaultValueWithPython(timeInterval * 2, attemps + 1) + } } - } + }, timeInterval) + } else { + console.warn(`Tried to sync default value for ${this.props.model} and failed after 3 attemps.`) + } + } + + UNSAFE_componentWillReceiveProps(nextProps) { + this.disconnectFromPython(); + this.id = (nextProps.id == undefined) ? nextProps.model : nextProps.id; + this.connectToPython(this.state.componentType, nextProps.model); + if ((this.state.searchText != nextProps.searchText) && (nextProps.searchText != undefined)) { + this.setState({ searchText: nextProps.searchText }); + } + if ((this.state.checked != nextProps.checked) && (nextProps.checked != undefined)) { + this.setState({ checked: nextProps.checked }); + } + if ((this.state.value != nextProps.value) && (nextProps.value != undefined)) { + this.setState({ value: nextProps.value }); + } + if ((this.state.model != nextProps.model) && (nextProps.model != undefined)) { + this.setState({ model: nextProps.model }); + } + } - UNSAFE_componentWillReceiveProps (nextProps) { - this.disconnectFromPython(); - this.id = (nextProps.id == undefined) ? nextProps.model : nextProps.id; - this.connectToPython(this.state.componentType, nextProps.model); - if ((this.state.searchText != nextProps.searchText) && (nextProps.searchText != undefined)) { - this.setState({ searchText: nextProps.searchText }); + componentDidUpdate(prevProps, prevState) { + switch (getNameFromWrappedComponent(WrappedComponent)) { + case 'AutoComplete': + if (this.state.searchText !== prevState.searchText && this.props.onChange) { + this.props.onChange(this.state.searchText); } - if ((this.state.checked != nextProps.checked) && (nextProps.checked != undefined)) { - this.setState({ checked: nextProps.checked }); + break; + case 'Checkbox': + if (this.state.checked !== prevState.checked && this.props.onChange) { + this.props.onChange(null, this.state.checked); } - if ((this.state.value != nextProps.value) && (nextProps.value != undefined)) { - this.setState({ value: nextProps.value }); + break; + default: + if (this.state.value !== prevState.value && this.props.onChange) { + this.props.onChange(null, null, this.state.value); } - if ((this.state.model != nextProps.model) && (nextProps.model != undefined)) { - this.setState({ model: nextProps.model }); - } - } + break; + } + if (this.props.validate) { + this.props.validate(this.state.value) + .then(response => { + if (this.state.errorState !== response.errorMsg) { + this.setState({ errorState: response.errorMsg }); + } + }); + } + + if ( + /* + * If the component changes id without unmounting, + * then default values will never be synched with python + */ + this.props.model === prevProps.model + && this.state.value === '' + && this.props.default + ) { + this.UNRELIABLE_SyncDefaultValueWithPython(1000) + } + } - componentDidUpdate (prevProps, prevState) { - switch (getNameFromWrappedComponent(WrappedComponent)) { - case 'AutoComplete': - if (this.state.searchText !== prevState.searchText && this.props.onChange) { - this.props.onChange(this.state.searchText); + updatePythonValue(newValue) { + if (this.props.prePythonSyncProcessing !== undefined) { + newValue = this.props.prePythonSyncProcessing(newValue); + } + // whenever we invoke syncValueWithPython we will propagate the Javascript value of the model to Python + if (this.syncValueWithPython) { + // this.syncValueWithPython((event.target.type == 'number') ? parseFloat(this.state.value) : this.state.value, this.props.requirement); + switch (this.props.realType) { + case 'float': + if (!isNaN(newValue) && newValue !== '') { + newValue = parseFloat(newValue) } break; - case 'Checkbox': - if (this.state.checked !== prevState.checked && this.props.onChange) { - this.props.onChange(null, this.state.checked); + case 'dict': + if (typeof newValue === 'string') { + newValue = JSON.parse(newValue) } break; default: - if (this.state.value !== prevState.value && this.props.onChange) { - this.props.onChange(null, null, this.state.value); - } break; - } - if (this.props.validate) { - this.props.validate(this.state.value) - .then(response => { - if (this.state.errorState !== response.errorMsg) { - this.setState({ errorState: response.errorMsg }); - } - }); - } - - if ( - /* - * If the component changes id without unmounting, - * then default values will never be synched with python - */ - this.props.model === prevProps.model - && this.state.value === '' - && this.props.default - ) { - this.UNRELIABLE_SyncDefaultValueWithPython(1000) - } } - - updatePythonValue (newValue) { - if (this.props.prePythonSyncProcessing !== undefined) { - newValue = this.props.prePythonSyncProcessing(newValue); - } - // whenever we invoke syncValueWithPython we will propagate the Javascript value of the model to Python - if (this.syncValueWithPython) { - // this.syncValueWithPython((event.target.type == 'number') ? parseFloat(this.state.value) : this.state.value, this.props.requirement); - switch (this.props.realType) { - case 'float': - if (!isNaN(newValue) && newValue !== '') { - newValue = parseFloat(newValue) - } - break; - case 'dict': - if (typeof newValue === 'string') { - newValue = JSON.parse(newValue) - } - break; - default: - break; - } - // Don't sync if new value is emtpy string - if (newValue !== '') { - this.syncValueWithPython(newValue); - } - - if (this.props.callback) { - this.props.callback(newValue, this.oldValue); - } - this.oldValue = undefined - - } - this.setState({ value: newValue, searchText: newValue, checked: newValue }); - this.forceUpdate(); + // Don't sync if new value is emtpy string + if (newValue !== '') { + this.syncValueWithPython(newValue); } - triggerUpdate (updateMethod) { - // common strategy when triggering processing of a value change, delay it, every time there is a change we reset - if (this.updateTimer != undefined) { - clearTimeout(this.updateTimer); - } - this.updateTimer = setTimeout(updateMethod, 1000); + if (this.props.callback) { + this.props.callback(newValue, this.oldValue); } - // Default handle (mainly textfields and dropdowns) - handleChange (event, index, value) { - var targetValue = value; - if (event != null && event.target.value != undefined) { - targetValue = event.target.value; - } - if (this.oldValue === undefined) { - this.oldValue = this.state.value - } - - this.setState({ value: targetValue }); - - if (this.props.validate) { - this.props.validate(targetValue) - .then(response => { - if (response.errorMsg !== this.state.errorMsg) { - this.setState({ errorMsg: response.errorMsg }); - } - }); - } + this.oldValue = undefined - // For textfields value is retrieved from the event. For dropdown value is retrieved from the value - this.triggerUpdate(() => this.updatePythonValue(targetValue)); - } + } + this.setState({ value: newValue, searchText: newValue, checked: newValue }); + this.forceUpdate(); + } + + triggerUpdate(updateMethod) { + // common strategy when triggering processing of a value change, delay it, every time there is a change we reset + if (this.updateTimer != undefined) { + clearTimeout(this.updateTimer); + } + this.updateTimer = setTimeout(updateMethod, 1000); + } + // Default handle (mainly textfields and dropdowns) + handleChange(event, index, value) { + var targetValue = value; + if (event != null && event.target.value != undefined) { + targetValue = event.target.value; + } + if (this.oldValue === undefined) { + this.oldValue = this.state.value + } - // Autocomplete handle - handleUpdateInput (value) { - this.triggerUpdate(() => this.updatePythonValue(value)); - } + this.setState({ value: targetValue }); - // Checkbox - handleUpdateCheckbox (event, isInputChecked) { - this.updatePythonValue(isInputChecked); - } + if (this.props.validate) { + this.props.validate(targetValue) + .then(response => { + if (response.errorMsg !== this.state.errorMsg) { + this.setState({ errorMsg: response.errorMsg }); + } + }); + } - render () { - const wrappedComponentProps = Object.assign({}, this.props); - if (wrappedComponentProps.key == undefined) { - wrappedComponentProps.key = wrappedComponentProps.model; - } - if (wrappedComponentProps.id == undefined) { - wrappedComponentProps.id = wrappedComponentProps.model; - } - delete wrappedComponentProps.model; - delete wrappedComponentProps.handleChange; - - delete wrappedComponentProps.modelName; - delete wrappedComponentProps.dimensionType; - delete wrappedComponentProps.noStyle; - delete wrappedComponentProps.validate; - delete wrappedComponentProps.prePythonSyncProcessing; - delete wrappedComponentProps.callback; - - if (wrappedComponentProps.realType == 'func' || wrappedComponentProps.realType == 'float') { - wrappedComponentProps['helperText'] = this.state.errorMsg; - } - if (!getNameFromWrappedComponent(WrappedComponent).includes('ListComponent')) { - delete wrappedComponentProps.realType; - } + // For textfields value is retrieved from the event. For dropdown value is retrieved from the value + this.triggerUpdate(() => this.updatePythonValue(targetValue)); + } - switch (getNameFromWrappedComponent(WrappedComponent)) { - case 'AutoComplete': - wrappedComponentProps['onUpdateInput'] = this.handleUpdateInput; - wrappedComponentProps['searchText'] = this.state.searchText; - break; - case 'Checkbox': - wrappedComponentProps['onChange'] = this.handleUpdateCheckbox; - wrappedComponentProps['checked'] = this.state.checked; - delete wrappedComponentProps.searchText; - delete wrappedComponentProps.dataSource; - delete wrappedComponentProps.floatingLabelText; - delete wrappedComponentProps.hintText; - break; - default: - wrappedComponentProps['onChange'] = this.handleChange; - wrappedComponentProps.value = (typeof this.state.value === 'object' && this.state.value !== null && !Array.isArray(this.state.value)) ? JSON.stringify(this.state.value) : this.state.value; - // Fix case with multiple values: need to set an empty list in case the value is undefined - wrappedComponentProps.value = (wrappedComponentProps.multiple - && wrappedComponentProps.value !== undefined - && !wrappedComponentProps.value) ? [] : wrappedComponentProps.value; - delete wrappedComponentProps.searchText; - delete wrappedComponentProps.dataSource; - break; - } + // Autocomplete handle + handleUpdateInput(value) { + this.triggerUpdate(() => this.updatePythonValue(value)); + } - return ( - - ); - } + // Checkbox + handleUpdateCheckbox(event, isInputChecked) { + this.updatePythonValue(isInputChecked); + } + render() { + const wrappedComponentProps = Object.assign({}, this.props); + if (wrappedComponentProps.key == undefined) { + wrappedComponentProps.key = wrappedComponentProps.model; + } + if (wrappedComponentProps.id == undefined) { + wrappedComponentProps.id = wrappedComponentProps.model; + } + delete wrappedComponentProps.model; + delete wrappedComponentProps.handleChange; + + delete wrappedComponentProps.modelName; + delete wrappedComponentProps.dimensionType; + delete wrappedComponentProps.noStyle; + delete wrappedComponentProps.validate; + delete wrappedComponentProps.prePythonSyncProcessing; + delete wrappedComponentProps.callback; + + if (wrappedComponentProps.realType == 'func' || wrappedComponentProps.realType == 'float') { + wrappedComponentProps['helperText'] = this.state.errorMsg; + } + if (!getNameFromWrappedComponent(WrappedComponent).includes('ListComponent')) { + delete wrappedComponentProps.realType; } - return PythonControlledControl; - }, + switch (getNameFromWrappedComponent(WrappedComponent)) { + case 'AutoComplete': + wrappedComponentProps['onUpdateInput'] = this.handleUpdateInput; + wrappedComponentProps['searchText'] = this.state.searchText; + break; + case 'Checkbox': + wrappedComponentProps['onChange'] = this.handleUpdateCheckbox; + wrappedComponentProps['checked'] = this.state.checked; + delete wrappedComponentProps.searchText; + delete wrappedComponentProps.dataSource; + delete wrappedComponentProps.floatingLabelText; + delete wrappedComponentProps.hintText; + break; + default: + wrappedComponentProps['onChange'] = this.handleChange; + wrappedComponentProps.value = (typeof this.state.value === 'object' && this.state.value !== null && !Array.isArray(this.state.value)) ? JSON.stringify(this.state.value) : this.state.value; + // Fix case with multiple values: need to set an empty list in case the value is undefined + wrappedComponentProps.value = (wrappedComponentProps.multiple + && wrappedComponentProps.value !== undefined + && !wrappedComponentProps.value) ? [] : wrappedComponentProps.value; + delete wrappedComponentProps.searchText; + delete wrappedComponentProps.dataSource; + break; + } - createPythonControlledControlWithPythonDataFetch (WrappedComponent) { + return ( + + ); + } - var PythonControlledComponent = this.createPythonControlledComponent(WrappedComponent); - class PythonControlledControlWithPythonDataFetch extends PythonControlledComponent { + } - constructor (props) { - super(props); - this.state = { - ...this.state, - value: [], - pythonData: [] - } - // If a handleChange method is passed as a props it will overwrite the handleChange python controlled capability - this.handleChange = (this.props.handleChange == undefined) ? this.handleChange.bind(this) : this.props.handleChange.bind(this); - this.callPythonMethod(); - } + return PythonControlledControl; +} - UNSAFE_componentWillReceiveProps (nextProps) { - this.disconnectFromPython(); - this.id = (nextProps.id == undefined) ? nextProps.model : nextProps.id; - - this.connectToPython(this.state.componentType, nextProps.model); - this.callPythonMethod(); - } +export function createPythonControlledControlWithPythonDataFetch(WrappedComponent) { + var PythonControlledComponent = this.createPythonControlledComponent(WrappedComponent); + class PythonControlledControlWithPythonDataFetch extends PythonControlledComponent { - /* - * TODO: this function appears defined 2 times - * I think the last def is picked up, so I am commenting this one - * componentDidUpdate (prevProps, prevState) { - * if (this.state.value != prevState.value && this.props.onChange) { - * this.props.onChange(null, null, this.state.value); - * } - * } - */ + constructor(props) { + super(props); + this.state = { + ...this.state, + value: [], + pythonData: [] + } + // If a handleChange method is passed as a props it will overwrite the handleChange python controlled capability + this.handleChange = (this.props.handleChange == undefined) ? this.handleChange.bind(this) : this.props.handleChange.bind(this); + this.callPythonMethod(); + } + + UNSAFE_componentWillReceiveProps(nextProps) { + this.disconnectFromPython(); + this.id = (nextProps.id == undefined) ? nextProps.model : nextProps.id; + + this.connectToPython(this.state.componentType, nextProps.model); + this.callPythonMethod(); + } + + /* + * TODO: this function appears defined 2 times + * I think the last def is picked up, so I am commenting this one + * componentDidUpdate (prevProps, prevState) { + * if (this.state.value != prevState.value && this.props.onChange) { + * this.props.onChange(null, null, this.state.value); + * } + * } + */ + + updatePythonValue(newValue) { + this.setState({ value: newValue, searchText: newValue, checked: newValue }); + if (this.syncValueWithPython) { + this.syncValueWithPython(newValue); + } - updatePythonValue (newValue) { - this.setState({ value: newValue, searchText: newValue, checked: newValue }); - if (this.syncValueWithPython) { - this.syncValueWithPython(newValue); - } + this.forceUpdate(); + } - this.forceUpdate(); - } + // Default handle (mainly textfields and dropdowns) + handleChange(event, index, value) { + var targetValue = value; + if (event != null && event.target.value != undefined) { + targetValue = event.target.value; + } + this.setState({ value: targetValue }); + this.updatePythonValue(targetValue); + } - // Default handle (mainly textfields and dropdowns) - handleChange (event, index, value) { - var targetValue = value; - if (event != null && event.target.value != undefined) { - targetValue = event.target.value; - } - this.setState({ value: targetValue }); - this.updatePythonValue(targetValue); - } + compareArrays(array1, array2) { + // if the other array is a falsy value, return + if (!array1 || !array2) { + return false; + } - compareArrays (array1, array2) { - // if the other array is a falsy value, return - if (!array1 || !array2) { - return false; - } + // compare lengths - can save a lot of time + if (array1.length != array2.length) { + return false; + } - // compare lengths - can save a lot of time - if (array1.length != array2.length) { + for (var i = 0, l = array1.length; i < l; i++) { + // Check if we have nested arrays + if (array1[i] instanceof Array && array2[i] instanceof Array) { + // recurse into the nested arrays + if (!array1[i].equals(array2[i])) { return false; } - - for (var i = 0, l = array1.length; i < l; i++) { - // Check if we have nested arrays - if (array1[i] instanceof Array && array2[i] instanceof Array) { - // recurse into the nested arrays - if (!array1[i].equals(array2[i])) { - return false; - } - } else if (array1[i] != array2[i]) { - // Warning - two different object instances will never be equal: {x:20} != {x:20} - return false; - } - } - return true; + } else if (array1[i] != array2[i]) { + // Warning - two different object instances will never be equal: {x:20} != {x:20} + return false; } - - callPythonMethod = value => { - Utils.evalPythonMessage(this.props.method, []).then(response => { - if (this._isMounted) { - if (Object.keys(response).length != 0) { - this.setState({ pythonData: response }); - } else { - this.setState({ pythonData: [] }); - } - } - }); + } + return true; + } + + callPythonMethod = value => { + Utils.evalPythonMessage(this.props.method, []).then(response => { + if (this._isMounted) { + if (Object.keys(response).length != 0) { + this.setState({ pythonData: response }); + } else { + this.setState({ pythonData: [] }); + } } - - componentDidUpdate (prevProps, prevState) { - if (!this.compareArrays(this.state.value, prevState.value)) { - if ($.isArray(this.state.value)) { - for (var v in this.state.value) { - if (this.state.pythonData.indexOf(this.state.value[v]) < 0) { - var newValue = [this.state.value[v]]; - this.setState({ pythonData: this.state.pythonData.concat(newValue) }); - } - } + }); + } + + componentDidUpdate(prevProps, prevState) { + if (!this.compareArrays(this.state.value, prevState.value)) { + if (Array.isArray(this.state.value)) { + for (var v in this.state.value) { + if (this.state.pythonData.indexOf(this.state.value[v]) < 0) { + var newValue = [this.state.value[v]]; + this.setState({ pythonData: this.state.pythonData.concat(newValue) }); } } } + } + } - shouldComponentUpdate (nextProps, nextState) { - return !this.compareArrays(this.state.pythonData, nextState.pythonData) || !this.compareArrays(this.state.value, nextState.value); - } - - render () { - const wrappedComponentProps = Object.assign({}, this.props); - if (wrappedComponentProps.key == undefined) { - wrappedComponentProps.key = wrappedComponentProps.model; - } - if (wrappedComponentProps.id == undefined) { - wrappedComponentProps.id = wrappedComponentProps.model; - } - wrappedComponentProps.onChange = this.handleChange; - wrappedComponentProps.value = wrappedComponentProps.multiple && this.state.value !== undefined && !this.state.value ? [] : this.state.value; - delete wrappedComponentProps.model; - delete wrappedComponentProps.postProcessItems; - delete wrappedComponentProps.validate; - delete wrappedComponentProps.prePythonSyncProcessing; - delete wrappedComponentProps.updates; - - if (this.props.postProcessItems) { - var items = this.props.postProcessItems(this.state.pythonData, wrappedComponentProps.value); - } - - return ( - - {items} - - ); - } + shouldComponentUpdate(nextProps, nextState) { + return !this.compareArrays(this.state.pythonData, nextState.pythonData) || !this.compareArrays(this.state.value, nextState.value); + } + render() { + const wrappedComponentProps = Object.assign({}, this.props); + if (wrappedComponentProps.key == undefined) { + wrappedComponentProps.key = wrappedComponentProps.model; + } + if (wrappedComponentProps.id == undefined) { + wrappedComponentProps.id = wrappedComponentProps.model; } + wrappedComponentProps.onChange = this.handleChange; + wrappedComponentProps.value = wrappedComponentProps.multiple && this.state.value !== undefined && !this.state.value ? [] : this.state.value; + delete wrappedComponentProps.model; + delete wrappedComponentProps.postProcessItems; + delete wrappedComponentProps.validate; + delete wrappedComponentProps.prePythonSyncProcessing; + delete wrappedComponentProps.updates; + + if (this.props.postProcessItems) { + var items = this.props.postProcessItems(this.state.pythonData, wrappedComponentProps.value); + } + + return ( + + {items} + + ); + } - return PythonControlledControlWithPythonDataFetch; - }, } -}) -function getNameFromWrappedComponent (WrappedComponent) { - return WrappedComponent.name || WrappedComponent.displayName || WrappedComponent.Naked.render.name; + + return PythonControlledControlWithPythonDataFetch; } +export default { + createPythonControlledComponent, + createPythonControlledControl, + createPythonControlledControlWithPythonDataFetch +}; + +function getNameFromWrappedComponent(WrappedComponent) { + return WrappedComponent.name || WrappedComponent.displayName || WrappedComponent.Naked.render.name; +} \ No newline at end of file diff --git a/geppetto.js/geppetto-client/src/geppetto.ejs b/geppetto.js/geppetto-client/src/geppetto.ejs index ddbcf450d..2ce0c88b5 100644 --- a/geppetto.js/geppetto-client/src/geppetto.ejs +++ b/geppetto.js/geppetto-client/src/geppetto.ejs @@ -40,7 +40,7 @@ ({ - Model: Backbone.Model.extend({ - name: "", - instancePath: "", - id: "", - domainType: "", - _metaType: "", - aspectNode: null, - parent: null, - tags: null, - - /** - * Gets the instance path of the node - * - * @command Node.getInstancePath() - * @returns {String} Instance path of this node - * - */ - getInstancePath: function () { - return this.instancePath; - }, - - /** - * Gets the name of the node - * - * @command Node.getName() - * @returns {String} Name of the node - * - */ - getName: function () { - return this.name; - }, - - getAspectNode: function () { - return this.aspectNode; - }, - - /** - * Sets the name of the node - * - * @command Node.setName() - * - */ - setName: function (newname) { - this.name = newname; - }, - - /** - * Get the id associated with node - * - * @command Node.getId() - * @returns {String} ID of node - */ - getId: function () { - return this.id; - }, - - getDomainType: function () { - return this.domainType; - }, - - setDomainType: function (newDomainType) { - this.domainType = newDomainType; - }, - - setParent: function (parent) { - this.parent = parent; - }, - - getParent: function () { - return this.parent; - }, - - _all: function (predicate, matches) { - if (typeof matches === 'undefined') { - var matches = []; - } - if (predicate(this)) { - matches.push(this); - } +import _ from 'underscore'; +import Backbone from 'backbone'; + +const Model = Backbone.Model.extend({ + name: "", + instancePath: "", + id: "", + domainType: "", + _metaType: "", + aspectNode: null, + parent: null, + tags: null, + + /** + * Gets the instance path of the node + * + * @command Node.getInstancePath() + * @returns {String} Instance path of this node + * + */ + + getInstancePath () { + return this.instancePath; + }, + + /** + * Gets the name of the node + * + * @command Node.getName() + * @returns {String} Name of the node + * + */ + getName () { + return this.name; + }, + + getAspectNode () { + return this.aspectNode; + }, + + /** + * Sets the name of the node + * + * @command Node.setName() + * + */ + + setName (newname) { + this.name = newname; + }, + + /** + * Get the id associated with node + * + * @command Node.getId() + * @returns {String} ID of node + */ + + getId () { + return this.id; + }, + + getDomainType () { + return this.domainType; + }, + + setDomainType (newDomainType) { + this.domainType = newDomainType; + }, + + setParent (parent) { + this.parent = parent; + }, + + getParent () { + return this.parent; + }, + + _all (predicate, matches) { + if (typeof matches === 'undefined') { + var matches = []; + } - if (typeof this.getChildren === "function") { - var children = this.getChildren(); - for (var ci in children) { - this._all.call(children[ci], predicate, matches); - } - } + if (predicate(this)) { + matches.push(this); + } - return matches; - }, - - /** - * Search inside a node for all the nodes of a specific domain type. - * - * @param {String} - * domainType - Domain type - * @returns {Array} List of Nodes - * - */ - getSubNodesOfDomainType: function (domainType) { - return this._all(function (n) { - return n.domainType === domainType; - }); - }, - - /** - * Search inside a node for all the nodes of a specific meta type. - * - * @param {String} - * metaType - Meta Type - * @returns {Array} List of Nodes - * - */ - getSubNodesOfMetaType: function (metaType) { - return this._all(function (n) { - return n._metaType === metaType; - }); + if (typeof this.getChildren === "function") { + var children = this.getChildren(); + for (var ci in children) { + this._all.call(children[ci], predicate, matches); + } } - }) -})); + + return matches; + }, + + /** + * Search inside a node for all the nodes of a specific domain type. + * + * @param {String} + * domainType - Domain type + * @returns {Array} List of Nodes + * + */ + + getSubNodesOfDomainType (domainType) { + return this._all(function (n) { + return n.domainType === domainType; + }); + }, + + /** + * Search inside a node for all the nodes of a specific meta type. + * + * @param {String} + * metaType - Meta Type + * @returns {Array} List of Nodes + * + */ + + getSubNodesOfMetaType (metaType) { + return this._all(function (n) { + return n._metaType === metaType; + }); + } +}) + +export default { + Model, +}; From 7fde261bc4a66f9973b3b4c24884be3bb2d08389 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Tue, 8 Oct 2024 09:39:37 +0200 Subject: [PATCH 2/4] GEP-97 remove use of underscore --- geppetto.js/geppetto-core/src/model/Node.js | 71 ++++++++------------- 1 file changed, 27 insertions(+), 44 deletions(-) diff --git a/geppetto.js/geppetto-core/src/model/Node.js b/geppetto.js/geppetto-core/src/model/Node.js index 0ce65143c..a84884724 100644 --- a/geppetto.js/geppetto-core/src/model/Node.js +++ b/geppetto.js/geppetto-core/src/model/Node.js @@ -5,7 +5,6 @@ * @author Jesus R. Martinez (jesus@metacell.us) */ -import _ from 'underscore'; import Backbone from 'backbone'; const Model = Backbone.Model.extend({ @@ -23,10 +22,8 @@ const Model = Backbone.Model.extend({ * * @command Node.getInstancePath() * @returns {String} Instance path of this node - * */ - - getInstancePath () { + getInstancePath() { return this.instancePath; }, @@ -35,13 +32,12 @@ const Model = Backbone.Model.extend({ * * @command Node.getName() * @returns {String} Name of the node - * */ - getName () { + getName() { return this.name; }, - getAspectNode () { + getAspectNode() { return this.aspectNode; }, @@ -49,10 +45,8 @@ const Model = Backbone.Model.extend({ * Sets the name of the node * * @command Node.setName() - * */ - - setName (newname) { + setName(newname) { this.name = newname; }, @@ -62,41 +56,40 @@ const Model = Backbone.Model.extend({ * @command Node.getId() * @returns {String} ID of node */ - - getId () { + getId() { return this.id; }, - getDomainType () { + getDomainType() { return this.domainType; }, - setDomainType (newDomainType) { + setDomainType(newDomainType) { this.domainType = newDomainType; }, - setParent (parent) { + setParent(parent) { this.parent = parent; }, - getParent () { + getParent() { return this.parent; }, - _all (predicate, matches) { - if (typeof matches === 'undefined') { - var matches = []; - } - + /** + * Recursively collects all nodes matching the given predicate. + * @param {Function} predicate - Function to test each node. + * @param {Array} matches - Array to collect matched nodes. + * @returns {Array} List of matched nodes. + */ + _all(predicate, matches = []) { if (predicate(this)) { matches.push(this); } if (typeof this.getChildren === "function") { - var children = this.getChildren(); - for (var ci in children) { - this._all.call(children[ci], predicate, matches); - } + const children = this.getChildren(); + children.forEach((child) => child._all(predicate, matches)); } return matches; @@ -105,34 +98,24 @@ const Model = Backbone.Model.extend({ /** * Search inside a node for all the nodes of a specific domain type. * - * @param {String} - * domainType - Domain type + * @param {String} domainType - Domain type * @returns {Array} List of Nodes - * */ - - getSubNodesOfDomainType (domainType) { - return this._all(function (n) { - return n.domainType === domainType; - }); + getSubNodesOfDomainType(domainType) { + return this._all((n) => n.domainType === domainType); }, /** * Search inside a node for all the nodes of a specific meta type. * - * @param {String} - * metaType - Meta Type + * @param {String} metaType - Meta Type * @returns {Array} List of Nodes - * */ - - getSubNodesOfMetaType (metaType) { - return this._all(function (n) { - return n._metaType === metaType; - }); - } -}) + getSubNodesOfMetaType(metaType) { + return this._all((n) => n._metaType === metaType); + }, +}); export default { Model, -}; +}; \ No newline at end of file From 8ae40d6c5f376b34b077af82a7aece4f72c8f60e Mon Sep 17 00:00:00 2001 From: aranega Date: Wed, 9 Oct 2024 06:31:31 -0600 Subject: [PATCH 3/4] GEP-97 Fix loop semantic logic --- geppetto.js/geppetto-core/src/model/Node.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/geppetto.js/geppetto-core/src/model/Node.js b/geppetto.js/geppetto-core/src/model/Node.js index a84884724..5cb98a7a9 100644 --- a/geppetto.js/geppetto-core/src/model/Node.js +++ b/geppetto.js/geppetto-core/src/model/Node.js @@ -88,8 +88,9 @@ const Model = Backbone.Model.extend({ } if (typeof this.getChildren === "function") { - const children = this.getChildren(); - children.forEach((child) => child._all(predicate, matches)); + for (const child of Object.values(this.getChildren())) { + this._all.call(child, predicate, matches) + } } return matches; From de13b463b2d8b2c8539f222e321bb73f2d5faa69 Mon Sep 17 00:00:00 2001 From: Aiga115 Date: Thu, 10 Oct 2024 18:39:40 +0200 Subject: [PATCH 4/4] GEP-97 delete use of backbone --- geppetto-showcase/.eslintrc.js | 1 - geppetto.js/geppetto-client/eslint.config.mjs | 1 - .../PythonControlledCapability.js | 12 ++--- geppetto.js/geppetto-core/eslint.config.mjs | 1 - geppetto.js/geppetto-core/src/model/Node.js | 54 +++++++++---------- geppetto.js/geppetto-ui/eslint.config.mjs | 1 - 6 files changed, 30 insertions(+), 40 deletions(-) diff --git a/geppetto-showcase/.eslintrc.js b/geppetto-showcase/.eslintrc.js index 7177cdb43..1af9acd52 100644 --- a/geppetto-showcase/.eslintrc.js +++ b/geppetto-showcase/.eslintrc.js @@ -97,7 +97,6 @@ module.exports = { "stackViewerRequest": true, "_": true, "labelsInTV": true, - "Backbone": true, "registeredEvents": true, "Handlebars": true, "ActiveXObject": true, diff --git a/geppetto.js/geppetto-client/eslint.config.mjs b/geppetto.js/geppetto-client/eslint.config.mjs index 0f5898f7f..fd0079083 100644 --- a/geppetto.js/geppetto-client/eslint.config.mjs +++ b/geppetto.js/geppetto-client/eslint.config.mjs @@ -64,7 +64,6 @@ const jsConfig = { stackViewerRequest: true, _: true, labelsInTV: true, - Backbone: true, registeredEvents: true, Handlebars: true, ActiveXObject: true, diff --git a/geppetto.js/geppetto-client/src/communication/geppettoJupyter/PythonControlledCapability.js b/geppetto.js/geppetto-client/src/communication/geppettoJupyter/PythonControlledCapability.js index 267b64d5b..b85769817 100644 --- a/geppetto.js/geppetto-client/src/communication/geppettoJupyter/PythonControlledCapability.js +++ b/geppetto.js/geppetto-client/src/communication/geppettoJupyter/PythonControlledCapability.js @@ -8,7 +8,7 @@ import React from 'react'; import Utils from './GeppettoJupyterUtils'; -export function createPythonControlledComponent(WrappedComponent) { +export default function createPythonControlledComponent(WrappedComponent) { if (typeof WrappedComponent !== 'function') { class Wrapper extends React.Component { render() { @@ -73,7 +73,7 @@ export function createPythonControlledComponent(WrappedComponent) { return PythonControlledComponent; } -export function createPythonControlledControl(WrappedComponent) { +export default function createPythonControlledControl(WrappedComponent) { var PythonControlledComponent = this.createPythonControlledComponent(WrappedComponent); class PythonControlledControl extends PythonControlledComponent { @@ -316,7 +316,7 @@ export function createPythonControlledControl(WrappedComponent) { return PythonControlledControl; } -export function createPythonControlledControlWithPythonDataFetch(WrappedComponent) { +export default function createPythonControlledControlWithPythonDataFetch(WrappedComponent) { var PythonControlledComponent = this.createPythonControlledComponent(WrappedComponent); class PythonControlledControlWithPythonDataFetch extends PythonControlledComponent { @@ -458,12 +458,6 @@ export function createPythonControlledControlWithPythonDataFetch(WrappedComponen return PythonControlledControlWithPythonDataFetch; } -export default { - createPythonControlledComponent, - createPythonControlledControl, - createPythonControlledControlWithPythonDataFetch -}; - function getNameFromWrappedComponent(WrappedComponent) { return WrappedComponent.name || WrappedComponent.displayName || WrappedComponent.Naked.render.name; } \ No newline at end of file diff --git a/geppetto.js/geppetto-core/eslint.config.mjs b/geppetto.js/geppetto-core/eslint.config.mjs index 0f5898f7f..fd0079083 100644 --- a/geppetto.js/geppetto-core/eslint.config.mjs +++ b/geppetto.js/geppetto-core/eslint.config.mjs @@ -64,7 +64,6 @@ const jsConfig = { stackViewerRequest: true, _: true, labelsInTV: true, - Backbone: true, registeredEvents: true, Handlebars: true, ActiveXObject: true, diff --git a/geppetto.js/geppetto-core/src/model/Node.js b/geppetto.js/geppetto-core/src/model/Node.js index 5cb98a7a9..e2f496e89 100644 --- a/geppetto.js/geppetto-core/src/model/Node.js +++ b/geppetto.js/geppetto-core/src/model/Node.js @@ -5,17 +5,19 @@ * @author Jesus R. Martinez (jesus@metacell.us) */ -import Backbone from 'backbone'; - -const Model = Backbone.Model.extend({ - name: "", - instancePath: "", - id: "", - domainType: "", - _metaType: "", - aspectNode: null, - parent: null, - tags: null, + +class Model { + + constructor() { + this.name = ""; + this.instancePath = ""; + this.id = ""; + this.domainType = ""; + this._metaType = ""; + this.aspectNode = null; + this.parent = null; + this.tags = null; + } /** * Gets the instance path of the node @@ -25,7 +27,7 @@ const Model = Backbone.Model.extend({ */ getInstancePath() { return this.instancePath; - }, + } /** * Gets the name of the node @@ -35,11 +37,11 @@ const Model = Backbone.Model.extend({ */ getName() { return this.name; - }, + } getAspectNode() { return this.aspectNode; - }, + } /** * Sets the name of the node @@ -48,7 +50,7 @@ const Model = Backbone.Model.extend({ */ setName(newname) { this.name = newname; - }, + } /** * Get the id associated with node @@ -58,23 +60,23 @@ const Model = Backbone.Model.extend({ */ getId() { return this.id; - }, + } getDomainType() { return this.domainType; - }, + } setDomainType(newDomainType) { this.domainType = newDomainType; - }, + } setParent(parent) { this.parent = parent; - }, + } getParent() { return this.parent; - }, + } /** * Recursively collects all nodes matching the given predicate. @@ -94,7 +96,7 @@ const Model = Backbone.Model.extend({ } return matches; - }, + } /** * Search inside a node for all the nodes of a specific domain type. @@ -104,7 +106,7 @@ const Model = Backbone.Model.extend({ */ getSubNodesOfDomainType(domainType) { return this._all((n) => n.domainType === domainType); - }, + } /** * Search inside a node for all the nodes of a specific meta type. @@ -114,9 +116,7 @@ const Model = Backbone.Model.extend({ */ getSubNodesOfMetaType(metaType) { return this._all((n) => n._metaType === metaType); - }, -}); + } +}; -export default { - Model, -}; \ No newline at end of file +export default Model; \ No newline at end of file diff --git a/geppetto.js/geppetto-ui/eslint.config.mjs b/geppetto.js/geppetto-ui/eslint.config.mjs index 0f5898f7f..fd0079083 100644 --- a/geppetto.js/geppetto-ui/eslint.config.mjs +++ b/geppetto.js/geppetto-ui/eslint.config.mjs @@ -64,7 +64,6 @@ const jsConfig = { stackViewerRequest: true, _: true, labelsInTV: true, - Backbone: true, registeredEvents: true, Handlebars: true, ActiveXObject: true,