ESM Scripts - Nested Classes?

Hi all

Is there anyone with experience using ESM scripts that can explain why this script isn’t working as expected?

  1. the visibleif tags aren’t working, everything is always visible in editor regardless.
  2. when I click away from the script in the editor all the changes I have made to the attributes are erased

I have a suspicion that it is related to nesting classes, but I’m not certain.

Here is my script and the only error I get in the console (broswer dev console, not playcanvas)

import { Script, Asset, Color, Curve } from 'playcanvas';

import { ButtonStates } from './uiController.mjs'

/** @enum {number} */

export const ButtonStylesEnum = {

DEFAULT: 0,

CHECKBOX: 1,

MENU: 2

};




/** @enum {string} */

export const ElementTypes = {

IMAGE: "image",

TEXT: "text"

};





/** @interface */

class ButtonSpriteElement {

/**

     * @attribute

     * @type {Asset} 

     * @resource sprite

     */

    sprite;




/**

     * @attribute

     * @type {Color} 

     */

    color = new Color();

}




/** @interface */

class ButtonTextElement {

/**

     * @attribute

     * @type {Color} 

     */

    color = new Color();

}




/** @interface */

class ButtonElement {




/**

     * Optional: Name of the element (For the sake of readability)

     * @attribute

     * @type {string}

     */

    elementName;




/**

     * If false defaults to image element

     * @attribute

     * @type {boolean}

     * @title Is Text Element?

     */

    elementType;




/**

     * @attribute

     */

    elementEvents = { defaultState: true, hoverState: false, pressedState: false, disabledState: false };




/**

     * @attribute

     * @visibleif {elementEvents.defaultState}

     * @type {ButtonSpriteElement} 

     */

    defaultSpriteState;




/**

     * @attribute

     * @visibleif {elementEvents.hoverState && !elementType}

     * @type {ButtonSpriteElement} 

     */

    hoverSpriteState;




/**

     * @attribute

     * @visibleif {elementEvents.pressedState && !elementType}

     * @type {ButtonSpriteElement} 

     */

    pressedSpriteState;




/**

     * @attribute

     * @visibleif {elementEvents.disabledState && !elementType}

     * @type {ButtonSpriteElement} 

     */

    disabledSpriteState;




/**

     * @attribute

     * @visibleif {elementType}

     * @type {Asset} 

     * 

     * @resource font

     */

    font;




/**

     * @attribute

     * @visibleif {elementType}

     * @type {number} 

     */

    minFontSize = 8;




/**

     * @attribute

     * @visibleif {elementType}

     * @type {number} 

     */

    maxFontSize = 58;




/**

     * @attribute

     * @visibleif {elementEvents.defaultState && elementType}

     * @type {ButtonTextElement} 

     */

    defaultTextState;




/**

     * @attribute

     * @visibleif {elementEvents.hoverState && elementType}

     * @type {ButtonTextElement} 

     */

    hoverTextState;




/**

     * @attribute

     * @visibleif {elementEvents.pressedState && elementType}

     * @type {ButtonTextElement} 

     */

    pressedTextState;




/**

     * @attribute

     * @visibleif {elementEvents.disabledState && elementType}

     * @type {ButtonTextElement} 

     */

    disabledTextState;




}




/** @interface */

class ButtonStyle {

/**

     * @attribute

     * @type {ButtonStylesEnum}

     */

    styleID = ButtonStylesEnum.DEFAULT;




/**

     * The order of this array should match the order of the array of elements on the uiButton script (or viceversa).

     * @attribute

     * @type {ButtonElement[]}

     */

    buttonElements;

}




export var ButtonStyles = {};




/**

 * The {@link https://api.playcanvas.com/classes/Engine.Script.html | Script} class is

 * the base class for all PlayCanvas scripts. Learn more about writing scripts in the

 * {@link https://developer.playcanvas.com/user-manual/scripting/ | scripting guide}.

 */

export class UiStyles extends Script {




static scriptName = 'uiStyles';




    _buttonStyles = {};




/**

     * @attribute

     * @type {ButtonStyle[]}

     */

get buttonStyles() {

return Object.values(this._buttonStyles);

}

set buttonStyles(value) {

let newStylesData = {};




        value.forEach((style) => {

            newStylesData[style.styleID] = style;

});




this._buttonStyles = newStylesData;

ButtonStyles = this._buttonStyles;

}




/**

     * Called when the script is about to run for the first time.

     */

    init() {

}




/**

     * Called for enabled (running state) scripts on each tick.

     *

     * @param {number} dt - The delta time in seconds since the last frame.

     */

    update(dt) {

}

}





/** @interface */

class ButtonStyleOld {

/**

     * @type {ButtonStylesEnum}

     */

    styleID = ButtonStylesEnum.DEFAULT;





// Background Sprites

    _hoverSprite = null;

    _pressedSprite = null;

    _pressedIndicatorSprite = null;

    _disabledSprite = null;




    _hoverColor = null;

    _pressedColor = null;

    _pressedIndicatorColor = null;

    _disabledColor = null;




// Icon Sprites

    _iconDefaultSprite = null;

    _iconHovertSprite = null;

    _iconPressedSprite = null;

    _iconDisabledSprite = null;




    _iconDefaultColor = null;

    _iconHoverColor = null;

    _iconPressedColor = null;

    _iconDisabledColor = null;




// Fonts and text

    _textHoverColor = null;

    _textPressedColor = null;

    _textDisabledColor = null;




/**

     * @attribute

     */

    backgroundSpriteEvents = { hover: false, pressed: false, pressedIndicator: false, disabled: false };




/**

     * @attribute

     * @type {Asset} 

     * @resource sprite

     */

    backgroundDefaultSprite;




/**

     * @attribute

     * @type {Color} 

     */

    backgroundDefaultColor = new Color();




/**

     * @attribute

     * @visibleif {backgroundSpriteEvents.hover}

     * @type {Asset} 

     * @resource sprite

     */

get hoverSprite() {

return this.backgroundSpriteEvents.hover && this._hoverSprite != null ? this._hoverSprite : this.backgroundDefaultSprite;

}

set hoverSprite(value) {

this._hoverSprite = value;

}




/**

     * @attribute

     * @visibleif {backgroundSpriteEvents.hover}

     * @type {Color} 

     */

get hoverColor() {

return this.backgroundSpriteEvents.hover && this._hoverColor != null ? this._hoverColor : this.backgroundDefaultColor;

}

set hoverColor(value) {

this._hoverColor = value;

}




/**

     * @attribute

     * @visibleif {backgroundSpriteEvents.pressed}

     * @type {Asset} 

     * @resource sprite

     */

get pressedSprite() {

return this.backgroundSpriteEvents.pressed && this._pressedSprite != null ? this._pressedSprite : this.backgroundDefaultSprite;

}

set pressedSprite(value) {

this._pressedSprite = value;

}




/**

     * @attribute

     * @visibleif {backgroundSpriteEvents.pressed}

     * @type {Color} 

     */

get pressedColor() {

return this.backgroundSpriteEvents.pressed && this._pressedColor != null ? this._pressedColor : this.backgroundDefaultColor;

}

set pressedColor(value) {

this._pressedColor = value;

}




/**

     * @attribute

     * @visibleif {backgroundSpriteEvents.pressedIndicator}

     * @type {Asset} 

     * @resource sprite

     */

get pressedIndicatorSprite() {

return this.backgroundSpriteEvents.pressedIndicator ? this._pressedIndicatorSprite : null;

}

set pressedIndicatorSprite(value) {

this._pressedSprite = value;

}




/**

     * @attribute

     * @visibleif {backgroundSpriteEvents.pressedIndicator}

     * @type {Color} 

     */

get pressedIndicatorColor() {

return this.backgroundSpriteEvents.pressedIndicator && this._pressedIndicatorColor != null ? this._pressedIndicatorColor : Color.WHITE;

}

set pressedIndicatorColor(value) {

this._pressedIndicatorColor = value;

}




/**

     * @attribute

     * @visibleif {backgroundSpriteEvents.disabled}

     * @type {Asset} 

     * @resource sprite

     */

get disabledSprite() {

return this.backgroundSpriteEvents.disabled && this._disabledSprite != null ? this._disabledSprite : this.backgroundDefaultSprite;

}

set disabledSprite(value) {

this._disabledSprite = value;

}




/**

     * @attribute

     * @visibleif {backgroundSpriteEvents.disabled}

     * @type {Color} 

     */

get disabledColor() {

return this.backgroundSpriteEvents.disabled && this._disabledColor != null ? this._disabledColor : this.backgroundDefaultColor;

}

set disabledColor(value) {

this._disabledColor = value;

}




/**

     * @attribute

     */

    iconSpriteEvents = { hover: false, pressed: false, disabled: false };




/**

     * @attribute

     * @type {Asset} 

     * @resource sprite

     */

    iconDefaultSprite;




/**

     * @attribute

     * @type {Color} 

     */

    iconDefaultColor = new Color();




/**

     * @attribute

     * @visibleif {iconSpriteEvents.hover}

     * @type {Asset} 

     * @resource sprite

     */

get iconHoverSprite() {

return this.iconSpriteEvents.hover && this._iconHoverSprite != null ? this._iconHoverSprite : this.iconDefaultSprite;

}

set iconHoverSprite(value) {

this._iconHoverSprite = value;

}




/**

     * @attribute

     * @visibleif {iconSpriteEvents.hover}

     * @type {Color} 

     */

get iconHoverColor() {

return this.iconSpriteEvents.hover && this._iconHoverColor != null ? this._iconHoverColor : this.iconDefaultColor;

}

set iconHoverColor(value) {

this._iconHoverColor = value;

}




/**

     * @attribute

     * @visibleif {iconSpriteEvents.pressed}

     * @type {Asset} 

     * @resource sprite

     */

get iconPressedSprite() {

return this.iconSpriteEvents.pressed && this._iconPressedSprite != null ? this._iconPressedSprite : this.iconDefaultSprite;

}

set iconPressedSprite(value) {

this._iconPressedSprite = value;

}




/**

     * @attribute

     * @visibleif {iconSpriteEvents.pressed}

     * @type {Color} 

     */

get iconPressedColor() {

return this.iconSpriteEvents.pressed && this._iconPressedColor != null ? this._iconPressedColor : this.iconDefaultColor;

}

set iconPressedColor(value) {

this._iconPressedColor = value;

}




/**

     * @attribute

     * @visibleif {iconSpriteEvents.disabled}

     * @type {Asset} 

     * @resource sprite

     */

get iconDisabledSprite() {

return this.iconSpriteEvents.disabled && this._iconDisabledSprite != null ? this._iconDisabledSprite : this.iconDefaultSprite;

}

set iconDisabledSprite(value) {

this._iconDisabledSprite = value;

}




/**

     * @attribute

     * @visibleif {iconSpriteEvents.disabled}

     * @type {Color} 

     */

get iconDisabledColor() {

return this.iconSpriteEvents.disabled && this._iconDisabledColor != null ? this._iconDisabledColor : this.iconDefaultColor;

}

set iconDisabledColor(value) {

this._iconDisabledColor = value;

}




/**

     * @attribute

     * @type {Asset} 

     * @resource font

     */

    font;




/**

     * @attribute

     * @type {number} 

     */

    minFontSize = 8;




/**

     * @attribute

     * @type {number} 

     */

    maxFontSize = 58;




/**

     * @attribute

     * @type {Color} 

     */

    textDefaultColor = new Color();




/**

     * @attribute

     */

    textEvents = { hover: false, pressed: false, disabled: false };




/**

     * @attribute

     * @visibleif {textEvents.hover}

     * @type {Color} 

     */

get textHoverColor() {

return this.textEvents.hover && this._textHoverColor != null ? this._textHoverColor : this.textDefaultColor;

}

set textHoverColor(value) {

this._textHoverColor = value;

}




/**

     * @attribute

     * @visibleif {textEvents.pressed}

     * @type {Color} 

     */

get textPressedColor() {

return this.textEvents.pressed && this._textPressedColor != null ? this._textPressedColor : this.textDefaultColor;

}

set textPressedColor(value) {

this._textPressedColor = value;

}




/**

     * @attribute

     * @visibleif {textEvents.disabled}

     * @type {Color} 

     */

get textDisabledColor() {

return this.textEvents.pressed && this._textDisabledColor != null ? this._textDisabledColor : this.textDefaultColor;

}

set textDisabledColor(value) {

this._textDisabledColor = value;

}




};

settings:project:load (event error) index.mjs:204:29
z@https://playcanvas.com/editor/scene/js/editor.js:17295:8121
onLayersChanged@https://playcanvas.com/editor/scene/js/editor.js:12222:105972
fire@https://playcanvas.com/editor/scene/js/editor.js:3:41513
set layers@https://playcanvas.com/editor/scene/js/editor.js:755:11407
n@https://playcanvas.com/editor/scene/js/editor.js:17685:126862
emit@https://playcanvas.com/editor/scene/js/editor.js:17072:10685
e.reload/i/<@https://playcanvas.com/editor/scene/js/editor.js:17150:81845
AMe/n.prototype.emit@https://playcanvas.com/editor/scene/js/editor.js:1:41142
a$</tn.prototype.ingestSnapshot@https://playcanvas.com/editor/scene/js/editor.js:1:64174
a$</tn.prototype._handleSubscribe@https://playcanvas.com/editor/scene/js/editor.js:1:65615
yve</yi.prototype.handleMessage@https://playcanvas.com/editor/scene/js/editor.js:1:126009
yve</yi.prototype.bindToSocket/r.onmessage@https://playcanvas.com/editor/scene/js/editor.js:1:124579
_onauth/t.onmessage@https://playcanvas.com/editor/scene/js/editor.js:17148:10560
EventHandlerNonNull*_onauth@https://playcanvas.com/editor/scene/js/editor.js:17148:10291
i@https://playcanvas.com/editor/scene/js/editor.js:17148:11469
sentryWrapped@https://playcanvas.com/static/platform/js/sentry-libs/bundle.min.js:2:51939
EventListener.handleEventt.prototype.zt/</<@https://playcanvas.com/static/platform/js/sentry-libs/bundle.min.js:2:57324
connect@https://playcanvas.com/editor/scene/js/editor.js:17148:11484
@https://playcanvas.com/editor/scene/js/editor.js:17150:61936
once/s<@https://playcanvas.com/editor/scene/js/editor.js:17072:10482
emit@https://playcanvas.com/editor/scene/js/editor.js:17072:10685
N$/<@https://playcanvas.com/editor/scene/js/editor.js:17149:17805
once/s<@https://playcanvas.com/editor/scene/js/editor.js:17072:10482
emit@https://playcanvas.com/editor/scene/js/editor.js:17072:10685
_registerLoad/</<@https://playcanvas.com/editor/scene/js/editor.js:17149:13381
VoidFunction_registerLoad/<@https://playcanvas.com/editor/scene/js/editor.js:17149:13357
sentryWrapped@https://playcanvas.com/static/platform/js/sentry-libs/bundle.min.js:2:51939
EventListener.handleEvent*t.prototype.zt/</<@https://playcanvas.com/static/platform/js/sentry-libs/bundle.min.js:2:57324
_registerLoad@https://playcanvas.com/editor/scene/js/editor.js:17149:13298
MD@https://playcanvas.com/editor/scene/js/editor.js:17149:12748
N$@https://playcanvas.com/editor/scene/js/editor.js:17149:17719
@https://playcanvas.com/editor/scene/js/editor.js:17149:19316
@https://playcanvas.com/editor/scene/js/editor.js:17692:44635

Hi @lemonmad

I think the big thing is probably that you’re nesting interfaces within interfaces.

It can be an easy caveat to miss:

Un-nesting those should allow you to start having your changes apply permanently. As for the visibleifs, I have had some trouble with getting those to work using relative values of other interfaces. Someone from the playcanvas team may have to confirm, but I found that anything outside of the top level attribute values didn’t work for me when using visible ifs, so I had to hard code those values, when needed.

I hope this is helpful

3 Likes

I’ve read through that page so many times to see if there was something dumb I’d missed like that… Always humbling to miss something so simple!

Thanks @eproasim for taking the time to reply :slight_smile:

2 Likes

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.