lexgui 0.4.1 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/lexgui.js CHANGED
@@ -12,7 +12,7 @@ console.warn( 'Script _build/lexgui.js_ is depracated and will be removed soon.
12
12
  */
13
13
 
14
14
  var LX = {
15
- version: "0.4.1",
15
+ version: "0.4.2",
16
16
  ready: false,
17
17
  components: [], // Specific pre-build components
18
18
  signals: {}, // Events and triggers
@@ -169,7 +169,6 @@ function getThemeColor( colorName )
169
169
  {
170
170
  const r = getComputedStyle( document.querySelector( ':root' ) );
171
171
  const value = r.getPropertyValue( '--' + colorName );
172
- const theme = document.documentElement.getAttribute( "data-theme" );
173
172
 
174
173
  if( value.includes( "light-dark" ) )
175
174
  {
@@ -243,7 +242,7 @@ LX.rgbToHex = rgbToHex;
243
242
  /**
244
243
  * @method measureRealWidth
245
244
  * @description Measure the pixel width of a text
246
- * @param {Object} value Text to measure
245
+ * @param {Number} value Text to measure
247
246
  * @param {Number} paddingPlusMargin Padding offset
248
247
  */
249
248
  function measureRealWidth( value, paddingPlusMargin = 8 )
@@ -443,7 +442,7 @@ function makeCollapsible( domEl, content, parent, options = { } )
443
442
  actionIcon.dataset[ "collapsed" ] = collapsed;
444
443
  actionIcon.style.marginLeft = "auto";
445
444
 
446
- actionIcon.addEventListener( "click", function(e) {
445
+ actionIcon.addEventListener( "click", function( e ) {
447
446
  e.preventDefault();
448
447
  e.stopPropagation();
449
448
  if( this.dataset[ "collapsed" ] )
@@ -574,12 +573,13 @@ LX.makeCodeSnippet = makeCodeSnippet;
574
573
  * @description Gets an SVG element using one of LX.ICONS
575
574
  * @param {String} iconName
576
575
  * @param {String} iconTitle
576
+ * @param {String} extraClass
577
577
  */
578
- function makeIcon( iconName, iconTitle )
578
+ function makeIcon( iconName, iconTitle, extraClass = "" )
579
579
  {
580
580
  const icon = document.createElement( "a" );
581
581
  icon.title = iconTitle ?? "";
582
- icon.className = "lexicon";
582
+ icon.className = "lexicon " + extraClass;
583
583
  icon.innerHTML = LX.ICONS[ iconName ] ?? "";
584
584
  return icon;
585
585
  }
@@ -790,17 +790,17 @@ function _createCommandbar( root )
790
790
  searchItem.entry_name = t;
791
791
  searchItem.callback = c;
792
792
  searchItem.item = i;
793
- searchItem.addEventListener('click', function(e) {
793
+ searchItem.addEventListener('click', function( e ) {
794
794
  this.callback.call( window, this.entry_name );
795
795
  LX.setCommandbarState( false );
796
796
  _resetBar( true );
797
797
  });
798
- searchItem.addEventListener('mouseenter', function(e) {
798
+ searchItem.addEventListener('mouseenter', function( e ) {
799
799
  commandbar.querySelectorAll(".hovered").forEach(e => e.classList.remove('hovered'));
800
800
  this.classList.add('hovered');
801
801
  hoverElId = allItems.indexOf( this );
802
802
  });
803
- searchItem.addEventListener('mouseleave', function(e) {
803
+ searchItem.addEventListener('mouseleave', function( e ) {
804
804
  this.classList.remove('hovered');
805
805
  });
806
806
  allItems.push( searchItem );
@@ -881,7 +881,7 @@ function _createCommandbar( root )
881
881
  }
882
882
  }
883
883
 
884
- input.addEventListener('input', function(e) {
884
+ input.addEventListener('input', function( e ) {
885
885
  commandbar._addElements( this.value.toLowerCase() );
886
886
  });
887
887
 
@@ -1309,7 +1309,7 @@ LX.makeContainer = makeContainer;
1309
1309
 
1310
1310
  class IEvent {
1311
1311
 
1312
- constructor(name, value, domEvent) {
1312
+ constructor( name, value, domEvent ) {
1313
1313
  this.name = name;
1314
1314
  this.value = value;
1315
1315
  this.domEvent = domEvent;
@@ -2382,17 +2382,17 @@ class Tabs {
2382
2382
 
2383
2383
  let that = this;
2384
2384
 
2385
- container.addEventListener("dragenter", function(e) {
2385
+ container.addEventListener("dragenter", function( e ) {
2386
2386
  e.preventDefault(); // Prevent default action (open as link for some elements)
2387
2387
  this.classList.add("dockingtab");
2388
2388
  });
2389
2389
 
2390
- container.addEventListener("dragleave", function(e) {
2390
+ container.addEventListener("dragleave", function( e ) {
2391
2391
  e.preventDefault(); // Prevent default action (open as link for some elements)
2392
2392
  this.classList.remove("dockingtab");
2393
2393
  });
2394
2394
 
2395
- container.addEventListener("drop", function(e) {
2395
+ container.addEventListener("drop", function( e ) {
2396
2396
  e.preventDefault(); // Prevent default action (open as link for some elements)
2397
2397
 
2398
2398
  const tab_id = e.dataTransfer.getData("source");
@@ -2902,7 +2902,6 @@ class Menubar {
2902
2902
  this.shorts[ lastPath ] = options.short;
2903
2903
 
2904
2904
  let idx = 0;
2905
- let that = this;
2906
2905
 
2907
2906
  const _insertEntry = ( token, list ) => {
2908
2907
  if( token == undefined )
@@ -3023,15 +3022,15 @@ class Menubar {
3023
3022
 
3024
3023
  /**
3025
3024
  * @method getButton
3026
- * @param {String} title
3025
+ * @param {String} name
3027
3026
  */
3028
3027
 
3029
- getButton( title ) {
3030
- return this.buttons[ title ];
3028
+ getButton( name ) {
3029
+ return this.buttons[ name ];
3031
3030
  }
3032
3031
 
3033
3032
  /**
3034
- * @method getSubitems: recursive method to find subentries of a menu entry
3033
+ * @method getSubitems
3035
3034
  * @param {Object} item: parent item
3036
3035
  * @param {Array} tokens: split path strings
3037
3036
  */
@@ -3064,6 +3063,7 @@ class Menubar {
3064
3063
  * @param {String} path
3065
3064
  */
3066
3065
  getItem( path ) {
3066
+
3067
3067
  // process path
3068
3068
  const tokens = path.split("/");
3069
3069
 
@@ -3072,100 +3072,118 @@ class Menubar {
3072
3072
 
3073
3073
  /**
3074
3074
  * @method setButtonIcon
3075
- * @param {String} title
3075
+ * @param {String} name
3076
3076
  * @param {String} icon
3077
+ * @param {Function} callback
3078
+ * @param {Object} options
3077
3079
  */
3078
3080
 
3079
- setButtonIcon( title, icon, callback, options = {} ) {
3081
+ setButtonIcon( name, icon, callback, options = {} ) {
3082
+
3083
+ if( !name )
3084
+ {
3085
+ throw( "Set Button Name!" );
3086
+ }
3080
3087
 
3081
- const button = this.buttons[ title ];
3088
+ let button = this.buttons[ name ];
3082
3089
  if( button )
3083
3090
  {
3084
3091
  button.querySelector('a').className = "fa-solid" + " " + icon + " lexicon";
3092
+ return;
3085
3093
  }
3086
- else
3094
+
3095
+ // Otherwise, create it
3096
+ button = document.createElement('div');
3097
+ const disabled = options.disabled ?? false;
3098
+ button.className = "lexmenubutton main" + (disabled ? " disabled" : "");
3099
+ button.title = name;
3100
+ button.innerHTML = "<a class='" + icon + " lexicon'></a>";
3101
+
3102
+ if( options.float == "right" )
3087
3103
  {
3088
- let button = document.createElement('div');
3089
- const disabled = options.disabled ?? false;
3090
- button.className = "lexmenubutton" + (disabled ? " disabled" : "");
3091
- button.title = title ?? "";
3092
- button.innerHTML = "<a class='" + icon + " lexicon' style='font-size:x-large;'></a>";
3093
- button.style.padding = "5px 10px";
3094
- button.style.maxHeight = "calc(100% - 10px)";
3095
- button.style.alignItems = "center";
3104
+ button.right = true;
3105
+ }
3096
3106
 
3097
- if( options.float == "right" )
3098
- {
3099
- button.right = true;
3100
- }
3107
+ if( this.root.lastChild && this.root.lastChild.right )
3108
+ {
3109
+ this.root.lastChild.before( button );
3110
+ }
3111
+ else if( options.float == "left" )
3112
+ {
3113
+ this.root.prepend( button );
3114
+ }
3115
+ else
3116
+ {
3117
+ this.root.appendChild( button );
3118
+ }
3101
3119
 
3102
- if( this.root.lastChild && this.root.lastChild.right )
3120
+ const _b = button.querySelector('a');
3121
+ _b.addEventListener("click", (e) => {
3122
+ if( callback && !disabled )
3103
3123
  {
3104
- this.root.lastChild.before( button );
3105
- }
3106
- else if( options.float == "left" )
3107
- {
3108
- this.root.prepend( button );
3109
- }
3110
- else
3111
- {
3112
- this.root.appendChild( button );
3124
+ callback.call( this, _b, e );
3113
3125
  }
3126
+ });
3114
3127
 
3115
- const _b = button.querySelector('a');
3116
- _b.addEventListener("click", (e) => {
3117
- if(callback && !disabled)
3118
- callback.call( this, _b, e );
3119
- });
3120
- }
3128
+ this.buttons[ name ] = button;
3121
3129
  }
3122
3130
 
3123
3131
  /**
3124
3132
  * @method setButtonImage
3125
- * @param {String} title
3133
+ * @param {String} name
3126
3134
  * @param {String} src
3135
+ * @param {Function} callback
3136
+ * @param {Object} options
3127
3137
  */
3128
3138
 
3129
- setButtonImage( title, src, callback, options = {} ) {
3130
- const button = this.buttons[ title ];
3139
+ setButtonImage( name, src, callback, options = {} ) {
3140
+
3141
+ if( !name )
3142
+ {
3143
+ throw( "Set Button Name!" );
3144
+ }
3145
+
3146
+ let button = this.buttons[ name ];
3131
3147
  if( button )
3132
3148
  {
3133
- button.querySelector('a').className = "fa-solid" + " " + icon + " lexicon";
3149
+ button.querySelector('img').src = src;
3150
+ return;
3134
3151
  }
3135
- else
3152
+
3153
+ // Otherwise, create it
3154
+ button = document.createElement('div');
3155
+ const disabled = options.disabled ?? false;
3156
+ button.className = "lexmenubutton" + (disabled ? " disabled" : "");
3157
+ button.title = name;
3158
+ button.innerHTML = "<a><image src='" + src + "' class='lexicon' style='height:32px;'></a>";
3159
+
3160
+ if( options.float == "right" )
3136
3161
  {
3137
- let button = document.createElement('div');
3138
- const disabled = options.disabled ?? false;
3139
- button.className = "lexmenubutton" + (disabled ? " disabled" : "");
3140
- button.title = title ?? "";
3141
- button.innerHTML = "<a><image src='" + src + "' class='lexicon' style='height:32px;'></a>";
3142
- button.style.padding = "5px";
3143
- button.style.alignItems = "center";
3162
+ button.right = true;
3163
+ }
3144
3164
 
3145
- if( options.float == "right" )
3146
- {
3147
- button.right = true;
3148
- }
3165
+ if( this.root.lastChild && this.root.lastChild.right )
3166
+ {
3167
+ this.root.lastChild.before( button );
3168
+ }
3169
+ else if( options.float == "left" )
3170
+ {
3171
+ this.root.prepend( button );
3172
+ }
3173
+ else
3174
+ {
3175
+ this.root.appendChild( button );
3176
+ }
3149
3177
 
3150
- if( this.root.lastChild && this.root.lastChild.right )
3178
+ const _b = button.querySelector('a');
3179
+ _b.addEventListener("click", (e) => {
3180
+ if( callback && !disabled )
3151
3181
  {
3152
- this.root.lastChild.before( button );
3153
- }
3154
- else if( options.float == "left" )
3155
- {
3156
- this.root.prepend( button );
3157
- }
3158
- else
3159
- {
3160
- this.root.appendChild( button );
3182
+ callback.call( this, _b, e );
3161
3183
  }
3184
+ });
3162
3185
 
3163
- const _b = button.querySelector('a');
3164
- _b.addEventListener("click", (e) => {
3165
- if(callback && !disabled)
3166
- callback.call( this, _b, e );
3167
- });
3168
- }
3186
+ this.buttons[ name ] = button;
3169
3187
  }
3170
3188
 
3171
3189
  /**
@@ -3277,11 +3295,13 @@ class SideBar {
3277
3295
  * headerIcon: Icon to be shown as avatar (from LX.ICONS)
3278
3296
  * headerTitle: Header title
3279
3297
  * headerSubtitle: Header subtitle
3298
+ * header: HTMLElement to add a custom header
3280
3299
  * skipFooter: Do not use sidebar footer [false]
3281
3300
  * footerImg: Image to be shown as avatar
3282
3301
  * footerIcon: Icon to be shown as avatar (from LX.ICONS)
3283
3302
  * footerTitle: Footer title
3284
3303
  * footerSubtitle: Footer subtitle
3304
+ * footer: HTMLElement to add a custom footer
3285
3305
  * collapsable: Sidebar can toggle between collapsed/expanded [true]
3286
3306
  * collapseToIcons: When Sidebar collapses, icons remains visible [true]
3287
3307
  * onHeaderPressed: Function to call when header is pressed
@@ -3313,62 +3333,17 @@ class SideBar {
3313
3333
 
3314
3334
  }, 10 );
3315
3335
 
3316
- // This account for header, footer and all inner paddings
3317
- let contentOffset = 32;
3318
-
3319
3336
  // Header
3320
3337
  if( !( options.skipHeader ?? false ) )
3321
3338
  {
3322
- this.header = document.createElement( 'div' );
3339
+ this.header = options.header ?? this._generateDefaultHeader( options );
3340
+ console.assert( this.header.constructor === HTMLDivElement, "Use an HTMLDivElement to build your custom header" );
3323
3341
  this.header.className = "lexsidebarheader";
3324
3342
  this.root.appendChild( this.header );
3325
3343
 
3326
- this.header.addEventListener( "click", e => {
3327
- if( this.collapsed )
3328
- {
3329
- e.preventDefault();
3330
- e.stopPropagation();
3331
- this.toggleCollapsed();
3332
- }
3333
- else if( options.onHeaderPressed )
3334
- {
3335
- options.onHeaderPressed( e );
3336
- }
3337
- } );
3338
-
3339
- const avatar = document.createElement( 'span' );
3340
- avatar.className = "lexavatar";
3341
- this.header.appendChild( avatar );
3342
-
3343
- if( options.headerImage )
3344
- {
3345
- const avatarImg = document.createElement( 'img' );
3346
- avatarImg.src = options.headerImage;
3347
- avatar.appendChild( avatarImg );
3348
- }
3349
- else if( options.headerIcon )
3350
- {
3351
- const avatarIcon = LX.makeIcon( options.headerIcon );
3352
- avatar.appendChild( avatarIcon );
3353
- }
3354
-
3355
- // Info
3356
- {
3357
- const info = document.createElement( 'div' );
3358
- this.header.appendChild( info );
3359
-
3360
- const infoText = document.createElement( 'span' );
3361
- infoText.innerHTML = options.headerTitle ?? "";
3362
- info.appendChild( infoText );
3363
-
3364
- const infoSubtext = document.createElement( 'span' );
3365
- infoSubtext.innerHTML = options.headerSubtitle ?? "";
3366
- info.appendChild( infoSubtext );
3367
- }
3368
-
3369
3344
  if( this.collapsable )
3370
3345
  {
3371
- const icon = LX.makeIcon( "Sidebar", "Toggle Sidebar" );
3346
+ const icon = LX.makeIcon( "Sidebar", "Toggle Sidebar", "toggler" );
3372
3347
  this.header.appendChild( icon );
3373
3348
 
3374
3349
  icon.addEventListener( "click", (e) => {
@@ -3377,12 +3352,10 @@ class SideBar {
3377
3352
  this.toggleCollapsed();
3378
3353
  } );
3379
3354
  }
3380
-
3381
- contentOffset += 52;
3382
3355
  }
3383
3356
 
3384
3357
  // Entry filter
3385
- if( !( options.filter ?? false ) )
3358
+ if( ( options.filter ?? false ) )
3386
3359
  {
3387
3360
  const panel = new Panel();
3388
3361
  panel.addText(null, "", (value, event) => {
@@ -3391,7 +3364,6 @@ class SideBar {
3391
3364
  }, { placeholder: "Search...", icon: "fa-solid fa-magnifying-glass" });
3392
3365
  this.filter = panel.root.childNodes[ 0 ];
3393
3366
  this.root.appendChild( this.filter );
3394
- contentOffset += 31;
3395
3367
  }
3396
3368
 
3397
3369
  // Content
@@ -3404,59 +3376,123 @@ class SideBar {
3404
3376
  // Footer
3405
3377
  if( !( options.skipFooter ?? false ) )
3406
3378
  {
3407
- this.footer = document.createElement( 'div' );
3379
+ this.footer = options.footer ?? this._generateDefaultFooter( options );
3380
+ console.assert( this.footer.constructor === HTMLDivElement, "Use an HTMLDivElement to build your custom footer" );
3408
3381
  this.footer.className = "lexsidebarfooter";
3409
3382
  this.root.appendChild( this.footer );
3383
+ }
3410
3384
 
3411
- this.footer.addEventListener( "click", e => {
3412
- if( options.onFooterPressed )
3413
- {
3414
- options.onFooterPressed( e );
3415
- }
3416
- } );
3385
+ // Set width depending on header/footer
3386
+ doAsync( () => {
3387
+ // This account for header, footer and all inner paddings
3388
+ const contentOffset = 32 + ( this.header?.offsetHeight ?? 0 ) +
3389
+ ( this.filter?.offsetHeight ?? 0 ) +
3390
+ ( this.footer?.offsetHeight ?? 0 );
3391
+ this.content.style.height = `calc(100% - ${ contentOffset }px)`;
3392
+ }, 10 );
3393
+
3394
+ this.items = [ ];
3395
+ this.icons = { };
3396
+ this.groups = { };
3397
+ }
3417
3398
 
3418
- const avatar = document.createElement( 'span' );
3419
- avatar.className = "lexavatar";
3420
- this.footer.appendChild( avatar );
3399
+ _generateDefaultHeader( options ) {
3421
3400
 
3422
- if( options.footerImage )
3401
+ const header = document.createElement( 'div' );
3402
+
3403
+ header.addEventListener( "click", e => {
3404
+ if( this.collapsed )
3423
3405
  {
3424
- const avatarImg = document.createElement( 'img' );
3425
- avatarImg.src = options.footerImage;
3426
- avatar.appendChild( avatarImg );
3406
+ e.preventDefault();
3407
+ e.stopPropagation();
3408
+ this.toggleCollapsed();
3427
3409
  }
3428
- else if( options.footerIcon )
3410
+ else if( options.onHeaderPressed )
3429
3411
  {
3430
- const avatarIcon = LX.makeIcon( options.footerIcon );
3431
- avatar.appendChild( avatarIcon );
3412
+ options.onHeaderPressed( e );
3432
3413
  }
3414
+ } );
3433
3415
 
3434
- // Info
3435
- {
3436
- const info = document.createElement( 'div' );
3437
- this.footer.appendChild( info );
3416
+ const avatar = document.createElement( 'span' );
3417
+ avatar.className = "lexavatar";
3418
+ header.appendChild( avatar );
3419
+
3420
+ if( options.headerImage )
3421
+ {
3422
+ const avatarImg = document.createElement( 'img' );
3423
+ avatarImg.src = options.headerImage;
3424
+ avatar.appendChild( avatarImg );
3425
+ }
3426
+ else if( options.headerIcon )
3427
+ {
3428
+ const avatarIcon = LX.makeIcon( options.headerIcon );
3429
+ avatar.appendChild( avatarIcon );
3430
+ }
3431
+
3432
+ // Info
3433
+ {
3434
+ const info = document.createElement( 'div' );
3435
+ info.className = "infodefault";
3436
+ header.appendChild( info );
3437
+
3438
+ const infoText = document.createElement( 'span' );
3439
+ infoText.innerHTML = options.headerTitle ?? "";
3440
+ info.appendChild( infoText );
3441
+
3442
+ const infoSubtext = document.createElement( 'span' );
3443
+ infoSubtext.innerHTML = options.headerSubtitle ?? "";
3444
+ info.appendChild( infoSubtext );
3445
+ }
3446
+
3447
+ return header;
3448
+ }
3449
+
3450
+ _generateDefaultFooter( options ) {
3438
3451
 
3439
- const infoText = document.createElement( 'span' );
3440
- infoText.innerHTML = options.footerTitle ?? "";
3441
- info.appendChild( infoText );
3452
+ const footer = document.createElement( 'div' );
3442
3453
 
3443
- const infoSubtext = document.createElement( 'span' );
3444
- infoSubtext.innerHTML = options.footerSubtitle ?? "";
3445
- info.appendChild( infoSubtext );
3454
+ footer.addEventListener( "click", e => {
3455
+ if( options.onFooterPressed )
3456
+ {
3457
+ options.onFooterPressed( e );
3446
3458
  }
3459
+ } );
3447
3460
 
3448
- const icon = LX.makeIcon( "MenuArrows" );
3449
- this.footer.appendChild( icon );
3461
+ const avatar = document.createElement( 'span' );
3462
+ avatar.className = "lexavatar";
3463
+ footer.appendChild( avatar );
3450
3464
 
3451
- contentOffset += 52;
3465
+ if( options.footerImage )
3466
+ {
3467
+ const avatarImg = document.createElement( 'img' );
3468
+ avatarImg.src = options.footerImage;
3469
+ avatar.appendChild( avatarImg );
3470
+ }
3471
+ else if( options.footerIcon )
3472
+ {
3473
+ const avatarIcon = LX.makeIcon( options.footerIcon );
3474
+ avatar.appendChild( avatarIcon );
3452
3475
  }
3453
3476
 
3454
- // Set width depending on header/footer
3455
- this.content.style.height = `calc(100% - ${ contentOffset }px)`;
3477
+ // Info
3478
+ {
3479
+ const info = document.createElement( 'div' );
3480
+ info.className = "infodefault";
3481
+ footer.appendChild( info );
3456
3482
 
3457
- this.items = [ ];
3458
- this.icons = { };
3459
- this.groups = { };
3483
+ const infoText = document.createElement( 'span' );
3484
+ infoText.innerHTML = options.footerTitle ?? "";
3485
+ info.appendChild( infoText );
3486
+
3487
+ const infoSubtext = document.createElement( 'span' );
3488
+ infoSubtext.innerHTML = options.footerSubtitle ?? "";
3489
+ info.appendChild( infoSubtext );
3490
+ }
3491
+
3492
+ const icon = LX.makeIcon( "MenuArrows" );
3493
+ footer.appendChild( icon );
3494
+
3495
+ return footer;
3460
3496
  }
3461
3497
 
3462
3498
  /**
@@ -3529,9 +3565,9 @@ class SideBar {
3529
3565
  * @param {String} path
3530
3566
  * @param {Object} options:
3531
3567
  * callback: Function to call on each item
3532
- * icon: Entry icon
3533
- * collapsable: Add entry as a collapsable section
3534
3568
  * className: Add class to the entry DOM element
3569
+ * collapsable: Add entry as a collapsable section
3570
+ * icon: Entry icon
3535
3571
  */
3536
3572
 
3537
3573
  add( path, options = {} ) {
@@ -3765,6 +3801,8 @@ class SideBar {
3765
3801
  entry.classList.add( "selected" );
3766
3802
  });
3767
3803
 
3804
+ const isCollapsable = options.collapsable != undefined ? options.collapsable : ( options.collapsable || item[ key ].length );
3805
+
3768
3806
  if( options.action )
3769
3807
  {
3770
3808
  const actionIcon = LX.makeIcon( options.action.icon ?? "MoreHorizontal", options.action.name );
@@ -3777,7 +3815,7 @@ class SideBar {
3777
3815
  if( f ) f.call( this, key, e );
3778
3816
  } );
3779
3817
  }
3780
- else if( options.collapsable )
3818
+ else if( isCollapsable )
3781
3819
  {
3782
3820
  const collapsableContent = document.createElement( 'div' );
3783
3821
  Object.assign( collapsableContent.style, { width: "100%", display: "none" } );
@@ -3812,7 +3850,12 @@ class SideBar {
3812
3850
  let subentryContainer = document.createElement( 'div' );
3813
3851
  subentryContainer.className = "lexsidebarsubentrycontainer";
3814
3852
 
3815
- if( currentGroup )
3853
+ if( isCollapsable )
3854
+ {
3855
+ this.collapseContainer.appendChild( subentryContainer )
3856
+ delete this.collapseContainer;
3857
+ }
3858
+ else if( currentGroup )
3816
3859
  {
3817
3860
  currentGroup.appendChild( subentryContainer );
3818
3861
  }
@@ -3882,31 +3925,34 @@ class Widget {
3882
3925
  static CHECKBOX = 5;
3883
3926
  static TOGGLE = 6;
3884
3927
  static RADIO = 7;
3885
- static COLOR = 8;
3886
- static RANGE = 9;
3887
- static NUMBER = 10;
3888
- static TITLE = 11;
3889
- static VECTOR = 12;
3890
- static TREE = 13;
3891
- static PROGRESS = 14;
3892
- static FILE = 15;
3893
- static LAYERS = 16;
3894
- static ARRAY = 17;
3895
- static LIST = 18;
3896
- static TAGS = 19;
3897
- static CURVE = 20;
3898
- static CARD = 21;
3899
- static IMAGE = 22;
3900
- static CONTENT = 23;
3901
- static CUSTOM = 24;
3902
- static SEPARATOR = 25;
3903
- static KNOB = 26;
3904
- static SIZE = 27;
3905
- static PAD = 28;
3906
- static FORM = 29;
3907
- static DIAL = 30;
3908
- static COUNTER = 31;
3909
- static TABLE = 32;
3928
+ static BUTTONS = 8;
3929
+ static COLOR = 9;
3930
+ static RANGE = 10;
3931
+ static NUMBER = 11;
3932
+ static TITLE = 12;
3933
+ static VECTOR = 13;
3934
+ static TREE = 14;
3935
+ static PROGRESS = 15;
3936
+ static FILE = 16;
3937
+ static LAYERS = 17;
3938
+ static ARRAY = 18;
3939
+ static LIST = 19;
3940
+ static TAGS = 20;
3941
+ static CURVE = 21;
3942
+ static CARD = 22;
3943
+ static IMAGE = 23;
3944
+ static CONTENT = 24;
3945
+ static CUSTOM = 25;
3946
+ static SEPARATOR = 26;
3947
+ static KNOB = 27;
3948
+ static SIZE = 28;
3949
+ static PAD = 29;
3950
+ static FORM = 30;
3951
+ static DIAL = 31;
3952
+ static COUNTER = 32;
3953
+ static TABLE = 33;
3954
+ static TABS = 34;
3955
+ static BLANK = 35;
3910
3956
 
3911
3957
  static NO_CONTEXT_TYPES = [
3912
3958
  Widget.BUTTON,
@@ -3915,10 +3961,11 @@ class Widget {
3915
3961
  Widget.PROGRESS
3916
3962
  ];
3917
3963
 
3918
- constructor( name, type, options ) {
3964
+ constructor( name, type, value, options ) {
3919
3965
  this.name = name;
3920
3966
  this.type = type;
3921
3967
  this.options = options;
3968
+ this._initialValue = value;
3922
3969
  }
3923
3970
 
3924
3971
  value() {
@@ -3931,22 +3978,31 @@ class Widget {
3931
3978
  console.warn( "Can't get value of " + this.typeName() );
3932
3979
  }
3933
3980
 
3934
- set( value, skipCallback = false, signalName = "" ) {
3981
+ set( value, skipCallback, event ) {
3935
3982
 
3936
3983
  if( this.onSetValue )
3937
3984
  {
3938
- return this.onSetValue( value, skipCallback );
3985
+ let resetButton = this.domEl.querySelector( ".lexwidgetname .lexicon" );
3986
+ if( resetButton )
3987
+ {
3988
+ resetButton.style.display = ( value != this.value() ? "block" : "none" );
3989
+ resetButton.style.display = ( value != this._initialValue ? "block" : "none" );
3990
+ }
3991
+
3992
+ return this.onSetValue( value, skipCallback ?? false, event );
3939
3993
  }
3940
3994
 
3941
3995
  console.warn("Can't set value of " + this.typeName());
3942
3996
  }
3943
3997
 
3944
- oncontextmenu(e) {
3998
+ oncontextmenu( e ) {
3945
3999
 
3946
- if( Widget.NO_CONTEXT_TYPES.includes(this.type) )
4000
+ if( Widget.NO_CONTEXT_TYPES.includes( this.type ) )
4001
+ {
3947
4002
  return;
4003
+ }
3948
4004
 
3949
- addContextMenu(this.typeName(), e, c => {
4005
+ addContextMenu( this.typeName(), e, c => {
3950
4006
  c.add("Copy", () => { this.copy() });
3951
4007
  c.add("Paste", { disabled: !this._can_paste(), callback: () => { this.paste() } } );
3952
4008
  });
@@ -4003,6 +4059,8 @@ class Widget {
4003
4059
  case Widget.DIAL: return "Dial";
4004
4060
  case Widget.COUNTER: return "Counter";
4005
4061
  case Widget.TABLE: return "Table";
4062
+ case Widget.TABS: return "Tabs";
4063
+ case Widget.BLANK: return "Blank";
4006
4064
  case Widget.CUSTOM: return this.customName;
4007
4065
  }
4008
4066
 
@@ -4016,26 +4074,31 @@ class Widget {
4016
4074
 
4017
4075
  LX.Widget = Widget;
4018
4076
 
4019
- function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
4077
+ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
4020
4078
  {
4021
4079
  let custom_idx = simple_guidGenerator();
4022
4080
 
4023
- Panel.prototype[ 'add' + custom_widget_name ] = function( name, instance, callback ) {
4081
+ Panel.prototype[ 'add' + customWidgetName ] = function( name, instance, callback ) {
4024
4082
 
4025
- let widget = this.create_widget(name, Widget.CUSTOM, options);
4026
- widget.customName = custom_widget_name;
4083
+ let widget = this._createWidget( Widget.CUSTOM, name, null, options );
4084
+ widget.customName = customWidgetName;
4027
4085
  widget.customIdx = custom_idx;
4086
+
4028
4087
  widget.onGetValue = () => {
4029
4088
  return instance;
4030
4089
  };
4031
- widget.onSetValue = ( newValue, skipCallback ) => {
4090
+
4091
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
4032
4092
  instance = newValue;
4033
4093
  refresh_widget();
4034
4094
  element.querySelector( ".lexcustomitems" ).toggleAttribute( 'hidden', false );
4035
- if( !skipCallback ) this._trigger( new IEvent( name, instance, null ), callback );
4095
+ if( !skipCallback )
4096
+ {
4097
+ this._trigger( new IEvent( name, instance, event ), callback );
4098
+ }
4036
4099
  };
4037
4100
 
4038
- let element = widget.domEl;
4101
+ const element = widget.domEl;
4039
4102
  element.style.flexWrap = "wrap";
4040
4103
 
4041
4104
  let container, custom_widgets;
@@ -4045,8 +4108,10 @@ function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
4045
4108
 
4046
4109
  const refresh_widget = () => {
4047
4110
 
4048
- if(instance)
4111
+ if( instance )
4112
+ {
4049
4113
  widget.instance = instance = Object.assign(deepCopy(default_instance), instance);
4114
+ }
4050
4115
 
4051
4116
  if(container) container.remove();
4052
4117
  if(custom_widgets) custom_widgets.remove();
@@ -4058,7 +4123,7 @@ function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
4058
4123
  this.queue(container);
4059
4124
 
4060
4125
  let buttonName = "<a class='fa-solid " + (options.icon ?? "fa-cube") + "' style='float:left'></a>";
4061
- buttonName += custom_widget_name + (!instance ? " [empty]" : "");
4126
+ buttonName += customWidgetName + (!instance ? " [empty]" : "");
4062
4127
  // Add alwayis icon to keep spacing right
4063
4128
  buttonName += "<a class='fa-solid " + (instance ? "fa-bars-staggered" : " ") + " menu' style='float:right; width:5%;'></a>";
4064
4129
 
@@ -4070,7 +4135,7 @@ function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
4070
4135
  else
4071
4136
  {
4072
4137
  addContextMenu(null, event, c => {
4073
- c.add("New " + custom_widget_name, () => {
4138
+ c.add("New " + customWidgetName, () => {
4074
4139
  instance = {};
4075
4140
  refresh_widget();
4076
4141
  element.querySelector(".lexcustomitems").toggleAttribute('hidden', false);
@@ -4182,10 +4247,10 @@ class NodeTree {
4182
4247
  _create_item( parent, node, level = 0, selectedId ) {
4183
4248
 
4184
4249
  const that = this;
4185
- const node_filter_input = this.domEl.querySelector( "#lexnodetree_filter" );
4250
+ const nodeFilterInput = this.domEl.querySelector( "#lexnodetree_filter" );
4186
4251
 
4187
4252
  node.children = node.children ?? [];
4188
- if( node_filter_input && !node.id.includes( node_filter_input.value ) || (selectedId != undefined) && selectedId != node.id )
4253
+ if( nodeFilterInput && !node.id.includes( nodeFilterInput.value ) || (selectedId != undefined) && selectedId != node.id )
4189
4254
  {
4190
4255
  for( var i = 0; i < node.children.length; ++i )
4191
4256
  {
@@ -4425,7 +4490,7 @@ class NodeTree {
4425
4490
  const nameInput = document.createElement( "input" );
4426
4491
  nameInput.toggleAttribute( "hidden", !node.rename );
4427
4492
  nameInput.value = node.id;
4428
- item.appendChild(nameInput);
4493
+ item.appendChild( nameInput );
4429
4494
 
4430
4495
  if( node.rename )
4431
4496
  {
@@ -4433,7 +4498,7 @@ class NodeTree {
4433
4498
  nameInput.focus();
4434
4499
  }
4435
4500
 
4436
- nameInput.addEventListener("keyup", function(e){
4501
+ nameInput.addEventListener("keyup", function( e ) {
4437
4502
  if( e.key == "Enter" )
4438
4503
  {
4439
4504
  this.value = this.value.replace(/\s/g, '_');
@@ -4456,7 +4521,7 @@ class NodeTree {
4456
4521
  }
4457
4522
  });
4458
4523
 
4459
- nameInput.addEventListener("blur", function(e){
4524
+ nameInput.addEventListener("blur", function( e ) {
4460
4525
  delete node.rename;
4461
4526
  that.refresh();
4462
4527
  });
@@ -4464,7 +4529,7 @@ class NodeTree {
4464
4529
  if( this.options.draggable ?? true )
4465
4530
  {
4466
4531
  // Drag nodes
4467
- if(parent) // Root doesn't move!
4532
+ if( parent ) // Root doesn't move!
4468
4533
  {
4469
4534
  item.addEventListener("dragstart", e => {
4470
4535
  window.__tree_node_dragged = node;
@@ -4531,7 +4596,7 @@ class NodeTree {
4531
4596
  // Show/hide children
4532
4597
  if( isParent )
4533
4598
  {
4534
- item.querySelector('a.hierarchy').addEventListener("click", function(e) {
4599
+ item.querySelector('a.hierarchy').addEventListener("click", function( e ) {
4535
4600
 
4536
4601
  handled = true;
4537
4602
  e.stopImmediatePropagation();
@@ -4549,40 +4614,44 @@ class NodeTree {
4549
4614
 
4550
4615
  // Add button icons
4551
4616
 
4617
+ const inputContainer = document.createElement( "div" );
4618
+ item.appendChild( inputContainer );
4619
+
4620
+ if( node.actions )
4621
+ {
4622
+ for( let i = 0; i < node.actions.length; ++i )
4623
+ {
4624
+ let a = node.actions[ i ];
4625
+ let actionEl = document.createElement('a');
4626
+ actionEl.className = "lexicon " + a.icon;
4627
+ actionEl.title = a.name;
4628
+ actionEl.addEventListener("click", function( e ) {
4629
+ a.callback( node, actionEl );
4630
+ e.stopPropagation();
4631
+ });
4632
+
4633
+ inputContainer.appendChild( actionEl );
4634
+ }
4635
+ }
4636
+
4552
4637
  if( !node.skipVisibility ?? false )
4553
4638
  {
4554
- let visibility = document.createElement('a');
4555
- visibility.className = "itemicon fa-solid fa-eye" + (!node.visible ? "-slash" : "");
4639
+ let visibility = document.createElement( 'a' );
4640
+ visibility.className = "lexicon fa-solid fa-eye" + ( !node.visible ? "-slash" : "" );
4556
4641
  visibility.title = "Toggle visible";
4557
- visibility.addEventListener("click", function(e) {
4642
+ visibility.addEventListener("click", function( e ) {
4558
4643
  e.stopPropagation();
4559
4644
  node.visible = node.visible === undefined ? false : !node.visible;
4560
- this.className = "itemicon fa-solid fa-eye" + (!node.visible ? "-slash" : "");
4645
+ this.className = "lexicon fa-solid fa-eye" + ( !node.visible ? "-slash" : "" );
4561
4646
  // Trigger visibility event
4562
4647
  if( that.onevent )
4563
4648
  {
4564
- const event = new TreeEvent(TreeEvent.NODE_VISIBILITY, node, node.visible);
4649
+ const event = new TreeEvent( TreeEvent.NODE_VISIBILITY, node, node.visible );
4565
4650
  that.onevent( event );
4566
4651
  }
4567
4652
  });
4568
4653
 
4569
- item.appendChild(visibility);
4570
- }
4571
-
4572
- if( node.actions )
4573
- {
4574
- for( var i = 0; i < node.actions.length; ++i )
4575
- {
4576
- let a = node.actions[i];
4577
- var actionEl = document.createElement('a');
4578
- actionEl.className = "itemicon " + a.icon;
4579
- actionEl.title = a.name;
4580
- actionEl.addEventListener("click", function(e) {
4581
- a.callback(node, actionEl);
4582
- e.stopPropagation();
4583
- });
4584
- item.appendChild(actionEl);
4585
- }
4654
+ inputContainer.appendChild( visibility );
4586
4655
  }
4587
4656
 
4588
4657
  if( selectedId != undefined && node.id == selectedId )
@@ -4643,28 +4712,35 @@ class Panel {
4643
4712
  */
4644
4713
 
4645
4714
  constructor( options = {} ) {
4715
+
4646
4716
  var root = document.createElement('div');
4647
4717
  root.className = "lexpanel";
4718
+
4648
4719
  if( options.id )
4720
+ {
4649
4721
  root.id = options.id;
4722
+ }
4723
+
4650
4724
  if( options.className )
4725
+ {
4651
4726
  root.className += " " + options.className;
4727
+ }
4652
4728
 
4653
4729
  root.style.width = options.width || "calc( 100% - 6px )";
4654
4730
  root.style.height = options.height || "100%";
4655
- Object.assign(root.style, options.style ?? {});
4731
+ Object.assign( root.style, options.style ?? {} );
4656
4732
 
4657
4733
  this._inline_widgets_left = -1;
4658
4734
  this._inline_queued_container = null;
4659
4735
 
4660
4736
  this.root = root;
4661
4737
 
4662
- this.onevent = (e => {});
4738
+ this.onevent = ( e => {} );
4663
4739
 
4664
4740
  // branches
4665
- this.branch_open = false;
4741
+ this._branchOpen = false;
4666
4742
  this.branches = [];
4667
- this.current_branch = null;
4743
+ this._currentBranch = null;
4668
4744
  this.widgets = {};
4669
4745
  this._queue = []; // Append widgets in other locations
4670
4746
  }
@@ -4705,13 +4781,9 @@ class Panel {
4705
4781
 
4706
4782
  attach( content ) {
4707
4783
 
4708
- if(!content)
4709
- throw("no content to attach");
4710
-
4784
+ console.assert( content, "No content to attach!" );
4711
4785
  content.parent = this;
4712
- let element = content.root ? content.root : content;
4713
- //this.root.style.maxHeight = "800px"; // limit size when attaching stuff from outside
4714
- this.root.appendChild( element );
4786
+ this.root.appendChild( content.root ? content.root : content );
4715
4787
  }
4716
4788
 
4717
4789
  /**
@@ -4720,18 +4792,18 @@ class Panel {
4720
4792
 
4721
4793
  clear() {
4722
4794
 
4723
- this.branch_open = false;
4795
+ this._branchOpen = false;
4724
4796
  this.branches = [];
4725
- this.current_branch = null;
4797
+ this._currentBranch = null;
4726
4798
 
4727
4799
  for( let w in this.widgets )
4728
4800
  {
4729
- if( this.widgets[w].options && this.widgets[w].options.signal )
4801
+ if( this.widgets[ w ].options && this.widgets[ w ].options.signal )
4730
4802
  {
4731
- const signal = this.widgets[w].options.signal;
4803
+ const signal = this.widgets[ w ].options.signal;
4732
4804
  for( let i = 0; i < LX.signals[signal].length; i++ )
4733
4805
  {
4734
- if( LX.signals[signal][i] == this.widgets[w] )
4806
+ if( LX.signals[signal][i] == this.widgets[ w ] )
4735
4807
  {
4736
4808
  LX.signals[signal] = [...LX.signals[signal].slice(0, i), ...LX.signals[signal].slice(i+1)];
4737
4809
  }
@@ -4743,7 +4815,7 @@ class Panel {
4743
4815
  {
4744
4816
  for( let w = 0; w < this.signals.length; w++ )
4745
4817
  {
4746
- let widget = Object.values(this.signals[w])[0];
4818
+ let widget = Object.values(this.signals[ w ])[0];
4747
4819
  let signal = widget.options.signal;
4748
4820
  for( let i = 0; i < LX.signals[signal].length; i++ )
4749
4821
  {
@@ -4768,7 +4840,7 @@ class Panel {
4768
4840
  sameLine( number ) {
4769
4841
 
4770
4842
  this._inline_queued_container = this.queuedContainer;
4771
- this._inline_widgets_left = number || Infinity;
4843
+ this._inline_widgets_left = ( number || Infinity );
4772
4844
  }
4773
4845
 
4774
4846
  /**
@@ -4778,7 +4850,7 @@ class Panel {
4778
4850
 
4779
4851
  endLine( justifyContent ) {
4780
4852
 
4781
- if( this._inline_widgets_left == -1)
4853
+ if( this._inline_widgets_left == -1 )
4782
4854
  {
4783
4855
  console.warn("No pending widgets to be inlined!");
4784
4856
  return;
@@ -4800,27 +4872,37 @@ class Panel {
4800
4872
  // Push all elements single element or Array[element, container]
4801
4873
  for( let item of this._inlineWidgets )
4802
4874
  {
4803
- const is_pair = item.constructor == Array;
4875
+ const isPair = ( item.constructor == Array );
4804
4876
 
4805
- if(is_pair)
4877
+ if( isPair )
4806
4878
  {
4807
4879
  // eg. an array, inline items appended later to
4808
- if( this._inline_queued_container)
4809
- this._inlineContainer.appendChild( item[0] );
4880
+ if( this._inline_queued_container )
4881
+ {
4882
+ this._inlineContainer.appendChild( item[ 0 ] );
4883
+ }
4810
4884
  // eg. a dropdown, item is appended to parent, not to inline cont.
4811
4885
  else
4812
- item[1].appendChild(item[0]);
4886
+ {
4887
+ item[ 1 ].appendChild( item[ 0 ] );
4888
+ }
4813
4889
  }
4814
4890
  else
4891
+ {
4815
4892
  this._inlineContainer.appendChild( item );
4893
+ }
4816
4894
  }
4817
4895
 
4818
- if(!this._inline_queued_container)
4896
+ if( !this._inline_queued_container )
4819
4897
  {
4820
- if( this.current_branch)
4821
- this.current_branch.content.appendChild( this._inlineContainer );
4898
+ if( this._currentBranch )
4899
+ {
4900
+ this._currentBranch.content.appendChild( this._inlineContainer );
4901
+ }
4822
4902
  else
4903
+ {
4823
4904
  this.root.appendChild( this._inlineContainer );
4905
+ }
4824
4906
  }
4825
4907
  else
4826
4908
  {
@@ -4844,20 +4926,24 @@ class Panel {
4844
4926
 
4845
4927
  branch( name, options = {} ) {
4846
4928
 
4847
- if( this.branch_open )
4929
+ if( this._branchOpen )
4930
+ {
4848
4931
  this.merge();
4932
+ }
4849
4933
 
4850
4934
  // Create new branch
4851
- var branch = new Branch(name, options);
4935
+ var branch = new Branch( name, options );
4852
4936
  branch.panel = this;
4853
4937
 
4854
4938
  // Declare new open
4855
- this.branch_open = true;
4856
- this.current_branch = branch;
4939
+ this._branchOpen = true;
4940
+ this._currentBranch = branch;
4857
4941
 
4858
4942
  // Append to panel
4859
- if( this.branches.length == 0)
4943
+ if( this.branches.length == 0 )
4944
+ {
4860
4945
  branch.root.classList.add('first');
4946
+ }
4861
4947
 
4862
4948
  // This is the last!
4863
4949
  this.root.querySelectorAll(".lexbranch.last").forEach( e => { e.classList.remove("last"); } );
@@ -4869,16 +4955,15 @@ class Panel {
4869
4955
  // Add widget filter
4870
4956
  if( options.filter )
4871
4957
  {
4872
- this._addFilter( options.filter, {callback: this._searchWidgets.bind(this, branch.name)} );
4958
+ this._addFilter( options.filter, { callback: this._searchWidgets.bind( this, branch.name ) } );
4873
4959
  }
4874
4960
 
4875
4961
  return branch;
4876
4962
  }
4877
4963
 
4878
4964
  merge() {
4879
-
4880
- this.branch_open = false;
4881
- this.current_branch = null;
4965
+ this._branchOpen = false;
4966
+ this._currentBranch = null;
4882
4967
  }
4883
4968
 
4884
4969
  _pick( arg, def ) {
@@ -4904,9 +4989,16 @@ class Panel {
4904
4989
  Panel Widgets
4905
4990
  */
4906
4991
 
4907
- create_widget( name, type, options = {} ) {
4992
+ _createWidget( type, name, value, options = {} ) {
4993
+
4994
+ if( !LX.CREATED_INSTANCES )
4995
+ {
4996
+ LX.CREATED_INSTANCES = [];
4997
+ }
4998
+
4999
+ LX.CREATED_INSTANCES[ type ] = true;
4908
5000
 
4909
- let widget = new Widget( name, type, options );
5001
+ let widget = new Widget( name, type, value, options );
4910
5002
 
4911
5003
  let element = document.createElement( 'div' );
4912
5004
  element.className = "lexwidget";
@@ -4920,7 +5012,7 @@ class Panel {
4920
5012
 
4921
5013
  if( type != Widget.TITLE )
4922
5014
  {
4923
- element.style.width = "calc(100% - " + (this.current_branch || type == Widget.FILE ? 10 : 20) + "px)";
5015
+ element.style.width = "calc(100% - " + (this._currentBranch || type == Widget.FILE || type == Widget.TREE ? 10 : 20) + "px)";
4924
5016
 
4925
5017
  if( options.width )
4926
5018
  {
@@ -4942,7 +5034,7 @@ class Panel {
4942
5034
 
4943
5035
  if( name != undefined )
4944
5036
  {
4945
- if( !(options.hideName ?? false) )
5037
+ if( !( options.hideName ?? false ) )
4946
5038
  {
4947
5039
  let domName = document.createElement( 'div' );
4948
5040
  domName.className = "lexwidgetname";
@@ -4950,10 +5042,10 @@ class Panel {
4950
5042
  {
4951
5043
  domName.classList.add( "float-" + options.justifyName );
4952
5044
  }
4953
- domName.innerHTML = name || "";
5045
+ domName.innerHTML = name;
4954
5046
  domName.title = options.title ?? domName.innerHTML;
4955
5047
  domName.style.width = options.nameWidth || LX.DEFAULT_NAME_WIDTH;
4956
- element.appendChild(domName);
5048
+ element.appendChild( domName );
4957
5049
  element.domName = domName;
4958
5050
 
4959
5051
  // Copy-paste info
@@ -4961,10 +5053,23 @@ class Panel {
4961
5053
  e.preventDefault();
4962
5054
  widget.oncontextmenu( e );
4963
5055
  });
5056
+
5057
+ if( !( options.skipReset ?? false ) && ( value != null ) )
5058
+ {
5059
+ Panel._add_reset_property( domName, function( e ) {
5060
+ widget.set( widget._initialValue, false, e );
5061
+ // Og value, don't show it
5062
+ this.style.display = "none";
5063
+ });
5064
+ }
4964
5065
  }
4965
5066
 
4966
5067
  this.widgets[ name ] = widget;
4967
5068
  }
5069
+ else
5070
+ {
5071
+ options.hideName = true;
5072
+ }
4968
5073
 
4969
5074
  if( options.signal )
4970
5075
  {
@@ -4991,13 +5096,13 @@ class Panel {
4991
5096
  }
4992
5097
  else if( !this.queuedContainer )
4993
5098
  {
4994
- if( this.current_branch )
5099
+ if( this._currentBranch )
4995
5100
  {
4996
5101
  if( !options.skipWidget )
4997
5102
  {
4998
- this.current_branch.widgets.push( widget );
5103
+ this._currentBranch.widgets.push( widget );
4999
5104
  }
5000
- this.current_branch.content.appendChild( el );
5105
+ this._currentBranch.content.appendChild( el );
5001
5106
  }
5002
5107
  else
5003
5108
  {
@@ -5058,30 +5163,32 @@ class Panel {
5058
5163
  options.skipWidget = options.skipWidget ?? true;
5059
5164
  options.skipInlineCount = true;
5060
5165
 
5061
- let widget = this.create_widget(null, Widget.TEXT, options);
5062
- let element = widget.domEl;
5166
+ let widget = this._createWidget( Widget.TEXT, null, null, options );
5167
+ const element = widget.domEl;
5063
5168
  element.className += " lexfilter noname";
5064
5169
 
5065
5170
  let input = document.createElement('input');
5066
5171
  input.className = 'lexinput-filter';
5067
- input.setAttribute("placeholder", options.placeholder);
5172
+ input.setAttribute( "placeholder", options.placeholder );
5068
5173
  input.style.width = "calc( 100% - 17px )";
5069
5174
  input.value = options.filterValue || "";
5070
5175
 
5071
5176
  let searchIcon = document.createElement('a');
5072
5177
  searchIcon.className = "fa-solid fa-magnifying-glass";
5073
- element.appendChild(searchIcon);
5074
- element.appendChild(input);
5178
+ element.appendChild( searchIcon );
5179
+ element.appendChild( input );
5075
5180
 
5076
5181
  input.addEventListener("input", (e) => {
5077
- if(options.callback)
5078
- options.callback(input.value, e);
5182
+ if( options.callback )
5183
+ {
5184
+ options.callback( input.value, e );
5185
+ }
5079
5186
  });
5080
5187
 
5081
5188
  return element;
5082
5189
  }
5083
5190
 
5084
- _searchWidgets(branchName, value) {
5191
+ _searchWidgets( branchName, value ) {
5085
5192
 
5086
5193
  for( let b of this.branches )
5087
5194
  {
@@ -5155,10 +5262,14 @@ class Panel {
5155
5262
  _trigger( event, callback ) {
5156
5263
 
5157
5264
  if( callback )
5265
+ {
5158
5266
  callback.call( this, event.value, event.domEvent, event.name );
5267
+ }
5159
5268
 
5160
5269
  if( this.onevent )
5270
+ {
5161
5271
  this.onevent.call( this, event );
5272
+ }
5162
5273
  }
5163
5274
 
5164
5275
  /**
@@ -5173,7 +5284,7 @@ class Panel {
5173
5284
  return this.branches.find( b => b.name == name );
5174
5285
  }
5175
5286
 
5176
- return this.current_branch;
5287
+ return this._currentBranch;
5177
5288
  }
5178
5289
 
5179
5290
  /**
@@ -5183,9 +5294,9 @@ class Panel {
5183
5294
 
5184
5295
  queue( domEl ) {
5185
5296
 
5186
- if( !domEl && this.current_branch)
5297
+ if( !domEl && this._currentBranch)
5187
5298
  {
5188
- domEl = this.current_branch.root;
5299
+ domEl = this._currentBranch.root;
5189
5300
  }
5190
5301
 
5191
5302
  if( this.queuedContainer )
@@ -5218,12 +5329,14 @@ class Panel {
5218
5329
 
5219
5330
  addBlank( height = 8, width ) {
5220
5331
 
5221
- let widget = this.create_widget(null, Widget.addBlank);
5332
+ let widget = this._createWidget( Widget.BLANK );
5222
5333
  widget.domEl.className += " blank";
5223
5334
  widget.domEl.style.height = height + "px";
5224
5335
 
5225
- if(width)
5336
+ if( width )
5337
+ {
5226
5338
  widget.domEl.style.width = width;
5339
+ }
5227
5340
 
5228
5341
  return widget;
5229
5342
  }
@@ -5241,13 +5354,12 @@ class Panel {
5241
5354
 
5242
5355
  addTitle( name, options = {} ) {
5243
5356
 
5244
- if( !name )
5245
- {
5246
- throw( "Can't create Title without text!" );
5247
- }
5357
+ console.assert( name, "Can't create Title Widget without text!" );
5248
5358
 
5249
- let widget = this.create_widget( null, Widget.TITLE, options );
5250
- let element = widget.domEl;
5359
+ // Note: Titles are not registered in Panel.widgets by now
5360
+ let widget = this._createWidget( Widget.TITLE, null, null, options );
5361
+
5362
+ const element = widget.domEl;
5251
5363
  element.className = "lextitle";
5252
5364
 
5253
5365
  if( options.icon )
@@ -5258,7 +5370,7 @@ class Panel {
5258
5370
  element.appendChild( icon );
5259
5371
  }
5260
5372
 
5261
- let text = document.createElement( "span");
5373
+ let text = document.createElement( "span" );
5262
5374
  text.innerText = name;
5263
5375
  element.appendChild( text );
5264
5376
 
@@ -5284,6 +5396,7 @@ class Panel {
5284
5396
  * @param {String} value Text value
5285
5397
  * @param {Function} callback Callback function on change
5286
5398
  * @param {*} options:
5399
+ * hideName: Don't use name as label [false]
5287
5400
  * disabled: Make the widget disabled [false]
5288
5401
  * required: Make the input required
5289
5402
  * placeholder: Add input placeholder
@@ -5297,33 +5410,43 @@ class Panel {
5297
5410
 
5298
5411
  addText( name, value, callback, options = {} ) {
5299
5412
 
5300
- let widget = this.create_widget( name, Widget.TEXT, options );
5413
+ let widget = this._createWidget( Widget.TEXT, name, String( value ), options );
5301
5414
 
5302
5415
  widget.onGetValue = () => {
5303
- return wValue.value;
5416
+ return value;
5304
5417
  };
5305
5418
 
5306
- widget.onSetValue = ( newValue, skipCallback ) => {
5307
- this.disabled ? wValue.innerText = newValue : wValue.value = newValue;
5308
- Panel._dispatch_event( wValue, "focusout", skipCallback );
5419
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5420
+
5421
+ if( !widget.valid( newValue ) || ( this._lastValueTriggered == newValue ) )
5422
+ {
5423
+ return;
5424
+ }
5425
+
5426
+ this._lastValueTriggered = value = newValue;
5427
+
5428
+ if( options.disabled )
5429
+ {
5430
+ wValue.innerText = newValue;
5431
+ }
5432
+ else
5433
+ {
5434
+ wValue.value = newValue;
5435
+ }
5436
+
5437
+ if( !skipCallback )
5438
+ {
5439
+ this._trigger( new IEvent( name, newValue, event ), callback );
5440
+ }
5309
5441
  };
5310
5442
 
5311
- widget.valid = () => {
5312
- if( wValue.pattern == "" ) { return true; }
5443
+ widget.valid = ( v ) => {
5444
+ if( !v.length || wValue.pattern == "" ) return true;
5313
5445
  const regexp = new RegExp( wValue.pattern );
5314
- return regexp.test( wValue.value );
5446
+ return regexp.test( v );
5315
5447
  };
5316
5448
 
5317
- let element = widget.domEl;
5318
-
5319
- // Add reset functionality
5320
- if( widget.name && !( options.skipReset ?? false ) ) {
5321
- Panel._add_reset_property( element.domName, function() {
5322
- wValue.value = wValue.iValue;
5323
- this.style.display = "none";
5324
- Panel._dispatch_event( wValue, "focusout" );
5325
- } );
5326
- }
5449
+ const element = widget.domEl;
5327
5450
 
5328
5451
  // Add widget value
5329
5452
 
@@ -5346,7 +5469,7 @@ class Panel {
5346
5469
  wValue.type = options.type || "";
5347
5470
  wValue.value = wValue.iValue = value || "";
5348
5471
  wValue.style.width = "100%";
5349
- wValue.style.textAlign = options.float ?? "";
5472
+ wValue.style.textAlign = ( options.float ?? "" );
5350
5473
 
5351
5474
  wValue.setAttribute( "placeholder", options.placeholder ?? "" );
5352
5475
 
@@ -5360,41 +5483,25 @@ class Panel {
5360
5483
  wValue.setAttribute( "pattern", options.pattern );
5361
5484
  }
5362
5485
 
5363
- var resolve = ( function( val, event ) {
5364
-
5365
- if( !widget.valid() || ( this._lastValueTriggered == val ) )
5366
- {
5367
- return;
5368
- }
5369
-
5370
- const skipCallback = event.detail;
5371
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
5372
- if( btn ) btn.style.display = ( val != wValue.iValue ? "block" : "none" );
5373
- if( !skipCallback )
5374
- {
5375
- this._trigger( new IEvent( name, val, event ), callback );
5376
- }
5377
-
5378
- this._lastValueTriggered = val;
5379
-
5380
- }).bind( this );
5486
+ const trigger = options.trigger ?? "default";
5381
5487
 
5382
- const trigger = options.trigger ?? 'default';
5383
-
5384
- if( trigger == 'default' )
5488
+ if( trigger == "default" )
5385
5489
  {
5386
5490
  wValue.addEventListener( "keyup", function( e ){
5387
- if(e.key == 'Enter')
5388
- resolve( e.target.value, e );
5491
+ if( e.key == "Enter" )
5492
+ {
5493
+ wValue.blur();
5494
+ }
5389
5495
  });
5496
+
5390
5497
  wValue.addEventListener( "focusout", function( e ){
5391
- resolve( e.target.value, e );
5498
+ widget.set( e.target.value, false, e );
5392
5499
  });
5393
5500
  }
5394
- else if( trigger == 'input' )
5501
+ else if( trigger == "input" )
5395
5502
  {
5396
5503
  wValue.addEventListener("input", function( e ){
5397
- resolve( e.target.value, e );
5504
+ widget.set( e.target.value, false, e );
5398
5505
  });
5399
5506
  }
5400
5507
 
@@ -5410,14 +5517,17 @@ class Panel {
5410
5517
  container.appendChild( icon );
5411
5518
  }
5412
5519
 
5413
- } else
5520
+ }
5521
+ else
5414
5522
  {
5415
5523
  wValue = document.createElement( options.url ? 'a' : 'div' );
5524
+
5416
5525
  if( options.url )
5417
5526
  {
5418
5527
  wValue.href = options.url;
5419
5528
  wValue.target = "_blank";
5420
5529
  }
5530
+
5421
5531
  const icon = options.warning ? '<i class="fa-solid fa-triangle-exclamation"></i>' : '';
5422
5532
  wValue.innerHTML = ( icon + value ) || "";
5423
5533
  wValue.style.width = "100%";
@@ -5430,7 +5540,8 @@ class Panel {
5430
5540
  element.appendChild( container );
5431
5541
 
5432
5542
  // Remove branch padding and margins
5433
- if( !widget.name )
5543
+ const useNameAsLabel = !( options.hideName ?? false );
5544
+ if( !useNameAsLabel )
5434
5545
  {
5435
5546
  element.className += " noname";
5436
5547
  container.style.width = "100%";
@@ -5444,7 +5555,8 @@ class Panel {
5444
5555
  * @param {String} name Widget name
5445
5556
  * @param {String} value Text Area value
5446
5557
  * @param {Function} callback Callback function on change
5447
- * @param {*} options:
5558
+ * @param {Object} options:
5559
+ * hideName: Don't use name as label [false]
5448
5560
  * disabled: Make the widget disabled [false]
5449
5561
  * placeholder: Add input placeholder
5450
5562
  * trigger: Choose onchange trigger (default, input) [default]
@@ -5456,82 +5568,76 @@ class Panel {
5456
5568
 
5457
5569
  addTextArea( name, value, callback, options = {} ) {
5458
5570
 
5459
- let widget = this.create_widget( name, Widget.TEXTAREA, options );
5571
+ let widget = this._createWidget( Widget.TEXTAREA, name, value, options );
5460
5572
 
5461
5573
  widget.onGetValue = () => {
5462
- return wValue.value;
5463
- };
5464
- widget.onSetValue = ( newValue, skipCallback ) => {
5465
- wValue.value = newValue;
5466
- Panel._dispatch_event( wValue, "focusout", skipCallback );
5574
+ return value;
5467
5575
  };
5468
5576
 
5469
- let element = widget.domEl;
5577
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5470
5578
 
5471
- // Add reset functionality
5472
- if( widget.name && !( options.skipReset ?? false ) ) {
5473
- Panel._add_reset_property( element.domName, function() {
5474
- wValue.value = wValue.iValue;
5475
- this.style.display = "none";
5476
- Panel._dispatch_event( wValue, "focusout" );
5477
- });
5478
- }
5579
+ wValue.value = value = newValue;
5580
+
5581
+ if( !skipCallback )
5582
+ {
5583
+ this._trigger( new IEvent( name, newValue, event ), callback );
5584
+ }
5585
+ };
5586
+
5587
+ const element = widget.domEl;
5479
5588
 
5480
5589
  // Add widget value
5481
5590
 
5482
- let container = document.createElement( 'div' );
5591
+ let container = document.createElement( "div" );
5483
5592
  container.className = "lextextarea";
5484
5593
  container.style.width = options.inputWidth || "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + " )";
5485
5594
  container.style.height = options.height;
5486
5595
  container.style.display = "flex";
5487
5596
 
5488
- let wValue = document.createElement( 'textarea' );
5597
+ let wValue = document.createElement( "textarea" );
5489
5598
  wValue.value = wValue.iValue = value || "";
5490
5599
  wValue.style.width = "100%";
5491
5600
  wValue.style.textAlign = options.float ?? "";
5492
5601
  Object.assign( wValue.style, options.style ?? {} );
5493
5602
 
5494
- if( options.disabled ?? false ) wValue.setAttribute("disabled", true);
5495
- if( options.placeholder ) wValue.setAttribute("placeholder", options.placeholder);
5603
+ if( options.disabled ?? false ) wValue.setAttribute( "disabled", true );
5604
+ if( options.placeholder ) wValue.setAttribute( "placeholder", options.placeholder );
5496
5605
 
5497
- var resolve = (function( val, event ) {
5498
- const skipCallback = event.detail;
5499
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
5500
- if( btn ) btn.style.display = ( val != wValue.iValue ? "block" : "none" );
5501
- if( !skipCallback ) this._trigger( new IEvent( name, val, event ), callback );
5502
- }).bind(this);
5606
+ const trigger = options.trigger ?? "default";
5503
5607
 
5504
- const trigger = options.trigger ?? 'default';
5505
-
5506
- if(trigger == 'default')
5608
+ if( trigger == "default" )
5507
5609
  {
5508
- wValue.addEventListener("keyup", function(e){
5509
- if(e.key == 'Enter')
5510
- resolve(e.target.value, e);
5610
+ wValue.addEventListener("keyup", function( e ) {
5611
+ if( e.key == "Enter" )
5612
+ {
5613
+ wValue.blur();
5614
+ }
5511
5615
  });
5512
- wValue.addEventListener("focusout", function(e){
5513
- resolve(e.target.value, e);
5616
+
5617
+ wValue.addEventListener("focusout", function( e ) {
5618
+ widget.set( e.target.value, false, e );
5514
5619
  });
5515
5620
  }
5516
- else if(trigger == 'input')
5621
+ else if( trigger == "input" )
5517
5622
  {
5518
- wValue.addEventListener("input", function(e){
5519
- resolve(e.target.value, e);
5623
+ wValue.addEventListener("input", function( e ) {
5624
+ widget.set( e.target.value, false, e );
5520
5625
  });
5521
5626
  }
5522
5627
 
5523
- if(options.icon)
5628
+ if( options.icon )
5524
5629
  {
5525
5630
  let icon = document.createElement('a');
5526
5631
  icon.className = "inputicon " + options.icon;
5527
- container.appendChild(icon);
5632
+ container.appendChild( icon );
5528
5633
  }
5529
5634
 
5530
- container.appendChild(wValue);
5531
- element.appendChild(container);
5635
+ container.appendChild( wValue );
5636
+ element.appendChild( container );
5532
5637
 
5533
5638
  // Remove branch padding and margins
5534
- if( !widget.name )
5639
+ const useNameAsLabel = !( options.hideName ?? false );
5640
+ if( !useNameAsLabel )
5535
5641
  {
5536
5642
  element.className += " noname";
5537
5643
  container.style.width = "100%";
@@ -5544,7 +5650,7 @@ class Panel {
5544
5650
  // Update height depending on the content
5545
5651
  wValue.style.height = wValue.scrollHeight + "px";
5546
5652
  }
5547
- }, 10);
5653
+ }, 10 );
5548
5654
 
5549
5655
  return widget;
5550
5656
  }
@@ -5552,6 +5658,7 @@ class Panel {
5552
5658
  /**
5553
5659
  * @method addLabel
5554
5660
  * @param {String} value Information string
5661
+ * @param {Object} options Text options
5555
5662
  */
5556
5663
 
5557
5664
  addLabel( value, options = {} ) {
@@ -5566,6 +5673,7 @@ class Panel {
5566
5673
  * @param {String} value Button name
5567
5674
  * @param {Function} callback Callback function on click
5568
5675
  * @param {*} options:
5676
+ * hideName: Don't use name as label [false]
5569
5677
  * disabled: Make the widget disabled [false]
5570
5678
  * icon: Icon class to show as button value
5571
5679
  * img: Path to image to show as button value
@@ -5574,19 +5682,19 @@ class Panel {
5574
5682
 
5575
5683
  addButton( name, value, callback, options = {} ) {
5576
5684
 
5577
- let widget = this.create_widget( name, Widget.BUTTON, options );
5685
+ let widget = this._createWidget( Widget.BUTTON, name, null, options );
5578
5686
 
5579
5687
  widget.onGetValue = () => {
5580
5688
  return wValue.innerText;
5581
5689
  };
5582
5690
 
5583
- widget.onSetValue = ( newValue, skipCallback ) => {
5691
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5584
5692
  wValue.innerHTML =
5585
- (options.icon ? "<a class='" + options.icon + "'></a>" :
5586
- ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (newValue || "") + "</span>" ));
5693
+ ( options.icon ? "<a class='" + options.icon + "'></a>" :
5694
+ ( options.img ? "<img src='" + options.img + "'>" : "<span>" + ( newValue || "" ) + "</span>" ) );
5587
5695
  };
5588
5696
 
5589
- let element = widget.domEl;
5697
+ const element = widget.domEl;
5590
5698
 
5591
5699
  var wValue = document.createElement( 'button' );
5592
5700
  wValue.title = options.title ?? "";
@@ -5598,22 +5706,22 @@ class Panel {
5598
5706
  }
5599
5707
 
5600
5708
  wValue.innerHTML =
5601
- (options.icon ? "<a class='" + options.icon + "'></a>" :
5602
- ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (value || "") + "</span>" ));
5709
+ ( options.icon ? "<a class='" + options.icon + "'></a>" :
5710
+ ( options.img ? "<img src='" + options.img + "'>" : "<span>" + ( value || "" ) + "</span>" ) );
5603
5711
 
5604
- wValue.style.width = "calc( 100% - " + (options.nameWidth ?? LX.DEFAULT_NAME_WIDTH) + ")";
5712
+ wValue.style.width = "calc( 100% - " + ( options.nameWidth ?? LX.DEFAULT_NAME_WIDTH ) + ")";
5605
5713
 
5606
5714
  if( options.disabled )
5607
5715
  {
5608
5716
  wValue.setAttribute( "disabled", true );
5609
5717
  }
5610
5718
 
5611
- wValue.addEventListener("click", e => {
5719
+ wValue.addEventListener( "click", e => {
5612
5720
  if( options.selectable )
5613
5721
  {
5614
5722
  if( options.parent )
5615
5723
  {
5616
- options.parent.querySelectorAll(".lexbutton.selected").forEach( e => { if(e == wValue) return; e.classList.remove("selected") } );
5724
+ options.parent.querySelectorAll(".lexbutton.selected").forEach( e => { if( e == wValue ) return; e.classList.remove( "selected" ) } );
5617
5725
  }
5618
5726
 
5619
5727
  wValue.classList.toggle('selected');
@@ -5624,8 +5732,9 @@ class Panel {
5624
5732
 
5625
5733
  element.appendChild( wValue );
5626
5734
 
5627
- // Remove branch padding and margins
5628
- if( !widget.name )
5735
+ // Remove branch padding and
5736
+ const useNameAsLabel = !( options.hideName ?? false ) && !( options.icon || options.img );
5737
+ if( !useNameAsLabel )
5629
5738
  {
5630
5739
  wValue.className += " noname";
5631
5740
  wValue.style.width = "100%";
@@ -5639,6 +5748,7 @@ class Panel {
5639
5748
  * @param {String} name Widget name
5640
5749
  * @param {Array} values Each of the {value, callback, selected, disabled} items
5641
5750
  * @param {*} options:
5751
+ * hideName: Don't use name as label [false]
5642
5752
  * float: Justify content (left, center, right) [center]
5643
5753
  * @legacy selected: Selected item by default by value
5644
5754
  * noSelection: Buttons can be clicked, but they are not selectable
@@ -5647,8 +5757,8 @@ class Panel {
5647
5757
 
5648
5758
  addComboButtons( name, values, options = {} ) {
5649
5759
 
5650
- let widget = this.create_widget( name, Widget.BUTTON, options );
5651
- let element = widget.domEl;
5760
+ let widget = this._createWidget( Widget.BUTTONS, name, null, options );
5761
+ const element = widget.domEl;
5652
5762
 
5653
5763
  let that = this;
5654
5764
  let container = document.createElement('div');
@@ -5717,7 +5827,8 @@ class Panel {
5717
5827
  }
5718
5828
 
5719
5829
  // Remove branch padding and margins
5720
- if( !widget.name )
5830
+ const useNameAsLabel = !( options.hideName ?? false );
5831
+ if( !useNameAsLabel )
5721
5832
  {
5722
5833
  element.className += " noname";
5723
5834
  container.style.width = "100%";
@@ -5733,17 +5844,19 @@ class Panel {
5733
5844
  * @method addCard
5734
5845
  * @param {String} name Card Name
5735
5846
  * @param {*} options:
5736
- * title: title if any
5737
- * text: card text if any
5738
- * src: url of the image if any
5847
+ * text: Card text
5848
+ * link: Card link
5849
+ * title: Card dom title
5850
+ * src: url of the image
5739
5851
  * callback (Function): function to call on click
5740
5852
  */
5741
5853
 
5742
5854
  addCard( name, options = {} ) {
5743
5855
 
5744
5856
  options.hideName = true;
5745
- let widget = this.create_widget(name, Widget.CARD, options);
5746
- let element = widget.domEl;
5857
+
5858
+ let widget = this._createWidget( Widget.CARD, name, null, options );
5859
+ const element = widget.domEl;
5747
5860
 
5748
5861
  let container = document.createElement('div');
5749
5862
  container.className = "lexcard";
@@ -5755,7 +5868,7 @@ class Panel {
5755
5868
  img.src = options.img;
5756
5869
  container.appendChild(img);
5757
5870
 
5758
- if(options.link != undefined)
5871
+ if( options.link != undefined )
5759
5872
  {
5760
5873
  img.style.cursor = "pointer";
5761
5874
  img.addEventListener('click', function() {
@@ -5765,29 +5878,30 @@ class Panel {
5765
5878
  }
5766
5879
  }
5767
5880
 
5768
- let name_el = document.createElement('span');
5769
- name_el.innerText = name;
5881
+ let cardNameDom = document.createElement('span');
5882
+ cardNameDom.innerText = name;
5770
5883
 
5771
- if(options.link != undefined)
5884
+ if( options.link != undefined )
5772
5885
  {
5773
- let link_el = document.createElement('a');
5774
- link_el.innerText = name;
5775
- link_el.href = options.link;
5776
- link_el.target = options.target ?? "";
5777
- name_el.innerText = "";
5778
- name_el.appendChild(link_el);
5886
+ let cardLinkDom = document.createElement( 'a' );
5887
+ cardLinkDom.innerText = name;
5888
+ cardLinkDom.href = options.link;
5889
+ cardLinkDom.target = options.target ?? "";
5890
+ cardNameDom.innerText = "";
5891
+ cardNameDom.appendChild( cardLinkDom );
5779
5892
  }
5780
5893
 
5781
- container.appendChild(name_el);
5894
+ container.appendChild( cardNameDom );
5782
5895
 
5783
- if( options.callback ) {
5896
+ if( options.callback )
5897
+ {
5784
5898
  container.style.cursor = "pointer";
5785
- container.addEventListener("click", (e) => {
5786
- this._trigger( new IEvent(name, null, e), options.callback );
5899
+ container.addEventListener("click", ( e ) => {
5900
+ this._trigger( new IEvent( name, null, e ), options.callback );
5787
5901
  });
5788
5902
  }
5789
5903
 
5790
- element.appendChild(container);
5904
+ element.appendChild( container );
5791
5905
 
5792
5906
  return widget;
5793
5907
  }
@@ -5812,13 +5926,13 @@ class Panel {
5812
5926
  // Always hide name for this one
5813
5927
  options.hideName = true;
5814
5928
 
5815
- let widget = this.create_widget( name, Widget.FORM, options );
5929
+ let widget = this._createWidget( Widget.FORM, name, null, options );
5816
5930
 
5817
5931
  widget.onGetValue = () => {
5818
5932
  return container.formData;
5819
5933
  };
5820
5934
 
5821
- widget.onSetValue = ( newValue, skipCallback ) => {
5935
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5822
5936
  container.formData = newValue;
5823
5937
  const entries = container.querySelectorAll( ".lexwidget" );
5824
5938
  for( let i = 0; i < entries.length; ++i )
@@ -5837,7 +5951,7 @@ class Panel {
5837
5951
 
5838
5952
  // Add widget value
5839
5953
 
5840
- let element = widget.domEl;
5954
+ const element = widget.domEl;
5841
5955
 
5842
5956
  let container = document.createElement( 'div' );
5843
5957
  container.className = "lexformdata";
@@ -5891,31 +6005,29 @@ class Panel {
5891
6005
 
5892
6006
  element.appendChild( container );
5893
6007
 
5894
- if( !widget.name || options.hideName )
5895
- {
5896
- element.className += " noname";
5897
- container.style.width = "100%";
5898
- }
6008
+ // Form does not never use label
6009
+ element.className += " noname";
6010
+ container.style.width = "100%";
5899
6011
 
5900
6012
  return widget;
5901
6013
  }
5902
6014
 
5903
6015
  /**
5904
6016
  * @method addContent
6017
+ * @param {String} name Widget name
5905
6018
  * @param {HTMLElement/String} element
6019
+ * @param {Object} options
5906
6020
  */
5907
6021
 
5908
- addContent( element, options = {} ) {
6022
+ addContent( name, element, options = {} ) {
5909
6023
 
5910
- if( !element )
5911
- {
5912
- return;
5913
- }
6024
+ console.assert( element, "Empty content!" );
5914
6025
 
5915
6026
  if( element.constructor == String )
5916
6027
  {
5917
6028
  const tmp = document.createElement( "div" );
5918
6029
  tmp.innerHTML = element;
6030
+
5919
6031
  if( tmp.childElementCount > 1 )
5920
6032
  {
5921
6033
  element = tmp;
@@ -5926,27 +6038,28 @@ class Panel {
5926
6038
  }
5927
6039
  }
5928
6040
 
5929
- let widget = this.create_widget( null, Widget.CONTENT, options );
6041
+ options.hideName = true;
6042
+
6043
+ let widget = this._createWidget( Widget.CONTENT, name, null, options );
5930
6044
  widget.domEl.appendChild( element );
6045
+
5931
6046
  return widget;
5932
6047
  }
5933
6048
 
5934
6049
  /**
5935
6050
  * @method addImage
6051
+ * @param {String} name Widget name
5936
6052
  * @param {String} url Image Url
5937
6053
  * @param {*} options
6054
+ * hideName: Don't use name as label [false]
5938
6055
  */
5939
6056
 
5940
- async addImage( url, options = {} ) {
6057
+ async addImage( name, url, options = {} ) {
5941
6058
 
5942
- if( !url )
5943
- {
5944
- return;
5945
- }
6059
+ console.assert( url, "Empty src/url for Image!" );
5946
6060
 
5947
- options.hideName = true;
5948
- let widget = this.create_widget( null, Widget.IMAGE, options );
5949
- let element = widget.domEl;
6061
+ let widget = this._createWidget( Widget.IMAGE, name, null, options );
6062
+ const element = widget.domEl;
5950
6063
 
5951
6064
  let container = document.createElement( 'div' );
5952
6065
  container.className = "leximage";
@@ -5961,6 +6074,7 @@ class Panel {
5961
6074
  }
5962
6075
 
5963
6076
  await img.decode();
6077
+
5964
6078
  container.appendChild( img );
5965
6079
  element.appendChild( container );
5966
6080
 
@@ -5974,6 +6088,7 @@ class Panel {
5974
6088
  * @param {String} value Select by default option
5975
6089
  * @param {Function} callback Callback function on change
5976
6090
  * @param {*} options:
6091
+ * hideName: Don't use name as label [false]
5977
6092
  * filter: Add a search bar to the widget [false]
5978
6093
  * disabled: Make the widget disabled [false]
5979
6094
  * skipReset: Don't add the reset value button when value changes
@@ -5983,33 +6098,24 @@ class Panel {
5983
6098
 
5984
6099
  addDropdown( name, values, value, callback, options = {} ) {
5985
6100
 
5986
- let widget = this.create_widget( name, Widget.DROPDOWN, options );
6101
+ let widget = this._createWidget( Widget.DROPDOWN, name, value, options );
5987
6102
 
5988
6103
  widget.onGetValue = () => {
5989
6104
  return element.querySelector( "li.selected" ).getAttribute( 'value' );
5990
6105
  };
5991
6106
 
5992
- widget.onSetValue = ( newValue, skipCallback ) => {
5993
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
5994
- if( btn ) btn.style.display = ( newValue != wValue.iValue ? "block" : "none" );
6107
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5995
6108
  value = newValue;
5996
6109
  list.querySelectorAll( 'li' ).forEach( e => { if( e.getAttribute('value') == value ) e.click() } );
5997
- if( !skipCallback ) this._trigger( new IEvent( name, value, null ), callback );
6110
+ if( !skipCallback )
6111
+ {
6112
+ this._trigger( new IEvent( name, value, event ), callback );
6113
+ }
5998
6114
  };
5999
6115
 
6000
- let element = widget.domEl;
6116
+ const element = widget.domEl;
6001
6117
  let that = this;
6002
6118
 
6003
- // Add reset functionality
6004
- if(widget.name && !( options.skipReset ?? false ))
6005
- {
6006
- Panel._add_reset_property( element.domName, function() {
6007
- value = wValue.iValue;
6008
- list.querySelectorAll( 'li' ).forEach( e => { if( e.getAttribute('value') == value ) e.click() } );
6009
- this.style.display = "none";
6010
- });
6011
- }
6012
-
6013
6119
  let container = document.createElement( 'div' );
6014
6120
  container.className = "lexdropdown";
6015
6121
  container.style.width = options.inputWidth || "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
@@ -6218,16 +6324,19 @@ class Panel {
6218
6324
 
6219
6325
  li.addEventListener( "click", e => {
6220
6326
  listDialog.close();
6327
+
6221
6328
  const currentSelected = element.querySelector( ".lexoptions .selected" );
6222
- if(currentSelected) currentSelected.classList.remove( "selected" );
6329
+ if(currentSelected)
6330
+ {
6331
+ currentSelected.classList.remove( "selected" );
6332
+ }
6333
+
6223
6334
  value = e.currentTarget.getAttribute( "value" );
6224
6335
  e.currentTarget.toggleAttribute( "hidden", false );
6225
6336
  e.currentTarget.classList.add( "selected" );
6226
- selectedOption.refresh(value);
6337
+ selectedOption.refresh( value );
6227
6338
 
6228
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6229
- if( btn ) btn.style.display = (value != wValue.iValue ? "block" : "none");
6230
- that._trigger( new IEvent( name, value, null ), callback );
6339
+ widget.set( value, false, e );
6231
6340
 
6232
6341
  // Reset filter
6233
6342
  if( filter )
@@ -6284,7 +6393,8 @@ class Panel {
6284
6393
  element.appendChild( container );
6285
6394
 
6286
6395
  // Remove branch padding and margins
6287
- if( !widget.name )
6396
+ const useNameAsLabel = !( options.hideName ?? false );
6397
+ if( !useNameAsLabel )
6288
6398
  {
6289
6399
  element.className += " noname";
6290
6400
  container.style.width = "100%";
@@ -6311,39 +6421,25 @@ class Panel {
6311
6421
 
6312
6422
  addCurve( name, values, callback, options = {} ) {
6313
6423
 
6314
- if( !name )
6315
- {
6316
- throw( "Set Widget Name!" );
6317
- }
6424
+ let defaultValues = JSON.parse( JSON.stringify( values ) );
6318
6425
 
6319
6426
  let that = this;
6320
- let widget = this.create_widget( name, Widget.CURVE, options );
6427
+ let widget = this._createWidget( Widget.CURVE, name, defaultValues, options );
6321
6428
 
6322
6429
  widget.onGetValue = () => {
6323
6430
  return JSON.parse(JSON.stringify( curveInstance.element.value ));
6324
6431
  };
6325
6432
 
6326
- widget.onSetValue = ( newValue, skipCallback ) => {
6327
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6328
- if( btn ) btn.style.display = ( newValue != curveInstance.element.value ? "block" : "none" );
6433
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6329
6434
  curveInstance.element.value = JSON.parse( JSON.stringify( newValue ) );
6330
6435
  curveInstance.redraw();
6331
- if( !skipCallback ) that._trigger( new IEvent( name, curveInstance.element.value, null ), callback );
6436
+ if( !skipCallback )
6437
+ {
6438
+ that._trigger( new IEvent( name, curveInstance.element.value, event ), callback );
6439
+ }
6332
6440
  };
6333
6441
 
6334
- let element = widget.domEl;
6335
- let defaultValues = JSON.parse( JSON.stringify( values ) );
6336
-
6337
- // Add reset functionality
6338
- if( !(options.skipReset ?? false) )
6339
- {
6340
- Panel._add_reset_property(element.domName, function(e) {
6341
- this.style.display = "none";
6342
- curveInstance.element.value = JSON.parse( JSON.stringify( defaultValues ) );
6343
- curveInstance.redraw();
6344
- that._trigger( new IEvent( name, curveInstance.element.value, e ), callback );
6345
- });
6346
- }
6442
+ const element = widget.domEl;
6347
6443
 
6348
6444
  // Add widget value
6349
6445
 
@@ -6352,9 +6448,7 @@ class Panel {
6352
6448
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
6353
6449
 
6354
6450
  options.callback = (v, e) => {
6355
- let btn = element.querySelector(".lexwidgetname .lexicon");
6356
- if(btn) btn.style.display = (v != defaultValues ? "block" : "none");
6357
- that._trigger( new IEvent(name, v, e), callback );
6451
+ that._trigger( new IEvent( name, v, e ), callback );
6358
6452
  };
6359
6453
 
6360
6454
  options.name = name;
@@ -6393,34 +6487,25 @@ class Panel {
6393
6487
 
6394
6488
  addDial( name, values, callback, options = {} ) {
6395
6489
 
6490
+ let defaultValues = JSON.parse( JSON.stringify( values ) );
6491
+
6396
6492
  let that = this;
6397
- let widget = this.create_widget(name, Widget.DIAL, options);
6493
+ let widget = this._createWidget( Widget.DIAL, name, defaultValues, options );
6398
6494
 
6399
6495
  widget.onGetValue = () => {
6400
- return JSON.parse(JSON.stringify(curveInstance.element.value));
6496
+ return JSON.parse( JSON.stringify( curveInstance.element.value ) );
6401
6497
  };
6402
6498
 
6403
- widget.onSetValue = ( newValue, skipCallback ) => {
6404
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6405
- if( btn ) btn.style.display = ( newValue != curveInstance.element.value ? "block" : "none" );
6499
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6406
6500
  curveInstance.element.value = JSON.parse( JSON.stringify( newValue ) );
6407
6501
  curveInstance.redraw();
6408
- if( !skipCallback ) that._trigger( new IEvent( name, curveInstance.element.value, null ), callback );
6502
+ if( !skipCallback )
6503
+ {
6504
+ that._trigger( new IEvent( name, curveInstance.element.value, event ), callback );
6505
+ }
6409
6506
  };
6410
6507
 
6411
- let element = widget.domEl;
6412
- let defaultValues = JSON.parse( JSON.stringify( values ) );
6413
-
6414
- // Add reset functionality
6415
- if( widget.name && !(options.skipReset ?? false) )
6416
- {
6417
- Panel._add_reset_property(element.domName, function(e) {
6418
- this.style.display = "none";
6419
- curveInstance.element.value = JSON.parse( JSON.stringify( defaultValues ) );
6420
- curveInstance.redraw();
6421
- that._trigger( new IEvent( name, curveInstance.element.value, e ), callback );
6422
- });
6423
- }
6508
+ const element = widget.domEl;
6424
6509
 
6425
6510
  // Add widget value
6426
6511
 
@@ -6428,10 +6513,8 @@ class Panel {
6428
6513
  container.className = "lexcurve";
6429
6514
  container.style.width = widget.name ? "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")" : '100%';
6430
6515
 
6431
- options.callback = (v, e) => {
6432
- let btn = element.querySelector(".lexwidgetname .lexicon");
6433
- if(btn) btn.style.display = (v != defaultValues ? "block" : "none");
6434
- that._trigger( new IEvent(name, v, e), callback );
6516
+ options.callback = ( v, e ) => {
6517
+ that._trigger( new IEvent( name, v, e ), callback );
6435
6518
  };
6436
6519
 
6437
6520
  options.name = name;
@@ -6460,42 +6543,32 @@ class Panel {
6460
6543
  * @param {String} name Widget name
6461
6544
  * @param {Number} value Flag value by default option
6462
6545
  * @param {Function} callback Callback function on change
6463
- * @param {*} options:
6546
+ * @param {Object} options:
6464
6547
  */
6465
6548
 
6466
6549
  addLayers( name, value, callback, options = {} ) {
6467
6550
 
6468
- if( !name )
6469
- {
6470
- throw("Set Widget Name!");
6471
- }
6472
-
6473
6551
  let that = this;
6474
- let widget = this.create_widget(name, Widget.LAYERS, options);
6552
+ let widget = this._createWidget( Widget.LAYERS, name, value, options );
6553
+
6475
6554
  widget.onGetValue = () => {
6476
6555
  return element.value;
6477
6556
  };
6478
- widget.onSetValue = ( newValue, skipCallback ) => {
6479
- let btn = element.querySelector(".lexwidgetname .lexicon");
6480
- if(btn) btn.style.display = (newValue != defaultValue ? "block" : "none");
6557
+
6558
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6481
6559
  value = element.value = newValue;
6482
6560
  setLayers();
6483
- if( !skipCallback ) that._trigger( new IEvent(name, value), callback );
6561
+ if( !skipCallback )
6562
+ {
6563
+ that._trigger( new IEvent(name, value, event), callback );
6564
+ }
6484
6565
  };
6485
6566
 
6486
- let element = widget.domEl;
6487
-
6488
- // Add reset functionality
6489
- Panel._add_reset_property(element.domName, function(e) {
6490
- this.style.display = "none";
6491
- value = element.value = defaultValue;
6492
- setLayers();
6493
- that._trigger( new IEvent(name, value, e), callback );
6494
- });
6567
+ const element = widget.domEl;
6495
6568
 
6496
6569
  // Add widget value
6497
6570
 
6498
- var container = document.createElement('div');
6571
+ var container = document.createElement( "div" );
6499
6572
  container.className = "lexlayers";
6500
6573
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
6501
6574
 
@@ -6507,6 +6580,7 @@ class Panel {
6507
6580
 
6508
6581
  let binary = value.toString( 2 );
6509
6582
  let nbits = binary.length;
6583
+
6510
6584
  // fill zeros
6511
6585
  for( var i = 0; i < (16 - nbits); ++i )
6512
6586
  {
@@ -6520,33 +6594,28 @@ class Panel {
6520
6594
  if( value != undefined )
6521
6595
  {
6522
6596
  const valueBit = binary[ 16 - bit - 1 ];
6523
- if(valueBit != undefined && valueBit == '1')
6597
+ if( valueBit != undefined && valueBit == '1' )
6598
+ {
6524
6599
  layer.classList.add('selected');
6600
+ }
6525
6601
  }
6526
6602
  layer.innerText = bit + 1;
6527
6603
  layer.title = "Bit " + bit + ", value " + (1 << bit);
6528
6604
  container.appendChild( layer );
6529
6605
 
6530
6606
  layer.addEventListener("click", e => {
6531
-
6532
6607
  e.stopPropagation();
6533
6608
  e.stopImmediatePropagation();
6534
6609
  e.target.classList.toggle('selected');
6535
6610
  value ^= ( 1 << bit );
6536
- element.value = value;
6537
-
6538
- let btn = element.querySelector(".lexwidgetname .lexicon");
6539
- if(btn) btn.style.display = (value != defaultValue ? "block" : "none");
6540
-
6541
- this._trigger( new IEvent(name, value, e), callback );
6611
+ widget.set( value, false, e );
6542
6612
  });
6543
6613
  }
6544
-
6545
6614
  };
6546
6615
 
6547
6616
  setLayers();
6548
6617
 
6549
- element.appendChild(container);
6618
+ element.appendChild( container );
6550
6619
 
6551
6620
  return widget;
6552
6621
  }
@@ -6562,12 +6631,8 @@ class Panel {
6562
6631
 
6563
6632
  addArray( name, values = [], callback, options = {} ) {
6564
6633
 
6565
- if( !name )
6566
- {
6567
- throw( "Set Widget Name!" );
6568
- }
6634
+ let widget = this._createWidget( Widget.ARRAY, name, null, options );
6569
6635
 
6570
- let widget = this.create_widget(name, Widget.ARRAY, options);
6571
6636
  widget.onGetValue = () => {
6572
6637
  let array_inputs = element.querySelectorAll("input");
6573
6638
  let values = [];
@@ -6575,13 +6640,17 @@ class Panel {
6575
6640
  values.push( v.value );
6576
6641
  return values;
6577
6642
  };
6578
- widget.onSetValue = ( newValue, skipCallback ) => {
6643
+
6644
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6579
6645
  values = newValue;
6580
6646
  updateItems();
6581
- if( !skipCallback ) this._trigger( new IEvent(name, values, null), callback );
6647
+ if( !skipCallback )
6648
+ {
6649
+ this._trigger( new IEvent( name, values, event ), callback );
6650
+ }
6582
6651
  };
6583
6652
 
6584
- let element = widget.domEl;
6653
+ const element = widget.domEl;
6585
6654
  element.style.flexWrap = "wrap";
6586
6655
 
6587
6656
  // Add dropdown array button
@@ -6594,10 +6663,10 @@ class Panel {
6594
6663
 
6595
6664
  this.queue( container );
6596
6665
 
6597
- const angle_down = `<a class='fa-solid fa-angle-down' style='float:right; margin-right: 3px;'></a>`;
6666
+ const angleDown = `<a class='fa-solid fa-angle-down' style='float:right; margin-right: 3px;'></a>`;
6598
6667
 
6599
6668
  let buttonName = "Array (size " + values.length + ")";
6600
- buttonName += angle_down;
6669
+ buttonName += angleDown;
6601
6670
  this.addButton(null, buttonName, () => {
6602
6671
  element.querySelector(".lexarrayitems").toggleAttribute('hidden');
6603
6672
  }, { buttonClass: 'array' });
@@ -6606,24 +6675,24 @@ class Panel {
6606
6675
 
6607
6676
  // Show elements
6608
6677
 
6609
- let array_items = document.createElement('div');
6610
- array_items.className = "lexarrayitems";
6611
- array_items.toggleAttribute('hidden', true);
6678
+ let arrayItems = document.createElement( "div" );
6679
+ arrayItems.className = "lexarrayitems";
6680
+ arrayItems.toggleAttribute( "hidden", true );
6612
6681
 
6613
- element.appendChild(container);
6614
- element.appendChild(array_items);
6682
+ element.appendChild( container );
6683
+ element.appendChild( arrayItems );
6615
6684
 
6616
6685
  const updateItems = () => {
6617
6686
 
6618
6687
  // Update num items
6619
6688
  let buttonEl = element.querySelector(".lexbutton.array span");
6620
6689
  buttonEl.innerHTML = "Array (size " + values.length + ")";
6621
- buttonEl.innerHTML += angle_down;
6690
+ buttonEl.innerHTML += angleDown;
6622
6691
 
6623
6692
  // Update inputs
6624
- array_items.innerHTML = "";
6693
+ arrayItems.innerHTML = "";
6625
6694
 
6626
- this.queue( array_items );
6695
+ this.queue( arrayItems );
6627
6696
 
6628
6697
  for( let i = 0; i < values.length; ++i )
6629
6698
  {
@@ -6669,7 +6738,7 @@ class Panel {
6669
6738
  this._trigger( new IEvent(name, values, event), callback );
6670
6739
  }, { buttonClass: 'array' });
6671
6740
 
6672
- // Stop pushing to array_items
6741
+ // Stop pushing to arrayItems
6673
6742
  this.clearQueue();
6674
6743
  };
6675
6744
 
@@ -6685,22 +6754,30 @@ class Panel {
6685
6754
  * @param {String} value Selected list value
6686
6755
  * @param {Function} callback Callback function on change
6687
6756
  * @param {*} options:
6757
+ * hideName: Don't use name as label [false]
6688
6758
  */
6689
6759
 
6690
6760
  addList( name, values, value, callback, options = {} ) {
6691
6761
 
6692
- let widget = this.create_widget( name, Widget.LIST, options );
6762
+ let widget = this._createWidget( Widget.LIST, name, value, options );
6693
6763
 
6694
6764
  widget.onGetValue = () => {
6695
6765
  return value;
6696
6766
  };
6697
- widget.onSetValue = ( newValue, skipCallback ) => {
6767
+
6768
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6698
6769
  listContainer.querySelectorAll( '.lexlistitem' ).forEach( e => e.classList.remove( 'selected' ) );
6699
6770
  const idx = values.indexOf( newValue );
6700
- if( idx == -1 ) return;
6771
+ if( idx == -1 )
6772
+ {
6773
+ return;
6774
+ }
6701
6775
  listContainer.children[ idx ].classList.toggle( 'selected' );
6702
6776
  value = newValue;
6703
- if( !skipCallback ) this._trigger( new IEvent( name, newValue ), callback );
6777
+ if( !skipCallback )
6778
+ {
6779
+ this._trigger( new IEvent( name, newValue, event ), callback );
6780
+ }
6704
6781
  };
6705
6782
 
6706
6783
  widget.updateValues = ( newValues ) => {
@@ -6734,7 +6811,7 @@ class Panel {
6734
6811
  }
6735
6812
  };
6736
6813
 
6737
- let element = widget.domEl;
6814
+ const element = widget.domEl;
6738
6815
 
6739
6816
  // Show list
6740
6817
 
@@ -6745,7 +6822,8 @@ class Panel {
6745
6822
  widget.updateValues( values );
6746
6823
 
6747
6824
  // Remove branch padding and margins
6748
- if( !widget.name )
6825
+ const useNameAsLabel = !( options.hideName ?? false );
6826
+ if( !useNameAsLabel )
6749
6827
  {
6750
6828
  element.className += " noname";
6751
6829
  listContainer.style.width = "100%";
@@ -6762,46 +6840,39 @@ class Panel {
6762
6840
  * @param {String} value Comma separated tags
6763
6841
  * @param {Function} callback Callback function on change
6764
6842
  * @param {*} options:
6843
+ * hideName: Don't use name as label [false]
6765
6844
  */
6766
6845
 
6767
6846
  addTags( name, value, callback, options = {} ) {
6768
6847
 
6769
6848
  value = value.replace( /\s/g, '' ).split( ',' );
6849
+
6770
6850
  let defaultValue = [].concat( value );
6771
- let widget = this.create_widget( name, Widget.TAGS, options );
6851
+ let widget = this._createWidget( Widget.TAGS, name, defaultValue, options );
6772
6852
 
6773
6853
  widget.onGetValue = () => {
6774
6854
  return [].concat( value );
6775
6855
  };
6776
- widget.onSetValue = ( newValue, skipCallback ) => {
6856
+
6857
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6777
6858
  value = [].concat( newValue );
6778
- create_tags();
6779
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6780
- if( btn ) btn.style.display = ( newValue != defaultValue ? "block" : "none" );
6781
- if( !skipCallback ) that._trigger( new IEvent( name, value ), callback );
6859
+ _generateTags();
6860
+ if( !skipCallback )
6861
+ {
6862
+ that._trigger( new IEvent( name, value, event ), callback );
6863
+ }
6782
6864
  };
6783
6865
 
6784
- let element = widget.domEl;
6866
+ const element = widget.domEl;
6785
6867
  let that = this;
6786
6868
 
6787
- // Add reset functionality
6788
- if(widget.name)
6789
- {
6790
- Panel._add_reset_property(element.domName, function(e) {
6791
- this.style.display = "none";
6792
- value = [].concat(defaultValue);
6793
- create_tags();
6794
- that._trigger( new IEvent(name, value, e), callback );
6795
- });
6796
- }
6797
-
6798
6869
  // Show tags
6799
6870
 
6800
6871
  const tagsContainer = document.createElement('div');
6801
6872
  tagsContainer.className = "lextags";
6802
6873
  tagsContainer.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
6803
6874
 
6804
- const create_tags = () => {
6875
+ const _generateTags = () => {
6805
6876
 
6806
6877
  tagsContainer.innerHTML = "";
6807
6878
 
@@ -6819,9 +6890,7 @@ class Panel {
6819
6890
  removeButton.addEventListener( 'click', e => {
6820
6891
  tag.remove();
6821
6892
  value.splice( value.indexOf( tagName ), 1 );
6822
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6823
- if( btn ) btn.style.display = ( value != defaultValue ? "block" : "none" );
6824
- that._trigger( new IEvent( name, value, e ), callback );
6893
+ widget.set( value, false, e );
6825
6894
  } );
6826
6895
 
6827
6896
  tagsContainer.appendChild( tag );
@@ -6840,20 +6909,18 @@ class Panel {
6840
6909
  if( !val.length || value.indexOf( val ) > -1 )
6841
6910
  return;
6842
6911
  value.push( val );
6843
- create_tags();
6844
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6845
- if(btn) btn.style.display = "block";
6846
- that._trigger( new IEvent( name, value, e ), callback );
6912
+ widget.set( value, false, e );
6847
6913
  }
6848
6914
  };
6849
6915
 
6850
6916
  tagInput.focus();
6851
6917
  }
6852
6918
 
6853
- create_tags();
6919
+ _generateTags();
6854
6920
 
6855
6921
  // Remove branch padding and margins
6856
- if( !widget.name )
6922
+ const useNameAsLabel = !( options.hideName ?? false );
6923
+ if( !useNameAsLabel )
6857
6924
  {
6858
6925
  element.className += " noname";
6859
6926
  tagsContainer.style.width = "100%";
@@ -6883,44 +6950,44 @@ class Panel {
6883
6950
  throw( "Set Widget Name or at least a label!" );
6884
6951
  }
6885
6952
 
6886
- let widget = this.create_widget( name, Widget.CHECKBOX, options );
6953
+ let widget = this._createWidget( Widget.CHECKBOX, name, value, options );
6887
6954
 
6888
6955
  widget.onGetValue = () => {
6889
- return checkbox.checked;
6956
+ return value;
6890
6957
  };
6891
6958
 
6892
- widget.onSetValue = ( newValue, skipCallback ) => {
6893
- if( checkbox.checked !== newValue )
6959
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6960
+
6961
+ if( newValue == value )
6894
6962
  {
6895
- checkbox.checked = newValue;
6896
- Panel._dispatch_event( checkbox, "change", skipCallback );
6963
+ return;
6897
6964
  }
6898
- };
6899
6965
 
6900
- let element = widget.domEl;
6966
+ checkbox.checked = value = newValue;
6901
6967
 
6902
- // Add reset functionality
6903
- if( name )
6904
- {
6905
- Panel._add_reset_property( element.domName, function() {
6906
- checkbox.checked = !checkbox.checked;
6907
- Panel._dispatch_event( checkbox, "change" );
6908
- });
6909
- }
6968
+ // Update suboptions menu
6969
+ element.querySelector( ".lexcheckboxsubmenu" )?.toggleAttribute( 'hidden', !newValue );
6970
+
6971
+ if( !skipCallback )
6972
+ {
6973
+ this._trigger( new IEvent( name, newValue, event ), callback );
6974
+ }
6975
+ };
6976
+
6977
+ const element = widget.domEl;
6910
6978
 
6911
6979
  // Add widget value
6912
6980
 
6913
- var container = document.createElement('div');
6981
+ var container = document.createElement( "div" );
6914
6982
  container.className = "lexcheckboxcont";
6915
6983
 
6916
- let checkbox = document.createElement('input');
6984
+ let checkbox = document.createElement( "input" );
6917
6985
  checkbox.type = "checkbox";
6918
6986
  checkbox.className = "lexcheckbox " + ( options.className ?? "" );
6919
6987
  checkbox.checked = value;
6920
- checkbox.iValue = value;
6921
6988
  checkbox.disabled = options.disabled ?? false;
6922
6989
 
6923
- let valueName = document.createElement( 'span' );
6990
+ let valueName = document.createElement( "span" );
6924
6991
  valueName.className = "checkboxtext";
6925
6992
  valueName.innerHTML = options.label ?? "On";
6926
6993
 
@@ -6928,21 +6995,7 @@ class Panel {
6928
6995
  container.appendChild( valueName );
6929
6996
 
6930
6997
  checkbox.addEventListener( "change" , e => {
6931
-
6932
- const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
6933
-
6934
- // Reset button (default value)
6935
- if( !skipCallback )
6936
- {
6937
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6938
- if( btn ) btn.style.display = checkbox.checked != checkbox.iValue ? "block": "none";
6939
- }
6940
-
6941
- // Open suboptions
6942
- let submenu = element.querySelector( ".lexcheckboxsubmenu" );
6943
- if( submenu ) submenu.toggleAttribute( 'hidden', !checkbox.checked );
6944
-
6945
- if( !skipCallback ) this._trigger( new IEvent( name, checkbox.checked, e ), callback );
6998
+ widget.set( checkbox.checked, false, e );
6946
6999
  });
6947
7000
 
6948
7001
  element.appendChild( container );
@@ -6950,9 +7003,9 @@ class Panel {
6950
7003
  if( options.suboptions )
6951
7004
  {
6952
7005
  element.style.flexWrap = "wrap";
6953
- let suboptions = document.createElement('div');
7006
+ let suboptions = document.createElement( "div" );
6954
7007
  suboptions.className = "lexcheckboxsubmenu";
6955
- suboptions.toggleAttribute( 'hidden', !checkbox.checked );
7008
+ suboptions.toggleAttribute( "hidden", !checkbox.checked );
6956
7009
 
6957
7010
  this.queue( suboptions );
6958
7011
  options.suboptions.call(this, this);
@@ -6977,32 +7030,31 @@ class Panel {
6977
7030
 
6978
7031
  addToggle( name, value, callback, options = {} ) {
6979
7032
 
6980
- if( !name )
6981
- {
6982
- throw( "Set Widget Name!" );
6983
- }
6984
-
6985
- let widget = this.create_widget( name, Widget.TOGGLE, options );
7033
+ let widget = this._createWidget( Widget.TOGGLE, name, value, options );
6986
7034
 
6987
7035
  widget.onGetValue = () => {
6988
7036
  return toggle.checked;
6989
7037
  };
6990
7038
 
6991
- widget.onSetValue = ( newValue, skipCallback ) => {
6992
- if( toggle.checked !== newValue )
7039
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7040
+
7041
+ if( newValue == value )
6993
7042
  {
6994
- toggle.checked = newValue;
6995
- Panel._dispatch_event( toggle, "change", skipCallback );
7043
+ return;
6996
7044
  }
6997
- };
6998
7045
 
6999
- let element = widget.domEl;
7046
+ toggle.checked = value = newValue;
7000
7047
 
7001
- // Add reset functionality
7002
- Panel._add_reset_property( element.domName, function() {
7003
- toggle.checked = !toggle.checked;
7004
- Panel._dispatch_event( toggle, "change" );
7005
- });
7048
+ // Update suboptions menu
7049
+ element.querySelector( ".lextogglesubmenu" )?.toggleAttribute( 'hidden', !newValue );
7050
+
7051
+ if( !skipCallback )
7052
+ {
7053
+ this._trigger( new IEvent( name, newValue, event ), callback );
7054
+ }
7055
+ };
7056
+
7057
+ const element = widget.domEl;
7006
7058
 
7007
7059
  // Add widget value
7008
7060
 
@@ -7024,21 +7076,7 @@ class Panel {
7024
7076
  container.appendChild( valueName );
7025
7077
 
7026
7078
  toggle.addEventListener( "change" , e => {
7027
-
7028
- const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
7029
-
7030
- // Reset button (default value)
7031
- if( !skipCallback )
7032
- {
7033
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7034
- if( btn ) btn.style.display = toggle.checked != toggle.iValue ? "block": "none";
7035
- }
7036
-
7037
- // Open suboptions
7038
- let submenu = element.querySelector( ".lextogglesubmenu" );
7039
- if( submenu ) submenu.toggleAttribute( 'hidden', !toggle.checked );
7040
-
7041
- if( !skipCallback ) this._trigger( new IEvent( name, toggle.checked, e ), callback );
7079
+ widget.set( toggle.checked, false, e );
7042
7080
  });
7043
7081
 
7044
7082
  element.appendChild( container );
@@ -7062,43 +7100,43 @@ class Panel {
7062
7100
 
7063
7101
  /**
7064
7102
  * @method addRadioGroup
7103
+ * @param {String} name Widget name
7065
7104
  * @param {String} label Radio label
7066
7105
  * @param {Array} values Radio options
7067
7106
  * @param {Function} callback Callback function on change
7068
7107
  * @param {*} options:
7069
7108
  * disabled: Make the widget disabled [false]
7070
7109
  * className: Customize colors
7110
+ * selected: Index of the default selected option
7071
7111
  */
7072
7112
 
7073
- addRadioGroup( label, values, callback, options = {} ) {
7113
+ addRadioGroup( name, label, values, callback, options = {} ) {
7074
7114
 
7075
- let widget = this.create_widget( null, Widget.RADIO, options );
7115
+ let widget = this._createWidget( Widget.RADIO, name, null, options );
7116
+ let currentIndex = null;
7076
7117
 
7077
7118
  widget.onGetValue = () => {
7078
7119
  const items = container.querySelectorAll( 'button' );
7079
- for( let i = 0; i < items.length; ++i )
7080
- {
7081
- const optionItem = items[ i ];
7082
- if( optionItem.checked )
7083
- {
7084
- return [ i, values[ i ] ];
7085
- }
7086
- }
7120
+ return currentIndex ? [ currentIndex, items[ currentIndex ] ] : undefined;
7087
7121
  };
7088
7122
 
7089
- widget.onSetValue = ( newValue, skipCallback ) => {
7123
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7124
+ console.assert( newValue.constructor == Number, "RadioGroup _value_ must be an Array index!" );
7125
+
7090
7126
  const items = container.querySelectorAll( 'button' );
7091
- for( let i = 0; i < items.length; ++i )
7127
+ items.forEach( b => { b.checked = false; b.classList.remove( "checked" ) } );
7128
+
7129
+ const optionItem = items[ newValue ];
7130
+ optionItem.checked = !optionItem.checked;
7131
+ optionItem.classList.toggle( "checked" );
7132
+
7133
+ if( !skipCallback )
7092
7134
  {
7093
- const optionItem = items[ i ];
7094
- if( newValue == i )
7095
- {
7096
- Panel._dispatch_event( optionItem, "click", skipCallback );
7097
- }
7135
+ that._trigger( new IEvent( null, [ newValue, values[ newValue ] ], event ), callback );
7098
7136
  }
7099
7137
  };
7100
7138
 
7101
- let element = widget.domEl;
7139
+ const element = widget.domEl;
7102
7140
 
7103
7141
  // Add widget value
7104
7142
  var container = document.createElement( 'div' );
@@ -7121,18 +7159,12 @@ class Panel {
7121
7159
  optionButton.disabled = options.disabled ?? false;
7122
7160
  optionItem.appendChild( optionButton );
7123
7161
 
7124
- optionButton.addEventListener( "click", function( e ) {
7125
- const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
7126
- container.querySelectorAll( 'button' ).forEach( e => { e.checked = false; e.classList.remove( "checked" ) } );
7127
- this.checked = !this.checked;
7128
- this.classList.toggle( "checked" );
7129
- if( !skipCallback ) that._trigger( new IEvent( null, [ i, values[ i ] ], e ), callback );
7162
+ optionButton.addEventListener( "click", ( e ) => {
7163
+ widget.set( i, false, e );
7130
7164
  } );
7131
7165
 
7132
- {
7133
- const checkedSpan = document.createElement( 'span' );
7134
- optionButton.appendChild( checkedSpan );
7135
- }
7166
+ const checkedSpan = document.createElement( 'span' );
7167
+ optionButton.appendChild( checkedSpan );
7136
7168
 
7137
7169
  const optionLabel = document.createElement( 'span' );
7138
7170
  optionLabel.innerHTML = values[ i ];
@@ -7141,8 +7173,9 @@ class Panel {
7141
7173
 
7142
7174
  if( options.selected )
7143
7175
  {
7144
- console.assert( options.selected.constructor == Number );
7145
- widget.set( options.selected, true );
7176
+ console.assert( options.selected.constructor == Number, "RadioGroup _selected_ must be an Array index!" );
7177
+ currentIndex = options.selected;
7178
+ widget.set( currentIndex, true );
7146
7179
  }
7147
7180
 
7148
7181
  element.appendChild( container );
@@ -7162,30 +7195,36 @@ class Panel {
7162
7195
 
7163
7196
  addColor( name, value, callback, options = {} ) {
7164
7197
 
7165
- if( !name )
7166
- {
7167
- throw( "Set Widget Name!" );
7168
- }
7198
+ value = ( value.constructor === Array ) ? rgbToHex( value ) : value;
7169
7199
 
7170
- let widget = this.create_widget( name, Widget.COLOR, options );
7200
+ let widget = this._createWidget( Widget.COLOR, name, value, options );
7171
7201
 
7172
7202
  widget.onGetValue = () => {
7173
- return color.value;
7174
- };
7175
- widget.onSetValue = ( newValue, skipCallback ) => {
7176
- color.value = newValue;
7177
- Panel._dispatch_event( color, "input", skipCallback );
7203
+ return value;
7178
7204
  };
7179
7205
 
7180
- let element = widget.domEl;
7181
- let change_from_input = false;
7206
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7182
7207
 
7183
- // Add reset functionality
7184
- Panel._add_reset_property( element.domName, function() {
7185
- this.style.display = "none";
7186
- color.value = color.iValue;
7187
- Panel._dispatch_event( color, "input" );
7188
- });
7208
+ if( color.useRGB )
7209
+ {
7210
+ newValue = hexToRgb( newValue );
7211
+ }
7212
+
7213
+ // Means it was called from the color input listener, not the text
7214
+ if( event )
7215
+ {
7216
+ textWidget.set( newValue, true, event );
7217
+ }
7218
+
7219
+ color.value = value = newValue;
7220
+
7221
+ if( !skipCallback )
7222
+ {
7223
+ this._trigger( new IEvent( name, newValue, event ), callback );
7224
+ }
7225
+ };
7226
+
7227
+ const element = widget.domEl;
7189
7228
 
7190
7229
  // Add widget value
7191
7230
 
@@ -7199,7 +7238,7 @@ class Panel {
7199
7238
  color.className = "colorinput";
7200
7239
  color.id = "color" + simple_guidGenerator();
7201
7240
  color.useRGB = options.useRGB ?? false;
7202
- color.value = color.iValue = value.constructor === Array ? rgbToHex( value ) : value;
7241
+ color.value = color.iValue = value;
7203
7242
 
7204
7243
  if( options.disabled )
7205
7244
  {
@@ -7207,38 +7246,18 @@ class Panel {
7207
7246
  }
7208
7247
 
7209
7248
  color.addEventListener( "input", e => {
7210
- let val = e.target.value;
7211
-
7212
- const skipCallback = e.detail;
7213
-
7214
- // Change value (always hex)
7215
- if( !change_from_input )
7216
- text_widget.set( val );
7217
-
7218
- // Reset button (default value)
7219
- if( !skipCallback )
7220
- {
7221
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7222
- if( btn ) btn.style.display = val != color.iValue ? "block": "none";
7223
- }
7224
-
7225
- if( color.useRGB )
7226
- val = hexToRgb( val );
7227
-
7228
- if( !skipCallback ) this._trigger( new IEvent( name, val, e ), callback );
7249
+ widget.set( e.target.value, false, e );
7229
7250
  }, false );
7230
7251
 
7231
7252
  container.appendChild( color );
7232
7253
 
7233
7254
  this.queue( container );
7234
7255
 
7235
- const text_widget = this.addText( null, color.value, v => {
7236
- change_from_input = true;
7256
+ const textWidget = this.addText( null, color.value, v => {
7237
7257
  widget.set( v );
7238
- change_from_input = false;
7239
7258
  }, { width: "calc( 100% - 32px )"});
7240
7259
 
7241
- text_widget.domEl.style.marginLeft = "4px";
7260
+ textWidget.domEl.style.marginLeft = "4px";
7242
7261
 
7243
7262
  this.clearQueue();
7244
7263
 
@@ -7253,6 +7272,7 @@ class Panel {
7253
7272
  * @param {Number} value Default number value
7254
7273
  * @param {Function} callback Callback function on change
7255
7274
  * @param {*} options:
7275
+ * hideName: Don't use name as label [false]
7256
7276
  * className: Extra classes to customize style
7257
7277
  * disabled: Make the widget disabled [false]
7258
7278
  * left: The slider goes to the left instead of the right
@@ -7263,28 +7283,28 @@ class Panel {
7263
7283
 
7264
7284
  addRange( name, value, callback, options = {} ) {
7265
7285
 
7266
- let widget = this.create_widget( name, Widget.RANGE, options );
7286
+ let widget = this._createWidget( Widget.RANGE, name, value, options );
7267
7287
 
7268
7288
  widget.onGetValue = () => {
7269
- return +slider.value;
7289
+ return value;
7270
7290
  };
7271
7291
 
7272
- widget.onSetValue = ( newValue, skipCallback ) => {
7273
- slider.value = newValue;
7274
- Panel._dispatch_event( slider, "input", skipCallback );
7275
- };
7292
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7276
7293
 
7277
- let element = widget.domEl;
7294
+ if( isNaN( newValue ) )
7295
+ {
7296
+ return;
7297
+ }
7278
7298
 
7279
- // add reset functionality
7280
- if( widget.name )
7281
- {
7282
- Panel._add_reset_property( element.domName, function() {
7283
- this.style.display = "none";
7284
- slider.value = slider.iValue;
7285
- Panel._dispatch_event( slider, "input" );
7286
- });
7287
- }
7299
+ slider.value = value = clamp( +newValue, +slider.min, +slider.max );
7300
+
7301
+ if( !skipCallback )
7302
+ {
7303
+ this._trigger( new IEvent( name, options.left ? ( ( +slider.max ) - value + ( +slider.min ) ) : value, event ), callback );
7304
+ }
7305
+ };
7306
+
7307
+ const element = widget.domEl;
7288
7308
 
7289
7309
  // add widget value
7290
7310
 
@@ -7312,30 +7332,7 @@ class Panel {
7312
7332
  }
7313
7333
 
7314
7334
  slider.addEventListener( "input", e => {
7315
-
7316
- if( isNaN( e.target.valueAsNumber ) )
7317
- {
7318
- return;
7319
- }
7320
-
7321
- const skipCallback = e.detail;
7322
-
7323
- let val = e.target.value = clamp( +e.target.valueAsNumber, +slider.min, +slider.max );
7324
- slider.value = val;
7325
-
7326
- // Reset button (default value)
7327
- if( !skipCallback )
7328
- {
7329
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7330
- if( btn ) btn.style.display = val != slider.iValue ? "block": "none";
7331
- }
7332
-
7333
- if( options.left )
7334
- {
7335
- val = ( +slider.max ) - val + ( +slider.min );
7336
- }
7337
-
7338
- if( !skipCallback ) this._trigger( new IEvent( name, val, e ), callback );
7335
+ widget.set( e.target.valueAsNumber, false, e );
7339
7336
  }, { passive: false });
7340
7337
 
7341
7338
  slider.addEventListener( "mousedown", function( e ) {
@@ -7368,8 +7365,8 @@ class Panel {
7368
7365
  container.appendChild( slider );
7369
7366
  element.appendChild( container );
7370
7367
 
7371
- // Remove branch padding and margins
7372
- if( !widget.name )
7368
+ const useNameAsLabel = !( options.hideName ?? false );
7369
+ if( !useNameAsLabel )
7373
7370
  {
7374
7371
  element.className += " noname";
7375
7372
  container.style.width = "100%";
@@ -7384,6 +7381,7 @@ class Panel {
7384
7381
  * @param {Number} value Default number value
7385
7382
  * @param {Function} callback Callback function on change
7386
7383
  * @param {*} options:
7384
+ * hideName: Don't use name as label [false]
7387
7385
  * disabled: Make the widget disabled [false]
7388
7386
  * step: Step of the input
7389
7387
  * precision: The number of digits to appear after the decimal point
@@ -7396,28 +7394,40 @@ class Panel {
7396
7394
 
7397
7395
  addNumber( name, value, callback, options = {} ) {
7398
7396
 
7399
- let widget = this.create_widget( name, Widget.NUMBER, options );
7397
+ let widget = this._createWidget( Widget.NUMBER, name, value, options );
7400
7398
 
7401
7399
  widget.onGetValue = () => {
7402
- return +vecinput.value;
7400
+ return value;
7403
7401
  };
7404
7402
 
7405
- widget.onSetValue = ( newValue, skipCallback ) => {
7406
- vecinput.value = round( newValue, options.precision );
7407
- Panel._dispatch_event( vecinput, "change", skipCallback );
7408
- };
7403
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7404
+
7405
+ if( isNaN( newValue ) )
7406
+ {
7407
+ return;
7408
+ }
7409
7409
 
7410
- let element = widget.domEl;
7410
+ value = clamp( +newValue, +vecinput.min, +vecinput.max );
7411
+ vecinput.value = value = round( value, options.precision );
7411
7412
 
7412
- // add reset functionality
7413
- if( widget.name )
7414
- {
7415
- Panel._add_reset_property( element.domName, function() {
7416
- this.style.display = "none";
7417
- vecinput.value = vecinput.iValue;
7418
- Panel._dispatch_event( vecinput, "change" );
7419
- });
7420
- }
7413
+ // Update slider!
7414
+ if( box.querySelector( ".lexinputslider" ) )
7415
+ {
7416
+ box.querySelector( ".lexinputslider" ).value = value;
7417
+ }
7418
+
7419
+ if( options.units )
7420
+ {
7421
+ vecinput.unitSpan.style.left = measureRealWidth( value ) + "px";
7422
+ }
7423
+
7424
+ if( !skipCallback )
7425
+ {
7426
+ this._trigger( new IEvent( name, value, event ), callback );
7427
+ }
7428
+ };
7429
+
7430
+ const element = widget.domEl;
7421
7431
 
7422
7432
  // add widget value
7423
7433
 
@@ -7476,9 +7486,7 @@ class Panel {
7476
7486
  slider.value = value;
7477
7487
 
7478
7488
  slider.addEventListener( "input", function( e ) {
7479
- let new_value = +this.valueAsNumber;
7480
- vecinput.value = round( new_value, options.precision );
7481
- Panel._dispatch_event( vecinput, "change" );
7489
+ widget.set( this.valueAsNumber, false, e );
7482
7490
  }, false );
7483
7491
 
7484
7492
  slider.addEventListener( "mousedown", function( e ) {
@@ -7503,13 +7511,13 @@ class Panel {
7503
7511
  vecinput.max = slider.max = newMax ?? vecinput.max;
7504
7512
  vecinput.step = newStep ?? vecinput.step;
7505
7513
  slider.step = newStep ?? slider.step;
7506
- Panel._dispatch_event( vecinput, "change", true );
7514
+ widget.set( value, true );
7507
7515
  };
7508
7516
  }
7509
7517
 
7510
7518
  vecinput.addEventListener( "input", function( e ) {
7511
- let new_value = +this.valueAsNumber;
7512
- vecinput.value = round( new_value, options.precision );
7519
+ value = +this.valueAsNumber;
7520
+ value = round( value, options.precision );
7513
7521
  if( options.units )
7514
7522
  {
7515
7523
  vecinput.unitSpan.style.left = measureRealWidth( vecinput.value ) + "px";
@@ -7525,44 +7533,12 @@ class Panel {
7525
7533
  let mult = options.step ?? 1;
7526
7534
  if( e.shiftKey ) mult *= 10;
7527
7535
  else if( e.altKey ) mult *= 0.1;
7528
- let new_value = ( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ) );
7529
- this.value = round( new_value, options.precision );
7530
- Panel._dispatch_event(vecinput, "change");
7536
+ value = ( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ) );
7537
+ widget.set( value, false, e );
7531
7538
  }, { passive: false });
7532
7539
 
7533
- vecinput.addEventListener( "change", e => {
7534
-
7535
- if( isNaN( e.target.valueAsNumber ) )
7536
- {
7537
- return;
7538
- }
7539
-
7540
- const skipCallback = e.detail;
7541
-
7542
- let val = e.target.value = clamp( +e.target.valueAsNumber, +vecinput.min, +vecinput.max );
7543
- val = options.precision ? round( val, options.precision ) : val;
7544
-
7545
- // Update slider!
7546
- if( box.querySelector( ".lexinputslider" ) )
7547
- {
7548
- box.querySelector( ".lexinputslider" ).value = val;
7549
- }
7550
-
7551
- vecinput.value = val;
7552
-
7553
- if( options.units )
7554
- {
7555
- vecinput.unitSpan.style.left = measureRealWidth( vecinput.value ) + "px";
7556
- }
7557
-
7558
- // Reset button (default value)
7559
- if( !skipCallback )
7560
- {
7561
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7562
- if( btn ) btn.style.display = val != vecinput.iValue ? "block": "none";
7563
- }
7564
-
7565
- if( !skipCallback ) this._trigger( new IEvent( name, val, e ), callback );
7540
+ vecinput.addEventListener( "change", function( e ) {
7541
+ widget.set( this.valueAsNumber, false, e );
7566
7542
  }, { passive: false });
7567
7543
 
7568
7544
  // Add drag input
@@ -7606,9 +7582,9 @@ class Panel {
7606
7582
  let mult = options.step ?? 1;
7607
7583
  if( e.shiftKey ) mult *= 10;
7608
7584
  else if( e.altKey ) mult *= 0.1;
7609
- let new_value = ( +vecinput.valueAsNumber + mult * dt );
7610
- vecinput.value = ( +new_value ).toFixed( 4 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' );
7611
- Panel._dispatch_event( vecinput, "change" );
7585
+ value = ( +vecinput.valueAsNumber + mult * dt );
7586
+ widget.set( value, false, e );
7587
+ // vecinput.value = ( +new_value ).toFixed( 4 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' );
7612
7588
  }
7613
7589
 
7614
7590
  e.stopPropagation();
@@ -7638,7 +7614,8 @@ class Panel {
7638
7614
  element.appendChild( container );
7639
7615
 
7640
7616
  // Remove branch padding and margins
7641
- if( !widget.name )
7617
+ const useNameAsLabel = !( options.hideName ?? false );
7618
+ if( !useNameAsLabel )
7642
7619
  {
7643
7620
  element.className += " noname";
7644
7621
  container.style.width = "100%";
@@ -7649,17 +7626,12 @@ class Panel {
7649
7626
 
7650
7627
  static VECTOR_COMPONENTS = { 0: 'x', 1: 'y', 2: 'z', 3: 'w' };
7651
7628
 
7652
- _add_vector( num_components, name, value, callback, options = {} ) {
7653
-
7654
- num_components = clamp( num_components, 2, 4 );
7655
- value = value ?? new Array( num_components ).fill( 0 );
7629
+ _add_vector( numComponents, name, value, callback, options = {} ) {
7656
7630
 
7657
- if( !name )
7658
- {
7659
- throw( "Set Widget Name!" );
7660
- }
7631
+ numComponents = clamp( numComponents, 2, 4 );
7632
+ value = value ?? new Array( numComponents ).fill( 0 );
7661
7633
 
7662
- let widget = this.create_widget( name, Widget.VECTOR, options );
7634
+ let widget = this._createWidget( Widget.VECTOR, name, [].concat( value ), options );
7663
7635
 
7664
7636
  widget.onGetValue = () => {
7665
7637
  let inputs = element.querySelectorAll( "input" );
@@ -7671,33 +7643,30 @@ class Panel {
7671
7643
  return value;
7672
7644
  };
7673
7645
 
7674
- widget.onSetValue = ( newValue, skipCallback ) => {
7675
- const inputs = element.querySelectorAll( ".vecinput" );
7676
- if( inputs.length == newValue.length )
7646
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7647
+
7648
+ if( vectorInputs.length != newValue.length )
7677
7649
  {
7678
7650
  console.error( "Input length does not match vector length." );
7679
7651
  return;
7680
7652
  }
7681
7653
 
7682
- for( let i = 0; i < inputs.length; ++i )
7654
+ for( let i = 0; i < vectorInputs.length; ++i )
7683
7655
  {
7684
7656
  let value = newValue[ i ];
7685
- inputs[ i ].value = round( value, options.precision ) ?? 0;
7686
- Panel._dispatch_event( inputs[ i ], "change", skipCallback );
7657
+ value = clamp( value, +vectorInputs[ i ].min, +vectorInputs[ i ].max );
7658
+ value = round( value, options.precision ) ?? 0;
7659
+ vectorInputs[ i ].value = newValue[ i ] = value;
7687
7660
  }
7688
- };
7689
-
7690
- let element = widget.domEl;
7691
7661
 
7692
- // Add reset functionality
7693
- Panel._add_reset_property( element.domName, function() {
7694
- this.style.display = "none";
7695
- for( let v of element.querySelectorAll( ".vecinput" ) )
7662
+ if( !skipCallback )
7696
7663
  {
7697
- v.value = v.iValue;
7698
- Panel._dispatch_event( v, "change" );
7664
+ this._trigger( new IEvent( name, newValue, event ), callback );
7699
7665
  }
7700
- });
7666
+ };
7667
+
7668
+ const element = widget.domEl;
7669
+ const vectorInputs = [];
7701
7670
 
7702
7671
  // Add widget value
7703
7672
 
@@ -7705,20 +7674,21 @@ class Panel {
7705
7674
  container.className = "lexvector";
7706
7675
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
7707
7676
 
7708
- for( let i = 0; i < num_components; ++i )
7677
+ for( let i = 0; i < numComponents; ++i )
7709
7678
  {
7710
7679
  let box = document.createElement( 'div' );
7711
7680
  box.className = "vecbox";
7712
7681
  box.innerHTML = "<span class='" + Panel.VECTOR_COMPONENTS[ i ] + "'></span>";
7713
7682
 
7714
7683
  let vecinput = document.createElement( 'input' );
7715
- vecinput.className = "vecinput v" + num_components;
7684
+ vecinput.className = "vecinput v" + numComponents;
7716
7685
  vecinput.min = options.min ?? -1e24;
7717
7686
  vecinput.max = options.max ?? 1e24;
7718
7687
  vecinput.step = options.step ?? "any";
7719
7688
  vecinput.type = "number";
7720
- vecinput.id = "vec" + num_components + "_" + simple_guidGenerator();
7689
+ vecinput.id = "vec" + numComponents + "_" + simple_guidGenerator();
7721
7690
  vecinput.idx = i;
7691
+ vectorInputs[ i ] = vecinput;
7722
7692
 
7723
7693
  if( value[ i ].constructor == Number )
7724
7694
  {
@@ -7768,21 +7738,12 @@ class Panel {
7768
7738
  return;
7769
7739
  }
7770
7740
 
7771
- const skipCallback = e.detail;
7772
-
7773
- let val = e.target.value = clamp( e.target.value, +vecinput.min, +vecinput.max );
7741
+ let val = clamp( e.target.value, +vecinput.min, +vecinput.max );
7774
7742
  val = round( val, options.precision );
7775
7743
 
7776
- // Reset button (default value)
7777
- if( !skipCallback )
7778
- {
7779
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7780
- if( btn ) btn.style.display = val != vecinput.iValue ? "block" : "none";
7781
- }
7782
-
7783
7744
  if( locker.locked )
7784
7745
  {
7785
- for( let v of element.querySelectorAll( ".vecinput" ) )
7746
+ for( let v of vectorInputs )
7786
7747
  {
7787
7748
  v.value = val;
7788
7749
  value[ v.idx ] = val;
@@ -7794,7 +7755,7 @@ class Panel {
7794
7755
  value[ e.target.idx ] = val;
7795
7756
  }
7796
7757
 
7797
- if( !skipCallback ) this._trigger( new IEvent( name, value, e ), callback );
7758
+ widget.set( value, false, e );
7798
7759
  }, false );
7799
7760
 
7800
7761
  // Add drag input
@@ -7885,17 +7846,14 @@ class Panel {
7885
7846
  if( options.min !== undefined || options.max !== undefined )
7886
7847
  {
7887
7848
  widget.setLimits = ( newMin, newMax, newStep ) => {
7888
- const inputs = element.querySelectorAll(".vecinput");
7889
- for( let v of inputs )
7849
+ for( let v of vectorInputs )
7890
7850
  {
7891
7851
  v.min = newMin ?? v.min;
7892
7852
  v.max = newMax ?? v.max;
7893
7853
  v.step = newStep ?? v.step;
7894
- Panel._dispatch_event( v, "change", true );
7895
7854
  }
7896
7855
 
7897
- // To call onChange callback
7898
- this._trigger( new IEvent( name, value ), callback );
7856
+ widget.set( value, true );
7899
7857
  };
7900
7858
  }
7901
7859
 
@@ -7956,13 +7914,14 @@ class Panel {
7956
7914
  * @param {Number} value Default number value
7957
7915
  * @param {Function} callback Callback function on change
7958
7916
  * @param {*} options:
7917
+ * hideName: Don't use name as label [false]
7959
7918
  * disabled: Make the widget disabled [false]
7960
7919
  * units: Unit as string added to the end of the value
7961
7920
  */
7962
7921
 
7963
7922
  addSize( name, value, callback, options = {} ) {
7964
7923
 
7965
- let widget = this.create_widget( name, Widget.SIZE, options );
7924
+ let widget = this._createWidget( Widget.SIZE, name, value, options );
7966
7925
 
7967
7926
  widget.onGetValue = () => {
7968
7927
  const value = [];
@@ -7973,14 +7932,14 @@ class Panel {
7973
7932
  return value;
7974
7933
  };
7975
7934
 
7976
- widget.onSetValue = ( newValue, skipCallback ) => {
7935
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7977
7936
  for( let i = 0; i < element.dimensions.length; ++i )
7978
7937
  {
7979
7938
  element.dimensions[ i ].set( newValue[ i ], skipCallback );
7980
7939
  }
7981
7940
  };
7982
7941
 
7983
- let element = widget.domEl;
7942
+ const element = widget.domEl;
7984
7943
 
7985
7944
  this.queue( element );
7986
7945
 
@@ -8053,7 +8012,8 @@ class Panel {
8053
8012
  }
8054
8013
 
8055
8014
  // Remove branch padding and margins
8056
- if( !widget.name )
8015
+ const useNameAsLabel = !( options.hideName ?? false );
8016
+ if( !useNameAsLabel )
8057
8017
  {
8058
8018
  element.className += " noname";
8059
8019
  container.style.width = "100%";
@@ -8077,18 +8037,13 @@ class Panel {
8077
8037
 
8078
8038
  addPad( name, value, callback, options = {} ) {
8079
8039
 
8080
- if( !name )
8081
- {
8082
- throw( "Set Widget Name!" );
8083
- }
8084
-
8085
- let widget = this.create_widget( name, Widget.PAD, options );
8040
+ let widget = this._createWidget( Widget.PAD, name, null, options );
8086
8041
 
8087
8042
  widget.onGetValue = () => {
8088
8043
  return thumb.value.xy;
8089
8044
  };
8090
8045
 
8091
- widget.onSetValue = ( newValue, skipCallback ) => {
8046
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8092
8047
  thumb.value.set( newValue[ 0 ], newValue[ 1 ] );
8093
8048
  _updateValue( thumb.value );
8094
8049
  if( !skipCallback )
@@ -8097,7 +8052,7 @@ class Panel {
8097
8052
  }
8098
8053
  };
8099
8054
 
8100
- let element = widget.domEl;
8055
+ const element = widget.domEl;
8101
8056
 
8102
8057
  var container = document.createElement( 'div' );
8103
8058
  container.className = "lexpad";
@@ -8202,17 +8157,13 @@ class Panel {
8202
8157
 
8203
8158
  addProgress( name, value, options = {} ) {
8204
8159
 
8205
- if( !name )
8206
- {
8207
- throw("Set Widget Name!");
8208
- }
8209
-
8210
- let widget = this.create_widget( name, Widget.PROGRESS, options );
8160
+ let widget = this._createWidget( Widget.PROGRESS, name, value, options );
8211
8161
 
8212
8162
  widget.onGetValue = () => {
8213
8163
  return progress.value;
8214
8164
  };
8215
- widget.onSetValue = ( newValue, skipCallback ) => {
8165
+
8166
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8216
8167
  element.querySelector("meter").value = newValue;
8217
8168
  _updateColor();
8218
8169
  if( element.querySelector("span") )
@@ -8221,7 +8172,7 @@ class Panel {
8221
8172
  }
8222
8173
  };
8223
8174
 
8224
- let element = widget.domEl;
8175
+ const element = widget.domEl;
8225
8176
 
8226
8177
  var container = document.createElement('div');
8227
8178
  container.className = "lexprogress";
@@ -8343,13 +8294,8 @@ class Panel {
8343
8294
 
8344
8295
  addFile( name, callback, options = { } ) {
8345
8296
 
8346
- if( !name )
8347
- {
8348
- throw( "Set Widget Name!" );
8349
- }
8350
-
8351
- let widget = this.create_widget( name, Widget.FILE, options );
8352
- let element = widget.domEl;
8297
+ let widget = this._createWidget( Widget.FILE, name, null, options );
8298
+ const element = widget.domEl;
8353
8299
 
8354
8300
  let local = options.local ?? true;
8355
8301
  let type = options.type ?? 'text';
@@ -8435,20 +8381,28 @@ class Panel {
8435
8381
 
8436
8382
  addTree( name, data, options = {} ) {
8437
8383
 
8384
+ options.hideName = true;
8385
+
8386
+ const widget = this._createWidget( Widget.TREE, name, null, options );
8387
+ const element = widget.domEl;
8388
+
8438
8389
  let container = document.createElement('div');
8439
8390
  container.className = "lextree";
8391
+ element.appendChild( container );
8440
8392
 
8441
8393
  if( name )
8442
8394
  {
8443
8395
  let title = document.createElement('span');
8444
8396
  title.innerHTML = name;
8445
- container.appendChild(title);
8397
+ container.appendChild( title );
8446
8398
  }
8447
8399
 
8448
8400
  let toolsDiv = document.createElement('div');
8449
8401
  toolsDiv.className = "lextreetools";
8450
- if(!name)
8402
+ if( !name )
8403
+ {
8451
8404
  toolsDiv.className += " notitle";
8405
+ }
8452
8406
 
8453
8407
  // Tree icons
8454
8408
  if( options.icons )
@@ -8459,7 +8413,7 @@ class Panel {
8459
8413
  iconEl.title = data.name;
8460
8414
  iconEl.className = "lexicon " + data.icon;
8461
8415
  iconEl.addEventListener("click", data.callback);
8462
- toolsDiv.appendChild(iconEl);
8416
+ toolsDiv.appendChild( iconEl );
8463
8417
  }
8464
8418
  }
8465
8419
 
@@ -8467,35 +8421,36 @@ class Panel {
8467
8421
 
8468
8422
  options.filter = options.filter ?? true;
8469
8423
 
8470
- let node_filter_input = null;
8471
- if(options.filter)
8424
+ let nodeFilterInput = null;
8425
+ if( options.filter )
8472
8426
  {
8473
- node_filter_input = document.createElement('input');
8474
- node_filter_input.id = "lexnodetree_filter";
8475
- node_filter_input.setAttribute("placeholder", "Filter..");
8476
- node_filter_input.style.width = "calc( 100% - 17px )";
8477
- node_filter_input.addEventListener('input', function(){
8427
+ nodeFilterInput = document.createElement('input');
8428
+ nodeFilterInput.id = "lexnodetree_filter";
8429
+ nodeFilterInput.setAttribute("placeholder", "Filter..");
8430
+ nodeFilterInput.style.width = "calc( 100% - 17px )";
8431
+ nodeFilterInput.addEventListener('input', function(){
8478
8432
  nodeTree.refresh();
8479
8433
  });
8480
8434
 
8481
8435
  let searchIcon = document.createElement('a');
8482
8436
  searchIcon.className = "lexicon fa-solid fa-magnifying-glass";
8483
- toolsDiv.appendChild(node_filter_input);
8484
- toolsDiv.appendChild(searchIcon);
8437
+ toolsDiv.appendChild( nodeFilterInput );
8438
+ toolsDiv.appendChild( searchIcon );
8485
8439
  }
8486
8440
 
8487
- if(options.icons || options.filter)
8488
- container.appendChild(toolsDiv);
8441
+ if( options.icons || options.filter )
8442
+ {
8443
+ container.appendChild( toolsDiv );
8444
+ }
8489
8445
 
8490
8446
  // Tree
8491
8447
 
8492
8448
  let list = document.createElement('ul');
8493
- list.addEventListener("contextmenu", function(e) {
8449
+ list.addEventListener("contextmenu", function( e ) {
8494
8450
  e.preventDefault();
8495
8451
  });
8496
8452
 
8497
- container.appendChild(list);
8498
- this.root.appendChild(container);
8453
+ container.appendChild( list );
8499
8454
 
8500
8455
  const nodeTree = new NodeTree( container, data, options );
8501
8456
  return nodeTree;
@@ -8509,13 +8464,14 @@ class Panel {
8509
8464
 
8510
8465
  var element = document.createElement('div');
8511
8466
  element.className = "lexseparator";
8512
- let widget = new Widget( null, Widget.SEPARATOR );
8467
+
8468
+ let widget = new Widget( Widget.SEPARATOR );
8513
8469
  widget.domEl = element;
8514
8470
 
8515
- if( this.current_branch )
8471
+ if( this._currentBranch )
8516
8472
  {
8517
- this.current_branch.content.appendChild( element );
8518
- this.current_branch.widgets.push( widget );
8473
+ this._currentBranch.content.appendChild( element );
8474
+ this._currentBranch.widgets.push( widget );
8519
8475
  }
8520
8476
  else
8521
8477
  {
@@ -8538,12 +8494,8 @@ class Panel {
8538
8494
 
8539
8495
  addTabs( tabs, options = {} ) {
8540
8496
 
8541
- let root = this.current_branch ? this.current_branch.content : this.root;
8542
-
8543
- if( !this.current_branch )
8544
- {
8545
- console.warn("No current branch!");
8546
- }
8497
+ const widget = this._createWidget( Widget.TABS, null, null, options );
8498
+ const element = widget.domEl;
8547
8499
 
8548
8500
  if( tabs.constructor != Array )
8549
8501
  {
@@ -8563,7 +8515,7 @@ class Panel {
8563
8515
  let tabContainer = document.createElement( 'div' );
8564
8516
  tabContainer.className = 'tabs';
8565
8517
  container.appendChild( tabContainer );
8566
- root.appendChild( container );
8518
+ element.appendChild( container );
8567
8519
 
8568
8520
  for( let i = 0; i < tabs.length; ++i )
8569
8521
  {
@@ -8587,10 +8539,9 @@ class Panel {
8587
8539
  container.appendChild( infoContainer );
8588
8540
 
8589
8541
  tabEl.addEventListener( 'click', e => {
8590
-
8591
8542
  // Change selected tab
8592
8543
  tabContainer.querySelectorAll( '.lextab' ).forEach( e => { e.classList.remove( 'selected' ); } );
8593
- e.target.classList.add( 'selected' );
8544
+ tabEl.classList.add( 'selected' );
8594
8545
  // Hide all tabs content
8595
8546
  container.querySelectorAll(".widgets").forEach( e => { e.toggleAttribute( 'hidden', true ); } );
8596
8547
  // Show tab content
@@ -8631,42 +8582,38 @@ class Panel {
8631
8582
 
8632
8583
  addCounter( name, value, callback, options = { } ) {
8633
8584
 
8634
- let widget = this.create_widget( name, Widget.COUNTER, options );
8585
+ let widget = this._createWidget( Widget.COUNTER, name, value, options );
8635
8586
 
8636
8587
  widget.onGetValue = () => {
8637
8588
  return counterText.count;
8638
8589
  };
8639
8590
 
8640
- widget.onSetValue = ( newValue, skipCallback ) => {
8641
- _onChange( newValue, skipCallback );
8591
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8592
+ newValue = clamp( newValue, min, max );
8593
+ counterText.count = newValue;
8594
+ counterText.innerHTML = newValue;
8595
+ if( !skipCallback )
8596
+ {
8597
+ this._trigger( new IEvent( name, newValue, event ), callback );
8598
+ }
8642
8599
  };
8643
8600
 
8644
- let element = widget.domEl;
8601
+ const element = widget.domEl;
8645
8602
 
8646
8603
  const min = options.min ?? 0;
8647
8604
  const max = options.max ?? 100;
8648
8605
  const step = options.step ?? 1;
8649
8606
 
8650
- const _onChange = ( value, skipCallback, event ) => {
8651
- value = clamp( value, min, max );
8652
- counterText.count = value;
8653
- counterText.innerHTML = value;
8654
- if( !skipCallback )
8655
- {
8656
- this._trigger( new IEvent( name, value, event ), callback );
8657
- }
8658
- }
8659
-
8660
8607
  const container = document.createElement( 'div' );
8661
8608
  container.className = "lexcounter";
8662
8609
  element.appendChild( container );
8663
8610
 
8664
8611
  this.queue( container );
8665
8612
 
8666
- this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-minus'></a>", (value, e) => {
8613
+ this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-minus'></a>", ( value, e ) => {
8667
8614
  let mult = step ?? 1;
8668
8615
  if( e.shiftKey ) mult *= 10;
8669
- _onChange( counterText.count - mult, false, e );
8616
+ widget.set( counterText.count - mult, false, e );
8670
8617
  }, { className: "micro", skipInlineCount: true, title: "Minus" });
8671
8618
 
8672
8619
  this.clearQueue();
@@ -8691,10 +8638,10 @@ class Panel {
8691
8638
 
8692
8639
  this.queue( container );
8693
8640
 
8694
- this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-plus'></a>", (value, e) => {
8641
+ this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-plus'></a>", ( value, e ) => {
8695
8642
  let mult = step ?? 1;
8696
8643
  if( e.shiftKey ) mult *= 10;
8697
- _onChange( counterText.count + mult, false, e );
8644
+ widget.set( counterText.count + mult, false, e );
8698
8645
  }, { className: "micro", skipInlineCount: true, title: "Plus" });
8699
8646
 
8700
8647
  this.clearQueue();
@@ -8707,6 +8654,7 @@ class Panel {
8707
8654
  * @param {String} name Widget name
8708
8655
  * @param {Number} data Table data
8709
8656
  * @param {*} options:
8657
+ * hideName: Don't use name as label [false]
8710
8658
  * head: Table headers (each of the headers per column)
8711
8659
  * body: Table body (data per row for each column)
8712
8660
  * rowActions: Allow to add actions per row
@@ -8721,17 +8669,17 @@ class Panel {
8721
8669
  throw( "Data is needed to create a table!" );
8722
8670
  }
8723
8671
 
8724
- let widget = this.create_widget( name, Widget.TABLE, options );
8672
+ let widget = this._createWidget( Widget.TABLE, name, null, options );
8725
8673
 
8726
8674
  widget.onGetValue = () => {
8727
8675
 
8728
8676
  };
8729
8677
 
8730
- widget.onSetValue = ( newValue, skipCallback ) => {
8678
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8731
8679
 
8732
8680
  };
8733
8681
 
8734
- let element = widget.domEl;
8682
+ const element = widget.domEl;
8735
8683
 
8736
8684
  const container = document.createElement('div');
8737
8685
  container.className = "lextable";
@@ -8919,7 +8867,8 @@ class Panel {
8919
8867
 
8920
8868
  widget.refreshTable();
8921
8869
 
8922
- if( !widget.name )
8870
+ const useNameAsLabel = !( options.hideName ?? false );
8871
+ if( !useNameAsLabel )
8923
8872
  {
8924
8873
  element.className += " noname";
8925
8874
  container.style.width = "100%";
@@ -8966,12 +8915,14 @@ class Branch {
8966
8915
  var title = document.createElement( 'div' );
8967
8916
  title.className = "lexbranchtitle";
8968
8917
 
8969
- title.innerHTML = "<a class='fa-solid fa-angle-up switch-branch-button'></a>";
8970
8918
  if( options.icon )
8971
8919
  {
8972
- title.innerHTML += "<a class='branchicon " + options.icon + "' style='margin-right: 8px; margin-bottom: -2px;'>";
8920
+ title.innerHTML = "<a class='branchicon " + options.icon + "'>";
8973
8921
  }
8974
- title.innerHTML += name || "Branch";
8922
+
8923
+ title.innerHTML += ( name || "Branch" );
8924
+
8925
+ title.innerHTML += "<a class='fa-solid fa-angle-up switch-branch-button'></a>";
8975
8926
 
8976
8927
  root.appendChild( title );
8977
8928
 
@@ -9129,7 +9080,7 @@ class Branch {
9129
9080
  for( var i = 0; i < this.widgets.length; i++ )
9130
9081
  {
9131
9082
  let widget = this.widgets[ i ];
9132
- let element = widget.domEl;
9083
+ const element = widget.domEl;
9133
9084
 
9134
9085
  if( element.children.length < 2 )
9135
9086
  {
@@ -9302,7 +9253,7 @@ class Dialog {
9302
9253
  titleDiv.innerHTML = title;
9303
9254
  titleDiv.setAttribute('draggable', false);
9304
9255
 
9305
- titleDiv.oncontextmenu = function(e) {
9256
+ titleDiv.oncontextmenu = function( e ) {
9306
9257
  e.preventDefault();
9307
9258
  e.stopPropagation();
9308
9259
 
@@ -11139,7 +11090,7 @@ class AssetView {
11139
11090
  itemEl.appendChild(info);
11140
11091
  }
11141
11092
 
11142
- itemEl.addEventListener('click', function(e) {
11093
+ itemEl.addEventListener('click', function( e ) {
11143
11094
  e.stopImmediatePropagation();
11144
11095
  e.stopPropagation();
11145
11096
 
@@ -11176,7 +11127,7 @@ class AssetView {
11176
11127
 
11177
11128
  if( that.contextMenu )
11178
11129
  {
11179
- itemEl.addEventListener('contextmenu', function(e) {
11130
+ itemEl.addEventListener('contextmenu', function( e ) {
11180
11131
  e.preventDefault();
11181
11132
 
11182
11133
  const multiple = that.content.querySelectorAll('.selected').length;
@@ -11201,7 +11152,7 @@ class AssetView {
11201
11152
  });
11202
11153
  }
11203
11154
 
11204
- itemEl.addEventListener("dragstart", function(e) {
11155
+ itemEl.addEventListener("dragstart", function( e ) {
11205
11156
  e.preventDefault();
11206
11157
  }, false );
11207
11158
 
@@ -11598,7 +11549,7 @@ Object.assign(LX, {
11598
11549
  script.src = url[i] + ( version ? "?version=" + version : "" );
11599
11550
  script.original_src = url[i];
11600
11551
  script.async = false;
11601
- script.onload = function(e) {
11552
+ script.onload = function( e ) {
11602
11553
  total--;
11603
11554
  loaded_scripts.push(this);
11604
11555
  if(total)