lexgui 0.6.12 → 0.7.0

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.
Files changed (46) hide show
  1. package/README.md +14 -9
  2. package/build/{components → extensions}/audio.js +11 -11
  3. package/build/{components → extensions}/codeeditor.js +95 -63
  4. package/build/{components → extensions}/docmaker.js +10 -3
  5. package/build/{components → extensions}/imui.js +19 -12
  6. package/build/{components → extensions}/nodegraph.js +1 -1
  7. package/build/{components → extensions}/timeline.js +23 -23
  8. package/build/{components → extensions}/videoeditor.js +1 -1
  9. package/build/lexgui-docs.css +9 -9
  10. package/build/lexgui.css +484 -224
  11. package/build/lexgui.js +1399 -735
  12. package/build/lexgui.min.css +2 -2
  13. package/build/lexgui.min.js +1 -1
  14. package/build/lexgui.module.js +1400 -736
  15. package/build/lexgui.module.min.js +1 -1
  16. package/changelog.md +72 -21
  17. package/demo.js +43 -30
  18. package/examples/{all_widgets.html → all-components.html} +21 -4
  19. package/examples/{area_tabs.html → area-tabs.html} +3 -3
  20. package/examples/{asset_view.html → asset-view.html} +3 -3
  21. package/examples/{code_editor.html → code-editor.html} +4 -4
  22. package/examples/dialogs.html +3 -3
  23. package/examples/editor.html +27 -18
  24. package/examples/{immediate_ui.html → immediate-ui.html} +3 -3
  25. package/examples/index.html +8 -8
  26. package/examples/{node_graph.html → node-graph.html} +3 -3
  27. package/examples/previews/all-components.png +0 -0
  28. package/examples/previews/area-tabs.png +0 -0
  29. package/examples/previews/asset-view.png +0 -0
  30. package/examples/previews/code-editor.png +0 -0
  31. package/examples/previews/dialogs.png +0 -0
  32. package/examples/previews/editor.png +0 -0
  33. package/examples/previews/node-graph.png +0 -0
  34. package/examples/previews/side-bar.png +0 -0
  35. package/examples/previews/timeline.png +0 -0
  36. package/examples/{side_bar.html → side-bar.html} +3 -3
  37. package/examples/timeline.html +4 -4
  38. package/examples/{video_editor.html → video-editor.html} +3 -3
  39. package/examples/{video_editor2.html → video-editor2.html} +3 -3
  40. package/package.json +2 -2
  41. package/examples/previews/all_widgets.png +0 -0
  42. package/examples/previews/area_tabs.png +0 -0
  43. package/examples/previews/asset_view.png +0 -0
  44. package/examples/previews/code_editor.png +0 -0
  45. package/examples/previews/node_graph.png +0 -0
  46. package/examples/previews/side_bar.png +0 -0
@@ -7,9 +7,9 @@
7
7
  */
8
8
 
9
9
  const LX = {
10
- version: "0.6.12",
10
+ version: "0.7.0",
11
11
  ready: false,
12
- components: [], // Specific pre-build components
12
+ extensions: [], // Store extensions used
13
13
  signals: {}, // Events and triggers
14
14
  extraCommandbarEntries: [], // User specific entries for command bar
15
15
  activeDraggable: null // Watch for the current active draggable
@@ -203,7 +203,7 @@ function _createCommandbar( root )
203
203
  }
204
204
  else
205
205
  {
206
- for( let c of LX.components )
206
+ for( let c of LX.extensions )
207
207
  {
208
208
  if( !LX[ c ] || !LX[ c ].prototype.onKeyPressed )
209
209
  {
@@ -416,6 +416,7 @@ function _createCommandbar( root )
416
416
  * skipRoot: Skip adding LX root container
417
417
  * skipDefaultArea: Skip creation of main area
418
418
  * layoutMode: Sets page layout mode (document | app)
419
+ * spacingMode: Sets page layout spacing mode (default | compact)
419
420
  */
420
421
 
421
422
  async function init( options = { } )
@@ -467,6 +468,9 @@ async function init( options = { } )
467
468
  } );
468
469
  }
469
470
 
471
+ this.spacingMode = options.spacingMode ?? "default";
472
+ document.documentElement.setAttribute( "data-spacing", this.spacingMode );
473
+
470
474
  this.container.appendChild( this.modal );
471
475
 
472
476
  if( !options.skipRoot )
@@ -511,13 +515,13 @@ async function init( options = { } )
511
515
  this.DEFAULT_SPLITBAR_SIZE = 4;
512
516
  this.OPEN_CONTEXTMENU_ENTRY = 'click';
513
517
 
514
- this.widgetResizeObserver = new ResizeObserver( entries => {
518
+ this.componentResizeObserver = new ResizeObserver( entries => {
515
519
  for ( const entry of entries )
516
520
  {
517
- const widget = entry.target?.jsInstance;
518
- if( widget && widget.onResize )
521
+ const c = entry.target?.jsInstance;
522
+ if( c && c.onResize )
519
523
  {
520
- widget.onResize( entry.contentRect );
524
+ c.onResize( entry.contentRect );
521
525
  }
522
526
  }
523
527
  });
@@ -562,6 +566,19 @@ function setLayoutMode( mode )
562
566
 
563
567
  LX.setLayoutMode = setLayoutMode;
564
568
 
569
+ /**
570
+ * @method setSpacingMode
571
+ * @param {String} mode: default | compact
572
+ */
573
+
574
+ function setSpacingMode( mode )
575
+ {
576
+ this.spacingMode = mode;
577
+ document.documentElement.setAttribute( "data-spacing", this.spacingMode );
578
+ }
579
+
580
+ LX.setSpacingMode = setSpacingMode;
581
+
565
582
  /**
566
583
  * @method setCommandbarState
567
584
  * @param {Boolean} value
@@ -664,7 +681,7 @@ function emit( signalName, value, options = {} )
664
681
 
665
682
  for( let obj of data )
666
683
  {
667
- if( obj instanceof LX.Widget )
684
+ if( obj instanceof LX.BaseComponent )
668
685
  {
669
686
  obj.set( value, options.skipCallback ?? true );
670
687
  }
@@ -744,7 +761,18 @@ class Popover {
744
761
  this.root.dataset["side"] = this.side;
745
762
  this.root.tabIndex = "1";
746
763
  this.root.className = "lexpopover";
747
- LX.root.appendChild( this.root );
764
+
765
+ const nestedDialog = trigger.closest( "dialog" );
766
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
767
+ {
768
+ this._parent = nestedDialog;
769
+ }
770
+ else
771
+ {
772
+ this._parent = LX.root;
773
+ }
774
+
775
+ this._parent.appendChild( this.root );
748
776
 
749
777
  this.root.addEventListener( "keydown", (e) => {
750
778
  if( e.key == "Escape" )
@@ -866,6 +894,13 @@ class Popover {
866
894
  position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - this.root.offsetHeight - this._windowPadding );
867
895
  }
868
896
 
897
+ if( this._parent instanceof HTMLDialogElement )
898
+ {
899
+ let parentRect = this._parent.getBoundingClientRect();
900
+ position[ 0 ] -= parentRect.x;
901
+ position[ 1 ] -= parentRect.y;
902
+ }
903
+
869
904
  this.root.style.left = `${ position[ 0 ] }px`;
870
905
  this.root.style.top = `${ position[ 1 ] }px`;
871
906
  }
@@ -939,7 +974,7 @@ class Sheet {
939
974
  this.root.tabIndex = "1";
940
975
  this.root.role = "dialog";
941
976
  this.root.className = "lexsheet fixed z-1000 bg-primary";
942
- LX.root.appendChild( this.root );
977
+ document.body.appendChild( this.root );
943
978
 
944
979
  this.root.addEventListener( "keydown", (e) => {
945
980
  if( e.key == "Escape" )
@@ -980,17 +1015,21 @@ class Sheet {
980
1015
  this.root.style.height = "100%";
981
1016
  break;
982
1017
  case "top":
1018
+ this.root.style.left = 0;
983
1019
  this.root.style.top = 0;
984
1020
  this.root.style.width = "100%";
985
1021
  this.root.style.height = size;
986
1022
  break;
987
1023
  case "bottom":
1024
+ this.root.style.left = 0;
988
1025
  this.root.style.bottom = 0;
989
1026
  this.root.style.width = "100%";
990
1027
  this.root.style.height = size;
991
1028
  break;
992
1029
  }
993
1030
 
1031
+ document.documentElement.setAttribute( "data-scale", `sheet-${ this.side }` );
1032
+
994
1033
  this.root.focus();
995
1034
 
996
1035
  this._onClick = e => {
@@ -1008,6 +1047,8 @@ class Sheet {
1008
1047
 
1009
1048
  destroy() {
1010
1049
 
1050
+ document.documentElement.setAttribute( "data-scale", "" );
1051
+
1011
1052
  document.body.removeEventListener( "mousedown", this._onClick, true );
1012
1053
  document.body.removeEventListener( "focusin", this._onClick, true );
1013
1054
 
@@ -1057,7 +1098,18 @@ class DropdownMenu {
1057
1098
  this.root.dataset["side"] = this.side;
1058
1099
  this.root.tabIndex = "1";
1059
1100
  this.root.className = "lexdropdownmenu";
1060
- LX.root.appendChild( this.root );
1101
+
1102
+ const nestedDialog = trigger.closest( "dialog" );
1103
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
1104
+ {
1105
+ this._parent = nestedDialog;
1106
+ }
1107
+ else
1108
+ {
1109
+ this._parent = LX.root;
1110
+ }
1111
+
1112
+ this._parent.appendChild( this.root );
1061
1113
 
1062
1114
  this._create( this._items );
1063
1115
 
@@ -1093,7 +1145,7 @@ class DropdownMenu {
1093
1145
  document.body.removeEventListener( "mousedown", this._onClick, true );
1094
1146
  document.body.removeEventListener( "focusin", this._onClick, true );
1095
1147
 
1096
- LX.root.querySelectorAll( ".lexdropdownmenu" ).forEach( m => { m.remove(); } );
1148
+ this._parent.querySelectorAll( ".lexdropdownmenu" ).forEach( m => { m.remove(); } );
1097
1149
 
1098
1150
  DropdownMenu.currentMenu = null;
1099
1151
 
@@ -1118,14 +1170,22 @@ class DropdownMenu {
1118
1170
  newParent.className = "lexdropdownmenu";
1119
1171
  newParent.dataset["id"] = parentDom.dataset["id"];
1120
1172
  newParent.dataset["side"] = "right"; // submenus always come from the right
1121
- LX.root.appendChild( newParent );
1173
+ this._parent.appendChild( newParent );
1122
1174
 
1123
1175
  newParent.currentParent = parentDom;
1124
1176
  parentDom = newParent;
1125
1177
 
1126
1178
  LX.doAsync( () => {
1179
+
1127
1180
  const position = [ parentRect.x + parentRect.width, parentRect.y ];
1128
1181
 
1182
+ if( this._parent instanceof HTMLDialogElement )
1183
+ {
1184
+ let rootParentRect = this._parent.getBoundingClientRect();
1185
+ position[ 0 ] -= rootParentRect.x;
1186
+ position[ 1 ] -= rootParentRect.y;
1187
+ }
1188
+
1129
1189
  if( this.avoidCollisions )
1130
1190
  {
1131
1191
  position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - newParent.offsetWidth - this._windowPadding );
@@ -1141,139 +1201,180 @@ class DropdownMenu {
1141
1201
 
1142
1202
  for( let item of items )
1143
1203
  {
1144
- if( !item )
1145
- {
1146
- this._addSeparator( parentDom );
1147
- continue;
1148
- }
1204
+ this._createItem( item, parentDom, applyIconPadding );
1205
+ }
1206
+ }
1149
1207
 
1150
- const key = item.name ?? item;
1151
- const pKey = LX.getSupportedDOMName( key );
1208
+ _createItem( item, parentDom, applyIconPadding ) {
1152
1209
 
1153
- // Item already created
1154
- if( parentDom.querySelector( "#" + pKey ) )
1155
- {
1156
- continue;
1157
- }
1210
+ if( !item )
1211
+ {
1212
+ this._addSeparator( parentDom );
1213
+ return;
1214
+ }
1158
1215
 
1159
- const menuItem = document.createElement('div');
1160
- menuItem.className = "lexdropdownmenuitem" + ( item.name ? "" : " label" ) + ( item.disabled ?? false ? " disabled" : "" ) + ( ` ${ item.className ?? "" }` );
1161
- menuItem.dataset["id"] = pKey;
1162
- menuItem.innerHTML = `<span>${ key }</span>`;
1163
- menuItem.tabIndex = "1";
1164
- parentDom.appendChild( menuItem );
1216
+ const key = item.name ?? item;
1217
+ const pKey = LX.getSupportedDOMName( key );
1165
1218
 
1166
- if( item.constructor === String ) // Label case
1167
- {
1168
- continue;
1169
- }
1219
+ // Item already created
1220
+ if( parentDom.querySelector( "#" + pKey ) )
1221
+ {
1222
+ return;
1223
+ }
1170
1224
 
1171
- if( item.submenu )
1172
- {
1173
- const submenuIcon = LX.makeIcon( "Right", { svgClass: "sm" } );
1174
- menuItem.appendChild( submenuIcon );
1175
- }
1176
- else if( item.kbd )
1177
- {
1178
- item.kbd = [].concat( item.kbd );
1225
+ const menuItem = document.createElement('div');
1226
+ menuItem.className = "lexdropdownmenuitem" + ( item.name ? "" : " label" ) + ( item.disabled ?? false ? " disabled" : "" ) + ( ` ${ item.className ?? "" }` );
1227
+ menuItem.dataset["id"] = pKey;
1228
+ menuItem.innerHTML = `<span>${ key }</span>`;
1229
+ menuItem.tabIndex = "1";
1230
+ parentDom.appendChild( menuItem );
1179
1231
 
1180
- const kbd = LX.makeKbd( item.kbd );
1181
- menuItem.appendChild( kbd );
1232
+ if( item.constructor === String ) // Label case
1233
+ {
1234
+ return;
1235
+ }
1182
1236
 
1183
- document.addEventListener( "keydown", e => {
1184
- if( !this._trigger.ddm ) return;
1185
- e.preventDefault();
1186
- // Check if it's a letter or other key
1187
- let kdbKey = item.kbd.join("");
1188
- kdbKey = kdbKey.length == 1 ? kdbKey.toLowerCase() : kdbKey;
1189
- if( kdbKey == e.key )
1190
- {
1191
- menuItem.click();
1192
- }
1193
- } );
1194
- }
1237
+ if( item.submenu )
1238
+ {
1239
+ const submenuIcon = LX.makeIcon( "Right", { svgClass: "sm" } );
1240
+ menuItem.appendChild( submenuIcon );
1241
+ }
1242
+ else if( item.kbd )
1243
+ {
1244
+ item.kbd = [].concat( item.kbd );
1195
1245
 
1196
- const disabled = item.disabled ?? false;
1246
+ const kbd = LX.makeKbd( item.kbd );
1247
+ menuItem.appendChild( kbd );
1197
1248
 
1198
- if( item.icon )
1199
- {
1200
- const icon = LX.makeIcon( item.icon, { svgClass: disabled ? "fg-tertiary" : item.className } );
1201
- menuItem.prepend( icon );
1202
- }
1203
- else if( item.checked == undefined && applyIconPadding ) // no checkbox, no icon, apply padding if there's checkbox or icon in other items
1204
- {
1205
- menuItem.classList.add( "pl-8" );
1206
- }
1249
+ document.addEventListener( "keydown", e => {
1250
+ if( !this._trigger.ddm ) return;
1251
+ e.preventDefault();
1252
+ // Check if it's a letter or other key
1253
+ let kdbKey = item.kbd.join("");
1254
+ kdbKey = kdbKey.length == 1 ? kdbKey.toLowerCase() : kdbKey;
1255
+ if( kdbKey == e.key )
1256
+ {
1257
+ menuItem.click();
1258
+ }
1259
+ } );
1260
+ }
1207
1261
 
1208
- if( disabled )
1209
- {
1210
- continue;
1211
- }
1262
+ const disabled = item.disabled ?? false;
1212
1263
 
1213
- if( item.checked != undefined )
1264
+ if( this._radioGroup !== undefined )
1265
+ {
1266
+ if( item.name === this._radioGroup.selected )
1214
1267
  {
1215
- const checkbox = new LX.Checkbox( pKey + "_entryChecked", item.checked, (v) => {
1216
- const f = item[ 'callback' ];
1217
- item.checked = v;
1218
- if( f )
1219
- {
1220
- f.call( this, key, v, menuItem );
1221
- }
1222
- }, { className: "accent" });
1223
- const input = checkbox.root.querySelector( "input" );
1224
- input.classList.add( "ml-auto" );
1225
- menuItem.appendChild( input );
1226
-
1227
- menuItem.addEventListener( "click", (e) => {
1228
- if( e.target.type == "checkbox" ) return;
1229
- input.checked = !input.checked;
1230
- checkbox.set( input.checked );
1231
- } );
1268
+ const icon = LX.makeIcon( "Circle", { svgClass: "xxs fill-current" } );
1269
+ menuItem.prepend( icon );
1232
1270
  }
1233
- else
1234
- {
1235
- menuItem.addEventListener( "click", () => {
1236
- const f = item[ 'callback' ];
1237
- if( f )
1238
- {
1239
- f.call( this, key, menuItem );
1240
- }
1241
1271
 
1242
- this.destroy( true );
1243
- } );
1244
- }
1272
+ menuItem.setAttribute( "data-radioname", this._radioGroup.name );
1273
+ }
1274
+ else if( item.icon )
1275
+ {
1276
+ const icon = LX.makeIcon( item.icon, { svgClass: disabled ? "fg-tertiary" : item.className } );
1277
+ menuItem.prepend( icon );
1278
+ }
1279
+ else if( item.checked == undefined && applyIconPadding ) // no checkbox, no icon, apply padding if there's checkbox or icon in other items
1280
+ {
1281
+ menuItem.classList.add( "pl-8" );
1282
+ }
1245
1283
 
1246
- menuItem.addEventListener("mouseover", e => {
1284
+ if( disabled )
1285
+ {
1286
+ return;
1287
+ }
1247
1288
 
1248
- let path = menuItem.dataset["id"];
1249
- let p = parentDom;
1289
+ if( item.checked != undefined )
1290
+ {
1291
+ const checkbox = new LX.Checkbox( pKey + "_entryChecked", item.checked, (v) => {
1292
+ const f = item[ 'callback' ];
1293
+ item.checked = v;
1294
+ if( f )
1295
+ {
1296
+ f.call( this, key, v, menuItem );
1297
+ }
1298
+ }, { className: "accent" });
1299
+ const input = checkbox.root.querySelector( "input" );
1300
+ input.classList.add( "ml-auto" );
1301
+ menuItem.appendChild( input );
1302
+
1303
+ menuItem.addEventListener( "click", (e) => {
1304
+ if( e.target.type == "checkbox" ) return;
1305
+ input.checked = !input.checked;
1306
+ checkbox.set( input.checked );
1307
+ } );
1308
+ }
1309
+ else
1310
+ {
1311
+ menuItem.addEventListener( "click", () => {
1312
+ const f = item[ 'callback' ];
1313
+ if( f )
1314
+ {
1315
+ f.call( this, key, menuItem );
1316
+ }
1250
1317
 
1251
- while( p )
1318
+ const radioName = menuItem.getAttribute( "data-radioname" );
1319
+ if( radioName )
1252
1320
  {
1253
- path += "/" + p.dataset["id"];
1254
- p = p.currentParent?.parentElement;
1321
+ this._trigger[ radioName ] = key;
1255
1322
  }
1256
1323
 
1257
- LX.root.querySelectorAll( ".lexdropdownmenu" ).forEach( m => {
1258
- if( !path.includes( m.dataset["id"] ) )
1259
- {
1260
- m.currentParent.built = false;
1261
- m.remove();
1262
- }
1263
- } );
1324
+ this.destroy( true );
1325
+ } );
1326
+ }
1264
1327
 
1265
- if( item.submenu && this.inPlace )
1328
+ menuItem.addEventListener("mouseover", e => {
1329
+
1330
+ let path = menuItem.dataset["id"];
1331
+ let p = parentDom;
1332
+
1333
+ while( p )
1334
+ {
1335
+ path += "/" + p.dataset["id"];
1336
+ p = p.currentParent?.parentElement;
1337
+ }
1338
+
1339
+ this._parent.querySelectorAll( ".lexdropdownmenu" ).forEach( m => {
1340
+ if( !path.includes( m.dataset["id"] ) )
1266
1341
  {
1267
- if( menuItem.built )
1268
- {
1269
- return;
1270
- }
1271
- menuItem.built = true;
1272
- this._create( item.submenu, menuItem );
1342
+ m.currentParent.built = false;
1343
+ m.remove();
1273
1344
  }
1345
+ } );
1274
1346
 
1275
- e.stopPropagation();
1276
- });
1347
+ if( item.submenu && this.inPlace )
1348
+ {
1349
+ if( menuItem.built )
1350
+ {
1351
+ return;
1352
+ }
1353
+ menuItem.built = true;
1354
+ this._create( item.submenu, menuItem );
1355
+ }
1356
+
1357
+ e.stopPropagation();
1358
+ });
1359
+
1360
+ if( item.options )
1361
+ {
1362
+ this._addSeparator();
1363
+
1364
+ console.assert( this._trigger[ item.name ] && "An item of the radio group must be selected!" );
1365
+ this._radioGroup = {
1366
+ name: item.name,
1367
+ selected: this._trigger[ item.name ]
1368
+ };
1369
+
1370
+ for( let o of item.options )
1371
+ {
1372
+ this._createItem( o, parentDom, applyIconPadding );
1373
+ }
1374
+
1375
+ delete this._radioGroup;
1376
+
1377
+ this._addSeparator();
1277
1378
  }
1278
1379
  }
1279
1380
 
@@ -1333,6 +1434,13 @@ class DropdownMenu {
1333
1434
  position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - this.root.offsetHeight - this._windowPadding );
1334
1435
  }
1335
1436
 
1437
+ if( this._parent instanceof HTMLDialogElement )
1438
+ {
1439
+ let parentRect = this._parent.getBoundingClientRect();
1440
+ position[ 0 ] -= parentRect.x;
1441
+ position[ 1 ] -= parentRect.y;
1442
+ }
1443
+
1336
1444
  this.root.style.left = `${ position[ 0 ] }px`;
1337
1445
  this.root.style.top = `${ position[ 1 ] }px`;
1338
1446
  this.inPlace = true;
@@ -1593,25 +1701,25 @@ class ColorPicker {
1593
1701
  this._updateColorValue( null, true );
1594
1702
  } ).root );
1595
1703
 
1596
- this.labelWidget = new LX.TextInput( null, "", null, { inputClass: "bg-none", fit: true, disabled: true } );
1597
- colorLabel.appendChild( this.labelWidget.root );
1704
+ this.labelComponent = new LX.TextInput( null, "", null, { inputClass: "bg-none", fit: true, disabled: true } );
1705
+ colorLabel.appendChild( this.labelComponent.root );
1598
1706
 
1599
1707
  // Copy button
1600
1708
  {
1601
- const copyButtonWidget = new LX.Button(null, "copy", async () => {
1602
- navigator.clipboard.writeText( this.labelWidget.value() );
1603
- copyButtonWidget.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "none";
1709
+ const copyButtonComponent = new LX.Button(null, "copy", async () => {
1710
+ navigator.clipboard.writeText( this.labelComponent.value() );
1711
+ copyButtonComponent.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "none";
1604
1712
 
1605
1713
  LX.doAsync( () => {
1606
- copyButtonWidget.swap( true );
1607
- copyButtonWidget.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "auto";
1714
+ copyButtonComponent.swap( true );
1715
+ copyButtonComponent.root.querySelector( "input[type='checkbox']" ).style.pointerEvents = "auto";
1608
1716
  }, 3000 );
1609
1717
 
1610
1718
  }, { swap: "Check", icon: "Copy", buttonClass: "bg-none", className: "ml-auto", title: "Copy" } );
1611
1719
 
1612
- copyButtonWidget.root.querySelector( ".swap-on svg" ).addClass( "fg-success" );
1720
+ copyButtonComponent.root.querySelector( ".swap-on svg" ).addClass( "fg-success" );
1613
1721
 
1614
- colorLabel.appendChild( copyButtonWidget.root );
1722
+ colorLabel.appendChild( copyButtonComponent.root );
1615
1723
  }
1616
1724
 
1617
1725
  this._updateColorValue( hexValue, true );
@@ -1666,25 +1774,25 @@ class ColorPicker {
1666
1774
  if( this.colorModel == "CSS" )
1667
1775
  {
1668
1776
  const { r, g, b, a } = this.currentColor.css;
1669
- this.labelWidget.set( `rgb${ this.useAlpha ? 'a' : '' }(${ r },${ g },${ b }${ this.useAlpha ? ',' + toFixed( a ) : '' })` );
1777
+ this.labelComponent.set( `rgb${ this.useAlpha ? 'a' : '' }(${ r },${ g },${ b }${ this.useAlpha ? ',' + toFixed( a ) : '' })` );
1670
1778
  }
1671
1779
  else if( this.colorModel == "Hex" )
1672
1780
  {
1673
- this.labelWidget.set( ( this.useAlpha ? this.currentColor.hex : this.currentColor.hex.substr( 0, 7 ) ).toUpperCase() );
1781
+ this.labelComponent.set( ( this.useAlpha ? this.currentColor.hex : this.currentColor.hex.substr( 0, 7 ) ).toUpperCase() );
1674
1782
  }
1675
1783
  else if( this.colorModel == "HSV" )
1676
1784
  {
1677
1785
  const { h, s, v, a } = this.currentColor.hsv;
1678
1786
  const components = [ Math.floor( h ) + 'º', Math.floor( s * 100 ) + '%', Math.floor( v * 100 ) + '%' ];
1679
1787
  if( this.useAlpha ) components.push( toFixed( a ) );
1680
- this.labelWidget.set( components.join( ' ' ) );
1788
+ this.labelComponent.set( components.join( ' ' ) );
1681
1789
  }
1682
1790
  else // RGB
1683
1791
  {
1684
1792
  const { r, g, b, a } = this.currentColor.rgb;
1685
1793
  const components = [ toFixed( r ), toFixed( g ), toFixed( b ) ];
1686
1794
  if( this.useAlpha ) components.push( toFixed( a ) );
1687
- this.labelWidget.set( components.join( ' ' ) );
1795
+ this.labelComponent.set( components.join( ' ' ) );
1688
1796
  }
1689
1797
  }
1690
1798
 
@@ -1848,8 +1956,6 @@ class Calendar {
1848
1956
  let fromRangeDate = this.range ? LX.dateFromDateString( this.range[ 0 ] ) : null;
1849
1957
  let toRangeDate = this.range ? LX.dateFromDateString( this.range[ 1 ] ) : null;
1850
1958
 
1851
- this.currentDate ? new Date( `${ this.currentDate.month }/${ this.currentDate.day }/${ this.currentDate.year }` ) : null;
1852
-
1853
1959
  for( let week = 0; week < 6; week++ )
1854
1960
  {
1855
1961
  const hrow = document.createElement( 'tr' );
@@ -2000,6 +2106,13 @@ class Calendar {
2000
2106
  this.refresh();
2001
2107
  }
2002
2108
 
2109
+ setMonth( month ) {
2110
+
2111
+ this.month = month;
2112
+
2113
+ this.fromMonthYear( this.month, this.year );
2114
+ }
2115
+
2003
2116
  _getOrdinalSuffix( day ) {
2004
2117
  if ( day > 3 && day < 21 ) return "th";
2005
2118
  switch ( day % 10 )
@@ -2028,7 +2141,8 @@ class CalendarRange {
2028
2141
 
2029
2142
  console.assert( range && range.constructor === Array, "Range cannot be empty and has to be an Array!" );
2030
2143
 
2031
- let mustAdvanceMonth = false;
2144
+ let mustSetMonth = null;
2145
+ let dateReversed = false;
2032
2146
 
2033
2147
  // Fix any issues with date range picking
2034
2148
  {
@@ -2040,9 +2154,10 @@ class CalendarRange {
2040
2154
  const tmp = range[ 0 ];
2041
2155
  range[ 0 ] = range[ 1 ];
2042
2156
  range[ 1 ] = tmp;
2157
+ dateReversed = true;
2043
2158
  }
2044
2159
 
2045
- mustAdvanceMonth = ( t0.getMonth() == t1.getMonth() ) && ( t0.getFullYear() == t1.getFullYear() );
2160
+ mustSetMonth = (dateReversed ? t1.getMonth() : t0.getMonth() ) + 2; // +1 to convert range, +1 to use next month
2046
2161
  }
2047
2162
 
2048
2163
  this.from = range[ 0 ];
@@ -2095,10 +2210,8 @@ class CalendarRange {
2095
2210
  range
2096
2211
  });
2097
2212
 
2098
- if( mustAdvanceMonth )
2099
- {
2100
- this.toCalendar._nextMonth( true );
2101
- }
2213
+ console.assert( mustSetMonth && "New Month must be valid" );
2214
+ this.toCalendar.setMonth( mustSetMonth );
2102
2215
 
2103
2216
  this.root.appendChild( this.fromCalendar.root );
2104
2217
  this.root.appendChild( this.toCalendar.root );
@@ -2322,6 +2435,19 @@ class Tabs {
2322
2435
  tabEl.instance = this;
2323
2436
  contentEl.id = tabEl.id + "_content";
2324
2437
 
2438
+ if( options.badge )
2439
+ {
2440
+ const asChild = options.badge.asChild ?? false;
2441
+ const badgeOptions = { };
2442
+
2443
+ if( asChild )
2444
+ {
2445
+ badgeOptions.parent = tabEl;
2446
+ }
2447
+
2448
+ tabEl.innerHTML += LX.badge( options.badge.content ?? "", options.badge.className, badgeOptions );
2449
+ }
2450
+
2325
2451
  if( tabEl.selected )
2326
2452
  {
2327
2453
  this.selected = name;
@@ -2594,7 +2720,7 @@ class Dialog {
2594
2720
 
2595
2721
  if( !callback )
2596
2722
  {
2597
- console.warn("Content is empty, add some widgets using 'callback' parameter!");
2723
+ console.warn("Content is empty, add some components using 'callback' parameter!");
2598
2724
  }
2599
2725
 
2600
2726
  this._oncreate = callback;
@@ -2609,6 +2735,7 @@ class Dialog {
2609
2735
  let root = document.createElement('dialog');
2610
2736
  root.className = "lexdialog " + (options.className ?? "");
2611
2737
  root.id = options.id ?? "dialog" + Dialog._last_id++;
2738
+ root.dataset["modal"] = modal;
2612
2739
  LX.root.appendChild( root );
2613
2740
 
2614
2741
  LX.doAsync( () => {
@@ -2662,9 +2789,9 @@ class Dialog {
2662
2789
  const panelChildCount = panel.root.childElementCount;
2663
2790
 
2664
2791
  const branch = panel.branch( data.name, { closed: data.closed } );
2665
- branch.widgets = data.widgets;
2792
+ branch.components = data.components;
2666
2793
 
2667
- for( let w of branch.widgets )
2794
+ for( let w of branch.components )
2668
2795
  {
2669
2796
  branch.content.appendChild( w.root );
2670
2797
  }
@@ -2908,9 +3035,6 @@ class ContextMenu {
2908
3035
 
2909
3036
  this.root = document.createElement( "div" );
2910
3037
  this.root.className = "lexcontextmenu";
2911
- this.root.style.left = ( event.x - 48 ) + "px";
2912
- this.root.style.top = ( event.y - 8 ) + "px";
2913
-
2914
3038
  this.root.addEventListener("mouseleave", function() {
2915
3039
  this.remove();
2916
3040
  });
@@ -2926,31 +3050,57 @@ class ContextMenu {
2926
3050
  item[ "icon" ] = options.icon;
2927
3051
  this.items.push( item );
2928
3052
  }
3053
+
3054
+ const nestedDialog = event.target.closest( "dialog" );
3055
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
3056
+ {
3057
+ this._parent = nestedDialog;
3058
+ }
3059
+ else
3060
+ {
3061
+ this._parent = LX.root;
3062
+ }
3063
+
3064
+ this._parent.appendChild( this.root );
3065
+
3066
+ // Set position based on parent
3067
+ const position = [ event.x - 48, event.y - 8 ];
3068
+ if( this._parent instanceof HTMLDialogElement )
3069
+ {
3070
+ let parentRect = this._parent.getBoundingClientRect();
3071
+ position[ 0 ] -= parentRect.x;
3072
+ position[ 1 ] -= parentRect.y;
3073
+ }
3074
+
3075
+ this.root.style.left = `${ position[ 0 ] }px`;
3076
+ this.root.style.top = `${ position[ 1 ] }px`;
2929
3077
  }
2930
3078
 
2931
3079
  _adjustPosition( div, margin, useAbsolute = false ) {
2932
3080
 
2933
3081
  let rect = div.getBoundingClientRect();
3082
+ let left = parseInt( div.style.left );
3083
+ let top = parseInt( div.style.top );
2934
3084
 
2935
3085
  if( !useAbsolute )
2936
3086
  {
2937
3087
  let width = rect.width;
2938
3088
  if( rect.left < 0 )
2939
3089
  {
2940
- div.style.left = margin + "px";
3090
+ left = margin;
2941
3091
  }
2942
3092
  else if( window.innerWidth - rect.right < 0 )
2943
3093
  {
2944
- div.style.left = (window.innerWidth - width - margin) + "px";
3094
+ left = (window.innerWidth - width - margin);
2945
3095
  }
2946
3096
 
2947
3097
  if( rect.top < 0 )
2948
3098
  {
2949
- div.style.top = margin + "px";
3099
+ top = margin;
2950
3100
  }
2951
3101
  else if( (rect.top + rect.height) > window.innerHeight )
2952
3102
  {
2953
- div.style.top = (window.innerHeight - rect.height - margin) + "px";
3103
+ top = (window.innerHeight - rect.height - margin);
2954
3104
  }
2955
3105
  }
2956
3106
  else
@@ -2958,15 +3108,18 @@ class ContextMenu {
2958
3108
  let dt = window.innerWidth - rect.right;
2959
3109
  if( dt < 0 )
2960
3110
  {
2961
- div.style.left = div.offsetLeft + (dt - margin) + "px";
3111
+ left = div.offsetLeft + (dt - margin);
2962
3112
  }
2963
3113
 
2964
3114
  dt = window.innerHeight - (rect.top + rect.height);
2965
3115
  if( dt < 0 )
2966
3116
  {
2967
- div.style.top = div.offsetTop + (dt - margin + 20 ) + "px";
3117
+ top = div.offsetTop + (dt - margin + 20 );
2968
3118
  }
2969
3119
  }
3120
+
3121
+ div.style.left = `${ left }px`;
3122
+ div.style.top = `${ top }px`;
2970
3123
  }
2971
3124
 
2972
3125
  _createSubmenu( o, k, c, d ) {
@@ -3188,7 +3341,6 @@ LX.ContextMenu = ContextMenu;
3188
3341
  function addContextMenu( title, event, callback, options )
3189
3342
  {
3190
3343
  const menu = new ContextMenu( event, title, options );
3191
- LX.root.appendChild( menu.root );
3192
3344
 
3193
3345
  if( callback )
3194
3346
  {
@@ -4304,6 +4456,43 @@ class CanvasMap2D {
4304
4456
 
4305
4457
  LX.CanvasMap2D = CanvasMap2D;
4306
4458
 
4459
+ class Skeleton {
4460
+
4461
+ constructor( elements ) {
4462
+
4463
+ this.root = LX.makeContainer( [ "auto", "auto" ], "flex flex-row lexskeleton" );
4464
+
4465
+ if( elements.constructor === String )
4466
+ {
4467
+ this.root.innerHTML = elements;
4468
+ }
4469
+ else
4470
+ {
4471
+ // Force array
4472
+ elements = [].concat( elements );
4473
+
4474
+ for( let e of elements )
4475
+ {
4476
+ this.root.appendChild( e );
4477
+ }
4478
+ }
4479
+ }
4480
+
4481
+ destroy() {
4482
+
4483
+ this.root.dataset[ "closed" ] = true;
4484
+
4485
+ LX.doAsync( () => {
4486
+ this.root.remove();
4487
+ this.root = null;
4488
+ }, 200 );
4489
+ }
4490
+ }
4491
+
4492
+ LX.Skeleton = Skeleton;
4493
+
4494
+ // Js native overrides
4495
+
4307
4496
  Object.defineProperty(String.prototype, 'lastChar', {
4308
4497
  get: function() { return this[ this.length - 1 ]; },
4309
4498
  enumerable: true,
@@ -4604,6 +4793,28 @@ function deleteElement( element )
4604
4793
 
4605
4794
  LX.deleteElement = deleteElement;
4606
4795
 
4796
+ /**
4797
+ * @method toCamelCase
4798
+ * @param {String} str
4799
+ */
4800
+ function toCamelCase( str )
4801
+ {
4802
+ return str.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase());
4803
+ }
4804
+
4805
+ LX.toCamelCase = toCamelCase;
4806
+
4807
+ /**
4808
+ * @method toTitleCase
4809
+ * @param {String} str
4810
+ */
4811
+ function toTitleCase( str )
4812
+ {
4813
+ return str.replace(/-/g, " ").toLowerCase().replace(/\b\w/g, char => char.toUpperCase());
4814
+ }
4815
+
4816
+ LX.toTitleCase = toTitleCase;
4817
+
4607
4818
  /**
4608
4819
  * @method getSupportedDOMName
4609
4820
  * @description Convert a text string to a valid DOM name
@@ -4632,12 +4843,12 @@ LX.getSupportedDOMName = getSupportedDOMName;
4632
4843
 
4633
4844
  /**
4634
4845
  * @method has
4635
- * @description Ask if LexGUI is using a specific component
4636
- * @param {String} componentName Name of the LexGUI component
4846
+ * @description Ask if LexGUI is using a specific extension
4847
+ * @param {String} extensionName Name of the LexGUI extension
4637
4848
  */
4638
- function has( componentName )
4849
+ function has( extensionName )
4639
4850
  {
4640
- return ( LX.components.indexOf( componentName ) > -1 );
4851
+ return ( LX.extensions.indexOf( extensionName ) > -1 );
4641
4852
  }
4642
4853
 
4643
4854
  LX.has = has;
@@ -4814,6 +5025,19 @@ function getThemeColor( colorName )
4814
5025
 
4815
5026
  LX.getThemeColor = getThemeColor;
4816
5027
 
5028
+ /**
5029
+ * @method switchSpacing
5030
+ * @description Toggles between "default" and "compact" spacing layouts
5031
+ */
5032
+ function switchSpacing()
5033
+ {
5034
+ const currentSpacing = document.documentElement.getAttribute( "data-spacing" ) ?? "default";
5035
+ document.documentElement.setAttribute( "data-spacing", ( currentSpacing == "default" ) ? "compact" : "default" );
5036
+ LX.emit( "@on_new_spacing_layout", currentSpacing );
5037
+ }
5038
+
5039
+ LX.switchSpacing = switchSpacing;
5040
+
4817
5041
  /**
4818
5042
  * @method getBase64Image
4819
5043
  * @description Convert an image to a base64 string
@@ -5355,6 +5579,80 @@ function makeKbd( keys, useSpecialKeys = true, extraClass = "" )
5355
5579
 
5356
5580
  LX.makeKbd = makeKbd;
5357
5581
 
5582
+ /**
5583
+ * @method makeBreadcrumb
5584
+ * @description Displays the path to the current resource using a hierarchy
5585
+ * @param {Array} items
5586
+ * @param {Object} options
5587
+ * maxItems: Max items until ellipsis is used to overflow
5588
+ * separatorIcon: Customize separator icon
5589
+ */
5590
+ function makeBreadcrumb( items, options = {} )
5591
+ {
5592
+ const breadcrumb = LX.makeContainer( ["auto", "auto"], "flex flex-row gap-1" );
5593
+
5594
+ const separatorIcon = options.separatorIcon ?? "ChevronRight";
5595
+ const maxItems = options.maxItems ?? 4;
5596
+ const eraseNum = items.length - maxItems;
5597
+ if( eraseNum > 0 )
5598
+ {
5599
+ const erased = items.splice( 1, eraseNum + 1 );
5600
+ const ellipsisItem = { title: "...", ellipsis: erased.map( v => v.title ).join( "/" ) };
5601
+ items.splice( 1, 0, ellipsisItem );
5602
+ }
5603
+
5604
+ for( let i = 0; i < items.length; ++i )
5605
+ {
5606
+ const item = items[ i ];
5607
+ console.assert( item.title, "Breadcrumb item must have a title!" );
5608
+
5609
+ if( i != 0 )
5610
+ {
5611
+ const icon = LX.makeIcon( separatorIcon, { svgClass: "sm fg-secondary separator" } );
5612
+ breadcrumb.appendChild( icon );
5613
+ }
5614
+
5615
+ const lastElement = ( i == items.length - 1 );
5616
+ const breadcrumbItem = LX.makeContainer( ["auto", "auto"], `p-1 flex flex-row gap-1 items-center ${ lastElement ? "" : "fg-secondary" }` );
5617
+ breadcrumb.appendChild( breadcrumbItem );
5618
+
5619
+ let itemTitle = LX.makeElement( "p", "", item.title );
5620
+ if( item.icon )
5621
+ {
5622
+ breadcrumbItem.appendChild( LX.makeIcon( item.icon, { svgClass: "sm" } ) );
5623
+ }
5624
+
5625
+ if( item.items !== undefined )
5626
+ {
5627
+ const bDropdownTrigger = LX.makeContainer( ["auto", "auto"], `${ lastElement ? "" : "fg-secondary" }` );
5628
+ bDropdownTrigger.listen( "click", (e) => {
5629
+ new LX.DropdownMenu( e.target, item.items, { side: "bottom", align: "start" });
5630
+ } );
5631
+ bDropdownTrigger.append( itemTitle );
5632
+ breadcrumbItem.appendChild( bDropdownTrigger );
5633
+ }
5634
+ else if( item.url !== undefined )
5635
+ {
5636
+ let itemUrl = LX.makeElement( "a", `decoration-none fg-${ lastElement ? "primary" : "secondary" }`, "", breadcrumbItem );
5637
+ itemUrl.href = item.url;
5638
+ itemUrl.appendChild( itemTitle );
5639
+ }
5640
+ else
5641
+ {
5642
+ breadcrumbItem.appendChild( itemTitle );
5643
+ }
5644
+
5645
+ if( item.ellipsis )
5646
+ {
5647
+ LX.asTooltip( breadcrumbItem, item.ellipsis, { side: "bottom", offset: 4 } );
5648
+ }
5649
+ }
5650
+
5651
+ return breadcrumb;
5652
+ }
5653
+
5654
+ LX.makeBreadcrumb = makeBreadcrumb;
5655
+
5358
5656
  /**
5359
5657
  * @method makeIcon
5360
5658
  * @description Gets an SVG element using one of LX.ICONS
@@ -5671,6 +5969,7 @@ LX.prompt = prompt;
5671
5969
  * @param {String} title
5672
5970
  * @param {String} description (Optional)
5673
5971
  * @param {Object} options
5972
+ * position: Set new position for the toasts ("top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right")
5674
5973
  * action: Data of the custom action { name, callback }
5675
5974
  * closable: Allow closing the toast
5676
5975
  * timeout: Time in which the toast closed automatically, in ms. -1 means persistent. [3000]
@@ -5687,9 +5986,48 @@ function toast( title, description, options = {} )
5687
5986
 
5688
5987
  const toast = document.createElement( "li" );
5689
5988
  toast.className = "lextoast";
5690
- toast.style.translate = "0 calc(100% + 30px)";
5691
5989
  this.notifications.prepend( toast );
5692
5990
 
5991
+ const [ positionVertical, positionHorizontal ] = options.position ? options.position.split( "-" ) : [ "bottom", "right" ];
5992
+
5993
+ // Reset style
5994
+ this.notifications.style.right = "unset";
5995
+ this.notifications.style.left = "unset";
5996
+ this.notifications.style.top = "unset";
5997
+ this.notifications.style.bottom = "unset";
5998
+ this.notifications.style.placeSelf = "unset";
5999
+
6000
+ switch( positionVertical )
6001
+ {
6002
+ case "top":
6003
+ toast.style.translate = "0 -30px";
6004
+ this.notifications.style.top = "1rem";
6005
+ this.notifications.style.flexDirection = "column";
6006
+ break;
6007
+ case "bottom":
6008
+ toast.style.translate = "0 calc(100% + 30px)";
6009
+ this.notifications.style.top = "auto";
6010
+ this.notifications.style.bottom = "1rem";
6011
+ this.notifications.style.flexDirection = "column-reverse";
6012
+ break;
6013
+ }
6014
+
6015
+ switch( positionHorizontal )
6016
+ {
6017
+ case "left":
6018
+ this.notifications.style.left = "1rem";
6019
+ break;
6020
+ case "center":
6021
+ this.notifications.style.placeSelf = "center";
6022
+ break;
6023
+ case "right":
6024
+ this.notifications.style.right = "1rem";
6025
+ break;
6026
+ }
6027
+
6028
+ toast.classList.add( positionVertical );
6029
+ toast.classList.add( positionHorizontal );
6030
+
5693
6031
  LX.doAsync( () => {
5694
6032
 
5695
6033
  if( this.notifications.offsetWidth > this.notifications.iWidth )
@@ -5728,7 +6066,7 @@ function toast( title, description, options = {} )
5728
6066
  const that = this;
5729
6067
 
5730
6068
  toast.close = function() {
5731
- this.dataset[ "closed" ] = true;
6069
+ this.dataset[ "open" ] = false;
5732
6070
  LX.doAsync( () => {
5733
6071
  this.remove();
5734
6072
  if( !that.notifications.childElementCount )
@@ -5774,7 +6112,32 @@ function badge( text, className, options = {} )
5774
6112
  const container = document.createElement( "div" );
5775
6113
  container.innerHTML = text;
5776
6114
  container.className = "lexbadge " + ( className ?? "" );
6115
+
6116
+ if( options.chip )
6117
+ {
6118
+ container.classList.add( "chip" );
6119
+ }
6120
+
5777
6121
  Object.assign( container.style, options.style ?? {} );
6122
+
6123
+ if( options.callback )
6124
+ {
6125
+ const arrowIcon = LX.makeIcon( "ArrowUpRight", { svgClass: "xs fg-contrast" } );
6126
+ arrowIcon.querySelector("svg").style.marginLeft = "-0.25rem";
6127
+ container.innerHTML += arrowIcon.innerHTML;
6128
+ container.addEventListener( "click", e => {
6129
+ e.preventDefault();
6130
+ e.stopPropagation();
6131
+ options.callback();
6132
+ } );
6133
+ }
6134
+
6135
+ if( options.parent )
6136
+ {
6137
+ options.parent.classList.add( "lexbadge-parent" );
6138
+ options.parent.appendChild( container );
6139
+ }
6140
+
5778
6141
  return ( options.asElement ?? false ) ? container : container.outerHTML;
5779
6142
  }
5780
6143
 
@@ -5849,6 +6212,7 @@ function asTooltip( trigger, content, options = {} )
5849
6212
  trigger.dataset[ "disableTooltip" ] = !( options.active ?? true );
5850
6213
 
5851
6214
  let tooltipDom = null;
6215
+ let tooltipParent = LX.root;
5852
6216
 
5853
6217
  trigger.addEventListener( "mouseenter", function(e) {
5854
6218
 
@@ -5857,12 +6221,22 @@ function asTooltip( trigger, content, options = {} )
5857
6221
  return;
5858
6222
  }
5859
6223
 
5860
- LX.root.querySelectorAll( ".lextooltip" ).forEach( e => e.remove() );
5861
-
5862
6224
  tooltipDom = document.createElement( "div" );
5863
6225
  tooltipDom.className = "lextooltip";
5864
6226
  tooltipDom.innerHTML = content;
5865
6227
 
6228
+ const nestedDialog = trigger.closest( "dialog" );
6229
+ if( nestedDialog && nestedDialog.dataset[ "modal" ] == 'true' )
6230
+ {
6231
+ tooltipParent = nestedDialog;
6232
+ }
6233
+
6234
+ // Remove other first
6235
+ LX.root.querySelectorAll( ".lextooltip" ).forEach( e => e.remove() );
6236
+
6237
+ // Append new tooltip
6238
+ tooltipParent.appendChild( tooltipDom );
6239
+
5866
6240
  LX.doAsync( () => {
5867
6241
 
5868
6242
  const position = [ 0, 0 ];
@@ -5897,11 +6271,16 @@ function asTooltip( trigger, content, options = {} )
5897
6271
  position[ 0 ] = LX.clamp( position[ 0 ], 0, window.innerWidth - tooltipDom.offsetWidth - 4 );
5898
6272
  position[ 1 ] = LX.clamp( position[ 1 ], 0, window.innerHeight - tooltipDom.offsetHeight - 4 );
5899
6273
 
6274
+ if( tooltipParent instanceof HTMLDialogElement )
6275
+ {
6276
+ let parentRect = tooltipParent.getBoundingClientRect();
6277
+ position[ 0 ] -= parentRect.x;
6278
+ position[ 1 ] -= parentRect.y;
6279
+ }
6280
+
5900
6281
  tooltipDom.style.left = `${ position[ 0 ] }px`;
5901
6282
  tooltipDom.style.top = `${ position[ 1 ] }px`;
5902
6283
  } );
5903
-
5904
- LX.root.appendChild( tooltipDom );
5905
6284
  } );
5906
6285
 
5907
6286
  trigger.addEventListener( "mouseleave", function(e) {
@@ -6347,15 +6726,15 @@ class AreaOverlayButtons {
6347
6726
  }
6348
6727
 
6349
6728
  let callback = b.callback;
6350
- let widget = null;
6729
+ let component = null;
6351
6730
 
6352
6731
  if( b.options )
6353
6732
  {
6354
- widget = overlayPanel.addSelect( null, b.options, b.value ?? b.name, callback, _options );
6733
+ component = overlayPanel.addSelect( null, b.options, b.value ?? b.name, callback, _options );
6355
6734
  }
6356
6735
  else
6357
6736
  {
6358
- widget = overlayPanel.addButton( null, b.name, function( value, event ) {
6737
+ component = overlayPanel.addButton( null, b.name, function( value, event ) {
6359
6738
  if( b.selectable )
6360
6739
  {
6361
6740
  if( b.group )
@@ -6372,13 +6751,13 @@ class AreaOverlayButtons {
6372
6751
 
6373
6752
  if( callback )
6374
6753
  {
6375
- callback( value, event, widget.root );
6754
+ callback( value, event, component.root );
6376
6755
  }
6377
6756
 
6378
6757
  }, _options );
6379
6758
  }
6380
6759
 
6381
- this.buttons[ b.name ] = widget;
6760
+ this.buttons[ b.name ] = component;
6382
6761
 
6383
6762
  // ends the group
6384
6763
  if( overlayGroup && last )
@@ -6452,6 +6831,7 @@ class Area {
6452
6831
  * minHeight: Minimum height to be applied when resizing
6453
6832
  * maxWidth: Maximum width to be applied when resizing
6454
6833
  * maxHeight: Maximum height to be applied when resizing
6834
+ * layout: Layout to automatically split the area
6455
6835
  */
6456
6836
 
6457
6837
  constructor( options = {} ) {
@@ -6497,6 +6877,11 @@ class Area {
6497
6877
  lexroot.appendChild( this.root );
6498
6878
  }
6499
6879
 
6880
+ if( options.layout )
6881
+ {
6882
+ this.setLayout( options.layout );
6883
+ }
6884
+
6500
6885
  let overlay = options.overlay;
6501
6886
 
6502
6887
  if( overlay )
@@ -6657,6 +7042,47 @@ class Area {
6657
7042
  this.root.appendChild( element );
6658
7043
  }
6659
7044
 
7045
+ /**
7046
+ * @method setLayout
7047
+ * @description Automatically split the area to generate the desired layout
7048
+ * @param {Array} layout
7049
+ */
7050
+
7051
+ setLayout( layout ) {
7052
+
7053
+ this.layout = LX.deepCopy( layout );
7054
+
7055
+ if( !layout.splits )
7056
+ {
7057
+ console.warn( "Area layout has no splits!" );
7058
+ return;
7059
+ }
7060
+
7061
+ const _splitArea = ( area, layout ) => {
7062
+
7063
+ if( layout.className )
7064
+ {
7065
+ area.root.className += ` ${ layout.className }`;
7066
+ }
7067
+
7068
+ if( !layout.splits )
7069
+ {
7070
+ return;
7071
+ }
7072
+
7073
+ const type = layout.type ?? "horizontal";
7074
+ const resize = layout.resize ?? true;
7075
+ const minimizable = layout.minimizable ?? false;
7076
+ const [ splitA, splitB ] = area.split( { type, resize, minimizable, sizes: [ layout.splits[ 0 ].size, layout.splits[ 1 ].size ] } );
7077
+
7078
+ _splitArea( splitA, layout.splits[ 0 ] );
7079
+ _splitArea( splitB, layout.splits[ 1 ] );
7080
+
7081
+ };
7082
+
7083
+ _splitArea( this, layout );
7084
+ }
7085
+
6660
7086
  /**
6661
7087
  * @method split
6662
7088
  * @param {Object} options
@@ -6915,12 +7341,17 @@ class Area {
6915
7341
  doc.addEventListener( 'mouseup', innerMouseUp );
6916
7342
  e.stopPropagation();
6917
7343
  e.preventDefault();
6918
- document.body.classList.add( 'nocursor' );
6919
- that.splitBar.classList.add( 'nocursor' );
6920
7344
  }
6921
7345
 
6922
7346
  function innerMouseMove( e )
6923
7347
  {
7348
+ const rect = that.root.getBoundingClientRect();
7349
+ if( ( ( e.x ) < rect.x || ( e.x ) > ( rect.x + rect.width ) ) ||
7350
+ ( ( e.y ) < rect.y || ( e.y ) > ( rect.y + rect.height ) ) )
7351
+ {
7352
+ return;
7353
+ }
7354
+
6924
7355
  if( that.type == "horizontal" )
6925
7356
  {
6926
7357
  that._moveSplit( -e.movementX );
@@ -6939,8 +7370,6 @@ class Area {
6939
7370
  const doc = that.root.ownerDocument;
6940
7371
  doc.removeEventListener( 'mousemove', innerMouseMove );
6941
7372
  doc.removeEventListener( 'mouseup', innerMouseUp );
6942
- document.body.classList.remove( 'nocursor' );
6943
- that.splitBar.classList.remove( 'nocursor' );
6944
7373
  }
6945
7374
 
6946
7375
  return this.sections;
@@ -7299,10 +7728,11 @@ class Area {
7299
7728
  }
7300
7729
  else
7301
7730
  {
7731
+ const parentHeight = this.size[ 1 ];
7302
7732
  var size = Math.max( ( a2Root.offsetHeight + dt ) + a2.offset, parseInt( a2.minHeight ) );
7733
+ size = Math.min( parentHeight - LX.DEFAULT_SPLITBAR_SIZE, size );
7303
7734
  if( forceWidth ) size = forceWidth;
7304
7735
 
7305
- const parentHeight = this.size[ 1 ];
7306
7736
  const bottomPercent = ( size / parentHeight ) * 100;
7307
7737
  const topPercent = Math.max( 0, 100 - bottomPercent );
7308
7738
 
@@ -7361,13 +7791,13 @@ class Area {
7361
7791
  }
7362
7792
  LX.Area = Area;
7363
7793
 
7364
- // widget.js @jxarco
7794
+ // base_component.js @jxarco
7365
7795
 
7366
7796
  /**
7367
- * @class Widget
7797
+ * @class BaseComponent
7368
7798
  */
7369
7799
 
7370
- class Widget {
7800
+ class BaseComponent {
7371
7801
 
7372
7802
  static NONE = 0;
7373
7803
  static TEXT = 1;
@@ -7407,14 +7837,15 @@ class Widget {
7407
7837
  static TABS = 35;
7408
7838
  static DATE = 36;
7409
7839
  static MAP2D = 37;
7410
- static LABEL = 38;
7411
- static BLANK = 39;
7840
+ static LABEL = 39;
7841
+ static BLANK = 40;
7842
+ static RATE = 41;
7412
7843
 
7413
7844
  static NO_CONTEXT_TYPES = [
7414
- Widget.BUTTON,
7415
- Widget.LIST,
7416
- Widget.FILE,
7417
- Widget.PROGRESS
7845
+ BaseComponent.BUTTON,
7846
+ BaseComponent.LIST,
7847
+ BaseComponent.FILE,
7848
+ BaseComponent.PROGRESS
7418
7849
  ];
7419
7850
 
7420
7851
  constructor( type, name, value, options = {} ) {
@@ -7425,7 +7856,7 @@ class Widget {
7425
7856
  this._initialValue = value;
7426
7857
 
7427
7858
  const root = document.createElement( 'div' );
7428
- root.className = "lexwidget";
7859
+ root.className = "lexcomponent";
7429
7860
 
7430
7861
  if( options.id )
7431
7862
  {
@@ -7442,7 +7873,7 @@ class Widget {
7442
7873
  root.className += " " + options.className;
7443
7874
  }
7444
7875
 
7445
- if( type != Widget.TITLE )
7876
+ if( type != BaseComponent.TITLE )
7446
7877
  {
7447
7878
  if( options.width )
7448
7879
  {
@@ -7461,7 +7892,7 @@ class Widget {
7461
7892
  root.style.height = root.style.minHeight = options.height;
7462
7893
  }
7463
7894
 
7464
- LX.widgetResizeObserver.observe( root );
7895
+ LX.componentResizeObserver.observe( root );
7465
7896
  }
7466
7897
 
7467
7898
  if( name != undefined )
@@ -7469,7 +7900,7 @@ class Widget {
7469
7900
  if( !( options.hideName ?? false ) )
7470
7901
  {
7471
7902
  let domName = document.createElement( 'div' );
7472
- domName.className = "lexwidgetname";
7903
+ domName.className = "lexcomponentname";
7473
7904
 
7474
7905
  if( options.justifyName )
7475
7906
  {
@@ -7531,7 +7962,7 @@ class Widget {
7531
7962
  }
7532
7963
 
7533
7964
  _canPaste() {
7534
- let pasteAllowed = this.type === Widget.CUSTOM ?
7965
+ let pasteAllowed = this.type === BaseComponent.CUSTOM ?
7535
7966
  ( navigator.clipboard.customIdx !== undefined && this.customIdx == navigator.clipboard.customIdx ) : navigator.clipboard.type === this.type;
7536
7967
 
7537
7968
  pasteAllowed &= ( this.disabled !== true );
@@ -7568,7 +7999,7 @@ class Widget {
7568
7999
 
7569
8000
  if( this.onSetValue )
7570
8001
  {
7571
- let resetButton = this.root.querySelector( ".lexwidgetname .lexicon" );
8002
+ let resetButton = this.root.querySelector( ".lexcomponentname .lexicon" );
7572
8003
  if( resetButton )
7573
8004
  {
7574
8005
  resetButton.style.display = ( value != this.value() ? "block" : "none" );
@@ -7594,7 +8025,7 @@ class Widget {
7594
8025
 
7595
8026
  oncontextmenu( e ) {
7596
8027
 
7597
- if( Widget.NO_CONTEXT_TYPES.includes( this.type ) )
8028
+ if( BaseComponent.NO_CONTEXT_TYPES.includes( this.type ) )
7598
8029
  {
7599
8030
  return;
7600
8031
  }
@@ -7625,41 +8056,42 @@ class Widget {
7625
8056
 
7626
8057
  switch( this.type )
7627
8058
  {
7628
- case Widget.TEXT: return "Text";
7629
- case Widget.TEXTAREA: return "TextArea";
7630
- case Widget.BUTTON: return "Button";
7631
- case Widget.SELECT: return "Select";
7632
- case Widget.CHECKBOX: return "Checkbox";
7633
- case Widget.TOGGLE: return "Toggle";
7634
- case Widget.RADIO: return "Radio";
7635
- case Widget.COLOR: return "Color";
7636
- case Widget.RANGE: return "Range";
7637
- case Widget.NUMBER: return "Number";
7638
- case Widget.VECTOR: return "Vector";
7639
- case Widget.TREE: return "Tree";
7640
- case Widget.PROGRESS: return "Progress";
7641
- case Widget.FILE: return "File";
7642
- case Widget.LAYERS: return "Layers";
7643
- case Widget.ARRAY: return "Array";
7644
- case Widget.LIST: return "List";
7645
- case Widget.TAGS: return "Tags";
7646
- case Widget.CURVE: return "Curve";
7647
- case Widget.KNOB: return "Knob";
7648
- case Widget.SIZE: return "Size";
7649
- case Widget.PAD: return "Pad";
7650
- case Widget.FORM: return "Form";
7651
- case Widget.DIAL: return "Dial";
7652
- case Widget.COUNTER: return "Counter";
7653
- case Widget.TABLE: return "Table";
7654
- case Widget.TABS: return "Tabs";
7655
- case Widget.DATE: return "Date";
7656
- case Widget.MAP2D: return "Map2D";
7657
- case Widget.LABEL: return "Label";
7658
- case Widget.BLANK: return "Blank";
7659
- case Widget.CUSTOM: return this.customName;
7660
- }
7661
-
7662
- console.error( `Unknown Widget type: ${ this.type }` );
8059
+ case BaseComponent.TEXT: return "Text";
8060
+ case BaseComponent.TEXTAREA: return "TextArea";
8061
+ case BaseComponent.BUTTON: return "Button";
8062
+ case BaseComponent.SELECT: return "Select";
8063
+ case BaseComponent.CHECKBOX: return "Checkbox";
8064
+ case BaseComponent.TOGGLE: return "Toggle";
8065
+ case BaseComponent.RADIO: return "Radio";
8066
+ case BaseComponent.COLOR: return "Color";
8067
+ case BaseComponent.RANGE: return "Range";
8068
+ case BaseComponent.NUMBER: return "Number";
8069
+ case BaseComponent.VECTOR: return "Vector";
8070
+ case BaseComponent.TREE: return "Tree";
8071
+ case BaseComponent.PROGRESS: return "Progress";
8072
+ case BaseComponent.FILE: return "File";
8073
+ case BaseComponent.LAYERS: return "Layers";
8074
+ case BaseComponent.ARRAY: return "Array";
8075
+ case BaseComponent.LIST: return "List";
8076
+ case BaseComponent.TAGS: return "Tags";
8077
+ case BaseComponent.CURVE: return "Curve";
8078
+ case BaseComponent.KNOB: return "Knob";
8079
+ case BaseComponent.SIZE: return "Size";
8080
+ case BaseComponent.PAD: return "Pad";
8081
+ case BaseComponent.FORM: return "Form";
8082
+ case BaseComponent.DIAL: return "Dial";
8083
+ case BaseComponent.COUNTER: return "Counter";
8084
+ case BaseComponent.TABLE: return "Table";
8085
+ case BaseComponent.TABS: return "Tabs";
8086
+ case BaseComponent.DATE: return "Date";
8087
+ case BaseComponent.MAP2D: return "Map2D";
8088
+ case BaseComponent.RATE: return "Rate";
8089
+ case BaseComponent.LABEL: return "Label";
8090
+ case BaseComponent.BLANK: return "Blank";
8091
+ case BaseComponent.CUSTOM: return this.customName;
8092
+ }
8093
+
8094
+ console.error( `Unknown Component type: ${ this.type }` );
7663
8095
  }
7664
8096
 
7665
8097
  refresh() {
@@ -7667,52 +8099,52 @@ class Widget {
7667
8099
  }
7668
8100
  }
7669
8101
 
7670
- LX.Widget = Widget;
8102
+ LX.BaseComponent = BaseComponent;
7671
8103
 
7672
- function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
8104
+ function ADD_CUSTOM_COMPONENT( customComponentName, options = {} )
7673
8105
  {
7674
8106
  let customIdx = LX.guidGenerator();
7675
8107
 
7676
- LX.Panel.prototype[ 'add' + customWidgetName ] = function( name, instance, callback ) {
8108
+ LX.Panel.prototype[ 'add' + customComponentName ] = function( name, instance, callback ) {
7677
8109
 
7678
8110
  const userParams = Array.from( arguments ).slice( 3 );
7679
8111
 
7680
- let widget = new Widget( Widget.CUSTOM, name, null, options );
7681
- this._attachWidget( widget );
8112
+ let component = new BaseComponent( BaseComponent.CUSTOM, name, null, options );
8113
+ this._attachComponent( component );
7682
8114
 
7683
- widget.customName = customWidgetName;
7684
- widget.customIdx = customIdx;
8115
+ component.customName = customComponentName;
8116
+ component.customIdx = customIdx;
7685
8117
 
7686
- widget.onGetValue = () => {
8118
+ component.onGetValue = () => {
7687
8119
  return instance;
7688
8120
  };
7689
8121
 
7690
- widget.onSetValue = ( newValue, skipCallback, event ) => {
8122
+ component.onSetValue = ( newValue, skipCallback, event ) => {
7691
8123
  instance = newValue;
7692
- refresh_widget();
8124
+ _refreshComponent();
7693
8125
  element.querySelector( ".lexcustomitems" ).toggleAttribute( 'hidden', false );
7694
8126
  if( !skipCallback )
7695
8127
  {
7696
- widget._trigger( new LX.IEvent( name, instance, event ), callback );
8128
+ component._trigger( new LX.IEvent( name, instance, event ), callback );
7697
8129
  }
7698
8130
  };
7699
8131
 
7700
- widget.onResize = ( rect ) => {
7701
- const realNameWidth = ( widget.root.domName?.style.width ?? "0px" );
8132
+ component.onResize = ( rect ) => {
8133
+ const realNameWidth = ( component.root.domName?.style.width ?? "0px" );
7702
8134
  container.style.width = `calc( 100% - ${ realNameWidth })`;
7703
8135
  };
7704
8136
 
7705
- const element = widget.root;
8137
+ const element = component.root;
7706
8138
 
7707
- let container, customWidgetsDom;
8139
+ let container, customComponentsDom;
7708
8140
  let defaultInstance = options.default ?? {};
7709
8141
 
7710
8142
  // Add instance button
7711
8143
 
7712
- const refresh_widget = () => {
8144
+ const _refreshComponent = () => {
7713
8145
 
7714
8146
  if( container ) container.remove();
7715
- if( customWidgetsDom ) customWidgetsDom.remove();
8147
+ if( customComponentsDom ) customComponentsDom.remove();
7716
8148
 
7717
8149
  container = document.createElement('div');
7718
8150
  container.className = "lexcustomcontainer w-full";
@@ -7722,7 +8154,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7722
8154
  const customIcon = LX.makeIcon( options.icon ?? "Box" );
7723
8155
  const menuIcon = LX.makeIcon( "Menu" );
7724
8156
 
7725
- let buttonName = customWidgetName + (!instance ? " [empty]" : "");
8157
+ let buttonName = customComponentName + (!instance ? " [empty]" : "");
7726
8158
  let buttonEl = this.addButton(null, buttonName, (value, event) => {
7727
8159
  if( instance )
7728
8160
  {
@@ -7732,9 +8164,9 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7732
8164
  else
7733
8165
  {
7734
8166
  LX.addContextMenu(null, event, c => {
7735
- c.add("New " + customWidgetName, () => {
8167
+ c.add("New " + customComponentName, () => {
7736
8168
  instance = {};
7737
- refresh_widget();
8169
+ _refreshComponent();
7738
8170
  element.querySelector(".lexcustomitems").toggleAttribute('hidden', false);
7739
8171
  element.dataset["opened"] = !element.querySelector(".lexcustomitems").hasAttribute("hidden");
7740
8172
  });
@@ -7756,7 +8188,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7756
8188
  LX.addContextMenu(null, e, c => {
7757
8189
  c.add("Clear", () => {
7758
8190
  instance = null;
7759
- refresh_widget();
8191
+ _refreshComponent();
7760
8192
  });
7761
8193
  });
7762
8194
  });
@@ -7764,14 +8196,14 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7764
8196
 
7765
8197
  // Show elements
7766
8198
 
7767
- customWidgetsDom = document.createElement('div');
7768
- customWidgetsDom.className = "lexcustomitems";
7769
- customWidgetsDom.toggleAttribute('hidden', true);
7770
- element.appendChild( customWidgetsDom );
8199
+ customComponentsDom = document.createElement('div');
8200
+ customComponentsDom.className = "lexcustomitems";
8201
+ customComponentsDom.toggleAttribute('hidden', true);
8202
+ element.appendChild( customComponentsDom );
7771
8203
 
7772
8204
  if( instance )
7773
8205
  {
7774
- this.queue( customWidgetsDom );
8206
+ this.queue( customComponentsDom );
7775
8207
 
7776
8208
  const on_instance_changed = ( key, value, event ) => {
7777
8209
  const setter = options[ `_set_${ key }` ];
@@ -7783,7 +8215,7 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7783
8215
  {
7784
8216
  instance[ key ] = value;
7785
8217
  }
7786
- widget._trigger( new LX.IEvent( name, instance, event ), callback );
8218
+ component._trigger( new LX.IEvent( name, instance, event ), callback );
7787
8219
  };
7788
8220
 
7789
8221
  for( let key in defaultInstance )
@@ -7848,11 +8280,11 @@ function ADD_CUSTOM_WIDGET( customWidgetName, options = {} )
7848
8280
  }
7849
8281
  };
7850
8282
 
7851
- refresh_widget();
8283
+ _refreshComponent();
7852
8284
  };
7853
8285
  }
7854
8286
 
7855
- LX.ADD_CUSTOM_WIDGET = ADD_CUSTOM_WIDGET;
8287
+ LX.ADD_CUSTOM_COMPONENT = ADD_CUSTOM_COMPONENT;
7856
8288
 
7857
8289
  /**
7858
8290
  * @class NodeTree
@@ -8424,14 +8856,14 @@ LX.NodeTree = NodeTree;
8424
8856
 
8425
8857
  /**
8426
8858
  * @class Blank
8427
- * @description Blank Widget
8859
+ * @description Blank Component
8428
8860
  */
8429
8861
 
8430
- class Blank extends Widget {
8862
+ class Blank extends BaseComponent {
8431
8863
 
8432
8864
  constructor( width, height ) {
8433
8865
 
8434
- super( Widget.BLANK );
8866
+ super( BaseComponent.BLANK );
8435
8867
 
8436
8868
  this.root.style.width = width ?? "auto";
8437
8869
  this.root.style.height = height ?? "8px";
@@ -8442,17 +8874,17 @@ LX.Blank = Blank;
8442
8874
 
8443
8875
  /**
8444
8876
  * @class Title
8445
- * @description Title Widget
8877
+ * @description Title Component
8446
8878
  */
8447
8879
 
8448
- class Title extends Widget {
8880
+ class Title extends BaseComponent {
8449
8881
 
8450
8882
  constructor( name, options = {} ) {
8451
8883
 
8452
- console.assert( name, "Can't create Title Widget without text!" );
8884
+ console.assert( name, "Can't create Title Component without text!" );
8453
8885
 
8454
- // Note: Titles are not registered in Panel.widgets by now
8455
- super( Widget.TITLE, null, null, options );
8886
+ // Note: Titles are not registered in Panel.components by now
8887
+ super( BaseComponent.TITLE, null, null, options );
8456
8888
 
8457
8889
  this.root.className = `lextitle ${ this.root.className }`;
8458
8890
 
@@ -8486,14 +8918,14 @@ LX.Title = Title;
8486
8918
 
8487
8919
  /**
8488
8920
  * @class TextInput
8489
- * @description TextInput Widget
8921
+ * @description TextInput Component
8490
8922
  */
8491
8923
 
8492
- class TextInput extends Widget {
8924
+ class TextInput extends BaseComponent {
8493
8925
 
8494
8926
  constructor( name, value, callback, options = {} ) {
8495
8927
 
8496
- super( Widget.TEXT, name, String( value ), options );
8928
+ super( BaseComponent.TEXT, name, String( value ), options );
8497
8929
 
8498
8930
  this.onGetValue = () => {
8499
8931
  return value;
@@ -8626,14 +9058,14 @@ LX.TextInput = TextInput;
8626
9058
 
8627
9059
  /**
8628
9060
  * @class TextArea
8629
- * @description TextArea Widget
9061
+ * @description TextArea Component
8630
9062
  */
8631
9063
 
8632
- class TextArea extends Widget {
9064
+ class TextArea extends BaseComponent {
8633
9065
 
8634
9066
  constructor( name, value, callback, options = {} ) {
8635
9067
 
8636
- super( Widget.TEXTAREA, name, value, options );
9068
+ super( BaseComponent.TEXTAREA, name, value, options );
8637
9069
 
8638
9070
  this.onGetValue = () => {
8639
9071
  return value;
@@ -8727,14 +9159,14 @@ LX.TextArea = TextArea;
8727
9159
 
8728
9160
  /**
8729
9161
  * @class Button
8730
- * @description Button Widget
9162
+ * @description Button Component
8731
9163
  */
8732
9164
 
8733
- class Button extends Widget {
9165
+ class Button extends BaseComponent {
8734
9166
 
8735
9167
  constructor( name, value, callback, options = {} ) {
8736
9168
 
8737
- super( Widget.BUTTON, name, null, options );
9169
+ super( BaseComponent.BUTTON, name, null, options );
8738
9170
 
8739
9171
  this.onGetValue = () => {
8740
9172
  const swapInput = wValue.querySelector( "input" );
@@ -8949,10 +9381,10 @@ LX.Button = Button;
8949
9381
 
8950
9382
  /**
8951
9383
  * @class ComboButtons
8952
- * @description ComboButtons Widget
9384
+ * @description ComboButtons Component
8953
9385
  */
8954
9386
 
8955
- class ComboButtons extends Widget {
9387
+ class ComboButtons extends BaseComponent {
8956
9388
 
8957
9389
  constructor( name, values, options = {} ) {
8958
9390
 
@@ -9067,7 +9499,7 @@ class ComboButtons extends Widget {
9067
9499
  currentValue = currentValue[ 0 ];
9068
9500
  }
9069
9501
 
9070
- super( Widget.BUTTONS, name, null, options );
9502
+ super( BaseComponent.BUTTONS, name, null, options );
9071
9503
 
9072
9504
  this.onGetValue = () => {
9073
9505
  return currentValue;
@@ -9110,16 +9542,16 @@ LX.ComboButtons = ComboButtons;
9110
9542
 
9111
9543
  /**
9112
9544
  * @class Card
9113
- * @description Card Widget
9545
+ * @description Card Component
9114
9546
  */
9115
9547
 
9116
- class Card extends Widget {
9548
+ class Card extends BaseComponent {
9117
9549
 
9118
9550
  constructor( name, options = {} ) {
9119
9551
 
9120
9552
  options.hideName = true;
9121
9553
 
9122
- super( Widget.CARD, name, null, options );
9554
+ super( BaseComponent.CARD, name, null, options );
9123
9555
 
9124
9556
  let container = document.createElement('div');
9125
9557
  container.className = "lexcard";
@@ -9173,10 +9605,10 @@ LX.Card = Card;
9173
9605
 
9174
9606
  /**
9175
9607
  * @class Form
9176
- * @description Form Widget
9608
+ * @description Form Component
9177
9609
  */
9178
9610
 
9179
- class Form extends Widget {
9611
+ class Form extends BaseComponent {
9180
9612
 
9181
9613
  constructor( name, data, callback, options = {} ) {
9182
9614
 
@@ -9189,7 +9621,7 @@ class Form extends Widget {
9189
9621
  // Always hide name for this one
9190
9622
  options.hideName = true;
9191
9623
 
9192
- super( Widget.FORM, name, null, options );
9624
+ super( BaseComponent.FORM, name, null, options );
9193
9625
 
9194
9626
  this.onGetValue = () => {
9195
9627
  return container.formData;
@@ -9197,18 +9629,18 @@ class Form extends Widget {
9197
9629
 
9198
9630
  this.onSetValue = ( newValue, skipCallback, event ) => {
9199
9631
  container.formData = newValue;
9200
- const entries = container.querySelectorAll( ".lexwidget" );
9632
+ const entries = container.querySelectorAll( ".lexcomponent" );
9201
9633
  for( let i = 0; i < entries.length; ++i )
9202
9634
  {
9203
9635
  const entry = entries[ i ];
9204
- if( entry.jsInstance.type != LX.Widget.TEXT )
9636
+ if( entry.jsInstance.type != BaseComponent.TEXT )
9205
9637
  {
9206
9638
  continue;
9207
9639
  }
9208
- let entryName = entries[ i ].querySelector( ".lexwidgetname" ).innerText;
9640
+ let entryName = entries[ i ].querySelector( ".lexcomponentname" ).innerText;
9209
9641
  let entryInput = entries[ i ].querySelector( ".lextext input" );
9210
9642
  entryInput.value = newValue[ entryName ] ?? "";
9211
- Widget._dispatchEvent( entryInput, "focusout", skipCallback );
9643
+ BaseComponent._dispatchEvent( entryInput, "focusout", skipCallback );
9212
9644
  }
9213
9645
  };
9214
9646
 
@@ -9238,10 +9670,10 @@ class Form extends Widget {
9238
9670
  container.appendChild( label.root );
9239
9671
  }
9240
9672
 
9241
- entryData.textWidget = new LX.TextInput( null, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
9673
+ entryData.textComponent = new LX.TextInput( null, entryData.constructor == Object ? entryData.value : entryData, ( value ) => {
9242
9674
  container.formData[ entry ] = value;
9243
9675
  }, entryData );
9244
- container.appendChild( entryData.textWidget.root );
9676
+ container.appendChild( entryData.textComponent.root );
9245
9677
 
9246
9678
  container.formData[ entry ] = entryData.constructor == Object ? entryData.value : entryData;
9247
9679
  }
@@ -9266,7 +9698,7 @@ class Form extends Widget {
9266
9698
  {
9267
9699
  let entryData = data[ entry ];
9268
9700
 
9269
- if( !entryData.textWidget.valid() )
9701
+ if( !entryData.textComponent.valid() )
9270
9702
  {
9271
9703
  return;
9272
9704
  }
@@ -9286,14 +9718,14 @@ LX.Form = Form;
9286
9718
 
9287
9719
  /**
9288
9720
  * @class Select
9289
- * @description Select Widget
9721
+ * @description Select Component
9290
9722
  */
9291
9723
 
9292
- class Select extends Widget {
9724
+ class Select extends BaseComponent {
9293
9725
 
9294
9726
  constructor( name, values, value, callback, options = {} ) {
9295
9727
 
9296
- super( Widget.SELECT, name, value, options );
9728
+ super( BaseComponent.SELECT, name, value, options );
9297
9729
 
9298
9730
  this.onGetValue = () => {
9299
9731
  return value;
@@ -9366,7 +9798,7 @@ class Select extends Widget {
9366
9798
  options.overflowContainerX = options.overflowContainerY = options.overflowContainer;
9367
9799
  }
9368
9800
 
9369
- const _placeOptions = ( parent ) => {
9801
+ const _placeOptions = ( parent, forceLastPlacement ) => {
9370
9802
 
9371
9803
  const selectRoot = selectedOption.root;
9372
9804
  const rect = selectRoot.getBoundingClientRect();
@@ -9396,8 +9828,8 @@ class Select extends Widget {
9396
9828
  parent.style.top = ( topPosition + selectRoot.offsetHeight ) + 'px';
9397
9829
  list.style.height = ""; // set auto height by default
9398
9830
 
9399
- const failBelow = ( topPosition + listHeight ) > maxY;
9400
- const failAbove = ( topPosition - listHeight ) < 0;
9831
+ const failAbove = forceLastPlacement ? this._lastPlacement[ 0 ] : ( topPosition - listHeight ) < 0;
9832
+ const failBelow = forceLastPlacement ? this._lastPlacement[ 1 ] : ( topPosition + listHeight ) > maxY;
9401
9833
  if( failBelow && !failAbove )
9402
9834
  {
9403
9835
  parent.style.top = ( topPosition - listHeight ) + 'px';
@@ -9408,6 +9840,8 @@ class Select extends Widget {
9408
9840
  {
9409
9841
  list.style.height = `${ maxY - topPosition - 32 }px`; // 32px margin
9410
9842
  }
9843
+
9844
+ this._lastPlacement = [ failAbove, failBelow ];
9411
9845
  }
9412
9846
 
9413
9847
  // Manage horizontal aspect
@@ -9521,7 +9955,7 @@ class Select extends Widget {
9521
9955
  {
9522
9956
  const filterOptions = LX.deepCopy( options );
9523
9957
  filterOptions.placeholder = filterOptions.placeholder ?? "Search...";
9524
- filterOptions.skipWidget = filterOptions.skipWidget ?? true;
9958
+ filterOptions.skipComponent = filterOptions.skipComponent ?? true;
9525
9959
  filterOptions.trigger = "input";
9526
9960
  filterOptions.icon = "Search";
9527
9961
  filterOptions.className = "lexfilter";
@@ -9530,6 +9964,7 @@ class Select extends Widget {
9530
9964
  filter = new LX.TextInput(null, options.filterValue ?? "", ( v ) => {
9531
9965
  const filteredOptions = this._filterOptions( values, v );
9532
9966
  list.refresh( filteredOptions );
9967
+ _placeOptions( listDialog, true );
9533
9968
  }, filterOptions );
9534
9969
  filter.root.querySelector( ".lextext" ).style.border = "1px solid transparent";
9535
9970
 
@@ -9563,7 +9998,7 @@ class Select extends Widget {
9563
9998
 
9564
9999
  let option = document.createElement( "div" );
9565
10000
  option.className = "option";
9566
- option.innerHTML = iValue;
10001
+ option.innerHTML = ( LX.makeIcon( "Inbox", { svgClass: "mr-2" } ).innerHTML + iValue );
9567
10002
 
9568
10003
  let li = document.createElement( "li" );
9569
10004
  li.className = "lexselectitem empty";
@@ -9672,7 +10107,7 @@ class Select extends Widget {
9672
10107
  const emptyFilter = !value.length;
9673
10108
  let filteredOptions = [];
9674
10109
 
9675
- // Add widgets
10110
+ // Add components
9676
10111
  for( let i = 0; i < options.length; i++ )
9677
10112
  {
9678
10113
  let o = options[ i ];
@@ -9695,16 +10130,16 @@ LX.Select = Select;
9695
10130
 
9696
10131
  /**
9697
10132
  * @class Curve
9698
- * @description Curve Widget
10133
+ * @description Curve Component
9699
10134
  */
9700
10135
 
9701
- class Curve extends Widget {
10136
+ class Curve extends BaseComponent {
9702
10137
 
9703
10138
  constructor( name, values, callback, options = {} ) {
9704
10139
 
9705
10140
  let defaultValues = JSON.parse( JSON.stringify( values ) );
9706
10141
 
9707
- super( Widget.CURVE, name, defaultValues, options );
10142
+ super( BaseComponent.CURVE, name, defaultValues, options );
9708
10143
 
9709
10144
  this.onGetValue = () => {
9710
10145
  return JSON.parse(JSON.stringify( curveInstance.element.value ));
@@ -9756,16 +10191,16 @@ LX.Curve = Curve;
9756
10191
 
9757
10192
  /**
9758
10193
  * @class Dial
9759
- * @description Dial Widget
10194
+ * @description Dial Component
9760
10195
  */
9761
10196
 
9762
- class Dial extends Widget {
10197
+ class Dial extends BaseComponent {
9763
10198
 
9764
10199
  constructor( name, values, callback, options = {} ) {
9765
10200
 
9766
10201
  let defaultValues = JSON.parse( JSON.stringify( values ) );
9767
10202
 
9768
- super( Widget.DIAL, name, defaultValues, options );
10203
+ super( BaseComponent.DIAL, name, defaultValues, options );
9769
10204
 
9770
10205
  this.onGetValue = () => {
9771
10206
  return JSON.parse( JSON.stringify( dialInstance.element.value ) );
@@ -9813,14 +10248,14 @@ LX.Dial = Dial;
9813
10248
 
9814
10249
  /**
9815
10250
  * @class Layers
9816
- * @description Layers Widget
10251
+ * @description Layers Component
9817
10252
  */
9818
10253
 
9819
- class Layers extends Widget {
10254
+ class Layers extends BaseComponent {
9820
10255
 
9821
10256
  constructor( name, value, callback, options = {} ) {
9822
10257
 
9823
- super( Widget.LAYERS, name, value, options );
10258
+ super( BaseComponent.LAYERS, name, value, options );
9824
10259
 
9825
10260
  this.onGetValue = () => {
9826
10261
  return value;
@@ -9897,16 +10332,16 @@ LX.Layers = Layers;
9897
10332
 
9898
10333
  /**
9899
10334
  * @class ItemArray
9900
- * @description ItemArray Widget
10335
+ * @description ItemArray Component
9901
10336
  */
9902
10337
 
9903
- class ItemArray extends Widget {
10338
+ class ItemArray extends BaseComponent {
9904
10339
 
9905
10340
  constructor( name, values = [], callback, options = {} ) {
9906
10341
 
9907
10342
  options.nameWidth = "100%";
9908
10343
 
9909
- super( Widget.ARRAY, name, null, options );
10344
+ super( BaseComponent.ARRAY, name, null, options );
9910
10345
 
9911
10346
  this.onGetValue = () => {
9912
10347
  return values;
@@ -9961,41 +10396,41 @@ class ItemArray extends Widget {
9961
10396
  {
9962
10397
  const value = values[ i ];
9963
10398
  let baseclass = options.innerValues ? 'select' : value.constructor;
9964
- let widget = null;
10399
+ let component = null;
9965
10400
 
9966
10401
  switch( baseclass )
9967
10402
  {
9968
10403
  case String:
9969
- widget = new LX.TextInput(i + "", value, function(value, event) {
10404
+ component = new LX.TextInput(i + "", value, function(value, event) {
9970
10405
  values[ i ] = value;
9971
10406
  callback( values );
9972
10407
  }, { nameWidth: "12px", className: "p-0", skipReset: true });
9973
10408
  break;
9974
10409
  case Number:
9975
- widget = new NumberInput(i + "", value, function(value, event) {
10410
+ component = new NumberInput(i + "", value, function(value, event) {
9976
10411
  values[ i ] = value;
9977
10412
  callback( values );
9978
10413
  }, { nameWidth: "12px", className: "p-0", skipReset: true });
9979
10414
  break;
9980
10415
  case 'select':
9981
- widget = new Select(i + "", options.innerValues, value, function(value, event) {
10416
+ component = new Select(i + "", options.innerValues, value, function(value, event) {
9982
10417
  values[ i ] = value;
9983
10418
  callback( values );
9984
10419
  }, { nameWidth: "12px", className: "p-0", skipReset: true });
9985
10420
  break;
9986
10421
  }
9987
10422
 
9988
- console.assert( widget, `Value of type ${ baseclass } cannot be modified in ItemArray` );
10423
+ console.assert( component, `Value of type ${ baseclass } cannot be modified in ItemArray` );
9989
10424
 
9990
- arrayItems.appendChild( widget.root );
10425
+ arrayItems.appendChild( component.root );
9991
10426
 
9992
- const removeWidget = new LX.Button( null, "", ( v, event) => {
10427
+ const removeComponent = new LX.Button( null, "", ( v, event) => {
9993
10428
  values.splice( values.indexOf( value ), 1 );
9994
10429
  this._updateItems();
9995
10430
  this._trigger( new LX.IEvent(name, values, event), callback );
9996
10431
  }, { title: "Remove item", icon: "Trash3"} );
9997
10432
 
9998
- widget.root.appendChild( removeWidget.root );
10433
+ component.root.appendChild( removeComponent.root );
9999
10434
  }
10000
10435
 
10001
10436
  const addButton = new LX.Button(null, LX.makeIcon( "Plus", { svgClass: "sm" } ).innerHTML + "Add item", (v, event) => {
@@ -10015,14 +10450,14 @@ LX.ItemArray = ItemArray;
10015
10450
 
10016
10451
  /**
10017
10452
  * @class List
10018
- * @description List Widget
10453
+ * @description List Component
10019
10454
  */
10020
10455
 
10021
- class List extends Widget {
10456
+ class List extends BaseComponent {
10022
10457
 
10023
10458
  constructor( name, values, value, callback, options = {} ) {
10024
10459
 
10025
- super( Widget.LIST, name, value, options );
10460
+ super( BaseComponent.LIST, name, value, options );
10026
10461
 
10027
10462
  this.onGetValue = () => {
10028
10463
  return value;
@@ -10115,17 +10550,17 @@ LX.List = List;
10115
10550
 
10116
10551
  /**
10117
10552
  * @class Tags
10118
- * @description Tags Widget
10553
+ * @description Tags Component
10119
10554
  */
10120
10555
 
10121
- class Tags extends Widget {
10556
+ class Tags extends BaseComponent {
10122
10557
 
10123
10558
  constructor( name, value, callback, options = {} ) {
10124
10559
 
10125
10560
  value = value.replace( /\s/g, '' ).split( ',' );
10126
10561
 
10127
10562
  let defaultValue = [].concat( value );
10128
- super( Widget.TAGS, name, defaultValue, options );
10563
+ super( BaseComponent.TAGS, name, defaultValue, options );
10129
10564
 
10130
10565
  this.onGetValue = () => {
10131
10566
  return [].concat( value );
@@ -10204,19 +10639,19 @@ LX.Tags = Tags;
10204
10639
 
10205
10640
  /**
10206
10641
  * @class Checkbox
10207
- * @description Checkbox Widget
10642
+ * @description Checkbox Component
10208
10643
  */
10209
10644
 
10210
- class Checkbox extends Widget {
10645
+ class Checkbox extends BaseComponent {
10211
10646
 
10212
10647
  constructor( name, value, callback, options = {} ) {
10213
10648
 
10214
10649
  if( !name && !options.label )
10215
10650
  {
10216
- throw( "Set Widget Name or at least a label!" );
10651
+ throw( "Set Component Name or at least a label!" );
10217
10652
  }
10218
10653
 
10219
- super( Widget.CHECKBOX, name, value, options );
10654
+ super( BaseComponent.CHECKBOX, name, value, options );
10220
10655
 
10221
10656
  this.onGetValue = () => {
10222
10657
  return value;
@@ -10287,19 +10722,19 @@ LX.Checkbox = Checkbox;
10287
10722
 
10288
10723
  /**
10289
10724
  * @class Toggle
10290
- * @description Toggle Widget
10725
+ * @description Toggle Component
10291
10726
  */
10292
10727
 
10293
- class Toggle extends Widget {
10728
+ class Toggle extends BaseComponent {
10294
10729
 
10295
10730
  constructor( name, value, callback, options = {} ) {
10296
10731
 
10297
10732
  if( !name && !options.label )
10298
10733
  {
10299
- throw( "Set Widget Name or at least a label!" );
10734
+ throw( "Set Component Name or at least a label!" );
10300
10735
  }
10301
10736
 
10302
- super( Widget.TOGGLE, name, value, options );
10737
+ super( BaseComponent.TOGGLE, name, value, options );
10303
10738
 
10304
10739
  this.onGetValue = () => {
10305
10740
  return toggle.checked;
@@ -10371,14 +10806,14 @@ LX.Toggle = Toggle;
10371
10806
 
10372
10807
  /**
10373
10808
  * @class RadioGroup
10374
- * @description RadioGroup Widget
10809
+ * @description RadioGroup Component
10375
10810
  */
10376
10811
 
10377
- class RadioGroup extends Widget {
10812
+ class RadioGroup extends BaseComponent {
10378
10813
 
10379
10814
  constructor( name, label, values, callback, options = {} ) {
10380
10815
 
10381
- super( Widget.RADIO, name, null, options );
10816
+ super( BaseComponent.RADIO, name, null, options );
10382
10817
 
10383
10818
  let currentIndex = null;
10384
10819
 
@@ -10450,10 +10885,10 @@ LX.RadioGroup = RadioGroup;
10450
10885
 
10451
10886
  /**
10452
10887
  * @class ColorInput
10453
- * @description ColorInput Widget
10888
+ * @description ColorInput Component
10454
10889
  */
10455
10890
 
10456
- class ColorInput extends Widget {
10891
+ class ColorInput extends BaseComponent {
10457
10892
 
10458
10893
  constructor( name, value, callback, options = {} ) {
10459
10894
 
@@ -10462,12 +10897,12 @@ class ColorInput extends Widget {
10462
10897
  const useAlpha = options.useAlpha ??
10463
10898
  ( ( value.constructor === Object && 'a' in value ) || ( value.constructor === String && [ 5, 9 ].includes( value.length ) ) );
10464
10899
 
10465
- const widgetColor = new LX.Color( value );
10900
+ const componentColor = new LX.Color( value );
10466
10901
 
10467
10902
  // Force always hex internally
10468
- value = useAlpha ? widgetColor.hex : widgetColor.hex.substr( 0, 7 );
10903
+ value = useAlpha ? componentColor.hex : componentColor.hex.substr( 0, 7 );
10469
10904
 
10470
- super( Widget.COLOR, name, value, options );
10905
+ super( BaseComponent.COLOR, name, value, options );
10471
10906
 
10472
10907
  this.onGetValue = () => {
10473
10908
  const currentColor = new LX.Color( value );
@@ -10487,7 +10922,7 @@ class ColorInput extends Widget {
10487
10922
 
10488
10923
  if( !this._skipTextUpdate )
10489
10924
  {
10490
- textWidget.set( value, true, event );
10925
+ textComponent.set( value, true, event );
10491
10926
  }
10492
10927
 
10493
10928
  if( !skipCallback )
@@ -10555,15 +10990,15 @@ class ColorInput extends Widget {
10555
10990
  colorSampleRGB.style.width = "18px";
10556
10991
  }
10557
10992
 
10558
- const textWidget = new LX.TextInput( null, value, v => {
10993
+ const textComponent = new LX.TextInput( null, value, v => {
10559
10994
  this._skipTextUpdate = true;
10560
10995
  this.set( v );
10561
10996
  delete this._skipTextUpdate;
10562
10997
  this.picker.fromHexColor( v );
10563
10998
  }, { width: "calc( 100% - 24px )", disabled: options.disabled });
10564
10999
 
10565
- textWidget.root.style.marginLeft = "6px";
10566
- container.appendChild( textWidget.root );
11000
+ textComponent.root.style.marginLeft = "6px";
11001
+ container.appendChild( textComponent.root );
10567
11002
 
10568
11003
  LX.doAsync( this.onResize.bind( this ) );
10569
11004
  }
@@ -10573,14 +11008,14 @@ LX.ColorInput = ColorInput;
10573
11008
 
10574
11009
  /**
10575
11010
  * @class RangeInput
10576
- * @description RangeInput Widget
11011
+ * @description RangeInput Component
10577
11012
  */
10578
11013
 
10579
- class RangeInput extends Widget {
11014
+ class RangeInput extends BaseComponent {
10580
11015
 
10581
11016
  constructor( name, value, callback, options = {} ) {
10582
11017
 
10583
- super( Widget.RANGE, name, value, options );
11018
+ super( BaseComponent.RANGE, name, value, options );
10584
11019
 
10585
11020
  this.onGetValue = () => {
10586
11021
  return value;
@@ -10664,7 +11099,7 @@ class RangeInput extends Widget {
10664
11099
  slider.min = newMin ?? slider.min;
10665
11100
  slider.max = newMax ?? slider.max;
10666
11101
  slider.step = newStep ?? slider.step;
10667
- Widget._dispatchEvent( slider, "input", true );
11102
+ BaseComponent._dispatchEvent( slider, "input", true );
10668
11103
  };
10669
11104
 
10670
11105
  LX.doAsync( this.onResize.bind( this ) );
@@ -10675,14 +11110,14 @@ LX.RangeInput = RangeInput;
10675
11110
 
10676
11111
  /**
10677
11112
  * @class NumberInput
10678
- * @description NumberInput Widget
11113
+ * @description NumberInput Component
10679
11114
  */
10680
11115
 
10681
- class NumberInput extends Widget {
11116
+ class NumberInput extends BaseComponent {
10682
11117
 
10683
11118
  constructor( name, value, callback, options = {} ) {
10684
11119
 
10685
- super( Widget.NUMBER, name, value, options );
11120
+ super( BaseComponent.NUMBER, name, value, options );
10686
11121
 
10687
11122
  this.onGetValue = () => {
10688
11123
  return value;
@@ -10759,7 +11194,7 @@ class NumberInput extends Widget {
10759
11194
  // Add slider below
10760
11195
  if( !options.skipSlider && options.min !== undefined && options.max !== undefined )
10761
11196
  {
10762
- let sliderBox = LX.makeContainer( [ "100%", "auto" ], "", "", box );
11197
+ let sliderBox = LX.makeContainer( [ "100%", "auto" ], "z-1 input-box", "", box );
10763
11198
  let slider = document.createElement( 'input' );
10764
11199
  slider.className = "lexinputslider";
10765
11200
  slider.min = options.min;
@@ -10897,17 +11332,17 @@ LX.NumberInput = NumberInput;
10897
11332
 
10898
11333
  /**
10899
11334
  * @class Vector
10900
- * @description Vector Widget
11335
+ * @description Vector Component
10901
11336
  */
10902
11337
 
10903
- class Vector extends Widget {
11338
+ class Vector extends BaseComponent {
10904
11339
 
10905
11340
  constructor( numComponents, name, value, callback, options = {} ) {
10906
11341
 
10907
11342
  numComponents = LX.clamp( numComponents, 2, 4 );
10908
11343
  value = value ?? new Array( numComponents ).fill( 0 );
10909
11344
 
10910
- super( Widget.VECTOR, name, [].concat( value ), options );
11345
+ super( BaseComponent.VECTOR, name, [].concat( value ), options );
10911
11346
 
10912
11347
  this.onGetValue = () => {
10913
11348
  let inputs = this.root.querySelectorAll( "input[type='number']" );
@@ -11002,13 +11437,13 @@ class Vector extends Widget {
11002
11437
  for( let v of that.querySelectorAll(".vecinput") )
11003
11438
  {
11004
11439
  v.value = LX.round( +v.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ), options.precision );
11005
- Widget._dispatchEvent( v, "change" );
11440
+ BaseComponent._dispatchEvent( v, "change" );
11006
11441
  }
11007
11442
  }
11008
11443
  else
11009
11444
  {
11010
11445
  this.value = LX.round( +this.valueAsNumber - mult * ( e.deltaY > 0 ? 1 : -1 ), options.precision );
11011
- Widget._dispatchEvent( vecinput, "change" );
11446
+ BaseComponent._dispatchEvent( vecinput, "change" );
11012
11447
  }
11013
11448
  }, { passive: false } );
11014
11449
 
@@ -11082,13 +11517,13 @@ class Vector extends Widget {
11082
11517
  for( let v of that.root.querySelectorAll( ".vecinput" ) )
11083
11518
  {
11084
11519
  v.value = LX.round( +v.valueAsNumber + mult * dt, options.precision );
11085
- Widget._dispatchEvent( v, "change" );
11520
+ BaseComponent._dispatchEvent( v, "change" );
11086
11521
  }
11087
11522
  }
11088
11523
  else
11089
11524
  {
11090
11525
  vecinput.value = LX.round( +vecinput.valueAsNumber + mult * dt, options.precision );
11091
- Widget._dispatchEvent( vecinput, "change" );
11526
+ BaseComponent._dispatchEvent( vecinput, "change" );
11092
11527
  }
11093
11528
  }
11094
11529
 
@@ -11147,14 +11582,14 @@ LX.Vector = Vector;
11147
11582
 
11148
11583
  /**
11149
11584
  * @class SizeInput
11150
- * @description SizeInput Widget
11585
+ * @description SizeInput Component
11151
11586
  */
11152
11587
 
11153
- class SizeInput extends Widget {
11588
+ class SizeInput extends BaseComponent {
11154
11589
 
11155
11590
  constructor( name, value, callback, options = {} ) {
11156
11591
 
11157
- super( Widget.SIZE, name, value, options );
11592
+ super( BaseComponent.SIZE, name, value, options );
11158
11593
 
11159
11594
  this.onGetValue = () => {
11160
11595
  const value = [];
@@ -11235,10 +11670,10 @@ LX.SizeInput = SizeInput;
11235
11670
 
11236
11671
  /**
11237
11672
  * @class OTPInput
11238
- * @description OTPInput Widget
11673
+ * @description OTPInput Component
11239
11674
  */
11240
11675
 
11241
- class OTPInput extends Widget {
11676
+ class OTPInput extends BaseComponent {
11242
11677
 
11243
11678
  constructor( name, value, callback, options = {} ) {
11244
11679
 
@@ -11251,7 +11686,7 @@ class OTPInput extends Widget {
11251
11686
  value = "x".repeat( patternSize );
11252
11687
  }
11253
11688
 
11254
- super( Widget.OTP, name, value, options );
11689
+ super( BaseComponent.OTP, name, value, options );
11255
11690
 
11256
11691
  this.onGetValue = () => {
11257
11692
  return +value;
@@ -11393,14 +11828,14 @@ LX.OTPInput = OTPInput;
11393
11828
 
11394
11829
  /**
11395
11830
  * @class Pad
11396
- * @description Pad Widget
11831
+ * @description Pad Component
11397
11832
  */
11398
11833
 
11399
- class Pad extends Widget {
11834
+ class Pad extends BaseComponent {
11400
11835
 
11401
11836
  constructor( name, value, callback, options = {} ) {
11402
11837
 
11403
- super( Widget.PAD, name, null, options );
11838
+ super( BaseComponent.PAD, name, null, options );
11404
11839
 
11405
11840
  this.onGetValue = () => {
11406
11841
  return thumb.value.xy;
@@ -11513,14 +11948,14 @@ LX.Pad = Pad;
11513
11948
 
11514
11949
  /**
11515
11950
  * @class Progress
11516
- * @description Progress Widget
11951
+ * @description Progress Component
11517
11952
  */
11518
11953
 
11519
- class Progress extends Widget {
11954
+ class Progress extends BaseComponent {
11520
11955
 
11521
11956
  constructor( name, value, options = {} ) {
11522
11957
 
11523
- super( Widget.PROGRESS, name, value, options );
11958
+ super( BaseComponent.PROGRESS, name, value, options );
11524
11959
 
11525
11960
  this.onGetValue = () => {
11526
11961
  return progress.value;
@@ -11650,14 +12085,14 @@ LX.Progress = Progress;
11650
12085
 
11651
12086
  /**
11652
12087
  * @class FileInput
11653
- * @description FileInput Widget
12088
+ * @description FileInput Component
11654
12089
  */
11655
12090
 
11656
- class FileInput extends Widget {
12091
+ class FileInput extends BaseComponent {
11657
12092
 
11658
12093
  constructor( name, callback, options = { } ) {
11659
12094
 
11660
- super( Widget.FILE, name, null, options );
12095
+ super( BaseComponent.FILE, name, null, options );
11661
12096
 
11662
12097
  let local = options.local ?? true;
11663
12098
  let type = options.type ?? 'text';
@@ -11735,16 +12170,16 @@ LX.FileInput = FileInput;
11735
12170
 
11736
12171
  /**
11737
12172
  * @class Tree
11738
- * @description Tree Widget
12173
+ * @description Tree Component
11739
12174
  */
11740
12175
 
11741
- class Tree extends Widget {
12176
+ class Tree extends BaseComponent {
11742
12177
 
11743
12178
  constructor( name, data, options = {} ) {
11744
12179
 
11745
12180
  options.hideName = true;
11746
12181
 
11747
- super( Widget.TREE, name, null, options );
12182
+ super( BaseComponent.TREE, name, null, options );
11748
12183
 
11749
12184
  let container = document.createElement('div');
11750
12185
  container.className = "lextree";
@@ -11817,16 +12252,16 @@ LX.Tree = Tree;
11817
12252
 
11818
12253
  /**
11819
12254
  * @class TabSections
11820
- * @description TabSections Widget
12255
+ * @description TabSections Component
11821
12256
  */
11822
12257
 
11823
- class TabSections extends Widget {
12258
+ class TabSections extends BaseComponent {
11824
12259
 
11825
12260
  constructor( name, tabs, options = {} ) {
11826
12261
 
11827
12262
  options.hideName = true;
11828
12263
 
11829
- super( Widget.TABS, name, null, options );
12264
+ super( BaseComponent.TABS, name, null, options );
11830
12265
 
11831
12266
  if( tabs.constructor != Array )
11832
12267
  {
@@ -11872,7 +12307,7 @@ class TabSections extends Widget {
11872
12307
 
11873
12308
  let infoContainer = document.createElement( "div" );
11874
12309
  infoContainer.id = tab.name.replace( /\s/g, '' );
11875
- infoContainer.className = "widgets";
12310
+ infoContainer.className = "components";
11876
12311
  infoContainer.toggleAttribute( "hidden", !( tab.selected ?? false ) );
11877
12312
  container.appendChild( infoContainer );
11878
12313
 
@@ -11881,7 +12316,7 @@ class TabSections extends Widget {
11881
12316
  tabContainer.querySelectorAll( ".lextab" ).forEach( e => { e.classList.remove( "selected" ); } );
11882
12317
  tabEl.classList.add( "selected" );
11883
12318
  // Hide all tabs content
11884
- container.querySelectorAll(".widgets").forEach( e => { e.toggleAttribute( "hidden", true ); } );
12319
+ container.querySelectorAll(".components").forEach( e => { e.toggleAttribute( "hidden", true ); } );
11885
12320
  // Show tab content
11886
12321
  const el = container.querySelector( '#' + infoContainer.id );
11887
12322
  el.toggleAttribute( "hidden" );
@@ -11924,14 +12359,14 @@ LX.TabSections = TabSections;
11924
12359
 
11925
12360
  /**
11926
12361
  * @class Counter
11927
- * @description Counter Widget
12362
+ * @description Counter Component
11928
12363
  */
11929
12364
 
11930
- class Counter extends Widget {
12365
+ class Counter extends BaseComponent {
11931
12366
 
11932
12367
  constructor( name, value, callback, options = { } ) {
11933
12368
 
11934
- super( Widget.COUNTER, name, value, options );
12369
+ super( BaseComponent.COUNTER, name, value, options );
11935
12370
 
11936
12371
  this.onGetValue = () => {
11937
12372
  return counterText.count;
@@ -11994,10 +12429,10 @@ LX.Counter = Counter;
11994
12429
 
11995
12430
  /**
11996
12431
  * @class Table
11997
- * @description Table Widget
12432
+ * @description Table Component
11998
12433
  */
11999
12434
 
12000
- class Table extends Widget {
12435
+ class Table extends BaseComponent {
12001
12436
 
12002
12437
  constructor( name, data, options = { } ) {
12003
12438
 
@@ -12006,7 +12441,7 @@ class Table extends Widget {
12006
12441
  throw( "Data is needed to create a table!" );
12007
12442
  }
12008
12443
 
12009
- super( Widget.TABLE, name, null, options );
12444
+ super( BaseComponent.TABLE, name, null, options );
12010
12445
 
12011
12446
  this.onResize = ( rect ) => {
12012
12447
  const realNameWidth = ( this.root.domName?.style.width ?? "0px" );
@@ -12056,7 +12491,7 @@ class Table extends Widget {
12056
12491
  {
12057
12492
  const filterOptions = LX.deepCopy( options );
12058
12493
  filterOptions.placeholder = `Filter ${ this.filter }...`;
12059
- filterOptions.skipWidget = true;
12494
+ filterOptions.skipComponent = true;
12060
12495
  filterOptions.trigger = "input";
12061
12496
  filterOptions.inputClass = "outline";
12062
12497
 
@@ -12075,9 +12510,9 @@ class Table extends Widget {
12075
12510
 
12076
12511
  for( let f of this.customFilters )
12077
12512
  {
12078
- f.widget = new LX.Button(null, icon.innerHTML + f.name, ( v ) => {
12513
+ f.component = new LX.Button(null, icon.innerHTML + f.name, ( v ) => {
12079
12514
 
12080
- const spanName = f.widget.root.querySelector( "span" );
12515
+ const spanName = f.component.root.querySelector( "span" );
12081
12516
 
12082
12517
  if( f.options )
12083
12518
  {
@@ -12098,7 +12533,7 @@ class Table extends Widget {
12098
12533
  };
12099
12534
  return item;
12100
12535
  } );
12101
- new LX.DropdownMenu( f.widget.root, menuOptions, { side: "bottom", align: "start" });
12536
+ new LX.DropdownMenu( f.component.root, menuOptions, { side: "bottom", align: "start" });
12102
12537
  }
12103
12538
  else if( f.type == "range" )
12104
12539
  {
@@ -12117,12 +12552,14 @@ class Table extends Widget {
12117
12552
  f.start = v;
12118
12553
  const inUse = ( f.start != f.min || f.end != f.max );
12119
12554
  spanName.innerHTML = icon.innerHTML + f.name + ( inUse ? separatorHtml + LX.badge( `${ f.start } - ${ f.end } ${ f.units ?? "" }`, "bg-tertiary fg-secondary text-sm border-0" ) : "" );
12555
+ if( inUse ) this._resetCustomFiltersBtn.root.classList.remove( "hidden" );
12120
12556
  this.refresh();
12121
12557
  }, { skipSlider: true, min: f.min, max: f.max, step: f.step, units: f.units } );
12122
12558
  panel.addNumber( null, f.end, (v) => {
12123
12559
  f.end = v;
12124
12560
  const inUse = ( f.start != f.min || f.end != f.max );
12125
12561
  spanName.innerHTML = icon.innerHTML + f.name + ( inUse ? separatorHtml + LX.badge( `${ f.start } - ${ f.end } ${ f.units ?? "" }`, "bg-tertiary fg-secondary text-sm border-0" ) : "" );
12562
+ if( inUse ) this._resetCustomFiltersBtn.root.classList.remove( "hidden" );
12126
12563
  this.refresh();
12127
12564
  }, { skipSlider: true, min: f.min, max: f.max, step: f.step, units: f.units } );
12128
12565
  panel.addButton( null, "Reset", () => {
@@ -12135,11 +12572,43 @@ class Table extends Widget {
12135
12572
  };
12136
12573
  panel.refresh();
12137
12574
  container.appendChild( panel.root );
12138
- new LX.Popover( f.widget.root, [ container ], { side: "bottom" } );
12575
+ new LX.Popover( f.component.root, [ container ], { side: "bottom" } );
12576
+ }
12577
+ else if( f.type == "date" )
12578
+ {
12579
+ const container = LX.makeContainer( ["auto", "auto"], "text-md" );
12580
+ const panel = new LX.Panel();
12581
+ LX.makeContainer( ["100%", "auto"], "px-3 p-2 pb-0 text-md font-medium", f.name, container );
12582
+
12583
+ panel.refresh = () => {
12584
+ panel.clear();
12585
+
12586
+ // Generate default value once the filter is used
12587
+ if( !f.default )
12588
+ {
12589
+ const date = new Date();
12590
+ const todayStringDate = `${ date.getDate() }/${ date.getMonth() + 1 }/${ date.getFullYear() }`;
12591
+ f.default = [ todayStringDate, todayStringDate ];
12592
+ }
12593
+
12594
+ const calendar = new LX.CalendarRange( f.value, {
12595
+ onChange: ( dateRange ) => {
12596
+ f.value = dateRange;
12597
+ spanName.innerHTML = icon.innerHTML + f.name + ( separatorHtml + LX.badge( `${ calendar.getFullDate() }`, "bg-tertiary fg-secondary text-sm border-0" ) );
12598
+ this._resetCustomFiltersBtn.root.classList.remove( "hidden" );
12599
+ this.refresh();
12600
+ }
12601
+ });
12602
+
12603
+ panel.attach( calendar );
12604
+ };
12605
+ panel.refresh();
12606
+ container.appendChild( panel.root );
12607
+ new LX.Popover( f.component.root, [ container ], { side: "bottom" } );
12139
12608
  }
12140
12609
 
12141
12610
  }, { buttonClass: "px-2 primary dashed" } );
12142
- headerContainer.appendChild( f.widget.root );
12611
+ headerContainer.appendChild( f.component.root );
12143
12612
  }
12144
12613
 
12145
12614
  this._resetCustomFiltersBtn = new LX.Button(null, "resetButton", ( v ) => {
@@ -12147,12 +12616,16 @@ class Table extends Widget {
12147
12616
  this._resetCustomFiltersBtn.root.classList.add( "hidden" );
12148
12617
  for( let f of this.customFilters )
12149
12618
  {
12150
- f.widget.root.querySelector( "span" ).innerHTML = ( icon.innerHTML + f.name );
12619
+ f.component.root.querySelector( "span" ).innerHTML = ( icon.innerHTML + f.name );
12151
12620
  if( f.type == "range" )
12152
12621
  {
12153
12622
  f.start = f.min;
12154
12623
  f.end = f.max;
12155
12624
  }
12625
+ else if( f.type == "date" )
12626
+ {
12627
+ delete f.default;
12628
+ }
12156
12629
  }
12157
12630
  this.refresh();
12158
12631
  }, { title: "Reset filters", tooltip: true, icon: "X" } );
@@ -12434,7 +12907,30 @@ class Table extends Widget {
12434
12907
  }
12435
12908
  }
12436
12909
  else if( f.type == "date" )
12437
- ;
12910
+ {
12911
+ acfMap[ acfName ] = acfMap[ acfName ] ?? false;
12912
+
12913
+ const filterColIndex = data.head.indexOf( acfName );
12914
+ if( filterColIndex > -1 )
12915
+ {
12916
+ if( !f.default )
12917
+ {
12918
+ const date = new Date();
12919
+ const todayStringDate = `${ date.getDate() }/${ date.getMonth() + 1 }/${ date.getFullYear() }`;
12920
+ f.value = [ todayStringDate, todayStringDate ];
12921
+ acfMap[ acfName ] |= true;
12922
+ continue;
12923
+ }
12924
+
12925
+ f.value = f.value ?? f.default;
12926
+
12927
+ const dateString = bodyData[ filterColIndex ];
12928
+ const date = LX.dateFromDateString( dateString );
12929
+ const minDate = LX.dateFromDateString( f.value[ 0 ] );
12930
+ const maxDate = LX.dateFromDateString( f.value[ 1 ] );
12931
+ acfMap[ acfName ] |= ( date >= minDate ) && ( date <= maxDate );
12932
+ }
12933
+ }
12438
12934
  }
12439
12935
 
12440
12936
  const show = Object.values( acfMap ).reduce( ( e, acc ) => acc *= e, true );
@@ -12679,14 +13175,14 @@ LX.Table = Table;
12679
13175
 
12680
13176
  /**
12681
13177
  * @class DatePicker
12682
- * @description DatePicker Widget
13178
+ * @description DatePicker Component
12683
13179
  */
12684
13180
 
12685
- class DatePicker extends Widget {
13181
+ class DatePicker extends BaseComponent {
12686
13182
 
12687
13183
  constructor( name, dateValue, callback, options = { } ) {
12688
13184
 
12689
- super( Widget.DATE, name, null, options );
13185
+ super( BaseComponent.DATE, name, null, options );
12690
13186
 
12691
13187
  const dateAsRange = ( dateValue?.constructor === Array );
12692
13188
 
@@ -12759,10 +13255,6 @@ class DatePicker extends Widget {
12759
13255
  const calendarIcon = LX.makeIcon( "Calendar" );
12760
13256
  const calendarButton = new LX.Button( null, d0, () => {
12761
13257
  this._popover = new LX.Popover( calendarButton.root, [ this.calendar ] );
12762
- if( dateAsRange )
12763
- {
12764
- Object.assign( this._popover.root.style, { display: "flex", width: "auto" } );
12765
- }
12766
13258
  }, { buttonClass: `flex flex-row px-3 ${ emptyDate ? "" : "fg-tertiary" } justify-between` } );
12767
13259
  calendarButton.root.querySelector( "button" ).appendChild( calendarIcon );
12768
13260
  calendarButton.root.style.width = "100%";
@@ -12777,10 +13269,6 @@ class DatePicker extends Widget {
12777
13269
  const calendarIcon = LX.makeIcon( "Calendar" );
12778
13270
  const calendarButton = new LX.Button( null, d1, () => {
12779
13271
  this._popover = new LX.Popover( calendarButton.root, [ this.calendar ] );
12780
- if( dateAsRange )
12781
- {
12782
- Object.assign( this._popover.root.style, { display: "flex", width: "auto" } );
12783
- }
12784
13272
  }, { buttonClass: `flex flex-row px-3 ${ emptyDate ? "" : "fg-tertiary" } justify-between` } );
12785
13273
  calendarButton.root.querySelector( "button" ).appendChild( calendarIcon );
12786
13274
  calendarButton.root.style.width = "100%";
@@ -12805,14 +13293,14 @@ LX.DatePicker = DatePicker;
12805
13293
 
12806
13294
  /**
12807
13295
  * @class Map2D
12808
- * @description Map2D Widget
13296
+ * @description Map2D Component
12809
13297
  */
12810
13298
 
12811
- class Map2D extends Widget {
13299
+ class Map2D extends BaseComponent {
12812
13300
 
12813
13301
  constructor( name, points, callback, options = {} ) {
12814
13302
 
12815
- super( Widget.MAP2D, name, null, options );
13303
+ super( BaseComponent.MAP2D, name, null, options );
12816
13304
 
12817
13305
  this.onGetValue = () => {
12818
13306
  return this.map2d.weightsObj;
@@ -12850,6 +13338,127 @@ class Map2D extends Widget {
12850
13338
 
12851
13339
  LX.Map2D = Map2D;
12852
13340
 
13341
+ /**
13342
+ * @class Rate
13343
+ * @description Rate Component
13344
+ */
13345
+
13346
+ class Rate extends BaseComponent {
13347
+
13348
+ constructor( name, value, callback, options = {} ) {
13349
+
13350
+ const allowHalf = options.allowHalf ?? false;
13351
+
13352
+ if( !allowHalf)
13353
+ {
13354
+ value = Math.floor( value );
13355
+ }
13356
+
13357
+ super( BaseComponent.RATE, name, value, options );
13358
+
13359
+ this.onGetValue = () => {
13360
+ return value;
13361
+ };
13362
+
13363
+ this.onSetValue = ( newValue, skipCallback, event ) => {
13364
+
13365
+ value = newValue;
13366
+
13367
+ _updateStars( value );
13368
+
13369
+ if( !skipCallback )
13370
+ {
13371
+ this._trigger( new LX.IEvent( name, newValue, event ), callback );
13372
+ }
13373
+ };
13374
+
13375
+ this.onResize = ( rect ) => {
13376
+ const realNameWidth = ( this.root.domName?.style.width ?? "0px" );
13377
+ container.style.width = `calc( 100% - ${ realNameWidth })`;
13378
+ };
13379
+
13380
+ const container = document.createElement('div');
13381
+ container.className = "lexrate relative";
13382
+ this.root.appendChild( container );
13383
+
13384
+ const starsContainer = LX.makeContainer( ["fit-content", "auto"], "flex flex-row gap-1", "", container );
13385
+ const filledStarsContainer = LX.makeContainer( ["fit-content", "auto"], "absolute top-0 flex flex-row gap-1 pointer-events-none", "", container );
13386
+ const halfStarsContainer = LX.makeContainer( ["fit-content", "auto"], "absolute top-0 flex flex-row gap-1 pointer-events-none", "", container );
13387
+
13388
+ starsContainer.addEventListener("mousemove", e => {
13389
+ const star = e.target;
13390
+ const idx = star.dataset["idx"];
13391
+
13392
+ if( idx !== undefined )
13393
+ {
13394
+ const rect = star.getBoundingClientRect();
13395
+ const half = allowHalf && e.offsetX < ( rect.width * 0.5 );
13396
+ _updateStars( idx - ( half ? 0.5 : 0.0 ) );
13397
+ }
13398
+ }, false );
13399
+
13400
+ starsContainer.addEventListener("mouseleave", e => {
13401
+ _updateStars( value );
13402
+ }, false );
13403
+
13404
+ // Create all layers of stars
13405
+
13406
+ for( let i = 0; i < 5; ++i )
13407
+ {
13408
+ const starIcon = LX.makeIcon( "Star", { svgClass: `lg fill-current fg-secondary` } );
13409
+ starIcon.dataset["idx"] = ( i + 1 );
13410
+ starsContainer.appendChild( starIcon );
13411
+
13412
+ starIcon.addEventListener("click", e => {
13413
+ const rect = e.target.getBoundingClientRect();
13414
+ const half = allowHalf && e.offsetX < ( rect.width * 0.5 );
13415
+ this.set( parseFloat( e.target.dataset["idx"] ) - ( half ? 0.5 : 0.0 ) );
13416
+ }, false );
13417
+
13418
+ const filledStarIcon = LX.makeIcon( "Star", { svgClass: `lg fill-current metallicyellow` } );
13419
+ filledStarsContainer.appendChild( filledStarIcon );
13420
+
13421
+ const halfStarIcon = LX.makeIcon( "StarHalf", { svgClass: `lg fill-current metallicyellow` } );
13422
+ halfStarsContainer.appendChild( halfStarIcon );
13423
+ }
13424
+
13425
+ const _updateStars = ( v ) => {
13426
+
13427
+ for( let i = 0; i < 5; ++i )
13428
+ {
13429
+ const filled = ( v > ( i + 0.5 ) );
13430
+ const starIcon = filledStarsContainer.childNodes[ i ];
13431
+ const halfStarIcon = halfStarsContainer.childNodes[ i ];
13432
+ if( filled )
13433
+ {
13434
+ starIcon.style.opacity = 1;
13435
+ }
13436
+ else
13437
+ {
13438
+ starIcon.style.opacity = 0;
13439
+
13440
+ const halfFilled = allowHalf && ( v > i );
13441
+ if( halfFilled )
13442
+ {
13443
+ halfStarIcon.style.opacity = 1;
13444
+ }
13445
+ else
13446
+ {
13447
+ halfStarIcon.style.opacity = 0;
13448
+ }
13449
+
13450
+ }
13451
+ }
13452
+ };
13453
+
13454
+ _updateStars( value );
13455
+
13456
+ LX.doAsync( this.onResize.bind( this ) );
13457
+ }
13458
+ }
13459
+
13460
+ LX.Rate = Rate;
13461
+
12853
13462
  // panel.js @jxarco
12854
13463
 
12855
13464
  /**
@@ -12888,42 +13497,42 @@ class Panel {
12888
13497
 
12889
13498
  this.root = root;
12890
13499
  this.branches = [];
12891
- this.widgets = {};
13500
+ this.components = {};
12892
13501
 
12893
13502
  this._branchOpen = false;
12894
13503
  this._currentBranch = null;
12895
- this._queue = []; // Append widgets in other locations
12896
- this._inlineWidgetsLeft = -1;
12897
- this._inline_queued_container = null;
13504
+ this._queue = []; // Append components in other locations
13505
+ this._inlineComponentsLeft = -1;
13506
+ this._inlineQueuedContainer = null;
12898
13507
  }
12899
13508
 
12900
13509
  get( name ) {
12901
13510
 
12902
- return this.widgets[ name ];
13511
+ return this.components[ name ];
12903
13512
  }
12904
13513
 
12905
13514
  getValue( name ) {
12906
13515
 
12907
- let widget = this.widgets[ name ];
13516
+ let component = this.components[ name ];
12908
13517
 
12909
- if( !widget )
13518
+ if( !component )
12910
13519
  {
12911
- throw( "No widget called " + name );
13520
+ throw( "No component called " + name );
12912
13521
  }
12913
13522
 
12914
- return widget.value();
13523
+ return component.value();
12915
13524
  }
12916
13525
 
12917
13526
  setValue( name, value, skipCallback ) {
12918
13527
 
12919
- let widget = this.widgets[ name ];
13528
+ let component = this.components[ name ];
12920
13529
 
12921
- if( !widget )
13530
+ if( !component )
12922
13531
  {
12923
- throw( "No widget called " + name );
13532
+ throw( "No component called " + name );
12924
13533
  }
12925
13534
 
12926
- return widget.set( value, skipCallback );
13535
+ return component.set( value, skipCallback );
12927
13536
  }
12928
13537
 
12929
13538
  /**
@@ -12944,18 +13553,18 @@ class Panel {
12944
13553
 
12945
13554
  clear() {
12946
13555
 
12947
- this._branchOpen = false;
12948
13556
  this.branches = [];
13557
+ this._branchOpen = false;
12949
13558
  this._currentBranch = null;
12950
13559
 
12951
- for( let w in this.widgets )
13560
+ for( let w in this.components )
12952
13561
  {
12953
- if( this.widgets[ w ].options && this.widgets[ w ].options.signal )
13562
+ if( this.components[ w ].options && this.components[ w ].options.signal )
12954
13563
  {
12955
- const signal = this.widgets[ w ].options.signal;
13564
+ const signal = this.components[ w ].options.signal;
12956
13565
  for( let i = 0; i < LX.signals[signal].length; i++ )
12957
13566
  {
12958
- if( LX.signals[signal][ i ] == this.widgets[ w ] )
13567
+ if( LX.signals[signal][ i ] == this.components[ w ] )
12959
13568
  {
12960
13569
  LX.signals[signal] = [...LX.signals[signal].slice(0, i), ...LX.signals[signal].slice(i+1)];
12961
13570
  }
@@ -12967,11 +13576,11 @@ class Panel {
12967
13576
  {
12968
13577
  for( let w = 0; w < this.signals.length; w++ )
12969
13578
  {
12970
- let widget = Object.values(this.signals[ w ])[ 0 ];
12971
- let signal = widget.options.signal;
13579
+ let c = Object.values(this.signals[ w ])[ 0 ];
13580
+ let signal = c.options.signal;
12972
13581
  for( let i = 0; i < LX.signals[signal].length; i++ )
12973
13582
  {
12974
- if( LX.signals[signal][ i ] == widget )
13583
+ if( LX.signals[signal][ i ] == c )
12975
13584
  {
12976
13585
  LX.signals[signal] = [...LX.signals[signal].slice(0, i), ...LX.signals[signal].slice(i+1)];
12977
13586
  }
@@ -12979,46 +13588,46 @@ class Panel {
12979
13588
  }
12980
13589
  }
12981
13590
 
12982
- this.widgets = {};
13591
+ this.components = {};
12983
13592
  this.root.innerHTML = "";
12984
13593
  }
12985
13594
 
12986
13595
  /**
12987
13596
  * @method sameLine
12988
- * @param {Number} number Of widgets that will be placed in the same line
12989
- * @param {String} className Extra class to customize inline widgets parent container
12990
- * @description Next N widgets will be in the same line. If no number, it will inline all until calling nextLine()
13597
+ * @param {Number} numberOfComponents Of components that will be placed in the same line
13598
+ * @param {String} className Extra class to customize inline components parent container
13599
+ * @description Next N components will be in the same line. If no number, it will inline all until calling nextLine()
12991
13600
  */
12992
13601
 
12993
- sameLine( number, className ) {
13602
+ sameLine( numberOfComponents, className ) {
12994
13603
 
12995
- this._inline_queued_container = this.queuedContainer;
12996
- this._inlineWidgetsLeft = ( number ?? Infinity );
13604
+ this._inlineQueuedContainer = this.queuedContainer;
13605
+ this._inlineComponentsLeft = ( numberOfComponents ?? Infinity );
12997
13606
  this._inlineExtraClass = className ?? null;
12998
13607
  }
12999
13608
 
13000
13609
  /**
13001
13610
  * @method endLine
13002
- * @param {String} className Extra class to customize inline widgets parent container
13003
- * @description Stop inlining widgets. Use it only if the number of widgets to be inlined is NOT specified.
13611
+ * @param {String} className Extra class to customize inline components parent container
13612
+ * @description Stop inlining components. Use it only if the number of components to be inlined is NOT specified.
13004
13613
  */
13005
13614
 
13006
13615
  endLine( className ) {
13007
13616
 
13008
13617
  className = className ?? this._inlineExtraClass;
13009
13618
 
13010
- if( this._inlineWidgetsLeft == -1 )
13619
+ if( this._inlineComponentsLeft == -1 )
13011
13620
  {
13012
- console.warn("No pending widgets to be inlined!");
13621
+ console.warn("No pending components to be inlined!");
13013
13622
  return;
13014
13623
  }
13015
13624
 
13016
- this._inlineWidgetsLeft = -1;
13625
+ this._inlineComponentsLeft = -1;
13017
13626
 
13018
13627
  if( !this._inlineContainer )
13019
13628
  {
13020
13629
  this._inlineContainer = document.createElement('div');
13021
- this._inlineContainer.className = "lexinlinewidgets";
13630
+ this._inlineContainer.className = "lexinlinecomponents";
13022
13631
 
13023
13632
  if( className )
13024
13633
  {
@@ -13027,14 +13636,14 @@ class Panel {
13027
13636
  }
13028
13637
 
13029
13638
  // Push all elements single element or Array[element, container]
13030
- for( let item of this._inlineWidgets )
13639
+ for( let item of this._inlineComponents )
13031
13640
  {
13032
13641
  const isPair = ( item.constructor == Array );
13033
13642
 
13034
13643
  if( isPair )
13035
13644
  {
13036
13645
  // eg. an array, inline items appended later to
13037
- if( this._inline_queued_container )
13646
+ if( this._inlineQueuedContainer )
13038
13647
  {
13039
13648
  this._inlineContainer.appendChild( item[ 0 ] );
13040
13649
  }
@@ -13050,7 +13659,7 @@ class Panel {
13050
13659
  }
13051
13660
  }
13052
13661
 
13053
- if( !this._inline_queued_container )
13662
+ if( !this._inlineQueuedContainer )
13054
13663
  {
13055
13664
  if( this._currentBranch )
13056
13665
  {
@@ -13063,10 +13672,10 @@ class Panel {
13063
13672
  }
13064
13673
  else
13065
13674
  {
13066
- this._inline_queued_container.appendChild( this._inlineContainer );
13675
+ this._inlineQueuedContainer.appendChild( this._inlineContainer );
13067
13676
  }
13068
13677
 
13069
- delete this._inlineWidgets;
13678
+ delete this._inlineComponents;
13070
13679
  delete this._inlineContainer;
13071
13680
  delete this._inlineExtraClass;
13072
13681
  }
@@ -13079,7 +13688,7 @@ class Panel {
13079
13688
  * className: Add class to the branch
13080
13689
  * closed: Set branch collapsed/opened [false]
13081
13690
  * icon: Set branch icon (LX.ICONS)
13082
- * filter: Allow filter widgets in branch by name [false]
13691
+ * filter: Allow filter components in branch by name [false]
13083
13692
  */
13084
13693
 
13085
13694
  branch( name, options = {} ) {
@@ -13100,10 +13709,10 @@ class Panel {
13100
13709
  this.branches.push( branch );
13101
13710
  this.root.appendChild( branch.root );
13102
13711
 
13103
- // Add widget filter
13712
+ // Add component filter
13104
13713
  if( options.filter )
13105
13714
  {
13106
- this._addFilter( options.filter, { callback: this._searchWidgets.bind( this, branch.name ) } );
13715
+ this._addFilter( options.filter, { callback: this._searchComponents.bind( this, branch.name ) } );
13107
13716
  }
13108
13717
 
13109
13718
  return branch;
@@ -13119,27 +13728,27 @@ class Panel {
13119
13728
  }
13120
13729
 
13121
13730
  /*
13122
- Panel Widgets
13731
+ Panel Components
13123
13732
  */
13124
13733
 
13125
- _attachWidget( widget, options = {} ) {
13734
+ _attachComponent( component, options = {} ) {
13126
13735
 
13127
- if( widget.name != undefined )
13736
+ if( component.name != undefined )
13128
13737
  {
13129
- this.widgets[ widget.name ] = widget;
13738
+ this.components[ component.name ] = component;
13130
13739
  }
13131
13740
 
13132
- if( widget.options.signal && !widget.name )
13741
+ if( component.options.signal && !component.name )
13133
13742
  {
13134
13743
  if( !this.signals )
13135
13744
  {
13136
13745
  this.signals = [];
13137
13746
  }
13138
13747
 
13139
- this.signals.push( { [ widget.options.signal ]: widget } );
13748
+ this.signals.push( { [ component.options.signal ]: component } );
13140
13749
  }
13141
13750
 
13142
- const _insertWidget = el => {
13751
+ const _insertComponent = el => {
13143
13752
  if( options.container )
13144
13753
  {
13145
13754
  options.container.appendChild( el );
@@ -13148,9 +13757,9 @@ class Panel {
13148
13757
  {
13149
13758
  if( this._currentBranch )
13150
13759
  {
13151
- if( !options.skipWidget )
13760
+ if( !options.skipComponent )
13152
13761
  {
13153
- this._currentBranch.widgets.push( widget );
13762
+ this._currentBranch.components.push( component );
13154
13763
  }
13155
13764
  this._currentBranch.content.appendChild( el );
13156
13765
  }
@@ -13167,54 +13776,54 @@ class Panel {
13167
13776
  }
13168
13777
  };
13169
13778
 
13170
- const _storeWidget = el => {
13779
+ const _storeComponent = el => {
13171
13780
 
13172
13781
  if( !this.queuedContainer )
13173
13782
  {
13174
- this._inlineWidgets.push( el );
13783
+ this._inlineComponents.push( el );
13175
13784
  }
13176
13785
  // Append content to queued tab container
13177
13786
  else
13178
13787
  {
13179
- this._inlineWidgets.push( [ el, this.queuedContainer ] );
13788
+ this._inlineComponents.push( [ el, this.queuedContainer ] );
13180
13789
  }
13181
13790
  };
13182
13791
 
13183
- // Process inline widgets
13184
- if( this._inlineWidgetsLeft > 0 && !options.skipInlineCount )
13792
+ // Process inline components
13793
+ if( this._inlineComponentsLeft > 0 && !options.skipInlineCount )
13185
13794
  {
13186
- if( !this._inlineWidgets )
13795
+ if( !this._inlineComponents )
13187
13796
  {
13188
- this._inlineWidgets = [];
13797
+ this._inlineComponents = [];
13189
13798
  }
13190
13799
 
13191
- // Store widget and its container
13192
- _storeWidget( widget.root );
13800
+ // Store component and its container
13801
+ _storeComponent( component.root );
13193
13802
 
13194
- this._inlineWidgetsLeft--;
13803
+ this._inlineComponentsLeft--;
13195
13804
 
13196
- // Last widget
13197
- if( !this._inlineWidgetsLeft )
13805
+ // Last component
13806
+ if( !this._inlineComponentsLeft )
13198
13807
  {
13199
13808
  this.endLine();
13200
13809
  }
13201
13810
  }
13202
13811
  else
13203
13812
  {
13204
- _insertWidget( widget.root );
13813
+ _insertComponent( component.root );
13205
13814
  }
13206
13815
 
13207
- return widget;
13816
+ return component;
13208
13817
  }
13209
13818
 
13210
13819
  _addFilter( placeholder, options = {} ) {
13211
13820
 
13212
13821
  options.placeholder = placeholder.constructor == String ? placeholder : "Filter properties..";
13213
- options.skipWidget = options.skipWidget ?? true;
13822
+ options.skipComponent = options.skipComponent ?? true;
13214
13823
  options.skipInlineCount = true;
13215
13824
 
13216
- let widget = new LX.TextInput( null, null, null, options );
13217
- const element = widget.root;
13825
+ let component = new LX.TextInput( null, null, null, options );
13826
+ const element = component.root;
13218
13827
  element.className += " lexfilter";
13219
13828
 
13220
13829
  let input = document.createElement('input');
@@ -13237,7 +13846,7 @@ class Panel {
13237
13846
  return element;
13238
13847
  }
13239
13848
 
13240
- _searchWidgets( branchName, value ) {
13849
+ _searchComponents( branchName, value ) {
13241
13850
 
13242
13851
  for( let b of this.branches )
13243
13852
  {
@@ -13246,8 +13855,8 @@ class Panel {
13246
13855
  continue;
13247
13856
  }
13248
13857
 
13249
- // remove all widgets
13250
- for( let w of b.widgets )
13858
+ // remove all components
13859
+ for( let w of b.components )
13251
13860
  {
13252
13861
  if( w.domEl.classList.contains('lexfilter') )
13253
13862
  {
@@ -13261,8 +13870,8 @@ class Panel {
13261
13870
 
13262
13871
  const emptyFilter = !value.length;
13263
13872
 
13264
- // add widgets
13265
- for( let w of b.widgets )
13873
+ // add components
13874
+ for( let w of b.components )
13266
13875
  {
13267
13876
  if( !emptyFilter )
13268
13877
  {
@@ -13272,7 +13881,7 @@ class Panel {
13272
13881
  if(!name.includes(value)) continue;
13273
13882
  }
13274
13883
 
13275
- // insert filtered widget
13884
+ // insert filtered component
13276
13885
  this.queuedContainer.appendChild( w.domEl );
13277
13886
  }
13278
13887
 
@@ -13343,13 +13952,13 @@ class Panel {
13343
13952
  var element = document.createElement('div');
13344
13953
  element.className = "lexseparator";
13345
13954
 
13346
- let widget = new LX.Widget( LX.Widget.SEPARATOR );
13347
- widget.root = element;
13955
+ let component = new LX.BaseComponent( LX.BaseComponent.SEPARATOR );
13956
+ component.root = element;
13348
13957
 
13349
13958
  if( this._currentBranch )
13350
13959
  {
13351
13960
  this._currentBranch.content.appendChild( element );
13352
- this._currentBranch.widgets.push( widget );
13961
+ this._currentBranch.components.push( component );
13353
13962
  }
13354
13963
  else
13355
13964
  {
@@ -13364,8 +13973,8 @@ class Panel {
13364
13973
  */
13365
13974
 
13366
13975
  addBlank( width, height ) {
13367
- const widget = new LX.Blank( width, height );
13368
- return this._attachWidget( widget );
13976
+ const component = new LX.Blank( width, height );
13977
+ return this._attachComponent( component );
13369
13978
  }
13370
13979
 
13371
13980
  /**
@@ -13380,18 +13989,18 @@ class Panel {
13380
13989
  */
13381
13990
 
13382
13991
  addTitle( name, options = {} ) {
13383
- const widget = new LX.Title( name, options );
13384
- return this._attachWidget( widget );
13992
+ const component = new LX.Title( name, options );
13993
+ return this._attachComponent( component );
13385
13994
  }
13386
13995
 
13387
13996
  /**
13388
13997
  * @method addText
13389
- * @param {String} name Widget name
13998
+ * @param {String} name Component name
13390
13999
  * @param {String} value Text value
13391
14000
  * @param {Function} callback Callback function on change
13392
14001
  * @param {Object} options:
13393
14002
  * hideName: Don't use name as label [false]
13394
- * disabled: Make the widget disabled [false]
14003
+ * disabled: Make the component disabled [false]
13395
14004
  * required: Make the input required
13396
14005
  * placeholder: Add input placeholder
13397
14006
  * icon: Icon (if any) to append at the input start
@@ -13406,18 +14015,18 @@ class Panel {
13406
14015
  */
13407
14016
 
13408
14017
  addText( name, value, callback, options = {} ) {
13409
- const widget = new LX.TextInput( name, value, callback, options );
13410
- return this._attachWidget( widget );
14018
+ const component = new LX.TextInput( name, value, callback, options );
14019
+ return this._attachComponent( component );
13411
14020
  }
13412
14021
 
13413
14022
  /**
13414
14023
  * @method addTextArea
13415
- * @param {String} name Widget name
14024
+ * @param {String} name Component name
13416
14025
  * @param {String} value Text Area value
13417
14026
  * @param {Function} callback Callback function on change
13418
14027
  * @param {Object} options:
13419
14028
  * hideName: Don't use name as label [false]
13420
- * disabled: Make the widget disabled [false]
14029
+ * disabled: Make the component disabled [false]
13421
14030
  * placeholder: Add input placeholder
13422
14031
  * resize: Allow resize [true]
13423
14032
  * trigger: Choose onchange trigger (default, input) [default]
@@ -13428,8 +14037,8 @@ class Panel {
13428
14037
  */
13429
14038
 
13430
14039
  addTextArea( name, value, callback, options = {} ) {
13431
- const widget = new LX.TextArea( name, value, callback, options );
13432
- return this._attachWidget( widget );
14040
+ const component = new LX.TextArea( name, value, callback, options );
14041
+ return this._attachComponent( component );
13433
14042
  }
13434
14043
 
13435
14044
  /**
@@ -13441,19 +14050,19 @@ class Panel {
13441
14050
  addLabel( value, options = {} ) {
13442
14051
  options.disabled = true;
13443
14052
  options.inputClass = ( options.inputClass ?? "" ) + " nobg";
13444
- const widget = this.addText( null, value, null, options );
13445
- widget.type = LX.Widget.LABEL;
13446
- return widget;
14053
+ const component = this.addText( null, value, null, options );
14054
+ component.type = LX.BaseComponent.LABEL;
14055
+ return component;
13447
14056
  }
13448
14057
 
13449
14058
  /**
13450
14059
  * @method addButton
13451
- * @param {String} name Widget name
14060
+ * @param {String} name Component name
13452
14061
  * @param {String} value Button name
13453
14062
  * @param {Function} callback Callback function on click
13454
14063
  * @param {Object} options:
13455
14064
  * hideName: Don't use name as label [false]
13456
- * disabled: Make the widget disabled [false]
14065
+ * disabled: Make the component disabled [false]
13457
14066
  * icon: Icon class to show as button value
13458
14067
  * iconPosition: Icon position (cover|start|end)
13459
14068
  * fileInput: Button click requests a file
@@ -13465,13 +14074,13 @@ class Panel {
13465
14074
  */
13466
14075
 
13467
14076
  addButton( name, value, callback, options = {} ) {
13468
- const widget = new LX.Button( name, value, callback, options );
13469
- return this._attachWidget( widget );
14077
+ const component = new LX.Button( name, value, callback, options );
14078
+ return this._attachComponent( component );
13470
14079
  }
13471
14080
 
13472
14081
  /**
13473
14082
  * @method addComboButtons
13474
- * @param {String} name Widget name
14083
+ * @param {String} name Component name
13475
14084
  * @param {Array} values Each of the {value, callback, selected, disabled} items
13476
14085
  * @param {Object} options:
13477
14086
  * hideName: Don't use name as label [false]
@@ -13482,8 +14091,8 @@ class Panel {
13482
14091
  */
13483
14092
 
13484
14093
  addComboButtons( name, values, options = {} ) {
13485
- const widget = new LX.ComboButtons( name, values, options );
13486
- return this._attachWidget( widget );
14094
+ const component = new LX.ComboButtons( name, values, options );
14095
+ return this._attachComponent( component );
13487
14096
  }
13488
14097
 
13489
14098
  /**
@@ -13498,13 +14107,13 @@ class Panel {
13498
14107
  */
13499
14108
 
13500
14109
  addCard( name, options = {} ) {
13501
- const widget = new LX.Card( name, options );
13502
- return this._attachWidget( widget );
14110
+ const component = new LX.Card( name, options );
14111
+ return this._attachComponent( component );
13503
14112
  }
13504
14113
 
13505
14114
  /**
13506
14115
  * @method addForm
13507
- * @param {String} name Widget name
14116
+ * @param {String} name Component name
13508
14117
  * @param {Object} data Form data
13509
14118
  * @param {Function} callback Callback function on submit form
13510
14119
  * @param {Object} options:
@@ -13517,13 +14126,13 @@ class Panel {
13517
14126
  */
13518
14127
 
13519
14128
  addForm( name, data, callback, options = {} ) {
13520
- const widget = new LX.Form( name, data, callback, options );
13521
- return this._attachWidget( widget );
14129
+ const component = new LX.Form( name, data, callback, options );
14130
+ return this._attachComponent( component );
13522
14131
  }
13523
14132
 
13524
14133
  /**
13525
14134
  * @method addContent
13526
- * @param {String} name Widget name
14135
+ * @param {String} name Component name
13527
14136
  * @param {HTMLElement/String} element
13528
14137
  * @param {Object} options
13529
14138
  */
@@ -13549,15 +14158,15 @@ class Panel {
13549
14158
 
13550
14159
  options.hideName = true;
13551
14160
 
13552
- let widget = new LX.Widget( LX.Widget.CONTENT, name, null, options );
13553
- widget.root.appendChild( element );
14161
+ let component = new LX.BaseComponent( LX.BaseComponent.CONTENT, name, null, options );
14162
+ component.root.appendChild( element );
13554
14163
 
13555
- return this._attachWidget( widget );
14164
+ return this._attachComponent( component );
13556
14165
  }
13557
14166
 
13558
14167
  /**
13559
14168
  * @method addImage
13560
- * @param {String} name Widget name
14169
+ * @param {String} name Component name
13561
14170
  * @param {String} url Image Url
13562
14171
  * @param {Object} options
13563
14172
  * hideName: Don't use name as label [false]
@@ -13576,43 +14185,43 @@ class Panel {
13576
14185
  Object.assign( img.style, options.style ?? {} );
13577
14186
  container.appendChild( img );
13578
14187
 
13579
- let widget = new LX.Widget( LX.Widget.IMAGE, name, null, options );
13580
- widget.root.appendChild( container );
14188
+ let component = new LX.BaseComponent( LX.BaseComponent.IMAGE, name, null, options );
14189
+ component.root.appendChild( container );
13581
14190
 
13582
14191
  // await img.decode();
13583
14192
  img.decode();
13584
14193
 
13585
- return this._attachWidget( widget );
14194
+ return this._attachComponent( component );
13586
14195
  }
13587
14196
 
13588
14197
  /**
13589
14198
  * @method addSelect
13590
- * @param {String} name Widget name
13591
- * @param {Array} values Posible options of the select widget -> String (for default select) or Object = {value, url} (for images, gifs..)
14199
+ * @param {String} name Component name
14200
+ * @param {Array} values Posible options of the select component -> String (for default select) or Object = {value, url} (for images, gifs..)
13592
14201
  * @param {String} value Select by default option
13593
14202
  * @param {Function} callback Callback function on change
13594
14203
  * @param {Object} options:
13595
14204
  * hideName: Don't use name as label [false]
13596
- * filter: Add a search bar to the widget [false]
13597
- * disabled: Make the widget disabled [false]
14205
+ * filter: Add a search bar to the component [false]
14206
+ * disabled: Make the component disabled [false]
13598
14207
  * skipReset: Don't add the reset value button when value changes
13599
14208
  * placeholder: Placeholder for the filter input
13600
14209
  * emptyMsg: Custom message to show when no filtered results
13601
14210
  */
13602
14211
 
13603
14212
  addSelect( name, values, value, callback, options = {} ) {
13604
- const widget = new LX.Select( name, values, value, callback, options );
13605
- return this._attachWidget( widget );
14213
+ const component = new LX.Select( name, values, value, callback, options );
14214
+ return this._attachComponent( component );
13606
14215
  }
13607
14216
 
13608
14217
  /**
13609
14218
  * @method addCurve
13610
- * @param {String} name Widget name
14219
+ * @param {String} name Component name
13611
14220
  * @param {Array of Array} values Array of 2N Arrays of each value of the curve
13612
14221
  * @param {Function} callback Callback function on change
13613
14222
  * @param {Object} options:
13614
14223
  * skipReset: Don't add the reset value button when value changes
13615
- * bgColor: Widget background color
14224
+ * bgColor: Component background color
13616
14225
  * pointsColor: Curve points color
13617
14226
  * lineColor: Curve line color
13618
14227
  * noOverlap: Points do not overlap, replacing themselves if necessary
@@ -13622,18 +14231,18 @@ class Panel {
13622
14231
  */
13623
14232
 
13624
14233
  addCurve( name, values, callback, options = {} ) {
13625
- const widget = new LX.Curve( name, values, callback, options );
13626
- return this._attachWidget( widget );
14234
+ const component = new LX.Curve( name, values, callback, options );
14235
+ return this._attachComponent( component );
13627
14236
  }
13628
14237
 
13629
14238
  /**
13630
14239
  * @method addDial
13631
- * @param {String} name Widget name
14240
+ * @param {String} name Component name
13632
14241
  * @param {Array of Array} values Array of 2N Arrays of each value of the dial
13633
14242
  * @param {Function} callback Callback function on change
13634
14243
  * @param {Object} options:
13635
14244
  * skipReset: Don't add the reset value button when value changes
13636
- * bgColor: Widget background color
14245
+ * bgColor: Component background color
13637
14246
  * pointsColor: Curve points color
13638
14247
  * lineColor: Curve line color
13639
14248
  * noOverlap: Points do not overlap, replacing themselves if necessary
@@ -13643,26 +14252,26 @@ class Panel {
13643
14252
  */
13644
14253
 
13645
14254
  addDial( name, values, callback, options = {} ) {
13646
- const widget = new LX.Dial( name, values, callback, options );
13647
- return this._attachWidget( widget );
14255
+ const component = new LX.Dial( name, values, callback, options );
14256
+ return this._attachComponent( component );
13648
14257
  }
13649
14258
 
13650
14259
  /**
13651
14260
  * @method addLayers
13652
- * @param {String} name Widget name
14261
+ * @param {String} name Component name
13653
14262
  * @param {Number} value Flag value by default option
13654
14263
  * @param {Function} callback Callback function on change
13655
14264
  * @param {Object} options:
13656
14265
  */
13657
14266
 
13658
14267
  addLayers( name, value, callback, options = {} ) {
13659
- const widget = new LX.Layers( name, value, callback, options );
13660
- return this._attachWidget( widget );
14268
+ const component = new LX.Layers( name, value, callback, options );
14269
+ return this._attachComponent( component );
13661
14270
  }
13662
14271
 
13663
14272
  /**
13664
14273
  * @method addArray
13665
- * @param {String} name Widget name
14274
+ * @param {String} name Component name
13666
14275
  * @param {Array} values By default values in the array
13667
14276
  * @param {Function} callback Callback function on change
13668
14277
  * @param {Object} options:
@@ -13670,13 +14279,13 @@ class Panel {
13670
14279
  */
13671
14280
 
13672
14281
  addArray( name, values = [], callback, options = {} ) {
13673
- const widget = new LX.ItemArray( name, values, callback, options );
13674
- return this._attachWidget( widget );
14282
+ const component = new LX.ItemArray( name, values, callback, options );
14283
+ return this._attachComponent( component );
13675
14284
  }
13676
14285
 
13677
14286
  /**
13678
14287
  * @method addList
13679
- * @param {String} name Widget name
14288
+ * @param {String} name Component name
13680
14289
  * @param {Array} values List values
13681
14290
  * @param {String} value Selected list value
13682
14291
  * @param {Function} callback Callback function on change
@@ -13685,13 +14294,13 @@ class Panel {
13685
14294
  */
13686
14295
 
13687
14296
  addList( name, values, value, callback, options = {} ) {
13688
- const widget = new LX.List( name, values, value, callback, options );
13689
- return this._attachWidget( widget );
14297
+ const component = new LX.List( name, values, value, callback, options );
14298
+ return this._attachComponent( component );
13690
14299
  }
13691
14300
 
13692
14301
  /**
13693
14302
  * @method addTags
13694
- * @param {String} name Widget name
14303
+ * @param {String} name Component name
13695
14304
  * @param {String} value Comma separated tags
13696
14305
  * @param {Function} callback Callback function on change
13697
14306
  * @param {Object} options:
@@ -13699,85 +14308,85 @@ class Panel {
13699
14308
  */
13700
14309
 
13701
14310
  addTags( name, value, callback, options = {} ) {
13702
- const widget = new LX.Tags( name, value, callback, options );
13703
- return this._attachWidget( widget );
14311
+ const component = new LX.Tags( name, value, callback, options );
14312
+ return this._attachComponent( component );
13704
14313
  }
13705
14314
 
13706
14315
  /**
13707
14316
  * @method addCheckbox
13708
- * @param {String} name Widget name
14317
+ * @param {String} name Component name
13709
14318
  * @param {Boolean} value Value of the checkbox
13710
14319
  * @param {Function} callback Callback function on change
13711
14320
  * @param {Object} options:
13712
- * disabled: Make the widget disabled [false]
14321
+ * disabled: Make the component disabled [false]
13713
14322
  * label: Checkbox label
13714
- * suboptions: Callback to add widgets in case of TRUE value
14323
+ * suboptions: Callback to add components in case of TRUE value
13715
14324
  * className: Extra classes to customize style
13716
14325
  */
13717
14326
 
13718
14327
  addCheckbox( name, value, callback, options = {} ) {
13719
- const widget = new LX.Checkbox( name, value, callback, options );
13720
- return this._attachWidget( widget );
14328
+ const component = new LX.Checkbox( name, value, callback, options );
14329
+ return this._attachComponent( component );
13721
14330
  }
13722
14331
 
13723
14332
  /**
13724
14333
  * @method addToggle
13725
- * @param {String} name Widget name
14334
+ * @param {String} name Component name
13726
14335
  * @param {Boolean} value Value of the checkbox
13727
14336
  * @param {Function} callback Callback function on change
13728
14337
  * @param {Object} options:
13729
- * disabled: Make the widget disabled [false]
14338
+ * disabled: Make the component disabled [false]
13730
14339
  * label: Toggle label
13731
- * suboptions: Callback to add widgets in case of TRUE value
14340
+ * suboptions: Callback to add components in case of TRUE value
13732
14341
  * className: Customize colors
13733
14342
  */
13734
14343
 
13735
14344
  addToggle( name, value, callback, options = {} ) {
13736
- const widget = new LX.Toggle( name, value, callback, options );
13737
- return this._attachWidget( widget );
14345
+ const component = new LX.Toggle( name, value, callback, options );
14346
+ return this._attachComponent( component );
13738
14347
  }
13739
14348
 
13740
14349
  /**
13741
14350
  * @method addRadioGroup
13742
- * @param {String} name Widget name
14351
+ * @param {String} name Component name
13743
14352
  * @param {String} label Radio label
13744
14353
  * @param {Array} values Radio options
13745
14354
  * @param {Function} callback Callback function on change
13746
14355
  * @param {Object} options:
13747
- * disabled: Make the widget disabled [false]
14356
+ * disabled: Make the component disabled [false]
13748
14357
  * className: Customize colors
13749
14358
  * selected: Index of the default selected option
13750
14359
  */
13751
14360
 
13752
14361
  addRadioGroup( name, label, values, callback, options = {} ) {
13753
- const widget = new LX.RadioGroup( name, label, values, callback, options );
13754
- return this._attachWidget( widget );
14362
+ const component = new LX.RadioGroup( name, label, values, callback, options );
14363
+ return this._attachComponent( component );
13755
14364
  }
13756
14365
 
13757
14366
  /**
13758
14367
  * @method addColor
13759
- * @param {String} name Widget name
14368
+ * @param {String} name Component name
13760
14369
  * @param {String} value Default color (hex)
13761
14370
  * @param {Function} callback Callback function on change
13762
14371
  * @param {Object} options:
13763
- * disabled: Make the widget disabled [false]
14372
+ * disabled: Make the component disabled [false]
13764
14373
  * useRGB: The callback returns color as Array (r, g, b) and not hex [false]
13765
14374
  */
13766
14375
 
13767
14376
  addColor( name, value, callback, options = {} ) {
13768
- const widget = new LX.ColorInput( name, value, callback, options );
13769
- return this._attachWidget( widget );
14377
+ const component = new LX.ColorInput( name, value, callback, options );
14378
+ return this._attachComponent( component );
13770
14379
  }
13771
14380
 
13772
14381
  /**
13773
14382
  * @method addRange
13774
- * @param {String} name Widget name
14383
+ * @param {String} name Component name
13775
14384
  * @param {Number} value Default number value
13776
14385
  * @param {Function} callback Callback function on change
13777
14386
  * @param {Object} options:
13778
14387
  * hideName: Don't use name as label [false]
13779
14388
  * className: Extra classes to customize style
13780
- * disabled: Make the widget disabled [false]
14389
+ * disabled: Make the component disabled [false]
13781
14390
  * left: The slider goes to the left instead of the right
13782
14391
  * fill: Fill slider progress [true]
13783
14392
  * step: Step of the input
@@ -13785,18 +14394,18 @@ class Panel {
13785
14394
  */
13786
14395
 
13787
14396
  addRange( name, value, callback, options = {} ) {
13788
- const widget = new LX.RangeInput( name, value, callback, options );
13789
- return this._attachWidget( widget );
14397
+ const component = new LX.RangeInput( name, value, callback, options );
14398
+ return this._attachComponent( component );
13790
14399
  }
13791
14400
 
13792
14401
  /**
13793
14402
  * @method addNumber
13794
- * @param {String} name Widget name
14403
+ * @param {String} name Component name
13795
14404
  * @param {Number} value Default number value
13796
14405
  * @param {Function} callback Callback function on change
13797
14406
  * @param {Object} options:
13798
14407
  * hideName: Don't use name as label [false]
13799
- * disabled: Make the widget disabled [false]
14408
+ * disabled: Make the component disabled [false]
13800
14409
  * step: Step of the input
13801
14410
  * precision: The number of digits to appear after the decimal point
13802
14411
  * min, max: Min and Max values for the input
@@ -13807,24 +14416,24 @@ class Panel {
13807
14416
  */
13808
14417
 
13809
14418
  addNumber( name, value, callback, options = {} ) {
13810
- const widget = new LX.NumberInput( name, value, callback, options );
13811
- return this._attachWidget( widget );
14419
+ const component = new LX.NumberInput( name, value, callback, options );
14420
+ return this._attachComponent( component );
13812
14421
  }
13813
14422
 
13814
14423
  static VECTOR_COMPONENTS = { 0: 'x', 1: 'y', 2: 'z', 3: 'w' };
13815
14424
 
13816
14425
  _addVector( numComponents, name, value, callback, options = {} ) {
13817
- const widget = new LX.Vector( numComponents, name, value, callback, options );
13818
- return this._attachWidget( widget );
14426
+ const component = new LX.Vector( numComponents, name, value, callback, options );
14427
+ return this._attachComponent( component );
13819
14428
  }
13820
14429
 
13821
14430
  /**
13822
14431
  * @method addVector N (2, 3, 4)
13823
- * @param {String} name Widget name
14432
+ * @param {String} name Component name
13824
14433
  * @param {Array} value Array of N components
13825
14434
  * @param {Function} callback Callback function on change
13826
14435
  * @param {Object} options:
13827
- * disabled: Make the widget disabled [false]
14436
+ * disabled: Make the component disabled [false]
13828
14437
  * step: Step of the inputs
13829
14438
  * min, max: Min and Max values for the inputs
13830
14439
  * onPress: Callback function on mouse down
@@ -13845,43 +14454,43 @@ class Panel {
13845
14454
 
13846
14455
  /**
13847
14456
  * @method addSize
13848
- * @param {String} name Widget name
14457
+ * @param {String} name Component name
13849
14458
  * @param {Number} value Default number value
13850
14459
  * @param {Function} callback Callback function on change
13851
14460
  * @param {Object} options:
13852
14461
  * hideName: Don't use name as label [false]
13853
- * disabled: Make the widget disabled [false]
14462
+ * disabled: Make the component disabled [false]
13854
14463
  * units: Unit as string added to the end of the value
13855
14464
  */
13856
14465
 
13857
14466
  addSize( name, value, callback, options = {} ) {
13858
- const widget = new LX.SizeInput( name, value, callback, options );
13859
- return this._attachWidget( widget );
14467
+ const component = new LX.SizeInput( name, value, callback, options );
14468
+ return this._attachComponent( component );
13860
14469
  }
13861
14470
 
13862
14471
  /**
13863
14472
  * @method addOTP
13864
- * @param {String} name Widget name
14473
+ * @param {String} name Component name
13865
14474
  * @param {String} value Default numeric value in string format
13866
14475
  * @param {Function} callback Callback function on change
13867
14476
  * @param {Object} options:
13868
14477
  * hideName: Don't use name as label [false]
13869
- * disabled: Make the widget disabled [false]
14478
+ * disabled: Make the component disabled [false]
13870
14479
  * pattern: OTP numeric pattern
13871
14480
  */
13872
14481
 
13873
14482
  addOTP( name, value, callback, options = {} ) {
13874
- const widget = new LX.OTPInput( name, value, callback, options );
13875
- return this._attachWidget( widget );
14483
+ const component = new LX.OTPInput( name, value, callback, options );
14484
+ return this._attachComponent( component );
13876
14485
  }
13877
14486
 
13878
14487
  /**
13879
14488
  * @method addPad
13880
- * @param {String} name Widget name
14489
+ * @param {String} name Component name
13881
14490
  * @param {Array} value Pad value
13882
14491
  * @param {Function} callback Callback function on change
13883
14492
  * @param {Object} options:
13884
- * disabled: Make the widget disabled [false]
14493
+ * disabled: Make the component disabled [false]
13885
14494
  * min, max: Min and Max values
13886
14495
  * padSize: Size of the pad (css)
13887
14496
  * onPress: Callback function on mouse down
@@ -13889,13 +14498,13 @@ class Panel {
13889
14498
  */
13890
14499
 
13891
14500
  addPad( name, value, callback, options = {} ) {
13892
- const widget = new LX.Pad( name, value, callback, options );
13893
- return this._attachWidget( widget );
14501
+ const component = new LX.Pad( name, value, callback, options );
14502
+ return this._attachComponent( component );
13894
14503
  }
13895
14504
 
13896
14505
  /**
13897
14506
  * @method addProgress
13898
- * @param {String} name Widget name
14507
+ * @param {String} name Component name
13899
14508
  * @param {Number} value Progress value
13900
14509
  * @param {Object} options:
13901
14510
  * min, max: Min and Max values
@@ -13906,29 +14515,29 @@ class Panel {
13906
14515
  */
13907
14516
 
13908
14517
  addProgress( name, value, options = {} ) {
13909
- const widget = new LX.Progress( name, value, options );
13910
- return this._attachWidget( widget );
14518
+ const component = new LX.Progress( name, value, options );
14519
+ return this._attachComponent( component );
13911
14520
  }
13912
14521
 
13913
14522
  /**
13914
14523
  * @method addFile
13915
- * @param {String} name Widget name
14524
+ * @param {String} name Component name
13916
14525
  * @param {Function} callback Callback function on change
13917
14526
  * @param {Object} options:
13918
14527
  * local: Ask for local file
13919
- * disabled: Make the widget disabled [false]
14528
+ * disabled: Make the component disabled [false]
13920
14529
  * read: Return the file itself (False) or the contents (True)
13921
14530
  * type: type to read as [text (Default), buffer, bin, url]
13922
14531
  */
13923
14532
 
13924
14533
  addFile( name, callback, options = { } ) {
13925
- const widget = new LX.FileInput( name, callback, options );
13926
- return this._attachWidget( widget );
14534
+ const component = new LX.FileInput( name, callback, options );
14535
+ return this._attachComponent( component );
13927
14536
  }
13928
14537
 
13929
14538
  /**
13930
14539
  * @method addTree
13931
- * @param {String} name Widget name
14540
+ * @param {String} name Component name
13932
14541
  * @param {Object} data Data of the tree
13933
14542
  * @param {Object} options:
13934
14543
  * icons: Array of objects with icon button information {name, icon, callback}
@@ -13938,13 +14547,13 @@ class Panel {
13938
14547
  */
13939
14548
 
13940
14549
  addTree( name, data, options = {} ) {
13941
- const widget = new LX.Tree( name, data, options );
13942
- return this._attachWidget( widget );
14550
+ const component = new LX.Tree( name, data, options );
14551
+ return this._attachComponent( component );
13943
14552
  }
13944
14553
 
13945
14554
  /**
13946
14555
  * @method addTabSections
13947
- * @param {String} name Widget name
14556
+ * @param {String} name Component name
13948
14557
  * @param {Array} tabs Contains objects with {
13949
14558
  * name: Name of the tab (if icon, use as title)
13950
14559
  * icon: Icon to be used as the tab icon (optional)
@@ -13959,30 +14568,30 @@ class Panel {
13959
14568
  */
13960
14569
 
13961
14570
  addTabSections( name, tabs, options = {} ) {
13962
- const widget = new LX.TabSections( name, tabs, options );
13963
- return this._attachWidget( widget );
14571
+ const component = new LX.TabSections( name, tabs, options );
14572
+ return this._attachComponent( component );
13964
14573
  }
13965
14574
 
13966
14575
  /**
13967
14576
  * @method addCounter
13968
- * @param {String} name Widget name
14577
+ * @param {String} name Component name
13969
14578
  * @param {Number} value Counter value
13970
14579
  * @param {Function} callback Callback function on change
13971
14580
  * @param {Object} options:
13972
- * disabled: Make the widget disabled [false]
14581
+ * disabled: Make the component disabled [false]
13973
14582
  * min, max: Min and Max values
13974
14583
  * step: Step for adding/substracting
13975
14584
  * label: Text to show below the counter
13976
14585
  */
13977
14586
 
13978
14587
  addCounter( name, value, callback, options = { } ) {
13979
- const widget = new LX.Counter( name, value, callback, options );
13980
- return this._attachWidget( widget );
14588
+ const component = new LX.Counter( name, value, callback, options );
14589
+ return this._attachComponent( component );
13981
14590
  }
13982
14591
 
13983
14592
  /**
13984
14593
  * @method addTable
13985
- * @param {String} name Widget name
14594
+ * @param {String} name Component name
13986
14595
  * @param {Number} data Table data
13987
14596
  * @param {Object} options:
13988
14597
  * hideName: Don't use name as label [false]
@@ -14000,13 +14609,13 @@ class Panel {
14000
14609
  */
14001
14610
 
14002
14611
  addTable( name, data, options = { } ) {
14003
- const widget = new LX.Table( name, data, options );
14004
- return this._attachWidget( widget );
14612
+ const component = new LX.Table( name, data, options );
14613
+ return this._attachComponent( component );
14005
14614
  }
14006
14615
 
14007
14616
  /**
14008
14617
  * @method addDate
14009
- * @param {String} name Widget name
14618
+ * @param {String} name Component name
14010
14619
  * @param {String} dateValue
14011
14620
  * @param {Function} callback
14012
14621
  * @param {Object} options:
@@ -14017,21 +14626,34 @@ class Panel {
14017
14626
  */
14018
14627
 
14019
14628
  addDate( name, dateValue, callback, options = { } ) {
14020
- const widget = new LX.DatePicker( name, dateValue, callback, options );
14021
- return this._attachWidget( widget );
14629
+ const component = new LX.DatePicker( name, dateValue, callback, options );
14630
+ return this._attachComponent( component );
14022
14631
  }
14023
14632
 
14024
14633
  /**
14025
14634
  * @method addMap2D
14026
- * @param {String} name Widget name
14635
+ * @param {String} name Component name
14027
14636
  * @param {Array} points
14028
14637
  * @param {Function} callback
14029
14638
  * @param {Object} options:
14030
14639
  */
14031
14640
 
14032
14641
  addMap2D( name, points, callback, options = { } ) {
14033
- const widget = new LX.Map2D( name, points, callback, options );
14034
- return this._attachWidget( widget );
14642
+ const component = new LX.Map2D( name, points, callback, options );
14643
+ return this._attachComponent( component );
14644
+ }
14645
+
14646
+ /**
14647
+ * @method addRate
14648
+ * @param {String} name Component name
14649
+ * @param {Number} value
14650
+ * @param {Function} callback
14651
+ * @param {Object} options:
14652
+ */
14653
+
14654
+ addRate( name, value, callback, options = { } ) {
14655
+ const component = new LX.Rate( name, value, callback, options );
14656
+ return this._attachComponent( component );
14035
14657
  }
14036
14658
  }
14037
14659
 
@@ -14066,7 +14688,7 @@ class Branch {
14066
14688
 
14067
14689
  this.closed = options.closed ?? false;
14068
14690
  this.root = root;
14069
- this.widgets = [];
14691
+ this.components = [];
14070
14692
 
14071
14693
  // Create element
14072
14694
  var title = document.createElement( 'div' );
@@ -14137,8 +14759,8 @@ class Branch {
14137
14759
  _onMakeFloating() {
14138
14760
 
14139
14761
  const dialog = new LX.Dialog( this.name, p => {
14140
- // add widgets
14141
- for( let w of this.widgets )
14762
+ // Add components
14763
+ for( let w of this.components )
14142
14764
  {
14143
14765
  p.root.appendChild( w.root );
14144
14766
  }
@@ -14149,7 +14771,7 @@ class Branch {
14149
14771
 
14150
14772
  dialog.branchData = {
14151
14773
  name: this.name,
14152
- widgets: this.widgets,
14774
+ components: this.components,
14153
14775
  closed: this.closed,
14154
14776
  panel: this.panel,
14155
14777
  childIndex
@@ -14161,7 +14783,7 @@ class Branch {
14161
14783
  _addBranchSeparator() {
14162
14784
 
14163
14785
  const element = document.createElement('div');
14164
- element.className = "lexwidgetseparator";
14786
+ element.className = "lexcomponentseparator";
14165
14787
  element.style.width = "100%";
14166
14788
  element.style.background = "none";
14167
14789
 
@@ -14214,7 +14836,7 @@ class Branch {
14214
14836
 
14215
14837
  function innerMouseUp(e)
14216
14838
  {
14217
- that._updateWidgets();
14839
+ that._updateComponents();
14218
14840
 
14219
14841
  line.style.height = "0px";
14220
14842
 
@@ -14227,15 +14849,15 @@ class Branch {
14227
14849
  this.content.appendChild( element );
14228
14850
  }
14229
14851
 
14230
- _updateWidgets() {
14852
+ _updateComponents() {
14231
14853
 
14232
14854
  var size = this.grabber.style.marginLeft;
14233
14855
 
14234
- // Update sizes of widgets inside
14235
- for( let i = 0; i < this.widgets.length; i++ )
14856
+ // Update sizes of components inside
14857
+ for( let i = 0; i < this.components.length; i++ )
14236
14858
  {
14237
- let widget = this.widgets[ i ];
14238
- const element = widget.root;
14859
+ let component = this.components[ i ];
14860
+ const element = component.root;
14239
14861
 
14240
14862
  if( element.children.length < 2 )
14241
14863
  {
@@ -14248,10 +14870,10 @@ class Branch {
14248
14870
  name.style.width = size;
14249
14871
  name.style.minWidth = size;
14250
14872
 
14251
- switch( widget.type )
14873
+ switch( component.type )
14252
14874
  {
14253
- case LX.Widget.CUSTOM:
14254
- case LX.Widget.ARRAY:
14875
+ case LX.BaseComponent.CUSTOM:
14876
+ case LX.BaseComponent.ARRAY:
14255
14877
  continue;
14256
14878
  }
14257
14879
  value.style.width = "-moz-calc( 100% - " + size + " )";
@@ -14565,7 +15187,15 @@ class Menubar {
14565
15187
  {
14566
15188
  const data = buttons[ i ];
14567
15189
  const title = data.title;
14568
- const button = new LX.Button( title, data.label, data.callback, { title, buttonClass: "bg-none", disabled: data.disabled, icon: data.icon, hideName: true, swap: data.swap, iconPosition: "start" } );
15190
+ const button = new LX.Button( title, data.label, data.callback, {
15191
+ title,
15192
+ buttonClass: "bg-none",
15193
+ disabled: data.disabled,
15194
+ icon: data.icon,
15195
+ hideName: true,
15196
+ swap: data.swap,
15197
+ iconPosition: "start"
15198
+ } );
14569
15199
  this.buttonContainer.appendChild( button.root );
14570
15200
 
14571
15201
  if( title )
@@ -15139,9 +15769,21 @@ class Sidebar {
15139
15769
  LX.asTooltip( itemDom, key, { side: "right", offset: 16, active: false } );
15140
15770
  }
15141
15771
 
15142
- let itemName = document.createElement( 'a' );
15143
- itemName.innerHTML = key;
15144
- itemDom.appendChild( itemName );
15772
+ LX.makeElement( 'a', "grid-column-start-2", key, itemDom );
15773
+
15774
+ if( options.swap )
15775
+ {
15776
+ itemDom.classList.add( "swap", "inline-grid" );
15777
+ itemDom.querySelector( "a" ).classList.add( "swap-off" );
15778
+
15779
+ const input = document.createElement( "input" );
15780
+ input.className = "p-0 border-0";
15781
+ input.type = "checkbox";
15782
+ itemDom.prepend( input );
15783
+
15784
+ const swapIcon = LX.makeIcon( options.swap, { iconClass: "lexsidebarentryicon swap-on" } );
15785
+ itemDom.appendChild( swapIcon );
15786
+ }
15145
15787
 
15146
15788
  if( options.content )
15147
15789
  {
@@ -15170,8 +15812,14 @@ class Sidebar {
15170
15812
  item.checkbox.set( item.value, true );
15171
15813
  }
15172
15814
 
15815
+ if( options.swap && !( e.target instanceof HTMLInputElement ) )
15816
+ {
15817
+ const swapInput = itemDom.querySelector( "input" );
15818
+ swapInput.checked = !swapInput.checked;
15819
+ }
15820
+
15173
15821
  // Manage selected
15174
- if( this.displaySelected )
15822
+ if( this.displaySelected && !options.skipSelection )
15175
15823
  {
15176
15824
  this.root.querySelectorAll(".lexsidebarentry").forEach( e => e.classList.remove( 'selected' ) );
15177
15825
  entry.classList.add( "selected" );
@@ -15253,6 +15901,14 @@ class Sidebar {
15253
15901
 
15254
15902
  subentry.className = "lexsidebarentry";
15255
15903
  subentry.id = subkey;
15904
+
15905
+ if( suboptions.content )
15906
+ {
15907
+ const parentContainer = LX.makeElement( "div" );
15908
+ parentContainer.appendChild( suboptions.content );
15909
+ subentry.appendChild( parentContainer );
15910
+ }
15911
+
15256
15912
  subentryContainer.appendChild( subentry );
15257
15913
 
15258
15914
  subentry.addEventListener("click", (e) => {
@@ -15261,10 +15917,10 @@ class Sidebar {
15261
15917
  if( f ) f.call( this, subkey, subentry, e );
15262
15918
 
15263
15919
  // Manage selected
15264
- if( this.displaySelected )
15920
+ if( this.displaySelected && !suboptions.skipSelection )
15265
15921
  {
15266
15922
  this.root.querySelectorAll(".lexsidebarentry").forEach( e => e.classList.remove( 'selected' ) );
15267
- entry.classList.add( "selected" );
15923
+ subentry.classList.add( "selected" );
15268
15924
  }
15269
15925
  });
15270
15926
  }
@@ -15283,8 +15939,8 @@ class AssetViewEvent {
15283
15939
  static ASSET_RENAMED = 3;
15284
15940
  static ASSET_CLONED = 4;
15285
15941
  static ASSET_DBLCLICKED = 5;
15286
- static ENTER_FOLDER = 6;
15287
- static ASSET_CHECKED = 7;
15942
+ static ASSET_CHECKED = 6;
15943
+ static ENTER_FOLDER = 7;
15288
15944
 
15289
15945
  constructor( type, item, value ) {
15290
15946
  this.type = type || LX.TreeEvent.NONE;
@@ -15302,8 +15958,8 @@ class AssetViewEvent {
15302
15958
  case AssetViewEvent.ASSET_RENAMED: return "assetview_event_renamed";
15303
15959
  case AssetViewEvent.ASSET_CLONED: return "assetview_event_cloned";
15304
15960
  case AssetViewEvent.ASSET_DBLCLICKED: return "assetview_event_dblclicked";
15305
- case AssetViewEvent.ENTER_FOLDER: return "assetview_event_enter_folder";
15306
15961
  case AssetViewEvent.ASSET_CHECKED: return "assetview_event_checked";
15962
+ case AssetViewEvent.ENTER_FOLDER: return "assetview_event_enter_folder";
15307
15963
  }
15308
15964
  }
15309
15965
  }
@@ -15316,8 +15972,12 @@ LX.AssetViewEvent = AssetViewEvent;
15316
15972
 
15317
15973
  class AssetView {
15318
15974
 
15319
- static LAYOUT_CONTENT = 0;
15975
+ static LAYOUT_GRID = 0;
15320
15976
  static LAYOUT_LIST = 1;
15977
+
15978
+ static CONTENT_SORT_ASC = 0;
15979
+ static CONTENT_SORT_DESC = 1;
15980
+
15321
15981
  static MAX_PAGE_ELEMENTS = 50;
15322
15982
 
15323
15983
  /**
@@ -15326,7 +15986,8 @@ class AssetView {
15326
15986
  constructor( options = {} ) {
15327
15987
 
15328
15988
  this.rootPath = "https://raw.githubusercontent.com/jxarco/lexgui.js/master/";
15329
- this.layout = options.layout ?? AssetView.LAYOUT_CONTENT;
15989
+ this.layout = options.layout ?? AssetView.LAYOUT_GRID;
15990
+ this.sortMode = options.sortMode ?? AssetView.CONTENT_SORT_ASC;
15330
15991
  this.contentPage = 1;
15331
15992
 
15332
15993
  if( options.rootPath )
@@ -15444,9 +16105,9 @@ class AssetView {
15444
16105
  this.leftPanel.clear();
15445
16106
  }
15446
16107
 
15447
- if( this.rightPanel )
16108
+ if( this.toolsPanel )
15448
16109
  {
15449
- this.rightPanel.clear();
16110
+ this.toolsPanel.clear();
15450
16111
  }
15451
16112
  }
15452
16113
 
@@ -15558,7 +16219,7 @@ class AssetView {
15558
16219
  _setContentLayout( layoutMode ) {
15559
16220
 
15560
16221
  this.layout = layoutMode;
15561
-
16222
+ this.toolsPanel.refresh();
15562
16223
  this._refreshContent();
15563
16224
  }
15564
16225
 
@@ -15568,42 +16229,34 @@ class AssetView {
15568
16229
 
15569
16230
  _createContentPanel( area ) {
15570
16231
 
15571
- if( this.rightPanel )
16232
+ if( this.toolsPanel )
15572
16233
  {
15573
- this.rightPanel.clear();
16234
+ this.contentPanel.clear();
15574
16235
  }
15575
16236
  else
15576
16237
  {
15577
- this.rightPanel = area.addPanel({ className: 'lexassetcontentpanel flex flex-col overflow-hidden' });
16238
+ this.toolsPanel = area.addPanel({ className: 'flex flex-col overflow-hidden' });
16239
+ this.contentPanel = area.addPanel({ className: 'lexassetcontentpanel flex flex-col overflow-hidden' });
15578
16240
  }
15579
16241
 
15580
- const on_sort = ( value, event ) => {
15581
- const cmenu = LX.addContextMenu( "Sort by", event, c => {
15582
- c.add("Name", () => this._sortData('id') );
15583
- c.add("Type", () => this._sortData('type') );
15584
- c.add("");
15585
- c.add("Ascending", () => this._sortData() );
15586
- c.add("Descending", () => this._sortData(null, true) );
15587
- } );
15588
- const parent = this.parent.root.parentElement;
15589
- if( parent.classList.contains('lexdialog') )
15590
- {
15591
- cmenu.root.style.zIndex = (+getComputedStyle( parent ).zIndex) + 1;
15592
- }
16242
+ const _onSort = ( value, event ) => {
16243
+ new LX.DropdownMenu( event.target, [
16244
+ { name: "Name", icon: "ALargeSmall", callback: () => this._sortData( "id" ) },
16245
+ { name: "Type", icon: "Type", callback: () => this._sortData( "type" ) },
16246
+ null,
16247
+ { name: "Ascending", icon: "SortAsc", callback: () => this._sortData( null, AssetView.CONTENT_SORT_ASC ) },
16248
+ { name: "Descending", icon: "SortDesc", callback: () => this._sortData( null, AssetView.CONTENT_SORT_DESC ) }
16249
+ ], { side: "right", align: "start" });
15593
16250
  };
15594
16251
 
15595
- const on_change_view = ( value, event ) => {
15596
- const cmenu = LX.addContextMenu( "Layout", event, c => {
15597
- c.add("Content", () => this._setContentLayout( AssetView.LAYOUT_CONTENT ) );
15598
- c.add("");
15599
- c.add("List", () => this._setContentLayout( AssetView.LAYOUT_LIST ) );
15600
- } );
15601
- const parent = this.parent.root.parentElement;
15602
- if( parent.classList.contains('lexdialog') )
15603
- cmenu.root.style.zIndex = (+getComputedStyle( parent ).zIndex) + 1;
16252
+ const _onChangeView = ( value, event ) => {
16253
+ new LX.DropdownMenu( event.target, [
16254
+ { name: "Grid", icon: "LayoutGrid", callback: () => this._setContentLayout( AssetView.LAYOUT_GRID ) },
16255
+ { name: "List", icon: "LayoutList", callback: () => this._setContentLayout( AssetView.LAYOUT_LIST ) }
16256
+ ], { side: "right", align: "start" });
15604
16257
  };
15605
16258
 
15606
- const on_change_page = ( value, event ) => {
16259
+ const _onChangePage = ( value, event ) => {
15607
16260
  if( !this.allowNextPage )
15608
16261
  {
15609
16262
  return;
@@ -15619,64 +16272,71 @@ class AssetView {
15619
16272
  }
15620
16273
  };
15621
16274
 
15622
- this.rightPanel.sameLine();
15623
- this.rightPanel.addSelect( "Filter", this.allowedTypes, this.allowedTypes[ 0 ], v => this._refreshContent.call(this, null, v), { width: "30%", minWidth: "128px" } );
15624
- this.rightPanel.addText( null, this.searchValue ?? "", v => this._refreshContent.call(this, v, null), { placeholder: "Search assets.." } );
15625
- this.rightPanel.addButton( null, "", on_sort.bind(this), { title: "Sort", icon: "ArrowUpNarrowWide" } );
15626
- this.rightPanel.addButton( null, "", on_change_view.bind(this), { title: "View", icon: "GripHorizontal" } );
15627
- // Content Pages
15628
- this.rightPanel.addButton( null, "", on_change_page.bind(this, -1), { title: "Previous Page", icon: "ChevronsLeft", className: "ml-auto" } );
15629
- this.rightPanel.addButton( null, "", on_change_page.bind(this, 1), { title: "Next Page", icon: "ChevronsRight" } );
15630
- const textString = "Page " + this.contentPage + " / " + ((((this.currentData.length - 1) / AssetView.MAX_PAGE_ELEMENTS )|0) + 1);
15631
- this.rightPanel.addText(null, textString, null, {
15632
- inputClass: "nobg", disabled: true, signal: "@on_page_change", maxWidth: "16ch" }
15633
- );
15634
- this.rightPanel.endLine();
16275
+ this.toolsPanel.refresh = () => {
16276
+ this.toolsPanel.clear();
16277
+ this.toolsPanel.sameLine();
16278
+ this.toolsPanel.addSelect( "Filter", this.allowedTypes, this.filter ?? this.allowedTypes[ 0 ], v => {
16279
+ this._refreshContent( null, v );
16280
+ }, { width: "30%", minWidth: "128px" } );
16281
+ this.toolsPanel.addText( null, this.searchValue ?? "", v => this._refreshContent.call(this, v, null), { placeholder: "Search assets.." } );
16282
+ this.toolsPanel.addButton( null, "", _onSort.bind(this), { title: "Sort", tooltip: true, icon: ( this.sortMode === AssetView.CONTENT_SORT_ASC ) ? "SortAsc" : "SortDesc" } );
16283
+ this.toolsPanel.addButton( null, "", _onChangeView.bind(this), { title: "View", tooltip: true, icon: ( this.layout === AssetView.LAYOUT_GRID ) ? "LayoutGrid" : "LayoutList" } );
16284
+ // Content Pages
16285
+ this.toolsPanel.addButton( null, "", _onChangePage.bind(this, -1), { title: "Previous Page", icon: "ChevronsLeft", className: "ml-auto" } );
16286
+ this.toolsPanel.addButton( null, "", _onChangePage.bind(this, 1), { title: "Next Page", icon: "ChevronsRight" } );
16287
+ const textString = "Page " + this.contentPage + " / " + ((((this.currentData.length - 1) / AssetView.MAX_PAGE_ELEMENTS )|0) + 1);
16288
+ this.toolsPanel.addText(null, textString, null, {
16289
+ inputClass: "nobg", disabled: true, signal: "@on_page_change", maxWidth: "16ch" }
16290
+ );
15635
16291
 
15636
- if( !this.skipBrowser )
15637
- {
15638
- this.rightPanel.sameLine();
15639
- this.rightPanel.addComboButtons( null, [
15640
- {
15641
- value: "Left",
15642
- icon: "ArrowLeft",
15643
- callback: domEl => {
15644
- if(!this.prevData.length) return;
15645
- this.nextData.push( this.currentData );
15646
- this.currentData = this.prevData.pop();
15647
- this._refreshContent();
15648
- this._updatePath( this.currentData );
15649
- }
15650
- },
15651
- {
15652
- value: "Right",
15653
- icon: "ArrowRight",
15654
- callback: domEl => {
15655
- if(!this.nextData.length) return;
15656
- this.prevData.push( this.currentData );
15657
- this.currentData = this.nextData.pop();
15658
- this._refreshContent();
15659
- this._updatePath( this.currentData );
16292
+ if( !this.skipBrowser )
16293
+ {
16294
+ this.toolsPanel.addComboButtons( null, [
16295
+ {
16296
+ value: "Left",
16297
+ icon: "ArrowLeft",
16298
+ callback: domEl => {
16299
+ if(!this.prevData.length) return;
16300
+ this.nextData.push( this.currentData );
16301
+ this.currentData = this.prevData.pop();
16302
+ this._refreshContent();
16303
+ this._updatePath( this.currentData );
16304
+ }
16305
+ },
16306
+ {
16307
+ value: "Right",
16308
+ icon: "ArrowRight",
16309
+ callback: domEl => {
16310
+ if(!this.nextData.length) return;
16311
+ this.prevData.push( this.currentData );
16312
+ this.currentData = this.nextData.pop();
16313
+ this._refreshContent();
16314
+ this._updatePath( this.currentData );
16315
+ }
16316
+ },
16317
+ {
16318
+ value: "Refresh",
16319
+ icon: "Refresh",
16320
+ callback: domEl => { this._refreshContent(); }
15660
16321
  }
15661
- },
15662
- {
15663
- value: "Refresh",
15664
- icon: "Refresh",
15665
- callback: domEl => { this._refreshContent(); }
15666
- }
15667
- ], { noSelection: true } );
16322
+ ], { noSelection: true } );
15668
16323
 
15669
- this.rightPanel.addText(null, this.path.join('/'), null, {
15670
- inputClass: "nobg", disabled: true, signal: "@on_folder_change",
15671
- style: { fontWeight: "600", fontSize: "15px" }
15672
- });
16324
+ this.toolsPanel.addText(null, this.path.join('/'), null, {
16325
+ inputClass: "nobg", disabled: true, signal: "@on_folder_change",
16326
+ style: { fontWeight: "600", fontSize: "15px" }
16327
+ });
16328
+ }
15673
16329
 
15674
- this.rightPanel.endLine();
15675
- }
16330
+ this.toolsPanel.endLine();
16331
+ };
16332
+
16333
+ this.toolsPanel.refresh();
16334
+
16335
+ // Start content panel
15676
16336
 
15677
16337
  this.content = document.createElement('ul');
15678
16338
  this.content.className = "lexassetscontent";
15679
- this.rightPanel.root.appendChild(this.content);
16339
+ this.contentPanel.attach( this.content );
15680
16340
 
15681
16341
  this.content.addEventListener('dragenter', function( e ) {
15682
16342
  e.preventDefault();
@@ -15699,12 +16359,12 @@ class AssetView {
15699
16359
 
15700
16360
  _refreshContent( searchValue, filter ) {
15701
16361
 
15702
- const isContentLayout = ( this.layout == AssetView.LAYOUT_CONTENT ); // default
16362
+ const isGridLayout = ( this.layout == AssetView.LAYOUT_GRID ); // default
15703
16363
 
15704
16364
  this.filter = filter ?? ( this.filter ?? "None" );
15705
16365
  this.searchValue = searchValue ?? (this.searchValue ?? "");
15706
16366
  this.content.innerHTML = "";
15707
- this.content.className = (isContentLayout ? "lexassetscontent" : "lexassetscontent list");
16367
+ this.content.className = (isGridLayout ? "lexassetscontent" : "lexassetscontent list");
15708
16368
  let that = this;
15709
16369
 
15710
16370
  const _addItem = function(item) {
@@ -15727,7 +16387,7 @@ class AssetView {
15727
16387
 
15728
16388
  itemEl.addEventListener("mousemove", e => {
15729
16389
 
15730
- if( !isContentLayout )
16390
+ if( !isGridLayout )
15731
16391
  {
15732
16392
  return;
15733
16393
  }
@@ -15756,14 +16416,14 @@ class AssetView {
15756
16416
  });
15757
16417
 
15758
16418
  itemEl.addEventListener("mouseenter", () => {
15759
- if( isContentLayout )
16419
+ if( isGridLayout )
15760
16420
  {
15761
16421
  desc.style.display = "unset";
15762
16422
  }
15763
16423
  });
15764
16424
 
15765
16425
  itemEl.addEventListener("mouseleave", () => {
15766
- if( isContentLayout )
16426
+ if( isGridLayout )
15767
16427
  {
15768
16428
  setTimeout( () => {
15769
16429
  desc.style.display = "none";
@@ -15807,11 +16467,11 @@ class AssetView {
15807
16467
  let preview = null;
15808
16468
  const hasImage = item.src && (['png', 'jpg'].indexOf( LX.getExtension( item.src ) ) > -1 || item.src.includes("data:image/") ); // Support b64 image as src
15809
16469
 
15810
- if( hasImage || isFolder || !isContentLayout)
16470
+ if( hasImage || isFolder || !isGridLayout)
15811
16471
  {
15812
16472
  preview = document.createElement('img');
15813
16473
  let real_src = item.unknown_extension ? that.rootPath + "images/file.png" : (isFolder ? that.rootPath + "images/folder.png" : item.src);
15814
- preview.src = (isContentLayout || isFolder ? real_src : that.rootPath + "images/file.png");
16474
+ preview.src = (isGridLayout || isFolder ? real_src : that.rootPath + "images/file.png");
15815
16475
  itemEl.appendChild( preview );
15816
16476
  }
15817
16477
  else
@@ -16072,16 +16732,20 @@ class AssetView {
16072
16732
  }
16073
16733
  }
16074
16734
 
16075
- _sortData( sort_by, sort_descending = false ) {
16735
+ _sortData( sortBy, sortMode ) {
16076
16736
 
16077
- sort_by = sort_by ?? (this._lastSortBy ?? 'id');
16078
- this.currentData = this.currentData.sort( (a, b) => {
16079
- var r = sort_descending ? b[sort_by].localeCompare(a[sort_by]) : a[sort_by].localeCompare(b[sort_by]);
16080
- if(r == 0) r = sort_descending ? b['id'].localeCompare(a['id']) : a['id'].localeCompare(b['id']);
16737
+ sortBy = sortBy ?? ( this._lastSortBy ?? 'id' );
16738
+ sortMode = sortMode ?? this.sortMode;
16739
+ const sortDesc = ( sortMode === AssetView.CONTENT_SORT_DESC );
16740
+ this.currentData = this.currentData.sort( ( a, b ) => {
16741
+ var r = sortDesc ? b[ sortBy ].localeCompare( a[ sortBy ] ) : a[ sortBy ].localeCompare( b[ sortBy ] );
16742
+ if( r == 0 ) r = sortDesc ? b['id'].localeCompare( a['id'] ) : a[ 'id' ].localeCompare( b[ 'id' ] );
16081
16743
  return r;
16082
16744
  } );
16083
16745
 
16084
- this._lastSortBy = sort_by;
16746
+ this._lastSortBy = sortBy;
16747
+ this.sortMode = sortMode;
16748
+ this.toolsPanel.refresh();
16085
16749
  this._refreshContent();
16086
16750
  }
16087
16751
 
@@ -16451,4 +17115,4 @@ class Tour {
16451
17115
  }
16452
17116
  LX.Tour = Tour;
16453
17117
 
16454
- export { ADD_CUSTOM_WIDGET, Area, AssetView, AssetViewEvent, Branch, LX, Menubar, Panel, Sidebar, Tour, Widget };
17118
+ export { ADD_CUSTOM_COMPONENT, Area, AssetView, AssetViewEvent, BaseComponent, Branch, LX, Menubar, Panel, Sidebar, Tour };