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.
@@ -1,5 +1,3 @@
1
- 'use strict';
2
-
3
1
  // Lexgui.js @jxarco
4
2
 
5
3
  /**
@@ -8,7 +6,7 @@
8
6
  */
9
7
 
10
8
  var LX = {
11
- version: "0.4.1",
9
+ version: "0.4.2",
12
10
  ready: false,
13
11
  components: [], // Specific pre-build components
14
12
  signals: {}, // Events and triggers
@@ -165,7 +163,6 @@ function getThemeColor( colorName )
165
163
  {
166
164
  const r = getComputedStyle( document.querySelector( ':root' ) );
167
165
  const value = r.getPropertyValue( '--' + colorName );
168
- const theme = document.documentElement.getAttribute( "data-theme" );
169
166
 
170
167
  if( value.includes( "light-dark" ) )
171
168
  {
@@ -239,7 +236,7 @@ LX.rgbToHex = rgbToHex;
239
236
  /**
240
237
  * @method measureRealWidth
241
238
  * @description Measure the pixel width of a text
242
- * @param {Object} value Text to measure
239
+ * @param {Number} value Text to measure
243
240
  * @param {Number} paddingPlusMargin Padding offset
244
241
  */
245
242
  function measureRealWidth( value, paddingPlusMargin = 8 )
@@ -439,7 +436,7 @@ function makeCollapsible( domEl, content, parent, options = { } )
439
436
  actionIcon.dataset[ "collapsed" ] = collapsed;
440
437
  actionIcon.style.marginLeft = "auto";
441
438
 
442
- actionIcon.addEventListener( "click", function(e) {
439
+ actionIcon.addEventListener( "click", function( e ) {
443
440
  e.preventDefault();
444
441
  e.stopPropagation();
445
442
  if( this.dataset[ "collapsed" ] )
@@ -570,12 +567,13 @@ LX.makeCodeSnippet = makeCodeSnippet;
570
567
  * @description Gets an SVG element using one of LX.ICONS
571
568
  * @param {String} iconName
572
569
  * @param {String} iconTitle
570
+ * @param {String} extraClass
573
571
  */
574
- function makeIcon( iconName, iconTitle )
572
+ function makeIcon( iconName, iconTitle, extraClass = "" )
575
573
  {
576
574
  const icon = document.createElement( "a" );
577
575
  icon.title = iconTitle ?? "";
578
- icon.className = "lexicon";
576
+ icon.className = "lexicon " + extraClass;
579
577
  icon.innerHTML = LX.ICONS[ iconName ] ?? "";
580
578
  return icon;
581
579
  }
@@ -786,17 +784,17 @@ function _createCommandbar( root )
786
784
  searchItem.entry_name = t;
787
785
  searchItem.callback = c;
788
786
  searchItem.item = i;
789
- searchItem.addEventListener('click', function(e) {
787
+ searchItem.addEventListener('click', function( e ) {
790
788
  this.callback.call( window, this.entry_name );
791
789
  LX.setCommandbarState( false );
792
790
  _resetBar( true );
793
791
  });
794
- searchItem.addEventListener('mouseenter', function(e) {
792
+ searchItem.addEventListener('mouseenter', function( e ) {
795
793
  commandbar.querySelectorAll(".hovered").forEach(e => e.classList.remove('hovered'));
796
794
  this.classList.add('hovered');
797
795
  hoverElId = allItems.indexOf( this );
798
796
  });
799
- searchItem.addEventListener('mouseleave', function(e) {
797
+ searchItem.addEventListener('mouseleave', function( e ) {
800
798
  this.classList.remove('hovered');
801
799
  });
802
800
  allItems.push( searchItem );
@@ -877,7 +875,7 @@ function _createCommandbar( root )
877
875
  }
878
876
  }
879
877
 
880
- input.addEventListener('input', function(e) {
878
+ input.addEventListener('input', function( e ) {
881
879
  commandbar._addElements( this.value.toLowerCase() );
882
880
  });
883
881
 
@@ -1305,7 +1303,7 @@ LX.makeContainer = makeContainer;
1305
1303
 
1306
1304
  class IEvent {
1307
1305
 
1308
- constructor(name, value, domEvent) {
1306
+ constructor( name, value, domEvent ) {
1309
1307
  this.name = name;
1310
1308
  this.value = value;
1311
1309
  this.domEvent = domEvent;
@@ -2378,17 +2376,17 @@ class Tabs {
2378
2376
 
2379
2377
  let that = this;
2380
2378
 
2381
- container.addEventListener("dragenter", function(e) {
2379
+ container.addEventListener("dragenter", function( e ) {
2382
2380
  e.preventDefault(); // Prevent default action (open as link for some elements)
2383
2381
  this.classList.add("dockingtab");
2384
2382
  });
2385
2383
 
2386
- container.addEventListener("dragleave", function(e) {
2384
+ container.addEventListener("dragleave", function( e ) {
2387
2385
  e.preventDefault(); // Prevent default action (open as link for some elements)
2388
2386
  this.classList.remove("dockingtab");
2389
2387
  });
2390
2388
 
2391
- container.addEventListener("drop", function(e) {
2389
+ container.addEventListener("drop", function( e ) {
2392
2390
  e.preventDefault(); // Prevent default action (open as link for some elements)
2393
2391
 
2394
2392
  const tab_id = e.dataTransfer.getData("source");
@@ -2898,7 +2896,6 @@ class Menubar {
2898
2896
  this.shorts[ lastPath ] = options.short;
2899
2897
 
2900
2898
  let idx = 0;
2901
- let that = this;
2902
2899
 
2903
2900
  const _insertEntry = ( token, list ) => {
2904
2901
  if( token == undefined )
@@ -3019,15 +3016,15 @@ class Menubar {
3019
3016
 
3020
3017
  /**
3021
3018
  * @method getButton
3022
- * @param {String} title
3019
+ * @param {String} name
3023
3020
  */
3024
3021
 
3025
- getButton( title ) {
3026
- return this.buttons[ title ];
3022
+ getButton( name ) {
3023
+ return this.buttons[ name ];
3027
3024
  }
3028
3025
 
3029
3026
  /**
3030
- * @method getSubitems: recursive method to find subentries of a menu entry
3027
+ * @method getSubitems
3031
3028
  * @param {Object} item: parent item
3032
3029
  * @param {Array} tokens: split path strings
3033
3030
  */
@@ -3060,6 +3057,7 @@ class Menubar {
3060
3057
  * @param {String} path
3061
3058
  */
3062
3059
  getItem( path ) {
3060
+
3063
3061
  // process path
3064
3062
  const tokens = path.split("/");
3065
3063
 
@@ -3068,100 +3066,118 @@ class Menubar {
3068
3066
 
3069
3067
  /**
3070
3068
  * @method setButtonIcon
3071
- * @param {String} title
3069
+ * @param {String} name
3072
3070
  * @param {String} icon
3071
+ * @param {Function} callback
3072
+ * @param {Object} options
3073
3073
  */
3074
3074
 
3075
- setButtonIcon( title, icon, callback, options = {} ) {
3075
+ setButtonIcon( name, icon, callback, options = {} ) {
3076
+
3077
+ if( !name )
3078
+ {
3079
+ throw( "Set Button Name!" );
3080
+ }
3076
3081
 
3077
- const button = this.buttons[ title ];
3082
+ let button = this.buttons[ name ];
3078
3083
  if( button )
3079
3084
  {
3080
3085
  button.querySelector('a').className = "fa-solid" + " " + icon + " lexicon";
3086
+ return;
3081
3087
  }
3082
- else
3088
+
3089
+ // Otherwise, create it
3090
+ button = document.createElement('div');
3091
+ const disabled = options.disabled ?? false;
3092
+ button.className = "lexmenubutton main" + (disabled ? " disabled" : "");
3093
+ button.title = name;
3094
+ button.innerHTML = "<a class='" + icon + " lexicon'></a>";
3095
+
3096
+ if( options.float == "right" )
3083
3097
  {
3084
- let button = document.createElement('div');
3085
- const disabled = options.disabled ?? false;
3086
- button.className = "lexmenubutton" + (disabled ? " disabled" : "");
3087
- button.title = title ?? "";
3088
- button.innerHTML = "<a class='" + icon + " lexicon' style='font-size:x-large;'></a>";
3089
- button.style.padding = "5px 10px";
3090
- button.style.maxHeight = "calc(100% - 10px)";
3091
- button.style.alignItems = "center";
3098
+ button.right = true;
3099
+ }
3092
3100
 
3093
- if( options.float == "right" )
3094
- {
3095
- button.right = true;
3096
- }
3101
+ if( this.root.lastChild && this.root.lastChild.right )
3102
+ {
3103
+ this.root.lastChild.before( button );
3104
+ }
3105
+ else if( options.float == "left" )
3106
+ {
3107
+ this.root.prepend( button );
3108
+ }
3109
+ else
3110
+ {
3111
+ this.root.appendChild( button );
3112
+ }
3097
3113
 
3098
- if( this.root.lastChild && this.root.lastChild.right )
3099
- {
3100
- this.root.lastChild.before( button );
3101
- }
3102
- else if( options.float == "left" )
3103
- {
3104
- this.root.prepend( button );
3105
- }
3106
- else
3114
+ const _b = button.querySelector('a');
3115
+ _b.addEventListener("click", (e) => {
3116
+ if( callback && !disabled )
3107
3117
  {
3108
- this.root.appendChild( button );
3118
+ callback.call( this, _b, e );
3109
3119
  }
3120
+ });
3110
3121
 
3111
- const _b = button.querySelector('a');
3112
- _b.addEventListener("click", (e) => {
3113
- if(callback && !disabled)
3114
- callback.call( this, _b, e );
3115
- });
3116
- }
3122
+ this.buttons[ name ] = button;
3117
3123
  }
3118
3124
 
3119
3125
  /**
3120
3126
  * @method setButtonImage
3121
- * @param {String} title
3127
+ * @param {String} name
3122
3128
  * @param {String} src
3129
+ * @param {Function} callback
3130
+ * @param {Object} options
3123
3131
  */
3124
3132
 
3125
- setButtonImage( title, src, callback, options = {} ) {
3126
- const button = this.buttons[ title ];
3133
+ setButtonImage( name, src, callback, options = {} ) {
3134
+
3135
+ if( !name )
3136
+ {
3137
+ throw( "Set Button Name!" );
3138
+ }
3139
+
3140
+ let button = this.buttons[ name ];
3127
3141
  if( button )
3128
3142
  {
3129
- button.querySelector('a').className = "fa-solid" + " " + icon + " lexicon";
3143
+ button.querySelector('img').src = src;
3144
+ return;
3130
3145
  }
3131
- else
3146
+
3147
+ // Otherwise, create it
3148
+ button = document.createElement('div');
3149
+ const disabled = options.disabled ?? false;
3150
+ button.className = "lexmenubutton" + (disabled ? " disabled" : "");
3151
+ button.title = name;
3152
+ button.innerHTML = "<a><image src='" + src + "' class='lexicon' style='height:32px;'></a>";
3153
+
3154
+ if( options.float == "right" )
3132
3155
  {
3133
- let button = document.createElement('div');
3134
- const disabled = options.disabled ?? false;
3135
- button.className = "lexmenubutton" + (disabled ? " disabled" : "");
3136
- button.title = title ?? "";
3137
- button.innerHTML = "<a><image src='" + src + "' class='lexicon' style='height:32px;'></a>";
3138
- button.style.padding = "5px";
3139
- button.style.alignItems = "center";
3156
+ button.right = true;
3157
+ }
3140
3158
 
3141
- if( options.float == "right" )
3142
- {
3143
- button.right = true;
3144
- }
3159
+ if( this.root.lastChild && this.root.lastChild.right )
3160
+ {
3161
+ this.root.lastChild.before( button );
3162
+ }
3163
+ else if( options.float == "left" )
3164
+ {
3165
+ this.root.prepend( button );
3166
+ }
3167
+ else
3168
+ {
3169
+ this.root.appendChild( button );
3170
+ }
3145
3171
 
3146
- if( this.root.lastChild && this.root.lastChild.right )
3147
- {
3148
- this.root.lastChild.before( button );
3149
- }
3150
- else if( options.float == "left" )
3151
- {
3152
- this.root.prepend( button );
3153
- }
3154
- else
3172
+ const _b = button.querySelector('a');
3173
+ _b.addEventListener("click", (e) => {
3174
+ if( callback && !disabled )
3155
3175
  {
3156
- this.root.appendChild( button );
3176
+ callback.call( this, _b, e );
3157
3177
  }
3178
+ });
3158
3179
 
3159
- const _b = button.querySelector('a');
3160
- _b.addEventListener("click", (e) => {
3161
- if(callback && !disabled)
3162
- callback.call( this, _b, e );
3163
- });
3164
- }
3180
+ this.buttons[ name ] = button;
3165
3181
  }
3166
3182
 
3167
3183
  /**
@@ -3273,11 +3289,13 @@ class SideBar {
3273
3289
  * headerIcon: Icon to be shown as avatar (from LX.ICONS)
3274
3290
  * headerTitle: Header title
3275
3291
  * headerSubtitle: Header subtitle
3292
+ * header: HTMLElement to add a custom header
3276
3293
  * skipFooter: Do not use sidebar footer [false]
3277
3294
  * footerImg: Image to be shown as avatar
3278
3295
  * footerIcon: Icon to be shown as avatar (from LX.ICONS)
3279
3296
  * footerTitle: Footer title
3280
3297
  * footerSubtitle: Footer subtitle
3298
+ * footer: HTMLElement to add a custom footer
3281
3299
  * collapsable: Sidebar can toggle between collapsed/expanded [true]
3282
3300
  * collapseToIcons: When Sidebar collapses, icons remains visible [true]
3283
3301
  * onHeaderPressed: Function to call when header is pressed
@@ -3309,62 +3327,17 @@ class SideBar {
3309
3327
 
3310
3328
  }, 10 );
3311
3329
 
3312
- // This account for header, footer and all inner paddings
3313
- let contentOffset = 32;
3314
-
3315
3330
  // Header
3316
3331
  if( !( options.skipHeader ?? false ) )
3317
3332
  {
3318
- this.header = document.createElement( 'div' );
3333
+ this.header = options.header ?? this._generateDefaultHeader( options );
3334
+ console.assert( this.header.constructor === HTMLDivElement, "Use an HTMLDivElement to build your custom header" );
3319
3335
  this.header.className = "lexsidebarheader";
3320
3336
  this.root.appendChild( this.header );
3321
3337
 
3322
- this.header.addEventListener( "click", e => {
3323
- if( this.collapsed )
3324
- {
3325
- e.preventDefault();
3326
- e.stopPropagation();
3327
- this.toggleCollapsed();
3328
- }
3329
- else if( options.onHeaderPressed )
3330
- {
3331
- options.onHeaderPressed( e );
3332
- }
3333
- } );
3334
-
3335
- const avatar = document.createElement( 'span' );
3336
- avatar.className = "lexavatar";
3337
- this.header.appendChild( avatar );
3338
-
3339
- if( options.headerImage )
3340
- {
3341
- const avatarImg = document.createElement( 'img' );
3342
- avatarImg.src = options.headerImage;
3343
- avatar.appendChild( avatarImg );
3344
- }
3345
- else if( options.headerIcon )
3346
- {
3347
- const avatarIcon = LX.makeIcon( options.headerIcon );
3348
- avatar.appendChild( avatarIcon );
3349
- }
3350
-
3351
- // Info
3352
- {
3353
- const info = document.createElement( 'div' );
3354
- this.header.appendChild( info );
3355
-
3356
- const infoText = document.createElement( 'span' );
3357
- infoText.innerHTML = options.headerTitle ?? "";
3358
- info.appendChild( infoText );
3359
-
3360
- const infoSubtext = document.createElement( 'span' );
3361
- infoSubtext.innerHTML = options.headerSubtitle ?? "";
3362
- info.appendChild( infoSubtext );
3363
- }
3364
-
3365
3338
  if( this.collapsable )
3366
3339
  {
3367
- const icon = LX.makeIcon( "Sidebar", "Toggle Sidebar" );
3340
+ const icon = LX.makeIcon( "Sidebar", "Toggle Sidebar", "toggler" );
3368
3341
  this.header.appendChild( icon );
3369
3342
 
3370
3343
  icon.addEventListener( "click", (e) => {
@@ -3373,12 +3346,10 @@ class SideBar {
3373
3346
  this.toggleCollapsed();
3374
3347
  } );
3375
3348
  }
3376
-
3377
- contentOffset += 52;
3378
3349
  }
3379
3350
 
3380
3351
  // Entry filter
3381
- if( !( options.filter ?? false ) )
3352
+ if( ( options.filter ?? false ) )
3382
3353
  {
3383
3354
  const panel = new Panel();
3384
3355
  panel.addText(null, "", (value, event) => {
@@ -3387,7 +3358,6 @@ class SideBar {
3387
3358
  }, { placeholder: "Search...", icon: "fa-solid fa-magnifying-glass" });
3388
3359
  this.filter = panel.root.childNodes[ 0 ];
3389
3360
  this.root.appendChild( this.filter );
3390
- contentOffset += 31;
3391
3361
  }
3392
3362
 
3393
3363
  // Content
@@ -3400,59 +3370,123 @@ class SideBar {
3400
3370
  // Footer
3401
3371
  if( !( options.skipFooter ?? false ) )
3402
3372
  {
3403
- this.footer = document.createElement( 'div' );
3373
+ this.footer = options.footer ?? this._generateDefaultFooter( options );
3374
+ console.assert( this.footer.constructor === HTMLDivElement, "Use an HTMLDivElement to build your custom footer" );
3404
3375
  this.footer.className = "lexsidebarfooter";
3405
3376
  this.root.appendChild( this.footer );
3377
+ }
3406
3378
 
3407
- this.footer.addEventListener( "click", e => {
3408
- if( options.onFooterPressed )
3409
- {
3410
- options.onFooterPressed( e );
3411
- }
3412
- } );
3379
+ // Set width depending on header/footer
3380
+ doAsync( () => {
3381
+ // This account for header, footer and all inner paddings
3382
+ const contentOffset = 32 + ( this.header?.offsetHeight ?? 0 ) +
3383
+ ( this.filter?.offsetHeight ?? 0 ) +
3384
+ ( this.footer?.offsetHeight ?? 0 );
3385
+ this.content.style.height = `calc(100% - ${ contentOffset }px)`;
3386
+ }, 10 );
3387
+
3388
+ this.items = [ ];
3389
+ this.icons = { };
3390
+ this.groups = { };
3391
+ }
3392
+
3393
+ _generateDefaultHeader( options ) {
3413
3394
 
3414
- const avatar = document.createElement( 'span' );
3415
- avatar.className = "lexavatar";
3416
- this.footer.appendChild( avatar );
3395
+ const header = document.createElement( 'div' );
3417
3396
 
3418
- if( options.footerImage )
3397
+ header.addEventListener( "click", e => {
3398
+ if( this.collapsed )
3419
3399
  {
3420
- const avatarImg = document.createElement( 'img' );
3421
- avatarImg.src = options.footerImage;
3422
- avatar.appendChild( avatarImg );
3400
+ e.preventDefault();
3401
+ e.stopPropagation();
3402
+ this.toggleCollapsed();
3423
3403
  }
3424
- else if( options.footerIcon )
3404
+ else if( options.onHeaderPressed )
3425
3405
  {
3426
- const avatarIcon = LX.makeIcon( options.footerIcon );
3427
- avatar.appendChild( avatarIcon );
3406
+ options.onHeaderPressed( e );
3428
3407
  }
3408
+ } );
3429
3409
 
3430
- // Info
3431
- {
3432
- const info = document.createElement( 'div' );
3433
- this.footer.appendChild( info );
3410
+ const avatar = document.createElement( 'span' );
3411
+ avatar.className = "lexavatar";
3412
+ header.appendChild( avatar );
3413
+
3414
+ if( options.headerImage )
3415
+ {
3416
+ const avatarImg = document.createElement( 'img' );
3417
+ avatarImg.src = options.headerImage;
3418
+ avatar.appendChild( avatarImg );
3419
+ }
3420
+ else if( options.headerIcon )
3421
+ {
3422
+ const avatarIcon = LX.makeIcon( options.headerIcon );
3423
+ avatar.appendChild( avatarIcon );
3424
+ }
3425
+
3426
+ // Info
3427
+ {
3428
+ const info = document.createElement( 'div' );
3429
+ info.className = "infodefault";
3430
+ header.appendChild( info );
3431
+
3432
+ const infoText = document.createElement( 'span' );
3433
+ infoText.innerHTML = options.headerTitle ?? "";
3434
+ info.appendChild( infoText );
3434
3435
 
3435
- const infoText = document.createElement( 'span' );
3436
- infoText.innerHTML = options.footerTitle ?? "";
3437
- info.appendChild( infoText );
3436
+ const infoSubtext = document.createElement( 'span' );
3437
+ infoSubtext.innerHTML = options.headerSubtitle ?? "";
3438
+ info.appendChild( infoSubtext );
3439
+ }
3440
+
3441
+ return header;
3442
+ }
3443
+
3444
+ _generateDefaultFooter( options ) {
3438
3445
 
3439
- const infoSubtext = document.createElement( 'span' );
3440
- infoSubtext.innerHTML = options.footerSubtitle ?? "";
3441
- info.appendChild( infoSubtext );
3446
+ const footer = document.createElement( 'div' );
3447
+
3448
+ footer.addEventListener( "click", e => {
3449
+ if( options.onFooterPressed )
3450
+ {
3451
+ options.onFooterPressed( e );
3442
3452
  }
3453
+ } );
3443
3454
 
3444
- const icon = LX.makeIcon( "MenuArrows" );
3445
- this.footer.appendChild( icon );
3455
+ const avatar = document.createElement( 'span' );
3456
+ avatar.className = "lexavatar";
3457
+ footer.appendChild( avatar );
3446
3458
 
3447
- contentOffset += 52;
3459
+ if( options.footerImage )
3460
+ {
3461
+ const avatarImg = document.createElement( 'img' );
3462
+ avatarImg.src = options.footerImage;
3463
+ avatar.appendChild( avatarImg );
3464
+ }
3465
+ else if( options.footerIcon )
3466
+ {
3467
+ const avatarIcon = LX.makeIcon( options.footerIcon );
3468
+ avatar.appendChild( avatarIcon );
3448
3469
  }
3449
3470
 
3450
- // Set width depending on header/footer
3451
- this.content.style.height = `calc(100% - ${ contentOffset }px)`;
3471
+ // Info
3472
+ {
3473
+ const info = document.createElement( 'div' );
3474
+ info.className = "infodefault";
3475
+ footer.appendChild( info );
3452
3476
 
3453
- this.items = [ ];
3454
- this.icons = { };
3455
- this.groups = { };
3477
+ const infoText = document.createElement( 'span' );
3478
+ infoText.innerHTML = options.footerTitle ?? "";
3479
+ info.appendChild( infoText );
3480
+
3481
+ const infoSubtext = document.createElement( 'span' );
3482
+ infoSubtext.innerHTML = options.footerSubtitle ?? "";
3483
+ info.appendChild( infoSubtext );
3484
+ }
3485
+
3486
+ const icon = LX.makeIcon( "MenuArrows" );
3487
+ footer.appendChild( icon );
3488
+
3489
+ return footer;
3456
3490
  }
3457
3491
 
3458
3492
  /**
@@ -3525,9 +3559,9 @@ class SideBar {
3525
3559
  * @param {String} path
3526
3560
  * @param {Object} options:
3527
3561
  * callback: Function to call on each item
3528
- * icon: Entry icon
3529
- * collapsable: Add entry as a collapsable section
3530
3562
  * className: Add class to the entry DOM element
3563
+ * collapsable: Add entry as a collapsable section
3564
+ * icon: Entry icon
3531
3565
  */
3532
3566
 
3533
3567
  add( path, options = {} ) {
@@ -3761,6 +3795,8 @@ class SideBar {
3761
3795
  entry.classList.add( "selected" );
3762
3796
  });
3763
3797
 
3798
+ const isCollapsable = options.collapsable != undefined ? options.collapsable : ( options.collapsable || item[ key ].length );
3799
+
3764
3800
  if( options.action )
3765
3801
  {
3766
3802
  const actionIcon = LX.makeIcon( options.action.icon ?? "MoreHorizontal", options.action.name );
@@ -3773,7 +3809,7 @@ class SideBar {
3773
3809
  if( f ) f.call( this, key, e );
3774
3810
  } );
3775
3811
  }
3776
- else if( options.collapsable )
3812
+ else if( isCollapsable )
3777
3813
  {
3778
3814
  const collapsableContent = document.createElement( 'div' );
3779
3815
  Object.assign( collapsableContent.style, { width: "100%", display: "none" } );
@@ -3808,7 +3844,12 @@ class SideBar {
3808
3844
  let subentryContainer = document.createElement( 'div' );
3809
3845
  subentryContainer.className = "lexsidebarsubentrycontainer";
3810
3846
 
3811
- if( currentGroup )
3847
+ if( isCollapsable )
3848
+ {
3849
+ this.collapseContainer.appendChild( subentryContainer )
3850
+ delete this.collapseContainer;
3851
+ }
3852
+ else if( currentGroup )
3812
3853
  {
3813
3854
  currentGroup.appendChild( subentryContainer );
3814
3855
  }
@@ -3878,31 +3919,34 @@ class Widget {
3878
3919
  static CHECKBOX = 5;
3879
3920
  static TOGGLE = 6;
3880
3921
  static RADIO = 7;
3881
- static COLOR = 8;
3882
- static RANGE = 9;
3883
- static NUMBER = 10;
3884
- static TITLE = 11;
3885
- static VECTOR = 12;
3886
- static TREE = 13;
3887
- static PROGRESS = 14;
3888
- static FILE = 15;
3889
- static LAYERS = 16;
3890
- static ARRAY = 17;
3891
- static LIST = 18;
3892
- static TAGS = 19;
3893
- static CURVE = 20;
3894
- static CARD = 21;
3895
- static IMAGE = 22;
3896
- static CONTENT = 23;
3897
- static CUSTOM = 24;
3898
- static SEPARATOR = 25;
3899
- static KNOB = 26;
3900
- static SIZE = 27;
3901
- static PAD = 28;
3902
- static FORM = 29;
3903
- static DIAL = 30;
3904
- static COUNTER = 31;
3905
- static TABLE = 32;
3922
+ static BUTTONS = 8;
3923
+ static COLOR = 9;
3924
+ static RANGE = 10;
3925
+ static NUMBER = 11;
3926
+ static TITLE = 12;
3927
+ static VECTOR = 13;
3928
+ static TREE = 14;
3929
+ static PROGRESS = 15;
3930
+ static FILE = 16;
3931
+ static LAYERS = 17;
3932
+ static ARRAY = 18;
3933
+ static LIST = 19;
3934
+ static TAGS = 20;
3935
+ static CURVE = 21;
3936
+ static CARD = 22;
3937
+ static IMAGE = 23;
3938
+ static CONTENT = 24;
3939
+ static CUSTOM = 25;
3940
+ static SEPARATOR = 26;
3941
+ static KNOB = 27;
3942
+ static SIZE = 28;
3943
+ static PAD = 29;
3944
+ static FORM = 30;
3945
+ static DIAL = 31;
3946
+ static COUNTER = 32;
3947
+ static TABLE = 33;
3948
+ static TABS = 34;
3949
+ static BLANK = 35;
3906
3950
 
3907
3951
  static NO_CONTEXT_TYPES = [
3908
3952
  Widget.BUTTON,
@@ -3911,10 +3955,11 @@ class Widget {
3911
3955
  Widget.PROGRESS
3912
3956
  ];
3913
3957
 
3914
- constructor( name, type, options ) {
3958
+ constructor( name, type, value, options ) {
3915
3959
  this.name = name;
3916
3960
  this.type = type;
3917
3961
  this.options = options;
3962
+ this._initialValue = value;
3918
3963
  }
3919
3964
 
3920
3965
  value() {
@@ -3927,22 +3972,31 @@ class Widget {
3927
3972
  console.warn( "Can't get value of " + this.typeName() );
3928
3973
  }
3929
3974
 
3930
- set( value, skipCallback = false, signalName = "" ) {
3975
+ set( value, skipCallback, event ) {
3931
3976
 
3932
3977
  if( this.onSetValue )
3933
3978
  {
3934
- return this.onSetValue( value, skipCallback );
3979
+ let resetButton = this.domEl.querySelector( ".lexwidgetname .lexicon" );
3980
+ if( resetButton )
3981
+ {
3982
+ resetButton.style.display = ( value != this.value() ? "block" : "none" );
3983
+ resetButton.style.display = ( value != this._initialValue ? "block" : "none" );
3984
+ }
3985
+
3986
+ return this.onSetValue( value, skipCallback ?? false, event );
3935
3987
  }
3936
3988
 
3937
3989
  console.warn("Can't set value of " + this.typeName());
3938
3990
  }
3939
3991
 
3940
- oncontextmenu(e) {
3992
+ oncontextmenu( e ) {
3941
3993
 
3942
- if( Widget.NO_CONTEXT_TYPES.includes(this.type) )
3994
+ if( Widget.NO_CONTEXT_TYPES.includes( this.type ) )
3995
+ {
3943
3996
  return;
3997
+ }
3944
3998
 
3945
- addContextMenu(this.typeName(), e, c => {
3999
+ addContextMenu( this.typeName(), e, c => {
3946
4000
  c.add("Copy", () => { this.copy() });
3947
4001
  c.add("Paste", { disabled: !this._can_paste(), callback: () => { this.paste() } } );
3948
4002
  });
@@ -3999,6 +4053,8 @@ class Widget {
3999
4053
  case Widget.DIAL: return "Dial";
4000
4054
  case Widget.COUNTER: return "Counter";
4001
4055
  case Widget.TABLE: return "Table";
4056
+ case Widget.TABS: return "Tabs";
4057
+ case Widget.BLANK: return "Blank";
4002
4058
  case Widget.CUSTOM: return this.customName;
4003
4059
  }
4004
4060
 
@@ -4012,26 +4068,31 @@ class Widget {
4012
4068
 
4013
4069
  LX.Widget = Widget;
4014
4070
 
4015
- function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
4071
+ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
4016
4072
  {
4017
4073
  let custom_idx = simple_guidGenerator();
4018
4074
 
4019
- Panel.prototype[ 'add' + custom_widget_name ] = function( name, instance, callback ) {
4075
+ Panel.prototype[ 'add' + customWidgetName ] = function( name, instance, callback ) {
4020
4076
 
4021
- let widget = this.create_widget(name, Widget.CUSTOM, options);
4022
- widget.customName = custom_widget_name;
4077
+ let widget = this._createWidget( Widget.CUSTOM, name, null, options );
4078
+ widget.customName = customWidgetName;
4023
4079
  widget.customIdx = custom_idx;
4080
+
4024
4081
  widget.onGetValue = () => {
4025
4082
  return instance;
4026
4083
  };
4027
- widget.onSetValue = ( newValue, skipCallback ) => {
4084
+
4085
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
4028
4086
  instance = newValue;
4029
4087
  refresh_widget();
4030
4088
  element.querySelector( ".lexcustomitems" ).toggleAttribute( 'hidden', false );
4031
- if( !skipCallback ) this._trigger( new IEvent( name, instance, null ), callback );
4089
+ if( !skipCallback )
4090
+ {
4091
+ this._trigger( new IEvent( name, instance, event ), callback );
4092
+ }
4032
4093
  };
4033
4094
 
4034
- let element = widget.domEl;
4095
+ const element = widget.domEl;
4035
4096
  element.style.flexWrap = "wrap";
4036
4097
 
4037
4098
  let container, custom_widgets;
@@ -4041,8 +4102,10 @@ function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
4041
4102
 
4042
4103
  const refresh_widget = () => {
4043
4104
 
4044
- if(instance)
4105
+ if( instance )
4106
+ {
4045
4107
  widget.instance = instance = Object.assign(deepCopy(default_instance), instance);
4108
+ }
4046
4109
 
4047
4110
  if(container) container.remove();
4048
4111
  if(custom_widgets) custom_widgets.remove();
@@ -4054,7 +4117,7 @@ function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
4054
4117
  this.queue(container);
4055
4118
 
4056
4119
  let buttonName = "<a class='fa-solid " + (options.icon ?? "fa-cube") + "' style='float:left'></a>";
4057
- buttonName += custom_widget_name + (!instance ? " [empty]" : "");
4120
+ buttonName += customWidgetName + (!instance ? " [empty]" : "");
4058
4121
  // Add alwayis icon to keep spacing right
4059
4122
  buttonName += "<a class='fa-solid " + (instance ? "fa-bars-staggered" : " ") + " menu' style='float:right; width:5%;'></a>";
4060
4123
 
@@ -4066,7 +4129,7 @@ function ADD_CUSTOM_WIDGET( custom_widget_name, options = {} )
4066
4129
  else
4067
4130
  {
4068
4131
  addContextMenu(null, event, c => {
4069
- c.add("New " + custom_widget_name, () => {
4132
+ c.add("New " + customWidgetName, () => {
4070
4133
  instance = {};
4071
4134
  refresh_widget();
4072
4135
  element.querySelector(".lexcustomitems").toggleAttribute('hidden', false);
@@ -4178,10 +4241,10 @@ class NodeTree {
4178
4241
  _create_item( parent, node, level = 0, selectedId ) {
4179
4242
 
4180
4243
  const that = this;
4181
- const node_filter_input = this.domEl.querySelector( "#lexnodetree_filter" );
4244
+ const nodeFilterInput = this.domEl.querySelector( "#lexnodetree_filter" );
4182
4245
 
4183
4246
  node.children = node.children ?? [];
4184
- if( node_filter_input && !node.id.includes( node_filter_input.value ) || (selectedId != undefined) && selectedId != node.id )
4247
+ if( nodeFilterInput && !node.id.includes( nodeFilterInput.value ) || (selectedId != undefined) && selectedId != node.id )
4185
4248
  {
4186
4249
  for( var i = 0; i < node.children.length; ++i )
4187
4250
  {
@@ -4421,7 +4484,7 @@ class NodeTree {
4421
4484
  const nameInput = document.createElement( "input" );
4422
4485
  nameInput.toggleAttribute( "hidden", !node.rename );
4423
4486
  nameInput.value = node.id;
4424
- item.appendChild(nameInput);
4487
+ item.appendChild( nameInput );
4425
4488
 
4426
4489
  if( node.rename )
4427
4490
  {
@@ -4429,7 +4492,7 @@ class NodeTree {
4429
4492
  nameInput.focus();
4430
4493
  }
4431
4494
 
4432
- nameInput.addEventListener("keyup", function(e){
4495
+ nameInput.addEventListener("keyup", function( e ) {
4433
4496
  if( e.key == "Enter" )
4434
4497
  {
4435
4498
  this.value = this.value.replace(/\s/g, '_');
@@ -4452,7 +4515,7 @@ class NodeTree {
4452
4515
  }
4453
4516
  });
4454
4517
 
4455
- nameInput.addEventListener("blur", function(e){
4518
+ nameInput.addEventListener("blur", function( e ) {
4456
4519
  delete node.rename;
4457
4520
  that.refresh();
4458
4521
  });
@@ -4460,7 +4523,7 @@ class NodeTree {
4460
4523
  if( this.options.draggable ?? true )
4461
4524
  {
4462
4525
  // Drag nodes
4463
- if(parent) // Root doesn't move!
4526
+ if( parent ) // Root doesn't move!
4464
4527
  {
4465
4528
  item.addEventListener("dragstart", e => {
4466
4529
  window.__tree_node_dragged = node;
@@ -4527,7 +4590,7 @@ class NodeTree {
4527
4590
  // Show/hide children
4528
4591
  if( isParent )
4529
4592
  {
4530
- item.querySelector('a.hierarchy').addEventListener("click", function(e) {
4593
+ item.querySelector('a.hierarchy').addEventListener("click", function( e ) {
4531
4594
 
4532
4595
  handled = true;
4533
4596
  e.stopImmediatePropagation();
@@ -4545,40 +4608,44 @@ class NodeTree {
4545
4608
 
4546
4609
  // Add button icons
4547
4610
 
4611
+ const inputContainer = document.createElement( "div" );
4612
+ item.appendChild( inputContainer );
4613
+
4614
+ if( node.actions )
4615
+ {
4616
+ for( let i = 0; i < node.actions.length; ++i )
4617
+ {
4618
+ let a = node.actions[ i ];
4619
+ let actionEl = document.createElement('a');
4620
+ actionEl.className = "lexicon " + a.icon;
4621
+ actionEl.title = a.name;
4622
+ actionEl.addEventListener("click", function( e ) {
4623
+ a.callback( node, actionEl );
4624
+ e.stopPropagation();
4625
+ });
4626
+
4627
+ inputContainer.appendChild( actionEl );
4628
+ }
4629
+ }
4630
+
4548
4631
  if( !node.skipVisibility ?? false )
4549
4632
  {
4550
- let visibility = document.createElement('a');
4551
- visibility.className = "itemicon fa-solid fa-eye" + (!node.visible ? "-slash" : "");
4633
+ let visibility = document.createElement( 'a' );
4634
+ visibility.className = "lexicon fa-solid fa-eye" + ( !node.visible ? "-slash" : "" );
4552
4635
  visibility.title = "Toggle visible";
4553
- visibility.addEventListener("click", function(e) {
4636
+ visibility.addEventListener("click", function( e ) {
4554
4637
  e.stopPropagation();
4555
4638
  node.visible = node.visible === undefined ? false : !node.visible;
4556
- this.className = "itemicon fa-solid fa-eye" + (!node.visible ? "-slash" : "");
4639
+ this.className = "lexicon fa-solid fa-eye" + ( !node.visible ? "-slash" : "" );
4557
4640
  // Trigger visibility event
4558
4641
  if( that.onevent )
4559
4642
  {
4560
- const event = new TreeEvent(TreeEvent.NODE_VISIBILITY, node, node.visible);
4643
+ const event = new TreeEvent( TreeEvent.NODE_VISIBILITY, node, node.visible );
4561
4644
  that.onevent( event );
4562
4645
  }
4563
4646
  });
4564
4647
 
4565
- item.appendChild(visibility);
4566
- }
4567
-
4568
- if( node.actions )
4569
- {
4570
- for( var i = 0; i < node.actions.length; ++i )
4571
- {
4572
- let a = node.actions[i];
4573
- var actionEl = document.createElement('a');
4574
- actionEl.className = "itemicon " + a.icon;
4575
- actionEl.title = a.name;
4576
- actionEl.addEventListener("click", function(e) {
4577
- a.callback(node, actionEl);
4578
- e.stopPropagation();
4579
- });
4580
- item.appendChild(actionEl);
4581
- }
4648
+ inputContainer.appendChild( visibility );
4582
4649
  }
4583
4650
 
4584
4651
  if( selectedId != undefined && node.id == selectedId )
@@ -4639,28 +4706,35 @@ class Panel {
4639
4706
  */
4640
4707
 
4641
4708
  constructor( options = {} ) {
4709
+
4642
4710
  var root = document.createElement('div');
4643
4711
  root.className = "lexpanel";
4712
+
4644
4713
  if( options.id )
4714
+ {
4645
4715
  root.id = options.id;
4716
+ }
4717
+
4646
4718
  if( options.className )
4719
+ {
4647
4720
  root.className += " " + options.className;
4721
+ }
4648
4722
 
4649
4723
  root.style.width = options.width || "calc( 100% - 6px )";
4650
4724
  root.style.height = options.height || "100%";
4651
- Object.assign(root.style, options.style ?? {});
4725
+ Object.assign( root.style, options.style ?? {} );
4652
4726
 
4653
4727
  this._inline_widgets_left = -1;
4654
4728
  this._inline_queued_container = null;
4655
4729
 
4656
4730
  this.root = root;
4657
4731
 
4658
- this.onevent = (e => {});
4732
+ this.onevent = ( e => {} );
4659
4733
 
4660
4734
  // branches
4661
- this.branch_open = false;
4735
+ this._branchOpen = false;
4662
4736
  this.branches = [];
4663
- this.current_branch = null;
4737
+ this._currentBranch = null;
4664
4738
  this.widgets = {};
4665
4739
  this._queue = []; // Append widgets in other locations
4666
4740
  }
@@ -4701,13 +4775,9 @@ class Panel {
4701
4775
 
4702
4776
  attach( content ) {
4703
4777
 
4704
- if(!content)
4705
- throw("no content to attach");
4706
-
4778
+ console.assert( content, "No content to attach!" );
4707
4779
  content.parent = this;
4708
- let element = content.root ? content.root : content;
4709
- //this.root.style.maxHeight = "800px"; // limit size when attaching stuff from outside
4710
- this.root.appendChild( element );
4780
+ this.root.appendChild( content.root ? content.root : content );
4711
4781
  }
4712
4782
 
4713
4783
  /**
@@ -4716,18 +4786,18 @@ class Panel {
4716
4786
 
4717
4787
  clear() {
4718
4788
 
4719
- this.branch_open = false;
4789
+ this._branchOpen = false;
4720
4790
  this.branches = [];
4721
- this.current_branch = null;
4791
+ this._currentBranch = null;
4722
4792
 
4723
4793
  for( let w in this.widgets )
4724
4794
  {
4725
- if( this.widgets[w].options && this.widgets[w].options.signal )
4795
+ if( this.widgets[ w ].options && this.widgets[ w ].options.signal )
4726
4796
  {
4727
- const signal = this.widgets[w].options.signal;
4797
+ const signal = this.widgets[ w ].options.signal;
4728
4798
  for( let i = 0; i < LX.signals[signal].length; i++ )
4729
4799
  {
4730
- if( LX.signals[signal][i] == this.widgets[w] )
4800
+ if( LX.signals[signal][i] == this.widgets[ w ] )
4731
4801
  {
4732
4802
  LX.signals[signal] = [...LX.signals[signal].slice(0, i), ...LX.signals[signal].slice(i+1)];
4733
4803
  }
@@ -4739,7 +4809,7 @@ class Panel {
4739
4809
  {
4740
4810
  for( let w = 0; w < this.signals.length; w++ )
4741
4811
  {
4742
- let widget = Object.values(this.signals[w])[0];
4812
+ let widget = Object.values(this.signals[ w ])[0];
4743
4813
  let signal = widget.options.signal;
4744
4814
  for( let i = 0; i < LX.signals[signal].length; i++ )
4745
4815
  {
@@ -4764,7 +4834,7 @@ class Panel {
4764
4834
  sameLine( number ) {
4765
4835
 
4766
4836
  this._inline_queued_container = this.queuedContainer;
4767
- this._inline_widgets_left = number || Infinity;
4837
+ this._inline_widgets_left = ( number || Infinity );
4768
4838
  }
4769
4839
 
4770
4840
  /**
@@ -4774,7 +4844,7 @@ class Panel {
4774
4844
 
4775
4845
  endLine( justifyContent ) {
4776
4846
 
4777
- if( this._inline_widgets_left == -1)
4847
+ if( this._inline_widgets_left == -1 )
4778
4848
  {
4779
4849
  console.warn("No pending widgets to be inlined!");
4780
4850
  return;
@@ -4796,27 +4866,37 @@ class Panel {
4796
4866
  // Push all elements single element or Array[element, container]
4797
4867
  for( let item of this._inlineWidgets )
4798
4868
  {
4799
- const is_pair = item.constructor == Array;
4869
+ const isPair = ( item.constructor == Array );
4800
4870
 
4801
- if(is_pair)
4871
+ if( isPair )
4802
4872
  {
4803
4873
  // eg. an array, inline items appended later to
4804
- if( this._inline_queued_container)
4805
- this._inlineContainer.appendChild( item[0] );
4874
+ if( this._inline_queued_container )
4875
+ {
4876
+ this._inlineContainer.appendChild( item[ 0 ] );
4877
+ }
4806
4878
  // eg. a dropdown, item is appended to parent, not to inline cont.
4807
4879
  else
4808
- item[1].appendChild(item[0]);
4880
+ {
4881
+ item[ 1 ].appendChild( item[ 0 ] );
4882
+ }
4809
4883
  }
4810
4884
  else
4885
+ {
4811
4886
  this._inlineContainer.appendChild( item );
4887
+ }
4812
4888
  }
4813
4889
 
4814
- if(!this._inline_queued_container)
4890
+ if( !this._inline_queued_container )
4815
4891
  {
4816
- if( this.current_branch)
4817
- this.current_branch.content.appendChild( this._inlineContainer );
4892
+ if( this._currentBranch )
4893
+ {
4894
+ this._currentBranch.content.appendChild( this._inlineContainer );
4895
+ }
4818
4896
  else
4897
+ {
4819
4898
  this.root.appendChild( this._inlineContainer );
4899
+ }
4820
4900
  }
4821
4901
  else
4822
4902
  {
@@ -4840,20 +4920,24 @@ class Panel {
4840
4920
 
4841
4921
  branch( name, options = {} ) {
4842
4922
 
4843
- if( this.branch_open )
4923
+ if( this._branchOpen )
4924
+ {
4844
4925
  this.merge();
4926
+ }
4845
4927
 
4846
4928
  // Create new branch
4847
- var branch = new Branch(name, options);
4929
+ var branch = new Branch( name, options );
4848
4930
  branch.panel = this;
4849
4931
 
4850
4932
  // Declare new open
4851
- this.branch_open = true;
4852
- this.current_branch = branch;
4933
+ this._branchOpen = true;
4934
+ this._currentBranch = branch;
4853
4935
 
4854
4936
  // Append to panel
4855
- if( this.branches.length == 0)
4937
+ if( this.branches.length == 0 )
4938
+ {
4856
4939
  branch.root.classList.add('first');
4940
+ }
4857
4941
 
4858
4942
  // This is the last!
4859
4943
  this.root.querySelectorAll(".lexbranch.last").forEach( e => { e.classList.remove("last"); } );
@@ -4865,16 +4949,15 @@ class Panel {
4865
4949
  // Add widget filter
4866
4950
  if( options.filter )
4867
4951
  {
4868
- this._addFilter( options.filter, {callback: this._searchWidgets.bind(this, branch.name)} );
4952
+ this._addFilter( options.filter, { callback: this._searchWidgets.bind( this, branch.name ) } );
4869
4953
  }
4870
4954
 
4871
4955
  return branch;
4872
4956
  }
4873
4957
 
4874
4958
  merge() {
4875
-
4876
- this.branch_open = false;
4877
- this.current_branch = null;
4959
+ this._branchOpen = false;
4960
+ this._currentBranch = null;
4878
4961
  }
4879
4962
 
4880
4963
  _pick( arg, def ) {
@@ -4900,9 +4983,16 @@ class Panel {
4900
4983
  Panel Widgets
4901
4984
  */
4902
4985
 
4903
- create_widget( name, type, options = {} ) {
4986
+ _createWidget( type, name, value, options = {} ) {
4987
+
4988
+ if( !LX.CREATED_INSTANCES )
4989
+ {
4990
+ LX.CREATED_INSTANCES = [];
4991
+ }
4992
+
4993
+ LX.CREATED_INSTANCES[ type ] = true;
4904
4994
 
4905
- let widget = new Widget( name, type, options );
4995
+ let widget = new Widget( name, type, value, options );
4906
4996
 
4907
4997
  let element = document.createElement( 'div' );
4908
4998
  element.className = "lexwidget";
@@ -4916,7 +5006,7 @@ class Panel {
4916
5006
 
4917
5007
  if( type != Widget.TITLE )
4918
5008
  {
4919
- element.style.width = "calc(100% - " + (this.current_branch || type == Widget.FILE ? 10 : 20) + "px)";
5009
+ element.style.width = "calc(100% - " + (this._currentBranch || type == Widget.FILE || type == Widget.TREE ? 10 : 20) + "px)";
4920
5010
 
4921
5011
  if( options.width )
4922
5012
  {
@@ -4938,7 +5028,7 @@ class Panel {
4938
5028
 
4939
5029
  if( name != undefined )
4940
5030
  {
4941
- if( !(options.hideName ?? false) )
5031
+ if( !( options.hideName ?? false ) )
4942
5032
  {
4943
5033
  let domName = document.createElement( 'div' );
4944
5034
  domName.className = "lexwidgetname";
@@ -4946,10 +5036,10 @@ class Panel {
4946
5036
  {
4947
5037
  domName.classList.add( "float-" + options.justifyName );
4948
5038
  }
4949
- domName.innerHTML = name || "";
5039
+ domName.innerHTML = name;
4950
5040
  domName.title = options.title ?? domName.innerHTML;
4951
5041
  domName.style.width = options.nameWidth || LX.DEFAULT_NAME_WIDTH;
4952
- element.appendChild(domName);
5042
+ element.appendChild( domName );
4953
5043
  element.domName = domName;
4954
5044
 
4955
5045
  // Copy-paste info
@@ -4957,10 +5047,23 @@ class Panel {
4957
5047
  e.preventDefault();
4958
5048
  widget.oncontextmenu( e );
4959
5049
  });
5050
+
5051
+ if( !( options.skipReset ?? false ) && ( value != null ) )
5052
+ {
5053
+ Panel._add_reset_property( domName, function( e ) {
5054
+ widget.set( widget._initialValue, false, e );
5055
+ // Og value, don't show it
5056
+ this.style.display = "none";
5057
+ });
5058
+ }
4960
5059
  }
4961
5060
 
4962
5061
  this.widgets[ name ] = widget;
4963
5062
  }
5063
+ else
5064
+ {
5065
+ options.hideName = true;
5066
+ }
4964
5067
 
4965
5068
  if( options.signal )
4966
5069
  {
@@ -4987,13 +5090,13 @@ class Panel {
4987
5090
  }
4988
5091
  else if( !this.queuedContainer )
4989
5092
  {
4990
- if( this.current_branch )
5093
+ if( this._currentBranch )
4991
5094
  {
4992
5095
  if( !options.skipWidget )
4993
5096
  {
4994
- this.current_branch.widgets.push( widget );
5097
+ this._currentBranch.widgets.push( widget );
4995
5098
  }
4996
- this.current_branch.content.appendChild( el );
5099
+ this._currentBranch.content.appendChild( el );
4997
5100
  }
4998
5101
  else
4999
5102
  {
@@ -5054,30 +5157,32 @@ class Panel {
5054
5157
  options.skipWidget = options.skipWidget ?? true;
5055
5158
  options.skipInlineCount = true;
5056
5159
 
5057
- let widget = this.create_widget(null, Widget.TEXT, options);
5058
- let element = widget.domEl;
5160
+ let widget = this._createWidget( Widget.TEXT, null, null, options );
5161
+ const element = widget.domEl;
5059
5162
  element.className += " lexfilter noname";
5060
5163
 
5061
5164
  let input = document.createElement('input');
5062
5165
  input.className = 'lexinput-filter';
5063
- input.setAttribute("placeholder", options.placeholder);
5166
+ input.setAttribute( "placeholder", options.placeholder );
5064
5167
  input.style.width = "calc( 100% - 17px )";
5065
5168
  input.value = options.filterValue || "";
5066
5169
 
5067
5170
  let searchIcon = document.createElement('a');
5068
5171
  searchIcon.className = "fa-solid fa-magnifying-glass";
5069
- element.appendChild(searchIcon);
5070
- element.appendChild(input);
5172
+ element.appendChild( searchIcon );
5173
+ element.appendChild( input );
5071
5174
 
5072
5175
  input.addEventListener("input", (e) => {
5073
- if(options.callback)
5074
- options.callback(input.value, e);
5176
+ if( options.callback )
5177
+ {
5178
+ options.callback( input.value, e );
5179
+ }
5075
5180
  });
5076
5181
 
5077
5182
  return element;
5078
5183
  }
5079
5184
 
5080
- _searchWidgets(branchName, value) {
5185
+ _searchWidgets( branchName, value ) {
5081
5186
 
5082
5187
  for( let b of this.branches )
5083
5188
  {
@@ -5151,10 +5256,14 @@ class Panel {
5151
5256
  _trigger( event, callback ) {
5152
5257
 
5153
5258
  if( callback )
5259
+ {
5154
5260
  callback.call( this, event.value, event.domEvent, event.name );
5261
+ }
5155
5262
 
5156
5263
  if( this.onevent )
5264
+ {
5157
5265
  this.onevent.call( this, event );
5266
+ }
5158
5267
  }
5159
5268
 
5160
5269
  /**
@@ -5169,7 +5278,7 @@ class Panel {
5169
5278
  return this.branches.find( b => b.name == name );
5170
5279
  }
5171
5280
 
5172
- return this.current_branch;
5281
+ return this._currentBranch;
5173
5282
  }
5174
5283
 
5175
5284
  /**
@@ -5179,9 +5288,9 @@ class Panel {
5179
5288
 
5180
5289
  queue( domEl ) {
5181
5290
 
5182
- if( !domEl && this.current_branch)
5291
+ if( !domEl && this._currentBranch)
5183
5292
  {
5184
- domEl = this.current_branch.root;
5293
+ domEl = this._currentBranch.root;
5185
5294
  }
5186
5295
 
5187
5296
  if( this.queuedContainer )
@@ -5214,12 +5323,14 @@ class Panel {
5214
5323
 
5215
5324
  addBlank( height = 8, width ) {
5216
5325
 
5217
- let widget = this.create_widget(null, Widget.addBlank);
5326
+ let widget = this._createWidget( Widget.BLANK );
5218
5327
  widget.domEl.className += " blank";
5219
5328
  widget.domEl.style.height = height + "px";
5220
5329
 
5221
- if(width)
5330
+ if( width )
5331
+ {
5222
5332
  widget.domEl.style.width = width;
5333
+ }
5223
5334
 
5224
5335
  return widget;
5225
5336
  }
@@ -5237,13 +5348,12 @@ class Panel {
5237
5348
 
5238
5349
  addTitle( name, options = {} ) {
5239
5350
 
5240
- if( !name )
5241
- {
5242
- throw( "Can't create Title without text!" );
5243
- }
5351
+ console.assert( name, "Can't create Title Widget without text!" );
5352
+
5353
+ // Note: Titles are not registered in Panel.widgets by now
5354
+ let widget = this._createWidget( Widget.TITLE, null, null, options );
5244
5355
 
5245
- let widget = this.create_widget( null, Widget.TITLE, options );
5246
- let element = widget.domEl;
5356
+ const element = widget.domEl;
5247
5357
  element.className = "lextitle";
5248
5358
 
5249
5359
  if( options.icon )
@@ -5254,7 +5364,7 @@ class Panel {
5254
5364
  element.appendChild( icon );
5255
5365
  }
5256
5366
 
5257
- let text = document.createElement( "span");
5367
+ let text = document.createElement( "span" );
5258
5368
  text.innerText = name;
5259
5369
  element.appendChild( text );
5260
5370
 
@@ -5280,6 +5390,7 @@ class Panel {
5280
5390
  * @param {String} value Text value
5281
5391
  * @param {Function} callback Callback function on change
5282
5392
  * @param {*} options:
5393
+ * hideName: Don't use name as label [false]
5283
5394
  * disabled: Make the widget disabled [false]
5284
5395
  * required: Make the input required
5285
5396
  * placeholder: Add input placeholder
@@ -5293,33 +5404,43 @@ class Panel {
5293
5404
 
5294
5405
  addText( name, value, callback, options = {} ) {
5295
5406
 
5296
- let widget = this.create_widget( name, Widget.TEXT, options );
5407
+ let widget = this._createWidget( Widget.TEXT, name, String( value ), options );
5297
5408
 
5298
5409
  widget.onGetValue = () => {
5299
- return wValue.value;
5410
+ return value;
5300
5411
  };
5301
5412
 
5302
- widget.onSetValue = ( newValue, skipCallback ) => {
5303
- this.disabled ? wValue.innerText = newValue : wValue.value = newValue;
5304
- Panel._dispatch_event( wValue, "focusout", skipCallback );
5413
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5414
+
5415
+ if( !widget.valid( newValue ) || ( this._lastValueTriggered == newValue ) )
5416
+ {
5417
+ return;
5418
+ }
5419
+
5420
+ this._lastValueTriggered = value = newValue;
5421
+
5422
+ if( options.disabled )
5423
+ {
5424
+ wValue.innerText = newValue;
5425
+ }
5426
+ else
5427
+ {
5428
+ wValue.value = newValue;
5429
+ }
5430
+
5431
+ if( !skipCallback )
5432
+ {
5433
+ this._trigger( new IEvent( name, newValue, event ), callback );
5434
+ }
5305
5435
  };
5306
5436
 
5307
- widget.valid = () => {
5308
- if( wValue.pattern == "" ) { return true; }
5437
+ widget.valid = ( v ) => {
5438
+ if( !v.length || wValue.pattern == "" ) return true;
5309
5439
  const regexp = new RegExp( wValue.pattern );
5310
- return regexp.test( wValue.value );
5440
+ return regexp.test( v );
5311
5441
  };
5312
5442
 
5313
- let element = widget.domEl;
5314
-
5315
- // Add reset functionality
5316
- if( widget.name && !( options.skipReset ?? false ) ) {
5317
- Panel._add_reset_property( element.domName, function() {
5318
- wValue.value = wValue.iValue;
5319
- this.style.display = "none";
5320
- Panel._dispatch_event( wValue, "focusout" );
5321
- } );
5322
- }
5443
+ const element = widget.domEl;
5323
5444
 
5324
5445
  // Add widget value
5325
5446
 
@@ -5342,7 +5463,7 @@ class Panel {
5342
5463
  wValue.type = options.type || "";
5343
5464
  wValue.value = wValue.iValue = value || "";
5344
5465
  wValue.style.width = "100%";
5345
- wValue.style.textAlign = options.float ?? "";
5466
+ wValue.style.textAlign = ( options.float ?? "" );
5346
5467
 
5347
5468
  wValue.setAttribute( "placeholder", options.placeholder ?? "" );
5348
5469
 
@@ -5356,41 +5477,25 @@ class Panel {
5356
5477
  wValue.setAttribute( "pattern", options.pattern );
5357
5478
  }
5358
5479
 
5359
- var resolve = ( function( val, event ) {
5360
-
5361
- if( !widget.valid() || ( this._lastValueTriggered == val ) )
5362
- {
5363
- return;
5364
- }
5365
-
5366
- const skipCallback = event.detail;
5367
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
5368
- if( btn ) btn.style.display = ( val != wValue.iValue ? "block" : "none" );
5369
- if( !skipCallback )
5370
- {
5371
- this._trigger( new IEvent( name, val, event ), callback );
5372
- }
5373
-
5374
- this._lastValueTriggered = val;
5480
+ const trigger = options.trigger ?? "default";
5375
5481
 
5376
- }).bind( this );
5377
-
5378
- const trigger = options.trigger ?? 'default';
5379
-
5380
- if( trigger == 'default' )
5482
+ if( trigger == "default" )
5381
5483
  {
5382
5484
  wValue.addEventListener( "keyup", function( e ){
5383
- if(e.key == 'Enter')
5384
- resolve( e.target.value, e );
5485
+ if( e.key == "Enter" )
5486
+ {
5487
+ wValue.blur();
5488
+ }
5385
5489
  });
5490
+
5386
5491
  wValue.addEventListener( "focusout", function( e ){
5387
- resolve( e.target.value, e );
5492
+ widget.set( e.target.value, false, e );
5388
5493
  });
5389
5494
  }
5390
- else if( trigger == 'input' )
5495
+ else if( trigger == "input" )
5391
5496
  {
5392
5497
  wValue.addEventListener("input", function( e ){
5393
- resolve( e.target.value, e );
5498
+ widget.set( e.target.value, false, e );
5394
5499
  });
5395
5500
  }
5396
5501
 
@@ -5406,14 +5511,17 @@ class Panel {
5406
5511
  container.appendChild( icon );
5407
5512
  }
5408
5513
 
5409
- } else
5514
+ }
5515
+ else
5410
5516
  {
5411
5517
  wValue = document.createElement( options.url ? 'a' : 'div' );
5518
+
5412
5519
  if( options.url )
5413
5520
  {
5414
5521
  wValue.href = options.url;
5415
5522
  wValue.target = "_blank";
5416
5523
  }
5524
+
5417
5525
  const icon = options.warning ? '<i class="fa-solid fa-triangle-exclamation"></i>' : '';
5418
5526
  wValue.innerHTML = ( icon + value ) || "";
5419
5527
  wValue.style.width = "100%";
@@ -5426,7 +5534,8 @@ class Panel {
5426
5534
  element.appendChild( container );
5427
5535
 
5428
5536
  // Remove branch padding and margins
5429
- if( !widget.name )
5537
+ const useNameAsLabel = !( options.hideName ?? false );
5538
+ if( !useNameAsLabel )
5430
5539
  {
5431
5540
  element.className += " noname";
5432
5541
  container.style.width = "100%";
@@ -5440,7 +5549,8 @@ class Panel {
5440
5549
  * @param {String} name Widget name
5441
5550
  * @param {String} value Text Area value
5442
5551
  * @param {Function} callback Callback function on change
5443
- * @param {*} options:
5552
+ * @param {Object} options:
5553
+ * hideName: Don't use name as label [false]
5444
5554
  * disabled: Make the widget disabled [false]
5445
5555
  * placeholder: Add input placeholder
5446
5556
  * trigger: Choose onchange trigger (default, input) [default]
@@ -5452,82 +5562,76 @@ class Panel {
5452
5562
 
5453
5563
  addTextArea( name, value, callback, options = {} ) {
5454
5564
 
5455
- let widget = this.create_widget( name, Widget.TEXTAREA, options );
5565
+ let widget = this._createWidget( Widget.TEXTAREA, name, value, options );
5456
5566
 
5457
5567
  widget.onGetValue = () => {
5458
- return wValue.value;
5459
- };
5460
- widget.onSetValue = ( newValue, skipCallback ) => {
5461
- wValue.value = newValue;
5462
- Panel._dispatch_event( wValue, "focusout", skipCallback );
5568
+ return value;
5463
5569
  };
5464
5570
 
5465
- let element = widget.domEl;
5571
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5466
5572
 
5467
- // Add reset functionality
5468
- if( widget.name && !( options.skipReset ?? false ) ) {
5469
- Panel._add_reset_property( element.domName, function() {
5470
- wValue.value = wValue.iValue;
5471
- this.style.display = "none";
5472
- Panel._dispatch_event( wValue, "focusout" );
5473
- });
5474
- }
5573
+ wValue.value = value = newValue;
5574
+
5575
+ if( !skipCallback )
5576
+ {
5577
+ this._trigger( new IEvent( name, newValue, event ), callback );
5578
+ }
5579
+ };
5580
+
5581
+ const element = widget.domEl;
5475
5582
 
5476
5583
  // Add widget value
5477
5584
 
5478
- let container = document.createElement( 'div' );
5585
+ let container = document.createElement( "div" );
5479
5586
  container.className = "lextextarea";
5480
5587
  container.style.width = options.inputWidth || "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + " )";
5481
5588
  container.style.height = options.height;
5482
5589
  container.style.display = "flex";
5483
5590
 
5484
- let wValue = document.createElement( 'textarea' );
5591
+ let wValue = document.createElement( "textarea" );
5485
5592
  wValue.value = wValue.iValue = value || "";
5486
5593
  wValue.style.width = "100%";
5487
5594
  wValue.style.textAlign = options.float ?? "";
5488
5595
  Object.assign( wValue.style, options.style ?? {} );
5489
5596
 
5490
- if( options.disabled ?? false ) wValue.setAttribute("disabled", true);
5491
- if( options.placeholder ) wValue.setAttribute("placeholder", options.placeholder);
5492
-
5493
- var resolve = (function( val, event ) {
5494
- const skipCallback = event.detail;
5495
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
5496
- if( btn ) btn.style.display = ( val != wValue.iValue ? "block" : "none" );
5497
- if( !skipCallback ) this._trigger( new IEvent( name, val, event ), callback );
5498
- }).bind(this);
5597
+ if( options.disabled ?? false ) wValue.setAttribute( "disabled", true );
5598
+ if( options.placeholder ) wValue.setAttribute( "placeholder", options.placeholder );
5499
5599
 
5500
- const trigger = options.trigger ?? 'default';
5600
+ const trigger = options.trigger ?? "default";
5501
5601
 
5502
- if(trigger == 'default')
5602
+ if( trigger == "default" )
5503
5603
  {
5504
- wValue.addEventListener("keyup", function(e){
5505
- if(e.key == 'Enter')
5506
- resolve(e.target.value, e);
5604
+ wValue.addEventListener("keyup", function( e ) {
5605
+ if( e.key == "Enter" )
5606
+ {
5607
+ wValue.blur();
5608
+ }
5507
5609
  });
5508
- wValue.addEventListener("focusout", function(e){
5509
- resolve(e.target.value, e);
5610
+
5611
+ wValue.addEventListener("focusout", function( e ) {
5612
+ widget.set( e.target.value, false, e );
5510
5613
  });
5511
5614
  }
5512
- else if(trigger == 'input')
5615
+ else if( trigger == "input" )
5513
5616
  {
5514
- wValue.addEventListener("input", function(e){
5515
- resolve(e.target.value, e);
5617
+ wValue.addEventListener("input", function( e ) {
5618
+ widget.set( e.target.value, false, e );
5516
5619
  });
5517
5620
  }
5518
5621
 
5519
- if(options.icon)
5622
+ if( options.icon )
5520
5623
  {
5521
5624
  let icon = document.createElement('a');
5522
5625
  icon.className = "inputicon " + options.icon;
5523
- container.appendChild(icon);
5626
+ container.appendChild( icon );
5524
5627
  }
5525
5628
 
5526
- container.appendChild(wValue);
5527
- element.appendChild(container);
5629
+ container.appendChild( wValue );
5630
+ element.appendChild( container );
5528
5631
 
5529
5632
  // Remove branch padding and margins
5530
- if( !widget.name )
5633
+ const useNameAsLabel = !( options.hideName ?? false );
5634
+ if( !useNameAsLabel )
5531
5635
  {
5532
5636
  element.className += " noname";
5533
5637
  container.style.width = "100%";
@@ -5540,7 +5644,7 @@ class Panel {
5540
5644
  // Update height depending on the content
5541
5645
  wValue.style.height = wValue.scrollHeight + "px";
5542
5646
  }
5543
- }, 10);
5647
+ }, 10 );
5544
5648
 
5545
5649
  return widget;
5546
5650
  }
@@ -5548,6 +5652,7 @@ class Panel {
5548
5652
  /**
5549
5653
  * @method addLabel
5550
5654
  * @param {String} value Information string
5655
+ * @param {Object} options Text options
5551
5656
  */
5552
5657
 
5553
5658
  addLabel( value, options = {} ) {
@@ -5562,6 +5667,7 @@ class Panel {
5562
5667
  * @param {String} value Button name
5563
5668
  * @param {Function} callback Callback function on click
5564
5669
  * @param {*} options:
5670
+ * hideName: Don't use name as label [false]
5565
5671
  * disabled: Make the widget disabled [false]
5566
5672
  * icon: Icon class to show as button value
5567
5673
  * img: Path to image to show as button value
@@ -5570,19 +5676,19 @@ class Panel {
5570
5676
 
5571
5677
  addButton( name, value, callback, options = {} ) {
5572
5678
 
5573
- let widget = this.create_widget( name, Widget.BUTTON, options );
5679
+ let widget = this._createWidget( Widget.BUTTON, name, null, options );
5574
5680
 
5575
5681
  widget.onGetValue = () => {
5576
5682
  return wValue.innerText;
5577
5683
  };
5578
5684
 
5579
- widget.onSetValue = ( newValue, skipCallback ) => {
5685
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5580
5686
  wValue.innerHTML =
5581
- (options.icon ? "<a class='" + options.icon + "'></a>" :
5582
- ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (newValue || "") + "</span>" ));
5687
+ ( options.icon ? "<a class='" + options.icon + "'></a>" :
5688
+ ( options.img ? "<img src='" + options.img + "'>" : "<span>" + ( newValue || "" ) + "</span>" ) );
5583
5689
  };
5584
5690
 
5585
- let element = widget.domEl;
5691
+ const element = widget.domEl;
5586
5692
 
5587
5693
  var wValue = document.createElement( 'button' );
5588
5694
  wValue.title = options.title ?? "";
@@ -5594,22 +5700,22 @@ class Panel {
5594
5700
  }
5595
5701
 
5596
5702
  wValue.innerHTML =
5597
- (options.icon ? "<a class='" + options.icon + "'></a>" :
5598
- ( options.img ? "<img src='" + options.img + "'>" : "<span>" + (value || "") + "</span>" ));
5703
+ ( options.icon ? "<a class='" + options.icon + "'></a>" :
5704
+ ( options.img ? "<img src='" + options.img + "'>" : "<span>" + ( value || "" ) + "</span>" ) );
5599
5705
 
5600
- wValue.style.width = "calc( 100% - " + (options.nameWidth ?? LX.DEFAULT_NAME_WIDTH) + ")";
5706
+ wValue.style.width = "calc( 100% - " + ( options.nameWidth ?? LX.DEFAULT_NAME_WIDTH ) + ")";
5601
5707
 
5602
5708
  if( options.disabled )
5603
5709
  {
5604
5710
  wValue.setAttribute( "disabled", true );
5605
5711
  }
5606
5712
 
5607
- wValue.addEventListener("click", e => {
5713
+ wValue.addEventListener( "click", e => {
5608
5714
  if( options.selectable )
5609
5715
  {
5610
5716
  if( options.parent )
5611
5717
  {
5612
- options.parent.querySelectorAll(".lexbutton.selected").forEach( e => { if(e == wValue) return; e.classList.remove("selected") } );
5718
+ options.parent.querySelectorAll(".lexbutton.selected").forEach( e => { if( e == wValue ) return; e.classList.remove( "selected" ) } );
5613
5719
  }
5614
5720
 
5615
5721
  wValue.classList.toggle('selected');
@@ -5620,8 +5726,9 @@ class Panel {
5620
5726
 
5621
5727
  element.appendChild( wValue );
5622
5728
 
5623
- // Remove branch padding and margins
5624
- if( !widget.name )
5729
+ // Remove branch padding and
5730
+ const useNameAsLabel = !( options.hideName ?? false ) && !( options.icon || options.img );
5731
+ if( !useNameAsLabel )
5625
5732
  {
5626
5733
  wValue.className += " noname";
5627
5734
  wValue.style.width = "100%";
@@ -5635,6 +5742,7 @@ class Panel {
5635
5742
  * @param {String} name Widget name
5636
5743
  * @param {Array} values Each of the {value, callback, selected, disabled} items
5637
5744
  * @param {*} options:
5745
+ * hideName: Don't use name as label [false]
5638
5746
  * float: Justify content (left, center, right) [center]
5639
5747
  * @legacy selected: Selected item by default by value
5640
5748
  * noSelection: Buttons can be clicked, but they are not selectable
@@ -5643,8 +5751,8 @@ class Panel {
5643
5751
 
5644
5752
  addComboButtons( name, values, options = {} ) {
5645
5753
 
5646
- let widget = this.create_widget( name, Widget.BUTTON, options );
5647
- let element = widget.domEl;
5754
+ let widget = this._createWidget( Widget.BUTTONS, name, null, options );
5755
+ const element = widget.domEl;
5648
5756
 
5649
5757
  let that = this;
5650
5758
  let container = document.createElement('div');
@@ -5713,7 +5821,8 @@ class Panel {
5713
5821
  }
5714
5822
 
5715
5823
  // Remove branch padding and margins
5716
- if( !widget.name )
5824
+ const useNameAsLabel = !( options.hideName ?? false );
5825
+ if( !useNameAsLabel )
5717
5826
  {
5718
5827
  element.className += " noname";
5719
5828
  container.style.width = "100%";
@@ -5729,17 +5838,19 @@ class Panel {
5729
5838
  * @method addCard
5730
5839
  * @param {String} name Card Name
5731
5840
  * @param {*} options:
5732
- * title: title if any
5733
- * text: card text if any
5734
- * src: url of the image if any
5841
+ * text: Card text
5842
+ * link: Card link
5843
+ * title: Card dom title
5844
+ * src: url of the image
5735
5845
  * callback (Function): function to call on click
5736
5846
  */
5737
5847
 
5738
5848
  addCard( name, options = {} ) {
5739
5849
 
5740
5850
  options.hideName = true;
5741
- let widget = this.create_widget(name, Widget.CARD, options);
5742
- let element = widget.domEl;
5851
+
5852
+ let widget = this._createWidget( Widget.CARD, name, null, options );
5853
+ const element = widget.domEl;
5743
5854
 
5744
5855
  let container = document.createElement('div');
5745
5856
  container.className = "lexcard";
@@ -5751,7 +5862,7 @@ class Panel {
5751
5862
  img.src = options.img;
5752
5863
  container.appendChild(img);
5753
5864
 
5754
- if(options.link != undefined)
5865
+ if( options.link != undefined )
5755
5866
  {
5756
5867
  img.style.cursor = "pointer";
5757
5868
  img.addEventListener('click', function() {
@@ -5761,29 +5872,30 @@ class Panel {
5761
5872
  }
5762
5873
  }
5763
5874
 
5764
- let name_el = document.createElement('span');
5765
- name_el.innerText = name;
5875
+ let cardNameDom = document.createElement('span');
5876
+ cardNameDom.innerText = name;
5766
5877
 
5767
- if(options.link != undefined)
5878
+ if( options.link != undefined )
5768
5879
  {
5769
- let link_el = document.createElement('a');
5770
- link_el.innerText = name;
5771
- link_el.href = options.link;
5772
- link_el.target = options.target ?? "";
5773
- name_el.innerText = "";
5774
- name_el.appendChild(link_el);
5880
+ let cardLinkDom = document.createElement( 'a' );
5881
+ cardLinkDom.innerText = name;
5882
+ cardLinkDom.href = options.link;
5883
+ cardLinkDom.target = options.target ?? "";
5884
+ cardNameDom.innerText = "";
5885
+ cardNameDom.appendChild( cardLinkDom );
5775
5886
  }
5776
5887
 
5777
- container.appendChild(name_el);
5888
+ container.appendChild( cardNameDom );
5778
5889
 
5779
- if( options.callback ) {
5890
+ if( options.callback )
5891
+ {
5780
5892
  container.style.cursor = "pointer";
5781
- container.addEventListener("click", (e) => {
5782
- this._trigger( new IEvent(name, null, e), options.callback );
5893
+ container.addEventListener("click", ( e ) => {
5894
+ this._trigger( new IEvent( name, null, e ), options.callback );
5783
5895
  });
5784
5896
  }
5785
5897
 
5786
- element.appendChild(container);
5898
+ element.appendChild( container );
5787
5899
 
5788
5900
  return widget;
5789
5901
  }
@@ -5808,13 +5920,13 @@ class Panel {
5808
5920
  // Always hide name for this one
5809
5921
  options.hideName = true;
5810
5922
 
5811
- let widget = this.create_widget( name, Widget.FORM, options );
5923
+ let widget = this._createWidget( Widget.FORM, name, null, options );
5812
5924
 
5813
5925
  widget.onGetValue = () => {
5814
5926
  return container.formData;
5815
5927
  };
5816
5928
 
5817
- widget.onSetValue = ( newValue, skipCallback ) => {
5929
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5818
5930
  container.formData = newValue;
5819
5931
  const entries = container.querySelectorAll( ".lexwidget" );
5820
5932
  for( let i = 0; i < entries.length; ++i )
@@ -5833,7 +5945,7 @@ class Panel {
5833
5945
 
5834
5946
  // Add widget value
5835
5947
 
5836
- let element = widget.domEl;
5948
+ const element = widget.domEl;
5837
5949
 
5838
5950
  let container = document.createElement( 'div' );
5839
5951
  container.className = "lexformdata";
@@ -5887,31 +5999,29 @@ class Panel {
5887
5999
 
5888
6000
  element.appendChild( container );
5889
6001
 
5890
- if( !widget.name || options.hideName )
5891
- {
5892
- element.className += " noname";
5893
- container.style.width = "100%";
5894
- }
6002
+ // Form does not never use label
6003
+ element.className += " noname";
6004
+ container.style.width = "100%";
5895
6005
 
5896
6006
  return widget;
5897
6007
  }
5898
6008
 
5899
6009
  /**
5900
6010
  * @method addContent
6011
+ * @param {String} name Widget name
5901
6012
  * @param {HTMLElement/String} element
6013
+ * @param {Object} options
5902
6014
  */
5903
6015
 
5904
- addContent( element, options = {} ) {
6016
+ addContent( name, element, options = {} ) {
5905
6017
 
5906
- if( !element )
5907
- {
5908
- return;
5909
- }
6018
+ console.assert( element, "Empty content!" );
5910
6019
 
5911
6020
  if( element.constructor == String )
5912
6021
  {
5913
6022
  const tmp = document.createElement( "div" );
5914
6023
  tmp.innerHTML = element;
6024
+
5915
6025
  if( tmp.childElementCount > 1 )
5916
6026
  {
5917
6027
  element = tmp;
@@ -5922,27 +6032,28 @@ class Panel {
5922
6032
  }
5923
6033
  }
5924
6034
 
5925
- let widget = this.create_widget( null, Widget.CONTENT, options );
6035
+ options.hideName = true;
6036
+
6037
+ let widget = this._createWidget( Widget.CONTENT, name, null, options );
5926
6038
  widget.domEl.appendChild( element );
6039
+
5927
6040
  return widget;
5928
6041
  }
5929
6042
 
5930
6043
  /**
5931
6044
  * @method addImage
6045
+ * @param {String} name Widget name
5932
6046
  * @param {String} url Image Url
5933
6047
  * @param {*} options
6048
+ * hideName: Don't use name as label [false]
5934
6049
  */
5935
6050
 
5936
- async addImage( url, options = {} ) {
6051
+ async addImage( name, url, options = {} ) {
5937
6052
 
5938
- if( !url )
5939
- {
5940
- return;
5941
- }
6053
+ console.assert( url, "Empty src/url for Image!" );
5942
6054
 
5943
- options.hideName = true;
5944
- let widget = this.create_widget( null, Widget.IMAGE, options );
5945
- let element = widget.domEl;
6055
+ let widget = this._createWidget( Widget.IMAGE, name, null, options );
6056
+ const element = widget.domEl;
5946
6057
 
5947
6058
  let container = document.createElement( 'div' );
5948
6059
  container.className = "leximage";
@@ -5957,6 +6068,7 @@ class Panel {
5957
6068
  }
5958
6069
 
5959
6070
  await img.decode();
6071
+
5960
6072
  container.appendChild( img );
5961
6073
  element.appendChild( container );
5962
6074
 
@@ -5970,6 +6082,7 @@ class Panel {
5970
6082
  * @param {String} value Select by default option
5971
6083
  * @param {Function} callback Callback function on change
5972
6084
  * @param {*} options:
6085
+ * hideName: Don't use name as label [false]
5973
6086
  * filter: Add a search bar to the widget [false]
5974
6087
  * disabled: Make the widget disabled [false]
5975
6088
  * skipReset: Don't add the reset value button when value changes
@@ -5979,33 +6092,24 @@ class Panel {
5979
6092
 
5980
6093
  addDropdown( name, values, value, callback, options = {} ) {
5981
6094
 
5982
- let widget = this.create_widget( name, Widget.DROPDOWN, options );
6095
+ let widget = this._createWidget( Widget.DROPDOWN, name, value, options );
5983
6096
 
5984
6097
  widget.onGetValue = () => {
5985
6098
  return element.querySelector( "li.selected" ).getAttribute( 'value' );
5986
6099
  };
5987
6100
 
5988
- widget.onSetValue = ( newValue, skipCallback ) => {
5989
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
5990
- if( btn ) btn.style.display = ( newValue != wValue.iValue ? "block" : "none" );
6101
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
5991
6102
  value = newValue;
5992
6103
  list.querySelectorAll( 'li' ).forEach( e => { if( e.getAttribute('value') == value ) e.click() } );
5993
- if( !skipCallback ) this._trigger( new IEvent( name, value, null ), callback );
6104
+ if( !skipCallback )
6105
+ {
6106
+ this._trigger( new IEvent( name, value, event ), callback );
6107
+ }
5994
6108
  };
5995
6109
 
5996
- let element = widget.domEl;
6110
+ const element = widget.domEl;
5997
6111
  let that = this;
5998
6112
 
5999
- // Add reset functionality
6000
- if(widget.name && !( options.skipReset ?? false ))
6001
- {
6002
- Panel._add_reset_property( element.domName, function() {
6003
- value = wValue.iValue;
6004
- list.querySelectorAll( 'li' ).forEach( e => { if( e.getAttribute('value') == value ) e.click() } );
6005
- this.style.display = "none";
6006
- });
6007
- }
6008
-
6009
6113
  let container = document.createElement( 'div' );
6010
6114
  container.className = "lexdropdown";
6011
6115
  container.style.width = options.inputWidth || "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
@@ -6214,16 +6318,19 @@ class Panel {
6214
6318
 
6215
6319
  li.addEventListener( "click", e => {
6216
6320
  listDialog.close();
6321
+
6217
6322
  const currentSelected = element.querySelector( ".lexoptions .selected" );
6218
- if(currentSelected) currentSelected.classList.remove( "selected" );
6323
+ if(currentSelected)
6324
+ {
6325
+ currentSelected.classList.remove( "selected" );
6326
+ }
6327
+
6219
6328
  value = e.currentTarget.getAttribute( "value" );
6220
6329
  e.currentTarget.toggleAttribute( "hidden", false );
6221
6330
  e.currentTarget.classList.add( "selected" );
6222
- selectedOption.refresh(value);
6331
+ selectedOption.refresh( value );
6223
6332
 
6224
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6225
- if( btn ) btn.style.display = (value != wValue.iValue ? "block" : "none");
6226
- that._trigger( new IEvent( name, value, null ), callback );
6333
+ widget.set( value, false, e );
6227
6334
 
6228
6335
  // Reset filter
6229
6336
  if( filter )
@@ -6280,7 +6387,8 @@ class Panel {
6280
6387
  element.appendChild( container );
6281
6388
 
6282
6389
  // Remove branch padding and margins
6283
- if( !widget.name )
6390
+ const useNameAsLabel = !( options.hideName ?? false );
6391
+ if( !useNameAsLabel )
6284
6392
  {
6285
6393
  element.className += " noname";
6286
6394
  container.style.width = "100%";
@@ -6307,39 +6415,25 @@ class Panel {
6307
6415
 
6308
6416
  addCurve( name, values, callback, options = {} ) {
6309
6417
 
6310
- if( !name )
6311
- {
6312
- throw( "Set Widget Name!" );
6313
- }
6418
+ let defaultValues = JSON.parse( JSON.stringify( values ) );
6314
6419
 
6315
6420
  let that = this;
6316
- let widget = this.create_widget( name, Widget.CURVE, options );
6421
+ let widget = this._createWidget( Widget.CURVE, name, defaultValues, options );
6317
6422
 
6318
6423
  widget.onGetValue = () => {
6319
6424
  return JSON.parse(JSON.stringify( curveInstance.element.value ));
6320
6425
  };
6321
6426
 
6322
- widget.onSetValue = ( newValue, skipCallback ) => {
6323
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6324
- if( btn ) btn.style.display = ( newValue != curveInstance.element.value ? "block" : "none" );
6427
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6325
6428
  curveInstance.element.value = JSON.parse( JSON.stringify( newValue ) );
6326
6429
  curveInstance.redraw();
6327
- if( !skipCallback ) that._trigger( new IEvent( name, curveInstance.element.value, null ), callback );
6430
+ if( !skipCallback )
6431
+ {
6432
+ that._trigger( new IEvent( name, curveInstance.element.value, event ), callback );
6433
+ }
6328
6434
  };
6329
6435
 
6330
- let element = widget.domEl;
6331
- let defaultValues = JSON.parse( JSON.stringify( values ) );
6332
-
6333
- // Add reset functionality
6334
- if( !(options.skipReset ?? false) )
6335
- {
6336
- Panel._add_reset_property(element.domName, function(e) {
6337
- this.style.display = "none";
6338
- curveInstance.element.value = JSON.parse( JSON.stringify( defaultValues ) );
6339
- curveInstance.redraw();
6340
- that._trigger( new IEvent( name, curveInstance.element.value, e ), callback );
6341
- });
6342
- }
6436
+ const element = widget.domEl;
6343
6437
 
6344
6438
  // Add widget value
6345
6439
 
@@ -6348,9 +6442,7 @@ class Panel {
6348
6442
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
6349
6443
 
6350
6444
  options.callback = (v, e) => {
6351
- let btn = element.querySelector(".lexwidgetname .lexicon");
6352
- if(btn) btn.style.display = (v != defaultValues ? "block" : "none");
6353
- that._trigger( new IEvent(name, v, e), callback );
6445
+ that._trigger( new IEvent( name, v, e ), callback );
6354
6446
  };
6355
6447
 
6356
6448
  options.name = name;
@@ -6389,34 +6481,25 @@ class Panel {
6389
6481
 
6390
6482
  addDial( name, values, callback, options = {} ) {
6391
6483
 
6484
+ let defaultValues = JSON.parse( JSON.stringify( values ) );
6485
+
6392
6486
  let that = this;
6393
- let widget = this.create_widget(name, Widget.DIAL, options);
6487
+ let widget = this._createWidget( Widget.DIAL, name, defaultValues, options );
6394
6488
 
6395
6489
  widget.onGetValue = () => {
6396
- return JSON.parse(JSON.stringify(curveInstance.element.value));
6490
+ return JSON.parse( JSON.stringify( curveInstance.element.value ) );
6397
6491
  };
6398
6492
 
6399
- widget.onSetValue = ( newValue, skipCallback ) => {
6400
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6401
- if( btn ) btn.style.display = ( newValue != curveInstance.element.value ? "block" : "none" );
6493
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6402
6494
  curveInstance.element.value = JSON.parse( JSON.stringify( newValue ) );
6403
6495
  curveInstance.redraw();
6404
- if( !skipCallback ) that._trigger( new IEvent( name, curveInstance.element.value, null ), callback );
6496
+ if( !skipCallback )
6497
+ {
6498
+ that._trigger( new IEvent( name, curveInstance.element.value, event ), callback );
6499
+ }
6405
6500
  };
6406
6501
 
6407
- let element = widget.domEl;
6408
- let defaultValues = JSON.parse( JSON.stringify( values ) );
6409
-
6410
- // Add reset functionality
6411
- if( widget.name && !(options.skipReset ?? false) )
6412
- {
6413
- Panel._add_reset_property(element.domName, function(e) {
6414
- this.style.display = "none";
6415
- curveInstance.element.value = JSON.parse( JSON.stringify( defaultValues ) );
6416
- curveInstance.redraw();
6417
- that._trigger( new IEvent( name, curveInstance.element.value, e ), callback );
6418
- });
6419
- }
6502
+ const element = widget.domEl;
6420
6503
 
6421
6504
  // Add widget value
6422
6505
 
@@ -6424,10 +6507,8 @@ class Panel {
6424
6507
  container.className = "lexcurve";
6425
6508
  container.style.width = widget.name ? "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")" : '100%';
6426
6509
 
6427
- options.callback = (v, e) => {
6428
- let btn = element.querySelector(".lexwidgetname .lexicon");
6429
- if(btn) btn.style.display = (v != defaultValues ? "block" : "none");
6430
- that._trigger( new IEvent(name, v, e), callback );
6510
+ options.callback = ( v, e ) => {
6511
+ that._trigger( new IEvent( name, v, e ), callback );
6431
6512
  };
6432
6513
 
6433
6514
  options.name = name;
@@ -6456,42 +6537,32 @@ class Panel {
6456
6537
  * @param {String} name Widget name
6457
6538
  * @param {Number} value Flag value by default option
6458
6539
  * @param {Function} callback Callback function on change
6459
- * @param {*} options:
6540
+ * @param {Object} options:
6460
6541
  */
6461
6542
 
6462
6543
  addLayers( name, value, callback, options = {} ) {
6463
6544
 
6464
- if( !name )
6465
- {
6466
- throw("Set Widget Name!");
6467
- }
6468
-
6469
6545
  let that = this;
6470
- let widget = this.create_widget(name, Widget.LAYERS, options);
6546
+ let widget = this._createWidget( Widget.LAYERS, name, value, options );
6547
+
6471
6548
  widget.onGetValue = () => {
6472
6549
  return element.value;
6473
6550
  };
6474
- widget.onSetValue = ( newValue, skipCallback ) => {
6475
- let btn = element.querySelector(".lexwidgetname .lexicon");
6476
- if(btn) btn.style.display = (newValue != defaultValue ? "block" : "none");
6551
+
6552
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6477
6553
  value = element.value = newValue;
6478
6554
  setLayers();
6479
- if( !skipCallback ) that._trigger( new IEvent(name, value), callback );
6555
+ if( !skipCallback )
6556
+ {
6557
+ that._trigger( new IEvent(name, value, event), callback );
6558
+ }
6480
6559
  };
6481
6560
 
6482
- let element = widget.domEl;
6483
-
6484
- // Add reset functionality
6485
- Panel._add_reset_property(element.domName, function(e) {
6486
- this.style.display = "none";
6487
- value = element.value = defaultValue;
6488
- setLayers();
6489
- that._trigger( new IEvent(name, value, e), callback );
6490
- });
6561
+ const element = widget.domEl;
6491
6562
 
6492
6563
  // Add widget value
6493
6564
 
6494
- var container = document.createElement('div');
6565
+ var container = document.createElement( "div" );
6495
6566
  container.className = "lexlayers";
6496
6567
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
6497
6568
 
@@ -6503,6 +6574,7 @@ class Panel {
6503
6574
 
6504
6575
  let binary = value.toString( 2 );
6505
6576
  let nbits = binary.length;
6577
+
6506
6578
  // fill zeros
6507
6579
  for( var i = 0; i < (16 - nbits); ++i )
6508
6580
  {
@@ -6516,33 +6588,28 @@ class Panel {
6516
6588
  if( value != undefined )
6517
6589
  {
6518
6590
  const valueBit = binary[ 16 - bit - 1 ];
6519
- if(valueBit != undefined && valueBit == '1')
6591
+ if( valueBit != undefined && valueBit == '1' )
6592
+ {
6520
6593
  layer.classList.add('selected');
6594
+ }
6521
6595
  }
6522
6596
  layer.innerText = bit + 1;
6523
6597
  layer.title = "Bit " + bit + ", value " + (1 << bit);
6524
6598
  container.appendChild( layer );
6525
6599
 
6526
6600
  layer.addEventListener("click", e => {
6527
-
6528
6601
  e.stopPropagation();
6529
6602
  e.stopImmediatePropagation();
6530
6603
  e.target.classList.toggle('selected');
6531
6604
  value ^= ( 1 << bit );
6532
- element.value = value;
6533
-
6534
- let btn = element.querySelector(".lexwidgetname .lexicon");
6535
- if(btn) btn.style.display = (value != defaultValue ? "block" : "none");
6536
-
6537
- this._trigger( new IEvent(name, value, e), callback );
6605
+ widget.set( value, false, e );
6538
6606
  });
6539
6607
  }
6540
-
6541
6608
  };
6542
6609
 
6543
6610
  setLayers();
6544
6611
 
6545
- element.appendChild(container);
6612
+ element.appendChild( container );
6546
6613
 
6547
6614
  return widget;
6548
6615
  }
@@ -6558,12 +6625,8 @@ class Panel {
6558
6625
 
6559
6626
  addArray( name, values = [], callback, options = {} ) {
6560
6627
 
6561
- if( !name )
6562
- {
6563
- throw( "Set Widget Name!" );
6564
- }
6628
+ let widget = this._createWidget( Widget.ARRAY, name, null, options );
6565
6629
 
6566
- let widget = this.create_widget(name, Widget.ARRAY, options);
6567
6630
  widget.onGetValue = () => {
6568
6631
  let array_inputs = element.querySelectorAll("input");
6569
6632
  let values = [];
@@ -6571,13 +6634,17 @@ class Panel {
6571
6634
  values.push( v.value );
6572
6635
  return values;
6573
6636
  };
6574
- widget.onSetValue = ( newValue, skipCallback ) => {
6637
+
6638
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6575
6639
  values = newValue;
6576
6640
  updateItems();
6577
- if( !skipCallback ) this._trigger( new IEvent(name, values, null), callback );
6641
+ if( !skipCallback )
6642
+ {
6643
+ this._trigger( new IEvent( name, values, event ), callback );
6644
+ }
6578
6645
  };
6579
6646
 
6580
- let element = widget.domEl;
6647
+ const element = widget.domEl;
6581
6648
  element.style.flexWrap = "wrap";
6582
6649
 
6583
6650
  // Add dropdown array button
@@ -6590,10 +6657,10 @@ class Panel {
6590
6657
 
6591
6658
  this.queue( container );
6592
6659
 
6593
- const angle_down = `<a class='fa-solid fa-angle-down' style='float:right; margin-right: 3px;'></a>`;
6660
+ const angleDown = `<a class='fa-solid fa-angle-down' style='float:right; margin-right: 3px;'></a>`;
6594
6661
 
6595
6662
  let buttonName = "Array (size " + values.length + ")";
6596
- buttonName += angle_down;
6663
+ buttonName += angleDown;
6597
6664
  this.addButton(null, buttonName, () => {
6598
6665
  element.querySelector(".lexarrayitems").toggleAttribute('hidden');
6599
6666
  }, { buttonClass: 'array' });
@@ -6602,24 +6669,24 @@ class Panel {
6602
6669
 
6603
6670
  // Show elements
6604
6671
 
6605
- let array_items = document.createElement('div');
6606
- array_items.className = "lexarrayitems";
6607
- array_items.toggleAttribute('hidden', true);
6672
+ let arrayItems = document.createElement( "div" );
6673
+ arrayItems.className = "lexarrayitems";
6674
+ arrayItems.toggleAttribute( "hidden", true );
6608
6675
 
6609
- element.appendChild(container);
6610
- element.appendChild(array_items);
6676
+ element.appendChild( container );
6677
+ element.appendChild( arrayItems );
6611
6678
 
6612
6679
  const updateItems = () => {
6613
6680
 
6614
6681
  // Update num items
6615
6682
  let buttonEl = element.querySelector(".lexbutton.array span");
6616
6683
  buttonEl.innerHTML = "Array (size " + values.length + ")";
6617
- buttonEl.innerHTML += angle_down;
6684
+ buttonEl.innerHTML += angleDown;
6618
6685
 
6619
6686
  // Update inputs
6620
- array_items.innerHTML = "";
6687
+ arrayItems.innerHTML = "";
6621
6688
 
6622
- this.queue( array_items );
6689
+ this.queue( arrayItems );
6623
6690
 
6624
6691
  for( let i = 0; i < values.length; ++i )
6625
6692
  {
@@ -6665,7 +6732,7 @@ class Panel {
6665
6732
  this._trigger( new IEvent(name, values, event), callback );
6666
6733
  }, { buttonClass: 'array' });
6667
6734
 
6668
- // Stop pushing to array_items
6735
+ // Stop pushing to arrayItems
6669
6736
  this.clearQueue();
6670
6737
  };
6671
6738
 
@@ -6681,22 +6748,30 @@ class Panel {
6681
6748
  * @param {String} value Selected list value
6682
6749
  * @param {Function} callback Callback function on change
6683
6750
  * @param {*} options:
6751
+ * hideName: Don't use name as label [false]
6684
6752
  */
6685
6753
 
6686
6754
  addList( name, values, value, callback, options = {} ) {
6687
6755
 
6688
- let widget = this.create_widget( name, Widget.LIST, options );
6756
+ let widget = this._createWidget( Widget.LIST, name, value, options );
6689
6757
 
6690
6758
  widget.onGetValue = () => {
6691
6759
  return value;
6692
6760
  };
6693
- widget.onSetValue = ( newValue, skipCallback ) => {
6761
+
6762
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6694
6763
  listContainer.querySelectorAll( '.lexlistitem' ).forEach( e => e.classList.remove( 'selected' ) );
6695
6764
  const idx = values.indexOf( newValue );
6696
- if( idx == -1 ) return;
6765
+ if( idx == -1 )
6766
+ {
6767
+ return;
6768
+ }
6697
6769
  listContainer.children[ idx ].classList.toggle( 'selected' );
6698
6770
  value = newValue;
6699
- if( !skipCallback ) this._trigger( new IEvent( name, newValue ), callback );
6771
+ if( !skipCallback )
6772
+ {
6773
+ this._trigger( new IEvent( name, newValue, event ), callback );
6774
+ }
6700
6775
  };
6701
6776
 
6702
6777
  widget.updateValues = ( newValues ) => {
@@ -6730,7 +6805,7 @@ class Panel {
6730
6805
  }
6731
6806
  };
6732
6807
 
6733
- let element = widget.domEl;
6808
+ const element = widget.domEl;
6734
6809
 
6735
6810
  // Show list
6736
6811
 
@@ -6741,7 +6816,8 @@ class Panel {
6741
6816
  widget.updateValues( values );
6742
6817
 
6743
6818
  // Remove branch padding and margins
6744
- if( !widget.name )
6819
+ const useNameAsLabel = !( options.hideName ?? false );
6820
+ if( !useNameAsLabel )
6745
6821
  {
6746
6822
  element.className += " noname";
6747
6823
  listContainer.style.width = "100%";
@@ -6758,46 +6834,39 @@ class Panel {
6758
6834
  * @param {String} value Comma separated tags
6759
6835
  * @param {Function} callback Callback function on change
6760
6836
  * @param {*} options:
6837
+ * hideName: Don't use name as label [false]
6761
6838
  */
6762
6839
 
6763
6840
  addTags( name, value, callback, options = {} ) {
6764
6841
 
6765
6842
  value = value.replace( /\s/g, '' ).split( ',' );
6843
+
6766
6844
  let defaultValue = [].concat( value );
6767
- let widget = this.create_widget( name, Widget.TAGS, options );
6845
+ let widget = this._createWidget( Widget.TAGS, name, defaultValue, options );
6768
6846
 
6769
6847
  widget.onGetValue = () => {
6770
6848
  return [].concat( value );
6771
6849
  };
6772
- widget.onSetValue = ( newValue, skipCallback ) => {
6850
+
6851
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6773
6852
  value = [].concat( newValue );
6774
- create_tags();
6775
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6776
- if( btn ) btn.style.display = ( newValue != defaultValue ? "block" : "none" );
6777
- if( !skipCallback ) that._trigger( new IEvent( name, value ), callback );
6853
+ _generateTags();
6854
+ if( !skipCallback )
6855
+ {
6856
+ that._trigger( new IEvent( name, value, event ), callback );
6857
+ }
6778
6858
  };
6779
6859
 
6780
- let element = widget.domEl;
6860
+ const element = widget.domEl;
6781
6861
  let that = this;
6782
6862
 
6783
- // Add reset functionality
6784
- if(widget.name)
6785
- {
6786
- Panel._add_reset_property(element.domName, function(e) {
6787
- this.style.display = "none";
6788
- value = [].concat(defaultValue);
6789
- create_tags();
6790
- that._trigger( new IEvent(name, value, e), callback );
6791
- });
6792
- }
6793
-
6794
6863
  // Show tags
6795
6864
 
6796
6865
  const tagsContainer = document.createElement('div');
6797
6866
  tagsContainer.className = "lextags";
6798
6867
  tagsContainer.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
6799
6868
 
6800
- const create_tags = () => {
6869
+ const _generateTags = () => {
6801
6870
 
6802
6871
  tagsContainer.innerHTML = "";
6803
6872
 
@@ -6815,9 +6884,7 @@ class Panel {
6815
6884
  removeButton.addEventListener( 'click', e => {
6816
6885
  tag.remove();
6817
6886
  value.splice( value.indexOf( tagName ), 1 );
6818
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6819
- if( btn ) btn.style.display = ( value != defaultValue ? "block" : "none" );
6820
- that._trigger( new IEvent( name, value, e ), callback );
6887
+ widget.set( value, false, e );
6821
6888
  } );
6822
6889
 
6823
6890
  tagsContainer.appendChild( tag );
@@ -6836,20 +6903,18 @@ class Panel {
6836
6903
  if( !val.length || value.indexOf( val ) > -1 )
6837
6904
  return;
6838
6905
  value.push( val );
6839
- create_tags();
6840
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6841
- if(btn) btn.style.display = "block";
6842
- that._trigger( new IEvent( name, value, e ), callback );
6906
+ widget.set( value, false, e );
6843
6907
  }
6844
6908
  };
6845
6909
 
6846
6910
  tagInput.focus();
6847
6911
  }
6848
6912
 
6849
- create_tags();
6913
+ _generateTags();
6850
6914
 
6851
6915
  // Remove branch padding and margins
6852
- if( !widget.name )
6916
+ const useNameAsLabel = !( options.hideName ?? false );
6917
+ if( !useNameAsLabel )
6853
6918
  {
6854
6919
  element.className += " noname";
6855
6920
  tagsContainer.style.width = "100%";
@@ -6879,44 +6944,44 @@ class Panel {
6879
6944
  throw( "Set Widget Name or at least a label!" );
6880
6945
  }
6881
6946
 
6882
- let widget = this.create_widget( name, Widget.CHECKBOX, options );
6947
+ let widget = this._createWidget( Widget.CHECKBOX, name, value, options );
6883
6948
 
6884
6949
  widget.onGetValue = () => {
6885
- return checkbox.checked;
6950
+ return value;
6886
6951
  };
6887
6952
 
6888
- widget.onSetValue = ( newValue, skipCallback ) => {
6889
- if( checkbox.checked !== newValue )
6953
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
6954
+
6955
+ if( newValue == value )
6890
6956
  {
6891
- checkbox.checked = newValue;
6892
- Panel._dispatch_event( checkbox, "change", skipCallback );
6957
+ return;
6893
6958
  }
6894
- };
6895
6959
 
6896
- let element = widget.domEl;
6960
+ checkbox.checked = value = newValue;
6897
6961
 
6898
- // Add reset functionality
6899
- if( name )
6900
- {
6901
- Panel._add_reset_property( element.domName, function() {
6902
- checkbox.checked = !checkbox.checked;
6903
- Panel._dispatch_event( checkbox, "change" );
6904
- });
6905
- }
6962
+ // Update suboptions menu
6963
+ element.querySelector( ".lexcheckboxsubmenu" )?.toggleAttribute( 'hidden', !newValue );
6964
+
6965
+ if( !skipCallback )
6966
+ {
6967
+ this._trigger( new IEvent( name, newValue, event ), callback );
6968
+ }
6969
+ };
6970
+
6971
+ const element = widget.domEl;
6906
6972
 
6907
6973
  // Add widget value
6908
6974
 
6909
- var container = document.createElement('div');
6975
+ var container = document.createElement( "div" );
6910
6976
  container.className = "lexcheckboxcont";
6911
6977
 
6912
- let checkbox = document.createElement('input');
6978
+ let checkbox = document.createElement( "input" );
6913
6979
  checkbox.type = "checkbox";
6914
6980
  checkbox.className = "lexcheckbox " + ( options.className ?? "" );
6915
6981
  checkbox.checked = value;
6916
- checkbox.iValue = value;
6917
6982
  checkbox.disabled = options.disabled ?? false;
6918
6983
 
6919
- let valueName = document.createElement( 'span' );
6984
+ let valueName = document.createElement( "span" );
6920
6985
  valueName.className = "checkboxtext";
6921
6986
  valueName.innerHTML = options.label ?? "On";
6922
6987
 
@@ -6924,21 +6989,7 @@ class Panel {
6924
6989
  container.appendChild( valueName );
6925
6990
 
6926
6991
  checkbox.addEventListener( "change" , e => {
6927
-
6928
- const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
6929
-
6930
- // Reset button (default value)
6931
- if( !skipCallback )
6932
- {
6933
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
6934
- if( btn ) btn.style.display = checkbox.checked != checkbox.iValue ? "block": "none";
6935
- }
6936
-
6937
- // Open suboptions
6938
- let submenu = element.querySelector( ".lexcheckboxsubmenu" );
6939
- if( submenu ) submenu.toggleAttribute( 'hidden', !checkbox.checked );
6940
-
6941
- if( !skipCallback ) this._trigger( new IEvent( name, checkbox.checked, e ), callback );
6992
+ widget.set( checkbox.checked, false, e );
6942
6993
  });
6943
6994
 
6944
6995
  element.appendChild( container );
@@ -6946,9 +6997,9 @@ class Panel {
6946
6997
  if( options.suboptions )
6947
6998
  {
6948
6999
  element.style.flexWrap = "wrap";
6949
- let suboptions = document.createElement('div');
7000
+ let suboptions = document.createElement( "div" );
6950
7001
  suboptions.className = "lexcheckboxsubmenu";
6951
- suboptions.toggleAttribute( 'hidden', !checkbox.checked );
7002
+ suboptions.toggleAttribute( "hidden", !checkbox.checked );
6952
7003
 
6953
7004
  this.queue( suboptions );
6954
7005
  options.suboptions.call(this, this);
@@ -6973,32 +7024,31 @@ class Panel {
6973
7024
 
6974
7025
  addToggle( name, value, callback, options = {} ) {
6975
7026
 
6976
- if( !name )
6977
- {
6978
- throw( "Set Widget Name!" );
6979
- }
6980
-
6981
- let widget = this.create_widget( name, Widget.TOGGLE, options );
7027
+ let widget = this._createWidget( Widget.TOGGLE, name, value, options );
6982
7028
 
6983
7029
  widget.onGetValue = () => {
6984
7030
  return toggle.checked;
6985
7031
  };
6986
7032
 
6987
- widget.onSetValue = ( newValue, skipCallback ) => {
6988
- if( toggle.checked !== newValue )
7033
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7034
+
7035
+ if( newValue == value )
6989
7036
  {
6990
- toggle.checked = newValue;
6991
- Panel._dispatch_event( toggle, "change", skipCallback );
7037
+ return;
6992
7038
  }
6993
- };
6994
7039
 
6995
- let element = widget.domEl;
7040
+ toggle.checked = value = newValue;
6996
7041
 
6997
- // Add reset functionality
6998
- Panel._add_reset_property( element.domName, function() {
6999
- toggle.checked = !toggle.checked;
7000
- Panel._dispatch_event( toggle, "change" );
7001
- });
7042
+ // Update suboptions menu
7043
+ element.querySelector( ".lextogglesubmenu" )?.toggleAttribute( 'hidden', !newValue );
7044
+
7045
+ if( !skipCallback )
7046
+ {
7047
+ this._trigger( new IEvent( name, newValue, event ), callback );
7048
+ }
7049
+ };
7050
+
7051
+ const element = widget.domEl;
7002
7052
 
7003
7053
  // Add widget value
7004
7054
 
@@ -7020,21 +7070,7 @@ class Panel {
7020
7070
  container.appendChild( valueName );
7021
7071
 
7022
7072
  toggle.addEventListener( "change" , e => {
7023
-
7024
- const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
7025
-
7026
- // Reset button (default value)
7027
- if( !skipCallback )
7028
- {
7029
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7030
- if( btn ) btn.style.display = toggle.checked != toggle.iValue ? "block": "none";
7031
- }
7032
-
7033
- // Open suboptions
7034
- let submenu = element.querySelector( ".lextogglesubmenu" );
7035
- if( submenu ) submenu.toggleAttribute( 'hidden', !toggle.checked );
7036
-
7037
- if( !skipCallback ) this._trigger( new IEvent( name, toggle.checked, e ), callback );
7073
+ widget.set( toggle.checked, false, e );
7038
7074
  });
7039
7075
 
7040
7076
  element.appendChild( container );
@@ -7058,43 +7094,43 @@ class Panel {
7058
7094
 
7059
7095
  /**
7060
7096
  * @method addRadioGroup
7097
+ * @param {String} name Widget name
7061
7098
  * @param {String} label Radio label
7062
7099
  * @param {Array} values Radio options
7063
7100
  * @param {Function} callback Callback function on change
7064
7101
  * @param {*} options:
7065
7102
  * disabled: Make the widget disabled [false]
7066
7103
  * className: Customize colors
7104
+ * selected: Index of the default selected option
7067
7105
  */
7068
7106
 
7069
- addRadioGroup( label, values, callback, options = {} ) {
7107
+ addRadioGroup( name, label, values, callback, options = {} ) {
7070
7108
 
7071
- let widget = this.create_widget( null, Widget.RADIO, options );
7109
+ let widget = this._createWidget( Widget.RADIO, name, null, options );
7110
+ let currentIndex = null;
7072
7111
 
7073
7112
  widget.onGetValue = () => {
7074
7113
  const items = container.querySelectorAll( 'button' );
7075
- for( let i = 0; i < items.length; ++i )
7076
- {
7077
- const optionItem = items[ i ];
7078
- if( optionItem.checked )
7079
- {
7080
- return [ i, values[ i ] ];
7081
- }
7082
- }
7114
+ return currentIndex ? [ currentIndex, items[ currentIndex ] ] : undefined;
7083
7115
  };
7084
7116
 
7085
- widget.onSetValue = ( newValue, skipCallback ) => {
7117
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7118
+ console.assert( newValue.constructor == Number, "RadioGroup _value_ must be an Array index!" );
7119
+
7086
7120
  const items = container.querySelectorAll( 'button' );
7087
- for( let i = 0; i < items.length; ++i )
7121
+ items.forEach( b => { b.checked = false; b.classList.remove( "checked" ) } );
7122
+
7123
+ const optionItem = items[ newValue ];
7124
+ optionItem.checked = !optionItem.checked;
7125
+ optionItem.classList.toggle( "checked" );
7126
+
7127
+ if( !skipCallback )
7088
7128
  {
7089
- const optionItem = items[ i ];
7090
- if( newValue == i )
7091
- {
7092
- Panel._dispatch_event( optionItem, "click", skipCallback );
7093
- }
7129
+ that._trigger( new IEvent( null, [ newValue, values[ newValue ] ], event ), callback );
7094
7130
  }
7095
7131
  };
7096
7132
 
7097
- let element = widget.domEl;
7133
+ const element = widget.domEl;
7098
7134
 
7099
7135
  // Add widget value
7100
7136
  var container = document.createElement( 'div' );
@@ -7117,18 +7153,12 @@ class Panel {
7117
7153
  optionButton.disabled = options.disabled ?? false;
7118
7154
  optionItem.appendChild( optionButton );
7119
7155
 
7120
- optionButton.addEventListener( "click", function( e ) {
7121
- const skipCallback = ( e.detail?.constructor == Number ? null : e.detail );
7122
- container.querySelectorAll( 'button' ).forEach( e => { e.checked = false; e.classList.remove( "checked" ) } );
7123
- this.checked = !this.checked;
7124
- this.classList.toggle( "checked" );
7125
- if( !skipCallback ) that._trigger( new IEvent( null, [ i, values[ i ] ], e ), callback );
7156
+ optionButton.addEventListener( "click", ( e ) => {
7157
+ widget.set( i, false, e );
7126
7158
  } );
7127
7159
 
7128
- {
7129
- const checkedSpan = document.createElement( 'span' );
7130
- optionButton.appendChild( checkedSpan );
7131
- }
7160
+ const checkedSpan = document.createElement( 'span' );
7161
+ optionButton.appendChild( checkedSpan );
7132
7162
 
7133
7163
  const optionLabel = document.createElement( 'span' );
7134
7164
  optionLabel.innerHTML = values[ i ];
@@ -7137,8 +7167,9 @@ class Panel {
7137
7167
 
7138
7168
  if( options.selected )
7139
7169
  {
7140
- console.assert( options.selected.constructor == Number );
7141
- widget.set( options.selected, true );
7170
+ console.assert( options.selected.constructor == Number, "RadioGroup _selected_ must be an Array index!" );
7171
+ currentIndex = options.selected;
7172
+ widget.set( currentIndex, true );
7142
7173
  }
7143
7174
 
7144
7175
  element.appendChild( container );
@@ -7158,30 +7189,36 @@ class Panel {
7158
7189
 
7159
7190
  addColor( name, value, callback, options = {} ) {
7160
7191
 
7161
- if( !name )
7162
- {
7163
- throw( "Set Widget Name!" );
7164
- }
7192
+ value = ( value.constructor === Array ) ? rgbToHex( value ) : value;
7165
7193
 
7166
- let widget = this.create_widget( name, Widget.COLOR, options );
7194
+ let widget = this._createWidget( Widget.COLOR, name, value, options );
7167
7195
 
7168
7196
  widget.onGetValue = () => {
7169
- return color.value;
7170
- };
7171
- widget.onSetValue = ( newValue, skipCallback ) => {
7172
- color.value = newValue;
7173
- Panel._dispatch_event( color, "input", skipCallback );
7197
+ return value;
7174
7198
  };
7175
7199
 
7176
- let element = widget.domEl;
7177
- let change_from_input = false;
7200
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7178
7201
 
7179
- // Add reset functionality
7180
- Panel._add_reset_property( element.domName, function() {
7181
- this.style.display = "none";
7182
- color.value = color.iValue;
7183
- Panel._dispatch_event( color, "input" );
7184
- });
7202
+ if( color.useRGB )
7203
+ {
7204
+ newValue = hexToRgb( newValue );
7205
+ }
7206
+
7207
+ // Means it was called from the color input listener, not the text
7208
+ if( event )
7209
+ {
7210
+ textWidget.set( newValue, true, event );
7211
+ }
7212
+
7213
+ color.value = value = newValue;
7214
+
7215
+ if( !skipCallback )
7216
+ {
7217
+ this._trigger( new IEvent( name, newValue, event ), callback );
7218
+ }
7219
+ };
7220
+
7221
+ const element = widget.domEl;
7185
7222
 
7186
7223
  // Add widget value
7187
7224
 
@@ -7195,7 +7232,7 @@ class Panel {
7195
7232
  color.className = "colorinput";
7196
7233
  color.id = "color" + simple_guidGenerator();
7197
7234
  color.useRGB = options.useRGB ?? false;
7198
- color.value = color.iValue = value.constructor === Array ? rgbToHex( value ) : value;
7235
+ color.value = color.iValue = value;
7199
7236
 
7200
7237
  if( options.disabled )
7201
7238
  {
@@ -7203,38 +7240,18 @@ class Panel {
7203
7240
  }
7204
7241
 
7205
7242
  color.addEventListener( "input", e => {
7206
- let val = e.target.value;
7207
-
7208
- const skipCallback = e.detail;
7209
-
7210
- // Change value (always hex)
7211
- if( !change_from_input )
7212
- text_widget.set( val );
7213
-
7214
- // Reset button (default value)
7215
- if( !skipCallback )
7216
- {
7217
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7218
- if( btn ) btn.style.display = val != color.iValue ? "block": "none";
7219
- }
7220
-
7221
- if( color.useRGB )
7222
- val = hexToRgb( val );
7223
-
7224
- if( !skipCallback ) this._trigger( new IEvent( name, val, e ), callback );
7243
+ widget.set( e.target.value, false, e );
7225
7244
  }, false );
7226
7245
 
7227
7246
  container.appendChild( color );
7228
7247
 
7229
7248
  this.queue( container );
7230
7249
 
7231
- const text_widget = this.addText( null, color.value, v => {
7232
- change_from_input = true;
7250
+ const textWidget = this.addText( null, color.value, v => {
7233
7251
  widget.set( v );
7234
- change_from_input = false;
7235
7252
  }, { width: "calc( 100% - 32px )"});
7236
7253
 
7237
- text_widget.domEl.style.marginLeft = "4px";
7254
+ textWidget.domEl.style.marginLeft = "4px";
7238
7255
 
7239
7256
  this.clearQueue();
7240
7257
 
@@ -7249,6 +7266,7 @@ class Panel {
7249
7266
  * @param {Number} value Default number value
7250
7267
  * @param {Function} callback Callback function on change
7251
7268
  * @param {*} options:
7269
+ * hideName: Don't use name as label [false]
7252
7270
  * className: Extra classes to customize style
7253
7271
  * disabled: Make the widget disabled [false]
7254
7272
  * left: The slider goes to the left instead of the right
@@ -7259,28 +7277,28 @@ class Panel {
7259
7277
 
7260
7278
  addRange( name, value, callback, options = {} ) {
7261
7279
 
7262
- let widget = this.create_widget( name, Widget.RANGE, options );
7280
+ let widget = this._createWidget( Widget.RANGE, name, value, options );
7263
7281
 
7264
7282
  widget.onGetValue = () => {
7265
- return +slider.value;
7283
+ return value;
7266
7284
  };
7267
7285
 
7268
- widget.onSetValue = ( newValue, skipCallback ) => {
7269
- slider.value = newValue;
7270
- Panel._dispatch_event( slider, "input", skipCallback );
7271
- };
7286
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7272
7287
 
7273
- let element = widget.domEl;
7288
+ if( isNaN( newValue ) )
7289
+ {
7290
+ return;
7291
+ }
7274
7292
 
7275
- // add reset functionality
7276
- if( widget.name )
7277
- {
7278
- Panel._add_reset_property( element.domName, function() {
7279
- this.style.display = "none";
7280
- slider.value = slider.iValue;
7281
- Panel._dispatch_event( slider, "input" );
7282
- });
7283
- }
7293
+ slider.value = value = clamp( +newValue, +slider.min, +slider.max );
7294
+
7295
+ if( !skipCallback )
7296
+ {
7297
+ this._trigger( new IEvent( name, options.left ? ( ( +slider.max ) - value + ( +slider.min ) ) : value, event ), callback );
7298
+ }
7299
+ };
7300
+
7301
+ const element = widget.domEl;
7284
7302
 
7285
7303
  // add widget value
7286
7304
 
@@ -7308,30 +7326,7 @@ class Panel {
7308
7326
  }
7309
7327
 
7310
7328
  slider.addEventListener( "input", e => {
7311
-
7312
- if( isNaN( e.target.valueAsNumber ) )
7313
- {
7314
- return;
7315
- }
7316
-
7317
- const skipCallback = e.detail;
7318
-
7319
- let val = e.target.value = clamp( +e.target.valueAsNumber, +slider.min, +slider.max );
7320
- slider.value = val;
7321
-
7322
- // Reset button (default value)
7323
- if( !skipCallback )
7324
- {
7325
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7326
- if( btn ) btn.style.display = val != slider.iValue ? "block": "none";
7327
- }
7328
-
7329
- if( options.left )
7330
- {
7331
- val = ( +slider.max ) - val + ( +slider.min );
7332
- }
7333
-
7334
- if( !skipCallback ) this._trigger( new IEvent( name, val, e ), callback );
7329
+ widget.set( e.target.valueAsNumber, false, e );
7335
7330
  }, { passive: false });
7336
7331
 
7337
7332
  slider.addEventListener( "mousedown", function( e ) {
@@ -7364,8 +7359,8 @@ class Panel {
7364
7359
  container.appendChild( slider );
7365
7360
  element.appendChild( container );
7366
7361
 
7367
- // Remove branch padding and margins
7368
- if( !widget.name )
7362
+ const useNameAsLabel = !( options.hideName ?? false );
7363
+ if( !useNameAsLabel )
7369
7364
  {
7370
7365
  element.className += " noname";
7371
7366
  container.style.width = "100%";
@@ -7380,6 +7375,7 @@ class Panel {
7380
7375
  * @param {Number} value Default number value
7381
7376
  * @param {Function} callback Callback function on change
7382
7377
  * @param {*} options:
7378
+ * hideName: Don't use name as label [false]
7383
7379
  * disabled: Make the widget disabled [false]
7384
7380
  * step: Step of the input
7385
7381
  * precision: The number of digits to appear after the decimal point
@@ -7392,28 +7388,40 @@ class Panel {
7392
7388
 
7393
7389
  addNumber( name, value, callback, options = {} ) {
7394
7390
 
7395
- let widget = this.create_widget( name, Widget.NUMBER, options );
7391
+ let widget = this._createWidget( Widget.NUMBER, name, value, options );
7396
7392
 
7397
7393
  widget.onGetValue = () => {
7398
- return +vecinput.value;
7394
+ return value;
7399
7395
  };
7400
7396
 
7401
- widget.onSetValue = ( newValue, skipCallback ) => {
7402
- vecinput.value = round( newValue, options.precision );
7403
- Panel._dispatch_event( vecinput, "change", skipCallback );
7404
- };
7397
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7398
+
7399
+ if( isNaN( newValue ) )
7400
+ {
7401
+ return;
7402
+ }
7405
7403
 
7406
- let element = widget.domEl;
7404
+ value = clamp( +newValue, +vecinput.min, +vecinput.max );
7405
+ vecinput.value = value = round( value, options.precision );
7407
7406
 
7408
- // add reset functionality
7409
- if( widget.name )
7410
- {
7411
- Panel._add_reset_property( element.domName, function() {
7412
- this.style.display = "none";
7413
- vecinput.value = vecinput.iValue;
7414
- Panel._dispatch_event( vecinput, "change" );
7415
- });
7416
- }
7407
+ // Update slider!
7408
+ if( box.querySelector( ".lexinputslider" ) )
7409
+ {
7410
+ box.querySelector( ".lexinputslider" ).value = value;
7411
+ }
7412
+
7413
+ if( options.units )
7414
+ {
7415
+ vecinput.unitSpan.style.left = measureRealWidth( value ) + "px";
7416
+ }
7417
+
7418
+ if( !skipCallback )
7419
+ {
7420
+ this._trigger( new IEvent( name, value, event ), callback );
7421
+ }
7422
+ };
7423
+
7424
+ const element = widget.domEl;
7417
7425
 
7418
7426
  // add widget value
7419
7427
 
@@ -7472,9 +7480,7 @@ class Panel {
7472
7480
  slider.value = value;
7473
7481
 
7474
7482
  slider.addEventListener( "input", function( e ) {
7475
- let new_value = +this.valueAsNumber;
7476
- vecinput.value = round( new_value, options.precision );
7477
- Panel._dispatch_event( vecinput, "change" );
7483
+ widget.set( this.valueAsNumber, false, e );
7478
7484
  }, false );
7479
7485
 
7480
7486
  slider.addEventListener( "mousedown", function( e ) {
@@ -7499,13 +7505,13 @@ class Panel {
7499
7505
  vecinput.max = slider.max = newMax ?? vecinput.max;
7500
7506
  vecinput.step = newStep ?? vecinput.step;
7501
7507
  slider.step = newStep ?? slider.step;
7502
- Panel._dispatch_event( vecinput, "change", true );
7508
+ widget.set( value, true );
7503
7509
  };
7504
7510
  }
7505
7511
 
7506
7512
  vecinput.addEventListener( "input", function( e ) {
7507
- let new_value = +this.valueAsNumber;
7508
- vecinput.value = round( new_value, options.precision );
7513
+ value = +this.valueAsNumber;
7514
+ value = round( value, options.precision );
7509
7515
  if( options.units )
7510
7516
  {
7511
7517
  vecinput.unitSpan.style.left = measureRealWidth( vecinput.value ) + "px";
@@ -7521,44 +7527,12 @@ class Panel {
7521
7527
  let mult = options.step ?? 1;
7522
7528
  if( e.shiftKey ) mult *= 10;
7523
7529
  else if( e.altKey ) mult *= 0.1;
7524
- let new_value = ( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ) );
7525
- this.value = round( new_value, options.precision );
7526
- Panel._dispatch_event(vecinput, "change");
7530
+ value = ( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ) );
7531
+ widget.set( value, false, e );
7527
7532
  }, { passive: false });
7528
7533
 
7529
- vecinput.addEventListener( "change", e => {
7530
-
7531
- if( isNaN( e.target.valueAsNumber ) )
7532
- {
7533
- return;
7534
- }
7535
-
7536
- const skipCallback = e.detail;
7537
-
7538
- let val = e.target.value = clamp( +e.target.valueAsNumber, +vecinput.min, +vecinput.max );
7539
- val = options.precision ? round( val, options.precision ) : val;
7540
-
7541
- // Update slider!
7542
- if( box.querySelector( ".lexinputslider" ) )
7543
- {
7544
- box.querySelector( ".lexinputslider" ).value = val;
7545
- }
7546
-
7547
- vecinput.value = val;
7548
-
7549
- if( options.units )
7550
- {
7551
- vecinput.unitSpan.style.left = measureRealWidth( vecinput.value ) + "px";
7552
- }
7553
-
7554
- // Reset button (default value)
7555
- if( !skipCallback )
7556
- {
7557
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7558
- if( btn ) btn.style.display = val != vecinput.iValue ? "block": "none";
7559
- }
7560
-
7561
- if( !skipCallback ) this._trigger( new IEvent( name, val, e ), callback );
7534
+ vecinput.addEventListener( "change", function( e ) {
7535
+ widget.set( this.valueAsNumber, false, e );
7562
7536
  }, { passive: false });
7563
7537
 
7564
7538
  // Add drag input
@@ -7602,9 +7576,9 @@ class Panel {
7602
7576
  let mult = options.step ?? 1;
7603
7577
  if( e.shiftKey ) mult *= 10;
7604
7578
  else if( e.altKey ) mult *= 0.1;
7605
- let new_value = ( +vecinput.valueAsNumber + mult * dt );
7606
- vecinput.value = ( +new_value ).toFixed( 4 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' );
7607
- Panel._dispatch_event( vecinput, "change" );
7579
+ value = ( +vecinput.valueAsNumber + mult * dt );
7580
+ widget.set( value, false, e );
7581
+ // vecinput.value = ( +new_value ).toFixed( 4 ).replace( /([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/, '$1' );
7608
7582
  }
7609
7583
 
7610
7584
  e.stopPropagation();
@@ -7634,7 +7608,8 @@ class Panel {
7634
7608
  element.appendChild( container );
7635
7609
 
7636
7610
  // Remove branch padding and margins
7637
- if( !widget.name )
7611
+ const useNameAsLabel = !( options.hideName ?? false );
7612
+ if( !useNameAsLabel )
7638
7613
  {
7639
7614
  element.className += " noname";
7640
7615
  container.style.width = "100%";
@@ -7645,17 +7620,12 @@ class Panel {
7645
7620
 
7646
7621
  static VECTOR_COMPONENTS = { 0: 'x', 1: 'y', 2: 'z', 3: 'w' };
7647
7622
 
7648
- _add_vector( num_components, name, value, callback, options = {} ) {
7649
-
7650
- num_components = clamp( num_components, 2, 4 );
7651
- value = value ?? new Array( num_components ).fill( 0 );
7623
+ _add_vector( numComponents, name, value, callback, options = {} ) {
7652
7624
 
7653
- if( !name )
7654
- {
7655
- throw( "Set Widget Name!" );
7656
- }
7625
+ numComponents = clamp( numComponents, 2, 4 );
7626
+ value = value ?? new Array( numComponents ).fill( 0 );
7657
7627
 
7658
- let widget = this.create_widget( name, Widget.VECTOR, options );
7628
+ let widget = this._createWidget( Widget.VECTOR, name, [].concat( value ), options );
7659
7629
 
7660
7630
  widget.onGetValue = () => {
7661
7631
  let inputs = element.querySelectorAll( "input" );
@@ -7667,33 +7637,30 @@ class Panel {
7667
7637
  return value;
7668
7638
  };
7669
7639
 
7670
- widget.onSetValue = ( newValue, skipCallback ) => {
7671
- const inputs = element.querySelectorAll( ".vecinput" );
7672
- if( inputs.length == newValue.length )
7640
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7641
+
7642
+ if( vectorInputs.length != newValue.length )
7673
7643
  {
7674
7644
  console.error( "Input length does not match vector length." );
7675
7645
  return;
7676
7646
  }
7677
7647
 
7678
- for( let i = 0; i < inputs.length; ++i )
7648
+ for( let i = 0; i < vectorInputs.length; ++i )
7679
7649
  {
7680
7650
  let value = newValue[ i ];
7681
- inputs[ i ].value = round( value, options.precision ) ?? 0;
7682
- Panel._dispatch_event( inputs[ i ], "change", skipCallback );
7651
+ value = clamp( value, +vectorInputs[ i ].min, +vectorInputs[ i ].max );
7652
+ value = round( value, options.precision ) ?? 0;
7653
+ vectorInputs[ i ].value = newValue[ i ] = value;
7683
7654
  }
7684
- };
7685
-
7686
- let element = widget.domEl;
7687
7655
 
7688
- // Add reset functionality
7689
- Panel._add_reset_property( element.domName, function() {
7690
- this.style.display = "none";
7691
- for( let v of element.querySelectorAll( ".vecinput" ) )
7656
+ if( !skipCallback )
7692
7657
  {
7693
- v.value = v.iValue;
7694
- Panel._dispatch_event( v, "change" );
7658
+ this._trigger( new IEvent( name, newValue, event ), callback );
7695
7659
  }
7696
- });
7660
+ };
7661
+
7662
+ const element = widget.domEl;
7663
+ const vectorInputs = [];
7697
7664
 
7698
7665
  // Add widget value
7699
7666
 
@@ -7701,20 +7668,21 @@ class Panel {
7701
7668
  container.className = "lexvector";
7702
7669
  container.style.width = "calc( 100% - " + LX.DEFAULT_NAME_WIDTH + ")";
7703
7670
 
7704
- for( let i = 0; i < num_components; ++i )
7671
+ for( let i = 0; i < numComponents; ++i )
7705
7672
  {
7706
7673
  let box = document.createElement( 'div' );
7707
7674
  box.className = "vecbox";
7708
7675
  box.innerHTML = "<span class='" + Panel.VECTOR_COMPONENTS[ i ] + "'></span>";
7709
7676
 
7710
7677
  let vecinput = document.createElement( 'input' );
7711
- vecinput.className = "vecinput v" + num_components;
7678
+ vecinput.className = "vecinput v" + numComponents;
7712
7679
  vecinput.min = options.min ?? -1e24;
7713
7680
  vecinput.max = options.max ?? 1e24;
7714
7681
  vecinput.step = options.step ?? "any";
7715
7682
  vecinput.type = "number";
7716
- vecinput.id = "vec" + num_components + "_" + simple_guidGenerator();
7683
+ vecinput.id = "vec" + numComponents + "_" + simple_guidGenerator();
7717
7684
  vecinput.idx = i;
7685
+ vectorInputs[ i ] = vecinput;
7718
7686
 
7719
7687
  if( value[ i ].constructor == Number )
7720
7688
  {
@@ -7764,21 +7732,12 @@ class Panel {
7764
7732
  return;
7765
7733
  }
7766
7734
 
7767
- const skipCallback = e.detail;
7768
-
7769
- let val = e.target.value = clamp( e.target.value, +vecinput.min, +vecinput.max );
7735
+ let val = clamp( e.target.value, +vecinput.min, +vecinput.max );
7770
7736
  val = round( val, options.precision );
7771
7737
 
7772
- // Reset button (default value)
7773
- if( !skipCallback )
7774
- {
7775
- let btn = element.querySelector( ".lexwidgetname .lexicon" );
7776
- if( btn ) btn.style.display = val != vecinput.iValue ? "block" : "none";
7777
- }
7778
-
7779
7738
  if( locker.locked )
7780
7739
  {
7781
- for( let v of element.querySelectorAll( ".vecinput" ) )
7740
+ for( let v of vectorInputs )
7782
7741
  {
7783
7742
  v.value = val;
7784
7743
  value[ v.idx ] = val;
@@ -7790,7 +7749,7 @@ class Panel {
7790
7749
  value[ e.target.idx ] = val;
7791
7750
  }
7792
7751
 
7793
- if( !skipCallback ) this._trigger( new IEvent( name, value, e ), callback );
7752
+ widget.set( value, false, e );
7794
7753
  }, false );
7795
7754
 
7796
7755
  // Add drag input
@@ -7881,17 +7840,14 @@ class Panel {
7881
7840
  if( options.min !== undefined || options.max !== undefined )
7882
7841
  {
7883
7842
  widget.setLimits = ( newMin, newMax, newStep ) => {
7884
- const inputs = element.querySelectorAll(".vecinput");
7885
- for( let v of inputs )
7843
+ for( let v of vectorInputs )
7886
7844
  {
7887
7845
  v.min = newMin ?? v.min;
7888
7846
  v.max = newMax ?? v.max;
7889
7847
  v.step = newStep ?? v.step;
7890
- Panel._dispatch_event( v, "change", true );
7891
7848
  }
7892
7849
 
7893
- // To call onChange callback
7894
- this._trigger( new IEvent( name, value ), callback );
7850
+ widget.set( value, true );
7895
7851
  };
7896
7852
  }
7897
7853
 
@@ -7952,13 +7908,14 @@ class Panel {
7952
7908
  * @param {Number} value Default number value
7953
7909
  * @param {Function} callback Callback function on change
7954
7910
  * @param {*} options:
7911
+ * hideName: Don't use name as label [false]
7955
7912
  * disabled: Make the widget disabled [false]
7956
7913
  * units: Unit as string added to the end of the value
7957
7914
  */
7958
7915
 
7959
7916
  addSize( name, value, callback, options = {} ) {
7960
7917
 
7961
- let widget = this.create_widget( name, Widget.SIZE, options );
7918
+ let widget = this._createWidget( Widget.SIZE, name, value, options );
7962
7919
 
7963
7920
  widget.onGetValue = () => {
7964
7921
  const value = [];
@@ -7969,14 +7926,14 @@ class Panel {
7969
7926
  return value;
7970
7927
  };
7971
7928
 
7972
- widget.onSetValue = ( newValue, skipCallback ) => {
7929
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
7973
7930
  for( let i = 0; i < element.dimensions.length; ++i )
7974
7931
  {
7975
7932
  element.dimensions[ i ].set( newValue[ i ], skipCallback );
7976
7933
  }
7977
7934
  };
7978
7935
 
7979
- let element = widget.domEl;
7936
+ const element = widget.domEl;
7980
7937
 
7981
7938
  this.queue( element );
7982
7939
 
@@ -8049,7 +8006,8 @@ class Panel {
8049
8006
  }
8050
8007
 
8051
8008
  // Remove branch padding and margins
8052
- if( !widget.name )
8009
+ const useNameAsLabel = !( options.hideName ?? false );
8010
+ if( !useNameAsLabel )
8053
8011
  {
8054
8012
  element.className += " noname";
8055
8013
  container.style.width = "100%";
@@ -8073,18 +8031,13 @@ class Panel {
8073
8031
 
8074
8032
  addPad( name, value, callback, options = {} ) {
8075
8033
 
8076
- if( !name )
8077
- {
8078
- throw( "Set Widget Name!" );
8079
- }
8080
-
8081
- let widget = this.create_widget( name, Widget.PAD, options );
8034
+ let widget = this._createWidget( Widget.PAD, name, null, options );
8082
8035
 
8083
8036
  widget.onGetValue = () => {
8084
8037
  return thumb.value.xy;
8085
8038
  };
8086
8039
 
8087
- widget.onSetValue = ( newValue, skipCallback ) => {
8040
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8088
8041
  thumb.value.set( newValue[ 0 ], newValue[ 1 ] );
8089
8042
  _updateValue( thumb.value );
8090
8043
  if( !skipCallback )
@@ -8093,7 +8046,7 @@ class Panel {
8093
8046
  }
8094
8047
  };
8095
8048
 
8096
- let element = widget.domEl;
8049
+ const element = widget.domEl;
8097
8050
 
8098
8051
  var container = document.createElement( 'div' );
8099
8052
  container.className = "lexpad";
@@ -8198,17 +8151,13 @@ class Panel {
8198
8151
 
8199
8152
  addProgress( name, value, options = {} ) {
8200
8153
 
8201
- if( !name )
8202
- {
8203
- throw("Set Widget Name!");
8204
- }
8205
-
8206
- let widget = this.create_widget( name, Widget.PROGRESS, options );
8154
+ let widget = this._createWidget( Widget.PROGRESS, name, value, options );
8207
8155
 
8208
8156
  widget.onGetValue = () => {
8209
8157
  return progress.value;
8210
8158
  };
8211
- widget.onSetValue = ( newValue, skipCallback ) => {
8159
+
8160
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8212
8161
  element.querySelector("meter").value = newValue;
8213
8162
  _updateColor();
8214
8163
  if( element.querySelector("span") )
@@ -8217,7 +8166,7 @@ class Panel {
8217
8166
  }
8218
8167
  };
8219
8168
 
8220
- let element = widget.domEl;
8169
+ const element = widget.domEl;
8221
8170
 
8222
8171
  var container = document.createElement('div');
8223
8172
  container.className = "lexprogress";
@@ -8339,13 +8288,8 @@ class Panel {
8339
8288
 
8340
8289
  addFile( name, callback, options = { } ) {
8341
8290
 
8342
- if( !name )
8343
- {
8344
- throw( "Set Widget Name!" );
8345
- }
8346
-
8347
- let widget = this.create_widget( name, Widget.FILE, options );
8348
- let element = widget.domEl;
8291
+ let widget = this._createWidget( Widget.FILE, name, null, options );
8292
+ const element = widget.domEl;
8349
8293
 
8350
8294
  let local = options.local ?? true;
8351
8295
  let type = options.type ?? 'text';
@@ -8431,20 +8375,28 @@ class Panel {
8431
8375
 
8432
8376
  addTree( name, data, options = {} ) {
8433
8377
 
8378
+ options.hideName = true;
8379
+
8380
+ const widget = this._createWidget( Widget.TREE, name, null, options );
8381
+ const element = widget.domEl;
8382
+
8434
8383
  let container = document.createElement('div');
8435
8384
  container.className = "lextree";
8385
+ element.appendChild( container );
8436
8386
 
8437
8387
  if( name )
8438
8388
  {
8439
8389
  let title = document.createElement('span');
8440
8390
  title.innerHTML = name;
8441
- container.appendChild(title);
8391
+ container.appendChild( title );
8442
8392
  }
8443
8393
 
8444
8394
  let toolsDiv = document.createElement('div');
8445
8395
  toolsDiv.className = "lextreetools";
8446
- if(!name)
8396
+ if( !name )
8397
+ {
8447
8398
  toolsDiv.className += " notitle";
8399
+ }
8448
8400
 
8449
8401
  // Tree icons
8450
8402
  if( options.icons )
@@ -8455,7 +8407,7 @@ class Panel {
8455
8407
  iconEl.title = data.name;
8456
8408
  iconEl.className = "lexicon " + data.icon;
8457
8409
  iconEl.addEventListener("click", data.callback);
8458
- toolsDiv.appendChild(iconEl);
8410
+ toolsDiv.appendChild( iconEl );
8459
8411
  }
8460
8412
  }
8461
8413
 
@@ -8463,35 +8415,36 @@ class Panel {
8463
8415
 
8464
8416
  options.filter = options.filter ?? true;
8465
8417
 
8466
- let node_filter_input = null;
8467
- if(options.filter)
8418
+ let nodeFilterInput = null;
8419
+ if( options.filter )
8468
8420
  {
8469
- node_filter_input = document.createElement('input');
8470
- node_filter_input.id = "lexnodetree_filter";
8471
- node_filter_input.setAttribute("placeholder", "Filter..");
8472
- node_filter_input.style.width = "calc( 100% - 17px )";
8473
- node_filter_input.addEventListener('input', function(){
8421
+ nodeFilterInput = document.createElement('input');
8422
+ nodeFilterInput.id = "lexnodetree_filter";
8423
+ nodeFilterInput.setAttribute("placeholder", "Filter..");
8424
+ nodeFilterInput.style.width = "calc( 100% - 17px )";
8425
+ nodeFilterInput.addEventListener('input', function(){
8474
8426
  nodeTree.refresh();
8475
8427
  });
8476
8428
 
8477
8429
  let searchIcon = document.createElement('a');
8478
8430
  searchIcon.className = "lexicon fa-solid fa-magnifying-glass";
8479
- toolsDiv.appendChild(node_filter_input);
8480
- toolsDiv.appendChild(searchIcon);
8431
+ toolsDiv.appendChild( nodeFilterInput );
8432
+ toolsDiv.appendChild( searchIcon );
8481
8433
  }
8482
8434
 
8483
- if(options.icons || options.filter)
8484
- container.appendChild(toolsDiv);
8435
+ if( options.icons || options.filter )
8436
+ {
8437
+ container.appendChild( toolsDiv );
8438
+ }
8485
8439
 
8486
8440
  // Tree
8487
8441
 
8488
8442
  let list = document.createElement('ul');
8489
- list.addEventListener("contextmenu", function(e) {
8443
+ list.addEventListener("contextmenu", function( e ) {
8490
8444
  e.preventDefault();
8491
8445
  });
8492
8446
 
8493
- container.appendChild(list);
8494
- this.root.appendChild(container);
8447
+ container.appendChild( list );
8495
8448
 
8496
8449
  const nodeTree = new NodeTree( container, data, options );
8497
8450
  return nodeTree;
@@ -8505,13 +8458,14 @@ class Panel {
8505
8458
 
8506
8459
  var element = document.createElement('div');
8507
8460
  element.className = "lexseparator";
8508
- let widget = new Widget( null, Widget.SEPARATOR );
8461
+
8462
+ let widget = new Widget( Widget.SEPARATOR );
8509
8463
  widget.domEl = element;
8510
8464
 
8511
- if( this.current_branch )
8465
+ if( this._currentBranch )
8512
8466
  {
8513
- this.current_branch.content.appendChild( element );
8514
- this.current_branch.widgets.push( widget );
8467
+ this._currentBranch.content.appendChild( element );
8468
+ this._currentBranch.widgets.push( widget );
8515
8469
  }
8516
8470
  else
8517
8471
  {
@@ -8534,12 +8488,8 @@ class Panel {
8534
8488
 
8535
8489
  addTabs( tabs, options = {} ) {
8536
8490
 
8537
- let root = this.current_branch ? this.current_branch.content : this.root;
8538
-
8539
- if( !this.current_branch )
8540
- {
8541
- console.warn("No current branch!");
8542
- }
8491
+ const widget = this._createWidget( Widget.TABS, null, null, options );
8492
+ const element = widget.domEl;
8543
8493
 
8544
8494
  if( tabs.constructor != Array )
8545
8495
  {
@@ -8559,7 +8509,7 @@ class Panel {
8559
8509
  let tabContainer = document.createElement( 'div' );
8560
8510
  tabContainer.className = 'tabs';
8561
8511
  container.appendChild( tabContainer );
8562
- root.appendChild( container );
8512
+ element.appendChild( container );
8563
8513
 
8564
8514
  for( let i = 0; i < tabs.length; ++i )
8565
8515
  {
@@ -8583,10 +8533,9 @@ class Panel {
8583
8533
  container.appendChild( infoContainer );
8584
8534
 
8585
8535
  tabEl.addEventListener( 'click', e => {
8586
-
8587
8536
  // Change selected tab
8588
8537
  tabContainer.querySelectorAll( '.lextab' ).forEach( e => { e.classList.remove( 'selected' ); } );
8589
- e.target.classList.add( 'selected' );
8538
+ tabEl.classList.add( 'selected' );
8590
8539
  // Hide all tabs content
8591
8540
  container.querySelectorAll(".widgets").forEach( e => { e.toggleAttribute( 'hidden', true ); } );
8592
8541
  // Show tab content
@@ -8627,42 +8576,38 @@ class Panel {
8627
8576
 
8628
8577
  addCounter( name, value, callback, options = { } ) {
8629
8578
 
8630
- let widget = this.create_widget( name, Widget.COUNTER, options );
8579
+ let widget = this._createWidget( Widget.COUNTER, name, value, options );
8631
8580
 
8632
8581
  widget.onGetValue = () => {
8633
8582
  return counterText.count;
8634
8583
  };
8635
8584
 
8636
- widget.onSetValue = ( newValue, skipCallback ) => {
8637
- _onChange( newValue, skipCallback );
8585
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8586
+ newValue = clamp( newValue, min, max );
8587
+ counterText.count = newValue;
8588
+ counterText.innerHTML = newValue;
8589
+ if( !skipCallback )
8590
+ {
8591
+ this._trigger( new IEvent( name, newValue, event ), callback );
8592
+ }
8638
8593
  };
8639
8594
 
8640
- let element = widget.domEl;
8595
+ const element = widget.domEl;
8641
8596
 
8642
8597
  const min = options.min ?? 0;
8643
8598
  const max = options.max ?? 100;
8644
8599
  const step = options.step ?? 1;
8645
8600
 
8646
- const _onChange = ( value, skipCallback, event ) => {
8647
- value = clamp( value, min, max );
8648
- counterText.count = value;
8649
- counterText.innerHTML = value;
8650
- if( !skipCallback )
8651
- {
8652
- this._trigger( new IEvent( name, value, event ), callback );
8653
- }
8654
- }
8655
-
8656
8601
  const container = document.createElement( 'div' );
8657
8602
  container.className = "lexcounter";
8658
8603
  element.appendChild( container );
8659
8604
 
8660
8605
  this.queue( container );
8661
8606
 
8662
- this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-minus'></a>", (value, e) => {
8607
+ this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-minus'></a>", ( value, e ) => {
8663
8608
  let mult = step ?? 1;
8664
8609
  if( e.shiftKey ) mult *= 10;
8665
- _onChange( counterText.count - mult, false, e );
8610
+ widget.set( counterText.count - mult, false, e );
8666
8611
  }, { className: "micro", skipInlineCount: true, title: "Minus" });
8667
8612
 
8668
8613
  this.clearQueue();
@@ -8687,10 +8632,10 @@ class Panel {
8687
8632
 
8688
8633
  this.queue( container );
8689
8634
 
8690
- this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-plus'></a>", (value, e) => {
8635
+ this.addButton(null, "<a style='margin-top: 0px;' class='fa-solid fa-plus'></a>", ( value, e ) => {
8691
8636
  let mult = step ?? 1;
8692
8637
  if( e.shiftKey ) mult *= 10;
8693
- _onChange( counterText.count + mult, false, e );
8638
+ widget.set( counterText.count + mult, false, e );
8694
8639
  }, { className: "micro", skipInlineCount: true, title: "Plus" });
8695
8640
 
8696
8641
  this.clearQueue();
@@ -8703,6 +8648,7 @@ class Panel {
8703
8648
  * @param {String} name Widget name
8704
8649
  * @param {Number} data Table data
8705
8650
  * @param {*} options:
8651
+ * hideName: Don't use name as label [false]
8706
8652
  * head: Table headers (each of the headers per column)
8707
8653
  * body: Table body (data per row for each column)
8708
8654
  * rowActions: Allow to add actions per row
@@ -8717,17 +8663,17 @@ class Panel {
8717
8663
  throw( "Data is needed to create a table!" );
8718
8664
  }
8719
8665
 
8720
- let widget = this.create_widget( name, Widget.TABLE, options );
8666
+ let widget = this._createWidget( Widget.TABLE, name, null, options );
8721
8667
 
8722
8668
  widget.onGetValue = () => {
8723
8669
 
8724
8670
  };
8725
8671
 
8726
- widget.onSetValue = ( newValue, skipCallback ) => {
8672
+ widget.onSetValue = ( newValue, skipCallback, event ) => {
8727
8673
 
8728
8674
  };
8729
8675
 
8730
- let element = widget.domEl;
8676
+ const element = widget.domEl;
8731
8677
 
8732
8678
  const container = document.createElement('div');
8733
8679
  container.className = "lextable";
@@ -8915,7 +8861,8 @@ class Panel {
8915
8861
 
8916
8862
  widget.refreshTable();
8917
8863
 
8918
- if( !widget.name )
8864
+ const useNameAsLabel = !( options.hideName ?? false );
8865
+ if( !useNameAsLabel )
8919
8866
  {
8920
8867
  element.className += " noname";
8921
8868
  container.style.width = "100%";
@@ -8962,12 +8909,14 @@ class Branch {
8962
8909
  var title = document.createElement( 'div' );
8963
8910
  title.className = "lexbranchtitle";
8964
8911
 
8965
- title.innerHTML = "<a class='fa-solid fa-angle-up switch-branch-button'></a>";
8966
8912
  if( options.icon )
8967
8913
  {
8968
- title.innerHTML += "<a class='branchicon " + options.icon + "' style='margin-right: 8px; margin-bottom: -2px;'>";
8914
+ title.innerHTML = "<a class='branchicon " + options.icon + "'>";
8969
8915
  }
8970
- title.innerHTML += name || "Branch";
8916
+
8917
+ title.innerHTML += ( name || "Branch" );
8918
+
8919
+ title.innerHTML += "<a class='fa-solid fa-angle-up switch-branch-button'></a>";
8971
8920
 
8972
8921
  root.appendChild( title );
8973
8922
 
@@ -9125,7 +9074,7 @@ class Branch {
9125
9074
  for( var i = 0; i < this.widgets.length; i++ )
9126
9075
  {
9127
9076
  let widget = this.widgets[ i ];
9128
- let element = widget.domEl;
9077
+ const element = widget.domEl;
9129
9078
 
9130
9079
  if( element.children.length < 2 )
9131
9080
  {
@@ -9298,7 +9247,7 @@ class Dialog {
9298
9247
  titleDiv.innerHTML = title;
9299
9248
  titleDiv.setAttribute('draggable', false);
9300
9249
 
9301
- titleDiv.oncontextmenu = function(e) {
9250
+ titleDiv.oncontextmenu = function( e ) {
9302
9251
  e.preventDefault();
9303
9252
  e.stopPropagation();
9304
9253
 
@@ -11135,7 +11084,7 @@ class AssetView {
11135
11084
  itemEl.appendChild(info);
11136
11085
  }
11137
11086
 
11138
- itemEl.addEventListener('click', function(e) {
11087
+ itemEl.addEventListener('click', function( e ) {
11139
11088
  e.stopImmediatePropagation();
11140
11089
  e.stopPropagation();
11141
11090
 
@@ -11172,7 +11121,7 @@ class AssetView {
11172
11121
 
11173
11122
  if( that.contextMenu )
11174
11123
  {
11175
- itemEl.addEventListener('contextmenu', function(e) {
11124
+ itemEl.addEventListener('contextmenu', function( e ) {
11176
11125
  e.preventDefault();
11177
11126
 
11178
11127
  const multiple = that.content.querySelectorAll('.selected').length;
@@ -11197,7 +11146,7 @@ class AssetView {
11197
11146
  });
11198
11147
  }
11199
11148
 
11200
- itemEl.addEventListener("dragstart", function(e) {
11149
+ itemEl.addEventListener("dragstart", function( e ) {
11201
11150
  e.preventDefault();
11202
11151
  }, false );
11203
11152
 
@@ -11594,7 +11543,7 @@ Object.assign(LX, {
11594
11543
  script.src = url[i] + ( version ? "?version=" + version : "" );
11595
11544
  script.original_src = url[i];
11596
11545
  script.async = false;
11597
- script.onload = function(e) {
11546
+ script.onload = function( e ) {
11598
11547
  total--;
11599
11548
  loaded_scripts.push(this);
11600
11549
  if(total)